prowl-tools 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/lib.d.ts CHANGED
@@ -296,7 +296,10 @@ type AssertScreenshotStep = {
296
296
  threshold?: number;
297
297
  };
298
298
  };
299
- type Step = NavigateStep | ClickStep | FillStep | TypeStep | PressStep | WaitStep | SelectOptionStep | SelectStep | OnDialogStep | SetInputFilesStep | InlineAssertStep | RunHuntStep | WaitForSelectorStep | WaitForUrlStep | WaitForNetworkIdleStep | HoverStep | ScrollStep | ScrollToStep | ScreenshotStep | IfStep | RepeatStep | MockRouteStep | UnmockRouteStep | EvalScriptStep | RunScriptStep | AssertScreenshotStep | CopyTextStep | WaitForDownloadStep;
299
+ type AssertWithAiStep = {
300
+ assertWithAI: string;
301
+ };
302
+ type Step = NavigateStep | ClickStep | FillStep | TypeStep | PressStep | WaitStep | SelectOptionStep | SelectStep | OnDialogStep | SetInputFilesStep | InlineAssertStep | RunHuntStep | WaitForSelectorStep | WaitForUrlStep | WaitForNetworkIdleStep | HoverStep | ScrollStep | ScrollToStep | ScreenshotStep | IfStep | RepeatStep | MockRouteStep | UnmockRouteStep | EvalScriptStep | RunScriptStep | AssertScreenshotStep | AssertWithAiStep | CopyTextStep | WaitForDownloadStep;
300
303
  type Assertion = {
301
304
  selectorExists: string;
302
305
  } | {
@@ -312,7 +315,7 @@ type Assertion = {
312
315
  };
313
316
  type StepResult = {
314
317
  type: string;
315
- status: "pass" | "fail";
318
+ status: "pass" | "fail" | "warn";
316
319
  durationMs: number;
317
320
  selector?: string;
318
321
  value?: string;
@@ -591,6 +594,178 @@ declare function launchMacSession(options: LaunchMacOptions): Promise<MacSession
591
594
  /** Quit the target app (best effort) and shut the helper down. */
592
595
  declare function closeMacSession(session: MacSession): Promise<void>;
593
596
 
597
+ /** The selector kinds the native dialect understands. */
598
+ type NativeSelectorKind = "id" | "label" | "text" | "role" | "focused";
599
+ /**
600
+ * A Prowl native selector parsed into a neutral, platform-independent shape.
601
+ * `role` optionally carries a `name` (the `role=Type[name="…"]` form); `name` is
602
+ * present only when the bracket was supplied and non-empty.
603
+ */
604
+ type NativeSelector = {
605
+ kind: "id";
606
+ value: string;
607
+ } | {
608
+ kind: "label";
609
+ value: string;
610
+ } | {
611
+ kind: "text";
612
+ value: string;
613
+ } | {
614
+ kind: "role";
615
+ role: string;
616
+ name?: string;
617
+ } | {
618
+ kind: "focused";
619
+ };
620
+ /**
621
+ * Strip one layer of matching single/double quotes from a selector value. Mirrors
622
+ * the historical per-driver `unquote`, kept here so every native target unquotes
623
+ * identically.
624
+ */
625
+ declare function unquoteSelectorValue(value: string): string;
626
+ /** Wrap a selector value in double quotes (the ranker's canonical emitted form). */
627
+ declare function quoteSelectorValue(value: string): string;
628
+ /**
629
+ * Parse a Prowl selector string into a neutral {@link NativeSelector}. This is
630
+ * the single grammar shared by both mobile drivers (which then map the neutral
631
+ * kind onto their own wire query). Precedence — `:focus`, then `id=`, `role=`,
632
+ * `label=`, `text=`, and finally a bare string treated as `text=` — matches the
633
+ * behavior the per-driver parsers had before consolidation.
634
+ */
635
+ declare function parseNativeSelector(selector: string): NativeSelector;
636
+ /**
637
+ * The literal a `text=` selector matches, or null when `selector` is not an
638
+ * explicit `text=` form (a bare string is intentionally excluded — it mirrors the
639
+ * web driver's `parseTextSelector`, which `forbiddenSelectors` relies on). Shared
640
+ * so Android and iOS unwrap text selectors identically.
641
+ */
642
+ declare function unwrapNativeTextSelector(selector: string): string | null;
643
+ /**
644
+ * Qualify a bare Android `resource-id` with the app's package. The raw
645
+ * uiautomator2 server matches resource-ids exactly (bare names return no
646
+ * elements), so `id=save` becomes `<appPackage>:id/save`. Values that already
647
+ * contain a `:` (e.g. `android:id/title`) — or calls without a package — pass
648
+ * through untouched. Both the Android driver's locator translation and the
649
+ * host-side matcher qualify through this one function.
650
+ */
651
+ declare function qualifyResourceId(value: string, appPackage?: string): string;
652
+ /** Prefix `XCUIElementType` onto an iOS role shorthand (e.g. `Button`) when missing. */
653
+ declare function normalizeXcuiClassName(role: string): string;
654
+ /** Strip the `XCUIElementType` prefix for a friendlier `role=` shorthand. */
655
+ declare function shortIosType(type: string): string;
656
+ /**
657
+ * The per-node ingredients the ranker needs, already projected out of a
658
+ * platform's own node shape:
659
+ * - `id` the native identifier (Android resource-id, iOS accessibility id,
660
+ * macOS AXIdentifier) → emitted as `id=`.
661
+ * - `label` the exact accessibility label (Android content-desc, iOS label,
662
+ * macOS title/description) → emitted as `label="…"`.
663
+ * - `role` the class/type/role string to emit verbatim (already shortened for
664
+ * iOS) → emitted as `role=` and in `role=…[name="…"]`.
665
+ * - `name` the representative visible-text name used for `role=…[name]` and
666
+ * the `text=` fallback.
667
+ */
668
+ type NativeRankFields = {
669
+ id?: string;
670
+ label?: string;
671
+ role?: string;
672
+ name?: string;
673
+ };
674
+ /**
675
+ * Ranked selector candidates for a node, best → last resort:
676
+ * `id=` > `label="…"` > `role=…[name="…"]` > `text="…"`, with a bare `role=`
677
+ * fallback so any node carrying a role stays addressable. This is the one ranking
678
+ * algorithm every native analyzer emits through, so a change to selector priority
679
+ * happens in exactly one place. Returns an empty array when a node exposes nothing
680
+ * addressable.
681
+ */
682
+ declare function rankNativeSelectors(fields: NativeRankFields): string[];
683
+ /** The native targets whose selector dialect this module defines. */
684
+ type NativePlatform = "android" | "ios" | "macos";
685
+ /** How a selector kind resolves on one platform (mirrors the matrix above). */
686
+ type SelectorKindMapping = {
687
+ /** The native attribute(s) the kind targets. */
688
+ attribute: string;
689
+ /** The comparison mode against that attribute. */
690
+ match: "exact" | "exact (package-qualified)" | "substring";
691
+ };
692
+ /**
693
+ * The per-platform attribute mapping tables — the machine-readable form of the
694
+ * compatibility matrix, so the mapping is documented once and can be asserted in
695
+ * tests. Keyed by platform, then by the four addressable selector kinds. (`role`
696
+ * describes the bare `role=` form; the `role=…[name]` composite pairs an exact
697
+ * role with a substring name, per the matrix.)
698
+ */
699
+ declare const NATIVE_ATTRIBUTE_MAP: Readonly<Record<NativePlatform, Readonly<Record<"id" | "label" | "text" | "role", SelectorKindMapping>>>>;
700
+ /**
701
+ * A hierarchy node projected into the neutral attributes the matcher compares
702
+ * against. Platform code (analyzers, and later the runners/macdriver) projects
703
+ * its own node shape into this:
704
+ * - `id` native identifier for exact `id=` matching.
705
+ * - `label` exact accessibility label for exact `label=` matching.
706
+ * - `role` class/type/role in the platform's canonical (full) form; the
707
+ * dialect's {@link NativeMatchDialect.normalizeRole} reconciles a
708
+ * shorthand selector (`Button`) with a full node type.
709
+ * - `textValues` every string a `text=` / `[name]` substring should test
710
+ * (Android: `[text]`; iOS: `[label, value]`; macOS:
711
+ * `[title, description, value]`).
712
+ * - `focused` whether the node currently holds keyboard focus (`:focus`).
713
+ */
714
+ type NativeNode = {
715
+ id?: string;
716
+ label?: string;
717
+ role?: string;
718
+ textValues: string[];
719
+ focused?: boolean;
720
+ };
721
+ /** Options threaded into matching (currently only Android id package-qualification). */
722
+ type NativeMatchOptions = {
723
+ /** App package used to qualify a bare Android `id=` before an exact compare. */
724
+ appPackage?: string;
725
+ };
726
+ /**
727
+ * The platform-specific part of matching: how to normalize a role/type string for
728
+ * comparison, and how to normalize an `id=` value before an exact id compare.
729
+ * Everything else (exact id/label, substring text, focus) is platform-independent.
730
+ */
731
+ type NativeMatchDialect = {
732
+ platform: NativePlatform;
733
+ /** Canonicalize a role/type string so a shorthand selector matches a full node type. */
734
+ normalizeRole: (role: string) => string;
735
+ /** Canonicalize an `id=` value (Android package-qualifies bare ids; others identity). */
736
+ normalizeId: (value: string, options: NativeMatchOptions) => string;
737
+ };
738
+ /** Android matching dialect: identity role compare, package-qualified ids. */
739
+ declare const ANDROID_MATCH_DIALECT: NativeMatchDialect;
740
+ /** iOS matching dialect: `XCUIElementType…`-normalized role compare, identity ids. */
741
+ declare const IOS_MATCH_DIALECT: NativeMatchDialect;
742
+ /** macOS matching dialect (exposed for the future macdriver migration). */
743
+ declare const MACOS_MATCH_DIALECT: NativeMatchDialect;
744
+ /**
745
+ * Decide whether a single projected {@link NativeNode} satisfies a parsed
746
+ * {@link NativeSelector}, using the platform's attribute + match-mode semantics:
747
+ * - `id` exact match on `node.id` (Android value package-qualified first).
748
+ * - `label` exact match on `node.label`.
749
+ * - `text` substring match against any of `node.textValues`.
750
+ * - `role` exact (normalized) role match; with `name`, additionally a
751
+ * substring match against any of `node.textValues`.
752
+ * - `focused` node currently holds keyboard focus.
753
+ */
754
+ declare function nodeMatchesSelector(dialect: NativeMatchDialect, selector: NativeSelector, node: NativeNode, options?: NativeMatchOptions): boolean;
755
+ /**
756
+ * Walk a hierarchy of platform nodes depth-first and collect every node matching
757
+ * `selector`, in document order. Generic over the caller's own node type so the
758
+ * analyzers get their original, fully-typed nodes back: pass a `project` that maps
759
+ * a node to its {@link NativeNode} attributes and a `children` accessor.
760
+ */
761
+ declare function matchNativeTree<T>(dialect: NativeMatchDialect, selector: NativeSelector, root: T, project: (node: T) => NativeNode, children: (node: T) => readonly T[], options?: NativeMatchOptions): T[];
762
+ /**
763
+ * Parse a raw agent `/source` XML dump into an element tree (best-effort, via the
764
+ * dependency-free {@link parseXml}) so a caller can match against a snapshot
765
+ * without a device. Returns null when the payload holds no element.
766
+ */
767
+ declare function parseSnapshot(xml: string): XmlElement | null;
768
+
594
769
  /** A structured query the {@link AndroidAgentClient} resolves against the device. */
595
770
  type AndroidQuery = {
596
771
  by: "id";
@@ -638,6 +813,12 @@ interface AndroidAgentClient {
638
813
  pressKeyCode(keyCode: number): Promise<void>;
639
814
  /** Capture the current screen as PNG bytes. */
640
815
  screenshotPng(): Promise<Buffer>;
816
+ /**
817
+ * Return the current UI hierarchy as uiautomator2 `/source` XML. Present on
818
+ * live clients and consumed by the analyzer (PROWL-061); optional so lighter
819
+ * fakes that only drive/query need not implement it.
820
+ */
821
+ source?(): Promise<string>;
641
822
  close(): Promise<void>;
642
823
  }
643
824
  type AndroidDriverOptions = {
@@ -651,7 +832,12 @@ type AndroidDriverOptions = {
651
832
  declare const ANDROID_KEYCODES: Readonly<Record<string, number>>;
652
833
  /** Escape a string for embedding inside a `new UiSelector()...("...")` argument. */
653
834
  declare function escapeUiSelectorArg(value: string): string;
654
- /** Parse a Prowl selector string into an {@link AndroidQuery}. Bare text matches by text. */
835
+ /**
836
+ * Parse a Prowl selector string into an {@link AndroidQuery}. Bare text matches by
837
+ * text. The grammar (and its `label=`-in-assertions trap) is defined once in the
838
+ * shared native selector engine ({@link parseNativeSelector}, PROWL-060); this
839
+ * only maps the neutral result onto Android's on-device query.
840
+ */
655
841
  declare function parseAndroidSelector(selector: string): AndroidQuery;
656
842
  /**
657
843
  * Translate an {@link AndroidQuery} into the uiautomator2 server's native
@@ -832,33 +1018,6 @@ declare function createUia2AgentClient(transport: Uia2Transport, sessionId: stri
832
1018
  appPackage?: string;
833
1019
  }): AndroidAgentClient;
834
1020
 
835
- /**
836
- * PROWL-059 / ARCH-010 — iOS simulator implementation of {@link SessionDriver}.
837
- *
838
- * `IosDriver` drives a native iOS app through a prebuilt WebDriverAgent (WDA)
839
- * runner over its W3C-shaped HTTP/JSON API ({@link IosAgentClient}). Like the
840
- * macOS and Android drivers it implements only the portable subset of the driver
841
- * surface — its `capabilities` set is honest: `query`, `interact`, `wait`,
842
- * `screenshot`. Web-only verbs (navigation, network, dialogs, files, downloads,
843
- * script evaluation) are unsupported stubs; the runner never reaches them because
844
- * both the target step-compatibility check and the runtime capability gate reject
845
- * web-only steps for native targets.
846
- *
847
- * Screenshots are captured via `simctl` (injected as `captureScreenshot`), not
848
- * WDA, so artifacts still work even if the agent wedges.
849
- *
850
- * Selector dialect (parsed here into an {@link IosQuery}; the agent then matches
851
- * on the simulator). Semantics mirror the macOS/Android drivers so `id=`/`label=`/
852
- * `text=`/`role=` mean the same thing across native targets (PROWL-060 will unify
853
- * the engines later):
854
- * id=save → accessibility id (accessibilityIdentifier / name)
855
- * label="Submit" → predicate `label == "Submit"` (exact)
856
- * role=XCUIElementTypeButton → element class (shorthand `Button` is accepted too)
857
- * role=Button[name="Save"] → class + visible text (label/value substring)
858
- * text="Save" | Save → label/value substring
859
- * :focus → predicate `hasKeyboardFocus == 1`
860
- */
861
-
862
1021
  /** A structured query the {@link IosAgentClient} resolves against the simulator. */
863
1022
  type IosQuery = {
864
1023
  by: "accessibilityId";
@@ -899,6 +1058,12 @@ interface IosAgentClient {
899
1058
  sendKeys(keys: string[]): Promise<void>;
900
1059
  /** Return to the springboard home screen (WDA `/wda/homescreen`). */
901
1060
  homescreen(): Promise<void>;
1061
+ /**
1062
+ * Return the current UI hierarchy as WebDriverAgent `/source` XML. Present on
1063
+ * live clients and consumed by the analyzer (PROWL-061); optional so lighter
1064
+ * fakes that only drive/query need not implement it.
1065
+ */
1066
+ source?(): Promise<string>;
902
1067
  close(): Promise<void>;
903
1068
  }
904
1069
  type IosDriverOptions = {
@@ -915,9 +1080,12 @@ type IosDriverOptions = {
915
1080
  declare const IOS_PRESS_KEYS: readonly string[];
916
1081
  /** Escape a string for embedding inside a double-quoted NSPredicate string literal. */
917
1082
  declare function escapePredicateArg(value: string): string;
918
- /** Prefix `XCUIElementType` onto a role shorthand (e.g. `Button`) when missing. */
919
- declare function normalizeXcuiClassName(role: string): string;
920
- /** Parse a Prowl selector string into an {@link IosQuery}. Bare text matches by text. */
1083
+ /**
1084
+ * Parse a Prowl selector string into an {@link IosQuery}. Bare text matches by
1085
+ * text. The grammar (and its `label=`-in-assertions trap) is defined once in the
1086
+ * shared native selector engine ({@link parseNativeSelector}, PROWL-060); this
1087
+ * only maps the neutral result onto iOS's WDA query.
1088
+ */
921
1089
  declare function parseIosSelector(selector: string): IosQuery;
922
1090
  /**
923
1091
  * Translate an {@link IosQuery} into a WDA locator strategy. `id` uses the native
@@ -941,6 +1109,19 @@ type SimctlRunner = (args: string[], options?: {
941
1109
  timeoutMs?: number;
942
1110
  env?: NodeJS.ProcessEnv;
943
1111
  }) => Promise<SimctlResult>;
1112
+ /** A handle to a spawned long-running `xcrun` process (the WDA xcodebuild test host). */
1113
+ type XcrunProcessHandle = {
1114
+ kill(): void;
1115
+ };
1116
+ /**
1117
+ * Spawns a long-running `xcrun` command in the background — used for the
1118
+ * `xcodebuild test-without-building` run that hosts WebDriverAgent (which never
1119
+ * exits on its own; it serves HTTP until killed). Mirrors the Android
1120
+ * `AdbSpawner` so the launch flow stays unit-testable with a fake.
1121
+ */
1122
+ type XcrunSpawner = (args: string[], options?: {
1123
+ env?: NodeJS.ProcessEnv;
1124
+ }) => XcrunProcessHandle;
944
1125
  /** One simulator device from `simctl list devices --json`. */
945
1126
  type SimDevice = {
946
1127
  udid: string;
@@ -990,8 +1171,8 @@ declare function parseXcodeVersion(stdout: string): string | null;
990
1171
 
991
1172
  /** Bundle id of the prebuilt WebDriverAgent runner (its xctest host app). */
992
1173
  declare const WDA_RUNNER_BUNDLE_ID = "com.facebook.WebDriverAgentRunner.xctrunner";
993
- /** The runner `.app` produced by `xcodebuild build-for-testing`. */
994
- declare const WDA_RUNNER_APP_NAME = "WebDriverAgentRunner-Runner.app";
1174
+ /** Env var WDA reads to pick its HTTP port (injected into the runner's xctestrun env). */
1175
+ declare const WDA_USE_PORT_ENV = "USE_PORT";
995
1176
  /** Establishes a live {@link IosAgentClient} against a running WDA HTTP server. */
996
1177
  type IosAgentConnector = (options: {
997
1178
  host: string;
@@ -1009,7 +1190,7 @@ declare function resolveWdaProject(requireFn?: NodeRequire): {
1009
1190
  };
1010
1191
  /** Cache directory for a built WDA runner, keyed on the WDA + Xcode versions. */
1011
1192
  declare function wdaCacheDir(wdaVersion: string, xcode: string, homeDir?: string): string;
1012
- type ResolveWdaRunnerOptions = {
1193
+ type ResolveWdaTestRunOptions = {
1013
1194
  runner?: SimctlRunner;
1014
1195
  env?: NodeJS.ProcessEnv;
1015
1196
  homeDir?: string;
@@ -1018,13 +1199,50 @@ type ResolveWdaRunnerOptions = {
1018
1199
  logger?: (message: string) => void;
1019
1200
  };
1020
1201
  /**
1021
- * Resolve a WebDriverAgent runner `.app`. Order: (a) the `PROWL_WDA_RUNNER` env
1022
- * override; (b) a previously built runner in the version-keyed cache; (c) a
1023
- * one-time `xcodebuild build-for-testing` that populates the cache. Simulators
1024
- * need no code signing. Throws actionable errors when Xcode is missing or the
1025
- * build fails.
1202
+ * Resolve a WebDriverAgent `.xctestrun` file (the input to the XCTest host
1203
+ * launch). Order: (a) the `PROWL_WDA_RUNNER` env override; (b) a previously built
1204
+ * xctestrun in the version-keyed cache; (c) a one-time
1205
+ * `xcodebuild build-for-testing` that populates the cache. Simulators need no
1206
+ * code signing. Throws actionable errors when Xcode is missing or the build fails.
1207
+ */
1208
+ declare function resolveWdaTestRun(options?: ResolveWdaTestRunOptions): Promise<string>;
1209
+ /**
1210
+ * Return a deep copy of a parsed `.xctestrun` plist with the WDA HTTP port
1211
+ * (`USE_PORT`) injected into every test target's `EnvironmentVariables`. WDA
1212
+ * reads `USE_PORT` from its process environment; under `xcodebuild test` that
1213
+ * environment comes from the xctestrun, so the dynamic port must be written here.
1214
+ *
1215
+ * Handles both xctestrun layouts: format 1 (top-level dict keyed by test-target
1216
+ * name) and format 2 (a `TestConfigurations[].TestTargets[]` tree). A test target
1217
+ * is recognized by a `TestBundlePath`/`TestHostPath` string; any object that
1218
+ * already carries an `EnvironmentVariables` dict is updated too. Throws when no
1219
+ * target is found, so a future format change fails loudly rather than launching
1220
+ * WDA on the wrong port.
1221
+ */
1222
+ declare function injectUsePortIntoXctestrun(plist: unknown, port: number): unknown;
1223
+ /**
1224
+ * Produce a launch-specific `.xctestrun` with `USE_PORT` set to `port`, returning
1225
+ * its path. The base xctestrun (built/cached) is never mutated in place.
1226
+ */
1227
+ type WdaTestRunPreparer = (options: {
1228
+ xctestrunPath: string;
1229
+ port: number;
1230
+ }) => Promise<string>;
1231
+ /**
1232
+ * Default preparer: reads the base xctestrun with `plutil` (JSON), injects
1233
+ * `USE_PORT`, and writes a fresh, uniquely-named xctestrun **next to the base
1234
+ * one**. This placement is required, not incidental: an xctestrun's product
1235
+ * paths are relative to `__TESTROOT__`, which xcodebuild resolves to the
1236
+ * directory containing the xctestrun file — so a copy written to a temp dir
1237
+ * makes `xcodebuild test-without-building` fail with "Missing test product".
1238
+ * Writing the sibling into the build's Products dir keeps `__TESTROOT__` pointing
1239
+ * at the real products. The intermediate JSON goes to a temp dir; only the
1240
+ * `.xctestrun` lands beside the products and is removed on teardown. `plutil`
1241
+ * ships with macOS, so no extra dependency is added.
1026
1242
  */
1027
- declare function resolveWdaRunner(options?: ResolveWdaRunnerOptions): Promise<string>;
1243
+ declare const defaultWdaTestRunPreparer: WdaTestRunPreparer;
1244
+ /** The `xcodebuild test-without-building` args that host WDA against `udid`. */
1245
+ declare function wdaTestRunArgs(xctestrunPath: string, udid: string): string[];
1028
1246
  type IosSession = {
1029
1247
  client: IosAgentClient;
1030
1248
  driver: SessionDriver;
@@ -1044,10 +1262,14 @@ type LaunchIosOptions = {
1044
1262
  coldStart?: boolean;
1045
1263
  timeoutMs?: number;
1046
1264
  runner?: SimctlRunner;
1265
+ /** Spawner for the long-running `xcodebuild test-without-building` WDA host. */
1266
+ spawner?: XcrunSpawner;
1267
+ /** Builds the per-launch port-injected xctestrun. */
1268
+ testRunPreparer?: WdaTestRunPreparer;
1047
1269
  portAllocator?: () => Promise<number>;
1048
1270
  agentConnector?: IosAgentConnector;
1049
- /** Skip WDA build/resolution by supplying the runner app path directly. */
1050
- wdaRunnerApp?: string;
1271
+ /** Skip WDA build/resolution by supplying the base `.xctestrun` path directly. */
1272
+ wdaTestRun?: string;
1051
1273
  /** Optional app scope guardrail from config.guardrails.allowedApps. */
1052
1274
  allowedApps?: string[];
1053
1275
  /** Override the simulator lock root; intended for tests. */
@@ -1055,9 +1277,10 @@ type LaunchIosOptions = {
1055
1277
  logger?: (message: string) => void;
1056
1278
  };
1057
1279
  /**
1058
- * Preflight, install, launch, and attach WDA, returning a live {@link IosSession}.
1059
- * Actionable errors cover each failure mode: Xcode/simctl missing, no booted
1060
- * simulator (device selection), WDA build failures, agent unreachable (readiness).
1280
+ * Preflight, host WDA via `xcodebuild test-without-building`, launch the target
1281
+ * app, and attach, returning a live {@link IosSession}. Actionable errors cover
1282
+ * each failure mode: Xcode/simctl missing, no booted simulator (device
1283
+ * selection), WDA build failures, agent unreachable (readiness).
1061
1284
  */
1062
1285
  declare function launchIosSession(options: LaunchIosOptions): Promise<IosSession>;
1063
1286
  /** Tear down an iOS session (agent session, WDA runner, target app). */
@@ -1183,6 +1406,20 @@ declare function androidAppAllowedIdentities(app: string, resolvedPackage?: stri
1183
1406
  */
1184
1407
  declare function assertAndroidAppAllowed(allowedApps: string[], app: string, resolvedPackage?: string): void;
1185
1408
 
1409
+ type AiProvider = "anthropic" | "openai";
1410
+ type AiConfig = {
1411
+ provider: AiProvider;
1412
+ model: string;
1413
+ apiKey: string;
1414
+ /**
1415
+ * API root the request is sent to (no trailing slash, no path). When omitted,
1416
+ * falls back to the provider's public API. Set via `PROWL_AI_BASE_URL` so a
1417
+ * self-hosted gateway or (later) a managed Prowl proxy can slot in without
1418
+ * code changes.
1419
+ */
1420
+ baseUrl?: string;
1421
+ };
1422
+
1186
1423
  type StepCallback = (result: StepResult, step: Step, index: number) => void;
1187
1424
 
1188
1425
  type RunOptions = {
@@ -1950,12 +2187,262 @@ type AnalyzeMacOptions = {
1950
2187
  */
1951
2188
  declare function analyzeMacApp(client: MacHelperClient, options: AnalyzeMacOptions): Promise<MacAnalysisResult>;
1952
2189
 
1953
- type AiProvider = "anthropic" | "openai";
1954
- type AiConfig = {
1955
- provider: AiProvider;
1956
- model: string;
1957
- apiKey: string;
2190
+ /**
2191
+ * PROWL-061 — a tiny, dependency-free XML parser for on-device UI hierarchies.
2192
+ *
2193
+ * Both native mobile analyzers read an XML page source: Android via the
2194
+ * uiautomator2 agent's `GET /source` (a `<hierarchy>` of `<node>` elements) and
2195
+ * iOS via WebDriverAgent's `GET /source` (a tree of `<XCUIElementType…>`
2196
+ * elements). Both dialects share the same shape — a tree of elements whose data
2197
+ * lives entirely in double-quoted attributes, with no meaningful text between
2198
+ * tags — so one small scanner serves both, keeping with the repo's "no heavy
2199
+ * SDK" ethos (there is no XML parser in our own dependency set, only transitive
2200
+ * ones we must not rely on).
2201
+ *
2202
+ * The scanner is deliberately narrow: it understands element start/end/self-close
2203
+ * tags, quoted attributes (single or double), XML declarations, comments, and the
2204
+ * five predefined entities plus numeric character references. It ignores text
2205
+ * nodes and CDATA (the UI dumps carry none). It never throws on malformed input —
2206
+ * it returns the best-effort root element, or null when there is no element at
2207
+ * all — so a surprising payload degrades to an empty analysis rather than a crash.
2208
+ */
2209
+ /** A parsed XML element: its tag name, attributes, and child elements. */
2210
+ type XmlElement = {
2211
+ tag: string;
2212
+ attrs: Record<string, string>;
2213
+ children: XmlElement[];
2214
+ };
2215
+ /** Decode the five predefined XML entities and numeric character references. */
2216
+ declare function decodeXmlEntities(value: string): string;
2217
+ /**
2218
+ * Parse an XML document into its root {@link XmlElement} (best-effort). Returns
2219
+ * null when the input contains no element. Malformed markup is tolerated: unknown
2220
+ * constructs are skipped and mismatched end tags simply pop the stack.
2221
+ */
2222
+ declare function parseXml(input: string): XmlElement | null;
2223
+
2224
+ /**
2225
+ * PROWL-061 — Android analyzer.
2226
+ *
2227
+ * The Android analog of {@link analyzeMacApp}: dumps a running Android app's
2228
+ * interactive elements with ranked selector candidates so hunt authors don't have
2229
+ * to guess resource-ids. It reads the on-device UI hierarchy through the same
2230
+ * uiautomator2 agent the runner uses (`GET /source`, the standard `uiautomator
2231
+ * dump` XML) and shapes the result to mirror the macOS analyzer's feel.
2232
+ *
2233
+ * Selector ranking (best → last resort), matching the Android driver's selector
2234
+ * dialect so the emitted selectors are directly usable in hunts:
2235
+ * id=<resource-id> (best — the native `data-testid`; already
2236
+ * package-qualified in the dump, e.g.
2237
+ * `com.android.settings:id/title`)
2238
+ * label="<content-desc>" (exact content-description)
2239
+ * role=<class>[name="<text>"] (widget class + visible text, substring)
2240
+ * text="<text>" (last resort — visible-text substring)
2241
+ *
2242
+ * Read-only: this never taps, types, or otherwise mutates the app — it only reads
2243
+ * the page source.
2244
+ */
2245
+
2246
+ /**
2247
+ * Widget classes treated as interactive on their own (in addition to any node
2248
+ * flagged clickable / long-clickable / checkable / scrollable). Tuned to the
2249
+ * common `android.widget` / AndroidX input and control classes; text/containers
2250
+ * without an interactive flag are surfaced only when clickable.
2251
+ */
2252
+ declare const ANDROID_INTERACTIVE_CLASSES: ReadonlySet<string>;
2253
+ /** A single node in the uiautomator hierarchy (`<node>` element attributes). */
2254
+ type AndroidUiNode = {
2255
+ className?: string;
2256
+ resourceId?: string;
2257
+ contentDesc?: string;
2258
+ text?: string;
2259
+ package?: string;
2260
+ clickable?: boolean;
2261
+ longClickable?: boolean;
2262
+ checkable?: boolean;
2263
+ checked?: boolean;
2264
+ scrollable?: boolean;
2265
+ focusable?: boolean;
2266
+ /** Whether the node currently holds input focus (`:focus`; from the dump's `focused` attr). */
2267
+ focused?: boolean;
2268
+ enabled?: boolean;
2269
+ children: AndroidUiNode[];
2270
+ };
2271
+ /** An interactive element with ranked selector candidates (best first). */
2272
+ type AndroidAnalysisElement = {
2273
+ className: string;
2274
+ resourceId?: string;
2275
+ contentDesc?: string;
2276
+ text?: string;
2277
+ clickable?: boolean;
2278
+ checkable?: boolean;
2279
+ scrollable?: boolean;
2280
+ enabled?: boolean;
2281
+ /** Ranked selector candidates, best first. Always at least one entry. */
2282
+ selectors: string[];
2283
+ };
2284
+ type AndroidAnalysisResult = {
2285
+ /** Package name of the analyzed app. */
2286
+ app: string;
2287
+ elements: AndroidAnalysisElement[];
2288
+ };
2289
+ /** Parse a uiautomator2 `/source` XML dump into a tree of {@link AndroidUiNode}. */
2290
+ declare function parseAndroidHierarchy(xml: string): AndroidUiNode | null;
2291
+ /** Whether a node is worth surfacing as an interactive element. */
2292
+ declare function isAndroidInteractive(node: AndroidUiNode): boolean;
2293
+ /**
2294
+ * Ranked selector candidates for a node, best first, via the shared native
2295
+ * selector engine (PROWL-060). The Android attribute mapping is applied here —
2296
+ * `id=`←resource-id (already package-qualified in the dump, emitted verbatim),
2297
+ * `label=`←content-desc, `role=`←class, name←visible text — then
2298
+ * {@link rankNativeSelectors} imposes the shared `id=` > `label=` >
2299
+ * `role=…[name]` > `text=` order (with a bare `role=` fallback).
2300
+ */
2301
+ declare function rankAndroidSelectors(node: AndroidUiNode): string[];
2302
+ /**
2303
+ * Project an {@link AndroidUiNode} into the neutral {@link NativeNode} the shared
2304
+ * matcher compares against: `id`←resource-id, `label`←content-desc, `role`←class,
2305
+ * `text=` substring source ← visible text.
2306
+ */
2307
+ declare function androidNodeToNative(node: AndroidUiNode): NativeNode;
2308
+ /**
2309
+ * Host-side "snapshot-then-match": parse a uiautomator2 `/source` dump and return
2310
+ * every node the Prowl `selector` resolves to, in document order, using Android's
2311
+ * shared dialect semantics. Read-only and device-free. Exposed for tooling and a
2312
+ * future runner/macdriver migration; the runner still matches on-device today.
2313
+ */
2314
+ declare function matchAndroidSelector(xml: string, selector: string, options?: NativeMatchOptions): AndroidUiNode[];
2315
+ /** The minimal transport the Android analyzer needs: read the UI hierarchy XML. */
2316
+ interface AndroidUiSource {
2317
+ /** Return the current UI hierarchy as uiautomator2 `/source` XML. */
2318
+ source(): Promise<string>;
2319
+ }
2320
+ type AnalyzeAndroidOptions = {
2321
+ /** Package name to report in the result. */
2322
+ app: string;
2323
+ };
2324
+ /**
2325
+ * Analyze an already-launched Android app through `client`, returning its
2326
+ * interactive elements with ranked selectors.
2327
+ *
2328
+ * The caller owns the session lifecycle (launch + guardrails + teardown); this
2329
+ * function is strictly read-only — it only reads the page source.
2330
+ */
2331
+ declare function analyzeAndroidApp(client: AndroidUiSource, options: AnalyzeAndroidOptions): Promise<AndroidAnalysisResult>;
2332
+
2333
+ /**
2334
+ * PROWL-061 — iOS analyzer.
2335
+ *
2336
+ * The iOS analog of {@link analyzeMacApp}: dumps a running iOS app's interactive
2337
+ * elements (and its windows) with ranked selector candidates so hunt authors
2338
+ * don't have to guess accessibility identifiers. It reads the on-simulator UI
2339
+ * hierarchy through the same WebDriverAgent the runner uses (`GET /source`, WDA's
2340
+ * XML page source of `<XCUIElementType…>` elements) and shapes the result to
2341
+ * mirror the macOS analyzer's feel.
2342
+ *
2343
+ * Selector ranking (best → last resort), matching the iOS driver's selector
2344
+ * dialect so the emitted selectors are directly usable in hunts:
2345
+ * id=<accessibility id> (best — the native `data-testid`)
2346
+ * label="<label>" (exact accessibility label)
2347
+ * role=<Type>[name="<text>"] (element type + visible text, substring)
2348
+ * text="<text>" (last resort — label/value substring)
2349
+ *
2350
+ * Identifier caveat: WDA's page source exposes a single `name` attribute that is
2351
+ * the element's `accessibilityIdentifier` when one is set, otherwise its label.
2352
+ * We therefore rank `id=` only when `name` differs from `label`, so the analyzer
2353
+ * does not recommend label-shaped ids. Host-side matching still follows WDA's
2354
+ * `accessibility id` strategy and resolves `id=` against `name`.
2355
+ *
2356
+ * Read-only: this never taps, types, or otherwise mutates the app — it only reads
2357
+ * the page source.
2358
+ */
2359
+
2360
+ /**
2361
+ * Element types treated as interactive. Tuned to the common `XCUIElementType…`
2362
+ * controls; static text and layout containers are intentionally excluded.
2363
+ */
2364
+ declare const IOS_INTERACTIVE_TYPES: ReadonlySet<string>;
2365
+ /** The element type that represents a navigable window/screen surface. */
2366
+ declare const IOS_WINDOW_TYPE = "XCUIElementTypeWindow";
2367
+ /** A single node in the WDA hierarchy (`<XCUIElementType…>` element attributes). */
2368
+ type IosUiNode = {
2369
+ type?: string;
2370
+ name?: string;
2371
+ label?: string;
2372
+ value?: string;
2373
+ enabled?: boolean;
2374
+ visible?: boolean;
2375
+ children: IosUiNode[];
2376
+ };
2377
+ /** An interactive element with ranked selector candidates (best first). */
2378
+ type IosAnalysisElement = {
2379
+ type: string;
2380
+ name?: string;
2381
+ label?: string;
2382
+ value?: string;
2383
+ enabled?: boolean;
2384
+ visible?: boolean;
2385
+ /** Ranked selector candidates, best first. Always at least one entry. */
2386
+ selectors: string[];
2387
+ };
2388
+ /** A top-level window, exposed as a navigable surface with its best selector. */
2389
+ type IosAnalysisWindow = {
2390
+ name?: string;
2391
+ label?: string;
2392
+ /** Best selector candidate for the window. */
2393
+ selector: string;
2394
+ };
2395
+ type IosAnalysisResult = {
2396
+ /** Bundle id of the analyzed app. */
2397
+ app: string;
2398
+ elements: IosAnalysisElement[];
2399
+ windows: IosAnalysisWindow[];
2400
+ };
2401
+ /** Parse a WDA `/source` XML dump into a tree of {@link IosUiNode}. */
2402
+ declare function parseIosHierarchy(xml: string): IosUiNode | null;
2403
+ /**
2404
+ * Ranked selector candidates for a node, best first, via the shared native
2405
+ * selector engine (PROWL-060). The iOS attribute mapping is applied here — `id=`←
2406
+ * accessibility id (the `name` attribute, but only when it differs from the label;
2407
+ * see the caveat above), `label=`←label, `role=`←the short element type, name←
2408
+ * `label ?? value` — then {@link rankNativeSelectors} imposes the shared `id=` >
2409
+ * `label=` > `role=…[name]` > `text=` order (with a bare `role=` fallback).
2410
+ */
2411
+ declare function rankIosSelectors(node: IosUiNode): string[];
2412
+ /**
2413
+ * Project an {@link IosUiNode} into the neutral {@link NativeNode} the shared
2414
+ * matcher compares against: `id`←name (matching WDA's `accessibility id`
2415
+ * strategy even when it equals the label), `label`←label, `role`←the full
2416
+ * element type (the dialect normalizes a `Button` shorthand against it), and both
2417
+ * label and value as `text=` substring sources.
2418
+ */
2419
+ declare function iosNodeToNative(node: IosUiNode): NativeNode;
2420
+ /**
2421
+ * Host-side "snapshot-then-match": parse a WebDriverAgent `/source` dump and
2422
+ * return every node the Prowl `selector` resolves to, in document order, using
2423
+ * iOS's shared dialect semantics. Read-only and device-free. Exposed for tooling
2424
+ * and a future runner/macdriver migration; the runner still matches on-device.
2425
+ */
2426
+ declare function matchIosSelector(xml: string, selector: string): IosUiNode[];
2427
+ /** Whether a node is worth surfacing as an interactive element. */
2428
+ declare function isIosInteractive(node: IosUiNode): boolean;
2429
+ /** The minimal transport the iOS analyzer needs: read the UI hierarchy XML. */
2430
+ interface IosUiSource {
2431
+ /** Return the current UI hierarchy as WebDriverAgent `/source` XML. */
2432
+ source(): Promise<string>;
2433
+ }
2434
+ type AnalyzeIosOptions = {
2435
+ /** Bundle id to report in the result. */
2436
+ app: string;
1958
2437
  };
2438
+ /**
2439
+ * Analyze an already-launched iOS app through `client`, returning its interactive
2440
+ * elements and windows with ranked selectors.
2441
+ *
2442
+ * The caller owns the session lifecycle (launch + guardrails + teardown); this
2443
+ * function is strictly read-only — it only reads the page source.
2444
+ */
2445
+ declare function analyzeIosApp(client: IosUiSource, options: AnalyzeIosOptions): Promise<IosAnalysisResult>;
1959
2446
 
1960
2447
  type GenerateOptions = {
1961
2448
  url?: string;
@@ -1967,4 +2454,4 @@ type GenerateOptions = {
1967
2454
  };
1968
2455
  declare function generateHunt(options: GenerateOptions): Promise<string>;
1969
2456
 
1970
- export { ANDROID_KEYCODES, type AdbDevice, type AdbResult, type AdbRunner, type AdbSpawner, type AgentApks, type AgentConnector, type AnalysisResult, type AnalyzeMacOptions, type AndroidAgentClient, type AndroidDriverOptions, type AndroidLocator, type AndroidQuery, type AndroidSession, type AndroidTarget, type AssertScreenshotStep, type Assertion, type AssertionResult, type BrowserChannel, type BrowserEngine, type BugFailure, type BugLogSummary, type CiFailureCluster, type CiFlakyHunt, type CiHuntResult, type CiResult, type CiStatus, type Config, DEFAULT_AGENT_REQUEST_TIMEOUT_MS, DEFAULT_ANALYZE_TREE_DEPTH, DEFAULT_FLAKY_THRESHOLD, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_SIMULATOR_LOCK_ROOT, DEFAULT_WDA_REQUEST_TIMEOUT_MS, type EvalScriptStep, type FailureCluster, type FetchLike$1 as FetchLike, type FlakyScore, type GenerateOptions, type HealResult, type HistoryEntry, type HistoryFile, type Hunt, INTERACTIVE_ROLES, IOS_PRESS_KEYS, type IfStep, type IosAgentClient, type IosAgentConnector, type IosDriverOptions, type IosLocator, type IosQuery, type IosSession, type IosTarget, type LaunchAndroidOptions, type LaunchIosOptions, type LaunchMacOptions, type MacAnalysisElement, type MacAnalysisResult, type MacAnalysisWindow, type MacAxNode, type MacDriverOptions, type MacHelperClient, type MacQuery, type MacSession, type MacosTarget, type MockRouteStep, type PageElement, type PageForm, type PageLink, type RankFlakyOptions, type ReliabilityConfig, type RepeatStep, type ReserveSimulatorOptions, type ResolveWdaRunnerOptions, type RunArtifacts, type RunOptions, type RunResult, type RunScriptStep, type RunSuiteHooks, type RunSuiteOptions, type RunSuiteResult, type SimDevice, type SimctlResult, type SimctlRunner, type SimulatorReservation, SpawnMacHelperClient, type SpawnMacHelperOptions, type Step, type StepResult, type Target, UIA2_REMOTE_PORT, Uia2HttpError, Uia2Transport, type Uia2TransportOptions, type UnmockRouteStep, type UpdateBacklogOptions, type Viewport, WDA_RUNNER_APP_NAME, WDA_RUNNER_BUNDLE_ID, WEB_ONLY_STEP_TYPES, WdaHttpError, WdaTransport, type WdaTransportOptions, type WebTarget, analyzeMacApp, analyzePage, androidAppAllowedIdentities, androidQueryToLocator, assertAndroidAppAllowed, assertIosAppAllowed, assertStepsSupportedByTarget, assertTargetAppAllowed, bootedDevices, bootedSimulators, buildHealCandidates, closeAndroidSession, closeIosSession, closeMacSession, clusterFailures, computeFlakeScore, configSchema, createAndroidDriver, createIosDriver, createMacDriver, createUia2AgentClient, createUia2Session, createWdaAgentClient, createWdaSession, defaultAgentConnector, defaultIosAgentConnector, escapePredicateArg, escapeUiSelectorArg, extractElementId, extractFailures, extractSelectorIntent, findFreePort, generateHunt, healSelector, huntSchema, interpolateHunt, iosAppAllowedIdentities, iosQueryToLocator, launchAndroidSession, launchIosSession, launchMacSession, listDevices, listHunts, listSimulators, loadConfig, loadHunt, loadHuntMeta, loadHuntTags, macdriverBuildInstructions, normalizeXcuiClassName, parseAaptPackage, parseAdbDevices, parseAndroidSelector, parseForwardPort, parseIosSelector, parseMacSelector, parseSimctlDevices, parseXcodeVersion, rankFlaky, rankMacSelectors, readHistory, readHuntHistory, readIosBundleIdentifier, reserveSimulatorUdid, resolveAgentApks, resolveHelperBinary, resolveWdaProject, resolveWdaRunner, runHunt, runSuite, selectDeviceSerial, selectSimulatorUdid, stepSchema, unwrapAndroidTextSelector, unwrapIosTextSelector, updateBacklogFromSuite, waitForAgentReady, waitForWdaReady, wdaCacheDir, webOnlyReason };
2457
+ export { ANDROID_INTERACTIVE_CLASSES, ANDROID_KEYCODES, ANDROID_MATCH_DIALECT, type AdbDevice, type AdbResult, type AdbRunner, type AdbSpawner, type AgentApks, type AgentConnector, type AnalysisResult, type AnalyzeAndroidOptions, type AnalyzeIosOptions, type AnalyzeMacOptions, type AndroidAgentClient, type AndroidAnalysisElement, type AndroidAnalysisResult, type AndroidDriverOptions, type AndroidLocator, type AndroidQuery, type AndroidSession, type AndroidTarget, type AndroidUiNode, type AndroidUiSource, type AssertScreenshotStep, type Assertion, type AssertionResult, type BrowserChannel, type BrowserEngine, type BugFailure, type BugLogSummary, type CiFailureCluster, type CiFlakyHunt, type CiHuntResult, type CiResult, type CiStatus, type Config, DEFAULT_AGENT_REQUEST_TIMEOUT_MS, DEFAULT_ANALYZE_TREE_DEPTH, DEFAULT_FLAKY_THRESHOLD, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_SIMULATOR_LOCK_ROOT, DEFAULT_WDA_REQUEST_TIMEOUT_MS, type EvalScriptStep, type FailureCluster, type FetchLike$1 as FetchLike, type FlakyScore, type GenerateOptions, type HealResult, type HistoryEntry, type HistoryFile, type Hunt, INTERACTIVE_ROLES, IOS_INTERACTIVE_TYPES, IOS_MATCH_DIALECT, IOS_PRESS_KEYS, IOS_WINDOW_TYPE, type IfStep, type IosAgentClient, type IosAgentConnector, type IosAnalysisElement, type IosAnalysisResult, type IosAnalysisWindow, type IosDriverOptions, type IosLocator, type IosQuery, type IosSession, type IosTarget, type IosUiNode, type IosUiSource, type LaunchAndroidOptions, type LaunchIosOptions, type LaunchMacOptions, MACOS_MATCH_DIALECT, type MacAnalysisElement, type MacAnalysisResult, type MacAnalysisWindow, type MacAxNode, type MacDriverOptions, type MacHelperClient, type MacQuery, type MacSession, type MacosTarget, type MockRouteStep, NATIVE_ATTRIBUTE_MAP, type NativeMatchDialect, type NativeMatchOptions, type NativeNode, type NativePlatform, type NativeRankFields, type NativeSelector, type NativeSelectorKind, type PageElement, type PageForm, type PageLink, type RankFlakyOptions, type ReliabilityConfig, type RepeatStep, type ReserveSimulatorOptions, type ResolveWdaTestRunOptions, type RunArtifacts, type RunOptions, type RunResult, type RunScriptStep, type RunSuiteHooks, type RunSuiteOptions, type RunSuiteResult, type SelectorKindMapping, type SimDevice, type SimctlResult, type SimctlRunner, type SimulatorReservation, SpawnMacHelperClient, type SpawnMacHelperOptions, type Step, type StepResult, type Target, UIA2_REMOTE_PORT, Uia2HttpError, Uia2Transport, type Uia2TransportOptions, type UnmockRouteStep, type UpdateBacklogOptions, type Viewport, WDA_RUNNER_BUNDLE_ID, WDA_USE_PORT_ENV, WEB_ONLY_STEP_TYPES, WdaHttpError, type WdaTestRunPreparer, WdaTransport, type WdaTransportOptions, type WebTarget, type XmlElement, analyzeAndroidApp, analyzeIosApp, analyzeMacApp, analyzePage, androidAppAllowedIdentities, androidNodeToNative, androidQueryToLocator, assertAndroidAppAllowed, assertIosAppAllowed, assertStepsSupportedByTarget, assertTargetAppAllowed, bootedDevices, bootedSimulators, buildHealCandidates, closeAndroidSession, closeIosSession, closeMacSession, clusterFailures, computeFlakeScore, configSchema, createAndroidDriver, createIosDriver, createMacDriver, createUia2AgentClient, createUia2Session, createWdaAgentClient, createWdaSession, decodeXmlEntities, defaultAgentConnector, defaultIosAgentConnector, defaultWdaTestRunPreparer, escapePredicateArg, escapeUiSelectorArg, extractElementId, extractFailures, extractSelectorIntent, findFreePort, generateHunt, healSelector, huntSchema, injectUsePortIntoXctestrun, interpolateHunt, iosAppAllowedIdentities, iosNodeToNative, iosQueryToLocator, isAndroidInteractive, isIosInteractive, launchAndroidSession, launchIosSession, launchMacSession, listDevices, listHunts, listSimulators, loadConfig, loadHunt, loadHuntMeta, loadHuntTags, macdriverBuildInstructions, matchAndroidSelector, matchIosSelector, matchNativeTree, nodeMatchesSelector, normalizeXcuiClassName, parseAaptPackage, parseAdbDevices, parseAndroidHierarchy, parseAndroidSelector, parseForwardPort, parseIosHierarchy, parseIosSelector, parseMacSelector, parseNativeSelector, parseSimctlDevices, parseSnapshot, parseXcodeVersion, parseXml, qualifyResourceId, quoteSelectorValue, rankAndroidSelectors, rankFlaky, rankIosSelectors, rankMacSelectors, rankNativeSelectors, readHistory, readHuntHistory, readIosBundleIdentifier, reserveSimulatorUdid, resolveAgentApks, resolveHelperBinary, resolveWdaProject, resolveWdaTestRun, runHunt, runSuite, selectDeviceSerial, selectSimulatorUdid, shortIosType, stepSchema, unquoteSelectorValue, unwrapAndroidTextSelector, unwrapIosTextSelector, unwrapNativeTextSelector, updateBacklogFromSuite, waitForAgentReady, waitForWdaReady, wdaCacheDir, wdaTestRunArgs, webOnlyReason };