@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,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BaseEventsStore = void 0;
|
|
4
|
-
var tslib_1 = require("tslib");
|
|
5
4
|
var constants_1 = require("../constants");
|
|
5
|
+
var utf8_byte_length_1 = require("../utils/utf8-byte-length");
|
|
6
6
|
var BaseEventsStore = /** @class */ (function () {
|
|
7
7
|
function BaseEventsStore(args) {
|
|
8
8
|
var _this = this;
|
|
@@ -11,6 +11,10 @@ var BaseEventsStore = /** @class */ (function () {
|
|
|
11
11
|
this.maxInterval = constants_1.MAX_INTERVAL;
|
|
12
12
|
this.maxPersistedEventsSize = constants_1.DEFAULT_MAX_PERSISTED_EVENTS_SIZE_BYTES;
|
|
13
13
|
this._timeAtLastSplit = Date.now(); // Initialize this so we have a point of comparison when events are recorded
|
|
14
|
+
// Running UTF-8 sizes so shouldSplitEventsList only measures newly appended events.
|
|
15
|
+
// The IDB store loads a fresh array on every add, so it also keeps a per-session entry.
|
|
16
|
+
this.eventsContentBytes = new WeakMap();
|
|
17
|
+
this.sequenceContentBytes = new Map();
|
|
14
18
|
/**
|
|
15
19
|
* Determines whether to send the events list to the backend and start a new
|
|
16
20
|
* empty events list, based on the size of the list as well as the last time sent
|
|
@@ -18,10 +22,14 @@ var BaseEventsStore = /** @class */ (function () {
|
|
|
18
22
|
* @returns boolean
|
|
19
23
|
*/
|
|
20
24
|
this.shouldSplitEventsList = function (events, nextEventString) {
|
|
21
|
-
var sizeOfNextEvent = _this.getStringSize(nextEventString);
|
|
22
25
|
var sizeOfEventsList = _this.getEventsArraySize(events);
|
|
23
|
-
//
|
|
24
|
-
|
|
26
|
+
// 1–3 UTF-8 bytes per code unit: only scan the event when `length` alone cannot decide.
|
|
27
|
+
var room = _this.maxPersistedEventsSize - sizeOfEventsList;
|
|
28
|
+
var exceedsCap = nextEventString.length >= room;
|
|
29
|
+
if (!exceedsCap && nextEventString.length * 3 >= room) {
|
|
30
|
+
exceedsCap = (0, utf8_byte_length_1.utf8ByteLength)(nextEventString) >= room;
|
|
31
|
+
}
|
|
32
|
+
if (exceedsCap) {
|
|
25
33
|
return true;
|
|
26
34
|
}
|
|
27
35
|
if (Date.now() - _this.timeAtLastSplit > _this.interval && events.length) {
|
|
@@ -44,64 +52,38 @@ var BaseEventsStore = /** @class */ (function () {
|
|
|
44
52
|
enumerable: false,
|
|
45
53
|
configurable: true
|
|
46
54
|
});
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
// High surrogate — check for a valid low surrogate before consuming the pair.
|
|
63
|
-
var next = i + 1 < str.length ? str.charCodeAt(i + 1) : NaN;
|
|
64
|
-
if (next >= 0xdc00 && next <= 0xdfff) {
|
|
65
|
-
// Valid surrogate pair → encodes a code point above U+FFFF (4 UTF-8 bytes).
|
|
66
|
-
bytes += 4;
|
|
67
|
-
i++;
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
// Orphaned high surrogate — treated as a replacement character (3 UTF-8 bytes).
|
|
71
|
-
bytes += 3;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
else if (code >= 0xdc00 && code <= 0xdfff) {
|
|
75
|
-
// Orphaned low surrogate — treated as a replacement character (3 UTF-8 bytes).
|
|
76
|
-
bytes += 3;
|
|
77
|
-
}
|
|
78
|
-
else {
|
|
79
|
-
// Other BMP character (U+0800–U+FFFF, excluding surrogates): 3 UTF-8 bytes.
|
|
80
|
-
bytes += 3;
|
|
81
|
-
}
|
|
55
|
+
// `prev` is only reused when `events` still starts with the list it measured; the tail
|
|
56
|
+
// check catches a same-length rewrite of the last element.
|
|
57
|
+
BaseEventsStore.prototype.isPrefixOf = function (prev, events) {
|
|
58
|
+
if (events.length < prev.length)
|
|
59
|
+
return false;
|
|
60
|
+
return prev.length === 0 || events[prev.length - 1] === prev.tail;
|
|
61
|
+
};
|
|
62
|
+
BaseEventsStore.prototype.measureContentBytes = function (prev, events) {
|
|
63
|
+
if (prev && prev.length === events.length && this.isPrefixOf(prev, events))
|
|
64
|
+
return prev;
|
|
65
|
+
var contentBytes = 0;
|
|
66
|
+
var start = 0;
|
|
67
|
+
if (prev && this.isPrefixOf(prev, events)) {
|
|
68
|
+
contentBytes = prev.contentBytes;
|
|
69
|
+
start = prev.length;
|
|
82
70
|
}
|
|
83
|
-
|
|
71
|
+
for (var i = start; i < events.length; i++) {
|
|
72
|
+
contentBytes += (0, utf8_byte_length_1.utf8ByteLength)(events[i]);
|
|
73
|
+
}
|
|
74
|
+
return { length: events.length, contentBytes: contentBytes, tail: events.length > 0 ? events[events.length - 1] : undefined };
|
|
75
|
+
};
|
|
76
|
+
BaseEventsStore.prototype.getEventsContentBytes = function (events) {
|
|
77
|
+
var entry = this.measureContentBytes(this.eventsContentBytes.get(events), events);
|
|
78
|
+
this.eventsContentBytes.set(events, entry);
|
|
79
|
+
return entry.contentBytes;
|
|
84
80
|
};
|
|
85
81
|
/**
|
|
86
82
|
* Calculates the total UTF-8 byte size of events array
|
|
87
83
|
* Accounts for JSON serialization overhead when sent to backend
|
|
88
84
|
*/
|
|
89
85
|
BaseEventsStore.prototype.getEventsArraySize = function (events) {
|
|
90
|
-
var
|
|
91
|
-
var totalSize = 0;
|
|
92
|
-
try {
|
|
93
|
-
for (var events_1 = tslib_1.__values(events), events_1_1 = events_1.next(); !events_1_1.done; events_1_1 = events_1.next()) {
|
|
94
|
-
var event_1 = events_1_1.value;
|
|
95
|
-
totalSize += this.getStringSize(event_1);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
99
|
-
finally {
|
|
100
|
-
try {
|
|
101
|
-
if (events_1_1 && !events_1_1.done && (_a = events_1.return)) _a.call(events_1);
|
|
102
|
-
}
|
|
103
|
-
finally { if (e_1) throw e_1.error; }
|
|
104
|
-
}
|
|
86
|
+
var totalSize = this.getEventsContentBytes(events);
|
|
105
87
|
// Approximate overhead from the array portion of the JSON payload:
|
|
106
88
|
// - Array brackets: [] = 2 bytes
|
|
107
89
|
// - Commas between events: events.length - 1 bytes
|
|
@@ -112,6 +94,20 @@ var BaseEventsStore = /** @class */ (function () {
|
|
|
112
94
|
var overhead = 2 + Math.max(0, events.length - 1) + events.length * 2;
|
|
113
95
|
return totalSize + overhead;
|
|
114
96
|
};
|
|
97
|
+
BaseEventsStore.prototype.recallSequenceContentBytes = function (sessionId, events) {
|
|
98
|
+
var cached = this.sequenceContentBytes.get(sessionId);
|
|
99
|
+
if (!cached || cached.length !== events.length || !this.isPrefixOf(cached, events))
|
|
100
|
+
return;
|
|
101
|
+
this.eventsContentBytes.set(events, cached);
|
|
102
|
+
};
|
|
103
|
+
BaseEventsStore.prototype.rememberSequenceContentBytes = function (sessionId, events) {
|
|
104
|
+
var entry = this.measureContentBytes(this.sequenceContentBytes.get(sessionId), events);
|
|
105
|
+
this.sequenceContentBytes.set(sessionId, entry);
|
|
106
|
+
this.eventsContentBytes.set(events, entry);
|
|
107
|
+
};
|
|
108
|
+
BaseEventsStore.prototype.forgetSequenceContentBytes = function (sessionId) {
|
|
109
|
+
this.sequenceContentBytes.delete(sessionId);
|
|
110
|
+
};
|
|
115
111
|
return BaseEventsStore;
|
|
116
112
|
}());
|
|
117
113
|
exports.BaseEventsStore = 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"}
|
|
@@ -5,6 +5,7 @@ var tslib_1 = require("tslib");
|
|
|
5
5
|
var analytics_core_1 = require("@amplitude/analytics-core");
|
|
6
6
|
var rrweb_types_1 = require("@amplitude/rrweb-types");
|
|
7
7
|
var constants_1 = require("../constants");
|
|
8
|
+
var utf8_byte_length_1 = require("../utils/utf8-byte-length");
|
|
8
9
|
var merge_mutation_events_1 = require("./merge-mutation-events");
|
|
9
10
|
var DEFAULT_TIMEOUT = 2000;
|
|
10
11
|
var EventCompressor = /** @class */ (function () {
|
|
@@ -28,8 +29,9 @@ var EventCompressor = /** @class */ (function () {
|
|
|
28
29
|
var _a;
|
|
29
30
|
// UTF-8 byte size, not JS char count: a 9 M-char string of CJK/emoji can be 18–27 MB
|
|
30
31
|
// on the wire and would otherwise slip past a char-count guard.
|
|
31
|
-
var
|
|
32
|
-
|
|
32
|
+
var maxEventBytes = (_a = _this.config.maxSingleEventSizeBytes) !== null && _a !== void 0 ? _a : constants_1.MAX_SINGLE_EVENT_SIZE;
|
|
33
|
+
var eventSizeBytes = (0, utf8_byte_length_1.utf8ByteLengthIfOver)(compressedEvent, maxEventBytes);
|
|
34
|
+
if (eventSizeBytes !== undefined) {
|
|
33
35
|
_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."));
|
|
34
36
|
return;
|
|
35
37
|
}
|
|
@@ -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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"}
|
|
@@ -273,12 +273,14 @@ var SessionReplayEventsIDBStore = /** @class */ (function (_super) {
|
|
|
273
273
|
})];
|
|
274
274
|
case 4:
|
|
275
275
|
_a.sent();
|
|
276
|
+
this.rememberSequenceContentBytes(sessionId, []);
|
|
276
277
|
this.recordSuccess();
|
|
277
278
|
cancelTimeout();
|
|
278
279
|
_tabId = currentSequenceData.tabId, rest = tslib_1.__rest(currentSequenceData, ["tabId"]);
|
|
279
280
|
return [2 /*return*/, tslib_1.__assign(tslib_1.__assign({}, rest), { sessionId: sessionId, sequenceId: sequenceId })];
|
|
280
281
|
case 5:
|
|
281
282
|
e_2 = _a.sent();
|
|
283
|
+
this.forgetSequenceContentBytes(sessionId);
|
|
282
284
|
if (!timedOut) {
|
|
283
285
|
errorLogged = true;
|
|
284
286
|
(0, is_abort_error_1.logIdbError)(this.loggerProvider, "".concat(messages_1.STORAGE_FAILURE, ": ").concat(e_2), e_2);
|
|
@@ -290,7 +292,7 @@ var SessionReplayEventsIDBStore = /** @class */ (function (_super) {
|
|
|
290
292
|
});
|
|
291
293
|
}); };
|
|
292
294
|
_this.addEventToCurrentSequence = function (sessionId, event) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
|
293
|
-
var errorLogged, timedOut, tx, cancelTimeout, sequenceEvents, ownedSequence, eventsToSend, sequenceId, e_3;
|
|
295
|
+
var errorLogged, timedOut, tx, cancelTimeout, sequenceEvents, nextEvents_1, ownedSequence, nextEvents_2, nextEvents_3, eventsToSend, nextEvents_4, nextEvents, sequenceId, e_3;
|
|
294
296
|
var _this = this;
|
|
295
297
|
return tslib_1.__generator(this, function (_a) {
|
|
296
298
|
switch (_a.label) {
|
|
@@ -331,28 +333,35 @@ var SessionReplayEventsIDBStore = /** @class */ (function (_super) {
|
|
|
331
333
|
case 3:
|
|
332
334
|
_a.sent();
|
|
333
335
|
_a.label = 4;
|
|
334
|
-
case 4:
|
|
336
|
+
case 4:
|
|
337
|
+
nextEvents_1 = [event];
|
|
338
|
+
return [4 /*yield*/, tx.objectStore(exports.currentSequenceKey).put({ sessionId: sessionId, events: nextEvents_1, tabId: this.tabId })];
|
|
335
339
|
case 5:
|
|
336
340
|
_a.sent();
|
|
341
|
+
this.rememberSequenceContentBytes(sessionId, nextEvents_1);
|
|
337
342
|
this.recordSuccess();
|
|
338
343
|
cancelTimeout();
|
|
339
344
|
return [2 /*return*/, undefined];
|
|
340
345
|
case 6:
|
|
341
346
|
ownedSequence = sequenceEvents;
|
|
342
347
|
if (!!ownedSequence) return [3 /*break*/, 8];
|
|
343
|
-
|
|
348
|
+
nextEvents_2 = [event];
|
|
349
|
+
return [4 /*yield*/, tx.objectStore(exports.currentSequenceKey).put({ sessionId: sessionId, events: nextEvents_2, tabId: this.tabId })];
|
|
344
350
|
case 7:
|
|
345
351
|
_a.sent();
|
|
352
|
+
this.rememberSequenceContentBytes(sessionId, nextEvents_2);
|
|
346
353
|
this.recordSuccess();
|
|
347
354
|
cancelTimeout();
|
|
348
355
|
return [2 /*return*/, undefined];
|
|
349
356
|
case 8:
|
|
357
|
+
// Freshly loaded array: seed it with the session's running size so the split check is O(1).
|
|
358
|
+
this.recallSequenceContentBytes(sessionId, ownedSequence.events);
|
|
350
359
|
if (!!this.shouldSplitEventsList(ownedSequence.events, event)) return [3 /*break*/, 10];
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
.put({ sessionId: sessionId, events: ownedSequence.events.concat(event), tabId: this.tabId })];
|
|
360
|
+
nextEvents_3 = ownedSequence.events.concat(event);
|
|
361
|
+
return [4 /*yield*/, tx.objectStore(exports.currentSequenceKey).put({ sessionId: sessionId, events: nextEvents_3, tabId: this.tabId })];
|
|
354
362
|
case 9:
|
|
355
363
|
_a.sent();
|
|
364
|
+
this.rememberSequenceContentBytes(sessionId, nextEvents_3);
|
|
356
365
|
this.recordSuccess();
|
|
357
366
|
cancelTimeout();
|
|
358
367
|
return [2 /*return*/, undefined];
|
|
@@ -360,15 +369,20 @@ var SessionReplayEventsIDBStore = /** @class */ (function (_super) {
|
|
|
360
369
|
eventsToSend = ownedSequence.events;
|
|
361
370
|
if (!(eventsToSend.length === 0)) return [3 /*break*/, 12];
|
|
362
371
|
this.maybeLogEmptyFiltered('addEventToCurrentSequence');
|
|
363
|
-
|
|
372
|
+
nextEvents_4 = [event];
|
|
373
|
+
return [4 /*yield*/, tx.objectStore(exports.currentSequenceKey).put({ sessionId: sessionId, events: nextEvents_4, tabId: this.tabId })];
|
|
364
374
|
case 11:
|
|
365
375
|
_a.sent();
|
|
376
|
+
this.rememberSequenceContentBytes(sessionId, nextEvents_4);
|
|
366
377
|
this.recordSuccess();
|
|
367
378
|
cancelTimeout();
|
|
368
379
|
return [2 /*return*/, undefined];
|
|
369
|
-
case 12:
|
|
380
|
+
case 12:
|
|
381
|
+
nextEvents = [event];
|
|
382
|
+
return [4 /*yield*/, tx.objectStore(exports.currentSequenceKey).put({ sessionId: sessionId, events: nextEvents, tabId: this.tabId })];
|
|
370
383
|
case 13:
|
|
371
384
|
_a.sent();
|
|
385
|
+
this.rememberSequenceContentBytes(sessionId, nextEvents);
|
|
372
386
|
return [4 /*yield*/, tx.objectStore(exports.sequencesToSendKey).put({
|
|
373
387
|
sessionId: sessionId,
|
|
374
388
|
events: eventsToSend,
|
|
@@ -385,6 +399,7 @@ var SessionReplayEventsIDBStore = /** @class */ (function (_super) {
|
|
|
385
399
|
}];
|
|
386
400
|
case 15:
|
|
387
401
|
e_3 = _a.sent();
|
|
402
|
+
this.forgetSequenceContentBytes(sessionId);
|
|
388
403
|
if (!timedOut) {
|
|
389
404
|
errorLogged = true;
|
|
390
405
|
(0, is_abort_error_1.logIdbError)(this.loggerProvider, "".concat(messages_1.STORAGE_FAILURE, ": ").concat(e_3), e_3);
|