@multiplayer-app/session-recorder-browser
Version:
Multiplayer Fullstack Session Recorder for Browser
107 lines • 4.12 kB
JavaScript
import { isDocument, isFormData, isNullish, isObject, isString, } from '../utils/type-utils';
import { formDataToQuery } from '../utils/request-utils';
import { configs } from './configs';
function _tryReadXHRBody({ body, url, }) {
if (isNullish(body)) {
return null;
}
if (isString(body)) {
return body;
}
if (isDocument(body)) {
return body.textContent;
}
if (isFormData(body)) {
return formDataToQuery(body);
}
if (isObject(body)) {
try {
return JSON.stringify(body);
}
catch (_a) {
return '[XHR] Failed to stringify response object';
}
}
return `[XHR] Cannot read body of type ${toString.call(body)}`;
}
function _isWithinPayloadLimit(payload) {
try {
if (typeof Blob !== 'undefined') {
return new Blob([payload]).size <= configs.maxCapturingHttpPayloadSize;
}
}
catch (_a) {
// ignore and fallback to string length
}
return payload.length <= configs.maxCapturingHttpPayloadSize;
}
// Only patch XHR in environments where it exists (avoid SSR/Node)
if (typeof XMLHttpRequest !== 'undefined') {
(function (xhr) {
// Idempotency guard: avoid double-patching
// @ts-ignore
if (xhr.__mp_session_recorder_patched__) {
return;
}
// @ts-ignore
;
xhr.__mp_session_recorder_patched__ = true;
const originalOpen = xhr.open;
xhr.open = function (method, url, async = true, username, password) {
const xhr = this;
const networkRequest = {};
// @ts-ignore
const requestHeaders = {};
const originalSetRequestHeader = xhr.setRequestHeader.bind(xhr);
xhr.setRequestHeader = (header, value) => {
requestHeaders[header] = value;
return originalSetRequestHeader(header, value);
};
if (configs.recordRequestHeaders) {
networkRequest.requestHeaders = requestHeaders;
}
const originalSend = xhr.send.bind(xhr);
xhr.send = (body) => {
if (configs.shouldRecordBody) {
const requestBody = _tryReadXHRBody({ body, url });
if ((requestBody === null || requestBody === void 0 ? void 0 : requestBody.length)
&& _isWithinPayloadLimit(requestBody)) {
networkRequest.requestBody = requestBody;
}
}
return originalSend(body);
};
xhr.addEventListener('readystatechange', () => {
if (xhr.readyState !== xhr.DONE) {
return;
}
// @ts-ignore
const responseHeaders = {};
const rawHeaders = xhr.getAllResponseHeaders();
const headers = rawHeaders.trim().split(/[\r\n]+/);
headers.forEach((line) => {
const parts = line.split(': ');
const header = parts.shift();
const value = parts.join(': ');
if (header) {
responseHeaders[header] = value;
}
});
if (configs.recordResponseHeaders) {
networkRequest.responseHeaders = responseHeaders;
}
if (configs.shouldRecordBody) {
const responseBody = _tryReadXHRBody({ body: xhr.response, url });
if ((responseBody === null || responseBody === void 0 ? void 0 : responseBody.length)
&& _isWithinPayloadLimit(responseBody)) {
networkRequest.responseBody = responseBody;
}
}
});
// @ts-ignore
xhr.networkRequest = networkRequest;
originalOpen.call(xhr, method, url, async, username, password);
};
})(XMLHttpRequest.prototype);
}
//# sourceMappingURL=xhr.js.map