@amplitude/session-replay-browser 1.37.0 → 1.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/lib/cjs/config/local-config.d.ts +1 -0
  2. package/lib/cjs/config/local-config.d.ts.map +1 -1
  3. package/lib/cjs/config/local-config.js +2 -1
  4. package/lib/cjs/config/local-config.js.map +1 -1
  5. package/lib/cjs/config/types.d.ts +19 -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/events/event-compressor.d.ts +2 -0
  9. package/lib/cjs/events/event-compressor.d.ts.map +1 -1
  10. package/lib/cjs/events/event-compressor.js +54 -6
  11. package/lib/cjs/events/event-compressor.js.map +1 -1
  12. package/lib/cjs/events/merge-mutation-events.d.ts +10 -0
  13. package/lib/cjs/events/merge-mutation-events.d.ts.map +1 -0
  14. package/lib/cjs/events/merge-mutation-events.js +232 -0
  15. package/lib/cjs/events/merge-mutation-events.js.map +1 -0
  16. package/lib/cjs/session-replay.d.ts.map +1 -1
  17. package/lib/cjs/session-replay.js +1 -0
  18. package/lib/cjs/session-replay.js.map +1 -1
  19. package/lib/cjs/utils/rrweb.d.ts +6 -0
  20. package/lib/cjs/utils/rrweb.d.ts.map +1 -1
  21. package/lib/cjs/utils/rrweb.js.map +1 -1
  22. package/lib/cjs/version.d.ts +1 -1
  23. package/lib/cjs/version.js +1 -1
  24. package/lib/cjs/version.js.map +1 -1
  25. package/lib/esm/config/local-config.d.ts +1 -0
  26. package/lib/esm/config/local-config.d.ts.map +1 -1
  27. package/lib/esm/config/local-config.js +2 -1
  28. package/lib/esm/config/local-config.js.map +1 -1
  29. package/lib/esm/config/types.d.ts +19 -0
  30. package/lib/esm/config/types.d.ts.map +1 -1
  31. package/lib/esm/config/types.js.map +1 -1
  32. package/lib/esm/events/event-compressor.d.ts +2 -0
  33. package/lib/esm/events/event-compressor.d.ts.map +1 -1
  34. package/lib/esm/events/event-compressor.js +55 -7
  35. package/lib/esm/events/event-compressor.js.map +1 -1
  36. package/lib/esm/events/merge-mutation-events.d.ts +10 -0
  37. package/lib/esm/events/merge-mutation-events.d.ts.map +1 -0
  38. package/lib/esm/events/merge-mutation-events.js +228 -0
  39. package/lib/esm/events/merge-mutation-events.js.map +1 -0
  40. package/lib/esm/session-replay.d.ts.map +1 -1
  41. package/lib/esm/session-replay.js +1 -0
  42. package/lib/esm/session-replay.js.map +1 -1
  43. package/lib/esm/utils/rrweb.d.ts +6 -0
  44. package/lib/esm/utils/rrweb.d.ts.map +1 -1
  45. package/lib/esm/utils/rrweb.js.map +1 -1
  46. package/lib/esm/version.d.ts +1 -1
  47. package/lib/esm/version.js +1 -1
  48. package/lib/esm/version.js.map +1 -1
  49. package/lib/scripts/index-min.js +1 -1
  50. package/lib/scripts/index-min.js.gz +0 -0
  51. package/lib/scripts/index-min.js.map +1 -1
  52. package/lib/scripts/session-replay-browser-min.js +1 -1
  53. package/lib/scripts/session-replay-browser-min.js.gz +0 -0
  54. package/lib/scripts/session-replay-browser-min.js.map +1 -1
  55. package/package.json +4 -4
@@ -28,6 +28,7 @@ export declare class SessionReplayLocalConfig extends Config implements ISession
28
28
  enableUrlChangePolling?: boolean;
29
29
  urlChangePollingInterval?: number;
30
30
  captureDocumentTitle?: boolean;
31
+ captureAdoptedStyleSheets?: boolean;
31
32
  constructor(apiKey: string, options: SessionReplayOptions);
32
33
  }
33
34
  //# 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,EAAE,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAOrF,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC5E,OAAO,EACL,wBAAwB,IAAI,yBAAyB,EACrD,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,eAAe,CAAC,EAAE;QAChB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC;IACF,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,oBAAoB,CAAC,EAAE,OAAO,CAAC;gBAEnB,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB;CAsD1D"}
1
+ {"version":3,"file":"local-config.d.ts","sourceRoot":"","sources":["../../../src/config/local-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAOrF,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC5E,OAAO,EACL,wBAAwB,IAAI,yBAAyB,EACrD,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,eAAe,CAAC,EAAE;QAChB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC;IACF,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,yBAAyB,CAAC,EAAE,OAAO,CAAC;gBAExB,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB;CAuD1D"}
@@ -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;
20
+ var _a, _b, _c, _d, _e, _f, _g;
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 =
@@ -63,6 +63,7 @@ var SessionReplayLocalConfig = /** @class */ (function (_super) {
63
63
  if (options.omitElementTags) {
64
64
  _this.omitElementTags = options.omitElementTags;
65
65
  }
66
+ _this.captureAdoptedStyleSheets = (_g = options.captureAdoptedStyleSheets) !== null && _g !== void 0 ? _g : true;
66
67
  return _this;
67
68
  }
68
69
  return SessionReplayLocalConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"local-config.js","sourceRoot":"","sources":["../../../src/config/local-config.ts"],"names":[],"mappings":";;;;AAAA,4DAAqF;AACrF,0CAKsB;AAStB,oCAA+C;AAC/C,sCAAoD;AAE7C,IAAM,gBAAgB,GAAG,cAAM,OAAA,CAAC;IACrC,eAAe,EAAE,CAAC;IAClB,QAAQ,EAAE,yBAAQ,CAAC,IAAI;IACvB,cAAc,EAAE,IAAI,uBAAM,EAAE;IAC5B,iBAAiB,EAAE,IAAI,+BAAc,EAAE;CACxC,CAAC,EALoC,CAKpC,CAAC;AALU,QAAA,gBAAgB,oBAK1B;AAEH;IAA8C,oDAAM;IAsBlD,kCAAY,MAAc,EAAE,OAA6B;QAAzD,iBAqDC;;QApDC,IAAM,aAAa,GAAG,IAAA,wBAAgB,GAAE,CAAC;gBACzC,sDACE,iBAAiB,EAAE,aAAa,CAAC,iBAAiB,EAClD,cAAc,EAAE,IAAI,2BAAkB,CAAC,OAAO,CAAC,cAAc,IAAI,aAAa,CAAC,cAAc,CAAC,IAC3F,OAAO,KACV,MAAM,QAAA,IACN;QACF,KAAI,CAAC,eAAe;YAClB,OAAO,CAAC,eAAe,KAAK,SAAS,IAAI,OAAO,CAAC,eAAe,IAAI,aAAa,CAAC,eAAe;gBAC/F,CAAC,CAAC,OAAO,CAAC,eAAe;gBACzB,CAAC,CAAC,aAAa,CAAC,eAAe,CAAC;QAEpC,KAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,KAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,+BAAmB,CAAC;QAC5D,KAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,+BAAmB,CAAC;QAC5D,KAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;QAC/C,KAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;QAC7C,KAAI,CAAC,sBAAsB,GAAG,OAAO,CAAC,sBAAsB,CAAC;QAC7D,KAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,KAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,IAAI,sCAA0B,CAAC;QACjF,KAAI,CAAC,SAAS,GAAG,MAAA,OAAO,CAAC,SAAS,mCAAI,KAAK,CAAC;QAC5C,KAAI,CAAC,qCAAqC,GAAG,MAAA,OAAO,CAAC,qCAAqC,mCAAI,KAAK,CAAC;QACpG,KAAI,CAAC,sBAAsB,GAAG,MAAA,OAAO,CAAC,sBAAsB,mCAAI,KAAK,CAAC;QACtE,KAAI,CAAC,wBAAwB,GAAG,MAAA,OAAO,CAAC,wBAAwB,mCAAI,+CAAmC,CAAC;QACxG,KAAI,CAAC,oBAAoB,GAAG,MAAA,OAAO,CAAC,oBAAoB,mCAAI,KAAK,CAAC;QAElE,IAAI,OAAO,CAAC,aAAa,EAAE;YACzB,KAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;SAC5C;QACD,IAAI,OAAO,CAAC,iBAAiB,EAAE;YAC7B,KAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;YAEnD,+FAA+F;YAC/F,IAAI,KAAI,CAAC,iBAAiB,CAAC,cAAc,EAAE;gBACzC,IAAA,gCAAsB,EAAC,KAAI,CAAC,iBAAiB,CAAC,cAAc,CAAC,CAAC;aAC/D;SACF;QACD,IAAI,OAAO,CAAC,SAAS,EAAE;YACrB,KAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;SACpC;QACD,iGAAiG;QACjG,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS,EAAE;YACtC,KAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;SAC1C;aAAM;YACL,IAAM,aAAa,GAAG,OAAwD,CAAC;YAC/E,IAAI,CAAA,MAAA,aAAa,CAAC,YAAY,0CAAE,YAAY,MAAK,SAAS,EAAE;gBAC1D,KAAI,CAAC,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC;aAC7D;SACF;QACD,IAAI,OAAO,CAAC,eAAe,EAAE;YAC3B,KAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;SAChD;;IACH,CAAC;IACH,+BAAC;AAAD,CAAC,AA5ED,CAA8C,uBAAM,GA4EnD;AA5EY,4DAAwB","sourcesContent":["import { Config, 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} from '../constants';\nimport { SessionReplayOptions, StoreType } from '../typings/session-replay';\nimport {\n SessionReplayLocalConfig as ISessionReplayLocalConfig,\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 omitElementTags?: {\n script?: boolean;\n comment?: boolean;\n };\n enableUrlChangePolling?: boolean;\n urlChangePollingInterval?: number;\n captureDocumentTitle?: boolean;\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 ?? 'idb';\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\n if (options.privacyConfig) {\n this.privacyConfig = options.privacyConfig;\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 if (options.omitElementTags) {\n this.omitElementTags = options.omitElementTags;\n }\n }\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, 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} from '../constants';\nimport { SessionReplayOptions, StoreType } from '../typings/session-replay';\nimport {\n SessionReplayLocalConfig as ISessionReplayLocalConfig,\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 omitElementTags?: {\n script?: boolean;\n comment?: boolean;\n };\n enableUrlChangePolling?: boolean;\n urlChangePollingInterval?: number;\n captureDocumentTitle?: boolean;\n captureAdoptedStyleSheets?: boolean;\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 ?? 'idb';\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\n if (options.privacyConfig) {\n this.privacyConfig = options.privacyConfig;\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 if (options.omitElementTags) {\n this.omitElementTags = options.omitElementTags;\n }\n this.captureAdoptedStyleSheets = options.captureAdoptedStyleSheets ?? true;\n }\n}\n"]}
@@ -194,6 +194,19 @@ export interface SessionReplayLocalConfig extends IConfig {
194
194
  */
195
195
  captureDocumentTitle?: boolean;
196
196
  interactionConfig?: InteractionConfig;
197
+ /**
198
+ * When true (default), the CSS rules of any `adoptedStyleSheets` on shadow roots and
199
+ * the document are serialized **inline** within the full snapshot. This makes the snapshot
200
+ * self-contained so that shadow DOM styles are replayed correctly even if subsequent
201
+ * incremental `AdoptedStyleSheet` events are dropped in transit.
202
+ *
203
+ * Set to `false` to revert to the legacy behavior where adopted stylesheet rules are
204
+ * emitted as separate incremental events (which may be lost if delivery is unreliable).
205
+ * Only consider opting out if snapshot payload size is a critical concern.
206
+ *
207
+ * @defaultValue true
208
+ */
209
+ captureAdoptedStyleSheets?: boolean;
197
210
  }
198
211
  export interface SessionReplayJoinedConfig extends SessionReplayLocalConfig {
199
212
  captureEnabled?: boolean;
@@ -243,6 +256,12 @@ export interface SessionReplayPerformanceConfig {
243
256
  * before executing the deferred compression task, even if the browser is not idle.
244
257
  */
245
258
  timeout?: number;
259
+ /**
260
+ * If enabled, consecutive mutation events will be merged into a single event before
261
+ * compression, reducing stored event count without changing replay semantics.
262
+ * Defaults to false.
263
+ */
264
+ mergeMutations?: boolean;
246
265
  /**
247
266
  * Performance configuration for interaction tracking (clicks, scrolls).
248
267
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/config/types.ts"],"names":[],"mappings":"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"}
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":";;;AAuDa,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}\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 inputs\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 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 * Remove certain parts of the DOM from being captured. These are typically ignored when blocking by selectors.\n */\n omitElementTags?: {\n /**\n * If true, removes script tags from the DOM, but not noscript tags.\n */\n script?: boolean;\n /**\n * If true, removes comment tags from the DOM.\n */\n comment?: boolean;\n };\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\nexport interface SessionReplayJoinedConfig extends SessionReplayLocalConfig {\n captureEnabled?: boolean;\n interactionConfig?: InteractionConfig;\n loggingConfig?: LoggingConfig;\n targetingConfig?: TargetingConfig;\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 * 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":";;;AAuDa,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}\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 inputs\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 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 * Remove certain parts of the DOM from being captured. These are typically ignored when blocking by selectors.\n */\n omitElementTags?: {\n /**\n * If true, removes script tags from the DOM, but not noscript tags.\n */\n script?: boolean;\n /**\n * If true, removes comment tags from the DOM.\n */\n comment?: boolean;\n };\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\nexport interface SessionReplayJoinedConfig extends SessionReplayLocalConfig {\n captureEnabled?: boolean;\n interactionConfig?: InteractionConfig;\n loggingConfig?: LoggingConfig;\n targetingConfig?: TargetingConfig;\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"]}
@@ -7,6 +7,7 @@ interface TaskQueue {
7
7
  }
8
8
  export declare class EventCompressor {
9
9
  taskQueue: TaskQueue[];
10
+ pendingQueue: TaskQueue[];
10
11
  isProcessing: boolean;
11
12
  eventsManager?: SessionReplayEventsManager<'replay' | 'interaction', string>;
12
13
  config: SessionReplayJoinedConfig;
@@ -22,6 +23,7 @@ export declare class EventCompressor {
22
23
  compressEvent: (event: eventWithTime) => string;
23
24
  private addCompressedEventToManager;
24
25
  addCompressedEvent: (event: eventWithTime, sessionId: string | number) => void;
26
+ private mergeMutationTasks;
25
27
  terminate: () => void;
26
28
  }
27
29
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"event-compressor.d.ts","sourceRoot":"","sources":["../../../src/events/event-compressor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAEvE,UAAU,SAAS;IACjB,KAAK,EAAE,aAAa,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5B;AAGD,qBAAa,eAAe;IAC1B,SAAS,EAAE,SAAS,EAAE,CAAM;IAC5B,YAAY,UAAS;IACrB,aAAa,CAAC,EAAE,0BAA0B,CAAC,QAAQ,GAAG,aAAa,EAAE,MAAM,CAAC,CAAC;IAC7E,MAAM,EAAE,yBAAyB,CAAC;IAClC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,kBAAkB,EAAE,OAAO,GAAG,SAAS,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uBAAuB,CAAC,EAAE,MAAM,IAAI,CAAC;gBAGnC,aAAa,EAAE,0BAA0B,CAAC,QAAQ,GAAG,aAAa,EAAE,MAAM,CAAC,EAC3E,MAAM,EAAE,yBAAyB,EACjC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,YAAY,CAAC,EAAE,MAAM,EACrB,uBAAuB,CAAC,EAAE,MAAM,IAAI;IAuC/B,sBAAsB,IAAI,IAAI;IAa9B,YAAY,CAAC,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAkCpE,YAAY,CAAC,YAAY,EAAE,YAAY,GAAG,IAAI;IAuBrD,aAAa,UAAW,aAAa,KAAG,MAAM,CAS5C;IAEF,OAAO,CAAC,2BAA2B,CAQjC;IAEK,kBAAkB,UAAW,aAAa,aAAa,MAAM,GAAG,MAAM,UAkB3E;IAEK,SAAS,aAEd;CACH"}
1
+ {"version":3,"file":"event-compressor.d.ts","sourceRoot":"","sources":["../../../src/events/event-compressor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAGvE,UAAU,SAAS;IACjB,KAAK,EAAE,aAAa,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5B;AAGD,qBAAa,eAAe;IAC1B,SAAS,EAAE,SAAS,EAAE,CAAM;IAC5B,YAAY,EAAE,SAAS,EAAE,CAAM;IAC/B,YAAY,UAAS;IACrB,aAAa,CAAC,EAAE,0BAA0B,CAAC,QAAQ,GAAG,aAAa,EAAE,MAAM,CAAC,CAAC;IAC7E,MAAM,EAAE,yBAAyB,CAAC;IAClC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,kBAAkB,EAAE,OAAO,GAAG,SAAS,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uBAAuB,CAAC,EAAE,MAAM,IAAI,CAAC;gBAGnC,aAAa,EAAE,0BAA0B,CAAC,QAAQ,GAAG,aAAa,EAAE,MAAM,CAAC,EAC3E,MAAM,EAAE,yBAAyB,EACjC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,YAAY,CAAC,EAAE,MAAM,EACrB,uBAAuB,CAAC,EAAE,MAAM,IAAI;IAuC/B,sBAAsB,IAAI,IAAI;IAa9B,YAAY,CAAC,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAmCpE,YAAY,CAAC,YAAY,EAAE,YAAY,GAAG,IAAI;IA6BrD,aAAa,UAAW,aAAa,KAAG,MAAM,CAS5C;IAEF,OAAO,CAAC,2BAA2B,CAQjC;IAEK,kBAAkB,UAAW,aAAa,aAAa,MAAM,GAAG,MAAM,UAkB3E;IAKF,OAAO,CAAC,kBAAkB;IA4BnB,SAAS,aAEd;CACH"}
@@ -4,12 +4,14 @@ exports.EventCompressor = void 0;
4
4
  var tslib_1 = require("tslib");
5
5
  var analytics_core_1 = require("@amplitude/analytics-core");
6
6
  var rrweb_types_1 = require("@amplitude/rrweb-types");
7
+ var merge_mutation_events_1 = require("./merge-mutation-events");
7
8
  var DEFAULT_TIMEOUT = 2000;
8
9
  var EventCompressor = /** @class */ (function () {
9
10
  function EventCompressor(eventsManager, config, deviceId, workerScript, onFullSnapshotProcessed) {
10
11
  var _this = this;
11
12
  var _a;
12
13
  this.taskQueue = [];
14
+ this.pendingQueue = [];
13
15
  this.isProcessing = false;
14
16
  this.compressEvent = function (event) {
15
17
  // Serialize with type+timestamp first for streaming parser compatibility.
@@ -109,10 +111,11 @@ var EventCompressor = /** @class */ (function () {
109
111
  // Those events reference the pre-snapshot DOM and must be sent before
110
112
  // the full snapshot; if we let them be processed later they'd arrive at
111
113
  // the server after the snapshot and cause "node not found" replay errors.
112
- if (this.taskQueue.length > 0) {
114
+ if (this.taskQueue.length > 0 || this.pendingQueue.length > 0) {
115
+ var allTasks = tslib_1.__spreadArray(tslib_1.__spreadArray([], tslib_1.__read(this.taskQueue.splice(0)), false), tslib_1.__read(this.mergeMutationTasks(this.pendingQueue.splice(0))), false);
113
116
  try {
114
- for (var _d = tslib_1.__values(this.taskQueue.splice(0)), _e = _d.next(); !_e.done; _e = _d.next()) {
115
- var task = _e.value;
117
+ for (var allTasks_1 = tslib_1.__values(allTasks), allTasks_1_1 = allTasks_1.next(); !allTasks_1_1.done; allTasks_1_1 = allTasks_1.next()) {
118
+ var task = allTasks_1_1.value;
116
119
  var compressed = this.compressEvent(task.event);
117
120
  this.addCompressedEventToManager(compressed, task.sessionId);
118
121
  }
@@ -120,7 +123,7 @@ var EventCompressor = /** @class */ (function () {
120
123
  catch (e_1_1) { e_1 = { error: e_1_1 }; }
121
124
  finally {
122
125
  try {
123
- if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
126
+ if (allTasks_1_1 && !allTasks_1_1.done && (_a = allTasks_1.return)) _a.call(allTasks_1);
124
127
  }
125
128
  finally { if (e_1) throw e_1.error; }
126
129
  }
@@ -133,7 +136,7 @@ var EventCompressor = /** @class */ (function () {
133
136
  }
134
137
  if (this.canUseIdleCallback && ((_c = this.config.performanceConfig) === null || _c === void 0 ? void 0 : _c.enabled)) {
135
138
  this.config.loggerProvider.debug('Enqueuing event for processing during idle time.');
136
- this.taskQueue.push({ event: event, sessionId: sessionId });
139
+ this.pendingQueue.push({ event: event, sessionId: sessionId });
137
140
  this.scheduleIdleProcessing();
138
141
  }
139
142
  else {
@@ -143,7 +146,14 @@ var EventCompressor = /** @class */ (function () {
143
146
  };
144
147
  // Process the task queue during idle time
145
148
  EventCompressor.prototype.processQueue = function (idleDeadline) {
149
+ var _a;
146
150
  var _this = this;
151
+ // Merge newly-arrived pending events and append to the already-merged taskQueue.
152
+ // Keeping them separate prevents re-merging already-merged tasks on subsequent calls,
153
+ // which would corrupt move semantics for nodes that appear in multiple merge passes.
154
+ if (this.pendingQueue.length > 0) {
155
+ (_a = this.taskQueue).push.apply(_a, tslib_1.__spreadArray([], tslib_1.__read(this.mergeMutationTasks(this.pendingQueue.splice(0))), false));
156
+ }
147
157
  // Process tasks while there's idle time or until the max number of tasks is reached
148
158
  while (this.taskQueue.length > 0 && (idleDeadline.timeRemaining() > 0 || idleDeadline.didTimeout)) {
149
159
  var task = this.taskQueue.shift();
@@ -153,7 +163,7 @@ var EventCompressor = /** @class */ (function () {
153
163
  }
154
164
  }
155
165
  // If there are still tasks in the queue, schedule the next idle callback
156
- if (this.taskQueue.length > 0) {
166
+ if (this.taskQueue.length > 0 || this.pendingQueue.length > 0) {
157
167
  requestIdleCallback(function (idleDeadline) {
158
168
  _this.processQueue(idleDeadline);
159
169
  }, { timeout: this.timeout });
@@ -162,6 +172,44 @@ var EventCompressor = /** @class */ (function () {
162
172
  this.isProcessing = false;
163
173
  }
164
174
  };
175
+ // Merge consecutive mutation tasks with the same sessionId before processing,
176
+ // reducing the number of events serialized and stored without changing replay semantics.
177
+ // Only runs when performanceConfig.mergeMutations is explicitly enabled.
178
+ EventCompressor.prototype.mergeMutationTasks = function (tasks) {
179
+ var e_2, _a;
180
+ var _b;
181
+ if (!((_b = this.config.performanceConfig) === null || _b === void 0 ? void 0 : _b.mergeMutations))
182
+ return tasks;
183
+ if (tasks.length <= 1)
184
+ return tasks;
185
+ var result = [];
186
+ var i = 0;
187
+ while (i < tasks.length) {
188
+ var sessionId = tasks[i].sessionId;
189
+ // Find the end of the current session run
190
+ var j = i + 1;
191
+ while (j < tasks.length && tasks[j].sessionId === sessionId) {
192
+ j++;
193
+ }
194
+ // Merge consecutive mutations within this session run; non-mutations pass through unchanged
195
+ var merged = (0, merge_mutation_events_1.mergeMutationEvents)(tasks.slice(i, j).map(function (t) { return t.event; }));
196
+ try {
197
+ for (var merged_1 = (e_2 = void 0, tslib_1.__values(merged)), merged_1_1 = merged_1.next(); !merged_1_1.done; merged_1_1 = merged_1.next()) {
198
+ var event_2 = merged_1_1.value;
199
+ result.push({ event: event_2, sessionId: sessionId });
200
+ }
201
+ }
202
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
203
+ finally {
204
+ try {
205
+ if (merged_1_1 && !merged_1_1.done && (_a = merged_1.return)) _a.call(merged_1);
206
+ }
207
+ finally { if (e_2) throw e_2.error; }
208
+ }
209
+ i = j;
210
+ }
211
+ return result;
212
+ };
165
213
  return EventCompressor;
166
214
  }());
167
215
  exports.EventCompressor = EventCompressor;
@@ -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 { SessionReplayEventsManager } from '../typings/session-replay';\n\ninterface TaskQueue {\n event: eventWithTime;\n sessionId: string | number;\n}\n\nconst DEFAULT_TIMEOUT = 2000;\nexport class EventCompressor {\n taskQueue: 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) {\n for (const task of this.taskQueue.splice(0)) {\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.taskQueue.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 // 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) {\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 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 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 { 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 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 // 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"]}
@@ -0,0 +1,10 @@
1
+ import type { eventWithTime } from '@amplitude/rrweb-types';
2
+ /**
3
+ * Merges consecutive IncrementalSnapshot mutation events into a single event,
4
+ * reducing overall event count without changing replay semantics.
5
+ *
6
+ * isAttachIframe events are never merged — they carry a full iframe document
7
+ * tree and must remain isolated.
8
+ */
9
+ export declare function mergeMutationEvents(events: eventWithTime[]): eventWithTime[];
10
+ //# sourceMappingURL=merge-mutation-events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-mutation-events.d.ts","sourceRoot":"","sources":["../../../src/events/merge-mutation-events.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAgB,MAAM,wBAAwB,CAAC;AAiK1E;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,aAAa,EAAE,CAuB5E"}