@openreplay/tracker 5.0.2-beta → 5.0.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 +21 -0
- package/cjs/app/guards.js +37 -0
- package/cjs/app/index.d.ts +118 -0
- package/cjs/app/index.js +438 -0
- package/cjs/app/logger.d.ts +26 -0
- package/cjs/app/logger.js +45 -0
- package/cjs/app/messages.gen.d.ts +63 -0
- package/cjs/app/messages.gen.js +551 -0
- package/cjs/app/nodes.d.ts +18 -0
- package/cjs/app/nodes.js +82 -0
- package/cjs/app/observer/iframe_observer.d.ts +4 -0
- package/cjs/app/observer/iframe_observer.js +23 -0
- package/cjs/app/observer/iframe_offsets.d.ts +8 -0
- package/cjs/app/observer/iframe_offsets.js +59 -0
- package/cjs/app/observer/observer.d.ts +23 -0
- package/cjs/app/observer/observer.js +340 -0
- package/cjs/app/observer/shadow_root_observer.d.ts +4 -0
- package/cjs/app/observer/shadow_root_observer.js +21 -0
- package/cjs/app/observer/top_observer.d.ts +24 -0
- package/cjs/app/observer/top_observer.js +113 -0
- package/cjs/app/sanitizer.d.ts +24 -0
- package/cjs/app/sanitizer.js +76 -0
- package/cjs/app/session.d.ts +38 -0
- package/cjs/app/session.js +114 -0
- package/cjs/app/ticker.d.ts +12 -0
- package/cjs/app/ticker.js +42 -0
- package/cjs/common/interaction.d.ts +24 -0
- package/cjs/common/interaction.js +2 -0
- package/cjs/common/messages.gen.d.ts +427 -0
- package/cjs/common/messages.gen.js +4 -0
- package/cjs/index.d.ts +47 -0
- package/cjs/index.js +254 -0
- package/cjs/modules/connection.d.ts +2 -0
- package/cjs/modules/connection.js +15 -0
- package/cjs/modules/console.d.ts +6 -0
- package/cjs/modules/console.js +119 -0
- package/cjs/modules/constructedStyleSheets.d.ts +4 -0
- package/cjs/modules/constructedStyleSheets.js +131 -0
- package/cjs/modules/cssrules.d.ts +2 -0
- package/cjs/modules/cssrules.js +99 -0
- package/cjs/modules/exception.d.ts +16 -0
- package/cjs/modules/exception.js +77 -0
- package/cjs/modules/focus.d.ts +2 -0
- package/cjs/modules/focus.js +45 -0
- package/cjs/modules/fonts.d.ts +2 -0
- package/cjs/modules/fonts.js +57 -0
- package/cjs/modules/img.d.ts +2 -0
- package/cjs/modules/img.js +110 -0
- package/cjs/modules/input.d.ts +16 -0
- package/cjs/modules/input.js +163 -0
- package/cjs/modules/mouse.d.ts +2 -0
- package/cjs/modules/mouse.js +148 -0
- package/cjs/modules/network.d.ts +28 -0
- package/cjs/modules/network.js +203 -0
- package/cjs/modules/performance.d.ts +7 -0
- package/cjs/modules/performance.js +53 -0
- package/cjs/modules/scroll.d.ts +2 -0
- package/cjs/modules/scroll.js +79 -0
- package/cjs/modules/timing.d.ts +7 -0
- package/cjs/modules/timing.js +160 -0
- package/cjs/modules/viewport.d.ts +2 -0
- package/cjs/modules/viewport.js +43 -0
- package/cjs/package.json +1 -0
- package/cjs/utils.d.ts +13 -0
- package/cjs/utils.js +71 -0
- package/cjs/vendors/finder/finder.d.ts +12 -0
- package/cjs/vendors/finder/finder.js +352 -0
- package/lib/app/guards.d.ts +21 -0
- package/lib/app/guards.js +26 -0
- package/lib/app/index.d.ts +118 -0
- package/lib/app/index.js +434 -0
- package/lib/app/logger.d.ts +26 -0
- package/lib/app/logger.js +41 -0
- package/lib/app/messages.gen.d.ts +63 -0
- package/lib/app/messages.gen.js +486 -0
- package/lib/app/nodes.d.ts +18 -0
- package/lib/app/nodes.js +79 -0
- package/lib/app/observer/iframe_observer.d.ts +4 -0
- package/lib/app/observer/iframe_observer.js +20 -0
- package/lib/app/observer/iframe_offsets.d.ts +8 -0
- package/lib/app/observer/iframe_offsets.js +56 -0
- package/lib/app/observer/observer.d.ts +23 -0
- package/lib/app/observer/observer.js +337 -0
- package/lib/app/observer/shadow_root_observer.d.ts +4 -0
- package/lib/app/observer/shadow_root_observer.js +18 -0
- package/lib/app/observer/top_observer.d.ts +24 -0
- package/lib/app/observer/top_observer.js +110 -0
- package/lib/app/sanitizer.d.ts +24 -0
- package/lib/app/sanitizer.js +72 -0
- package/lib/app/session.d.ts +38 -0
- package/lib/app/session.js +111 -0
- package/lib/app/ticker.d.ts +12 -0
- package/lib/app/ticker.js +39 -0
- package/lib/common/interaction.d.ts +24 -0
- package/lib/common/interaction.js +1 -0
- package/lib/common/messages.gen.d.ts +427 -0
- package/lib/common/messages.gen.js +3 -0
- package/lib/common/tsconfig.tsbuildinfo +1 -0
- package/lib/index.d.ts +47 -0
- package/lib/index.js +248 -0
- package/lib/modules/connection.d.ts +2 -0
- package/lib/modules/connection.js +12 -0
- package/lib/modules/console.d.ts +6 -0
- package/lib/modules/console.js +116 -0
- package/lib/modules/constructedStyleSheets.d.ts +4 -0
- package/lib/modules/constructedStyleSheets.js +126 -0
- package/lib/modules/cssrules.d.ts +2 -0
- package/lib/modules/cssrules.js +97 -0
- package/lib/modules/exception.d.ts +16 -0
- package/lib/modules/exception.js +71 -0
- package/lib/modules/focus.d.ts +2 -0
- package/lib/modules/focus.js +42 -0
- package/lib/modules/fonts.d.ts +2 -0
- package/lib/modules/fonts.js +54 -0
- package/lib/modules/img.d.ts +2 -0
- package/lib/modules/img.js +107 -0
- package/lib/modules/input.d.ts +16 -0
- package/lib/modules/input.js +158 -0
- package/lib/modules/mouse.d.ts +2 -0
- package/lib/modules/mouse.js +145 -0
- package/lib/modules/network.d.ts +28 -0
- package/lib/modules/network.js +200 -0
- package/lib/modules/performance.d.ts +7 -0
- package/lib/modules/performance.js +49 -0
- package/lib/modules/scroll.d.ts +2 -0
- package/lib/modules/scroll.js +76 -0
- package/lib/modules/timing.d.ts +7 -0
- package/lib/modules/timing.js +157 -0
- package/lib/modules/viewport.d.ts +2 -0
- package/lib/modules/viewport.js +40 -0
- package/lib/utils.d.ts +13 -0
- package/lib/utils.js +61 -0
- package/lib/vendors/finder/finder.d.ts +12 -0
- package/lib/vendors/finder/finder.js +348 -0
- package/package.json +1 -1
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const messages_gen_js_1 = require("../app/messages.gen.js");
|
|
4
|
+
const utils_js_1 = require("../utils.js");
|
|
5
|
+
function getXHRRequestDataObject(xhr) {
|
|
6
|
+
// @ts-ignore this is 3x faster than using Map<XHR, XHRRequestData>
|
|
7
|
+
if (!xhr.__or_req_data__) {
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
xhr.__or_req_data__ = { body: undefined, headers: {} };
|
|
10
|
+
}
|
|
11
|
+
// @ts-ignore
|
|
12
|
+
return xhr.__or_req_data__;
|
|
13
|
+
}
|
|
14
|
+
function strMethod(method) {
|
|
15
|
+
return typeof method === 'string' ? method.toUpperCase() : 'GET';
|
|
16
|
+
}
|
|
17
|
+
function default_1(app, opts = {}) {
|
|
18
|
+
const options = Object.assign({
|
|
19
|
+
failuresOnly: false,
|
|
20
|
+
ignoreHeaders: ['Cookie', 'Set-Cookie', 'Authorization'],
|
|
21
|
+
capturePayload: false,
|
|
22
|
+
sessionTokenHeader: false,
|
|
23
|
+
}, opts);
|
|
24
|
+
const ignoreHeaders = options.ignoreHeaders;
|
|
25
|
+
const isHIgnored = Array.isArray(ignoreHeaders)
|
|
26
|
+
? (name) => ignoreHeaders.includes(name)
|
|
27
|
+
: () => ignoreHeaders;
|
|
28
|
+
const stHeader = options.sessionTokenHeader === true ? 'X-OpenReplay-SessionToken' : options.sessionTokenHeader;
|
|
29
|
+
function setSessionTokenHeader(setRequestHeader) {
|
|
30
|
+
if (stHeader) {
|
|
31
|
+
const sessionToken = app.getSessionToken();
|
|
32
|
+
if (sessionToken) {
|
|
33
|
+
app.safe(setRequestHeader)(stHeader, sessionToken);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function sanitize(reqResInfo) {
|
|
38
|
+
if (!options.capturePayload) {
|
|
39
|
+
delete reqResInfo.request.body;
|
|
40
|
+
delete reqResInfo.response.body;
|
|
41
|
+
}
|
|
42
|
+
if (options.sanitizer) {
|
|
43
|
+
const resBody = reqResInfo.response.body;
|
|
44
|
+
if (typeof resBody === 'string') {
|
|
45
|
+
// Parse response in order to have handy view in sanitisation function
|
|
46
|
+
try {
|
|
47
|
+
reqResInfo.response.body = JSON.parse(resBody);
|
|
48
|
+
}
|
|
49
|
+
catch (_a) { }
|
|
50
|
+
}
|
|
51
|
+
return options.sanitizer(reqResInfo);
|
|
52
|
+
}
|
|
53
|
+
return reqResInfo;
|
|
54
|
+
}
|
|
55
|
+
function stringify(r) {
|
|
56
|
+
if (r && typeof r.body !== 'string') {
|
|
57
|
+
try {
|
|
58
|
+
r.body = JSON.stringify(r.body);
|
|
59
|
+
}
|
|
60
|
+
catch (_a) {
|
|
61
|
+
r.body = '<unable to stringify>';
|
|
62
|
+
app.notify.warn("Openreplay fetch couldn't stringify body:", r.body);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return JSON.stringify(r);
|
|
66
|
+
}
|
|
67
|
+
/* ====== Fetch ====== */
|
|
68
|
+
const origFetch = window.fetch.bind(window);
|
|
69
|
+
window.fetch = (input, init = {}) => {
|
|
70
|
+
if (!(typeof input === 'string' || input instanceof URL) || app.isServiceURL(String(input))) {
|
|
71
|
+
return origFetch(input, init);
|
|
72
|
+
}
|
|
73
|
+
setSessionTokenHeader(function (name, value) {
|
|
74
|
+
if (init.headers === undefined) {
|
|
75
|
+
init.headers = {};
|
|
76
|
+
}
|
|
77
|
+
if (init.headers instanceof Headers) {
|
|
78
|
+
init.headers.append(name, value);
|
|
79
|
+
}
|
|
80
|
+
else if (Array.isArray(init.headers)) {
|
|
81
|
+
init.headers.push([name, value]);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
init.headers[name] = value;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
const startTime = performance.now();
|
|
88
|
+
return origFetch(input, init).then((response) => {
|
|
89
|
+
const duration = performance.now() - startTime;
|
|
90
|
+
if (options.failuresOnly && response.status < 400) {
|
|
91
|
+
return response;
|
|
92
|
+
}
|
|
93
|
+
const r = response.clone();
|
|
94
|
+
r.text()
|
|
95
|
+
.then((text) => {
|
|
96
|
+
const reqHs = {};
|
|
97
|
+
const resHs = {};
|
|
98
|
+
if (ignoreHeaders !== true) {
|
|
99
|
+
// request headers
|
|
100
|
+
const writeReqHeader = ([n, v]) => {
|
|
101
|
+
if (!isHIgnored(n)) {
|
|
102
|
+
reqHs[n] = v;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
if (init.headers instanceof Headers) {
|
|
106
|
+
init.headers.forEach((v, n) => writeReqHeader([n, v]));
|
|
107
|
+
}
|
|
108
|
+
else if (Array.isArray(init.headers)) {
|
|
109
|
+
init.headers.forEach(writeReqHeader);
|
|
110
|
+
}
|
|
111
|
+
else if (typeof init.headers === 'object') {
|
|
112
|
+
Object.entries(init.headers).forEach(writeReqHeader);
|
|
113
|
+
}
|
|
114
|
+
// response headers
|
|
115
|
+
r.headers.forEach((v, n) => {
|
|
116
|
+
if (!isHIgnored(n))
|
|
117
|
+
resHs[n] = v;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
const method = strMethod(init.method);
|
|
121
|
+
const reqResInfo = sanitize({
|
|
122
|
+
url: String(input),
|
|
123
|
+
method,
|
|
124
|
+
status: r.status,
|
|
125
|
+
request: {
|
|
126
|
+
headers: reqHs,
|
|
127
|
+
body: init.body,
|
|
128
|
+
},
|
|
129
|
+
response: {
|
|
130
|
+
headers: resHs,
|
|
131
|
+
body: text,
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
if (!reqResInfo) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
app.send((0, messages_gen_js_1.NetworkRequest)('fetch', method, String(reqResInfo.url), stringify(reqResInfo.request), stringify(reqResInfo.response), r.status, startTime + (0, utils_js_1.getTimeOrigin)(), duration));
|
|
138
|
+
})
|
|
139
|
+
.catch((e) => app.debug.error('Could not process Fetch response:', e));
|
|
140
|
+
return response;
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
/* ====== <> ====== */
|
|
144
|
+
/* ====== XHR ====== */
|
|
145
|
+
const nativeOpen = XMLHttpRequest.prototype.open;
|
|
146
|
+
XMLHttpRequest.prototype.open = function (initMethod, url) {
|
|
147
|
+
const xhr = this;
|
|
148
|
+
setSessionTokenHeader((name, value) => xhr.setRequestHeader(name, value));
|
|
149
|
+
let startTime = 0;
|
|
150
|
+
xhr.addEventListener('loadstart', (e) => {
|
|
151
|
+
startTime = e.timeStamp;
|
|
152
|
+
});
|
|
153
|
+
xhr.addEventListener('load', app.safe((e) => {
|
|
154
|
+
const { headers: reqHs, body: reqBody } = getXHRRequestDataObject(xhr);
|
|
155
|
+
const duration = startTime > 0 ? e.timeStamp - startTime : 0;
|
|
156
|
+
const hString = ignoreHeaders ? '' : xhr.getAllResponseHeaders(); // might be null (though only if no response received though)
|
|
157
|
+
const resHs = hString
|
|
158
|
+
? hString
|
|
159
|
+
.split('\r\n')
|
|
160
|
+
.map((h) => h.split(':'))
|
|
161
|
+
.filter((entry) => !isHIgnored(entry[0]))
|
|
162
|
+
.reduce((hds, [name, value]) => (Object.assign(Object.assign({}, hds), { [name]: value })), {})
|
|
163
|
+
: {};
|
|
164
|
+
const method = strMethod(initMethod);
|
|
165
|
+
const reqResInfo = sanitize({
|
|
166
|
+
url: String(url),
|
|
167
|
+
method,
|
|
168
|
+
status: xhr.status,
|
|
169
|
+
request: {
|
|
170
|
+
headers: reqHs,
|
|
171
|
+
body: reqBody,
|
|
172
|
+
},
|
|
173
|
+
response: {
|
|
174
|
+
headers: resHs,
|
|
175
|
+
body: xhr.response,
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
if (!reqResInfo) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
app.send((0, messages_gen_js_1.NetworkRequest)('xhr', method, String(reqResInfo.url), stringify(reqResInfo.request), stringify(reqResInfo.response), xhr.status, startTime + (0, utils_js_1.getTimeOrigin)(), duration));
|
|
182
|
+
}));
|
|
183
|
+
//TODO: handle error (though it has no Error API nor any useful information)
|
|
184
|
+
//xhr.addEventListener('error', (e) => {})
|
|
185
|
+
return nativeOpen.apply(this, arguments);
|
|
186
|
+
};
|
|
187
|
+
const nativeSend = XMLHttpRequest.prototype.send;
|
|
188
|
+
XMLHttpRequest.prototype.send = function (body) {
|
|
189
|
+
const rdo = getXHRRequestDataObject(this);
|
|
190
|
+
rdo.body = body;
|
|
191
|
+
return nativeSend.apply(this, arguments);
|
|
192
|
+
};
|
|
193
|
+
const nativeSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
|
|
194
|
+
XMLHttpRequest.prototype.setRequestHeader = function (name, value) {
|
|
195
|
+
if (!isHIgnored(name)) {
|
|
196
|
+
const rdo = getXHRRequestDataObject(this);
|
|
197
|
+
rdo.headers[name] = value;
|
|
198
|
+
}
|
|
199
|
+
return nativeSetRequestHeader.apply(this, arguments);
|
|
200
|
+
};
|
|
201
|
+
/* ====== <> ====== */
|
|
202
|
+
}
|
|
203
|
+
exports.default = default_1;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.jsHeapSizeLimit = exports.deviceMemory = void 0;
|
|
4
|
+
const utils_js_1 = require("../utils.js");
|
|
5
|
+
const messages_gen_js_1 = require("../app/messages.gen.js");
|
|
6
|
+
const perf = utils_js_1.IN_BROWSER && 'performance' in window && 'memory' in performance // works in Chrome only
|
|
7
|
+
? performance
|
|
8
|
+
: { memory: {} };
|
|
9
|
+
exports.deviceMemory = utils_js_1.IN_BROWSER ? (navigator.deviceMemory || 0) * 1024 : 0;
|
|
10
|
+
exports.jsHeapSizeLimit = perf.memory.jsHeapSizeLimit || 0;
|
|
11
|
+
function default_1(app, opts) {
|
|
12
|
+
const options = Object.assign({
|
|
13
|
+
capturePerformance: true,
|
|
14
|
+
}, opts);
|
|
15
|
+
if (!options.capturePerformance) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
let frames;
|
|
19
|
+
let ticks;
|
|
20
|
+
const nextFrame = () => {
|
|
21
|
+
if (frames === undefined || frames === -1) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
frames++;
|
|
25
|
+
requestAnimationFrame(nextFrame);
|
|
26
|
+
};
|
|
27
|
+
app.ticker.attach(() => {
|
|
28
|
+
if (ticks === undefined || ticks === -1) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
ticks++;
|
|
32
|
+
}, 0, false);
|
|
33
|
+
const sendPerformanceTrack = () => {
|
|
34
|
+
if (frames === undefined || ticks === undefined) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
app.send((0, messages_gen_js_1.PerformanceTrack)(frames, ticks, perf.memory.totalJSHeapSize || 0, perf.memory.usedJSHeapSize || 0));
|
|
38
|
+
ticks = frames = document.hidden ? -1 : 0;
|
|
39
|
+
};
|
|
40
|
+
app.attachStartCallback(() => {
|
|
41
|
+
ticks = frames = -1;
|
|
42
|
+
sendPerformanceTrack();
|
|
43
|
+
nextFrame();
|
|
44
|
+
});
|
|
45
|
+
app.attachStopCallback(() => {
|
|
46
|
+
ticks = frames = undefined;
|
|
47
|
+
});
|
|
48
|
+
app.ticker.attach(sendPerformanceTrack, 40, false);
|
|
49
|
+
if (document.hidden !== undefined) {
|
|
50
|
+
app.attachEventListener(document, 'visibilitychange', sendPerformanceTrack, false, false);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.default = default_1;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const messages_gen_js_1 = require("../app/messages.gen.js");
|
|
4
|
+
const guards_js_1 = require("../app/guards.js");
|
|
5
|
+
function getDocumentScroll(doc) {
|
|
6
|
+
const win = doc.defaultView;
|
|
7
|
+
return [
|
|
8
|
+
(win && win.pageXOffset) ||
|
|
9
|
+
(doc.documentElement && doc.documentElement.scrollLeft) ||
|
|
10
|
+
(doc.body && doc.body.scrollLeft) ||
|
|
11
|
+
0,
|
|
12
|
+
(win && win.pageYOffset) ||
|
|
13
|
+
(doc.documentElement && doc.documentElement.scrollTop) ||
|
|
14
|
+
(doc.body && doc.body.scrollTop) ||
|
|
15
|
+
0,
|
|
16
|
+
];
|
|
17
|
+
}
|
|
18
|
+
function default_1(app) {
|
|
19
|
+
let documentScroll = false;
|
|
20
|
+
const nodeScroll = new Map();
|
|
21
|
+
function setNodeScroll(target) {
|
|
22
|
+
if (!(0, guards_js_1.isNode)(target)) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if ((0, guards_js_1.isElementNode)(target)) {
|
|
26
|
+
nodeScroll.set(target, [target.scrollLeft, target.scrollTop]);
|
|
27
|
+
}
|
|
28
|
+
if ((0, guards_js_1.isDocument)(target)) {
|
|
29
|
+
nodeScroll.set(target, getDocumentScroll(target));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const sendSetViewportScroll = app.safe(() => app.send((0, messages_gen_js_1.SetViewportScroll)(...getDocumentScroll(document))));
|
|
33
|
+
const sendSetNodeScroll = app.safe((s, node) => {
|
|
34
|
+
const id = app.nodes.getID(node);
|
|
35
|
+
if (id !== undefined) {
|
|
36
|
+
app.send((0, messages_gen_js_1.SetNodeScroll)(id, s[0], s[1]));
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
app.attachStartCallback(sendSetViewportScroll);
|
|
40
|
+
app.attachStopCallback(() => {
|
|
41
|
+
documentScroll = false;
|
|
42
|
+
nodeScroll.clear();
|
|
43
|
+
});
|
|
44
|
+
app.nodes.attachNodeCallback((node, isStart) => {
|
|
45
|
+
// MBTODO: iterate over all the nodes on start instead of using isStart hack
|
|
46
|
+
if (isStart) {
|
|
47
|
+
if ((0, guards_js_1.isElementNode)(node) && node.scrollLeft + node.scrollTop > 0) {
|
|
48
|
+
nodeScroll.set(node, [node.scrollLeft, node.scrollTop]);
|
|
49
|
+
}
|
|
50
|
+
else if ((0, guards_js_1.isDocument)(node)) {
|
|
51
|
+
// DRY somehow?
|
|
52
|
+
nodeScroll.set(node, getDocumentScroll(node));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if ((0, guards_js_1.isRootNode)(node)) {
|
|
56
|
+
// scroll is not-composed event (https://javascript.info/shadow-dom-events)
|
|
57
|
+
app.nodes.attachNodeListener(node, 'scroll', (e) => {
|
|
58
|
+
setNodeScroll(e.target);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
app.attachEventListener(document, 'scroll', (e) => {
|
|
63
|
+
const target = e.target;
|
|
64
|
+
if (target === document) {
|
|
65
|
+
documentScroll = true;
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
setNodeScroll(target);
|
|
69
|
+
});
|
|
70
|
+
app.ticker.attach(() => {
|
|
71
|
+
if (documentScroll) {
|
|
72
|
+
sendSetViewportScroll();
|
|
73
|
+
documentScroll = false;
|
|
74
|
+
}
|
|
75
|
+
nodeScroll.forEach(sendSetNodeScroll);
|
|
76
|
+
nodeScroll.clear();
|
|
77
|
+
}, 5, false);
|
|
78
|
+
}
|
|
79
|
+
exports.default = default_1;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const guards_js_1 = require("../app/guards.js");
|
|
4
|
+
const utils_js_1 = require("../utils.js");
|
|
5
|
+
const messages_gen_js_1 = require("../app/messages.gen.js");
|
|
6
|
+
function getPaintBlocks(resources) {
|
|
7
|
+
const paintBlocks = [];
|
|
8
|
+
const elements = document.getElementsByTagName('*');
|
|
9
|
+
const styleURL = /url\(("[^"]*"|'[^']*'|[^)]*)\)/i;
|
|
10
|
+
for (let i = 0; i < elements.length; i++) {
|
|
11
|
+
const element = elements[i];
|
|
12
|
+
let src = '';
|
|
13
|
+
if ((0, guards_js_1.hasTag)(element, 'img')) {
|
|
14
|
+
src = element.currentSrc || element.src;
|
|
15
|
+
}
|
|
16
|
+
if (!src) {
|
|
17
|
+
const backgroundImage = getComputedStyle(element).getPropertyValue('background-image');
|
|
18
|
+
if (backgroundImage) {
|
|
19
|
+
const matches = styleURL.exec(backgroundImage);
|
|
20
|
+
if (matches !== null) {
|
|
21
|
+
src = matches[1];
|
|
22
|
+
if (src.startsWith('"') || src.startsWith("'")) {
|
|
23
|
+
src = src.substr(1, src.length - 2);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (!src)
|
|
29
|
+
continue;
|
|
30
|
+
const time = src.substr(0, 10) === 'data:image' ? 0 : resources[src];
|
|
31
|
+
if (time === undefined)
|
|
32
|
+
continue;
|
|
33
|
+
const rect = element.getBoundingClientRect();
|
|
34
|
+
const top = Math.max(rect.top, 0);
|
|
35
|
+
const left = Math.max(rect.left, 0);
|
|
36
|
+
const bottom = Math.min(rect.bottom, window.innerHeight ||
|
|
37
|
+
(document.documentElement && document.documentElement.clientHeight) ||
|
|
38
|
+
0);
|
|
39
|
+
const right = Math.min(rect.right, window.innerWidth || (document.documentElement && document.documentElement.clientWidth) || 0);
|
|
40
|
+
if (bottom <= top || right <= left)
|
|
41
|
+
continue;
|
|
42
|
+
const area = (bottom - top) * (right - left);
|
|
43
|
+
paintBlocks.push({ time, area });
|
|
44
|
+
}
|
|
45
|
+
return paintBlocks;
|
|
46
|
+
}
|
|
47
|
+
function calculateSpeedIndex(firstContentfulPaint, paintBlocks) {
|
|
48
|
+
let a = (Math.max((document.documentElement && document.documentElement.clientWidth) || 0, window.innerWidth || 0) *
|
|
49
|
+
Math.max((document.documentElement && document.documentElement.clientHeight) || 0, window.innerHeight || 0)) /
|
|
50
|
+
10;
|
|
51
|
+
let s = a * firstContentfulPaint;
|
|
52
|
+
for (let i = 0; i < paintBlocks.length; i++) {
|
|
53
|
+
const { time, area } = paintBlocks[i];
|
|
54
|
+
a += area;
|
|
55
|
+
s += area * (time > firstContentfulPaint ? time : firstContentfulPaint);
|
|
56
|
+
}
|
|
57
|
+
return a === 0 ? 0 : s / a;
|
|
58
|
+
}
|
|
59
|
+
function default_1(app, opts) {
|
|
60
|
+
const options = Object.assign({
|
|
61
|
+
captureResourceTimings: true,
|
|
62
|
+
capturePageLoadTimings: true,
|
|
63
|
+
capturePageRenderTimings: true,
|
|
64
|
+
}, opts);
|
|
65
|
+
if (!('PerformanceObserver' in window)) {
|
|
66
|
+
options.captureResourceTimings = false;
|
|
67
|
+
}
|
|
68
|
+
if (!options.captureResourceTimings) {
|
|
69
|
+
return;
|
|
70
|
+
} // Resources are necessary for all timings
|
|
71
|
+
let resources = {};
|
|
72
|
+
function resourceTiming(entry) {
|
|
73
|
+
if (entry.duration < 0 || !(0, utils_js_1.isURL)(entry.name) || app.isServiceURL(entry.name))
|
|
74
|
+
return;
|
|
75
|
+
if (resources !== null) {
|
|
76
|
+
resources[entry.name] = entry.startTime + entry.duration;
|
|
77
|
+
}
|
|
78
|
+
app.send((0, messages_gen_js_1.ResourceTiming)(entry.startTime + (0, utils_js_1.getTimeOrigin)(), entry.duration, entry.responseStart && entry.startTime ? entry.responseStart - entry.startTime : 0, entry.transferSize > entry.encodedBodySize ? entry.transferSize - entry.encodedBodySize : 0, entry.encodedBodySize || 0, entry.decodedBodySize || 0, entry.name, entry.initiatorType));
|
|
79
|
+
}
|
|
80
|
+
const observer = new PerformanceObserver((list) => list.getEntries().forEach(resourceTiming));
|
|
81
|
+
let prevSessionID;
|
|
82
|
+
app.attachStartCallback(function ({ sessionID }) {
|
|
83
|
+
if (sessionID !== prevSessionID) {
|
|
84
|
+
// Send past page resources on a newly started session
|
|
85
|
+
performance.getEntriesByType('resource').forEach(resourceTiming);
|
|
86
|
+
prevSessionID = sessionID;
|
|
87
|
+
}
|
|
88
|
+
observer.observe({ entryTypes: ['resource'] });
|
|
89
|
+
});
|
|
90
|
+
app.attachStopCallback(function () {
|
|
91
|
+
observer.disconnect();
|
|
92
|
+
});
|
|
93
|
+
let firstPaint = 0, firstContentfulPaint = 0;
|
|
94
|
+
if (options.capturePageLoadTimings) {
|
|
95
|
+
let pageLoadTimingSent = false;
|
|
96
|
+
app.ticker.attach(() => {
|
|
97
|
+
if (pageLoadTimingSent) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (firstPaint === 0 || firstContentfulPaint === 0) {
|
|
101
|
+
performance.getEntriesByType('paint').forEach((entry) => {
|
|
102
|
+
const { name, startTime } = entry;
|
|
103
|
+
switch (name) {
|
|
104
|
+
case 'first-paint':
|
|
105
|
+
firstPaint = startTime;
|
|
106
|
+
break;
|
|
107
|
+
case 'first-contentful-paint':
|
|
108
|
+
firstContentfulPaint = startTime;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
if (performance.timing.loadEventEnd || performance.now() > 30000) {
|
|
114
|
+
pageLoadTimingSent = true;
|
|
115
|
+
const {
|
|
116
|
+
// should be ok to use here, (https://github.com/mdn/content/issues/4713)
|
|
117
|
+
// since it is compared with the values obtained on the page load (before any possible sleep state)
|
|
118
|
+
// deprecated though
|
|
119
|
+
navigationStart, requestStart, responseStart, responseEnd, domContentLoadedEventStart, domContentLoadedEventEnd, loadEventStart, loadEventEnd, } = performance.timing;
|
|
120
|
+
app.send((0, messages_gen_js_1.PageLoadTiming)(requestStart - navigationStart || 0, responseStart - navigationStart || 0, responseEnd - navigationStart || 0, domContentLoadedEventStart - navigationStart || 0, domContentLoadedEventEnd - navigationStart || 0, loadEventStart - navigationStart || 0, loadEventEnd - navigationStart || 0, firstPaint, firstContentfulPaint));
|
|
121
|
+
}
|
|
122
|
+
}, 30);
|
|
123
|
+
}
|
|
124
|
+
if (options.capturePageRenderTimings) {
|
|
125
|
+
let visuallyComplete = 0, interactiveWindowStartTime = 0, interactiveWindowTickTime = 0, paintBlocks = null;
|
|
126
|
+
let pageRenderTimingSent = false;
|
|
127
|
+
app.ticker.attach(() => {
|
|
128
|
+
if (pageRenderTimingSent) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const time = performance.now();
|
|
132
|
+
if (resources !== null) {
|
|
133
|
+
visuallyComplete = Math.max.apply(null, Object.keys(resources).map((k) => resources[k]));
|
|
134
|
+
if (time - visuallyComplete > 1000) {
|
|
135
|
+
paintBlocks = getPaintBlocks(resources);
|
|
136
|
+
resources = null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (interactiveWindowTickTime !== null) {
|
|
140
|
+
if (time - interactiveWindowTickTime > 50) {
|
|
141
|
+
interactiveWindowStartTime = time;
|
|
142
|
+
}
|
|
143
|
+
interactiveWindowTickTime = time - interactiveWindowStartTime > 5000 ? null : time;
|
|
144
|
+
}
|
|
145
|
+
if ((paintBlocks !== null && interactiveWindowTickTime === null) || time > 30000) {
|
|
146
|
+
pageRenderTimingSent = true;
|
|
147
|
+
resources = null;
|
|
148
|
+
const speedIndex = paintBlocks === null
|
|
149
|
+
? 0
|
|
150
|
+
: calculateSpeedIndex(firstContentfulPaint || firstPaint, paintBlocks);
|
|
151
|
+
const { domContentLoadedEventEnd, navigationStart } = performance.timing;
|
|
152
|
+
const timeToInteractive = interactiveWindowTickTime === null
|
|
153
|
+
? Math.max(interactiveWindowStartTime, firstContentfulPaint, domContentLoadedEventEnd - navigationStart || 0)
|
|
154
|
+
: 0;
|
|
155
|
+
app.send((0, messages_gen_js_1.PageRenderTiming)(speedIndex, firstContentfulPaint > visuallyComplete ? firstContentfulPaint : visuallyComplete, timeToInteractive));
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
exports.default = default_1;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const utils_js_1 = require("../utils.js");
|
|
4
|
+
const messages_gen_js_1 = require("../app/messages.gen.js");
|
|
5
|
+
function default_1(app) {
|
|
6
|
+
let url, width, height;
|
|
7
|
+
let navigationStart;
|
|
8
|
+
let referrer = document.referrer;
|
|
9
|
+
const sendSetPageLocation = app.safe(() => {
|
|
10
|
+
const { URL } = document;
|
|
11
|
+
if (URL !== url) {
|
|
12
|
+
url = URL;
|
|
13
|
+
app.send((0, messages_gen_js_1.SetPageLocation)(url, referrer, navigationStart));
|
|
14
|
+
navigationStart = 0;
|
|
15
|
+
referrer = url;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
const sendSetViewportSize = app.safe(() => {
|
|
19
|
+
const { innerWidth, innerHeight } = window;
|
|
20
|
+
if (innerWidth !== width || innerHeight !== height) {
|
|
21
|
+
width = innerWidth;
|
|
22
|
+
height = innerHeight;
|
|
23
|
+
app.send((0, messages_gen_js_1.SetViewportSize)(width, height));
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
const sendSetPageVisibility = document.hidden === undefined
|
|
27
|
+
? Function.prototype
|
|
28
|
+
: app.safe(() => app.send((0, messages_gen_js_1.SetPageVisibility)(document.hidden)));
|
|
29
|
+
app.attachStartCallback(() => {
|
|
30
|
+
url = '';
|
|
31
|
+
navigationStart = (0, utils_js_1.getTimeOrigin)();
|
|
32
|
+
width = height = -1;
|
|
33
|
+
sendSetPageLocation();
|
|
34
|
+
sendSetViewportSize();
|
|
35
|
+
sendSetPageVisibility();
|
|
36
|
+
});
|
|
37
|
+
if (document.hidden !== undefined) {
|
|
38
|
+
app.attachEventListener(document, 'visibilitychange', sendSetPageVisibility, false, false);
|
|
39
|
+
}
|
|
40
|
+
app.ticker.attach(sendSetPageLocation, 1, false);
|
|
41
|
+
app.ticker.attach(sendSetViewportSize, 5, false);
|
|
42
|
+
}
|
|
43
|
+
exports.default = default_1;
|
package/cjs/package.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "type": "commonjs" }
|
package/cjs/utils.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const IN_BROWSER: boolean;
|
|
2
|
+
export declare const IS_FIREFOX: false | RegExpMatchArray | null;
|
|
3
|
+
export declare const MAX_STR_LEN = 100000;
|
|
4
|
+
export declare function adjustTimeOrigin(): void;
|
|
5
|
+
export declare function getTimeOrigin(): number;
|
|
6
|
+
export declare const now: () => number;
|
|
7
|
+
export declare const stars: (str: string) => string;
|
|
8
|
+
export declare function normSpaces(str: string): string;
|
|
9
|
+
export declare function isURL(s: string): boolean;
|
|
10
|
+
export declare const DOCS_HOST = "https://docs.openreplay.com";
|
|
11
|
+
export declare function deprecationWarn(nameOfFeature: string, useInstead: string, docsPath?: string): void;
|
|
12
|
+
export declare function getLabelAttribute(e: Element): string | null;
|
|
13
|
+
export declare function hasOpenreplayAttribute(e: Element, attr: string): boolean;
|
package/cjs/utils.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hasOpenreplayAttribute = exports.getLabelAttribute = exports.deprecationWarn = exports.DOCS_HOST = exports.isURL = exports.normSpaces = exports.stars = exports.now = exports.getTimeOrigin = exports.adjustTimeOrigin = exports.MAX_STR_LEN = exports.IS_FIREFOX = exports.IN_BROWSER = void 0;
|
|
4
|
+
const DEPRECATED_ATTRS = { htmlmasked: 'hidden', masked: 'obscured' };
|
|
5
|
+
exports.IN_BROWSER = !(typeof window === 'undefined');
|
|
6
|
+
exports.IS_FIREFOX = exports.IN_BROWSER && navigator.userAgent.match(/firefox|fxios/i);
|
|
7
|
+
exports.MAX_STR_LEN = 1e5;
|
|
8
|
+
// Buggy to use `performance.timeOrigin || performance.timing.navigationStart`
|
|
9
|
+
// https://github.com/mdn/content/issues/4713
|
|
10
|
+
// Maybe move to timer/ticker
|
|
11
|
+
let timeOrigin = exports.IN_BROWSER ? Date.now() - performance.now() : 0;
|
|
12
|
+
function adjustTimeOrigin() {
|
|
13
|
+
timeOrigin = Date.now() - performance.now();
|
|
14
|
+
}
|
|
15
|
+
exports.adjustTimeOrigin = adjustTimeOrigin;
|
|
16
|
+
function getTimeOrigin() {
|
|
17
|
+
return timeOrigin;
|
|
18
|
+
}
|
|
19
|
+
exports.getTimeOrigin = getTimeOrigin;
|
|
20
|
+
exports.now = exports.IN_BROWSER && !!performance.now
|
|
21
|
+
? () => Math.round(performance.now() + timeOrigin)
|
|
22
|
+
: () => Date.now();
|
|
23
|
+
exports.stars = 'repeat' in String.prototype
|
|
24
|
+
? (str) => '*'.repeat(str.length)
|
|
25
|
+
: (str) => str.replace(/./g, '*');
|
|
26
|
+
function normSpaces(str) {
|
|
27
|
+
return str.trim().replace(/\s+/g, ' ');
|
|
28
|
+
}
|
|
29
|
+
exports.normSpaces = normSpaces;
|
|
30
|
+
// isAbsoluteUrl regexp: /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url)
|
|
31
|
+
function isURL(s) {
|
|
32
|
+
return s.startsWith('https://') || s.startsWith('http://');
|
|
33
|
+
}
|
|
34
|
+
exports.isURL = isURL;
|
|
35
|
+
// TODO: JOIN IT WITH LOGGER somehow (use logging decorators?); Don't forget about index.js loggin when there is no logger instance.
|
|
36
|
+
exports.DOCS_HOST = 'https://docs.openreplay.com';
|
|
37
|
+
const warnedFeatures = {};
|
|
38
|
+
function deprecationWarn(nameOfFeature, useInstead, docsPath = '/') {
|
|
39
|
+
if (warnedFeatures[nameOfFeature]) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
console.warn(`OpenReplay: ${nameOfFeature} is deprecated. ${useInstead ? `Please, use ${useInstead} instead.` : ''} Visit ${exports.DOCS_HOST}${docsPath} for more information.`);
|
|
43
|
+
warnedFeatures[nameOfFeature] = true;
|
|
44
|
+
}
|
|
45
|
+
exports.deprecationWarn = deprecationWarn;
|
|
46
|
+
function getLabelAttribute(e) {
|
|
47
|
+
let value = e.getAttribute('data-openreplay-label');
|
|
48
|
+
if (value !== null) {
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
value = e.getAttribute('data-asayer-label');
|
|
52
|
+
if (value !== null) {
|
|
53
|
+
deprecationWarn('"data-asayer-label" attribute', '"data-openreplay-label" attribute', '/');
|
|
54
|
+
}
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
exports.getLabelAttribute = getLabelAttribute;
|
|
58
|
+
function hasOpenreplayAttribute(e, attr) {
|
|
59
|
+
const newName = `data-openreplay-${attr}`;
|
|
60
|
+
if (e.hasAttribute(newName)) {
|
|
61
|
+
// @ts-ignore
|
|
62
|
+
if (DEPRECATED_ATTRS[attr]) {
|
|
63
|
+
deprecationWarn(`"${newName}" attribute`,
|
|
64
|
+
// @ts-ignore
|
|
65
|
+
`"${DEPRECATED_ATTRS[attr]}" attribute`, '/installation/sanitize-data');
|
|
66
|
+
}
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
exports.hasOpenreplayAttribute = hasOpenreplayAttribute;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type Options = {
|
|
2
|
+
root: Element;
|
|
3
|
+
idName: (name: string) => boolean;
|
|
4
|
+
className: (name: string) => boolean;
|
|
5
|
+
tagName: (name: string) => boolean;
|
|
6
|
+
attr: (name: string, value: string) => boolean;
|
|
7
|
+
seedMinLength: number;
|
|
8
|
+
optimizedMinLength: number;
|
|
9
|
+
threshold: number;
|
|
10
|
+
maxNumberOfTries: number;
|
|
11
|
+
};
|
|
12
|
+
export declare function finder(input: Element, options?: Partial<Options>): string;
|