@openreplay/tracker 6.0.1 → 6.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/CHANGELOG.md +4 -0
- package/cjs/app/index.js +1 -1
- package/cjs/app/observer/top_observer.js +0 -2
- package/cjs/index.js +1 -1
- package/cjs/modules/network.d.ts +1 -1
- package/cjs/modules/network.js +125 -195
- package/cjs/utils.d.ts +3 -0
- package/cjs/utils.js +3 -0
- package/lib/app/index.js +1 -1
- package/lib/app/observer/top_observer.js +0 -2
- package/lib/index.js +1 -1
- package/lib/modules/network.d.ts +1 -1
- package/lib/modules/network.js +125 -195
- package/lib/utils.d.ts +3 -0
- package/lib/utils.js +3 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/cjs/app/index.js
CHANGED
|
@@ -34,7 +34,7 @@ class App {
|
|
|
34
34
|
this.stopCallbacks = [];
|
|
35
35
|
this.commitCallbacks = [];
|
|
36
36
|
this.activityState = ActivityState.NotActive;
|
|
37
|
-
this.version = '6.0.
|
|
37
|
+
this.version = '6.0.2'; // TODO: version compatability check inside each plugin.
|
|
38
38
|
this.compressionThreshold = 24 * 1000;
|
|
39
39
|
this._usingOldFetchPlugin = false;
|
|
40
40
|
this.delay = 0;
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const observer_js_1 = require("./observer.js");
|
|
4
4
|
const guards_js_1 = require("../guards.js");
|
|
5
|
-
const network_js_1 = require("../../modules/network.js");
|
|
6
5
|
const iframe_observer_js_1 = require("./iframe_observer.js");
|
|
7
6
|
const shadow_root_observer_js_1 = require("./shadow_root_observer.js");
|
|
8
7
|
const iframe_offsets_js_1 = require("./iframe_offsets.js");
|
|
@@ -70,7 +69,6 @@ class TopObserver extends observer_js_1.default {
|
|
|
70
69
|
//TODO: more explicit logic
|
|
71
70
|
) {
|
|
72
71
|
this.contextsSet.add(currentWin);
|
|
73
|
-
(0, network_js_1.default)(this.app, this.app.networkOptions, currentWin);
|
|
74
72
|
//@ts-ignore https://github.com/microsoft/TypeScript/issues/41684
|
|
75
73
|
this.contextCallbacks.forEach((cb) => cb(currentWin));
|
|
76
74
|
}
|
package/cjs/index.js
CHANGED
|
@@ -142,7 +142,7 @@ class API {
|
|
|
142
142
|
// no-cors issue only with text/plain or not-set Content-Type
|
|
143
143
|
// req.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
|
|
144
144
|
req.send(JSON.stringify({
|
|
145
|
-
trackerVersion: '6.0.
|
|
145
|
+
trackerVersion: '6.0.2',
|
|
146
146
|
projectKey: options.projectKey,
|
|
147
147
|
doNotTrack,
|
|
148
148
|
// TODO: add precise reason (an exact API missing)
|
package/cjs/modules/network.d.ts
CHANGED
|
@@ -24,5 +24,5 @@ export interface Options {
|
|
|
24
24
|
capturePayload: boolean;
|
|
25
25
|
sanitizer?: Sanitizer;
|
|
26
26
|
}
|
|
27
|
-
export default function (app: App, opts?: Partial<Options
|
|
27
|
+
export default function (app: App, opts?: Partial<Options>): void;
|
|
28
28
|
export {};
|
package/cjs/modules/network.js
CHANGED
|
@@ -2,43 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const messages_gen_js_1 = require("../app/messages.gen.js");
|
|
4
4
|
const utils_js_1 = require("../utils.js");
|
|
5
|
-
// Request:
|
|
6
|
-
// declare const enum BodyType {
|
|
7
|
-
// Blob = "Blob",
|
|
8
|
-
// ArrayBuffer = "ArrayBuffer",
|
|
9
|
-
// TypedArray = "TypedArray",
|
|
10
|
-
// DataView = "DataView",
|
|
11
|
-
// FormData = "FormData",
|
|
12
|
-
// URLSearchParams = "URLSearchParams",
|
|
13
|
-
// Document = "Document", // XHR only
|
|
14
|
-
// ReadableStream = "ReadableStream", // Fetch only
|
|
15
|
-
// Literal = "literal",
|
|
16
|
-
// Unknown = "unk",
|
|
17
|
-
// }
|
|
18
|
-
// XHRResponse body: ArrayBuffer, a Blob, a Document, a JavaScript Object, or a string
|
|
19
|
-
// TODO: extract maximum of useful information from any type of Request/Responce bodies
|
|
20
|
-
// function objectifyBody(body: any): RequestBody {
|
|
21
|
-
// if (body instanceof Blob) {
|
|
22
|
-
// return {
|
|
23
|
-
// body: `<Blob type: ${body.type}>; size: ${body.size}`,
|
|
24
|
-
// bodyType: BodyType.Blob,
|
|
25
|
-
// }
|
|
26
|
-
// }
|
|
27
|
-
// return {
|
|
28
|
-
// body,
|
|
29
|
-
// bodyType: BodyType.Literal,
|
|
30
|
-
// }
|
|
31
|
-
// }
|
|
32
|
-
function checkCacheByPerformanceTimings(requestUrl) {
|
|
33
|
-
if (performance) {
|
|
34
|
-
const timings = performance.getEntriesByName(requestUrl)[0];
|
|
35
|
-
if (timings) {
|
|
36
|
-
// @ts-ignore - weird ts typings, please refer to https://developer.mozilla.org/en-US/docs/Web/API/PerformanceNavigationTiming
|
|
37
|
-
return timings.transferSize === 0 || timings.responseStart - timings.requestStart < 10;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
5
|
function getXHRRequestDataObject(xhr) {
|
|
43
6
|
// @ts-ignore this is 3x faster than using Map<XHR, XHRRequestData>
|
|
44
7
|
if (!xhr.__or_req_data__) {
|
|
@@ -51,7 +14,7 @@ function getXHRRequestDataObject(xhr) {
|
|
|
51
14
|
function strMethod(method) {
|
|
52
15
|
return typeof method === 'string' ? method.toUpperCase() : 'GET';
|
|
53
16
|
}
|
|
54
|
-
function default_1(app, opts = {}
|
|
17
|
+
function default_1(app, opts = {}) {
|
|
55
18
|
const options = Object.assign({
|
|
56
19
|
failuresOnly: false,
|
|
57
20
|
ignoreHeaders: ['Cookie', 'Set-Cookie', 'Authorization'],
|
|
@@ -101,182 +64,149 @@ function default_1(app, opts = {}, customEnv) {
|
|
|
101
64
|
}
|
|
102
65
|
return JSON.stringify(r);
|
|
103
66
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return origFetch(input, init);
|
|
111
|
-
}
|
|
112
|
-
setSessionTokenHeader(function (name, value) {
|
|
113
|
-
if (init.headers === undefined) {
|
|
114
|
-
init.headers = {};
|
|
115
|
-
}
|
|
116
|
-
if (init.headers instanceof Headers) {
|
|
117
|
-
init.headers.append(name, value);
|
|
118
|
-
}
|
|
119
|
-
else if (Array.isArray(init.headers)) {
|
|
120
|
-
init.headers.push([name, value]);
|
|
67
|
+
const patchWindow = (context) => {
|
|
68
|
+
/* ====== Fetch ====== */
|
|
69
|
+
const origFetch = context.fetch.bind(context);
|
|
70
|
+
const trackFetch = (input, init = {}) => {
|
|
71
|
+
if (!(typeof input === 'string' || input instanceof URL) || app.isServiceURL(String(input))) {
|
|
72
|
+
return origFetch(input, init);
|
|
121
73
|
}
|
|
122
|
-
|
|
123
|
-
init.headers
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
74
|
+
setSessionTokenHeader(function (name, value) {
|
|
75
|
+
if (init.headers === undefined) {
|
|
76
|
+
init.headers = {};
|
|
77
|
+
}
|
|
78
|
+
if (init.headers instanceof Headers) {
|
|
79
|
+
init.headers.append(name, value);
|
|
80
|
+
}
|
|
81
|
+
else if (Array.isArray(init.headers)) {
|
|
82
|
+
init.headers.push([name, value]);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
init.headers[name] = value;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
const startTime = performance.now();
|
|
89
|
+
return origFetch(input, init).then((response) => {
|
|
90
|
+
const duration = performance.now() - startTime;
|
|
91
|
+
if (options.failuresOnly && response.status < 400) {
|
|
92
|
+
return response;
|
|
93
|
+
}
|
|
94
|
+
const r = response.clone();
|
|
95
|
+
r.text()
|
|
96
|
+
.then((text) => {
|
|
97
|
+
const reqHs = {};
|
|
98
|
+
const resHs = {};
|
|
99
|
+
if (ignoreHeaders !== true) {
|
|
100
|
+
// request headers
|
|
101
|
+
const writeReqHeader = ([n, v]) => {
|
|
102
|
+
if (!isHIgnored(n)) {
|
|
103
|
+
reqHs[n] = v;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
if (init.headers instanceof Headers) {
|
|
107
|
+
init.headers.forEach((v, n) => writeReqHeader([n, v]));
|
|
142
108
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
109
|
+
else if (Array.isArray(init.headers)) {
|
|
110
|
+
init.headers.forEach(writeReqHeader);
|
|
111
|
+
}
|
|
112
|
+
else if (typeof init.headers === 'object') {
|
|
113
|
+
Object.entries(init.headers).forEach(writeReqHeader);
|
|
114
|
+
}
|
|
115
|
+
// response headers
|
|
116
|
+
r.headers.forEach((v, n) => {
|
|
117
|
+
if (!isHIgnored(n))
|
|
118
|
+
resHs[n] = v;
|
|
119
|
+
});
|
|
152
120
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
121
|
+
const method = strMethod(init.method);
|
|
122
|
+
const reqResInfo = sanitize({
|
|
123
|
+
url: String(input),
|
|
124
|
+
method,
|
|
125
|
+
status: r.status,
|
|
126
|
+
request: {
|
|
127
|
+
headers: reqHs,
|
|
128
|
+
body: init.body,
|
|
129
|
+
},
|
|
130
|
+
response: {
|
|
131
|
+
headers: resHs,
|
|
132
|
+
body: text,
|
|
133
|
+
},
|
|
157
134
|
});
|
|
158
|
-
|
|
159
|
-
|
|
135
|
+
if (!reqResInfo) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
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));
|
|
139
|
+
})
|
|
140
|
+
.catch((e) => app.debug.error('Could not process Fetch response:', e));
|
|
141
|
+
return response;
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
context.fetch = trackFetch;
|
|
145
|
+
/* ====== <> ====== */
|
|
146
|
+
/* ====== XHR ====== */
|
|
147
|
+
const nativeOpen = context.XMLHttpRequest.prototype.open;
|
|
148
|
+
function trackXMLHttpReqOpen(initMethod, url) {
|
|
149
|
+
const xhr = this;
|
|
150
|
+
setSessionTokenHeader((name, value) => xhr.setRequestHeader(name, value));
|
|
151
|
+
let startTime = 0;
|
|
152
|
+
xhr.addEventListener('loadstart', (e) => {
|
|
153
|
+
startTime = e.timeStamp;
|
|
154
|
+
});
|
|
155
|
+
xhr.addEventListener('load', app.safe((e) => {
|
|
156
|
+
const { headers: reqHs, body: reqBody } = getXHRRequestDataObject(xhr);
|
|
157
|
+
const duration = startTime > 0 ? e.timeStamp - startTime : 0;
|
|
158
|
+
const hString = ignoreHeaders ? '' : xhr.getAllResponseHeaders(); // might be null (though only if no response received though)
|
|
159
|
+
const resHs = hString
|
|
160
|
+
? hString
|
|
161
|
+
.split('\r\n')
|
|
162
|
+
.map((h) => h.split(':'))
|
|
163
|
+
.filter((entry) => !isHIgnored(entry[0]))
|
|
164
|
+
.reduce((hds, [name, value]) => (Object.assign(Object.assign({}, hds), { [name]: value })), {})
|
|
165
|
+
: {};
|
|
166
|
+
const method = strMethod(initMethod);
|
|
160
167
|
const reqResInfo = sanitize({
|
|
161
|
-
url: String(
|
|
168
|
+
url: String(url),
|
|
162
169
|
method,
|
|
163
|
-
status:
|
|
170
|
+
status: xhr.status,
|
|
164
171
|
request: {
|
|
165
172
|
headers: reqHs,
|
|
166
|
-
body:
|
|
173
|
+
body: reqBody,
|
|
167
174
|
},
|
|
168
175
|
response: {
|
|
169
176
|
headers: resHs,
|
|
170
|
-
body:
|
|
177
|
+
body: xhr.response,
|
|
171
178
|
},
|
|
172
179
|
});
|
|
173
180
|
if (!reqResInfo) {
|
|
174
181
|
return;
|
|
175
182
|
}
|
|
176
|
-
app.send((0, messages_gen_js_1.NetworkRequest)('
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
};
|
|
182
|
-
if (customEnv) {
|
|
183
|
-
if ('fetch' in customEnv) {
|
|
184
|
-
customEnv.fetch = trackFetch;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
else {
|
|
188
|
-
window.fetch = trackFetch;
|
|
189
|
-
}
|
|
190
|
-
/* ====== <> ====== */
|
|
191
|
-
/* ====== XHR ====== */
|
|
192
|
-
const nativeOpen = customEnv
|
|
193
|
-
? customEnv.XMLHttpRequest.prototype.open
|
|
194
|
-
: XMLHttpRequest.prototype.open;
|
|
195
|
-
function trackXMLHttpReqOpen(initMethod, url) {
|
|
196
|
-
// @ts-ignore ??? this -> XMLHttpRequest
|
|
197
|
-
const xhr = this;
|
|
198
|
-
setSessionTokenHeader((name, value) => xhr.setRequestHeader(name, value));
|
|
199
|
-
let startTime = 0;
|
|
200
|
-
xhr.addEventListener('loadstart', (e) => {
|
|
201
|
-
startTime = e.timeStamp;
|
|
202
|
-
});
|
|
203
|
-
xhr.addEventListener('load', app.safe((e) => {
|
|
204
|
-
const { headers: reqHs, body: reqBody } = getXHRRequestDataObject(xhr);
|
|
205
|
-
const duration = startTime > 0 ? e.timeStamp - startTime : 0;
|
|
206
|
-
const hString = ignoreHeaders ? '' : xhr.getAllResponseHeaders(); // might be null (though only if no response received though)
|
|
207
|
-
const resHs = hString
|
|
208
|
-
? hString
|
|
209
|
-
.split('\r\n')
|
|
210
|
-
.map((h) => h.split(':'))
|
|
211
|
-
.filter((entry) => !isHIgnored(entry[0]))
|
|
212
|
-
.reduce((hds, [name, value]) => (Object.assign(Object.assign({}, hds), { [name]: value })), {})
|
|
213
|
-
: {};
|
|
214
|
-
const method = strMethod(initMethod);
|
|
215
|
-
const reqResInfo = sanitize({
|
|
216
|
-
url: String(url),
|
|
217
|
-
method,
|
|
218
|
-
status: xhr.status,
|
|
219
|
-
request: {
|
|
220
|
-
headers: reqHs,
|
|
221
|
-
body: reqBody,
|
|
222
|
-
},
|
|
223
|
-
response: {
|
|
224
|
-
headers: resHs,
|
|
225
|
-
body: xhr.response,
|
|
226
|
-
},
|
|
227
|
-
});
|
|
228
|
-
if (!reqResInfo) {
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
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));
|
|
232
|
-
}));
|
|
233
|
-
//TODO: handle error (though it has no Error API nor any useful information)
|
|
234
|
-
//xhr.addEventListener('error', (e) => {})
|
|
235
|
-
// @ts-ignore ??? this -> XMLHttpRequest
|
|
236
|
-
return nativeOpen.apply(this, arguments);
|
|
237
|
-
}
|
|
238
|
-
if (customEnv) {
|
|
239
|
-
if ('XMLHttpRequest' in customEnv) {
|
|
240
|
-
customEnv.XMLHttpRequest.prototype.open = trackXMLHttpReqOpen.bind(customEnv);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
else {
|
|
244
|
-
XMLHttpRequest.prototype.open = trackXMLHttpReqOpen;
|
|
245
|
-
}
|
|
246
|
-
const nativeSend = XMLHttpRequest.prototype.send;
|
|
247
|
-
function trackXHRSend(body) {
|
|
248
|
-
// @ts-ignore ??? this -> XMLHttpRequest
|
|
249
|
-
const rdo = getXHRRequestDataObject(this);
|
|
250
|
-
rdo.body = body;
|
|
251
|
-
// @ts-ignore ??? this -> XMLHttpRequest
|
|
252
|
-
return nativeSend.apply(this, arguments);
|
|
253
|
-
}
|
|
254
|
-
if (customEnv) {
|
|
255
|
-
if ('XMLHttpRequest' in customEnv) {
|
|
256
|
-
customEnv.XMLHttpRequest.prototype.send = trackXHRSend.bind(customEnv);
|
|
183
|
+
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));
|
|
184
|
+
}));
|
|
185
|
+
//TODO: handle error (though it has no Error API nor any useful information)
|
|
186
|
+
//xhr.addEventListener('error', (e) => {})
|
|
187
|
+
return nativeOpen.apply(this, arguments);
|
|
257
188
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
262
|
-
const nativeSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
|
|
263
|
-
function trackSetReqHeader(name, value) {
|
|
264
|
-
if (!isHIgnored(name)) {
|
|
265
|
-
// @ts-ignore ??? this -> XMLHttpRequest
|
|
189
|
+
context.XMLHttpRequest.prototype.open = trackXMLHttpReqOpen;
|
|
190
|
+
const nativeSend = context.XMLHttpRequest.prototype.send;
|
|
191
|
+
function trackXHRSend(body) {
|
|
266
192
|
const rdo = getXHRRequestDataObject(this);
|
|
267
|
-
rdo.
|
|
193
|
+
rdo.body = body;
|
|
194
|
+
// @ts-ignore ??? this -> XMLHttpRequest
|
|
195
|
+
return nativeSend.apply(this, arguments);
|
|
268
196
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
197
|
+
context.XMLHttpRequest.prototype.send = trackXHRSend;
|
|
198
|
+
const nativeSetRequestHeader = context.XMLHttpRequest.prototype.setRequestHeader;
|
|
199
|
+
function trackSetReqHeader(name, value) {
|
|
200
|
+
if (!isHIgnored(name)) {
|
|
201
|
+
const rdo = getXHRRequestDataObject(this);
|
|
202
|
+
rdo.headers[name] = value;
|
|
203
|
+
}
|
|
204
|
+
return nativeSetRequestHeader.apply(this, arguments);
|
|
275
205
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
206
|
+
context.XMLHttpRequest.prototype.setRequestHeader = trackSetReqHeader;
|
|
207
|
+
/* ====== <> ====== */
|
|
208
|
+
};
|
|
209
|
+
patchWindow(window);
|
|
210
|
+
app.observer.attachContextCallback(app.safe(patchWindow));
|
|
281
211
|
}
|
|
282
212
|
exports.default = default_1;
|
package/cjs/utils.d.ts
CHANGED
|
@@ -12,4 +12,7 @@ export declare function deprecationWarn(nameOfFeature: string, useInstead: strin
|
|
|
12
12
|
export declare function getLabelAttribute(e: Element): string | null;
|
|
13
13
|
export declare function hasOpenreplayAttribute(e: Element, attr: string): boolean;
|
|
14
14
|
export declare function isIframeCrossdomain(e: HTMLIFrameElement): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* checks if iframe is accessible
|
|
17
|
+
**/
|
|
15
18
|
export declare function canAccessIframe(iframe: HTMLIFrameElement): boolean;
|
package/cjs/utils.js
CHANGED
package/lib/app/index.js
CHANGED
|
@@ -31,7 +31,7 @@ export default class App {
|
|
|
31
31
|
this.stopCallbacks = [];
|
|
32
32
|
this.commitCallbacks = [];
|
|
33
33
|
this.activityState = ActivityState.NotActive;
|
|
34
|
-
this.version = '6.0.
|
|
34
|
+
this.version = '6.0.2'; // TODO: version compatability check inside each plugin.
|
|
35
35
|
this.compressionThreshold = 24 * 1000;
|
|
36
36
|
this._usingOldFetchPlugin = false;
|
|
37
37
|
this.delay = 0;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import Observer from './observer.js';
|
|
2
2
|
import { isElementNode, hasTag } from '../guards.js';
|
|
3
|
-
import Network from '../../modules/network.js';
|
|
4
3
|
import IFrameObserver from './iframe_observer.js';
|
|
5
4
|
import ShadowRootObserver from './shadow_root_observer.js';
|
|
6
5
|
import IFrameOffsets from './iframe_offsets.js';
|
|
@@ -68,7 +67,6 @@ export default class TopObserver extends Observer {
|
|
|
68
67
|
//TODO: more explicit logic
|
|
69
68
|
) {
|
|
70
69
|
this.contextsSet.add(currentWin);
|
|
71
|
-
Network(this.app, this.app.networkOptions, currentWin);
|
|
72
70
|
//@ts-ignore https://github.com/microsoft/TypeScript/issues/41684
|
|
73
71
|
this.contextCallbacks.forEach((cb) => cb(currentWin));
|
|
74
72
|
}
|
package/lib/index.js
CHANGED
|
@@ -137,7 +137,7 @@ export default class API {
|
|
|
137
137
|
// no-cors issue only with text/plain or not-set Content-Type
|
|
138
138
|
// req.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
|
|
139
139
|
req.send(JSON.stringify({
|
|
140
|
-
trackerVersion: '6.0.
|
|
140
|
+
trackerVersion: '6.0.2',
|
|
141
141
|
projectKey: options.projectKey,
|
|
142
142
|
doNotTrack,
|
|
143
143
|
// TODO: add precise reason (an exact API missing)
|
package/lib/modules/network.d.ts
CHANGED
|
@@ -24,5 +24,5 @@ export interface Options {
|
|
|
24
24
|
capturePayload: boolean;
|
|
25
25
|
sanitizer?: Sanitizer;
|
|
26
26
|
}
|
|
27
|
-
export default function (app: App, opts?: Partial<Options
|
|
27
|
+
export default function (app: App, opts?: Partial<Options>): void;
|
|
28
28
|
export {};
|
package/lib/modules/network.js
CHANGED
|
@@ -1,42 +1,5 @@
|
|
|
1
1
|
import { NetworkRequest } from '../app/messages.gen.js';
|
|
2
2
|
import { getTimeOrigin } from '../utils.js';
|
|
3
|
-
// Request:
|
|
4
|
-
// declare const enum BodyType {
|
|
5
|
-
// Blob = "Blob",
|
|
6
|
-
// ArrayBuffer = "ArrayBuffer",
|
|
7
|
-
// TypedArray = "TypedArray",
|
|
8
|
-
// DataView = "DataView",
|
|
9
|
-
// FormData = "FormData",
|
|
10
|
-
// URLSearchParams = "URLSearchParams",
|
|
11
|
-
// Document = "Document", // XHR only
|
|
12
|
-
// ReadableStream = "ReadableStream", // Fetch only
|
|
13
|
-
// Literal = "literal",
|
|
14
|
-
// Unknown = "unk",
|
|
15
|
-
// }
|
|
16
|
-
// XHRResponse body: ArrayBuffer, a Blob, a Document, a JavaScript Object, or a string
|
|
17
|
-
// TODO: extract maximum of useful information from any type of Request/Responce bodies
|
|
18
|
-
// function objectifyBody(body: any): RequestBody {
|
|
19
|
-
// if (body instanceof Blob) {
|
|
20
|
-
// return {
|
|
21
|
-
// body: `<Blob type: ${body.type}>; size: ${body.size}`,
|
|
22
|
-
// bodyType: BodyType.Blob,
|
|
23
|
-
// }
|
|
24
|
-
// }
|
|
25
|
-
// return {
|
|
26
|
-
// body,
|
|
27
|
-
// bodyType: BodyType.Literal,
|
|
28
|
-
// }
|
|
29
|
-
// }
|
|
30
|
-
function checkCacheByPerformanceTimings(requestUrl) {
|
|
31
|
-
if (performance) {
|
|
32
|
-
const timings = performance.getEntriesByName(requestUrl)[0];
|
|
33
|
-
if (timings) {
|
|
34
|
-
// @ts-ignore - weird ts typings, please refer to https://developer.mozilla.org/en-US/docs/Web/API/PerformanceNavigationTiming
|
|
35
|
-
return timings.transferSize === 0 || timings.responseStart - timings.requestStart < 10;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
3
|
function getXHRRequestDataObject(xhr) {
|
|
41
4
|
// @ts-ignore this is 3x faster than using Map<XHR, XHRRequestData>
|
|
42
5
|
if (!xhr.__or_req_data__) {
|
|
@@ -49,7 +12,7 @@ function getXHRRequestDataObject(xhr) {
|
|
|
49
12
|
function strMethod(method) {
|
|
50
13
|
return typeof method === 'string' ? method.toUpperCase() : 'GET';
|
|
51
14
|
}
|
|
52
|
-
export default function (app, opts = {}
|
|
15
|
+
export default function (app, opts = {}) {
|
|
53
16
|
const options = Object.assign({
|
|
54
17
|
failuresOnly: false,
|
|
55
18
|
ignoreHeaders: ['Cookie', 'Set-Cookie', 'Authorization'],
|
|
@@ -99,181 +62,148 @@ export default function (app, opts = {}, customEnv) {
|
|
|
99
62
|
}
|
|
100
63
|
return JSON.stringify(r);
|
|
101
64
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return origFetch(input, init);
|
|
109
|
-
}
|
|
110
|
-
setSessionTokenHeader(function (name, value) {
|
|
111
|
-
if (init.headers === undefined) {
|
|
112
|
-
init.headers = {};
|
|
113
|
-
}
|
|
114
|
-
if (init.headers instanceof Headers) {
|
|
115
|
-
init.headers.append(name, value);
|
|
116
|
-
}
|
|
117
|
-
else if (Array.isArray(init.headers)) {
|
|
118
|
-
init.headers.push([name, value]);
|
|
65
|
+
const patchWindow = (context) => {
|
|
66
|
+
/* ====== Fetch ====== */
|
|
67
|
+
const origFetch = context.fetch.bind(context);
|
|
68
|
+
const trackFetch = (input, init = {}) => {
|
|
69
|
+
if (!(typeof input === 'string' || input instanceof URL) || app.isServiceURL(String(input))) {
|
|
70
|
+
return origFetch(input, init);
|
|
119
71
|
}
|
|
120
|
-
|
|
121
|
-
init.headers
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
72
|
+
setSessionTokenHeader(function (name, value) {
|
|
73
|
+
if (init.headers === undefined) {
|
|
74
|
+
init.headers = {};
|
|
75
|
+
}
|
|
76
|
+
if (init.headers instanceof Headers) {
|
|
77
|
+
init.headers.append(name, value);
|
|
78
|
+
}
|
|
79
|
+
else if (Array.isArray(init.headers)) {
|
|
80
|
+
init.headers.push([name, value]);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
init.headers[name] = value;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
const startTime = performance.now();
|
|
87
|
+
return origFetch(input, init).then((response) => {
|
|
88
|
+
const duration = performance.now() - startTime;
|
|
89
|
+
if (options.failuresOnly && response.status < 400) {
|
|
90
|
+
return response;
|
|
91
|
+
}
|
|
92
|
+
const r = response.clone();
|
|
93
|
+
r.text()
|
|
94
|
+
.then((text) => {
|
|
95
|
+
const reqHs = {};
|
|
96
|
+
const resHs = {};
|
|
97
|
+
if (ignoreHeaders !== true) {
|
|
98
|
+
// request headers
|
|
99
|
+
const writeReqHeader = ([n, v]) => {
|
|
100
|
+
if (!isHIgnored(n)) {
|
|
101
|
+
reqHs[n] = v;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
if (init.headers instanceof Headers) {
|
|
105
|
+
init.headers.forEach((v, n) => writeReqHeader([n, v]));
|
|
140
106
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
107
|
+
else if (Array.isArray(init.headers)) {
|
|
108
|
+
init.headers.forEach(writeReqHeader);
|
|
109
|
+
}
|
|
110
|
+
else if (typeof init.headers === 'object') {
|
|
111
|
+
Object.entries(init.headers).forEach(writeReqHeader);
|
|
112
|
+
}
|
|
113
|
+
// response headers
|
|
114
|
+
r.headers.forEach((v, n) => {
|
|
115
|
+
if (!isHIgnored(n))
|
|
116
|
+
resHs[n] = v;
|
|
117
|
+
});
|
|
150
118
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
119
|
+
const method = strMethod(init.method);
|
|
120
|
+
const reqResInfo = sanitize({
|
|
121
|
+
url: String(input),
|
|
122
|
+
method,
|
|
123
|
+
status: r.status,
|
|
124
|
+
request: {
|
|
125
|
+
headers: reqHs,
|
|
126
|
+
body: init.body,
|
|
127
|
+
},
|
|
128
|
+
response: {
|
|
129
|
+
headers: resHs,
|
|
130
|
+
body: text,
|
|
131
|
+
},
|
|
155
132
|
});
|
|
156
|
-
|
|
157
|
-
|
|
133
|
+
if (!reqResInfo) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
app.send(NetworkRequest('fetch', method, String(reqResInfo.url), stringify(reqResInfo.request), stringify(reqResInfo.response), r.status, startTime + getTimeOrigin(), duration));
|
|
137
|
+
})
|
|
138
|
+
.catch((e) => app.debug.error('Could not process Fetch response:', e));
|
|
139
|
+
return response;
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
context.fetch = trackFetch;
|
|
143
|
+
/* ====== <> ====== */
|
|
144
|
+
/* ====== XHR ====== */
|
|
145
|
+
const nativeOpen = context.XMLHttpRequest.prototype.open;
|
|
146
|
+
function trackXMLHttpReqOpen(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);
|
|
158
165
|
const reqResInfo = sanitize({
|
|
159
|
-
url: String(
|
|
166
|
+
url: String(url),
|
|
160
167
|
method,
|
|
161
|
-
status:
|
|
168
|
+
status: xhr.status,
|
|
162
169
|
request: {
|
|
163
170
|
headers: reqHs,
|
|
164
|
-
body:
|
|
171
|
+
body: reqBody,
|
|
165
172
|
},
|
|
166
173
|
response: {
|
|
167
174
|
headers: resHs,
|
|
168
|
-
body:
|
|
175
|
+
body: xhr.response,
|
|
169
176
|
},
|
|
170
177
|
});
|
|
171
178
|
if (!reqResInfo) {
|
|
172
179
|
return;
|
|
173
180
|
}
|
|
174
|
-
app.send(NetworkRequest('
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
};
|
|
180
|
-
if (customEnv) {
|
|
181
|
-
if ('fetch' in customEnv) {
|
|
182
|
-
customEnv.fetch = trackFetch;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
window.fetch = trackFetch;
|
|
187
|
-
}
|
|
188
|
-
/* ====== <> ====== */
|
|
189
|
-
/* ====== XHR ====== */
|
|
190
|
-
const nativeOpen = customEnv
|
|
191
|
-
? customEnv.XMLHttpRequest.prototype.open
|
|
192
|
-
: XMLHttpRequest.prototype.open;
|
|
193
|
-
function trackXMLHttpReqOpen(initMethod, url) {
|
|
194
|
-
// @ts-ignore ??? this -> XMLHttpRequest
|
|
195
|
-
const xhr = this;
|
|
196
|
-
setSessionTokenHeader((name, value) => xhr.setRequestHeader(name, value));
|
|
197
|
-
let startTime = 0;
|
|
198
|
-
xhr.addEventListener('loadstart', (e) => {
|
|
199
|
-
startTime = e.timeStamp;
|
|
200
|
-
});
|
|
201
|
-
xhr.addEventListener('load', app.safe((e) => {
|
|
202
|
-
const { headers: reqHs, body: reqBody } = getXHRRequestDataObject(xhr);
|
|
203
|
-
const duration = startTime > 0 ? e.timeStamp - startTime : 0;
|
|
204
|
-
const hString = ignoreHeaders ? '' : xhr.getAllResponseHeaders(); // might be null (though only if no response received though)
|
|
205
|
-
const resHs = hString
|
|
206
|
-
? hString
|
|
207
|
-
.split('\r\n')
|
|
208
|
-
.map((h) => h.split(':'))
|
|
209
|
-
.filter((entry) => !isHIgnored(entry[0]))
|
|
210
|
-
.reduce((hds, [name, value]) => (Object.assign(Object.assign({}, hds), { [name]: value })), {})
|
|
211
|
-
: {};
|
|
212
|
-
const method = strMethod(initMethod);
|
|
213
|
-
const reqResInfo = sanitize({
|
|
214
|
-
url: String(url),
|
|
215
|
-
method,
|
|
216
|
-
status: xhr.status,
|
|
217
|
-
request: {
|
|
218
|
-
headers: reqHs,
|
|
219
|
-
body: reqBody,
|
|
220
|
-
},
|
|
221
|
-
response: {
|
|
222
|
-
headers: resHs,
|
|
223
|
-
body: xhr.response,
|
|
224
|
-
},
|
|
225
|
-
});
|
|
226
|
-
if (!reqResInfo) {
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
app.send(NetworkRequest('xhr', method, String(reqResInfo.url), stringify(reqResInfo.request), stringify(reqResInfo.response), xhr.status, startTime + getTimeOrigin(), duration));
|
|
230
|
-
}));
|
|
231
|
-
//TODO: handle error (though it has no Error API nor any useful information)
|
|
232
|
-
//xhr.addEventListener('error', (e) => {})
|
|
233
|
-
// @ts-ignore ??? this -> XMLHttpRequest
|
|
234
|
-
return nativeOpen.apply(this, arguments);
|
|
235
|
-
}
|
|
236
|
-
if (customEnv) {
|
|
237
|
-
if ('XMLHttpRequest' in customEnv) {
|
|
238
|
-
customEnv.XMLHttpRequest.prototype.open = trackXMLHttpReqOpen.bind(customEnv);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
else {
|
|
242
|
-
XMLHttpRequest.prototype.open = trackXMLHttpReqOpen;
|
|
243
|
-
}
|
|
244
|
-
const nativeSend = XMLHttpRequest.prototype.send;
|
|
245
|
-
function trackXHRSend(body) {
|
|
246
|
-
// @ts-ignore ??? this -> XMLHttpRequest
|
|
247
|
-
const rdo = getXHRRequestDataObject(this);
|
|
248
|
-
rdo.body = body;
|
|
249
|
-
// @ts-ignore ??? this -> XMLHttpRequest
|
|
250
|
-
return nativeSend.apply(this, arguments);
|
|
251
|
-
}
|
|
252
|
-
if (customEnv) {
|
|
253
|
-
if ('XMLHttpRequest' in customEnv) {
|
|
254
|
-
customEnv.XMLHttpRequest.prototype.send = trackXHRSend.bind(customEnv);
|
|
181
|
+
app.send(NetworkRequest('xhr', method, String(reqResInfo.url), stringify(reqResInfo.request), stringify(reqResInfo.response), xhr.status, startTime + 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);
|
|
255
186
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}
|
|
260
|
-
const nativeSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
|
|
261
|
-
function trackSetReqHeader(name, value) {
|
|
262
|
-
if (!isHIgnored(name)) {
|
|
263
|
-
// @ts-ignore ??? this -> XMLHttpRequest
|
|
187
|
+
context.XMLHttpRequest.prototype.open = trackXMLHttpReqOpen;
|
|
188
|
+
const nativeSend = context.XMLHttpRequest.prototype.send;
|
|
189
|
+
function trackXHRSend(body) {
|
|
264
190
|
const rdo = getXHRRequestDataObject(this);
|
|
265
|
-
rdo.
|
|
191
|
+
rdo.body = body;
|
|
192
|
+
// @ts-ignore ??? this -> XMLHttpRequest
|
|
193
|
+
return nativeSend.apply(this, arguments);
|
|
266
194
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
195
|
+
context.XMLHttpRequest.prototype.send = trackXHRSend;
|
|
196
|
+
const nativeSetRequestHeader = context.XMLHttpRequest.prototype.setRequestHeader;
|
|
197
|
+
function trackSetReqHeader(name, value) {
|
|
198
|
+
if (!isHIgnored(name)) {
|
|
199
|
+
const rdo = getXHRRequestDataObject(this);
|
|
200
|
+
rdo.headers[name] = value;
|
|
201
|
+
}
|
|
202
|
+
return nativeSetRequestHeader.apply(this, arguments);
|
|
273
203
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
204
|
+
context.XMLHttpRequest.prototype.setRequestHeader = trackSetReqHeader;
|
|
205
|
+
/* ====== <> ====== */
|
|
206
|
+
};
|
|
207
|
+
patchWindow(window);
|
|
208
|
+
app.observer.attachContextCallback(app.safe(patchWindow));
|
|
279
209
|
}
|
package/lib/utils.d.ts
CHANGED
|
@@ -12,4 +12,7 @@ export declare function deprecationWarn(nameOfFeature: string, useInstead: strin
|
|
|
12
12
|
export declare function getLabelAttribute(e: Element): string | null;
|
|
13
13
|
export declare function hasOpenreplayAttribute(e: Element, attr: string): boolean;
|
|
14
14
|
export declare function isIframeCrossdomain(e: HTMLIFrameElement): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* checks if iframe is accessible
|
|
17
|
+
**/
|
|
15
18
|
export declare function canAccessIframe(iframe: HTMLIFrameElement): boolean;
|
package/lib/utils.js
CHANGED