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