@allstak/react-native 0.3.0 → 0.3.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
@@ -967,7 +967,7 @@ function installHttpInstrumentation(module, options, ownIngestHost) {
967
967
  // src/client.ts
968
968
  var INGEST_HOST = "https://api.allstak.sa";
969
969
  var SDK_NAME = "allstak-react-native";
970
- var SDK_VERSION = "0.3.0";
970
+ var SDK_VERSION = "0.3.1";
971
971
  var ERRORS_PATH = "/ingest/v1/errors";
972
972
  var LOGS_PATH = "/ingest/v1/logs";
973
973
  var VALID_BREADCRUMB_TYPES = /* @__PURE__ */ new Set(["http", "log", "ui", "navigation", "query", "default"]);
@@ -1448,6 +1448,9 @@ var AllStak = {
1448
1448
  }
1449
1449
  };
1450
1450
 
1451
+ // src/provider.tsx
1452
+ import * as React from "react";
1453
+
1451
1454
  // src/auto-breadcrumbs.ts
1452
1455
  var FETCH_FLAG2 = "__allstak_fetch_patched__";
1453
1456
  var CONSOLE_FLAG = "__allstak_console_patched__";
@@ -1494,36 +1497,92 @@ function instrumentFetch(addBreadcrumb, ownBaseUrl) {
1494
1497
  wrapped[FETCH_FLAG2] = true;
1495
1498
  g.fetch = wrapped;
1496
1499
  }
1497
- function instrumentConsole(addBreadcrumb) {
1500
+ var CONSOLE_DEFAULTS = {
1501
+ log: false,
1502
+ info: false,
1503
+ warn: true,
1504
+ error: true
1505
+ };
1506
+ var CONSOLE_METHOD_TO_LEVEL = {
1507
+ log: "info",
1508
+ info: "info",
1509
+ warn: "warn",
1510
+ error: "error"
1511
+ };
1512
+ var MAX_ARG_BYTES = 5e3;
1513
+ function instrumentConsole(addBreadcrumb, options = {}) {
1498
1514
  if (typeof console === "undefined") return;
1499
1515
  if (console[CONSOLE_FLAG]) return;
1500
- const origWarn = console.warn;
1501
- const origError = console.error;
1502
- console.warn = function(...args) {
1503
- try {
1504
- addBreadcrumb("log", args.map(safeString).join(" "), "warn");
1505
- } catch {
1506
- }
1507
- return origWarn.apply(console, args);
1516
+ const opts = {
1517
+ log: options.log ?? CONSOLE_DEFAULTS.log,
1518
+ info: options.info ?? CONSOLE_DEFAULTS.info,
1519
+ warn: options.warn ?? CONSOLE_DEFAULTS.warn,
1520
+ error: options.error ?? CONSOLE_DEFAULTS.error
1508
1521
  };
1509
- console.error = function(...args) {
1510
- try {
1511
- addBreadcrumb("log", args.map(safeString).join(" "), "error");
1512
- } catch {
1513
- }
1514
- return origError.apply(console, args);
1522
+ const wrap = (method) => {
1523
+ const orig = console[method];
1524
+ if (typeof orig !== "function") return;
1525
+ const level = CONSOLE_METHOD_TO_LEVEL[method];
1526
+ console[method] = function(...args) {
1527
+ if (opts[method]) {
1528
+ try {
1529
+ const serialized = args.map(safeStringifyArg);
1530
+ const message = truncate(serialized.join(" "));
1531
+ addBreadcrumb("log", message, level, {
1532
+ category: "console",
1533
+ method,
1534
+ args: serialized
1535
+ });
1536
+ } catch {
1537
+ }
1538
+ }
1539
+ return orig.apply(console, args);
1540
+ };
1515
1541
  };
1542
+ if (opts.log) wrap("log");
1543
+ if (opts.info) wrap("info");
1544
+ if (opts.warn) wrap("warn");
1545
+ if (opts.error) wrap("error");
1516
1546
  console[CONSOLE_FLAG] = true;
1517
1547
  }
1518
- function safeString(v) {
1519
- if (v == null) return String(v);
1548
+ function __resetConsoleInstrumentationFlagForTest() {
1549
+ if (typeof console !== "undefined") {
1550
+ delete console[CONSOLE_FLAG];
1551
+ }
1552
+ }
1553
+ function safeStringifyArg(v) {
1554
+ if (v === null || v === void 0) return String(v);
1520
1555
  if (typeof v === "string") return v;
1521
- if (v instanceof Error) return `${v.name}: ${v.message}`;
1522
- try {
1523
- return typeof v === "object" ? JSON.stringify(v) : String(v);
1524
- } catch {
1525
- return Object.prototype.toString.call(v);
1556
+ if (typeof v === "number" || typeof v === "boolean" || typeof v === "bigint") return String(v);
1557
+ if (typeof v === "symbol") return v.toString();
1558
+ if (typeof v === "function") return `[Function${v.name ? ` ${v.name}` : ""}]`;
1559
+ if (v instanceof Error) {
1560
+ return `${v.name || "Error"}: ${v.message}${v.stack ? `
1561
+ ${v.stack}` : ""}`;
1562
+ }
1563
+ if (typeof v === "object") {
1564
+ try {
1565
+ const seen = /* @__PURE__ */ new WeakSet();
1566
+ const out = JSON.stringify(v, (_key, val) => {
1567
+ if (typeof val === "object" && val !== null) {
1568
+ if (seen.has(val)) return "[Circular]";
1569
+ seen.add(val);
1570
+ }
1571
+ if (typeof val === "bigint") return val.toString();
1572
+ if (typeof val === "function") return `[Function${val.name ? ` ${val.name}` : ""}]`;
1573
+ if (typeof val === "symbol") return val.toString();
1574
+ return val;
1575
+ });
1576
+ return out ?? Object.prototype.toString.call(v);
1577
+ } catch {
1578
+ return Object.prototype.toString.call(v);
1579
+ }
1526
1580
  }
1581
+ return String(v);
1582
+ }
1583
+ function truncate(s) {
1584
+ if (s.length <= MAX_ARG_BYTES) return s;
1585
+ return s.slice(0, MAX_ARG_BYTES) + "\u2026[truncated]";
1527
1586
  }
1528
1587
 
1529
1588
  // src/architecture.ts
@@ -1551,6 +1610,7 @@ function applyArchitectureTags(setTag) {
1551
1610
  // src/navigation.ts
1552
1611
  var NAV_FLAG = /* @__PURE__ */ Symbol.for("allstak.nav.subscribed");
1553
1612
  var LINKING_FLAG = "__allstak_linking_patched__";
1613
+ var NAV_AUTO_PATCH_FLAG = /* @__PURE__ */ Symbol.for("allstak.nav.autoPatched");
1554
1614
  function instrumentReactNavigation(navigationRef, options = {}) {
1555
1615
  if (!navigationRef || typeof navigationRef.addListener !== "function") {
1556
1616
  return () => {
@@ -1602,7 +1662,7 @@ function instrumentReactNavigation(navigationRef, options = {}) {
1602
1662
  }
1603
1663
  function instrumentNavigationFromLinking() {
1604
1664
  try {
1605
- const rn = __require("react-native");
1665
+ const rn = require("react-native");
1606
1666
  const Linking = rn?.Linking;
1607
1667
  if (!Linking || typeof Linking.addEventListener !== "function") return;
1608
1668
  if (Linking[LINKING_FLAG]) return;
@@ -1618,8 +1678,59 @@ function instrumentNavigationFromLinking() {
1618
1678
  } catch {
1619
1679
  }
1620
1680
  }
1681
+ function tryAutoInstrumentNavigation() {
1682
+ const g = globalThis;
1683
+ const isMetro = typeof g.__METRO_GLOBAL_PREFIX__ !== "undefined" || typeof g.__r === "function" || typeof g.HermesInternal !== "undefined";
1684
+ if (isMetro) return false;
1685
+ try {
1686
+ const rnav = require("@react-navigation/native");
1687
+ if (!rnav || !rnav.NavigationContainer) return false;
1688
+ if (rnav[NAV_AUTO_PATCH_FLAG]) return true;
1689
+ const React2 = require("react");
1690
+ if (!React2 || typeof React2.forwardRef !== "function") return false;
1691
+ const OrigContainer = rnav.NavigationContainer;
1692
+ const Wrapped = React2.forwardRef(function AllStakNavigationContainer(props, userRef) {
1693
+ const internalRef = React2.useRef(null);
1694
+ const setRef = React2.useCallback((r) => {
1695
+ internalRef.current = r;
1696
+ if (typeof userRef === "function") userRef(r);
1697
+ else if (userRef) userRef.current = r;
1698
+ }, [userRef]);
1699
+ React2.useEffect(() => {
1700
+ if (internalRef.current) {
1701
+ try {
1702
+ instrumentReactNavigation(internalRef.current);
1703
+ } catch {
1704
+ }
1705
+ }
1706
+ }, []);
1707
+ return React2.createElement(OrigContainer, { ...props, ref: setRef });
1708
+ });
1709
+ Wrapped.displayName = "AllStakNavigationContainer";
1710
+ try {
1711
+ Object.defineProperty(rnav, "NavigationContainer", {
1712
+ value: Wrapped,
1713
+ configurable: true,
1714
+ writable: true
1715
+ });
1716
+ rnav[NAV_AUTO_PATCH_FLAG] = true;
1717
+ return true;
1718
+ } catch {
1719
+ return false;
1720
+ }
1721
+ } catch {
1722
+ return false;
1723
+ }
1724
+ }
1725
+ function __resetAutoNavigationFlagForTest() {
1726
+ try {
1727
+ const rnav = require("@react-navigation/native");
1728
+ if (rnav) delete rnav[NAV_AUTO_PATCH_FLAG];
1729
+ } catch {
1730
+ }
1731
+ }
1621
1732
 
1622
- // src/index.ts
1733
+ // src/install.ts
1623
1734
  function instrumentXmlHttpRequest() {
1624
1735
  const flag = "__allstak_xhr_patched__";
1625
1736
  const X = globalThis.XMLHttpRequest;
@@ -1671,43 +1782,6 @@ function instrumentXmlHttpRequest() {
1671
1782
  };
1672
1783
  X.prototype[flag] = true;
1673
1784
  }
1674
- var __testNativeModule = null;
1675
- function __setNativeModuleForTest(mod) {
1676
- __testNativeModule = mod;
1677
- }
1678
- async function drainPendingNativeCrashes(release) {
1679
- try {
1680
- let native = __testNativeModule;
1681
- if (!native) {
1682
- const rn = __require("react-native");
1683
- native = rn?.NativeModules?.AllStakNative;
1684
- }
1685
- if (!native) return;
1686
- if (typeof native.install === "function") {
1687
- try {
1688
- await native.install(release ?? "");
1689
- } catch {
1690
- }
1691
- }
1692
- if (typeof native.drainPendingCrash === "function") {
1693
- const json = await native.drainPendingCrash();
1694
- if (json && json !== "") {
1695
- try {
1696
- const payload = JSON.parse(json);
1697
- const err = new Error(payload?.message ?? "Native crash");
1698
- err.name = payload?.exceptionClass ?? "NativeCrash";
1699
- err.stack = Array.isArray(payload?.stackTrace) ? payload.stackTrace.join("\n") : String(payload?.stackTrace ?? "");
1700
- AllStak.captureException(err, {
1701
- ...payload?.metadata || {},
1702
- "native.crash": "true"
1703
- });
1704
- } catch {
1705
- }
1706
- }
1707
- }
1708
- } catch {
1709
- }
1710
- }
1711
1785
  function installReactNative(options = {}) {
1712
1786
  const autoError = options.autoErrorHandler !== false;
1713
1787
  const autoPromise = options.autoPromiseRejections !== false;
@@ -1723,7 +1797,7 @@ function installReactNative(options = {}) {
1723
1797
  const hermes = typeof globalThis.HermesInternal !== "undefined";
1724
1798
  let dist;
1725
1799
  try {
1726
- const rn = __require("react-native");
1800
+ const rn = require("react-native");
1727
1801
  const os = rn?.Platform?.OS;
1728
1802
  if (os === "ios" || os === "android") {
1729
1803
  dist = `${os}-${hermes ? "hermes" : "jsc"}`;
@@ -1754,13 +1828,25 @@ function installReactNative(options = {}) {
1754
1828
  }
1755
1829
  if (options.autoConsoleBreadcrumbs !== false) {
1756
1830
  try {
1757
- instrumentConsole(__safeAddBreadcrumbForInstrumentation);
1831
+ const cfg = AllStak.getConfig();
1832
+ instrumentConsole(__safeAddBreadcrumbForInstrumentation, cfg?.captureConsole);
1758
1833
  } catch {
1759
1834
  }
1760
1835
  }
1836
+ if (options.autoNavigationBreadcrumbs !== false) {
1837
+ let navResult = false;
1838
+ try {
1839
+ navResult = tryAutoInstrumentNavigation();
1840
+ } catch {
1841
+ }
1842
+ if (options.debugLogs) {
1843
+ if (navResult) console.log("[AllStak] Navigation auto-instrumentation enabled");
1844
+ else console.log("[AllStak] Navigation auto-instrumentation not applied; use instrumentReactNavigation(ref) fallback");
1845
+ }
1846
+ }
1761
1847
  if (autoDevice) {
1762
1848
  try {
1763
- const rn = __require("react-native");
1849
+ const rn = require("react-native");
1764
1850
  const Platform = rn?.Platform;
1765
1851
  if (Platform) {
1766
1852
  AllStak.setTag("device.os", String(Platform.OS ?? ""));
@@ -1774,7 +1860,7 @@ function installReactNative(options = {}) {
1774
1860
  }
1775
1861
  if (autoAppState) {
1776
1862
  try {
1777
- const rn = __require("react-native");
1863
+ const rn = require("react-native");
1778
1864
  const AppState = rn?.AppState;
1779
1865
  if (AppState && typeof AppState.addEventListener === "function") {
1780
1866
  AppState.addEventListener("change", (next) => {
@@ -1807,17 +1893,30 @@ function installReactNative(options = {}) {
1807
1893
  }
1808
1894
  }
1809
1895
  if (autoPromise) {
1896
+ const wrapTrackerReason = (rejection) => rejection instanceof Error ? rejection : new Error(`Unhandled promise rejection: ${String(rejection)}`);
1897
+ const ship = (err) => {
1898
+ try {
1899
+ AllStak.captureException(err, { source: "unhandledRejection" });
1900
+ } catch {
1901
+ }
1902
+ };
1810
1903
  try {
1811
- const tracking = __require("promise/setimmediate/rejection-tracking");
1904
+ const hermesInternal = globalThis.HermesInternal;
1905
+ if (hermesInternal && typeof hermesInternal.enablePromiseRejectionTracker === "function") {
1906
+ hermesInternal.enablePromiseRejectionTracker({
1907
+ allRejections: true,
1908
+ onUnhandled: (_id, rejection) => ship(wrapTrackerReason(rejection)),
1909
+ onHandled: () => {
1910
+ }
1911
+ });
1912
+ }
1913
+ } catch {
1914
+ }
1915
+ try {
1916
+ const tracking = require("promise/setimmediate/rejection-tracking");
1812
1917
  tracking.enable({
1813
1918
  allRejections: true,
1814
- onUnhandled: (_id, rejection) => {
1815
- const err = rejection instanceof Error ? rejection : new Error(`Unhandled promise rejection: ${String(rejection)}`);
1816
- try {
1817
- AllStak.captureException(err, { source: "unhandledRejection" });
1818
- } catch {
1819
- }
1820
- },
1919
+ onUnhandled: (_id, rejection) => ship(wrapTrackerReason(rejection)),
1821
1920
  onHandled: () => {
1822
1921
  }
1823
1922
  });
@@ -1827,30 +1926,230 @@ function installReactNative(options = {}) {
1827
1926
  g.addEventListener("unhandledrejection", (ev) => {
1828
1927
  const reason = ev?.reason;
1829
1928
  const err = reason instanceof Error ? reason : new Error(String(reason));
1830
- try {
1831
- AllStak.captureException(err, { source: "unhandledRejection" });
1832
- } catch {
1833
- }
1929
+ ship(err);
1834
1930
  });
1835
1931
  }
1836
1932
  }
1837
1933
  }
1838
1934
  }
1935
+
1936
+ // src/provider.tsx
1937
+ var AllStakContext = React.createContext(null);
1938
+ var __providerOwnedInstance = null;
1939
+ var AllStakErrorBoundary = class extends React.Component {
1940
+ constructor() {
1941
+ super(...arguments);
1942
+ this.state = { error: null };
1943
+ this.resetError = () => this.setState({ error: null });
1944
+ }
1945
+ static getDerivedStateFromError(error) {
1946
+ return { error };
1947
+ }
1948
+ componentDidCatch(error, info) {
1949
+ try {
1950
+ AllStak.addBreadcrumb("ui", "React error boundary caught error", "error", {
1951
+ componentStack: info.componentStack ?? ""
1952
+ });
1953
+ AllStak.captureException(error, {
1954
+ componentStack: info.componentStack ?? "",
1955
+ source: "AllStakProvider.ErrorBoundary"
1956
+ });
1957
+ if (this.props.debug) {
1958
+ console.log(`[AllStak] Captured render error: ${error.message}`);
1959
+ }
1960
+ } catch {
1961
+ }
1962
+ try {
1963
+ this.props.onError?.(error, info.componentStack ?? void 0);
1964
+ } catch {
1965
+ }
1966
+ }
1967
+ render() {
1968
+ if (this.state.error) {
1969
+ const { fallback } = this.props;
1970
+ if (typeof fallback === "function") {
1971
+ return fallback({ error: this.state.error, resetError: this.resetError });
1972
+ }
1973
+ if (fallback !== void 0) return fallback;
1974
+ return null;
1975
+ }
1976
+ return this.props.children;
1977
+ }
1978
+ };
1979
+ function AllStakProvider({
1980
+ children,
1981
+ apiKey,
1982
+ environment,
1983
+ release,
1984
+ host,
1985
+ user,
1986
+ tags,
1987
+ debug,
1988
+ enableHttpTracking,
1989
+ httpTracking,
1990
+ captureConsole,
1991
+ sampleRate,
1992
+ beforeSend,
1993
+ replay,
1994
+ tracesSampleRate,
1995
+ service,
1996
+ dist,
1997
+ destroyOnUnmount = false,
1998
+ fallback,
1999
+ onError,
2000
+ autoErrorHandler,
2001
+ autoPromiseRejections,
2002
+ autoDeviceTags,
2003
+ autoAppStateBreadcrumbs,
2004
+ autoNetworkCapture,
2005
+ autoFetchBreadcrumbs,
2006
+ autoConsoleBreadcrumbs,
2007
+ autoNavigationBreadcrumbs
2008
+ }) {
2009
+ const clientRef = React.useRef(null);
2010
+ if (!clientRef.current) {
2011
+ const existing = AllStak._getInstance();
2012
+ if (existing && __providerOwnedInstance === existing) {
2013
+ clientRef.current = existing;
2014
+ if (debug) {
2015
+ console.log(`[AllStak] Reusing session ${AllStak.getSessionId()}`);
2016
+ }
2017
+ } else {
2018
+ const config = {
2019
+ apiKey,
2020
+ environment,
2021
+ release,
2022
+ host,
2023
+ user,
2024
+ tags,
2025
+ enableHttpTracking,
2026
+ httpTracking,
2027
+ captureConsole,
2028
+ sampleRate,
2029
+ beforeSend,
2030
+ replay,
2031
+ tracesSampleRate,
2032
+ service,
2033
+ dist
2034
+ };
2035
+ clientRef.current = AllStak.init(config);
2036
+ __providerOwnedInstance = clientRef.current;
2037
+ installReactNative({
2038
+ autoErrorHandler,
2039
+ autoPromiseRejections,
2040
+ autoDeviceTags,
2041
+ autoAppStateBreadcrumbs,
2042
+ autoNetworkCapture,
2043
+ autoFetchBreadcrumbs,
2044
+ autoConsoleBreadcrumbs,
2045
+ autoNavigationBreadcrumbs,
2046
+ debugLogs: debug
2047
+ });
2048
+ if (debug) {
2049
+ console.log(`[AllStak] Initialized \u2014 session ${AllStak.getSessionId()}`);
2050
+ }
2051
+ }
2052
+ }
2053
+ React.useEffect(() => {
2054
+ return () => {
2055
+ if (destroyOnUnmount) {
2056
+ AllStak.destroy();
2057
+ __providerOwnedInstance = null;
2058
+ clientRef.current = null;
2059
+ if (debug) console.log("[AllStak] Destroyed on unmount");
2060
+ }
2061
+ };
2062
+ }, [destroyOnUnmount, debug]);
2063
+ return /* @__PURE__ */ React.createElement(AllStakContext.Provider, { value: clientRef.current }, /* @__PURE__ */ React.createElement(AllStakErrorBoundary, { fallback, onError, debug }, children));
2064
+ }
2065
+ function useAllStak() {
2066
+ return React.useMemo(
2067
+ () => ({
2068
+ captureException: (error, ctx) => AllStak.captureException(error, ctx),
2069
+ captureMessage: (msg, level = "info") => AllStak.captureMessage(msg, level),
2070
+ setUser: (user) => AllStak.setUser(user),
2071
+ setTag: (key, value) => AllStak.setTag(key, value),
2072
+ addBreadcrumb: (type, message, level, data) => AllStak.addBreadcrumb(type, message, level, data)
2073
+ }),
2074
+ []
2075
+ );
2076
+ }
2077
+ function __resetProviderInstanceForTest() {
2078
+ __providerOwnedInstance = null;
2079
+ }
2080
+
2081
+ // src/index.ts
2082
+ var __testNativeModule = null;
2083
+ function __setNativeModuleForTest(mod) {
2084
+ __testNativeModule = mod;
2085
+ }
2086
+ async function __devTriggerNativeCrash() {
2087
+ try {
2088
+ let native = __testNativeModule;
2089
+ if (!native) {
2090
+ const rn = require("react-native");
2091
+ native = rn?.NativeModules?.AllStakNative;
2092
+ }
2093
+ if (!native || typeof native.__devTriggerCrash !== "function") return;
2094
+ await native.__devTriggerCrash();
2095
+ } catch {
2096
+ }
2097
+ }
2098
+ async function drainPendingNativeCrashes(release) {
2099
+ try {
2100
+ let native = __testNativeModule;
2101
+ if (!native) {
2102
+ const rn = require("react-native");
2103
+ native = rn?.NativeModules?.AllStakNative;
2104
+ }
2105
+ if (!native) return;
2106
+ if (typeof native.install === "function") {
2107
+ try {
2108
+ await native.install(release ?? "");
2109
+ } catch {
2110
+ }
2111
+ }
2112
+ if (typeof native.drainPendingCrash === "function") {
2113
+ const json = await native.drainPendingCrash();
2114
+ if (json && json !== "") {
2115
+ try {
2116
+ const payload = JSON.parse(json);
2117
+ const err = new Error(payload?.message ?? "Native crash");
2118
+ err.name = payload?.exceptionClass ?? "NativeCrash";
2119
+ err.stack = Array.isArray(payload?.stackTrace) ? payload.stackTrace.join("\n") : String(payload?.stackTrace ?? "");
2120
+ AllStak.captureException(err, {
2121
+ ...payload?.metadata || {},
2122
+ "native.crash": "true"
2123
+ });
2124
+ } catch {
2125
+ }
2126
+ }
2127
+ }
2128
+ } catch {
2129
+ }
2130
+ }
1839
2131
  export {
1840
2132
  AllStak,
1841
2133
  AllStakClient,
2134
+ AllStakProvider,
1842
2135
  HttpRequestModule,
1843
2136
  INGEST_HOST,
1844
2137
  ReplaySurrogate,
1845
2138
  SDK_NAME,
1846
2139
  SDK_VERSION,
1847
2140
  Scope,
2141
+ __devTriggerNativeCrash,
2142
+ __resetAutoNavigationFlagForTest,
2143
+ __resetConsoleInstrumentationFlagForTest,
2144
+ __resetProviderInstanceForTest,
1848
2145
  __setNativeModuleForTest,
1849
2146
  applyArchitectureTags,
1850
2147
  detectArchitecture,
1851
2148
  drainPendingNativeCrashes,
1852
2149
  installReactNative,
1853
2150
  instrumentNavigationFromLinking,
1854
- instrumentReactNavigation
2151
+ instrumentReactNavigation,
2152
+ tryAutoInstrumentNavigation,
2153
+ useAllStak
1855
2154
  };
1856
2155
  //# sourceMappingURL=index.mjs.map