@amplitude/session-replay-browser 1.44.0 → 1.45.0-sr-4646-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/lib/cjs/config/local-config.d.ts +4 -0
  2. package/lib/cjs/config/local-config.d.ts.map +1 -1
  3. package/lib/cjs/config/local-config.js +39 -2
  4. package/lib/cjs/config/local-config.js.map +1 -1
  5. package/lib/cjs/config/types.d.ts +52 -0
  6. package/lib/cjs/config/types.d.ts.map +1 -1
  7. package/lib/cjs/config/types.js.map +1 -1
  8. package/lib/cjs/constants.d.ts +1 -1
  9. package/lib/cjs/constants.d.ts.map +1 -1
  10. package/lib/cjs/constants.js +11 -4
  11. package/lib/cjs/constants.js.map +1 -1
  12. package/lib/cjs/events/base-events-store.js +2 -2
  13. package/lib/cjs/events/base-events-store.js.map +1 -1
  14. package/lib/cjs/events/event-compressor.js +2 -1
  15. package/lib/cjs/events/event-compressor.js.map +1 -1
  16. package/lib/cjs/events/events-manager.d.ts +2 -1
  17. package/lib/cjs/events/events-manager.d.ts.map +1 -1
  18. package/lib/cjs/events/events-manager.js +12 -10
  19. package/lib/cjs/events/events-manager.js.map +1 -1
  20. package/lib/cjs/events/events-memory-store.js +1 -1
  21. package/lib/cjs/events/events-memory-store.js.map +1 -1
  22. package/lib/cjs/session-replay.d.ts.map +1 -1
  23. package/lib/cjs/session-replay.js +5 -2
  24. package/lib/cjs/session-replay.js.map +1 -1
  25. package/lib/cjs/track-destination.d.ts +3 -1
  26. package/lib/cjs/track-destination.d.ts.map +1 -1
  27. package/lib/cjs/track-destination.js +4 -2
  28. package/lib/cjs/track-destination.js.map +1 -1
  29. package/lib/cjs/version.d.ts +1 -1
  30. package/lib/cjs/version.d.ts.map +1 -1
  31. package/lib/cjs/version.js +1 -1
  32. package/lib/cjs/version.js.map +1 -1
  33. package/lib/cjs/worker/index.js +1 -1
  34. package/lib/esm/config/local-config.d.ts +4 -0
  35. package/lib/esm/config/local-config.d.ts.map +1 -1
  36. package/lib/esm/config/local-config.js +39 -2
  37. package/lib/esm/config/local-config.js.map +1 -1
  38. package/lib/esm/config/types.d.ts +52 -0
  39. package/lib/esm/config/types.d.ts.map +1 -1
  40. package/lib/esm/config/types.js.map +1 -1
  41. package/lib/esm/constants.d.ts +1 -1
  42. package/lib/esm/constants.d.ts.map +1 -1
  43. package/lib/esm/constants.js +11 -4
  44. package/lib/esm/constants.js.map +1 -1
  45. package/lib/esm/events/base-events-store.js +2 -2
  46. package/lib/esm/events/base-events-store.js.map +1 -1
  47. package/lib/esm/events/event-compressor.js +2 -1
  48. package/lib/esm/events/event-compressor.js.map +1 -1
  49. package/lib/esm/events/events-manager.d.ts +2 -1
  50. package/lib/esm/events/events-manager.d.ts.map +1 -1
  51. package/lib/esm/events/events-manager.js +12 -10
  52. package/lib/esm/events/events-manager.js.map +1 -1
  53. package/lib/esm/events/events-memory-store.js +1 -1
  54. package/lib/esm/events/events-memory-store.js.map +1 -1
  55. package/lib/esm/session-replay.d.ts.map +1 -1
  56. package/lib/esm/session-replay.js +5 -2
  57. package/lib/esm/session-replay.js.map +1 -1
  58. package/lib/esm/track-destination.d.ts +3 -1
  59. package/lib/esm/track-destination.d.ts.map +1 -1
  60. package/lib/esm/track-destination.js +4 -2
  61. package/lib/esm/track-destination.js.map +1 -1
  62. package/lib/esm/version.d.ts +1 -1
  63. package/lib/esm/version.d.ts.map +1 -1
  64. package/lib/esm/version.js +1 -1
  65. package/lib/esm/version.js.map +1 -1
  66. package/lib/esm/worker/index.js +1 -1
  67. package/lib/scripts/index-min.js +1 -1
  68. package/lib/scripts/index-min.js.gz +0 -0
  69. package/lib/scripts/index-min.js.map +1 -1
  70. package/lib/scripts/session-replay-browser-min.js +1 -1
  71. package/lib/scripts/session-replay-browser-min.js.gz +0 -0
  72. package/lib/scripts/session-replay-browser-min.js.map +1 -1
  73. package/lib/scripts/worker-min.js +1 -1
  74. package/lib/scripts/worker-min.js.gz +0 -0
  75. package/package.json +3 -3
@@ -20,6 +20,7 @@ export declare class SessionReplayLocalConfig extends Config implements ISession
20
20
  storeType: StoreType;
21
21
  performanceConfig?: SessionReplayPerformanceConfig;
22
22
  useWebWorker?: boolean;
23
+ enableTransportCompression?: boolean;
23
24
  applyBackgroundColorToBlockedElements?: boolean;
24
25
  enableUrlChangePolling?: boolean;
25
26
  urlChangePollingInterval?: number;
@@ -28,6 +29,9 @@ export declare class SessionReplayLocalConfig extends Config implements ISession
28
29
  crossOriginIframes?: CrossOriginIframesConfig;
29
30
  fullSnapshotIntervalMs?: number;
30
31
  flushIntervalConfig?: FlushIntervalConfig;
32
+ eagerFullSnapshotSend?: boolean;
33
+ maxPersistedEventsSizeBytes?: number;
34
+ maxSingleEventSizeBytes?: number;
31
35
  constructor(apiKey: string, options: SessionReplayOptions);
32
36
  }
33
37
  //# sourceMappingURL=local-config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"local-config.d.ts","sourceRoot":"","sources":["../../../src/config/local-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAW,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAU9F,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC5E,OAAO,EACL,wBAAwB,IAAI,yBAAyB,EACrD,wBAAwB,EACxB,mBAAmB,EACnB,iBAAiB,EACjB,aAAa,EACb,8BAA8B,EAC9B,oBAAoB,EACrB,MAAM,SAAS,CAAC;AAIjB,eAAO,MAAM,gBAAgB;;;;;CAK3B,CAAC;AAEH,qBAAa,wBAAyB,SAAQ,MAAO,YAAW,yBAAyB;IACvF,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,SAAS,CAAC;IACrB,iBAAiB,CAAC,EAAE,8BAA8B,CAAC;IACnD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,qCAAqC,CAAC,EAAE,OAAO,CAAC;IAChD,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,kBAAkB,CAAC,EAAE,wBAAwB,CAAC;IAC9C,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;gBAE9B,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB;CAgE1D"}
1
+ {"version":3,"file":"local-config.d.ts","sourceRoot":"","sources":["../../../src/config/local-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAW,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAU9F,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC5E,OAAO,EACL,wBAAwB,IAAI,yBAAyB,EACrD,wBAAwB,EACxB,mBAAmB,EACnB,iBAAiB,EACjB,aAAa,EACb,8BAA8B,EAC9B,oBAAoB,EACrB,MAAM,SAAS,CAAC;AAIjB,eAAO,MAAM,gBAAgB;;;;;CAK3B,CAAC;AAEH,qBAAa,wBAAyB,SAAQ,MAAO,YAAW,yBAAyB;IACvF,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,SAAS,CAAC;IACrB,iBAAiB,CAAC,EAAE,8BAA8B,CAAC;IACnD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,qCAAqC,CAAC,EAAE,OAAO,CAAC;IAChD,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,kBAAkB,CAAC,EAAE,wBAAwB,CAAC;IAC9C,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,uBAAuB,CAAC,EAAE,MAAM,CAAC;gBAErB,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB;CAsF1D"}
@@ -17,7 +17,7 @@ var SessionReplayLocalConfig = /** @class */ (function (_super) {
17
17
  tslib_1.__extends(SessionReplayLocalConfig, _super);
18
18
  function SessionReplayLocalConfig(apiKey, options) {
19
19
  var _this = this;
20
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
20
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
21
21
  var defaultConfig = (0, exports.getDefaultConfig)();
22
22
  _this = _super.call(this, tslib_1.__assign(tslib_1.__assign({ transportProvider: defaultConfig.transportProvider, loggerProvider: new logger_1.SafeLoggerProvider(options.loggerProvider || defaultConfig.loggerProvider) }, options), { apiKey: apiKey })) || this;
23
23
  _this.flushMaxRetries =
@@ -40,6 +40,15 @@ var SessionReplayLocalConfig = /** @class */ (function (_super) {
40
40
  if (options.fullSnapshotIntervalMs !== undefined) {
41
41
  _this.fullSnapshotIntervalMs = options.fullSnapshotIntervalMs;
42
42
  }
43
+ if (options.eagerFullSnapshotSend !== undefined) {
44
+ _this.eagerFullSnapshotSend = options.eagerFullSnapshotSend;
45
+ }
46
+ if (options.maxPersistedEventsSizeBytes !== undefined) {
47
+ _this.maxPersistedEventsSizeBytes = sanitizeByteSize(options.maxPersistedEventsSizeBytes, MIN_EVENT_BYTE_SIZE, MAX_PERSISTED_EVENTS_SIZE_CEILING, 'maxPersistedEventsSizeBytes', _this.loggerProvider);
48
+ }
49
+ if (options.maxSingleEventSizeBytes !== undefined) {
50
+ _this.maxSingleEventSizeBytes = sanitizeByteSize(options.maxSingleEventSizeBytes, MIN_EVENT_BYTE_SIZE, MAX_SINGLE_EVENT_SIZE_CEILING, 'maxSingleEventSizeBytes', _this.loggerProvider);
51
+ }
43
52
  // Auto-include .amp-unmask as a default unmaskSelector entry so it works
44
53
  // symmetrically with amp-mask/amp-block without requiring explicit config (SR-2945).
45
54
  _this.privacyConfig = tslib_1.__assign(tslib_1.__assign({}, ((_f = options.privacyConfig) !== null && _f !== void 0 ? _f : {})), { unmaskSelector: Array.from(new Set(tslib_1.__spreadArray([".".concat(constants_1.UNMASK_TEXT_CLASS)], tslib_1.__read(((_h = (_g = options.privacyConfig) === null || _g === void 0 ? void 0 : _g.unmaskSelector) !== null && _h !== void 0 ? _h : [])), false))) });
@@ -63,7 +72,8 @@ var SessionReplayLocalConfig = /** @class */ (function (_super) {
63
72
  _this.useWebWorker = legacyOptions.experimental.useWebWorker;
64
73
  }
65
74
  }
66
- _this.captureAdoptedStyleSheets = (_k = options.captureAdoptedStyleSheets) !== null && _k !== void 0 ? _k : true;
75
+ _this.enableTransportCompression = (_k = options.enableTransportCompression) !== null && _k !== void 0 ? _k : true;
76
+ _this.captureAdoptedStyleSheets = (_l = options.captureAdoptedStyleSheets) !== null && _l !== void 0 ? _l : true;
67
77
  if (options.crossOriginIframes) {
68
78
  _this.crossOriginIframes = options.crossOriginIframes;
69
79
  }
@@ -79,6 +89,33 @@ exports.SessionReplayLocalConfig = SessionReplayLocalConfig;
79
89
  // Customers wanting fewer requests should be raising the value, not lowering it; the floor
80
90
  // is just a defensive guard against typos and unsigned-int rollovers.
81
91
  var MIN_FLUSH_INTERVAL_FLOOR_MS = 100;
92
+ // Shared 1 KB floor for the byte-size overrides — small enough to exercise splitting/drops
93
+ // while debugging, large enough to avoid a 0/negative config that splits on every event.
94
+ var MIN_EVENT_BYTE_SIZE = 1000;
95
+ // Batch cap ceiling: stay under the SR ingest service's 10 MB decompressed split threshold
96
+ // (above which the server splits the batch itself) with headroom for the request wrapper.
97
+ var MAX_PERSISTED_EVENTS_SIZE_CEILING = 8000000;
98
+ // Single-event ceiling: the server rejects a single event above ~10 MB, so never allow an
99
+ // override to exceed that.
100
+ var MAX_SINGLE_EVENT_SIZE_CEILING = 10000000;
101
+ // Defensive bounds for the byte-size overrides. Non-finite inputs are ignored (fall back to
102
+ // the SDK default); out-of-range values are clamped and logged so a typo can't silently
103
+ // disable splitting or push past the server's limits.
104
+ function sanitizeByteSize(raw, min, max, name, loggerProvider) {
105
+ if (!Number.isFinite(raw)) {
106
+ loggerProvider.warn("".concat(name, " value is not a finite number (got ").concat(String(raw), "); ignoring."));
107
+ return undefined;
108
+ }
109
+ if (raw < min) {
110
+ loggerProvider.warn("".concat(name, " ").concat(raw, " is below floor ").concat(min, "; clamping."));
111
+ return min;
112
+ }
113
+ if (raw > max) {
114
+ loggerProvider.warn("".concat(name, " ").concat(raw, " exceeds ceiling ").concat(max, "; clamping."));
115
+ return max;
116
+ }
117
+ return raw;
118
+ }
82
119
  function sanitizeFlushIntervalConfig(raw, loggerProvider) {
83
120
  var _a, _b;
84
121
  var sanitized = {};
@@ -1 +1 @@
1
- {"version":3,"file":"local-config.js","sourceRoot":"","sources":["../../../src/config/local-config.ts"],"names":[],"mappings":"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","sourcesContent":["import { Config, ILogger, Logger, FetchTransport, LogLevel } from '@amplitude/analytics-core';\nimport {\n DEFAULT_PERFORMANCE_CONFIG,\n DEFAULT_SAMPLE_RATE,\n DEFAULT_SERVER_ZONE,\n DEFAULT_URL_CHANGE_POLLING_INTERVAL,\n MAX_INTERVAL,\n MIN_INTERVAL,\n UNMASK_TEXT_CLASS,\n} from '../constants';\nimport { SessionReplayOptions, StoreType } from '../typings/session-replay';\nimport {\n SessionReplayLocalConfig as ISessionReplayLocalConfig,\n CrossOriginIframesConfig,\n FlushIntervalConfig,\n InteractionConfig,\n PrivacyConfig,\n SessionReplayPerformanceConfig,\n SessionReplayVersion,\n} from './types';\nimport { SafeLoggerProvider } from '../logger';\nimport { validateUGCFilterRules } from '../helpers';\n\nexport const getDefaultConfig = () => ({\n flushMaxRetries: 2,\n logLevel: LogLevel.Warn,\n loggerProvider: new Logger(),\n transportProvider: new FetchTransport(),\n});\n\nexport class SessionReplayLocalConfig extends Config implements ISessionReplayLocalConfig {\n apiKey: string;\n sampleRate: number;\n privacyConfig?: PrivacyConfig;\n interactionConfig?: InteractionConfig;\n debugMode?: boolean;\n configServerUrl?: string;\n trackServerUrl?: string;\n shouldInlineStylesheet?: boolean;\n version?: SessionReplayVersion;\n storeType: StoreType;\n performanceConfig?: SessionReplayPerformanceConfig;\n useWebWorker?: boolean;\n applyBackgroundColorToBlockedElements?: boolean;\n enableUrlChangePolling?: boolean;\n urlChangePollingInterval?: number;\n captureDocumentTitle?: boolean;\n captureAdoptedStyleSheets?: boolean;\n crossOriginIframes?: CrossOriginIframesConfig;\n fullSnapshotIntervalMs?: number;\n flushIntervalConfig?: FlushIntervalConfig;\n\n constructor(apiKey: string, options: SessionReplayOptions) {\n const defaultConfig = getDefaultConfig();\n super({\n transportProvider: defaultConfig.transportProvider,\n loggerProvider: new SafeLoggerProvider(options.loggerProvider || defaultConfig.loggerProvider),\n ...options,\n apiKey,\n });\n this.flushMaxRetries =\n options.flushMaxRetries !== undefined && options.flushMaxRetries <= defaultConfig.flushMaxRetries\n ? options.flushMaxRetries\n : defaultConfig.flushMaxRetries;\n\n this.apiKey = apiKey;\n this.sampleRate = options.sampleRate || DEFAULT_SAMPLE_RATE;\n this.serverZone = options.serverZone || DEFAULT_SERVER_ZONE;\n this.configServerUrl = options.configServerUrl;\n this.trackServerUrl = options.trackServerUrl;\n this.shouldInlineStylesheet = options.shouldInlineStylesheet;\n this.version = options.version;\n this.performanceConfig = options.performanceConfig || DEFAULT_PERFORMANCE_CONFIG;\n this.storeType = options.storeType ?? 'memory';\n this.applyBackgroundColorToBlockedElements = options.applyBackgroundColorToBlockedElements ?? false;\n this.enableUrlChangePolling = options.enableUrlChangePolling ?? false;\n this.urlChangePollingInterval = options.urlChangePollingInterval ?? DEFAULT_URL_CHANGE_POLLING_INTERVAL;\n this.captureDocumentTitle = options.captureDocumentTitle ?? false;\n if (options.fullSnapshotIntervalMs !== undefined) {\n this.fullSnapshotIntervalMs = options.fullSnapshotIntervalMs;\n }\n\n // Auto-include .amp-unmask as a default unmaskSelector entry so it works\n // symmetrically with amp-mask/amp-block without requiring explicit config (SR-2945).\n this.privacyConfig = {\n ...(options.privacyConfig ?? {}),\n unmaskSelector: Array.from(new Set([`.${UNMASK_TEXT_CLASS}`, ...(options.privacyConfig?.unmaskSelector ?? [])])),\n };\n if (options.interactionConfig) {\n this.interactionConfig = options.interactionConfig;\n\n // validate ugcFilterRules, throw error if invalid - throw error at the beginning of the config\n if (this.interactionConfig.ugcFilterRules) {\n validateUGCFilterRules(this.interactionConfig.ugcFilterRules);\n }\n }\n if (options.debugMode) {\n this.debugMode = options.debugMode;\n }\n // Support both new useWebWorker and legacy experimental.useWebWorker for backwards compatibility\n if (options.useWebWorker !== undefined) {\n this.useWebWorker = options.useWebWorker;\n } else {\n const legacyOptions = options as { experimental?: { useWebWorker?: boolean } };\n if (legacyOptions.experimental?.useWebWorker !== undefined) {\n this.useWebWorker = legacyOptions.experimental.useWebWorker;\n }\n }\n this.captureAdoptedStyleSheets = options.captureAdoptedStyleSheets ?? true;\n if (options.crossOriginIframes) {\n this.crossOriginIframes = options.crossOriginIframes;\n }\n if (options.flushIntervalConfig) {\n this.flushIntervalConfig = sanitizeFlushIntervalConfig(options.flushIntervalConfig, this.loggerProvider);\n }\n }\n}\n\n// 100ms floor avoids degenerate configs (0/negative) that would split on every event.\n// Customers wanting fewer requests should be raising the value, not lowering it; the floor\n// is just a defensive guard against typos and unsigned-int rollovers.\nconst MIN_FLUSH_INTERVAL_FLOOR_MS = 100;\n\nfunction sanitizeFlushIntervalConfig(raw: FlushIntervalConfig, loggerProvider: ILogger): FlushIntervalConfig {\n const sanitized: FlushIntervalConfig = {};\n if (raw.minIntervalMs !== undefined) {\n if (!Number.isFinite(raw.minIntervalMs) || raw.minIntervalMs < MIN_FLUSH_INTERVAL_FLOOR_MS) {\n loggerProvider.warn(\n `flushIntervalConfig.minIntervalMs ${raw.minIntervalMs} is below floor ${MIN_FLUSH_INTERVAL_FLOOR_MS}ms; clamping.`,\n );\n sanitized.minIntervalMs = MIN_FLUSH_INTERVAL_FLOOR_MS;\n } else {\n sanitized.minIntervalMs = raw.minIntervalMs;\n }\n }\n if (raw.maxIntervalMs !== undefined) {\n // Unlike min, `Infinity` is a meaningful value here: it means \"no upper bound on interval\n // growth\" (Math.min(Infinity, x) === x in BaseEventsStore.shouldSplitEventsList). Reject\n // only NaN and sub-floor values; pass Infinity through.\n if (Number.isNaN(raw.maxIntervalMs) || raw.maxIntervalMs < MIN_FLUSH_INTERVAL_FLOOR_MS) {\n loggerProvider.warn(\n `flushIntervalConfig.maxIntervalMs ${raw.maxIntervalMs} is below floor ${MIN_FLUSH_INTERVAL_FLOOR_MS}ms; clamping.`,\n );\n sanitized.maxIntervalMs = MIN_FLUSH_INTERVAL_FLOOR_MS;\n } else {\n sanitized.maxIntervalMs = raw.maxIntervalMs;\n }\n }\n // Cross-validate against the SDK's effective defaults so that a partial config (only one of\n // {minIntervalMs, maxIntervalMs}) doesn't get silently clamped by the unspecified default.\n // Concrete failure mode without this: customer sets only `minIntervalMs: 30_000`, the store's\n // `maxInterval` falls back to `MAX_INTERVAL = 10_000`, and `shouldSplitEventsList` then\n // caps the effective interval at 10s — silently negating the customer's tune-up.\n // The user-supplied value always wins; we fill in the other side to match.\n if (sanitized.minIntervalMs !== undefined || sanitized.maxIntervalMs !== undefined) {\n const effectiveMin = sanitized.minIntervalMs ?? MIN_INTERVAL;\n const effectiveMax = sanitized.maxIntervalMs ?? MAX_INTERVAL;\n if (effectiveMax < effectiveMin) {\n if (sanitized.maxIntervalMs === undefined) {\n loggerProvider.warn(\n `flushIntervalConfig.minIntervalMs (${effectiveMin}) exceeds the default maxIntervalMs (${MAX_INTERVAL}); raising max to match min.`,\n );\n sanitized.maxIntervalMs = effectiveMin;\n } else if (sanitized.minIntervalMs === undefined) {\n loggerProvider.warn(\n `flushIntervalConfig.maxIntervalMs (${effectiveMax}) is below the default minIntervalMs (${MIN_INTERVAL}); lowering min to match max.`,\n );\n sanitized.minIntervalMs = effectiveMax;\n } else {\n loggerProvider.warn(\n `flushIntervalConfig.maxIntervalMs (${sanitized.maxIntervalMs}) is less than minIntervalMs (${sanitized.minIntervalMs}); raising max to match min.`,\n );\n sanitized.maxIntervalMs = sanitized.minIntervalMs;\n }\n }\n }\n return sanitized;\n}\n"]}
1
+ {"version":3,"file":"local-config.js","sourceRoot":"","sources":["../../../src/config/local-config.ts"],"names":[],"mappings":"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","sourcesContent":["import { Config, ILogger, Logger, FetchTransport, LogLevel } from '@amplitude/analytics-core';\nimport {\n DEFAULT_PERFORMANCE_CONFIG,\n DEFAULT_SAMPLE_RATE,\n DEFAULT_SERVER_ZONE,\n DEFAULT_URL_CHANGE_POLLING_INTERVAL,\n MAX_INTERVAL,\n MIN_INTERVAL,\n UNMASK_TEXT_CLASS,\n} from '../constants';\nimport { SessionReplayOptions, StoreType } from '../typings/session-replay';\nimport {\n SessionReplayLocalConfig as ISessionReplayLocalConfig,\n CrossOriginIframesConfig,\n FlushIntervalConfig,\n InteractionConfig,\n PrivacyConfig,\n SessionReplayPerformanceConfig,\n SessionReplayVersion,\n} from './types';\nimport { SafeLoggerProvider } from '../logger';\nimport { validateUGCFilterRules } from '../helpers';\n\nexport const getDefaultConfig = () => ({\n flushMaxRetries: 2,\n logLevel: LogLevel.Warn,\n loggerProvider: new Logger(),\n transportProvider: new FetchTransport(),\n});\n\nexport class SessionReplayLocalConfig extends Config implements ISessionReplayLocalConfig {\n apiKey: string;\n sampleRate: number;\n privacyConfig?: PrivacyConfig;\n interactionConfig?: InteractionConfig;\n debugMode?: boolean;\n configServerUrl?: string;\n trackServerUrl?: string;\n shouldInlineStylesheet?: boolean;\n version?: SessionReplayVersion;\n storeType: StoreType;\n performanceConfig?: SessionReplayPerformanceConfig;\n useWebWorker?: boolean;\n enableTransportCompression?: boolean;\n applyBackgroundColorToBlockedElements?: boolean;\n enableUrlChangePolling?: boolean;\n urlChangePollingInterval?: number;\n captureDocumentTitle?: boolean;\n captureAdoptedStyleSheets?: boolean;\n crossOriginIframes?: CrossOriginIframesConfig;\n fullSnapshotIntervalMs?: number;\n flushIntervalConfig?: FlushIntervalConfig;\n eagerFullSnapshotSend?: boolean;\n maxPersistedEventsSizeBytes?: number;\n maxSingleEventSizeBytes?: number;\n\n constructor(apiKey: string, options: SessionReplayOptions) {\n const defaultConfig = getDefaultConfig();\n super({\n transportProvider: defaultConfig.transportProvider,\n loggerProvider: new SafeLoggerProvider(options.loggerProvider || defaultConfig.loggerProvider),\n ...options,\n apiKey,\n });\n this.flushMaxRetries =\n options.flushMaxRetries !== undefined && options.flushMaxRetries <= defaultConfig.flushMaxRetries\n ? options.flushMaxRetries\n : defaultConfig.flushMaxRetries;\n\n this.apiKey = apiKey;\n this.sampleRate = options.sampleRate || DEFAULT_SAMPLE_RATE;\n this.serverZone = options.serverZone || DEFAULT_SERVER_ZONE;\n this.configServerUrl = options.configServerUrl;\n this.trackServerUrl = options.trackServerUrl;\n this.shouldInlineStylesheet = options.shouldInlineStylesheet;\n this.version = options.version;\n this.performanceConfig = options.performanceConfig || DEFAULT_PERFORMANCE_CONFIG;\n this.storeType = options.storeType ?? 'memory';\n this.applyBackgroundColorToBlockedElements = options.applyBackgroundColorToBlockedElements ?? false;\n this.enableUrlChangePolling = options.enableUrlChangePolling ?? false;\n this.urlChangePollingInterval = options.urlChangePollingInterval ?? DEFAULT_URL_CHANGE_POLLING_INTERVAL;\n this.captureDocumentTitle = options.captureDocumentTitle ?? false;\n if (options.fullSnapshotIntervalMs !== undefined) {\n this.fullSnapshotIntervalMs = options.fullSnapshotIntervalMs;\n }\n if (options.eagerFullSnapshotSend !== undefined) {\n this.eagerFullSnapshotSend = options.eagerFullSnapshotSend;\n }\n if (options.maxPersistedEventsSizeBytes !== undefined) {\n this.maxPersistedEventsSizeBytes = sanitizeByteSize(\n options.maxPersistedEventsSizeBytes,\n MIN_EVENT_BYTE_SIZE,\n MAX_PERSISTED_EVENTS_SIZE_CEILING,\n 'maxPersistedEventsSizeBytes',\n this.loggerProvider,\n );\n }\n if (options.maxSingleEventSizeBytes !== undefined) {\n this.maxSingleEventSizeBytes = sanitizeByteSize(\n options.maxSingleEventSizeBytes,\n MIN_EVENT_BYTE_SIZE,\n MAX_SINGLE_EVENT_SIZE_CEILING,\n 'maxSingleEventSizeBytes',\n this.loggerProvider,\n );\n }\n\n // Auto-include .amp-unmask as a default unmaskSelector entry so it works\n // symmetrically with amp-mask/amp-block without requiring explicit config (SR-2945).\n this.privacyConfig = {\n ...(options.privacyConfig ?? {}),\n unmaskSelector: Array.from(new Set([`.${UNMASK_TEXT_CLASS}`, ...(options.privacyConfig?.unmaskSelector ?? [])])),\n };\n if (options.interactionConfig) {\n this.interactionConfig = options.interactionConfig;\n\n // validate ugcFilterRules, throw error if invalid - throw error at the beginning of the config\n if (this.interactionConfig.ugcFilterRules) {\n validateUGCFilterRules(this.interactionConfig.ugcFilterRules);\n }\n }\n if (options.debugMode) {\n this.debugMode = options.debugMode;\n }\n // Support both new useWebWorker and legacy experimental.useWebWorker for backwards compatibility\n if (options.useWebWorker !== undefined) {\n this.useWebWorker = options.useWebWorker;\n } else {\n const legacyOptions = options as { experimental?: { useWebWorker?: boolean } };\n if (legacyOptions.experimental?.useWebWorker !== undefined) {\n this.useWebWorker = legacyOptions.experimental.useWebWorker;\n }\n }\n this.enableTransportCompression = options.enableTransportCompression ?? true;\n this.captureAdoptedStyleSheets = options.captureAdoptedStyleSheets ?? true;\n if (options.crossOriginIframes) {\n this.crossOriginIframes = options.crossOriginIframes;\n }\n if (options.flushIntervalConfig) {\n this.flushIntervalConfig = sanitizeFlushIntervalConfig(options.flushIntervalConfig, this.loggerProvider);\n }\n }\n}\n\n// 100ms floor avoids degenerate configs (0/negative) that would split on every event.\n// Customers wanting fewer requests should be raising the value, not lowering it; the floor\n// is just a defensive guard against typos and unsigned-int rollovers.\nconst MIN_FLUSH_INTERVAL_FLOOR_MS = 100;\n\n// Shared 1 KB floor for the byte-size overrides — small enough to exercise splitting/drops\n// while debugging, large enough to avoid a 0/negative config that splits on every event.\nconst MIN_EVENT_BYTE_SIZE = 1_000;\n// Batch cap ceiling: stay under the SR ingest service's 10 MB decompressed split threshold\n// (above which the server splits the batch itself) with headroom for the request wrapper.\nconst MAX_PERSISTED_EVENTS_SIZE_CEILING = 8_000_000;\n// Single-event ceiling: the server rejects a single event above ~10 MB, so never allow an\n// override to exceed that.\nconst MAX_SINGLE_EVENT_SIZE_CEILING = 10_000_000;\n\n// Defensive bounds for the byte-size overrides. Non-finite inputs are ignored (fall back to\n// the SDK default); out-of-range values are clamped and logged so a typo can't silently\n// disable splitting or push past the server's limits.\nfunction sanitizeByteSize(\n raw: number,\n min: number,\n max: number,\n name: string,\n loggerProvider: ILogger,\n): number | undefined {\n if (!Number.isFinite(raw)) {\n loggerProvider.warn(`${name} value is not a finite number (got ${String(raw)}); ignoring.`);\n return undefined;\n }\n if (raw < min) {\n loggerProvider.warn(`${name} ${raw} is below floor ${min}; clamping.`);\n return min;\n }\n if (raw > max) {\n loggerProvider.warn(`${name} ${raw} exceeds ceiling ${max}; clamping.`);\n return max;\n }\n return raw;\n}\n\nfunction sanitizeFlushIntervalConfig(raw: FlushIntervalConfig, loggerProvider: ILogger): FlushIntervalConfig {\n const sanitized: FlushIntervalConfig = {};\n if (raw.minIntervalMs !== undefined) {\n if (!Number.isFinite(raw.minIntervalMs) || raw.minIntervalMs < MIN_FLUSH_INTERVAL_FLOOR_MS) {\n loggerProvider.warn(\n `flushIntervalConfig.minIntervalMs ${raw.minIntervalMs} is below floor ${MIN_FLUSH_INTERVAL_FLOOR_MS}ms; clamping.`,\n );\n sanitized.minIntervalMs = MIN_FLUSH_INTERVAL_FLOOR_MS;\n } else {\n sanitized.minIntervalMs = raw.minIntervalMs;\n }\n }\n if (raw.maxIntervalMs !== undefined) {\n // Unlike min, `Infinity` is a meaningful value here: it means \"no upper bound on interval\n // growth\" (Math.min(Infinity, x) === x in BaseEventsStore.shouldSplitEventsList). Reject\n // only NaN and sub-floor values; pass Infinity through.\n if (Number.isNaN(raw.maxIntervalMs) || raw.maxIntervalMs < MIN_FLUSH_INTERVAL_FLOOR_MS) {\n loggerProvider.warn(\n `flushIntervalConfig.maxIntervalMs ${raw.maxIntervalMs} is below floor ${MIN_FLUSH_INTERVAL_FLOOR_MS}ms; clamping.`,\n );\n sanitized.maxIntervalMs = MIN_FLUSH_INTERVAL_FLOOR_MS;\n } else {\n sanitized.maxIntervalMs = raw.maxIntervalMs;\n }\n }\n // Cross-validate against the SDK's effective defaults so that a partial config (only one of\n // {minIntervalMs, maxIntervalMs}) doesn't get silently clamped by the unspecified default.\n // Concrete failure mode without this: customer sets only `minIntervalMs: 30_000`, the store's\n // `maxInterval` falls back to `MAX_INTERVAL = 10_000`, and `shouldSplitEventsList` then\n // caps the effective interval at 10s — silently negating the customer's tune-up.\n // The user-supplied value always wins; we fill in the other side to match.\n if (sanitized.minIntervalMs !== undefined || sanitized.maxIntervalMs !== undefined) {\n const effectiveMin = sanitized.minIntervalMs ?? MIN_INTERVAL;\n const effectiveMax = sanitized.maxIntervalMs ?? MAX_INTERVAL;\n if (effectiveMax < effectiveMin) {\n if (sanitized.maxIntervalMs === undefined) {\n loggerProvider.warn(\n `flushIntervalConfig.minIntervalMs (${effectiveMin}) exceeds the default maxIntervalMs (${MAX_INTERVAL}); raising max to match min.`,\n );\n sanitized.maxIntervalMs = effectiveMin;\n } else if (sanitized.minIntervalMs === undefined) {\n loggerProvider.warn(\n `flushIntervalConfig.maxIntervalMs (${effectiveMax}) is below the default minIntervalMs (${MIN_INTERVAL}); lowering min to match max.`,\n );\n sanitized.minIntervalMs = effectiveMax;\n } else {\n loggerProvider.warn(\n `flushIntervalConfig.maxIntervalMs (${sanitized.maxIntervalMs}) is less than minIntervalMs (${sanitized.minIntervalMs}); raising max to match min.`,\n );\n sanitized.maxIntervalMs = sanitized.minIntervalMs;\n }\n }\n }\n return sanitized;\n}\n"]}
@@ -168,6 +168,22 @@ export interface SessionReplayLocalConfig extends IConfig {
168
168
  * @defaultValue false
169
169
  */
170
170
  useWebWorker?: boolean;
171
+ /**
172
+ * Controls transport-layer gzip compression of session replay request bodies.
173
+ * When true (default), the SDK gzip-compresses the JSON request body via the browser's
174
+ * `CompressionStream` API and sets `Content-Encoding: gzip` on the POST. When false,
175
+ * the SDK sends the raw JSON body with no `Content-Encoding` header.
176
+ *
177
+ * Disabling is intended as a debugging / safety opt-out (e.g. for diagnosing
178
+ * server-side decompression issues); it increases egress bytes and is not
179
+ * recommended for production.
180
+ *
181
+ * Note: This is independent of `useWebWorker` / `performanceConfig`, which control
182
+ * per-event rrweb compression that runs before events are queued.
183
+ *
184
+ * @defaultValue true
185
+ */
186
+ enableTransportCompression?: boolean;
171
187
  userProperties?: {
172
188
  [key: string]: any;
173
189
  };
@@ -223,6 +239,18 @@ export interface SessionReplayLocalConfig extends IConfig {
223
239
  crossOriginIframes?: CrossOriginIframesConfig;
224
240
  /** Interval in ms at which the SDK takes a full DOM snapshot. Disabled by default — periodic snapshots are expensive. Recommended value: 300000 (5 min). */
225
241
  fullSnapshotIntervalMs?: number;
242
+ /**
243
+ * When true (default), every rrweb full snapshot is flushed to the server immediately so
244
+ * replays become playable as early as possible. Set to `false` to defer full-snapshot
245
+ * sends to the normal interval/size flush cadence instead. The snapshot is still compressed
246
+ * and buffered immediately either way (ordering and page-exit beacon coverage are preserved);
247
+ * only the eager network send is suppressed. Disabling reduces request volume for pages that
248
+ * produce many full snapshots (e.g. focus-driven or `fullSnapshotIntervalMs` checkouts),
249
+ * especially when many SDK instances run on the same page.
250
+ *
251
+ * @defaultValue true
252
+ */
253
+ eagerFullSnapshotSend?: boolean;
226
254
  /**
227
255
  * Controls how often the SDK splits buffered rrweb events into a sequence and dispatches
228
256
  * the resulting batch to the server. The interval starts at `minIntervalMs` and grows by
@@ -235,6 +263,30 @@ export interface SessionReplayLocalConfig extends IConfig {
235
263
  * back-pressuring the SDK on session start.
236
264
  */
237
265
  flushIntervalConfig?: FlushIntervalConfig;
266
+ /**
267
+ * Raw (uncompressed) UTF-8 byte cap for a single buffered events list before the store
268
+ * splits it into its own request. Larger values produce fewer, larger requests (the primary
269
+ * steady-state lever for request volume); smaller values split sooner. Payloads are gzipped
270
+ * on the wire, so several MB of replay JSON compresses to well under 1 MB.
271
+ *
272
+ * Advanced/debug knob — the default already balances request volume against the server's
273
+ * decompressed-size split threshold. Clamped to a safe range; values outside it are clamped
274
+ * and logged.
275
+ *
276
+ * @defaultValue 2000000 (2 MB, {@link MAX_EVENT_LIST_SIZE})
277
+ */
278
+ maxPersistedEventsSizeBytes?: number;
279
+ /**
280
+ * Raw (uncompressed) UTF-8 byte cap for a single rrweb event. Events larger than this are
281
+ * dropped (with a warning) both at capture time and as a pre-send backstop, because the SR
282
+ * ingest service rejects a single event above ~10 MB. Lower this to exercise drop behavior
283
+ * for large full snapshots while debugging.
284
+ *
285
+ * Advanced/debug knob. Clamped to a safe range; values outside it are clamped and logged.
286
+ *
287
+ * @defaultValue 9000000 (9 MB, {@link MAX_SINGLE_EVENT_SIZE})
288
+ */
289
+ maxSingleEventSizeBytes?: number;
238
290
  }
239
291
  export interface FlushIntervalConfig {
240
292
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/config/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;IACzB,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf;;OAEG;IACH,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE;QACP,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,eAAe,EAAE,CAAC;KAC3B,CAAC;IACF,OAAO,CAAC,EAAE;QACR,OAAO,EAAE,OAAO,CAAC;QACjB,IAAI,CAAC,EAAE;YACL,OAAO,CAAC,EAAE,OAAO,CAAC;YAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;YACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;SAC3B,CAAC;KACH,CAAC;CACH;AAED,MAAM,MAAM,eAAe,GAAG,aAAa,CAAC;AAE5C,MAAM,MAAM,yBAAyB,GAAG;IACtC,kBAAkB,CAAC,EAAE,cAAc,CAAC;IACpC,iBAAiB,CAAC,EAAE,aAAa,CAAC;IAClC,qBAAqB,CAAC,EAAE,iBAAiB,CAAC;IAC1C,iBAAiB,CAAC,EAAE,aAAa,CAAC;IAClC,mBAAmB,CAAC,EAAE,eAAe,CAAC;CACvC,CAAC;AAEF,MAAM,WAAW,oCAAoC;IACnD,OAAO,EAAE;QACP,aAAa,EAAE,yBAAyB,CAAC;KAC1C,CAAC;CACH;AAED,MAAM,MAAM,SAAS,GACjB,OAAO,GACP,QAAQ,GACR,cAAc,CAAC;AAEnB,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAG3C,MAAM,MAAM,aAAa,GAAG;IAC1B,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAClC,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B;;;;;;;;;;;OAWG;IACH,aAAa,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,SAAS,CAAA;KAAE,CAAC,CAAC;CAChE,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;;;;;;;;;OAaG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,wBAAyB,SAAQ,OAAO;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,OAAO,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,EAAE,QAAQ,CAAC;IACnB;;;;;OAKG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB;;;;;;;;OAQG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,8BAA8B,CAAC;IACnD;;;;OAIG;IACH,SAAS,EAAE,SAAS,CAAC;IAErB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB,cAAc,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC;IAExC;;;OAGG;IACH,qCAAqC,CAAC,EAAE,OAAO,CAAC;IAChD;;;;;;;OAOG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC;;;;;OAKG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC;;;;;;;;;;;OAWG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,wBAAwB,CAAC;IAC9C,4JAA4J;IAC5J,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC;;;;;;;;;;OAUG;IACH,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;CAC3C;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,yBAA0B,SAAQ,wBAAwB;IACzE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,wBAAwB,CAAC;IACtC,YAAY,EAAE,yBAAyB,CAAC;IACxC,YAAY,EAAE,yBAAyB,GAAG,SAAS,CAAC;CACrD;AACD,MAAM,WAAW,kCAAkC;IACjD,oBAAoB,EAAE,MAAM,OAAO,CAAC,oBAAoB,CAAC,CAAC;CAC3D;AAED,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,yBAAyB,GAAG,SAAS,CAAC;IACpD,WAAW,EAAE,wBAAwB,CAAC;IACtC,YAAY,EAAE,yBAAyB,CAAC;IACxC,SAAS,CAAC,EAAE;QACV,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,iBAAiB,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC7C;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;OAEG;IACH,WAAW,CAAC,EAAE,4BAA4B,CAAC;CAC5C;AAED;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC3C;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG,QAAQ,GAAG,SAAS,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/config/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/config/types.ts"],"names":[],"mappings":";;;AAwDa,QAAA,kBAAkB,GAAG,QAAQ,CAAC","sourcesContent":["import { IConfig, LogLevel, ILogger } from '@amplitude/analytics-core';\nimport { StoreType, ConsoleLogLevel } from '../typings/session-replay';\nimport { TargetingFlag } from '@amplitude/targeting';\n\nexport interface SamplingConfig {\n sample_rate: number;\n capture_enabled: boolean;\n min_session_duration_ms?: number;\n}\n\nexport interface InteractionConfig {\n trackEveryNms?: number;\n enabled: boolean; // defaults to false\n batch: boolean; // defaults to false\n /**\n * UGC filter rules.\n */\n ugcFilterRules?: UGCFilterRule[];\n}\n\nexport interface LoggingConfig {\n console: {\n enabled: boolean;\n levels: ConsoleLogLevel[];\n };\n network?: {\n enabled: boolean;\n body?: {\n request?: boolean;\n response?: boolean;\n maxBodySizeBytes?: number;\n };\n };\n}\n\nexport type TargetingConfig = TargetingFlag;\n\nexport type SessionReplayRemoteConfig = {\n sr_sampling_config?: SamplingConfig;\n sr_privacy_config?: PrivacyConfig;\n sr_interaction_config?: InteractionConfig;\n sr_logging_config?: LoggingConfig;\n sr_targeting_config?: TargetingConfig;\n};\n\nexport interface SessionReplayRemoteConfigAPIResponse {\n configs: {\n sessionReplay: SessionReplayRemoteConfig;\n };\n}\n\nexport type MaskLevel =\n | 'light' // only mask a subset of inputs that's deemed sensitive - password, credit card, telephone #, email. These are information we never want to capture.\n | 'medium' // mask all form fields (inputs); page text is captured as-is\n | 'conservative'; // mask all inputs and all texts\n\nexport const DEFAULT_MASK_LEVEL = 'medium';\n\n// err on the side of excluding more\nexport type PrivacyConfig = {\n blockSelector?: string | string[]; // exclude in the UI\n defaultMaskLevel?: MaskLevel;\n maskSelector?: string[];\n unmaskSelector?: string[];\n maskAttributes?: string[]; // HTML attribute names to mask (e.g. [\"placeholder\", \"aria-label\"])\n /**\n * Per-URL overrides for `defaultMaskLevel`. Each entry contains a glob pattern (`match`)\n * and a `maskLevel` to apply when the current page URL matches that pattern.\n * Rules are evaluated in order; the first match wins. Remote rules take precedence\n * over local rules (remote entries are prepended before local entries).\n *\n * @example\n * urlMaskLevels: [\n * { match: 'https://example.com/checkout/*', maskLevel: 'conservative' },\n * { match: 'https://example.com/public/*', maskLevel: 'light' },\n * ]\n */\n urlMaskLevels?: Array<{ match: string; maskLevel: MaskLevel }>;\n};\n\n/**\n * UGC filter rule.\n */\nexport type UGCFilterRule = {\n /**\n * The selector of the UGC element.\n */\n selector: string;\n /**\n * The replacement text for the UGC element.\n */\n replacement: string;\n};\n\nexport interface CrossOriginIframesConfig {\n enabled: boolean;\n /**\n * When true (default), the parent SDK sends start/stop signals to child iframes via\n * postMessage, keeping their recording lifecycle in sync with the parent.\n *\n * **Privacy note:** The child page's rrweb instance performs its own DOM serialization,\n * so the parent's privacy config (mask levels, block selectors) does NOT automatically\n * apply inside the iframe. Privacy settings must be configured independently on the child page.\n *\n * **Third-party iframes:** Cannot capture iframes you don't control (e.g. Stripe, Google\n * Maps) — both parent and child pages must load the SDK with `crossOriginIframes.enabled: true`.\n *\n * Set to `false` to skip coordination and manage the child recording lifecycle yourself.\n * @defaultValue true\n */\n coordinateChildren?: boolean;\n}\n\nexport interface SessionReplayLocalConfig extends IConfig {\n apiKey: string;\n loggerProvider: ILogger;\n /**\n * LogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug.\n * Sets the log level.\n *\n * @defaultValue LogLevel.Warn\n */\n logLevel: LogLevel;\n /**\n * The maximum number of retries allowed for sending replay events.\n * Once this limit is reached, failed events will no longer be sent.\n *\n * @defaultValue 2\n */\n flushMaxRetries: number;\n /**\n * Use this option to control how many sessions to select for replay collection.\n * The number should be a decimal between 0 and 1, for example 0.4, representing\n * the fraction of sessions to have randomly selected for replay collection.\n * Over a large number of sessions, 0.4 would select 40% of those sessions.\n * Sample rates as small as six decimal places (0.000001) are supported.\n *\n * @defaultValue 0\n */\n sampleRate: number;\n privacyConfig?: PrivacyConfig;\n /**\n * Adds additional debug event property to help debug instrumentation issues\n * (such as mismatching apps). Only recommended for debugging initial setup,\n * and not recommended for production.\n */\n debugMode?: boolean;\n /**\n * Specifies the endpoint URL to fetch remote configuration.\n * If provided, it overrides the default server zone configuration.\n */\n configServerUrl?: string;\n /**\n * Specifies the endpoint URL for sending session replay data.\n * If provided, it overrides the default server zone configuration.\n */\n trackServerUrl?: string;\n /**\n * If stylesheets are inlined, the contents of the stylesheet will be stored.\n * During replay, the stored stylesheet will be used instead of attempting to fetch it remotely.\n * This prevents replays from appearing broken due to missing stylesheets.\n * Note: Inlining stylesheets may not work in all cases.\n */\n shouldInlineStylesheet?: boolean;\n version?: SessionReplayVersion;\n /**\n * Performance configuration config. If enabled, we will defer compression\n * to be done during the browser's idle periods.\n */\n performanceConfig?: SessionReplayPerformanceConfig;\n /**\n * Specifies how replay events should be stored. `idb` uses IndexedDB to persist replay events\n * when all events cannot be sent during capture. `memory` stores replay events only in memory,\n * meaning events are lost when the page is closed. If IndexedDB is unavailable, the system falls back to `memory`.\n */\n storeType: StoreType;\n\n /**\n * If true, the SDK will compress replay events using a web worker.\n * This offloads compression to a separate thread, improving performance on the main thread.\n *\n * @defaultValue false\n */\n useWebWorker?: boolean;\n\n userProperties?: { [key: string]: any };\n\n /**\n * If true, applies a background color to blocked elements in the replay.\n * This helps visualize which elements are blocked from being captured.\n */\n applyBackgroundColorToBlockedElements?: boolean;\n /**\n * Enables URL change polling as a fallback for SPA route tracking.\n * When enabled, the SDK will periodically check for URL changes every second\n * in addition to patching the History API. This is useful for edge cases where\n * route changes might bypass the standard History API methods.\n *\n * @defaultValue false\n */\n enableUrlChangePolling?: boolean;\n /**\n * Specifies the interval in milliseconds for URL change polling when enableUrlChangePolling is true.\n * The SDK will check for URL changes at this interval as a fallback for SPA route tracking.\n *\n * @defaultValue 1000\n */\n urlChangePollingInterval?: number;\n /**\n * Whether to capture document title in URL change events.\n * When disabled, the title field will be empty in URL change events.\n *\n * @defaultValue false\n */\n captureDocumentTitle?: boolean;\n interactionConfig?: InteractionConfig;\n /**\n * When true (default), the CSS rules of any `adoptedStyleSheets` on shadow roots and\n * the document are serialized **inline** within the full snapshot. This makes the snapshot\n * self-contained so that shadow DOM styles are replayed correctly even if subsequent\n * incremental `AdoptedStyleSheet` events are dropped in transit.\n *\n * Set to `false` to revert to the legacy behavior where adopted stylesheet rules are\n * emitted as separate incremental events (which may be lost if delivery is unreliable).\n * Only consider opting out if snapshot payload size is a critical concern.\n *\n * @defaultValue true\n */\n captureAdoptedStyleSheets?: boolean;\n /**\n * Enables recording of cross-origin iframes. Both the parent page and each child iframe\n * page must load the Amplitude Session Replay SDK with this option enabled.\n *\n * When enabled, rrweb uses `postMessage` to relay child DOM events to the parent, which\n * merges them into a single unified event stream.\n */\n crossOriginIframes?: CrossOriginIframesConfig;\n /** Interval in ms at which the SDK takes a full DOM snapshot. Disabled by default — periodic snapshots are expensive. Recommended value: 300000 (5 min). */\n fullSnapshotIntervalMs?: number;\n /**\n * Controls how often the SDK splits buffered rrweb events into a sequence and dispatches\n * the resulting batch to the server. The interval starts at `minIntervalMs` and grows by\n * `minIntervalMs` after each split, capped at `maxIntervalMs`. Lowering values increases\n * replay availability latency improvements at the cost of more requests; raising them\n * reduces request volume (and 200+`X-Session-Replay-Event-Skipped` throttling responses)\n * at the cost of slightly delayed replay availability.\n *\n * Defaults: `{ minIntervalMs: 500, maxIntervalMs: 10_000 }`. Tune up if the server is\n * back-pressuring the SDK on session start.\n */\n flushIntervalConfig?: FlushIntervalConfig;\n}\n\nexport interface FlushIntervalConfig {\n /**\n * Lower bound on the rrweb event-split interval in milliseconds. Also the increment\n * added to the interval after each split. Must be > 0; values are clamped to a 100ms floor.\n *\n * @defaultValue 500\n */\n minIntervalMs?: number;\n /**\n * Upper bound on the rrweb event-split interval in milliseconds. Must be >= `minIntervalMs`.\n *\n * @defaultValue 10000\n */\n maxIntervalMs?: number;\n}\n\nexport interface SessionReplayJoinedConfig extends SessionReplayLocalConfig {\n captureEnabled?: boolean;\n interactionConfig?: InteractionConfig;\n loggingConfig?: LoggingConfig;\n targetingConfig?: TargetingConfig;\n minSessionDurationMs?: number;\n}\n\nexport interface SessionReplayConfigs {\n localConfig: SessionReplayLocalConfig;\n joinedConfig: SessionReplayJoinedConfig;\n remoteConfig: SessionReplayRemoteConfig | undefined;\n}\nexport interface SessionReplayJoinedConfigGenerator {\n generateJoinedConfig: () => Promise<SessionReplayConfigs>;\n}\n\nexport interface SessionReplayMetadata {\n remoteConfig: SessionReplayRemoteConfig | undefined;\n localConfig: SessionReplayLocalConfig;\n joinedConfig: SessionReplayJoinedConfig;\n framework?: {\n name: string;\n version: string;\n };\n sessionId: string | number | undefined;\n hashValue?: number;\n sampleRate: number;\n replaySDKType: string | null;\n replaySDKVersion: string | undefined;\n standaloneSDKType: string;\n standaloneSDKVersion: string | undefined;\n}\n\nexport interface SessionReplayVersion {\n version: string;\n type: SessionReplayType;\n}\n\n/**\n * Configuration options for session replay performance.\n */\nexport interface SessionReplayPerformanceConfig {\n /**\n * If enabled, event compression will be deferred to occur during the browser's idle periods.\n */\n enabled: boolean;\n /**\n * Optional timeout in milliseconds for the `requestIdleCallback` API.\n * If specified, this value will be used to set a maximum time for the browser to wait\n * before executing the deferred compression task, even if the browser is not idle.\n */\n timeout?: number;\n /**\n * If enabled, consecutive mutation events will be merged into a single event before\n * compression, reducing stored event count without changing replay semantics.\n * Defaults to false.\n */\n mergeMutations?: boolean;\n /**\n * Performance configuration for interaction tracking (clicks, scrolls).\n */\n interaction?: InteractionPerformanceConfig;\n}\n\n/**\n * Performance configuration for interaction tracking, specifically for CSS selector generation.\n */\nexport interface InteractionPerformanceConfig {\n /**\n * Maximum time in milliseconds allowed for CSS selector generation.\n * If selector generation takes longer than this, it will throw a timeout error.\n * Default: undefined (no timeout limit)\n */\n timeoutMs?: number;\n /**\n * Maximum number of attempts to optimize/simplify the CSS selector path.\n * Higher values may produce shorter selectors but take longer to compute.\n * Default: 10000\n */\n maxNumberOfTries?: number;\n /**\n * Maximum number of CSS selector combinations to test for uniqueness.\n * If more combinations would be generated, falls back to a simpler strategy.\n * Default: 1000\n */\n threshold?: number;\n}\n\nexport type SessionReplayType = 'standalone' | 'plugin' | 'segment';\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/config/types.ts"],"names":[],"mappings":";;;AAwDa,QAAA,kBAAkB,GAAG,QAAQ,CAAC","sourcesContent":["import { IConfig, LogLevel, ILogger } from '@amplitude/analytics-core';\nimport { StoreType, ConsoleLogLevel } from '../typings/session-replay';\nimport { TargetingFlag } from '@amplitude/targeting';\n\nexport interface SamplingConfig {\n sample_rate: number;\n capture_enabled: boolean;\n min_session_duration_ms?: number;\n}\n\nexport interface InteractionConfig {\n trackEveryNms?: number;\n enabled: boolean; // defaults to false\n batch: boolean; // defaults to false\n /**\n * UGC filter rules.\n */\n ugcFilterRules?: UGCFilterRule[];\n}\n\nexport interface LoggingConfig {\n console: {\n enabled: boolean;\n levels: ConsoleLogLevel[];\n };\n network?: {\n enabled: boolean;\n body?: {\n request?: boolean;\n response?: boolean;\n maxBodySizeBytes?: number;\n };\n };\n}\n\nexport type TargetingConfig = TargetingFlag;\n\nexport type SessionReplayRemoteConfig = {\n sr_sampling_config?: SamplingConfig;\n sr_privacy_config?: PrivacyConfig;\n sr_interaction_config?: InteractionConfig;\n sr_logging_config?: LoggingConfig;\n sr_targeting_config?: TargetingConfig;\n};\n\nexport interface SessionReplayRemoteConfigAPIResponse {\n configs: {\n sessionReplay: SessionReplayRemoteConfig;\n };\n}\n\nexport type MaskLevel =\n | 'light' // only mask a subset of inputs that's deemed sensitive - password, credit card, telephone #, email. These are information we never want to capture.\n | 'medium' // mask all form fields (inputs); page text is captured as-is\n | 'conservative'; // mask all inputs and all texts\n\nexport const DEFAULT_MASK_LEVEL = 'medium';\n\n// err on the side of excluding more\nexport type PrivacyConfig = {\n blockSelector?: string | string[]; // exclude in the UI\n defaultMaskLevel?: MaskLevel;\n maskSelector?: string[];\n unmaskSelector?: string[];\n maskAttributes?: string[]; // HTML attribute names to mask (e.g. [\"placeholder\", \"aria-label\"])\n /**\n * Per-URL overrides for `defaultMaskLevel`. Each entry contains a glob pattern (`match`)\n * and a `maskLevel` to apply when the current page URL matches that pattern.\n * Rules are evaluated in order; the first match wins. Remote rules take precedence\n * over local rules (remote entries are prepended before local entries).\n *\n * @example\n * urlMaskLevels: [\n * { match: 'https://example.com/checkout/*', maskLevel: 'conservative' },\n * { match: 'https://example.com/public/*', maskLevel: 'light' },\n * ]\n */\n urlMaskLevels?: Array<{ match: string; maskLevel: MaskLevel }>;\n};\n\n/**\n * UGC filter rule.\n */\nexport type UGCFilterRule = {\n /**\n * The selector of the UGC element.\n */\n selector: string;\n /**\n * The replacement text for the UGC element.\n */\n replacement: string;\n};\n\nexport interface CrossOriginIframesConfig {\n enabled: boolean;\n /**\n * When true (default), the parent SDK sends start/stop signals to child iframes via\n * postMessage, keeping their recording lifecycle in sync with the parent.\n *\n * **Privacy note:** The child page's rrweb instance performs its own DOM serialization,\n * so the parent's privacy config (mask levels, block selectors) does NOT automatically\n * apply inside the iframe. Privacy settings must be configured independently on the child page.\n *\n * **Third-party iframes:** Cannot capture iframes you don't control (e.g. Stripe, Google\n * Maps) — both parent and child pages must load the SDK with `crossOriginIframes.enabled: true`.\n *\n * Set to `false` to skip coordination and manage the child recording lifecycle yourself.\n * @defaultValue true\n */\n coordinateChildren?: boolean;\n}\n\nexport interface SessionReplayLocalConfig extends IConfig {\n apiKey: string;\n loggerProvider: ILogger;\n /**\n * LogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug.\n * Sets the log level.\n *\n * @defaultValue LogLevel.Warn\n */\n logLevel: LogLevel;\n /**\n * The maximum number of retries allowed for sending replay events.\n * Once this limit is reached, failed events will no longer be sent.\n *\n * @defaultValue 2\n */\n flushMaxRetries: number;\n /**\n * Use this option to control how many sessions to select for replay collection.\n * The number should be a decimal between 0 and 1, for example 0.4, representing\n * the fraction of sessions to have randomly selected for replay collection.\n * Over a large number of sessions, 0.4 would select 40% of those sessions.\n * Sample rates as small as six decimal places (0.000001) are supported.\n *\n * @defaultValue 0\n */\n sampleRate: number;\n privacyConfig?: PrivacyConfig;\n /**\n * Adds additional debug event property to help debug instrumentation issues\n * (such as mismatching apps). Only recommended for debugging initial setup,\n * and not recommended for production.\n */\n debugMode?: boolean;\n /**\n * Specifies the endpoint URL to fetch remote configuration.\n * If provided, it overrides the default server zone configuration.\n */\n configServerUrl?: string;\n /**\n * Specifies the endpoint URL for sending session replay data.\n * If provided, it overrides the default server zone configuration.\n */\n trackServerUrl?: string;\n /**\n * If stylesheets are inlined, the contents of the stylesheet will be stored.\n * During replay, the stored stylesheet will be used instead of attempting to fetch it remotely.\n * This prevents replays from appearing broken due to missing stylesheets.\n * Note: Inlining stylesheets may not work in all cases.\n */\n shouldInlineStylesheet?: boolean;\n version?: SessionReplayVersion;\n /**\n * Performance configuration config. If enabled, we will defer compression\n * to be done during the browser's idle periods.\n */\n performanceConfig?: SessionReplayPerformanceConfig;\n /**\n * Specifies how replay events should be stored. `idb` uses IndexedDB to persist replay events\n * when all events cannot be sent during capture. `memory` stores replay events only in memory,\n * meaning events are lost when the page is closed. If IndexedDB is unavailable, the system falls back to `memory`.\n */\n storeType: StoreType;\n\n /**\n * If true, the SDK will compress replay events using a web worker.\n * This offloads compression to a separate thread, improving performance on the main thread.\n *\n * @defaultValue false\n */\n useWebWorker?: boolean;\n\n /**\n * Controls transport-layer gzip compression of session replay request bodies.\n * When true (default), the SDK gzip-compresses the JSON request body via the browser's\n * `CompressionStream` API and sets `Content-Encoding: gzip` on the POST. When false,\n * the SDK sends the raw JSON body with no `Content-Encoding` header.\n *\n * Disabling is intended as a debugging / safety opt-out (e.g. for diagnosing\n * server-side decompression issues); it increases egress bytes and is not\n * recommended for production.\n *\n * Note: This is independent of `useWebWorker` / `performanceConfig`, which control\n * per-event rrweb compression that runs before events are queued.\n *\n * @defaultValue true\n */\n enableTransportCompression?: boolean;\n\n userProperties?: { [key: string]: any };\n\n /**\n * If true, applies a background color to blocked elements in the replay.\n * This helps visualize which elements are blocked from being captured.\n */\n applyBackgroundColorToBlockedElements?: boolean;\n /**\n * Enables URL change polling as a fallback for SPA route tracking.\n * When enabled, the SDK will periodically check for URL changes every second\n * in addition to patching the History API. This is useful for edge cases where\n * route changes might bypass the standard History API methods.\n *\n * @defaultValue false\n */\n enableUrlChangePolling?: boolean;\n /**\n * Specifies the interval in milliseconds for URL change polling when enableUrlChangePolling is true.\n * The SDK will check for URL changes at this interval as a fallback for SPA route tracking.\n *\n * @defaultValue 1000\n */\n urlChangePollingInterval?: number;\n /**\n * Whether to capture document title in URL change events.\n * When disabled, the title field will be empty in URL change events.\n *\n * @defaultValue false\n */\n captureDocumentTitle?: boolean;\n interactionConfig?: InteractionConfig;\n /**\n * When true (default), the CSS rules of any `adoptedStyleSheets` on shadow roots and\n * the document are serialized **inline** within the full snapshot. This makes the snapshot\n * self-contained so that shadow DOM styles are replayed correctly even if subsequent\n * incremental `AdoptedStyleSheet` events are dropped in transit.\n *\n * Set to `false` to revert to the legacy behavior where adopted stylesheet rules are\n * emitted as separate incremental events (which may be lost if delivery is unreliable).\n * Only consider opting out if snapshot payload size is a critical concern.\n *\n * @defaultValue true\n */\n captureAdoptedStyleSheets?: boolean;\n /**\n * Enables recording of cross-origin iframes. Both the parent page and each child iframe\n * page must load the Amplitude Session Replay SDK with this option enabled.\n *\n * When enabled, rrweb uses `postMessage` to relay child DOM events to the parent, which\n * merges them into a single unified event stream.\n */\n crossOriginIframes?: CrossOriginIframesConfig;\n /** Interval in ms at which the SDK takes a full DOM snapshot. Disabled by default — periodic snapshots are expensive. Recommended value: 300000 (5 min). */\n fullSnapshotIntervalMs?: number;\n /**\n * When true (default), every rrweb full snapshot is flushed to the server immediately so\n * replays become playable as early as possible. Set to `false` to defer full-snapshot\n * sends to the normal interval/size flush cadence instead. The snapshot is still compressed\n * and buffered immediately either way (ordering and page-exit beacon coverage are preserved);\n * only the eager network send is suppressed. Disabling reduces request volume for pages that\n * produce many full snapshots (e.g. focus-driven or `fullSnapshotIntervalMs` checkouts),\n * especially when many SDK instances run on the same page.\n *\n * @defaultValue true\n */\n eagerFullSnapshotSend?: boolean;\n /**\n * Controls how often the SDK splits buffered rrweb events into a sequence and dispatches\n * the resulting batch to the server. The interval starts at `minIntervalMs` and grows by\n * `minIntervalMs` after each split, capped at `maxIntervalMs`. Lowering values increases\n * replay availability latency improvements at the cost of more requests; raising them\n * reduces request volume (and 200+`X-Session-Replay-Event-Skipped` throttling responses)\n * at the cost of slightly delayed replay availability.\n *\n * Defaults: `{ minIntervalMs: 500, maxIntervalMs: 10_000 }`. Tune up if the server is\n * back-pressuring the SDK on session start.\n */\n flushIntervalConfig?: FlushIntervalConfig;\n /**\n * Raw (uncompressed) UTF-8 byte cap for a single buffered events list before the store\n * splits it into its own request. Larger values produce fewer, larger requests (the primary\n * steady-state lever for request volume); smaller values split sooner. Payloads are gzipped\n * on the wire, so several MB of replay JSON compresses to well under 1 MB.\n *\n * Advanced/debug knob — the default already balances request volume against the server's\n * decompressed-size split threshold. Clamped to a safe range; values outside it are clamped\n * and logged.\n *\n * @defaultValue 2000000 (2 MB, {@link MAX_EVENT_LIST_SIZE})\n */\n maxPersistedEventsSizeBytes?: number;\n /**\n * Raw (uncompressed) UTF-8 byte cap for a single rrweb event. Events larger than this are\n * dropped (with a warning) both at capture time and as a pre-send backstop, because the SR\n * ingest service rejects a single event above ~10 MB. Lower this to exercise drop behavior\n * for large full snapshots while debugging.\n *\n * Advanced/debug knob. Clamped to a safe range; values outside it are clamped and logged.\n *\n * @defaultValue 9000000 (9 MB, {@link MAX_SINGLE_EVENT_SIZE})\n */\n maxSingleEventSizeBytes?: number;\n}\n\nexport interface FlushIntervalConfig {\n /**\n * Lower bound on the rrweb event-split interval in milliseconds. Also the increment\n * added to the interval after each split. Must be > 0; values are clamped to a 100ms floor.\n *\n * @defaultValue 500\n */\n minIntervalMs?: number;\n /**\n * Upper bound on the rrweb event-split interval in milliseconds. Must be >= `minIntervalMs`.\n *\n * @defaultValue 10000\n */\n maxIntervalMs?: number;\n}\n\nexport interface SessionReplayJoinedConfig extends SessionReplayLocalConfig {\n captureEnabled?: boolean;\n interactionConfig?: InteractionConfig;\n loggingConfig?: LoggingConfig;\n targetingConfig?: TargetingConfig;\n minSessionDurationMs?: number;\n}\n\nexport interface SessionReplayConfigs {\n localConfig: SessionReplayLocalConfig;\n joinedConfig: SessionReplayJoinedConfig;\n remoteConfig: SessionReplayRemoteConfig | undefined;\n}\nexport interface SessionReplayJoinedConfigGenerator {\n generateJoinedConfig: () => Promise<SessionReplayConfigs>;\n}\n\nexport interface SessionReplayMetadata {\n remoteConfig: SessionReplayRemoteConfig | undefined;\n localConfig: SessionReplayLocalConfig;\n joinedConfig: SessionReplayJoinedConfig;\n framework?: {\n name: string;\n version: string;\n };\n sessionId: string | number | undefined;\n hashValue?: number;\n sampleRate: number;\n replaySDKType: string | null;\n replaySDKVersion: string | undefined;\n standaloneSDKType: string;\n standaloneSDKVersion: string | undefined;\n}\n\nexport interface SessionReplayVersion {\n version: string;\n type: SessionReplayType;\n}\n\n/**\n * Configuration options for session replay performance.\n */\nexport interface SessionReplayPerformanceConfig {\n /**\n * If enabled, event compression will be deferred to occur during the browser's idle periods.\n */\n enabled: boolean;\n /**\n * Optional timeout in milliseconds for the `requestIdleCallback` API.\n * If specified, this value will be used to set a maximum time for the browser to wait\n * before executing the deferred compression task, even if the browser is not idle.\n */\n timeout?: number;\n /**\n * If enabled, consecutive mutation events will be merged into a single event before\n * compression, reducing stored event count without changing replay semantics.\n * Defaults to false.\n */\n mergeMutations?: boolean;\n /**\n * Performance configuration for interaction tracking (clicks, scrolls).\n */\n interaction?: InteractionPerformanceConfig;\n}\n\n/**\n * Performance configuration for interaction tracking, specifically for CSS selector generation.\n */\nexport interface InteractionPerformanceConfig {\n /**\n * Maximum time in milliseconds allowed for CSS selector generation.\n * If selector generation takes longer than this, it will throw a timeout error.\n * Default: undefined (no timeout limit)\n */\n timeoutMs?: number;\n /**\n * Maximum number of attempts to optimize/simplify the CSS selector path.\n * Higher values may produce shorter selectors but take longer to compute.\n * Default: 10000\n */\n maxNumberOfTries?: number;\n /**\n * Maximum number of CSS selector combinations to test for uniqueness.\n * If more combinations would be generated, falls back to a simpler strategy.\n * Default: 1000\n */\n threshold?: number;\n}\n\nexport type SessionReplayType = 'standalone' | 'plugin' | 'segment';\n"]}
@@ -17,7 +17,7 @@ export declare const SESSION_REPLAY_SERVER_URL = "https://api-sr.amplitude.com/s
17
17
  export declare const SESSION_REPLAY_EU_URL = "https://api-sr.eu.amplitude.com/sessions/v2/track";
18
18
  export declare const SESSION_REPLAY_STAGING_URL = "https://api-sr.stag2.amplitude.com/sessions/v2/track";
19
19
  export declare const STORAGE_PREFIX: string;
20
- export declare const MAX_EVENT_LIST_SIZE = 700000;
20
+ export declare const MAX_EVENT_LIST_SIZE = 2000000;
21
21
  export declare const MAX_SINGLE_EVENT_SIZE: number;
22
22
  export declare const WAF_PAYLOAD_TOO_LARGE_PATTERN: RegExp;
23
23
  export declare const INTERACTION_MIN_INTERVAL = 30000;
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAEzE,eAAO,MAAM,6BAA6B,gBAAgB,CAAC;AAE3D,eAAO,MAAM,+BAA+B,QAAuD,CAAC;AACpG,eAAO,MAAM,2BAA2B,kBAAkB,CAAC;AAC3D,eAAO,MAAM,yBAAyB,gBAAgB,CAAC;AACvD,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC,eAAO,MAAM,mBAAmB,gBAAgB,CAAC;AACjD,eAAO,MAAM,0BAA0B;;CAAoB,CAAC;AAC5D,eAAO,MAAM,mCAAmC,OAAO,CAAC;AAExD,eAAO,MAAM,6BAA6B,QAA0D,CAAC;AAErG,eAAO,MAAM,WAAW,cAAc,CAAC;AACvC,eAAO,MAAM,eAAe,aAAa,CAAC;AAC1C,eAAO,MAAM,iBAAiB,eAAe,CAAC;AAC9C,eAAO,MAAM,yBAAyB,mDAAmD,CAAC;AAC1F,eAAO,MAAM,qBAAqB,sDAAsD,CAAC;AACzF,eAAO,MAAM,0BAA0B,yDAAyD,CAAC;AACjG,eAAO,MAAM,cAAc,QAAsC,CAAC;AAIlE,eAAO,MAAM,mBAAmB,SAAU,CAAC;AAI3C,eAAO,MAAM,qBAAqB,QAAc,CAAC;AAIjD,eAAO,MAAM,6BAA6B,QAAqB,CAAC;AAChE,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAC/C,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAC/C,eAAO,MAAM,YAAY,MAAM,CAAC;AAChC,eAAO,MAAM,YAAY,QAAY,CAAC;AACtC,eAAO,MAAM,sBAAsB,QAA0B,CAAC;AAC9D,eAAO,MAAM,OAAO,OAAO,CAAC;AAC5B,eAAO,MAAM,cAAc,OAAO,CAAC;AACnC,eAAO,MAAM,gBAAgB,OAAO,CAAC;AACrC,eAAO,MAAM,mBAAmB,QAAY,CAAC;AAK7C,eAAO,MAAM,oBAAoB,mCAAmC,CAAC;AACrE,eAAO,MAAM,yBAAyB,QAAQ,CAAC;AAC/C,eAAO,MAAM,6BAA6B,SAAS,CAAC;AACpD,eAAO,MAAM,gCAAgC,SAAS,CAAC;AAEvD,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAM/C,eAAO,MAAM,6BAA6B,QAA0B,CAAC;AAErE,eAAO,MAAM,gCAAgC,wBAAwB,CAAC;AAEtE,oBAAY,gBAAgB;IAC1B,YAAY,iBAAiB;IAC7B,UAAU,eAAe;IACzB,aAAa,kBAAkB;IAC/B,QAAQ,aAAa;IACrB,kBAAkB,uBAAuB;IACzC;;;;;;;;OAQG;IACH,oBAAoB,yBAAyB;CAC9C"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAEzE,eAAO,MAAM,6BAA6B,gBAAgB,CAAC;AAE3D,eAAO,MAAM,+BAA+B,QAAuD,CAAC;AACpG,eAAO,MAAM,2BAA2B,kBAAkB,CAAC;AAC3D,eAAO,MAAM,yBAAyB,gBAAgB,CAAC;AACvD,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC,eAAO,MAAM,mBAAmB,gBAAgB,CAAC;AACjD,eAAO,MAAM,0BAA0B;;CAAoB,CAAC;AAC5D,eAAO,MAAM,mCAAmC,OAAO,CAAC;AAExD,eAAO,MAAM,6BAA6B,QAA0D,CAAC;AAErG,eAAO,MAAM,WAAW,cAAc,CAAC;AACvC,eAAO,MAAM,eAAe,aAAa,CAAC;AAC1C,eAAO,MAAM,iBAAiB,eAAe,CAAC;AAC9C,eAAO,MAAM,yBAAyB,mDAAmD,CAAC;AAC1F,eAAO,MAAM,qBAAqB,sDAAsD,CAAC;AACzF,eAAO,MAAM,0BAA0B,yDAAyD,CAAC;AACjG,eAAO,MAAM,cAAc,QAAsC,CAAC;AAWlE,eAAO,MAAM,mBAAmB,UAAY,CAAC;AAI7C,eAAO,MAAM,qBAAqB,QAAc,CAAC;AAIjD,eAAO,MAAM,6BAA6B,QAAqB,CAAC;AAChE,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAC/C,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAC/C,eAAO,MAAM,YAAY,MAAM,CAAC;AAChC,eAAO,MAAM,YAAY,QAAY,CAAC;AACtC,eAAO,MAAM,sBAAsB,QAA0B,CAAC;AAC9D,eAAO,MAAM,OAAO,OAAO,CAAC;AAC5B,eAAO,MAAM,cAAc,OAAO,CAAC;AACnC,eAAO,MAAM,gBAAgB,OAAO,CAAC;AACrC,eAAO,MAAM,mBAAmB,QAAY,CAAC;AAK7C,eAAO,MAAM,oBAAoB,mCAAmC,CAAC;AACrE,eAAO,MAAM,yBAAyB,QAAQ,CAAC;AAC/C,eAAO,MAAM,6BAA6B,SAAS,CAAC;AACpD,eAAO,MAAM,gCAAgC,SAAS,CAAC;AAEvD,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAM/C,eAAO,MAAM,6BAA6B,QAA0B,CAAC;AAErE,eAAO,MAAM,gCAAgC,wBAAwB,CAAC;AAEtE,oBAAY,gBAAgB;IAC1B,YAAY,iBAAiB;IAC7B,UAAU,eAAe;IACzB,aAAa,kBAAkB;IAC/B,QAAQ,aAAa;IACrB,kBAAkB,uBAAuB;IACzC;;;;;;;;OAQG;IACH,oBAAoB,yBAAyB;CAC9C"}
@@ -18,10 +18,17 @@ exports.SESSION_REPLAY_SERVER_URL = 'https://api-sr.amplitude.com/sessions/v2/tr
18
18
  exports.SESSION_REPLAY_EU_URL = 'https://api-sr.eu.amplitude.com/sessions/v2/track';
19
19
  exports.SESSION_REPLAY_STAGING_URL = 'https://api-sr.stag2.amplitude.com/sessions/v2/track';
20
20
  exports.STORAGE_PREFIX = "".concat(analytics_core_1.AMPLITUDE_PREFIX, "_replay_unsent");
21
- // Reduced from 1,000,000 to leave headroom for double-JSON-encoding overhead and the
22
- // uncompressed fallback path. The HTTP body is ~10-30% larger than raw string length
23
- // because events are re-serialized inside the { version, events } wrapper at send time.
24
- exports.MAX_EVENT_LIST_SIZE = 700000;
21
+ // Raw (uncompressed) UTF-8 byte cap for a single batched events list before the store splits
22
+ // it into its own request. Larger batches mean fewer requests the primary steady-state lever
23
+ // for request volume while the time-based flush (flushIntervalConfig) still controls send
24
+ // cadence, so this only decides whether a single high-activity flush gets split into multiple
25
+ // requests. Set to 2 MB: gzipped on the wire that's ~0.2 MB, far under the SR ingest service's
26
+ // 10,000,000-byte DECOMPRESSED split threshold (nova SessionReplayServletV2 splits batches above
27
+ // it server-side and only 413s a single >10 MB event), and just above nova's 1.5 MB "large
28
+ // request" marker with ~5x headroom. Kept at 2 MB (not higher) so it stays a safe POST body on
29
+ // the no-CompressionStream fallback path (raw == wire there) and keeps per-session memory/IDB
30
+ // buffering modest on low-end devices, where bigger buys nothing server-side.
31
+ exports.MAX_EVENT_LIST_SIZE = 2000000;
25
32
  // 9 MB UTF-8 bytes — just under the server's 10 MB per-event threshold. Compared against the
26
33
  // UTF-8 byte length of the serialized event (via Blob/TextEncoder), not the JS string length,
27
34
  // so multi-byte payloads (CJK, emoji) are gated correctly.
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":";;;AAAA,4DAAyE;AAE5D,QAAA,6BAA6B,GAAG,aAAa,CAAC;AAE9C,QAAA,+BAA+B,GAAG,UAAG,qCAA6B,uBAAoB,CAAC;AACvF,QAAA,2BAA2B,GAAG,eAAe,CAAC;AAC9C,QAAA,yBAAyB,GAAG,aAAa,CAAC;AAC1C,QAAA,mBAAmB,GAAG,CAAC,CAAC;AACxB,QAAA,mBAAmB,GAAG,2BAAU,CAAC,EAAE,CAAC;AACpC,QAAA,0BAA0B,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC/C,QAAA,mCAAmC,GAAG,IAAI,CAAC;AAE3C,QAAA,6BAA6B,GAAG,UAAG,qCAA6B,0BAAuB,CAAC;AAExF,QAAA,WAAW,GAAG,WAAW,CAAC;AAC1B,QAAA,eAAe,GAAG,UAAU,CAAC;AAC7B,QAAA,iBAAiB,GAAG,YAAY,CAAC;AACjC,QAAA,yBAAyB,GAAG,gDAAgD,CAAC;AAC7E,QAAA,qBAAqB,GAAG,mDAAmD,CAAC;AAC5E,QAAA,0BAA0B,GAAG,sDAAsD,CAAC;AACpF,QAAA,cAAc,GAAG,UAAG,iCAAgB,mBAAgB,CAAC;AAClE,qFAAqF;AACrF,qFAAqF;AACrF,wFAAwF;AAC3E,QAAA,mBAAmB,GAAG,MAAO,CAAC;AAC3C,6FAA6F;AAC7F,8FAA8F;AAC9F,2DAA2D;AAC9C,QAAA,qBAAqB,GAAG,CAAC,GAAG,OAAO,CAAC;AACjD,gFAAgF;AAChF,sFAAsF;AACtF,mFAAmF;AACtE,QAAA,6BAA6B,GAAG,kBAAkB,CAAC;AACnD,QAAA,wBAAwB,GAAG,KAAM,CAAC,CAAC,aAAa;AAChD,QAAA,wBAAwB,GAAG,KAAM,CAAC,CAAC,WAAW;AAC9C,QAAA,YAAY,GAAG,GAAG,CAAC,CAAC,SAAS;AAC7B,QAAA,YAAY,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,aAAa;AACvC,QAAA,sBAAsB,GAAG,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,SAAS;AAC3D,QAAA,OAAO,GAAG,IAAI,CAAC;AACf,QAAA,cAAc,GAAG,IAAI,CAAC;AACtB,QAAA,gBAAgB,GAAG,IAAI,CAAC;AACxB,QAAA,mBAAmB,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,kDAAkD;AAEhG,sGAAsG;AACtG,kFAAkF;AAClF,sDAAsD;AACzC,QAAA,oBAAoB,GAAG,gCAAgC,CAAC;AACxD,QAAA,yBAAyB,GAAG,KAAK,CAAC;AAClC,QAAA,6BAA6B,GAAG,MAAM,CAAC;AACvC,QAAA,gCAAgC,GAAG,MAAM,CAAC;AACvD,4EAA4E;AAC/D,QAAA,wBAAwB,GAAG,KAAM,CAAC;AAC/C,gFAAgF;AAChF,uFAAuF;AACvF,sFAAsF;AACtF,wFAAwF;AACxF,kFAAkF;AACrE,QAAA,6BAA6B,GAAG,CAAC,GAAG,2BAAmB,CAAC;AAExD,QAAA,gCAAgC,GAAG,qBAAqB,CAAC;AAEtE,IAAY,gBAgBX;AAhBD,WAAY,gBAAgB;IAC1B,iDAA6B,CAAA;IAC7B,6CAAyB,CAAA;IACzB,mDAA+B,CAAA;IAC/B,yCAAqB,CAAA;IACrB,6DAAyC,CAAA;IACzC;;;;;;;;OAQG;IACH,iEAA6C,CAAA;AAC/C,CAAC,EAhBW,gBAAgB,GAAhB,wBAAgB,KAAhB,wBAAgB,QAgB3B","sourcesContent":["import { AMPLITUDE_PREFIX, ServerZone } from '@amplitude/analytics-core';\n\nexport const DEFAULT_EVENT_PROPERTY_PREFIX = '[Amplitude]';\n\nexport const DEFAULT_SESSION_REPLAY_PROPERTY = `${DEFAULT_EVENT_PROPERTY_PREFIX} Session Replay ID`;\nexport const DEFAULT_SESSION_START_EVENT = 'session_start';\nexport const DEFAULT_SESSION_END_EVENT = 'session_end';\nexport const DEFAULT_SAMPLE_RATE = 0;\nexport const DEFAULT_SERVER_ZONE = ServerZone.US;\nexport const DEFAULT_PERFORMANCE_CONFIG = { enabled: true };\nexport const DEFAULT_URL_CHANGE_POLLING_INTERVAL = 1000;\n\nexport const SESSION_REPLAY_DEBUG_PROPERTY = `${DEFAULT_EVENT_PROPERTY_PREFIX} Session Replay Debug`;\n\nexport const BLOCK_CLASS = 'amp-block';\nexport const MASK_TEXT_CLASS = 'amp-mask';\nexport const UNMASK_TEXT_CLASS = 'amp-unmask';\nexport const SESSION_REPLAY_SERVER_URL = 'https://api-sr.amplitude.com/sessions/v2/track';\nexport const SESSION_REPLAY_EU_URL = 'https://api-sr.eu.amplitude.com/sessions/v2/track';\nexport const SESSION_REPLAY_STAGING_URL = 'https://api-sr.stag2.amplitude.com/sessions/v2/track';\nexport const STORAGE_PREFIX = `${AMPLITUDE_PREFIX}_replay_unsent`;\n// Reduced from 1,000,000 to leave headroom for double-JSON-encoding overhead and the\n// uncompressed fallback path. The HTTP body is ~10-30% larger than raw string length\n// because events are re-serialized inside the { version, events } wrapper at send time.\nexport const MAX_EVENT_LIST_SIZE = 700_000;\n// 9 MB UTF-8 bytes — just under the server's 10 MB per-event threshold. Compared against the\n// UTF-8 byte length of the serialized event (via Blob/TextEncoder), not the JS string length,\n// so multi-byte payloads (CJK, emoji) are gated correctly.\nexport const MAX_SINGLE_EVENT_SIZE = 9 * 1000000;\n// WAF rejects oversized compressed payloads with a body containing wording like\n// \"Payload exceeds the maximum allowed size of 10MB\". Match loosely so vendor wording\n// tweaks (rule updates, capitalization, etc.) don't silently disable bisect-retry.\nexport const WAF_PAYLOAD_TOO_LARGE_PATTERN = /payload.*exceed/i;\nexport const INTERACTION_MIN_INTERVAL = 30_000; // 30 seconds\nexport const INTERACTION_MAX_INTERVAL = 60_000; // 1 minute\nexport const MIN_INTERVAL = 500; // 500 ms\nexport const MAX_INTERVAL = 10 * 1000; // 10 seconds\nexport const MAX_IDB_STORAGE_LENGTH = 1000 * 60 * 60 * 24 * 3; // 3 days\nexport const KB_SIZE = 1024;\nexport const MAX_URL_LENGTH = 1000;\nexport const RETRY_TIMEOUT_MS = 1000;\nexport const MAX_KEEPALIVE_BYTES = 64 * 1024; // browser keepalive budget shared with sendBeacon\n\n// Server returns 200 + this header for \"no-retry\" drops (throttle / capture disabled / out-of-range).\n// See projects/sessionreplay/sessionreplay-ingestion/.../SessionReplayError.java.\n// Header value is the numeric error code as a string.\nexport const EVENT_SKIPPED_HEADER = 'X-Session-Replay-Event-Skipped';\nexport const EVENT_SKIP_CODE_THROTTLED = '429';\nexport const EVENT_SKIP_CODE_INVALID_RANGE = '4004';\nexport const EVENT_SKIP_CODE_CAPTURE_DISABLED = '4005';\n// How long to pause the flush schedule after the server signals a throttle.\nexport const THROTTLED_FLUSH_PAUSE_MS = 60_000;\n// Soft UTF-8 byte cap for merging same-session contexts after a throttle pause.\n// Set to 2 * MAX_EVENT_LIST_SIZE so we'll merge at most ~2 max-size sequences (or many\n// small ones) into one POST — fewer requests during recovery without pushing close to\n// the server's 10MB-compressed 413 ceiling. Compared against UTF-8 byte size (via Blob)\n// to match the per-sequence limit's units enforced upstream by base-events-store.\nexport const MERGE_AFTER_THROTTLE_SOFT_CAP = 2 * MAX_EVENT_LIST_SIZE;\n\nexport const CROSS_ORIGIN_IFRAME_MESSAGE_TYPE = 'amplitude-sr-iframe';\n\nexport enum CustomRRwebEvent {\n GET_SR_PROPS = 'get-sr-props',\n DEBUG_INFO = 'debug-info',\n FETCH_REQUEST = 'fetch-request',\n METADATA = 'metadata',\n TARGETING_DECISION = 'targeting-decision',\n /**\n * Emitted once per session, on the first send that passes the min_session_duration_ms\n * gate. Captures how many sends were suppressed before passing and the elapsed time\n * spent below the threshold. Lets backend ingestion diff intended replay count vs\n * actual ingestion so on-call can spot start-time-tracking regressions.\n *\n * Sessions that bounce before crossing the threshold never emit this event by design\n * (the whole payload is suppressed); their absence is the signal.\n */\n REPLAY_GATE_DECISION = 'replay-gate-decision',\n}\n"]}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":";;;AAAA,4DAAyE;AAE5D,QAAA,6BAA6B,GAAG,aAAa,CAAC;AAE9C,QAAA,+BAA+B,GAAG,UAAG,qCAA6B,uBAAoB,CAAC;AACvF,QAAA,2BAA2B,GAAG,eAAe,CAAC;AAC9C,QAAA,yBAAyB,GAAG,aAAa,CAAC;AAC1C,QAAA,mBAAmB,GAAG,CAAC,CAAC;AACxB,QAAA,mBAAmB,GAAG,2BAAU,CAAC,EAAE,CAAC;AACpC,QAAA,0BAA0B,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC/C,QAAA,mCAAmC,GAAG,IAAI,CAAC;AAE3C,QAAA,6BAA6B,GAAG,UAAG,qCAA6B,0BAAuB,CAAC;AAExF,QAAA,WAAW,GAAG,WAAW,CAAC;AAC1B,QAAA,eAAe,GAAG,UAAU,CAAC;AAC7B,QAAA,iBAAiB,GAAG,YAAY,CAAC;AACjC,QAAA,yBAAyB,GAAG,gDAAgD,CAAC;AAC7E,QAAA,qBAAqB,GAAG,mDAAmD,CAAC;AAC5E,QAAA,0BAA0B,GAAG,sDAAsD,CAAC;AACpF,QAAA,cAAc,GAAG,UAAG,iCAAgB,mBAAgB,CAAC;AAClE,6FAA6F;AAC7F,+FAA+F;AAC/F,4FAA4F;AAC5F,8FAA8F;AAC9F,+FAA+F;AAC/F,iGAAiG;AACjG,2FAA2F;AAC3F,+FAA+F;AAC/F,8FAA8F;AAC9F,8EAA8E;AACjE,QAAA,mBAAmB,GAAG,OAAS,CAAC;AAC7C,6FAA6F;AAC7F,8FAA8F;AAC9F,2DAA2D;AAC9C,QAAA,qBAAqB,GAAG,CAAC,GAAG,OAAO,CAAC;AACjD,gFAAgF;AAChF,sFAAsF;AACtF,mFAAmF;AACtE,QAAA,6BAA6B,GAAG,kBAAkB,CAAC;AACnD,QAAA,wBAAwB,GAAG,KAAM,CAAC,CAAC,aAAa;AAChD,QAAA,wBAAwB,GAAG,KAAM,CAAC,CAAC,WAAW;AAC9C,QAAA,YAAY,GAAG,GAAG,CAAC,CAAC,SAAS;AAC7B,QAAA,YAAY,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,aAAa;AACvC,QAAA,sBAAsB,GAAG,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,SAAS;AAC3D,QAAA,OAAO,GAAG,IAAI,CAAC;AACf,QAAA,cAAc,GAAG,IAAI,CAAC;AACtB,QAAA,gBAAgB,GAAG,IAAI,CAAC;AACxB,QAAA,mBAAmB,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,kDAAkD;AAEhG,sGAAsG;AACtG,kFAAkF;AAClF,sDAAsD;AACzC,QAAA,oBAAoB,GAAG,gCAAgC,CAAC;AACxD,QAAA,yBAAyB,GAAG,KAAK,CAAC;AAClC,QAAA,6BAA6B,GAAG,MAAM,CAAC;AACvC,QAAA,gCAAgC,GAAG,MAAM,CAAC;AACvD,4EAA4E;AAC/D,QAAA,wBAAwB,GAAG,KAAM,CAAC;AAC/C,gFAAgF;AAChF,uFAAuF;AACvF,sFAAsF;AACtF,wFAAwF;AACxF,kFAAkF;AACrE,QAAA,6BAA6B,GAAG,CAAC,GAAG,2BAAmB,CAAC;AAExD,QAAA,gCAAgC,GAAG,qBAAqB,CAAC;AAEtE,IAAY,gBAgBX;AAhBD,WAAY,gBAAgB;IAC1B,iDAA6B,CAAA;IAC7B,6CAAyB,CAAA;IACzB,mDAA+B,CAAA;IAC/B,yCAAqB,CAAA;IACrB,6DAAyC,CAAA;IACzC;;;;;;;;OAQG;IACH,iEAA6C,CAAA;AAC/C,CAAC,EAhBW,gBAAgB,GAAhB,wBAAgB,KAAhB,wBAAgB,QAgB3B","sourcesContent":["import { AMPLITUDE_PREFIX, ServerZone } from '@amplitude/analytics-core';\n\nexport const DEFAULT_EVENT_PROPERTY_PREFIX = '[Amplitude]';\n\nexport const DEFAULT_SESSION_REPLAY_PROPERTY = `${DEFAULT_EVENT_PROPERTY_PREFIX} Session Replay ID`;\nexport const DEFAULT_SESSION_START_EVENT = 'session_start';\nexport const DEFAULT_SESSION_END_EVENT = 'session_end';\nexport const DEFAULT_SAMPLE_RATE = 0;\nexport const DEFAULT_SERVER_ZONE = ServerZone.US;\nexport const DEFAULT_PERFORMANCE_CONFIG = { enabled: true };\nexport const DEFAULT_URL_CHANGE_POLLING_INTERVAL = 1000;\n\nexport const SESSION_REPLAY_DEBUG_PROPERTY = `${DEFAULT_EVENT_PROPERTY_PREFIX} Session Replay Debug`;\n\nexport const BLOCK_CLASS = 'amp-block';\nexport const MASK_TEXT_CLASS = 'amp-mask';\nexport const UNMASK_TEXT_CLASS = 'amp-unmask';\nexport const SESSION_REPLAY_SERVER_URL = 'https://api-sr.amplitude.com/sessions/v2/track';\nexport const SESSION_REPLAY_EU_URL = 'https://api-sr.eu.amplitude.com/sessions/v2/track';\nexport const SESSION_REPLAY_STAGING_URL = 'https://api-sr.stag2.amplitude.com/sessions/v2/track';\nexport const STORAGE_PREFIX = `${AMPLITUDE_PREFIX}_replay_unsent`;\n// Raw (uncompressed) UTF-8 byte cap for a single batched events list before the store splits\n// it into its own request. Larger batches mean fewer requests — the primary steady-state lever\n// for request volume — while the time-based flush (flushIntervalConfig) still controls send\n// cadence, so this only decides whether a single high-activity flush gets split into multiple\n// requests. Set to 2 MB: gzipped on the wire that's ~0.2 MB, far under the SR ingest service's\n// 10,000,000-byte DECOMPRESSED split threshold (nova SessionReplayServletV2 splits batches above\n// it server-side and only 413s a single >10 MB event), and just above nova's 1.5 MB \"large\n// request\" marker with ~5x headroom. Kept at 2 MB (not higher) so it stays a safe POST body on\n// the no-CompressionStream fallback path (raw == wire there) and keeps per-session memory/IDB\n// buffering modest on low-end devices, where bigger buys nothing server-side.\nexport const MAX_EVENT_LIST_SIZE = 2_000_000;\n// 9 MB UTF-8 bytes — just under the server's 10 MB per-event threshold. Compared against the\n// UTF-8 byte length of the serialized event (via Blob/TextEncoder), not the JS string length,\n// so multi-byte payloads (CJK, emoji) are gated correctly.\nexport const MAX_SINGLE_EVENT_SIZE = 9 * 1000000;\n// WAF rejects oversized compressed payloads with a body containing wording like\n// \"Payload exceeds the maximum allowed size of 10MB\". Match loosely so vendor wording\n// tweaks (rule updates, capitalization, etc.) don't silently disable bisect-retry.\nexport const WAF_PAYLOAD_TOO_LARGE_PATTERN = /payload.*exceed/i;\nexport const INTERACTION_MIN_INTERVAL = 30_000; // 30 seconds\nexport const INTERACTION_MAX_INTERVAL = 60_000; // 1 minute\nexport const MIN_INTERVAL = 500; // 500 ms\nexport const MAX_INTERVAL = 10 * 1000; // 10 seconds\nexport const MAX_IDB_STORAGE_LENGTH = 1000 * 60 * 60 * 24 * 3; // 3 days\nexport const KB_SIZE = 1024;\nexport const MAX_URL_LENGTH = 1000;\nexport const RETRY_TIMEOUT_MS = 1000;\nexport const MAX_KEEPALIVE_BYTES = 64 * 1024; // browser keepalive budget shared with sendBeacon\n\n// Server returns 200 + this header for \"no-retry\" drops (throttle / capture disabled / out-of-range).\n// See projects/sessionreplay/sessionreplay-ingestion/.../SessionReplayError.java.\n// Header value is the numeric error code as a string.\nexport const EVENT_SKIPPED_HEADER = 'X-Session-Replay-Event-Skipped';\nexport const EVENT_SKIP_CODE_THROTTLED = '429';\nexport const EVENT_SKIP_CODE_INVALID_RANGE = '4004';\nexport const EVENT_SKIP_CODE_CAPTURE_DISABLED = '4005';\n// How long to pause the flush schedule after the server signals a throttle.\nexport const THROTTLED_FLUSH_PAUSE_MS = 60_000;\n// Soft UTF-8 byte cap for merging same-session contexts after a throttle pause.\n// Set to 2 * MAX_EVENT_LIST_SIZE so we'll merge at most ~2 max-size sequences (or many\n// small ones) into one POST — fewer requests during recovery without pushing close to\n// the server's 10MB-compressed 413 ceiling. Compared against UTF-8 byte size (via Blob)\n// to match the per-sequence limit's units enforced upstream by base-events-store.\nexport const MERGE_AFTER_THROTTLE_SOFT_CAP = 2 * MAX_EVENT_LIST_SIZE;\n\nexport const CROSS_ORIGIN_IFRAME_MESSAGE_TYPE = 'amplitude-sr-iframe';\n\nexport enum CustomRRwebEvent {\n GET_SR_PROPS = 'get-sr-props',\n DEBUG_INFO = 'debug-info',\n FETCH_REQUEST = 'fetch-request',\n METADATA = 'metadata',\n TARGETING_DECISION = 'targeting-decision',\n /**\n * Emitted once per session, on the first send that passes the min_session_duration_ms\n * gate. Captures how many sends were suppressed before passing and the elapsed time\n * spent below the threshold. Lets backend ingestion diff intended replay count vs\n * actual ingestion so on-call can spot start-time-tracking regressions.\n *\n * Sessions that bounce before crossing the threshold never emit this event by design\n * (the whole payload is suppressed); their absence is the signal.\n */\n REPLAY_GATE_DECISION = 'replay-gate-decision',\n}\n"]}
@@ -107,8 +107,8 @@ var BaseEventsStore = /** @class */ (function () {
107
107
  // - Commas between events: events.length - 1 bytes
108
108
  // - Double quotes wrapping each event string: events.length * 2 bytes
109
109
  // Note: does not include the outer { version, events } wrapper (~22 bytes) or
110
- // per-event JSON-escaping of " and \ characters; the reduced MAX_EVENT_LIST_SIZE
111
- // cap (700 KB vs 1 MB) provides headroom for those.
110
+ // per-event JSON-escaping of " and \ characters; the MAX_EVENT_LIST_SIZE cap leaves
111
+ // ample headroom (a 2 MB raw batch is still well under the server's 10 MB threshold).
112
112
  var overhead = 2 + Math.max(0, events.length - 1) + events.length * 2;
113
113
  return totalSize + overhead;
114
114
  };
@@ -1 +1 @@
1
- {"version":3,"file":"base-events-store.js","sourceRoot":"","sources":["../../../src/events/base-events-store.ts"],"names":[],"mappings":";;;;AACA,0CAA+E;AAU/E;IAgBE,yBAAY,IAAkB;QAA9B,iBAMC;;QApBO,gBAAW,GAAG,wBAAY,CAAC;QAC3B,gBAAW,GAAG,wBAAY,CAAC;QAC3B,2BAAsB,GAAG,+BAAmB,CAAC;QAM7C,qBAAgB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,4EAA4E;QA+EnH;;;;;WAKG;QACH,0BAAqB,GAAG,UAAC,MAAc,EAAE,eAAuB;YAC9D,IAAM,eAAe,GAAG,KAAI,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC;YAC5D,IAAM,gBAAgB,GAAG,KAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;YAEzD,uDAAuD;YACvD,IAAI,gBAAgB,GAAG,eAAe,IAAI,KAAI,CAAC,sBAAsB,EAAE;gBACrE,OAAO,IAAI,CAAC;aACb;YACD,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAI,CAAC,eAAe,GAAG,KAAI,CAAC,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE;gBACtE,KAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,KAAI,CAAC,WAAW,EAAE,KAAI,CAAC,QAAQ,GAAG,KAAI,CAAC,WAAW,CAAC,CAAC;gBAC7E,KAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBACnC,OAAO,IAAI,CAAC;aACb;YACD,OAAO,KAAK,CAAC;QACf,CAAC,CAAC;QA5FA,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC;QAC1C,IAAI,CAAC,WAAW,GAAG,MAAA,IAAI,CAAC,WAAW,mCAAI,IAAI,CAAC,WAAW,CAAC;QACxD,IAAI,CAAC,WAAW,GAAG,MAAA,IAAI,CAAC,WAAW,mCAAI,IAAI,CAAC,WAAW,CAAC;QACxD,IAAI,CAAC,sBAAsB,GAAG,MAAA,IAAI,CAAC,sBAAsB,mCAAI,IAAI,CAAC,sBAAsB,CAAC;QACzF,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC;IACnC,CAAC;IAVD,sBAAW,4CAAe;aAA1B;YACE,OAAO,IAAI,CAAC,gBAAgB,CAAC;QAC/B,CAAC;;;OAAA;IAmBD;;;OAGG;IACK,uCAAa,GAArB,UAAsB,GAAW;QAC/B,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YACnC,IAAM,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YAC/B,IAAI,IAAI,IAAI,IAAI,EAAE;gBAChB,KAAK,EAAE,CAAC;aACT;iBAAM,IAAI,IAAI,IAAI,KAAK,EAAE;gBACxB,KAAK,IAAI,CAAC,CAAC;aACZ;iBAAM,IAAI,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,EAAE;gBAC3C,8EAA8E;gBAC9E,IAAM,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;gBAC9D,IAAI,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,EAAE;oBACpC,4EAA4E;oBAC5E,KAAK,IAAI,CAAC,CAAC;oBACX,CAAC,EAAE,CAAC;iBACL;qBAAM;oBACL,gFAAgF;oBAChF,KAAK,IAAI,CAAC,CAAC;iBACZ;aACF;iBAAM,IAAI,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,EAAE;gBAC3C,+EAA+E;gBAC/E,KAAK,IAAI,CAAC,CAAC;aACZ;iBAAM;gBACL,4EAA4E;gBAC5E,KAAK,IAAI,CAAC,CAAC;aACZ;SACF;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;OAGG;IACK,4CAAkB,GAA1B,UAA2B,MAAc;;QACvC,IAAI,SAAS,GAAG,CAAC,CAAC;;YAClB,KAAoB,IAAA,WAAA,iBAAA,MAAM,CAAA,8BAAA,kDAAE;gBAAvB,IAAM,OAAK,mBAAA;gBACd,SAAS,IAAI,IAAI,CAAC,aAAa,CAAC,OAAK,CAAC,CAAC;aACxC;;;;;;;;;QAED,mEAAmE;QACnE,iCAAiC;QACjC,mDAAmD;QACnD,sEAAsE;QACtE,8EAA8E;QAC9E,iFAAiF;QACjF,oDAAoD;QACpD,IAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;QAExE,OAAO,SAAS,GAAG,QAAQ,CAAC;IAC9B,CAAC;IAuBH,sBAAC;AAAD,CAAC,AA9GD,IA8GC;AA9GqB,0CAAe","sourcesContent":["import { ILogger } from '@amplitude/analytics-core';\nimport { MAX_EVENT_LIST_SIZE, MAX_INTERVAL, MIN_INTERVAL } from '../constants';\nimport { Events, EventsStore, SendingSequencesReturn } from '../typings/session-replay';\n\nexport type InstanceArgs = {\n loggerProvider: ILogger;\n minInterval?: number;\n maxInterval?: number;\n maxPersistedEventsSize?: number;\n};\n\nexport abstract class BaseEventsStore<KeyType> implements EventsStore<KeyType> {\n protected readonly loggerProvider: ILogger;\n private minInterval = MIN_INTERVAL;\n private maxInterval = MAX_INTERVAL;\n private maxPersistedEventsSize = MAX_EVENT_LIST_SIZE;\n // Assigned in the constructor after `minInterval` is overridden by `args`. Class-field\n // initializers run before the constructor body, so initializing here would freeze\n // `interval` at the class-field default (500ms) — defeating any caller-supplied minInterval\n // for the very first split.\n private interval!: number;\n private _timeAtLastSplit = Date.now(); // Initialize this so we have a point of comparison when events are recorded\n\n public get timeAtLastSplit() {\n return this._timeAtLastSplit;\n }\n\n constructor(args: InstanceArgs) {\n this.loggerProvider = args.loggerProvider;\n this.minInterval = args.minInterval ?? this.minInterval;\n this.maxInterval = args.maxInterval ?? this.maxInterval;\n this.maxPersistedEventsSize = args.maxPersistedEventsSize ?? this.maxPersistedEventsSize;\n this.interval = this.minInterval;\n }\n\n abstract addEventToCurrentSequence(\n sessionId: string | number,\n event: string,\n ): Promise<SendingSequencesReturn<KeyType> | undefined>;\n abstract getSequencesToSend(): Promise<SendingSequencesReturn<KeyType>[] | undefined>;\n abstract storeCurrentSequence(sessionId: number): Promise<SendingSequencesReturn<KeyType> | undefined>;\n abstract storeSendingEvents(sessionId: string | number, events: Events): Promise<KeyType | undefined>;\n abstract cleanUpSessionEventsStore(sessionId: number, sequenceId: KeyType): Promise<void>;\n\n /**\n * Returns the UTF-8 byte size of a string without buffer allocation, matching the\n * actual wire byte count for non-ASCII content (Base64 image data, emoji, etc.).\n */\n private getStringSize(str: string): number {\n let bytes = 0;\n for (let i = 0; i < str.length; i++) {\n const code = str.charCodeAt(i);\n if (code <= 0x7f) {\n bytes++;\n } else if (code <= 0x7ff) {\n bytes += 2;\n } else if (code >= 0xd800 && code <= 0xdbff) {\n // High surrogate — check for a valid low surrogate before consuming the pair.\n const next = i + 1 < str.length ? str.charCodeAt(i + 1) : NaN;\n if (next >= 0xdc00 && next <= 0xdfff) {\n // Valid surrogate pair → encodes a code point above U+FFFF (4 UTF-8 bytes).\n bytes += 4;\n i++;\n } else {\n // Orphaned high surrogate — treated as a replacement character (3 UTF-8 bytes).\n bytes += 3;\n }\n } else if (code >= 0xdc00 && code <= 0xdfff) {\n // Orphaned low surrogate — treated as a replacement character (3 UTF-8 bytes).\n bytes += 3;\n } else {\n // Other BMP character (U+0800–U+FFFF, excluding surrogates): 3 UTF-8 bytes.\n bytes += 3;\n }\n }\n return bytes;\n }\n\n /**\n * Calculates the total UTF-8 byte size of events array\n * Accounts for JSON serialization overhead when sent to backend\n */\n private getEventsArraySize(events: Events): number {\n let totalSize = 0;\n for (const event of events) {\n totalSize += this.getStringSize(event);\n }\n\n // Approximate overhead from the array portion of the JSON payload:\n // - Array brackets: [] = 2 bytes\n // - Commas between events: events.length - 1 bytes\n // - Double quotes wrapping each event string: events.length * 2 bytes\n // Note: does not include the outer { version, events } wrapper (~22 bytes) or\n // per-event JSON-escaping of \" and \\ characters; the reduced MAX_EVENT_LIST_SIZE\n // cap (700 KB vs 1 MB) provides headroom for those.\n const overhead = 2 + Math.max(0, events.length - 1) + events.length * 2;\n\n return totalSize + overhead;\n }\n\n /**\n * Determines whether to send the events list to the backend and start a new\n * empty events list, based on the size of the list as well as the last time sent\n * @param nextEventString\n * @returns boolean\n */\n shouldSplitEventsList = (events: Events, nextEventString: string): boolean => {\n const sizeOfNextEvent = this.getStringSize(nextEventString);\n const sizeOfEventsList = this.getEventsArraySize(events);\n\n // Check size constraint first (most likely to trigger)\n if (sizeOfEventsList + sizeOfNextEvent >= this.maxPersistedEventsSize) {\n return true;\n }\n if (Date.now() - this.timeAtLastSplit > this.interval && events.length) {\n this.interval = Math.min(this.maxInterval, this.interval + this.minInterval);\n this._timeAtLastSplit = Date.now();\n return true;\n }\n return false;\n };\n}\n"]}
1
+ {"version":3,"file":"base-events-store.js","sourceRoot":"","sources":["../../../src/events/base-events-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { ILogger } from '@amplitude/analytics-core';\nimport { MAX_EVENT_LIST_SIZE, MAX_INTERVAL, MIN_INTERVAL } from '../constants';\nimport { Events, EventsStore, SendingSequencesReturn } from '../typings/session-replay';\n\nexport type InstanceArgs = {\n loggerProvider: ILogger;\n minInterval?: number;\n maxInterval?: number;\n maxPersistedEventsSize?: number;\n};\n\nexport abstract class BaseEventsStore<KeyType> implements EventsStore<KeyType> {\n protected readonly loggerProvider: ILogger;\n private minInterval = MIN_INTERVAL;\n private maxInterval = MAX_INTERVAL;\n private maxPersistedEventsSize = MAX_EVENT_LIST_SIZE;\n // Assigned in the constructor after `minInterval` is overridden by `args`. Class-field\n // initializers run before the constructor body, so initializing here would freeze\n // `interval` at the class-field default (500ms) — defeating any caller-supplied minInterval\n // for the very first split.\n private interval!: number;\n private _timeAtLastSplit = Date.now(); // Initialize this so we have a point of comparison when events are recorded\n\n public get timeAtLastSplit() {\n return this._timeAtLastSplit;\n }\n\n constructor(args: InstanceArgs) {\n this.loggerProvider = args.loggerProvider;\n this.minInterval = args.minInterval ?? this.minInterval;\n this.maxInterval = args.maxInterval ?? this.maxInterval;\n this.maxPersistedEventsSize = args.maxPersistedEventsSize ?? this.maxPersistedEventsSize;\n this.interval = this.minInterval;\n }\n\n abstract addEventToCurrentSequence(\n sessionId: string | number,\n event: string,\n ): Promise<SendingSequencesReturn<KeyType> | undefined>;\n abstract getSequencesToSend(): Promise<SendingSequencesReturn<KeyType>[] | undefined>;\n abstract storeCurrentSequence(sessionId: number): Promise<SendingSequencesReturn<KeyType> | undefined>;\n abstract storeSendingEvents(sessionId: string | number, events: Events): Promise<KeyType | undefined>;\n abstract cleanUpSessionEventsStore(sessionId: number, sequenceId: KeyType): Promise<void>;\n\n /**\n * Returns the UTF-8 byte size of a string without buffer allocation, matching the\n * actual wire byte count for non-ASCII content (Base64 image data, emoji, etc.).\n */\n private getStringSize(str: string): number {\n let bytes = 0;\n for (let i = 0; i < str.length; i++) {\n const code = str.charCodeAt(i);\n if (code <= 0x7f) {\n bytes++;\n } else if (code <= 0x7ff) {\n bytes += 2;\n } else if (code >= 0xd800 && code <= 0xdbff) {\n // High surrogate — check for a valid low surrogate before consuming the pair.\n const next = i + 1 < str.length ? str.charCodeAt(i + 1) : NaN;\n if (next >= 0xdc00 && next <= 0xdfff) {\n // Valid surrogate pair → encodes a code point above U+FFFF (4 UTF-8 bytes).\n bytes += 4;\n i++;\n } else {\n // Orphaned high surrogate — treated as a replacement character (3 UTF-8 bytes).\n bytes += 3;\n }\n } else if (code >= 0xdc00 && code <= 0xdfff) {\n // Orphaned low surrogate — treated as a replacement character (3 UTF-8 bytes).\n bytes += 3;\n } else {\n // Other BMP character (U+0800–U+FFFF, excluding surrogates): 3 UTF-8 bytes.\n bytes += 3;\n }\n }\n return bytes;\n }\n\n /**\n * Calculates the total UTF-8 byte size of events array\n * Accounts for JSON serialization overhead when sent to backend\n */\n private getEventsArraySize(events: Events): number {\n let totalSize = 0;\n for (const event of events) {\n totalSize += this.getStringSize(event);\n }\n\n // Approximate overhead from the array portion of the JSON payload:\n // - Array brackets: [] = 2 bytes\n // - Commas between events: events.length - 1 bytes\n // - Double quotes wrapping each event string: events.length * 2 bytes\n // Note: does not include the outer { version, events } wrapper (~22 bytes) or\n // per-event JSON-escaping of \" and \\ characters; the MAX_EVENT_LIST_SIZE cap leaves\n // ample headroom (a 2 MB raw batch is still well under the server's 10 MB threshold).\n const overhead = 2 + Math.max(0, events.length - 1) + events.length * 2;\n\n return totalSize + overhead;\n }\n\n /**\n * Determines whether to send the events list to the backend and start a new\n * empty events list, based on the size of the list as well as the last time sent\n * @param nextEventString\n * @returns boolean\n */\n shouldSplitEventsList = (events: Events, nextEventString: string): boolean => {\n const sizeOfNextEvent = this.getStringSize(nextEventString);\n const sizeOfEventsList = this.getEventsArraySize(events);\n\n // Check size constraint first (most likely to trigger)\n if (sizeOfEventsList + sizeOfNextEvent >= this.maxPersistedEventsSize) {\n return true;\n }\n if (Date.now() - this.timeAtLastSplit > this.interval && events.length) {\n this.interval = Math.min(this.maxInterval, this.interval + this.minInterval);\n this._timeAtLastSplit = Date.now();\n return true;\n }\n return false;\n };\n}\n"]}
@@ -25,10 +25,11 @@ var EventCompressor = /** @class */ (function () {
25
25
  return delay != null ? JSON.stringify({ type: type, timestamp: timestamp, delay: delay, data: data }) : JSON.stringify({ type: type, timestamp: timestamp, data: data });
26
26
  };
27
27
  this.addCompressedEventToManager = function (compressedEvent, sessionId) {
28
+ var _a;
28
29
  // UTF-8 byte size, not JS char count: a 9 M-char string of CJK/emoji can be 18–27 MB
29
30
  // on the wire and would otherwise slip past a char-count guard.
30
31
  var eventSizeBytes = new Blob([compressedEvent]).size;
31
- if (eventSizeBytes > constants_1.MAX_SINGLE_EVENT_SIZE) {
32
+ if (eventSizeBytes > ((_a = _this.config.maxSingleEventSizeBytes) !== null && _a !== void 0 ? _a : constants_1.MAX_SINGLE_EVENT_SIZE)) {
32
33
  _this.config.loggerProvider.warn("Session replay event dropped: serialized size ".concat(Math.round(eventSizeBytes / 1024), " KB exceeds maximum allowed event size. If this recurs, please open a GitHub issue at https://github.com/amplitude/Amplitude-TypeScript/issues or contact Amplitude support."));
33
34
  return;
34
35
  }
@@ -1 +1 @@
1
- {"version":3,"file":"event-compressor.js","sourceRoot":"","sources":["../../../src/events/event-compressor.ts"],"names":[],"mappings":"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","sourcesContent":["import { getGlobalScope } from '@amplitude/analytics-core';\nimport { EventType as RRWebEventType } from '@amplitude/rrweb-types';\nimport type { eventWithTime } from '@amplitude/rrweb-types';\nimport { SessionReplayJoinedConfig } from '../config/types';\nimport { MAX_SINGLE_EVENT_SIZE } from '../constants';\nimport { SessionReplayEventsManager } from '../typings/session-replay';\nimport { mergeMutationEvents } from './merge-mutation-events';\n\ninterface TaskQueue {\n event: eventWithTime;\n sessionId: string | number;\n}\n\nconst DEFAULT_TIMEOUT = 2000;\nexport class EventCompressor {\n taskQueue: TaskQueue[] = [];\n pendingQueue: TaskQueue[] = [];\n isProcessing = false;\n eventsManager?: SessionReplayEventsManager<'replay' | 'interaction', string>;\n config: SessionReplayJoinedConfig;\n deviceId: string | undefined;\n canUseIdleCallback: boolean | undefined;\n timeout: number;\n worker?: Worker;\n onFullSnapshotProcessed?: () => void;\n\n constructor(\n eventsManager: SessionReplayEventsManager<'replay' | 'interaction', string>,\n config: SessionReplayJoinedConfig,\n deviceId: string | undefined,\n workerScript?: string,\n onFullSnapshotProcessed?: () => void,\n ) {\n const globalScope = getGlobalScope();\n this.canUseIdleCallback = globalScope && 'requestIdleCallback' in globalScope;\n this.eventsManager = eventsManager;\n this.config = config;\n this.deviceId = deviceId;\n this.timeout = config.performanceConfig?.timeout || DEFAULT_TIMEOUT;\n this.onFullSnapshotProcessed = onFullSnapshotProcessed;\n\n if (workerScript) {\n config.loggerProvider.log('Enabling web worker for compression');\n\n try {\n const blob = new Blob([workerScript], { type: 'application/javascript' });\n const blobUrl = URL.createObjectURL(blob);\n const worker = new Worker(blobUrl);\n\n worker.onerror = (e) => {\n e.preventDefault();\n config.loggerProvider.error(\n `Worker failed, falling back to non-worker compression: ${e.message} (${e.filename}:${e.lineno})`,\n );\n worker.terminate();\n this.worker = undefined;\n };\n worker.onmessage = (e) => {\n const { compressedEvent, sessionId } = e.data as Record<string, string>;\n this.addCompressedEventToManager(compressedEvent, sessionId);\n };\n\n this.worker = worker;\n } catch (error) {\n config.loggerProvider.error('Failed to create worker, falling back to non-worker compression:', error);\n }\n }\n }\n\n // Schedule processing during idle time\n public scheduleIdleProcessing(): void {\n if (!this.isProcessing) {\n this.isProcessing = true;\n requestIdleCallback(\n (idleDeadline) => {\n this.processQueue(idleDeadline);\n },\n { timeout: this.timeout },\n );\n }\n }\n\n // Add an event to the task queue if idle callback is supported or compress the event directly\n public enqueueEvent(event: eventWithTime, sessionId: string | number): void {\n // Full snapshot (type 2) is the most critical event — a replay cannot be played without it.\n // Process and flush immediately rather than waiting for the idle scheduler or web worker,\n // maximising the chance it is delivered before the user exits the page.\n if (event.type === RRWebEventType.FullSnapshot) {\n this.config.loggerProvider.debug('Processing full snapshot immediately.');\n // Drain any events still pending in the idle-callback queue first.\n // Those events reference the pre-snapshot DOM and must be sent before\n // the full snapshot; if we let them be processed later they'd arrive at\n // the server after the snapshot and cause \"node not found\" replay errors.\n if (this.taskQueue.length > 0 || this.pendingQueue.length > 0) {\n const allTasks = [...this.taskQueue.splice(0), ...this.mergeMutationTasks(this.pendingQueue.splice(0))];\n for (const task of allTasks) {\n const compressed = this.compressEvent(task.event);\n this.addCompressedEventToManager(compressed, task.sessionId);\n }\n this.isProcessing = false;\n }\n const compressedEvent = this.compressEvent(event);\n this.addCompressedEventToManager(compressedEvent, sessionId);\n this.onFullSnapshotProcessed?.();\n return;\n }\n\n if (this.canUseIdleCallback && this.config.performanceConfig?.enabled) {\n this.config.loggerProvider.debug('Enqueuing event for processing during idle time.');\n this.pendingQueue.push({ event, sessionId });\n this.scheduleIdleProcessing();\n } else {\n this.config.loggerProvider.debug('Processing event without idle callback.');\n this.addCompressedEvent(event, sessionId);\n }\n }\n\n // Process the task queue during idle time\n public processQueue(idleDeadline: IdleDeadline): void {\n // Merge newly-arrived pending events and append to the already-merged taskQueue.\n // Keeping them separate prevents re-merging already-merged tasks on subsequent calls,\n // which would corrupt move semantics for nodes that appear in multiple merge passes.\n if (this.pendingQueue.length > 0) {\n this.taskQueue.push(...this.mergeMutationTasks(this.pendingQueue.splice(0)));\n }\n // Process tasks while there's idle time or until the max number of tasks is reached\n while (this.taskQueue.length > 0 && (idleDeadline.timeRemaining() > 0 || idleDeadline.didTimeout)) {\n const task = this.taskQueue.shift();\n if (task) {\n const { event, sessionId } = task;\n this.addCompressedEvent(event, sessionId);\n }\n }\n\n // If there are still tasks in the queue, schedule the next idle callback\n if (this.taskQueue.length > 0 || this.pendingQueue.length > 0) {\n requestIdleCallback(\n (idleDeadline) => {\n this.processQueue(idleDeadline);\n },\n { timeout: this.timeout },\n );\n } else {\n this.isProcessing = false;\n }\n }\n\n compressEvent = (event: eventWithTime): string => {\n // Serialize with type+timestamp first for streaming parser compatibility.\n // JS engines serialize non-integer string keys in insertion order (ES2015 spec,\n // reliable across V8/SpiderMonkey/JSC), so explicit construction controls key order.\n // `delay` is an rrweb player field: an optional ms offset applied on top of\n // `timestamp` during replay to smooth out batched/throttled events. Preserve\n // it when present so playback timing is accurate.\n const { type, timestamp, delay, data } = event as eventWithTime & { delay?: number };\n return delay != null ? JSON.stringify({ type, timestamp, delay, data }) : JSON.stringify({ type, timestamp, data });\n };\n\n private addCompressedEventToManager = (compressedEvent: string, sessionId: string | number) => {\n // UTF-8 byte size, not JS char count: a 9 M-char string of CJK/emoji can be 18–27 MB\n // on the wire and would otherwise slip past a char-count guard.\n const eventSizeBytes = new Blob([compressedEvent]).size;\n if (eventSizeBytes > MAX_SINGLE_EVENT_SIZE) {\n this.config.loggerProvider.warn(\n `Session replay event dropped: serialized size ${Math.round(\n eventSizeBytes / 1024,\n )} KB exceeds maximum allowed event size. If this recurs, please open a GitHub issue at https://github.com/amplitude/Amplitude-TypeScript/issues or contact Amplitude support.`,\n );\n return;\n }\n if (this.eventsManager && this.deviceId) {\n this.eventsManager.addEvent({\n event: { type: 'replay', data: compressedEvent },\n sessionId,\n deviceId: this.deviceId,\n });\n }\n };\n\n public addCompressedEvent = (event: eventWithTime, sessionId: string | number) => {\n if (this.worker) {\n // This indirectly compresses the event.\n try {\n this.worker.postMessage({ event, sessionId });\n } catch (err: any) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n if (err.name === 'DataCloneError') {\n // fallback: serialize\n this.worker.postMessage(JSON.stringify({ event, sessionId }));\n } else {\n this.config.loggerProvider.warn('Unexpected error while posting message to worker:', err);\n }\n }\n } else {\n const compressedEvent = this.compressEvent(event);\n this.addCompressedEventToManager(compressedEvent, sessionId);\n }\n };\n\n /**\n * Synchronously drain all queued events. Called during page unload to prevent\n * data loss from events waiting in the requestIdleCallback queue.\n */\n public flushQueue = () => {\n // Merge any events still in pendingQueue into taskQueue first.\n // Events land in pendingQueue when the idle callback hasn't fired yet;\n // without this step they would be silently lost on page unload.\n if (this.pendingQueue.length > 0) {\n this.taskQueue.push(...this.mergeMutationTasks(this.pendingQueue.splice(0)));\n }\n while (this.taskQueue.length > 0) {\n const task = this.taskQueue.shift();\n if (task) {\n const { event, sessionId } = task;\n // Bypass the web worker: compress synchronously on the main thread and\n // write directly to the manager. postMessage is async — during page\n // unload the worker response would never arrive and events would be\n // silently dropped. This mirrors the pattern used for full snapshots in\n // enqueueEvent().\n const compressed = this.compressEvent(event);\n this.addCompressedEventToManager(compressed, sessionId);\n }\n }\n this.isProcessing = false;\n };\n\n // Merge consecutive mutation tasks with the same sessionId before processing,\n // reducing the number of events serialized and stored without changing replay semantics.\n // Only runs when performanceConfig.mergeMutations is explicitly enabled.\n private mergeMutationTasks(tasks: TaskQueue[]): TaskQueue[] {\n if (!this.config.performanceConfig?.mergeMutations) return tasks;\n if (tasks.length <= 1) return tasks;\n\n const result: TaskQueue[] = [];\n let i = 0;\n\n while (i < tasks.length) {\n const sessionId = tasks[i].sessionId;\n\n // Find the end of the current session run\n let j = i + 1;\n while (j < tasks.length && tasks[j].sessionId === sessionId) {\n j++;\n }\n\n // Merge consecutive mutations within this session run; non-mutations pass through unchanged\n const merged = mergeMutationEvents(tasks.slice(i, j).map((t) => t.event));\n for (const event of merged) {\n result.push({ event, sessionId });\n }\n\n i = j;\n }\n\n return result;\n }\n\n public terminate = () => {\n this.worker?.terminate();\n };\n}\n"]}
1
+ {"version":3,"file":"event-compressor.js","sourceRoot":"","sources":["../../../src/events/event-compressor.ts"],"names":[],"mappings":"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","sourcesContent":["import { getGlobalScope } from '@amplitude/analytics-core';\nimport { EventType as RRWebEventType } from '@amplitude/rrweb-types';\nimport type { eventWithTime } from '@amplitude/rrweb-types';\nimport { SessionReplayJoinedConfig } from '../config/types';\nimport { MAX_SINGLE_EVENT_SIZE } from '../constants';\nimport { SessionReplayEventsManager } from '../typings/session-replay';\nimport { mergeMutationEvents } from './merge-mutation-events';\n\ninterface TaskQueue {\n event: eventWithTime;\n sessionId: string | number;\n}\n\nconst DEFAULT_TIMEOUT = 2000;\nexport class EventCompressor {\n taskQueue: TaskQueue[] = [];\n pendingQueue: TaskQueue[] = [];\n isProcessing = false;\n eventsManager?: SessionReplayEventsManager<'replay' | 'interaction', string>;\n config: SessionReplayJoinedConfig;\n deviceId: string | undefined;\n canUseIdleCallback: boolean | undefined;\n timeout: number;\n worker?: Worker;\n onFullSnapshotProcessed?: () => void;\n\n constructor(\n eventsManager: SessionReplayEventsManager<'replay' | 'interaction', string>,\n config: SessionReplayJoinedConfig,\n deviceId: string | undefined,\n workerScript?: string,\n onFullSnapshotProcessed?: () => void,\n ) {\n const globalScope = getGlobalScope();\n this.canUseIdleCallback = globalScope && 'requestIdleCallback' in globalScope;\n this.eventsManager = eventsManager;\n this.config = config;\n this.deviceId = deviceId;\n this.timeout = config.performanceConfig?.timeout || DEFAULT_TIMEOUT;\n this.onFullSnapshotProcessed = onFullSnapshotProcessed;\n\n if (workerScript) {\n config.loggerProvider.log('Enabling web worker for compression');\n\n try {\n const blob = new Blob([workerScript], { type: 'application/javascript' });\n const blobUrl = URL.createObjectURL(blob);\n const worker = new Worker(blobUrl);\n\n worker.onerror = (e) => {\n e.preventDefault();\n config.loggerProvider.error(\n `Worker failed, falling back to non-worker compression: ${e.message} (${e.filename}:${e.lineno})`,\n );\n worker.terminate();\n this.worker = undefined;\n };\n worker.onmessage = (e) => {\n const { compressedEvent, sessionId } = e.data as Record<string, string>;\n this.addCompressedEventToManager(compressedEvent, sessionId);\n };\n\n this.worker = worker;\n } catch (error) {\n config.loggerProvider.error('Failed to create worker, falling back to non-worker compression:', error);\n }\n }\n }\n\n // Schedule processing during idle time\n public scheduleIdleProcessing(): void {\n if (!this.isProcessing) {\n this.isProcessing = true;\n requestIdleCallback(\n (idleDeadline) => {\n this.processQueue(idleDeadline);\n },\n { timeout: this.timeout },\n );\n }\n }\n\n // Add an event to the task queue if idle callback is supported or compress the event directly\n public enqueueEvent(event: eventWithTime, sessionId: string | number): void {\n // Full snapshot (type 2) is the most critical event — a replay cannot be played without it.\n // Process and flush immediately rather than waiting for the idle scheduler or web worker,\n // maximising the chance it is delivered before the user exits the page.\n if (event.type === RRWebEventType.FullSnapshot) {\n this.config.loggerProvider.debug('Processing full snapshot immediately.');\n // Drain any events still pending in the idle-callback queue first.\n // Those events reference the pre-snapshot DOM and must be sent before\n // the full snapshot; if we let them be processed later they'd arrive at\n // the server after the snapshot and cause \"node not found\" replay errors.\n if (this.taskQueue.length > 0 || this.pendingQueue.length > 0) {\n const allTasks = [...this.taskQueue.splice(0), ...this.mergeMutationTasks(this.pendingQueue.splice(0))];\n for (const task of allTasks) {\n const compressed = this.compressEvent(task.event);\n this.addCompressedEventToManager(compressed, task.sessionId);\n }\n this.isProcessing = false;\n }\n const compressedEvent = this.compressEvent(event);\n this.addCompressedEventToManager(compressedEvent, sessionId);\n this.onFullSnapshotProcessed?.();\n return;\n }\n\n if (this.canUseIdleCallback && this.config.performanceConfig?.enabled) {\n this.config.loggerProvider.debug('Enqueuing event for processing during idle time.');\n this.pendingQueue.push({ event, sessionId });\n this.scheduleIdleProcessing();\n } else {\n this.config.loggerProvider.debug('Processing event without idle callback.');\n this.addCompressedEvent(event, sessionId);\n }\n }\n\n // Process the task queue during idle time\n public processQueue(idleDeadline: IdleDeadline): void {\n // Merge newly-arrived pending events and append to the already-merged taskQueue.\n // Keeping them separate prevents re-merging already-merged tasks on subsequent calls,\n // which would corrupt move semantics for nodes that appear in multiple merge passes.\n if (this.pendingQueue.length > 0) {\n this.taskQueue.push(...this.mergeMutationTasks(this.pendingQueue.splice(0)));\n }\n // Process tasks while there's idle time or until the max number of tasks is reached\n while (this.taskQueue.length > 0 && (idleDeadline.timeRemaining() > 0 || idleDeadline.didTimeout)) {\n const task = this.taskQueue.shift();\n if (task) {\n const { event, sessionId } = task;\n this.addCompressedEvent(event, sessionId);\n }\n }\n\n // If there are still tasks in the queue, schedule the next idle callback\n if (this.taskQueue.length > 0 || this.pendingQueue.length > 0) {\n requestIdleCallback(\n (idleDeadline) => {\n this.processQueue(idleDeadline);\n },\n { timeout: this.timeout },\n );\n } else {\n this.isProcessing = false;\n }\n }\n\n compressEvent = (event: eventWithTime): string => {\n // Serialize with type+timestamp first for streaming parser compatibility.\n // JS engines serialize non-integer string keys in insertion order (ES2015 spec,\n // reliable across V8/SpiderMonkey/JSC), so explicit construction controls key order.\n // `delay` is an rrweb player field: an optional ms offset applied on top of\n // `timestamp` during replay to smooth out batched/throttled events. Preserve\n // it when present so playback timing is accurate.\n const { type, timestamp, delay, data } = event as eventWithTime & { delay?: number };\n return delay != null ? JSON.stringify({ type, timestamp, delay, data }) : JSON.stringify({ type, timestamp, data });\n };\n\n private addCompressedEventToManager = (compressedEvent: string, sessionId: string | number) => {\n // UTF-8 byte size, not JS char count: a 9 M-char string of CJK/emoji can be 18–27 MB\n // on the wire and would otherwise slip past a char-count guard.\n const eventSizeBytes = new Blob([compressedEvent]).size;\n if (eventSizeBytes > (this.config.maxSingleEventSizeBytes ?? MAX_SINGLE_EVENT_SIZE)) {\n this.config.loggerProvider.warn(\n `Session replay event dropped: serialized size ${Math.round(\n eventSizeBytes / 1024,\n )} KB exceeds maximum allowed event size. If this recurs, please open a GitHub issue at https://github.com/amplitude/Amplitude-TypeScript/issues or contact Amplitude support.`,\n );\n return;\n }\n if (this.eventsManager && this.deviceId) {\n this.eventsManager.addEvent({\n event: { type: 'replay', data: compressedEvent },\n sessionId,\n deviceId: this.deviceId,\n });\n }\n };\n\n public addCompressedEvent = (event: eventWithTime, sessionId: string | number) => {\n if (this.worker) {\n // This indirectly compresses the event.\n try {\n this.worker.postMessage({ event, sessionId });\n } catch (err: any) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n if (err.name === 'DataCloneError') {\n // fallback: serialize\n this.worker.postMessage(JSON.stringify({ event, sessionId }));\n } else {\n this.config.loggerProvider.warn('Unexpected error while posting message to worker:', err);\n }\n }\n } else {\n const compressedEvent = this.compressEvent(event);\n this.addCompressedEventToManager(compressedEvent, sessionId);\n }\n };\n\n /**\n * Synchronously drain all queued events. Called during page unload to prevent\n * data loss from events waiting in the requestIdleCallback queue.\n */\n public flushQueue = () => {\n // Merge any events still in pendingQueue into taskQueue first.\n // Events land in pendingQueue when the idle callback hasn't fired yet;\n // without this step they would be silently lost on page unload.\n if (this.pendingQueue.length > 0) {\n this.taskQueue.push(...this.mergeMutationTasks(this.pendingQueue.splice(0)));\n }\n while (this.taskQueue.length > 0) {\n const task = this.taskQueue.shift();\n if (task) {\n const { event, sessionId } = task;\n // Bypass the web worker: compress synchronously on the main thread and\n // write directly to the manager. postMessage is async — during page\n // unload the worker response would never arrive and events would be\n // silently dropped. This mirrors the pattern used for full snapshots in\n // enqueueEvent().\n const compressed = this.compressEvent(event);\n this.addCompressedEventToManager(compressed, sessionId);\n }\n }\n this.isProcessing = false;\n };\n\n // Merge consecutive mutation tasks with the same sessionId before processing,\n // reducing the number of events serialized and stored without changing replay semantics.\n // Only runs when performanceConfig.mergeMutations is explicitly enabled.\n private mergeMutationTasks(tasks: TaskQueue[]): TaskQueue[] {\n if (!this.config.performanceConfig?.mergeMutations) return tasks;\n if (tasks.length <= 1) return tasks;\n\n const result: TaskQueue[] = [];\n let i = 0;\n\n while (i < tasks.length) {\n const sessionId = tasks[i].sessionId;\n\n // Find the end of the current session run\n let j = i + 1;\n while (j < tasks.length && tasks[j].sessionId === sessionId) {\n j++;\n }\n\n // Merge consecutive mutations within this session run; non-mutations pass through unchanged\n const merged = mergeMutationEvents(tasks.slice(i, j).map((t) => t.event));\n for (const event of merged) {\n result.push({ event, sessionId });\n }\n\n i = j;\n }\n\n return result;\n }\n\n public terminate = () => {\n this.worker?.terminate();\n };\n}\n"]}