@goliapkg/smix 0.1.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 (64) hide show
  1. package/README.md +193 -0
  2. package/dist/A11yNode.d.ts +36 -0
  3. package/dist/A11yNode.d.ts.map +1 -0
  4. package/dist/A11yNode.js +30 -0
  5. package/dist/A11yNode.js.map +1 -0
  6. package/dist/App.d.ts +57 -0
  7. package/dist/App.d.ts.map +1 -0
  8. package/dist/App.js +123 -0
  9. package/dist/App.js.map +1 -0
  10. package/dist/ExpectationFailure.d.ts +30 -0
  11. package/dist/ExpectationFailure.d.ts.map +1 -0
  12. package/dist/ExpectationFailure.js +47 -0
  13. package/dist/ExpectationFailure.js.map +1 -0
  14. package/dist/HttpRunner.d.ts +61 -0
  15. package/dist/HttpRunner.d.ts.map +1 -0
  16. package/dist/HttpRunner.js +148 -0
  17. package/dist/HttpRunner.js.map +1 -0
  18. package/dist/Locator.d.ts +60 -0
  19. package/dist/Locator.d.ts.map +1 -0
  20. package/dist/Locator.js +177 -0
  21. package/dist/Locator.js.map +1 -0
  22. package/dist/Pattern.d.ts +21 -0
  23. package/dist/Pattern.d.ts.map +1 -0
  24. package/dist/Pattern.js +44 -0
  25. package/dist/Pattern.js.map +1 -0
  26. package/dist/Selector.d.ts +96 -0
  27. package/dist/Selector.d.ts.map +1 -0
  28. package/dist/Selector.js +276 -0
  29. package/dist/Selector.js.map +1 -0
  30. package/dist/SelectorResolver.d.ts +41 -0
  31. package/dist/SelectorResolver.d.ts.map +1 -0
  32. package/dist/SelectorResolver.js +55 -0
  33. package/dist/SelectorResolver.js.map +1 -0
  34. package/dist/SimRuntime.d.ts +81 -0
  35. package/dist/SimRuntime.d.ts.map +1 -0
  36. package/dist/SimRuntime.js +91 -0
  37. package/dist/SimRuntime.js.map +1 -0
  38. package/dist/Smix.d.ts +22 -0
  39. package/dist/Smix.d.ts.map +1 -0
  40. package/dist/Smix.js +35 -0
  41. package/dist/Smix.js.map +1 -0
  42. package/dist/index.d.ts +11 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +16 -0
  45. package/dist/index.js.map +1 -0
  46. package/package.json +44 -0
  47. package/src/A11yNode.ts +62 -0
  48. package/src/App.ts +153 -0
  49. package/src/ExpectationFailure.ts +67 -0
  50. package/src/HttpRunner.ts +185 -0
  51. package/src/Locator.ts +190 -0
  52. package/src/Pattern.ts +51 -0
  53. package/src/Selector.ts +296 -0
  54. package/src/SelectorResolver.ts +76 -0
  55. package/src/SimRuntime.ts +145 -0
  56. package/src/Smix.ts +49 -0
  57. package/src/__tests__/AppFullApi.test.ts +168 -0
  58. package/src/__tests__/AppTapMock.test.ts +122 -0
  59. package/src/__tests__/HttpRunner.test.ts +121 -0
  60. package/src/__tests__/Locator.test.ts +128 -0
  61. package/src/__tests__/LocatorToHave.test.ts +118 -0
  62. package/src/__tests__/MvpApiShape.test.ts +117 -0
  63. package/src/__tests__/SelectorFullSchema.test.ts +161 -0
  64. package/src/index.ts +76 -0
@@ -0,0 +1,148 @@
1
+ // v7.5 c3 — HTTP-based SmixSimRuntime + SelectorResolver implementations.
2
+ //
3
+ // Per R-7.5-B HTTP-runtime decision: TS SDK communicates with smix-runner
4
+ // via fetch JSON-RPC-ish API (one POST per call). Default endpoint
5
+ // matches existing smix-runner-client conventions; consumers override
6
+ // via base URL constructor param.
7
+ //
8
+ // Wire contract (mirrors smix-runner-client v3+):
9
+ // POST /sim/launch { bundleId } → 200
10
+ // POST /sim/terminate { bundleId } → 200
11
+ // POST /sim/launch-fresh { bundleId, clearState, clearKeychain, appPath? } → 200
12
+ // POST /sim/launch-from-path { appPath } → 200
13
+ // POST /sim/screenshot {} → 200 { png: base64 }
14
+ // POST /sim/system-popups {} → 200 { nodes: A11yNode[] }
15
+ // POST /sim/open-url { url } → 200
16
+ // POST /a11y/snapshot {} → 200 { tree: A11yNode }
17
+ // POST /input/tap { x, y } → 200
18
+ // POST /input/tap-normalized { nx, ny } → 200
19
+ // POST /input/swipe { direction } → 200
20
+ // POST /input/send-string { text } → 200
21
+ // POST /input/press-key { key } → 200
22
+ // POST /select/resolve { treeJson, selectorJson } → 200 { ids: string[] }
23
+ //
24
+ // v7.7 c1 contract additions (server impl deferred):
25
+ // POST /select/resolve-count { treeJson, selectorJson } → 200 { count: u32 }
26
+ // POST /select/resolve-labels { treeJson, selectorJson } → 200 { labels: string[] }
27
+ //
28
+ // The exact endpoint shapes are negotiated with smix-runner-server in
29
+ // follow-up Swift work; for v7.5-v7.7 c1 we wire the TS-side fetch +
30
+ // decode and a MockHttpRunnerClient stub that lets vitest exercise the
31
+ // runtime without booting a real server.
32
+ /**
33
+ * SmixSimRuntime + SelectorResolver wrapper around the smix-runner HTTP
34
+ * server. Used in production RN/Expo test targets via fetch.
35
+ */
36
+ export class HttpSimRuntime {
37
+ baseUrl;
38
+ fetchImpl;
39
+ resolver;
40
+ /**
41
+ * v7.7 c1 — labels resolver wrapping Rust `resolve_selector_labels`
42
+ * via smix-runner-server `/select/resolve-labels` endpoint. Used by
43
+ * `Locator.toHaveCount` (count = labels.length) + Locator.toHaveLabel.
44
+ * Pass directly to App constructor or Smix.launchApp.
45
+ */
46
+ labelsResolver;
47
+ constructor(baseUrl, fetchImpl = globalThis.fetch) {
48
+ this.baseUrl = baseUrl;
49
+ this.fetchImpl = fetchImpl;
50
+ this.resolver = async (treeJson, selectorJson) => {
51
+ const r = await this.post('/select/resolve', { treeJson, selectorJson });
52
+ return r.ids;
53
+ };
54
+ this.labelsResolver = async (treeJson, selectorJson) => {
55
+ const r = await this.post('/select/resolve-labels', { treeJson, selectorJson });
56
+ return r.labels;
57
+ };
58
+ }
59
+ /**
60
+ * v7.7 c1 — direct match-count via dedicated FFI fn (avoid allocating
61
+ * the full ids list when callers only need count). Mirrors Rust
62
+ * `resolve_selector_count`. Locator.toHaveCount can prefer this over
63
+ * `labelsResolver` if it needs only count, not the labels themselves.
64
+ */
65
+ async resolveCount(treeJson, selectorJson) {
66
+ const r = await this.post('/select/resolve-count', { treeJson, selectorJson });
67
+ return r.count;
68
+ }
69
+ async launch(bundleId) {
70
+ await this.post('/sim/launch', { bundleId });
71
+ }
72
+ async terminate(bundleId) {
73
+ await this.post('/sim/terminate', { bundleId });
74
+ }
75
+ async snapshotTree() {
76
+ const r = await this.post('/a11y/snapshot', {});
77
+ return r.tree;
78
+ }
79
+ async synthesizeTap(x, y) {
80
+ await this.post('/input/tap', { x, y });
81
+ }
82
+ async sendString(text) {
83
+ await this.post('/input/send-string', { text });
84
+ }
85
+ async pressKey(key) {
86
+ await this.post('/input/press-key', { key });
87
+ }
88
+ async swipe(direction) {
89
+ await this.post('/input/swipe', { direction });
90
+ }
91
+ async screenshot() {
92
+ const r = await this.post('/sim/screenshot', {});
93
+ return base64ToBytes(r.png);
94
+ }
95
+ async systemPopups() {
96
+ const r = await this.post('/sim/system-popups', {});
97
+ return r.nodes;
98
+ }
99
+ async openUrl(url) {
100
+ await this.post('/sim/open-url', { url });
101
+ }
102
+ async launchFresh(opts) {
103
+ const payload = {
104
+ bundleId: opts.bundleId,
105
+ clearState: opts.clearState,
106
+ clearKeychain: opts.clearKeychain,
107
+ appPath: opts.appPath,
108
+ };
109
+ await this.post('/sim/launch-fresh', payload);
110
+ }
111
+ async launchFromPath(appPath) {
112
+ await this.post('/sim/launch-from-path', { appPath });
113
+ }
114
+ async synthesizeTapAtNormalized(nx, ny) {
115
+ await this.post('/input/tap-normalized', { nx, ny });
116
+ }
117
+ async post(path, body) {
118
+ const url = `${this.baseUrl}${path}`;
119
+ const resp = await this.fetchImpl(url, {
120
+ method: 'POST',
121
+ headers: { 'content-type': 'application/json' },
122
+ body: JSON.stringify(body),
123
+ });
124
+ if (!resp.ok) {
125
+ const text = await resp.text();
126
+ throw new Error(`smix-runner ${path} → HTTP ${resp.status}: ${text}`);
127
+ }
128
+ // 200 with no body returns undefined; tolerate empty response
129
+ try {
130
+ return await resp.json();
131
+ }
132
+ catch {
133
+ return undefined;
134
+ }
135
+ }
136
+ }
137
+ function base64ToBytes(b64) {
138
+ if (typeof globalThis.atob === 'function') {
139
+ const bin = globalThis.atob(b64);
140
+ const out = new Uint8Array(bin.length);
141
+ for (let i = 0; i < bin.length; i++)
142
+ out[i] = bin.charCodeAt(i);
143
+ return out;
144
+ }
145
+ // Node fallback
146
+ return Uint8Array.from(Buffer.from(b64, 'base64'));
147
+ }
148
+ //# sourceMappingURL=HttpRunner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HttpRunner.js","sourceRoot":"","sources":["../src/HttpRunner.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,EAAE;AACF,0EAA0E;AAC1E,mEAAmE;AACnE,sEAAsE;AACtE,kCAAkC;AAClC,EAAE;AACF,kDAAkD;AAClD,iFAAiF;AACjF,iFAAiF;AACjF,qFAAqF;AACrF,iFAAiF;AACjF,iGAAiG;AACjG,uGAAuG;AACvG,iFAAiF;AACjF,oGAAoG;AACpG,iFAAiF;AACjF,iFAAiF;AACjF,iFAAiF;AACjF,iFAAiF;AACjF,iFAAiF;AACjF,mGAAmG;AACnG,EAAE;AACF,qDAAqD;AACrD,gGAAgG;AAChG,sGAAsG;AACtG,EAAE;AACF,sEAAsE;AACtE,qEAAqE;AACrE,uEAAuE;AACvE,yCAAyC;AAoBzC;;;GAGG;AACH,MAAM,OAAO,cAAc;IAWP;IACA;IAXT,QAAQ,CAAkB;IACnC;;;;;OAKG;IACM,cAAc,CAAgB;IAEvC,YACkB,OAAe,EACf,YAAuB,UAAU,CAAC,KAA6B;QAD/D,YAAO,GAAP,OAAO,CAAQ;QACf,cAAS,GAAT,SAAS,CAAsD;QAE/E,IAAI,CAAC,QAAQ,GAAG,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE;YAC/C,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC,CAAA;YACxE,OAAQ,CAAgC,CAAC,GAAG,CAAA;QAC9C,CAAC,CAAA;QACD,IAAI,CAAC,cAAc,GAAG,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE;YACrD,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,wBAAwB,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC,CAAA;YAC/E,OAAQ,CAAmC,CAAC,MAAM,CAAA;QACpD,CAAC,CAAA;IACH,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,YAAY,CAAC,QAAgB,EAAE,YAAoB;QACvD,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,uBAAuB,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC,CAAA;QAC9E,OAAQ,CAAuB,CAAC,KAAK,CAAA;IACvC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,QAAgB;QAC3B,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAA;IAC9C,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,QAAgB;QAC9B,MAAM,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAA;IACjD,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAA;QAC/C,OAAQ,CAAwB,CAAC,IAAI,CAAA;IACvC,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,CAAS,EAAE,CAAS;QACtC,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;IACzC,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAY;QAC3B,MAAM,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;IACjD,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,GAAY;QACzB,MAAM,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,GAAG,EAAE,CAAC,CAAA;IAC9C,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,SAAyB;QACnC,MAAM,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,CAAC,CAAA;IAChD,CAAC;IAED,KAAK,CAAC,UAAU;QACd,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,CAAoB,CAAA;QACnE,OAAO,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;IAC7B,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAA0B,CAAA;QAC5E,OAAO,CAAC,CAAC,KAAK,CAAA;IAChB,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAW;QACvB,MAAM,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,GAAG,EAAE,CAAC,CAAA;IAC3C,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,IAKjB;QACC,MAAM,OAAO,GAAoB;YAC/B,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAA;QACD,MAAM,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAAA;IAC/C,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,OAAe;QAClC,MAAM,IAAI,CAAC,IAAI,CAAC,uBAAuB,EAAE,EAAE,OAAO,EAAE,CAAC,CAAA;IACvD,CAAC;IAED,KAAK,CAAC,yBAAyB,CAAC,EAAU,EAAE,EAAU;QACpD,MAAM,IAAI,CAAC,IAAI,CAAC,uBAAuB,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA;IACtD,CAAC;IAEO,KAAK,CAAC,IAAI,CAAC,IAAY,EAAE,IAAa;QAC5C,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,CAAA;QACpC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE;YACrC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;SAC3B,CAAC,CAAA;QACF,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACb,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAA;YAC9B,MAAM,IAAI,KAAK,CAAC,eAAe,IAAI,WAAW,IAAI,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAA;QACvE,CAAC;QACD,8DAA8D;QAC9D,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,IAAI,EAAE,CAAA;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAA;QAClB,CAAC;IACH,CAAC;CACF;AAED,SAAS,aAAa,CAAC,GAAW;IAChC,IAAI,OAAO,UAAU,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QAC1C,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAChC,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE;YAAE,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;QAC/D,OAAO,GAAG,CAAA;IACZ,CAAC;IACD,gBAAgB;IAChB,OAAO,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAA;AACpD,CAAC"}
@@ -0,0 +1,60 @@
1
+ import type { App } from './App.js';
2
+ import { type Selector } from './Selector.js';
3
+ export declare class Locator {
4
+ readonly app: App;
5
+ readonly selector: Selector;
6
+ constructor(app: App, selector: Selector);
7
+ /**
8
+ * Resolve [selector] until at least one match exists AND that match
9
+ * is `visible=true`. Polls every 250ms until [timeoutMs] expires.
10
+ */
11
+ toBeVisible(opts?: {
12
+ timeoutMs?: number;
13
+ }): Promise<void>;
14
+ /**
15
+ * Resolve [selector] until a match exists AND `text.includes(needle)`
16
+ * on label/title/text fields.
17
+ */
18
+ toContainText(text: string, opts?: {
19
+ timeoutMs?: number;
20
+ }): Promise<void>;
21
+ /**
22
+ * v7.6 c1 — Resolve [selector] until at least one match exists AND
23
+ * that match's label equals [label]. Polls via standard resolver +
24
+ * findById path (label is on the snapshot node, not on FFI's labels
25
+ * resolver which is more useful for count-based assertions).
26
+ */
27
+ toHaveLabel(label: string, opts?: {
28
+ timeoutMs?: number;
29
+ }): Promise<void>;
30
+ /**
31
+ * v7.6 c1 — Resolve [selector] until the labels resolver returns
32
+ * exactly [n] matches. Uses dedicated `app.labelsResolver` (wraps
33
+ * Rust `resolve_selector_labels`) so count semantics skip index
34
+ * modifier per Playwright "match all" behavior.
35
+ */
36
+ toHaveCount(n: number, opts?: {
37
+ timeoutMs?: number;
38
+ }): Promise<void>;
39
+ /**
40
+ * Generic 250ms poll loop. On each tick, resolve [selector] via the
41
+ * app's runtime + resolver, then check predicates.
42
+ *
43
+ * - [successPredicate] returns true → return immediately
44
+ * - [wrongStatePredicate] returns true → throw wrongState
45
+ * - Otherwise, continue polling.
46
+ *
47
+ * On timeout, throws timeout with the last tree's first-20 nodes.
48
+ */
49
+ private pollUntil;
50
+ }
51
+ /**
52
+ * Compile-time-known unimplemented surface (distinct from
53
+ * ExpectationFailure which is runtime resolution failure).
54
+ */
55
+ export declare class SmixNotImplementedError extends Error {
56
+ readonly stage: string;
57
+ readonly api: string;
58
+ constructor(stage: string, api: string);
59
+ }
60
+ //# sourceMappingURL=Locator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Locator.d.ts","sourceRoot":"","sources":["../src/Locator.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAEnC,OAAO,EAEL,KAAK,QAAQ,EACd,MAAM,eAAe,CAAA;AAKtB,qBAAa,OAAO;aAEA,GAAG,EAAE,GAAG;aACR,QAAQ,EAAE,QAAQ;gBADlB,GAAG,EAAE,GAAG,EACR,QAAQ,EAAE,QAAQ;IAGpC;;;OAGG;IACG,WAAW,CAAC,IAAI,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IASnE;;;OAGG;IACG,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAcnF;;;;;OAKG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IASlF;;;;;OAKG;IACG,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAqC9E;;;;;;;;;OASG;YACW,SAAS;CAoDxB;AAMD;;;GAGG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;aACpB,KAAK,EAAE,MAAM;aAAkB,GAAG,EAAE,MAAM;gBAA1C,KAAK,EAAE,MAAM,EAAkB,GAAG,EAAE,MAAM;CAIvE"}
@@ -0,0 +1,177 @@
1
+ // v7.5 c3 — Locator poll loop, mirror Kotlin v7.4 c3 + Swift v7.1 c3.
2
+ //
3
+ // 250ms tick over a millisecond deadline. On timeout, throws
4
+ // ExpectationFailure with the last tree's first-20 visible elements
5
+ // populated for AI agent diagnosis.
6
+ import { findById, flatten } from './A11yNode.js';
7
+ import { ExpectationFailure } from './ExpectationFailure.js';
8
+ import { encodeSelectorJson, } from './Selector.js';
9
+ const POLL_INTERVAL_MS = 250;
10
+ const DEFAULT_TIMEOUT_MS = 5_000;
11
+ export class Locator {
12
+ app;
13
+ selector;
14
+ constructor(app, selector) {
15
+ this.app = app;
16
+ this.selector = selector;
17
+ }
18
+ /**
19
+ * Resolve [selector] until at least one match exists AND that match
20
+ * is `visible=true`. Polls every 250ms until [timeoutMs] expires.
21
+ */
22
+ async toBeVisible(opts = {}) {
23
+ return this.pollUntil({
24
+ timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS,
25
+ wrongStatePredicate: node => node !== null && node.visible !== true,
26
+ successPredicate: node => node !== null && node.visible === true,
27
+ timeoutMessage: 'element never became visible',
28
+ });
29
+ }
30
+ /**
31
+ * Resolve [selector] until a match exists AND `text.includes(needle)`
32
+ * on label/title/text fields.
33
+ */
34
+ async toContainText(text, opts = {}) {
35
+ return this.pollUntil({
36
+ timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS,
37
+ wrongStatePredicate: () => false,
38
+ successPredicate: node => node !== null && (node.label?.includes(text) === true ||
39
+ node.title?.includes(text) === true ||
40
+ node.text?.includes(text) === true),
41
+ timeoutMessage: `element text never contained "${text}"`,
42
+ });
43
+ }
44
+ /**
45
+ * v7.6 c1 — Resolve [selector] until at least one match exists AND
46
+ * that match's label equals [label]. Polls via standard resolver +
47
+ * findById path (label is on the snapshot node, not on FFI's labels
48
+ * resolver which is more useful for count-based assertions).
49
+ */
50
+ async toHaveLabel(label, opts = {}) {
51
+ return this.pollUntil({
52
+ timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS,
53
+ wrongStatePredicate: () => false,
54
+ successPredicate: node => node !== null && node.label === label,
55
+ timeoutMessage: `element label never equaled "${label}"`,
56
+ });
57
+ }
58
+ /**
59
+ * v7.6 c1 — Resolve [selector] until the labels resolver returns
60
+ * exactly [n] matches. Uses dedicated `app.labelsResolver` (wraps
61
+ * Rust `resolve_selector_labels`) so count semantics skip index
62
+ * modifier per Playwright "match all" behavior.
63
+ */
64
+ async toHaveCount(n, opts = {}) {
65
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
66
+ const deadline = Date.now() + timeoutMs;
67
+ let lastCount = 0;
68
+ let lastTree = null;
69
+ const selectorJson = encodeSelectorJson(this.selector);
70
+ while (true) {
71
+ const tree = await this.app.runtime.snapshotTree();
72
+ lastTree = tree;
73
+ let labels;
74
+ try {
75
+ labels = await this.app.labelsResolver(JSON.stringify(tree), selectorJson);
76
+ }
77
+ catch (e) {
78
+ const msg = e instanceof Error ? e.message : String(e);
79
+ throw new ExpectationFailure({
80
+ code: 'unknown',
81
+ message: `FFI resolveSelectorLabels raised during poll: ${msg}`,
82
+ selectorJson,
83
+ });
84
+ }
85
+ lastCount = labels.length;
86
+ if (lastCount === n)
87
+ return;
88
+ if (Date.now() >= deadline)
89
+ break;
90
+ await delay(POLL_INTERVAL_MS);
91
+ }
92
+ throw new ExpectationFailure({
93
+ code: 'wrongState',
94
+ message: `expected ${n} matches, last poll saw ${lastCount}`,
95
+ selectorJson,
96
+ visibleElements: lastTree !== null ? flatten(lastTree).slice(0, 20) : [],
97
+ suggestions: [
98
+ 'verify the selector matches the expected count in the current tree',
99
+ 'consider extending the timeout if render is slow',
100
+ ],
101
+ });
102
+ }
103
+ /**
104
+ * Generic 250ms poll loop. On each tick, resolve [selector] via the
105
+ * app's runtime + resolver, then check predicates.
106
+ *
107
+ * - [successPredicate] returns true → return immediately
108
+ * - [wrongStatePredicate] returns true → throw wrongState
109
+ * - Otherwise, continue polling.
110
+ *
111
+ * On timeout, throws timeout with the last tree's first-20 nodes.
112
+ */
113
+ async pollUntil(opts) {
114
+ const deadline = Date.now() + opts.timeoutMs;
115
+ let lastTree = null;
116
+ while (true) {
117
+ const tree = await this.app.runtime.snapshotTree();
118
+ lastTree = tree;
119
+ const selectorJson = encodeSelectorJson(this.selector);
120
+ let matches;
121
+ try {
122
+ matches = await this.app.resolver(JSON.stringify(tree), selectorJson);
123
+ }
124
+ catch (e) {
125
+ const msg = e instanceof Error ? e.message : String(e);
126
+ throw new ExpectationFailure({
127
+ code: 'unknown',
128
+ message: `FFI resolveSelector raised during poll: ${msg}`,
129
+ selectorJson,
130
+ });
131
+ }
132
+ const firstId = matches[0];
133
+ const node = firstId !== undefined ? findById(tree, firstId) : null;
134
+ if (opts.wrongStatePredicate(node)) {
135
+ throw new ExpectationFailure({
136
+ code: 'wrongState',
137
+ message: 'element matched but predicate failed (e.g. not visible)',
138
+ selectorJson,
139
+ visibleElements: node !== null ? [node, ...flatten(tree).slice(0, 19)] : flatten(tree).slice(0, 20),
140
+ });
141
+ }
142
+ if (opts.successPredicate(node))
143
+ return;
144
+ if (Date.now() >= deadline)
145
+ break;
146
+ await delay(POLL_INTERVAL_MS);
147
+ }
148
+ throw new ExpectationFailure({
149
+ code: 'timeout',
150
+ message: opts.timeoutMessage,
151
+ selectorJson: encodeSelectorJson(this.selector),
152
+ visibleElements: lastTree !== null ? flatten(lastTree).slice(0, 20) : [],
153
+ suggestions: [
154
+ 'consider extending the timeout if the UI takes longer to settle',
155
+ 'verify the selector matches when manually inspected (use app.tree())',
156
+ ],
157
+ });
158
+ }
159
+ }
160
+ function delay(ms) {
161
+ return new Promise(resolve => setTimeout(resolve, ms));
162
+ }
163
+ /**
164
+ * Compile-time-known unimplemented surface (distinct from
165
+ * ExpectationFailure which is runtime resolution failure).
166
+ */
167
+ export class SmixNotImplementedError extends Error {
168
+ stage;
169
+ api;
170
+ constructor(stage, api) {
171
+ super(`${api} not implemented yet (lands ${stage})`);
172
+ this.stage = stage;
173
+ this.api = api;
174
+ this.name = 'SmixNotImplementedError';
175
+ }
176
+ }
177
+ //# sourceMappingURL=Locator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Locator.js","sourceRoot":"","sources":["../src/Locator.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,EAAE;AACF,6DAA6D;AAC7D,oEAAoE;AACpE,oCAAoC;AAEpC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAiB,MAAM,eAAe,CAAA;AAEhE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAC5D,OAAO,EACL,kBAAkB,GAEnB,MAAM,eAAe,CAAA;AAEtB,MAAM,gBAAgB,GAAG,GAAG,CAAA;AAC5B,MAAM,kBAAkB,GAAG,KAAK,CAAA;AAEhC,MAAM,OAAO,OAAO;IAEA;IACA;IAFlB,YACkB,GAAQ,EACR,QAAkB;QADlB,QAAG,GAAH,GAAG,CAAK;QACR,aAAQ,GAAR,QAAQ,CAAU;IACjC,CAAC;IAEJ;;;OAGG;IACH,KAAK,CAAC,WAAW,CAAC,OAA+B,EAAE;QACjD,OAAO,IAAI,CAAC,SAAS,CAAC;YACpB,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,kBAAkB;YAC/C,mBAAmB,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI;YACnE,gBAAgB,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI;YAChE,cAAc,EAAE,8BAA8B;SAC/C,CAAC,CAAA;IACJ,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,aAAa,CAAC,IAAY,EAAE,OAA+B,EAAE;QACjE,OAAO,IAAI,CAAC,SAAS,CAAC;YACpB,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,kBAAkB;YAC/C,mBAAmB,EAAE,GAAG,EAAE,CAAC,KAAK;YAChC,gBAAgB,EAAE,IAAI,CAAC,EAAE,CACvB,IAAI,KAAK,IAAI,IAAI,CACf,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI;gBACnC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI;gBACnC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,CACnC;YACH,cAAc,EAAE,iCAAiC,IAAI,GAAG;SACzD,CAAC,CAAA;IACJ,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,WAAW,CAAC,KAAa,EAAE,OAA+B,EAAE;QAChE,OAAO,IAAI,CAAC,SAAS,CAAC;YACpB,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,kBAAkB;YAC/C,mBAAmB,EAAE,GAAG,EAAE,CAAC,KAAK;YAChC,gBAAgB,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK;YAC/D,cAAc,EAAE,gCAAgC,KAAK,GAAG;SACzD,CAAC,CAAA;IACJ,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,WAAW,CAAC,CAAS,EAAE,OAA+B,EAAE;QAC5D,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,kBAAkB,CAAA;QACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAA;QACvC,IAAI,SAAS,GAAG,CAAC,CAAA;QACjB,IAAI,QAAQ,GAAoB,IAAI,CAAA;QACpC,MAAM,YAAY,GAAG,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACtD,OAAO,IAAI,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,CAAA;YAClD,QAAQ,GAAG,IAAI,CAAA;YACf,IAAI,MAAyB,CAAA;YAC7B,IAAI,CAAC;gBACH,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,YAAY,CAAC,CAAA;YAC5E,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;gBACtD,MAAM,IAAI,kBAAkB,CAAC;oBAC3B,IAAI,EAAE,SAAS;oBACf,OAAO,EAAE,iDAAiD,GAAG,EAAE;oBAC/D,YAAY;iBACb,CAAC,CAAA;YACJ,CAAC;YACD,SAAS,GAAG,MAAM,CAAC,MAAM,CAAA;YACzB,IAAI,SAAS,KAAK,CAAC;gBAAE,OAAM;YAC3B,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,QAAQ;gBAAE,MAAK;YACjC,MAAM,KAAK,CAAC,gBAAgB,CAAC,CAAA;QAC/B,CAAC;QACD,MAAM,IAAI,kBAAkB,CAAC;YAC3B,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,YAAY,CAAC,2BAA2B,SAAS,EAAE;YAC5D,YAAY;YACZ,eAAe,EAAE,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;YACxE,WAAW,EAAE;gBACX,oEAAoE;gBACpE,kDAAkD;aACnD;SACF,CAAC,CAAA;IACJ,CAAC;IAED;;;;;;;;;OASG;IACK,KAAK,CAAC,SAAS,CAAC,IAKvB;QACC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAA;QAC5C,IAAI,QAAQ,GAAoB,IAAI,CAAA;QAEpC,OAAO,IAAI,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,CAAA;YAClD,QAAQ,GAAG,IAAI,CAAA;YACf,MAAM,YAAY,GAAG,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;YACtD,IAAI,OAA0B,CAAA;YAC9B,IAAI,CAAC;gBACH,OAAO,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,YAAY,CAAC,CAAA;YACvE,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;gBACtD,MAAM,IAAI,kBAAkB,CAAC;oBAC3B,IAAI,EAAE,SAAS;oBACf,OAAO,EAAE,2CAA2C,GAAG,EAAE;oBACzD,YAAY;iBACb,CAAC,CAAA;YACJ,CAAC;YACD,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;YAC1B,MAAM,IAAI,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;YAEnE,IAAI,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;gBACnC,MAAM,IAAI,kBAAkB,CAAC;oBAC3B,IAAI,EAAE,YAAY;oBAClB,OAAO,EAAE,yDAAyD;oBAClE,YAAY;oBACZ,eAAe,EAAE,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;iBACpG,CAAC,CAAA;YACJ,CAAC;YACD,IAAI,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC;gBAAE,OAAM;YAEvC,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,QAAQ;gBAAE,MAAK;YACjC,MAAM,KAAK,CAAC,gBAAgB,CAAC,CAAA;QAC/B,CAAC;QAED,MAAM,IAAI,kBAAkB,CAAC;YAC3B,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,IAAI,CAAC,cAAc;YAC5B,YAAY,EAAE,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC;YAC/C,eAAe,EAAE,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;YACxE,WAAW,EAAE;gBACX,iEAAiE;gBACjE,sEAAsE;aACvE;SACF,CAAC,CAAA;IACJ,CAAC;CACF;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAA;AACxD,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IACpB;IAA+B;IAA3D,YAA4B,KAAa,EAAkB,GAAW;QACpE,KAAK,CAAC,GAAG,GAAG,+BAA+B,KAAK,GAAG,CAAC,CAAA;QAD1B,UAAK,GAAL,KAAK,CAAQ;QAAkB,QAAG,GAAH,GAAG,CAAQ;QAEpE,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAA;IACvC,CAAC;CACF"}
@@ -0,0 +1,21 @@
1
+ export type Pattern = {
2
+ kind: 'literal';
3
+ value: string;
4
+ } | {
5
+ kind: 'regex';
6
+ regex: string;
7
+ flags: string;
8
+ };
9
+ export declare function literal(value: string): Pattern;
10
+ export declare function regex(pat: string, flags?: string): Pattern;
11
+ /**
12
+ * Encode a Pattern to its Rust-compatible untagged JSON form
13
+ * (bare string for literal / `{regex, flags}` object for regex).
14
+ * Returns a JSON-encodable value (not a JSON string).
15
+ */
16
+ export declare function patternToJson(p: Pattern): unknown;
17
+ /**
18
+ * Decode a JSON value into a Pattern. Throws on unrecognized shape.
19
+ */
20
+ export declare function patternFromJson(value: unknown): Pattern;
21
+ //# sourceMappingURL=Pattern.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Pattern.d.ts","sourceRoot":"","sources":["../src/Pattern.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,OAAO,GACf;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAEnD,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,SAAM,GAAG,OAAO,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAOjD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAcvD"}
@@ -0,0 +1,44 @@
1
+ // v7.5 c1 — Pattern (literal vs regex) mirror Swift v7.2 c3 + Kotlin v7.4 c4
2
+ // + Rust smix-selector `#[serde(untagged)] enum Pattern`.
3
+ //
4
+ // Wire JSON forms (untagged):
5
+ // Literal: bare string e.g. "hello"
6
+ // Regex: {"regex":"...","flags":"i"} (flags default "i")
7
+ export function literal(value) {
8
+ return { kind: 'literal', value };
9
+ }
10
+ export function regex(pat, flags = 'i') {
11
+ return { kind: 'regex', regex: pat, flags };
12
+ }
13
+ /**
14
+ * Encode a Pattern to its Rust-compatible untagged JSON form
15
+ * (bare string for literal / `{regex, flags}` object for regex).
16
+ * Returns a JSON-encodable value (not a JSON string).
17
+ */
18
+ export function patternToJson(p) {
19
+ switch (p.kind) {
20
+ case 'literal':
21
+ return p.value;
22
+ case 'regex':
23
+ return { regex: p.regex, flags: p.flags };
24
+ }
25
+ }
26
+ /**
27
+ * Decode a JSON value into a Pattern. Throws on unrecognized shape.
28
+ */
29
+ export function patternFromJson(value) {
30
+ if (typeof value === 'string') {
31
+ return { kind: 'literal', value };
32
+ }
33
+ if (typeof value === 'object' && value !== null && 'regex' in value) {
34
+ const obj = value;
35
+ const rx = obj.regex;
36
+ if (typeof rx !== 'string') {
37
+ throw new Error(`Pattern.regex must be string, got: ${typeof rx}`);
38
+ }
39
+ const flags = typeof obj.flags === 'string' ? obj.flags : 'i';
40
+ return { kind: 'regex', regex: rx, flags };
41
+ }
42
+ throw new Error(`Pattern must be string or {regex, flags?} object, got: ${JSON.stringify(value)}`);
43
+ }
44
+ //# sourceMappingURL=Pattern.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Pattern.js","sourceRoot":"","sources":["../src/Pattern.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,0DAA0D;AAC1D,EAAE;AACF,8BAA8B;AAC9B,wDAAwD;AACxD,gEAAgE;AAMhE,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAA;AACnC,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,GAAW,EAAE,KAAK,GAAG,GAAG;IAC5C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAA;AAC7C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,CAAU;IACtC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;QACf,KAAK,SAAS;YACZ,OAAO,CAAC,CAAC,KAAK,CAAA;QAChB,KAAK,OAAO;YACV,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAA;IAC7C,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAA;IACnC,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,EAAE,CAAC;QACpE,MAAM,GAAG,GAAG,KAAgC,CAAA;QAC5C,MAAM,EAAE,GAAG,GAAG,CAAC,KAAK,CAAA;QACpB,IAAI,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,sCAAsC,OAAO,EAAE,EAAE,CAAC,CAAA;QACpE,CAAC;QACD,MAAM,KAAK,GAAG,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAA;QAC7D,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,CAAA;IAC5C,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,0DAA0D,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;AACpG,CAAC"}
@@ -0,0 +1,96 @@
1
+ import { type Pattern } from './Pattern.js';
2
+ /** All-optional modifier set stacked onto a base Selector. */
3
+ export interface Modifiers {
4
+ near?: Selector | undefined;
5
+ below?: Selector | undefined;
6
+ above?: Selector | undefined;
7
+ leftOf?: Selector | undefined;
8
+ rightOf?: Selector | undefined;
9
+ inside?: Selector | undefined;
10
+ ancestor?: Selector | undefined;
11
+ nth?: number | undefined;
12
+ first?: boolean | undefined;
13
+ last?: boolean | undefined;
14
+ }
15
+ /** Spatial-only anchor box (no index, no base). Used by `anchor()`. */
16
+ export interface AnchorBox {
17
+ near?: Selector | undefined;
18
+ below?: Selector | undefined;
19
+ above?: Selector | undefined;
20
+ leftOf?: Selector | undefined;
21
+ rightOf?: Selector | undefined;
22
+ inside?: Selector | undefined;
23
+ ancestor?: Selector | undefined;
24
+ }
25
+ /** Index-only modifier subset used by `anchor()`. */
26
+ export interface IndexModifiers {
27
+ nth?: number | undefined;
28
+ first?: boolean | undefined;
29
+ last?: boolean | undefined;
30
+ }
31
+ export type SelectorKind = 'id' | 'text' | 'label' | 'role' | 'focused' | 'anchor' | 'localizedText';
32
+ /** Discriminated union (kind tag for narrowing; wire-shape is untagged). */
33
+ export type SelectorData = {
34
+ kind: 'id';
35
+ id: string;
36
+ modifiers: Modifiers;
37
+ } | {
38
+ kind: 'text';
39
+ text: Pattern;
40
+ modifiers: Modifiers;
41
+ } | {
42
+ kind: 'label';
43
+ label: string;
44
+ modifiers: Modifiers;
45
+ } | {
46
+ kind: 'role';
47
+ role: string;
48
+ name?: Pattern | undefined;
49
+ modifiers: Modifiers;
50
+ } | {
51
+ kind: 'focused';
52
+ } | {
53
+ kind: 'anchor';
54
+ box: AnchorBox;
55
+ index: IndexModifiers;
56
+ } | {
57
+ kind: 'localizedText';
58
+ map: Readonly<Record<string, string>>;
59
+ modifiers: Modifiers;
60
+ };
61
+ /**
62
+ * A11y selector — identifies a node in the tree.
63
+ *
64
+ * Wire JSON shape matches Rust smix-selector `#[serde(untagged)]`:
65
+ * discriminator is which key is present. Modifiers are flattened
66
+ * into the same JSON body. Implemented via [selectorToJson].
67
+ */
68
+ export declare class Selector {
69
+ readonly data: SelectorData;
70
+ constructor(data: SelectorData);
71
+ static id(value: string): Selector;
72
+ static text(p: Pattern): Selector;
73
+ static label(value: string): Selector;
74
+ static role(value: string, name?: Pattern): Selector;
75
+ static focused(): Selector;
76
+ static anchor(box: AnchorBox, index?: IndexModifiers): Selector;
77
+ static localizedText(map: Record<string, string>): Selector;
78
+ below(anchor: Selector): Selector;
79
+ above(anchor: Selector): Selector;
80
+ leftOf(anchor: Selector): Selector;
81
+ rightOf(anchor: Selector): Selector;
82
+ near(anchor: Selector): Selector;
83
+ inside(anchor: Selector): Selector;
84
+ ancestor(anchor: Selector): Selector;
85
+ nth(i: number): Selector;
86
+ first(): Selector;
87
+ last(): Selector;
88
+ private withMods;
89
+ }
90
+ /** Encode a Selector to its Rust-compatible untagged JSON value. */
91
+ export declare function selectorToJsonValue(s: Selector): unknown;
92
+ /** Encode a Selector to its Rust-compatible untagged JSON string. */
93
+ export declare function encodeSelectorJson(s: Selector): string;
94
+ /** Decode a Rust-compatible untagged JSON value into a Selector. */
95
+ export declare function selectorFromJsonValue(value: unknown): Selector;
96
+ //# sourceMappingURL=Selector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Selector.d.ts","sourceRoot":"","sources":["../src/Selector.ts"],"names":[],"mappings":"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"}