@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.
- package/NOTICE +15 -0
- package/README.md +26 -0
- package/dist/compat/index.d.ts +37 -0
- package/dist/compat/index.d.ts.map +1 -0
- package/dist/compat/index.js +97 -0
- package/dist/compat/index.js.map +1 -0
- package/dist/compression/index.d.ts +24 -0
- package/dist/compression/index.d.ts.map +1 -0
- package/dist/compression/index.js +61 -0
- package/dist/compression/index.js.map +1 -0
- package/dist/console/buffer.d.ts +99 -0
- package/dist/console/buffer.d.ts.map +1 -0
- package/dist/console/buffer.js +169 -0
- package/dist/console/buffer.js.map +1 -0
- package/dist/console/index.d.ts +3 -0
- package/dist/console/index.d.ts.map +1 -0
- package/dist/console/index.js +16 -0
- package/dist/console/index.js.map +1 -0
- package/dist/console/types.d.ts +61 -0
- package/dist/console/types.d.ts.map +1 -0
- package/dist/console/types.js +11 -0
- package/dist/console/types.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/masking/body.d.ts +30 -0
- package/dist/masking/body.d.ts.map +1 -0
- package/dist/masking/body.js +33 -0
- package/dist/masking/body.js.map +1 -0
- package/dist/masking/headers.d.ts +16 -0
- package/dist/masking/headers.d.ts.map +1 -0
- package/dist/masking/headers.js +46 -0
- package/dist/masking/headers.js.map +1 -0
- package/dist/masking/index.d.ts +8 -0
- package/dist/masking/index.d.ts.map +1 -0
- package/dist/masking/index.js +11 -0
- package/dist/masking/index.js.map +1 -0
- package/dist/masking/inputs.d.ts +15 -0
- package/dist/masking/inputs.d.ts.map +1 -0
- package/dist/masking/inputs.js +36 -0
- package/dist/masking/inputs.js.map +1 -0
- package/dist/masking/regex.d.ts +96 -0
- package/dist/masking/regex.d.ts.map +1 -0
- package/dist/masking/regex.js +182 -0
- package/dist/masking/regex.js.map +1 -0
- package/dist/masking/selectors.d.ts +67 -0
- package/dist/masking/selectors.d.ts.map +1 -0
- package/dist/masking/selectors.js +137 -0
- package/dist/masking/selectors.js.map +1 -0
- package/dist/masking/text.d.ts +9 -0
- package/dist/masking/text.d.ts.map +1 -0
- package/dist/masking/text.js +15 -0
- package/dist/masking/text.js.map +1 -0
- package/dist/network/cdp.d.ts +54 -0
- package/dist/network/cdp.d.ts.map +1 -0
- package/dist/network/cdp.js +282 -0
- package/dist/network/cdp.js.map +1 -0
- package/dist/network/index.d.ts +4 -0
- package/dist/network/index.d.ts.map +1 -0
- package/dist/network/index.js +14 -0
- package/dist/network/index.js.map +1 -0
- package/dist/network/types.d.ts +133 -0
- package/dist/network/types.d.ts.map +1 -0
- package/dist/network/types.js +35 -0
- package/dist/network/types.js.map +1 -0
- package/dist/network/web-request.d.ts +76 -0
- package/dist/network/web-request.d.ts.map +1 -0
- package/dist/network/web-request.js +294 -0
- package/dist/network/web-request.js.map +1 -0
- package/dist/persistence/index.d.ts +3 -0
- package/dist/persistence/index.d.ts.map +1 -0
- package/dist/persistence/index.js +11 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/persistence/store.d.ts +18 -0
- package/dist/persistence/store.d.ts.map +1 -0
- package/dist/persistence/store.js +327 -0
- package/dist/persistence/store.js.map +1 -0
- package/dist/persistence/types.d.ts +76 -0
- package/dist/persistence/types.d.ts.map +1 -0
- package/dist/persistence/types.js +21 -0
- package/dist/persistence/types.js.map +1 -0
- package/dist/rrweb.d.ts +5 -0
- package/dist/rrweb.d.ts.map +1 -0
- package/dist/rrweb.js +13 -0
- package/dist/rrweb.js.map +1 -0
- package/dist/screenshot/base64.d.ts +8 -0
- package/dist/screenshot/base64.d.ts.map +1 -0
- package/dist/screenshot/base64.js +21 -0
- package/dist/screenshot/base64.js.map +1 -0
- package/dist/screenshot/cdp.d.ts +50 -0
- package/dist/screenshot/cdp.d.ts.map +1 -0
- package/dist/screenshot/cdp.js +65 -0
- package/dist/screenshot/cdp.js.map +1 -0
- package/dist/screenshot/index.d.ts +4 -0
- package/dist/screenshot/index.d.ts.map +1 -0
- package/dist/screenshot/index.js +10 -0
- package/dist/screenshot/index.js.map +1 -0
- package/dist/screenshot/tabs.d.ts +44 -0
- package/dist/screenshot/tabs.d.ts.map +1 -0
- package/dist/screenshot/tabs.js +63 -0
- package/dist/screenshot/tabs.js.map +1 -0
- package/dist/screenshot/types.d.ts +27 -0
- package/dist/screenshot/types.d.ts.map +1 -0
- package/dist/screenshot/types.js +18 -0
- package/dist/screenshot/types.js.map +1 -0
- package/dist/shadow-dom/index.d.ts +3 -0
- package/dist/shadow-dom/index.d.ts.map +1 -0
- package/dist/shadow-dom/index.js +8 -0
- package/dist/shadow-dom/index.js.map +1 -0
- package/dist/shadow-dom/traverse.d.ts +54 -0
- package/dist/shadow-dom/traverse.d.ts.map +1 -0
- package/dist/shadow-dom/traverse.js +209 -0
- package/dist/shadow-dom/traverse.js.map +1 -0
- package/dist/shadow-dom/types.d.ts +43 -0
- package/dist/shadow-dom/types.d.ts.map +1 -0
- package/dist/shadow-dom/types.js +25 -0
- package/dist/shadow-dom/types.js.map +1 -0
- package/dist/throttling/apply.d.ts +59 -0
- package/dist/throttling/apply.d.ts.map +1 -0
- package/dist/throttling/apply.js +101 -0
- package/dist/throttling/apply.js.map +1 -0
- package/dist/throttling/defaults.d.ts +60 -0
- package/dist/throttling/defaults.d.ts.map +1 -0
- package/dist/throttling/defaults.js +81 -0
- package/dist/throttling/defaults.js.map +1 -0
- package/dist/throttling/guards.d.ts +69 -0
- package/dist/throttling/guards.d.ts.map +1 -0
- package/dist/throttling/guards.js +212 -0
- package/dist/throttling/guards.js.map +1 -0
- package/dist/throttling/index.d.ts +5 -0
- package/dist/throttling/index.d.ts.map +1 -0
- package/dist/throttling/index.js +11 -0
- package/dist/throttling/index.js.map +1 -0
- package/package.json +35 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// Public throttling entrypoint — Task 1.4.
|
|
2
|
+
//
|
|
3
|
+
// `applyLargeDomGuards` is the only function the substrate exports from this
|
|
4
|
+
// module (alongside `LARGE_DOM_DEFAULTS`). It composes the three guards from
|
|
5
|
+
// `guards.ts` into a single `recordOptions` transform:
|
|
6
|
+
//
|
|
7
|
+
// 1. Pull recordOptions-shaped defaults (mousemoveWait/sampling/etc.) onto
|
|
8
|
+
// the consumer's options, with the consumer winning on conflicts.
|
|
9
|
+
// 2. Replace the consumer's `emit` with a guarded wrapper that runs every
|
|
10
|
+
// event through dataUrl → eventSize → mutation, in that order. The
|
|
11
|
+
// ordering matters: the data-URL guard shrinks oversized snapshot
|
|
12
|
+
// payloads BEFORE the event-size guard measures them, otherwise an
|
|
13
|
+
// oversize data URL would always trip the size cap and silently drop
|
|
14
|
+
// the whole snapshot.
|
|
15
|
+
//
|
|
16
|
+
// The guards never reach into rrweb's subscription, so this function can't
|
|
17
|
+
// truly stop the recorder on its own. The hard-mutation-limit semantics are:
|
|
18
|
+
// further events are dropped at the wrapper, AND the consumer's `onLimit`
|
|
19
|
+
// callback fires once (consumers wire this to rrweb's teardown if they want
|
|
20
|
+
// a true stop).
|
|
21
|
+
import { LARGE_DOM_DEFAULTS } from './defaults';
|
|
22
|
+
import { applyDataUrlGuard, applyEventSizeGuard, applyMutationGuard, } from './guards';
|
|
23
|
+
/**
|
|
24
|
+
* Compose the throttling defaults and guard chain onto an rrweb
|
|
25
|
+
* `recordOptions` object.
|
|
26
|
+
*
|
|
27
|
+
* Returns a new `recordOptions` with:
|
|
28
|
+
* - the first six `LARGE_DOM_DEFAULTS` (mousemoveWait, sampling,
|
|
29
|
+
* inlineImages, collectFonts, recordCanvas, plus the
|
|
30
|
+
* `checkoutEveryNms` mapped from `defaults.checkoutEveryMs`) merged in
|
|
31
|
+
* where the caller didn't already set them; and
|
|
32
|
+
* - an `emit` wrapper that runs dataUrl → eventSize → mutation in order.
|
|
33
|
+
*
|
|
34
|
+
* Note on semantics: because `applyLargeDomGuards` does not own the rrweb
|
|
35
|
+
* subscription, the hard mutation limit can only stop events from being
|
|
36
|
+
* forwarded from this wrapper onward — it cannot tear down the rrweb
|
|
37
|
+
* `record()` subscription on its own. Wire your own teardown via `onLimit`
|
|
38
|
+
* if you need the recorder itself to stop.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* const stop = record(
|
|
42
|
+
* applyLargeDomGuards(
|
|
43
|
+
* { emit: forwardToTransport },
|
|
44
|
+
* {
|
|
45
|
+
* onWarn: (e) => forwardToTransport(e),
|
|
46
|
+
* onLimit: () => stop?.(),
|
|
47
|
+
* },
|
|
48
|
+
* ),
|
|
49
|
+
* );
|
|
50
|
+
*/
|
|
51
|
+
export function applyLargeDomGuards(recordOpts, options = {}) {
|
|
52
|
+
const defaults = options.defaults ?? LARGE_DOM_DEFAULTS;
|
|
53
|
+
const onWarn = options.onWarn ?? noop;
|
|
54
|
+
const callerEmit = recordOpts.emit ?? noopEmit;
|
|
55
|
+
// Build the guard chain. Order is significant — see file header.
|
|
56
|
+
// Note: `onLimit` is spread conditionally because tsconfig
|
|
57
|
+
// `exactOptionalPropertyTypes: true` disallows `undefined` on an optional
|
|
58
|
+
// field — the property either exists with a function value or is absent.
|
|
59
|
+
const mutationHooks = {
|
|
60
|
+
softWarnAt: defaults.mutationSoftWarnAt,
|
|
61
|
+
hardLimit: defaults.mutationLimit,
|
|
62
|
+
emitWarn: onWarn,
|
|
63
|
+
emitLimit: onWarn,
|
|
64
|
+
...(options.onLimit !== undefined ? { onLimit: options.onLimit } : {}),
|
|
65
|
+
};
|
|
66
|
+
const guardedEmit = applyMutationGuard((event, isCheckout) => {
|
|
67
|
+
const post = applyEventSizeGuard(event, defaults.singleEventMaxBytes, onWarn);
|
|
68
|
+
if (post === null)
|
|
69
|
+
return;
|
|
70
|
+
callerEmit(post, isCheckout);
|
|
71
|
+
}, mutationHooks);
|
|
72
|
+
const composedEmit = (event, isCheckout) => {
|
|
73
|
+
const post = applyDataUrlGuard(event, defaults.dataUrlMaxBytes);
|
|
74
|
+
guardedEmit(post, isCheckout);
|
|
75
|
+
};
|
|
76
|
+
// Merge — caller wins on every key that's explicitly set.
|
|
77
|
+
const merged = {
|
|
78
|
+
mousemoveWait: defaults.mousemoveWait,
|
|
79
|
+
sampling: { ...defaults.sampling },
|
|
80
|
+
inlineImages: defaults.inlineImages,
|
|
81
|
+
collectFonts: defaults.collectFonts,
|
|
82
|
+
recordCanvas: defaults.recordCanvas,
|
|
83
|
+
checkoutEveryNms: defaults.checkoutEveryMs,
|
|
84
|
+
...recordOpts,
|
|
85
|
+
// emit is always the composed wrapper; the caller's emit has been captured
|
|
86
|
+
// by `callerEmit` above.
|
|
87
|
+
emit: composedEmit,
|
|
88
|
+
};
|
|
89
|
+
// `sampling` is a nested object — re-merge so caller-provided keys win.
|
|
90
|
+
if (recordOpts.sampling !== undefined) {
|
|
91
|
+
merged.sampling = { ...defaults.sampling, ...recordOpts.sampling };
|
|
92
|
+
}
|
|
93
|
+
return merged;
|
|
94
|
+
}
|
|
95
|
+
function noop() {
|
|
96
|
+
/* intentionally empty — default onWarn / onLimit */
|
|
97
|
+
}
|
|
98
|
+
function noopEmit(_event, _isCheckout) {
|
|
99
|
+
/* intentionally empty — fallback when caller didn't provide an emit */
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=apply.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply.js","sourceRoot":"","sources":["../../src/throttling/apply.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,uDAAuD;AACvD,EAAE;AACF,6EAA6E;AAC7E,uEAAuE;AACvE,4EAA4E;AAC5E,wEAAwE;AACxE,uEAAuE;AACvE,wEAAwE;AACxE,0EAA0E;AAC1E,2BAA2B;AAC3B,EAAE;AACF,2EAA2E;AAC3E,6EAA6E;AAC7E,0EAA0E;AAC1E,4EAA4E;AAC5E,gBAAgB;AAGhB,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAEL,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,UAAU,CAAC;AA4BlB;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,mBAAmB,CACjC,UAAwC,EACxC,UAAsC,EAAE;IAExC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,kBAAkB,CAAC;IACxD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC;IAEtC,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,IAAI,QAAQ,CAAC;IAE/C,iEAAiE;IACjE,2DAA2D;IAC3D,0EAA0E;IAC1E,yEAAyE;IACzE,MAAM,aAAa,GAAuB;QACxC,UAAU,EAAE,QAAQ,CAAC,kBAAkB;QACvC,SAAS,EAAE,QAAQ,CAAC,aAAa;QACjC,QAAQ,EAAE,MAAM;QAChB,SAAS,EAAE,MAAM;QACjB,GAAG,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACvE,CAAC;IAEF,MAAM,WAAW,GAAG,kBAAkB,CAAC,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE;QAC3D,MAAM,IAAI,GAAG,mBAAmB,CAAC,KAAK,EAAE,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;QAC9E,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO;QAC1B,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAC/B,CAAC,EAAE,aAAa,CAAC,CAAC;IAElB,MAAM,YAAY,GAAG,CAAC,KAAoB,EAAE,UAAoB,EAAQ,EAAE;QACxE,MAAM,IAAI,GAAG,iBAAiB,CAAC,KAAK,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;QAChE,WAAW,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAChC,CAAC,CAAC;IAEF,0DAA0D;IAC1D,MAAM,MAAM,GAAiC;QAC3C,aAAa,EAAE,QAAQ,CAAC,aAAa;QACrC,QAAQ,EAAE,EAAE,GAAG,QAAQ,CAAC,QAAQ,EAAE;QAClC,YAAY,EAAE,QAAQ,CAAC,YAAY;QACnC,YAAY,EAAE,QAAQ,CAAC,YAAY;QACnC,YAAY,EAAE,QAAQ,CAAC,YAAY;QACnC,gBAAgB,EAAE,QAAQ,CAAC,eAAe;QAC1C,GAAG,UAAU;QACb,2EAA2E;QAC3E,yBAAyB;QACzB,IAAI,EAAE,YAAY;KACnB,CAAC;IAEF,wEAAwE;IACxE,IAAI,UAAU,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACtC,MAAM,CAAC,QAAQ,GAAG,EAAE,GAAG,QAAQ,CAAC,QAAQ,EAAE,GAAG,UAAU,CAAC,QAAQ,EAAE,CAAC;IACrE,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,IAAI;IACX,oDAAoD;AACtD,CAAC;AAED,SAAS,QAAQ,CAAC,MAAqB,EAAE,WAAqB;IAC5D,uEAAuE;AACzE,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default throttling configuration for the substrate.
|
|
3
|
+
*
|
|
4
|
+
* The first six properties land directly on rrweb's `recordOptions`; the
|
|
5
|
+
* remaining five are interpreted by `applyLargeDomGuards`. Values match the
|
|
6
|
+
* shared-preamble §2 numbers verbatim — tune only on the back of real-world
|
|
7
|
+
* session data.
|
|
8
|
+
*/
|
|
9
|
+
export declare const LARGE_DOM_DEFAULTS: Readonly<{
|
|
10
|
+
/** Throttle window for mousemove sampling, in milliseconds. */
|
|
11
|
+
mousemoveWait: 50;
|
|
12
|
+
/**
|
|
13
|
+
* Frozen rrweb sampling strategy. `scroll: 100` throttles scroll events;
|
|
14
|
+
* `input: 'last'` only emits the final input value rather than every
|
|
15
|
+
* keystroke (mirrors PostHog's session-replay default).
|
|
16
|
+
*/
|
|
17
|
+
sampling: Readonly<{
|
|
18
|
+
scroll: 100;
|
|
19
|
+
input: "last";
|
|
20
|
+
}>;
|
|
21
|
+
/** Disable base64-inlined images — too expensive on rich pages. */
|
|
22
|
+
inlineImages: false;
|
|
23
|
+
/** Disable font sniffing — produces enormous events on font-heavy sites. */
|
|
24
|
+
collectFonts: false;
|
|
25
|
+
/** Disable canvas recording — canvas streams alone can dwarf a session. */
|
|
26
|
+
recordCanvas: false;
|
|
27
|
+
/**
|
|
28
|
+
* Hard mutation cap (cumulative across the recording). When exceeded,
|
|
29
|
+
* `applyLargeDomGuards` stops forwarding events and invokes the consumer's
|
|
30
|
+
* `onLimit` callback (the consumer can then call rrweb's teardown).
|
|
31
|
+
*/
|
|
32
|
+
mutationLimit: 10000;
|
|
33
|
+
/**
|
|
34
|
+
* Per-batch soft warning threshold. When a single mutation batch exceeds
|
|
35
|
+
* this, the guard emits one `tracelane.mutation.warn` custom event per
|
|
36
|
+
* batch — never spammed.
|
|
37
|
+
*/
|
|
38
|
+
mutationSoftWarnAt: 750;
|
|
39
|
+
/**
|
|
40
|
+
* Maximum size for a single `data:` URL value (in `src`/`href` attributes).
|
|
41
|
+
* Larger values are replaced with the SVG placeholder declared in
|
|
42
|
+
* `guards.ts`. 5 MB matches PostHog's session-replay tuning.
|
|
43
|
+
*/
|
|
44
|
+
dataUrlMaxBytes: number;
|
|
45
|
+
/**
|
|
46
|
+
* Maximum JSON-stringified size for a single event. Anything larger is
|
|
47
|
+
* dropped (replaced with a `tracelane.event.dropped` breadcrumb) — we
|
|
48
|
+
* don't try to truncate inside an event because the result is rarely
|
|
49
|
+
* replayable.
|
|
50
|
+
*/
|
|
51
|
+
singleEventMaxBytes: number;
|
|
52
|
+
/**
|
|
53
|
+
* Buffer roll-over cadence in milliseconds. rrweb re-emits a full
|
|
54
|
+
* snapshot on this interval. 30s avoids the documented PostHog
|
|
55
|
+
* single-page-app missing-recording bug.
|
|
56
|
+
*/
|
|
57
|
+
checkoutEveryMs: 30000;
|
|
58
|
+
}>;
|
|
59
|
+
export type LargeDomDefaults = typeof LARGE_DOM_DEFAULTS;
|
|
60
|
+
//# sourceMappingURL=defaults.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/throttling/defaults.ts"],"names":[],"mappings":"AAuBA;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB;IAC7B,+DAA+D;;IAG/D;;;;OAIG;;;;;IAMH,mEAAmE;;IAGnE,4EAA4E;;IAG5E,2EAA2E;;IAG3E;;;;OAIG;;IAGH;;;;OAIG;;IAGH;;;;OAIG;;IAGH;;;;;OAKG;;IAGH;;;;OAIG;;EAEH,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,OAAO,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Large-DOM throttling defaults — Task 1.4.
|
|
2
|
+
//
|
|
3
|
+
// The first six values (mousemoveWait / sampling.scroll / sampling.input /
|
|
4
|
+
// inlineImages / collectFonts / recordCanvas) are pass-through rrweb config
|
|
5
|
+
// inherited from the PostHog fork's session-replay tuning. They prevent
|
|
6
|
+
// recordings from exploding on heavy SPAs.
|
|
7
|
+
//
|
|
8
|
+
// The last five values are tracelane-specific guard thresholds applied via
|
|
9
|
+
// `applyLargeDomGuards`:
|
|
10
|
+
// - mutationLimit / mutationSoftWarnAt — Sentry-parity mutation guard
|
|
11
|
+
// - dataUrlMaxBytes — PostHog-parity data-URL guard
|
|
12
|
+
// - singleEventMaxBytes — Kafka-class ingest realism cap
|
|
13
|
+
// - checkoutEveryMs — re-snapshot cadence to dodge the
|
|
14
|
+
// documented PostHog SPA
|
|
15
|
+
// missing-recording bug
|
|
16
|
+
//
|
|
17
|
+
// All values are frozen at module load so neither the substrate nor consumers
|
|
18
|
+
// can mutate them at runtime; consumers needing different thresholds pass a
|
|
19
|
+
// fresh `defaults` object into `applyLargeDomGuards`.
|
|
20
|
+
//
|
|
21
|
+
// See shared-preamble §2 ("Large-DOM throttling defaults") and ADR-0002 for
|
|
22
|
+
// the rationale and source links.
|
|
23
|
+
/**
|
|
24
|
+
* Default throttling configuration for the substrate.
|
|
25
|
+
*
|
|
26
|
+
* The first six properties land directly on rrweb's `recordOptions`; the
|
|
27
|
+
* remaining five are interpreted by `applyLargeDomGuards`. Values match the
|
|
28
|
+
* shared-preamble §2 numbers verbatim — tune only on the back of real-world
|
|
29
|
+
* session data.
|
|
30
|
+
*/
|
|
31
|
+
export const LARGE_DOM_DEFAULTS = Object.freeze({
|
|
32
|
+
/** Throttle window for mousemove sampling, in milliseconds. */
|
|
33
|
+
mousemoveWait: 50,
|
|
34
|
+
/**
|
|
35
|
+
* Frozen rrweb sampling strategy. `scroll: 100` throttles scroll events;
|
|
36
|
+
* `input: 'last'` only emits the final input value rather than every
|
|
37
|
+
* keystroke (mirrors PostHog's session-replay default).
|
|
38
|
+
*/
|
|
39
|
+
sampling: Object.freeze({
|
|
40
|
+
scroll: 100,
|
|
41
|
+
input: 'last',
|
|
42
|
+
}),
|
|
43
|
+
/** Disable base64-inlined images — too expensive on rich pages. */
|
|
44
|
+
inlineImages: false,
|
|
45
|
+
/** Disable font sniffing — produces enormous events on font-heavy sites. */
|
|
46
|
+
collectFonts: false,
|
|
47
|
+
/** Disable canvas recording — canvas streams alone can dwarf a session. */
|
|
48
|
+
recordCanvas: false,
|
|
49
|
+
/**
|
|
50
|
+
* Hard mutation cap (cumulative across the recording). When exceeded,
|
|
51
|
+
* `applyLargeDomGuards` stops forwarding events and invokes the consumer's
|
|
52
|
+
* `onLimit` callback (the consumer can then call rrweb's teardown).
|
|
53
|
+
*/
|
|
54
|
+
mutationLimit: 10000,
|
|
55
|
+
/**
|
|
56
|
+
* Per-batch soft warning threshold. When a single mutation batch exceeds
|
|
57
|
+
* this, the guard emits one `tracelane.mutation.warn` custom event per
|
|
58
|
+
* batch — never spammed.
|
|
59
|
+
*/
|
|
60
|
+
mutationSoftWarnAt: 750,
|
|
61
|
+
/**
|
|
62
|
+
* Maximum size for a single `data:` URL value (in `src`/`href` attributes).
|
|
63
|
+
* Larger values are replaced with the SVG placeholder declared in
|
|
64
|
+
* `guards.ts`. 5 MB matches PostHog's session-replay tuning.
|
|
65
|
+
*/
|
|
66
|
+
dataUrlMaxBytes: 5 * 1024 * 1024, // 5 MB
|
|
67
|
+
/**
|
|
68
|
+
* Maximum JSON-stringified size for a single event. Anything larger is
|
|
69
|
+
* dropped (replaced with a `tracelane.event.dropped` breadcrumb) — we
|
|
70
|
+
* don't try to truncate inside an event because the result is rarely
|
|
71
|
+
* replayable.
|
|
72
|
+
*/
|
|
73
|
+
singleEventMaxBytes: 1024 * 1024, // 1 MB
|
|
74
|
+
/**
|
|
75
|
+
* Buffer roll-over cadence in milliseconds. rrweb re-emits a full
|
|
76
|
+
* snapshot on this interval. 30s avoids the documented PostHog
|
|
77
|
+
* single-page-app missing-recording bug.
|
|
78
|
+
*/
|
|
79
|
+
checkoutEveryMs: 30_000,
|
|
80
|
+
});
|
|
81
|
+
//# sourceMappingURL=defaults.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/throttling/defaults.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,EAAE;AACF,2EAA2E;AAC3E,4EAA4E;AAC5E,wEAAwE;AACxE,2CAA2C;AAC3C,EAAE;AACF,2EAA2E;AAC3E,yBAAyB;AACzB,wEAAwE;AACxE,yEAAyE;AACzE,0EAA0E;AAC1E,4EAA4E;AAC5E,kEAAkE;AAClE,iEAAiE;AACjE,EAAE;AACF,8EAA8E;AAC9E,4EAA4E;AAC5E,sDAAsD;AACtD,EAAE;AACF,4EAA4E;AAC5E,kCAAkC;AAElC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,CAAC;IAC9C,+DAA+D;IAC/D,aAAa,EAAE,EAAE;IAEjB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC;QACtB,MAAM,EAAE,GAAG;QACX,KAAK,EAAE,MAAe;KACvB,CAAC;IAEF,mEAAmE;IACnE,YAAY,EAAE,KAAK;IAEnB,4EAA4E;IAC5E,YAAY,EAAE,KAAK;IAEnB,2EAA2E;IAC3E,YAAY,EAAE,KAAK;IAEnB;;;;OAIG;IACH,aAAa,EAAE,KAAK;IAEpB;;;;OAIG;IACH,kBAAkB,EAAE,GAAG;IAEvB;;;;OAIG;IACH,eAAe,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI,EAAE,OAAO;IAEzC;;;;;OAKG;IACH,mBAAmB,EAAE,IAAI,GAAG,IAAI,EAAE,OAAO;IAEzC;;;;OAIG;IACH,eAAe,EAAE,MAAM;CACxB,CAAC,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { customEvent, eventWithTime } from '../rrweb';
|
|
2
|
+
/**
|
|
3
|
+
* 1×1 transparent SVG carrying the text marker `CUBENEST-DATA-URL-OVERSIZE`.
|
|
4
|
+
* Base64-encoded so the resulting `data:image/svg+xml;base64,...` URL is
|
|
5
|
+
* compact and replays without parse warnings in modern browsers.
|
|
6
|
+
*
|
|
7
|
+
* Source SVG (pre-base64):
|
|
8
|
+
* <svg xmlns="http://www.w3.org/2000/svg" width="1" height="1">
|
|
9
|
+
* <text>CUBENEST-DATA-URL-OVERSIZE</text>
|
|
10
|
+
* </svg>
|
|
11
|
+
*
|
|
12
|
+
* Substituted by `applyDataUrlGuard` when a data: URL exceeds the configured
|
|
13
|
+
* `dataUrlMaxBytes`. Kept here as a frozen constant so test fixtures and
|
|
14
|
+
* downstream tooling can identify oversize hits by exact-match.
|
|
15
|
+
*/
|
|
16
|
+
export declare const DATA_URL_PLACEHOLDER = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxIiBoZWlnaHQ9IjEiPjx0ZXh0PkNVQkVORVNULURBVEEtVVJMLU9WRVJTSVpFPC90ZXh0Pjwvc3ZnPg==";
|
|
17
|
+
/**
|
|
18
|
+
* Replaces oversized data: URLs in either:
|
|
19
|
+
* - a FullSnapshot event's tree (`data.node`), or
|
|
20
|
+
* - an IncrementalSnapshot.Mutation event's `adds` array (each `node`).
|
|
21
|
+
*
|
|
22
|
+
* Non-snapshot events are returned unchanged. Small data URLs and non-data
|
|
23
|
+
* URLs are preserved verbatim. The original event is never mutated.
|
|
24
|
+
*/
|
|
25
|
+
export declare function applyDataUrlGuard(event: eventWithTime, maxBytes: number): eventWithTime;
|
|
26
|
+
/**
|
|
27
|
+
* Drops events whose JSON-stringified size exceeds `maxBytes`.
|
|
28
|
+
*
|
|
29
|
+
* When dropping, the guard emits a sibling `tracelane.event.dropped` custom
|
|
30
|
+
* event via `onDrop` (so the consumer can forward it). The dropped breadcrumb
|
|
31
|
+
* carries `{ type, ts, size }` — enough for postmortem reasoning without
|
|
32
|
+
* leaking the dropped event's payload. Returns `null` when the input is
|
|
33
|
+
* dropped, otherwise returns the input unchanged.
|
|
34
|
+
*
|
|
35
|
+
* Size is measured via `JSON.stringify().length` (UTF-16 code units), which
|
|
36
|
+
* is a close-enough proxy for transport bytes at this layer.
|
|
37
|
+
*/
|
|
38
|
+
export declare function applyEventSizeGuard(event: eventWithTime, maxBytes: number, onDrop: (breadcrumb: customEvent<{
|
|
39
|
+
type: number;
|
|
40
|
+
ts: number;
|
|
41
|
+
size: number;
|
|
42
|
+
}> & {
|
|
43
|
+
timestamp: number;
|
|
44
|
+
}) => void): eventWithTime | null;
|
|
45
|
+
/**
|
|
46
|
+
* Hooks supplied to `applyMutationGuard`. The guard fires `onWarn` at most
|
|
47
|
+
* once per batch (when batch count > soft threshold), and `onLimit` at most
|
|
48
|
+
* once per recording (when cumulative count > hard limit). After `onLimit`,
|
|
49
|
+
* the wrapped emit is a no-op — consumers should call rrweb's teardown from
|
|
50
|
+
* `onLimit` to truly stop the recorder.
|
|
51
|
+
*/
|
|
52
|
+
export interface MutationGuardHooks {
|
|
53
|
+
softWarnAt: number;
|
|
54
|
+
hardLimit: number;
|
|
55
|
+
emitWarn: (event: customEvent & {
|
|
56
|
+
timestamp: number;
|
|
57
|
+
}) => void;
|
|
58
|
+
emitLimit: (event: customEvent & {
|
|
59
|
+
timestamp: number;
|
|
60
|
+
}) => void;
|
|
61
|
+
onLimit?: () => void;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Wraps an `emit` callback with the cumulative + per-batch mutation counters.
|
|
65
|
+
* Returns a new emit function with the same signature; the wrapper is
|
|
66
|
+
* stateful (closure-captured counters).
|
|
67
|
+
*/
|
|
68
|
+
export declare function applyMutationGuard(emit: (event: eventWithTime, isCheckout?: boolean) => void, hooks: MutationGuardHooks): (event: eventWithTime, isCheckout?: boolean) => void;
|
|
69
|
+
//# sourceMappingURL=guards.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guards.d.ts","sourceRoot":"","sources":["../../src/throttling/guards.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAM3D;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,oBAAoB,+KAC6I,CAAC;AAsD/K;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,CA8BvF;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,CACN,UAAU,EAAE,WAAW,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG;IACpE,SAAS,EAAE,MAAM,CAAC;CACnB,KACE,IAAI,GACR,aAAa,GAAG,IAAI,CA4BtB;AA4BD;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC/D,SAAS,EAAE,CAAC,KAAK,EAAE,WAAW,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAChE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,UAAU,CAAC,EAAE,OAAO,KAAK,IAAI,EAC1D,KAAK,EAAE,kBAAkB,GACxB,CAAC,KAAK,EAAE,aAAa,EAAE,UAAU,CAAC,EAAE,OAAO,KAAK,IAAI,CA0CtD"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// Guard implementations — Task 1.4.
|
|
2
|
+
//
|
|
3
|
+
// Three pure-ish guards, applied in this order by `applyLargeDomGuards`:
|
|
4
|
+
//
|
|
5
|
+
// 1. `applyDataUrlGuard` — substitutes oversized data:URL attributes
|
|
6
|
+
// 2. `applyEventSizeGuard` — drops events whose JSON payload exceeds the
|
|
7
|
+
// configured cap
|
|
8
|
+
// 3. `applyMutationGuard` — stateful counter wrapping the emit callback;
|
|
9
|
+
// warns above the soft threshold, halts above
|
|
10
|
+
// the hard limit
|
|
11
|
+
//
|
|
12
|
+
// Guards 1 and 2 are pure transforms (return a new event or null). Guard 3
|
|
13
|
+
// is a higher-order function that returns a wrapped emitter — the only
|
|
14
|
+
// stateful guard, because per-batch + cumulative counters need persistence
|
|
15
|
+
// across calls.
|
|
16
|
+
//
|
|
17
|
+
// None of the guards mutate their input events; they return new objects
|
|
18
|
+
// (shallow + relevant nested clone) so the caller's references stay clean.
|
|
19
|
+
import { EventType, IncrementalSource } from '../rrweb';
|
|
20
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
21
|
+
// Data-URL placeholder
|
|
22
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
23
|
+
/**
|
|
24
|
+
* 1×1 transparent SVG carrying the text marker `CUBENEST-DATA-URL-OVERSIZE`.
|
|
25
|
+
* Base64-encoded so the resulting `data:image/svg+xml;base64,...` URL is
|
|
26
|
+
* compact and replays without parse warnings in modern browsers.
|
|
27
|
+
*
|
|
28
|
+
* Source SVG (pre-base64):
|
|
29
|
+
* <svg xmlns="http://www.w3.org/2000/svg" width="1" height="1">
|
|
30
|
+
* <text>CUBENEST-DATA-URL-OVERSIZE</text>
|
|
31
|
+
* </svg>
|
|
32
|
+
*
|
|
33
|
+
* Substituted by `applyDataUrlGuard` when a data: URL exceeds the configured
|
|
34
|
+
* `dataUrlMaxBytes`. Kept here as a frozen constant so test fixtures and
|
|
35
|
+
* downstream tooling can identify oversize hits by exact-match.
|
|
36
|
+
*/
|
|
37
|
+
export const DATA_URL_PLACEHOLDER = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxIiBoZWlnaHQ9IjEiPjx0ZXh0PkNVQkVORVNULURBVEEtVVJMLU9WRVJTSVpFPC90ZXh0Pjwvc3ZnPg==';
|
|
38
|
+
const DATA_URL_PREFIX = /^data:/;
|
|
39
|
+
function isObject(v) {
|
|
40
|
+
return typeof v === 'object' && v !== null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Walks a serialized node and its `childNodes` recursively, replacing any
|
|
44
|
+
* `src`/`href` attribute whose value is a data: URL longer than `maxBytes`
|
|
45
|
+
* with the SVG placeholder. Returns a deeply-cloned node — input is not
|
|
46
|
+
* mutated.
|
|
47
|
+
*/
|
|
48
|
+
function walkAndGuardNode(node, maxBytes) {
|
|
49
|
+
const clone = { ...node };
|
|
50
|
+
if (isObject(node.attributes)) {
|
|
51
|
+
const attrs = { ...node.attributes };
|
|
52
|
+
for (const key of ['src', 'href']) {
|
|
53
|
+
const value = attrs[key];
|
|
54
|
+
if (typeof value === 'string' && DATA_URL_PREFIX.test(value) && value.length > maxBytes) {
|
|
55
|
+
attrs[key] = DATA_URL_PLACEHOLDER;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
clone.attributes = attrs;
|
|
59
|
+
}
|
|
60
|
+
if (Array.isArray(node.childNodes)) {
|
|
61
|
+
clone.childNodes = node.childNodes.map((child) => isObject(child) ? walkAndGuardNode(child, maxBytes) : child);
|
|
62
|
+
}
|
|
63
|
+
return clone;
|
|
64
|
+
}
|
|
65
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
66
|
+
// Data-URL guard
|
|
67
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
68
|
+
/**
|
|
69
|
+
* Replaces oversized data: URLs in either:
|
|
70
|
+
* - a FullSnapshot event's tree (`data.node`), or
|
|
71
|
+
* - an IncrementalSnapshot.Mutation event's `adds` array (each `node`).
|
|
72
|
+
*
|
|
73
|
+
* Non-snapshot events are returned unchanged. Small data URLs and non-data
|
|
74
|
+
* URLs are preserved verbatim. The original event is never mutated.
|
|
75
|
+
*/
|
|
76
|
+
export function applyDataUrlGuard(event, maxBytes) {
|
|
77
|
+
if (event.type === EventType.FullSnapshot) {
|
|
78
|
+
const data = event.data;
|
|
79
|
+
if (!isObject(data) || !isObject(data.node))
|
|
80
|
+
return event;
|
|
81
|
+
return {
|
|
82
|
+
...event,
|
|
83
|
+
data: {
|
|
84
|
+
...data,
|
|
85
|
+
node: walkAndGuardNode(data.node, maxBytes),
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (event.type === EventType.IncrementalSnapshot) {
|
|
90
|
+
const data = event.data;
|
|
91
|
+
if (!isObject(data) || data.source !== IncrementalSource.Mutation)
|
|
92
|
+
return event;
|
|
93
|
+
if (!Array.isArray(data.adds) || data.adds.length === 0)
|
|
94
|
+
return event;
|
|
95
|
+
const adds = data.adds.map((entry) => {
|
|
96
|
+
if (!isObject(entry) || !isObject(entry.node))
|
|
97
|
+
return entry;
|
|
98
|
+
return { ...entry, node: walkAndGuardNode(entry.node, maxBytes) };
|
|
99
|
+
});
|
|
100
|
+
return {
|
|
101
|
+
...event,
|
|
102
|
+
data: { ...data, adds },
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return event;
|
|
106
|
+
}
|
|
107
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
108
|
+
// Event-size guard
|
|
109
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
110
|
+
/**
|
|
111
|
+
* Drops events whose JSON-stringified size exceeds `maxBytes`.
|
|
112
|
+
*
|
|
113
|
+
* When dropping, the guard emits a sibling `tracelane.event.dropped` custom
|
|
114
|
+
* event via `onDrop` (so the consumer can forward it). The dropped breadcrumb
|
|
115
|
+
* carries `{ type, ts, size }` — enough for postmortem reasoning without
|
|
116
|
+
* leaking the dropped event's payload. Returns `null` when the input is
|
|
117
|
+
* dropped, otherwise returns the input unchanged.
|
|
118
|
+
*
|
|
119
|
+
* Size is measured via `JSON.stringify().length` (UTF-16 code units), which
|
|
120
|
+
* is a close-enough proxy for transport bytes at this layer.
|
|
121
|
+
*/
|
|
122
|
+
export function applyEventSizeGuard(event, maxBytes, onDrop) {
|
|
123
|
+
let serialized;
|
|
124
|
+
try {
|
|
125
|
+
serialized = JSON.stringify(event);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// Circular reference or BigInt — defensively drop. Same breadcrumb shape.
|
|
129
|
+
onDrop({
|
|
130
|
+
type: EventType.Custom,
|
|
131
|
+
data: {
|
|
132
|
+
tag: 'tracelane.event.dropped',
|
|
133
|
+
payload: { type: event.type, ts: event.timestamp, size: -1 },
|
|
134
|
+
},
|
|
135
|
+
timestamp: event.timestamp,
|
|
136
|
+
});
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
if (serialized.length <= maxBytes)
|
|
140
|
+
return event;
|
|
141
|
+
onDrop({
|
|
142
|
+
type: EventType.Custom,
|
|
143
|
+
data: {
|
|
144
|
+
tag: 'tracelane.event.dropped',
|
|
145
|
+
payload: { type: event.type, ts: event.timestamp, size: serialized.length },
|
|
146
|
+
},
|
|
147
|
+
timestamp: event.timestamp,
|
|
148
|
+
});
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
152
|
+
// Mutation guard
|
|
153
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
154
|
+
/**
|
|
155
|
+
* Counts the mutations in an IncrementalSnapshot.Mutation batch. Sums
|
|
156
|
+
* texts + attributes + removes + adds. Non-mutation events return 0.
|
|
157
|
+
*/
|
|
158
|
+
function countMutationsInEvent(event) {
|
|
159
|
+
if (event.type !== EventType.IncrementalSnapshot)
|
|
160
|
+
return 0;
|
|
161
|
+
const data = event.data;
|
|
162
|
+
if (!isObject(data) || data.source !== IncrementalSource.Mutation)
|
|
163
|
+
return 0;
|
|
164
|
+
return ((Array.isArray(data.texts) ? data.texts.length : 0) +
|
|
165
|
+
(Array.isArray(data.attributes) ? data.attributes.length : 0) +
|
|
166
|
+
(Array.isArray(data.removes) ? data.removes.length : 0) +
|
|
167
|
+
(Array.isArray(data.adds) ? data.adds.length : 0));
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Wraps an `emit` callback with the cumulative + per-batch mutation counters.
|
|
171
|
+
* Returns a new emit function with the same signature; the wrapper is
|
|
172
|
+
* stateful (closure-captured counters).
|
|
173
|
+
*/
|
|
174
|
+
export function applyMutationGuard(emit, hooks) {
|
|
175
|
+
let cumulative = 0;
|
|
176
|
+
let limitReached = false;
|
|
177
|
+
return (event, isCheckout) => {
|
|
178
|
+
if (limitReached)
|
|
179
|
+
return;
|
|
180
|
+
const batchCount = countMutationsInEvent(event);
|
|
181
|
+
if (batchCount > hooks.softWarnAt) {
|
|
182
|
+
const warn = {
|
|
183
|
+
type: EventType.Custom,
|
|
184
|
+
data: {
|
|
185
|
+
tag: 'tracelane.mutation.warn',
|
|
186
|
+
payload: { count: batchCount, batchTs: event.timestamp },
|
|
187
|
+
},
|
|
188
|
+
timestamp: event.timestamp,
|
|
189
|
+
};
|
|
190
|
+
hooks.emitWarn(warn);
|
|
191
|
+
}
|
|
192
|
+
cumulative += batchCount;
|
|
193
|
+
if (cumulative > hooks.hardLimit) {
|
|
194
|
+
limitReached = true;
|
|
195
|
+
const limitEvent = {
|
|
196
|
+
type: EventType.Custom,
|
|
197
|
+
data: {
|
|
198
|
+
tag: 'tracelane.mutation.limit',
|
|
199
|
+
payload: { totalCount: cumulative },
|
|
200
|
+
},
|
|
201
|
+
timestamp: event.timestamp,
|
|
202
|
+
};
|
|
203
|
+
hooks.emitLimit(limitEvent);
|
|
204
|
+
hooks.onLimit?.();
|
|
205
|
+
// Drop the offending event itself — the limit breadcrumb is the final
|
|
206
|
+
// signal forwarded for this recording.
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
emit(event, isCheckout);
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=guards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guards.js","sourceRoot":"","sources":["../../src/throttling/guards.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/throttling/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAC9C,YAAY,EAAE,0BAA0B,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Public barrel for the throttling module.
|
|
2
|
+
//
|
|
3
|
+
// Locked surface per IMPLEMENTATION_PLAN.md Public API contract
|
|
4
|
+
// (lines 695-696): the `LARGE_DOM_DEFAULTS` constant plus the
|
|
5
|
+
// `applyLargeDomGuards` function. The guard implementations
|
|
6
|
+
// (`applyDataUrlGuard`, `applyEventSizeGuard`, `applyMutationGuard`) and the
|
|
7
|
+
// SVG placeholder constant stay internal — they're composed by
|
|
8
|
+
// `applyLargeDomGuards` and not intended for direct consumption.
|
|
9
|
+
export { LARGE_DOM_DEFAULTS } from './defaults';
|
|
10
|
+
export { applyLargeDomGuards } from './apply';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/throttling/index.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,EAAE;AACF,gEAAgE;AAChE,8DAA8D;AAC9D,4DAA4D;AAC5D,6EAA6E;AAC7E,+DAA+D;AAC/D,iEAAiE;AAEjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEhD,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cubenest/rrweb-core",
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
|
+
"description": "Shared rrweb-based capture substrate for tracelane and peek.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" }
|
|
11
|
+
},
|
|
12
|
+
"files": ["dist", "NOTICE", "README.md"],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc -p tsconfig.json",
|
|
15
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
16
|
+
"test": "vitest run --passWithNoTests"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@posthog/rrweb": "0.0.34",
|
|
20
|
+
"@posthog/rrweb-types": "0.0.24",
|
|
21
|
+
"@rrweb/rrweb-plugin-console-record": "2.0.0-alpha.20",
|
|
22
|
+
"fflate": "^0.8.2"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"jsdom": "^25.0.1",
|
|
26
|
+
"fake-indexeddb": "^6.0.0"
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": { "access": "public", "provenance": false },
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/Cubenest/rrweb-stack.git",
|
|
32
|
+
"directory": "packages/rrweb-core"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://github.com/Cubenest/rrweb-stack/tree/main/packages/rrweb-core#readme"
|
|
35
|
+
}
|