@openreplay/tracker 18.1.3 → 18.1.5

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.
@@ -12,24 +12,16 @@ type Start = {
12
12
  url: string;
13
13
  tabId: string;
14
14
  localDebug?: boolean;
15
+ compressionThreshold?: number;
15
16
  } & Options;
16
17
  type Auth = {
17
18
  type: 'auth';
18
19
  token: string;
19
20
  beaconSizeLimit?: number;
20
21
  protocolVersion?: number;
22
+ compressionThreshold?: number;
21
23
  };
22
- export type ToWorkerData = null | 'stop' | Start | Auth | Array<Message> | {
23
- type: 'compressed';
24
- batch: Uint8Array;
25
- dataType: DataType;
26
- split?: number;
27
- } | {
28
- type: 'uncompressed';
29
- batch: Uint8Array;
30
- dataType: DataType;
31
- split?: number;
32
- } | 'forceFlushBatch' | 'closing' | 'check_queue';
24
+ export type ToWorkerData = null | 'stop' | Start | Auth | Array<Message> | 'forceFlushBatch' | 'closing' | 'check_queue';
33
25
  type Failure = {
34
26
  type: 'failure';
35
27
  reason: string;
@@ -42,10 +34,5 @@ type LocalSave = {
42
34
  name: string;
43
35
  batch: Uint8Array;
44
36
  };
45
- export type FromWorkerData = 'a_stop' | 'a_start' | Failure | 'not_init' | {
46
- type: 'compress';
47
- batch: Uint8Array;
48
- dataType: DataType;
49
- split?: number;
50
- } | QEmpty | LocalSave;
37
+ export type FromWorkerData = 'a_stop' | 'a_start' | Failure | 'not_init' | QEmpty | LocalSave;
51
38
  export {};
package/dist/cjs/entry.js CHANGED
@@ -459,6 +459,80 @@ function getClassSelector(e) {
459
459
  return '';
460
460
  return '.' + Array.from(e.classList).join('.');
461
461
  }
462
+ const cssEscape = (typeof CSS !== 'undefined' && CSS.escape) || ((str) => str);
463
+ /** escape a value that goes inside a quoted css attribute selector */
464
+ function cssAttrValue(value) {
465
+ return value.replace(/(["\\])/g, '\\$1');
466
+ }
467
+ function getCustomAttributeSelector(e, customAttributes) {
468
+ if (!customAttributes || customAttributes.length === 0)
469
+ return '';
470
+ for (const attr of customAttributes) {
471
+ const value = e.getAttribute(attr);
472
+ if (value !== null) {
473
+ return `[${attr}="${cssAttrValue(value)}"]`;
474
+ }
475
+ }
476
+ return '';
477
+ }
478
+ const TEXT_LABEL_ATTRIBUTES = ['aria-label', 'title', 'alt', 'placeholder'];
479
+ const VALUE_INPUT_TYPES = ['button', 'submit', 'reset'];
480
+ function cleanLabel(str) {
481
+ const label = normSpaces(str);
482
+ return /[a-z0-9]/i.test(label) ? label.slice(0, 100) : '';
483
+ }
484
+ function getOwnTextLabel(e, getInnerText, isAncestor) {
485
+ const tag = e.tagName.toUpperCase();
486
+ if (tag !== 'SELECT') {
487
+ const rawText = getInnerText(e) || '';
488
+ // truncated or multiline text belongs to a container, not to this element
489
+ if (rawText.length <= 100 && !(isAncestor && /[\r\n]/.test(rawText))) {
490
+ const text = cleanLabel(rawText);
491
+ if (text)
492
+ return text;
493
+ }
494
+ }
495
+ for (const attr of TEXT_LABEL_ATTRIBUTES) {
496
+ const value = e.getAttribute(attr);
497
+ if (value) {
498
+ const label = cleanLabel(value);
499
+ if (label)
500
+ return label;
501
+ }
502
+ }
503
+ if (tag === 'INPUT' && VALUE_INPUT_TYPES.includes(e.type)) {
504
+ const label = cleanLabel(e.value);
505
+ if (label)
506
+ return label;
507
+ }
508
+ // icon-only elements carry their meaning on a child
509
+ const describedChild = e.querySelector('[aria-label], img[alt], [title]');
510
+ if (describedChild) {
511
+ for (const attr of TEXT_LABEL_ATTRIBUTES) {
512
+ const value = describedChild.getAttribute(attr);
513
+ if (value) {
514
+ const label = cleanLabel(value);
515
+ if (label)
516
+ return label;
517
+ }
518
+ }
519
+ }
520
+ return '';
521
+ }
522
+ /**
523
+ * human readable label of a clicked element: its own text/description,
524
+ * then the same for a few ancestors (a click on an icon inside a card)
525
+ * */
526
+ function getTextualLabel(e, getInnerText, maxAncestors = 2) {
527
+ let el = e;
528
+ for (let depth = 0; el !== null && depth <= maxAncestors; depth++) {
529
+ const label = getOwnTextLabel(el, getInnerText, depth > 0);
530
+ if (label)
531
+ return label;
532
+ el = el.parentElement;
533
+ }
534
+ return '';
535
+ }
462
536
  function getLabelAttribute(e) {
463
537
  let value = e.getAttribute('data-openreplay-label');
464
538
  if (value !== null) {
@@ -635,11 +709,21 @@ class FIFOTaskScheduler {
635
709
  this.isRunning = false;
636
710
  return;
637
711
  }
638
- // Get the next task and execute it
712
+ // Get the next task and execute it. A task that throws (or rejects) must
713
+ // still schedule the next one: leaving `isRunning` true strands the queue
714
+ // for the lifetime of the page, and since commits run through here that
715
+ // silently ends the recording. See #4836.
639
716
  const nextTask = this.taskQueue.shift();
640
- Promise.resolve(nextTask()).then(() => {
641
- requestAnimationFrame(() => executeNextTask());
642
- });
717
+ const scheduleNext = () => requestAnimationFrame(() => executeNextTask());
718
+ let result;
719
+ try {
720
+ result = nextTask();
721
+ }
722
+ catch (e) {
723
+ scheduleNext();
724
+ throw e;
725
+ }
726
+ Promise.resolve(result).then(scheduleNext, scheduleNext);
643
727
  };
644
728
  executeNextTask();
645
729
  }
@@ -4299,7 +4383,7 @@ class Ticker {
4299
4383
  * this value is injected during build time via rollup
4300
4384
  * */
4301
4385
  // @ts-ignore
4302
- 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.inflightKeepaliveBytes=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,s=\"player\",i){if(this.busy||!this.token)this.queue.push({batch:t,dataType:s,split:i});else if(this.busy=!0,this.isCompressing&&this.onCompress)this.onCompress(t,s,i);else{const e=++this.lastBatchNum;this.sendBatch(t,!1,e,s,i)}}sendNext(){const t=this.queue.shift();if(t)if(this.busy=!0,this.isCompressing&&this.onCompress)this.onCompress(t.batch,t.dataType,t.split);else{const s=++this.lastBatchNum;this.sendBatch(t.batch,!1,s,t.dataType,t.split)}else this.busy=!1}retry(t,s,i,e=\"player\",n){if(this.attemptsCount>=this.MAX_ATTEMPTS_COUNT)return void this.onFailure(`Failed to send batch after ${this.attemptsCount} attempts.`);this.attemptsCount++;const h=new Uint8Array(t);setTimeout((()=>this.sendBatch(h,s,i,e,n)),this.ATTEMPT_TIMEOUT*this.attemptsCount)}sendBatch(t,s,i,e=\"player\",n){var h,r,a;if(0===t.length)return console.error(\"OpenReplay: refusing to send 0-byte batch.\",{batchNum:i,dataType:e,isCompressed:s,batch:t}),this.attemptsCount=0,void this.sendNext();const u=(null!==(h=null==i?void 0:i.toString())&&void 0!==h?h:\"0\").match(/^([^_]+)(?:_([^_]+))?/),o=null!==(r=null==u?void 0:u[1])&&void 0!==r?r:\"0\",l=(null==u?void 0:u[2])?u[2]:\"\";this.busy=!0;const c={Authorization:`Bearer ${this.token}`,DataType:e};if(s&&(c[\"Content-Encoding\"]=\"gzip\"),null===this.token)return void setTimeout((()=>{this.sendBatch(t,s,`${null!=i?i:\"noBatchNum\"}_newToken`,e,n)}),500);const d=t.length<65536&&this.inflightKeepaliveBytes+t.length<=65536;d&&(this.inflightKeepaliveBytes+=t.length);const p=()=>{d&&(this.inflightKeepaliveBytes-=t.length)},f=t.byteLength;let g=this.ingestURL;g+=`?batch=${null!==(a=this.pageNo)&&void 0!==a?a:0}`,g+=`_${o}`,g+=`_${f}`,g+=\"_\"+(d?\"kyes\":\"kno\"),l&&(g+=`_${l}`),void 0!==n&&(g+=`&split=${n}`),fetch(g,{body:t,method:\"POST\",headers:c,keepalive:d}).then((h=>{var r;if(p(),null===(r=h.body)||void 0===r||r.cancel().catch((()=>{})),401===h.status)return this.busy=!1,void this.onUnauthorised();h.status>=400?this.retry(t,s,`${null!=i?i:\"noBatchNum\"}_network:${h.status}`,e,n):(this.attemptsCount=0,this.sendNext())})).catch((h=>{p(),console.warn(\"OpenReplay:\",h),this.retry(t,s,`${null!=i?i:\"noBatchNum\"}_reject:${h.message}`,e,n)}))}sendCompressed(t,s=\"player\",i){const e=++this.lastBatchNum;this.sendBatch(t,!0,e,s,i)}sendUncompressed(t,s=\"player\",i){const e=++this.lastBatchNum;this.sendBatch(t,!1,e,s,i)}clean(){this.sendNext(),setTimeout((()=>{this.token=null,this.queue.length=0}),10)}}const s=new Set([60,61,71,73]),i=new Set([21,22,40,41,44,45,46,47,48,79,83,84,85,87,89,116,120,121,123]),e=new Set([17,23,24,27,28,29,30,42,63,64,78,112,115,124]),n=\"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 h{constructor(t){this.size=t,this.offset=0,this.checkpointOffset=0,this.data=new Uint8Array(t)}getCurrentOffset(){return this.offset}getCurrentCheckpoint(){return this.checkpointOffset}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 s=n.encode(t),i=s.byteLength;return!(!this.uint(i)||this.offset+i>this.size)&&(this.data.set(s,this.offset),this.offset+=i,!0)}reset(){this.offset=0,this.checkpointOffset=0}rewind(t,s){t>this.offset||s>this.checkpointOffset||(this.offset=t,this.checkpointOffset=s)}flush(){const t=this.data.slice(0,this.checkpointOffset);return this.reset(),t}}class r extends h{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 a{constructor(t,s,i){this.bufferSize=t,this.version=s,this.dataType=i,this.sizeBuffer=new Uint8Array(3),this.snap=null,this.hasNonTimestamp=!1,this.lastPushedTs=0,this.encoder=new r(t)}push(t,s){const i=this.encoder,e=null===this.snap,n=i.getCurrentOffset(),h=i.getCurrentCheckpoint();if(e){const t={pageNo:s.pageNo,firstIndex:s.index,timestamp:s.timestamp,url:s.url,tabId:s.tabId};if(!this.writeHeader(t))return i.rewind(n,h),!1;this.snap=t,this.lastPushedTs=s.timestamp}return 0===t[0]||s.timestamp===this.lastPushedTs||this.writeMessageWithSize([0,s.timestamp])?this.writeMessageWithSize(t)?(this.lastPushedTs=s.timestamp,0!==t[0]&&(this.hasNonTimestamp=!0),!0):(i.rewind(n,h),e&&(this.snap=null),!1):(i.rewind(n,h),!1)}hasContent(){return null!==this.snap&&this.hasNonTimestamp}size(){return null===this.snap?0:this.encoder.getCurrentOffset()}flush(){if(!this.hasContent())return this.reset(),null;const t=this.encoder.flush();return this.snap=null,this.hasNonTimestamp=!1,this.lastPushedTs=0,t}reset(){this.encoder.reset(),this.snap=null,this.hasNonTimestamp=!1,this.lastPushedTs=0}writeHeader(t){const s=this.encoder,i=[81,this.version,t.pageNo,t.firstIndex,t.timestamp,t.url];return!!s.uint(i[0])&&(!!s.encode(i)&&(!(s.getCurrentOffset()>this.bufferSize)&&(s.checkpoint(),!!this.writeMessageWithSize([0,t.timestamp])&&!!this.writeMessageWithSize([118,t.tabId]))))}writeMessageWithSize(t){const s=this.encoder;if(!s.uint(t[0])||!s.skip(3))return!1;const i=s.getCurrentOffset();if(!s.encode(t))return!1;const e=s.getCurrentOffset(),n=e-i;return n>16777215?(console.warn(\"OpenReplay: max message size overflow.\"),!1):!(e>this.bufferSize)&&(this.writeSizeAt(n,i-3),s.checkpoint(),!0)}writeSizeAt(t,s){for(let s=0;s<3;s++)this.sizeBuffer[s]=t>>8*s;this.encoder.set(this.sizeBuffer,s)}}class u{constructor(t,s,i,e,n,h,r=!1,u){this.pageNo=t,this.timestamp=s,this.url=i,this.onBatch=e,this.tabId=n,this.onOfflineEnd=h,this.localDebug=r,this.onLocalSave=u,this.nextIndex=0,this.beaconSize=2e5,this.beaconSizeLimit=1e6,this.protocolVersion=1,this.visualSent=!1,this.signalSeen=!1,this.heldOther=[],this.playerBuilder=new a(this.beaconSize,this.playerVersion(),\"player\"),this.assetBuilder=new a(this.beaconSize,3,\"assets\"),this.devtoolsBuilder=new a(this.beaconSize,4,\"devtools\"),this.analyticsBuilder=new a(this.beaconSize,5,\"analytics\")}initActive(){return 2===this.protocolVersion&&!this.visualSent}playerVersion(){return 2===this.protocolVersion?2:1}currentCtx(){return{pageNo:this.pageNo,index:this.nextIndex,timestamp:this.timestamp,url:this.url,tabId:this.tabId}}setBeaconSizeLimit(t){this.beaconSizeLimit=t}setProtocolVersion(t){if(this.protocolVersion!==t){if(this.protocolVersion=t,2===t&&!this.signalSeen)return this.playerBuilder.reset(),this.assetBuilder.reset(),this.playerBuilder=new a(this.beaconSizeLimit,this.playerVersion(),\"player\"),void(this.assetBuilder=new a(this.beaconSizeLimit,3,\"assets\"));2===t&&(this.visualSent=!0),this.playerBuilder.reset(),this.playerBuilder=new a(this.beaconSize,this.playerVersion(),\"player\")}}writeMessage(t){if(-1===t[0])return this.finaliseBatch(),this.onOfflineEnd();if(12===t[0]&&\"orloaded\"===t[2])return void(this.initActive()?this.finalizeVisual():this.signalSeen=!0);0===t[0]&&(this.timestamp=t[1]),122===t[0]&&(this.url=t[1]);const s=this.routeMessage(t);this.pushTo(s,t)}routeMessage(t){if(2===this.protocolVersion){const n=t[0];if(s.has(n))return this.assetBuilder;if(i.has(n))return this.devtoolsBuilder;if(e.has(n))return this.analyticsBuilder}return this.playerBuilder}pushTo(t,s){const i=this.currentCtx();if(this.initActive())return void this.pushDuringInit(t,s,i);if(t.push(s,i))return void this.nextIndex++;if(t===this.assetBuilder&&this.flushBuilder(this.playerBuilder),this.flushBuilder(t),t.push(s,i))return void this.nextIndex++;const e=new a(this.beaconSizeLimit,t.version,t.dataType);if(!e.push(s,i))return void console.warn(\"OpenReplay: beacon size overflow. Skipping large message.\",s);this.nextIndex++;const n=e.flush();n&&(t===this.assetBuilder&&this.flushBuilder(this.playerBuilder),this.emitBatch(n,t.dataType,!1))}pushDuringInit(t,s,i){const e=t===this.playerBuilder||t===this.assetBuilder;if(t.push(s,i))return this.nextIndex++,void(e&&this.playerBuilder.size()+this.assetBuilder.size()>=this.beaconSizeLimit&&this.finalizeVisual());if(e)return this.finalizeVisual(),void this.pushTo(this.routeMessage(s),s);if(this.flushBuilderToHeld(t),t.push(s,i))return void this.nextIndex++;const n=new a(this.beaconSizeLimit,t.version,t.dataType);if(!n.push(s,i))return void console.warn(\"OpenReplay: beacon size overflow. Skipping large message.\",s);this.nextIndex++;const h=n.flush();h&&this.heldOther.push({batch:h,dataType:t.dataType})}flushBuilderToHeld(t){const s=t.flush();s&&this.heldOther.push({batch:s,dataType:t.dataType})}finalizeVisual(t=!1){const s=this.playerBuilder.flush(),i=this.assetBuilder.flush();if(this.visualSent=!0,this.playerBuilder=new a(this.beaconSize,this.playerVersion(),\"player\"),this.assetBuilder=new a(this.beaconSize,3,\"assets\"),s&&i){const e=new Uint8Array(s.length+i.length);e.set(s,0),e.set(i,s.length),this.emitBatch(e,\"visual\",t,s.length)}else s?this.emitBatch(s,\"player\",t):i&&this.emitBatch(i,\"assets\",t);for(const s of this.heldOther)this.emitBatch(s.batch,s.dataType,t);this.heldOther.length=0,this.flushBuilder(this.devtoolsBuilder,t),this.flushBuilder(this.analyticsBuilder,t)}flushBuilder(t,s=!1){const i=t.flush();return!!i&&(this.emitBatch(i,t.dataType,s),!0)}emitBatch(t,s,i,e){this.localDebug&&this.onLocalSave&&this.onLocalSave(`${s}-${Date.now()}`,t.slice()),this.onBatch(t,i,s,e)}finaliseBatch(t=!1){this.initActive()?this.finalizeVisual(t):(this.flushBuilder(this.playerBuilder,t),this.flushBuilder(this.assetBuilder,t),this.flushBuilder(this.devtoolsBuilder,t),this.flushBuilder(this.analyticsBuilder,t))}clean(){this.playerBuilder.reset(),this.assetBuilder.reset(),this.devtoolsBuilder.reset(),this.analyticsBuilder.reset(),this.heldOther.length=0,this.visualSent=!1,this.signalSeen=!1}}var o;!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\"}(o||(o={}));let l=null,c=null,d=o.NotActive;function p(t){c&&c.finaliseBatch(t)}function f(){return new Promise((t=>{d=o.Stopping,null!==y&&(clearInterval(y),y=null),c&&(c.clean(),c=null),l&&(l.clean(),setTimeout((()=>{l=null}),20)),setTimeout((()=>{d=o.NotActive,t(null)}),100)}))}function g(){[o.Stopped,o.Stopping].includes(d)||(postMessage(\"a_stop\"),f().then((()=>{postMessage(\"a_start\")})))}let m,y=null;self.onmessage=({data:s})=>{var i;if(\"stop\"===s)return p(),void f().then((()=>{d=o.Stopped}));if(\"forceFlushBatch\"!==s)if(\"closing\"!==s){if(!Array.isArray(s)){if(\"compressed\"===s.type){if(!l)return console.debug(\"OR WebWorker: sender not initialised. Compressed batch.\"),void g();s.batch&&l.sendCompressed(s.batch,s.dataType,s.split)}if(\"uncompressed\"===s.type){if(!l)return console.debug(\"OR WebWorker: sender not initialised. Uncompressed batch.\"),void g();s.batch&&l.sendUncompressed(s.batch,s.dataType,s.split)}return\"start\"===s.type?(d=o.Starting,l=new t(s.ingestPoint,(()=>{g()}),(t=>{!function(t){postMessage({type:\"failure\",reason:t}),f()}(t)}),s.connAttemptCount,s.connAttemptGap,((t,s,i)=>{postMessage({type:\"compress\",batch:t,dataType:s,split:i},[t.buffer])}),s.pageNo),c=new u(s.pageNo,s.timestamp,s.url,((t,s,i=\"player\",e)=>{l&&(s?l.sendUncompressed(t,i,e):l.push(t,i,e))}),s.tabId,(()=>postMessage({type:\"queue_empty\"})),null!==(i=s.localDebug)&&void 0!==i&&i,((t,s)=>{postMessage({type:\"local_save\",name:t,batch:s},[s.buffer])})),null===y&&(y=setInterval(p,3e4)),d=o.Active):\"auth\"===s.type?l?c?(l.authorise(s.token),s.beaconSizeLimit&&c.setBeaconSizeLimit(s.beaconSizeLimit),void(s.protocolVersion&&c.setProtocolVersion(s.protocolVersion))):(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(c){const t=c;s.forEach((s=>{55===s[0]&&(s[1]?m=setTimeout((()=>g()),18e5):clearTimeout(m)),t.writeMessage(s)}))}else postMessage(\"not_init\"),g()}else p(!0);else p()}}();\n";
4386
+ const workerBodyFn = "!function(){\"use strict\";function t(t,i,s,e){return new(s||(s=Promise))(function(n,h){function r(t){try{u(e.next(t))}catch(t){h(t)}}function a(t){try{u(e.throw(t))}catch(t){h(t)}}function u(t){var i;t.done?n(t.value):(i=t.value,i instanceof s?i:new s(function(t){t(i)})).then(r,a)}u((e=e.apply(t,i||[])).next())})}\"function\"==typeof SuppressedError&&SuppressedError;const i=\"undefined\"!=typeof CompressionStream;class s{constructor(t,i,s,e=10,n=250,h,r){this.onUnauthorised=i,this.onFailure=s,this.MAX_ATTEMPTS_COUNT=e,this.ATTEMPT_TIMEOUT=n,this.pageNo=h,this.attemptsCount=0,this.queue=[],this.inFlight=null,this.inFlightDispatched=!1,this.compressionEpoch=0,this.token=null,this.stopped=!1,this.lastSeq=0,this.compressionThreshold=24e3,this.inflightKeepaliveBytes=0,this.ingestURL=t+\"/v1/web/i\",\"number\"==typeof r&&(this.compressionThreshold=r)}getQueueStatus(){return 0===this.queue.length&&null===this.inFlight}setCompressionThreshold(t){this.compressionThreshold=t}authorise(t){this.token=t,this.pump()}push(t,i=\"player\",s,e=!1){this.stopped||(this.queue.push({seq:++this.lastSeq,batch:t,dataType:i,split:s,raw:e}),this.pump())}pump(){if(this.stopped||null!==this.inFlight||null===this.token||0===this.queue.length)return;const t=this.queue.shift();if(this.inFlight=t,this.inFlightDispatched=!1,t.raw||!i||t.batch.length<=this.compressionThreshold)return void this.dispatch(t,t.batch,!1);const s=++this.compressionEpoch;this.gzip(t.batch).then(i=>{this.compressionEpoch===s&&this.inFlight===t&&this.dispatch(t,i,!0)}).catch(()=>{this.compressionEpoch===s&&this.inFlight===t&&this.dispatch(t,t.batch,!1)})}gzip(i){return t(this,void 0,void 0,function*(){const t=new Blob([i]).stream().pipeThrough(new CompressionStream(\"gzip\"));return new Uint8Array(yield new Response(t).arrayBuffer())})}finish(){this.inFlight=null,this.inFlightDispatched=!1,this.pump()}retry(t,i,s,e){if(this.attemptsCount>=this.MAX_ATTEMPTS_COUNT)return void this.onFailure(`Failed to send batch after ${this.attemptsCount} attempts.`);this.attemptsCount++;const n=new Uint8Array(i);setTimeout(()=>this.dispatch(t,n,s,e),this.ATTEMPT_TIMEOUT*this.attemptsCount)}dispatch(t,i,s,e=\"\"){var n;if(0===i.length)return console.error(\"OpenReplay: refusing to send 0-byte batch.\",{seq:t.seq,dataType:t.dataType,isCompressed:s}),this.attemptsCount=0,void this.finish();this.inFlight===t&&(this.inFlightDispatched=!0);const h={Authorization:`Bearer ${this.token}`,DataType:t.dataType};if(s&&(h[\"Content-Encoding\"]=\"gzip\"),null===this.token)return void setTimeout(()=>this.dispatch(t,i,s,\"newToken\"),500);const r=i.length<65536&&this.inflightKeepaliveBytes+i.length<=65536;r&&(this.inflightKeepaliveBytes+=i.length);let a=!1;const u=()=>{r&&!a&&(a=!0,this.inflightKeepaliveBytes-=i.length)};let o=this.ingestURL;o+=`?batch=${null!==(n=this.pageNo)&&void 0!==n?n:0}`,o+=`_${t.seq}`,o+=`_${i.byteLength}`,o+=\"_\"+(r?\"kyes\":\"kno\"),e&&(o+=`_${e}`),void 0!==t.split&&(o+=`&split=${t.split}`),fetch(o,{body:i,method:\"POST\",headers:h,keepalive:r}).then(e=>{var n;if(u(),null===(n=e.body)||void 0===n||n.cancel().catch(()=>{}),401===e.status)return this.inFlight=null,this.inFlightDispatched=!1,void this.onUnauthorised();e.status>=400?this.retry(t,i,s,`network:${e.status}`):(this.attemptsCount=0,this.finish())}).catch(e=>{u(),console.warn(\"OpenReplay:\",e),this.retry(t,i,s,`reject:${e.message}`)})}flushAll(){if(null===this.token)return;const t=this.queue.splice(0),i=this.inFlight;null===i||this.inFlightDispatched||(this.compressionEpoch++,this.inFlight=null,t.unshift(i));for(const i of t)i.raw=!0,this.dispatch(i,i.batch,!1)}clean(){this.flushAll(),this.stopped=!0,setTimeout(()=>{this.token=null,this.queue.length=0,this.inFlight=null,this.inFlightDispatched=!1},10)}}const e=new Set([60,61,71,73]),n=new Set([21,22,40,41,44,45,46,47,48,79,83,84,85,87,89,116,120,121,123]),h=new Set([17,23,24,27,28,29,30,42,63,64,78,112,115,124]),r=\"function\"==typeof TextEncoder?new TextEncoder:{encode(t){const i=t.length,s=new Uint8Array(3*i);let e=-1;for(let n=0,h=0,r=0;r!==i;){if(n=t.charCodeAt(r),r+=1,n>=55296&&n<=56319){if(r===i){s[e+=1]=239,s[e+=1]=191,s[e+=1]=189;break}if(h=t.charCodeAt(r),!(h>=56320&&h<=57343)){s[e+=1]=239,s[e+=1]=191,s[e+=1]=189;continue}if(n=1024*(n-55296)+h-56320+65536,r+=1,n>65535){s[e+=1]=240|n>>>18,s[e+=1]=128|n>>>12&63,s[e+=1]=128|n>>>6&63,s[e+=1]=128|63&n;continue}}n<=127?s[e+=1]=0|n:n<=2047?(s[e+=1]=192|n>>>6,s[e+=1]=128|63&n):(s[e+=1]=224|n>>>12,s[e+=1]=128|n>>>6&63,s[e+=1]=128|63&n)}return s.subarray(0,e+1)}};class a{constructor(t){this.size=t,this.offset=0,this.checkpointOffset=0,this.data=new Uint8Array(t)}getCurrentOffset(){return this.offset}getCurrentCheckpoint(){return this.checkpointOffset}checkpoint(){this.checkpointOffset=this.offset}get isEmpty(){return 0===this.offset}skip(t){return this.offset+=t,this.offset<=this.size}set(t,i){this.data.set(t,i)}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=r.encode(t),s=i.byteLength;return!(!this.uint(s)||this.offset+s>this.size)&&(this.data.set(i,this.offset),this.offset+=s,!0)}reset(){this.offset=0,this.checkpointOffset=0}rewind(t,i){t>this.offset||i>this.checkpointOffset||(this.offset=t,this.checkpointOffset=i)}flush(){const t=this.data.slice(0,this.checkpointOffset);return this.reset(),t}}class u extends a{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 o{constructor(t,i,s){this.bufferSize=t,this.version=i,this.dataType=s,this.sizeBuffer=new Uint8Array(3),this.snap=null,this.hasNonTimestamp=!1,this.lastPushedTs=0,this.encoder=new u(t)}push(t,i){const s=this.encoder,e=null===this.snap,n=s.getCurrentOffset(),h=s.getCurrentCheckpoint();if(e){const t={pageNo:i.pageNo,firstIndex:i.index,timestamp:i.timestamp,url:i.url,tabId:i.tabId};if(!this.writeHeader(t))return s.rewind(n,h),!1;this.snap=t,this.lastPushedTs=i.timestamp}return 0===t[0]||i.timestamp===this.lastPushedTs||this.writeMessageWithSize([0,i.timestamp])?this.writeMessageWithSize(t)?(this.lastPushedTs=i.timestamp,0!==t[0]&&(this.hasNonTimestamp=!0),!0):(s.rewind(n,h),e&&(this.snap=null),!1):(s.rewind(n,h),!1)}hasContent(){return null!==this.snap&&this.hasNonTimestamp}size(){return null===this.snap?0:this.encoder.getCurrentOffset()}flush(){if(!this.hasContent())return this.reset(),null;const t=this.encoder.flush();return this.snap=null,this.hasNonTimestamp=!1,this.lastPushedTs=0,t}headerOnly(t){this.reset();const i=this.writeHeader({pageNo:t.pageNo,firstIndex:t.index,timestamp:t.timestamp,url:t.url,tabId:t.tabId})?this.encoder.flush():null;return this.reset(),null!==i&&i.length>0?i:null}reset(){this.encoder.reset(),this.snap=null,this.hasNonTimestamp=!1,this.lastPushedTs=0}writeHeader(t){const i=this.encoder,s=[81,this.version,t.pageNo,t.firstIndex,t.timestamp,t.url];return!!i.uint(s[0])&&(!!i.encode(s)&&(!(i.getCurrentOffset()>this.bufferSize)&&(i.checkpoint(),!!this.writeMessageWithSize([0,t.timestamp])&&!!this.writeMessageWithSize([118,t.tabId]))))}writeMessageWithSize(t){const i=this.encoder;if(!i.uint(t[0])||!i.skip(3))return!1;const s=i.getCurrentOffset();if(!i.encode(t))return!1;const e=i.getCurrentOffset(),n=e-s;return n>16777215?(console.warn(\"OpenReplay: max message size overflow.\"),!1):!(e>this.bufferSize)&&(this.writeSizeAt(n,s-3),i.checkpoint(),!0)}writeSizeAt(t,i){for(let i=0;i<3;i++)this.sizeBuffer[i]=t>>8*i;this.encoder.set(this.sizeBuffer,i)}}class l{constructor(t,i,s,e,n,h,r=!1,a){this.pageNo=t,this.timestamp=i,this.url=s,this.onBatch=e,this.tabId=n,this.onOfflineEnd=h,this.localDebug=r,this.onLocalSave=a,this.nextIndex=0,this.beaconSize=2e5,this.beaconSizeLimit=1e6,this.protocolVersion=1,this.visualSent=!1,this.signalSeen=!1,this.heldOther=[],this.playerBuilder=new o(this.beaconSize,this.playerVersion(),\"player\"),this.assetBuilder=new o(this.beaconSize,3,\"assets\"),this.devtoolsBuilder=new o(this.beaconSize,4,\"devtools\"),this.analyticsBuilder=new o(this.beaconSize,5,\"analytics\")}initActive(){return 2===this.protocolVersion&&!this.visualSent}playerVersion(){return 2===this.protocolVersion?2:1}currentCtx(){return{pageNo:this.pageNo,index:this.nextIndex,timestamp:this.timestamp,url:this.url,tabId:this.tabId}}setBeaconSizeLimit(t){this.beaconSizeLimit=t}setProtocolVersion(t){if(this.protocolVersion!==t){if(this.protocolVersion=t,2===t&&!this.signalSeen)return this.playerBuilder.reset(),this.assetBuilder.reset(),this.playerBuilder=new o(this.beaconSizeLimit,this.playerVersion(),\"player\"),void(this.assetBuilder=new o(this.beaconSizeLimit,3,\"assets\"));2===t&&(this.visualSent=!0),this.playerBuilder.reset(),this.playerBuilder=new o(this.beaconSize,this.playerVersion(),\"player\")}}writeMessage(t){if(-1===t[0])return this.finaliseBatch(),this.onOfflineEnd();if(12===t[0]&&\"orloaded\"===t[2])return void(this.initActive()?this.finalizeVisual():this.signalSeen=!0);0===t[0]&&(this.timestamp=t[1]),122===t[0]&&(this.url=t[1]);const i=this.routeMessage(t);this.pushTo(i,t)}routeMessage(t){if(2===this.protocolVersion){const i=t[0];if(e.has(i))return this.assetBuilder;if(n.has(i))return this.devtoolsBuilder;if(h.has(i))return this.analyticsBuilder}return this.playerBuilder}pushTo(t,i){const s=this.currentCtx();if(this.initActive())return void this.pushDuringInit(t,i,s);if(t.push(i,s))return void this.nextIndex++;if(t===this.assetBuilder&&this.flushBuilder(this.playerBuilder),this.flushBuilder(t),t.push(i,s))return void this.nextIndex++;const e=new o(this.beaconSizeLimit,t.version,t.dataType);if(!e.push(i,s))return void console.warn(\"OpenReplay: beacon size overflow. Skipping large message.\",i);this.nextIndex++;const n=e.flush();n&&(t===this.assetBuilder&&this.flushBuilder(this.playerBuilder),this.emitBatch(n,t.dataType,!1))}pushDuringInit(t,i,s){const e=t===this.playerBuilder||t===this.assetBuilder;if(t.push(i,s))return this.nextIndex++,void(e&&this.playerBuilder.size()+this.assetBuilder.size()>=this.beaconSizeLimit&&this.finalizeVisual());if(e)return this.finalizeVisual(),void this.pushTo(this.routeMessage(i),i);if(this.flushBuilderToHeld(t),t.push(i,s))return void this.nextIndex++;const n=new o(this.beaconSizeLimit,t.version,t.dataType);if(!n.push(i,s))return void console.warn(\"OpenReplay: beacon size overflow. Skipping large message.\",i);this.nextIndex++;const h=n.flush();h&&this.heldOther.push({batch:h,dataType:t.dataType})}flushBuilderToHeld(t){const i=t.flush();i&&this.heldOther.push({batch:i,dataType:t.dataType})}finalizeVisual(t=!1){const i=this.playerBuilder.flush(),s=this.assetBuilder.flush();this.visualSent=!0,this.playerBuilder=new o(this.beaconSize,this.playerVersion(),\"player\"),this.assetBuilder=new o(this.beaconSize,3,\"assets\");const e=i&&this.withHeader(i,\"player\",this.playerVersion()),n=s&&this.withHeader(s,\"assets\",3);if(e&&n){const i=new Uint8Array(e.length+n.length);i.set(e,0),i.set(n,e.length),this.emitBatch(i,\"visual\",t,e.length)}else e?this.emitBatch(e,\"player\",t):n&&this.emitBatch(n,\"assets\",t);for(const i of this.heldOther)this.emitBatch(i.batch,i.dataType,t);this.heldOther.length=0,this.flushBuilder(this.devtoolsBuilder,t),this.flushBuilder(this.analyticsBuilder,t)}flushBuilder(t,i=!1){const s=t.flush();return!!s&&(this.emitBatch(s,t.dataType,i),!0)}startsWithMeta(t){return t.length>0&&81===t[0]}repairHeader(t,i){const s=new o(16384,i,\"player\").headerOnly(this.currentCtx());if(!s)return null;const e=new Uint8Array(s.length+t.length);return e.set(s,0),e.set(t,s.length),e}withHeader(t,i,s){if(this.startsWithMeta(t))return t;const e=this.repairHeader(t,s);return console.warn(`OpenReplay: ${i} batch had no leading BatchMetadata (${t.length}B, head ${this.headHex(t)}) — `+(e?\"header rebuilt.\":\"could not rebuild the header, skipping.\")),e}versionOf(t){switch(t){case\"assets\":return 3;case\"devtools\":return 4;case\"analytics\":return 5;default:return this.playerVersion()}}emitBatch(t,i,s,e){const n=void 0===e?this.withHeader(t,i,this.versionOf(i)):t;if(!n)return;let h=e;if(void 0!==h&&!this.isBoundary(n,h)){const t=this.findBoundary(n);if(console.warn(`OpenReplay: ${i} batch split ${String(h)} is not a batch boundary (${n.length}B) — `+(t>0?`corrected to ${t}.`:\"no boundary found, skipping.\")),t<=0)return;h=t}this.localDebug&&this.verifyBody(n,i,h),this.localDebug&&this.onLocalSave&&this.onLocalSave(`${i}-${Date.now()}`,n.slice()),this.onBatch(n,s,i,h)}isBoundary(t,i){return i>0&&i<t.length&&81===t[i]}headHex(t){return Array.from(t.subarray(0,8),t=>t.toString(16).padStart(2,\"0\")).join(\" \")}scanBatch(t){let i=0,s=0;const e=()=>{let s=0,e=0;for(;i<t.length;){const n=t[i++];if(s+=(127&n)*Math.pow(2,e),!(128&n))return s;e+=7}return-1};for(;i<t.length;){const n=i,h=e();if(s++,h<0)return{boundary:-1,fault:`message ${s}: truncated type`};if(81===h){if(s>1)return{boundary:n,fault:null};for(let t=0;t<4;t++)if(e()<0)return{boundary:-1,fault:\"truncated BatchMetadata\"};const h=e();if(h<0||i+h>t.length)return{boundary:-1,fault:\"truncated BatchMetadata url\"};i+=h;continue}if(1===s)return{boundary:-1,fault:`leading message is type ${h}, not BatchMetadata`};if(i+3>t.length)return{boundary:-1,fault:`message ${s}: truncated size prefix`};const r=t[i]|t[i+1]<<8|t[i+2]<<16;if(i+=3,i+r>t.length)return{boundary:-1,fault:`message ${s}: size ${r} overruns the batch`};i+=r}return{boundary:-1,fault:null}}findBoundary(t){return this.scanBatch(t).boundary}verifyBody(t,i,s){if(void 0!==s)return this.verifyBody(t.subarray(0,s),`${i}:player`),void this.verifyBody(t.subarray(s),`${i}:assets`);const{boundary:e,fault:n}=this.scanBatch(t),h=null!=n?n:e>=0?`BatchMetadata at byte ${e} is not the first message`:null;null!==h&&console.warn(`OpenReplay: malformed ${i} batch — ${h} (${t.length}B, head ${this.headHex(t)}).`)}finaliseBatch(t=!1){this.initActive()?this.finalizeVisual(t):(this.flushBuilder(this.playerBuilder,t),this.flushBuilder(this.assetBuilder,t),this.flushBuilder(this.devtoolsBuilder,t),this.flushBuilder(this.analyticsBuilder,t))}clean(){this.playerBuilder.reset(),this.assetBuilder.reset(),this.devtoolsBuilder.reset(),this.analyticsBuilder.reset(),this.heldOther.length=0,this.visualSent=!1,this.signalSeen=!1}}var c;!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\"}(c||(c={}));let d=null,p=null,f=c.NotActive;function g(t){p&&p.finaliseBatch(t)}function y(){return new Promise(t=>{f=c.Stopping,null!==B&&(clearInterval(B),B=null),p&&(p.clean(),p=null),d&&(d.clean(),setTimeout(()=>{d=null},20)),setTimeout(()=>{f=c.NotActive,t(null)},100)})}function m(){[c.Stopped,c.Stopping].includes(f)||(postMessage(\"a_stop\"),y().then(()=>{postMessage(\"a_start\")}))}let b,B=null;self.onmessage=({data:t})=>{var i;if(\"stop\"===t)return g(),void y().then(()=>{f=c.Stopped});if(\"forceFlushBatch\"!==t){if(\"closing\"===t)return g(!0),void(null==d||d.flushAll());if(!Array.isArray(t))return\"start\"===t.type?(f=c.Starting,d=new s(t.ingestPoint,()=>{m()},t=>{!function(t){postMessage({type:\"failure\",reason:t}),y()}(t)},t.connAttemptCount,t.connAttemptGap,t.pageNo,t.compressionThreshold),p=new l(t.pageNo,t.timestamp,t.url,(t,i,s=\"player\",e)=>{d&&d.push(t,s,e,i)},t.tabId,()=>postMessage({type:\"queue_empty\"}),null!==(i=t.localDebug)&&void 0!==i&&i,(t,i)=>{postMessage({type:\"local_save\",name:t,batch:i},[i.buffer])}),null===B&&(B=setInterval(g,3e4)),f=c.Active):\"auth\"===t.type?d?p?(\"number\"==typeof t.compressionThreshold&&d.setCompressionThreshold(t.compressionThreshold),d.authorise(t.token),t.beaconSizeLimit&&p.setBeaconSizeLimit(t.beaconSizeLimit),void(t.protocolVersion&&p.setProtocolVersion(t.protocolVersion))):(console.debug(\"OR WebWorker: writer not initialised. Received auth.\"),void m()):(console.debug(\"OR WebWorker: sender not initialised. Received auth.\"),void m()):void 0;if(p){const i=p;t.forEach(t=>{55===t[0]&&(t[1]?b=setTimeout(()=>m(),18e5):clearTimeout(b)),i.writeMessage(t)})}else postMessage(\"not_init\"),m()}else g()}}();\n";
4303
4387
  const CANCELED = 'canceled';
4304
4388
  const bufferStorageKey = 'or_buffer_1';
4305
4389
  const UnsuccessfulStart = (reason) => ({ reason, success: false });
@@ -4364,9 +4448,8 @@ class App {
4364
4448
  this.stopCallbacks = [];
4365
4449
  this.commitCallbacks = [];
4366
4450
  this.activityState = ActivityState.NotActive;
4367
- this.version = '18.1.3'; // TODO: version compatability check inside each plugin.
4451
+ this.version = '18.1.5'; // TODO: version compatability check inside each plugin.
4368
4452
  this.socketMode = false;
4369
- this.compressionThreshold = 24 * 1000;
4370
4453
  this.bc = null;
4371
4454
  this.canvasRecorder = null;
4372
4455
  this.conditionsManager = null;
@@ -5362,35 +5445,6 @@ class App {
5362
5445
  this.debug.error('worker_failed', data.reason);
5363
5446
  this._debug('worker_failed', data.reason);
5364
5447
  }
5365
- else if (data.type === 'compress') {
5366
- const batch = data.batch;
5367
- const dataType = data.dataType;
5368
- // split is a decompressed-byte offset, so it survives gzip unchanged.
5369
- const split = data.split;
5370
- const batchSize = batch.byteLength;
5371
- const hasCompressionAPI = 'CompressionStream' in globalThis;
5372
- if (batchSize > this.compressionThreshold && hasCompressionAPI) {
5373
- const blob = new Blob([batch]);
5374
- const stream = blob.stream().pipeThrough(new CompressionStream('gzip'));
5375
- new Response(stream)
5376
- .arrayBuffer()
5377
- .then((compressedBuffer) => {
5378
- this.worker?.postMessage({
5379
- type: 'compressed',
5380
- batch: new Uint8Array(compressedBuffer),
5381
- dataType,
5382
- split,
5383
- });
5384
- })
5385
- .catch((err) => {
5386
- this.debug.error('Openreplay compression error:', err);
5387
- this.worker?.postMessage({ type: 'uncompressed', batch: batch, dataType, split });
5388
- });
5389
- }
5390
- else {
5391
- this.worker?.postMessage({ type: 'uncompressed', batch: batch, dataType, split });
5392
- }
5393
- }
5394
5448
  else if (data.type === 'local_save') {
5395
5449
  const blob = new Blob([data.batch], { type: 'application/octet-stream' });
5396
5450
  const url = URL.createObjectURL(blob);
@@ -5999,6 +6053,7 @@ class App {
5999
6053
  token,
6000
6054
  beaconSizeLimit,
6001
6055
  protocolVersion,
6056
+ compressionThreshold,
6002
6057
  });
6003
6058
  }
6004
6059
  if (!isNewSession && token === sessionToken) {
@@ -6009,7 +6064,6 @@ class App {
6009
6064
  // (Re)send Metadata for the case of a new session
6010
6065
  Object.entries(this.session.getInfo().metadata).forEach(([key, value]) => this.send(Metadata(key, value)));
6011
6066
  this.localStorage.setItem(this.options.local_uuid_key, userUUID);
6012
- this.compressionThreshold = compressionThreshold;
6013
6067
  const onStartInfo = { sessionToken: token, userUUID, sessionID };
6014
6068
  // TODO: start as early as possible (before receiving the token)
6015
6069
  /** after start */
@@ -6940,12 +6994,24 @@ function Input (app, opts) {
6940
6994
  }));
6941
6995
  }
6942
6996
 
6943
- const cssEscape = (typeof CSS !== 'undefined' && CSS.escape) || ((t) => t);
6944
6997
  const docClassCache = new WeakMap();
6945
- function _getSelector(target) {
6946
- const selector = getCSSPath(target);
6998
+ function _getSelector(target, customAttributes) {
6999
+ const selector = getCSSPath(target, customAttributes);
6947
7000
  return selector || '';
6948
7001
  }
7002
+ /**
7003
+ * short selector for elements we don't build a full css path for
7004
+ * (non-clickable targets, or a label fallback when clickmaps are off)
7005
+ * */
7006
+ function getCheapSelector(target, customAttributes) {
7007
+ const attributeSelector = getCustomAttributeSelector(target, customAttributes);
7008
+ if (attributeSelector)
7009
+ return attributeSelector;
7010
+ if (target.id)
7011
+ return `#${cssEscape(target.id)}`;
7012
+ const uniqueClass = getUniqueWordLikeClass(target);
7013
+ return uniqueClass ? `${target.tagName.toLowerCase()}.${cssEscape(uniqueClass)}` : '';
7014
+ }
6949
7015
  function isClickable(element) {
6950
7016
  const tag = element.tagName.toUpperCase();
6951
7017
  return (tag === 'BUTTON' ||
@@ -6959,14 +7025,79 @@ function isClickable(element) {
6959
7025
  //|| element.className.includes("btn")
6960
7026
  // MBTODO: intercept addEventListener
6961
7027
  }
7028
+ const CLICKABLE_ROLES = [
7029
+ 'button',
7030
+ 'link',
7031
+ 'tab',
7032
+ 'menuitem',
7033
+ 'menuitemcheckbox',
7034
+ 'menuitemradio',
7035
+ 'option',
7036
+ 'checkbox',
7037
+ 'radio',
7038
+ 'switch',
7039
+ 'combobox',
7040
+ ];
7041
+ const MAX_POINTER_CLIMB = 6;
7042
+ const pointerCursorCache = new WeakMap();
7043
+ function hasPointerCursor(element) {
7044
+ const cached = pointerCursorCache.get(element);
7045
+ if (cached !== undefined)
7046
+ return cached;
7047
+ let isPointer = false;
7048
+ try {
7049
+ const view = element.ownerDocument.defaultView;
7050
+ isPointer = !!view && view.getComputedStyle(element).cursor === 'pointer';
7051
+ }
7052
+ catch {
7053
+ isPointer = false;
7054
+ }
7055
+ pointerCursorCache.set(element, isPointer);
7056
+ return isPointer;
7057
+ }
7058
+ /**
7059
+ * framework handlers (react & co) are delegated to the root, so onclick is null
7060
+ * on the div that visually is a button; these signals catch it instead.
7061
+ * computed style forces a style recalc, so it is only used on the click path
7062
+ * */
7063
+ function looksClickable(element) {
7064
+ return (element.hasAttribute('onclick') ||
7065
+ element.tagName.toUpperCase() === 'SUMMARY' ||
7066
+ element.isContentEditable === true ||
7067
+ CLICKABLE_ROLES.includes(element.getAttribute('role') || '') ||
7068
+ element.tabIndex >= 0);
7069
+ }
7070
+ function isDeepClickable(element) {
7071
+ return isClickable(element) || looksClickable(element) || hasPointerCursor(element);
7072
+ }
7073
+ /**
7074
+ * cursor is inherited, so every child of a clickable div reports a pointer too;
7075
+ * climb to the outermost element that still owns the pointer
7076
+ * */
7077
+ function resolvePointerRoot(element) {
7078
+ let result = element;
7079
+ let parent = element.parentElement;
7080
+ for (let depth = 0; parent !== null && depth < MAX_POINTER_CLIMB; depth++) {
7081
+ if (parent === element.ownerDocument.documentElement || parent === element.ownerDocument.body) {
7082
+ return result;
7083
+ }
7084
+ if (isClickable(parent) || looksClickable(parent))
7085
+ return parent;
7086
+ if (!hasPointerCursor(parent))
7087
+ return result;
7088
+ result = parent;
7089
+ parent = parent.parentElement;
7090
+ }
7091
+ return result;
7092
+ }
6962
7093
  //TODO: fix (typescript is not sure about target variable after assignation of svg)
6963
- function getTarget(target, document) {
7094
+ function getTarget(target, document, deep = false) {
6964
7095
  if (target instanceof Element) {
6965
- return _getTarget(target, document);
7096
+ return _getTarget(target, document, deep);
6966
7097
  }
6967
7098
  return null;
6968
7099
  }
6969
- function _getTarget(target, document) {
7100
+ function _getTarget(target, document, deep = false) {
6970
7101
  let element = target;
6971
7102
  while (element !== null && element !== document.documentElement) {
6972
7103
  if (hasOpenreplayAttribute(element, 'masked')) {
@@ -6993,12 +7124,22 @@ function _getTarget(target, document) {
6993
7124
  if (isClickable(element) || getLabelAttribute(element) !== null) {
6994
7125
  return element;
6995
7126
  }
7127
+ if (deep) {
7128
+ if (looksClickable(element)) {
7129
+ return element;
7130
+ }
7131
+ if (hasPointerCursor(element)) {
7132
+ return resolvePointerRoot(element);
7133
+ }
7134
+ }
6996
7135
  element = element.parentElement;
6997
7136
  }
6998
7137
  return target === document.documentElement ? null : target;
6999
7138
  }
7000
7139
  function Mouse (app, options) {
7001
7140
  const { disableClickmaps = false, customAttributes } = options || {};
7141
+ /** innerText exists on html elements only, svg/jsdom nodes would break the sanitizer */
7142
+ const getSecureInnerText = (el) => typeof el.innerText === 'string' ? app.sanitizer.getInnerTextSecure(el) : '';
7002
7143
  function getTargetLabel(target) {
7003
7144
  const dl = getLabelAttribute(target);
7004
7145
  if (dl !== null) {
@@ -7010,19 +7151,7 @@ function Mouse (app, options) {
7010
7151
  const customAttributeLabel = getCustomAttributeLabel(target, customAttributes);
7011
7152
  if (customAttributeLabel)
7012
7153
  return customAttributeLabel;
7013
- if (target.id)
7014
- return `#${target.id}`;
7015
- const classLabel = getClassSelector(target);
7016
- if (classLabel)
7017
- return classLabel;
7018
- if (isClickable(target)) {
7019
- let label = '';
7020
- if (target instanceof HTMLElement) {
7021
- label = app.sanitizer.getInnerTextSecure(target);
7022
- }
7023
- return normSpaces(label).slice(0, 100);
7024
- }
7025
- return '';
7154
+ return getTextualLabel(target, getSecureInnerText);
7026
7155
  }
7027
7156
  let mousePositionX = -1;
7028
7157
  let mousePositionY = -1;
@@ -7064,6 +7193,9 @@ function Mouse (app, options) {
7064
7193
  clearInterval(checkIntervalId);
7065
7194
  }
7066
7195
  });
7196
+ /** hover is resolved without the computed style check, so it can land on a relative */
7197
+ const isHesitationTarget = (target) => mouseTarget === target ||
7198
+ (mouseTarget !== null && (target.contains(mouseTarget) || mouseTarget.contains(target)));
7067
7199
  const sendMouseMove = () => {
7068
7200
  if (mousePositionChanged) {
7069
7201
  app.send(MouseMove(mousePositionX, mousePositionY));
@@ -7078,7 +7210,12 @@ function Mouse (app, options) {
7078
7210
  if (tagMatch) {
7079
7211
  return (selectorMap[id] = tagMatch.selector);
7080
7212
  }
7081
- return (selectorMap[id] = _getSelector(target));
7213
+ if (!isDeepClickable(target)) {
7214
+ const cheapSelector = getCheapSelector(target, customAttributes);
7215
+ if (cheapSelector)
7216
+ return (selectorMap[id] = cheapSelector);
7217
+ }
7218
+ return (selectorMap[id] = _getSelector(target, customAttributes));
7082
7219
  }
7083
7220
  const attachListener = topframe
7084
7221
  ? app.attachEventListener.bind(app) // attached/removed on start/stop
@@ -7103,7 +7240,7 @@ function Mouse (app, options) {
7103
7240
  }
7104
7241
  }, false);
7105
7242
  attachListener(document, 'click', (e) => {
7106
- const target = getTarget(e.target, document);
7243
+ const target = getTarget(e.target, document, true);
7107
7244
  if ((!e.clientX && !e.clientY) || target === null) {
7108
7245
  return;
7109
7246
  }
@@ -7116,8 +7253,10 @@ function Mouse (app, options) {
7116
7253
  const normalizedX = roundNumber(clickX / contentWidth);
7117
7254
  const normalizedY = roundNumber(clickY / contentHeight);
7118
7255
  sendMouseMove();
7119
- const label = getTargetLabel(target);
7120
- app.send(MouseClick(id, mouseTarget === target ? Math.round(performance.now() - mouseTargetTime) : 0, app.sanitizer.privateMode ? label.replaceAll(/./g, '*') : label, isClickable(target) && !disableClickmaps ? getSelector(id, target) : '', normalizedX, normalizedY), true);
7256
+ const selector = disableClickmaps ? '' : getSelector(id, target);
7257
+ // backend drops clicks without a label, so a selector is the last resort
7258
+ const label = getTargetLabel(target) || selector || getCheapSelector(target, customAttributes);
7259
+ app.send(MouseClick(id, isHesitationTarget(target) ? Math.round(performance.now() - mouseTargetTime) : 0, app.sanitizer.privateMode ? label.replaceAll(/./g, '*') : label, selector, normalizedX, normalizedY), true);
7121
7260
  }
7122
7261
  mouseTarget = null;
7123
7262
  });
@@ -7164,18 +7303,27 @@ function wordLike(name) {
7164
7303
  }
7165
7304
  return false;
7166
7305
  }
7167
- function getCSSPath(el) {
7306
+ function getCSSPath(el, customAttributes) {
7168
7307
  if (!el || el.nodeType !== 1)
7169
7308
  return false;
7309
+ // customer configured attributes are the most stable thing we can get
7310
+ const customAttr = getCustomAttributeSelector(el, customAttributes);
7311
+ if (customAttr)
7312
+ return customAttr;
7170
7313
  if (el.id)
7171
7314
  return `#${cssEscape(el.id)}`;
7172
7315
  // if has data attributes - use them as they are more likely to be stable and unique
7173
7316
  const dataAttr = Array.from(el.attributes).find(attr => attr.name.startsWith('data-'));
7174
7317
  if (dataAttr) {
7175
- return `[${dataAttr.name}="${cssEscape(dataAttr.value)}"]`;
7318
+ return `[${dataAttr.name}="${cssAttrValue(dataAttr.value)}"]`;
7176
7319
  }
7177
7320
  const parts = [];
7178
7321
  while (el && el.nodeType === 1 && el !== el.ownerDocument) {
7322
+ const ancestorAttr = getCustomAttributeSelector(el, customAttributes);
7323
+ if (ancestorAttr) {
7324
+ parts.unshift(ancestorAttr);
7325
+ break;
7326
+ }
7179
7327
  if (el.id) {
7180
7328
  parts.unshift(`#${cssEscape(el.id)}`);
7181
7329
  break;
@@ -7209,6 +7357,15 @@ function getCSSPath(el) {
7209
7357
  }
7210
7358
  return parts.join(' > ');
7211
7359
  }
7360
+ function getUniqueWordLikeClass(el) {
7361
+ if (!el.classList?.length)
7362
+ return null;
7363
+ for (const cls of Array.from(el.classList)) {
7364
+ if (wordLike(cls) && isDocUniqueClass(cls, el.ownerDocument))
7365
+ return cls;
7366
+ }
7367
+ return null;
7368
+ }
7212
7369
  function getUniqueSiblingClass(el) {
7213
7370
  if (!el.classList?.length || !el.parentNode)
7214
7371
  return null;
@@ -9791,7 +9948,7 @@ class ConstantProperties {
9791
9948
  user_id: this.user_id,
9792
9949
  distinct_id: this.deviceId,
9793
9950
  sdk_edition: 'web',
9794
- sdk_version: '18.1.3',
9951
+ sdk_version: '18.1.5',
9795
9952
  timezone: getUTCOffsetString(),
9796
9953
  search_engine: this.searchEngine,
9797
9954
  };
@@ -10532,7 +10689,7 @@ class API {
10532
10689
  this.signalStartIssue = (reason, missingApi) => {
10533
10690
  const doNotTrack = this.checkDoNotTrack();
10534
10691
  console.log("Tracker couldn't start due to:", JSON.stringify({
10535
- trackerVersion: '18.1.3',
10692
+ trackerVersion: '18.1.5',
10536
10693
  projectKey: this.options.projectKey,
10537
10694
  doNotTrack,
10538
10695
  reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,