@reticlehq/browser 2.2.1 → 2.4.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 (273) hide show
  1. package/dist/actions/actions-dom.d.ts +0 -1
  2. package/dist/actions/actions-dom.js +0 -1
  3. package/dist/actions/actions.d.ts +3 -1
  4. package/dist/actions/actions.js +55 -36
  5. package/dist/actions/scroll.d.ts +0 -1
  6. package/dist/actions/scroll.js +0 -1
  7. package/dist/actions/value-input.d.ts +33 -0
  8. package/dist/actions/value-input.js +63 -0
  9. package/dist/commands/commands.d.ts +0 -1
  10. package/dist/commands/commands.js +27 -7
  11. package/dist/connect-options.d.ts +97 -0
  12. package/dist/connect-options.js +1 -0
  13. package/dist/dom/a11y.d.ts +0 -2
  14. package/dist/dom/a11y.js +54 -24
  15. package/dist/dom/auto-anchor.d.ts +0 -1
  16. package/dist/dom/auto-anchor.js +0 -1
  17. package/dist/dom/blind-spots.d.ts +0 -1
  18. package/dist/dom/blind-spots.js +0 -1
  19. package/dist/dom/chart.d.ts +0 -1
  20. package/dist/dom/chart.js +0 -1
  21. package/dist/dom/desktop-capture.d.ts +36 -0
  22. package/dist/dom/desktop-capture.js +113 -0
  23. package/dist/dom/dom-ignore.d.ts +7 -1
  24. package/dist/dom/dom-ignore.js +1 -2
  25. package/dist/dom/occlusion.d.ts +0 -1
  26. package/dist/dom/occlusion.js +0 -1
  27. package/dist/dom/query.d.ts +0 -1
  28. package/dist/dom/query.js +61 -13
  29. package/dist/dom/realm.d.ts +30 -0
  30. package/dist/dom/realm.js +58 -0
  31. package/dist/dom/refs.d.ts +0 -1
  32. package/dist/dom/refs.js +0 -1
  33. package/dist/dom/shadow-registry.d.ts +40 -0
  34. package/dist/dom/shadow-registry.js +165 -0
  35. package/dist/dom/snapshot.d.ts +2 -2
  36. package/dist/dom/snapshot.js +21 -4
  37. package/dist/dom/source.d.ts +0 -1
  38. package/dist/dom/source.js +0 -1
  39. package/dist/dom/theme.d.ts +0 -1
  40. package/dist/dom/theme.js +0 -1
  41. package/dist/dom/virtualized.d.ts +19 -0
  42. package/dist/dom/virtualized.js +80 -0
  43. package/dist/index.d.ts +3 -4
  44. package/dist/index.js +2 -3
  45. package/dist/observers/animation.d.ts +0 -1
  46. package/dist/observers/animation.js +0 -1
  47. package/dist/observers/blind-spots.d.ts +16 -4
  48. package/dist/observers/blind-spots.js +65 -11
  49. package/dist/observers/console.d.ts +0 -1
  50. package/dist/observers/console.js +0 -1
  51. package/dist/observers/dom.d.ts +0 -2
  52. package/dist/observers/dom.js +67 -9
  53. package/dist/observers/download.d.ts +6 -0
  54. package/dist/observers/download.js +103 -0
  55. package/dist/observers/focus.d.ts +0 -1
  56. package/dist/observers/focus.js +0 -1
  57. package/dist/observers/frames.d.ts +12 -0
  58. package/dist/observers/frames.js +67 -0
  59. package/dist/observers/health.d.ts +0 -1
  60. package/dist/observers/health.js +30 -2
  61. package/dist/observers/install-all.d.ts +5 -0
  62. package/dist/observers/install-all.js +63 -0
  63. package/dist/observers/ipc.d.ts +22 -0
  64. package/dist/observers/ipc.js +121 -0
  65. package/dist/observers/network-body.d.ts +17 -1
  66. package/dist/observers/network-body.js +26 -1
  67. package/dist/observers/network-redact.d.ts +0 -1
  68. package/dist/observers/network-redact.js +6 -3
  69. package/dist/observers/network-stream.d.ts +23 -0
  70. package/dist/observers/network-stream.js +102 -0
  71. package/dist/observers/network.d.ts +11 -1
  72. package/dist/observers/network.js +7 -1
  73. package/dist/observers/perf.d.ts +0 -1
  74. package/dist/observers/perf.js +0 -1
  75. package/dist/observers/route.d.ts +0 -1
  76. package/dist/observers/route.js +0 -1
  77. package/dist/observers/scroll.d.ts +0 -1
  78. package/dist/observers/scroll.js +0 -1
  79. package/dist/observers/sdk-failure.d.ts +28 -0
  80. package/dist/observers/sdk-failure.js +60 -0
  81. package/dist/observers/state.d.ts +0 -1
  82. package/dist/observers/state.js +0 -1
  83. package/dist/observers/storage.d.ts +0 -1
  84. package/dist/observers/storage.js +0 -1
  85. package/dist/observers/types.d.ts +0 -1
  86. package/dist/observers/types.js +0 -1
  87. package/dist/patching/capture-method.d.ts +0 -1
  88. package/dist/patching/capture-method.js +0 -1
  89. package/dist/presenter/overlay.d.ts +0 -1
  90. package/dist/presenter/overlay.js +0 -1
  91. package/dist/presenter/presenter-config.d.ts +0 -1
  92. package/dist/presenter/presenter-config.js +0 -1
  93. package/dist/presenter/presenter-controls.d.ts +0 -1
  94. package/dist/presenter/presenter-controls.js +0 -1
  95. package/dist/presenter/presenter-effects.d.ts +0 -1
  96. package/dist/presenter/presenter-effects.js +0 -1
  97. package/dist/presenter/presenter-glow.d.ts +0 -1
  98. package/dist/presenter/presenter-glow.js +0 -1
  99. package/dist/presenter/presenter-log.d.ts +0 -1
  100. package/dist/presenter/presenter-log.js +0 -1
  101. package/dist/presenter/presenter-run-state.d.ts +0 -1
  102. package/dist/presenter/presenter-run-state.js +0 -1
  103. package/dist/presenter/presenter-styles.d.ts +0 -1
  104. package/dist/presenter/presenter-styles.js +0 -1
  105. package/dist/presenter/presenter-tally.d.ts +0 -1
  106. package/dist/presenter/presenter-tally.js +0 -1
  107. package/dist/presenter/presenter-test-helpers.d.ts +0 -1
  108. package/dist/presenter/presenter-test-helpers.js +0 -1
  109. package/dist/presenter/presenter-verbs.d.ts +0 -1
  110. package/dist/presenter/presenter-verbs.js +0 -1
  111. package/dist/presenter/presenter.d.ts +0 -1
  112. package/dist/presenter/presenter.js +0 -1
  113. package/dist/recorder/recorder-styles.d.ts +0 -1
  114. package/dist/recorder/recorder-styles.js +0 -1
  115. package/dist/recorder/recorder.d.ts +0 -1
  116. package/dist/recorder/recorder.js +0 -1
  117. package/dist/registry/adapters.d.ts +0 -1
  118. package/dist/registry/adapters.js +0 -1
  119. package/dist/registry/capabilities.d.ts +2 -1
  120. package/dist/registry/capabilities.js +16 -1
  121. package/dist/registry/commit-and-signal.d.ts +0 -1
  122. package/dist/registry/commit-and-signal.js +0 -1
  123. package/dist/registry/domains.d.ts +0 -1
  124. package/dist/registry/domains.js +0 -1
  125. package/dist/registry/emitter.d.ts +0 -1
  126. package/dist/registry/emitter.js +0 -1
  127. package/dist/registry/store-adapters.d.ts +141 -7
  128. package/dist/registry/store-adapters.js +178 -1
  129. package/dist/registry/stores.d.ts +0 -1
  130. package/dist/registry/stores.js +8 -1
  131. package/dist/reticle-presenter-helpers.d.ts +0 -1
  132. package/dist/reticle-presenter-helpers.js +0 -1
  133. package/dist/reticle.d.ts +5 -59
  134. package/dist/reticle.js +37 -33
  135. package/dist/review/annotator.d.ts +0 -1
  136. package/dist/review/annotator.js +0 -1
  137. package/dist/review/mark-anchor.d.ts +0 -1
  138. package/dist/review/mark-anchor.js +0 -1
  139. package/dist/security/serialization.d.ts +0 -1
  140. package/dist/security/serialization.js +0 -1
  141. package/dist/timers/clock.d.ts +0 -1
  142. package/dist/timers/clock.js +0 -1
  143. package/dist/timers/native-console.d.ts +0 -1
  144. package/dist/timers/native-console.js +0 -1
  145. package/dist/timers/native-timers.d.ts +0 -1
  146. package/dist/timers/native-timers.js +0 -1
  147. package/dist/transport/transport.d.ts +12 -6
  148. package/dist/transport/transport.js +107 -17
  149. package/package.json +13 -3
  150. package/dist/actions/actions-dom.d.ts.map +0 -1
  151. package/dist/actions/actions-dom.js.map +0 -1
  152. package/dist/actions/actions.d.ts.map +0 -1
  153. package/dist/actions/actions.js.map +0 -1
  154. package/dist/actions/scroll.d.ts.map +0 -1
  155. package/dist/actions/scroll.js.map +0 -1
  156. package/dist/commands/commands.d.ts.map +0 -1
  157. package/dist/commands/commands.js.map +0 -1
  158. package/dist/dom/a11y.d.ts.map +0 -1
  159. package/dist/dom/a11y.js.map +0 -1
  160. package/dist/dom/auto-anchor.d.ts.map +0 -1
  161. package/dist/dom/auto-anchor.js.map +0 -1
  162. package/dist/dom/blind-spots.d.ts.map +0 -1
  163. package/dist/dom/blind-spots.js.map +0 -1
  164. package/dist/dom/chart.d.ts.map +0 -1
  165. package/dist/dom/chart.js.map +0 -1
  166. package/dist/dom/dom-ignore.d.ts.map +0 -1
  167. package/dist/dom/dom-ignore.js.map +0 -1
  168. package/dist/dom/occlusion.d.ts.map +0 -1
  169. package/dist/dom/occlusion.js.map +0 -1
  170. package/dist/dom/query.d.ts.map +0 -1
  171. package/dist/dom/query.js.map +0 -1
  172. package/dist/dom/refs.d.ts.map +0 -1
  173. package/dist/dom/refs.js.map +0 -1
  174. package/dist/dom/snapshot.d.ts.map +0 -1
  175. package/dist/dom/snapshot.js.map +0 -1
  176. package/dist/dom/source.d.ts.map +0 -1
  177. package/dist/dom/source.js.map +0 -1
  178. package/dist/dom/theme.d.ts.map +0 -1
  179. package/dist/dom/theme.js.map +0 -1
  180. package/dist/index.d.ts.map +0 -1
  181. package/dist/index.js.map +0 -1
  182. package/dist/observers/animation.d.ts.map +0 -1
  183. package/dist/observers/animation.js.map +0 -1
  184. package/dist/observers/blind-spots.d.ts.map +0 -1
  185. package/dist/observers/blind-spots.js.map +0 -1
  186. package/dist/observers/console.d.ts.map +0 -1
  187. package/dist/observers/console.js.map +0 -1
  188. package/dist/observers/dom.d.ts.map +0 -1
  189. package/dist/observers/dom.js.map +0 -1
  190. package/dist/observers/focus.d.ts.map +0 -1
  191. package/dist/observers/focus.js.map +0 -1
  192. package/dist/observers/health.d.ts.map +0 -1
  193. package/dist/observers/health.js.map +0 -1
  194. package/dist/observers/network-body.d.ts.map +0 -1
  195. package/dist/observers/network-body.js.map +0 -1
  196. package/dist/observers/network-redact.d.ts.map +0 -1
  197. package/dist/observers/network-redact.js.map +0 -1
  198. package/dist/observers/network.d.ts.map +0 -1
  199. package/dist/observers/network.js.map +0 -1
  200. package/dist/observers/perf.d.ts.map +0 -1
  201. package/dist/observers/perf.js.map +0 -1
  202. package/dist/observers/route.d.ts.map +0 -1
  203. package/dist/observers/route.js.map +0 -1
  204. package/dist/observers/scroll.d.ts.map +0 -1
  205. package/dist/observers/scroll.js.map +0 -1
  206. package/dist/observers/state.d.ts.map +0 -1
  207. package/dist/observers/state.js.map +0 -1
  208. package/dist/observers/storage.d.ts.map +0 -1
  209. package/dist/observers/storage.js.map +0 -1
  210. package/dist/observers/types.d.ts.map +0 -1
  211. package/dist/observers/types.js.map +0 -1
  212. package/dist/patching/capture-method.d.ts.map +0 -1
  213. package/dist/patching/capture-method.js.map +0 -1
  214. package/dist/presenter/overlay.d.ts.map +0 -1
  215. package/dist/presenter/overlay.js.map +0 -1
  216. package/dist/presenter/presenter-config.d.ts.map +0 -1
  217. package/dist/presenter/presenter-config.js.map +0 -1
  218. package/dist/presenter/presenter-controls.d.ts.map +0 -1
  219. package/dist/presenter/presenter-controls.js.map +0 -1
  220. package/dist/presenter/presenter-effects.d.ts.map +0 -1
  221. package/dist/presenter/presenter-effects.js.map +0 -1
  222. package/dist/presenter/presenter-glow.d.ts.map +0 -1
  223. package/dist/presenter/presenter-glow.js.map +0 -1
  224. package/dist/presenter/presenter-log.d.ts.map +0 -1
  225. package/dist/presenter/presenter-log.js.map +0 -1
  226. package/dist/presenter/presenter-run-state.d.ts.map +0 -1
  227. package/dist/presenter/presenter-run-state.js.map +0 -1
  228. package/dist/presenter/presenter-styles.d.ts.map +0 -1
  229. package/dist/presenter/presenter-styles.js.map +0 -1
  230. package/dist/presenter/presenter-tally.d.ts.map +0 -1
  231. package/dist/presenter/presenter-tally.js.map +0 -1
  232. package/dist/presenter/presenter-test-helpers.d.ts.map +0 -1
  233. package/dist/presenter/presenter-test-helpers.js.map +0 -1
  234. package/dist/presenter/presenter-verbs.d.ts.map +0 -1
  235. package/dist/presenter/presenter-verbs.js.map +0 -1
  236. package/dist/presenter/presenter.d.ts.map +0 -1
  237. package/dist/presenter/presenter.js.map +0 -1
  238. package/dist/recorder/recorder-styles.d.ts.map +0 -1
  239. package/dist/recorder/recorder-styles.js.map +0 -1
  240. package/dist/recorder/recorder.d.ts.map +0 -1
  241. package/dist/recorder/recorder.js.map +0 -1
  242. package/dist/registry/adapters.d.ts.map +0 -1
  243. package/dist/registry/adapters.js.map +0 -1
  244. package/dist/registry/capabilities.d.ts.map +0 -1
  245. package/dist/registry/capabilities.js.map +0 -1
  246. package/dist/registry/commit-and-signal.d.ts.map +0 -1
  247. package/dist/registry/commit-and-signal.js.map +0 -1
  248. package/dist/registry/domains.d.ts.map +0 -1
  249. package/dist/registry/domains.js.map +0 -1
  250. package/dist/registry/emitter.d.ts.map +0 -1
  251. package/dist/registry/emitter.js.map +0 -1
  252. package/dist/registry/store-adapters.d.ts.map +0 -1
  253. package/dist/registry/store-adapters.js.map +0 -1
  254. package/dist/registry/stores.d.ts.map +0 -1
  255. package/dist/registry/stores.js.map +0 -1
  256. package/dist/reticle-presenter-helpers.d.ts.map +0 -1
  257. package/dist/reticle-presenter-helpers.js.map +0 -1
  258. package/dist/reticle.d.ts.map +0 -1
  259. package/dist/reticle.js.map +0 -1
  260. package/dist/review/annotator.d.ts.map +0 -1
  261. package/dist/review/annotator.js.map +0 -1
  262. package/dist/review/mark-anchor.d.ts.map +0 -1
  263. package/dist/review/mark-anchor.js.map +0 -1
  264. package/dist/security/serialization.d.ts.map +0 -1
  265. package/dist/security/serialization.js.map +0 -1
  266. package/dist/timers/clock.d.ts.map +0 -1
  267. package/dist/timers/clock.js.map +0 -1
  268. package/dist/timers/native-console.d.ts.map +0 -1
  269. package/dist/timers/native-console.js.map +0 -1
  270. package/dist/timers/native-timers.d.ts.map +0 -1
  271. package/dist/timers/native-timers.js.map +0 -1
  272. package/dist/transport/transport.d.ts.map +0 -1
  273. package/dist/transport/transport.js.map +0 -1
@@ -421,4 +421,3 @@ export class Presenter {
421
421
  spawnRipple(this.#root, cx, cy);
422
422
  }
423
423
  }
424
- //# sourceMappingURL=presenter.js.map
@@ -9,4 +9,3 @@ export declare const BTN_CSS: string;
9
9
  export declare const NAME_CSS: string;
10
10
  export declare const STATUS_CSS: string;
11
11
  export declare const MENU_CSS: string;
12
- //# sourceMappingURL=recorder-styles.d.ts.map
@@ -42,4 +42,3 @@ export const NAME_CSS = [
42
42
  ].join(';');
43
43
  export const STATUS_CSS = ['opacity:.75', 'margin-left:4px'].join(';');
44
44
  export const MENU_CSS = ['display:flex', 'gap:6px', 'align-items:center', 'flex-wrap:wrap'].join(';');
45
- //# sourceMappingURL=recorder-styles.js.map
@@ -57,4 +57,3 @@ export declare function compileRecording(name: string, steps: FlowStep[], annota
57
57
  /** Install the floating recorder toolbar. Default off; gated by the host (reticle.ts recorder flag). */
58
58
  export declare function installRecorder(deps: RecorderDeps): RecorderHandle;
59
59
  export {};
60
- //# sourceMappingURL=recorder.d.ts.map
@@ -451,4 +451,3 @@ class Recorder {
451
451
  export function installRecorder(deps) {
452
452
  return new Recorder(deps);
453
453
  }
454
- //# sourceMappingURL=recorder.js.map
@@ -28,4 +28,3 @@ export declare function readComponentState(el: Element): unknown;
28
28
  */
29
29
  export declare function elementHasHoverHandlers(el: Element): boolean;
30
30
  export declare function adapterNames(): string[];
31
- //# sourceMappingURL=adapters.d.ts.map
@@ -44,4 +44,3 @@ export function elementHasHoverHandlers(el) {
44
44
  export function adapterNames() {
45
45
  return adapters.map((a) => a.name);
46
46
  }
47
- //# sourceMappingURL=adapters.js.map
@@ -16,10 +16,11 @@ export interface CapabilitiesInput {
16
16
  stores?: string[];
17
17
  flows?: CapabilityFlow[];
18
18
  }
19
+ /** Set by the SDK at connect. Idempotent; the last connect wins. */
20
+ export declare function setCapabilitiesListener(cb: (() => void) | undefined): void;
19
21
  /** Called by the host app via reticle.describe. Merges (idempotent), never replaces wholesale. */
20
22
  export declare function registerCapabilities(input: CapabilitiesInput): void;
21
23
  /** Snapshot copy of the registered capabilities (defensive — never hand out the live arrays). */
22
24
  export declare function getCapabilities(): Capabilities;
23
25
  /** Whether the app has advertised any capabilities (used in the HELLO flag). */
24
26
  export declare function hasCapabilities(): boolean;
25
- //# sourceMappingURL=capabilities.d.ts.map
@@ -12,6 +12,21 @@ function mergeUnique(into, add) {
12
12
  if (!into.includes(v))
13
13
  into.push(v);
14
14
  }
15
+ /**
16
+ * Notified whenever capabilities change, so the SDK can re-announce them to the bridge.
17
+ *
18
+ * `hasCapabilities` rides in the HELLO, which goes out at connect() — and registering deliberately
19
+ * happens AFTER connect, because `registerStore` needs a live SDK to subscribe through. Without a
20
+ * notification, an app that declared its whole testable surface still appeared to the agent as
21
+ * having none, permanently. The hook lives HERE rather than on `reticle.describe` because the
22
+ * documented entry point is this bare function; only wiring `describe` would have fixed the path
23
+ * almost nobody uses.
24
+ */
25
+ let onChanged;
26
+ /** Set by the SDK at connect. Idempotent; the last connect wins. */
27
+ export function setCapabilitiesListener(cb) {
28
+ onChanged = cb;
29
+ }
15
30
  /** Called by the host app via reticle.describe. Merges (idempotent), never replaces wholesale. */
16
31
  export function registerCapabilities(input) {
17
32
  mergeUnique(capabilities.testids, input.testids);
@@ -28,6 +43,7 @@ export function registerCapabilities(input) {
28
43
  }
29
44
  }
30
45
  }
46
+ onChanged?.();
31
47
  }
32
48
  /** Snapshot copy of the registered capabilities (defensive — never hand out the live arrays). */
33
49
  export function getCapabilities() {
@@ -45,4 +61,3 @@ export function hasCapabilities() {
45
61
  capabilities.stores.length > 0 ||
46
62
  capabilities.flows.length > 0);
47
63
  }
48
- //# sourceMappingURL=capabilities.js.map
@@ -6,4 +6,3 @@
6
6
  */
7
7
  import type { ReticleEmitter } from './emitter.js';
8
8
  export declare function commitAndSignal<T>(emitter: ReticleEmitter, mutate: () => T, name: string, data?: Record<string, unknown>): T;
9
- //# sourceMappingURL=commit-and-signal.d.ts.map
@@ -9,4 +9,3 @@ export function commitAndSignal(emitter, mutate, name, data) {
9
9
  emitter.signal(name, data);
10
10
  return result;
11
11
  }
12
- //# sourceMappingURL=commit-and-signal.js.map
@@ -12,4 +12,3 @@ export interface ReticleDomain {
12
12
  stores?: readonly string[];
13
13
  }
14
14
  export declare function registerReticleDomain(domain: ReticleDomain): void;
15
- //# sourceMappingURL=domains.d.ts.map
@@ -16,4 +16,3 @@ export function registerReticleDomain(domain) {
16
16
  input.stores = [...domain.stores];
17
17
  registerCapabilities(input);
18
18
  }
19
- //# sourceMappingURL=domains.js.map
@@ -25,4 +25,3 @@ export interface CreateReticleEmitterOptions {
25
25
  * made at module load — before `reticle.connect` — starts working the moment Reticle connects.
26
26
  */
27
27
  export declare function createReticleEmitter(options?: CreateReticleEmitterOptions): ReticleEmitter;
28
- //# sourceMappingURL=emitter.d.ts.map
@@ -24,4 +24,3 @@ export function createReticleEmitter(options = {}) {
24
24
  },
25
25
  };
26
26
  }
27
- //# sourceMappingURL=emitter.js.map
@@ -1,4 +1,4 @@
1
- import type { StoreLike } from './stores.js';
1
+ import type { StoreLike, StoreSubscribe } from './stores.js';
2
2
  /**
3
3
  * Adapters that give non-`{getState, subscribe}` state libraries the shape `registerStore` wants.
4
4
  *
@@ -60,10 +60,19 @@ export interface QuerySnapshot {
60
60
  * ```
61
61
  */
62
62
  export declare function tanstackQueryStore(client: QueryClientLike): StoreLike;
63
- /** The minimum of a Jotai vanilla store this adapter touches. */
64
- interface JotaiStoreLike {
65
- get: (atom: object) => unknown;
66
- sub: (atom: object, listener: () => void) => () => void;
63
+ /**
64
+ * The minimum of a Jotai vanilla store this adapter touches, generic in the ATOM type.
65
+ *
66
+ * `atom: object` was rejected by TypeScript for a real store and the reason is contravariance, not
67
+ * pedantry: Jotai's `get` is `<Value>(atom: Atom<Value>) => Value`, and a function that requires an
68
+ * `Atom` cannot stand in for one that accepts any `object`. So `jotaiStore(createStore(), …)` did
69
+ * not compile — the adapter was advertised, shipped, and unusable from TypeScript without a cast,
70
+ * which the shape-matched fake in the sibling test could never reveal because the fake was written
71
+ * to the same belief the adapter was.
72
+ */
73
+ interface JotaiStoreLike<A> {
74
+ get: (atom: A) => unknown;
75
+ sub: (atom: A, listener: () => void) => () => void;
67
76
  }
68
77
  /**
69
78
  * Expose a chosen set of Jotai atoms as one Reticle store.
@@ -76,7 +85,7 @@ interface JotaiStoreLike {
76
85
  * registerStore('app', jotaiStore(getDefaultStore(), { cart: cartAtom, user: userAtom }));
77
86
  * ```
78
87
  */
79
- export declare function jotaiStore(store: JotaiStoreLike, atoms: Record<string, object>): StoreLike;
88
+ export declare function jotaiStore<A>(store: JotaiStoreLike<A>, atoms: Record<string, A>): StoreLike;
80
89
  /** The minimum of an XState actor this adapter touches. */
81
90
  interface ActorLike {
82
91
  getSnapshot: () => unknown;
@@ -110,6 +119,132 @@ export declare function valtioStore<T extends object>(proxy: T, snapshot: (p: T)
110
119
  * ```
111
120
  */
112
121
  export declare function mobxStore<T>(observable: T, toJS: (value: T) => unknown, reaction: (track: () => unknown, effect: () => void) => () => void): StoreLike;
122
+ /** The minimum of a Recoil `Loadable` this adapter touches. */
123
+ interface RecoilLoadableLike {
124
+ /** 'hasValue' | 'loading' | 'hasError'. Widened to string: it arrives from outside this package. */
125
+ state: string;
126
+ /** The value, the pending promise, or the thrown error — which one depends on `state`. */
127
+ contents: unknown;
128
+ }
129
+ /** The minimum of a Recoil `Snapshot` this adapter touches, generic in the ATOM type. */
130
+ interface RecoilSnapshotLike<A> {
131
+ getLoadable: (atom: A) => RecoilLoadableLike;
132
+ }
133
+ /** How one Recoil atom is projected: the value never travels without the load state beside it. */
134
+ export interface RecoilAtomSnapshot {
135
+ status: string;
136
+ value: unknown;
137
+ error: string | null;
138
+ }
139
+ /**
140
+ * Expose a chosen set of Recoil atoms as one Reticle store.
141
+ *
142
+ * Three things about Recoil's API shape this adapter is built around, none of them optional:
143
+ *
144
+ * - **The atom map is forced, not an ergonomic choice.** Recoil has no enumerable registry of live
145
+ * atoms, so "the whole store" is not a thing that exists. Naming the atoms is the only way to
146
+ * snapshot them, and it doubles as a declaration of what matters — the same reason `jotaiStore`
147
+ * takes one.
148
+ * - **`.state`/`.contents`, never `.getValue()`.** On a pending async selector `getValue()` THROWS
149
+ * the pending promise, and on a failed one it throws the error. Either would take down the whole
150
+ * state read over one slow atom — so a loading atom reports `status: 'loading'` with a null value
151
+ * rather than silently becoming an empty object or an exception.
152
+ * - **One subscription, not one per atom.** Recoil exposes no per-atom subscription outside React
153
+ * (`jotaiStore` gets `store.sub(atom, …)`; there is no equivalent here). The transaction stream is
154
+ * the only public change signal, so the caller passes it in — the same "the library ships free
155
+ * functions, so hand them over" shape as `valtioStore` and `mobxStore`.
156
+ *
157
+ * Wire it from a bridge component, which is where `useRecoilTransactionObserver_UNSTABLE` lives:
158
+ *
159
+ * ```tsx
160
+ * const latest = useRef(snapshot_UNSTABLE());
161
+ * const listeners = useRef(new Set<() => void>()).current;
162
+ * useRecoilTransactionObserver_UNSTABLE(({ snapshot }) => {
163
+ * latest.current = snapshot;
164
+ * for (const l of listeners) l();
165
+ * });
166
+ * registerStore('recoil', recoilStore({ cart: cartAtom }, () => latest.current, (l) => {
167
+ * listeners.add(l);
168
+ * return () => listeners.delete(l);
169
+ * }));
170
+ * ```
171
+ */
172
+ export declare function recoilStore<A>(atoms: Record<string, A>, snapshot: () => RecoilSnapshotLike<A>, subscribe: StoreSubscribe): StoreLike;
173
+ /**
174
+ * The minimum of a Svelte store this adapter touches.
175
+ *
176
+ * `subscribe` may return either the unsubscribe function or an object carrying one — the store
177
+ * contract permits both, and an RxJS-shaped source returns the object form.
178
+ */
179
+ interface SvelteReadable {
180
+ subscribe: (run: (value: unknown) => void) => (() => void) | {
181
+ unsubscribe: () => void;
182
+ };
183
+ }
184
+ /**
185
+ * Expose a Svelte store as a Reticle store.
186
+ *
187
+ * Structurally unlike every other adapter in this file: they all wrap something that can be PULLED
188
+ * (`getState`, `getSnapshot`, `snapshot(proxy)`, `toJS`). A Svelte store has no pull side at all —
189
+ * `{ subscribe }` is the entire contract. What makes a pull possible anyway is the part of that
190
+ * contract which says `subscribe` must call back **immediately and synchronously** with the current
191
+ * value: subscribe, catch the value, unsubscribe, return it. That is precisely how `svelte/store`'s
192
+ * own `get()` is implemented, so it is a blessed read rather than a trick.
193
+ *
194
+ * Doing it that way is what keeps this adapter free of the `dispose` the obvious implementation
195
+ * needs. Caching the latest value would mean holding a permanent subscription, which means a
196
+ * teardown the other five adapters don't have, which means a leak the moment a caller forgets it.
197
+ * A transient subscription per read owns nothing and leaks nothing.
198
+ *
199
+ * ```ts
200
+ * registerStore('cart', svelteStore(cartStore));
201
+ * ```
202
+ */
203
+ export declare function svelteStore(readable: SvelteReadable, warn?: (message: string) => void): StoreLike;
204
+ /**
205
+ * The minimum of a Pinia store this adapter touches.
206
+ *
207
+ * Two things here look over-specified and are not, both found by compiling against the real library
208
+ * rather than against a fake written to the same belief as the adapter:
209
+ *
210
+ * - **`$state: object`, not `Record<string, unknown>`.** A Pinia store's `$state` is a concrete
211
+ * object type with named keys and no index signature, and TypeScript rejects those for a
212
+ * `Record<string, unknown>` parameter. `piniaStore(useCartStore())` did not compile — the same
213
+ * shape of failure as the Jotai contravariance note above, and equally invisible to a fake.
214
+ * - **`flush` as the literal union**, not `string`. The option object is checked contravariantly, so
215
+ * a widened `string` here makes the real `$subscribe` unassignable to this shape and the whole
216
+ * store stops matching.
217
+ */
218
+ interface PiniaStoreLike {
219
+ $state: object;
220
+ $subscribe: (callback: (mutation: unknown, state: unknown) => void, options?: {
221
+ detached?: boolean;
222
+ flush?: 'pre' | 'post' | 'sync';
223
+ }) => () => void;
224
+ }
225
+ /**
226
+ * Expose a Pinia store as a Reticle store. `$state` / `$subscribe` are close to `{getState,
227
+ * subscribe}` but not close enough for `registerStore`'s duck-type, which is the whole reason a Vue
228
+ * app currently connects and then reports no state at all.
229
+ *
230
+ * Both `$subscribe` options are load-bearing:
231
+ *
232
+ * - **`detached`** keeps the subscription alive past the component that happened to register the
233
+ * store. Without it Pinia tears the listener down on unmount, so a store registered from inside a
234
+ * component goes permanently silent after the first route change — readable, but never again
235
+ * emitting a STATE_CHANGE, which reads exactly like an app that stopped changing.
236
+ * - **`flush: 'sync'`** puts the notification inside the action's attribution window. The default
237
+ * (`'post'`) defers it past the Vue update tick, and a state change that lands after the window
238
+ * closed is no longer linked to the click that caused it in the causal summary.
239
+ *
240
+ * `$state` carries state, not getters — a Pinia getter is derived, so asserting on the state it
241
+ * derives from is the stronger assertion anyway.
242
+ *
243
+ * ```ts
244
+ * registerStore('cart', piniaStore(useCartStore()));
245
+ * ```
246
+ */
247
+ export declare function piniaStore(store: PiniaStoreLike): StoreLike;
113
248
  /**
114
249
  * Build a store whose value is PUSHED in rather than pulled — for state that has no object to read.
115
250
  *
@@ -125,4 +260,3 @@ export declare function pushStore(initial: unknown): {
125
260
  push: (value: unknown) => void;
126
261
  };
127
262
  export {};
128
- //# sourceMappingURL=store-adapters.d.ts.map
@@ -1,3 +1,4 @@
1
+ import { nativeWarn } from '../timers/native-console.js';
1
2
  /**
2
3
  * Expose a TanStack Query cache as a Reticle store, keyed by query key.
3
4
  *
@@ -106,6 +107,183 @@ export function mobxStore(observable, toJS, reaction) {
106
107
  subscribe: (listener) => reaction(() => toJS(observable), () => listener()),
107
108
  };
108
109
  }
110
+ /**
111
+ * The load states a Recoil `Loadable` reports. Named because the projection below branches on them
112
+ * and a bare 'hasValue' in three places is exactly the free string the rules forbid.
113
+ */
114
+ const RecoilLoadState = {
115
+ HAS_VALUE: 'hasValue',
116
+ LOADING: 'loading',
117
+ HAS_ERROR: 'hasError',
118
+ };
119
+ function projectLoadable(loadable) {
120
+ if (loadable.state === RecoilLoadState.HAS_ERROR) {
121
+ const contents = loadable.contents;
122
+ return {
123
+ status: loadable.state,
124
+ value: null,
125
+ error: contents instanceof Error ? contents.message : String(contents),
126
+ };
127
+ }
128
+ // A LOADING loadable's `contents` is the pending promise. Sending it would serialize to `{}` and
129
+ // read as an empty value — so the value stays null and the status carries the truth.
130
+ if (loadable.state !== RecoilLoadState.HAS_VALUE) {
131
+ return { status: loadable.state, value: null, error: null };
132
+ }
133
+ return { status: loadable.state, value: loadable.contents, error: null };
134
+ }
135
+ /**
136
+ * Expose a chosen set of Recoil atoms as one Reticle store.
137
+ *
138
+ * Three things about Recoil's API shape this adapter is built around, none of them optional:
139
+ *
140
+ * - **The atom map is forced, not an ergonomic choice.** Recoil has no enumerable registry of live
141
+ * atoms, so "the whole store" is not a thing that exists. Naming the atoms is the only way to
142
+ * snapshot them, and it doubles as a declaration of what matters — the same reason `jotaiStore`
143
+ * takes one.
144
+ * - **`.state`/`.contents`, never `.getValue()`.** On a pending async selector `getValue()` THROWS
145
+ * the pending promise, and on a failed one it throws the error. Either would take down the whole
146
+ * state read over one slow atom — so a loading atom reports `status: 'loading'` with a null value
147
+ * rather than silently becoming an empty object or an exception.
148
+ * - **One subscription, not one per atom.** Recoil exposes no per-atom subscription outside React
149
+ * (`jotaiStore` gets `store.sub(atom, …)`; there is no equivalent here). The transaction stream is
150
+ * the only public change signal, so the caller passes it in — the same "the library ships free
151
+ * functions, so hand them over" shape as `valtioStore` and `mobxStore`.
152
+ *
153
+ * Wire it from a bridge component, which is where `useRecoilTransactionObserver_UNSTABLE` lives:
154
+ *
155
+ * ```tsx
156
+ * const latest = useRef(snapshot_UNSTABLE());
157
+ * const listeners = useRef(new Set<() => void>()).current;
158
+ * useRecoilTransactionObserver_UNSTABLE(({ snapshot }) => {
159
+ * latest.current = snapshot;
160
+ * for (const l of listeners) l();
161
+ * });
162
+ * registerStore('recoil', recoilStore({ cart: cartAtom }, () => latest.current, (l) => {
163
+ * listeners.add(l);
164
+ * return () => listeners.delete(l);
165
+ * }));
166
+ * ```
167
+ */
168
+ export function recoilStore(atoms, snapshot, subscribe) {
169
+ return {
170
+ // Resolved per call, not captured once: a Recoil snapshot is IMMUTABLE, so an adapter holding
171
+ // one would keep answering from the transaction it was built in and never see another write.
172
+ getState: () => {
173
+ const current = snapshot();
174
+ const out = {};
175
+ for (const [name, atom] of Object.entries(atoms)) {
176
+ out[name] = projectLoadable(current.getLoadable(atom));
177
+ }
178
+ return out;
179
+ },
180
+ subscribe,
181
+ };
182
+ }
183
+ function stopSubscription(handle) {
184
+ if (typeof handle === 'function') {
185
+ handle();
186
+ return;
187
+ }
188
+ handle.unsubscribe();
189
+ }
190
+ /**
191
+ * Expose a Svelte store as a Reticle store.
192
+ *
193
+ * Structurally unlike every other adapter in this file: they all wrap something that can be PULLED
194
+ * (`getState`, `getSnapshot`, `snapshot(proxy)`, `toJS`). A Svelte store has no pull side at all —
195
+ * `{ subscribe }` is the entire contract. What makes a pull possible anyway is the part of that
196
+ * contract which says `subscribe` must call back **immediately and synchronously** with the current
197
+ * value: subscribe, catch the value, unsubscribe, return it. That is precisely how `svelte/store`'s
198
+ * own `get()` is implemented, so it is a blessed read rather than a trick.
199
+ *
200
+ * Doing it that way is what keeps this adapter free of the `dispose` the obvious implementation
201
+ * needs. Caching the latest value would mean holding a permanent subscription, which means a
202
+ * teardown the other five adapters don't have, which means a leak the moment a caller forgets it.
203
+ * A transient subscription per read owns nothing and leaks nothing.
204
+ *
205
+ * ```ts
206
+ * registerStore('cart', svelteStore(cartStore));
207
+ * ```
208
+ */
209
+ export function svelteStore(readable, warn = nativeWarn) {
210
+ let warnedAboutLazyStore = false;
211
+ return {
212
+ getState: () => {
213
+ let value;
214
+ let called = false;
215
+ stopSubscription(readable.subscribe((next) => {
216
+ value = next;
217
+ called = true;
218
+ }));
219
+ // A store that did NOT call back synchronously (an RxJS Observable that is not a
220
+ // BehaviorSubject, a hand-rolled store that breaks the contract) leaves `value` undefined —
221
+ // indistinguishable from a store legitimately holding undefined. Reading empty when the answer
222
+ // is "unknown" is the false green this project exists to prevent, so say it once, out loud.
223
+ if (!called && !warnedAboutLazyStore) {
224
+ warnedAboutLazyStore = true;
225
+ warn('[reticle] a store passed to svelteStore did not call its subscriber synchronously, so ' +
226
+ 'its current value cannot be read. reticle_state will report undefined for it. Svelte ' +
227
+ 'stores always call back immediately; an RxJS Observable does not unless it is a ' +
228
+ 'BehaviorSubject.');
229
+ }
230
+ return value;
231
+ },
232
+ subscribe: (listener) => {
233
+ // Swallow whatever arrives DURING the subscribe call. Forwarding it would emit a STATE_CHANGE
234
+ // at REGISTRATION time for a change that never happened — a diff of nothing that shows up in
235
+ // causal summaries and that a {kind:'state'} predicate could satisfy without the app doing
236
+ // anything at all.
237
+ //
238
+ // The window is what makes this correct, rather than "swallow the first callback whenever it
239
+ // arrives". A store that does NOT call back synchronously (an RxJS Observable that is not a
240
+ // BehaviorSubject — the exact store `getState` warns about) has no registration callback to
241
+ // drop, so counting callbacks ate its first REAL change: no STATE_CHANGE, and a
242
+ // {kind:'state'} assertion with nothing to match. A silently missed state change is the false
243
+ // green this project exists to prevent, so the rule is positional, not ordinal.
244
+ let inSubscribeCall = true;
245
+ const handle = readable.subscribe(() => {
246
+ if (!inSubscribeCall)
247
+ listener();
248
+ });
249
+ inSubscribeCall = false;
250
+ return () => stopSubscription(handle);
251
+ },
252
+ };
253
+ }
254
+ /** Pinia `$subscribe` options. `sync` and `detached` are library option values, not free strings. */
255
+ const PINIA_SUBSCRIBE_OPTIONS = { detached: true, flush: 'sync' };
256
+ /**
257
+ * Expose a Pinia store as a Reticle store. `$state` / `$subscribe` are close to `{getState,
258
+ * subscribe}` but not close enough for `registerStore`'s duck-type, which is the whole reason a Vue
259
+ * app currently connects and then reports no state at all.
260
+ *
261
+ * Both `$subscribe` options are load-bearing:
262
+ *
263
+ * - **`detached`** keeps the subscription alive past the component that happened to register the
264
+ * store. Without it Pinia tears the listener down on unmount, so a store registered from inside a
265
+ * component goes permanently silent after the first route change — readable, but never again
266
+ * emitting a STATE_CHANGE, which reads exactly like an app that stopped changing.
267
+ * - **`flush: 'sync'`** puts the notification inside the action's attribution window. The default
268
+ * (`'post'`) defers it past the Vue update tick, and a state change that lands after the window
269
+ * closed is no longer linked to the click that caused it in the causal summary.
270
+ *
271
+ * `$state` carries state, not getters — a Pinia getter is derived, so asserting on the state it
272
+ * derives from is the stronger assertion anyway.
273
+ *
274
+ * ```ts
275
+ * registerStore('cart', piniaStore(useCartStore()));
276
+ * ```
277
+ */
278
+ export function piniaStore(store) {
279
+ return {
280
+ // `$state` is a live reactive proxy, so this reads through to the current value rather than
281
+ // snapshotting at registration. The transport serializer walks it like any object and guards
282
+ // each key, which is what keeps a throwing reactive trap from costing the whole read.
283
+ getState: () => store.$state,
284
+ subscribe: (listener) => store.$subscribe(() => listener(), { ...PINIA_SUBSCRIBE_OPTIONS }),
285
+ };
286
+ }
109
287
  /**
110
288
  * Build a store whose value is PUSHED in rather than pulled — for state that has no object to read.
111
289
  *
@@ -132,4 +310,3 @@ export function pushStore(initial) {
132
310
  },
133
311
  };
134
312
  }
135
- //# sourceMappingURL=store-adapters.js.map
@@ -48,4 +48,3 @@ export declare function readStoresWithTruncation(only?: string): {
48
48
  stores: Record<string, unknown>;
49
49
  truncation?: Record<string, TruncationReport>;
50
50
  };
51
- //# sourceMappingURL=stores.d.ts.map
@@ -42,6 +42,14 @@ export function registerStore(name, source, subscribe) {
42
42
  notifyRegistered(name, getter, sub);
43
43
  return;
44
44
  }
45
+ if (typeof source !== 'function') {
46
+ const hasSubscribe = typeof source === 'object' && source !== null && 'subscribe' in source;
47
+ nativeWarn(`[reticle] store "${name}" is neither a getter function nor a {getState, subscribe} store` +
48
+ (hasSubscribe
49
+ ? ` — it has subscribe but no getState. Wrap it with an adapter or add getState.`
50
+ : '.'));
51
+ return;
52
+ }
45
53
  stores.set(name, source);
46
54
  if (subscribe !== undefined) {
47
55
  subscribers.set(name, subscribe);
@@ -141,4 +149,3 @@ function warnSilentStoreOnce(name) {
141
149
  `changes are invisible: no STATE_CHANGE events, no state diffs in causal summaries, and a state ` +
142
150
  `predicate will never see it update. Pass the store object (or a subscribe callback) to fix.`);
143
151
  }
144
- //# sourceMappingURL=stores.js.map
@@ -16,4 +16,3 @@ export declare function modeForCommand(commandName: string): PresenterMode;
16
16
  * element". Falls back to the bare verb when no target is in the args.
17
17
  */
18
18
  export declare function presentStatus(commandName: string, args?: Record<string, unknown>): string;
19
- //# sourceMappingURL=reticle-presenter-helpers.d.ts.map
@@ -78,4 +78,3 @@ function queryTarget(q) {
78
78
  return name !== undefined ? `"${value}" (${name})` : `"${value}"`;
79
79
  return name !== undefined ? `"${name}"` : undefined;
80
80
  }
81
- //# sourceMappingURL=reticle-presenter-helpers.js.map
package/dist/reticle.d.ts CHANGED
@@ -1,62 +1,7 @@
1
1
  import { EventType, RETICLE_URL_PARAM, type ReticleEvent } from '@reticlehq/core';
2
2
  import { type CapabilitiesInput } from './registry/capabilities.js';
3
- export interface ReticleConnectOptions {
4
- /** WS endpoint of the local bridge. Defaults to ws://localhost:<port><path>. */
5
- url?: string;
6
- /** Human-friendly session label so the agent can target the right tab. */
7
- session?: string;
8
- /**
9
- * Stable project identity, normally stamped by the build plugin (e.g. "acme-web-9f3c1d"). Lets the
10
- * agent scope to the right app even when its dev server boots on an unexpected port. Optional.
11
- */
12
- projectId?: string;
13
- /** Browser/bridge pairing token. Required when either endpoint is non-localhost. */
14
- token?: string;
15
- /** Explicitly allow Reticle on a non-localhost page or bridge. Requires token. */
16
- allowNonLocalhost?: boolean;
17
- /**
18
- * Escape hatch for the production backstop. Reticle is dev-only and refuses to connect when the
19
- * build reports NODE_ENV=production (an SSR healthcheck or a prod bundle opened locally would
20
- * otherwise activate). The real fix is to gate the import behind `import.meta.env.DEV` so it's
21
- * tree-shaken out; this flag only exists for the rare intentional prod diagnostic.
22
- */
23
- allowInProduction?: boolean;
24
- /** Show a small in-page status chip (connection + event count). */
25
- overlay?: boolean;
26
- /**
27
- * Capture request/response bodies on net.request events (dev-only; text-like content only,
28
- * sensitive keys redacted, per-body capped). Off by default — bodies cost tokens and can carry PII.
29
- */
30
- captureNetworkBodies?: boolean;
31
- /** Presenter mode: glow border, animated cursor, click/hover effects, narration HUD. */
32
- present?: boolean;
33
- /** Per-action pacing (ms) in presenter mode so a human can follow. Default 450. */
34
- pace?: number;
35
- /** Min ms each narration line stays visible before the next replaces it (presenter). Default 3000. */
36
- narrationDwellMs?: number;
37
- /**
38
- * Border behavior in presenter mode: 'session' (default) persists the border for the whole
39
- * session; 'busy' restores the fade-after-idle behavior.
40
- */
41
- border?: 'session' | 'busy';
42
- /** Max accumulated activity-log rows before the oldest are pruned (presenter). Default 50. */
43
- logMax?: number;
44
- /**
45
- * Mount the floating human-recorder toolbar (Record/Stop/Annotate).
46
- * Default off — purely additive, dev-only.
47
- */
48
- recorder?: boolean;
49
- /**
50
- * Mount the "Flag a bug" annotator: the human clicks an element that looks wrong, types what's
51
- * wrong, and Reticle emits a HUMAN_MARK the agent drains via reticle_review. Defaults to ON with the
52
- * presenter (it's the human's side of the look→act→assert loop); pass `annotate: false` to suppress.
53
- */
54
- annotate?: boolean;
55
- /** Live-control: overridable ended-border fade delay (native timer). Default 4000. */
56
- endedFadeMs?: number;
57
- /** Session auto-end after this much agent idle (presenter). Default 5min; agent-tunable via reticle_session. */
58
- idleEndMs?: number;
59
- }
3
+ import type { ReticleConnectOptions } from './connect-options.js';
4
+ export type { ReticleConnectOptions };
60
5
  /**
61
6
  * Runtime backstop for the dev-only SDK: block connecting when the build reports production, unless
62
7
  * explicitly overridden. Pure so it's testable; connect reads NODE_ENV safely (process may be absent
@@ -64,7 +9,9 @@ export interface ReticleConnectOptions {
64
9
  * behind `import.meta.env.DEV` so the SDK is dead-code-eliminated from prod bundles entirely.
65
10
  */
66
11
  export declare function shouldBlockProduction(nodeEnv: string | undefined, allowInProduction: boolean): boolean;
67
- export declare function connectionPolicy(pageHostname: string, bridgeUrl: string, allowNonLocalhost: boolean, token: string | undefined): {
12
+ export declare function connectionPolicy(pageHostname: string, bridgeUrl: string, allowNonLocalhost: boolean, token: string | undefined,
13
+ /** The page's protocol. Desktop webviews (file:, app:, tauri:) are local despite a non-loopback host. */
14
+ pageProtocol?: string): {
68
15
  allowed: boolean;
69
16
  reason?: string;
70
17
  };
@@ -134,4 +81,3 @@ export declare class Reticle {
134
81
  endSession(): void;
135
82
  disconnect(): void;
136
83
  }
137
- //# sourceMappingURL=reticle.d.ts.map