@amplitude/session-replay-browser 1.50.4 → 1.51.1
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/README.md +15 -0
- package/lib/cjs/config/joined-config.d.ts.map +1 -1
- package/lib/cjs/config/joined-config.js +6 -25
- package/lib/cjs/config/joined-config.js.map +1 -1
- package/lib/cjs/config/types.d.ts +2 -25
- package/lib/cjs/config/types.d.ts.map +1 -1
- package/lib/cjs/config/types.js +0 -2
- package/lib/cjs/config/types.js.map +1 -1
- 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/helpers.d.ts +1 -16
- package/lib/cjs/helpers.d.ts.map +1 -1
- package/lib/cjs/helpers.js +8 -166
- package/lib/cjs/helpers.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/session-replay.d.ts.map +1 -1
- package/lib/cjs/session-replay.js +6 -35
- package/lib/cjs/session-replay.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/config/joined-config.d.ts.map +1 -1
- package/lib/esm/config/joined-config.js +6 -25
- package/lib/esm/config/joined-config.js.map +1 -1
- package/lib/esm/config/types.d.ts +2 -25
- package/lib/esm/config/types.d.ts.map +1 -1
- package/lib/esm/config/types.js +1 -1
- package/lib/esm/config/types.js.map +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/helpers.d.ts +1 -16
- package/lib/esm/helpers.d.ts.map +1 -1
- package/lib/esm/helpers.js +7 -161
- package/lib/esm/helpers.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/session-replay.d.ts.map +1 -1
- package/lib/esm/session-replay.js +8 -37
- package/lib/esm/session-replay.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 +3 -2
- package/lib/cjs/utils/get-input-type.d.ts +0 -9
- package/lib/cjs/utils/get-input-type.d.ts.map +0 -1
- package/lib/cjs/utils/get-input-type.js +0 -25
- package/lib/cjs/utils/get-input-type.js.map +0 -1
- package/lib/esm/utils/get-input-type.d.ts +0 -9
- package/lib/esm/utils/get-input-type.d.ts.map +0 -1
- package/lib/esm/utils/get-input-type.js +0 -20
- package/lib/esm/utils/get-input-type.js.map +0 -1
|
@@ -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":"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;;;;mBA2E7C;IAEF,yBAAyB,cAAqB,MAAM,SAAS,MAAM;;;;mBA0HjE;IAEF,kBAAkB,cAAqB,MAAM,iDAc3C;IAEF,yBAAyB,eAAsB,MAAM,eAAe,MAAM,mBAWxE;CACH"}
|
|
@@ -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);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events-idb-store.js","sourceRoot":"","sources":["../../../src/events/events-idb-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { DBSchema, IDBPDatabase, openDB } from 'idb';\nimport { STORAGE_FAILURE } from '../messages';\nimport { EventType, Events, SendingSequencesReturn } from '../typings/session-replay';\nimport { BaseEventsStore, InstanceArgs as BaseInstanceArgs } from './base-events-store';\nimport { logIdbError } from '../utils/is-abort-error';\n\n// crypto.randomUUID() requires a secure context (https). Fall back to a\n// Math.random-based UUID for http origins or older browsers — tab IDs don't\n// need to be cryptographically secure, just unique within a session.\nexport function generateUUID(): string {\n try {\n return crypto.randomUUID();\n } catch {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);\n });\n }\n}\n\nexport const currentSequenceKey = 'sessionCurrentSequence';\nexport const sequencesToSendKey = 'sequencesToSend';\nexport const remoteConfigKey = 'remoteConfig';\n\n// Timeout for openDB at init. IDB openDB can hang forever when another tab\n// holds an open connection during a version upgrade, or when the DB is in a\n// \"closing\" state (documented Chrome behaviour). When that happens we want\n// SessionReplayEventsIDBStore.new() to bail out so the caller can fall back\n// to the in-memory store.\nexport const OPEN_DB_TIMEOUT_MS = 2000;\n\n// Timeout for per-operation tx.done settlement. Mid-recording a readwrite\n// transaction can stall (storage pressure in some browsers stalls instead of\n// throwing); without a timeout, recordFailure() is never called and the\n// memory fallback never triggers. The transaction may still settle later;\n// the timedOut flag prevents double-counting alongside errorLogged.\nexport const TX_DONE_TIMEOUT_MS = 5000;\n\n/**\n * Race a promise against a timeout. Resolves/rejects with the original\n * promise's value when it settles first; rejects with a timeout error if\n * `ms` elapses first. Either way the timer is cleared so we don't leak\n * pending setTimeouts.\n */\nexport function withTimeout<T>(promise: Promise<T>, ms: number, message = 'IDB operation timed out'): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error(`${message} after ${ms}ms`)), ms);\n promise.then(\n (v) => {\n clearTimeout(timer);\n resolve(v);\n },\n (e) => {\n clearTimeout(timer);\n reject(e);\n },\n );\n });\n}\n\n/**\n * Arms a watchdog that fires `onTimeout` only if `tx.done` hasn't settled\n * within `ms`. A \"soft cancel\" returned to the caller suppresses the timeout\n * if the synchronous operation body completes without exception — important\n * because in test environments fake timers can fire ahead of tx.done's commit\n * microtask. Production behaviour is preserved: a genuinely stalled\n * transaction (no commit, no error, individual op promises never resolved)\n * cannot reach the soft-cancel call and the timeout fires as designed.\n *\n * Soft-cancel is only invoked once the awaits inside the operation's outer\n * try block have all returned — i.e. the IDB driver acknowledged each\n * individual put/get. If the driver accepts a put but the underlying\n * transaction silently fails to commit (the production stall scenario),\n * tx.done still hasn't settled, and recordFailure must NOT have been\n * suppressed. The fix for that scenario in production is the tx.done.catch\n * handler attached separately by callers, which catches the eventual abort.\n *\n * The soft-cancel pattern only suppresses the timeout for the \"all puts\n * resolved successfully\" case — a case where tx.done is overwhelmingly\n * likely to settle imminently in production. If it doesn't, callers can\n * still detect the failure on the NEXT operation (which will fail to open\n * a transaction or hit the same pressure), because all three methods use\n * readwrite transactions on the same two stores, which IDB serializes:\n * if T1's tx.done never settles, T2 is blocked waiting for T1 to commit or\n * abort — T2's put/get requests never resolve, T2 never reaches its\n * soft-cancel, and T2's watchdog fires, calling recordFailure().\n */\nfunction armTxDoneTimeout(txDone: Promise<unknown>, ms: number, onTimeout: () => void): () => void {\n const timer = setTimeout(onTimeout, ms);\n // Belt-and-braces: clear the timer when tx.done settles, even though the\n // primary cancel path is the caller's success-path cancel(). This covers\n // the case where tx.done settles with no caller cancellation (shouldn't\n // happen in current code paths, but cheap insurance).\n txDone.then(\n () => clearTimeout(timer),\n () => clearTimeout(timer),\n );\n return () => clearTimeout(timer);\n}\n\nexport interface SessionReplayDB extends DBSchema {\n sessionCurrentSequence: {\n key: number;\n value: Omit<SendingSequencesReturn<number>, 'sequenceId'> & { tabId?: string };\n };\n sequencesToSend: {\n key: number;\n value: Omit<SendingSequencesReturn<number>, 'sequenceId'> & { tabId?: string };\n indexes: { sessionId: string | number };\n };\n}\n\nexport const defineObjectStores = (db: IDBPDatabase<SessionReplayDB>) => {\n let sequencesStore;\n let currentSequenceStore;\n if (!db.objectStoreNames.contains(currentSequenceKey)) {\n currentSequenceStore = db.createObjectStore(currentSequenceKey, {\n keyPath: 'sessionId',\n });\n }\n if (!db.objectStoreNames.contains(sequencesToSendKey)) {\n sequencesStore = db.createObjectStore(sequencesToSendKey, {\n keyPath: 'sequenceId',\n autoIncrement: true,\n });\n sequencesStore.createIndex('sessionId', 'sessionId');\n }\n return {\n sequencesStore,\n currentSequenceStore,\n };\n};\n\nexport const createStore = async (dbName: string) => {\n // Wrap openDB with a timeout so a hung connection (foreign tab holding an\n // open handle during version upgrade, or \"closing\" DB) doesn't block the\n // SDK from initialising. On timeout this rejects, which propagates up to\n // SessionReplayEventsIDBStore.new()'s catch block, returning undefined and\n // triggering the memory fallback.\n return await withTimeout(\n openDB<SessionReplayDB>(dbName, 1, {\n upgrade: defineObjectStores,\n }),\n OPEN_DB_TIMEOUT_MS,\n 'IDB openDB timed out',\n );\n};\n\ntype InstanceArgs = {\n apiKey: string;\n db: IDBPDatabase<SessionReplayDB>;\n tabId: string;\n onPersistentFailure?: () => void;\n consecutiveFailureThreshold?: number;\n} & BaseInstanceArgs;\n\nexport class SessionReplayEventsIDBStore extends BaseEventsStore<number> {\n private readonly db: IDBPDatabase<SessionReplayDB>;\n private readonly tabId: string;\n private readonly onPersistentFailure?: () => void;\n private readonly consecutiveFailureThreshold: number;\n private consecutiveFailures = 0;\n private hasTriggeredFallback = false;\n private emptyFilteredCount = 0;\n\n // Sampled (1 in 100) debug log so we can observe whether the store-layer guards\n // are catching empty-batch cases that would otherwise hit the empty-body 400 path\n // on the server. Logged at debug, not warn — this is operational telemetry for\n // post-deploy verification, not a customer-actionable warning. Per-store-instance\n // counter (rather than Math.random) keeps the first hit deterministic for tests.\n private maybeLogEmptyFiltered(source: string) {\n if (this.emptyFilteredCount++ % 100 === 0) {\n this.loggerProvider.debug(`Filtered empty session replay sequence at ${source} (idb store)`);\n }\n }\n\n constructor(args: InstanceArgs) {\n super(args);\n this.db = args.db;\n this.tabId = args.tabId;\n this.onPersistentFailure = args.onPersistentFailure;\n // Default threshold of 1: fall back to memory immediately on the first IDB failure.\n // Session replay correctness is far more important than persistence, and IDB errors\n // are typically the symptom of a deeper problem (storage pressure, locked DB, broken\n // browser implementation) that won't recover within a single session. Memory store\n // is always safe — fall back early.\n this.consecutiveFailureThreshold = args.consecutiveFailureThreshold ?? 1;\n }\n\n private recordFailure() {\n this.consecutiveFailures++;\n if (!this.hasTriggeredFallback && this.consecutiveFailures >= this.consecutiveFailureThreshold) {\n this.hasTriggeredFallback = true;\n this.onPersistentFailure?.();\n }\n }\n\n private recordSuccess() {\n this.consecutiveFailures = 0;\n }\n\n static async new(\n type: EventType,\n args: Omit<InstanceArgs, 'db' | 'tabId'> & { tabId?: string },\n ): Promise<SessionReplayEventsIDBStore | undefined> {\n try {\n const dbSuffix = type === 'replay' ? '' : `_${type}`;\n const dbName = `${args.apiKey.substring(0, 10)}_amp_session_replay_events${dbSuffix}`;\n const db = await createStore(dbName);\n // Generate a fresh in-memory UUID per store instance. sessionStorage is\n // intentionally avoided: standalone session-replay customers (without the\n // analytics-browser SDK) would be exposed to a new storage surface they\n // did not consent to, and persistence across page reloads is not needed —\n // completed sequences in sequencesToSend are flushed by any tab/instance.\n const tabId = args.tabId ?? generateUUID();\n return new SessionReplayEventsIDBStore({\n ...args,\n db,\n tabId,\n });\n } catch (e) {\n logIdbError(args.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n }\n return;\n }\n\n async getCurrentSequenceEvents(sessionId?: number) {\n if (sessionId) {\n const record = await this.db.get('sessionCurrentSequence', sessionId);\n if (!record) {\n return undefined;\n }\n // Only return our own tab's record (or legacy untagged records).\n if (record.tabId && record.tabId !== this.tabId) {\n return undefined;\n }\n const { tabId: _tabId, ...rest } = record;\n return [rest];\n }\n\n const allEvents = [];\n for (const record of await this.db.getAll('sessionCurrentSequence')) {\n if (record.tabId && record.tabId !== this.tabId) {\n continue;\n }\n const { tabId: _tabId, ...rest } = record;\n allEvents.push(rest);\n }\n\n return allEvents;\n }\n\n getSequencesToSend = async (): Promise<SendingSequencesReturn<number>[] | undefined> => {\n let errorLogged = false;\n let timedOut = false;\n try {\n const sequences: SendingSequencesReturn<number>[] = [];\n // readwrite mode so we can prune stale empty rows in-place. Without that,\n // older-SDK-persisted events:[] rows sit in IDB indefinitely and re-fire the\n // sampled log on every flush cycle, creating Datadog noise indistinguishable\n // from active bug occurrences.\n const tx = this.db.transaction('sequencesToSend', 'readwrite');\n // Attach a catch handler immediately so tx.done rejections (e.g. AbortError after\n // cursor traversal completes) are always handled without blocking the return path.\n // The errorLogged / timedOut flags prevent double-logging and double-recording\n // when the outer catch (or the timeout race) already fired for the same abort.\n tx.done.catch((e: unknown) => {\n if (!errorLogged && !timedOut) {\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n });\n // Arm a watchdog so a stalled transaction (no error, no commit, e.g.\n // storage pressure on some browsers) still trips the failure counter.\n // The watchdog fires only when tx.done genuinely never settles AND the\n // operation's success path didn't run; if tx.done rejects (abort), the\n // tx.done.catch handler above is the sole recorder of failure.\n const cancelTimeout = armTxDoneTimeout(tx.done, TX_DONE_TIMEOUT_MS, () => {\n if (!errorLogged && !timedOut) {\n timedOut = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: transaction timed out`);\n this.recordFailure();\n }\n });\n let cursor = await tx.store.openCursor();\n while (cursor) {\n const { sessionId, events } = cursor.value;\n // Skip empty persisted records. These can come from older SDK builds that\n // wrote zero-event sequences via addEventToCurrentSequence's split path\n // (when a single oversized event triggered a split with empty buffer);\n // flushing them produces empty-body POSTs the server rejects with 400.\n // Delete them in-place so they don't re-fire the sampled log on every\n // subsequent flush — by construction (this fix) we never write empty rows\n // anymore, so any empty row is unambiguously stale.\n if (events.length === 0) {\n this.maybeLogEmptyFiltered('getSequencesToSend');\n await cursor.delete();\n } else {\n // Return all completed sequences regardless of tabId. Filtering by tab\n // would cause event loss on page reload: a new store instance gets a\n // fresh in-memory UUID and would never see sequences written by the\n // previous instance. Completed sequences are safe to flush by any\n // tab/instance; the server deduplicates, and cleanUpSessionEventsStore\n // on an already-deleted key is a no-op.\n sequences.push({\n events,\n sequenceId: cursor.key,\n sessionId,\n });\n }\n cursor = await cursor.continue();\n }\n\n this.recordSuccess();\n cancelTimeout();\n return sequences;\n } catch (e) {\n if (!timedOut) {\n errorLogged = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n }\n return undefined;\n };\n\n storeCurrentSequence = async (sessionId: number) => {\n let errorLogged = false;\n let timedOut = false;\n try {\n // Wrap the read of sessionCurrentSequence and the writes to sequencesToSend +\n // sessionCurrentSequence in a single readwrite transaction so the three operations\n // commit or roll back atomically. Without this, a concurrent addEventToCurrentSequence\n // call could interleave and either lose the events being promoted or duplicate them\n // (storeCurrentSequence reads N events, addEvent appends an N+1th, storeCurrentSequence\n // writes only the first N back to sequencesToSend, then resets the slot — losing N+1).\n const tx = this.db.transaction([currentSequenceKey, sequencesToSendKey], 'readwrite');\n tx.done.catch((e: unknown) => {\n if (!errorLogged && !timedOut) {\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n });\n // Stalled-transaction protection: see armTxDoneTimeout in getSequencesToSend.\n const cancelTimeout = armTxDoneTimeout(tx.done, TX_DONE_TIMEOUT_MS, () => {\n if (!errorLogged && !timedOut) {\n timedOut = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: transaction timed out`);\n this.recordFailure();\n }\n });\n\n const currentSequenceData = await tx.objectStore(currentSequenceKey).get(sessionId);\n // Skip promotion if the slot is empty or owned by another tab — let the owning\n // tab promote its own events on its next addEventToCurrentSequence/storeCurrentSequence\n // call (via Bug 1's foreign-tab promotion path).\n // Don't call recordSuccess() here: no write was performed, so this is not\n // evidence the storage layer is healthy — leave the failure counter unchanged.\n if (!currentSequenceData || (currentSequenceData.tabId && currentSequenceData.tabId !== this.tabId)) {\n cancelTimeout();\n return undefined;\n }\n\n // Skip empty sequences — no point writing a zero-event row to sequencesToSend\n // (would later POST as an empty body and 400 on the server).\n if (currentSequenceData.events.length === 0) {\n this.maybeLogEmptyFiltered('storeCurrentSequence');\n cancelTimeout();\n return undefined;\n }\n\n const sequenceId = await tx.objectStore(sequencesToSendKey).put({\n sessionId,\n events: currentSequenceData.events,\n tabId: this.tabId,\n });\n\n await tx.objectStore(currentSequenceKey).put({\n sessionId,\n events: [],\n tabId: this.tabId,\n });\n\n this.recordSuccess();\n cancelTimeout();\n const { tabId: _tabId, ...rest } = currentSequenceData;\n return {\n ...rest,\n sessionId,\n sequenceId,\n };\n } catch (e) {\n if (!timedOut) {\n errorLogged = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n }\n return undefined;\n };\n\n addEventToCurrentSequence = async (sessionId: number, event: string) => {\n let errorLogged = false;\n let timedOut = false;\n try {\n // Always open a readwrite transaction over both stores so that the read and\n // any subsequent write are atomic. IDB serializes readwrite transactions on\n // overlapping stores, so concurrent fire-and-forget callers (events-manager\n // does not await this method) are queued by the engine rather than interleaving\n // — eliminating the TOCTOU race that a narrow-read + separate-write approach\n // would introduce on the split path.\n const tx = this.db.transaction([currentSequenceKey, sequencesToSendKey], 'readwrite');\n // Attach a catch handler immediately so tx.done rejections (e.g. AbortError after\n // put succeeds but before auto-commit) are always handled without blocking.\n // The errorLogged / timedOut flags prevent double-logging when the outer catch\n // (or the timeout) already fired for the same transaction.\n tx.done.catch((e: unknown) => {\n if (!errorLogged && !timedOut) {\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n });\n // Stalled-transaction protection: see armTxDoneTimeout in getSequencesToSend.\n const cancelTimeout = armTxDoneTimeout(tx.done, TX_DONE_TIMEOUT_MS, () => {\n if (!errorLogged && !timedOut) {\n timedOut = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: transaction timed out`);\n this.recordFailure();\n }\n });\n const sequenceEvents = await tx.objectStore(currentSequenceKey).get(sessionId);\n\n // Foreign-tab record path: another tab owns the current-sequence slot for this\n // sessionId. Don't silently overwrite — that would drop the foreign tab's\n // in-progress events. Promote them to sequencesToSend (tabId kept for forensics)\n // before claiming the slot for ourselves. getSequencesToSend no longer filters\n // by tabId, so either tab may flush the promoted sequence; server deduplicates.\n if (sequenceEvents?.tabId && sequenceEvents.tabId !== this.tabId) {\n if (sequenceEvents.events.length > 0) {\n await tx.objectStore(sequencesToSendKey).put({\n sessionId,\n events: sequenceEvents.events,\n tabId: sequenceEvents.tabId,\n });\n }\n await tx.objectStore(currentSequenceKey).put({ sessionId, events: [event], tabId: this.tabId });\n this.recordSuccess();\n cancelTimeout();\n return undefined;\n }\n\n // ownedSequence is either undefined (no record yet) or this tab's record.\n const ownedSequence = sequenceEvents;\n\n if (!ownedSequence) {\n await tx.objectStore(currentSequenceKey).put({ sessionId, events: [event], tabId: this.tabId });\n this.recordSuccess();\n cancelTimeout();\n return undefined;\n }\n\n if (!this.shouldSplitEventsList(ownedSequence.events, event)) {\n await tx\n .objectStore(currentSequenceKey)\n .put({ sessionId, events: ownedSequence.events.concat(event), tabId: this.tabId });\n this.recordSuccess();\n cancelTimeout();\n return undefined;\n }\n\n // Split path: reset sessionCurrentSequence and write the old events to\n // sequencesToSend atomically within the same transaction.\n const eventsToSend = ownedSequence.events;\n\n // shouldSplitEventsList can return true with an empty buffer when a single\n // incoming event is larger than MAX_EVENT_LIST_SIZE (2 MB) — the size-constraint\n // branch fires regardless of current length. Don't write a zero-event row to\n // sequencesToSend (which would later POST as an empty body, the SR-4284 root\n // cause); just claim the slot for the new event without finalizing anything.\n // This is the *primary* root-cause filter site: warn here so post-deploy\n // Datadog can confirm the new SDK is actually preventing the bug at its source\n // (vs. only seeing leftover-state hits at the get/storeCurrentSequence layers).\n if (eventsToSend.length === 0) {\n this.maybeLogEmptyFiltered('addEventToCurrentSequence');\n await tx.objectStore(currentSequenceKey).put({ sessionId, events: [event], tabId: this.tabId });\n this.recordSuccess();\n cancelTimeout();\n return undefined;\n }\n\n await tx.objectStore(currentSequenceKey).put({ sessionId, events: [event], tabId: this.tabId });\n const sequenceId = await tx.objectStore(sequencesToSendKey).put({\n sessionId,\n events: eventsToSend,\n tabId: this.tabId,\n });\n\n this.recordSuccess();\n cancelTimeout();\n return {\n events: eventsToSend,\n sessionId,\n sequenceId,\n };\n } catch (e) {\n if (!timedOut) {\n errorLogged = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n }\n return undefined;\n };\n\n storeSendingEvents = async (sessionId: number, events: Events) => {\n try {\n const sequenceId = await this.db.put<'sequencesToSend'>(sequencesToSendKey, {\n sessionId: sessionId,\n events: events,\n tabId: this.tabId,\n });\n this.recordSuccess();\n return sequenceId;\n } catch (e) {\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n return undefined;\n };\n\n cleanUpSessionEventsStore = async (_sessionId: number, sequenceId?: number) => {\n if (!sequenceId) {\n return;\n }\n try {\n await this.db.delete<'sequencesToSend'>(sequencesToSendKey, sequenceId);\n this.recordSuccess();\n } catch (e) {\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"events-idb-store.js","sourceRoot":"","sources":["../../../src/events/events-idb-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { DBSchema, IDBPDatabase, openDB } from 'idb';\nimport { STORAGE_FAILURE } from '../messages';\nimport { EventType, Events, SendingSequencesReturn } from '../typings/session-replay';\nimport { BaseEventsStore, InstanceArgs as BaseInstanceArgs } from './base-events-store';\nimport { logIdbError } from '../utils/is-abort-error';\n\n// crypto.randomUUID() requires a secure context (https). Fall back to a\n// Math.random-based UUID for http origins or older browsers — tab IDs don't\n// need to be cryptographically secure, just unique within a session.\nexport function generateUUID(): string {\n try {\n return crypto.randomUUID();\n } catch {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);\n });\n }\n}\n\nexport const currentSequenceKey = 'sessionCurrentSequence';\nexport const sequencesToSendKey = 'sequencesToSend';\nexport const remoteConfigKey = 'remoteConfig';\n\n// Timeout for openDB at init. IDB openDB can hang forever when another tab\n// holds an open connection during a version upgrade, or when the DB is in a\n// \"closing\" state (documented Chrome behaviour). When that happens we want\n// SessionReplayEventsIDBStore.new() to bail out so the caller can fall back\n// to the in-memory store.\nexport const OPEN_DB_TIMEOUT_MS = 2000;\n\n// Timeout for per-operation tx.done settlement. Mid-recording a readwrite\n// transaction can stall (storage pressure in some browsers stalls instead of\n// throwing); without a timeout, recordFailure() is never called and the\n// memory fallback never triggers. The transaction may still settle later;\n// the timedOut flag prevents double-counting alongside errorLogged.\nexport const TX_DONE_TIMEOUT_MS = 5000;\n\n/**\n * Race a promise against a timeout. Resolves/rejects with the original\n * promise's value when it settles first; rejects with a timeout error if\n * `ms` elapses first. Either way the timer is cleared so we don't leak\n * pending setTimeouts.\n */\nexport function withTimeout<T>(promise: Promise<T>, ms: number, message = 'IDB operation timed out'): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error(`${message} after ${ms}ms`)), ms);\n promise.then(\n (v) => {\n clearTimeout(timer);\n resolve(v);\n },\n (e) => {\n clearTimeout(timer);\n reject(e);\n },\n );\n });\n}\n\n/**\n * Arms a watchdog that fires `onTimeout` only if `tx.done` hasn't settled\n * within `ms`. A \"soft cancel\" returned to the caller suppresses the timeout\n * if the synchronous operation body completes without exception — important\n * because in test environments fake timers can fire ahead of tx.done's commit\n * microtask. Production behaviour is preserved: a genuinely stalled\n * transaction (no commit, no error, individual op promises never resolved)\n * cannot reach the soft-cancel call and the timeout fires as designed.\n *\n * Soft-cancel is only invoked once the awaits inside the operation's outer\n * try block have all returned — i.e. the IDB driver acknowledged each\n * individual put/get. If the driver accepts a put but the underlying\n * transaction silently fails to commit (the production stall scenario),\n * tx.done still hasn't settled, and recordFailure must NOT have been\n * suppressed. The fix for that scenario in production is the tx.done.catch\n * handler attached separately by callers, which catches the eventual abort.\n *\n * The soft-cancel pattern only suppresses the timeout for the \"all puts\n * resolved successfully\" case — a case where tx.done is overwhelmingly\n * likely to settle imminently in production. If it doesn't, callers can\n * still detect the failure on the NEXT operation (which will fail to open\n * a transaction or hit the same pressure), because all three methods use\n * readwrite transactions on the same two stores, which IDB serializes:\n * if T1's tx.done never settles, T2 is blocked waiting for T1 to commit or\n * abort — T2's put/get requests never resolve, T2 never reaches its\n * soft-cancel, and T2's watchdog fires, calling recordFailure().\n */\nfunction armTxDoneTimeout(txDone: Promise<unknown>, ms: number, onTimeout: () => void): () => void {\n const timer = setTimeout(onTimeout, ms);\n // Belt-and-braces: clear the timer when tx.done settles, even though the\n // primary cancel path is the caller's success-path cancel(). This covers\n // the case where tx.done settles with no caller cancellation (shouldn't\n // happen in current code paths, but cheap insurance).\n txDone.then(\n () => clearTimeout(timer),\n () => clearTimeout(timer),\n );\n return () => clearTimeout(timer);\n}\n\nexport interface SessionReplayDB extends DBSchema {\n sessionCurrentSequence: {\n key: number;\n value: Omit<SendingSequencesReturn<number>, 'sequenceId'> & { tabId?: string };\n };\n sequencesToSend: {\n key: number;\n value: Omit<SendingSequencesReturn<number>, 'sequenceId'> & { tabId?: string };\n indexes: { sessionId: string | number };\n };\n}\n\nexport const defineObjectStores = (db: IDBPDatabase<SessionReplayDB>) => {\n let sequencesStore;\n let currentSequenceStore;\n if (!db.objectStoreNames.contains(currentSequenceKey)) {\n currentSequenceStore = db.createObjectStore(currentSequenceKey, {\n keyPath: 'sessionId',\n });\n }\n if (!db.objectStoreNames.contains(sequencesToSendKey)) {\n sequencesStore = db.createObjectStore(sequencesToSendKey, {\n keyPath: 'sequenceId',\n autoIncrement: true,\n });\n sequencesStore.createIndex('sessionId', 'sessionId');\n }\n return {\n sequencesStore,\n currentSequenceStore,\n };\n};\n\nexport const createStore = async (dbName: string) => {\n // Wrap openDB with a timeout so a hung connection (foreign tab holding an\n // open handle during version upgrade, or \"closing\" DB) doesn't block the\n // SDK from initialising. On timeout this rejects, which propagates up to\n // SessionReplayEventsIDBStore.new()'s catch block, returning undefined and\n // triggering the memory fallback.\n return await withTimeout(\n openDB<SessionReplayDB>(dbName, 1, {\n upgrade: defineObjectStores,\n }),\n OPEN_DB_TIMEOUT_MS,\n 'IDB openDB timed out',\n );\n};\n\ntype InstanceArgs = {\n apiKey: string;\n db: IDBPDatabase<SessionReplayDB>;\n tabId: string;\n onPersistentFailure?: () => void;\n consecutiveFailureThreshold?: number;\n} & BaseInstanceArgs;\n\nexport class SessionReplayEventsIDBStore extends BaseEventsStore<number> {\n private readonly db: IDBPDatabase<SessionReplayDB>;\n private readonly tabId: string;\n private readonly onPersistentFailure?: () => void;\n private readonly consecutiveFailureThreshold: number;\n private consecutiveFailures = 0;\n private hasTriggeredFallback = false;\n private emptyFilteredCount = 0;\n\n // Sampled (1 in 100) debug log so we can observe whether the store-layer guards\n // are catching empty-batch cases that would otherwise hit the empty-body 400 path\n // on the server. Logged at debug, not warn — this is operational telemetry for\n // post-deploy verification, not a customer-actionable warning. Per-store-instance\n // counter (rather than Math.random) keeps the first hit deterministic for tests.\n private maybeLogEmptyFiltered(source: string) {\n if (this.emptyFilteredCount++ % 100 === 0) {\n this.loggerProvider.debug(`Filtered empty session replay sequence at ${source} (idb store)`);\n }\n }\n\n constructor(args: InstanceArgs) {\n super(args);\n this.db = args.db;\n this.tabId = args.tabId;\n this.onPersistentFailure = args.onPersistentFailure;\n // Default threshold of 1: fall back to memory immediately on the first IDB failure.\n // Session replay correctness is far more important than persistence, and IDB errors\n // are typically the symptom of a deeper problem (storage pressure, locked DB, broken\n // browser implementation) that won't recover within a single session. Memory store\n // is always safe — fall back early.\n this.consecutiveFailureThreshold = args.consecutiveFailureThreshold ?? 1;\n }\n\n private recordFailure() {\n this.consecutiveFailures++;\n if (!this.hasTriggeredFallback && this.consecutiveFailures >= this.consecutiveFailureThreshold) {\n this.hasTriggeredFallback = true;\n this.onPersistentFailure?.();\n }\n }\n\n private recordSuccess() {\n this.consecutiveFailures = 0;\n }\n\n static async new(\n type: EventType,\n args: Omit<InstanceArgs, 'db' | 'tabId'> & { tabId?: string },\n ): Promise<SessionReplayEventsIDBStore | undefined> {\n try {\n const dbSuffix = type === 'replay' ? '' : `_${type}`;\n const dbName = `${args.apiKey.substring(0, 10)}_amp_session_replay_events${dbSuffix}`;\n const db = await createStore(dbName);\n // Generate a fresh in-memory UUID per store instance. sessionStorage is\n // intentionally avoided: standalone session-replay customers (without the\n // analytics-browser SDK) would be exposed to a new storage surface they\n // did not consent to, and persistence across page reloads is not needed —\n // completed sequences in sequencesToSend are flushed by any tab/instance.\n const tabId = args.tabId ?? generateUUID();\n return new SessionReplayEventsIDBStore({\n ...args,\n db,\n tabId,\n });\n } catch (e) {\n logIdbError(args.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n }\n return;\n }\n\n async getCurrentSequenceEvents(sessionId?: number) {\n if (sessionId) {\n const record = await this.db.get('sessionCurrentSequence', sessionId);\n if (!record) {\n return undefined;\n }\n // Only return our own tab's record (or legacy untagged records).\n if (record.tabId && record.tabId !== this.tabId) {\n return undefined;\n }\n const { tabId: _tabId, ...rest } = record;\n return [rest];\n }\n\n const allEvents = [];\n for (const record of await this.db.getAll('sessionCurrentSequence')) {\n if (record.tabId && record.tabId !== this.tabId) {\n continue;\n }\n const { tabId: _tabId, ...rest } = record;\n allEvents.push(rest);\n }\n\n return allEvents;\n }\n\n getSequencesToSend = async (): Promise<SendingSequencesReturn<number>[] | undefined> => {\n let errorLogged = false;\n let timedOut = false;\n try {\n const sequences: SendingSequencesReturn<number>[] = [];\n // readwrite mode so we can prune stale empty rows in-place. Without that,\n // older-SDK-persisted events:[] rows sit in IDB indefinitely and re-fire the\n // sampled log on every flush cycle, creating Datadog noise indistinguishable\n // from active bug occurrences.\n const tx = this.db.transaction('sequencesToSend', 'readwrite');\n // Attach a catch handler immediately so tx.done rejections (e.g. AbortError after\n // cursor traversal completes) are always handled without blocking the return path.\n // The errorLogged / timedOut flags prevent double-logging and double-recording\n // when the outer catch (or the timeout race) already fired for the same abort.\n tx.done.catch((e: unknown) => {\n if (!errorLogged && !timedOut) {\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n });\n // Arm a watchdog so a stalled transaction (no error, no commit, e.g.\n // storage pressure on some browsers) still trips the failure counter.\n // The watchdog fires only when tx.done genuinely never settles AND the\n // operation's success path didn't run; if tx.done rejects (abort), the\n // tx.done.catch handler above is the sole recorder of failure.\n const cancelTimeout = armTxDoneTimeout(tx.done, TX_DONE_TIMEOUT_MS, () => {\n if (!errorLogged && !timedOut) {\n timedOut = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: transaction timed out`);\n this.recordFailure();\n }\n });\n let cursor = await tx.store.openCursor();\n while (cursor) {\n const { sessionId, events } = cursor.value;\n // Skip empty persisted records. These can come from older SDK builds that\n // wrote zero-event sequences via addEventToCurrentSequence's split path\n // (when a single oversized event triggered a split with empty buffer);\n // flushing them produces empty-body POSTs the server rejects with 400.\n // Delete them in-place so they don't re-fire the sampled log on every\n // subsequent flush — by construction (this fix) we never write empty rows\n // anymore, so any empty row is unambiguously stale.\n if (events.length === 0) {\n this.maybeLogEmptyFiltered('getSequencesToSend');\n await cursor.delete();\n } else {\n // Return all completed sequences regardless of tabId. Filtering by tab\n // would cause event loss on page reload: a new store instance gets a\n // fresh in-memory UUID and would never see sequences written by the\n // previous instance. Completed sequences are safe to flush by any\n // tab/instance; the server deduplicates, and cleanUpSessionEventsStore\n // on an already-deleted key is a no-op.\n sequences.push({\n events,\n sequenceId: cursor.key,\n sessionId,\n });\n }\n cursor = await cursor.continue();\n }\n\n this.recordSuccess();\n cancelTimeout();\n return sequences;\n } catch (e) {\n if (!timedOut) {\n errorLogged = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n }\n return undefined;\n };\n\n storeCurrentSequence = async (sessionId: number) => {\n let errorLogged = false;\n let timedOut = false;\n try {\n // Wrap the read of sessionCurrentSequence and the writes to sequencesToSend +\n // sessionCurrentSequence in a single readwrite transaction so the three operations\n // commit or roll back atomically. Without this, a concurrent addEventToCurrentSequence\n // call could interleave and either lose the events being promoted or duplicate them\n // (storeCurrentSequence reads N events, addEvent appends an N+1th, storeCurrentSequence\n // writes only the first N back to sequencesToSend, then resets the slot — losing N+1).\n const tx = this.db.transaction([currentSequenceKey, sequencesToSendKey], 'readwrite');\n tx.done.catch((e: unknown) => {\n if (!errorLogged && !timedOut) {\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n });\n // Stalled-transaction protection: see armTxDoneTimeout in getSequencesToSend.\n const cancelTimeout = armTxDoneTimeout(tx.done, TX_DONE_TIMEOUT_MS, () => {\n if (!errorLogged && !timedOut) {\n timedOut = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: transaction timed out`);\n this.recordFailure();\n }\n });\n\n const currentSequenceData = await tx.objectStore(currentSequenceKey).get(sessionId);\n // Skip promotion if the slot is empty or owned by another tab — let the owning\n // tab promote its own events on its next addEventToCurrentSequence/storeCurrentSequence\n // call (via Bug 1's foreign-tab promotion path).\n // Don't call recordSuccess() here: no write was performed, so this is not\n // evidence the storage layer is healthy — leave the failure counter unchanged.\n if (!currentSequenceData || (currentSequenceData.tabId && currentSequenceData.tabId !== this.tabId)) {\n cancelTimeout();\n return undefined;\n }\n\n // Skip empty sequences — no point writing a zero-event row to sequencesToSend\n // (would later POST as an empty body and 400 on the server).\n if (currentSequenceData.events.length === 0) {\n this.maybeLogEmptyFiltered('storeCurrentSequence');\n cancelTimeout();\n return undefined;\n }\n\n const sequenceId = await tx.objectStore(sequencesToSendKey).put({\n sessionId,\n events: currentSequenceData.events,\n tabId: this.tabId,\n });\n\n await tx.objectStore(currentSequenceKey).put({\n sessionId,\n events: [],\n tabId: this.tabId,\n });\n this.rememberSequenceContentBytes(sessionId, []);\n\n this.recordSuccess();\n cancelTimeout();\n const { tabId: _tabId, ...rest } = currentSequenceData;\n return {\n ...rest,\n sessionId,\n sequenceId,\n };\n } catch (e) {\n this.forgetSequenceContentBytes(sessionId);\n if (!timedOut) {\n errorLogged = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n }\n return undefined;\n };\n\n addEventToCurrentSequence = async (sessionId: number, event: string) => {\n let errorLogged = false;\n let timedOut = false;\n try {\n // Always open a readwrite transaction over both stores so that the read and\n // any subsequent write are atomic. IDB serializes readwrite transactions on\n // overlapping stores, so concurrent fire-and-forget callers (events-manager\n // does not await this method) are queued by the engine rather than interleaving\n // — eliminating the TOCTOU race that a narrow-read + separate-write approach\n // would introduce on the split path.\n const tx = this.db.transaction([currentSequenceKey, sequencesToSendKey], 'readwrite');\n // Attach a catch handler immediately so tx.done rejections (e.g. AbortError after\n // put succeeds but before auto-commit) are always handled without blocking.\n // The errorLogged / timedOut flags prevent double-logging when the outer catch\n // (or the timeout) already fired for the same transaction.\n tx.done.catch((e: unknown) => {\n if (!errorLogged && !timedOut) {\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n });\n // Stalled-transaction protection: see armTxDoneTimeout in getSequencesToSend.\n const cancelTimeout = armTxDoneTimeout(tx.done, TX_DONE_TIMEOUT_MS, () => {\n if (!errorLogged && !timedOut) {\n timedOut = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: transaction timed out`);\n this.recordFailure();\n }\n });\n const sequenceEvents = await tx.objectStore(currentSequenceKey).get(sessionId);\n\n // Foreign-tab record path: another tab owns the current-sequence slot for this\n // sessionId. Don't silently overwrite — that would drop the foreign tab's\n // in-progress events. Promote them to sequencesToSend (tabId kept for forensics)\n // before claiming the slot for ourselves. getSequencesToSend no longer filters\n // by tabId, so either tab may flush the promoted sequence; server deduplicates.\n if (sequenceEvents?.tabId && sequenceEvents.tabId !== this.tabId) {\n if (sequenceEvents.events.length > 0) {\n await tx.objectStore(sequencesToSendKey).put({\n sessionId,\n events: sequenceEvents.events,\n tabId: sequenceEvents.tabId,\n });\n }\n const nextEvents = [event];\n await tx.objectStore(currentSequenceKey).put({ sessionId, events: nextEvents, tabId: this.tabId });\n this.rememberSequenceContentBytes(sessionId, nextEvents);\n this.recordSuccess();\n cancelTimeout();\n return undefined;\n }\n\n // ownedSequence is either undefined (no record yet) or this tab's record.\n const ownedSequence = sequenceEvents;\n\n if (!ownedSequence) {\n const nextEvents = [event];\n await tx.objectStore(currentSequenceKey).put({ sessionId, events: nextEvents, tabId: this.tabId });\n this.rememberSequenceContentBytes(sessionId, nextEvents);\n this.recordSuccess();\n cancelTimeout();\n return undefined;\n }\n\n // Freshly loaded array: seed it with the session's running size so the split check is O(1).\n this.recallSequenceContentBytes(sessionId, ownedSequence.events);\n if (!this.shouldSplitEventsList(ownedSequence.events, event)) {\n const nextEvents = ownedSequence.events.concat(event);\n await tx.objectStore(currentSequenceKey).put({ sessionId, events: nextEvents, tabId: this.tabId });\n this.rememberSequenceContentBytes(sessionId, nextEvents);\n this.recordSuccess();\n cancelTimeout();\n return undefined;\n }\n\n // Split path: reset sessionCurrentSequence and write the old events to\n // sequencesToSend atomically within the same transaction.\n const eventsToSend = ownedSequence.events;\n\n // shouldSplitEventsList can return true with an empty buffer when a single\n // incoming event is larger than MAX_EVENT_LIST_SIZE (2 MB) — the size-constraint\n // branch fires regardless of current length. Don't write a zero-event row to\n // sequencesToSend (which would later POST as an empty body, the SR-4284 root\n // cause); just claim the slot for the new event without finalizing anything.\n // This is the *primary* root-cause filter site: warn here so post-deploy\n // Datadog can confirm the new SDK is actually preventing the bug at its source\n // (vs. only seeing leftover-state hits at the get/storeCurrentSequence layers).\n if (eventsToSend.length === 0) {\n this.maybeLogEmptyFiltered('addEventToCurrentSequence');\n const nextEvents = [event];\n await tx.objectStore(currentSequenceKey).put({ sessionId, events: nextEvents, tabId: this.tabId });\n this.rememberSequenceContentBytes(sessionId, nextEvents);\n this.recordSuccess();\n cancelTimeout();\n return undefined;\n }\n\n const nextEvents = [event];\n await tx.objectStore(currentSequenceKey).put({ sessionId, events: nextEvents, tabId: this.tabId });\n this.rememberSequenceContentBytes(sessionId, nextEvents);\n const sequenceId = await tx.objectStore(sequencesToSendKey).put({\n sessionId,\n events: eventsToSend,\n tabId: this.tabId,\n });\n\n this.recordSuccess();\n cancelTimeout();\n return {\n events: eventsToSend,\n sessionId,\n sequenceId,\n };\n } catch (e) {\n this.forgetSequenceContentBytes(sessionId);\n if (!timedOut) {\n errorLogged = true;\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n }\n return undefined;\n };\n\n storeSendingEvents = async (sessionId: number, events: Events) => {\n try {\n const sequenceId = await this.db.put<'sequencesToSend'>(sequencesToSendKey, {\n sessionId: sessionId,\n events: events,\n tabId: this.tabId,\n });\n this.recordSuccess();\n return sequenceId;\n } catch (e) {\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n return undefined;\n };\n\n cleanUpSessionEventsStore = async (_sessionId: number, sequenceId?: number) => {\n if (!sequenceId) {\n return;\n }\n try {\n await this.db.delete<'sequencesToSend'>(sequencesToSendKey, sequenceId);\n this.recordSuccess();\n } catch (e) {\n logIdbError(this.loggerProvider, `${STORAGE_FAILURE}: ${e as string}`, e);\n this.recordFailure();\n }\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events-manager.d.ts","sourceRoot":"","sources":["../../../src/events/events-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,IAAI,mCAAmC,EAEjE,SAAS,EACT,SAAS,EACV,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"events-manager.d.ts","sourceRoot":"","sources":["../../../src/events/events-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,IAAI,mCAAmC,EAEjE,SAAS,EACT,SAAS,EACV,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAI5D,OAAO,EAAE,cAAc,EAAE,6BAA6B,EAAE,MAAM,sBAAsB,CAAC;AAIrF,MAAM,MAAM,uBAAuB,CAAC,IAAI,SAAS,SAAS,IAAI,mCAAmC,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG;IAChH;;;OAGG;IACH,eAAe,IAAI,MAAM,EAAE,CAAC;IAC5B;;;OAGG;IACH,uBAAuB,IAAI,IAAI,CAAC;IAChC,gBAAgB,EAAE,6BAA6B,CAAC;CACjD,CAAC;AAEF,eAAO,MAAM,mBAAmB;YAWtB,yBAAyB;;;;;;eAMtB,SAAS;;wBAED,OAAO;4CAoR3B,CAAC"}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.createEventsManager = void 0;
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
5
|
var constants_1 = require("../constants");
|
|
6
|
+
var utf8_byte_length_1 = require("../utils/utf8-byte-length");
|
|
6
7
|
var helpers_1 = require("../helpers");
|
|
7
8
|
var track_destination_1 = require("../track-destination");
|
|
8
9
|
var events_idb_store_1 = require("./events-idb-store");
|
|
@@ -116,14 +117,25 @@ var createEventsManager = function (_a) {
|
|
|
116
117
|
// addCompressedEventToManager (e.g. stored by a previous SDK version or via
|
|
117
118
|
// storeCurrentSequence/sendStoredEvents which bypass the capture-time check).
|
|
118
119
|
// Compare UTF-8 byte size, not JS char count, to match the server-side limit.
|
|
119
|
-
var
|
|
120
|
-
var
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
120
|
+
var events = rawEvents;
|
|
121
|
+
var oversizedSizes = [];
|
|
122
|
+
for (var i = 0; i < rawEvents.length; i++) {
|
|
123
|
+
var bytes = (0, utf8_byte_length_1.utf8ByteLengthIfOver)(rawEvents[i], maxSingleEventSize);
|
|
124
|
+
if (bytes === undefined) {
|
|
125
|
+
if (events !== rawEvents)
|
|
126
|
+
events.push(rawEvents[i]);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (oversizedSizes.length === 0) {
|
|
130
|
+
events = rawEvents.slice(0, i);
|
|
131
|
+
}
|
|
132
|
+
oversizedSizes.push(bytes);
|
|
133
|
+
}
|
|
134
|
+
if (oversizedSizes.length > 0) {
|
|
135
|
+
config.loggerProvider.warn("Dropping ".concat(oversizedSizes.length, " oversized event(s) from session replay sequence before send. Sizes: ").concat(oversizedSizes
|
|
136
|
+
.map(function (bytes) { return "".concat(Math.round(bytes / 1024), " KB"); })
|
|
124
137
|
.join(', '), ". If this recurs, please open a GitHub issue at https://github.com/amplitude/Amplitude-TypeScript/issues or contact Amplitude support."));
|
|
125
138
|
}
|
|
126
|
-
var events = oversized.length > 0 ? sizedEvents.filter(function (s) { return s.bytes <= maxSingleEventSize; }).map(function (s) { return s.event; }) : rawEvents;
|
|
127
139
|
if (events.length === 0) {
|
|
128
140
|
store.cleanUpSessionEventsStore(sessionId, sequenceId).catch(function (e) {
|
|
129
141
|
config.loggerProvider.warn('Failed to clean up session replay events store:', e);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events-manager.js","sourceRoot":"","sources":["../../../src/events/events-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n SessionReplayEventsManager as AmplitudeSessionReplayEventsManager,\n EventsStore,\n EventType,\n StoreType,\n} from '../typings/session-replay';\n\nimport { SessionReplayJoinedConfig } from '../config/types';\nimport { MAX_SINGLE_EVENT_SIZE } from '../constants';\nimport { getStorageSize } from '../helpers';\nimport { PayloadBatcher, SessionReplayTrackDestination } from '../track-destination';\nimport { SessionReplayEventsIDBStore } from './events-idb-store';\nimport { InMemoryEventsStore } from './events-memory-store';\n\nexport type EventsManagerWithBeacon<Type extends EventType> = AmplitudeSessionReplayEventsManager<Type, string> & {\n /**\n * Returns current pending events (since last flush) for synchronous access on page exit.\n * Used to populate a sendBeacon payload when the page is unloading.\n */\n getBeaconEvents(): string[];\n /**\n * Drops all pending beacon events. Used when the session is decided to be below\n * the min duration threshold so its events don't leak into a later session's beacon.\n */\n dropPendingBeaconEvents(): void;\n trackDestination: SessionReplayTrackDestination;\n};\n\nexport const createEventsManager = async <Type extends EventType>({\n config,\n minInterval,\n maxInterval,\n maxPersistedEventsSize,\n type,\n payloadBatcher,\n storeType,\n trackDestinationWorkerScript,\n shouldSend,\n}: {\n config: SessionReplayJoinedConfig;\n type: Type;\n minInterval?: number;\n maxInterval?: number;\n maxPersistedEventsSize?: number;\n payloadBatcher?: PayloadBatcher;\n storeType: StoreType;\n trackDestinationWorkerScript?: string;\n shouldSend?: () => boolean;\n}): Promise<EventsManagerWithBeacon<Type>> => {\n // Configurable per-single-event drop cap (defaults to MAX_SINGLE_EVENT_SIZE); enforced both\n // here as a pre-send backstop and at capture time in EventCompressor.\n const maxSingleEventSize = config.maxSingleEventSizeBytes ?? MAX_SINGLE_EVENT_SIZE;\n const trackDestination = new SessionReplayTrackDestination({\n ...config,\n loggerProvider: config.loggerProvider,\n payloadBatcher,\n workerScript: trackDestinationWorkerScript,\n });\n\n const getMemoryStore = (): EventsStore<number> => {\n return new InMemoryEventsStore({\n loggerProvider: config.loggerProvider,\n maxInterval,\n minInterval,\n maxPersistedEventsSize,\n });\n };\n\n let lastKnownDeviceId: string | undefined;\n let usingIdbStore = false;\n let store!: EventsStore<number>;\n\n const switchToMemoryStore = async () => {\n if (!usingIdbStore) return;\n usingIdbStore = false;\n config.loggerProvider.warn('IDB store is experiencing repeated failures; falling back to in-memory event store.');\n const sequences = lastKnownDeviceId ? await store.getSequencesToSend() : undefined;\n store = getMemoryStore();\n if (sequences && lastKnownDeviceId) {\n const deviceId = lastKnownDeviceId;\n sequences.forEach((seq) => {\n sendEventsList({ sequenceId: seq.sequenceId, events: seq.events, sessionId: seq.sessionId, deviceId });\n });\n }\n };\n\n const getIdbStoreOrFallback = async (): Promise<EventsStore<number>> => {\n const idb = await SessionReplayEventsIDBStore.new(type, {\n loggerProvider: config.loggerProvider,\n minInterval,\n maxInterval,\n maxPersistedEventsSize,\n apiKey: config.apiKey,\n onPersistentFailure: () => {\n void switchToMemoryStore();\n },\n });\n if (!idb) {\n config.loggerProvider.log('Failed to initialize idb store, falling back to memory store.');\n return getMemoryStore();\n }\n usingIdbStore = true;\n return idb;\n };\n\n store = storeType === 'idb' ? await getIdbStoreOrFallback() : getMemoryStore();\n\n // Beacon buffer: a sliding window of pending (unsent) event strings for synchronous\n // access on page exit. Uses an absolute index counter to correctly handle concurrent\n // async flushes without losing events added between the flush call and its resolution.\n const beaconBuffer: string[] = [];\n let beaconWindowStart = 0; // absolute index of the first element in beaconBuffer\n\n const advanceBeaconWindow = (upToAbsoluteIdx: number) => {\n if (upToAbsoluteIdx <= beaconWindowStart) return;\n const trimCount = Math.min(upToAbsoluteIdx - beaconWindowStart, beaconBuffer.length);\n if (trimCount > 0) {\n beaconBuffer.splice(0, trimCount);\n beaconWindowStart = upToAbsoluteIdx;\n }\n };\n\n /**\n * Immediately sends events to the track destination.\n */\n const sendEventsList = ({\n events: rawEvents,\n sessionId,\n deviceId,\n sequenceId,\n }: {\n events: string[];\n sessionId: string | number;\n deviceId: string;\n sequenceId?: number;\n }) => {\n // Backstop for events that entered IDB before the per-event size guard in\n // addCompressedEventToManager (e.g. stored by a previous SDK version or via\n // storeCurrentSequence/sendStoredEvents which bypass the capture-time check).\n // Compare UTF-8 byte size, not JS char count, to match the server-side limit.\n const sizedEvents = rawEvents.map((e) => ({ event: e, bytes: new Blob([e]).size }));\n const oversized = sizedEvents.filter((s) => s.bytes > maxSingleEventSize);\n if (oversized.length > 0) {\n config.loggerProvider.warn(\n `Dropping ${oversized.length} oversized event(s) from session replay sequence before send. Sizes: ${oversized\n .map((s) => `${Math.round(s.bytes / 1024)} KB`)\n .join(\n ', ',\n )}. If this recurs, please open a GitHub issue at https://github.com/amplitude/Amplitude-TypeScript/issues or contact Amplitude support.`,\n );\n }\n const events =\n oversized.length > 0 ? sizedEvents.filter((s) => s.bytes <= maxSingleEventSize).map((s) => s.event) : rawEvents;\n if (events.length === 0) {\n store.cleanUpSessionEventsStore(sessionId, sequenceId).catch((e) => {\n config.loggerProvider.warn('Failed to clean up session replay events store:', e);\n });\n return;\n }\n\n if (config.debugMode) {\n getStorageSize()\n .then(({ totalStorageSize, percentOfQuota, usageDetails }) => {\n config.loggerProvider.debug(\n `Total storage size: ${totalStorageSize} KB, percentage of quota: ${percentOfQuota}%, usage details: ${usageDetails}`,\n );\n })\n .catch(() => {\n // swallow error\n });\n }\n\n trackDestination.sendEventsList({\n events: events,\n sessionId: sessionId,\n flushMaxRetries: config.flushMaxRetries,\n apiKey: config.apiKey,\n deviceId: deviceId,\n sampleRate: config.sampleRate,\n serverZone: config.serverZone,\n version: config.version,\n type,\n onComplete: async () => {\n await store.cleanUpSessionEventsStore(sessionId, sequenceId);\n return;\n },\n });\n };\n\n const sendCurrentSequenceEvents = ({ sessionId, deviceId }: { sessionId: number; deviceId: string }) => {\n lastKnownDeviceId = deviceId;\n // Evaluate shouldSend synchronously before the async store read. asyncSetSessionId\n // updates sessionStartTime immediately after calling sendEvents(), so by the time\n // storeCurrentSequence resolves the start time would reflect the new session and\n // the elapsed check would compute ~0ms, silently dropping the previous session's events.\n if (shouldSend && !shouldSend()) return;\n // Snapshot the absolute end-index before the async store read so that any events\n // pushed after this point are NOT considered sent and remain in the beacon buffer.\n const snapshotAbsIdx = beaconWindowStart + beaconBuffer.length;\n store\n .storeCurrentSequence(sessionId)\n .then((currentSequence) => {\n if (currentSequence) {\n advanceBeaconWindow(snapshotAbsIdx);\n sendEventsList({\n sequenceId: currentSequence.sequenceId,\n events: currentSequence.events,\n sessionId: currentSequence.sessionId,\n deviceId,\n });\n }\n })\n .catch((e) => {\n config.loggerProvider.warn('Failed to get current sequence of session replay events for session:', e);\n });\n };\n\n const sendStoredEvents = async ({ deviceId }: { deviceId: string }) => {\n lastKnownDeviceId = deviceId;\n const sequencesToSend = await store.getSequencesToSend();\n if (!sequencesToSend?.length) {\n return;\n }\n config.loggerProvider.log(`Draining ${sequencesToSend.length} stored sequence(s) from previous session.`);\n // These persisted sequences are about to be enqueued back-to-back. Without\n // coalescing they flush as N separate POSTs — a request flood on page load. Mark the\n // imminent flush to merge same-identity batches (the enqueues below are synchronous, so\n // the whole backlog lands in the queue before the deferred flush consumes the flag). Skip\n // the single-sequence case where there's nothing to merge.\n if (sequencesToSend.length > 1) {\n trackDestination.markCoalesceNextFlush();\n }\n sequencesToSend.forEach((sequence) => {\n sendEventsList({\n sequenceId: sequence.sequenceId,\n events: sequence.events,\n sessionId: sequence.sessionId,\n deviceId,\n });\n });\n };\n\n const addEvent = ({\n event,\n sessionId,\n deviceId,\n }: {\n event: { type: Type; data: string };\n sessionId: number;\n deviceId: string;\n }) => {\n lastKnownDeviceId = deviceId;\n // Capture shouldSend synchronously before the async store write, for the same\n // reason as sendCurrentSequenceEvents: asyncSetSessionId updates sessionStartTime\n // synchronously, so evaluating inside the .then() would use the new session's\n // start time and compute ~0ms elapsed, silently dropping a valid batch.\n const canSend = !shouldSend || shouldSend();\n // Record the absolute index of this event in the beacon buffer before the async\n // store operation. If a batch split occurs, we advance the window up to (but not\n // including) this event so that it starts the next pending window.\n const absIdx = beaconWindowStart + beaconBuffer.length;\n beaconBuffer.push(event.data);\n store\n .addEventToCurrentSequence(sessionId, event.data)\n .then((sequenceToSend) => {\n if (sequenceToSend) {\n if (!canSend) {\n // The split atomically moved events to sequencesToSend; without cleanup\n // they would be unconditionally replayed on next page load via\n // sendStoredEvents, bypassing the min session duration gate. The split\n // batch must also be dropped from the beacon buffer so it isn't sent\n // via sendBeacon on page unload (potentially attributed to a later session).\n store.cleanUpSessionEventsStore(sequenceToSend.sessionId, sequenceToSend.sequenceId).catch((e) => {\n config.loggerProvider.warn('Failed to clean up dropped session replay sequence:', e);\n });\n advanceBeaconWindow(absIdx);\n return;\n }\n // Events before absIdx belong to the split batch being sent; advance window.\n advanceBeaconWindow(absIdx);\n sendEventsList({\n sequenceId: sequenceToSend.sequenceId,\n events: sequenceToSend.events,\n sessionId: sequenceToSend.sessionId,\n deviceId,\n });\n }\n })\n .catch((e) => {\n config.loggerProvider.warn('Failed to add event to session replay capture:', e);\n });\n };\n\n async function flush(useRetry = false) {\n return trackDestination.flush(useRetry);\n }\n\n const getBeaconEvents = (): string[] => [...beaconBuffer];\n\n const dropPendingBeaconEvents = () => {\n advanceBeaconWindow(beaconWindowStart + beaconBuffer.length);\n };\n\n return {\n sendCurrentSequenceEvents,\n addEvent,\n sendStoredEvents,\n flush,\n getBeaconEvents,\n dropPendingBeaconEvents,\n trackDestination,\n };\n};\n"]}
|
|
1
|
+
{"version":3,"file":"events-manager.js","sourceRoot":"","sources":["../../../src/events/events-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n SessionReplayEventsManager as AmplitudeSessionReplayEventsManager,\n EventsStore,\n EventType,\n StoreType,\n} from '../typings/session-replay';\n\nimport { SessionReplayJoinedConfig } from '../config/types';\nimport { MAX_SINGLE_EVENT_SIZE } from '../constants';\nimport { utf8ByteLengthIfOver } from '../utils/utf8-byte-length';\nimport { getStorageSize } from '../helpers';\nimport { PayloadBatcher, SessionReplayTrackDestination } from '../track-destination';\nimport { SessionReplayEventsIDBStore } from './events-idb-store';\nimport { InMemoryEventsStore } from './events-memory-store';\n\nexport type EventsManagerWithBeacon<Type extends EventType> = AmplitudeSessionReplayEventsManager<Type, string> & {\n /**\n * Returns current pending events (since last flush) for synchronous access on page exit.\n * Used to populate a sendBeacon payload when the page is unloading.\n */\n getBeaconEvents(): string[];\n /**\n * Drops all pending beacon events. Used when the session is decided to be below\n * the min duration threshold so its events don't leak into a later session's beacon.\n */\n dropPendingBeaconEvents(): void;\n trackDestination: SessionReplayTrackDestination;\n};\n\nexport const createEventsManager = async <Type extends EventType>({\n config,\n minInterval,\n maxInterval,\n maxPersistedEventsSize,\n type,\n payloadBatcher,\n storeType,\n trackDestinationWorkerScript,\n shouldSend,\n}: {\n config: SessionReplayJoinedConfig;\n type: Type;\n minInterval?: number;\n maxInterval?: number;\n maxPersistedEventsSize?: number;\n payloadBatcher?: PayloadBatcher;\n storeType: StoreType;\n trackDestinationWorkerScript?: string;\n shouldSend?: () => boolean;\n}): Promise<EventsManagerWithBeacon<Type>> => {\n // Configurable per-single-event drop cap (defaults to MAX_SINGLE_EVENT_SIZE); enforced both\n // here as a pre-send backstop and at capture time in EventCompressor.\n const maxSingleEventSize = config.maxSingleEventSizeBytes ?? MAX_SINGLE_EVENT_SIZE;\n const trackDestination = new SessionReplayTrackDestination({\n ...config,\n loggerProvider: config.loggerProvider,\n payloadBatcher,\n workerScript: trackDestinationWorkerScript,\n });\n\n const getMemoryStore = (): EventsStore<number> => {\n return new InMemoryEventsStore({\n loggerProvider: config.loggerProvider,\n maxInterval,\n minInterval,\n maxPersistedEventsSize,\n });\n };\n\n let lastKnownDeviceId: string | undefined;\n let usingIdbStore = false;\n let store!: EventsStore<number>;\n\n const switchToMemoryStore = async () => {\n if (!usingIdbStore) return;\n usingIdbStore = false;\n config.loggerProvider.warn('IDB store is experiencing repeated failures; falling back to in-memory event store.');\n const sequences = lastKnownDeviceId ? await store.getSequencesToSend() : undefined;\n store = getMemoryStore();\n if (sequences && lastKnownDeviceId) {\n const deviceId = lastKnownDeviceId;\n sequences.forEach((seq) => {\n sendEventsList({ sequenceId: seq.sequenceId, events: seq.events, sessionId: seq.sessionId, deviceId });\n });\n }\n };\n\n const getIdbStoreOrFallback = async (): Promise<EventsStore<number>> => {\n const idb = await SessionReplayEventsIDBStore.new(type, {\n loggerProvider: config.loggerProvider,\n minInterval,\n maxInterval,\n maxPersistedEventsSize,\n apiKey: config.apiKey,\n onPersistentFailure: () => {\n void switchToMemoryStore();\n },\n });\n if (!idb) {\n config.loggerProvider.log('Failed to initialize idb store, falling back to memory store.');\n return getMemoryStore();\n }\n usingIdbStore = true;\n return idb;\n };\n\n store = storeType === 'idb' ? await getIdbStoreOrFallback() : getMemoryStore();\n\n // Beacon buffer: a sliding window of pending (unsent) event strings for synchronous\n // access on page exit. Uses an absolute index counter to correctly handle concurrent\n // async flushes without losing events added between the flush call and its resolution.\n const beaconBuffer: string[] = [];\n let beaconWindowStart = 0; // absolute index of the first element in beaconBuffer\n\n const advanceBeaconWindow = (upToAbsoluteIdx: number) => {\n if (upToAbsoluteIdx <= beaconWindowStart) return;\n const trimCount = Math.min(upToAbsoluteIdx - beaconWindowStart, beaconBuffer.length);\n if (trimCount > 0) {\n beaconBuffer.splice(0, trimCount);\n beaconWindowStart = upToAbsoluteIdx;\n }\n };\n\n /**\n * Immediately sends events to the track destination.\n */\n const sendEventsList = ({\n events: rawEvents,\n sessionId,\n deviceId,\n sequenceId,\n }: {\n events: string[];\n sessionId: string | number;\n deviceId: string;\n sequenceId?: number;\n }) => {\n // Backstop for events that entered IDB before the per-event size guard in\n // addCompressedEventToManager (e.g. stored by a previous SDK version or via\n // storeCurrentSequence/sendStoredEvents which bypass the capture-time check).\n // Compare UTF-8 byte size, not JS char count, to match the server-side limit.\n let events = rawEvents;\n const oversizedSizes: number[] = [];\n for (let i = 0; i < rawEvents.length; i++) {\n const bytes = utf8ByteLengthIfOver(rawEvents[i], maxSingleEventSize);\n if (bytes === undefined) {\n if (events !== rawEvents) events.push(rawEvents[i]);\n continue;\n }\n if (oversizedSizes.length === 0) {\n events = rawEvents.slice(0, i);\n }\n oversizedSizes.push(bytes);\n }\n if (oversizedSizes.length > 0) {\n config.loggerProvider.warn(\n `Dropping ${\n oversizedSizes.length\n } oversized event(s) from session replay sequence before send. Sizes: ${oversizedSizes\n .map((bytes) => `${Math.round(bytes / 1024)} KB`)\n .join(\n ', ',\n )}. If this recurs, please open a GitHub issue at https://github.com/amplitude/Amplitude-TypeScript/issues or contact Amplitude support.`,\n );\n }\n if (events.length === 0) {\n store.cleanUpSessionEventsStore(sessionId, sequenceId).catch((e) => {\n config.loggerProvider.warn('Failed to clean up session replay events store:', e);\n });\n return;\n }\n\n if (config.debugMode) {\n getStorageSize()\n .then(({ totalStorageSize, percentOfQuota, usageDetails }) => {\n config.loggerProvider.debug(\n `Total storage size: ${totalStorageSize} KB, percentage of quota: ${percentOfQuota}%, usage details: ${usageDetails}`,\n );\n })\n .catch(() => {\n // swallow error\n });\n }\n\n trackDestination.sendEventsList({\n events: events,\n sessionId: sessionId,\n flushMaxRetries: config.flushMaxRetries,\n apiKey: config.apiKey,\n deviceId: deviceId,\n sampleRate: config.sampleRate,\n serverZone: config.serverZone,\n version: config.version,\n type,\n onComplete: async () => {\n await store.cleanUpSessionEventsStore(sessionId, sequenceId);\n return;\n },\n });\n };\n\n const sendCurrentSequenceEvents = ({ sessionId, deviceId }: { sessionId: number; deviceId: string }) => {\n lastKnownDeviceId = deviceId;\n // Evaluate shouldSend synchronously before the async store read. asyncSetSessionId\n // updates sessionStartTime immediately after calling sendEvents(), so by the time\n // storeCurrentSequence resolves the start time would reflect the new session and\n // the elapsed check would compute ~0ms, silently dropping the previous session's events.\n if (shouldSend && !shouldSend()) return;\n // Snapshot the absolute end-index before the async store read so that any events\n // pushed after this point are NOT considered sent and remain in the beacon buffer.\n const snapshotAbsIdx = beaconWindowStart + beaconBuffer.length;\n store\n .storeCurrentSequence(sessionId)\n .then((currentSequence) => {\n if (currentSequence) {\n advanceBeaconWindow(snapshotAbsIdx);\n sendEventsList({\n sequenceId: currentSequence.sequenceId,\n events: currentSequence.events,\n sessionId: currentSequence.sessionId,\n deviceId,\n });\n }\n })\n .catch((e) => {\n config.loggerProvider.warn('Failed to get current sequence of session replay events for session:', e);\n });\n };\n\n const sendStoredEvents = async ({ deviceId }: { deviceId: string }) => {\n lastKnownDeviceId = deviceId;\n const sequencesToSend = await store.getSequencesToSend();\n if (!sequencesToSend?.length) {\n return;\n }\n config.loggerProvider.log(`Draining ${sequencesToSend.length} stored sequence(s) from previous session.`);\n // These persisted sequences are about to be enqueued back-to-back. Without\n // coalescing they flush as N separate POSTs — a request flood on page load. Mark the\n // imminent flush to merge same-identity batches (the enqueues below are synchronous, so\n // the whole backlog lands in the queue before the deferred flush consumes the flag). Skip\n // the single-sequence case where there's nothing to merge.\n if (sequencesToSend.length > 1) {\n trackDestination.markCoalesceNextFlush();\n }\n sequencesToSend.forEach((sequence) => {\n sendEventsList({\n sequenceId: sequence.sequenceId,\n events: sequence.events,\n sessionId: sequence.sessionId,\n deviceId,\n });\n });\n };\n\n const addEvent = ({\n event,\n sessionId,\n deviceId,\n }: {\n event: { type: Type; data: string };\n sessionId: number;\n deviceId: string;\n }) => {\n lastKnownDeviceId = deviceId;\n // Capture shouldSend synchronously before the async store write, for the same\n // reason as sendCurrentSequenceEvents: asyncSetSessionId updates sessionStartTime\n // synchronously, so evaluating inside the .then() would use the new session's\n // start time and compute ~0ms elapsed, silently dropping a valid batch.\n const canSend = !shouldSend || shouldSend();\n // Record the absolute index of this event in the beacon buffer before the async\n // store operation. If a batch split occurs, we advance the window up to (but not\n // including) this event so that it starts the next pending window.\n const absIdx = beaconWindowStart + beaconBuffer.length;\n beaconBuffer.push(event.data);\n store\n .addEventToCurrentSequence(sessionId, event.data)\n .then((sequenceToSend) => {\n if (sequenceToSend) {\n if (!canSend) {\n // The split atomically moved events to sequencesToSend; without cleanup\n // they would be unconditionally replayed on next page load via\n // sendStoredEvents, bypassing the min session duration gate. The split\n // batch must also be dropped from the beacon buffer so it isn't sent\n // via sendBeacon on page unload (potentially attributed to a later session).\n store.cleanUpSessionEventsStore(sequenceToSend.sessionId, sequenceToSend.sequenceId).catch((e) => {\n config.loggerProvider.warn('Failed to clean up dropped session replay sequence:', e);\n });\n advanceBeaconWindow(absIdx);\n return;\n }\n // Events before absIdx belong to the split batch being sent; advance window.\n advanceBeaconWindow(absIdx);\n sendEventsList({\n sequenceId: sequenceToSend.sequenceId,\n events: sequenceToSend.events,\n sessionId: sequenceToSend.sessionId,\n deviceId,\n });\n }\n })\n .catch((e) => {\n config.loggerProvider.warn('Failed to add event to session replay capture:', e);\n });\n };\n\n async function flush(useRetry = false) {\n return trackDestination.flush(useRetry);\n }\n\n const getBeaconEvents = (): string[] => [...beaconBuffer];\n\n const dropPendingBeaconEvents = () => {\n advanceBeaconWindow(beaconWindowStart + beaconBuffer.length);\n };\n\n return {\n sendCurrentSequenceEvents,\n addEvent,\n sendStoredEvents,\n flush,\n getBeaconEvents,\n dropPendingBeaconEvents,\n trackDestination,\n };\n};\n"]}
|
package/lib/cjs/helpers.d.ts
CHANGED
|
@@ -1,21 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SessionReplayJoinedConfig, UGCFilterRule } from './config/types';
|
|
2
2
|
import { StorageData } from './typings/session-replay';
|
|
3
3
|
export { getServerUrl } from './utils/server-url';
|
|
4
|
-
/**
|
|
5
|
-
* Returns the effective mask level for a given URL by checking `urlMaskLevels`
|
|
6
|
-
* (first match wins) and falling back to `defaultMaskLevel`.
|
|
7
|
-
*/
|
|
8
|
-
export declare const getEffectiveMaskLevel: (url: string | undefined, config: PrivacyConfig) => MaskLevel;
|
|
9
|
-
/**
|
|
10
|
-
* Checks if the given element set to be masked by rrweb
|
|
11
|
-
*
|
|
12
|
-
* Priority is:
|
|
13
|
-
* 1. [In code] Element/class based masking/unmasking <> [Config based] Selector based masking/unmasking
|
|
14
|
-
* 2. Use app defaults
|
|
15
|
-
*/
|
|
16
|
-
export declare const isMasked: (elementType: 'input' | 'text', config: PrivacyConfig | undefined, element: HTMLElement | null, currentUrl?: string) => boolean;
|
|
17
|
-
export declare const maskFn: (elementType: 'text' | 'input', config?: PrivacyConfig, getCurrentUrl?: () => string) => (text: string, element: HTMLElement | null) => string;
|
|
18
|
-
export declare const maskAttributeFn: (config?: PrivacyConfig, getCurrentUrl?: () => string) => (key: string, value: string, element: HTMLElement) => string;
|
|
19
4
|
export declare const getCurrentUrl: () => string;
|
|
20
5
|
export declare const generateSessionReplayId: (sessionId: string | number, deviceId: string) => string;
|
|
21
6
|
export declare const validateUGCFilterRules: (ugcFilterRules: UGCFilterRule[]) => void;
|
package/lib/cjs/helpers.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/helpers.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/helpers.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE1E,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAQlD,eAAO,MAAM,aAAa,cAGzB,CAAC;AAEF,eAAO,MAAM,uBAAuB,cAAe,MAAM,GAAG,MAAM,YAAY,MAAM,KAAG,MAEtF,CAAC;AASF,eAAO,MAAM,sBAAsB,mBAAoB,aAAa,EAAE,SAUrE,CAAC;AAEF,eAAO,MAAM,UAAU,YAAa,MAAM,kBAAkB,aAAa,EAAE,WAW1E,CAAC;AAEF,eAAO,MAAM,cAAc,QAAa,QAAQ,WAAW,CAa1D,CAAC;AAEF,eAAO,MAAM,cAAc,WAAY,yBAAyB,KAAG,yBAOlE,CAAC"}
|