@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.
@@ -251,6 +251,91 @@ function isShadowRoot(n2) {
251
251
  function isNativeShadowDom(shadowRoot2) {
252
252
  return Object.prototype.toString.call(shadowRoot2) === "[object ShadowRoot]";
253
253
  }
254
+ function fixBrowserCompatibilityIssuesInCSS(cssText) {
255
+ if (cssText.includes(" background-clip: text;") && !cssText.includes(" -webkit-background-clip: text;")) {
256
+ cssText = cssText.replace(
257
+ /\sbackground-clip:\s*text;/g,
258
+ " -webkit-background-clip: text; background-clip: text;"
259
+ );
260
+ }
261
+ return cssText;
262
+ }
263
+ function escapeImportStatement(rule2) {
264
+ const { cssText } = rule2;
265
+ if (cssText.split('"').length < 3) return cssText;
266
+ const statement = ["@import", `url(${JSON.stringify(rule2.href)})`];
267
+ if (rule2.layerName === "") {
268
+ statement.push(`layer`);
269
+ } else if (rule2.layerName) {
270
+ statement.push(`layer(${rule2.layerName})`);
271
+ }
272
+ if (rule2.supportsText) {
273
+ statement.push(`supports(${rule2.supportsText})`);
274
+ }
275
+ if (rule2.media.length) {
276
+ statement.push(rule2.media.mediaText);
277
+ }
278
+ return statement.join(" ") + ";";
279
+ }
280
+ function stringifyStylesheet(s2) {
281
+ try {
282
+ const rules2 = s2.rules || s2.cssRules;
283
+ if (!rules2) {
284
+ return null;
285
+ }
286
+ let sheetHref = s2.href;
287
+ if (!sheetHref && s2.ownerNode) {
288
+ sheetHref = s2.ownerNode.baseURI;
289
+ }
290
+ const stringifiedRules = Array.from(
291
+ rules2,
292
+ (rule2) => stringifyRule(rule2, sheetHref)
293
+ ).join("");
294
+ return fixBrowserCompatibilityIssuesInCSS(stringifiedRules);
295
+ } catch (error) {
296
+ return null;
297
+ }
298
+ }
299
+ function stringifyRule(rule2, sheetHref) {
300
+ var _a2;
301
+ if (isCSSImportRule(rule2)) {
302
+ let importStringified;
303
+ try {
304
+ importStringified = // for same-origin stylesheets,
305
+ // we can access the imported stylesheet rules directly
306
+ stringifyStylesheet(rule2.styleSheet) || // work around browser issues with the raw string `@import url(...)` statement
307
+ escapeImportStatement(rule2);
308
+ } catch (error) {
309
+ importStringified = rule2.cssText;
310
+ }
311
+ try {
312
+ if (importStringified && ((_a2 = rule2.styleSheet) == null ? void 0 : _a2.href)) {
313
+ return absolutifyURLs(importStringified, rule2.styleSheet.href);
314
+ }
315
+ } catch (e) {
316
+ }
317
+ return importStringified;
318
+ } else {
319
+ let ruleStringified = rule2.cssText;
320
+ if (isCSSStyleRule(rule2) && rule2.selectorText.includes(":")) {
321
+ ruleStringified = fixSafariColons(ruleStringified);
322
+ }
323
+ if (sheetHref) {
324
+ return absolutifyURLs(ruleStringified, sheetHref);
325
+ }
326
+ return ruleStringified;
327
+ }
328
+ }
329
+ function fixSafariColons(cssStringified) {
330
+ const regex = /(\[(?:[\w-]+)[^\\])(:(?:[\w-]+)\])/gm;
331
+ return cssStringified.replace(regex, "$1\\$2");
332
+ }
333
+ function isCSSImportRule(rule2) {
334
+ return "styleSheet" in rule2;
335
+ }
336
+ function isCSSStyleRule(rule2) {
337
+ return "selectorText" in rule2;
338
+ }
254
339
  class Mirror {
255
340
  constructor() {
256
341
  __publicField$1(this, "idNodeMap", /* @__PURE__ */ new Map());
@@ -374,91 +459,6 @@ function extractFileExtension(path, baseURL) {
374
459
  const match = url.pathname.match(regex);
375
460
  return (_a2 = match == null ? void 0 : match[1]) != null ? _a2 : null;
376
461
  }
377
- function fixBrowserCompatibilityIssuesInCSS(cssText) {
378
- if (cssText.includes(" background-clip: text;") && !cssText.includes(" -webkit-background-clip: text;")) {
379
- cssText = cssText.replace(
380
- /\sbackground-clip:\s*text;/g,
381
- " -webkit-background-clip: text; background-clip: text;"
382
- );
383
- }
384
- return cssText;
385
- }
386
- function escapeImportStatement(rule2) {
387
- const { cssText } = rule2;
388
- if (cssText.split('"').length < 3) return cssText;
389
- const statement = ["@import", `url(${JSON.stringify(rule2.href)})`];
390
- if (rule2.layerName === "") {
391
- statement.push(`layer`);
392
- } else if (rule2.layerName) {
393
- statement.push(`layer(${rule2.layerName})`);
394
- }
395
- if (rule2.supportsText) {
396
- statement.push(`supports(${rule2.supportsText})`);
397
- }
398
- if (rule2.media.length) {
399
- statement.push(rule2.media.mediaText);
400
- }
401
- return statement.join(" ") + ";";
402
- }
403
- function stringifyStylesheet(s2) {
404
- try {
405
- const rules2 = s2.rules || s2.cssRules;
406
- if (!rules2) {
407
- return null;
408
- }
409
- let sheetHref = s2.href;
410
- if (!sheetHref && s2.ownerNode) {
411
- sheetHref = s2.ownerNode.baseURI;
412
- }
413
- const stringifiedRules = Array.from(
414
- rules2,
415
- (rule2) => stringifyRule(rule2, sheetHref)
416
- ).join("");
417
- return fixBrowserCompatibilityIssuesInCSS(stringifiedRules);
418
- } catch (error) {
419
- return null;
420
- }
421
- }
422
- function stringifyRule(rule2, sheetHref) {
423
- var _a2;
424
- if (isCSSImportRule(rule2)) {
425
- let importStringified;
426
- try {
427
- importStringified = // for same-origin stylesheets,
428
- // we can access the imported stylesheet rules directly
429
- stringifyStylesheet(rule2.styleSheet) || // work around browser issues with the raw string `@import url(...)` statement
430
- escapeImportStatement(rule2);
431
- } catch (error) {
432
- importStringified = rule2.cssText;
433
- }
434
- try {
435
- if (importStringified && ((_a2 = rule2.styleSheet) == null ? void 0 : _a2.href)) {
436
- return absolutifyURLs(importStringified, rule2.styleSheet.href);
437
- }
438
- } catch (e) {
439
- }
440
- return importStringified;
441
- } else {
442
- let ruleStringified = rule2.cssText;
443
- if (isCSSStyleRule(rule2) && rule2.selectorText.includes(":")) {
444
- ruleStringified = fixSafariColons(ruleStringified);
445
- }
446
- if (sheetHref) {
447
- return absolutifyURLs(ruleStringified, sheetHref);
448
- }
449
- return ruleStringified;
450
- }
451
- }
452
- function fixSafariColons(cssStringified) {
453
- const regex = /(\[(?:[\w-]+)[^\\])(:(?:[\w-]+)\])/gm;
454
- return cssStringified.replace(regex, "$1\\$2");
455
- }
456
- function isCSSImportRule(rule2) {
457
- return "styleSheet" in rule2;
458
- }
459
- function isCSSStyleRule(rule2) {
460
- return "selectorText" in rule2;
461
- }
462
462
  function extractOrigin(url) {
463
463
  let origin = "";
464
464
  if (url.indexOf("//") > -1) {
@@ -507,6 +507,36 @@ function absolutifyURLs(cssText, href) {
507
507
  }
508
508
  );
509
509
  }
510
+ const STRIPED_PLACEHOLDER_SVG = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxkZWZzPgogICAgPHBhdHRlcm4gaWQ9InN0cmlwZXMiIHBhdHRlcm5Vbml0cz0idXNlclNwYWNlT25Vc2UiIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+CiAgICAgIDxyZWN0IHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iYmxhY2siLz4KICAgICAgPHBhdGggZD0iTTggMEgxNkwwIDE2VjhMOCAwWiIgZmlsbD0iIzJEMkQyRCIvPgogICAgICA8cGF0aCBkPSJNMTYgOFYxNkg4TDE2IDhaIiBmaWxsPSIjMkQyRDJEIi8+CiAgICA8L3BhdHRlcm4+CiAgPC9kZWZzPgogIDxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjc3RyaXBlcykiLz4KPC9zdmc+Cg==";
511
+ const MAX_IMAGE_DIMENSION_FOR_RECOMPRESSION = 4096;
512
+ function recompressBase64Image(img, dataURL, type, quality) {
513
+ if (!img.complete || img.naturalWidth === 0) {
514
+ return dataURL;
515
+ }
516
+ if (img.naturalWidth > MAX_IMAGE_DIMENSION_FOR_RECOMPRESSION || img.naturalHeight > MAX_IMAGE_DIMENSION_FOR_RECOMPRESSION) {
517
+ return dataURL;
518
+ }
519
+ try {
520
+ const canvas = document.createElement("canvas");
521
+ canvas.width = img.naturalWidth;
522
+ canvas.height = img.naturalHeight;
523
+ const ctx = canvas.getContext("2d");
524
+ if (!ctx) {
525
+ return dataURL;
526
+ }
527
+ ctx.drawImage(img, 0, 0);
528
+ const recompressed = canvas.toDataURL(type || "image/webp", quality != null ? quality : 0.4);
529
+ return recompressed;
530
+ } catch (err) {
531
+ return dataURL;
532
+ }
533
+ }
534
+ function checkDataURLSize(dataURL, maxLength) {
535
+ if (!maxLength || dataURL.length <= maxLength) {
536
+ return dataURL;
537
+ }
538
+ return STRIPED_PLACEHOLDER_SVG;
539
+ }
510
540
  let _id = 1;
511
541
  const tagNameRegex = new RegExp("[^a-z0-9-_:]");
512
542
  const IGNORED_NODE = -2;
@@ -605,12 +635,32 @@ function getHref(doc, customHref) {
605
635
  a2.setAttribute("href", customHref);
606
636
  return a2.href;
607
637
  }
608
- function transformAttribute(doc, tagName, name, value) {
638
+ function transformAttribute(doc, tagName, name, value, element, dataURLOptions) {
609
639
  if (!value) {
610
640
  return value;
611
641
  }
612
642
  if (name === "src" || name === "href" && !(tagName === "use" && value[0] === "#")) {
613
- return absoluteToDoc(doc, value);
643
+ const transformedValue = absoluteToDoc(doc, value);
644
+ if (tagName === "img" && transformedValue.startsWith("data:") && element) {
645
+ const img = element;
646
+ let processedDataURL = transformedValue;
647
+ if ((dataURLOptions == null ? void 0 : dataURLOptions.type) || (dataURLOptions == null ? void 0 : dataURLOptions.quality) !== void 0) {
648
+ processedDataURL = recompressBase64Image(
649
+ img,
650
+ transformedValue,
651
+ dataURLOptions.type,
652
+ dataURLOptions.quality
653
+ );
654
+ }
655
+ if (dataURLOptions == null ? void 0 : dataURLOptions.maxBase64ImageLength) {
656
+ processedDataURL = checkDataURLSize(
657
+ processedDataURL,
658
+ dataURLOptions.maxBase64ImageLength
659
+ );
660
+ }
661
+ return processedDataURL;
662
+ }
663
+ return transformedValue;
614
664
  } else if (name === "xlink:href" && value[0] !== "#") {
615
665
  return absoluteToDoc(doc, value);
616
666
  } else if (name === "background" && (tagName === "table" || tagName === "td" || tagName === "th")) {
@@ -901,7 +951,9 @@ function serializeElementNode(n2, options) {
901
951
  doc,
902
952
  tagName,
903
953
  toLowerCase(attr.name),
904
- attr.value
954
+ attr.value,
955
+ n2,
956
+ dataURLOptions
905
957
  );
906
958
  }
907
959
  }
@@ -10178,7 +10230,9 @@ class MutationBuffer {
10178
10230
  this.doc,
10179
10231
  toLowerCase(target.tagName),
10180
10232
  toLowerCase(attributeName),
10181
- value
10233
+ value,
10234
+ target,
10235
+ this.dataURLOptions
10182
10236
  );
10183
10237
  if (attributeName === "style") {
10184
10238
  if (!this.unattachedDoc) {
@@ -10337,6 +10391,11 @@ class MutationBuffer {
10337
10391
  this.shadowDomManager.reset();
10338
10392
  this.canvasManager.reset();
10339
10393
  }
10394
+ destroy() {
10395
+ while (this.mapRemoves.length) {
10396
+ this.mirror.removeNodeFromMap(this.mapRemoves.shift());
10397
+ }
10398
+ }
10340
10399
  }
10341
10400
  function deepDelete(addsSet, n2) {
10342
10401
  addsSet.delete(n2);
@@ -11381,6 +11440,7 @@ function initObservers(o2, hooks = {}) {
11381
11440
  );
11382
11441
  }
11383
11442
  return callbackWrapper(() => {
11443
+ mutationBuffers.forEach((b) => b.destroy());
11384
11444
  mutationBuffers.forEach((b) => b.reset());
11385
11445
  mutationObserver == null ? void 0 : mutationObserver.disconnect();
11386
11446
  mousemoveHandler();
@@ -11718,6 +11778,8 @@ class IframeManager {
11718
11778
  contentWindow.removeEventListener("message", handler);
11719
11779
  });
11720
11780
  this.nestedIframeListeners.clear();
11781
+ this.crossOriginIframeMirror.reset();
11782
+ this.crossOriginIframeStyleMirror.reset();
11721
11783
  }
11722
11784
  }
11723
11785
  class ShadowDomManager {
@@ -12174,6 +12236,8 @@ class CanvasManager {
12174
12236
  __publicField(this, "resetObservers");
12175
12237
  __publicField(this, "frozen", false);
12176
12238
  __publicField(this, "locked", false);
12239
+ __publicField(this, "rafIdTimestamp", null);
12240
+ __publicField(this, "rafIdFlush", null);
12177
12241
  __publicField(this, "processMutation", (target, mutation) => {
12178
12242
  const newFrame = this.rafStamps.invokeId && this.rafStamps.latestId !== this.rafStamps.invokeId;
12179
12243
  if (newFrame || !this.rafStamps.invokeId)
@@ -12208,6 +12272,14 @@ class CanvasManager {
12208
12272
  reset() {
12209
12273
  this.pendingCanvasMutations.clear();
12210
12274
  this.resetObservers && this.resetObservers();
12275
+ if (this.rafIdTimestamp !== null) {
12276
+ cancelAnimationFrame(this.rafIdTimestamp);
12277
+ this.rafIdTimestamp = null;
12278
+ }
12279
+ if (this.rafIdFlush !== null) {
12280
+ cancelAnimationFrame(this.rafIdFlush);
12281
+ this.rafIdFlush = null;
12282
+ }
12211
12283
  }
12212
12284
  freeze() {
12213
12285
  this.frozen = true;
@@ -12358,14 +12430,16 @@ class CanvasManager {
12358
12430
  };
12359
12431
  }
12360
12432
  startPendingCanvasMutationFlusher() {
12361
- requestAnimationFrame(() => this.flushPendingCanvasMutations());
12433
+ this.rafIdFlush = requestAnimationFrame(
12434
+ () => this.flushPendingCanvasMutations()
12435
+ );
12362
12436
  }
12363
12437
  startRAFTimestamping() {
12364
12438
  const setLatestRAFTimestamp = (timestamp) => {
12365
12439
  this.rafStamps.latestId = timestamp;
12366
- requestAnimationFrame(setLatestRAFTimestamp);
12440
+ this.rafIdTimestamp = requestAnimationFrame(setLatestRAFTimestamp);
12367
12441
  };
12368
- requestAnimationFrame(setLatestRAFTimestamp);
12442
+ this.rafIdTimestamp = requestAnimationFrame(setLatestRAFTimestamp);
12369
12443
  }
12370
12444
  flushPendingCanvasMutations() {
12371
12445
  this.pendingCanvasMutations.forEach(
@@ -12374,7 +12448,9 @@ class CanvasManager {
12374
12448
  this.flushPendingCanvasMutationFor(canvas, id);
12375
12449
  }
12376
12450
  );
12377
- requestAnimationFrame(() => this.flushPendingCanvasMutations());
12451
+ this.rafIdFlush = requestAnimationFrame(
12452
+ () => this.flushPendingCanvasMutations()
12453
+ );
12378
12454
  }
12379
12455
  flushPendingCanvasMutationFor(canvas, id) {
12380
12456
  if (this.frozen || this.locked) {
@@ -12509,7 +12585,7 @@ function record(options = {}) {
12509
12585
  hooks,
12510
12586
  packFn,
12511
12587
  sampling = {},
12512
- dataURLOptions = {},
12588
+ dataURLOptions: _dataURLOptions = {},
12513
12589
  mousemoveWait,
12514
12590
  recordDOM = true,
12515
12591
  recordCanvas = false,
@@ -12524,6 +12600,11 @@ function record(options = {}) {
12524
12600
  errorHandler: errorHandler2
12525
12601
  } = options;
12526
12602
  registerErrorHandler(errorHandler2);
12603
+ const dataURLOptions = __spreadValues({
12604
+ type: "image/webp",
12605
+ quality: 0.4,
12606
+ maxBase64ImageLength: 1048576
12607
+ }, _dataURLOptions);
12527
12608
  const inEmittingFrame = recordCrossOriginIframes ? window.parent === window : true;
12528
12609
  let passEmitsToParent = false;
12529
12610
  if (!inEmittingFrame) {
@@ -12940,6 +13021,7 @@ function record(options = {}) {
12940
13021
  processedNodeManager.destroy();
12941
13022
  iframeManager.removeLoadListener();
12942
13023
  iframeManager.destroy();
13024
+ mirror.reset();
12943
13025
  recording = false;
12944
13026
  unregisterErrorHandler();
12945
13027
  };