@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/CHANGELOG.md +76 -0
- package/dist/index.d.mts +245 -4
- package/dist/index.d.ts +245 -4
- package/dist/index.js +504 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +504 -19
- 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
|
|
@@ -1559,7 +1566,9 @@ function tryRequire(id2) {
|
|
|
1559
1566
|
|
|
1560
1567
|
// src/privacy.tsx
|
|
1561
1568
|
var React = __toESM(require("react"));
|
|
1562
|
-
|
|
1569
|
+
function getRN() {
|
|
1570
|
+
return tryRequire("react-native");
|
|
1571
|
+
}
|
|
1563
1572
|
var state = {
|
|
1564
1573
|
isCapturing: false,
|
|
1565
1574
|
sensitiveRefs: /* @__PURE__ */ new Set()
|
|
@@ -1607,9 +1616,18 @@ function useAllStakPrivacy() {
|
|
|
1607
1616
|
const isCapturing = useIsCapturing();
|
|
1608
1617
|
return { isCapturing, registerSensitiveRef };
|
|
1609
1618
|
}
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1619
|
+
function getView() {
|
|
1620
|
+
const RN = getRN();
|
|
1621
|
+
return RN?.View ?? ((props) => React.createElement(React.Fragment, null, props.children));
|
|
1622
|
+
}
|
|
1623
|
+
function getText() {
|
|
1624
|
+
const RN = getRN();
|
|
1625
|
+
return RN?.Text ?? ((props) => React.createElement(React.Fragment, null, props.children));
|
|
1626
|
+
}
|
|
1627
|
+
function getTextInput() {
|
|
1628
|
+
const RN = getRN();
|
|
1629
|
+
return RN?.TextInput ?? ((props) => React.createElement(React.Fragment, null, null));
|
|
1630
|
+
}
|
|
1613
1631
|
var DEFAULT_MASK_COLOR = "#d8dde7";
|
|
1614
1632
|
var DEFAULT_MASK_LABEL = "\u2022\u2022\u2022\u2022\u2022\u2022";
|
|
1615
1633
|
function AllStakMaskedView({
|
|
@@ -1622,6 +1640,8 @@ function AllStakMaskedView({
|
|
|
1622
1640
|
...rest
|
|
1623
1641
|
}) {
|
|
1624
1642
|
const isCapturing = useIsCapturing();
|
|
1643
|
+
const View = getView();
|
|
1644
|
+
const Text = getText();
|
|
1625
1645
|
if (!isCapturing || privacy === "show") {
|
|
1626
1646
|
return React.createElement(View, { style, ...rest }, children);
|
|
1627
1647
|
}
|
|
@@ -1644,6 +1664,8 @@ function AllStakTextInput({
|
|
|
1644
1664
|
...rest
|
|
1645
1665
|
}) {
|
|
1646
1666
|
const isCapturing = useIsCapturing();
|
|
1667
|
+
const View = getView();
|
|
1668
|
+
const TextInput = getTextInput();
|
|
1647
1669
|
if (isCapturing && privacy !== "show") {
|
|
1648
1670
|
return React.createElement(View, {
|
|
1649
1671
|
style: [{ minHeight: 40, backgroundColor: maskColor, borderRadius: 4 }, style]
|
|
@@ -1659,6 +1681,7 @@ function AllStakSensitiveText({
|
|
|
1659
1681
|
...rest
|
|
1660
1682
|
}) {
|
|
1661
1683
|
const isCapturing = useIsCapturing();
|
|
1684
|
+
const Text = getText();
|
|
1662
1685
|
if (isCapturing && privacy !== "show") {
|
|
1663
1686
|
return React.createElement(Text, { style, ...rest }, maskLabel);
|
|
1664
1687
|
}
|
|
@@ -1760,8 +1783,8 @@ function estimateBase64Size(base64) {
|
|
|
1760
1783
|
}
|
|
1761
1784
|
function readDimensions() {
|
|
1762
1785
|
try {
|
|
1763
|
-
const
|
|
1764
|
-
const dims =
|
|
1786
|
+
const RN = tryRequire("react-native");
|
|
1787
|
+
const dims = RN?.Dimensions?.get?.("window");
|
|
1765
1788
|
if (dims && typeof dims.width === "number" && typeof dims.height === "number") {
|
|
1766
1789
|
return { width: Math.round(dims.width), height: Math.round(dims.height) };
|
|
1767
1790
|
}
|
|
@@ -1844,15 +1867,325 @@ function pickScreenshotConfig(source) {
|
|
|
1844
1867
|
return out;
|
|
1845
1868
|
}
|
|
1846
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
|
+
|
|
1847
2180
|
// src/client.ts
|
|
1848
2181
|
var INGEST_HOST = "https://api.allstak.sa";
|
|
1849
2182
|
var SDK_NAME = "allstak-react-native";
|
|
1850
|
-
var SDK_VERSION = "0.
|
|
2183
|
+
var SDK_VERSION = "0.5.0";
|
|
1851
2184
|
var ERRORS_PATH = "/ingest/v1/errors";
|
|
1852
2185
|
var LOGS_PATH = "/ingest/v1/logs";
|
|
1853
2186
|
var VALID_BREADCRUMB_TYPES = /* @__PURE__ */ new Set(["http", "log", "ui", "navigation", "query", "default"]);
|
|
1854
2187
|
var VALID_BREADCRUMB_LEVELS = /* @__PURE__ */ new Set(["info", "warn", "error", "debug"]);
|
|
1855
|
-
var DEFAULT_MAX_BREADCRUMBS =
|
|
2188
|
+
var DEFAULT_MAX_BREADCRUMBS = 100;
|
|
1856
2189
|
function frameToString(f) {
|
|
1857
2190
|
const fn = f.function && f.function.length > 0 ? f.function : "<anonymous>";
|
|
1858
2191
|
const file = f.filename || f.absPath || "<anonymous>";
|
|
@@ -1881,6 +2214,12 @@ var AllStakClient = class {
|
|
|
1881
2214
|
this.replay = null;
|
|
1882
2215
|
this.httpRequests = null;
|
|
1883
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;
|
|
1884
2223
|
this.config = { ...config };
|
|
1885
2224
|
if (!this.config.environment) this.config.environment = "production";
|
|
1886
2225
|
if (!this.config.sdkName) this.config.sdkName = SDK_NAME;
|
|
@@ -1902,6 +2241,18 @@ var AllStakClient = class {
|
|
|
1902
2241
|
} catch {
|
|
1903
2242
|
}
|
|
1904
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
|
+
}
|
|
1905
2256
|
if (config.enableHttpTracking) {
|
|
1906
2257
|
try {
|
|
1907
2258
|
this.httpRequests = new HttpRequestModule(this.transport);
|
|
@@ -1944,7 +2295,7 @@ var AllStakClient = class {
|
|
|
1944
2295
|
return this.httpRequests?.getRecentFailed() ?? [];
|
|
1945
2296
|
}
|
|
1946
2297
|
// ── Public API ────────────────────────────────────────────────────
|
|
1947
|
-
captureException(error, context) {
|
|
2298
|
+
captureException(error, context, opts) {
|
|
1948
2299
|
if (!this.passesSampleRate()) return;
|
|
1949
2300
|
const frames = parseStack(error.stack).map((f) => ({
|
|
1950
2301
|
...f,
|
|
@@ -2026,6 +2377,35 @@ var AllStakClient = class {
|
|
|
2026
2377
|
breadcrumbs: currentBreadcrumbs,
|
|
2027
2378
|
fingerprint: eff.fingerprint
|
|
2028
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
|
+
};
|
|
2029
2409
|
const flatPresent = this.config.captureScreenshotOnError === true;
|
|
2030
2410
|
const callbackPresent = Boolean(this.config.screenshot?.provider);
|
|
2031
2411
|
warnIfBothApisPresent(callbackPresent, flatPresent);
|
|
@@ -2183,16 +2563,59 @@ var AllStakClient = class {
|
|
|
2183
2563
|
}
|
|
2184
2564
|
}
|
|
2185
2565
|
addBreadcrumb(type, message, level, data) {
|
|
2186
|
-
|
|
2566
|
+
let crumb = {
|
|
2187
2567
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2188
2568
|
type: VALID_BREADCRUMB_TYPES.has(type) ? type : "default",
|
|
2189
2569
|
message,
|
|
2190
2570
|
level: level && VALID_BREADCRUMB_LEVELS.has(level) ? level : "info",
|
|
2191
2571
|
...data ? { data } : {}
|
|
2192
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
|
+
}
|
|
2193
2591
|
if (this.breadcrumbs.length >= this.maxBreadcrumbs) this.breadcrumbs.shift();
|
|
2194
2592
|
this.breadcrumbs.push(crumb);
|
|
2195
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
|
+
}
|
|
2196
2619
|
clearBreadcrumbs() {
|
|
2197
2620
|
this.breadcrumbs = [];
|
|
2198
2621
|
}
|
|
@@ -2473,9 +2896,9 @@ var AllStak = {
|
|
|
2473
2896
|
return instance;
|
|
2474
2897
|
}
|
|
2475
2898
|
},
|
|
2476
|
-
captureException(error, context) {
|
|
2899
|
+
captureException(error, context, opts) {
|
|
2477
2900
|
try {
|
|
2478
|
-
maybeInit()?.captureException(error, context);
|
|
2901
|
+
maybeInit()?.captureException(error, context, opts);
|
|
2479
2902
|
} catch {
|
|
2480
2903
|
}
|
|
2481
2904
|
},
|
|
@@ -2558,6 +2981,27 @@ var AllStak = {
|
|
|
2558
2981
|
} catch {
|
|
2559
2982
|
}
|
|
2560
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
|
+
},
|
|
2561
3005
|
/**
|
|
2562
3006
|
* Run `callback` with a fresh scoped context. Any user/tag/extra/context/
|
|
2563
3007
|
* fingerprint/level set on the passed `Scope` is visible only inside the
|
|
@@ -2647,6 +3091,29 @@ var AllStak = {
|
|
|
2647
3091
|
return instance;
|
|
2648
3092
|
}
|
|
2649
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
|
+
}
|
|
2650
3117
|
function byteSize(value) {
|
|
2651
3118
|
if (!value) return 0;
|
|
2652
3119
|
try {
|
|
@@ -2851,6 +3318,10 @@ function instrumentReactNavigation(navigationRef, options = {}) {
|
|
|
2851
3318
|
);
|
|
2852
3319
|
} catch {
|
|
2853
3320
|
}
|
|
3321
|
+
try {
|
|
3322
|
+
AllStak.__setTransactionSilent?.(next);
|
|
3323
|
+
} catch {
|
|
3324
|
+
}
|
|
2854
3325
|
if (forwardToReplay) {
|
|
2855
3326
|
try {
|
|
2856
3327
|
const replay = AllStak.getReplay?.();
|
|
@@ -3090,7 +3561,7 @@ function installReactNative(options = {}) {
|
|
|
3090
3561
|
AllStak.captureException(error, {
|
|
3091
3562
|
source: "react-native-ErrorUtils",
|
|
3092
3563
|
fatal: String(Boolean(isFatal))
|
|
3093
|
-
});
|
|
3564
|
+
}, { mechanism: "onerror", handled: false });
|
|
3094
3565
|
} catch {
|
|
3095
3566
|
}
|
|
3096
3567
|
try {
|
|
@@ -3104,7 +3575,11 @@ function installReactNative(options = {}) {
|
|
|
3104
3575
|
const wrapTrackerReason = (rejection) => rejection instanceof Error ? rejection : new Error(`Unhandled promise rejection: ${String(rejection)}`);
|
|
3105
3576
|
const ship = (err) => {
|
|
3106
3577
|
try {
|
|
3107
|
-
AllStak.captureException(
|
|
3578
|
+
AllStak.captureException(
|
|
3579
|
+
err,
|
|
3580
|
+
{ source: "unhandledRejection" },
|
|
3581
|
+
{ mechanism: "onunhandledrejection", handled: false }
|
|
3582
|
+
);
|
|
3108
3583
|
} catch {
|
|
3109
3584
|
}
|
|
3110
3585
|
};
|
|
@@ -3142,8 +3617,13 @@ function installReactNative(options = {}) {
|
|
|
3142
3617
|
}
|
|
3143
3618
|
|
|
3144
3619
|
// src/provider.tsx
|
|
3145
|
-
|
|
3146
|
-
|
|
3620
|
+
function getRN2() {
|
|
3621
|
+
return tryRequire("react-native");
|
|
3622
|
+
}
|
|
3623
|
+
function getRootView() {
|
|
3624
|
+
const RN = getRN2();
|
|
3625
|
+
return RN?.View;
|
|
3626
|
+
}
|
|
3147
3627
|
var AllStakContext = React2.createContext(null);
|
|
3148
3628
|
var __providerOwnedInstance = null;
|
|
3149
3629
|
var AllStakErrorBoundary = class extends React2.Component {
|
|
@@ -3163,7 +3643,7 @@ var AllStakErrorBoundary = class extends React2.Component {
|
|
|
3163
3643
|
AllStak.captureException(error, {
|
|
3164
3644
|
componentStack: info.componentStack ?? "",
|
|
3165
3645
|
source: "AllStakProvider.ErrorBoundary"
|
|
3166
|
-
});
|
|
3646
|
+
}, { mechanism: "errorboundary", handled: true });
|
|
3167
3647
|
if (this.props.debug) {
|
|
3168
3648
|
console.log(`[AllStak] Captured render error: ${error.message}`);
|
|
3169
3649
|
}
|
|
@@ -3281,6 +3761,10 @@ function AllStakProvider({
|
|
|
3281
3761
|
};
|
|
3282
3762
|
clientRef.current = AllStak.init(config);
|
|
3283
3763
|
__providerOwnedInstance = clientRef.current;
|
|
3764
|
+
try {
|
|
3765
|
+
AllStak.addBreadcrumb("default", "app.start", "info", { source: "AllStakProvider" });
|
|
3766
|
+
} catch {
|
|
3767
|
+
}
|
|
3284
3768
|
installReactNative({
|
|
3285
3769
|
autoErrorHandler,
|
|
3286
3770
|
autoPromiseRejections,
|
|
@@ -3308,7 +3792,8 @@ function AllStakProvider({
|
|
|
3308
3792
|
};
|
|
3309
3793
|
}, [destroyOnUnmount, debug]);
|
|
3310
3794
|
const boundary = /* @__PURE__ */ React2.createElement(AllStakErrorBoundary, { fallback, onError, debug }, children);
|
|
3311
|
-
const
|
|
3795
|
+
const RootView = getRootView();
|
|
3796
|
+
const wrapped = RootView ? React2.createElement(
|
|
3312
3797
|
RootView,
|
|
3313
3798
|
{ ref: rootRef, style: { flex: 1 }, collapsable: false },
|
|
3314
3799
|
boundary
|
|
@@ -3373,7 +3858,7 @@ async function drainPendingNativeCrashes(release) {
|
|
|
3373
3858
|
AllStak.captureException(err, {
|
|
3374
3859
|
...payload?.metadata || {},
|
|
3375
3860
|
"native.crash": "true"
|
|
3376
|
-
});
|
|
3861
|
+
}, { mechanism: "native_crash", handled: false });
|
|
3377
3862
|
} catch {
|
|
3378
3863
|
}
|
|
3379
3864
|
}
|