@openreplay/tracker 3.5.13-beta.0 → 3.5.15-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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-beta.0'; // 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;function ht(){rt&&rt.finaliseBatch()}function ot(){et.Stopping,null!==at&&(clearInterval(at),at=null),rt&&(rt.clean(),rt=null),et.NotActive}et.NotActive;let ct,at=null;self.onmessage=({data:t})=>{if(null!=t){if("stop"===t)return ht(),void ot();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?ct=setTimeout(()=>self.postMessage("restart"),18e5):clearTimeout(ct)),s.writeMessage(e)})}else{if("start"===t.type)return et.Starting,nt=new Z(t.ingestPoint,()=>{self.postMessage("restart")},()=>{nt&&(nt.clean(),nt=null),ot(),self.postMessage("failed")},t.connAttemptCount,t.connAttemptGap),rt=new st(t.pageNo,t.timestamp,t=>nt&&nt.push(t)),null===at&&(at=setInterval(ht,1e4)),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 ht()};
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,7 +1,7 @@
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
- private readonly nodes;
4
+ private nodes;
5
5
  private readonly nodeCallbacks;
6
6
  private readonly elementListeners;
7
7
  constructor(node_id: string);
@@ -9,7 +9,8 @@ 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
+ cleanTree(): (Node | undefined)[];
13
+ callNodeCallbacks(node: Node, isStart: boolean): void;
13
14
  getID(node: Node): number | undefined;
14
15
  getNode(id: number): Node | undefined;
15
16
  clear(): void;
package/cjs/app/nodes.js CHANGED
@@ -47,8 +47,11 @@ class Nodes {
47
47
  }
48
48
  return id;
49
49
  }
50
- callNodeCallbacks(node) {
51
- this.nodeCallbacks.forEach((cb) => cb(node));
50
+ cleanTree() {
51
+ return this.nodes = this.nodes.filter((n) => n !== undefined);
52
+ }
53
+ callNodeCallbacks(node, isStart) {
54
+ this.nodeCallbacks.forEach((cb) => cb(node, isStart));
52
55
  }
53
56
  getID(node) {
54
57
  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,29 +27,40 @@ 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
+ // const deleted: number[] = [];
55
+ if (!isObservable(target)) {
45
56
  continue;
46
57
  }
47
58
  if (type === 'childList') {
48
59
  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
- this.bindTree(mutation.removedNodes[i]);
60
+ // this.bindTree(mutation.removedNodes[i]);
61
+ const id = this.unbindNode(mutation.removedNodes[i]);
62
+ // id && this.recents.delete(id)
63
+ // id && deleted.push(id)
53
64
  }
54
65
  for (let i = 0; i < mutation.addedNodes.length; i++) {
55
66
  this.bindTree(mutation.addedNodes[i]);
@@ -60,35 +71,36 @@ class Observer {
60
71
  if (id === undefined) {
61
72
  continue;
62
73
  }
74
+ if (!this.recents.has(id)) {
75
+ this.recents.set(id, RecentsType.Changed); // TODO only when altered
76
+ }
63
77
  if (type === 'attributes') {
64
78
  const name = mutation.attributeName;
65
79
  if (name === null) {
66
80
  continue;
67
81
  }
68
- let attr = this.attributesList[id];
82
+ let attr = this.attributesMap.get(id);
69
83
  if (attr === undefined) {
70
- this.attributesList[id] = attr = new Set();
84
+ this.attributesMap.set(id, attr = new Set());
71
85
  }
72
86
  attr.add(name);
73
- this.affectedSet.add(id);
74
87
  continue;
75
88
  }
76
89
  if (type === 'characterData') {
77
90
  this.textSet.add(id);
78
- this.affectedSet.add(id);
79
91
  continue;
80
92
  }
81
93
  }
82
94
  this.commitNodes();
95
+ this.app.nodes.cleanTree();
83
96
  }));
84
97
  }
85
98
  clear() {
86
99
  this.commited.length = 0;
100
+ this.recents.clear();
87
101
  this.indexes.length = 1;
88
- this.attributesList.length = 0;
102
+ this.attributesMap.clear();
89
103
  this.textSet.clear();
90
- this.newSet.clear();
91
- this.affectedSet.clear();
92
104
  }
93
105
  sendNodeAttribute(id, node, name, value) {
94
106
  if ((0, guards_js_1.isSVGElement)(node)) {
@@ -138,7 +150,7 @@ class Observer {
138
150
  this.app.send(new messages_js_1.SetNodeAttribute(id, name, value));
139
151
  }
140
152
  sendNodeData(id, parentElement, data) {
141
- if ((0, guards_js_1.hasTag)(parentElement, "STYLE")) {
153
+ if ((0, guards_js_1.hasTag)(parentElement, "STYLE") || (0, guards_js_1.hasTag)(parentElement, "style")) {
142
154
  this.app.send(new messages_js_1.SetCSSDataURLBased(id, data, this.app.getBaseHref()));
143
155
  return;
144
156
  }
@@ -148,9 +160,11 @@ class Observer {
148
160
  bindNode(node) {
149
161
  const [id, isNew] = this.app.nodes.registerNode(node);
150
162
  if (isNew) {
151
- this.newSet.add(id);
163
+ this.recents.set(id, RecentsType.New);
164
+ }
165
+ else if (this.recents.get(id) !== RecentsType.New) { // can we do just `else` here?
166
+ this.recents.set(id, RecentsType.Removed);
152
167
  }
153
- this.affectedSet.add(id);
154
168
  }
155
169
  bindTree(node) {
156
170
  if (!isObservable(node)) {
@@ -170,11 +184,12 @@ class Observer {
170
184
  }
171
185
  unbindNode(node) {
172
186
  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.
187
+ if (id !== undefined && this.recents.get(id) === RecentsType.Removed) {
175
188
  this.app.send(new messages_js_1.RemoveNode(id));
176
189
  }
190
+ return id;
177
191
  }
192
+ // A top-consumption function on the infinite lists test. (~1% of performance resources)
178
193
  _commitNode(id, node) {
179
194
  if ((0, guards_js_1.isRootNode)(node)) {
180
195
  return true;
@@ -183,9 +198,11 @@ class Observer {
183
198
  let parentID;
184
199
  // Disable parent check for the upper context HTMLHtmlElement, because it is root there... (before)
185
200
  // 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)
201
+ // TODO: Clean the logic (though now it workd fine)
187
202
  if (!(0, guards_js_1.hasTag)(node, "HTML") || !this.isTopContext) {
188
203
  if (parent === null) {
204
+ // Sometimes one observation contains attribute mutations for the removimg node, which gets ignored here.
205
+ // That shouldn't affect the visual rendering ( should it? )
189
206
  this.unbindNode(node);
190
207
  return false;
191
208
  }
@@ -199,7 +216,11 @@ class Observer {
199
216
  return false;
200
217
  }
201
218
  this.app.sanitizer.handleNode(id, parentID, node);
219
+ if (this.app.sanitizer.isMaskedContainer(parentID)) {
220
+ return false;
221
+ }
202
222
  }
223
+ // From here parentID === undefined if node is top context HTML node
203
224
  let sibling = node.previousSibling;
204
225
  while (sibling !== null) {
205
226
  const siblingID = this.app.nodes.getID(sibling);
@@ -213,19 +234,28 @@ class Observer {
213
234
  if (sibling === null) {
214
235
  this.indexes[id] = 0;
215
236
  }
216
- const isNew = this.newSet.has(id);
237
+ const recentsType = this.recents.get(id);
238
+ const isNew = recentsType === RecentsType.New;
217
239
  const index = this.indexes[id];
218
240
  if (index === undefined) {
219
241
  throw 'commitNode: missing node index';
220
242
  }
221
- if (isNew === true) {
243
+ if (isNew) {
222
244
  if ((0, guards_js_1.isElementNode)(node)) {
245
+ let el = node;
223
246
  if (parentID !== undefined) {
224
- this.app.send(new messages_js_1.CreateElementNode(id, parentID, index, node.tagName, (0, guards_js_1.isSVGElement)(node)));
247
+ if (this.app.sanitizer.isMaskedContainer(id)) {
248
+ const width = el.clientWidth;
249
+ const height = el.clientHeight;
250
+ el = node.cloneNode();
251
+ el.style.width = width + 'px';
252
+ el.style.height = height + 'px';
253
+ }
254
+ this.app.send(new messages_js_1.CreateElementNode(id, parentID, index, el.tagName, (0, guards_js_1.isSVGElement)(node)));
225
255
  }
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);
256
+ for (let i = 0; i < el.attributes.length; i++) {
257
+ const attr = el.attributes[i];
258
+ this.sendNodeAttribute(id, el, attr.nodeName, attr.value);
229
259
  }
230
260
  }
231
261
  else if ((0, guards_js_1.isTextNode)(node)) {
@@ -235,11 +265,10 @@ class Observer {
235
265
  }
236
266
  return true;
237
267
  }
238
- if (isNew === false && parentID !== undefined) {
239
- // does this happen a lot?
268
+ if (recentsType === RecentsType.Removed && parentID !== undefined) {
240
269
  this.app.send(new messages_js_1.MoveNode(id, parentID, index));
241
270
  }
242
- const attr = this.attributesList[id];
271
+ const attr = this.attributesMap.get(id);
243
272
  if (attr !== undefined) {
244
273
  if (!(0, guards_js_1.isElementNode)(node)) {
245
274
  throw 'commitNode: node is not an element';
@@ -268,12 +297,12 @@ class Observer {
268
297
  }
269
298
  return (this.commited[id] = this._commitNode(id, node));
270
299
  }
271
- commitNodes() {
300
+ commitNodes(isStart = false) {
272
301
  let node;
273
- this.affectedSet.forEach(id => {
302
+ this.recents.forEach((type, id) => {
274
303
  this.commitNode(id);
275
- if (this.newSet.has(id) && (node = this.app.nodes.getNode(id))) {
276
- this.app.nodes.callNodeCallbacks(node);
304
+ if (type === RecentsType.New && (node = this.app.nodes.getNode(id))) {
305
+ this.app.nodes.callNodeCallbacks(node, isStart);
277
306
  }
278
307
  });
279
308
  this.clear();
@@ -290,7 +319,7 @@ class Observer {
290
319
  });
291
320
  this.bindTree(nodeToBind);
292
321
  beforeCommit(this.app.nodes.getID(node));
293
- this.commitNodes();
322
+ this.commitNodes(true);
294
323
  }
295
324
  disconnect() {
296
325
  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 {};