crumbtrail-react-native 0.2.0

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.cjs ADDED
@@ -0,0 +1,799 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ CrumbtrailReactNativeErrorBoundary: () => CrumbtrailReactNativeErrorBoundary,
24
+ CrumbtrailReactNativeProvider: () => CrumbtrailReactNativeProvider,
25
+ REACT_NATIVE_CAPABILITY_BITS: () => REACT_NATIVE_CAPABILITY_BITS,
26
+ createReactNativeCrumbtrail: () => createReactNativeCrumbtrail,
27
+ createReactNativeCrumbtrailAsync: () => createReactNativeCrumbtrailAsync,
28
+ createReactNativeReplayLite: () => createReactNativeReplayLite,
29
+ createReactNativeSessionStore: () => createReactNativeSessionStore,
30
+ createReactNativeTargetDescriptor: () => createReactNativeTargetDescriptor,
31
+ detectReactNativeCapabilities: () => detectReactNativeCapabilities,
32
+ redactReactNativeSnapshot: () => redactReactNativeSnapshot,
33
+ startReactNativeCollectors: () => startReactNativeCollectors,
34
+ useBugState: () => useBugState,
35
+ useCrumbtrailReactNative: () => useCrumbtrailReactNative
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+
39
+ // src/capabilities.ts
40
+ var REACT_NATIVE_CAPABILITY_BITS = {
41
+ asyncStorage: 1 << 0,
42
+ navigation: 1 << 1,
43
+ viewShot: 1 << 2
44
+ };
45
+ var OPTIONAL_MODULES = [
46
+ {
47
+ key: "asyncStorage",
48
+ packageName: "@react-native-async-storage/async-storage",
49
+ capability: "async-storage",
50
+ bit: REACT_NATIVE_CAPABILITY_BITS.asyncStorage
51
+ },
52
+ {
53
+ key: "navigation",
54
+ packageName: "@react-navigation/native",
55
+ capability: "navigation",
56
+ bit: REACT_NATIVE_CAPABILITY_BITS.navigation
57
+ },
58
+ {
59
+ key: "viewShot",
60
+ packageName: "react-native-view-shot",
61
+ capability: "view-snapshot",
62
+ bit: REACT_NATIVE_CAPABILITY_BITS.viewShot
63
+ }
64
+ ];
65
+ function detectReactNativeCapabilities(options = {}) {
66
+ const resolver = options.resolver ?? safeRequireOptionalModule;
67
+ const modules = {};
68
+ const capabilities = [];
69
+ let bitset = 0;
70
+ for (const optionalModule of OPTIONAL_MODULES) {
71
+ const present = isModulePresent(optionalModule.packageName, resolver);
72
+ modules[optionalModule.key] = {
73
+ packageName: optionalModule.packageName,
74
+ present,
75
+ status: present ? "present" : "absent"
76
+ };
77
+ if (present) {
78
+ bitset |= optionalModule.bit;
79
+ capabilities.push(optionalModule.capability);
80
+ }
81
+ }
82
+ return { bitset, capabilities, modules };
83
+ }
84
+ function isModulePresent(packageName, resolver) {
85
+ try {
86
+ return resolver(packageName) !== void 0;
87
+ } catch {
88
+ return false;
89
+ }
90
+ }
91
+ function safeRequireOptionalModule(packageName) {
92
+ try {
93
+ const requireFn = getRequire();
94
+ return requireFn ? requireFn(packageName) : void 0;
95
+ } catch {
96
+ return void 0;
97
+ }
98
+ }
99
+ function getRequire() {
100
+ try {
101
+ const maybeRequire = Function(
102
+ 'return typeof require === "function" ? require : undefined'
103
+ )();
104
+ return typeof maybeRequire === "function" ? maybeRequire : void 0;
105
+ } catch {
106
+ return void 0;
107
+ }
108
+ }
109
+
110
+ // src/session-store.ts
111
+ var import_crumbtrail_core = require("crumbtrail-core");
112
+ function createReactNativeSessionStore(storage, key = import_crumbtrail_core.DEFAULT_SESSION_STORAGE_KEY) {
113
+ if (!storage) return void 0;
114
+ let cached;
115
+ return {
116
+ read() {
117
+ return cached;
118
+ },
119
+ write(session) {
120
+ cached = session;
121
+ try {
122
+ const result = storage.setItem(key, JSON.stringify(session));
123
+ if (result && typeof result.catch === "function") {
124
+ void result.catch(() => {
125
+ });
126
+ }
127
+ } catch {
128
+ }
129
+ },
130
+ async hydrate() {
131
+ try {
132
+ const raw = await storage.getItem(key);
133
+ cached = parsePersistedSession(raw);
134
+ return cached;
135
+ } catch {
136
+ return void 0;
137
+ }
138
+ }
139
+ };
140
+ }
141
+ function parsePersistedSession(raw) {
142
+ if (!raw) return void 0;
143
+ try {
144
+ const parsed = JSON.parse(raw);
145
+ if (typeof parsed.id !== "string" || parsed.id.length === 0)
146
+ return void 0;
147
+ return {
148
+ id: parsed.id,
149
+ lastActivity: typeof parsed.lastActivity === "number" ? parsed.lastActivity : 0
150
+ };
151
+ } catch {
152
+ return void 0;
153
+ }
154
+ }
155
+
156
+ // src/logger.ts
157
+ var import_crumbtrail_core2 = require("crumbtrail-core");
158
+
159
+ // src/target-descriptor.ts
160
+ function createReactNativeTargetDescriptor(input) {
161
+ const descriptor = {
162
+ role: input.role,
163
+ label: input.label ?? input.accessibilityLabel,
164
+ testID: input.testID ?? input.testId,
165
+ accessibilityId: input.accessibilityId,
166
+ componentName: input.componentName,
167
+ routePath: input.routePath,
168
+ ancestryHash: input.ancestryHash,
169
+ bounds: input.bounds,
170
+ redaction: input.redaction
171
+ };
172
+ const hasIdentity = Boolean(
173
+ descriptor.role || descriptor.label || descriptor.testID || descriptor.accessibilityId || descriptor.componentName || descriptor.routePath || descriptor.ancestryHash
174
+ );
175
+ return hasIdentity ? descriptor : void 0;
176
+ }
177
+
178
+ // src/replay-lite.ts
179
+ function createReactNativeReplayLite(options) {
180
+ const emit2 = (type, data, target) => {
181
+ options.logger.addEvent({
182
+ type,
183
+ data,
184
+ platform: "react-native",
185
+ sdk: { name: "crumbtrail-react-native" },
186
+ capabilities: options.capabilities,
187
+ ...target ? { target } : {}
188
+ });
189
+ };
190
+ return {
191
+ recordViewSnapshot(snapshot) {
192
+ emit2("view-snapshot", {
193
+ kind: "component-tree",
194
+ routePath: snapshot.routePath,
195
+ root: sanitizeNode(snapshot.root)
196
+ });
197
+ },
198
+ recordTouch(overlay) {
199
+ const target = overlay.target ? createReactNativeTargetDescriptor(
200
+ overlay.target
201
+ ) : void 0;
202
+ emit2(
203
+ "touch",
204
+ {
205
+ kind: "overlay",
206
+ x: overlay.x,
207
+ y: overlay.y,
208
+ phase: overlay.phase ?? "press"
209
+ },
210
+ target
211
+ );
212
+ },
213
+ async captureCrashScreenshot(target) {
214
+ const capture = target !== void 0 && options.viewShot?.captureRef ? options.viewShot.captureRef(target, { format: "jpg", quality: 0.7 }) : options.viewShot?.captureScreen?.({ format: "jpg", quality: 0.7 });
215
+ if (!capture) return void 0;
216
+ try {
217
+ const uri = await capture;
218
+ emit2("view-snapshot", {
219
+ kind: "crash-screenshot",
220
+ uri,
221
+ capture: "react-native-view-shot"
222
+ });
223
+ return uri;
224
+ } catch {
225
+ return void 0;
226
+ }
227
+ }
228
+ };
229
+ }
230
+ function sanitizeNode(node) {
231
+ return {
232
+ id: node.id,
233
+ componentName: node.componentName,
234
+ role: node.role,
235
+ label: node.label,
236
+ testID: node.testID,
237
+ accessibilityId: node.accessibilityId,
238
+ bounds: node.bounds,
239
+ children: node.children?.map(sanitizeNode)
240
+ };
241
+ }
242
+
243
+ // src/collectors.ts
244
+ var DEFAULT_COLLECTORS = {
245
+ console: true,
246
+ errors: true,
247
+ network: true,
248
+ appState: true,
249
+ environment: true,
250
+ navigation: true,
251
+ replayLite: true
252
+ };
253
+ function startReactNativeCollectors(logger, options) {
254
+ const enabled = resolveCollectorConfig(options.config);
255
+ const globalObject = options.globalObject ?? globalThis;
256
+ const reactNative = options.reactNative ?? resolveModule("react-native", options.resolver);
257
+ const cleanup = [];
258
+ if (enabled.console)
259
+ cleanup.push(
260
+ startConsoleCollector(logger, options.capabilities, globalObject)
261
+ );
262
+ if (enabled.errors)
263
+ cleanup.push(
264
+ startErrorCollector(
265
+ logger,
266
+ options.capabilities,
267
+ globalObject,
268
+ options.errorUtils
269
+ )
270
+ );
271
+ if (enabled.network)
272
+ cleanup.push(
273
+ startNetworkCollector(logger, options.capabilities, globalObject)
274
+ );
275
+ if (enabled.appState)
276
+ cleanup.push(
277
+ startAppStateCollector(
278
+ logger,
279
+ options.capabilities,
280
+ reactNative?.AppState
281
+ )
282
+ );
283
+ if (enabled.environment)
284
+ startEnvironmentCollector(logger, options.capabilities, reactNative);
285
+ if (enabled.navigation)
286
+ cleanup.push(
287
+ startNavigationCollector(
288
+ logger,
289
+ options.capabilities,
290
+ options.navigation
291
+ )
292
+ );
293
+ const viewShot = resolveModule(
294
+ "react-native-view-shot",
295
+ options.resolver
296
+ );
297
+ const replayLite = enabled.replayLite ? createReactNativeReplayLite({
298
+ logger,
299
+ capabilities: options.capabilities.capabilities,
300
+ viewShot
301
+ }) : void 0;
302
+ return {
303
+ cleanup() {
304
+ for (const stop of cleanup.splice(0).reverse()) stop();
305
+ },
306
+ replayLite
307
+ };
308
+ }
309
+ function resolveCollectorConfig(config) {
310
+ if (config === false) {
311
+ return Object.fromEntries(
312
+ Object.keys(DEFAULT_COLLECTORS).map((key) => [key, false])
313
+ );
314
+ }
315
+ if (config === true || config === void 0) return { ...DEFAULT_COLLECTORS };
316
+ return { ...DEFAULT_COLLECTORS, ...config };
317
+ }
318
+ function emit(logger, capabilities, type, data, target) {
319
+ logger.addEvent({
320
+ type,
321
+ data,
322
+ platform: "react-native",
323
+ sdk: { name: "crumbtrail-react-native" },
324
+ capabilities: capabilities.capabilities,
325
+ ...target ? { target } : {}
326
+ });
327
+ }
328
+ function startConsoleCollector(logger, capabilities, globalObject) {
329
+ const consoleObject = globalObject.console;
330
+ if (!consoleObject) return () => {
331
+ };
332
+ const mutableConsole = consoleObject;
333
+ const methods = ["log", "warn", "error", "debug", "info"];
334
+ const originals = /* @__PURE__ */ new Map();
335
+ const level = {
336
+ log: "log",
337
+ warn: "warn",
338
+ error: "err",
339
+ debug: "dbg",
340
+ info: "info"
341
+ };
342
+ for (const method of methods) {
343
+ if (typeof mutableConsole[method] !== "function") continue;
344
+ const original = mutableConsole[method].bind(consoleObject);
345
+ originals.set(method, original);
346
+ mutableConsole[method] = (...args) => {
347
+ emit(logger, capabilities, "con", {
348
+ lv: level[method],
349
+ args: args.map((arg) => safeStringify(arg))
350
+ });
351
+ original(...args);
352
+ };
353
+ }
354
+ return () => {
355
+ for (const [method, original] of originals) {
356
+ mutableConsole[method] = original;
357
+ }
358
+ };
359
+ }
360
+ function startErrorCollector(logger, capabilities, globalObject, suppliedErrorUtils) {
361
+ const cleanup = [];
362
+ const errorUtils = suppliedErrorUtils ?? globalObject.ErrorUtils;
363
+ if (errorUtils?.setGlobalHandler && errorUtils.getGlobalHandler) {
364
+ const previous = errorUtils.getGlobalHandler();
365
+ if (typeof previous !== "function") return () => {
366
+ };
367
+ errorUtils.setGlobalHandler((error, isFatal) => {
368
+ emit(logger, capabilities, "err", {
369
+ msg: error instanceof Error ? error.message : String(error),
370
+ stk: error instanceof Error ? error.stack : void 0,
371
+ fatal: Boolean(isFatal),
372
+ source: "react-native-global-handler"
373
+ });
374
+ previous?.(error, isFatal);
375
+ });
376
+ cleanup.push(() => {
377
+ if (previous) errorUtils.setGlobalHandler?.(previous);
378
+ });
379
+ }
380
+ const addEventListener = globalObject.addEventListener;
381
+ const removeEventListener = globalObject.removeEventListener;
382
+ if (addEventListener && removeEventListener) {
383
+ const onUnhandledRejection = (event) => {
384
+ const reason = event.reason;
385
+ emit(logger, capabilities, "rej", {
386
+ msg: reason instanceof Error ? reason.message : String(reason),
387
+ stk: reason instanceof Error ? reason.stack : void 0,
388
+ source: "react-native-unhandled-rejection"
389
+ });
390
+ };
391
+ addEventListener.call(
392
+ globalObject,
393
+ "unhandledrejection",
394
+ onUnhandledRejection
395
+ );
396
+ cleanup.push(
397
+ () => removeEventListener.call(
398
+ globalObject,
399
+ "unhandledrejection",
400
+ onUnhandledRejection
401
+ )
402
+ );
403
+ }
404
+ return () => {
405
+ for (const stop of cleanup.reverse()) stop();
406
+ };
407
+ }
408
+ function startNetworkCollector(logger, capabilities, globalObject) {
409
+ const cleanup = [];
410
+ const originalFetch = globalObject.fetch;
411
+ if (typeof originalFetch === "function") {
412
+ globalObject.fetch = (async (input, init) => {
413
+ const startedAt = Date.now();
414
+ const method = init?.method?.toUpperCase() ?? "GET";
415
+ const url = extractUrl(input);
416
+ try {
417
+ const response = await originalFetch(input, init);
418
+ emit(logger, capabilities, "net", {
419
+ url,
420
+ method,
421
+ status: response.status,
422
+ ok: response.ok,
423
+ dur: Date.now() - startedAt,
424
+ source: "fetch"
425
+ });
426
+ return response;
427
+ } catch (error) {
428
+ emit(logger, capabilities, "net", {
429
+ url,
430
+ method,
431
+ error: error instanceof Error ? error.message : String(error),
432
+ dur: Date.now() - startedAt,
433
+ source: "fetch"
434
+ });
435
+ throw error;
436
+ }
437
+ });
438
+ cleanup.push(() => {
439
+ globalObject.fetch = originalFetch;
440
+ });
441
+ }
442
+ const Xhr = globalObject.XMLHttpRequest;
443
+ if (Xhr?.prototype) {
444
+ const originalOpen = Xhr.prototype.open;
445
+ const originalSend = Xhr.prototype.send;
446
+ if (typeof originalOpen === "function" && typeof originalSend === "function") {
447
+ Xhr.prototype.open = function open(method, url, ...rest) {
448
+ this.__crumbtrailNetwork = { method, url };
449
+ return originalOpen.call(this, method, url, ...rest);
450
+ };
451
+ Xhr.prototype.send = function send(body) {
452
+ const startedAt = Date.now();
453
+ const info = this.__crumbtrailNetwork;
454
+ const previous = this.onreadystatechange;
455
+ this.onreadystatechange = () => {
456
+ if (this.readyState === 4) {
457
+ emit(logger, capabilities, "net", {
458
+ url: info?.url,
459
+ method: info?.method?.toUpperCase(),
460
+ status: this.status,
461
+ dur: Date.now() - startedAt,
462
+ source: "xmlhttprequest"
463
+ });
464
+ }
465
+ previous?.call(this);
466
+ };
467
+ return originalSend.call(this, body);
468
+ };
469
+ cleanup.push(() => {
470
+ Xhr.prototype.open = originalOpen;
471
+ Xhr.prototype.send = originalSend;
472
+ });
473
+ }
474
+ }
475
+ return () => {
476
+ for (const stop of cleanup.reverse()) stop();
477
+ };
478
+ }
479
+ function startAppStateCollector(logger, capabilities, appState) {
480
+ if (!appState?.addEventListener) return () => {
481
+ };
482
+ const subscription = appState.addEventListener("change", (state) => {
483
+ emit(logger, capabilities, "app-lifecycle", { state, source: "AppState" });
484
+ });
485
+ emit(logger, capabilities, "app-lifecycle", {
486
+ state: appState.currentState,
487
+ source: "AppState",
488
+ kind: "initial"
489
+ });
490
+ return toCleanup(subscription);
491
+ }
492
+ function startEnvironmentCollector(logger, capabilities, reactNative) {
493
+ const window = reactNative?.Dimensions?.get?.("window");
494
+ emit(logger, capabilities, "env", {
495
+ kind: "snapshot",
496
+ platform: {
497
+ os: reactNative?.Platform?.OS,
498
+ version: reactNative?.Platform?.Version,
499
+ constants: reactNative?.Platform?.constants
500
+ },
501
+ viewport: window ? {
502
+ w: window.width,
503
+ h: window.height,
504
+ scale: window.scale,
505
+ fontScale: window.fontScale
506
+ } : void 0
507
+ });
508
+ }
509
+ function startNavigationCollector(logger, capabilities, navigation) {
510
+ if (!navigation?.addListener) return () => {
511
+ };
512
+ let previousRouteKey;
513
+ const emitCurrentRoute = () => {
514
+ const route = navigation.getCurrentRoute?.();
515
+ if (!route || route.key === previousRouteKey) return;
516
+ previousRouteKey = route.key;
517
+ emit(logger, capabilities, "navigation", {
518
+ name: route.name,
519
+ path: route.path,
520
+ key: route.key
521
+ });
522
+ };
523
+ const subscription = navigation.addListener("state", emitCurrentRoute);
524
+ emitCurrentRoute();
525
+ return toCleanup(subscription);
526
+ }
527
+ function resolveModule(packageName, resolver) {
528
+ if (!resolver) return void 0;
529
+ try {
530
+ return resolver(packageName);
531
+ } catch {
532
+ return void 0;
533
+ }
534
+ }
535
+ function extractUrl(input) {
536
+ if (typeof input === "string") return input;
537
+ if (input instanceof URL) return input.toString();
538
+ if (typeof Request !== "undefined" && input instanceof Request)
539
+ return input.url;
540
+ return String(input);
541
+ }
542
+ function safeStringify(value) {
543
+ if (typeof value === "string") return value;
544
+ try {
545
+ return JSON.stringify(value);
546
+ } catch {
547
+ return String(value);
548
+ }
549
+ }
550
+ function toCleanup(subscription) {
551
+ if (typeof subscription === "function") return subscription;
552
+ if (subscription?.remove) return () => subscription.remove?.();
553
+ return () => {
554
+ };
555
+ }
556
+
557
+ // src/logger.ts
558
+ var REACT_NATIVE_DEFAULT_CONFIG = {
559
+ network: false,
560
+ interactions: false,
561
+ keystrokes: false,
562
+ scroll: false,
563
+ visibility: false,
564
+ clipboard: false,
565
+ cookies: false,
566
+ storage: false,
567
+ performance: false,
568
+ video: false,
569
+ audio: false,
570
+ widget: false,
571
+ sessionPersistence: "session"
572
+ };
573
+ function createReactNativeCrumbtrail(options = {}) {
574
+ const capabilities = detectReactNativeCapabilities({
575
+ resolver: options.resolver
576
+ });
577
+ const userConfig = options.config;
578
+ const sessionStore = options.asyncStorage ? createReactNativeSessionStore(options.asyncStorage) : userConfig?.sessionStore;
579
+ const config = {
580
+ ...REACT_NATIVE_DEFAULT_CONFIG,
581
+ ...userConfig,
582
+ ...sessionStore ? { sessionStore } : {}
583
+ };
584
+ const logger = import_crumbtrail_core2.Crumbtrail.init(config);
585
+ if (options.reportCapabilities !== false) {
586
+ logger.addEvent({
587
+ type: "rn.capabilities",
588
+ data: {
589
+ bitset: capabilities.bitset,
590
+ capabilities: capabilities.capabilities,
591
+ modules: capabilities.modules
592
+ },
593
+ platform: "react-native",
594
+ sdk: { name: "crumbtrail-react-native" },
595
+ capabilities: capabilities.capabilities
596
+ });
597
+ }
598
+ const collectors = startReactNativeCollectors(logger, {
599
+ config: options.collectors,
600
+ capabilities,
601
+ resolver: options.resolver,
602
+ globalObject: options.globalObject,
603
+ reactNative: options.reactNative,
604
+ navigation: options.navigation,
605
+ errorUtils: options.errorUtils
606
+ });
607
+ wrapStopWithCollectorCleanup(logger, collectors);
608
+ return { logger, capabilities, collectors };
609
+ }
610
+ async function createReactNativeCrumbtrailAsync(options = {}) {
611
+ const sessionStore = options.asyncStorage ? createReactNativeSessionStore(options.asyncStorage) : void 0;
612
+ await sessionStore?.hydrate();
613
+ return createReactNativeCrumbtrail({
614
+ ...options,
615
+ asyncStorage: void 0,
616
+ config: {
617
+ ...options.config,
618
+ ...sessionStore ? { sessionStore } : {}
619
+ }
620
+ });
621
+ }
622
+ function wrapStopWithCollectorCleanup(logger, collectors) {
623
+ const stop = logger.stop.bind(logger);
624
+ let cleaned = false;
625
+ logger.stop = async () => {
626
+ if (!cleaned) {
627
+ cleaned = true;
628
+ collectors.cleanup();
629
+ }
630
+ return stop();
631
+ };
632
+ }
633
+
634
+ // src/provider.ts
635
+ var import_react = require("react");
636
+ var CrumbtrailReactNativeContext = (0, import_react.createContext)(void 0);
637
+ function CrumbtrailReactNativeProvider(props) {
638
+ const [value, setValue] = (0, import_react.useState)(() => {
639
+ if (!props.logger && props.asyncStorage) return void 0;
640
+ return props.logger ? {
641
+ logger: props.logger,
642
+ capabilities: detectReactNativeCapabilities({
643
+ resolver: props.resolver
644
+ })
645
+ } : createReactNativeCrumbtrail({
646
+ config: props.config,
647
+ resolver: props.resolver,
648
+ reportCapabilities: props.reportCapabilities
649
+ });
650
+ });
651
+ (0, import_react.useEffect)(() => {
652
+ let active = true;
653
+ if (props.logger) {
654
+ setValue({
655
+ logger: props.logger,
656
+ capabilities: detectReactNativeCapabilities({
657
+ resolver: props.resolver
658
+ })
659
+ });
660
+ return;
661
+ }
662
+ if (!props.asyncStorage) return;
663
+ let hydratedLogger;
664
+ createReactNativeCrumbtrailAsync({
665
+ config: props.config,
666
+ asyncStorage: props.asyncStorage,
667
+ resolver: props.resolver,
668
+ reportCapabilities: props.reportCapabilities
669
+ }).then((result) => {
670
+ if (!active) {
671
+ void result.logger.stop();
672
+ return;
673
+ }
674
+ hydratedLogger = result.logger;
675
+ setValue(result);
676
+ }).catch(() => {
677
+ });
678
+ return () => {
679
+ active = false;
680
+ if (hydratedLogger) void hydratedLogger.stop();
681
+ };
682
+ }, [
683
+ props.asyncStorage,
684
+ props.config,
685
+ props.logger,
686
+ props.reportCapabilities,
687
+ props.resolver
688
+ ]);
689
+ if (!value) return props.fallback ?? null;
690
+ return (0, import_react.createElement)(
691
+ CrumbtrailReactNativeContext.Provider,
692
+ { value },
693
+ props.children
694
+ );
695
+ }
696
+ function useCrumbtrailReactNative() {
697
+ const value = (0, import_react.useContext)(CrumbtrailReactNativeContext);
698
+ if (!value) {
699
+ throw new Error(
700
+ "useCrumbtrailReactNative must be used within CrumbtrailReactNativeProvider"
701
+ );
702
+ }
703
+ return value;
704
+ }
705
+
706
+ // src/use-bug-state.ts
707
+ var import_react2 = require("react");
708
+ var REDACTED_VALUE = "[REDACTED]";
709
+ var SENSITIVE_NAME_RE = /(^|[^a-z0-9])(access[-_]?token|api[-_]?key|auth|authorization|bearer|card[-_]?number|client[-_]?secret|cookie|credential(s)?|creds|csrf|cvv|cvc|id[-_]?token|jsessionid|jwt|mfa|otp|pass[-_]?phrase|pass(code|word)?|passwd|password[-_]?confirmation|pin|private[-_]?key|pwd|refresh[-_]?token|secret|security[-_]?code|session|session[-_]?id|sid|ssn|token|verification[-_]?code|xsrf)([^a-z0-9]|$)/i;
710
+ var TOKEN_RE = /\b(?:Bearer|Token|Basic)\s+[A-Za-z0-9._~+/=-]{8,}\b|\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b|(?:sk|pk|rk|ghp|gho|ghu|ghs|glpat|xox[baprs])[-_][A-Za-z0-9_.=-]{12,}|\b[A-Fa-f0-9]{32,}\b|\b[A-Za-z0-9_-]{40,}\b/gi;
711
+ function isSensitiveName(name) {
712
+ const normalized = name.replace(/([a-z])([A-Z])/g, "$1_$2");
713
+ return SENSITIVE_NAME_RE.test(name) || SENSITIVE_NAME_RE.test(normalized);
714
+ }
715
+ function redactReactNativeSnapshot(value, keyName) {
716
+ if (keyName && isSensitiveName(keyName)) return REDACTED_VALUE;
717
+ if (typeof value === "string") {
718
+ if (/^\s*[^:=\n\r]{1,120}\s*[:=]/.test(value)) {
719
+ return value.replace(
720
+ /^(\s*[^:=\n\r]{1,120}\s*[:=]).*$/s,
721
+ (_match, key) => {
722
+ return isSensitiveName(key) ? `${key}${REDACTED_VALUE}` : value.replace(TOKEN_RE, REDACTED_VALUE);
723
+ }
724
+ );
725
+ }
726
+ return value.replace(TOKEN_RE, REDACTED_VALUE);
727
+ }
728
+ if (Array.isArray(value))
729
+ return value.map((entry) => redactReactNativeSnapshot(entry));
730
+ if (value && typeof value === "object") {
731
+ const output = {};
732
+ for (const [key, entry] of Object.entries(
733
+ value
734
+ )) {
735
+ output[key] = redactReactNativeSnapshot(entry, key);
736
+ }
737
+ return output;
738
+ }
739
+ return value;
740
+ }
741
+ function useBugState(logger, name, value, options = {}) {
742
+ const valueRef = (0, import_react2.useRef)(value);
743
+ valueRef.current = value;
744
+ (0, import_react2.useEffect)(() => {
745
+ if (!logger || typeof logger.registerStateProvider !== "function") return;
746
+ return logger.registerStateProvider(
747
+ name,
748
+ () => options.captureRawState ? valueRef.current : redactReactNativeSnapshot(valueRef.current)
749
+ );
750
+ }, [logger, name, options.captureRawState]);
751
+ }
752
+
753
+ // src/error-boundary.ts
754
+ var import_react3 = require("react");
755
+ var CrumbtrailReactNativeErrorBoundary = class extends import_react3.Component {
756
+ constructor(props) {
757
+ super(props);
758
+ this.state = { hasError: false };
759
+ }
760
+ static getDerivedStateFromError(_error) {
761
+ return { hasError: true };
762
+ }
763
+ componentDidCatch(error, errorInfo) {
764
+ this.props.logger.addEvent({
765
+ type: "err",
766
+ data: {
767
+ msg: redactReactNativeSnapshot(error.message),
768
+ stack: redactReactNativeSnapshot(error.stack),
769
+ componentStack: redactReactNativeSnapshot(errorInfo.componentStack),
770
+ source: "react-native-error-boundary"
771
+ }
772
+ });
773
+ }
774
+ resetError() {
775
+ this.setState({ hasError: false });
776
+ }
777
+ render() {
778
+ if (this.state.hasError) {
779
+ return this.props.fallback ?? null;
780
+ }
781
+ return this.props.children;
782
+ }
783
+ };
784
+ // Annotate the CommonJS export names for ESM import in node:
785
+ 0 && (module.exports = {
786
+ CrumbtrailReactNativeErrorBoundary,
787
+ CrumbtrailReactNativeProvider,
788
+ REACT_NATIVE_CAPABILITY_BITS,
789
+ createReactNativeCrumbtrail,
790
+ createReactNativeCrumbtrailAsync,
791
+ createReactNativeReplayLite,
792
+ createReactNativeSessionStore,
793
+ createReactNativeTargetDescriptor,
794
+ detectReactNativeCapabilities,
795
+ redactReactNativeSnapshot,
796
+ startReactNativeCollectors,
797
+ useBugState,
798
+ useCrumbtrailReactNative
799
+ });