@openreplay/tracker 4.1.10 → 4.1.11
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/.eslintignore +0 -3
- package/README.md +18 -22
- package/cjs/app/guards.d.ts +12 -11
- package/cjs/app/guards.js +1 -2
- package/cjs/app/index.d.ts +8 -11
- package/cjs/app/index.js +8 -27
- package/cjs/app/logger.d.ts +3 -3
- package/cjs/app/messages.d.ts +52 -0
- package/cjs/app/messages.gen.d.ts +6 -8
- package/cjs/app/messages.gen.js +94 -116
- package/cjs/app/messages.js +234 -0
- package/cjs/app/nodes.d.ts +1 -1
- package/cjs/app/observer/iframe_offsets.d.ts +1 -1
- package/cjs/app/observer/observer.js +5 -5
- package/cjs/app/observer/top_observer.d.ts +2 -2
- package/cjs/app/observer/top_observer.js +1 -1
- package/cjs/app/session.d.ts +2 -2
- package/cjs/app/ticker.d.ts +1 -1
- package/cjs/common/interaction.d.ts +5 -5
- package/cjs/common/messages.gen.d.ts +86 -104
- package/cjs/index.d.ts +2 -6
- package/cjs/index.js +5 -7
- package/cjs/modules/cssrules.js +1 -1
- package/cjs/modules/focus.js +2 -2
- package/cjs/modules/fonts.js +2 -2
- package/cjs/modules/img.js +5 -6
- package/cjs/modules/input.d.ts +1 -1
- package/cjs/modules/input.js +23 -15
- package/cjs/modules/mouse.js +1 -1
- package/cjs/modules/timing.js +3 -7
- package/cjs/modules/viewport.js +1 -3
- package/cjs/vendors/finder/finder.d.ts +1 -1
- package/lib/app/guards.d.ts +12 -11
- package/lib/app/guards.js +1 -2
- package/lib/app/index.d.ts +8 -11
- package/lib/app/index.js +9 -28
- package/lib/app/logger.d.ts +3 -3
- package/lib/app/messages.d.ts +52 -0
- package/lib/app/messages.gen.d.ts +6 -8
- package/lib/app/messages.gen.js +87 -107
- package/lib/app/messages.js +181 -0
- package/lib/app/nodes.d.ts +1 -1
- package/lib/app/observer/iframe_offsets.d.ts +1 -1
- package/lib/app/observer/observer.js +5 -5
- package/lib/app/observer/top_observer.d.ts +2 -2
- package/lib/app/observer/top_observer.js +1 -1
- package/lib/app/session.d.ts +2 -2
- package/lib/app/ticker.d.ts +1 -1
- package/lib/common/interaction.d.ts +5 -5
- package/lib/common/messages.gen.d.ts +86 -104
- package/lib/common/tsconfig.tsbuildinfo +1 -1
- package/lib/index.d.ts +2 -6
- package/lib/index.js +6 -8
- package/lib/modules/cssrules.js +1 -1
- package/lib/modules/focus.js +2 -2
- package/lib/modules/fonts.js +2 -2
- package/lib/modules/img.js +5 -6
- package/lib/modules/input.d.ts +1 -1
- package/lib/modules/input.js +23 -15
- package/lib/modules/mouse.js +1 -1
- package/lib/modules/timing.js +4 -8
- package/lib/modules/viewport.js +1 -3
- package/lib/vendors/finder/finder.d.ts +1 -1
- package/package.json +3 -8
- package/tsconfig-base.json +1 -2
- package/CHANGELOG.md +0 -19
- package/cjs/modules/network.d.ts +0 -28
- package/cjs/modules/network.js +0 -203
- package/jest.config.js +0 -11
- package/lib/modules/network.d.ts +0 -28
- package/lib/modules/network.js +0 -200
package/lib/index.d.ts
CHANGED
|
@@ -9,16 +9,14 @@ import type { Options as ExceptionOptions } from './modules/exception.js';
|
|
|
9
9
|
import type { Options as InputOptions } from './modules/input.js';
|
|
10
10
|
import type { Options as PerformanceOptions } from './modules/performance.js';
|
|
11
11
|
import type { Options as TimingOptions } from './modules/timing.js';
|
|
12
|
-
import type { Options as NetworkOptions } from './modules/network.js';
|
|
13
12
|
import type { StartOptions } from './app/index.js';
|
|
14
13
|
import type { StartPromiseReturn } from './app/index.js';
|
|
15
|
-
export type Options = Partial<AppOptions & ConsoleOptions & ExceptionOptions & InputOptions & PerformanceOptions & TimingOptions> & {
|
|
14
|
+
export declare type Options = Partial<AppOptions & ConsoleOptions & ExceptionOptions & InputOptions & PerformanceOptions & TimingOptions> & {
|
|
16
15
|
projectID?: number;
|
|
17
16
|
projectKey: string;
|
|
18
17
|
sessionToken?: string;
|
|
19
18
|
respectDoNotTrack?: boolean;
|
|
20
19
|
autoResetOnWindowOpen?: boolean;
|
|
21
|
-
network?: NetworkOptions;
|
|
22
20
|
__DISABLE_SECURE_MODE?: boolean;
|
|
23
21
|
};
|
|
24
22
|
export default class API {
|
|
@@ -32,9 +30,7 @@ export default class API {
|
|
|
32
30
|
getSessionToken(): string | null | undefined;
|
|
33
31
|
getSessionID(): string | null | undefined;
|
|
34
32
|
sessionID(): string | null | undefined;
|
|
35
|
-
getSessionURL(
|
|
36
|
-
withCurrentTime?: boolean;
|
|
37
|
-
}): string | undefined;
|
|
33
|
+
getSessionURL(): string | undefined;
|
|
38
34
|
setUserID(id: string): void;
|
|
39
35
|
userID(id: string): void;
|
|
40
36
|
setUserAnonymousID(id: string): void;
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import App, { DEFAULT_INGEST_POINT } from './app/index.js';
|
|
2
2
|
export { default as App } from './app/index.js';
|
|
3
|
-
import { UserAnonymousID,
|
|
3
|
+
import { UserAnonymousID, RawCustomEvent, CustomIssue } from './app/messages.gen.js';
|
|
4
4
|
import * as _Messages from './app/messages.gen.js';
|
|
5
5
|
export const Messages = _Messages;
|
|
6
6
|
export { SanitizeLevel } from './app/sanitizer.js';
|
|
@@ -17,10 +17,9 @@ import Viewport from './modules/viewport.js';
|
|
|
17
17
|
import CSSRules from './modules/cssrules.js';
|
|
18
18
|
import Focus from './modules/focus.js';
|
|
19
19
|
import Fonts from './modules/fonts.js';
|
|
20
|
-
import Network from './modules/network.js';
|
|
21
20
|
import ConstructedStyleSheets from './modules/constructedStyleSheets.js';
|
|
22
21
|
import { IN_BROWSER, deprecationWarn, DOCS_HOST } from './utils.js';
|
|
23
|
-
const DOCS_SETUP = '/installation/
|
|
22
|
+
const DOCS_SETUP = '/installation/setup-or';
|
|
24
23
|
function processOptions(obj) {
|
|
25
24
|
if (obj == null) {
|
|
26
25
|
console.error(`OpenReplay: invalid options argument type. Please, check documentation on ${DOCS_HOST}${DOCS_SETUP}`);
|
|
@@ -110,7 +109,6 @@ export default class API {
|
|
|
110
109
|
Scroll(app);
|
|
111
110
|
Focus(app);
|
|
112
111
|
Fonts(app);
|
|
113
|
-
Network(app, options.network);
|
|
114
112
|
window.__OPENREPLAY__ = this;
|
|
115
113
|
if (options.autoResetOnWindowOpen) {
|
|
116
114
|
const wOpen = window.open;
|
|
@@ -135,7 +133,7 @@ export default class API {
|
|
|
135
133
|
// no-cors issue only with text/plain or not-set Content-Type
|
|
136
134
|
// req.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
|
|
137
135
|
req.send(JSON.stringify({
|
|
138
|
-
trackerVersion: '4.1.
|
|
136
|
+
trackerVersion: '4.1.11',
|
|
139
137
|
projectKey: options.projectKey,
|
|
140
138
|
doNotTrack,
|
|
141
139
|
// TODO: add precise reason (an exact API missing)
|
|
@@ -185,11 +183,11 @@ export default class API {
|
|
|
185
183
|
deprecationWarn("'sessionID' method", "'getSessionID' method", '/');
|
|
186
184
|
return this.getSessionID();
|
|
187
185
|
}
|
|
188
|
-
getSessionURL(
|
|
186
|
+
getSessionURL() {
|
|
189
187
|
if (this.app === null) {
|
|
190
188
|
return undefined;
|
|
191
189
|
}
|
|
192
|
-
return this.app.getSessionURL(
|
|
190
|
+
return this.app.getSessionURL();
|
|
193
191
|
}
|
|
194
192
|
setUserID(id) {
|
|
195
193
|
if (typeof id === 'string' && this.app !== null) {
|
|
@@ -230,7 +228,7 @@ export default class API {
|
|
|
230
228
|
catch (e) {
|
|
231
229
|
return;
|
|
232
230
|
}
|
|
233
|
-
this.app.send(
|
|
231
|
+
this.app.send(RawCustomEvent(key, payload));
|
|
234
232
|
}
|
|
235
233
|
}
|
|
236
234
|
}
|
package/lib/modules/cssrules.js
CHANGED
|
@@ -75,7 +75,7 @@ export default function (app) {
|
|
|
75
75
|
patchContext(window);
|
|
76
76
|
app.observer.attachContextCallback(patchContext);
|
|
77
77
|
app.nodes.attachNodeCallback((node) => {
|
|
78
|
-
if (!hasTag(node, 'style') || !node.sheet) {
|
|
78
|
+
if (!(hasTag(node, 'STYLE') || hasTag(node, 'style')) || !node.sheet) {
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
81
|
if (node.textContent !== null && node.textContent.trim().length > 0) {
|
package/lib/modules/focus.js
CHANGED
|
@@ -9,7 +9,7 @@ export default function (app) {
|
|
|
9
9
|
}
|
|
10
10
|
let blurred = false;
|
|
11
11
|
app.nodes.attachNodeCallback((node) => {
|
|
12
|
-
if (!hasTag(node, '
|
|
12
|
+
if (!hasTag(node, 'BODY')) {
|
|
13
13
|
return;
|
|
14
14
|
}
|
|
15
15
|
app.nodes.attachNodeListener(node, 'focus', (e) => {
|
|
@@ -32,7 +32,7 @@ export default function (app) {
|
|
|
32
32
|
});
|
|
33
33
|
app.attachStartCallback(() => {
|
|
34
34
|
let elem = document.activeElement;
|
|
35
|
-
while (elem && hasTag(elem, '
|
|
35
|
+
while (elem && hasTag(elem, 'IFRAME') && elem.contentDocument) {
|
|
36
36
|
elem = elem.contentDocument.activeElement;
|
|
37
37
|
}
|
|
38
38
|
if (elem && elem !== elem.ownerDocument.body) {
|
package/lib/modules/fonts.js
CHANGED
|
@@ -35,7 +35,7 @@ export default function (app) {
|
|
|
35
35
|
};
|
|
36
36
|
app.observer.attachContextCallback(patchWindow);
|
|
37
37
|
patchWindow(window);
|
|
38
|
-
app.nodes.attachNodeCallback(
|
|
38
|
+
app.nodes.attachNodeCallback((node) => {
|
|
39
39
|
if (!isDocument(node)) {
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
@@ -50,5 +50,5 @@ export default function (app) {
|
|
|
50
50
|
ffDataArr.forEach((ffData) => {
|
|
51
51
|
app.send(LoadFontFace(parentID, ...ffData));
|
|
52
52
|
});
|
|
53
|
-
})
|
|
53
|
+
});
|
|
54
54
|
}
|
package/lib/modules/img.js
CHANGED
|
@@ -3,16 +3,15 @@ import { ResourceTiming, SetNodeAttributeURLBased, SetNodeAttribute } from '../a
|
|
|
3
3
|
import { hasTag } from '../app/guards.js';
|
|
4
4
|
function resolveURL(url, location = document.location) {
|
|
5
5
|
url = url.trim();
|
|
6
|
-
if (url.startsWith('
|
|
7
|
-
|
|
6
|
+
if (url.startsWith('/')) {
|
|
7
|
+
return location.origin + url;
|
|
8
|
+
}
|
|
9
|
+
else if (url.startsWith('http://') ||
|
|
8
10
|
url.startsWith('https://') ||
|
|
9
11
|
url.startsWith('data:') // any other possible value here? https://bugzilla.mozilla.org/show_bug.cgi?id=1758035
|
|
10
12
|
) {
|
|
11
13
|
return url;
|
|
12
14
|
}
|
|
13
|
-
else if (url.startsWith('/')) {
|
|
14
|
-
return location.origin + url;
|
|
15
|
-
}
|
|
16
15
|
else {
|
|
17
16
|
return location.origin + location.pathname + url;
|
|
18
17
|
}
|
|
@@ -96,7 +95,7 @@ export default function (app) {
|
|
|
96
95
|
observer.disconnect();
|
|
97
96
|
});
|
|
98
97
|
app.nodes.attachNodeCallback((node) => {
|
|
99
|
-
if (!hasTag(node, '
|
|
98
|
+
if (!hasTag(node, 'IMG')) {
|
|
100
99
|
return;
|
|
101
100
|
}
|
|
102
101
|
app.nodes.attachNodeListener(node, 'error', () => sendImgError(node));
|
package/lib/modules/input.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type App from '../app/index.js';
|
|
2
|
-
type TextEditableElement = HTMLInputElement | HTMLTextAreaElement;
|
|
2
|
+
declare type TextEditableElement = HTMLInputElement | HTMLTextAreaElement;
|
|
3
3
|
export declare function getInputLabel(node: TextEditableElement): string;
|
|
4
4
|
export declare const enum InputMode {
|
|
5
5
|
Plain = 0,
|
package/lib/modules/input.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { normSpaces, IN_BROWSER, getLabelAttribute } from '../utils.js';
|
|
2
2
|
import { hasTag } from '../app/guards.js';
|
|
3
3
|
import { SetInputTarget, SetInputValue, SetInputChecked } from '../app/messages.gen.js';
|
|
4
|
-
const INPUT_TYPES = ['text', 'password', 'email', 'search', 'number', 'range', 'date'
|
|
4
|
+
const INPUT_TYPES = ['text', 'password', 'email', 'search', 'number', 'range', 'date'];
|
|
5
5
|
function isTextEditable(node) {
|
|
6
|
-
if (hasTag(node, '
|
|
6
|
+
if (hasTag(node, 'TEXTAREA')) {
|
|
7
7
|
return true;
|
|
8
8
|
}
|
|
9
|
-
if (!hasTag(node, '
|
|
9
|
+
if (!hasTag(node, 'INPUT')) {
|
|
10
10
|
return false;
|
|
11
11
|
}
|
|
12
12
|
return INPUT_TYPES.includes(node.type);
|
|
13
13
|
}
|
|
14
14
|
function isCheckable(node) {
|
|
15
|
-
if (!hasTag(node, '
|
|
15
|
+
if (!hasTag(node, 'INPUT')) {
|
|
16
16
|
return false;
|
|
17
17
|
}
|
|
18
18
|
const type = node.type;
|
|
@@ -22,7 +22,7 @@ const labelElementFor = IN_BROWSER && 'labels' in HTMLInputElement.prototype
|
|
|
22
22
|
? (node) => {
|
|
23
23
|
let p = node;
|
|
24
24
|
while ((p = p.parentNode) !== null) {
|
|
25
|
-
if (hasTag(p, '
|
|
25
|
+
if (hasTag(p, 'LABEL')) {
|
|
26
26
|
return p;
|
|
27
27
|
}
|
|
28
28
|
}
|
|
@@ -34,7 +34,7 @@ const labelElementFor = IN_BROWSER && 'labels' in HTMLInputElement.prototype
|
|
|
34
34
|
: (node) => {
|
|
35
35
|
let p = node;
|
|
36
36
|
while ((p = p.parentNode) !== null) {
|
|
37
|
-
if (hasTag(p, '
|
|
37
|
+
if (hasTag(p, 'LABEL')) {
|
|
38
38
|
return p;
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -64,7 +64,7 @@ export default function (app, opts) {
|
|
|
64
64
|
const options = Object.assign({
|
|
65
65
|
obscureInputNumbers: true,
|
|
66
66
|
obscureInputEmails: true,
|
|
67
|
-
defaultInputMode: 0 /*
|
|
67
|
+
defaultInputMode: 0 /* Plain */,
|
|
68
68
|
obscureInputDates: false,
|
|
69
69
|
}, opts);
|
|
70
70
|
function sendInputTarget(id, node) {
|
|
@@ -77,22 +77,22 @@ export default function (app, opts) {
|
|
|
77
77
|
let value = node.value;
|
|
78
78
|
let inputMode = options.defaultInputMode;
|
|
79
79
|
if (node.type === 'password' || app.sanitizer.isHidden(id)) {
|
|
80
|
-
inputMode = 2 /*
|
|
80
|
+
inputMode = 2 /* Hidden */;
|
|
81
81
|
}
|
|
82
82
|
else if (app.sanitizer.isObscured(id) ||
|
|
83
|
-
(inputMode === 0 /*
|
|
83
|
+
(inputMode === 0 /* Plain */ &&
|
|
84
84
|
((options.obscureInputNumbers && node.type !== 'date' && /\d\d\d\d/.test(value)) ||
|
|
85
85
|
(options.obscureInputDates && node.type === 'date') ||
|
|
86
86
|
(options.obscureInputEmails && (node.type === 'email' || !!~value.indexOf('@')))))) {
|
|
87
|
-
inputMode = 1 /*
|
|
87
|
+
inputMode = 1 /* Obscured */;
|
|
88
88
|
}
|
|
89
89
|
let mask = 0;
|
|
90
90
|
switch (inputMode) {
|
|
91
|
-
case 2 /*
|
|
91
|
+
case 2 /* Hidden */:
|
|
92
92
|
mask = -1;
|
|
93
93
|
value = '';
|
|
94
94
|
break;
|
|
95
|
-
case 1 /*
|
|
95
|
+
case 1 /* Obscured */:
|
|
96
96
|
mask = value.length;
|
|
97
97
|
value = '';
|
|
98
98
|
break;
|
|
@@ -111,7 +111,11 @@ export default function (app, opts) {
|
|
|
111
111
|
inputValues.forEach((value, id) => {
|
|
112
112
|
const node = app.nodes.getNode(id);
|
|
113
113
|
if (!node)
|
|
114
|
-
return
|
|
114
|
+
return;
|
|
115
|
+
if (!isTextEditable(node)) {
|
|
116
|
+
inputValues.delete(id);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
115
119
|
if (value !== node.value) {
|
|
116
120
|
inputValues.set(id, node.value);
|
|
117
121
|
if (!registeredTargets.has(id)) {
|
|
@@ -124,7 +128,11 @@ export default function (app, opts) {
|
|
|
124
128
|
checkableValues.forEach((checked, id) => {
|
|
125
129
|
const node = app.nodes.getNode(id);
|
|
126
130
|
if (!node)
|
|
127
|
-
return
|
|
131
|
+
return;
|
|
132
|
+
if (!isCheckable(node)) {
|
|
133
|
+
checkableValues.delete(id);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
128
136
|
if (checked !== node.checked) {
|
|
129
137
|
checkableValues.set(id, node.checked);
|
|
130
138
|
app.send(SetInputChecked(id, node.checked));
|
|
@@ -138,7 +146,7 @@ export default function (app, opts) {
|
|
|
138
146
|
return;
|
|
139
147
|
}
|
|
140
148
|
// TODO: support multiple select (?): use selectedOptions; Need send target?
|
|
141
|
-
if (hasTag(node, '
|
|
149
|
+
if (hasTag(node, 'SELECT')) {
|
|
142
150
|
sendInputValue(id, node);
|
|
143
151
|
app.attachEventListener(node, 'change', () => {
|
|
144
152
|
sendInputValue(id, node);
|
package/lib/modules/mouse.js
CHANGED
package/lib/modules/timing.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { hasTag } from '../app/guards.js';
|
|
2
|
-
import { isURL
|
|
2
|
+
import { isURL } from '../utils.js';
|
|
3
3
|
import { ResourceTiming, PageLoadTiming, PageRenderTiming } from '../app/messages.gen.js';
|
|
4
4
|
function getPaintBlocks(resources) {
|
|
5
5
|
const paintBlocks = [];
|
|
@@ -8,7 +8,7 @@ function getPaintBlocks(resources) {
|
|
|
8
8
|
for (let i = 0; i < elements.length; i++) {
|
|
9
9
|
const element = elements[i];
|
|
10
10
|
let src = '';
|
|
11
|
-
if (hasTag(element, '
|
|
11
|
+
if (hasTag(element, 'IMG')) {
|
|
12
12
|
src = element.currentSrc || element.src;
|
|
13
13
|
}
|
|
14
14
|
if (!src) {
|
|
@@ -73,7 +73,7 @@ export default function (app, opts) {
|
|
|
73
73
|
if (resources !== null) {
|
|
74
74
|
resources[entry.name] = entry.startTime + entry.duration;
|
|
75
75
|
}
|
|
76
|
-
app.send(ResourceTiming(entry.startTime +
|
|
76
|
+
app.send(ResourceTiming(entry.startTime + performance.timing.navigationStart, entry.duration, entry.responseStart && entry.startTime ? entry.responseStart - entry.startTime : 0, entry.transferSize > entry.encodedBodySize ? entry.transferSize - entry.encodedBodySize : 0, entry.encodedBodySize || 0, entry.decodedBodySize || 0, entry.name, entry.initiatorType));
|
|
77
77
|
}
|
|
78
78
|
const observer = new PerformanceObserver((list) => list.getEntries().forEach(resourceTiming));
|
|
79
79
|
let prevSessionID;
|
|
@@ -110,11 +110,7 @@ export default function (app, opts) {
|
|
|
110
110
|
}
|
|
111
111
|
if (performance.timing.loadEventEnd || performance.now() > 30000) {
|
|
112
112
|
pageLoadTimingSent = true;
|
|
113
|
-
const {
|
|
114
|
-
// should be ok to use here, (https://github.com/mdn/content/issues/4713)
|
|
115
|
-
// since it is compared with the values obtained on the page load (before any possible sleep state)
|
|
116
|
-
// deprecated though
|
|
117
|
-
navigationStart, requestStart, responseStart, responseEnd, domContentLoadedEventStart, domContentLoadedEventEnd, loadEventStart, loadEventEnd, } = performance.timing;
|
|
113
|
+
const { navigationStart, requestStart, responseStart, responseEnd, domContentLoadedEventStart, domContentLoadedEventEnd, loadEventStart, loadEventEnd, } = performance.timing;
|
|
118
114
|
app.send(PageLoadTiming(requestStart - navigationStart || 0, responseStart - navigationStart || 0, responseEnd - navigationStart || 0, domContentLoadedEventStart - navigationStart || 0, domContentLoadedEventEnd - navigationStart || 0, loadEventStart - navigationStart || 0, loadEventEnd - navigationStart || 0, firstPaint, firstContentfulPaint));
|
|
119
115
|
}
|
|
120
116
|
}, 30);
|
package/lib/modules/viewport.js
CHANGED
|
@@ -3,14 +3,12 @@ import { SetPageLocation, SetViewportSize, SetPageVisibility } from '../app/mess
|
|
|
3
3
|
export default function (app) {
|
|
4
4
|
let url, width, height;
|
|
5
5
|
let navigationStart;
|
|
6
|
-
let referrer = document.referrer;
|
|
7
6
|
const sendSetPageLocation = app.safe(() => {
|
|
8
7
|
const { URL } = document;
|
|
9
8
|
if (URL !== url) {
|
|
10
9
|
url = URL;
|
|
11
|
-
app.send(SetPageLocation(url, referrer, navigationStart));
|
|
10
|
+
app.send(SetPageLocation(url, document.referrer, navigationStart));
|
|
12
11
|
navigationStart = 0;
|
|
13
|
-
referrer = url;
|
|
14
12
|
}
|
|
15
13
|
});
|
|
16
14
|
const sendSetViewportSize = app.safe(() => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openreplay/tracker",
|
|
3
3
|
"description": "The OpenReplay tracker main package",
|
|
4
|
-
"version": "4.1.
|
|
4
|
+
"version": "4.1.11",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"logging",
|
|
7
7
|
"replay"
|
|
@@ -21,12 +21,10 @@
|
|
|
21
21
|
"compile": "node --experimental-modules --experimental-json-modules scripts/compile.cjs",
|
|
22
22
|
"build": "npm run clean && npm run tscRun && npm run rollup && npm run compile",
|
|
23
23
|
"prepare": "cd ../../ && husky install tracker/.husky/",
|
|
24
|
-
"lint-front": "lint-staged"
|
|
25
|
-
"test": "jest"
|
|
24
|
+
"lint-front": "lint-staged"
|
|
26
25
|
},
|
|
27
26
|
"devDependencies": {
|
|
28
27
|
"@babel/core": "^7.10.2",
|
|
29
|
-
"@jest/globals": "^29.3.1",
|
|
30
28
|
"@rollup/plugin-babel": "^5.0.3",
|
|
31
29
|
"@rollup/plugin-node-resolve": "^10.0.0",
|
|
32
30
|
"@typescript-eslint/eslint-plugin": "^5.30.0",
|
|
@@ -35,16 +33,13 @@
|
|
|
35
33
|
"eslint-config-prettier": "^8.5.0",
|
|
36
34
|
"eslint-plugin-prettier": "^4.2.1",
|
|
37
35
|
"husky": "^8.0.1",
|
|
38
|
-
"jest": "^29.3.1",
|
|
39
|
-
"jest-environment-jsdom": "^29.3.1",
|
|
40
36
|
"lint-staged": "^13.0.3",
|
|
41
37
|
"prettier": "^2.7.1",
|
|
42
38
|
"replace-in-files": "^2.0.3",
|
|
43
39
|
"rollup": "^2.17.0",
|
|
44
40
|
"rollup-plugin-terser": "^6.1.0",
|
|
45
41
|
"semver": "^6.3.0",
|
|
46
|
-
"
|
|
47
|
-
"typescript": "^4.9.4"
|
|
42
|
+
"typescript": "4.6.0-dev.20211126"
|
|
48
43
|
},
|
|
49
44
|
"dependencies": {
|
|
50
45
|
"error-stack-parser": "^2.0.6"
|
package/tsconfig-base.json
CHANGED
package/CHANGELOG.md
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
## 4.1.10
|
|
2
|
-
|
|
3
|
-
- Added "tel" to supported input types
|
|
4
|
-
- Added `{ withCurrentTime: true }` to `tracker.getSessionURL` method which will return sessionURL with current session's timestamp
|
|
5
|
-
- Added Network module that captures fetch/xhr by default (with no plugin required)
|
|
6
|
-
- Use `timeOrigin()` instead of `performance.timing.navigationStart` in ResourceTiming messages
|
|
7
|
-
- Added app restart when service worker died after inactivity (mobile safari)
|
|
8
|
-
|
|
9
|
-
## 4.1.8
|
|
10
|
-
|
|
11
|
-
- recalculate timeOrigin on start to prevent wrong timestamps on "sleeping" sessions
|
|
12
|
-
|
|
13
|
-
## 4.1.7
|
|
14
|
-
|
|
15
|
-
- resend metadata on start
|
|
16
|
-
|
|
17
|
-
## 4.1.6
|
|
18
|
-
|
|
19
|
-
- remove log that potentially caused crashed during slow initial render
|
package/cjs/modules/network.d.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type App from '../app/index.js';
|
|
2
|
-
type XHRRequestBody = Parameters<XMLHttpRequest['send']>[0];
|
|
3
|
-
type FetchRequestBody = RequestInit['body'];
|
|
4
|
-
interface RequestData {
|
|
5
|
-
body: XHRRequestBody | FetchRequestBody;
|
|
6
|
-
headers: Record<string, string>;
|
|
7
|
-
}
|
|
8
|
-
interface ResponseData {
|
|
9
|
-
body: any;
|
|
10
|
-
headers: Record<string, string>;
|
|
11
|
-
}
|
|
12
|
-
interface RequestResponseData {
|
|
13
|
-
readonly status: number;
|
|
14
|
-
readonly method: string;
|
|
15
|
-
url: string;
|
|
16
|
-
request: RequestData;
|
|
17
|
-
response: ResponseData;
|
|
18
|
-
}
|
|
19
|
-
type Sanitizer = (data: RequestResponseData) => RequestResponseData | null;
|
|
20
|
-
export interface Options {
|
|
21
|
-
sessionTokenHeader: string | boolean;
|
|
22
|
-
failuresOnly: boolean;
|
|
23
|
-
ignoreHeaders: Array<string> | boolean;
|
|
24
|
-
capturePayload: boolean;
|
|
25
|
-
sanitizer?: Sanitizer;
|
|
26
|
-
}
|
|
27
|
-
export default function (app: App, opts?: Partial<Options>): void;
|
|
28
|
-
export {};
|
package/cjs/modules/network.js
DELETED
|
@@ -1,203 +0,0 @@
|
|
|
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;
|