@amplitude/session-replay-browser 1.43.0 → 1.44.1

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 (75) hide show
  1. package/lib/cjs/config/local-config.d.ts +3 -1
  2. package/lib/cjs/config/local-config.d.ts.map +1 -1
  3. package/lib/cjs/config/local-config.js +60 -2
  4. package/lib/cjs/config/local-config.js.map +1 -1
  5. package/lib/cjs/config/types.d.ts +43 -0
  6. package/lib/cjs/config/types.d.ts.map +1 -1
  7. package/lib/cjs/config/types.js.map +1 -1
  8. package/lib/cjs/constants.d.ts +1 -0
  9. package/lib/cjs/constants.d.ts.map +1 -1
  10. package/lib/cjs/constants.js +7 -1
  11. package/lib/cjs/constants.js.map +1 -1
  12. package/lib/cjs/events/base-events-store.d.ts.map +1 -1
  13. package/lib/cjs/events/base-events-store.js +1 -1
  14. package/lib/cjs/events/base-events-store.js.map +1 -1
  15. package/lib/cjs/events/events-idb-store.d.ts +2 -0
  16. package/lib/cjs/events/events-idb-store.d.ts.map +1 -1
  17. package/lib/cjs/events/events-idb-store.js +44 -15
  18. package/lib/cjs/events/events-idb-store.js.map +1 -1
  19. package/lib/cjs/events/events-memory-store.d.ts +2 -0
  20. package/lib/cjs/events/events-memory-store.d.ts.map +1 -1
  21. package/lib/cjs/events/events-memory-store.js +60 -11
  22. package/lib/cjs/events/events-memory-store.js.map +1 -1
  23. package/lib/cjs/session-replay.d.ts.map +1 -1
  24. package/lib/cjs/session-replay.js +27 -25
  25. package/lib/cjs/session-replay.js.map +1 -1
  26. package/lib/cjs/track-destination.d.ts +19 -1
  27. package/lib/cjs/track-destination.d.ts.map +1 -1
  28. package/lib/cjs/track-destination.js +188 -11
  29. package/lib/cjs/track-destination.js.map +1 -1
  30. package/lib/cjs/version.d.ts +1 -1
  31. package/lib/cjs/version.js +1 -1
  32. package/lib/cjs/version.js.map +1 -1
  33. package/lib/cjs/worker/index.js +1 -1
  34. package/lib/esm/config/local-config.d.ts +3 -1
  35. package/lib/esm/config/local-config.d.ts.map +1 -1
  36. package/lib/esm/config/local-config.js +61 -3
  37. package/lib/esm/config/local-config.js.map +1 -1
  38. package/lib/esm/config/types.d.ts +43 -0
  39. package/lib/esm/config/types.d.ts.map +1 -1
  40. package/lib/esm/config/types.js.map +1 -1
  41. package/lib/esm/constants.d.ts +1 -0
  42. package/lib/esm/constants.d.ts.map +1 -1
  43. package/lib/esm/constants.js +6 -0
  44. package/lib/esm/constants.js.map +1 -1
  45. package/lib/esm/events/base-events-store.d.ts.map +1 -1
  46. package/lib/esm/events/base-events-store.js +1 -1
  47. package/lib/esm/events/base-events-store.js.map +1 -1
  48. package/lib/esm/events/events-idb-store.d.ts +2 -0
  49. package/lib/esm/events/events-idb-store.d.ts.map +1 -1
  50. package/lib/esm/events/events-idb-store.js +44 -15
  51. package/lib/esm/events/events-idb-store.js.map +1 -1
  52. package/lib/esm/events/events-memory-store.d.ts +2 -0
  53. package/lib/esm/events/events-memory-store.d.ts.map +1 -1
  54. package/lib/esm/events/events-memory-store.js +61 -12
  55. package/lib/esm/events/events-memory-store.js.map +1 -1
  56. package/lib/esm/session-replay.d.ts.map +1 -1
  57. package/lib/esm/session-replay.js +27 -25
  58. package/lib/esm/session-replay.js.map +1 -1
  59. package/lib/esm/track-destination.d.ts +19 -1
  60. package/lib/esm/track-destination.d.ts.map +1 -1
  61. package/lib/esm/track-destination.js +190 -13
  62. package/lib/esm/track-destination.js.map +1 -1
  63. package/lib/esm/version.d.ts +1 -1
  64. package/lib/esm/version.js +1 -1
  65. package/lib/esm/version.js.map +1 -1
  66. package/lib/esm/worker/index.js +1 -1
  67. package/lib/scripts/index-min.js +1 -1
  68. package/lib/scripts/index-min.js.gz +0 -0
  69. package/lib/scripts/index-min.js.map +1 -1
  70. package/lib/scripts/session-replay-browser-min.js +1 -1
  71. package/lib/scripts/session-replay-browser-min.js.gz +0 -0
  72. package/lib/scripts/session-replay-browser-min.js.map +1 -1
  73. package/lib/scripts/worker-min.js +1 -1
  74. package/lib/scripts/worker-min.js.gz +0 -0
  75. package/package.json +3 -3
@@ -138,6 +138,7 @@ var SessionReplayEventsIDBStore = /** @class */ (function (_super) {
138
138
  _this = _super.call(this, args) || this;
139
139
  _this.consecutiveFailures = 0;
140
140
  _this.hasTriggeredFallback = false;
141
+ _this.emptyFilteredCount = 0;
141
142
  _this.getSequencesToSend = function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
142
143
  var errorLogged, timedOut, sequences, tx, cancelTimeout, cursor, _a, sessionId, events, e_1;
143
144
  var _this = this;
@@ -148,9 +149,9 @@ var SessionReplayEventsIDBStore = /** @class */ (function (_super) {
148
149
  timedOut = false;
149
150
  _b.label = 1;
150
151
  case 1:
151
- _b.trys.push([1, 6, , 7]);
152
+ _b.trys.push([1, 9, , 10]);
152
153
  sequences = [];
153
- tx = this.db.transaction('sequencesToSend');
154
+ tx = this.db.transaction('sequencesToSend', 'readwrite');
154
155
  // Attach a catch handler immediately so tx.done rejections (e.g. AbortError after
155
156
  // cursor traversal completes) are always handled without blocking the return path.
156
157
  // The errorLogged / timedOut flags prevent double-logging and double-recording
@@ -173,8 +174,15 @@ var SessionReplayEventsIDBStore = /** @class */ (function (_super) {
173
174
  cursor = _b.sent();
174
175
  _b.label = 3;
175
176
  case 3:
176
- if (!cursor) return [3 /*break*/, 5];
177
+ if (!cursor) return [3 /*break*/, 8];
177
178
  _a = cursor.value, sessionId = _a.sessionId, events = _a.events;
179
+ if (!(events.length === 0)) return [3 /*break*/, 5];
180
+ this.maybeLogEmptyFiltered('getSequencesToSend');
181
+ return [4 /*yield*/, cursor.delete()];
182
+ case 4:
183
+ _b.sent();
184
+ return [3 /*break*/, 6];
185
+ case 5:
178
186
  // Return all completed sequences regardless of tabId. Filtering by tab
179
187
  // would cause event loss on page reload: a new store instance gets a
180
188
  // fresh in-memory UUID and would never see sequences written by the
@@ -186,23 +194,24 @@ var SessionReplayEventsIDBStore = /** @class */ (function (_super) {
186
194
  sequenceId: cursor.key,
187
195
  sessionId: sessionId,
188
196
  });
189
- return [4 /*yield*/, cursor.continue()];
190
- case 4:
197
+ _b.label = 6;
198
+ case 6: return [4 /*yield*/, cursor.continue()];
199
+ case 7:
191
200
  cursor = _b.sent();
192
201
  return [3 /*break*/, 3];
193
- case 5:
202
+ case 8:
194
203
  this.recordSuccess();
195
204
  cancelTimeout();
196
205
  return [2 /*return*/, sequences];
197
- case 6:
206
+ case 9:
198
207
  e_1 = _b.sent();
199
208
  if (!timedOut) {
200
209
  errorLogged = true;
201
210
  (0, is_abort_error_1.logIdbError)(this.loggerProvider, "".concat(messages_1.STORAGE_FAILURE, ": ").concat(e_1), e_1);
202
211
  this.recordFailure();
203
212
  }
204
- return [3 /*break*/, 7];
205
- case 7: return [2 /*return*/, undefined];
213
+ return [3 /*break*/, 10];
214
+ case 10: return [2 /*return*/, undefined];
206
215
  }
207
216
  });
208
217
  }); };
@@ -243,8 +252,10 @@ var SessionReplayEventsIDBStore = /** @class */ (function (_super) {
243
252
  cancelTimeout();
244
253
  return [2 /*return*/, undefined];
245
254
  }
246
- // Skip empty sequences — no point writing a zero-event row to sequencesToSend.
255
+ // Skip empty sequences — no point writing a zero-event row to sequencesToSend
256
+ // (would later POST as an empty body and 400 on the server).
247
257
  if (currentSequenceData.events.length === 0) {
258
+ this.maybeLogEmptyFiltered('storeCurrentSequence');
248
259
  cancelTimeout();
249
260
  return [2 /*return*/, undefined];
250
261
  }
@@ -288,7 +299,7 @@ var SessionReplayEventsIDBStore = /** @class */ (function (_super) {
288
299
  timedOut = false;
289
300
  _a.label = 1;
290
301
  case 1:
291
- _a.trys.push([1, 13, , 14]);
302
+ _a.trys.push([1, 15, , 16]);
292
303
  tx = this.db.transaction([exports.currentSequenceKey, exports.sequencesToSendKey], 'readwrite');
293
304
  // Attach a catch handler immediately so tx.done rejections (e.g. AbortError after
294
305
  // put succeeds but before auto-commit) are always handled without blocking.
@@ -347,15 +358,23 @@ var SessionReplayEventsIDBStore = /** @class */ (function (_super) {
347
358
  return [2 /*return*/, undefined];
348
359
  case 10:
349
360
  eventsToSend = ownedSequence.events;
361
+ if (!(eventsToSend.length === 0)) return [3 /*break*/, 12];
362
+ this.maybeLogEmptyFiltered('addEventToCurrentSequence');
350
363
  return [4 /*yield*/, tx.objectStore(exports.currentSequenceKey).put({ sessionId: sessionId, events: [event], tabId: this.tabId })];
351
364
  case 11:
365
+ _a.sent();
366
+ this.recordSuccess();
367
+ cancelTimeout();
368
+ return [2 /*return*/, undefined];
369
+ case 12: return [4 /*yield*/, tx.objectStore(exports.currentSequenceKey).put({ sessionId: sessionId, events: [event], tabId: this.tabId })];
370
+ case 13:
352
371
  _a.sent();
353
372
  return [4 /*yield*/, tx.objectStore(exports.sequencesToSendKey).put({
354
373
  sessionId: sessionId,
355
374
  events: eventsToSend,
356
375
  tabId: this.tabId,
357
376
  })];
358
- case 12:
377
+ case 14:
359
378
  sequenceId = _a.sent();
360
379
  this.recordSuccess();
361
380
  cancelTimeout();
@@ -364,15 +383,15 @@ var SessionReplayEventsIDBStore = /** @class */ (function (_super) {
364
383
  sessionId: sessionId,
365
384
  sequenceId: sequenceId,
366
385
  }];
367
- case 13:
386
+ case 15:
368
387
  e_3 = _a.sent();
369
388
  if (!timedOut) {
370
389
  errorLogged = true;
371
390
  (0, is_abort_error_1.logIdbError)(this.loggerProvider, "".concat(messages_1.STORAGE_FAILURE, ": ").concat(e_3), e_3);
372
391
  this.recordFailure();
373
392
  }
374
- return [3 /*break*/, 14];
375
- case 14: return [2 /*return*/, undefined];
393
+ return [3 /*break*/, 16];
394
+ case 16: return [2 /*return*/, undefined];
376
395
  }
377
396
  });
378
397
  }); };
@@ -436,6 +455,16 @@ var SessionReplayEventsIDBStore = /** @class */ (function (_super) {
436
455
  _this.consecutiveFailureThreshold = (_a = args.consecutiveFailureThreshold) !== null && _a !== void 0 ? _a : 1;
437
456
  return _this;
438
457
  }
458
+ // Sampled (1 in 100) debug log so we can observe whether the store-layer guards
459
+ // are catching empty-batch cases that would otherwise hit the empty-body 400 path
460
+ // on the server. Logged at debug, not warn — this is operational telemetry for
461
+ // post-deploy verification, not a customer-actionable warning. Per-store-instance
462
+ // counter (rather than Math.random) keeps the first hit deterministic for tests.
463
+ SessionReplayEventsIDBStore.prototype.maybeLogEmptyFiltered = function (source) {
464
+ if (this.emptyFilteredCount++ % 100 === 0) {
465
+ this.loggerProvider.debug("Filtered empty session replay sequence at ".concat(source, " (idb store)"));
466
+ }
467
+ };
439
468
  SessionReplayEventsIDBStore.prototype.recordFailure = function () {
440
469
  var _a;
441
470
  this.consecutiveFailures++;
@@ -1 +1 @@
1
- {"version":3,"file":"events-idb-store.js","sourceRoot":"","sources":["../../../src/events/events-idb-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { DBSchema, IDBPDatabase, openDB } from 'idb';\nimport { STORAGE_FAILURE } from '../messages';\nimport { EventType, Events, SendingSequencesReturn } from '../typings/session-replay';\nimport { BaseEventsStore, InstanceArgs as BaseInstanceArgs } from './base-events-store';\nimport { logIdbError } from '../utils/is-abort-error';\n\n// crypto.randomUUID() requires a secure context (https). Fall back to a\n// Math.random-based UUID for http origins or older browsers — tab IDs don't\n// need to be cryptographically secure, just unique within a session.\nexport function generateUUID(): string {\n try {\n return crypto.randomUUID();\n } catch {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);\n });\n }\n}\n\nexport const currentSequenceKey = 'sessionCurrentSequence';\nexport const sequencesToSendKey = 'sequencesToSend';\nexport const remoteConfigKey = 'remoteConfig';\n\n// Timeout for openDB at init. IDB openDB can hang forever when another tab\n// holds an open connection during a version upgrade, or when the DB is in a\n// \"closing\" state (documented Chrome behaviour). When that happens we want\n// SessionReplayEventsIDBStore.new() to bail out so the caller can fall back\n// to the in-memory store.\nexport const OPEN_DB_TIMEOUT_MS = 2000;\n\n// Timeout for per-operation tx.done settlement. Mid-recording a readwrite\n// transaction can stall (storage pressure in some browsers stalls instead of\n// throwing); without a timeout, recordFailure() is never called and the\n// memory fallback never triggers. The transaction may still settle later;\n// the timedOut flag prevents double-counting alongside errorLogged.\nexport const TX_DONE_TIMEOUT_MS = 5000;\n\n/**\n * Race a promise against a timeout. Resolves/rejects with the original\n * promise's value when it settles first; rejects with a timeout error if\n * `ms` elapses first. Either way the timer is cleared so we don't leak\n * pending setTimeouts.\n */\nexport function withTimeout<T>(promise: Promise<T>, ms: number, message = 'IDB operation timed out'): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error(`${message} after ${ms}ms`)), ms);\n promise.then(\n (v) => {\n clearTimeout(timer);\n resolve(v);\n },\n (e) => {\n clearTimeout(timer);\n reject(e);\n },\n );\n });\n}\n\n/**\n * Arms a watchdog that fires `onTimeout` only if `tx.done` hasn't settled\n * within `ms`. A \"soft cancel\" returned to the caller suppresses the timeout\n * if the synchronous operation body completes without exception — important\n * because in test environments fake timers can fire ahead of tx.done's commit\n * microtask. Production behaviour is preserved: a genuinely stalled\n * transaction (no commit, no error, individual op promises never resolved)\n * cannot reach the soft-cancel call and the timeout fires as designed.\n *\n * Soft-cancel is only invoked once the awaits inside the operation's outer\n * try block have all returned — i.e. the IDB driver acknowledged each\n * individual put/get. If the driver accepts a put but the underlying\n * transaction silently fails to commit (the production stall scenario),\n * tx.done still hasn't settled, and recordFailure must NOT have been\n * suppressed. The fix for that scenario in production is the tx.done.catch\n * handler attached separately by callers, which catches the eventual abort.\n *\n * The soft-cancel pattern only suppresses the timeout for the \"all puts\n * resolved successfully\" case — a case where tx.done is overwhelmingly\n * likely to settle imminently in production. If it doesn't, callers can\n * still detect the failure on the NEXT operation (which will fail to open\n * a transaction or hit the same pressure), because all three methods use\n * readwrite transactions on the same two stores, which IDB serializes:\n * if T1's tx.done never settles, T2 is blocked waiting for T1 to commit or\n * abort — T2's put/get requests never resolve, T2 never reaches its\n * soft-cancel, and T2's watchdog fires, calling recordFailure().\n */\nfunction armTxDoneTimeout(txDone: Promise<unknown>, ms: number, onTimeout: () => void): () => void {\n const timer = setTimeout(onTimeout, ms);\n // Belt-and-braces: clear the timer when tx.done settles, even though the\n // primary cancel path is the caller's success-path cancel(). This covers\n // the case where tx.done settles with no caller cancellation (shouldn't\n // happen in current code paths, but cheap insurance).\n txDone.then(\n () => clearTimeout(timer),\n () => clearTimeout(timer),\n );\n return () => clearTimeout(timer);\n}\n\nexport interface SessionReplayDB extends DBSchema {\n sessionCurrentSequence: {\n key: number;\n value: Omit<SendingSequencesReturn<number>, 'sequenceId'> & { tabId?: string };\n };\n sequencesToSend: {\n key: number;\n value: Omit<SendingSequencesReturn<number>, 'sequenceId'> & { tabId?: string };\n indexes: { sessionId: string | number };\n };\n}\n\nexport const defineObjectStores = (db: IDBPDatabase<SessionReplayDB>) => {\n let sequencesStore;\n let currentSequenceStore;\n if (!db.objectStoreNames.contains(currentSequenceKey)) {\n currentSequenceStore = db.createObjectStore(currentSequenceKey, {\n keyPath: 'sessionId',\n });\n }\n if (!db.objectStoreNames.contains(sequencesToSendKey)) {\n sequencesStore = db.createObjectStore(sequencesToSendKey, {\n keyPath: 'sequenceId',\n autoIncrement: true,\n });\n sequencesStore.createIndex('sessionId', 'sessionId');\n }\n return {\n sequencesStore,\n currentSequenceStore,\n };\n};\n\nexport const createStore = async (dbName: string) => {\n // Wrap openDB with a timeout so a hung connection (foreign tab holding an\n // open handle during version upgrade, or \"closing\" DB) doesn't block the\n // SDK from initialising. On timeout this rejects, which propagates up to\n // SessionReplayEventsIDBStore.new()'s catch block, returning undefined and\n // triggering the memory fallback.\n return await withTimeout(\n openDB<SessionReplayDB>(dbName, 1, {\n upgrade: defineObjectStores,\n }),\n OPEN_DB_TIMEOUT_MS,\n 'IDB openDB timed out',\n );\n};\n\ntype InstanceArgs = {\n apiKey: string;\n db: IDBPDatabase<SessionReplayDB>;\n tabId: string;\n onPersistentFailure?: () => void;\n consecutiveFailureThreshold?: number;\n} & BaseInstanceArgs;\n\nexport class SessionReplayEventsIDBStore extends BaseEventsStore<number> {\n private readonly db: IDBPDatabase<SessionReplayDB>;\n private readonly tabId: string;\n private readonly onPersistentFailure?: () => void;\n private readonly consecutiveFailureThreshold: number;\n private consecutiveFailures = 0;\n private hasTriggeredFallback = false;\n\n constructor(args: InstanceArgs) {\n super(args);\n this.db = args.db;\n this.tabId = args.tabId;\n this.onPersistentFailure = args.onPersistentFailure;\n // Default threshold of 1: fall back to memory immediately on the first IDB failure.\n // Session replay correctness is far more important than persistence, and IDB errors\n // are typically the symptom of a deeper problem (storage pressure, locked DB, broken\n // browser implementation) that won't recover within a single session. Memory store\n // is always safe — fall back early.\n this.consecutiveFailureThreshold = args.consecutiveFailureThreshold ?? 1;\n }\n\n private recordFailure() {\n this.consecutiveFailures++;\n if (!this.hasTriggeredFallback && this.consecutiveFailures >= this.consecutiveFailureThreshold) {\n this.hasTriggeredFallback = true;\n this.onPersistentFailure?.();\n }\n }\n\n private recordSuccess() {\n this.consecutiveFailures = 0;\n }\n\n static async new(\n type: EventType,\n args: Omit<InstanceArgs, 'db' | 'tabId'> & { tabId?: string },\n ): Promise<SessionReplayEventsIDBStore | undefined> {\n try {\n const dbSuffix = type === 'replay' ? '' : `_${type}`;\n const dbName = `${args.apiKey.substring(0, 10)}_amp_session_replay_events${dbSuffix}`;\n const db = await createStore(dbName);\n // Generate a fresh in-memory UUID per store instance. sessionStorage is\n // intentionally avoided: standalone session-replay customers (without the\n // analytics-browser SDK) would be exposed to a new storage surface they\n // did not consent to, and persistence across page reloads is not needed —\n // completed sequences in sequencesToSend are flushed by any tab/instance.\n const tabId = args.tabId ?? generateUUID();\n return new SessionReplayEventsIDBStore({\n ...args,\n db,\n tabId,\n });\n } catch (e) {\n logIdbError(args.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n }\n return;\n }\n\n async getCurrentSequenceEvents(sessionId?: number) {\n if (sessionId) {\n const record = await this.db.get('sessionCurrentSequence', sessionId);\n if (!record) {\n return undefined;\n }\n // Only return our own tab's record (or legacy untagged records).\n if (record.tabId && record.tabId !== this.tabId) {\n return undefined;\n }\n const { tabId: _tabId, ...rest } = record;\n return [rest];\n }\n\n const allEvents = [];\n for (const record of await this.db.getAll('sessionCurrentSequence')) {\n if (record.tabId && record.tabId !== this.tabId) {\n continue;\n }\n const { tabId: _tabId, ...rest } = record;\n allEvents.push(rest);\n }\n\n return allEvents;\n }\n\n getSequencesToSend = async (): Promise<SendingSequencesReturn<number>[] | undefined> => {\n let errorLogged = false;\n let timedOut = false;\n try {\n const sequences: SendingSequencesReturn<number>[] = [];\n const tx = this.db.transaction('sequencesToSend');\n // Attach a catch handler immediately so tx.done rejections (e.g. AbortError after\n // cursor traversal completes) are always handled without blocking the return path.\n // The errorLogged / timedOut flags prevent double-logging and double-recording\n // when the outer catch (or the timeout race) already fired for the same abort.\n tx.done.catch((e: unknown) => {\n if (!errorLogged && !timedOut) {\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n });\n // Arm a watchdog so a stalled transaction (no error, no commit, e.g.\n // storage pressure on some browsers) still trips the failure counter.\n // The watchdog fires only when tx.done genuinely never settles AND the\n // operation's success path didn't run; if tx.done rejects (abort), the\n // tx.done.catch handler above is the sole recorder of failure.\n const cancelTimeout = armTxDoneTimeout(tx.done, TX_DONE_TIMEOUT_MS, () => {\n if (!errorLogged && !timedOut) {\n timedOut = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: transaction timed out`);\n this.recordFailure();\n }\n });\n let cursor = await tx.store.openCursor();\n while (cursor) {\n const { sessionId, events } = cursor.value;\n // Return all completed sequences regardless of tabId. Filtering by tab\n // would cause event loss on page reload: a new store instance gets a\n // fresh in-memory UUID and would never see sequences written by the\n // previous instance. Completed sequences are safe to flush by any\n // tab/instance; the server deduplicates, and cleanUpSessionEventsStore\n // on an already-deleted key is a no-op.\n sequences.push({\n events,\n sequenceId: cursor.key,\n sessionId,\n });\n cursor = await cursor.continue();\n }\n\n this.recordSuccess();\n cancelTimeout();\n return sequences;\n } catch (e) {\n if (!timedOut) {\n errorLogged = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n }\n return undefined;\n };\n\n storeCurrentSequence = async (sessionId: number) => {\n let errorLogged = false;\n let timedOut = false;\n try {\n // Wrap the read of sessionCurrentSequence and the writes to sequencesToSend +\n // sessionCurrentSequence in a single readwrite transaction so the three operations\n // commit or roll back atomically. Without this, a concurrent addEventToCurrentSequence\n // call could interleave and either lose the events being promoted or duplicate them\n // (storeCurrentSequence reads N events, addEvent appends an N+1th, storeCurrentSequence\n // writes only the first N back to sequencesToSend, then resets the slot — losing N+1).\n const tx = this.db.transaction([currentSequenceKey, sequencesToSendKey], 'readwrite');\n tx.done.catch((e: unknown) => {\n if (!errorLogged && !timedOut) {\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n });\n // Stalled-transaction protection: see armTxDoneTimeout in getSequencesToSend.\n const cancelTimeout = armTxDoneTimeout(tx.done, TX_DONE_TIMEOUT_MS, () => {\n if (!errorLogged && !timedOut) {\n timedOut = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: transaction timed out`);\n this.recordFailure();\n }\n });\n\n const currentSequenceData = await tx.objectStore(currentSequenceKey).get(sessionId);\n // Skip promotion if the slot is empty or owned by another tab — let the owning\n // tab promote its own events on its next addEventToCurrentSequence/storeCurrentSequence\n // call (via Bug 1's foreign-tab promotion path).\n // Don't call recordSuccess() here: no write was performed, so this is not\n // evidence the storage layer is healthy — leave the failure counter unchanged.\n if (!currentSequenceData || (currentSequenceData.tabId && currentSequenceData.tabId !== this.tabId)) {\n cancelTimeout();\n return undefined;\n }\n\n // Skip empty sequences — no point writing a zero-event row to sequencesToSend.\n if (currentSequenceData.events.length === 0) {\n cancelTimeout();\n return undefined;\n }\n\n const sequenceId = await tx.objectStore(sequencesToSendKey).put({\n sessionId,\n events: currentSequenceData.events,\n tabId: this.tabId,\n });\n\n await tx.objectStore(currentSequenceKey).put({\n sessionId,\n events: [],\n tabId: this.tabId,\n });\n\n this.recordSuccess();\n cancelTimeout();\n const { tabId: _tabId, ...rest } = currentSequenceData;\n return {\n ...rest,\n sessionId,\n sequenceId,\n };\n } catch (e) {\n if (!timedOut) {\n errorLogged = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n }\n return undefined;\n };\n\n addEventToCurrentSequence = async (sessionId: number, event: string) => {\n let errorLogged = false;\n let timedOut = false;\n try {\n // Always open a readwrite transaction over both stores so that the read and\n // any subsequent write are atomic. IDB serializes readwrite transactions on\n // overlapping stores, so concurrent fire-and-forget callers (events-manager\n // does not await this method) are queued by the engine rather than interleaving\n // — eliminating the TOCTOU race that a narrow-read + separate-write approach\n // would introduce on the split path.\n const tx = this.db.transaction([currentSequenceKey, sequencesToSendKey], 'readwrite');\n // Attach a catch handler immediately so tx.done rejections (e.g. AbortError after\n // put succeeds but before auto-commit) are always handled without blocking.\n // The errorLogged / timedOut flags prevent double-logging when the outer catch\n // (or the timeout) already fired for the same transaction.\n tx.done.catch((e: unknown) => {\n if (!errorLogged && !timedOut) {\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n });\n // Stalled-transaction protection: see armTxDoneTimeout in getSequencesToSend.\n const cancelTimeout = armTxDoneTimeout(tx.done, TX_DONE_TIMEOUT_MS, () => {\n if (!errorLogged && !timedOut) {\n timedOut = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: transaction timed out`);\n this.recordFailure();\n }\n });\n const sequenceEvents = await tx.objectStore(currentSequenceKey).get(sessionId);\n\n // Foreign-tab record path: another tab owns the current-sequence slot for this\n // sessionId. Don't silently overwrite — that would drop the foreign tab's\n // in-progress events. Promote them to sequencesToSend (tabId kept for forensics)\n // before claiming the slot for ourselves. getSequencesToSend no longer filters\n // by tabId, so either tab may flush the promoted sequence; server deduplicates.\n if (sequenceEvents?.tabId && sequenceEvents.tabId !== this.tabId) {\n if (sequenceEvents.events.length > 0) {\n await tx.objectStore(sequencesToSendKey).put({\n sessionId,\n events: sequenceEvents.events,\n tabId: sequenceEvents.tabId,\n });\n }\n await tx.objectStore(currentSequenceKey).put({ sessionId, events: [event], tabId: this.tabId });\n this.recordSuccess();\n cancelTimeout();\n return undefined;\n }\n\n // ownedSequence is either undefined (no record yet) or this tab's record.\n const ownedSequence = sequenceEvents;\n\n if (!ownedSequence) {\n await tx.objectStore(currentSequenceKey).put({ sessionId, events: [event], tabId: this.tabId });\n this.recordSuccess();\n cancelTimeout();\n return undefined;\n }\n\n if (!this.shouldSplitEventsList(ownedSequence.events, event)) {\n await tx\n .objectStore(currentSequenceKey)\n .put({ sessionId, events: ownedSequence.events.concat(event), tabId: this.tabId });\n this.recordSuccess();\n cancelTimeout();\n return undefined;\n }\n\n // Split path: reset sessionCurrentSequence and write the old events to\n // sequencesToSend atomically within the same transaction.\n const eventsToSend = ownedSequence.events;\n await tx.objectStore(currentSequenceKey).put({ sessionId, events: [event], tabId: this.tabId });\n const sequenceId = await tx.objectStore(sequencesToSendKey).put({\n sessionId,\n events: eventsToSend,\n tabId: this.tabId,\n });\n\n this.recordSuccess();\n cancelTimeout();\n return {\n events: eventsToSend,\n sessionId,\n sequenceId,\n };\n } catch (e) {\n if (!timedOut) {\n errorLogged = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n }\n return undefined;\n };\n\n storeSendingEvents = async (sessionId: number, events: Events) => {\n try {\n const sequenceId = await this.db.put<'sequencesToSend'>(sequencesToSendKey, {\n sessionId: sessionId,\n events: events,\n tabId: this.tabId,\n });\n this.recordSuccess();\n return sequenceId;\n } catch (e) {\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n return undefined;\n };\n\n cleanUpSessionEventsStore = async (_sessionId: number, sequenceId?: number) => {\n if (!sequenceId) {\n return;\n }\n try {\n await this.db.delete<'sequencesToSend'>(sequencesToSendKey, sequenceId);\n this.recordSuccess();\n } catch (e) {\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n };\n}\n"]}
1
+ {"version":3,"file":"events-idb-store.js","sourceRoot":"","sources":["../../../src/events/events-idb-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { DBSchema, IDBPDatabase, openDB } from 'idb';\nimport { STORAGE_FAILURE } from '../messages';\nimport { EventType, Events, SendingSequencesReturn } from '../typings/session-replay';\nimport { BaseEventsStore, InstanceArgs as BaseInstanceArgs } from './base-events-store';\nimport { logIdbError } from '../utils/is-abort-error';\n\n// crypto.randomUUID() requires a secure context (https). Fall back to a\n// Math.random-based UUID for http origins or older browsers — tab IDs don't\n// need to be cryptographically secure, just unique within a session.\nexport function generateUUID(): string {\n try {\n return crypto.randomUUID();\n } catch {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);\n });\n }\n}\n\nexport const currentSequenceKey = 'sessionCurrentSequence';\nexport const sequencesToSendKey = 'sequencesToSend';\nexport const remoteConfigKey = 'remoteConfig';\n\n// Timeout for openDB at init. IDB openDB can hang forever when another tab\n// holds an open connection during a version upgrade, or when the DB is in a\n// \"closing\" state (documented Chrome behaviour). When that happens we want\n// SessionReplayEventsIDBStore.new() to bail out so the caller can fall back\n// to the in-memory store.\nexport const OPEN_DB_TIMEOUT_MS = 2000;\n\n// Timeout for per-operation tx.done settlement. Mid-recording a readwrite\n// transaction can stall (storage pressure in some browsers stalls instead of\n// throwing); without a timeout, recordFailure() is never called and the\n// memory fallback never triggers. The transaction may still settle later;\n// the timedOut flag prevents double-counting alongside errorLogged.\nexport const TX_DONE_TIMEOUT_MS = 5000;\n\n/**\n * Race a promise against a timeout. Resolves/rejects with the original\n * promise's value when it settles first; rejects with a timeout error if\n * `ms` elapses first. Either way the timer is cleared so we don't leak\n * pending setTimeouts.\n */\nexport function withTimeout<T>(promise: Promise<T>, ms: number, message = 'IDB operation timed out'): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error(`${message} after ${ms}ms`)), ms);\n promise.then(\n (v) => {\n clearTimeout(timer);\n resolve(v);\n },\n (e) => {\n clearTimeout(timer);\n reject(e);\n },\n );\n });\n}\n\n/**\n * Arms a watchdog that fires `onTimeout` only if `tx.done` hasn't settled\n * within `ms`. A \"soft cancel\" returned to the caller suppresses the timeout\n * if the synchronous operation body completes without exception — important\n * because in test environments fake timers can fire ahead of tx.done's commit\n * microtask. Production behaviour is preserved: a genuinely stalled\n * transaction (no commit, no error, individual op promises never resolved)\n * cannot reach the soft-cancel call and the timeout fires as designed.\n *\n * Soft-cancel is only invoked once the awaits inside the operation's outer\n * try block have all returned — i.e. the IDB driver acknowledged each\n * individual put/get. If the driver accepts a put but the underlying\n * transaction silently fails to commit (the production stall scenario),\n * tx.done still hasn't settled, and recordFailure must NOT have been\n * suppressed. The fix for that scenario in production is the tx.done.catch\n * handler attached separately by callers, which catches the eventual abort.\n *\n * The soft-cancel pattern only suppresses the timeout for the \"all puts\n * resolved successfully\" case — a case where tx.done is overwhelmingly\n * likely to settle imminently in production. If it doesn't, callers can\n * still detect the failure on the NEXT operation (which will fail to open\n * a transaction or hit the same pressure), because all three methods use\n * readwrite transactions on the same two stores, which IDB serializes:\n * if T1's tx.done never settles, T2 is blocked waiting for T1 to commit or\n * abort — T2's put/get requests never resolve, T2 never reaches its\n * soft-cancel, and T2's watchdog fires, calling recordFailure().\n */\nfunction armTxDoneTimeout(txDone: Promise<unknown>, ms: number, onTimeout: () => void): () => void {\n const timer = setTimeout(onTimeout, ms);\n // Belt-and-braces: clear the timer when tx.done settles, even though the\n // primary cancel path is the caller's success-path cancel(). This covers\n // the case where tx.done settles with no caller cancellation (shouldn't\n // happen in current code paths, but cheap insurance).\n txDone.then(\n () => clearTimeout(timer),\n () => clearTimeout(timer),\n );\n return () => clearTimeout(timer);\n}\n\nexport interface SessionReplayDB extends DBSchema {\n sessionCurrentSequence: {\n key: number;\n value: Omit<SendingSequencesReturn<number>, 'sequenceId'> & { tabId?: string };\n };\n sequencesToSend: {\n key: number;\n value: Omit<SendingSequencesReturn<number>, 'sequenceId'> & { tabId?: string };\n indexes: { sessionId: string | number };\n };\n}\n\nexport const defineObjectStores = (db: IDBPDatabase<SessionReplayDB>) => {\n let sequencesStore;\n let currentSequenceStore;\n if (!db.objectStoreNames.contains(currentSequenceKey)) {\n currentSequenceStore = db.createObjectStore(currentSequenceKey, {\n keyPath: 'sessionId',\n });\n }\n if (!db.objectStoreNames.contains(sequencesToSendKey)) {\n sequencesStore = db.createObjectStore(sequencesToSendKey, {\n keyPath: 'sequenceId',\n autoIncrement: true,\n });\n sequencesStore.createIndex('sessionId', 'sessionId');\n }\n return {\n sequencesStore,\n currentSequenceStore,\n };\n};\n\nexport const createStore = async (dbName: string) => {\n // Wrap openDB with a timeout so a hung connection (foreign tab holding an\n // open handle during version upgrade, or \"closing\" DB) doesn't block the\n // SDK from initialising. On timeout this rejects, which propagates up to\n // SessionReplayEventsIDBStore.new()'s catch block, returning undefined and\n // triggering the memory fallback.\n return await withTimeout(\n openDB<SessionReplayDB>(dbName, 1, {\n upgrade: defineObjectStores,\n }),\n OPEN_DB_TIMEOUT_MS,\n 'IDB openDB timed out',\n );\n};\n\ntype InstanceArgs = {\n apiKey: string;\n db: IDBPDatabase<SessionReplayDB>;\n tabId: string;\n onPersistentFailure?: () => void;\n consecutiveFailureThreshold?: number;\n} & BaseInstanceArgs;\n\nexport class SessionReplayEventsIDBStore extends BaseEventsStore<number> {\n private readonly db: IDBPDatabase<SessionReplayDB>;\n private readonly tabId: string;\n private readonly onPersistentFailure?: () => void;\n private readonly consecutiveFailureThreshold: number;\n private consecutiveFailures = 0;\n private hasTriggeredFallback = false;\n private emptyFilteredCount = 0;\n\n // Sampled (1 in 100) debug log so we can observe whether the store-layer guards\n // are catching empty-batch cases that would otherwise hit the empty-body 400 path\n // on the server. Logged at debug, not warn — this is operational telemetry for\n // post-deploy verification, not a customer-actionable warning. Per-store-instance\n // counter (rather than Math.random) keeps the first hit deterministic for tests.\n private maybeLogEmptyFiltered(source: string) {\n if (this.emptyFilteredCount++ % 100 === 0) {\n this.loggerProvider.debug(`Filtered empty session replay sequence at ${source} (idb store)`);\n }\n }\n\n constructor(args: InstanceArgs) {\n super(args);\n this.db = args.db;\n this.tabId = args.tabId;\n this.onPersistentFailure = args.onPersistentFailure;\n // Default threshold of 1: fall back to memory immediately on the first IDB failure.\n // Session replay correctness is far more important than persistence, and IDB errors\n // are typically the symptom of a deeper problem (storage pressure, locked DB, broken\n // browser implementation) that won't recover within a single session. Memory store\n // is always safe — fall back early.\n this.consecutiveFailureThreshold = args.consecutiveFailureThreshold ?? 1;\n }\n\n private recordFailure() {\n this.consecutiveFailures++;\n if (!this.hasTriggeredFallback && this.consecutiveFailures >= this.consecutiveFailureThreshold) {\n this.hasTriggeredFallback = true;\n this.onPersistentFailure?.();\n }\n }\n\n private recordSuccess() {\n this.consecutiveFailures = 0;\n }\n\n static async new(\n type: EventType,\n args: Omit<InstanceArgs, 'db' | 'tabId'> & { tabId?: string },\n ): Promise<SessionReplayEventsIDBStore | undefined> {\n try {\n const dbSuffix = type === 'replay' ? '' : `_${type}`;\n const dbName = `${args.apiKey.substring(0, 10)}_amp_session_replay_events${dbSuffix}`;\n const db = await createStore(dbName);\n // Generate a fresh in-memory UUID per store instance. sessionStorage is\n // intentionally avoided: standalone session-replay customers (without the\n // analytics-browser SDK) would be exposed to a new storage surface they\n // did not consent to, and persistence across page reloads is not needed —\n // completed sequences in sequencesToSend are flushed by any tab/instance.\n const tabId = args.tabId ?? generateUUID();\n return new SessionReplayEventsIDBStore({\n ...args,\n db,\n tabId,\n });\n } catch (e) {\n logIdbError(args.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n }\n return;\n }\n\n async getCurrentSequenceEvents(sessionId?: number) {\n if (sessionId) {\n const record = await this.db.get('sessionCurrentSequence', sessionId);\n if (!record) {\n return undefined;\n }\n // Only return our own tab's record (or legacy untagged records).\n if (record.tabId && record.tabId !== this.tabId) {\n return undefined;\n }\n const { tabId: _tabId, ...rest } = record;\n return [rest];\n }\n\n const allEvents = [];\n for (const record of await this.db.getAll('sessionCurrentSequence')) {\n if (record.tabId && record.tabId !== this.tabId) {\n continue;\n }\n const { tabId: _tabId, ...rest } = record;\n allEvents.push(rest);\n }\n\n return allEvents;\n }\n\n getSequencesToSend = async (): Promise<SendingSequencesReturn<number>[] | undefined> => {\n let errorLogged = false;\n let timedOut = false;\n try {\n const sequences: SendingSequencesReturn<number>[] = [];\n // readwrite mode so we can prune stale empty rows in-place. Without that,\n // older-SDK-persisted events:[] rows sit in IDB indefinitely and re-fire the\n // sampled log on every flush cycle, creating Datadog noise indistinguishable\n // from active bug occurrences.\n const tx = this.db.transaction('sequencesToSend', 'readwrite');\n // Attach a catch handler immediately so tx.done rejections (e.g. AbortError after\n // cursor traversal completes) are always handled without blocking the return path.\n // The errorLogged / timedOut flags prevent double-logging and double-recording\n // when the outer catch (or the timeout race) already fired for the same abort.\n tx.done.catch((e: unknown) => {\n if (!errorLogged && !timedOut) {\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n });\n // Arm a watchdog so a stalled transaction (no error, no commit, e.g.\n // storage pressure on some browsers) still trips the failure counter.\n // The watchdog fires only when tx.done genuinely never settles AND the\n // operation's success path didn't run; if tx.done rejects (abort), the\n // tx.done.catch handler above is the sole recorder of failure.\n const cancelTimeout = armTxDoneTimeout(tx.done, TX_DONE_TIMEOUT_MS, () => {\n if (!errorLogged && !timedOut) {\n timedOut = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: transaction timed out`);\n this.recordFailure();\n }\n });\n let cursor = await tx.store.openCursor();\n while (cursor) {\n const { sessionId, events } = cursor.value;\n // Skip empty persisted records. These can come from older SDK builds that\n // wrote zero-event sequences via addEventToCurrentSequence's split path\n // (when a single oversized event triggered a split with empty buffer);\n // flushing them produces empty-body POSTs the server rejects with 400.\n // Delete them in-place so they don't re-fire the sampled log on every\n // subsequent flush — by construction (this fix) we never write empty rows\n // anymore, so any empty row is unambiguously stale.\n if (events.length === 0) {\n this.maybeLogEmptyFiltered('getSequencesToSend');\n await cursor.delete();\n } else {\n // Return all completed sequences regardless of tabId. Filtering by tab\n // would cause event loss on page reload: a new store instance gets a\n // fresh in-memory UUID and would never see sequences written by the\n // previous instance. Completed sequences are safe to flush by any\n // tab/instance; the server deduplicates, and cleanUpSessionEventsStore\n // on an already-deleted key is a no-op.\n sequences.push({\n events,\n sequenceId: cursor.key,\n sessionId,\n });\n }\n cursor = await cursor.continue();\n }\n\n this.recordSuccess();\n cancelTimeout();\n return sequences;\n } catch (e) {\n if (!timedOut) {\n errorLogged = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n }\n return undefined;\n };\n\n storeCurrentSequence = async (sessionId: number) => {\n let errorLogged = false;\n let timedOut = false;\n try {\n // Wrap the read of sessionCurrentSequence and the writes to sequencesToSend +\n // sessionCurrentSequence in a single readwrite transaction so the three operations\n // commit or roll back atomically. Without this, a concurrent addEventToCurrentSequence\n // call could interleave and either lose the events being promoted or duplicate them\n // (storeCurrentSequence reads N events, addEvent appends an N+1th, storeCurrentSequence\n // writes only the first N back to sequencesToSend, then resets the slot — losing N+1).\n const tx = this.db.transaction([currentSequenceKey, sequencesToSendKey], 'readwrite');\n tx.done.catch((e: unknown) => {\n if (!errorLogged && !timedOut) {\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n });\n // Stalled-transaction protection: see armTxDoneTimeout in getSequencesToSend.\n const cancelTimeout = armTxDoneTimeout(tx.done, TX_DONE_TIMEOUT_MS, () => {\n if (!errorLogged && !timedOut) {\n timedOut = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: transaction timed out`);\n this.recordFailure();\n }\n });\n\n const currentSequenceData = await tx.objectStore(currentSequenceKey).get(sessionId);\n // Skip promotion if the slot is empty or owned by another tab — let the owning\n // tab promote its own events on its next addEventToCurrentSequence/storeCurrentSequence\n // call (via Bug 1's foreign-tab promotion path).\n // Don't call recordSuccess() here: no write was performed, so this is not\n // evidence the storage layer is healthy — leave the failure counter unchanged.\n if (!currentSequenceData || (currentSequenceData.tabId && currentSequenceData.tabId !== this.tabId)) {\n cancelTimeout();\n return undefined;\n }\n\n // Skip empty sequences — no point writing a zero-event row to sequencesToSend\n // (would later POST as an empty body and 400 on the server).\n if (currentSequenceData.events.length === 0) {\n this.maybeLogEmptyFiltered('storeCurrentSequence');\n cancelTimeout();\n return undefined;\n }\n\n const sequenceId = await tx.objectStore(sequencesToSendKey).put({\n sessionId,\n events: currentSequenceData.events,\n tabId: this.tabId,\n });\n\n await tx.objectStore(currentSequenceKey).put({\n sessionId,\n events: [],\n tabId: this.tabId,\n });\n\n this.recordSuccess();\n cancelTimeout();\n const { tabId: _tabId, ...rest } = currentSequenceData;\n return {\n ...rest,\n sessionId,\n sequenceId,\n };\n } catch (e) {\n if (!timedOut) {\n errorLogged = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n }\n return undefined;\n };\n\n addEventToCurrentSequence = async (sessionId: number, event: string) => {\n let errorLogged = false;\n let timedOut = false;\n try {\n // Always open a readwrite transaction over both stores so that the read and\n // any subsequent write are atomic. IDB serializes readwrite transactions on\n // overlapping stores, so concurrent fire-and-forget callers (events-manager\n // does not await this method) are queued by the engine rather than interleaving\n // — eliminating the TOCTOU race that a narrow-read + separate-write approach\n // would introduce on the split path.\n const tx = this.db.transaction([currentSequenceKey, sequencesToSendKey], 'readwrite');\n // Attach a catch handler immediately so tx.done rejections (e.g. AbortError after\n // put succeeds but before auto-commit) are always handled without blocking.\n // The errorLogged / timedOut flags prevent double-logging when the outer catch\n // (or the timeout) already fired for the same transaction.\n tx.done.catch((e: unknown) => {\n if (!errorLogged && !timedOut) {\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n });\n // Stalled-transaction protection: see armTxDoneTimeout in getSequencesToSend.\n const cancelTimeout = armTxDoneTimeout(tx.done, TX_DONE_TIMEOUT_MS, () => {\n if (!errorLogged && !timedOut) {\n timedOut = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: transaction timed out`);\n this.recordFailure();\n }\n });\n const sequenceEvents = await tx.objectStore(currentSequenceKey).get(sessionId);\n\n // Foreign-tab record path: another tab owns the current-sequence slot for this\n // sessionId. Don't silently overwrite — that would drop the foreign tab's\n // in-progress events. Promote them to sequencesToSend (tabId kept for forensics)\n // before claiming the slot for ourselves. getSequencesToSend no longer filters\n // by tabId, so either tab may flush the promoted sequence; server deduplicates.\n if (sequenceEvents?.tabId && sequenceEvents.tabId !== this.tabId) {\n if (sequenceEvents.events.length > 0) {\n await tx.objectStore(sequencesToSendKey).put({\n sessionId,\n events: sequenceEvents.events,\n tabId: sequenceEvents.tabId,\n });\n }\n await tx.objectStore(currentSequenceKey).put({ sessionId, events: [event], tabId: this.tabId });\n this.recordSuccess();\n cancelTimeout();\n return undefined;\n }\n\n // ownedSequence is either undefined (no record yet) or this tab's record.\n const ownedSequence = sequenceEvents;\n\n if (!ownedSequence) {\n await tx.objectStore(currentSequenceKey).put({ sessionId, events: [event], tabId: this.tabId });\n this.recordSuccess();\n cancelTimeout();\n return undefined;\n }\n\n if (!this.shouldSplitEventsList(ownedSequence.events, event)) {\n await tx\n .objectStore(currentSequenceKey)\n .put({ sessionId, events: ownedSequence.events.concat(event), tabId: this.tabId });\n this.recordSuccess();\n cancelTimeout();\n return undefined;\n }\n\n // Split path: reset sessionCurrentSequence and write the old events to\n // sequencesToSend atomically within the same transaction.\n const eventsToSend = ownedSequence.events;\n\n // shouldSplitEventsList can return true with an empty buffer when a single\n // incoming event is larger than MAX_EVENT_LIST_SIZE (700 KB) — the size-constraint\n // branch fires regardless of current length. Don't write a zero-event row to\n // sequencesToSend (which would later POST as an empty body, the SR-4284 root\n // cause); just claim the slot for the new event without finalizing anything.\n // This is the *primary* root-cause filter site: warn here so post-deploy\n // Datadog can confirm the new SDK is actually preventing the bug at its source\n // (vs. only seeing leftover-state hits at the get/storeCurrentSequence layers).\n if (eventsToSend.length === 0) {\n this.maybeLogEmptyFiltered('addEventToCurrentSequence');\n await tx.objectStore(currentSequenceKey).put({ sessionId, events: [event], tabId: this.tabId });\n this.recordSuccess();\n cancelTimeout();\n return undefined;\n }\n\n await tx.objectStore(currentSequenceKey).put({ sessionId, events: [event], tabId: this.tabId });\n const sequenceId = await tx.objectStore(sequencesToSendKey).put({\n sessionId,\n events: eventsToSend,\n tabId: this.tabId,\n });\n\n this.recordSuccess();\n cancelTimeout();\n return {\n events: eventsToSend,\n sessionId,\n sequenceId,\n };\n } catch (e) {\n if (!timedOut) {\n errorLogged = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n }\n return undefined;\n };\n\n storeSendingEvents = async (sessionId: number, events: Events) => {\n try {\n const sequenceId = await this.db.put<'sequencesToSend'>(sequencesToSendKey, {\n sessionId: sessionId,\n events: events,\n tabId: this.tabId,\n });\n this.recordSuccess();\n return sequenceId;\n } catch (e) {\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n return undefined;\n };\n\n cleanUpSessionEventsStore = async (_sessionId: number, sequenceId?: number) => {\n if (!sequenceId) {\n return;\n }\n try {\n await this.db.delete<'sequencesToSend'>(sequencesToSendKey, sequenceId);\n this.recordSuccess();\n } catch (e) {\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n };\n}\n"]}
@@ -4,8 +4,10 @@ export declare class InMemoryEventsStore extends BaseEventsStore<number> {
4
4
  private finalizedSequences;
5
5
  private sequences;
6
6
  private sequenceId;
7
+ private emptyFilteredCount;
7
8
  private resetCurrentSequence;
8
9
  private addSequence;
10
+ private maybeLogEmptyFiltered;
9
11
  getSequencesToSend(): Promise<SendingSequencesReturn<number>[] | undefined>;
10
12
  storeCurrentSequence(sessionId: string | number): Promise<SendingSequencesReturn<number> | undefined>;
11
13
  addEventToCurrentSequence(sessionId: number, event: string): Promise<SendingSequencesReturn<number> | undefined>;
@@ -1 +1 @@
1
- {"version":3,"file":"events-memory-store.d.ts","sourceRoot":"","sources":["../../../src/events/events-memory-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,qBAAa,mBAAoB,SAAQ,eAAe,CAAC,MAAM,CAAC;IAC9D,OAAO,CAAC,kBAAkB,CAAwE;IAClG,OAAO,CAAC,SAAS,CAAyC;IAC1D,OAAO,CAAC,UAAU,CAAK;IAEvB,OAAO,CAAC,oBAAoB;IAI5B,OAAO,CAAC,WAAW;IAQb,kBAAkB,IAAI,OAAO,CAAC,sBAAsB,CAAC,MAAM,CAAC,EAAE,GAAG,SAAS,CAAC;IAQ3E,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAOrG,yBAAyB,CAC7B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,sBAAsB,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAehD,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAMlF,yBAAyB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAKxF"}
1
+ {"version":3,"file":"events-memory-store.d.ts","sourceRoot":"","sources":["../../../src/events/events-memory-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,qBAAa,mBAAoB,SAAQ,eAAe,CAAC,MAAM,CAAC;IAC9D,OAAO,CAAC,kBAAkB,CAAwE;IAClG,OAAO,CAAC,SAAS,CAAyC;IAC1D,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,kBAAkB,CAAK;IAE/B,OAAO,CAAC,oBAAoB;IAI5B,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,qBAAqB;IAMvB,kBAAkB,IAAI,OAAO,CAAC,sBAAsB,CAAC,MAAM,CAAC,EAAE,GAAG,SAAS,CAAC;IAkB3E,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAcrG,yBAAyB,CAC7B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,sBAAsB,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAyBhD,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAMlF,yBAAyB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAKxF"}
@@ -10,6 +10,7 @@ var InMemoryEventsStore = /** @class */ (function (_super) {
10
10
  _this.finalizedSequences = {};
11
11
  _this.sequences = {};
12
12
  _this.sequenceId = 0;
13
+ _this.emptyFilteredCount = 0;
13
14
  return _this;
14
15
  }
15
16
  InMemoryEventsStore.prototype.resetCurrentSequence = function (sessionId) {
@@ -22,24 +23,61 @@ var InMemoryEventsStore = /** @class */ (function (_super) {
22
23
  this.resetCurrentSequence(sessionId);
23
24
  return { sequenceId: sequenceId, events: events, sessionId: sessionId };
24
25
  };
26
+ // Sampled (1 in 100) debug log so we can observe whether the store-layer guards
27
+ // are actually catching cases that would otherwise hit the empty-body 400 path on
28
+ // the server. Logged at debug, not warn — this is operational telemetry for
29
+ // post-deploy verification, not a customer-actionable warning. Per-store-instance
30
+ // counter rather than Math.random keeps the first hit deterministic for tests.
31
+ InMemoryEventsStore.prototype.maybeLogEmptyFiltered = function (source) {
32
+ if (this.emptyFilteredCount++ % 100 === 0) {
33
+ this.loggerProvider.debug("Filtered empty session replay sequence at ".concat(source, " (in-memory store)"));
34
+ }
35
+ };
25
36
  InMemoryEventsStore.prototype.getSequencesToSend = function () {
26
37
  return tslib_1.__awaiter(this, void 0, void 0, function () {
27
- return tslib_1.__generator(this, function (_a) {
28
- return [2 /*return*/, Object.entries(this.finalizedSequences).map(function (_a) {
29
- var _b = tslib_1.__read(_a, 2), sequenceId = _b[0], _c = _b[1], sessionId = _c.sessionId, events = _c.events;
30
- return ({
31
- sequenceId: Number(sequenceId),
32
- sessionId: sessionId,
33
- events: events,
34
- });
35
- })];
38
+ var result, _a, _b, _c, sequenceId, _d, sessionId, events;
39
+ var e_1, _e;
40
+ return tslib_1.__generator(this, function (_f) {
41
+ result = [];
42
+ try {
43
+ for (_a = tslib_1.__values(Object.entries(this.finalizedSequences)), _b = _a.next(); !_b.done; _b = _a.next()) {
44
+ _c = tslib_1.__read(_b.value, 2), sequenceId = _c[0], _d = _c[1], sessionId = _d.sessionId, events = _d.events;
45
+ if (events.length === 0) {
46
+ // Prune in-place for consistency with the IDB store: by construction we
47
+ // never write empty sequences anymore, so any empty entry is unambiguously
48
+ // stale residue. Without the delete, every subsequent getSequencesToSend
49
+ // would re-iterate the empty entry and re-fire the sampled log, producing
50
+ // repeated noise that's indistinguishable from active bug occurrences.
51
+ this.maybeLogEmptyFiltered('getSequencesToSend');
52
+ delete this.finalizedSequences[Number(sequenceId)];
53
+ continue;
54
+ }
55
+ result.push({ sequenceId: Number(sequenceId), sessionId: sessionId, events: events });
56
+ }
57
+ }
58
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
59
+ finally {
60
+ try {
61
+ if (_b && !_b.done && (_e = _a.return)) _e.call(_a);
62
+ }
63
+ finally { if (e_1) throw e_1.error; }
64
+ }
65
+ return [2 /*return*/, result];
36
66
  });
37
67
  });
38
68
  };
39
69
  InMemoryEventsStore.prototype.storeCurrentSequence = function (sessionId) {
40
70
  return tslib_1.__awaiter(this, void 0, void 0, function () {
71
+ var buffered;
41
72
  return tslib_1.__generator(this, function (_a) {
42
- if (!this.sequences[sessionId]) {
73
+ buffered = this.sequences[sessionId];
74
+ if (!buffered) {
75
+ return [2 /*return*/, undefined];
76
+ }
77
+ if (buffered.length === 0) {
78
+ // Slot exists but is empty (e.g. drained by a prior storeCurrentSequence then
79
+ // re-flushed before any new event landed). Don't finalize a zero-event row.
80
+ this.maybeLogEmptyFiltered('storeCurrentSequence');
43
81
  return [2 /*return*/, undefined];
44
82
  }
45
83
  return [2 /*return*/, this.addSequence(sessionId)];
@@ -53,8 +91,19 @@ var InMemoryEventsStore = /** @class */ (function (_super) {
53
91
  if (!this.sequences[sessionId]) {
54
92
  this.resetCurrentSequence(sessionId);
55
93
  }
94
+ // shouldSplitEventsList can return true with an empty buffer when a single
95
+ // incoming event is larger than MAX_EVENT_LIST_SIZE (700 KB) — the size-constraint
96
+ // branch fires regardless of current length. Don't finalize a zero-event sequence
97
+ // (the SR-4284 root cause); just hold the incoming event in the buffer.
98
+ // shouldSplitEventsList's time-elapsed branch only fires when events.length > 0
99
+ // (see base-events-store.ts), so calling it on an empty buffer has no side effects.
56
100
  if (this.shouldSplitEventsList(this.sequences[sessionId], event)) {
57
- sequenceReturn = this.addSequence(sessionId);
101
+ if (this.sequences[sessionId].length === 0) {
102
+ this.maybeLogEmptyFiltered('addEventToCurrentSequence');
103
+ }
104
+ else {
105
+ sequenceReturn = this.addSequence(sessionId);
106
+ }
58
107
  }
59
108
  this.sequences[sessionId].push(event);
60
109
  return [2 /*return*/, sequenceReturn];
@@ -1 +1 @@
1
- {"version":3,"file":"events-memory-store.js","sourceRoot":"","sources":["../../../src/events/events-memory-store.ts"],"names":[],"mappings":";;;;AACA,yDAAsD;AAEtD;IAAyC,+CAAuB;IAAhE;QAAA,qEA6DC;QA5DS,wBAAkB,GAAqE,EAAE,CAAC;QAC1F,eAAS,GAAsC,EAAE,CAAC;QAClD,gBAAU,GAAG,CAAC,CAAC;;IA0DzB,CAAC;IAxDS,kDAAoB,GAA5B,UAA6B,SAA0B;QACrD,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC;IACjC,CAAC;IAEO,yCAAW,GAAnB,UAAoB,SAA0B;QAC5C,IAAM,UAAU,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QACrC,IAAM,MAAM,4CAAO,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,SAAC,CAAC;QAC9C,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,GAAG,EAAE,SAAS,WAAA,EAAE,MAAM,QAAA,EAAE,CAAC;QAC5D,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC;QACrC,OAAO,EAAE,UAAU,YAAA,EAAE,MAAM,QAAA,EAAE,SAAS,WAAA,EAAE,CAAC;IAC3C,CAAC;IAEK,gDAAkB,GAAxB;;;gBACE,sBAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,GAAG,CAAC,UAAC,EAAmC;4BAAnC,KAAA,qBAAmC,EAAlC,UAAU,QAAA,EAAE,UAAqB,EAAnB,SAAS,eAAA,EAAE,MAAM,YAAA;wBAAQ,OAAA,CAAC;4BAC3F,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;4BAC9B,SAAS,WAAA;4BACT,MAAM,QAAA;yBACP,CAAC;oBAJ0F,CAI1F,CAAC,EAAC;;;KACL;IAEK,kDAAoB,GAA1B,UAA2B,SAA0B;;;gBACnD,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE;oBAC9B,sBAAO,SAAS,EAAC;iBAClB;gBACD,sBAAO,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,EAAC;;;KACpC;IAEK,uDAAyB,GAA/B,UACE,SAAiB,EACjB,KAAa;;;;gBAEb,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE;oBAC9B,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC;iBACtC;gBAGD,IAAI,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,EAAE;oBAChE,cAAc,GAAG,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;iBAC9C;gBAED,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAEtC,sBAAO,cAAc,EAAC;;;KACvB;IAEK,gDAAkB,GAAxB,UAAyB,SAAiB,EAAE,MAAc;;;gBACxD,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,SAAS,WAAA,EAAE,MAAM,QAAA,EAAE,CAAC;gBAEjE,sBAAO,IAAI,CAAC,UAAU,EAAE,EAAC;;;KAC1B;IAEK,uDAAyB,GAA/B,UAAgC,UAAkB,EAAE,UAAmB;;;gBACrE,IAAI,UAAU,KAAK,SAAS,EAAE;oBAC5B,OAAO,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC;iBAC5C;;;;KACF;IACH,0BAAC;AAAD,CAAC,AA7DD,CAAyC,mCAAe,GA6DvD;AA7DY,kDAAmB","sourcesContent":["import { Events, SendingSequencesReturn } from '../typings/session-replay';\nimport { BaseEventsStore } from './base-events-store';\n\nexport class InMemoryEventsStore extends BaseEventsStore<number> {\n private finalizedSequences: Record<number, { sessionId: string | number; events: string[] }> = {};\n private sequences: Record<string | number, string[]> = {};\n private sequenceId = 0;\n\n private resetCurrentSequence(sessionId: string | number) {\n this.sequences[sessionId] = [];\n }\n\n private addSequence(sessionId: string | number): SendingSequencesReturn<number> {\n const sequenceId = this.sequenceId++;\n const events = [...this.sequences[sessionId]];\n this.finalizedSequences[sequenceId] = { sessionId, events };\n this.resetCurrentSequence(sessionId);\n return { sequenceId, events, sessionId };\n }\n\n async getSequencesToSend(): Promise<SendingSequencesReturn<number>[] | undefined> {\n return Object.entries(this.finalizedSequences).map(([sequenceId, { sessionId, events }]) => ({\n sequenceId: Number(sequenceId),\n sessionId,\n events,\n }));\n }\n\n async storeCurrentSequence(sessionId: string | number): Promise<SendingSequencesReturn<number> | undefined> {\n if (!this.sequences[sessionId]) {\n return undefined;\n }\n return this.addSequence(sessionId);\n }\n\n async addEventToCurrentSequence(\n sessionId: number,\n event: string,\n ): Promise<SendingSequencesReturn<number> | undefined> {\n if (!this.sequences[sessionId]) {\n this.resetCurrentSequence(sessionId);\n }\n\n let sequenceReturn: SendingSequencesReturn<number> | undefined;\n if (this.shouldSplitEventsList(this.sequences[sessionId], event)) {\n sequenceReturn = this.addSequence(sessionId);\n }\n\n this.sequences[sessionId].push(event);\n\n return sequenceReturn;\n }\n\n async storeSendingEvents(sessionId: number, events: Events): Promise<number | undefined> {\n this.finalizedSequences[this.sequenceId] = { sessionId, events };\n\n return this.sequenceId++;\n }\n\n async cleanUpSessionEventsStore(_sessionId: number, sequenceId?: number): Promise<void> {\n if (sequenceId !== undefined) {\n delete this.finalizedSequences[sequenceId];\n }\n }\n}\n"]}
1
+ {"version":3,"file":"events-memory-store.js","sourceRoot":"","sources":["../../../src/events/events-memory-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { Events, SendingSequencesReturn } from '../typings/session-replay';\nimport { BaseEventsStore } from './base-events-store';\n\nexport class InMemoryEventsStore extends BaseEventsStore<number> {\n private finalizedSequences: Record<number, { sessionId: string | number; events: string[] }> = {};\n private sequences: Record<string | number, string[]> = {};\n private sequenceId = 0;\n private emptyFilteredCount = 0;\n\n private resetCurrentSequence(sessionId: string | number) {\n this.sequences[sessionId] = [];\n }\n\n private addSequence(sessionId: string | number): SendingSequencesReturn<number> {\n const sequenceId = this.sequenceId++;\n const events = [...this.sequences[sessionId]];\n this.finalizedSequences[sequenceId] = { sessionId, events };\n this.resetCurrentSequence(sessionId);\n return { sequenceId, events, sessionId };\n }\n\n // Sampled (1 in 100) debug log so we can observe whether the store-layer guards\n // are actually catching cases that would otherwise hit the empty-body 400 path on\n // the server. Logged at debug, not warn — this is operational telemetry for\n // post-deploy verification, not a customer-actionable warning. Per-store-instance\n // counter rather than Math.random keeps the first hit deterministic for tests.\n private maybeLogEmptyFiltered(source: string) {\n if (this.emptyFilteredCount++ % 100 === 0) {\n this.loggerProvider.debug(`Filtered empty session replay sequence at ${source} (in-memory store)`);\n }\n }\n\n async getSequencesToSend(): Promise<SendingSequencesReturn<number>[] | undefined> {\n const result: SendingSequencesReturn<number>[] = [];\n for (const [sequenceId, { sessionId, events }] of Object.entries(this.finalizedSequences)) {\n if (events.length === 0) {\n // Prune in-place for consistency with the IDB store: by construction we\n // never write empty sequences anymore, so any empty entry is unambiguously\n // stale residue. Without the delete, every subsequent getSequencesToSend\n // would re-iterate the empty entry and re-fire the sampled log, producing\n // repeated noise that's indistinguishable from active bug occurrences.\n this.maybeLogEmptyFiltered('getSequencesToSend');\n delete this.finalizedSequences[Number(sequenceId)];\n continue;\n }\n result.push({ sequenceId: Number(sequenceId), sessionId, events });\n }\n return result;\n }\n\n async storeCurrentSequence(sessionId: string | number): Promise<SendingSequencesReturn<number> | undefined> {\n const buffered = this.sequences[sessionId];\n if (!buffered) {\n return undefined;\n }\n if (buffered.length === 0) {\n // Slot exists but is empty (e.g. drained by a prior storeCurrentSequence then\n // re-flushed before any new event landed). Don't finalize a zero-event row.\n this.maybeLogEmptyFiltered('storeCurrentSequence');\n return undefined;\n }\n return this.addSequence(sessionId);\n }\n\n async addEventToCurrentSequence(\n sessionId: number,\n event: string,\n ): Promise<SendingSequencesReturn<number> | undefined> {\n if (!this.sequences[sessionId]) {\n this.resetCurrentSequence(sessionId);\n }\n\n let sequenceReturn: SendingSequencesReturn<number> | undefined;\n // shouldSplitEventsList can return true with an empty buffer when a single\n // incoming event is larger than MAX_EVENT_LIST_SIZE (700 KB) — the size-constraint\n // branch fires regardless of current length. Don't finalize a zero-event sequence\n // (the SR-4284 root cause); just hold the incoming event in the buffer.\n // shouldSplitEventsList's time-elapsed branch only fires when events.length > 0\n // (see base-events-store.ts), so calling it on an empty buffer has no side effects.\n if (this.shouldSplitEventsList(this.sequences[sessionId], event)) {\n if (this.sequences[sessionId].length === 0) {\n this.maybeLogEmptyFiltered('addEventToCurrentSequence');\n } else {\n sequenceReturn = this.addSequence(sessionId);\n }\n }\n\n this.sequences[sessionId].push(event);\n\n return sequenceReturn;\n }\n\n async storeSendingEvents(sessionId: number, events: Events): Promise<number | undefined> {\n this.finalizedSequences[this.sequenceId] = { sessionId, events };\n\n return this.sequenceId++;\n }\n\n async cleanUpSessionEventsStore(_sessionId: number, sequenceId?: number): Promise<void> {\n if (sequenceId !== undefined) {\n delete this.finalizedSequences[sequenceId];\n }\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"session-replay.d.ts","sourceRoot":"","sources":["../../src/session-replay.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-replay.d.ts","sourceRoot":"","sources":["../../src/session-replay.ts"],"names":[],"mappings":"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"}