@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.
@@ -25051,7 +25051,7 @@ const CONTINUOUS_DEBUGGING_TIMEOUT = 60000; // 1 minutes
25051
25051
  const DEBUG_SESSION_MAX_DURATION_SECONDS = 10 * 60 + 30; // TODO: move to shared config otel core
25052
25052
  const REMOTE_SESSION_RECORDING_START = 'remote-session-recording:start';
25053
25053
  const REMOTE_SESSION_RECORDING_STOP = 'remote-session-recording:stop';
25054
- const PACKAGE_VERSION_EXPORT = "1.3.13" || 0;
25054
+ const PACKAGE_VERSION_EXPORT = "1.3.15" || 0;
25055
25055
  // Regex patterns for OpenTelemetry ignore URLs
25056
25056
  const OTEL_IGNORE_URLS = [
25057
25057
  // Traces endpoint
@@ -25989,7 +25989,6 @@ __webpack_require__.r(__webpack_exports__);
25989
25989
 
25990
25990
 
25991
25991
 
25992
-
25993
25992
  class TracerBrowserSDK {
25994
25993
  constructor() {
25995
25994
  this.sessionId = '';
@@ -26164,14 +26163,7 @@ class TracerBrowserSDK {
26164
26163
  try {
26165
26164
  const activeSpan = _opentelemetry_api__WEBPACK_IMPORTED_MODULE_10__.trace.getSpan(_opentelemetry_api__WEBPACK_IMPORTED_MODULE_11__.context.active());
26166
26165
  if (activeSpan) {
26167
- // Standard OTEL exception event + span status
26168
- _multiplayer_app_session_recorder_common__WEBPACK_IMPORTED_MODULE_1__.SessionRecorderSdk.captureException(error);
26169
- activeSpan.addEvent('exception', {
26170
- 'exception.type': error.name || 'Error',
26171
- 'exception.message': error.message,
26172
- 'exception.stacktrace': error.stack || '',
26173
- ...(errorInfo || {}),
26174
- });
26166
+ this._recordException(activeSpan, error, errorInfo);
26175
26167
  return;
26176
26168
  }
26177
26169
  // eslint-disable-next-line
@@ -26181,19 +26173,24 @@ class TracerBrowserSDK {
26181
26173
  try {
26182
26174
  const tracer = _opentelemetry_api__WEBPACK_IMPORTED_MODULE_10__.trace.getTracer('exception');
26183
26175
  const span = tracer.startSpan(error.name || 'Error');
26184
- span.recordException(error);
26185
- span.setStatus({ code: _opentelemetry_api__WEBPACK_IMPORTED_MODULE_12__.SpanStatusCode.ERROR, message: error.message });
26186
- span.addEvent('exception', {
26187
- 'exception.type': error.name || 'Error',
26188
- 'exception.message': error.message,
26189
- 'exception.stacktrace': error.stack || '',
26190
- ...(errorInfo || {}),
26191
- });
26176
+ this._recordException(span, error, errorInfo);
26192
26177
  span.end();
26193
26178
  // eslint-disable-next-line
26194
26179
  }
26195
26180
  catch (_ignored) { }
26196
26181
  }
26182
+ _recordException(span, error, errorInfo) {
26183
+ span.recordException(error);
26184
+ span.setStatus({ code: _opentelemetry_api__WEBPACK_IMPORTED_MODULE_12__.SpanStatusCode.ERROR, message: error.message });
26185
+ span.setAttribute('exception.type', error.name || 'Error');
26186
+ span.setAttribute('exception.message', error.message);
26187
+ span.setAttribute('exception.stacktrace', error.stack || '');
26188
+ if (errorInfo) {
26189
+ Object.entries(errorInfo).forEach(([key, value]) => {
26190
+ span.setAttribute(`error_info.${key}`, value);
26191
+ });
26192
+ }
26193
+ }
26197
26194
  _getSpanSessionIdProcessor() {
26198
26195
  return {
26199
26196
  forceFlush: () => Promise.resolve(),
@@ -26283,7 +26280,7 @@ __webpack_require__.r(__webpack_exports__);
26283
26280
 
26284
26281
 
26285
26282
 
26286
- function _tryReadFetchBody({ body, url, }) {
26283
+ function _tryReadFetchBody({ body, url }) {
26287
26284
  if ((0,_utils_type_utils__WEBPACK_IMPORTED_MODULE_0__.isNullish)(body)) {
26288
26285
  return null;
26289
26286
  }
@@ -26306,32 +26303,82 @@ function _tryReadFetchBody({ body, url, }) {
26306
26303
  }
26307
26304
  return `[Fetch] Cannot read body of type ${toString.call(body)}`;
26308
26305
  }
26306
+ /**
26307
+ * Detects if a response is a streaming response that should NOT have its body read.
26308
+ * Reading the body of streaming responses (SSE, chunked streams, etc.) will either:
26309
+ * - Block forever (SSE streams never end)
26310
+ * - Corrupt the stream for the actual consumer
26311
+ */
26312
+ function _isStreamingResponse(response) {
26313
+ var _a, _b, _c;
26314
+ const contentType = (_b = (_a = response.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : '';
26315
+ // SSE - Server-Sent Events (infinite stream)
26316
+ if (contentType.includes('text/event-stream')) {
26317
+ return true;
26318
+ }
26319
+ // Binary streams that are typically long-running
26320
+ if (contentType.includes('application/octet-stream')) {
26321
+ return true;
26322
+ }
26323
+ // NDJSON streaming (newline-delimited JSON, common in streaming APIs)
26324
+ if (contentType.includes('application/x-ndjson') || contentType.includes('application/ndjson')) {
26325
+ return true;
26326
+ }
26327
+ // gRPC-web streaming
26328
+ if (contentType.includes('application/grpc')) {
26329
+ return true;
26330
+ }
26331
+ // Check for chunked transfer encoding (often indicates streaming)
26332
+ const transferEncoding = (_c = response.headers.get('transfer-encoding')) === null || _c === void 0 ? void 0 : _c.toLowerCase();
26333
+ if (transferEncoding === null || transferEncoding === void 0 ? void 0 : transferEncoding.includes('chunked')) {
26334
+ // Chunked alone isn't definitive, but combined with no content-length = streaming
26335
+ const contentLength = response.headers.get('content-length');
26336
+ if (!contentLength) {
26337
+ return true;
26338
+ }
26339
+ }
26340
+ return false;
26341
+ }
26342
+ /**
26343
+ * Safely reads response body for non-streaming responses.
26344
+ * Returns null for streaming responses to avoid blocking/corruption.
26345
+ */
26309
26346
  async function _tryReadResponseBody(response) {
26310
26347
  var _a, _b;
26348
+ // CRITICAL: Never attempt to read streaming response bodies
26349
+ if (_isStreamingResponse(response)) {
26350
+ return null;
26351
+ }
26311
26352
  try {
26312
- // Clone the response to avoid consuming the original stream
26353
+ // Clone the response to avoid consuming the original
26313
26354
  const clonedResponse = response.clone();
26314
- // Try different methods to read the body
26315
- if ((_a = response.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.includes('application/json')) {
26355
+ const contentType = (_b = (_a = response.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : '';
26356
+ // Check content-length to avoid reading massive responses
26357
+ const contentLength = response.headers.get('content-length');
26358
+ if (contentLength) {
26359
+ const length = parseInt(contentLength, 10);
26360
+ if (!isNaN(length) && length > _configs__WEBPACK_IMPORTED_MODULE_2__.configs.maxCapturingHttpPayloadSize) {
26361
+ return `[Fetch] Response too large (${length} bytes)`;
26362
+ }
26363
+ }
26364
+ if (contentType.includes('application/json')) {
26316
26365
  const json = await clonedResponse.json();
26317
26366
  return JSON.stringify(json);
26318
26367
  }
26319
- else if ((_b = response.headers.get('content-type')) === null || _b === void 0 ? void 0 : _b.includes('text/')) {
26368
+ if (contentType.includes('text/')) {
26320
26369
  return await clonedResponse.text();
26321
26370
  }
26322
- else {
26323
- // For other content types, try text first, fallback to arrayBuffer
26371
+ // For unknown types, attempt text read with timeout protection
26372
+ try {
26373
+ return await clonedResponse.text();
26374
+ }
26375
+ catch (_c) {
26324
26376
  try {
26325
- return await clonedResponse.text();
26377
+ const arrayBuffer = await clonedResponse.arrayBuffer();
26378
+ return `[Fetch] Binary data (${arrayBuffer.byteLength} bytes)`;
26326
26379
  }
26327
- catch (_c) {
26328
- try {
26329
- const arrayBuffer = await clonedResponse.arrayBuffer();
26330
- return `[Fetch] Binary data (${arrayBuffer.byteLength} bytes)`;
26331
- }
26332
- catch (_d) {
26333
- return '[Fetch] Unable to read response body';
26334
- }
26380
+ catch (_d) {
26381
+ return '[Fetch] Unable to read response body';
26335
26382
  }
26336
26383
  }
26337
26384
  }
@@ -26374,6 +26421,7 @@ if (typeof window !== 'undefined' && typeof window.fetch !== 'undefined') {
26374
26421
  }
26375
26422
  else {
26376
26423
  // @ts-ignore
26424
+ ;
26377
26425
  window.fetch.__mp_session_recorder_patched__ = true;
26378
26426
  // Store original fetch
26379
26427
  const originalFetch = window.fetch;
@@ -26395,7 +26443,9 @@ if (typeof window !== 'undefined' && typeof window.fetch !== 'undefined') {
26395
26443
  if (_configs__WEBPACK_IMPORTED_MODULE_2__.configs.shouldRecordBody) {
26396
26444
  const urlStr = inputIsRequest
26397
26445
  ? input.url
26398
- : (typeof input === 'string' || input instanceof URL ? String(input) : '');
26446
+ : typeof input === 'string' || input instanceof URL
26447
+ ? String(input)
26448
+ : '';
26399
26449
  // Only attempt to read the body from init (safe); avoid constructing/cloning Requests
26400
26450
  // If the caller passed a Request as input, we do not attempt to read its body here
26401
26451
  // eslint-disable-next-line
@@ -26403,10 +26453,9 @@ if (typeof window !== 'undefined' && typeof window.fetch !== 'undefined') {
26403
26453
  if (!(0,_utils_type_utils__WEBPACK_IMPORTED_MODULE_0__.isNullish)(candidateBody)) {
26404
26454
  const requestBody = _tryReadFetchBody({
26405
26455
  body: candidateBody,
26406
- url: urlStr,
26456
+ url: urlStr
26407
26457
  });
26408
- if ((requestBody === null || requestBody === void 0 ? void 0 : requestBody.length) &&
26409
- new Blob([requestBody]).size <= _configs__WEBPACK_IMPORTED_MODULE_2__.configs.maxCapturingHttpPayloadSize) {
26458
+ if ((requestBody === null || requestBody === void 0 ? void 0 : requestBody.length) && new Blob([requestBody]).size <= _configs__WEBPACK_IMPORTED_MODULE_2__.configs.maxCapturingHttpPayloadSize) {
26410
26459
  networkRequest.requestBody = requestBody;
26411
26460
  }
26412
26461
  }
@@ -26420,8 +26469,7 @@ if (typeof window !== 'undefined' && typeof window.fetch !== 'undefined') {
26420
26469
  }
26421
26470
  if (_configs__WEBPACK_IMPORTED_MODULE_2__.configs.shouldRecordBody) {
26422
26471
  const responseBody = await _tryReadResponseBody(response);
26423
- if ((responseBody === null || responseBody === void 0 ? void 0 : responseBody.length) &&
26424
- new Blob([responseBody]).size <= _configs__WEBPACK_IMPORTED_MODULE_2__.configs.maxCapturingHttpPayloadSize) {
26472
+ if ((responseBody === null || responseBody === void 0 ? void 0 : responseBody.length) && new Blob([responseBody]).size <= _configs__WEBPACK_IMPORTED_MODULE_2__.configs.maxCapturingHttpPayloadSize) {
26425
26473
  networkRequest.responseBody = responseBody;
26426
26474
  }
26427
26475
  }