@openreplay/tracker 3.5.0 → 3.5.3

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.
@@ -3,6 +3,7 @@ import Nodes from "./nodes.js";
3
3
  import Sanitizer from "./sanitizer.js";
4
4
  import Ticker from "./ticker.js";
5
5
  import Logger from "./logger.js";
6
+ import Session from "./session.js";
6
7
  import type { Options as ObserverOptions } from "./observer/top_observer.js";
7
8
  import type { Options as SanitizerOptions } from "./sanitizer.js";
8
9
  import type { Options as LoggerOptions } from "./logger.js";
@@ -44,6 +45,7 @@ export default class App {
44
45
  readonly sanitizer: Sanitizer;
45
46
  readonly debug: Logger;
46
47
  readonly notify: Logger;
48
+ readonly session: Session;
47
49
  private readonly messages;
48
50
  private readonly observer;
49
51
  private readonly startCallbacks;
@@ -51,18 +53,16 @@ export default class App {
51
53
  private readonly commitCallbacks;
52
54
  private readonly options;
53
55
  private readonly revID;
54
- private _sessionID;
55
- private _userID;
56
- private _metadata;
57
56
  private activityState;
58
57
  private version;
59
58
  private readonly worker?;
60
59
  constructor(projectKey: string, sessionToken: string | null | undefined, options: Partial<Options>);
61
60
  private _debug;
61
+ private readonly preStartMessages;
62
62
  send(message: Message, urgent?: boolean): void;
63
63
  private commit;
64
- attachCommitCallback(cb: CommitCallback): void;
65
64
  safe<T extends (...args: any[]) => void>(fn: T): T;
65
+ attachCommitCallback(cb: CommitCallback): void;
66
66
  attachStartCallback(cb: Callback): void;
67
67
  attachStopCallback(cb: Callback): void;
68
68
  attachEventListener(target: EventTarget, type: string, listener: EventListener, useSafe?: boolean, useCapture?: boolean): void;
@@ -74,10 +74,10 @@ export default class App {
74
74
  revID: string;
75
75
  timestamp: number;
76
76
  trackerVersion: string;
77
- userID: string | null;
78
77
  isSnippet: boolean;
79
78
  sessionID: string | null;
80
79
  metadata: Record<string, string>;
80
+ userID: string | null;
81
81
  };
82
82
  getSessionToken(): string | undefined;
83
83
  getSessionID(): string | undefined;
package/cjs/app/index.js CHANGED
@@ -8,6 +8,7 @@ const top_observer_js_1 = require("./observer/top_observer.js");
8
8
  const sanitizer_js_1 = require("./sanitizer.js");
9
9
  const ticker_js_1 = require("./ticker.js");
10
10
  const logger_js_1 = require("./logger.js");
11
+ const session_js_1 = require("./session.js");
11
12
  const performance_js_1 = require("../modules/performance.js");
12
13
  var ActivityState;
13
14
  (function (ActivityState) {
@@ -27,11 +28,9 @@ class App {
27
28
  this.startCallbacks = [];
28
29
  this.stopCallbacks = [];
29
30
  this.commitCallbacks = [];
30
- this._sessionID = null;
31
- this._userID = null;
32
- this._metadata = {};
33
31
  this.activityState = ActivityState.NotActive;
34
- this.version = '3.5.0'; // TODO: version compatability check inside each plugin.
32
+ this.version = '3.5.3'; // TODO: version compatability check inside each plugin.
33
+ this.preStartMessages = [];
35
34
  this.projectKey = projectKey;
36
35
  this.options = Object.assign({
37
36
  revID: '',
@@ -57,25 +56,20 @@ class App {
57
56
  this.ticker.attach(() => this.commit());
58
57
  this.debug = new logger_js_1.default(this.options.__debug__);
59
58
  this.notify = new logger_js_1.default(this.options.verbose ? logger_js_1.LogLevel.Warnings : logger_js_1.LogLevel.Silent);
59
+ this.session = new session_js_1.default(this);
60
60
  try {
61
61
  this.worker = new Worker(URL.createObjectURL(new Blob([`"use strict";function t(t){function s(...s){return new t(...s)}return s.prototype=t.prototype,s}const s=new Map;const i=t(class{constructor(t,s,i){this.pageNo=t,this.firstIndex=s,this.timestamp=i,this._id=80}encode(t){return t.uint(80)&&t.uint(this.pageNo)&&t.uint(this.firstIndex)&&t.int(this.timestamp)}});s.set(80,i);const n=t(class{constructor(t){this.timestamp=t,this._id=0}encode(t){return t.uint(0)&&t.uint(this.timestamp)}});s.set(0,n);const e=t(class{constructor(t,s,i){this.url=t,this.referrer=s,this.navigationStart=i,this._id=4}encode(t){return t.uint(4)&&t.string(this.url)&&t.string(this.referrer)&&t.uint(this.navigationStart)}});s.set(4,e);const r=t(class{constructor(t,s){this.width=t,this.height=s,this._id=5}encode(t){return t.uint(5)&&t.uint(this.width)&&t.uint(this.height)}});s.set(5,r);const o=t(class{constructor(t,s){this.x=t,this.y=s,this._id=6}encode(t){return t.uint(6)&&t.int(this.x)&&t.int(this.y)}});s.set(6,o);const h=t(class{constructor(){this._id=7}encode(t){return t.uint(7)}});s.set(7,h);const c=t(class{constructor(t,s,i,n,e){this.id=t,this.parentID=s,this.index=i,this.tag=n,this.svg=e,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)}});s.set(8,c);const u=t(class{constructor(t,s,i){this.id=t,this.parentID=s,this.index=i,this._id=9}encode(t){return t.uint(9)&&t.uint(this.id)&&t.uint(this.parentID)&&t.uint(this.index)}});s.set(9,u);const a=t(class{constructor(t,s,i){this.id=t,this.parentID=s,this.index=i,this._id=10}encode(t){return t.uint(10)&&t.uint(this.id)&&t.uint(this.parentID)&&t.uint(this.index)}});s.set(10,a);const d=t(class{constructor(t){this.id=t,this._id=11}encode(t){return t.uint(11)&&t.uint(this.id)}});s.set(11,d);const l=t(class{constructor(t,s,i){this.id=t,this.name=s,this.value=i,this._id=12}encode(t){return t.uint(12)&&t.uint(this.id)&&t.string(this.name)&&t.string(this.value)}});s.set(12,l);const g=t(class{constructor(t,s){this.id=t,this.name=s,this._id=13}encode(t){return t.uint(13)&&t.uint(this.id)&&t.string(this.name)}});s.set(13,g);const f=t(class{constructor(t,s){this.id=t,this.data=s,this._id=14}encode(t){return t.uint(14)&&t.uint(this.id)&&t.string(this.data)}});s.set(14,f);const p=t(class{constructor(t,s,i){this.id=t,this.x=s,this.y=i,this._id=16}encode(t){return t.uint(16)&&t.uint(this.id)&&t.int(this.x)&&t.int(this.y)}});s.set(16,p);const m=t(class{constructor(t,s){this.id=t,this.label=s,this._id=17}encode(t){return t.uint(17)&&t.uint(this.id)&&t.string(this.label)}});s.set(17,m);const _=t(class{constructor(t,s,i){this.id=t,this.value=s,this.mask=i,this._id=18}encode(t){return t.uint(18)&&t.uint(this.id)&&t.string(this.value)&&t.int(this.mask)}});s.set(18,_);const y=t(class{constructor(t,s){this.id=t,this.checked=s,this._id=19}encode(t){return t.uint(19)&&t.uint(this.id)&&t.boolean(this.checked)}});s.set(19,y);const v=t(class{constructor(t,s){this.x=t,this.y=s,this._id=20}encode(t){return t.uint(20)&&t.uint(this.x)&&t.uint(this.y)}});s.set(20,v);const S=t(class{constructor(t,s){this.level=t,this.value=s,this._id=22}encode(t){return t.uint(22)&&t.string(this.level)&&t.string(this.value)}});s.set(22,S);const b=t(class{constructor(t,s,i,n,e,r,o,h,c){this.requestStart=t,this.responseStart=s,this.responseEnd=i,this.domContentLoadedEventStart=n,this.domContentLoadedEventEnd=e,this.loadEventStart=r,this.loadEventEnd=o,this.firstPaint=h,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)}});s.set(23,b);const x=t(class{constructor(t,s,i){this.speedIndex=t,this.visuallyComplete=s,this.timeToInteractive=i,this._id=24}encode(t){return t.uint(24)&&t.uint(this.speedIndex)&&t.uint(this.visuallyComplete)&&t.uint(this.timeToInteractive)}});s.set(24,x);const E=t(class{constructor(t,s,i){this.name=t,this.message=s,this.payload=i,this._id=25}encode(t){return t.uint(25)&&t.string(this.name)&&t.string(this.message)&&t.string(this.payload)}});s.set(25,E);const k=t(class{constructor(t,s){this.name=t,this.payload=s,this._id=27}encode(t){return t.uint(27)&&t.string(this.name)&&t.string(this.payload)}});s.set(27,k);const I=t(class{constructor(t){this.id=t,this._id=28}encode(t){return t.uint(28)&&t.string(this.id)}});s.set(28,I);const z=t(class{constructor(t){this.id=t,this._id=29}encode(t){return t.uint(29)&&t.string(this.id)}});s.set(29,z);const w=t(class{constructor(t,s){this.key=t,this.value=s,this._id=30}encode(t){return t.uint(30)&&t.string(this.key)&&t.string(this.value)}});s.set(30,w);const T=t(class{constructor(t,s,i){this.id=t,this.rule=s,this.index=i,this._id=37}encode(t){return t.uint(37)&&t.uint(this.id)&&t.string(this.rule)&&t.uint(this.index)}});s.set(37,T);const L=t(class{constructor(t,s){this.id=t,this.index=s,this._id=38}encode(t){return t.uint(38)&&t.uint(this.id)&&t.uint(this.index)}});s.set(38,L);const A=t(class{constructor(t,s,i,n,e,r,o){this.method=t,this.url=s,this.request=i,this.response=n,this.status=e,this.timestamp=r,this.duration=o,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)}});s.set(39,A);const C=t(class{constructor(t,s,i,n){this.name=t,this.duration=s,this.args=i,this.result=n,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)}});s.set(40,C);const M=t(class{constructor(t,s){this.key=t,this.value=s,this._id=41}encode(t){return t.uint(41)&&t.string(this.key)&&t.string(this.value)}});s.set(41,M);const R=t(class{constructor(t){this.type=t,this._id=42}encode(t){return t.uint(42)&&t.string(this.type)}});s.set(42,R);const N=t(class{constructor(t,s,i){this.action=t,this.state=s,this.duration=i,this._id=44}encode(t){return t.uint(44)&&t.string(this.action)&&t.string(this.state)&&t.uint(this.duration)}});s.set(44,N);const D=t(class{constructor(t,s){this.mutation=t,this.state=s,this._id=45}encode(t){return t.uint(45)&&t.string(this.mutation)&&t.string(this.state)}});s.set(45,D);const U=t(class{constructor(t,s){this.type=t,this.payload=s,this._id=46}encode(t){return t.uint(46)&&t.string(this.type)&&t.string(this.payload)}});s.set(46,U);const O=t(class{constructor(t,s,i){this.action=t,this.state=s,this.duration=i,this._id=47}encode(t){return t.uint(47)&&t.string(this.action)&&t.string(this.state)&&t.uint(this.duration)}});s.set(47,O);const q=t(class{constructor(t,s,i,n){this.operationKind=t,this.operationName=s,this.variables=i,this.response=n,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)}});s.set(48,q);const H=t(class{constructor(t,s,i,n){this.frames=t,this.ticks=s,this.totalJSHeapSize=i,this.usedJSHeapSize=n,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)}});s.set(49,H);const P=t(class{constructor(t,s,i,n,e,r,o,h){this.timestamp=t,this.duration=s,this.ttfb=i,this.headerSize=n,this.encodedBodySize=e,this.decodedBodySize=r,this.url=o,this.initiator=h,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)}});s.set(53,P);const B=t(class{constructor(t,s){this.downlink=t,this.type=s,this._id=54}encode(t){return t.uint(54)&&t.uint(this.downlink)&&t.string(this.type)}});s.set(54,B);const J=t(class{constructor(t){this.hidden=t,this._id=55}encode(t){return t.uint(55)&&t.boolean(this.hidden)}});s.set(55,J);const j=t(class{constructor(t,s,i,n,e,r,o){this.timestamp=t,this.duration=s,this.context=i,this.containerType=n,this.containerSrc=e,this.containerId=r,this.containerName=o,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)}});s.set(59,j);const G=t(class{constructor(t,s,i,n){this.id=t,this.name=s,this.value=i,this.baseURL=n,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)}});s.set(60,G);const K=t(class{constructor(t,s,i){this.id=t,this.data=s,this.baseURL=i,this._id=61}encode(t){return t.uint(61)&&t.uint(this.id)&&t.string(this.data)&&t.string(this.baseURL)}});s.set(61,K);const X=t(class{constructor(t,s){this.type=t,this.value=s,this._id=63}encode(t){return t.uint(63)&&t.string(this.type)&&t.string(this.value)}});s.set(63,X);const F=t(class{constructor(t,s){this.name=t,this.payload=s,this._id=64}encode(t){return t.uint(64)&&t.string(this.name)&&t.string(this.payload)}});s.set(64,F);const Q=t(class{constructor(){this._id=65}encode(t){return t.uint(65)}});s.set(65,Q);const V=t(class{constructor(t,s,i,n){this.id=t,this.rule=s,this.index=i,this.baseURL=n,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)}});s.set(67,V);const W=t(class{constructor(t,s,i,n){this.id=t,this.hesitationTime=s,this.label=i,this.selector=n,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)}});s.set(69,W);const Y=t(class{constructor(t,s){this.frameID=t,this.id=s,this._id=70}encode(t){return t.uint(70)&&t.uint(this.frameID)&&t.uint(this.id)}});s.set(70,Y);const Z="function"==typeof TextEncoder?new TextEncoder:{encode(t){const s=t.length,i=new Uint8Array(3*s);let n=-1;for(var e=0,r=0,o=0;o!==s;){if(e=t.charCodeAt(o),o+=1,e>=55296&&e<=56319){if(o===s){i[n+=1]=239,i[n+=1]=191,i[n+=1]=189;break}if(!((r=t.charCodeAt(o))>=56320&&r<=57343)){i[n+=1]=239,i[n+=1]=191,i[n+=1]=189;continue}if(o+=1,(e=1024*(e-55296)+r-56320+65536)>65535){i[n+=1]=240|e>>>18,i[n+=1]=128|e>>>12&63,i[n+=1]=128|e>>>6&63,i[n+=1]=128|63&e;continue}}e<=127?i[n+=1]=0|e:e<=2047?(i[n+=1]=192|e>>>6,i[n+=1]=128|63&e):(i[n+=1]=224|e>>>12,i[n+=1]=128|e>>>6&63,i[n+=1]=128|63&e)}return i.subarray(0,n+1)}};class tt{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 s=Z.encode(t),i=s.byteLength;return!(!this.uint(i)||this.offset+i>this.size)&&(this.data.set(s,this.offset),this.offset+=i,!0)}reset(){this.offset=0,this.checkpointOffset=0}flush(){const t=this.data.slice(0,this.checkpointOffset);return this.reset(),t}}let st=1e6,it=2e5,nt=new tt(it),et="",rt="",ot=0,ht=0,ct=0,ut=0,at=!0;function dt(){return new i(ot,ut,ht).encode(nt)}let lt=null;const gt=[];let ft,pt=!1,mt=0,_t=8e3,yt=10;function vt(){if(at||""===rt||""===et)return;const t=nt.flush();pt?gt.push(t):(pt=!0,function t(s){const i=new XMLHttpRequest;i.open("POST",et+"/v1/web/i",!1),i.setRequestHeader("Authorization","Bearer "+rt),i.onreadystatechange=function(){if(4===this.readyState){if(0==this.status)return;if(this.status>=400)return pt=!1,St(),gt.length=0,401===this.status?void self.postMessage("restart"):void self.postMessage(null);const s=gt.shift();s?t(s):pt=!1}},i.onerror=function(i){if(mt>=yt)return St(),void self.postMessage(null);mt++,setTimeout(()=>t(s),_t)},i.send(s.buffer)}(t)),at=!0,dt()}function St(){et="",rt="",null!==lt&&(clearInterval(lt),lt=null),nt.reset()}self.onmessage=({data:t})=>{if(null!==t)return"stop"===t?(vt(),void St()):Array.isArray(t)?void t.forEach(t=>{const i=new(s.get(t._id));if(Object.assign(i,t),i instanceof n?ht=i.timestamp:i instanceof J&&(i.hidden?ft=setTimeout(()=>self.postMessage("restart"),18e5):clearTimeout(ft)),nt.checkpoint(),!i.encode(nt)&&(vt(),!i.encode(nt)))for(;!i.encode(nt);){if(it===st)return console.warn("OpenReplay: beacon size overflow."),nt.reset(),void dt();it=Math.min(2*it,st),nt=new tt(it),dt()}ut++,at=!1}):(et=t.ingestPoint||et,rt=t.token||rt,ot=t.pageNo||ot,ht=t.startTimestamp||ht,ct=t.timeAdjustment||ct,yt=t.connAttemptCount||yt,_t=t.connAttemptGap||_t,st=t.beaconSizeLimit||st,it=Math.min(st,t.beaconSize||it),nt.isEmpty()&&dt(),void(null===lt&&(lt=setInterval(vt,1e4))));vt()};
62
62
  `], { type: 'text/javascript' })));
63
63
  this.worker.onerror = e => {
64
64
  this._debug("webworker_error", e);
65
65
  };
66
- let lastTs = (0, utils_js_1.timestamp)();
67
- let fileno = 0;
68
66
  this.worker.onmessage = ({ data }) => {
69
67
  if (data === null) {
70
68
  this.stop();
71
69
  }
72
70
  else if (data === "restart") {
73
71
  this.stop();
74
- this.start({
75
- forceNew: true,
76
- userID: this._userID || undefined,
77
- metadata: this._metadata || undefined,
78
- });
72
+ this.start({ forceNew: true });
79
73
  }
80
74
  };
81
75
  const alertWorker = () => {
@@ -106,9 +100,16 @@ class App {
106
100
  this.debug.error("OpenReplay error: ", context, e);
107
101
  }
108
102
  send(message, urgent = false) {
109
- if (this.activityState !== ActivityState.Active) {
103
+ if (this.activityState === ActivityState.NotActive) {
110
104
  return;
111
105
  }
106
+ if (this.activityState === ActivityState.Starting) {
107
+ this.preStartMessages.push(message);
108
+ }
109
+ if (this.preStartMessages.length) {
110
+ this.messages.push(...this.preStartMessages);
111
+ this.preStartMessages.length = 0;
112
+ }
112
113
  this.messages.push(message);
113
114
  if (urgent) {
114
115
  this.commit();
@@ -122,9 +123,6 @@ class App {
122
123
  this.messages.length = 0;
123
124
  }
124
125
  }
125
- attachCommitCallback(cb) {
126
- this.commitCallbacks.push(cb);
127
- }
128
126
  safe(fn) {
129
127
  const app = this;
130
128
  return function (...args) {
@@ -140,6 +138,9 @@ class App {
140
138
  }
141
139
  }; // TODO: correct typing
142
140
  }
141
+ attachCommitCallback(cb) {
142
+ this.commitCallbacks.push(cb);
143
+ }
143
144
  attachStartCallback(cb) {
144
145
  this.startCallbacks.push(cb);
145
146
  }
@@ -170,12 +171,11 @@ class App {
170
171
  revID: this.revID,
171
172
  timestamp: (0, utils_js_1.timestamp)(),
172
173
  trackerVersion: this.version,
173
- userID: this._userID,
174
174
  isSnippet: this.options.__is_snippet,
175
175
  };
176
176
  }
177
177
  getSessionInfo() {
178
- return Object.assign({ sessionID: this._sessionID, metadata: this._metadata }, this.getStartInfo());
178
+ return Object.assign(Object.assign({}, this.session.getInfo()), this.getStartInfo());
179
179
  }
180
180
  getSessionToken() {
181
181
  const token = sessionStorage.getItem(this.options.session_token_key);
@@ -184,7 +184,7 @@ class App {
184
184
  }
185
185
  }
186
186
  getSessionID() {
187
- return this._sessionID || undefined;
187
+ return this.session.getInfo().sessionID || undefined;
188
188
  }
189
189
  getHost() {
190
190
  return new URL(this.options.ingestPoint).hostname;
@@ -241,8 +241,6 @@ class App {
241
241
  pageNo++;
242
242
  }
243
243
  sessionStorage.setItem(this.options.session_pageno_key, pageNo.toString());
244
- this._userID = startOpts.userID || null;
245
- this._metadata = startOpts.metadata || {}; // TODO: update both dynamically on corresponding messages
246
244
  const startInfo = this.getStartInfo();
247
245
  const messageData = {
248
246
  ingestPoint: this.options.ingestPoint,
@@ -259,7 +257,7 @@ class App {
259
257
  headers: {
260
258
  'Content-Type': 'application/json',
261
259
  },
262
- body: JSON.stringify(Object.assign(Object.assign({}, startInfo), { token: sessionStorage.getItem(this.options.session_token_key), deviceMemory: performance_js_1.deviceMemory,
260
+ body: JSON.stringify(Object.assign(Object.assign({}, startInfo), { userID: startOpts.userID || this.session.getInfo().userID, token: sessionStorage.getItem(this.options.session_token_key), deviceMemory: performance_js_1.deviceMemory,
263
261
  jsHeapSizeLimit: performance_js_1.jsHeapSizeLimit, reset: startOpts.forceNew || sReset !== null })),
264
262
  })
265
263
  .then(r => {
@@ -268,7 +266,7 @@ class App {
268
266
  }
269
267
  else {
270
268
  return r.text().then(text => text === exports.CANCELED
271
- ? Promise.reject(exports.CANCELED)
269
+ ? Promise.reject(exports.CANCELED) // TODO: return {error: CANCELED} instead
272
270
  : Promise.reject(`Server error: ${r.status}. ${text}`));
273
271
  }
274
272
  })
@@ -284,15 +282,12 @@ class App {
284
282
  }
285
283
  sessionStorage.setItem(this.options.session_token_key, token);
286
284
  localStorage.setItem(this.options.local_uuid_key, userUUID);
287
- if (typeof sessionID === 'string') {
288
- this._sessionID = sessionID;
289
- }
285
+ this.session.update(Object.assign({ sessionID }, startOpts));
290
286
  this.activityState = ActivityState.Active;
291
287
  this.worker.postMessage({ token, beaconSizeLimit });
292
288
  this.startCallbacks.forEach((cb) => cb());
293
289
  this.observer.observe();
294
290
  this.ticker.start();
295
- Object.entries(this._metadata).forEach(([key, value]) => this.send(new index_js_1.Metadata(key, value)));
296
291
  this.notify.log("OpenReplay tracking started.");
297
292
  // TODO: get rid of onStart
298
293
  const onStartInfo = { sessionToken: token, userUUID, sessionID };
@@ -1,8 +1,8 @@
1
1
  export declare const LogLevel: {
2
- readonly Verbose: 4;
3
- readonly Errors: 4;
2
+ readonly Verbose: 5;
3
+ readonly Log: 4;
4
4
  readonly Warnings: 3;
5
- readonly Log: 2;
5
+ readonly Errors: 2;
6
6
  readonly Silent: 0;
7
7
  };
8
8
  declare type LogLevel = typeof LogLevel[keyof typeof LogLevel];
@@ -18,7 +18,6 @@ interface _Options {
18
18
  export declare type Options = true | _Options | LogLevel;
19
19
  export default class Logger {
20
20
  private readonly options;
21
- private readonly opts;
22
21
  constructor(options?: Options);
23
22
  log(...args: any): void;
24
23
  warn(...args: any): void;
package/cjs/app/logger.js CHANGED
@@ -2,10 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LogLevel = void 0;
4
4
  exports.LogLevel = {
5
- Verbose: 4,
6
- Errors: 4,
5
+ Verbose: 5,
6
+ Log: 4,
7
7
  Warnings: 3,
8
- Log: 2,
8
+ Errors: 2,
9
9
  Silent: 0,
10
10
  };
11
11
  function IsCustomLevel(l) {
@@ -13,29 +13,28 @@ function IsCustomLevel(l) {
13
13
  }
14
14
  class Logger {
15
15
  constructor(options = exports.LogLevel.Silent) {
16
- this.options = options;
17
- this.opts = options === true
16
+ this.options = options === true
18
17
  ? { level: exports.LogLevel.Verbose }
19
18
  : typeof options === "number" ? { level: options } : options;
20
19
  }
21
20
  log(...args) {
22
- if (IsCustomLevel(this.opts.level)
23
- ? this.opts.level.log
24
- : this.opts.level >= exports.LogLevel.Log) {
21
+ if (IsCustomLevel(this.options.level)
22
+ ? this.options.level.log
23
+ : this.options.level >= exports.LogLevel.Log) {
25
24
  console.log(...args);
26
25
  }
27
26
  }
28
27
  warn(...args) {
29
- if (IsCustomLevel(this.opts.level)
30
- ? this.opts.level.warn
31
- : this.opts.level >= exports.LogLevel.Warnings) {
28
+ if (IsCustomLevel(this.options.level)
29
+ ? this.options.level.warn
30
+ : this.options.level >= exports.LogLevel.Warnings) {
32
31
  console.warn(...args);
33
32
  }
34
33
  }
35
34
  error(...args) {
36
- if (IsCustomLevel(this.opts.level)
37
- ? this.opts.level.error
38
- : this.opts.level >= exports.LogLevel.Errors) {
35
+ if (IsCustomLevel(this.options.level)
36
+ ? this.options.level.error
37
+ : this.options.level >= exports.LogLevel.Errors) {
39
38
  console.error(...args);
40
39
  }
41
40
  }
@@ -0,0 +1,25 @@
1
+ import App from "./index.js";
2
+ interface SessionInfo {
3
+ sessionID: string | null;
4
+ metadata: Record<string, string>;
5
+ userID: string | null;
6
+ }
7
+ declare type OnUpdateCallback = (i: Partial<SessionInfo>) => void;
8
+ export default class Session {
9
+ private app;
10
+ private metadata;
11
+ private userID;
12
+ private sessionID;
13
+ private activityState;
14
+ private callbacks;
15
+ constructor(app: App);
16
+ attachUpdateCallback(cb: OnUpdateCallback): void;
17
+ private handleUpdate;
18
+ update({ userID, metadata, sessionID }: Partial<SessionInfo>): void;
19
+ private _setMetadata;
20
+ private _setUserID;
21
+ setMetadata(key: string, value: string): void;
22
+ setUserID(userID: string): void;
23
+ getInfo(): SessionInfo;
24
+ }
25
+ export {};
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const index_js_1 = require("../messages/index.js");
4
+ var ActivityState;
5
+ (function (ActivityState) {
6
+ ActivityState[ActivityState["NotActive"] = 0] = "NotActive";
7
+ ActivityState[ActivityState["Starting"] = 1] = "Starting";
8
+ ActivityState[ActivityState["Active"] = 2] = "Active";
9
+ })(ActivityState || (ActivityState = {}));
10
+ class Session {
11
+ constructor(app) {
12
+ this.app = app;
13
+ this.metadata = {};
14
+ this.userID = null;
15
+ this.sessionID = null;
16
+ this.activityState = ActivityState.NotActive;
17
+ this.callbacks = [];
18
+ }
19
+ attachUpdateCallback(cb) {
20
+ this.callbacks.push(cb);
21
+ }
22
+ handleUpdate() {
23
+ const sessInfo = this.getInfo();
24
+ if (sessInfo.userID == null) {
25
+ delete sessInfo.userID;
26
+ }
27
+ if (sessInfo.sessionID == null) {
28
+ delete sessInfo.sessionID;
29
+ }
30
+ this.callbacks.forEach(cb => cb(sessInfo));
31
+ }
32
+ update({ userID, metadata, sessionID }) {
33
+ if (userID != null) { // TODO clear nullable/undefinable types
34
+ this._setUserID(userID);
35
+ }
36
+ if (metadata !== undefined) {
37
+ Object.entries(metadata).forEach(kv => this._setMetadata(...kv));
38
+ }
39
+ if (sessionID !== undefined) {
40
+ this.sessionID = sessionID;
41
+ }
42
+ this.handleUpdate();
43
+ }
44
+ _setMetadata(key, value) {
45
+ this.app.send(new index_js_1.Metadata(key, value));
46
+ this.metadata[key] = value;
47
+ }
48
+ _setUserID(userID) {
49
+ this.app.send(new index_js_1.UserID(userID));
50
+ this.userID = userID;
51
+ }
52
+ setMetadata(key, value) {
53
+ this._setMetadata(key, value);
54
+ this.handleUpdate();
55
+ }
56
+ setUserID(userID) {
57
+ this._setUserID(userID);
58
+ this.handleUpdate();
59
+ }
60
+ getInfo() {
61
+ return {
62
+ sessionID: this.sessionID,
63
+ metadata: this.metadata,
64
+ userID: this.userID,
65
+ };
66
+ }
67
+ }
68
+ exports.default = Session;
package/cjs/index.js CHANGED
@@ -17,7 +17,6 @@ const timing_js_1 = require("./modules/timing.js");
17
17
  const performance_js_1 = require("./modules/performance.js");
18
18
  const scroll_js_1 = require("./modules/scroll.js");
19
19
  const viewport_js_1 = require("./modules/viewport.js");
20
- const longtasks_js_1 = require("./modules/longtasks.js");
21
20
  const cssrules_js_1 = require("./modules/cssrules.js");
22
21
  const utils_js_1 = require("./utils.js");
23
22
  const DOCS_SETUP = '/installation/setup-or';
@@ -104,7 +103,6 @@ class API {
104
103
  (0, timing_js_1.default)(app, options);
105
104
  (0, performance_js_1.default)(app, options);
106
105
  (0, scroll_js_1.default)(app);
107
- (0, longtasks_js_1.default)(app);
108
106
  window.__OPENREPLAY__ = this;
109
107
  if (options.autoResetOnWindowOpen) {
110
108
  const wOpen = window.open;
@@ -129,7 +127,7 @@ class API {
129
127
  // no-cors issue only with text/plain or not-set Content-Type
130
128
  // req.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
131
129
  req.send(JSON.stringify({
132
- trackerVersion: '3.5.0',
130
+ trackerVersion: '3.5.3',
133
131
  projectKey: options.projectKey,
134
132
  doNotTrack,
135
133
  // TODO: add precise reason (an exact API missing)
@@ -180,7 +178,7 @@ class API {
180
178
  }
181
179
  setUserID(id) {
182
180
  if (typeof id === 'string' && this.app !== null) {
183
- this.app.send(new index_js_3.UserID(id));
181
+ this.app.session.setUserID(id);
184
182
  }
185
183
  }
186
184
  userID(id) {
@@ -200,7 +198,7 @@ class API {
200
198
  if (typeof key === 'string' &&
201
199
  typeof value === 'string' &&
202
200
  this.app !== null) {
203
- this.app.send(new index_js_3.Metadata(key, value));
201
+ this.app.session.setMetadata(key, value);
204
202
  }
205
203
  }
206
204
  metadata(key, value) {
@@ -3,6 +3,7 @@ import Nodes from "./nodes.js";
3
3
  import Sanitizer from "./sanitizer.js";
4
4
  import Ticker from "./ticker.js";
5
5
  import Logger from "./logger.js";
6
+ import Session from "./session.js";
6
7
  import type { Options as ObserverOptions } from "./observer/top_observer.js";
7
8
  import type { Options as SanitizerOptions } from "./sanitizer.js";
8
9
  import type { Options as LoggerOptions } from "./logger.js";
@@ -44,6 +45,7 @@ export default class App {
44
45
  readonly sanitizer: Sanitizer;
45
46
  readonly debug: Logger;
46
47
  readonly notify: Logger;
48
+ readonly session: Session;
47
49
  private readonly messages;
48
50
  private readonly observer;
49
51
  private readonly startCallbacks;
@@ -51,18 +53,16 @@ export default class App {
51
53
  private readonly commitCallbacks;
52
54
  private readonly options;
53
55
  private readonly revID;
54
- private _sessionID;
55
- private _userID;
56
- private _metadata;
57
56
  private activityState;
58
57
  private version;
59
58
  private readonly worker?;
60
59
  constructor(projectKey: string, sessionToken: string | null | undefined, options: Partial<Options>);
61
60
  private _debug;
61
+ private readonly preStartMessages;
62
62
  send(message: Message, urgent?: boolean): void;
63
63
  private commit;
64
- attachCommitCallback(cb: CommitCallback): void;
65
64
  safe<T extends (...args: any[]) => void>(fn: T): T;
65
+ attachCommitCallback(cb: CommitCallback): void;
66
66
  attachStartCallback(cb: Callback): void;
67
67
  attachStopCallback(cb: Callback): void;
68
68
  attachEventListener(target: EventTarget, type: string, listener: EventListener, useSafe?: boolean, useCapture?: boolean): void;
@@ -74,10 +74,10 @@ export default class App {
74
74
  revID: string;
75
75
  timestamp: number;
76
76
  trackerVersion: string;
77
- userID: string | null;
78
77
  isSnippet: boolean;
79
78
  sessionID: string | null;
80
79
  metadata: Record<string, string>;
80
+ userID: string | null;
81
81
  };
82
82
  getSessionToken(): string | undefined;
83
83
  getSessionID(): string | undefined;
package/lib/app/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import { timestamp } from "../utils.js";
2
- import { Timestamp, Metadata } from "../messages/index.js";
2
+ import { Timestamp } from "../messages/index.js";
3
3
  import Nodes from "./nodes.js";
4
4
  import Observer from "./observer/top_observer.js";
5
5
  import Sanitizer from "./sanitizer.js";
6
6
  import Ticker from "./ticker.js";
7
7
  import Logger, { LogLevel } from "./logger.js";
8
+ import Session from "./session.js";
8
9
  import { deviceMemory, jsHeapSizeLimit } from "../modules/performance.js";
9
10
  var ActivityState;
10
11
  (function (ActivityState) {
@@ -24,11 +25,9 @@ export default class App {
24
25
  this.startCallbacks = [];
25
26
  this.stopCallbacks = [];
26
27
  this.commitCallbacks = [];
27
- this._sessionID = null;
28
- this._userID = null;
29
- this._metadata = {};
30
28
  this.activityState = ActivityState.NotActive;
31
- this.version = '3.5.0'; // TODO: version compatability check inside each plugin.
29
+ this.version = '3.5.3'; // TODO: version compatability check inside each plugin.
30
+ this.preStartMessages = [];
32
31
  this.projectKey = projectKey;
33
32
  this.options = Object.assign({
34
33
  revID: '',
@@ -54,25 +53,20 @@ export default class App {
54
53
  this.ticker.attach(() => this.commit());
55
54
  this.debug = new Logger(this.options.__debug__);
56
55
  this.notify = new Logger(this.options.verbose ? LogLevel.Warnings : LogLevel.Silent);
56
+ this.session = new Session(this);
57
57
  try {
58
58
  this.worker = new Worker(URL.createObjectURL(new Blob([`"use strict";function t(t){function s(...s){return new t(...s)}return s.prototype=t.prototype,s}const s=new Map;const i=t(class{constructor(t,s,i){this.pageNo=t,this.firstIndex=s,this.timestamp=i,this._id=80}encode(t){return t.uint(80)&&t.uint(this.pageNo)&&t.uint(this.firstIndex)&&t.int(this.timestamp)}});s.set(80,i);const n=t(class{constructor(t){this.timestamp=t,this._id=0}encode(t){return t.uint(0)&&t.uint(this.timestamp)}});s.set(0,n);const e=t(class{constructor(t,s,i){this.url=t,this.referrer=s,this.navigationStart=i,this._id=4}encode(t){return t.uint(4)&&t.string(this.url)&&t.string(this.referrer)&&t.uint(this.navigationStart)}});s.set(4,e);const r=t(class{constructor(t,s){this.width=t,this.height=s,this._id=5}encode(t){return t.uint(5)&&t.uint(this.width)&&t.uint(this.height)}});s.set(5,r);const o=t(class{constructor(t,s){this.x=t,this.y=s,this._id=6}encode(t){return t.uint(6)&&t.int(this.x)&&t.int(this.y)}});s.set(6,o);const h=t(class{constructor(){this._id=7}encode(t){return t.uint(7)}});s.set(7,h);const c=t(class{constructor(t,s,i,n,e){this.id=t,this.parentID=s,this.index=i,this.tag=n,this.svg=e,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)}});s.set(8,c);const u=t(class{constructor(t,s,i){this.id=t,this.parentID=s,this.index=i,this._id=9}encode(t){return t.uint(9)&&t.uint(this.id)&&t.uint(this.parentID)&&t.uint(this.index)}});s.set(9,u);const a=t(class{constructor(t,s,i){this.id=t,this.parentID=s,this.index=i,this._id=10}encode(t){return t.uint(10)&&t.uint(this.id)&&t.uint(this.parentID)&&t.uint(this.index)}});s.set(10,a);const d=t(class{constructor(t){this.id=t,this._id=11}encode(t){return t.uint(11)&&t.uint(this.id)}});s.set(11,d);const l=t(class{constructor(t,s,i){this.id=t,this.name=s,this.value=i,this._id=12}encode(t){return t.uint(12)&&t.uint(this.id)&&t.string(this.name)&&t.string(this.value)}});s.set(12,l);const g=t(class{constructor(t,s){this.id=t,this.name=s,this._id=13}encode(t){return t.uint(13)&&t.uint(this.id)&&t.string(this.name)}});s.set(13,g);const f=t(class{constructor(t,s){this.id=t,this.data=s,this._id=14}encode(t){return t.uint(14)&&t.uint(this.id)&&t.string(this.data)}});s.set(14,f);const p=t(class{constructor(t,s,i){this.id=t,this.x=s,this.y=i,this._id=16}encode(t){return t.uint(16)&&t.uint(this.id)&&t.int(this.x)&&t.int(this.y)}});s.set(16,p);const m=t(class{constructor(t,s){this.id=t,this.label=s,this._id=17}encode(t){return t.uint(17)&&t.uint(this.id)&&t.string(this.label)}});s.set(17,m);const _=t(class{constructor(t,s,i){this.id=t,this.value=s,this.mask=i,this._id=18}encode(t){return t.uint(18)&&t.uint(this.id)&&t.string(this.value)&&t.int(this.mask)}});s.set(18,_);const y=t(class{constructor(t,s){this.id=t,this.checked=s,this._id=19}encode(t){return t.uint(19)&&t.uint(this.id)&&t.boolean(this.checked)}});s.set(19,y);const v=t(class{constructor(t,s){this.x=t,this.y=s,this._id=20}encode(t){return t.uint(20)&&t.uint(this.x)&&t.uint(this.y)}});s.set(20,v);const S=t(class{constructor(t,s){this.level=t,this.value=s,this._id=22}encode(t){return t.uint(22)&&t.string(this.level)&&t.string(this.value)}});s.set(22,S);const b=t(class{constructor(t,s,i,n,e,r,o,h,c){this.requestStart=t,this.responseStart=s,this.responseEnd=i,this.domContentLoadedEventStart=n,this.domContentLoadedEventEnd=e,this.loadEventStart=r,this.loadEventEnd=o,this.firstPaint=h,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)}});s.set(23,b);const x=t(class{constructor(t,s,i){this.speedIndex=t,this.visuallyComplete=s,this.timeToInteractive=i,this._id=24}encode(t){return t.uint(24)&&t.uint(this.speedIndex)&&t.uint(this.visuallyComplete)&&t.uint(this.timeToInteractive)}});s.set(24,x);const E=t(class{constructor(t,s,i){this.name=t,this.message=s,this.payload=i,this._id=25}encode(t){return t.uint(25)&&t.string(this.name)&&t.string(this.message)&&t.string(this.payload)}});s.set(25,E);const k=t(class{constructor(t,s){this.name=t,this.payload=s,this._id=27}encode(t){return t.uint(27)&&t.string(this.name)&&t.string(this.payload)}});s.set(27,k);const I=t(class{constructor(t){this.id=t,this._id=28}encode(t){return t.uint(28)&&t.string(this.id)}});s.set(28,I);const z=t(class{constructor(t){this.id=t,this._id=29}encode(t){return t.uint(29)&&t.string(this.id)}});s.set(29,z);const w=t(class{constructor(t,s){this.key=t,this.value=s,this._id=30}encode(t){return t.uint(30)&&t.string(this.key)&&t.string(this.value)}});s.set(30,w);const T=t(class{constructor(t,s,i){this.id=t,this.rule=s,this.index=i,this._id=37}encode(t){return t.uint(37)&&t.uint(this.id)&&t.string(this.rule)&&t.uint(this.index)}});s.set(37,T);const L=t(class{constructor(t,s){this.id=t,this.index=s,this._id=38}encode(t){return t.uint(38)&&t.uint(this.id)&&t.uint(this.index)}});s.set(38,L);const A=t(class{constructor(t,s,i,n,e,r,o){this.method=t,this.url=s,this.request=i,this.response=n,this.status=e,this.timestamp=r,this.duration=o,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)}});s.set(39,A);const C=t(class{constructor(t,s,i,n){this.name=t,this.duration=s,this.args=i,this.result=n,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)}});s.set(40,C);const M=t(class{constructor(t,s){this.key=t,this.value=s,this._id=41}encode(t){return t.uint(41)&&t.string(this.key)&&t.string(this.value)}});s.set(41,M);const R=t(class{constructor(t){this.type=t,this._id=42}encode(t){return t.uint(42)&&t.string(this.type)}});s.set(42,R);const N=t(class{constructor(t,s,i){this.action=t,this.state=s,this.duration=i,this._id=44}encode(t){return t.uint(44)&&t.string(this.action)&&t.string(this.state)&&t.uint(this.duration)}});s.set(44,N);const D=t(class{constructor(t,s){this.mutation=t,this.state=s,this._id=45}encode(t){return t.uint(45)&&t.string(this.mutation)&&t.string(this.state)}});s.set(45,D);const U=t(class{constructor(t,s){this.type=t,this.payload=s,this._id=46}encode(t){return t.uint(46)&&t.string(this.type)&&t.string(this.payload)}});s.set(46,U);const O=t(class{constructor(t,s,i){this.action=t,this.state=s,this.duration=i,this._id=47}encode(t){return t.uint(47)&&t.string(this.action)&&t.string(this.state)&&t.uint(this.duration)}});s.set(47,O);const q=t(class{constructor(t,s,i,n){this.operationKind=t,this.operationName=s,this.variables=i,this.response=n,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)}});s.set(48,q);const H=t(class{constructor(t,s,i,n){this.frames=t,this.ticks=s,this.totalJSHeapSize=i,this.usedJSHeapSize=n,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)}});s.set(49,H);const P=t(class{constructor(t,s,i,n,e,r,o,h){this.timestamp=t,this.duration=s,this.ttfb=i,this.headerSize=n,this.encodedBodySize=e,this.decodedBodySize=r,this.url=o,this.initiator=h,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)}});s.set(53,P);const B=t(class{constructor(t,s){this.downlink=t,this.type=s,this._id=54}encode(t){return t.uint(54)&&t.uint(this.downlink)&&t.string(this.type)}});s.set(54,B);const J=t(class{constructor(t){this.hidden=t,this._id=55}encode(t){return t.uint(55)&&t.boolean(this.hidden)}});s.set(55,J);const j=t(class{constructor(t,s,i,n,e,r,o){this.timestamp=t,this.duration=s,this.context=i,this.containerType=n,this.containerSrc=e,this.containerId=r,this.containerName=o,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)}});s.set(59,j);const G=t(class{constructor(t,s,i,n){this.id=t,this.name=s,this.value=i,this.baseURL=n,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)}});s.set(60,G);const K=t(class{constructor(t,s,i){this.id=t,this.data=s,this.baseURL=i,this._id=61}encode(t){return t.uint(61)&&t.uint(this.id)&&t.string(this.data)&&t.string(this.baseURL)}});s.set(61,K);const X=t(class{constructor(t,s){this.type=t,this.value=s,this._id=63}encode(t){return t.uint(63)&&t.string(this.type)&&t.string(this.value)}});s.set(63,X);const F=t(class{constructor(t,s){this.name=t,this.payload=s,this._id=64}encode(t){return t.uint(64)&&t.string(this.name)&&t.string(this.payload)}});s.set(64,F);const Q=t(class{constructor(){this._id=65}encode(t){return t.uint(65)}});s.set(65,Q);const V=t(class{constructor(t,s,i,n){this.id=t,this.rule=s,this.index=i,this.baseURL=n,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)}});s.set(67,V);const W=t(class{constructor(t,s,i,n){this.id=t,this.hesitationTime=s,this.label=i,this.selector=n,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)}});s.set(69,W);const Y=t(class{constructor(t,s){this.frameID=t,this.id=s,this._id=70}encode(t){return t.uint(70)&&t.uint(this.frameID)&&t.uint(this.id)}});s.set(70,Y);const Z="function"==typeof TextEncoder?new TextEncoder:{encode(t){const s=t.length,i=new Uint8Array(3*s);let n=-1;for(var e=0,r=0,o=0;o!==s;){if(e=t.charCodeAt(o),o+=1,e>=55296&&e<=56319){if(o===s){i[n+=1]=239,i[n+=1]=191,i[n+=1]=189;break}if(!((r=t.charCodeAt(o))>=56320&&r<=57343)){i[n+=1]=239,i[n+=1]=191,i[n+=1]=189;continue}if(o+=1,(e=1024*(e-55296)+r-56320+65536)>65535){i[n+=1]=240|e>>>18,i[n+=1]=128|e>>>12&63,i[n+=1]=128|e>>>6&63,i[n+=1]=128|63&e;continue}}e<=127?i[n+=1]=0|e:e<=2047?(i[n+=1]=192|e>>>6,i[n+=1]=128|63&e):(i[n+=1]=224|e>>>12,i[n+=1]=128|e>>>6&63,i[n+=1]=128|63&e)}return i.subarray(0,n+1)}};class tt{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 s=Z.encode(t),i=s.byteLength;return!(!this.uint(i)||this.offset+i>this.size)&&(this.data.set(s,this.offset),this.offset+=i,!0)}reset(){this.offset=0,this.checkpointOffset=0}flush(){const t=this.data.slice(0,this.checkpointOffset);return this.reset(),t}}let st=1e6,it=2e5,nt=new tt(it),et="",rt="",ot=0,ht=0,ct=0,ut=0,at=!0;function dt(){return new i(ot,ut,ht).encode(nt)}let lt=null;const gt=[];let ft,pt=!1,mt=0,_t=8e3,yt=10;function vt(){if(at||""===rt||""===et)return;const t=nt.flush();pt?gt.push(t):(pt=!0,function t(s){const i=new XMLHttpRequest;i.open("POST",et+"/v1/web/i",!1),i.setRequestHeader("Authorization","Bearer "+rt),i.onreadystatechange=function(){if(4===this.readyState){if(0==this.status)return;if(this.status>=400)return pt=!1,St(),gt.length=0,401===this.status?void self.postMessage("restart"):void self.postMessage(null);const s=gt.shift();s?t(s):pt=!1}},i.onerror=function(i){if(mt>=yt)return St(),void self.postMessage(null);mt++,setTimeout(()=>t(s),_t)},i.send(s.buffer)}(t)),at=!0,dt()}function St(){et="",rt="",null!==lt&&(clearInterval(lt),lt=null),nt.reset()}self.onmessage=({data:t})=>{if(null!==t)return"stop"===t?(vt(),void St()):Array.isArray(t)?void t.forEach(t=>{const i=new(s.get(t._id));if(Object.assign(i,t),i instanceof n?ht=i.timestamp:i instanceof J&&(i.hidden?ft=setTimeout(()=>self.postMessage("restart"),18e5):clearTimeout(ft)),nt.checkpoint(),!i.encode(nt)&&(vt(),!i.encode(nt)))for(;!i.encode(nt);){if(it===st)return console.warn("OpenReplay: beacon size overflow."),nt.reset(),void dt();it=Math.min(2*it,st),nt=new tt(it),dt()}ut++,at=!1}):(et=t.ingestPoint||et,rt=t.token||rt,ot=t.pageNo||ot,ht=t.startTimestamp||ht,ct=t.timeAdjustment||ct,yt=t.connAttemptCount||yt,_t=t.connAttemptGap||_t,st=t.beaconSizeLimit||st,it=Math.min(st,t.beaconSize||it),nt.isEmpty()&&dt(),void(null===lt&&(lt=setInterval(vt,1e4))));vt()};
59
59
  `], { type: 'text/javascript' })));
60
60
  this.worker.onerror = e => {
61
61
  this._debug("webworker_error", e);
62
62
  };
63
- let lastTs = timestamp();
64
- let fileno = 0;
65
63
  this.worker.onmessage = ({ data }) => {
66
64
  if (data === null) {
67
65
  this.stop();
68
66
  }
69
67
  else if (data === "restart") {
70
68
  this.stop();
71
- this.start({
72
- forceNew: true,
73
- userID: this._userID || undefined,
74
- metadata: this._metadata || undefined,
75
- });
69
+ this.start({ forceNew: true });
76
70
  }
77
71
  };
78
72
  const alertWorker = () => {
@@ -103,9 +97,16 @@ export default class App {
103
97
  this.debug.error("OpenReplay error: ", context, e);
104
98
  }
105
99
  send(message, urgent = false) {
106
- if (this.activityState !== ActivityState.Active) {
100
+ if (this.activityState === ActivityState.NotActive) {
107
101
  return;
108
102
  }
103
+ if (this.activityState === ActivityState.Starting) {
104
+ this.preStartMessages.push(message);
105
+ }
106
+ if (this.preStartMessages.length) {
107
+ this.messages.push(...this.preStartMessages);
108
+ this.preStartMessages.length = 0;
109
+ }
109
110
  this.messages.push(message);
110
111
  if (urgent) {
111
112
  this.commit();
@@ -119,9 +120,6 @@ export default class App {
119
120
  this.messages.length = 0;
120
121
  }
121
122
  }
122
- attachCommitCallback(cb) {
123
- this.commitCallbacks.push(cb);
124
- }
125
123
  safe(fn) {
126
124
  const app = this;
127
125
  return function (...args) {
@@ -137,6 +135,9 @@ export default class App {
137
135
  }
138
136
  }; // TODO: correct typing
139
137
  }
138
+ attachCommitCallback(cb) {
139
+ this.commitCallbacks.push(cb);
140
+ }
140
141
  attachStartCallback(cb) {
141
142
  this.startCallbacks.push(cb);
142
143
  }
@@ -167,12 +168,11 @@ export default class App {
167
168
  revID: this.revID,
168
169
  timestamp: timestamp(),
169
170
  trackerVersion: this.version,
170
- userID: this._userID,
171
171
  isSnippet: this.options.__is_snippet,
172
172
  };
173
173
  }
174
174
  getSessionInfo() {
175
- return Object.assign({ sessionID: this._sessionID, metadata: this._metadata }, this.getStartInfo());
175
+ return Object.assign(Object.assign({}, this.session.getInfo()), this.getStartInfo());
176
176
  }
177
177
  getSessionToken() {
178
178
  const token = sessionStorage.getItem(this.options.session_token_key);
@@ -181,7 +181,7 @@ export default class App {
181
181
  }
182
182
  }
183
183
  getSessionID() {
184
- return this._sessionID || undefined;
184
+ return this.session.getInfo().sessionID || undefined;
185
185
  }
186
186
  getHost() {
187
187
  return new URL(this.options.ingestPoint).hostname;
@@ -238,8 +238,6 @@ export default class App {
238
238
  pageNo++;
239
239
  }
240
240
  sessionStorage.setItem(this.options.session_pageno_key, pageNo.toString());
241
- this._userID = startOpts.userID || null;
242
- this._metadata = startOpts.metadata || {}; // TODO: update both dynamically on corresponding messages
243
241
  const startInfo = this.getStartInfo();
244
242
  const messageData = {
245
243
  ingestPoint: this.options.ingestPoint,
@@ -256,7 +254,7 @@ export default class App {
256
254
  headers: {
257
255
  'Content-Type': 'application/json',
258
256
  },
259
- body: JSON.stringify(Object.assign(Object.assign({}, startInfo), { token: sessionStorage.getItem(this.options.session_token_key), deviceMemory,
257
+ body: JSON.stringify(Object.assign(Object.assign({}, startInfo), { userID: startOpts.userID || this.session.getInfo().userID, token: sessionStorage.getItem(this.options.session_token_key), deviceMemory,
260
258
  jsHeapSizeLimit, reset: startOpts.forceNew || sReset !== null })),
261
259
  })
262
260
  .then(r => {
@@ -265,7 +263,7 @@ export default class App {
265
263
  }
266
264
  else {
267
265
  return r.text().then(text => text === CANCELED
268
- ? Promise.reject(CANCELED)
266
+ ? Promise.reject(CANCELED) // TODO: return {error: CANCELED} instead
269
267
  : Promise.reject(`Server error: ${r.status}. ${text}`));
270
268
  }
271
269
  })
@@ -281,15 +279,12 @@ export default class App {
281
279
  }
282
280
  sessionStorage.setItem(this.options.session_token_key, token);
283
281
  localStorage.setItem(this.options.local_uuid_key, userUUID);
284
- if (typeof sessionID === 'string') {
285
- this._sessionID = sessionID;
286
- }
282
+ this.session.update(Object.assign({ sessionID }, startOpts));
287
283
  this.activityState = ActivityState.Active;
288
284
  this.worker.postMessage({ token, beaconSizeLimit });
289
285
  this.startCallbacks.forEach((cb) => cb());
290
286
  this.observer.observe();
291
287
  this.ticker.start();
292
- Object.entries(this._metadata).forEach(([key, value]) => this.send(new Metadata(key, value)));
293
288
  this.notify.log("OpenReplay tracking started.");
294
289
  // TODO: get rid of onStart
295
290
  const onStartInfo = { sessionToken: token, userUUID, sessionID };
@@ -1,8 +1,8 @@
1
1
  export declare const LogLevel: {
2
- readonly Verbose: 4;
3
- readonly Errors: 4;
2
+ readonly Verbose: 5;
3
+ readonly Log: 4;
4
4
  readonly Warnings: 3;
5
- readonly Log: 2;
5
+ readonly Errors: 2;
6
6
  readonly Silent: 0;
7
7
  };
8
8
  declare type LogLevel = typeof LogLevel[keyof typeof LogLevel];
@@ -18,7 +18,6 @@ interface _Options {
18
18
  export declare type Options = true | _Options | LogLevel;
19
19
  export default class Logger {
20
20
  private readonly options;
21
- private readonly opts;
22
21
  constructor(options?: Options);
23
22
  log(...args: any): void;
24
23
  warn(...args: any): void;
package/lib/app/logger.js CHANGED
@@ -1,8 +1,8 @@
1
1
  export const LogLevel = {
2
- Verbose: 4,
3
- Errors: 4,
2
+ Verbose: 5,
3
+ Log: 4,
4
4
  Warnings: 3,
5
- Log: 2,
5
+ Errors: 2,
6
6
  Silent: 0,
7
7
  };
8
8
  function IsCustomLevel(l) {
@@ -10,29 +10,28 @@ function IsCustomLevel(l) {
10
10
  }
11
11
  export default class Logger {
12
12
  constructor(options = LogLevel.Silent) {
13
- this.options = options;
14
- this.opts = options === true
13
+ this.options = options === true
15
14
  ? { level: LogLevel.Verbose }
16
15
  : typeof options === "number" ? { level: options } : options;
17
16
  }
18
17
  log(...args) {
19
- if (IsCustomLevel(this.opts.level)
20
- ? this.opts.level.log
21
- : this.opts.level >= LogLevel.Log) {
18
+ if (IsCustomLevel(this.options.level)
19
+ ? this.options.level.log
20
+ : this.options.level >= LogLevel.Log) {
22
21
  console.log(...args);
23
22
  }
24
23
  }
25
24
  warn(...args) {
26
- if (IsCustomLevel(this.opts.level)
27
- ? this.opts.level.warn
28
- : this.opts.level >= LogLevel.Warnings) {
25
+ if (IsCustomLevel(this.options.level)
26
+ ? this.options.level.warn
27
+ : this.options.level >= LogLevel.Warnings) {
29
28
  console.warn(...args);
30
29
  }
31
30
  }
32
31
  error(...args) {
33
- if (IsCustomLevel(this.opts.level)
34
- ? this.opts.level.error
35
- : this.opts.level >= LogLevel.Errors) {
32
+ if (IsCustomLevel(this.options.level)
33
+ ? this.options.level.error
34
+ : this.options.level >= LogLevel.Errors) {
36
35
  console.error(...args);
37
36
  }
38
37
  }
@@ -0,0 +1,25 @@
1
+ import App from "./index.js";
2
+ interface SessionInfo {
3
+ sessionID: string | null;
4
+ metadata: Record<string, string>;
5
+ userID: string | null;
6
+ }
7
+ declare type OnUpdateCallback = (i: Partial<SessionInfo>) => void;
8
+ export default class Session {
9
+ private app;
10
+ private metadata;
11
+ private userID;
12
+ private sessionID;
13
+ private activityState;
14
+ private callbacks;
15
+ constructor(app: App);
16
+ attachUpdateCallback(cb: OnUpdateCallback): void;
17
+ private handleUpdate;
18
+ update({ userID, metadata, sessionID }: Partial<SessionInfo>): void;
19
+ private _setMetadata;
20
+ private _setUserID;
21
+ setMetadata(key: string, value: string): void;
22
+ setUserID(userID: string): void;
23
+ getInfo(): SessionInfo;
24
+ }
25
+ export {};
@@ -0,0 +1,65 @@
1
+ import { UserID, Metadata } from "../messages/index.js";
2
+ var ActivityState;
3
+ (function (ActivityState) {
4
+ ActivityState[ActivityState["NotActive"] = 0] = "NotActive";
5
+ ActivityState[ActivityState["Starting"] = 1] = "Starting";
6
+ ActivityState[ActivityState["Active"] = 2] = "Active";
7
+ })(ActivityState || (ActivityState = {}));
8
+ export default class Session {
9
+ constructor(app) {
10
+ this.app = app;
11
+ this.metadata = {};
12
+ this.userID = null;
13
+ this.sessionID = null;
14
+ this.activityState = ActivityState.NotActive;
15
+ this.callbacks = [];
16
+ }
17
+ attachUpdateCallback(cb) {
18
+ this.callbacks.push(cb);
19
+ }
20
+ handleUpdate() {
21
+ const sessInfo = this.getInfo();
22
+ if (sessInfo.userID == null) {
23
+ delete sessInfo.userID;
24
+ }
25
+ if (sessInfo.sessionID == null) {
26
+ delete sessInfo.sessionID;
27
+ }
28
+ this.callbacks.forEach(cb => cb(sessInfo));
29
+ }
30
+ update({ userID, metadata, sessionID }) {
31
+ if (userID != null) { // TODO clear nullable/undefinable types
32
+ this._setUserID(userID);
33
+ }
34
+ if (metadata !== undefined) {
35
+ Object.entries(metadata).forEach(kv => this._setMetadata(...kv));
36
+ }
37
+ if (sessionID !== undefined) {
38
+ this.sessionID = sessionID;
39
+ }
40
+ this.handleUpdate();
41
+ }
42
+ _setMetadata(key, value) {
43
+ this.app.send(new Metadata(key, value));
44
+ this.metadata[key] = value;
45
+ }
46
+ _setUserID(userID) {
47
+ this.app.send(new UserID(userID));
48
+ this.userID = userID;
49
+ }
50
+ setMetadata(key, value) {
51
+ this._setMetadata(key, value);
52
+ this.handleUpdate();
53
+ }
54
+ setUserID(userID) {
55
+ this._setUserID(userID);
56
+ this.handleUpdate();
57
+ }
58
+ getInfo() {
59
+ return {
60
+ sessionID: this.sessionID,
61
+ metadata: this.metadata,
62
+ userID: this.userID,
63
+ };
64
+ }
65
+ }
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import App, { DEFAULT_INGEST_POINT } from "./app/index.js";
2
2
  export { default as App } from './app/index.js';
3
- import { UserID, UserAnonymousID, Metadata, RawCustomEvent, CustomIssue } from "./messages/index.js";
3
+ import { UserAnonymousID, RawCustomEvent, CustomIssue } from "./messages/index.js";
4
4
  import * as _Messages from "./messages/index.js";
5
5
  export const Messages = _Messages;
6
6
  import Connection from "./modules/connection.js";
@@ -13,7 +13,6 @@ import Timing from "./modules/timing.js";
13
13
  import Performance from "./modules/performance.js";
14
14
  import Scroll from "./modules/scroll.js";
15
15
  import Viewport from "./modules/viewport.js";
16
- import Longtasks from "./modules/longtasks.js";
17
16
  import CSSRules from "./modules/cssrules.js";
18
17
  import { IN_BROWSER, deprecationWarn, DOCS_HOST } from "./utils.js";
19
18
  const DOCS_SETUP = '/installation/setup-or';
@@ -100,7 +99,6 @@ export default class API {
100
99
  Timing(app, options);
101
100
  Performance(app, options);
102
101
  Scroll(app);
103
- Longtasks(app);
104
102
  window.__OPENREPLAY__ = this;
105
103
  if (options.autoResetOnWindowOpen) {
106
104
  const wOpen = window.open;
@@ -125,7 +123,7 @@ export default class API {
125
123
  // no-cors issue only with text/plain or not-set Content-Type
126
124
  // req.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
127
125
  req.send(JSON.stringify({
128
- trackerVersion: '3.5.0',
126
+ trackerVersion: '3.5.3',
129
127
  projectKey: options.projectKey,
130
128
  doNotTrack,
131
129
  // TODO: add precise reason (an exact API missing)
@@ -176,7 +174,7 @@ export default class API {
176
174
  }
177
175
  setUserID(id) {
178
176
  if (typeof id === 'string' && this.app !== null) {
179
- this.app.send(new UserID(id));
177
+ this.app.session.setUserID(id);
180
178
  }
181
179
  }
182
180
  userID(id) {
@@ -196,7 +194,7 @@ export default class API {
196
194
  if (typeof key === 'string' &&
197
195
  typeof value === 'string' &&
198
196
  this.app !== null) {
199
- this.app.send(new Metadata(key, value));
197
+ this.app.session.setMetadata(key, value);
200
198
  }
201
199
  }
202
200
  metadata(key, value) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@openreplay/tracker",
3
3
  "description": "The OpenReplay tracker main package",
4
- "version": "3.5.0",
4
+ "version": "3.5.3",
5
5
  "keywords": [
6
6
  "logging",
7
7
  "replay"