@amplitude/session-replay-browser 1.51.0 → 1.51.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/lib/cjs/events/base-events-store.d.ts +8 -5
  2. package/lib/cjs/events/base-events-store.d.ts.map +1 -1
  3. package/lib/cjs/events/base-events-store.js +51 -55
  4. package/lib/cjs/events/base-events-store.js.map +1 -1
  5. package/lib/cjs/events/event-compressor.d.ts.map +1 -1
  6. package/lib/cjs/events/event-compressor.js +4 -2
  7. package/lib/cjs/events/event-compressor.js.map +1 -1
  8. package/lib/cjs/events/events-idb-store.d.ts.map +1 -1
  9. package/lib/cjs/events/events-idb-store.js +23 -8
  10. package/lib/cjs/events/events-idb-store.js.map +1 -1
  11. package/lib/cjs/events/events-manager.d.ts.map +1 -1
  12. package/lib/cjs/events/events-manager.js +18 -6
  13. package/lib/cjs/events/events-manager.js.map +1 -1
  14. package/lib/cjs/observers.d.ts.map +1 -1
  15. package/lib/cjs/observers.js +2 -22
  16. package/lib/cjs/observers.js.map +1 -1
  17. package/lib/cjs/track-destination.d.ts.map +1 -1
  18. package/lib/cjs/track-destination.js +6 -5
  19. package/lib/cjs/track-destination.js.map +1 -1
  20. package/lib/cjs/utils/utf8-byte-length.d.ts +17 -0
  21. package/lib/cjs/utils/utf8-byte-length.d.ts.map +1 -0
  22. package/lib/cjs/utils/utf8-byte-length.js +104 -0
  23. package/lib/cjs/utils/utf8-byte-length.js.map +1 -0
  24. package/lib/cjs/version.d.ts +1 -1
  25. package/lib/cjs/version.js +1 -1
  26. package/lib/cjs/version.js.map +1 -1
  27. package/lib/cjs/worker/index.js +1 -1
  28. package/lib/esm/events/base-events-store.d.ts +8 -5
  29. package/lib/esm/events/base-events-store.d.ts.map +1 -1
  30. package/lib/esm/events/base-events-store.js +51 -55
  31. package/lib/esm/events/base-events-store.js.map +1 -1
  32. package/lib/esm/events/event-compressor.d.ts.map +1 -1
  33. package/lib/esm/events/event-compressor.js +4 -2
  34. package/lib/esm/events/event-compressor.js.map +1 -1
  35. package/lib/esm/events/events-idb-store.d.ts.map +1 -1
  36. package/lib/esm/events/events-idb-store.js +23 -8
  37. package/lib/esm/events/events-idb-store.js.map +1 -1
  38. package/lib/esm/events/events-manager.d.ts.map +1 -1
  39. package/lib/esm/events/events-manager.js +18 -6
  40. package/lib/esm/events/events-manager.js.map +1 -1
  41. package/lib/esm/observers.d.ts.map +1 -1
  42. package/lib/esm/observers.js +2 -22
  43. package/lib/esm/observers.js.map +1 -1
  44. package/lib/esm/track-destination.d.ts.map +1 -1
  45. package/lib/esm/track-destination.js +6 -5
  46. package/lib/esm/track-destination.js.map +1 -1
  47. package/lib/esm/utils/utf8-byte-length.d.ts +17 -0
  48. package/lib/esm/utils/utf8-byte-length.d.ts.map +1 -0
  49. package/lib/esm/utils/utf8-byte-length.js +97 -0
  50. package/lib/esm/utils/utf8-byte-length.js.map +1 -0
  51. package/lib/esm/version.d.ts +1 -1
  52. package/lib/esm/version.js +1 -1
  53. package/lib/esm/version.js.map +1 -1
  54. package/lib/esm/worker/index.js +1 -1
  55. package/lib/scripts/index-min.js +1 -1
  56. package/lib/scripts/index-min.js.gz +0 -0
  57. package/lib/scripts/index-min.js.map +1 -1
  58. package/lib/scripts/observers-min.js +1 -1
  59. package/lib/scripts/observers-min.js.gz +0 -0
  60. package/lib/scripts/observers-min.js.map +1 -1
  61. package/lib/scripts/session-replay-browser-min.js +1 -1
  62. package/lib/scripts/session-replay-browser-min.js.gz +0 -0
  63. package/lib/scripts/session-replay-browser-min.js.map +1 -1
  64. package/lib/scripts/worker-min.js +1 -1
  65. package/lib/scripts/worker-min.js.gz +0 -0
  66. package/package.json +4 -4
@@ -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;AAG5D,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;4CAyQ3B,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"}
@@ -1,5 +1,6 @@
1
1
  import { __assign, __awaiter, __generator, __read, __spreadArray } from "tslib";
2
2
  import { MAX_SINGLE_EVENT_SIZE } from '../constants';
3
+ import { utf8ByteLengthIfOver } from '../utils/utf8-byte-length';
3
4
  import { getStorageSize } from '../helpers';
4
5
  import { SessionReplayTrackDestination } from '../track-destination';
5
6
  import { SessionReplayEventsIDBStore } from './events-idb-store';
@@ -113,14 +114,25 @@ export var createEventsManager = function (_a) {
113
114
  // addCompressedEventToManager (e.g. stored by a previous SDK version or via
114
115
  // storeCurrentSequence/sendStoredEvents which bypass the capture-time check).
115
116
  // Compare UTF-8 byte size, not JS char count, to match the server-side limit.
116
- var sizedEvents = rawEvents.map(function (e) { return ({ event: e, bytes: new Blob([e]).size }); });
117
- var oversized = sizedEvents.filter(function (s) { return s.bytes > maxSingleEventSize; });
118
- if (oversized.length > 0) {
119
- config.loggerProvider.warn("Dropping ".concat(oversized.length, " oversized event(s) from session replay sequence before send. Sizes: ").concat(oversized
120
- .map(function (s) { return "".concat(Math.round(s.bytes / 1024), " KB"); })
117
+ var events = rawEvents;
118
+ var oversizedSizes = [];
119
+ for (var i = 0; i < rawEvents.length; i++) {
120
+ var bytes = utf8ByteLengthIfOver(rawEvents[i], maxSingleEventSize);
121
+ if (bytes === undefined) {
122
+ if (events !== rawEvents)
123
+ events.push(rawEvents[i]);
124
+ continue;
125
+ }
126
+ if (oversizedSizes.length === 0) {
127
+ events = rawEvents.slice(0, i);
128
+ }
129
+ oversizedSizes.push(bytes);
130
+ }
131
+ if (oversizedSizes.length > 0) {
132
+ config.loggerProvider.warn("Dropping ".concat(oversizedSizes.length, " oversized event(s) from session replay sequence before send. Sizes: ").concat(oversizedSizes
133
+ .map(function (bytes) { return "".concat(Math.round(bytes / 1024), " KB"); })
121
134
  .join(', '), ". If this recurs, please open a GitHub issue at https://github.com/amplitude/Amplitude-TypeScript/issues or contact Amplitude support."));
122
135
  }
123
- var events = oversized.length > 0 ? sizedEvents.filter(function (s) { return s.bytes <= maxSingleEventSize; }).map(function (s) { return s.event; }) : rawEvents;
124
136
  if (events.length === 0) {
125
137
  store.cleanUpSessionEventsStore(sessionId, sequenceId).catch(function (e) {
126
138
  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"]}
@@ -1 +1 @@
1
- {"version":3,"file":"observers.d.ts","sourceRoot":"","sources":["../../src/observers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,qBAAqB,EAA+C,MAAM,wBAAwB,CAAC;AAEjH,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,WAAW,GAAG,gBAAgB,GAAG,OAAO,CAAC;AAEvF,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,KAAK,CAAC,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;AAExE,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,qBAAqB,CAAC;IAChC,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,iBAAiB,CAAC;CAC1B;AAkDD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,aAAa,CAAC,CAAuB;IAC7C,OAAO,CAAC,aAAa,CAAC,CAAgB;IAEtC,KAAK,CAAC,aAAa,EAAE,oBAAoB,EAAE,aAAa,CAAC,EAAE,aAAa;IAMxE,IAAI;IAOJ,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,mBAAmB;IAIhD,OAAO,CAAC,YAAY;CAkGrB"}
1
+ {"version":3,"file":"observers.d.ts","sourceRoot":"","sources":["../../src/observers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,qBAAqB,EAA+C,MAAM,wBAAwB,CAAC;AAGjH,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,WAAW,GAAG,gBAAgB,GAAG,OAAO,CAAC;AAEvF,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,KAAK,CAAC,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;AAExE,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,qBAAqB,CAAC;IAChC,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,iBAAiB,CAAC;CAC1B;AA8BD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,aAAa,CAAC,CAAuB;IAC7C,OAAO,CAAC,aAAa,CAAC,CAAgB;IAEtC,KAAK,CAAC,aAAa,EAAE,oBAAoB,EAAE,aAAa,CAAC,EAAE,aAAa;IAMxE,IAAI;IAOJ,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,mBAAmB;IAIhD,OAAO,CAAC,YAAY;CAkGrB"}
@@ -1,6 +1,7 @@
1
1
  import { __awaiter, __generator } from "tslib";
2
2
  import { getGlobalScope } from '@amplitude/analytics-core';
3
3
  import { captureSerializedBody, isBodyCaptureEnabled } from './network-body-capture';
4
+ import { truncateUtf8 } from './utils/utf8-byte-length';
4
5
  var DEFAULT_MAX_BODY_SIZE_BYTES = 10240; // 10KB
5
6
  var BINARY_CONTENT_TYPE_PREFIXES = ['image/', 'audio/', 'video/', 'application/octet-stream', 'font/'];
6
7
  function isBinaryContentType(contentType) {
@@ -26,28 +27,7 @@ function serializeRequestBody(body) {
26
27
  return undefined;
27
28
  }
28
29
  function truncateToByteLimit(str, maxBytes) {
29
- if (new Blob([str]).size <= maxBytes) {
30
- return { value: str, truncated: false };
31
- }
32
- // Binary search for the longest prefix whose UTF-8 byte length fits within maxBytes.
33
- // Cap hi at maxBytes since each UTF-8 character is at least 1 byte, so no more than
34
- // maxBytes characters can ever fit — this avoids large intermediate Blob allocations.
35
- var lo = 0;
36
- var hi = Math.min(str.length, maxBytes);
37
- while (lo < hi) {
38
- var mid = Math.ceil((lo + hi) / 2);
39
- if (new Blob([str.slice(0, mid)]).size <= maxBytes) {
40
- lo = mid;
41
- }
42
- else {
43
- hi = mid - 1;
44
- }
45
- }
46
- // Avoid splitting a surrogate pair: if lo landed after a high surrogate, back up one position
47
- if (lo > 0 && str.charCodeAt(lo - 1) >= 0xd800 && str.charCodeAt(lo - 1) <= 0xdbff) {
48
- lo -= 1;
49
- }
50
- return { value: str.slice(0, lo), truncated: true };
30
+ return truncateUtf8(str, maxBytes);
51
31
  }
52
32
  var NetworkObservers = /** @class */ (function () {
53
33
  function NetworkObservers() {
@@ -1 +1 @@
1
- {"version":3,"file":"observers.js","sourceRoot":"","sources":["../../src/observers.ts"],"names":[],"mappings":"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","sourcesContent":["import { getGlobalScope } from '@amplitude/analytics-core';\n\nimport { type BodyCaptureRuleConfig, captureSerializedBody, isBodyCaptureEnabled } from './network-body-capture';\n\nexport type ResponseBodyStatus = 'captured' | 'truncated' | 'skipped_binary' | 'error';\n\nexport interface NetworkRequestEvent {\n timestamp: number;\n type: 'fetch';\n method: string;\n url: string;\n status?: number;\n duration?: number;\n requestHeaders?: Record<string, string>;\n responseHeaders?: Record<string, string>;\n requestBody?: string;\n responseBody?: string;\n responseBodyStatus?: ResponseBodyStatus;\n error?: {\n name: string;\n message: string;\n };\n}\n\nexport type NetworkEventCallback = (event: NetworkRequestEvent) => void;\n\nexport interface NetworkBodyConfig {\n request?: BodyCaptureRuleConfig;\n response?: BodyCaptureRuleConfig;\n maxBodySizeBytes?: number;\n}\n\nexport interface NetworkConfig {\n enabled: boolean;\n body?: NetworkBodyConfig;\n}\n\nconst DEFAULT_MAX_BODY_SIZE_BYTES = 10240; // 10KB\n\nconst BINARY_CONTENT_TYPE_PREFIXES = ['image/', 'audio/', 'video/', 'application/octet-stream', 'font/'];\n\nfunction isBinaryContentType(contentType: string | null): boolean {\n if (!contentType) return false;\n return BINARY_CONTENT_TYPE_PREFIXES.some((prefix) => contentType.toLowerCase().startsWith(prefix));\n}\n\nfunction serializeRequestBody(body: BodyInit | null | undefined): string | undefined {\n if (body === null || body === undefined) return undefined;\n if (typeof body === 'string') return body;\n if (body instanceof URLSearchParams) return body.toString();\n if (body instanceof FormData) {\n const parts: string[] = [];\n body.forEach((value, key) => {\n parts.push(`${key}=${typeof value === 'string' ? value : '[File]'}`);\n });\n return parts.join('&');\n }\n // Blob, ArrayBuffer, ArrayBufferView, ReadableStream — skip\n return undefined;\n}\n\nfunction truncateToByteLimit(str: string, maxBytes: number): { value: string; truncated: boolean } {\n if (new Blob([str]).size <= maxBytes) {\n return { value: str, truncated: false };\n }\n // Binary search for the longest prefix whose UTF-8 byte length fits within maxBytes.\n // Cap hi at maxBytes since each UTF-8 character is at least 1 byte, so no more than\n // maxBytes characters can ever fit — this avoids large intermediate Blob allocations.\n let lo = 0;\n let hi = Math.min(str.length, maxBytes);\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (new Blob([str.slice(0, mid)]).size <= maxBytes) {\n lo = mid;\n } else {\n hi = mid - 1;\n }\n }\n // Avoid splitting a surrogate pair: if lo landed after a high surrogate, back up one position\n if (lo > 0 && str.charCodeAt(lo - 1) >= 0xd800 && str.charCodeAt(lo - 1) <= 0xdbff) {\n lo -= 1;\n }\n return { value: str.slice(0, lo), truncated: true };\n}\n\nexport class NetworkObservers {\n private fetchObserver: (() => void) | null = null;\n private eventCallback?: NetworkEventCallback;\n private networkConfig?: NetworkConfig;\n\n start(eventCallback: NetworkEventCallback, networkConfig?: NetworkConfig) {\n this.eventCallback = eventCallback;\n this.networkConfig = networkConfig;\n this.observeFetch();\n }\n\n stop() {\n this.fetchObserver?.();\n this.fetchObserver = null;\n this.eventCallback = undefined;\n this.networkConfig = undefined;\n }\n\n protected notifyEvent(event: NetworkRequestEvent) {\n this.eventCallback?.(event);\n }\n\n private observeFetch() {\n const globalScope = getGlobalScope();\n if (!globalScope) return;\n\n const originalFetch = globalScope.fetch;\n if (!originalFetch) return;\n\n globalScope.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {\n const startTime = Date.now();\n const requestEvent: NetworkRequestEvent = {\n timestamp: startTime,\n type: 'fetch',\n method: init?.method || 'GET', // Fetch API defaulted to GET when no method is provided\n url: input.toString(),\n requestHeaders: init?.headers as Record<string, string>,\n };\n\n // Capture request body if configured\n const bodyConfig = this.networkConfig?.body;\n if (bodyConfig && isBodyCaptureEnabled(bodyConfig.request)) {\n const serialized = serializeRequestBody(init?.body);\n if (serialized !== undefined) {\n const maxBytes = bodyConfig.maxBodySizeBytes ?? DEFAULT_MAX_BODY_SIZE_BYTES;\n requestEvent.requestBody = captureSerializedBody(\n serialized,\n bodyConfig.request,\n maxBytes,\n truncateToByteLimit,\n ).value;\n }\n }\n\n try {\n const response = await originalFetch(input, init);\n const endTime = Date.now();\n\n requestEvent.status = response.status;\n requestEvent.duration = endTime - startTime;\n\n // Convert Headers\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n headers[key] = value;\n });\n requestEvent.responseHeaders = headers;\n\n if (bodyConfig && isBodyCaptureEnabled(bodyConfig.response)) {\n const contentType = headers['content-type'] || null;\n if (isBinaryContentType(contentType)) {\n requestEvent.responseBodyStatus = 'skipped_binary';\n this.notifyEvent(requestEvent);\n } else {\n const cloned = response.clone();\n // Read body without blocking the response return to the caller\n cloned.text().then(\n (text) => {\n const maxBytes = bodyConfig.maxBodySizeBytes ?? DEFAULT_MAX_BODY_SIZE_BYTES;\n const { value, truncated } = captureSerializedBody(\n text,\n bodyConfig.response,\n maxBytes,\n truncateToByteLimit,\n );\n requestEvent.responseBody = value;\n requestEvent.responseBodyStatus = truncated ? 'truncated' : 'captured';\n this.notifyEvent(requestEvent);\n },\n () => {\n requestEvent.responseBodyStatus = 'error';\n this.notifyEvent(requestEvent);\n },\n );\n }\n } else {\n this.notifyEvent(requestEvent);\n }\n\n return response;\n } catch (error) {\n const endTime = Date.now();\n requestEvent.duration = endTime - startTime;\n\n // Capture error information\n const typedError = error as Error;\n requestEvent.error = {\n name: typedError.name || 'UnknownError',\n message: typedError.message || 'An unknown error occurred',\n };\n\n this.notifyEvent(requestEvent);\n throw error;\n }\n };\n\n this.fetchObserver = () => {\n globalScope.fetch = originalFetch;\n };\n }\n}\n"]}
1
+ {"version":3,"file":"observers.js","sourceRoot":"","sources":["../../src/observers.ts"],"names":[],"mappings":"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","sourcesContent":["import { getGlobalScope } from '@amplitude/analytics-core';\n\nimport { type BodyCaptureRuleConfig, captureSerializedBody, isBodyCaptureEnabled } from './network-body-capture';\nimport { truncateUtf8 } from './utils/utf8-byte-length';\n\nexport type ResponseBodyStatus = 'captured' | 'truncated' | 'skipped_binary' | 'error';\n\nexport interface NetworkRequestEvent {\n timestamp: number;\n type: 'fetch';\n method: string;\n url: string;\n status?: number;\n duration?: number;\n requestHeaders?: Record<string, string>;\n responseHeaders?: Record<string, string>;\n requestBody?: string;\n responseBody?: string;\n responseBodyStatus?: ResponseBodyStatus;\n error?: {\n name: string;\n message: string;\n };\n}\n\nexport type NetworkEventCallback = (event: NetworkRequestEvent) => void;\n\nexport interface NetworkBodyConfig {\n request?: BodyCaptureRuleConfig;\n response?: BodyCaptureRuleConfig;\n maxBodySizeBytes?: number;\n}\n\nexport interface NetworkConfig {\n enabled: boolean;\n body?: NetworkBodyConfig;\n}\n\nconst DEFAULT_MAX_BODY_SIZE_BYTES = 10240; // 10KB\n\nconst BINARY_CONTENT_TYPE_PREFIXES = ['image/', 'audio/', 'video/', 'application/octet-stream', 'font/'];\n\nfunction isBinaryContentType(contentType: string | null): boolean {\n if (!contentType) return false;\n return BINARY_CONTENT_TYPE_PREFIXES.some((prefix) => contentType.toLowerCase().startsWith(prefix));\n}\n\nfunction serializeRequestBody(body: BodyInit | null | undefined): string | undefined {\n if (body === null || body === undefined) return undefined;\n if (typeof body === 'string') return body;\n if (body instanceof URLSearchParams) return body.toString();\n if (body instanceof FormData) {\n const parts: string[] = [];\n body.forEach((value, key) => {\n parts.push(`${key}=${typeof value === 'string' ? value : '[File]'}`);\n });\n return parts.join('&');\n }\n // Blob, ArrayBuffer, ArrayBufferView, ReadableStream — skip\n return undefined;\n}\n\nfunction truncateToByteLimit(str: string, maxBytes: number): { value: string; truncated: boolean } {\n return truncateUtf8(str, maxBytes);\n}\n\nexport class NetworkObservers {\n private fetchObserver: (() => void) | null = null;\n private eventCallback?: NetworkEventCallback;\n private networkConfig?: NetworkConfig;\n\n start(eventCallback: NetworkEventCallback, networkConfig?: NetworkConfig) {\n this.eventCallback = eventCallback;\n this.networkConfig = networkConfig;\n this.observeFetch();\n }\n\n stop() {\n this.fetchObserver?.();\n this.fetchObserver = null;\n this.eventCallback = undefined;\n this.networkConfig = undefined;\n }\n\n protected notifyEvent(event: NetworkRequestEvent) {\n this.eventCallback?.(event);\n }\n\n private observeFetch() {\n const globalScope = getGlobalScope();\n if (!globalScope) return;\n\n const originalFetch = globalScope.fetch;\n if (!originalFetch) return;\n\n globalScope.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {\n const startTime = Date.now();\n const requestEvent: NetworkRequestEvent = {\n timestamp: startTime,\n type: 'fetch',\n method: init?.method || 'GET', // Fetch API defaulted to GET when no method is provided\n url: input.toString(),\n requestHeaders: init?.headers as Record<string, string>,\n };\n\n // Capture request body if configured\n const bodyConfig = this.networkConfig?.body;\n if (bodyConfig && isBodyCaptureEnabled(bodyConfig.request)) {\n const serialized = serializeRequestBody(init?.body);\n if (serialized !== undefined) {\n const maxBytes = bodyConfig.maxBodySizeBytes ?? DEFAULT_MAX_BODY_SIZE_BYTES;\n requestEvent.requestBody = captureSerializedBody(\n serialized,\n bodyConfig.request,\n maxBytes,\n truncateToByteLimit,\n ).value;\n }\n }\n\n try {\n const response = await originalFetch(input, init);\n const endTime = Date.now();\n\n requestEvent.status = response.status;\n requestEvent.duration = endTime - startTime;\n\n // Convert Headers\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n headers[key] = value;\n });\n requestEvent.responseHeaders = headers;\n\n if (bodyConfig && isBodyCaptureEnabled(bodyConfig.response)) {\n const contentType = headers['content-type'] || null;\n if (isBinaryContentType(contentType)) {\n requestEvent.responseBodyStatus = 'skipped_binary';\n this.notifyEvent(requestEvent);\n } else {\n const cloned = response.clone();\n // Read body without blocking the response return to the caller\n cloned.text().then(\n (text) => {\n const maxBytes = bodyConfig.maxBodySizeBytes ?? DEFAULT_MAX_BODY_SIZE_BYTES;\n const { value, truncated } = captureSerializedBody(\n text,\n bodyConfig.response,\n maxBytes,\n truncateToByteLimit,\n );\n requestEvent.responseBody = value;\n requestEvent.responseBodyStatus = truncated ? 'truncated' : 'captured';\n this.notifyEvent(requestEvent);\n },\n () => {\n requestEvent.responseBodyStatus = 'error';\n this.notifyEvent(requestEvent);\n },\n );\n }\n } else {\n this.notifyEvent(requestEvent);\n }\n\n return response;\n } catch (error) {\n const endTime = Date.now();\n requestEvent.duration = endTime - startTime;\n\n // Capture error information\n const typedError = error as Error;\n requestEvent.error = {\n name: typedError.name || 'UnknownError',\n message: typedError.message || 'An unknown error occurred',\n };\n\n this.notifyEvent(requestEvent);\n throw error;\n }\n };\n\n this.fetchObserver = () => {\n globalScope.fetch = originalFetch;\n };\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"track-destination.d.ts","sourceRoot":"","sources":["../../src/track-destination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,OAAO,EAAE,UAAU,EAAU,MAAM,2BAA2B,CAAC;AAUvG,OAAO,EACL,6BAA6B,IAAI,sCAAsC,EACvE,wBAAwB,EACxB,+BAA+B,EAChC,MAAM,0BAA0B,CAAC;AAgBlC,OAAO,EAAE,8BAA8B,EAAE,MAAM,gBAAgB,CAAC;AAuChE,MAAM,MAAM,cAAc,GAAG,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,KAAK;IAC3F,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,EAAE,CAAC;CACnB,CAAC;AAuCF,qBAAa,6BAA8B,YAAW,sCAAsC;IAC1F,cAAc,EAAE,OAAO,CAAC;IACxB,UAAU,SAAM;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,SAAQ;IAIpB,OAAO,CAAC,0BAA0B,CAAU;IAI5C,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,SAAS,CAA8C;IAC/D,cAAc,EAAE,cAAc,CAAC;IAC/B,KAAK,EAAE,+BAA+B,EAAE,CAAM;IAC9C,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,qBAAqB,CAGzB;IAOJ,OAAO,CAAC,sBAAsB,CAAsD;IAIpF,OAAO,CAAC,iBAAiB,CAAK;IAI9B,OAAO,CAAC,gBAAgB,CAAS;IAIjC,OAAO,CAAC,iBAAiB,CAAS;IAGlC,OAAO,CAAC,sBAAsB,CAAS;IACvC,OAAO,CAAC,cAAc,CAA8B;IAIpD,OAAO,CAAC,gBAAgB,CAAC,CAAiC;gBAE9C,EACV,cAAc,EACd,cAAc,EACd,cAAc,EACd,YAAY,EACZ,0BAA0B,EAC1B,aAAa,EACb,gBAAgB,GACjB,EAAE;QACD,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,cAAc,EAAE,OAAO,CAAC;QACxB,cAAc,CAAC,EAAE,cAAc,CAAC;QAChC,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,0BAA0B,CAAC,EAAE,OAAO,CAAC;QACrC,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,gBAAgB,CAAC,EAAE,8BAA8B,CAAC;KACnD;IAmGD,cAAc,CAAC,eAAe,EAAE,wBAAwB;IAQxD;;;;;;;;;;;;;OAaG;IACH,qBAAqB;IAKrB;;;;;;OAMG;IACH,UAAU,CAAC,EACT,MAAM,EACN,SAAS,EACT,QAAQ,EACR,MAAM,EACN,UAAU,GACX,EAAE;QACD,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;QAC3B,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,OAAO,UAAU,CAAC;KACtC;IAkFD,UAAU,CAAC,GAAG,IAAI,EAAE,+BAA+B,EAAE;IA2BrD,QAAQ,CAAC,OAAO,EAAE,MAAM;IAqBlB,KAAK,CAAC,QAAQ,UAAQ;IAyB5B;;;;OAIG;IACH,OAAO,CAAC,uBAAuB;IAW/B;;;;;;;OAOG;IACH,OAAO,CAAC,iBAAiB;IAUzB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,kBAAkB;IAwEpB,IAAI,CAAC,OAAO,EAAE,+BAA+B,EAAE,QAAQ,UAAO;YAkCtD,aAAa;IAgE3B,OAAO,CAAC,uBAAuB;YAiBjB,oBAAoB;YA+CpB,gBAAgB;IA8IxB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,+BAA+B,EAAE,YAAY,SAAK;IAwB/F,6BAA6B,CAAC,OAAO,EAAE,+BAA+B,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAuC7F,qBAAqB,CAAC,OAAO,EAAE,+BAA+B;IAQxD,mBAAmB,CAAC,OAAO,EAAE,+BAA+B;IAWlE,eAAe,CAAC,EACd,OAAO,EACP,GAAG,EACH,OAAO,GACR,EAAE;QACD,OAAO,EAAE,+BAA+B,CAAC;QACzC,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB;IASD;;;;;;;;;;OAUG;IACH,OAAO,CAAC,oBAAoB;IA0B5B,OAAO,CAAC,WAAW;CAyBpB"}
1
+ {"version":3,"file":"track-destination.d.ts","sourceRoot":"","sources":["../../src/track-destination.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,7 @@ import { MAX_RETRIES_EXCEEDED_MESSAGE, MISSING_API_KEY_MESSAGE, MISSING_DEVICE_I
5
5
  import { VERSION } from './version';
6
6
  import { MAX_URL_LENGTH, KB_SIZE, MAX_KEEPALIVE_BYTES, WAF_PAYLOAD_TOO_LARGE_PATTERN, EVENT_SKIPPED_HEADER, EVENT_SKIP_CODE_THROTTLED, EVENT_SKIP_CODE_INVALID_RANGE, EVENT_SKIP_CODE_CAPTURE_DISABLED, THROTTLED_FLUSH_PAUSE_MS, MERGE_AFTER_THROTTLE_SOFT_CAP, SEND_TIMEOUT_MS, } from './constants';
7
7
  import { gzipJson } from './utils/gzip';
8
+ import { utf8ByteLength, utf8ByteLengthOf } from './utils/utf8-byte-length';
8
9
  // Bounded so a long-lived SDK instance can't accumulate kill records indefinitely;
9
10
  // sessions are time-bounded in practice, this cap is just a defensive ceiling.
10
11
  var MAX_KILLED_SESSIONS = 256;
@@ -217,7 +218,7 @@ var SessionReplayTrackDestination = /** @class */ (function () {
217
218
  var _b, _c;
218
219
  var events = _a.events, sessionId = _a.sessionId, deviceId = _a.deviceId, apiKey = _a.apiKey, serverZone = _a.serverZone;
219
220
  var MAX_BEACON_BYTES = 64 * 1024;
220
- var byteLength = function (s) { return new Blob([s]).size; };
221
+ var byteLength = function (s) { return utf8ByteLength(s); };
221
222
  var trimmedEvents = events;
222
223
  var payload = JSON.stringify({ version: 2, events: trimmedEvents });
223
224
  if (byteLength(payload) > MAX_BEACON_BYTES) {
@@ -495,9 +496,9 @@ var SessionReplayTrackDestination = /** @class */ (function () {
495
496
  };
496
497
  var _loop_2 = function (ctx) {
497
498
  // UTF-8 byte size, matching how the events store enforces MAX_EVENT_LIST_SIZE
498
- // (see base-events-store.ts:getStringSize). Using char length would let a CJK/
499
+ // (see utils/utf8-byte-length.ts). Using char length would let a CJK/
499
500
  // emoji-heavy payload sneak past the cap.
500
- var ctxBytes = ctx.events.reduce(function (sum, e) { return sum + new Blob([e]).size; }, 0);
501
+ var ctxBytes = utf8ByteLengthOf(ctx.events);
501
502
  if (current === null) {
502
503
  // Reset attempts to 0 on the merged context so the post-throttle delivery gets a
503
504
  // full retry budget. The throttle pause has already absorbed back-pressure; the
@@ -788,7 +789,7 @@ var SessionReplayTrackDestination = /** @class */ (function () {
788
789
  _l.label = 4;
789
790
  case 4:
790
791
  gzipped = _h;
791
- payloadSize = gzipped ? gzipped.byteLength : new Blob([payloadJson]).size;
792
+ payloadSize = gzipped ? gzipped.byteLength : utf8ByteLength(payloadJson);
792
793
  controller_1 = new AbortController();
793
794
  headers = __assign({ 'Content-Type': 'application/json', Accept: '*/*', Authorization: "Bearer ".concat(apiKey), 'X-Client-Version': version, 'X-Client-Library': sessionReplayLibrary, 'X-Client-Url': url.substring(0, MAX_URL_LENGTH), 'X-Client-Sample-Rate': "".concat(sampleRate), 'X-Sampling-Hash-Alg': 'xxhash32' }, (gzipped ? { 'Content-Encoding': 'gzip' } : {}));
794
795
  body = gzipped !== null && gzipped !== void 0 ? gzipped : payloadJson;
@@ -973,7 +974,7 @@ var SessionReplayTrackDestination = /** @class */ (function () {
973
974
  this.sendEventsList(__assign(__assign({}, context), { events: context.events.slice(mid), onComplete: noop }));
974
975
  };
975
976
  SessionReplayTrackDestination.prototype.handleSuccessResponse = function (context) {
976
- var sizeOfEventsList = Math.round(new Blob(context.events).size / KB_SIZE);
977
+ var sizeOfEventsList = Math.round(utf8ByteLengthOf(context.events) / KB_SIZE);
977
978
  this.completeRequest({
978
979
  context: context,
979
980
  success: "Session replay event batch tracked successfully for session id ".concat(context.sessionId, ", size of events: ").concat(sizeOfEventsList, " KB"),