@posthog/rrweb 0.0.24 → 0.0.26

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
@@ -238,8 +238,8 @@ function stringifyStylesheet(s2) {
238
238
  return null;
239
239
  }
240
240
  let sheetHref = s2.href;
241
- if (!sheetHref && s2.ownerNode && s2.ownerNode.ownerDocument) {
242
- sheetHref = s2.ownerNode.ownerDocument.baseURI;
241
+ if (!sheetHref && s2.ownerNode) {
242
+ sheetHref = s2.ownerNode.baseURI;
243
243
  }
244
244
  const stringifiedRules = Array.from(
245
245
  rules2,
@@ -5640,6 +5640,12 @@ function buildNodeWithSN(n2, options) {
5640
5640
  } else {
5641
5641
  node2.appendChild(childNode);
5642
5642
  }
5643
+ } else if (n2.type === NodeType$1$1.Document && childN.type === NodeType$1$1.DocumentType) {
5644
+ if (node2.firstChild && node2.firstChild.nodeType === 1) {
5645
+ node2.insertBefore(childNode, node2.firstChild);
5646
+ } else {
5647
+ node2.appendChild(childNode);
5648
+ }
5643
5649
  } else {
5644
5650
  node2.appendChild(childNode);
5645
5651
  }
@@ -13221,6 +13227,7 @@ function initObservers(o2, hooks = {}) {
13221
13227
  selectionObserver();
13222
13228
  customElementObserver();
13223
13229
  pluginHandlers.forEach((h) => h());
13230
+ mutationBuffers.length = 0;
13224
13231
  });
13225
13232
  }
13226
13233
  function hasNestedCSSRule(prop) {
@@ -13307,6 +13314,9 @@ class IframeManager {
13307
13314
  __publicField(this, "loadListener");
13308
13315
  __publicField(this, "stylesheetManager");
13309
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());
13310
13320
  this.mutationCb = options.mutationCb;
13311
13321
  this.wrappedEmit = options.wrappedEmit;
13312
13322
  this.stylesheetManager = options.stylesheetManager;
@@ -13317,8 +13327,9 @@ class IframeManager {
13317
13327
  )
13318
13328
  );
13319
13329
  this.mirror = options.mirror;
13330
+ this.messageHandler = this.handleMessage.bind(this);
13320
13331
  if (this.recordCrossOriginIframes) {
13321
- window.addEventListener("message", this.handleMessage.bind(this));
13332
+ window.addEventListener("message", this.messageHandler);
13322
13333
  }
13323
13334
  }
13324
13335
  addIframe(iframeEl) {
@@ -13330,7 +13341,7 @@ class IframeManager {
13330
13341
  this.loadListener = cb;
13331
13342
  }
13332
13343
  attachIframe(iframeEl, childSn) {
13333
- var _a2, _b;
13344
+ var _a2;
13334
13345
  this.mutationCb({
13335
13346
  adds: [
13336
13347
  {
@@ -13344,12 +13355,13 @@ class IframeManager {
13344
13355
  attributes: [],
13345
13356
  isAttachIframe: true
13346
13357
  });
13347
- if (this.recordCrossOriginIframes)
13348
- (_a2 = iframeEl.contentWindow) == null ? void 0 : _a2.addEventListener(
13349
- "message",
13350
- this.handleMessage.bind(this)
13351
- );
13352
- (_b = this.loadListener) == null ? void 0 : _b.call(this, iframeEl);
13358
+ const win = iframeEl.contentWindow;
13359
+ if (this.recordCrossOriginIframes && win && !this.nestedIframeListeners.has(win)) {
13360
+ const nestedHandler = this.handleMessage.bind(this);
13361
+ this.nestedIframeListeners.set(win, nestedHandler);
13362
+ win.addEventListener("message", nestedHandler);
13363
+ }
13364
+ (_a2 = this.loadListener) == null ? void 0 : _a2.call(this, iframeEl);
13353
13365
  if (iframeEl.contentDocument && iframeEl.contentDocument.adoptedStyleSheets && iframeEl.contentDocument.adoptedStyleSheets.length > 0)
13354
13366
  this.stylesheetManager.adoptStyleSheets(
13355
13367
  iframeEl.contentDocument.adoptedStyleSheets,
@@ -13527,6 +13539,15 @@ class IframeManager {
13527
13539
  });
13528
13540
  }
13529
13541
  }
13542
+ destroy() {
13543
+ if (this.recordCrossOriginIframes) {
13544
+ window.removeEventListener("message", this.messageHandler);
13545
+ }
13546
+ this.nestedIframeListeners.forEach((handler, contentWindow) => {
13547
+ contentWindow.removeEventListener("message", handler);
13548
+ });
13549
+ this.nestedIframeListeners.clear();
13550
+ }
13530
13551
  }
13531
13552
  class ShadowDomManager {
13532
13553
  constructor(options) {
@@ -14762,6 +14783,7 @@ function record(options = {}) {
14762
14783
  return () => {
14763
14784
  handlers.forEach((h) => h());
14764
14785
  processedNodeManager.destroy();
14786
+ iframeManager.destroy();
14765
14787
  recording = false;
14766
14788
  unregisterErrorHandler();
14767
14789
  };
@@ -15984,6 +16006,12 @@ class Replayer {
15984
16006
  __publicField(this, "constructedStyleMutations", []);
15985
16007
  // Similar to the reason for constructedStyleMutations.
15986
16008
  __publicField(this, "adoptedStyleSheets", []);
16009
+ // Track resources for cleanup
16010
+ __publicField(this, "emitterHandlers", []);
16011
+ __publicField(this, "serviceSubscription");
16012
+ __publicField(this, "speedServiceSubscription");
16013
+ __publicField(this, "timeouts", /* @__PURE__ */ new Set());
16014
+ __publicField(this, "styleSheetLoadListeners", /* @__PURE__ */ new Map());
15987
16015
  __publicField(this, "handleResize", (dimension) => {
15988
16016
  this.iframe.style.display = "inherit";
15989
16017
  for (const el of [this.mouseTail, this.iframe]) {
@@ -16143,12 +16171,12 @@ class Replayer {
16143
16171
  this.handleResize = this.handleResize.bind(this);
16144
16172
  this.getCastFn = this.getCastFn.bind(this);
16145
16173
  this.applyEventsSynchronously = this.applyEventsSynchronously.bind(this);
16146
- this.emitter.on(ReplayerEvents.Resize, this.handleResize);
16174
+ this.addEmitterHandler(ReplayerEvents.Resize, this.handleResize);
16147
16175
  this.setupDom();
16148
16176
  for (const plugin of this.config.plugins || []) {
16149
16177
  if (plugin.getMirror) plugin.getMirror({ nodeMirror: this.mirror });
16150
16178
  }
16151
- this.emitter.on(ReplayerEvents.Flush, () => {
16179
+ const flushHandler = () => {
16152
16180
  if (this.usingVirtualDom) {
16153
16181
  const replayerHandler = {
16154
16182
  mirror: this.mirror,
@@ -16244,13 +16272,15 @@ class Replayer {
16244
16272
  this.applySelection(this.lastSelectionData);
16245
16273
  this.lastSelectionData = null;
16246
16274
  }
16247
- });
16248
- this.emitter.on(ReplayerEvents.PlayBack, () => {
16275
+ };
16276
+ this.addEmitterHandler(ReplayerEvents.Flush, flushHandler);
16277
+ const playBackHandler = () => {
16249
16278
  this.firstFullSnapshot = null;
16250
16279
  this.mirror.reset();
16251
16280
  this.styleMirror.reset();
16252
16281
  this.mediaManager.reset();
16253
- });
16282
+ };
16283
+ this.addEmitterHandler(ReplayerEvents.PlayBack, playBackHandler);
16254
16284
  const timer = new Timer([], {
16255
16285
  speed: this.config.speed
16256
16286
  });
@@ -16274,7 +16304,7 @@ class Replayer {
16274
16304
  }
16275
16305
  );
16276
16306
  this.service.start();
16277
- this.service.subscribe((state) => {
16307
+ this.serviceSubscription = this.service.subscribe((state) => {
16278
16308
  this.emitter.emit(ReplayerEvents.StateChange, {
16279
16309
  player: state
16280
16310
  });
@@ -16284,7 +16314,7 @@ class Replayer {
16284
16314
  timer
16285
16315
  });
16286
16316
  this.speedService.start();
16287
- this.speedService.subscribe((state) => {
16317
+ this.speedServiceSubscription = this.speedService.subscribe((state) => {
16288
16318
  this.emitter.emit(ReplayerEvents.StateChange, {
16289
16319
  speed: state
16290
16320
  });
@@ -16304,7 +16334,7 @@ class Replayer {
16304
16334
  );
16305
16335
  if (firstMeta) {
16306
16336
  const { width, height } = firstMeta.data;
16307
- setTimeout(() => {
16337
+ this.addTimeout(() => {
16308
16338
  this.emitter.emit(ReplayerEvents.Resize, {
16309
16339
  width,
16310
16340
  height
@@ -16312,7 +16342,7 @@ class Replayer {
16312
16342
  }, 0);
16313
16343
  }
16314
16344
  if (firstFullsnapshot) {
16315
- setTimeout(() => {
16345
+ this.addTimeout(() => {
16316
16346
  var _a2;
16317
16347
  if (this.firstFullSnapshot) {
16318
16348
  return;
@@ -16341,6 +16371,24 @@ class Replayer {
16341
16371
  this.emitter.off(event, handler);
16342
16372
  return this;
16343
16373
  }
16374
+ /**
16375
+ * Track emitter handlers for cleanup
16376
+ */
16377
+ addEmitterHandler(event, handler) {
16378
+ this.emitter.on(event, handler);
16379
+ this.emitterHandlers.push({ event, handler });
16380
+ }
16381
+ /**
16382
+ * Track timeouts for cleanup
16383
+ */
16384
+ addTimeout(callback, delay) {
16385
+ const timeout = setTimeout(() => {
16386
+ this.timeouts.delete(timeout);
16387
+ callback();
16388
+ }, delay);
16389
+ this.timeouts.add(timeout);
16390
+ return timeout;
16391
+ }
16344
16392
  setConfig(config) {
16345
16393
  Object.keys(config).forEach((key) => {
16346
16394
  config[key];
@@ -16443,10 +16491,31 @@ class Replayer {
16443
16491
  * Memory occupation can be released by removing all references to this replayer.
16444
16492
  */
16445
16493
  destroy() {
16494
+ var _a2, _b;
16495
+ if (!this.wrapper || !this.wrapper.parentNode) {
16496
+ return;
16497
+ }
16446
16498
  this.pause();
16499
+ this.emitterHandlers.forEach(({ event, handler }) => {
16500
+ this.emitter.off(event, handler);
16501
+ });
16502
+ this.emitterHandlers = [];
16503
+ (_a2 = this.serviceSubscription) == null ? void 0 : _a2.unsubscribe();
16504
+ (_b = this.speedServiceSubscription) == null ? void 0 : _b.unsubscribe();
16505
+ this.serviceSubscription = void 0;
16506
+ this.speedServiceSubscription = void 0;
16507
+ this.timeouts.forEach((timeout) => clearTimeout(timeout));
16508
+ this.timeouts.clear();
16509
+ this.styleSheetLoadListeners.forEach((handler, element) => {
16510
+ element.removeEventListener("load", handler);
16511
+ });
16512
+ this.styleSheetLoadListeners.clear();
16513
+ this.imageMap.clear();
16514
+ this.canvasEventMap.clear();
16447
16515
  this.mirror.reset();
16448
16516
  this.styleMirror.reset();
16449
16517
  this.mediaManager.reset();
16518
+ this.resetCache();
16450
16519
  this.config.root.removeChild(this.wrapper);
16451
16520
  this.emitter.emit(ReplayerEvents.Destroy);
16452
16521
  }