@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
@@ -1,2 +1,24 @@
1
1
  import type App from '../app/index.js';
2
- export default function (app: App): void;
2
+ export interface MouseHandlerOptions {
3
+ disableClickmaps?: boolean;
4
+ /** minimum length of an optimised selector.
5
+ *
6
+ * body > div > div > p => body > p for example
7
+ *
8
+ * default 2
9
+ * */
10
+ minSelectorDepth?: number;
11
+ /** how many selectors to try before falling back to nth-child selectors
12
+ * performance expensive operation
13
+ *
14
+ * default 1000
15
+ * */
16
+ nthThreshold?: number;
17
+ /**
18
+ * how many tries to optimise and shorten the selector
19
+ *
20
+ * default 10_000
21
+ * */
22
+ maxOptimiseTries?: number;
23
+ }
24
+ export default function (app: App, options?: MouseHandlerOptions): void;
@@ -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,39 @@ 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 > 4 && acceleration > shakeThreshold) {
110
+ app.send((0, messages_gen_js_1.MouseThrashing)((0, utils_js_1.now)()));
111
+ }
112
+ distance = 0;
113
+ directionChangeCount = 0;
114
+ velocity = nextVelocity;
115
+ }
116
+ app.attachStartCallback(() => {
117
+ checkIntervalId = setInterval(() => checkMouseShaking(), shakeCheckInterval);
118
+ });
94
119
  app.attachStopCallback(() => {
95
120
  mousePositionX = -1;
96
121
  mousePositionY = -1;
97
122
  mousePositionChanged = false;
98
123
  mouseTarget = null;
99
124
  selectorMap = {};
125
+ if (checkIntervalId) {
126
+ clearInterval(checkIntervalId);
127
+ }
100
128
  });
101
129
  const sendMouseMove = () => {
102
130
  if (mousePositionChanged) {
@@ -105,8 +133,8 @@ function default_1(app) {
105
133
  }
106
134
  };
107
135
  const patchDocument = (document, topframe = false) => {
108
- function getSelector(id, target) {
109
- return (selectorMap[id] = selectorMap[id] || _getSelector(target, document));
136
+ function getSelector(id, target, options) {
137
+ return (selectorMap[id] = selectorMap[id] || _getSelector(target, document, options));
110
138
  }
111
139
  const attachListener = topframe
112
140
  ? app.attachEventListener.bind(app) // attached/removed on start/stop
@@ -123,6 +151,12 @@ function default_1(app) {
123
151
  mousePositionX = e.clientX + left;
124
152
  mousePositionY = e.clientY + top;
125
153
  mousePositionChanged = true;
154
+ const nextDirection = Math.sign(e.movementX);
155
+ distance += Math.abs(e.movementX) + Math.abs(e.movementY);
156
+ if (nextDirection !== direction) {
157
+ direction = nextDirection;
158
+ directionChangeCount++;
159
+ }
126
160
  }, false);
127
161
  attachListener(document, 'click', (e) => {
128
162
  const target = getTarget(e.target, document);
@@ -132,7 +166,7 @@ function default_1(app) {
132
166
  const id = app.nodes.getID(target);
133
167
  if (id !== undefined) {
134
168
  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);
169
+ 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
170
  }
137
171
  mouseTarget = null;
138
172
  });
@@ -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;
package/cjs/utils.d.ts CHANGED
@@ -11,3 +11,5 @@ export declare const DOCS_HOST = "https://docs.openreplay.com";
11
11
  export declare function deprecationWarn(nameOfFeature: string, useInstead: string, docsPath?: string): void;
12
12
  export declare function getLabelAttribute(e: Element): string | null;
13
13
  export declare function hasOpenreplayAttribute(e: Element, attr: string): boolean;
14
+ export declare function isIframeCrossdomain(e: HTMLIFrameElement): boolean;
15
+ export declare function canAccessIframe(iframe: HTMLIFrameElement): boolean;
package/cjs/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.hasOpenreplayAttribute = exports.getLabelAttribute = exports.deprecationWarn = exports.DOCS_HOST = exports.isURL = exports.normSpaces = exports.stars = exports.now = exports.getTimeOrigin = exports.adjustTimeOrigin = exports.MAX_STR_LEN = exports.IS_FIREFOX = exports.IN_BROWSER = void 0;
3
+ exports.canAccessIframe = exports.isIframeCrossdomain = exports.hasOpenreplayAttribute = exports.getLabelAttribute = exports.deprecationWarn = exports.DOCS_HOST = exports.isURL = exports.normSpaces = exports.stars = exports.now = exports.getTimeOrigin = exports.adjustTimeOrigin = exports.MAX_STR_LEN = exports.IS_FIREFOX = exports.IN_BROWSER = void 0;
4
4
  const DEPRECATED_ATTRS = { htmlmasked: 'hidden', masked: 'obscured' };
5
5
  exports.IN_BROWSER = !(typeof window === 'undefined');
6
6
  exports.IS_FIREFOX = exports.IN_BROWSER && navigator.userAgent.match(/firefox|fxios/i);
@@ -69,3 +69,22 @@ function hasOpenreplayAttribute(e, attr) {
69
69
  return false;
70
70
  }
71
71
  exports.hasOpenreplayAttribute = hasOpenreplayAttribute;
72
+ function isIframeCrossdomain(e) {
73
+ var _a;
74
+ try {
75
+ return ((_a = e.contentWindow) === null || _a === void 0 ? void 0 : _a.location.href) !== window.location.href;
76
+ }
77
+ catch (e) {
78
+ return true;
79
+ }
80
+ }
81
+ exports.isIframeCrossdomain = isIframeCrossdomain;
82
+ function canAccessIframe(iframe) {
83
+ try {
84
+ return Boolean(iframe.contentDocument);
85
+ }
86
+ catch (e) {
87
+ return false;
88
+ }
89
+ }
90
+ exports.canAccessIframe = canAccessIframe;
@@ -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'; // TODO: version compatability check inside each plugin.
33
+ this.version = '6.0.1-beta.1'; // 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;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' })));
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,10 +1,11 @@
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';
6
7
  import { CreateDocument } from '../messages.gen.js';
7
- import { IN_BROWSER, hasOpenreplayAttribute } from '../../utils.js';
8
+ import { IN_BROWSER, hasOpenreplayAttribute, canAccessIframe } from '../../utils.js';
8
9
  const attachShadowNativeFn = IN_BROWSER ? Element.prototype.attachShadow : () => new ShadowRoot();
9
10
  export default class TopObserver extends Observer {
10
11
  constructor(app, options) {
@@ -49,6 +50,8 @@ export default class TopObserver extends Observer {
49
50
  //log
50
51
  return;
51
52
  }
53
+ if (!canAccessIframe(iframe))
54
+ return;
52
55
  const currentWin = iframe.contentWindow;
53
56
  const currentDoc = iframe.contentDocument;
54
57
  if (currentDoc && currentDoc !== doc) {
@@ -65,6 +68,7 @@ export default class TopObserver extends Observer {
65
68
  //TODO: more explicit logic
66
69
  ) {
67
70
  this.contextsSet.add(currentWin);
71
+ Network(this.app, this.app.networkOptions, currentWin);
68
72
  //@ts-ignore https://github.com/microsoft/TypeScript/issues/41684
69
73
  this.contextCallbacks.forEach((cb) => cb(currentWin));
70
74
  }
@@ -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;