swipium 1.1.0 → 1.3.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.
Files changed (109) hide show
  1. package/README.md +14 -9
  2. package/THREAT_MODEL.md +59 -0
  3. package/dist/appMap/diff.js +60 -0
  4. package/dist/appMap/diff.js.map +1 -0
  5. package/dist/appMap/featureIndex.js +183 -0
  6. package/dist/appMap/featureIndex.js.map +1 -0
  7. package/dist/automation/capabilities.js +167 -0
  8. package/dist/automation/capabilities.js.map +1 -0
  9. package/dist/automation/gestures.js +94 -0
  10. package/dist/automation/gestures.js.map +1 -0
  11. package/dist/automation/maestroIr.js +282 -0
  12. package/dist/automation/maestroIr.js.map +1 -0
  13. package/dist/automation/plan.js +268 -0
  14. package/dist/automation/plan.js.map +1 -0
  15. package/dist/automation/report.js +83 -0
  16. package/dist/automation/report.js.map +1 -0
  17. package/dist/automation/selectors.js +201 -0
  18. package/dist/automation/selectors.js.map +1 -0
  19. package/dist/automation/types.js +13 -0
  20. package/dist/automation/types.js.map +1 -0
  21. package/dist/automation/waits.js +91 -0
  22. package/dist/automation/waits.js.map +1 -0
  23. package/dist/automation/webview.js +81 -0
  24. package/dist/automation/webview.js.map +1 -0
  25. package/dist/featureTesting/executionBootstrap.js +14 -10
  26. package/dist/featureTesting/executionBootstrap.js.map +1 -1
  27. package/dist/featureTesting/featureMap.js +53 -0
  28. package/dist/featureTesting/featureMap.js.map +1 -0
  29. package/dist/featureTesting/featureScope.js +354 -0
  30. package/dist/featureTesting/featureScope.js.map +1 -0
  31. package/dist/featureTesting/mapFeatureScope.js +143 -0
  32. package/dist/featureTesting/mapFeatureScope.js.map +1 -0
  33. package/dist/featureTesting/objectiveModel.js +129 -0
  34. package/dist/featureTesting/objectiveModel.js.map +1 -0
  35. package/dist/featureTesting/resultMerge.js +112 -0
  36. package/dist/featureTesting/resultMerge.js.map +1 -0
  37. package/dist/featureTesting/sources.js +96 -0
  38. package/dist/featureTesting/sources.js.map +1 -0
  39. package/dist/featureTesting/suiteBridge.js +51 -0
  40. package/dist/featureTesting/suiteBridge.js.map +1 -0
  41. package/dist/featureTesting/synonyms.js +73 -0
  42. package/dist/featureTesting/synonyms.js.map +1 -0
  43. package/dist/featureTesting/testCaseFactory.js +107 -0
  44. package/dist/featureTesting/testCaseFactory.js.map +1 -0
  45. package/dist/featureTesting/testPlan.js +50 -0
  46. package/dist/featureTesting/testPlan.js.map +1 -0
  47. package/dist/flows/repair.js +363 -0
  48. package/dist/flows/repair.js.map +1 -0
  49. package/dist/interop/maestro.js +151 -0
  50. package/dist/interop/maestro.js.map +1 -0
  51. package/dist/mobileAudit/checks.js +251 -0
  52. package/dist/mobileAudit/checks.js.map +1 -0
  53. package/dist/mobileAudit/evidence.js +49 -0
  54. package/dist/mobileAudit/evidence.js.map +1 -0
  55. package/dist/mobileAudit/profiles.js +73 -0
  56. package/dist/mobileAudit/profiles.js.map +1 -0
  57. package/dist/mobileAudit/results.js +41 -0
  58. package/dist/mobileAudit/results.js.map +1 -0
  59. package/dist/mobileAudit/runner.js +229 -0
  60. package/dist/mobileAudit/runner.js.map +1 -0
  61. package/dist/oracle/locator.js +177 -0
  62. package/dist/oracle/locator.js.map +1 -0
  63. package/dist/server.js +31 -0
  64. package/dist/server.js.map +1 -1
  65. package/dist/testSuite/exporter.js +70 -0
  66. package/dist/testSuite/exporter.js.map +1 -0
  67. package/dist/testSuite/lint.js +53 -0
  68. package/dist/testSuite/lint.js.map +1 -0
  69. package/dist/tools/build.js +172 -0
  70. package/dist/tools/build.js.map +1 -0
  71. package/dist/tools/bundletool.js +145 -0
  72. package/dist/tools/bundletool.js.map +1 -0
  73. package/dist/tools/capabilities.js +66 -4
  74. package/dist/tools/capabilities.js.map +1 -1
  75. package/dist/tools/featureTesting.js +344 -0
  76. package/dist/tools/featureTesting.js.map +1 -0
  77. package/dist/tools/flow.js +67 -1
  78. package/dist/tools/flow.js.map +1 -1
  79. package/dist/tools/flowRepair.js +66 -0
  80. package/dist/tools/flowRepair.js.map +1 -0
  81. package/dist/tools/idling.js +104 -0
  82. package/dist/tools/idling.js.map +1 -0
  83. package/dist/tools/inputCapabilities.js +30 -0
  84. package/dist/tools/inputCapabilities.js.map +1 -0
  85. package/dist/tools/issues.js +218 -0
  86. package/dist/tools/issues.js.map +1 -0
  87. package/dist/tools/jobs.js +12 -1
  88. package/dist/tools/jobs.js.map +1 -1
  89. package/dist/tools/locator.js +57 -0
  90. package/dist/tools/locator.js.map +1 -0
  91. package/dist/tools/maestro.js +78 -0
  92. package/dist/tools/maestro.js.map +1 -0
  93. package/dist/tools/mobileAudit.js +115 -0
  94. package/dist/tools/mobileAudit.js.map +1 -0
  95. package/dist/tools/resolveArtifact.js +80 -0
  96. package/dist/tools/resolveArtifact.js.map +1 -0
  97. package/dist/tools/resolveTarget.js +67 -0
  98. package/dist/tools/resolveTarget.js.map +1 -0
  99. package/dist/tools/suite.js +49 -1
  100. package/dist/tools/suite.js.map +1 -1
  101. package/dist/tools/testSuite.js +233 -0
  102. package/dist/tools/testSuite.js.map +1 -0
  103. package/dist/tools/wait.js +58 -0
  104. package/dist/tools/wait.js.map +1 -0
  105. package/dist/version.js +41 -2
  106. package/dist/version.js.map +1 -1
  107. package/docs/mcp-server.md +1 -1
  108. package/docs/tools.md +86 -3
  109. package/package.json +3 -2
package/README.md CHANGED
@@ -236,27 +236,32 @@ After setup, verify that the client lists `qa_test_this`, `qa_capabilities`, and
236
236
 
237
237
  ## Tool Docs
238
238
 
239
- Swipium exposes 59 public MCP tools. Start with `qa_test_this` for low-context requests.
239
+ Swipium exposes 91 public MCP tools. Start with `qa_test_this` for low-context requests.
240
240
 
241
241
  Full reference: [docs/tools.md](docs/tools.md)
242
242
 
243
- New in 1.1.0 — 17 additional tools, all backward compatible:
243
+ New in 1.3.0 — 8 additional tools, all backward compatible:
244
244
 
245
- - **Device and app environment parity** (no raw `adb`/`simctl`): `qa_device_info`, `qa_permissions`, `qa_orientation`, `qa_geolocation`, `qa_network`, `qa_metro`, `qa_app_control`, `qa_screen_info`, `qa_screen_record`. Mutating actions are consent-gated and recorded; network changes auto-restore at report end.
246
- - **Local-first visual intelligence**: `qa_visual` (baseline, regression diff, image-target matching with tappable coordinates, optional OCR) and `qa_visual_find_text` (OCR text location with coordinate-space conversion).
247
- - **Seeded state**: `qa_seed`, `qa_state_prepare`, `qa_state_verify`, `qa_state_teardown` — create and verify a reproducible precondition instead of only reporting it as missing.
248
- - **Report history**: `qa_report_compare` (diff a run against a baseline) and `qa_run_history` (pass rate, failures, flaky flows, confidence calibration across local runs).
245
+ - **Feature-focused testing**: `qa_feature_scope`, `qa_feature_test_plan`, `qa_test_feature` — test a specific feature by name ("the login flow"), backed by the app knowledge map.
246
+ - **Local build and artifact resolution**: `qa_resolve_target`, `qa_resolve_artifact`, `qa_build_plan`, `qa_build`, `qa_bundletool` — pick a device, find or build an installable artifact, and convert an `.aab` to an installable APK.
247
+
248
+ Earlier releases added device-parity, local-first visual, seeded-state, report-history (1.1.0) and durable issue memory, persistent test suite, flow plan/repair, Maestro interop, and agent helpers (1.2.0).
249
249
 
250
250
  | Group | Tools |
251
251
  | --- | --- |
252
- | Start | `qa_agent_brief`, `qa_capabilities`, `qa_test_this`, `qa_job_status`, `qa_status`, `qa_explain_blocker`, `qa_continue_from_blocker`, `qa_get_artifact` |
252
+ | Start | `qa_agent_brief`, `qa_capabilities`, `qa_test_this`, `qa_job_status`, `qa_job_cancel`, `qa_status`, `qa_explain_blocker`, `qa_continue_from_blocker`, `qa_get_artifact` |
253
253
  | Setup | `qa_doctor`, `qa_start_session`, `qa_detect_context`, `qa_plan`, `qa_prepare_target`, `qa_prepare_ios_target`, `qa_ios`, `qa_wda` |
254
+ | Build | `qa_resolve_target`, `qa_resolve_artifact`, `qa_build_plan`, `qa_build`, `qa_bundletool` |
254
255
  | Device | `qa_device_info`, `qa_permissions`, `qa_orientation`, `qa_geolocation`, `qa_network`, `qa_metro`, `qa_app_control`, `qa_screen_info`, `qa_screen_record` |
255
- | Drive | `qa_snapshot`, `qa_act`, `qa_clear_overlay`, `qa_check_health`, `qa_screenshot`, `qa_note`, `qa_assert_visual`, `qa_visual`, `qa_visual_find_text` |
256
+ | Drive | `qa_snapshot`, `qa_act`, `qa_clear_overlay`, `qa_check_health`, `qa_screenshot`, `qa_note`, `qa_assert_visual`, `qa_visual`, `qa_visual_find_text`, `qa_locator_suggest`, `qa_input_capabilities`, `qa_wait`, `qa_idling_status` |
256
257
  | State | `qa_seed`, `qa_state_prepare`, `qa_state_verify`, `qa_state_teardown` |
257
258
  | Run | `qa_smoke`, `qa_explore`, `qa_report`, `qa_report_compare`, `qa_run_history` |
258
259
  | App map | `qa_app_map_build`, `qa_app_map_read`, `qa_app_map_query`, `qa_app_map_feature_scope`, `qa_app_map_validate` |
259
- | Flows and suites | `qa_flow_check`, `qa_flow_run`, `qa_flow_generate`, `qa_suite_generate`, `qa_suite_compile`, `qa_testcase_generate` |
260
+ | Feature | `qa_feature_scope`, `qa_feature_test_plan`, `qa_test_feature` |
261
+ | Flows and suites | `qa_flow_check`, `qa_flow_plan`, `qa_flow_run`, `qa_flow_generate`, `qa_flow_repair`, `qa_suite_generate`, `qa_suite_compile`, `qa_suite_lint`, `qa_pom_generate`, `qa_testcase_generate` |
262
+ | Test suite | `qa_test_suite_read`, `qa_test_suite_update`, `qa_test_suite_generate`, `qa_test_suite_export`, `qa_test_suite_lint` |
263
+ | Interop | `qa_maestro_import`, `qa_maestro_export` |
264
+ | Issues | `qa_issue_log`, `qa_issue_history`, `qa_issue_mark_fixed`, `qa_issue_triage`, `qa_issue_suppress`, `qa_issue_verify_fixed`, `qa_issue_metrics`, `qa_mobile_audit` |
260
265
  | First run | `qa_first_run_plan`, `qa_first_run_continue` |
261
266
  | Automation | `qa_automation_plan`, `qa_automation_generate`, `qa_automation_validate` |
262
267
 
@@ -0,0 +1,59 @@
1
+ # Swipium Threat Model
2
+
3
+ Last updated: 2026-06-02 (Swipium 2.1.0)
4
+
5
+ This document describes Swipium's trust boundaries, the threats it defends against, and the controls that enforce those defenses. It follows the MCP security guidance: validate inputs, use least privilege, obtain explicit consent for sensitive operations, protect secrets, and do not over-trust tool metadata.
6
+
7
+ ## What Swipium Is
8
+
9
+ Swipium is a local stdio MCP server that lets an AI agent run mobile QA against an Android Emulator or iOS Simulator on the developer's own machine. It is not a remote service. It has no network listener, no authentication surface, and no multi-tenant state.
10
+
11
+ ## Trust Boundaries
12
+
13
+ 1. MCP client to Swipium: the client (Claude, Gemini, Codex, or another MCP host) sends tool calls over stdio. Swipium trusts the transport but not the semantic intent: destructive actions require explicit, server-side consent regardless of what the client requests.
14
+ 2. Swipium to local toolchain: Swipium shells out to `adb`, `emulator`, `xcrun`/`simctl`, Gradle, and Metro. These run with the developer's own privileges. Swipium does not escalate privileges.
15
+ 3. Swipium to the project: Swipium reads and writes within the resolved project root and `.swipium/`. It does not write outside the project root without explicit approval.
16
+ 4. App under test to Swipium: screenshots, UI dumps, and logs from the app can contain sensitive data. Swipium treats this data as untrusted and redacts known secret shapes before surfacing it.
17
+
18
+ ## Assets
19
+
20
+ - Developer machine integrity and the local toolchain.
21
+ - The project source tree and `.swipium/` state (config, app map, flows, issue ledger, run history).
22
+ - Secrets the agent handles during testing (credentials, OTPs, tokens).
23
+ - App data on the emulator/simulator.
24
+
25
+ ## Adversaries and Threats
26
+
27
+ - Malicious or compromised MCP client / prompt injection: a client (or a poisoned tool description in another server) tries to trigger a destructive or exfiltrating action. Mitigation: server-side consent state machine for all destructive/privileged actions; exact command and effect are shown before execution; consent is per-action and not satisfiable by client assertion alone. Tool descriptions are linted (`test/toolMetadata.test.ts`) to stay honest and non-manipulative.
28
+ - Untrusted app content: the app renders attacker-controlled text (deep links, usernames, server responses) that could carry injection payloads into the transcript. Mitigation: snapshots and logs are structured and redacted; Swipium does not execute app-derived text as commands.
29
+ - Accidental data loss: a data wipe (`clear_data`, `fresh_start`) on a debug RN/Expo build removes the cached JS bundle and breaks the app. Mitigation: these actions are consent-gated high and additionally require `acknowledgeBundleRisk:true`; `qa_plan` surfaces `fresh_start` as UNSAFE with reason `bundle_cache_loss`.
30
+ - Environment left dirty: a run changes network state or leaves a recorder or Metro bundler running. Mitigation: network changes record the original state and auto-restore at report end, on `restore`, and on server shutdown; screen recordings and Metro bundlers are stopped on shutdown.
31
+ - Secret leakage into artifacts/reports: credentials or tokens entered during testing get written to logs, dumps, or reports. Mitigation: secret values provided via `qa_continue_from_blocker` are registered for redaction; known secret shapes are redacted from snapshots and reports; sensitive mode withholds screenshots and visual OCR on password/OTP screens.
32
+ - Sensitive-screen capture: screen recording or OCR captures a password/payment screen. Mitigation: recording and visual OCR are consent-gated, refuse sensitive sessions, and visual text/diff ops are withheld when a secure field is on screen unless explicitly forced.
33
+ - Untrusted seed/fixture execution: a fixture seed runs a local script or API call. Mitigation: all seed/state mutations are consent-gated with risk scaled by type (`script` high), git commands are refused, and seed failures are reported as setup failures, not app bugs.
34
+ - Stale client after upgrade: an MCP client keeps an old server process after an upgrade, exposing a stale tool surface. Mitigation: version and tool-count are reported on start and by `qa_doctor`; a stale-client hint is shown.
35
+
36
+ ## Controls Summary
37
+
38
+ - Server-side consent state machine with exact command/effect display and project-root boundary.
39
+ - Destructive-action guardrails (bundle-loss refusal) that the client cannot override.
40
+ - Secret redaction and sensitive-screen mode.
41
+ - Network restore and recorder/Metro shutdown hooks.
42
+ - External artifact handling within the project root; no writes outside without approval.
43
+ - Tool metadata lint and public tool-surface lockstep tests.
44
+ - Coordinate-space metadata on all visual results.
45
+
46
+ ## Explicit Non-Goals (Current Scope)
47
+
48
+ Swipium is simulator-local. The following are intentionally out of scope until a dedicated design and threat model exist for each:
49
+
50
+ - Remote or HTTP transport, authentication, and multi-tenant operation.
51
+ - Real-device execution.
52
+ - External service integrations (for example issue trackers or CI back-ends that send data off the machine).
53
+ - Remote AI vision that sends screenshots to a third-party service by default.
54
+
55
+ Adding any of these requires extending this document first.
56
+
57
+ ## Reporting
58
+
59
+ Security issues should be reported per `SECURITY.md`.
@@ -0,0 +1,60 @@
1
+ // Map diff (SWIPIUM-REQ-01 qa_app_map_diff). Reports added/removed/changed screens, feature
2
+ // coverage changes, stale test cases, locator-readiness changes, and new untested code areas
3
+ // between two AppKnowledgeMap snapshots. Pure — the tool layer resolves which two maps to compare.
4
+ function ids(arr) {
5
+ return new Set(arr.map((a) => a.id));
6
+ }
7
+ export function diffAppMaps(baseline, current) {
8
+ const baseStatic = ids(baseline.staticTopology.screens);
9
+ const curStatic = ids(current.staticTopology.screens);
10
+ const baseRuntime = ids(baseline.runtimeTopology.screens);
11
+ const curRuntime = ids(current.runtimeTopology.screens);
12
+ const staticAdded = [...curStatic].filter((id) => !baseStatic.has(id));
13
+ const staticRemoved = [...baseStatic].filter((id) => !curStatic.has(id));
14
+ const runtimeAdded = [...curRuntime].filter((id) => !baseRuntime.has(id));
15
+ const runtimeRemoved = [...baseRuntime].filter((id) => !curRuntime.has(id));
16
+ // changed runtime screens: visits / link / readiness drift
17
+ const changedScreens = [];
18
+ const locatorReadinessChanges = [];
19
+ for (const cur of current.runtimeTopology.screens) {
20
+ const prev = baseline.runtimeTopology.screens.find((s) => s.id === cur.id || s.signature === cur.signature);
21
+ if (!prev)
22
+ continue;
23
+ if (prev.linkedStaticScreenId !== cur.linkedStaticScreenId) {
24
+ changedScreens.push({ id: cur.id, field: 'linkedStaticScreenId', was: prev.linkedStaticScreenId ?? null, now: cur.linkedStaticScreenId ?? null });
25
+ }
26
+ if (prev.locatorReadiness !== cur.locatorReadiness) {
27
+ locatorReadinessChanges.push({ id: cur.id, was: prev.locatorReadiness, now: cur.locatorReadiness });
28
+ }
29
+ }
30
+ // feature coverage changes
31
+ const featureCoverageChanges = [];
32
+ for (const cur of current.features) {
33
+ const prev = baseline.features.find((f) => f.id === cur.id);
34
+ if (prev && prev.testCoverage !== cur.testCoverage) {
35
+ featureCoverageChanges.push({ featureId: cur.id, was: prev.testCoverage, now: cur.testCoverage });
36
+ }
37
+ }
38
+ // stale tests: cases whose feature/screen no longer exists in current
39
+ const curFeatureIds = ids(current.features);
40
+ const staleTests = current.testSuite.cases
41
+ .filter((c) => (c.featureId && !curFeatureIds.has(c.featureId)) || (c.screenId && !curStatic.has(c.screenId) && !curRuntime.has(c.screenId)) || c.stale)
42
+ .map((c) => c.id);
43
+ // new untested code areas: newly added static screens with no linked runtime screen
44
+ const linkedStatic = new Set(current.runtimeTopology.screens.map((r) => r.linkedStaticScreenId).filter(Boolean));
45
+ const newUntestedCodeAreas = staticAdded.filter((id) => !linkedStatic.has(id));
46
+ const summary = `+${staticAdded.length}/-${staticRemoved.length} static screens, +${runtimeAdded.length}/-${runtimeRemoved.length} runtime screens; ` +
47
+ `${featureCoverageChanges.length} coverage change(s), ${locatorReadinessChanges.length} locator-readiness change(s), ` +
48
+ `${staleTests.length} stale test(s), ${newUntestedCodeAreas.length} new untested area(s)`;
49
+ return {
50
+ staticScreens: { added: staticAdded, removed: staticRemoved },
51
+ runtimeScreens: { added: runtimeAdded, removed: runtimeRemoved },
52
+ changedScreens,
53
+ featureCoverageChanges,
54
+ locatorReadinessChanges,
55
+ staleTests,
56
+ newUntestedCodeAreas,
57
+ summary,
58
+ };
59
+ }
60
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/appMap/diff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,183 @@
1
+ // Code-aware feature index (SWIPIUM-REQ-03, foundation from SWIPIUM-REQ-01). A deterministic,
2
+ // LLM-free static index over the project's source so a natural-language feature request can be
3
+ // mapped to concrete code: screen/component/service/hook symbols, navigation route names, and
4
+ // per-file token sets. Pure logic lives in tokenize/extractSymbols/extractRoutes (unit-tested
5
+ // without a filesystem); buildFeatureIndex walks the tree and applies them.
6
+ //
7
+ // v1 is intentionally deterministic (Non-Goals): regex + name heuristics, not semantic analysis.
8
+ // The schema carries tokens so synonym/locale expansion can happen at query time later.
9
+ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
10
+ import { join, relative, extname, basename } from 'node:path';
11
+ const SOURCE_EXT = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.swift', '.kt', '.java', '.dart', '.vue']);
12
+ const SKIP_DIRS = new Set([
13
+ 'node_modules', '.git', '.svn', '.hg', 'dist', 'build', 'out', 'coverage', '.next', '.expo',
14
+ '.gradle', 'Pods', 'DerivedData', 'vendor', '.swipium', '__snapshots__', '.cache', 'tmp', '.idea', '.vscode',
15
+ ]);
16
+ /** Split an identifier (camelCase / PascalCase / snake_case / kebab-case) into lowercase word tokens. */
17
+ export function tokenize(name) {
18
+ return name
19
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2') // camelCase boundary
20
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2') // ACRONYMWord boundary
21
+ .split(/[^A-Za-z0-9]+/)
22
+ .map((t) => t.trim().toLowerCase())
23
+ .filter((t) => t.length > 1 && !/^\d+$/.test(t));
24
+ }
25
+ /** Classify a symbol by conventional name suffixes/prefixes — deterministic, English-leaning v1. */
26
+ export function classifySymbol(name) {
27
+ if (/^use[A-Z]/.test(name))
28
+ return 'hook';
29
+ if (/(Screen|Page|View|Activity|Fragment|Route)$/.test(name))
30
+ return 'screen';
31
+ if (/(Service|Api|Client|Repository|Store|Manager|Provider|Controller|Analyzer|Engine)$/.test(name))
32
+ return 'service';
33
+ if (/(Component|Card|Button|List|Modal|Sheet|Header|Footer|Item|Row|Tile|Widget)$/.test(name))
34
+ return 'component';
35
+ if (/^[A-Z]/.test(name))
36
+ return 'component'; // PascalCase default → likely a component/class
37
+ return 'function';
38
+ }
39
+ /** Extract declared symbol names from a source file's text (JS/TS/Swift/Kotlin/Java/Dart). PURE. */
40
+ export function extractSymbols(text) {
41
+ const out = [];
42
+ const seen = new Set();
43
+ const lines = text.split('\n');
44
+ const patterns = [
45
+ // JS/TS declarations
46
+ /(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)/,
47
+ /(?:export\s+)?(?:abstract\s+)?class\s+([A-Za-z_$][\w$]*)/,
48
+ /(?:export\s+)?const\s+([A-Za-z_$][\w$]*)\s*(?::[^=]+)?=\s*(?:async\s*)?(?:\(|function|React\.|memo\(|forwardRef\(|styled)/,
49
+ /(?:export\s+)?(?:interface|type|enum)\s+([A-Za-z_$][\w$]*)/,
50
+ // Swift / Kotlin / Java
51
+ /(?:struct|protocol|extension)\s+([A-Z][\w]*)/,
52
+ /(?:fun|func)\s+([A-Za-z_][\w]*)/,
53
+ // Dart / Kotlin widgets
54
+ /(?:Widget|StatelessWidget|StatefulWidget)\s+([A-Z][\w]*)/,
55
+ ];
56
+ for (let i = 0; i < lines.length; i++) {
57
+ const line = lines[i];
58
+ for (const re of patterns) {
59
+ const m = re.exec(line);
60
+ if (!m)
61
+ continue;
62
+ const name = m[1];
63
+ const key = `${name}@${i}`;
64
+ if (seen.has(key))
65
+ continue;
66
+ seen.add(key);
67
+ out.push({ name, kind: classifySymbol(name), line: i + 1 });
68
+ }
69
+ }
70
+ return out;
71
+ }
72
+ /** Extract navigation route / deep-link names from a source file's text. PURE. */
73
+ export function extractRoutes(text) {
74
+ const out = [];
75
+ const seen = new Set();
76
+ const lines = text.split('\n');
77
+ const patterns = [
78
+ /\bpath\s*:\s*['"`]([^'"`]+)['"`]/g, // react-router / vue-router { path: '/weather' }
79
+ /\b(?:name|routeName|screen)\s*:\s*['"`]([A-Za-z][^'"`]*)['"`]/g, // { name: 'WeatherAnalysis' }
80
+ /<(?:Stack|Tab|Drawer|NativeStack)?\.?Screen\b[^>]*\bname\s*=\s*['"`]([^'"`]+)['"`]/g, // RN <Stack.Screen name="Weather" />
81
+ /navigation\.(?:navigate|push|replace)\(\s*['"`]([^'"`]+)['"`]/g, // navigation.navigate('Weather')
82
+ /(?:Link|NavLink|router\.push)\(?\s*[^'"`]*['"`](\/[A-Za-z][^'"`]*)['"`]/g, // router.push('/weather')
83
+ /['"`]([a-z][\w.+-]*:\/\/[^'"`\s]+)['"`]/g, // deep links myapp://weather
84
+ ];
85
+ for (let i = 0; i < lines.length; i++) {
86
+ for (const re of patterns) {
87
+ re.lastIndex = 0;
88
+ let m;
89
+ while ((m = re.exec(lines[i]))) {
90
+ const route = m[1];
91
+ if (!route || route.length > 80)
92
+ continue;
93
+ const key = `${route}@${i}`;
94
+ if (seen.has(key))
95
+ continue;
96
+ seen.add(key);
97
+ out.push({ route, line: i + 1 });
98
+ }
99
+ }
100
+ }
101
+ return out;
102
+ }
103
+ /** Tokenize a route string into searchable terms (drops scheme/slashes/params). */
104
+ function routeTokens(route) {
105
+ return tokenize(route.replace(/^[a-z]+:\/\//i, '').replace(/[/:?#&=]/g, ' '));
106
+ }
107
+ /**
108
+ * Walk the project tree and build the static feature index. Reads files (best-effort) and applies
109
+ * the pure extractors. Skips build/vendor dirs and caps the file count to keep context bounded.
110
+ */
111
+ export function buildFeatureIndex(root, opts = {}) {
112
+ const maxFiles = opts.maxFiles ?? 4000;
113
+ const maxBytes = opts.maxBytesPerFile ?? 512 * 1024;
114
+ const symbols = [];
115
+ const routes = [];
116
+ const files = [];
117
+ let scannedFiles = 0;
118
+ let truncated = false;
119
+ const walk = (dir) => {
120
+ if (scannedFiles >= maxFiles) {
121
+ truncated = true;
122
+ return;
123
+ }
124
+ let entries;
125
+ try {
126
+ entries = readdirSync(dir).sort();
127
+ }
128
+ catch {
129
+ return;
130
+ }
131
+ for (const name of entries) {
132
+ if (scannedFiles >= maxFiles) {
133
+ truncated = true;
134
+ return;
135
+ }
136
+ if (name.startsWith('.') && name !== '.')
137
+ continue;
138
+ const full = join(dir, name);
139
+ let st;
140
+ try {
141
+ st = statSync(full);
142
+ }
143
+ catch {
144
+ continue;
145
+ }
146
+ if (st.isDirectory()) {
147
+ if (SKIP_DIRS.has(name))
148
+ continue;
149
+ walk(full);
150
+ }
151
+ else if (st.isFile()) {
152
+ const ext = extname(name).toLowerCase();
153
+ if (!SOURCE_EXT.has(ext))
154
+ continue;
155
+ if (/\.(d\.ts|test\.[tj]sx?|spec\.[tj]sx?)$/.test(name))
156
+ continue; // skip declarations + tests
157
+ if (st.size > maxBytes)
158
+ continue;
159
+ const rel = relative(root, full);
160
+ const base = basename(name, ext);
161
+ files.push({ file: rel, base, tokens: tokenize(base) });
162
+ scannedFiles++;
163
+ let text;
164
+ try {
165
+ text = readFileSync(full, 'utf8');
166
+ }
167
+ catch {
168
+ continue;
169
+ }
170
+ for (const s of extractSymbols(text)) {
171
+ symbols.push({ name: s.name, kind: s.kind, file: rel, line: s.line, tokens: tokenize(s.name) });
172
+ }
173
+ for (const r of extractRoutes(text)) {
174
+ routes.push({ route: r.route, file: rel, line: r.line, tokens: routeTokens(r.route) });
175
+ }
176
+ }
177
+ }
178
+ };
179
+ if (existsSync(root))
180
+ walk(root);
181
+ return { root, symbols, routes, files, scannedFiles, truncated };
182
+ }
183
+ //# sourceMappingURL=featureIndex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"featureIndex.js","sourceRoot":"","sources":["../../src/appMap/featureIndex.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,+FAA+F;AAC/F,8FAA8F;AAC9F,8FAA8F;AAC9F,4EAA4E;AAC5E,EAAE;AACF,iGAAiG;AACjG,wFAAwF;AAExF,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAkC9D,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;AACtH,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC;IACxB,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO;IAC3F,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS;CAC7G,CAAC,CAAC;AAEH,yGAAyG;AACzG,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,OAAO,IAAI;SACR,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC,qBAAqB;SAC5D,OAAO,CAAC,uBAAuB,EAAE,OAAO,CAAC,CAAC,uBAAuB;SACjE,KAAK,CAAC,eAAe,CAAC;SACtB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;SAClC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AACrD,CAAC;AAED,oGAAoG;AACpG,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,MAAM,CAAC;IAC1C,IAAI,6CAA6C,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC9E,IAAI,oFAAoF,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACtH,IAAI,8EAA8E,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,WAAW,CAAC;IAClH,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,WAAW,CAAC,CAAC,gDAAgD;IAC7F,OAAO,UAAU,CAAC;AACpB,CAAC;AAOD,oGAAoG;AACpG,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,GAAG,GAA4D,EAAE,CAAC;IACxE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,QAAQ,GAAa;QACzB,qBAAqB;QACrB,yEAAyE;QACzE,0DAA0D;QAC1D,2HAA2H;QAC3H,4DAA4D;QAC5D,wBAAwB;QACxB,8CAA8C;QAC9C,iCAAiC;QACjC,wBAAwB;QACxB,0DAA0D;KAC3D,CAAC;IACF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxB,IAAI,CAAC,CAAC;gBAAE,SAAS;YACjB,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAClB,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC;YAC3B,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS;YAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACd,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,GAAG,GAA2C,EAAE,CAAC;IACvD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,QAAQ,GAAa;QACzB,mCAAmC,EAAE,iDAAiD;QACtF,gEAAgE,EAAE,8BAA8B;QAChG,qFAAqF,EAAE,qCAAqC;QAC5H,gEAAgE,EAAE,iCAAiC;QACnG,0EAA0E,EAAE,0BAA0B;QACtG,0CAA0C,EAAE,6BAA6B;KAC1E,CAAC;IACF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,EAAE,CAAC,SAAS,GAAG,CAAC,CAAC;YACjB,IAAI,CAAyB,CAAC;YAC9B,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC/B,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;gBACnB,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE;oBAAE,SAAS;gBAC1C,MAAM,GAAG,GAAG,GAAG,KAAK,IAAI,CAAC,EAAE,CAAC;gBAC5B,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;oBAAE,SAAS;gBAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACd,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,mFAAmF;AACnF,SAAS,WAAW,CAAC,KAAa;IAChC,OAAO,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC;AAChF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,OAAwD,EAAE;IACxG,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC;IACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,IAAI,GAAG,GAAG,IAAI,CAAC;IACpD,MAAM,OAAO,GAAmB,EAAE,CAAC;IACnC,MAAM,MAAM,GAAe,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAsB,EAAE,CAAC;IACpC,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,SAAS,GAAG,KAAK,CAAC;IAEtB,MAAM,IAAI,GAAG,CAAC,GAAW,EAAQ,EAAE;QACjC,IAAI,YAAY,IAAI,QAAQ,EAAE,CAAC;YAC7B,SAAS,GAAG,IAAI,CAAC;YACjB,OAAO;QACT,CAAC;QACD,IAAI,OAAiB,CAAC;QACtB,IAAI,CAAC;YACH,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;YAC3B,IAAI,YAAY,IAAI,QAAQ,EAAE,CAAC;gBAC7B,SAAS,GAAG,IAAI,CAAC;gBACjB,OAAO;YACT,CAAC;YACD,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,KAAK,GAAG;gBAAE,SAAS;YACnD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YAC7B,IAAI,EAAE,CAAC;YACP,IAAI,CAAC;gBACH,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;YACtB,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;gBACrB,IAAI,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;oBAAE,SAAS;gBAClC,IAAI,CAAC,IAAI,CAAC,CAAC;YACb,CAAC;iBAAM,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC;gBACvB,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;gBACxC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC;oBAAE,SAAS;gBACnC,IAAI,wCAAwC,CAAC,IAAI,CAAC,IAAI,CAAC;oBAAE,SAAS,CAAC,4BAA4B;gBAC/F,IAAI,EAAE,CAAC,IAAI,GAAG,QAAQ;oBAAE,SAAS;gBACjC,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBACjC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;gBACjC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACxD,YAAY,EAAE,CAAC;gBACf,IAAI,IAAY,CAAC;gBACjB,IAAI,CAAC;oBACH,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;gBACpC,CAAC;gBAAC,MAAM,CAAC;oBACP,SAAS;gBACX,CAAC;gBACD,KAAK,MAAM,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;oBACrC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAClG,CAAC;gBACD,KAAK,MAAM,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;oBACpC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBACzF,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,IAAI,UAAU,CAAC,IAAI,CAAC;QAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACjC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;AACnE,CAAC"}
@@ -0,0 +1,167 @@
1
+ // Automation Kernel V2 — Workstream 1: backend capability matrix. Pure functions that answer
2
+ // "what can this backend actually do?" before any device is touched. The planner (plan.ts) consumes
3
+ // these to classify each step as native / supported_with_fallback / visual_only / unsupported and to
4
+ // name the exact missing capability when something cannot run.
5
+ //
6
+ // Driver.kind values map to backends as: 'direct' → android-direct, 'simulator' → ios-raw-simulator,
7
+ // 'wda' → ios-wda, 'remote' → an Appium backend (uiautomator2 | xcuitest, disambiguated by session).
8
+ function base(backend) {
9
+ return {
10
+ backend,
11
+ install: false,
12
+ launch: false,
13
+ terminate: false,
14
+ openUrl: false,
15
+ screenshot: false,
16
+ logs: false,
17
+ structuredTree: false,
18
+ nativeSelectors: false,
19
+ resourceId: false,
20
+ accessibilityId: false,
21
+ iosPredicate: false,
22
+ iosClassChain: false,
23
+ textSelector: false,
24
+ textInputAscii: false,
25
+ textInputUnicode: false,
26
+ clearText: false,
27
+ longPress: false,
28
+ doubleTap: false,
29
+ scrollUntilVisible: false,
30
+ pinch: false,
31
+ drag: false,
32
+ systemAlerts: false,
33
+ appDeclaredIdling: false,
34
+ wdaIdling: false,
35
+ webviewContexts: false,
36
+ screenRecording: false,
37
+ visualFallback: false,
38
+ ocrFallback: false,
39
+ notes: [],
40
+ };
41
+ }
42
+ /** Map a Swipium Driver.kind (+ optional Appium session) to an automation backend identity. */
43
+ export function backendForDriverKind(kind, session) {
44
+ switch (kind) {
45
+ case 'direct':
46
+ return 'android-direct';
47
+ case 'simulator':
48
+ return 'ios-raw-simulator';
49
+ case 'wda':
50
+ return 'ios-wda';
51
+ case 'remote':
52
+ return appiumBackendFromSession(session);
53
+ default:
54
+ return 'unknown';
55
+ }
56
+ }
57
+ export function appiumBackendFromSession(session) {
58
+ const name = (session?.automationName ?? '').toLowerCase();
59
+ const platform = (session?.platformName ?? '').toLowerCase();
60
+ // iOS evidence first (an XCUITest automationName or an iOS platform), then Android.
61
+ if (name.includes('xcuitest') || platform.includes('ios'))
62
+ return 'appium-xcuitest';
63
+ // Android: infer UiAutomator2 from the driver name OR from Android platform evidence alone
64
+ // (espresso also implies an Android session).
65
+ if (name.includes('uiautomator2') || name.includes('espresso') || name.includes('android') || platform.includes('android'))
66
+ return 'appium-uiautomator2';
67
+ // Without driver/session evidence we cannot claim a concrete Appium driver family.
68
+ return 'unknown';
69
+ }
70
+ export function backendCapabilities(backend, session) {
71
+ const c = base(backend);
72
+ switch (backend) {
73
+ case 'android-direct':
74
+ // Android DirectDriver via adb + UI Automator dump: resource-id / text / accessibility tree,
75
+ // ascii text input, basic gestures. No WebView context switching, no app-declared idle proof.
76
+ Object.assign(c, {
77
+ install: true, launch: true, terminate: true, openUrl: true, screenshot: true, logs: true,
78
+ structuredTree: true, nativeSelectors: true, resourceId: true, accessibilityId: true,
79
+ textSelector: true, textInputAscii: true, textInputUnicode: false, clearText: true,
80
+ longPress: true, doubleTap: true, scrollUntilVisible: true, pinch: false, drag: true,
81
+ systemAlerts: true, appDeclaredIdling: false, screenRecording: true,
82
+ visualFallback: true, ocrFallback: true,
83
+ });
84
+ c.notes.push('Unicode text input is not safe on adb input; use a fixture-provided ASCII value or a stronger backend.');
85
+ c.notes.push('No WebView context switching on the Android direct backend; attach Appium UiAutomator2 for hybrid contexts.');
86
+ return c;
87
+ case 'ios-raw-simulator':
88
+ // simctl lifecycle + screenshot + deep links + visual/OCR only. NO structured taps/selectors.
89
+ Object.assign(c, {
90
+ install: true, launch: true, terminate: true, openUrl: true, screenshot: true, logs: true,
91
+ structuredTree: false, nativeSelectors: false, screenRecording: true,
92
+ visualFallback: true, ocrFallback: true,
93
+ });
94
+ c.notes.push('iOS raw simulator cannot run structured taps; attach WDA or Appium XCUITest for selector-based automation.');
95
+ return c;
96
+ case 'ios-wda':
97
+ // WebDriverAgent / XCTest: accessibility id / name / predicate / class-chain, idle settings.
98
+ Object.assign(c, {
99
+ install: true, launch: true, terminate: true, openUrl: true, screenshot: true, logs: true,
100
+ structuredTree: true, nativeSelectors: true, accessibilityId: true, iosPredicate: true,
101
+ iosClassChain: true, textSelector: true, textInputAscii: true, textInputUnicode: true,
102
+ clearText: true, longPress: true, doubleTap: true, scrollUntilVisible: true, pinch: false,
103
+ drag: true, systemAlerts: true, wdaIdling: true, screenRecording: true,
104
+ visualFallback: true, ocrFallback: true,
105
+ });
106
+ c.notes.push('iOS resource-id selectors do not exist; use accessibility id / name / predicate / class chain.');
107
+ c.notes.push('Pinch/zoom is capability-gated until a tested WDA/Appium implementation exists.');
108
+ return c;
109
+ case 'appium-uiautomator2':
110
+ Object.assign(c, {
111
+ install: true, launch: true, terminate: true, openUrl: true, screenshot: true, logs: true,
112
+ structuredTree: true, nativeSelectors: true, resourceId: true, accessibilityId: true,
113
+ textSelector: true, textInputAscii: true, textInputUnicode: true, clearText: true,
114
+ longPress: true, doubleTap: true, scrollUntilVisible: true, pinch: true, drag: true,
115
+ systemAlerts: true, appDeclaredIdling: false, screenRecording: true,
116
+ visualFallback: true, ocrFallback: true,
117
+ // Web/hybrid potential is only claimed when the live session reports contexts.
118
+ webviewContexts: session?.webviewContextsAvailable === true,
119
+ });
120
+ if (!c.webviewContexts)
121
+ c.notes.push('WebView contexts only available when the Appium session reports a non-native context; not claimed without session evidence.');
122
+ return c;
123
+ case 'appium-xcuitest':
124
+ Object.assign(c, {
125
+ install: true, launch: true, terminate: true, openUrl: true, screenshot: true, logs: true,
126
+ structuredTree: true, nativeSelectors: true, accessibilityId: true, iosPredicate: true,
127
+ iosClassChain: true, textSelector: true, textInputAscii: true, textInputUnicode: true,
128
+ clearText: true, longPress: true, doubleTap: true, scrollUntilVisible: true, pinch: true,
129
+ drag: true, systemAlerts: true, wdaIdling: true, screenRecording: true,
130
+ visualFallback: true, ocrFallback: true,
131
+ webviewContexts: session?.webviewContextsAvailable === true,
132
+ });
133
+ c.notes.push('XCUITest sessions expose WDA settings (snapshot depth, waitForIdleTimeout, reduceMotion); bind automation evidence to them.');
134
+ if (!c.webviewContexts)
135
+ c.notes.push('WebView contexts only available when the Appium session reports a non-native context; not claimed without session evidence.');
136
+ return c;
137
+ case 'unknown':
138
+ default:
139
+ c.notes.push('Backend is unknown; attach a device/session so capabilities can be resolved.');
140
+ return c;
141
+ }
142
+ }
143
+ /** Whether a backend can target an element with the given selector strategy. */
144
+ export function selectorSupported(caps, strategy) {
145
+ switch (strategy) {
146
+ case 'resource_id':
147
+ return caps.resourceId;
148
+ case 'accessibility_id':
149
+ return caps.accessibilityId;
150
+ case 'text':
151
+ return caps.textSelector;
152
+ case 'ios_predicate':
153
+ return caps.iosPredicate;
154
+ case 'ios_class_chain':
155
+ return caps.iosClassChain;
156
+ case 'ocr_text':
157
+ return caps.ocrFallback;
158
+ case 'image':
159
+ return caps.visualFallback;
160
+ case 'coordinate':
161
+ // Any backend that can screenshot can be driven by coordinate as a last resort.
162
+ return caps.screenshot;
163
+ default:
164
+ return false;
165
+ }
166
+ }
167
+ //# sourceMappingURL=capabilities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../../src/automation/capabilities.ts"],"names":[],"mappings":"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"}