@multiplayer-app/session-recorder-browser 1.3.13 → 1.3.15
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/dist/browser/index.js +88 -40
- package/dist/exporters/index.js +1 -1
- package/dist/index.js +88 -40
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +88 -40
- package/dist/index.umd.js.map +1 -1
- package/dist/otel/index.d.ts +1 -0
- package/dist/otel/index.d.ts.map +1 -1
- package/dist/otel/index.js +14 -17
- package/dist/otel/index.js.map +1 -1
- package/dist/patch/fetch.js +74 -23
- package/dist/patch/fetch.js.map +1 -1
- package/package.json +3 -3
package/dist/index.umd.js
CHANGED
|
@@ -24323,7 +24323,7 @@ const CONTINUOUS_DEBUGGING_TIMEOUT = 60000; // 1 minutes
|
|
|
24323
24323
|
const DEBUG_SESSION_MAX_DURATION_SECONDS = 10 * 60 + 30; // TODO: move to shared config otel core
|
|
24324
24324
|
const REMOTE_SESSION_RECORDING_START = 'remote-session-recording:start';
|
|
24325
24325
|
const REMOTE_SESSION_RECORDING_STOP = 'remote-session-recording:stop';
|
|
24326
|
-
const PACKAGE_VERSION_EXPORT = "1.3.
|
|
24326
|
+
const PACKAGE_VERSION_EXPORT = "1.3.15" || 0;
|
|
24327
24327
|
// Regex patterns for OpenTelemetry ignore URLs
|
|
24328
24328
|
const OTEL_IGNORE_URLS = [
|
|
24329
24329
|
// Traces endpoint
|
|
@@ -25233,7 +25233,6 @@ const getElementTextContent = (element) => {
|
|
|
25233
25233
|
|
|
25234
25234
|
|
|
25235
25235
|
|
|
25236
|
-
|
|
25237
25236
|
class TracerBrowserSDK {
|
|
25238
25237
|
constructor() {
|
|
25239
25238
|
this.sessionId = '';
|
|
@@ -25408,14 +25407,7 @@ class TracerBrowserSDK {
|
|
|
25408
25407
|
try {
|
|
25409
25408
|
const activeSpan = _opentelemetry_api__WEBPACK_IMPORTED_MODULE_10__.trace.getSpan(_opentelemetry_api__WEBPACK_IMPORTED_MODULE_11__.context.active());
|
|
25410
25409
|
if (activeSpan) {
|
|
25411
|
-
|
|
25412
|
-
_multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_1__.SessionRecorderSdk.captureException(error);
|
|
25413
|
-
activeSpan.addEvent('exception', {
|
|
25414
|
-
'exception.type': error.name || 'Error',
|
|
25415
|
-
'exception.message': error.message,
|
|
25416
|
-
'exception.stacktrace': error.stack || '',
|
|
25417
|
-
...(errorInfo || {}),
|
|
25418
|
-
});
|
|
25410
|
+
this._recordException(activeSpan, error, errorInfo);
|
|
25419
25411
|
return;
|
|
25420
25412
|
}
|
|
25421
25413
|
// eslint-disable-next-line
|
|
@@ -25425,19 +25417,24 @@ class TracerBrowserSDK {
|
|
|
25425
25417
|
try {
|
|
25426
25418
|
const tracer = _opentelemetry_api__WEBPACK_IMPORTED_MODULE_10__.trace.getTracer('exception');
|
|
25427
25419
|
const span = tracer.startSpan(error.name || 'Error');
|
|
25428
|
-
|
|
25429
|
-
span.setStatus({ code: _opentelemetry_api__WEBPACK_IMPORTED_MODULE_12__.SpanStatusCode.ERROR, message: error.message });
|
|
25430
|
-
span.addEvent('exception', {
|
|
25431
|
-
'exception.type': error.name || 'Error',
|
|
25432
|
-
'exception.message': error.message,
|
|
25433
|
-
'exception.stacktrace': error.stack || '',
|
|
25434
|
-
...(errorInfo || {}),
|
|
25435
|
-
});
|
|
25420
|
+
this._recordException(span, error, errorInfo);
|
|
25436
25421
|
span.end();
|
|
25437
25422
|
// eslint-disable-next-line
|
|
25438
25423
|
}
|
|
25439
25424
|
catch (_ignored) { }
|
|
25440
25425
|
}
|
|
25426
|
+
_recordException(span, error, errorInfo) {
|
|
25427
|
+
span.recordException(error);
|
|
25428
|
+
span.setStatus({ code: _opentelemetry_api__WEBPACK_IMPORTED_MODULE_12__.SpanStatusCode.ERROR, message: error.message });
|
|
25429
|
+
span.setAttribute('exception.type', error.name || 'Error');
|
|
25430
|
+
span.setAttribute('exception.message', error.message);
|
|
25431
|
+
span.setAttribute('exception.stacktrace', error.stack || '');
|
|
25432
|
+
if (errorInfo) {
|
|
25433
|
+
Object.entries(errorInfo).forEach(([key, value]) => {
|
|
25434
|
+
span.setAttribute(`error_info.${key}`, value);
|
|
25435
|
+
});
|
|
25436
|
+
}
|
|
25437
|
+
}
|
|
25441
25438
|
_getSpanSessionIdProcessor() {
|
|
25442
25439
|
return {
|
|
25443
25440
|
forceFlush: () => Promise.resolve(),
|
|
@@ -25525,7 +25522,7 @@ const setShouldRecordHttpData = (shouldRecordBody, shouldRecordHeaders) => {
|
|
|
25525
25522
|
|
|
25526
25523
|
|
|
25527
25524
|
|
|
25528
|
-
function _tryReadFetchBody({ body, url
|
|
25525
|
+
function _tryReadFetchBody({ body, url }) {
|
|
25529
25526
|
if ((0,_utils_type_utils__WEBPACK_IMPORTED_MODULE_0__.isNullish)(body)) {
|
|
25530
25527
|
return null;
|
|
25531
25528
|
}
|
|
@@ -25548,32 +25545,82 @@ function _tryReadFetchBody({ body, url, }) {
|
|
|
25548
25545
|
}
|
|
25549
25546
|
return `[Fetch] Cannot read body of type ${toString.call(body)}`;
|
|
25550
25547
|
}
|
|
25548
|
+
/**
|
|
25549
|
+
* Detects if a response is a streaming response that should NOT have its body read.
|
|
25550
|
+
* Reading the body of streaming responses (SSE, chunked streams, etc.) will either:
|
|
25551
|
+
* - Block forever (SSE streams never end)
|
|
25552
|
+
* - Corrupt the stream for the actual consumer
|
|
25553
|
+
*/
|
|
25554
|
+
function _isStreamingResponse(response) {
|
|
25555
|
+
var _a, _b, _c;
|
|
25556
|
+
const contentType = (_b = (_a = response.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : '';
|
|
25557
|
+
// SSE - Server-Sent Events (infinite stream)
|
|
25558
|
+
if (contentType.includes('text/event-stream')) {
|
|
25559
|
+
return true;
|
|
25560
|
+
}
|
|
25561
|
+
// Binary streams that are typically long-running
|
|
25562
|
+
if (contentType.includes('application/octet-stream')) {
|
|
25563
|
+
return true;
|
|
25564
|
+
}
|
|
25565
|
+
// NDJSON streaming (newline-delimited JSON, common in streaming APIs)
|
|
25566
|
+
if (contentType.includes('application/x-ndjson') || contentType.includes('application/ndjson')) {
|
|
25567
|
+
return true;
|
|
25568
|
+
}
|
|
25569
|
+
// gRPC-web streaming
|
|
25570
|
+
if (contentType.includes('application/grpc')) {
|
|
25571
|
+
return true;
|
|
25572
|
+
}
|
|
25573
|
+
// Check for chunked transfer encoding (often indicates streaming)
|
|
25574
|
+
const transferEncoding = (_c = response.headers.get('transfer-encoding')) === null || _c === void 0 ? void 0 : _c.toLowerCase();
|
|
25575
|
+
if (transferEncoding === null || transferEncoding === void 0 ? void 0 : transferEncoding.includes('chunked')) {
|
|
25576
|
+
// Chunked alone isn't definitive, but combined with no content-length = streaming
|
|
25577
|
+
const contentLength = response.headers.get('content-length');
|
|
25578
|
+
if (!contentLength) {
|
|
25579
|
+
return true;
|
|
25580
|
+
}
|
|
25581
|
+
}
|
|
25582
|
+
return false;
|
|
25583
|
+
}
|
|
25584
|
+
/**
|
|
25585
|
+
* Safely reads response body for non-streaming responses.
|
|
25586
|
+
* Returns null for streaming responses to avoid blocking/corruption.
|
|
25587
|
+
*/
|
|
25551
25588
|
async function _tryReadResponseBody(response) {
|
|
25552
25589
|
var _a, _b;
|
|
25590
|
+
// CRITICAL: Never attempt to read streaming response bodies
|
|
25591
|
+
if (_isStreamingResponse(response)) {
|
|
25592
|
+
return null;
|
|
25593
|
+
}
|
|
25553
25594
|
try {
|
|
25554
|
-
// Clone the response to avoid consuming the original
|
|
25595
|
+
// Clone the response to avoid consuming the original
|
|
25555
25596
|
const clonedResponse = response.clone();
|
|
25556
|
-
|
|
25557
|
-
|
|
25597
|
+
const contentType = (_b = (_a = response.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : '';
|
|
25598
|
+
// Check content-length to avoid reading massive responses
|
|
25599
|
+
const contentLength = response.headers.get('content-length');
|
|
25600
|
+
if (contentLength) {
|
|
25601
|
+
const length = parseInt(contentLength, 10);
|
|
25602
|
+
if (!isNaN(length) && length > _configs__WEBPACK_IMPORTED_MODULE_2__.configs.maxCapturingHttpPayloadSize) {
|
|
25603
|
+
return `[Fetch] Response too large (${length} bytes)`;
|
|
25604
|
+
}
|
|
25605
|
+
}
|
|
25606
|
+
if (contentType.includes('application/json')) {
|
|
25558
25607
|
const json = await clonedResponse.json();
|
|
25559
25608
|
return JSON.stringify(json);
|
|
25560
25609
|
}
|
|
25561
|
-
|
|
25610
|
+
if (contentType.includes('text/')) {
|
|
25562
25611
|
return await clonedResponse.text();
|
|
25563
25612
|
}
|
|
25564
|
-
|
|
25565
|
-
|
|
25613
|
+
// For unknown types, attempt text read with timeout protection
|
|
25614
|
+
try {
|
|
25615
|
+
return await clonedResponse.text();
|
|
25616
|
+
}
|
|
25617
|
+
catch (_c) {
|
|
25566
25618
|
try {
|
|
25567
|
-
|
|
25619
|
+
const arrayBuffer = await clonedResponse.arrayBuffer();
|
|
25620
|
+
return `[Fetch] Binary data (${arrayBuffer.byteLength} bytes)`;
|
|
25568
25621
|
}
|
|
25569
|
-
catch (
|
|
25570
|
-
|
|
25571
|
-
const arrayBuffer = await clonedResponse.arrayBuffer();
|
|
25572
|
-
return `[Fetch] Binary data (${arrayBuffer.byteLength} bytes)`;
|
|
25573
|
-
}
|
|
25574
|
-
catch (_d) {
|
|
25575
|
-
return '[Fetch] Unable to read response body';
|
|
25576
|
-
}
|
|
25622
|
+
catch (_d) {
|
|
25623
|
+
return '[Fetch] Unable to read response body';
|
|
25577
25624
|
}
|
|
25578
25625
|
}
|
|
25579
25626
|
}
|
|
@@ -25616,6 +25663,7 @@ if (typeof window !== 'undefined' && typeof window.fetch !== 'undefined') {
|
|
|
25616
25663
|
}
|
|
25617
25664
|
else {
|
|
25618
25665
|
// @ts-ignore
|
|
25666
|
+
;
|
|
25619
25667
|
window.fetch.__mp_session_recorder_patched__ = true;
|
|
25620
25668
|
// Store original fetch
|
|
25621
25669
|
const originalFetch = window.fetch;
|
|
@@ -25637,7 +25685,9 @@ if (typeof window !== 'undefined' && typeof window.fetch !== 'undefined') {
|
|
|
25637
25685
|
if (_configs__WEBPACK_IMPORTED_MODULE_2__.configs.shouldRecordBody) {
|
|
25638
25686
|
const urlStr = inputIsRequest
|
|
25639
25687
|
? input.url
|
|
25640
|
-
:
|
|
25688
|
+
: typeof input === 'string' || input instanceof URL
|
|
25689
|
+
? String(input)
|
|
25690
|
+
: '';
|
|
25641
25691
|
// Only attempt to read the body from init (safe); avoid constructing/cloning Requests
|
|
25642
25692
|
// If the caller passed a Request as input, we do not attempt to read its body here
|
|
25643
25693
|
// eslint-disable-next-line
|
|
@@ -25645,10 +25695,9 @@ if (typeof window !== 'undefined' && typeof window.fetch !== 'undefined') {
|
|
|
25645
25695
|
if (!(0,_utils_type_utils__WEBPACK_IMPORTED_MODULE_0__.isNullish)(candidateBody)) {
|
|
25646
25696
|
const requestBody = _tryReadFetchBody({
|
|
25647
25697
|
body: candidateBody,
|
|
25648
|
-
url: urlStr
|
|
25698
|
+
url: urlStr
|
|
25649
25699
|
});
|
|
25650
|
-
if ((requestBody === null || requestBody === void 0 ? void 0 : requestBody.length) &&
|
|
25651
|
-
new Blob([requestBody]).size <= _configs__WEBPACK_IMPORTED_MODULE_2__.configs.maxCapturingHttpPayloadSize) {
|
|
25700
|
+
if ((requestBody === null || requestBody === void 0 ? void 0 : requestBody.length) && new Blob([requestBody]).size <= _configs__WEBPACK_IMPORTED_MODULE_2__.configs.maxCapturingHttpPayloadSize) {
|
|
25652
25701
|
networkRequest.requestBody = requestBody;
|
|
25653
25702
|
}
|
|
25654
25703
|
}
|
|
@@ -25662,8 +25711,7 @@ if (typeof window !== 'undefined' && typeof window.fetch !== 'undefined') {
|
|
|
25662
25711
|
}
|
|
25663
25712
|
if (_configs__WEBPACK_IMPORTED_MODULE_2__.configs.shouldRecordBody) {
|
|
25664
25713
|
const responseBody = await _tryReadResponseBody(response);
|
|
25665
|
-
if ((responseBody === null || responseBody === void 0 ? void 0 : responseBody.length) &&
|
|
25666
|
-
new Blob([responseBody]).size <= _configs__WEBPACK_IMPORTED_MODULE_2__.configs.maxCapturingHttpPayloadSize) {
|
|
25714
|
+
if ((responseBody === null || responseBody === void 0 ? void 0 : responseBody.length) && new Blob([responseBody]).size <= _configs__WEBPACK_IMPORTED_MODULE_2__.configs.maxCapturingHttpPayloadSize) {
|
|
25667
25715
|
networkRequest.responseBody = responseBody;
|
|
25668
25716
|
}
|
|
25669
25717
|
}
|