@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.
- package/lib/cjs/events/base-events-store.d.ts +8 -5
- package/lib/cjs/events/base-events-store.d.ts.map +1 -1
- package/lib/cjs/events/base-events-store.js +51 -55
- package/lib/cjs/events/base-events-store.js.map +1 -1
- package/lib/cjs/events/event-compressor.d.ts.map +1 -1
- package/lib/cjs/events/event-compressor.js +4 -2
- package/lib/cjs/events/event-compressor.js.map +1 -1
- package/lib/cjs/events/events-idb-store.d.ts.map +1 -1
- package/lib/cjs/events/events-idb-store.js +23 -8
- package/lib/cjs/events/events-idb-store.js.map +1 -1
- package/lib/cjs/events/events-manager.d.ts.map +1 -1
- package/lib/cjs/events/events-manager.js +18 -6
- package/lib/cjs/events/events-manager.js.map +1 -1
- package/lib/cjs/observers.d.ts.map +1 -1
- package/lib/cjs/observers.js +2 -22
- package/lib/cjs/observers.js.map +1 -1
- package/lib/cjs/track-destination.d.ts.map +1 -1
- package/lib/cjs/track-destination.js +6 -5
- package/lib/cjs/track-destination.js.map +1 -1
- package/lib/cjs/utils/utf8-byte-length.d.ts +17 -0
- package/lib/cjs/utils/utf8-byte-length.d.ts.map +1 -0
- package/lib/cjs/utils/utf8-byte-length.js +104 -0
- package/lib/cjs/utils/utf8-byte-length.js.map +1 -0
- package/lib/cjs/version.d.ts +1 -1
- package/lib/cjs/version.js +1 -1
- package/lib/cjs/version.js.map +1 -1
- package/lib/cjs/worker/index.js +1 -1
- package/lib/esm/events/base-events-store.d.ts +8 -5
- package/lib/esm/events/base-events-store.d.ts.map +1 -1
- package/lib/esm/events/base-events-store.js +51 -55
- package/lib/esm/events/base-events-store.js.map +1 -1
- package/lib/esm/events/event-compressor.d.ts.map +1 -1
- package/lib/esm/events/event-compressor.js +4 -2
- package/lib/esm/events/event-compressor.js.map +1 -1
- package/lib/esm/events/events-idb-store.d.ts.map +1 -1
- package/lib/esm/events/events-idb-store.js +23 -8
- package/lib/esm/events/events-idb-store.js.map +1 -1
- package/lib/esm/events/events-manager.d.ts.map +1 -1
- package/lib/esm/events/events-manager.js +18 -6
- package/lib/esm/events/events-manager.js.map +1 -1
- package/lib/esm/observers.d.ts.map +1 -1
- package/lib/esm/observers.js +2 -22
- package/lib/esm/observers.js.map +1 -1
- package/lib/esm/track-destination.d.ts.map +1 -1
- package/lib/esm/track-destination.js +6 -5
- package/lib/esm/track-destination.js.map +1 -1
- package/lib/esm/utils/utf8-byte-length.d.ts +17 -0
- package/lib/esm/utils/utf8-byte-length.d.ts.map +1 -0
- package/lib/esm/utils/utf8-byte-length.js +97 -0
- package/lib/esm/utils/utf8-byte-length.js.map +1 -0
- package/lib/esm/version.d.ts +1 -1
- package/lib/esm/version.js +1 -1
- package/lib/esm/version.js.map +1 -1
- package/lib/esm/worker/index.js +1 -1
- package/lib/scripts/index-min.js +1 -1
- package/lib/scripts/index-min.js.gz +0 -0
- package/lib/scripts/index-min.js.map +1 -1
- package/lib/scripts/observers-min.js +1 -1
- package/lib/scripts/observers-min.js.gz +0 -0
- package/lib/scripts/observers-min.js.map +1 -1
- package/lib/scripts/session-replay-browser-min.js +1 -1
- package/lib/scripts/session-replay-browser-min.js.gz +0 -0
- package/lib/scripts/session-replay-browser-min.js.map +1 -1
- package/lib/scripts/worker-min.js +1 -1
- package/lib/scripts/worker-min.js.gz +0 -0
- 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
|
-
|
|
25
|
-
|
|
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;
|
|
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
|
-
//
|
|
21
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
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
|
|
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;
|
|
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
|
|
29
|
-
|
|
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":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAU,MAAM,KAAK,CAAC;AAErD,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACtF,OAAO,EAAE,eAAe,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAMxF,wBAAgB,YAAY,IAAI,MAAM,CASrC;AAED,eAAO,MAAM,kBAAkB,2BAA2B,CAAC;AAC3D,eAAO,MAAM,kBAAkB,oBAAoB,CAAC;AACpD,eAAO,MAAM,eAAe,iBAAiB,CAAC;AAO9C,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAOvC,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,SAA4B,GAAG,OAAO,CAAC,CAAC,CAAC,CAc/G;AA0CD,MAAM,WAAW,eAAgB,SAAQ,QAAQ;IAC/C,sBAAsB,EAAE;QACtB,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC,GAAG;YAAE,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KAChF,CAAC;IACF,eAAe,EAAE;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC,GAAG;YAAE,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAC/E,OAAO,EAAE;YAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAC;KACzC,CAAC;CACH;AAED,eAAO,MAAM,kBAAkB,OAAQ,aAAa,eAAe,CAAC;;;CAmBnE,CAAC;AAEF,eAAO,MAAM,WAAW,WAAkB,MAAM,2CAa/C,CAAC;AAEF,KAAK,YAAY,GAAG;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,YAAY,CAAC,eAAe,CAAC,CAAC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;IACjC,2BAA2B,CAAC,EAAE,MAAM,CAAC;CACtC,GAAG,gBAAgB,CAAC;AAErB,qBAAa,2BAA4B,SAAQ,eAAe,CAAC,MAAM,CAAC;IACtE,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAgC;IACnD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAa;IAClD,OAAO,CAAC,QAAQ,CAAC,2BAA2B,CAAS;IACrD,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,oBAAoB,CAAS;IACrC,OAAO,CAAC,kBAAkB,CAAK;IAO/B,OAAO,CAAC,qBAAqB;gBAMjB,IAAI,EAAE,YAAY;IAa9B,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,aAAa;WAIR,GAAG,CACd,IAAI,EAAE,SAAS,EACf,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,GAAG,OAAO,CAAC,GAAG;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAC5D,OAAO,CAAC,2BAA2B,GAAG,SAAS,CAAC;IAsB7C,wBAAwB,CAAC,SAAS,CAAC,EAAE,MAAM;;;;IA0BjD,kBAAkB,QAAa,QAAQ,uBAAuB,MAAM,CAAC,EAAE,GAAG,SAAS,CAAC,CAwElF;IAEF,oBAAoB,cAAqB,MAAM;;;;
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
345
|
-
|
|
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
|
-
|
|
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:
|
|
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);
|