@openreplay/tracker 3.5.13-beta.0 → 3.5.15

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.
@@ -10,7 +10,8 @@ declare type TagTypeMap = {
10
10
  SELECT: HTMLSelectElement;
11
11
  LABEL: HTMLLabelElement;
12
12
  IFRAME: HTMLIFrameElement;
13
- STYLE: HTMLStyleElement | SVGStyleElement;
13
+ STYLE: HTMLStyleElement;
14
+ style: SVGStyleElement;
14
15
  LINK: HTMLLinkElement;
15
16
  };
16
17
  export declare function hasTag<T extends keyof TagTypeMap>(el: Node, tagName: T): el is TagTypeMap[typeof tagName];
package/cjs/app/guards.js CHANGED
@@ -19,6 +19,6 @@ function isRootNode(node) {
19
19
  }
20
20
  exports.isRootNode = isRootNode;
21
21
  function hasTag(el, tagName) {
22
- return el.nodeName.toUpperCase() === tagName;
22
+ return el.nodeName === tagName;
23
23
  }
24
24
  exports.hasTag = hasTag;
@@ -103,6 +103,6 @@ export default class App {
103
103
  resetNextPageSession(flag: boolean): void;
104
104
  private _start;
105
105
  start(options?: StartOptions): Promise<StartPromiseReturn>;
106
- stop(): void;
106
+ stop(calledFromAPI?: boolean): void;
107
107
  }
108
108
  export {};
package/cjs/app/index.js CHANGED
@@ -32,7 +32,7 @@ class App {
32
32
  this.stopCallbacks = [];
33
33
  this.commitCallbacks = [];
34
34
  this.activityState = ActivityState.NotActive;
35
- this.version = '3.5.13-beta.0'; // TODO: version compatability check inside each plugin.
35
+ this.version = '3.5.15'; // TODO: version compatability check inside each plugin.
36
36
  this.projectKey = projectKey;
37
37
  this.options = Object.assign({
38
38
  revID: '',
@@ -57,14 +57,22 @@ class App {
57
57
  this.ticker.attach(() => this.commit());
58
58
  this.debug = new logger_js_1.default(this.options.__debug__);
59
59
  this.notify = new logger_js_1.default(this.options.verbose ? logger_js_1.LogLevel.Warnings : logger_js_1.LogLevel.Silent);
60
- this.session = new session_js_1.default(this);
60
+ this.session = new session_js_1.default();
61
+ this.session.attachUpdateCallback(({ userID, metadata }) => {
62
+ if (userID != null) { // TODO: nullable userID
63
+ this.send(new messages_js_1.UserID(userID));
64
+ }
65
+ if (metadata != null) {
66
+ Object.entries(metadata).forEach(([key, value]) => this.send(new messages_js_1.Metadata(key, value)));
67
+ }
68
+ });
61
69
  this.localStorage = this.options.localStorage;
62
70
  this.sessionStorage = this.options.sessionStorage;
63
71
  if (sessionToken != null) {
64
72
  this.sessionStorage.setItem(this.options.session_token_key, sessionToken);
65
73
  }
66
74
  try {
67
- this.worker = new Worker(URL.createObjectURL(new Blob([`"use strict";function t(t){function i(...i){return new t(...i)}return i.prototype=t.prototype,i}const i=new Map;const s=t(class{constructor(t,i,s){this.pageNo=t,this.firstIndex=i,this.timestamp=s,this._id=80}encode(t){return t.uint(80)&&t.uint(this.pageNo)&&t.uint(this.firstIndex)&&t.int(this.timestamp)}});i.set(80,s);const e=t(class{constructor(t){this.timestamp=t,this._id=0}encode(t){return t.uint(0)&&t.uint(this.timestamp)}});i.set(0,e);const n=t(class{constructor(t,i,s){this.url=t,this.referrer=i,this.navigationStart=s,this._id=4}encode(t){return t.uint(4)&&t.string(this.url)&&t.string(this.referrer)&&t.uint(this.navigationStart)}});i.set(4,n);const r=t(class{constructor(t,i){this.width=t,this.height=i,this._id=5}encode(t){return t.uint(5)&&t.uint(this.width)&&t.uint(this.height)}});i.set(5,r);const h=t(class{constructor(t,i){this.x=t,this.y=i,this._id=6}encode(t){return t.uint(6)&&t.int(this.x)&&t.int(this.y)}});i.set(6,h);const o=t(class{constructor(){this._id=7}encode(t){return t.uint(7)}});i.set(7,o);const c=t(class{constructor(t,i,s,e,n){this.id=t,this.parentID=i,this.index=s,this.tag=e,this.svg=n,this._id=8}encode(t){return t.uint(8)&&t.uint(this.id)&&t.uint(this.parentID)&&t.uint(this.index)&&t.string(this.tag)&&t.boolean(this.svg)}});i.set(8,c);const a=t(class{constructor(t,i,s){this.id=t,this.parentID=i,this.index=s,this._id=9}encode(t){return t.uint(9)&&t.uint(this.id)&&t.uint(this.parentID)&&t.uint(this.index)}});i.set(9,a);const u=t(class{constructor(t,i,s){this.id=t,this.parentID=i,this.index=s,this._id=10}encode(t){return t.uint(10)&&t.uint(this.id)&&t.uint(this.parentID)&&t.uint(this.index)}});i.set(10,u);const d=t(class{constructor(t){this.id=t,this._id=11}encode(t){return t.uint(11)&&t.uint(this.id)}});i.set(11,d);const l=t(class{constructor(t,i,s){this.id=t,this.name=i,this.value=s,this._id=12}encode(t){return t.uint(12)&&t.uint(this.id)&&t.string(this.name)&&t.string(this.value)}});i.set(12,l);const p=t(class{constructor(t,i){this.id=t,this.name=i,this._id=13}encode(t){return t.uint(13)&&t.uint(this.id)&&t.string(this.name)}});i.set(13,p);const m=t(class{constructor(t,i){this.id=t,this.data=i,this._id=14}encode(t){return t.uint(14)&&t.uint(this.id)&&t.string(this.data)}});i.set(14,m);const g=t(class{constructor(t,i,s){this.id=t,this.x=i,this.y=s,this._id=16}encode(t){return t.uint(16)&&t.uint(this.id)&&t.int(this.x)&&t.int(this.y)}});i.set(16,g);const f=t(class{constructor(t,i){this.id=t,this.label=i,this._id=17}encode(t){return t.uint(17)&&t.uint(this.id)&&t.string(this.label)}});i.set(17,f);const y=t(class{constructor(t,i,s){this.id=t,this.value=i,this.mask=s,this._id=18}encode(t){return t.uint(18)&&t.uint(this.id)&&t.string(this.value)&&t.int(this.mask)}});i.set(18,y);const _=t(class{constructor(t,i){this.id=t,this.checked=i,this._id=19}encode(t){return t.uint(19)&&t.uint(this.id)&&t.boolean(this.checked)}});i.set(19,_);const v=t(class{constructor(t,i){this.x=t,this.y=i,this._id=20}encode(t){return t.uint(20)&&t.uint(this.x)&&t.uint(this.y)}});i.set(20,v);const b=t(class{constructor(t,i){this.level=t,this.value=i,this._id=22}encode(t){return t.uint(22)&&t.string(this.level)&&t.string(this.value)}});i.set(22,b);const S=t(class{constructor(t,i,s,e,n,r,h,o,c){this.requestStart=t,this.responseStart=i,this.responseEnd=s,this.domContentLoadedEventStart=e,this.domContentLoadedEventEnd=n,this.loadEventStart=r,this.loadEventEnd=h,this.firstPaint=o,this.firstContentfulPaint=c,this._id=23}encode(t){return t.uint(23)&&t.uint(this.requestStart)&&t.uint(this.responseStart)&&t.uint(this.responseEnd)&&t.uint(this.domContentLoadedEventStart)&&t.uint(this.domContentLoadedEventEnd)&&t.uint(this.loadEventStart)&&t.uint(this.loadEventEnd)&&t.uint(this.firstPaint)&&t.uint(this.firstContentfulPaint)}});i.set(23,S);const w=t(class{constructor(t,i,s){this.speedIndex=t,this.visuallyComplete=i,this.timeToInteractive=s,this._id=24}encode(t){return t.uint(24)&&t.uint(this.speedIndex)&&t.uint(this.visuallyComplete)&&t.uint(this.timeToInteractive)}});i.set(24,w);const E=t(class{constructor(t,i,s){this.name=t,this.message=i,this.payload=s,this._id=25}encode(t){return t.uint(25)&&t.string(this.name)&&t.string(this.message)&&t.string(this.payload)}});i.set(25,E);const x=t(class{constructor(t,i){this.name=t,this.payload=i,this._id=27}encode(t){return t.uint(27)&&t.string(this.name)&&t.string(this.payload)}});i.set(27,x);const T=t(class{constructor(t){this.id=t,this._id=28}encode(t){return t.uint(28)&&t.string(this.id)}});i.set(28,T);const z=t(class{constructor(t){this.id=t,this._id=29}encode(t){return t.uint(29)&&t.string(this.id)}});i.set(29,z);const k=t(class{constructor(t,i){this.key=t,this.value=i,this._id=30}encode(t){return t.uint(30)&&t.string(this.key)&&t.string(this.value)}});i.set(30,k);const I=t(class{constructor(t,i,s){this.id=t,this.rule=i,this.index=s,this._id=37}encode(t){return t.uint(37)&&t.uint(this.id)&&t.string(this.rule)&&t.uint(this.index)}});i.set(37,I);const M=t(class{constructor(t,i){this.id=t,this.index=i,this._id=38}encode(t){return t.uint(38)&&t.uint(this.id)&&t.uint(this.index)}});i.set(38,M);const B=t(class{constructor(t,i,s,e,n,r,h){this.method=t,this.url=i,this.request=s,this.response=e,this.status=n,this.timestamp=r,this.duration=h,this._id=39}encode(t){return t.uint(39)&&t.string(this.method)&&t.string(this.url)&&t.string(this.request)&&t.string(this.response)&&t.uint(this.status)&&t.uint(this.timestamp)&&t.uint(this.duration)}});i.set(39,B);const L=t(class{constructor(t,i,s,e){this.name=t,this.duration=i,this.args=s,this.result=e,this._id=40}encode(t){return t.uint(40)&&t.string(this.name)&&t.uint(this.duration)&&t.string(this.args)&&t.string(this.result)}});i.set(40,L);const C=t(class{constructor(t,i){this.key=t,this.value=i,this._id=41}encode(t){return t.uint(41)&&t.string(this.key)&&t.string(this.value)}});i.set(41,C);const A=t(class{constructor(t){this.type=t,this._id=42}encode(t){return t.uint(42)&&t.string(this.type)}});i.set(42,A);const U=t(class{constructor(t,i,s){this.action=t,this.state=i,this.duration=s,this._id=44}encode(t){return t.uint(44)&&t.string(this.action)&&t.string(this.state)&&t.uint(this.duration)}});i.set(44,U);const N=t(class{constructor(t,i){this.mutation=t,this.state=i,this._id=45}encode(t){return t.uint(45)&&t.string(this.mutation)&&t.string(this.state)}});i.set(45,N);const R=t(class{constructor(t,i){this.type=t,this.payload=i,this._id=46}encode(t){return t.uint(46)&&t.string(this.type)&&t.string(this.payload)}});i.set(46,R);const O=t(class{constructor(t,i,s){this.action=t,this.state=i,this.duration=s,this._id=47}encode(t){return t.uint(47)&&t.string(this.action)&&t.string(this.state)&&t.uint(this.duration)}});i.set(47,O);const P=t(class{constructor(t,i,s,e){this.operationKind=t,this.operationName=i,this.variables=s,this.response=e,this._id=48}encode(t){return t.uint(48)&&t.string(this.operationKind)&&t.string(this.operationName)&&t.string(this.variables)&&t.string(this.response)}});i.set(48,P);const q=t(class{constructor(t,i,s,e){this.frames=t,this.ticks=i,this.totalJSHeapSize=s,this.usedJSHeapSize=e,this._id=49}encode(t){return t.uint(49)&&t.int(this.frames)&&t.int(this.ticks)&&t.uint(this.totalJSHeapSize)&&t.uint(this.usedJSHeapSize)}});i.set(49,q);const D=t(class{constructor(t,i,s,e,n,r,h,o){this.timestamp=t,this.duration=i,this.ttfb=s,this.headerSize=e,this.encodedBodySize=n,this.decodedBodySize=r,this.url=h,this.initiator=o,this._id=53}encode(t){return t.uint(53)&&t.uint(this.timestamp)&&t.uint(this.duration)&&t.uint(this.ttfb)&&t.uint(this.headerSize)&&t.uint(this.encodedBodySize)&&t.uint(this.decodedBodySize)&&t.string(this.url)&&t.string(this.initiator)}});i.set(53,D);const W=t(class{constructor(t,i){this.downlink=t,this.type=i,this._id=54}encode(t){return t.uint(54)&&t.uint(this.downlink)&&t.string(this.type)}});i.set(54,W);const H=t(class{constructor(t){this.hidden=t,this._id=55}encode(t){return t.uint(55)&&t.boolean(this.hidden)}});i.set(55,H);const J=t(class{constructor(t,i,s,e,n,r,h){this.timestamp=t,this.duration=i,this.context=s,this.containerType=e,this.containerSrc=n,this.containerId=r,this.containerName=h,this._id=59}encode(t){return t.uint(59)&&t.uint(this.timestamp)&&t.uint(this.duration)&&t.uint(this.context)&&t.uint(this.containerType)&&t.string(this.containerSrc)&&t.string(this.containerId)&&t.string(this.containerName)}});i.set(59,J);const F=t(class{constructor(t,i,s,e){this.id=t,this.name=i,this.value=s,this.baseURL=e,this._id=60}encode(t){return t.uint(60)&&t.uint(this.id)&&t.string(this.name)&&t.string(this.value)&&t.string(this.baseURL)}});i.set(60,F);const X=t(class{constructor(t,i,s){this.id=t,this.data=i,this.baseURL=s,this._id=61}encode(t){return t.uint(61)&&t.uint(this.id)&&t.string(this.data)&&t.string(this.baseURL)}});i.set(61,X);const G=t(class{constructor(t,i){this.type=t,this.value=i,this._id=63}encode(t){return t.uint(63)&&t.string(this.type)&&t.string(this.value)}});i.set(63,G);const K=t(class{constructor(t,i){this.name=t,this.payload=i,this._id=64}encode(t){return t.uint(64)&&t.string(this.name)&&t.string(this.payload)}});i.set(64,K);const j=t(class{constructor(){this._id=65}encode(t){return t.uint(65)}});i.set(65,j);const Q=t(class{constructor(t,i,s,e){this.id=t,this.rule=i,this.index=s,this.baseURL=e,this._id=67}encode(t){return t.uint(67)&&t.uint(this.id)&&t.string(this.rule)&&t.uint(this.index)&&t.string(this.baseURL)}});i.set(67,Q);const V=t(class{constructor(t,i,s,e){this.id=t,this.hesitationTime=i,this.label=s,this.selector=e,this._id=69}encode(t){return t.uint(69)&&t.uint(this.id)&&t.uint(this.hesitationTime)&&t.string(this.label)&&t.string(this.selector)}});i.set(69,V);const Y=t(class{constructor(t,i){this.frameID=t,this.id=i,this._id=70}encode(t){return t.uint(70)&&t.uint(this.frameID)&&t.uint(this.id)}});i.set(70,Y);class Z{constructor(t,i,s,e=10,n=1e3){this.onUnauthorised=i,this.onFailure=s,this.MAX_ATTEMPTS_COUNT=e,this.ATTEMPT_TIMEOUT=n,this.attemptsCount=0,this.busy=!1,this.queue=[],this.token=null,this.ingestURL=t+"/v1/web/i"}authorise(t){this.token=t}push(t){this.busy||!this.token?this.queue.push(t):this.sendBatch(t)}retry(t){this.attemptsCount>=this.MAX_ATTEMPTS_COUNT?this.onFailure():(this.attemptsCount++,setTimeout(()=>this.sendBatch(t),this.ATTEMPT_TIMEOUT*this.attemptsCount))}sendBatch(t){this.busy=!0,fetch(this.ingestURL,{body:t,method:"POST",headers:{Authorization:"Bearer "+this.token},keepalive:t.length<65536}).then(i=>{if(401===i.status)return this.busy=!1,void this.onUnauthorised();if(i.status>=400)return void this.retry(t);this.attemptsCount=0;const s=this.queue.shift();s?this.sendBatch(s):this.busy=!1}).catch(i=>{console.warn("OpenReplay:",i),this.retry(t)})}clean(){this.queue.length=0}}const tt="function"==typeof TextEncoder?new TextEncoder:{encode(t){const i=t.length,s=new Uint8Array(3*i);let e=-1;for(var n=0,r=0,h=0;h!==i;){if(n=t.charCodeAt(h),h+=1,n>=55296&&n<=56319){if(h===i){s[e+=1]=239,s[e+=1]=191,s[e+=1]=189;break}if(!((r=t.charCodeAt(h))>=56320&&r<=57343)){s[e+=1]=239,s[e+=1]=191,s[e+=1]=189;continue}if(h+=1,(n=1024*(n-55296)+r-56320+65536)>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 it{constructor(t){this.size=t,this.offset=0,this.checkpointOffset=0,this.data=new Uint8Array(t)}checkpoint(){this.checkpointOffset=this.offset}isEmpty(){return 0===this.offset}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=tt.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}flush(){const t=this.data.slice(0,this.checkpointOffset);return this.reset(),t}}class st{constructor(t,i,s){this.pageNo=t,this.timestamp=i,this.onBatch=s,this.nextIndex=0,this.beaconSize=2e5,this.writer=new it(this.beaconSize),this.isEmpty=!0,this.beaconSizeLimit=1e6,this.prepareBatchMeta()}prepareBatchMeta(){return new s(this.pageNo,this.nextIndex,this.timestamp).encode(this.writer)}setBeaconSizeLimit(t){this.beaconSizeLimit=t}writeMessage(t){if(t instanceof e&&(this.timestamp=t.timestamp),!t.encode(this.writer))for(this.isEmpty||(this.onBatch(this.writer.flush()),this.prepareBatchMeta());!t.encode(this.writer);){if(this.beaconSize===this.beaconSizeLimit)return console.warn("OpenReplay: beacon size overflow. Skipping large message."),this.writer.reset(),this.prepareBatchMeta(),void(this.isEmpty=!0);this.beaconSize=Math.min(2*this.beaconSize,this.beaconSizeLimit),this.writer=new it(this.beaconSize),this.prepareBatchMeta()}this.writer.checkpoint(),this.nextIndex++,this.isEmpty=!1}finaliseBatch(){this.isEmpty||(this.onBatch(this.writer.flush()),this.prepareBatchMeta(),this.isEmpty=!0)}clean(){this.writer.reset()}}let et=null,nt=null;function rt(){nt&&nt.finaliseBatch()}function ht(){null!==ct&&(clearInterval(ct),ct=null),nt&&(nt.clean(),nt=null)}let ot,ct=null;self.onmessage=({data:t})=>{if(null!=t){if("stop"===t)return rt(),void ht();if(Array.isArray(t)){if(!nt)throw new Error("WebWorker: writer not initialised.");const s=nt;t.forEach(t=>{const e=new(i.get(t._id));Object.assign(e,t),e instanceof H&&(e.hidden?ot=setTimeout(()=>self.postMessage("restart"),18e5):clearTimeout(ot)),s.writeMessage(e)})}else{if("start"===t.type)return et=new Z(t.ingestPoint,()=>{self.postMessage("restart")},()=>{et&&(et.clean(),et=null),ht(),self.postMessage("failed")},t.connAttemptCount,t.connAttemptGap),nt=new st(t.pageNo,t.timestamp,t=>et&&et.push(t)),void(null===ct&&(ct=setInterval(rt,1e4)));if("auth"===t.type){if(!et)throw new Error("WebWorker: sender not initialised. Recieved auth.");if(!nt)throw new Error("WebWorker: writer not initialised. Recieved auth.");return et.authorise(t.token),void(t.beaconSizeLimit&&nt.setBeaconSizeLimit(t.beaconSizeLimit))}}}else rt()};
75
+ this.worker = new Worker(URL.createObjectURL(new Blob([`"use strict";function t(t){function i(...i){return new t(...i)}return i.prototype=t.prototype,i}const i=new Map;const s=t(class{constructor(t,i,s){this.pageNo=t,this.firstIndex=i,this.timestamp=s,this._id=80}encode(t){return t.uint(80)&&t.uint(this.pageNo)&&t.uint(this.firstIndex)&&t.int(this.timestamp)}});i.set(80,s);const e=t(class{constructor(t){this.timestamp=t,this._id=0}encode(t){return t.uint(0)&&t.uint(this.timestamp)}});i.set(0,e);const n=t(class{constructor(t,i,s){this.url=t,this.referrer=i,this.navigationStart=s,this._id=4}encode(t){return t.uint(4)&&t.string(this.url)&&t.string(this.referrer)&&t.uint(this.navigationStart)}});i.set(4,n);const r=t(class{constructor(t,i){this.width=t,this.height=i,this._id=5}encode(t){return t.uint(5)&&t.uint(this.width)&&t.uint(this.height)}});i.set(5,r);const h=t(class{constructor(t,i){this.x=t,this.y=i,this._id=6}encode(t){return t.uint(6)&&t.int(this.x)&&t.int(this.y)}});i.set(6,h);const o=t(class{constructor(){this._id=7}encode(t){return t.uint(7)}});i.set(7,o);const c=t(class{constructor(t,i,s,e,n){this.id=t,this.parentID=i,this.index=s,this.tag=e,this.svg=n,this._id=8}encode(t){return t.uint(8)&&t.uint(this.id)&&t.uint(this.parentID)&&t.uint(this.index)&&t.string(this.tag)&&t.boolean(this.svg)}});i.set(8,c);const a=t(class{constructor(t,i,s){this.id=t,this.parentID=i,this.index=s,this._id=9}encode(t){return t.uint(9)&&t.uint(this.id)&&t.uint(this.parentID)&&t.uint(this.index)}});i.set(9,a);const u=t(class{constructor(t,i,s){this.id=t,this.parentID=i,this.index=s,this._id=10}encode(t){return t.uint(10)&&t.uint(this.id)&&t.uint(this.parentID)&&t.uint(this.index)}});i.set(10,u);const d=t(class{constructor(t){this.id=t,this._id=11}encode(t){return t.uint(11)&&t.uint(this.id)}});i.set(11,d);const l=t(class{constructor(t,i,s){this.id=t,this.name=i,this.value=s,this._id=12}encode(t){return t.uint(12)&&t.uint(this.id)&&t.string(this.name)&&t.string(this.value)}});i.set(12,l);const p=t(class{constructor(t,i){this.id=t,this.name=i,this._id=13}encode(t){return t.uint(13)&&t.uint(this.id)&&t.string(this.name)}});i.set(13,p);const g=t(class{constructor(t,i){this.id=t,this.data=i,this._id=14}encode(t){return t.uint(14)&&t.uint(this.id)&&t.string(this.data)}});i.set(14,g);const m=t(class{constructor(t,i,s){this.id=t,this.x=i,this.y=s,this._id=16}encode(t){return t.uint(16)&&t.uint(this.id)&&t.int(this.x)&&t.int(this.y)}});i.set(16,m);const f=t(class{constructor(t,i){this.id=t,this.label=i,this._id=17}encode(t){return t.uint(17)&&t.uint(this.id)&&t.string(this.label)}});i.set(17,f);const y=t(class{constructor(t,i,s){this.id=t,this.value=i,this.mask=s,this._id=18}encode(t){return t.uint(18)&&t.uint(this.id)&&t.string(this.value)&&t.int(this.mask)}});i.set(18,y);const _=t(class{constructor(t,i){this.id=t,this.checked=i,this._id=19}encode(t){return t.uint(19)&&t.uint(this.id)&&t.boolean(this.checked)}});i.set(19,_);const v=t(class{constructor(t,i){this.x=t,this.y=i,this._id=20}encode(t){return t.uint(20)&&t.uint(this.x)&&t.uint(this.y)}});i.set(20,v);const S=t(class{constructor(t,i){this.level=t,this.value=i,this._id=22}encode(t){return t.uint(22)&&t.string(this.level)&&t.string(this.value)}});i.set(22,S);const b=t(class{constructor(t,i,s,e,n,r,h,o,c){this.requestStart=t,this.responseStart=i,this.responseEnd=s,this.domContentLoadedEventStart=e,this.domContentLoadedEventEnd=n,this.loadEventStart=r,this.loadEventEnd=h,this.firstPaint=o,this.firstContentfulPaint=c,this._id=23}encode(t){return t.uint(23)&&t.uint(this.requestStart)&&t.uint(this.responseStart)&&t.uint(this.responseEnd)&&t.uint(this.domContentLoadedEventStart)&&t.uint(this.domContentLoadedEventEnd)&&t.uint(this.loadEventStart)&&t.uint(this.loadEventEnd)&&t.uint(this.firstPaint)&&t.uint(this.firstContentfulPaint)}});i.set(23,b);const w=t(class{constructor(t,i,s){this.speedIndex=t,this.visuallyComplete=i,this.timeToInteractive=s,this._id=24}encode(t){return t.uint(24)&&t.uint(this.speedIndex)&&t.uint(this.visuallyComplete)&&t.uint(this.timeToInteractive)}});i.set(24,w);const E=t(class{constructor(t,i,s){this.name=t,this.message=i,this.payload=s,this._id=25}encode(t){return t.uint(25)&&t.string(this.name)&&t.string(this.message)&&t.string(this.payload)}});i.set(25,E);const x=t(class{constructor(t,i){this.name=t,this.payload=i,this._id=27}encode(t){return t.uint(27)&&t.string(this.name)&&t.string(this.payload)}});i.set(27,x);const T=t(class{constructor(t){this.id=t,this._id=28}encode(t){return t.uint(28)&&t.string(this.id)}});i.set(28,T);const z=t(class{constructor(t){this.id=t,this._id=29}encode(t){return t.uint(29)&&t.string(this.id)}});i.set(29,z);const k=t(class{constructor(t,i){this.key=t,this.value=i,this._id=30}encode(t){return t.uint(30)&&t.string(this.key)&&t.string(this.value)}});i.set(30,k);const A=t(class{constructor(t,i,s){this.id=t,this.rule=i,this.index=s,this._id=37}encode(t){return t.uint(37)&&t.uint(this.id)&&t.string(this.rule)&&t.uint(this.index)}});i.set(37,A);const I=t(class{constructor(t,i){this.id=t,this.index=i,this._id=38}encode(t){return t.uint(38)&&t.uint(this.id)&&t.uint(this.index)}});i.set(38,I);const L=t(class{constructor(t,i,s,e,n,r,h){this.method=t,this.url=i,this.request=s,this.response=e,this.status=n,this.timestamp=r,this.duration=h,this._id=39}encode(t){return t.uint(39)&&t.string(this.method)&&t.string(this.url)&&t.string(this.request)&&t.string(this.response)&&t.uint(this.status)&&t.uint(this.timestamp)&&t.uint(this.duration)}});i.set(39,L);const C=t(class{constructor(t,i,s,e){this.name=t,this.duration=i,this.args=s,this.result=e,this._id=40}encode(t){return t.uint(40)&&t.string(this.name)&&t.uint(this.duration)&&t.string(this.args)&&t.string(this.result)}});i.set(40,C);const M=t(class{constructor(t,i){this.key=t,this.value=i,this._id=41}encode(t){return t.uint(41)&&t.string(this.key)&&t.string(this.value)}});i.set(41,M);const N=t(class{constructor(t){this.type=t,this._id=42}encode(t){return t.uint(42)&&t.string(this.type)}});i.set(42,N);const B=t(class{constructor(t,i,s){this.action=t,this.state=i,this.duration=s,this._id=44}encode(t){return t.uint(44)&&t.string(this.action)&&t.string(this.state)&&t.uint(this.duration)}});i.set(44,B);const U=t(class{constructor(t,i){this.mutation=t,this.state=i,this._id=45}encode(t){return t.uint(45)&&t.string(this.mutation)&&t.string(this.state)}});i.set(45,U);const R=t(class{constructor(t,i){this.type=t,this.payload=i,this._id=46}encode(t){return t.uint(46)&&t.string(this.type)&&t.string(this.payload)}});i.set(46,R);const O=t(class{constructor(t,i,s){this.action=t,this.state=i,this.duration=s,this._id=47}encode(t){return t.uint(47)&&t.string(this.action)&&t.string(this.state)&&t.uint(this.duration)}});i.set(47,O);const P=t(class{constructor(t,i,s,e){this.operationKind=t,this.operationName=i,this.variables=s,this.response=e,this._id=48}encode(t){return t.uint(48)&&t.string(this.operationKind)&&t.string(this.operationName)&&t.string(this.variables)&&t.string(this.response)}});i.set(48,P);const q=t(class{constructor(t,i,s,e){this.frames=t,this.ticks=i,this.totalJSHeapSize=s,this.usedJSHeapSize=e,this._id=49}encode(t){return t.uint(49)&&t.int(this.frames)&&t.int(this.ticks)&&t.uint(this.totalJSHeapSize)&&t.uint(this.usedJSHeapSize)}});i.set(49,q);const D=t(class{constructor(t,i,s,e,n,r,h,o){this.timestamp=t,this.duration=i,this.ttfb=s,this.headerSize=e,this.encodedBodySize=n,this.decodedBodySize=r,this.url=h,this.initiator=o,this._id=53}encode(t){return t.uint(53)&&t.uint(this.timestamp)&&t.uint(this.duration)&&t.uint(this.ttfb)&&t.uint(this.headerSize)&&t.uint(this.encodedBodySize)&&t.uint(this.decodedBodySize)&&t.string(this.url)&&t.string(this.initiator)}});i.set(53,D);const W=t(class{constructor(t,i){this.downlink=t,this.type=i,this._id=54}encode(t){return t.uint(54)&&t.uint(this.downlink)&&t.string(this.type)}});i.set(54,W);const H=t(class{constructor(t){this.hidden=t,this._id=55}encode(t){return t.uint(55)&&t.boolean(this.hidden)}});i.set(55,H);const J=t(class{constructor(t,i,s,e,n,r,h){this.timestamp=t,this.duration=i,this.context=s,this.containerType=e,this.containerSrc=n,this.containerId=r,this.containerName=h,this._id=59}encode(t){return t.uint(59)&&t.uint(this.timestamp)&&t.uint(this.duration)&&t.uint(this.context)&&t.uint(this.containerType)&&t.string(this.containerSrc)&&t.string(this.containerId)&&t.string(this.containerName)}});i.set(59,J);const F=t(class{constructor(t,i,s,e){this.id=t,this.name=i,this.value=s,this.baseURL=e,this._id=60}encode(t){return t.uint(60)&&t.uint(this.id)&&t.string(this.name)&&t.string(this.value)&&t.string(this.baseURL)}});i.set(60,F);const X=t(class{constructor(t,i,s){this.id=t,this.data=i,this.baseURL=s,this._id=61}encode(t){return t.uint(61)&&t.uint(this.id)&&t.string(this.data)&&t.string(this.baseURL)}});i.set(61,X);const G=t(class{constructor(t,i){this.type=t,this.value=i,this._id=63}encode(t){return t.uint(63)&&t.string(this.type)&&t.string(this.value)}});i.set(63,G);const K=t(class{constructor(t,i){this.name=t,this.payload=i,this._id=64}encode(t){return t.uint(64)&&t.string(this.name)&&t.string(this.payload)}});i.set(64,K);const j=t(class{constructor(){this._id=65}encode(t){return t.uint(65)}});i.set(65,j);const Q=t(class{constructor(t,i,s,e){this.id=t,this.rule=i,this.index=s,this.baseURL=e,this._id=67}encode(t){return t.uint(67)&&t.uint(this.id)&&t.string(this.rule)&&t.uint(this.index)&&t.string(this.baseURL)}});i.set(67,Q);const V=t(class{constructor(t,i,s,e){this.id=t,this.hesitationTime=i,this.label=s,this.selector=e,this._id=69}encode(t){return t.uint(69)&&t.uint(this.id)&&t.uint(this.hesitationTime)&&t.string(this.label)&&t.string(this.selector)}});i.set(69,V);const Y=t(class{constructor(t,i){this.frameID=t,this.id=i,this._id=70}encode(t){return t.uint(70)&&t.uint(this.frameID)&&t.uint(this.id)}});i.set(70,Y);class Z{constructor(t,i,s,e=10,n=1e3){this.onUnauthorised=i,this.onFailure=s,this.MAX_ATTEMPTS_COUNT=e,this.ATTEMPT_TIMEOUT=n,this.attemptsCount=0,this.busy=!1,this.queue=[],this.token=null,this.ingestURL=t+"/v1/web/i"}authorise(t){this.token=t}push(t){this.busy||!this.token?this.queue.push(t):this.sendBatch(t)}retry(t){this.attemptsCount>=this.MAX_ATTEMPTS_COUNT?this.onFailure():(this.attemptsCount++,setTimeout(()=>this.sendBatch(t),this.ATTEMPT_TIMEOUT*this.attemptsCount))}sendBatch(t){this.busy=!0,fetch(this.ingestURL,{body:t,method:"POST",headers:{Authorization:"Bearer "+this.token},keepalive:t.length<65536}).then(i=>{if(401===i.status)return this.busy=!1,void this.onUnauthorised();if(i.status>=400)return void this.retry(t);this.attemptsCount=0;const s=this.queue.shift();s?this.sendBatch(s):this.busy=!1}).catch(i=>{console.warn("OpenReplay:",i),this.retry(t)})}clean(){this.queue.length=0}}const tt="function"==typeof TextEncoder?new TextEncoder:{encode(t){const i=t.length,s=new Uint8Array(3*i);let e=-1;for(var n=0,r=0,h=0;h!==i;){if(n=t.charCodeAt(h),h+=1,n>=55296&&n<=56319){if(h===i){s[e+=1]=239,s[e+=1]=191,s[e+=1]=189;break}if(!((r=t.charCodeAt(h))>=56320&&r<=57343)){s[e+=1]=239,s[e+=1]=191,s[e+=1]=189;continue}if(h+=1,(n=1024*(n-55296)+r-56320+65536)>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 it{constructor(t){this.size=t,this.offset=0,this.checkpointOffset=0,this.data=new Uint8Array(t)}checkpoint(){this.checkpointOffset=this.offset}isEmpty(){return 0===this.offset}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=tt.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}flush(){const t=this.data.slice(0,this.checkpointOffset);return this.reset(),t}}class st{constructor(t,i,s){this.pageNo=t,this.timestamp=i,this.onBatch=s,this.nextIndex=0,this.beaconSize=2e5,this.writer=new it(this.beaconSize),this.isEmpty=!0,this.beaconSizeLimit=1e6,this.prepare()}prepare(){this.writer.isEmpty()&&new s(this.pageNo,this.nextIndex,this.timestamp).encode(this.writer)}write(t){const i=t.encode(this.writer);return i&&(this.isEmpty=!1,this.writer.checkpoint(),this.nextIndex++),i}setBeaconSizeLimit(t){this.beaconSizeLimit=t}writeMessage(t){for(t instanceof e&&(this.timestamp=t.timestamp);!this.write(t);){if(this.finaliseBatch(),this.beaconSize===this.beaconSizeLimit)return console.warn("OpenReplay: beacon size overflow. Skipping large message."),this.writer.reset(),this.prepare(),void(this.isEmpty=!0);this.beaconSize=Math.min(2*this.beaconSize,this.beaconSizeLimit),this.writer=new it(this.beaconSize),this.prepare(),this.isEmpty=!0}}finaliseBatch(){this.isEmpty||(this.onBatch(this.writer.flush()),this.prepare(),this.isEmpty=!0)}clean(){this.writer.reset()}}var et;!function(t){t[t.NotActive=0]="NotActive",t[t.Starting=1]="Starting",t[t.Stopping=2]="Stopping",t[t.Active=3]="Active"}(et||(et={}));let nt=null,rt=null,ht=et.NotActive;function ot(){rt&&rt.finaliseBatch()}function ct(){ht=et.Stopping,null!==ut&&(clearInterval(ut),ut=null),rt&&(rt.clean(),rt=null),ht=et.NotActive}let at,ut=null;self.onmessage=({data:t})=>{if(null!=t){if("stop"===t)return ot(),void ct();if(Array.isArray(t)){if(!rt)throw new Error("WebWorker: writer not initialised. Service Should be Started.");const s=rt;t.forEach(t=>{const e=new(i.get(t._id));Object.assign(e,t),e instanceof H&&(e.hidden?at=setTimeout(()=>self.postMessage("restart"),18e5):clearTimeout(at)),s.writeMessage(e)})}else{if("start"===t.type)return ht=et.Starting,nt=new Z(t.ingestPoint,()=>{self.postMessage("restart")},()=>{nt&&(nt.clean(),nt=null),ct(),self.postMessage("failed")},t.connAttemptCount,t.connAttemptGap),rt=new st(t.pageNo,t.timestamp,t=>nt&&nt.push(t)),null===ut&&(ut=setInterval(ot,1e4)),ht=et.Active;if("auth"===t.type){if(!nt)throw new Error("WebWorker: sender not initialised. Received auth.");if(!rt)throw new Error("WebWorker: writer not initialised. Received auth.");return nt.authorise(t.token),void(t.beaconSizeLimit&&rt.setBeaconSizeLimit(t.beaconSizeLimit))}}}else ot()};
68
76
  `], { type: 'text/javascript' })));
69
77
  this.worker.onerror = e => {
70
78
  this._debug("webworker_error", e);
@@ -72,6 +80,7 @@ class App {
72
80
  this.worker.onmessage = ({ data }) => {
73
81
  if (data === "failed") {
74
82
  this.stop();
83
+ this._debug("worker_failed", {}); // add context (from worker)
75
84
  }
76
85
  else if (data === "restart") {
77
86
  this.stop();
@@ -83,9 +92,10 @@ class App {
83
92
  this.worker.postMessage(null);
84
93
  }
85
94
  };
86
- // TODO: keep better tactics, discard others (look https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon)
95
+ // keep better tactics, discard others?
87
96
  this.attachEventListener(window, 'beforeunload', alertWorker, false);
88
- this.attachEventListener(document, 'mouseleave', alertWorker, false, false);
97
+ this.attachEventListener(document.body, 'mouseleave', alertWorker, false, false);
98
+ // TODO: stop session after inactivity timeout (make configurable)
89
99
  this.attachEventListener(document, 'visibilitychange', alertWorker, false);
90
100
  }
91
101
  catch (e) {
@@ -159,8 +169,8 @@ class App {
159
169
  }
160
170
  // TODO: full correct semantic
161
171
  checkRequiredVersion(version) {
162
- const reqVer = version.split('.');
163
- const ver = this.version.split('.');
172
+ const reqVer = version.split(/[.-]/);
173
+ const ver = this.version.split(/[.-]/);
164
174
  for (let i = 0; i < 3; i++) {
165
175
  if (Number(ver[i]) < Number(reqVer[i]) || isNaN(Number(ver[i])) || isNaN(Number(reqVer[i]))) {
166
176
  return false;
@@ -254,7 +264,13 @@ class App {
254
264
  connAttemptCount: this.options.connAttemptCount,
255
265
  connAttemptGap: this.options.connAttemptGap,
256
266
  };
257
- this.worker.postMessage(startWorkerMsg); // brings delay of 10th ms?
267
+ this.worker.postMessage(startWorkerMsg);
268
+ this.session.update({
269
+ // "updating" with old metadata in order to trigger session's UpdateCallbacks.
270
+ // (for the case of internal .start() calls, like on "restart" webworker signal or assistent connection in tracker-assist )
271
+ metadata: startOpts.metadata || this.session.getInfo().metadata,
272
+ userID: startOpts.userID,
273
+ });
258
274
  const sReset = this.sessionStorage.getItem(this.options.session_reset_key);
259
275
  this.sessionStorage.removeItem(this.options.session_reset_key);
260
276
  return window.fetch(this.options.ingestPoint + '/v1/web/start', {
@@ -262,7 +278,7 @@ class App {
262
278
  headers: {
263
279
  'Content-Type': 'application/json',
264
280
  },
265
- body: JSON.stringify(Object.assign(Object.assign({}, startInfo), { userID: startOpts.userID || this.session.getInfo().userID, token: this.sessionStorage.getItem(this.options.session_token_key), deviceMemory: performance_js_1.deviceMemory,
281
+ body: JSON.stringify(Object.assign(Object.assign({}, startInfo), { userID: this.session.getInfo().userID, token: this.sessionStorage.getItem(this.options.session_token_key), deviceMemory: performance_js_1.deviceMemory,
266
282
  jsHeapSizeLimit: performance_js_1.jsHeapSizeLimit, reset: startOpts.forceNew || sReset !== null })),
267
283
  })
268
284
  .then(r => {
@@ -271,7 +287,7 @@ class App {
271
287
  }
272
288
  else {
273
289
  return r.text().then(text => text === CANCELED
274
- ? Promise.reject(CANCELED) // TODO: return {error: CANCELED} instead
290
+ ? Promise.reject(CANCELED)
275
291
  : Promise.reject(`Server error: ${r.status}. ${text}`));
276
292
  }
277
293
  })
@@ -287,20 +303,20 @@ class App {
287
303
  }
288
304
  this.sessionStorage.setItem(this.options.session_token_key, token);
289
305
  this.localStorage.setItem(this.options.local_uuid_key, userUUID);
290
- this.session.update(Object.assign({ sessionID }, startOpts));
291
- this.activityState = ActivityState.Active;
306
+ this.session.update({ sessionID }); // TODO: no no-explicit 'any'
292
307
  const startWorkerMsg = {
293
308
  type: "auth",
294
309
  token,
295
310
  beaconSizeLimit
296
311
  };
297
312
  this.worker.postMessage(startWorkerMsg);
313
+ this.activityState = ActivityState.Active;
298
314
  const onStartInfo = { sessionToken: token, userUUID, sessionID };
299
- this.startCallbacks.forEach((cb) => cb(onStartInfo));
315
+ this.startCallbacks.forEach((cb) => cb(onStartInfo)); // TODO: start as early as possible (before receiving the token)
300
316
  this.observer.observe();
301
317
  this.ticker.start();
302
318
  this.notify.log("OpenReplay tracking started.");
303
- // TODO: get rid of onStart
319
+ // get rid of onStart ?
304
320
  if (typeof this.options.onStart === 'function') {
305
321
  this.options.onStart(onStartInfo);
306
322
  }
@@ -333,18 +349,21 @@ class App {
333
349
  });
334
350
  }
335
351
  }
336
- stop() {
352
+ stop(calledFromAPI = false) {
337
353
  if (this.activityState !== ActivityState.NotActive) {
338
354
  try {
339
- if (this.worker) {
340
- this.worker.postMessage("stop");
341
- }
342
355
  this.sanitizer.clear();
343
356
  this.observer.disconnect();
344
357
  this.nodes.clear();
345
358
  this.ticker.stop();
346
359
  this.stopCallbacks.forEach((cb) => cb());
360
+ if (calledFromAPI) {
361
+ this.session.reset();
362
+ }
347
363
  this.notify.log("OpenReplay tracking stopped.");
364
+ if (this.worker) {
365
+ this.worker.postMessage("stop");
366
+ }
348
367
  }
349
368
  finally {
350
369
  this.activityState = ActivityState.NotActive;
@@ -1,4 +1,4 @@
1
- declare type NodeCallback = (node: Node) => void;
1
+ declare type NodeCallback = (node: Node, isStart: boolean) => void;
2
2
  export default class Nodes {
3
3
  private readonly node_id;
4
4
  private readonly nodes;
@@ -9,7 +9,7 @@ export default class Nodes {
9
9
  attachElementListener(type: string, node: Element, elementListener: EventListener): void;
10
10
  registerNode(node: Node): [id: number, isNew: boolean];
11
11
  unregisterNode(node: Node): number | undefined;
12
- callNodeCallbacks(node: Node): void;
12
+ callNodeCallbacks(node: Node, isStart: boolean): void;
13
13
  getID(node: Node): number | undefined;
14
14
  getNode(id: number): Node | undefined;
15
15
  clear(): void;
package/cjs/app/nodes.js CHANGED
@@ -47,8 +47,8 @@ class Nodes {
47
47
  }
48
48
  return id;
49
49
  }
50
- callNodeCallbacks(node) {
51
- this.nodeCallbacks.forEach((cb) => cb(node));
50
+ callNodeCallbacks(node, isStart) {
51
+ this.nodeCallbacks.forEach((cb) => cb(node, isStart));
52
52
  }
53
53
  getID(node) {
54
54
  return node[this.node_id];
@@ -4,11 +4,10 @@ export default abstract class Observer {
4
4
  protected readonly isTopContext: boolean;
5
5
  private readonly observer;
6
6
  private readonly commited;
7
+ private readonly recents;
7
8
  private readonly indexes;
8
- private readonly attributesList;
9
+ private readonly attributesMap;
9
10
  private readonly textSet;
10
- private readonly newSet;
11
- private readonly affectedSet;
12
11
  constructor(app: App, isTopContext?: boolean);
13
12
  private clear;
14
13
  private sendNodeAttribute;
@@ -27,28 +27,35 @@ function isObservable(node) {
27
27
  }
28
28
  return !isIgnored(node);
29
29
  }
30
+ /*
31
+ TODO:
32
+ - fix unbinding logic + send all removals first (ensure sequence is correct)
33
+ - use document as a 0-node in the upper context (should be updated in player at first)
34
+ */
35
+ var RecentsType;
36
+ (function (RecentsType) {
37
+ RecentsType[RecentsType["New"] = 0] = "New";
38
+ RecentsType[RecentsType["Removed"] = 1] = "Removed";
39
+ RecentsType[RecentsType["Changed"] = 2] = "Changed";
40
+ })(RecentsType || (RecentsType = {}));
30
41
  class Observer {
31
42
  constructor(app, isTopContext = false) {
32
43
  this.app = app;
33
44
  this.isTopContext = isTopContext;
34
45
  this.commited = [];
46
+ this.recents = new Map();
35
47
  this.indexes = [];
36
- this.attributesList = [];
48
+ this.attributesMap = new Map();
37
49
  this.textSet = new Set();
38
- this.newSet = new Set();
39
- this.affectedSet = new Set();
40
50
  this.observer = new MutationObserver(this.app.safe((mutations) => {
41
- for (const mutation of mutations) {
51
+ for (const mutation of mutations) { // mutations order is sequential
42
52
  const target = mutation.target;
43
53
  const type = mutation.type;
44
- if (!isObservable(target) /*|| !inDocument() */) {
54
+ if (!isObservable(target)) {
45
55
  continue;
46
56
  }
47
57
  if (type === 'childList') {
48
58
  for (let i = 0; i < mutation.removedNodes.length; i++) {
49
- // TODO: handle node removal separately from binding.
50
- // Node removals should go first in the commit.
51
- // To check: MoveNode and other possible unbinding behaviours
52
59
  this.bindTree(mutation.removedNodes[i]);
53
60
  }
54
61
  for (let i = 0; i < mutation.addedNodes.length; i++) {
@@ -60,22 +67,23 @@ class Observer {
60
67
  if (id === undefined) {
61
68
  continue;
62
69
  }
70
+ if (!this.recents.has(id)) {
71
+ this.recents.set(id, RecentsType.Changed); // TODO only when altered
72
+ }
63
73
  if (type === 'attributes') {
64
74
  const name = mutation.attributeName;
65
75
  if (name === null) {
66
76
  continue;
67
77
  }
68
- let attr = this.attributesList[id];
78
+ let attr = this.attributesMap.get(id);
69
79
  if (attr === undefined) {
70
- this.attributesList[id] = attr = new Set();
80
+ this.attributesMap.set(id, attr = new Set());
71
81
  }
72
82
  attr.add(name);
73
- this.affectedSet.add(id);
74
83
  continue;
75
84
  }
76
85
  if (type === 'characterData') {
77
86
  this.textSet.add(id);
78
- this.affectedSet.add(id);
79
87
  continue;
80
88
  }
81
89
  }
@@ -84,11 +92,10 @@ class Observer {
84
92
  }
85
93
  clear() {
86
94
  this.commited.length = 0;
95
+ this.recents.clear();
87
96
  this.indexes.length = 1;
88
- this.attributesList.length = 0;
97
+ this.attributesMap.clear();
89
98
  this.textSet.clear();
90
- this.newSet.clear();
91
- this.affectedSet.clear();
92
99
  }
93
100
  sendNodeAttribute(id, node, name, value) {
94
101
  if ((0, guards_js_1.isSVGElement)(node)) {
@@ -138,7 +145,7 @@ class Observer {
138
145
  this.app.send(new messages_js_1.SetNodeAttribute(id, name, value));
139
146
  }
140
147
  sendNodeData(id, parentElement, data) {
141
- if ((0, guards_js_1.hasTag)(parentElement, "STYLE")) {
148
+ if ((0, guards_js_1.hasTag)(parentElement, "STYLE") || (0, guards_js_1.hasTag)(parentElement, "style")) {
142
149
  this.app.send(new messages_js_1.SetCSSDataURLBased(id, data, this.app.getBaseHref()));
143
150
  return;
144
151
  }
@@ -148,9 +155,11 @@ class Observer {
148
155
  bindNode(node) {
149
156
  const [id, isNew] = this.app.nodes.registerNode(node);
150
157
  if (isNew) {
151
- this.newSet.add(id);
158
+ this.recents.set(id, RecentsType.New);
159
+ }
160
+ else if (this.recents.get(id) !== RecentsType.New) { // can we do just `else` here?
161
+ this.recents.set(id, RecentsType.Removed);
152
162
  }
153
- this.affectedSet.add(id);
154
163
  }
155
164
  bindTree(node) {
156
165
  if (!isObservable(node)) {
@@ -170,11 +179,11 @@ class Observer {
170
179
  }
171
180
  unbindNode(node) {
172
181
  const id = this.app.nodes.unregisterNode(node);
173
- // if (id !== undefined && this.recents[id] === false) { // In the old version it === flase when bindNode() was called on node but it was not new
174
- if (id !== undefined && !this.newSet.has(id) && this.affectedSet.has(id)) { // Unbinding logic should be simplified. Node removals should go first.
182
+ if (id !== undefined && this.recents.get(id) === RecentsType.Removed) {
175
183
  this.app.send(new messages_js_1.RemoveNode(id));
176
184
  }
177
185
  }
186
+ // A top-consumption function on the infinite lists test. (~1% of performance resources)
178
187
  _commitNode(id, node) {
179
188
  if ((0, guards_js_1.isRootNode)(node)) {
180
189
  return true;
@@ -183,9 +192,11 @@ class Observer {
183
192
  let parentID;
184
193
  // Disable parent check for the upper context HTMLHtmlElement, because it is root there... (before)
185
194
  // TODO: get rid of "special" cases (there is an issue with CreateDocument altered behaviour though)
186
- // TODO: Clean the logic (though now it workd fine)
195
+ // TODO: Clean the logic (though now it workd fine)
187
196
  if (!(0, guards_js_1.hasTag)(node, "HTML") || !this.isTopContext) {
188
197
  if (parent === null) {
198
+ // Sometimes one observation contains attribute mutations for the removimg node, which gets ignored here.
199
+ // That shouldn't affect the visual rendering ( should it? )
189
200
  this.unbindNode(node);
190
201
  return false;
191
202
  }
@@ -199,7 +210,11 @@ class Observer {
199
210
  return false;
200
211
  }
201
212
  this.app.sanitizer.handleNode(id, parentID, node);
213
+ if (this.app.sanitizer.isMaskedContainer(parentID)) {
214
+ return false;
215
+ }
202
216
  }
217
+ // From here parentID === undefined if node is top context HTML node
203
218
  let sibling = node.previousSibling;
204
219
  while (sibling !== null) {
205
220
  const siblingID = this.app.nodes.getID(sibling);
@@ -213,19 +228,28 @@ class Observer {
213
228
  if (sibling === null) {
214
229
  this.indexes[id] = 0;
215
230
  }
216
- const isNew = this.newSet.has(id);
231
+ const recentsType = this.recents.get(id);
232
+ const isNew = recentsType === RecentsType.New;
217
233
  const index = this.indexes[id];
218
234
  if (index === undefined) {
219
235
  throw 'commitNode: missing node index';
220
236
  }
221
- if (isNew === true) {
237
+ if (isNew) {
222
238
  if ((0, guards_js_1.isElementNode)(node)) {
239
+ let el = node;
223
240
  if (parentID !== undefined) {
224
- this.app.send(new messages_js_1.CreateElementNode(id, parentID, index, node.tagName, (0, guards_js_1.isSVGElement)(node)));
241
+ if (this.app.sanitizer.isMaskedContainer(id)) {
242
+ const width = el.clientWidth;
243
+ const height = el.clientHeight;
244
+ el = node.cloneNode();
245
+ el.style.width = width + 'px';
246
+ el.style.height = height + 'px';
247
+ }
248
+ this.app.send(new messages_js_1.CreateElementNode(id, parentID, index, el.tagName, (0, guards_js_1.isSVGElement)(node)));
225
249
  }
226
- for (let i = 0; i < node.attributes.length; i++) {
227
- const attr = node.attributes[i];
228
- this.sendNodeAttribute(id, node, attr.nodeName, attr.value);
250
+ for (let i = 0; i < el.attributes.length; i++) {
251
+ const attr = el.attributes[i];
252
+ this.sendNodeAttribute(id, el, attr.nodeName, attr.value);
229
253
  }
230
254
  }
231
255
  else if ((0, guards_js_1.isTextNode)(node)) {
@@ -235,11 +259,10 @@ class Observer {
235
259
  }
236
260
  return true;
237
261
  }
238
- if (isNew === false && parentID !== undefined) {
239
- // does this happen a lot?
262
+ if (recentsType === RecentsType.Removed && parentID !== undefined) {
240
263
  this.app.send(new messages_js_1.MoveNode(id, parentID, index));
241
264
  }
242
- const attr = this.attributesList[id];
265
+ const attr = this.attributesMap.get(id);
243
266
  if (attr !== undefined) {
244
267
  if (!(0, guards_js_1.isElementNode)(node)) {
245
268
  throw 'commitNode: node is not an element';
@@ -268,12 +291,12 @@ class Observer {
268
291
  }
269
292
  return (this.commited[id] = this._commitNode(id, node));
270
293
  }
271
- commitNodes() {
294
+ commitNodes(isStart = false) {
272
295
  let node;
273
- this.affectedSet.forEach(id => {
296
+ this.recents.forEach((type, id) => {
274
297
  this.commitNode(id);
275
- if (this.newSet.has(id) && (node = this.app.nodes.getNode(id))) {
276
- this.app.nodes.callNodeCallbacks(node);
298
+ if (type === RecentsType.New && (node = this.app.nodes.getNode(id))) {
299
+ this.app.nodes.callNodeCallbacks(node, isStart);
277
300
  }
278
301
  });
279
302
  this.clear();
@@ -290,7 +313,7 @@ class Observer {
290
313
  });
291
314
  this.bindTree(nodeToBind);
292
315
  beforeCommit(this.app.nodes.getID(node));
293
- this.commitNodes();
316
+ this.commitNodes(true);
294
317
  }
295
318
  disconnect() {
296
319
  this.observer.disconnect();
@@ -1,4 +1,4 @@
1
- import App from "./index.js";
1
+ import type App from "./index.js";
2
2
  export interface Options {
3
3
  obscureTextEmails: boolean;
4
4
  obscureTextNumbers: boolean;
@@ -6,11 +6,13 @@ export interface Options {
6
6
  export default class Sanitizer {
7
7
  private readonly app;
8
8
  private readonly masked;
9
+ private readonly maskedContainers;
9
10
  private readonly options;
10
11
  constructor(app: App, options: Partial<Options>);
11
12
  handleNode(id: number, parentID: number, node: Node): void;
12
13
  sanitize(id: number, data: string): string;
13
14
  isMasked(id: number): boolean;
15
+ isMaskedContainer(id: number): boolean;
14
16
  getInnerTextSecure(el: HTMLElement): string;
15
17
  clear(): void;
16
18
  }
@@ -6,6 +6,7 @@ class Sanitizer {
6
6
  constructor(app, options) {
7
7
  this.app = app;
8
8
  this.masked = new Set();
9
+ this.maskedContainers = new Set();
9
10
  this.options = Object.assign({
10
11
  obscureTextEmails: true,
11
12
  obscureTextNumbers: false,
@@ -13,9 +14,15 @@ class Sanitizer {
13
14
  }
14
15
  handleNode(id, parentID, node) {
15
16
  if (this.masked.has(parentID) ||
16
- ((0, guards_js_1.isElementNode)(node) && (0, utils_js_1.hasOpenreplayAttribute)(node, 'masked'))) {
17
+ ((0, guards_js_1.isElementNode)(node) &&
18
+ (0, utils_js_1.hasOpenreplayAttribute)(node, 'masked'))) {
17
19
  this.masked.add(id);
18
20
  }
21
+ if (this.maskedContainers.has(parentID) ||
22
+ ((0, guards_js_1.isElementNode)(node) &&
23
+ (0, utils_js_1.hasOpenreplayAttribute)(node, 'htmlmasked'))) {
24
+ this.maskedContainers.add(id);
25
+ }
19
26
  }
20
27
  sanitize(id, data) {
21
28
  if (this.masked.has(id)) {
@@ -33,6 +40,9 @@ class Sanitizer {
33
40
  isMasked(id) {
34
41
  return this.masked.has(id);
35
42
  }
43
+ isMaskedContainer(id) {
44
+ return this.maskedContainers.has(id);
45
+ }
36
46
  getInnerTextSecure(el) {
37
47
  const id = this.app.nodes.getID(el);
38
48
  if (!id) {
@@ -42,6 +52,7 @@ class Sanitizer {
42
52
  }
43
53
  clear() {
44
54
  this.masked.clear();
55
+ this.maskedContainers.clear();
45
56
  }
46
57
  }
47
58
  exports.default = Sanitizer;
@@ -1,4 +1,3 @@
1
- import App from "./index.js";
2
1
  interface SessionInfo {
3
2
  sessionID: string | null;
4
3
  metadata: Record<string, string>;
@@ -6,20 +5,16 @@ interface SessionInfo {
6
5
  }
7
6
  declare type OnUpdateCallback = (i: Partial<SessionInfo>) => void;
8
7
  export default class Session {
9
- private app;
10
8
  private metadata;
11
9
  private userID;
12
10
  private sessionID;
13
- private activityState;
14
11
  private callbacks;
15
- constructor(app: App);
16
12
  attachUpdateCallback(cb: OnUpdateCallback): void;
17
13
  private handleUpdate;
18
- update({ userID, metadata, sessionID }: Partial<SessionInfo>): void;
19
- private _setMetadata;
20
- private _setUserID;
14
+ update(newInfo: Partial<SessionInfo>): void;
21
15
  setMetadata(key: string, value: string): void;
22
16
  setUserID(userID: string): void;
23
17
  getInfo(): SessionInfo;
18
+ reset(): void;
24
19
  }
25
20
  export {};