@reticlehq/browser 2.0.1 → 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 (179) hide show
  1. package/NOTICE +5 -0
  2. package/dist/actions/actions-dom.d.ts.map +1 -1
  3. package/dist/actions/actions-dom.js +3 -20
  4. package/dist/actions/actions-dom.js.map +1 -1
  5. package/dist/actions/actions.d.ts +1 -1
  6. package/dist/actions/actions.d.ts.map +1 -1
  7. package/dist/actions/actions.js +37 -19
  8. package/dist/actions/actions.js.map +1 -1
  9. package/dist/commands/commands.d.ts +2 -0
  10. package/dist/commands/commands.d.ts.map +1 -1
  11. package/dist/commands/commands.js +30 -31
  12. package/dist/commands/commands.js.map +1 -1
  13. package/dist/dom/a11y.d.ts +17 -6
  14. package/dist/dom/a11y.d.ts.map +1 -1
  15. package/dist/dom/a11y.js +68 -32
  16. package/dist/dom/a11y.js.map +1 -1
  17. package/dist/dom/auto-anchor.d.ts +10 -10
  18. package/dist/dom/auto-anchor.js +10 -10
  19. package/dist/dom/blind-spots.d.ts +14 -0
  20. package/dist/dom/blind-spots.d.ts.map +1 -0
  21. package/dist/dom/blind-spots.js +30 -0
  22. package/dist/dom/blind-spots.js.map +1 -0
  23. package/dist/dom/chart.d.ts +93 -0
  24. package/dist/dom/chart.d.ts.map +1 -0
  25. package/dist/dom/chart.js +119 -0
  26. package/dist/dom/chart.js.map +1 -0
  27. package/dist/dom/dom-ignore.d.ts +2 -0
  28. package/dist/dom/dom-ignore.d.ts.map +1 -1
  29. package/dist/dom/dom-ignore.js +17 -2
  30. package/dist/dom/dom-ignore.js.map +1 -1
  31. package/dist/dom/occlusion.d.ts +9 -0
  32. package/dist/dom/occlusion.d.ts.map +1 -0
  33. package/dist/dom/occlusion.js +21 -0
  34. package/dist/dom/occlusion.js.map +1 -0
  35. package/dist/dom/query.d.ts +17 -4
  36. package/dist/dom/query.d.ts.map +1 -1
  37. package/dist/dom/query.js +185 -30
  38. package/dist/dom/query.js.map +1 -1
  39. package/dist/dom/refs.d.ts +26 -3
  40. package/dist/dom/refs.d.ts.map +1 -1
  41. package/dist/dom/refs.js +79 -4
  42. package/dist/dom/refs.js.map +1 -1
  43. package/dist/dom/snapshot.d.ts +7 -1
  44. package/dist/dom/snapshot.d.ts.map +1 -1
  45. package/dist/dom/snapshot.js +43 -16
  46. package/dist/dom/snapshot.js.map +1 -1
  47. package/dist/dom/source.d.ts +40 -0
  48. package/dist/dom/source.d.ts.map +1 -0
  49. package/dist/dom/source.js +59 -0
  50. package/dist/dom/source.js.map +1 -0
  51. package/dist/dom/theme.js +2 -2
  52. package/dist/dom/theme.js.map +1 -1
  53. package/dist/index.d.ts +6 -2
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +9 -1
  56. package/dist/index.js.map +1 -1
  57. package/dist/observers/animation.d.ts +1 -1
  58. package/dist/observers/animation.d.ts.map +1 -1
  59. package/dist/observers/animation.js +1 -1
  60. package/dist/observers/animation.js.map +1 -1
  61. package/dist/observers/blind-spots.d.ts +19 -0
  62. package/dist/observers/blind-spots.d.ts.map +1 -0
  63. package/dist/observers/blind-spots.js +89 -0
  64. package/dist/observers/blind-spots.js.map +1 -0
  65. package/dist/observers/console.d.ts.map +1 -1
  66. package/dist/observers/console.js +39 -6
  67. package/dist/observers/console.js.map +1 -1
  68. package/dist/observers/dom.d.ts +1 -1
  69. package/dist/observers/dom.d.ts.map +1 -1
  70. package/dist/observers/dom.js +69 -10
  71. package/dist/observers/dom.js.map +1 -1
  72. package/dist/observers/focus.d.ts +10 -0
  73. package/dist/observers/focus.d.ts.map +1 -0
  74. package/dist/observers/focus.js +49 -0
  75. package/dist/observers/focus.js.map +1 -0
  76. package/dist/observers/network-body.d.ts +19 -0
  77. package/dist/observers/network-body.d.ts.map +1 -0
  78. package/dist/observers/network-body.js +143 -0
  79. package/dist/observers/network-body.js.map +1 -0
  80. package/dist/observers/network-redact.d.ts +8 -0
  81. package/dist/observers/network-redact.d.ts.map +1 -0
  82. package/dist/observers/network-redact.js +66 -0
  83. package/dist/observers/network-redact.js.map +1 -0
  84. package/dist/observers/network.d.ts +19 -7
  85. package/dist/observers/network.d.ts.map +1 -1
  86. package/dist/observers/network.js +439 -53
  87. package/dist/observers/network.js.map +1 -1
  88. package/dist/observers/perf.d.ts +13 -0
  89. package/dist/observers/perf.d.ts.map +1 -0
  90. package/dist/observers/perf.js +62 -0
  91. package/dist/observers/perf.js.map +1 -0
  92. package/dist/observers/route.d.ts.map +1 -1
  93. package/dist/observers/route.js +19 -10
  94. package/dist/observers/route.js.map +1 -1
  95. package/dist/observers/scroll.d.ts +1 -1
  96. package/dist/observers/scroll.d.ts.map +1 -1
  97. package/dist/observers/scroll.js +36 -9
  98. package/dist/observers/scroll.js.map +1 -1
  99. package/dist/observers/state.d.ts +20 -0
  100. package/dist/observers/state.d.ts.map +1 -0
  101. package/dist/observers/state.js +87 -0
  102. package/dist/observers/state.js.map +1 -0
  103. package/dist/observers/storage.d.ts +23 -0
  104. package/dist/observers/storage.d.ts.map +1 -0
  105. package/dist/observers/storage.js +177 -0
  106. package/dist/observers/storage.js.map +1 -0
  107. package/dist/observers/types.d.ts +15 -0
  108. package/dist/observers/types.d.ts.map +1 -1
  109. package/dist/observers/types.js +29 -1
  110. package/dist/observers/types.js.map +1 -1
  111. package/dist/patching/capture-method.d.ts +27 -0
  112. package/dist/patching/capture-method.d.ts.map +1 -0
  113. package/dist/patching/capture-method.js +37 -0
  114. package/dist/patching/capture-method.js.map +1 -0
  115. package/dist/presenter/presenter-config.d.ts +3 -3
  116. package/dist/presenter/presenter-config.d.ts.map +1 -1
  117. package/dist/presenter/presenter-config.js +3 -3
  118. package/dist/presenter/presenter-config.js.map +1 -1
  119. package/dist/presenter/presenter-controls.d.ts +1 -1
  120. package/dist/presenter/presenter-controls.d.ts.map +1 -1
  121. package/dist/presenter/presenter-controls.js +3 -3
  122. package/dist/presenter/presenter-controls.js.map +1 -1
  123. package/dist/presenter/presenter-glow.d.ts +1 -1
  124. package/dist/presenter/presenter-glow.js +1 -1
  125. package/dist/presenter/presenter-styles.d.ts +1 -1
  126. package/dist/presenter/presenter-styles.d.ts.map +1 -1
  127. package/dist/presenter/presenter-styles.js +12 -3
  128. package/dist/presenter/presenter-styles.js.map +1 -1
  129. package/dist/presenter/presenter-test-helpers.d.ts +16 -0
  130. package/dist/presenter/presenter-test-helpers.d.ts.map +1 -0
  131. package/dist/presenter/presenter-test-helpers.js +42 -0
  132. package/dist/presenter/presenter-test-helpers.js.map +1 -0
  133. package/dist/presenter/presenter.d.ts +4 -6
  134. package/dist/presenter/presenter.d.ts.map +1 -1
  135. package/dist/presenter/presenter.js +5 -9
  136. package/dist/presenter/presenter.js.map +1 -1
  137. package/dist/recorder/recorder.d.ts +8 -8
  138. package/dist/recorder/recorder.d.ts.map +1 -1
  139. package/dist/recorder/recorder.js +22 -9
  140. package/dist/recorder/recorder.js.map +1 -1
  141. package/dist/registry/capabilities.d.ts +2 -2
  142. package/dist/registry/capabilities.d.ts.map +1 -1
  143. package/dist/registry/capabilities.js +1 -1
  144. package/dist/registry/capabilities.js.map +1 -1
  145. package/dist/registry/domains.d.ts +1 -1
  146. package/dist/registry/domains.js +1 -1
  147. package/dist/registry/emitter.d.ts +2 -2
  148. package/dist/registry/emitter.js +2 -2
  149. package/dist/registry/store-adapters.d.ts +128 -0
  150. package/dist/registry/store-adapters.d.ts.map +1 -0
  151. package/dist/registry/store-adapters.js +135 -0
  152. package/dist/registry/store-adapters.js.map +1 -0
  153. package/dist/registry/stores.d.ts +45 -3
  154. package/dist/registry/stores.d.ts.map +1 -1
  155. package/dist/registry/stores.js +121 -8
  156. package/dist/registry/stores.js.map +1 -1
  157. package/dist/reticle.d.ts +28 -4
  158. package/dist/reticle.d.ts.map +1 -1
  159. package/dist/reticle.js +83 -15
  160. package/dist/reticle.js.map +1 -1
  161. package/dist/review/annotator.d.ts.map +1 -1
  162. package/dist/review/annotator.js +4 -2
  163. package/dist/review/annotator.js.map +1 -1
  164. package/dist/review/mark-anchor.d.ts.map +1 -1
  165. package/dist/review/mark-anchor.js +1 -21
  166. package/dist/review/mark-anchor.js.map +1 -1
  167. package/dist/security/serialization.d.ts +29 -1
  168. package/dist/security/serialization.d.ts.map +1 -1
  169. package/dist/security/serialization.js +172 -20
  170. package/dist/security/serialization.js.map +1 -1
  171. package/dist/timers/clock.d.ts +13 -0
  172. package/dist/timers/clock.d.ts.map +1 -1
  173. package/dist/timers/clock.js +76 -6
  174. package/dist/timers/clock.js.map +1 -1
  175. package/dist/transport/transport.d.ts +1 -1
  176. package/dist/transport/transport.d.ts.map +1 -1
  177. package/dist/transport/transport.js +49 -8
  178. package/dist/transport/transport.js.map +1 -1
  179. package/package.json +7 -3
@@ -1 +1 @@
1
- {"version":3,"file":"console.js","sourceRoot":"","sources":["../../src/observers/console.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAI7D,MAAM,YAAY,GAAqC;IACrD,GAAG,EAAE,SAAS,CAAC,WAAW;IAC1B,IAAI,EAAE,SAAS,CAAC,YAAY;IAC5B,KAAK,EAAE,SAAS,CAAC,aAAa;CAC/B,CAAC;AAEF,SAAS,aAAa,CAAC,IAAe;IACpC,OAAO,IAAI;SACR,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,IAAI,OAAO,CAAC,KAAK,QAAQ;YAAE,OAAO,CAAC,CAAC;QACpC,IAAI,CAAC,YAAY,KAAK;YAAE,OAAO,CAAC,CAAC,OAAO,CAAC;QACzC,OAAO,aAAa,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC,CAAC;SACD,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,cAAc,CAAC,IAAU;IACvC,MAAM,OAAO,GAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1D,MAAM,SAAS,GAAG,IAAI,GAAG,EAA+C,CAAC;IAEzE,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,4EAA4E;QAC5E,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAiC,CAAC;QACjE,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAChC,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5C,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAe,EAAQ,EAAE;YAC7C,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,EAAE,OAAO,EAAE,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC7D,YAAY,CAAC,GAAG,IAAI,CAAC,CAAC;QACxB,CAAC,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,CAAC,KAAiB,EAAQ,EAAE;QAC1C,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE;YAC7B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,MAAM,EAAE,KAAK,CAAC,QAAQ;YACtB,IAAI,EAAE,KAAK,CAAC,MAAM;SACnB,CAAC,CAAC;IACL,CAAC,CAAC;IACF,MAAM,WAAW,GAAG,CAAC,KAA4B,EAAQ,EAAE;QACzD,MAAM,MAAM,GAAY,KAAK,CAAC,MAAM,CAAC;QACrC,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE;YAC7B,OAAO,EAAE,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;YAClE,IAAI,EAAE,oBAAoB;SAC3B,CAAC,CAAC;IACL,CAAC,CAAC;IACF,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC1C,MAAM,CAAC,gBAAgB,CAAC,oBAAoB,EAAE,WAAW,CAAC,CAAC;IAE3D,OAAO,GAAG,EAAE;QACV,KAAK,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,SAAS,EAAE,CAAC;YAC3C,OAAO,CAAC,MAAM,CAAC,GAAG,QAA8B,CAAC;QACnD,CAAC;QACD,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC7C,MAAM,CAAC,mBAAmB,CAAC,oBAAoB,EAAE,WAAW,CAAC,CAAC;IAChE,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"console.js","sourceRoot":"","sources":["../../src/observers/console.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
1
  import type { Emit, Teardown } from './types.js';
2
- /** Observe DOM mutations and emit semantic (not raw) events. See plan/03 §1. */
2
+ /** Observe DOM mutations and emit semantic (not raw) events. */
3
3
  export declare function installDom(emit: Emit): Teardown;
4
4
  //# sourceMappingURL=dom.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/observers/dom.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAwBjD,gFAAgF;AAChF,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ,CA0E/C"}
1
+ {"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/observers/dom.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAqCjD,gEAAgE;AAChE,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ,CA+G/C"}
@@ -1,6 +1,19 @@
1
- import { EventType } from '@reticlehq/core';
1
+ import { EventType, TruncationChannel } from '@reticlehq/core';
2
2
  import { getAccessibleName, getRole, isVisible } from '../dom/a11y.js';
3
3
  import { refs } from '../dom/refs.js';
4
+ /**
5
+ * A STABLE identity for the region a mutation happened in. Ambient learning cannot key on the element
6
+ * ref for a churning list: every appended row is a NEW element (fresh ref) and a removed one has no ref
7
+ * at all, so per-ref counts never accumulate and the region is never recognized as ambient. The
8
+ * container persists across ticks, so its testid (or ref) is the identity that does accumulate.
9
+ */
10
+ function regionKeyOf(target) {
11
+ const el = target instanceof Element ? target : null;
12
+ if (el === null)
13
+ return undefined;
14
+ const labelled = el.closest('[data-testid]');
15
+ return labelled?.getAttribute('data-testid') ?? refs.refFor(el);
16
+ }
4
17
  import { isReticleOverlay } from '../dom/dom-ignore.js';
5
18
  const WATCHED_ATTRS = [
6
19
  'class',
@@ -12,7 +25,19 @@ const WATCHED_ATTRS = [
12
25
  'aria-selected',
13
26
  'aria-checked',
14
27
  'data-state',
28
+ // Widened: visual + resource + form-value attributes. Values are capped (they can be long).
29
+ 'style',
30
+ 'src',
31
+ 'href',
32
+ 'value',
15
33
  ];
34
+ /** Attribute/text values are capped per event — style/src can be huge and would bloat the ledger. */
35
+ const MAX_ATTR_VALUE_LEN = 120;
36
+ function capValue(value) {
37
+ if (value === null)
38
+ return undefined;
39
+ return value.length > MAX_ATTR_VALUE_LEN ? `${value.slice(0, MAX_ATTR_VALUE_LEN)}…` : value;
40
+ }
16
41
  const DIALOG_ROLES = new Set(['dialog', 'alertdialog']);
17
42
  const LIVE_ROLES = new Set(['alert', 'status']);
18
43
  /** Max meaningful added/removed nodes reported per mutation batch (backpressure). */
@@ -20,21 +45,35 @@ const MAX_PER_BATCH = 40;
20
45
  function isMeaningful(role, name) {
21
46
  return role !== 'generic' || name.length > 0;
22
47
  }
23
- /** Observe DOM mutations and emit semantic (not raw) events. See plan/03 §1. */
48
+ /** Observe DOM mutations and emit semantic (not raw) events. */
24
49
  export function installDom(emit) {
25
50
  const observer = new MutationObserver((records) => {
26
51
  let added = 0;
27
52
  let removed = 0;
28
53
  let changed = 0;
54
+ // Count element nodes dropped purely because a per-batch cap was already reached. The cap is on
55
+ // MEANINGFUL events, so it is only hit after a real flood — the count is a raw over-estimate
56
+ // (it can include not-yet-inspected noise) but `dropped > 0` honestly means "this batch was capped".
57
+ let dropped = 0;
29
58
  for (const record of records) {
30
59
  if (record.type === 'attributes') {
31
60
  const target = record.target;
32
61
  if (target instanceof Element &&
33
62
  record.attributeName !== null &&
34
- changed < MAX_PER_BATCH &&
35
63
  !isReticleOverlay(target)) {
64
+ if (changed >= MAX_PER_BATCH) {
65
+ dropped += 1;
66
+ continue;
67
+ }
36
68
  changed += 1;
37
- emit(EventType.DOM_ATTR, { attr: record.attributeName, value: target.getAttribute(record.attributeName) }, refs.refFor(target));
69
+ // Old value (attributeOldValue) + capped new value — a diff, not just a reading.
70
+ const value = capValue(target.getAttribute(record.attributeName));
71
+ const old = capValue(record.oldValue);
72
+ emit(EventType.DOM_ATTR, {
73
+ attr: record.attributeName,
74
+ ...(value === undefined ? {} : { value }),
75
+ ...(old === undefined ? {} : { old }),
76
+ }, refs.refFor(target));
38
77
  }
39
78
  continue;
40
79
  }
@@ -42,16 +81,25 @@ export function installDom(emit) {
42
81
  // In-place text change inside an existing subtree (wizard steps, inline edits) —
43
82
  // childList-only would miss this.
44
83
  const parent = record.target.parentElement;
45
- if (parent !== null && changed < MAX_PER_BATCH && !isReticleOverlay(parent)) {
84
+ if (parent !== null && !isReticleOverlay(parent)) {
85
+ if (changed >= MAX_PER_BATCH) {
86
+ dropped += 1;
87
+ continue;
88
+ }
46
89
  changed += 1;
47
90
  const text = (record.target.textContent ?? '').trim().slice(0, 80);
48
- emit(EventType.DOM_TEXT, { text }, refs.refFor(parent));
91
+ const old = capValue(record.oldValue?.trim() ?? null);
92
+ emit(EventType.DOM_TEXT, { text, ...(old === undefined ? {} : { old }) }, refs.refFor(parent));
49
93
  }
50
94
  continue;
51
95
  }
52
96
  for (const node of record.addedNodes) {
53
- if (!(node instanceof Element) || added >= MAX_PER_BATCH)
97
+ if (!(node instanceof Element))
98
+ continue;
99
+ if (added >= MAX_PER_BATCH) {
100
+ dropped += 1;
54
101
  continue;
102
+ }
55
103
  if (isReticleOverlay(node))
56
104
  continue;
57
105
  const role = getRole(node);
@@ -60,7 +108,7 @@ export function installDom(emit) {
60
108
  continue;
61
109
  added += 1;
62
110
  const ref = refs.refFor(node);
63
- emit(EventType.DOM_ADDED, { role, name }, ref);
111
+ emit(EventType.DOM_ADDED, { role, name, region: regionKeyOf(record.target) }, ref);
64
112
  if (DIALOG_ROLES.has(role) ||
65
113
  LIVE_ROLES.has(role) ||
66
114
  node.getAttribute('aria-modal') === 'true') {
@@ -69,8 +117,12 @@ export function installDom(emit) {
69
117
  }
70
118
  }
71
119
  for (const node of record.removedNodes) {
72
- if (!(node instanceof Element) || removed >= MAX_PER_BATCH)
120
+ if (!(node instanceof Element))
121
+ continue;
122
+ if (removed >= MAX_PER_BATCH) {
123
+ dropped += 1;
73
124
  continue;
125
+ }
74
126
  if (isReticleOverlay(node))
75
127
  continue;
76
128
  const role = getRole(node);
@@ -78,16 +130,23 @@ export function installDom(emit) {
78
130
  if (!isMeaningful(role, name))
79
131
  continue;
80
132
  removed += 1;
81
- emit(EventType.DOM_REMOVED, { role, name });
133
+ // A removed node has no ref (it is gone), so the CONTAINER is the only stable identity — and it
134
+ // is what ambient learning needs to recognize a churning region.
135
+ emit(EventType.DOM_REMOVED, { role, name, region: regionKeyOf(record.target) });
82
136
  }
83
137
  }
138
+ // Never silent: a capped batch tells the ledger its DOM counts understate reality.
139
+ if (dropped > 0)
140
+ emit(EventType.TRUNCATED, { channel: TruncationChannel.DOM, dropped });
84
141
  });
85
142
  observer.observe(document.documentElement, {
86
143
  subtree: true,
87
144
  childList: true,
88
145
  attributes: true,
89
146
  attributeFilter: WATCHED_ATTRS,
147
+ attributeOldValue: true,
90
148
  characterData: true,
149
+ characterDataOldValue: true,
91
150
  });
92
151
  return () => {
93
152
  observer.disconnect();
@@ -1 +1 @@
1
- {"version":3,"file":"dom.js","sourceRoot":"","sources":["../../src/observers/dom.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dom.js","sourceRoot":"","sources":["../../src/observers/dom.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { Emit, Teardown } from './types.js';
2
+ /** A human-legible label for a focused element, or undefined for body/none. Pure; exported for testing. */
3
+ export declare function focusLabel(el: EventTarget | null): string | undefined;
4
+ /**
5
+ * Track element focus movement. Focus dropping to <body> after an act — a modal that closed and stranded
6
+ * focus, a button that vanished — is a real accessibility/UX regression no screenshot names. Emits
7
+ * FOCUS_CHANGE {to, from, toBody}. Reversible.
8
+ */
9
+ export declare function installFocus(emit: Emit): Teardown;
10
+ //# sourceMappingURL=focus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"focus.d.ts","sourceRoot":"","sources":["../../src/observers/focus.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEjD,2GAA2G;AAC3G,wBAAgB,UAAU,CAAC,EAAE,EAAE,WAAW,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAKrE;AAMD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ,CA+BjD"}
@@ -0,0 +1,49 @@
1
+ import { EventType } from '@reticlehq/core';
2
+ import { getAccessibleName, getRole } from '../dom/a11y.js';
3
+ /** A human-legible label for a focused element, or undefined for body/none. Pure; exported for testing. */
4
+ export function focusLabel(el) {
5
+ if (!(el instanceof Element) || el === document.body)
6
+ return undefined;
7
+ const role = getRole(el);
8
+ const name = getAccessibleName(el);
9
+ return name.length > 0 ? `${role} "${name}"` : role;
10
+ }
11
+ function toBody(el) {
12
+ return el === null || el === document.body;
13
+ }
14
+ /**
15
+ * Track element focus movement. Focus dropping to <body> after an act — a modal that closed and stranded
16
+ * focus, a button that vanished — is a real accessibility/UX regression no screenshot names. Emits
17
+ * FOCUS_CHANGE {to, from, toBody}. Reversible.
18
+ */
19
+ export function installFocus(emit) {
20
+ let last = null;
21
+ const onFocusIn = (event) => {
22
+ const to = event.target;
23
+ const from = last;
24
+ last = to;
25
+ emit(EventType.FOCUS_CHANGE, {
26
+ ...(focusLabel(to) === undefined ? {} : { to: focusLabel(to) }),
27
+ ...(focusLabel(from) === undefined ? {} : { from: focusLabel(from) }),
28
+ toBody: toBody(to),
29
+ });
30
+ };
31
+ const onFocusOut = (event) => {
32
+ // Focus left with nothing gaining it → it dropped to the body (the regression case).
33
+ if (event.relatedTarget !== null)
34
+ return;
35
+ const from = last;
36
+ last = null;
37
+ emit(EventType.FOCUS_CHANGE, {
38
+ ...(focusLabel(from) === undefined ? {} : { from: focusLabel(from) }),
39
+ toBody: true,
40
+ });
41
+ };
42
+ document.addEventListener('focusin', onFocusIn);
43
+ document.addEventListener('focusout', onFocusOut);
44
+ return () => {
45
+ document.removeEventListener('focusin', onFocusIn);
46
+ document.removeEventListener('focusout', onFocusOut);
47
+ };
48
+ }
49
+ //# sourceMappingURL=focus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"focus.js","sourceRoot":"","sources":["../../src/observers/focus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAG5D,2GAA2G;AAC3G,MAAM,UAAU,UAAU,CAAC,EAAsB;IAC/C,IAAI,CAAC,CAAC,EAAE,YAAY,OAAO,CAAC,IAAI,EAAE,KAAK,QAAQ,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IACvE,MAAM,IAAI,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC;IACzB,MAAM,IAAI,GAAG,iBAAiB,CAAC,EAAE,CAAC,CAAC;IACnC,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AACtD,CAAC;AAED,SAAS,MAAM,CAAC,EAAsB;IACpC,OAAO,EAAE,KAAK,IAAI,IAAI,EAAE,KAAK,QAAQ,CAAC,IAAI,CAAC;AAC7C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,IAAU;IACrC,IAAI,IAAI,GAAuB,IAAI,CAAC;IAEpC,MAAM,SAAS,GAAG,CAAC,KAAiB,EAAQ,EAAE;QAC5C,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC;QACxB,MAAM,IAAI,GAAG,IAAI,CAAC;QAClB,IAAI,GAAG,EAAE,CAAC;QACV,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE;YAC3B,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,CAAC,EAAE,CAAC;YAC/D,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACrE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;SACnB,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,UAAU,GAAG,CAAC,KAAiB,EAAQ,EAAE;QAC7C,qFAAqF;QACrF,IAAI,KAAK,CAAC,aAAa,KAAK,IAAI;YAAE,OAAO;QACzC,MAAM,IAAI,GAAG,IAAI,CAAC;QAClB,IAAI,GAAG,IAAI,CAAC;QACZ,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE;YAC3B,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACrE,MAAM,EAAE,IAAI;SACb,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAChD,QAAQ,CAAC,gBAAgB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAClD,OAAO,GAAG,EAAE;QACV,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QACnD,QAAQ,CAAC,mBAAmB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IACvD,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Resolve with the body text, or `undefined` if it takes too long.
3
+ *
4
+ * The app is already awaiting our patched fetch, so any unbounded read here is a hang in the host app.
5
+ * Losing an observation is always preferable to freezing the page being observed.
6
+ */
7
+ export declare function withBodyDeadline(read: Promise<string>): Promise<string | undefined>;
8
+ export declare function isCapturableType(contentType: string | null): boolean;
9
+ /**
10
+ * Redact + cap a body for the agent transcript. JSON is parsed and run through the same
11
+ * sanitizeForTransport redaction used for state (sensitive keys -> [REDACTED]); every other text body
12
+ * (and JSON that didn't parse) goes through redactText so form/plain-text credentials can't leak.
13
+ * Returns the (possibly truncated) body plus whether it was cut.
14
+ */
15
+ export declare function projectBody(rawText: string, contentType: string | null): {
16
+ body: string;
17
+ truncated: boolean;
18
+ };
19
+ //# sourceMappingURL=network-body.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network-body.d.ts","sourceRoot":"","sources":["../../src/observers/network-body.ts"],"names":[],"mappings":"AAuCA;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAgBzF;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAIpE;AAkDD;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GAAG,IAAI,GACzB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CA8BtC"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Response/request BODY projection for the network observer.
3
+ *
4
+ * Split out of network.ts, which had grown to hold URL redaction, body projection, fetch, XHR, SSE,
5
+ * WebSocket and sendBeacon at once. This module owns exactly one question: given a body, what is safe
6
+ * and affordable to report?
7
+ */
8
+ import { REDACTED_VALUE } from '@reticlehq/core';
9
+ import { isSensitiveKey, safeStringify, scrubKnownSecrets } from '../security/serialization.js';
10
+ import { nativeClearTimeout, nativeSetTimeout } from '../timers/native-timers.js';
11
+ /** Only text-like bodies are worth capturing; binary (images/fonts/octet-stream) is skipped. */
12
+ const CAPTURABLE_CONTENT = /application\/json|text\/|application\/xml|x-www-form-urlencoded|graphql/i;
13
+ /**
14
+ * Content types that are STREAMS, never complete bodies.
15
+ *
16
+ * `text/event-stream` matches `text/` in CAPTURABLE_CONTENT, and body capture awaited
17
+ * `res.clone.text` BEFORE returning the response to the app. A clone of a stream only settles when
18
+ * the stream ends — and an SSE stream does not end — so the app's `await fetch...)` never resolved.
19
+ * Enabling body capture silently hung every streaming endpoint (SSE, and the chunked `application/json`
20
+ * every token-streaming model API uses), leaving a permanently loading UI with no reason to suspect the
21
+ * observability SDK. Never read these; the frame observer covers SSE properly.
22
+ */
23
+ const STREAMING_CONTENT = /event-stream|x-ndjson|application\/stream/i;
24
+ /**
25
+ * Longest the SDK will wait on a response-body clone before emitting without it.
26
+ *
27
+ * Two layers protect the app. `text/event-stream` and friends are skipped outright, which covers the
28
+ * common streaming case at zero cost. This deadline is the backstop for a body that streams WITHOUT
29
+ * announcing it in its content type — chunked `application/json` from a token-streaming API. Gating on
30
+ * `content-length` instead was tried and rejected: plenty of complete responses omit it (gzip, HTTP/2),
31
+ * so that would silently stop capturing bodies for ordinary apps. A bounded half-second on a rare path
32
+ * beats an unbounded hang, and beats losing capture everywhere.
33
+ */
34
+ const BODY_READ_TIMEOUT_MS = 500;
35
+ /**
36
+ * Resolve with the body text, or `undefined` if it takes too long.
37
+ *
38
+ * The app is already awaiting our patched fetch, so any unbounded read here is a hang in the host app.
39
+ * Losing an observation is always preferable to freezing the page being observed.
40
+ */
41
+ export async function withBodyDeadline(read) {
42
+ // The timer is CLEARED on the winning path. Left uncleared, every captured body leaves a live timer
43
+ // and a retained closure behind — harmless individually, but `boundedFrame` in this same package
44
+ // already races-with-cleanup correctly, so not reusing that shape was an oversight rather than a
45
+ // choice. It also keeps test workers from being held open by pending handles.
46
+ let timer;
47
+ try {
48
+ return await Promise.race([
49
+ read,
50
+ new Promise((resolve) => {
51
+ timer = nativeSetTimeout(() => resolve(undefined), BODY_READ_TIMEOUT_MS);
52
+ }),
53
+ ]);
54
+ }
55
+ finally {
56
+ if (timer !== undefined)
57
+ nativeClearTimeout(timer);
58
+ }
59
+ }
60
+ export function isCapturableType(contentType) {
61
+ if (contentType === null)
62
+ return false;
63
+ if (STREAMING_CONTENT.test(contentType))
64
+ return false;
65
+ return CAPTURABLE_CONTENT.test(contentType);
66
+ }
67
+ /**
68
+ * Per-body character cap. Bodies ride the same ring buffer as the DOM/route/console timeline, so an
69
+ * uncapped body could evict the whole behavioral history; the small cap keeps a few large responses
70
+ * from starving the timeline (the separate-budget concern in the scalability audit).
71
+ */
72
+ const MAX_BODY_CHARS = 8192;
73
+ /**
74
+ * Hard bound on how much of a body is ever PARSED or SCANNED, independent of how much is reported.
75
+ *
76
+ * Only twice the output cap, and that ceiling is measured rather than guessed. The redaction pass uses
77
+ * `[A-Za-z0-9_.-]+` followed by a delimiter, which backtracks quadratically on a long run without one.
78
+ * Timed on this machine: 8 KB → 36 ms, 16 KB → 136 ms, 64 KB → 2067 ms. A first attempt at 8× the
79
+ * output cap therefore still froze the main thread for seconds, so the bound is set where the cost
80
+ * stays bounded AND redaction keeps enough context to catch a secret straddling the reported edge.
81
+ * Scanning far past the output cap is wasted work in any case — only 8 KB is ever reported.
82
+ */
83
+ const MAX_BODY_SCAN_CHARS = MAX_BODY_CHARS * 2;
84
+ /**
85
+ * An `Authorization: Bearer …` / `Basic …` credential. The token side requires credential shape — 16+
86
+ * chars AND at least one digit or symbol — so ordinary prose ("Basic subscription includes…", "Bearer
87
+ * capacity exceeded") is NOT mangled, only actual opaque tokens are.
88
+ */
89
+ const AUTH_SCHEME_TOKEN = /\b(Bearer|Basic)\s+(?=[A-Za-z0-9._~+/=-]{16,})(?=[A-Za-z0-9._~+/=-]*[\d._~+/=-])[A-Za-z0-9._~+/=-]+/gi;
90
+ /**
91
+ * Redact credentials in ANY text body (form-urlencoded, text/plain, xml, or JSON that failed to parse).
92
+ * sanitizeForTransport only redacts KEYS inside a parsed object, so a `password=secret` form post or a
93
+ * `Bearer <token>` string would otherwise ship verbatim. Redacts the value of any `key=value` / `key: value`
94
+ * pair whose key is sensitive, plus credential-shaped auth-scheme tokens.
95
+ */
96
+ function redactText(text) {
97
+ return (text
98
+ // Auth-scheme tokens FIRST: `Authorization: Bearer <token>` — the key/value rule below would
99
+ // otherwise consume just "Bearer" as Authorization's value (it stops at whitespace) and leave the
100
+ // token behind, so the scheme rule must run before it.
101
+ .replace(AUTH_SCHEME_TOKEN, (_m, scheme) => `${scheme} ${REDACTED_VALUE}`)
102
+ .replace(/([A-Za-z0-9_.-]+)(\s*[=:]\s*"?)([^&\s,;"}]+)/g, (match, key, sep) => isSensitiveKey(key) ? `${key}${sep}${REDACTED_VALUE}` : match));
103
+ }
104
+ /**
105
+ * Redact + cap a body for the agent transcript. JSON is parsed and run through the same
106
+ * sanitizeForTransport redaction used for state (sensitive keys -> [REDACTED]); every other text body
107
+ * (and JSON that didn't parse) goes through redactText so form/plain-text credentials can't leak.
108
+ * Returns the (possibly truncated) body plus whether it was cut.
109
+ */
110
+ export function projectBody(rawText, contentType) {
111
+ // Bound the INPUT before any parsing or scanning. The cap used to be applied only at the end, so a
112
+ // large body was JSON.parsed in full, deep-walked, re-stringified, and swept by two global regexes
113
+ // before all but 8 KB of the result was thrown away — seconds of synchronous main-thread work for a
114
+ // fixed-size output. A 30 MB CSV export or a big HTML error page froze the app, which is the same
115
+ // class of host-app damage as the streaming hang, with the SDK still the cause.
116
+ //
117
+ // The slice is generous (a multiple of the output cap) so redaction still sees enough context to
118
+ // recognise a secret that straddles the boundary, while the work stays bounded regardless of body size.
119
+ const oversized = rawText.length > MAX_BODY_SCAN_CHARS;
120
+ const text = oversized ? rawText.slice(0, MAX_BODY_SCAN_CHARS) : rawText;
121
+ let out;
122
+ if (contentType !== null && /json|graphql/i.test(contentType)) {
123
+ try {
124
+ // safeStringify already sanitizes internally — the extra sanitizeForTransport here re-walked
125
+ // every captured JSON body a second time (~21% of the per-response cost) for an identical result.
126
+ out = safeStringify(JSON.parse(text));
127
+ }
128
+ catch {
129
+ out = redactText(text); // looked like JSON but wasn't — still redact key/value + auth tokens
130
+ }
131
+ }
132
+ else {
133
+ out = redactText(text);
134
+ }
135
+ // Key-based redaction can't see a secret sitting in a VALUE under a benign key — scan the projected
136
+ // text for high-confidence secret shapes (JWTs, provider keys) as a backstop, JSON or not.
137
+ out = scrubKnownSecrets(out);
138
+ // Truncated if the projection overflows the cap OR the input itself was clipped above — a body the
139
+ // SDK never fully read must not be reported as complete.
140
+ const truncated = oversized || out.length > MAX_BODY_CHARS;
141
+ return { body: out.length > MAX_BODY_CHARS ? out.slice(0, MAX_BODY_CHARS) : out, truncated };
142
+ }
143
+ //# sourceMappingURL=network-body.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network-body.js","sourceRoot":"","sources":["../../src/observers/network-body.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Redact credential-bearing values so they don't leak into the agent transcript / flow / run
3
+ * artifacts: query params (`?access_token=…`, signed-URL keys) via the shared `isSensitiveKey` regex,
4
+ * AND path-embedded tokens (`/reset/<token>`, `/invite/<token>`) that live in the path, not the query.
5
+ * The hash is preserved and the URL is returned byte-for-byte when nothing matched.
6
+ */
7
+ export declare function redactUrl(raw: string): string;
8
+ //# sourceMappingURL=network-redact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network-redact.d.ts","sourceRoot":"","sources":["../../src/observers/network-redact.ts"],"names":[],"mappings":"AASA;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA4D7C"}
@@ -0,0 +1,66 @@
1
+ import { REDACTED_VALUE } from '@reticlehq/core';
2
+ import { isSensitiveKey } from '../security/serialization.js';
3
+ /** A path segment name that is typically followed by a single-use secret token in the NEXT segment. */
4
+ const SENSITIVE_PATH_SEGMENT = /^(reset|verify|verification|confirm|activate|invite|magic|magiclink|token|key|oauth|unsubscribe|password)$/i;
5
+ /** Only mask a following segment that looks token-like — short ids/words (`reset/form`) are left alone. */
6
+ const PATH_TOKEN_MIN_LENGTH = 12;
7
+ /**
8
+ * Redact credential-bearing values so they don't leak into the agent transcript / flow / run
9
+ * artifacts: query params (`?access_token=…`, signed-URL keys) via the shared `isSensitiveKey` regex,
10
+ * AND path-embedded tokens (`/reset/<token>`, `/invite/<token>`) that live in the path, not the query.
11
+ * The hash is preserved and the URL is returned byte-for-byte when nothing matched.
12
+ */
13
+ export function redactUrl(raw) {
14
+ const hashStart = raw.indexOf('#');
15
+ const hash = hashStart === -1 ? '' : raw.slice(hashStart);
16
+ const beforeHash = hashStart === -1 ? raw : raw.slice(0, hashStart);
17
+ const queryStart = beforeHash.indexOf('?');
18
+ const pathPart = queryStart === -1 ? beforeHash : beforeHash.slice(0, queryStart);
19
+ const query = queryStart === -1 ? '' : beforeHash.slice(queryStart + 1);
20
+ let changed = false;
21
+ // Credentials in the authority (`scheme://user:pass@host`) never belong in a transcript.
22
+ // Match to the LAST `@` before the path (`[^/]*@`, greedy), not the first — a password containing
23
+ // `@` (`user:p@ss@host`) otherwise left its tail (`ss@host`) in the clear.
24
+ let authority = pathPart;
25
+ const userinfo = /^([a-z][a-z0-9+.-]*:\/\/)[^/]*@/i.exec(pathPart);
26
+ if (userinfo !== null) {
27
+ authority = `${userinfo[1] ?? ''}${REDACTED_VALUE}@${pathPart.slice(userinfo[0].length)}`;
28
+ changed = true;
29
+ }
30
+ let newQuery = query;
31
+ if (query !== '') {
32
+ const params = new URLSearchParams(query);
33
+ for (const key of [...params.keys()]) {
34
+ if (isSensitiveKey(key)) {
35
+ params.set(key, REDACTED_VALUE);
36
+ changed = true;
37
+ }
38
+ }
39
+ newQuery = params.toString();
40
+ }
41
+ const segments = authority.split('/');
42
+ for (let i = 0; i + 1 < segments.length; i++) {
43
+ const name = segments[i];
44
+ const next = segments[i + 1];
45
+ if (name !== undefined &&
46
+ next !== undefined &&
47
+ next.length >= PATH_TOKEN_MIN_LENGTH &&
48
+ SENSITIVE_PATH_SEGMENT.test(name)) {
49
+ segments[i + 1] = REDACTED_VALUE;
50
+ changed = true;
51
+ }
52
+ }
53
+ // OAuth implicit flow puts the access_token in the FRAGMENT (`#access_token=…`), and hash-routers carry
54
+ // `?token=…` in the hash — redact sensitive params there too, leaving plain anchors (`#section`) alone.
55
+ let newHash = hash;
56
+ if (hash.length > 1) {
57
+ newHash = hash.replace(/([A-Za-z0-9_.-]+)=([^&\s]+)/g, (m, key) => isSensitiveKey(key) ? `${key}=${REDACTED_VALUE}` : m);
58
+ if (newHash !== hash)
59
+ changed = true;
60
+ }
61
+ if (!changed)
62
+ return raw;
63
+ const queryOut = queryStart === -1 ? '' : `?${newQuery}`;
64
+ return `${segments.join('/')}${queryOut}${newHash}`;
65
+ }
66
+ //# sourceMappingURL=network-redact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network-redact.js","sourceRoot":"","sources":["../../src/observers/network-redact.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,23 @@
1
1
  import type { Emit, Teardown } from './types.js';
2
+ import { redactUrl } from './network-redact.js';
3
+ export { redactUrl };
4
+ /** Config for the network observer. Body capture is OFF by default and dev-only opt-in. */
5
+ export interface NetworkOptions {
6
+ /** Capture request/response bodies (text-like content only, redacted, per-body capped). */
7
+ captureBodies?: boolean;
8
+ }
9
+ /** Pure: the first real app-code frame in a stack string, capped. Exported for unit testing. */
10
+ export declare function firstAppFrame(stack: string | undefined): string | undefined;
11
+ /** The timing fields we lift from a resource entry — TTFB is the perf signal a duration alone hides. */
12
+ export interface NetTiming {
13
+ ttfbMs?: number;
14
+ transferSize?: number;
15
+ }
2
16
  /**
3
- * Redact credential-bearing query params (`?access_token=…`, signed-URL keys, magic-link tokens) so
4
- * they don't leak into the agent transcript / flow / run artifacts. Only the query string is rewritten
5
- * — the path (relative or absolute) and any hash are preserved — and only when something actually
6
- * matched, so non-sensitive URLs pass through byte-for-byte. Uses the shared `isSensitiveKey` regex.
17
+ * Pure: derive TTFB + transfer size from a PerformanceResourceTiming entry. TTFB = responseStart -
18
+ * requestStart; cross-origin responses zero those (Timing-Allow-Origin), so we omit rather than report a
19
+ * bogus 0. Exported for unit testing (jsdom doesn't post real resource entries).
7
20
  */
8
- export declare function redactUrl(raw: string): string;
9
- /** Patch fetch + XMLHttpRequest to emit net.request events. Fully reversible. */
10
- export declare function installNetwork(emit: Emit): Teardown;
21
+ export declare function extractTiming(entry: PerformanceResourceTiming | undefined): NetTiming;
22
+ export declare function installNetwork(emit: Emit, opts?: NetworkOptions): Teardown;
11
23
  //# sourceMappingURL=network.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../src/observers/network.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAiB7C;AAqBD,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ,CAiGnD"}
1
+ {"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../src/observers/network.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEjD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAIhD,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,2FAA2F;AAC3F,MAAM,WAAW,cAAc;IAC7B,2FAA2F;IAC3F,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AA2GD,gGAAgG;AAChG,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAS3E;AAMD,wGAAwG;AACxG,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,yBAAyB,GAAG,SAAS,GAAG,SAAS,CAQrF;AAwDD,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,GAAE,cAAmB,GAAG,QAAQ,CAgV9E"}