@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/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.13" || 0;
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
- // Standard OTEL exception event + span status
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
- span.recordException(error);
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 stream
25595
+ // Clone the response to avoid consuming the original
25555
25596
  const clonedResponse = response.clone();
25556
- // Try different methods to read the body
25557
- if ((_a = response.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.includes('application/json')) {
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
- else if ((_b = response.headers.get('content-type')) === null || _b === void 0 ? void 0 : _b.includes('text/')) {
25610
+ if (contentType.includes('text/')) {
25562
25611
  return await clonedResponse.text();
25563
25612
  }
25564
- else {
25565
- // For other content types, try text first, fallback to arrayBuffer
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
- return await clonedResponse.text();
25619
+ const arrayBuffer = await clonedResponse.arrayBuffer();
25620
+ return `[Fetch] Binary data (${arrayBuffer.byteLength} bytes)`;
25568
25621
  }
25569
- catch (_c) {
25570
- try {
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
- : (typeof input === 'string' || input instanceof URL ? String(input) : '');
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
  }