@xenon-device-management/xenon 1.17.0 → 1.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xenon-device-management/xenon",
3
- "version": "1.17.0",
3
+ "version": "1.17.2",
4
4
  "description": "Xenon - Intelligent Mobile Infrastructure. A self-healing device orchestration platform for Appium.",
5
5
  "main": "./lib/src/index.js",
6
6
  "exports": {
@@ -56,6 +56,7 @@ exports.getXenonCapabilities = getXenonCapabilities;
56
56
  const get_port_1 = __importDefault(require("get-port"));
57
57
  const lodash_1 = __importDefault(require("lodash"));
58
58
  const typedi_1 = require("typedi");
59
+ const PortAllocator_1 = require("./services/PortAllocator");
59
60
  const logger_1 = __importDefault(require("./logger"));
60
61
  var XENON_CAPABILITIES;
61
62
  (function (XENON_CAPABILITIES) {
@@ -128,18 +129,19 @@ function androidCapabilities(caps, freeDevice) {
128
129
  }
129
130
  function iOSCapabilities(caps, freeDevice) {
130
131
  return __awaiter(this, void 0, void 0, function* () {
132
+ var _a, _b;
131
133
  const fm = ensureFirstMatch(caps);
132
134
  fm['appium:udid'] = freeDevice.udid;
133
135
  fm['platformName'] = freeDevice.platform;
134
136
  fm['appium:deviceName'] = freeDevice.name;
135
137
  fm['appium:platformVersion'] = freeDevice.sdk;
136
- if (!fm['appium:wdaLocalPort']) {
137
- fm['appium:wdaLocalPort'] = freeDevice.wdaLocalPort;
138
- }
139
- if (!fm['appium:mjpegServerPort']) {
140
- fm['appium:mjpegServerPort'] = freeDevice.mjpegServerPort;
141
- }
142
138
  fm['appium:derivedDataPath'] = freeDevice.derivedDataPath;
139
+ // Whether this session will stand up its own WebDriverAgent, or ride the one
140
+ // the stream service already has. Decided before any port is assigned: a
141
+ // session that reuses the stream's WDA must not be handed ports, and
142
+ // acquiring them first only to delete them below would burn a lease out of a
143
+ // hundred on every such session.
144
+ let reusingStreamWda = false;
143
145
  // Technical Optimization: Reuse existing WDA tunnel if Stream Service is active
144
146
  // This prevents "Port Occupied" errors when the dashboard is open and speeds up startup by 15-30s
145
147
  try {
@@ -150,6 +152,7 @@ function iOSCapabilities(caps, freeDevice) {
150
152
  if (streamStatus && (streamStatus.status === 'running' || streamStatus.status === 'starting')) {
151
153
  const wdaUrl = `http://127.0.0.1:${streamStatus.wdaPort}`;
152
154
  fm['appium:webDriverAgentUrl'] = wdaUrl;
155
+ reusingStreamWda = true;
153
156
  // If we are reusing the WDA, we MUST NOT pass wdaLocalPort or mjpegServerPort
154
157
  // as XCUITestDriver will still try to verify they are free and fail if busy.
155
158
  delete fm['appium:wdaLocalPort'];
@@ -164,6 +167,27 @@ function iOSCapabilities(caps, freeDevice) {
164
167
  catch (e) {
165
168
  logger_1.default.warn(`[Xenon] ⚠️ Failed to check Stream Service: ${e.message}`);
166
169
  }
170
+ // Ports, at the one moment we know they are needed and by which device.
171
+ //
172
+ // Discovery no longer leases one to every simulator on the machine — it
173
+ // cannot, there are more simulators than ports — so the device picked for
174
+ // this session may arrive without one. Acquiring here is deliberate: it is
175
+ // the first point at which a specific device has been chosen.
176
+ //
177
+ // Exhaustion raises rather than passing undefined through. Without a port
178
+ // XCUITest falls back to its own default of 8100, which is the same number
179
+ // for every device, so a silent skip trades a clear failure for two sessions
180
+ // quietly fighting over one port.
181
+ if (!reusingStreamWda) {
182
+ if (!fm['appium:wdaLocalPort']) {
183
+ fm['appium:wdaLocalPort'] =
184
+ (_a = freeDevice.wdaLocalPort) !== null && _a !== void 0 ? _a : (yield typedi_1.Container.get(PortAllocator_1.PortAllocator).acquire('wda', freeDevice.udid));
185
+ }
186
+ if (!fm['appium:mjpegServerPort']) {
187
+ fm['appium:mjpegServerPort'] =
188
+ (_b = freeDevice.mjpegServerPort) !== null && _b !== void 0 ? _b : (yield typedi_1.Container.get(PortAllocator_1.PortAllocator).acquire('mjpeg', freeDevice.udid));
189
+ }
190
+ }
167
191
  // Senior Resiliency: Inject higher defaults for WebDriverAgent in enterprise environments
168
192
  if (!isCapabilityAlreadyPresent(caps, 'appium:wdaLaunchTimeout')) {
169
193
  // 180s is safer for physical devices that might need WDA signing/installation
@@ -55,6 +55,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
55
55
  };
56
56
  Object.defineProperty(exports, "__esModule", { value: true });
57
57
  exports.IOSDiscoveryService = void 0;
58
+ exports.simulatorNeedsPortNow = simulatorNeedsPortNow;
58
59
  const node_simctl_1 = __importDefault(require("node-simctl"));
59
60
  const lodash_1 = require("lodash");
60
61
  const appium_ios_device_1 = require("appium-ios-device");
@@ -73,6 +74,23 @@ const config_1 = require("../../config");
73
74
  const device_service_1 = require("../../data-service/device-service");
74
75
  const iOSTracker_1 = require("../iOSTracker");
75
76
  const networkAddresses_1 = require("../../helpers/networkAddresses");
77
+ /**
78
+ * Should this simulator be given wda/mjpeg ports the moment we notice it?
79
+ *
80
+ * Every simulator installed on the machine is discovered, booted or not, and a
81
+ * Mac carries far more of them than the port ranges hold — 158 against 100
82
+ * where this surfaced. A shut-down simulator is not running WebDriverAgent and
83
+ * will not until someone boots it, so it gets nothing here; `iOSCapabilities`
84
+ * acquires a port at session time for whichever device is actually chosen.
85
+ *
86
+ * Draining the range did not merely waste ports. Discovery threw once it ran
87
+ * dry, so IOSDeviceManager returned no devices at all, and the physically
88
+ * attached iPhone on the same Mac was reaped as stale for not being in its own
89
+ * device list.
90
+ */
91
+ function simulatorNeedsPortNow(state) {
92
+ return state === 'Booted';
93
+ }
76
94
  let IOSDiscoveryService = class IOSDiscoveryService {
77
95
  constructor(context) {
78
96
  this.context = context;
@@ -160,8 +178,11 @@ let IOSDiscoveryService = class IOSDiscoveryService {
160
178
  const host = this.pluginArgs.remoteMachineProxyIP
161
179
  ? String(this.pluginArgs.remoteMachineProxyIP)
162
180
  : `http://${this.pluginArgs.bindHostOrIp}:${this.hostPort}`;
163
- const wdaLocalPort = (storeDevice === null || storeDevice === void 0 ? void 0 : storeDevice.wdaLocalPort) || (yield typedi_1.Container.get(PortAllocator_1.PortAllocator).acquire('wda', udid));
164
- const mjpegServerPort = (storeDevice === null || storeDevice === void 0 ? void 0 : storeDevice.mjpegServerPort) || (yield typedi_1.Container.get(PortAllocator_1.PortAllocator).acquire('mjpeg', udid));
181
+ // A real device is going to want these, so ask now — but not at the cost
182
+ // of the whole discovery pass. See tryAcquire: an exhausted range used to
183
+ // throw out of here and take the device list with it.
184
+ const wdaLocalPort = (storeDevice === null || storeDevice === void 0 ? void 0 : storeDevice.wdaLocalPort) || (yield typedi_1.Container.get(PortAllocator_1.PortAllocator).tryAcquire('wda', udid));
185
+ const mjpegServerPort = (storeDevice === null || storeDevice === void 0 ? void 0 : storeDevice.mjpegServerPort) || (yield typedi_1.Container.get(PortAllocator_1.PortAllocator).tryAcquire('mjpeg', udid));
165
186
  const totalUtilizationTimeMilliSec = yield (0, device_utils_1.getUtilizationTime)(udid);
166
187
  let sdk = 'Unknown';
167
188
  let name = 'iPhone';
@@ -229,8 +250,14 @@ let IOSDiscoveryService = class IOSDiscoveryService {
229
250
  return yield Promise.all(simulators.map((d) => __awaiter(this, void 0, void 0, function* () {
230
251
  var _a;
231
252
  const storeDevice = yield store.findDevice({ udid: d.udid });
232
- return Object.assign(Object.assign({}, d), { wdaLocalPort: (storeDevice === null || storeDevice === void 0 ? void 0 : storeDevice.wdaLocalPort) || (yield typedi_1.Container.get(PortAllocator_1.PortAllocator).acquire('wda', d.udid)), mjpegServerPort: (storeDevice === null || storeDevice === void 0 ? void 0 : storeDevice.mjpegServerPort) ||
233
- (yield typedi_1.Container.get(PortAllocator_1.PortAllocator).acquire('mjpeg', d.udid)), busy: false, realDevice: false, platform: (((_a = d.name) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes('tv')) ? 'tvos' : 'ios'), deviceType: 'simulator', host: `http://${this.pluginArgs.bindHostOrIp}:${this.hostPort}`, ip: nodeLanIp, totalUtilizationTimeMilliSec: yield (0, device_utils_1.getUtilizationTime)(d.udid), sessionStartTime: 0 });
253
+ const willRunWda = simulatorNeedsPortNow(d.state);
254
+ return Object.assign(Object.assign({}, d), { wdaLocalPort: (storeDevice === null || storeDevice === void 0 ? void 0 : storeDevice.wdaLocalPort) ||
255
+ (willRunWda
256
+ ? yield typedi_1.Container.get(PortAllocator_1.PortAllocator).tryAcquire('wda', d.udid)
257
+ : undefined), mjpegServerPort: (storeDevice === null || storeDevice === void 0 ? void 0 : storeDevice.mjpegServerPort) ||
258
+ (willRunWda
259
+ ? yield typedi_1.Container.get(PortAllocator_1.PortAllocator).tryAcquire('mjpeg', d.udid)
260
+ : undefined), busy: false, realDevice: false, platform: (((_a = d.name) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes('tv')) ? 'tvos' : 'ios'), deviceType: 'simulator', host: `http://${this.pluginArgs.bindHostOrIp}:${this.hostPort}`, ip: nodeLanIp, totalUtilizationTimeMilliSec: yield (0, device_utils_1.getUtilizationTime)(d.udid), sessionStartTime: 0 });
234
261
  })));
235
262
  });
236
263
  }
@@ -111,6 +111,33 @@ let PortAllocator = class PortAllocator {
111
111
  throw new PortRangeExhaustedError(purpose);
112
112
  });
113
113
  }
114
+ /**
115
+ * `acquire`, but exhaustion is `undefined` rather than a throw.
116
+ *
117
+ * For callers where a port is nice-to-have rather than required — device
118
+ * discovery being the one that matters. A device that cannot get a port yet
119
+ * is still a device, and must still be listed: when an exhausted range threw
120
+ * out of the discovery pass, the whole platform's device list came back
121
+ * empty and `removeStaleDevices` then deleted a physically-attached iPhone
122
+ * for not being in it.
123
+ *
124
+ * Only exhaustion is swallowed. A database or probe failure is a different
125
+ * kind of problem and still raises.
126
+ */
127
+ tryAcquire(purpose_1, udid_1) {
128
+ return __awaiter(this, arguments, void 0, function* (purpose, udid, opts = {}) {
129
+ try {
130
+ return yield this.acquire(purpose, udid, opts);
131
+ }
132
+ catch (err) {
133
+ if (err instanceof PortRangeExhaustedError) {
134
+ this.log.warn(`No ${purpose} port available for ${udid}; continuing without one. It will be acquired when the device actually needs it.`);
135
+ return undefined;
136
+ }
137
+ throw err;
138
+ }
139
+ });
140
+ }
114
141
  /**
115
142
  * Soft-block a port so the next `acquire` for the same purpose skips it.
116
143
  * Used when bind fails (EADDRINUSE) even though our lease owned the number —
@@ -329,7 +329,12 @@ let SessionLifecycleService = class SessionLifecycleService {
329
329
  }
330
330
  else if (isWdaActive) {
331
331
  const wdaUrl = `http://127.0.0.1:${streamStatus.wdaPort}`;
332
- this.injectWDAUrl(caps, wdaUrl);
332
+ // The bundle id matters as much here as on the branch above, and this
333
+ // is the branch that runs in the ordinary case — a preview already
334
+ // open when a test starts. Omitting it told Appium to manage "the
335
+ // preinstalled WDA" without saying which, and it removed ours.
336
+ const bundleId = yield streamService.detectWDABundleId(device.udid);
337
+ this.injectWDAUrl(caps, wdaUrl, bundleId);
333
338
  }
334
339
  const hasWdaUrl = lodash_1.default.has(caps.alwaysMatch, 'appium:webDriverAgentUrl') ||
335
340
  lodash_1.default.has((_a = caps.firstMatch) === null || _a === void 0 ? void 0 : _a[0], 'appium:webDriverAgentUrl');
@@ -355,9 +360,33 @@ let SessionLifecycleService = class SessionLifecycleService {
355
360
  return;
356
361
  const other = target === caps.alwaysMatch ? (_c = caps.firstMatch) === null || _c === void 0 ? void 0 : _c[0] : caps.alwaysMatch;
357
362
  target['appium:webDriverAgentUrl'] = wdaUrl;
358
- target['appium:usePreinstalledWDA'] = true;
359
- if (bundleId)
363
+ // `usePreinstalledWDA` and `updatedWDABundleId` travel together or not at
364
+ // all. On its own, the first one sends the driver down
365
+ // `preparePreinstalled`, which calls `cleanupApps(driver,
366
+ // [driver.wda.bundleIdForXctest])` — that removes every installed app
367
+ // whose CFBundleName is WebDriverAgentRunner except the one bundle id it
368
+ // was told to keep. Without `updatedWDABundleId` that keep-list is the
369
+ // driver's own default, `com.facebook.WebDriverAgentRunner.xctrunner`, so
370
+ // the WDA Xenon just launched (`com.qasecret.…` here) is not on it and is
371
+ // uninstalled from the device mid-session. Observed twice on a real
372
+ // iPhone: "Removing WebDriverAgent runner app
373
+ // 'com.qasecret.WebDriverAgentRunner.xctrunner'", after which every iOS
374
+ // feature fails with "WebDriverAgent is not installed" until someone
375
+ // re-signs and reinstalls it by hand.
376
+ //
377
+ // `webDriverAgentUrl` alone is enough to reuse a running WDA, and it does
378
+ // not enter that cleanup path — so when the bundle id is unknown, the safe
379
+ // move is to say less, not more.
380
+ if (bundleId) {
381
+ target['appium:usePreinstalledWDA'] = true;
360
382
  target['appium:updatedWDABundleId'] = bundleId;
383
+ }
384
+ else {
385
+ logger_1.default.warn(`[Xenon] Could not identify the WebDriverAgent bundle on this device; reusing it by URL only. ` +
386
+ `Sending usePreinstalledWDA without a bundle id would let the driver uninstall it.`);
387
+ delete target['appium:usePreinstalledWDA'];
388
+ delete target['appium:updatedWDABundleId'];
389
+ }
361
390
  // These conflict with a pre-installed WDA URL — strip from both buckets.
362
391
  for (const bucket of [target, other]) {
363
392
  if (!bucket)
@@ -33,10 +33,21 @@ describe('SessionLifecycleService.injectWDAUrl — W3C capability composition',
33
33
  });
34
34
  it('falls back to firstMatch[0] when there is no alwaysMatch', () => {
35
35
  const caps = { firstMatch: [{ 'appium:udid': 'abc' }] };
36
- injectWDAUrl(caps, URL);
36
+ injectWDAUrl(caps, URL, 'com.qasecret.WebDriverAgentRunner');
37
37
  expect(caps.firstMatch[0]['appium:webDriverAgentUrl']).to.equal(URL);
38
38
  expect(caps.firstMatch[0]['appium:usePreinstalledWDA']).to.equal(true);
39
39
  });
40
+ it('does not claim a preinstalled WDA it cannot name, in either bucket', () => {
41
+ // This case used to assert the opposite, and the behaviour it pinned
42
+ // uninstalled WebDriverAgent from the device: `usePreinstalledWDA` without
43
+ // `updatedWDABundleId` sends the driver into `cleanupApps`, whose keep-list
44
+ // is then its own default bundle id rather than ours. See
45
+ // test/unit/wda-cap-injection.spec.ts for the full chain.
46
+ const caps = { firstMatch: [{ 'appium:udid': 'abc' }] };
47
+ injectWDAUrl(caps, URL);
48
+ expect(caps.firstMatch[0]['appium:webDriverAgentUrl']).to.equal(URL);
49
+ expect(caps.firstMatch[0]).to.not.have.property('appium:usePreinstalledWDA');
50
+ });
40
51
  it('scrubs conflicting WDA caps from both buckets', () => {
41
52
  const caps = {
42
53
  alwaysMatch: { 'appium:usePrebuiltWDA': true, 'appium:wdaLocalPort': 8100 },
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ var __importDefault = (this && this.__importDefault) || function (mod) {
45
+ return (mod && mod.__esModule) ? mod : { "default": mod };
46
+ };
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ require("reflect-metadata");
49
+ const chai_1 = require("chai");
50
+ const sinon_1 = __importDefault(require("sinon"));
51
+ const PortAllocator_1 = require("../../src/services/PortAllocator");
52
+ const IOSDiscoveryService_1 = require("../../src/device-managers/ios/IOSDiscoveryService");
53
+ const XenonCapabilityManager_1 = require("../../src/XenonCapabilityManager");
54
+ const typedi_1 = require("typedi");
55
+ const prisma_1 = require("../../src/prisma");
56
+ /**
57
+ * A Mac carries far more simulators than these ranges hold — 158 against 100
58
+ * where this surfaced. Discovery leased a wda and an mjpeg port to every one
59
+ * of them, drained both ranges, and then threw; IOSDeviceManager returned no
60
+ * devices at all, and the physically-attached iPhone on the same Mac was
61
+ * reaped as stale for not appearing in its own device list.
62
+ */
63
+ describe('port exhaustion during iOS discovery', () => {
64
+ beforeEach(() => {
65
+ sinon_1.default.stub(prisma_1.prisma.portLease, 'findMany').resolves([]);
66
+ sinon_1.default.stub(prisma_1.prisma.portLease, 'findFirst').resolves(null);
67
+ sinon_1.default.stub(prisma_1.prisma.portLease, 'deleteMany').resolves({ count: 0 });
68
+ sinon_1.default.stub(prisma_1.prisma.portLease, 'delete').resolves({});
69
+ sinon_1.default.stub(prisma_1.prisma.portLease, 'update').resolves({});
70
+ });
71
+ afterEach(() => sinon_1.default.restore());
72
+ function exhaustedAllocator() {
73
+ const a = new PortAllocator_1.PortAllocator();
74
+ a.configure({ wda: [8100, 8101] });
75
+ // Every port in the range already leased to somebody else.
76
+ prisma_1.prisma.portLease.findMany.resolves([{ port: 8100 }, { port: 8101 }]);
77
+ return a;
78
+ }
79
+ it('acquire still raises when the range is exhausted', () => __awaiter(void 0, void 0, void 0, function* () {
80
+ // The strict form is unchanged — callers that genuinely need a port must
81
+ // still hear about it.
82
+ let err;
83
+ yield exhaustedAllocator()
84
+ .acquire('wda', 'udid-1')
85
+ .catch((e) => (err = e));
86
+ (0, chai_1.expect)(err).to.be.instanceOf(PortAllocator_1.PortRangeExhaustedError);
87
+ }));
88
+ it('tryAcquire reports exhaustion as undefined instead', () => __awaiter(void 0, void 0, void 0, function* () {
89
+ (0, chai_1.expect)(yield exhaustedAllocator().tryAcquire('wda', 'udid-1')).to.equal(undefined);
90
+ }));
91
+ it('tryAcquire does not swallow a failure that is not exhaustion', () => __awaiter(void 0, void 0, void 0, function* () {
92
+ // A database or probe failure is a different problem and must not be
93
+ // quietly turned into "no port available".
94
+ const a = new PortAllocator_1.PortAllocator();
95
+ a.configure({ wda: [8100, 8101] });
96
+ prisma_1.prisma.portLease.findMany.rejects(new Error('database is locked'));
97
+ let message = '';
98
+ yield a.tryAcquire('wda', 'udid-1').catch((e) => (message = e.message));
99
+ (0, chai_1.expect)(message).to.equal('database is locked');
100
+ }));
101
+ it('one device exhausting the range does not stop the next from being listed', () => __awaiter(void 0, void 0, void 0, function* () {
102
+ // The behaviour that matters: discovery maps over devices, and the pass
103
+ // has to survive a device that cannot get a port.
104
+ const a = exhaustedAllocator();
105
+ const discovered = yield Promise.all(['sim-1', 'sim-2', 'iphone'].map((udid) => __awaiter(void 0, void 0, void 0, function* () {
106
+ return ({
107
+ udid,
108
+ wdaLocalPort: yield a.tryAcquire('wda', udid),
109
+ });
110
+ })));
111
+ (0, chai_1.expect)(discovered.map((d) => d.udid)).to.deep.equal(['sim-1', 'sim-2', 'iphone']);
112
+ (0, chai_1.expect)(discovered.every((d) => d.wdaLocalPort === undefined)).to.equal(true);
113
+ }));
114
+ });
115
+ /**
116
+ * The other half: not needing the ports in the first place. A shut-down
117
+ * simulator is not running WebDriverAgent and will not until someone boots it.
118
+ */
119
+ describe('who gets a port at discovery time', () => {
120
+ const acquiredFor = [];
121
+ const allocator = {
122
+ tryAcquire: (_purpose, udid) => __awaiter(void 0, void 0, void 0, function* () {
123
+ acquiredFor.push(udid);
124
+ return 8100 + acquiredFor.length;
125
+ }),
126
+ };
127
+ // The real predicate from the discovery service, not a copy of it — a
128
+ // restatement here would keep passing after the rule changed.
129
+ function portFor(sim) {
130
+ return __awaiter(this, void 0, void 0, function* () {
131
+ return (sim.stored ||
132
+ ((0, IOSDiscoveryService_1.simulatorNeedsPortNow)(sim.state) ? yield allocator.tryAcquire('wda', sim.udid) : undefined));
133
+ });
134
+ }
135
+ beforeEach(() => (acquiredFor.length = 0));
136
+ it('leases to a booted simulator', () => __awaiter(void 0, void 0, void 0, function* () {
137
+ (0, chai_1.expect)(yield portFor({ udid: 'booted-1', state: 'Booted' })).to.be.a('number');
138
+ (0, chai_1.expect)(acquiredFor).to.deep.equal(['booted-1']);
139
+ }));
140
+ it('leases nothing to a shut-down simulator', () => __awaiter(void 0, void 0, void 0, function* () {
141
+ (0, chai_1.expect)(yield portFor({ udid: 'off-1', state: 'Shutdown' })).to.equal(undefined);
142
+ (0, chai_1.expect)(acquiredFor).to.deep.equal([]);
143
+ }));
144
+ it('keeps a port the device already has, whatever its state', () => __awaiter(void 0, void 0, void 0, function* () {
145
+ (0, chai_1.expect)(yield portFor({ udid: 'off-2', state: 'Shutdown', stored: 8137 })).to.equal(8137);
146
+ (0, chai_1.expect)(acquiredFor).to.deep.equal([]);
147
+ }));
148
+ it('does not drain the range across a machine full of idle simulators', () => __awaiter(void 0, void 0, void 0, function* () {
149
+ const sims = Array.from({ length: 158 }, (_, i) => ({ udid: `sim-${i}`, state: 'Shutdown' }));
150
+ yield Promise.all(sims.map(portFor));
151
+ (0, chai_1.expect)(acquiredFor.length).to.equal(0);
152
+ }));
153
+ });
154
+ /**
155
+ * Discovery no longer hands a port to every simulator, so the device chosen
156
+ * for a session can arrive without one. This is where it gets one.
157
+ */
158
+ describe('iOSCapabilities port acquisition', () => {
159
+ const device = {
160
+ udid: 'sim-1',
161
+ name: 'iPhone 15',
162
+ realDevice: false,
163
+ sdk: '17.5',
164
+ platform: 'ios',
165
+ };
166
+ let acquireStub;
167
+ beforeEach(() => {
168
+ acquireStub = sinon_1.default.stub(PortAllocator_1.PortAllocator.prototype, 'acquire');
169
+ acquireStub.withArgs('wda').resolves(8123);
170
+ acquireStub.withArgs('mjpeg').resolves(9123);
171
+ });
172
+ afterEach(() => sinon_1.default.restore());
173
+ it('acquires when the chosen device arrived without ports', () => __awaiter(void 0, void 0, void 0, function* () {
174
+ const caps = { alwaysMatch: {}, firstMatch: [{}] };
175
+ yield (0, XenonCapabilityManager_1.iOSCapabilities)(caps, device);
176
+ (0, chai_1.expect)(caps.firstMatch[0]['appium:wdaLocalPort']).to.equal(8123);
177
+ (0, chai_1.expect)(caps.firstMatch[0]['appium:mjpegServerPort']).to.equal(9123);
178
+ }));
179
+ it('uses the port the device already holds rather than taking another', () => __awaiter(void 0, void 0, void 0, function* () {
180
+ const caps = { alwaysMatch: {}, firstMatch: [{}] };
181
+ yield (0, XenonCapabilityManager_1.iOSCapabilities)(caps, Object.assign(Object.assign({}, device), { wdaLocalPort: 8150, mjpegServerPort: 9150 }));
182
+ (0, chai_1.expect)(caps.firstMatch[0]['appium:wdaLocalPort']).to.equal(8150);
183
+ (0, chai_1.expect)(acquireStub.calledWith('wda')).to.equal(false);
184
+ }));
185
+ it('leaves a caller-supplied port alone', () => __awaiter(void 0, void 0, void 0, function* () {
186
+ const caps = { alwaysMatch: {}, firstMatch: [{ 'appium:wdaLocalPort': 8199 }] };
187
+ yield (0, XenonCapabilityManager_1.iOSCapabilities)(caps, device);
188
+ (0, chai_1.expect)(caps.firstMatch[0]['appium:wdaLocalPort']).to.equal(8199);
189
+ (0, chai_1.expect)(acquireStub.calledWith('wda')).to.equal(false);
190
+ }));
191
+ it('takes no lease at all when the session will reuse the stream service WDA', () => __awaiter(void 0, void 0, void 0, function* () {
192
+ // That branch deletes both ports a few lines later. Acquiring first would
193
+ // burn one out of a hundred on every session opened while the dashboard
194
+ // has a preview up — the exact pressure this change exists to relieve.
195
+ const { default: IOSStreamService } = yield Promise.resolve().then(() => __importStar(require('../../src/device-managers/ios/IOSStreamService')));
196
+ sinon_1.default
197
+ .stub(typedi_1.Container.get(IOSStreamService), 'getStreamStatus')
198
+ .returns({ status: 'running', wdaPort: 8100 });
199
+ const caps = { alwaysMatch: {}, firstMatch: [{}] };
200
+ yield (0, XenonCapabilityManager_1.iOSCapabilities)(caps, device);
201
+ (0, chai_1.expect)(caps.firstMatch[0]['appium:webDriverAgentUrl']).to.equal('http://127.0.0.1:8100');
202
+ (0, chai_1.expect)(caps.firstMatch[0]['appium:wdaLocalPort']).to.equal(undefined);
203
+ (0, chai_1.expect)(acquireStub.called).to.equal(false);
204
+ }));
205
+ });
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ require("reflect-metadata");
4
+ const chai_1 = require("chai");
5
+ const SessionLifecycleService_1 = require("../../src/services/SessionLifecycleService");
6
+ /**
7
+ * `appium:usePreinstalledWDA` sends appium-xcuitest-driver down
8
+ * `preparePreinstalled`, which calls
9
+ * `cleanupApps(driver, [driver.wda.bundleIdForXctest])` — removing every
10
+ * installed app whose CFBundleName is WebDriverAgentRunner except the single
11
+ * bundle id on that keep-list.
12
+ *
13
+ * Without `appium:updatedWDABundleId`, that keep-list holds the driver's own
14
+ * default (`com.facebook.WebDriverAgentRunner.xctrunner`), so a differently
15
+ * signed WDA — which is what Xenon launches — is not on it and gets
16
+ * uninstalled from the device mid-session. Observed twice on a real iPhone,
17
+ * logged verbatim by the driver as:
18
+ *
19
+ * Removing WebDriverAgent runner app 'com.qasecret.WebDriverAgentRunner.xctrunner'
20
+ *
21
+ * after which every iOS feature fails with "WebDriverAgent is not installed"
22
+ * until it is re-signed and reinstalled by hand.
23
+ *
24
+ * So the two capabilities are a pair. These tests pin that they can never be
25
+ * emitted apart.
26
+ */
27
+ describe('WDA capability injection', () => {
28
+ // The method is private by design; this is the one place its contract is
29
+ // asserted, so it is reached deliberately rather than widening the API.
30
+ const inject = (caps, url, bundleId) => SessionLifecycleService_1.SessionLifecycleService.prototype.injectWDAUrl.call(Object.create(SessionLifecycleService_1.SessionLifecycleService.prototype), caps, url, bundleId);
31
+ const WDA_URL = 'http://127.0.0.1:8100';
32
+ const BUNDLE = 'com.qasecret.WebDriverAgentRunner.xctrunner';
33
+ it('names the bundle whenever it claims a preinstalled WDA', () => {
34
+ const caps = { alwaysMatch: {}, firstMatch: [{}] };
35
+ inject(caps, WDA_URL, BUNDLE);
36
+ (0, chai_1.expect)(caps.alwaysMatch['appium:usePreinstalledWDA']).to.equal(true);
37
+ (0, chai_1.expect)(caps.alwaysMatch['appium:updatedWDABundleId']).to.equal(BUNDLE);
38
+ });
39
+ it('never claims a preinstalled WDA it cannot name', () => {
40
+ // The destructive combination. `webDriverAgentUrl` alone still reuses the
41
+ // running WDA and does not enter the cleanup path at all.
42
+ const caps = { alwaysMatch: {}, firstMatch: [{}] };
43
+ inject(caps, WDA_URL, null);
44
+ (0, chai_1.expect)(caps.alwaysMatch['appium:webDriverAgentUrl']).to.equal(WDA_URL);
45
+ (0, chai_1.expect)(caps.alwaysMatch['appium:usePreinstalledWDA']).to.equal(undefined);
46
+ (0, chai_1.expect)(caps.alwaysMatch['appium:updatedWDABundleId']).to.equal(undefined);
47
+ });
48
+ it('never claims one when the bundle id is simply absent', () => {
49
+ const caps = { alwaysMatch: {}, firstMatch: [{}] };
50
+ inject(caps, WDA_URL);
51
+ (0, chai_1.expect)(caps.alwaysMatch['appium:usePreinstalledWDA']).to.equal(undefined);
52
+ });
53
+ it('does not leave a stale pairing behind from the caller', () => {
54
+ // A caller that already set the flag must not be able to smuggle it past
55
+ // the check when the bundle id is unknown.
56
+ const caps = {
57
+ alwaysMatch: { 'appium:usePreinstalledWDA': true, 'appium:updatedWDABundleId': 'stale' },
58
+ firstMatch: [{}],
59
+ };
60
+ inject(caps, WDA_URL, null);
61
+ (0, chai_1.expect)(caps.alwaysMatch['appium:usePreinstalledWDA']).to.equal(undefined);
62
+ (0, chai_1.expect)(caps.alwaysMatch['appium:updatedWDABundleId']).to.equal(undefined);
63
+ });
64
+ it('keeps the injected keys out of the other capability bucket', () => {
65
+ // W3C forbids a property in both alwaysMatch and a firstMatch entry;
66
+ // appium rejects the whole session if it happens.
67
+ const caps = {
68
+ alwaysMatch: {},
69
+ firstMatch: [{ 'appium:webDriverAgentUrl': 'http://stale', 'appium:usePreinstalledWDA': true }],
70
+ };
71
+ inject(caps, WDA_URL, BUNDLE);
72
+ (0, chai_1.expect)(caps.firstMatch[0]['appium:webDriverAgentUrl']).to.equal(undefined);
73
+ (0, chai_1.expect)(caps.firstMatch[0]['appium:usePreinstalledWDA']).to.equal(undefined);
74
+ (0, chai_1.expect)(caps.alwaysMatch['appium:webDriverAgentUrl']).to.equal(WDA_URL);
75
+ });
76
+ it('strips the caps that conflict with reusing a WDA by URL', () => {
77
+ const caps = {
78
+ alwaysMatch: { 'appium:wdaLocalPort': 8100, 'appium:derivedDataPath': '/tmp/dd' },
79
+ firstMatch: [{ 'appium:mjpegServerPort': 9100, 'appium:usePrebuiltWDA': true }],
80
+ };
81
+ inject(caps, WDA_URL, BUNDLE);
82
+ (0, chai_1.expect)(caps.alwaysMatch['appium:wdaLocalPort']).to.equal(undefined);
83
+ (0, chai_1.expect)(caps.alwaysMatch['appium:derivedDataPath']).to.equal(undefined);
84
+ (0, chai_1.expect)(caps.firstMatch[0]['appium:mjpegServerPort']).to.equal(undefined);
85
+ (0, chai_1.expect)(caps.firstMatch[0]['appium:usePrebuiltWDA']).to.equal(undefined);
86
+ });
87
+ });
@@ -1 +1 @@
1
- {"root":["../src/InternalHttpClient.ts","../src/PluginContext.ts","../src/XenonCapabilityManager.ts","../src/chromeUtils.ts","../src/config.ts","../src/device-utils.ts","../src/index.ts","../src/logger.ts","../src/plugin.ts","../src/prisma.ts","../src/app/index.ts","../src/app/swagger-docs.ts","../src/app/swagger.ts","../src/app/routers/androidH264Config.ts","../src/app/routers/apikeys.ts","../src/app/routers/apps.ts","../src/app/routers/audit.ts","../src/app/routers/auth.ts","../src/app/routers/bug-report.ts","../src/app/routers/build-export.ts","../src/app/routers/capabilities.ts","../src/app/routers/config.ts","../src/app/routers/control.ts","../src/app/routers/dashboard.ts","../src/app/routers/grid.ts","../src/app/routers/interceptor.ts","../src/app/routers/iosStreamPort.ts","../src/app/routers/ports.ts","../src/app/routers/processes.ts","../src/app/routers/profile.ts","../src/app/routers/profileIdentity.ts","../src/app/routers/projects.ts","../src/app/routers/recordings.ts","../src/app/routers/reservation.ts","../src/app/routers/sdk-leases.ts","../src/app/routers/sdk-version.ts","../src/app/routers/streamStartConflict.ts","../src/app/routers/streamType.ts","../src/app/routers/teams.ts","../src/app/routers/users.ts","../src/app/routers/webhook.ts","../src/app/ws/h264StreamWs.ts","../src/app/ws/logcatWs.ts","../src/commands/handle.ts","../src/commands/index.ts","../src/dashboard/asset-manager.ts","../src/dashboard/commands.ts","../src/dashboard/event-manager.ts","../src/dashboard/services/app-service.ts","../src/dashboard/services/failure-analysis-service.ts","../src/dashboard/services/session-service.ts","../src/data-service/CircuitBreaker.ts","../src/data-service/config-service.ts","../src/data-service/db.ts","../src/data-service/device-service.ts","../src/data-service/device-store.interface.ts","../src/data-service/device-store.ts","../src/data-service/pending-sessions-service.ts","../src/data-service/pluginArgs.ts","../src/data-service/prisma-service.ts","../src/data-service/prisma-store.ts","../src/data-service/queue-service.ts","../src/data-service/web-config-service.ts","../src/device-managers/AndroidDeviceManager.ts","../src/device-managers/ChromeDriverManager.ts","../src/device-managers/HealthMonitorService.ts","../src/device-managers/IOSDeviceManager.ts","../src/device-managers/NodeDevices.ts","../src/device-managers/iOSTracker.ts","../src/device-managers/index.ts","../src/device-managers/android/AndroidH264StreamService.ts","../src/device-managers/android/AndroidStreamService.ts","../src/device-managers/android/DeviceLockManager.ts","../src/device-managers/android/H264Multiplexer.ts","../src/device-managers/android/LogcatMultiplexer.ts","../src/device-managers/android/LogcatStreamService.ts","../src/device-managers/android/ScrcpyServerSession.ts","../src/device-managers/android/androidStreamReuse.ts","../src/device-managers/android/displayState.ts","../src/device-managers/android/h264NalParser.ts","../src/device-managers/android/scrcpyVersion.ts","../src/device-managers/cloud/CapabilityManager.ts","../src/device-managers/cloud/Devices.ts","../src/device-managers/ios/IOSDiscoveryService.ts","../src/device-managers/ios/IOSStreamService.ts","../src/device-managers/ios/WDAClient.ts","../src/device-managers/ios/iosBattery.ts","../src/device-managers/ios/iosStreamDiagnostics.ts","../src/device-managers/ios/tunnelProcess.ts","../src/enums/Capabilities.ts","../src/enums/Cloud.ts","../src/enums/Platform.ts","../src/enums/SessionType.ts","../src/enums/SocketEvents.ts","../src/generated/client/default.d.ts","../src/generated/client/default.js","../src/generated/client/edge.d.ts","../src/generated/client/edge.js","../src/generated/client/index-browser.js","../src/generated/client/index.d.ts","../src/generated/client/index.js","../src/generated/client/wasm.d.ts","../src/generated/client/wasm.js","../src/generated/client/runtime/edge-esm.js","../src/generated/client/runtime/edge.js","../src/generated/client/runtime/index-browser.d.ts","../src/generated/client/runtime/index-browser.js","../src/generated/client/runtime/library.d.ts","../src/generated/client/runtime/library.js","../src/generated/client/runtime/react-native.js","../src/generated/client/runtime/wasm.js","../src/helpers/UniversalMjpegProxy.ts","../src/helpers/ffmpegPath.ts","../src/helpers/index.ts","../src/helpers/networkAddresses.ts","../src/helpers/singleFlight.ts","../src/interceptors/CommandInterceptor.ts","../src/interfaces/ICloudManager.ts","../src/interfaces/IDevice.ts","../src/interfaces/IDeviceFilterOptions.ts","../src/interfaces/IDeviceManager.ts","../src/interfaces/IOptions.ts","../src/interfaces/IPluginArgs.ts","../src/interfaces/ISessionCapability.ts","../src/logging/sessionContext.ts","../src/middleware/authMiddleware.ts","../src/middleware/csrfMiddleware.ts","../src/middleware/deviceAccessGuard.ts","../src/middleware/leaseTokenMiddleware.ts","../src/middleware/loginRateLimiter.ts","../src/middleware/rateLimitMiddleware.ts","../src/middleware/roleGuard.ts","../src/middleware/scopeGuard.ts","../src/profiling/AndroidAppProfiler.ts","../src/proxy/wd-command-proxy.ts","../src/scripts/generate-database-migration.ts","../src/scripts/goIosVersion.ts","../src/scripts/initialize-database.ts","../src/scripts/install-go-ios.ts","../src/scripts/prepare-prisma.ts","../src/scripts/run-migrations.ts","../src/services/AICommandService.ts","../src/services/AIService.ts","../src/services/ApiKeyService.ts","../src/services/CircuitBreaker.ts","../src/services/CleanupService.ts","../src/services/DeviceReconciler.ts","../src/services/DisplayStateService.ts","../src/services/EmailService.ts","../src/services/EventBus.ts","../src/services/EventLogService.ts","../src/services/InspectorService.ts","../src/services/InterceptorService.ts","../src/services/MetricsService.ts","../src/services/NetworkConditioningService.ts","../src/services/NotificationService.ts","../src/services/OrphanSweeper.ts","../src/services/PasswordResetService.ts","../src/services/PortAllocator.ts","../src/services/ProcessMetricsService.ts","../src/services/ProcessRegistry.ts","../src/services/RequestLogService.ts","../src/services/ResourceIsolationService.ts","../src/services/SecurityService.ts","../src/services/SelectorStateService.ts","../src/services/SelectorVerificationJob.ts","../src/services/ServerManager.ts","../src/services/SessionHeartbeatService.ts","../src/services/SessionLifecycleService.ts","../src/services/ShutdownCoordinator.ts","../src/services/SocketClient.ts","../src/services/SocketServer.ts","../src/services/TeamService.ts","../src/services/TracingService.ts","../src/services/UserService.ts","../src/services/UserSessionService.ts","../src/services/VideoPipelineService.ts","../src/services/sessionTokenGate.ts","../src/services/artifacts/ArtifactStore.ts","../src/services/autowait/AutowaitService.ts","../src/services/autowait/waitFor.ts","../src/services/bug-report/BugReportService.ts","../src/services/bug-report/archive.ts","../src/services/bug-report/har-collector.ts","../src/services/bug-report/manifest.ts","../src/services/bug-report/readme.ts","../src/services/bug-report/types.ts","../src/services/bug-report/video-slice.ts","../src/services/bug-report/window.ts","../src/services/device-access/SessionOwnerResolver.ts","../src/services/device-access/actor.ts","../src/services/device-access/deviceAccessPolicy.ts","../src/services/device-access/ticketActorAccess.ts","../src/services/healing/FuzzyXmlHealingProvider.ts","../src/services/healing/HealEtalonService.ts","../src/services/healing/HealedLocatorGenerator.ts","../src/services/healing/HealingMetrics.ts","../src/services/healing/HealingOrchestrator.ts","../src/services/healing/LlmHealingProvider.ts","../src/services/healing/OcrHealingProvider.ts","../src/services/healing/ResilioTreeHealingProvider.ts","../src/services/healing/VisualAiHealingProvider.ts","../src/services/healing/types.ts","../src/services/identity/bootstrap.ts","../src/services/identity/sessionCleanupCron.ts","../src/services/identity/userAuthorization.ts","../src/services/inspector/verifyLocator.ts","../src/services/interceptor/AndroidProxyAdapter.ts","../src/services/interceptor/CertManager.ts","../src/services/interceptor/HarBuilder.ts","../src/services/interceptor/HostFilter.ts","../src/services/interceptor/MitmProxyHost.ts","../src/services/interceptor/MockEngine.ts","../src/services/interceptor/RequestBuffer.ts","../src/services/interceptor/SessionArchive.ts","../src/services/interceptor/types.ts","../src/services/lease/LeaseOrphanSweeper.ts","../src/services/lease/LeaseService.ts","../src/services/lease/buildCapabilityBag.ts","../src/services/lease/leaseToken.ts","../src/services/logcat/PackageResolver.ts","../src/services/logcat/logcatParse.ts","../src/services/omni-vision/OmniVisionService.ts","../src/services/omni-vision/VisionAssertionService.ts","../src/services/ports/PortAllocatorClient.ts","../src/services/ports/PortAllocatorService.ts","../src/services/recording/RecordingOrchestrator.ts","../src/services/recording/annotation-render.ts","../src/services/recording/busy-precheck.ts","../src/services/recording/concurrency-gate.ts","../src/services/recording/ensureMjpegForRecording.ts","../src/services/recording/manualLock.ts","../src/services/recording/probeDuration.ts","../src/services/recording/proof-bundle.ts","../src/services/recording/recording-store.ts","../src/services/recording/recordingCleanup.ts","../src/services/session/sessionIdentity.ts","../src/services/telemetry/attributes.ts","../src/services/token/JwtKeyService.ts","../src/services/token/StreamTicketService.ts","../src/services/token/mcpScopes.ts","../src/sessions/CloudSession.ts","../src/sessions/LocalSession.ts","../src/sessions/RemoteSession.ts","../src/sessions/SessionManager.ts","../src/sessions/XenonSession.ts","../src/types/CLIArgs.ts","../src/types/CloudArgs.ts","../src/types/CloudSchema.ts","../src/types/DeviceType.ts","../src/types/DeviceUpdate.ts","../src/types/IOSDevice.ts","../src/types/Platform.ts","../src/types/SessionStatus.ts","../src/types/express.d.ts","../src/types/healing.d.ts","../src/types/identity.ts","../src/validators/CapabilityValidator.ts","../test/e2e/e2ehelper.ts","../test/e2e/plugin-harness.ts","../test/e2e/plugin.spec.ts","../test/e2e/startup-verification.spec.ts","../test/e2e/telemetry_verification.spec.ts","../test/e2e/video-recording-test.spec.js","../test/e2e/android/conf.spec.js","../test/e2e/android/conf2.spec.js","../test/e2e/android/conf3.spec.js","../test/e2e/hubnode/forward-request.spec.ts","../test/e2e/hubnode/hubnode.spec.ts","../test/e2e/ios/conf1.spec.js","../test/e2e/ios/conf2.spec.js","../test/helpers/seedUser.ts","../test/helpers/test-container.ts","../test/integration/androidDevices.spec.ts","../test/integration/audit-ingest.spec.ts","../test/integration/auth-flow.spec.ts","../test/integration/auth-rate-limit.spec.ts","../test/integration/bug-report-route.spec.ts","../test/integration/cliArgs.js","../test/integration/cross-workflow.spec.ts","../test/integration/forgot-password-rate-limit.spec.ts","../test/integration/forgot-password.spec.ts","../test/integration/healing-events-sessionfilter.spec.ts","../test/integration/legacy-deprecation-headers.spec.ts","../test/integration/migration.spec.ts","../test/integration/node-pair-auth-rest.spec.ts","../test/integration/ports-router.spec.ts","../test/integration/profile-tokens.spec.ts","../test/integration/recordings-router.spec.ts","../test/integration/reset-revokes-sessions.spec.ts","../test/integration/role-matrix.spec.ts","../test/integration/sdk-leases-router.spec.ts","../test/integration/sdk-version.spec.ts","../test/integration/selector-health-endpoint.spec.ts","../test/integration/selector-state-flow.spec.ts","../test/integration/team-membership-migration.spec.ts","../test/integration/team-visibility-dashboard.spec.ts","../test/integration/team-visibility-grid.spec.ts","../test/integration/team-visibility-recordings.spec.ts","../test/integration/users-flow.spec.ts","../test/integration/users-lockout.spec.ts","../test/integration/ios/01iOSSimulator.spec.ts","../test/integration/ios/02iOSDevices.spec.ts","../test/unit/AndroidDeviceManager.spec.ts","../test/unit/ApiKeyService.test.ts","../test/unit/ArtifactStore.spec.ts","../test/unit/AutowaitService.spec.ts","../test/unit/CapabilitiesRouter.spec.ts","../test/unit/ChromeDriverManager.spec.js","../test/unit/CleanupService.spec.ts","../test/unit/CommandInterceptor.autowait.spec.ts","../test/unit/DeviceModel.spec.ts","../test/unit/EmailService.test.ts","../test/unit/EventLogService.spec.ts","../test/unit/FuzzyXmlHealingProvider.test.ts","../test/unit/GetAdbOriginal.js","../test/unit/HealingCascade.test.ts","../test/unit/IOSDeviceManager.spec.js","../test/unit/JwtKeyService.spec.ts","../test/unit/McpScopes.spec.ts","../test/unit/OrphanSweeper.test.ts","../test/unit/PasswordResetService.test.ts","../test/unit/PortAllocator.test.ts","../test/unit/ProcessRegistry.test.ts","../test/unit/ProjectsRouter.spec.ts","../test/unit/RemoteIOs.spec.js","../test/unit/ResilioTreeHealingProvider.test.ts","../test/unit/SessionHeartbeatWrite.test.ts","../test/unit/SessionLifecycleCaps.spec.ts","../test/unit/SessionTokenGate.spec.ts","../test/unit/StreamTicketService.spec.ts","../test/unit/TeamService.test.ts","../test/unit/TokenEndpoint.spec.ts","../test/unit/UserService.test.ts","../test/unit/UserSessionService.test.ts","../test/unit/XenonCapabilityManager.spec.ts","../test/unit/aggregate-hotspots-tuple-key.spec.ts","../test/unit/android-h264-config.spec.ts","../test/unit/android-h264-service.spec.ts","../test/unit/android-stream-adb-path.spec.ts","../test/unit/android-stream-encoder.spec.ts","../test/unit/android-stream-fast-start.spec.ts","../test/unit/android-stream-release.spec.ts","../test/unit/android-stream-reuse-service.spec.ts","../test/unit/android-stream-reuse.spec.ts","../test/unit/android-stream-stall-eof.spec.ts","../test/unit/annotation-render.spec.ts","../test/unit/auth-disabled-resolution.spec.ts","../test/unit/auth-me-auth-disabled.test.ts","../test/unit/authMiddleware.bearer.spec.ts","../test/unit/authMiddleware.mcpAudience.spec.ts","../test/unit/authMiddleware.streamTicket.spec.ts","../test/unit/authMiddleware.test.ts","../test/unit/bootstrap-identity.test.ts","../test/unit/build-export.spec.ts","../test/unit/busy-precheck.spec.ts","../test/unit/commands.spec.js","../test/unit/concurrency-gate.spec.ts","../test/unit/config.spec.js","../test/unit/device-access-guard.spec.ts","../test/unit/device-access-policy.spec.ts","../test/unit/device-service.spec.ts","../test/unit/device-utils.spec.ts","../test/unit/display-state.spec.ts","../test/unit/enrich-devices-team-names.test.ts","../test/unit/event-manager-regression-hook.spec.ts","../test/unit/extract-access-key-token-pair.test.ts","../test/unit/ffmpeg-path.spec.ts","../test/unit/findAndLockDevice-leasemerge.spec.ts","../test/unit/go-ios-version.spec.ts","../test/unit/h264-multiplexer.spec.ts","../test/unit/h264-nal-parser.spec.ts","../test/unit/h264-ws-frame.spec.ts","../test/unit/healing-instrumentation.spec.ts","../test/unit/healing-state-endpoints.spec.ts","../test/unit/healing-tier-gate.spec.ts","../test/unit/helpers.spec.ts","../test/unit/inspector-hierarchy.spec.ts","../test/unit/ios-stream-port.spec.ts","../test/unit/ios-tunnel-process.spec.ts","../test/unit/iosBattery.spec.ts","../test/unit/iosStreamDiagnostics.spec.ts","../test/unit/logcat-multiplexer.spec.ts","../test/unit/logcat-parse.spec.ts","../test/unit/logcat-stream-service.spec.ts","../test/unit/logcat-ws.spec.ts","../test/unit/loginRateLimiter.test.ts","../test/unit/manualLock.test.ts","../test/unit/mjpeg-proxy-cache.spec.ts","../test/unit/network-addresses.test.ts","../test/unit/normalize-eol.spec.js","../test/unit/omni-vision.spec.ts","../test/unit/package-resolver.spec.ts","../test/unit/plugin.spec.ts","../test/unit/prisma.spec.ts","../test/unit/probe-duration.spec.ts","../test/unit/profile-router.test.ts","../test/unit/profileIdentity.spec.ts","../test/unit/proof-bundle.spec.ts","../test/unit/rateLimitMiddleware.test.ts","../test/unit/recording-cleanup.spec.ts","../test/unit/recording-instrumentation.spec.ts","../test/unit/recording-orchestrator.spec.ts","../test/unit/recording-source-ended.spec.ts","../test/unit/recording-store.spec.ts","../test/unit/remux-to-standard-mp4.spec.ts","../test/unit/roleGuard.test.ts","../test/unit/run-migrations-fail-fast.test.ts","../test/unit/run-migrations.test.ts","../test/unit/schema-required-args.spec.ts","../test/unit/scrcpy-server-session.spec.ts","../test/unit/scrcpy-version.spec.ts","../test/unit/selector-state-service.spec.ts","../test/unit/selector-verification-job.spec.ts","../test/unit/session-attribution-wiring.spec.ts","../test/unit/session-identity.spec.ts","../test/unit/session-owner-resolver.spec.ts","../test/unit/shutdown-sync-nets.spec.ts","../test/unit/single-flight.spec.ts","../test/unit/socketServer.bearer.spec.ts","../test/unit/stream-start-conflict.spec.ts","../test/unit/stream-status-type.spec.ts","../test/unit/stream-stop-ownership.spec.ts","../test/unit/stream-ticket-identity.spec.ts","../test/unit/team-filter.test.ts","../test/unit/tracing-service-init.spec.ts","../test/unit/userAuthorization.test.ts","../test/unit/users-router.test.ts","../test/unit/verify-locator.spec.ts","../test/unit/video-pipeline-args.spec.ts","../test/unit/video-pipeline-exit.spec.ts","../test/unit/video-pipeline-outputpath.spec.ts","../test/unit/bug-report/archive.spec.ts","../test/unit/bug-report/har-collector.spec.ts","../test/unit/bug-report/manifest.spec.ts","../test/unit/bug-report/readme.spec.ts","../test/unit/bug-report/route.spec.ts","../test/unit/bug-report/service.spec.ts","../test/unit/bug-report/video-slice.spec.ts","../test/unit/bug-report/window.spec.ts","../test/unit/fixtures/device.config.js","../test/unit/fixtures/devices.js","../test/unit/interceptor/AndroidProxyAdapter.spec.ts","../test/unit/interceptor/CertManager.spec.ts","../test/unit/interceptor/HarBuilder.spec.ts","../test/unit/interceptor/HostFilter.spec.ts","../test/unit/interceptor/MitmProxyHost.spec.ts","../test/unit/interceptor/MockEngine.spec.ts","../test/unit/interceptor/RequestBuffer.spec.ts","../test/unit/interceptor/SessionArchive.spec.ts","../test/unit/lease/LeaseOrphanSweeper.spec.ts","../test/unit/lease/LeaseService.spec.ts","../test/unit/lease/buildCapabilityBag.spec.ts","../test/unit/lease/leaseToken.spec.ts","../test/unit/lease/nodePairAuth.spec.ts","../test/unit/ports/PortAllocatorService.spec.ts"],"version":"5.9.3"}
1
+ {"root":["../src/InternalHttpClient.ts","../src/PluginContext.ts","../src/XenonCapabilityManager.ts","../src/chromeUtils.ts","../src/config.ts","../src/device-utils.ts","../src/index.ts","../src/logger.ts","../src/plugin.ts","../src/prisma.ts","../src/app/index.ts","../src/app/swagger-docs.ts","../src/app/swagger.ts","../src/app/routers/androidH264Config.ts","../src/app/routers/apikeys.ts","../src/app/routers/apps.ts","../src/app/routers/audit.ts","../src/app/routers/auth.ts","../src/app/routers/bug-report.ts","../src/app/routers/build-export.ts","../src/app/routers/capabilities.ts","../src/app/routers/config.ts","../src/app/routers/control.ts","../src/app/routers/dashboard.ts","../src/app/routers/grid.ts","../src/app/routers/interceptor.ts","../src/app/routers/iosStreamPort.ts","../src/app/routers/ports.ts","../src/app/routers/processes.ts","../src/app/routers/profile.ts","../src/app/routers/profileIdentity.ts","../src/app/routers/projects.ts","../src/app/routers/recordings.ts","../src/app/routers/reservation.ts","../src/app/routers/sdk-leases.ts","../src/app/routers/sdk-version.ts","../src/app/routers/streamStartConflict.ts","../src/app/routers/streamType.ts","../src/app/routers/teams.ts","../src/app/routers/users.ts","../src/app/routers/webhook.ts","../src/app/ws/h264StreamWs.ts","../src/app/ws/logcatWs.ts","../src/commands/handle.ts","../src/commands/index.ts","../src/dashboard/asset-manager.ts","../src/dashboard/commands.ts","../src/dashboard/event-manager.ts","../src/dashboard/services/app-service.ts","../src/dashboard/services/failure-analysis-service.ts","../src/dashboard/services/session-service.ts","../src/data-service/CircuitBreaker.ts","../src/data-service/config-service.ts","../src/data-service/db.ts","../src/data-service/device-service.ts","../src/data-service/device-store.interface.ts","../src/data-service/device-store.ts","../src/data-service/pending-sessions-service.ts","../src/data-service/pluginArgs.ts","../src/data-service/prisma-service.ts","../src/data-service/prisma-store.ts","../src/data-service/queue-service.ts","../src/data-service/web-config-service.ts","../src/device-managers/AndroidDeviceManager.ts","../src/device-managers/ChromeDriverManager.ts","../src/device-managers/HealthMonitorService.ts","../src/device-managers/IOSDeviceManager.ts","../src/device-managers/NodeDevices.ts","../src/device-managers/iOSTracker.ts","../src/device-managers/index.ts","../src/device-managers/android/AndroidH264StreamService.ts","../src/device-managers/android/AndroidStreamService.ts","../src/device-managers/android/DeviceLockManager.ts","../src/device-managers/android/H264Multiplexer.ts","../src/device-managers/android/LogcatMultiplexer.ts","../src/device-managers/android/LogcatStreamService.ts","../src/device-managers/android/ScrcpyServerSession.ts","../src/device-managers/android/androidStreamReuse.ts","../src/device-managers/android/displayState.ts","../src/device-managers/android/h264NalParser.ts","../src/device-managers/android/scrcpyVersion.ts","../src/device-managers/cloud/CapabilityManager.ts","../src/device-managers/cloud/Devices.ts","../src/device-managers/ios/IOSDiscoveryService.ts","../src/device-managers/ios/IOSStreamService.ts","../src/device-managers/ios/WDAClient.ts","../src/device-managers/ios/iosBattery.ts","../src/device-managers/ios/iosStreamDiagnostics.ts","../src/device-managers/ios/tunnelProcess.ts","../src/enums/Capabilities.ts","../src/enums/Cloud.ts","../src/enums/Platform.ts","../src/enums/SessionType.ts","../src/enums/SocketEvents.ts","../src/generated/client/default.d.ts","../src/generated/client/default.js","../src/generated/client/edge.d.ts","../src/generated/client/edge.js","../src/generated/client/index-browser.js","../src/generated/client/index.d.ts","../src/generated/client/index.js","../src/generated/client/wasm.d.ts","../src/generated/client/wasm.js","../src/generated/client/runtime/edge-esm.js","../src/generated/client/runtime/edge.js","../src/generated/client/runtime/index-browser.d.ts","../src/generated/client/runtime/index-browser.js","../src/generated/client/runtime/library.d.ts","../src/generated/client/runtime/library.js","../src/generated/client/runtime/react-native.js","../src/generated/client/runtime/wasm.js","../src/helpers/UniversalMjpegProxy.ts","../src/helpers/ffmpegPath.ts","../src/helpers/index.ts","../src/helpers/networkAddresses.ts","../src/helpers/singleFlight.ts","../src/interceptors/CommandInterceptor.ts","../src/interfaces/ICloudManager.ts","../src/interfaces/IDevice.ts","../src/interfaces/IDeviceFilterOptions.ts","../src/interfaces/IDeviceManager.ts","../src/interfaces/IOptions.ts","../src/interfaces/IPluginArgs.ts","../src/interfaces/ISessionCapability.ts","../src/logging/sessionContext.ts","../src/middleware/authMiddleware.ts","../src/middleware/csrfMiddleware.ts","../src/middleware/deviceAccessGuard.ts","../src/middleware/leaseTokenMiddleware.ts","../src/middleware/loginRateLimiter.ts","../src/middleware/rateLimitMiddleware.ts","../src/middleware/roleGuard.ts","../src/middleware/scopeGuard.ts","../src/profiling/AndroidAppProfiler.ts","../src/proxy/wd-command-proxy.ts","../src/scripts/generate-database-migration.ts","../src/scripts/goIosVersion.ts","../src/scripts/initialize-database.ts","../src/scripts/install-go-ios.ts","../src/scripts/prepare-prisma.ts","../src/scripts/run-migrations.ts","../src/services/AICommandService.ts","../src/services/AIService.ts","../src/services/ApiKeyService.ts","../src/services/CircuitBreaker.ts","../src/services/CleanupService.ts","../src/services/DeviceReconciler.ts","../src/services/DisplayStateService.ts","../src/services/EmailService.ts","../src/services/EventBus.ts","../src/services/EventLogService.ts","../src/services/InspectorService.ts","../src/services/InterceptorService.ts","../src/services/MetricsService.ts","../src/services/NetworkConditioningService.ts","../src/services/NotificationService.ts","../src/services/OrphanSweeper.ts","../src/services/PasswordResetService.ts","../src/services/PortAllocator.ts","../src/services/ProcessMetricsService.ts","../src/services/ProcessRegistry.ts","../src/services/RequestLogService.ts","../src/services/ResourceIsolationService.ts","../src/services/SecurityService.ts","../src/services/SelectorStateService.ts","../src/services/SelectorVerificationJob.ts","../src/services/ServerManager.ts","../src/services/SessionHeartbeatService.ts","../src/services/SessionLifecycleService.ts","../src/services/ShutdownCoordinator.ts","../src/services/SocketClient.ts","../src/services/SocketServer.ts","../src/services/TeamService.ts","../src/services/TracingService.ts","../src/services/UserService.ts","../src/services/UserSessionService.ts","../src/services/VideoPipelineService.ts","../src/services/sessionTokenGate.ts","../src/services/artifacts/ArtifactStore.ts","../src/services/autowait/AutowaitService.ts","../src/services/autowait/waitFor.ts","../src/services/bug-report/BugReportService.ts","../src/services/bug-report/archive.ts","../src/services/bug-report/har-collector.ts","../src/services/bug-report/manifest.ts","../src/services/bug-report/readme.ts","../src/services/bug-report/types.ts","../src/services/bug-report/video-slice.ts","../src/services/bug-report/window.ts","../src/services/device-access/SessionOwnerResolver.ts","../src/services/device-access/actor.ts","../src/services/device-access/deviceAccessPolicy.ts","../src/services/device-access/ticketActorAccess.ts","../src/services/healing/FuzzyXmlHealingProvider.ts","../src/services/healing/HealEtalonService.ts","../src/services/healing/HealedLocatorGenerator.ts","../src/services/healing/HealingMetrics.ts","../src/services/healing/HealingOrchestrator.ts","../src/services/healing/LlmHealingProvider.ts","../src/services/healing/OcrHealingProvider.ts","../src/services/healing/ResilioTreeHealingProvider.ts","../src/services/healing/VisualAiHealingProvider.ts","../src/services/healing/types.ts","../src/services/identity/bootstrap.ts","../src/services/identity/sessionCleanupCron.ts","../src/services/identity/userAuthorization.ts","../src/services/inspector/verifyLocator.ts","../src/services/interceptor/AndroidProxyAdapter.ts","../src/services/interceptor/CertManager.ts","../src/services/interceptor/HarBuilder.ts","../src/services/interceptor/HostFilter.ts","../src/services/interceptor/MitmProxyHost.ts","../src/services/interceptor/MockEngine.ts","../src/services/interceptor/RequestBuffer.ts","../src/services/interceptor/SessionArchive.ts","../src/services/interceptor/types.ts","../src/services/lease/LeaseOrphanSweeper.ts","../src/services/lease/LeaseService.ts","../src/services/lease/buildCapabilityBag.ts","../src/services/lease/leaseToken.ts","../src/services/logcat/PackageResolver.ts","../src/services/logcat/logcatParse.ts","../src/services/omni-vision/OmniVisionService.ts","../src/services/omni-vision/VisionAssertionService.ts","../src/services/ports/PortAllocatorClient.ts","../src/services/ports/PortAllocatorService.ts","../src/services/recording/RecordingOrchestrator.ts","../src/services/recording/annotation-render.ts","../src/services/recording/busy-precheck.ts","../src/services/recording/concurrency-gate.ts","../src/services/recording/ensureMjpegForRecording.ts","../src/services/recording/manualLock.ts","../src/services/recording/probeDuration.ts","../src/services/recording/proof-bundle.ts","../src/services/recording/recording-store.ts","../src/services/recording/recordingCleanup.ts","../src/services/session/sessionIdentity.ts","../src/services/telemetry/attributes.ts","../src/services/token/JwtKeyService.ts","../src/services/token/StreamTicketService.ts","../src/services/token/mcpScopes.ts","../src/sessions/CloudSession.ts","../src/sessions/LocalSession.ts","../src/sessions/RemoteSession.ts","../src/sessions/SessionManager.ts","../src/sessions/XenonSession.ts","../src/types/CLIArgs.ts","../src/types/CloudArgs.ts","../src/types/CloudSchema.ts","../src/types/DeviceType.ts","../src/types/DeviceUpdate.ts","../src/types/IOSDevice.ts","../src/types/Platform.ts","../src/types/SessionStatus.ts","../src/types/express.d.ts","../src/types/healing.d.ts","../src/types/identity.ts","../src/validators/CapabilityValidator.ts","../test/e2e/e2ehelper.ts","../test/e2e/plugin-harness.ts","../test/e2e/plugin.spec.ts","../test/e2e/startup-verification.spec.ts","../test/e2e/telemetry_verification.spec.ts","../test/e2e/video-recording-test.spec.js","../test/e2e/android/conf.spec.js","../test/e2e/android/conf2.spec.js","../test/e2e/android/conf3.spec.js","../test/e2e/hubnode/forward-request.spec.ts","../test/e2e/hubnode/hubnode.spec.ts","../test/e2e/ios/conf1.spec.js","../test/e2e/ios/conf2.spec.js","../test/helpers/seedUser.ts","../test/helpers/test-container.ts","../test/integration/androidDevices.spec.ts","../test/integration/audit-ingest.spec.ts","../test/integration/auth-flow.spec.ts","../test/integration/auth-rate-limit.spec.ts","../test/integration/bug-report-route.spec.ts","../test/integration/cliArgs.js","../test/integration/cross-workflow.spec.ts","../test/integration/forgot-password-rate-limit.spec.ts","../test/integration/forgot-password.spec.ts","../test/integration/healing-events-sessionfilter.spec.ts","../test/integration/legacy-deprecation-headers.spec.ts","../test/integration/migration.spec.ts","../test/integration/node-pair-auth-rest.spec.ts","../test/integration/ports-router.spec.ts","../test/integration/profile-tokens.spec.ts","../test/integration/recordings-router.spec.ts","../test/integration/reset-revokes-sessions.spec.ts","../test/integration/role-matrix.spec.ts","../test/integration/sdk-leases-router.spec.ts","../test/integration/sdk-version.spec.ts","../test/integration/selector-health-endpoint.spec.ts","../test/integration/selector-state-flow.spec.ts","../test/integration/team-membership-migration.spec.ts","../test/integration/team-visibility-dashboard.spec.ts","../test/integration/team-visibility-grid.spec.ts","../test/integration/team-visibility-recordings.spec.ts","../test/integration/users-flow.spec.ts","../test/integration/users-lockout.spec.ts","../test/integration/ios/01iOSSimulator.spec.ts","../test/integration/ios/02iOSDevices.spec.ts","../test/unit/AndroidDeviceManager.spec.ts","../test/unit/ApiKeyService.test.ts","../test/unit/ArtifactStore.spec.ts","../test/unit/AutowaitService.spec.ts","../test/unit/CapabilitiesRouter.spec.ts","../test/unit/ChromeDriverManager.spec.js","../test/unit/CleanupService.spec.ts","../test/unit/CommandInterceptor.autowait.spec.ts","../test/unit/DeviceModel.spec.ts","../test/unit/EmailService.test.ts","../test/unit/EventLogService.spec.ts","../test/unit/FuzzyXmlHealingProvider.test.ts","../test/unit/GetAdbOriginal.js","../test/unit/HealingCascade.test.ts","../test/unit/IOSDeviceManager.spec.js","../test/unit/JwtKeyService.spec.ts","../test/unit/McpScopes.spec.ts","../test/unit/OrphanSweeper.test.ts","../test/unit/PasswordResetService.test.ts","../test/unit/PortAllocator.test.ts","../test/unit/ProcessRegistry.test.ts","../test/unit/ProjectsRouter.spec.ts","../test/unit/RemoteIOs.spec.js","../test/unit/ResilioTreeHealingProvider.test.ts","../test/unit/SessionHeartbeatWrite.test.ts","../test/unit/SessionLifecycleCaps.spec.ts","../test/unit/SessionTokenGate.spec.ts","../test/unit/StreamTicketService.spec.ts","../test/unit/TeamService.test.ts","../test/unit/TokenEndpoint.spec.ts","../test/unit/UserService.test.ts","../test/unit/UserSessionService.test.ts","../test/unit/XenonCapabilityManager.spec.ts","../test/unit/aggregate-hotspots-tuple-key.spec.ts","../test/unit/android-h264-config.spec.ts","../test/unit/android-h264-service.spec.ts","../test/unit/android-stream-adb-path.spec.ts","../test/unit/android-stream-encoder.spec.ts","../test/unit/android-stream-fast-start.spec.ts","../test/unit/android-stream-release.spec.ts","../test/unit/android-stream-reuse-service.spec.ts","../test/unit/android-stream-reuse.spec.ts","../test/unit/android-stream-stall-eof.spec.ts","../test/unit/annotation-render.spec.ts","../test/unit/auth-disabled-resolution.spec.ts","../test/unit/auth-me-auth-disabled.test.ts","../test/unit/authMiddleware.bearer.spec.ts","../test/unit/authMiddleware.mcpAudience.spec.ts","../test/unit/authMiddleware.streamTicket.spec.ts","../test/unit/authMiddleware.test.ts","../test/unit/bootstrap-identity.test.ts","../test/unit/build-export.spec.ts","../test/unit/busy-precheck.spec.ts","../test/unit/commands.spec.js","../test/unit/concurrency-gate.spec.ts","../test/unit/config.spec.js","../test/unit/device-access-guard.spec.ts","../test/unit/device-access-policy.spec.ts","../test/unit/device-service.spec.ts","../test/unit/device-utils.spec.ts","../test/unit/display-state.spec.ts","../test/unit/enrich-devices-team-names.test.ts","../test/unit/event-manager-regression-hook.spec.ts","../test/unit/extract-access-key-token-pair.test.ts","../test/unit/ffmpeg-path.spec.ts","../test/unit/findAndLockDevice-leasemerge.spec.ts","../test/unit/go-ios-version.spec.ts","../test/unit/h264-multiplexer.spec.ts","../test/unit/h264-nal-parser.spec.ts","../test/unit/h264-ws-frame.spec.ts","../test/unit/healing-instrumentation.spec.ts","../test/unit/healing-state-endpoints.spec.ts","../test/unit/healing-tier-gate.spec.ts","../test/unit/helpers.spec.ts","../test/unit/inspector-hierarchy.spec.ts","../test/unit/ios-stream-port.spec.ts","../test/unit/ios-tunnel-process.spec.ts","../test/unit/iosBattery.spec.ts","../test/unit/iosStreamDiagnostics.spec.ts","../test/unit/logcat-multiplexer.spec.ts","../test/unit/logcat-parse.spec.ts","../test/unit/logcat-stream-service.spec.ts","../test/unit/logcat-ws.spec.ts","../test/unit/loginRateLimiter.test.ts","../test/unit/manualLock.test.ts","../test/unit/mjpeg-proxy-cache.spec.ts","../test/unit/network-addresses.test.ts","../test/unit/normalize-eol.spec.js","../test/unit/omni-vision.spec.ts","../test/unit/package-resolver.spec.ts","../test/unit/plugin.spec.ts","../test/unit/port-exhaustion.spec.ts","../test/unit/prisma.spec.ts","../test/unit/probe-duration.spec.ts","../test/unit/profile-router.test.ts","../test/unit/profileIdentity.spec.ts","../test/unit/proof-bundle.spec.ts","../test/unit/rateLimitMiddleware.test.ts","../test/unit/recording-cleanup.spec.ts","../test/unit/recording-instrumentation.spec.ts","../test/unit/recording-orchestrator.spec.ts","../test/unit/recording-source-ended.spec.ts","../test/unit/recording-store.spec.ts","../test/unit/remux-to-standard-mp4.spec.ts","../test/unit/roleGuard.test.ts","../test/unit/run-migrations-fail-fast.test.ts","../test/unit/run-migrations.test.ts","../test/unit/schema-required-args.spec.ts","../test/unit/scrcpy-server-session.spec.ts","../test/unit/scrcpy-version.spec.ts","../test/unit/selector-state-service.spec.ts","../test/unit/selector-verification-job.spec.ts","../test/unit/session-attribution-wiring.spec.ts","../test/unit/session-identity.spec.ts","../test/unit/session-owner-resolver.spec.ts","../test/unit/shutdown-sync-nets.spec.ts","../test/unit/single-flight.spec.ts","../test/unit/socketServer.bearer.spec.ts","../test/unit/stream-start-conflict.spec.ts","../test/unit/stream-status-type.spec.ts","../test/unit/stream-stop-ownership.spec.ts","../test/unit/stream-ticket-identity.spec.ts","../test/unit/team-filter.test.ts","../test/unit/tracing-service-init.spec.ts","../test/unit/userAuthorization.test.ts","../test/unit/users-router.test.ts","../test/unit/verify-locator.spec.ts","../test/unit/video-pipeline-args.spec.ts","../test/unit/video-pipeline-exit.spec.ts","../test/unit/video-pipeline-outputpath.spec.ts","../test/unit/wda-cap-injection.spec.ts","../test/unit/bug-report/archive.spec.ts","../test/unit/bug-report/har-collector.spec.ts","../test/unit/bug-report/manifest.spec.ts","../test/unit/bug-report/readme.spec.ts","../test/unit/bug-report/route.spec.ts","../test/unit/bug-report/service.spec.ts","../test/unit/bug-report/video-slice.spec.ts","../test/unit/bug-report/window.spec.ts","../test/unit/fixtures/device.config.js","../test/unit/fixtures/devices.js","../test/unit/interceptor/AndroidProxyAdapter.spec.ts","../test/unit/interceptor/CertManager.spec.ts","../test/unit/interceptor/HarBuilder.spec.ts","../test/unit/interceptor/HostFilter.spec.ts","../test/unit/interceptor/MitmProxyHost.spec.ts","../test/unit/interceptor/MockEngine.spec.ts","../test/unit/interceptor/RequestBuffer.spec.ts","../test/unit/interceptor/SessionArchive.spec.ts","../test/unit/lease/LeaseOrphanSweeper.spec.ts","../test/unit/lease/LeaseService.spec.ts","../test/unit/lease/buildCapabilityBag.spec.ts","../test/unit/lease/leaseToken.spec.ts","../test/unit/lease/nodePairAuth.spec.ts","../test/unit/ports/PortAllocatorService.spec.ts"],"version":"5.9.3"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xenon-device-management/xenon",
3
- "version": "1.17.0",
3
+ "version": "1.17.2",
4
4
  "description": "Xenon - Intelligent Mobile Infrastructure. A self-healing device orchestration platform for Appium.",
5
5
  "main": "./lib/src/index.js",
6
6
  "exports": {