@openreplay/tracker 5.0.2 → 6.0.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +25 -1
  2. package/cjs/app/index.d.ts +3 -0
  3. package/cjs/app/index.js +4 -3
  4. package/cjs/app/messages.gen.d.ts +6 -1
  5. package/cjs/app/messages.gen.js +56 -5
  6. package/cjs/app/nodes.d.ts +1 -0
  7. package/cjs/app/nodes.js +3 -0
  8. package/cjs/app/observer/observer.js +7 -1
  9. package/cjs/app/observer/top_observer.js +4 -0
  10. package/cjs/app/ticker.d.ts +6 -0
  11. package/cjs/app/ticker.js +6 -0
  12. package/cjs/common/messages.gen.d.ts +46 -5
  13. package/cjs/index.d.ts +2 -0
  14. package/cjs/index.js +4 -2
  15. package/cjs/modules/img.js +1 -1
  16. package/cjs/modules/input.d.ts +2 -2
  17. package/cjs/modules/input.js +61 -39
  18. package/cjs/modules/mouse.d.ts +23 -1
  19. package/cjs/modules/mouse.js +43 -9
  20. package/cjs/modules/network.d.ts +1 -1
  21. package/cjs/modules/network.js +81 -10
  22. package/cjs/modules/selection.d.ts +7 -0
  23. package/cjs/modules/selection.js +37 -0
  24. package/cjs/modules/timing.js +3 -1
  25. package/cjs/utils.d.ts +2 -0
  26. package/cjs/utils.js +20 -1
  27. package/lib/app/index.d.ts +3 -0
  28. package/lib/app/index.js +4 -3
  29. package/lib/app/messages.gen.d.ts +6 -1
  30. package/lib/app/messages.gen.js +48 -2
  31. package/lib/app/nodes.d.ts +1 -0
  32. package/lib/app/nodes.js +3 -0
  33. package/lib/app/observer/observer.js +8 -2
  34. package/lib/app/observer/top_observer.js +5 -1
  35. package/lib/app/ticker.d.ts +6 -0
  36. package/lib/app/ticker.js +6 -0
  37. package/lib/common/messages.gen.d.ts +46 -5
  38. package/lib/common/tsconfig.tsbuildinfo +1 -1
  39. package/lib/index.d.ts +2 -0
  40. package/lib/index.js +4 -2
  41. package/lib/modules/img.js +1 -1
  42. package/lib/modules/input.d.ts +2 -2
  43. package/lib/modules/input.js +63 -41
  44. package/lib/modules/mouse.d.ts +23 -1
  45. package/lib/modules/mouse.js +45 -11
  46. package/lib/modules/network.d.ts +1 -1
  47. package/lib/modules/network.js +81 -10
  48. package/lib/modules/selection.d.ts +7 -0
  49. package/lib/modules/selection.js +35 -0
  50. package/lib/modules/timing.js +3 -1
  51. package/lib/utils.d.ts +2 -0
  52. package/lib/utils.js +17 -0
  53. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ # 6.0.1
2
+
3
+ - fix webworker writer re-init request
4
+ - remove useless logs
5
+ - tune mouse thrashing detection
6
+ - fix iframe handling
7
+
8
+ # 6.0.0
9
+
10
+ **(Compatible with OpenReplay v1.11.0+ only)**
11
+
12
+ - **[breaking]:** Capture mouse thrashing, input hesitation+duration, click hesitation
13
+ - Capture DOM node drop event (>30% nodes removed)
14
+ - Capture iframe network requests
15
+ - Detect cached requests to img, css and js resources; send transferred size
16
+ - added `{ mouse: { disableClickmaps: boolean } }` to disable calculating el. selectors
17
+ - added `{ mouse: { minSelectorDepth?: number; nthThreshold?: number; maxOptimiseTries?: number }` for selector finding optimizations
18
+
19
+ ## 5.0.2
20
+
21
+ - fixed inline css loading in specific cases when assets gets around min flush size
22
+
1
23
  ## 5.0.1
2
24
 
3
25
  - Re-init worker after device sleep/hybernation
@@ -6,12 +28,14 @@
6
28
 
7
29
  ## 5.0.0
8
30
 
31
+ **(Compatible with OpenReplay v1.10.0+ only)**
32
+
33
+ - **[breaking]:** string dictionary to reduce session size
9
34
  - Added "tel" to supported input types
10
35
  - Added `{ withCurrentTime: true }` to `tracker.getSessionURL` method which will return sessionURL with current session's timestamp
11
36
  - Added Network module that captures fetch/xhr by default (with no plugin required)
12
37
  - Use `timeOrigin()` instead of `performance.timing.navigationStart` in ResourceTiming messages
13
38
  - Added app restart when service worker died after inactivity (mobile safari)
14
- - **[breaking]** string dictionary to reduce session size
15
39
 
16
40
  ## 4.1.8
17
41
 
@@ -9,6 +9,7 @@ import type { Options as ObserverOptions } from './observer/top_observer.js';
9
9
  import type { Options as SanitizerOptions } from './sanitizer.js';
10
10
  import type { Options as LoggerOptions } from './logger.js';
11
11
  import type { Options as SessOptions } from './session.js';
12
+ import type { Options as NetworkOptions } from '../modules/network.js';
12
13
  import type { Options as WebworkerOptions } from '../common/interaction.js';
13
14
  export interface StartOptions {
14
15
  userID?: string;
@@ -50,6 +51,7 @@ type AppOptions = {
50
51
  localStorage: Storage | null;
51
52
  sessionStorage: Storage | null;
52
53
  onStart?: StartCallback;
54
+ network?: NetworkOptions;
53
55
  } & WebworkerOptions & SessOptions;
54
56
  export type Options = AppOptions & ObserverOptions & SanitizerOptions;
55
57
  export declare const DEFAULT_INGEST_POINT = "https://api.openreplay.com/ingest";
@@ -69,6 +71,7 @@ export default class App {
69
71
  private readonly stopCallbacks;
70
72
  private readonly commitCallbacks;
71
73
  private readonly options;
74
+ readonly networkOptions?: NetworkOptions;
72
75
  private readonly revID;
73
76
  private activityState;
74
77
  private readonly version;
package/cjs/app/index.js CHANGED
@@ -33,10 +33,11 @@ class App {
33
33
  this.stopCallbacks = [];
34
34
  this.commitCallbacks = [];
35
35
  this.activityState = ActivityState.NotActive;
36
- this.version = '5.0.2'; // TODO: version compatability check inside each plugin.
36
+ this.version = '6.0.1-beta.1'; // TODO: version compatability check inside each plugin.
37
37
  this._usingOldFetchPlugin = false;
38
38
  this.delay = 0;
39
39
  this.projectKey = projectKey;
40
+ this.networkOptions = options.network;
40
41
  this.options = Object.assign({
41
42
  revID: '',
42
43
  node_id: '__openreplay_id',
@@ -72,12 +73,12 @@ class App {
72
73
  Object.entries(metadata).forEach(([key, value]) => this.send((0, messages_gen_js_1.Metadata)(key, value)));
73
74
  }
74
75
  });
75
- // @depricated (use sessionHash on start instead)
76
+ // @deprecated (use sessionHash on start instead)
76
77
  if (sessionToken != null) {
77
78
  this.session.applySessionHash(sessionToken);
78
79
  }
79
80
  try {
80
- this.worker = new Worker(URL.createObjectURL(new Blob(['"use strict";class t{constructor(t,i,s,e=10,n=1e3){this.onUnauthorised=i,this.onFailure=s,this.MAX_ATTEMPTS_COUNT=e,this.ATTEMPT_TIMEOUT=n,this.attemptsCount=0,this.busy=!1,this.queue=[],this.token=null,this.ingestURL=t+"/v1/web/i"}authorise(t){this.token=t,this.busy||this.sendNext()}push(t){this.busy||!this.token?this.queue.push(t):this.sendBatch(t)}sendNext(){const t=this.queue.shift();t?this.sendBatch(t):this.busy=!1}retry(t){this.attemptsCount>=this.MAX_ATTEMPTS_COUNT?this.onFailure(`Failed to send batch after ${this.attemptsCount} attempts.`):(this.attemptsCount++,setTimeout(()=>this.sendBatch(t),this.ATTEMPT_TIMEOUT*this.attemptsCount))}sendBatch(t){this.busy=!0,fetch(this.ingestURL,{body:t,method:"POST",headers:{Authorization:"Bearer "+this.token},keepalive:t.length<65536}).then(i=>{if(401===i.status)return this.busy=!1,void this.onUnauthorised();i.status>=400?this.retry(t):(this.attemptsCount=0,this.sendNext())}).catch(i=>{console.warn("OpenReplay:",i),this.retry(t)})}clean(){this.queue.length=0,this.token=null}}const i="function"==typeof TextEncoder?new TextEncoder:{encode(t){const i=t.length,s=new Uint8Array(3*i);let e=-1;for(let n=0,r=0,h=0;h!==i;){if(n=t.charCodeAt(h),h+=1,n>=55296&&n<=56319){if(h===i){s[e+=1]=239,s[e+=1]=191,s[e+=1]=189;break}if(r=t.charCodeAt(h),!(r>=56320&&r<=57343)){s[e+=1]=239,s[e+=1]=191,s[e+=1]=189;continue}if(n=1024*(n-55296)+r-56320+65536,h+=1,n>65535){s[e+=1]=240|n>>>18,s[e+=1]=128|n>>>12&63,s[e+=1]=128|n>>>6&63,s[e+=1]=128|63&n;continue}}n<=127?s[e+=1]=0|n:n<=2047?(s[e+=1]=192|n>>>6,s[e+=1]=128|63&n):(s[e+=1]=224|n>>>12,s[e+=1]=128|n>>>6&63,s[e+=1]=128|63&n)}return s.subarray(0,e+1)}};class s extends class{constructor(t){this.size=t,this.offset=0,this.checkpointOffset=0,this.data=new Uint8Array(t)}getCurrentOffset(){return this.offset}checkpoint(){this.checkpointOffset=this.offset}get isEmpty(){return 0===this.offset}skip(t){return this.offset+=t,this.offset<=this.size}set(t,i){this.data.set(t,i)}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=i.encode(t),e=s.byteLength;return!(!this.uint(e)||this.offset+e>this.size)&&(this.data.set(s,this.offset),this.offset+=e,!0)}reset(){this.offset=0,this.checkpointOffset=0}flush(){const t=this.data.slice(0,this.checkpointOffset);return this.reset(),t}}{encode(t){switch(t[0]){case 0:return this.uint(t[1]);case 4:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3]);case 5:return this.uint(t[1])&&this.uint(t[2]);case 6:return this.int(t[1])&&this.int(t[2]);case 7:return!0;case 8:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.string(t[4])&&this.boolean(t[5]);case 9:case 10:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3]);case 11:return this.uint(t[1]);case 12:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3]);case 13:case 14:return this.uint(t[1])&&this.string(t[2]);case 16:return this.uint(t[1])&&this.int(t[2])&&this.int(t[3]);case 17:return this.uint(t[1])&&this.string(t[2]);case 18:return this.uint(t[1])&&this.string(t[2])&&this.int(t[3]);case 19:return this.uint(t[1])&&this.boolean(t[2]);case 20:return this.uint(t[1])&&this.uint(t[2]);case 21:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.string(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8]);case 22:return this.string(t[1])&&this.string(t[2]);case 23:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8])&&this.uint(t[9]);case 24:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3]);case 27:return this.string(t[1])&&this.string(t[2]);case 28:case 29:return this.string(t[1]);case 30:return this.string(t[1])&&this.string(t[2]);case 37:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3]);case 38:return this.uint(t[1])&&this.uint(t[2]);case 39:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7]);case 40:return this.string(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 41:return this.string(t[1])&&this.string(t[2]);case 42:return this.string(t[1]);case 44:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3]);case 45:case 46:return this.string(t[1])&&this.string(t[2]);case 47:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3]);case 48:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 49:return this.int(t[1])&&this.int(t[2])&&this.uint(t[3])&&this.uint(t[4]);case 50:return this.uint(t[1])&&this.string(t[2]);case 51:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3]);case 53:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8]);case 54:return this.uint(t[1])&&this.string(t[2]);case 55:return this.boolean(t[1]);case 57:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 58:return this.int(t[1]);case 59:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.string(t[5])&&this.string(t[6])&&this.string(t[7]);case 60:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 61:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3]);case 63:case 64:return this.string(t[1])&&this.string(t[2]);case 67:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);case 69:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 70:return this.uint(t[1])&&this.uint(t[2]);case 71:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3]);case 73:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);case 75:case 76:case 77:return this.uint(t[1])&&this.uint(t[2]);case 78:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 79:return this.string(t[1])&&this.string(t[2]);case 81:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.int(t[4])&&this.string(t[5]);case 82:return this.uint(t[1])&&this.uint(t[2])}}}class e{constructor(){this.idx=1,this.backDict={}}getKey(t){let i=!1;return this.backDict[t]||(i=!0,this.backDict[t]=this.idx++),[this.backDict[t],i]}}class n{constructor(t,i,n,r){this.pageNo=t,this.timestamp=i,this.url=n,this.onBatch=r,this.nextIndex=0,this.beaconSize=2e5,this.encoder=new s(this.beaconSize),this.strDict=new e,this.sizeBuffer=new Uint8Array(3),this.isEmpty=!0,this.beaconSizeLimit=1e6,this.prepare()}writeType(t){return this.encoder.uint(t[0])}writeFields(t){return this.encoder.encode(t)}writeSizeAt(t,i){for(let i=0;i<3;i++)this.sizeBuffer[i]=t>>8*i;this.encoder.set(this.sizeBuffer,i)}prepare(){if(!this.encoder.isEmpty)return;const t=[81,1,this.pageNo,this.nextIndex,this.timestamp,this.url];this.writeType(t),this.writeFields(t),this.isEmpty=!0}writeWithSize(t){const i=this.encoder;if(!this.writeType(t)||!i.skip(3))return!1;const s=i.getCurrentOffset(),e=this.writeFields(t);if(e){const e=i.getCurrentOffset()-s;if(e>16777215)return console.warn("OpenReplay: max message size overflow."),!1;this.writeSizeAt(e,s-3),i.checkpoint(),this.isEmpty=this.isEmpty&&0===t[0],this.nextIndex++}return e}setBeaconSizeLimit(t){this.beaconSizeLimit=t}applyDict(t){const[i,s]=this.strDict.getKey(t);return s&&this.writeMessage([50,i,t]),i}writeMessage(t){0===t[0]&&(this.timestamp=t[1]),4===t[0]&&(this.url=t[1]),12===t[0]&&(t=[51,t[1],this.applyDict(t[2]),this.applyDict(t[3])]),this.writeWithSize(t)||(this.finaliseBatch(),this.writeWithSize(t)||(this.encoder=new s(this.beaconSizeLimit),this.prepare(),this.writeWithSize(t)?this.finaliseBatch():console.warn("OpenReplay: beacon size overflow. Skipping large message.",t,this),this.encoder=new s(this.beaconSize),this.prepare()))}finaliseBatch(){this.isEmpty||(this.onBatch(this.encoder.flush()),this.prepare())}clean(){this.encoder.reset()}}var r;!function(t){t[t.NotActive=0]="NotActive",t[t.Starting=1]="Starting",t[t.Stopping=2]="Stopping",t[t.Active=3]="Active"}(r||(r={}));let h=null,u=null;r.NotActive;let a=0;function c(){u&&u.finaliseBatch()}function o(){r.Stopping,null!==f&&(clearInterval(f),f=null),u&&(u.clean(),u=null),h&&(h.clean(),h=null),r.NotActive}function g(){postMessage("restart"),o()}let l,f=null;self.onmessage=({data:i})=>{if(null!=i){if("stop"===i)return c(),void o();if(!Array.isArray(i))return"start"===i.type?(r.Starting,h=new t(i.ingestPoint,()=>{g()},t=>{!function(t){postMessage({type:"failure",reason:t}),o()}(t)},i.connAttemptCount,i.connAttemptGap),u=new n(i.pageNo,i.timestamp,i.url,t=>h&&h.push(t)),null===f&&(f=setInterval(c,1e4)),r.Active):"auth"===i.type?h?u?(h.authorise(i.token),void(i.beaconSizeLimit&&u.setBeaconSizeLimit(i.beaconSizeLimit))):(console.debug("WebWorker: writer not initialised. Received auth."),void g()):(console.debug("WebWorker: sender not initialised. Received auth."),void g()):void 0;if(null!==u){const t=u;i.forEach(i=>{55===i[0]&&(i[1]?l=setTimeout(()=>g(),18e5):clearTimeout(l)),t.writeMessage(i)})}u||(postMessage("not_init"),0===a&&(a+=1,g()))}else c()};'], { type: 'text/javascript' })));
81
+ this.worker = new Worker(URL.createObjectURL(new Blob(['"use strict";class t{constructor(t,i,s,e=10,n=1e3){this.onUnauthorised=i,this.onFailure=s,this.MAX_ATTEMPTS_COUNT=e,this.ATTEMPT_TIMEOUT=n,this.attemptsCount=0,this.busy=!1,this.queue=[],this.token=null,this.ingestURL=t+"/v1/web/i"}authorise(t){this.token=t,this.busy||this.sendNext()}push(t){this.busy||!this.token?this.queue.push(t):this.sendBatch(t)}sendNext(){const t=this.queue.shift();t?this.sendBatch(t):this.busy=!1}retry(t){this.attemptsCount>=this.MAX_ATTEMPTS_COUNT?this.onFailure(`Failed to send batch after ${this.attemptsCount} attempts.`):(this.attemptsCount++,setTimeout(()=>this.sendBatch(t),this.ATTEMPT_TIMEOUT*this.attemptsCount))}sendBatch(t){this.busy=!0,fetch(this.ingestURL,{body:t,method:"POST",headers:{Authorization:"Bearer "+this.token},keepalive:t.length<65536}).then(i=>{if(401===i.status)return this.busy=!1,void this.onUnauthorised();i.status>=400?this.retry(t):(this.attemptsCount=0,this.sendNext())}).catch(i=>{console.warn("OpenReplay:",i),this.retry(t)})}clean(){this.queue.length=0,this.token=null}}const i="function"==typeof TextEncoder?new TextEncoder:{encode(t){const i=t.length,s=new Uint8Array(3*i);let e=-1;for(let n=0,h=0,r=0;r!==i;){if(n=t.charCodeAt(r),r+=1,n>=55296&&n<=56319){if(r===i){s[e+=1]=239,s[e+=1]=191,s[e+=1]=189;break}if(h=t.charCodeAt(r),!(h>=56320&&h<=57343)){s[e+=1]=239,s[e+=1]=191,s[e+=1]=189;continue}if(n=1024*(n-55296)+h-56320+65536,r+=1,n>65535){s[e+=1]=240|n>>>18,s[e+=1]=128|n>>>12&63,s[e+=1]=128|n>>>6&63,s[e+=1]=128|63&n;continue}}n<=127?s[e+=1]=0|n:n<=2047?(s[e+=1]=192|n>>>6,s[e+=1]=128|63&n):(s[e+=1]=224|n>>>12,s[e+=1]=128|n>>>6&63,s[e+=1]=128|63&n)}return s.subarray(0,e+1)}};class s extends class{constructor(t){this.size=t,this.offset=0,this.checkpointOffset=0,this.data=new Uint8Array(t)}getCurrentOffset(){return this.offset}checkpoint(){this.checkpointOffset=this.offset}get isEmpty(){return 0===this.offset}skip(t){return this.offset+=t,this.offset<=this.size}set(t,i){this.data.set(t,i)}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=i.encode(t),e=s.byteLength;return!(!this.uint(e)||this.offset+e>this.size)&&(this.data.set(s,this.offset),this.offset+=e,!0)}reset(){this.offset=0,this.checkpointOffset=0}flush(){const t=this.data.slice(0,this.checkpointOffset);return this.reset(),t}}{encode(t){switch(t[0]){case 0:return this.uint(t[1]);case 4:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3]);case 5:return this.uint(t[1])&&this.uint(t[2]);case 6:return this.int(t[1])&&this.int(t[2]);case 7:return!0;case 8:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.string(t[4])&&this.boolean(t[5]);case 9:case 10:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3]);case 11:return this.uint(t[1]);case 12:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3]);case 13:case 14:return this.uint(t[1])&&this.string(t[2]);case 16:return this.uint(t[1])&&this.int(t[2])&&this.int(t[3]);case 17:return this.uint(t[1])&&this.string(t[2]);case 18:return this.uint(t[1])&&this.string(t[2])&&this.int(t[3]);case 19:return this.uint(t[1])&&this.boolean(t[2]);case 20:return this.uint(t[1])&&this.uint(t[2]);case 21:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.string(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8]);case 22:return this.string(t[1])&&this.string(t[2]);case 23:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8])&&this.uint(t[9]);case 24:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3]);case 27:return this.string(t[1])&&this.string(t[2]);case 28:case 29:return this.string(t[1]);case 30:return this.string(t[1])&&this.string(t[2]);case 37:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3]);case 38:return this.uint(t[1])&&this.uint(t[2]);case 39:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7]);case 40:return this.string(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 41:return this.string(t[1])&&this.string(t[2]);case 42:return this.string(t[1]);case 44:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3]);case 45:case 46:return this.string(t[1])&&this.string(t[2]);case 47:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3]);case 48:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 49:return this.int(t[1])&&this.int(t[2])&&this.uint(t[3])&&this.uint(t[4]);case 50:return this.uint(t[1])&&this.string(t[2]);case 51:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3]);case 53:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8]);case 54:return this.uint(t[1])&&this.string(t[2]);case 55:return this.boolean(t[1]);case 57:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 58:return this.int(t[1]);case 59:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.string(t[5])&&this.string(t[6])&&this.string(t[7]);case 60:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 61:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3]);case 63:case 64:return this.string(t[1])&&this.string(t[2]);case 67:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);case 69:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 70:return this.uint(t[1])&&this.uint(t[2]);case 71:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3]);case 73:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);case 75:case 76:case 77:return this.uint(t[1])&&this.uint(t[2]);case 78:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 79:return this.string(t[1])&&this.string(t[2]);case 81:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.int(t[4])&&this.string(t[5]);case 82:return this.uint(t[1])&&this.uint(t[2]);case 112:return this.uint(t[1])&&this.string(t[2])&&this.boolean(t[3])&&this.string(t[4])&&this.int(t[5])&&this.int(t[6]);case 113:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3]);case 114:case 115:return this.uint(t[1]);case 116:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8])&&this.uint(t[9])&&this.boolean(t[10])}}}class e{constructor(){this.idx=1,this.backDict={}}getKey(t){let i=!1;return this.backDict[t]||(i=!0,this.backDict[t]=this.idx++),[this.backDict[t],i]}}class n{constructor(t,i,n,h){this.pageNo=t,this.timestamp=i,this.url=n,this.onBatch=h,this.nextIndex=0,this.beaconSize=2e5,this.encoder=new s(this.beaconSize),this.strDict=new e,this.sizeBuffer=new Uint8Array(3),this.isEmpty=!0,this.beaconSizeLimit=1e6,this.prepare()}writeType(t){return this.encoder.uint(t[0])}writeFields(t){return this.encoder.encode(t)}writeSizeAt(t,i){for(let i=0;i<3;i++)this.sizeBuffer[i]=t>>8*i;this.encoder.set(this.sizeBuffer,i)}prepare(){if(!this.encoder.isEmpty)return;const t=[81,1,this.pageNo,this.nextIndex,this.timestamp,this.url];this.writeType(t),this.writeFields(t),this.isEmpty=!0}writeWithSize(t){const i=this.encoder;if(!this.writeType(t)||!i.skip(3))return!1;const s=i.getCurrentOffset(),e=this.writeFields(t);if(e){const e=i.getCurrentOffset()-s;if(e>16777215)return console.warn("OpenReplay: max message size overflow."),!1;this.writeSizeAt(e,s-3),i.checkpoint(),this.isEmpty=this.isEmpty&&0===t[0],this.nextIndex++}return e}setBeaconSizeLimit(t){this.beaconSizeLimit=t}applyDict(t){const[i,s]=this.strDict.getKey(t);return s&&this.writeMessage([50,i,t]),i}writeMessage(t){0===t[0]&&(this.timestamp=t[1]),4===t[0]&&(this.url=t[1]),12===t[0]&&(t=[51,t[1],this.applyDict(t[2]),this.applyDict(t[3])]),this.writeWithSize(t)||(this.finaliseBatch(),this.writeWithSize(t)||(this.encoder=new s(this.beaconSizeLimit),this.prepare(),this.writeWithSize(t)?this.finaliseBatch():console.warn("OpenReplay: beacon size overflow. Skipping large message.",t,this),this.encoder=new s(this.beaconSize),this.prepare()))}finaliseBatch(){this.isEmpty||(this.onBatch(this.encoder.flush()),this.prepare())}clean(){this.encoder.reset()}}var h;!function(t){t[t.NotActive=0]="NotActive",t[t.Starting=1]="Starting",t[t.Stopping=2]="Stopping",t[t.Active=3]="Active"}(h||(h={}));let r=null,u=null;function a(){u&&u.finaliseBatch()}function c(){h.Stopping,null!==l&&(clearInterval(l),l=null),u&&(u.clean(),u=null),r&&(r.clean(),r=null),h.NotActive}function o(){postMessage("restart"),c()}h.NotActive;let g,l=null;self.onmessage=({data:i})=>{if(null!=i){if("stop"===i)return a(),void c();if(!Array.isArray(i))return"start"===i.type?(h.Starting,r=new t(i.ingestPoint,()=>{o()},t=>{!function(t){postMessage({type:"failure",reason:t}),c()}(t)},i.connAttemptCount,i.connAttemptGap),u=new n(i.pageNo,i.timestamp,i.url,t=>r&&r.push(t)),null===l&&(l=setInterval(a,1e4)),h.Active):"auth"===i.type?r?u?(r.authorise(i.token),void(i.beaconSizeLimit&&u.setBeaconSizeLimit(i.beaconSizeLimit))):(console.debug("WebWorker: writer not initialised. Received auth."),void o()):(console.debug("WebWorker: sender not initialised. Received auth."),void o()):void 0;if(null!==u){const t=u;i.forEach(i=>{55===i[0]&&(i[1]?g=setTimeout(()=>o(),18e5):clearTimeout(g)),t.writeMessage(i)})}u||(postMessage("not_init"),o())}else a()};'], { type: 'text/javascript' })));
81
82
  this.worker.onerror = (e) => {
82
83
  this._debug('webworker_error', e);
83
84
  };
@@ -39,7 +39,7 @@ export declare function GraphQL(operationKind: string, operationName: string, va
39
39
  export declare function PerformanceTrack(frames: number, ticks: number, totalJSHeapSize: number, usedJSHeapSize: number): Messages.PerformanceTrack;
40
40
  export declare function StringDict(key: number, value: string): Messages.StringDict;
41
41
  export declare function SetNodeAttributeDict(id: number, nameKey: number, valueKey: number): Messages.SetNodeAttributeDict;
42
- export declare function ResourceTiming(timestamp: number, duration: number, ttfb: number, headerSize: number, encodedBodySize: number, decodedBodySize: number, url: string, initiator: string): Messages.ResourceTiming;
42
+ export declare function ResourceTimingDeprecated(timestamp: number, duration: number, ttfb: number, headerSize: number, encodedBodySize: number, decodedBodySize: number, url: string, initiator: string): Messages.ResourceTimingDeprecated;
43
43
  export declare function ConnectionInformation(downlink: number, type: string): Messages.ConnectionInformation;
44
44
  export declare function SetPageVisibility(hidden: boolean): Messages.SetPageVisibility;
45
45
  export declare function LoadFontFace(parentID: number, family: string, source: string, descriptors: string): Messages.LoadFontFace;
@@ -61,3 +61,8 @@ export declare function JSException(name: string, message: string, payload: stri
61
61
  export declare function Zustand(mutation: string, state: string): Messages.Zustand;
62
62
  export declare function BatchMetadata(version: number, pageNo: number, firstIndex: number, timestamp: number, location: string): Messages.BatchMetadata;
63
63
  export declare function PartitionedMessage(partNo: number, partTotal: number): Messages.PartitionedMessage;
64
+ export declare function InputChange(id: number, value: string, valueMasked: boolean, label: string, hesitationTime: number, inputDuration: number): Messages.InputChange;
65
+ export declare function SelectionChange(selectionStart: number, selectionEnd: number, selection: string): Messages.SelectionChange;
66
+ export declare function MouseThrashing(timestamp: number): Messages.MouseThrashing;
67
+ export declare function UnbindNodes(totalRemovedPercent: number): Messages.UnbindNodes;
68
+ export declare function ResourceTiming(timestamp: number, duration: number, ttfb: number, headerSize: number, encodedBodySize: number, decodedBodySize: number, url: string, initiator: string, transferredSize: number, cached: boolean): Messages.ResourceTiming;
@@ -2,8 +2,8 @@
2
2
  // Auto-generated, do not edit
3
3
  /* eslint-disable */
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.CSSInsertRuleURLBased = exports.CustomIssue = exports.TechnicalInfo = exports.SetCSSDataURLBased = exports.SetNodeAttributeURLBased = exports.LongTask = exports.SetNodeFocus = exports.LoadFontFace = exports.SetPageVisibility = exports.ConnectionInformation = exports.ResourceTiming = exports.SetNodeAttributeDict = exports.StringDict = exports.PerformanceTrack = exports.GraphQL = exports.NgRx = exports.MobX = exports.Vuex = exports.Redux = exports.StateAction = exports.OTable = exports.Profiler = exports.Fetch = exports.CSSDeleteRule = exports.CSSInsertRule = exports.Metadata = exports.UserAnonymousID = exports.UserID = exports.CustomEvent = exports.PageRenderTiming = exports.PageLoadTiming = exports.ConsoleLog = exports.NetworkRequest = exports.MouseMove = exports.SetInputChecked = exports.SetInputValue = exports.SetInputTarget = exports.SetNodeScroll = exports.SetNodeData = exports.RemoveNodeAttribute = exports.SetNodeAttribute = exports.RemoveNode = exports.MoveNode = exports.CreateTextNode = exports.CreateElementNode = exports.CreateDocument = exports.SetViewportScroll = exports.SetViewportSize = exports.SetPageLocation = exports.Timestamp = void 0;
6
- exports.PartitionedMessage = exports.BatchMetadata = exports.Zustand = exports.JSException = exports.AdoptedSSRemoveOwner = exports.AdoptedSSAddOwner = exports.AdoptedSSDeleteRule = exports.AdoptedSSInsertRuleURLBased = exports.AdoptedSSReplaceURLBased = exports.CreateIFrameDocument = exports.MouseClick = void 0;
5
+ exports.CSSInsertRuleURLBased = exports.CustomIssue = exports.TechnicalInfo = exports.SetCSSDataURLBased = exports.SetNodeAttributeURLBased = exports.LongTask = exports.SetNodeFocus = exports.LoadFontFace = exports.SetPageVisibility = exports.ConnectionInformation = exports.ResourceTimingDeprecated = exports.SetNodeAttributeDict = exports.StringDict = exports.PerformanceTrack = exports.GraphQL = exports.NgRx = exports.MobX = exports.Vuex = exports.Redux = exports.StateAction = exports.OTable = exports.Profiler = exports.Fetch = exports.CSSDeleteRule = exports.CSSInsertRule = exports.Metadata = exports.UserAnonymousID = exports.UserID = exports.CustomEvent = exports.PageRenderTiming = exports.PageLoadTiming = exports.ConsoleLog = exports.NetworkRequest = exports.MouseMove = exports.SetInputChecked = exports.SetInputValue = exports.SetInputTarget = exports.SetNodeScroll = exports.SetNodeData = exports.RemoveNodeAttribute = exports.SetNodeAttribute = exports.RemoveNode = exports.MoveNode = exports.CreateTextNode = exports.CreateElementNode = exports.CreateDocument = exports.SetViewportScroll = exports.SetViewportSize = exports.SetPageLocation = exports.Timestamp = void 0;
6
+ exports.ResourceTiming = exports.UnbindNodes = exports.MouseThrashing = exports.SelectionChange = exports.InputChange = exports.PartitionedMessage = exports.BatchMetadata = exports.Zustand = exports.JSException = exports.AdoptedSSRemoveOwner = exports.AdoptedSSAddOwner = exports.AdoptedSSDeleteRule = exports.AdoptedSSInsertRuleURLBased = exports.AdoptedSSReplaceURLBased = exports.CreateIFrameDocument = exports.MouseClick = void 0;
7
7
  function Timestamp(timestamp) {
8
8
  return [
9
9
  0 /* Messages.Type.Timestamp */,
@@ -347,9 +347,9 @@ function SetNodeAttributeDict(id, nameKey, valueKey) {
347
347
  ];
348
348
  }
349
349
  exports.SetNodeAttributeDict = SetNodeAttributeDict;
350
- function ResourceTiming(timestamp, duration, ttfb, headerSize, encodedBodySize, decodedBodySize, url, initiator) {
350
+ function ResourceTimingDeprecated(timestamp, duration, ttfb, headerSize, encodedBodySize, decodedBodySize, url, initiator) {
351
351
  return [
352
- 53 /* Messages.Type.ResourceTiming */,
352
+ 53 /* Messages.Type.ResourceTimingDeprecated */,
353
353
  timestamp,
354
354
  duration,
355
355
  ttfb,
@@ -360,7 +360,7 @@ function ResourceTiming(timestamp, duration, ttfb, headerSize, encodedBodySize,
360
360
  initiator,
361
361
  ];
362
362
  }
363
- exports.ResourceTiming = ResourceTiming;
363
+ exports.ResourceTimingDeprecated = ResourceTimingDeprecated;
364
364
  function ConnectionInformation(downlink, type) {
365
365
  return [
366
366
  54 /* Messages.Type.ConnectionInformation */,
@@ -549,3 +549,54 @@ function PartitionedMessage(partNo, partTotal) {
549
549
  ];
550
550
  }
551
551
  exports.PartitionedMessage = PartitionedMessage;
552
+ function InputChange(id, value, valueMasked, label, hesitationTime, inputDuration) {
553
+ return [
554
+ 112 /* Messages.Type.InputChange */,
555
+ id,
556
+ value,
557
+ valueMasked,
558
+ label,
559
+ hesitationTime,
560
+ inputDuration,
561
+ ];
562
+ }
563
+ exports.InputChange = InputChange;
564
+ function SelectionChange(selectionStart, selectionEnd, selection) {
565
+ return [
566
+ 113 /* Messages.Type.SelectionChange */,
567
+ selectionStart,
568
+ selectionEnd,
569
+ selection,
570
+ ];
571
+ }
572
+ exports.SelectionChange = SelectionChange;
573
+ function MouseThrashing(timestamp) {
574
+ return [
575
+ 114 /* Messages.Type.MouseThrashing */,
576
+ timestamp,
577
+ ];
578
+ }
579
+ exports.MouseThrashing = MouseThrashing;
580
+ function UnbindNodes(totalRemovedPercent) {
581
+ return [
582
+ 115 /* Messages.Type.UnbindNodes */,
583
+ totalRemovedPercent,
584
+ ];
585
+ }
586
+ exports.UnbindNodes = UnbindNodes;
587
+ function ResourceTiming(timestamp, duration, ttfb, headerSize, encodedBodySize, decodedBodySize, url, initiator, transferredSize, cached) {
588
+ return [
589
+ 116 /* Messages.Type.ResourceTiming */,
590
+ timestamp,
591
+ duration,
592
+ ttfb,
593
+ headerSize,
594
+ encodedBodySize,
595
+ decodedBodySize,
596
+ url,
597
+ initiator,
598
+ transferredSize,
599
+ cached,
600
+ ];
601
+ }
602
+ exports.ResourceTiming = ResourceTiming;
@@ -13,6 +13,7 @@ export default class Nodes {
13
13
  callNodeCallbacks(node: Node, isStart: boolean): void;
14
14
  getID(node: Node): number | undefined;
15
15
  getNode(id: number): void | Node;
16
+ getNodeCount(): number;
16
17
  clear(): void;
17
18
  }
18
19
  export {};
package/cjs/app/nodes.js CHANGED
@@ -68,6 +68,9 @@ class Nodes {
68
68
  getNode(id) {
69
69
  return this.nodes[id];
70
70
  }
71
+ getNodeCount() {
72
+ return this.nodes.filter(Boolean).length;
73
+ }
71
74
  clear() {
72
75
  for (let id = 0; id < this.nodes.length; id++) {
73
76
  const node = this.nodes[id];
@@ -193,10 +193,16 @@ class Observer {
193
193
  },
194
194
  // @ts-ignore
195
195
  false);
196
+ let removed = 0;
197
+ const totalBeforeRemove = this.app.nodes.getNodeCount();
196
198
  while (walker.nextNode()) {
199
+ removed += 1;
197
200
  this.app.nodes.unregisterNode(walker.currentNode);
198
201
  }
199
- // MBTODO: count and send RemovedNodesCount (for the page crash detection in heuristics)
202
+ const removedPercent = Math.floor((removed / totalBeforeRemove) * 100);
203
+ if (removedPercent > 30) {
204
+ this.app.send((0, messages_gen_js_1.UnbindNodes)(removedPercent));
205
+ }
200
206
  }
201
207
  }
202
208
  // A top-consumption function on the infinite lists test. (~1% of performance resources)
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const observer_js_1 = require("./observer.js");
4
4
  const guards_js_1 = require("../guards.js");
5
+ const network_js_1 = require("../../modules/network.js");
5
6
  const iframe_observer_js_1 = require("./iframe_observer.js");
6
7
  const shadow_root_observer_js_1 = require("./shadow_root_observer.js");
7
8
  const iframe_offsets_js_1 = require("./iframe_offsets.js");
@@ -51,6 +52,8 @@ class TopObserver extends observer_js_1.default {
51
52
  //log
52
53
  return;
53
54
  }
55
+ if (!(0, utils_js_1.canAccessIframe)(iframe))
56
+ return;
54
57
  const currentWin = iframe.contentWindow;
55
58
  const currentDoc = iframe.contentDocument;
56
59
  if (currentDoc && currentDoc !== doc) {
@@ -67,6 +70,7 @@ class TopObserver extends observer_js_1.default {
67
70
  //TODO: more explicit logic
68
71
  ) {
69
72
  this.contextsSet.add(currentWin);
73
+ (0, network_js_1.default)(this.app, this.app.networkOptions, currentWin);
70
74
  //@ts-ignore https://github.com/microsoft/TypeScript/issues/41684
71
75
  this.contextCallbacks.forEach((cb) => cb(currentWin));
72
76
  }
@@ -5,6 +5,12 @@ export default class Ticker {
5
5
  private timer;
6
6
  private readonly callbacks;
7
7
  constructor(app: App);
8
+ /**
9
+ * @param {Callback} callback - repeated cb
10
+ * @param {number} n - number of turn skips; ticker have a 30 ms cycle
11
+ * @param {boolean} useSafe - using safe wrapper to check if app is active
12
+ * @param {object} thisArg - link to <this>
13
+ * */
8
14
  attach(callback: Callback, n?: number, useSafe?: boolean, thisArg?: any): void;
9
15
  start(): void;
10
16
  stop(): void;
package/cjs/app/ticker.js CHANGED
@@ -15,6 +15,12 @@ class Ticker {
15
15
  this.timer = null;
16
16
  this.callbacks = [];
17
17
  }
18
+ /**
19
+ * @param {Callback} callback - repeated cb
20
+ * @param {number} n - number of turn skips; ticker have a 30 ms cycle
21
+ * @param {boolean} useSafe - using safe wrapper to check if app is active
22
+ * @param {object} thisArg - link to <this>
23
+ * */
18
24
  attach(callback, n = 0, useSafe = true, thisArg) {
19
25
  if (thisArg) {
20
26
  callback = callback.bind(thisArg);
@@ -38,7 +38,7 @@ export declare const enum Type {
38
38
  PerformanceTrack = 49,
39
39
  StringDict = 50,
40
40
  SetNodeAttributeDict = 51,
41
- ResourceTiming = 53,
41
+ ResourceTimingDeprecated = 53,
42
42
  ConnectionInformation = 54,
43
43
  SetPageVisibility = 55,
44
44
  LoadFontFace = 57,
@@ -59,7 +59,12 @@ export declare const enum Type {
59
59
  JSException = 78,
60
60
  Zustand = 79,
61
61
  BatchMetadata = 81,
62
- PartitionedMessage = 82
62
+ PartitionedMessage = 82,
63
+ InputChange = 112,
64
+ SelectionChange = 113,
65
+ MouseThrashing = 114,
66
+ UnbindNodes = 115,
67
+ ResourceTiming = 116
63
68
  }
64
69
  export type Timestamp = [
65
70
  Type.Timestamp,
@@ -287,8 +292,8 @@ export type SetNodeAttributeDict = [
287
292
  number,
288
293
  number
289
294
  ];
290
- export type ResourceTiming = [
291
- Type.ResourceTiming,
295
+ export type ResourceTimingDeprecated = [
296
+ Type.ResourceTimingDeprecated,
292
297
  number,
293
298
  number,
294
299
  number,
@@ -423,5 +428,41 @@ export type PartitionedMessage = [
423
428
  number,
424
429
  number
425
430
  ];
426
- type Message = Timestamp | SetPageLocation | SetViewportSize | SetViewportScroll | CreateDocument | CreateElementNode | CreateTextNode | MoveNode | RemoveNode | SetNodeAttribute | RemoveNodeAttribute | SetNodeData | SetNodeScroll | SetInputTarget | SetInputValue | SetInputChecked | MouseMove | NetworkRequest | ConsoleLog | PageLoadTiming | PageRenderTiming | CustomEvent | UserID | UserAnonymousID | Metadata | CSSInsertRule | CSSDeleteRule | Fetch | Profiler | OTable | StateAction | Redux | Vuex | MobX | NgRx | GraphQL | PerformanceTrack | StringDict | SetNodeAttributeDict | ResourceTiming | ConnectionInformation | SetPageVisibility | LoadFontFace | SetNodeFocus | LongTask | SetNodeAttributeURLBased | SetCSSDataURLBased | TechnicalInfo | CustomIssue | CSSInsertRuleURLBased | MouseClick | CreateIFrameDocument | AdoptedSSReplaceURLBased | AdoptedSSInsertRuleURLBased | AdoptedSSDeleteRule | AdoptedSSAddOwner | AdoptedSSRemoveOwner | JSException | Zustand | BatchMetadata | PartitionedMessage;
431
+ export type InputChange = [
432
+ Type.InputChange,
433
+ number,
434
+ string,
435
+ boolean,
436
+ string,
437
+ number,
438
+ number
439
+ ];
440
+ export type SelectionChange = [
441
+ Type.SelectionChange,
442
+ number,
443
+ number,
444
+ string
445
+ ];
446
+ export type MouseThrashing = [
447
+ Type.MouseThrashing,
448
+ number
449
+ ];
450
+ export type UnbindNodes = [
451
+ Type.UnbindNodes,
452
+ number
453
+ ];
454
+ export type ResourceTiming = [
455
+ Type.ResourceTiming,
456
+ number,
457
+ number,
458
+ number,
459
+ number,
460
+ number,
461
+ number,
462
+ string,
463
+ string,
464
+ number,
465
+ boolean
466
+ ];
467
+ type Message = Timestamp | SetPageLocation | SetViewportSize | SetViewportScroll | CreateDocument | CreateElementNode | CreateTextNode | MoveNode | RemoveNode | SetNodeAttribute | RemoveNodeAttribute | SetNodeData | SetNodeScroll | SetInputTarget | SetInputValue | SetInputChecked | MouseMove | NetworkRequest | ConsoleLog | PageLoadTiming | PageRenderTiming | CustomEvent | UserID | UserAnonymousID | Metadata | CSSInsertRule | CSSDeleteRule | Fetch | Profiler | OTable | StateAction | Redux | Vuex | MobX | NgRx | GraphQL | PerformanceTrack | StringDict | SetNodeAttributeDict | ResourceTimingDeprecated | ConnectionInformation | SetPageVisibility | LoadFontFace | SetNodeFocus | LongTask | SetNodeAttributeURLBased | SetCSSDataURLBased | TechnicalInfo | CustomIssue | CSSInsertRuleURLBased | MouseClick | CreateIFrameDocument | AdoptedSSReplaceURLBased | AdoptedSSInsertRuleURLBased | AdoptedSSDeleteRule | AdoptedSSAddOwner | AdoptedSSRemoveOwner | JSException | Zustand | BatchMetadata | PartitionedMessage | InputChange | SelectionChange | MouseThrashing | UnbindNodes | ResourceTiming;
427
468
  export default Message;
package/cjs/index.d.ts CHANGED
@@ -10,6 +10,7 @@ import type { Options as InputOptions } from './modules/input.js';
10
10
  import type { Options as PerformanceOptions } from './modules/performance.js';
11
11
  import type { Options as TimingOptions } from './modules/timing.js';
12
12
  import type { Options as NetworkOptions } from './modules/network.js';
13
+ import type { MouseHandlerOptions } from './modules/mouse.js';
13
14
  import type { StartOptions } from './app/index.js';
14
15
  import type { StartPromiseReturn } from './app/index.js';
15
16
  export type Options = Partial<AppOptions & ConsoleOptions & ExceptionOptions & InputOptions & PerformanceOptions & TimingOptions> & {
@@ -19,6 +20,7 @@ export type Options = Partial<AppOptions & ConsoleOptions & ExceptionOptions & I
19
20
  respectDoNotTrack?: boolean;
20
21
  autoResetOnWindowOpen?: boolean;
21
22
  network?: NetworkOptions;
23
+ mouse?: MouseHandlerOptions;
22
24
  __DISABLE_SECURE_MODE?: boolean;
23
25
  };
24
26
  export default class API {
package/cjs/index.js CHANGED
@@ -24,6 +24,7 @@ const focus_js_1 = require("./modules/focus.js");
24
24
  const fonts_js_1 = require("./modules/fonts.js");
25
25
  const network_js_1 = require("./modules/network.js");
26
26
  const constructedStyleSheets_js_1 = require("./modules/constructedStyleSheets.js");
27
+ const selection_js_1 = require("./modules/selection.js");
27
28
  const utils_js_1 = require("./utils.js");
28
29
  const DOCS_SETUP = '/installation/javascript-sdk';
29
30
  function processOptions(obj) {
@@ -109,13 +110,14 @@ class API {
109
110
  (0, exception_js_1.default)(app, options);
110
111
  (0, img_js_1.default)(app);
111
112
  (0, input_js_1.default)(app, options);
112
- (0, mouse_js_1.default)(app);
113
+ (0, mouse_js_1.default)(app, options.mouse);
113
114
  (0, timing_js_1.default)(app, options);
114
115
  (0, performance_js_1.default)(app, options);
115
116
  (0, scroll_js_1.default)(app);
116
117
  (0, focus_js_1.default)(app);
117
118
  (0, fonts_js_1.default)(app);
118
119
  (0, network_js_1.default)(app, options.network);
120
+ (0, selection_js_1.default)(app);
119
121
  window.__OPENREPLAY__ = this;
120
122
  if (options.autoResetOnWindowOpen) {
121
123
  const wOpen = window.open;
@@ -140,7 +142,7 @@ class API {
140
142
  // no-cors issue only with text/plain or not-set Content-Type
141
143
  // req.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
142
144
  req.send(JSON.stringify({
143
- trackerVersion: '5.0.2',
145
+ trackerVersion: '6.0.1-beta.1',
144
146
  projectKey: options.projectKey,
145
147
  doNotTrack,
146
148
  // TODO: add precise reason (an exact API missing)
@@ -55,7 +55,7 @@ function default_1(app) {
55
55
  const sendImgError = app.safe(function (img) {
56
56
  const resolvedSrc = resolveURL(img.src || ''); // Src type is null sometimes. - is it true?
57
57
  if ((0, utils_js_1.isURL)(resolvedSrc)) {
58
- app.send((0, messages_gen_js_1.ResourceTiming)(app.timestamp(), 0, 0, 0, 0, 0, resolvedSrc, 'img'));
58
+ app.send((0, messages_gen_js_1.ResourceTiming)(app.timestamp(), 0, 0, 0, 0, 0, resolvedSrc, 'img', 0, false));
59
59
  }
60
60
  });
61
61
  const sendImgAttrs = app.safe(function (img) {
@@ -1,6 +1,6 @@
1
1
  import type App from '../app/index.js';
2
- type TextEditableElement = HTMLInputElement | HTMLTextAreaElement;
3
- export declare function getInputLabel(node: TextEditableElement): string;
2
+ type TextFieldElement = HTMLInputElement | HTMLTextAreaElement;
3
+ export declare function getInputLabel(node: TextFieldElement): string;
4
4
  export declare const enum InputMode {
5
5
  Plain = 0,
6
6
  Obscured = 1,
@@ -5,7 +5,7 @@ const utils_js_1 = require("../utils.js");
5
5
  const guards_js_1 = require("../app/guards.js");
6
6
  const messages_gen_js_1 = require("../app/messages.gen.js");
7
7
  const INPUT_TYPES = ['text', 'password', 'email', 'search', 'number', 'range', 'date', 'tel'];
8
- function isTextEditable(node) {
8
+ function isTextFieldElement(node) {
9
9
  if ((0, guards_js_1.hasTag)(node, 'textarea')) {
10
10
  return true;
11
11
  }
@@ -14,7 +14,7 @@ function isTextEditable(node) {
14
14
  }
15
15
  return INPUT_TYPES.includes(node.type);
16
16
  }
17
- function isCheckable(node) {
17
+ function isCheckbox(node) {
18
18
  if (!(0, guards_js_1.hasTag)(node, 'input')) {
19
19
  return false;
20
20
  }
@@ -71,13 +71,7 @@ function default_1(app, opts) {
71
71
  defaultInputMode: 1 /* InputMode.Obscured */,
72
72
  obscureInputDates: false,
73
73
  }, opts);
74
- function sendInputTarget(id, node) {
75
- const label = getInputLabel(node);
76
- if (label !== '') {
77
- app.send((0, messages_gen_js_1.SetInputTarget)(id, label));
78
- }
79
- }
80
- function sendInputValue(id, node) {
74
+ function getInputValue(id, node) {
81
75
  let value = node.value;
82
76
  let inputMode = options.defaultInputMode;
83
77
  if (node.type === 'password' || app.sanitizer.isHidden(id)) {
@@ -101,61 +95,89 @@ function default_1(app, opts) {
101
95
  value = '';
102
96
  break;
103
97
  }
98
+ return { value, mask };
99
+ }
100
+ function sendInputValue(id, node) {
101
+ const { value, mask } = getInputValue(id, node);
104
102
  app.send((0, messages_gen_js_1.SetInputValue)(id, value, mask));
105
103
  }
106
104
  const inputValues = new Map();
107
- const checkableValues = new Map();
108
- const registeredTargets = new Set();
105
+ const checkboxValues = new Map();
109
106
  app.attachStopCallback(() => {
110
107
  inputValues.clear();
111
- checkableValues.clear();
112
- registeredTargets.clear();
108
+ checkboxValues.clear();
113
109
  });
110
+ function trackInputValue(id, node) {
111
+ if (inputValues.get(id) === node.value) {
112
+ return;
113
+ }
114
+ inputValues.set(id, node.value);
115
+ sendInputValue(id, node);
116
+ }
117
+ function trackCheckboxValue(id, value) {
118
+ if (checkboxValues.get(id) === value) {
119
+ return;
120
+ }
121
+ checkboxValues.set(id, value);
122
+ app.send((0, messages_gen_js_1.SetInputChecked)(id, value));
123
+ }
124
+ // The only way (to our knowledge) to track all kinds of input changes, including those made by JS
114
125
  app.ticker.attach(() => {
115
126
  inputValues.forEach((value, id) => {
116
127
  const node = app.nodes.getNode(id);
117
128
  if (!node)
118
129
  return inputValues.delete(id);
119
- if (value !== node.value) {
120
- inputValues.set(id, node.value);
121
- if (!registeredTargets.has(id)) {
122
- registeredTargets.add(id);
123
- sendInputTarget(id, node);
124
- }
125
- sendInputValue(id, node);
126
- }
130
+ trackInputValue(id, node);
127
131
  });
128
- checkableValues.forEach((checked, id) => {
132
+ checkboxValues.forEach((checked, id) => {
129
133
  const node = app.nodes.getNode(id);
130
134
  if (!node)
131
- return checkableValues.delete(id);
132
- if (checked !== node.checked) {
133
- checkableValues.set(id, node.checked);
134
- app.send((0, messages_gen_js_1.SetInputChecked)(id, node.checked));
135
- }
135
+ return checkboxValues.delete(id);
136
+ trackCheckboxValue(id, node.checked);
136
137
  });
137
- });
138
- app.ticker.attach(Set.prototype.clear, 100, false, registeredTargets);
138
+ }, 3);
139
+ function sendInputChange(id, node, hesitationTime, inputTime) {
140
+ const { value, mask } = getInputValue(id, node);
141
+ const label = getInputLabel(node);
142
+ app.send((0, messages_gen_js_1.InputChange)(id, value, mask !== 0, label, hesitationTime, inputTime));
143
+ }
139
144
  app.nodes.attachNodeCallback(app.safe((node) => {
140
145
  const id = app.nodes.getID(node);
141
146
  if (id === undefined) {
142
147
  return;
143
148
  }
144
- // TODO: support multiple select (?): use selectedOptions; Need send target?
149
+ // TODO: support multiple select (?): use selectedOptions;
145
150
  if ((0, guards_js_1.hasTag)(node, 'select')) {
146
151
  sendInputValue(id, node);
147
- app.attachEventListener(node, 'change', () => {
148
- sendInputValue(id, node);
149
- });
152
+ app.nodes.attachNodeListener(node, 'change', () => sendInputValue(id, node));
150
153
  }
151
- if (isTextEditable(node)) {
152
- inputValues.set(id, node.value);
153
- sendInputValue(id, node);
154
+ if (isTextFieldElement(node)) {
155
+ trackInputValue(id, node);
156
+ let nodeFocusTime = 0;
157
+ let nodeHesitationTime = 0;
158
+ let inputTime = 0;
159
+ const onFocus = () => {
160
+ nodeFocusTime = (0, utils_js_1.now)();
161
+ };
162
+ const onInput = () => {
163
+ if (nodeHesitationTime === 0) {
164
+ nodeHesitationTime = (0, utils_js_1.now)() - nodeFocusTime;
165
+ }
166
+ };
167
+ const onChange = () => {
168
+ inputTime = (0, utils_js_1.now)() - nodeFocusTime;
169
+ sendInputChange(id, node, nodeHesitationTime, inputTime);
170
+ nodeHesitationTime = 0;
171
+ inputTime = 0;
172
+ };
173
+ app.nodes.attachNodeListener(node, 'focus', onFocus);
174
+ app.nodes.attachNodeListener(node, 'input', onInput);
175
+ app.nodes.attachNodeListener(node, 'change', onChange);
154
176
  return;
155
177
  }
156
- if (isCheckable(node)) {
157
- checkableValues.set(id, node.checked);
158
- app.send((0, messages_gen_js_1.SetInputChecked)(id, node.checked));
178
+ if (isCheckbox(node)) {
179
+ trackCheckboxValue(id, node.checked);
180
+ app.nodes.attachNodeListener(node, 'change', () => trackCheckboxValue(id, node.checked));
159
181
  return;
160
182
  }
161
183
  }));