@openreplay/tracker 3.5.2 → 3.5.5-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,37 +12,60 @@ function isInstance(node, constr) {
12
12
  // @ts-ignore (for EI, Safary)
13
13
  doc.parentWindow ||
14
14
  doc.defaultView; // TODO: smart global typing for Window object
15
- while (context.parent && context.parent !== context) {
15
+ while (context !== window) {
16
16
  // @ts-ignore
17
17
  if (node instanceof context[constr.name]) {
18
18
  return true;
19
19
  }
20
20
  // @ts-ignore
21
- context = context.parent;
21
+ context = context.parent || window;
22
22
  }
23
23
  // @ts-ignore
24
24
  return node instanceof context[constr.name];
25
25
  }
26
26
  exports.isInstance = isInstance;
27
+ // TODO: ensure 1. it works in every cases (iframes/detached nodes) and 2. the most efficient
27
28
  function inDocument(node) {
28
29
  const doc = node.ownerDocument;
29
30
  if (!doc) {
30
- return false;
31
- }
32
- if (doc.contains(node)) {
33
31
  return true;
34
- }
35
- let context =
36
- // @ts-ignore (for EI, Safary)
37
- doc.parentWindow ||
38
- doc.defaultView;
39
- while (context.parent && context.parent !== context) {
40
- if (context.document.contains(node)) {
32
+ } // Document
33
+ let current = node;
34
+ while (current) {
35
+ if (current === doc) {
41
36
  return true;
42
37
  }
43
- // @ts-ignore
44
- context = context.parent;
38
+ else if (isInstance(current, ShadowRoot)) {
39
+ current = current.host;
40
+ }
41
+ else {
42
+ current = current.parentNode;
43
+ }
45
44
  }
46
45
  return false;
47
46
  }
48
47
  exports.inDocument = inDocument;
48
+ // export function inDocument(node: Node): boolean {
49
+ // // @ts-ignore compatability
50
+ // if (node.getRootNode) {
51
+ // let root: Node
52
+ // while ((root = node.getRootNode()) !== node) {
53
+ // ////
54
+ // }
55
+ // }
56
+ // const doc = node.ownerDocument
57
+ // if (!doc) { return false }
58
+ // if (doc.contains(node)) { return true }
59
+ // let context: Window =
60
+ // // @ts-ignore (for EI, Safary)
61
+ // doc.parentWindow ||
62
+ // doc.defaultView;
63
+ // while(context.parent && context.parent !== context) {
64
+ // if (context.document.contains(node)) {
65
+ // return true
66
+ // }
67
+ // // @ts-ignore
68
+ // context = context.parent
69
+ // }
70
+ // return false;
71
+ // }
@@ -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,9 +53,6 @@ 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?;
@@ -62,8 +61,8 @@ export default class App {
62
61
  private readonly preStartMessages;
63
62
  send(message: Message, urgent?: boolean): void;
64
63
  private commit;
65
- attachCommitCallback(cb: CommitCallback): void;
66
64
  safe<T extends (...args: any[]) => void>(fn: T): T;
65
+ attachCommitCallback(cb: CommitCallback): void;
67
66
  attachStartCallback(cb: Callback): void;
68
67
  attachStopCallback(cb: Callback): void;
69
68
  attachEventListener(target: EventTarget, type: string, listener: EventListener, useSafe?: boolean, useCapture?: boolean): void;
@@ -75,10 +74,10 @@ export default class App {
75
74
  revID: string;
76
75
  timestamp: number;
77
76
  trackerVersion: string;
78
- userID: string | null;
79
77
  isSnippet: boolean;
80
78
  sessionID: string | null;
81
79
  metadata: Record<string, string>;
80
+ userID: string | null;
82
81
  };
83
82
  getSessionToken(): string | undefined;
84
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,8 @@ 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.2'; // TODO: version compatability check inside each plugin.
32
+ this.version = '3.5.5-beta.0'; // TODO: version compatability check inside each plugin.
35
33
  this.preStartMessages = [];
36
34
  this.projectKey = projectKey;
37
35
  this.options = Object.assign({
@@ -58,25 +56,20 @@ class App {
58
56
  this.ticker.attach(() => this.commit());
59
57
  this.debug = new logger_js_1.default(this.options.__debug__);
60
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);
61
60
  try {
62
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()};
63
62
  `], { type: 'text/javascript' })));
64
63
  this.worker.onerror = e => {
65
64
  this._debug("webworker_error", e);
66
65
  };
67
- let lastTs = (0, utils_js_1.timestamp)();
68
- let fileno = 0;
69
66
  this.worker.onmessage = ({ data }) => {
70
67
  if (data === null) {
71
68
  this.stop();
72
69
  }
73
70
  else if (data === "restart") {
74
71
  this.stop();
75
- this.start({
76
- forceNew: true,
77
- userID: this._userID || undefined,
78
- metadata: this._metadata || undefined,
79
- });
72
+ this.start({ forceNew: true });
80
73
  }
81
74
  };
82
75
  const alertWorker = () => {
@@ -130,9 +123,6 @@ class App {
130
123
  this.messages.length = 0;
131
124
  }
132
125
  }
133
- attachCommitCallback(cb) {
134
- this.commitCallbacks.push(cb);
135
- }
136
126
  safe(fn) {
137
127
  const app = this;
138
128
  return function (...args) {
@@ -148,6 +138,9 @@ class App {
148
138
  }
149
139
  }; // TODO: correct typing
150
140
  }
141
+ attachCommitCallback(cb) {
142
+ this.commitCallbacks.push(cb);
143
+ }
151
144
  attachStartCallback(cb) {
152
145
  this.startCallbacks.push(cb);
153
146
  }
@@ -178,12 +171,11 @@ class App {
178
171
  revID: this.revID,
179
172
  timestamp: (0, utils_js_1.timestamp)(),
180
173
  trackerVersion: this.version,
181
- userID: this._userID,
182
174
  isSnippet: this.options.__is_snippet,
183
175
  };
184
176
  }
185
177
  getSessionInfo() {
186
- return Object.assign({ sessionID: this._sessionID, metadata: this._metadata }, this.getStartInfo());
178
+ return Object.assign(Object.assign({}, this.session.getInfo()), this.getStartInfo());
187
179
  }
188
180
  getSessionToken() {
189
181
  const token = sessionStorage.getItem(this.options.session_token_key);
@@ -192,7 +184,7 @@ class App {
192
184
  }
193
185
  }
194
186
  getSessionID() {
195
- return this._sessionID || undefined;
187
+ return this.session.getInfo().sessionID || undefined;
196
188
  }
197
189
  getHost() {
198
190
  return new URL(this.options.ingestPoint).hostname;
@@ -249,8 +241,6 @@ class App {
249
241
  pageNo++;
250
242
  }
251
243
  sessionStorage.setItem(this.options.session_pageno_key, pageNo.toString());
252
- this._userID = startOpts.userID || null;
253
- this._metadata = startOpts.metadata || {}; // TODO: update both dynamically on corresponding messages
254
244
  const startInfo = this.getStartInfo();
255
245
  const messageData = {
256
246
  ingestPoint: this.options.ingestPoint,
@@ -267,7 +257,7 @@ class App {
267
257
  headers: {
268
258
  'Content-Type': 'application/json',
269
259
  },
270
- 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,
271
261
  jsHeapSizeLimit: performance_js_1.jsHeapSizeLimit, reset: startOpts.forceNew || sReset !== null })),
272
262
  })
273
263
  .then(r => {
@@ -292,15 +282,12 @@ class App {
292
282
  }
293
283
  sessionStorage.setItem(this.options.session_token_key, token);
294
284
  localStorage.setItem(this.options.local_uuid_key, userUUID);
295
- if (typeof sessionID === 'string') {
296
- this._sessionID = sessionID;
297
- }
285
+ this.session.update(Object.assign({ sessionID }, startOpts));
298
286
  this.activityState = ActivityState.Active;
299
287
  this.worker.postMessage({ token, beaconSizeLimit });
300
288
  this.startCallbacks.forEach((cb) => cb());
301
289
  this.observer.observe();
302
290
  this.ticker.start();
303
- Object.entries(this._metadata).forEach(([key, value]) => this.send(new index_js_1.Metadata(key, value)));
304
291
  this.notify.log("OpenReplay tracking started.");
305
292
  // TODO: get rid of onStart
306
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
  }
@@ -1,7 +1,7 @@
1
1
  import App from "../index.js";
2
2
  export default abstract class Observer {
3
3
  protected readonly app: App;
4
- protected readonly context: Window;
4
+ protected readonly isTopContext: boolean;
5
5
  private readonly observer;
6
6
  private readonly commited;
7
7
  private readonly recents;
@@ -9,8 +9,7 @@ export default abstract class Observer {
9
9
  private readonly indexes;
10
10
  private readonly attributesList;
11
11
  private readonly textSet;
12
- private readonly inUpperContext;
13
- constructor(app: App, context?: Window);
12
+ constructor(app: App, isTopContext?: boolean);
14
13
  private clear;
15
14
  private sendNodeAttribute;
16
15
  private sendNodeData;
@@ -34,16 +34,15 @@ function isObservable(node) {
34
34
  return !isIgnored(node);
35
35
  }
36
36
  class Observer {
37
- constructor(app, context = window) {
37
+ constructor(app, isTopContext = false) {
38
38
  this.app = app;
39
- this.context = context;
39
+ this.isTopContext = isTopContext;
40
40
  this.commited = [];
41
41
  this.recents = [];
42
42
  this.myNodes = [];
43
43
  this.indexes = [];
44
44
  this.attributesList = [];
45
45
  this.textSet = new Set();
46
- this.inUpperContext = context.parent === context; //TODO: get rid of context here
47
46
  this.observer = new MutationObserver(this.app.safe((mutations) => {
48
47
  for (const mutation of mutations) {
49
48
  const target = mutation.target;
@@ -186,7 +185,7 @@ class Observer {
186
185
  // Disable parent check for the upper context HTMLHtmlElement, because it is root there... (before)
187
186
  // TODO: get rid of "special" cases (there is an issue with CreateDocument altered behaviour though)
188
187
  // TODO: Clean the logic (though now it workd fine)
189
- if (!(0, context_js_1.isInstance)(node, HTMLHtmlElement) || !this.inUpperContext) {
188
+ if (!(0, context_js_1.isInstance)(node, HTMLHtmlElement) || !this.isTopContext) {
190
189
  if (parent === null) {
191
190
  this.unbindNode(node);
192
191
  return false;
@@ -274,6 +273,8 @@ class Observer {
274
273
  for (let id = 0; id < this.recents.length; id++) {
275
274
  // TODO: make things/logic nice here.
276
275
  // commit required in any case if recents[id] true or false (in case of unbinding) or undefined (in case of attr change).
276
+ // Possible solution: separate new node commit (recents) and new attribute/move node commit
277
+ // Otherwise commitNode is called on each node, which might be a lot
277
278
  if (!this.myNodes[id]) {
278
279
  continue;
279
280
  }
@@ -9,16 +9,17 @@ const utils_js_1 = require("../../utils.js");
9
9
  const attachShadowNativeFn = utils_js_1.IN_BROWSER ? Element.prototype.attachShadow : () => new ShadowRoot();
10
10
  class TopObserver extends observer_js_1.default {
11
11
  constructor(app, options) {
12
- super(app);
12
+ super(app, true);
13
13
  this.iframeObservers = [];
14
14
  this.shadowRootObservers = [];
15
15
  this.options = Object.assign({
16
- captureIFrames: false
16
+ captureIFrames: true
17
17
  }, options);
18
18
  // IFrames
19
19
  this.app.nodes.attachNodeCallback(node => {
20
20
  if ((0, context_js_1.isInstance)(node, HTMLIFrameElement) &&
21
- (this.options.captureIFrames || node.getAttribute("data-openreplay-capture"))) {
21
+ ((this.options.captureIFrames && !(0, utils_js_1.hasOpenreplayAttribute)(node, "obscured"))
22
+ || (0, utils_js_1.hasOpenreplayAttribute)(node, "capture"))) {
22
23
  this.handleIframe(node);
23
24
  }
24
25
  });
@@ -30,28 +31,28 @@ class TopObserver extends observer_js_1.default {
30
31
  });
31
32
  }
32
33
  handleIframe(iframe) {
33
- let context = null;
34
+ let doc = null;
34
35
  const handle = this.app.safe(() => {
35
36
  const id = this.app.nodes.getID(iframe);
36
37
  if (id === undefined) {
37
38
  return;
38
39
  } //log
39
- if (iframe.contentWindow === context) {
40
+ if (iframe.contentDocument === doc) {
40
41
  return;
41
- } //Does this happen frequently?
42
- context = iframe.contentWindow;
43
- if (!context) {
42
+ } // How frequently can it happen?
43
+ doc = iframe.contentDocument;
44
+ if (!doc || !iframe.contentWindow) {
44
45
  return;
45
46
  }
46
- const observer = new iframe_observer_js_1.default(this.app, context);
47
+ const observer = new iframe_observer_js_1.default(this.app);
47
48
  this.iframeObservers.push(observer);
48
49
  observer.observe(iframe);
49
50
  });
50
- this.app.attachEventListener(iframe, "load", handle);
51
+ iframe.addEventListener("load", handle); // why app.attachEventListener not working?
51
52
  handle();
52
53
  }
53
54
  handleShadowRoot(shRoot) {
54
- const observer = new shadow_root_observer_js_1.default(this.app, this.context);
55
+ const observer = new shadow_root_observer_js_1.default(this.app);
55
56
  this.shadowRootObservers.push(observer);
56
57
  observer.observe(shRoot.host);
57
58
  }
@@ -69,9 +70,9 @@ class TopObserver extends observer_js_1.default {
69
70
  // the change in the re-player behaviour caused by CreateDocument message:
70
71
  // the 0-node ("fRoot") will become #document rather than documentElement as it is now.
71
72
  // Alternatively - observe(#document) then bindNode(documentElement)
72
- this.observeRoot(this.context.document, () => {
73
+ this.observeRoot(window.document, () => {
73
74
  this.app.send(new index_js_1.CreateDocument());
74
- }, this.context.document.documentElement);
75
+ }, window.document.documentElement);
75
76
  }
76
77
  disconnect() {
77
78
  Element.prototype.attachShadow = attachShadowNativeFn;
@@ -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.2',
130
+ trackerVersion: '3.5.5-beta.0',
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) {
@@ -9,35 +9,58 @@ export function isInstance(node, constr) {
9
9
  // @ts-ignore (for EI, Safary)
10
10
  doc.parentWindow ||
11
11
  doc.defaultView; // TODO: smart global typing for Window object
12
- while (context.parent && context.parent !== context) {
12
+ while (context !== window) {
13
13
  // @ts-ignore
14
14
  if (node instanceof context[constr.name]) {
15
15
  return true;
16
16
  }
17
17
  // @ts-ignore
18
- context = context.parent;
18
+ context = context.parent || window;
19
19
  }
20
20
  // @ts-ignore
21
21
  return node instanceof context[constr.name];
22
22
  }
23
+ // TODO: ensure 1. it works in every cases (iframes/detached nodes) and 2. the most efficient
23
24
  export function inDocument(node) {
24
25
  const doc = node.ownerDocument;
25
26
  if (!doc) {
26
- return false;
27
- }
28
- if (doc.contains(node)) {
29
27
  return true;
30
- }
31
- let context =
32
- // @ts-ignore (for EI, Safary)
33
- doc.parentWindow ||
34
- doc.defaultView;
35
- while (context.parent && context.parent !== context) {
36
- if (context.document.contains(node)) {
28
+ } // Document
29
+ let current = node;
30
+ while (current) {
31
+ if (current === doc) {
37
32
  return true;
38
33
  }
39
- // @ts-ignore
40
- context = context.parent;
34
+ else if (isInstance(current, ShadowRoot)) {
35
+ current = current.host;
36
+ }
37
+ else {
38
+ current = current.parentNode;
39
+ }
41
40
  }
42
41
  return false;
43
42
  }
43
+ // export function inDocument(node: Node): boolean {
44
+ // // @ts-ignore compatability
45
+ // if (node.getRootNode) {
46
+ // let root: Node
47
+ // while ((root = node.getRootNode()) !== node) {
48
+ // ////
49
+ // }
50
+ // }
51
+ // const doc = node.ownerDocument
52
+ // if (!doc) { return false }
53
+ // if (doc.contains(node)) { return true }
54
+ // let context: Window =
55
+ // // @ts-ignore (for EI, Safary)
56
+ // doc.parentWindow ||
57
+ // doc.defaultView;
58
+ // while(context.parent && context.parent !== context) {
59
+ // if (context.document.contains(node)) {
60
+ // return true
61
+ // }
62
+ // // @ts-ignore
63
+ // context = context.parent
64
+ // }
65
+ // return false;
66
+ // }
@@ -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,9 +53,6 @@ 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?;
@@ -62,8 +61,8 @@ export default class App {
62
61
  private readonly preStartMessages;
63
62
  send(message: Message, urgent?: boolean): void;
64
63
  private commit;
65
- attachCommitCallback(cb: CommitCallback): void;
66
64
  safe<T extends (...args: any[]) => void>(fn: T): T;
65
+ attachCommitCallback(cb: CommitCallback): void;
67
66
  attachStartCallback(cb: Callback): void;
68
67
  attachStopCallback(cb: Callback): void;
69
68
  attachEventListener(target: EventTarget, type: string, listener: EventListener, useSafe?: boolean, useCapture?: boolean): void;
@@ -75,10 +74,10 @@ export default class App {
75
74
  revID: string;
76
75
  timestamp: number;
77
76
  trackerVersion: string;
78
- userID: string | null;
79
77
  isSnippet: boolean;
80
78
  sessionID: string | null;
81
79
  metadata: Record<string, string>;
80
+ userID: string | null;
82
81
  };
83
82
  getSessionToken(): string | undefined;
84
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,8 @@ 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.2'; // TODO: version compatability check inside each plugin.
29
+ this.version = '3.5.5-beta.0'; // TODO: version compatability check inside each plugin.
32
30
  this.preStartMessages = [];
33
31
  this.projectKey = projectKey;
34
32
  this.options = Object.assign({
@@ -55,25 +53,20 @@ export default class App {
55
53
  this.ticker.attach(() => this.commit());
56
54
  this.debug = new Logger(this.options.__debug__);
57
55
  this.notify = new Logger(this.options.verbose ? LogLevel.Warnings : LogLevel.Silent);
56
+ this.session = new Session(this);
58
57
  try {
59
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()};
60
59
  `], { type: 'text/javascript' })));
61
60
  this.worker.onerror = e => {
62
61
  this._debug("webworker_error", e);
63
62
  };
64
- let lastTs = timestamp();
65
- let fileno = 0;
66
63
  this.worker.onmessage = ({ data }) => {
67
64
  if (data === null) {
68
65
  this.stop();
69
66
  }
70
67
  else if (data === "restart") {
71
68
  this.stop();
72
- this.start({
73
- forceNew: true,
74
- userID: this._userID || undefined,
75
- metadata: this._metadata || undefined,
76
- });
69
+ this.start({ forceNew: true });
77
70
  }
78
71
  };
79
72
  const alertWorker = () => {
@@ -127,9 +120,6 @@ export default class App {
127
120
  this.messages.length = 0;
128
121
  }
129
122
  }
130
- attachCommitCallback(cb) {
131
- this.commitCallbacks.push(cb);
132
- }
133
123
  safe(fn) {
134
124
  const app = this;
135
125
  return function (...args) {
@@ -145,6 +135,9 @@ export default class App {
145
135
  }
146
136
  }; // TODO: correct typing
147
137
  }
138
+ attachCommitCallback(cb) {
139
+ this.commitCallbacks.push(cb);
140
+ }
148
141
  attachStartCallback(cb) {
149
142
  this.startCallbacks.push(cb);
150
143
  }
@@ -175,12 +168,11 @@ export default class App {
175
168
  revID: this.revID,
176
169
  timestamp: timestamp(),
177
170
  trackerVersion: this.version,
178
- userID: this._userID,
179
171
  isSnippet: this.options.__is_snippet,
180
172
  };
181
173
  }
182
174
  getSessionInfo() {
183
- return Object.assign({ sessionID: this._sessionID, metadata: this._metadata }, this.getStartInfo());
175
+ return Object.assign(Object.assign({}, this.session.getInfo()), this.getStartInfo());
184
176
  }
185
177
  getSessionToken() {
186
178
  const token = sessionStorage.getItem(this.options.session_token_key);
@@ -189,7 +181,7 @@ export default class App {
189
181
  }
190
182
  }
191
183
  getSessionID() {
192
- return this._sessionID || undefined;
184
+ return this.session.getInfo().sessionID || undefined;
193
185
  }
194
186
  getHost() {
195
187
  return new URL(this.options.ingestPoint).hostname;
@@ -246,8 +238,6 @@ export default class App {
246
238
  pageNo++;
247
239
  }
248
240
  sessionStorage.setItem(this.options.session_pageno_key, pageNo.toString());
249
- this._userID = startOpts.userID || null;
250
- this._metadata = startOpts.metadata || {}; // TODO: update both dynamically on corresponding messages
251
241
  const startInfo = this.getStartInfo();
252
242
  const messageData = {
253
243
  ingestPoint: this.options.ingestPoint,
@@ -264,7 +254,7 @@ export default class App {
264
254
  headers: {
265
255
  'Content-Type': 'application/json',
266
256
  },
267
- 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,
268
258
  jsHeapSizeLimit, reset: startOpts.forceNew || sReset !== null })),
269
259
  })
270
260
  .then(r => {
@@ -289,15 +279,12 @@ export default class App {
289
279
  }
290
280
  sessionStorage.setItem(this.options.session_token_key, token);
291
281
  localStorage.setItem(this.options.local_uuid_key, userUUID);
292
- if (typeof sessionID === 'string') {
293
- this._sessionID = sessionID;
294
- }
282
+ this.session.update(Object.assign({ sessionID }, startOpts));
295
283
  this.activityState = ActivityState.Active;
296
284
  this.worker.postMessage({ token, beaconSizeLimit });
297
285
  this.startCallbacks.forEach((cb) => cb());
298
286
  this.observer.observe();
299
287
  this.ticker.start();
300
- Object.entries(this._metadata).forEach(([key, value]) => this.send(new Metadata(key, value)));
301
288
  this.notify.log("OpenReplay tracking started.");
302
289
  // TODO: get rid of onStart
303
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
  }
@@ -1,7 +1,7 @@
1
1
  import App from "../index.js";
2
2
  export default abstract class Observer {
3
3
  protected readonly app: App;
4
- protected readonly context: Window;
4
+ protected readonly isTopContext: boolean;
5
5
  private readonly observer;
6
6
  private readonly commited;
7
7
  private readonly recents;
@@ -9,8 +9,7 @@ export default abstract class Observer {
9
9
  private readonly indexes;
10
10
  private readonly attributesList;
11
11
  private readonly textSet;
12
- private readonly inUpperContext;
13
- constructor(app: App, context?: Window);
12
+ constructor(app: App, isTopContext?: boolean);
14
13
  private clear;
15
14
  private sendNodeAttribute;
16
15
  private sendNodeData;
@@ -32,16 +32,15 @@ function isObservable(node) {
32
32
  return !isIgnored(node);
33
33
  }
34
34
  export default class Observer {
35
- constructor(app, context = window) {
35
+ constructor(app, isTopContext = false) {
36
36
  this.app = app;
37
- this.context = context;
37
+ this.isTopContext = isTopContext;
38
38
  this.commited = [];
39
39
  this.recents = [];
40
40
  this.myNodes = [];
41
41
  this.indexes = [];
42
42
  this.attributesList = [];
43
43
  this.textSet = new Set();
44
- this.inUpperContext = context.parent === context; //TODO: get rid of context here
45
44
  this.observer = new MutationObserver(this.app.safe((mutations) => {
46
45
  for (const mutation of mutations) {
47
46
  const target = mutation.target;
@@ -184,7 +183,7 @@ export default class Observer {
184
183
  // Disable parent check for the upper context HTMLHtmlElement, because it is root there... (before)
185
184
  // TODO: get rid of "special" cases (there is an issue with CreateDocument altered behaviour though)
186
185
  // TODO: Clean the logic (though now it workd fine)
187
- if (!isInstance(node, HTMLHtmlElement) || !this.inUpperContext) {
186
+ if (!isInstance(node, HTMLHtmlElement) || !this.isTopContext) {
188
187
  if (parent === null) {
189
188
  this.unbindNode(node);
190
189
  return false;
@@ -272,6 +271,8 @@ export default class Observer {
272
271
  for (let id = 0; id < this.recents.length; id++) {
273
272
  // TODO: make things/logic nice here.
274
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
275
276
  if (!this.myNodes[id]) {
276
277
  continue;
277
278
  }
@@ -3,20 +3,21 @@ import { isInstance } from "../context.js";
3
3
  import IFrameObserver from "./iframe_observer.js";
4
4
  import ShadowRootObserver from "./shadow_root_observer.js";
5
5
  import { CreateDocument } from "../../messages/index.js";
6
- import { IN_BROWSER } from '../../utils.js';
6
+ import { IN_BROWSER, hasOpenreplayAttribute } from '../../utils.js';
7
7
  const attachShadowNativeFn = IN_BROWSER ? Element.prototype.attachShadow : () => new ShadowRoot();
8
8
  export default class TopObserver extends Observer {
9
9
  constructor(app, options) {
10
- super(app);
10
+ super(app, true);
11
11
  this.iframeObservers = [];
12
12
  this.shadowRootObservers = [];
13
13
  this.options = Object.assign({
14
- captureIFrames: false
14
+ captureIFrames: true
15
15
  }, options);
16
16
  // IFrames
17
17
  this.app.nodes.attachNodeCallback(node => {
18
18
  if (isInstance(node, HTMLIFrameElement) &&
19
- (this.options.captureIFrames || node.getAttribute("data-openreplay-capture"))) {
19
+ ((this.options.captureIFrames && !hasOpenreplayAttribute(node, "obscured"))
20
+ || hasOpenreplayAttribute(node, "capture"))) {
20
21
  this.handleIframe(node);
21
22
  }
22
23
  });
@@ -28,28 +29,28 @@ export default class TopObserver extends Observer {
28
29
  });
29
30
  }
30
31
  handleIframe(iframe) {
31
- let context = null;
32
+ let doc = null;
32
33
  const handle = this.app.safe(() => {
33
34
  const id = this.app.nodes.getID(iframe);
34
35
  if (id === undefined) {
35
36
  return;
36
37
  } //log
37
- if (iframe.contentWindow === context) {
38
+ if (iframe.contentDocument === doc) {
38
39
  return;
39
- } //Does this happen frequently?
40
- context = iframe.contentWindow;
41
- if (!context) {
40
+ } // How frequently can it happen?
41
+ doc = iframe.contentDocument;
42
+ if (!doc || !iframe.contentWindow) {
42
43
  return;
43
44
  }
44
- const observer = new IFrameObserver(this.app, context);
45
+ const observer = new IFrameObserver(this.app);
45
46
  this.iframeObservers.push(observer);
46
47
  observer.observe(iframe);
47
48
  });
48
- this.app.attachEventListener(iframe, "load", handle);
49
+ iframe.addEventListener("load", handle); // why app.attachEventListener not working?
49
50
  handle();
50
51
  }
51
52
  handleShadowRoot(shRoot) {
52
- const observer = new ShadowRootObserver(this.app, this.context);
53
+ const observer = new ShadowRootObserver(this.app);
53
54
  this.shadowRootObservers.push(observer);
54
55
  observer.observe(shRoot.host);
55
56
  }
@@ -67,9 +68,9 @@ export default class TopObserver extends Observer {
67
68
  // the change in the re-player behaviour caused by CreateDocument message:
68
69
  // the 0-node ("fRoot") will become #document rather than documentElement as it is now.
69
70
  // Alternatively - observe(#document) then bindNode(documentElement)
70
- this.observeRoot(this.context.document, () => {
71
+ this.observeRoot(window.document, () => {
71
72
  this.app.send(new CreateDocument());
72
- }, this.context.document.documentElement);
73
+ }, window.document.documentElement);
73
74
  }
74
75
  disconnect() {
75
76
  Element.prototype.attachShadow = attachShadowNativeFn;
@@ -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.2',
126
+ trackerVersion: '3.5.5-beta.0',
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.2",
4
+ "version": "3.5.5-beta.0",
5
5
  "keywords": [
6
6
  "logging",
7
7
  "replay"