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