@openreplay/tracker 6.0.1-beta.2 → 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 CHANGED
@@ -2,12 +2,17 @@
2
2
 
3
3
  - **[breaking]** added gzip compression for large messages
4
4
 
5
+ # 6.0.2
6
+
7
+ - fix network tracking for samedomain iframes created by js code
8
+
5
9
  # 6.0.1
6
10
 
7
11
  - fix webworker writer re-init request
8
12
  - remove useless logs
9
13
  - tune mouse thrashing detection
10
14
  - fix iframe handling
15
+ - improve node counting for dom drop
11
16
 
12
17
  # 6.0.0
13
18
 
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.1-beta.2'; // TODO: version compatability check inside each plugin.
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,6 +2,7 @@ type NodeCallback = (node: Node, isStart: boolean) => void;
2
2
  export default class Nodes {
3
3
  private readonly node_id;
4
4
  private nodes;
5
+ private totalNodeAmount;
5
6
  private readonly nodeCallbacks;
6
7
  private readonly elementListeners;
7
8
  constructor(node_id: string);
package/cjs/app/nodes.js CHANGED
@@ -4,6 +4,7 @@ class Nodes {
4
4
  constructor(node_id) {
5
5
  this.node_id = node_id;
6
6
  this.nodes = [];
7
+ this.totalNodeAmount = 0;
7
8
  this.nodeCallbacks = [];
8
9
  this.elementListeners = new Map();
9
10
  }
@@ -28,6 +29,7 @@ class Nodes {
28
29
  let id = node[this.node_id];
29
30
  const isNew = id === undefined;
30
31
  if (isNew) {
32
+ this.totalNodeAmount++;
31
33
  id = this.nodes.length;
32
34
  this.nodes[id] = node;
33
35
  node[this.node_id] = id;
@@ -44,6 +46,7 @@ class Nodes {
44
46
  this.elementListeners.delete(id);
45
47
  listeners.forEach((listener) => node.removeEventListener(listener[0], listener[1], listener[2]));
46
48
  }
49
+ this.totalNodeAmount--;
47
50
  }
48
51
  return id;
49
52
  }
@@ -69,7 +72,7 @@ class Nodes {
69
72
  return this.nodes[id];
70
73
  }
71
74
  getNodeCount() {
72
- return this.nodes.filter(Boolean).length;
75
+ return this.totalNodeAmount;
73
76
  }
74
77
  clear() {
75
78
  for (let id = 0; id < this.nodes.length; id++) {
@@ -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.1-beta.2',
145
+ trackerVersion: '6.0.2',
146
146
  projectKey: options.projectKey,
147
147
  doNotTrack,
148
148
  // TODO: add precise reason (an exact API missing)
@@ -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>, customEnv?: Record<string, any>): void;
27
+ export default function (app: App, opts?: Partial<Options>): void;
28
28
  export {};
@@ -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 = {}, customEnv) {
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
- /* ====== Fetch ====== */
105
- const origFetch = customEnv
106
- ? customEnv.fetch.bind(customEnv)
107
- : window.fetch.bind(window);
108
- const trackFetch = (input, init = {}) => {
109
- if (!(typeof input === 'string' || input instanceof URL) || app.isServiceURL(String(input))) {
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
- else {
123
- init.headers[name] = value;
124
- }
125
- });
126
- const startTime = performance.now();
127
- return origFetch(input, init).then((response) => {
128
- const duration = performance.now() - startTime;
129
- if (options.failuresOnly && response.status < 400) {
130
- return response;
131
- }
132
- const r = response.clone();
133
- r.text()
134
- .then((text) => {
135
- const reqHs = {};
136
- const resHs = {};
137
- if (ignoreHeaders !== true) {
138
- // request headers
139
- const writeReqHeader = ([n, v]) => {
140
- if (!isHIgnored(n)) {
141
- reqHs[n] = v;
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
- if (init.headers instanceof Headers) {
145
- init.headers.forEach((v, n) => writeReqHeader([n, v]));
146
- }
147
- else if (Array.isArray(init.headers)) {
148
- init.headers.forEach(writeReqHeader);
149
- }
150
- else if (typeof init.headers === 'object') {
151
- Object.entries(init.headers).forEach(writeReqHeader);
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
- // response headers
154
- r.headers.forEach((v, n) => {
155
- if (!isHIgnored(n))
156
- resHs[n] = v;
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
- const method = strMethod(init.method);
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(input),
168
+ url: String(url),
162
169
  method,
163
- status: r.status,
170
+ status: xhr.status,
164
171
  request: {
165
172
  headers: reqHs,
166
- body: init.body,
173
+ body: reqBody,
167
174
  },
168
175
  response: {
169
176
  headers: resHs,
170
- body: text,
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)('fetch', method, String(reqResInfo.url), stringify(reqResInfo.request), stringify(reqResInfo.response), r.status, startTime + (0, utils_js_1.getTimeOrigin)(), duration));
177
- })
178
- .catch((e) => app.debug.error('Could not process Fetch response:', e));
179
- return response;
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
- else {
260
- XMLHttpRequest.prototype.send = trackXHRSend;
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.headers[name] = value;
193
+ rdo.body = body;
194
+ // @ts-ignore ??? this -> XMLHttpRequest
195
+ return nativeSend.apply(this, arguments);
268
196
  }
269
- // @ts-ignore ??? this -> XMLHttpRequest
270
- return nativeSetRequestHeader.apply(this, arguments);
271
- }
272
- if (customEnv) {
273
- if ('XMLHttpRequest' in customEnv) {
274
- customEnv.XMLHttpRequest.prototype.setRequestHeader = trackSetReqHeader.bind(customEnv);
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
- else {
278
- XMLHttpRequest.prototype.setRequestHeader = trackSetReqHeader;
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
@@ -79,6 +79,9 @@ function isIframeCrossdomain(e) {
79
79
  }
80
80
  }
81
81
  exports.isIframeCrossdomain = isIframeCrossdomain;
82
+ /**
83
+ * checks if iframe is accessible
84
+ **/
82
85
  function canAccessIframe(iframe) {
83
86
  try {
84
87
  return Boolean(iframe.contentDocument);
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.1-beta.2'; // TODO: version compatability check inside each plugin.
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;
@@ -2,6 +2,7 @@ type NodeCallback = (node: Node, isStart: boolean) => void;
2
2
  export default class Nodes {
3
3
  private readonly node_id;
4
4
  private nodes;
5
+ private totalNodeAmount;
5
6
  private readonly nodeCallbacks;
6
7
  private readonly elementListeners;
7
8
  constructor(node_id: string);
package/lib/app/nodes.js CHANGED
@@ -2,6 +2,7 @@ export default class Nodes {
2
2
  constructor(node_id) {
3
3
  this.node_id = node_id;
4
4
  this.nodes = [];
5
+ this.totalNodeAmount = 0;
5
6
  this.nodeCallbacks = [];
6
7
  this.elementListeners = new Map();
7
8
  }
@@ -26,6 +27,7 @@ export default class Nodes {
26
27
  let id = node[this.node_id];
27
28
  const isNew = id === undefined;
28
29
  if (isNew) {
30
+ this.totalNodeAmount++;
29
31
  id = this.nodes.length;
30
32
  this.nodes[id] = node;
31
33
  node[this.node_id] = id;
@@ -42,6 +44,7 @@ export default class Nodes {
42
44
  this.elementListeners.delete(id);
43
45
  listeners.forEach((listener) => node.removeEventListener(listener[0], listener[1], listener[2]));
44
46
  }
47
+ this.totalNodeAmount--;
45
48
  }
46
49
  return id;
47
50
  }
@@ -67,7 +70,7 @@ export default class Nodes {
67
70
  return this.nodes[id];
68
71
  }
69
72
  getNodeCount() {
70
- return this.nodes.filter(Boolean).length;
73
+ return this.totalNodeAmount;
71
74
  }
72
75
  clear() {
73
76
  for (let id = 0; id < this.nodes.length; id++) {
@@ -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.1-beta.2',
140
+ trackerVersion: '6.0.2',
141
141
  projectKey: options.projectKey,
142
142
  doNotTrack,
143
143
  // TODO: add precise reason (an exact API missing)
@@ -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>, customEnv?: Record<string, any>): void;
27
+ export default function (app: App, opts?: Partial<Options>): void;
28
28
  export {};
@@ -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 = {}, customEnv) {
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
- /* ====== Fetch ====== */
103
- const origFetch = customEnv
104
- ? customEnv.fetch.bind(customEnv)
105
- : window.fetch.bind(window);
106
- const trackFetch = (input, init = {}) => {
107
- if (!(typeof input === 'string' || input instanceof URL) || app.isServiceURL(String(input))) {
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
- else {
121
- init.headers[name] = value;
122
- }
123
- });
124
- const startTime = performance.now();
125
- return origFetch(input, init).then((response) => {
126
- const duration = performance.now() - startTime;
127
- if (options.failuresOnly && response.status < 400) {
128
- return response;
129
- }
130
- const r = response.clone();
131
- r.text()
132
- .then((text) => {
133
- const reqHs = {};
134
- const resHs = {};
135
- if (ignoreHeaders !== true) {
136
- // request headers
137
- const writeReqHeader = ([n, v]) => {
138
- if (!isHIgnored(n)) {
139
- reqHs[n] = v;
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
- if (init.headers instanceof Headers) {
143
- init.headers.forEach((v, n) => writeReqHeader([n, v]));
144
- }
145
- else if (Array.isArray(init.headers)) {
146
- init.headers.forEach(writeReqHeader);
147
- }
148
- else if (typeof init.headers === 'object') {
149
- Object.entries(init.headers).forEach(writeReqHeader);
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
- // response headers
152
- r.headers.forEach((v, n) => {
153
- if (!isHIgnored(n))
154
- resHs[n] = v;
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
- const method = strMethod(init.method);
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(input),
166
+ url: String(url),
160
167
  method,
161
- status: r.status,
168
+ status: xhr.status,
162
169
  request: {
163
170
  headers: reqHs,
164
- body: init.body,
171
+ body: reqBody,
165
172
  },
166
173
  response: {
167
174
  headers: resHs,
168
- body: text,
175
+ body: xhr.response,
169
176
  },
170
177
  });
171
178
  if (!reqResInfo) {
172
179
  return;
173
180
  }
174
- app.send(NetworkRequest('fetch', method, String(reqResInfo.url), stringify(reqResInfo.request), stringify(reqResInfo.response), r.status, startTime + getTimeOrigin(), duration));
175
- })
176
- .catch((e) => app.debug.error('Could not process Fetch response:', e));
177
- return response;
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
- else {
258
- XMLHttpRequest.prototype.send = trackXHRSend;
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.headers[name] = value;
191
+ rdo.body = body;
192
+ // @ts-ignore ??? this -> XMLHttpRequest
193
+ return nativeSend.apply(this, arguments);
266
194
  }
267
- // @ts-ignore ??? this -> XMLHttpRequest
268
- return nativeSetRequestHeader.apply(this, arguments);
269
- }
270
- if (customEnv) {
271
- if ('XMLHttpRequest' in customEnv) {
272
- customEnv.XMLHttpRequest.prototype.setRequestHeader = trackSetReqHeader.bind(customEnv);
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
- else {
276
- XMLHttpRequest.prototype.setRequestHeader = trackSetReqHeader;
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
@@ -68,6 +68,9 @@ export function isIframeCrossdomain(e) {
68
68
  return true;
69
69
  }
70
70
  }
71
+ /**
72
+ * checks if iframe is accessible
73
+ **/
71
74
  export function canAccessIframe(iframe) {
72
75
  try {
73
76
  return Boolean(iframe.contentDocument);
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": "6.0.1-beta.2",
4
+ "version": "6.0.2",
5
5
  "keywords": [
6
6
  "logging",
7
7
  "replay"