@delacour/warden-e2e 0.13.0 → 0.14.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.
@@ -236,6 +236,10 @@ function settleDetailOf(outcome) {
236
236
  }
237
237
  var DEFAULT_SETTLE_QUIET_MS = 250;
238
238
  var DEFAULT_SETTLE_TIMEOUT_MS = 3000;
239
+ var HASH_FALLBACK_MIN_QUIET_MS = 500;
240
+ function hashFallbackQuietMs(quietMs) {
241
+ return Math.max(quietMs, HASH_FALLBACK_MIN_QUIET_MS);
242
+ }
239
243
  var DEFAULT_POLL_MS = 50;
240
244
  async function settleByHash(poll, deps) {
241
245
  const { quietMs, timeoutMs, now, sleep } = deps;
@@ -479,6 +483,34 @@ function assertDegrees(degrees) {
479
483
  }
480
484
  }
481
485
 
486
+ // ../../packages/e2e/src/screen/degraded-read.ts
487
+ function degradedRead(snapshot) {
488
+ if (snapshot.fellBackFrom !== undefined) {
489
+ return { kind: "fallback", from: snapshot.fellBackFrom, backend: snapshot.backend };
490
+ }
491
+ const [root, ...others] = snapshot.elements;
492
+ if (root !== undefined && others.length === 0 && root.children.length === 0) {
493
+ const { width, height } = root.frame;
494
+ if (width * height === 0)
495
+ return { kind: "emptyRoot", backend: snapshot.backend };
496
+ }
497
+ return;
498
+ }
499
+ function describeDegraded(read) {
500
+ if (read.kind === "emptyRoot")
501
+ return `the ${read.backend} read returned an empty root (backend: ${read.backend})`;
502
+ return `the read fell back from ${read.from} to ${read.backend} (fellBackFrom: ${read.from}, backend: ${read.backend}), which can drop React Native testIDs`;
503
+ }
504
+ var REACT_NOTES = {
505
+ unanswered: "the React tree did not answer",
506
+ "no-devtools": "there is no React tree to ask (no devtools: not a dev client with Metro)",
507
+ "not-testid": "only a plain getByTestId can be asked of the React tree"
508
+ };
509
+ function degradedFailure(locator, read, waitedMs, react, screen) {
510
+ const message = `${locator} could not be confirmed within ${waitedMs} ms: bridge degraded, ${describeDegraded(read)}, ` + `and ${REACT_NOTES[react]}. A missing node on such a read is not proof of absence.`;
511
+ return new E2eError2("READ_DEGRADED", message, screen);
512
+ }
513
+
482
514
  // ../../packages/e2e/src/screen/roles.ts
483
515
  var QUERY_ROLES = [
484
516
  "button",
@@ -1534,7 +1566,7 @@ function describeExpression(expr) {
1534
1566
  }
1535
1567
 
1536
1568
  // ../../packages/e2e/src/screen/react-locator.ts
1537
- async function locateByTestIdViaReact(facet, testId, view, options = {}) {
1569
+ async function lookUpTestIdViaReact(facet, testId, view, options = {}) {
1538
1570
  const treeOptions = {
1539
1571
  ...view.platform === "android" ? { pixelRatio: view.screen.scale } : {},
1540
1572
  ...options.screenName === undefined ? {} : { screen: options.screenName },
@@ -1544,10 +1576,10 @@ async function locateByTestIdViaReact(facet, testId, view, options = {}) {
1544
1576
  try {
1545
1577
  result = await facet.tree(treeOptions);
1546
1578
  } catch {
1547
- return [];
1579
+ return { answered: false };
1548
1580
  }
1549
1581
  if (!result.success)
1550
- return [];
1582
+ return { answered: false };
1551
1583
  const matches = [];
1552
1584
  for (const component of result.data.components) {
1553
1585
  const { testID, rect } = component;
@@ -1558,10 +1590,11 @@ async function locateByTestIdViaReact(facet, testId, view, options = {}) {
1558
1590
  tapPoint: { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 },
1559
1591
  source: "react",
1560
1592
  testId: testID,
1561
- name: component.name
1593
+ name: component.name,
1594
+ ...component.text === undefined ? {} : { text: component.text }
1562
1595
  });
1563
1596
  }
1564
- return matches;
1597
+ return { answered: true, matches };
1565
1598
  }
1566
1599
  function reactTapPoint(match, position) {
1567
1600
  if (position === undefined)
@@ -1569,6 +1602,41 @@ function reactTapPoint(match, position) {
1569
1602
  return { x: match.frame.x + position.x, y: match.frame.y + position.y };
1570
1603
  }
1571
1604
 
1605
+ // ../../packages/e2e/src/screen/react-merge.ts
1606
+ var EDGE_SLACK = 1.5;
1607
+ function sameRect(a, b) {
1608
+ if (a.width <= 0 || a.height <= 0 || b.width <= 0 || b.height <= 0)
1609
+ return false;
1610
+ return Math.abs(a.x - b.x) <= EDGE_SLACK && Math.abs(a.y - b.y) <= EDGE_SLACK && Math.abs(a.x + a.width - (b.x + b.width)) <= EDGE_SLACK && Math.abs(a.y + a.height - (b.y + b.height)) <= EDGE_SLACK;
1611
+ }
1612
+ function ownText(entry) {
1613
+ return normalizeText(entry.name ?? entry.value ?? "");
1614
+ }
1615
+ function nodeOfReactMatch(match, index) {
1616
+ const candidates = index.entries.filter((entry) => {
1617
+ const id = entry.element.identifier;
1618
+ return (id === undefined || id === match.testId) && sameRect(entry.element.frame, match.frame);
1619
+ });
1620
+ const text = match.text === undefined ? "" : normalizeText(match.text);
1621
+ const worded = text === "" ? [] : candidates.filter((entry) => ownText(entry) === text);
1622
+ const pool = worded.length > 0 ? worded : candidates;
1623
+ let best;
1624
+ for (const entry of pool)
1625
+ if (best === undefined || entry.depth > best.depth)
1626
+ best = entry;
1627
+ return best;
1628
+ }
1629
+ function mergeReactMatches(matches, index) {
1630
+ const entries = [];
1631
+ for (const match of matches) {
1632
+ const entry = nodeOfReactMatch(match, index);
1633
+ if (entry === undefined || entries.includes(entry))
1634
+ return;
1635
+ entries.push(entry);
1636
+ }
1637
+ return entries;
1638
+ }
1639
+
1572
1640
  // ../../packages/e2e/src/screen/locator.ts
1573
1641
  var WAIT_STATES = ["attached", "detached", "visible", "hidden"];
1574
1642
  var DEFAULT_MAX_SWIPES = 10;
@@ -1581,6 +1649,9 @@ function assertOptions(verb, options, allowed) {
1581
1649
  throw new E2eError2("INVALID_ARGUMENT", `${verb} does not take ${unknown.map((key) => `"${key}"`).join(", ")}; it takes ${allowed.join(", ")}`);
1582
1650
  }
1583
1651
  }
1652
+ function reactVisible(match, observed) {
1653
+ return match.frame.width > 0 && match.frame.height > 0 && isOnScreen(match.tapPoint, observed.snapshot.screen);
1654
+ }
1584
1655
  var REACT_ONLY = "exists only in the React tree, not in the accessibility tree, so only tap() and boundingBox() work on it; " + "expose it to accessibility (accessible, accessibilityLabel) to use anything else";
1585
1656
  function plainTestId(expr) {
1586
1657
  if (expr.kind !== "query" || expr.scope !== undefined || expr.query.kind !== "testId")
@@ -1665,8 +1736,78 @@ class Locator {
1665
1736
  const observed = await this.rt.observe(options);
1666
1737
  return { entries: resolve(this.expr, observed.index), observed };
1667
1738
  }
1668
- matches() {
1669
- return this.look();
1739
+ async resolveIn(observed) {
1740
+ const entries = resolve(this.expr, observed.index);
1741
+ if (entries.length > 0)
1742
+ return { kind: "nodes", entries };
1743
+ const read = degradedRead(observed.snapshot);
1744
+ if (read === undefined)
1745
+ return { kind: "nodes", entries };
1746
+ const testId = plainTestId(this.expr);
1747
+ const lookup = testId === undefined ? undefined : await this.rt.reactLookup(testId, observed);
1748
+ if (lookup === undefined || !lookup.answered)
1749
+ return { kind: "unknown", read };
1750
+ if (lookup.matches.length === 0)
1751
+ return { kind: "nodes", entries };
1752
+ const merged = mergeReactMatches(lookup.matches, observed.index);
1753
+ return merged === undefined ? { kind: "react", matches: lookup.matches } : { kind: "nodes", entries: merged };
1754
+ }
1755
+ reactNote() {
1756
+ if (plainTestId(this.expr) === undefined)
1757
+ return "not-testid";
1758
+ return this.rt.hasReactTree() ? "unanswered" : "no-devtools";
1759
+ }
1760
+ degradedFailure(read, waitedMs, snapshot) {
1761
+ const screen = snapshot === undefined ? undefined : this.rt.excerpt(snapshot);
1762
+ return degradedFailure(this.toString(), read, waitedMs, this.reactNote(), screen);
1763
+ }
1764
+ async conclusive(options = {}) {
1765
+ const first = await this.look(options);
1766
+ const settled = await this.settled(first.observed);
1767
+ if (settled !== undefined)
1768
+ return settled;
1769
+ const timeoutMs = this.rt.actionTimeout();
1770
+ let last;
1771
+ const result = await this.rt.poll(async () => {
1772
+ const observation = await this.rt.tryObserve({ reuse: false });
1773
+ if (!observation.ok) {
1774
+ if (!observation.error.retryable)
1775
+ throw new E2eError2("DRIVER", `snapshot failed: ${observation.error.message}`);
1776
+ return { done: false, state: undefined };
1777
+ }
1778
+ const answer = await this.settled(observation.observed);
1779
+ if (answer !== undefined)
1780
+ return { done: true, value: answer };
1781
+ const read = degradedRead(observation.observed.snapshot);
1782
+ if (read !== undefined)
1783
+ last = { read, snapshot: observation.observed.snapshot };
1784
+ return { done: false, state: undefined };
1785
+ }, timeoutMs);
1786
+ if (result.status === "done")
1787
+ return result.value;
1788
+ const fallback = degradedRead(first.observed.snapshot);
1789
+ const read = last?.read ?? fallback;
1790
+ if (read === undefined)
1791
+ throw new E2eError2("TIMEOUT", `${this} could not be read within ${timeoutMs} ms`);
1792
+ throw this.degradedFailure(read, timeoutMs, last?.snapshot ?? first.observed.snapshot);
1793
+ }
1794
+ async settled(observed) {
1795
+ const resolved = await this.resolveIn(observed);
1796
+ if (resolved.kind === "unknown")
1797
+ return;
1798
+ if (resolved.kind === "react")
1799
+ return { entries: [], react: resolved.matches, observed };
1800
+ return { entries: resolved.entries, react: undefined, observed };
1801
+ }
1802
+ async matches() {
1803
+ const { entries, observed } = await this.conclusive();
1804
+ return { entries, observed };
1805
+ }
1806
+ reactOnly(matches, observed) {
1807
+ const [only, ...others] = matches;
1808
+ if (only !== undefined && others.length === 0)
1809
+ return only;
1810
+ throw new E2eError2("LOCATOR_AMBIGUOUS", `${this} matched ${matches.length} components in the React tree`, this.rt.excerpt(observed.snapshot), matches.length);
1670
1811
  }
1671
1812
  async single() {
1672
1813
  const { entries, observed } = await this.matches();
@@ -1686,27 +1827,58 @@ class Locator {
1686
1827
  return this.rt.span(`read.${name}`, run, { detail: () => ({ locator: this.toString() }) });
1687
1828
  }
1688
1829
  count() {
1689
- return this.traced("count", async () => (await this.matches()).entries.length);
1830
+ return this.traced("count", async () => {
1831
+ const { entries, react } = await this.conclusive();
1832
+ return react?.length ?? entries.length;
1833
+ });
1690
1834
  }
1691
1835
  async all() {
1692
1836
  const count = await this.count();
1693
1837
  return Array.from({ length: count }, (_, index) => this.nth(index));
1694
1838
  }
1695
1839
  text() {
1696
- return this.traced("text", async () => textOf((await this.single()).entry));
1840
+ return this.traced("text", async () => {
1841
+ const { entries, react, observed } = await this.conclusive();
1842
+ if (react !== undefined)
1843
+ return this.reactText(this.reactOnly(react, observed), observed);
1844
+ if (entries.length === 0 && (await this.reactCandidates(observed)).length > 0) {
1845
+ throw new E2eError2("NOT_ACTIONABLE", `${this} ${REACT_ONLY}`, this.rt.excerpt(observed.snapshot));
1846
+ }
1847
+ return textOf(this.exactlyOne(entries, observed));
1848
+ });
1697
1849
  }
1698
1850
  allTexts() {
1699
- return this.traced("allTexts", async () => (await this.matches()).entries.map(textOf));
1851
+ return this.traced("allTexts", async () => {
1852
+ const { entries, react, observed } = await this.conclusive();
1853
+ if (react !== undefined)
1854
+ return react.map((match) => this.reactText(match, observed));
1855
+ return entries.map(textOf);
1856
+ });
1857
+ }
1858
+ reactText(match, observed) {
1859
+ if (match.text !== undefined)
1860
+ return normalizeText(match.text);
1861
+ throw new E2eError2("NOT_ACTIONABLE", `${this} ${REACT_ONLY}`, this.rt.excerpt(observed.snapshot));
1700
1862
  }
1701
1863
  value() {
1702
1864
  return this.traced("value", async () => fieldValue((await this.single()).entry));
1703
1865
  }
1704
1866
  isVisible() {
1705
- return this.traced("isVisible", async () => this.visibleIn((await this.matches()).observed));
1867
+ return this.traced("isVisible", async () => {
1868
+ const { entries, react, observed } = await this.conclusive();
1869
+ if (react !== undefined)
1870
+ return reactVisible(this.reactOnly(react, observed), observed);
1871
+ return entries.length > 0 && this.exactlyOne(entries, observed).visible;
1872
+ });
1706
1873
  }
1707
1874
  read() {
1708
1875
  return this.traced("read", async () => {
1709
- const { entries, observed } = await this.matches();
1876
+ const { entries, react, observed } = await this.conclusive();
1877
+ if (react !== undefined) {
1878
+ const match = this.reactOnly(react, observed);
1879
+ const visible = reactVisible(match, observed);
1880
+ return match.text === undefined ? { visible } : { visible, text: normalizeText(match.text) };
1881
+ }
1710
1882
  if (entries.length === 0)
1711
1883
  return { visible: false };
1712
1884
  const entry = this.exactlyOne(entries, observed);
@@ -1718,11 +1890,13 @@ class Locator {
1718
1890
  };
1719
1891
  });
1720
1892
  }
1721
- visibleIn(observed) {
1722
- const entries = resolve(this.expr, observed.index);
1723
- if (entries.length === 0)
1893
+ async shownIn(observed) {
1894
+ const resolved = await this.resolveIn(observed);
1895
+ if (resolved.kind === "unknown")
1724
1896
  return false;
1725
- return this.exactlyOne(entries, observed).visible;
1897
+ if (resolved.kind === "react")
1898
+ return reactVisible(this.reactOnly(resolved.matches, observed), observed);
1899
+ return resolved.entries.length > 0 && this.exactlyOne(resolved.entries, observed).visible;
1726
1900
  }
1727
1901
  async isHidden() {
1728
1902
  return !await this.isVisible();
@@ -1735,7 +1909,7 @@ class Locator {
1735
1909
  }
1736
1910
  boundingBox() {
1737
1911
  return this.traced("boundingBox", async () => {
1738
- const { entries, observed } = await this.look({ reuse: false });
1912
+ const { entries, observed } = await this.conclusive({ reuse: false });
1739
1913
  if (entries.length === 0) {
1740
1914
  const [only, ...others] = await this.reactCandidates(observed);
1741
1915
  if (only !== undefined && others.length === 0)
@@ -1762,14 +1936,19 @@ class Locator {
1762
1936
  return { done: false, state: undefined };
1763
1937
  }
1764
1938
  const { observed } = observation;
1765
- const entries = resolve(this.expr, observed.index);
1766
- const entry = entries.length === 0 ? undefined : this.exactlyOne(entries, observed);
1767
- return reached(state, entry) ? { done: true, value: true } : { done: false, state: observed.snapshot };
1939
+ const resolved = await this.resolveIn(observed);
1940
+ if (resolved.kind === "unknown")
1941
+ return { done: false, state: { snapshot: observed.snapshot, degraded: resolved.read } };
1942
+ const done = resolved.kind === "react" ? reachedByReact(state, reactVisible(this.reactOnly(resolved.matches, observed), observed)) : reached(state, resolved.entries.length === 0 ? undefined : this.exactlyOne(resolved.entries, observed));
1943
+ return done ? { done: true, value: true } : { done: false, state: { snapshot: observed.snapshot } };
1768
1944
  }, timeoutMs);
1769
1945
  if (result.status === "done")
1770
1946
  return;
1947
+ const last = result.last;
1948
+ if (last?.degraded !== undefined)
1949
+ throw this.degradedFailure(last.degraded, timeoutMs, last.snapshot);
1771
1950
  const message = `${this} did not become ${state} within ${timeoutMs} ms`;
1772
- throw new E2eError2("TIMEOUT", message, result.last === undefined ? undefined : this.rt.excerpt(result.last));
1951
+ throw new E2eError2("TIMEOUT", message, last === undefined ? undefined : this.rt.excerpt(last.snapshot));
1773
1952
  }
1774
1953
  act(verb, run, suffix = "") {
1775
1954
  return this.rt.step(`${verb} ${this}${suffix}`, run);
@@ -1795,6 +1974,8 @@ class Locator {
1795
1974
  }
1796
1975
  if (seen?.kind === "driver")
1797
1976
  throw new E2eError2("DRIVER", `snapshot kept failing: ${seen.message}`);
1977
+ if (seen?.kind === "degraded")
1978
+ throw this.degradedFailure(seen.read, timeoutMs, seen.snapshot);
1798
1979
  throw new E2eError2("LOCATOR_NOT_FOUND", `${this} matched no elements within ${timeoutMs} ms`, seen === undefined ? undefined : this.rt.excerpt(seen.snapshot));
1799
1980
  }
1800
1981
  async actionableLook(spec, allowReact, anyFrame) {
@@ -1909,7 +2090,7 @@ class Locator {
1909
2090
  watch.unchanged = 0;
1910
2091
  return { done: false, state: undefined };
1911
2092
  }
1912
- if (until.visibleIn(observation.observed))
2093
+ if (await until.shownIn(observation.observed))
1913
2094
  return { done: true, value: "arrived" };
1914
2095
  if (snapshotHash(observation.observed.snapshot) === watch.pressedOn)
1915
2096
  watch.unchanged += 1;
@@ -2034,10 +2215,17 @@ class Locator {
2034
2215
  return scrollUntilVisible(this.rt, this, options);
2035
2216
  }
2036
2217
  async reactStep(spec, observed, allowReact) {
2037
- const found = await this.reactCandidates(observed);
2218
+ const read = degradedRead(observed.snapshot);
2219
+ const testId = plainTestId(this.expr);
2220
+ const lookup = testId === undefined ? undefined : await this.rt.reactLookup(testId, observed);
2221
+ const found = lookup?.answered === true ? lookup.matches : [];
2038
2222
  const [match, ...others] = found;
2039
- if (match === undefined)
2223
+ if (match === undefined) {
2224
+ if (read !== undefined && lookup?.answered !== true) {
2225
+ return { done: false, state: { kind: "degraded", read, snapshot: observed.snapshot } };
2226
+ }
2040
2227
  return { done: false, state: { kind: "none", snapshot: observed.snapshot } };
2228
+ }
2041
2229
  if (!allowReact)
2042
2230
  return { done: false, state: { kind: "react", snapshot: observed.snapshot, matches: found.length } };
2043
2231
  if (others.length > 0) {
@@ -2176,6 +2364,18 @@ function reached(state, entry) {
2176
2364
  return entry === undefined || !entry.visible;
2177
2365
  }
2178
2366
  }
2367
+ function reachedByReact(state, visible) {
2368
+ switch (state) {
2369
+ case "attached":
2370
+ return true;
2371
+ case "detached":
2372
+ return false;
2373
+ case "visible":
2374
+ return visible;
2375
+ case "hidden":
2376
+ return !visible;
2377
+ }
2378
+ }
2179
2379
  function blockedReason(entry, spec, observed) {
2180
2380
  if (!entry.visible)
2181
2381
  return "is not visible";
@@ -2189,7 +2389,11 @@ function blockedReason(entry, spec, observed) {
2189
2389
  function isOnScreenEntry(entry, observed) {
2190
2390
  return entry?.visible === true && isOnScreen(tapPointOf(entry), observed.snapshot.screen);
2191
2391
  }
2192
- function scrollGaveUp(target, absent, effort, screen) {
2392
+ function scrollGaveUp(target, absent, effort, observed, waitedMs) {
2393
+ const read = absent ? degradedRead(observed.snapshot) : undefined;
2394
+ if (read !== undefined)
2395
+ return target.degradedFailure(read, waitedMs, observed.snapshot);
2396
+ const screen = target.runtime.excerpt(observed.snapshot);
2193
2397
  if (absent)
2194
2398
  return new E2eError2("LOCATOR_NOT_FOUND", `${target} was not found after ${effort}`, screen);
2195
2399
  return new E2eError2("NOT_ACTIONABLE", `${target} is still not on screen after ${effort}`, screen);
@@ -2218,7 +2422,8 @@ async function scrollUntilVisible(rt, target, options = {}) {
2218
2422
  if (isOnScreenEntry(entry, observed))
2219
2423
  return;
2220
2424
  if (swipes >= maxSwipes || budget.remaining() <= 0) {
2221
- throw scrollGaveUp(target, entry === undefined, `${swipes} swipes ${direction}`, rt.excerpt(observed.snapshot));
2425
+ const effort = `${swipes} swipes ${direction}`;
2426
+ throw scrollGaveUp(target, entry === undefined, effort, observed, budget.total);
2222
2427
  }
2223
2428
  await scrollOnce(rt, observed, direction, options.momentum);
2224
2429
  await observed.driver.settle(SCROLL_SETTLE);
@@ -4421,19 +4626,113 @@ function driverErr(code, message, extra) {
4421
4626
  }
4422
4627
 
4423
4628
  // ../../packages/e2e/src/driver/ios/simd-protocol.ts
4629
+ import { z as z7 } from "zod";
4630
+
4631
+ // ../../packages/e2e/src/driver/bridge-stall.ts
4424
4632
  import { z as z6 } from "zod";
4633
+ var bridgeStallSchema = z6.looseObject({
4634
+ stage: z6.string(),
4635
+ readMs: z6.number().optional(),
4636
+ deadlineMs: z6.number().optional(),
4637
+ guestAgeMs: z6.number().optional(),
4638
+ cold: z6.boolean(),
4639
+ cooldownRemainingMs: z6.number().optional(),
4640
+ probes: z6.number(),
4641
+ recycles: z6.number().optional(),
4642
+ recyclesExhausted: z6.boolean().optional()
4643
+ });
4644
+ var SPAN_FIELDS = [
4645
+ ["stallStage", "stage"],
4646
+ ["stallReadMs", "readMs"],
4647
+ ["stallDeadlineMs", "deadlineMs"],
4648
+ ["stallGuestAgeMs", "guestAgeMs"],
4649
+ ["stallCold", "cold"],
4650
+ ["stallCooldownMs", "cooldownRemainingMs"],
4651
+ ["stallProbes", "probes"],
4652
+ ["stallRecycles", "recycles"],
4653
+ ["stallRecyclesExhausted", "recyclesExhausted"]
4654
+ ];
4655
+ function toBridgeStall(wire) {
4656
+ const stall = { stage: wire.stage, cold: wire.cold, probes: wire.probes };
4657
+ if (wire.readMs !== undefined)
4658
+ stall.readMs = wire.readMs;
4659
+ if (wire.deadlineMs !== undefined)
4660
+ stall.deadlineMs = wire.deadlineMs;
4661
+ if (wire.guestAgeMs !== undefined)
4662
+ stall.guestAgeMs = wire.guestAgeMs;
4663
+ if (wire.cooldownRemainingMs !== undefined)
4664
+ stall.cooldownRemainingMs = wire.cooldownRemainingMs;
4665
+ if (wire.recycles !== undefined)
4666
+ stall.recycles = wire.recycles;
4667
+ if (wire.recyclesExhausted !== undefined)
4668
+ stall.recyclesExhausted = wire.recyclesExhausted;
4669
+ return stall;
4670
+ }
4671
+ function noteBridgeStall(stall) {
4672
+ for (const [key, field] of SPAN_FIELDS) {
4673
+ const value = stall[field];
4674
+ if (value !== undefined)
4675
+ noteSpanDetail(key, value);
4676
+ }
4677
+ }
4678
+ function bridgeStallFromSpan(detail) {
4679
+ const stage = detail?.stallStage;
4680
+ if (detail === undefined || typeof stage !== "string")
4681
+ return;
4682
+ const stall = {
4683
+ stage,
4684
+ cold: detail.stallCold === true,
4685
+ probes: typeof detail.stallProbes === "number" ? detail.stallProbes : 0
4686
+ };
4687
+ const numbers = [
4688
+ ["stallReadMs", "readMs"],
4689
+ ["stallDeadlineMs", "deadlineMs"],
4690
+ ["stallGuestAgeMs", "guestAgeMs"],
4691
+ ["stallCooldownMs", "cooldownRemainingMs"],
4692
+ ["stallRecycles", "recycles"]
4693
+ ];
4694
+ for (const [key, field] of numbers) {
4695
+ const value = detail[key];
4696
+ if (typeof value === "number")
4697
+ stall[field] = value;
4698
+ }
4699
+ if (typeof detail.stallRecyclesExhausted === "boolean")
4700
+ stall.recyclesExhausted = detail.stallRecyclesExhausted;
4701
+ return stall;
4702
+ }
4703
+ var seconds = (ms) => `${(ms / 1000).toFixed(1)} s`;
4704
+ function formatBridgeStall(stall) {
4705
+ const parts = [`stage ${stall.stage}`];
4706
+ if (stall.readMs !== undefined) {
4707
+ parts.push(stall.deadlineMs === undefined ? `read ${stall.readMs} ms` : `read ${stall.readMs} ms of a ${stall.deadlineMs} ms deadline`);
4708
+ } else if (stall.deadlineMs !== undefined)
4709
+ parts.push(`${stall.deadlineMs} ms deadline`);
4710
+ parts.push(stall.cold ? "cold (first read)" : "warm");
4711
+ if (stall.guestAgeMs !== undefined)
4712
+ parts.push(`guest ${seconds(stall.guestAgeMs)} old`);
4713
+ if (stall.cooldownRemainingMs !== undefined)
4714
+ parts.push(`cooldown ${seconds(stall.cooldownRemainingMs)} left`);
4715
+ parts.push(`${stall.probes} failed bridge reads in a row`);
4716
+ if (stall.recycles !== undefined) {
4717
+ const guests = stall.recycles === 1 ? "guest" : "guests";
4718
+ parts.push(`${stall.recycles} stalled ${guests} recycled${stall.recyclesExhausted === true ? ", none left" : ""}`);
4719
+ }
4720
+ return parts.join(" \xB7 ");
4721
+ }
4722
+
4723
+ // ../../packages/e2e/src/driver/ios/simd-protocol.ts
4425
4724
  var protocolVersion = 1;
4426
- var rectSchema2 = z6.object({
4427
- x: z6.number(),
4428
- y: z6.number(),
4429
- width: z6.number(),
4430
- height: z6.number()
4725
+ var rectSchema2 = z7.object({
4726
+ x: z7.number(),
4727
+ y: z7.number(),
4728
+ width: z7.number(),
4729
+ height: z7.number()
4431
4730
  });
4432
- var screenInfoSchema = z6.object({
4433
- width: z6.number(),
4434
- height: z6.number(),
4435
- scale: z6.number(),
4436
- rotation: z6.number().optional()
4731
+ var screenInfoSchema = z7.object({
4732
+ width: z7.number(),
4733
+ height: z7.number(),
4734
+ scale: z7.number(),
4735
+ rotation: z7.number().optional()
4437
4736
  });
4438
4737
  var ROLES = [
4439
4738
  "button",
@@ -4460,29 +4759,29 @@ var ROLES = [
4460
4759
  "application",
4461
4760
  "other"
4462
4761
  ];
4463
- var pointSchema = z6.object({ x: z6.number(), y: z6.number() });
4464
- var wireElementSchema = z6.lazy(() => z6.object({
4465
- id: z6.string().nullish(),
4466
- role: z6.enum(ROLES),
4467
- rawType: z6.string(),
4468
- label: z6.string().nullish(),
4469
- identifier: z6.string().nullish(),
4470
- value: z6.string().nullish(),
4471
- text: z6.string().nullish(),
4472
- placeholder: z6.string().nullish(),
4762
+ var pointSchema = z7.object({ x: z7.number(), y: z7.number() });
4763
+ var wireElementSchema = z7.lazy(() => z7.object({
4764
+ id: z7.string().nullish(),
4765
+ role: z7.enum(ROLES),
4766
+ rawType: z7.string(),
4767
+ label: z7.string().nullish(),
4768
+ identifier: z7.string().nullish(),
4769
+ value: z7.string().nullish(),
4770
+ text: z7.string().nullish(),
4771
+ placeholder: z7.string().nullish(),
4473
4772
  frame: rectSchema2,
4474
4773
  normFrame: rectSchema2.nullish(),
4475
- traits: z6.array(z6.string()).nullish(),
4476
- enabled: z6.boolean().nullish(),
4477
- focused: z6.boolean().nullish(),
4478
- hidden: z6.boolean().nullish(),
4479
- selected: z6.boolean().nullish(),
4480
- checked: z6.boolean().nullish(),
4481
- hittable: z6.boolean().nullish(),
4774
+ traits: z7.array(z7.string()).nullish(),
4775
+ enabled: z7.boolean().nullish(),
4776
+ focused: z7.boolean().nullish(),
4777
+ hidden: z7.boolean().nullish(),
4778
+ selected: z7.boolean().nullish(),
4779
+ checked: z7.boolean().nullish(),
4780
+ hittable: z7.boolean().nullish(),
4482
4781
  tapPoint: pointSchema.nullish(),
4483
- pid: z6.number().nullish(),
4484
- depth: z6.number().nullish(),
4485
- children: z6.array(wireElementSchema).nullish()
4782
+ pid: z7.number().nullish(),
4783
+ depth: z7.number().nullish(),
4784
+ children: z7.array(wireElementSchema).nullish()
4486
4785
  }));
4487
4786
  var ROLE_SET = new Set(ROLES);
4488
4787
  var isBag = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -4520,33 +4819,33 @@ function wireElementProblem(value) {
4520
4819
  const issue = parsed.error.issues[0];
4521
4820
  return issue ? `${issue.path.join(".") || "(element)"}: ${issue.message}` : "invalid element";
4522
4821
  }
4523
- var wireErrorSchema = z6.object({
4524
- code: z6.string(),
4525
- message: z6.string(),
4526
- retryable: z6.boolean().optional(),
4527
- detail: z6.unknown().optional()
4822
+ var wireErrorSchema = z7.object({
4823
+ code: z7.string(),
4824
+ message: z7.string(),
4825
+ retryable: z7.boolean().optional(),
4826
+ detail: z7.unknown().optional()
4528
4827
  });
4529
- var readyEventSchema = z6.object({
4530
- event: z6.literal("ready"),
4531
- protocol: z6.number(),
4532
- version: z6.string(),
4533
- udid: z6.string(),
4534
- hid: z6.object({ transport: z6.string() }),
4535
- ax: z6.object({ default: z6.string(), bridge: z6.string() }),
4828
+ var readyEventSchema = z7.object({
4829
+ event: z7.literal("ready"),
4830
+ protocol: z7.number(),
4831
+ version: z7.string(),
4832
+ udid: z7.string(),
4833
+ hid: z7.object({ transport: z7.string() }),
4834
+ ax: z7.object({ default: z7.string(), bridge: z7.string() }),
4536
4835
  screen: screenInfoSchema
4537
4836
  });
4538
- var logEventSchema = z6.object({
4539
- event: z6.literal("log"),
4540
- level: z6.enum(["debug", "info", "warn", "error"]),
4541
- message: z6.string(),
4542
- op: z6.string().optional(),
4543
- id: z6.string().optional()
4837
+ var logEventSchema = z7.object({
4838
+ event: z7.literal("log"),
4839
+ level: z7.enum(["debug", "info", "warn", "error"]),
4840
+ message: z7.string(),
4841
+ op: z7.string().optional(),
4842
+ id: z7.string().optional()
4544
4843
  });
4545
- var fatalEventSchema = z6.object({ event: z6.literal("fatal"), error: wireErrorSchema });
4546
- var eventSchema = z6.discriminatedUnion("event", [readyEventSchema, logEventSchema, fatalEventSchema]);
4547
- var responseSchema = z6.discriminatedUnion("ok", [
4548
- z6.object({ id: z6.string(), ok: z6.literal(true), data: z6.unknown().optional() }),
4549
- z6.object({ id: z6.string(), ok: z6.literal(false), error: wireErrorSchema })
4844
+ var fatalEventSchema = z7.object({ event: z7.literal("fatal"), error: wireErrorSchema });
4845
+ var eventSchema = z7.discriminatedUnion("event", [readyEventSchema, logEventSchema, fatalEventSchema]);
4846
+ var responseSchema = z7.discriminatedUnion("ok", [
4847
+ z7.object({ id: z7.string(), ok: z7.literal(true), data: z7.unknown().optional() }),
4848
+ z7.object({ id: z7.string(), ok: z7.literal(false), error: wireErrorSchema })
4550
4849
  ]);
4551
4850
  function parseLine(line) {
4552
4851
  let json;
@@ -4562,74 +4861,80 @@ function parseLine(line) {
4562
4861
  const response = responseSchema.safeParse(json);
4563
4862
  return response.success ? { success: true, message: { kind: "response", response: response.data } } : { success: false, reason: `bad response: ${response.error.message}` };
4564
4863
  }
4565
- var healthDataSchema = z6.looseObject({
4566
- version: z6.string(),
4567
- udid: z6.string(),
4568
- hid: z6.object({ transport: z6.string() }),
4569
- ax: z6.object({
4570
- default: z6.string(),
4571
- bridge: z6.enum(["available", "degraded", "missing", "failed"]),
4572
- error: z6.string().optional(),
4573
- lastError: z6.object({ message: z6.string(), agoMs: z6.number() }).optional()
4864
+ var healthDataSchema = z7.looseObject({
4865
+ version: z7.string(),
4866
+ udid: z7.string(),
4867
+ hid: z7.object({ transport: z7.string() }),
4868
+ ax: z7.object({
4869
+ default: z7.string(),
4870
+ bridge: z7.enum(["available", "degraded", "missing", "failed"]),
4871
+ error: z7.string().optional(),
4872
+ lastError: z7.object({ message: z7.string(), agoMs: z7.number() }).optional()
4574
4873
  }),
4575
4874
  screen: screenInfoSchema,
4576
- socket: z6.object({ path: z6.string(), clients: z6.number() }).optional(),
4577
- idb: z6.string().optional(),
4578
- xcode: z6.string().optional(),
4579
- builtWith: z6.object({ xcode: z6.string().optional(), idb: z6.string().optional() }).optional(),
4580
- coreSimulatorVersion: z6.string().nullish(),
4581
- frontmost: z6.object({ unresolvedAgoMs: z6.number(), method: z6.string() }).optional(),
4582
- state: z6.string().optional(),
4583
- uptimeMs: z6.number().optional()
4875
+ socket: z7.object({ path: z7.string(), clients: z7.number() }).optional(),
4876
+ idb: z7.string().optional(),
4877
+ xcode: z7.string().optional(),
4878
+ builtWith: z7.object({ xcode: z7.string().optional(), idb: z7.string().optional() }).optional(),
4879
+ coreSimulatorVersion: z7.string().nullish(),
4880
+ frontmost: z7.object({ unresolvedAgoMs: z7.number(), method: z7.string() }).optional(),
4881
+ bridgeRecycles: z7.object({ count: z7.number(), exhausted: z7.boolean() }).optional(),
4882
+ state: z7.string().optional(),
4883
+ uptimeMs: z7.number().optional()
4584
4884
  });
4585
- var snapshotDataSchema = z6.looseObject({
4586
- backend: z6.enum(["axbridge", "ax", "uiautomator"]),
4885
+ var snapshotDataSchema = z7.looseObject({
4886
+ backend: z7.enum(["axbridge", "ax", "uiautomator"]),
4587
4887
  screen: screenInfoSchema,
4588
- modal: z6.object({ pid: z6.number().optional(), description: z6.string().optional() }).nullish(),
4589
- truncated: z6.boolean(),
4590
- elements: z6.array(z6.custom(isWireElement, { error: (issue) => wireElementProblem(issue.input) })),
4591
- ms: z6.number(),
4592
- fallback: z6.object({ from: z6.string(), to: z6.string(), reason: z6.string() }).optional(),
4593
- target: z6.object({ pid: z6.number(), source: z6.string() }).optional(),
4594
- readMs: z6.number().optional(),
4595
- nodes: z6.number().optional()
4888
+ modal: z7.object({ pid: z7.number().optional(), description: z7.string().optional() }).nullish(),
4889
+ truncated: z7.boolean(),
4890
+ elements: z7.array(z7.custom(isWireElement, { error: (issue) => wireElementProblem(issue.input) })),
4891
+ ms: z7.number(),
4892
+ fallback: z7.object({
4893
+ from: z7.string(),
4894
+ to: z7.string(),
4895
+ reason: z7.string(),
4896
+ stall: bridgeStallSchema.optional()
4897
+ }).optional(),
4898
+ target: z7.object({ pid: z7.number(), source: z7.string() }).optional(),
4899
+ readMs: z7.number().optional(),
4900
+ nodes: z7.number().optional()
4596
4901
  });
4597
4902
  var elementAtPointDataSchema = wireElementSchema.nullable();
4598
- var screenshotDataSchema = z6.looseObject({
4599
- path: z6.string().optional(),
4600
- png: z6.string().optional(),
4601
- width: z6.number().optional(),
4602
- height: z6.number().optional()
4903
+ var screenshotDataSchema = z7.looseObject({
4904
+ path: z7.string().optional(),
4905
+ png: z7.string().optional(),
4906
+ width: z7.number().optional(),
4907
+ height: z7.number().optional()
4603
4908
  });
4604
- var settleDataSchema = z6.looseObject({
4605
- quiet: z6.boolean(),
4606
- waitedMs: z6.number(),
4607
- via: z6.enum(["stream", "snapshot"]).optional().catch(undefined),
4608
- reason: z6.string().optional(),
4609
- frontmostPid: z6.number().optional()
4909
+ var settleDataSchema = z7.looseObject({
4910
+ quiet: z7.boolean(),
4911
+ waitedMs: z7.number(),
4912
+ via: z7.enum(["stream", "snapshot"]).optional().catch(undefined),
4913
+ reason: z7.string().optional(),
4914
+ frontmostPid: z7.number().optional()
4610
4915
  });
4611
- var rebindDataSchema = z6.looseObject({
4612
- bound: z6.boolean(),
4613
- waitedMs: z6.number(),
4614
- pid: z6.number().optional(),
4615
- restarts: z6.number().optional(),
4616
- recycled: z6.boolean().optional(),
4617
- reason: z6.string().optional()
4916
+ var rebindDataSchema = z7.looseObject({
4917
+ bound: z7.boolean(),
4918
+ waitedMs: z7.number(),
4919
+ pid: z7.number().optional(),
4920
+ restarts: z7.number().optional(),
4921
+ recycled: z7.boolean().optional(),
4922
+ reason: z7.string().optional()
4618
4923
  });
4619
- var alertButtonsDataSchema = z6.object({
4620
- title: z6.string().optional(),
4621
- message: z6.string().optional(),
4622
- buttons: z6.array(wireElementSchema)
4924
+ var alertButtonsDataSchema = z7.object({
4925
+ title: z7.string().optional(),
4926
+ message: z7.string().optional(),
4927
+ buttons: z7.array(wireElementSchema)
4623
4928
  }).nullable();
4624
- var frontmostDataSchema = z6.looseObject({
4625
- frontmost: z6.boolean(),
4626
- pid: z6.number().optional(),
4627
- frontmostPid: z6.number().optional()
4929
+ var frontmostDataSchema = z7.looseObject({
4930
+ frontmost: z7.boolean(),
4931
+ pid: z7.number().optional(),
4932
+ frontmostPid: z7.number().optional()
4628
4933
  });
4629
- var launchDataSchema = z6.looseObject({ pid: z6.number() });
4630
- var terminateDataSchema = z6.looseObject({ terminated: z6.boolean() });
4631
- var orientationDataSchema = z6.object({
4632
- orientation: z6.enum([
4934
+ var launchDataSchema = z7.looseObject({ pid: z7.number() });
4935
+ var terminateDataSchema = z7.looseObject({ terminated: z7.boolean() });
4936
+ var orientationDataSchema = z7.object({
4937
+ orientation: z7.enum([
4633
4938
  "portrait",
4634
4939
  "portraitUpsideDown",
4635
4940
  "landscapeLeft",
@@ -4638,7 +4943,7 @@ var orientationDataSchema = z6.object({
4638
4943
  "faceDown",
4639
4944
  "unknown"
4640
4945
  ]),
4641
- source: z6.enum(["device", "written", "assumed"]).optional()
4946
+ source: z7.enum(["device", "written", "assumed"]).optional()
4642
4947
  });
4643
4948
  var opSchemas = {
4644
4949
  health: healthDataSchema,
@@ -5079,7 +5384,7 @@ async function createSimdClient(factory, options = {}) {
5079
5384
  const transport = session.data.transport;
5080
5385
  if (!transport)
5081
5386
  return { success: false, error: driverError("E_INTERNAL", "warden-simd transport missing") };
5082
- const timeoutMs = opts.timeoutMs ?? defaultTimeoutMs;
5387
+ const timeoutMs = Math.max(opts.timeoutMs ?? defaultTimeoutMs, opts.minTimeoutMs ?? 0);
5083
5388
  const id = String(++seq);
5084
5389
  const result = await new Promise((resolve) => {
5085
5390
  const timer = setTimeout(() => {
@@ -5342,7 +5647,7 @@ function traceDriver(driver, options) {
5342
5647
  }
5343
5648
 
5344
5649
  // ../../packages/core/src/a11y/a11y.config.ts
5345
- import { z as z7 } from "zod";
5650
+ import { z as z8 } from "zod";
5346
5651
  var A11Y_RULE_IDS = [
5347
5652
  "missing-name",
5348
5653
  "image-no-name",
@@ -5355,11 +5660,11 @@ var A11Y_RULE_IDS = [
5355
5660
  "text-clipped",
5356
5661
  "native"
5357
5662
  ];
5358
- var ruleId = z7.enum(A11Y_RULE_IDS);
5359
- var a11yAuditConfigSchema = z7.object({
5360
- ignore: z7.array(z7.object({ rule: ruleId, identifier: z7.string().min(1).optional() }).strict()).optional(),
5361
- minSeverity: z7.enum(["error", "warning"]).optional(),
5362
- rules: z7.array(ruleId).min(1).optional()
5663
+ var ruleId = z8.enum(A11Y_RULE_IDS);
5664
+ var a11yAuditConfigSchema = z8.object({
5665
+ ignore: z8.array(z8.object({ rule: ruleId, identifier: z8.string().min(1).optional() }).strict()).optional(),
5666
+ minSeverity: z8.enum(["error", "warning"]).optional(),
5667
+ rules: z8.array(ruleId).min(1).optional()
5363
5668
  }).strict();
5364
5669
  function parseA11yAuditEnv(raw) {
5365
5670
  let json;
@@ -5375,7 +5680,7 @@ function parseA11yAuditEnv(raw) {
5375
5680
  }
5376
5681
 
5377
5682
  // ../../packages/core/src/conditions/conditions.config.ts
5378
- import { z as z8 } from "zod";
5683
+ import { z as z9 } from "zod";
5379
5684
 
5380
5685
  // ../../packages/core/src/duration.ts
5381
5686
  var UNIT_MS = { ms: 1, s: 1000, m: 60000, h: 3600000, d: 86400000 };
@@ -5536,51 +5841,51 @@ function appliedA11y(op, applied) {
5536
5841
 
5537
5842
  // ../../packages/core/src/conditions/conditions.config.ts
5538
5843
  var NETWORK_WORDS = ["online", "offline", ...NETWORK_PRESET_NAMES];
5539
- var nonNegative2 = z8.number().nonnegative().finite();
5540
- var customNetworkSchema = z8.object({
5844
+ var nonNegative2 = z9.number().nonnegative().finite();
5845
+ var customNetworkSchema = z9.object({
5541
5846
  latencyMs: nonNegative2.optional(),
5542
5847
  jitterMs: nonNegative2.optional(),
5543
5848
  downKbps: nonNegative2.optional(),
5544
5849
  upKbps: nonNegative2.optional(),
5545
- lossPct: z8.number().min(0).max(100).optional()
5850
+ lossPct: z9.number().min(0).max(100).optional()
5546
5851
  }).strict();
5547
- var isoDate = z8.string().refine((v) => !Number.isNaN(Date.parse(v)), { message: "invalid date (use ISO 8601)" });
5548
- var offsetDuration = z8.string().refine((v) => parseDuration(v).success, { message: "invalid duration (use e.g. 2d)" });
5549
- var textSize = z8.union([z8.enum(TEXT_SIZE_NAMES), z8.number().positive().finite()]);
5550
- var a11yConfigSchema = z8.object({
5852
+ var isoDate = z9.string().refine((v) => !Number.isNaN(Date.parse(v)), { message: "invalid date (use ISO 8601)" });
5853
+ var offsetDuration = z9.string().refine((v) => parseDuration(v).success, { message: "invalid duration (use e.g. 2d)" });
5854
+ var textSize = z9.union([z9.enum(TEXT_SIZE_NAMES), z9.number().positive().finite()]);
5855
+ var a11yConfigSchema = z9.object({
5551
5856
  textSize: textSize.optional(),
5552
- boldText: z8.boolean().optional(),
5553
- contrast: z8.enum(["increased", "normal"]).optional(),
5554
- reduceMotion: z8.boolean().optional()
5857
+ boldText: z9.boolean().optional(),
5858
+ contrast: z9.enum(["increased", "normal"]).optional(),
5859
+ reduceMotion: z9.boolean().optional()
5555
5860
  }).strict();
5556
- var conditionsConfigSchema = z8.object({
5557
- network: z8.union([z8.enum(NETWORK_WORDS), customNetworkSchema]).optional(),
5558
- location: z8.union([
5559
- z8.enum(["city-run", "city-bike", "freeway"]),
5560
- z8.object({
5561
- lat: z8.number().min(-90).max(90),
5562
- lon: z8.number().min(-180).max(180),
5563
- altM: z8.number().finite().optional()
5861
+ var conditionsConfigSchema = z9.object({
5862
+ network: z9.union([z9.enum(NETWORK_WORDS), customNetworkSchema]).optional(),
5863
+ location: z9.union([
5864
+ z9.enum(["city-run", "city-bike", "freeway"]),
5865
+ z9.object({
5866
+ lat: z9.number().min(-90).max(90),
5867
+ lon: z9.number().min(-180).max(180),
5868
+ altM: z9.number().finite().optional()
5564
5869
  }).strict()
5565
5870
  ]).optional(),
5566
- clock: z8.union([
5871
+ clock: z9.union([
5567
5872
  isoDate,
5568
- z8.object({ at: isoDate, frozen: z8.boolean().optional() }).strict(),
5569
- z8.object({ offset: offsetDuration }).strict()
5873
+ z9.object({ at: isoDate, frozen: z9.boolean().optional() }).strict(),
5874
+ z9.object({ offset: offsetDuration }).strict()
5570
5875
  ]).optional(),
5571
- permissions: z8.partialRecord(z8.enum(PERMISSION_NAMES), z8.enum(["granted", "denied", "unset"])).optional(),
5572
- locale: z8.string().min(1).optional(),
5573
- timezone: z8.string().min(1).optional(),
5876
+ permissions: z9.partialRecord(z9.enum(PERMISSION_NAMES), z9.enum(["granted", "denied", "unset"])).optional(),
5877
+ locale: z9.string().min(1).optional(),
5878
+ timezone: z9.string().min(1).optional(),
5574
5879
  a11y: a11yConfigSchema.optional()
5575
5880
  }).strict();
5576
- var cameraFeedInput = z8.union([
5577
- z8.string().min(1),
5578
- z8.object({ path: z8.string().min(1), loop: z8.boolean().optional() }).strict()
5881
+ var cameraFeedInput = z9.union([
5882
+ z9.string().min(1),
5883
+ z9.object({ path: z9.string().min(1), loop: z9.boolean().optional() }).strict()
5579
5884
  ]);
5580
- var cameraConfigSchema = z8.object({ back: cameraFeedInput.optional(), front: cameraFeedInput.optional() }).strict().refine((v) => v.back !== undefined || v.front !== undefined, { message: "camera needs a back or a front asset" });
5581
- var cameraFeedsSchema = z8.object({
5582
- back: z8.object({ path: z8.string().min(1), loop: z8.boolean() }).strict().optional(),
5583
- front: z8.object({ path: z8.string().min(1), loop: z8.boolean() }).strict().optional()
5885
+ var cameraConfigSchema = z9.object({ back: cameraFeedInput.optional(), front: cameraFeedInput.optional() }).strict().refine((v) => v.back !== undefined || v.front !== undefined, { message: "camera needs a back or a front asset" });
5886
+ var cameraFeedsSchema = z9.object({
5887
+ back: z9.object({ path: z9.string().min(1), loop: z9.boolean() }).strict().optional(),
5888
+ front: z9.object({ path: z9.string().min(1), loop: z9.boolean() }).strict().optional()
5584
5889
  }).strict();
5585
5890
  function parseCameraFeedsEnv(raw) {
5586
5891
  let json;
@@ -5662,7 +5967,7 @@ function parseConditionsEnv(raw) {
5662
5967
  }
5663
5968
 
5664
5969
  // ../../packages/e2e/src/env.ts
5665
- import { z as z9 } from "zod";
5970
+ import { z as z10 } from "zod";
5666
5971
  var ENV_NAMES = {
5667
5972
  udid: "WARDEN_UDID",
5668
5973
  deviceName: "WARDEN_DEVICE_NAME",
@@ -5700,68 +6005,68 @@ var ENV_NAMES = {
5700
6005
  var DEFAULT_TIMEOUT_MS5 = 120000;
5701
6006
  var DEFAULT_ACTION_TIMEOUT_MS = 30000;
5702
6007
  var DEFAULT_ASSERTION_TIMEOUT_MS = 5000;
5703
- var toggle = z9.enum(["1", "true", "0", "false"]).transform((v) => v === "1" || v === "true");
5704
- var millis = z9.string().regex(/^\d+$/, "must be a whole number of milliseconds").transform(Number).pipe(z9.number().int().positive());
5705
- var launchArgs = z9.string().transform((raw, ctx) => {
6008
+ var toggle = z10.enum(["1", "true", "0", "false"]).transform((v) => v === "1" || v === "true");
6009
+ var millis = z10.string().regex(/^\d+$/, "must be a whole number of milliseconds").transform(Number).pipe(z10.number().int().positive());
6010
+ var launchArgs = z10.string().transform((raw, ctx) => {
5706
6011
  try {
5707
- const parsed = z9.array(z9.string()).safeParse(JSON.parse(raw));
6012
+ const parsed = z10.array(z10.string()).safeParse(JSON.parse(raw));
5708
6013
  if (parsed.success)
5709
6014
  return parsed.data;
5710
6015
  } catch {}
5711
6016
  ctx.addIssue({ code: "custom", message: "must be a JSON array of strings" });
5712
- return z9.NEVER;
6017
+ return z10.NEVER;
5713
6018
  });
5714
- var conditions = z9.string().transform((raw, ctx) => {
6019
+ var conditions = z10.string().transform((raw, ctx) => {
5715
6020
  const parsed = parseConditionsEnv(raw);
5716
6021
  if (parsed.success)
5717
6022
  return parsed.data;
5718
6023
  ctx.addIssue({ code: "custom", message: parsed.error });
5719
- return z9.NEVER;
6024
+ return z10.NEVER;
5720
6025
  });
5721
- var a11yAudit = z9.string().transform((raw, ctx) => {
6026
+ var a11yAudit = z10.string().transform((raw, ctx) => {
5722
6027
  const parsed = parseA11yAuditEnv(raw);
5723
6028
  if (parsed.success)
5724
6029
  return parsed.data;
5725
6030
  ctx.addIssue({ code: "custom", message: parsed.error });
5726
- return z9.NEVER;
6031
+ return z10.NEVER;
5727
6032
  });
5728
- var cameraFeeds = z9.string().transform((raw, ctx) => {
6033
+ var cameraFeeds = z10.string().transform((raw, ctx) => {
5729
6034
  const parsed = parseCameraFeedsEnv(raw);
5730
6035
  if (parsed.success)
5731
6036
  return parsed.data;
5732
6037
  ctx.addIssue({ code: "custom", message: parsed.error });
5733
- return z9.NEVER;
6038
+ return z10.NEVER;
5734
6039
  });
5735
6040
  var REQUIRED = "required when WARDEN_UDID is set";
5736
- var schema = z9.object({
5737
- udid: z9.string().min(1),
5738
- deviceName: z9.string().optional(),
5739
- platform: z9.enum(["ios", "android"]).default("ios"),
5740
- bundleId: z9.string({ error: REQUIRED }).min(1, REQUIRED),
5741
- appPath: z9.string().optional(),
5742
- metroUrl: z9.string().optional(),
5743
- devClientScheme: z9.string().min(1).optional(),
6041
+ var schema = z10.object({
6042
+ udid: z10.string().min(1),
6043
+ deviceName: z10.string().optional(),
6044
+ platform: z10.enum(["ios", "android"]).default("ios"),
6045
+ bundleId: z10.string({ error: REQUIRED }).min(1, REQUIRED),
6046
+ appPath: z10.string().optional(),
6047
+ metroUrl: z10.string().optional(),
6048
+ devClientScheme: z10.string().min(1).optional(),
5744
6049
  launchArgs: launchArgs.default([]),
5745
- out: z9.string().optional(),
6050
+ out: z10.string().optional(),
5746
6051
  timeout: millis.default(DEFAULT_TIMEOUT_MS5),
5747
6052
  actionTimeout: millis.default(DEFAULT_ACTION_TIMEOUT_MS),
5748
6053
  assertionTimeout: millis.default(DEFAULT_ASSERTION_TIMEOUT_MS),
5749
- specsDir: z9.string().optional(),
5750
- helperDir: z9.string().optional(),
5751
- simd: z9.string().optional(),
5752
- simdSocket: z9.string().optional(),
5753
- iosRunnerSocket: z9.string().optional(),
6054
+ specsDir: z10.string().optional(),
6055
+ helperDir: z10.string().optional(),
6056
+ simd: z10.string().optional(),
6057
+ simdSocket: z10.string().optional(),
6058
+ iosRunnerSocket: z10.string().optional(),
5754
6059
  logTimestamps: toggle.default(false),
5755
- deviceKind: z9.enum(["physical"]).optional(),
6060
+ deviceKind: z10.enum(["physical"]).optional(),
5756
6061
  iosRunnerRestart: launchArgs.optional(),
5757
6062
  afterReboot: launchArgs.optional(),
5758
- version: z9.string().optional(),
5759
- bin: z9.string().optional(),
5760
- perf: z9.string().optional(),
5761
- perfBudgets: z9.string().optional(),
6063
+ version: z10.string().optional(),
6064
+ bin: z10.string().optional(),
6065
+ perf: z10.string().optional(),
6066
+ perfBudgets: z10.string().optional(),
5762
6067
  conditions: conditions.optional(),
5763
- leaseId: z9.string().min(1).optional(),
5764
- netproxyCommand: launchArgs.pipe(z9.array(z9.string()).min(1)).optional(),
6068
+ leaseId: z10.string().min(1).optional(),
6069
+ netproxyCommand: launchArgs.pipe(z10.array(z10.string()).min(1)).optional(),
5765
6070
  biometrics: toggle.optional(),
5766
6071
  a11yAudit: a11yAudit.optional(),
5767
6072
  cameraFeeds: cameraFeeds.optional()
@@ -5771,7 +6076,7 @@ function defined2(values) {
5771
6076
  }
5772
6077
  function parseArgv(raw) {
5773
6078
  try {
5774
- const parsed = z9.array(z9.string()).min(1).safeParse(JSON.parse(raw));
6079
+ const parsed = z10.array(z10.string()).min(1).safeParse(JSON.parse(raw));
5775
6080
  if (parsed.success)
5776
6081
  return parsed.data;
5777
6082
  } catch {}
@@ -5841,25 +6146,43 @@ function readEnv(env, opts = {}) {
5841
6146
  // ../../packages/e2e/src/runner/results.ts
5842
6147
  import { appendFileSync, mkdirSync as mkdirSync2 } from "fs";
5843
6148
  import { dirname as dirname3 } from "path";
5844
- import { z as z10 } from "zod";
5845
- var testStatusSchema = z10.enum(["passed", "failed", "skipped", "timed-out"]);
5846
- var testResultSchema = z10.object({
5847
- file: z10.string(),
5848
- title: z10.array(z10.string()),
6149
+ import { z as z11 } from "zod";
6150
+ var bridgeReadSchema = z11.object({
6151
+ reason: z11.string().optional(),
6152
+ code: z11.string().optional(),
6153
+ stage: z11.string().optional(),
6154
+ readMs: z11.number().optional(),
6155
+ deadlineMs: z11.number().optional(),
6156
+ guestAgeMs: z11.number().optional(),
6157
+ cold: z11.boolean().optional(),
6158
+ cooldownRemainingMs: z11.number().optional(),
6159
+ probes: z11.number().optional(),
6160
+ recycles: z11.number().optional(),
6161
+ recyclesExhausted: z11.boolean().optional()
6162
+ });
6163
+ var testStatusSchema = z11.enum(["passed", "failed", "skipped", "timed-out"]);
6164
+ var testResultSchema = z11.object({
6165
+ file: z11.string(),
6166
+ title: z11.array(z11.string()),
5849
6167
  status: testStatusSchema,
5850
- durationMs: z10.number(),
5851
- startedAt: z10.number(),
5852
- only: z10.boolean().optional(),
5853
- error: z10.object({
5854
- message: z10.string(),
5855
- code: z10.string().optional(),
5856
- stack: z10.string().optional()
6168
+ durationMs: z11.number(),
6169
+ startedAt: z11.number(),
6170
+ only: z11.boolean().optional(),
6171
+ error: z11.object({
6172
+ message: z11.string(),
6173
+ code: z11.string().optional(),
6174
+ stack: z11.string().optional()
6175
+ }).optional(),
6176
+ artifacts: z11.object({ dir: z11.string(), files: z11.array(z11.string()) }).optional(),
6177
+ bridge: z11.object({
6178
+ fallbacks: z11.number(),
6179
+ first: bridgeReadSchema,
6180
+ last: bridgeReadSchema
5857
6181
  }).optional(),
5858
- artifacts: z10.object({ dir: z10.string(), files: z10.array(z10.string()) }).optional(),
5859
- device: z10.object({
5860
- udid: z10.string(),
5861
- platform: z10.enum(["ios", "android"]),
5862
- name: z10.string().optional()
6182
+ device: z11.object({
6183
+ udid: z11.string(),
6184
+ platform: z11.enum(["ios", "android"]),
6185
+ name: z11.string().optional()
5863
6186
  })
5864
6187
  });
5865
6188
  function openResults(path) {
@@ -9327,6 +9650,6 @@ function onlyWasSeen() {
9327
9650
 
9328
9651
  // ../../packages/e2e/src/version.ts
9329
9652
  var WORKSPACE_VERSION = "0.0.0-workspace";
9330
- var WARDEN_E2E_VERSION2 = "0.13.0";
9653
+ var WARDEN_E2E_VERSION2 = "0.14.0";
9331
9654
 
9332
- export { E2eError2, registerA11yTarget, a11yTargetOf, canActOnElements, noteSpanDetail, addSpanDetail, canObserve, canDetectAlerts, settleDetailOf, DEFAULT_SETTLE_QUIET_MS, DEFAULT_SETTLE_TIMEOUT_MS, settleByHash, snapshotHash, ok, err, swipeVector, screenRect, fieldValue, PINCH_MS, ROTATE_MS, PATH_MS, centreOf, pinchRadius, rotateRadius, requireGesture, assertScale, assertDegrees, isInteractiveRole, resolveRole, normalizeText, matchesText, indexSnapshot, testIdQuery, textQuery, roleQuery, selectorExpression, renderSnapshot, renderHeader, resolve, locateByTestIdViaReact, isLocator, Locator, pickOne, textOf, scrollUntilVisible, formatNativeTail, realNativeLogDeps, captureNativeLog, bunExec, execError, driverError, driverErr, logEventSchema, fatalEventSchema, responseSchema, mapWireError, createLineSplitter, encodeRequest, bunSpawn, stdioTransport, socketTransport, createSimdClient, spawnSimd, connectSimd, isRecoverableLoss, parseDuration, CAMERA_FACINGS, CONDITION_AREAS, resolveNetwork, resolveTextSize, androidFontScale, ENV_NAMES, DEFAULT_ASSERTION_TIMEOUT_MS, readEnv, runStep, setDefaultHidRecovery, setDefaultNativeLog, setDefaultDriverFactory, hasDefaultDriverFactory, setDefaultFixtureFactory, hasDefaultFixtureFactory, attemptSession, getSession, peekSession, accessibleName, announce, failing, formatIssues, toE2eError, settled, unwrap, parseUiadA11yEvents, parseInjectEvents, withA11yRead, renderA11yScreen, createDeviceA11y, IOS_DEVICE_CAPABILITIES, sniffCameraMedia, cameraFeedDir, clearCameraFeeds, isLeaseAlive, processAlive, describeOwner, isPhysicalIosId, wardenHome, defaultDbPath, ownerKey, openStore, callerFile, formatCoord, DEFAULT_SPEED_MPS, DEFAULT_INTERVAL_S, realTickerClock, startRouteTicker, startFlap, registerConditions, conditionsOf, conditionSinkFor, createDeviceConditions, parseBudgets, createDevicePerf, perfStartFor, spawnWarden, hash6, attemptDir, screenshotPath, specIdOf, BUN_TIMEOUT_MARGIN_MS2, ownHook, currentScope, createScope, runInScope, hooksFor, resolveUse, registrationDefaults, markOnly, onlyWasSeen, WORKSPACE_VERSION, WARDEN_E2E_VERSION2 };
9655
+ export { E2eError2, registerA11yTarget, a11yTargetOf, canActOnElements, noteSpanDetail, addSpanDetail, canObserve, canDetectAlerts, settleDetailOf, DEFAULT_SETTLE_QUIET_MS, DEFAULT_SETTLE_TIMEOUT_MS, hashFallbackQuietMs, settleByHash, snapshotHash, ok, err, swipeVector, screenRect, isOnScreen, fieldValue, PINCH_MS, ROTATE_MS, PATH_MS, centreOf, pinchRadius, rotateRadius, requireGesture, assertScale, assertDegrees, isInteractiveRole, resolveRole, normalizeText, matchesText, indexSnapshot, testIdQuery, textQuery, roleQuery, selectorExpression, renderSnapshot, renderHeader, lookUpTestIdViaReact, isLocator, Locator, pickOne, textOf, scrollUntilVisible, formatNativeTail, realNativeLogDeps, captureNativeLog, bunExec, execError, driverError, driverErr, bridgeStallSchema, toBridgeStall, noteBridgeStall, bridgeStallFromSpan, formatBridgeStall, logEventSchema, fatalEventSchema, responseSchema, mapWireError, createLineSplitter, encodeRequest, bunSpawn, stdioTransport, socketTransport, createSimdClient, spawnSimd, connectSimd, isRecoverableLoss, parseDuration, CAMERA_FACINGS, CONDITION_AREAS, resolveNetwork, resolveTextSize, androidFontScale, ENV_NAMES, DEFAULT_ASSERTION_TIMEOUT_MS, readEnv, runStep, setDefaultHidRecovery, setDefaultNativeLog, setDefaultDriverFactory, hasDefaultDriverFactory, setDefaultFixtureFactory, hasDefaultFixtureFactory, attemptSession, getSession, peekSession, accessibleName, announce, failing, formatIssues, toE2eError, settled, unwrap, parseUiadA11yEvents, parseInjectEvents, withA11yRead, renderA11yScreen, createDeviceA11y, IOS_DEVICE_CAPABILITIES, sniffCameraMedia, cameraFeedDir, clearCameraFeeds, isLeaseAlive, processAlive, describeOwner, isPhysicalIosId, wardenHome, defaultDbPath, ownerKey, openStore, callerFile, formatCoord, DEFAULT_SPEED_MPS, DEFAULT_INTERVAL_S, realTickerClock, startRouteTicker, startFlap, registerConditions, conditionsOf, conditionSinkFor, createDeviceConditions, parseBudgets, createDevicePerf, perfStartFor, spawnWarden, hash6, attemptDir, screenshotPath, specIdOf, BUN_TIMEOUT_MARGIN_MS2, ownHook, currentScope, createScope, runInScope, hooksFor, resolveUse, registrationDefaults, markOnly, onlyWasSeen, WORKSPACE_VERSION, WARDEN_E2E_VERSION2 };