@allstak/react-native 0.4.1 → 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.js CHANGED
@@ -56,23 +56,30 @@ __export(index_exports, {
56
56
  __resetRuntimeModeForTest: () => __resetRuntimeModeForTest,
57
57
  __setNativeModuleForTest: () => __setNativeModuleForTest,
58
58
  applyArchitectureTags: () => applyArchitectureTags,
59
+ buildExceptionChain: () => buildExceptionChain,
60
+ buildUserContext: () => buildUserContext,
59
61
  captureBodyResult: () => captureBodyResult,
60
62
  captureViaViewShot: () => captureViaViewShot,
63
+ classifyHttpError: () => classifyHttpError,
64
+ collectAutoContexts: () => collectAutoContexts,
61
65
  detectArchitecture: () => detectArchitecture,
62
66
  detectRuntimeMode: () => detectRuntimeMode,
63
67
  drainPendingNativeCrashes: () => drainPendingNativeCrashes,
68
+ extractAxiosRequest: () => extractAxiosRequest,
64
69
  installReactNative: () => installReactNative,
65
70
  instrumentNavigationFromLinking: () => instrumentNavigationFromLinking,
66
71
  instrumentReactNavigation: () => instrumentReactNavigation,
67
72
  isCapturingScreenshot: () => isCapturingScreenshot,
68
73
  isViewShotAvailable: () => isViewShotAvailable,
69
74
  maybeCaptureScreenshot: () => maybeCaptureScreenshot,
75
+ maybeExtractHttpRequest: () => maybeExtractHttpRequest,
70
76
  pickScreenshotConfig: () => pickScreenshotConfig,
71
77
  redactUrl: () => redactUrl,
72
78
  registerSensitiveRef: () => registerSensitiveRef,
73
79
  resolveScreenshotConfig: () => resolveScreenshotConfig,
74
80
  runtimeAllowsScreenshot: () => runtimeAllowsScreenshot,
75
81
  sanitizeHeaders: () => sanitizeHeaders,
82
+ sanitizeUrl: () => sanitizeUrl,
76
83
  tryAutoInstrumentNavigation: () => tryAutoInstrumentNavigation,
77
84
  useAllStak: () => useAllStak,
78
85
  useAllStakPrivacy: () => useAllStakPrivacy
@@ -1860,15 +1867,325 @@ function pickScreenshotConfig(source) {
1860
1867
  return out;
1861
1868
  }
1862
1869
 
1870
+ // src/contexts.ts
1871
+ function tryReq(id2) {
1872
+ try {
1873
+ return require(id2);
1874
+ } catch {
1875
+ return null;
1876
+ }
1877
+ }
1878
+ function defaultExport(mod) {
1879
+ if (!mod) return null;
1880
+ return mod.default ?? mod;
1881
+ }
1882
+ function strOrUndef(v) {
1883
+ if (v == null) return void 0;
1884
+ const s = String(v);
1885
+ return s.length > 0 ? s : void 0;
1886
+ }
1887
+ function collectAutoContexts(opts = {}) {
1888
+ const captureDev = opts.captureDeviceContext !== false;
1889
+ const captureScreen = opts.captureScreenContext !== false;
1890
+ const contexts = {};
1891
+ const tags = {};
1892
+ const RN = tryReq("react-native");
1893
+ const Platform = RN?.Platform;
1894
+ const Dimensions = RN?.Dimensions;
1895
+ const NativeModules = RN?.NativeModules;
1896
+ const osName = strOrUndef(Platform?.OS);
1897
+ const osVersion = strOrUndef(Platform?.Version);
1898
+ const osConstants = Platform?.constants ?? {};
1899
+ if (osName) {
1900
+ contexts.os = {
1901
+ name: osName === "ios" ? "iOS" : osName === "android" ? "Android" : osName,
1902
+ version: osVersion,
1903
+ build: strOrUndef(osConstants?.osBuildId ?? osConstants?.Release)
1904
+ };
1905
+ tags["os.name"] = osName;
1906
+ if (osVersion) tags["os.version"] = osVersion;
1907
+ }
1908
+ const g = globalThis;
1909
+ const hermes = typeof g.HermesInternal !== "undefined";
1910
+ const fabric = typeof g.__turboModuleProxy !== "undefined";
1911
+ const turboModules = fabric;
1912
+ const bridgeless = typeof g.RN$Bridgeless !== "undefined" && !!g.RN$Bridgeless;
1913
+ const jsEngine = hermes ? "hermes" : "jsc";
1914
+ tags["js_engine"] = jsEngine;
1915
+ tags["fabric"] = String(fabric);
1916
+ tags["turbo_modules"] = String(turboModules);
1917
+ const hermesVersion = (() => {
1918
+ try {
1919
+ return g.HermesInternal?.getRuntimeProperties?.()?.["OSS Release Version"];
1920
+ } catch {
1921
+ return void 0;
1922
+ }
1923
+ })();
1924
+ contexts.runtime = {
1925
+ name: jsEngine,
1926
+ version: strOrUndef(hermesVersion) ?? "unknown",
1927
+ bridgeless
1928
+ };
1929
+ if (captureDev) {
1930
+ const device = {};
1931
+ if (osConstants?.Model) device.model = String(osConstants.Model);
1932
+ if (osConstants?.Brand) device.brand = String(osConstants.Brand);
1933
+ if (osConstants?.Manufacturer) device.manufacturer = String(osConstants.Manufacturer);
1934
+ if (osConstants?.systemName) device.systemName = String(osConstants.systemName);
1935
+ if (osConstants?.interfaceIdiom) device.family = String(osConstants.interfaceIdiom);
1936
+ const isSim = Boolean(osConstants?.isTesting) || typeof osConstants?.reactNativeVersion === "object" && String(osConstants?.systemName ?? "").toLowerCase().includes("simulator");
1937
+ if (isSim) {
1938
+ device.simulator = true;
1939
+ tags["simulator"] = "true";
1940
+ }
1941
+ const expoDevice = tryReq("expo-device");
1942
+ if (expoDevice) {
1943
+ const ed = defaultExport(expoDevice) ?? expoDevice;
1944
+ if (ed.modelName) device.model = String(ed.modelName);
1945
+ if (ed.brand) device.brand = String(ed.brand);
1946
+ if (ed.manufacturer) device.manufacturer = String(ed.manufacturer);
1947
+ if (ed.deviceYearClass != null) device.yearClass = ed.deviceYearClass;
1948
+ if (ed.totalMemory != null) device.memory_size = ed.totalMemory;
1949
+ if (typeof ed.supportedCpuArchitectures !== "undefined") {
1950
+ const arr = ed.supportedCpuArchitectures;
1951
+ if (Array.isArray(arr) && arr.length > 0) device.arch = String(arr[0]);
1952
+ }
1953
+ if (typeof ed.isDevice === "boolean" && !ed.isDevice) {
1954
+ device.simulator = true;
1955
+ tags["simulator"] = "true";
1956
+ }
1957
+ } else {
1958
+ const dinfo = tryReq("react-native-device-info");
1959
+ if (dinfo) {
1960
+ const d = defaultExport(dinfo) ?? dinfo;
1961
+ try {
1962
+ device.model = device.model ?? d.getModel?.();
1963
+ } catch {
1964
+ }
1965
+ try {
1966
+ device.manufacturer = device.manufacturer ?? d.getManufacturerSync?.();
1967
+ } catch {
1968
+ }
1969
+ try {
1970
+ device.memory_size = device.memory_size ?? d.getTotalMemorySync?.();
1971
+ } catch {
1972
+ }
1973
+ try {
1974
+ const isEm = d.isEmulatorSync?.();
1975
+ if (isEm) {
1976
+ device.simulator = true;
1977
+ tags["simulator"] = "true";
1978
+ }
1979
+ } catch {
1980
+ }
1981
+ }
1982
+ }
1983
+ if (captureScreen && Dimensions && typeof Dimensions.get === "function") {
1984
+ try {
1985
+ const screen = Dimensions.get("screen");
1986
+ if (screen?.width && screen?.height) {
1987
+ device.screen_width_pixels = Math.round(screen.width * (screen.scale ?? 1));
1988
+ device.screen_height_pixels = Math.round(screen.height * (screen.scale ?? 1));
1989
+ device.screen_density = screen.scale;
1990
+ device.orientation = screen.width >= screen.height ? "landscape" : "portrait";
1991
+ tags["app_orientation"] = device.orientation;
1992
+ }
1993
+ } catch {
1994
+ }
1995
+ }
1996
+ if (opts.captureBattery) {
1997
+ const expoBattery = tryReq("expo-battery");
1998
+ if (expoBattery) {
1999
+ try {
2000
+ const eb = defaultExport(expoBattery) ?? expoBattery;
2001
+ device.battery_available = true;
2002
+ if (typeof eb.getBatteryLevelAsync === "function") {
2003
+ }
2004
+ } catch {
2005
+ }
2006
+ }
2007
+ }
2008
+ if (Object.keys(device).length > 0) {
2009
+ contexts.device = device;
2010
+ if (device.model) tags["device.model"] = String(device.model);
2011
+ }
2012
+ }
2013
+ const app = {};
2014
+ app.app_start_time = (/* @__PURE__ */ new Date()).toISOString();
2015
+ const expoApp = tryReq("expo-application");
2016
+ if (expoApp) {
2017
+ const ea = defaultExport(expoApp) ?? expoApp;
2018
+ if (ea.applicationName) app.app_name = String(ea.applicationName);
2019
+ if (ea.applicationId) app.app_identifier = String(ea.applicationId);
2020
+ if (ea.nativeBuildVersion) app.app_build = String(ea.nativeBuildVersion);
2021
+ if (ea.nativeApplicationVersion) app.app_version = String(ea.nativeApplicationVersion);
2022
+ } else {
2023
+ const dinfo = tryReq("react-native-device-info");
2024
+ if (dinfo) {
2025
+ const d = defaultExport(dinfo) ?? dinfo;
2026
+ try {
2027
+ app.app_name = d.getApplicationName?.();
2028
+ } catch {
2029
+ }
2030
+ try {
2031
+ app.app_identifier = d.getBundleId?.();
2032
+ } catch {
2033
+ }
2034
+ try {
2035
+ app.app_build = d.getBuildNumber?.();
2036
+ } catch {
2037
+ }
2038
+ try {
2039
+ app.app_version = d.getVersion?.();
2040
+ } catch {
2041
+ }
2042
+ }
2043
+ }
2044
+ if (Object.keys(app).length > 0) {
2045
+ contexts.app = app;
2046
+ if (app.app_version) tags["app.version"] = String(app.app_version);
2047
+ if (app.app_build) tags["app.build"] = String(app.app_build);
2048
+ }
2049
+ const rn = {
2050
+ hermes,
2051
+ fabric,
2052
+ turbo_modules: turboModules,
2053
+ bridgeless,
2054
+ js_engine: jsEngine
2055
+ };
2056
+ if (osConstants?.reactNativeVersion) {
2057
+ const v = osConstants.reactNativeVersion;
2058
+ if (typeof v === "object" && v.major != null) {
2059
+ rn.react_native_version = `${v.major}.${v.minor ?? 0}.${v.patch ?? 0}`;
2060
+ } else if (typeof v === "string") {
2061
+ rn.react_native_version = v;
2062
+ }
2063
+ }
2064
+ const expoConstants = tryReq("expo-constants");
2065
+ if (expoConstants) {
2066
+ const ec = defaultExport(expoConstants) ?? expoConstants;
2067
+ if (ec.expoVersion) rn.expo = ec.expoVersion;
2068
+ else if (ec.expoConfig?.sdkVersion) rn.expo = ec.expoConfig.sdkVersion;
2069
+ if (ec.appOwnership) rn.expo_application_ownership = String(ec.appOwnership);
2070
+ if (ec.executionEnvironment) rn.expo_execution_environment = String(ec.executionEnvironment);
2071
+ } else {
2072
+ rn.expo = false;
2073
+ }
2074
+ contexts.react_native = rn;
2075
+ if (rn.expo && rn.expo !== false) tags["expo"] = "true";
2076
+ if (rn.react_native_version) tags["rn.version"] = String(rn.react_native_version);
2077
+ return { contexts, tags };
2078
+ }
2079
+ function buildUserContext(user, opts = {}) {
2080
+ if (!user) return void 0;
2081
+ const out = {};
2082
+ if (user.id) out.id = user.id;
2083
+ if (user.username) out.username = user.username;
2084
+ if (opts.sendDefaultPii) {
2085
+ if (user.email) out.email = user.email;
2086
+ if (user.ip_address) out.ip_address = user.ip_address;
2087
+ }
2088
+ return Object.keys(out).length > 0 ? out : void 0;
2089
+ }
2090
+
2091
+ // src/mechanism.ts
2092
+ var URL_QUERY_RE = /\?.*$/;
2093
+ function sanitizeUrl(url) {
2094
+ if (!url) return "";
2095
+ const s = String(url).replace(URL_QUERY_RE, "");
2096
+ return s;
2097
+ }
2098
+ function exceptionClassName(err) {
2099
+ if (err && typeof err === "object") {
2100
+ const e = err;
2101
+ const fromName = e.name && e.name !== "Error" ? e.name : void 0;
2102
+ return fromName ?? e.constructor?.name ?? "Error";
2103
+ }
2104
+ return "Error";
2105
+ }
2106
+ function exceptionValue(err) {
2107
+ if (err == null) return "";
2108
+ if (err instanceof Error) return err.message ?? "";
2109
+ if (typeof err === "string") return err;
2110
+ try {
2111
+ return JSON.stringify(err);
2112
+ } catch {
2113
+ return String(err);
2114
+ }
2115
+ }
2116
+ function buildExceptionChain(err, mechanism, handled) {
2117
+ const values = [];
2118
+ const seen = /* @__PURE__ */ new Set();
2119
+ let cursor = err;
2120
+ let depth = 0;
2121
+ while (cursor && depth < 5 && !seen.has(cursor)) {
2122
+ seen.add(cursor);
2123
+ const e = cursor;
2124
+ const frames = parseStack(e?.stack);
2125
+ values.push({
2126
+ type: exceptionClassName(e),
2127
+ value: exceptionValue(e),
2128
+ stacktrace: frames.length > 0 ? { frames } : void 0,
2129
+ // Only the outermost exception carries the mechanism, like Sentry.
2130
+ ...depth === 0 ? { mechanism: { type: mechanism, handled } } : {}
2131
+ });
2132
+ cursor = e?.cause;
2133
+ depth += 1;
2134
+ }
2135
+ return values.reverse();
2136
+ }
2137
+ function extractAxiosRequest(err) {
2138
+ if (!err || typeof err !== "object") return null;
2139
+ const e = err;
2140
+ if (e.isAxiosError !== true) return null;
2141
+ const cfg = e.config ?? {};
2142
+ const resp = e.response ?? null;
2143
+ const method = strUpper(cfg.method);
2144
+ const rawUrl = composeAxiosUrl(cfg);
2145
+ const url_sanitized = sanitizeUrl(rawUrl);
2146
+ const status_code = typeof resp?.status === "number" ? resp.status : void 0;
2147
+ const duration_ms = typeof e.duration === "number" ? e.duration : void 0;
2148
+ const category = classifyHttpError(e, status_code);
2149
+ return {
2150
+ method,
2151
+ url_sanitized,
2152
+ status_code,
2153
+ duration_ms,
2154
+ category
2155
+ };
2156
+ }
2157
+ function composeAxiosUrl(cfg) {
2158
+ const base = typeof cfg?.baseURL === "string" ? cfg.baseURL.replace(/\/$/, "") : "";
2159
+ const path = typeof cfg?.url === "string" ? cfg.url : "";
2160
+ if (!base && !path) return "";
2161
+ if (path.startsWith("http://") || path.startsWith("https://")) return path;
2162
+ return base + path;
2163
+ }
2164
+ function strUpper(v) {
2165
+ if (typeof v !== "string" || v.length === 0) return void 0;
2166
+ return v.toUpperCase();
2167
+ }
2168
+ function classifyHttpError(err, status) {
2169
+ const code = typeof err?.code === "string" ? err.code.toUpperCase() : "";
2170
+ if (code === "ECONNABORTED" || code === "ETIMEDOUT") return "timeout";
2171
+ if (code === "ERR_CANCELED" || code === "CANCELED") return "cancel";
2172
+ if (typeof status === "number" && status >= 500) return "http_server_error";
2173
+ if (typeof status === "number" && status >= 400) return "http_client_error";
2174
+ return "network";
2175
+ }
2176
+ function maybeExtractHttpRequest(err) {
2177
+ return extractAxiosRequest(err);
2178
+ }
2179
+
1863
2180
  // src/client.ts
1864
2181
  var INGEST_HOST = "https://api.allstak.sa";
1865
2182
  var SDK_NAME = "allstak-react-native";
1866
- var SDK_VERSION = "0.4.1";
2183
+ var SDK_VERSION = "0.5.0";
1867
2184
  var ERRORS_PATH = "/ingest/v1/errors";
1868
2185
  var LOGS_PATH = "/ingest/v1/logs";
1869
2186
  var VALID_BREADCRUMB_TYPES = /* @__PURE__ */ new Set(["http", "log", "ui", "navigation", "query", "default"]);
1870
2187
  var VALID_BREADCRUMB_LEVELS = /* @__PURE__ */ new Set(["info", "warn", "error", "debug"]);
1871
- var DEFAULT_MAX_BREADCRUMBS = 50;
2188
+ var DEFAULT_MAX_BREADCRUMBS = 100;
1872
2189
  function frameToString(f) {
1873
2190
  const fn = f.function && f.function.length > 0 ? f.function : "<anonymous>";
1874
2191
  const file = f.filename || f.absPath || "<anonymous>";
@@ -1897,6 +2214,12 @@ var AllStakClient = class {
1897
2214
  this.replay = null;
1898
2215
  this.httpRequests = null;
1899
2216
  this._instrumentAxios = null;
2217
+ /** Auto-collected Sentry-shape contexts (device/os/app/react_native/runtime). */
2218
+ this.autoContexts = {};
2219
+ /** Auto-collected tags (device.model, os.name, js_engine, …). */
2220
+ this.autoTags = {};
2221
+ /** Current screen / transaction name — set via setCurrentScreen() or nav auto-instrument. */
2222
+ this.currentTransaction = null;
1900
2223
  this.config = { ...config };
1901
2224
  if (!this.config.environment) this.config.environment = "production";
1902
2225
  if (!this.config.sdkName) this.config.sdkName = SDK_NAME;
@@ -1918,6 +2241,18 @@ var AllStakClient = class {
1918
2241
  } catch {
1919
2242
  }
1920
2243
  }
2244
+ try {
2245
+ const opts = {
2246
+ captureDeviceContext: config.captureDeviceContext !== false,
2247
+ captureBattery: config.captureBattery === true,
2248
+ captureScreenContext: config.captureScreenContext !== false,
2249
+ sendDefaultPii: config.sendDefaultPii === true
2250
+ };
2251
+ const { contexts, tags } = collectAutoContexts(opts);
2252
+ this.autoContexts = contexts;
2253
+ this.autoTags = tags;
2254
+ } catch {
2255
+ }
1921
2256
  if (config.enableHttpTracking) {
1922
2257
  try {
1923
2258
  this.httpRequests = new HttpRequestModule(this.transport);
@@ -1960,7 +2295,7 @@ var AllStakClient = class {
1960
2295
  return this.httpRequests?.getRecentFailed() ?? [];
1961
2296
  }
1962
2297
  // ── Public API ────────────────────────────────────────────────────
1963
- captureException(error, context) {
2298
+ captureException(error, context, opts) {
1964
2299
  if (!this.passesSampleRate()) return;
1965
2300
  const frames = parseStack(error.stack).map((f) => ({
1966
2301
  ...f,
@@ -2042,6 +2377,35 @@ var AllStakClient = class {
2042
2377
  breadcrumbs: currentBreadcrumbs,
2043
2378
  fingerprint: eff.fingerprint
2044
2379
  };
2380
+ const mechanism = opts?.mechanism ?? "captureException";
2381
+ const handled = opts?.handled ?? (mechanism === "captureException" || mechanism === "errorboundary");
2382
+ payload.eventId = generateEventId();
2383
+ payload.timestamp = (/* @__PURE__ */ new Date()).toISOString();
2384
+ payload.handled = handled;
2385
+ payload.mechanism = mechanism;
2386
+ if (this.currentTransaction) payload.transaction = this.currentTransaction;
2387
+ payload.exception = { values: buildExceptionChain(error, mechanism, handled) };
2388
+ const req = maybeExtractHttpRequest(error);
2389
+ if (req) payload.request = req;
2390
+ const userCtx = buildUserContext(eff.user, { sendDefaultPii: this.config.sendDefaultPii });
2391
+ const traceCtx = {};
2392
+ if (payload.traceId) traceCtx.trace_id = payload.traceId;
2393
+ if (payload.spanId) traceCtx.span_id = payload.spanId;
2394
+ if (payload.parentSpanId) traceCtx.parent_span_id = payload.parentSpanId;
2395
+ payload.contexts = {
2396
+ ...this.autoContexts,
2397
+ ...eff.contexts ?? {},
2398
+ ...userCtx ? { user: userCtx } : {},
2399
+ ...Object.keys(traceCtx).length > 0 ? { trace: traceCtx } : {}
2400
+ };
2401
+ payload.tags = {
2402
+ ...this.autoTags,
2403
+ ...this.config.tags ?? {},
2404
+ ...eff.tags ?? {},
2405
+ environment: this.config.environment ?? "production",
2406
+ ...this.config.release ? { release: this.config.release } : {},
2407
+ ...this.config.dist ? { dist: this.config.dist } : {}
2408
+ };
2045
2409
  const flatPresent = this.config.captureScreenshotOnError === true;
2046
2410
  const callbackPresent = Boolean(this.config.screenshot?.provider);
2047
2411
  warnIfBothApisPresent(callbackPresent, flatPresent);
@@ -2199,16 +2563,59 @@ var AllStakClient = class {
2199
2563
  }
2200
2564
  }
2201
2565
  addBreadcrumb(type, message, level, data) {
2202
- const crumb = {
2566
+ let crumb = {
2203
2567
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2204
2568
  type: VALID_BREADCRUMB_TYPES.has(type) ? type : "default",
2205
2569
  message,
2206
2570
  level: level && VALID_BREADCRUMB_LEVELS.has(level) ? level : "info",
2207
2571
  ...data ? { data } : {}
2208
2572
  };
2573
+ if (crumb.type === "http" && (this.config.denyUrls || this.config.allowUrls)) {
2574
+ const url = typeof crumb.data?.url === "string" ? crumb.data.url : "";
2575
+ if (url) {
2576
+ if (this.config.denyUrls && this.config.denyUrls.some((p) => matchUrlPattern(url, p))) return;
2577
+ if (this.config.allowUrls && this.config.allowUrls.length > 0 && !this.config.allowUrls.some((p) => matchUrlPattern(url, p))) return;
2578
+ }
2579
+ }
2580
+ if (crumb.data && this.config.scrubKeys && this.config.scrubKeys.length > 0) {
2581
+ crumb = { ...crumb, data: scrubObject(crumb.data, this.config.scrubKeys) };
2582
+ }
2583
+ if (this.config.beforeBreadcrumb) {
2584
+ try {
2585
+ const out = this.config.beforeBreadcrumb(crumb);
2586
+ if (out === null) return;
2587
+ if (out) crumb = out;
2588
+ } catch {
2589
+ }
2590
+ }
2209
2591
  if (this.breadcrumbs.length >= this.maxBreadcrumbs) this.breadcrumbs.shift();
2210
2592
  this.breadcrumbs.push(crumb);
2211
2593
  }
2594
+ /**
2595
+ * Set the current screen / route name. Stamps `transaction` on every
2596
+ * subsequent event and emits a `navigation` breadcrumb. Use this when
2597
+ * not on `@react-navigation/native` (the nav auto-instrument calls
2598
+ * this for you).
2599
+ */
2600
+ setCurrentScreen(name) {
2601
+ if (!name) return;
2602
+ const prev = this.currentTransaction;
2603
+ this.currentTransaction = name;
2604
+ if (prev !== name) {
2605
+ this.addBreadcrumb("navigation", `${prev ?? "<start>"} -> ${name}`, "info", {
2606
+ from: prev,
2607
+ to: name
2608
+ });
2609
+ }
2610
+ }
2611
+ /** @internal — current transaction (or undefined). */
2612
+ getCurrentTransaction() {
2613
+ return this.currentTransaction ?? void 0;
2614
+ }
2615
+ /** @internal — set transaction without emitting a breadcrumb. */
2616
+ __setTransactionSilent(name) {
2617
+ this.currentTransaction = name && name.length > 0 ? name : null;
2618
+ }
2212
2619
  clearBreadcrumbs() {
2213
2620
  this.breadcrumbs = [];
2214
2621
  }
@@ -2489,9 +2896,9 @@ var AllStak = {
2489
2896
  return instance;
2490
2897
  }
2491
2898
  },
2492
- captureException(error, context) {
2899
+ captureException(error, context, opts) {
2493
2900
  try {
2494
- maybeInit()?.captureException(error, context);
2901
+ maybeInit()?.captureException(error, context, opts);
2495
2902
  } catch {
2496
2903
  }
2497
2904
  },
@@ -2574,6 +2981,27 @@ var AllStak = {
2574
2981
  } catch {
2575
2982
  }
2576
2983
  },
2984
+ /** Set the current screen / transaction name. Stamps event.transaction + emits nav breadcrumb. */
2985
+ setCurrentScreen(name) {
2986
+ try {
2987
+ maybeInit()?.setCurrentScreen(name);
2988
+ } catch {
2989
+ }
2990
+ },
2991
+ getCurrentTransaction() {
2992
+ try {
2993
+ return maybeInit()?.getCurrentTransaction();
2994
+ } catch {
2995
+ return void 0;
2996
+ }
2997
+ },
2998
+ /** @internal — set transaction without emitting a breadcrumb (nav auto-instrument uses this). */
2999
+ __setTransactionSilent(name) {
3000
+ try {
3001
+ maybeInit()?.__setTransactionSilent(name);
3002
+ } catch {
3003
+ }
3004
+ },
2577
3005
  /**
2578
3006
  * Run `callback` with a fresh scoped context. Any user/tag/extra/context/
2579
3007
  * fingerprint/level set on the passed `Scope` is visible only inside the
@@ -2663,6 +3091,29 @@ var AllStak = {
2663
3091
  return instance;
2664
3092
  }
2665
3093
  };
3094
+ function matchUrlPattern(url, p) {
3095
+ if (!url || !p) return false;
3096
+ if (typeof p === "string") return url.includes(p);
3097
+ try {
3098
+ return p.test(url);
3099
+ } catch {
3100
+ return false;
3101
+ }
3102
+ }
3103
+ function scrubObject(obj, keys) {
3104
+ if (!obj || keys.length === 0) return obj;
3105
+ const out = {};
3106
+ const lower = new Set(keys.map((k) => k.toLowerCase()));
3107
+ for (const [k, v] of Object.entries(obj)) {
3108
+ out[k] = lower.has(k.toLowerCase()) ? "[Filtered]" : v;
3109
+ }
3110
+ return out;
3111
+ }
3112
+ function generateEventId() {
3113
+ const hex = (n) => Math.floor(Math.random() * n).toString(16).padStart(1, "0");
3114
+ const seg = (len) => Array.from({ length: len }, () => hex(16)).join("");
3115
+ return `${seg(8)}-${seg(4)}-4${seg(3)}-${(8 + Math.floor(Math.random() * 4)).toString(16)}${seg(3)}-${seg(12)}`;
3116
+ }
2666
3117
  function byteSize(value) {
2667
3118
  if (!value) return 0;
2668
3119
  try {
@@ -2867,6 +3318,10 @@ function instrumentReactNavigation(navigationRef, options = {}) {
2867
3318
  );
2868
3319
  } catch {
2869
3320
  }
3321
+ try {
3322
+ AllStak.__setTransactionSilent?.(next);
3323
+ } catch {
3324
+ }
2870
3325
  if (forwardToReplay) {
2871
3326
  try {
2872
3327
  const replay = AllStak.getReplay?.();
@@ -3106,7 +3561,7 @@ function installReactNative(options = {}) {
3106
3561
  AllStak.captureException(error, {
3107
3562
  source: "react-native-ErrorUtils",
3108
3563
  fatal: String(Boolean(isFatal))
3109
- });
3564
+ }, { mechanism: "onerror", handled: false });
3110
3565
  } catch {
3111
3566
  }
3112
3567
  try {
@@ -3120,7 +3575,11 @@ function installReactNative(options = {}) {
3120
3575
  const wrapTrackerReason = (rejection) => rejection instanceof Error ? rejection : new Error(`Unhandled promise rejection: ${String(rejection)}`);
3121
3576
  const ship = (err) => {
3122
3577
  try {
3123
- AllStak.captureException(err, { source: "unhandledRejection" });
3578
+ AllStak.captureException(
3579
+ err,
3580
+ { source: "unhandledRejection" },
3581
+ { mechanism: "onunhandledrejection", handled: false }
3582
+ );
3124
3583
  } catch {
3125
3584
  }
3126
3585
  };
@@ -3184,7 +3643,7 @@ var AllStakErrorBoundary = class extends React2.Component {
3184
3643
  AllStak.captureException(error, {
3185
3644
  componentStack: info.componentStack ?? "",
3186
3645
  source: "AllStakProvider.ErrorBoundary"
3187
- });
3646
+ }, { mechanism: "errorboundary", handled: true });
3188
3647
  if (this.props.debug) {
3189
3648
  console.log(`[AllStak] Captured render error: ${error.message}`);
3190
3649
  }
@@ -3302,6 +3761,10 @@ function AllStakProvider({
3302
3761
  };
3303
3762
  clientRef.current = AllStak.init(config);
3304
3763
  __providerOwnedInstance = clientRef.current;
3764
+ try {
3765
+ AllStak.addBreadcrumb("default", "app.start", "info", { source: "AllStakProvider" });
3766
+ } catch {
3767
+ }
3305
3768
  installReactNative({
3306
3769
  autoErrorHandler,
3307
3770
  autoPromiseRejections,
@@ -3395,7 +3858,7 @@ async function drainPendingNativeCrashes(release) {
3395
3858
  AllStak.captureException(err, {
3396
3859
  ...payload?.metadata || {},
3397
3860
  "native.crash": "true"
3398
- });
3861
+ }, { mechanism: "native_crash", handled: false });
3399
3862
  } catch {
3400
3863
  }
3401
3864
  }