@goliapkg/smix 1.0.26 → 2.0.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 (74) hide show
  1. package/README.md +53 -130
  2. package/dist/A11yNode.d.ts.map +1 -1
  3. package/dist/A11yNode.js +2 -3
  4. package/dist/A11yNode.js.map +1 -1
  5. package/dist/App.d.ts +37 -22
  6. package/dist/App.d.ts.map +1 -1
  7. package/dist/App.js +72 -66
  8. package/dist/App.js.map +1 -1
  9. package/dist/ExpectationFailure.d.ts +8 -1
  10. package/dist/ExpectationFailure.d.ts.map +1 -1
  11. package/dist/ExpectationFailure.js +7 -7
  12. package/dist/ExpectationFailure.js.map +1 -1
  13. package/dist/HttpRunner.d.ts +9 -31
  14. package/dist/HttpRunner.d.ts.map +1 -1
  15. package/dist/HttpRunner.js +14 -100
  16. package/dist/HttpRunner.js.map +1 -1
  17. package/dist/Locator.d.ts +7 -9
  18. package/dist/Locator.d.ts.map +1 -1
  19. package/dist/Locator.js +17 -18
  20. package/dist/Locator.js.map +1 -1
  21. package/dist/NodeDriver.d.ts +53 -0
  22. package/dist/NodeDriver.d.ts.map +1 -0
  23. package/dist/NodeDriver.js +70 -0
  24. package/dist/NodeDriver.js.map +1 -0
  25. package/dist/Pattern.js +2 -2
  26. package/dist/Pattern.js.map +1 -1
  27. package/dist/Selector.d.ts.map +1 -1
  28. package/dist/Selector.js +5 -8
  29. package/dist/Selector.js.map +1 -1
  30. package/dist/SelectorResolver.d.ts +4 -6
  31. package/dist/SelectorResolver.d.ts.map +1 -1
  32. package/dist/SelectorResolver.js +5 -8
  33. package/dist/SelectorResolver.js.map +1 -1
  34. package/dist/Smix.d.ts +12 -4
  35. package/dist/Smix.d.ts.map +1 -1
  36. package/dist/Smix.js +25 -23
  37. package/dist/Smix.js.map +1 -1
  38. package/dist/SystemPopup.d.ts +18 -0
  39. package/dist/SystemPopup.d.ts.map +1 -0
  40. package/dist/SystemPopup.js +7 -0
  41. package/dist/SystemPopup.js.map +1 -0
  42. package/dist/index.d.ts +3 -1
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +2 -1
  45. package/dist/index.js.map +1 -1
  46. package/dist/loadNodeDriver.d.ts +21 -0
  47. package/dist/loadNodeDriver.d.ts.map +1 -0
  48. package/dist/loadNodeDriver.js +33 -0
  49. package/dist/loadNodeDriver.js.map +1 -0
  50. package/package.json +5 -2
  51. package/src/A11yNode.ts +2 -3
  52. package/src/App.ts +75 -78
  53. package/src/ExpectationFailure.ts +23 -13
  54. package/src/HttpRunner.ts +15 -126
  55. package/src/Locator.ts +17 -18
  56. package/src/NodeDriver.ts +109 -0
  57. package/src/Pattern.ts +2 -2
  58. package/src/Selector.ts +5 -8
  59. package/src/SelectorResolver.ts +7 -11
  60. package/src/Smix.ts +30 -26
  61. package/src/SystemPopup.ts +24 -0
  62. package/src/__tests__/AppDriving.test.ts +106 -0
  63. package/src/__tests__/MvpApiShape.test.ts +78 -46
  64. package/src/__tests__/ReadmeSnippets.test.ts +132 -0
  65. package/src/__tests__/RealAddonLoopback.test.ts +81 -0
  66. package/src/__tests__/SelectorFullSchema.test.ts +2 -4
  67. package/src/index.ts +19 -8
  68. package/src/loadNodeDriver.ts +41 -0
  69. package/src/SimRuntime.ts +0 -145
  70. package/src/__tests__/AppFullApi.test.ts +0 -168
  71. package/src/__tests__/AppTapMock.test.ts +0 -122
  72. package/src/__tests__/HttpRunner.test.ts +0 -121
  73. package/src/__tests__/Locator.test.ts +0 -128
  74. package/src/__tests__/LocatorToHave.test.ts +0 -118
package/README.md CHANGED
@@ -1,9 +1,33 @@
1
1
  # @goliapkg/smix
2
2
 
3
- Playwright-style iOS Simulator + Android emulator UI automation SDK for
4
- TypeScript / React Native / Expo / Detox-compatible test runners. Brings
5
- smix's Rust-core selector resolver to TS via HTTP client (production) or
6
- in-process mock (unit tests), packaged as an ESM Node 20+ module.
3
+ TypeScript types and host-side building blocks for
4
+ [smix](https://github.com/goliajp/smix) — AI-native UI automation for the
5
+ iOS Simulator and Android emulator.
6
+
7
+ **Live driving runs through the napi addon.** This package ships the same
8
+ typed API surface as the Swift / Kotlin / Rust SDKs; `Smix.launchApp`,
9
+ `App.tap`, `App.fill`, and the rest drive a device by calling into
10
+ `@goliapkg/smix-node`. `Smix.launchApp` loads the per-platform addon
11
+ automatically (via `loadNodeDriver`); the addon is built by a per-triple
12
+ prebuild matrix in CI, and its npm release follows in a later version — you
13
+ can also inject a `NodeDriver` explicitly for tests or a custom transport.
14
+ Three surfaces still throw `SmixNotImplementedError` pending their transport:
15
+ `App.screenshot` and `App.openUrl` need a runner wire route, and
16
+ `App.launchFresh` needs host-side state clearing. To drive a device without
17
+ the addon today, use the [`smix` CLI](https://crates.io/crates/smix-cli) or
18
+ one of the Swift / Kotlin / Rust SDKs.
19
+
20
+ ## What works today
21
+
22
+ - **Selector DSL** — the full selector language, encoding to the exact
23
+ JSON wire shape the Rust core resolves.
24
+ - **Session lifecycle** — `Session.open` / `stillValid` / `relaunchApp` /
25
+ `renewActivation` / `close` against a running smix runner, over HTTP.
26
+ - **`ExpectationFailure`** — parse and produce smix's AI-readable
27
+ failure JSON.
28
+ - **`A11yNode`** — typed accessibility-tree model with `flatten` /
29
+ `findById` / `rectCenter` helpers for working with `smix tree --json`
30
+ output.
7
31
 
8
32
  ## Installation
9
33
 
@@ -13,165 +37,64 @@ bun add -D @goliapkg/smix
13
37
  npm install -D @goliapkg/smix
14
38
  ```
15
39
 
16
- For test target / Detox-equivalent consumer. This package should only be
17
- listed in `devDependencies` — never bundled into a production RN/Expo app
18
- via metro/expo tree-shaking.
40
+ Test-target only: keep it in `devDependencies`, never bundled into a
41
+ production RN / Expo app.
19
42
 
20
- ## Quick start (MockSimRuntime — unit tests)
43
+ ## Selector
21
44
 
22
45
  ```typescript
23
- import { Smix, Selector, literal, MockSimRuntime, MockSelectorResolver, bundleId } from '@goliapkg/smix'
46
+ import { Selector, literal, regex } from '@goliapkg/smix'
24
47
 
25
- const runtime = new MockSimRuntime({ snapshotResult: /* A11yNode tree */ })
26
- const resolver = new MockSelectorResolver()
27
- resolver.registerHit('{"id":"btn-login"}', 'btn-login')
28
-
29
- const app = await Smix.launchApp(bundleId('com.example.MyApp'), runtime, resolver.resolve)
30
- await app.tap(Selector.id('btn-login'))
31
- await app.fill(Selector.id('input-username'), 'alice')
32
-
33
- const welcome = app.find(Selector.text(literal('Welcome back')))
34
- await welcome.toBeVisible({ timeoutMs: 5_000 })
35
- ```
36
-
37
- ## Production: HTTP runtime
38
-
39
- ```typescript
40
- import { Smix, Selector, bundleId, HttpSimRuntime } from '@goliapkg/smix'
41
-
42
- // Connects to smix-runner-server running on host (see swift-bridge/SmixRunnerCore)
43
- const runtime = new HttpSimRuntime('http://localhost:14101')
44
-
45
- const app = await Smix.launchApp(bundleId('com.example.MyApp'), runtime, runtime.resolver, runtime.labelsResolver)
46
- await app.tap(Selector.id('btn-login').below(Selector.text(literal('Sign In'))).nth(0))
47
- await app.find(Selector.role('button')).toHaveCount(3, { timeoutMs: 5_000 })
48
- ```
49
-
50
- HTTP endpoints documented in [`src/HttpRunner.ts`](src/HttpRunner.ts) +
51
- [Swift server route handler](../../swift-bridge/Sources/SmixRunnerCore/SmixRunnerServer.swift).
52
-
53
- ## API surface
54
-
55
- ### Selector
56
-
57
- ```typescript
58
- // Base discriminators (untagged JSON wire shape — matches Rust smix-selector)
59
48
  Selector.id('btn-login')
60
49
  Selector.text(literal('Sign In'))
61
50
  Selector.text(regex('^Sub', 'i'))
62
51
  Selector.label('Settings')
63
- Selector.role('button')
64
52
  Selector.role('button', literal('Submit'))
65
- Selector.focused()
66
- Selector.anchor({ below: Selector.text(literal('Total')) }, { nth: 0 })
67
53
  Selector.localizedText({ en: 'Submit', ja: '送信' })
68
54
 
69
- // Fluent modifier chaining (returns new Selector with Modifiers updated)
70
- Selector.id('btn').below(Selector.text(literal('Address')))
71
- Selector.label('Edit').nth(0)
72
- Selector.text(literal('Item')).above(Selector.text(literal('Footer'))).first()
73
- Selector.role('button').near(Selector.text(literal('Confirm'))).ancestor(Selector.role('dialog'))
55
+ // Fluent modifier chaining (returns a new Selector)
56
+ Selector.id('btn').below(Selector.text(literal('Address'))).nth(0)
57
+ Selector.role('button').near(Selector.text(literal('Confirm')))
74
58
  ```
75
59
 
76
- Wire JSON (untagged + flatten — byte-identical to Rust smix-selector):
60
+ Wire JSON (untagged + flattened — byte-identical to Rust `smix-selector`):
77
61
 
78
62
  ```json
79
63
  {"id": "btn-login", "below": {"text": "Address"}, "nth": 0}
80
64
  ```
81
65
 
82
- ### App (act)
66
+ ## Session lifecycle
83
67
 
84
68
  ```typescript
85
- await app.tap(selector: Selector, opts?: { timeoutMs?: number })
86
- await app.fill(selector: Selector, text: string)
87
- await app.pressKey(key: KeyName) // 'return' | 'delete' | 'space' | 'tab' | 'escape' | 'enter'
88
- await app.swipe(direction: SwipeDirection) // 'up' | 'down' | 'left' | 'right'
89
- await app.tapAtCoord(nx: number, ny: number) // 0..1, throws if out-of-range
90
- await app.terminate()
91
- await app.relaunch()
92
- await app.launchFresh({ clearState?: boolean, clearKeychain?: boolean, appPath?: string })
93
- ```
69
+ import { HttpSimRuntime, Session } from '@goliapkg/smix'
94
70
 
95
- ### App (sense)
96
-
97
- ```typescript
98
- const png: Uint8Array = await app.screenshot()
99
- const tree: A11yNode = await app.tree()
100
- const popups: A11yNode[] = await app.systemPopups()
101
- await app.openUrl('myapp://deep/link')
71
+ const runtime = new HttpSimRuntime('http://127.0.0.1:22087')
72
+ const session = await Session.open(runtime, 'com.example.app')
73
+ await session.relaunchApp()
74
+ await session.close()
102
75
  ```
103
76
 
104
- ### Locator (assertions)
77
+ `HttpSimRuntime` also exposes `resolver` / `labelsResolver`, which
78
+ resolve a selector against a caller-supplied tree JSON via the runner's
79
+ `/select/resolve` routes.
105
80
 
106
- ```typescript
107
- const loc = app.find(Selector.text(literal('Welcome')))
108
- await loc.toBeVisible({ timeoutMs: 5_000 }) // polls 250ms
109
- await loc.toContainText('alice', { timeoutMs: 5_000 }) // substring
110
- await loc.toHaveLabel('Sign In', { timeoutMs: 5_000 }) // strict equal
111
- await loc.toHaveCount(3, { timeoutMs: 5_000 }) // exact match count
112
- ```
113
-
114
- ### ExpectationFailure (AI-readable JSON)
81
+ ## ExpectationFailure
115
82
 
116
83
  ```typescript
84
+ import { ExpectationFailure } from '@goliapkg/smix'
85
+
117
86
  try {
118
- await app.tap(Selector.id('btn-missing'))
87
+ // a driving call, once the native transport lands
119
88
  } catch (e) {
120
89
  if (e instanceof ExpectationFailure) {
121
- e.code // 'notFound' | 'ambiguous' | 'notInteractable' | 'timeout' | 'wrongState' | 'unknown'
122
- e.message // human-readable
123
- e.selectorJson // original Selector encoded as JSON
124
- e.visibleElements // A11yNode[] — first 20 nodes from current tree
125
- e.suggestions // string[] — ['check accessibilityIdentifier...', ...]
126
- e.toJson() // single-line sorted-keys JSON for AI agent consumption
90
+ e.code // 'ELEMENT_NOT_FOUND' | 'AMBIGUOUS' | 'TIMEOUT' | ...
91
+ e.visibleElements // A11yNode[] — context for AI diagnosis
92
+ e.suggestions // string[]
93
+ e.toJson() // single-line JSON for agent consumption
127
94
  }
128
95
  }
129
96
  ```
130
97
 
131
- ## Demos
132
-
133
- Run any of the realistic e2e flows in [`examples/demo-app/`](examples/demo-app):
134
-
135
- ```bash
136
- cd examples/demo-app
137
- bun login-flow.ts # login + welcome assertion
138
- bun form-validation-flow.ts # 3-cycle form validation + inline errors
139
- bun list-scroll-flow.ts # scrollable list + toHaveCount semantics
140
- bun multi-screen-nav-flow.ts # nav + tapAtCoord + openUrl + relaunch
141
- ```
142
-
143
- See [`examples/demo-app/COVERAGE.md`](examples/demo-app/COVERAGE.md) for the
144
- SDK surface coverage matrix across demos.
145
-
146
- ## Conformance
147
-
148
- Cross-binary harness verifies SDK output byte-identical to Rust:
149
-
150
- ```bash
151
- bash ../../scripts/sdk/run-cross-binary-harness.sh
152
- # Summary: 24 / 24 fixtures byte-identical (Rust + Swift + TS)
153
- ```
154
-
155
- ## Test-target-only
156
-
157
- `@goliapkg/smix` is intended for `devDependencies` only — never bundled
158
- into a production RN/Expo app. `metro`/`expo`'s tree-shaking guarantees
159
- the production bundle excludes it. The `HttpSimRuntime` connects to a
160
- smix-runner-server running on the host (via `127.0.0.1:port` loopback),
161
- not in-process.
162
-
163
- ## Architecture
164
-
165
- - **Rust stone core** (`crates/smix-{selector,selector-resolver,screen,error}`):
166
- selector resolution + AI-readable failure types, shared across all
167
- language SDKs.
168
- - **HTTP runtime** (`src/HttpRunner.ts`): wraps fetch to smix-runner-server
169
- on the host. Production-default.
170
- - **Mock runtime** (`src/SimRuntime.ts MockSimRuntime`): in-memory tree +
171
- event log. Used by vitest unit tests + demo flows.
172
- - **SDK facade** (`src/{Smix,App,Selector,Modifiers,Locator,ExpectationFailure}.ts`):
173
- Playwright-style ergonomic surface, class-based fluent chaining.
174
-
175
98
  ## License
176
99
 
177
100
  Apache-2.0 OR MIT (dual, at your option).
@@ -1 +1 @@
1
- {"version":3,"file":"A11yNode.d.ts","sourceRoot":"","sources":["../src/A11yNode.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,IAAI;IACnB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,IAAI,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAE5D;AAED,MAAM,MAAM,QAAQ,GAChB,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,iBAAiB,GAAG,aAAa,GACnE,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,YAAY,GACnE,KAAK,GAAG,QAAQ,GAAG,eAAe,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAChE,QAAQ,GAAG,aAAa,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,GACzD,YAAY,GAAG,kBAAkB,GAAG,OAAO,GAAG,gBAAgB,GAC9D,SAAS,GAAG,UAAU,CAAA;AAE1B,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAA;IAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACrC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,MAAM,EAAE,IAAI,CAAA;IACZ,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC9B,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC9B,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,QAAQ,CAAC,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAA;CAClC;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAOpE;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,EAAE,CAMlD"}
1
+ {"version":3,"file":"A11yNode.d.ts","sourceRoot":"","sources":["../src/A11yNode.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,IAAI;IACnB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,IAAI,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAE5D;AAED,MAAM,MAAM,QAAQ,GAChB,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,iBAAiB,GAAG,aAAa,GACnE,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,YAAY,GACnE,KAAK,GAAG,QAAQ,GAAG,eAAe,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAChE,QAAQ,GAAG,aAAa,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,GACzD,YAAY,GAAG,kBAAkB,GAAG,OAAO,GAAG,gBAAgB,GAC9D,SAAS,GAAG,UAAU,CAAA;AAE1B,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAA;IAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACrC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,MAAM,EAAE,IAAI,CAAA;IACZ,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC9B,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC9B,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,QAAQ,CAAC,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAA;CAClC;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAOpE;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,EAAE,CAMlD"}
package/dist/A11yNode.js CHANGED
@@ -1,6 +1,5 @@
1
- // v7.5 c1 — A11yNode + Rect mirror Rust smix-screen A11yNode +
2
- // Swift v7.2 + Kotlin v7.4 A11yNode. CamelCase wire keys match
3
- // Rust `#[serde(rename_all = "camelCase")]`.
1
+ // The wire form of `smix-screen`'s types. The camelCase keys are the
2
+ // contract with the runner, not a local style choice.
4
3
  export function rectCenter(r) {
5
4
  return { x: r.x + r.w / 2, y: r.y + r.h / 2 };
6
5
  }
@@ -1 +1 @@
1
- {"version":3,"file":"A11yNode.js","sourceRoot":"","sources":["../src/A11yNode.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAC/D,6CAA6C;AAS7C,MAAM,UAAU,UAAU,CAAC,CAAO;IAChC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAA;AAC/C,CAAC;AA2BD;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAc,EAAE,EAAU;IACjD,IAAI,IAAI,CAAC,UAAU,KAAK,EAAE;QAAE,OAAO,IAAI,CAAA;IACvC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;QACxC,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QAC/B,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO,GAAG,CAAA;IAC9B,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,OAAO,CAAC,IAAc;IACpC,MAAM,GAAG,GAAe,CAAC,IAAI,CAAC,CAAA;IAC9B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;QACxC,GAAG,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;IAC7B,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC"}
1
+ {"version":3,"file":"A11yNode.js","sourceRoot":"","sources":["../src/A11yNode.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,sDAAsD;AAStD,MAAM,UAAU,UAAU,CAAC,CAAO;IAChC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAA;AAC/C,CAAC;AA2BD;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAc,EAAE,EAAU;IACjD,IAAI,IAAI,CAAC,UAAU,KAAK,EAAE;QAAE,OAAO,IAAI,CAAA;IACvC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;QACxC,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QAC/B,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO,GAAG,CAAA;IAC9B,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,OAAO,CAAC,IAAc;IACpC,MAAM,GAAG,GAAe,CAAC,IAAI,CAAC,CAAA;IAC9B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;QACxC,GAAG,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;IAC7B,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC"}
package/dist/App.d.ts CHANGED
@@ -1,57 +1,72 @@
1
1
  import { type A11yNode } from './A11yNode.js';
2
2
  import { Locator, SmixNotImplementedError } from './Locator.js';
3
+ import type { NodeDriver, NodeSession } from './NodeDriver.js';
3
4
  import { type Selector } from './Selector.js';
4
- import { type KeyName, type SmixSimRuntime, type SwipeDirection } from './SimRuntime.js';
5
5
  import type { LabelsResolver, SelectorResolver } from './SelectorResolver.js';
6
+ import type { SystemPopup } from './SystemPopup.js';
7
+ type KeyName = 'return' | 'delete' | 'space' | 'tab' | 'escape' | 'enter';
8
+ type SwipeDirection = 'up' | 'down' | 'left' | 'right';
6
9
  /**
7
10
  * A handle to a running app on the simulator / emulator.
8
11
  */
9
12
  export declare class App {
10
13
  readonly bundleId: string;
11
- readonly runtime: SmixSimRuntime;
14
+ /** The napi driver (act + sense); a mock in tests, the real addon in prod. */
15
+ readonly driver: NodeDriver;
16
+ /** The napi session this app was launched in (lifecycle verbs). */
17
+ readonly session: NodeSession;
12
18
  readonly resolver: SelectorResolver;
13
19
  /**
14
- * v7.6 c1 — wraps Rust `resolve_selector_labels` (each match's
15
- * `.label`). Used by Locator.toHaveCount + toHaveLabel. Defaults
16
- * to a stub that throws — tests + production should pass a real
17
- * impl (HttpSimRuntime.labelsResolver or MockLabelsResolver.resolve).
20
+ * Wraps `resolve_selector_labels`, backing toHaveCount and
21
+ * toHaveLabel. The default throws rather than guessing: there is no
22
+ * sensible answer without a resolver, and a silent empty list would
23
+ * read as "no matches".
18
24
  */
19
25
  readonly labelsResolver: LabelsResolver;
20
- constructor(bundleId: string, runtime: SmixSimRuntime, resolver: SelectorResolver,
26
+ constructor(bundleId: string,
27
+ /** The napi driver (act + sense); a mock in tests, the real addon in prod. */
28
+ driver: NodeDriver,
29
+ /** The napi session this app was launched in (lifecycle verbs). */
30
+ session: NodeSession, resolver: SelectorResolver,
21
31
  /**
22
- * v7.6 c1 — wraps Rust `resolve_selector_labels` (each match's
23
- * `.label`). Used by Locator.toHaveCount + toHaveLabel. Defaults
24
- * to a stub that throws — tests + production should pass a real
25
- * impl (HttpSimRuntime.labelsResolver or MockLabelsResolver.resolve).
32
+ * Wraps `resolve_selector_labels`, backing toHaveCount and
33
+ * toHaveLabel. The default throws rather than guessing: there is no
34
+ * sensible answer without a resolver, and a silent empty list would
35
+ * read as "no matches".
26
36
  */
27
37
  labelsResolver?: LabelsResolver);
38
+ /**
39
+ * Snapshot the tree, resolve `selector` against it, and return the first
40
+ * matching id. Selectors resolve to an id on this side; only the id crosses
41
+ * the napi boundary. No match throws ELEMENT_NOT_FOUND with the visible
42
+ * elements so the failure is AI-readable.
43
+ */
44
+ private resolveFirstOrThrow;
28
45
  tap(selector: Selector, _opts?: {
29
46
  timeoutMs?: number;
30
47
  }): Promise<void>;
31
- /**
32
- * Resolve [selector], tap to focus, then type [text]. Mirror
33
- * Kotlin App.fill from v7.4 c3.
34
- */
35
48
  fill(selector: Selector, text: string): Promise<void>;
36
49
  pressKey(key: KeyName): Promise<void>;
37
50
  swipe(direction: SwipeDirection): Promise<void>;
38
51
  screenshot(): Promise<Uint8Array>;
39
- /**
40
- * Tap at a normalized coordinate (0..1) — escape hatch per §9 #3.
41
- * Out-of-range values throw ExpectationFailure code=wrongState.
42
- */
52
+ /** Tap at a normalized coordinate (0..1) — escape hatch per §9 #3. */
43
53
  tapAtCoord(nx: number, ny: number): Promise<void>;
44
54
  terminate(): Promise<void>;
45
55
  relaunch(): Promise<void>;
46
- launchFresh(opts?: {
56
+ launchFresh(_opts?: {
47
57
  clearState?: boolean;
48
58
  clearKeychain?: boolean;
49
59
  appPath?: string;
50
60
  }): Promise<void>;
51
61
  find(selector: Selector): Locator;
62
+ /**
63
+ * The tree seam Locator polls. Pending napi: without an FFI path there is
64
+ * no live simulator to snapshot. Lands with the napi axis.
65
+ */
66
+ snapshotTree(): Promise<A11yNode>;
52
67
  tree(): Promise<A11yNode>;
53
- systemPopups(): Promise<A11yNode[]>;
54
- openUrl(url: string): Promise<void>;
68
+ systemPopups(): Promise<SystemPopup[]>;
69
+ openUrl(_url: string): Promise<void>;
55
70
  }
56
71
  export { SmixNotImplementedError };
57
72
  //# sourceMappingURL=App.d.ts.map
package/dist/App.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../src/App.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,QAAQ,EAAqB,MAAM,eAAe,CAAA;AAEhE,OAAO,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAC/D,OAAO,EAEL,KAAK,QAAQ,EACd,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAE7E;;GAEG;AACH,qBAAa,GAAG;aAEI,QAAQ,EAAE,MAAM;aAChB,OAAO,EAAE,cAAc;aACvB,QAAQ,EAAE,gBAAgB;IAC1C;;;;;OAKG;aACa,cAAc,EAAE,cAAc;gBAT9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,gBAAgB;IAC1C;;;;;OAKG;IACa,cAAc,GAAE,cAE/B;IAKG,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAsChF;;;OAGG;IACG,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrD,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrC,KAAK,CAAC,SAAS,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC;IAIvC;;;OAGG;IACG,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWjD,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAI1B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAKzB,WAAW,CAAC,IAAI,GAAE;QACtB,UAAU,CAAC,EAAE,OAAO,CAAA;QACpB,aAAa,CAAC,EAAE,OAAO,CAAA;QACvB,OAAO,CAAC,EAAE,MAAM,CAAA;KACZ,GAAG,OAAO,CAAC,IAAI,CAAC;IAWtB,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO;IAI3B,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC;IAIzB,YAAY,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;IAInC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG1C;AAED,OAAO,EAAE,uBAAuB,EAAE,CAAA"}
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../src/App.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,QAAQ,EAAW,MAAM,eAAe,CAAA;AAEtD,OAAO,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC9D,OAAO,EAAE,KAAK,QAAQ,EAAsB,MAAM,eAAe,CAAA;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAC7E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAEnD,KAAK,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAA;AACzE,KAAK,cAAc,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AAEtD;;GAEG;AACH,qBAAa,GAAG;aAEI,QAAQ,EAAE,MAAM;IAChC,8EAA8E;aAC9D,MAAM,EAAE,UAAU;IAClC,mEAAmE;aACnD,OAAO,EAAE,WAAW;aACpB,QAAQ,EAAE,gBAAgB;IAC1C;;;;;OAKG;aACa,cAAc,EAAE,cAAc;gBAZ9B,QAAQ,EAAE,MAAM;IAChC,8EAA8E;IAC9D,MAAM,EAAE,UAAU;IAClC,mEAAmE;IACnD,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,gBAAgB;IAC1C;;;;;OAKG;IACa,cAAc,GAAE,cAE/B;IAKH;;;;;OAKG;YACW,mBAAmB;IAiB3B,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1E,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMrD,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrC,KAAK,CAAC,SAAS,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC;IAMvC,sEAAsE;IAChE,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUjD,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAI1B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAIzB,WAAW,CAAC,KAAK,GAAE;QACvB,UAAU,CAAC,EAAE,OAAO,CAAA;QACpB,aAAa,CAAC,EAAE,OAAO,CAAA;QACvB,OAAO,CAAC,EAAE,MAAM,CAAA;KACZ,GAAG,OAAO,CAAC,IAAI,CAAC;IAStB,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO;IAIjC;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,QAAQ,CAAC;IAIjC,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC;IAIzB,YAAY,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAItC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAK3C;AAED,OAAO,EAAE,uBAAuB,EAAE,CAAA"}
package/dist/App.js CHANGED
@@ -1,122 +1,128 @@
1
- // v7.5 c3 — App class full wire mirrors Kotlin v7.4 c5 / Swift v7.2.
2
- import { findById, flatten } from './A11yNode.js';
1
+ // A handle to a running app. Live driving (tap/fill/swipe/...) and live
2
+ // sense (snapshotTree) are pending the napi axis: the TS SDK has no FFI
3
+ // path, so with the fictional HTTP wire deleted there is no way to reach a
4
+ // simulator from here yet. These methods throw SmixNotImplementedError
5
+ // rather than posting to a route the runner never served. What does work is
6
+ // the resolver seam: given a treeJson, `resolver` / `labelsResolver` resolve
7
+ // selectors against the runner's served /select/resolve* routes.
8
+ import { flatten } from './A11yNode.js';
3
9
  import { ExpectationFailure } from './ExpectationFailure.js';
4
10
  import { Locator, SmixNotImplementedError } from './Locator.js';
5
- import { encodeSelectorJson, } from './Selector.js';
11
+ import { encodeSelectorJson } from './Selector.js';
6
12
  /**
7
13
  * A handle to a running app on the simulator / emulator.
8
14
  */
9
15
  export class App {
10
16
  bundleId;
11
- runtime;
17
+ driver;
18
+ session;
12
19
  resolver;
13
20
  labelsResolver;
14
- constructor(bundleId, runtime, resolver,
21
+ constructor(bundleId,
22
+ /** The napi driver (act + sense); a mock in tests, the real addon in prod. */
23
+ driver,
24
+ /** The napi session this app was launched in (lifecycle verbs). */
25
+ session, resolver,
15
26
  /**
16
- * v7.6 c1 — wraps Rust `resolve_selector_labels` (each match's
17
- * `.label`). Used by Locator.toHaveCount + toHaveLabel. Defaults
18
- * to a stub that throws — tests + production should pass a real
19
- * impl (HttpSimRuntime.labelsResolver or MockLabelsResolver.resolve).
27
+ * Wraps `resolve_selector_labels`, backing toHaveCount and
28
+ * toHaveLabel. The default throws rather than guessing: there is no
29
+ * sensible answer without a resolver, and a silent empty list would
30
+ * read as "no matches".
20
31
  */
21
32
  labelsResolver = async () => {
22
33
  throw new Error('labelsResolver not configured; pass one to Smix.launchApp or App constructor');
23
34
  }) {
24
35
  this.bundleId = bundleId;
25
- this.runtime = runtime;
36
+ this.driver = driver;
37
+ this.session = session;
26
38
  this.resolver = resolver;
27
39
  this.labelsResolver = labelsResolver;
28
40
  }
29
41
  // ---- act methods --------------------------------------------------
30
- async tap(selector, _opts = {}) {
31
- const tree = await this.runtime.snapshotTree();
42
+ /**
43
+ * Snapshot the tree, resolve `selector` against it, and return the first
44
+ * matching id. Selectors resolve to an id on this side; only the id crosses
45
+ * the napi boundary. No match throws ELEMENT_NOT_FOUND with the visible
46
+ * elements so the failure is AI-readable.
47
+ */
48
+ async resolveFirstOrThrow(selector) {
49
+ const treeJson = await this.driver.snapshotTree();
32
50
  const selectorJson = encodeSelectorJson(selector);
33
- const treeJson = JSON.stringify(tree);
34
- let matches;
35
- try {
36
- matches = await this.resolver(treeJson, selectorJson);
37
- }
38
- catch (e) {
39
- const msg = e instanceof Error ? e.message : String(e);
51
+ const ids = await this.resolver(treeJson, selectorJson);
52
+ const first = ids[0];
53
+ if (first === undefined) {
54
+ const tree = JSON.parse(treeJson);
40
55
  throw new ExpectationFailure({
41
- code: 'unknown',
42
- message: `FFI resolveSelector raised: ${msg}`,
43
- selectorJson,
44
- });
45
- }
46
- const firstId = matches[0];
47
- const node = firstId !== undefined ? findById(tree, firstId) : null;
48
- if (node === null) {
49
- throw new ExpectationFailure({
50
- code: 'notFound',
51
- message: 'no candidates after spatial + index filters',
56
+ code: 'ELEMENT_NOT_FOUND',
57
+ message: `no element matched selector ${selectorJson}`,
52
58
  selectorJson,
53
59
  visibleElements: flatten(tree).slice(0, 20),
54
- suggestions: [
55
- 'check `accessibilityIdentifier` is set on the target view',
56
- 'consider relaxing to text(...) if the label is more stable than the id',
57
- ],
58
60
  });
59
61
  }
60
- await this.runtime.synthesizeTap(node.bounds.x + node.bounds.w / 2, node.bounds.y + node.bounds.h / 2);
62
+ return first;
63
+ }
64
+ async tap(selector, _opts = {}) {
65
+ await this.driver.tapById(await this.resolveFirstOrThrow(selector));
61
66
  }
62
- /**
63
- * Resolve [selector], tap to focus, then type [text]. Mirror
64
- * Kotlin App.fill from v7.4 c3.
65
- */
66
67
  async fill(selector, text) {
67
- await this.tap(selector);
68
- await this.runtime.sendString(text);
68
+ const id = await this.resolveFirstOrThrow(selector);
69
+ await this.driver.tapById(id);
70
+ await this.driver.inputText(text);
69
71
  }
70
72
  async pressKey(key) {
71
- await this.runtime.pressKey(key);
73
+ await this.driver.pressKey(key === 'enter' ? 'return' : key);
72
74
  }
73
75
  async swipe(direction) {
74
- await this.runtime.swipe(direction);
76
+ await this.driver.swipe(direction);
75
77
  }
76
78
  async screenshot() {
77
- return this.runtime.screenshot();
79
+ // No runner wire route yet — singled out to a later checkpoint (v2.md
80
+ // decision, 2026-07-24). The blocker is the wire, not napi.
81
+ throw new SmixNotImplementedError('wire', 'App.screenshot');
78
82
  }
79
- /**
80
- * Tap at a normalized coordinate (0..1) — escape hatch per §9 #3.
81
- * Out-of-range values throw ExpectationFailure code=wrongState.
82
- */
83
+ /** Tap at a normalized coordinate (0..1) — escape hatch per §9 #3. */
83
84
  async tapAtCoord(nx, ny) {
84
85
  if (nx < 0 || nx > 1 || ny < 0 || ny > 1) {
85
86
  throw new ExpectationFailure({
86
- code: 'wrongState',
87
- message: `tapAtCoord arguments out of [0,1] range: nx=${nx}, ny=${ny}`,
88
- suggestions: ['use normalized 0..1 — for raw points, divide by viewport size'],
87
+ code: 'ASSERTION_FAILED',
88
+ message: `tapAtCoord expects normalized 0..1 coordinates, got (${nx}, ${ny})`,
89
89
  });
90
90
  }
91
- await this.runtime.synthesizeTapAtNormalized(nx, ny);
91
+ await this.driver.tapAtCoord(nx, ny);
92
92
  }
93
93
  async terminate() {
94
- await this.runtime.terminate(this.bundleId);
94
+ await this.session.terminateApp();
95
95
  }
96
96
  async relaunch() {
97
- await this.runtime.terminate(this.bundleId);
98
- await this.runtime.launch(this.bundleId);
97
+ await this.session.relaunchApp();
99
98
  }
100
- async launchFresh(opts = {}) {
101
- await this.runtime.launchFresh({
102
- bundleId: this.bundleId,
103
- clearState: opts.clearState ?? false,
104
- clearKeychain: opts.clearKeychain ?? false,
105
- appPath: opts.appPath,
106
- });
99
+ async launchFresh(_opts = {}) {
100
+ // Clearing state / installing an appPath is host-side (simctl), with no
101
+ // runner wire — singled out to a later checkpoint. The blocker is the
102
+ // host, not napi.
103
+ throw new SmixNotImplementedError('host', 'App.launchFresh');
107
104
  }
108
105
  // ---- sense methods ------------------------------------------------
109
106
  find(selector) {
110
107
  return new Locator(this, selector);
111
108
  }
109
+ /**
110
+ * The tree seam Locator polls. Pending napi: without an FFI path there is
111
+ * no live simulator to snapshot. Lands with the napi axis.
112
+ */
113
+ async snapshotTree() {
114
+ return JSON.parse(await this.driver.snapshotTree());
115
+ }
112
116
  async tree() {
113
- return this.runtime.snapshotTree();
117
+ return this.snapshotTree();
114
118
  }
115
119
  async systemPopups() {
116
- return this.runtime.systemPopups();
120
+ return JSON.parse(await this.driver.systemPopups());
117
121
  }
118
- async openUrl(url) {
119
- await this.runtime.openUrl(url);
122
+ async openUrl(_url) {
123
+ // No runner wire route yet — singled out to a later checkpoint. The
124
+ // blocker is the wire, not napi.
125
+ throw new SmixNotImplementedError('wire', 'App.openUrl');
120
126
  }
121
127
  }
122
128
  export { SmixNotImplementedError };
package/dist/App.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"App.js","sourceRoot":"","sources":["../src/App.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"App.js","sourceRoot":"","sources":["../src/App.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,12 @@
1
1
  import type { A11yNode } from './A11yNode.js';
2
- export type FailureCode = 'notFound' | 'ambiguous' | 'notInteractable' | 'timeout' | 'wrongState' | 'unknown';
2
+ /**
3
+ * Rust `smix_error::FailureCode`'s wire strings verbatim —
4
+ * `crates/smix-error/tests/sdk_failure_code_parity.rs` reads this
5
+ * declaration and fails if the two sets ever diverge.
6
+ */
7
+ export type FailureCode = 'ELEMENT_NOT_FOUND' | 'NOT_VISIBLE' | 'NOT_ENABLED' | 'AMBIGUOUS' | 'TIMEOUT' | 'ASSERTION_FAILED' | 'APP_NOT_RUNNING' | 'SIMULATOR_NOT_BOOTED'
8
+ /** The touch was synthesised, and it did not land inside the element the selector matched. Distinct from element-not-found: not-found means fix the selector, missed means the element was there and the touch went elsewhere. */
9
+ | 'TAP_MISSED' | 'DRIVER_ERROR';
3
10
  export declare const FAILURE_CODES: readonly FailureCode[];
4
11
  /**
5
12
  * Structured failure thrown by SDK resolver paths (App.tap, Locator
@@ -1 +1 @@
1
- {"version":3,"file":"ExpectationFailure.d.ts","sourceRoot":"","sources":["../src/ExpectationFailure.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAE7C,MAAM,MAAM,WAAW,GACnB,UAAU,GACV,WAAW,GACX,iBAAiB,GACjB,SAAS,GACT,YAAY,GACZ,SAAS,CAAA;AAEb,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EAGtC,CAAA;AAEV;;;;GAIG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IACpC,QAAQ,CAAC,eAAe,EAAE,SAAS,QAAQ,EAAE,CAAA;IAC7C,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;gBAEd,IAAI,EAAE;QAChB,IAAI,EAAE,WAAW,CAAA;QACjB,OAAO,EAAE,MAAM,CAAA;QACf,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QACjC,eAAe,CAAC,EAAE,SAAS,QAAQ,EAAE,GAAG,SAAS,CAAA;QACjD,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;QAC3C,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAC/B;IAUD;;;;OAIG;IACH,MAAM,IAAI,MAAM;CAUjB"}
1
+ {"version":3,"file":"ExpectationFailure.d.ts","sourceRoot":"","sources":["../src/ExpectationFailure.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAE7C;;;;GAIG;AACH,MAAM,MAAM,WAAW,GACnB,mBAAmB,GACnB,aAAa,GACb,aAAa,GACb,WAAW,GACX,SAAS,GACT,kBAAkB,GAClB,iBAAiB,GACjB,sBAAsB;AACxB,kOAAkO;GAChO,YAAY,GACZ,cAAc,CAAA;AAElB,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EAItC,CAAA;AAEV;;;;GAIG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IACpC,QAAQ,CAAC,eAAe,EAAE,SAAS,QAAQ,EAAE,CAAA;IAC7C,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;gBAEd,IAAI,EAAE;QAChB,IAAI,EAAE,WAAW,CAAA;QACjB,OAAO,EAAE,MAAM,CAAA;QACf,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QACjC,eAAe,CAAC,EAAE,SAAS,QAAQ,EAAE,GAAG,SAAS,CAAA;QACjD,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;QAC3C,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAC/B;IAUD;;;;OAIG;IACH,MAAM,IAAI,MAAM;CAUjB"}
@@ -1,12 +1,12 @@
1
- // v7.5 c1 — ExpectationFailure + FailureCode mirror Swift v7.2 c4 +
2
- // Kotlin v7.4 c1 + Rust smix-error.
1
+ // The wire form of `smix-error`'s failure surface.
3
2
  //
4
- // AI-readable JSON contract: toJson() emits sorted-keys, ISO-8601
5
- // timestamp single-line JSON matching Swift errorDescription contract
6
- // (per design.md §9 #11).
3
+ // toJson() emits sorted keys and an ISO-8601 timestamp on one line. Every
4
+ // SDK emits the same bytes for the same failure, so a reader — human or
5
+ // model — sees one format whichever language raised it.
7
6
  export const FAILURE_CODES = [
8
- 'notFound', 'ambiguous', 'notInteractable',
9
- 'timeout', 'wrongState', 'unknown',
7
+ 'ELEMENT_NOT_FOUND', 'NOT_VISIBLE', 'NOT_ENABLED',
8
+ 'AMBIGUOUS', 'TIMEOUT', 'ASSERTION_FAILED',
9
+ 'APP_NOT_RUNNING', 'SIMULATOR_NOT_BOOTED', 'TAP_MISSED', 'DRIVER_ERROR',
10
10
  ];
11
11
  /**
12
12
  * Structured failure thrown by SDK resolver paths (App.tap, Locator