@openreplay/tracker 3.6.1 → 3.6.2

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/LICENSE +1 -1
  2. package/cjs/app/guards.d.ts +1 -2
  3. package/cjs/app/guards.js +3 -6
  4. package/cjs/app/index.d.ts +23 -28
  5. package/cjs/app/index.js +86 -107
  6. package/cjs/app/logger.js +3 -6
  7. package/cjs/app/nodes.d.ts +1 -1
  8. package/cjs/app/nodes.js +0 -2
  9. package/cjs/app/observer/iframe_observer.d.ts +1 -1
  10. package/cjs/app/observer/iframe_observer.js +3 -3
  11. package/cjs/app/observer/observer.d.ts +3 -2
  12. package/cjs/app/observer/observer.js +52 -50
  13. package/cjs/app/observer/shadow_root_observer.d.ts +1 -1
  14. package/cjs/app/observer/shadow_root_observer.js +3 -3
  15. package/cjs/app/observer/top_observer.d.ts +2 -13
  16. package/cjs/app/observer/top_observer.js +23 -58
  17. package/cjs/app/sanitizer.d.ts +1 -1
  18. package/cjs/app/sanitizer.js +5 -5
  19. package/cjs/app/session.d.ts +2 -20
  20. package/cjs/app/session.js +6 -65
  21. package/cjs/app/ticker.d.ts +1 -1
  22. package/cjs/common/messages.d.ts +444 -0
  23. package/cjs/common/messages.js +794 -0
  24. package/cjs/common/types.d.ts +9 -0
  25. package/cjs/common/{interaction.js → types.js} +0 -0
  26. package/cjs/common/{interaction.d.ts → webworker.d.ts} +5 -5
  27. package/cjs/common/{messages.gen.js → webworker.js} +0 -1
  28. package/cjs/index.d.ts +9 -10
  29. package/cjs/index.js +36 -47
  30. package/cjs/modules/connection.d.ts +1 -1
  31. package/cjs/modules/connection.js +2 -2
  32. package/cjs/modules/console.d.ts +1 -1
  33. package/cjs/modules/console.js +21 -7
  34. package/cjs/modules/cssrules.d.ts +1 -1
  35. package/cjs/modules/cssrules.js +14 -18
  36. package/cjs/modules/exception.d.ts +3 -3
  37. package/cjs/modules/exception.js +18 -23
  38. package/cjs/modules/img.d.ts +1 -1
  39. package/cjs/modules/img.js +26 -39
  40. package/cjs/modules/input.d.ts +1 -1
  41. package/cjs/modules/input.js +21 -21
  42. package/cjs/modules/longtasks.d.ts +2 -0
  43. package/cjs/modules/longtasks.js +26 -0
  44. package/cjs/modules/mouse.d.ts +1 -1
  45. package/cjs/modules/mouse.js +43 -50
  46. package/cjs/modules/performance.d.ts +1 -1
  47. package/cjs/modules/performance.js +2 -2
  48. package/cjs/modules/scroll.d.ts +1 -1
  49. package/cjs/modules/scroll.js +7 -16
  50. package/cjs/modules/timing.d.ts +1 -1
  51. package/cjs/modules/timing.js +26 -14
  52. package/cjs/modules/viewport.d.ts +1 -1
  53. package/cjs/modules/viewport.js +4 -4
  54. package/cjs/utils.js +7 -7
  55. package/cjs/vendors/finder/finder.js +48 -53
  56. package/lib/app/guards.d.ts +1 -2
  57. package/lib/app/guards.js +2 -4
  58. package/lib/app/index.d.ts +23 -28
  59. package/lib/app/index.js +94 -115
  60. package/lib/app/logger.js +3 -6
  61. package/lib/app/nodes.d.ts +1 -1
  62. package/lib/app/nodes.js +0 -2
  63. package/lib/app/observer/iframe_observer.d.ts +1 -1
  64. package/lib/app/observer/iframe_observer.js +3 -3
  65. package/lib/app/observer/observer.d.ts +3 -2
  66. package/lib/app/observer/observer.js +53 -51
  67. package/lib/app/observer/shadow_root_observer.d.ts +1 -1
  68. package/lib/app/observer/shadow_root_observer.js +3 -3
  69. package/lib/app/observer/top_observer.d.ts +2 -13
  70. package/lib/app/observer/top_observer.js +27 -62
  71. package/lib/app/sanitizer.d.ts +1 -1
  72. package/lib/app/sanitizer.js +7 -7
  73. package/lib/app/session.d.ts +2 -20
  74. package/lib/app/session.js +6 -65
  75. package/lib/app/ticker.d.ts +1 -1
  76. package/lib/common/messages.d.ts +444 -0
  77. package/lib/common/messages.js +790 -0
  78. package/lib/common/tsconfig.tsbuildinfo +1 -1
  79. package/lib/common/types.d.ts +9 -0
  80. package/lib/common/{interaction.js → types.js} +0 -0
  81. package/lib/common/{interaction.d.ts → webworker.d.ts} +5 -5
  82. package/lib/common/webworker.js +1 -0
  83. package/lib/index.d.ts +9 -10
  84. package/lib/index.js +49 -60
  85. package/lib/modules/connection.d.ts +1 -1
  86. package/lib/modules/connection.js +2 -2
  87. package/lib/modules/console.d.ts +1 -1
  88. package/lib/modules/console.js +22 -8
  89. package/lib/modules/cssrules.d.ts +1 -1
  90. package/lib/modules/cssrules.js +15 -19
  91. package/lib/modules/exception.d.ts +3 -3
  92. package/lib/modules/exception.js +18 -23
  93. package/lib/modules/img.d.ts +1 -1
  94. package/lib/modules/img.js +28 -41
  95. package/lib/modules/input.d.ts +1 -1
  96. package/lib/modules/input.js +23 -23
  97. package/lib/modules/longtasks.d.ts +2 -0
  98. package/lib/modules/longtasks.js +23 -0
  99. package/lib/modules/mouse.d.ts +1 -1
  100. package/lib/modules/mouse.js +46 -53
  101. package/lib/modules/performance.d.ts +1 -1
  102. package/lib/modules/performance.js +3 -3
  103. package/lib/modules/scroll.d.ts +1 -1
  104. package/lib/modules/scroll.js +8 -17
  105. package/lib/modules/timing.d.ts +1 -1
  106. package/lib/modules/timing.js +28 -16
  107. package/lib/modules/viewport.d.ts +1 -1
  108. package/lib/modules/viewport.js +4 -4
  109. package/lib/utils.js +7 -7
  110. package/lib/vendors/finder/finder.js +48 -53
  111. package/package.json +10 -27
  112. package/.eslintignore +0 -8
  113. package/.prettierignore +0 -1
  114. package/cjs/app/messages.d.ts +0 -52
  115. package/cjs/app/messages.gen.d.ts +0 -57
  116. package/cjs/app/messages.gen.js +0 -493
  117. package/cjs/app/messages.js +0 -234
  118. package/cjs/common/messages.gen.d.ts +0 -382
  119. package/cjs/modules/adoptedStyleSheets.d.ts +0 -2
  120. package/cjs/modules/adoptedStyleSheets.js +0 -127
  121. package/lib/app/messages.d.ts +0 -52
  122. package/lib/app/messages.gen.d.ts +0 -57
  123. package/lib/app/messages.gen.js +0 -434
  124. package/lib/app/messages.js +0 -181
  125. package/lib/common/messages.gen.d.ts +0 -382
  126. package/lib/common/messages.gen.js +0 -2
  127. package/lib/modules/adoptedStyleSheets.d.ts +0 -2
  128. package/lib/modules/adoptedStyleSheets.js +0 -124
@@ -1,5 +1,5 @@
1
- import { RemoveNodeAttribute, SetNodeAttribute, SetNodeAttributeURLBased, SetCSSDataURLBased, SetNodeData, CreateTextNode, CreateElementNode, MoveNode, RemoveNode, } from '../messages.gen.js';
2
- import { isRootNode, isTextNode, isElementNode, isSVGElement, hasTag } from '../guards.js';
1
+ import { RemoveNodeAttribute, SetNodeAttribute, SetNodeAttributeURLBased, SetCSSDataURLBased, SetNodeData, CreateTextNode, CreateElementNode, MoveNode, RemoveNode, } from "../../common/messages.js";
2
+ import { isRootNode, isTextNode, isElementNode, isSVGElement, hasTag, } from "../guards.js";
3
3
  function isIgnored(node) {
4
4
  if (isTextNode(node)) {
5
5
  return false;
@@ -11,9 +11,13 @@ function isIgnored(node) {
11
11
  if (tag === 'LINK') {
12
12
  const rel = node.getAttribute('rel');
13
13
  const as = node.getAttribute('as');
14
- return !((rel === null || rel === void 0 ? void 0 : rel.includes('stylesheet')) || as === 'style' || as === 'font');
14
+ return !((rel === null || rel === void 0 ? void 0 : rel.includes('stylesheet')) || as === "style" || as === "font");
15
15
  }
16
- return (tag === 'SCRIPT' || tag === 'NOSCRIPT' || tag === 'META' || tag === 'TITLE' || tag === 'BASE');
16
+ return (tag === 'SCRIPT' ||
17
+ tag === 'NOSCRIPT' ||
18
+ tag === 'META' ||
19
+ tag === 'TITLE' ||
20
+ tag === 'BASE');
17
21
  }
18
22
  function isObservable(node) {
19
23
  if (isRootNode(node)) {
@@ -26,11 +30,17 @@ function isObservable(node) {
26
30
  - fix unbinding logic + send all removals first (ensure sequence is correct)
27
31
  - use document as a 0-node in the upper context (should be updated in player at first)
28
32
  */
33
+ /*
34
+ Nikita:
35
+ - rn we only send unbind event for parent (all child nodes will be cut in the live replay anyways)
36
+ to prevent sending 1k+ unbinds for child nodes and making replay file bigger than it should be
37
+ */
29
38
  var RecentsType;
30
39
  (function (RecentsType) {
31
40
  RecentsType[RecentsType["New"] = 0] = "New";
32
41
  RecentsType[RecentsType["Removed"] = 1] = "Removed";
33
42
  RecentsType[RecentsType["Changed"] = 2] = "Changed";
43
+ RecentsType[RecentsType["RemovedChild"] = 3] = "RemovedChild";
34
44
  })(RecentsType || (RecentsType = {}));
35
45
  export default class Observer {
36
46
  constructor(app, isTopContext = false) {
@@ -42,8 +52,7 @@ export default class Observer {
42
52
  this.attributesMap = new Map();
43
53
  this.textSet = new Set();
44
54
  this.observer = new MutationObserver(this.app.safe((mutations) => {
45
- for (const mutation of mutations) {
46
- // mutations order is sequential
55
+ for (const mutation of mutations) { // mutations order is sequential
47
56
  const target = mutation.target;
48
57
  const type = mutation.type;
49
58
  if (!isObservable(target)) {
@@ -51,10 +60,7 @@ export default class Observer {
51
60
  }
52
61
  if (type === 'childList') {
53
62
  for (let i = 0; i < mutation.removedNodes.length; i++) {
54
- // Should be the same as bindTree(mutation.removedNodes[i]), but logic needs to be be untied
55
- if (isObservable(mutation.removedNodes[i])) {
56
- this.bindNode(mutation.removedNodes[i]);
57
- }
63
+ this.bindTree(mutation.removedNodes[i], true);
58
64
  }
59
65
  for (let i = 0; i < mutation.addedNodes.length; i++) {
60
66
  this.bindTree(mutation.addedNodes[i]);
@@ -75,7 +81,7 @@ export default class Observer {
75
81
  }
76
82
  let attr = this.attributesMap.get(id);
77
83
  if (attr === undefined) {
78
- this.attributesMap.set(id, (attr = new Set()));
84
+ this.attributesMap.set(id, attr = new Set());
79
85
  }
80
86
  attr.add(name);
81
87
  continue;
@@ -101,16 +107,16 @@ export default class Observer {
101
107
  name = name.substr(6);
102
108
  }
103
109
  if (value === null) {
104
- this.app.send(RemoveNodeAttribute(id, name));
110
+ this.app.send(new RemoveNodeAttribute(id, name));
105
111
  }
106
112
  else if (name === 'href') {
107
113
  if (value.length > 1e5) {
108
114
  value = '';
109
115
  }
110
- this.app.send(SetNodeAttributeURLBased(id, name, value, this.app.getBaseHref()));
116
+ this.app.send(new SetNodeAttributeURLBased(id, name, value, this.app.getBaseHref()));
111
117
  }
112
118
  else {
113
- this.app.send(SetNodeAttribute(id, name, value));
119
+ this.app.send(new SetNodeAttribute(id, name, value));
114
120
  }
115
121
  return;
116
122
  }
@@ -123,75 +129,72 @@ export default class Observer {
123
129
  return;
124
130
  }
125
131
  if (name === 'value' &&
126
- hasTag(node, 'INPUT') &&
132
+ hasTag(node, "INPUT") &&
127
133
  node.type !== 'button' &&
128
134
  node.type !== 'reset' &&
129
135
  node.type !== 'submit') {
130
136
  return;
131
137
  }
132
138
  if (value === null) {
133
- this.app.send(RemoveNodeAttribute(id, name));
139
+ this.app.send(new RemoveNodeAttribute(id, name));
134
140
  return;
135
141
  }
136
- if (name === 'style' || (name === 'href' && hasTag(node, 'LINK'))) {
137
- this.app.send(SetNodeAttributeURLBased(id, name, value, this.app.getBaseHref()));
142
+ if (name === 'style' || name === 'href' && hasTag(node, "LINK")) {
143
+ this.app.send(new SetNodeAttributeURLBased(id, name, value, this.app.getBaseHref()));
138
144
  return;
139
145
  }
140
146
  if (name === 'href' || value.length > 1e5) {
141
147
  value = '';
142
148
  }
143
- this.app.send(SetNodeAttribute(id, name, value));
149
+ this.app.send(new SetNodeAttribute(id, name, value));
144
150
  }
145
151
  sendNodeData(id, parentElement, data) {
146
- if (hasTag(parentElement, 'STYLE') || hasTag(parentElement, 'style')) {
147
- this.app.send(SetCSSDataURLBased(id, data, this.app.getBaseHref()));
152
+ if (hasTag(parentElement, "STYLE") || hasTag(parentElement, "style")) {
153
+ this.app.send(new SetCSSDataURLBased(id, data, this.app.getBaseHref()));
148
154
  return;
149
155
  }
150
156
  data = this.app.sanitizer.sanitize(id, data);
151
- this.app.send(SetNodeData(id, data));
157
+ this.app.send(new SetNodeData(id, data));
152
158
  }
153
159
  bindNode(node) {
154
160
  const [id, isNew] = this.app.nodes.registerNode(node);
155
161
  if (isNew) {
156
162
  this.recents.set(id, RecentsType.New);
157
163
  }
158
- else if (this.recents.get(id) !== RecentsType.New) {
164
+ else if (this.recents.get(id) !== RecentsType.New) { // can we do just `else` here?
159
165
  this.recents.set(id, RecentsType.Removed);
160
166
  }
161
167
  }
162
- bindTree(node) {
168
+ unbindChildNode(node) {
169
+ const [id] = this.app.nodes.registerNode(node);
170
+ this.recents.set(id, RecentsType.RemovedChild);
171
+ }
172
+ bindTree(node, isChildUnbinding = false) {
163
173
  if (!isObservable(node)) {
164
174
  return;
165
175
  }
166
176
  this.bindNode(node);
167
177
  const walker = document.createTreeWalker(node, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_TEXT, {
168
- acceptNode: (node) => isIgnored(node) || this.app.nodes.getID(node) !== undefined
178
+ acceptNode: (node) => isIgnored(node)
179
+ || (this.app.nodes.getID(node) !== undefined && !isChildUnbinding)
169
180
  ? NodeFilter.FILTER_REJECT
170
181
  : NodeFilter.FILTER_ACCEPT,
171
182
  },
172
183
  // @ts-ignore
173
184
  false);
174
185
  while (walker.nextNode()) {
175
- this.bindNode(walker.currentNode);
186
+ if (isChildUnbinding) {
187
+ this.unbindChildNode(walker.currentNode);
188
+ }
189
+ else {
190
+ this.bindNode(walker.currentNode);
191
+ }
176
192
  }
177
193
  }
178
- unbindTree(node) {
194
+ unbindNode(node) {
179
195
  const id = this.app.nodes.unregisterNode(node);
180
196
  if (id !== undefined && this.recents.get(id) === RecentsType.Removed) {
181
- // Sending RemoveNode only for parent to maintain
182
- this.app.send(RemoveNode(id));
183
- // Unregistering all the children in order to clear the memory
184
- const walker = document.createTreeWalker(node, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_TEXT, {
185
- acceptNode: (node) => isIgnored(node) || this.app.nodes.getID(node) === undefined
186
- ? NodeFilter.FILTER_REJECT
187
- : NodeFilter.FILTER_ACCEPT,
188
- },
189
- // @ts-ignore
190
- false);
191
- while (walker.nextNode()) {
192
- this.app.nodes.unregisterNode(walker.currentNode);
193
- }
194
- // MBTODO: count and send RemovedNodesCount (for the page crash detection in heuristics)
197
+ this.app.send(new RemoveNode(id));
195
198
  }
196
199
  }
197
200
  // A top-consumption function on the infinite lists test. (~1% of performance resources)
@@ -204,20 +207,20 @@ export default class Observer {
204
207
  // Disable parent check for the upper context HTMLHtmlElement, because it is root there... (before)
205
208
  // TODO: get rid of "special" cases (there is an issue with CreateDocument altered behaviour though)
206
209
  // TODO: Clean the logic (though now it workd fine)
207
- if (!hasTag(node, 'HTML') || !this.isTopContext) {
210
+ if (!hasTag(node, "HTML") || !this.isTopContext) {
208
211
  if (parent === null) {
209
212
  // Sometimes one observation contains attribute mutations for the removimg node, which gets ignored here.
210
- // That shouldn't affect the visual rendering ( should it? maybe when transition applied? )
211
- this.unbindTree(node);
213
+ // That shouldn't affect the visual rendering ( should it? )
214
+ this.unbindNode(node);
212
215
  return false;
213
216
  }
214
217
  parentID = this.app.nodes.getID(parent);
215
218
  if (parentID === undefined) {
216
- this.unbindTree(node);
219
+ this.unbindNode(node);
217
220
  return false;
218
221
  }
219
222
  if (!this.commitNode(parentID)) {
220
- this.unbindTree(node);
223
+ this.unbindNode(node);
221
224
  return false;
222
225
  }
223
226
  this.app.sanitizer.handleNode(id, parentID, node);
@@ -256,7 +259,7 @@ export default class Observer {
256
259
  el.style.width = width + 'px';
257
260
  el.style.height = height + 'px';
258
261
  }
259
- this.app.send(CreateElementNode(id, parentID, index, el.tagName, isSVGElement(node)));
262
+ this.app.send(new CreateElementNode(id, parentID, index, el.tagName, isSVGElement(node)));
260
263
  }
261
264
  for (let i = 0; i < el.attributes.length; i++) {
262
265
  const attr = el.attributes[i];
@@ -265,13 +268,13 @@ export default class Observer {
265
268
  }
266
269
  else if (isTextNode(node)) {
267
270
  // for text node id != 0, hence parentID !== undefined and parent is Element
268
- this.app.send(CreateTextNode(id, parentID, index));
271
+ this.app.send(new CreateTextNode(id, parentID, index));
269
272
  this.sendNodeData(id, parent, node.data);
270
273
  }
271
274
  return true;
272
275
  }
273
276
  if (recentsType === RecentsType.Removed && parentID !== undefined) {
274
- this.app.send(MoveNode(id, parentID, index));
277
+ this.app.send(new MoveNode(id, parentID, index));
275
278
  }
276
279
  const attr = this.attributesMap.get(id);
277
280
  if (attr !== undefined) {
@@ -312,8 +315,7 @@ export default class Observer {
312
315
  });
313
316
  this.clear();
314
317
  }
315
- // ISSSUE (nodeToBinde should be the same as node. Look at the comment about 0-node at the beginning of the file.)
316
- // TODO: use one observer instance for all iframes/shadowRoots (composition instiad of inheritance)
318
+ // ISSSUE
317
319
  observeRoot(node, beforeCommit, nodeToBind = node) {
318
320
  this.observer.observe(node, {
319
321
  childList: true,
@@ -1,4 +1,4 @@
1
- import Observer from './observer.js';
1
+ import Observer from "./observer.js";
2
2
  export default class ShadowRootObserver extends Observer {
3
3
  observe(el: Element): void;
4
4
  }
@@ -1,5 +1,5 @@
1
- import Observer from './observer.js';
2
- import { CreateIFrameDocument } from '../messages.gen.js';
1
+ import Observer from "./observer.js";
2
+ import { CreateIFrameDocument } from "../../common/messages.js";
3
3
  export default class ShadowRootObserver extends Observer {
4
4
  observe(el) {
5
5
  const shRoot = el.shadowRoot;
@@ -9,7 +9,7 @@ export default class ShadowRootObserver extends Observer {
9
9
  } // log
10
10
  this.observeRoot(shRoot, (rootID) => {
11
11
  if (rootID === undefined) {
12
- console.log('OpenReplay: Shadow Root was not bound');
12
+ console.log("OpenReplay: Shadow Root was not bound");
13
13
  return;
14
14
  }
15
15
  this.app.send(CreateIFrameDocument(hostID, rootID));
@@ -1,21 +1,11 @@
1
- import Observer from './observer.js';
2
- import App from '../index.js';
1
+ import Observer from "./observer.js";
2
+ import App from "../index.js";
3
3
  export interface Options {
4
4
  captureIFrames: boolean;
5
5
  }
6
- declare type Context = Window & typeof globalThis;
7
- declare type ContextCallback = (context: Context) => void;
8
- declare type Offset = {
9
- top: number;
10
- left: number;
11
- };
12
6
  export default class TopObserver extends Observer {
13
7
  private readonly options;
14
8
  constructor(app: App, options: Partial<Options>);
15
- private readonly contextCallbacks;
16
- private readonly contextsSet;
17
- attachContextCallback(cb: ContextCallback): void;
18
- getDocumentOffset(doc: Document): Offset;
19
9
  private iframeObservers;
20
10
  private handleIframe;
21
11
  private shadowRootObservers;
@@ -23,4 +13,3 @@ export default class TopObserver extends Observer {
23
13
  observe(): void;
24
14
  disconnect(): void;
25
15
  }
26
- export {};
@@ -1,86 +1,52 @@
1
- import Observer from './observer.js';
2
- import { isElementNode, hasTag } from '../guards.js';
3
- import IFrameObserver from './iframe_observer.js';
4
- import ShadowRootObserver from './shadow_root_observer.js';
5
- import { CreateDocument } from '../messages.gen.js';
1
+ import Observer from "./observer.js";
2
+ import { isElementNode, hasTag, } from "../guards.js";
3
+ import IFrameObserver from "./iframe_observer.js";
4
+ import ShadowRootObserver from "./shadow_root_observer.js";
5
+ import { CreateDocument } from "../../common/messages.js";
6
6
  import { IN_BROWSER, hasOpenreplayAttribute } from '../../utils.js';
7
- function isPatchedDocument(doc) {
8
- // @ts-ignore
9
- return typeof doc.__openreplay__getOffset === 'function';
10
- }
11
7
  const attachShadowNativeFn = IN_BROWSER ? Element.prototype.attachShadow : () => new ShadowRoot();
12
8
  export default class TopObserver extends Observer {
13
9
  constructor(app, options) {
14
10
  super(app, true);
15
- this.contextCallbacks = [];
16
- // Attached once per Tracker instance
17
- this.contextsSet = new Set();
18
11
  this.iframeObservers = [];
19
12
  this.shadowRootObservers = [];
20
13
  this.options = Object.assign({
21
- captureIFrames: true,
14
+ captureIFrames: true
22
15
  }, options);
23
16
  // IFrames
24
- this.app.nodes.attachNodeCallback((node) => {
25
- if (hasTag(node, 'IFRAME') &&
26
- ((this.options.captureIFrames && !hasOpenreplayAttribute(node, 'obscured')) ||
27
- hasOpenreplayAttribute(node, 'capture'))) {
17
+ this.app.nodes.attachNodeCallback(node => {
18
+ if (hasTag(node, "IFRAME") &&
19
+ ((this.options.captureIFrames && !hasOpenreplayAttribute(node, "obscured"))
20
+ || hasOpenreplayAttribute(node, "capture"))) {
28
21
  this.handleIframe(node);
29
22
  }
30
23
  });
31
24
  // ShadowDOM
32
- this.app.nodes.attachNodeCallback((node) => {
25
+ this.app.nodes.attachNodeCallback(node => {
33
26
  if (isElementNode(node) && node.shadowRoot !== null) {
34
27
  this.handleShadowRoot(node.shadowRoot);
35
28
  }
36
29
  });
37
30
  }
38
- attachContextCallback(cb) {
39
- this.contextCallbacks.push(cb);
40
- }
41
- // Le truc
42
- getDocumentOffset(doc) {
43
- if (isPatchedDocument(doc)) {
44
- return doc.__openreplay__getOffset();
45
- }
46
- return { top: 0, left: 0 };
47
- }
48
31
  handleIframe(iframe) {
49
32
  let doc = null;
50
- let win = null;
51
33
  const handle = this.app.safe(() => {
52
34
  const id = this.app.nodes.getID(iframe);
53
35
  if (id === undefined) {
54
- //log
36
+ return;
37
+ } //log
38
+ if (iframe.contentDocument === doc) {
39
+ return;
40
+ } // How frequently can it happen?
41
+ doc = iframe.contentDocument;
42
+ if (!doc || !iframe.contentWindow) {
55
43
  return;
56
44
  }
57
- const currentWin = iframe.contentWindow;
58
- const currentDoc = iframe.contentDocument;
59
- if (currentDoc && currentDoc !== doc) {
60
- const observer = new IFrameObserver(this.app);
61
- this.iframeObservers.push(observer);
62
- observer.observe(iframe);
63
- doc = currentDoc;
64
- doc.__openreplay__getOffset = () => {
65
- const { top, left } = this.getDocumentOffset(iframe.ownerDocument);
66
- return {
67
- top: iframe.offsetTop + top,
68
- left: iframe.offsetLeft + left,
69
- };
70
- };
71
- }
72
- if (currentWin &&
73
- // Sometimes currentWin.window is null (not in specification). Such window object is not functional
74
- currentWin === currentWin.window &&
75
- !this.contextsSet.has(currentWin) // for each context callbacks called once per Tracker (TopObserver) instance
76
- ) {
77
- this.contextsSet.add(currentWin);
78
- //@ts-ignore https://github.com/microsoft/TypeScript/issues/41684
79
- this.contextCallbacks.forEach((cb) => cb(currentWin));
80
- win = currentWin;
81
- }
45
+ const observer = new IFrameObserver(this.app);
46
+ this.iframeObservers.push(observer);
47
+ observer.observe(iframe);
82
48
  });
83
- iframe.addEventListener('load', handle); // why app.attachEventListener not working?
49
+ iframe.addEventListener("load", handle); // why app.attachEventListener not working?
84
50
  handle();
85
51
  }
86
52
  handleShadowRoot(shRoot) {
@@ -92,26 +58,25 @@ export default class TopObserver extends Observer {
92
58
  // Protection from several subsequent calls?
93
59
  const observer = this;
94
60
  Element.prototype.attachShadow = function () {
95
- // eslint-disable-next-line
96
61
  const shadow = attachShadowNativeFn.apply(this, arguments);
97
62
  observer.handleShadowRoot(shadow);
98
63
  return shadow;
99
64
  };
100
65
  // Can observe documentElement (<html>) here, because it is not supposed to be changing.
101
66
  // However, it is possible in some exotic cases and may cause an ignorance of the newly created <html>
102
- // In this case context.document have to be observed, but this will cause
103
- // the change in the re-player behaviour caused by CreateDocument message:
67
+ // In this case context.document have to be observed, but this will cause
68
+ // the change in the re-player behaviour caused by CreateDocument message:
104
69
  // the 0-node ("fRoot") will become #document rather than documentElement as it is now.
105
70
  // Alternatively - observe(#document) then bindNode(documentElement)
106
71
  this.observeRoot(window.document, () => {
107
- this.app.send(CreateDocument());
72
+ this.app.send(new CreateDocument());
108
73
  }, window.document.documentElement);
109
74
  }
110
75
  disconnect() {
111
76
  Element.prototype.attachShadow = attachShadowNativeFn;
112
- this.iframeObservers.forEach((o) => o.disconnect());
77
+ this.iframeObservers.forEach(o => o.disconnect());
113
78
  this.iframeObservers = [];
114
- this.shadowRootObservers.forEach((o) => o.disconnect());
79
+ this.shadowRootObservers.forEach(o => o.disconnect());
115
80
  this.shadowRootObservers = [];
116
81
  super.disconnect();
117
82
  }
@@ -1,4 +1,4 @@
1
- import type App from './index.js';
1
+ import type App from "./index.js";
2
2
  export interface Options {
3
3
  obscureTextEmails: boolean;
4
4
  obscureTextNumbers: boolean;
@@ -1,5 +1,5 @@
1
- import { stars, hasOpenreplayAttribute } from '../utils.js';
2
- import { isElementNode } from './guards.js';
1
+ import { stars, hasOpenreplayAttribute } from "../utils.js";
2
+ import { isElementNode } from "./guards.js";
3
3
  export default class Sanitizer {
4
4
  constructor(app, options) {
5
5
  this.app = app;
@@ -12,20 +12,20 @@ export default class Sanitizer {
12
12
  }
13
13
  handleNode(id, parentID, node) {
14
14
  if (this.masked.has(parentID) ||
15
- (isElementNode(node) && hasOpenreplayAttribute(node, 'masked'))) {
15
+ (isElementNode(node) &&
16
+ hasOpenreplayAttribute(node, 'masked'))) {
16
17
  this.masked.add(id);
17
18
  }
18
19
  if (this.maskedContainers.has(parentID) ||
19
- (isElementNode(node) && hasOpenreplayAttribute(node, 'htmlmasked'))) {
20
+ (isElementNode(node) &&
21
+ hasOpenreplayAttribute(node, 'htmlmasked'))) {
20
22
  this.maskedContainers.add(id);
21
23
  }
22
24
  }
23
25
  sanitize(id, data) {
24
26
  if (this.masked.has(id)) {
25
27
  // TODO: is it the best place to put trim() ? Might trimmed spaces be considered in layout in certain cases?
26
- return data
27
- .trim()
28
- .replace(/[^\f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]/g, '█');
28
+ return data.trim().replace(/[^\f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]/g, '█');
29
29
  }
30
30
  if (this.options.obscureTextNumbers) {
31
31
  data = data.replace(/\d/g, '0');
@@ -1,37 +1,19 @@
1
- import type App from './index.js';
2
1
  interface SessionInfo {
3
- sessionID: string | undefined;
2
+ sessionID: string | null;
4
3
  metadata: Record<string, string>;
5
4
  userID: string | null;
6
- timestamp: number;
7
- projectID?: string;
8
5
  }
9
6
  declare type OnUpdateCallback = (i: Partial<SessionInfo>) => void;
10
- export declare type Options = {
11
- session_token_key: string;
12
- session_pageno_key: string;
13
- };
14
7
  export default class Session {
15
- private readonly app;
16
- private readonly options;
17
8
  private metadata;
18
9
  private userID;
19
10
  private sessionID;
20
- private readonly callbacks;
21
- private timestamp;
22
- private projectID;
23
- constructor(app: App, options: Options);
11
+ private callbacks;
24
12
  attachUpdateCallback(cb: OnUpdateCallback): void;
25
13
  private handleUpdate;
26
14
  update(newInfo: Partial<SessionInfo>): void;
27
15
  setMetadata(key: string, value: string): void;
28
16
  setUserID(userID: string): void;
29
- private getPageNumber;
30
- incPageNo(): number;
31
- getSessionToken(): string | undefined;
32
- setSessionToken(token: string): void;
33
- applySessionHash(hash: string): void;
34
- getSessionHash(): string | undefined;
35
17
  getInfo(): SessionInfo;
36
18
  reset(): void;
37
19
  }
@@ -1,11 +1,9 @@
1
1
  export default class Session {
2
- constructor(app, options) {
3
- this.app = app;
4
- this.options = options;
2
+ constructor() {
5
3
  this.metadata = {};
6
4
  this.userID = null;
5
+ this.sessionID = null;
7
6
  this.callbacks = [];
8
- this.timestamp = 0;
9
7
  }
10
8
  attachUpdateCallback(cb) {
11
9
  this.callbacks.push(cb);
@@ -17,25 +15,18 @@ export default class Session {
17
15
  if (newInfo.sessionID == null) {
18
16
  delete newInfo.sessionID;
19
17
  }
20
- this.callbacks.forEach((cb) => cb(newInfo));
18
+ this.callbacks.forEach(cb => cb(newInfo));
21
19
  }
22
20
  update(newInfo) {
23
- if (newInfo.userID !== undefined) {
24
- // TODO clear nullable/undefinable types
21
+ if (newInfo.userID !== undefined) { // TODO clear nullable/undefinable types
25
22
  this.userID = newInfo.userID;
26
23
  }
27
24
  if (newInfo.metadata !== undefined) {
28
- Object.entries(newInfo.metadata).forEach(([k, v]) => (this.metadata[k] = v));
25
+ Object.entries(newInfo.metadata).forEach(([k, v]) => this.metadata[k] = v);
29
26
  }
30
27
  if (newInfo.sessionID !== undefined) {
31
28
  this.sessionID = newInfo.sessionID;
32
29
  }
33
- if (newInfo.timestamp !== undefined) {
34
- this.timestamp = newInfo.timestamp;
35
- }
36
- if (newInfo.projectID !== undefined) {
37
- this.projectID = newInfo.projectID;
38
- }
39
30
  this.handleUpdate(newInfo);
40
31
  }
41
32
  setMetadata(key, value) {
@@ -46,66 +37,16 @@ export default class Session {
46
37
  this.userID = userID;
47
38
  this.handleUpdate({ userID });
48
39
  }
49
- getPageNumber() {
50
- const pageNoStr = this.app.sessionStorage.getItem(this.options.session_pageno_key);
51
- if (pageNoStr == null) {
52
- return undefined;
53
- }
54
- return parseInt(pageNoStr);
55
- }
56
- incPageNo() {
57
- let pageNo = this.getPageNumber();
58
- if (pageNo === undefined) {
59
- pageNo = 0;
60
- }
61
- else {
62
- pageNo++;
63
- }
64
- this.app.sessionStorage.setItem(this.options.session_pageno_key, pageNo.toString());
65
- return pageNo;
66
- }
67
- getSessionToken() {
68
- return this.app.sessionStorage.getItem(this.options.session_token_key) || undefined;
69
- }
70
- setSessionToken(token) {
71
- this.app.sessionStorage.setItem(this.options.session_token_key, token);
72
- }
73
- applySessionHash(hash) {
74
- const hashParts = decodeURI(hash).split('&');
75
- let token = hash;
76
- let pageNoStr = '100500'; // back-compat for sessionToken
77
- if (hashParts.length == 2) {
78
- ;
79
- [token, pageNoStr] = hashParts;
80
- }
81
- if (!pageNoStr || !token) {
82
- return;
83
- }
84
- this.app.sessionStorage.setItem(this.options.session_token_key, token);
85
- this.app.sessionStorage.setItem(this.options.session_pageno_key, pageNoStr);
86
- }
87
- getSessionHash() {
88
- const pageNo = this.getPageNumber();
89
- const token = this.getSessionToken();
90
- if (pageNo === undefined || token === undefined) {
91
- return;
92
- }
93
- return encodeURI(String(pageNo) + '&' + token);
94
- }
95
40
  getInfo() {
96
41
  return {
97
42
  sessionID: this.sessionID,
98
43
  metadata: this.metadata,
99
44
  userID: this.userID,
100
- timestamp: this.timestamp,
101
- projectID: this.projectID,
102
45
  };
103
46
  }
104
47
  reset() {
105
- this.app.sessionStorage.removeItem(this.options.session_token_key);
106
48
  this.metadata = {};
107
49
  this.userID = null;
108
- this.sessionID = undefined;
109
- this.timestamp = 0;
50
+ this.sessionID = null;
110
51
  }
111
52
  }
@@ -1,4 +1,4 @@
1
- import App from './index.js';
1
+ import App from "./index.js";
2
2
  declare type Callback = () => void;
3
3
  export default class Ticker {
4
4
  private readonly app;