@amplitude/session-replay-browser 1.51.0 → 1.51.2

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 (66) hide show
  1. package/lib/cjs/events/base-events-store.d.ts +8 -5
  2. package/lib/cjs/events/base-events-store.d.ts.map +1 -1
  3. package/lib/cjs/events/base-events-store.js +51 -55
  4. package/lib/cjs/events/base-events-store.js.map +1 -1
  5. package/lib/cjs/events/event-compressor.d.ts.map +1 -1
  6. package/lib/cjs/events/event-compressor.js +4 -2
  7. package/lib/cjs/events/event-compressor.js.map +1 -1
  8. package/lib/cjs/events/events-idb-store.d.ts.map +1 -1
  9. package/lib/cjs/events/events-idb-store.js +23 -8
  10. package/lib/cjs/events/events-idb-store.js.map +1 -1
  11. package/lib/cjs/events/events-manager.d.ts.map +1 -1
  12. package/lib/cjs/events/events-manager.js +18 -6
  13. package/lib/cjs/events/events-manager.js.map +1 -1
  14. package/lib/cjs/observers.d.ts.map +1 -1
  15. package/lib/cjs/observers.js +2 -22
  16. package/lib/cjs/observers.js.map +1 -1
  17. package/lib/cjs/track-destination.d.ts.map +1 -1
  18. package/lib/cjs/track-destination.js +6 -5
  19. package/lib/cjs/track-destination.js.map +1 -1
  20. package/lib/cjs/utils/utf8-byte-length.d.ts +17 -0
  21. package/lib/cjs/utils/utf8-byte-length.d.ts.map +1 -0
  22. package/lib/cjs/utils/utf8-byte-length.js +104 -0
  23. package/lib/cjs/utils/utf8-byte-length.js.map +1 -0
  24. package/lib/cjs/version.d.ts +1 -1
  25. package/lib/cjs/version.js +1 -1
  26. package/lib/cjs/version.js.map +1 -1
  27. package/lib/cjs/worker/index.js +1 -1
  28. package/lib/esm/events/base-events-store.d.ts +8 -5
  29. package/lib/esm/events/base-events-store.d.ts.map +1 -1
  30. package/lib/esm/events/base-events-store.js +51 -55
  31. package/lib/esm/events/base-events-store.js.map +1 -1
  32. package/lib/esm/events/event-compressor.d.ts.map +1 -1
  33. package/lib/esm/events/event-compressor.js +4 -2
  34. package/lib/esm/events/event-compressor.js.map +1 -1
  35. package/lib/esm/events/events-idb-store.d.ts.map +1 -1
  36. package/lib/esm/events/events-idb-store.js +23 -8
  37. package/lib/esm/events/events-idb-store.js.map +1 -1
  38. package/lib/esm/events/events-manager.d.ts.map +1 -1
  39. package/lib/esm/events/events-manager.js +18 -6
  40. package/lib/esm/events/events-manager.js.map +1 -1
  41. package/lib/esm/observers.d.ts.map +1 -1
  42. package/lib/esm/observers.js +2 -22
  43. package/lib/esm/observers.js.map +1 -1
  44. package/lib/esm/track-destination.d.ts.map +1 -1
  45. package/lib/esm/track-destination.js +6 -5
  46. package/lib/esm/track-destination.js.map +1 -1
  47. package/lib/esm/utils/utf8-byte-length.d.ts +17 -0
  48. package/lib/esm/utils/utf8-byte-length.d.ts.map +1 -0
  49. package/lib/esm/utils/utf8-byte-length.js +97 -0
  50. package/lib/esm/utils/utf8-byte-length.js.map +1 -0
  51. package/lib/esm/version.d.ts +1 -1
  52. package/lib/esm/version.js +1 -1
  53. package/lib/esm/version.js.map +1 -1
  54. package/lib/esm/worker/index.js +1 -1
  55. package/lib/scripts/index-min.js +1 -1
  56. package/lib/scripts/index-min.js.gz +0 -0
  57. package/lib/scripts/index-min.js.map +1 -1
  58. package/lib/scripts/observers-min.js +1 -1
  59. package/lib/scripts/observers-min.js.gz +0 -0
  60. package/lib/scripts/observers-min.js.map +1 -1
  61. package/lib/scripts/session-replay-browser-min.js +1 -1
  62. package/lib/scripts/session-replay-browser-min.js.gz +0 -0
  63. package/lib/scripts/session-replay-browser-min.js.map +1 -1
  64. package/lib/scripts/worker-min.js +1 -1
  65. package/lib/scripts/worker-min.js.gz +0 -0
  66. package/package.json +4 -4
@@ -13,6 +13,8 @@ export declare abstract class BaseEventsStore<KeyType> implements EventsStore<Ke
13
13
  private maxPersistedEventsSize;
14
14
  private interval;
15
15
  private _timeAtLastSplit;
16
+ private readonly eventsContentBytes;
17
+ private readonly sequenceContentBytes;
16
18
  get timeAtLastSplit(): number;
17
19
  constructor(args: InstanceArgs);
18
20
  abstract addEventToCurrentSequence(sessionId: string | number, event: string): Promise<SendingSequencesReturn<KeyType> | undefined>;
@@ -20,16 +22,17 @@ export declare abstract class BaseEventsStore<KeyType> implements EventsStore<Ke
20
22
  abstract storeCurrentSequence(sessionId: number): Promise<SendingSequencesReturn<KeyType> | undefined>;
21
23
  abstract storeSendingEvents(sessionId: string | number, events: Events): Promise<KeyType | undefined>;
22
24
  abstract cleanUpSessionEventsStore(sessionId: number, sequenceId: KeyType): Promise<void>;
23
- /**
24
- * Returns the UTF-8 byte size of a string without buffer allocation, matching the
25
- * actual wire byte count for non-ASCII content (Base64 image data, emoji, etc.).
26
- */
27
- private getStringSize;
25
+ private isPrefixOf;
26
+ private measureContentBytes;
27
+ private getEventsContentBytes;
28
28
  /**
29
29
  * Calculates the total UTF-8 byte size of events array
30
30
  * Accounts for JSON serialization overhead when sent to backend
31
31
  */
32
32
  private getEventsArraySize;
33
+ protected recallSequenceContentBytes(sessionId: string | number, events: Events): void;
34
+ protected rememberSequenceContentBytes(sessionId: string | number, events: Events): void;
35
+ protected forgetSequenceContentBytes(sessionId: string | number): void;
33
36
  /**
34
37
  * Determines whether to send the events list to the backend and start a new
35
38
  * empty events list, based on the size of the list as well as the last time sent
@@ -1 +1 @@
1
- {"version":3,"file":"base-events-store.d.ts","sourceRoot":"","sources":["../../../src/events/base-events-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAEpD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAExF,MAAM,MAAM,YAAY,GAAG;IACzB,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC,CAAC;AAEF,8BAAsB,eAAe,CAAC,OAAO,CAAE,YAAW,WAAW,CAAC,OAAO,CAAC;IAC5E,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IAC3C,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,sBAAsB,CAA2C;IAKzE,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,gBAAgB,CAAc;IAEtC,IAAW,eAAe,WAEzB;gBAEW,IAAI,EAAE,YAAY;IAQ9B,QAAQ,CAAC,yBAAyB,CAChC,SAAS,EAAE,MAAM,GAAG,MAAM,EAC1B,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,sBAAsB,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC;IACvD,QAAQ,CAAC,kBAAkB,IAAI,OAAO,CAAC,sBAAsB,CAAC,OAAO,CAAC,EAAE,GAAG,SAAS,CAAC;IACrF,QAAQ,CAAC,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC;IACtG,QAAQ,CAAC,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IACrG,QAAQ,CAAC,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAEzF;;;OAGG;IACH,OAAO,CAAC,aAAa;IA8BrB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAkB1B;;;;;OAKG;IACH,qBAAqB,oCAAqC,MAAM,KAAG,OAAO,CAcxE;CACH"}
1
+ {"version":3,"file":"base-events-store.d.ts","sourceRoot":"","sources":["../../../src/events/base-events-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAEpD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AASxF,MAAM,MAAM,YAAY,GAAG;IACzB,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC,CAAC;AAEF,8BAAsB,eAAe,CAAC,OAAO,CAAE,YAAW,WAAW,CAAC,OAAO,CAAC;IAC5E,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IAC3C,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,sBAAsB,CAA2C;IAKzE,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,gBAAgB,CAAc;IAGtC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAgD;IACnF,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAqD;IAE1F,IAAW,eAAe,WAEzB;gBAEW,IAAI,EAAE,YAAY;IAQ9B,QAAQ,CAAC,yBAAyB,CAChC,SAAS,EAAE,MAAM,GAAG,MAAM,EAC1B,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,sBAAsB,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC;IACvD,QAAQ,CAAC,kBAAkB,IAAI,OAAO,CAAC,sBAAsB,CAAC,OAAO,CAAC,EAAE,GAAG,SAAS,CAAC;IACrF,QAAQ,CAAC,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC;IACtG,QAAQ,CAAC,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IACrG,QAAQ,CAAC,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzF,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,mBAAmB;IAc3B,OAAO,CAAC,qBAAqB;IAM7B;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAe1B,SAAS,CAAC,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAMtF,SAAS,CAAC,4BAA4B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAMxF,SAAS,CAAC,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAItE;;;;;OAKG;IACH,qBAAqB,oCAAqC,MAAM,KAAG,OAAO,CAiBxE;CACH"}
@@ -1,5 +1,5 @@
1
- import { __values } from "tslib";
2
1
  import { DEFAULT_MAX_PERSISTED_EVENTS_SIZE_BYTES, MAX_INTERVAL, MIN_INTERVAL } from '../constants';
2
+ import { utf8ByteLength } from '../utils/utf8-byte-length';
3
3
  var BaseEventsStore = /** @class */ (function () {
4
4
  function BaseEventsStore(args) {
5
5
  var _this = this;
@@ -8,6 +8,10 @@ var BaseEventsStore = /** @class */ (function () {
8
8
  this.maxInterval = MAX_INTERVAL;
9
9
  this.maxPersistedEventsSize = DEFAULT_MAX_PERSISTED_EVENTS_SIZE_BYTES;
10
10
  this._timeAtLastSplit = Date.now(); // Initialize this so we have a point of comparison when events are recorded
11
+ // Running UTF-8 sizes so shouldSplitEventsList only measures newly appended events.
12
+ // The IDB store loads a fresh array on every add, so it also keeps a per-session entry.
13
+ this.eventsContentBytes = new WeakMap();
14
+ this.sequenceContentBytes = new Map();
11
15
  /**
12
16
  * Determines whether to send the events list to the backend and start a new
13
17
  * empty events list, based on the size of the list as well as the last time sent
@@ -15,10 +19,14 @@ var BaseEventsStore = /** @class */ (function () {
15
19
  * @returns boolean
16
20
  */
17
21
  this.shouldSplitEventsList = function (events, nextEventString) {
18
- var sizeOfNextEvent = _this.getStringSize(nextEventString);
19
22
  var sizeOfEventsList = _this.getEventsArraySize(events);
20
- // Check size constraint first (most likely to trigger)
21
- if (sizeOfEventsList + sizeOfNextEvent >= _this.maxPersistedEventsSize) {
23
+ // 1–3 UTF-8 bytes per code unit: only scan the event when `length` alone cannot decide.
24
+ var room = _this.maxPersistedEventsSize - sizeOfEventsList;
25
+ var exceedsCap = nextEventString.length >= room;
26
+ if (!exceedsCap && nextEventString.length * 3 >= room) {
27
+ exceedsCap = utf8ByteLength(nextEventString) >= room;
28
+ }
29
+ if (exceedsCap) {
22
30
  return true;
23
31
  }
24
32
  if (Date.now() - _this.timeAtLastSplit > _this.interval && events.length) {
@@ -41,64 +49,38 @@ var BaseEventsStore = /** @class */ (function () {
41
49
  enumerable: false,
42
50
  configurable: true
43
51
  });
44
- /**
45
- * Returns the UTF-8 byte size of a string without buffer allocation, matching the
46
- * actual wire byte count for non-ASCII content (Base64 image data, emoji, etc.).
47
- */
48
- BaseEventsStore.prototype.getStringSize = function (str) {
49
- var bytes = 0;
50
- for (var i = 0; i < str.length; i++) {
51
- var code = str.charCodeAt(i);
52
- if (code <= 0x7f) {
53
- bytes++;
54
- }
55
- else if (code <= 0x7ff) {
56
- bytes += 2;
57
- }
58
- else if (code >= 0xd800 && code <= 0xdbff) {
59
- // High surrogate — check for a valid low surrogate before consuming the pair.
60
- var next = i + 1 < str.length ? str.charCodeAt(i + 1) : NaN;
61
- if (next >= 0xdc00 && next <= 0xdfff) {
62
- // Valid surrogate pair → encodes a code point above U+FFFF (4 UTF-8 bytes).
63
- bytes += 4;
64
- i++;
65
- }
66
- else {
67
- // Orphaned high surrogate — treated as a replacement character (3 UTF-8 bytes).
68
- bytes += 3;
69
- }
70
- }
71
- else if (code >= 0xdc00 && code <= 0xdfff) {
72
- // Orphaned low surrogate — treated as a replacement character (3 UTF-8 bytes).
73
- bytes += 3;
74
- }
75
- else {
76
- // Other BMP character (U+0800–U+FFFF, excluding surrogates): 3 UTF-8 bytes.
77
- bytes += 3;
78
- }
52
+ // `prev` is only reused when `events` still starts with the list it measured; the tail
53
+ // check catches a same-length rewrite of the last element.
54
+ BaseEventsStore.prototype.isPrefixOf = function (prev, events) {
55
+ if (events.length < prev.length)
56
+ return false;
57
+ return prev.length === 0 || events[prev.length - 1] === prev.tail;
58
+ };
59
+ BaseEventsStore.prototype.measureContentBytes = function (prev, events) {
60
+ if (prev && prev.length === events.length && this.isPrefixOf(prev, events))
61
+ return prev;
62
+ var contentBytes = 0;
63
+ var start = 0;
64
+ if (prev && this.isPrefixOf(prev, events)) {
65
+ contentBytes = prev.contentBytes;
66
+ start = prev.length;
79
67
  }
80
- return bytes;
68
+ for (var i = start; i < events.length; i++) {
69
+ contentBytes += utf8ByteLength(events[i]);
70
+ }
71
+ return { length: events.length, contentBytes: contentBytes, tail: events.length > 0 ? events[events.length - 1] : undefined };
72
+ };
73
+ BaseEventsStore.prototype.getEventsContentBytes = function (events) {
74
+ var entry = this.measureContentBytes(this.eventsContentBytes.get(events), events);
75
+ this.eventsContentBytes.set(events, entry);
76
+ return entry.contentBytes;
81
77
  };
82
78
  /**
83
79
  * Calculates the total UTF-8 byte size of events array
84
80
  * Accounts for JSON serialization overhead when sent to backend
85
81
  */
86
82
  BaseEventsStore.prototype.getEventsArraySize = function (events) {
87
- var e_1, _a;
88
- var totalSize = 0;
89
- try {
90
- for (var events_1 = __values(events), events_1_1 = events_1.next(); !events_1_1.done; events_1_1 = events_1.next()) {
91
- var event_1 = events_1_1.value;
92
- totalSize += this.getStringSize(event_1);
93
- }
94
- }
95
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
96
- finally {
97
- try {
98
- if (events_1_1 && !events_1_1.done && (_a = events_1.return)) _a.call(events_1);
99
- }
100
- finally { if (e_1) throw e_1.error; }
101
- }
83
+ var totalSize = this.getEventsContentBytes(events);
102
84
  // Approximate overhead from the array portion of the JSON payload:
103
85
  // - Array brackets: [] = 2 bytes
104
86
  // - Commas between events: events.length - 1 bytes
@@ -109,6 +91,20 @@ var BaseEventsStore = /** @class */ (function () {
109
91
  var overhead = 2 + Math.max(0, events.length - 1) + events.length * 2;
110
92
  return totalSize + overhead;
111
93
  };
94
+ BaseEventsStore.prototype.recallSequenceContentBytes = function (sessionId, events) {
95
+ var cached = this.sequenceContentBytes.get(sessionId);
96
+ if (!cached || cached.length !== events.length || !this.isPrefixOf(cached, events))
97
+ return;
98
+ this.eventsContentBytes.set(events, cached);
99
+ };
100
+ BaseEventsStore.prototype.rememberSequenceContentBytes = function (sessionId, events) {
101
+ var entry = this.measureContentBytes(this.sequenceContentBytes.get(sessionId), events);
102
+ this.sequenceContentBytes.set(sessionId, entry);
103
+ this.eventsContentBytes.set(events, entry);
104
+ };
105
+ BaseEventsStore.prototype.forgetSequenceContentBytes = function (sessionId) {
106
+ this.sequenceContentBytes.delete(sessionId);
107
+ };
112
108
  return BaseEventsStore;
113
109
  }());
114
110
  export { BaseEventsStore };
@@ -1 +1 @@
1
- {"version":3,"file":"base-events-store.js","sourceRoot":"","sources":["../../../src/events/base-events-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { ILogger } from '@amplitude/analytics-core';\nimport { DEFAULT_MAX_PERSISTED_EVENTS_SIZE_BYTES, MAX_INTERVAL, MIN_INTERVAL } from '../constants';\nimport { Events, EventsStore, SendingSequencesReturn } from '../typings/session-replay';\n\nexport type InstanceArgs = {\n loggerProvider: ILogger;\n minInterval?: number;\n maxInterval?: number;\n maxPersistedEventsSize?: number;\n};\n\nexport abstract class BaseEventsStore<KeyType> implements EventsStore<KeyType> {\n protected readonly loggerProvider: ILogger;\n private minInterval = MIN_INTERVAL;\n private maxInterval = MAX_INTERVAL;\n private maxPersistedEventsSize = DEFAULT_MAX_PERSISTED_EVENTS_SIZE_BYTES;\n // Assigned in the constructor after `minInterval` is overridden by `args`. Class-field\n // initializers run before the constructor body, so initializing here would freeze\n // `interval` at the class-field default (500ms) — defeating any caller-supplied minInterval\n // for the very first split.\n private interval!: number;\n private _timeAtLastSplit = Date.now(); // Initialize this so we have a point of comparison when events are recorded\n\n public get timeAtLastSplit() {\n return this._timeAtLastSplit;\n }\n\n constructor(args: InstanceArgs) {\n this.loggerProvider = args.loggerProvider;\n this.minInterval = args.minInterval ?? this.minInterval;\n this.maxInterval = args.maxInterval ?? this.maxInterval;\n this.maxPersistedEventsSize = args.maxPersistedEventsSize ?? this.maxPersistedEventsSize;\n this.interval = this.minInterval;\n }\n\n abstract addEventToCurrentSequence(\n sessionId: string | number,\n event: string,\n ): Promise<SendingSequencesReturn<KeyType> | undefined>;\n abstract getSequencesToSend(): Promise<SendingSequencesReturn<KeyType>[] | undefined>;\n abstract storeCurrentSequence(sessionId: number): Promise<SendingSequencesReturn<KeyType> | undefined>;\n abstract storeSendingEvents(sessionId: string | number, events: Events): Promise<KeyType | undefined>;\n abstract cleanUpSessionEventsStore(sessionId: number, sequenceId: KeyType): Promise<void>;\n\n /**\n * Returns the UTF-8 byte size of a string without buffer allocation, matching the\n * actual wire byte count for non-ASCII content (Base64 image data, emoji, etc.).\n */\n private getStringSize(str: string): number {\n let bytes = 0;\n for (let i = 0; i < str.length; i++) {\n const code = str.charCodeAt(i);\n if (code <= 0x7f) {\n bytes++;\n } else if (code <= 0x7ff) {\n bytes += 2;\n } else if (code >= 0xd800 && code <= 0xdbff) {\n // High surrogate — check for a valid low surrogate before consuming the pair.\n const next = i + 1 < str.length ? str.charCodeAt(i + 1) : NaN;\n if (next >= 0xdc00 && next <= 0xdfff) {\n // Valid surrogate pair → encodes a code point above U+FFFF (4 UTF-8 bytes).\n bytes += 4;\n i++;\n } else {\n // Orphaned high surrogate — treated as a replacement character (3 UTF-8 bytes).\n bytes += 3;\n }\n } else if (code >= 0xdc00 && code <= 0xdfff) {\n // Orphaned low surrogate — treated as a replacement character (3 UTF-8 bytes).\n bytes += 3;\n } else {\n // Other BMP character (U+0800–U+FFFF, excluding surrogates): 3 UTF-8 bytes.\n bytes += 3;\n }\n }\n return bytes;\n }\n\n /**\n * Calculates the total UTF-8 byte size of events array\n * Accounts for JSON serialization overhead when sent to backend\n */\n private getEventsArraySize(events: Events): number {\n let totalSize = 0;\n for (const event of events) {\n totalSize += this.getStringSize(event);\n }\n\n // Approximate overhead from the array portion of the JSON payload:\n // - Array brackets: [] = 2 bytes\n // - Commas between events: events.length - 1 bytes\n // - Double quotes wrapping each event string: events.length * 2 bytes\n // Note: does not include the outer { version, events } wrapper (~22 bytes) or\n // per-event JSON-escaping of \" and \\ characters; the 2 MB MAX_EVENT_LIST_SIZE cap\n // stays well under the server's 10 MB decompressed split threshold even with those.\n const overhead = 2 + Math.max(0, events.length - 1) + events.length * 2;\n\n return totalSize + overhead;\n }\n\n /**\n * Determines whether to send the events list to the backend and start a new\n * empty events list, based on the size of the list as well as the last time sent\n * @param nextEventString\n * @returns boolean\n */\n shouldSplitEventsList = (events: Events, nextEventString: string): boolean => {\n const sizeOfNextEvent = this.getStringSize(nextEventString);\n const sizeOfEventsList = this.getEventsArraySize(events);\n\n // Check size constraint first (most likely to trigger)\n if (sizeOfEventsList + sizeOfNextEvent >= this.maxPersistedEventsSize) {\n return true;\n }\n if (Date.now() - this.timeAtLastSplit > this.interval && events.length) {\n this.interval = Math.min(this.maxInterval, this.interval + this.minInterval);\n this._timeAtLastSplit = Date.now();\n return true;\n }\n return false;\n };\n}\n"]}
1
+ {"version":3,"file":"base-events-store.js","sourceRoot":"","sources":["../../../src/events/base-events-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { ILogger } from '@amplitude/analytics-core';\nimport { DEFAULT_MAX_PERSISTED_EVENTS_SIZE_BYTES, MAX_INTERVAL, MIN_INTERVAL } from '../constants';\nimport { Events, EventsStore, SendingSequencesReturn } from '../typings/session-replay';\nimport { utf8ByteLength } from '../utils/utf8-byte-length';\n\ntype ContentByteCacheEntry = {\n length: number;\n contentBytes: number;\n tail?: string;\n};\n\nexport type InstanceArgs = {\n loggerProvider: ILogger;\n minInterval?: number;\n maxInterval?: number;\n maxPersistedEventsSize?: number;\n};\n\nexport abstract class BaseEventsStore<KeyType> implements EventsStore<KeyType> {\n protected readonly loggerProvider: ILogger;\n private minInterval = MIN_INTERVAL;\n private maxInterval = MAX_INTERVAL;\n private maxPersistedEventsSize = DEFAULT_MAX_PERSISTED_EVENTS_SIZE_BYTES;\n // Assigned in the constructor after `minInterval` is overridden by `args`. Class-field\n // initializers run before the constructor body, so initializing here would freeze\n // `interval` at the class-field default (500ms) — defeating any caller-supplied minInterval\n // for the very first split.\n private interval!: number;\n private _timeAtLastSplit = Date.now(); // Initialize this so we have a point of comparison when events are recorded\n // Running UTF-8 sizes so shouldSplitEventsList only measures newly appended events.\n // The IDB store loads a fresh array on every add, so it also keeps a per-session entry.\n private readonly eventsContentBytes = new WeakMap<Events, ContentByteCacheEntry>();\n private readonly sequenceContentBytes = new Map<string | number, ContentByteCacheEntry>();\n\n public get timeAtLastSplit() {\n return this._timeAtLastSplit;\n }\n\n constructor(args: InstanceArgs) {\n this.loggerProvider = args.loggerProvider;\n this.minInterval = args.minInterval ?? this.minInterval;\n this.maxInterval = args.maxInterval ?? this.maxInterval;\n this.maxPersistedEventsSize = args.maxPersistedEventsSize ?? this.maxPersistedEventsSize;\n this.interval = this.minInterval;\n }\n\n abstract addEventToCurrentSequence(\n sessionId: string | number,\n event: string,\n ): Promise<SendingSequencesReturn<KeyType> | undefined>;\n abstract getSequencesToSend(): Promise<SendingSequencesReturn<KeyType>[] | undefined>;\n abstract storeCurrentSequence(sessionId: number): Promise<SendingSequencesReturn<KeyType> | undefined>;\n abstract storeSendingEvents(sessionId: string | number, events: Events): Promise<KeyType | undefined>;\n abstract cleanUpSessionEventsStore(sessionId: number, sequenceId: KeyType): Promise<void>;\n\n // `prev` is only reused when `events` still starts with the list it measured; the tail\n // check catches a same-length rewrite of the last element.\n private isPrefixOf(prev: ContentByteCacheEntry, events: Events): boolean {\n if (events.length < prev.length) return false;\n return prev.length === 0 || events[prev.length - 1] === prev.tail;\n }\n\n private measureContentBytes(prev: ContentByteCacheEntry | undefined, events: Events): ContentByteCacheEntry {\n if (prev && prev.length === events.length && this.isPrefixOf(prev, events)) return prev;\n let contentBytes = 0;\n let start = 0;\n if (prev && this.isPrefixOf(prev, events)) {\n contentBytes = prev.contentBytes;\n start = prev.length;\n }\n for (let i = start; i < events.length; i++) {\n contentBytes += utf8ByteLength(events[i]);\n }\n return { length: events.length, contentBytes, tail: events.length > 0 ? events[events.length - 1] : undefined };\n }\n\n private getEventsContentBytes(events: Events): number {\n const entry = this.measureContentBytes(this.eventsContentBytes.get(events), events);\n this.eventsContentBytes.set(events, entry);\n return entry.contentBytes;\n }\n\n /**\n * Calculates the total UTF-8 byte size of events array\n * Accounts for JSON serialization overhead when sent to backend\n */\n private getEventsArraySize(events: Events): number {\n const totalSize = this.getEventsContentBytes(events);\n\n // Approximate overhead from the array portion of the JSON payload:\n // - Array brackets: [] = 2 bytes\n // - Commas between events: events.length - 1 bytes\n // - Double quotes wrapping each event string: events.length * 2 bytes\n // Note: does not include the outer { version, events } wrapper (~22 bytes) or\n // per-event JSON-escaping of \" and \\ characters; the 2 MB MAX_EVENT_LIST_SIZE cap\n // stays well under the server's 10 MB decompressed split threshold even with those.\n const overhead = 2 + Math.max(0, events.length - 1) + events.length * 2;\n\n return totalSize + overhead;\n }\n\n protected recallSequenceContentBytes(sessionId: string | number, events: Events): void {\n const cached = this.sequenceContentBytes.get(sessionId);\n if (!cached || cached.length !== events.length || !this.isPrefixOf(cached, events)) return;\n this.eventsContentBytes.set(events, cached);\n }\n\n protected rememberSequenceContentBytes(sessionId: string | number, events: Events): void {\n const entry = this.measureContentBytes(this.sequenceContentBytes.get(sessionId), events);\n this.sequenceContentBytes.set(sessionId, entry);\n this.eventsContentBytes.set(events, entry);\n }\n\n protected forgetSequenceContentBytes(sessionId: string | number): void {\n this.sequenceContentBytes.delete(sessionId);\n }\n\n /**\n * Determines whether to send the events list to the backend and start a new\n * empty events list, based on the size of the list as well as the last time sent\n * @param nextEventString\n * @returns boolean\n */\n shouldSplitEventsList = (events: Events, nextEventString: string): boolean => {\n const sizeOfEventsList = this.getEventsArraySize(events);\n // 1–3 UTF-8 bytes per code unit: only scan the event when `length` alone cannot decide.\n const room = this.maxPersistedEventsSize - sizeOfEventsList;\n let exceedsCap = nextEventString.length >= room;\n if (!exceedsCap && nextEventString.length * 3 >= room) {\n exceedsCap = utf8ByteLength(nextEventString) >= room;\n }\n if (exceedsCap) {\n return true;\n }\n if (Date.now() - this.timeAtLastSplit > this.interval && events.length) {\n this.interval = Math.min(this.maxInterval, this.interval + this.minInterval);\n this._timeAtLastSplit = Date.now();\n return true;\n }\n return false;\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"event-compressor.d.ts","sourceRoot":"","sources":["../../../src/events/event-compressor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAGvE,UAAU,SAAS;IACjB,KAAK,EAAE,aAAa,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5B;AAGD,qBAAa,eAAe;IAC1B,SAAS,EAAE,SAAS,EAAE,CAAM;IAC5B,YAAY,EAAE,SAAS,EAAE,CAAM;IAC/B,YAAY,UAAS;IACrB,aAAa,CAAC,EAAE,0BAA0B,CAAC,QAAQ,GAAG,aAAa,EAAE,MAAM,CAAC,CAAC;IAC7E,MAAM,EAAE,yBAAyB,CAAC;IAClC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,kBAAkB,EAAE,OAAO,GAAG,SAAS,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uBAAuB,CAAC,EAAE,MAAM,IAAI,CAAC;gBAGnC,aAAa,EAAE,0BAA0B,CAAC,QAAQ,GAAG,aAAa,EAAE,MAAM,CAAC,EAC3E,MAAM,EAAE,yBAAyB,EACjC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,YAAY,CAAC,EAAE,MAAM,EACrB,uBAAuB,CAAC,EAAE,MAAM,IAAI;IAuC/B,sBAAsB,IAAI,IAAI;IAa9B,YAAY,CAAC,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAmCpE,YAAY,CAAC,YAAY,EAAE,YAAY,GAAG,IAAI;IA6BrD,aAAa,UAAW,aAAa,KAAG,MAAM,CAS5C;IAEF,OAAO,CAAC,2BAA2B,CAmBjC;IAEK,kBAAkB,UAAW,aAAa,aAAa,MAAM,GAAG,MAAM,UAkB3E;IAEF;;;OAGG;IACI,UAAU,aAqBf;IAMF,OAAO,CAAC,kBAAkB;IA4BnB,SAAS,aAEd;CACH"}
1
+ {"version":3,"file":"event-compressor.d.ts","sourceRoot":"","sources":["../../../src/events/event-compressor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAG5D,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAGvE,UAAU,SAAS;IACjB,KAAK,EAAE,aAAa,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5B;AAGD,qBAAa,eAAe;IAC1B,SAAS,EAAE,SAAS,EAAE,CAAM;IAC5B,YAAY,EAAE,SAAS,EAAE,CAAM;IAC/B,YAAY,UAAS;IACrB,aAAa,CAAC,EAAE,0BAA0B,CAAC,QAAQ,GAAG,aAAa,EAAE,MAAM,CAAC,CAAC;IAC7E,MAAM,EAAE,yBAAyB,CAAC;IAClC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,kBAAkB,EAAE,OAAO,GAAG,SAAS,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uBAAuB,CAAC,EAAE,MAAM,IAAI,CAAC;gBAGnC,aAAa,EAAE,0BAA0B,CAAC,QAAQ,GAAG,aAAa,EAAE,MAAM,CAAC,EAC3E,MAAM,EAAE,yBAAyB,EACjC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,YAAY,CAAC,EAAE,MAAM,EACrB,uBAAuB,CAAC,EAAE,MAAM,IAAI;IAuC/B,sBAAsB,IAAI,IAAI;IAa9B,YAAY,CAAC,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAmCpE,YAAY,CAAC,YAAY,EAAE,YAAY,GAAG,IAAI;IA6BrD,aAAa,UAAW,aAAa,KAAG,MAAM,CAS5C;IAEF,OAAO,CAAC,2BAA2B,CAoBjC;IAEK,kBAAkB,UAAW,aAAa,aAAa,MAAM,GAAG,MAAM,UAkB3E;IAEF;;;OAGG;IACI,UAAU,aAqBf;IAMF,OAAO,CAAC,kBAAkB;IA4BnB,SAAS,aAEd;CACH"}
@@ -2,6 +2,7 @@ import { __read, __spreadArray, __values } from "tslib";
2
2
  import { getGlobalScope } from '@amplitude/analytics-core';
3
3
  import { EventType as RRWebEventType } from '@amplitude/rrweb-types';
4
4
  import { MAX_SINGLE_EVENT_SIZE } from '../constants';
5
+ import { utf8ByteLengthIfOver } from '../utils/utf8-byte-length';
5
6
  import { mergeMutationEvents } from './merge-mutation-events';
6
7
  var DEFAULT_TIMEOUT = 2000;
7
8
  var EventCompressor = /** @class */ (function () {
@@ -25,8 +26,9 @@ var EventCompressor = /** @class */ (function () {
25
26
  var _a;
26
27
  // UTF-8 byte size, not JS char count: a 9 M-char string of CJK/emoji can be 18–27 MB
27
28
  // on the wire and would otherwise slip past a char-count guard.
28
- var eventSizeBytes = new Blob([compressedEvent]).size;
29
- if (eventSizeBytes > ((_a = _this.config.maxSingleEventSizeBytes) !== null && _a !== void 0 ? _a : MAX_SINGLE_EVENT_SIZE)) {
29
+ var maxEventBytes = (_a = _this.config.maxSingleEventSizeBytes) !== null && _a !== void 0 ? _a : MAX_SINGLE_EVENT_SIZE;
30
+ var eventSizeBytes = utf8ByteLengthIfOver(compressedEvent, maxEventBytes);
31
+ if (eventSizeBytes !== undefined) {
30
32
  _this.config.loggerProvider.warn("Session replay event dropped: serialized size ".concat(Math.round(eventSizeBytes / 1024), " KB exceeds maximum allowed event size. If this recurs, please open a GitHub issue at https://github.com/amplitude/Amplitude-TypeScript/issues or contact Amplitude support."));
31
33
  return;
32
34
  }
@@ -1 +1 @@
1
- {"version":3,"file":"event-compressor.js","sourceRoot":"","sources":["../../../src/events/event-compressor.ts"],"names":[],"mappings":"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","sourcesContent":["import { getGlobalScope } from '@amplitude/analytics-core';\nimport { EventType as RRWebEventType } from '@amplitude/rrweb-types';\nimport type { eventWithTime } from '@amplitude/rrweb-types';\nimport { SessionReplayJoinedConfig } from '../config/types';\nimport { MAX_SINGLE_EVENT_SIZE } from '../constants';\nimport { SessionReplayEventsManager } from '../typings/session-replay';\nimport { mergeMutationEvents } from './merge-mutation-events';\n\ninterface TaskQueue {\n event: eventWithTime;\n sessionId: string | number;\n}\n\nconst DEFAULT_TIMEOUT = 2000;\nexport class EventCompressor {\n taskQueue: TaskQueue[] = [];\n pendingQueue: TaskQueue[] = [];\n isProcessing = false;\n eventsManager?: SessionReplayEventsManager<'replay' | 'interaction', string>;\n config: SessionReplayJoinedConfig;\n deviceId: string | undefined;\n canUseIdleCallback: boolean | undefined;\n timeout: number;\n worker?: Worker;\n onFullSnapshotProcessed?: () => void;\n\n constructor(\n eventsManager: SessionReplayEventsManager<'replay' | 'interaction', string>,\n config: SessionReplayJoinedConfig,\n deviceId: string | undefined,\n workerScript?: string,\n onFullSnapshotProcessed?: () => void,\n ) {\n const globalScope = getGlobalScope();\n this.canUseIdleCallback = globalScope && 'requestIdleCallback' in globalScope;\n this.eventsManager = eventsManager;\n this.config = config;\n this.deviceId = deviceId;\n this.timeout = config.performanceConfig?.timeout || DEFAULT_TIMEOUT;\n this.onFullSnapshotProcessed = onFullSnapshotProcessed;\n\n if (workerScript) {\n config.loggerProvider.log('Enabling web worker for compression');\n\n try {\n const blob = new Blob([workerScript], { type: 'application/javascript' });\n const blobUrl = URL.createObjectURL(blob);\n const worker = new Worker(blobUrl);\n\n worker.onerror = (e) => {\n e.preventDefault();\n config.loggerProvider.error(\n `Worker failed, falling back to non-worker compression: ${e.message} (${e.filename}:${e.lineno})`,\n );\n worker.terminate();\n this.worker = undefined;\n };\n worker.onmessage = (e) => {\n const { compressedEvent, sessionId } = e.data as Record<string, string>;\n this.addCompressedEventToManager(compressedEvent, sessionId);\n };\n\n this.worker = worker;\n } catch (error) {\n config.loggerProvider.error('Failed to create worker, falling back to non-worker compression:', error);\n }\n }\n }\n\n // Schedule processing during idle time\n public scheduleIdleProcessing(): void {\n if (!this.isProcessing) {\n this.isProcessing = true;\n requestIdleCallback(\n (idleDeadline) => {\n this.processQueue(idleDeadline);\n },\n { timeout: this.timeout },\n );\n }\n }\n\n // Add an event to the task queue if idle callback is supported or compress the event directly\n public enqueueEvent(event: eventWithTime, sessionId: string | number): void {\n // Full snapshot (type 2) is the most critical event — a replay cannot be played without it.\n // Process and flush immediately rather than waiting for the idle scheduler or web worker,\n // maximising the chance it is delivered before the user exits the page.\n if (event.type === RRWebEventType.FullSnapshot) {\n this.config.loggerProvider.debug('Processing full snapshot immediately.');\n // Drain any events still pending in the idle-callback queue first.\n // Those events reference the pre-snapshot DOM and must be sent before\n // the full snapshot; if we let them be processed later they'd arrive at\n // the server after the snapshot and cause \"node not found\" replay errors.\n if (this.taskQueue.length > 0 || this.pendingQueue.length > 0) {\n const allTasks = [...this.taskQueue.splice(0), ...this.mergeMutationTasks(this.pendingQueue.splice(0))];\n for (const task of allTasks) {\n const compressed = this.compressEvent(task.event);\n this.addCompressedEventToManager(compressed, task.sessionId);\n }\n this.isProcessing = false;\n }\n const compressedEvent = this.compressEvent(event);\n this.addCompressedEventToManager(compressedEvent, sessionId);\n this.onFullSnapshotProcessed?.();\n return;\n }\n\n if (this.canUseIdleCallback && this.config.performanceConfig?.enabled) {\n this.config.loggerProvider.debug('Enqueuing event for processing during idle time.');\n this.pendingQueue.push({ event, sessionId });\n this.scheduleIdleProcessing();\n } else {\n this.config.loggerProvider.debug('Processing event without idle callback.');\n this.addCompressedEvent(event, sessionId);\n }\n }\n\n // Process the task queue during idle time\n public processQueue(idleDeadline: IdleDeadline): void {\n // Merge newly-arrived pending events and append to the already-merged taskQueue.\n // Keeping them separate prevents re-merging already-merged tasks on subsequent calls,\n // which would corrupt move semantics for nodes that appear in multiple merge passes.\n if (this.pendingQueue.length > 0) {\n this.taskQueue.push(...this.mergeMutationTasks(this.pendingQueue.splice(0)));\n }\n // Process tasks while there's idle time or until the max number of tasks is reached\n while (this.taskQueue.length > 0 && (idleDeadline.timeRemaining() > 0 || idleDeadline.didTimeout)) {\n const task = this.taskQueue.shift();\n if (task) {\n const { event, sessionId } = task;\n this.addCompressedEvent(event, sessionId);\n }\n }\n\n // If there are still tasks in the queue, schedule the next idle callback\n if (this.taskQueue.length > 0 || this.pendingQueue.length > 0) {\n requestIdleCallback(\n (idleDeadline) => {\n this.processQueue(idleDeadline);\n },\n { timeout: this.timeout },\n );\n } else {\n this.isProcessing = false;\n }\n }\n\n compressEvent = (event: eventWithTime): string => {\n // Serialize with type+timestamp first for streaming parser compatibility.\n // JS engines serialize non-integer string keys in insertion order (ES2015 spec,\n // reliable across V8/SpiderMonkey/JSC), so explicit construction controls key order.\n // `delay` is an rrweb player field: an optional ms offset applied on top of\n // `timestamp` during replay to smooth out batched/throttled events. Preserve\n // it when present so playback timing is accurate.\n const { type, timestamp, delay, data } = event as eventWithTime & { delay?: number };\n return delay != null ? JSON.stringify({ type, timestamp, delay, data }) : JSON.stringify({ type, timestamp, data });\n };\n\n private addCompressedEventToManager = (compressedEvent: string, sessionId: string | number) => {\n // UTF-8 byte size, not JS char count: a 9 M-char string of CJK/emoji can be 18–27 MB\n // on the wire and would otherwise slip past a char-count guard.\n const eventSizeBytes = new Blob([compressedEvent]).size;\n if (eventSizeBytes > (this.config.maxSingleEventSizeBytes ?? MAX_SINGLE_EVENT_SIZE)) {\n this.config.loggerProvider.warn(\n `Session replay event dropped: serialized size ${Math.round(\n eventSizeBytes / 1024,\n )} KB exceeds maximum allowed event size. If this recurs, please open a GitHub issue at https://github.com/amplitude/Amplitude-TypeScript/issues or contact Amplitude support.`,\n );\n return;\n }\n if (this.eventsManager && this.deviceId) {\n this.eventsManager.addEvent({\n event: { type: 'replay', data: compressedEvent },\n sessionId,\n deviceId: this.deviceId,\n });\n }\n };\n\n public addCompressedEvent = (event: eventWithTime, sessionId: string | number) => {\n if (this.worker) {\n // This indirectly compresses the event.\n try {\n this.worker.postMessage({ event, sessionId });\n } catch (err: any) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n if (err.name === 'DataCloneError') {\n // fallback: serialize\n this.worker.postMessage(JSON.stringify({ event, sessionId }));\n } else {\n this.config.loggerProvider.warn('Unexpected error while posting message to worker:', err);\n }\n }\n } else {\n const compressedEvent = this.compressEvent(event);\n this.addCompressedEventToManager(compressedEvent, sessionId);\n }\n };\n\n /**\n * Synchronously drain all queued events. Called during page unload to prevent\n * data loss from events waiting in the requestIdleCallback queue.\n */\n public flushQueue = () => {\n // Merge any events still in pendingQueue into taskQueue first.\n // Events land in pendingQueue when the idle callback hasn't fired yet;\n // without this step they would be silently lost on page unload.\n if (this.pendingQueue.length > 0) {\n this.taskQueue.push(...this.mergeMutationTasks(this.pendingQueue.splice(0)));\n }\n while (this.taskQueue.length > 0) {\n const task = this.taskQueue.shift();\n if (task) {\n const { event, sessionId } = task;\n // Bypass the web worker: compress synchronously on the main thread and\n // write directly to the manager. postMessage is async — during page\n // unload the worker response would never arrive and events would be\n // silently dropped. This mirrors the pattern used for full snapshots in\n // enqueueEvent().\n const compressed = this.compressEvent(event);\n this.addCompressedEventToManager(compressed, sessionId);\n }\n }\n this.isProcessing = false;\n };\n\n // Merge consecutive mutation tasks with the same sessionId before processing,\n // reducing the number of events serialized and stored without changing replay semantics.\n // Enabled by default (validated amp-on-amp perf config, SR-4646); only skipped when\n // performanceConfig.mergeMutations is explicitly set to false.\n private mergeMutationTasks(tasks: TaskQueue[]): TaskQueue[] {\n if (this.config.performanceConfig?.mergeMutations === false) return tasks;\n if (tasks.length <= 1) return tasks;\n\n const result: TaskQueue[] = [];\n let i = 0;\n\n while (i < tasks.length) {\n const sessionId = tasks[i].sessionId;\n\n // Find the end of the current session run\n let j = i + 1;\n while (j < tasks.length && tasks[j].sessionId === sessionId) {\n j++;\n }\n\n // Merge consecutive mutations within this session run; non-mutations pass through unchanged\n const merged = mergeMutationEvents(tasks.slice(i, j).map((t) => t.event));\n for (const event of merged) {\n result.push({ event, sessionId });\n }\n\n i = j;\n }\n\n return result;\n }\n\n public terminate = () => {\n this.worker?.terminate();\n };\n}\n"]}
1
+ {"version":3,"file":"event-compressor.js","sourceRoot":"","sources":["../../../src/events/event-compressor.ts"],"names":[],"mappings":"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","sourcesContent":["import { getGlobalScope } from '@amplitude/analytics-core';\nimport { EventType as RRWebEventType } from '@amplitude/rrweb-types';\nimport type { eventWithTime } from '@amplitude/rrweb-types';\nimport { SessionReplayJoinedConfig } from '../config/types';\nimport { MAX_SINGLE_EVENT_SIZE } from '../constants';\nimport { utf8ByteLengthIfOver } from '../utils/utf8-byte-length';\nimport { SessionReplayEventsManager } from '../typings/session-replay';\nimport { mergeMutationEvents } from './merge-mutation-events';\n\ninterface TaskQueue {\n event: eventWithTime;\n sessionId: string | number;\n}\n\nconst DEFAULT_TIMEOUT = 2000;\nexport class EventCompressor {\n taskQueue: TaskQueue[] = [];\n pendingQueue: TaskQueue[] = [];\n isProcessing = false;\n eventsManager?: SessionReplayEventsManager<'replay' | 'interaction', string>;\n config: SessionReplayJoinedConfig;\n deviceId: string | undefined;\n canUseIdleCallback: boolean | undefined;\n timeout: number;\n worker?: Worker;\n onFullSnapshotProcessed?: () => void;\n\n constructor(\n eventsManager: SessionReplayEventsManager<'replay' | 'interaction', string>,\n config: SessionReplayJoinedConfig,\n deviceId: string | undefined,\n workerScript?: string,\n onFullSnapshotProcessed?: () => void,\n ) {\n const globalScope = getGlobalScope();\n this.canUseIdleCallback = globalScope && 'requestIdleCallback' in globalScope;\n this.eventsManager = eventsManager;\n this.config = config;\n this.deviceId = deviceId;\n this.timeout = config.performanceConfig?.timeout || DEFAULT_TIMEOUT;\n this.onFullSnapshotProcessed = onFullSnapshotProcessed;\n\n if (workerScript) {\n config.loggerProvider.log('Enabling web worker for compression');\n\n try {\n const blob = new Blob([workerScript], { type: 'application/javascript' });\n const blobUrl = URL.createObjectURL(blob);\n const worker = new Worker(blobUrl);\n\n worker.onerror = (e) => {\n e.preventDefault();\n config.loggerProvider.error(\n `Worker failed, falling back to non-worker compression: ${e.message} (${e.filename}:${e.lineno})`,\n );\n worker.terminate();\n this.worker = undefined;\n };\n worker.onmessage = (e) => {\n const { compressedEvent, sessionId } = e.data as Record<string, string>;\n this.addCompressedEventToManager(compressedEvent, sessionId);\n };\n\n this.worker = worker;\n } catch (error) {\n config.loggerProvider.error('Failed to create worker, falling back to non-worker compression:', error);\n }\n }\n }\n\n // Schedule processing during idle time\n public scheduleIdleProcessing(): void {\n if (!this.isProcessing) {\n this.isProcessing = true;\n requestIdleCallback(\n (idleDeadline) => {\n this.processQueue(idleDeadline);\n },\n { timeout: this.timeout },\n );\n }\n }\n\n // Add an event to the task queue if idle callback is supported or compress the event directly\n public enqueueEvent(event: eventWithTime, sessionId: string | number): void {\n // Full snapshot (type 2) is the most critical event — a replay cannot be played without it.\n // Process and flush immediately rather than waiting for the idle scheduler or web worker,\n // maximising the chance it is delivered before the user exits the page.\n if (event.type === RRWebEventType.FullSnapshot) {\n this.config.loggerProvider.debug('Processing full snapshot immediately.');\n // Drain any events still pending in the idle-callback queue first.\n // Those events reference the pre-snapshot DOM and must be sent before\n // the full snapshot; if we let them be processed later they'd arrive at\n // the server after the snapshot and cause \"node not found\" replay errors.\n if (this.taskQueue.length > 0 || this.pendingQueue.length > 0) {\n const allTasks = [...this.taskQueue.splice(0), ...this.mergeMutationTasks(this.pendingQueue.splice(0))];\n for (const task of allTasks) {\n const compressed = this.compressEvent(task.event);\n this.addCompressedEventToManager(compressed, task.sessionId);\n }\n this.isProcessing = false;\n }\n const compressedEvent = this.compressEvent(event);\n this.addCompressedEventToManager(compressedEvent, sessionId);\n this.onFullSnapshotProcessed?.();\n return;\n }\n\n if (this.canUseIdleCallback && this.config.performanceConfig?.enabled) {\n this.config.loggerProvider.debug('Enqueuing event for processing during idle time.');\n this.pendingQueue.push({ event, sessionId });\n this.scheduleIdleProcessing();\n } else {\n this.config.loggerProvider.debug('Processing event without idle callback.');\n this.addCompressedEvent(event, sessionId);\n }\n }\n\n // Process the task queue during idle time\n public processQueue(idleDeadline: IdleDeadline): void {\n // Merge newly-arrived pending events and append to the already-merged taskQueue.\n // Keeping them separate prevents re-merging already-merged tasks on subsequent calls,\n // which would corrupt move semantics for nodes that appear in multiple merge passes.\n if (this.pendingQueue.length > 0) {\n this.taskQueue.push(...this.mergeMutationTasks(this.pendingQueue.splice(0)));\n }\n // Process tasks while there's idle time or until the max number of tasks is reached\n while (this.taskQueue.length > 0 && (idleDeadline.timeRemaining() > 0 || idleDeadline.didTimeout)) {\n const task = this.taskQueue.shift();\n if (task) {\n const { event, sessionId } = task;\n this.addCompressedEvent(event, sessionId);\n }\n }\n\n // If there are still tasks in the queue, schedule the next idle callback\n if (this.taskQueue.length > 0 || this.pendingQueue.length > 0) {\n requestIdleCallback(\n (idleDeadline) => {\n this.processQueue(idleDeadline);\n },\n { timeout: this.timeout },\n );\n } else {\n this.isProcessing = false;\n }\n }\n\n compressEvent = (event: eventWithTime): string => {\n // Serialize with type+timestamp first for streaming parser compatibility.\n // JS engines serialize non-integer string keys in insertion order (ES2015 spec,\n // reliable across V8/SpiderMonkey/JSC), so explicit construction controls key order.\n // `delay` is an rrweb player field: an optional ms offset applied on top of\n // `timestamp` during replay to smooth out batched/throttled events. Preserve\n // it when present so playback timing is accurate.\n const { type, timestamp, delay, data } = event as eventWithTime & { delay?: number };\n return delay != null ? JSON.stringify({ type, timestamp, delay, data }) : JSON.stringify({ type, timestamp, data });\n };\n\n private addCompressedEventToManager = (compressedEvent: string, sessionId: string | number) => {\n // UTF-8 byte size, not JS char count: a 9 M-char string of CJK/emoji can be 18–27 MB\n // on the wire and would otherwise slip past a char-count guard.\n const maxEventBytes = this.config.maxSingleEventSizeBytes ?? MAX_SINGLE_EVENT_SIZE;\n const eventSizeBytes = utf8ByteLengthIfOver(compressedEvent, maxEventBytes);\n if (eventSizeBytes !== undefined) {\n this.config.loggerProvider.warn(\n `Session replay event dropped: serialized size ${Math.round(\n eventSizeBytes / 1024,\n )} KB exceeds maximum allowed event size. If this recurs, please open a GitHub issue at https://github.com/amplitude/Amplitude-TypeScript/issues or contact Amplitude support.`,\n );\n return;\n }\n if (this.eventsManager && this.deviceId) {\n this.eventsManager.addEvent({\n event: { type: 'replay', data: compressedEvent },\n sessionId,\n deviceId: this.deviceId,\n });\n }\n };\n\n public addCompressedEvent = (event: eventWithTime, sessionId: string | number) => {\n if (this.worker) {\n // This indirectly compresses the event.\n try {\n this.worker.postMessage({ event, sessionId });\n } catch (err: any) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n if (err.name === 'DataCloneError') {\n // fallback: serialize\n this.worker.postMessage(JSON.stringify({ event, sessionId }));\n } else {\n this.config.loggerProvider.warn('Unexpected error while posting message to worker:', err);\n }\n }\n } else {\n const compressedEvent = this.compressEvent(event);\n this.addCompressedEventToManager(compressedEvent, sessionId);\n }\n };\n\n /**\n * Synchronously drain all queued events. Called during page unload to prevent\n * data loss from events waiting in the requestIdleCallback queue.\n */\n public flushQueue = () => {\n // Merge any events still in pendingQueue into taskQueue first.\n // Events land in pendingQueue when the idle callback hasn't fired yet;\n // without this step they would be silently lost on page unload.\n if (this.pendingQueue.length > 0) {\n this.taskQueue.push(...this.mergeMutationTasks(this.pendingQueue.splice(0)));\n }\n while (this.taskQueue.length > 0) {\n const task = this.taskQueue.shift();\n if (task) {\n const { event, sessionId } = task;\n // Bypass the web worker: compress synchronously on the main thread and\n // write directly to the manager. postMessage is async — during page\n // unload the worker response would never arrive and events would be\n // silently dropped. This mirrors the pattern used for full snapshots in\n // enqueueEvent().\n const compressed = this.compressEvent(event);\n this.addCompressedEventToManager(compressed, sessionId);\n }\n }\n this.isProcessing = false;\n };\n\n // Merge consecutive mutation tasks with the same sessionId before processing,\n // reducing the number of events serialized and stored without changing replay semantics.\n // Enabled by default (validated amp-on-amp perf config, SR-4646); only skipped when\n // performanceConfig.mergeMutations is explicitly set to false.\n private mergeMutationTasks(tasks: TaskQueue[]): TaskQueue[] {\n if (this.config.performanceConfig?.mergeMutations === false) return tasks;\n if (tasks.length <= 1) return tasks;\n\n const result: TaskQueue[] = [];\n let i = 0;\n\n while (i < tasks.length) {\n const sessionId = tasks[i].sessionId;\n\n // Find the end of the current session run\n let j = i + 1;\n while (j < tasks.length && tasks[j].sessionId === sessionId) {\n j++;\n }\n\n // Merge consecutive mutations within this session run; non-mutations pass through unchanged\n const merged = mergeMutationEvents(tasks.slice(i, j).map((t) => t.event));\n for (const event of merged) {\n result.push({ event, sessionId });\n }\n\n i = j;\n }\n\n return result;\n }\n\n public terminate = () => {\n this.worker?.terminate();\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"events-idb-store.d.ts","sourceRoot":"","sources":["../../../src/events/events-idb-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"events-idb-store.d.ts","sourceRoot":"","sources":["../../../src/events/events-idb-store.ts"],"names":[],"mappings":"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"}
@@ -266,12 +266,14 @@ var SessionReplayEventsIDBStore = /** @class */ (function (_super) {
266
266
  })];
267
267
  case 4:
268
268
  _a.sent();
269
+ this.rememberSequenceContentBytes(sessionId, []);
269
270
  this.recordSuccess();
270
271
  cancelTimeout();
271
272
  _tabId = currentSequenceData.tabId, rest = __rest(currentSequenceData, ["tabId"]);
272
273
  return [2 /*return*/, __assign(__assign({}, rest), { sessionId: sessionId, sequenceId: sequenceId })];
273
274
  case 5:
274
275
  e_2 = _a.sent();
276
+ this.forgetSequenceContentBytes(sessionId);
275
277
  if (!timedOut) {
276
278
  errorLogged = true;
277
279
  logIdbError(this.loggerProvider, "".concat(STORAGE_FAILURE, ": ").concat(e_2), e_2);
@@ -283,7 +285,7 @@ var SessionReplayEventsIDBStore = /** @class */ (function (_super) {
283
285
  });
284
286
  }); };
285
287
  _this.addEventToCurrentSequence = function (sessionId, event) { return __awaiter(_this, void 0, void 0, function () {
286
- var errorLogged, timedOut, tx, cancelTimeout, sequenceEvents, ownedSequence, eventsToSend, sequenceId, e_3;
288
+ var errorLogged, timedOut, tx, cancelTimeout, sequenceEvents, nextEvents_1, ownedSequence, nextEvents_2, nextEvents_3, eventsToSend, nextEvents_4, nextEvents, sequenceId, e_3;
287
289
  var _this = this;
288
290
  return __generator(this, function (_a) {
289
291
  switch (_a.label) {
@@ -324,28 +326,35 @@ var SessionReplayEventsIDBStore = /** @class */ (function (_super) {
324
326
  case 3:
325
327
  _a.sent();
326
328
  _a.label = 4;
327
- case 4: return [4 /*yield*/, tx.objectStore(currentSequenceKey).put({ sessionId: sessionId, events: [event], tabId: this.tabId })];
329
+ case 4:
330
+ nextEvents_1 = [event];
331
+ return [4 /*yield*/, tx.objectStore(currentSequenceKey).put({ sessionId: sessionId, events: nextEvents_1, tabId: this.tabId })];
328
332
  case 5:
329
333
  _a.sent();
334
+ this.rememberSequenceContentBytes(sessionId, nextEvents_1);
330
335
  this.recordSuccess();
331
336
  cancelTimeout();
332
337
  return [2 /*return*/, undefined];
333
338
  case 6:
334
339
  ownedSequence = sequenceEvents;
335
340
  if (!!ownedSequence) return [3 /*break*/, 8];
336
- return [4 /*yield*/, tx.objectStore(currentSequenceKey).put({ sessionId: sessionId, events: [event], tabId: this.tabId })];
341
+ nextEvents_2 = [event];
342
+ return [4 /*yield*/, tx.objectStore(currentSequenceKey).put({ sessionId: sessionId, events: nextEvents_2, tabId: this.tabId })];
337
343
  case 7:
338
344
  _a.sent();
345
+ this.rememberSequenceContentBytes(sessionId, nextEvents_2);
339
346
  this.recordSuccess();
340
347
  cancelTimeout();
341
348
  return [2 /*return*/, undefined];
342
349
  case 8:
350
+ // Freshly loaded array: seed it with the session's running size so the split check is O(1).
351
+ this.recallSequenceContentBytes(sessionId, ownedSequence.events);
343
352
  if (!!this.shouldSplitEventsList(ownedSequence.events, event)) return [3 /*break*/, 10];
344
- return [4 /*yield*/, tx
345
- .objectStore(currentSequenceKey)
346
- .put({ sessionId: sessionId, events: ownedSequence.events.concat(event), tabId: this.tabId })];
353
+ nextEvents_3 = ownedSequence.events.concat(event);
354
+ return [4 /*yield*/, tx.objectStore(currentSequenceKey).put({ sessionId: sessionId, events: nextEvents_3, tabId: this.tabId })];
347
355
  case 9:
348
356
  _a.sent();
357
+ this.rememberSequenceContentBytes(sessionId, nextEvents_3);
349
358
  this.recordSuccess();
350
359
  cancelTimeout();
351
360
  return [2 /*return*/, undefined];
@@ -353,15 +362,20 @@ var SessionReplayEventsIDBStore = /** @class */ (function (_super) {
353
362
  eventsToSend = ownedSequence.events;
354
363
  if (!(eventsToSend.length === 0)) return [3 /*break*/, 12];
355
364
  this.maybeLogEmptyFiltered('addEventToCurrentSequence');
356
- return [4 /*yield*/, tx.objectStore(currentSequenceKey).put({ sessionId: sessionId, events: [event], tabId: this.tabId })];
365
+ nextEvents_4 = [event];
366
+ return [4 /*yield*/, tx.objectStore(currentSequenceKey).put({ sessionId: sessionId, events: nextEvents_4, tabId: this.tabId })];
357
367
  case 11:
358
368
  _a.sent();
369
+ this.rememberSequenceContentBytes(sessionId, nextEvents_4);
359
370
  this.recordSuccess();
360
371
  cancelTimeout();
361
372
  return [2 /*return*/, undefined];
362
- case 12: return [4 /*yield*/, tx.objectStore(currentSequenceKey).put({ sessionId: sessionId, events: [event], tabId: this.tabId })];
373
+ case 12:
374
+ nextEvents = [event];
375
+ return [4 /*yield*/, tx.objectStore(currentSequenceKey).put({ sessionId: sessionId, events: nextEvents, tabId: this.tabId })];
363
376
  case 13:
364
377
  _a.sent();
378
+ this.rememberSequenceContentBytes(sessionId, nextEvents);
365
379
  return [4 /*yield*/, tx.objectStore(sequencesToSendKey).put({
366
380
  sessionId: sessionId,
367
381
  events: eventsToSend,
@@ -378,6 +392,7 @@ var SessionReplayEventsIDBStore = /** @class */ (function (_super) {
378
392
  }];
379
393
  case 15:
380
394
  e_3 = _a.sent();
395
+ this.forgetSequenceContentBytes(sessionId);
381
396
  if (!timedOut) {
382
397
  errorLogged = true;
383
398
  logIdbError(this.loggerProvider, "".concat(STORAGE_FAILURE, ": ").concat(e_3), e_3);