@amplitude/plugin-session-replay-browser 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +46 -0
- package/lib/cjs/constants.d.ts +5 -0
- package/lib/cjs/constants.d.ts.map +1 -0
- package/lib/cjs/constants.js +7 -0
- package/lib/cjs/constants.js.map +1 -0
- package/lib/cjs/helpers.d.ts +2 -0
- package/lib/cjs/helpers.d.ts.map +1 -0
- package/lib/cjs/helpers.js +12 -0
- package/lib/cjs/helpers.js.map +1 -0
- package/lib/cjs/index.d.ts +2 -0
- package/lib/cjs/index.d.ts.map +1 -0
- package/lib/cjs/index.js +6 -0
- package/lib/cjs/index.js.map +1 -0
- package/lib/cjs/messages.d.ts +5 -0
- package/lib/cjs/messages.d.ts.map +1 -0
- package/lib/cjs/messages.js +7 -0
- package/lib/cjs/messages.js.map +1 -0
- package/lib/cjs/session-replay.d.ts +3 -0
- package/lib/cjs/session-replay.d.ts.map +1 -0
- package/lib/cjs/session-replay.js +353 -0
- package/lib/cjs/session-replay.js.map +1 -0
- package/lib/cjs/typings/session-replay.d.ts +54 -0
- package/lib/cjs/typings/session-replay.d.ts.map +1 -0
- package/lib/cjs/typings/session-replay.js +2 -0
- package/lib/cjs/typings/session-replay.js.map +1 -0
- package/lib/esm/constants.d.ts +5 -0
- package/lib/esm/constants.d.ts.map +1 -0
- package/lib/esm/constants.js +5 -0
- package/lib/esm/constants.js.map +1 -0
- package/lib/esm/helpers.d.ts +2 -0
- package/lib/esm/helpers.d.ts.map +1 -0
- package/lib/esm/helpers.js +9 -0
- package/lib/esm/helpers.js.map +1 -0
- package/lib/esm/index.d.ts +2 -0
- package/lib/esm/index.d.ts.map +1 -0
- package/lib/esm/index.js +2 -0
- package/lib/esm/index.js.map +1 -0
- package/lib/esm/messages.d.ts +5 -0
- package/lib/esm/messages.d.ts.map +1 -0
- package/lib/esm/messages.js +5 -0
- package/lib/esm/messages.js.map +1 -0
- package/lib/esm/session-replay.d.ts +3 -0
- package/lib/esm/session-replay.d.ts.map +1 -0
- package/lib/esm/session-replay.js +350 -0
- package/lib/esm/session-replay.js.map +1 -0
- package/lib/esm/typings/session-replay.d.ts +54 -0
- package/lib/esm/typings/session-replay.d.ts.map +1 -0
- package/lib/esm/typings/session-replay.js +2 -0
- package/lib/esm/typings/session-replay.js.map +1 -0
- package/lib/scripts/amplitude-min.js +1 -0
- package/lib/scripts/amplitude-min.js.gz +0 -0
- package/lib/scripts/amplitude-min.umd.js +1 -0
- package/lib/scripts/amplitude-min.umd.js.gz +0 -0
- package/lib/scripts/constants.d.ts +5 -0
- package/lib/scripts/constants.d.ts.map +1 -0
- package/lib/scripts/helpers.d.ts +2 -0
- package/lib/scripts/helpers.d.ts.map +1 -0
- package/lib/scripts/index.d.ts +2 -0
- package/lib/scripts/index.d.ts.map +1 -0
- package/lib/scripts/messages.d.ts +5 -0
- package/lib/scripts/messages.d.ts.map +1 -0
- package/lib/scripts/session-replay.d.ts +3 -0
- package/lib/scripts/session-replay.d.ts.map +1 -0
- package/lib/scripts/typings/session-replay.d.ts +54 -0
- package/lib/scripts/typings/session-replay.d.ts.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,6BAA6B,gBAAgB,CAAC;AAE3D,eAAO,MAAM,+BAA+B,QAAsD,CAAC;AACnG,eAAO,MAAM,2BAA2B,kBAAkB,CAAC;AAC3D,eAAO,MAAM,yBAAyB,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export var DEFAULT_EVENT_PROPERTY_PREFIX = '[Amplitude]';
|
|
2
|
+
export var DEFAULT_SESSION_REPLAY_PROPERTY = "".concat(DEFAULT_EVENT_PROPERTY_PREFIX, " Session Recorded");
|
|
3
|
+
export var DEFAULT_SESSION_START_EVENT = 'session_start';
|
|
4
|
+
export var DEFAULT_SESSION_END_EVENT = 'session_end';
|
|
5
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,IAAM,6BAA6B,GAAG,aAAa,CAAC;AAE3D,MAAM,CAAC,IAAM,+BAA+B,GAAG,UAAG,6BAA6B,sBAAmB,CAAC;AACnG,MAAM,CAAC,IAAM,2BAA2B,GAAG,eAAe,CAAC;AAC3D,MAAM,CAAC,IAAM,yBAAyB,GAAG,aAAa,CAAC","sourcesContent":["export const DEFAULT_EVENT_PROPERTY_PREFIX = '[Amplitude]';\n\nexport const DEFAULT_SESSION_REPLAY_PROPERTY = `${DEFAULT_EVENT_PROPERTY_PREFIX} Session Recorded`;\nexport const DEFAULT_SESSION_START_EVENT = 'session_start';\nexport const DEFAULT_SESSION_END_EVENT = 'session_end';\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/helpers.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB,eAAgB,MAAM,EAAE,mBAAmB,MAAM,eAAe,MAAM,KAAG,OAO1G,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export var shouldSplitEventsList = function (eventsList, nextEventString, maxListSize) {
|
|
2
|
+
var sizeOfNextEvent = new Blob([nextEventString]).size;
|
|
3
|
+
var sizeOfEventsList = new Blob(eventsList).size;
|
|
4
|
+
if (sizeOfEventsList + sizeOfNextEvent >= maxListSize) {
|
|
5
|
+
return true;
|
|
6
|
+
}
|
|
7
|
+
return false;
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/helpers.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,IAAM,qBAAqB,GAAG,UAAC,UAAoB,EAAE,eAAuB,EAAE,WAAmB;IACtG,IAAM,eAAe,GAAG,IAAI,IAAI,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC;IACzD,IAAM,gBAAgB,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC;IACnD,IAAI,gBAAgB,GAAG,eAAe,IAAI,WAAW,EAAE;QACrD,OAAO,IAAI,CAAC;KACb;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC","sourcesContent":["export const shouldSplitEventsList = (eventsList: string[], nextEventString: string, maxListSize: number): boolean => {\n const sizeOfNextEvent = new Blob([nextEventString]).size;\n const sizeOfEventsList = new Blob(eventsList).size;\n if (sizeOfEventsList + sizeOfNextEvent >= maxListSize) {\n return true;\n }\n return false;\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,IAAI,MAAM,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC"}
|
package/lib/esm/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,IAAI,MAAM,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC","sourcesContent":["export { sessionReplayPlugin as plugin, sessionReplayPlugin } from './session-replay';\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const SUCCESS_MESSAGE = "Session replay event batch tracked successfully";
|
|
2
|
+
export declare const UNEXPECTED_ERROR_MESSAGE = "Unexpected error occurred";
|
|
3
|
+
export declare const MAX_RETRIES_EXCEEDED_MESSAGE = "Session replay event batch rejected due to exceeded retry count";
|
|
4
|
+
export declare const STORAGE_FAILURE = "Failed to store session replay events in IndexedDB";
|
|
5
|
+
//# sourceMappingURL=messages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/messages.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,oDAAoD,CAAC;AACjF,eAAO,MAAM,wBAAwB,8BAA8B,CAAC;AACpE,eAAO,MAAM,4BAA4B,oEAAoE,CAAC;AAC9G,eAAO,MAAM,eAAe,uDAAuD,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export var SUCCESS_MESSAGE = 'Session replay event batch tracked successfully';
|
|
2
|
+
export var UNEXPECTED_ERROR_MESSAGE = 'Unexpected error occurred';
|
|
3
|
+
export var MAX_RETRIES_EXCEEDED_MESSAGE = 'Session replay event batch rejected due to exceeded retry count';
|
|
4
|
+
export var STORAGE_FAILURE = 'Failed to store session replay events in IndexedDB';
|
|
5
|
+
//# sourceMappingURL=messages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/messages.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,IAAM,eAAe,GAAG,iDAAiD,CAAC;AACjF,MAAM,CAAC,IAAM,wBAAwB,GAAG,2BAA2B,CAAC;AACpE,MAAM,CAAC,IAAM,4BAA4B,GAAG,iEAAiE,CAAC;AAC9G,MAAM,CAAC,IAAM,eAAe,GAAG,oDAAoD,CAAC","sourcesContent":["export const SUCCESS_MESSAGE = 'Session replay event batch tracked successfully';\nexport const UNEXPECTED_ERROR_MESSAGE = 'Unexpected error occurred';\nexport const MAX_RETRIES_EXCEEDED_MESSAGE = 'Session replay event batch rejected due to exceeded retry count';\nexport const STORAGE_FAILURE = 'Failed to store session replay events in IndexedDB';\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-replay.d.ts","sourceRoot":"","sources":["../../src/session-replay.ts"],"names":[],"mappings":"AAOA,OAAO,EAKL,mBAAmB,EACpB,MAAM,0BAA0B,CAAC;AAwRlC,eAAO,MAAM,mBAAmB,EAAE,mBAEjC,CAAC"}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { __assign, __awaiter, __generator } from "tslib";
|
|
2
|
+
import { AMPLITUDE_PREFIX, BaseTransport } from '@amplitude/analytics-core';
|
|
3
|
+
import { PluginType, Status } from '@amplitude/analytics-types';
|
|
4
|
+
import * as IDBKeyVal from 'idb-keyval';
|
|
5
|
+
import { pack, record } from 'rrweb';
|
|
6
|
+
import { DEFAULT_SESSION_END_EVENT, DEFAULT_SESSION_REPLAY_PROPERTY, DEFAULT_SESSION_START_EVENT } from './constants';
|
|
7
|
+
import { shouldSplitEventsList } from './helpers';
|
|
8
|
+
import { MAX_RETRIES_EXCEEDED_MESSAGE, STORAGE_FAILURE, SUCCESS_MESSAGE, UNEXPECTED_ERROR_MESSAGE } from './messages';
|
|
9
|
+
var SESSION_REPLAY_SERVER_URL = 'https://api-secure.amplitude.com/sessions/track';
|
|
10
|
+
var STORAGE_PREFIX = "".concat(AMPLITUDE_PREFIX, "_replay_unsent");
|
|
11
|
+
var PAYLOAD_ESTIMATED_SIZE_IN_BYTES_WITHOUT_EVENTS = 200; // derived by JSON stringifying an example payload without events
|
|
12
|
+
var MAX_EVENT_LIST_SIZE_IN_BYTES = 20 * 1000000 - PAYLOAD_ESTIMATED_SIZE_IN_BYTES_WITHOUT_EVENTS;
|
|
13
|
+
var SessionReplay = /** @class */ (function () {
|
|
14
|
+
function SessionReplay() {
|
|
15
|
+
this.name = '@amplitude/plugin-session-replay-browser';
|
|
16
|
+
this.type = PluginType.ENRICHMENT;
|
|
17
|
+
this.storageKey = '';
|
|
18
|
+
this.retryTimeout = 1000;
|
|
19
|
+
this.events = [];
|
|
20
|
+
this.currentSequenceId = 0;
|
|
21
|
+
this.scheduled = null;
|
|
22
|
+
this.queue = [];
|
|
23
|
+
this.stopRecordingEvents = null;
|
|
24
|
+
this.maxPersistedEventsSize = MAX_EVENT_LIST_SIZE_IN_BYTES;
|
|
25
|
+
}
|
|
26
|
+
SessionReplay.prototype.setup = function (config) {
|
|
27
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
28
|
+
return __generator(this, function (_a) {
|
|
29
|
+
config.loggerProvider.log('Installing @amplitude/plugin-session-replay.');
|
|
30
|
+
this.config = config;
|
|
31
|
+
this.storageKey = "".concat(STORAGE_PREFIX, "_").concat(this.config.apiKey.substring(0, 10));
|
|
32
|
+
void this.emptyStoreAndReset();
|
|
33
|
+
return [2 /*return*/];
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
SessionReplay.prototype.execute = function (event) {
|
|
38
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
39
|
+
var _a;
|
|
40
|
+
return __generator(this, function (_b) {
|
|
41
|
+
event.event_properties = __assign(__assign({}, event.event_properties), (_a = {}, _a[DEFAULT_SESSION_REPLAY_PROPERTY] = true, _a));
|
|
42
|
+
if (event.event_type === DEFAULT_SESSION_START_EVENT && this.stopRecordingEvents) {
|
|
43
|
+
this.stopRecordingEvents();
|
|
44
|
+
this.recordEvents();
|
|
45
|
+
}
|
|
46
|
+
else if (event.event_type === DEFAULT_SESSION_END_EVENT) {
|
|
47
|
+
if (event.session_id) {
|
|
48
|
+
this.sendEventsList({
|
|
49
|
+
events: this.events,
|
|
50
|
+
sequenceId: this.currentSequenceId,
|
|
51
|
+
sessionId: event.session_id,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
this.events = [];
|
|
55
|
+
this.currentSequenceId = 0;
|
|
56
|
+
}
|
|
57
|
+
return [2 /*return*/, Promise.resolve(event)];
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
SessionReplay.prototype.emptyStoreAndReset = function () {
|
|
62
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
63
|
+
var storedReplaySessions, sessionId, storedReplayEvents, currentSessionStoredEvents;
|
|
64
|
+
return __generator(this, function (_a) {
|
|
65
|
+
switch (_a.label) {
|
|
66
|
+
case 0: return [4 /*yield*/, this.getAllSessionEventsFromStore()];
|
|
67
|
+
case 1:
|
|
68
|
+
storedReplaySessions = _a.sent();
|
|
69
|
+
if (storedReplaySessions) {
|
|
70
|
+
for (sessionId in storedReplaySessions) {
|
|
71
|
+
storedReplayEvents = storedReplaySessions[sessionId];
|
|
72
|
+
if (storedReplayEvents.events.length) {
|
|
73
|
+
this.sendEventsList({
|
|
74
|
+
events: storedReplayEvents.events,
|
|
75
|
+
sequenceId: storedReplayEvents.sequenceId,
|
|
76
|
+
sessionId: parseInt(sessionId, 10),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
this.events = [];
|
|
81
|
+
currentSessionStoredEvents = this.config.sessionId && storedReplaySessions[this.config.sessionId];
|
|
82
|
+
this.currentSequenceId = currentSessionStoredEvents ? currentSessionStoredEvents.sequenceId + 1 : 0;
|
|
83
|
+
void this.storeEventsForSession([], this.currentSequenceId);
|
|
84
|
+
this.recordEvents();
|
|
85
|
+
}
|
|
86
|
+
return [2 /*return*/];
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
SessionReplay.prototype.recordEvents = function () {
|
|
92
|
+
var _this = this;
|
|
93
|
+
this.stopRecordingEvents = record({
|
|
94
|
+
emit: function (event) {
|
|
95
|
+
var eventString = JSON.stringify(event);
|
|
96
|
+
var shouldSplit = shouldSplitEventsList(_this.events, eventString, _this.maxPersistedEventsSize);
|
|
97
|
+
if (shouldSplit) {
|
|
98
|
+
_this.sendEventsList({
|
|
99
|
+
events: _this.events,
|
|
100
|
+
sequenceId: _this.currentSequenceId,
|
|
101
|
+
sessionId: _this.config.sessionId,
|
|
102
|
+
});
|
|
103
|
+
_this.events = [];
|
|
104
|
+
_this.currentSequenceId++;
|
|
105
|
+
}
|
|
106
|
+
_this.events.push(eventString);
|
|
107
|
+
void _this.storeEventsForSession(_this.events, _this.currentSequenceId);
|
|
108
|
+
},
|
|
109
|
+
packFn: pack,
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
SessionReplay.prototype.sendEventsList = function (_a) {
|
|
113
|
+
var events = _a.events, sequenceId = _a.sequenceId, sessionId = _a.sessionId;
|
|
114
|
+
this.addToQueue({
|
|
115
|
+
events: events,
|
|
116
|
+
sequenceId: sequenceId,
|
|
117
|
+
attempts: 0,
|
|
118
|
+
timeout: 0,
|
|
119
|
+
sessionId: sessionId,
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
SessionReplay.prototype.addToQueue = function () {
|
|
123
|
+
var _this = this;
|
|
124
|
+
var list = [];
|
|
125
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
126
|
+
list[_i] = arguments[_i];
|
|
127
|
+
}
|
|
128
|
+
var tryable = list.filter(function (context) {
|
|
129
|
+
if (context.attempts < _this.config.flushMaxRetries) {
|
|
130
|
+
context.attempts += 1;
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
_this.completeRequest({
|
|
134
|
+
context: context,
|
|
135
|
+
err: "".concat(MAX_RETRIES_EXCEEDED_MESSAGE, ", batch sequence id, ").concat(context.sequenceId),
|
|
136
|
+
});
|
|
137
|
+
return false;
|
|
138
|
+
});
|
|
139
|
+
tryable.forEach(function (context) {
|
|
140
|
+
_this.queue = _this.queue.concat(context);
|
|
141
|
+
if (context.timeout === 0) {
|
|
142
|
+
_this.schedule(_this.config.flushIntervalMillis);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
setTimeout(function () {
|
|
146
|
+
context.timeout = 0;
|
|
147
|
+
_this.schedule(0);
|
|
148
|
+
}, context.timeout);
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
SessionReplay.prototype.schedule = function (timeout) {
|
|
152
|
+
var _this = this;
|
|
153
|
+
if (this.scheduled)
|
|
154
|
+
return;
|
|
155
|
+
this.scheduled = setTimeout(function () {
|
|
156
|
+
void _this.flush(true).then(function () {
|
|
157
|
+
if (_this.queue.length > 0) {
|
|
158
|
+
_this.schedule(timeout);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}, timeout);
|
|
162
|
+
};
|
|
163
|
+
SessionReplay.prototype.flush = function (useRetry) {
|
|
164
|
+
if (useRetry === void 0) { useRetry = false; }
|
|
165
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
166
|
+
var list, later;
|
|
167
|
+
var _this = this;
|
|
168
|
+
return __generator(this, function (_a) {
|
|
169
|
+
switch (_a.label) {
|
|
170
|
+
case 0:
|
|
171
|
+
list = [];
|
|
172
|
+
later = [];
|
|
173
|
+
this.queue.forEach(function (context) { return (context.timeout === 0 ? list.push(context) : later.push(context)); });
|
|
174
|
+
this.queue = later;
|
|
175
|
+
if (this.scheduled) {
|
|
176
|
+
clearTimeout(this.scheduled);
|
|
177
|
+
this.scheduled = null;
|
|
178
|
+
}
|
|
179
|
+
return [4 /*yield*/, Promise.all(list.map(function (context) { return _this.send(context, useRetry); }))];
|
|
180
|
+
case 1:
|
|
181
|
+
_a.sent();
|
|
182
|
+
return [2 /*return*/];
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
};
|
|
187
|
+
SessionReplay.prototype.send = function (context, useRetry) {
|
|
188
|
+
if (useRetry === void 0) { useRetry = true; }
|
|
189
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
190
|
+
var payload, options, res, responseBody, e_1;
|
|
191
|
+
return __generator(this, function (_a) {
|
|
192
|
+
switch (_a.label) {
|
|
193
|
+
case 0:
|
|
194
|
+
payload = {
|
|
195
|
+
api_key: this.config.apiKey,
|
|
196
|
+
device_id: this.config.deviceId,
|
|
197
|
+
session_id: context.sessionId,
|
|
198
|
+
start_timestamp: context.sessionId,
|
|
199
|
+
events_batch: {
|
|
200
|
+
version: 1,
|
|
201
|
+
events: context.events,
|
|
202
|
+
seq_number: context.sequenceId,
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
_a.label = 1;
|
|
206
|
+
case 1:
|
|
207
|
+
_a.trys.push([1, 3, , 4]);
|
|
208
|
+
options = {
|
|
209
|
+
headers: {
|
|
210
|
+
'Content-Type': 'application/json',
|
|
211
|
+
Accept: '*/*',
|
|
212
|
+
},
|
|
213
|
+
body: JSON.stringify(payload),
|
|
214
|
+
method: 'POST',
|
|
215
|
+
};
|
|
216
|
+
return [4 /*yield*/, fetch(SESSION_REPLAY_SERVER_URL, options)];
|
|
217
|
+
case 2:
|
|
218
|
+
res = _a.sent();
|
|
219
|
+
if (res === null) {
|
|
220
|
+
this.completeRequest({ context: context, err: UNEXPECTED_ERROR_MESSAGE, removeEvents: false });
|
|
221
|
+
return [2 /*return*/];
|
|
222
|
+
}
|
|
223
|
+
if (!useRetry) {
|
|
224
|
+
responseBody = '';
|
|
225
|
+
try {
|
|
226
|
+
responseBody = JSON.stringify(res.body, null, 2);
|
|
227
|
+
}
|
|
228
|
+
catch (_b) {
|
|
229
|
+
// to avoid crash, but don't care about the error, add comment to avoid empty block lint error
|
|
230
|
+
}
|
|
231
|
+
this.completeRequest({ context: context, success: "".concat(res.status, ": ").concat(responseBody) });
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
this.handleReponse(res.status, context);
|
|
235
|
+
}
|
|
236
|
+
return [3 /*break*/, 4];
|
|
237
|
+
case 3:
|
|
238
|
+
e_1 = _a.sent();
|
|
239
|
+
this.completeRequest({ context: context, err: e_1, removeEvents: false });
|
|
240
|
+
return [3 /*break*/, 4];
|
|
241
|
+
case 4: return [2 /*return*/];
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
};
|
|
246
|
+
SessionReplay.prototype.handleReponse = function (status, context) {
|
|
247
|
+
var parsedStatus = new BaseTransport().buildStatus(status);
|
|
248
|
+
switch (parsedStatus) {
|
|
249
|
+
case Status.Success:
|
|
250
|
+
this.handleSuccessResponse(context);
|
|
251
|
+
break;
|
|
252
|
+
default:
|
|
253
|
+
this.handleOtherResponse(context);
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
SessionReplay.prototype.handleSuccessResponse = function (context) {
|
|
257
|
+
this.completeRequest({ context: context, success: SUCCESS_MESSAGE });
|
|
258
|
+
};
|
|
259
|
+
SessionReplay.prototype.handleOtherResponse = function (context) {
|
|
260
|
+
this.addToQueue(__assign(__assign({}, context), { timeout: context.attempts * this.retryTimeout }));
|
|
261
|
+
};
|
|
262
|
+
SessionReplay.prototype.getAllSessionEventsFromStore = function () {
|
|
263
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
264
|
+
var storedReplaySessionContexts, e_2;
|
|
265
|
+
return __generator(this, function (_a) {
|
|
266
|
+
switch (_a.label) {
|
|
267
|
+
case 0:
|
|
268
|
+
_a.trys.push([0, 2, , 3]);
|
|
269
|
+
return [4 /*yield*/, IDBKeyVal.get(this.storageKey)];
|
|
270
|
+
case 1:
|
|
271
|
+
storedReplaySessionContexts = _a.sent();
|
|
272
|
+
return [2 /*return*/, storedReplaySessionContexts];
|
|
273
|
+
case 2:
|
|
274
|
+
e_2 = _a.sent();
|
|
275
|
+
this.config.loggerProvider.error("".concat(STORAGE_FAILURE, ": ").concat(e_2));
|
|
276
|
+
return [3 /*break*/, 3];
|
|
277
|
+
case 3: return [2 /*return*/, undefined];
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
};
|
|
282
|
+
SessionReplay.prototype.storeEventsForSession = function (events, sequenceId) {
|
|
283
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
284
|
+
var e_3;
|
|
285
|
+
var _this = this;
|
|
286
|
+
return __generator(this, function (_a) {
|
|
287
|
+
switch (_a.label) {
|
|
288
|
+
case 0:
|
|
289
|
+
_a.trys.push([0, 2, , 3]);
|
|
290
|
+
return [4 /*yield*/, IDBKeyVal.update(this.storageKey, function (sessionMap) {
|
|
291
|
+
var _a;
|
|
292
|
+
return __assign(__assign({}, sessionMap), (_this.config.sessionId && (_a = {},
|
|
293
|
+
_a[_this.config.sessionId] = {
|
|
294
|
+
events: events,
|
|
295
|
+
sequenceId: sequenceId,
|
|
296
|
+
},
|
|
297
|
+
_a)));
|
|
298
|
+
})];
|
|
299
|
+
case 1:
|
|
300
|
+
_a.sent();
|
|
301
|
+
return [3 /*break*/, 3];
|
|
302
|
+
case 2:
|
|
303
|
+
e_3 = _a.sent();
|
|
304
|
+
this.config.loggerProvider.error("".concat(STORAGE_FAILURE, ": ").concat(e_3));
|
|
305
|
+
return [3 /*break*/, 3];
|
|
306
|
+
case 3: return [2 /*return*/];
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
};
|
|
311
|
+
SessionReplay.prototype.removeSessionEventsStore = function (sessionId) {
|
|
312
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
313
|
+
var e_4;
|
|
314
|
+
return __generator(this, function (_a) {
|
|
315
|
+
switch (_a.label) {
|
|
316
|
+
case 0:
|
|
317
|
+
_a.trys.push([0, 2, , 3]);
|
|
318
|
+
return [4 /*yield*/, IDBKeyVal.update(this.storageKey, function (sessionMap) {
|
|
319
|
+
if (sessionMap === void 0) { sessionMap = {}; }
|
|
320
|
+
delete sessionMap[sessionId];
|
|
321
|
+
return sessionMap;
|
|
322
|
+
})];
|
|
323
|
+
case 1:
|
|
324
|
+
_a.sent();
|
|
325
|
+
return [3 /*break*/, 3];
|
|
326
|
+
case 2:
|
|
327
|
+
e_4 = _a.sent();
|
|
328
|
+
this.config.loggerProvider.error("".concat(STORAGE_FAILURE, ": ").concat(e_4));
|
|
329
|
+
return [3 /*break*/, 3];
|
|
330
|
+
case 3: return [2 /*return*/];
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
};
|
|
335
|
+
SessionReplay.prototype.completeRequest = function (_a) {
|
|
336
|
+
var context = _a.context, err = _a.err, success = _a.success, _b = _a.removeEvents, removeEvents = _b === void 0 ? true : _b;
|
|
337
|
+
removeEvents && context.sessionId && this.removeSessionEventsStore(context.sessionId);
|
|
338
|
+
if (err) {
|
|
339
|
+
this.config.loggerProvider.error(err);
|
|
340
|
+
}
|
|
341
|
+
else if (success) {
|
|
342
|
+
this.config.loggerProvider.log(success);
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
return SessionReplay;
|
|
346
|
+
}());
|
|
347
|
+
export var sessionReplayPlugin = function () {
|
|
348
|
+
return new SessionReplay();
|
|
349
|
+
};
|
|
350
|
+
//# sourceMappingURL=session-replay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-replay.js","sourceRoot":"","sources":["../../src/session-replay.ts"],"names":[],"mappings":"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","sourcesContent":["import { AMPLITUDE_PREFIX, BaseTransport } from '@amplitude/analytics-core';\nimport { BrowserConfig, Event, PluginType, Status } from '@amplitude/analytics-types';\nimport * as IDBKeyVal from 'idb-keyval';\nimport { pack, record } from 'rrweb';\nimport { DEFAULT_SESSION_END_EVENT, DEFAULT_SESSION_REPLAY_PROPERTY, DEFAULT_SESSION_START_EVENT } from './constants';\nimport { shouldSplitEventsList } from './helpers';\nimport { MAX_RETRIES_EXCEEDED_MESSAGE, STORAGE_FAILURE, SUCCESS_MESSAGE, UNEXPECTED_ERROR_MESSAGE } from './messages';\nimport {\n Events,\n IDBStore,\n SessionReplayContext,\n SessionReplayEnrichmentPlugin,\n SessionReplayPlugin,\n} from './typings/session-replay';\n\nconst SESSION_REPLAY_SERVER_URL = 'https://api-secure.amplitude.com/sessions/track';\nconst STORAGE_PREFIX = `${AMPLITUDE_PREFIX}_replay_unsent`;\nconst PAYLOAD_ESTIMATED_SIZE_IN_BYTES_WITHOUT_EVENTS = 200; // derived by JSON stringifying an example payload without events\nconst MAX_EVENT_LIST_SIZE_IN_BYTES = 20 * 1000000 - PAYLOAD_ESTIMATED_SIZE_IN_BYTES_WITHOUT_EVENTS;\n\nclass SessionReplay implements SessionReplayEnrichmentPlugin {\n name = '@amplitude/plugin-session-replay-browser';\n type = PluginType.ENRICHMENT as const;\n // this.config is defined in setup() which will always be called first\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n config: BrowserConfig;\n storageKey = '';\n retryTimeout = 1000;\n events: Events = [];\n currentSequenceId = 0;\n private scheduled: ReturnType<typeof setTimeout> | null = null;\n queue: SessionReplayContext[] = [];\n stopRecordingEvents: ReturnType<typeof record> | null = null;\n maxPersistedEventsSize = MAX_EVENT_LIST_SIZE_IN_BYTES;\n\n async setup(config: BrowserConfig) {\n config.loggerProvider.log('Installing @amplitude/plugin-session-replay.');\n\n this.config = config;\n this.storageKey = `${STORAGE_PREFIX}_${this.config.apiKey.substring(0, 10)}`;\n void this.emptyStoreAndReset();\n }\n\n async execute(event: Event) {\n event.event_properties = {\n ...event.event_properties,\n [DEFAULT_SESSION_REPLAY_PROPERTY]: true,\n };\n if (event.event_type === DEFAULT_SESSION_START_EVENT && this.stopRecordingEvents) {\n this.stopRecordingEvents();\n this.recordEvents();\n } else if (event.event_type === DEFAULT_SESSION_END_EVENT) {\n if (event.session_id) {\n this.sendEventsList({\n events: this.events,\n sequenceId: this.currentSequenceId,\n sessionId: event.session_id,\n });\n }\n this.events = [];\n this.currentSequenceId = 0;\n }\n return Promise.resolve(event);\n }\n\n async emptyStoreAndReset() {\n const storedReplaySessions = await this.getAllSessionEventsFromStore();\n if (storedReplaySessions) {\n for (const sessionId in storedReplaySessions) {\n const storedReplayEvents = storedReplaySessions[sessionId];\n if (storedReplayEvents.events.length) {\n this.sendEventsList({\n events: storedReplayEvents.events,\n sequenceId: storedReplayEvents.sequenceId,\n sessionId: parseInt(sessionId, 10),\n });\n }\n }\n this.events = [];\n const currentSessionStoredEvents = this.config.sessionId && storedReplaySessions[this.config.sessionId];\n this.currentSequenceId = currentSessionStoredEvents ? currentSessionStoredEvents.sequenceId + 1 : 0;\n void this.storeEventsForSession([], this.currentSequenceId);\n this.recordEvents();\n }\n }\n\n recordEvents() {\n this.stopRecordingEvents = record({\n emit: (event) => {\n const eventString = JSON.stringify(event);\n const shouldSplit = shouldSplitEventsList(this.events, eventString, this.maxPersistedEventsSize);\n if (shouldSplit) {\n this.sendEventsList({\n events: this.events,\n sequenceId: this.currentSequenceId,\n sessionId: this.config.sessionId as number,\n });\n this.events = [];\n this.currentSequenceId++;\n }\n this.events.push(eventString);\n void this.storeEventsForSession(this.events, this.currentSequenceId);\n },\n packFn: pack,\n });\n }\n\n sendEventsList({ events, sequenceId, sessionId }: { events: string[]; sequenceId: number; sessionId: number }) {\n this.addToQueue({\n events,\n sequenceId,\n attempts: 0,\n timeout: 0,\n sessionId,\n });\n }\n\n addToQueue(...list: SessionReplayContext[]) {\n const tryable = list.filter((context) => {\n if (context.attempts < this.config.flushMaxRetries) {\n context.attempts += 1;\n return true;\n }\n this.completeRequest({\n context,\n err: `${MAX_RETRIES_EXCEEDED_MESSAGE}, batch sequence id, ${context.sequenceId}`,\n });\n return false;\n });\n tryable.forEach((context) => {\n this.queue = this.queue.concat(context);\n if (context.timeout === 0) {\n this.schedule(this.config.flushIntervalMillis);\n return;\n }\n\n setTimeout(() => {\n context.timeout = 0;\n this.schedule(0);\n }, context.timeout);\n });\n }\n\n schedule(timeout: number) {\n if (this.scheduled) return;\n this.scheduled = setTimeout(() => {\n void this.flush(true).then(() => {\n if (this.queue.length > 0) {\n this.schedule(timeout);\n }\n });\n }, timeout);\n }\n\n async flush(useRetry = false) {\n const list: SessionReplayContext[] = [];\n const later: SessionReplayContext[] = [];\n this.queue.forEach((context) => (context.timeout === 0 ? list.push(context) : later.push(context)));\n this.queue = later;\n\n if (this.scheduled) {\n clearTimeout(this.scheduled);\n this.scheduled = null;\n }\n\n await Promise.all(list.map((context) => this.send(context, useRetry)));\n }\n\n async send(context: SessionReplayContext, useRetry = true) {\n const payload = {\n api_key: this.config.apiKey,\n device_id: this.config.deviceId,\n session_id: context.sessionId,\n start_timestamp: context.sessionId,\n events_batch: {\n version: 1,\n events: context.events,\n seq_number: context.sequenceId,\n },\n };\n try {\n const options: RequestInit = {\n headers: {\n 'Content-Type': 'application/json',\n Accept: '*/*',\n },\n body: JSON.stringify(payload),\n method: 'POST',\n };\n const res = await fetch(SESSION_REPLAY_SERVER_URL, options);\n if (res === null) {\n this.completeRequest({ context, err: UNEXPECTED_ERROR_MESSAGE, removeEvents: false });\n return;\n }\n if (!useRetry) {\n let responseBody = '';\n try {\n responseBody = JSON.stringify(res.body, null, 2);\n } catch {\n // to avoid crash, but don't care about the error, add comment to avoid empty block lint error\n }\n this.completeRequest({ context, success: `${res.status}: ${responseBody}` });\n } else {\n this.handleReponse(res.status, context);\n }\n } catch (e) {\n this.completeRequest({ context, err: e as string, removeEvents: false });\n }\n }\n\n handleReponse(status: number, context: SessionReplayContext) {\n const parsedStatus = new BaseTransport().buildStatus(status);\n switch (parsedStatus) {\n case Status.Success:\n this.handleSuccessResponse(context);\n break;\n default:\n this.handleOtherResponse(context);\n }\n }\n\n handleSuccessResponse(context: SessionReplayContext) {\n this.completeRequest({ context, success: SUCCESS_MESSAGE });\n }\n\n handleOtherResponse(context: SessionReplayContext) {\n this.addToQueue({\n ...context,\n timeout: context.attempts * this.retryTimeout,\n });\n }\n\n async getAllSessionEventsFromStore() {\n try {\n const storedReplaySessionContexts: IDBStore | undefined = await IDBKeyVal.get(this.storageKey);\n\n return storedReplaySessionContexts;\n } catch (e) {\n this.config.loggerProvider.error(`${STORAGE_FAILURE}: ${e as string}`);\n }\n return undefined;\n }\n\n async storeEventsForSession(events: Events, sequenceId: number) {\n try {\n await IDBKeyVal.update(this.storageKey, (sessionMap: IDBStore | undefined): IDBStore => {\n return {\n ...sessionMap,\n ...(this.config.sessionId && {\n [this.config.sessionId]: {\n events: events,\n sequenceId,\n },\n }),\n };\n });\n } catch (e) {\n this.config.loggerProvider.error(`${STORAGE_FAILURE}: ${e as string}`);\n }\n }\n\n async removeSessionEventsStore(sessionId: number) {\n try {\n await IDBKeyVal.update(this.storageKey, (sessionMap: IDBStore = {}): IDBStore => {\n delete sessionMap[sessionId];\n return sessionMap;\n });\n } catch (e) {\n this.config.loggerProvider.error(`${STORAGE_FAILURE}: ${e as string}`);\n }\n }\n\n completeRequest({\n context,\n err,\n success,\n removeEvents = true,\n }: {\n context: SessionReplayContext;\n err?: string;\n success?: string;\n removeEvents?: boolean;\n }) {\n removeEvents && context.sessionId && this.removeSessionEventsStore(context.sessionId);\n if (err) {\n this.config.loggerProvider.error(err);\n } else if (success) {\n this.config.loggerProvider.log(success);\n }\n }\n}\n\nexport const sessionReplayPlugin: SessionReplayPlugin = () => {\n return new SessionReplay();\n};\n"]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { BrowserClient, BrowserConfig, EnrichmentPlugin } from '@amplitude/analytics-types';
|
|
2
|
+
import { record } from 'rrweb';
|
|
3
|
+
export interface Options {
|
|
4
|
+
}
|
|
5
|
+
export type Events = string[];
|
|
6
|
+
export interface SessionReplayContext {
|
|
7
|
+
events: Events;
|
|
8
|
+
sequenceId: number;
|
|
9
|
+
attempts: number;
|
|
10
|
+
timeout: number;
|
|
11
|
+
sessionId: number;
|
|
12
|
+
}
|
|
13
|
+
export interface IDBStore {
|
|
14
|
+
[sessionId: number]: {
|
|
15
|
+
events: Events;
|
|
16
|
+
sequenceId: number;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export interface SessionReplayEnrichmentPlugin extends EnrichmentPlugin {
|
|
20
|
+
config: BrowserConfig;
|
|
21
|
+
storageKey: string;
|
|
22
|
+
retryTimeout: number;
|
|
23
|
+
events: Events;
|
|
24
|
+
currentSequenceId: number;
|
|
25
|
+
queue: SessionReplayContext[];
|
|
26
|
+
stopRecordingEvents: ReturnType<typeof record> | null;
|
|
27
|
+
maxPersistedEventsSize: number;
|
|
28
|
+
emptyStoreAndReset: () => Promise<void>;
|
|
29
|
+
recordEvents: () => void;
|
|
30
|
+
sendEventsList: ({ events, sequenceId, sessionId, }: {
|
|
31
|
+
events: string[];
|
|
32
|
+
sequenceId: number;
|
|
33
|
+
sessionId: number;
|
|
34
|
+
}) => void;
|
|
35
|
+
addToQueue: (...list: SessionReplayContext[]) => void;
|
|
36
|
+
schedule: (timeout: number) => void;
|
|
37
|
+
flush: (useRetry?: boolean) => Promise<void>;
|
|
38
|
+
send: (context: SessionReplayContext, useRetry?: boolean) => Promise<void>;
|
|
39
|
+
completeRequest({ context, err, success, removeEvents, }: {
|
|
40
|
+
context: SessionReplayContext;
|
|
41
|
+
err?: string | undefined;
|
|
42
|
+
success?: string | undefined;
|
|
43
|
+
removeEvents?: boolean | undefined;
|
|
44
|
+
}): void;
|
|
45
|
+
getAllSessionEventsFromStore: () => Promise<IDBStore | undefined>;
|
|
46
|
+
storeEventsForSession: (events: Events, sequenceId: number) => Promise<void>;
|
|
47
|
+
removeSessionEventsStore: (sessionId: number) => Promise<void>;
|
|
48
|
+
}
|
|
49
|
+
export interface SessionReplayPlugin {
|
|
50
|
+
(client: BrowserClient, options?: Options): SessionReplayEnrichmentPlugin;
|
|
51
|
+
(options?: Options): SessionReplayEnrichmentPlugin;
|
|
52
|
+
}
|
|
53
|
+
export type SessionReplayPluginParameters = [BrowserClient, Options?] | [Options?];
|
|
54
|
+
//# sourceMappingURL=session-replay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-replay.d.ts","sourceRoot":"","sources":["../../../src/typings/session-replay.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC5F,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAG/B,MAAM,WAAW,OAAO;CAAG;AAE3B,MAAM,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;AAE9B,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,CAAC,SAAS,EAAE,MAAM,GAAG;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AACD,MAAM,WAAW,6BAA8B,SAAQ,gBAAgB;IACrE,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,MAAM,CAAC;IAC1B,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,mBAAmB,EAAE,UAAU,CAAC,OAAO,MAAM,CAAC,GAAG,IAAI,CAAC;IACtD,sBAAsB,EAAE,MAAM,CAAC;IAC/B,kBAAkB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,cAAc,EAAE,CAAC,EACf,MAAM,EACN,UAAU,EACV,SAAS,GACV,EAAE;QACD,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;KACnB,KAAK,IAAI,CAAC;IACX,UAAU,EAAE,CAAC,GAAG,IAAI,EAAE,oBAAoB,EAAE,KAAK,IAAI,CAAC;IACtD,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,KAAK,EAAE,CAAC,QAAQ,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,EAAE,CAAC,OAAO,EAAE,oBAAoB,EAAE,QAAQ,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3E,eAAe,CAAC,EACd,OAAO,EACP,GAAG,EACH,OAAO,EACP,YAAY,GACb,EAAE;QACD,OAAO,EAAE,oBAAoB,CAAC;QAC9B,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACzB,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC7B,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;KACpC,GAAG,IAAI,CAAC;IACT,4BAA4B,EAAE,MAAM,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;IAClE,qBAAqB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,wBAAwB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChE;AAED,MAAM,WAAW,mBAAmB;IAClC,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,6BAA6B,CAAC;IAC1E,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,6BAA6B,CAAC;CACpD;AAED,MAAM,MAAM,6BAA6B,GAAG,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-replay.js","sourceRoot":"","sources":["../../../src/typings/session-replay.ts"],"names":[],"mappings":"","sourcesContent":["import { BrowserClient, BrowserConfig, EnrichmentPlugin } from '@amplitude/analytics-types';\nimport { record } from 'rrweb';\n\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface Options {}\n\nexport type Events = string[];\n\nexport interface SessionReplayContext {\n events: Events;\n sequenceId: number;\n attempts: number;\n timeout: number;\n sessionId: number;\n}\n\nexport interface IDBStore {\n [sessionId: number]: {\n events: Events;\n sequenceId: number;\n };\n}\nexport interface SessionReplayEnrichmentPlugin extends EnrichmentPlugin {\n config: BrowserConfig;\n storageKey: string;\n retryTimeout: number;\n events: Events;\n currentSequenceId: number;\n queue: SessionReplayContext[];\n stopRecordingEvents: ReturnType<typeof record> | null;\n maxPersistedEventsSize: number;\n emptyStoreAndReset: () => Promise<void>;\n recordEvents: () => void;\n sendEventsList: ({\n events,\n sequenceId,\n sessionId,\n }: {\n events: string[];\n sequenceId: number;\n sessionId: number;\n }) => void;\n addToQueue: (...list: SessionReplayContext[]) => void;\n schedule: (timeout: number) => void;\n flush: (useRetry?: boolean) => Promise<void>;\n send: (context: SessionReplayContext, useRetry?: boolean) => Promise<void>;\n completeRequest({\n context,\n err,\n success,\n removeEvents,\n }: {\n context: SessionReplayContext;\n err?: string | undefined;\n success?: string | undefined;\n removeEvents?: boolean | undefined;\n }): void;\n getAllSessionEventsFromStore: () => Promise<IDBStore | undefined>;\n storeEventsForSession: (events: Events, sequenceId: number) => Promise<void>;\n removeSessionEventsStore: (sessionId: number) => Promise<void>;\n}\n\nexport interface SessionReplayPlugin {\n (client: BrowserClient, options?: Options): SessionReplayEnrichmentPlugin;\n (options?: Options): SessionReplayEnrichmentPlugin;\n}\n\nexport type SessionReplayPluginParameters = [BrowserClient, Options?] | [Options?];\n"]}
|