@openreplay/tracker 7.0.2-beta.1 → 7.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
@@ -1,3 +1,7 @@
1
+ # 7.0.2
2
+
3
+ - fixed header sanitization for axios causing empty string in some cases
4
+
1
5
  # 7.0.1
2
6
 
3
7
  - fix time inputs capturing
@@ -33,6 +33,7 @@ interface AxiosResponse<T = any> {
33
33
  response?: AxiosRequestConfig;
34
34
  }
35
35
  export interface AxiosInstance extends Record<string, any> {
36
+ getUri: (config?: AxiosRequestConfig) => string;
36
37
  interceptors: {
37
38
  request: AxiosInterceptorManager<InternalAxiosRequestConfig>;
38
39
  response: AxiosInterceptorManager<AxiosResponse>;
@@ -2,15 +2,16 @@
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
+ const exception_js_1 = require("./exception.js");
5
6
  function default_1(app, instance, opts, sanitize, stringify) {
7
+ app.debug.log('Openreplay: attaching axios spy to instance', instance);
6
8
  function captureResponseData(axiosResponseObj) {
7
- const { headers: reqHs, data: reqData, method, url } = axiosResponseObj.config;
9
+ app.debug.log('Openreplay: capturing axios response data', axiosResponseObj);
10
+ const { headers: reqHs, data: reqData, method, url, baseURL } = axiosResponseObj.config;
8
11
  const { data: rData, headers: rHs, status: globStatus, response } = axiosResponseObj;
9
12
  const { data: resData, headers: resHs, status: resStatus } = response || {};
10
13
  const ihOpt = opts.ignoreHeaders;
11
- const isHIgnoring = Array.isArray(ihOpt)
12
- ? (name) => ihOpt.includes(name)
13
- : () => ihOpt;
14
+ const isHIgnoring = Array.isArray(ihOpt) ? (name) => ihOpt.includes(name) : () => ihOpt;
14
15
  function writeHeader(hsObj, header) {
15
16
  if (!isHIgnoring(header[0])) {
16
17
  hsObj[header[0]] = header[1];
@@ -60,13 +61,16 @@ function default_1(app, instance, opts, sanitize, stringify) {
60
61
  },
61
62
  });
62
63
  if (!reqResInfo) {
64
+ app.debug.log('Openreplay: empty request/response info, skipping');
63
65
  return;
64
66
  }
65
67
  const requestStart = axiosResponseObj.config.__openreplay_timing;
66
68
  const duration = performance.now() - requestStart;
69
+ app.debug.log('Openreplay: final req object', reqResInfo);
67
70
  app.send((0, messages_gen_js_1.NetworkRequest)('xhr', String(method), String(reqResInfo.url), stringify(reqResInfo.request), stringify(reqResInfo.response), reqResInfo.status, requestStart + (0, utils_js_1.getTimeOrigin)(), duration));
68
71
  }
69
72
  function getStartTime(config) {
73
+ app.debug.log('Openreplay: capturing API request', config);
70
74
  config.__openreplay_timing = performance.now();
71
75
  if (opts.sessionTokenHeader) {
72
76
  const header = typeof opts.sessionTokenHeader === 'string'
@@ -86,10 +90,21 @@ function default_1(app, instance, opts, sanitize, stringify) {
86
90
  return response;
87
91
  }
88
92
  function captureNetworkError(error) {
89
- captureResponseData(error);
93
+ app.debug.log('Openreplay: capturing API request error', error);
94
+ if (isAxiosError(error)) {
95
+ captureResponseData(error.response);
96
+ }
97
+ else if (error instanceof Error) {
98
+ app.send((0, exception_js_1.getExceptionMessage)(error, []));
99
+ }
90
100
  return Promise.reject(error);
91
101
  }
92
- const reqInt = instance.interceptors.request.use(getStartTime, null, { synchronous: true });
102
+ function logRequestError(ev) {
103
+ app.debug.log('Openreplay: failed API request, skipping', ev);
104
+ }
105
+ const reqInt = instance.interceptors.request.use(getStartTime, logRequestError, {
106
+ synchronous: true,
107
+ });
93
108
  const resInt = instance.interceptors.response.use(captureNetworkRequest, captureNetworkError, {
94
109
  synchronous: true,
95
110
  });
@@ -100,3 +115,9 @@ function default_1(app, instance, opts, sanitize, stringify) {
100
115
  });
101
116
  }
102
117
  exports.default = default_1;
118
+ function isAxiosError(payload) {
119
+ return isObject(payload) && payload.isAxiosError === true;
120
+ }
121
+ function isObject(thing) {
122
+ return thing !== null && typeof thing === 'object';
123
+ }
@@ -160,14 +160,16 @@ function default_1(app, opts = {}) {
160
160
  xhr.addEventListener('load', app.safe((e) => {
161
161
  const { headers: reqHs, body: reqBody } = getXHRRequestDataObject(xhr);
162
162
  const duration = startTime > 0 ? e.timeStamp - startTime : 0;
163
- const hString = ignoreHeaders ? '' : xhr.getAllResponseHeaders(); // might be null (though only if no response received though)
164
- const resHs = hString
165
- ? hString
166
- .split('\r\n')
167
- .map((h) => h.split(':'))
168
- .filter((entry) => !isHIgnored(entry[0]))
169
- .reduce((hds, [name, value]) => (Object.assign(Object.assign({}, hds), { [name]: value })), {})
170
- : {};
163
+ const hString = xhr.getAllResponseHeaders() || ''; // might be null (though only if no response received though)
164
+ const headersArr = hString.trim().split(/[\r\n]+/);
165
+ const headerMap = {};
166
+ headersArr.forEach(function (line) {
167
+ const parts = line.split(': ');
168
+ const header = parts.shift();
169
+ if (!isHIgnored(header)) {
170
+ headerMap[header] = parts.join(': ');
171
+ }
172
+ });
171
173
  const method = strMethod(initMethod);
172
174
  const reqResInfo = sanitize({
173
175
  url: String(url),
@@ -178,7 +180,7 @@ function default_1(app, opts = {}) {
178
180
  body: reqBody,
179
181
  },
180
182
  response: {
181
- headers: resHs,
183
+ headers: headerMap,
182
184
  body: xhr.response,
183
185
  },
184
186
  });
@@ -33,6 +33,7 @@ interface AxiosResponse<T = any> {
33
33
  response?: AxiosRequestConfig;
34
34
  }
35
35
  export interface AxiosInstance extends Record<string, any> {
36
+ getUri: (config?: AxiosRequestConfig) => string;
36
37
  interceptors: {
37
38
  request: AxiosInterceptorManager<InternalAxiosRequestConfig>;
38
39
  response: AxiosInterceptorManager<AxiosResponse>;
@@ -1,14 +1,15 @@
1
1
  import { NetworkRequest } from '../app/messages.gen.js';
2
2
  import { getTimeOrigin } from '../utils.js';
3
+ import { getExceptionMessage } from './exception.js';
3
4
  export default function (app, instance, opts, sanitize, stringify) {
5
+ app.debug.log('Openreplay: attaching axios spy to instance', instance);
4
6
  function captureResponseData(axiosResponseObj) {
5
- const { headers: reqHs, data: reqData, method, url } = axiosResponseObj.config;
7
+ app.debug.log('Openreplay: capturing axios response data', axiosResponseObj);
8
+ const { headers: reqHs, data: reqData, method, url, baseURL } = axiosResponseObj.config;
6
9
  const { data: rData, headers: rHs, status: globStatus, response } = axiosResponseObj;
7
10
  const { data: resData, headers: resHs, status: resStatus } = response || {};
8
11
  const ihOpt = opts.ignoreHeaders;
9
- const isHIgnoring = Array.isArray(ihOpt)
10
- ? (name) => ihOpt.includes(name)
11
- : () => ihOpt;
12
+ const isHIgnoring = Array.isArray(ihOpt) ? (name) => ihOpt.includes(name) : () => ihOpt;
12
13
  function writeHeader(hsObj, header) {
13
14
  if (!isHIgnoring(header[0])) {
14
15
  hsObj[header[0]] = header[1];
@@ -58,13 +59,16 @@ export default function (app, instance, opts, sanitize, stringify) {
58
59
  },
59
60
  });
60
61
  if (!reqResInfo) {
62
+ app.debug.log('Openreplay: empty request/response info, skipping');
61
63
  return;
62
64
  }
63
65
  const requestStart = axiosResponseObj.config.__openreplay_timing;
64
66
  const duration = performance.now() - requestStart;
67
+ app.debug.log('Openreplay: final req object', reqResInfo);
65
68
  app.send(NetworkRequest('xhr', String(method), String(reqResInfo.url), stringify(reqResInfo.request), stringify(reqResInfo.response), reqResInfo.status, requestStart + getTimeOrigin(), duration));
66
69
  }
67
70
  function getStartTime(config) {
71
+ app.debug.log('Openreplay: capturing API request', config);
68
72
  config.__openreplay_timing = performance.now();
69
73
  if (opts.sessionTokenHeader) {
70
74
  const header = typeof opts.sessionTokenHeader === 'string'
@@ -84,10 +88,21 @@ export default function (app, instance, opts, sanitize, stringify) {
84
88
  return response;
85
89
  }
86
90
  function captureNetworkError(error) {
87
- captureResponseData(error);
91
+ app.debug.log('Openreplay: capturing API request error', error);
92
+ if (isAxiosError(error)) {
93
+ captureResponseData(error.response);
94
+ }
95
+ else if (error instanceof Error) {
96
+ app.send(getExceptionMessage(error, []));
97
+ }
88
98
  return Promise.reject(error);
89
99
  }
90
- const reqInt = instance.interceptors.request.use(getStartTime, null, { synchronous: true });
100
+ function logRequestError(ev) {
101
+ app.debug.log('Openreplay: failed API request, skipping', ev);
102
+ }
103
+ const reqInt = instance.interceptors.request.use(getStartTime, logRequestError, {
104
+ synchronous: true,
105
+ });
91
106
  const resInt = instance.interceptors.response.use(captureNetworkRequest, captureNetworkError, {
92
107
  synchronous: true,
93
108
  });
@@ -97,3 +112,9 @@ export default function (app, instance, opts, sanitize, stringify) {
97
112
  (_d = (_c = instance.interceptors.response).eject) === null || _d === void 0 ? void 0 : _d.call(_c, resInt);
98
113
  });
99
114
  }
115
+ function isAxiosError(payload) {
116
+ return isObject(payload) && payload.isAxiosError === true;
117
+ }
118
+ function isObject(thing) {
119
+ return thing !== null && typeof thing === 'object';
120
+ }
@@ -158,14 +158,16 @@ export default function (app, opts = {}) {
158
158
  xhr.addEventListener('load', app.safe((e) => {
159
159
  const { headers: reqHs, body: reqBody } = getXHRRequestDataObject(xhr);
160
160
  const duration = startTime > 0 ? e.timeStamp - startTime : 0;
161
- const hString = ignoreHeaders ? '' : xhr.getAllResponseHeaders(); // might be null (though only if no response received though)
162
- const resHs = hString
163
- ? hString
164
- .split('\r\n')
165
- .map((h) => h.split(':'))
166
- .filter((entry) => !isHIgnored(entry[0]))
167
- .reduce((hds, [name, value]) => (Object.assign(Object.assign({}, hds), { [name]: value })), {})
168
- : {};
161
+ const hString = xhr.getAllResponseHeaders() || ''; // might be null (though only if no response received though)
162
+ const headersArr = hString.trim().split(/[\r\n]+/);
163
+ const headerMap = {};
164
+ headersArr.forEach(function (line) {
165
+ const parts = line.split(': ');
166
+ const header = parts.shift();
167
+ if (!isHIgnored(header)) {
168
+ headerMap[header] = parts.join(': ');
169
+ }
170
+ });
169
171
  const method = strMethod(initMethod);
170
172
  const reqResInfo = sanitize({
171
173
  url: String(url),
@@ -176,7 +178,7 @@ export default function (app, opts = {}) {
176
178
  body: reqBody,
177
179
  },
178
180
  response: {
179
- headers: resHs,
181
+ headers: headerMap,
180
182
  body: xhr.response,
181
183
  },
182
184
  });
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": "7.0.2-beta.1",
4
+ "version": "7.0.2",
5
5
  "keywords": [
6
6
  "logging",
7
7
  "replay"