@openreplay/tracker 3.5.11 → 3.5.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +5 -1
  2. package/cjs/app/guards.d.ts +18 -0
  3. package/cjs/app/guards.js +24 -0
  4. package/cjs/app/index.d.ts +18 -4
  5. package/cjs/app/index.js +69 -43
  6. package/cjs/app/nodes.d.ts +3 -3
  7. package/cjs/app/nodes.js +2 -2
  8. package/cjs/app/observer/observer.d.ts +1 -2
  9. package/cjs/app/observer/observer.js +73 -60
  10. package/cjs/app/observer/top_observer.js +3 -3
  11. package/cjs/app/sanitizer.d.ts +3 -1
  12. package/cjs/app/sanitizer.js +13 -2
  13. package/cjs/app/session.d.ts +2 -7
  14. package/cjs/app/session.js +24 -37
  15. package/cjs/index.d.ts +2 -2
  16. package/cjs/index.js +2 -2
  17. package/cjs/modules/console.d.ts +1 -1
  18. package/cjs/modules/console.js +2 -1
  19. package/cjs/modules/cssrules.d.ts +1 -1
  20. package/cjs/modules/cssrules.js +2 -4
  21. package/cjs/modules/exception.d.ts +1 -1
  22. package/cjs/modules/img.d.ts +1 -1
  23. package/cjs/modules/img.js +14 -6
  24. package/cjs/modules/input.d.ts +2 -1
  25. package/cjs/modules/input.js +18 -20
  26. package/cjs/modules/longtasks.d.ts +1 -1
  27. package/cjs/modules/mouse.d.ts +1 -1
  28. package/cjs/modules/mouse.js +3 -2
  29. package/cjs/modules/performance.d.ts +1 -1
  30. package/cjs/modules/scroll.d.ts +1 -1
  31. package/cjs/modules/scroll.js +3 -2
  32. package/cjs/modules/timing.d.ts +1 -1
  33. package/cjs/modules/timing.js +2 -1
  34. package/cjs/modules/viewport.d.ts +1 -1
  35. package/lib/app/guards.d.ts +18 -0
  36. package/lib/app/guards.js +16 -0
  37. package/lib/app/index.d.ts +18 -4
  38. package/lib/app/index.js +68 -42
  39. package/lib/app/nodes.d.ts +3 -3
  40. package/lib/app/nodes.js +2 -2
  41. package/lib/app/observer/observer.d.ts +1 -2
  42. package/lib/app/observer/observer.js +70 -57
  43. package/lib/app/observer/top_observer.js +3 -3
  44. package/lib/app/sanitizer.d.ts +3 -1
  45. package/lib/app/sanitizer.js +13 -2
  46. package/lib/app/session.d.ts +2 -7
  47. package/lib/app/session.js +24 -37
  48. package/lib/common/tsconfig.tsbuildinfo +1 -1
  49. package/lib/index.d.ts +2 -2
  50. package/lib/index.js +2 -2
  51. package/lib/modules/console.d.ts +1 -1
  52. package/lib/modules/console.js +2 -1
  53. package/lib/modules/cssrules.d.ts +1 -1
  54. package/lib/modules/cssrules.js +2 -4
  55. package/lib/modules/exception.d.ts +1 -1
  56. package/lib/modules/img.d.ts +1 -1
  57. package/lib/modules/img.js +14 -6
  58. package/lib/modules/input.d.ts +2 -1
  59. package/lib/modules/input.js +18 -20
  60. package/lib/modules/longtasks.d.ts +1 -1
  61. package/lib/modules/mouse.d.ts +1 -1
  62. package/lib/modules/mouse.js +3 -2
  63. package/lib/modules/performance.d.ts +1 -1
  64. package/lib/modules/scroll.d.ts +1 -1
  65. package/lib/modules/scroll.js +3 -2
  66. package/lib/modules/timing.d.ts +1 -1
  67. package/lib/modules/timing.js +2 -1
  68. package/lib/modules/viewport.d.ts +1 -1
  69. package/package.json +2 -2
  70. package/cjs/app/context.d.ts +0 -18
  71. package/cjs/app/context.js +0 -73
  72. package/lib/app/context.d.ts +0 -18
  73. package/lib/app/context.js +0 -68
package/lib/app/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Timestamp } from "../common/messages.js";
1
+ import { Timestamp, Metadata, UserID } from "../common/messages.js";
2
2
  import { timestamp } from "../utils.js";
3
3
  import Nodes from "./nodes.js";
4
4
  import Observer from "./observer/top_observer.js";
@@ -7,13 +7,16 @@ import Ticker from "./ticker.js";
7
7
  import Logger, { LogLevel } from "./logger.js";
8
8
  import Session from "./session.js";
9
9
  import { deviceMemory, jsHeapSizeLimit } from "../modules/performance.js";
10
+ const CANCELED = "canceled";
11
+ const START_ERROR = ":(";
12
+ const UnsuccessfulStart = (reason) => ({ reason, success: false });
13
+ const SuccessfulStart = (body) => (Object.assign(Object.assign({}, body), { success: true }));
10
14
  var ActivityState;
11
15
  (function (ActivityState) {
12
16
  ActivityState[ActivityState["NotActive"] = 0] = "NotActive";
13
17
  ActivityState[ActivityState["Starting"] = 1] = "Starting";
14
18
  ActivityState[ActivityState["Active"] = 2] = "Active";
15
19
  })(ActivityState || (ActivityState = {}));
16
- export const CANCELED = "canceled";
17
20
  // TODO: use backendHost only
18
21
  export const DEFAULT_INGEST_POINT = 'https://api.openreplay.com/ingest';
19
22
  export default class App {
@@ -26,7 +29,7 @@ export default class App {
26
29
  this.stopCallbacks = [];
27
30
  this.commitCallbacks = [];
28
31
  this.activityState = ActivityState.NotActive;
29
- this.version = '3.5.11'; // TODO: version compatability check inside each plugin.
32
+ this.version = '3.5.13'; // TODO: version compatability check inside each plugin.
30
33
  this.projectKey = projectKey;
31
34
  this.options = Object.assign({
32
35
  revID: '',
@@ -40,10 +43,9 @@ export default class App {
40
43
  verbose: false,
41
44
  __is_snippet: false,
42
45
  __debug_report_edp: null,
46
+ localStorage: window.localStorage,
47
+ sessionStorage: window.sessionStorage,
43
48
  }, options);
44
- if (sessionToken != null) {
45
- sessionStorage.setItem(this.options.session_token_key, sessionToken);
46
- }
47
49
  this.revID = this.options.revID;
48
50
  this.sanitizer = new Sanitizer(this, options);
49
51
  this.nodes = new Nodes(this.options.node_id);
@@ -52,9 +54,22 @@ export default class App {
52
54
  this.ticker.attach(() => this.commit());
53
55
  this.debug = new Logger(this.options.__debug__);
54
56
  this.notify = new Logger(this.options.verbose ? LogLevel.Warnings : LogLevel.Silent);
55
- this.session = new Session(this);
57
+ this.session = new Session();
58
+ this.session.attachUpdateCallback(({ userID, metadata }) => {
59
+ if (userID != null) { // TODO: nullable userID
60
+ this.send(new UserID(userID));
61
+ }
62
+ if (metadata != null) {
63
+ Object.entries(metadata).forEach(([key, value]) => this.send(new Metadata(key, value)));
64
+ }
65
+ });
66
+ this.localStorage = this.options.localStorage;
67
+ this.sessionStorage = this.options.sessionStorage;
68
+ if (sessionToken != null) {
69
+ this.sessionStorage.setItem(this.options.session_token_key, sessionToken);
70
+ }
56
71
  try {
57
- 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()};
72
+ 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()};
58
73
  `], { type: 'text/javascript' })));
59
74
  this.worker.onerror = e => {
60
75
  this._debug("webworker_error", e);
@@ -62,6 +77,7 @@ export default class App {
62
77
  this.worker.onmessage = ({ data }) => {
63
78
  if (data === "failed") {
64
79
  this.stop();
80
+ this._debug("worker_failed", {}); // add context (from worker)
65
81
  }
66
82
  else if (data === "restart") {
67
83
  this.stop();
@@ -73,9 +89,10 @@ export default class App {
73
89
  this.worker.postMessage(null);
74
90
  }
75
91
  };
76
- // TODO: keep better tactics, discard others (look https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon)
92
+ // keep better tactics, discard others?
77
93
  this.attachEventListener(window, 'beforeunload', alertWorker, false);
78
- this.attachEventListener(document, 'mouseleave', alertWorker, false, false);
94
+ this.attachEventListener(document.body, 'mouseleave', alertWorker, false, false);
95
+ // TODO: stop session after inactivity timeout (make configurable)
79
96
  this.attachEventListener(document, 'visibilitychange', alertWorker, false);
80
97
  }
81
98
  catch (e) {
@@ -149,8 +166,8 @@ export default class App {
149
166
  }
150
167
  // TODO: full correct semantic
151
168
  checkRequiredVersion(version) {
152
- const reqVer = version.split('.');
153
- const ver = this.version.split('.');
169
+ const reqVer = version.split(/[.-]/);
170
+ const ver = this.version.split(/[.-]/);
154
171
  for (let i = 0; i < 3; i++) {
155
172
  if (Number(ver[i]) < Number(reqVer[i]) || isNaN(Number(ver[i])) || isNaN(Number(reqVer[i]))) {
156
173
  return false;
@@ -160,7 +177,7 @@ export default class App {
160
177
  }
161
178
  getStartInfo() {
162
179
  return {
163
- userUUID: localStorage.getItem(this.options.local_uuid_key),
180
+ userUUID: this.localStorage.getItem(this.options.local_uuid_key),
164
181
  projectKey: this.projectKey,
165
182
  revID: this.revID,
166
183
  timestamp: timestamp(),
@@ -172,7 +189,7 @@ export default class App {
172
189
  return Object.assign(Object.assign({}, this.session.getInfo()), this.getStartInfo());
173
190
  }
174
191
  getSessionToken() {
175
- const token = sessionStorage.getItem(this.options.session_token_key);
192
+ const token = this.sessionStorage.getItem(this.options.session_token_key);
176
193
  if (token !== null) {
177
194
  return token;
178
195
  }
@@ -214,27 +231,27 @@ export default class App {
214
231
  }
215
232
  resetNextPageSession(flag) {
216
233
  if (flag) {
217
- sessionStorage.setItem(this.options.session_reset_key, 't');
234
+ this.sessionStorage.setItem(this.options.session_reset_key, 't');
218
235
  }
219
236
  else {
220
- sessionStorage.removeItem(this.options.session_reset_key);
237
+ this.sessionStorage.removeItem(this.options.session_reset_key);
221
238
  }
222
239
  }
223
240
  _start(startOpts) {
224
241
  if (!this.worker) {
225
- return Promise.reject("No worker found: perhaps, CSP is not set.");
242
+ return Promise.resolve(UnsuccessfulStart("No worker found: perhaps, CSP is not set."));
226
243
  }
227
244
  if (this.activityState !== ActivityState.NotActive) {
228
- return Promise.reject("OpenReplay: trying to call `start()` on the instance that has been started already.");
245
+ return Promise.resolve(UnsuccessfulStart("OpenReplay: trying to call `start()` on the instance that has been started already."));
229
246
  }
230
247
  this.activityState = ActivityState.Starting;
231
248
  let pageNo = 0;
232
- const pageNoStr = sessionStorage.getItem(this.options.session_pageno_key);
249
+ const pageNoStr = this.sessionStorage.getItem(this.options.session_pageno_key);
233
250
  if (pageNoStr != null) {
234
251
  pageNo = parseInt(pageNoStr);
235
252
  pageNo++;
236
253
  }
237
- sessionStorage.setItem(this.options.session_pageno_key, pageNo.toString());
254
+ this.sessionStorage.setItem(this.options.session_pageno_key, pageNo.toString());
238
255
  const startInfo = this.getStartInfo();
239
256
  const startWorkerMsg = {
240
257
  type: "start",
@@ -244,15 +261,21 @@ export default class App {
244
261
  connAttemptCount: this.options.connAttemptCount,
245
262
  connAttemptGap: this.options.connAttemptGap,
246
263
  };
247
- this.worker.postMessage(startWorkerMsg); // brings delay of 10th ms?
248
- const sReset = sessionStorage.getItem(this.options.session_reset_key);
249
- sessionStorage.removeItem(this.options.session_reset_key);
264
+ this.worker.postMessage(startWorkerMsg);
265
+ this.session.update({
266
+ // "updating" with old metadata in order to trigger session's UpdateCallbacks.
267
+ // (for the case of internal .start() calls, like on "restart" webworker signal or assistent connection in tracker-assist )
268
+ metadata: startOpts.metadata || this.session.getInfo().metadata,
269
+ userID: startOpts.userID,
270
+ });
271
+ const sReset = this.sessionStorage.getItem(this.options.session_reset_key);
272
+ this.sessionStorage.removeItem(this.options.session_reset_key);
250
273
  return window.fetch(this.options.ingestPoint + '/v1/web/start', {
251
274
  method: 'POST',
252
275
  headers: {
253
276
  'Content-Type': 'application/json',
254
277
  },
255
- body: JSON.stringify(Object.assign(Object.assign({}, startInfo), { userID: startOpts.userID || this.session.getInfo().userID, token: sessionStorage.getItem(this.options.session_token_key), deviceMemory,
278
+ body: JSON.stringify(Object.assign(Object.assign({}, startInfo), { userID: this.session.getInfo().userID, token: this.sessionStorage.getItem(this.options.session_token_key), deviceMemory,
256
279
  jsHeapSizeLimit, reset: startOpts.forceNew || sReset !== null })),
257
280
  })
258
281
  .then(r => {
@@ -261,7 +284,7 @@ export default class App {
261
284
  }
262
285
  else {
263
286
  return r.text().then(text => text === CANCELED
264
- ? Promise.reject(CANCELED) // TODO: return {error: CANCELED} instead
287
+ ? Promise.reject(CANCELED)
265
288
  : Promise.reject(`Server error: ${r.status}. ${text}`));
266
289
  }
267
290
  })
@@ -275,36 +298,36 @@ export default class App {
275
298
  (typeof beaconSizeLimit !== 'number' && typeof beaconSizeLimit !== 'undefined')) {
276
299
  return Promise.reject(`Incorrect server response: ${JSON.stringify(r)}`);
277
300
  }
278
- sessionStorage.setItem(this.options.session_token_key, token);
279
- localStorage.setItem(this.options.local_uuid_key, userUUID);
280
- this.session.update(Object.assign({ sessionID }, startOpts));
281
- this.activityState = ActivityState.Active;
301
+ this.sessionStorage.setItem(this.options.session_token_key, token);
302
+ this.localStorage.setItem(this.options.local_uuid_key, userUUID);
303
+ this.session.update({ sessionID }); // TODO: no no-explicit 'any'
282
304
  const startWorkerMsg = {
283
305
  type: "auth",
284
306
  token,
285
307
  beaconSizeLimit
286
308
  };
287
309
  this.worker.postMessage(startWorkerMsg);
310
+ this.activityState = ActivityState.Active;
288
311
  const onStartInfo = { sessionToken: token, userUUID, sessionID };
289
- this.startCallbacks.forEach((cb) => cb(onStartInfo));
312
+ this.startCallbacks.forEach((cb) => cb(onStartInfo)); // TODO: start as early as possible (before receiving the token)
290
313
  this.observer.observe();
291
314
  this.ticker.start();
292
315
  this.notify.log("OpenReplay tracking started.");
293
- // TODO: get rid of onStart
316
+ // get rid of onStart ?
294
317
  if (typeof this.options.onStart === 'function') {
295
318
  this.options.onStart(onStartInfo);
296
319
  }
297
- return onStartInfo;
320
+ return SuccessfulStart(onStartInfo);
298
321
  })
299
322
  .catch(reason => {
300
- sessionStorage.removeItem(this.options.session_token_key);
323
+ this.sessionStorage.removeItem(this.options.session_token_key);
301
324
  this.stop();
302
- //if (reason === CANCELED) { return Promise.resolve(CANCELED) } // TODO: what to return ????? Throwing is baad
303
- if (reason !== CANCELED) {
304
- this.notify.log("OpenReplay was unable to start. ", reason);
305
- this._debug("session_start", reason);
325
+ if (reason === CANCELED) {
326
+ return UnsuccessfulStart(CANCELED);
306
327
  }
307
- return Promise.reject(reason);
328
+ this.notify.log("OpenReplay was unable to start. ", reason);
329
+ this._debug("session_start", reason);
330
+ return UnsuccessfulStart(START_ERROR);
308
331
  });
309
332
  }
310
333
  start(options = {}) {
@@ -323,18 +346,21 @@ export default class App {
323
346
  });
324
347
  }
325
348
  }
326
- stop() {
349
+ stop(calledFromAPI = false) {
327
350
  if (this.activityState !== ActivityState.NotActive) {
328
351
  try {
329
- if (this.worker) {
330
- this.worker.postMessage("stop");
331
- }
332
352
  this.sanitizer.clear();
333
353
  this.observer.disconnect();
334
354
  this.nodes.clear();
335
355
  this.ticker.stop();
336
356
  this.stopCallbacks.forEach((cb) => cb());
357
+ if (calledFromAPI) {
358
+ this.session.reset();
359
+ }
337
360
  this.notify.log("OpenReplay tracking stopped.");
361
+ if (this.worker) {
362
+ this.worker.postMessage("stop");
363
+ }
338
364
  }
339
365
  finally {
340
366
  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;
@@ -7,9 +7,9 @@ export default class Nodes {
7
7
  constructor(node_id: string);
8
8
  attachNodeCallback(nodeCallback: NodeCallback): void;
9
9
  attachElementListener(type: string, node: Element, elementListener: EventListener): void;
10
- registerNode(node: Node): [number, boolean];
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/lib/app/nodes.js CHANGED
@@ -45,8 +45,8 @@ export default class Nodes {
45
45
  }
46
46
  return id;
47
47
  }
48
- callNodeCallbacks(node) {
49
- this.nodeCallbacks.forEach((cb) => cb(node));
48
+ callNodeCallbacks(node, isStart) {
49
+ this.nodeCallbacks.forEach((cb) => cb(node, isStart));
50
50
  }
51
51
  getID(node) {
52
52
  return node[this.node_id];
@@ -5,9 +5,8 @@ export default abstract class Observer {
5
5
  private readonly observer;
6
6
  private readonly commited;
7
7
  private readonly recents;
8
- private readonly myNodes;
9
8
  private readonly indexes;
10
- private readonly attributesList;
9
+ private readonly attributesMap;
11
10
  private readonly textSet;
12
11
  constructor(app: App, isTopContext?: boolean);
13
12
  private clear;
@@ -1,13 +1,10 @@
1
1
  import { RemoveNodeAttribute, SetNodeAttribute, SetNodeAttributeURLBased, SetCSSDataURLBased, SetNodeData, CreateTextNode, CreateElementNode, MoveNode, RemoveNode, } from "../../common/messages.js";
2
- import { isInstance, inDocument } from "../context.js";
3
- function isSVGElement(node) {
4
- return node.namespaceURI === 'http://www.w3.org/2000/svg';
5
- }
2
+ import { isRootNode, isTextNode, isElementNode, isSVGElement, hasTag, } from "../guards.js";
6
3
  function isIgnored(node) {
7
- if (isInstance(node, Text)) {
4
+ if (isTextNode(node)) {
8
5
  return false;
9
6
  }
10
- if (!isInstance(node, Element)) {
7
+ if (!isElementNode(node)) {
11
8
  return true;
12
9
  }
13
10
  const tag = node.tagName.toUpperCase();
@@ -22,30 +19,37 @@ function isIgnored(node) {
22
19
  tag === 'TITLE' ||
23
20
  tag === 'BASE');
24
21
  }
25
- function isRootNode(node) {
26
- return isInstance(node, Document) || isInstance(node, ShadowRoot);
27
- }
28
22
  function isObservable(node) {
29
23
  if (isRootNode(node)) {
30
24
  return true;
31
25
  }
32
26
  return !isIgnored(node);
33
27
  }
28
+ /*
29
+ TODO:
30
+ - fix unbinding logic + send all removals first (ensure sequence is correct)
31
+ - use document as a 0-node in the upper context (should be updated in player at first)
32
+ */
33
+ var RecentsType;
34
+ (function (RecentsType) {
35
+ RecentsType[RecentsType["New"] = 0] = "New";
36
+ RecentsType[RecentsType["Removed"] = 1] = "Removed";
37
+ RecentsType[RecentsType["Changed"] = 2] = "Changed";
38
+ })(RecentsType || (RecentsType = {}));
34
39
  export default class Observer {
35
40
  constructor(app, isTopContext = false) {
36
41
  this.app = app;
37
42
  this.isTopContext = isTopContext;
38
43
  this.commited = [];
39
- this.recents = [];
40
- this.myNodes = [];
44
+ this.recents = new Map();
41
45
  this.indexes = [];
42
- this.attributesList = [];
46
+ this.attributesMap = new Map();
43
47
  this.textSet = new Set();
44
48
  this.observer = new MutationObserver(this.app.safe((mutations) => {
45
- for (const mutation of mutations) {
49
+ for (const mutation of mutations) { // mutations order is sequential
46
50
  const target = mutation.target;
47
51
  const type = mutation.type;
48
- if (!isObservable(target) || !inDocument(target)) {
52
+ if (!isObservable(target)) {
49
53
  continue;
50
54
  }
51
55
  if (type === 'childList') {
@@ -61,17 +65,17 @@ export default class Observer {
61
65
  if (id === undefined) {
62
66
  continue;
63
67
  }
64
- if (id >= this.recents.length) { // TODO: something more convinient
65
- this.recents[id] = undefined;
68
+ if (!this.recents.has(id)) {
69
+ this.recents.set(id, RecentsType.Changed); // TODO only when altered
66
70
  }
67
71
  if (type === 'attributes') {
68
72
  const name = mutation.attributeName;
69
73
  if (name === null) {
70
74
  continue;
71
75
  }
72
- let attr = this.attributesList[id];
76
+ let attr = this.attributesMap.get(id);
73
77
  if (attr === undefined) {
74
- this.attributesList[id] = attr = new Set();
78
+ this.attributesMap.set(id, attr = new Set());
75
79
  }
76
80
  attr.add(name);
77
81
  continue;
@@ -86,9 +90,9 @@ export default class Observer {
86
90
  }
87
91
  clear() {
88
92
  this.commited.length = 0;
89
- this.recents.length = 0;
93
+ this.recents.clear();
90
94
  this.indexes.length = 1;
91
- this.attributesList.length = 0;
95
+ this.attributesMap.clear();
92
96
  this.textSet.clear();
93
97
  }
94
98
  sendNodeAttribute(id, node, name, value) {
@@ -119,7 +123,7 @@ export default class Observer {
119
123
  return;
120
124
  }
121
125
  if (name === 'value' &&
122
- isInstance(node, HTMLInputElement) &&
126
+ hasTag(node, "INPUT") &&
123
127
  node.type !== 'button' &&
124
128
  node.type !== 'reset' &&
125
129
  node.type !== 'submit') {
@@ -129,7 +133,7 @@ export default class Observer {
129
133
  this.app.send(new RemoveNodeAttribute(id, name));
130
134
  return;
131
135
  }
132
- if (name === 'style' || name === 'href' && isInstance(node, HTMLLinkElement)) {
136
+ if (name === 'style' || name === 'href' && hasTag(node, "LINK")) {
133
137
  this.app.send(new SetNodeAttributeURLBased(id, name, value, this.app.getBaseHref()));
134
138
  return;
135
139
  }
@@ -139,7 +143,7 @@ export default class Observer {
139
143
  this.app.send(new SetNodeAttribute(id, name, value));
140
144
  }
141
145
  sendNodeData(id, parentElement, data) {
142
- if (isInstance(parentElement, HTMLStyleElement) || isInstance(parentElement, SVGStyleElement)) {
146
+ if (hasTag(parentElement, "STYLE") || hasTag(parentElement, "style")) {
143
147
  this.app.send(new SetCSSDataURLBased(id, data, this.app.getBaseHref()));
144
148
  return;
145
149
  }
@@ -147,10 +151,13 @@ export default class Observer {
147
151
  this.app.send(new SetNodeData(id, data));
148
152
  }
149
153
  bindNode(node) {
150
- const r = this.app.nodes.registerNode(node);
151
- const id = r[0];
152
- this.recents[id] = r[1] || this.recents[id] || false;
153
- this.myNodes[id] = true;
154
+ const [id, isNew] = this.app.nodes.registerNode(node);
155
+ if (isNew) {
156
+ this.recents.set(id, RecentsType.New);
157
+ }
158
+ else if (!this.recents.has(id)) {
159
+ this.recents.set(id, RecentsType.Removed);
160
+ }
154
161
  }
155
162
  bindTree(node) {
156
163
  if (!isObservable(node)) {
@@ -170,10 +177,11 @@ export default class Observer {
170
177
  }
171
178
  unbindNode(node) {
172
179
  const id = this.app.nodes.unregisterNode(node);
173
- if (id !== undefined && this.recents[id] === false) {
180
+ if (id !== undefined && this.recents.get(id) === RecentsType.Removed) {
174
181
  this.app.send(new RemoveNode(id));
175
182
  }
176
183
  }
184
+ // A top-consumption function on the infinite lists test. (~1% of performance resources)
177
185
  _commitNode(id, node) {
178
186
  if (isRootNode(node)) {
179
187
  return true;
@@ -182,8 +190,8 @@ export default class Observer {
182
190
  let parentID;
183
191
  // Disable parent check for the upper context HTMLHtmlElement, because it is root there... (before)
184
192
  // TODO: get rid of "special" cases (there is an issue with CreateDocument altered behaviour though)
185
- // TODO: Clean the logic (though now it workd fine)
186
- if (!isInstance(node, HTMLHtmlElement) || !this.isTopContext) {
193
+ // TODO: Clean the logic (though now it workd fine)
194
+ if (!hasTag(node, "HTML") || !this.isTopContext) {
187
195
  if (parent === null) {
188
196
  this.unbindNode(node);
189
197
  return false;
@@ -198,7 +206,11 @@ export default class Observer {
198
206
  return false;
199
207
  }
200
208
  this.app.sanitizer.handleNode(id, parentID, node);
209
+ if (this.app.sanitizer.isMaskedContainer(parentID)) {
210
+ return false;
211
+ }
201
212
  }
213
+ // From here parentID === undefined if node is top context HTML node
202
214
  let sibling = node.previousSibling;
203
215
  while (sibling !== null) {
204
216
  const siblingID = this.app.nodes.getID(sibling);
@@ -210,36 +222,45 @@ export default class Observer {
210
222
  sibling = sibling.previousSibling;
211
223
  }
212
224
  if (sibling === null) {
213
- this.indexes[id] = 0; //
225
+ this.indexes[id] = 0;
214
226
  }
215
- const isNew = this.recents[id];
227
+ const recentsType = this.recents.get(id);
228
+ const isNew = recentsType === RecentsType.New;
216
229
  const index = this.indexes[id];
217
230
  if (index === undefined) {
218
231
  throw 'commitNode: missing node index';
219
232
  }
220
- if (isNew === true) {
221
- if (isInstance(node, Element)) {
233
+ if (isNew) {
234
+ if (isElementNode(node)) {
235
+ let el = node;
222
236
  if (parentID !== undefined) {
223
- this.app.send(new CreateElementNode(id, parentID, index, node.tagName, isSVGElement(node)));
237
+ if (this.app.sanitizer.isMaskedContainer(id)) {
238
+ const width = el.clientWidth;
239
+ const height = el.clientHeight;
240
+ el = node.cloneNode();
241
+ el.style.width = width + 'px';
242
+ el.style.height = height + 'px';
243
+ }
244
+ this.app.send(new CreateElementNode(id, parentID, index, el.tagName, isSVGElement(node)));
224
245
  }
225
- for (let i = 0; i < node.attributes.length; i++) {
226
- const attr = node.attributes[i];
227
- this.sendNodeAttribute(id, node, attr.nodeName, attr.value);
246
+ for (let i = 0; i < el.attributes.length; i++) {
247
+ const attr = el.attributes[i];
248
+ this.sendNodeAttribute(id, el, attr.nodeName, attr.value);
228
249
  }
229
250
  }
230
- else if (isInstance(node, Text)) {
251
+ else if (isTextNode(node)) {
231
252
  // for text node id != 0, hence parentID !== undefined and parent is Element
232
253
  this.app.send(new CreateTextNode(id, parentID, index));
233
254
  this.sendNodeData(id, parent, node.data);
234
255
  }
235
256
  return true;
236
257
  }
237
- if (isNew === false && parentID !== undefined) {
258
+ if (recentsType === RecentsType.Removed && parentID !== undefined) {
238
259
  this.app.send(new MoveNode(id, parentID, index));
239
260
  }
240
- const attr = this.attributesList[id];
261
+ const attr = this.attributesMap.get(id);
241
262
  if (attr !== undefined) {
242
- if (!isInstance(node, Element)) {
263
+ if (!isElementNode(node)) {
243
264
  throw 'commitNode: node is not an element';
244
265
  }
245
266
  for (const name of attr) {
@@ -247,7 +268,7 @@ export default class Observer {
247
268
  }
248
269
  }
249
270
  if (this.textSet.has(id)) {
250
- if (!isInstance(node, Text)) {
271
+ if (!isTextNode(node)) {
251
272
  throw 'commitNode: node is not a text';
252
273
  }
253
274
  // for text node id != 0, hence parent is Element
@@ -266,21 +287,14 @@ export default class Observer {
266
287
  }
267
288
  return (this.commited[id] = this._commitNode(id, node));
268
289
  }
269
- commitNodes() {
290
+ commitNodes(isStart = false) {
270
291
  let node;
271
- for (let id = 0; id < this.recents.length; id++) {
272
- // TODO: make things/logic nice here.
273
- // commit required in any case if recents[id] true or false (in case of unbinding) or undefined (in case of attr change).
274
- // Possible solution: separate new node commit (recents) and new attribute/move node commit
275
- // Otherwise commitNode is called on each node, which might be a lot
276
- if (!this.myNodes[id]) {
277
- continue;
278
- }
292
+ this.recents.forEach((type, id) => {
279
293
  this.commitNode(id);
280
- if (this.recents[id] === true && (node = this.app.nodes.getNode(id))) {
281
- this.app.nodes.callNodeCallbacks(node);
294
+ if (type === RecentsType.New && (node = this.app.nodes.getNode(id))) {
295
+ this.app.nodes.callNodeCallbacks(node, isStart);
282
296
  }
283
- }
297
+ });
284
298
  this.clear();
285
299
  }
286
300
  // ISSSUE
@@ -295,11 +309,10 @@ export default class Observer {
295
309
  });
296
310
  this.bindTree(nodeToBind);
297
311
  beforeCommit(this.app.nodes.getID(node));
298
- this.commitNodes();
312
+ this.commitNodes(true);
299
313
  }
300
314
  disconnect() {
301
315
  this.observer.disconnect();
302
316
  this.clear();
303
- this.myNodes.length = 0;
304
317
  }
305
318
  }