@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.js
CHANGED
|
@@ -24149,7 +24149,7 @@ const CONTINUOUS_DEBUGGING_TIMEOUT = 60000; // 1 minutes
|
|
|
24149
24149
|
const DEBUG_SESSION_MAX_DURATION_SECONDS = 10 * 60 + 30; // TODO: move to shared config otel core
|
|
24150
24150
|
const REMOTE_SESSION_RECORDING_START = 'remote-session-recording:start';
|
|
24151
24151
|
const REMOTE_SESSION_RECORDING_STOP = 'remote-session-recording:stop';
|
|
24152
|
-
const PACKAGE_VERSION_EXPORT = "1.3.
|
|
24152
|
+
const PACKAGE_VERSION_EXPORT = "1.3.15" || 0;
|
|
24153
24153
|
// Regex patterns for OpenTelemetry ignore URLs
|
|
24154
24154
|
const OTEL_IGNORE_URLS = [
|
|
24155
24155
|
// Traces endpoint
|
|
@@ -25046,7 +25046,6 @@ const getElementTextContent = (element) => {
|
|
|
25046
25046
|
|
|
25047
25047
|
|
|
25048
25048
|
|
|
25049
|
-
|
|
25050
25049
|
class TracerBrowserSDK {
|
|
25051
25050
|
constructor() {
|
|
25052
25051
|
this.sessionId = '';
|
|
@@ -25221,14 +25220,7 @@ class TracerBrowserSDK {
|
|
|
25221
25220
|
try {
|
|
25222
25221
|
const activeSpan = _opentelemetry_api__WEBPACK_IMPORTED_MODULE_10__.trace.getSpan(_opentelemetry_api__WEBPACK_IMPORTED_MODULE_11__.context.active());
|
|
25223
25222
|
if (activeSpan) {
|
|
25224
|
-
|
|
25225
|
-
_multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_1__.SessionRecorderSdk.captureException(error);
|
|
25226
|
-
activeSpan.addEvent('exception', {
|
|
25227
|
-
'exception.type': error.name || 'Error',
|
|
25228
|
-
'exception.message': error.message,
|
|
25229
|
-
'exception.stacktrace': error.stack || '',
|
|
25230
|
-
...(errorInfo || {}),
|
|
25231
|
-
});
|
|
25223
|
+
this._recordException(activeSpan, error, errorInfo);
|
|
25232
25224
|
return;
|
|
25233
25225
|
}
|
|
25234
25226
|
// eslint-disable-next-line
|
|
@@ -25238,19 +25230,24 @@ class TracerBrowserSDK {
|
|
|
25238
25230
|
try {
|
|
25239
25231
|
const tracer = _opentelemetry_api__WEBPACK_IMPORTED_MODULE_10__.trace.getTracer('exception');
|
|
25240
25232
|
const span = tracer.startSpan(error.name || 'Error');
|
|
25241
|
-
|
|
25242
|
-
span.setStatus({ code: _opentelemetry_api__WEBPACK_IMPORTED_MODULE_12__.SpanStatusCode.ERROR, message: error.message });
|
|
25243
|
-
span.addEvent('exception', {
|
|
25244
|
-
'exception.type': error.name || 'Error',
|
|
25245
|
-
'exception.message': error.message,
|
|
25246
|
-
'exception.stacktrace': error.stack || '',
|
|
25247
|
-
...(errorInfo || {}),
|
|
25248
|
-
});
|
|
25233
|
+
this._recordException(span, error, errorInfo);
|
|
25249
25234
|
span.end();
|
|
25250
25235
|
// eslint-disable-next-line
|
|
25251
25236
|
}
|
|
25252
25237
|
catch (_ignored) { }
|
|
25253
25238
|
}
|
|
25239
|
+
_recordException(span, error, errorInfo) {
|
|
25240
|
+
span.recordException(error);
|
|
25241
|
+
span.setStatus({ code: _opentelemetry_api__WEBPACK_IMPORTED_MODULE_12__.SpanStatusCode.ERROR, message: error.message });
|
|
25242
|
+
span.setAttribute('exception.type', error.name || 'Error');
|
|
25243
|
+
span.setAttribute('exception.message', error.message);
|
|
25244
|
+
span.setAttribute('exception.stacktrace', error.stack || '');
|
|
25245
|
+
if (errorInfo) {
|
|
25246
|
+
Object.entries(errorInfo).forEach(([key, value]) => {
|
|
25247
|
+
span.setAttribute(`error_info.${key}`, value);
|
|
25248
|
+
});
|
|
25249
|
+
}
|
|
25250
|
+
}
|
|
25254
25251
|
_getSpanSessionIdProcessor() {
|
|
25255
25252
|
return {
|
|
25256
25253
|
forceFlush: () => Promise.resolve(),
|
|
@@ -25336,7 +25333,7 @@ const setShouldRecordHttpData = (shouldRecordBody, shouldRecordHeaders) => {
|
|
|
25336
25333
|
|
|
25337
25334
|
|
|
25338
25335
|
|
|
25339
|
-
function _tryReadFetchBody({ body, url
|
|
25336
|
+
function _tryReadFetchBody({ body, url }) {
|
|
25340
25337
|
if ((0,_utils_type_utils__WEBPACK_IMPORTED_MODULE_0__.isNullish)(body)) {
|
|
25341
25338
|
return null;
|
|
25342
25339
|
}
|
|
@@ -25359,32 +25356,82 @@ function _tryReadFetchBody({ body, url, }) {
|
|
|
25359
25356
|
}
|
|
25360
25357
|
return `[Fetch] Cannot read body of type ${toString.call(body)}`;
|
|
25361
25358
|
}
|
|
25359
|
+
/**
|
|
25360
|
+
* Detects if a response is a streaming response that should NOT have its body read.
|
|
25361
|
+
* Reading the body of streaming responses (SSE, chunked streams, etc.) will either:
|
|
25362
|
+
* - Block forever (SSE streams never end)
|
|
25363
|
+
* - Corrupt the stream for the actual consumer
|
|
25364
|
+
*/
|
|
25365
|
+
function _isStreamingResponse(response) {
|
|
25366
|
+
var _a, _b, _c;
|
|
25367
|
+
const contentType = (_b = (_a = response.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : '';
|
|
25368
|
+
// SSE - Server-Sent Events (infinite stream)
|
|
25369
|
+
if (contentType.includes('text/event-stream')) {
|
|
25370
|
+
return true;
|
|
25371
|
+
}
|
|
25372
|
+
// Binary streams that are typically long-running
|
|
25373
|
+
if (contentType.includes('application/octet-stream')) {
|
|
25374
|
+
return true;
|
|
25375
|
+
}
|
|
25376
|
+
// NDJSON streaming (newline-delimited JSON, common in streaming APIs)
|
|
25377
|
+
if (contentType.includes('application/x-ndjson') || contentType.includes('application/ndjson')) {
|
|
25378
|
+
return true;
|
|
25379
|
+
}
|
|
25380
|
+
// gRPC-web streaming
|
|
25381
|
+
if (contentType.includes('application/grpc')) {
|
|
25382
|
+
return true;
|
|
25383
|
+
}
|
|
25384
|
+
// Check for chunked transfer encoding (often indicates streaming)
|
|
25385
|
+
const transferEncoding = (_c = response.headers.get('transfer-encoding')) === null || _c === void 0 ? void 0 : _c.toLowerCase();
|
|
25386
|
+
if (transferEncoding === null || transferEncoding === void 0 ? void 0 : transferEncoding.includes('chunked')) {
|
|
25387
|
+
// Chunked alone isn't definitive, but combined with no content-length = streaming
|
|
25388
|
+
const contentLength = response.headers.get('content-length');
|
|
25389
|
+
if (!contentLength) {
|
|
25390
|
+
return true;
|
|
25391
|
+
}
|
|
25392
|
+
}
|
|
25393
|
+
return false;
|
|
25394
|
+
}
|
|
25395
|
+
/**
|
|
25396
|
+
* Safely reads response body for non-streaming responses.
|
|
25397
|
+
* Returns null for streaming responses to avoid blocking/corruption.
|
|
25398
|
+
*/
|
|
25362
25399
|
async function _tryReadResponseBody(response) {
|
|
25363
25400
|
var _a, _b;
|
|
25401
|
+
// CRITICAL: Never attempt to read streaming response bodies
|
|
25402
|
+
if (_isStreamingResponse(response)) {
|
|
25403
|
+
return null;
|
|
25404
|
+
}
|
|
25364
25405
|
try {
|
|
25365
|
-
// Clone the response to avoid consuming the original
|
|
25406
|
+
// Clone the response to avoid consuming the original
|
|
25366
25407
|
const clonedResponse = response.clone();
|
|
25367
|
-
|
|
25368
|
-
|
|
25408
|
+
const contentType = (_b = (_a = response.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : '';
|
|
25409
|
+
// Check content-length to avoid reading massive responses
|
|
25410
|
+
const contentLength = response.headers.get('content-length');
|
|
25411
|
+
if (contentLength) {
|
|
25412
|
+
const length = parseInt(contentLength, 10);
|
|
25413
|
+
if (!isNaN(length) && length > _configs__WEBPACK_IMPORTED_MODULE_2__.configs.maxCapturingHttpPayloadSize) {
|
|
25414
|
+
return `[Fetch] Response too large (${length} bytes)`;
|
|
25415
|
+
}
|
|
25416
|
+
}
|
|
25417
|
+
if (contentType.includes('application/json')) {
|
|
25369
25418
|
const json = await clonedResponse.json();
|
|
25370
25419
|
return JSON.stringify(json);
|
|
25371
25420
|
}
|
|
25372
|
-
|
|
25421
|
+
if (contentType.includes('text/')) {
|
|
25373
25422
|
return await clonedResponse.text();
|
|
25374
25423
|
}
|
|
25375
|
-
|
|
25376
|
-
|
|
25424
|
+
// For unknown types, attempt text read with timeout protection
|
|
25425
|
+
try {
|
|
25426
|
+
return await clonedResponse.text();
|
|
25427
|
+
}
|
|
25428
|
+
catch (_c) {
|
|
25377
25429
|
try {
|
|
25378
|
-
|
|
25430
|
+
const arrayBuffer = await clonedResponse.arrayBuffer();
|
|
25431
|
+
return `[Fetch] Binary data (${arrayBuffer.byteLength} bytes)`;
|
|
25379
25432
|
}
|
|
25380
|
-
catch (
|
|
25381
|
-
|
|
25382
|
-
const arrayBuffer = await clonedResponse.arrayBuffer();
|
|
25383
|
-
return `[Fetch] Binary data (${arrayBuffer.byteLength} bytes)`;
|
|
25384
|
-
}
|
|
25385
|
-
catch (_d) {
|
|
25386
|
-
return '[Fetch] Unable to read response body';
|
|
25387
|
-
}
|
|
25433
|
+
catch (_d) {
|
|
25434
|
+
return '[Fetch] Unable to read response body';
|
|
25388
25435
|
}
|
|
25389
25436
|
}
|
|
25390
25437
|
}
|
|
@@ -25427,6 +25474,7 @@ if (typeof window !== 'undefined' && typeof window.fetch !== 'undefined') {
|
|
|
25427
25474
|
}
|
|
25428
25475
|
else {
|
|
25429
25476
|
// @ts-ignore
|
|
25477
|
+
;
|
|
25430
25478
|
window.fetch.__mp_session_recorder_patched__ = true;
|
|
25431
25479
|
// Store original fetch
|
|
25432
25480
|
const originalFetch = window.fetch;
|
|
@@ -25448,7 +25496,9 @@ if (typeof window !== 'undefined' && typeof window.fetch !== 'undefined') {
|
|
|
25448
25496
|
if (_configs__WEBPACK_IMPORTED_MODULE_2__.configs.shouldRecordBody) {
|
|
25449
25497
|
const urlStr = inputIsRequest
|
|
25450
25498
|
? input.url
|
|
25451
|
-
:
|
|
25499
|
+
: typeof input === 'string' || input instanceof URL
|
|
25500
|
+
? String(input)
|
|
25501
|
+
: '';
|
|
25452
25502
|
// Only attempt to read the body from init (safe); avoid constructing/cloning Requests
|
|
25453
25503
|
// If the caller passed a Request as input, we do not attempt to read its body here
|
|
25454
25504
|
// eslint-disable-next-line
|
|
@@ -25456,10 +25506,9 @@ if (typeof window !== 'undefined' && typeof window.fetch !== 'undefined') {
|
|
|
25456
25506
|
if (!(0,_utils_type_utils__WEBPACK_IMPORTED_MODULE_0__.isNullish)(candidateBody)) {
|
|
25457
25507
|
const requestBody = _tryReadFetchBody({
|
|
25458
25508
|
body: candidateBody,
|
|
25459
|
-
url: urlStr
|
|
25509
|
+
url: urlStr
|
|
25460
25510
|
});
|
|
25461
|
-
if ((requestBody === null || requestBody === void 0 ? void 0 : requestBody.length) &&
|
|
25462
|
-
new Blob([requestBody]).size <= _configs__WEBPACK_IMPORTED_MODULE_2__.configs.maxCapturingHttpPayloadSize) {
|
|
25511
|
+
if ((requestBody === null || requestBody === void 0 ? void 0 : requestBody.length) && new Blob([requestBody]).size <= _configs__WEBPACK_IMPORTED_MODULE_2__.configs.maxCapturingHttpPayloadSize) {
|
|
25463
25512
|
networkRequest.requestBody = requestBody;
|
|
25464
25513
|
}
|
|
25465
25514
|
}
|
|
@@ -25473,8 +25522,7 @@ if (typeof window !== 'undefined' && typeof window.fetch !== 'undefined') {
|
|
|
25473
25522
|
}
|
|
25474
25523
|
if (_configs__WEBPACK_IMPORTED_MODULE_2__.configs.shouldRecordBody) {
|
|
25475
25524
|
const responseBody = await _tryReadResponseBody(response);
|
|
25476
|
-
if ((responseBody === null || responseBody === void 0 ? void 0 : responseBody.length) &&
|
|
25477
|
-
new Blob([responseBody]).size <= _configs__WEBPACK_IMPORTED_MODULE_2__.configs.maxCapturingHttpPayloadSize) {
|
|
25525
|
+
if ((responseBody === null || responseBody === void 0 ? void 0 : responseBody.length) && new Blob([responseBody]).size <= _configs__WEBPACK_IMPORTED_MODULE_2__.configs.maxCapturingHttpPayloadSize) {
|
|
25478
25526
|
networkRequest.responseBody = responseBody;
|
|
25479
25527
|
}
|
|
25480
25528
|
}
|