@allstak/react-native 0.4.0 → 0.5.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.mjs CHANGED
@@ -1482,7 +1482,9 @@ function tryRequire(id2) {
1482
1482
 
1483
1483
  // src/privacy.tsx
1484
1484
  import * as React from "react";
1485
- var RN = tryRequire("react-native");
1485
+ function getRN() {
1486
+ return tryRequire("react-native");
1487
+ }
1486
1488
  var state = {
1487
1489
  isCapturing: false,
1488
1490
  sensitiveRefs: /* @__PURE__ */ new Set()
@@ -1530,9 +1532,18 @@ function useAllStakPrivacy() {
1530
1532
  const isCapturing = useIsCapturing();
1531
1533
  return { isCapturing, registerSensitiveRef };
1532
1534
  }
1533
- var View = RN?.View ?? ((props) => React.createElement("View", props));
1534
- var Text = RN?.Text ?? ((props) => React.createElement("Text", props));
1535
- var TextInput = RN?.TextInput ?? ((props) => React.createElement("TextInput", props));
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
+ }
1536
1547
  var DEFAULT_MASK_COLOR = "#d8dde7";
1537
1548
  var DEFAULT_MASK_LABEL = "\u2022\u2022\u2022\u2022\u2022\u2022";
1538
1549
  function AllStakMaskedView({
@@ -1545,6 +1556,8 @@ function AllStakMaskedView({
1545
1556
  ...rest
1546
1557
  }) {
1547
1558
  const isCapturing = useIsCapturing();
1559
+ const View = getView();
1560
+ const Text = getText();
1548
1561
  if (!isCapturing || privacy === "show") {
1549
1562
  return React.createElement(View, { style, ...rest }, children);
1550
1563
  }
@@ -1567,6 +1580,8 @@ function AllStakTextInput({
1567
1580
  ...rest
1568
1581
  }) {
1569
1582
  const isCapturing = useIsCapturing();
1583
+ const View = getView();
1584
+ const TextInput = getTextInput();
1570
1585
  if (isCapturing && privacy !== "show") {
1571
1586
  return React.createElement(View, {
1572
1587
  style: [{ minHeight: 40, backgroundColor: maskColor, borderRadius: 4 }, style]
@@ -1582,6 +1597,7 @@ function AllStakSensitiveText({
1582
1597
  ...rest
1583
1598
  }) {
1584
1599
  const isCapturing = useIsCapturing();
1600
+ const Text = getText();
1585
1601
  if (isCapturing && privacy !== "show") {
1586
1602
  return React.createElement(Text, { style, ...rest }, maskLabel);
1587
1603
  }
@@ -1683,8 +1699,8 @@ function estimateBase64Size(base64) {
1683
1699
  }
1684
1700
  function readDimensions() {
1685
1701
  try {
1686
- const RN3 = tryRequire("react-native");
1687
- const dims = RN3?.Dimensions?.get?.("window");
1702
+ const RN = tryRequire("react-native");
1703
+ const dims = RN?.Dimensions?.get?.("window");
1688
1704
  if (dims && typeof dims.width === "number" && typeof dims.height === "number") {
1689
1705
  return { width: Math.round(dims.width), height: Math.round(dims.height) };
1690
1706
  }
@@ -1767,15 +1783,325 @@ function pickScreenshotConfig(source) {
1767
1783
  return out;
1768
1784
  }
1769
1785
 
1786
+ // src/contexts.ts
1787
+ function tryReq(id2) {
1788
+ try {
1789
+ return require(id2);
1790
+ } catch {
1791
+ return null;
1792
+ }
1793
+ }
1794
+ function defaultExport(mod) {
1795
+ if (!mod) return null;
1796
+ return mod.default ?? mod;
1797
+ }
1798
+ function strOrUndef(v) {
1799
+ if (v == null) return void 0;
1800
+ const s = String(v);
1801
+ return s.length > 0 ? s : void 0;
1802
+ }
1803
+ function collectAutoContexts(opts = {}) {
1804
+ const captureDev = opts.captureDeviceContext !== false;
1805
+ const captureScreen = opts.captureScreenContext !== false;
1806
+ const contexts = {};
1807
+ const tags = {};
1808
+ const RN = tryReq("react-native");
1809
+ const Platform = RN?.Platform;
1810
+ const Dimensions = RN?.Dimensions;
1811
+ const NativeModules = RN?.NativeModules;
1812
+ const osName = strOrUndef(Platform?.OS);
1813
+ const osVersion = strOrUndef(Platform?.Version);
1814
+ const osConstants = Platform?.constants ?? {};
1815
+ if (osName) {
1816
+ contexts.os = {
1817
+ name: osName === "ios" ? "iOS" : osName === "android" ? "Android" : osName,
1818
+ version: osVersion,
1819
+ build: strOrUndef(osConstants?.osBuildId ?? osConstants?.Release)
1820
+ };
1821
+ tags["os.name"] = osName;
1822
+ if (osVersion) tags["os.version"] = osVersion;
1823
+ }
1824
+ const g = globalThis;
1825
+ const hermes = typeof g.HermesInternal !== "undefined";
1826
+ const fabric = typeof g.__turboModuleProxy !== "undefined";
1827
+ const turboModules = fabric;
1828
+ const bridgeless = typeof g.RN$Bridgeless !== "undefined" && !!g.RN$Bridgeless;
1829
+ const jsEngine = hermes ? "hermes" : "jsc";
1830
+ tags["js_engine"] = jsEngine;
1831
+ tags["fabric"] = String(fabric);
1832
+ tags["turbo_modules"] = String(turboModules);
1833
+ const hermesVersion = (() => {
1834
+ try {
1835
+ return g.HermesInternal?.getRuntimeProperties?.()?.["OSS Release Version"];
1836
+ } catch {
1837
+ return void 0;
1838
+ }
1839
+ })();
1840
+ contexts.runtime = {
1841
+ name: jsEngine,
1842
+ version: strOrUndef(hermesVersion) ?? "unknown",
1843
+ bridgeless
1844
+ };
1845
+ if (captureDev) {
1846
+ const device = {};
1847
+ if (osConstants?.Model) device.model = String(osConstants.Model);
1848
+ if (osConstants?.Brand) device.brand = String(osConstants.Brand);
1849
+ if (osConstants?.Manufacturer) device.manufacturer = String(osConstants.Manufacturer);
1850
+ if (osConstants?.systemName) device.systemName = String(osConstants.systemName);
1851
+ if (osConstants?.interfaceIdiom) device.family = String(osConstants.interfaceIdiom);
1852
+ const isSim = Boolean(osConstants?.isTesting) || typeof osConstants?.reactNativeVersion === "object" && String(osConstants?.systemName ?? "").toLowerCase().includes("simulator");
1853
+ if (isSim) {
1854
+ device.simulator = true;
1855
+ tags["simulator"] = "true";
1856
+ }
1857
+ const expoDevice = tryReq("expo-device");
1858
+ if (expoDevice) {
1859
+ const ed = defaultExport(expoDevice) ?? expoDevice;
1860
+ if (ed.modelName) device.model = String(ed.modelName);
1861
+ if (ed.brand) device.brand = String(ed.brand);
1862
+ if (ed.manufacturer) device.manufacturer = String(ed.manufacturer);
1863
+ if (ed.deviceYearClass != null) device.yearClass = ed.deviceYearClass;
1864
+ if (ed.totalMemory != null) device.memory_size = ed.totalMemory;
1865
+ if (typeof ed.supportedCpuArchitectures !== "undefined") {
1866
+ const arr = ed.supportedCpuArchitectures;
1867
+ if (Array.isArray(arr) && arr.length > 0) device.arch = String(arr[0]);
1868
+ }
1869
+ if (typeof ed.isDevice === "boolean" && !ed.isDevice) {
1870
+ device.simulator = true;
1871
+ tags["simulator"] = "true";
1872
+ }
1873
+ } else {
1874
+ const dinfo = tryReq("react-native-device-info");
1875
+ if (dinfo) {
1876
+ const d = defaultExport(dinfo) ?? dinfo;
1877
+ try {
1878
+ device.model = device.model ?? d.getModel?.();
1879
+ } catch {
1880
+ }
1881
+ try {
1882
+ device.manufacturer = device.manufacturer ?? d.getManufacturerSync?.();
1883
+ } catch {
1884
+ }
1885
+ try {
1886
+ device.memory_size = device.memory_size ?? d.getTotalMemorySync?.();
1887
+ } catch {
1888
+ }
1889
+ try {
1890
+ const isEm = d.isEmulatorSync?.();
1891
+ if (isEm) {
1892
+ device.simulator = true;
1893
+ tags["simulator"] = "true";
1894
+ }
1895
+ } catch {
1896
+ }
1897
+ }
1898
+ }
1899
+ if (captureScreen && Dimensions && typeof Dimensions.get === "function") {
1900
+ try {
1901
+ const screen = Dimensions.get("screen");
1902
+ if (screen?.width && screen?.height) {
1903
+ device.screen_width_pixels = Math.round(screen.width * (screen.scale ?? 1));
1904
+ device.screen_height_pixels = Math.round(screen.height * (screen.scale ?? 1));
1905
+ device.screen_density = screen.scale;
1906
+ device.orientation = screen.width >= screen.height ? "landscape" : "portrait";
1907
+ tags["app_orientation"] = device.orientation;
1908
+ }
1909
+ } catch {
1910
+ }
1911
+ }
1912
+ if (opts.captureBattery) {
1913
+ const expoBattery = tryReq("expo-battery");
1914
+ if (expoBattery) {
1915
+ try {
1916
+ const eb = defaultExport(expoBattery) ?? expoBattery;
1917
+ device.battery_available = true;
1918
+ if (typeof eb.getBatteryLevelAsync === "function") {
1919
+ }
1920
+ } catch {
1921
+ }
1922
+ }
1923
+ }
1924
+ if (Object.keys(device).length > 0) {
1925
+ contexts.device = device;
1926
+ if (device.model) tags["device.model"] = String(device.model);
1927
+ }
1928
+ }
1929
+ const app = {};
1930
+ app.app_start_time = (/* @__PURE__ */ new Date()).toISOString();
1931
+ const expoApp = tryReq("expo-application");
1932
+ if (expoApp) {
1933
+ const ea = defaultExport(expoApp) ?? expoApp;
1934
+ if (ea.applicationName) app.app_name = String(ea.applicationName);
1935
+ if (ea.applicationId) app.app_identifier = String(ea.applicationId);
1936
+ if (ea.nativeBuildVersion) app.app_build = String(ea.nativeBuildVersion);
1937
+ if (ea.nativeApplicationVersion) app.app_version = String(ea.nativeApplicationVersion);
1938
+ } else {
1939
+ const dinfo = tryReq("react-native-device-info");
1940
+ if (dinfo) {
1941
+ const d = defaultExport(dinfo) ?? dinfo;
1942
+ try {
1943
+ app.app_name = d.getApplicationName?.();
1944
+ } catch {
1945
+ }
1946
+ try {
1947
+ app.app_identifier = d.getBundleId?.();
1948
+ } catch {
1949
+ }
1950
+ try {
1951
+ app.app_build = d.getBuildNumber?.();
1952
+ } catch {
1953
+ }
1954
+ try {
1955
+ app.app_version = d.getVersion?.();
1956
+ } catch {
1957
+ }
1958
+ }
1959
+ }
1960
+ if (Object.keys(app).length > 0) {
1961
+ contexts.app = app;
1962
+ if (app.app_version) tags["app.version"] = String(app.app_version);
1963
+ if (app.app_build) tags["app.build"] = String(app.app_build);
1964
+ }
1965
+ const rn = {
1966
+ hermes,
1967
+ fabric,
1968
+ turbo_modules: turboModules,
1969
+ bridgeless,
1970
+ js_engine: jsEngine
1971
+ };
1972
+ if (osConstants?.reactNativeVersion) {
1973
+ const v = osConstants.reactNativeVersion;
1974
+ if (typeof v === "object" && v.major != null) {
1975
+ rn.react_native_version = `${v.major}.${v.minor ?? 0}.${v.patch ?? 0}`;
1976
+ } else if (typeof v === "string") {
1977
+ rn.react_native_version = v;
1978
+ }
1979
+ }
1980
+ const expoConstants = tryReq("expo-constants");
1981
+ if (expoConstants) {
1982
+ const ec = defaultExport(expoConstants) ?? expoConstants;
1983
+ if (ec.expoVersion) rn.expo = ec.expoVersion;
1984
+ else if (ec.expoConfig?.sdkVersion) rn.expo = ec.expoConfig.sdkVersion;
1985
+ if (ec.appOwnership) rn.expo_application_ownership = String(ec.appOwnership);
1986
+ if (ec.executionEnvironment) rn.expo_execution_environment = String(ec.executionEnvironment);
1987
+ } else {
1988
+ rn.expo = false;
1989
+ }
1990
+ contexts.react_native = rn;
1991
+ if (rn.expo && rn.expo !== false) tags["expo"] = "true";
1992
+ if (rn.react_native_version) tags["rn.version"] = String(rn.react_native_version);
1993
+ return { contexts, tags };
1994
+ }
1995
+ function buildUserContext(user, opts = {}) {
1996
+ if (!user) return void 0;
1997
+ const out = {};
1998
+ if (user.id) out.id = user.id;
1999
+ if (user.username) out.username = user.username;
2000
+ if (opts.sendDefaultPii) {
2001
+ if (user.email) out.email = user.email;
2002
+ if (user.ip_address) out.ip_address = user.ip_address;
2003
+ }
2004
+ return Object.keys(out).length > 0 ? out : void 0;
2005
+ }
2006
+
2007
+ // src/mechanism.ts
2008
+ var URL_QUERY_RE = /\?.*$/;
2009
+ function sanitizeUrl(url) {
2010
+ if (!url) return "";
2011
+ const s = String(url).replace(URL_QUERY_RE, "");
2012
+ return s;
2013
+ }
2014
+ function exceptionClassName(err) {
2015
+ if (err && typeof err === "object") {
2016
+ const e = err;
2017
+ const fromName = e.name && e.name !== "Error" ? e.name : void 0;
2018
+ return fromName ?? e.constructor?.name ?? "Error";
2019
+ }
2020
+ return "Error";
2021
+ }
2022
+ function exceptionValue(err) {
2023
+ if (err == null) return "";
2024
+ if (err instanceof Error) return err.message ?? "";
2025
+ if (typeof err === "string") return err;
2026
+ try {
2027
+ return JSON.stringify(err);
2028
+ } catch {
2029
+ return String(err);
2030
+ }
2031
+ }
2032
+ function buildExceptionChain(err, mechanism, handled) {
2033
+ const values = [];
2034
+ const seen = /* @__PURE__ */ new Set();
2035
+ let cursor = err;
2036
+ let depth = 0;
2037
+ while (cursor && depth < 5 && !seen.has(cursor)) {
2038
+ seen.add(cursor);
2039
+ const e = cursor;
2040
+ const frames = parseStack(e?.stack);
2041
+ values.push({
2042
+ type: exceptionClassName(e),
2043
+ value: exceptionValue(e),
2044
+ stacktrace: frames.length > 0 ? { frames } : void 0,
2045
+ // Only the outermost exception carries the mechanism, like Sentry.
2046
+ ...depth === 0 ? { mechanism: { type: mechanism, handled } } : {}
2047
+ });
2048
+ cursor = e?.cause;
2049
+ depth += 1;
2050
+ }
2051
+ return values.reverse();
2052
+ }
2053
+ function extractAxiosRequest(err) {
2054
+ if (!err || typeof err !== "object") return null;
2055
+ const e = err;
2056
+ if (e.isAxiosError !== true) return null;
2057
+ const cfg = e.config ?? {};
2058
+ const resp = e.response ?? null;
2059
+ const method = strUpper(cfg.method);
2060
+ const rawUrl = composeAxiosUrl(cfg);
2061
+ const url_sanitized = sanitizeUrl(rawUrl);
2062
+ const status_code = typeof resp?.status === "number" ? resp.status : void 0;
2063
+ const duration_ms = typeof e.duration === "number" ? e.duration : void 0;
2064
+ const category = classifyHttpError(e, status_code);
2065
+ return {
2066
+ method,
2067
+ url_sanitized,
2068
+ status_code,
2069
+ duration_ms,
2070
+ category
2071
+ };
2072
+ }
2073
+ function composeAxiosUrl(cfg) {
2074
+ const base = typeof cfg?.baseURL === "string" ? cfg.baseURL.replace(/\/$/, "") : "";
2075
+ const path = typeof cfg?.url === "string" ? cfg.url : "";
2076
+ if (!base && !path) return "";
2077
+ if (path.startsWith("http://") || path.startsWith("https://")) return path;
2078
+ return base + path;
2079
+ }
2080
+ function strUpper(v) {
2081
+ if (typeof v !== "string" || v.length === 0) return void 0;
2082
+ return v.toUpperCase();
2083
+ }
2084
+ function classifyHttpError(err, status) {
2085
+ const code = typeof err?.code === "string" ? err.code.toUpperCase() : "";
2086
+ if (code === "ECONNABORTED" || code === "ETIMEDOUT") return "timeout";
2087
+ if (code === "ERR_CANCELED" || code === "CANCELED") return "cancel";
2088
+ if (typeof status === "number" && status >= 500) return "http_server_error";
2089
+ if (typeof status === "number" && status >= 400) return "http_client_error";
2090
+ return "network";
2091
+ }
2092
+ function maybeExtractHttpRequest(err) {
2093
+ return extractAxiosRequest(err);
2094
+ }
2095
+
1770
2096
  // src/client.ts
1771
2097
  var INGEST_HOST = "https://api.allstak.sa";
1772
2098
  var SDK_NAME = "allstak-react-native";
1773
- var SDK_VERSION = "0.4.0";
2099
+ var SDK_VERSION = "0.5.0";
1774
2100
  var ERRORS_PATH = "/ingest/v1/errors";
1775
2101
  var LOGS_PATH = "/ingest/v1/logs";
1776
2102
  var VALID_BREADCRUMB_TYPES = /* @__PURE__ */ new Set(["http", "log", "ui", "navigation", "query", "default"]);
1777
2103
  var VALID_BREADCRUMB_LEVELS = /* @__PURE__ */ new Set(["info", "warn", "error", "debug"]);
1778
- var DEFAULT_MAX_BREADCRUMBS = 50;
2104
+ var DEFAULT_MAX_BREADCRUMBS = 100;
1779
2105
  function frameToString(f) {
1780
2106
  const fn = f.function && f.function.length > 0 ? f.function : "<anonymous>";
1781
2107
  const file = f.filename || f.absPath || "<anonymous>";
@@ -1804,6 +2130,12 @@ var AllStakClient = class {
1804
2130
  this.replay = null;
1805
2131
  this.httpRequests = null;
1806
2132
  this._instrumentAxios = null;
2133
+ /** Auto-collected Sentry-shape contexts (device/os/app/react_native/runtime). */
2134
+ this.autoContexts = {};
2135
+ /** Auto-collected tags (device.model, os.name, js_engine, …). */
2136
+ this.autoTags = {};
2137
+ /** Current screen / transaction name — set via setCurrentScreen() or nav auto-instrument. */
2138
+ this.currentTransaction = null;
1807
2139
  this.config = { ...config };
1808
2140
  if (!this.config.environment) this.config.environment = "production";
1809
2141
  if (!this.config.sdkName) this.config.sdkName = SDK_NAME;
@@ -1825,6 +2157,18 @@ var AllStakClient = class {
1825
2157
  } catch {
1826
2158
  }
1827
2159
  }
2160
+ try {
2161
+ const opts = {
2162
+ captureDeviceContext: config.captureDeviceContext !== false,
2163
+ captureBattery: config.captureBattery === true,
2164
+ captureScreenContext: config.captureScreenContext !== false,
2165
+ sendDefaultPii: config.sendDefaultPii === true
2166
+ };
2167
+ const { contexts, tags } = collectAutoContexts(opts);
2168
+ this.autoContexts = contexts;
2169
+ this.autoTags = tags;
2170
+ } catch {
2171
+ }
1828
2172
  if (config.enableHttpTracking) {
1829
2173
  try {
1830
2174
  this.httpRequests = new HttpRequestModule(this.transport);
@@ -1867,7 +2211,7 @@ var AllStakClient = class {
1867
2211
  return this.httpRequests?.getRecentFailed() ?? [];
1868
2212
  }
1869
2213
  // ── Public API ────────────────────────────────────────────────────
1870
- captureException(error, context) {
2214
+ captureException(error, context, opts) {
1871
2215
  if (!this.passesSampleRate()) return;
1872
2216
  const frames = parseStack(error.stack).map((f) => ({
1873
2217
  ...f,
@@ -1949,6 +2293,35 @@ var AllStakClient = class {
1949
2293
  breadcrumbs: currentBreadcrumbs,
1950
2294
  fingerprint: eff.fingerprint
1951
2295
  };
2296
+ const mechanism = opts?.mechanism ?? "captureException";
2297
+ const handled = opts?.handled ?? (mechanism === "captureException" || mechanism === "errorboundary");
2298
+ payload.eventId = generateEventId();
2299
+ payload.timestamp = (/* @__PURE__ */ new Date()).toISOString();
2300
+ payload.handled = handled;
2301
+ payload.mechanism = mechanism;
2302
+ if (this.currentTransaction) payload.transaction = this.currentTransaction;
2303
+ payload.exception = { values: buildExceptionChain(error, mechanism, handled) };
2304
+ const req = maybeExtractHttpRequest(error);
2305
+ if (req) payload.request = req;
2306
+ const userCtx = buildUserContext(eff.user, { sendDefaultPii: this.config.sendDefaultPii });
2307
+ const traceCtx = {};
2308
+ if (payload.traceId) traceCtx.trace_id = payload.traceId;
2309
+ if (payload.spanId) traceCtx.span_id = payload.spanId;
2310
+ if (payload.parentSpanId) traceCtx.parent_span_id = payload.parentSpanId;
2311
+ payload.contexts = {
2312
+ ...this.autoContexts,
2313
+ ...eff.contexts ?? {},
2314
+ ...userCtx ? { user: userCtx } : {},
2315
+ ...Object.keys(traceCtx).length > 0 ? { trace: traceCtx } : {}
2316
+ };
2317
+ payload.tags = {
2318
+ ...this.autoTags,
2319
+ ...this.config.tags ?? {},
2320
+ ...eff.tags ?? {},
2321
+ environment: this.config.environment ?? "production",
2322
+ ...this.config.release ? { release: this.config.release } : {},
2323
+ ...this.config.dist ? { dist: this.config.dist } : {}
2324
+ };
1952
2325
  const flatPresent = this.config.captureScreenshotOnError === true;
1953
2326
  const callbackPresent = Boolean(this.config.screenshot?.provider);
1954
2327
  warnIfBothApisPresent(callbackPresent, flatPresent);
@@ -2106,16 +2479,59 @@ var AllStakClient = class {
2106
2479
  }
2107
2480
  }
2108
2481
  addBreadcrumb(type, message, level, data) {
2109
- const crumb = {
2482
+ let crumb = {
2110
2483
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2111
2484
  type: VALID_BREADCRUMB_TYPES.has(type) ? type : "default",
2112
2485
  message,
2113
2486
  level: level && VALID_BREADCRUMB_LEVELS.has(level) ? level : "info",
2114
2487
  ...data ? { data } : {}
2115
2488
  };
2489
+ if (crumb.type === "http" && (this.config.denyUrls || this.config.allowUrls)) {
2490
+ const url = typeof crumb.data?.url === "string" ? crumb.data.url : "";
2491
+ if (url) {
2492
+ if (this.config.denyUrls && this.config.denyUrls.some((p) => matchUrlPattern(url, p))) return;
2493
+ if (this.config.allowUrls && this.config.allowUrls.length > 0 && !this.config.allowUrls.some((p) => matchUrlPattern(url, p))) return;
2494
+ }
2495
+ }
2496
+ if (crumb.data && this.config.scrubKeys && this.config.scrubKeys.length > 0) {
2497
+ crumb = { ...crumb, data: scrubObject(crumb.data, this.config.scrubKeys) };
2498
+ }
2499
+ if (this.config.beforeBreadcrumb) {
2500
+ try {
2501
+ const out = this.config.beforeBreadcrumb(crumb);
2502
+ if (out === null) return;
2503
+ if (out) crumb = out;
2504
+ } catch {
2505
+ }
2506
+ }
2116
2507
  if (this.breadcrumbs.length >= this.maxBreadcrumbs) this.breadcrumbs.shift();
2117
2508
  this.breadcrumbs.push(crumb);
2118
2509
  }
2510
+ /**
2511
+ * Set the current screen / route name. Stamps `transaction` on every
2512
+ * subsequent event and emits a `navigation` breadcrumb. Use this when
2513
+ * not on `@react-navigation/native` (the nav auto-instrument calls
2514
+ * this for you).
2515
+ */
2516
+ setCurrentScreen(name) {
2517
+ if (!name) return;
2518
+ const prev = this.currentTransaction;
2519
+ this.currentTransaction = name;
2520
+ if (prev !== name) {
2521
+ this.addBreadcrumb("navigation", `${prev ?? "<start>"} -> ${name}`, "info", {
2522
+ from: prev,
2523
+ to: name
2524
+ });
2525
+ }
2526
+ }
2527
+ /** @internal — current transaction (or undefined). */
2528
+ getCurrentTransaction() {
2529
+ return this.currentTransaction ?? void 0;
2530
+ }
2531
+ /** @internal — set transaction without emitting a breadcrumb. */
2532
+ __setTransactionSilent(name) {
2533
+ this.currentTransaction = name && name.length > 0 ? name : null;
2534
+ }
2119
2535
  clearBreadcrumbs() {
2120
2536
  this.breadcrumbs = [];
2121
2537
  }
@@ -2396,9 +2812,9 @@ var AllStak = {
2396
2812
  return instance;
2397
2813
  }
2398
2814
  },
2399
- captureException(error, context) {
2815
+ captureException(error, context, opts) {
2400
2816
  try {
2401
- maybeInit()?.captureException(error, context);
2817
+ maybeInit()?.captureException(error, context, opts);
2402
2818
  } catch {
2403
2819
  }
2404
2820
  },
@@ -2481,6 +2897,27 @@ var AllStak = {
2481
2897
  } catch {
2482
2898
  }
2483
2899
  },
2900
+ /** Set the current screen / transaction name. Stamps event.transaction + emits nav breadcrumb. */
2901
+ setCurrentScreen(name) {
2902
+ try {
2903
+ maybeInit()?.setCurrentScreen(name);
2904
+ } catch {
2905
+ }
2906
+ },
2907
+ getCurrentTransaction() {
2908
+ try {
2909
+ return maybeInit()?.getCurrentTransaction();
2910
+ } catch {
2911
+ return void 0;
2912
+ }
2913
+ },
2914
+ /** @internal — set transaction without emitting a breadcrumb (nav auto-instrument uses this). */
2915
+ __setTransactionSilent(name) {
2916
+ try {
2917
+ maybeInit()?.__setTransactionSilent(name);
2918
+ } catch {
2919
+ }
2920
+ },
2484
2921
  /**
2485
2922
  * Run `callback` with a fresh scoped context. Any user/tag/extra/context/
2486
2923
  * fingerprint/level set on the passed `Scope` is visible only inside the
@@ -2570,6 +3007,29 @@ var AllStak = {
2570
3007
  return instance;
2571
3008
  }
2572
3009
  };
3010
+ function matchUrlPattern(url, p) {
3011
+ if (!url || !p) return false;
3012
+ if (typeof p === "string") return url.includes(p);
3013
+ try {
3014
+ return p.test(url);
3015
+ } catch {
3016
+ return false;
3017
+ }
3018
+ }
3019
+ function scrubObject(obj, keys) {
3020
+ if (!obj || keys.length === 0) return obj;
3021
+ const out = {};
3022
+ const lower = new Set(keys.map((k) => k.toLowerCase()));
3023
+ for (const [k, v] of Object.entries(obj)) {
3024
+ out[k] = lower.has(k.toLowerCase()) ? "[Filtered]" : v;
3025
+ }
3026
+ return out;
3027
+ }
3028
+ function generateEventId() {
3029
+ const hex = (n) => Math.floor(Math.random() * n).toString(16).padStart(1, "0");
3030
+ const seg = (len) => Array.from({ length: len }, () => hex(16)).join("");
3031
+ return `${seg(8)}-${seg(4)}-4${seg(3)}-${(8 + Math.floor(Math.random() * 4)).toString(16)}${seg(3)}-${seg(12)}`;
3032
+ }
2573
3033
  function byteSize(value) {
2574
3034
  if (!value) return 0;
2575
3035
  try {
@@ -2774,6 +3234,10 @@ function instrumentReactNavigation(navigationRef, options = {}) {
2774
3234
  );
2775
3235
  } catch {
2776
3236
  }
3237
+ try {
3238
+ AllStak.__setTransactionSilent?.(next);
3239
+ } catch {
3240
+ }
2777
3241
  if (forwardToReplay) {
2778
3242
  try {
2779
3243
  const replay = AllStak.getReplay?.();
@@ -3013,7 +3477,7 @@ function installReactNative(options = {}) {
3013
3477
  AllStak.captureException(error, {
3014
3478
  source: "react-native-ErrorUtils",
3015
3479
  fatal: String(Boolean(isFatal))
3016
- });
3480
+ }, { mechanism: "onerror", handled: false });
3017
3481
  } catch {
3018
3482
  }
3019
3483
  try {
@@ -3027,7 +3491,11 @@ function installReactNative(options = {}) {
3027
3491
  const wrapTrackerReason = (rejection) => rejection instanceof Error ? rejection : new Error(`Unhandled promise rejection: ${String(rejection)}`);
3028
3492
  const ship = (err) => {
3029
3493
  try {
3030
- AllStak.captureException(err, { source: "unhandledRejection" });
3494
+ AllStak.captureException(
3495
+ err,
3496
+ { source: "unhandledRejection" },
3497
+ { mechanism: "onunhandledrejection", handled: false }
3498
+ );
3031
3499
  } catch {
3032
3500
  }
3033
3501
  };
@@ -3065,8 +3533,13 @@ function installReactNative(options = {}) {
3065
3533
  }
3066
3534
 
3067
3535
  // src/provider.tsx
3068
- var RN2 = tryRequire("react-native");
3069
- var RootView = RN2?.View ?? ((props) => React2.createElement("View", props));
3536
+ function getRN2() {
3537
+ return tryRequire("react-native");
3538
+ }
3539
+ function getRootView() {
3540
+ const RN = getRN2();
3541
+ return RN?.View;
3542
+ }
3070
3543
  var AllStakContext = React2.createContext(null);
3071
3544
  var __providerOwnedInstance = null;
3072
3545
  var AllStakErrorBoundary = class extends React2.Component {
@@ -3086,7 +3559,7 @@ var AllStakErrorBoundary = class extends React2.Component {
3086
3559
  AllStak.captureException(error, {
3087
3560
  componentStack: info.componentStack ?? "",
3088
3561
  source: "AllStakProvider.ErrorBoundary"
3089
- });
3562
+ }, { mechanism: "errorboundary", handled: true });
3090
3563
  if (this.props.debug) {
3091
3564
  console.log(`[AllStak] Captured render error: ${error.message}`);
3092
3565
  }
@@ -3204,6 +3677,10 @@ function AllStakProvider({
3204
3677
  };
3205
3678
  clientRef.current = AllStak.init(config);
3206
3679
  __providerOwnedInstance = clientRef.current;
3680
+ try {
3681
+ AllStak.addBreadcrumb("default", "app.start", "info", { source: "AllStakProvider" });
3682
+ } catch {
3683
+ }
3207
3684
  installReactNative({
3208
3685
  autoErrorHandler,
3209
3686
  autoPromiseRejections,
@@ -3231,7 +3708,8 @@ function AllStakProvider({
3231
3708
  };
3232
3709
  }, [destroyOnUnmount, debug]);
3233
3710
  const boundary = /* @__PURE__ */ React2.createElement(AllStakErrorBoundary, { fallback, onError, debug }, children);
3234
- const wrapped = RN2 ? React2.createElement(
3711
+ const RootView = getRootView();
3712
+ const wrapped = RootView ? React2.createElement(
3235
3713
  RootView,
3236
3714
  { ref: rootRef, style: { flex: 1 }, collapsable: false },
3237
3715
  boundary
@@ -3296,7 +3774,7 @@ async function drainPendingNativeCrashes(release) {
3296
3774
  AllStak.captureException(err, {
3297
3775
  ...payload?.metadata || {},
3298
3776
  "native.crash": "true"
3299
- });
3777
+ }, { mechanism: "native_crash", handled: false });
3300
3778
  } catch {
3301
3779
  }
3302
3780
  }
@@ -3331,23 +3809,30 @@ export {
3331
3809
  __resetRuntimeModeForTest,
3332
3810
  __setNativeModuleForTest,
3333
3811
  applyArchitectureTags,
3812
+ buildExceptionChain,
3813
+ buildUserContext,
3334
3814
  captureBodyResult,
3335
3815
  captureViaViewShot,
3816
+ classifyHttpError,
3817
+ collectAutoContexts,
3336
3818
  detectArchitecture,
3337
3819
  detectRuntimeMode,
3338
3820
  drainPendingNativeCrashes,
3821
+ extractAxiosRequest,
3339
3822
  installReactNative,
3340
3823
  instrumentNavigationFromLinking,
3341
3824
  instrumentReactNavigation,
3342
3825
  isCapturingScreenshot,
3343
3826
  isViewShotAvailable,
3344
3827
  maybeCaptureScreenshot,
3828
+ maybeExtractHttpRequest,
3345
3829
  pickScreenshotConfig,
3346
3830
  redactUrl,
3347
3831
  registerSensitiveRef,
3348
3832
  resolveScreenshotConfig,
3349
3833
  runtimeAllowsScreenshot,
3350
3834
  sanitizeHeaders,
3835
+ sanitizeUrl,
3351
3836
  tryAutoInstrumentNavigation,
3352
3837
  useAllStak,
3353
3838
  useAllStakPrivacy