@cubenest/rrweb-core 0.1.0-alpha.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 (135) hide show
  1. package/NOTICE +15 -0
  2. package/README.md +26 -0
  3. package/dist/compat/index.d.ts +37 -0
  4. package/dist/compat/index.d.ts.map +1 -0
  5. package/dist/compat/index.js +97 -0
  6. package/dist/compat/index.js.map +1 -0
  7. package/dist/compression/index.d.ts +24 -0
  8. package/dist/compression/index.d.ts.map +1 -0
  9. package/dist/compression/index.js +61 -0
  10. package/dist/compression/index.js.map +1 -0
  11. package/dist/console/buffer.d.ts +99 -0
  12. package/dist/console/buffer.d.ts.map +1 -0
  13. package/dist/console/buffer.js +169 -0
  14. package/dist/console/buffer.js.map +1 -0
  15. package/dist/console/index.d.ts +3 -0
  16. package/dist/console/index.d.ts.map +1 -0
  17. package/dist/console/index.js +16 -0
  18. package/dist/console/index.js.map +1 -0
  19. package/dist/console/types.d.ts +61 -0
  20. package/dist/console/types.d.ts.map +1 -0
  21. package/dist/console/types.js +11 -0
  22. package/dist/console/types.js.map +1 -0
  23. package/dist/index.d.ts +17 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +18 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/masking/body.d.ts +30 -0
  28. package/dist/masking/body.d.ts.map +1 -0
  29. package/dist/masking/body.js +33 -0
  30. package/dist/masking/body.js.map +1 -0
  31. package/dist/masking/headers.d.ts +16 -0
  32. package/dist/masking/headers.d.ts.map +1 -0
  33. package/dist/masking/headers.js +46 -0
  34. package/dist/masking/headers.js.map +1 -0
  35. package/dist/masking/index.d.ts +8 -0
  36. package/dist/masking/index.d.ts.map +1 -0
  37. package/dist/masking/index.js +11 -0
  38. package/dist/masking/index.js.map +1 -0
  39. package/dist/masking/inputs.d.ts +15 -0
  40. package/dist/masking/inputs.d.ts.map +1 -0
  41. package/dist/masking/inputs.js +36 -0
  42. package/dist/masking/inputs.js.map +1 -0
  43. package/dist/masking/regex.d.ts +96 -0
  44. package/dist/masking/regex.d.ts.map +1 -0
  45. package/dist/masking/regex.js +182 -0
  46. package/dist/masking/regex.js.map +1 -0
  47. package/dist/masking/selectors.d.ts +67 -0
  48. package/dist/masking/selectors.d.ts.map +1 -0
  49. package/dist/masking/selectors.js +137 -0
  50. package/dist/masking/selectors.js.map +1 -0
  51. package/dist/masking/text.d.ts +9 -0
  52. package/dist/masking/text.d.ts.map +1 -0
  53. package/dist/masking/text.js +15 -0
  54. package/dist/masking/text.js.map +1 -0
  55. package/dist/network/cdp.d.ts +54 -0
  56. package/dist/network/cdp.d.ts.map +1 -0
  57. package/dist/network/cdp.js +282 -0
  58. package/dist/network/cdp.js.map +1 -0
  59. package/dist/network/index.d.ts +4 -0
  60. package/dist/network/index.d.ts.map +1 -0
  61. package/dist/network/index.js +14 -0
  62. package/dist/network/index.js.map +1 -0
  63. package/dist/network/types.d.ts +133 -0
  64. package/dist/network/types.d.ts.map +1 -0
  65. package/dist/network/types.js +35 -0
  66. package/dist/network/types.js.map +1 -0
  67. package/dist/network/web-request.d.ts +76 -0
  68. package/dist/network/web-request.d.ts.map +1 -0
  69. package/dist/network/web-request.js +294 -0
  70. package/dist/network/web-request.js.map +1 -0
  71. package/dist/persistence/index.d.ts +3 -0
  72. package/dist/persistence/index.d.ts.map +1 -0
  73. package/dist/persistence/index.js +11 -0
  74. package/dist/persistence/index.js.map +1 -0
  75. package/dist/persistence/store.d.ts +18 -0
  76. package/dist/persistence/store.d.ts.map +1 -0
  77. package/dist/persistence/store.js +327 -0
  78. package/dist/persistence/store.js.map +1 -0
  79. package/dist/persistence/types.d.ts +76 -0
  80. package/dist/persistence/types.d.ts.map +1 -0
  81. package/dist/persistence/types.js +21 -0
  82. package/dist/persistence/types.js.map +1 -0
  83. package/dist/rrweb.d.ts +5 -0
  84. package/dist/rrweb.d.ts.map +1 -0
  85. package/dist/rrweb.js +13 -0
  86. package/dist/rrweb.js.map +1 -0
  87. package/dist/screenshot/base64.d.ts +8 -0
  88. package/dist/screenshot/base64.d.ts.map +1 -0
  89. package/dist/screenshot/base64.js +21 -0
  90. package/dist/screenshot/base64.js.map +1 -0
  91. package/dist/screenshot/cdp.d.ts +50 -0
  92. package/dist/screenshot/cdp.d.ts.map +1 -0
  93. package/dist/screenshot/cdp.js +65 -0
  94. package/dist/screenshot/cdp.js.map +1 -0
  95. package/dist/screenshot/index.d.ts +4 -0
  96. package/dist/screenshot/index.d.ts.map +1 -0
  97. package/dist/screenshot/index.js +10 -0
  98. package/dist/screenshot/index.js.map +1 -0
  99. package/dist/screenshot/tabs.d.ts +44 -0
  100. package/dist/screenshot/tabs.d.ts.map +1 -0
  101. package/dist/screenshot/tabs.js +63 -0
  102. package/dist/screenshot/tabs.js.map +1 -0
  103. package/dist/screenshot/types.d.ts +27 -0
  104. package/dist/screenshot/types.d.ts.map +1 -0
  105. package/dist/screenshot/types.js +18 -0
  106. package/dist/screenshot/types.js.map +1 -0
  107. package/dist/shadow-dom/index.d.ts +3 -0
  108. package/dist/shadow-dom/index.d.ts.map +1 -0
  109. package/dist/shadow-dom/index.js +8 -0
  110. package/dist/shadow-dom/index.js.map +1 -0
  111. package/dist/shadow-dom/traverse.d.ts +54 -0
  112. package/dist/shadow-dom/traverse.d.ts.map +1 -0
  113. package/dist/shadow-dom/traverse.js +209 -0
  114. package/dist/shadow-dom/traverse.js.map +1 -0
  115. package/dist/shadow-dom/types.d.ts +43 -0
  116. package/dist/shadow-dom/types.d.ts.map +1 -0
  117. package/dist/shadow-dom/types.js +25 -0
  118. package/dist/shadow-dom/types.js.map +1 -0
  119. package/dist/throttling/apply.d.ts +59 -0
  120. package/dist/throttling/apply.d.ts.map +1 -0
  121. package/dist/throttling/apply.js +101 -0
  122. package/dist/throttling/apply.js.map +1 -0
  123. package/dist/throttling/defaults.d.ts +60 -0
  124. package/dist/throttling/defaults.d.ts.map +1 -0
  125. package/dist/throttling/defaults.js +81 -0
  126. package/dist/throttling/defaults.js.map +1 -0
  127. package/dist/throttling/guards.d.ts +69 -0
  128. package/dist/throttling/guards.d.ts.map +1 -0
  129. package/dist/throttling/guards.js +212 -0
  130. package/dist/throttling/guards.js.map +1 -0
  131. package/dist/throttling/index.d.ts +5 -0
  132. package/dist/throttling/index.d.ts.map +1 -0
  133. package/dist/throttling/index.js +11 -0
  134. package/dist/throttling/index.js.map +1 -0
  135. package/package.json +35 -0
@@ -0,0 +1,4 @@
1
+ export type { ScreenshotAdapter } from './types';
2
+ export { createCDPScreenshotAdapter, type CDPTransport, type CDPScreenshotOptions, } from './cdp';
3
+ export { createTabsScreenshotAdapter, type CaptureVisibleTabFn, type TabsScreenshotOptions, } from './tabs';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/screenshot/index.ts"],"names":[],"mappings":"AAQA,YAAY,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EACL,0BAA0B,EAC1B,KAAK,YAAY,EACjB,KAAK,oBAAoB,GAC1B,MAAM,OAAO,CAAC;AACf,OAAO,EACL,2BAA2B,EAC3B,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,GAC3B,MAAM,QAAQ,CAAC"}
@@ -0,0 +1,10 @@
1
+ // Public barrel for the screenshot fallback module.
2
+ //
3
+ // Locked surface per IMPLEMENTATION_PLAN.md Public API contract (lines
4
+ // 721-723): one type (`ScreenshotAdapter`) and two factories
5
+ // (`createCDPScreenshotAdapter`, `createTabsScreenshotAdapter`). The
6
+ // transport-shape and option types are re-exported so consumers can
7
+ // declare-and-pass without importing internal paths.
8
+ export { createCDPScreenshotAdapter, } from './cdp';
9
+ export { createTabsScreenshotAdapter, } from './tabs';
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/screenshot/index.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,EAAE;AACF,uEAAuE;AACvE,6DAA6D;AAC7D,qEAAqE;AACrE,oEAAoE;AACpE,qDAAqD;AAGrD,OAAO,EACL,0BAA0B,GAG3B,MAAM,OAAO,CAAC;AACf,OAAO,EACL,2BAA2B,GAG5B,MAAM,QAAQ,CAAC"}
@@ -0,0 +1,44 @@
1
+ import type { ScreenshotAdapter } from './types';
2
+ /**
3
+ * Structural shape of `chrome.tabs.captureVisibleTab`. The Chrome API
4
+ * resolves to a `data:image/<fmt>;base64,...` URL (or an empty string on
5
+ * failure in older Chromium builds — handled below).
6
+ *
7
+ * The argument order matches the Chrome API: `(windowId?, options?)`.
8
+ * Passing `undefined` for `windowId` selects the current window.
9
+ */
10
+ export type CaptureVisibleTabFn = (windowId?: number, options?: {
11
+ format?: 'png' | 'jpeg';
12
+ quality?: number;
13
+ }) => Promise<string>;
14
+ /**
15
+ * Factory-time options for the tabs screenshot adapter. `windowId` is
16
+ * passed through verbatim; `format` and `quality` are forwarded to the
17
+ * Chrome API's `options` bag.
18
+ */
19
+ export interface TabsScreenshotOptions {
20
+ /**
21
+ * The browser window to capture from. Defaults to `undefined`, which the
22
+ * Chrome API interprets as "the currently focused window".
23
+ */
24
+ windowId?: number;
25
+ /** PNG (default) or JPEG. */
26
+ format?: 'png' | 'jpeg';
27
+ /** 0-100. JPEG only; Chrome ignores it for PNG. */
28
+ quality?: number;
29
+ }
30
+ /**
31
+ * Build a `ScreenshotAdapter` that captures via `chrome.tabs.captureVisibleTab`.
32
+ *
33
+ * The factory takes the `captureVisibleTab` reference as a parameter so the
34
+ * substrate is not coupled to the `chrome.*` namespace — pass
35
+ * `chrome.tabs.captureVisibleTab.bind(chrome.tabs)` from the extension
36
+ * service worker.
37
+ *
38
+ * The returned adapter is stateless; `dispose()` is a no-op.
39
+ *
40
+ * @param captureVisibleTab A function with the `chrome.tabs.captureVisibleTab` shape.
41
+ * @param options Factory-time defaults.
42
+ */
43
+ export declare function createTabsScreenshotAdapter(captureVisibleTab: CaptureVisibleTabFn, options?: TabsScreenshotOptions): ScreenshotAdapter;
44
+ //# sourceMappingURL=tabs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["../../src/screenshot/tabs.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD;;;;;;;GAOG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAChC,QAAQ,CAAC,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,KACpD,OAAO,CAAC,MAAM,CAAC,CAAC;AAErB;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6BAA6B;IAC7B,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,2BAA2B,CACzC,iBAAiB,EAAE,mBAAmB,EACtC,OAAO,GAAE,qBAA0B,GAClC,iBAAiB,CAqCnB"}
@@ -0,0 +1,63 @@
1
+ // chrome.tabs.captureVisibleTab adapter — Task 1.6.
2
+ //
3
+ // Used by P2/peek from the extension service worker. The MV3 service worker
4
+ // has access to `chrome.tabs.captureVisibleTab(windowId?, options?)`, which
5
+ // returns a `data:image/<fmt>;base64,...` URL. We accept the function as a
6
+ // parameter rather than reading `chrome.tabs` off a global so the substrate
7
+ // stays environment-agnostic and trivially testable.
8
+ //
9
+ // The function's argument ergonomics (windowId first, options second) match
10
+ // the Chrome API exactly — when no windowId is configured the factory
11
+ // passes `undefined`, mirroring `chrome.tabs.captureVisibleTab(undefined, opts)`,
12
+ // which the API treats as "the current window".
13
+ //
14
+ // Why not just `globalThis.chrome?.tabs?.captureVisibleTab`: (1) we want
15
+ // the substrate to typecheck and bundle outside an extension context;
16
+ // (2) callers often want to wrap the call with permission checks or
17
+ // telemetry; (3) tests stay synchronous-fixture-friendly.
18
+ import { decodeBase64 } from './base64';
19
+ /**
20
+ * Build a `ScreenshotAdapter` that captures via `chrome.tabs.captureVisibleTab`.
21
+ *
22
+ * The factory takes the `captureVisibleTab` reference as a parameter so the
23
+ * substrate is not coupled to the `chrome.*` namespace — pass
24
+ * `chrome.tabs.captureVisibleTab.bind(chrome.tabs)` from the extension
25
+ * service worker.
26
+ *
27
+ * The returned adapter is stateless; `dispose()` is a no-op.
28
+ *
29
+ * @param captureVisibleTab A function with the `chrome.tabs.captureVisibleTab` shape.
30
+ * @param options Factory-time defaults.
31
+ */
32
+ export function createTabsScreenshotAdapter(captureVisibleTab, options = {}) {
33
+ const windowId = options.windowId;
34
+ const format = options.format ?? 'png';
35
+ const quality = options.quality;
36
+ return {
37
+ async capture() {
38
+ const tabOptions = { format };
39
+ if (quality !== undefined) {
40
+ tabOptions.quality = quality;
41
+ }
42
+ const dataUrl = await captureVisibleTab(windowId, tabOptions);
43
+ if (typeof dataUrl !== 'string' || dataUrl.length === 0) {
44
+ // Chrome historically resolved with an empty string on permission
45
+ // failures rather than rejecting; surface that as a useful error.
46
+ throw new Error('chrome.tabs.captureVisibleTab returned an empty result');
47
+ }
48
+ // Expected shape: `data:image/png;base64,AAAA…` — but we accept any
49
+ // mime-type, only the `;base64,` segment matters for decoding.
50
+ const marker = ';base64,';
51
+ const idx = dataUrl.indexOf(marker);
52
+ if (idx === -1) {
53
+ throw new Error('captureVisibleTab result is not a base64-encoded data URL (missing `;base64,` segment)');
54
+ }
55
+ const payload = dataUrl.slice(idx + marker.length);
56
+ return decodeBase64(payload);
57
+ },
58
+ async dispose() {
59
+ // No listeners or session state to release.
60
+ },
61
+ };
62
+ }
63
+ //# sourceMappingURL=tabs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs.js","sourceRoot":"","sources":["../../src/screenshot/tabs.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,EAAE;AACF,4EAA4E;AAC5E,4EAA4E;AAC5E,2EAA2E;AAC3E,4EAA4E;AAC5E,qDAAqD;AACrD,EAAE;AACF,4EAA4E;AAC5E,sEAAsE;AACtE,kFAAkF;AAClF,gDAAgD;AAChD,EAAE;AACF,yEAAyE;AACzE,sEAAsE;AACtE,oEAAoE;AACpE,0DAA0D;AAE1D,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAiCxC;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,2BAA2B,CACzC,iBAAsC,EACtC,UAAiC,EAAE;IAEnC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC;IACvC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAEhC,OAAO;QACL,KAAK,CAAC,OAAO;YACX,MAAM,UAAU,GAAiD,EAAE,MAAM,EAAE,CAAC;YAC5E,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC1B,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;YAC/B,CAAC;YAED,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;YAE9D,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxD,kEAAkE;gBAClE,kEAAkE;gBAClE,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;YAC5E,CAAC;YAED,oEAAoE;YACpE,+DAA+D;YAC/D,MAAM,MAAM,GAAG,UAAU,CAAC;YAC1B,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACpC,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;gBACf,MAAM,IAAI,KAAK,CACb,wFAAwF,CACzF,CAAC;YACJ,CAAC;YAED,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;YACnD,OAAO,YAAY,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;QACD,KAAK,CAAC,OAAO;YACX,4CAA4C;QAC9C,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Capture the visible viewport as PNG (or JPEG, if the factory was
3
+ * configured with `format: 'jpeg'`) bytes.
4
+ *
5
+ * The bytes are the raw image payload — callers can persist directly, embed
6
+ * via `data:image/<fmt>;base64,...` URLs, or stream into a video assembler.
7
+ * No framing, no envelope.
8
+ */
9
+ export interface ScreenshotAdapter {
10
+ /**
11
+ * Capture the visible viewport. Resolves to a Uint8Array of image bytes
12
+ * in the format configured at factory time (PNG by default).
13
+ *
14
+ * Rejections propagate the transport error verbatim — the substrate does
15
+ * not retry, throttle, or wrap. The caller decides whether a missed
16
+ * frame is recoverable.
17
+ */
18
+ capture(): Promise<Uint8Array>;
19
+ /**
20
+ * Optional cleanup hook. The reference adapters resolve immediately — the
21
+ * field exists so product-specific adapters that *do* hold listeners
22
+ * (e.g. a long-lived CDP event subscription) can be torn down without a
23
+ * contract change.
24
+ */
25
+ dispose?(): Promise<void>;
26
+ }
27
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/screenshot/types.ts"],"names":[],"mappings":"AAiBA;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;;;;OAOG;IACH,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;IAE/B;;;;;OAKG;IACH,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B"}
@@ -0,0 +1,18 @@
1
+ // Screenshot fallback interface — Task 1.6.
2
+ //
3
+ // ADR-0002: when rrweb misbehaves on hard sites (canvas/webgl heavy, hostile
4
+ // CSS, very large DOMs throttled out of full-fidelity capture), a periodic
5
+ // screenshot keeps the recording useful for triage. Both products consume
6
+ // the same `ScreenshotAdapter` contract; the transport differs:
7
+ //
8
+ // - P1/tracelane → CDP `Page.captureScreenshot` via a WebDriver-supplied
9
+ // CDP session (see `createCDPScreenshotAdapter`).
10
+ // - P2/peek → `chrome.tabs.captureVisibleTab` from the extension
11
+ // service worker (see `createTabsScreenshotAdapter`).
12
+ //
13
+ // The substrate stays environment-agnostic by injecting the transport as a
14
+ // parameter rather than importing `chrome.*` or any CDP client library.
15
+ // Tests at this layer cover the contract; real-environment integration
16
+ // tests live in the product packages.
17
+ export {};
18
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/screenshot/types.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,0EAA0E;AAC1E,gEAAgE;AAChE,EAAE;AACF,2EAA2E;AAC3E,sDAAsD;AACtD,uEAAuE;AACvE,0DAA0D;AAC1D,EAAE;AACF,2EAA2E;AAC3E,wEAAwE;AACxE,uEAAuE;AACvE,sCAAsC"}
@@ -0,0 +1,3 @@
1
+ export { traverseShadowRoots } from './traverse';
2
+ export type { ShadowRootInfo } from './types';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shadow-dom/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC"}
@@ -0,0 +1,8 @@
1
+ // Public barrel for the shadow-DOM module.
2
+ //
3
+ // Locked surface per IMPLEMENTATION_PLAN.md Public API contract (lines
4
+ // 715-716): the `traverseShadowRoots` function plus the `ShadowRootInfo`
5
+ // type. The traversal options interface stays internal — callers pass an
6
+ // inline object literal and TS infers the shape.
7
+ export { traverseShadowRoots } from './traverse';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/shadow-dom/index.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,EAAE;AACF,uEAAuE;AACvE,yEAAyE;AACzE,yEAAyE;AACzE,iDAAiD;AAEjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,54 @@
1
+ import type { ShadowRootInfo } from './types';
2
+ export interface TraverseShadowRootsOptions {
3
+ /**
4
+ * Maximum shadow-root nesting depth to recurse into. Defaults to 16.
5
+ * Hosts at exactly `maxDepth - 1` are recorded; their shadow contents
6
+ * are not recursed into.
7
+ */
8
+ maxDepth?: number;
9
+ /**
10
+ * Called once for every host we detected but could not reach into. Fires
11
+ * for both the closed-shadow-root MAIN-world case and the custom-element
12
+ * heuristic. Throws are swallowed so a noisy consumer can't break the
13
+ * walk.
14
+ */
15
+ onUnreachable?: (host: Element) => void;
16
+ /**
17
+ * When `true`, the walker will call `openOrClosedShadowRoot` (if
18
+ * provided) to try to reach closed shadow roots. Defaults to `false` —
19
+ * MAIN-world callers should leave this off; ISOLATED-world content
20
+ * scripts that have wired `chrome.dom.openOrClosedShadowRoot` should
21
+ * pass `true`.
22
+ */
23
+ includeClosed?: boolean;
24
+ /**
25
+ * Caller-injected helper, intended to be
26
+ * `chrome.dom.openOrClosedShadowRoot` in an ISOLATED-world content
27
+ * script. Returning `null`/`undefined` is interpreted as "no shadow
28
+ * root on this element"; returning a `ShadowRoot` is interpreted as
29
+ * "this element has a (probably closed) shadow root, here it is."
30
+ *
31
+ * Only consulted when `includeClosed === true`.
32
+ */
33
+ openOrClosedShadowRoot?: (el: Element) => ShadowRoot | null | undefined;
34
+ }
35
+ /**
36
+ * Walk the DOM from `root` and return every shadow host found.
37
+ *
38
+ * Open shadow roots are resolved via `el.shadowRoot`. Closed shadow roots
39
+ * are only reachable if the caller injects `openOrClosedShadowRoot` AND
40
+ * passes `includeClosed: true` — typically a content script forwarding
41
+ * `chrome.dom.openOrClosedShadowRoot` from an ISOLATED world. In MAIN
42
+ * world, closed shadow hosts are recorded as `'unreachable'`; browser
43
+ * encapsulation is respected (we do NOT reflect-hack into closed shadows).
44
+ *
45
+ * Does not descend into iframes — `iframe.contentDocument` is usually
46
+ * cross-origin and rrweb has its own iframe recording pathway.
47
+ *
48
+ * @param root Document, fragment, or element to walk.
49
+ * @param options See {@link TraverseShadowRootsOptions}.
50
+ * @returns A flat array of `ShadowRootInfo`, ordered by traversal (DFS
51
+ * pre-order). Each host appears at most once.
52
+ */
53
+ export declare function traverseShadowRoots(root: Document | DocumentFragment | Element, options?: TraverseShadowRootsOptions): ShadowRootInfo[];
54
+ //# sourceMappingURL=traverse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traverse.d.ts","sourceRoot":"","sources":["../../src/shadow-dom/traverse.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAK9C,MAAM,WAAW,0BAA0B;IACzC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;;;;OAQG;IACH,sBAAsB,CAAC,EAAE,CAAC,EAAE,EAAE,OAAO,KAAK,UAAU,GAAG,IAAI,GAAG,SAAS,CAAC;CACzE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,QAAQ,GAAG,gBAAgB,GAAG,OAAO,EAC3C,OAAO,GAAE,0BAA+B,GACvC,cAAc,EAAE,CA+ClB"}
@@ -0,0 +1,209 @@
1
+ // Shadow DOM walker — Task 1.5.
2
+ //
3
+ // Walks the DOM from a given root and reports every shadow host it finds.
4
+ // Pure enumeration — does NOT serialize shadow contents, does NOT subscribe
5
+ // to mutations. rrweb does that elsewhere; we just answer "where are the
6
+ // hosts and what can we see through them?".
7
+ //
8
+ // Why this exists despite the PostHog fork already handling shadow hosts:
9
+ // ADR-0002 calls it "a thin wrapper around the PostHog fork's shadow-host
10
+ // handling that closes known gaps in custom-element traversal and documents
11
+ // the gaps that remain." The fork's logic is internal; we re-implement at
12
+ // this layer so consumers have a stable surface (`ShadowRootInfo[]`) and a
13
+ // single seam to inject the ISOLATED-world `chrome.dom.openOrClosedShadowRoot`
14
+ // helper from a content script.
15
+ //
16
+ // Closed shadow roots in MAIN world: the browser intentionally hides
17
+ // `el.shadowRoot` for `attachShadow({mode:'closed'})`. We respect that —
18
+ // no reflect-hacking, no Proxy tricks. Such hosts land on the
19
+ // `'unreachable'` path and (if the caller wired one) fire
20
+ // `options.onUnreachable(el)` so the consumer can log/breadcrumb.
21
+ //
22
+ // We do NOT traverse into `iframe.contentDocument`. In real usage iframes
23
+ // are very often cross-origin, where `contentDocument` access throws. Even
24
+ // when same-origin, iframes have independent recording semantics in rrweb
25
+ // and should be handled by the recorder's `recordCrossOriginIframes`
26
+ // pathway, not by us.
27
+ /** Default `maxDepth` — pathological apps have been observed nesting ~8 deep. */
28
+ const DEFAULT_MAX_DEPTH = 16;
29
+ /**
30
+ * Walk the DOM from `root` and return every shadow host found.
31
+ *
32
+ * Open shadow roots are resolved via `el.shadowRoot`. Closed shadow roots
33
+ * are only reachable if the caller injects `openOrClosedShadowRoot` AND
34
+ * passes `includeClosed: true` — typically a content script forwarding
35
+ * `chrome.dom.openOrClosedShadowRoot` from an ISOLATED world. In MAIN
36
+ * world, closed shadow hosts are recorded as `'unreachable'`; browser
37
+ * encapsulation is respected (we do NOT reflect-hack into closed shadows).
38
+ *
39
+ * Does not descend into iframes — `iframe.contentDocument` is usually
40
+ * cross-origin and rrweb has its own iframe recording pathway.
41
+ *
42
+ * @param root Document, fragment, or element to walk.
43
+ * @param options See {@link TraverseShadowRootsOptions}.
44
+ * @returns A flat array of `ShadowRootInfo`, ordered by traversal (DFS
45
+ * pre-order). Each host appears at most once.
46
+ */
47
+ export function traverseShadowRoots(root, options = {}) {
48
+ const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
49
+ const includeClosed = options.includeClosed ?? false;
50
+ const openOrClosed = options.openOrClosedShadowRoot;
51
+ const onUnreachable = options.onUnreachable;
52
+ const seen = new Set();
53
+ const results = [];
54
+ // Avoid recursion to keep stack depth bounded for pathological nesting.
55
+ // The work list carries (subtree-root, depth) — depth is the depth the
56
+ // hosts inside that subtree-root will land at.
57
+ const work = [
58
+ { subtree: root, depth: 0 },
59
+ ];
60
+ while (work.length > 0) {
61
+ const item = work.shift();
62
+ if (!item)
63
+ break;
64
+ const { subtree, depth } = item;
65
+ if (depth >= maxDepth)
66
+ continue;
67
+ for (const el of iterDescendantElements(subtree)) {
68
+ if (seen.has(el))
69
+ continue;
70
+ const info = resolveHost(el, depth, includeClosed, openOrClosed);
71
+ if (!info)
72
+ continue;
73
+ seen.add(el);
74
+ results.push(info);
75
+ if (info.source === 'unreachable') {
76
+ if (onUnreachable) {
77
+ try {
78
+ onUnreachable(el);
79
+ }
80
+ catch {
81
+ // Consumer callback errors must not abort the walk.
82
+ }
83
+ }
84
+ }
85
+ else if (info.root) {
86
+ // Queue the shadow root for traversal; nested hosts land at depth+1.
87
+ work.push({ subtree: info.root, depth: depth + 1 });
88
+ }
89
+ }
90
+ }
91
+ return results;
92
+ }
93
+ /**
94
+ * Resolve a single element into a `ShadowRootInfo`, or `null` if it isn't a
95
+ * shadow host. Implements the per-host resolution algorithm from
96
+ * IMPLEMENTATION_PLAN.md Task 1.5.
97
+ */
98
+ function resolveHost(el, depth, includeClosed, openOrClosed) {
99
+ // Step 1: open shadow root via the standard reflection.
100
+ const openRoot = el.shadowRoot;
101
+ if (openRoot) {
102
+ return {
103
+ host: el,
104
+ root: openRoot,
105
+ mode: 'open',
106
+ source: 'attachShadow',
107
+ depth,
108
+ };
109
+ }
110
+ // Step 2: caller-provided helper (chrome.dom.openOrClosedShadowRoot).
111
+ if (includeClosed && openOrClosed) {
112
+ let injectedRoot;
113
+ try {
114
+ injectedRoot = openOrClosed(el);
115
+ }
116
+ catch {
117
+ // The helper may throw on detached nodes etc; treat as "no root".
118
+ injectedRoot = null;
119
+ }
120
+ if (injectedRoot) {
121
+ return {
122
+ host: el,
123
+ root: injectedRoot,
124
+ mode: 'closed',
125
+ source: 'chrome.dom',
126
+ depth,
127
+ };
128
+ }
129
+ }
130
+ // Step 3: best-effort heuristic — a custom element (tag name contains a
131
+ // hyphen) with no light-DOM children is *probably* a closed shadow host.
132
+ // This is intentionally permissive; the consumer's `onUnreachable`
133
+ // callback exists precisely so callers can downgrade these reports if
134
+ // they have richer information. See ADR-0002 — "closes known gaps in
135
+ // custom-element traversal and documents the gaps that remain."
136
+ if (isLikelyClosedCustomElementHost(el)) {
137
+ return {
138
+ host: el,
139
+ root: null,
140
+ mode: 'unknown',
141
+ source: 'unreachable',
142
+ depth,
143
+ };
144
+ }
145
+ // Step 4: not a shadow host.
146
+ return null;
147
+ }
148
+ /**
149
+ * Cheap heuristic: an element is "probably a closed shadow host" if its
150
+ * tag name contains a hyphen (Custom Elements v1 requirement) and it has
151
+ * no child nodes in the light DOM. False positives are intentional — the
152
+ * consumer's `onUnreachable` is the escape hatch.
153
+ */
154
+ function isLikelyClosedCustomElementHost(el) {
155
+ const tag = el.tagName;
156
+ if (!tag)
157
+ return false;
158
+ // Built-ins like `<div>`/`<span>` have no hyphen. Custom elements must.
159
+ if (!tag.includes('-'))
160
+ return false;
161
+ // If we can see light DOM children, it's almost certainly not a closed
162
+ // shadow host (or, if it is, we'd still walk into the light tree below
163
+ // anyway — no information is lost by skipping it here).
164
+ if (el.childNodes.length > 0)
165
+ return false;
166
+ return true;
167
+ }
168
+ /**
169
+ * Iterate the element descendants of a subtree root (the root itself is
170
+ * NOT yielded — the caller has already considered it on the previous
171
+ * iteration if it was a host).
172
+ *
173
+ * Uses a manual stack rather than `TreeWalker` to keep semantics
174
+ * predictable across jsdom/Chromium/WebKit, all of which have had
175
+ * shadow-related TreeWalker quirks.
176
+ */
177
+ function* iterDescendantElements(subtree) {
178
+ // For a Document, start at documentElement (the <html>); for fragments
179
+ // and elements, start at the first child.
180
+ const starts = [];
181
+ if (isDocument(subtree)) {
182
+ if (subtree.documentElement)
183
+ starts.push(subtree.documentElement);
184
+ }
185
+ else {
186
+ for (const child of Array.from(subtree.children)) {
187
+ starts.push(child);
188
+ }
189
+ }
190
+ const stack = [...starts].reverse();
191
+ while (stack.length > 0) {
192
+ const el = stack.pop();
193
+ if (!el)
194
+ break;
195
+ yield el;
196
+ // Push children in reverse so we visit them left-to-right (pre-order).
197
+ const children = el.children;
198
+ for (let i = children.length - 1; i >= 0; i--) {
199
+ const child = children[i];
200
+ if (child)
201
+ stack.push(child);
202
+ }
203
+ }
204
+ }
205
+ function isDocument(x) {
206
+ // Document.nodeType === 9; DocumentFragment.nodeType === 11; Element.nodeType === 1.
207
+ return x.nodeType === 9;
208
+ }
209
+ //# sourceMappingURL=traverse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traverse.js","sourceRoot":"","sources":["../../src/shadow-dom/traverse.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * A single shadow host detected by `traverseShadowRoots`.
3
+ */
4
+ export interface ShadowRootInfo {
5
+ /** The host element on the light-DOM side of the boundary. */
6
+ host: Element;
7
+ /**
8
+ * The shadow root if we could reach it, otherwise `null`. `null` is
9
+ * always paired with `source: 'unreachable'`.
10
+ */
11
+ root: ShadowRoot | null;
12
+ /**
13
+ * What the host's shadow root is declared as.
14
+ *
15
+ * - `'open'` — resolved via `el.shadowRoot`.
16
+ * - `'closed'` — confirmed closed (resolved via the injected helper, or
17
+ * attached as closed via `attachShadow({mode:'closed'})`
18
+ * and we noticed via the helper).
19
+ * - `'unknown'` — heuristic detection without confirmation (e.g.
20
+ * custom-element heuristic).
21
+ */
22
+ mode: 'open' | 'closed' | 'unknown';
23
+ /**
24
+ * How the root was resolved.
25
+ *
26
+ * - `'attachShadow'` — `el.shadowRoot` reflection (open roots only).
27
+ * - `'chrome.dom'` — caller-injected `openOrClosedShadowRoot` helper,
28
+ * typically backed by `chrome.dom.openOrClosedShadowRoot`
29
+ * in an ISOLATED-world content script.
30
+ * - `'unreachable'` — host detected but root not accessible from this
31
+ * execution context. Browser encapsulation is being
32
+ * respected; this is the intended outcome in MAIN
33
+ * world for closed shadow roots.
34
+ */
35
+ source: 'attachShadow' | 'chrome.dom' | 'unreachable';
36
+ /**
37
+ * Nesting depth. The first generation of hosts found directly under the
38
+ * traversal `root` is `0`; hosts inside those hosts' shadow roots are
39
+ * `1`, and so on.
40
+ */
41
+ depth: number;
42
+ }
43
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/shadow-dom/types.ts"],"names":[],"mappings":"AAwBA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,8DAA8D;IAC9D,IAAI,EAAE,OAAO,CAAC;IACd;;;OAGG;IACH,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IACxB;;;;;;;;;OASG;IACH,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IACpC;;;;;;;;;;;OAWG;IACH,MAAM,EAAE,cAAc,GAAG,YAAY,GAAG,aAAa,CAAC;IACtD;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC;CACf"}
@@ -0,0 +1,25 @@
1
+ // Shadow DOM adapter types — Task 1.5.
2
+ //
3
+ // `ShadowRootInfo` is the single value the walker emits per host element.
4
+ // It captures both the resolved root (when reachable) and enough provenance
5
+ // for the consumer to understand WHY a given root is or is not in hand:
6
+ //
7
+ // - `mode` — what the host's shadow root was declared as. `'unknown'`
8
+ // is reserved for the heuristic "probably closed shadow,
9
+ // couldn't confirm" path so we don't lie and say `'closed'`
10
+ // when we never asked attachShadow at all.
11
+ // - `source` — how we resolved the root. `'attachShadow'` is the plain
12
+ // `el.shadowRoot` reflection (open roots). `'chrome.dom'`
13
+ // is the ISOLATED-world `chrome.dom.openOrClosedShadowRoot`
14
+ // helper that callers can inject. `'unreachable'` means we
15
+ // detected a host but couldn't reach into it (closed shadow
16
+ // in MAIN world, or the custom-element heuristic).
17
+ //
18
+ // `host` always points at the element on the document side. `root` is null
19
+ // only on the `'unreachable'` path — the consumer should not treat null as
20
+ // a value-tagged optional but as the literal "we cannot see inside this."
21
+ //
22
+ // See P2 PRD §A.3 for the closed-shadow-root MAIN-world limitation this
23
+ // shape documents.
24
+ export {};
25
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/shadow-dom/types.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,EAAE;AACF,0EAA0E;AAC1E,4EAA4E;AAC5E,wEAAwE;AACxE,EAAE;AACF,0EAA0E;AAC1E,wEAAwE;AACxE,2EAA2E;AAC3E,0DAA0D;AAC1D,yEAAyE;AACzE,yEAAyE;AACzE,2EAA2E;AAC3E,0EAA0E;AAC1E,2EAA2E;AAC3E,kEAAkE;AAClE,EAAE;AACF,2EAA2E;AAC3E,2EAA2E;AAC3E,0EAA0E;AAC1E,EAAE;AACF,wEAAwE;AACxE,mBAAmB"}
@@ -0,0 +1,59 @@
1
+ import type { customEvent, eventWithTime, recordOptions } from '../rrweb';
2
+ import { LARGE_DOM_DEFAULTS } from './defaults';
3
+ /**
4
+ * Options for `applyLargeDomGuards`. Everything is optional — the function
5
+ * defaults to `LARGE_DOM_DEFAULTS` and a no-op `onWarn` / `onLimit`.
6
+ */
7
+ export interface ApplyLargeDomGuardsOptions {
8
+ /**
9
+ * Override the bundled defaults. Useful for products that want to tune the
10
+ * mutation thresholds for their workload — but per IMPLEMENTATION_PLAN.md,
11
+ * tuning happens after we have real-world session data. Use sparingly.
12
+ */
13
+ defaults?: typeof LARGE_DOM_DEFAULTS;
14
+ /**
15
+ * Invoked with the `tracelane.mutation.warn` and `tracelane.event.dropped`
16
+ * breadcrumb custom events. Consumers can forward these to their own
17
+ * telemetry pipeline. Default: no-op.
18
+ */
19
+ onWarn?: (event: customEvent & {
20
+ timestamp: number;
21
+ }) => void;
22
+ /**
23
+ * Invoked exactly once when the cumulative mutation count exceeds
24
+ * `defaults.mutationLimit`. After this fires, the guard's emit wrapper
25
+ * stops forwarding events. Consumers should call rrweb's `record()`
26
+ * teardown from here if they want to truly halt the recorder.
27
+ */
28
+ onLimit?: () => void;
29
+ }
30
+ /**
31
+ * Compose the throttling defaults and guard chain onto an rrweb
32
+ * `recordOptions` object.
33
+ *
34
+ * Returns a new `recordOptions` with:
35
+ * - the first six `LARGE_DOM_DEFAULTS` (mousemoveWait, sampling,
36
+ * inlineImages, collectFonts, recordCanvas, plus the
37
+ * `checkoutEveryNms` mapped from `defaults.checkoutEveryMs`) merged in
38
+ * where the caller didn't already set them; and
39
+ * - an `emit` wrapper that runs dataUrl → eventSize → mutation in order.
40
+ *
41
+ * Note on semantics: because `applyLargeDomGuards` does not own the rrweb
42
+ * subscription, the hard mutation limit can only stop events from being
43
+ * forwarded from this wrapper onward — it cannot tear down the rrweb
44
+ * `record()` subscription on its own. Wire your own teardown via `onLimit`
45
+ * if you need the recorder itself to stop.
46
+ *
47
+ * @example
48
+ * const stop = record(
49
+ * applyLargeDomGuards(
50
+ * { emit: forwardToTransport },
51
+ * {
52
+ * onWarn: (e) => forwardToTransport(e),
53
+ * onLimit: () => stop?.(),
54
+ * },
55
+ * ),
56
+ * );
57
+ */
58
+ export declare function applyLargeDomGuards(recordOpts: recordOptions<eventWithTime>, options?: ApplyLargeDomGuardsOptions): recordOptions<eventWithTime>;
59
+ //# sourceMappingURL=apply.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/throttling/apply.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAQhD;;;GAGG;AACH,MAAM,WAAW,0BAA0B;IACzC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,kBAAkB,CAAC;IACrC;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC9D;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,aAAa,CAAC,aAAa,CAAC,EACxC,OAAO,GAAE,0BAA+B,GACvC,aAAa,CAAC,aAAa,CAAC,CAiD9B"}