@logbrew/react-native 0.1.0 → 0.1.2

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.
Files changed (51) hide show
  1. package/README.md +431 -18
  2. package/apollo.cjs +301 -0
  3. package/apollo.d.cts +70 -0
  4. package/apollo.d.ts +70 -0
  5. package/apollo.js +294 -0
  6. package/examples/apollo-link-spans.mjs +149 -0
  7. package/examples/index.mjs +29 -1
  8. package/examples/instrumentation-kit.mjs +304 -0
  9. package/examples/lifecycle-spans.mjs +131 -0
  10. package/examples/native-bridge-scope.mjs +107 -0
  11. package/examples/navigation-resource-spans.mjs +156 -0
  12. package/examples/package.json +8 -1
  13. package/examples/real-user-smoke.mjs +106 -9
  14. package/examples/resource-fetch-spans.mjs +133 -0
  15. package/examples/trace-correlation.mjs +135 -0
  16. package/global-errors.cjs +366 -0
  17. package/global-errors.d.cts +63 -0
  18. package/global-errors.d.ts +63 -0
  19. package/global-errors.js +7 -0
  20. package/index.cjs +625 -111
  21. package/index.d.cts +236 -0
  22. package/index.d.ts +236 -0
  23. package/index.js +613 -95
  24. package/index.native.js +18 -0
  25. package/instrumentation.cjs +639 -0
  26. package/instrumentation.d.cts +84 -0
  27. package/instrumentation.d.ts +84 -0
  28. package/instrumentation.js +634 -0
  29. package/lifecycle.cjs +129 -0
  30. package/lifecycle.d.cts +50 -0
  31. package/lifecycle.d.ts +50 -0
  32. package/lifecycle.js +121 -0
  33. package/metadata.cjs +175 -0
  34. package/metadata.js +165 -0
  35. package/metro.cjs +310 -0
  36. package/metro.d.cts +37 -0
  37. package/metro.d.ts +37 -0
  38. package/metro.js +6 -0
  39. package/native-bridge.cjs +127 -0
  40. package/native-bridge.d.cts +60 -0
  41. package/native-bridge.d.ts +60 -0
  42. package/native-bridge.js +125 -0
  43. package/package.json +128 -4
  44. package/release-artifacts.cjs +344 -0
  45. package/release-artifacts.d.cts +55 -0
  46. package/release-artifacts.d.ts +53 -0
  47. package/release-artifacts.js +8 -0
  48. package/resource-fetch.cjs +469 -0
  49. package/resource-fetch.d.cts +60 -0
  50. package/resource-fetch.d.ts +60 -0
  51. package/resource-fetch.js +464 -0
package/index.native.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import { AppState, Platform } from "react-native";
2
2
  import {
3
3
  captureAppStateChange,
4
+ captureReactNativeAction,
4
5
  captureReactNativeError,
6
+ captureReactNativeNetwork,
5
7
  captureScreenView,
6
8
  createAppStateListener,
7
9
  createLogBrewReactNativeClient,
@@ -34,6 +36,22 @@ export function captureDefaultAppStateChange(client, state, options = {}) {
34
36
  });
35
37
  }
36
38
 
39
+ export function captureDefaultReactNativeAction(client, input = {}) {
40
+ return captureReactNativeAction(client, {
41
+ platform: Platform,
42
+ appState: AppState,
43
+ ...input
44
+ });
45
+ }
46
+
47
+ export function captureDefaultReactNativeNetwork(client, input = {}) {
48
+ return captureReactNativeNetwork(client, {
49
+ platform: Platform,
50
+ appState: AppState,
51
+ ...input
52
+ });
53
+ }
54
+
37
55
  export function captureDefaultReactNativeError(client, error, options = {}) {
38
56
  return captureReactNativeError(client, error, {
39
57
  platform: Platform,
@@ -0,0 +1,639 @@
1
+ "use strict";
2
+
3
+ const { SdkError } = require("@logbrew/sdk");
4
+ const {
5
+ captureReactNativeResourceSpan,
6
+ createReactNavigationSpanListener,
7
+ createReactNativeTraceContext,
8
+ createReactNativeTraceHeaders,
9
+ getActiveLogBrewTrace,
10
+ shouldPropagateTraceparent
11
+ } = require("./index.cjs");
12
+ const { createAppStateLifecycleSpanListener } = require("./lifecycle.cjs");
13
+ const { createSafeReactNativeMetadata } = require("./metadata.cjs");
14
+ const {
15
+ clearLogBrewNativeBridgeScope,
16
+ syncLogBrewNativeBridgeScope,
17
+ withLogBrewNativeBridgeScope
18
+ } = require("./native-bridge.cjs");
19
+ const { createReactNativeResourceFetch } = require("./resource-fetch.cjs");
20
+
21
+ function createLogBrewReactNativeInstrumentation(client, {
22
+ appState,
23
+ captureInitialLifecycleState = false,
24
+ captureInitialNavigationRoute = false,
25
+ fetchImpl,
26
+ globalObject = globalThis,
27
+ includeRouteKey = false,
28
+ instrumentGlobalFetch = false,
29
+ instrumentGlobalXMLHttpRequest = false,
30
+ logger,
31
+ measureFetchResponseBodySize = false,
32
+ measureXhrResponseBodySize = false,
33
+ metadata = {},
34
+ metadataFactory,
35
+ nativeBridge,
36
+ navigation,
37
+ navigationContainer,
38
+ now = () => new Date().toISOString(),
39
+ nowMs = () => Date.now(),
40
+ onError,
41
+ platform,
42
+ randomValues,
43
+ routeTemplate,
44
+ routeTemplateFactory,
45
+ screen,
46
+ sessionId,
47
+ trace,
48
+ traceFlags = "01",
49
+ tracePropagationTargets = []
50
+ } = {}) {
51
+ requireClient(client);
52
+ const activeTrace = resolveInstrumentationTrace({ randomValues, trace, traceFlags });
53
+ const removers = [];
54
+ const resolvedNavigation = navigationContainer ?? navigation;
55
+
56
+ if (appState?.addEventListener) {
57
+ removers.push(createAppStateLifecycleSpanListener(client, appState, {
58
+ captureInitialState: captureInitialLifecycleState,
59
+ metadata,
60
+ now,
61
+ nowMs,
62
+ onError,
63
+ platform,
64
+ screen,
65
+ sessionId,
66
+ trace: activeTrace
67
+ }));
68
+ }
69
+
70
+ if (resolvedNavigation) {
71
+ removers.push(createReactNavigationSpanListener(client, resolvedNavigation, {
72
+ appState,
73
+ captureInitialRoute: captureInitialNavigationRoute,
74
+ includeRouteKey,
75
+ metadata,
76
+ now,
77
+ nowMs,
78
+ onError,
79
+ platform,
80
+ trace: activeTrace
81
+ }));
82
+ }
83
+
84
+ if (nativeBridge) {
85
+ syncLogBrewNativeBridgeScope(nativeBridge, {
86
+ logger,
87
+ metadata,
88
+ screen,
89
+ sessionId,
90
+ source: "react-native.instrumentation",
91
+ trace: activeTrace
92
+ });
93
+ }
94
+
95
+ const resourceFetch = createReactNativeResourceFetch(client, {
96
+ appState,
97
+ fetchImpl,
98
+ measureResponseBodySize: measureFetchResponseBodySize,
99
+ metadata,
100
+ metadataFactory,
101
+ now,
102
+ nowMs,
103
+ platform,
104
+ randomValues,
105
+ routeTemplate,
106
+ routeTemplateFactory,
107
+ screen,
108
+ sessionId,
109
+ trace: activeTrace,
110
+ traceFlags,
111
+ tracePropagationTargets
112
+ });
113
+ let globalFetch;
114
+ let globalXMLHttpRequest;
115
+ try {
116
+ globalFetch = instrumentGlobalFetch ? installGlobalFetchInstrumentation(client, {
117
+ appState,
118
+ globalObject,
119
+ measureFetchResponseBodySize,
120
+ metadata,
121
+ metadataFactory,
122
+ now,
123
+ nowMs,
124
+ platform,
125
+ randomValues,
126
+ routeTemplate,
127
+ routeTemplateFactory,
128
+ screen,
129
+ sessionId,
130
+ trace: activeTrace,
131
+ traceFlags,
132
+ tracePropagationTargets
133
+ }) : undefined;
134
+ if (globalFetch) {
135
+ removers.push(globalFetch.remove);
136
+ }
137
+ globalXMLHttpRequest = instrumentGlobalXMLHttpRequest ? installGlobalXMLHttpRequestInstrumentation(client, {
138
+ appState,
139
+ globalObject,
140
+ measureXhrResponseBodySize,
141
+ metadata,
142
+ metadataFactory,
143
+ now,
144
+ nowMs,
145
+ platform,
146
+ routeTemplate,
147
+ routeTemplateFactory,
148
+ screen,
149
+ sessionId,
150
+ trace: activeTrace,
151
+ tracePropagationTargets
152
+ }) : undefined;
153
+ if (globalXMLHttpRequest) {
154
+ removers.push(globalXMLHttpRequest.remove);
155
+ }
156
+ } catch (error) {
157
+ removeConfiguredInstrumentation({ nativeBridge, removers });
158
+ throw error;
159
+ }
160
+
161
+ let removed = false;
162
+ const remove = () => {
163
+ if (removed) {
164
+ return;
165
+ }
166
+ removed = true;
167
+ removeConfiguredInstrumentation({ nativeBridge, removers });
168
+ };
169
+
170
+ const handle = {
171
+ globalFetch,
172
+ globalXMLHttpRequest,
173
+ remove,
174
+ resourceFetch,
175
+ stop: remove,
176
+ syncNativeBridgeScope(options = {}) {
177
+ if (!nativeBridge) {
178
+ return undefined;
179
+ }
180
+ return syncLogBrewNativeBridgeScope(nativeBridge, {
181
+ logger,
182
+ metadata,
183
+ screen,
184
+ sessionId,
185
+ source: "react-native.instrumentation",
186
+ trace: activeTrace,
187
+ ...options
188
+ });
189
+ },
190
+ trace: activeTrace,
191
+ withNativeBridgeScope(callbackOrOptions, maybeCallback) {
192
+ if (!nativeBridge) {
193
+ throw new SdkError("configuration_error", "withNativeBridgeScope requires nativeBridge");
194
+ }
195
+ if (typeof callbackOrOptions === "function") {
196
+ return withLogBrewNativeBridgeScope(nativeBridge, {
197
+ logger,
198
+ metadata,
199
+ screen,
200
+ sessionId,
201
+ source: "react-native.instrumentation",
202
+ trace: activeTrace
203
+ }, callbackOrOptions);
204
+ }
205
+ return withLogBrewNativeBridgeScope(nativeBridge, {
206
+ logger,
207
+ metadata,
208
+ screen,
209
+ sessionId,
210
+ source: "react-native.instrumentation",
211
+ trace: activeTrace,
212
+ ...(callbackOrOptions ?? {})
213
+ }, maybeCallback);
214
+ }
215
+ };
216
+ return Object.freeze(handle);
217
+ }
218
+
219
+ function resolveInstrumentationTrace({ randomValues, trace, traceFlags }) {
220
+ if (typeof trace === "string") {
221
+ return createReactNativeTraceContext({ randomValues, traceFlags, traceparent: trace });
222
+ }
223
+ return trace ?? getActiveLogBrewTrace() ?? createReactNativeTraceContext({ randomValues, traceFlags });
224
+ }
225
+
226
+ function requireClient(client) {
227
+ if (!client) {
228
+ throw new SdkError("configuration_error", "createLogBrewReactNativeInstrumentation requires a client");
229
+ }
230
+ }
231
+
232
+ function removeConfiguredInstrumentation({ nativeBridge, removers }) {
233
+ if (nativeBridge) {
234
+ clearLogBrewNativeBridgeScope(nativeBridge);
235
+ }
236
+ for (const removeListener of removers.splice(0).reverse()) {
237
+ removeListener();
238
+ }
239
+ }
240
+
241
+ function installGlobalFetchInstrumentation(client, {
242
+ appState,
243
+ globalObject,
244
+ measureFetchResponseBodySize,
245
+ metadata,
246
+ metadataFactory,
247
+ now,
248
+ nowMs,
249
+ platform,
250
+ randomValues,
251
+ routeTemplate,
252
+ routeTemplateFactory,
253
+ screen,
254
+ sessionId,
255
+ trace,
256
+ traceFlags,
257
+ tracePropagationTargets
258
+ }) {
259
+ if ((typeof globalObject !== "object" && typeof globalObject !== "function") || globalObject === null) {
260
+ throw new SdkError("configuration_error", "instrumentGlobalFetch requires a globalObject");
261
+ }
262
+ const originalFetch = globalObject.fetch;
263
+ if (typeof originalFetch !== "function") {
264
+ throw new SdkError("configuration_error", "instrumentGlobalFetch requires globalObject.fetch");
265
+ }
266
+ const globalResourceFetch = createReactNativeResourceFetch(client, {
267
+ appState,
268
+ fetchImpl: (input, init) => originalFetch.call(globalObject, input, init),
269
+ measureResponseBodySize: measureFetchResponseBodySize,
270
+ metadata,
271
+ metadataFactory,
272
+ now,
273
+ nowMs,
274
+ platform,
275
+ randomValues,
276
+ routeTemplate,
277
+ routeTemplateFactory,
278
+ screen,
279
+ sessionId,
280
+ trace,
281
+ traceFlags,
282
+ tracePropagationTargets
283
+ });
284
+ let removed = false;
285
+ const wrappedFetch = (input, init) => globalResourceFetch(input, init);
286
+ try {
287
+ globalObject.fetch = wrappedFetch;
288
+ } catch {
289
+ throw new SdkError("configuration_error", "instrumentGlobalFetch could not patch globalObject.fetch");
290
+ }
291
+ if (globalObject.fetch !== wrappedFetch) {
292
+ throw new SdkError("configuration_error", "instrumentGlobalFetch could not patch globalObject.fetch");
293
+ }
294
+ const remove = () => {
295
+ if (removed) {
296
+ return;
297
+ }
298
+ removed = true;
299
+ if (globalObject.fetch === wrappedFetch) {
300
+ globalObject.fetch = originalFetch;
301
+ }
302
+ };
303
+ return Object.freeze({
304
+ fetch: wrappedFetch,
305
+ remove,
306
+ stop: remove
307
+ });
308
+ }
309
+
310
+ /* eslint-disable no-invalid-this */
311
+ function installGlobalXMLHttpRequestInstrumentation(client, {
312
+ appState,
313
+ globalObject,
314
+ measureXhrResponseBodySize,
315
+ metadata,
316
+ metadataFactory,
317
+ now,
318
+ nowMs,
319
+ platform,
320
+ routeTemplate,
321
+ routeTemplateFactory,
322
+ screen,
323
+ sessionId,
324
+ trace,
325
+ tracePropagationTargets
326
+ }) {
327
+ if ((typeof globalObject !== "object" && typeof globalObject !== "function") || globalObject === null) {
328
+ throw new SdkError("configuration_error", "instrumentGlobalXMLHttpRequest requires a globalObject");
329
+ }
330
+ const xhrType = globalObject.XMLHttpRequest;
331
+ if (typeof xhrType !== "function" || !xhrType.prototype) {
332
+ throw new SdkError("configuration_error", "instrumentGlobalXMLHttpRequest requires globalObject.XMLHttpRequest");
333
+ }
334
+ const prototype = xhrType.prototype;
335
+ const originalOpen = prototype.open;
336
+ const originalSend = prototype.send;
337
+ const originalSetRequestHeader = prototype.setRequestHeader;
338
+ if (typeof originalOpen !== "function" || typeof originalSend !== "function" || typeof originalSetRequestHeader !== "function") {
339
+ throw new SdkError("configuration_error", "instrumentGlobalXMLHttpRequest requires open, send, and setRequestHeader");
340
+ }
341
+
342
+ const contextKey = Symbol("logbrew.xhr");
343
+ const headersReceivedState = typeof xhrType.HEADERS_RECEIVED === "number" ? xhrType.HEADERS_RECEIVED : 2;
344
+ const doneState = typeof xhrType.DONE === "number" ? xhrType.DONE : 4;
345
+ const safeRouteTemplateFactory = routeTemplateFactory ?? defaultXhrRouteTemplateFactory;
346
+
347
+ function wrappedOpen(method, url) {
348
+ this[contextKey] = {
349
+ method: normalizeXhrMethod(method),
350
+ reported: false,
351
+ url: xhrUrl(url)
352
+ };
353
+ return originalOpen.apply(this, arguments);
354
+ }
355
+
356
+ function wrappedSend(body) {
357
+ const context = this[contextKey];
358
+ if (!context) {
359
+ return originalSend.apply(this, arguments);
360
+ }
361
+ context.body = xhrRequestBody(body);
362
+ context.startedAtMs = nowMs();
363
+ context.timestamp = now();
364
+ installXhrReadyStateTracker(this, context, {
365
+ appState,
366
+ client,
367
+ doneState,
368
+ headersReceivedState,
369
+ measureXhrResponseBodySize,
370
+ metadata,
371
+ metadataFactory,
372
+ nowMs,
373
+ platform,
374
+ routeTemplate,
375
+ routeTemplateFactory: safeRouteTemplateFactory,
376
+ screen,
377
+ sessionId,
378
+ trace
379
+ });
380
+ if (shouldPropagateTraceparent(context.url, tracePropagationTargets)) {
381
+ originalSetRequestHeader.call(this, "traceparent", createReactNativeTraceHeaders(trace).traceparent);
382
+ }
383
+ try {
384
+ return originalSend.apply(this, arguments);
385
+ } catch (error) {
386
+ captureXhrResourceSpan(this, context, {
387
+ appState,
388
+ client,
389
+ metadata: {
390
+ ...metadata,
391
+ xhrErrorName: errorName(error),
392
+ xhrErrorValueType: typeof error
393
+ },
394
+ metadataFactory,
395
+ nowMs,
396
+ platform,
397
+ routeTemplate,
398
+ routeTemplateFactory: safeRouteTemplateFactory,
399
+ screen,
400
+ sessionId,
401
+ status: "error",
402
+ trace
403
+ });
404
+ throw error;
405
+ }
406
+ }
407
+
408
+ prototype.open = wrappedOpen;
409
+ prototype.send = wrappedSend;
410
+ if (prototype.open !== wrappedOpen || prototype.send !== wrappedSend) {
411
+ throw new SdkError("configuration_error", "instrumentGlobalXMLHttpRequest could not patch XMLHttpRequest");
412
+ }
413
+
414
+ let removed = false;
415
+ const remove = () => {
416
+ if (removed) {
417
+ return;
418
+ }
419
+ removed = true;
420
+ if (prototype.open === wrappedOpen) {
421
+ prototype.open = originalOpen;
422
+ }
423
+ if (prototype.send === wrappedSend) {
424
+ prototype.send = originalSend;
425
+ }
426
+ };
427
+
428
+ return Object.freeze({
429
+ remove,
430
+ stop: remove
431
+ });
432
+ }
433
+
434
+ function installXhrReadyStateTracker(xhr, context, options) {
435
+ const onReadyStateChange = () => {
436
+ if (xhr.readyState === options.headersReceivedState && context.responseStartAtMs === undefined) {
437
+ context.responseStartAtMs = options.nowMs();
438
+ return;
439
+ }
440
+ if (xhr.readyState === options.doneState && !context.reported) {
441
+ captureXhrResourceSpan(xhr, context, options);
442
+ }
443
+ };
444
+ if (typeof xhr.addEventListener === "function") {
445
+ xhr.addEventListener("readystatechange", onReadyStateChange);
446
+ return;
447
+ }
448
+ const originalOnReadyStateChange = xhr.onreadystatechange;
449
+ xhr.onreadystatechange = function logBrewOnReadyStateChange() {
450
+ onReadyStateChange();
451
+ if (typeof originalOnReadyStateChange === "function") {
452
+ return originalOnReadyStateChange.apply(this, arguments);
453
+ }
454
+ return undefined;
455
+ };
456
+ }
457
+
458
+ function captureXhrResourceSpan(xhr, context, {
459
+ appState,
460
+ client,
461
+ measureXhrResponseBodySize,
462
+ metadata,
463
+ metadataFactory,
464
+ nowMs,
465
+ platform,
466
+ routeTemplate,
467
+ routeTemplateFactory,
468
+ screen,
469
+ sessionId,
470
+ status,
471
+ trace
472
+ }) {
473
+ if (context.reported) {
474
+ return;
475
+ }
476
+ context.reported = true;
477
+ const durationMs = elapsedMs(context.startedAtMs, nowMs);
478
+ const responseStartDurationMs = context.responseStartAtMs === undefined
479
+ ? undefined
480
+ : elapsedMs(context.startedAtMs, () => context.responseStartAtMs);
481
+ const responseSizeBytes = xhrResponseSizeBytes(xhr, { measureXhrResponseBodySize });
482
+ const safeRouteTemplate = routeTemplate ?? routeTemplateFactory({ url: context.url });
483
+ const statusCode = xhrStatusCode(xhr);
484
+ captureReactNativeResourceSpan(client, {
485
+ appState,
486
+ durationMs,
487
+ metadata: {
488
+ ...createSafeReactNativeMetadata(metadata, metadataFactory, {
489
+ durationMs,
490
+ init: context.body === undefined ? undefined : { body: context.body },
491
+ input: context.url,
492
+ method: context.method,
493
+ routeTemplate: safeRouteTemplate,
494
+ status: status ?? undefined,
495
+ statusCode,
496
+ url: context.url
497
+ }),
498
+ responseStartDurationMs,
499
+ transport: "xhr"
500
+ },
501
+ method: context.method,
502
+ platform,
503
+ routeTemplate: safeRouteTemplate,
504
+ screen,
505
+ responseSizeBytes,
506
+ sessionId,
507
+ status: status ?? undefined,
508
+ statusCode,
509
+ timestamp: context.timestamp,
510
+ trace
511
+ });
512
+ }
513
+
514
+ function normalizeXhrMethod(method) {
515
+ return String(method ?? "GET").toUpperCase();
516
+ }
517
+
518
+ function xhrUrl(url) {
519
+ if (typeof url === "string") {
520
+ return url;
521
+ }
522
+ try {
523
+ return url.toString();
524
+ } catch {
525
+ return String(url);
526
+ }
527
+ }
528
+
529
+ function xhrRequestBody(body) {
530
+ if (typeof body === "string") {
531
+ return body;
532
+ }
533
+ if (body instanceof String) {
534
+ return body.toString();
535
+ }
536
+ return undefined;
537
+ }
538
+
539
+ function defaultXhrRouteTemplateFactory({ url }) {
540
+ const URLConstructor = globalThis.URL;
541
+ if (typeof URLConstructor === "function") {
542
+ try {
543
+ const parsedUrl = new URLConstructor(url, "https://logbrew.local");
544
+ return parsedUrl.pathname;
545
+ } catch {
546
+ // Fall back to query/hash stripping below for non-standard request keys.
547
+ }
548
+ }
549
+ return String(url).split(/[?#]/u, 1)[0];
550
+ }
551
+
552
+ function elapsedMs(startedAtMs, nowMs) {
553
+ const durationMs = nowMs() - startedAtMs;
554
+ return Number.isFinite(durationMs) ? Math.max(0, durationMs) : undefined;
555
+ }
556
+
557
+ function xhrStatusCode(xhr) {
558
+ return typeof xhr?.status === "number" && Number.isFinite(xhr.status) ? xhr.status : undefined;
559
+ }
560
+
561
+ function xhrResponseSizeBytes(xhr, { measureXhrResponseBodySize = false } = {}) {
562
+ if (typeof xhr?.getResponseHeader !== "function") {
563
+ return measureXhrResponseBodySize ? xhrResponseBodySizeBytes(xhr) : undefined;
564
+ }
565
+ const contentLength = Number.parseInt(String(xhr.getResponseHeader("Content-Length") ?? ""), 10);
566
+ if (Number.isFinite(contentLength) && contentLength >= 0) {
567
+ return contentLength;
568
+ }
569
+ return measureXhrResponseBodySize ? xhrResponseBodySizeBytes(xhr) : undefined;
570
+ }
571
+
572
+ function xhrResponseBodySizeBytes(xhr) {
573
+ const responseType = readXhrResponseValue(xhr, "responseType");
574
+ if (responseType === undefined || responseType === "" || responseType === "text") {
575
+ const responseText = readXhrResponseValue(xhr, "responseText");
576
+ if (typeof responseText === "string" || responseText instanceof String) {
577
+ return utf8ByteLength(responseText.toString());
578
+ }
579
+ }
580
+ const response = readXhrResponseValue(xhr, "response");
581
+ if (typeof response === "string" || response instanceof String) {
582
+ return utf8ByteLength(response.toString());
583
+ }
584
+ if (typeof ArrayBuffer === "function") {
585
+ if (response instanceof ArrayBuffer) {
586
+ return response.byteLength;
587
+ }
588
+ if (typeof ArrayBuffer.isView === "function" && ArrayBuffer.isView(response)) {
589
+ return response.byteLength;
590
+ }
591
+ }
592
+ if (typeof response?.size === "number" && Number.isFinite(response.size) && response.size >= 0) {
593
+ return response.size;
594
+ }
595
+ return undefined;
596
+ }
597
+
598
+ function readXhrResponseValue(xhr, property) {
599
+ try {
600
+ return xhr?.[property];
601
+ } catch {
602
+ return undefined;
603
+ }
604
+ }
605
+
606
+ function utf8ByteLength(value) {
607
+ let bytes = 0;
608
+ for (let index = 0; index < value.length; index += 1) {
609
+ const code = value.charCodeAt(index);
610
+ if (code <= 0x7f) {
611
+ bytes += 1;
612
+ } else if (code <= 0x7ff) {
613
+ bytes += 2;
614
+ } else if (code >= 0xd800 && code <= 0xdbff && index + 1 < value.length) {
615
+ const next = value.charCodeAt(index + 1);
616
+ if (next >= 0xdc00 && next <= 0xdfff) {
617
+ bytes += 4;
618
+ index += 1;
619
+ } else {
620
+ bytes += 3;
621
+ }
622
+ } else {
623
+ bytes += 3;
624
+ }
625
+ }
626
+ return bytes;
627
+ }
628
+
629
+ function errorName(error) {
630
+ return typeof error?.name === "string" && error.name.trim() !== "" ? error.name : "Error";
631
+ }
632
+ /* eslint-enable no-invalid-this */
633
+
634
+ module.exports = {
635
+ createLogBrewReactNativeInstrumentation,
636
+ default: {
637
+ createLogBrewReactNativeInstrumentation
638
+ }
639
+ };