@delacour/warden-e2e 0.12.1 → 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",
@@ -723,8 +755,9 @@ var UNCHECKED_WORDS = new Set(["0", "off", "false", "unchecked"]);
723
755
  function nonEmpty(value) {
724
756
  return value === undefined || value === "" ? undefined : value;
725
757
  }
758
+ var MASKED_VALUE = /^[\u2022\u25CF]+$/;
726
759
  function isSecure(element, traits) {
727
- return element.role === "securefield" || traits.has("password") || traits.has("secure") || element.rawType.toLowerCase().includes("secure");
760
+ return element.role === "textfield" && element.value !== undefined && MASKED_VALUE.test(element.value) || element.role === "securefield" || traits.has("password") || traits.has("secure") || element.rawType.toLowerCase().includes("secure");
728
761
  }
729
762
  function deriveChecked(element, role, spelled, traits, value) {
730
763
  if (spelled?.checked !== undefined)
@@ -1533,7 +1566,7 @@ function describeExpression(expr) {
1533
1566
  }
1534
1567
 
1535
1568
  // ../../packages/e2e/src/screen/react-locator.ts
1536
- async function locateByTestIdViaReact(facet, testId, view, options = {}) {
1569
+ async function lookUpTestIdViaReact(facet, testId, view, options = {}) {
1537
1570
  const treeOptions = {
1538
1571
  ...view.platform === "android" ? { pixelRatio: view.screen.scale } : {},
1539
1572
  ...options.screenName === undefined ? {} : { screen: options.screenName },
@@ -1543,10 +1576,10 @@ async function locateByTestIdViaReact(facet, testId, view, options = {}) {
1543
1576
  try {
1544
1577
  result = await facet.tree(treeOptions);
1545
1578
  } catch {
1546
- return [];
1579
+ return { answered: false };
1547
1580
  }
1548
1581
  if (!result.success)
1549
- return [];
1582
+ return { answered: false };
1550
1583
  const matches = [];
1551
1584
  for (const component of result.data.components) {
1552
1585
  const { testID, rect } = component;
@@ -1557,10 +1590,11 @@ async function locateByTestIdViaReact(facet, testId, view, options = {}) {
1557
1590
  tapPoint: { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 },
1558
1591
  source: "react",
1559
1592
  testId: testID,
1560
- name: component.name
1593
+ name: component.name,
1594
+ ...component.text === undefined ? {} : { text: component.text }
1561
1595
  });
1562
1596
  }
1563
- return matches;
1597
+ return { answered: true, matches };
1564
1598
  }
1565
1599
  function reactTapPoint(match, position) {
1566
1600
  if (position === undefined)
@@ -1568,6 +1602,41 @@ function reactTapPoint(match, position) {
1568
1602
  return { x: match.frame.x + position.x, y: match.frame.y + position.y };
1569
1603
  }
1570
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
+
1571
1640
  // ../../packages/e2e/src/screen/locator.ts
1572
1641
  var WAIT_STATES = ["attached", "detached", "visible", "hidden"];
1573
1642
  var DEFAULT_MAX_SWIPES = 10;
@@ -1580,6 +1649,9 @@ function assertOptions(verb, options, allowed) {
1580
1649
  throw new E2eError2("INVALID_ARGUMENT", `${verb} does not take ${unknown.map((key) => `"${key}"`).join(", ")}; it takes ${allowed.join(", ")}`);
1581
1650
  }
1582
1651
  }
1652
+ function reactVisible(match, observed) {
1653
+ return match.frame.width > 0 && match.frame.height > 0 && isOnScreen(match.tapPoint, observed.snapshot.screen);
1654
+ }
1583
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";
1584
1656
  function plainTestId(expr) {
1585
1657
  if (expr.kind !== "query" || expr.scope !== undefined || expr.query.kind !== "testId")
@@ -1664,8 +1736,78 @@ class Locator {
1664
1736
  const observed = await this.rt.observe(options);
1665
1737
  return { entries: resolve(this.expr, observed.index), observed };
1666
1738
  }
1667
- matches() {
1668
- 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);
1669
1811
  }
1670
1812
  async single() {
1671
1813
  const { entries, observed } = await this.matches();
@@ -1685,27 +1827,58 @@ class Locator {
1685
1827
  return this.rt.span(`read.${name}`, run, { detail: () => ({ locator: this.toString() }) });
1686
1828
  }
1687
1829
  count() {
1688
- 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
+ });
1689
1834
  }
1690
1835
  async all() {
1691
1836
  const count = await this.count();
1692
1837
  return Array.from({ length: count }, (_, index) => this.nth(index));
1693
1838
  }
1694
1839
  text() {
1695
- 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
+ });
1696
1849
  }
1697
1850
  allTexts() {
1698
- 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));
1699
1862
  }
1700
1863
  value() {
1701
1864
  return this.traced("value", async () => fieldValue((await this.single()).entry));
1702
1865
  }
1703
1866
  isVisible() {
1704
- 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
+ });
1705
1873
  }
1706
1874
  read() {
1707
1875
  return this.traced("read", async () => {
1708
- 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
+ }
1709
1882
  if (entries.length === 0)
1710
1883
  return { visible: false };
1711
1884
  const entry = this.exactlyOne(entries, observed);
@@ -1717,11 +1890,13 @@ class Locator {
1717
1890
  };
1718
1891
  });
1719
1892
  }
1720
- visibleIn(observed) {
1721
- const entries = resolve(this.expr, observed.index);
1722
- if (entries.length === 0)
1893
+ async shownIn(observed) {
1894
+ const resolved = await this.resolveIn(observed);
1895
+ if (resolved.kind === "unknown")
1723
1896
  return false;
1724
- 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;
1725
1900
  }
1726
1901
  async isHidden() {
1727
1902
  return !await this.isVisible();
@@ -1734,7 +1909,7 @@ class Locator {
1734
1909
  }
1735
1910
  boundingBox() {
1736
1911
  return this.traced("boundingBox", async () => {
1737
- const { entries, observed } = await this.look({ reuse: false });
1912
+ const { entries, observed } = await this.conclusive({ reuse: false });
1738
1913
  if (entries.length === 0) {
1739
1914
  const [only, ...others] = await this.reactCandidates(observed);
1740
1915
  if (only !== undefined && others.length === 0)
@@ -1761,14 +1936,19 @@ class Locator {
1761
1936
  return { done: false, state: undefined };
1762
1937
  }
1763
1938
  const { observed } = observation;
1764
- const entries = resolve(this.expr, observed.index);
1765
- const entry = entries.length === 0 ? undefined : this.exactlyOne(entries, observed);
1766
- 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 } };
1767
1944
  }, timeoutMs);
1768
1945
  if (result.status === "done")
1769
1946
  return;
1947
+ const last = result.last;
1948
+ if (last?.degraded !== undefined)
1949
+ throw this.degradedFailure(last.degraded, timeoutMs, last.snapshot);
1770
1950
  const message = `${this} did not become ${state} within ${timeoutMs} ms`;
1771
- 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));
1772
1952
  }
1773
1953
  act(verb, run, suffix = "") {
1774
1954
  return this.rt.step(`${verb} ${this}${suffix}`, run);
@@ -1794,6 +1974,8 @@ class Locator {
1794
1974
  }
1795
1975
  if (seen?.kind === "driver")
1796
1976
  throw new E2eError2("DRIVER", `snapshot kept failing: ${seen.message}`);
1977
+ if (seen?.kind === "degraded")
1978
+ throw this.degradedFailure(seen.read, timeoutMs, seen.snapshot);
1797
1979
  throw new E2eError2("LOCATOR_NOT_FOUND", `${this} matched no elements within ${timeoutMs} ms`, seen === undefined ? undefined : this.rt.excerpt(seen.snapshot));
1798
1980
  }
1799
1981
  async actionableLook(spec, allowReact, anyFrame) {
@@ -1908,7 +2090,7 @@ class Locator {
1908
2090
  watch.unchanged = 0;
1909
2091
  return { done: false, state: undefined };
1910
2092
  }
1911
- if (until.visibleIn(observation.observed))
2093
+ if (await until.shownIn(observation.observed))
1912
2094
  return { done: true, value: "arrived" };
1913
2095
  if (snapshotHash(observation.observed.snapshot) === watch.pressedOn)
1914
2096
  watch.unchanged += 1;
@@ -2033,10 +2215,17 @@ class Locator {
2033
2215
  return scrollUntilVisible(this.rt, this, options);
2034
2216
  }
2035
2217
  async reactStep(spec, observed, allowReact) {
2036
- 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 : [];
2037
2222
  const [match, ...others] = found;
2038
- 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
+ }
2039
2227
  return { done: false, state: { kind: "none", snapshot: observed.snapshot } };
2228
+ }
2040
2229
  if (!allowReact)
2041
2230
  return { done: false, state: { kind: "react", snapshot: observed.snapshot, matches: found.length } };
2042
2231
  if (others.length > 0) {
@@ -2175,6 +2364,18 @@ function reached(state, entry) {
2175
2364
  return entry === undefined || !entry.visible;
2176
2365
  }
2177
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
+ }
2178
2379
  function blockedReason(entry, spec, observed) {
2179
2380
  if (!entry.visible)
2180
2381
  return "is not visible";
@@ -2188,7 +2389,11 @@ function blockedReason(entry, spec, observed) {
2188
2389
  function isOnScreenEntry(entry, observed) {
2189
2390
  return entry?.visible === true && isOnScreen(tapPointOf(entry), observed.snapshot.screen);
2190
2391
  }
2191
- 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);
2192
2397
  if (absent)
2193
2398
  return new E2eError2("LOCATOR_NOT_FOUND", `${target} was not found after ${effort}`, screen);
2194
2399
  return new E2eError2("NOT_ACTIONABLE", `${target} is still not on screen after ${effort}`, screen);
@@ -2217,7 +2422,8 @@ async function scrollUntilVisible(rt, target, options = {}) {
2217
2422
  if (isOnScreenEntry(entry, observed))
2218
2423
  return;
2219
2424
  if (swipes >= maxSwipes || budget.remaining() <= 0) {
2220
- 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);
2221
2427
  }
2222
2428
  await scrollOnce(rt, observed, direction, options.momentum);
2223
2429
  await observed.driver.settle(SCROLL_SETTLE);
@@ -4420,19 +4626,113 @@ function driverErr(code, message, extra) {
4420
4626
  }
4421
4627
 
4422
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
4423
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
4424
4724
  var protocolVersion = 1;
4425
- var rectSchema2 = z6.object({
4426
- x: z6.number(),
4427
- y: z6.number(),
4428
- width: z6.number(),
4429
- height: z6.number()
4725
+ var rectSchema2 = z7.object({
4726
+ x: z7.number(),
4727
+ y: z7.number(),
4728
+ width: z7.number(),
4729
+ height: z7.number()
4430
4730
  });
4431
- var screenInfoSchema = z6.object({
4432
- width: z6.number(),
4433
- height: z6.number(),
4434
- scale: z6.number(),
4435
- 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()
4436
4736
  });
4437
4737
  var ROLES = [
4438
4738
  "button",
@@ -4459,29 +4759,29 @@ var ROLES = [
4459
4759
  "application",
4460
4760
  "other"
4461
4761
  ];
4462
- var pointSchema = z6.object({ x: z6.number(), y: z6.number() });
4463
- var wireElementSchema = z6.lazy(() => z6.object({
4464
- id: z6.string().nullish(),
4465
- role: z6.enum(ROLES),
4466
- rawType: z6.string(),
4467
- label: z6.string().nullish(),
4468
- identifier: z6.string().nullish(),
4469
- value: z6.string().nullish(),
4470
- text: z6.string().nullish(),
4471
- 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(),
4472
4772
  frame: rectSchema2,
4473
4773
  normFrame: rectSchema2.nullish(),
4474
- traits: z6.array(z6.string()).nullish(),
4475
- enabled: z6.boolean().nullish(),
4476
- focused: z6.boolean().nullish(),
4477
- hidden: z6.boolean().nullish(),
4478
- selected: z6.boolean().nullish(),
4479
- checked: z6.boolean().nullish(),
4480
- 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(),
4481
4781
  tapPoint: pointSchema.nullish(),
4482
- pid: z6.number().nullish(),
4483
- depth: z6.number().nullish(),
4484
- children: z6.array(wireElementSchema).nullish()
4782
+ pid: z7.number().nullish(),
4783
+ depth: z7.number().nullish(),
4784
+ children: z7.array(wireElementSchema).nullish()
4485
4785
  }));
4486
4786
  var ROLE_SET = new Set(ROLES);
4487
4787
  var isBag = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -4519,33 +4819,33 @@ function wireElementProblem(value) {
4519
4819
  const issue = parsed.error.issues[0];
4520
4820
  return issue ? `${issue.path.join(".") || "(element)"}: ${issue.message}` : "invalid element";
4521
4821
  }
4522
- var wireErrorSchema = z6.object({
4523
- code: z6.string(),
4524
- message: z6.string(),
4525
- retryable: z6.boolean().optional(),
4526
- 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()
4527
4827
  });
4528
- var readyEventSchema = z6.object({
4529
- event: z6.literal("ready"),
4530
- protocol: z6.number(),
4531
- version: z6.string(),
4532
- udid: z6.string(),
4533
- hid: z6.object({ transport: z6.string() }),
4534
- 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() }),
4535
4835
  screen: screenInfoSchema
4536
4836
  });
4537
- var logEventSchema = z6.object({
4538
- event: z6.literal("log"),
4539
- level: z6.enum(["debug", "info", "warn", "error"]),
4540
- message: z6.string(),
4541
- op: z6.string().optional(),
4542
- 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()
4543
4843
  });
4544
- var fatalEventSchema = z6.object({ event: z6.literal("fatal"), error: wireErrorSchema });
4545
- var eventSchema = z6.discriminatedUnion("event", [readyEventSchema, logEventSchema, fatalEventSchema]);
4546
- var responseSchema = z6.discriminatedUnion("ok", [
4547
- z6.object({ id: z6.string(), ok: z6.literal(true), data: z6.unknown().optional() }),
4548
- 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 })
4549
4849
  ]);
4550
4850
  function parseLine(line) {
4551
4851
  let json;
@@ -4561,74 +4861,80 @@ function parseLine(line) {
4561
4861
  const response = responseSchema.safeParse(json);
4562
4862
  return response.success ? { success: true, message: { kind: "response", response: response.data } } : { success: false, reason: `bad response: ${response.error.message}` };
4563
4863
  }
4564
- var healthDataSchema = z6.looseObject({
4565
- version: z6.string(),
4566
- udid: z6.string(),
4567
- hid: z6.object({ transport: z6.string() }),
4568
- ax: z6.object({
4569
- default: z6.string(),
4570
- bridge: z6.enum(["available", "degraded", "missing", "failed"]),
4571
- error: z6.string().optional(),
4572
- 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()
4573
4873
  }),
4574
4874
  screen: screenInfoSchema,
4575
- socket: z6.object({ path: z6.string(), clients: z6.number() }).optional(),
4576
- idb: z6.string().optional(),
4577
- xcode: z6.string().optional(),
4578
- builtWith: z6.object({ xcode: z6.string().optional(), idb: z6.string().optional() }).optional(),
4579
- coreSimulatorVersion: z6.string().nullish(),
4580
- frontmost: z6.object({ unresolvedAgoMs: z6.number(), method: z6.string() }).optional(),
4581
- state: z6.string().optional(),
4582
- 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()
4583
4884
  });
4584
- var snapshotDataSchema = z6.looseObject({
4585
- backend: z6.enum(["axbridge", "ax", "uiautomator"]),
4885
+ var snapshotDataSchema = z7.looseObject({
4886
+ backend: z7.enum(["axbridge", "ax", "uiautomator"]),
4586
4887
  screen: screenInfoSchema,
4587
- modal: z6.object({ pid: z6.number().optional(), description: z6.string().optional() }).nullish(),
4588
- truncated: z6.boolean(),
4589
- elements: z6.array(z6.custom(isWireElement, { error: (issue) => wireElementProblem(issue.input) })),
4590
- ms: z6.number(),
4591
- fallback: z6.object({ from: z6.string(), to: z6.string(), reason: z6.string() }).optional(),
4592
- target: z6.object({ pid: z6.number(), source: z6.string() }).optional(),
4593
- readMs: z6.number().optional(),
4594
- 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()
4595
4901
  });
4596
4902
  var elementAtPointDataSchema = wireElementSchema.nullable();
4597
- var screenshotDataSchema = z6.looseObject({
4598
- path: z6.string().optional(),
4599
- png: z6.string().optional(),
4600
- width: z6.number().optional(),
4601
- 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()
4602
4908
  });
4603
- var settleDataSchema = z6.looseObject({
4604
- quiet: z6.boolean(),
4605
- waitedMs: z6.number(),
4606
- via: z6.enum(["stream", "snapshot"]).optional().catch(undefined),
4607
- reason: z6.string().optional(),
4608
- 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()
4609
4915
  });
4610
- var rebindDataSchema = z6.looseObject({
4611
- bound: z6.boolean(),
4612
- waitedMs: z6.number(),
4613
- pid: z6.number().optional(),
4614
- restarts: z6.number().optional(),
4615
- recycled: z6.boolean().optional(),
4616
- 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()
4617
4923
  });
4618
- var alertButtonsDataSchema = z6.object({
4619
- title: z6.string().optional(),
4620
- message: z6.string().optional(),
4621
- buttons: z6.array(wireElementSchema)
4924
+ var alertButtonsDataSchema = z7.object({
4925
+ title: z7.string().optional(),
4926
+ message: z7.string().optional(),
4927
+ buttons: z7.array(wireElementSchema)
4622
4928
  }).nullable();
4623
- var frontmostDataSchema = z6.looseObject({
4624
- frontmost: z6.boolean(),
4625
- pid: z6.number().optional(),
4626
- frontmostPid: z6.number().optional()
4929
+ var frontmostDataSchema = z7.looseObject({
4930
+ frontmost: z7.boolean(),
4931
+ pid: z7.number().optional(),
4932
+ frontmostPid: z7.number().optional()
4627
4933
  });
4628
- var launchDataSchema = z6.looseObject({ pid: z6.number() });
4629
- var terminateDataSchema = z6.looseObject({ terminated: z6.boolean() });
4630
- var orientationDataSchema = z6.object({
4631
- 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([
4632
4938
  "portrait",
4633
4939
  "portraitUpsideDown",
4634
4940
  "landscapeLeft",
@@ -4637,7 +4943,7 @@ var orientationDataSchema = z6.object({
4637
4943
  "faceDown",
4638
4944
  "unknown"
4639
4945
  ]),
4640
- source: z6.enum(["device", "written", "assumed"]).optional()
4946
+ source: z7.enum(["device", "written", "assumed"]).optional()
4641
4947
  });
4642
4948
  var opSchemas = {
4643
4949
  health: healthDataSchema,
@@ -5078,7 +5384,7 @@ async function createSimdClient(factory, options = {}) {
5078
5384
  const transport = session.data.transport;
5079
5385
  if (!transport)
5080
5386
  return { success: false, error: driverError("E_INTERNAL", "warden-simd transport missing") };
5081
- const timeoutMs = opts.timeoutMs ?? defaultTimeoutMs;
5387
+ const timeoutMs = Math.max(opts.timeoutMs ?? defaultTimeoutMs, opts.minTimeoutMs ?? 0);
5082
5388
  const id = String(++seq);
5083
5389
  const result = await new Promise((resolve) => {
5084
5390
  const timer = setTimeout(() => {
@@ -5341,7 +5647,7 @@ function traceDriver(driver, options) {
5341
5647
  }
5342
5648
 
5343
5649
  // ../../packages/core/src/a11y/a11y.config.ts
5344
- import { z as z7 } from "zod";
5650
+ import { z as z8 } from "zod";
5345
5651
  var A11Y_RULE_IDS = [
5346
5652
  "missing-name",
5347
5653
  "image-no-name",
@@ -5354,11 +5660,11 @@ var A11Y_RULE_IDS = [
5354
5660
  "text-clipped",
5355
5661
  "native"
5356
5662
  ];
5357
- var ruleId = z7.enum(A11Y_RULE_IDS);
5358
- var a11yAuditConfigSchema = z7.object({
5359
- ignore: z7.array(z7.object({ rule: ruleId, identifier: z7.string().min(1).optional() }).strict()).optional(),
5360
- minSeverity: z7.enum(["error", "warning"]).optional(),
5361
- 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()
5362
5668
  }).strict();
5363
5669
  function parseA11yAuditEnv(raw) {
5364
5670
  let json;
@@ -5374,7 +5680,7 @@ function parseA11yAuditEnv(raw) {
5374
5680
  }
5375
5681
 
5376
5682
  // ../../packages/core/src/conditions/conditions.config.ts
5377
- import { z as z8 } from "zod";
5683
+ import { z as z9 } from "zod";
5378
5684
 
5379
5685
  // ../../packages/core/src/duration.ts
5380
5686
  var UNIT_MS = { ms: 1, s: 1000, m: 60000, h: 3600000, d: 86400000 };
@@ -5535,51 +5841,51 @@ function appliedA11y(op, applied) {
5535
5841
 
5536
5842
  // ../../packages/core/src/conditions/conditions.config.ts
5537
5843
  var NETWORK_WORDS = ["online", "offline", ...NETWORK_PRESET_NAMES];
5538
- var nonNegative2 = z8.number().nonnegative().finite();
5539
- var customNetworkSchema = z8.object({
5844
+ var nonNegative2 = z9.number().nonnegative().finite();
5845
+ var customNetworkSchema = z9.object({
5540
5846
  latencyMs: nonNegative2.optional(),
5541
5847
  jitterMs: nonNegative2.optional(),
5542
5848
  downKbps: nonNegative2.optional(),
5543
5849
  upKbps: nonNegative2.optional(),
5544
- lossPct: z8.number().min(0).max(100).optional()
5850
+ lossPct: z9.number().min(0).max(100).optional()
5545
5851
  }).strict();
5546
- var isoDate = z8.string().refine((v) => !Number.isNaN(Date.parse(v)), { message: "invalid date (use ISO 8601)" });
5547
- var offsetDuration = z8.string().refine((v) => parseDuration(v).success, { message: "invalid duration (use e.g. 2d)" });
5548
- var textSize = z8.union([z8.enum(TEXT_SIZE_NAMES), z8.number().positive().finite()]);
5549
- 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({
5550
5856
  textSize: textSize.optional(),
5551
- boldText: z8.boolean().optional(),
5552
- contrast: z8.enum(["increased", "normal"]).optional(),
5553
- reduceMotion: z8.boolean().optional()
5857
+ boldText: z9.boolean().optional(),
5858
+ contrast: z9.enum(["increased", "normal"]).optional(),
5859
+ reduceMotion: z9.boolean().optional()
5554
5860
  }).strict();
5555
- var conditionsConfigSchema = z8.object({
5556
- network: z8.union([z8.enum(NETWORK_WORDS), customNetworkSchema]).optional(),
5557
- location: z8.union([
5558
- z8.enum(["city-run", "city-bike", "freeway"]),
5559
- z8.object({
5560
- lat: z8.number().min(-90).max(90),
5561
- lon: z8.number().min(-180).max(180),
5562
- 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()
5563
5869
  }).strict()
5564
5870
  ]).optional(),
5565
- clock: z8.union([
5871
+ clock: z9.union([
5566
5872
  isoDate,
5567
- z8.object({ at: isoDate, frozen: z8.boolean().optional() }).strict(),
5568
- z8.object({ offset: offsetDuration }).strict()
5873
+ z9.object({ at: isoDate, frozen: z9.boolean().optional() }).strict(),
5874
+ z9.object({ offset: offsetDuration }).strict()
5569
5875
  ]).optional(),
5570
- permissions: z8.partialRecord(z8.enum(PERMISSION_NAMES), z8.enum(["granted", "denied", "unset"])).optional(),
5571
- locale: z8.string().min(1).optional(),
5572
- 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(),
5573
5879
  a11y: a11yConfigSchema.optional()
5574
5880
  }).strict();
5575
- var cameraFeedInput = z8.union([
5576
- z8.string().min(1),
5577
- 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()
5578
5884
  ]);
5579
- 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" });
5580
- var cameraFeedsSchema = z8.object({
5581
- back: z8.object({ path: z8.string().min(1), loop: z8.boolean() }).strict().optional(),
5582
- 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()
5583
5889
  }).strict();
5584
5890
  function parseCameraFeedsEnv(raw) {
5585
5891
  let json;
@@ -5661,7 +5967,7 @@ function parseConditionsEnv(raw) {
5661
5967
  }
5662
5968
 
5663
5969
  // ../../packages/e2e/src/env.ts
5664
- import { z as z9 } from "zod";
5970
+ import { z as z10 } from "zod";
5665
5971
  var ENV_NAMES = {
5666
5972
  udid: "WARDEN_UDID",
5667
5973
  deviceName: "WARDEN_DEVICE_NAME",
@@ -5699,68 +6005,68 @@ var ENV_NAMES = {
5699
6005
  var DEFAULT_TIMEOUT_MS5 = 120000;
5700
6006
  var DEFAULT_ACTION_TIMEOUT_MS = 30000;
5701
6007
  var DEFAULT_ASSERTION_TIMEOUT_MS = 5000;
5702
- var toggle = z9.enum(["1", "true", "0", "false"]).transform((v) => v === "1" || v === "true");
5703
- var millis = z9.string().regex(/^\d+$/, "must be a whole number of milliseconds").transform(Number).pipe(z9.number().int().positive());
5704
- 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) => {
5705
6011
  try {
5706
- const parsed = z9.array(z9.string()).safeParse(JSON.parse(raw));
6012
+ const parsed = z10.array(z10.string()).safeParse(JSON.parse(raw));
5707
6013
  if (parsed.success)
5708
6014
  return parsed.data;
5709
6015
  } catch {}
5710
6016
  ctx.addIssue({ code: "custom", message: "must be a JSON array of strings" });
5711
- return z9.NEVER;
6017
+ return z10.NEVER;
5712
6018
  });
5713
- var conditions = z9.string().transform((raw, ctx) => {
6019
+ var conditions = z10.string().transform((raw, ctx) => {
5714
6020
  const parsed = parseConditionsEnv(raw);
5715
6021
  if (parsed.success)
5716
6022
  return parsed.data;
5717
6023
  ctx.addIssue({ code: "custom", message: parsed.error });
5718
- return z9.NEVER;
6024
+ return z10.NEVER;
5719
6025
  });
5720
- var a11yAudit = z9.string().transform((raw, ctx) => {
6026
+ var a11yAudit = z10.string().transform((raw, ctx) => {
5721
6027
  const parsed = parseA11yAuditEnv(raw);
5722
6028
  if (parsed.success)
5723
6029
  return parsed.data;
5724
6030
  ctx.addIssue({ code: "custom", message: parsed.error });
5725
- return z9.NEVER;
6031
+ return z10.NEVER;
5726
6032
  });
5727
- var cameraFeeds = z9.string().transform((raw, ctx) => {
6033
+ var cameraFeeds = z10.string().transform((raw, ctx) => {
5728
6034
  const parsed = parseCameraFeedsEnv(raw);
5729
6035
  if (parsed.success)
5730
6036
  return parsed.data;
5731
6037
  ctx.addIssue({ code: "custom", message: parsed.error });
5732
- return z9.NEVER;
6038
+ return z10.NEVER;
5733
6039
  });
5734
6040
  var REQUIRED = "required when WARDEN_UDID is set";
5735
- var schema = z9.object({
5736
- udid: z9.string().min(1),
5737
- deviceName: z9.string().optional(),
5738
- platform: z9.enum(["ios", "android"]).default("ios"),
5739
- bundleId: z9.string({ error: REQUIRED }).min(1, REQUIRED),
5740
- appPath: z9.string().optional(),
5741
- metroUrl: z9.string().optional(),
5742
- 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(),
5743
6049
  launchArgs: launchArgs.default([]),
5744
- out: z9.string().optional(),
6050
+ out: z10.string().optional(),
5745
6051
  timeout: millis.default(DEFAULT_TIMEOUT_MS5),
5746
6052
  actionTimeout: millis.default(DEFAULT_ACTION_TIMEOUT_MS),
5747
6053
  assertionTimeout: millis.default(DEFAULT_ASSERTION_TIMEOUT_MS),
5748
- specsDir: z9.string().optional(),
5749
- helperDir: z9.string().optional(),
5750
- simd: z9.string().optional(),
5751
- simdSocket: z9.string().optional(),
5752
- 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(),
5753
6059
  logTimestamps: toggle.default(false),
5754
- deviceKind: z9.enum(["physical"]).optional(),
6060
+ deviceKind: z10.enum(["physical"]).optional(),
5755
6061
  iosRunnerRestart: launchArgs.optional(),
5756
6062
  afterReboot: launchArgs.optional(),
5757
- version: z9.string().optional(),
5758
- bin: z9.string().optional(),
5759
- perf: z9.string().optional(),
5760
- perfBudgets: z9.string().optional(),
6063
+ version: z10.string().optional(),
6064
+ bin: z10.string().optional(),
6065
+ perf: z10.string().optional(),
6066
+ perfBudgets: z10.string().optional(),
5761
6067
  conditions: conditions.optional(),
5762
- leaseId: z9.string().min(1).optional(),
5763
- 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(),
5764
6070
  biometrics: toggle.optional(),
5765
6071
  a11yAudit: a11yAudit.optional(),
5766
6072
  cameraFeeds: cameraFeeds.optional()
@@ -5770,7 +6076,7 @@ function defined2(values) {
5770
6076
  }
5771
6077
  function parseArgv(raw) {
5772
6078
  try {
5773
- 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));
5774
6080
  if (parsed.success)
5775
6081
  return parsed.data;
5776
6082
  } catch {}
@@ -5840,25 +6146,43 @@ function readEnv(env, opts = {}) {
5840
6146
  // ../../packages/e2e/src/runner/results.ts
5841
6147
  import { appendFileSync, mkdirSync as mkdirSync2 } from "fs";
5842
6148
  import { dirname as dirname3 } from "path";
5843
- import { z as z10 } from "zod";
5844
- var testStatusSchema = z10.enum(["passed", "failed", "skipped", "timed-out"]);
5845
- var testResultSchema = z10.object({
5846
- file: z10.string(),
5847
- 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()),
5848
6167
  status: testStatusSchema,
5849
- durationMs: z10.number(),
5850
- startedAt: z10.number(),
5851
- only: z10.boolean().optional(),
5852
- error: z10.object({
5853
- message: z10.string(),
5854
- code: z10.string().optional(),
5855
- 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()
5856
6175
  }).optional(),
5857
- artifacts: z10.object({ dir: z10.string(), files: z10.array(z10.string()) }).optional(),
5858
- device: z10.object({
5859
- udid: z10.string(),
5860
- platform: z10.enum(["ios", "android"]),
5861
- name: z10.string().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
6181
+ }).optional(),
6182
+ device: z11.object({
6183
+ udid: z11.string(),
6184
+ platform: z11.enum(["ios", "android"]),
6185
+ name: z11.string().optional()
5862
6186
  })
5863
6187
  });
5864
6188
  function openResults(path) {
@@ -6298,6 +6622,9 @@ function getSession(opts = {}) {
6298
6622
  slot()[SESSION_KEY] = session;
6299
6623
  return session;
6300
6624
  }
6625
+ function peekSession() {
6626
+ return slot()[SESSION_KEY];
6627
+ }
6301
6628
 
6302
6629
  // ../../packages/e2e/src/a11y/speech.ts
6303
6630
  var ACTIONABLE_ROLES = new Set([
@@ -9323,6 +9650,6 @@ function onlyWasSeen() {
9323
9650
 
9324
9651
  // ../../packages/e2e/src/version.ts
9325
9652
  var WORKSPACE_VERSION = "0.0.0-workspace";
9326
- var WARDEN_E2E_VERSION2 = "0.12.1";
9653
+ var WARDEN_E2E_VERSION2 = "0.14.0";
9327
9654
 
9328
- 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, CAMERA_FACINGS, CONDITION_AREAS, resolveNetwork, resolveTextSize, androidFontScale, ENV_NAMES, DEFAULT_ASSERTION_TIMEOUT_MS, readEnv, runStep, setDefaultHidRecovery, setDefaultNativeLog, setDefaultDriverFactory, hasDefaultDriverFactory, setDefaultFixtureFactory, hasDefaultFixtureFactory, attemptSession, getSession, 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 };