@reticlehq/browser 2.13.1 → 3.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 (244) hide show
  1. package/dist/actions/actions-dom.d.ts +8 -20
  2. package/dist/actions/actions-dom.js +70 -69
  3. package/dist/actions/actions-press.d.ts +14 -0
  4. package/dist/actions/actions-press.js +123 -0
  5. package/dist/actions/actions.d.ts +1 -89
  6. package/dist/actions/actions.js +64 -365
  7. package/dist/actions/appeared-text.d.ts +0 -20
  8. package/dist/actions/appeared-text.js +6 -36
  9. package/dist/actions/danger-context.d.ts +3 -0
  10. package/dist/actions/danger-context.js +39 -0
  11. package/dist/actions/scroll.d.ts +8 -10
  12. package/dist/actions/scroll.js +28 -17
  13. package/dist/actions/synthetic/synthetic-input.d.ts +2 -0
  14. package/dist/actions/synthetic/synthetic-input.js +13 -0
  15. package/dist/actions/value-input.d.ts +0 -30
  16. package/dist/actions/value-input.js +0 -33
  17. package/dist/commands/commands.d.ts +0 -2
  18. package/dist/commands/commands.js +10 -70
  19. package/dist/connect-options.d.ts +2 -96
  20. package/dist/dom/a11y.d.ts +0 -29
  21. package/dist/dom/a11y.js +3 -90
  22. package/dist/dom/addressing/auto-anchor.d.ts +25 -0
  23. package/dist/dom/addressing/auto-anchor.js +41 -0
  24. package/dist/dom/addressing/refs.d.ts +14 -0
  25. package/dist/dom/addressing/refs.js +114 -0
  26. package/dist/dom/addressing/source.d.ts +9 -0
  27. package/dist/dom/addressing/source.js +29 -0
  28. package/dist/dom/chart.d.ts +0 -61
  29. package/dist/dom/chart.js +0 -60
  30. package/dist/dom/desktop-capture.d.ts +0 -30
  31. package/dist/dom/desktop-capture.js +1 -73
  32. package/dist/dom/dom-ignore.d.ts +0 -30
  33. package/dist/dom/dom-ignore.js +1 -48
  34. package/dist/dom/occlusion.d.ts +0 -7
  35. package/dist/dom/occlusion.js +31 -12
  36. package/dist/dom/paint-context.d.ts +6 -0
  37. package/dist/dom/paint-context.js +38 -0
  38. package/dist/dom/query.d.ts +0 -14
  39. package/dist/dom/query.js +50 -216
  40. package/dist/dom/realm.d.ts +0 -20
  41. package/dist/dom/realm.js +0 -28
  42. package/dist/dom/shadow-registry.d.ts +0 -33
  43. package/dist/dom/shadow-registry.js +2 -72
  44. package/dist/dom/snapshot.d.ts +2 -43
  45. package/dist/dom/snapshot.js +27 -152
  46. package/dist/dom/theme.d.ts +0 -37
  47. package/dist/dom/theme.js +2 -52
  48. package/dist/dom/virtualized.d.ts +0 -17
  49. package/dist/dom/virtualized.js +0 -27
  50. package/dist/edit-epoch.d.ts +1 -23
  51. package/dist/edit-epoch.js +1 -53
  52. package/dist/index.d.ts +4 -5
  53. package/dist/index.js +4 -28
  54. package/dist/observers/animation.d.ts +0 -1
  55. package/dist/observers/animation.js +1 -4
  56. package/dist/observers/blind-spots.d.ts +0 -26
  57. package/dist/observers/blind-spots.js +3 -62
  58. package/dist/observers/console.d.ts +1 -2
  59. package/dist/observers/console.js +41 -30
  60. package/dist/observers/context-open.d.ts +1 -11
  61. package/dist/observers/context-open.js +10 -14
  62. package/dist/observers/departure.d.ts +3 -0
  63. package/dist/observers/departure.js +52 -0
  64. package/dist/observers/dialogs.d.ts +2 -0
  65. package/dist/observers/dialogs.js +39 -0
  66. package/dist/observers/dom.js +28 -52
  67. package/dist/observers/download.d.ts +1 -2
  68. package/dist/observers/download.js +39 -56
  69. package/dist/observers/focus.d.ts +0 -6
  70. package/dist/observers/focus.js +0 -7
  71. package/dist/observers/frames.d.ts +0 -9
  72. package/dist/observers/frames.js +1 -28
  73. package/dist/observers/health.d.ts +0 -5
  74. package/dist/observers/health.js +2 -34
  75. package/dist/observers/install-all.d.ts +2 -0
  76. package/dist/observers/install-all.js +26 -27
  77. package/dist/observers/ipc.d.ts +0 -25
  78. package/dist/observers/ipc.js +0 -65
  79. package/dist/observers/navigation.d.ts +2 -0
  80. package/dist/observers/navigation.js +33 -0
  81. package/dist/observers/net-detail/network-body.d.ts +12 -0
  82. package/dist/observers/net-detail/network-body.js +71 -0
  83. package/dist/observers/net-detail/network-redact.d.ts +1 -0
  84. package/dist/observers/net-detail/network-redact.js +1 -0
  85. package/dist/observers/network-stream.d.ts +0 -21
  86. package/dist/observers/network-stream.js +3 -46
  87. package/dist/observers/network.d.ts +3 -27
  88. package/dist/observers/network.js +178 -295
  89. package/dist/observers/perf.d.ts +0 -10
  90. package/dist/observers/perf.js +0 -16
  91. package/dist/observers/route.d.ts +1 -2
  92. package/dist/observers/route.js +13 -19
  93. package/dist/observers/scroll.d.ts +0 -1
  94. package/dist/observers/scroll.js +2 -11
  95. package/dist/observers/sdk-failure.d.ts +1 -9
  96. package/dist/observers/sdk-failure.js +1 -30
  97. package/dist/observers/state.d.ts +1 -11
  98. package/dist/observers/state.js +13 -41
  99. package/dist/observers/storage.d.ts +0 -14
  100. package/dist/observers/storage.js +10 -34
  101. package/dist/observers/types.d.ts +0 -15
  102. package/dist/observers/types.js +0 -14
  103. package/dist/patching/capture-method.d.ts +0 -24
  104. package/dist/patching/capture-method.js +0 -26
  105. package/dist/presenter/chrome/layers.d.ts +2 -0
  106. package/dist/presenter/chrome/layers.js +2 -0
  107. package/dist/presenter/chrome/log-kinds.d.ts +12 -0
  108. package/dist/presenter/chrome/log-kinds.js +7 -0
  109. package/dist/presenter/{overlay.d.ts → chrome/overlay.d.ts} +0 -1
  110. package/dist/presenter/{overlay.js → chrome/overlay.js} +0 -1
  111. package/dist/presenter/chrome/presenter-brand.d.ts +3 -0
  112. package/dist/presenter/{presenter-brand.js → chrome/presenter-brand.js} +0 -21
  113. package/dist/presenter/chrome/presenter-effects.d.ts +4 -0
  114. package/dist/presenter/{presenter-effects.js → chrome/presenter-effects.js} +1 -10
  115. package/dist/presenter/chrome/presenter-hud-chrome.d.ts +7 -0
  116. package/dist/presenter/{presenter-hud-chrome.js → chrome/presenter-hud-chrome.js} +0 -10
  117. package/dist/presenter/chrome/presenter-log.d.ts +17 -0
  118. package/dist/presenter/{presenter-log.js → chrome/presenter-log.js} +5 -54
  119. package/dist/presenter/chrome/presenter-report-copy.d.ts +51 -0
  120. package/dist/presenter/chrome/presenter-report-copy.js +95 -0
  121. package/dist/presenter/chrome/presenter-safe-html.d.ts +2 -0
  122. package/dist/presenter/chrome/presenter-safe-html.js +16 -0
  123. package/dist/presenter/{presenter-tally.d.ts → chrome/presenter-tally.d.ts} +0 -5
  124. package/dist/presenter/{presenter-tally.js → chrome/presenter-tally.js} +1 -5
  125. package/dist/presenter/{presenter-verbs.js → chrome/presenter-verbs.js} +0 -7
  126. package/dist/presenter/{presenter-heroicons-data.d.ts → icons/presenter-heroicons-data.d.ts} +1 -8
  127. package/dist/presenter/{presenter-heroicons-data.js → icons/presenter-heroicons-data.js} +1 -8
  128. package/dist/presenter/{presenter-icons.d.ts → icons/presenter-icons.d.ts} +1 -6
  129. package/dist/presenter/{presenter-icons.js → icons/presenter-icons.js} +1 -6
  130. package/dist/presenter/presenter-account-styles.d.ts +1 -0
  131. package/dist/presenter/presenter-account-styles.js +99 -0
  132. package/dist/presenter/presenter-account.d.ts +40 -0
  133. package/dist/presenter/presenter-account.js +178 -0
  134. package/dist/presenter/presenter-config.d.ts +10 -107
  135. package/dist/presenter/presenter-config.js +5 -92
  136. package/dist/presenter/presenter-controls.d.ts +1 -35
  137. package/dist/presenter/presenter-controls.js +10 -133
  138. package/dist/presenter/presenter-dock-layout.d.ts +0 -3
  139. package/dist/presenter/presenter-dock-layout.js +1 -5
  140. package/dist/presenter/presenter-drag.d.ts +2 -42
  141. package/dist/presenter/presenter-drag.js +3 -76
  142. package/dist/presenter/presenter-glow.d.ts +0 -21
  143. package/dist/presenter/presenter-glow.js +2 -28
  144. package/dist/presenter/presenter-hud-position.d.ts +18 -0
  145. package/dist/presenter/presenter-hud-position.js +50 -0
  146. package/dist/presenter/presenter-report-styles.d.ts +0 -6
  147. package/dist/presenter/presenter-report-styles.js +22 -7
  148. package/dist/presenter/presenter-report.d.ts +5 -7
  149. package/dist/presenter/presenter-report.js +44 -95
  150. package/dist/presenter/presenter-run-state.d.ts +1 -8
  151. package/dist/presenter/presenter-run-state.js +1 -6
  152. package/dist/presenter/presenter-settings-styles.d.ts +0 -1
  153. package/dist/presenter/presenter-settings-styles.js +2 -2
  154. package/dist/presenter/presenter-settings.d.ts +5 -23
  155. package/dist/presenter/presenter-settings.js +21 -33
  156. package/dist/presenter/presenter-shell-styles.js +23 -20
  157. package/dist/presenter/presenter-shell.d.ts +6 -18
  158. package/dist/presenter/presenter-shell.js +26 -77
  159. package/dist/presenter/presenter-styles.d.ts +2 -10
  160. package/dist/presenter/presenter-styles.js +6 -12
  161. package/dist/presenter/presenter-workspace.d.ts +4 -4
  162. package/dist/presenter/presenter-workspace.js +30 -10
  163. package/dist/presenter/presenter.d.ts +3 -63
  164. package/dist/presenter/presenter.js +29 -132
  165. package/dist/presenter/tour/tour-view.d.ts +33 -0
  166. package/dist/presenter/tour/tour-view.js +188 -0
  167. package/dist/presenter/tour/tour.d.ts +25 -0
  168. package/dist/presenter/tour/tour.js +245 -0
  169. package/dist/recorder/recorder-styles.d.ts +0 -6
  170. package/dist/recorder/recorder-styles.js +0 -6
  171. package/dist/recorder/recorder.d.ts +0 -34
  172. package/dist/recorder/recorder.js +6 -49
  173. package/dist/registry/auto-testids.d.ts +0 -19
  174. package/dist/registry/auto-testids.js +1 -21
  175. package/dist/registry/capabilities.d.ts +0 -31
  176. package/dist/registry/capabilities.js +1 -46
  177. package/dist/registry/commit-and-signal.d.ts +0 -6
  178. package/dist/registry/commit-and-signal.js +1 -7
  179. package/dist/registry/declared-channels.d.ts +2 -0
  180. package/dist/registry/declared-channels.js +14 -0
  181. package/dist/registry/declared-platform.d.ts +4 -0
  182. package/dist/registry/declared-platform.js +34 -0
  183. package/dist/registry/domains.d.ts +0 -8
  184. package/dist/registry/domains.js +0 -7
  185. package/dist/registry/emitter.d.ts +0 -14
  186. package/dist/registry/emitter.js +1 -11
  187. package/dist/registry/store-adapters.d.ts +0 -186
  188. package/dist/registry/store-adapters.js +1 -164
  189. package/dist/registry/{adapters.d.ts → stores/adapters.d.ts} +0 -10
  190. package/dist/registry/{adapters.js → stores/adapters.js} +0 -10
  191. package/dist/registry/stores.d.ts +0 -33
  192. package/dist/registry/stores.js +1 -60
  193. package/dist/reticle-presenter-helpers.d.ts +0 -13
  194. package/dist/reticle-presenter-helpers.js +10 -20
  195. package/dist/reticle-singleton.d.ts +2 -0
  196. package/dist/reticle-singleton.js +3 -0
  197. package/dist/reticle.d.ts +1 -52
  198. package/dist/reticle.js +115 -180
  199. package/dist/review/annotator-styles.js +1 -8
  200. package/dist/review/annotator.d.ts +2 -13
  201. package/dist/review/annotator.js +2 -42
  202. package/dist/review/mark-anchor.d.ts +0 -11
  203. package/dist/review/mark-anchor.js +3 -11
  204. package/dist/security/serialization.d.ts +0 -20
  205. package/dist/security/serialization.js +1 -66
  206. package/dist/session-continuity.d.ts +0 -16
  207. package/dist/session-continuity.js +1 -20
  208. package/dist/timers/clock.d.ts +1 -14
  209. package/dist/timers/clock.js +59 -60
  210. package/dist/timers/console/native-console.js +5 -0
  211. package/dist/timers/native/native-timers.d.ts +11 -0
  212. package/dist/timers/native/native-timers.js +48 -0
  213. package/dist/transport/transport.d.ts +0 -51
  214. package/dist/transport/transport.js +5 -131
  215. package/dist/transport/unreachable-message.d.ts +0 -17
  216. package/dist/transport/unreachable-message.js +0 -17
  217. package/dist/util/captured-method.d.ts +0 -35
  218. package/dist/util/captured-method.js +0 -35
  219. package/package.json +5 -4
  220. package/dist/actions/synthetic-input.d.ts +0 -9
  221. package/dist/actions/synthetic-input.js +0 -40
  222. package/dist/dom/auto-anchor.d.ts +0 -65
  223. package/dist/dom/auto-anchor.js +0 -74
  224. package/dist/dom/refs.d.ts +0 -90
  225. package/dist/dom/refs.js +0 -245
  226. package/dist/dom/source.d.ts +0 -53
  227. package/dist/dom/source.js +0 -74
  228. package/dist/observers/network-body.d.ts +0 -35
  229. package/dist/observers/network-body.js +0 -168
  230. package/dist/observers/network-redact.d.ts +0 -20
  231. package/dist/observers/network-redact.js +0 -80
  232. package/dist/presenter/presenter-brand.d.ts +0 -22
  233. package/dist/presenter/presenter-effects.d.ts +0 -13
  234. package/dist/presenter/presenter-hud-chrome.d.ts +0 -17
  235. package/dist/presenter/presenter-log.d.ts +0 -64
  236. package/dist/presenter/presenter-report-copy.d.ts +0 -112
  237. package/dist/presenter/presenter-report-copy.js +0 -156
  238. package/dist/presenter/presenter-test-helpers.d.ts +0 -16
  239. package/dist/presenter/presenter-test-helpers.js +0 -41
  240. package/dist/timers/native-console.js +0 -9
  241. package/dist/timers/native-timers.d.ts +0 -36
  242. package/dist/timers/native-timers.js +0 -78
  243. /package/dist/presenter/{presenter-verbs.d.ts → chrome/presenter-verbs.d.ts} +0 -0
  244. /package/dist/timers/{native-console.d.ts → console/native-console.d.ts} +0 -0
@@ -1,22 +1,6 @@
1
- import { nativeWarn } from '../timers/native-console.js';
1
+ import { nativeWarn } from '../timers/console/native-console.js';
2
2
  import { markAdapterSource } from './stores.js';
3
- /**
4
- * Expose a TanStack Query cache as a Reticle store, keyed by query key.
5
- *
6
- * `isStale` / `fetchStatus` / `dataUpdatedAt` are carried deliberately: they are what let an agent
7
- * assert the stronger property — not merely "the value rendered is X" but "the cache the UI rendered
8
- * from was actually fresh". A screenshot cannot distinguish a correct number from a correct-looking
9
- * stale one, and neither can a network log when the request never fired.
10
- *
11
- * ```ts
12
- * registerStore('queries', tanstackQueryStore(queryClient));
13
- * ```
14
- */
15
3
  export function tanstackQueryStore(client) {
16
- // Resolved per call, not captured once. A QueryClient can be rebuilt — React Strict Mode double
17
- // effects, a provider remount, HMR — and an adapter holding the old cache would keep answering
18
- // from a store the app no longer reads, which is a stale-data bug inside the tool whose job is
19
- // catching stale data.
20
4
  const store = {
21
5
  getState: () => {
22
6
  const out = {};
@@ -35,22 +19,9 @@ export function tanstackQueryStore(client) {
35
19
  },
36
20
  subscribe: (listener) => client.getQueryCache().subscribe(listener),
37
21
  };
38
- // Registering this adapter claims the CLIENT too, so fiber-tree discovery can tell that the app
39
- // already wired this exact QueryClient and must not add a second entry for it.
40
22
  markAdapterSource(store, client);
41
23
  return store;
42
24
  }
43
- /**
44
- * Expose a chosen set of Jotai atoms as one Reticle store.
45
- *
46
- * The atom map is not an ergonomic shortcut, it is forced by the design: Jotai has no registry of live
47
- * atoms to enumerate, so "the whole store" is not a thing that exists. Naming the atoms you care about
48
- * is the only way to snapshot them, and it doubles as a declaration of what matters.
49
- *
50
- * ```ts
51
- * registerStore('app', jotaiStore(getDefaultStore(), { cart: cartAtom, user: userAtom }));
52
- * ```
53
- */
54
25
  export function jotaiStore(store, atoms) {
55
26
  return {
56
27
  getState: () => {
@@ -68,10 +39,6 @@ export function jotaiStore(store, atoms) {
68
39
  },
69
40
  };
70
41
  }
71
- /**
72
- * Expose an XState actor as a Reticle store. Its `subscribe` returns a subscription OBJECT rather than
73
- * an unsubscribe function, which is the single reason it does not satisfy `StoreLike` already.
74
- */
75
42
  export function xstateStore(actor) {
76
43
  return {
77
44
  getState: () => actor.getSnapshot(),
@@ -81,41 +48,18 @@ export function xstateStore(actor) {
81
48
  },
82
49
  };
83
50
  }
84
- /**
85
- * Expose a Valtio proxy as a Reticle store. Valtio ships `snapshot`/`subscribe` as free functions
86
- * rather than methods, so the caller passes them in — importing them here would make every consumer
87
- * of this file depend on valtio.
88
- *
89
- * ```ts
90
- * import { snapshot, subscribe } from 'valtio/vanilla';
91
- * registerStore('app', valtioStore(state, snapshot, subscribe));
92
- * ```
93
- */
94
51
  export function valtioStore(proxy, snapshot, subscribe) {
95
52
  return {
96
53
  getState: () => snapshot(proxy),
97
54
  subscribe: (listener) => subscribe(proxy, listener),
98
55
  };
99
56
  }
100
- /**
101
- * Expose a MobX observable as a Reticle store. `toJS` and `reaction` are passed in for the same
102
- * dependency reason as valtio above.
103
- *
104
- * ```ts
105
- * import { reaction, toJS } from 'mobx';
106
- * registerStore('app', mobxStore(store, toJS, reaction));
107
- * ```
108
- */
109
57
  export function mobxStore(observable, toJS, reaction) {
110
58
  return {
111
59
  getState: () => toJS(observable),
112
60
  subscribe: (listener) => reaction(() => toJS(observable), () => listener()),
113
61
  };
114
62
  }
115
- /**
116
- * The load states a Recoil `Loadable` reports. Named because the projection below branches on them
117
- * and a bare 'hasValue' in three places is exactly the free string the rules forbid.
118
- */
119
63
  const RecoilLoadState = {
120
64
  HAS_VALUE: 'hasValue',
121
65
  LOADING: 'loading',
@@ -130,50 +74,13 @@ function projectLoadable(loadable) {
130
74
  error: contents instanceof Error ? contents.message : String(contents),
131
75
  };
132
76
  }
133
- // A LOADING loadable's `contents` is the pending promise. Sending it would serialize to `{}` and
134
- // read as an empty value — so the value stays null and the status carries the truth.
135
77
  if (loadable.state !== RecoilLoadState.HAS_VALUE) {
136
78
  return { status: loadable.state, value: null, error: null };
137
79
  }
138
80
  return { status: loadable.state, value: loadable.contents, error: null };
139
81
  }
140
- /**
141
- * Expose a chosen set of Recoil atoms as one Reticle store.
142
- *
143
- * Three things about Recoil's API shape this adapter is built around, none of them optional:
144
- *
145
- * - **The atom map is forced, not an ergonomic choice.** Recoil has no enumerable registry of live
146
- * atoms, so "the whole store" is not a thing that exists. Naming the atoms is the only way to
147
- * snapshot them, and it doubles as a declaration of what matters — the same reason `jotaiStore`
148
- * takes one.
149
- * - **`.state`/`.contents`, never `.getValue()`.** On a pending async selector `getValue()` THROWS
150
- * the pending promise, and on a failed one it throws the error. Either would take down the whole
151
- * state read over one slow atom — so a loading atom reports `status: 'loading'` with a null value
152
- * rather than silently becoming an empty object or an exception.
153
- * - **One subscription, not one per atom.** Recoil exposes no per-atom subscription outside React
154
- * (`jotaiStore` gets `store.sub(atom, …)`; there is no equivalent here). The transaction stream is
155
- * the only public change signal, so the caller passes it in — the same "the library ships free
156
- * functions, so hand them over" shape as `valtioStore` and `mobxStore`.
157
- *
158
- * Wire it from a bridge component, which is where `useRecoilTransactionObserver_UNSTABLE` lives:
159
- *
160
- * ```tsx
161
- * const latest = useRef(snapshot_UNSTABLE());
162
- * const listeners = useRef(new Set<() => void>()).current;
163
- * useRecoilTransactionObserver_UNSTABLE(({ snapshot }) => {
164
- * latest.current = snapshot;
165
- * for (const l of listeners) l();
166
- * });
167
- * registerStore('recoil', recoilStore({ cart: cartAtom }, () => latest.current, (l) => {
168
- * listeners.add(l);
169
- * return () => listeners.delete(l);
170
- * }));
171
- * ```
172
- */
173
82
  export function recoilStore(atoms, snapshot, subscribe) {
174
83
  return {
175
- // Resolved per call, not captured once: a Recoil snapshot is IMMUTABLE, so an adapter holding
176
- // one would keep answering from the transaction it was built in and never see another write.
177
84
  getState: () => {
178
85
  const current = snapshot();
179
86
  const out = {};
@@ -192,25 +99,6 @@ function stopSubscription(handle) {
192
99
  }
193
100
  handle.unsubscribe();
194
101
  }
195
- /**
196
- * Expose a Svelte store as a Reticle store.
197
- *
198
- * Structurally unlike every other adapter in this file: they all wrap something that can be PULLED
199
- * (`getState`, `getSnapshot`, `snapshot(proxy)`, `toJS`). A Svelte store has no pull side at all —
200
- * `{ subscribe }` is the entire contract. What makes a pull possible anyway is the part of that
201
- * contract which says `subscribe` must call back **immediately and synchronously** with the current
202
- * value: subscribe, catch the value, unsubscribe, return it. That is precisely how `svelte/store`'s
203
- * own `get()` is implemented, so it is a blessed read rather than a trick.
204
- *
205
- * Doing it that way is what keeps this adapter free of the `dispose` the obvious implementation
206
- * needs. Caching the latest value would mean holding a permanent subscription, which means a
207
- * teardown the other five adapters don't have, which means a leak the moment a caller forgets it.
208
- * A transient subscription per read owns nothing and leaks nothing.
209
- *
210
- * ```ts
211
- * registerStore('cart', svelteStore(cartStore));
212
- * ```
213
- */
214
102
  export function svelteStore(readable, warn = nativeWarn) {
215
103
  let warnedAboutLazyStore = false;
216
104
  return {
@@ -221,10 +109,6 @@ export function svelteStore(readable, warn = nativeWarn) {
221
109
  value = next;
222
110
  called = true;
223
111
  }));
224
- // A store that did NOT call back synchronously (an RxJS Observable that is not a
225
- // BehaviorSubject, a hand-rolled store that breaks the contract) leaves `value` undefined —
226
- // indistinguishable from a store legitimately holding undefined. Reading empty when the answer
227
- // is "unknown" is the false green this project exists to prevent, so say it once, out loud.
228
112
  if (!called && !warnedAboutLazyStore) {
229
113
  warnedAboutLazyStore = true;
230
114
  warn('[reticle] a store passed to svelteStore did not call its subscriber synchronously, so ' +
@@ -235,17 +119,6 @@ export function svelteStore(readable, warn = nativeWarn) {
235
119
  return value;
236
120
  },
237
121
  subscribe: (listener) => {
238
- // Swallow whatever arrives DURING the subscribe call. Forwarding it would emit a STATE_CHANGE
239
- // at REGISTRATION time for a change that never happened — a diff of nothing that shows up in
240
- // causal summaries and that a {kind:'state'} predicate could satisfy without the app doing
241
- // anything at all.
242
- //
243
- // The window is what makes this correct, rather than "swallow the first callback whenever it
244
- // arrives". A store that does NOT call back synchronously (an RxJS Observable that is not a
245
- // BehaviorSubject — the exact store `getState` warns about) has no registration callback to
246
- // drop, so counting callbacks ate its first REAL change: no STATE_CHANGE, and a
247
- // {kind:'state'} assertion with nothing to match. A silently missed state change is the false
248
- // green this project exists to prevent, so the rule is positional, not ordinal.
249
122
  let inSubscribeCall = true;
250
123
  const handle = readable.subscribe(() => {
251
124
  if (!inSubscribeCall)
@@ -256,49 +129,13 @@ export function svelteStore(readable, warn = nativeWarn) {
256
129
  },
257
130
  };
258
131
  }
259
- /** Pinia `$subscribe` options. `sync` and `detached` are library option values, not free strings. */
260
132
  const PINIA_SUBSCRIBE_OPTIONS = { detached: true, flush: 'sync' };
261
- /**
262
- * Expose a Pinia store as a Reticle store. `$state` / `$subscribe` are close to `{getState,
263
- * subscribe}` but not close enough for `registerStore`'s duck-type, which is the whole reason a Vue
264
- * app currently connects and then reports no state at all.
265
- *
266
- * Both `$subscribe` options are load-bearing:
267
- *
268
- * - **`detached`** keeps the subscription alive past the component that happened to register the
269
- * store. Without it Pinia tears the listener down on unmount, so a store registered from inside a
270
- * component goes permanently silent after the first route change — readable, but never again
271
- * emitting a STATE_CHANGE, which reads exactly like an app that stopped changing.
272
- * - **`flush: 'sync'`** puts the notification inside the action's attribution window. The default
273
- * (`'post'`) defers it past the Vue update tick, and a state change that lands after the window
274
- * closed is no longer linked to the click that caused it in the causal summary.
275
- *
276
- * `$state` carries state, not getters — a Pinia getter is derived, so asserting on the state it
277
- * derives from is the stronger assertion anyway.
278
- *
279
- * ```ts
280
- * registerStore('cart', piniaStore(useCartStore()));
281
- * ```
282
- */
283
133
  export function piniaStore(store) {
284
134
  return {
285
- // `$state` is a live reactive proxy, so this reads through to the current value rather than
286
- // snapshotting at registration. The transport serializer walks it like any object and guards
287
- // each key, which is what keeps a throwing reactive trap from costing the whole read.
288
135
  getState: () => store.$state,
289
136
  subscribe: (listener) => store.$subscribe(() => listener(), { ...PINIA_SUBSCRIBE_OPTIONS }),
290
137
  };
291
138
  }
292
- /**
293
- * Build a store whose value is PUSHED in rather than pulled — for state that has no object to read.
294
- *
295
- * React Context and `useState`/`useReducer` keep their value inside the fiber tree; there is no store
296
- * instance and no subscription point outside React, so no adapter over a public API is possible. The
297
- * only way in is to invert the direction: the component holding the value tells Reticle when it
298
- * changes. `@reticlehq/react`'s `useReticleStore` hook is the ergonomic wrapper over this.
299
- *
300
- * Returns the store plus the `push` that updates it, so the caller owns the write side.
301
- */
302
139
  export function pushStore(initial) {
303
140
  let current = initial;
304
141
  const listeners = new Set();
@@ -1,4 +1,3 @@
1
- /** Framework adapter registry — enriches elements with component identity + source. */
2
1
  export interface ComponentSource {
3
2
  file: string;
4
3
  line: number;
@@ -11,20 +10,11 @@ export interface ComponentInfo {
11
10
  export interface ReticleAdapter {
12
11
  name: string;
13
12
  identify: (el: Element) => ComponentInfo | null;
14
- /** Best-effort: read a component's hook state for a DOM element. */
15
13
  readState?: (el: Element) => unknown;
16
- /** Best-effort: does the element declare framework enter/leave handlers synthetic hover may not fire? */
17
14
  hasHoverHandlers?: (el: Element) => boolean;
18
15
  }
19
- /** Called by @reticlehq/react (and future adapters) to register themselves. */
20
16
  export declare function registerAdapter(adapter: ReticleAdapter): void;
21
- /** First adapter that can identify the element wins. */
22
17
  export declare function identifyComponent(el: Element): ComponentInfo | null;
23
- /** First adapter that returns non-undefined component state for the element wins. */
24
18
  export declare function readComponentState(el: Element): unknown;
25
- /**
26
- * True if any installed adapter reports framework enter/leave hover handlers on the element.
27
- * No-op-safe: returns false when no adapter is installed or none implements the probe.
28
- */
29
19
  export declare function elementHasHoverHandlers(el: Element): boolean;
30
20
  export declare function adapterNames(): string[];
@@ -1,14 +1,9 @@
1
- /** Framework adapter registry — enriches elements with component identity + source. */
2
- // Persist on a global so the registry survives HMR module re-evaluation (otherwise the
3
- // adapter silently drops after a hot reload and source mapping degrades). See feedback #7.
4
1
  const globalStore = globalThis;
5
2
  const adapters = (globalStore.__reticleAdapters ??= []);
6
- /** Called by @reticlehq/react (and future adapters) to register themselves. */
7
3
  export function registerAdapter(adapter) {
8
4
  if (!adapters.some((a) => a.name === adapter.name))
9
5
  adapters.push(adapter);
10
6
  }
11
- /** First adapter that can identify the element wins. */
12
7
  export function identifyComponent(el) {
13
8
  for (const adapter of adapters) {
14
9
  const info = adapter.identify(el);
@@ -17,7 +12,6 @@ export function identifyComponent(el) {
17
12
  }
18
13
  return null;
19
14
  }
20
- /** First adapter that returns non-undefined component state for the element wins. */
21
15
  export function readComponentState(el) {
22
16
  for (const adapter of adapters) {
23
17
  if (adapter.readState === undefined)
@@ -28,10 +22,6 @@ export function readComponentState(el) {
28
22
  }
29
23
  return undefined;
30
24
  }
31
- /**
32
- * True if any installed adapter reports framework enter/leave hover handlers on the element.
33
- * No-op-safe: returns false when no adapter is installed or none implements the probe.
34
- */
35
25
  export function elementHasHoverHandlers(el) {
36
26
  for (const adapter of adapters) {
37
27
  if (adapter.hasHoverHandlers === undefined)
@@ -1,54 +1,21 @@
1
1
  import { type TruncationReport } from '../security/serialization.js';
2
- /** Store registry — lets the agent pull live framework/store state on demand. */
3
2
  export type StoreGetter = () => unknown;
4
- /** A store's subscribe method (Zustand/Redux shape): register a listener, get back an unsubscribe. */
5
3
  export type StoreSubscribe = (listener: () => void) => () => void;
6
- /** Notified when a SUBSCRIBABLE store is registered, so a late registration is still observed. */
7
4
  export type StoreRegisteredListener = (entry: [string, StoreGetter, StoreSubscribe]) => void;
8
- /**
9
- * Observe FUTURE subscribable-store registrations. The SDK installs its observers during connect, but
10
- * apps call registerStore afterwards — so enumerating once at install time subscribes to nothing and
11
- * STATE_CHANGE never fires. Observers use this to pick up stores registered after they installed.
12
- */
13
5
  export declare function onStoreRegistered(listener: StoreRegisteredListener): () => void;
14
- /** A Zustand/Redux-shaped store: exposes both current state and a subscribe. */
15
6
  export interface StoreLike {
16
7
  getState: () => unknown;
17
8
  subscribe: StoreSubscribe;
18
9
  }
19
- /**
20
- * App calls this once per store. PREFER passing the store itself — `registerStore('app', useApp)` —
21
- * which wires `getState` AND `subscribe`, so every mutation emits a STATE_CHANGE path diff automatically
22
- * (the before→after the causal summary reports). Passing a bare getter still works for back-compat, but
23
- * leaves the store on pull-only reads: no change events, and an empty `stateDiffs` in every act summary.
24
- */
25
10
  export declare function registerStore(name: string, source: StoreGetter | StoreLike, subscribe?: StoreSubscribe): void;
26
11
  export declare function unregisterStore(name: string): void;
27
12
  export declare function storeNames(): string[];
28
- /** Registered stores that exposed a subscribe method, as [name, getter, subscribe] tuples. */
29
13
  export declare function subscribableStores(): Array<[string, StoreGetter, StoreSubscribe]>;
30
- /**
31
- * Read one store (by name) or all of them WITHOUT transport sanitization. For scoped reads that
32
- * select a sub-tree first, so a deep/large path (e.g. row 250 of a 500-row array) isn't truncated
33
- * before selection. Each getter is isolated: a throw becomes an error object.
34
- */
35
14
  export declare function readStoresRaw(only?: string): Record<string, unknown>;
36
- /** Read one store (by name) or all of them, each capped for transport. */
37
15
  export declare function readStores(only?: string): Record<string, unknown>;
38
- /**
39
- * As `readStores`, plus which stores came back INCOMPLETE.
40
- *
41
- * The whole-store read is the one path where the transport caps could silently hand back a fraction
42
- * of a large store — ~142 of 1,000 entities, with the remainder simply absent. A caller comparing
43
- * that against expected data reads it as "the app lost the rest", which is a false failure; a caller
44
- * asserting something is absent reads it as proof, which is a false green. Neither is recoverable
45
- * from the payload, so the fact of truncation travels beside it.
46
- */
47
16
  export declare function readStoresWithTruncation(only?: string): {
48
17
  stores: Record<string, unknown>;
49
18
  truncation?: Record<string, TruncationReport>;
50
19
  };
51
- /** Called by an adapter to record what it was built from. See `sourceOwner`. */
52
20
  export declare function markAdapterSource(store: object, source: object): void;
53
- /** The name this store source is registered under, or undefined if nothing has claimed it. */
54
21
  export declare function sourceOwner(value: unknown): string | undefined;
@@ -1,25 +1,16 @@
1
1
  import { RETICLE_RENDERS_STORE } from '@reticlehq/core';
2
- import { nativeWarn } from '../timers/native-console.js';
2
+ import { nativeWarn } from '../timers/console/native-console.js';
3
3
  import { sanitizeWithReport } from '../security/serialization.js';
4
4
  const globalStore = globalThis;
5
5
  const stores = (globalStore.__reticleStores ??= new Map());
6
- // Parallel map: stores that also provided a subscribe method, for automatic STATE_CHANGE diffs.
7
6
  const subscribers = (globalStore.__reticleStoreSubs ??= new Map());
8
7
  const registrationListeners = (globalStore.__reticleStoreListeners ??=
9
8
  new Set());
10
- /**
11
- * Observe FUTURE subscribable-store registrations. The SDK installs its observers during connect, but
12
- * apps call registerStore afterwards — so enumerating once at install time subscribes to nothing and
13
- * STATE_CHANGE never fires. Observers use this to pick up stores registered after they installed.
14
- */
15
9
  export function onStoreRegistered(listener) {
16
10
  registrationListeners.add(listener);
17
11
  return () => registrationListeners.delete(listener);
18
12
  }
19
13
  function isStoreLike(source) {
20
- // Zustand's `create` returns a CALLABLE hook that also carries getState/subscribe, so a store can be
21
- // typeof 'function' as well as 'object'. What distinguishes it from a plain getter is those two members
22
- // — never the typeof. (Checking only for 'object' left every Zustand app on pull-only reads.)
23
14
  if (null === source)
24
15
  return false;
25
16
  if (typeof source !== 'object' && typeof source !== 'function')
@@ -27,12 +18,6 @@ function isStoreLike(source) {
27
18
  const candidate = source;
28
19
  return 'function' === typeof candidate.getState && 'function' === typeof candidate.subscribe;
29
20
  }
30
- /**
31
- * App calls this once per store. PREFER passing the store itself — `registerStore('app', useApp)` —
32
- * which wires `getState` AND `subscribe`, so every mutation emits a STATE_CHANGE path diff automatically
33
- * (the before→after the causal summary reports). Passing a bare getter still works for back-compat, but
34
- * leaves the store on pull-only reads: no change events, and an empty `stateDiffs` in every act summary.
35
- */
36
21
  export function registerStore(name, source, subscribe) {
37
22
  claimSource(name, source);
38
23
  if (isStoreLike(source)) {
@@ -57,11 +42,6 @@ export function registerStore(name, source, subscribe) {
57
42
  notifyRegistered(name, source, subscribe);
58
43
  return;
59
44
  }
60
- // Readable but SILENT: reticle_state can read this store on demand, yet nothing will ever emit a
61
- // STATE_CHANGE for it, so causal summaries show no state diff and a {kind:"state"} predicate can
62
- // never observe it changing. That reads exactly like "the app did not change anything" — a false
63
- // negative with no error attached. Say so once, at registration, where the developer can fix it by
64
- // passing the store itself (or a subscribe function) instead of a bare getter.
65
45
  warnSilentStoreOnce(name);
66
46
  }
67
47
  function notifyRegistered(name, getter, subscribe) {
@@ -70,7 +50,6 @@ function notifyRegistered(name, getter, subscribe) {
70
50
  listener([name, getter, subscribe]);
71
51
  }
72
52
  catch {
73
- // a broken observer must never break the app's registerStore call
74
53
  }
75
54
  }
76
55
  }
@@ -81,7 +60,6 @@ export function unregisterStore(name) {
81
60
  export function storeNames() {
82
61
  return [...stores.keys()];
83
62
  }
84
- /** Registered stores that exposed a subscribe method, as [name, getter, subscribe] tuples. */
85
63
  export function subscribableStores() {
86
64
  const out = [];
87
65
  for (const [name, subscribe] of subscribers) {
@@ -91,11 +69,6 @@ export function subscribableStores() {
91
69
  }
92
70
  return out;
93
71
  }
94
- /**
95
- * Read one store (by name) or all of them WITHOUT transport sanitization. For scoped reads that
96
- * select a sub-tree first, so a deep/large path (e.g. row 250 of a 500-row array) isn't truncated
97
- * before selection. Each getter is isolated: a throw becomes an error object.
98
- */
99
72
  export function readStoresRaw(only) {
100
73
  const out = {};
101
74
  for (const [name, getter] of stores) {
@@ -110,19 +83,9 @@ export function readStoresRaw(only) {
110
83
  }
111
84
  return out;
112
85
  }
113
- /** Read one store (by name) or all of them, each capped for transport. */
114
86
  export function readStores(only) {
115
87
  return readStoresWithTruncation(only).stores;
116
88
  }
117
- /**
118
- * As `readStores`, plus which stores came back INCOMPLETE.
119
- *
120
- * The whole-store read is the one path where the transport caps could silently hand back a fraction
121
- * of a large store — ~142 of 1,000 entities, with the remainder simply absent. A caller comparing
122
- * that against expected data reads it as "the app lost the rest", which is a false failure; a caller
123
- * asserting something is absent reads it as proof, which is a false green. Neither is recoverable
124
- * from the payload, so the fact of truncation travels beside it.
125
- */
126
89
  export function readStoresWithTruncation(only) {
127
90
  const stores = {};
128
91
  const truncation = {};
@@ -134,13 +97,7 @@ export function readStoresWithTruncation(only) {
134
97
  }
135
98
  return Object.keys(truncation).length > 0 ? { stores, truncation } : { stores };
136
99
  }
137
- /** Stores already warned about — a warning that repeats on every HMR cycle or remount becomes noise. */
138
100
  const warnedSilent = new Set();
139
- /**
140
- * Reticle's OWN read-only stores. `@reticlehq/react` registers a render-stats getter that the app
141
- * developer never wrote and cannot change, so warning about it would be Reticle telling the user to fix
142
- * Reticle — the same self-contradiction as a tool description naming a tool the profile does not expose.
143
- */
144
101
  const RETICLE_OWNED_STORES = new Set([RETICLE_RENDERS_STORE]);
145
102
  function warnSilentStoreOnce(name) {
146
103
  if (RETICLE_OWNED_STORES.has(name) || warnedSilent.has(name))
@@ -150,23 +107,8 @@ function warnSilentStoreOnce(name) {
150
107
  `changes are invisible: no STATE_CHANGE events, no state diffs in causal summaries, and a state ` +
151
108
  `predicate will never see it update. Pass the store object (or a subscribe callback) to fix.`);
152
109
  }
153
- /**
154
- * Who owns a given store SOURCE — the object the app (or an adapter) was built from.
155
- *
156
- * Registration is keyed by name, which cannot answer "is this same store already readable?". The
157
- * question matters because the React adapter discovers stores from the fiber tree, and an app that
158
- * has already registered the very same store by hand must not get a second entry for it under a
159
- * second name. Comparing the READ VALUE cannot answer it either: `tanstackQueryStore` builds a fresh
160
- * snapshot object on every `getState`, so identity never matches. Measured on bench-app, which
161
- * registers `queries` itself and got an auto-discovered `query` beside it — one cache, listed twice,
162
- * in every state read.
163
- *
164
- * A WeakMap so nothing here keeps an app's store alive.
165
- */
166
110
  const sourceOwners = new WeakMap();
167
- /** Adapter instance → the object it wraps, so registering an adapter also claims its source. */
168
111
  const adapterSources = new WeakMap();
169
- /** Called by an adapter to record what it was built from. See `sourceOwner`. */
170
112
  export function markAdapterSource(store, source) {
171
113
  adapterSources.set(store, source);
172
114
  }
@@ -178,7 +120,6 @@ function claimSource(name, source) {
178
120
  if (wrapped !== undefined)
179
121
  sourceOwners.set(wrapped, name);
180
122
  }
181
- /** The name this store source is registered under, or undefined if nothing has claimed it. */
182
123
  export function sourceOwner(value) {
183
124
  if (null === value || ('object' !== typeof value && 'function' !== typeof value))
184
125
  return undefined;
@@ -1,18 +1,5 @@
1
1
  import { PresenterMode } from '@reticlehq/core';
2
- /** Coerce an unknown arg to a string, falling back to `fallback` (default empty) when it isn't one. */
3
2
  export declare function str(value: unknown, fallback?: string): string;
4
- /** A short human label for a ref ("button \"Save\"") for the presenter HUD. */
5
3
  export declare function refLabel(refId: string): string;
6
- /**
7
- * Classify a browser command into the presenter intent the human watcher sees. Exhaustive
8
- * over the ReticleCommand names that actually reach the browser. CLOCK/NARRATE are control/meta
9
- * (neither a page read nor an act) -> IDLE so they don't paint a misleading chip. NARRATE never
10
- * reaches #presentBefore anyway (it returns early in #handleCommand) and must not clear the mode.
11
- */
12
4
  export declare function modeForCommand(commandName: string): PresenterMode;
13
- /**
14
- * Human-legible status for a read command — now WITH the target (which testid/value/ref/store), so
15
- * the watcher sees "Finding [testid=row-3700]" instead of a meaningless repeating "Finding an
16
- * element". Falls back to the bare verb when no target is in the args.
17
- */
18
5
  export declare function presentStatus(commandName: string, args?: Record<string, unknown>): string;
@@ -1,11 +1,13 @@
1
1
  import { ReticleCommand, PresenterMode, QueryBy } from '@reticlehq/core';
2
- import { refs } from './dom/refs.js';
2
+ import { refs } from './dom/addressing/refs.js';
3
3
  import { describe } from './dom/a11y.js';
4
- /** Coerce an unknown arg to a string, falling back to `fallback` (default empty) when it isn't one. */
5
4
  export function str(value, fallback = '') {
6
5
  return 'string' === typeof value ? value : fallback;
7
6
  }
8
- /** A short human label for a ref ("button \"Save\"") for the presenter HUD. */
7
+ function text(value) {
8
+ const asString = str(value);
9
+ return '' === asString ? undefined : asString;
10
+ }
9
11
  export function refLabel(refId) {
10
12
  const el = refs.resolve(refId);
11
13
  if (!(el instanceof Element))
@@ -13,12 +15,6 @@ export function refLabel(refId) {
13
15
  const d = describe(el);
14
16
  return d.name.length > 0 ? `${d.role} "${d.name}"` : `${d.role} (${refId})`;
15
17
  }
16
- /**
17
- * Classify a browser command into the presenter intent the human watcher sees. Exhaustive
18
- * over the ReticleCommand names that actually reach the browser. CLOCK/NARRATE are control/meta
19
- * (neither a page read nor an act) -> IDLE so they don't paint a misleading chip. NARRATE never
20
- * reaches #presentBefore anyway (it returns early in #handleCommand) and must not clear the mode.
21
- */
22
18
  export function modeForCommand(commandName) {
23
19
  switch (commandName) {
24
20
  case ReticleCommand.ACT:
@@ -36,11 +32,6 @@ export function modeForCommand(commandName) {
36
32
  return PresenterMode.IDLE;
37
33
  }
38
34
  }
39
- /**
40
- * Human-legible status for a read command — now WITH the target (which testid/value/ref/store), so
41
- * the watcher sees "Finding [testid=row-3700]" instead of a meaningless repeating "Finding an
42
- * element". Falls back to the bare verb when no target is in the args.
43
- */
44
35
  export function presentStatus(commandName, args = {}) {
45
36
  switch (commandName) {
46
37
  case ReticleCommand.SNAPSHOT:
@@ -52,13 +43,13 @@ export function presentStatus(commandName, args = {}) {
52
43
  return target !== undefined ? `Finding ${target}` : 'Finding an element';
53
44
  }
54
45
  case ReticleCommand.INSPECT: {
55
- const ref = str(args['ref']);
46
+ const ref = text(args['ref']);
56
47
  return ref !== undefined ? `Inspecting ${refLabel(ref)}` : 'Inspecting an element';
57
48
  }
58
49
  case ReticleCommand.ANIMATIONS:
59
50
  return 'Reading animations';
60
51
  case ReticleCommand.STATE_READ: {
61
- const store = str(args['store']);
52
+ const store = text(args['store']);
62
53
  return store !== undefined ? `Reading state: ${store}` : 'Reading state';
63
54
  }
64
55
  case ReticleCommand.CAPABILITIES:
@@ -67,13 +58,12 @@ export function presentStatus(commandName, args = {}) {
67
58
  return commandName;
68
59
  }
69
60
  }
70
- /** Compact "what we're looking for" from a query's args (testid/value/name/role/text/label). */
71
61
  function queryTarget(q) {
72
- const testid = str(q['testid']) ?? (QueryBy.TESTID === str(q['by']) ? str(q['value']) : undefined);
62
+ const testid = text(q['testid']) ?? (QueryBy.TESTID === str(q['by']) ? text(q['value']) : undefined);
73
63
  if (testid !== undefined)
74
64
  return `[testid=${testid}]`;
75
- const name = str(q['name']);
76
- const value = str(q['value']) ?? str(q['text']) ?? str(q['label']) ?? str(q['role']);
65
+ const name = text(q['name']);
66
+ const value = text(q['value']) ?? text(q['text']) ?? text(q['label']) ?? text(q['role']);
77
67
  if (value !== undefined)
78
68
  return name !== undefined ? `"${value}" (${name})` : `"${value}"`;
79
69
  return name !== undefined ? `"${name}"` : undefined;
@@ -0,0 +1,2 @@
1
+ import { Reticle } from './reticle.js';
2
+ export declare const reticle: Reticle;
@@ -0,0 +1,3 @@
1
+ import { Reticle } from './reticle.js';
2
+ const globalStore = globalThis;
3
+ export const reticle = (globalStore.__reticleInstance ??= new Reticle());