@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
package/NOTICE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
rrweb-stack
|
|
2
|
+
Copyright 2026 Cubenest
|
|
3
|
+
|
|
4
|
+
This product includes software developed by:
|
|
5
|
+
- PostHog (https://posthog.com) — vendored rrweb fork:
|
|
6
|
+
@posthog/rrweb 0.0.34 (MIT License)
|
|
7
|
+
@posthog/rrweb-types 0.0.24 (MIT License)
|
|
8
|
+
Forked from upstream rrweb-io/rrweb 2.0.0-alpha.17.
|
|
9
|
+
- rrweb-io (https://github.com/rrweb-io/rrweb) — upstream rrweb plugin:
|
|
10
|
+
@rrweb/rrweb-plugin-console-record 2.0.0-alpha.20 (MIT License)
|
|
11
|
+
Used to provide getRecordConsolePlugin because the PostHog fork at
|
|
12
|
+
0.0.34 doesn't bundle it (it shipped separately since upstream 2.x).
|
|
13
|
+
|
|
14
|
+
This product is licensed under the Apache License, Version 2.0.
|
|
15
|
+
See the LICENSE file for details.
|
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# @cubenest/rrweb-core
|
|
2
|
+
|
|
3
|
+
Shared rrweb-based capture substrate. Used by [`tracelane`](https://github.com/Cubenest/rrweb-stack/tree/main/packages/tracelane-core) and [`peek`](https://github.com/Cubenest/rrweb-stack/tree/main/packages/peek-extension). Not generally intended for direct consumption — depend on a product package instead.
|
|
4
|
+
|
|
5
|
+
## What's in here
|
|
6
|
+
|
|
7
|
+
See [ADR-0002](https://github.com/Cubenest/rrweb-stack/blob/main/prds/adrs/0002-rrweb-posthog-fork-substrate.md) for the full inventory.
|
|
8
|
+
|
|
9
|
+
- Vendored PostHog rrweb fork (`@posthog/rrweb@0.0.34`)
|
|
10
|
+
- PII masking primitives (selectors + regex bank + body/header redaction)
|
|
11
|
+
- Large-DOM throttling defaults (mutation guard, data-URL guard, single-event size cap)
|
|
12
|
+
- Shadow DOM adapter
|
|
13
|
+
- Screenshot fallback interface
|
|
14
|
+
- Network capture abstraction (CDP and `chrome.webRequest` implementations)
|
|
15
|
+
- Console capture
|
|
16
|
+
- Compression helpers (`fflate`)
|
|
17
|
+
- IndexedDB persistence helper
|
|
18
|
+
- Compatibility matrix
|
|
19
|
+
|
|
20
|
+
## Versioning
|
|
21
|
+
|
|
22
|
+
Independent semver. Breaking changes are coordinated across `tracelane` and `peek` releases per [ADR-0002](https://github.com/Cubenest/rrweb-stack/blob/main/prds/adrs/0002-rrweb-posthog-fork-substrate.md).
|
|
23
|
+
|
|
24
|
+
## License
|
|
25
|
+
|
|
26
|
+
Apache 2.0. Vendored rrweb fork remains MIT-licensed; see NOTICE.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single entry in the compatibility matrix. Documents how
|
|
3
|
+
* rrweb-based capture behaves on a specific target application or
|
|
4
|
+
* page kind.
|
|
5
|
+
*
|
|
6
|
+
* Sources: PostHog's session-replay compatibility docs
|
|
7
|
+
* (https://posthog.com/docs/session-replay/troubleshooting),
|
|
8
|
+
* rrweb-io issue tracker, and our own first-party observations as
|
|
9
|
+
* they accumulate. Updates go via a Changesets PR.
|
|
10
|
+
*/
|
|
11
|
+
export interface CompatEntry {
|
|
12
|
+
/**
|
|
13
|
+
* Identifying URL or pattern. Doesn't need to be a real URL — can
|
|
14
|
+
* be a category like 'youtube.com' or 'github.com/*'.
|
|
15
|
+
*/
|
|
16
|
+
readonly url: string;
|
|
17
|
+
/** Coarse category — pick the most representative. */
|
|
18
|
+
readonly category: 'developer-tools' | 'spa-framework' | 'rich-text-editor' | 'video-streaming' | 'canvas-webgl' | 'chat-messaging' | 'email-webmail' | 'docs-collaboration' | 'social-feed' | 'commerce' | 'auth-flow' | 'iframe-heavy' | 'pdf-viewer' | 'other';
|
|
19
|
+
/** Coarse status — set by the maintainer based on the latest evidence. */
|
|
20
|
+
readonly status: 'good' | 'caveats' | 'poor';
|
|
21
|
+
/**
|
|
22
|
+
* Short, factual description of what works / doesn't / mitigations.
|
|
23
|
+
* Keep < 240 chars per entry.
|
|
24
|
+
*/
|
|
25
|
+
readonly notes: string;
|
|
26
|
+
/** Last verified date (YYYY-MM-DD). */
|
|
27
|
+
readonly lastVerified: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Seed entries — 5 known-good, 5 known-caveats/poor. Sourced from
|
|
31
|
+
* PostHog's published session-replay docs and the rrweb-io issue
|
|
32
|
+
* tracker. Each entry is defensible against external documentation;
|
|
33
|
+
* future updates go through Changesets PRs with a link to the
|
|
34
|
+
* supporting evidence.
|
|
35
|
+
*/
|
|
36
|
+
export declare const COMPATIBILITY_MATRIX: readonly CompatEntry[];
|
|
37
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compat/index.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;GASG;AACH,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,QAAQ,CAAC,QAAQ,EACb,iBAAiB,GACjB,eAAe,GACf,kBAAkB,GAClB,iBAAiB,GACjB,cAAc,GACd,gBAAgB,GAChB,eAAe,GACf,oBAAoB,GACpB,aAAa,GACb,UAAU,GACV,WAAW,GACX,cAAc,GACd,YAAY,GACZ,OAAO,CAAC;IACZ,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;IAC7C;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,uCAAuC;IACvC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,EAAE,SAAS,WAAW,EAkFrD,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Compatibility matrix — Task 1.11.
|
|
2
|
+
//
|
|
3
|
+
// Locked surface per IMPLEMENTATION_PLAN.md Public API contract (line
|
|
4
|
+
// 732):
|
|
5
|
+
//
|
|
6
|
+
// export { COMPATIBILITY_MATRIX, type CompatEntry } from './compat';
|
|
7
|
+
//
|
|
8
|
+
// One frozen array of entries + one entry type. The TypeScript source
|
|
9
|
+
// is authoritative; `packages/rrweb-core/COMPATIBILITY.md` mirrors it
|
|
10
|
+
// for human readers and a drift-free test guards both stay in sync.
|
|
11
|
+
//
|
|
12
|
+
// ADR-0002: "A published list of target applications that are known to
|
|
13
|
+
// record cleanly, record with caveats, or record poorly. Both products
|
|
14
|
+
// inherit rrweb's limits; both products inherit the matrix. Updating it
|
|
15
|
+
// once benefits both."
|
|
16
|
+
/**
|
|
17
|
+
* Seed entries — 5 known-good, 5 known-caveats/poor. Sourced from
|
|
18
|
+
* PostHog's published session-replay docs and the rrweb-io issue
|
|
19
|
+
* tracker. Each entry is defensible against external documentation;
|
|
20
|
+
* future updates go through Changesets PRs with a link to the
|
|
21
|
+
* supporting evidence.
|
|
22
|
+
*/
|
|
23
|
+
export const COMPATIBILITY_MATRIX = Object.freeze([
|
|
24
|
+
// ─── Good (records cleanly) ───────────────────────────────────────
|
|
25
|
+
Object.freeze({
|
|
26
|
+
url: 'posthog.com',
|
|
27
|
+
category: 'developer-tools',
|
|
28
|
+
status: 'good',
|
|
29
|
+
notes: 'Recorded by PostHog themselves in production; canonical baseline for the fork. DOM-only, no canvas, no closed shadow roots in critical paths.',
|
|
30
|
+
lastVerified: '2026-05-26',
|
|
31
|
+
}),
|
|
32
|
+
Object.freeze({
|
|
33
|
+
url: 'github.com',
|
|
34
|
+
category: 'developer-tools',
|
|
35
|
+
status: 'good',
|
|
36
|
+
notes: 'DOM-heavy SPA, no embedded canvas/WebGL; recording captures full session including code-review UIs and PR diff views.',
|
|
37
|
+
lastVerified: '2026-05-26',
|
|
38
|
+
}),
|
|
39
|
+
Object.freeze({
|
|
40
|
+
url: 'cypress.io',
|
|
41
|
+
category: 'developer-tools',
|
|
42
|
+
status: 'good',
|
|
43
|
+
notes: 'Static marketing pages + dashboards; clean capture, no special handling required.',
|
|
44
|
+
lastVerified: '2026-05-26',
|
|
45
|
+
}),
|
|
46
|
+
Object.freeze({
|
|
47
|
+
url: 'vercel.com',
|
|
48
|
+
category: 'developer-tools',
|
|
49
|
+
status: 'good',
|
|
50
|
+
notes: 'Dashboard SPA; Next.js + RSC; clean capture, streaming HTML islands settle predictably.',
|
|
51
|
+
lastVerified: '2026-05-26',
|
|
52
|
+
}),
|
|
53
|
+
Object.freeze({
|
|
54
|
+
url: 'linear.app',
|
|
55
|
+
category: 'spa-framework',
|
|
56
|
+
status: 'good',
|
|
57
|
+
notes: "Heavy React SPA with high mutation rates; rrweb's mutation guard (10k cap) recommended to bound replay size.",
|
|
58
|
+
lastVerified: '2026-05-26',
|
|
59
|
+
}),
|
|
60
|
+
// ─── Caveats / poor (records with limits) ─────────────────────────
|
|
61
|
+
Object.freeze({
|
|
62
|
+
url: 'figma.com',
|
|
63
|
+
category: 'canvas-webgl',
|
|
64
|
+
status: 'poor',
|
|
65
|
+
notes: 'Canvas-based editor; rrweb sees the parent DOM but not the canvas contents. Use screenshot fallback for the canvas region.',
|
|
66
|
+
lastVerified: '2026-05-26',
|
|
67
|
+
}),
|
|
68
|
+
Object.freeze({
|
|
69
|
+
url: 'youtube.com',
|
|
70
|
+
category: 'video-streaming',
|
|
71
|
+
status: 'caveats',
|
|
72
|
+
notes: '<video> tags record but media playback is not part of the snapshot; replay shows poster frames + UI mutations only.',
|
|
73
|
+
lastVerified: '2026-05-26',
|
|
74
|
+
}),
|
|
75
|
+
Object.freeze({
|
|
76
|
+
url: 'gmail.com',
|
|
77
|
+
category: 'email-webmail',
|
|
78
|
+
status: 'caveats',
|
|
79
|
+
notes: 'Heavy custom elements + shadow DOM; closed shadow roots in MAIN world are unreachable. ISOLATED-world relay via chrome.dom.openOrClosedShadowRoot (peek path) closes the gap.',
|
|
80
|
+
lastVerified: '2026-05-26',
|
|
81
|
+
}),
|
|
82
|
+
Object.freeze({
|
|
83
|
+
url: 'notion.so',
|
|
84
|
+
category: 'rich-text-editor',
|
|
85
|
+
status: 'caveats',
|
|
86
|
+
notes: 'Slate-based editor with high mutation rates; rely on the mutation guard (10k cap) to avoid runaway recordings.',
|
|
87
|
+
lastVerified: '2026-05-26',
|
|
88
|
+
}),
|
|
89
|
+
Object.freeze({
|
|
90
|
+
url: 'docs.google.com',
|
|
91
|
+
category: 'docs-collaboration',
|
|
92
|
+
status: 'poor',
|
|
93
|
+
notes: 'Custom rendering layer (not real DOM text nodes); rrweb captures empty containers. Use screenshot fallback for content. Selection state not recoverable.',
|
|
94
|
+
lastVerified: '2026-05-26',
|
|
95
|
+
}),
|
|
96
|
+
]);
|
|
97
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/compat/index.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,EAAE;AACF,sEAAsE;AACtE,QAAQ;AACR,EAAE;AACF,uEAAuE;AACvE,EAAE;AACF,sEAAsE;AACtE,sEAAsE;AACtE,oEAAoE;AACpE,EAAE;AACF,uEAAuE;AACvE,uEAAuE;AACvE,wEAAwE;AACxE,uBAAuB;AA6CvB;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAA2B,MAAM,CAAC,MAAM,CAAC;IACxE,qEAAqE;IACrE,MAAM,CAAC,MAAM,CAAC;QACZ,GAAG,EAAE,aAAa;QAClB,QAAQ,EAAE,iBAAiB;QAC3B,MAAM,EAAE,MAAM;QACd,KAAK,EACH,+IAA+I;QACjJ,YAAY,EAAE,YAAY;KAC3B,CAAC;IACF,MAAM,CAAC,MAAM,CAAC;QACZ,GAAG,EAAE,YAAY;QACjB,QAAQ,EAAE,iBAAiB;QAC3B,MAAM,EAAE,MAAM;QACd,KAAK,EACH,uHAAuH;QACzH,YAAY,EAAE,YAAY;KAC3B,CAAC;IACF,MAAM,CAAC,MAAM,CAAC;QACZ,GAAG,EAAE,YAAY;QACjB,QAAQ,EAAE,iBAAiB;QAC3B,MAAM,EAAE,MAAM;QACd,KAAK,EAAE,mFAAmF;QAC1F,YAAY,EAAE,YAAY;KAC3B,CAAC;IACF,MAAM,CAAC,MAAM,CAAC;QACZ,GAAG,EAAE,YAAY;QACjB,QAAQ,EAAE,iBAAiB;QAC3B,MAAM,EAAE,MAAM;QACd,KAAK,EACH,yFAAyF;QAC3F,YAAY,EAAE,YAAY;KAC3B,CAAC;IACF,MAAM,CAAC,MAAM,CAAC;QACZ,GAAG,EAAE,YAAY;QACjB,QAAQ,EAAE,eAAe;QACzB,MAAM,EAAE,MAAM;QACd,KAAK,EACH,8GAA8G;QAChH,YAAY,EAAE,YAAY;KAC3B,CAAC;IACF,qEAAqE;IACrE,MAAM,CAAC,MAAM,CAAC;QACZ,GAAG,EAAE,WAAW;QAChB,QAAQ,EAAE,cAAc;QACxB,MAAM,EAAE,MAAM;QACd,KAAK,EACH,4HAA4H;QAC9H,YAAY,EAAE,YAAY;KAC3B,CAAC;IACF,MAAM,CAAC,MAAM,CAAC;QACZ,GAAG,EAAE,aAAa;QAClB,QAAQ,EAAE,iBAAiB;QAC3B,MAAM,EAAE,SAAS;QACjB,KAAK,EACH,qHAAqH;QACvH,YAAY,EAAE,YAAY;KAC3B,CAAC;IACF,MAAM,CAAC,MAAM,CAAC;QACZ,GAAG,EAAE,WAAW;QAChB,QAAQ,EAAE,eAAe;QACzB,MAAM,EAAE,SAAS;QACjB,KAAK,EACH,+KAA+K;QACjL,YAAY,EAAE,YAAY;KAC3B,CAAC;IACF,MAAM,CAAC,MAAM,CAAC;QACZ,GAAG,EAAE,WAAW;QAChB,QAAQ,EAAE,kBAAkB;QAC5B,MAAM,EAAE,SAAS;QACjB,KAAK,EACH,gHAAgH;QAClH,YAAY,EAAE,YAAY;KAC3B,CAAC;IACF,MAAM,CAAC,MAAM,CAAC;QACZ,GAAG,EAAE,iBAAiB;QACtB,QAAQ,EAAE,oBAAoB;QAC9B,MAAM,EAAE,MAAM;QACd,KAAK,EACH,0JAA0J;QAC5J,YAAY,EAAE,YAAY;KAC3B,CAAC;CACH,CAAC,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { eventWithTime } from '../rrweb';
|
|
2
|
+
/**
|
|
3
|
+
* Per-batch gzip compression of an rrweb event array.
|
|
4
|
+
*
|
|
5
|
+
* Always operates on the entire array — never per-event.
|
|
6
|
+
*
|
|
7
|
+
* @param events the rrweb event array to compress
|
|
8
|
+
* @returns gzipped bytes; suitable for base64-embedding in a self-contained
|
|
9
|
+
* HTML report or persisting to IndexedDB
|
|
10
|
+
* @throws {TypeError} if `events` is not an array
|
|
11
|
+
*/
|
|
12
|
+
export declare function compress(events: eventWithTime[]): Uint8Array;
|
|
13
|
+
/**
|
|
14
|
+
* Inverse of `compress`. Decompresses gzipped bytes back into the original
|
|
15
|
+
* event array.
|
|
16
|
+
*
|
|
17
|
+
* @param bytes gzipped output from `compress`
|
|
18
|
+
* @returns the original event array
|
|
19
|
+
* @throws {TypeError} if `bytes` is not a Uint8Array, if the gzip frame is
|
|
20
|
+
* malformed (fflate's own error), or if the inflated
|
|
21
|
+
* payload does not deserialize to a JSON array
|
|
22
|
+
*/
|
|
23
|
+
export declare function decompress(bytes: Uint8Array): eventWithTime[];
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compression/index.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,UAAU,CAO5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,aAAa,EAAE,CAW7D"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Compression helpers — Task 1.9.
|
|
2
|
+
//
|
|
3
|
+
// Two thin functions, backed by fflate's synchronous gzip APIs. Per ADR-0002
|
|
4
|
+
// + PostHog's own guidance, the shared core does NOT register a packFn for
|
|
5
|
+
// per-event compression — per-event compression is inefficient compared to
|
|
6
|
+
// per-batch. These helpers always operate on an entire event array at once.
|
|
7
|
+
//
|
|
8
|
+
// Use cases:
|
|
9
|
+
// • Embedding a session into a self-contained HTML report (base64 the
|
|
10
|
+
// gzipped bytes, drop into a <script> tag).
|
|
11
|
+
// • Persisting batched chunks to IndexedDB before upload.
|
|
12
|
+
// • Sending batched payloads to a backend.
|
|
13
|
+
//
|
|
14
|
+
// Intentional non-features (YAGNI per ADR-0002):
|
|
15
|
+
// • No streaming / async APIs — synchronous gzip is fine for batch sizes
|
|
16
|
+
// we care about (under ~25 MB by report-size guard).
|
|
17
|
+
// • No options block — level=6 is fflate's default and a sane sweet spot
|
|
18
|
+
// between speed and ratio for repetitive rrweb payloads.
|
|
19
|
+
// • No custom dictionaries.
|
|
20
|
+
import { gunzipSync, gzipSync, strFromU8, strToU8 } from 'fflate';
|
|
21
|
+
/**
|
|
22
|
+
* Per-batch gzip compression of an rrweb event array.
|
|
23
|
+
*
|
|
24
|
+
* Always operates on the entire array — never per-event.
|
|
25
|
+
*
|
|
26
|
+
* @param events the rrweb event array to compress
|
|
27
|
+
* @returns gzipped bytes; suitable for base64-embedding in a self-contained
|
|
28
|
+
* HTML report or persisting to IndexedDB
|
|
29
|
+
* @throws {TypeError} if `events` is not an array
|
|
30
|
+
*/
|
|
31
|
+
export function compress(events) {
|
|
32
|
+
if (!Array.isArray(events)) {
|
|
33
|
+
throw new TypeError('compress: events must be an array');
|
|
34
|
+
}
|
|
35
|
+
const json = JSON.stringify(events);
|
|
36
|
+
const bytes = strToU8(json);
|
|
37
|
+
return gzipSync(bytes, { level: 6 });
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Inverse of `compress`. Decompresses gzipped bytes back into the original
|
|
41
|
+
* event array.
|
|
42
|
+
*
|
|
43
|
+
* @param bytes gzipped output from `compress`
|
|
44
|
+
* @returns the original event array
|
|
45
|
+
* @throws {TypeError} if `bytes` is not a Uint8Array, if the gzip frame is
|
|
46
|
+
* malformed (fflate's own error), or if the inflated
|
|
47
|
+
* payload does not deserialize to a JSON array
|
|
48
|
+
*/
|
|
49
|
+
export function decompress(bytes) {
|
|
50
|
+
if (!(bytes instanceof Uint8Array)) {
|
|
51
|
+
throw new TypeError('decompress: bytes must be a Uint8Array');
|
|
52
|
+
}
|
|
53
|
+
const decompressed = gunzipSync(bytes);
|
|
54
|
+
const json = strFromU8(decompressed);
|
|
55
|
+
const parsed = JSON.parse(json);
|
|
56
|
+
if (!Array.isArray(parsed)) {
|
|
57
|
+
throw new TypeError('decompress: payload did not deserialize to an array');
|
|
58
|
+
}
|
|
59
|
+
return parsed;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/compression/index.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,2EAA2E;AAC3E,4EAA4E;AAC5E,EAAE;AACF,aAAa;AACb,wEAAwE;AACxE,gDAAgD;AAChD,4DAA4D;AAC5D,6CAA6C;AAC7C,EAAE;AACF,iDAAiD;AACjD,2EAA2E;AAC3E,yDAAyD;AACzD,2EAA2E;AAC3E,6DAA6D;AAC7D,8BAA8B;AAE9B,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAGlE;;;;;;;;;GASG;AACH,MAAM,UAAU,QAAQ,CAAC,MAAuB;IAC9C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,SAAS,CAAC,mCAAmC,CAAC,CAAC;IAC3D,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,OAAO,QAAQ,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;AACvC,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,UAAU,CAAC,KAAiB;IAC1C,IAAI,CAAC,CAAC,KAAK,YAAY,UAAU,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,SAAS,CAAC,wCAAwC,CAAC,CAAC;IAChE,CAAC;IACD,MAAM,YAAY,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,SAAS,CAAC,YAAY,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,SAAS,CAAC,qDAAqD,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,MAAyB,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { getRecordConsolePlugin } from '../rrweb';
|
|
2
|
+
import type { eventWithTime } from '../rrweb';
|
|
3
|
+
import type { ConsoleEvent, ConsoleLevel } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* Factory-time configuration for `createConsoleCaptureBuffer`.
|
|
6
|
+
*/
|
|
7
|
+
export interface ConsoleCaptureOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Console levels to capture. When omitted, the plugin's default level
|
|
10
|
+
* list is used (the full 19-level vocabulary documented on `ConsoleLevel`).
|
|
11
|
+
* Most consumers will narrow this to the `BasicConsoleLevel` quartet.
|
|
12
|
+
*/
|
|
13
|
+
level?: ConsoleLevel[];
|
|
14
|
+
/**
|
|
15
|
+
* Max number of plugin-side log records before the plugin emits a
|
|
16
|
+
* single threshold warning and stops invoking the callback. Forwarded
|
|
17
|
+
* to the plugin verbatim — this is NOT the same as `maxBuffered` (which
|
|
18
|
+
* is OUR ring-buffer cap, applied after the plugin emits).
|
|
19
|
+
* Default: 10_000.
|
|
20
|
+
*/
|
|
21
|
+
lengthThreshold?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Serializer knobs forwarded to the plugin's `stringify` step. Defaults
|
|
24
|
+
* favor short, shallow output — long string clip at 1000 chars, object
|
|
25
|
+
* keys clip at 100, depth clips at 1.
|
|
26
|
+
*/
|
|
27
|
+
stringifyOptions?: {
|
|
28
|
+
stringLengthLimit?: number;
|
|
29
|
+
numOfKeysLimit?: number;
|
|
30
|
+
depthOfLimit?: number;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Max number of `ConsoleEvent`s retained in the buffer. When `push()`
|
|
34
|
+
* would push beyond this cap, the OLDEST entries are dropped (FIFO).
|
|
35
|
+
* Default: 5000.
|
|
36
|
+
*/
|
|
37
|
+
maxBuffered?: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The public buffer surface. Consumers register `plugin` with the rrweb
|
|
41
|
+
* recorder AND forward every emitted rrweb event into `push()`:
|
|
42
|
+
*
|
|
43
|
+
* const buf = createConsoleCaptureBuffer();
|
|
44
|
+
* record({
|
|
45
|
+
* emit: (event) => { buf.push(event); ...your other sinks... },
|
|
46
|
+
* plugins: [buf.plugin],
|
|
47
|
+
* });
|
|
48
|
+
*
|
|
49
|
+
* The split (plugin registration ↔ event forwarding) is intentional:
|
|
50
|
+
* rrweb owns the recording lifecycle, we own the normalized buffer.
|
|
51
|
+
*/
|
|
52
|
+
export interface ConsoleCaptureBuffer {
|
|
53
|
+
/**
|
|
54
|
+
* The rrweb plugin instance to pass into `record({ plugins: [...] })`.
|
|
55
|
+
* Same shape as a direct `getRecordConsolePlugin(...)` call — the buffer
|
|
56
|
+
* wrapper does not re-shape it.
|
|
57
|
+
*/
|
|
58
|
+
readonly plugin: ReturnType<typeof getRecordConsolePlugin>;
|
|
59
|
+
/**
|
|
60
|
+
* Feed an rrweb event into the buffer. Events of type
|
|
61
|
+
* `EventType.Plugin` (6) with `data.plugin === 'rrweb/console@1'` are
|
|
62
|
+
* normalized into `ConsoleEvent` and pushed onto the ring buffer; all
|
|
63
|
+
* other events are silently ignored (this method is safe to wire
|
|
64
|
+
* unconditionally inside the rrweb `emit` callback).
|
|
65
|
+
*
|
|
66
|
+
* Malformed plugin events (missing fields, wrong types) are silently
|
|
67
|
+
* dropped — see "Recursion guard" in the module header for why we
|
|
68
|
+
* never throw or log from this path.
|
|
69
|
+
*/
|
|
70
|
+
push(event: eventWithTime): void;
|
|
71
|
+
/**
|
|
72
|
+
* Returns and EMPTIES the buffer. Used by P1 on test-fail to ship the
|
|
73
|
+
* captured console alongside the rrweb event stream, and by P2 on MCP
|
|
74
|
+
* tool invocation to surface recent logs to the model.
|
|
75
|
+
*/
|
|
76
|
+
drain(): ConsoleEvent[];
|
|
77
|
+
/**
|
|
78
|
+
* Returns a read-only snapshot of the buffer WITHOUT emptying it. Used
|
|
79
|
+
* by surfaces that want to render the current state without disturbing
|
|
80
|
+
* the buffer (e.g. live debug overlays).
|
|
81
|
+
*/
|
|
82
|
+
peek(): readonly ConsoleEvent[];
|
|
83
|
+
/**
|
|
84
|
+
* Current number of `ConsoleEvent`s retained in the buffer.
|
|
85
|
+
*/
|
|
86
|
+
size(): number;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Create a console capture buffer.
|
|
90
|
+
*
|
|
91
|
+
* The returned `plugin` field is the rrweb plugin instance — consumers
|
|
92
|
+
* pass it into `record({ plugins: [...] })`. The returned `push`/`drain`/
|
|
93
|
+
* `peek`/`size` methods operate on the bounded ring buffer.
|
|
94
|
+
*
|
|
95
|
+
* @see ConsoleCaptureOptions for tuning knobs.
|
|
96
|
+
* @see ConsoleCaptureBuffer for the runtime API.
|
|
97
|
+
*/
|
|
98
|
+
export declare function createConsoleCaptureBuffer(options?: ConsoleCaptureOptions): ConsoleCaptureBuffer;
|
|
99
|
+
//# sourceMappingURL=buffer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buffer.d.ts","sourceRoot":"","sources":["../../src/console/buffer.ts"],"names":[],"mappings":"AA8CA,OAAO,EAAa,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AA+B1D;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;IACvB;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE;QACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC;IAC3D;;;;;;;;;;OAUG;IACH,IAAI,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IACjC;;;;OAIG;IACH,KAAK,IAAI,YAAY,EAAE,CAAC;IACxB;;;;OAIG;IACH,IAAI,IAAI,SAAS,YAAY,EAAE,CAAC;IAChC;;OAEG;IACH,IAAI,IAAI,MAAM,CAAC;CAChB;AAyBD;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,GAAE,qBAA0B,GAClC,oBAAoB,CAuEtB"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// Console capture buffer — Task 1.8.
|
|
2
|
+
//
|
|
3
|
+
// Wraps the upstream `@rrweb/rrweb-plugin-console-record` (re-exported via
|
|
4
|
+
// `../rrweb` per Task 1.2) with two concerns the plugin doesn't own:
|
|
5
|
+
//
|
|
6
|
+
// 1. A bounded in-memory buffer that consumers can `drain()` on demand
|
|
7
|
+
// (P1: dump-on-fail; P2: dump-on-MCP-tool-call). The plugin only
|
|
8
|
+
// emits events into rrweb's event stream — it doesn't buffer them
|
|
9
|
+
// itself.
|
|
10
|
+
//
|
|
11
|
+
// 2. A normalized event shape (`ConsoleEvent`) that hides the awkward
|
|
12
|
+
// double-`payload` naming in the raw rrweb plugin event
|
|
13
|
+
// (`event.data.payload.payload` is the args array).
|
|
14
|
+
//
|
|
15
|
+
// The plugin's emitted shape, verified against
|
|
16
|
+
// `@rrweb/rrweb-plugin-console-record@2.0.0-alpha.20`'s `dist/index.d.ts`
|
|
17
|
+
// and source (`initLogObserver` → `cb({ level, trace, payload })` →
|
|
18
|
+
// rrweb's `wrappedEmit({ type: 6, data: { plugin: 'rrweb/console@1',
|
|
19
|
+
// payload: <LogData> } })`), is:
|
|
20
|
+
//
|
|
21
|
+
// {
|
|
22
|
+
// type: 6 /* EventType.Plugin */,
|
|
23
|
+
// timestamp: number,
|
|
24
|
+
// data: {
|
|
25
|
+
// plugin: 'rrweb/console@1',
|
|
26
|
+
// payload: {
|
|
27
|
+
// level: LogLevel,
|
|
28
|
+
// trace: string[],
|
|
29
|
+
// payload: string[] /* the stringified args */
|
|
30
|
+
// }
|
|
31
|
+
// }
|
|
32
|
+
// }
|
|
33
|
+
//
|
|
34
|
+
// ── Recursion guard ────────────────────────────────────────────────────────
|
|
35
|
+
// The plugin patches `console.*` to call `cb(…)` AFTER invoking the
|
|
36
|
+
// original. If our buffer code itself called `console.*`, that would
|
|
37
|
+
// re-enter the patch and emit a synthetic event for every event we
|
|
38
|
+
// process — fast path to infinite recursion (the plugin has its own
|
|
39
|
+
// `inStack` guard but we should not rely on it, since we sit OUTSIDE
|
|
40
|
+
// the plugin in the rrweb event pipeline).
|
|
41
|
+
//
|
|
42
|
+
// Therefore: no `console.*` calls from this module. If `push()` sees a
|
|
43
|
+
// malformed plugin event (wrong shape, missing fields), it silently
|
|
44
|
+
// ignores it — better to lose one buggy event than risk a recursive
|
|
45
|
+
// flood from a defensive log line. Tests cover this contract.
|
|
46
|
+
import { EventType, getRecordConsolePlugin } from '../rrweb';
|
|
47
|
+
/** Vendor's plugin name string — emitted as `event.data.plugin`. */
|
|
48
|
+
const CONSOLE_PLUGIN_NAME = 'rrweb/console@1';
|
|
49
|
+
/** Default cap on retained events before FIFO eviction kicks in. */
|
|
50
|
+
const DEFAULT_MAX_BUFFERED = 5000;
|
|
51
|
+
/** Default per-argument string truncation cap (matches plugin default). */
|
|
52
|
+
const DEFAULT_STRING_LENGTH_LIMIT = 1000;
|
|
53
|
+
/** Default object-key count cap when serializing arguments. */
|
|
54
|
+
const DEFAULT_NUM_OF_KEYS_LIMIT = 100;
|
|
55
|
+
/**
|
|
56
|
+
* Default object-graph depth limit. Kept SHALLOW (1) because console.log
|
|
57
|
+
* is often called with whole DOM nodes or huge state objects, and a
|
|
58
|
+
* deeper serializer would inflate event size into the multi-MB range.
|
|
59
|
+
* Consumers that actually want deep object dumps can override.
|
|
60
|
+
*/
|
|
61
|
+
const DEFAULT_DEPTH_OF_LIMIT = 1;
|
|
62
|
+
/**
|
|
63
|
+
* Default plugin-level event cap (the plugin has its own `lengthThreshold`
|
|
64
|
+
* that emits a warning + stops calling cb once exceeded — defaults to 1000
|
|
65
|
+
* in the plugin, but we raise it slightly so the plugin's own cap doesn't
|
|
66
|
+
* bite before our buffer cap does. Set to 10_000 to match what the plan
|
|
67
|
+
* documents as the per-event size budget across the substrate.
|
|
68
|
+
*/
|
|
69
|
+
const DEFAULT_LENGTH_THRESHOLD = 10_000;
|
|
70
|
+
/**
|
|
71
|
+
* Type guard for the structural shape of a console-plugin event's payload.
|
|
72
|
+
* Returns true ONLY when `value` matches the LogData contract:
|
|
73
|
+
* `{ level: string, trace: string[], payload: string[] }`. Anything else
|
|
74
|
+
* is treated as malformed and dropped by `push()`.
|
|
75
|
+
*/
|
|
76
|
+
function isConsoleLogData(value) {
|
|
77
|
+
if (value === null || typeof value !== 'object')
|
|
78
|
+
return false;
|
|
79
|
+
const candidate = value;
|
|
80
|
+
if (typeof candidate.level !== 'string')
|
|
81
|
+
return false;
|
|
82
|
+
if (!Array.isArray(candidate.trace))
|
|
83
|
+
return false;
|
|
84
|
+
if (!Array.isArray(candidate.payload))
|
|
85
|
+
return false;
|
|
86
|
+
// All trace + payload entries must be strings (plugin enforces this; we
|
|
87
|
+
// double-check to keep the consumer-facing shape strict).
|
|
88
|
+
for (const t of candidate.trace)
|
|
89
|
+
if (typeof t !== 'string')
|
|
90
|
+
return false;
|
|
91
|
+
for (const p of candidate.payload)
|
|
92
|
+
if (typeof p !== 'string')
|
|
93
|
+
return false;
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Create a console capture buffer.
|
|
98
|
+
*
|
|
99
|
+
* The returned `plugin` field is the rrweb plugin instance — consumers
|
|
100
|
+
* pass it into `record({ plugins: [...] })`. The returned `push`/`drain`/
|
|
101
|
+
* `peek`/`size` methods operate on the bounded ring buffer.
|
|
102
|
+
*
|
|
103
|
+
* @see ConsoleCaptureOptions for tuning knobs.
|
|
104
|
+
* @see ConsoleCaptureBuffer for the runtime API.
|
|
105
|
+
*/
|
|
106
|
+
export function createConsoleCaptureBuffer(options = {}) {
|
|
107
|
+
const maxBuffered = options.maxBuffered ?? DEFAULT_MAX_BUFFERED;
|
|
108
|
+
const stringifyOptions = {
|
|
109
|
+
stringLengthLimit: options.stringifyOptions?.stringLengthLimit ?? DEFAULT_STRING_LENGTH_LIMIT,
|
|
110
|
+
numOfKeysLimit: options.stringifyOptions?.numOfKeysLimit ?? DEFAULT_NUM_OF_KEYS_LIMIT,
|
|
111
|
+
depthOfLimit: options.stringifyOptions?.depthOfLimit ?? DEFAULT_DEPTH_OF_LIMIT,
|
|
112
|
+
};
|
|
113
|
+
// Build the plugin. Only forward `level` when the caller specified it —
|
|
114
|
+
// otherwise the plugin's own default-level list applies (the full 19-
|
|
115
|
+
// level vocabulary), which is what consumers usually want.
|
|
116
|
+
const plugin = getRecordConsolePlugin({
|
|
117
|
+
...(options.level !== undefined ? { level: options.level } : {}),
|
|
118
|
+
lengthThreshold: options.lengthThreshold ?? DEFAULT_LENGTH_THRESHOLD,
|
|
119
|
+
stringifyOptions,
|
|
120
|
+
});
|
|
121
|
+
// Ring buffer. We use a plain array + shift() for eviction; for the
|
|
122
|
+
// sizes we expect (single-digit thousands), the O(n) shift is fine and
|
|
123
|
+
// the code is dramatically simpler than a circular index.
|
|
124
|
+
const buffer = [];
|
|
125
|
+
function push(event) {
|
|
126
|
+
// Filter: only plugin events from the console-record plugin reach the
|
|
127
|
+
// buffer. Everything else (DOM mutations, mouse moves, snapshots) is
|
|
128
|
+
// ignored — consumers can forward the full event stream into push()
|
|
129
|
+
// without filtering upstream.
|
|
130
|
+
if (event.type !== EventType.Plugin)
|
|
131
|
+
return;
|
|
132
|
+
const data = event.data;
|
|
133
|
+
if (data === null || typeof data !== 'object')
|
|
134
|
+
return;
|
|
135
|
+
const { plugin: pluginName, payload } = data;
|
|
136
|
+
if (pluginName !== CONSOLE_PLUGIN_NAME)
|
|
137
|
+
return;
|
|
138
|
+
if (!isConsoleLogData(payload))
|
|
139
|
+
return;
|
|
140
|
+
// Normalize: drop trace if empty (no signal in an empty array), and
|
|
141
|
+
// freeze the args array so consumers can't mutate buffered state.
|
|
142
|
+
const normalized = {
|
|
143
|
+
ts: event.timestamp,
|
|
144
|
+
level: payload.level,
|
|
145
|
+
args: [...payload.payload],
|
|
146
|
+
...(payload.trace.length > 0 ? { trace: [...payload.trace] } : {}),
|
|
147
|
+
};
|
|
148
|
+
buffer.push(normalized);
|
|
149
|
+
// FIFO eviction. The buffer can be over capacity by exactly one entry
|
|
150
|
+
// after push, so a single shift suffices — but keep the loop in case
|
|
151
|
+
// a future caller mutates maxBuffered downward at runtime.
|
|
152
|
+
while (buffer.length > maxBuffered) {
|
|
153
|
+
buffer.shift();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function drain() {
|
|
157
|
+
const out = buffer.slice();
|
|
158
|
+
buffer.length = 0;
|
|
159
|
+
return out;
|
|
160
|
+
}
|
|
161
|
+
function peek() {
|
|
162
|
+
return buffer.slice();
|
|
163
|
+
}
|
|
164
|
+
function size() {
|
|
165
|
+
return buffer.length;
|
|
166
|
+
}
|
|
167
|
+
return { plugin, push, drain, peek, size };
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=buffer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buffer.js","sourceRoot":"","sources":["../../src/console/buffer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/console/index.ts"],"names":[],"mappings":"AAeA,OAAO,EACL,0BAA0B,EAC1B,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Public barrel for the console capture module.
|
|
2
|
+
//
|
|
3
|
+
// Locked surface per IMPLEMENTATION_PLAN.md Public API contract (line 703):
|
|
4
|
+
//
|
|
5
|
+
// export { createConsoleCaptureBuffer, type ConsoleEvent } from './console';
|
|
6
|
+
//
|
|
7
|
+
// The factory + companion option/buffer types are re-exported so consumers
|
|
8
|
+
// can declare-and-pass without reaching into `./buffer` directly.
|
|
9
|
+
//
|
|
10
|
+
// `getRecordConsolePlugin` is intentionally NOT re-exported from this
|
|
11
|
+
// barrel — it's already exported from the package-level `./rrweb` barrel
|
|
12
|
+
// (Task 1.2's wiring), and a second re-export would create two import
|
|
13
|
+
// paths for the same symbol. Consumers who want the raw plugin import it
|
|
14
|
+
// from `@cubenest/rrweb-core`.
|
|
15
|
+
export { createConsoleCaptureBuffer, } from './buffer';
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/console/index.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,EAAE;AACF,4EAA4E;AAC5E,EAAE;AACF,+EAA+E;AAC/E,EAAE;AACF,2EAA2E;AAC3E,kEAAkE;AAClE,EAAE;AACF,sEAAsE;AACtE,yEAAyE;AACzE,sEAAsE;AACtE,yEAAyE;AACzE,+BAA+B;AAE/B,OAAO,EACL,0BAA0B,GAG3B,MAAM,UAAU,CAAC"}
|