@houwert/conductor 0.16.0 → 0.17.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.
@@ -0,0 +1,300 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.HELP = void 0;
7
+ exports.profileCpu = profileCpu;
8
+ exports.profileMemory = profileMemory;
9
+ exports.profileReactStart = profileReactStart;
10
+ exports.profileReactStop = profileReactStop;
11
+ exports.HELP = ` profile cpu --duration <s> [--out <path>]
12
+ Record a CPU trace (iOS: xctrace, Android: simpleperf)
13
+ profile memory --track <s> [--interval <ms>] [<appId>]
14
+ Sample memory for N seconds, report deltas
15
+ profile react start Install a React commit-profiler hook in the JS runtime
16
+ profile react stop [--top N] Stop and summarise captured React commits`;
17
+ const child_process_1 = require("child_process");
18
+ const os_1 = __importDefault(require("os"));
19
+ const path_1 = __importDefault(require("path"));
20
+ const output_js_1 = require("../output.js");
21
+ const bootstrap_js_1 = require("../drivers/bootstrap.js");
22
+ const sdk_js_1 = require("../android/sdk.js");
23
+ const memory_js_1 = require("./memory.js");
24
+ const metro_cdp_js_1 = require("../drivers/metro-cdp.js");
25
+ function defaultTracePath(prefix, ext) {
26
+ const ts = new Date().toISOString().replace(/[:.]/g, '-');
27
+ return path_1.default.join(os_1.default.tmpdir(), `${prefix}-${ts}.${ext}`);
28
+ }
29
+ async function recordIosCpu(deviceId, appId, durationSec, out) {
30
+ const args = [
31
+ 'xctrace',
32
+ 'record',
33
+ '--template',
34
+ 'Time Profiler',
35
+ '--device',
36
+ deviceId,
37
+ '--time-limit',
38
+ `${durationSec}s`,
39
+ '--output',
40
+ out,
41
+ ];
42
+ if (appId)
43
+ args.push('--attach', appId);
44
+ await new Promise((resolve, reject) => {
45
+ const proc = (0, child_process_1.spawn)('xcrun', args, { stdio: 'inherit' });
46
+ proc.on('close', (code) => code === 0 ? resolve() : reject(new Error(`xctrace exited with code ${code}`)));
47
+ proc.on('error', reject);
48
+ });
49
+ }
50
+ async function recordAndroidCpu(deviceId, appId, durationSec, out) {
51
+ const adb = (0, sdk_js_1.resolveAndroidTool)('adb');
52
+ const env = (0, sdk_js_1.androidSpawnEnv)();
53
+ const remote = `/data/local/tmp/conductor-perf-${Date.now()}.data`;
54
+ const recordArgs = [
55
+ '-s',
56
+ deviceId,
57
+ 'shell',
58
+ 'simpleperf',
59
+ 'record',
60
+ '-o',
61
+ remote,
62
+ '--duration',
63
+ String(durationSec),
64
+ ];
65
+ if (appId) {
66
+ recordArgs.push('--app', appId);
67
+ }
68
+ else {
69
+ recordArgs.push('-a');
70
+ }
71
+ await new Promise((resolve, reject) => {
72
+ const proc = (0, child_process_1.spawn)(adb, recordArgs, { stdio: 'inherit', env });
73
+ proc.on('close', (code) => code === 0 ? resolve() : reject(new Error(`simpleperf record exited with ${code}`)));
74
+ proc.on('error', reject);
75
+ });
76
+ await new Promise((resolve, reject) => {
77
+ const proc = (0, child_process_1.spawn)(adb, ['-s', deviceId, 'pull', remote, out], { stdio: 'inherit', env });
78
+ proc.on('close', (code) => code === 0 ? resolve() : reject(new Error(`adb pull exited with ${code}`)));
79
+ proc.on('error', reject);
80
+ });
81
+ await new Promise((resolve) => {
82
+ const proc = (0, child_process_1.spawn)(adb, ['-s', deviceId, 'shell', 'rm', remote], { stdio: 'ignore', env });
83
+ proc.on('close', () => resolve());
84
+ proc.on('error', () => resolve());
85
+ });
86
+ }
87
+ async function profileCpu(opts, sessionName, profileOpts) {
88
+ if (sessionName === 'default') {
89
+ (0, output_js_1.printError)('profile cpu requires a --device', opts);
90
+ return 1;
91
+ }
92
+ const platform = await (0, bootstrap_js_1.detectPlatform)(sessionName).catch(() => null);
93
+ const isIos = platform === 'ios' || platform === 'tvos';
94
+ const out = profileOpts.out ?? defaultTracePath('cpu', isIos ? 'trace' : 'perf.data');
95
+ try {
96
+ if (isIos) {
97
+ await recordIosCpu(sessionName, profileOpts.appId, profileOpts.durationSec, out);
98
+ }
99
+ else if (platform === 'android') {
100
+ await recordAndroidCpu(sessionName, profileOpts.appId, profileOpts.durationSec, out);
101
+ }
102
+ else {
103
+ (0, output_js_1.printError)(`profile cpu is not supported on platform ${platform ?? '(unknown)'}`, opts);
104
+ return 1;
105
+ }
106
+ if (opts.json)
107
+ (0, output_js_1.printData)({ out, durationSec: profileOpts.durationSec, platform }, opts);
108
+ else
109
+ (0, output_js_1.printSuccess)(`profile cpu — recorded ${profileOpts.durationSec}s → ${out}`, opts);
110
+ return 0;
111
+ }
112
+ catch (err) {
113
+ (0, output_js_1.printError)(`profile cpu — ${err instanceof Error ? err.message : String(err)}`, opts);
114
+ return 1;
115
+ }
116
+ }
117
+ async function profileMemory(opts, sessionName, profileOpts) {
118
+ const samples = [];
119
+ const start = Date.now();
120
+ const end = start + profileOpts.trackSec * 1000;
121
+ while (Date.now() < end) {
122
+ const at = Date.now() - start;
123
+ // Capture memory output for this sample by intercepting stdout.
124
+ const captured = await captureStdout(async () => {
125
+ await (0, memory_js_1.memory)(profileOpts.appId, { json: true }, sessionName, {});
126
+ });
127
+ samples.push({ at, sample: captured });
128
+ if (Date.now() < end) {
129
+ await new Promise((r) => setTimeout(r, profileOpts.intervalMs));
130
+ }
131
+ }
132
+ const parsed = samples.map((s) => {
133
+ try {
134
+ return { at: s.at, data: JSON.parse(s.sample) };
135
+ }
136
+ catch {
137
+ return { at: s.at, data: null };
138
+ }
139
+ });
140
+ if (opts.json) {
141
+ (0, output_js_1.printData)({ samples: parsed, durationMs: Date.now() - start }, opts);
142
+ }
143
+ else {
144
+ console.log(`profile memory — ${samples.length} samples over ${profileOpts.trackSec}s`);
145
+ for (const p of parsed) {
146
+ const summary = p.data && typeof p.data === 'object'
147
+ ? Object.entries(p.data)
148
+ .slice(0, 4)
149
+ .map(([k, v]) => `${k}=${typeof v === 'object' ? '…' : String(v)}`)
150
+ .join(' ')
151
+ : '(parse error)';
152
+ console.log(` t+${(p.at / 1000).toFixed(1)}s ${summary}`);
153
+ }
154
+ }
155
+ return 0;
156
+ }
157
+ async function captureStdout(fn) {
158
+ const chunks = [];
159
+ const origWrite = process.stdout.write.bind(process.stdout);
160
+ process.stdout.write = ((c) => {
161
+ chunks.push(typeof c === 'string' ? c : Buffer.from(c).toString());
162
+ return true;
163
+ });
164
+ try {
165
+ await fn();
166
+ }
167
+ finally {
168
+ process.stdout.write = origWrite;
169
+ }
170
+ return chunks.join('');
171
+ }
172
+ // ── React profiler ────────────────────────────────────────────────────────────
173
+ const REACT_PROFILER_INSTALL = `
174
+ (() => {
175
+ if (globalThis.__CONDUCTOR_REACT_PROFILER__) {
176
+ return { installed: true, already: true };
177
+ }
178
+ const hook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
179
+ if (!hook) return { installed: false, error: 'No React DevTools hook (Hermes only?)' };
180
+ const commits = [];
181
+ const MAX = 500;
182
+ const orig = hook.onCommitFiberRoot;
183
+ hook.onCommitFiberRoot = function(rendererID, root, priorityLevel) {
184
+ try {
185
+ const entry = { at: Date.now(), rendererID, components: [] };
186
+ let node = root.current;
187
+ const stack = [{ fiber: node, depth: 0 }];
188
+ let count = 0;
189
+ while (stack.length && count < 200) {
190
+ const { fiber, depth } = stack.pop();
191
+ if (!fiber) continue;
192
+ const dur = fiber.actualDuration ?? 0;
193
+ if (dur > 0) {
194
+ const name = (fiber.type && (fiber.type.displayName || fiber.type.name)) || (typeof fiber.type === 'string' ? fiber.type : null);
195
+ if (name) {
196
+ entry.components.push({ name, depth, actualDuration: dur, selfDuration: fiber.selfBaseDuration ?? 0 });
197
+ count++;
198
+ }
199
+ }
200
+ if (fiber.child) stack.push({ fiber: fiber.child, depth: depth + 1 });
201
+ if (fiber.sibling) stack.push({ fiber: fiber.sibling, depth });
202
+ }
203
+ commits.push(entry);
204
+ if (commits.length > MAX) commits.shift();
205
+ } catch (e) {}
206
+ if (typeof orig === 'function') return orig.apply(this, arguments);
207
+ };
208
+ globalThis.__CONDUCTOR_REACT_PROFILER__ = {
209
+ installed: true,
210
+ commits,
211
+ uninstall: () => { hook.onCommitFiberRoot = orig; }
212
+ };
213
+ return { installed: true, already: false };
214
+ })()
215
+ `;
216
+ const REACT_PROFILER_READ = (top) => `
217
+ (() => {
218
+ const p = globalThis.__CONDUCTOR_REACT_PROFILER__;
219
+ if (!p) return { installed: false, commits: [] };
220
+ const commits = p.commits.slice();
221
+ const byName = {};
222
+ for (const c of commits) {
223
+ for (const comp of c.components) {
224
+ byName[comp.name] = byName[comp.name] ?? { name: comp.name, totalMs: 0, renders: 0 };
225
+ byName[comp.name].totalMs += comp.actualDuration;
226
+ byName[comp.name].renders += 1;
227
+ }
228
+ }
229
+ const top = Object.values(byName).sort((a, b) => b.totalMs - a.totalMs).slice(0, ${top});
230
+ return { installed: true, commits, totalCommits: commits.length, top };
231
+ })()
232
+ `;
233
+ const REACT_PROFILER_STOP = `
234
+ (() => {
235
+ const p = globalThis.__CONDUCTOR_REACT_PROFILER__;
236
+ if (!p) return { installed: false };
237
+ if (typeof p.uninstall === 'function') p.uninstall();
238
+ delete globalThis.__CONDUCTOR_REACT_PROFILER__;
239
+ return { installed: true, stopped: true };
240
+ })()
241
+ `;
242
+ async function profileReactStart(opts, sessionName, cdpOpts) {
243
+ try {
244
+ const platform = await (0, bootstrap_js_1.detectPlatform)(sessionName).catch(() => undefined);
245
+ const client = new metro_cdp_js_1.MetroCdpClient();
246
+ await client.connect({
247
+ port: cdpOpts.port ?? 8081,
248
+ deviceId: sessionName !== 'default' ? sessionName : undefined,
249
+ platform,
250
+ targetIndex: cdpOpts.targetIndex,
251
+ });
252
+ const result = await client.evaluate(REACT_PROFILER_INSTALL);
253
+ client.close();
254
+ if (!result.installed) {
255
+ (0, output_js_1.printError)(`profile react start — ${result.error ?? 'install failed'}`, opts);
256
+ return 1;
257
+ }
258
+ if (opts.json)
259
+ (0, output_js_1.printData)(result, opts);
260
+ else
261
+ (0, output_js_1.printSuccess)(`profile react start — ${result.already ? 'already installed' : 'installed'}`, opts);
262
+ return 0;
263
+ }
264
+ catch (err) {
265
+ (0, output_js_1.printError)(`profile react start — ${err instanceof Error ? err.message : String(err)}`, opts);
266
+ return 1;
267
+ }
268
+ }
269
+ async function profileReactStop(opts, sessionName, cdpOpts, top) {
270
+ try {
271
+ const platform = await (0, bootstrap_js_1.detectPlatform)(sessionName).catch(() => undefined);
272
+ const client = new metro_cdp_js_1.MetroCdpClient();
273
+ await client.connect({
274
+ port: cdpOpts.port ?? 8081,
275
+ deviceId: sessionName !== 'default' ? sessionName : undefined,
276
+ platform,
277
+ targetIndex: cdpOpts.targetIndex,
278
+ });
279
+ const read = await client.evaluate(REACT_PROFILER_READ(top));
280
+ await client.evaluate(REACT_PROFILER_STOP);
281
+ client.close();
282
+ if (!read.installed) {
283
+ (0, output_js_1.printError)('profile react stop — profiler was not installed', opts);
284
+ return 1;
285
+ }
286
+ if (opts.json)
287
+ (0, output_js_1.printData)(read, opts);
288
+ else {
289
+ console.log(`profile react — ${read.totalCommits ?? 0} commit(s)`);
290
+ for (const t of read.top ?? []) {
291
+ console.log(` ${t.totalMs.toFixed(1)}ms ${t.renders}x ${t.name}`);
292
+ }
293
+ }
294
+ return 0;
295
+ }
296
+ catch (err) {
297
+ (0, output_js_1.printError)(`profile react stop — ${err instanceof Error ? err.message : String(err)}`, opts);
298
+ return 1;
299
+ }
300
+ }
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.HELP = void 0;
7
+ exports.runSequence = runSequence;
8
+ exports.HELP = ` run-sequence [--file path.json] Run a sequence of conductor commands serially against one session
9
+ JSON shape: {"steps":[{"cmd":"tap-on","args":["Login"]}, ...]}
10
+ Reads stdin when --file is omitted. Stops on first non-zero exit.`;
11
+ const child_process_1 = require("child_process");
12
+ const fs_1 = __importDefault(require("fs"));
13
+ const path_1 = __importDefault(require("path"));
14
+ const output_js_1 = require("../output.js");
15
+ function flagsToArgs(flags) {
16
+ if (!flags)
17
+ return [];
18
+ const out = [];
19
+ for (const [key, value] of Object.entries(flags)) {
20
+ if (value === false)
21
+ continue;
22
+ out.push(`--${key}`);
23
+ if (value !== true)
24
+ out.push(String(value));
25
+ }
26
+ return out;
27
+ }
28
+ function readStdin() {
29
+ return new Promise((resolve, reject) => {
30
+ let buf = '';
31
+ process.stdin.setEncoding('utf-8');
32
+ process.stdin.on('data', (chunk) => {
33
+ buf += chunk;
34
+ });
35
+ process.stdin.on('end', () => resolve(buf));
36
+ process.stdin.on('error', reject);
37
+ });
38
+ }
39
+ async function runSequence(filePath, opts = {}, sessionName = 'default') {
40
+ let raw;
41
+ if (filePath) {
42
+ const resolved = path_1.default.resolve(filePath);
43
+ if (!fs_1.default.existsSync(resolved)) {
44
+ (0, output_js_1.printError)(`run-sequence: file not found: ${resolved}`, opts);
45
+ return 1;
46
+ }
47
+ raw = fs_1.default.readFileSync(resolved, 'utf-8');
48
+ }
49
+ else {
50
+ raw = await readStdin();
51
+ }
52
+ let parsed;
53
+ try {
54
+ parsed = JSON.parse(raw);
55
+ }
56
+ catch (err) {
57
+ (0, output_js_1.printError)(`run-sequence: invalid JSON\n${err instanceof Error ? err.message : String(err)}`, opts);
58
+ return 1;
59
+ }
60
+ if (!parsed.steps || !Array.isArray(parsed.steps)) {
61
+ (0, output_js_1.printError)('run-sequence: input must be {"steps":[...]}', opts);
62
+ return 1;
63
+ }
64
+ const results = [];
65
+ const conductorBin = process.argv[1] ?? 'conductor';
66
+ const deviceArgs = sessionName !== 'default' ? ['--device', sessionName] : [];
67
+ for (let i = 0; i < parsed.steps.length; i++) {
68
+ const step = parsed.steps[i];
69
+ const args = [step.cmd, ...(step.args ?? []), ...flagsToArgs(step.flags), ...deviceArgs];
70
+ if (!opts.json) {
71
+ console.log(`[${i + 1}/${parsed.steps.length}] conductor ${args.join(' ')}`);
72
+ }
73
+ const result = await runStep(conductorBin, args);
74
+ results.push({
75
+ cmd: step.cmd,
76
+ args: step.args ?? [],
77
+ exitCode: result.exitCode,
78
+ stdout: result.stdout,
79
+ stderr: result.stderr,
80
+ });
81
+ if (!opts.json && result.stdout)
82
+ process.stdout.write(result.stdout);
83
+ if (!opts.json && result.stderr)
84
+ process.stderr.write(result.stderr);
85
+ if (result.exitCode !== 0) {
86
+ if (opts.json) {
87
+ (0, output_js_1.printData)({ ok: false, completed: i, total: parsed.steps.length, results }, opts);
88
+ }
89
+ else {
90
+ (0, output_js_1.printError)(`run-sequence aborted at step ${i + 1}/${parsed.steps.length} (${step.cmd}, exit ${result.exitCode})`, opts);
91
+ }
92
+ return result.exitCode;
93
+ }
94
+ }
95
+ if (opts.json) {
96
+ (0, output_js_1.printData)({ ok: true, completed: parsed.steps.length, total: parsed.steps.length, results }, opts);
97
+ }
98
+ else {
99
+ console.log(`run-sequence — completed ${parsed.steps.length} step(s)`);
100
+ }
101
+ return 0;
102
+ }
103
+ function runStep(bin, args) {
104
+ return new Promise((resolve) => {
105
+ const proc = (0, child_process_1.spawn)(process.execPath, [bin, ...args], {
106
+ stdio: ['ignore', 'pipe', 'pipe'],
107
+ env: process.env,
108
+ });
109
+ let stdout = '';
110
+ let stderr = '';
111
+ proc.stdout.on('data', (c) => {
112
+ stdout += c.toString();
113
+ });
114
+ proc.stderr.on('data', (c) => {
115
+ stderr += c.toString();
116
+ });
117
+ proc.on('close', (code) => {
118
+ resolve({ exitCode: code ?? 1, stdout, stderr });
119
+ });
120
+ proc.on('error', (err) => {
121
+ resolve({ exitCode: 1, stdout: '', stderr: err.message });
122
+ });
123
+ });
124
+ }
@@ -13,6 +13,7 @@ const ios_js_1 = require("../drivers/ios.js");
13
13
  const android_js_1 = require("../drivers/android.js");
14
14
  const web_js_1 = require("../drivers/web.js");
15
15
  const element_resolver_js_1 = require("../drivers/element-resolver.js");
16
+ const direct_ios_selector_js_1 = require("../drivers/direct-ios-selector.js");
16
17
  const utils_js_1 = require("../utils.js");
17
18
  async function scrollUntilVisible(element, opts = {}, sessionName = 'default', flags = {}) {
18
19
  if (!element && !flags.id && !flags.text) {
@@ -38,9 +39,25 @@ async function scrollUntilVisible(element, opts = {}, sessionName = 'default', f
38
39
  try {
39
40
  const driver = await (0, runner_js_1.getDriver)(sessionName);
40
41
  const deadline = Date.now() + timeoutMs;
42
+ // Resolve simple selectors with a direct runner query, skipping the
43
+ // full-tree snapshot on every scroll iteration.
44
+ const iosDirectResolve = driver instanceof ios_js_1.IOSDriver ? (0, direct_ios_selector_js_1.makeIOSDirectResolver)(driver, sel) : undefined;
41
45
  while (Date.now() < deadline) {
42
46
  try {
43
47
  if (driver instanceof ios_js_1.IOSDriver) {
48
+ if (iosDirectResolve) {
49
+ let fast = null;
50
+ try {
51
+ fast = await iosDirectResolve();
52
+ }
53
+ catch {
54
+ // direct query failed — fall back to the snapshot check below
55
+ }
56
+ if (fast) {
57
+ (0, output_js_1.printSuccess)(`scroll-until-visible ${label} — found`, opts);
58
+ return 0;
59
+ }
60
+ }
44
61
  const root = await driver.viewHierarchy().then((h) => h.axElement);
45
62
  if ((0, element_resolver_js_1.findIOSElement)(root, sel)) {
46
63
  (0, output_js_1.printSuccess)(`scroll-until-visible ${label} — found`, opts);
@@ -165,6 +165,9 @@ async function startIOS(osVersion, opts, name, deviceType) {
165
165
  }
166
166
  }
167
167
  const displayName = name ?? sim.name;
168
+ // Prewarm the driver so the first interaction command is not the
169
+ // one that pays the XCTest runner startup cost.
170
+ await (0, runner_js_1.prewarmDriver)(sim.udid);
168
171
  (0, output_js_1.printSuccess)(`Simulator already booted: ${displayName} (${sim.udid})`, opts);
169
172
  return 0;
170
173
  }
@@ -203,6 +206,9 @@ async function startIOS(osVersion, opts, name, deviceType) {
203
206
  }
204
207
  (0, child_process_1.spawn)('open', ['-a', 'Simulator'], { detached: true, stdio: 'ignore' }).unref();
205
208
  const displayName = name ?? deviceType;
209
+ // Prewarm the driver so the first interaction command is not the
210
+ // one that pays the XCTest runner startup cost.
211
+ await (0, runner_js_1.prewarmDriver)(udid);
206
212
  (0, output_js_1.printSuccess)(`Booted: ${displayName} (${udid})`, opts);
207
213
  return 0;
208
214
  }
@@ -238,6 +244,9 @@ async function startIOS(osVersion, opts, name, deviceType) {
238
244
  // Open the Simulator.app so the window appears
239
245
  (0, child_process_1.spawn)('open', ['-a', 'Simulator'], { detached: true, stdio: 'ignore' }).unref();
240
246
  const displayName = name ?? device.name;
247
+ // Prewarm the driver so the first interaction command is not the
248
+ // one that pays the XCTest runner startup cost.
249
+ await (0, runner_js_1.prewarmDriver)(device.udid);
241
250
  (0, output_js_1.printSuccess)(`Booted: ${displayName} (${device.udid})`, opts);
242
251
  return 0;
243
252
  }
@@ -320,6 +329,9 @@ async function startTvOS(osVersion, opts, name, deviceType) {
320
329
  }
321
330
  }
322
331
  const displayName = name ?? sim.name;
332
+ // Prewarm the driver so the first interaction command is not the
333
+ // one that pays the XCTest runner startup cost.
334
+ await (0, runner_js_1.prewarmDriver)(sim.udid);
323
335
  (0, output_js_1.printSuccess)(`Simulator already booted: ${displayName} (${sim.udid})`, opts);
324
336
  return 0;
325
337
  }
@@ -358,6 +370,9 @@ async function startTvOS(osVersion, opts, name, deviceType) {
358
370
  }
359
371
  (0, child_process_1.spawn)('open', ['-a', 'Simulator'], { detached: true, stdio: 'ignore' }).unref();
360
372
  const displayName = name ?? deviceType;
373
+ // Prewarm the driver so the first interaction command is not the
374
+ // one that pays the XCTest runner startup cost.
375
+ await (0, runner_js_1.prewarmDriver)(udid);
361
376
  (0, output_js_1.printSuccess)(`Booted: ${displayName} (${udid})`, opts);
362
377
  return 0;
363
378
  }
@@ -393,6 +408,9 @@ async function startTvOS(osVersion, opts, name, deviceType) {
393
408
  // Open the Simulator.app so the window appears
394
409
  (0, child_process_1.spawn)('open', ['-a', 'Simulator'], { detached: true, stdio: 'ignore' }).unref();
395
410
  const displayName = name ?? device.name;
411
+ // Prewarm the driver so the first interaction command is not the
412
+ // one that pays the XCTest runner startup cost.
413
+ await (0, runner_js_1.prewarmDriver)(device.udid);
396
414
  (0, output_js_1.printSuccess)(`Booted: ${displayName} (${device.udid})`, opts);
397
415
  return 0;
398
416
  }
@@ -23,6 +23,7 @@ const ios_js_1 = require("../drivers/ios.js");
23
23
  const android_js_1 = require("../drivers/android.js");
24
24
  const web_js_1 = require("../drivers/web.js");
25
25
  const wait_js_1 = require("../drivers/wait.js");
26
+ const direct_ios_selector_js_1 = require("../drivers/direct-ios-selector.js");
26
27
  const utils_js_1 = require("../utils.js");
27
28
  async function tap(query, opts = {}, sessionName = 'default', flags = {}) {
28
29
  if (!query && !flags.id && !flags.text) {
@@ -49,7 +50,7 @@ async function tap(query, opts = {}, sessionName = 'default', flags = {}) {
49
50
  }
50
51
  let el;
51
52
  if (driver instanceof ios_js_1.IOSDriver) {
52
- el = await (0, wait_js_1.waitForIOSElement)(() => driver.viewHierarchy().then((h) => h.axElement), sel);
53
+ el = await (0, wait_js_1.waitForIOSElement)((o) => driver.viewHierarchy(false, [], { cache: o?.cached }).then((h) => h.axElement), sel, undefined, undefined, (0, direct_ios_selector_js_1.makeIOSDirectResolver)(driver, sel));
53
54
  }
54
55
  else if (driver instanceof web_js_1.WebDriver) {
55
56
  el = await (0, wait_js_1.waitForWebElement)(() => driver.viewHierarchy(), sel);