@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,327 @@
1
+ // IndexedDB-backed session chunk store — Task 1.10.
2
+ //
3
+ // `createSessionStore(sessionId)` opens (or creates) the @cubenest/rrweb-core
4
+ // IDB database, returns a `SessionStore` handle whose methods append/read
5
+ // gzipped event chunks keyed by `[sessionId, seq]`. The store is purpose-
6
+ // built for one consumer (@peekdev's MV3 extension) and one shape of
7
+ // payload (the `Uint8Array` output of `../compression`); we keep the
8
+ // abstraction deliberately thin per ADR-0002's "core stays minimal" pact.
9
+ //
10
+ // Why a separate seq column (vs. autoIncrement on the object store)?
11
+ // - autoIncrement is per-store, not per-session. With many sessions
12
+ // interleaved, the keys would still be globally monotonic but the
13
+ // per-session sequence would have gaps, defeating range reads.
14
+ // - A composite primary key of `[sessionId, seq]` lets us range-scan
15
+ // by session cheaply via the natural keyspace ordering. No secondary
16
+ // index needed for reads.
17
+ // - `count()` on the `by-session` index gives us the next `seq` cheaply
18
+ // (no full scan) without persisting a counter row. For the v0.1
19
+ // throughput target (a few hundred chunks per session) this is fine;
20
+ // a future optimization could cache `lastSeq` in memory but adds a
21
+ // correctness hazard across multi-tab opens of the same session.
22
+ //
23
+ // Why no transactions across awaits?
24
+ // - IDB transactions auto-close at the end of the current microtask
25
+ // queue tick (the "task" boundary). Awaiting an unrelated promise
26
+ // between two operations on the SAME transaction is undefined on
27
+ // Chromium and outright broken on Safari (the spec was tightened
28
+ // post-Firefox/Edge convergence; older patterns that relied on
29
+ // "transactions stay open as long as you keep doing IDB" no longer
30
+ // hold). Each public method opens its own transaction and only
31
+ // awaits the wrapped Promise. We pay an extra `transaction()` call
32
+ // per op; in exchange we get portability and predictable behavior.
33
+ //
34
+ // Why structured-clone-safe `bytes` (Uint8Array)?
35
+ // - IDB stores values via structured clone. `Uint8Array` is on the
36
+ // supported list; `ArrayBuffer` would also work, but `Uint8Array`
37
+ // is what `compress()` returns, so we avoid a useless view shuffle.
38
+ const DEFAULT_DB_NAME = '@cubenest/rrweb-core';
39
+ const DEFAULT_STORE_NAME = 'session-chunks';
40
+ const SCHEMA_VERSION = 1;
41
+ const SESSION_INDEX = 'by-session';
42
+ /**
43
+ * Promisify a single `IDBRequest`. Resolves on `success`, rejects on `error`.
44
+ *
45
+ * Keep the body tight — the listeners run on the IDB callback queue and
46
+ * must NOT do work that could throw before binding both handlers, or the
47
+ * UA will swallow the rejection and the caller's `await` will hang
48
+ * forever. This is the standard `req<T>` pattern; we lift it into a
49
+ * helper because every method below uses it ≥1 time.
50
+ */
51
+ function req(r) {
52
+ return new Promise((resolve, reject) => {
53
+ r.onsuccess = () => {
54
+ resolve(r.result);
55
+ };
56
+ r.onerror = () => {
57
+ reject(r.error ?? new Error('IDBRequest failed without an error object'));
58
+ };
59
+ });
60
+ }
61
+ /**
62
+ * Promisify an `IDBTransaction` completion. Resolves on `complete`,
63
+ * rejects on `error` / `abort`. Use after firing all writes so the
64
+ * caller knows the transaction has actually committed before returning.
65
+ */
66
+ function txDone(tx) {
67
+ return new Promise((resolve, reject) => {
68
+ tx.oncomplete = () => {
69
+ resolve();
70
+ };
71
+ tx.onerror = () => {
72
+ reject(tx.error ?? new Error('IDBTransaction failed without an error object'));
73
+ };
74
+ tx.onabort = () => {
75
+ reject(tx.error ?? new Error('IDBTransaction aborted'));
76
+ };
77
+ });
78
+ }
79
+ /**
80
+ * Open the database, creating the object store + index on first run.
81
+ *
82
+ * The `onupgradeneeded` path is the ONLY place schema mutations are
83
+ * allowed. We pin to `SCHEMA_VERSION = 1` for v0.1; future schema
84
+ * changes bump the version and add a migration branch here. Bumping
85
+ * the version triggers a `versionchange` event on any other open
86
+ * connection — callers in long-lived MV3 service workers should be
87
+ * prepared to close-and-reopen.
88
+ */
89
+ function openDb(idb, dbName, storeName) {
90
+ return new Promise((resolve, reject) => {
91
+ const request = idb.open(dbName, SCHEMA_VERSION);
92
+ request.onupgradeneeded = () => {
93
+ const db = request.result;
94
+ if (!db.objectStoreNames.contains(storeName)) {
95
+ // Composite key `[sessionId, seq]` gives us natural range scans
96
+ // by session without a secondary index for reads. The `by-session`
97
+ // index is used for `count()`-based seq assignment and for
98
+ // prune/clear cursors.
99
+ const store = db.createObjectStore(storeName, { keyPath: ['sessionId', 'seq'] });
100
+ store.createIndex(SESSION_INDEX, 'sessionId', { unique: false });
101
+ }
102
+ };
103
+ request.onsuccess = () => {
104
+ resolve(request.result);
105
+ };
106
+ request.onerror = () => {
107
+ reject(request.error ?? new Error('IDB open failed'));
108
+ };
109
+ request.onblocked = () => {
110
+ // A previous connection with an older version is still open and
111
+ // blocking our upgrade. We can't make progress; surface it as a
112
+ // rejection rather than hanging forever.
113
+ reject(new Error(`IDB open blocked by another open connection to ${dbName}`));
114
+ };
115
+ });
116
+ }
117
+ /**
118
+ * Open an IDB-backed chunk store for a session.
119
+ *
120
+ * The returned `SessionStore` is scoped to `sessionId`; all read/write
121
+ * operations are filtered by it. Multiple stores for different sessions
122
+ * can be opened concurrently — they share the same DB connection's
123
+ * keyspace but are isolated by the composite key.
124
+ *
125
+ * @param sessionId non-empty session identifier; used as the first
126
+ * component of every chunk's composite key
127
+ * @param options optional overrides (dbName, storeName, IDBFactory)
128
+ * @throws {Error} if `sessionId` is empty / non-string, or if no
129
+ * `IDBFactory` is available (`globalThis.indexedDB`
130
+ * undefined and `options.indexedDB` not provided)
131
+ */
132
+ export async function createSessionStore(sessionId, options) {
133
+ if (typeof sessionId !== 'string' || sessionId.length === 0) {
134
+ throw new Error('createSessionStore: sessionId must be a non-empty string');
135
+ }
136
+ const dbName = options?.dbName ?? DEFAULT_DB_NAME;
137
+ const storeName = options?.storeName ?? DEFAULT_STORE_NAME;
138
+ // Resolve the IDBFactory. Explicit injection wins so tests / non-browser
139
+ // environments can opt out of `globalThis.indexedDB`. The lookup
140
+ // happens at construction time (not lazily per-op) so misconfiguration
141
+ // fails fast with a clear error instead of a confusing "undefined is
142
+ // not a function" deep inside an op.
143
+ const idb = options?.indexedDB ?? globalThis.indexedDB;
144
+ if (!idb) {
145
+ throw new Error('createSessionStore: no IDBFactory available — pass options.indexedDB or run in a browser');
146
+ }
147
+ const db = await openDb(idb, dbName, storeName);
148
+ // `closed` is the public-visible "is this store usable" flag. We flip
149
+ // it to true on `close()` and check it at the top of every op. Doing
150
+ // this in user-space (rather than relying on the IDB connection's
151
+ // own closed state) lets us surface a clean, package-branded error
152
+ // instead of whatever cryptic message the UA produces on a closed
153
+ // connection (Chromium's "TransactionInactiveError" vs Safari's
154
+ // various "InvalidStateError" wordings).
155
+ let closed = false;
156
+ function assertOpen() {
157
+ if (closed) {
158
+ throw new Error('SessionStore: connection is closed');
159
+ }
160
+ }
161
+ async function append(bytes, meta) {
162
+ assertOpen();
163
+ if (!(bytes instanceof Uint8Array)) {
164
+ throw new TypeError('SessionStore.append: bytes must be a Uint8Array');
165
+ }
166
+ // Step 1 — figure out the next `seq` by counting existing rows for
167
+ // this session. We do this in a `readonly` transaction so it doesn't
168
+ // contend with concurrent reads on other sessions; the cost is one
169
+ // extra `transaction()` per append vs. doing the count + put in a
170
+ // single `readwrite` (which would also work, but is harder to read
171
+ // and gives no measurable benefit for our throughput).
172
+ const countTx = db.transaction(storeName, 'readonly');
173
+ const countStore = countTx.objectStore(storeName);
174
+ const idx = countStore.index(SESSION_INDEX);
175
+ const seq = await req(idx.count(IDBKeyRange.only(sessionId)));
176
+ // Step 2 — write the row. Use `add()` (not `put()`) so a duplicate
177
+ // composite key throws instead of silently overwriting; this guards
178
+ // against the multi-tab race where two writers compute the same
179
+ // `seq` concurrently. The caller can retry on the rejection.
180
+ const writeTx = db.transaction(storeName, 'readwrite');
181
+ const writeStore = writeTx.objectStore(storeName);
182
+ const row = {
183
+ sessionId,
184
+ seq,
185
+ ts: Date.now(),
186
+ bytes,
187
+ ...(meta !== undefined ? { meta } : {}),
188
+ };
189
+ await req(writeStore.add(row));
190
+ await txDone(writeTx);
191
+ return seq;
192
+ }
193
+ async function read(fromSeq, toSeq) {
194
+ assertOpen();
195
+ if (!Number.isFinite(fromSeq) || fromSeq < 0) {
196
+ throw new RangeError('SessionStore.read: fromSeq must be a non-negative number');
197
+ }
198
+ // Bound the range. When toSeq is omitted we want "everything ≥ fromSeq",
199
+ // which we express as `[sessionId, fromSeq] .. [sessionId, +Infinity]`.
200
+ // IDB compares composite keys lexicographically, so the upper bound
201
+ // `[sessionId, +Infinity]` correctly stops before keys with a larger
202
+ // sessionId. Note: `[sessionId, undefined]` does NOT work — undefined
203
+ // is not a valid key component.
204
+ const upper = toSeq ?? Number.POSITIVE_INFINITY;
205
+ const range = IDBKeyRange.bound([sessionId, fromSeq], [sessionId, upper]);
206
+ const tx = db.transaction(storeName, 'readonly');
207
+ const store = tx.objectStore(storeName);
208
+ const all = await req(store.getAll(range));
209
+ // The cursor would already return rows in key order, but `getAll`
210
+ // also returns them sorted by key. We assert the invariant anyway
211
+ // — defensive, since downstream consumers depend on it.
212
+ const chunks = all.map((r) => ({
213
+ seq: r.seq,
214
+ ts: r.ts,
215
+ bytes: r.bytes,
216
+ ...(r.meta !== undefined ? { meta: r.meta } : {}),
217
+ }));
218
+ for (let i = 1; i < chunks.length; i++) {
219
+ const prev = chunks[i - 1];
220
+ const curr = chunks[i];
221
+ if (prev && curr && prev.seq >= curr.seq) {
222
+ // Should never happen given the composite-key sort, but if the
223
+ // UA ever returns out-of-order rows we want a loud failure here
224
+ // rather than a silently-corrupted replay.
225
+ throw new Error(`SessionStore.read: rows out of order at index ${i} (${prev.seq} >= ${curr.seq})`);
226
+ }
227
+ }
228
+ return chunks;
229
+ }
230
+ async function readLastN(n) {
231
+ assertOpen();
232
+ if (!Number.isFinite(n) || n < 0) {
233
+ throw new RangeError('SessionStore.readLastN: n must be a non-negative number');
234
+ }
235
+ if (n === 0)
236
+ return [];
237
+ const total = await size();
238
+ const fromSeq = Math.max(0, total - n);
239
+ return read(fromSeq);
240
+ }
241
+ async function size() {
242
+ assertOpen();
243
+ const tx = db.transaction(storeName, 'readonly');
244
+ const store = tx.objectStore(storeName);
245
+ const idx = store.index(SESSION_INDEX);
246
+ return req(idx.count(IDBKeyRange.only(sessionId)));
247
+ }
248
+ async function totalBytes() {
249
+ assertOpen();
250
+ // We walk the keyspace via `getAll` and sum byteLength. For v0.1
251
+ // session sizes (a few hundred chunks at most), this is fine; if
252
+ // future workloads need true streaming sums we'd switch to a
253
+ // cursor and accumulate without materializing the rows.
254
+ const range = IDBKeyRange.bound([sessionId, 0], [sessionId, Number.POSITIVE_INFINITY]);
255
+ const tx = db.transaction(storeName, 'readonly');
256
+ const store = tx.objectStore(storeName);
257
+ const all = (await req(store.getAll(range)));
258
+ let sum = 0;
259
+ for (const row of all) {
260
+ sum += row.bytes.byteLength;
261
+ }
262
+ return sum;
263
+ }
264
+ async function prune(beforeSeq) {
265
+ assertOpen();
266
+ if (!Number.isFinite(beforeSeq) && beforeSeq !== Number.POSITIVE_INFINITY) {
267
+ throw new RangeError('SessionStore.prune: beforeSeq must be a finite number or +Infinity');
268
+ }
269
+ if (beforeSeq <= 0)
270
+ return 0;
271
+ // Range covers `[sessionId, 0] .. [sessionId, beforeSeq)` — open
272
+ // upper bound excludes `beforeSeq` itself, matching the docstring
273
+ // "delete chunks with seq < beforeSeq".
274
+ const range = IDBKeyRange.bound([sessionId, 0], [sessionId, beforeSeq], false, true);
275
+ const tx = db.transaction(storeName, 'readwrite');
276
+ const store = tx.objectStore(storeName);
277
+ // Cursor-based delete is the IDB idiom for "delete range". We can't
278
+ // use `delete(range)` here without losing the count of deleted
279
+ // rows, and the count is part of the public return value.
280
+ const deleted = await new Promise((resolve, reject) => {
281
+ const cursorReq = store.openCursor(range);
282
+ let count = 0;
283
+ cursorReq.onsuccess = () => {
284
+ const cursor = cursorReq.result;
285
+ if (!cursor) {
286
+ resolve(count);
287
+ return;
288
+ }
289
+ cursor.delete();
290
+ count++;
291
+ cursor.continue();
292
+ };
293
+ cursorReq.onerror = () => {
294
+ reject(cursorReq.error ?? new Error('SessionStore.prune: cursor failed'));
295
+ };
296
+ });
297
+ await txDone(tx);
298
+ return deleted;
299
+ }
300
+ async function clear() {
301
+ assertOpen();
302
+ // `prune(+Infinity)` deletes everything `seq < +Infinity` for this
303
+ // session — i.e. every row scoped to `sessionId`. Other sessions'
304
+ // rows are untouched because the range is bounded on the lower
305
+ // side by `[sessionId, 0]` and on the upper by `[sessionId, ∞)`.
306
+ await prune(Number.POSITIVE_INFINITY);
307
+ }
308
+ function close() {
309
+ if (closed)
310
+ return Promise.resolve();
311
+ closed = true;
312
+ db.close();
313
+ return Promise.resolve();
314
+ }
315
+ return {
316
+ sessionId,
317
+ append,
318
+ read,
319
+ readLastN,
320
+ size,
321
+ totalBytes,
322
+ prune,
323
+ clear,
324
+ close,
325
+ };
326
+ }
327
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/persistence/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * One stored chunk: a gzipped rrweb-event batch plus the bookkeeping the
3
+ * store assigns at append time. `seq` is monotonically increasing per
4
+ * session; `ts` is the wall-clock at append; `bytes` is the gzipped output
5
+ * of `compress()`, persisted verbatim and never re-encoded by the store.
6
+ *
7
+ * `meta` is an opt-in escape hatch for adapter-supplied metadata
8
+ * (e.g. `{ url, isFirst, viewportWidth }`). Keys are plain strings;
9
+ * values are restricted to JSON-safe scalars so the structured-clone
10
+ * write path stays predictable across browsers.
11
+ */
12
+ export interface SessionChunk {
13
+ /** Monotonically increasing per session. The store assigns this on append. */
14
+ readonly seq: number;
15
+ /** ms epoch when the chunk was created. */
16
+ readonly ts: number;
17
+ /** Gzipped event bytes (from compress()). Persisted as-is. */
18
+ readonly bytes: Uint8Array;
19
+ /** Optional adapter-supplied metadata. */
20
+ readonly meta?: Record<string, string | number | boolean>;
21
+ }
22
+ /**
23
+ * The handle returned by `createSessionStore`. All operations are async
24
+ * and back onto the underlying IDB connection opened by the factory.
25
+ *
26
+ * Operations don't share transactions — each call opens its own. This is
27
+ * the safest pattern under IndexedDB's "transactions auto-close at the
28
+ * end of the current task" rule: holding a transaction across an `await`
29
+ * boundary is undefined behaviour on most engines and outright broken on
30
+ * Safari. The internal helper wraps each `IDBRequest` in a Promise that
31
+ * resolves on `onsuccess` and rejects on `onerror`.
32
+ *
33
+ * After `close()`, every subsequent op rejects with an Error rather than
34
+ * silently queueing — callers can race on the rejection to decide whether
35
+ * to reopen.
36
+ */
37
+ export interface SessionStore {
38
+ /** The session id this store was opened for. Immutable after construction. */
39
+ readonly sessionId: string;
40
+ /** Append a chunk. Returns the assigned seq. */
41
+ append(bytes: Uint8Array, meta?: SessionChunk['meta']): Promise<number>;
42
+ /** Read chunks with seq in [fromSeq, toSeq] inclusive (toSeq optional = unlimited). */
43
+ read(fromSeq: number, toSeq?: number): Promise<SessionChunk[]>;
44
+ /** Read the last N chunks. */
45
+ readLastN(n: number): Promise<SessionChunk[]>;
46
+ /** Total chunk count for the session. */
47
+ size(): Promise<number>;
48
+ /** Total compressed bytes across all chunks for the session. */
49
+ totalBytes(): Promise<number>;
50
+ /** Delete chunks with seq < beforeSeq (used by the extension to keep recent-N). */
51
+ prune(beforeSeq: number): Promise<number>;
52
+ /** Delete every chunk for this session. */
53
+ clear(): Promise<void>;
54
+ /** Close the underlying IDB connection. */
55
+ close(): Promise<void>;
56
+ }
57
+ /**
58
+ * Options for `createSessionStore`. Every field is optional; defaults
59
+ * are picked so a plain `createSessionStore(sessionId)` call works in
60
+ * any browser that exposes `globalThis.indexedDB`.
61
+ *
62
+ * `indexedDB` is the test/polyfill seam. Pass `fake-indexeddb`'s factory
63
+ * here in unit tests, or your environment's polyfill in non-standard
64
+ * runtimes (worker shims, jsdom, etc.). When omitted, the store reads
65
+ * `globalThis.indexedDB` at construction time and throws if it's
66
+ * undefined — fail fast beats lazy null-deref.
67
+ */
68
+ export interface SessionStoreOptions {
69
+ /** IDB database name. Default '@cubenest/rrweb-core'. */
70
+ dbName?: string;
71
+ /** IDB object-store name. Default 'session-chunks'. */
72
+ storeName?: string;
73
+ /** Optional injection for tests / non-browser environments. */
74
+ indexedDB?: IDBFactory;
75
+ }
76
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/persistence/types.ts"],"names":[],"mappings":"AAoBA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,YAAY;IAC3B,8EAA8E;IAC9E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;CAC3D;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,YAAY;IAC3B,8EAA8E;IAC9E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,gDAAgD;IAChD,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxE,uFAAuF;IACvF,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAC/D,8BAA8B;IAC9B,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAC9C,yCAAyC;IACzC,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACxB,gEAAgE;IAChE,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9B,mFAAmF;IACnF,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,2CAA2C;IAC3C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,2CAA2C;IAC3C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,mBAAmB;IAClC,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,UAAU,CAAC;CACxB"}
@@ -0,0 +1,21 @@
1
+ // IndexedDB persistence types — Task 1.10.
2
+ //
3
+ // Public shapes for the IDB-backed chunk store used by the @peekdev extension
4
+ // to persist rrweb recordings across MV3 service-worker restarts. Tracelane
5
+ // ignores this module — its self-contained HTML report path doesn't need
6
+ // crash-safe persistence.
7
+ //
8
+ // A "chunk" is the output of `compress(events)` — gzipped event bytes that
9
+ // land here as-is. The store does NOT decode them; the caller round-trips
10
+ // via `decompress` (see ../compression).
11
+ //
12
+ // Public API contract (IMPLEMENTATION_PLAN.md line 726):
13
+ //
14
+ // export { createSessionStore, type SessionChunk } from './persistence';
15
+ //
16
+ // The factory and the chunk type are the "locked" surface. The store
17
+ // interface and options interface are re-exported for declare-and-pass
18
+ // ergonomics but are not part of the contract line; their shape can
19
+ // evolve under semver.
20
+ export {};
21
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/persistence/types.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,EAAE;AACF,8EAA8E;AAC9E,4EAA4E;AAC5E,yEAAyE;AACzE,0BAA0B;AAC1B,EAAE;AACF,2EAA2E;AAC3E,0EAA0E;AAC1E,yCAAyC;AACzC,EAAE;AACF,yDAAyD;AACzD,EAAE;AACF,2EAA2E;AAC3E,EAAE;AACF,qEAAqE;AACrE,uEAAuE;AACvE,oEAAoE;AACpE,uBAAuB"}
@@ -0,0 +1,5 @@
1
+ export { record, Replayer, EventType, IncrementalSource, MouseInteractions, } from '@posthog/rrweb';
2
+ export { getRecordConsolePlugin } from '@rrweb/rrweb-plugin-console-record';
3
+ export type { eventWithTime, customEvent, } from '@posthog/rrweb-types';
4
+ export type { recordOptions } from '@posthog/rrweb';
5
+ //# sourceMappingURL=rrweb.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rrweb.d.ts","sourceRoot":"","sources":["../src/rrweb.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,MAAM,EACN,QAAQ,EACR,SAAS,EACT,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAE5E,YAAY,EACV,aAAa,EACb,WAAW,GACZ,MAAM,sBAAsB,CAAC;AAI9B,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/rrweb.js ADDED
@@ -0,0 +1,13 @@
1
+ // Re-export the vendored capture surface.
2
+ //
3
+ // Engine comes from the PostHog fork (@posthog/rrweb), which is feature-frozen
4
+ // at 0.0.34 — see ADR-0002. The fork inherits upstream rrweb 2.x's package
5
+ // split, so plugin code is NOT in the main bundle. We pull the console-record
6
+ // plugin from the upstream MIT package because event shapes are compatible
7
+ // (both descend from rrweb@2.0.0-alpha.17).
8
+ //
9
+ // If PostHog renames an export, or upstream renames a plugin export, this
10
+ // file is where the build will break — that is the smoke test.
11
+ export { record, Replayer, EventType, IncrementalSource, MouseInteractions, } from '@posthog/rrweb';
12
+ export { getRecordConsolePlugin } from '@rrweb/rrweb-plugin-console-record';
13
+ //# sourceMappingURL=rrweb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rrweb.js","sourceRoot":"","sources":["../src/rrweb.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,EAAE;AACF,+EAA+E;AAC/E,2EAA2E;AAC3E,8EAA8E;AAC9E,2EAA2E;AAC3E,4CAA4C;AAC5C,EAAE;AACF,0EAA0E;AAC1E,+DAA+D;AAC/D,OAAO,EACL,MAAM,EACN,QAAQ,EACR,SAAS,EACT,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Decode a base64 string to bytes using the platform-native `atob`.
3
+ *
4
+ * @throws DOMException ("InvalidCharacterError") if the input is not
5
+ * well-formed base64 — caller decides whether to wrap.
6
+ */
7
+ export declare function decodeBase64(base64: string): Uint8Array;
8
+ //# sourceMappingURL=base64.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base64.d.ts","sourceRoot":"","sources":["../../src/screenshot/base64.ts"],"names":[],"mappings":"AAWA;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAGvD"}
@@ -0,0 +1,21 @@
1
+ // Platform-native base64 decoder — Task 1.6.
2
+ //
3
+ // The substrate must stay environment-agnostic. `Buffer.from(str, 'base64')`
4
+ // would tie us to Node; pulling in a JS-side decoder bloats the bundle. Both
5
+ // targets (Node 20.10+ and modern browsers) ship `atob` on globalThis, so
6
+ // we use it.
7
+ //
8
+ // `atob` returns a binary string — each char's `.charCodeAt(0)` is the
9
+ // corresponding byte. We materialise that into a Uint8Array with
10
+ // `Uint8Array.from`, which is O(n) and allocates exactly once.
11
+ /**
12
+ * Decode a base64 string to bytes using the platform-native `atob`.
13
+ *
14
+ * @throws DOMException ("InvalidCharacterError") if the input is not
15
+ * well-formed base64 — caller decides whether to wrap.
16
+ */
17
+ export function decodeBase64(base64) {
18
+ const bin = atob(base64);
19
+ return Uint8Array.from(bin, (ch) => ch.charCodeAt(0));
20
+ }
21
+ //# sourceMappingURL=base64.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base64.js","sourceRoot":"","sources":["../../src/screenshot/base64.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,0EAA0E;AAC1E,aAAa;AACb,EAAE;AACF,uEAAuE;AACvE,iEAAiE;AACjE,+DAA+D;AAE/D;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IACzB,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AACxD,CAAC"}
@@ -0,0 +1,50 @@
1
+ import type { ScreenshotAdapter } from './types';
2
+ /**
3
+ * Structural shape of a CDP transport — typically a thin wrapper around
4
+ * `chrome-remote-interface`, a WebDriver CDP bridge (e.g.
5
+ * `browser.cdp` in WDIO), or Playwright's `CDPSession.send`.
6
+ *
7
+ * The factory only calls `.send`; emitting events back is the consumer's
8
+ * problem and lives outside this contract.
9
+ */
10
+ export interface CDPTransport {
11
+ send(method: string, params?: Record<string, unknown>): Promise<unknown>;
12
+ }
13
+ /**
14
+ * Factory-time options for the CDP screenshot adapter. All fields map
15
+ * directly onto the [`Page.captureScreenshot`][cdp] parameters; defaults
16
+ * are applied at `capture()` time, not at factory time, so a single adapter
17
+ * can be reused across captures without mutation.
18
+ *
19
+ * [cdp]: https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-captureScreenshot
20
+ */
21
+ export interface CDPScreenshotOptions {
22
+ /** PNG (default) or JPEG. JPEG is smaller but lossy. */
23
+ format?: 'png' | 'jpeg';
24
+ /** 0-100. Only meaningful when `format === 'jpeg'`; CDP ignores it for PNG. */
25
+ quality?: number;
26
+ /**
27
+ * `true` (default) — capture from the render surface, bypassing window
28
+ * compositing and avoiding the OS cursor. `false` mirrors a screen grab.
29
+ */
30
+ fromSurface?: boolean;
31
+ /**
32
+ * When `true`, capture the full scrollable area, not just the viewport.
33
+ * Defaults to `false` because the contract is "the visible viewport" and
34
+ * full-page captures are dramatically larger.
35
+ */
36
+ captureBeyondViewport?: boolean;
37
+ }
38
+ /**
39
+ * Build a `ScreenshotAdapter` that captures via CDP `Page.captureScreenshot`.
40
+ *
41
+ * The returned adapter is stateless beyond its closure: it can be called
42
+ * concurrently, and `dispose()` is a no-op (the transport's lifecycle is
43
+ * owned by the caller).
44
+ *
45
+ * @param transport Any object that can `.send('Page.captureScreenshot', …)`.
46
+ * @param options Factory-time defaults; `capture()` forwards them as
47
+ * CDP params.
48
+ */
49
+ export declare function createCDPScreenshotAdapter(transport: CDPTransport, options?: CDPScreenshotOptions): ScreenshotAdapter;
50
+ //# sourceMappingURL=cdp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdp.d.ts","sourceRoot":"","sources":["../../src/screenshot/cdp.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1E;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAoB;IACnC,wDAAwD;IACxD,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,YAAY,EACvB,OAAO,GAAE,oBAAyB,GACjC,iBAAiB,CAuCnB"}
@@ -0,0 +1,65 @@
1
+ // CDP screenshot adapter — Task 1.6.
2
+ //
3
+ // Wraps a caller-supplied CDP transport and exposes the `ScreenshotAdapter`
4
+ // surface. Used by P1/tracelane against the WebDriver-supplied CDP session
5
+ // (every framework adapter — `@tracelane/wdio`, `@tracelane/playwright`,
6
+ // `@tracelane/cypress` — converges on a `cdp(domain, command, params)` shim
7
+ // per P1 PRD §A, and our factory talks to that shim through the
8
+ // `CDPTransport` shape below).
9
+ //
10
+ // Why injection-by-parameter (rather than `import { CDP } from 'chrome-remote-interface'`):
11
+ // it keeps the substrate environment-agnostic and lets each framework
12
+ // reuse the CDP session it *already* has — re-attaching a second client
13
+ // would duplicate event streams and consume a connection slot. The
14
+ // transport is structural: anything with `.send(method, params)` returning
15
+ // a Promise will do.
16
+ //
17
+ // We deliberately do not implement retry, throttling, or queueing. The
18
+ // caller schedules captures (typically 0.2-1 Hz per ADR-0002's screenshot
19
+ // fallback note), and a single in-flight rejection propagates verbatim.
20
+ import { decodeBase64 } from './base64';
21
+ /**
22
+ * Build a `ScreenshotAdapter` that captures via CDP `Page.captureScreenshot`.
23
+ *
24
+ * The returned adapter is stateless beyond its closure: it can be called
25
+ * concurrently, and `dispose()` is a no-op (the transport's lifecycle is
26
+ * owned by the caller).
27
+ *
28
+ * @param transport Any object that can `.send('Page.captureScreenshot', …)`.
29
+ * @param options Factory-time defaults; `capture()` forwards them as
30
+ * CDP params.
31
+ */
32
+ export function createCDPScreenshotAdapter(transport, options = {}) {
33
+ const format = options.format ?? 'png';
34
+ const fromSurface = options.fromSurface ?? true;
35
+ const captureBeyondViewport = options.captureBeyondViewport ?? false;
36
+ // `quality` is left undefined unless explicitly set — CDP rejects unknown
37
+ // undefineds in some language bindings, and the param is meaningful only
38
+ // for JPEG anyway.
39
+ const quality = options.quality;
40
+ return {
41
+ async capture() {
42
+ const params = {
43
+ format,
44
+ fromSurface,
45
+ captureBeyondViewport,
46
+ };
47
+ if (quality !== undefined) {
48
+ params.quality = quality;
49
+ }
50
+ const reply = (await transport.send('Page.captureScreenshot', params));
51
+ const data = reply && typeof reply === 'object' ? reply.data : undefined;
52
+ if (typeof data !== 'string') {
53
+ const got = data === undefined ? 'undefined' : typeof data;
54
+ throw new Error(`Page.captureScreenshot returned no base64 \`data\` field — got ${got}`);
55
+ }
56
+ return decodeBase64(data);
57
+ },
58
+ async dispose() {
59
+ // The transport lifecycle is owned by the caller — there is nothing
60
+ // for the substrate to release. The hook stays present for symmetry
61
+ // with adapters that *do* hold subscriptions.
62
+ },
63
+ };
64
+ }
65
+ //# sourceMappingURL=cdp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdp.js","sourceRoot":"","sources":["../../src/screenshot/cdp.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,EAAE;AACF,4EAA4E;AAC5E,2EAA2E;AAC3E,yEAAyE;AACzE,4EAA4E;AAC5E,gEAAgE;AAChE,+BAA+B;AAC/B,EAAE;AACF,4FAA4F;AAC5F,sEAAsE;AACtE,wEAAwE;AACxE,mEAAmE;AACnE,2EAA2E;AAC3E,qBAAqB;AACrB,EAAE;AACF,uEAAuE;AACvE,0EAA0E;AAC1E,wEAAwE;AAExE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAyCxC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,0BAA0B,CACxC,SAAuB,EACvB,UAAgC,EAAE;IAElC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC;IACvC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC;IAChD,MAAM,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,IAAI,KAAK,CAAC;IACrE,0EAA0E;IAC1E,yEAAyE;IACzE,mBAAmB;IACnB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAEhC,OAAO;QACL,KAAK,CAAC,OAAO;YACX,MAAM,MAAM,GAA4B;gBACtC,MAAM;gBACN,WAAW;gBACX,qBAAqB;aACtB,CAAC;YACF,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC1B,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;YAC3B,CAAC;YAED,MAAM,KAAK,GAAG,CAAC,MAAM,SAAS,CAAC,IAAI,CAAC,wBAAwB,EAAE,MAAM,CAAC,CAGxD,CAAC;YAEd,MAAM,IAAI,GAAG,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;YACzE,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC7B,MAAM,GAAG,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC;gBAC3D,MAAM,IAAI,KAAK,CAAC,kEAAkE,GAAG,EAAE,CAAC,CAAC;YAC3F,CAAC;YAED,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QACD,KAAK,CAAC,OAAO;YACX,oEAAoE;YACpE,oEAAoE;YACpE,8CAA8C;QAChD,CAAC;KACF,CAAC;AACJ,CAAC"}