@delacour/warden-e2e 0.1.1 → 0.3.1

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/preload.mjs CHANGED
@@ -1,93 +1,78 @@
1
1
  // @bun
2
2
  import {
3
- BUN_TIMEOUT_MARGIN_MS,
4
- DEFAULT_ASSERTION_TIMEOUT_MS,
3
+ E2eError2,
4
+ noteSpanDetail,
5
+ addSpanDetail,
6
+ canObserve,
7
+ canDetectAlerts,
8
+ settleDetailOf,
5
9
  DEFAULT_SETTLE_QUIET_MS,
6
10
  DEFAULT_SETTLE_TIMEOUT_MS,
7
- E2eError,
8
- ENV_NAMES,
9
- Locator,
10
- PATH_MS,
11
+ settleByHash,
12
+ snapshotHash,
13
+ ok,
14
+ err,
15
+ swipeVector,
16
+ screenRect,
11
17
  PINCH_MS,
12
18
  ROTATE_MS,
13
- WARDEN_E2E_VERSION,
14
- WORKSPACE_VERSION,
15
- addSpanDetail,
16
- assertDegrees,
17
- assertScale,
18
- canDetectAlerts,
19
- canObserve,
19
+ PATH_MS,
20
20
  centreOf,
21
- err,
22
- getSession,
23
- hasDefaultDriverFactory,
24
- hasDefaultFixtureFactory,
25
- indexSnapshot,
26
- isInteractiveRole,
27
- isLocator,
28
- locateByTestIdViaReact,
29
- noteSpanDetail,
30
- ok,
31
- onlyWasSeen,
32
21
  pinchRadius,
33
- readEnv,
34
- renderHeader,
35
- renderSnapshot,
22
+ rotateRadius,
36
23
  requireGesture,
24
+ assertScale,
25
+ assertDegrees,
26
+ isInteractiveRole,
37
27
  resolveRole,
28
+ indexSnapshot,
29
+ testIdQuery,
30
+ textQuery,
38
31
  roleQuery,
39
- rotateRadius,
40
- screenRect,
41
- screenshotPath,
42
- scrollUntilVisible,
43
32
  selectorExpression,
33
+ renderSnapshot,
34
+ renderHeader,
35
+ locateByTestIdViaReact,
36
+ isLocator,
37
+ Locator,
38
+ scrollUntilVisible,
39
+ bunExec,
40
+ execError,
41
+ driverError,
42
+ driverErr,
43
+ logEventSchema,
44
+ fatalEventSchema,
45
+ responseSchema,
46
+ mapWireError,
47
+ encodeRequest,
48
+ bunSpawn,
49
+ stdioTransport,
50
+ spawnSimd,
51
+ connectSimd,
52
+ ENV_NAMES,
53
+ DEFAULT_ASSERTION_TIMEOUT_MS,
54
+ readEnv,
55
+ setDefaultHidRecovery,
56
+ setDefaultNativeLog,
44
57
  setDefaultDriverFactory,
58
+ hasDefaultDriverFactory,
45
59
  setDefaultFixtureFactory,
46
- settleByHash,
47
- settleDetailOf,
48
- snapshotHash,
49
- swipeVector,
50
- testIdQuery,
51
- textQuery
52
- } from "./chunk-qtkmfjbq.mjs";
60
+ hasDefaultFixtureFactory,
61
+ getSession,
62
+ parseBudgets,
63
+ createDevicePerf,
64
+ spawnWarden,
65
+ screenshotPath,
66
+ realNativeLogDeps,
67
+ BUN_TIMEOUT_MARGIN_MS2,
68
+ onlyWasSeen,
69
+ WORKSPACE_VERSION,
70
+ WARDEN_E2E_VERSION2
71
+ } from "./chunk-8em9kgsk.mjs";
53
72
 
54
73
  // ../../packages/e2e/src/preload.ts
55
74
  import { afterAll, setDefaultTimeout } from "bun:test";
56
75
 
57
- // ../../packages/core/src/exec.ts
58
- import { existsSync } from "fs";
59
- function spawnFailure(cmd, cwd, error) {
60
- if (cwd !== undefined && !existsSync(cwd))
61
- return `cannot run \`${cmd.join(" ")}\`: no such directory ${cwd}`;
62
- return `cannot run \`${cmd.join(" ")}\`: ${error instanceof Error ? error.message : String(error)}`;
63
- }
64
- var bunExec = async (cmd, opts = {}) => {
65
- try {
66
- const proc = Bun.spawn([...cmd], {
67
- cwd: opts.cwd,
68
- env: opts.env ? { ...process.env, ...opts.env } : process.env,
69
- stdout: "pipe",
70
- stderr: "pipe",
71
- stdin: "ignore",
72
- timeout: opts.timeoutMs,
73
- ...opts.signal !== undefined ? { signal: opts.signal } : {}
74
- });
75
- const [stdout, stderr, exitCode] = await Promise.all([
76
- new Response(proc.stdout).text(),
77
- new Response(proc.stderr).text(),
78
- proc.exited
79
- ]);
80
- return { exitCode, stdout, stderr };
81
- } catch (error) {
82
- return { exitCode: 127, stdout: "", stderr: spawnFailure(cmd, opts.cwd, error) };
83
- }
84
- };
85
- function execError(cmd, result) {
86
- const detail = (result.stderr || result.stdout).trim().split(`
87
- `).slice(-3).join(" | ");
88
- return `\`${cmd.join(" ")}\` exited ${result.exitCode}${detail ? `: ${detail}` : ""}`;
89
- }
90
-
91
76
  // ../../packages/core/src/providers/android.ts
92
77
  import { join as join2 } from "path";
93
78
 
@@ -119,26 +104,6 @@ var avdLists = new WeakMap;
119
104
  import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
120
105
  import { dirname as dirname2 } from "path";
121
106
 
122
- // ../../packages/e2e/src/driver/errors.ts
123
- var RETRYABLE = new Set([
124
- "E_TIMEOUT",
125
- "E_HELPER_CRASHED",
126
- "E_AX_UNAVAILABLE",
127
- "E_APP_NOT_RESPONDING",
128
- "E_STALE_REF"
129
- ]);
130
- function driverError(code, message, extra = {}) {
131
- return {
132
- code,
133
- message,
134
- retryable: extra.retryable ?? RETRYABLE.has(code),
135
- ...extra.detail !== undefined ? { detail: extra.detail } : {}
136
- };
137
- }
138
- function driverErr(code, message, extra) {
139
- return { success: false, error: driverError(code, message, extra) };
140
- }
141
-
142
107
  // ../../packages/e2e/src/driver/gesture-geometry.ts
143
108
  var GESTURE_FRAME_MS = 16;
144
109
  var GESTURE_MAX_FRAMES = 1000;
@@ -331,14 +296,14 @@ function openAdbSession(options) {
331
296
  function check(l) {
332
297
  if (!current)
333
298
  return;
334
- const start2 = l.buffer.indexOf(current.prefix);
335
- if (start2 < 0)
299
+ const start = l.buffer.indexOf(current.prefix);
300
+ if (start < 0)
336
301
  return;
337
- const afterPrefix = start2 + current.prefix.length;
302
+ const afterPrefix = start + current.prefix.length;
338
303
  const m = /^(\d+)__(\r?\n)?/.exec(l.buffer.slice(afterPrefix));
339
304
  if (!m)
340
305
  return;
341
- const stdout = l.buffer.slice(0, start2);
306
+ const stdout = l.buffer.slice(0, start);
342
307
  l.buffer = l.buffer.slice(afterPrefix + m[0].length);
343
308
  const settle = current.settle;
344
309
  current = undefined;
@@ -1231,8 +1196,8 @@ function createDumper(deps) {
1231
1196
  async function fetchRoute(route) {
1232
1197
  if (route === "tty") {
1233
1198
  try {
1234
- const res2 = await deps.exec(dumpTtyHostArgv(deps.adb, deps.serial), { timeoutMs: DUMP_TIMEOUT_MS });
1235
- return { text: res2.stdout };
1199
+ const res = await deps.exec(dumpTtyHostArgv(deps.adb, deps.serial), { timeoutMs: DUMP_TIMEOUT_MS });
1200
+ return { text: res.stdout };
1236
1201
  } catch (error) {
1237
1202
  return { error: { code: "E_IO", message: String(error), retryable: false } };
1238
1203
  }
@@ -1283,452 +1248,8 @@ function createDumper(deps) {
1283
1248
  };
1284
1249
  }
1285
1250
 
1286
- // ../../packages/e2e/src/driver/ios/simd-protocol.ts
1287
- import { z as z2 } from "zod";
1288
- var protocolVersion = 1;
1289
- var rectSchema = z2.object({
1290
- x: z2.number(),
1291
- y: z2.number(),
1292
- width: z2.number(),
1293
- height: z2.number()
1294
- });
1295
- var screenInfoSchema = z2.object({
1296
- width: z2.number(),
1297
- height: z2.number(),
1298
- scale: z2.number(),
1299
- rotation: z2.number().optional()
1300
- });
1301
- var ROLES = [
1302
- "button",
1303
- "text",
1304
- "textfield",
1305
- "securefield",
1306
- "searchfield",
1307
- "image",
1308
- "switch",
1309
- "checkbox",
1310
- "slider",
1311
- "link",
1312
- "cell",
1313
- "list",
1314
- "scrollview",
1315
- "tab",
1316
- "tabbar",
1317
- "alert",
1318
- "sheet",
1319
- "keyboard",
1320
- "navbar",
1321
- "toolbar",
1322
- "window",
1323
- "application",
1324
- "other"
1325
- ];
1326
- var pointSchema = z2.object({ x: z2.number(), y: z2.number() });
1327
- var wireElementSchema = z2.lazy(() => z2.object({
1328
- id: z2.string().nullish(),
1329
- role: z2.enum(ROLES),
1330
- rawType: z2.string(),
1331
- label: z2.string().nullish(),
1332
- identifier: z2.string().nullish(),
1333
- value: z2.string().nullish(),
1334
- text: z2.string().nullish(),
1335
- placeholder: z2.string().nullish(),
1336
- frame: rectSchema,
1337
- normFrame: rectSchema.nullish(),
1338
- traits: z2.array(z2.string()).nullish(),
1339
- enabled: z2.boolean().nullish(),
1340
- focused: z2.boolean().nullish(),
1341
- hidden: z2.boolean().nullish(),
1342
- selected: z2.boolean().nullish(),
1343
- checked: z2.boolean().nullish(),
1344
- hittable: z2.boolean().nullish(),
1345
- tapPoint: pointSchema.nullish(),
1346
- pid: z2.number().nullish(),
1347
- depth: z2.number().nullish(),
1348
- children: z2.array(wireElementSchema).nullish()
1349
- }));
1350
- var ROLE_SET = new Set(ROLES);
1351
- var isBag = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1352
- var isNum = (value) => typeof value === "number" && Number.isFinite(value);
1353
- var isNullish = (value) => value === null || value === undefined;
1354
- function isRect(value) {
1355
- return isBag(value) && isNum(value.x) && isNum(value.y) && isNum(value.width) && isNum(value.height);
1356
- }
1357
- var OPTIONAL_STRINGS = ["id", "label", "identifier", "value", "text", "placeholder"];
1358
- var OPTIONAL_FLAGS = ["enabled", "focused", "hidden", "selected", "checked", "hittable"];
1359
- var isString = (value) => typeof value === "string";
1360
- var isBool = (value) => typeof value === "boolean";
1361
- var isPoint = (value) => isBag(value) && isNum(value.x) && isNum(value.y);
1362
- var isStringArray = (value) => Array.isArray(value) && value.every(isString);
1363
- var orNullish = (value, check) => isNullish(value) || check(value);
1364
- function hasValidOptionals(node) {
1365
- return OPTIONAL_STRINGS.every((key) => orNullish(node[key], isString)) && OPTIONAL_FLAGS.every((key) => orNullish(node[key], isBool)) && orNullish(node.normFrame, isRect) && orNullish(node.traits, isStringArray) && orNullish(node.tapPoint, isPoint) && orNullish(node.pid, isNum) && orNullish(node.depth, isNum);
1366
- }
1367
- function isWireElement(value) {
1368
- if (!isBag(value))
1369
- return false;
1370
- if (!isString(value.role) || !ROLE_SET.has(value.role))
1371
- return false;
1372
- if (!isString(value.rawType) || !isRect(value.frame) || !hasValidOptionals(value))
1373
- return false;
1374
- const { children } = value;
1375
- if (isNullish(children))
1376
- return true;
1377
- return Array.isArray(children) && children.every(isWireElement);
1378
- }
1379
- function wireElementProblem(value) {
1380
- const parsed = wireElementSchema.safeParse(value);
1381
- if (parsed.success)
1382
- return "invalid element";
1383
- const issue = parsed.error.issues[0];
1384
- return issue ? `${issue.path.join(".") || "(element)"}: ${issue.message}` : "invalid element";
1385
- }
1386
- var wireErrorSchema = z2.object({
1387
- code: z2.string(),
1388
- message: z2.string(),
1389
- retryable: z2.boolean().optional(),
1390
- detail: z2.unknown().optional()
1391
- });
1392
- var readyEventSchema = z2.object({
1393
- event: z2.literal("ready"),
1394
- protocol: z2.number(),
1395
- version: z2.string(),
1396
- udid: z2.string(),
1397
- hid: z2.object({ transport: z2.string() }),
1398
- ax: z2.object({ default: z2.string(), bridge: z2.string() }),
1399
- screen: screenInfoSchema
1400
- });
1401
- var logEventSchema = z2.object({
1402
- event: z2.literal("log"),
1403
- level: z2.enum(["debug", "info", "warn", "error"]),
1404
- message: z2.string(),
1405
- op: z2.string().optional(),
1406
- id: z2.string().optional()
1407
- });
1408
- var fatalEventSchema = z2.object({ event: z2.literal("fatal"), error: wireErrorSchema });
1409
- var eventSchema = z2.discriminatedUnion("event", [readyEventSchema, logEventSchema, fatalEventSchema]);
1410
- var responseSchema = z2.discriminatedUnion("ok", [
1411
- z2.object({ id: z2.string(), ok: z2.literal(true), data: z2.unknown().optional() }),
1412
- z2.object({ id: z2.string(), ok: z2.literal(false), error: wireErrorSchema })
1413
- ]);
1414
- function parseLine(line) {
1415
- let json;
1416
- try {
1417
- json = JSON.parse(line);
1418
- } catch {
1419
- return { success: false, reason: "not JSON" };
1420
- }
1421
- if (typeof json === "object" && json !== null && "event" in json) {
1422
- const event = eventSchema.safeParse(json);
1423
- return event.success ? { success: true, message: { kind: "event", event: event.data } } : { success: false, reason: `bad event: ${event.error.message}` };
1424
- }
1425
- const response = responseSchema.safeParse(json);
1426
- return response.success ? { success: true, message: { kind: "response", response: response.data } } : { success: false, reason: `bad response: ${response.error.message}` };
1427
- }
1428
- var healthDataSchema = z2.looseObject({
1429
- version: z2.string(),
1430
- udid: z2.string(),
1431
- hid: z2.object({ transport: z2.string() }),
1432
- ax: z2.object({
1433
- default: z2.string(),
1434
- bridge: z2.enum(["available", "degraded", "missing", "failed"]),
1435
- error: z2.string().optional(),
1436
- lastError: z2.object({ message: z2.string(), agoMs: z2.number() }).optional()
1437
- }),
1438
- screen: screenInfoSchema,
1439
- socket: z2.object({ path: z2.string(), clients: z2.number() }).optional(),
1440
- idb: z2.string().optional(),
1441
- xcode: z2.string().optional(),
1442
- state: z2.string().optional(),
1443
- uptimeMs: z2.number().optional()
1444
- });
1445
- var snapshotDataSchema = z2.looseObject({
1446
- backend: z2.enum(["axbridge", "ax", "uiautomator"]),
1447
- screen: screenInfoSchema,
1448
- modal: z2.object({ pid: z2.number().optional(), description: z2.string().optional() }).nullish(),
1449
- truncated: z2.boolean(),
1450
- elements: z2.array(z2.custom(isWireElement, { error: (issue) => wireElementProblem(issue.input) })),
1451
- ms: z2.number(),
1452
- fallback: z2.object({ from: z2.string(), to: z2.string(), reason: z2.string() }).optional(),
1453
- readMs: z2.number().optional(),
1454
- nodes: z2.number().optional()
1455
- });
1456
- var elementAtPointDataSchema = wireElementSchema.nullable();
1457
- var screenshotDataSchema = z2.looseObject({
1458
- path: z2.string().optional(),
1459
- png: z2.string().optional(),
1460
- width: z2.number().optional(),
1461
- height: z2.number().optional()
1462
- });
1463
- var settleDataSchema = z2.looseObject({
1464
- quiet: z2.boolean(),
1465
- waitedMs: z2.number(),
1466
- via: z2.enum(["stream", "snapshot"]).optional().catch(undefined),
1467
- reason: z2.string().optional(),
1468
- frontmostPid: z2.number().optional()
1469
- });
1470
- var rebindDataSchema = z2.looseObject({
1471
- bound: z2.boolean(),
1472
- waitedMs: z2.number(),
1473
- pid: z2.number().optional(),
1474
- restarts: z2.number().optional(),
1475
- recycled: z2.boolean().optional(),
1476
- reason: z2.string().optional()
1477
- });
1478
- var alertButtonsDataSchema = z2.object({
1479
- title: z2.string().optional(),
1480
- message: z2.string().optional(),
1481
- buttons: z2.array(wireElementSchema)
1482
- }).nullable();
1483
- var frontmostDataSchema = z2.looseObject({
1484
- frontmost: z2.boolean(),
1485
- pid: z2.number().optional(),
1486
- frontmostPid: z2.number().optional()
1487
- });
1488
- var orientationDataSchema = z2.object({
1489
- orientation: z2.enum([
1490
- "portrait",
1491
- "portraitUpsideDown",
1492
- "landscapeLeft",
1493
- "landscapeRight",
1494
- "faceUp",
1495
- "faceDown",
1496
- "unknown"
1497
- ]),
1498
- source: z2.enum(["device", "written", "assumed"]).optional()
1499
- });
1500
- var opSchemas = {
1501
- health: healthDataSchema,
1502
- snapshot: snapshotDataSchema,
1503
- elementAtPoint: elementAtPointDataSchema,
1504
- screenshot: screenshotDataSchema,
1505
- settle: settleDataSchema,
1506
- rebind: rebindDataSchema,
1507
- alertButtons: alertButtonsDataSchema,
1508
- frontmost: frontmostDataSchema,
1509
- orientation: orientationDataSchema
1510
- };
1511
- function hasOpSchema(op) {
1512
- return Object.hasOwn(opSchemas, op);
1513
- }
1514
- var DRIVER_CODES = new Set([
1515
- "E_HELPER_MISSING",
1516
- "E_HELPER_CRASHED",
1517
- "E_TIMEOUT",
1518
- "E_NOT_BOOTED",
1519
- "E_UNKNOWN_DEVICE",
1520
- "E_AX_UNAVAILABLE",
1521
- "E_APP_UNAVAILABLE",
1522
- "E_APP_NOT_RESPONDING",
1523
- "E_NO_ELEMENT",
1524
- "E_STALE_REF",
1525
- "E_UNSUPPORTED",
1526
- "E_UNSUPPORTED_TEXT",
1527
- "E_HID",
1528
- "E_IO",
1529
- "E_BAD_REQUEST",
1530
- "E_INTERNAL"
1531
- ]);
1532
- function isDriverCode(code) {
1533
- return DRIVER_CODES.has(code);
1534
- }
1535
- function mapWireError(wire) {
1536
- const retryable = wire.retryable === undefined ? {} : { retryable: wire.retryable };
1537
- if (wire.code === "E_UNKNOWN_UDID") {
1538
- return driverError("E_UNKNOWN_DEVICE", wire.message, { ...retryable, detail: wire.detail });
1539
- }
1540
- if (isDriverCode(wire.code))
1541
- return driverError(wire.code, wire.message, { ...retryable, detail: wire.detail });
1542
- return driverError("E_INTERNAL", wire.message, {
1543
- ...retryable,
1544
- detail: { wireCode: wire.code, ...wire.detail !== undefined ? { wireDetail: wire.detail } : {} }
1545
- });
1546
- }
1547
- function createLineSplitter() {
1548
- const decoder = new TextDecoder("utf-8");
1549
- let buffer = "";
1550
- return {
1551
- push(chunk) {
1552
- buffer += decoder.decode(chunk, { stream: true });
1553
- const lines = buffer.split(`
1554
- `);
1555
- buffer = lines.pop() ?? "";
1556
- return lines.map((line) => line.endsWith("\r") ? line.slice(0, -1) : line).filter((line) => line.length > 0);
1557
- },
1558
- rest: () => buffer
1559
- };
1560
- }
1561
- function encodeRequest(request) {
1562
- return `${JSON.stringify(request)}
1563
- `;
1564
- }
1565
-
1566
- // ../../packages/e2e/src/driver/ios/simd-transport.ts
1567
- var bunSpawn = (cmd) => {
1568
- const proc = Bun.spawn([...cmd], { stdin: "pipe", stdout: "pipe", stderr: "pipe" });
1569
- return {
1570
- write(data) {
1571
- proc.stdin.write(data);
1572
- proc.stdin.flush();
1573
- },
1574
- end() {
1575
- proc.stdin.end();
1576
- },
1577
- kill() {
1578
- proc.kill();
1579
- },
1580
- stdout: proc.stdout,
1581
- stderr: proc.stderr,
1582
- exited: proc.exited
1583
- };
1584
- };
1585
- async function pump(stream, onLine) {
1586
- const splitter = createLineSplitter();
1587
- const reader = stream.getReader();
1588
- try {
1589
- for (;; ) {
1590
- const { done, value } = await reader.read();
1591
- if (done)
1592
- break;
1593
- for (const line of splitter.push(value))
1594
- onLine(line);
1595
- }
1596
- } catch {}
1597
- const rest = splitter.rest().trim();
1598
- if (rest)
1599
- onLine(rest);
1600
- }
1601
- var STDERR_TAIL = 20;
1602
- var DEFAULT_GRACE_MS = 1500;
1603
- function stdioTransport(opts) {
1604
- const spawn = opts.spawn ?? bunSpawn;
1605
- const graceMs = opts.graceMs ?? DEFAULT_GRACE_MS;
1606
- return async (handlers) => {
1607
- let proc;
1608
- try {
1609
- proc = spawn(opts.cmd);
1610
- } catch (error) {
1611
- const reason = error instanceof Error ? error.message : String(error);
1612
- return {
1613
- success: false,
1614
- error: driverError("E_HELPER_MISSING", `cannot start ${opts.cmd[0]}: ${reason}`, {
1615
- detail: { cmd: opts.cmd }
1616
- })
1617
- };
1618
- }
1619
- const stderrTail = [];
1620
- const stdout = pump(proc.stdout, handlers.onLine);
1621
- const stderr = proc.stderr ? pump(proc.stderr, (line) => {
1622
- stderrTail.push(line);
1623
- if (stderrTail.length > STDERR_TAIL)
1624
- stderrTail.shift();
1625
- handlers.onDiagnostic(line);
1626
- }) : Promise.resolve();
1627
- const exited = proc.exited.catch(() => -1);
1628
- Promise.all([stdout, stderr, exited]).then(([, , exitCode]) => {
1629
- handlers.onClose({ exitCode, stderrTail: [...stderrTail] });
1630
- });
1631
- return {
1632
- success: true,
1633
- data: {
1634
- send: (data) => proc.write(data),
1635
- async close() {
1636
- try {
1637
- proc.end();
1638
- } catch {}
1639
- let timer;
1640
- const timedOut = new Promise((resolve) => {
1641
- timer = setTimeout(() => resolve("timeout"), graceMs);
1642
- });
1643
- const outcome = await Promise.race([exited.then(() => "exited"), timedOut]);
1644
- clearTimeout(timer);
1645
- if (outcome === "timeout") {
1646
- proc.kill();
1647
- await exited;
1648
- }
1649
- }
1650
- }
1651
- };
1652
- };
1653
- }
1654
- function socketTransport(opts) {
1655
- return async (handlers) => {
1656
- const splitter = createLineSplitter();
1657
- const encoder = new TextEncoder;
1658
- let socket;
1659
- let queued = new Uint8Array(0);
1660
- let closedNotified = false;
1661
- const notifyClosed = () => {
1662
- if (closedNotified)
1663
- return;
1664
- closedNotified = true;
1665
- handlers.onClose({ exitCode: null, stderrTail: [] });
1666
- };
1667
- const flush = () => {
1668
- if (!socket || queued.length === 0)
1669
- return;
1670
- const written = socket.write(queued);
1671
- queued = written >= queued.length ? new Uint8Array(0) : queued.slice(Math.max(written, 0));
1672
- };
1673
- try {
1674
- const connected = await Bun.connect({
1675
- unix: opts.socketPath,
1676
- socket: {
1677
- open(s) {
1678
- socket = s;
1679
- },
1680
- data(_s, chunk) {
1681
- for (const line of splitter.push(chunk))
1682
- handlers.onLine(line);
1683
- },
1684
- drain() {
1685
- flush();
1686
- },
1687
- close() {
1688
- notifyClosed();
1689
- },
1690
- error() {
1691
- notifyClosed();
1692
- },
1693
- end() {
1694
- notifyClosed();
1695
- }
1696
- }
1697
- });
1698
- socket = connected;
1699
- } catch (error) {
1700
- const reason = error instanceof Error ? error.message : String(error);
1701
- return {
1702
- success: false,
1703
- error: driverError("E_HELPER_CRASHED", `cannot connect to warden-simd socket ${opts.socketPath}: ${reason}`, {
1704
- detail: { socketPath: opts.socketPath }
1705
- })
1706
- };
1707
- }
1708
- return {
1709
- success: true,
1710
- data: {
1711
- send(data) {
1712
- const bytes = encoder.encode(data);
1713
- const merged = new Uint8Array(queued.length + bytes.length);
1714
- merged.set(queued, 0);
1715
- merged.set(bytes, queued.length);
1716
- queued = merged;
1717
- flush();
1718
- },
1719
- async close() {
1720
- try {
1721
- socket?.end();
1722
- } catch {}
1723
- notifyClosed();
1724
- }
1725
- }
1726
- };
1727
- };
1728
- }
1729
-
1730
1251
  // ../../packages/e2e/src/driver/android/uiad-protocol.ts
1731
- import { z as z3 } from "zod";
1252
+ import { z as z2 } from "zod";
1732
1253
  var UIAD_PROTOCOL = 1;
1733
1254
  var UIAD_MAIN_CLASS = "nz.co.delacour.warden.uiad.Main";
1734
1255
  var STRING_FIELDS = ["class", "pkg", "id", "text", "desc", "hint"];
@@ -1786,48 +1307,48 @@ function readNode(value, path = "elements") {
1786
1307
  node.children = children;
1787
1308
  return node;
1788
1309
  }
1789
- var screenSchema = z3.object({
1790
- width: z3.number(),
1791
- height: z3.number(),
1792
- scale: z3.number().optional(),
1793
- rotation: z3.number().optional()
1310
+ var screenSchema = z2.object({
1311
+ width: z2.number(),
1312
+ height: z2.number(),
1313
+ scale: z2.number().optional(),
1314
+ rotation: z2.number().optional()
1794
1315
  });
1795
- var healthSchema = z3.looseObject({
1796
- version: z3.string(),
1797
- protocol: z3.number(),
1798
- sdk: z3.number(),
1799
- release: z3.string().optional(),
1800
- model: z3.string().optional(),
1801
- uid: z3.number().optional(),
1802
- pid: z3.number().optional(),
1803
- uiAutomation: z3.looseObject({ state: z3.string(), connect: z3.string().optional(), connectMs: z3.number().optional() }),
1804
- events: z3.looseObject({ count: z3.number(), lastAgoMs: z3.number() }).optional(),
1805
- uptimeMs: z3.number().optional(),
1316
+ var healthSchema = z2.looseObject({
1317
+ version: z2.string(),
1318
+ protocol: z2.number(),
1319
+ sdk: z2.number(),
1320
+ release: z2.string().optional(),
1321
+ model: z2.string().optional(),
1322
+ uid: z2.number().optional(),
1323
+ pid: z2.number().optional(),
1324
+ uiAutomation: z2.looseObject({ state: z2.string(), connect: z2.string().optional(), connectMs: z2.number().optional() }),
1325
+ events: z2.looseObject({ count: z2.number(), lastAgoMs: z2.number() }).optional(),
1326
+ uptimeMs: z2.number().optional(),
1806
1327
  screen: screenSchema.optional(),
1807
- displayError: z3.string().optional()
1328
+ displayError: z2.string().optional()
1808
1329
  });
1809
- var settleDataSchema2 = z3.looseObject({
1810
- quiet: z3.boolean(),
1811
- waitedMs: z3.number(),
1812
- via: z3.literal("events"),
1813
- events: z3.number(),
1814
- firstEventMs: z3.number().optional(),
1815
- reason: z3.string().optional()
1330
+ var settleDataSchema = z2.looseObject({
1331
+ quiet: z2.boolean(),
1332
+ waitedMs: z2.number(),
1333
+ via: z2.literal("events"),
1334
+ events: z2.number(),
1335
+ firstEventMs: z2.number().optional(),
1336
+ reason: z2.string().optional()
1816
1337
  });
1817
- var textDataSchema = z3.looseObject({
1818
- text: z3.string(),
1819
- caret: z3.number().optional(),
1820
- caretPlaced: z3.boolean().optional()
1338
+ var textDataSchema = z2.looseObject({
1339
+ text: z2.string(),
1340
+ caret: z2.number().optional(),
1341
+ caretPlaced: z2.boolean().optional()
1821
1342
  });
1822
- var snapshotEnvelope = z3.looseObject({
1823
- backend: z3.literal("uiad"),
1343
+ var snapshotEnvelope = z2.looseObject({
1344
+ backend: z2.literal("uiad"),
1824
1345
  screen: screenSchema,
1825
- windows: z3.array(z3.unknown()).optional(),
1826
- elements: z3.array(z3.unknown()),
1827
- truncated: z3.boolean(),
1828
- nodes: z3.number(),
1829
- readMs: z3.number(),
1830
- ms: z3.number()
1346
+ windows: z2.array(z2.unknown()).optional(),
1347
+ elements: z2.array(z2.unknown()),
1348
+ truncated: z2.boolean(),
1349
+ nodes: z2.number(),
1350
+ readMs: z2.number(),
1351
+ ms: z2.number()
1831
1352
  });
1832
1353
  function readSnapshotData(data) {
1833
1354
  const envelope = snapshotEnvelope.safeParse(data);
@@ -1844,13 +1365,13 @@ function readSnapshotData(data) {
1844
1365
  }
1845
1366
  return { ...envelope.data, elements };
1846
1367
  }
1847
- var readyEventSchema2 = z3.object({
1848
- event: z3.literal("ready"),
1849
- protocol: z3.number(),
1850
- version: z3.string(),
1368
+ var readyEventSchema = z2.object({
1369
+ event: z2.literal("ready"),
1370
+ protocol: z2.number(),
1371
+ version: z2.string(),
1851
1372
  data: healthSchema
1852
1373
  });
1853
- var eventSchema2 = z3.discriminatedUnion("event", [readyEventSchema2, logEventSchema, fatalEventSchema]);
1374
+ var eventSchema = z2.discriminatedUnion("event", [readyEventSchema, logEventSchema, fatalEventSchema]);
1854
1375
  function parseUiadLine(line) {
1855
1376
  let json;
1856
1377
  try {
@@ -1859,7 +1380,7 @@ function parseUiadLine(line) {
1859
1380
  return { success: false, reason: "not JSON" };
1860
1381
  }
1861
1382
  if (isRecord(json) && "event" in json) {
1862
- const event = eventSchema2.safeParse(json);
1383
+ const event = eventSchema.safeParse(json);
1863
1384
  return event.success ? { success: true, message: { kind: "event", event: event.data } } : { success: false, reason: `bad event: ${event.error.message}` };
1864
1385
  }
1865
1386
  const response = responseSchema.safeParse(json);
@@ -1869,7 +1390,7 @@ function parseUiadLine(line) {
1869
1390
  // ../../packages/e2e/src/driver/android/uiad-client.ts
1870
1391
  var DEFAULT_READY_TIMEOUT_MS2 = 5000;
1871
1392
  var DEFAULT_REQUEST_TIMEOUT_MS = 1e4;
1872
- var DEFAULT_GRACE_MS2 = 1000;
1393
+ var DEFAULT_GRACE_MS = 1000;
1873
1394
  var SHUTDOWN_WAIT_MS = 300;
1874
1395
  var NOT_SENT = { sent: false };
1875
1396
  function schemaReader(schema) {
@@ -1966,7 +1487,7 @@ async function startUiad(options) {
1966
1487
  const factory = stdioTransport({
1967
1488
  cmd: options.cmd,
1968
1489
  ...options.spawn ? { spawn: options.spawn } : { spawn: bunSpawn },
1969
- graceMs: options.graceMs ?? DEFAULT_GRACE_MS2
1490
+ graceMs: options.graceMs ?? DEFAULT_GRACE_MS
1970
1491
  });
1971
1492
  const waiting = new Promise((resolve) => {
1972
1493
  settleHandshake = (result) => {
@@ -2044,7 +1565,7 @@ async function startUiad(options) {
2044
1565
  return through(await request("snapshot", { ...params }, opts), readSnapshot, "snapshot");
2045
1566
  },
2046
1567
  async settle(params) {
2047
- return through(await request("settle", params, { timeoutMs: params.timeoutMs + requestTimeoutMs }), schemaReader(settleDataSchema2), "settle");
1568
+ return through(await request("settle", params, { timeoutMs: params.timeoutMs + requestTimeoutMs }), schemaReader(settleDataSchema), "settle");
2048
1569
  },
2049
1570
  async typeText(text) {
2050
1571
  return through(await request("typeText", { text }), schemaReader(textDataSchema), "typeText");
@@ -2126,12 +1647,12 @@ function uiadPresentCommand(devicePath, bytes) {
2126
1647
  var bunPushDex = async (argv, bytes) => {
2127
1648
  try {
2128
1649
  const proc = Bun.spawn([...argv], { stdin: bytes, stdout: "pipe", stderr: "pipe", timeout: 15000 });
2129
- const [out, err2, exitCode] = await Promise.all([
1650
+ const [out, err, exitCode] = await Promise.all([
2130
1651
  new Response(proc.stdout).text(),
2131
1652
  new Response(proc.stderr).text(),
2132
1653
  proc.exited
2133
1654
  ]);
2134
- return { exitCode, output: `${out}${err2}`.trim() };
1655
+ return { exitCode, output: `${out}${err}`.trim() };
2135
1656
  } catch (error) {
2136
1657
  return { exitCode: 127, output: error instanceof Error ? error.message : String(error) };
2137
1658
  }
@@ -3062,9 +2583,9 @@ function buildDriver(options, session, baseScreen, clock, execBinary, facts) {
3062
2583
  const orientation = async (set) => {
3063
2584
  if (set) {
3064
2585
  for (const argv of rotationArgvs(orientationToRotation(set))) {
3065
- const res2 = await input(shellCommand(argv));
3066
- if (!res2.success)
3067
- return res2;
2586
+ const res = await input(shellCommand(argv));
2587
+ if (!res.success)
2588
+ return res;
3068
2589
  }
3069
2590
  return { success: true, data: set };
3070
2591
  }
@@ -3111,8 +2632,8 @@ function buildDriver(options, session, baseScreen, clock, execBinary, facts) {
3111
2632
  };
3112
2633
  const appearance = async (set) => {
3113
2634
  if (set) {
3114
- const res2 = await runChecked(session, shellCommand(uimodeSetArgv(set === "dark")), "E_UNSUPPORTED");
3115
- return res2.success ? { success: true, data: set } : res2;
2635
+ const res = await runChecked(session, shellCommand(uimodeSetArgv(set === "dark")), "E_UNSUPPORTED");
2636
+ return res.success ? { success: true, data: set } : res;
3116
2637
  }
3117
2638
  const res = await runChecked(session, shellCommand(uimodeGetArgv()), "E_UNSUPPORTED");
3118
2639
  if (!res.success)
@@ -3121,12 +2642,12 @@ function buildDriver(options, session, baseScreen, clock, execBinary, facts) {
3121
2642
  };
3122
2643
  const clipboard = async () => driverErr("E_UNSUPPORTED", "Android has no public shell clipboard access; type ASCII with typeText");
3123
2644
  const health = async () => {
3124
- const [release, sdk2, model] = await Promise.all(["ro.build.version.release", "ro.build.version.sdk", "ro.product.model"].map((k) => session.run(shellCommand(getpropArgv(k)))));
2645
+ const [release, sdk, model] = await Promise.all(["ro.build.version.release", "ro.build.version.sdk", "ro.product.model"].map((k) => session.run(shellCommand(getpropArgv(k)))));
3125
2646
  const text = (r) => r?.success ? r.data.stdout.trim() : "";
3126
2647
  const info = {
3127
2648
  platform: "android",
3128
2649
  id: serial,
3129
- version: `Android ${text(release)} (sdk ${text(sdk2)})`,
2650
+ version: `Android ${text(release)} (sdk ${text(sdk)})`,
3130
2651
  transport: "adb",
3131
2652
  screen: baseScreen,
3132
2653
  detail: { model: text(model), dumpRoute: dumper.route() ?? null, uiad: uiad?.status() ?? "off" }
@@ -3159,7 +2680,7 @@ function buildDriver(options, session, baseScreen, clock, execBinary, facts) {
3159
2680
  orientation,
3160
2681
  settle,
3161
2682
  alert,
3162
- alertIn: (snapshot2) => detectAlert(snapshot2.flat),
2683
+ alertIn: (snapshot) => detectAlert(snapshot.flat),
3163
2684
  appearance,
3164
2685
  clipboard,
3165
2686
  app: createAppCommands({
@@ -3180,7 +2701,7 @@ function buildDriver(options, session, baseScreen, clock, execBinary, facts) {
3180
2701
  }
3181
2702
 
3182
2703
  // ../../packages/e2e/src/driver/ios/ios-driver.ts
3183
- import { existsSync as existsSync3 } from "fs";
2704
+ import { existsSync as existsSync2 } from "fs";
3184
2705
  import { dirname as dirname4, join as join6 } from "path";
3185
2706
 
3186
2707
  // ../../packages/e2e/src/driver/ios/ios-session.ts
@@ -3235,7 +2756,7 @@ function swappableClient(initial, o) {
3235
2756
  }
3236
2757
 
3237
2758
  // ../../packages/e2e/src/driver/ios/locate-helper.ts
3238
- import { existsSync as existsSync2, realpathSync } from "fs";
2759
+ import { existsSync, realpathSync } from "fs";
3239
2760
  import { dirname as dirname3, join as join4, resolve } from "path";
3240
2761
  var HELPER_NAME = "warden-simd";
3241
2762
  var DOCTOR_HINT = "run `warden doctor` to check the iOS driver helper, or set WARDEN_SIMD=/path/to/warden-simd";
@@ -3286,7 +2807,7 @@ function resolveHelper(o) {
3286
2807
  execPath: o.execPath ?? process.execPath,
3287
2808
  importDir: o.importDir ?? import.meta.dir,
3288
2809
  wardenHome: o.wardenHome ?? wardenHome(),
3289
- exists: o.exists ?? existsSync2,
2810
+ exists: o.exists ?? existsSync,
3290
2811
  realpath: o.realpath ?? realpathSync,
3291
2812
  ...o.version ? { version: o.version } : {}
3292
2813
  });
@@ -3554,6 +3075,20 @@ var statusBarClearArgv = (udid) => [...XCRUN, "status_bar", udid, "clear"];
3554
3075
  function pbcopyArgv(udid, text) {
3555
3076
  return ["sh", "-c", 'printf %s "$1" | xcrun simctl pbcopy "$2"', "sh", text, udid];
3556
3077
  }
3078
+ function devicectlPbcopyArgv(udid, text) {
3079
+ return [
3080
+ "sh",
3081
+ "-c",
3082
+ 'printf %s "$1" | xcrun devicectl device pasteboard copy --quiet --device "$2"',
3083
+ "sh",
3084
+ text,
3085
+ udid
3086
+ ];
3087
+ }
3088
+ var PBCOPY_CHANNELS = [
3089
+ { name: "devicectl pasteboard copy", argv: devicectlPbcopyArgv },
3090
+ { name: "simctl pbcopy", argv: pbcopyArgv }
3091
+ ];
3557
3092
  function escapeRegExp(text) {
3558
3093
  return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3559
3094
  }
@@ -3645,7 +3180,26 @@ function createSimctl(deps) {
3645
3180
  const code = /is not installed/.test(reset.error) ? "E_APP_UNAVAILABLE" : "E_IO";
3646
3181
  return { success: false, error: driverError(code, reset.error) };
3647
3182
  },
3648
- pbcopy: (text) => runVoid(pbcopyArgv(udid, text)),
3183
+ async pbcopy(text) {
3184
+ const outcomes = [];
3185
+ for (const channel of PBCOPY_CHANNELS) {
3186
+ const copied = await exec(channel.argv(udid, text), { timeoutMs: SHORT_MS });
3187
+ if (copied.exitCode !== 0) {
3188
+ outcomes.push(`${channel.name} exit ${copied.exitCode}`);
3189
+ continue;
3190
+ }
3191
+ const read = await run(pbpasteArgv(udid), SHORT_MS);
3192
+ if (!read.ok)
3193
+ return { success: false, error: read.error };
3194
+ if (read.res.stdout === text)
3195
+ return ok4(undefined);
3196
+ outcomes.push(`${channel.name} exit 0 but pbpaste reads other text`);
3197
+ }
3198
+ return {
3199
+ success: false,
3200
+ error: driverError("E_IO", `simulator pasteboard did not take the text (${outcomes.join("; ")})`)
3201
+ };
3202
+ },
3649
3203
  async pbpaste() {
3650
3204
  const result = await run(pbpasteArgv(udid), SHORT_MS);
3651
3205
  return result.ok ? ok4(result.res.stdout) : { success: false, error: result.error };
@@ -3682,299 +3236,6 @@ function createSimctl(deps) {
3682
3236
  };
3683
3237
  }
3684
3238
 
3685
- // ../../packages/e2e/src/driver/ios/simd-client.ts
3686
- var DEFAULT_TIMEOUT_MS = 1e4;
3687
- var DEFAULT_READY_TIMEOUT_MS3 = 15000;
3688
- var SHUTDOWN_TIMEOUT_MS2 = 500;
3689
- var STDERR_TAIL2 = 20;
3690
- function crashed(info, what) {
3691
- const exit = info?.exitCode === null || info?.exitCode === undefined ? "" : ` (exit ${info.exitCode})`;
3692
- const tail = info?.stderrTail.length ? `: ${info.stderrTail.slice(-3).join(" | ")}` : "";
3693
- return driverError("E_HELPER_CRASHED", `warden-simd ${what}${exit}${tail}`, {
3694
- detail: info ? { exitCode: info.exitCode, stderrTail: info.stderrTail } : undefined
3695
- });
3696
- }
3697
- async function createSimdClient(factory, options = {}) {
3698
- const defaultTimeoutMs = options.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS;
3699
- const readyTimeoutMs = options.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS3;
3700
- const maxRespawns = options.maxRespawns ?? 0;
3701
- const shutdownOnClose = options.shutdownOnClose ?? false;
3702
- const log = options.log;
3703
- const pending = new Map;
3704
- const listeners = new Map;
3705
- let seq = 0;
3706
- let respawns = 0;
3707
- let closed = false;
3708
- let closing = false;
3709
- let shutdownAcked = false;
3710
- let current;
3711
- let opening;
3712
- let lastCrash;
3713
- let fatalError;
3714
- function emit(event, payload) {
3715
- for (const listener of listeners.get(event) ?? []) {
3716
- try {
3717
- listener(payload);
3718
- } catch (error) {
3719
- log?.(`[simd] ${event} listener threw: ${error instanceof Error ? error.message : String(error)}`);
3720
- }
3721
- }
3722
- }
3723
- function failAll(error) {
3724
- for (const [id, entry] of pending) {
3725
- clearTimeout(entry.timer);
3726
- pending.delete(id);
3727
- entry.resolve({ success: false, error });
3728
- }
3729
- }
3730
- function handleResponse(response) {
3731
- respawns = 0;
3732
- const entry = pending.get(response.id);
3733
- if (!entry) {
3734
- log?.(`[simd] response for unknown id ${response.id} (timed out?)`);
3735
- return;
3736
- }
3737
- clearTimeout(entry.timer);
3738
- pending.delete(response.id);
3739
- entry.resolve(response.ok ? { success: true, data: response.data ?? null } : { success: false, error: mapWireError(response.error) });
3740
- }
3741
- function handleFatal(session, event) {
3742
- const error = mapWireError(event.error);
3743
- fatalError = error;
3744
- log?.(`[simd] fatal: ${error.code}: ${error.message}`);
3745
- emit("fatal", error);
3746
- if (session.handshaking) {
3747
- session.handshaking = false;
3748
- session.handshake({ success: false, error });
3749
- }
3750
- failAll(error);
3751
- }
3752
- function handleEvent(session, event) {
3753
- if (event.event === "log") {
3754
- log?.(`[simd] ${event.level}: ${event.message}`);
3755
- emit("log", event);
3756
- } else if (event.event === "fatal") {
3757
- handleFatal(session, event);
3758
- } else if (session.handshaking) {
3759
- session.handshaking = false;
3760
- session.ready = event;
3761
- emit("ready", event);
3762
- session.handshake({ success: true, data: event });
3763
- }
3764
- }
3765
- function handleMessage(session, message) {
3766
- if (message.kind === "response")
3767
- handleResponse(message.response);
3768
- else
3769
- handleEvent(session, message.event);
3770
- }
3771
- function handleLine(session, line) {
3772
- const parsed = parseLine(line);
3773
- if (!parsed.success) {
3774
- log?.(`[simd] ignoring line (${parsed.reason}): ${line.slice(0, 200)}`);
3775
- return;
3776
- }
3777
- handleMessage(session, parsed.message);
3778
- }
3779
- function handleClose(session, info) {
3780
- if (!session.alive)
3781
- return;
3782
- session.alive = false;
3783
- const expected = closed || closing;
3784
- emit("exit", { exitCode: info.exitCode, expected });
3785
- if (session.handshaking) {
3786
- session.handshaking = false;
3787
- session.handshake({ success: false, error: fatalError ?? crashed(info, "exited before it was ready") });
3788
- return;
3789
- }
3790
- if (fatalError)
3791
- return;
3792
- const error = crashed(info, expected ? "stopped" : "exited unexpectedly");
3793
- if (!expected)
3794
- lastCrash = error;
3795
- failAll(expected ? driverError("E_INTERNAL", "client closed", { retryable: false }) : error);
3796
- }
3797
- async function openSession() {
3798
- let resolveHandshake = () => {
3799
- return;
3800
- };
3801
- const handshake = new Promise((resolve2) => {
3802
- resolveHandshake = resolve2;
3803
- });
3804
- const session = { alive: true, handshake: resolveHandshake, handshaking: true };
3805
- const stderrTail = [];
3806
- const opened = await factory({
3807
- onLine: (line) => handleLine(session, line),
3808
- onDiagnostic: (line) => {
3809
- stderrTail.push(line);
3810
- if (stderrTail.length > STDERR_TAIL2)
3811
- stderrTail.shift();
3812
- log?.(`[simd:stderr] ${line}`);
3813
- },
3814
- onClose: (info) => handleClose(session, { ...info, stderrTail: info.stderrTail.length ? info.stderrTail : [...stderrTail] })
3815
- });
3816
- if (!opened.success)
3817
- return opened;
3818
- session.transport = opened.data;
3819
- let timer;
3820
- const timeout = new Promise((resolve2) => {
3821
- timer = setTimeout(() => resolve2({
3822
- success: false,
3823
- error: driverError("E_TIMEOUT", `warden-simd sent no ready event within ${readyTimeoutMs} ms`)
3824
- }), readyTimeoutMs);
3825
- });
3826
- const ready = await Promise.race([handshake, timeout]);
3827
- clearTimeout(timer);
3828
- if (!ready.success) {
3829
- session.handshaking = false;
3830
- session.alive = false;
3831
- await session.transport.close().catch(() => {
3832
- return;
3833
- });
3834
- return ready;
3835
- }
3836
- if (ready.data.protocol !== protocolVersion) {
3837
- session.alive = false;
3838
- await session.transport.close().catch(() => {
3839
- return;
3840
- });
3841
- return {
3842
- success: false,
3843
- error: driverError("E_INTERNAL", `warden-simd speaks protocol ${ready.data.protocol}, this client expects ${protocolVersion}; rebuild or reinstall the helper`, { retryable: false })
3844
- };
3845
- }
3846
- current = session;
3847
- return { success: true, data: session };
3848
- }
3849
- async function ensureSession() {
3850
- if (current?.alive)
3851
- return { success: true, data: current };
3852
- if (opening)
3853
- return opening;
3854
- if (respawns >= maxRespawns) {
3855
- return {
3856
- success: false,
3857
- error: lastCrash ?? driverError("E_HELPER_CRASHED", "warden-simd connection is closed")
3858
- };
3859
- }
3860
- respawns += 1;
3861
- log?.(`[simd] respawning helper (${respawns}/${maxRespawns})`);
3862
- opening = openSession().finally(() => {
3863
- opening = undefined;
3864
- });
3865
- return opening;
3866
- }
3867
- function validate(op, data) {
3868
- if (!hasOpSchema(op))
3869
- return { success: true, data };
3870
- const parsed = opSchemas[op].safeParse(data);
3871
- if (parsed.success)
3872
- return { success: true, data: parsed.data };
3873
- return {
3874
- success: false,
3875
- error: driverError("E_INTERNAL", `warden-simd returned an unexpected \`${op}\` payload: ${parsed.error.message}`, {
3876
- retryable: false
3877
- })
3878
- };
3879
- }
3880
- async function request(op, params, opts) {
3881
- if (closed)
3882
- return { success: false, error: driverError("E_INTERNAL", "warden-simd client is closed", { retryable: false }) };
3883
- if (fatalError)
3884
- return { success: false, error: fatalError };
3885
- const session = await ensureSession();
3886
- if (!session.success)
3887
- return session;
3888
- const transport = session.data.transport;
3889
- if (!transport)
3890
- return { success: false, error: driverError("E_INTERNAL", "warden-simd transport missing") };
3891
- const timeoutMs = opts.timeoutMs ?? defaultTimeoutMs;
3892
- const id = String(++seq);
3893
- const result = await new Promise((resolve2) => {
3894
- const timer = setTimeout(() => {
3895
- pending.delete(id);
3896
- resolve2({
3897
- success: false,
3898
- error: driverError("E_TIMEOUT", `warden-simd \`${op}\` timed out after ${timeoutMs} ms`, {
3899
- detail: { op, timeoutMs }
3900
- })
3901
- });
3902
- }, timeoutMs);
3903
- pending.set(id, { op, resolve: resolve2, timer });
3904
- try {
3905
- transport.send(encodeRequest({ timeoutMs, ...params, id, op }));
3906
- } catch (error) {
3907
- clearTimeout(timer);
3908
- pending.delete(id);
3909
- resolve2({
3910
- success: false,
3911
- error: crashed(undefined, `rejected the \`${op}\` request: ${error instanceof Error ? error.message : String(error)}`)
3912
- });
3913
- }
3914
- });
3915
- if (result.success && op === "shutdown")
3916
- shutdownAcked = true;
3917
- return result.success ? validate(op, result.data) : result;
3918
- }
3919
- const first = await openSession();
3920
- if (!first.success)
3921
- return first;
3922
- const client = {
3923
- get ready() {
3924
- const ready = current?.ready ?? first.data.ready;
3925
- if (!ready)
3926
- throw new Error("warden-simd client has no handshake");
3927
- return ready;
3928
- },
3929
- request: (op, params = {}, opts = {}) => request(op, params, opts),
3930
- on(event, handler) {
3931
- const stored = handler;
3932
- const set = listeners.get(event) ?? new Set;
3933
- set.add(stored);
3934
- listeners.set(event, set);
3935
- return () => {
3936
- set.delete(stored);
3937
- };
3938
- },
3939
- async close() {
3940
- if (closed)
3941
- return;
3942
- closing = true;
3943
- const session = current;
3944
- if (shutdownOnClose && session?.alive && !fatalError && !shutdownAcked) {
3945
- await request("shutdown", {}, { timeoutMs: SHUTDOWN_TIMEOUT_MS2 });
3946
- }
3947
- closed = true;
3948
- failAll(driverError("E_INTERNAL", "warden-simd client closed", { retryable: false }));
3949
- await session?.transport?.close().catch(() => {
3950
- return;
3951
- });
3952
- }
3953
- };
3954
- return { success: true, data: client };
3955
- }
3956
- function spawnSimd(options) {
3957
- const cmd = [
3958
- options.helperPath,
3959
- "--udid",
3960
- options.udid,
3961
- ...options.resources ? ["--resources", options.resources] : [],
3962
- ...options.args ?? []
3963
- ];
3964
- return createSimdClient(stdioTransport({ cmd, spawn: options.spawn, graceMs: options.graceMs }), {
3965
- ...options,
3966
- maxRespawns: options.maxRespawns ?? 1,
3967
- shutdownOnClose: options.shutdownOnClose ?? true
3968
- });
3969
- }
3970
- function connectSimd(options) {
3971
- return createSimdClient(socketTransport({ socketPath: options.socketPath }), {
3972
- ...options,
3973
- maxRespawns: options.maxRespawns ?? 0,
3974
- shutdownOnClose: options.shutdownOnClose ?? false
3975
- });
3976
- }
3977
-
3978
3239
  // ../../packages/e2e/src/driver/ios/type-budget.ts
3979
3240
  var TYPE_BASE_MS = 1e4;
3980
3241
  var TYPE_PER_CHAR_MS = 70;
@@ -4050,6 +3311,25 @@ function nativeDetail(data) {
4050
3311
  noteSpanDetail("frontmostPid", frontmostPid);
4051
3312
  return detail;
4052
3313
  }
3314
+ function noteRebind(data) {
3315
+ noteSpanDetail("rebind", data.bound ? "bound" : data.reason ?? "unbound");
3316
+ noteSpanDetail("rebindMs", data.waitedMs);
3317
+ if (data.restarts !== undefined && data.restarts > 0)
3318
+ noteSpanDetail("rebindRestarts", data.restarts);
3319
+ if (data.recycled === true)
3320
+ noteSpanDetail("rebindRecycled", true);
3321
+ }
3322
+ function healthDetail(d) {
3323
+ return {
3324
+ axDefault: d.ax.default,
3325
+ ...d.ax.bridge === "degraded" ? { axBridge: "degraded" } : {},
3326
+ ...d.ax.lastError ? { axLastError: d.ax.lastError } : {},
3327
+ ...d.ax.error !== undefined ? { axError: d.ax.error } : {},
3328
+ ...d.socket ? { socket: d.socket } : {},
3329
+ ...d.xcode ? { xcode: d.xcode } : {},
3330
+ ...d.state ? { state: d.state } : {}
3331
+ };
3332
+ }
4053
3333
 
4054
3334
  class DriverFailure extends Error {
4055
3335
  error;
@@ -4216,36 +3496,37 @@ function createIosDriver(swap, simctl, opts) {
4216
3496
  opts.log?.(`[ios] rebind after launch failed (${reply.error.code}): ${reply.error.message}`);
4217
3497
  return false;
4218
3498
  }
4219
- noteSpanDetail("rebind", reply.data.bound ? "bound" : reply.data.reason ?? "unbound");
4220
- noteSpanDetail("rebindMs", reply.data.waitedMs);
4221
- if (reply.data.restarts !== undefined && reply.data.restarts > 0)
4222
- noteSpanDetail("rebindRestarts", reply.data.restarts);
4223
- if (reply.data.recycled === true)
4224
- noteSpanDetail("rebindRecycled", true);
3499
+ noteRebind(reply.data);
4225
3500
  return reply.data.bound;
4226
3501
  }
3502
+ async function settleViaHelper(quietMs, timeoutMs) {
3503
+ const expected = launchedBundle;
3504
+ const native = await request("settle", { quietMs, settleTimeoutMs: timeoutMs, ...expected === undefined ? {} : { bundleId: expected } }, { timeoutMs: timeoutMs + SETTLE_MARGIN_MS });
3505
+ if (native.success) {
3506
+ if (expected !== undefined && native.data.reason !== "frontmost_mismatch")
3507
+ launchedBundle = undefined;
3508
+ return { success: true, data: nativeDetail(native.data) };
3509
+ }
3510
+ if (native.error.code === "E_TIMEOUT") {
3511
+ noteSpanDetail("reason", "helper_timeout");
3512
+ return {
3513
+ success: true,
3514
+ data: { quiet: false, waitedMs: timeoutMs + SETTLE_MARGIN_MS, reason: "helper_timeout" }
3515
+ };
3516
+ }
3517
+ if (native.error.code !== "E_UNSUPPORTED")
3518
+ return native;
3519
+ settleUnsupported = true;
3520
+ opts.log?.("[ios] native settle unsupported, polling snapshot hashes");
3521
+ return;
3522
+ }
4227
3523
  async function settle(o = {}) {
4228
3524
  const quietMs = o.quietMs ?? DEFAULT_QUIET_MS;
4229
3525
  const timeoutMs = o.timeoutMs ?? DEFAULT_SETTLE_TIMEOUT_MS2;
4230
3526
  if (!settleUnsupported) {
4231
- const expected = launchedBundle;
4232
- const native = await request("settle", { quietMs, settleTimeoutMs: timeoutMs, ...expected === undefined ? {} : { bundleId: expected } }, { timeoutMs: timeoutMs + SETTLE_MARGIN_MS });
4233
- if (native.success) {
4234
- if (expected !== undefined && native.data.reason !== "frontmost_mismatch")
4235
- launchedBundle = undefined;
4236
- return { success: true, data: nativeDetail(native.data) };
4237
- }
4238
- if (native.error.code === "E_TIMEOUT") {
4239
- noteSpanDetail("reason", "helper_timeout");
4240
- return {
4241
- success: true,
4242
- data: { quiet: false, waitedMs: timeoutMs + SETTLE_MARGIN_MS, reason: "helper_timeout" }
4243
- };
4244
- }
4245
- if (native.error.code !== "E_UNSUPPORTED")
3527
+ const native = await settleViaHelper(quietMs, timeoutMs);
3528
+ if (native)
4246
3529
  return native;
4247
- settleUnsupported = true;
4248
- opts.log?.("[ios] native settle unsupported, polling snapshot hashes");
4249
3530
  }
4250
3531
  return hashSettle({ quietMs, timeoutMs });
4251
3532
  }
@@ -4303,15 +3584,7 @@ function createIosDriver(swap, simctl, opts) {
4303
3584
  transport: d.hid.transport,
4304
3585
  bridge: d.ax.bridge === "degraded" ? "available" : d.ax.bridge,
4305
3586
  screen: d.screen,
4306
- detail: {
4307
- axDefault: d.ax.default,
4308
- ...d.ax.bridge === "degraded" ? { axBridge: "degraded" } : {},
4309
- ...d.ax.lastError ? { axLastError: d.ax.lastError } : {},
4310
- ...d.ax.error !== undefined ? { axError: d.ax.error } : {},
4311
- ...d.socket ? { socket: d.socket } : {},
4312
- ...d.xcode ? { xcode: d.xcode } : {},
4313
- ...d.state ? { state: d.state } : {}
4314
- }
3587
+ detail: healthDetail(d)
4315
3588
  }
4316
3589
  };
4317
3590
  }
@@ -4431,7 +3704,7 @@ async function connect(o, log) {
4431
3704
  helperPath: helper.data,
4432
3705
  udid: o.udid,
4433
3706
  log,
4434
- ...(o.exists ?? existsSync3)(resources) ? { resources } : {},
3707
+ ...(o.exists ?? existsSync2)(resources) ? { resources } : {},
4435
3708
  ...o.spawn ? { spawn: o.spawn } : {},
4436
3709
  ...timeoutOption(o)
4437
3710
  });
@@ -4535,12 +3808,12 @@ function codeFor(code) {
4535
3808
  return "ACTION_FAILED";
4536
3809
  }
4537
3810
  function toE2eError(error, action) {
4538
- return new E2eError(codeFor(error.code), `${action} failed: ${error.code}: ${error.message}`);
3811
+ return new E2eError2(codeFor(error.code), `${action} failed: ${error.code}: ${error.message}`);
4539
3812
  }
4540
3813
  function aborted(action, signal) {
4541
3814
  const reason = signal.reason;
4542
3815
  const detail = reason instanceof Error ? `: ${reason.message}` : "";
4543
- return new E2eError("TIMEOUT", `${action} aborted (test deadline reached or cancelled)${detail}`);
3816
+ return new E2eError2("TIMEOUT", `${action} aborted (test deadline reached or cancelled)${detail}`);
4544
3817
  }
4545
3818
  async function settled(call, action, signal) {
4546
3819
  if (signal === undefined)
@@ -4569,7 +3842,7 @@ async function unwrap(call, action, signal) {
4569
3842
  // ../../packages/e2e/src/fixture-session.ts
4570
3843
  var systemClock = {
4571
3844
  now: () => Date.now(),
4572
- sleep: (ms) => new Promise((resolve2) => setTimeout(resolve2, ms))
3845
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms))
4573
3846
  };
4574
3847
 
4575
3848
  // ../../packages/e2e/src/app.ts
@@ -4606,11 +3879,11 @@ function parseWait(wait) {
4606
3879
  if (typeof wait === "object" && wait !== null && isLocator(wait.until)) {
4607
3880
  const { timeout } = wait;
4608
3881
  if (timeout !== undefined && (!Number.isFinite(timeout) || timeout < 0)) {
4609
- throw new E2eError("INVALID_ARGUMENT", `app.open wait timeout must be a non-negative number, got ${timeout}`);
3882
+ throw new E2eError2("INVALID_ARGUMENT", `app.open wait timeout must be a non-negative number, got ${timeout}`);
4610
3883
  }
4611
3884
  return timeout === undefined ? { kind: "until", until: wait.until } : { kind: "until", until: wait.until, timeout };
4612
3885
  }
4613
- throw new E2eError("INVALID_ARGUMENT", `app.open wait must be "quiet", "none" or { until: <locator made by screen> }, got ${describeWait(wait)}`);
3886
+ throw new E2eError2("INVALID_ARGUMENT", `app.open wait must be "quiet", "none" or { until: <locator made by screen> }, got ${describeWait(wait)}`);
4614
3887
  }
4615
3888
  function describeWait(wait) {
4616
3889
  if (typeof wait === "string")
@@ -4689,7 +3962,7 @@ function createApp({ session, driver, env, log = stderrLog2, clock = systemClock
4689
3962
  async reinstall() {
4690
3963
  const path = env.appPath;
4691
3964
  if (path === undefined) {
4692
- throw new E2eError("INVALID_ARGUMENT", "app.reinstall() needs WARDEN_APP_PATH (the build to install) to be set");
3965
+ throw new E2eError2("INVALID_ARGUMENT", "app.reinstall() needs WARDEN_APP_PATH (the build to install) to be set");
4693
3966
  }
4694
3967
  const installed = await unwrap(() => driver.app.isInstalled(bundleId), `check ${bundleId}`, session.signal);
4695
3968
  if (installed)
@@ -4762,11 +4035,11 @@ function createAlertReader({ driver, clock, signal }) {
4762
4035
 
4763
4036
  // ../../packages/e2e/src/device-devtools.ts
4764
4037
  function createDeviceDevtools(source) {
4765
- const connect2 = async () => {
4038
+ const connect = async () => {
4766
4039
  const attached = await source.devtools();
4767
4040
  if (attached.success)
4768
4041
  return attached.data;
4769
- throw new E2eError("SESSION", `devtools unavailable: ${attached.error.message}`);
4042
+ throw new E2eError2("SESSION", `devtools unavailable: ${attached.error.message}`);
4770
4043
  };
4771
4044
  const attach = () => source.devtools();
4772
4045
  return {
@@ -4786,24 +4059,24 @@ function createDeviceDevtools(source) {
4786
4059
  },
4787
4060
  network: {
4788
4061
  async entries(query) {
4789
- return (await connect2()).network.entries(query);
4062
+ return (await connect()).network.entries(query);
4790
4063
  },
4791
4064
  async clear() {
4792
- await (await connect2()).network.clear();
4065
+ await (await connect()).network.clear();
4793
4066
  }
4794
4067
  },
4795
4068
  console: {
4796
4069
  async lines() {
4797
- return (await connect2()).console.lines();
4070
+ return (await connect()).console.lines();
4798
4071
  },
4799
4072
  async clear() {
4800
- (await connect2()).console.clear();
4073
+ (await connect()).console.clear();
4801
4074
  },
4802
4075
  async mark(label) {
4803
- (await connect2()).console.mark(label);
4076
+ (await connect()).console.mark(label);
4804
4077
  }
4805
4078
  },
4806
- connect: connect2
4079
+ connect
4807
4080
  };
4808
4081
  }
4809
4082
 
@@ -4864,8 +4137,8 @@ async function lookForSheet(deps, plan, watch, started, remembered) {
4864
4137
  watch.lastSheetAt = now;
4865
4138
  watch.sheetSeenAt ??= now;
4866
4139
  const since = (at) => at === undefined ? Number.POSITIVE_INFINITY : now - at;
4867
- const settled2 = since(watch.sheetSeenAt) >= SHEET_SETTLE_MS && since(watch.lastPressAt) >= PRESS_GAP_MS;
4868
- if (!settled2)
4140
+ const settled = since(watch.sheetSeenAt) >= SHEET_SETTLE_MS && since(watch.lastPressAt) >= PRESS_GAP_MS;
4141
+ if (!settled)
4869
4142
  return { sheetUp: true, confirmed: false, finished: false };
4870
4143
  if (watch.presses >= MAX_PRESSES)
4871
4144
  return { sheetUp: true, confirmed: false, finished: plan.until === undefined };
@@ -4954,11 +4227,11 @@ function alertTarget(info, action, platform) {
4954
4227
  const target = findButton(buttons, action.button);
4955
4228
  if (target === undefined) {
4956
4229
  const names = buttons.map((b) => nameOf(b) ?? "?").join(", ");
4957
- throw new E2eError("ACTION_FAILED", `alert has no "${action.button}" button (buttons: ${names})`);
4230
+ throw new E2eError2("ACTION_FAILED", `alert has no "${action.button}" button (buttons: ${names})`);
4958
4231
  }
4959
4232
  return target;
4960
4233
  }
4961
- function createDevice({ session, driver, env, clock = systemClock }) {
4234
+ function createDevice({ session, driver, env, clock = systemClock, perfExec }) {
4962
4235
  const issued = new Map;
4963
4236
  const signal = () => session.signal;
4964
4237
  const pressAlertButton = async (button, action) => {
@@ -4980,7 +4253,7 @@ function createDevice({ session, driver, env, clock = systemClock }) {
4980
4253
  return false;
4981
4254
  }
4982
4255
  };
4983
- const readSnapshot2 = (opts) => unwrap(() => driver.snapshot(opts), "snapshot", signal());
4256
+ const readSnapshot = (opts) => unwrap(() => driver.snapshot(opts), "snapshot", signal());
4984
4257
  const { devtools: attach, peekDevtools } = session;
4985
4258
  let devtools;
4986
4259
  const lazyDevtools = () => {
@@ -4995,10 +4268,19 @@ function createDevice({ session, driver, env, clock = systemClock }) {
4995
4268
  }
4996
4269
  return devtools;
4997
4270
  };
4271
+ const perf = createDevicePerf({
4272
+ exec: perfExec ?? spawnWarden(env.wardenBin),
4273
+ udid: env.udid,
4274
+ budgets: parseBudgets(env.perfBudgets),
4275
+ attemptDir: () => session.attemptDir,
4276
+ addFile: (relative) => session.attempt?.files.add(relative),
4277
+ ...env.wardenBin === undefined && perfExec === undefined ? { unavailable: "WARDEN_BIN is not set (run the spec with `warden e2e`)" } : {}
4278
+ });
4998
4279
  const device = {
4999
4280
  get devtools() {
5000
4281
  return lazyDevtools();
5001
4282
  },
4283
+ perf,
5002
4284
  async alert(action) {
5003
4285
  const info = await readAlert("axbridge");
5004
4286
  if (info === null || info.buttons.length === 0)
@@ -5014,7 +4296,7 @@ function createDevice({ session, driver, env, clock = systemClock }) {
5014
4296
  async openLink(url, options = {}) {
5015
4297
  const waitMs = options.waitMs ?? OPEN_LINK_WAIT_MS;
5016
4298
  if (!Number.isFinite(waitMs) || waitMs < 0) {
5017
- throw new E2eError("INVALID_ARGUMENT", `openLink waitMs must be a non-negative number, got ${String(waitMs)}`);
4299
+ throw new E2eError2("INVALID_ARGUMENT", `openLink waitMs must be a non-negative number, got ${String(waitMs)}`);
5018
4300
  }
5019
4301
  await unwrap(() => driver.app.openUrl(url), `open ${url}`, signal());
5020
4302
  const answersSheet = options.accept !== false && driver.platform === "ios" && driver.capabilities.alerts;
@@ -5034,7 +4316,7 @@ function createDevice({ session, driver, env, clock = systemClock }) {
5034
4316
  },
5035
4317
  async back() {
5036
4318
  if (driver.platform === "ios") {
5037
- throw new E2eError("INVALID_ARGUMENT", "device.back() is Android only: iOS has no back key");
4319
+ throw new E2eError2("INVALID_ARGUMENT", "device.back() is Android only: iOS has no back key");
5038
4320
  }
5039
4321
  await unwrap(() => driver.pressButton("back"), "press back", signal());
5040
4322
  },
@@ -5051,7 +4333,7 @@ function createDevice({ session, driver, env, clock = systemClock }) {
5051
4333
  await unwrap(() => driver.clipboard(text), "set clipboard", signal());
5052
4334
  },
5053
4335
  async hideKeyboard() {
5054
- const snapshot = await readSnapshot2();
4336
+ const snapshot = await readSnapshot();
5055
4337
  if (!snapshot.flat.some((element) => element.role === "keyboard"))
5056
4338
  return;
5057
4339
  if (driver.platform === "ios")
@@ -5073,7 +4355,7 @@ function createDevice({ session, driver, env, clock = systemClock }) {
5073
4355
  },
5074
4356
  async snapshot(options = {}) {
5075
4357
  const interactiveOnly = options.interactiveOnly ?? false;
5076
- const snapshot = await readSnapshot2({ interactiveOnly });
4358
+ const snapshot = await readSnapshot({ interactiveOnly });
5077
4359
  const rendered = renderSnapshot(snapshot, { interactiveOnly, refs: new Map });
5078
4360
  return [renderHeader({ snapshot, rendered, interactiveOnly }), ...rendered.lines].join(`
5079
4361
  `);
@@ -5086,7 +4368,7 @@ function createDevice({ session, driver, env, clock = systemClock }) {
5086
4368
  return settleDetailOf(native.data);
5087
4369
  if (native.error.code !== "E_UNSUPPORTED")
5088
4370
  throw toE2eError(native.error, "settle");
5089
- const polled = await settleByHash(async () => snapshotHash(await readSnapshot2()), {
4371
+ const polled = await settleByHash(async () => snapshotHash(await readSnapshot()), {
5090
4372
  quietMs,
5091
4373
  timeoutMs,
5092
4374
  now: clock.now,
@@ -5102,16 +4384,16 @@ function createDevice({ session, driver, env, clock = systemClock }) {
5102
4384
  var systemClock2 = {
5103
4385
  now: () => Date.now(),
5104
4386
  sleep(ms, signal) {
5105
- return new Promise((resolve2) => {
4387
+ return new Promise((resolve) => {
5106
4388
  if (signal?.aborted)
5107
- return resolve2();
4389
+ return resolve();
5108
4390
  const onAbort = () => {
5109
4391
  clearTimeout(timer);
5110
- resolve2();
4392
+ resolve();
5111
4393
  };
5112
4394
  const timer = setTimeout(() => {
5113
4395
  signal?.removeEventListener("abort", onAbort);
5114
- resolve2();
4396
+ resolve();
5115
4397
  }, ms);
5116
4398
  signal?.addEventListener("abort", onAbort, { once: true });
5117
4399
  });
@@ -5285,13 +4567,13 @@ class Runtime {
5285
4567
  }
5286
4568
  }
5287
4569
  function driverFailure(verb, error, code = "DRIVER") {
5288
- return new E2eError(code, `${verb} failed: ${error.code}: ${error.message}`);
4570
+ return new E2eError2(code, `${verb} failed: ${error.code}: ${error.message}`);
5289
4571
  }
5290
4572
  function abortError(signal) {
5291
4573
  const reason = signal?.reason;
5292
- if (reason instanceof E2eError)
4574
+ if (reason instanceof E2eError2)
5293
4575
  return reason;
5294
- return new E2eError("TIMEOUT", "the test was aborted before the action finished");
4576
+ return new E2eError2("TIMEOUT", "the test was aborted before the action finished");
5295
4577
  }
5296
4578
 
5297
4579
  // ../../packages/e2e/src/screen/screen.ts
@@ -5306,7 +4588,7 @@ function createScreen(host) {
5306
4588
  const allowed = "direction" in options ? ["direction", "momentum"] : ["from", "to", "durationMs"];
5307
4589
  const unknown = keys.filter((key) => !allowed.includes(key));
5308
4590
  if (unknown.length > 0 || keys.length === 0 || "direction" in options && "from" in options) {
5309
- throw new E2eError("INVALID_ARGUMENT", "swipe takes either { direction, momentum? } or { from, to, durationMs? }, not a mix");
4591
+ throw new E2eError2("INVALID_ARGUMENT", "swipe takes either { direction, momentum? } or { from, to, durationMs? }, not a mix");
5310
4592
  }
5311
4593
  return rt.step("swipe", async () => {
5312
4594
  if ("direction" in options) {
@@ -5316,7 +4598,7 @@ function createScreen(host) {
5316
4598
  return;
5317
4599
  }
5318
4600
  if (!isFinitePoint(options.from) || !isFinitePoint(options.to)) {
5319
- throw new E2eError("INVALID_ARGUMENT", "swipe needs finite coordinates for from and to");
4601
+ throw new E2eError2("INVALID_ARGUMENT", "swipe needs finite coordinates for from and to");
5320
4602
  }
5321
4603
  const driver = await host.driver();
5322
4604
  const opts = options.durationMs === undefined ? undefined : { durationMs: options.durationMs };
@@ -5333,7 +4615,7 @@ function createScreen(host) {
5333
4615
  locator: (selector) => new Locator(selectorExpression(selector), rt),
5334
4616
  async tapAt(point) {
5335
4617
  if (!isFinitePoint(point))
5336
- throw new E2eError("INVALID_ARGUMENT", `tapAt needs finite x and y, got ${JSON.stringify(point)}`);
4618
+ throw new E2eError2("INVALID_ARGUMENT", `tapAt needs finite x and y, got ${JSON.stringify(point)}`);
5337
4619
  return rt.step(`tapAt (${point.x}, ${point.y})`, async () => {
5338
4620
  const driver = await host.driver();
5339
4621
  rt.drive("tapAt", await driver.tap(point));
@@ -5368,10 +4650,10 @@ function createScreen(host) {
5368
4650
  async path(options) {
5369
4651
  const { paths } = options;
5370
4652
  if (paths.length < 1 || paths.length > 2) {
5371
- throw new E2eError("INVALID_ARGUMENT", `gesture.path takes 1 or 2 paths, got ${paths.length}`);
4653
+ throw new E2eError2("INVALID_ARGUMENT", `gesture.path takes 1 or 2 paths, got ${paths.length}`);
5372
4654
  }
5373
4655
  if (paths.some((points) => points.length < 1 || !points.every(isFinitePoint))) {
5374
- throw new E2eError("INVALID_ARGUMENT", "gesture.path needs at least one point per path, all with finite x and y");
4656
+ throw new E2eError2("INVALID_ARGUMENT", "gesture.path needs at least one point per path, all with finite x and y");
5375
4657
  }
5376
4658
  return rt.step(`gesture path (${paths.length} ${paths.length === 1 ? "finger" : "fingers"})`, async () => {
5377
4659
  const driver = await host.driver();
@@ -5393,6 +4675,9 @@ function traceDevice(device, span) {
5393
4675
  get devtools() {
5394
4676
  return device.devtools;
5395
4677
  },
4678
+ get perf() {
4679
+ return device.perf;
4680
+ },
5396
4681
  alert: (action) => span("device.alert", () => device.alert(action), { detail: (answered) => ({ answered }) }),
5397
4682
  openLink: (url, options) => span("device.openLink", () => device.openLink(url, options)),
5398
4683
  home: () => span("device.home", () => device.home()),
@@ -5479,17 +4764,20 @@ ${read.error}
5479
4764
  deps.exit(2);
5480
4765
  return;
5481
4766
  }
5482
- deps.setDefaultTimeout(read.data.timeoutMs + BUN_TIMEOUT_MARGIN_MS);
4767
+ deps.setDefaultTimeout(read.data.timeoutMs + BUN_TIMEOUT_MARGIN_MS2);
5483
4768
  const session = deps.createSession({ env: read.data });
5484
4769
  deps.afterAll(async () => {
5485
4770
  await session.close();
5486
4771
  if (isCi(deps.env) && deps.onlySeen()) {
5487
- throw new E2eError("INVALID_ARGUMENT", "test.only / test(..., { only: true }) is not allowed in CI");
4772
+ throw new E2eError2("INVALID_ARGUMENT", "test.only / test(..., { only: true }) is not allowed in CI");
5488
4773
  }
5489
4774
  });
5490
4775
  }
5491
- if (!hasDefaultDriverFactory())
4776
+ if (!hasDefaultDriverFactory()) {
5492
4777
  setDefaultDriverFactory(defaultDriverFactory);
4778
+ setDefaultNativeLog(realNativeLogDeps(bunExec, androidTools(process.env).adb));
4779
+ setDefaultHidRecovery({ exec: bunExec });
4780
+ }
5493
4781
  if (!hasDefaultFixtureFactory())
5494
4782
  setDefaultFixtureFactory(defaultFixtureFactory);
5495
4783
  installPreload({
@@ -5497,7 +4785,7 @@ installPreload({
5497
4785
  setDefaultTimeout,
5498
4786
  afterAll,
5499
4787
  createSession: getSession,
5500
- version: WARDEN_E2E_VERSION,
4788
+ version: WARDEN_E2E_VERSION2,
5501
4789
  onlySeen: onlyWasSeen,
5502
4790
  stderr: (text) => {
5503
4791
  process.stderr.write(text);
@@ -5505,8 +4793,8 @@ installPreload({
5505
4793
  exit: (code) => process.exit(code)
5506
4794
  });
5507
4795
  export {
5508
- versionMismatch,
5509
- isCi,
4796
+ BUN_TIMEOUT_MARGIN_MS2 as BUN_TIMEOUT_MARGIN_MS,
5510
4797
  installPreload,
5511
- BUN_TIMEOUT_MARGIN_MS
4798
+ isCi,
4799
+ versionMismatch
5512
4800
  };