@openreplay/tracker 8.0.0 → 8.0.1-beta14
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/index.d.ts +4 -1
- package/cjs/app/index.js +18 -2
- package/cjs/app/session.d.ts +10 -0
- package/cjs/app/session.js +3 -0
- package/cjs/index.d.ts +11 -0
- package/cjs/index.js +28 -1
- package/cjs/modules/Network/fetchProxy.d.ts +34 -0
- package/cjs/modules/Network/fetchProxy.js +240 -0
- package/cjs/modules/Network/index.d.ts +3 -0
- package/cjs/modules/Network/index.js +9 -0
- package/cjs/modules/Network/networkMessage.d.ts +49 -0
- package/cjs/modules/Network/networkMessage.js +82 -0
- package/cjs/modules/Network/types.d.ts +13 -0
- package/cjs/modules/Network/types.js +3 -0
- package/cjs/modules/Network/utils.d.ts +11 -0
- package/cjs/modules/Network/utils.js +213 -0
- package/cjs/modules/Network/xhrProxy.d.ts +47 -0
- package/cjs/modules/Network/xhrProxy.js +209 -0
- package/cjs/modules/console.js +21 -13
- package/cjs/modules/featureFlags.d.ts +25 -0
- package/cjs/modules/featureFlags.js +100 -0
- package/cjs/modules/network.d.ts +3 -4
- package/cjs/modules/network.js +13 -3
- package/coverage/clover.xml +898 -449
- package/coverage/coverage-final.json +13 -8
- package/coverage/lcov-report/index.html +50 -35
- package/coverage/lcov-report/main/app/guards.ts.html +1 -1
- package/coverage/lcov-report/main/app/index.html +21 -21
- package/coverage/lcov-report/main/app/index.ts.html +46 -4
- package/coverage/lcov-report/main/app/logger.ts.html +1 -1
- package/coverage/lcov-report/main/app/messages.gen.ts.html +146 -146
- package/coverage/lcov-report/main/app/observer/iframe_observer.ts.html +1 -1
- package/coverage/lcov-report/main/app/observer/iframe_offsets.ts.html +1 -1
- package/coverage/lcov-report/main/app/observer/index.html +1 -1
- package/coverage/lcov-report/main/app/observer/shadow_root_observer.ts.html +1 -1
- package/coverage/lcov-report/main/app/observer/top_observer.ts.html +1 -1
- package/coverage/lcov-report/main/app/sanitizer.ts.html +1 -1
- package/coverage/lcov-report/main/app/session.ts.html +47 -5
- package/coverage/lcov-report/main/app/ticker.ts.html +1 -1
- package/coverage/lcov-report/main/index.html +20 -20
- package/coverage/lcov-report/main/index.ts.html +38 -26
- package/coverage/lcov-report/main/modules/Network/fetchProxy.ts.html +949 -0
- package/coverage/lcov-report/main/modules/Network/index.html +176 -0
- package/coverage/lcov-report/main/modules/Network/index.ts.html +169 -0
- package/coverage/lcov-report/main/modules/Network/networkMessage.ts.html +382 -0
- package/coverage/lcov-report/main/modules/Network/utils.ts.html +700 -0
- package/coverage/lcov-report/main/modules/Network/xhrProxy.ts.html +823 -0
- package/coverage/lcov-report/main/modules/attributeSender.ts.html +1 -1
- package/coverage/lcov-report/main/modules/axiosSpy.ts.html +1 -1
- package/coverage/lcov-report/main/modules/connection.ts.html +1 -1
- package/coverage/lcov-report/main/modules/console.ts.html +174 -147
- package/coverage/lcov-report/main/modules/constructedStyleSheets.ts.html +1 -1
- package/coverage/lcov-report/main/modules/cssrules.ts.html +1 -1
- package/coverage/lcov-report/main/modules/exception.ts.html +1 -1
- package/coverage/lcov-report/main/modules/featureFlags.ts.html +102 -51
- package/coverage/lcov-report/main/modules/focus.ts.html +1 -1
- package/coverage/lcov-report/main/modules/fonts.ts.html +1 -1
- package/coverage/lcov-report/main/modules/img.ts.html +1 -1
- package/coverage/lcov-report/main/modules/index.html +33 -33
- package/coverage/lcov-report/main/modules/input.ts.html +1 -1
- package/coverage/lcov-report/main/modules/mouse.ts.html +1 -1
- package/coverage/lcov-report/main/modules/network.ts.html +70 -70
- package/coverage/lcov-report/main/modules/performance.ts.html +1 -1
- package/coverage/lcov-report/main/modules/scroll.ts.html +1 -1
- package/coverage/lcov-report/main/modules/selection.ts.html +1 -1
- package/coverage/lcov-report/main/modules/tabs.ts.html +1 -1
- package/coverage/lcov-report/main/modules/timing.ts.html +1 -1
- package/coverage/lcov-report/main/modules/viewport.ts.html +1 -1
- package/coverage/lcov-report/main/utils.ts.html +45 -45
- package/coverage/lcov-report/webworker/BatchWriter.ts.html +1 -1
- package/coverage/lcov-report/webworker/MessageEncoder.gen.ts.html +1 -1
- package/coverage/lcov-report/webworker/PrimitiveEncoder.ts.html +1 -1
- package/coverage/lcov-report/webworker/QueueSender.ts.html +1 -1
- package/coverage/lcov-report/webworker/index.html +1 -1
- package/coverage/lcov-report/webworker/index.ts.html +1 -1
- package/coverage/lcov.info +1558 -726
- package/lib/app/index.d.ts +4 -1
- package/lib/app/index.js +18 -2
- package/lib/app/session.d.ts +10 -0
- package/lib/app/session.js +3 -0
- package/lib/index.d.ts +11 -0
- package/lib/index.js +28 -1
- package/lib/modules/Network/fetchProxy.d.ts +34 -0
- package/lib/modules/Network/fetchProxy.js +234 -0
- package/lib/modules/Network/index.d.ts +3 -0
- package/lib/modules/Network/index.js +6 -0
- package/lib/modules/Network/networkMessage.d.ts +49 -0
- package/lib/modules/Network/networkMessage.js +78 -0
- package/lib/modules/Network/types.d.ts +13 -0
- package/lib/modules/Network/types.js +2 -0
- package/lib/modules/Network/utils.d.ts +11 -0
- package/lib/modules/Network/utils.js +201 -0
- package/lib/modules/Network/xhrProxy.d.ts +47 -0
- package/lib/modules/Network/xhrProxy.js +204 -0
- package/lib/modules/console.js +21 -13
- package/lib/modules/featureFlags.d.ts +25 -0
- package/lib/modules/featureFlags.js +97 -0
- package/lib/modules/network.d.ts +3 -4
- package/lib/modules/network.js +13 -3
- package/package.json +1 -1
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getURL = exports.formatByteSize = exports.isIterable = exports.isPureObject = exports.genFormattedBody = exports.genGetDataByUrl = exports.genStringBody = exports.getStringResponseByType = exports.genResponseByType = void 0;
|
|
4
|
+
const genResponseByType = (responseType, response) => {
|
|
5
|
+
let result = '';
|
|
6
|
+
switch (responseType) {
|
|
7
|
+
case '':
|
|
8
|
+
case 'text':
|
|
9
|
+
case 'json':
|
|
10
|
+
if (typeof response == 'string') {
|
|
11
|
+
try {
|
|
12
|
+
result = JSON.parse(response);
|
|
13
|
+
}
|
|
14
|
+
catch (e) {
|
|
15
|
+
// not a JSON string
|
|
16
|
+
result = response.slice(0, 10000);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
else if (isPureObject(response) || Array.isArray(response)) {
|
|
20
|
+
result = JSON.stringify(response);
|
|
21
|
+
}
|
|
22
|
+
else if (typeof response !== 'undefined') {
|
|
23
|
+
result = Object.prototype.toString.call(response);
|
|
24
|
+
}
|
|
25
|
+
break;
|
|
26
|
+
case 'blob':
|
|
27
|
+
case 'document':
|
|
28
|
+
case 'arraybuffer':
|
|
29
|
+
default:
|
|
30
|
+
if (typeof response !== 'undefined') {
|
|
31
|
+
result = Object.prototype.toString.call(response);
|
|
32
|
+
}
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
exports.genResponseByType = genResponseByType;
|
|
38
|
+
const getStringResponseByType = (responseType, response) => {
|
|
39
|
+
let result = '';
|
|
40
|
+
switch (responseType) {
|
|
41
|
+
case '':
|
|
42
|
+
case 'text':
|
|
43
|
+
case 'json':
|
|
44
|
+
if (typeof response == 'string') {
|
|
45
|
+
result = response;
|
|
46
|
+
}
|
|
47
|
+
else if (isPureObject(response) || Array.isArray(response)) {
|
|
48
|
+
result = JSON.stringify(response);
|
|
49
|
+
}
|
|
50
|
+
else if (typeof response !== 'undefined') {
|
|
51
|
+
result = Object.prototype.toString.call(response);
|
|
52
|
+
}
|
|
53
|
+
break;
|
|
54
|
+
case 'blob':
|
|
55
|
+
case 'document':
|
|
56
|
+
case 'arraybuffer':
|
|
57
|
+
default:
|
|
58
|
+
if (typeof response !== 'undefined') {
|
|
59
|
+
result = Object.prototype.toString.call(response);
|
|
60
|
+
}
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
};
|
|
65
|
+
exports.getStringResponseByType = getStringResponseByType;
|
|
66
|
+
const genStringBody = (body) => {
|
|
67
|
+
if (!body) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
let result;
|
|
71
|
+
if (typeof body === 'string') {
|
|
72
|
+
if (body[0] === '{' || body[0] === '[') {
|
|
73
|
+
result = body;
|
|
74
|
+
}
|
|
75
|
+
// 'a=1&b=2' => try to parse as query
|
|
76
|
+
const arr = body.split('&');
|
|
77
|
+
if (arr.length === 1) {
|
|
78
|
+
// not a query, parse as original string
|
|
79
|
+
result = body;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
// 'a=1&b=2&c' => parse as query
|
|
83
|
+
result = arr.join(',');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else if (isIterable(body)) {
|
|
87
|
+
// FormData or URLSearchParams or Array
|
|
88
|
+
const arr = [];
|
|
89
|
+
for (const [key, value] of body) {
|
|
90
|
+
arr.push(`${key}=${typeof value === 'string' ? value : '[object Object]'}`);
|
|
91
|
+
}
|
|
92
|
+
result = arr.join(',');
|
|
93
|
+
}
|
|
94
|
+
else if (body instanceof Blob ||
|
|
95
|
+
body instanceof ReadableStream ||
|
|
96
|
+
body instanceof ArrayBuffer) {
|
|
97
|
+
result = 'byte data';
|
|
98
|
+
}
|
|
99
|
+
else if (isPureObject(body)) {
|
|
100
|
+
// overriding ArrayBufferView which is not convertable to string
|
|
101
|
+
result = body;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
result = `can't parse body ${typeof body}`;
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
};
|
|
108
|
+
exports.genStringBody = genStringBody;
|
|
109
|
+
const genGetDataByUrl = (url, getData = {}) => {
|
|
110
|
+
if (!isPureObject(getData)) {
|
|
111
|
+
getData = {};
|
|
112
|
+
}
|
|
113
|
+
let query = url ? url.split('?') : []; // a.php?b=c&d=?e => ['a.php', 'b=c&d=', 'e']
|
|
114
|
+
query.shift(); // => ['b=c&d=', 'e']
|
|
115
|
+
if (query.length > 0) {
|
|
116
|
+
query = query.join('?').split('&'); // => 'b=c&d=?e' => ['b=c', 'd=?e']
|
|
117
|
+
for (const q of query) {
|
|
118
|
+
const kv = q.split('=');
|
|
119
|
+
try {
|
|
120
|
+
getData[kv[0]] = decodeURIComponent(kv[1]);
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
// "URIError: URI malformed" will be thrown when `kv[1]` contains "%", so just use raw data
|
|
124
|
+
// @issue #470
|
|
125
|
+
// @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Malformed_URI
|
|
126
|
+
getData[kv[0]] = kv[1];
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return getData;
|
|
131
|
+
};
|
|
132
|
+
exports.genGetDataByUrl = genGetDataByUrl;
|
|
133
|
+
const genFormattedBody = (body) => {
|
|
134
|
+
if (!body) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
let result;
|
|
138
|
+
if (typeof body === 'string') {
|
|
139
|
+
try {
|
|
140
|
+
// '{a:1}' =>
|
|
141
|
+
result = JSON.parse(body);
|
|
142
|
+
}
|
|
143
|
+
catch (e) {
|
|
144
|
+
// 'a=1&b=2' => try to parse as query
|
|
145
|
+
const arr = body.split('&');
|
|
146
|
+
result = {};
|
|
147
|
+
// eslint-disable-next-line
|
|
148
|
+
for (let q of arr) {
|
|
149
|
+
const kv = q.split('=');
|
|
150
|
+
result[kv[0]] = kv[1] === undefined ? 'undefined' : kv[1];
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
else if (isIterable(body)) {
|
|
155
|
+
// FormData or URLSearchParams or Array
|
|
156
|
+
result = {};
|
|
157
|
+
for (const [key, value] of body) {
|
|
158
|
+
result[key] = typeof value === 'string' ? value : '[object Object]';
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
else if (body instanceof Blob ||
|
|
162
|
+
body instanceof ReadableStream ||
|
|
163
|
+
body instanceof ArrayBuffer) {
|
|
164
|
+
result = 'byte data';
|
|
165
|
+
}
|
|
166
|
+
else if (isPureObject(body)) {
|
|
167
|
+
// overriding ArrayBufferView which is not convertable to string
|
|
168
|
+
result = body;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
result = `can't parse body ${typeof body}`;
|
|
172
|
+
}
|
|
173
|
+
return result;
|
|
174
|
+
};
|
|
175
|
+
exports.genFormattedBody = genFormattedBody;
|
|
176
|
+
function isPureObject(input) {
|
|
177
|
+
return null !== input && typeof input === 'object';
|
|
178
|
+
}
|
|
179
|
+
exports.isPureObject = isPureObject;
|
|
180
|
+
function isIterable(value) {
|
|
181
|
+
if (value === null || value === undefined) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
return typeof Symbol !== 'undefined' && typeof value[Symbol.iterator] === 'function';
|
|
185
|
+
}
|
|
186
|
+
exports.isIterable = isIterable;
|
|
187
|
+
function formatByteSize(bytes) {
|
|
188
|
+
if (bytes <= 0) {
|
|
189
|
+
// shouldn't happen?
|
|
190
|
+
return '';
|
|
191
|
+
}
|
|
192
|
+
if (bytes >= 1000 * 1000) {
|
|
193
|
+
return (bytes / 1000 / 1000).toFixed(1) + ' MB';
|
|
194
|
+
}
|
|
195
|
+
if (bytes >= 1000) {
|
|
196
|
+
return (bytes / 1000).toFixed(1) + ' KB';
|
|
197
|
+
}
|
|
198
|
+
return `${bytes}B`;
|
|
199
|
+
}
|
|
200
|
+
exports.formatByteSize = formatByteSize;
|
|
201
|
+
const getURL = (urlString) => {
|
|
202
|
+
if (urlString.startsWith('//')) {
|
|
203
|
+
const baseUrl = new URL(window.location.href);
|
|
204
|
+
urlString = `${baseUrl.protocol}${urlString}`;
|
|
205
|
+
}
|
|
206
|
+
if (urlString.startsWith('http')) {
|
|
207
|
+
return new URL(urlString);
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
return new URL(urlString, window.location.href);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
exports.getURL = getURL;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* I took inspiration in few stack exchange posts
|
|
3
|
+
* and Tencent vConsole library (MIT)
|
|
4
|
+
* by wrapping the XMLHttpRequest object in a Proxy
|
|
5
|
+
* we can intercept the network requests
|
|
6
|
+
* in not-so-hacky way
|
|
7
|
+
* */
|
|
8
|
+
import NetworkMessage from './networkMessage.js';
|
|
9
|
+
import { RequestResponseData } from './types.js';
|
|
10
|
+
import { NetworkRequest } from '../../common/messages.gen.js';
|
|
11
|
+
export declare class XHRProxyHandler<T extends XMLHttpRequest> implements ProxyHandler<T> {
|
|
12
|
+
private readonly ignoredHeaders;
|
|
13
|
+
private readonly setSessionTokenHeader;
|
|
14
|
+
private readonly sanitize;
|
|
15
|
+
private readonly sendMessage;
|
|
16
|
+
private readonly isServiceUrl;
|
|
17
|
+
XMLReq: XMLHttpRequest;
|
|
18
|
+
item: NetworkMessage;
|
|
19
|
+
constructor(XMLReq: XMLHttpRequest, ignoredHeaders: boolean | string[], setSessionTokenHeader: (cb: (name: string, value: string) => void) => void, sanitize: (data: RequestResponseData) => RequestResponseData, sendMessage: (message: NetworkRequest) => void, isServiceUrl: (url: string) => boolean);
|
|
20
|
+
get(target: T, key: string): any;
|
|
21
|
+
set(target: T, key: string, value: (args: any[]) => any): boolean;
|
|
22
|
+
onReadyStateChange(): void;
|
|
23
|
+
onAbort(): void;
|
|
24
|
+
onTimeout(): void;
|
|
25
|
+
protected getOpen(target: T): (...args: any[]) => any;
|
|
26
|
+
protected getSend(target: T): (...args: any[]) => any;
|
|
27
|
+
protected getSetRequestHeader(target: T): (...args: any[]) => any;
|
|
28
|
+
protected setOnReadyStateChange(target: T, key: string, orscFunction: (args: any[]) => any): boolean;
|
|
29
|
+
protected setOnAbort(target: T, key: string, oaFunction: (args: any[]) => any): boolean;
|
|
30
|
+
protected setOnTimeout(target: T, key: string, otFunction: (args: any[]) => any): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Update item's properties according to readyState.
|
|
33
|
+
*/
|
|
34
|
+
protected updateItemByReadyState(): void;
|
|
35
|
+
}
|
|
36
|
+
export default class XHRProxy {
|
|
37
|
+
static origXMLHttpRequest: {
|
|
38
|
+
new (): XMLHttpRequest;
|
|
39
|
+
prototype: XMLHttpRequest;
|
|
40
|
+
readonly DONE: number;
|
|
41
|
+
readonly HEADERS_RECEIVED: number;
|
|
42
|
+
readonly LOADING: number;
|
|
43
|
+
readonly OPENED: number;
|
|
44
|
+
readonly UNSENT: number;
|
|
45
|
+
};
|
|
46
|
+
static create(ignoredHeaders: boolean | string[], setSessionTokenHeader: (cb: (name: string, value: string) => void) => void, sanitize: (data: RequestResponseData) => RequestResponseData, sendMessage: (data: NetworkRequest) => void, isServiceUrl: (url: string) => boolean): any;
|
|
47
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* I took inspiration in few stack exchange posts
|
|
4
|
+
* and Tencent vConsole library (MIT)
|
|
5
|
+
* by wrapping the XMLHttpRequest object in a Proxy
|
|
6
|
+
* we can intercept the network requests
|
|
7
|
+
* in not-so-hacky way
|
|
8
|
+
* */
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.XHRProxyHandler = void 0;
|
|
11
|
+
const networkMessage_js_1 = require("./networkMessage.js");
|
|
12
|
+
const utils_js_1 = require("./utils.js");
|
|
13
|
+
class XHRProxyHandler {
|
|
14
|
+
constructor(XMLReq, ignoredHeaders, setSessionTokenHeader, sanitize, sendMessage, isServiceUrl) {
|
|
15
|
+
this.ignoredHeaders = ignoredHeaders;
|
|
16
|
+
this.setSessionTokenHeader = setSessionTokenHeader;
|
|
17
|
+
this.sanitize = sanitize;
|
|
18
|
+
this.sendMessage = sendMessage;
|
|
19
|
+
this.isServiceUrl = isServiceUrl;
|
|
20
|
+
this.XMLReq = XMLReq;
|
|
21
|
+
this.XMLReq.onreadystatechange = () => {
|
|
22
|
+
this.onReadyStateChange();
|
|
23
|
+
};
|
|
24
|
+
this.XMLReq.onabort = () => {
|
|
25
|
+
this.onAbort();
|
|
26
|
+
};
|
|
27
|
+
this.XMLReq.ontimeout = () => {
|
|
28
|
+
this.onTimeout();
|
|
29
|
+
};
|
|
30
|
+
this.item = new networkMessage_js_1.default(ignoredHeaders, setSessionTokenHeader, sanitize);
|
|
31
|
+
this.item.requestType = 'xhr';
|
|
32
|
+
}
|
|
33
|
+
get(target, key) {
|
|
34
|
+
switch (key) {
|
|
35
|
+
case 'open':
|
|
36
|
+
return this.getOpen(target);
|
|
37
|
+
case 'send':
|
|
38
|
+
return this.getSend(target);
|
|
39
|
+
case 'setRequestHeader':
|
|
40
|
+
return this.getSetRequestHeader(target);
|
|
41
|
+
default:
|
|
42
|
+
// eslint-disable-next-line no-case-declarations
|
|
43
|
+
const value = Reflect.get(target, key);
|
|
44
|
+
if (typeof value === 'function') {
|
|
45
|
+
return value.bind(target);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
set(target, key, value) {
|
|
53
|
+
switch (key) {
|
|
54
|
+
case 'onreadystatechange':
|
|
55
|
+
return this.setOnReadyStateChange(target, key, value);
|
|
56
|
+
case 'onabort':
|
|
57
|
+
return this.setOnAbort(target, key, value);
|
|
58
|
+
case 'ontimeout':
|
|
59
|
+
return this.setOnTimeout(target, key, value);
|
|
60
|
+
default:
|
|
61
|
+
// not tracked methods
|
|
62
|
+
}
|
|
63
|
+
return Reflect.set(target, key, value);
|
|
64
|
+
}
|
|
65
|
+
onReadyStateChange() {
|
|
66
|
+
if (this.item.url && this.isServiceUrl(this.item.url))
|
|
67
|
+
return;
|
|
68
|
+
this.item.readyState = this.XMLReq.readyState;
|
|
69
|
+
this.item.responseType = this.XMLReq.responseType;
|
|
70
|
+
this.item.endTime = performance.now();
|
|
71
|
+
this.item.duration = this.item.endTime - this.item.startTime;
|
|
72
|
+
this.updateItemByReadyState();
|
|
73
|
+
setTimeout(() => {
|
|
74
|
+
this.item.response = (0, utils_js_1.getStringResponseByType)(this.item.responseType, this.item.response);
|
|
75
|
+
}, 0);
|
|
76
|
+
if (this.XMLReq.readyState === networkMessage_js_1.RequestState.DONE) {
|
|
77
|
+
this.sendMessage(this.item.getMessage());
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
onAbort() {
|
|
81
|
+
this.item.cancelState = 1;
|
|
82
|
+
this.item.statusText = 'Abort';
|
|
83
|
+
this.sendMessage(this.item.getMessage());
|
|
84
|
+
}
|
|
85
|
+
onTimeout() {
|
|
86
|
+
this.item.cancelState = 3;
|
|
87
|
+
this.item.statusText = 'Timeout';
|
|
88
|
+
this.sendMessage(this.item.getMessage());
|
|
89
|
+
}
|
|
90
|
+
getOpen(target) {
|
|
91
|
+
const targetFunction = Reflect.get(target, 'open');
|
|
92
|
+
return (...args) => {
|
|
93
|
+
const method = args[0];
|
|
94
|
+
const url = args[1];
|
|
95
|
+
this.item.method = method ? method.toUpperCase() : 'GET';
|
|
96
|
+
this.item.url = url || '';
|
|
97
|
+
this.item.name = this.item.url.replace(new RegExp('/*$'), '').split('/').pop() || '';
|
|
98
|
+
this.item.getData = (0, utils_js_1.genGetDataByUrl)(this.item.url, {});
|
|
99
|
+
return targetFunction.apply(target, args);
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
getSend(target) {
|
|
103
|
+
const targetFunction = Reflect.get(target, 'send');
|
|
104
|
+
return (...args) => {
|
|
105
|
+
const data = args[0];
|
|
106
|
+
this.item.requestData = (0, utils_js_1.genStringBody)(data);
|
|
107
|
+
return targetFunction.apply(target, args);
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
getSetRequestHeader(target) {
|
|
111
|
+
const targetFunction = Reflect.get(target, 'setRequestHeader');
|
|
112
|
+
return (...args) => {
|
|
113
|
+
if (!this.item.requestHeader) {
|
|
114
|
+
this.item.requestHeader = {};
|
|
115
|
+
}
|
|
116
|
+
// @ts-ignore
|
|
117
|
+
this.item.requestHeader[args[0]] = args[1];
|
|
118
|
+
return targetFunction.apply(target, args);
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
setOnReadyStateChange(target, key, orscFunction) {
|
|
122
|
+
return Reflect.set(target, key, (...args) => {
|
|
123
|
+
this.onReadyStateChange();
|
|
124
|
+
orscFunction.apply(target, args);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
setOnAbort(target, key, oaFunction) {
|
|
128
|
+
return Reflect.set(target, key, (...args) => {
|
|
129
|
+
this.onAbort();
|
|
130
|
+
oaFunction.apply(target, args);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
setOnTimeout(target, key, otFunction) {
|
|
134
|
+
return Reflect.set(target, key, (...args) => {
|
|
135
|
+
this.onTimeout();
|
|
136
|
+
otFunction.apply(target, args);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Update item's properties according to readyState.
|
|
141
|
+
*/
|
|
142
|
+
updateItemByReadyState() {
|
|
143
|
+
switch (this.XMLReq.readyState) {
|
|
144
|
+
case networkMessage_js_1.RequestState.UNSENT:
|
|
145
|
+
case networkMessage_js_1.RequestState.OPENED:
|
|
146
|
+
this.item.status = networkMessage_js_1.RequestState.UNSENT;
|
|
147
|
+
this.item.statusText = 'Pending';
|
|
148
|
+
if (!this.item.startTime) {
|
|
149
|
+
this.item.startTime = performance.now();
|
|
150
|
+
}
|
|
151
|
+
break;
|
|
152
|
+
case networkMessage_js_1.RequestState.HEADERS_RECEIVED:
|
|
153
|
+
this.item.status = this.XMLReq.status;
|
|
154
|
+
this.item.statusText = 'Loading';
|
|
155
|
+
this.item.header = {};
|
|
156
|
+
// eslint-disable-next-line no-case-declarations
|
|
157
|
+
const header = this.XMLReq.getAllResponseHeaders() || '', headerArr = header.split('\n');
|
|
158
|
+
// extract plain text to key-value format
|
|
159
|
+
for (let i = 0; i < headerArr.length; i++) {
|
|
160
|
+
const line = headerArr[i];
|
|
161
|
+
if (!line) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
const arr = line.split(': ');
|
|
165
|
+
const key = arr[0];
|
|
166
|
+
this.item.header[key] = arr.slice(1).join(': ');
|
|
167
|
+
}
|
|
168
|
+
break;
|
|
169
|
+
case networkMessage_js_1.RequestState.LOADING:
|
|
170
|
+
this.item.status = this.XMLReq.status;
|
|
171
|
+
this.item.statusText = 'Loading';
|
|
172
|
+
if (!!this.XMLReq.response && this.XMLReq.response.length) {
|
|
173
|
+
this.item.responseSize = this.XMLReq.response.length;
|
|
174
|
+
this.item.responseSizeText = (0, utils_js_1.formatByteSize)(this.item.responseSize);
|
|
175
|
+
}
|
|
176
|
+
break;
|
|
177
|
+
case networkMessage_js_1.RequestState.DONE:
|
|
178
|
+
// `XMLReq.abort()` will change `status` from 200 to 0, so use previous value in this case
|
|
179
|
+
this.item.status = this.XMLReq.status || this.item.status || 0;
|
|
180
|
+
// show status code when request completed
|
|
181
|
+
this.item.statusText = String(this.item.status);
|
|
182
|
+
this.item.endTime = performance.now();
|
|
183
|
+
this.item.duration = this.item.endTime - (this.item.startTime || this.item.endTime);
|
|
184
|
+
this.item.response = this.XMLReq.response;
|
|
185
|
+
if (!!this.XMLReq.response && this.XMLReq.response.length) {
|
|
186
|
+
this.item.responseSize = this.XMLReq.response.length;
|
|
187
|
+
this.item.responseSizeText = (0, utils_js_1.formatByteSize)(this.item.responseSize);
|
|
188
|
+
}
|
|
189
|
+
break;
|
|
190
|
+
default:
|
|
191
|
+
this.item.status = this.XMLReq.status;
|
|
192
|
+
this.item.statusText = 'Unknown';
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
exports.XHRProxyHandler = XHRProxyHandler;
|
|
198
|
+
class XHRProxy {
|
|
199
|
+
static create(ignoredHeaders, setSessionTokenHeader, sanitize, sendMessage, isServiceUrl) {
|
|
200
|
+
return new Proxy(XMLHttpRequest, {
|
|
201
|
+
construct(original) {
|
|
202
|
+
const XMLReq = new original();
|
|
203
|
+
return new Proxy(XMLReq, new XHRProxyHandler(XMLReq, ignoredHeaders, setSessionTokenHeader, sanitize, sendMessage, isServiceUrl));
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
exports.default = XHRProxy;
|
|
209
|
+
XHRProxy.origXMLHttpRequest = XMLHttpRequest;
|
package/cjs/modules/console.js
CHANGED
|
@@ -92,26 +92,34 @@ function default_1(app, opts) {
|
|
|
92
92
|
return;
|
|
93
93
|
}
|
|
94
94
|
const sendConsoleLog = app.safe((level, args) => app.send((0, messages_gen_js_1.ConsoleLog)(level, printf(args))));
|
|
95
|
-
let n;
|
|
95
|
+
let n = 0;
|
|
96
96
|
const reset = () => {
|
|
97
97
|
n = 0;
|
|
98
98
|
};
|
|
99
99
|
app.attachStartCallback(reset);
|
|
100
100
|
app.ticker.attach(reset, 33, false);
|
|
101
|
-
const patchConsole = (console) =>
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
101
|
+
const patchConsole = (console) => {
|
|
102
|
+
const handler = {
|
|
103
|
+
apply: function (target, thisArg, argumentsList) {
|
|
104
|
+
target.apply(console, argumentsList);
|
|
105
|
+
n = n + 1;
|
|
106
|
+
if (n > options.consoleThrottling) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
sendConsoleLog(target.name, argumentsList);
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
options.consoleMethods.forEach((method) => {
|
|
115
|
+
if (consoleMethods.indexOf(method) === -1) {
|
|
116
|
+
app.debug.error(`OpenReplay: unsupported console method "${method}"`);
|
|
110
117
|
return;
|
|
111
118
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
119
|
+
const fn = console[method];
|
|
120
|
+
console[method] = new Proxy(fn, handler);
|
|
121
|
+
});
|
|
122
|
+
};
|
|
115
123
|
const patchContext = app.safe((context) => patchConsole(context.console));
|
|
116
124
|
patchContext(window);
|
|
117
125
|
app.observer.attachContextCallback(patchContext);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import App from '../app/index.js';
|
|
2
|
+
export interface IFeatureFlag {
|
|
3
|
+
key: string;
|
|
4
|
+
is_persist: boolean;
|
|
5
|
+
value: string | boolean;
|
|
6
|
+
payload: string;
|
|
7
|
+
}
|
|
8
|
+
export interface FetchPersistFlagsData {
|
|
9
|
+
key: string;
|
|
10
|
+
value: string | boolean;
|
|
11
|
+
}
|
|
12
|
+
export default class FeatureFlags {
|
|
13
|
+
private readonly app;
|
|
14
|
+
flags: IFeatureFlag[];
|
|
15
|
+
storageKey: string;
|
|
16
|
+
onFlagsCb: (flags: IFeatureFlag[]) => void;
|
|
17
|
+
constructor(app: App);
|
|
18
|
+
getFeatureFlag(flagName: string): IFeatureFlag | undefined;
|
|
19
|
+
isFlagEnabled(flagName: string): boolean;
|
|
20
|
+
onFlagsLoad(cb: (flags: IFeatureFlag[]) => void): void;
|
|
21
|
+
reloadFlags(): Promise<void>;
|
|
22
|
+
handleFlags(flags: IFeatureFlag[]): void;
|
|
23
|
+
clearPersistFlags(): void;
|
|
24
|
+
diffPersist(flags: IFeatureFlag[]): IFeatureFlag[];
|
|
25
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
class FeatureFlags {
|
|
13
|
+
constructor(app) {
|
|
14
|
+
this.app = app;
|
|
15
|
+
this.storageKey = '__openreplay_flags';
|
|
16
|
+
const persistFlags = this.app.sessionStorage.getItem(this.storageKey);
|
|
17
|
+
if (persistFlags) {
|
|
18
|
+
const persistFlagsStrArr = persistFlags.split(';').filter(Boolean);
|
|
19
|
+
this.flags = persistFlagsStrArr.map((flag) => JSON.parse(flag));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
getFeatureFlag(flagName) {
|
|
23
|
+
return this.flags.find((flag) => flag.key === flagName);
|
|
24
|
+
}
|
|
25
|
+
isFlagEnabled(flagName) {
|
|
26
|
+
return this.flags.findIndex((flag) => flag.key === flagName) !== -1;
|
|
27
|
+
}
|
|
28
|
+
onFlagsLoad(cb) {
|
|
29
|
+
this.onFlagsCb = cb;
|
|
30
|
+
}
|
|
31
|
+
reloadFlags() {
|
|
32
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
33
|
+
const persistFlagsStr = this.app.sessionStorage.getItem(this.storageKey);
|
|
34
|
+
const persistFlags = {};
|
|
35
|
+
if (persistFlagsStr) {
|
|
36
|
+
const persistArray = persistFlagsStr.split(';').filter(Boolean);
|
|
37
|
+
persistArray.forEach((flag) => {
|
|
38
|
+
const flagObj = JSON.parse(flag);
|
|
39
|
+
persistFlags[flagObj.key] = { key: flagObj.key, value: flagObj.value };
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
const sessionInfo = this.app.session.getInfo();
|
|
43
|
+
const userInfo = this.app.session.userInfo;
|
|
44
|
+
const requestObject = {
|
|
45
|
+
projectID: sessionInfo.projectID,
|
|
46
|
+
userID: sessionInfo.userID,
|
|
47
|
+
metadata: sessionInfo.metadata,
|
|
48
|
+
referrer: document.referrer,
|
|
49
|
+
// todo: get from backend
|
|
50
|
+
os: userInfo.userOS,
|
|
51
|
+
device: userInfo.userDevice,
|
|
52
|
+
country: userInfo.userCountry,
|
|
53
|
+
state: userInfo.userState,
|
|
54
|
+
city: userInfo.userCity,
|
|
55
|
+
browser: userInfo.userBrowser,
|
|
56
|
+
persistFlags: persistFlags,
|
|
57
|
+
};
|
|
58
|
+
const resp = yield fetch(this.app.options.ingestPoint + '/v1/web/feature-flags', {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
headers: {
|
|
61
|
+
'Content-Type': 'application/json',
|
|
62
|
+
Authorization: `Bearer ${this.app.session.getSessionToken()}`,
|
|
63
|
+
},
|
|
64
|
+
body: JSON.stringify(requestObject),
|
|
65
|
+
});
|
|
66
|
+
if (resp.status === 200) {
|
|
67
|
+
const data = yield resp.json();
|
|
68
|
+
return this.handleFlags(data.flags);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
handleFlags(flags) {
|
|
73
|
+
var _a;
|
|
74
|
+
const persistFlags = [];
|
|
75
|
+
flags.forEach((flag) => {
|
|
76
|
+
if (flag.is_persist)
|
|
77
|
+
persistFlags.push(flag);
|
|
78
|
+
});
|
|
79
|
+
let str = '';
|
|
80
|
+
const uniquePersistFlags = this.diffPersist(persistFlags);
|
|
81
|
+
uniquePersistFlags.forEach((flag) => {
|
|
82
|
+
str += `${JSON.stringify(flag)};`;
|
|
83
|
+
});
|
|
84
|
+
this.app.sessionStorage.setItem(this.storageKey, str);
|
|
85
|
+
this.flags = flags;
|
|
86
|
+
return (_a = this.onFlagsCb) === null || _a === void 0 ? void 0 : _a.call(this, flags);
|
|
87
|
+
}
|
|
88
|
+
clearPersistFlags() {
|
|
89
|
+
this.app.sessionStorage.removeItem(this.storageKey);
|
|
90
|
+
}
|
|
91
|
+
diffPersist(flags) {
|
|
92
|
+
const persistFlags = this.app.sessionStorage.getItem(this.storageKey);
|
|
93
|
+
if (!persistFlags)
|
|
94
|
+
return flags;
|
|
95
|
+
const persistFlagsStrArr = persistFlags.split(';').filter(Boolean);
|
|
96
|
+
const persistFlagsArr = persistFlagsStrArr.map((flag) => JSON.parse(flag));
|
|
97
|
+
return flags.filter((flag) => persistFlagsArr.findIndex((pf) => pf.key === flag.key) === -1);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
exports.default = FeatureFlags;
|
package/cjs/modules/network.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import type App from '../app/index.js';
|
|
2
2
|
import type { AxiosInstance } from './axiosSpy.js';
|
|
3
|
-
type XHRRequestBody = Parameters<XMLHttpRequest['send']>[0];
|
|
4
|
-
type FetchRequestBody = RequestInit['body'];
|
|
5
3
|
interface RequestData {
|
|
6
|
-
body:
|
|
4
|
+
body: string | null;
|
|
7
5
|
headers: Record<string, string>;
|
|
8
6
|
}
|
|
9
7
|
interface ResponseData {
|
|
@@ -17,7 +15,7 @@ export interface RequestResponseData {
|
|
|
17
15
|
request: RequestData;
|
|
18
16
|
response: ResponseData;
|
|
19
17
|
}
|
|
20
|
-
type Sanitizer = (data: RequestResponseData) => RequestResponseData
|
|
18
|
+
type Sanitizer = (data: RequestResponseData) => RequestResponseData;
|
|
21
19
|
export interface Options {
|
|
22
20
|
sessionTokenHeader: string | boolean;
|
|
23
21
|
failuresOnly: boolean;
|
|
@@ -26,6 +24,7 @@ export interface Options {
|
|
|
26
24
|
captureInIframes: boolean;
|
|
27
25
|
sanitizer?: Sanitizer;
|
|
28
26
|
axiosInstances?: Array<AxiosInstance>;
|
|
27
|
+
useProxy?: boolean;
|
|
29
28
|
}
|
|
30
29
|
export default function (app: App, opts?: Partial<Options>): void;
|
|
31
30
|
export {};
|