@openreplay/tracker 3.6.2 → 4.0.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 (128) hide show
  1. package/.eslintignore +8 -0
  2. package/.prettierignore +1 -0
  3. package/LICENSE +1 -1
  4. package/cjs/app/guards.d.ts +2 -1
  5. package/cjs/app/guards.js +6 -3
  6. package/cjs/app/index.d.ts +28 -23
  7. package/cjs/app/index.js +107 -86
  8. package/cjs/app/logger.js +6 -3
  9. package/cjs/app/messages.d.ts +52 -0
  10. package/cjs/app/messages.gen.d.ts +58 -0
  11. package/cjs/app/messages.gen.js +501 -0
  12. package/cjs/app/messages.js +234 -0
  13. package/cjs/app/nodes.d.ts +1 -1
  14. package/cjs/app/nodes.js +2 -0
  15. package/cjs/app/observer/iframe_observer.d.ts +1 -1
  16. package/cjs/app/observer/iframe_observer.js +3 -3
  17. package/cjs/app/observer/observer.d.ts +2 -3
  18. package/cjs/app/observer/observer.js +50 -52
  19. package/cjs/app/observer/shadow_root_observer.d.ts +1 -1
  20. package/cjs/app/observer/shadow_root_observer.js +3 -3
  21. package/cjs/app/observer/top_observer.d.ts +13 -2
  22. package/cjs/app/observer/top_observer.js +58 -23
  23. package/cjs/app/sanitizer.d.ts +1 -1
  24. package/cjs/app/sanitizer.js +5 -5
  25. package/cjs/app/session.d.ts +20 -2
  26. package/cjs/app/session.js +65 -6
  27. package/cjs/app/ticker.d.ts +1 -1
  28. package/cjs/common/{webworker.d.ts → interaction.d.ts} +5 -5
  29. package/cjs/common/{types.js → interaction.js} +0 -0
  30. package/cjs/common/messages.gen.d.ts +388 -0
  31. package/cjs/common/{webworker.js → messages.gen.js} +1 -0
  32. package/cjs/index.d.ts +10 -9
  33. package/cjs/index.js +47 -36
  34. package/cjs/modules/adoptedStyleSheets.d.ts +2 -0
  35. package/cjs/modules/adoptedStyleSheets.js +127 -0
  36. package/cjs/modules/connection.d.ts +1 -1
  37. package/cjs/modules/connection.js +2 -2
  38. package/cjs/modules/console.d.ts +1 -1
  39. package/cjs/modules/console.js +7 -21
  40. package/cjs/modules/cssrules.d.ts +1 -1
  41. package/cjs/modules/cssrules.js +18 -14
  42. package/cjs/modules/exception.d.ts +3 -3
  43. package/cjs/modules/exception.js +23 -18
  44. package/cjs/modules/img.d.ts +1 -1
  45. package/cjs/modules/img.js +39 -26
  46. package/cjs/modules/input.d.ts +1 -1
  47. package/cjs/modules/input.js +21 -21
  48. package/cjs/modules/mouse.d.ts +1 -1
  49. package/cjs/modules/mouse.js +50 -43
  50. package/cjs/modules/performance.d.ts +1 -1
  51. package/cjs/modules/performance.js +2 -2
  52. package/cjs/modules/scroll.d.ts +1 -1
  53. package/cjs/modules/scroll.js +16 -7
  54. package/cjs/modules/timing.d.ts +1 -1
  55. package/cjs/modules/timing.js +14 -26
  56. package/cjs/modules/viewport.d.ts +1 -1
  57. package/cjs/modules/viewport.js +4 -4
  58. package/cjs/utils.js +7 -7
  59. package/cjs/vendors/finder/finder.js +53 -48
  60. package/lib/app/guards.d.ts +2 -1
  61. package/lib/app/guards.js +4 -2
  62. package/lib/app/index.d.ts +28 -23
  63. package/lib/app/index.js +115 -94
  64. package/lib/app/logger.js +6 -3
  65. package/lib/app/messages.d.ts +52 -0
  66. package/lib/app/messages.gen.d.ts +58 -0
  67. package/lib/app/messages.gen.js +441 -0
  68. package/lib/app/messages.js +181 -0
  69. package/lib/app/nodes.d.ts +1 -1
  70. package/lib/app/nodes.js +2 -0
  71. package/lib/app/observer/iframe_observer.d.ts +1 -1
  72. package/lib/app/observer/iframe_observer.js +3 -3
  73. package/lib/app/observer/observer.d.ts +2 -3
  74. package/lib/app/observer/observer.js +51 -53
  75. package/lib/app/observer/shadow_root_observer.d.ts +1 -1
  76. package/lib/app/observer/shadow_root_observer.js +3 -3
  77. package/lib/app/observer/top_observer.d.ts +13 -2
  78. package/lib/app/observer/top_observer.js +62 -27
  79. package/lib/app/sanitizer.d.ts +1 -1
  80. package/lib/app/sanitizer.js +7 -7
  81. package/lib/app/session.d.ts +20 -2
  82. package/lib/app/session.js +65 -6
  83. package/lib/app/ticker.d.ts +1 -1
  84. package/lib/common/{webworker.d.ts → interaction.d.ts} +5 -5
  85. package/lib/common/{types.js → interaction.js} +0 -0
  86. package/lib/common/messages.gen.d.ts +388 -0
  87. package/lib/common/messages.gen.js +2 -0
  88. package/lib/common/tsconfig.tsbuildinfo +1 -1
  89. package/lib/index.d.ts +10 -9
  90. package/lib/index.js +60 -49
  91. package/lib/modules/adoptedStyleSheets.d.ts +2 -0
  92. package/lib/modules/adoptedStyleSheets.js +124 -0
  93. package/lib/modules/connection.d.ts +1 -1
  94. package/lib/modules/connection.js +2 -2
  95. package/lib/modules/console.d.ts +1 -1
  96. package/lib/modules/console.js +8 -22
  97. package/lib/modules/cssrules.d.ts +1 -1
  98. package/lib/modules/cssrules.js +19 -15
  99. package/lib/modules/exception.d.ts +3 -3
  100. package/lib/modules/exception.js +23 -18
  101. package/lib/modules/img.d.ts +1 -1
  102. package/lib/modules/img.js +41 -28
  103. package/lib/modules/input.d.ts +1 -1
  104. package/lib/modules/input.js +23 -23
  105. package/lib/modules/mouse.d.ts +1 -1
  106. package/lib/modules/mouse.js +53 -46
  107. package/lib/modules/performance.d.ts +1 -1
  108. package/lib/modules/performance.js +3 -3
  109. package/lib/modules/scroll.d.ts +1 -1
  110. package/lib/modules/scroll.js +17 -8
  111. package/lib/modules/timing.d.ts +1 -1
  112. package/lib/modules/timing.js +16 -28
  113. package/lib/modules/viewport.d.ts +1 -1
  114. package/lib/modules/viewport.js +4 -4
  115. package/lib/utils.js +7 -7
  116. package/lib/vendors/finder/finder.js +53 -48
  117. package/package.json +27 -10
  118. package/cjs/common/messages.d.ts +0 -444
  119. package/cjs/common/messages.js +0 -794
  120. package/cjs/common/types.d.ts +0 -9
  121. package/cjs/modules/longtasks.d.ts +0 -2
  122. package/cjs/modules/longtasks.js +0 -26
  123. package/lib/common/messages.d.ts +0 -444
  124. package/lib/common/messages.js +0 -790
  125. package/lib/common/types.d.ts +0 -9
  126. package/lib/common/webworker.js +0 -1
  127. package/lib/modules/longtasks.d.ts +0 -2
  128. package/lib/modules/longtasks.js +0 -23
@@ -1,6 +1,6 @@
1
- import { timestamp, isURL } from "../utils.js";
2
- import { ResourceTiming, SetNodeAttributeURLBased, SetNodeAttribute } from "../common/messages.js";
3
- import { hasTag } from "../app/guards.js";
1
+ import { timestamp, isURL } from '../utils.js';
2
+ import { ResourceTiming, SetNodeAttributeURLBased, SetNodeAttribute } from '../app/messages.gen.js';
3
+ import { hasTag } from '../app/guards.js';
4
4
  function resolveURL(url, location = document.location) {
5
5
  url = url.trim();
6
6
  if (url.startsWith('/')) {
@@ -16,19 +16,34 @@ function resolveURL(url, location = document.location) {
16
16
  return location.origin + location.pathname + url;
17
17
  }
18
18
  }
19
- const PLACEHOLDER_SRC = "https://static.openreplay.com/tracker/placeholder.jpeg";
19
+ const PLACEHOLDER_SRC = 'https://static.openreplay.com/tracker/placeholder.jpeg';
20
20
  export default function (app) {
21
21
  function sendPlaceholder(id, node) {
22
- app.send(new SetNodeAttribute(id, "src", PLACEHOLDER_SRC));
22
+ app.send(SetNodeAttribute(id, 'src', PLACEHOLDER_SRC));
23
23
  const { width, height } = node.getBoundingClientRect();
24
- if (!node.hasAttribute("width")) {
25
- app.send(new SetNodeAttribute(id, "width", String(width)));
24
+ if (!node.hasAttribute('width')) {
25
+ app.send(SetNodeAttribute(id, 'width', String(width)));
26
26
  }
27
- if (!node.hasAttribute("height")) {
28
- app.send(new SetNodeAttribute(id, "height", String(height)));
27
+ if (!node.hasAttribute('height')) {
28
+ app.send(SetNodeAttribute(id, 'height', String(height)));
29
29
  }
30
30
  }
31
- const sendImgSrc = app.safe(function () {
31
+ const sendSrcset = function (id, img) {
32
+ const { srcset } = img;
33
+ if (!srcset) {
34
+ return;
35
+ }
36
+ const resolvedSrcset = srcset
37
+ .split(',')
38
+ .map((str) => resolveURL(str))
39
+ .join(',');
40
+ app.send(SetNodeAttribute(id, 'srcset', resolvedSrcset));
41
+ };
42
+ const sendSrc = function (id, img) {
43
+ const src = img.src;
44
+ app.send(SetNodeAttributeURLBased(id, 'src', src, app.getBaseHref()));
45
+ };
46
+ const sendImgAttrs = app.safe(function () {
32
47
  const id = app.nodes.getID(this);
33
48
  if (id === undefined) {
34
49
  return;
@@ -40,46 +55,44 @@ export default function (app) {
40
55
  const resolvedSrc = resolveURL(src || ''); // Src type is null sometimes. - is it true?
41
56
  if (naturalWidth === 0 && naturalHeight === 0) {
42
57
  if (isURL(resolvedSrc)) {
43
- app.send(new ResourceTiming(timestamp(), 0, 0, 0, 0, 0, resolvedSrc, 'img'));
58
+ app.send(ResourceTiming(timestamp(), 0, 0, 0, 0, 0, resolvedSrc, 'img'));
44
59
  }
45
60
  }
46
61
  else if (resolvedSrc.length >= 1e5 || app.sanitizer.isMasked(id)) {
47
62
  sendPlaceholder(id, this);
48
63
  }
49
64
  else {
50
- app.send(new SetNodeAttribute(id, 'src', resolvedSrc));
51
- if (srcset) {
52
- const resolvedSrcset = srcset.split(',').map(str => resolveURL(str)).join(',');
53
- app.send(new SetNodeAttribute(id, 'srcset', resolvedSrcset));
54
- }
65
+ sendSrc(id, this);
66
+ sendSrcset(id, this);
55
67
  }
56
68
  });
57
69
  const observer = new MutationObserver((mutations) => {
58
70
  for (const mutation of mutations) {
59
- if (mutation.type === "attributes") {
71
+ if (mutation.type === 'attributes') {
60
72
  const target = mutation.target;
61
73
  const id = app.nodes.getID(target);
62
74
  if (id === undefined) {
63
75
  return;
64
76
  }
65
- if (mutation.attributeName === "src") {
66
- const src = target.src;
67
- app.send(new SetNodeAttributeURLBased(id, 'src', src, app.getBaseHref()));
77
+ if (mutation.attributeName === 'src') {
78
+ sendSrc(id, target);
68
79
  }
69
- if (mutation.attributeName === "srcset") {
70
- const srcset = target.srcset;
71
- app.send(new SetNodeAttribute(id, 'srcset', srcset));
80
+ if (mutation.attributeName === 'srcset') {
81
+ sendSrcset(id, target);
72
82
  }
73
83
  }
74
84
  }
75
85
  });
86
+ app.attachStopCallback(() => {
87
+ observer.disconnect();
88
+ });
76
89
  app.nodes.attachNodeCallback((node) => {
77
- if (!hasTag(node, "IMG")) {
90
+ if (!hasTag(node, 'IMG')) {
78
91
  return;
79
92
  }
80
- app.nodes.attachElementListener('error', node, sendImgSrc);
81
- app.nodes.attachElementListener('load', node, sendImgSrc);
82
- sendImgSrc.call(node);
83
- observer.observe(node, { attributes: true, attributeFilter: ["src", "srcset"] });
93
+ app.nodes.attachElementListener('error', node, sendImgAttrs.bind(node));
94
+ app.nodes.attachElementListener('load', node, sendImgAttrs.bind(node));
95
+ sendImgAttrs.call(node);
96
+ observer.observe(node, { attributes: true, attributeFilter: ['src', 'srcset'] });
84
97
  });
85
98
  }
@@ -1,4 +1,4 @@
1
- import type App from "../app/index.js";
1
+ import type App from '../app/index.js';
2
2
  declare type TextEditableElement = HTMLInputElement | HTMLTextAreaElement;
3
3
  export declare function getInputLabel(node: TextEditableElement): string;
4
4
  export declare const enum InputMode {
@@ -1,18 +1,18 @@
1
- import { normSpaces, IN_BROWSER, getLabelAttribute, hasOpenreplayAttribute, } from "../utils.js";
2
- import { hasTag } from "../app/guards.js";
3
- import { SetInputTarget, SetInputValue, SetInputChecked } from "../common/messages.js";
1
+ import { normSpaces, IN_BROWSER, getLabelAttribute, hasOpenreplayAttribute } from '../utils.js';
2
+ import { hasTag } from '../app/guards.js';
3
+ import { SetInputTarget, SetInputValue, SetInputChecked } from '../app/messages.gen.js';
4
4
  const INPUT_TYPES = ['text', 'password', 'email', 'search', 'number', 'range', 'date'];
5
5
  function isTextEditable(node) {
6
- if (hasTag(node, "TEXTAREA")) {
6
+ if (hasTag(node, 'TEXTAREA')) {
7
7
  return true;
8
8
  }
9
- if (!hasTag(node, "INPUT")) {
9
+ if (!hasTag(node, 'INPUT')) {
10
10
  return false;
11
11
  }
12
12
  return INPUT_TYPES.includes(node.type);
13
13
  }
14
14
  function isCheckable(node) {
15
- if (!hasTag(node, "INPUT")) {
15
+ if (!hasTag(node, 'INPUT')) {
16
16
  return false;
17
17
  }
18
18
  const type = node.type;
@@ -22,7 +22,7 @@ const labelElementFor = IN_BROWSER && 'labels' in HTMLInputElement.prototype
22
22
  ? (node) => {
23
23
  let p = node;
24
24
  while ((p = p.parentNode) !== null) {
25
- if (hasTag(p, "LABEL")) {
25
+ if (hasTag(p, 'LABEL')) {
26
26
  return p;
27
27
  }
28
28
  }
@@ -34,13 +34,13 @@ const labelElementFor = IN_BROWSER && 'labels' in HTMLInputElement.prototype
34
34
  : (node) => {
35
35
  let p = node;
36
36
  while ((p = p.parentNode) !== null) {
37
- if (hasTag(p, "LABEL")) {
37
+ if (hasTag(p, 'LABEL')) {
38
38
  return p;
39
39
  }
40
40
  }
41
41
  const id = node.id;
42
42
  if (id) {
43
- const labels = document.querySelectorAll('label[for="' + id + '"]');
43
+ const labels = node.ownerDocument.querySelectorAll('label[for="' + id + '"]');
44
44
  if (labels !== null && labels.length === 1) {
45
45
  return labels[0];
46
46
  }
@@ -50,12 +50,13 @@ export function getInputLabel(node) {
50
50
  let label = getLabelAttribute(node);
51
51
  if (label === null) {
52
52
  const labelElement = labelElementFor(node);
53
- label = (labelElement && labelElement.innerText)
54
- || node.placeholder
55
- || node.name
56
- || node.id
57
- || node.className
58
- || node.type;
53
+ label =
54
+ (labelElement && labelElement.innerText) ||
55
+ node.placeholder ||
56
+ node.name ||
57
+ node.id ||
58
+ node.className ||
59
+ node.type;
59
60
  }
60
61
  return normSpaces(label).slice(0, 100);
61
62
  }
@@ -69,7 +70,7 @@ export default function (app, opts) {
69
70
  function sendInputTarget(id, node) {
70
71
  const label = getInputLabel(node);
71
72
  if (label !== '') {
72
- app.send(new SetInputTarget(id, label));
73
+ app.send(SetInputTarget(id, label));
73
74
  }
74
75
  }
75
76
  function sendInputValue(id, node) {
@@ -82,8 +83,7 @@ export default function (app, opts) {
82
83
  (inputMode === 0 /* Plain */ &&
83
84
  ((options.obscureInputNumbers && node.type !== 'date' && /\d\d\d\d/.test(value)) ||
84
85
  (options.obscureInputDates && node.type === 'date') ||
85
- (options.obscureInputEmails &&
86
- (node.type === 'email' || !!~value.indexOf('@')))))) {
86
+ (options.obscureInputEmails && (node.type === 'email' || !!~value.indexOf('@')))))) {
87
87
  inputMode = 1 /* Obscured */;
88
88
  }
89
89
  let mask = 0;
@@ -97,7 +97,7 @@ export default function (app, opts) {
97
97
  value = '';
98
98
  break;
99
99
  }
100
- app.send(new SetInputValue(id, value, mask));
100
+ app.send(SetInputValue(id, value, mask));
101
101
  }
102
102
  const inputValues = new Map();
103
103
  const checkableValues = new Map();
@@ -135,7 +135,7 @@ export default function (app, opts) {
135
135
  }
136
136
  if (checked !== node.checked) {
137
137
  checkableValues.set(id, node.checked);
138
- app.send(new SetInputChecked(id, node.checked));
138
+ app.send(SetInputChecked(id, node.checked));
139
139
  }
140
140
  });
141
141
  });
@@ -146,9 +146,9 @@ export default function (app, opts) {
146
146
  return;
147
147
  }
148
148
  // TODO: support multiple select (?): use selectedOptions; Need send target?
149
- if (hasTag(node, "SELECT")) {
149
+ if (hasTag(node, 'SELECT')) {
150
150
  sendInputValue(id, node);
151
- app.attachEventListener(node, "change", () => {
151
+ app.attachEventListener(node, 'change', () => {
152
152
  sendInputValue(id, node);
153
153
  });
154
154
  }
@@ -159,7 +159,7 @@ export default function (app, opts) {
159
159
  }
160
160
  if (isCheckable(node)) {
161
161
  checkableValues.set(id, node.checked);
162
- app.send(new SetInputChecked(id, node.checked));
162
+ app.send(SetInputChecked(id, node.checked));
163
163
  return;
164
164
  }
165
165
  }));
@@ -1,2 +1,2 @@
1
- import type App from "../app/index.js";
1
+ import type App from '../app/index.js';
2
2
  export default function (app: App): void;
@@ -1,8 +1,8 @@
1
- import { hasTag, isSVGElement } from "../app/guards.js";
2
- import { normSpaces, hasOpenreplayAttribute, getLabelAttribute, } from "../utils.js";
3
- import { MouseMove, MouseClick } from "../common/messages.js";
4
- import { getInputLabel } from "./input.js";
5
- function _getSelector(target) {
1
+ import { hasTag, isSVGElement, isDocument } from '../app/guards.js';
2
+ import { normSpaces, hasOpenreplayAttribute, getLabelAttribute } from '../utils.js';
3
+ import { MouseMove, MouseClick } from '../app/messages.gen.js';
4
+ import { getInputLabel } from './input.js';
5
+ function _getSelector(target, document) {
6
6
  let el = target;
7
7
  let selector = null;
8
8
  do {
@@ -10,9 +10,10 @@ function _getSelector(target) {
10
10
  return `#${el.id}` + (selector ? ` > ${selector}` : '');
11
11
  }
12
12
  selector =
13
- el.className.split(' ')
14
- .map(cn => cn.trim())
15
- .filter(cn => cn !== '')
13
+ el.className
14
+ .split(' ')
15
+ .map((cn) => cn.trim())
16
+ .filter((cn) => cn !== '')
16
17
  .reduce((sel, cn) => `${sel}.${cn}`, el.tagName.toLowerCase()) +
17
18
  (selector ? ` > ${selector}` : '');
18
19
  if (el === document.body) {
@@ -24,23 +25,23 @@ function _getSelector(target) {
24
25
  }
25
26
  function isClickable(element) {
26
27
  const tag = element.tagName.toUpperCase();
27
- return tag === 'BUTTON' ||
28
+ return (tag === 'BUTTON' ||
28
29
  tag === 'A' ||
29
30
  tag === 'LI' ||
30
31
  tag === 'SELECT' ||
31
32
  element.onclick != null ||
32
- element.getAttribute('role') === 'button';
33
+ element.getAttribute('role') === 'button');
33
34
  //|| element.className.includes("btn")
34
- // MBTODO: intersect addEventListener
35
+ // MBTODO: intersept addEventListener
35
36
  }
36
- //TODO: fix (typescript doesn't allow work when the guard is inside the function)
37
- function getTarget(target) {
37
+ //TODO: fix (typescript is not sure about target variable after assignation of svg)
38
+ function getTarget(target, document) {
38
39
  if (target instanceof Element) {
39
- return _getTarget(target);
40
+ return _getTarget(target, document);
40
41
  }
41
42
  return null;
42
43
  }
43
- function _getTarget(target) {
44
+ function _getTarget(target, document) {
44
45
  let element = target;
45
46
  while (element !== null && element !== document.documentElement) {
46
47
  if (hasOpenreplayAttribute(element, 'masked')) {
@@ -64,8 +65,7 @@ function _getTarget(target) {
64
65
  if (tag === 'INPUT') {
65
66
  return element;
66
67
  }
67
- if (isClickable(element) ||
68
- getLabelAttribute(element) !== null) {
68
+ if (isClickable(element) || getLabelAttribute(element) !== null) {
69
69
  return element;
70
70
  }
71
71
  element = element.parentElement;
@@ -78,7 +78,7 @@ export default function (app) {
78
78
  if (dl !== null) {
79
79
  return dl;
80
80
  }
81
- if (hasTag(target, "INPUT")) {
81
+ if (hasTag(target, 'INPUT')) {
82
82
  return getInputLabel(target);
83
83
  }
84
84
  if (isClickable(target)) {
@@ -104,39 +104,46 @@ export default function (app) {
104
104
  });
105
105
  const sendMouseMove = () => {
106
106
  if (mousePositionChanged) {
107
- app.send(new MouseMove(mousePositionX, mousePositionY));
107
+ app.send(MouseMove(mousePositionX, mousePositionY));
108
108
  mousePositionChanged = false;
109
109
  }
110
110
  };
111
- const selectorMap = {};
112
- function getSelector(id, target) {
113
- return selectorMap[id] = selectorMap[id] || _getSelector(target);
114
- }
115
- app.attachEventListener(document.documentElement, 'mouseover', (e) => {
116
- const target = getTarget(e.target);
117
- if (target !== mouseTarget) {
118
- mouseTarget = target;
119
- mouseTargetTime = performance.now();
120
- }
121
- });
122
- app.attachEventListener(document, 'mousemove', (e) => {
123
- mousePositionX = e.clientX;
124
- mousePositionY = e.clientY;
125
- mousePositionChanged = true;
126
- }, false);
127
- app.attachEventListener(document, 'click', (e) => {
128
- const target = getTarget(e.target);
129
- if ((!e.clientX && !e.clientY) || target === null) {
130
- return;
111
+ const patchDocument = (document) => {
112
+ const selectorMap = {};
113
+ function getSelector(id, target) {
114
+ return (selectorMap[id] = selectorMap[id] || _getSelector(target, document));
131
115
  }
132
- const id = app.nodes.getID(target);
133
- if (id !== undefined) {
134
- sendMouseMove();
135
- app.send(new MouseClick(id, mouseTarget === target
136
- ? Math.round(performance.now() - mouseTargetTime)
137
- : 0, getTargetLabel(target), getSelector(id, target)), true);
116
+ app.attachEventListener(document.documentElement, 'mouseover', (e) => {
117
+ const target = getTarget(e.target, document);
118
+ if (target !== mouseTarget) {
119
+ mouseTarget = target;
120
+ mouseTargetTime = performance.now();
121
+ }
122
+ });
123
+ app.attachEventListener(document, 'mousemove', (e) => {
124
+ const { top, left } = app.observer.getDocumentOffset(document);
125
+ mousePositionX = e.clientX + left;
126
+ mousePositionY = e.clientY + top;
127
+ mousePositionChanged = true;
128
+ }, false);
129
+ app.attachEventListener(document, 'click', (e) => {
130
+ const target = getTarget(e.target, document);
131
+ if ((!e.clientX && !e.clientY) || target === null) {
132
+ return;
133
+ }
134
+ const id = app.nodes.getID(target);
135
+ if (id !== undefined) {
136
+ sendMouseMove();
137
+ app.send(MouseClick(id, mouseTarget === target ? Math.round(performance.now() - mouseTargetTime) : 0, getTargetLabel(target), getSelector(id, target)), true);
138
+ }
139
+ mouseTarget = null;
140
+ });
141
+ };
142
+ app.nodes.attachNodeCallback((node) => {
143
+ if (isDocument(node)) {
144
+ patchDocument(node);
138
145
  }
139
- mouseTarget = null;
140
146
  });
147
+ patchDocument(document);
141
148
  app.ticker.attach(sendMouseMove, 10);
142
149
  }
@@ -1,4 +1,4 @@
1
- import type App from "../app/index.js";
1
+ import type App from '../app/index.js';
2
2
  export declare const deviceMemory: number;
3
3
  export declare const jsHeapSizeLimit: number;
4
4
  export interface Options {
@@ -1,5 +1,5 @@
1
- import { IN_BROWSER } from "../utils.js";
2
- import { PerformanceTrack } from "../common/messages.js";
1
+ import { IN_BROWSER } from '../utils.js';
2
+ import { PerformanceTrack } from '../app/messages.gen.js';
3
3
  const perf = IN_BROWSER && 'performance' in window && 'memory' in performance // works in Chrome only
4
4
  ? performance
5
5
  : { memory: {} };
@@ -31,7 +31,7 @@ export default function (app, opts) {
31
31
  if (frames === undefined || ticks === undefined) {
32
32
  return;
33
33
  }
34
- app.send(new PerformanceTrack(frames, ticks, perf.memory.totalJSHeapSize || 0, perf.memory.usedJSHeapSize || 0));
34
+ app.send(PerformanceTrack(frames, ticks, perf.memory.totalJSHeapSize || 0, perf.memory.usedJSHeapSize || 0));
35
35
  ticks = frames = document.hidden ? -1 : 0;
36
36
  };
37
37
  app.attachStartCallback(() => {
@@ -1,2 +1,2 @@
1
- import type App from "../app/index.js";
1
+ import type App from '../app/index.js';
2
2
  export default function (app: App): void;
@@ -1,9 +1,14 @@
1
- import { SetViewportScroll, SetNodeScroll } from "../common/messages.js";
2
- import { isElementNode } from "../app/guards.js";
1
+ import { SetViewportScroll, SetNodeScroll } from '../app/messages.gen.js';
2
+ import { isElementNode, isRootNode } from '../app/guards.js';
3
3
  export default function (app) {
4
4
  let documentScroll = false;
5
5
  const nodeScroll = new Map();
6
- const sendSetViewportScroll = app.safe(() => app.send(new SetViewportScroll(window.pageXOffset ||
6
+ function setNodeScroll(target) {
7
+ if (target instanceof Element) {
8
+ nodeScroll.set(target, [target.scrollLeft, target.scrollTop]);
9
+ }
10
+ }
11
+ const sendSetViewportScroll = app.safe(() => app.send(SetViewportScroll(window.pageXOffset ||
7
12
  (document.documentElement && document.documentElement.scrollLeft) ||
8
13
  (document.body && document.body.scrollLeft) ||
9
14
  0, window.pageYOffset ||
@@ -13,7 +18,7 @@ export default function (app) {
13
18
  const sendSetNodeScroll = app.safe((s, node) => {
14
19
  const id = app.nodes.getID(node);
15
20
  if (id !== undefined) {
16
- app.send(new SetNodeScroll(id, s[0], s[1]));
21
+ app.send(SetNodeScroll(id, s[0], s[1]));
17
22
  }
18
23
  });
19
24
  app.attachStartCallback(sendSetViewportScroll);
@@ -25,16 +30,20 @@ export default function (app) {
25
30
  if (isStart && isElementNode(node) && node.scrollLeft + node.scrollTop > 0) {
26
31
  nodeScroll.set(node, [node.scrollLeft, node.scrollTop]);
27
32
  }
33
+ else if (isRootNode(node)) {
34
+ // scroll is not-composed event (https://javascript.info/shadow-dom-events)
35
+ app.attachEventListener(node, 'scroll', (e) => {
36
+ setNodeScroll(e.target);
37
+ });
38
+ }
28
39
  });
29
- app.attachEventListener(window, 'scroll', (e) => {
40
+ app.attachEventListener(document, 'scroll', (e) => {
30
41
  const target = e.target;
31
42
  if (target === document) {
32
43
  documentScroll = true;
33
44
  return;
34
45
  }
35
- if (target instanceof Element) {
36
- nodeScroll.set(target, [target.scrollLeft, target.scrollTop]);
37
- }
46
+ setNodeScroll(target);
38
47
  });
39
48
  app.ticker.attach(() => {
40
49
  if (documentScroll) {
@@ -1,4 +1,4 @@
1
- import type App from "../app/index.js";
1
+ import type App from '../app/index.js';
2
2
  export interface Options {
3
3
  captureResourceTimings: boolean;
4
4
  capturePageLoadTimings: boolean;
@@ -1,6 +1,6 @@
1
- import { hasTag } from "../app/guards.js";
2
- import { isURL } from "../utils.js";
3
- import { ResourceTiming, PageLoadTiming, PageRenderTiming } from "../common/messages.js";
1
+ import { hasTag } from '../app/guards.js';
2
+ import { isURL } from '../utils.js';
3
+ import { ResourceTiming, PageLoadTiming, PageRenderTiming } from '../app/messages.gen.js';
4
4
  function getPaintBlocks(resources) {
5
5
  const paintBlocks = [];
6
6
  const elements = document.getElementsByTagName('*');
@@ -8,7 +8,7 @@ function getPaintBlocks(resources) {
8
8
  for (let i = 0; i < elements.length; i++) {
9
9
  const element = elements[i];
10
10
  let src = '';
11
- if (hasTag(element, "IMG")) {
11
+ if (hasTag(element, 'IMG')) {
12
12
  src = element.currentSrc || element.src;
13
13
  }
14
14
  if (!src) {
@@ -34,9 +34,7 @@ function getPaintBlocks(resources) {
34
34
  const bottom = Math.min(rect.bottom, window.innerHeight ||
35
35
  (document.documentElement && document.documentElement.clientHeight) ||
36
36
  0);
37
- const right = Math.min(rect.right, window.innerWidth ||
38
- (document.documentElement && document.documentElement.clientWidth) ||
39
- 0);
37
+ const right = Math.min(rect.right, window.innerWidth || (document.documentElement && document.documentElement.clientWidth) || 0);
40
38
  if (bottom <= top || right <= left)
41
39
  continue;
42
40
  const area = (bottom - top) * (right - left);
@@ -46,8 +44,7 @@ function getPaintBlocks(resources) {
46
44
  }
47
45
  function calculateSpeedIndex(firstContentfulPaint, paintBlocks) {
48
46
  let a = (Math.max((document.documentElement && document.documentElement.clientWidth) || 0, window.innerWidth || 0) *
49
- Math.max((document.documentElement && document.documentElement.clientHeight) ||
50
- 0, window.innerHeight || 0)) /
47
+ Math.max((document.documentElement && document.documentElement.clientHeight) || 0, window.innerHeight || 0)) /
51
48
  10;
52
49
  let s = a * firstContentfulPaint;
53
50
  for (let i = 0; i < paintBlocks.length; i++) {
@@ -76,16 +73,13 @@ export default function (app, opts) {
76
73
  if (resources !== null) {
77
74
  resources[entry.name] = entry.startTime + entry.duration;
78
75
  }
79
- app.send(new ResourceTiming(entry.startTime + performance.timing.navigationStart, entry.duration, entry.responseStart && entry.startTime
80
- ? entry.responseStart - entry.startTime
81
- : 0, entry.transferSize > entry.encodedBodySize
82
- ? entry.transferSize - entry.encodedBodySize
83
- : 0, entry.encodedBodySize || 0, entry.decodedBodySize || 0, entry.name, entry.initiatorType));
76
+ app.send(ResourceTiming(entry.startTime + performance.timing.navigationStart, 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));
84
77
  }
85
78
  const observer = new PerformanceObserver((list) => list.getEntries().forEach(resourceTiming));
86
79
  let prevSessionID;
87
80
  app.attachStartCallback(function ({ sessionID }) {
88
- if (sessionID !== prevSessionID) { // Send past page resources on a newly started session
81
+ if (sessionID !== prevSessionID) {
82
+ // Send past page resources on a newly started session
89
83
  performance.getEntriesByType('resource').forEach(resourceTiming);
90
84
  prevSessionID = sessionID;
91
85
  }
@@ -102,9 +96,7 @@ export default function (app, opts) {
102
96
  return;
103
97
  }
104
98
  if (firstPaint === 0 || firstContentfulPaint === 0) {
105
- performance
106
- .getEntriesByType('paint')
107
- .forEach((entry) => {
99
+ performance.getEntriesByType('paint').forEach((entry) => {
108
100
  const { name, startTime } = entry;
109
101
  switch (name) {
110
102
  case 'first-paint':
@@ -119,7 +111,7 @@ export default function (app, opts) {
119
111
  if (performance.timing.loadEventEnd || performance.now() > 30000) {
120
112
  pageLoadTimingSent = true;
121
113
  const { navigationStart, requestStart, responseStart, responseEnd, domContentLoadedEventStart, domContentLoadedEventEnd, loadEventStart, loadEventEnd, } = performance.timing;
122
- app.send(new PageLoadTiming(requestStart - navigationStart || 0, responseStart - navigationStart || 0, responseEnd - navigationStart || 0, domContentLoadedEventStart - navigationStart || 0, domContentLoadedEventEnd - navigationStart || 0, loadEventStart - navigationStart || 0, loadEventEnd - navigationStart || 0, firstPaint, firstContentfulPaint));
114
+ app.send(PageLoadTiming(requestStart - navigationStart || 0, responseStart - navigationStart || 0, responseEnd - navigationStart || 0, domContentLoadedEventStart - navigationStart || 0, domContentLoadedEventEnd - navigationStart || 0, loadEventStart - navigationStart || 0, loadEventEnd - navigationStart || 0, firstPaint, firstContentfulPaint));
123
115
  }
124
116
  }, 30);
125
117
  }
@@ -142,23 +134,19 @@ export default function (app, opts) {
142
134
  if (time - interactiveWindowTickTime > 50) {
143
135
  interactiveWindowStartTime = time;
144
136
  }
145
- interactiveWindowTickTime =
146
- time - interactiveWindowStartTime > 5000 ? null : time;
137
+ interactiveWindowTickTime = time - interactiveWindowStartTime > 5000 ? null : time;
147
138
  }
148
- if ((paintBlocks !== null && interactiveWindowTickTime === null) ||
149
- time > 30000) {
139
+ if ((paintBlocks !== null && interactiveWindowTickTime === null) || time > 30000) {
150
140
  pageRenderTimingSent = true;
151
141
  resources = null;
152
142
  const speedIndex = paintBlocks === null
153
143
  ? 0
154
144
  : calculateSpeedIndex(firstContentfulPaint || firstPaint, paintBlocks);
155
145
  const timeToInteractive = interactiveWindowTickTime === null
156
- ? Math.max(interactiveWindowStartTime, firstContentfulPaint, performance.timing.domContentLoadedEventEnd -
157
- performance.timing.navigationStart || 0)
146
+ ? Math.max(interactiveWindowStartTime, firstContentfulPaint, performance.timing.domContentLoadedEventEnd - performance.timing.navigationStart ||
147
+ 0)
158
148
  : 0;
159
- app.send(new PageRenderTiming(speedIndex, firstContentfulPaint > visuallyComplete
160
- ? firstContentfulPaint
161
- : visuallyComplete, timeToInteractive));
149
+ app.send(PageRenderTiming(speedIndex, firstContentfulPaint > visuallyComplete ? firstContentfulPaint : visuallyComplete, timeToInteractive));
162
150
  }
163
151
  });
164
152
  }
@@ -1,2 +1,2 @@
1
- import type App from "../app/index.js";
1
+ import type App from '../app/index.js';
2
2
  export default function (app: App): void;
@@ -1,4 +1,4 @@
1
- import { SetPageLocation, SetViewportSize, SetPageVisibility, } from "../common/messages.js";
1
+ import { SetPageLocation, SetViewportSize, SetPageVisibility } from '../app/messages.gen.js';
2
2
  export default function (app) {
3
3
  let url, width, height;
4
4
  let navigationStart = performance.timing.navigationStart;
@@ -6,7 +6,7 @@ export default function (app) {
6
6
  const { URL } = document;
7
7
  if (URL !== url) {
8
8
  url = URL;
9
- app.send(new SetPageLocation(url, document.referrer, navigationStart));
9
+ app.send(SetPageLocation(url, document.referrer, navigationStart));
10
10
  navigationStart = 0;
11
11
  }
12
12
  });
@@ -15,12 +15,12 @@ export default function (app) {
15
15
  if (innerWidth !== width || innerHeight !== height) {
16
16
  width = innerWidth;
17
17
  height = innerHeight;
18
- app.send(new SetViewportSize(width, height));
18
+ app.send(SetViewportSize(width, height));
19
19
  }
20
20
  });
21
21
  const sendSetPageVisibility = document.hidden === undefined
22
22
  ? Function.prototype
23
- : app.safe(() => app.send(new SetPageVisibility(document.hidden)));
23
+ : app.safe(() => app.send(SetPageVisibility(document.hidden)));
24
24
  app.attachStartCallback(() => {
25
25
  url = '';
26
26
  width = height = -1;