@openreplay/tracker 17.2.6 → 17.2.9

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.
@@ -61,7 +61,6 @@ export declare const enum Type {
61
61
  JSException = 78,
62
62
  Zustand = 79,
63
63
  BatchMetadata = 81,
64
- PartitionedMessage = 82,
65
64
  NetworkRequest = 83,
66
65
  WSChannel = 84,
67
66
  ResourceTiming = 85,
@@ -442,11 +441,6 @@ export type BatchMetadata = [
442
441
  number,
443
442
  string
444
443
  ];
445
- export type PartitionedMessage = [
446
- Type.PartitionedMessage,
447
- number,
448
- number
449
- ];
450
444
  export type NetworkRequest = [
451
445
  Type.NetworkRequest,
452
446
  string,
@@ -583,5 +577,5 @@ export type WebVitals = [
583
577
  string,
584
578
  string
585
579
  ];
586
- type Message = Timestamp | SetPageLocationDeprecated | SetViewportSize | SetViewportScroll | CreateDocument | CreateElementNode | CreateTextNode | MoveNode | RemoveNode | SetNodeAttribute | RemoveNodeAttribute | SetNodeData | SetNodeScroll | SetInputTarget | SetInputValue | SetInputChecked | MouseMove | NetworkRequestDeprecated | ConsoleLog | PageLoadTiming | PageRenderTiming | CustomEvent | UserID | UserAnonymousID | Metadata | StringDictGlobal | SetNodeAttributeDictGlobal | NodeAnimationResult | Profiler | OTable | StateAction | ReduxDeprecated | Vuex | MobX | NgRx | GraphQLDeprecated | PerformanceTrack | StringDictDeprecated | SetNodeAttributeDictDeprecated | StringDict | SetNodeAttributeDict | ResourceTimingDeprecatedDeprecated | ConnectionInformation | SetPageVisibility | LoadFontFace | SetNodeFocus | SetNodeAttributeURLBased | SetCSSDataURLBased | TechnicalInfo | CustomIssue | SetNodeSlot | MouseClick | MouseClickDeprecated | CreateIFrameDocument | AdoptedSSReplaceURLBased | AdoptedSSInsertRuleURLBased | AdoptedSSDeleteRule | AdoptedSSAddOwner | AdoptedSSRemoveOwner | JSException | Zustand | BatchMetadata | PartitionedMessage | NetworkRequest | WSChannel | ResourceTiming | Incident | LongAnimationTask | InputChange | SelectionChange | MouseThrashing | UnbindNodes | ResourceTimingDeprecated | TabChange | TabData | CanvasNode | TagTrigger | Redux | SetPageLocation | GraphQL | WebVitals;
580
+ type Message = Timestamp | SetPageLocationDeprecated | SetViewportSize | SetViewportScroll | CreateDocument | CreateElementNode | CreateTextNode | MoveNode | RemoveNode | SetNodeAttribute | RemoveNodeAttribute | SetNodeData | SetNodeScroll | SetInputTarget | SetInputValue | SetInputChecked | MouseMove | NetworkRequestDeprecated | ConsoleLog | PageLoadTiming | PageRenderTiming | CustomEvent | UserID | UserAnonymousID | Metadata | StringDictGlobal | SetNodeAttributeDictGlobal | NodeAnimationResult | Profiler | OTable | StateAction | ReduxDeprecated | Vuex | MobX | NgRx | GraphQLDeprecated | PerformanceTrack | StringDictDeprecated | SetNodeAttributeDictDeprecated | StringDict | SetNodeAttributeDict | ResourceTimingDeprecatedDeprecated | ConnectionInformation | SetPageVisibility | LoadFontFace | SetNodeFocus | SetNodeAttributeURLBased | SetCSSDataURLBased | TechnicalInfo | CustomIssue | SetNodeSlot | MouseClick | MouseClickDeprecated | CreateIFrameDocument | AdoptedSSReplaceURLBased | AdoptedSSInsertRuleURLBased | AdoptedSSDeleteRule | AdoptedSSAddOwner | AdoptedSSRemoveOwner | JSException | Zustand | BatchMetadata | NetworkRequest | WSChannel | ResourceTiming | Incident | LongAnimationTask | InputChange | SelectionChange | MouseThrashing | UnbindNodes | ResourceTimingDeprecated | TabChange | TabData | CanvasNode | TagTrigger | Redux | SetPageLocation | GraphQL | WebVitals;
587
581
  export default Message;
package/dist/cjs/entry.js CHANGED
@@ -1211,13 +1211,6 @@ function BatchMetadata(version, pageNo, firstIndex, timestamp, location) {
1211
1211
  location,
1212
1212
  ];
1213
1213
  }
1214
- function PartitionedMessage(partNo, partTotal) {
1215
- return [
1216
- 82 /* Messages.Type.PartitionedMessage */,
1217
- partNo,
1218
- partTotal,
1219
- ];
1220
- }
1221
1214
  function NetworkRequest(type, method, url, request, response, status, timestamp, duration, transferredBodySize) {
1222
1215
  return [
1223
1216
  83 /* Messages.Type.NetworkRequest */,
@@ -1429,7 +1422,6 @@ var _Messages = /*#__PURE__*/Object.freeze({
1429
1422
  OTable: OTable,
1430
1423
  PageLoadTiming: PageLoadTiming,
1431
1424
  PageRenderTiming: PageRenderTiming,
1432
- PartitionedMessage: PartitionedMessage,
1433
1425
  PerformanceTrack: PerformanceTrack,
1434
1426
  Profiler: Profiler,
1435
1427
  Redux: Redux,
@@ -1524,11 +1516,157 @@ function Performance (app, opts) {
1524
1516
  }
1525
1517
  }
1526
1518
 
1519
+ /**
1520
+ * Two-tier tag matching:
1521
+ * 1. Fast fingerprint lookup by id, data-attr,
1522
+ * or class from the selector's last segment
1523
+ * 2. Fallback iteration using native element.matches()
1524
+ */
1525
+ class TagMatcher {
1526
+ constructor() {
1527
+ this.tags = [];
1528
+ this.byId = new Map();
1529
+ this.byDataAttr = new Map();
1530
+ this.byClass = new Map();
1531
+ this.fallback = [];
1532
+ }
1533
+ setTags(tags) {
1534
+ this.tags = tags;
1535
+ this.byId.clear();
1536
+ this.byDataAttr.clear();
1537
+ this.byClass.clear();
1538
+ this.fallback = [];
1539
+ for (const tag of tags) {
1540
+ const last = lastSegment(tag.selector);
1541
+ if (!last) {
1542
+ this.fallback.push(tag);
1543
+ continue;
1544
+ }
1545
+ if (last.startsWith('#')) {
1546
+ this.byId.set(last.slice(1), tag);
1547
+ }
1548
+ else if (last.startsWith('[data-')) {
1549
+ this.byDataAttr.set(last, tag);
1550
+ }
1551
+ else {
1552
+ const cls = extractClass(last);
1553
+ if (cls) {
1554
+ this.byClass.set(cls, tag);
1555
+ }
1556
+ else {
1557
+ this.fallback.push(tag);
1558
+ }
1559
+ }
1560
+ }
1561
+ }
1562
+ getTags() {
1563
+ return this.tags;
1564
+ }
1565
+ /** Match element, its parent, or direct children against known tag selectors */
1566
+ match(el) {
1567
+ const direct = this.matchExact(el);
1568
+ if (direct)
1569
+ return direct;
1570
+ if (el.parentElement) {
1571
+ const parent = this.matchExact(el.parentElement);
1572
+ if (parent)
1573
+ return parent;
1574
+ }
1575
+ const children = el.children;
1576
+ for (let i = 0; i < children.length; i++) {
1577
+ const child = this.matchExact(children[i]);
1578
+ if (child)
1579
+ return child;
1580
+ }
1581
+ return null;
1582
+ }
1583
+ matchExact(el) {
1584
+ if (el.id && this.byId.has(el.id)) {
1585
+ const tag = this.byId.get(el.id);
1586
+ if (safeMatches(el, tag.selector) && matchesLocation(tag))
1587
+ return tag;
1588
+ }
1589
+ if (this.byDataAttr.size > 0) {
1590
+ const attrs = el.attributes;
1591
+ for (let i = 0; i < attrs.length; i++) {
1592
+ const attr = attrs[i];
1593
+ if (attr.name.startsWith('data-')) {
1594
+ const key = `[${attr.name}="${attr.value}"]`;
1595
+ if (this.byDataAttr.has(key)) {
1596
+ const tag = this.byDataAttr.get(key);
1597
+ if (safeMatches(el, tag.selector) && matchesLocation(tag))
1598
+ return tag;
1599
+ }
1600
+ }
1601
+ }
1602
+ }
1603
+ if (this.byClass.size > 0 && el.classList) {
1604
+ for (let i = 0; i < el.classList.length; i++) {
1605
+ const cls = el.classList[i];
1606
+ if (this.byClass.has(cls)) {
1607
+ const tag = this.byClass.get(cls);
1608
+ if (safeMatches(el, tag.selector) && matchesLocation(tag))
1609
+ return tag;
1610
+ }
1611
+ }
1612
+ }
1613
+ for (const tag of this.fallback) {
1614
+ if (safeMatches(el, tag.selector) && matchesLocation(tag))
1615
+ return tag;
1616
+ }
1617
+ return null;
1618
+ }
1619
+ clear() {
1620
+ this.tags = [];
1621
+ this.byId.clear();
1622
+ this.byDataAttr.clear();
1623
+ this.byClass.clear();
1624
+ this.fallback = [];
1625
+ }
1626
+ }
1627
+ /** Last combinator-separated segment of a CSS selector */
1628
+ function lastSegment(selector) {
1629
+ const trimmed = selector.trim();
1630
+ if (!trimmed)
1631
+ return null;
1632
+ const parts = trimmed.split(/\s*[>+~ ]\s*/);
1633
+ const last = parts[parts.length - 1]?.trim();
1634
+ return last || null;
1635
+ }
1636
+ /** First class name from a selector segment, e.g. "div.my-class" -> "my-class" */
1637
+ function extractClass(segment) {
1638
+ const match = segment.match(/\.([a-zA-Z_-][\w-]*)/);
1639
+ return match ? match[1] : null;
1640
+ }
1641
+ function safeMatches(el, selector) {
1642
+ try {
1643
+ return el.matches(selector);
1644
+ }
1645
+ catch {
1646
+ return false;
1647
+ }
1648
+ }
1649
+ function matchesLocation(tag) {
1650
+ if (!tag.location)
1651
+ return true;
1652
+ try {
1653
+ const loc = tag.location;
1654
+ if (loc.startsWith('/')) {
1655
+ return window.location.pathname === loc;
1656
+ }
1657
+ return window.location.href === loc;
1658
+ }
1659
+ catch {
1660
+ return true;
1661
+ }
1662
+ }
1663
+
1527
1664
  const WATCHED_TAGS_KEY = '__or__watched_tags__';
1528
1665
  class TagWatcher {
1529
1666
  constructor(params) {
1530
1667
  this.interval = null;
1531
1668
  this.tags = [];
1669
+ this.matcher = new TagMatcher();
1532
1670
  this.sessionStorage = params.sessionStorage;
1533
1671
  this.errLog = params.errLog;
1534
1672
  this.onTag = params.onTag;
@@ -1569,12 +1707,15 @@ class TagWatcher {
1569
1707
  }
1570
1708
  setTags(tags) {
1571
1709
  this.tags = tags;
1710
+ this.matcher.setTags(tags);
1572
1711
  if (this.interval) {
1573
1712
  clearInterval(this.interval);
1574
1713
  this.interval = null;
1575
1714
  }
1576
1715
  this.interval = setInterval(() => {
1577
1716
  this.tags.forEach((tag) => {
1717
+ if (!matchesLocation(tag))
1718
+ return;
1578
1719
  const possibleEls = document.querySelectorAll(tag.selector);
1579
1720
  if (possibleEls.length > 0) {
1580
1721
  const el = possibleEls[0];
@@ -1586,13 +1727,17 @@ class TagWatcher {
1586
1727
  }, 500);
1587
1728
  }
1588
1729
  onTagRendered(tagId) {
1589
- if (this.tags.findIndex(t => t.id === tagId)) {
1590
- this.tags = this.tags.filter((tag) => tag.id !== tagId);
1730
+ const tag = this.tags.find(t => t.id === tagId);
1731
+ if (tag) {
1732
+ this.tags = this.tags.filter((t) => t.id !== tagId);
1733
+ if (!matchesLocation(tag))
1734
+ return;
1591
1735
  }
1592
1736
  this.onTag(tagId);
1593
1737
  }
1594
1738
  clear() {
1595
1739
  this.tags = [];
1740
+ this.matcher.clear();
1596
1741
  if (this.interval) {
1597
1742
  clearInterval(this.interval);
1598
1743
  this.interval = null;
@@ -2245,8 +2390,44 @@ function inlineRemoteCss(node, id, baseHref, getNextID, insertRule, addOwner, fo
2245
2390
  })
2246
2391
  .catch(error => {
2247
2392
  console.error(`OpenReplay: Failed to fetch CSS from ${node.href}:`, error);
2393
+ // Fetch failed (likely CORS) — retry via load event + sheet access
2394
+ retryViaLoadEvent();
2248
2395
  });
2249
2396
  }
2397
+ else {
2398
+ retryViaLoadEvent();
2399
+ }
2400
+ function retryViaLoadEvent() {
2401
+ const trySheet = () => {
2402
+ const loadedSheet = node.sheet;
2403
+ if (loadedSheet) {
2404
+ try {
2405
+ const cssText = stringifyStylesheet(loadedSheet);
2406
+ if (cssText) {
2407
+ if (sendPlain && onPlain) {
2408
+ onPlain(cssText, fakeIdHolder++);
2409
+ }
2410
+ else {
2411
+ processCssText(cssText);
2412
+ }
2413
+ return;
2414
+ }
2415
+ }
2416
+ catch (e) {
2417
+ // cross-origin sheet — nothing more we can do
2418
+ }
2419
+ }
2420
+ };
2421
+ if (node.sheet) {
2422
+ trySheet();
2423
+ }
2424
+ else {
2425
+ node.addEventListener('load', function onLoad() {
2426
+ node.removeEventListener('load', onLoad);
2427
+ trySheet();
2428
+ });
2429
+ }
2430
+ }
2250
2431
  function processCssText(cssText) {
2251
2432
  // Remove comments
2252
2433
  cssText = cssText.replace(/\/\*[\s\S]*?\*\//g, '');
@@ -2468,7 +2649,6 @@ function ConstructedStyleSheets (app) {
2468
2649
  if (!hasAdoptedSS(document)) {
2469
2650
  return;
2470
2651
  }
2471
- const styleSheetIDMap = new Map();
2472
2652
  const adoptedStyleSheetsOwnings = new Map();
2473
2653
  const sendAdoptedStyleSheetsUpdate = (root) => setTimeout(() => {
2474
2654
  let nodeID = app.nodes.getID(root);
@@ -2733,6 +2913,12 @@ class Observer {
2733
2913
  this.inlineRemoteCss = false;
2734
2914
  this.inlinerOptions = undefined;
2735
2915
  this.domParser = new DOMParser();
2916
+ /**
2917
+ * Bumped on every disconnect(). Stale async CSS-inlining callbacks from a
2918
+ * previous session (e.g. agent reload → tracker restart) compare against
2919
+ * this and bail instead of sending messages that reference vanished node ids.
2920
+ */
2921
+ this.generation = 0;
2736
2922
  this.throttling = true;
2737
2923
  this.throttledSetNodeData = throttleWithTrailing((id, parentElement, data) => this.sendNodeData(id, parentElement, data), 30);
2738
2924
  this.throttling = !Boolean(options.disableThrottling);
@@ -2879,18 +3065,23 @@ class Observer {
2879
3065
  }
2880
3066
  if (name === 'style' || (name === 'href' && hasTag(node, 'link'))) {
2881
3067
  if ('rel' in node && node.rel === 'stylesheet' && this.inlineRemoteCss) {
3068
+ const gen = this.generation;
2882
3069
  setTimeout(() => {
2883
3070
  inlineRemoteCss(
2884
3071
  // @ts-ignore
2885
3072
  node, id, this.app.getBaseHref(), nextID, (id, cssText, index, baseHref) => {
3073
+ if (this.generation !== gen)
3074
+ return;
2886
3075
  this.app.send(AdoptedSSInsertRuleURLBased(id, cssText, index, baseHref));
2887
3076
  }, (sheetId, ownerId) => {
3077
+ if (this.generation !== gen)
3078
+ return;
2888
3079
  this.app.send(AdoptedSSAddOwner(sheetId, ownerId));
2889
3080
  }, this.inlinerOptions?.forceFetch, this.inlinerOptions?.forcePlain, (cssText, fakeTextId) => {
3081
+ if (this.generation !== gen)
3082
+ return;
2890
3083
  this.app.send(CreateTextNode(fakeTextId, id, 0));
2891
- setTimeout(() => {
2892
- this.app.send(SetCSSDataURLBased(fakeTextId, cssText, this.app.getBaseHref()));
2893
- }, 10);
3084
+ this.app.send(SetCSSDataURLBased(fakeTextId, cssText, this.app.getBaseHref()));
2894
3085
  });
2895
3086
  }, 0);
2896
3087
  return;
@@ -3154,6 +3345,7 @@ class Observer {
3154
3345
  this.observer.disconnect();
3155
3346
  this.clear();
3156
3347
  this.throttledSetNodeData.clear();
3348
+ this.generation++;
3157
3349
  }
3158
3350
  }
3159
3351
 
@@ -3758,7 +3950,7 @@ class Ticker {
3758
3950
  * this value is injected during build time via rollup
3759
3951
  * */
3760
3952
  // @ts-ignore
3761
- 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 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 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 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 73:return this.uint(t[1])&&this.string(t[2])&&this.uint(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";
3953
+ 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 65:case 70:case 75:case 76:case 77: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 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 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 73:return this.uint(t[1])&&this.string(t[2])&&this.uint(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";
3762
3954
  const CANCELED = 'canceled';
3763
3955
  const bufferStorageKey = 'or_buffer_1';
3764
3956
  const UnsuccessfulStart = (reason) => ({ reason, success: false });
@@ -3800,6 +3992,9 @@ const proto = {
3800
3992
  reset: 'reset-your-session-please',
3801
3993
  };
3802
3994
  class App {
3995
+ get tagMatcher() {
3996
+ return this.tagWatcher.matcher;
3997
+ }
3803
3998
  constructor(projectKey, sessionToken, options, signalError, insideIframe) {
3804
3999
  this.signalError = signalError;
3805
4000
  this.insideIframe = insideIframe;
@@ -3814,7 +4009,7 @@ class App {
3814
4009
  this.stopCallbacks = [];
3815
4010
  this.commitCallbacks = [];
3816
4011
  this.activityState = ActivityState.NotActive;
3817
- this.version = '17.2.6'; // TODO: version compatability check inside each plugin.
4012
+ this.version = '17.2.9'; // TODO: version compatability check inside each plugin.
3818
4013
  this.socketMode = false;
3819
4014
  this.compressionThreshold = 24 * 1000;
3820
4015
  this.bc = null;
@@ -3836,12 +4031,19 @@ class App {
3836
4031
  if (!data || event.source === window)
3837
4032
  return;
3838
4033
  if (data.line === proto.startIframe) {
3839
- if (this.active())
4034
+ // Avoid corrupting an in-flight start; let it complete.
4035
+ if (this.activityState === ActivityState.Starting)
3840
4036
  return;
3841
4037
  try {
4038
+ if (this.active()) {
4039
+ this.stop();
4040
+ }
3842
4041
  if (data.token) {
3843
4042
  this.session.setSessionToken(data.token, this.projectKey);
3844
4043
  }
4044
+ if (data.id !== undefined) {
4045
+ this.rootId = data.id;
4046
+ }
3845
4047
  this.allowAppStart();
3846
4048
  void this.start();
3847
4049
  }
@@ -3867,17 +4069,22 @@ class App {
3867
4069
  }
3868
4070
  }
3869
4071
  };
3870
- /**
3871
- * context ids for iframes,
3872
- * order is not so important as long as its consistent
3873
- * */
3874
4072
  this.trackedFrames = [];
4073
+ this.frameLastSeen = new Map();
4074
+ this.FRAME_STALE_MS = 1500;
3875
4075
  this.crossDomainIframeListener = (event) => {
3876
- if (!this.active() || event.source === window)
4076
+ if (event.source === window)
3877
4077
  return;
3878
4078
  const { data } = event;
3879
4079
  if (!data)
3880
4080
  return;
4081
+ // Record liveness regardless of our own active state so the queue can prune
4082
+ // stale contexts reliably once we resume dispatching commands after a cycle.
4083
+ if ((data.line === proto.polling || data.line === proto.iframeSignal) && data.context) {
4084
+ this.frameLastSeen.set(data.context, Date.now());
4085
+ }
4086
+ if (!this.active())
4087
+ return;
3881
4088
  if (data.line === proto.iframeSignal) {
3882
4089
  // @ts-ignore
3883
4090
  event.source?.postMessage({ ping: true, line: proto.parentAlive }, '*');
@@ -3975,20 +4182,41 @@ class App {
3975
4182
  if (!this.pollingQueue.order.length) {
3976
4183
  return;
3977
4184
  }
3978
- const nextCommand = this.pollingQueue.order[0];
3979
- if (nextCommand && this.pollingQueue[nextCommand].length === 0) {
3980
- this.pollingQueue.order = this.pollingQueue.order.filter((c) => c !== nextCommand);
4185
+ this.pruneStaleFrames();
4186
+ const liveSet = new Set(this.trackedFrames);
4187
+ while (this.pollingQueue.order.length > 0) {
4188
+ const head = this.pollingQueue.order[0];
4189
+ this.pollingQueue[head] = this.pollingQueue[head].filter((ctx) => liveSet.has(ctx));
4190
+ if (this.pollingQueue[head].length === 0) {
4191
+ delete this.pollingQueue[head];
4192
+ this.pollingQueue.order.shift();
4193
+ }
4194
+ else {
4195
+ break;
4196
+ }
4197
+ }
4198
+ if (!this.pollingQueue.order.length) {
3981
4199
  return;
3982
4200
  }
4201
+ const nextCommand = this.pollingQueue.order[0];
3983
4202
  if (this.pollingQueue[nextCommand].includes(data.context)) {
3984
4203
  this.pollingQueue[nextCommand] = this.pollingQueue[nextCommand].filter((c) => c !== data.context);
3985
4204
  const message = { line: nextCommand };
3986
4205
  if (nextCommand === proto.startIframe) {
3987
4206
  message.token = this.session.getSessionToken(this.projectKey);
4207
+ const targetFrame = this.pageFrames.find((f) => f.contentWindow === event.source)
4208
+ || Array.from(document.querySelectorAll('iframe')).find((f) => f.contentWindow === event.source);
4209
+ if (targetFrame) {
4210
+ const nodeId = targetFrame[this.options.node_id];
4211
+ if (nodeId !== undefined) {
4212
+ message.id = nodeId;
4213
+ }
4214
+ }
3988
4215
  }
3989
4216
  // @ts-ignore
3990
4217
  event.source?.postMessage(message, '*');
3991
4218
  if (this.pollingQueue[nextCommand].length === 0) {
4219
+ delete this.pollingQueue[nextCommand];
3992
4220
  this.pollingQueue.order.shift();
3993
4221
  }
3994
4222
  }
@@ -4002,6 +4230,11 @@ class App {
4002
4230
  order: [],
4003
4231
  };
4004
4232
  this.addCommand = (cmd) => {
4233
+ this.pruneStaleFrames();
4234
+ if (this.pollingQueue[cmd]) {
4235
+ this.pollingQueue[cmd] = Array.from(new Set([...this.pollingQueue[cmd], ...this.trackedFrames]));
4236
+ return;
4237
+ }
4005
4238
  this.pollingQueue.order.push(cmd);
4006
4239
  this.pollingQueue[cmd] = [...this.trackedFrames];
4007
4240
  };
@@ -4313,6 +4546,16 @@ class App {
4313
4546
  };
4314
4547
  }
4315
4548
  }
4549
+ pruneStaleFrames() {
4550
+ const staleAfter = Date.now() - this.FRAME_STALE_MS;
4551
+ this.trackedFrames = this.trackedFrames.filter((ctx) => {
4552
+ const last = this.frameLastSeen.get(ctx);
4553
+ if (last !== undefined && last >= staleAfter)
4554
+ return true;
4555
+ this.frameLastSeen.delete(ctx);
4556
+ return false;
4557
+ });
4558
+ }
4316
4559
  allowAppStart() {
4317
4560
  this.canStart = true;
4318
4561
  if (this.startTimeout) {
@@ -4863,6 +5106,9 @@ class App {
4863
5106
  if (Object.keys(startOpts).length !== 0) {
4864
5107
  this.prevOpts = startOpts;
4865
5108
  }
5109
+ if (startOpts.startCallback) {
5110
+ this.userStartCallback = startOpts.startCallback;
5111
+ }
4866
5112
  const isColdStart = this.activityState === ActivityState.ColdStart;
4867
5113
  if (isColdStart && this.coldInterval) {
4868
5114
  clearInterval(this.coldInterval);
@@ -5002,8 +5248,8 @@ class App {
5002
5248
  // TODO: start as early as possible (before receiving the token)
5003
5249
  /** after start */
5004
5250
  this.startCallbacks.forEach((cb) => cb(onStartInfo)); // MBTODO: callbacks after DOM "mounted" (observed)
5005
- if (startOpts.startCallback) {
5006
- startOpts.startCallback(SuccessfulStart(onStartInfo));
5251
+ if (this.userStartCallback) {
5252
+ this.userStartCallback(SuccessfulStart(onStartInfo));
5007
5253
  }
5008
5254
  this.activityState = ActivityState.Active;
5009
5255
  if (this.options.crossdomain?.enabled) {
@@ -5166,6 +5412,7 @@ class App {
5166
5412
  this.canvasRecorder?.clear();
5167
5413
  this.messages.length = 0;
5168
5414
  this.parentActive = false;
5415
+ this.pageFrames = [];
5169
5416
  }
5170
5417
  finally {
5171
5418
  this.activityState = ActivityState.NotActive;
@@ -5748,6 +5995,18 @@ function Input (app, opts) {
5748
5995
  defaultInputMode: InputMode.Obscured,
5749
5996
  obscureInputDates: false,
5750
5997
  }, opts);
5998
+ const tagSelectorMap = new Map();
5999
+ function getTagSelector(id, node) {
6000
+ const cached = tagSelectorMap.get(id);
6001
+ if (cached !== undefined)
6002
+ return cached;
6003
+ const tagMatch = app.tagMatcher.match(node);
6004
+ if (tagMatch) {
6005
+ tagSelectorMap.set(id, tagMatch.selector);
6006
+ return tagMatch.selector;
6007
+ }
6008
+ return null;
6009
+ }
5751
6010
  function getInputValue(id, node) {
5752
6011
  let value = node.value;
5753
6012
  let inputMode = options.defaultInputMode;
@@ -5783,6 +6042,7 @@ function Input (app, opts) {
5783
6042
  app.attachStopCallback(() => {
5784
6043
  inputValues.clear();
5785
6044
  checkboxValues.clear();
6045
+ tagSelectorMap.clear();
5786
6046
  });
5787
6047
  function trackInputValue(id, node) {
5788
6048
  if (inputValues.get(id) === node.value) {
@@ -5815,7 +6075,7 @@ function Input (app, opts) {
5815
6075
  }, 3);
5816
6076
  function sendInputChange(id, node, hesitationTime, inputTime) {
5817
6077
  const { value, mask } = getInputValue(id, node);
5818
- let label = getInputLabel(node, options.customAttributes);
6078
+ let label = getTagSelector(id, node) || getInputLabel(node, options.customAttributes);
5819
6079
  if (app.sanitizer.privateMode) {
5820
6080
  label = label.replaceAll(/./g, '*');
5821
6081
  }
@@ -5998,7 +6258,13 @@ function Mouse (app, options) {
5998
6258
  };
5999
6259
  const patchDocument = (document, topframe = false) => {
6000
6260
  function getSelector(id, target) {
6001
- return (selectorMap[id] = selectorMap[id] || _getSelector(target));
6261
+ if (selectorMap[id])
6262
+ return selectorMap[id];
6263
+ const tagMatch = app.tagMatcher.match(target);
6264
+ if (tagMatch) {
6265
+ return (selectorMap[id] = tagMatch.selector);
6266
+ }
6267
+ return (selectorMap[id] = _getSelector(target));
6002
6268
  }
6003
6269
  const attachListener = topframe
6004
6270
  ? app.attachEventListener.bind(app) // attached/removed on start/stop
@@ -8676,7 +8942,7 @@ class ConstantProperties {
8676
8942
  user_id: this.user_id,
8677
8943
  distinct_id: this.deviceId,
8678
8944
  sdk_edition: 'web',
8679
- sdk_version: '17.2.6',
8945
+ sdk_version: '17.2.9',
8680
8946
  timezone: getUTCOffsetString(),
8681
8947
  search_engine: this.searchEngine,
8682
8948
  };
@@ -9378,7 +9644,7 @@ class API {
9378
9644
  this.signalStartIssue = (reason, missingApi) => {
9379
9645
  const doNotTrack = this.checkDoNotTrack();
9380
9646
  console.log("Tracker couldn't start due to:", JSON.stringify({
9381
- trackerVersion: '17.2.6',
9647
+ trackerVersion: '17.2.9',
9382
9648
  projectKey: this.options.projectKey,
9383
9649
  doNotTrack,
9384
9650
  reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,