@amplitude/plugin-session-replay-browser 0.1.2 → 0.2.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.
@@ -1,15 +1,17 @@
1
- import { __assign, __awaiter, __generator } from "tslib";
1
+ import { __assign, __awaiter, __generator, __read } from "tslib";
2
+ import { getGlobalScope } from '@amplitude/analytics-client-common';
2
3
  import { AMPLITUDE_PREFIX, BaseTransport } from '@amplitude/analytics-core';
3
4
  import { Status } from '@amplitude/analytics-types';
4
5
  import * as IDBKeyVal from 'idb-keyval';
5
6
  import { pack, record } from 'rrweb';
6
7
  import { DEFAULT_SESSION_END_EVENT, DEFAULT_SESSION_REPLAY_PROPERTY, DEFAULT_SESSION_START_EVENT } from './constants';
7
8
  import { MAX_RETRIES_EXCEEDED_MESSAGE, STORAGE_FAILURE, SUCCESS_MESSAGE, UNEXPECTED_ERROR_MESSAGE } from './messages';
9
+ import { RecordingStatus, } from './typings/session-replay';
8
10
  var SESSION_REPLAY_SERVER_URL = 'https://api-secure.amplitude.com/sessions/track';
9
11
  var STORAGE_PREFIX = "".concat(AMPLITUDE_PREFIX, "_replay_unsent");
10
12
  var PAYLOAD_ESTIMATED_SIZE_IN_BYTES_WITHOUT_EVENTS = 200; // derived by JSON stringifying an example payload without events
11
13
  var MAX_EVENT_LIST_SIZE_IN_BYTES = 20 * 1000000 - PAYLOAD_ESTIMATED_SIZE_IN_BYTES_WITHOUT_EVENTS;
12
- var MIN_INTERVAL = 1 * 1000; // 1 second
14
+ var MIN_INTERVAL = 500; // 500 ms
13
15
  var MAX_INTERVAL = 10 * 1000; // 10 seconds
14
16
  var SessionReplay = /** @class */ (function () {
15
17
  function SessionReplay() {
@@ -38,8 +40,9 @@ var SessionReplay = /** @class */ (function () {
38
40
  if (sizeOfEventsList + sizeOfNextEvent >= _this.maxPersistedEventsSize) {
39
41
  return true;
40
42
  }
41
- if (_this.timeAtLastSend !== null && Date.now() - _this.timeAtLastSend > _this.interval) {
43
+ if (_this.timeAtLastSend !== null && Date.now() - _this.timeAtLastSend > _this.interval && _this.events.length) {
42
44
  _this.interval = Math.min(MAX_INTERVAL, _this.interval + MIN_INTERVAL);
45
+ _this.timeAtLastSend = Date.now();
43
46
  return true;
44
47
  }
45
48
  return false;
@@ -47,12 +50,29 @@ var SessionReplay = /** @class */ (function () {
47
50
  }
48
51
  SessionReplay.prototype.setup = function (config) {
49
52
  return __awaiter(this, void 0, void 0, function () {
53
+ var GlobalScope;
54
+ var _this = this;
50
55
  return __generator(this, function (_a) {
51
- config.loggerProvider.log('Installing @amplitude/plugin-session-replay.');
52
- this.config = config;
53
- this.storageKey = "".concat(STORAGE_PREFIX, "_").concat(this.config.apiKey.substring(0, 10));
54
- void this.emptyStoreAndReset();
55
- return [2 /*return*/];
56
+ switch (_a.label) {
57
+ case 0:
58
+ config.loggerProvider.log('Installing @amplitude/plugin-session-replay.');
59
+ this.config = config;
60
+ this.storageKey = "".concat(STORAGE_PREFIX, "_").concat(this.config.apiKey.substring(0, 10));
61
+ return [4 /*yield*/, this.initialize(true)];
62
+ case 1:
63
+ _a.sent();
64
+ GlobalScope = getGlobalScope();
65
+ if (GlobalScope && GlobalScope.window) {
66
+ GlobalScope.window.addEventListener('blur', function () {
67
+ _this.stopRecordingEvents && _this.stopRecordingEvents();
68
+ _this.stopRecordingEvents = null;
69
+ });
70
+ GlobalScope.window.addEventListener('focus', function () {
71
+ void _this.initialize();
72
+ });
73
+ }
74
+ return [2 /*return*/];
75
+ }
56
76
  });
57
77
  });
58
78
  };
@@ -61,7 +81,7 @@ var SessionReplay = /** @class */ (function () {
61
81
  var _a;
62
82
  return __generator(this, function (_b) {
63
83
  event.event_properties = __assign(__assign({}, event.event_properties), (_a = {}, _a[DEFAULT_SESSION_REPLAY_PROPERTY] = true, _a));
64
- if (event.event_type === DEFAULT_SESSION_START_EVENT) {
84
+ if (event.event_type === DEFAULT_SESSION_START_EVENT && !this.stopRecordingEvents) {
65
85
  this.recordEvents();
66
86
  }
67
87
  else if (event.event_type === DEFAULT_SESSION_END_EVENT) {
@@ -73,6 +93,7 @@ var SessionReplay = /** @class */ (function () {
73
93
  });
74
94
  }
75
95
  this.stopRecordingEvents && this.stopRecordingEvents();
96
+ this.stopRecordingEvents = null;
76
97
  this.events = [];
77
98
  this.currentSequenceId = 0;
78
99
  }
@@ -80,29 +101,38 @@ var SessionReplay = /** @class */ (function () {
80
101
  });
81
102
  });
82
103
  };
83
- SessionReplay.prototype.emptyStoreAndReset = function () {
104
+ SessionReplay.prototype.initialize = function (shouldSendStoredEvents) {
105
+ if (shouldSendStoredEvents === void 0) { shouldSendStoredEvents = false; }
84
106
  return __awaiter(this, void 0, void 0, function () {
85
- var storedReplaySessions, sessionId, storedReplayEvents, currentSessionStoredEvents;
107
+ var storedReplaySessions, storedSequencesForSession, storedSeqId, lastSequence;
86
108
  return __generator(this, function (_a) {
87
109
  switch (_a.label) {
88
- case 0: return [4 /*yield*/, this.getAllSessionEventsFromStore()];
110
+ case 0:
111
+ this.timeAtLastSend = Date.now(); // Initialize this so we have a point of comparison when events are recorded
112
+ if (!this.config.sessionId) {
113
+ return [2 /*return*/];
114
+ }
115
+ return [4 /*yield*/, this.getAllSessionEventsFromStore()];
89
116
  case 1:
90
117
  storedReplaySessions = _a.sent();
91
- if (storedReplaySessions) {
92
- for (sessionId in storedReplaySessions) {
93
- storedReplayEvents = storedReplaySessions[sessionId];
94
- if (storedReplayEvents.events.length) {
95
- this.sendEventsList({
96
- events: storedReplayEvents.events,
97
- sequenceId: storedReplayEvents.sequenceId,
98
- sessionId: parseInt(sessionId, 10),
99
- });
100
- }
118
+ storedSequencesForSession = storedReplaySessions && storedReplaySessions[this.config.sessionId];
119
+ if (storedReplaySessions && storedSequencesForSession && storedSequencesForSession.sessionSequences) {
120
+ storedSeqId = storedSequencesForSession.currentSequenceId;
121
+ lastSequence = storedSequencesForSession.sessionSequences[storedSeqId];
122
+ if (lastSequence.status !== RecordingStatus.RECORDING) {
123
+ this.currentSequenceId = storedSeqId + 1;
124
+ this.events = [];
101
125
  }
102
- this.events = [];
103
- currentSessionStoredEvents = this.config.sessionId && storedReplaySessions[this.config.sessionId];
104
- this.currentSequenceId = currentSessionStoredEvents ? currentSessionStoredEvents.sequenceId + 1 : 0;
105
- void this.storeEventsForSession([], this.currentSequenceId);
126
+ else {
127
+ // Pick up recording where it was left off in another tab or window
128
+ this.currentSequenceId = storedSeqId;
129
+ this.events = lastSequence.events;
130
+ }
131
+ }
132
+ if (shouldSendStoredEvents && storedReplaySessions) {
133
+ this.sendStoredEvents(storedReplaySessions);
134
+ }
135
+ if (!this.stopRecordingEvents) {
106
136
  this.recordEvents();
107
137
  }
108
138
  return [2 /*return*/];
@@ -110,22 +140,31 @@ var SessionReplay = /** @class */ (function () {
110
140
  });
111
141
  });
112
142
  };
143
+ SessionReplay.prototype.sendStoredEvents = function (storedReplaySessions) {
144
+ for (var sessionId in storedReplaySessions) {
145
+ var storedSequences = storedReplaySessions[sessionId].sessionSequences;
146
+ for (var storedSeqId in storedSequences) {
147
+ var seq = storedSequences[storedSeqId];
148
+ var numericSeqId = parseInt(storedSeqId, 10);
149
+ var numericSessionId = parseInt(sessionId, 10);
150
+ if (numericSessionId === this.config.sessionId && numericSeqId === this.currentSequenceId) {
151
+ continue;
152
+ }
153
+ if (seq.events.length && seq.status === RecordingStatus.RECORDING) {
154
+ this.sendEventsList({
155
+ events: seq.events,
156
+ sequenceId: numericSeqId,
157
+ sessionId: numericSessionId,
158
+ });
159
+ }
160
+ }
161
+ }
162
+ };
113
163
  SessionReplay.prototype.recordEvents = function () {
114
164
  var _this = this;
115
165
  this.stopRecordingEvents = record({
116
166
  emit: function (event) {
117
167
  var eventString = JSON.stringify(event);
118
- // Send the first two recorded events immediately
119
- if (_this.events.length === 1 && _this.currentSequenceId === 0) {
120
- _this.sendEventsList({
121
- events: _this.events.concat(eventString),
122
- sequenceId: _this.currentSequenceId,
123
- sessionId: _this.config.sessionId,
124
- });
125
- _this.events = [];
126
- _this.currentSequenceId++;
127
- return;
128
- }
129
168
  var shouldSplit = _this.shouldSplitEventsList(eventString);
130
169
  if (shouldSplit) {
131
170
  _this.sendEventsList({
@@ -137,7 +176,7 @@ var SessionReplay = /** @class */ (function () {
137
176
  _this.currentSequenceId++;
138
177
  }
139
178
  _this.events.push(eventString);
140
- void _this.storeEventsForSession(_this.events, _this.currentSequenceId);
179
+ void _this.storeEventsForSession(_this.events, _this.currentSequenceId, _this.config.sessionId);
141
180
  },
142
181
  packFn: pack,
143
182
  maskAllInputs: true,
@@ -313,22 +352,25 @@ var SessionReplay = /** @class */ (function () {
313
352
  });
314
353
  });
315
354
  };
316
- SessionReplay.prototype.storeEventsForSession = function (events, sequenceId) {
355
+ SessionReplay.prototype.storeEventsForSession = function (events, sequenceId, sessionId) {
317
356
  return __awaiter(this, void 0, void 0, function () {
318
357
  var e_3;
319
- var _this = this;
320
358
  return __generator(this, function (_a) {
321
359
  switch (_a.label) {
322
360
  case 0:
323
361
  _a.trys.push([0, 2, , 3]);
324
362
  return [4 /*yield*/, IDBKeyVal.update(this.storageKey, function (sessionMap) {
325
- var _a;
326
- return __assign(__assign({}, sessionMap), (_this.config.sessionId && (_a = {},
327
- _a[_this.config.sessionId] = {
328
- events: events,
329
- sequenceId: sequenceId,
330
- },
331
- _a)));
363
+ var _a, _b;
364
+ if (sessionMap === void 0) { sessionMap = {}; }
365
+ var session = sessionMap[sessionId] || {
366
+ currentSequenceId: 0,
367
+ sessionSequences: [],
368
+ };
369
+ session.currentSequenceId = sequenceId;
370
+ var currentSequence = (session.sessionSequences && session.sessionSequences[sequenceId]) || {};
371
+ currentSequence.events = events;
372
+ currentSequence.status = RecordingStatus.RECORDING;
373
+ return __assign(__assign({}, sessionMap), (_a = {}, _a[sessionId] = __assign(__assign({}, session), { sessionSequences: __assign(__assign({}, session.sessionSequences), (_b = {}, _b[sequenceId] = currentSequence, _b)) }), _a));
332
374
  })];
333
375
  case 1:
334
376
  _a.sent();
@@ -342,7 +384,7 @@ var SessionReplay = /** @class */ (function () {
342
384
  });
343
385
  });
344
386
  };
345
- SessionReplay.prototype.removeSessionEventsStore = function (sessionId) {
387
+ SessionReplay.prototype.cleanUpSessionEventsStore = function (sessionId, sequenceId) {
346
388
  return __awaiter(this, void 0, void 0, function () {
347
389
  var e_4;
348
390
  return __generator(this, function (_a) {
@@ -351,7 +393,20 @@ var SessionReplay = /** @class */ (function () {
351
393
  _a.trys.push([0, 2, , 3]);
352
394
  return [4 /*yield*/, IDBKeyVal.update(this.storageKey, function (sessionMap) {
353
395
  if (sessionMap === void 0) { sessionMap = {}; }
354
- delete sessionMap[sessionId];
396
+ var session = sessionMap[sessionId];
397
+ var sequenceToUpdate = (session === null || session === void 0 ? void 0 : session.sessionSequences) && session.sessionSequences[sequenceId];
398
+ if (!sequenceToUpdate) {
399
+ return sessionMap;
400
+ }
401
+ sequenceToUpdate.events = [];
402
+ sequenceToUpdate.status = RecordingStatus.SENT;
403
+ Object.entries(session.sessionSequences).forEach(function (_a) {
404
+ var _b = __read(_a, 2), storedSeqId = _b[0], sequence = _b[1];
405
+ var numericStoredSeqId = parseInt(storedSeqId, 10);
406
+ if (sequence.status === RecordingStatus.SENT && sequenceId !== numericStoredSeqId) {
407
+ delete session.sessionSequences[numericStoredSeqId];
408
+ }
409
+ });
355
410
  return sessionMap;
356
411
  })];
357
412
  case 1:
@@ -368,12 +423,11 @@ var SessionReplay = /** @class */ (function () {
368
423
  };
369
424
  SessionReplay.prototype.completeRequest = function (_a) {
370
425
  var context = _a.context, err = _a.err, success = _a.success, _b = _a.removeEvents, removeEvents = _b === void 0 ? true : _b;
371
- removeEvents && context.sessionId && this.removeSessionEventsStore(context.sessionId);
426
+ removeEvents && context.sessionId && this.cleanUpSessionEventsStore(context.sessionId, context.sequenceId);
372
427
  if (err) {
373
428
  this.config.loggerProvider.error(err);
374
429
  }
375
430
  else if (success) {
376
- this.timeAtLastSend = Date.now();
377
431
  this.config.loggerProvider.log(success);
378
432
  }
379
433
  };
@@ -1 +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, 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 { 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;\nconst MIN_INTERVAL = 1 * 1000; // 1 second\nconst MAX_INTERVAL = 10 * 1000; // 10 seconds\n\nclass SessionReplay implements SessionReplayEnrichmentPlugin {\n name = '@amplitude/plugin-session-replay-browser';\n type = '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 interval = MIN_INTERVAL;\n timeAtLastSend: number | null = null;\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) {\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.stopRecordingEvents && this.stopRecordingEvents();\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 // Send the first two recorded events immediately\n if (this.events.length === 1 && this.currentSequenceId === 0) {\n this.sendEventsList({\n events: this.events.concat(eventString),\n sequenceId: this.currentSequenceId,\n sessionId: this.config.sessionId as number,\n });\n this.events = [];\n this.currentSequenceId++;\n return;\n }\n\n const shouldSplit = this.shouldSplitEventsList(eventString);\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 maskAllInputs: true,\n });\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 = (nextEventString: string): boolean => {\n const sizeOfNextEvent = new Blob([nextEventString]).size;\n const sizeOfEventsList = new Blob(this.events).size;\n if (sizeOfEventsList + sizeOfNextEvent >= this.maxPersistedEventsSize) {\n return true;\n }\n if (this.timeAtLastSend !== null && Date.now() - this.timeAtLastSend > this.interval) {\n this.interval = Math.min(MAX_INTERVAL, this.interval + MIN_INTERVAL);\n return true;\n }\n return false;\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(0);\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.timeAtLastSend = Date.now();\n this.config.loggerProvider.log(success);\n }\n }\n}\n\nexport const sessionReplayPlugin: SessionReplayPlugin = () => {\n return new SessionReplay();\n};\n"]}
1
+ {"version":3,"file":"session-replay.js","sourceRoot":"","sources":["../../src/session-replay.ts"],"names":[],"mappings":"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","sourcesContent":["import { getGlobalScope } from '@amplitude/analytics-client-common';\nimport { AMPLITUDE_PREFIX, BaseTransport } from '@amplitude/analytics-core';\nimport { BrowserConfig, Event, 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 { MAX_RETRIES_EXCEEDED_MESSAGE, STORAGE_FAILURE, SUCCESS_MESSAGE, UNEXPECTED_ERROR_MESSAGE } from './messages';\nimport {\n Events,\n IDBStore,\n RecordingStatus,\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;\nconst MIN_INTERVAL = 500; // 500 ms\nconst MAX_INTERVAL = 10 * 1000; // 10 seconds\n\nclass SessionReplay implements SessionReplayEnrichmentPlugin {\n name = '@amplitude/plugin-session-replay-browser';\n type = '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 interval = MIN_INTERVAL;\n timeAtLastSend: number | null = null;\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 await this.initialize(true);\n\n const GlobalScope = getGlobalScope();\n if (GlobalScope && GlobalScope.window) {\n GlobalScope.window.addEventListener('blur', () => {\n this.stopRecordingEvents && this.stopRecordingEvents();\n this.stopRecordingEvents = null;\n });\n GlobalScope.window.addEventListener('focus', () => {\n void this.initialize();\n });\n }\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.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.stopRecordingEvents && this.stopRecordingEvents();\n this.stopRecordingEvents = null;\n this.events = [];\n this.currentSequenceId = 0;\n }\n return Promise.resolve(event);\n }\n\n async initialize(shouldSendStoredEvents = false) {\n this.timeAtLastSend = Date.now(); // Initialize this so we have a point of comparison when events are recorded\n if (!this.config.sessionId) {\n return;\n }\n const storedReplaySessions = await this.getAllSessionEventsFromStore();\n const storedSequencesForSession = storedReplaySessions && storedReplaySessions[this.config.sessionId];\n if (storedReplaySessions && storedSequencesForSession && storedSequencesForSession.sessionSequences) {\n const storedSeqId = storedSequencesForSession.currentSequenceId;\n const lastSequence = storedSequencesForSession.sessionSequences[storedSeqId];\n if (lastSequence.status !== RecordingStatus.RECORDING) {\n this.currentSequenceId = storedSeqId + 1;\n this.events = [];\n } else {\n // Pick up recording where it was left off in another tab or window\n this.currentSequenceId = storedSeqId;\n this.events = lastSequence.events;\n }\n }\n if (shouldSendStoredEvents && storedReplaySessions) {\n this.sendStoredEvents(storedReplaySessions);\n }\n if (!this.stopRecordingEvents) {\n this.recordEvents();\n }\n }\n\n sendStoredEvents(storedReplaySessions: IDBStore) {\n for (const sessionId in storedReplaySessions) {\n const storedSequences = storedReplaySessions[sessionId].sessionSequences;\n for (const storedSeqId in storedSequences) {\n const seq = storedSequences[storedSeqId];\n const numericSeqId = parseInt(storedSeqId, 10);\n const numericSessionId = parseInt(sessionId, 10);\n if (numericSessionId === this.config.sessionId && numericSeqId === this.currentSequenceId) {\n continue;\n }\n if (seq.events.length && seq.status === RecordingStatus.RECORDING) {\n this.sendEventsList({\n events: seq.events,\n sequenceId: numericSeqId,\n sessionId: numericSessionId,\n });\n }\n }\n }\n }\n\n recordEvents() {\n this.stopRecordingEvents = record({\n emit: (event) => {\n const eventString = JSON.stringify(event);\n\n const shouldSplit = this.shouldSplitEventsList(eventString);\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, this.config.sessionId as number);\n },\n packFn: pack,\n maskAllInputs: true,\n });\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 = (nextEventString: string): boolean => {\n const sizeOfNextEvent = new Blob([nextEventString]).size;\n const sizeOfEventsList = new Blob(this.events).size;\n if (sizeOfEventsList + sizeOfNextEvent >= this.maxPersistedEventsSize) {\n return true;\n }\n if (this.timeAtLastSend !== null && Date.now() - this.timeAtLastSend > this.interval && this.events.length) {\n this.interval = Math.min(MAX_INTERVAL, this.interval + MIN_INTERVAL);\n this.timeAtLastSend = Date.now();\n return true;\n }\n return false;\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(0);\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, sessionId: number) {\n try {\n await IDBKeyVal.update(this.storageKey, (sessionMap: IDBStore = {}): IDBStore => {\n const session = sessionMap[sessionId] || {\n currentSequenceId: 0,\n sessionSequences: [],\n };\n session.currentSequenceId = sequenceId;\n\n const currentSequence = (session.sessionSequences && session.sessionSequences[sequenceId]) || {};\n\n currentSequence.events = events;\n currentSequence.status = RecordingStatus.RECORDING;\n\n return {\n ...sessionMap,\n [sessionId]: {\n ...session,\n sessionSequences: {\n ...session.sessionSequences,\n [sequenceId]: currentSequence,\n },\n },\n };\n });\n } catch (e) {\n this.config.loggerProvider.error(`${STORAGE_FAILURE}: ${e as string}`);\n }\n }\n\n async cleanUpSessionEventsStore(sessionId: number, sequenceId: number) {\n try {\n await IDBKeyVal.update(this.storageKey, (sessionMap: IDBStore = {}): IDBStore => {\n const session = sessionMap[sessionId];\n const sequenceToUpdate = session?.sessionSequences && session.sessionSequences[sequenceId];\n if (!sequenceToUpdate) {\n return sessionMap;\n }\n\n sequenceToUpdate.events = [];\n sequenceToUpdate.status = RecordingStatus.SENT;\n\n Object.entries(session.sessionSequences).forEach(([storedSeqId, sequence]) => {\n const numericStoredSeqId = parseInt(storedSeqId, 10);\n if (sequence.status === RecordingStatus.SENT && sequenceId !== numericStoredSeqId) {\n delete session.sessionSequences[numericStoredSeqId];\n }\n });\n\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.cleanUpSessionEventsStore(context.sessionId, context.sequenceId);\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"]}
@@ -10,10 +10,21 @@ export interface SessionReplayContext {
10
10
  timeout: number;
11
11
  sessionId: number;
12
12
  }
13
+ export declare enum RecordingStatus {
14
+ RECORDING = "recording",
15
+ SENDING = "sending",
16
+ SENT = "sent"
17
+ }
18
+ export interface IDBStoreSequence {
19
+ events: Events;
20
+ status: RecordingStatus;
21
+ }
13
22
  export interface IDBStore {
14
23
  [sessionId: number]: {
15
- events: Events;
16
- sequenceId: number;
24
+ currentSequenceId: number;
25
+ sessionSequences: {
26
+ [sequenceId: number]: IDBStoreSequence;
27
+ };
17
28
  };
18
29
  }
19
30
  export interface SessionReplayEnrichmentPlugin extends EnrichmentPlugin {
@@ -27,7 +38,7 @@ export interface SessionReplayEnrichmentPlugin extends EnrichmentPlugin {
27
38
  timeAtLastSend: number | null;
28
39
  stopRecordingEvents: ReturnType<typeof record> | null;
29
40
  maxPersistedEventsSize: number;
30
- emptyStoreAndReset: () => Promise<void>;
41
+ initialize: (shouldSendStoredEvents?: boolean) => Promise<void>;
31
42
  recordEvents: () => void;
32
43
  shouldSplitEventsList: (nextEventString: string) => boolean;
33
44
  sendEventsList: ({ events, sequenceId, sessionId, }: {
@@ -46,8 +57,8 @@ export interface SessionReplayEnrichmentPlugin extends EnrichmentPlugin {
46
57
  removeEvents?: boolean | undefined;
47
58
  }): void;
48
59
  getAllSessionEventsFromStore: () => Promise<IDBStore | undefined>;
49
- storeEventsForSession: (events: Events, sequenceId: number) => Promise<void>;
50
- removeSessionEventsStore: (sessionId: number) => Promise<void>;
60
+ storeEventsForSession: (events: Events, sequenceId: number, sessionId: number) => Promise<void>;
61
+ cleanUpSessionEventsStore: (sessionId: number, sequenceId: number) => Promise<void>;
51
62
  }
52
63
  export interface SessionReplayPlugin {
53
64
  (client: BrowserClient, options?: Options): SessionReplayEnrichmentPlugin;
@@ -1 +1 @@
1
- {"version":3,"file":"session-replay.d.ts","sourceRoot":"","sources":["../../../src/typings/session-replay.ts"],"names":[],"mappings":"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"}
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,oBAAY,eAAe;IACzB,SAAS,cAAc;IACvB,OAAO,YAAY;IACnB,IAAI,SAAS;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,eAAe,CAAC;CACzB;AAED,MAAM,WAAW,QAAQ;IACvB,CAAC,SAAS,EAAE,MAAM,GAAG;QACnB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,gBAAgB,EAAE;YAAE,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB,CAAA;SAAE,CAAC;KAC9D,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,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,mBAAmB,EAAE,UAAU,CAAC,OAAO,MAAM,CAAC,GAAG,IAAI,CAAC;IACtD,sBAAsB,EAAE,MAAM,CAAC;IAC/B,UAAU,EAAE,CAAC,sBAAsB,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,qBAAqB,EAAE,CAAC,eAAe,EAAE,MAAM,KAAK,OAAO,CAAC;IAC5D,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,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChG,yBAAyB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrF;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"}
@@ -1,2 +1,7 @@
1
- export {};
1
+ export var RecordingStatus;
2
+ (function (RecordingStatus) {
3
+ RecordingStatus["RECORDING"] = "recording";
4
+ RecordingStatus["SENDING"] = "sending";
5
+ RecordingStatus["SENT"] = "sent";
6
+ })(RecordingStatus || (RecordingStatus = {}));
2
7
  //# sourceMappingURL=session-replay.js.map
@@ -1 +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 interval: number;\n queue: SessionReplayContext[];\n timeAtLastSend: number | null;\n stopRecordingEvents: ReturnType<typeof record> | null;\n maxPersistedEventsSize: number;\n emptyStoreAndReset: () => Promise<void>;\n recordEvents: () => void;\n shouldSplitEventsList: (nextEventString: string) => boolean;\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"]}
1
+ {"version":3,"file":"session-replay.js","sourceRoot":"","sources":["../../../src/typings/session-replay.ts"],"names":[],"mappings":"AAgBA,MAAM,CAAN,IAAY,eAIX;AAJD,WAAY,eAAe;IACzB,0CAAuB,CAAA;IACvB,sCAAmB,CAAA;IACnB,gCAAa,CAAA;AACf,CAAC,EAJW,eAAe,KAAf,eAAe,QAI1B","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 enum RecordingStatus {\n RECORDING = 'recording',\n SENDING = 'sending',\n SENT = 'sent',\n}\n\nexport interface IDBStoreSequence {\n events: Events;\n status: RecordingStatus;\n}\n\nexport interface IDBStore {\n [sessionId: number]: {\n currentSequenceId: number;\n sessionSequences: { [sequenceId: number]: IDBStoreSequence };\n };\n}\nexport interface SessionReplayEnrichmentPlugin extends EnrichmentPlugin {\n config: BrowserConfig;\n storageKey: string;\n retryTimeout: number;\n events: Events;\n currentSequenceId: number;\n interval: number;\n queue: SessionReplayContext[];\n timeAtLastSend: number | null;\n stopRecordingEvents: ReturnType<typeof record> | null;\n maxPersistedEventsSize: number;\n initialize: (shouldSendStoredEvents?: boolean) => Promise<void>;\n recordEvents: () => void;\n shouldSplitEventsList: (nextEventString: string) => boolean;\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, sessionId: number) => Promise<void>;\n cleanUpSessionEventsStore: (sessionId: number, sequenceId: 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"]}