@posthog/rrweb 0.0.25 → 0.0.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/rrweb.d.cts CHANGED
@@ -282,9 +282,16 @@ export declare class Replayer {
282
282
  private lastSelectionData;
283
283
  private constructedStyleMutations;
284
284
  private adoptedStyleSheets;
285
+ private emitterHandlers;
286
+ private serviceSubscription?;
287
+ private speedServiceSubscription?;
288
+ private timeouts;
289
+ private styleSheetLoadListeners;
285
290
  constructor(events: Array<eventWithTime | string>, config?: Partial<playerConfig>);
286
291
  on(event: string, handler: Handler): this;
287
292
  off(event: string, handler: Handler): this;
293
+ private addEmitterHandler;
294
+ private addTimeout;
288
295
  setConfig(config: Partial<playerConfig>): void;
289
296
  getMetaData(): playerMetaData;
290
297
  getCurrentTime(): number;
package/dist/rrweb.d.ts CHANGED
@@ -282,9 +282,16 @@ export declare class Replayer {
282
282
  private lastSelectionData;
283
283
  private constructedStyleMutations;
284
284
  private adoptedStyleSheets;
285
+ private emitterHandlers;
286
+ private serviceSubscription?;
287
+ private speedServiceSubscription?;
288
+ private timeouts;
289
+ private styleSheetLoadListeners;
285
290
  constructor(events: Array<eventWithTime | string>, config?: Partial<playerConfig>);
286
291
  on(event: string, handler: Handler): this;
287
292
  off(event: string, handler: Handler): this;
293
+ private addEmitterHandler;
294
+ private addTimeout;
288
295
  setConfig(config: Partial<playerConfig>): void;
289
296
  getMetaData(): playerMetaData;
290
297
  getCurrentTime(): number;
package/dist/rrweb.js CHANGED
@@ -13227,6 +13227,7 @@ function initObservers(o2, hooks = {}) {
13227
13227
  selectionObserver();
13228
13228
  customElementObserver();
13229
13229
  pluginHandlers.forEach((h) => h());
13230
+ mutationBuffers.length = 0;
13230
13231
  });
13231
13232
  }
13232
13233
  function hasNestedCSSRule(prop) {
@@ -13313,6 +13314,9 @@ class IframeManager {
13313
13314
  __publicField(this, "loadListener");
13314
13315
  __publicField(this, "stylesheetManager");
13315
13316
  __publicField(this, "recordCrossOriginIframes");
13317
+ __publicField(this, "messageHandler");
13318
+ // Map window to handler for cleanup - windows are browser-owned and won't prevent GC
13319
+ __publicField(this, "nestedIframeListeners", /* @__PURE__ */ new Map());
13316
13320
  this.mutationCb = options.mutationCb;
13317
13321
  this.wrappedEmit = options.wrappedEmit;
13318
13322
  this.stylesheetManager = options.stylesheetManager;
@@ -13323,8 +13327,9 @@ class IframeManager {
13323
13327
  )
13324
13328
  );
13325
13329
  this.mirror = options.mirror;
13330
+ this.messageHandler = this.handleMessage.bind(this);
13326
13331
  if (this.recordCrossOriginIframes) {
13327
- window.addEventListener("message", this.handleMessage.bind(this));
13332
+ window.addEventListener("message", this.messageHandler);
13328
13333
  }
13329
13334
  }
13330
13335
  addIframe(iframeEl) {
@@ -13335,8 +13340,11 @@ class IframeManager {
13335
13340
  addLoadListener(cb) {
13336
13341
  this.loadListener = cb;
13337
13342
  }
13343
+ removeLoadListener() {
13344
+ this.loadListener = void 0;
13345
+ }
13338
13346
  attachIframe(iframeEl, childSn) {
13339
- var _a2, _b;
13347
+ var _a2;
13340
13348
  this.mutationCb({
13341
13349
  adds: [
13342
13350
  {
@@ -13350,12 +13358,13 @@ class IframeManager {
13350
13358
  attributes: [],
13351
13359
  isAttachIframe: true
13352
13360
  });
13353
- if (this.recordCrossOriginIframes)
13354
- (_a2 = iframeEl.contentWindow) == null ? void 0 : _a2.addEventListener(
13355
- "message",
13356
- this.handleMessage.bind(this)
13357
- );
13358
- (_b = this.loadListener) == null ? void 0 : _b.call(this, iframeEl);
13361
+ const win = iframeEl.contentWindow;
13362
+ if (this.recordCrossOriginIframes && win && !this.nestedIframeListeners.has(win)) {
13363
+ const nestedHandler = this.handleMessage.bind(this);
13364
+ this.nestedIframeListeners.set(win, nestedHandler);
13365
+ win.addEventListener("message", nestedHandler);
13366
+ }
13367
+ (_a2 = this.loadListener) == null ? void 0 : _a2.call(this, iframeEl);
13359
13368
  if (iframeEl.contentDocument && iframeEl.contentDocument.adoptedStyleSheets && iframeEl.contentDocument.adoptedStyleSheets.length > 0)
13360
13369
  this.stylesheetManager.adoptStyleSheets(
13361
13370
  iframeEl.contentDocument.adoptedStyleSheets,
@@ -13533,6 +13542,15 @@ class IframeManager {
13533
13542
  });
13534
13543
  }
13535
13544
  }
13545
+ destroy() {
13546
+ if (this.recordCrossOriginIframes) {
13547
+ window.removeEventListener("message", this.messageHandler);
13548
+ }
13549
+ this.nestedIframeListeners.forEach((handler, contentWindow) => {
13550
+ contentWindow.removeEventListener("message", handler);
13551
+ });
13552
+ this.nestedIframeListeners.clear();
13553
+ }
13536
13554
  }
13537
13555
  class ShadowDomManager {
13538
13556
  constructor(options) {
@@ -14727,13 +14745,14 @@ function record(options = {}) {
14727
14745
  hooks
14728
14746
  );
14729
14747
  };
14730
- iframeManager.addLoadListener((iframeEl) => {
14748
+ const loadListener = (iframeEl) => {
14731
14749
  try {
14732
14750
  handlers.push(observe(iframeEl.contentDocument));
14733
14751
  } catch (error) {
14734
14752
  console.warn(error);
14735
14753
  }
14736
- });
14754
+ };
14755
+ iframeManager.addLoadListener(loadListener);
14737
14756
  const init = () => {
14738
14757
  takeFullSnapshot$1();
14739
14758
  handlers.push(observe(document));
@@ -14768,6 +14787,8 @@ function record(options = {}) {
14768
14787
  return () => {
14769
14788
  handlers.forEach((h) => h());
14770
14789
  processedNodeManager.destroy();
14790
+ iframeManager.removeLoadListener();
14791
+ iframeManager.destroy();
14771
14792
  recording = false;
14772
14793
  unregisterErrorHandler();
14773
14794
  };
@@ -15990,6 +16011,12 @@ class Replayer {
15990
16011
  __publicField(this, "constructedStyleMutations", []);
15991
16012
  // Similar to the reason for constructedStyleMutations.
15992
16013
  __publicField(this, "adoptedStyleSheets", []);
16014
+ // Track resources for cleanup
16015
+ __publicField(this, "emitterHandlers", []);
16016
+ __publicField(this, "serviceSubscription");
16017
+ __publicField(this, "speedServiceSubscription");
16018
+ __publicField(this, "timeouts", /* @__PURE__ */ new Set());
16019
+ __publicField(this, "styleSheetLoadListeners", /* @__PURE__ */ new Map());
15993
16020
  __publicField(this, "handleResize", (dimension) => {
15994
16021
  this.iframe.style.display = "inherit";
15995
16022
  for (const el of [this.mouseTail, this.iframe]) {
@@ -16149,12 +16176,12 @@ class Replayer {
16149
16176
  this.handleResize = this.handleResize.bind(this);
16150
16177
  this.getCastFn = this.getCastFn.bind(this);
16151
16178
  this.applyEventsSynchronously = this.applyEventsSynchronously.bind(this);
16152
- this.emitter.on(ReplayerEvents.Resize, this.handleResize);
16179
+ this.addEmitterHandler(ReplayerEvents.Resize, this.handleResize);
16153
16180
  this.setupDom();
16154
16181
  for (const plugin of this.config.plugins || []) {
16155
16182
  if (plugin.getMirror) plugin.getMirror({ nodeMirror: this.mirror });
16156
16183
  }
16157
- this.emitter.on(ReplayerEvents.Flush, () => {
16184
+ const flushHandler = () => {
16158
16185
  if (this.usingVirtualDom) {
16159
16186
  const replayerHandler = {
16160
16187
  mirror: this.mirror,
@@ -16250,13 +16277,15 @@ class Replayer {
16250
16277
  this.applySelection(this.lastSelectionData);
16251
16278
  this.lastSelectionData = null;
16252
16279
  }
16253
- });
16254
- this.emitter.on(ReplayerEvents.PlayBack, () => {
16280
+ };
16281
+ this.addEmitterHandler(ReplayerEvents.Flush, flushHandler);
16282
+ const playBackHandler = () => {
16255
16283
  this.firstFullSnapshot = null;
16256
16284
  this.mirror.reset();
16257
16285
  this.styleMirror.reset();
16258
16286
  this.mediaManager.reset();
16259
- });
16287
+ };
16288
+ this.addEmitterHandler(ReplayerEvents.PlayBack, playBackHandler);
16260
16289
  const timer = new Timer([], {
16261
16290
  speed: this.config.speed
16262
16291
  });
@@ -16280,7 +16309,7 @@ class Replayer {
16280
16309
  }
16281
16310
  );
16282
16311
  this.service.start();
16283
- this.service.subscribe((state) => {
16312
+ this.serviceSubscription = this.service.subscribe((state) => {
16284
16313
  this.emitter.emit(ReplayerEvents.StateChange, {
16285
16314
  player: state
16286
16315
  });
@@ -16290,7 +16319,7 @@ class Replayer {
16290
16319
  timer
16291
16320
  });
16292
16321
  this.speedService.start();
16293
- this.speedService.subscribe((state) => {
16322
+ this.speedServiceSubscription = this.speedService.subscribe((state) => {
16294
16323
  this.emitter.emit(ReplayerEvents.StateChange, {
16295
16324
  speed: state
16296
16325
  });
@@ -16310,7 +16339,7 @@ class Replayer {
16310
16339
  );
16311
16340
  if (firstMeta) {
16312
16341
  const { width, height } = firstMeta.data;
16313
- setTimeout(() => {
16342
+ this.addTimeout(() => {
16314
16343
  this.emitter.emit(ReplayerEvents.Resize, {
16315
16344
  width,
16316
16345
  height
@@ -16318,7 +16347,7 @@ class Replayer {
16318
16347
  }, 0);
16319
16348
  }
16320
16349
  if (firstFullsnapshot) {
16321
- setTimeout(() => {
16350
+ this.addTimeout(() => {
16322
16351
  var _a2;
16323
16352
  if (this.firstFullSnapshot) {
16324
16353
  return;
@@ -16347,6 +16376,24 @@ class Replayer {
16347
16376
  this.emitter.off(event, handler);
16348
16377
  return this;
16349
16378
  }
16379
+ /**
16380
+ * Track emitter handlers for cleanup
16381
+ */
16382
+ addEmitterHandler(event, handler) {
16383
+ this.emitter.on(event, handler);
16384
+ this.emitterHandlers.push({ event, handler });
16385
+ }
16386
+ /**
16387
+ * Track timeouts for cleanup
16388
+ */
16389
+ addTimeout(callback, delay) {
16390
+ const timeout = setTimeout(() => {
16391
+ this.timeouts.delete(timeout);
16392
+ callback();
16393
+ }, delay);
16394
+ this.timeouts.add(timeout);
16395
+ return timeout;
16396
+ }
16350
16397
  setConfig(config) {
16351
16398
  Object.keys(config).forEach((key) => {
16352
16399
  config[key];
@@ -16449,10 +16496,31 @@ class Replayer {
16449
16496
  * Memory occupation can be released by removing all references to this replayer.
16450
16497
  */
16451
16498
  destroy() {
16499
+ var _a2, _b;
16500
+ if (!this.wrapper || !this.wrapper.parentNode) {
16501
+ return;
16502
+ }
16452
16503
  this.pause();
16504
+ this.emitterHandlers.forEach(({ event, handler }) => {
16505
+ this.emitter.off(event, handler);
16506
+ });
16507
+ this.emitterHandlers = [];
16508
+ (_a2 = this.serviceSubscription) == null ? void 0 : _a2.unsubscribe();
16509
+ (_b = this.speedServiceSubscription) == null ? void 0 : _b.unsubscribe();
16510
+ this.serviceSubscription = void 0;
16511
+ this.speedServiceSubscription = void 0;
16512
+ this.timeouts.forEach((timeout) => clearTimeout(timeout));
16513
+ this.timeouts.clear();
16514
+ this.styleSheetLoadListeners.forEach((handler, element) => {
16515
+ element.removeEventListener("load", handler);
16516
+ });
16517
+ this.styleSheetLoadListeners.clear();
16518
+ this.imageMap.clear();
16519
+ this.canvasEventMap.clear();
16453
16520
  this.mirror.reset();
16454
16521
  this.styleMirror.reset();
16455
16522
  this.mediaManager.reset();
16523
+ this.resetCache();
16456
16524
  this.config.root.removeChild(this.wrapper);
16457
16525
  this.emitter.emit(ReplayerEvents.Destroy);
16458
16526
  }