@reticlehq/browser 2.4.0 → 2.5.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 (71) hide show
  1. package/dist/actions/actions-dom.js +10 -10
  2. package/dist/actions/actions.d.ts +13 -0
  3. package/dist/actions/actions.js +45 -15
  4. package/dist/actions/scroll.js +1 -1
  5. package/dist/commands/commands.js +27 -22
  6. package/dist/connect-options.d.ts +6 -0
  7. package/dist/dom/a11y.js +20 -20
  8. package/dist/dom/auto-anchor.js +1 -1
  9. package/dist/dom/blind-spots.js +1 -1
  10. package/dist/dom/chart.js +5 -5
  11. package/dist/dom/desktop-capture.d.ts +2 -1
  12. package/dist/dom/desktop-capture.js +4 -4
  13. package/dist/dom/occlusion.js +2 -2
  14. package/dist/dom/query.js +28 -18
  15. package/dist/dom/realm.js +13 -13
  16. package/dist/dom/refs.d.ts +14 -0
  17. package/dist/dom/refs.js +20 -1
  18. package/dist/dom/shadow-registry.js +2 -2
  19. package/dist/dom/snapshot.js +7 -7
  20. package/dist/dom/source.js +3 -3
  21. package/dist/dom/theme.js +6 -6
  22. package/dist/dom/virtualized.js +1 -1
  23. package/dist/observers/blind-spots.js +2 -2
  24. package/dist/observers/console.js +3 -3
  25. package/dist/observers/dom.js +5 -5
  26. package/dist/observers/download.d.ts +2 -1
  27. package/dist/observers/download.js +2 -2
  28. package/dist/observers/focus.js +1 -1
  29. package/dist/observers/frames.js +1 -1
  30. package/dist/observers/health.js +70 -2
  31. package/dist/observers/install-all.d.ts +2 -1
  32. package/dist/observers/ipc.d.ts +2 -7
  33. package/dist/observers/ipc.js +7 -7
  34. package/dist/observers/network-body.js +2 -2
  35. package/dist/observers/network-redact.js +5 -5
  36. package/dist/observers/network-stream.js +2 -2
  37. package/dist/observers/network.d.ts +3 -3
  38. package/dist/observers/network.js +9 -9
  39. package/dist/observers/perf.js +1 -1
  40. package/dist/observers/scroll.js +1 -1
  41. package/dist/observers/state.js +1 -1
  42. package/dist/observers/storage.js +7 -7
  43. package/dist/presenter/presenter-brand.d.ts +19 -0
  44. package/dist/presenter/presenter-brand.js +21 -0
  45. package/dist/presenter/presenter-config.d.ts +17 -0
  46. package/dist/presenter/presenter-config.js +21 -0
  47. package/dist/presenter/presenter-controls.d.ts +1 -11
  48. package/dist/presenter/presenter-controls.js +6 -7
  49. package/dist/presenter/presenter-log.js +2 -2
  50. package/dist/presenter/presenter-run-state.js +1 -1
  51. package/dist/presenter/presenter-styles.d.ts +1 -1
  52. package/dist/presenter/presenter-styles.js +20 -9
  53. package/dist/presenter/presenter-tally.js +3 -3
  54. package/dist/presenter/presenter-test-helpers.d.ts +2 -1
  55. package/dist/presenter/presenter-test-helpers.js +2 -2
  56. package/dist/presenter/presenter-verbs.js +16 -11
  57. package/dist/presenter/presenter.js +13 -10
  58. package/dist/recorder/recorder.js +10 -9
  59. package/dist/registry/store-adapters.js +1 -1
  60. package/dist/registry/stores.js +3 -3
  61. package/dist/reticle-presenter-helpers.js +3 -3
  62. package/dist/reticle.js +31 -13
  63. package/dist/review/annotator.js +7 -7
  64. package/dist/security/serialization.js +9 -9
  65. package/dist/session-continuity.d.ts +17 -0
  66. package/dist/session-continuity.js +42 -0
  67. package/dist/timers/clock.js +4 -4
  68. package/dist/timers/native-console.js +1 -1
  69. package/dist/timers/native-timers.js +4 -4
  70. package/dist/transport/transport.js +5 -5
  71. package/package.json +6 -4
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Keep a tab's session id across a reload.
3
+ *
4
+ * The id was minted fresh on every `connect()`. A reload is a new document, so the page came back as
5
+ * a NEW session while the agent still held the old id — and every call after
6
+ * `reticle_navigate { reload: true }` was refused with "no browser session connected". Reported on 6
7
+ * of 6 apps; a reload is the most ordinary thing an agent does.
8
+ *
9
+ * `sessionStorage` is exactly the right scope: it survives reloads and same-tab navigations, and is
10
+ * NOT shared with another tab of the same app — so two tabs stay two sessions, which is what the
11
+ * multi-session tooling assumes.
12
+ *
13
+ * An explicit id still wins. `connect({ session })` and the `__reticle_session` URL param a lease
14
+ * stamps are callers ASSERTING an identity, not asking for one; they are also remembered, so a
15
+ * leased tab that reloads rejoins its own lease.
16
+ */
17
+ /** Where the id lives. Namespaced like the URL param it mirrors. */
18
+ const STORAGE_KEY = '__reticle_session';
19
+ export function rememberSessionLabel(explicit, store, generate) {
20
+ const read = () => {
21
+ try {
22
+ const value = store?.getItem(STORAGE_KEY);
23
+ return value !== null && value !== undefined && value.length > 0 ? value : undefined;
24
+ }
25
+ catch {
26
+ return undefined; // a sandboxed iframe throws on access; a session must still start
27
+ }
28
+ };
29
+ const write = (value) => {
30
+ try {
31
+ store?.setItem(STORAGE_KEY, value);
32
+ }
33
+ catch {
34
+ /* same: never block connect on storage */
35
+ }
36
+ };
37
+ // `explicit` may be '' — resolveSessionLabel yields that for "none given" — and `??` would take it.
38
+ const asked = explicit !== undefined && explicit.length > 0 ? explicit : undefined;
39
+ const chosen = asked ?? read() ?? generate();
40
+ write(chosen);
41
+ return chosen;
42
+ }
@@ -12,7 +12,7 @@ export function isClockFrozen() {
12
12
  return installed;
13
13
  }
14
14
  export function freezeClock() {
15
- if (installed || typeof window === 'undefined')
15
+ if (installed || 'undefined' === typeof window)
16
16
  return;
17
17
  installed = true;
18
18
  virtualNow = 0;
@@ -81,7 +81,7 @@ export function advanceClock(ms) {
81
81
  * in still has ~3s to go), and the ids the app is holding keep working — see the translation note.
82
82
  */
83
83
  export function resetClock() {
84
- if (!installed || originals === null)
84
+ if (!installed || null === originals)
85
85
  return;
86
86
  const natives = originals;
87
87
  const pending = tasks;
@@ -95,7 +95,7 @@ export function resetClock() {
95
95
  tasks = [];
96
96
  installed = false;
97
97
  virtualNow = 0;
98
- if (pending.length === 0)
98
+ if (0 === pending.length)
99
99
  return;
100
100
  // Re-arm the app's pending work onto REAL timers, translating ids as we go.
101
101
  //
@@ -112,7 +112,7 @@ export function resetClock() {
112
112
  const reArmed = new Map();
113
113
  const done = (virtualId) => {
114
114
  reArmed.delete(virtualId);
115
- if (reArmed.size === 0)
115
+ if (0 === reArmed.size)
116
116
  restoreRawClears(natives);
117
117
  };
118
118
  for (const task of pending) {
@@ -3,7 +3,7 @@
3
3
  // MUST use this instead of bare `console.warn` — otherwise they emit spurious CONSOLE_WARN
4
4
  // events to the agent, polluting the observation stream with SDK internals.
5
5
  const g = globalThis;
6
- const realWarn = typeof g.console?.warn === 'function' ? g.console.warn.bind(g.console) : null;
6
+ const realWarn = 'function' === typeof g.console?.warn ? g.console.warn.bind(g.console) : null;
7
7
  export const nativeWarn = (...args) => {
8
8
  realWarn?.(...args);
9
9
  };
@@ -5,10 +5,10 @@
5
5
  // Important: we bind the reference now. Resolving `setTimeout` at call time would pick up the
6
6
  // patched (frozen) timer once reticle_clock freezes the clock — which would hang the SDK.
7
7
  const g = globalThis;
8
- const realSetTimeout = typeof g.setTimeout === 'function' ? g.setTimeout.bind(g) : null;
9
- const realClearTimeout = typeof g.clearTimeout === 'function' ? g.clearTimeout.bind(g) : null;
10
- const realRaf = typeof g.requestAnimationFrame === 'function' ? g.requestAnimationFrame.bind(g) : null;
11
- const realPerfNow = typeof g.performance?.now === 'function' ? g.performance.now.bind(g.performance) : null;
8
+ const realSetTimeout = 'function' === typeof g.setTimeout ? g.setTimeout.bind(g) : null;
9
+ const realClearTimeout = 'function' === typeof g.clearTimeout ? g.clearTimeout.bind(g) : null;
10
+ const realRaf = 'function' === typeof g.requestAnimationFrame ? g.requestAnimationFrame.bind(g) : null;
11
+ const realPerfNow = 'function' === typeof g.performance?.now ? g.performance.now.bind(g.performance) : null;
12
12
  /**
13
13
  * Monotonic "now" bound at load. clock.ts deliberately does NOT patch performance.now, so this
14
14
  * stays live even when the app clock (reticle_clock) is frozen — the presenter state machine relies
@@ -4,10 +4,10 @@ import { nativeWarn } from '../timers/native-console.js';
4
4
  import { safeStringify } from '../security/serialization.js';
5
5
  /** Default visibility source: fire `handler` whenever the document returns to the foreground. */
6
6
  function subscribeDocumentVisible(handler) {
7
- if (typeof document === 'undefined')
7
+ if ('undefined' === typeof document)
8
8
  return () => undefined;
9
9
  const listener = () => {
10
- if (document.visibilityState === 'visible')
10
+ if ('visible' === document.visibilityState)
11
11
  handler();
12
12
  };
13
13
  document.addEventListener('visibilitychange', listener);
@@ -60,7 +60,7 @@ export class Transport {
60
60
  this.#now = deps.now ?? nativeNow;
61
61
  }
62
62
  connect() {
63
- if (typeof WebSocket === 'undefined')
63
+ if ('undefined' === typeof WebSocket)
64
64
  return;
65
65
  this.#closed = false;
66
66
  this.#unsubscribeVisible ??= (this.#deps.onVisible ?? subscribeDocumentVisible)(() => this.#onVisible());
@@ -114,7 +114,7 @@ export class Transport {
114
114
  };
115
115
  ws.onmessage = (event) => {
116
116
  const data = event.data;
117
- void this.#onMessage(typeof data === 'string' ? data : String(data));
117
+ void this.#onMessage('string' === typeof data ? data : String(data));
118
118
  };
119
119
  ws.onclose = (event) => {
120
120
  this.#ws = undefined;
@@ -189,7 +189,7 @@ export class Transport {
189
189
  // failure mode where the cause is knowable and was being thrown away. Reply if we can recover
190
190
  // an id; a message without one is not a command we can answer at all.
191
191
  const id = parsed?.id;
192
- if (typeof id === 'string' && id.length > 0) {
192
+ if ('string' === typeof id && id.length > 0) {
193
193
  this.#sendRaw(safeStringify({
194
194
  kind: MessageKind.COMMAND_RESULT,
195
195
  id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reticlehq/browser",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "description": "Reticle browser SDK — installs observers, builds semantic snapshots, executes actions, talks to the bridge.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -9,11 +9,12 @@
9
9
  ".": {
10
10
  "types": "./dist/index.d.ts",
11
11
  "default": "./dist/index.js"
12
- }
12
+ },
13
+ "./package.json": "./package.json"
13
14
  },
14
15
  "dependencies": {
15
16
  "@testing-library/dom": "^10.4.1",
16
- "@reticlehq/core": "2.4.0"
17
+ "@reticlehq/core": "2.5.0"
17
18
  },
18
19
  "devDependencies": {
19
20
  "@tanstack/query-core": "^5.101.4",
@@ -61,6 +62,7 @@
61
62
  "build": "tsc -b",
62
63
  "typecheck": "tsc -b",
63
64
  "lint": "eslint src",
64
- "test:unit": "vitest run src --passWithNoTests"
65
+ "test:unit": "vitest run src --passWithNoTests",
66
+ "test:coverage": "vitest run src --passWithNoTests --coverage.enabled --coverage.provider=v8 --coverage.reporter=text-summary --coverage.include='src/**/*.ts' --coverage.exclude='src/**/*.test.ts'"
65
67
  }
66
68
  }