@posthog/rrweb-record 0.0.28 → 0.0.30

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.
@@ -205,6 +205,91 @@ function isShadowRoot(n2) {
205
205
  function isNativeShadowDom(shadowRoot2) {
206
206
  return Object.prototype.toString.call(shadowRoot2) === "[object ShadowRoot]";
207
207
  }
208
+ function fixBrowserCompatibilityIssuesInCSS(cssText) {
209
+ if (cssText.includes(" background-clip: text;") && !cssText.includes(" -webkit-background-clip: text;")) {
210
+ cssText = cssText.replace(
211
+ /\sbackground-clip:\s*text;/g,
212
+ " -webkit-background-clip: text; background-clip: text;"
213
+ );
214
+ }
215
+ return cssText;
216
+ }
217
+ function escapeImportStatement(rule2) {
218
+ const { cssText } = rule2;
219
+ if (cssText.split('"').length < 3) return cssText;
220
+ const statement = ["@import", `url(${JSON.stringify(rule2.href)})`];
221
+ if (rule2.layerName === "") {
222
+ statement.push(`layer`);
223
+ } else if (rule2.layerName) {
224
+ statement.push(`layer(${rule2.layerName})`);
225
+ }
226
+ if (rule2.supportsText) {
227
+ statement.push(`supports(${rule2.supportsText})`);
228
+ }
229
+ if (rule2.media.length) {
230
+ statement.push(rule2.media.mediaText);
231
+ }
232
+ return statement.join(" ") + ";";
233
+ }
234
+ function stringifyStylesheet(s2) {
235
+ try {
236
+ const rules2 = s2.rules || s2.cssRules;
237
+ if (!rules2) {
238
+ return null;
239
+ }
240
+ let sheetHref = s2.href;
241
+ if (!sheetHref && s2.ownerNode) {
242
+ sheetHref = s2.ownerNode.baseURI;
243
+ }
244
+ const stringifiedRules = Array.from(
245
+ rules2,
246
+ (rule2) => stringifyRule(rule2, sheetHref)
247
+ ).join("");
248
+ return fixBrowserCompatibilityIssuesInCSS(stringifiedRules);
249
+ } catch (error) {
250
+ return null;
251
+ }
252
+ }
253
+ function stringifyRule(rule2, sheetHref) {
254
+ var _a2;
255
+ if (isCSSImportRule(rule2)) {
256
+ let importStringified;
257
+ try {
258
+ importStringified = // for same-origin stylesheets,
259
+ // we can access the imported stylesheet rules directly
260
+ stringifyStylesheet(rule2.styleSheet) || // work around browser issues with the raw string `@import url(...)` statement
261
+ escapeImportStatement(rule2);
262
+ } catch (error) {
263
+ importStringified = rule2.cssText;
264
+ }
265
+ try {
266
+ if (importStringified && ((_a2 = rule2.styleSheet) == null ? void 0 : _a2.href)) {
267
+ return absolutifyURLs(importStringified, rule2.styleSheet.href);
268
+ }
269
+ } catch {
270
+ }
271
+ return importStringified;
272
+ } else {
273
+ let ruleStringified = rule2.cssText;
274
+ if (isCSSStyleRule(rule2) && rule2.selectorText.includes(":")) {
275
+ ruleStringified = fixSafariColons(ruleStringified);
276
+ }
277
+ if (sheetHref) {
278
+ return absolutifyURLs(ruleStringified, sheetHref);
279
+ }
280
+ return ruleStringified;
281
+ }
282
+ }
283
+ function fixSafariColons(cssStringified) {
284
+ const regex = /(\[(?:[\w-]+)[^\\])(:(?:[\w-]+)\])/gm;
285
+ return cssStringified.replace(regex, "$1\\$2");
286
+ }
287
+ function isCSSImportRule(rule2) {
288
+ return "styleSheet" in rule2;
289
+ }
290
+ function isCSSStyleRule(rule2) {
291
+ return "selectorText" in rule2;
292
+ }
208
293
  class Mirror {
209
294
  constructor() {
210
295
  __publicField$1(this, "idNodeMap", /* @__PURE__ */ new Map());
@@ -327,91 +412,6 @@ function extractFileExtension(path, baseURL) {
327
412
  const match = url.pathname.match(regex);
328
413
  return (match == null ? void 0 : match[1]) ?? null;
329
414
  }
330
- function fixBrowserCompatibilityIssuesInCSS(cssText) {
331
- if (cssText.includes(" background-clip: text;") && !cssText.includes(" -webkit-background-clip: text;")) {
332
- cssText = cssText.replace(
333
- /\sbackground-clip:\s*text;/g,
334
- " -webkit-background-clip: text; background-clip: text;"
335
- );
336
- }
337
- return cssText;
338
- }
339
- function escapeImportStatement(rule2) {
340
- const { cssText } = rule2;
341
- if (cssText.split('"').length < 3) return cssText;
342
- const statement = ["@import", `url(${JSON.stringify(rule2.href)})`];
343
- if (rule2.layerName === "") {
344
- statement.push(`layer`);
345
- } else if (rule2.layerName) {
346
- statement.push(`layer(${rule2.layerName})`);
347
- }
348
- if (rule2.supportsText) {
349
- statement.push(`supports(${rule2.supportsText})`);
350
- }
351
- if (rule2.media.length) {
352
- statement.push(rule2.media.mediaText);
353
- }
354
- return statement.join(" ") + ";";
355
- }
356
- function stringifyStylesheet(s2) {
357
- try {
358
- const rules2 = s2.rules || s2.cssRules;
359
- if (!rules2) {
360
- return null;
361
- }
362
- let sheetHref = s2.href;
363
- if (!sheetHref && s2.ownerNode) {
364
- sheetHref = s2.ownerNode.baseURI;
365
- }
366
- const stringifiedRules = Array.from(
367
- rules2,
368
- (rule2) => stringifyRule(rule2, sheetHref)
369
- ).join("");
370
- return fixBrowserCompatibilityIssuesInCSS(stringifiedRules);
371
- } catch (error) {
372
- return null;
373
- }
374
- }
375
- function stringifyRule(rule2, sheetHref) {
376
- var _a2;
377
- if (isCSSImportRule(rule2)) {
378
- let importStringified;
379
- try {
380
- importStringified = // for same-origin stylesheets,
381
- // we can access the imported stylesheet rules directly
382
- stringifyStylesheet(rule2.styleSheet) || // work around browser issues with the raw string `@import url(...)` statement
383
- escapeImportStatement(rule2);
384
- } catch (error) {
385
- importStringified = rule2.cssText;
386
- }
387
- try {
388
- if (importStringified && ((_a2 = rule2.styleSheet) == null ? void 0 : _a2.href)) {
389
- return absolutifyURLs(importStringified, rule2.styleSheet.href);
390
- }
391
- } catch {
392
- }
393
- return importStringified;
394
- } else {
395
- let ruleStringified = rule2.cssText;
396
- if (isCSSStyleRule(rule2) && rule2.selectorText.includes(":")) {
397
- ruleStringified = fixSafariColons(ruleStringified);
398
- }
399
- if (sheetHref) {
400
- return absolutifyURLs(ruleStringified, sheetHref);
401
- }
402
- return ruleStringified;
403
- }
404
- }
405
- function fixSafariColons(cssStringified) {
406
- const regex = /(\[(?:[\w-]+)[^\\])(:(?:[\w-]+)\])/gm;
407
- return cssStringified.replace(regex, "$1\\$2");
408
- }
409
- function isCSSImportRule(rule2) {
410
- return "styleSheet" in rule2;
411
- }
412
- function isCSSStyleRule(rule2) {
413
- return "selectorText" in rule2;
414
- }
415
415
  function extractOrigin(url) {
416
416
  let origin = "";
417
417
  if (url.indexOf("//") > -1) {
@@ -460,6 +460,36 @@ function absolutifyURLs(cssText, href) {
460
460
  }
461
461
  );
462
462
  }
463
+ const STRIPED_PLACEHOLDER_SVG = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxkZWZzPgogICAgPHBhdHRlcm4gaWQ9InN0cmlwZXMiIHBhdHRlcm5Vbml0cz0idXNlclNwYWNlT25Vc2UiIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+CiAgICAgIDxyZWN0IHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iYmxhY2siLz4KICAgICAgPHBhdGggZD0iTTggMEgxNkwwIDE2VjhMOCAwWiIgZmlsbD0iIzJEMkQyRCIvPgogICAgICA8cGF0aCBkPSJNMTYgOFYxNkg4TDE2IDhaIiBmaWxsPSIjMkQyRDJEIi8+CiAgICA8L3BhdHRlcm4+CiAgPC9kZWZzPgogIDxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjc3RyaXBlcykiLz4KPC9zdmc+Cg==";
464
+ const MAX_IMAGE_DIMENSION_FOR_RECOMPRESSION = 4096;
465
+ function recompressBase64Image(img, dataURL, type, quality) {
466
+ if (!img.complete || img.naturalWidth === 0) {
467
+ return dataURL;
468
+ }
469
+ if (img.naturalWidth > MAX_IMAGE_DIMENSION_FOR_RECOMPRESSION || img.naturalHeight > MAX_IMAGE_DIMENSION_FOR_RECOMPRESSION) {
470
+ return dataURL;
471
+ }
472
+ try {
473
+ const canvas = document.createElement("canvas");
474
+ canvas.width = img.naturalWidth;
475
+ canvas.height = img.naturalHeight;
476
+ const ctx = canvas.getContext("2d");
477
+ if (!ctx) {
478
+ return dataURL;
479
+ }
480
+ ctx.drawImage(img, 0, 0);
481
+ const recompressed = canvas.toDataURL(type || "image/webp", quality ?? 0.4);
482
+ return recompressed;
483
+ } catch (err) {
484
+ return dataURL;
485
+ }
486
+ }
487
+ function checkDataURLSize(dataURL, maxLength) {
488
+ if (!maxLength || dataURL.length <= maxLength) {
489
+ return dataURL;
490
+ }
491
+ return STRIPED_PLACEHOLDER_SVG;
492
+ }
463
493
  let _id = 1;
464
494
  const tagNameRegex = new RegExp("[^a-z0-9-_:]");
465
495
  const IGNORED_NODE = -2;
@@ -558,12 +588,32 @@ function getHref(doc, customHref) {
558
588
  a2.setAttribute("href", customHref);
559
589
  return a2.href;
560
590
  }
561
- function transformAttribute(doc, tagName, name, value) {
591
+ function transformAttribute(doc, tagName, name, value, element, dataURLOptions) {
562
592
  if (!value) {
563
593
  return value;
564
594
  }
565
595
  if (name === "src" || name === "href" && !(tagName === "use" && value[0] === "#")) {
566
- return absoluteToDoc(doc, value);
596
+ const transformedValue = absoluteToDoc(doc, value);
597
+ if (tagName === "img" && transformedValue.startsWith("data:") && element) {
598
+ const img = element;
599
+ let processedDataURL = transformedValue;
600
+ if ((dataURLOptions == null ? void 0 : dataURLOptions.type) || (dataURLOptions == null ? void 0 : dataURLOptions.quality) !== void 0) {
601
+ processedDataURL = recompressBase64Image(
602
+ img,
603
+ transformedValue,
604
+ dataURLOptions.type,
605
+ dataURLOptions.quality
606
+ );
607
+ }
608
+ if (dataURLOptions == null ? void 0 : dataURLOptions.maxBase64ImageLength) {
609
+ processedDataURL = checkDataURLSize(
610
+ processedDataURL,
611
+ dataURLOptions.maxBase64ImageLength
612
+ );
613
+ }
614
+ return processedDataURL;
615
+ }
616
+ return transformedValue;
567
617
  } else if (name === "xlink:href" && value[0] !== "#") {
568
618
  return absoluteToDoc(doc, value);
569
619
  } else if (name === "background" && (tagName === "table" || tagName === "td" || tagName === "th")) {
@@ -854,7 +904,9 @@ function serializeElementNode(n2, options) {
854
904
  doc,
855
905
  tagName,
856
906
  toLowerCase(attr.name),
857
- attr.value
907
+ attr.value,
908
+ n2,
909
+ dataURLOptions
858
910
  );
859
911
  }
860
912
  }
@@ -10132,7 +10184,9 @@ class MutationBuffer {
10132
10184
  this.doc,
10133
10185
  toLowerCase(target.tagName),
10134
10186
  toLowerCase(attributeName),
10135
- value
10187
+ value,
10188
+ target,
10189
+ this.dataURLOptions
10136
10190
  );
10137
10191
  if (attributeName === "style") {
10138
10192
  if (!this.unattachedDoc) {
@@ -10291,6 +10345,11 @@ class MutationBuffer {
10291
10345
  this.shadowDomManager.reset();
10292
10346
  this.canvasManager.reset();
10293
10347
  }
10348
+ destroy() {
10349
+ while (this.mapRemoves.length) {
10350
+ this.mirror.removeNodeFromMap(this.mapRemoves.shift());
10351
+ }
10352
+ }
10294
10353
  }
10295
10354
  function deepDelete(addsSet, n2) {
10296
10355
  addsSet.delete(n2);
@@ -11337,6 +11396,7 @@ function initObservers(o2, hooks = {}) {
11337
11396
  );
11338
11397
  }
11339
11398
  return callbackWrapper(() => {
11399
+ mutationBuffers.forEach((b) => b.destroy());
11340
11400
  mutationBuffers.forEach((b) => b.reset());
11341
11401
  mutationObserver == null ? void 0 : mutationObserver.disconnect();
11342
11402
  mousemoveHandler();
@@ -11674,6 +11734,8 @@ class IframeManager {
11674
11734
  contentWindow.removeEventListener("message", handler);
11675
11735
  });
11676
11736
  this.nestedIframeListeners.clear();
11737
+ this.crossOriginIframeMirror.reset();
11738
+ this.crossOriginIframeStyleMirror.reset();
11677
11739
  }
11678
11740
  }
11679
11741
  class ShadowDomManager {
@@ -12132,6 +12194,8 @@ class CanvasManager {
12132
12194
  __publicField(this, "resetObservers");
12133
12195
  __publicField(this, "frozen", false);
12134
12196
  __publicField(this, "locked", false);
12197
+ __publicField(this, "rafIdTimestamp", null);
12198
+ __publicField(this, "rafIdFlush", null);
12135
12199
  __publicField(this, "processMutation", (target, mutation) => {
12136
12200
  const newFrame = this.rafStamps.invokeId && this.rafStamps.latestId !== this.rafStamps.invokeId;
12137
12201
  if (newFrame || !this.rafStamps.invokeId)
@@ -12166,6 +12230,14 @@ class CanvasManager {
12166
12230
  reset() {
12167
12231
  this.pendingCanvasMutations.clear();
12168
12232
  this.resetObservers && this.resetObservers();
12233
+ if (this.rafIdTimestamp !== null) {
12234
+ cancelAnimationFrame(this.rafIdTimestamp);
12235
+ this.rafIdTimestamp = null;
12236
+ }
12237
+ if (this.rafIdFlush !== null) {
12238
+ cancelAnimationFrame(this.rafIdFlush);
12239
+ this.rafIdFlush = null;
12240
+ }
12169
12241
  }
12170
12242
  freeze() {
12171
12243
  this.frozen = true;
@@ -12316,14 +12388,16 @@ class CanvasManager {
12316
12388
  };
12317
12389
  }
12318
12390
  startPendingCanvasMutationFlusher() {
12319
- requestAnimationFrame(() => this.flushPendingCanvasMutations());
12391
+ this.rafIdFlush = requestAnimationFrame(
12392
+ () => this.flushPendingCanvasMutations()
12393
+ );
12320
12394
  }
12321
12395
  startRAFTimestamping() {
12322
12396
  const setLatestRAFTimestamp = (timestamp) => {
12323
12397
  this.rafStamps.latestId = timestamp;
12324
- requestAnimationFrame(setLatestRAFTimestamp);
12398
+ this.rafIdTimestamp = requestAnimationFrame(setLatestRAFTimestamp);
12325
12399
  };
12326
- requestAnimationFrame(setLatestRAFTimestamp);
12400
+ this.rafIdTimestamp = requestAnimationFrame(setLatestRAFTimestamp);
12327
12401
  }
12328
12402
  flushPendingCanvasMutations() {
12329
12403
  this.pendingCanvasMutations.forEach(
@@ -12332,7 +12406,9 @@ class CanvasManager {
12332
12406
  this.flushPendingCanvasMutationFor(canvas, id);
12333
12407
  }
12334
12408
  );
12335
- requestAnimationFrame(() => this.flushPendingCanvasMutations());
12409
+ this.rafIdFlush = requestAnimationFrame(
12410
+ () => this.flushPendingCanvasMutations()
12411
+ );
12336
12412
  }
12337
12413
  flushPendingCanvasMutationFor(canvas, id) {
12338
12414
  if (this.frozen || this.locked) {
@@ -12467,7 +12543,7 @@ function record(options = {}) {
12467
12543
  hooks,
12468
12544
  packFn,
12469
12545
  sampling = {},
12470
- dataURLOptions = {},
12546
+ dataURLOptions: _dataURLOptions = {},
12471
12547
  mousemoveWait,
12472
12548
  recordDOM = true,
12473
12549
  recordCanvas = false,
@@ -12482,6 +12558,12 @@ function record(options = {}) {
12482
12558
  errorHandler: errorHandler2
12483
12559
  } = options;
12484
12560
  registerErrorHandler(errorHandler2);
12561
+ const dataURLOptions = {
12562
+ type: "image/webp",
12563
+ quality: 0.4,
12564
+ maxBase64ImageLength: 1048576,
12565
+ ..._dataURLOptions
12566
+ };
12485
12567
  const inEmittingFrame = recordCrossOriginIframes ? window.parent === window : true;
12486
12568
  let passEmitsToParent = false;
12487
12569
  if (!inEmittingFrame) {
@@ -12911,6 +12993,7 @@ function record(options = {}) {
12911
12993
  processedNodeManager.destroy();
12912
12994
  iframeManager.removeLoadListener();
12913
12995
  iframeManager.destroy();
12996
+ mirror.reset();
12914
12997
  recording = false;
12915
12998
  unregisterErrorHandler();
12916
12999
  };