@openreplay/tracker 5.0.2-beta.1 → 6.0.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +13 -0
  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 +2 -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 +44 -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/lib/app/index.d.ts +3 -0
  26. package/lib/app/index.js +4 -3
  27. package/lib/app/messages.gen.d.ts +6 -1
  28. package/lib/app/messages.gen.js +48 -2
  29. package/lib/app/nodes.d.ts +1 -0
  30. package/lib/app/nodes.js +3 -0
  31. package/lib/app/observer/observer.js +8 -2
  32. package/lib/app/observer/top_observer.js +2 -0
  33. package/lib/app/ticker.d.ts +6 -0
  34. package/lib/app/ticker.js +6 -0
  35. package/lib/common/messages.gen.d.ts +46 -5
  36. package/lib/common/tsconfig.tsbuildinfo +1 -1
  37. package/lib/index.d.ts +2 -0
  38. package/lib/index.js +4 -2
  39. package/lib/modules/img.js +1 -1
  40. package/lib/modules/input.d.ts +2 -2
  41. package/lib/modules/input.js +63 -41
  42. package/lib/modules/mouse.d.ts +23 -1
  43. package/lib/modules/mouse.js +46 -11
  44. package/lib/modules/network.d.ts +1 -1
  45. package/lib/modules/network.js +81 -10
  46. package/lib/modules/selection.d.ts +7 -0
  47. package/lib/modules/selection.js +35 -0
  48. package/lib/modules/timing.js +3 -1
  49. package/package.json +1 -1
@@ -5,13 +5,13 @@ const utils_js_1 = require("../utils.js");
5
5
  const messages_gen_js_1 = require("../app/messages.gen.js");
6
6
  const input_js_1 = require("./input.js");
7
7
  const finder_1 = require("@medv/finder");
8
- function _getSelector(target, document) {
8
+ function _getSelector(target, document, options) {
9
9
  const selector = (0, finder_1.finder)(target, {
10
10
  root: document.body,
11
11
  seedMinLength: 3,
12
- optimizedMinLength: 2,
13
- threshold: 1000,
14
- maxNumberOfTries: 10000,
12
+ optimizedMinLength: (options === null || options === void 0 ? void 0 : options.minSelectorDepth) || 2,
13
+ threshold: (options === null || options === void 0 ? void 0 : options.nthThreshold) || 1000,
14
+ maxNumberOfTries: (options === null || options === void 0 ? void 0 : options.maxOptimiseTries) || 10000,
15
15
  });
16
16
  return selector;
17
17
  }
@@ -26,7 +26,7 @@ function isClickable(element) {
26
26
  element.onclick != null ||
27
27
  element.getAttribute('role') === 'button');
28
28
  //|| element.className.includes("btn")
29
- // MBTODO: intersept addEventListener
29
+ // MBTODO: intercept addEventListener
30
30
  }
31
31
  //TODO: fix (typescript is not sure about target variable after assignation of svg)
32
32
  function getTarget(target, document) {
@@ -66,7 +66,8 @@ function _getTarget(target, document) {
66
66
  }
67
67
  return target === document.documentElement ? null : target;
68
68
  }
69
- function default_1(app) {
69
+ function default_1(app, options) {
70
+ const { disableClickmaps = false } = options || {};
70
71
  function getTargetLabel(target) {
71
72
  const dl = (0, utils_js_1.getLabelAttribute)(target);
72
73
  if (dl !== null) {
@@ -91,12 +92,40 @@ function default_1(app) {
91
92
  let mouseTarget = null;
92
93
  let mouseTargetTime = 0;
93
94
  let selectorMap = {};
95
+ let velocity = 0;
96
+ let direction = 0;
97
+ let directionChangeCount = 0;
98
+ let distance = 0;
99
+ let checkIntervalId;
100
+ const shakeThreshold = 0.008;
101
+ const shakeCheckInterval = 225;
102
+ function checkMouseShaking() {
103
+ const nextVelocity = distance / shakeCheckInterval;
104
+ if (!velocity) {
105
+ velocity = nextVelocity;
106
+ return;
107
+ }
108
+ const acceleration = (nextVelocity - velocity) / shakeCheckInterval;
109
+ if (directionChangeCount > 3 && acceleration > shakeThreshold) {
110
+ console.log('Mouse shake detected!');
111
+ app.send((0, messages_gen_js_1.MouseThrashing)((0, utils_js_1.now)()));
112
+ }
113
+ distance = 0;
114
+ directionChangeCount = 0;
115
+ velocity = nextVelocity;
116
+ }
117
+ app.attachStartCallback(() => {
118
+ checkIntervalId = setInterval(() => checkMouseShaking(), shakeCheckInterval);
119
+ });
94
120
  app.attachStopCallback(() => {
95
121
  mousePositionX = -1;
96
122
  mousePositionY = -1;
97
123
  mousePositionChanged = false;
98
124
  mouseTarget = null;
99
125
  selectorMap = {};
126
+ if (checkIntervalId) {
127
+ clearInterval(checkIntervalId);
128
+ }
100
129
  });
101
130
  const sendMouseMove = () => {
102
131
  if (mousePositionChanged) {
@@ -105,8 +134,8 @@ function default_1(app) {
105
134
  }
106
135
  };
107
136
  const patchDocument = (document, topframe = false) => {
108
- function getSelector(id, target) {
109
- return (selectorMap[id] = selectorMap[id] || _getSelector(target, document));
137
+ function getSelector(id, target, options) {
138
+ return (selectorMap[id] = selectorMap[id] || _getSelector(target, document, options));
110
139
  }
111
140
  const attachListener = topframe
112
141
  ? app.attachEventListener.bind(app) // attached/removed on start/stop
@@ -123,6 +152,12 @@ function default_1(app) {
123
152
  mousePositionX = e.clientX + left;
124
153
  mousePositionY = e.clientY + top;
125
154
  mousePositionChanged = true;
155
+ const nextDirection = Math.sign(e.movementX);
156
+ distance += Math.abs(e.movementX) + Math.abs(e.movementY);
157
+ if (nextDirection !== direction) {
158
+ direction = nextDirection;
159
+ directionChangeCount++;
160
+ }
126
161
  }, false);
127
162
  attachListener(document, 'click', (e) => {
128
163
  const target = getTarget(e.target, document);
@@ -132,7 +167,7 @@ function default_1(app) {
132
167
  const id = app.nodes.getID(target);
133
168
  if (id !== undefined) {
134
169
  sendMouseMove();
135
- app.send((0, messages_gen_js_1.MouseClick)(id, mouseTarget === target ? Math.round(performance.now() - mouseTargetTime) : 0, getTargetLabel(target), isClickable(target) ? getSelector(id, target) : ''), true);
170
+ app.send((0, messages_gen_js_1.MouseClick)(id, mouseTarget === target ? Math.round(performance.now() - mouseTargetTime) : 0, getTargetLabel(target), isClickable(target) && !disableClickmaps ? getSelector(id, target, options) : ''), true);
136
171
  }
137
172
  mouseTarget = null;
138
173
  });
@@ -24,5 +24,5 @@ export interface Options {
24
24
  capturePayload: boolean;
25
25
  sanitizer?: Sanitizer;
26
26
  }
27
- export default function (app: App, opts?: Partial<Options>): void;
27
+ export default function (app: App, opts?: Partial<Options>, customEnv?: Record<string, any>): void;
28
28
  export {};
@@ -2,6 +2,43 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const messages_gen_js_1 = require("../app/messages.gen.js");
4
4
  const utils_js_1 = require("../utils.js");
5
+ // Request:
6
+ // declare const enum BodyType {
7
+ // Blob = "Blob",
8
+ // ArrayBuffer = "ArrayBuffer",
9
+ // TypedArray = "TypedArray",
10
+ // DataView = "DataView",
11
+ // FormData = "FormData",
12
+ // URLSearchParams = "URLSearchParams",
13
+ // Document = "Document", // XHR only
14
+ // ReadableStream = "ReadableStream", // Fetch only
15
+ // Literal = "literal",
16
+ // Unknown = "unk",
17
+ // }
18
+ // XHRResponse body: ArrayBuffer, a Blob, a Document, a JavaScript Object, or a string
19
+ // TODO: extract maximum of useful information from any type of Request/Responce bodies
20
+ // function objectifyBody(body: any): RequestBody {
21
+ // if (body instanceof Blob) {
22
+ // return {
23
+ // body: `<Blob type: ${body.type}>; size: ${body.size}`,
24
+ // bodyType: BodyType.Blob,
25
+ // }
26
+ // }
27
+ // return {
28
+ // body,
29
+ // bodyType: BodyType.Literal,
30
+ // }
31
+ // }
32
+ function checkCacheByPerformanceTimings(requestUrl) {
33
+ if (performance) {
34
+ const timings = performance.getEntriesByName(requestUrl)[0];
35
+ if (timings) {
36
+ // @ts-ignore - weird ts typings, please refer to https://developer.mozilla.org/en-US/docs/Web/API/PerformanceNavigationTiming
37
+ return timings.transferSize === 0 || timings.responseStart - timings.requestStart < 10;
38
+ }
39
+ }
40
+ return false;
41
+ }
5
42
  function getXHRRequestDataObject(xhr) {
6
43
  // @ts-ignore this is 3x faster than using Map<XHR, XHRRequestData>
7
44
  if (!xhr.__or_req_data__) {
@@ -14,7 +51,7 @@ function getXHRRequestDataObject(xhr) {
14
51
  function strMethod(method) {
15
52
  return typeof method === 'string' ? method.toUpperCase() : 'GET';
16
53
  }
17
- function default_1(app, opts = {}) {
54
+ function default_1(app, opts = {}, customEnv) {
18
55
  const options = Object.assign({
19
56
  failuresOnly: false,
20
57
  ignoreHeaders: ['Cookie', 'Set-Cookie', 'Authorization'],
@@ -65,8 +102,10 @@ function default_1(app, opts = {}) {
65
102
  return JSON.stringify(r);
66
103
  }
67
104
  /* ====== Fetch ====== */
68
- const origFetch = window.fetch.bind(window);
69
- window.fetch = (input, init = {}) => {
105
+ const origFetch = customEnv
106
+ ? customEnv.fetch.bind(customEnv)
107
+ : window.fetch.bind(window);
108
+ const trackFetch = (input, init = {}) => {
70
109
  if (!(typeof input === 'string' || input instanceof URL) || app.isServiceURL(String(input))) {
71
110
  return origFetch(input, init);
72
111
  }
@@ -140,10 +179,19 @@ function default_1(app, opts = {}) {
140
179
  return response;
141
180
  });
142
181
  };
182
+ if (customEnv) {
183
+ customEnv.fetch = trackFetch;
184
+ }
185
+ else {
186
+ window.fetch = trackFetch;
187
+ }
143
188
  /* ====== <> ====== */
144
189
  /* ====== XHR ====== */
145
- const nativeOpen = XMLHttpRequest.prototype.open;
146
- XMLHttpRequest.prototype.open = function (initMethod, url) {
190
+ const nativeOpen = customEnv
191
+ ? customEnv.XMLHttpRequest.prototype.open
192
+ : XMLHttpRequest.prototype.open;
193
+ function trackXMLHttpReqOpen(initMethod, url) {
194
+ // @ts-ignore ??? this -> XMLHttpRequest
147
195
  const xhr = this;
148
196
  setSessionTokenHeader((name, value) => xhr.setRequestHeader(name, value));
149
197
  let startTime = 0;
@@ -182,22 +230,45 @@ function default_1(app, opts = {}) {
182
230
  }));
183
231
  //TODO: handle error (though it has no Error API nor any useful information)
184
232
  //xhr.addEventListener('error', (e) => {})
233
+ // @ts-ignore ??? this -> XMLHttpRequest
185
234
  return nativeOpen.apply(this, arguments);
186
- };
235
+ }
236
+ if (customEnv) {
237
+ customEnv.XMLHttpRequest.prototype.open = trackXMLHttpReqOpen.bind(customEnv);
238
+ }
239
+ else {
240
+ XMLHttpRequest.prototype.open = trackXMLHttpReqOpen;
241
+ }
187
242
  const nativeSend = XMLHttpRequest.prototype.send;
188
- XMLHttpRequest.prototype.send = function (body) {
243
+ function trackXHRSend(body) {
244
+ // @ts-ignore ??? this -> XMLHttpRequest
189
245
  const rdo = getXHRRequestDataObject(this);
190
246
  rdo.body = body;
247
+ // @ts-ignore ??? this -> XMLHttpRequest
191
248
  return nativeSend.apply(this, arguments);
192
- };
249
+ }
250
+ if (customEnv) {
251
+ customEnv.XMLHttpRequest.prototype.send = trackXHRSend.bind(customEnv);
252
+ }
253
+ else {
254
+ XMLHttpRequest.prototype.send = trackXHRSend;
255
+ }
193
256
  const nativeSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
194
- XMLHttpRequest.prototype.setRequestHeader = function (name, value) {
257
+ function trackSetReqHeader(name, value) {
195
258
  if (!isHIgnored(name)) {
259
+ // @ts-ignore ??? this -> XMLHttpRequest
196
260
  const rdo = getXHRRequestDataObject(this);
197
261
  rdo.headers[name] = value;
198
262
  }
263
+ // @ts-ignore ??? this -> XMLHttpRequest
199
264
  return nativeSetRequestHeader.apply(this, arguments);
200
- };
265
+ }
266
+ if (customEnv) {
267
+ customEnv.XMLHttpRequest.prototype.setRequestHeader = trackSetReqHeader.bind(customEnv);
268
+ }
269
+ else {
270
+ XMLHttpRequest.prototype.setRequestHeader = trackSetReqHeader;
271
+ }
201
272
  /* ====== <> ====== */
202
273
  }
203
274
  exports.default = default_1;
@@ -0,0 +1,7 @@
1
+ import type App from '../app/index.js';
2
+ declare function selection(app: App): void;
3
+ export default selection;
4
+ /** TODO: research how to get all in-between nodes inside selection range
5
+ * including nodes between anchor and focus nodes and their children
6
+ * without recursively searching the dom tree
7
+ */
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const messages_gen_js_1 = require("../app/messages.gen.js");
4
+ function selection(app) {
5
+ app.attachEventListener(document, 'selectionchange', () => {
6
+ const selection = document.getSelection();
7
+ if (selection !== null && !selection.isCollapsed) {
8
+ const selectionStart = app.nodes.getID(selection.anchorNode);
9
+ const selectionEnd = app.nodes.getID(selection.focusNode);
10
+ const selectedText = selection.toString().replace(/\s+/g, ' ');
11
+ if (selectionStart && selectionEnd) {
12
+ app.send((0, messages_gen_js_1.SelectionChange)(selectionStart, selectionEnd, selectedText));
13
+ }
14
+ }
15
+ else {
16
+ app.send((0, messages_gen_js_1.SelectionChange)(-1, -1, ''));
17
+ }
18
+ });
19
+ }
20
+ exports.default = selection;
21
+ /** TODO: research how to get all in-between nodes inside selection range
22
+ * including nodes between anchor and focus nodes and their children
23
+ * without recursively searching the dom tree
24
+ */
25
+ // if (selection.rangeCount) {
26
+ // const nodes = [];
27
+ // for (let i = 0; i < selection.rangeCount; i++) {
28
+ // const range = selection.getRangeAt(i);
29
+ // let node: Node | null = range.startContainer;
30
+ // while (node) {
31
+ // nodes.push(node);
32
+ // if (node === range.endContainer) break;
33
+ // node = node.nextSibling;
34
+ // }
35
+ // }
36
+ // // send selected nodes
37
+ // }
@@ -75,7 +75,9 @@ function default_1(app, opts) {
75
75
  if (resources !== null) {
76
76
  resources[entry.name] = entry.startTime + entry.duration;
77
77
  }
78
- app.send((0, messages_gen_js_1.ResourceTiming)(entry.startTime + (0, utils_js_1.getTimeOrigin)(), entry.duration, entry.responseStart && entry.startTime ? entry.responseStart - entry.startTime : 0, entry.transferSize > entry.encodedBodySize ? entry.transferSize - entry.encodedBodySize : 0, entry.encodedBodySize || 0, entry.decodedBodySize || 0, entry.name, entry.initiatorType));
78
+ app.send((0, messages_gen_js_1.ResourceTiming)(entry.startTime + (0, utils_js_1.getTimeOrigin)(), entry.duration, entry.responseStart && entry.startTime ? entry.responseStart - entry.startTime : 0, entry.transferSize > entry.encodedBodySize ? entry.transferSize - entry.encodedBodySize : 0, entry.encodedBodySize || 0, entry.decodedBodySize || 0, entry.name, entry.initiatorType, entry.transferSize,
79
+ // @ts-ignore
80
+ (entry.responseStatus && entry.responseStatus === 304) || entry.transferSize === 0));
79
81
  }
80
82
  const observer = new PerformanceObserver((list) => list.getEntries().forEach(resourceTiming));
81
83
  let prevSessionID;
@@ -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/lib/app/index.js CHANGED
@@ -30,10 +30,11 @@ export default class App {
30
30
  this.stopCallbacks = [];
31
31
  this.commitCallbacks = [];
32
32
  this.activityState = ActivityState.NotActive;
33
- this.version = '5.0.2-beta.1'; // TODO: version compatability check inside each plugin.
33
+ this.version = '6.0.0'; // TODO: version compatability check inside each plugin.
34
34
  this._usingOldFetchPlugin = false;
35
35
  this.delay = 0;
36
36
  this.projectKey = projectKey;
37
+ this.networkOptions = options.network;
37
38
  this.options = Object.assign({
38
39
  revID: '',
39
40
  node_id: '__openreplay_id',
@@ -69,12 +70,12 @@ export default class App {
69
70
  Object.entries(metadata).forEach(([key, value]) => this.send(Metadata(key, value)));
70
71
  }
71
72
  });
72
- // @depricated (use sessionHash on start instead)
73
+ // @deprecated (use sessionHash on start instead)
73
74
  if (sessionToken != null) {
74
75
  this.session.applySessionHash(sessionToken);
75
76
  }
76
77
  try {
77
- 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' })));
78
+ 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;h.NotActive;let a=0;function c(){u&&u.finaliseBatch()}function o(){h.Stopping,null!==f&&(clearInterval(f),f=null),u&&(u.clean(),u=null),r&&(r.clean(),r=null),h.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?(h.Starting,r=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=>r&&r.push(t)),null===f&&(f=setInterval(c,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 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' })));
78
79
  this.worker.onerror = (e) => {
79
80
  this._debug('webworker_error', e);
80
81
  };
@@ -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;
@@ -304,9 +304,9 @@ export function SetNodeAttributeDict(id, nameKey, valueKey) {
304
304
  valueKey,
305
305
  ];
306
306
  }
307
- export function ResourceTiming(timestamp, duration, ttfb, headerSize, encodedBodySize, decodedBodySize, url, initiator) {
307
+ export function ResourceTimingDeprecated(timestamp, duration, ttfb, headerSize, encodedBodySize, decodedBodySize, url, initiator) {
308
308
  return [
309
- 53 /* Messages.Type.ResourceTiming */,
309
+ 53 /* Messages.Type.ResourceTimingDeprecated */,
310
310
  timestamp,
311
311
  duration,
312
312
  ttfb,
@@ -484,3 +484,49 @@ export function PartitionedMessage(partNo, partTotal) {
484
484
  partTotal,
485
485
  ];
486
486
  }
487
+ export function InputChange(id, value, valueMasked, label, hesitationTime, inputDuration) {
488
+ return [
489
+ 112 /* Messages.Type.InputChange */,
490
+ id,
491
+ value,
492
+ valueMasked,
493
+ label,
494
+ hesitationTime,
495
+ inputDuration,
496
+ ];
497
+ }
498
+ export function SelectionChange(selectionStart, selectionEnd, selection) {
499
+ return [
500
+ 113 /* Messages.Type.SelectionChange */,
501
+ selectionStart,
502
+ selectionEnd,
503
+ selection,
504
+ ];
505
+ }
506
+ export function MouseThrashing(timestamp) {
507
+ return [
508
+ 114 /* Messages.Type.MouseThrashing */,
509
+ timestamp,
510
+ ];
511
+ }
512
+ export function UnbindNodes(totalRemovedPercent) {
513
+ return [
514
+ 115 /* Messages.Type.UnbindNodes */,
515
+ totalRemovedPercent,
516
+ ];
517
+ }
518
+ export function ResourceTiming(timestamp, duration, ttfb, headerSize, encodedBodySize, decodedBodySize, url, initiator, transferredSize, cached) {
519
+ return [
520
+ 116 /* Messages.Type.ResourceTiming */,
521
+ timestamp,
522
+ duration,
523
+ ttfb,
524
+ headerSize,
525
+ encodedBodySize,
526
+ decodedBodySize,
527
+ url,
528
+ initiator,
529
+ transferredSize,
530
+ cached,
531
+ ];
532
+ }
@@ -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/lib/app/nodes.js CHANGED
@@ -66,6 +66,9 @@ export default class Nodes {
66
66
  getNode(id) {
67
67
  return this.nodes[id];
68
68
  }
69
+ getNodeCount() {
70
+ return this.nodes.filter(Boolean).length;
71
+ }
69
72
  clear() {
70
73
  for (let id = 0; id < this.nodes.length; id++) {
71
74
  const node = this.nodes[id];
@@ -1,4 +1,4 @@
1
- import { RemoveNodeAttribute, SetNodeAttribute, SetNodeAttributeURLBased, SetCSSDataURLBased, SetNodeData, CreateTextNode, CreateElementNode, MoveNode, RemoveNode, } from '../messages.gen.js';
1
+ import { RemoveNodeAttribute, SetNodeAttribute, SetNodeAttributeURLBased, SetCSSDataURLBased, SetNodeData, CreateTextNode, CreateElementNode, MoveNode, RemoveNode, UnbindNodes, } from '../messages.gen.js';
2
2
  import { isRootNode, isTextNode, isElementNode, isSVGElement, hasTag, isCommentNode, } from '../guards.js';
3
3
  function isIgnored(node) {
4
4
  if (isCommentNode(node)) {
@@ -191,10 +191,16 @@ export default class Observer {
191
191
  },
192
192
  // @ts-ignore
193
193
  false);
194
+ let removed = 0;
195
+ const totalBeforeRemove = this.app.nodes.getNodeCount();
194
196
  while (walker.nextNode()) {
197
+ removed += 1;
195
198
  this.app.nodes.unregisterNode(walker.currentNode);
196
199
  }
197
- // MBTODO: count and send RemovedNodesCount (for the page crash detection in heuristics)
200
+ const removedPercent = Math.floor((removed / totalBeforeRemove) * 100);
201
+ if (removedPercent > 30) {
202
+ this.app.send(UnbindNodes(removedPercent));
203
+ }
198
204
  }
199
205
  }
200
206
  // A top-consumption function on the infinite lists test. (~1% of performance resources)
@@ -1,5 +1,6 @@
1
1
  import Observer from './observer.js';
2
2
  import { isElementNode, hasTag } from '../guards.js';
3
+ import Network from '../../modules/network.js';
3
4
  import IFrameObserver from './iframe_observer.js';
4
5
  import ShadowRootObserver from './shadow_root_observer.js';
5
6
  import IFrameOffsets from './iframe_offsets.js';
@@ -65,6 +66,7 @@ export default class TopObserver extends Observer {
65
66
  //TODO: more explicit logic
66
67
  ) {
67
68
  this.contextsSet.add(currentWin);
69
+ Network(this.app, this.app.networkOptions, currentWin);
68
70
  //@ts-ignore https://github.com/microsoft/TypeScript/issues/41684
69
71
  this.contextCallbacks.forEach((cb) => cb(currentWin));
70
72
  }
@@ -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/lib/app/ticker.js CHANGED
@@ -13,6 +13,12 @@ export default class Ticker {
13
13
  this.timer = null;
14
14
  this.callbacks = [];
15
15
  }
16
+ /**
17
+ * @param {Callback} callback - repeated cb
18
+ * @param {number} n - number of turn skips; ticker have a 30 ms cycle
19
+ * @param {boolean} useSafe - using safe wrapper to check if app is active
20
+ * @param {object} thisArg - link to <this>
21
+ * */
16
22
  attach(callback, n = 0, useSafe = true, thisArg) {
17
23
  if (thisArg) {
18
24
  callback = callback.bind(thisArg);