@allstak/react-native 0.3.4 → 0.4.1

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.mjs CHANGED
@@ -29,6 +29,58 @@ var HttpTransport = class {
29
29
  this.enqueueOrDispatch({ path, payload });
30
30
  return Promise.resolve();
31
31
  }
32
+ /**
33
+ * One-shot POST that resolves with the parsed JSON response body. Used
34
+ * by `captureException` to retrieve the server-assigned event id so
35
+ * follow-up attachment uploads can be linked.
36
+ *
37
+ * Fail-open: returns `null` on any error (network, non-2xx, parse).
38
+ * Respects {@link timeoutMs} via `AbortController`. Bounded retries.
39
+ */
40
+ async sendAndRead(path, payload, options = {}) {
41
+ if (!this.enabled) return null;
42
+ const timeoutMs = options.timeoutMs ?? 5e3;
43
+ const retries = Math.max(0, options.retries ?? 1);
44
+ let attempt = 0;
45
+ let lastError = null;
46
+ while (attempt <= retries) {
47
+ try {
48
+ const url = `${this.baseUrl}${path}`;
49
+ const controller = new AbortController();
50
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
51
+ try {
52
+ const res = await fetch(url, {
53
+ method: "POST",
54
+ headers: {
55
+ "Content-Type": "application/json",
56
+ "X-AllStak-Key": this.apiKey
57
+ },
58
+ body: JSON.stringify(payload),
59
+ signal: controller.signal
60
+ });
61
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
62
+ const text = await res.text();
63
+ if (!text) return null;
64
+ try {
65
+ return JSON.parse(text);
66
+ } catch {
67
+ return null;
68
+ }
69
+ } finally {
70
+ clearTimeout(timeoutId);
71
+ }
72
+ } catch (err) {
73
+ lastError = err;
74
+ attempt += 1;
75
+ if (attempt > retries) break;
76
+ await new Promise((r) => setTimeout(r, 200 * attempt));
77
+ }
78
+ }
79
+ if (lastError && typeof __DEV__ !== "undefined" && __DEV__) {
80
+ console.warn("[AllStak] sendAndRead failed:", lastError?.message);
81
+ }
82
+ return null;
83
+ }
32
84
  enqueueOrDispatch(item) {
33
85
  if (Date.now() < this.circuitOpenUntil) {
34
86
  this.push(item);
@@ -1383,10 +1435,358 @@ function unbindHttpInstrumentation() {
1383
1435
  _currentRuntime = null;
1384
1436
  }
1385
1437
 
1438
+ // src/runtime.ts
1439
+ var cached = null;
1440
+ function detectRuntimeMode() {
1441
+ if (cached) return cached;
1442
+ cached = computeRuntimeMode();
1443
+ return cached;
1444
+ }
1445
+ function __resetRuntimeModeForTest() {
1446
+ cached = null;
1447
+ }
1448
+ function computeRuntimeMode() {
1449
+ try {
1450
+ const Constants = require("expo-constants");
1451
+ const appOwnership = Constants?.default?.appOwnership ?? Constants?.appOwnership;
1452
+ if (appOwnership === "expo") return "expo-go";
1453
+ if (appOwnership === "standalone" || appOwnership === "guest") return "expo-dev-client";
1454
+ const exec = Constants?.default?.executionEnvironment ?? Constants?.executionEnvironment;
1455
+ if (exec === "storeClient") return "expo-go";
1456
+ if (exec === "standalone" || exec === "bare") return "expo-dev-client";
1457
+ } catch {
1458
+ }
1459
+ try {
1460
+ require("expo");
1461
+ return "expo-dev-client";
1462
+ } catch {
1463
+ }
1464
+ try {
1465
+ require("react-native");
1466
+ return "rn-cli";
1467
+ } catch {
1468
+ }
1469
+ return "unknown";
1470
+ }
1471
+ function runtimeAllowsScreenshot(mode = detectRuntimeMode()) {
1472
+ if (mode === "expo-go") return false;
1473
+ return true;
1474
+ }
1475
+ function tryRequire(id2) {
1476
+ try {
1477
+ return require(id2);
1478
+ } catch {
1479
+ return null;
1480
+ }
1481
+ }
1482
+
1483
+ // src/privacy.tsx
1484
+ import * as React from "react";
1485
+ function getRN() {
1486
+ return tryRequire("react-native");
1487
+ }
1488
+ var state = {
1489
+ isCapturing: false,
1490
+ sensitiveRefs: /* @__PURE__ */ new Set()
1491
+ };
1492
+ var listeners = /* @__PURE__ */ new Set();
1493
+ function __setCapturing(value) {
1494
+ if (state.isCapturing === value) return;
1495
+ state.isCapturing = value;
1496
+ for (const fn of listeners) {
1497
+ try {
1498
+ fn(value);
1499
+ } catch {
1500
+ }
1501
+ }
1502
+ }
1503
+ function __resetPrivacyStateForTest() {
1504
+ state.isCapturing = false;
1505
+ state.sensitiveRefs.clear();
1506
+ listeners.clear();
1507
+ }
1508
+ function isCapturingScreenshot() {
1509
+ return state.isCapturing;
1510
+ }
1511
+ function sensitiveRefCount() {
1512
+ return state.sensitiveRefs.size;
1513
+ }
1514
+ function registerSensitiveRef(ref) {
1515
+ state.sensitiveRefs.add(ref);
1516
+ return () => {
1517
+ state.sensitiveRefs.delete(ref);
1518
+ };
1519
+ }
1520
+ function useIsCapturing() {
1521
+ const [val, setVal] = React.useState(state.isCapturing);
1522
+ React.useEffect(() => {
1523
+ const fn = (v) => setVal(v);
1524
+ listeners.add(fn);
1525
+ return () => {
1526
+ listeners.delete(fn);
1527
+ };
1528
+ }, []);
1529
+ return val;
1530
+ }
1531
+ function useAllStakPrivacy() {
1532
+ const isCapturing = useIsCapturing();
1533
+ return { isCapturing, registerSensitiveRef };
1534
+ }
1535
+ function getView() {
1536
+ const RN = getRN();
1537
+ return RN?.View ?? ((props) => React.createElement(React.Fragment, null, props.children));
1538
+ }
1539
+ function getText() {
1540
+ const RN = getRN();
1541
+ return RN?.Text ?? ((props) => React.createElement(React.Fragment, null, props.children));
1542
+ }
1543
+ function getTextInput() {
1544
+ const RN = getRN();
1545
+ return RN?.TextInput ?? ((props) => React.createElement(React.Fragment, null, null));
1546
+ }
1547
+ var DEFAULT_MASK_COLOR = "#d8dde7";
1548
+ var DEFAULT_MASK_LABEL = "\u2022\u2022\u2022\u2022\u2022\u2022";
1549
+ function AllStakMaskedView({
1550
+ children,
1551
+ maskLabel = DEFAULT_MASK_LABEL,
1552
+ maskColor = DEFAULT_MASK_COLOR,
1553
+ hideScreenshot = false,
1554
+ privacy = "mask",
1555
+ style,
1556
+ ...rest
1557
+ }) {
1558
+ const isCapturing = useIsCapturing();
1559
+ const View = getView();
1560
+ const Text = getText();
1561
+ if (!isCapturing || privacy === "show") {
1562
+ return React.createElement(View, { style, ...rest }, children);
1563
+ }
1564
+ if (privacy === "hide" || hideScreenshot) {
1565
+ return React.createElement(View, { style: [{ backgroundColor: maskColor }, style], ...rest });
1566
+ }
1567
+ return React.createElement(
1568
+ View,
1569
+ { style: [{ backgroundColor: maskColor, alignItems: "center", justifyContent: "center" }, style], ...rest },
1570
+ React.createElement(Text, { style: { color: "#3f4652", fontSize: 12 } }, maskLabel)
1571
+ );
1572
+ }
1573
+ function AllStakPrivacyView(props) {
1574
+ return React.createElement(AllStakMaskedView, { hideScreenshot: true, ...props });
1575
+ }
1576
+ function AllStakTextInput({
1577
+ privacy = "mask",
1578
+ style,
1579
+ maskColor = DEFAULT_MASK_COLOR,
1580
+ ...rest
1581
+ }) {
1582
+ const isCapturing = useIsCapturing();
1583
+ const View = getView();
1584
+ const TextInput = getTextInput();
1585
+ if (isCapturing && privacy !== "show") {
1586
+ return React.createElement(View, {
1587
+ style: [{ minHeight: 40, backgroundColor: maskColor, borderRadius: 4 }, style]
1588
+ });
1589
+ }
1590
+ return React.createElement(TextInput, { style, ...rest });
1591
+ }
1592
+ function AllStakSensitiveText({
1593
+ children,
1594
+ privacy = "mask",
1595
+ style,
1596
+ maskLabel = DEFAULT_MASK_LABEL,
1597
+ ...rest
1598
+ }) {
1599
+ const isCapturing = useIsCapturing();
1600
+ const Text = getText();
1601
+ if (isCapturing && privacy !== "show") {
1602
+ return React.createElement(Text, { style, ...rest }, maskLabel);
1603
+ }
1604
+ return React.createElement(Text, { style, ...rest }, children);
1605
+ }
1606
+
1607
+ // src/screenshot.ts
1608
+ var DEFAULT_SCREENSHOT_CONFIG = {
1609
+ captureScreenshotOnError: false,
1610
+ screenshotRedaction: "strict",
1611
+ screenshotMaskStyle: "solid",
1612
+ screenshotMaxBytes: 5e5,
1613
+ screenshotQuality: 0.7,
1614
+ screenshotFormat: "jpg",
1615
+ screenshotSampleRate: 1,
1616
+ screenshotOnUnhandledOnly: true,
1617
+ screenshotUploadTimeoutMs: 8e3,
1618
+ screenshotCaptureTimeoutMs: 2e3,
1619
+ screenshotNativeMode: "auto",
1620
+ screenshotFailPolicy: "send-event-only"
1621
+ };
1622
+ function resolveScreenshotConfig(partial) {
1623
+ const c = { ...DEFAULT_SCREENSHOT_CONFIG, ...partial ?? {} };
1624
+ c.screenshotMaxBytes = clamp(c.screenshotMaxBytes, 1024, 5e6);
1625
+ c.screenshotQuality = clamp(c.screenshotQuality, 0, 1);
1626
+ c.screenshotSampleRate = clamp(c.screenshotSampleRate, 0, 1);
1627
+ c.screenshotUploadTimeoutMs = clamp(c.screenshotUploadTimeoutMs, 500, 6e4);
1628
+ c.screenshotCaptureTimeoutMs = clamp(c.screenshotCaptureTimeoutMs, 100, 3e4);
1629
+ return c;
1630
+ }
1631
+ function clamp(n, lo, hi) {
1632
+ if (typeof n !== "number" || !Number.isFinite(n)) return lo;
1633
+ return Math.max(lo, Math.min(hi, n));
1634
+ }
1635
+ var rootViewRef = null;
1636
+ function __setRootViewRef(ref) {
1637
+ rootViewRef = ref;
1638
+ }
1639
+ var warnedAboutBothApis = false;
1640
+ function warnIfBothApisPresent(callbackPresent, flatPresent) {
1641
+ if (!callbackPresent || !flatPresent || warnedAboutBothApis) return;
1642
+ warnedAboutBothApis = true;
1643
+ console.warn(
1644
+ "[AllStak] Both `screenshot.provider` (deprecated) and the flat `captureScreenshotOnError` API are configured. The flat API takes precedence. Remove `screenshot.provider` to silence this warning."
1645
+ );
1646
+ }
1647
+ function isViewShotAvailable() {
1648
+ return tryRequire("react-native-view-shot") !== null;
1649
+ }
1650
+ async function captureViaViewShot(config) {
1651
+ if (config.screenshotNativeMode === "disabled") return null;
1652
+ const viewShot = tryRequire("react-native-view-shot");
1653
+ if (!viewShot) return null;
1654
+ const captureRef = viewShot.captureRef ?? viewShot.default?.captureRef;
1655
+ if (typeof captureRef !== "function") return null;
1656
+ const refTarget = rootViewRef?.current;
1657
+ if (!refTarget) return null;
1658
+ const format = config.screenshotFormat === "jpg" ? "jpg" : config.screenshotFormat;
1659
+ const dimensions = readDimensions();
1660
+ __setCapturing(true);
1661
+ await new Promise((r) => setTimeout(r, 16));
1662
+ try {
1663
+ const captured = await Promise.race([
1664
+ Promise.resolve(captureRef(refTarget, {
1665
+ format,
1666
+ quality: config.screenshotQuality,
1667
+ result: "base64"
1668
+ })),
1669
+ new Promise((resolve) => setTimeout(() => resolve(null), config.screenshotCaptureTimeoutMs))
1670
+ ]);
1671
+ if (!captured || typeof captured !== "string") return null;
1672
+ const sizeBytes = estimateBase64Size(captured);
1673
+ if (sizeBytes > config.screenshotMaxBytes) {
1674
+ if (__DEV__) {
1675
+ console.warn(`[AllStak] Screenshot ${sizeBytes}B exceeds limit ${config.screenshotMaxBytes}B; dropping.`);
1676
+ }
1677
+ return null;
1678
+ }
1679
+ const contentType = format === "png" ? "image/png" : format === "webp" ? "image/webp" : "image/jpeg";
1680
+ return {
1681
+ dataBase64: captured,
1682
+ contentType,
1683
+ width: dimensions.width,
1684
+ height: dimensions.height,
1685
+ sizeBytes
1686
+ };
1687
+ } catch (err) {
1688
+ if (__DEV__) {
1689
+ console.warn("[AllStak] view-shot capture failed:", err?.message);
1690
+ }
1691
+ return null;
1692
+ } finally {
1693
+ __setCapturing(false);
1694
+ }
1695
+ }
1696
+ function estimateBase64Size(base64) {
1697
+ const padding = base64.endsWith("==") ? 2 : base64.endsWith("=") ? 1 : 0;
1698
+ return Math.floor(base64.length * 3 / 4) - padding;
1699
+ }
1700
+ function readDimensions() {
1701
+ try {
1702
+ const RN = tryRequire("react-native");
1703
+ const dims = RN?.Dimensions?.get?.("window");
1704
+ if (dims && typeof dims.width === "number" && typeof dims.height === "number") {
1705
+ return { width: Math.round(dims.width), height: Math.round(dims.height) };
1706
+ }
1707
+ } catch {
1708
+ }
1709
+ return { width: 0, height: 0 };
1710
+ }
1711
+ async function maybeCaptureScreenshot(config, ctx) {
1712
+ try {
1713
+ if (!config.captureScreenshotOnError) return null;
1714
+ if (config.screenshotOnUnhandledOnly && ctx.unhandled === false) return null;
1715
+ if (config.screenshotSampleRate < 1 && Math.random() >= config.screenshotSampleRate) return null;
1716
+ if (!runtimeAllowsScreenshot(ctx.runtimeMode)) return null;
1717
+ if (config.isScreenshotAllowed) {
1718
+ try {
1719
+ const allowed = await config.isScreenshotAllowed(ctx);
1720
+ if (!allowed) return null;
1721
+ } catch {
1722
+ return null;
1723
+ }
1724
+ }
1725
+ if (config.beforeScreenshotCapture) {
1726
+ try {
1727
+ const cont = await config.beforeScreenshotCapture(ctx);
1728
+ if (!cont) return null;
1729
+ } catch {
1730
+ return null;
1731
+ }
1732
+ }
1733
+ const upload = await captureViaViewShot(config);
1734
+ if (!upload) return null;
1735
+ const metadata = {
1736
+ captureMethod: "react-native-view-shot",
1737
+ redactionMode: config.screenshotRedaction,
1738
+ maskStyle: config.screenshotMaskStyle,
1739
+ format: config.screenshotFormat,
1740
+ width: upload.width,
1741
+ height: upload.height,
1742
+ sizeBytes: upload.sizeBytes,
1743
+ privacyComponentsDetected: sensitiveRefCount(),
1744
+ runtimeMode: ctx.runtimeMode
1745
+ };
1746
+ if (config.beforeScreenshotUpload) {
1747
+ try {
1748
+ const filtered = await config.beforeScreenshotUpload(upload, metadata);
1749
+ if (!filtered) return null;
1750
+ return { upload: filtered, metadata };
1751
+ } catch {
1752
+ return null;
1753
+ }
1754
+ }
1755
+ return { upload, metadata };
1756
+ } catch (err) {
1757
+ if (__DEV__) {
1758
+ console.warn("[AllStak] maybeCaptureScreenshot fail-open:", err?.message);
1759
+ }
1760
+ return null;
1761
+ }
1762
+ }
1763
+ function pickScreenshotConfig(source) {
1764
+ const out = {};
1765
+ const pick = (key) => {
1766
+ if (source[key] !== void 0) out[key] = source[key];
1767
+ };
1768
+ pick("captureScreenshotOnError");
1769
+ pick("screenshotRedaction");
1770
+ pick("screenshotMaskStyle");
1771
+ pick("screenshotMaxBytes");
1772
+ pick("screenshotQuality");
1773
+ pick("screenshotFormat");
1774
+ pick("screenshotSampleRate");
1775
+ pick("screenshotOnUnhandledOnly");
1776
+ pick("screenshotUploadTimeoutMs");
1777
+ pick("screenshotCaptureTimeoutMs");
1778
+ pick("screenshotNativeMode");
1779
+ pick("screenshotFailPolicy");
1780
+ pick("beforeScreenshotCapture");
1781
+ pick("beforeScreenshotUpload");
1782
+ pick("isScreenshotAllowed");
1783
+ return out;
1784
+ }
1785
+
1386
1786
  // src/client.ts
1387
1787
  var INGEST_HOST = "https://api.allstak.sa";
1388
1788
  var SDK_NAME = "allstak-react-native";
1389
- var SDK_VERSION = "0.3.4";
1789
+ var SDK_VERSION = "0.4.1";
1390
1790
  var ERRORS_PATH = "/ingest/v1/errors";
1391
1791
  var LOGS_PATH = "/ingest/v1/logs";
1392
1792
  var VALID_BREADCRUMB_TYPES = /* @__PURE__ */ new Set(["http", "log", "ui", "navigation", "query", "default"]);
@@ -1565,6 +1965,18 @@ var AllStakClient = class {
1565
1965
  breadcrumbs: currentBreadcrumbs,
1566
1966
  fingerprint: eff.fingerprint
1567
1967
  };
1968
+ const flatPresent = this.config.captureScreenshotOnError === true;
1969
+ const callbackPresent = Boolean(this.config.screenshot?.provider);
1970
+ warnIfBothApisPresent(callbackPresent, flatPresent);
1971
+ if (flatPresent) {
1972
+ void this.runFlatScreenshotPipeline(error, payload).catch(() => {
1973
+ void this.sendThroughBeforeSend({
1974
+ ...payload,
1975
+ metadata: { ...payload.metadata ?? {}, "screenshot.status": "failed" }
1976
+ });
1977
+ });
1978
+ return;
1979
+ }
1568
1980
  if (this.shouldCaptureScreenshot()) {
1569
1981
  void this.withScreenshotMetadata(error, payload).then((enriched) => this.sendThroughBeforeSend(enriched)).catch(() => this.sendThroughBeforeSend({
1570
1982
  ...payload,
@@ -1580,6 +1992,86 @@ var AllStakClient = class {
1580
1992
  }
1581
1993
  });
1582
1994
  }
1995
+ /**
1996
+ * Flat screenshot API path. Capture first (so masking primitives can
1997
+ * swap), send the event with a `screenshot.status` metadata tag, read
1998
+ * back the server-assigned errorId, then upload the attachment.
1999
+ *
2000
+ * Fail-open at every step.
2001
+ */
2002
+ async runFlatScreenshotPipeline(error, payload) {
2003
+ const sc = resolveScreenshotConfig(pickScreenshotConfig(this.config));
2004
+ const runtimeMode = detectRuntimeMode();
2005
+ const ctx = { error, unhandled: true, runtimeMode };
2006
+ let captured = null;
2007
+ try {
2008
+ captured = await maybeCaptureScreenshot(sc, ctx);
2009
+ } catch {
2010
+ captured = null;
2011
+ }
2012
+ const status = !sc.captureScreenshotOnError ? "disabled" : captured ? "captured" : runtimeMode === "expo-go" ? "unsupported_runtime" : "unavailable";
2013
+ const eventPayload = {
2014
+ ...payload,
2015
+ metadata: {
2016
+ ...payload.metadata ?? {},
2017
+ "screenshot.status": status,
2018
+ "screenshot.runtimeMode": runtimeMode,
2019
+ ...captured ? {
2020
+ "screenshot.contentType": captured.upload.contentType,
2021
+ "screenshot.width": captured.upload.width,
2022
+ "screenshot.height": captured.upload.height,
2023
+ "screenshot.sizeBytes": captured.upload.sizeBytes,
2024
+ "screenshot.redactionMode": captured.metadata.redactionMode,
2025
+ "screenshot.maskStyle": captured.metadata.maskStyle,
2026
+ "screenshot.captureMethod": captured.metadata.captureMethod
2027
+ } : {}
2028
+ }
2029
+ };
2030
+ let finalPayload = eventPayload;
2031
+ if (this.config.beforeSend) {
2032
+ try {
2033
+ finalPayload = await this.config.beforeSend(eventPayload);
2034
+ } catch {
2035
+ finalPayload = eventPayload;
2036
+ }
2037
+ }
2038
+ if (!finalPayload) return;
2039
+ let eventId = null;
2040
+ try {
2041
+ const resp = await this.transport.sendAndRead(
2042
+ ERRORS_PATH,
2043
+ finalPayload,
2044
+ { timeoutMs: 5e3, retries: 1 }
2045
+ );
2046
+ eventId = resp?.data?.id ?? resp?.id ?? null;
2047
+ } catch {
2048
+ }
2049
+ if (!captured || !eventId) return;
2050
+ try {
2051
+ await this.transport.sendAndRead(
2052
+ `/ingest/v1/errors/${encodeURIComponent(eventId)}/attachments`,
2053
+ {
2054
+ kind: "screenshot",
2055
+ contentType: captured.upload.contentType,
2056
+ dataBase64: captured.upload.dataBase64,
2057
+ width: captured.upload.width,
2058
+ height: captured.upload.height,
2059
+ redactionMode: captured.metadata.redactionMode,
2060
+ captureMethod: captured.metadata.captureMethod,
2061
+ sizeBytes: captured.upload.sizeBytes,
2062
+ metadata: {
2063
+ maskStyle: captured.metadata.maskStyle,
2064
+ format: captured.metadata.format,
2065
+ runtimeMode: captured.metadata.runtimeMode,
2066
+ privacyComponentsDetected: captured.metadata.privacyComponentsDetected ?? 0,
2067
+ sdkVersion: SDK_VERSION
2068
+ }
2069
+ },
2070
+ { timeoutMs: sc.screenshotUploadTimeoutMs, retries: 2 }
2071
+ );
2072
+ } catch {
2073
+ }
2074
+ }
1583
2075
  /** Start a new span. Auto-parented to any currently-active span. */
1584
2076
  startSpan(operation, options) {
1585
2077
  return this.tracing.startSpan(operation, options);
@@ -2104,7 +2596,7 @@ function byteSize(value) {
2104
2596
  }
2105
2597
 
2106
2598
  // src/provider.tsx
2107
- import * as React from "react";
2599
+ import * as React2 from "react";
2108
2600
 
2109
2601
  // src/auto-breadcrumbs.ts
2110
2602
  var FETCH_FLAG2 = "__allstak_fetch_patched__";
@@ -2341,17 +2833,17 @@ function tryAutoInstrumentNavigation() {
2341
2833
  const rnav = require("@react-navigation/native");
2342
2834
  if (!rnav || !rnav.NavigationContainer) return false;
2343
2835
  if (rnav[NAV_AUTO_PATCH_FLAG]) return true;
2344
- const React2 = require("react");
2345
- if (!React2 || typeof React2.forwardRef !== "function") return false;
2836
+ const React3 = require("react");
2837
+ if (!React3 || typeof React3.forwardRef !== "function") return false;
2346
2838
  const OrigContainer = rnav.NavigationContainer;
2347
- const Wrapped = React2.forwardRef(function AllStakNavigationContainer(props, userRef) {
2348
- const internalRef = React2.useRef(null);
2349
- const setRef = React2.useCallback((r) => {
2839
+ const Wrapped = React3.forwardRef(function AllStakNavigationContainer(props, userRef) {
2840
+ const internalRef = React3.useRef(null);
2841
+ const setRef = React3.useCallback((r) => {
2350
2842
  internalRef.current = r;
2351
2843
  if (typeof userRef === "function") userRef(r);
2352
2844
  else if (userRef) userRef.current = r;
2353
2845
  }, [userRef]);
2354
- React2.useEffect(() => {
2846
+ React3.useEffect(() => {
2355
2847
  if (internalRef.current) {
2356
2848
  try {
2357
2849
  instrumentReactNavigation(internalRef.current);
@@ -2359,7 +2851,7 @@ function tryAutoInstrumentNavigation() {
2359
2851
  }
2360
2852
  }
2361
2853
  }, []);
2362
- return React2.createElement(OrigContainer, { ...props, ref: setRef });
2854
+ return React3.createElement(OrigContainer, { ...props, ref: setRef });
2363
2855
  });
2364
2856
  Wrapped.displayName = "AllStakNavigationContainer";
2365
2857
  try {
@@ -2589,9 +3081,16 @@ function installReactNative(options = {}) {
2589
3081
  }
2590
3082
 
2591
3083
  // src/provider.tsx
2592
- var AllStakContext = React.createContext(null);
3084
+ function getRN2() {
3085
+ return tryRequire("react-native");
3086
+ }
3087
+ function getRootView() {
3088
+ const RN = getRN2();
3089
+ return RN?.View;
3090
+ }
3091
+ var AllStakContext = React2.createContext(null);
2593
3092
  var __providerOwnedInstance = null;
2594
- var AllStakErrorBoundary = class extends React.Component {
3093
+ var AllStakErrorBoundary = class extends React2.Component {
2595
3094
  constructor() {
2596
3095
  super(...arguments);
2597
3096
  this.state = { error: null };
@@ -2659,9 +3158,31 @@ function AllStakProvider({
2659
3158
  autoNetworkCapture,
2660
3159
  autoFetchBreadcrumbs,
2661
3160
  autoConsoleBreadcrumbs,
2662
- autoNavigationBreadcrumbs
3161
+ autoNavigationBreadcrumbs,
3162
+ captureScreenshotOnError,
3163
+ screenshotRedaction,
3164
+ screenshotMaskStyle,
3165
+ screenshotMaxBytes,
3166
+ screenshotQuality,
3167
+ screenshotFormat,
3168
+ screenshotSampleRate,
3169
+ screenshotOnUnhandledOnly,
3170
+ screenshotUploadTimeoutMs,
3171
+ screenshotCaptureTimeoutMs,
3172
+ screenshotNativeMode,
3173
+ screenshotFailPolicy,
3174
+ beforeScreenshotCapture,
3175
+ beforeScreenshotUpload,
3176
+ isScreenshotAllowed
2663
3177
  }) {
2664
- const clientRef = React.useRef(null);
3178
+ const clientRef = React2.useRef(null);
3179
+ const rootRef = React2.useRef(null);
3180
+ React2.useEffect(() => {
3181
+ __setRootViewRef(rootRef);
3182
+ return () => {
3183
+ __setRootViewRef(null);
3184
+ };
3185
+ }, []);
2665
3186
  if (!clientRef.current) {
2666
3187
  const existing = AllStak._getInstance();
2667
3188
  if (existing && __providerOwnedInstance === existing) {
@@ -2685,7 +3206,22 @@ function AllStakProvider({
2685
3206
  replay,
2686
3207
  tracesSampleRate,
2687
3208
  service,
2688
- dist
3209
+ dist,
3210
+ captureScreenshotOnError,
3211
+ screenshotRedaction,
3212
+ screenshotMaskStyle,
3213
+ screenshotMaxBytes,
3214
+ screenshotQuality,
3215
+ screenshotFormat,
3216
+ screenshotSampleRate,
3217
+ screenshotOnUnhandledOnly,
3218
+ screenshotUploadTimeoutMs,
3219
+ screenshotCaptureTimeoutMs,
3220
+ screenshotNativeMode,
3221
+ screenshotFailPolicy,
3222
+ beforeScreenshotCapture,
3223
+ beforeScreenshotUpload,
3224
+ isScreenshotAllowed
2689
3225
  };
2690
3226
  clientRef.current = AllStak.init(config);
2691
3227
  __providerOwnedInstance = clientRef.current;
@@ -2705,7 +3241,7 @@ function AllStakProvider({
2705
3241
  }
2706
3242
  }
2707
3243
  }
2708
- React.useEffect(() => {
3244
+ React2.useEffect(() => {
2709
3245
  return () => {
2710
3246
  if (destroyOnUnmount) {
2711
3247
  AllStak.destroy();
@@ -2715,10 +3251,17 @@ function AllStakProvider({
2715
3251
  }
2716
3252
  };
2717
3253
  }, [destroyOnUnmount, debug]);
2718
- return /* @__PURE__ */ React.createElement(AllStakContext.Provider, { value: clientRef.current }, /* @__PURE__ */ React.createElement(AllStakErrorBoundary, { fallback, onError, debug }, children));
3254
+ const boundary = /* @__PURE__ */ React2.createElement(AllStakErrorBoundary, { fallback, onError, debug }, children);
3255
+ const RootView = getRootView();
3256
+ const wrapped = RootView ? React2.createElement(
3257
+ RootView,
3258
+ { ref: rootRef, style: { flex: 1 }, collapsable: false },
3259
+ boundary
3260
+ ) : boundary;
3261
+ return /* @__PURE__ */ React2.createElement(AllStakContext.Provider, { value: clientRef.current }, wrapped);
2719
3262
  }
2720
3263
  function useAllStak() {
2721
- return React.useMemo(
3264
+ return React2.useMemo(
2722
3265
  () => ({
2723
3266
  captureException: (error, ctx) => AllStak.captureException(error, ctx),
2724
3267
  captureMessage: (msg, level = "info") => AllStak.captureMessage(msg, level),
@@ -2788,8 +3331,13 @@ export {
2788
3331
  ALWAYS_REDACT_QUERY,
2789
3332
  AllStak,
2790
3333
  AllStakClient,
3334
+ AllStakMaskedView,
3335
+ AllStakPrivacyView,
2791
3336
  AllStakProvider,
3337
+ AllStakSensitiveText,
3338
+ AllStakTextInput,
2792
3339
  DEFAULT_REDACT_BODY_FIELDS,
3340
+ DEFAULT_SCREENSHOT_CONFIG,
2793
3341
  HttpRequestModule,
2794
3342
  INGEST_HOST,
2795
3343
  REDACTED,
@@ -2800,18 +3348,30 @@ export {
2800
3348
  __devTriggerNativeCrash,
2801
3349
  __resetAutoNavigationFlagForTest,
2802
3350
  __resetConsoleInstrumentationFlagForTest,
3351
+ __resetPrivacyStateForTest,
2803
3352
  __resetProviderInstanceForTest,
3353
+ __resetRuntimeModeForTest,
2804
3354
  __setNativeModuleForTest,
2805
3355
  applyArchitectureTags,
2806
3356
  captureBodyResult,
3357
+ captureViaViewShot,
2807
3358
  detectArchitecture,
3359
+ detectRuntimeMode,
2808
3360
  drainPendingNativeCrashes,
2809
3361
  installReactNative,
2810
3362
  instrumentNavigationFromLinking,
2811
3363
  instrumentReactNavigation,
3364
+ isCapturingScreenshot,
3365
+ isViewShotAvailable,
3366
+ maybeCaptureScreenshot,
3367
+ pickScreenshotConfig,
2812
3368
  redactUrl,
3369
+ registerSensitiveRef,
3370
+ resolveScreenshotConfig,
3371
+ runtimeAllowsScreenshot,
2813
3372
  sanitizeHeaders,
2814
3373
  tryAutoInstrumentNavigation,
2815
- useAllStak
3374
+ useAllStak,
3375
+ useAllStakPrivacy
2816
3376
  };
2817
3377
  //# sourceMappingURL=index.mjs.map