@openreplay/tracker 16.4.10 → 16.4.11-beta.0

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.
Files changed (48) hide show
  1. package/dist/cjs/common/messages.gen.d.ts +57 -7
  2. package/dist/cjs/entry.js +502 -252
  3. package/dist/cjs/entry.js.map +1 -1
  4. package/dist/cjs/index.js +502 -252
  5. package/dist/cjs/index.js.map +1 -1
  6. package/dist/cjs/main/app/guards.d.ts +1 -0
  7. package/dist/cjs/main/app/index.d.ts +5 -6
  8. package/dist/cjs/main/app/messages.gen.d.ts +7 -2
  9. package/dist/cjs/main/app/nodes/index.d.ts +1 -2
  10. package/dist/cjs/main/app/observer/observer.d.ts +4 -0
  11. package/dist/cjs/main/app/observer/top_observer.d.ts +3 -2
  12. package/dist/cjs/main/index.d.ts +9 -1
  13. package/dist/cjs/main/modules/longAnimationTask.d.ts +25 -0
  14. package/dist/cjs/main/modules/tagWatcher.d.ts +1 -1
  15. package/dist/cjs/main/modules/webAnimations.d.ts +9 -0
  16. package/dist/cjs/main/utils.d.ts +3 -0
  17. package/dist/lib/common/messages.gen.d.ts +57 -7
  18. package/dist/lib/entry.js +502 -252
  19. package/dist/lib/entry.js.map +1 -1
  20. package/dist/lib/index.js +502 -252
  21. package/dist/lib/index.js.map +1 -1
  22. package/dist/lib/main/app/guards.d.ts +1 -0
  23. package/dist/lib/main/app/index.d.ts +5 -6
  24. package/dist/lib/main/app/messages.gen.d.ts +7 -2
  25. package/dist/lib/main/app/nodes/index.d.ts +1 -2
  26. package/dist/lib/main/app/observer/observer.d.ts +4 -0
  27. package/dist/lib/main/app/observer/top_observer.d.ts +3 -2
  28. package/dist/lib/main/index.d.ts +9 -1
  29. package/dist/lib/main/modules/longAnimationTask.d.ts +25 -0
  30. package/dist/lib/main/modules/tagWatcher.d.ts +1 -1
  31. package/dist/lib/main/modules/webAnimations.d.ts +9 -0
  32. package/dist/lib/main/utils.d.ts +3 -0
  33. package/dist/types/common/messages.gen.d.ts +57 -7
  34. package/dist/types/main/app/guards.d.ts +1 -0
  35. package/dist/types/main/app/index.d.ts +5 -6
  36. package/dist/types/main/app/messages.gen.d.ts +7 -2
  37. package/dist/types/main/app/nodes/index.d.ts +1 -2
  38. package/dist/types/main/app/observer/observer.d.ts +4 -0
  39. package/dist/types/main/app/observer/top_observer.d.ts +3 -2
  40. package/dist/types/main/index.d.ts +9 -1
  41. package/dist/types/main/modules/longAnimationTask.d.ts +25 -0
  42. package/dist/types/main/modules/tagWatcher.d.ts +1 -1
  43. package/dist/types/main/modules/webAnimations.d.ts +9 -0
  44. package/dist/types/main/utils.d.ts +3 -0
  45. package/package.json +2 -2
  46. package/dist/cjs/main/app/nodes/idSeq.d.ts +0 -14
  47. package/dist/lib/main/app/nodes/idSeq.d.ts +0 -14
  48. package/dist/types/main/app/nodes/idSeq.d.ts +0 -14
package/dist/cjs/index.js CHANGED
@@ -1469,6 +1469,43 @@ function simpleMerge(defaultObj, givenObj) {
1469
1469
  }
1470
1470
  return result;
1471
1471
  }
1472
+ function throttleWithTrailing(fn, interval) {
1473
+ const lastCalls = new Map();
1474
+ const timeouts = new Map();
1475
+ const lastArgs = new Map();
1476
+ const throttled = function (key, ...args) {
1477
+ const now = Date.now();
1478
+ const lastCall = lastCalls.get(key) ?? 0;
1479
+ const remaining = interval - (now - lastCall);
1480
+ lastArgs.set(key, args);
1481
+ if (remaining <= 0) {
1482
+ if (timeouts.has(key)) {
1483
+ clearTimeout(timeouts.get(key));
1484
+ timeouts.delete(key);
1485
+ }
1486
+ lastCalls.set(key, now);
1487
+ fn(key, ...args);
1488
+ }
1489
+ else if (!timeouts.has(key)) {
1490
+ const timeoutId = setTimeout(() => {
1491
+ lastCalls.set(key, Date.now());
1492
+ timeouts.delete(key);
1493
+ const finalArgs = lastArgs.get(key);
1494
+ fn(key, ...finalArgs);
1495
+ }, remaining);
1496
+ timeouts.set(key, timeoutId);
1497
+ }
1498
+ };
1499
+ throttled.clear = () => {
1500
+ for (const timeout of timeouts.values()) {
1501
+ clearTimeout(timeout);
1502
+ }
1503
+ timeouts.clear();
1504
+ lastArgs.clear();
1505
+ lastCalls.clear();
1506
+ };
1507
+ return throttled;
1508
+ }
1472
1509
 
1473
1510
  // Auto-generated, do not edit
1474
1511
  /* eslint-disable */
@@ -1679,6 +1716,13 @@ function SetNodeAttributeDictGlobal(id, name, value) {
1679
1716
  value,
1680
1717
  ];
1681
1718
  }
1719
+ function NodeAnimationResult(id, styles) {
1720
+ return [
1721
+ 36 /* Messages.Type.NodeAnimationResult */,
1722
+ id,
1723
+ styles,
1724
+ ];
1725
+ }
1682
1726
  function CSSInsertRule(id, rule, index) {
1683
1727
  return [
1684
1728
  37 /* Messages.Type.CSSInsertRule */,
@@ -1807,9 +1851,9 @@ function SetNodeAttributeDict(id, name, value) {
1807
1851
  value,
1808
1852
  ];
1809
1853
  }
1810
- function ResourceTimingDeprecated(timestamp, duration, ttfb, headerSize, encodedBodySize, decodedBodySize, url, initiator) {
1854
+ function ResourceTimingDeprecatedDeprecated(timestamp, duration, ttfb, headerSize, encodedBodySize, decodedBodySize, url, initiator) {
1811
1855
  return [
1812
- 53 /* Messages.Type.ResourceTimingDeprecated */,
1856
+ 53 /* Messages.Type.ResourceTimingDeprecatedDeprecated */,
1813
1857
  timestamp,
1814
1858
  duration,
1815
1859
  ttfb,
@@ -1891,6 +1935,13 @@ function CustomIssue(name, payload) {
1891
1935
  payload,
1892
1936
  ];
1893
1937
  }
1938
+ function SetNodeSlot(id, slotID) {
1939
+ return [
1940
+ 65 /* Messages.Type.SetNodeSlot */,
1941
+ id,
1942
+ slotID,
1943
+ ];
1944
+ }
1894
1945
  function CSSInsertRuleURLBased(id, rule, index, baseURL) {
1895
1946
  return [
1896
1947
  67 /* Messages.Type.CSSInsertRuleURLBased */,
@@ -2023,6 +2074,47 @@ function WSChannel(chType, channelName, data, timestamp, dir, messageType) {
2023
2074
  messageType,
2024
2075
  ];
2025
2076
  }
2077
+ function ResourceTiming(timestamp, duration, ttfb, headerSize, encodedBodySize, decodedBodySize, url, initiator, transferredSize, cached, queueing, dnsLookup, initialConnection, ssl, contentDownload, total, stalled) {
2078
+ return [
2079
+ 85 /* Messages.Type.ResourceTiming */,
2080
+ timestamp,
2081
+ duration,
2082
+ ttfb,
2083
+ headerSize,
2084
+ encodedBodySize,
2085
+ decodedBodySize,
2086
+ url,
2087
+ initiator,
2088
+ transferredSize,
2089
+ cached,
2090
+ queueing,
2091
+ dnsLookup,
2092
+ initialConnection,
2093
+ ssl,
2094
+ contentDownload,
2095
+ total,
2096
+ stalled,
2097
+ ];
2098
+ }
2099
+ function Incident(label, startTime, endTime) {
2100
+ return [
2101
+ 87 /* Messages.Type.Incident */,
2102
+ label,
2103
+ startTime,
2104
+ endTime,
2105
+ ];
2106
+ }
2107
+ function LongAnimationTask$1(name, duration, blockingDuration, firstUIEventTimestamp, startTime, scripts) {
2108
+ return [
2109
+ 89 /* Messages.Type.LongAnimationTask */,
2110
+ name,
2111
+ duration,
2112
+ blockingDuration,
2113
+ firstUIEventTimestamp,
2114
+ startTime,
2115
+ scripts,
2116
+ ];
2117
+ }
2026
2118
  function InputChange(id, value, valueMasked, label, hesitationTime, inputDuration) {
2027
2119
  return [
2028
2120
  112 /* Messages.Type.InputChange */,
@@ -2054,9 +2146,9 @@ function UnbindNodes(totalRemovedPercent) {
2054
2146
  totalRemovedPercent,
2055
2147
  ];
2056
2148
  }
2057
- function ResourceTiming(timestamp, duration, ttfb, headerSize, encodedBodySize, decodedBodySize, url, initiator, transferredSize, cached) {
2149
+ function ResourceTimingDeprecated(timestamp, duration, ttfb, headerSize, encodedBodySize, decodedBodySize, url, initiator, transferredSize, cached) {
2058
2150
  return [
2059
- 116 /* Messages.Type.ResourceTiming */,
2151
+ 116 /* Messages.Type.ResourceTimingDeprecated */,
2060
2152
  timestamp,
2061
2153
  duration,
2062
2154
  ttfb,
@@ -2153,9 +2245,11 @@ var _Messages = /*#__PURE__*/Object.freeze({
2153
2245
  Fetch: Fetch,
2154
2246
  GraphQL: GraphQL,
2155
2247
  GraphQLDeprecated: GraphQLDeprecated,
2248
+ Incident: Incident,
2156
2249
  InputChange: InputChange,
2157
2250
  JSException: JSException,
2158
2251
  LoadFontFace: LoadFontFace,
2252
+ LongAnimationTask: LongAnimationTask$1,
2159
2253
  LongTask: LongTask,
2160
2254
  Metadata: Metadata,
2161
2255
  MobX: MobX,
@@ -2167,6 +2261,7 @@ var _Messages = /*#__PURE__*/Object.freeze({
2167
2261
  NetworkRequest: NetworkRequest,
2168
2262
  NetworkRequestDeprecated: NetworkRequestDeprecated,
2169
2263
  NgRx: NgRx,
2264
+ NodeAnimationResult: NodeAnimationResult,
2170
2265
  OTable: OTable,
2171
2266
  PageLoadTiming: PageLoadTiming,
2172
2267
  PageRenderTiming: PageRenderTiming,
@@ -2179,6 +2274,7 @@ var _Messages = /*#__PURE__*/Object.freeze({
2179
2274
  RemoveNodeAttribute: RemoveNodeAttribute,
2180
2275
  ResourceTiming: ResourceTiming,
2181
2276
  ResourceTimingDeprecated: ResourceTimingDeprecated,
2277
+ ResourceTimingDeprecatedDeprecated: ResourceTimingDeprecatedDeprecated,
2182
2278
  SelectionChange: SelectionChange,
2183
2279
  SetCSSDataURLBased: SetCSSDataURLBased,
2184
2280
  SetInputChecked: SetInputChecked,
@@ -2192,6 +2288,7 @@ var _Messages = /*#__PURE__*/Object.freeze({
2192
2288
  SetNodeData: SetNodeData,
2193
2289
  SetNodeFocus: SetNodeFocus,
2194
2290
  SetNodeScroll: SetNodeScroll,
2291
+ SetNodeSlot: SetNodeSlot,
2195
2292
  SetPageLocation: SetPageLocation,
2196
2293
  SetPageLocationDeprecated: SetPageLocationDeprecated,
2197
2294
  SetPageVisibility: SetPageVisibility,
@@ -2266,7 +2363,7 @@ function Performance (app, opts) {
2266
2363
  const WATCHED_TAGS_KEY = '__or__watched_tags__';
2267
2364
  class TagWatcher {
2268
2365
  constructor(params) {
2269
- this.intervals = {};
2366
+ this.interval = null;
2270
2367
  this.tags = [];
2271
2368
  this.sessionStorage = params.sessionStorage;
2272
2369
  this.errLog = params.errLog;
@@ -2308,9 +2405,12 @@ class TagWatcher {
2308
2405
  }
2309
2406
  setTags(tags) {
2310
2407
  this.tags = tags;
2311
- this.intervals = {};
2312
- tags.forEach((tag) => {
2313
- this.intervals[tag.id] = setInterval(() => {
2408
+ if (this.interval) {
2409
+ clearInterval(this.interval);
2410
+ this.interval = null;
2411
+ }
2412
+ this.interval = setInterval(() => {
2413
+ this.tags.forEach((tag) => {
2314
2414
  const possibleEls = document.querySelectorAll(tag.selector);
2315
2415
  if (possibleEls.length > 0) {
2316
2416
  const el = possibleEls[0];
@@ -2318,21 +2418,21 @@ class TagWatcher {
2318
2418
  el.__or_watcher_tagname = tag.id;
2319
2419
  this.observer.observe(el);
2320
2420
  }
2321
- }, 500);
2322
- });
2421
+ });
2422
+ }, 500);
2323
2423
  }
2324
2424
  onTagRendered(tagId) {
2325
- if (this.intervals[tagId]) {
2326
- clearInterval(this.intervals[tagId]);
2425
+ if (this.tags.findIndex(t => t.id === tagId)) {
2426
+ this.tags = this.tags.filter((tag) => tag.id !== tagId);
2327
2427
  }
2328
2428
  this.onTag(tagId);
2329
2429
  }
2330
2430
  clear() {
2331
- this.tags.forEach((tag) => {
2332
- clearInterval(this.intervals[tag.id]);
2333
- });
2334
2431
  this.tags = [];
2335
- this.intervals = {};
2432
+ if (this.interval) {
2433
+ clearInterval(this.interval);
2434
+ this.interval = null;
2435
+ }
2336
2436
  this.observer.disconnect();
2337
2437
  }
2338
2438
  }
@@ -3649,27 +3749,6 @@ class Maintainer {
3649
3749
  }
3650
3750
  }
3651
3751
 
3652
- // 4 levels, 128 frames between each level, 8_388_608 nodes per page
3653
- // lets hope no one will need more :D
3654
- const BITS_LEVEL = 2; // 4
3655
- const BITS_ORDER = 7; // 128
3656
- const BITS_NODE = 22; // 8_388_608
3657
- const SHIFT_ORDER = BITS_NODE;
3658
- const SHIFT_LEVEL = BITS_NODE + BITS_ORDER;
3659
- const MASK_NODE = (1 << BITS_NODE) - 1;
3660
- const MASK_ORDER = (1 << BITS_ORDER) - 1;
3661
- const MASK_LEVEL = (1 << BITS_LEVEL) - 1;
3662
- function pack(level, order, nodeId) {
3663
- if (level < 0 || level > MASK_LEVEL)
3664
- throw new RangeError('OR: nesting level overflow, max 4');
3665
- if (order < 0 || order > MASK_ORDER)
3666
- throw new RangeError('OR: frame order overflow, max 128');
3667
- const v = ((level & MASK_LEVEL) << SHIFT_LEVEL) |
3668
- ((order & MASK_ORDER) << SHIFT_ORDER) |
3669
- (nodeId & MASK_NODE);
3670
- return v >>> 0;
3671
- }
3672
-
3673
3752
  class Nodes {
3674
3753
  constructor(params) {
3675
3754
  this.nodes = new Map();
@@ -3697,9 +3776,6 @@ class Nodes {
3697
3776
  }
3698
3777
  listeners.push([type, listener, useCapture]);
3699
3778
  };
3700
- this.createFrameId = (level, frameOrder) => {
3701
- return pack(level, frameOrder, 0);
3702
- };
3703
3779
  this.unregisterNode = (node) => {
3704
3780
  const id = node[this.node_id];
3705
3781
  if (id !== undefined) {
@@ -3720,8 +3796,16 @@ class Nodes {
3720
3796
  this.maintainer = new Maintainer(this.nodes, this.unregisterNode, params.maintainer);
3721
3797
  this.maintainer.start();
3722
3798
  }
3723
- crossdomainMode(level, frameOrder) {
3724
- this.nextNodeId = this.createFrameId(level, frameOrder);
3799
+ syntheticMode(frameOrder) {
3800
+ const maxSafeNumber = Number.MAX_SAFE_INTEGER;
3801
+ const placeholderSize = 99999999;
3802
+ const nextFrameId = placeholderSize * frameOrder;
3803
+ // I highly doubt that this will ever happen,
3804
+ // but it will be easier to debug if it does
3805
+ if (nextFrameId > maxSafeNumber) {
3806
+ throw new Error('Placeholder id overflow');
3807
+ }
3808
+ this.nextNodeId = nextFrameId;
3725
3809
  }
3726
3810
  registerNode(node) {
3727
3811
  let id = node[this.node_id];
@@ -4156,6 +4240,13 @@ async function parseUseEl(useElement, mode, domParser) {
4156
4240
  return;
4157
4241
  }
4158
4242
  let [url, symbolId] = href.split('#');
4243
+ if (!url && !symbolId) {
4244
+ console.warn('Openreplay: Invalid xlink:href or href found on <use>.');
4245
+ return;
4246
+ }
4247
+ if (iconCache[symbolId]) {
4248
+ return iconCache[symbolId];
4249
+ }
4159
4250
  // happens if svg spritemap is local, fastest case for us
4160
4251
  if (!url && symbolId) {
4161
4252
  const hasHashtag = href.startsWith('#');
@@ -4181,13 +4272,6 @@ async function parseUseEl(useElement, mode, domParser) {
4181
4272
  return;
4182
4273
  }
4183
4274
  }
4184
- if (!url && !symbolId) {
4185
- console.warn('Openreplay: Invalid xlink:href or href found on <use>.');
4186
- return;
4187
- }
4188
- if (iconCache[symbolId]) {
4189
- return iconCache[symbolId];
4190
- }
4191
4275
  let svgDoc;
4192
4276
  if (svgUrlCache[url]) {
4193
4277
  if (svgUrlCache[url] === 1) {
@@ -4287,6 +4371,7 @@ class Observer {
4287
4371
  this.indexes = [];
4288
4372
  this.attributesMap = new Map();
4289
4373
  this.textSet = new Set();
4374
+ this.slotMap = new Map();
4290
4375
  this.disableSprites = false;
4291
4376
  /**
4292
4377
  * this option means that, instead of using link element with href to load css,
@@ -4296,6 +4381,9 @@ class Observer {
4296
4381
  this.inlineRemoteCss = false;
4297
4382
  this.inlinerOptions = undefined;
4298
4383
  this.domParser = new DOMParser();
4384
+ this.throttling = true;
4385
+ this.throttledSetNodeData = throttleWithTrailing((id, parentElement, data) => this.sendNodeData(id, parentElement, data), 30);
4386
+ this.throttling = !Boolean(options.disableThrottling);
4299
4387
  this.disableSprites = Boolean(options.disableSprites);
4300
4388
  this.inlineRemoteCss = Boolean(options.inlineRemoteCss);
4301
4389
  this.inlinerOptions = options.inlinerOptions;
@@ -4476,6 +4564,18 @@ class Observer {
4476
4564
  }
4477
4565
  bindNode(node) {
4478
4566
  const [id, isNew] = this.app.nodes.registerNode(node);
4567
+ if (isElementNode(node) && hasTag(node, 'slot')) {
4568
+ this.app.nodes.attachNodeListener(node, 'slotchange', () => {
4569
+ const sl = node;
4570
+ sl.assignedNodes({ flatten: true }).forEach((n) => {
4571
+ const nid = this.app.nodes.getID(n);
4572
+ if (nid !== undefined) {
4573
+ this.recents.set(nid, RecentsType.Removed);
4574
+ this.commitNode(nid);
4575
+ }
4576
+ });
4577
+ });
4578
+ }
4479
4579
  if (isNew) {
4480
4580
  this.recents.set(id, RecentsType.New);
4481
4581
  }
@@ -4506,6 +4606,9 @@ class Observer {
4506
4606
  }
4507
4607
  unbindTree(node) {
4508
4608
  const id = this.app.nodes.unregisterNode(node);
4609
+ if (id !== undefined) {
4610
+ this.slotMap.delete(id);
4611
+ }
4509
4612
  if (id !== undefined && this.recents.get(id) === RecentsType.Removed) {
4510
4613
  // Sending RemoveNode only for parent to maintain
4511
4614
  this.app.send(RemoveNode(id));
@@ -4534,8 +4637,15 @@ class Observer {
4534
4637
  if (isRootNode(node)) {
4535
4638
  return true;
4536
4639
  }
4537
- // @ts-ignore SALESFORCE
4538
- const parent = node.assignedSlot ? node.assignedSlot : node.parentNode;
4640
+ let slot = node.assignedSlot;
4641
+ let isLightDom = false;
4642
+ if (slot) {
4643
+ // Check if the node is in light DOM (not in shadow DOM)
4644
+ // This is a workaround for the issue with shadow DOM and slots
4645
+ // where the slot is not assigned to the node in shadow DOM.
4646
+ isLightDom = node.getRootNode() instanceof ShadowRoot;
4647
+ }
4648
+ const parent = node.parentNode;
4539
4649
  let parentID;
4540
4650
  // Disable parent check for the upper context HTMLHtmlElement, because it is root there... (before)
4541
4651
  // TODO: get rid of "special" cases (there is an issue with CreateDocument altered behaviour though)
@@ -4547,7 +4657,15 @@ class Observer {
4547
4657
  this.unbindTree(node);
4548
4658
  return false;
4549
4659
  }
4550
- parentID = this.app.nodes.getID(parent);
4660
+ if (isLightDom && slot) {
4661
+ parentID = this.app.nodes.getID(slot);
4662
+ // in light dom, we don't "slot" the node,
4663
+ // but rather use the slot as a parent
4664
+ slot = null;
4665
+ }
4666
+ else {
4667
+ parentID = this.app.nodes.getID(parent);
4668
+ }
4551
4669
  if (parentID === undefined) {
4552
4670
  this.unbindTree(node);
4553
4671
  return false;
@@ -4607,12 +4725,35 @@ class Observer {
4607
4725
  else if (isTextNode(node)) {
4608
4726
  // for text node id != 0, hence parentID !== undefined and parent is Element
4609
4727
  this.app.send(CreateTextNode(id, parentID, index));
4610
- this.sendNodeData(id, parent, node.data);
4728
+ if (this.throttling) {
4729
+ this.throttledSetNodeData(id, parent, node.data);
4730
+ }
4731
+ else {
4732
+ this.sendNodeData(id, parent, node.data);
4733
+ }
4734
+ }
4735
+ if (slot) {
4736
+ const slotID = this.app.nodes.getID(slot);
4737
+ if (slotID !== undefined) {
4738
+ this.slotMap.set(id, slotID);
4739
+ this.app.send(SetNodeSlot(id, slotID));
4740
+ }
4611
4741
  }
4612
4742
  return true;
4613
4743
  }
4614
4744
  if (recentsType === RecentsType.Removed && parentID !== undefined) {
4615
4745
  this.app.send(MoveNode(id, parentID, index));
4746
+ if (slot) {
4747
+ const slotID = this.app.nodes.getID(slot);
4748
+ if (slotID !== undefined && this.slotMap.get(id) !== slotID) {
4749
+ this.slotMap.set(id, slotID);
4750
+ this.app.send(SetNodeSlot(id, slotID));
4751
+ }
4752
+ }
4753
+ else if (this.slotMap.has(id)) {
4754
+ this.slotMap.delete(id);
4755
+ this.app.send(SetNodeSlot(id, 0));
4756
+ }
4616
4757
  }
4617
4758
  const attr = this.attributesMap.get(id);
4618
4759
  if (attr !== undefined) {
@@ -4628,7 +4769,12 @@ class Observer {
4628
4769
  throw 'commitNode: node is not a text';
4629
4770
  }
4630
4771
  // for text node id != 0, hence parent is Element
4631
- this.sendNodeData(id, parent, node.data);
4772
+ if (this.throttling) {
4773
+ this.throttledSetNodeData(id, parent, node.data);
4774
+ }
4775
+ else {
4776
+ this.sendNodeData(id, parent, node.data);
4777
+ }
4632
4778
  }
4633
4779
  return true;
4634
4780
  }
@@ -4671,6 +4817,7 @@ class Observer {
4671
4817
  disconnect() {
4672
4818
  this.observer.disconnect();
4673
4819
  this.clear();
4820
+ this.throttledSetNodeData.clear();
4674
4821
  }
4675
4822
  }
4676
4823
 
@@ -4778,7 +4925,7 @@ var InlineCssMode;
4778
4925
  InlineCssMode[InlineCssMode["Disabled"] = 0] = "Disabled";
4779
4926
  /** will attempt to record the linked css file as AdoptedStyleSheet object */
4780
4927
  InlineCssMode[InlineCssMode["Inline"] = 1] = "Inline";
4781
- /** will fetch the file, then simulated AdoptedStyleSheets behavior programmaticaly for the replay */
4928
+ /** will fetch the file, then simulate AdoptedStyleSheets behavior programmaticaly for the replay */
4782
4929
  InlineCssMode[InlineCssMode["InlineFetched"] = 2] = "InlineFetched";
4783
4930
  /** will fetch the file, then save it as plain css inside <style> node */
4784
4931
  InlineCssMode[InlineCssMode["PlainFetched"] = 3] = "PlainFetched";
@@ -4830,6 +4977,7 @@ class TopObserver extends Observer {
4830
4977
  }, params.options);
4831
4978
  const observerOptions = {
4832
4979
  disableSprites: opts.disableSprites,
4980
+ disableThrottling: opts.disableThrottling,
4833
4981
  ...getInlineOptions(opts.inlineCss)
4834
4982
  };
4835
4983
  super(params.app, true, observerOptions);
@@ -4929,7 +5077,7 @@ class TopObserver extends Observer {
4929
5077
  this.app.nodes.callNodeCallbacks(document, true);
4930
5078
  }, window.document.documentElement);
4931
5079
  }
4932
- crossdomainObserve(rootNodeId, frameOder, frameLevel) {
5080
+ crossdomainObserve(rootNodeId, frameOder) {
4933
5081
  const observer = this;
4934
5082
  Element.prototype.attachShadow = function () {
4935
5083
  // eslint-disable-next-line
@@ -4938,7 +5086,7 @@ class TopObserver extends Observer {
4938
5086
  return shadow;
4939
5087
  };
4940
5088
  this.app.nodes.clear();
4941
- this.app.nodes.crossdomainMode(frameLevel, frameOder);
5089
+ this.app.nodes.syntheticMode(frameOder);
4942
5090
  const iframeObserver = new IFrameObserver(this.app);
4943
5091
  this.iframeObservers.set(window.document, iframeObserver);
4944
5092
  iframeObserver.syntheticObserve(rootNodeId, window.document);
@@ -5238,7 +5386,7 @@ class Ticker {
5238
5386
  * this value is injected during build time via rollup
5239
5387
  * */
5240
5388
  // @ts-ignore
5241
- const workerBodyFn = "!function(){\"use strict\";class t{constructor(t,s,i,e=10,n=250,h,r){this.onUnauthorised=s,this.onFailure=i,this.MAX_ATTEMPTS_COUNT=e,this.ATTEMPT_TIMEOUT=n,this.onCompress=h,this.pageNo=r,this.attemptsCount=0,this.busy=!1,this.queue=[],this.token=null,this.lastBatchNum=0,this.ingestURL=t+\"/v1/web/i\",this.isCompressing=void 0!==h}getQueueStatus(){return 0===this.queue.length&&!this.busy}authorise(t){this.token=t,this.busy||this.sendNext()}push(t){if(this.busy||!this.token)this.queue.push(t);else if(this.busy=!0,this.isCompressing&&this.onCompress)this.onCompress(t);else{const s=++this.lastBatchNum;this.sendBatch(t,!1,s)}}sendNext(){const t=this.queue.shift();if(t)if(this.busy=!0,this.isCompressing&&this.onCompress)this.onCompress(t);else{const s=++this.lastBatchNum;this.sendBatch(t,!1,s)}else this.busy=!1}retry(t,s,i){this.attemptsCount>=this.MAX_ATTEMPTS_COUNT?this.onFailure(`Failed to send batch after ${this.attemptsCount} attempts.`):(this.attemptsCount++,setTimeout((()=>this.sendBatch(t,s,i)),this.ATTEMPT_TIMEOUT*this.attemptsCount))}sendBatch(t,s,i){var e;const n=null==i?void 0:i.toString().replace(/^([^_]+)_([^_]+).*/,\"$1_$2_$3\");this.busy=!0;const h={Authorization:`Bearer ${this.token}`};s&&(h[\"Content-Encoding\"]=\"gzip\"),null!==this.token?fetch(`${this.ingestURL}?batch=${null!==(e=this.pageNo)&&void 0!==e?e:\"noPageNum\"}_${null!=n?n:\"noBatchNum\"}`,{body:t,method:\"POST\",headers:h,keepalive:t.length<65536}).then((e=>{if(401===e.status)return this.busy=!1,void this.onUnauthorised();e.status>=400?this.retry(t,s,`${null!=i?i:\"noBatchNum\"}_network:${e.status}`):(this.attemptsCount=0,this.sendNext())})).catch((e=>{console.warn(\"OpenReplay:\",e),this.retry(t,s,`${null!=i?i:\"noBatchNum\"}_reject:${e.message}`)})):setTimeout((()=>{this.sendBatch(t,s,`${null!=i?i:\"noBatchNum\"}_newToken`)}),500)}sendCompressed(t){const s=++this.lastBatchNum;this.sendBatch(t,!0,s)}sendUncompressed(t){const s=++this.lastBatchNum;this.sendBatch(t,!1,s)}clean(){this.sendNext(),setTimeout((()=>{this.token=null,this.queue.length=0}),10)}}const s=\"function\"==typeof TextEncoder?new TextEncoder:{encode(t){const s=t.length,i=new Uint8Array(3*s);let e=-1;for(let n=0,h=0,r=0;r!==s;){if(n=t.charCodeAt(r),r+=1,n>=55296&&n<=56319){if(r===s){i[e+=1]=239,i[e+=1]=191,i[e+=1]=189;break}if(h=t.charCodeAt(r),!(h>=56320&&h<=57343)){i[e+=1]=239,i[e+=1]=191,i[e+=1]=189;continue}if(n=1024*(n-55296)+h-56320+65536,r+=1,n>65535){i[e+=1]=240|n>>>18,i[e+=1]=128|n>>>12&63,i[e+=1]=128|n>>>6&63,i[e+=1]=128|63&n;continue}}n<=127?i[e+=1]=0|n:n<=2047?(i[e+=1]=192|n>>>6,i[e+=1]=128|63&n):(i[e+=1]=224|n>>>12,i[e+=1]=128|n>>>6&63,i[e+=1]=128|63&n)}return i.subarray(0,e+1)}};class i{constructor(t){this.size=t,this.offset=0,this.checkpointOffset=0,this.data=new Uint8Array(t)}getCurrentOffset(){return this.offset}checkpoint(){this.checkpointOffset=this.offset}get isEmpty(){return 0===this.offset}skip(t){return this.offset+=t,this.offset<=this.size}set(t,s){this.data.set(t,s)}boolean(t){return this.data[this.offset++]=+t,this.offset<=this.size}uint(t){for((t<0||t>Number.MAX_SAFE_INTEGER)&&(t=0);t>=128;)this.data[this.offset++]=t%256|128,t=Math.floor(t/128);return this.data[this.offset++]=t,this.offset<=this.size}int(t){return t=Math.round(t),this.uint(t>=0?2*t:-2*t-1)}string(t){const i=s.encode(t),e=i.byteLength;return!(!this.uint(e)||this.offset+e>this.size)&&(this.data.set(i,this.offset),this.offset+=e,!0)}reset(){this.offset=0,this.checkpointOffset=0}flush(){const t=this.data.slice(0,this.checkpointOffset);return this.reset(),t}}class e extends i{encode(t){switch(t[0]){case 0:case 11:case 114:case 115:return this.uint(t[1]);case 4:case 44:case 47:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3]);case 5:case 20:case 38:case 70:case 75:case 76:case 77:case 82:return this.uint(t[1])&&this.uint(t[2]);case 6:return this.int(t[1])&&this.int(t[2]);case 7:return!0;case 8:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.string(t[4])&&this.boolean(t[5]);case 9:case 10:case 24:case 35:case 51:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3]);case 12:case 52:case 61:case 71:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3]);case 13:case 14:case 17:case 34:case 50:case 54:return this.uint(t[1])&&this.string(t[2]);case 16:return this.uint(t[1])&&this.int(t[2])&&this.int(t[3]);case 18:return this.uint(t[1])&&this.string(t[2])&&this.int(t[3]);case 19:return this.uint(t[1])&&this.boolean(t[2]);case 21:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.string(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8]);case 22:case 27:case 30:case 41:case 45:case 46:case 43:case 63:case 64:case 79:case 124:return this.string(t[1])&&this.string(t[2]);case 23:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8])&&this.uint(t[9]);case 28:case 29:case 42:case 117:case 118:return this.string(t[1]);case 37:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3]);case 39:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7]);case 40:return this.string(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 48:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.int(t[5]);case 49:return this.int(t[1])&&this.int(t[2])&&this.uint(t[3])&&this.uint(t[4]);case 53:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8]);case 55:return this.boolean(t[1]);case 57:case 60:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 58:case 120:return this.int(t[1]);case 59:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.string(t[5])&&this.string(t[6])&&this.string(t[7]);case 67:case 73:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);case 68:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])&&this.uint(t[6]);case 69:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 78:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 81:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.int(t[4])&&this.string(t[5]);case 83:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.string(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8])&&this.uint(t[9]);case 84:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.uint(t[4])&&this.string(t[5])&&this.string(t[6]);case 112:return this.uint(t[1])&&this.string(t[2])&&this.boolean(t[3])&&this.string(t[4])&&this.int(t[5])&&this.int(t[6]);case 113:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3]);case 116:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8])&&this.uint(t[9])&&this.boolean(t[10]);case 119:return this.string(t[1])&&this.uint(t[2]);case 121:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.uint(t[4]);case 122:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);case 123:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])}}}class n{constructor(t,s,i,n,h,r){this.pageNo=t,this.timestamp=s,this.url=i,this.onBatch=n,this.tabId=h,this.onOfflineEnd=r,this.nextIndex=0,this.beaconSize=2e5,this.encoder=new e(this.beaconSize),this.sizeBuffer=new Uint8Array(3),this.isEmpty=!0,this.checkpoints=[],this.beaconSizeLimit=1e6,this.prepare()}writeType(t){return this.encoder.uint(t[0])}writeFields(t){return this.encoder.encode(t)}writeSizeAt(t,s){for(let s=0;s<3;s++)this.sizeBuffer[s]=t>>8*s;this.encoder.set(this.sizeBuffer,s)}prepare(){if(!this.encoder.isEmpty)return;this.checkpoints.length=0;const t=[81,1,this.pageNo,this.nextIndex,this.timestamp,this.url],s=[0,this.timestamp],i=[118,this.tabId];this.writeType(t),this.writeFields(t),this.writeWithSize(s),this.writeWithSize(i),this.isEmpty=!0}writeWithSize(t){const s=this.encoder;if(!this.writeType(t)||!s.skip(3))return!1;const i=s.getCurrentOffset(),e=this.writeFields(t);if(e){const e=s.getCurrentOffset()-i;if(e>16777215)return console.warn(\"OpenReplay: max message size overflow.\"),!1;this.writeSizeAt(e,i-3),s.checkpoint(),this.checkpoints.push(s.getCurrentOffset()),this.isEmpty=this.isEmpty&&0===t[0],this.nextIndex++}return e}setBeaconSizeLimit(t){this.beaconSizeLimit=t}writeMessage(t){if(\"q_end\"===t[0])return this.finaliseBatch(),this.onOfflineEnd();0===t[0]&&(this.timestamp=t[1]),122===t[0]&&(this.url=t[1]),this.writeWithSize(t)||(this.finaliseBatch(),this.writeWithSize(t)||(this.encoder=new e(this.beaconSizeLimit),this.prepare(),this.writeWithSize(t)?this.finaliseBatch():console.warn(\"OpenReplay: beacon size overflow. Skipping large message.\",t,this),this.encoder=new e(this.beaconSize),this.prepare()))}finaliseBatch(t=!1){if(this.isEmpty)return;const s=this.encoder.flush();this.onBatch(s,t),this.prepare()}clean(){this.encoder.reset(),this.checkpoints.length=0}}var h;!function(t){t[t.NotActive=0]=\"NotActive\",t[t.Starting=1]=\"Starting\",t[t.Stopping=2]=\"Stopping\",t[t.Active=3]=\"Active\",t[t.Stopped=4]=\"Stopped\"}(h||(h={}));let r=null,a=null,u=h.NotActive;function o(t){a&&a.finaliseBatch(t)}function c(){return new Promise((t=>{u=h.Stopping,null!==p&&(clearInterval(p),p=null),a&&(a.clean(),a=null),r&&(r.clean(),setTimeout((()=>{r=null}),20)),setTimeout((()=>{u=h.NotActive,t(null)}),100)}))}function g(){[h.Stopped,h.Stopping].includes(u)||(postMessage(\"a_stop\"),c().then((()=>{postMessage(\"a_start\")})))}let l,p=null;self.onmessage=({data:s})=>{if(\"stop\"===s)return o(),void c().then((()=>{u=h.Stopped}));if(\"forceFlushBatch\"!==s)if(\"closing\"!==s){if(!Array.isArray(s)){if(\"compressed\"===s.type){if(!r)return console.debug(\"OR WebWorker: sender not initialised. Compressed batch.\"),void g();s.batch&&r.sendCompressed(s.batch)}if(\"uncompressed\"===s.type){if(!r)return console.debug(\"OR WebWorker: sender not initialised. Uncompressed batch.\"),void g();s.batch&&r.sendUncompressed(s.batch)}return\"start\"===s.type?(u=h.Starting,r=new t(s.ingestPoint,(()=>{g()}),(t=>{!function(t){postMessage({type:\"failure\",reason:t}),c()}(t)}),s.connAttemptCount,s.connAttemptGap,(t=>{postMessage({type:\"compress\",batch:t},[t.buffer])}),s.pageNo),a=new n(s.pageNo,s.timestamp,s.url,((t,s)=>{r&&(s?r.sendUncompressed(t):r.push(t))}),s.tabId,(()=>postMessage({type:\"queue_empty\"}))),null===p&&(p=setInterval(o,3e4)),u=h.Active):\"auth\"===s.type?r?a?(r.authorise(s.token),void(s.beaconSizeLimit&&a.setBeaconSizeLimit(s.beaconSizeLimit))):(console.debug(\"OR WebWorker: writer not initialised. Received auth.\"),void g()):(console.debug(\"OR WebWorker: sender not initialised. Received auth.\"),void g()):void 0}if(a){const t=a;s.forEach((s=>{55===s[0]&&(s[1]?l=setTimeout((()=>g()),18e5):clearTimeout(l)),t.writeMessage(s)}))}else postMessage(\"not_init\"),g()}else o(!0);else o()}}();\n";
5389
+ const workerBodyFn = "!function(){\"use strict\";class t{constructor(t,s,i,e=10,n=250,h,r){this.onUnauthorised=s,this.onFailure=i,this.MAX_ATTEMPTS_COUNT=e,this.ATTEMPT_TIMEOUT=n,this.onCompress=h,this.pageNo=r,this.attemptsCount=0,this.busy=!1,this.queue=[],this.token=null,this.lastBatchNum=0,this.ingestURL=t+\"/v1/web/i\",this.isCompressing=void 0!==h}getQueueStatus(){return 0===this.queue.length&&!this.busy}authorise(t){this.token=t,this.busy||this.sendNext()}push(t){if(this.busy||!this.token)this.queue.push(t);else if(this.busy=!0,this.isCompressing&&this.onCompress)this.onCompress(t);else{const s=++this.lastBatchNum;this.sendBatch(t,!1,s)}}sendNext(){const t=this.queue.shift();if(t)if(this.busy=!0,this.isCompressing&&this.onCompress)this.onCompress(t);else{const s=++this.lastBatchNum;this.sendBatch(t,!1,s)}else this.busy=!1}retry(t,s,i){this.attemptsCount>=this.MAX_ATTEMPTS_COUNT?this.onFailure(`Failed to send batch after ${this.attemptsCount} attempts.`):(this.attemptsCount++,setTimeout((()=>this.sendBatch(t,s,i)),this.ATTEMPT_TIMEOUT*this.attemptsCount))}sendBatch(t,s,i){var e;const n=null==i?void 0:i.toString().replace(/^([^_]+)_([^_]+).*/,\"$1_$2_$3\");this.busy=!0;const h={Authorization:`Bearer ${this.token}`};s&&(h[\"Content-Encoding\"]=\"gzip\"),null!==this.token?fetch(`${this.ingestURL}?batch=${null!==(e=this.pageNo)&&void 0!==e?e:\"noPageNum\"}_${null!=n?n:\"noBatchNum\"}`,{body:t,method:\"POST\",headers:h,keepalive:t.length<65536}).then((e=>{if(401===e.status)return this.busy=!1,void this.onUnauthorised();e.status>=400?this.retry(t,s,`${null!=i?i:\"noBatchNum\"}_network:${e.status}`):(this.attemptsCount=0,this.sendNext())})).catch((e=>{console.warn(\"OpenReplay:\",e),this.retry(t,s,`${null!=i?i:\"noBatchNum\"}_reject:${e.message}`)})):setTimeout((()=>{this.sendBatch(t,s,`${null!=i?i:\"noBatchNum\"}_newToken`)}),500)}sendCompressed(t){const s=++this.lastBatchNum;this.sendBatch(t,!0,s)}sendUncompressed(t){const s=++this.lastBatchNum;this.sendBatch(t,!1,s)}clean(){this.sendNext(),setTimeout((()=>{this.token=null,this.queue.length=0}),10)}}const s=\"function\"==typeof TextEncoder?new TextEncoder:{encode(t){const s=t.length,i=new Uint8Array(3*s);let e=-1;for(let n=0,h=0,r=0;r!==s;){if(n=t.charCodeAt(r),r+=1,n>=55296&&n<=56319){if(r===s){i[e+=1]=239,i[e+=1]=191,i[e+=1]=189;break}if(h=t.charCodeAt(r),!(h>=56320&&h<=57343)){i[e+=1]=239,i[e+=1]=191,i[e+=1]=189;continue}if(n=1024*(n-55296)+h-56320+65536,r+=1,n>65535){i[e+=1]=240|n>>>18,i[e+=1]=128|n>>>12&63,i[e+=1]=128|n>>>6&63,i[e+=1]=128|63&n;continue}}n<=127?i[e+=1]=0|n:n<=2047?(i[e+=1]=192|n>>>6,i[e+=1]=128|63&n):(i[e+=1]=224|n>>>12,i[e+=1]=128|n>>>6&63,i[e+=1]=128|63&n)}return i.subarray(0,e+1)}};class i{constructor(t){this.size=t,this.offset=0,this.checkpointOffset=0,this.data=new Uint8Array(t)}getCurrentOffset(){return this.offset}checkpoint(){this.checkpointOffset=this.offset}get isEmpty(){return 0===this.offset}skip(t){return this.offset+=t,this.offset<=this.size}set(t,s){this.data.set(t,s)}boolean(t){return this.data[this.offset++]=+t,this.offset<=this.size}uint(t){for((t<0||t>Number.MAX_SAFE_INTEGER)&&(t=0);t>=128;)this.data[this.offset++]=t%256|128,t=Math.floor(t/128);return this.data[this.offset++]=t,this.offset<=this.size}int(t){return t=Math.round(t),this.uint(t>=0?2*t:-2*t-1)}string(t){const i=s.encode(t),e=i.byteLength;return!(!this.uint(e)||this.offset+e>this.size)&&(this.data.set(i,this.offset),this.offset+=e,!0)}reset(){this.offset=0,this.checkpointOffset=0}flush(){const t=this.data.slice(0,this.checkpointOffset);return this.reset(),t}}class e extends i{encode(t){switch(t[0]){case 0:case 11:case 114:case 115:return this.uint(t[1]);case 4:case 44:case 47:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3]);case 5:case 20:case 38:case 65:case 70:case 75:case 76:case 77:case 82:return this.uint(t[1])&&this.uint(t[2]);case 6:return this.int(t[1])&&this.int(t[2]);case 7:return!0;case 8:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.string(t[4])&&this.boolean(t[5]);case 9:case 10:case 24:case 35:case 51:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3]);case 12:case 52:case 61:case 71:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3]);case 13:case 14:case 17:case 34:case 36:case 50:case 54:return this.uint(t[1])&&this.string(t[2]);case 16:return this.uint(t[1])&&this.int(t[2])&&this.int(t[3]);case 18:return this.uint(t[1])&&this.string(t[2])&&this.int(t[3]);case 19:return this.uint(t[1])&&this.boolean(t[2]);case 21:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.string(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8]);case 22:case 27:case 30:case 41:case 45:case 46:case 43:case 63:case 64:case 79:case 124:return this.string(t[1])&&this.string(t[2]);case 23:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8])&&this.uint(t[9]);case 28:case 29:case 42:case 117:case 118:return this.string(t[1]);case 37:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3]);case 39:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7]);case 40:return this.string(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 48:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.int(t[5]);case 49:return this.int(t[1])&&this.int(t[2])&&this.uint(t[3])&&this.uint(t[4]);case 53:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8]);case 55:return this.boolean(t[1]);case 57:case 60:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 58:case 120:return this.int(t[1]);case 59:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.string(t[5])&&this.string(t[6])&&this.string(t[7]);case 67:case 73:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);case 68:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])&&this.uint(t[6]);case 69:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 78:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 81:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.int(t[4])&&this.string(t[5]);case 83:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.string(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8])&&this.uint(t[9]);case 84:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.uint(t[4])&&this.string(t[5])&&this.string(t[6]);case 85:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8])&&this.uint(t[9])&&this.boolean(t[10])&&this.uint(t[11])&&this.uint(t[12])&&this.uint(t[13])&&this.uint(t[14])&&this.uint(t[15])&&this.uint(t[16])&&this.uint(t[17]);case 87:return this.string(t[1])&&this.int(t[2])&&this.int(t[3]);case 89:return this.string(t[1])&&this.int(t[2])&&this.int(t[3])&&this.int(t[4])&&this.int(t[5])&&this.string(t[6]);case 112:return this.uint(t[1])&&this.string(t[2])&&this.boolean(t[3])&&this.string(t[4])&&this.int(t[5])&&this.int(t[6]);case 113:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3]);case 116:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8])&&this.uint(t[9])&&this.boolean(t[10]);case 119:return this.string(t[1])&&this.uint(t[2]);case 121:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.uint(t[4]);case 122:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);case 123:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])}}}class n{constructor(t,s,i,n,h,r){this.pageNo=t,this.timestamp=s,this.url=i,this.onBatch=n,this.tabId=h,this.onOfflineEnd=r,this.nextIndex=0,this.beaconSize=2e5,this.encoder=new e(this.beaconSize),this.sizeBuffer=new Uint8Array(3),this.isEmpty=!0,this.checkpoints=[],this.beaconSizeLimit=1e6,this.prepare()}writeType(t){return this.encoder.uint(t[0])}writeFields(t){return this.encoder.encode(t)}writeSizeAt(t,s){for(let s=0;s<3;s++)this.sizeBuffer[s]=t>>8*s;this.encoder.set(this.sizeBuffer,s)}prepare(){if(!this.encoder.isEmpty)return;this.checkpoints.length=0;const t=[81,1,this.pageNo,this.nextIndex,this.timestamp,this.url],s=[0,this.timestamp],i=[118,this.tabId];this.writeType(t),this.writeFields(t),this.writeWithSize(s),this.writeWithSize(i),this.isEmpty=!0}writeWithSize(t){const s=this.encoder;if(!this.writeType(t)||!s.skip(3))return!1;const i=s.getCurrentOffset(),e=this.writeFields(t);if(e){const e=s.getCurrentOffset()-i;if(e>16777215)return console.warn(\"OpenReplay: max message size overflow.\"),!1;this.writeSizeAt(e,i-3),s.checkpoint(),this.checkpoints.push(s.getCurrentOffset()),this.isEmpty=this.isEmpty&&0===t[0],this.nextIndex++}return e}setBeaconSizeLimit(t){this.beaconSizeLimit=t}writeMessage(t){if(-1===t[0])return this.finaliseBatch(),this.onOfflineEnd();0===t[0]&&(this.timestamp=t[1]),122===t[0]&&(this.url=t[1]),this.writeWithSize(t)||(this.finaliseBatch(),this.writeWithSize(t)||(this.encoder=new e(this.beaconSizeLimit),this.prepare(),this.writeWithSize(t)?this.finaliseBatch():console.warn(\"OpenReplay: beacon size overflow. Skipping large message.\",t,this),this.encoder=new e(this.beaconSize),this.prepare()))}finaliseBatch(t=!1){if(this.isEmpty)return;const s=this.encoder.flush();this.onBatch(s,t),this.prepare()}clean(){this.encoder.reset(),this.checkpoints.length=0}}var h;!function(t){t[t.NotActive=0]=\"NotActive\",t[t.Starting=1]=\"Starting\",t[t.Stopping=2]=\"Stopping\",t[t.Active=3]=\"Active\",t[t.Stopped=4]=\"Stopped\"}(h||(h={}));let r=null,u=null,a=h.NotActive;function o(t){u&&u.finaliseBatch(t)}function c(){return new Promise((t=>{a=h.Stopping,null!==p&&(clearInterval(p),p=null),u&&(u.clean(),u=null),r&&(r.clean(),setTimeout((()=>{r=null}),20)),setTimeout((()=>{a=h.NotActive,t(null)}),100)}))}function g(){[h.Stopped,h.Stopping].includes(a)||(postMessage(\"a_stop\"),c().then((()=>{postMessage(\"a_start\")})))}let l,p=null;self.onmessage=({data:s})=>{if(\"stop\"===s)return o(),void c().then((()=>{a=h.Stopped}));if(\"forceFlushBatch\"!==s)if(\"closing\"!==s){if(!Array.isArray(s)){if(\"compressed\"===s.type){if(!r)return console.debug(\"OR WebWorker: sender not initialised. Compressed batch.\"),void g();s.batch&&r.sendCompressed(s.batch)}if(\"uncompressed\"===s.type){if(!r)return console.debug(\"OR WebWorker: sender not initialised. Uncompressed batch.\"),void g();s.batch&&r.sendUncompressed(s.batch)}return\"start\"===s.type?(a=h.Starting,r=new t(s.ingestPoint,(()=>{g()}),(t=>{!function(t){postMessage({type:\"failure\",reason:t}),c()}(t)}),s.connAttemptCount,s.connAttemptGap,(t=>{postMessage({type:\"compress\",batch:t},[t.buffer])}),s.pageNo),u=new n(s.pageNo,s.timestamp,s.url,((t,s)=>{r&&(s?r.sendUncompressed(t):r.push(t))}),s.tabId,(()=>postMessage({type:\"queue_empty\"}))),null===p&&(p=setInterval(o,3e4)),a=h.Active):\"auth\"===s.type?r?u?(r.authorise(s.token),void(s.beaconSizeLimit&&u.setBeaconSizeLimit(s.beaconSizeLimit))):(console.debug(\"OR WebWorker: writer not initialised. Received auth.\"),void g()):(console.debug(\"OR WebWorker: sender not initialised. Received auth.\"),void g()):void 0}if(u){const t=u;s.forEach((s=>{55===s[0]&&(s[1]?l=setTimeout((()=>g()),18e5):clearTimeout(l)),t.writeMessage(s)}))}else postMessage(\"not_init\"),g()}else o(!0);else o()}}();\n";
5242
5390
  const CANCELED = 'canceled';
5243
5391
  const uxtStorageKey = 'or_uxt_active';
5244
5392
  const bufferStorageKey = 'or_buffer_1';
@@ -5292,7 +5440,7 @@ class App {
5292
5440
  this.stopCallbacks = [];
5293
5441
  this.commitCallbacks = [];
5294
5442
  this.activityState = ActivityState.NotActive;
5295
- this.version = '16.4.10'; // TODO: version compatability check inside each plugin.
5443
+ this.version = '16.4.11-beta.0'; // TODO: version compatability check inside each plugin.
5296
5444
  this.socketMode = false;
5297
5445
  this.compressionThreshold = 24 * 1000;
5298
5446
  this.bc = null;
@@ -5302,8 +5450,10 @@ class App {
5302
5450
  this.rootId = null;
5303
5451
  this.pageFrames = [];
5304
5452
  this.frameOderNumber = 0;
5305
- this.frameLevel = 0;
5306
- this.features = {};
5453
+ this.features = {
5454
+ 'feature-flags': true,
5455
+ 'usability-test': true,
5456
+ };
5307
5457
  this.emptyBatchCounter = 0;
5308
5458
  /** used by child iframes for crossdomain only */
5309
5459
  this.parentActive = false;
@@ -5333,7 +5483,6 @@ class App {
5333
5483
  this.rootId = data.id;
5334
5484
  this.session.setSessionToken(data.token, this.projectKey);
5335
5485
  this.frameOderNumber = data.frameOrderNumber;
5336
- this.frameLevel = data.frameLevel;
5337
5486
  this.debug.log('starting iframe tracking', data);
5338
5487
  this.allowAppStart();
5339
5488
  }
@@ -5383,8 +5532,8 @@ class App {
5383
5532
  line: proto.iframeId,
5384
5533
  id,
5385
5534
  token,
5535
+ // since indexes go from 0 we +1
5386
5536
  frameOrderNumber: order,
5387
- frameLevel: this.frameLevel + 1,
5388
5537
  };
5389
5538
  this.debug.log('Got child frame signal; nodeId', id, event.source, iframeData);
5390
5539
  // @ts-ignore
@@ -5395,13 +5544,6 @@ class App {
5395
5544
  }
5396
5545
  };
5397
5546
  void signalId();
5398
- if (this.active()) {
5399
- // @ts-ignore
5400
- event.source?.postMessage({ line: proto.startIframe }, '*');
5401
- }
5402
- else {
5403
- this.addCommand(proto.startIframe);
5404
- }
5405
5547
  }
5406
5548
  /**
5407
5549
  * proxying messages from iframe to main body, so they can be in one batch (same indexes, etc)
@@ -5586,170 +5728,9 @@ class App {
5586
5728
  this.orderNumber = 0;
5587
5729
  this.coldStartTs = 0;
5588
5730
  this.singleBuffer = false;
5589
- /**
5590
- * start buffering messages without starting the actual session, which gives
5591
- * user 30 seconds to "activate" and record session by calling `start()` on conditional trigger,
5592
- * and we will then send buffered batch, so it won't get lost
5593
- * */
5594
- this.coldStart = async (startOpts = {}, conditional) => {
5595
- this.singleBuffer = false;
5596
- const second = 1000;
5597
- const isNewSession = this.checkSessionToken(startOpts.forceNew);
5598
- if (conditional) {
5599
- await this.setupConditionalStart(startOpts);
5600
- }
5601
- const cycle = () => {
5602
- this.orderNumber += 1;
5603
- adjustTimeOrigin();
5604
- this.coldStartTs = now();
5605
- if (this.orderNumber % 2 === 0) {
5606
- this.bufferedMessages1.length = 0;
5607
- this.bufferedMessages1.push(Timestamp(this.timestamp()));
5608
- this.bufferedMessages1.push(TabData(this.session.getTabId()));
5609
- }
5610
- else {
5611
- this.bufferedMessages2.length = 0;
5612
- this.bufferedMessages2.push(Timestamp(this.timestamp()));
5613
- this.bufferedMessages2.push(TabData(this.session.getTabId()));
5614
- }
5615
- this.stop(false);
5616
- this.activityState = ActivityState.ColdStart;
5617
- if (startOpts.sessionHash) {
5618
- this.session.applySessionHash(startOpts.sessionHash);
5619
- }
5620
- if (startOpts.forceNew) {
5621
- this.session.reset();
5622
- }
5623
- this.session.assign({
5624
- userID: startOpts.userID,
5625
- metadata: startOpts.metadata,
5626
- });
5627
- if (!isNewSession) {
5628
- this.debug.log('continuing session on new tab', this.session.getTabId());
5629
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
5630
- this.send(TabChange(this.session.getTabId()));
5631
- }
5632
- this.observer.observe();
5633
- this.ticker.start();
5634
- };
5635
- this.coldInterval = setInterval(() => {
5636
- cycle();
5637
- }, 30 * second);
5638
- cycle();
5639
- };
5640
- this.setupConditionalStart = async (startOpts) => {
5641
- this.conditionsManager = new ConditionsManager(this, startOpts);
5642
- const r = await fetch(this.options.ingestPoint + '/v1/web/start', {
5643
- method: 'POST',
5644
- headers: {
5645
- 'Content-Type': 'application/json',
5646
- },
5647
- body: JSON.stringify({
5648
- ...this.getTrackerInfo(),
5649
- timestamp: now(),
5650
- doNotRecord: true,
5651
- bufferDiff: 0,
5652
- userID: this.session.getInfo().userID,
5653
- token: undefined,
5654
- deviceMemory,
5655
- jsHeapSizeLimit,
5656
- timezone: getTimezone(),
5657
- width: window.screen.width,
5658
- height: window.screen.height,
5659
- }),
5660
- });
5661
- const {
5662
- // this token is needed to fetch conditions and flags,
5663
- // but it can't be used to record a session
5664
- token, userBrowser, userCity, userCountry, userDevice, userOS, userState, projectID, features, } = await r.json();
5665
- this.features = features ? features : this.features;
5666
- this.session.assign({ projectID });
5667
- this.session.setUserInfo({
5668
- userBrowser,
5669
- userCity,
5670
- userCountry,
5671
- userDevice,
5672
- userOS,
5673
- userState,
5674
- });
5675
- const onStartInfo = { sessionToken: token, userUUID: '', sessionID: '' };
5676
- this.startCallbacks.forEach((cb) => cb(onStartInfo));
5677
- await this.conditionsManager?.fetchConditions(projectID, token);
5678
- if (this.features['feature-flags']) {
5679
- await this.featureFlags.reloadFlags(token);
5680
- this.conditionsManager?.processFlags(this.featureFlags.flags);
5681
- }
5682
- await this.tagWatcher.fetchTags(this.options.ingestPoint, token);
5683
- };
5684
5731
  this.onSessionSent = () => {
5685
5732
  return;
5686
5733
  };
5687
- /**
5688
- * Starts offline session recording
5689
- * @param {Object} startOpts - options for session start, same as .start()
5690
- * @param {Function} onSessionSent - callback that will be called once session is fully sent
5691
- * */
5692
- this.offlineRecording = (startOpts = {}, onSessionSent) => {
5693
- this.onSessionSent = onSessionSent;
5694
- this.singleBuffer = true;
5695
- const isNewSession = this.checkSessionToken(startOpts.forceNew);
5696
- adjustTimeOrigin();
5697
- this.coldStartTs = now();
5698
- const saverBuffer = this.localStorage.getItem(bufferStorageKey);
5699
- if (saverBuffer) {
5700
- const data = JSON.parse(saverBuffer);
5701
- this.bufferedMessages1 = Array.isArray(data) ? data : this.bufferedMessages1;
5702
- this.localStorage.removeItem(bufferStorageKey);
5703
- }
5704
- this.bufferedMessages1.push(Timestamp(this.timestamp()));
5705
- this.bufferedMessages1.push(TabData(this.session.getTabId()));
5706
- this.activityState = ActivityState.ColdStart;
5707
- if (startOpts.sessionHash) {
5708
- this.session.applySessionHash(startOpts.sessionHash);
5709
- }
5710
- if (startOpts.forceNew) {
5711
- this.session.reset();
5712
- }
5713
- this.session.assign({
5714
- userID: startOpts.userID,
5715
- metadata: startOpts.metadata,
5716
- });
5717
- const onStartInfo = { sessionToken: '', userUUID: '', sessionID: '' };
5718
- this.startCallbacks.forEach((cb) => cb(onStartInfo));
5719
- if (!isNewSession) {
5720
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
5721
- this.send(TabChange(this.session.getTabId()));
5722
- }
5723
- this.observer.observe();
5724
- this.ticker.start();
5725
- return {
5726
- saveBuffer: this.saveBuffer,
5727
- getBuffer: this.getBuffer,
5728
- setBuffer: this.setBuffer,
5729
- };
5730
- };
5731
- /**
5732
- * Saves the captured messages in localStorage (or whatever is used in its place)
5733
- *
5734
- * Then, when this.offlineRecording is called, it will preload this messages and clear the storage item
5735
- *
5736
- * Keeping the size of local storage reasonable is up to the end users of this library
5737
- * */
5738
- this.saveBuffer = () => {
5739
- this.localStorage.setItem(bufferStorageKey, JSON.stringify(this.bufferedMessages1));
5740
- };
5741
- /**
5742
- * @returns buffer with stored messages for offline recording
5743
- * */
5744
- this.getBuffer = () => {
5745
- return this.bufferedMessages1;
5746
- };
5747
- /**
5748
- * Used to set a buffer with messages array
5749
- * */
5750
- this.setBuffer = (buffer) => {
5751
- this.bufferedMessages1 = buffer;
5752
- };
5753
5734
  this.prevOpts = {};
5754
5735
  this.restartCanvasTracking = () => {
5755
5736
  this.canvasRecorder?.restartTracking();
@@ -5816,6 +5797,7 @@ class App {
5816
5797
  forceNgOff: false,
5817
5798
  inlineCss: 0,
5818
5799
  disableSprites: false,
5800
+ disableThrottling: false,
5819
5801
  };
5820
5802
  this.options = simpleMerge(defaultOptions, options);
5821
5803
  if (!this.insideIframe &&
@@ -5871,11 +5853,7 @@ class App {
5871
5853
  * listen for messages from parent window, so we can signal that we're alive
5872
5854
  * */
5873
5855
  window.addEventListener('message', this.parentCrossDomainFrameListener);
5874
- window.addEventListener('message', this.crossDomainIframeListener);
5875
5856
  setInterval(() => {
5876
- if (document.hidden) {
5877
- return;
5878
- }
5879
5857
  window.parent.postMessage({
5880
5858
  line: proto.polling,
5881
5859
  context: this.contextId,
@@ -6258,6 +6236,167 @@ class App {
6258
6236
  const sessionToken = this.session.getSessionToken(this.projectKey);
6259
6237
  return needNewSessionID || !sessionToken;
6260
6238
  }
6239
+ /**
6240
+ * start buffering messages without starting the actual session, which gives
6241
+ * user 30 seconds to "activate" and record session by calling `start()` on conditional trigger,
6242
+ * and we will then send buffered batch, so it won't get lost
6243
+ * */
6244
+ async coldStart(startOpts = {}, conditional) {
6245
+ this.singleBuffer = false;
6246
+ const second = 1000;
6247
+ const isNewSession = this.checkSessionToken(startOpts.forceNew);
6248
+ if (conditional) {
6249
+ await this.setupConditionalStart(startOpts);
6250
+ }
6251
+ const cycle = () => {
6252
+ this.orderNumber += 1;
6253
+ adjustTimeOrigin();
6254
+ this.coldStartTs = now();
6255
+ if (this.orderNumber % 2 === 0) {
6256
+ this.bufferedMessages1.length = 0;
6257
+ this.bufferedMessages1.push(Timestamp(this.timestamp()));
6258
+ this.bufferedMessages1.push(TabData(this.session.getTabId()));
6259
+ }
6260
+ else {
6261
+ this.bufferedMessages2.length = 0;
6262
+ this.bufferedMessages2.push(Timestamp(this.timestamp()));
6263
+ this.bufferedMessages2.push(TabData(this.session.getTabId()));
6264
+ }
6265
+ this.stop(false);
6266
+ this.activityState = ActivityState.ColdStart;
6267
+ if (startOpts.sessionHash) {
6268
+ this.session.applySessionHash(startOpts.sessionHash);
6269
+ }
6270
+ if (startOpts.forceNew) {
6271
+ this.session.reset();
6272
+ }
6273
+ this.session.assign({
6274
+ userID: startOpts.userID,
6275
+ metadata: startOpts.metadata,
6276
+ });
6277
+ if (!isNewSession) {
6278
+ this.debug.log('continuing session on new tab', this.session.getTabId());
6279
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
6280
+ this.send(TabChange(this.session.getTabId()));
6281
+ }
6282
+ this.observer.observe();
6283
+ this.ticker.start();
6284
+ };
6285
+ this.coldInterval = setInterval(() => {
6286
+ cycle();
6287
+ }, 30 * second);
6288
+ cycle();
6289
+ }
6290
+ async setupConditionalStart(startOpts) {
6291
+ this.conditionsManager = new ConditionsManager(this, startOpts);
6292
+ const r = await fetch(this.options.ingestPoint + '/v1/web/start', {
6293
+ method: 'POST',
6294
+ headers: {
6295
+ 'Content-Type': 'application/json',
6296
+ },
6297
+ body: JSON.stringify({
6298
+ ...this.getTrackerInfo(),
6299
+ timestamp: now(),
6300
+ doNotRecord: true,
6301
+ bufferDiff: 0,
6302
+ userID: this.session.getInfo().userID,
6303
+ token: undefined,
6304
+ deviceMemory,
6305
+ jsHeapSizeLimit,
6306
+ timezone: getTimezone(),
6307
+ width: window.screen.width,
6308
+ height: window.screen.height,
6309
+ }),
6310
+ });
6311
+ const {
6312
+ // this token is needed to fetch conditions and flags,
6313
+ // but it can't be used to record a session
6314
+ token, userBrowser, userCity, userCountry, userDevice, userOS, userState, projectID, features, } = await r.json();
6315
+ this.features = features ? features : this.features;
6316
+ this.session.assign({ projectID });
6317
+ this.session.setUserInfo({
6318
+ userBrowser,
6319
+ userCity,
6320
+ userCountry,
6321
+ userDevice,
6322
+ userOS,
6323
+ userState,
6324
+ });
6325
+ const onStartInfo = { sessionToken: token, userUUID: '', sessionID: '' };
6326
+ this.startCallbacks.forEach((cb) => cb(onStartInfo));
6327
+ await this.conditionsManager?.fetchConditions(projectID, token);
6328
+ if (this.features['feature-flags']) {
6329
+ await this.featureFlags.reloadFlags(token);
6330
+ this.conditionsManager?.processFlags(this.featureFlags.flags);
6331
+ }
6332
+ await this.tagWatcher.fetchTags(this.options.ingestPoint, token);
6333
+ }
6334
+ /**
6335
+ * Starts offline session recording
6336
+ * @param {Object} startOpts - options for session start, same as .start()
6337
+ * @param {Function} onSessionSent - callback that will be called once session is fully sent
6338
+ * */
6339
+ offlineRecording(startOpts = {}, onSessionSent) {
6340
+ this.onSessionSent = onSessionSent;
6341
+ this.singleBuffer = true;
6342
+ const isNewSession = this.checkSessionToken(startOpts.forceNew);
6343
+ adjustTimeOrigin();
6344
+ this.coldStartTs = now();
6345
+ const saverBuffer = this.localStorage.getItem(bufferStorageKey);
6346
+ if (saverBuffer) {
6347
+ const data = JSON.parse(saverBuffer);
6348
+ this.bufferedMessages1 = Array.isArray(data) ? data : this.bufferedMessages1;
6349
+ this.localStorage.removeItem(bufferStorageKey);
6350
+ }
6351
+ this.bufferedMessages1.push(Timestamp(this.timestamp()));
6352
+ this.bufferedMessages1.push(TabData(this.session.getTabId()));
6353
+ this.activityState = ActivityState.ColdStart;
6354
+ if (startOpts.sessionHash) {
6355
+ this.session.applySessionHash(startOpts.sessionHash);
6356
+ }
6357
+ if (startOpts.forceNew) {
6358
+ this.session.reset();
6359
+ }
6360
+ this.session.assign({
6361
+ userID: startOpts.userID,
6362
+ metadata: startOpts.metadata,
6363
+ });
6364
+ const onStartInfo = { sessionToken: '', userUUID: '', sessionID: '' };
6365
+ this.startCallbacks.forEach((cb) => cb(onStartInfo));
6366
+ if (!isNewSession) {
6367
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
6368
+ this.send(TabChange(this.session.getTabId()));
6369
+ }
6370
+ this.observer.observe();
6371
+ this.ticker.start();
6372
+ return {
6373
+ saveBuffer: this.saveBuffer,
6374
+ getBuffer: this.getBuffer,
6375
+ setBuffer: this.setBuffer,
6376
+ };
6377
+ }
6378
+ /**
6379
+ * Saves the captured messages in localStorage (or whatever is used in its place)
6380
+ *
6381
+ * Then, when this.offlineRecording is called, it will preload this messages and clear the storage item
6382
+ *
6383
+ * Keeping the size of local storage reasonable is up to the end users of this library
6384
+ * */
6385
+ saveBuffer() {
6386
+ this.localStorage.setItem(bufferStorageKey, JSON.stringify(this.bufferedMessages1));
6387
+ }
6388
+ /**
6389
+ * @returns buffer with stored messages for offline recording
6390
+ * */
6391
+ getBuffer() {
6392
+ return this.bufferedMessages1;
6393
+ }
6394
+ /**
6395
+ * Used to set a buffer with messages array
6396
+ * */
6397
+ setBuffer(buffer) {
6398
+ this.bufferedMessages1 = buffer;
6399
+ }
6261
6400
  /**
6262
6401
  * Uploads the stored session buffer to backend
6263
6402
  * @returns promise that resolves once messages are loaded, it has to be awaited
@@ -6313,7 +6452,7 @@ class App {
6313
6452
  while (this.bufferedMessages1.length > 0) {
6314
6453
  await this.flushBuffer(this.bufferedMessages1);
6315
6454
  }
6316
- this.postToWorker([['q_end']]);
6455
+ this.postToWorker([[-1]]);
6317
6456
  this.clearBuffers();
6318
6457
  }
6319
6458
  async _start(startOpts = {}, resetByWorker = false, conditionName) {
@@ -6465,7 +6604,7 @@ class App {
6465
6604
  }
6466
6605
  await this.tagWatcher.fetchTags(this.options.ingestPoint, token);
6467
6606
  this.activityState = ActivityState.Active;
6468
- if (this.options.crossdomain?.enabled) {
6607
+ if (this.options.crossdomain?.enabled && !this.insideIframe) {
6469
6608
  void this.bootChildrenFrames();
6470
6609
  }
6471
6610
  if (canvasEnabled && !this.options.canvas.disableCanvas) {
@@ -6491,15 +6630,13 @@ class App {
6491
6630
  this.commit();
6492
6631
  /** --------------- COLD START BUFFER ------------------*/
6493
6632
  }
6633
+ if (this.insideIframe && this.rootId) {
6634
+ this.observer.crossdomainObserve(this.rootId, this.frameOderNumber);
6635
+ }
6494
6636
  else {
6495
- if (this.insideIframe && this.rootId) {
6496
- this.observer.crossdomainObserve(this.rootId, this.frameOderNumber, this.frameLevel);
6497
- }
6498
- else {
6499
- this.observer.observe();
6500
- }
6501
- this.ticker.start();
6637
+ this.observer.observe();
6502
6638
  }
6639
+ this.ticker.start();
6503
6640
  this.canvasRecorder?.startTracking();
6504
6641
  if (this.features['usability-test'] && !this.insideIframe) {
6505
6642
  this.uxtManager = this.uxtManager
@@ -6647,7 +6784,7 @@ class App {
6647
6784
  stop(stopWorker = true) {
6648
6785
  if (this.activityState !== ActivityState.NotActive) {
6649
6786
  try {
6650
- if (this.options.crossdomain?.enabled) {
6787
+ if (!this.insideIframe && this.options.crossdomain?.enabled) {
6651
6788
  this.killChildrenFrames();
6652
6789
  }
6653
6790
  this.attributeSender.clear();
@@ -7102,7 +7239,7 @@ function Img (app) {
7102
7239
  const sendImgError = app.safe(function (img) {
7103
7240
  const resolvedSrc = resolveURL(img.src || ''); // Src type is null sometimes. - is it true?
7104
7241
  if (isURL(resolvedSrc)) {
7105
- app.send(ResourceTiming(app.timestamp(), 0, 0, 0, 0, 0, resolvedSrc, 'img', 0, false));
7242
+ app.send(ResourceTiming(app.timestamp(), 0, 0, 0, 0, 0, resolvedSrc, 'img', 0, false, 0, 0, 0, 0, 0, 0, 0));
7106
7243
  }
7107
7244
  });
7108
7245
  const sendImgAttrs = app.safe(function (img) {
@@ -7706,18 +7843,38 @@ function Timing (app, opts) {
7706
7843
  if (shouldSkip) {
7707
7844
  return;
7708
7845
  }
7846
+ // will probably require custom header added to responses for tracked requests:
7847
+ // Timing-Allow-Origin: *
7848
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Timing-Allow-Origin
7849
+ let stalled = 0;
7850
+ if (entry.connectEnd && entry.connectEnd > entry.domainLookupEnd) {
7851
+ // Usual case stalled is time between connection establishment and request start
7852
+ stalled = Math.max(0, entry.requestStart - entry.connectEnd);
7853
+ }
7854
+ else {
7855
+ // Connection reuse case - stalled is time between domain lookup and request start
7856
+ stalled = Math.max(0, entry.requestStart - entry.domainLookupEnd);
7857
+ }
7858
+ const timings = {
7859
+ queueing: entry.requestStart - entry.fetchStart,
7860
+ dnsLookup: entry.domainLookupEnd - entry.domainLookupStart,
7861
+ initialConnection: entry.connectEnd - entry.connectStart,
7862
+ ssl: entry.secureConnectionStart > 0 ? entry.connectEnd - entry.secureConnectionStart : 0,
7863
+ ttfb: entry.responseStart - entry.requestStart,
7864
+ contentDownload: entry.responseEnd - entry.responseStart,
7865
+ total: entry.duration ?? entry.responseEnd - entry.startTime,
7866
+ stalled,
7867
+ };
7709
7868
  const entryName = options.resourceNameSanitizer
7710
7869
  ? options.resourceNameSanitizer(entry.name)
7711
7870
  : entry.name;
7712
- const cached =
7713
- // @ts-ignore
7714
- (entry.responseStatus && entry.responseStatus === 304) ||
7871
+ const cached = (entry.responseStatus && entry.responseStatus === 304) ||
7715
7872
  // @ts-ignore
7716
7873
  (entry.deliveryType && entry.deliveryType === 'cache') ||
7717
7874
  (entry.transferSize === 0 && entry.decodedBodySize > 0);
7718
7875
  const requestFailed = entry.responseStatus && entry.responseStatus >= 400;
7719
7876
  const decodedBodySize = requestFailed ? -111 : entry.decodedBodySize || 0;
7720
- app.send(ResourceTiming(entry.startTime + getTimeOrigin(), entry.duration, entry.responseStart && entry.startTime ? entry.responseStart - entry.startTime : 0, entry.transferSize > entry.encodedBodySize ? entry.transferSize - entry.encodedBodySize : 0, entry.encodedBodySize || 0, decodedBodySize, app.sanitizer.privateMode ? entry.name.replaceAll(/./g, '*') : entryName, entry.initiatorType, entry.transferSize, cached));
7877
+ app.send(ResourceTiming(entry.startTime + getTimeOrigin(), entry.duration, timings.ttfb, entry.transferSize > entry.encodedBodySize ? entry.transferSize - entry.encodedBodySize : 0, entry.encodedBodySize || 0, decodedBodySize, app.sanitizer.privateMode ? entry.name.replaceAll(/./g, '*') : entryName, entry.initiatorType, entry.transferSize, cached, timings.queueing, timings.dnsLookup, timings.initialConnection, timings.ssl, timings.contentDownload, timings.total, timings.stalled));
7721
7878
  }
7722
7879
  const observer = new PerformanceObserver((list) => list.getEntries().forEach(resourceTiming));
7723
7880
  function onVitalsSignal(msg) {
@@ -9664,8 +9821,86 @@ function Tabs (app) {
9664
9821
  app.attachEventListener(window, 'focus', changeTab, false, false);
9665
9822
  }
9666
9823
 
9824
+ function LongAnimationTask (app, opts) {
9825
+ if (!opts.longTasks || !('PerformanceObserver' in window)) {
9826
+ return;
9827
+ }
9828
+ const onEntry = (entry) => {
9829
+ app.send(LongAnimationTask$1(entry.name, entry.duration, entry.blockingDuration, entry.firstUIEventTimestamp, entry.startTime, JSON.stringify(entry.scripts ?? [])));
9830
+ };
9831
+ const observer = new PerformanceObserver((entryList) => {
9832
+ entryList.getEntries().forEach((entry) => {
9833
+ if (entry.entryType === 'long-animation-frame') {
9834
+ onEntry(entry);
9835
+ }
9836
+ });
9837
+ });
9838
+ app.attachStartCallback(() => {
9839
+ performance.getEntriesByType('long-animation-frame').forEach((lat) => {
9840
+ onEntry(lat);
9841
+ });
9842
+ observer.observe({
9843
+ entryTypes: ['long-animation-frame'],
9844
+ });
9845
+ });
9846
+ app.attachStopCallback(() => {
9847
+ observer.disconnect();
9848
+ });
9849
+ }
9850
+
9851
+ const toIgnore = ["composite", "computedOffset", "easing", "offset"];
9852
+ function webAnimations(app, options = {}) {
9853
+ const { allElements = false } = options;
9854
+ let listening = new WeakSet();
9855
+ let handled = new WeakSet();
9856
+ function wire(anim, el, nodeId) {
9857
+ if (handled.has(anim))
9858
+ return;
9859
+ handled.add(anim);
9860
+ anim.addEventListener('finish', () => {
9861
+ const lastKF = anim.effect.getKeyframes().at(-1);
9862
+ const computedStyle = getComputedStyle(el);
9863
+ const keys = Object.keys(lastKF).filter((p) => !toIgnore.includes(p));
9864
+ // @ts-ignore
9865
+ const finalStyle = {};
9866
+ keys.forEach((key) => {
9867
+ finalStyle[key] = computedStyle[key];
9868
+ });
9869
+ app.send(NodeAnimationResult(nodeId, JSON.stringify(finalStyle)));
9870
+ }, { once: true });
9871
+ }
9872
+ function scanElement(el, nodeId) {
9873
+ el.getAnimations({ subtree: false }).forEach((anim) => wire(anim, el, nodeId));
9874
+ }
9875
+ app.nodes.attachNodeCallback((node) => {
9876
+ if ((allElements || node.nodeName.includes('-')) && 'getAnimations' in node) {
9877
+ const animations = node.getAnimations({ subtree: false });
9878
+ const id = app.nodes.getID(node);
9879
+ if (animations.length > 0 && !listening.has(node) && id) {
9880
+ listening.add(node);
9881
+ scanElement(node, id);
9882
+ node.addEventListener('animationstart', () => scanElement(node, id));
9883
+ }
9884
+ }
9885
+ });
9886
+ const origAnimate = Element.prototype.animate;
9887
+ Element.prototype.animate = function (...args) {
9888
+ const anim = origAnimate.apply(this, args);
9889
+ const id = app.nodes.getID(this);
9890
+ if (!id)
9891
+ return anim;
9892
+ wire(anim, this, id);
9893
+ return anim;
9894
+ };
9895
+ app.attachStopCallback(() => {
9896
+ Element.prototype.animate = origAnimate; // Restore original animate method
9897
+ listening = new WeakSet();
9898
+ handled = new WeakSet();
9899
+ });
9900
+ }
9901
+
9667
9902
  const Messages = _Messages;
9668
- const DOCS_SETUP = '/en/sdk/constructor';
9903
+ const DOCS_SETUP = '/en/sdk';
9669
9904
  function processOptions(obj) {
9670
9905
  if (obj == null) {
9671
9906
  console.error(`OpenReplay: invalid options argument type. Please, check documentation on ${DOCS_HOST}${DOCS_SETUP}`);
@@ -9715,7 +9950,7 @@ class API {
9715
9950
  this.signalStartIssue = (reason, missingApi) => {
9716
9951
  const doNotTrack = this.checkDoNotTrack();
9717
9952
  console.log("Tracker couldn't start due to:", JSON.stringify({
9718
- trackerVersion: '16.4.10',
9953
+ trackerVersion: '16.4.11-beta.0',
9719
9954
  projectKey: this.options.projectKey,
9720
9955
  doNotTrack,
9721
9956
  reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,
@@ -9743,6 +9978,12 @@ class API {
9743
9978
  }
9744
9979
  }
9745
9980
  };
9981
+ this.incident = (options) => {
9982
+ if (this.app === null) {
9983
+ return;
9984
+ }
9985
+ this.app.send(Incident(options.label ?? '', options.startTime, options.endTime ?? options.startTime));
9986
+ };
9746
9987
  this.crossdomainMode = Boolean(inIframe() && options.crossdomain?.enabled);
9747
9988
  if (!IN_BROWSER || !processOptions(options)) {
9748
9989
  return;
@@ -9821,6 +10062,7 @@ class API {
9821
10062
  Img(app);
9822
10063
  Input(app, options);
9823
10064
  Timing(app, options);
10065
+ LongAnimationTask(app, options);
9824
10066
  Focus(app);
9825
10067
  Fonts(app);
9826
10068
  const skipNetwork = options.network?.disabled;
@@ -9828,6 +10070,7 @@ class API {
9828
10070
  Network(app, options.network);
9829
10071
  }
9830
10072
  selection(app);
10073
+ webAnimations(app, options.webAnimations);
9831
10074
  window.__OPENREPLAY__ = this;
9832
10075
  if (options.flags && options.flags.onFlagsLoad) {
9833
10076
  this.onFlagsLoad(options.flags.onFlagsLoad);
@@ -10052,6 +10295,13 @@ class API {
10052
10295
  }
10053
10296
  else {
10054
10297
  try {
10298
+ if ('or_timestamp' in payload) {
10299
+ const startTs = this.getSessionInfo()?.timestamp ?? 0;
10300
+ const diff = payload.or_timestamp - startTs;
10301
+ if (diff < 0) {
10302
+ console.error(`OpenReplay: event ${key} has or_timestamp (${payload.or_timestamp}) before session start (${startTs}). It will be ignored.`);
10303
+ }
10304
+ }
10055
10305
  payload = JSON.stringify(payload);
10056
10306
  }
10057
10307
  catch (e) {