@reticlehq/browser 2.1.0 → 2.2.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 (156) hide show
  1. package/NOTICE +5 -0
  2. package/dist/actions/actions.d.ts +1 -1
  3. package/dist/actions/actions.d.ts.map +1 -1
  4. package/dist/actions/actions.js +37 -19
  5. package/dist/actions/actions.js.map +1 -1
  6. package/dist/commands/commands.d.ts +2 -0
  7. package/dist/commands/commands.d.ts.map +1 -1
  8. package/dist/commands/commands.js +20 -6
  9. package/dist/commands/commands.js.map +1 -1
  10. package/dist/dom/a11y.d.ts +17 -6
  11. package/dist/dom/a11y.d.ts.map +1 -1
  12. package/dist/dom/a11y.js +68 -32
  13. package/dist/dom/a11y.js.map +1 -1
  14. package/dist/dom/auto-anchor.d.ts +10 -10
  15. package/dist/dom/auto-anchor.js +10 -10
  16. package/dist/dom/blind-spots.d.ts +14 -0
  17. package/dist/dom/blind-spots.d.ts.map +1 -0
  18. package/dist/dom/blind-spots.js +30 -0
  19. package/dist/dom/blind-spots.js.map +1 -0
  20. package/dist/dom/chart.d.ts +93 -0
  21. package/dist/dom/chart.d.ts.map +1 -0
  22. package/dist/dom/chart.js +119 -0
  23. package/dist/dom/chart.js.map +1 -0
  24. package/dist/dom/dom-ignore.d.ts.map +1 -1
  25. package/dist/dom/dom-ignore.js +7 -2
  26. package/dist/dom/dom-ignore.js.map +1 -1
  27. package/dist/dom/query.d.ts +17 -4
  28. package/dist/dom/query.d.ts.map +1 -1
  29. package/dist/dom/query.js +185 -30
  30. package/dist/dom/query.js.map +1 -1
  31. package/dist/dom/refs.d.ts +25 -2
  32. package/dist/dom/refs.d.ts.map +1 -1
  33. package/dist/dom/refs.js +78 -3
  34. package/dist/dom/refs.js.map +1 -1
  35. package/dist/dom/snapshot.d.ts +6 -0
  36. package/dist/dom/snapshot.d.ts.map +1 -1
  37. package/dist/dom/snapshot.js +6 -1
  38. package/dist/dom/snapshot.js.map +1 -1
  39. package/dist/dom/source.d.ts +40 -0
  40. package/dist/dom/source.d.ts.map +1 -0
  41. package/dist/dom/source.js +59 -0
  42. package/dist/dom/source.js.map +1 -0
  43. package/dist/dom/theme.js +2 -2
  44. package/dist/dom/theme.js.map +1 -1
  45. package/dist/index.d.ts +6 -2
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +9 -1
  48. package/dist/index.js.map +1 -1
  49. package/dist/observers/blind-spots.d.ts +19 -0
  50. package/dist/observers/blind-spots.d.ts.map +1 -0
  51. package/dist/observers/blind-spots.js +89 -0
  52. package/dist/observers/blind-spots.js.map +1 -0
  53. package/dist/observers/console.d.ts.map +1 -1
  54. package/dist/observers/console.js +39 -6
  55. package/dist/observers/console.js.map +1 -1
  56. package/dist/observers/dom.d.ts.map +1 -1
  57. package/dist/observers/dom.js +68 -9
  58. package/dist/observers/dom.js.map +1 -1
  59. package/dist/observers/focus.d.ts +10 -0
  60. package/dist/observers/focus.d.ts.map +1 -0
  61. package/dist/observers/focus.js +49 -0
  62. package/dist/observers/focus.js.map +1 -0
  63. package/dist/observers/network-body.d.ts +19 -0
  64. package/dist/observers/network-body.d.ts.map +1 -0
  65. package/dist/observers/network-body.js +143 -0
  66. package/dist/observers/network-body.js.map +1 -0
  67. package/dist/observers/network-redact.d.ts +8 -0
  68. package/dist/observers/network-redact.d.ts.map +1 -0
  69. package/dist/observers/network-redact.js +66 -0
  70. package/dist/observers/network-redact.js.map +1 -0
  71. package/dist/observers/network.d.ts +13 -6
  72. package/dist/observers/network.d.ts.map +1 -1
  73. package/dist/observers/network.js +288 -162
  74. package/dist/observers/network.js.map +1 -1
  75. package/dist/observers/route.d.ts.map +1 -1
  76. package/dist/observers/route.js +15 -9
  77. package/dist/observers/route.js.map +1 -1
  78. package/dist/observers/scroll.d.ts.map +1 -1
  79. package/dist/observers/scroll.js +35 -8
  80. package/dist/observers/scroll.js.map +1 -1
  81. package/dist/observers/state.d.ts +20 -0
  82. package/dist/observers/state.d.ts.map +1 -0
  83. package/dist/observers/state.js +87 -0
  84. package/dist/observers/state.js.map +1 -0
  85. package/dist/observers/storage.d.ts +9 -0
  86. package/dist/observers/storage.d.ts.map +1 -1
  87. package/dist/observers/storage.js +105 -3
  88. package/dist/observers/storage.js.map +1 -1
  89. package/dist/observers/types.d.ts +15 -0
  90. package/dist/observers/types.d.ts.map +1 -1
  91. package/dist/observers/types.js +29 -1
  92. package/dist/observers/types.js.map +1 -1
  93. package/dist/patching/capture-method.d.ts +27 -0
  94. package/dist/patching/capture-method.d.ts.map +1 -0
  95. package/dist/patching/capture-method.js +37 -0
  96. package/dist/patching/capture-method.js.map +1 -0
  97. package/dist/presenter/presenter-config.d.ts +3 -3
  98. package/dist/presenter/presenter-config.d.ts.map +1 -1
  99. package/dist/presenter/presenter-config.js +3 -3
  100. package/dist/presenter/presenter-config.js.map +1 -1
  101. package/dist/presenter/presenter-controls.d.ts +1 -1
  102. package/dist/presenter/presenter-controls.d.ts.map +1 -1
  103. package/dist/presenter/presenter-controls.js +3 -3
  104. package/dist/presenter/presenter-controls.js.map +1 -1
  105. package/dist/presenter/presenter-glow.d.ts +1 -1
  106. package/dist/presenter/presenter-glow.js +1 -1
  107. package/dist/presenter/presenter-styles.d.ts +1 -1
  108. package/dist/presenter/presenter-styles.d.ts.map +1 -1
  109. package/dist/presenter/presenter-styles.js +12 -3
  110. package/dist/presenter/presenter-styles.js.map +1 -1
  111. package/dist/presenter/presenter.d.ts +4 -4
  112. package/dist/presenter/presenter.d.ts.map +1 -1
  113. package/dist/presenter/presenter.js +5 -5
  114. package/dist/presenter/presenter.js.map +1 -1
  115. package/dist/recorder/recorder.d.ts +8 -8
  116. package/dist/recorder/recorder.d.ts.map +1 -1
  117. package/dist/recorder/recorder.js +9 -9
  118. package/dist/recorder/recorder.js.map +1 -1
  119. package/dist/registry/capabilities.d.ts +2 -2
  120. package/dist/registry/capabilities.d.ts.map +1 -1
  121. package/dist/registry/capabilities.js +1 -1
  122. package/dist/registry/capabilities.js.map +1 -1
  123. package/dist/registry/domains.d.ts +1 -1
  124. package/dist/registry/domains.js +1 -1
  125. package/dist/registry/emitter.d.ts +2 -2
  126. package/dist/registry/emitter.js +2 -2
  127. package/dist/registry/store-adapters.d.ts +128 -0
  128. package/dist/registry/store-adapters.d.ts.map +1 -0
  129. package/dist/registry/store-adapters.js +135 -0
  130. package/dist/registry/store-adapters.js.map +1 -0
  131. package/dist/registry/stores.d.ts +38 -2
  132. package/dist/registry/stores.d.ts.map +1 -1
  133. package/dist/registry/stores.js +109 -8
  134. package/dist/registry/stores.js.map +1 -1
  135. package/dist/reticle.d.ts +22 -3
  136. package/dist/reticle.d.ts.map +1 -1
  137. package/dist/reticle.js +79 -13
  138. package/dist/reticle.js.map +1 -1
  139. package/dist/review/annotator.js +1 -1
  140. package/dist/review/annotator.js.map +1 -1
  141. package/dist/review/mark-anchor.d.ts.map +1 -1
  142. package/dist/review/mark-anchor.js +1 -21
  143. package/dist/review/mark-anchor.js.map +1 -1
  144. package/dist/security/serialization.d.ts +29 -3
  145. package/dist/security/serialization.d.ts.map +1 -1
  146. package/dist/security/serialization.js +169 -28
  147. package/dist/security/serialization.js.map +1 -1
  148. package/dist/timers/clock.d.ts +13 -0
  149. package/dist/timers/clock.d.ts.map +1 -1
  150. package/dist/timers/clock.js +76 -6
  151. package/dist/timers/clock.js.map +1 -1
  152. package/dist/transport/transport.d.ts +1 -1
  153. package/dist/transport/transport.d.ts.map +1 -1
  154. package/dist/transport/transport.js +42 -4
  155. package/dist/transport/transport.js.map +1 -1
  156. package/package.json +7 -3
@@ -1,4 +1,5 @@
1
1
  import { EventType } from '@reticlehq/core';
2
+ import { captureMethod } from '../patching/capture-method.js';
2
3
  function snapshotLocation() {
3
4
  return {
4
5
  pathname: location.pathname,
@@ -11,13 +12,11 @@ function snapshotLocation() {
11
12
  export function installRoute(emit) {
12
13
  // Keep the true originals for teardown identity; bound copies are used for invocation
13
14
  // (History methods throw "Illegal invocation" if called with the wrong `this`).
14
- /* eslint-disable @typescript-eslint/unbound-method -- captured to restore exact identity on teardown */
15
- const origPush = history.pushState;
16
- const origReplace = history.replaceState;
17
- /* eslint-enable @typescript-eslint/unbound-method */
15
+ const origPush = captureMethod(history, 'pushState');
16
+ const origReplace = captureMethod(history, 'replaceState');
18
17
  const callPush = origPush.bind(history);
19
18
  const callReplace = origReplace.bind(history);
20
- // The `from` baseline for the NEXT popstate/hashchange. Updated in fire() on every emitted change
19
+ // The `from` baseline for the NEXT popstate/hashchange. Updated in fire on every emitted change
21
20
  // — including pushState/replaceState — so a later Back nav isn't compared against a stale href
22
21
  // (which made `to === from` trip and silently drop the back navigation).
23
22
  let lastHref = location.href;
@@ -34,24 +33,31 @@ export function installRoute(emit) {
34
33
  hash: to.hash,
35
34
  });
36
35
  };
37
- history.pushState = (data, unused, url) => {
36
+ const patchedPush = (data, unused, url) => {
38
37
  const from = location.href;
39
38
  callPush(data, unused, url ?? null);
40
39
  fire(from);
41
40
  };
42
- history.replaceState = (data, unused, url) => {
41
+ const patchedReplace = (data, unused, url) => {
43
42
  const from = location.href;
44
43
  callReplace(data, unused, url ?? null);
45
44
  fire(from);
46
45
  };
46
+ history.pushState = patchedPush;
47
+ history.replaceState = patchedReplace;
47
48
  const onNav = () => {
48
49
  fire(lastHref);
49
50
  };
50
51
  window.addEventListener('popstate', onNav);
51
52
  window.addEventListener('hashchange', onNav);
52
53
  return () => {
53
- history.pushState = origPush;
54
- history.replaceState = origReplace;
54
+ // Restore ONLY if the slot still holds our wrapper. If a router/analytics SDK wrapped
55
+ // history.pushState AFTER connect(), unconditionally writing origPush back would silently
56
+ // uninstall THEIR instrumentation too — the SDK harming the app it only meant to observe.
57
+ if (history.pushState === patchedPush)
58
+ history.pushState = origPush;
59
+ if (history.replaceState === patchedReplace)
60
+ history.replaceState = origReplace;
55
61
  window.removeEventListener('popstate', onNav);
56
62
  window.removeEventListener('hashchange', onNav);
57
63
  };
@@ -1 +1 @@
1
- {"version":3,"file":"route.js","sourceRoot":"","sources":["../../src/observers/route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,SAAS,gBAAgB;IACvB,OAAO;QACL,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,IAAI,EAAE,QAAQ,CAAC,IAAI;KACpB,CAAC;AACJ,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,YAAY,CAAC,IAAU;IACrC,sFAAsF;IACtF,gFAAgF;IAChF,wGAAwG;IACxG,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IACnC,MAAM,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IACzC,qDAAqD;IACrD,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,WAAW,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAE9C,kGAAkG;IAClG,+FAA+F;IAC/F,yEAAyE;IACzE,IAAI,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC;IAE7B,MAAM,IAAI,GAAG,CAAC,IAAY,EAAQ,EAAE;QAClC,MAAM,EAAE,GAAG,gBAAgB,EAAE,CAAC;QAC9B,IAAI,EAAE,CAAC,IAAI,KAAK,IAAI;YAAE,OAAO;QAC7B,QAAQ,GAAG,EAAE,CAAC,IAAI,CAAC;QACnB,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE;YAC3B,IAAI;YACJ,EAAE,EAAE,EAAE,CAAC,IAAI;YACX,QAAQ,EAAE,EAAE,CAAC,QAAQ;YACrB,MAAM,EAAE,EAAE,CAAC,MAAM;YACjB,IAAI,EAAE,EAAE,CAAC,IAAI;SACd,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO,CAAC,SAAS,GAAG,CAAC,IAAa,EAAE,MAAc,EAAE,GAAyB,EAAQ,EAAE;QACrF,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;QAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,CAAC,CAAC;IACF,OAAO,CAAC,YAAY,GAAG,CAAC,IAAa,EAAE,MAAc,EAAE,GAAyB,EAAQ,EAAE;QACxF,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;QAC3B,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,GAAS,EAAE;QACvB,IAAI,CAAC,QAAQ,CAAC,CAAC;IACjB,CAAC,CAAC;IACF,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAC3C,MAAM,CAAC,gBAAgB,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IAE7C,OAAO,GAAG,EAAE;QACV,OAAO,CAAC,SAAS,GAAG,QAAQ,CAAC;QAC7B,OAAO,CAAC,YAAY,GAAG,WAAW,CAAC;QACnC,MAAM,CAAC,mBAAmB,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAC9C,MAAM,CAAC,mBAAmB,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IAClD,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../../src/observers/route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAE9D,SAAS,gBAAgB;IACvB,OAAO;QACL,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,IAAI,EAAE,QAAQ,CAAC,IAAI;KACpB,CAAC;AACJ,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,YAAY,CAAC,IAAU;IACrC,sFAAsF;IACtF,gFAAgF;IAChF,MAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IACrD,MAAM,WAAW,GAAG,aAAa,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;IAC3D,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,WAAW,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAE9C,gGAAgG;IAChG,+FAA+F;IAC/F,yEAAyE;IACzE,IAAI,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC;IAE7B,MAAM,IAAI,GAAG,CAAC,IAAY,EAAQ,EAAE;QAClC,MAAM,EAAE,GAAG,gBAAgB,EAAE,CAAC;QAC9B,IAAI,EAAE,CAAC,IAAI,KAAK,IAAI;YAAE,OAAO;QAC7B,QAAQ,GAAG,EAAE,CAAC,IAAI,CAAC;QACnB,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE;YAC3B,IAAI;YACJ,EAAE,EAAE,EAAE,CAAC,IAAI;YACX,QAAQ,EAAE,EAAE,CAAC,QAAQ;YACrB,MAAM,EAAE,EAAE,CAAC,MAAM;YACjB,IAAI,EAAE,EAAE,CAAC,IAAI;SACd,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG,CAAC,IAAa,EAAE,MAAc,EAAE,GAAyB,EAAQ,EAAE;QACrF,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;QAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,CAAC,CAAC;IACF,MAAM,cAAc,GAAG,CAAC,IAAa,EAAE,MAAc,EAAE,GAAyB,EAAQ,EAAE;QACxF,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;QAC3B,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,CAAC,CAAC;IACF,OAAO,CAAC,SAAS,GAAG,WAAW,CAAC;IAChC,OAAO,CAAC,YAAY,GAAG,cAAc,CAAC;IAEtC,MAAM,KAAK,GAAG,GAAS,EAAE;QACvB,IAAI,CAAC,QAAQ,CAAC,CAAC;IACjB,CAAC,CAAC;IACF,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAC3C,MAAM,CAAC,gBAAgB,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IAE7C,OAAO,GAAG,EAAE;QACV,sFAAsF;QACtF,0FAA0F;QAC1F,0FAA0F;QAC1F,IAAI,OAAO,CAAC,SAAS,KAAK,WAAW;YAAE,OAAO,CAAC,SAAS,GAAG,QAAQ,CAAC;QACpE,IAAI,OAAO,CAAC,YAAY,KAAK,cAAc;YAAE,OAAO,CAAC,YAAY,GAAG,WAAW,CAAC;QAChF,MAAM,CAAC,mBAAmB,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAC9C,MAAM,CAAC,mBAAmB,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IAClD,CAAC,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"scroll.d.ts","sourceRoot":"","sources":["../../src/observers/scroll.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAKjD,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ,CA2ClD"}
1
+ {"version":3,"file":"scroll.d.ts","sourceRoot":"","sources":["../../src/observers/scroll.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAKjD,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ,CAsElD"}
@@ -1,26 +1,44 @@
1
- import { EventType } from '@reticlehq/core';
1
+ import { EventType, ScrollDirection } from '@reticlehq/core';
2
2
  import { refs } from '../dom/refs.js';
3
+ import { nativeSetTimeout, nativeClearTimeout } from '../timers/native-timers.js';
3
4
  const THROTTLE_MS = 100;
4
5
  const REVEAL_SELECTOR = '[data-reticle-reveal], [data-reveal], section';
5
6
  /** Observe scroll position + reveal-on-scroll for modern scroll-reactive UIs. */
6
7
  export function installScroll(emit) {
7
8
  let lastEmit = 0;
8
9
  let lastY = 0;
9
- const onScroll = () => {
10
- const now = performance.now();
11
- if (now - lastEmit < THROTTLE_MS)
12
- return;
13
- lastEmit = now;
10
+ let trailingTimer;
11
+ const emitPosition = () => {
12
+ lastEmit = performance.now();
14
13
  const y = window.scrollY;
15
14
  const max = Math.max(1, document.documentElement.scrollHeight - window.innerHeight);
16
15
  emit(EventType.SCROLL_POSITION, {
17
16
  x: window.scrollX,
18
17
  y,
19
18
  percent: Math.round((y / max) * 100),
20
- direction: y >= lastY ? 'down' : 'up',
19
+ direction: y >= lastY ? ScrollDirection.DOWN : ScrollDirection.UP,
21
20
  });
22
21
  lastY = y;
23
22
  };
23
+ const onScroll = () => {
24
+ const elapsed = performance.now() - lastEmit;
25
+ if (elapsed >= THROTTLE_MS) {
26
+ if (trailingTimer !== undefined) {
27
+ nativeClearTimeout(trailingTimer);
28
+ trailingTimer = undefined;
29
+ }
30
+ emitPosition();
31
+ return;
32
+ }
33
+ // Leading-edge only dropped the FINAL, resting position — an agent asserting "scrolled to the
34
+ // footer" saw the last mid-scroll sample, not where the page settled. Schedule a trailing emit.
35
+ if (trailingTimer === undefined) {
36
+ trailingTimer = nativeSetTimeout(() => {
37
+ trailingTimer = undefined;
38
+ emitPosition();
39
+ }, THROTTLE_MS - elapsed);
40
+ }
41
+ };
24
42
  window.addEventListener('scroll', onScroll, { passive: true });
25
43
  let io;
26
44
  if (typeof IntersectionObserver === 'function') {
@@ -31,11 +49,20 @@ export function installScroll(emit) {
31
49
  }
32
50
  }
33
51
  }, { threshold: 0.25 });
52
+ const observer = io;
53
+ // Observe reveal targets present at install. Reveal-on-scroll sections are in the initial DOM by
54
+ // design (they exist hidden and animate in on scroll), so this covers the real case. Watching the
55
+ // whole body subtree for LATE-mounted reveal targets was tried and reverted: a second body-wide
56
+ // MutationObserver on EVERY app — running querySelectorAll(section,...) per mutation batch — is
57
+ // disproportionate overhead for a niche signal. Dynamically-mounted reveal targets are simply not
58
+ // tracked (an honest blind spot, consistent with the rest of the SDK).
34
59
  for (const el of document.querySelectorAll(REVEAL_SELECTOR))
35
- io.observe(el);
60
+ observer.observe(el);
36
61
  }
37
62
  return () => {
38
63
  window.removeEventListener('scroll', onScroll);
64
+ if (trailingTimer !== undefined)
65
+ nativeClearTimeout(trailingTimer);
39
66
  io?.disconnect();
40
67
  };
41
68
  }
@@ -1 +1 @@
1
- {"version":3,"file":"scroll.js","sourceRoot":"","sources":["../../src/observers/scroll.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAGtC,MAAM,WAAW,GAAG,GAAG,CAAC;AACxB,MAAM,eAAe,GAAG,+CAA+C,CAAC;AAExE,iFAAiF;AACjF,MAAM,UAAU,aAAa,CAAC,IAAU;IACtC,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,MAAM,QAAQ,GAAG,GAAS,EAAE;QAC1B,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAC9B,IAAI,GAAG,GAAG,QAAQ,GAAG,WAAW;YAAE,OAAO;QACzC,QAAQ,GAAG,GAAG,CAAC;QACf,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC;QACzB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,eAAe,CAAC,YAAY,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;QACpF,IAAI,CAAC,SAAS,CAAC,eAAe,EAAE;YAC9B,CAAC,EAAE,MAAM,CAAC,OAAO;YACjB,CAAC;YACD,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;YACpC,SAAS,EAAE,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI;SACtC,CAAC,CAAC;QACH,KAAK,GAAG,CAAC,CAAC;IACZ,CAAC,CAAC;IACF,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAE/D,IAAI,EAAoC,CAAC;IACzC,IAAI,OAAO,oBAAoB,KAAK,UAAU,EAAE,CAAC;QAC/C,EAAE,GAAG,IAAI,oBAAoB,CAC3B,CAAC,OAAO,EAAE,EAAE;YACV,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,IAAI,KAAK,CAAC,cAAc,EAAE,CAAC;oBACzB,IAAI,CACF,SAAS,CAAC,YAAY,EACtB,EAAE,KAAK,EAAE,KAAK,CAAC,iBAAiB,EAAE,EAClC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAC1B,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC,EACD,EAAE,SAAS,EAAE,IAAI,EAAE,CACpB,CAAC;QACF,KAAK,MAAM,EAAE,IAAI,QAAQ,CAAC,gBAAgB,CAAC,eAAe,CAAC;YAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED,OAAO,GAAG,EAAE;QACV,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC/C,EAAE,EAAE,UAAU,EAAE,CAAC;IACnB,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"scroll.js","sourceRoot":"","sources":["../../src/observers/scroll.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { Emit, Teardown } from './types.js';
2
+ interface StateChange {
3
+ path: string;
4
+ old: unknown;
5
+ new: unknown;
6
+ }
7
+ /**
8
+ * Shallow top-level diff of two store states — the keys whose value changed. Immutable updates
9
+ * (Zustand/Redux) replace the changed key's reference, so `Object.is` catches them. Pure; deep-nested
10
+ * diffing behind an unchanged top-level reference is a v2 concern.
11
+ */
12
+ export declare function diffState(prev: unknown, next: unknown): StateChange[];
13
+ /**
14
+ * Subscribe to every registered store that exposed a subscribe method, emitting a STATE_CHANGE per
15
+ * changed top-level path with {name, path, old, new} — automatic diffs, not pull-only readings. Stores
16
+ * without a subscribe fall back to the pull path (reticle_state). Fully reversible.
17
+ */
18
+ export declare function installStoreState(emit: Emit): Teardown;
19
+ export {};
20
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/observers/state.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEjD,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;CACd;AAMD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,WAAW,EAAE,CAUrE;AAeD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ,CA8CtD"}
@@ -0,0 +1,87 @@
1
+ import { EventType, REDACTED_VALUE } from '@reticlehq/core';
2
+ import { subscribableStores, onStoreRegistered, } from '../registry/stores.js';
3
+ import { isSensitiveKey, sanitizeForTransport } from '../security/serialization.js';
4
+ function isRecord(value) {
5
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
6
+ }
7
+ /**
8
+ * Shallow top-level diff of two store states — the keys whose value changed. Immutable updates
9
+ * (Zustand/Redux) replace the changed key's reference, so `Object.is` catches them. Pure; deep-nested
10
+ * diffing behind an unchanged top-level reference is a v2 concern.
11
+ */
12
+ export function diffState(prev, next) {
13
+ if (!isRecord(prev) || !isRecord(next)) {
14
+ return Object.is(prev, next) ? [] : [{ path: '', old: prev, new: next }];
15
+ }
16
+ const changes = [];
17
+ for (const key of new Set([...Object.keys(prev), ...Object.keys(next)])) {
18
+ if (!Object.is(prev[key], next[key]))
19
+ changes.push({ path: key, old: prev[key], new: next[key] });
20
+ }
21
+ return changes;
22
+ }
23
+ /** Redact a changed value when its path is credential-bearing, else cap it for transport. */
24
+ function project(path, value) {
25
+ return isSensitiveKey(path) ? REDACTED_VALUE : sanitizeForTransport(value);
26
+ }
27
+ function safeRead(getter) {
28
+ try {
29
+ return getter();
30
+ }
31
+ catch {
32
+ return undefined;
33
+ }
34
+ }
35
+ /**
36
+ * Subscribe to every registered store that exposed a subscribe method, emitting a STATE_CHANGE per
37
+ * changed top-level path with {name, path, old, new} — automatic diffs, not pull-only readings. Stores
38
+ * without a subscribe fall back to the pull path (reticle_state). Fully reversible.
39
+ */
40
+ export function installStoreState(emit) {
41
+ const active = new Map();
42
+ const watch = ([name, getter, subscribe]) => {
43
+ // A re-registration under the same name is the HMR cycle: the store INSTANCE is replaced (new
44
+ // getter + new subscribe). Skipping it (the old `seen` guard) left the subscription bound to the
45
+ // DEAD getter forever and the live store invisible until a full reload. Rebind: drop the previous
46
+ // subscription for this name, then subscribe the new tuple. Guard the old unsubscribe — if it throws
47
+ // (a misbehaving store), the rebind must still proceed, or the live store stays invisible.
48
+ try {
49
+ active.get(name)?.();
50
+ }
51
+ catch {
52
+ /* a faulty unsubscribe must not block re-binding to the new instance */
53
+ }
54
+ let last = safeRead(getter);
55
+ active.set(name, subscribe(() => {
56
+ const next = safeRead(getter);
57
+ for (const change of diffState(last, next)) {
58
+ emit(EventType.STATE_CHANGE, {
59
+ name,
60
+ path: change.path,
61
+ value: project(change.path, change.new),
62
+ old: project(change.path, change.old),
63
+ });
64
+ }
65
+ last = next;
66
+ }));
67
+ };
68
+ // Stores already registered...
69
+ for (const entry of subscribableStores())
70
+ watch(entry);
71
+ //...and any registered LATER. The SDK installs observers during connect, but apps call
72
+ // registerStore after that, so without this the common case subscribes to nothing.
73
+ const offRegistered = onStoreRegistered(watch);
74
+ return () => {
75
+ offRegistered();
76
+ for (const unsubscribe of active.values()) {
77
+ try {
78
+ unsubscribe();
79
+ }
80
+ catch {
81
+ /* one faulty store unsubscribe must not abort tearing the rest down */
82
+ }
83
+ }
84
+ active.clear();
85
+ };
86
+ }
87
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/observers/state.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { type Emit, type Teardown } from './types.js';
1
2
  /** The three readable client-side storage areas. httpOnly cookies are invisible to JS by design. */
2
3
  export interface StorageSnapshot {
3
4
  local: Record<string, string>;
@@ -11,4 +12,12 @@ export interface StorageSnapshot {
11
12
  * for all three. Sensitive keys are redacted; httpOnly cookies are unreadable by design (documented).
12
13
  */
13
14
  export declare function readStorage(area?: string): StorageSnapshot | Record<string, string>;
15
+ /**
16
+ * Observe storage WRITES (not just reads): patch Storage.setItem/removeItem so a token persisted, a
17
+ * cart updated, or a session cleared emits a STORAGE_CHANGE {area, key, old?, new?} diff — the "before"
18
+ * the pull path can never see. localStorage and sessionStorage share one prototype, so `this` inside the
19
+ * patch identifies the area. The native originals are read off the prototype descriptor (not a bare
20
+ * method access) and re-dispatched via `.call(this)`. Fully reversible; values redacted like the reads.
21
+ */
22
+ export declare function installStorage(emit: Emit): Teardown;
14
23
  //# sourceMappingURL=storage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/observers/storage.ts"],"names":[],"mappings":"AAGA,oGAAoG;AACpG,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAmDD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CASnF"}
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/observers/storage.ts"],"names":[],"mappings":"AAEA,OAAO,EAA+B,KAAK,IAAI,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEnF,oGAAoG;AACpG,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAmDD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CASnF;AAYD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ,CAwFnD"}
@@ -1,5 +1,6 @@
1
- import { REDACTED_VALUE } from '@reticlehq/core';
1
+ import { EventType, REDACTED_VALUE, StorageArea } from '@reticlehq/core';
2
2
  import { isSensitiveKey } from '../security/serialization.js';
3
+ import { observeSafely, observeValue } from './types.js';
3
4
  /** Accessing localStorage/sessionStorage throws in a sandboxed iframe / disabled-storage context. */
4
5
  function safeArea(get) {
5
6
  try {
@@ -60,9 +61,9 @@ function readCookies() {
60
61
  * for all three. Sensitive keys are redacted; httpOnly cookies are unreadable by design (documented).
61
62
  */
62
63
  export function readStorage(area) {
63
- if (area === 'local')
64
+ if (area === StorageArea.LOCAL)
64
65
  return readArea(safeArea(() => window.localStorage));
65
- if (area === 'session')
66
+ if (area === StorageArea.SESSION)
66
67
  return readArea(safeArea(() => window.sessionStorage));
67
68
  if (area === 'cookies')
68
69
  return readCookies();
@@ -72,4 +73,105 @@ export function readStorage(area) {
72
73
  cookies: readCookies(),
73
74
  };
74
75
  }
76
+ /** Redact a value when its key is credential-bearing — the same rule the pull path applies. */
77
+ function redactFor(key, value) {
78
+ if (value === null)
79
+ return undefined;
80
+ return isSensitiveKey(key) ? REDACTED_VALUE : value;
81
+ }
82
+ /**
83
+ * Observe storage WRITES (not just reads): patch Storage.setItem/removeItem so a token persisted, a
84
+ * cart updated, or a session cleared emits a STORAGE_CHANGE {area, key, old?, new?} diff — the "before"
85
+ * the pull path can never see. localStorage and sessionStorage share one prototype, so `this` inside the
86
+ * patch identifies the area. The native originals are read off the prototype descriptor (not a bare
87
+ * method access) and re-dispatched via `.call(this)`. Fully reversible; values redacted like the reads.
88
+ */
89
+ export function installStorage(emit) {
90
+ if (typeof Storage === 'undefined')
91
+ return () => undefined;
92
+ const proto = Storage.prototype;
93
+ const origSet = Object.getOwnPropertyDescriptor(proto, 'setItem')?.value;
94
+ const origRemove = Object.getOwnPropertyDescriptor(proto, 'removeItem')?.value;
95
+ const origClear = Object.getOwnPropertyDescriptor(proto, 'clear')?.value;
96
+ if (origSet === undefined || origRemove === undefined || origClear === undefined) {
97
+ return () => undefined;
98
+ }
99
+ // Resolve sessionStorage at CALL time, not once at install. If the property threw during install
100
+ // (a transient sandbox state) the captured reference would be null and every session write would be
101
+ // misreported as `local` for the life of the page. Reads are cheap and safeArea swallows throws.
102
+ const areaOf = (storage) => storage === safeArea(() => window.sessionStorage) ? StorageArea.SESSION : StorageArea.LOCAL;
103
+ const readOld = (storage, key) => {
104
+ try {
105
+ return storage.getItem(key);
106
+ }
107
+ catch {
108
+ return null;
109
+ }
110
+ };
111
+ const patchedSetItem = function (key, value) {
112
+ // The app's write happens FIRST and outside the guard — it must succeed or fail on its own terms.
113
+ const old = observeValue(() => readOld(this, key)) ?? null;
114
+ origSet.call(this, key, value);
115
+ observeSafely(() => {
116
+ emit(EventType.STORAGE_CHANGE, {
117
+ area: areaOf(this),
118
+ key,
119
+ ...(redactFor(key, old) === undefined ? {} : { old: redactFor(key, old) }),
120
+ new: redactFor(key, value) ?? REDACTED_VALUE,
121
+ });
122
+ });
123
+ };
124
+ proto.setItem = patchedSetItem;
125
+ const patchedRemoveItem = function (key) {
126
+ const old = observeValue(() => readOld(this, key)) ?? null;
127
+ origRemove.call(this, key);
128
+ observeSafely(() => {
129
+ // No `new` field ⇒ the key was removed.
130
+ emit(EventType.STORAGE_CHANGE, {
131
+ area: areaOf(this),
132
+ key,
133
+ ...(redactFor(key, old) === undefined ? {} : { old: redactFor(key, old) }),
134
+ });
135
+ });
136
+ };
137
+ proto.removeItem = patchedRemoveItem;
138
+ const patchedClear = function () {
139
+ // localStorage.clear() is the most common logout — but it emitted NOTHING, so "logout cleared the
140
+ // session" was unverifiable from the write path. Snapshot the keys BEFORE clearing (the values are
141
+ // gone after), then report each as a removal, reusing the {area, key, old} shape removeItem emits.
142
+ const area = areaOf(this);
143
+ const removed = [];
144
+ try {
145
+ for (let i = 0; i < this.length; i += 1) {
146
+ const key = this.key(i);
147
+ if (key !== null)
148
+ removed.push({ key, old: readOld(this, key) });
149
+ }
150
+ }
151
+ catch {
152
+ // Enumerating keys can throw in a locked-down context; clear anyway, just without the diff.
153
+ }
154
+ origClear.call(this);
155
+ for (const { key, old } of removed) {
156
+ observeSafely(() => {
157
+ emit(EventType.STORAGE_CHANGE, {
158
+ area,
159
+ key,
160
+ ...(redactFor(key, old) === undefined ? {} : { old: redactFor(key, old) }),
161
+ });
162
+ });
163
+ }
164
+ };
165
+ proto.clear = patchedClear;
166
+ return () => {
167
+ // Restore only if the slot still holds our wrapper — never uninstall a wrapper the app layered
168
+ // on top of ours after connect().
169
+ if (proto.setItem === patchedSetItem)
170
+ proto.setItem = origSet;
171
+ if (proto.removeItem === patchedRemoveItem)
172
+ proto.removeItem = origRemove;
173
+ if (proto.clear === patchedClear)
174
+ proto.clear = origClear;
175
+ };
176
+ }
75
177
  //# sourceMappingURL=storage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/observers/storage.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/observers/storage.ts"],"names":[],"mappings":"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"}
@@ -3,4 +3,19 @@ import type { EventType } from '@reticlehq/core';
3
3
  export type Emit = (type: EventType, data: Record<string, unknown>, ref?: string) => void;
4
4
  /** Every observer returns a teardown that fully restores any patched globals. */
5
5
  export type Teardown = () => void;
6
+ /**
7
+ * Run an observation so it can never escape into the app's call stack.
8
+ *
9
+ * Every monkey-patch calls its observation INSIDE the function it replaced, so a throw does not surface
10
+ * as an SDK error — it propagates out of `localStorage.setItem` after the write already succeeded, out
11
+ * of `history.pushState` (crashing a router's navigate), or out of `console.log` before the message
12
+ * reaches the console. Guarding the transport hop alone is not enough: the payload is BUILT here too
13
+ * (reading the old value, resolving the area, redacting), and any of that can throw on a hostile or
14
+ * exotic object.
15
+ *
16
+ * A dev-only observability SDK must never be able to break the app it is observing.
17
+ */
18
+ export declare function observeSafely(observation: () => void): void;
19
+ /** Like observeSafely, but for reading a value the observation needs; undefined if the read throws. */
20
+ export declare function observeValue<T>(read: () => T): T | undefined;
6
21
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/observers/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,yFAAyF;AACzF,MAAM,MAAM,IAAI,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;AAE1F,iFAAiF;AACjF,MAAM,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/observers/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,yFAAyF;AACzF,MAAM,MAAM,IAAI,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;AAE1F,iFAAiF;AACjF,MAAM,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC;AAElC;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,IAAI,GAAG,IAAI,CAM3D;AAED,uGAAuG;AACvG,wBAAgB,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAM5D"}
@@ -1,2 +1,30 @@
1
- export {};
1
+ /**
2
+ * Run an observation so it can never escape into the app's call stack.
3
+ *
4
+ * Every monkey-patch calls its observation INSIDE the function it replaced, so a throw does not surface
5
+ * as an SDK error — it propagates out of `localStorage.setItem` after the write already succeeded, out
6
+ * of `history.pushState` (crashing a router's navigate), or out of `console.log` before the message
7
+ * reaches the console. Guarding the transport hop alone is not enough: the payload is BUILT here too
8
+ * (reading the old value, resolving the area, redacting), and any of that can throw on a hostile or
9
+ * exotic object.
10
+ *
11
+ * A dev-only observability SDK must never be able to break the app it is observing.
12
+ */
13
+ export function observeSafely(observation) {
14
+ try {
15
+ observation();
16
+ }
17
+ catch {
18
+ /* observation is best-effort; the host app's control flow is not */
19
+ }
20
+ }
21
+ /** Like observeSafely, but for reading a value the observation needs; undefined if the read throws. */
22
+ export function observeValue(read) {
23
+ try {
24
+ return read();
25
+ }
26
+ catch {
27
+ return undefined;
28
+ }
29
+ }
2
30
  //# sourceMappingURL=types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/observers/types.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/observers/types.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAAC,WAAuB;IACnD,IAAI,CAAC;QACH,WAAW,EAAE,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,oEAAoE;IACtE,CAAC;AACH,CAAC;AAED,uGAAuG;AACvG,MAAM,UAAU,YAAY,CAAI,IAAa;IAC3C,IAAI,CAAC;QACH,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Capture a method off an object so it can be restored — or re-invoked via `.call` — later.
3
+ *
4
+ * Monkeypatching is unavoidable in this SDK: observing history, console, fetch and XHR means
5
+ * replacing a method and putting the original back on teardown. Putting it back requires holding the
6
+ * UNBOUND original, because identity is what teardown compares — a bound copy is a different function
7
+ * and would leave the patch installed forever.
8
+ *
9
+ * `@typescript-eslint/unbound-method` exists to catch the common mistake this pattern deliberately
10
+ * makes, so every such site used to carry an eslint-disable. Disabling a rule seven times to express
11
+ * one intention is worse than naming the intention once: those disables also suppressed the rule for
12
+ * any OTHER unbound access on the same lines, which is exactly what the rule is for.
13
+ *
14
+ * The generic index access here is not something the rule flags, so callers stay clean and the
15
+ * "I meant to do this" is stated in one place instead of seven.
16
+ */
17
+ export declare function captureMethod<T, K extends keyof T>(target: T, key: K): T[K];
18
+ /**
19
+ * The `value` setter defined on a prototype, for inputs whose value must be set the way the browser
20
+ * does it — React installs its own descriptor, and assigning `el.value` directly skips the tracker it
21
+ * relies on to notice a change.
22
+ *
23
+ * Same reason as `captureMethod` for existing separately from its call site: a property descriptor's
24
+ * `set` is an unbound function, and naming that intention once beats disabling the rule at each use.
25
+ */
26
+ export declare function captureValueSetter(proto: object): ((this: HTMLElement, value: string) => void) | undefined;
27
+ //# sourceMappingURL=capture-method.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture-method.d.ts","sourceRoot":"","sources":["../../src/patching/capture-method.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAE3E;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,GACZ,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAO1D"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Capture a method off an object so it can be restored — or re-invoked via `.call` — later.
3
+ *
4
+ * Monkeypatching is unavoidable in this SDK: observing history, console, fetch and XHR means
5
+ * replacing a method and putting the original back on teardown. Putting it back requires holding the
6
+ * UNBOUND original, because identity is what teardown compares — a bound copy is a different function
7
+ * and would leave the patch installed forever.
8
+ *
9
+ * `@typescript-eslint/unbound-method` exists to catch the common mistake this pattern deliberately
10
+ * makes, so every such site used to carry an eslint-disable. Disabling a rule seven times to express
11
+ * one intention is worse than naming the intention once: those disables also suppressed the rule for
12
+ * any OTHER unbound access on the same lines, which is exactly what the rule is for.
13
+ *
14
+ * The generic index access here is not something the rule flags, so callers stay clean and the
15
+ * "I meant to do this" is stated in one place instead of seven.
16
+ */
17
+ export function captureMethod(target, key) {
18
+ return target[key];
19
+ }
20
+ /**
21
+ * The `value` setter defined on a prototype, for inputs whose value must be set the way the browser
22
+ * does it — React installs its own descriptor, and assigning `el.value` directly skips the tracker it
23
+ * relies on to notice a change.
24
+ *
25
+ * Same reason as `captureMethod` for existing separately from its call site: a property descriptor's
26
+ * `set` is an unbound function, and naming that intention once beats disabling the rule at each use.
27
+ */
28
+ export function captureValueSetter(proto) {
29
+ const descriptor = Object.getOwnPropertyDescriptor(proto, 'value');
30
+ if (descriptor === undefined)
31
+ return undefined;
32
+ // Read through the same generic index access `captureMethod` uses: reaching for `.set` directly is
33
+ // what the rule flags, and it is right to in general — this is the one place that means it.
34
+ const setter = captureMethod(descriptor, 'set');
35
+ return setter;
36
+ }
37
+ //# sourceMappingURL=capture-method.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture-method.js","sourceRoot":"","sources":["../../src/patching/capture-method.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,aAAa,CAAuB,MAAS,EAAE,GAAM;IACnE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;AACrB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAChC,KAAa;IAEb,MAAM,UAAU,GAAG,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACnE,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC/C,mGAAmG;IACnG,4FAA4F;IAC5F,MAAM,MAAM,GAAG,aAAa,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAChD,OAAO,MAAkE,CAAC;AAC5E,CAAC"}
@@ -50,7 +50,7 @@ export interface PresenterOptions {
50
50
  }
51
51
  export declare const DEFAULT_PACE = 450;
52
52
  /**
53
- * Glow state machine phases (exposed via glowPhase() for tests). A burst of activity flips the
53
+ * Glow state machine phases (exposed via glowPhase for tests). A burst of activity flips the
54
54
  * border IN once on the first activity, holds steady (the slow reticle-pulse breathing keeps running
55
55
  * uninterrupted — no per-action restart/strobe), then fades OUT once after a quiet window.
56
56
  */
@@ -71,11 +71,11 @@ export declare const HEARTBEAT_MS = 1000;
71
71
  */
72
72
  export declare const IDLE_NOTICE_MS = 4000;
73
73
  /** Default session-idle-end: after this much quiet the session auto-ends (glow off, panel persists
74
- * for analysis). Agent-tweakable via reticle_session { idleEndMs } for the app's needs. */
74
+ * for analysis). Agent-tweakable via reticle_session { idleEndMs } for the app's needs. */
75
75
  export declare const IDLE_END_MS = 300000;
76
76
  /** Floor for a tweaked idle-end so the agent can't set a uselessly tiny window. */
77
77
  export declare const IDLE_END_MIN_MS = 5000;
78
- /** Must match the glow CSS opacity transition (.25s) so phase reaches idle after the fade paints. */
78
+ /** Must match the glow CSS opacity transition.25s) so phase reaches idle after the fade paints. */
79
79
  export declare const GLOW_FADE_MS = 250;
80
80
  export declare const GLOW_ON = "1";
81
81
  export declare const GLOW_OFF = "0";
@@ -1 +1 @@
1
- {"version":3,"file":"presenter-config.d.ts","sourceRoot":"","sources":["../../src/presenter/presenter-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D;;;;GAIG;AAEH;;;;GAIG;AACH,eAAO,MAAM,UAAU;;;CAAgD,CAAC;AACxE,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,OAAO,UAAU,CAAC,CAAC;AACtE,eAAO,MAAM,mBAAmB,EAAE,UAA+B,CAAC;AAClE,eAAO,MAAM,SAAS,cAAc,CAAC;AACrC,eAAO,MAAM,OAAO,MAAM,CAAC;AAC3B,eAAO,MAAM,QAAQ,MAAM,CAAC;AAE5B,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+FAA+F;IAC/F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uGAAuG;IACvG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wGAAwG;IACxG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uFAAuF;IACvF,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,YAAY,MAAM,CAAC;AAEhC;;;;GAIG;AACH,eAAO,MAAM,SAAS;;;;CAIZ,CAAC;AACX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC;AAEnE,0CAA0C;AAC1C,eAAO,MAAM,aAAa,MAAM,CAAC;AACjC,2FAA2F;AAC3F,eAAO,MAAM,YAAY,OAAO,CAAC;AACjC;;;;GAIG;AACH,eAAO,MAAM,cAAc,OAAO,CAAC;AACnC;4FAC4F;AAC5F,eAAO,MAAM,WAAW,SAAU,CAAC;AACnC,mFAAmF;AACnF,eAAO,MAAM,eAAe,OAAQ,CAAC;AACrC,qGAAqG;AACrG,eAAO,MAAM,YAAY,MAAM,CAAC;AAChC,eAAO,MAAM,OAAO,MAAM,CAAC;AAC3B,eAAO,MAAM,QAAQ,MAAM,CAAC;AAC5B,eAAO,MAAM,OAAO,YAAY,CAAC;AACjC,2EAA2E;AAC3E,eAAO,MAAM,QAAQ,qBAAqB,CAAC;AAC3C,eAAO,MAAM,cAAc,2BAA2B,CAAC"}
1
+ {"version":3,"file":"presenter-config.d.ts","sourceRoot":"","sources":["../../src/presenter/presenter-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D;;;;GAIG;AAEH;;;;GAIG;AACH,eAAO,MAAM,UAAU;;;CAAgD,CAAC;AACxE,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,OAAO,UAAU,CAAC,CAAC;AACtE,eAAO,MAAM,mBAAmB,EAAE,UAA+B,CAAC;AAClE,eAAO,MAAM,SAAS,cAAc,CAAC;AACrC,eAAO,MAAM,OAAO,MAAM,CAAC;AAC3B,eAAO,MAAM,QAAQ,MAAM,CAAC;AAE5B,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+FAA+F;IAC/F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uGAAuG;IACvG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wGAAwG;IACxG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uFAAuF;IACvF,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,YAAY,MAAM,CAAC;AAEhC;;;;GAIG;AACH,eAAO,MAAM,SAAS;;;;CAIZ,CAAC;AACX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC;AAEnE,0CAA0C;AAC1C,eAAO,MAAM,aAAa,MAAM,CAAC;AACjC,2FAA2F;AAC3F,eAAO,MAAM,YAAY,OAAO,CAAC;AACjC;;;;GAIG;AACH,eAAO,MAAM,cAAc,OAAO,CAAC;AACnC;2FAC2F;AAC3F,eAAO,MAAM,WAAW,SAAU,CAAC;AACnC,mFAAmF;AACnF,eAAO,MAAM,eAAe,OAAQ,CAAC;AACrC,mGAAmG;AACnG,eAAO,MAAM,YAAY,MAAM,CAAC;AAChC,eAAO,MAAM,OAAO,MAAM,CAAC;AAC3B,eAAO,MAAM,QAAQ,MAAM,CAAC;AAC5B,eAAO,MAAM,OAAO,YAAY,CAAC;AACjC,2EAA2E;AAC3E,eAAO,MAAM,QAAQ,qBAAqB,CAAC;AAC3C,eAAO,MAAM,cAAc,2BAA2B,CAAC"}