@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/CHANGELOG.md +61 -0
- package/dist/index.d.mts +245 -4
- package/dist/index.d.ts +245 -4
- package/dist/index.js +473 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +473 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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.
|
|
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 =
|
|
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
|
-
|
|
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(
|
|
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
|
}
|