@allstak/react-native 0.3.4 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -0
- package/README.md +398 -95
- package/app.plugin.js +1 -3
- package/build-hooks/upload-sourcemaps.js +0 -15
- package/dist/build/sourcemaps.js +2 -18
- package/dist/build/sourcemaps.js.map +1 -1
- package/dist/build/sourcemaps.mjs +4 -20
- package/dist/build/sourcemaps.mjs.map +1 -1
- package/dist/expo-plugin.js +5 -1
- package/dist/expo-plugin.js.map +1 -1
- package/dist/expo-plugin.mjs +5 -1
- package/dist/expo-plugin.mjs.map +1 -1
- package/dist/index.d.mts +237 -3
- package/dist/index.d.ts +237 -3
- package/dist/index.js +578 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +578 -18
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -3
package/dist/index.mjs
CHANGED
|
@@ -29,6 +29,58 @@ var HttpTransport = class {
|
|
|
29
29
|
this.enqueueOrDispatch({ path, payload });
|
|
30
30
|
return Promise.resolve();
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* One-shot POST that resolves with the parsed JSON response body. Used
|
|
34
|
+
* by `captureException` to retrieve the server-assigned event id so
|
|
35
|
+
* follow-up attachment uploads can be linked.
|
|
36
|
+
*
|
|
37
|
+
* Fail-open: returns `null` on any error (network, non-2xx, parse).
|
|
38
|
+
* Respects {@link timeoutMs} via `AbortController`. Bounded retries.
|
|
39
|
+
*/
|
|
40
|
+
async sendAndRead(path, payload, options = {}) {
|
|
41
|
+
if (!this.enabled) return null;
|
|
42
|
+
const timeoutMs = options.timeoutMs ?? 5e3;
|
|
43
|
+
const retries = Math.max(0, options.retries ?? 1);
|
|
44
|
+
let attempt = 0;
|
|
45
|
+
let lastError = null;
|
|
46
|
+
while (attempt <= retries) {
|
|
47
|
+
try {
|
|
48
|
+
const url = `${this.baseUrl}${path}`;
|
|
49
|
+
const controller = new AbortController();
|
|
50
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
51
|
+
try {
|
|
52
|
+
const res = await fetch(url, {
|
|
53
|
+
method: "POST",
|
|
54
|
+
headers: {
|
|
55
|
+
"Content-Type": "application/json",
|
|
56
|
+
"X-AllStak-Key": this.apiKey
|
|
57
|
+
},
|
|
58
|
+
body: JSON.stringify(payload),
|
|
59
|
+
signal: controller.signal
|
|
60
|
+
});
|
|
61
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
62
|
+
const text = await res.text();
|
|
63
|
+
if (!text) return null;
|
|
64
|
+
try {
|
|
65
|
+
return JSON.parse(text);
|
|
66
|
+
} catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
} finally {
|
|
70
|
+
clearTimeout(timeoutId);
|
|
71
|
+
}
|
|
72
|
+
} catch (err) {
|
|
73
|
+
lastError = err;
|
|
74
|
+
attempt += 1;
|
|
75
|
+
if (attempt > retries) break;
|
|
76
|
+
await new Promise((r) => setTimeout(r, 200 * attempt));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (lastError && typeof __DEV__ !== "undefined" && __DEV__) {
|
|
80
|
+
console.warn("[AllStak] sendAndRead failed:", lastError?.message);
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
32
84
|
enqueueOrDispatch(item) {
|
|
33
85
|
if (Date.now() < this.circuitOpenUntil) {
|
|
34
86
|
this.push(item);
|
|
@@ -1383,10 +1435,358 @@ function unbindHttpInstrumentation() {
|
|
|
1383
1435
|
_currentRuntime = null;
|
|
1384
1436
|
}
|
|
1385
1437
|
|
|
1438
|
+
// src/runtime.ts
|
|
1439
|
+
var cached = null;
|
|
1440
|
+
function detectRuntimeMode() {
|
|
1441
|
+
if (cached) return cached;
|
|
1442
|
+
cached = computeRuntimeMode();
|
|
1443
|
+
return cached;
|
|
1444
|
+
}
|
|
1445
|
+
function __resetRuntimeModeForTest() {
|
|
1446
|
+
cached = null;
|
|
1447
|
+
}
|
|
1448
|
+
function computeRuntimeMode() {
|
|
1449
|
+
try {
|
|
1450
|
+
const Constants = require("expo-constants");
|
|
1451
|
+
const appOwnership = Constants?.default?.appOwnership ?? Constants?.appOwnership;
|
|
1452
|
+
if (appOwnership === "expo") return "expo-go";
|
|
1453
|
+
if (appOwnership === "standalone" || appOwnership === "guest") return "expo-dev-client";
|
|
1454
|
+
const exec = Constants?.default?.executionEnvironment ?? Constants?.executionEnvironment;
|
|
1455
|
+
if (exec === "storeClient") return "expo-go";
|
|
1456
|
+
if (exec === "standalone" || exec === "bare") return "expo-dev-client";
|
|
1457
|
+
} catch {
|
|
1458
|
+
}
|
|
1459
|
+
try {
|
|
1460
|
+
require("expo");
|
|
1461
|
+
return "expo-dev-client";
|
|
1462
|
+
} catch {
|
|
1463
|
+
}
|
|
1464
|
+
try {
|
|
1465
|
+
require("react-native");
|
|
1466
|
+
return "rn-cli";
|
|
1467
|
+
} catch {
|
|
1468
|
+
}
|
|
1469
|
+
return "unknown";
|
|
1470
|
+
}
|
|
1471
|
+
function runtimeAllowsScreenshot(mode = detectRuntimeMode()) {
|
|
1472
|
+
if (mode === "expo-go") return false;
|
|
1473
|
+
return true;
|
|
1474
|
+
}
|
|
1475
|
+
function tryRequire(id2) {
|
|
1476
|
+
try {
|
|
1477
|
+
return require(id2);
|
|
1478
|
+
} catch {
|
|
1479
|
+
return null;
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
// src/privacy.tsx
|
|
1484
|
+
import * as React from "react";
|
|
1485
|
+
function getRN() {
|
|
1486
|
+
return tryRequire("react-native");
|
|
1487
|
+
}
|
|
1488
|
+
var state = {
|
|
1489
|
+
isCapturing: false,
|
|
1490
|
+
sensitiveRefs: /* @__PURE__ */ new Set()
|
|
1491
|
+
};
|
|
1492
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
1493
|
+
function __setCapturing(value) {
|
|
1494
|
+
if (state.isCapturing === value) return;
|
|
1495
|
+
state.isCapturing = value;
|
|
1496
|
+
for (const fn of listeners) {
|
|
1497
|
+
try {
|
|
1498
|
+
fn(value);
|
|
1499
|
+
} catch {
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
function __resetPrivacyStateForTest() {
|
|
1504
|
+
state.isCapturing = false;
|
|
1505
|
+
state.sensitiveRefs.clear();
|
|
1506
|
+
listeners.clear();
|
|
1507
|
+
}
|
|
1508
|
+
function isCapturingScreenshot() {
|
|
1509
|
+
return state.isCapturing;
|
|
1510
|
+
}
|
|
1511
|
+
function sensitiveRefCount() {
|
|
1512
|
+
return state.sensitiveRefs.size;
|
|
1513
|
+
}
|
|
1514
|
+
function registerSensitiveRef(ref) {
|
|
1515
|
+
state.sensitiveRefs.add(ref);
|
|
1516
|
+
return () => {
|
|
1517
|
+
state.sensitiveRefs.delete(ref);
|
|
1518
|
+
};
|
|
1519
|
+
}
|
|
1520
|
+
function useIsCapturing() {
|
|
1521
|
+
const [val, setVal] = React.useState(state.isCapturing);
|
|
1522
|
+
React.useEffect(() => {
|
|
1523
|
+
const fn = (v) => setVal(v);
|
|
1524
|
+
listeners.add(fn);
|
|
1525
|
+
return () => {
|
|
1526
|
+
listeners.delete(fn);
|
|
1527
|
+
};
|
|
1528
|
+
}, []);
|
|
1529
|
+
return val;
|
|
1530
|
+
}
|
|
1531
|
+
function useAllStakPrivacy() {
|
|
1532
|
+
const isCapturing = useIsCapturing();
|
|
1533
|
+
return { isCapturing, registerSensitiveRef };
|
|
1534
|
+
}
|
|
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
|
+
}
|
|
1547
|
+
var DEFAULT_MASK_COLOR = "#d8dde7";
|
|
1548
|
+
var DEFAULT_MASK_LABEL = "\u2022\u2022\u2022\u2022\u2022\u2022";
|
|
1549
|
+
function AllStakMaskedView({
|
|
1550
|
+
children,
|
|
1551
|
+
maskLabel = DEFAULT_MASK_LABEL,
|
|
1552
|
+
maskColor = DEFAULT_MASK_COLOR,
|
|
1553
|
+
hideScreenshot = false,
|
|
1554
|
+
privacy = "mask",
|
|
1555
|
+
style,
|
|
1556
|
+
...rest
|
|
1557
|
+
}) {
|
|
1558
|
+
const isCapturing = useIsCapturing();
|
|
1559
|
+
const View = getView();
|
|
1560
|
+
const Text = getText();
|
|
1561
|
+
if (!isCapturing || privacy === "show") {
|
|
1562
|
+
return React.createElement(View, { style, ...rest }, children);
|
|
1563
|
+
}
|
|
1564
|
+
if (privacy === "hide" || hideScreenshot) {
|
|
1565
|
+
return React.createElement(View, { style: [{ backgroundColor: maskColor }, style], ...rest });
|
|
1566
|
+
}
|
|
1567
|
+
return React.createElement(
|
|
1568
|
+
View,
|
|
1569
|
+
{ style: [{ backgroundColor: maskColor, alignItems: "center", justifyContent: "center" }, style], ...rest },
|
|
1570
|
+
React.createElement(Text, { style: { color: "#3f4652", fontSize: 12 } }, maskLabel)
|
|
1571
|
+
);
|
|
1572
|
+
}
|
|
1573
|
+
function AllStakPrivacyView(props) {
|
|
1574
|
+
return React.createElement(AllStakMaskedView, { hideScreenshot: true, ...props });
|
|
1575
|
+
}
|
|
1576
|
+
function AllStakTextInput({
|
|
1577
|
+
privacy = "mask",
|
|
1578
|
+
style,
|
|
1579
|
+
maskColor = DEFAULT_MASK_COLOR,
|
|
1580
|
+
...rest
|
|
1581
|
+
}) {
|
|
1582
|
+
const isCapturing = useIsCapturing();
|
|
1583
|
+
const View = getView();
|
|
1584
|
+
const TextInput = getTextInput();
|
|
1585
|
+
if (isCapturing && privacy !== "show") {
|
|
1586
|
+
return React.createElement(View, {
|
|
1587
|
+
style: [{ minHeight: 40, backgroundColor: maskColor, borderRadius: 4 }, style]
|
|
1588
|
+
});
|
|
1589
|
+
}
|
|
1590
|
+
return React.createElement(TextInput, { style, ...rest });
|
|
1591
|
+
}
|
|
1592
|
+
function AllStakSensitiveText({
|
|
1593
|
+
children,
|
|
1594
|
+
privacy = "mask",
|
|
1595
|
+
style,
|
|
1596
|
+
maskLabel = DEFAULT_MASK_LABEL,
|
|
1597
|
+
...rest
|
|
1598
|
+
}) {
|
|
1599
|
+
const isCapturing = useIsCapturing();
|
|
1600
|
+
const Text = getText();
|
|
1601
|
+
if (isCapturing && privacy !== "show") {
|
|
1602
|
+
return React.createElement(Text, { style, ...rest }, maskLabel);
|
|
1603
|
+
}
|
|
1604
|
+
return React.createElement(Text, { style, ...rest }, children);
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
// src/screenshot.ts
|
|
1608
|
+
var DEFAULT_SCREENSHOT_CONFIG = {
|
|
1609
|
+
captureScreenshotOnError: false,
|
|
1610
|
+
screenshotRedaction: "strict",
|
|
1611
|
+
screenshotMaskStyle: "solid",
|
|
1612
|
+
screenshotMaxBytes: 5e5,
|
|
1613
|
+
screenshotQuality: 0.7,
|
|
1614
|
+
screenshotFormat: "jpg",
|
|
1615
|
+
screenshotSampleRate: 1,
|
|
1616
|
+
screenshotOnUnhandledOnly: true,
|
|
1617
|
+
screenshotUploadTimeoutMs: 8e3,
|
|
1618
|
+
screenshotCaptureTimeoutMs: 2e3,
|
|
1619
|
+
screenshotNativeMode: "auto",
|
|
1620
|
+
screenshotFailPolicy: "send-event-only"
|
|
1621
|
+
};
|
|
1622
|
+
function resolveScreenshotConfig(partial) {
|
|
1623
|
+
const c = { ...DEFAULT_SCREENSHOT_CONFIG, ...partial ?? {} };
|
|
1624
|
+
c.screenshotMaxBytes = clamp(c.screenshotMaxBytes, 1024, 5e6);
|
|
1625
|
+
c.screenshotQuality = clamp(c.screenshotQuality, 0, 1);
|
|
1626
|
+
c.screenshotSampleRate = clamp(c.screenshotSampleRate, 0, 1);
|
|
1627
|
+
c.screenshotUploadTimeoutMs = clamp(c.screenshotUploadTimeoutMs, 500, 6e4);
|
|
1628
|
+
c.screenshotCaptureTimeoutMs = clamp(c.screenshotCaptureTimeoutMs, 100, 3e4);
|
|
1629
|
+
return c;
|
|
1630
|
+
}
|
|
1631
|
+
function clamp(n, lo, hi) {
|
|
1632
|
+
if (typeof n !== "number" || !Number.isFinite(n)) return lo;
|
|
1633
|
+
return Math.max(lo, Math.min(hi, n));
|
|
1634
|
+
}
|
|
1635
|
+
var rootViewRef = null;
|
|
1636
|
+
function __setRootViewRef(ref) {
|
|
1637
|
+
rootViewRef = ref;
|
|
1638
|
+
}
|
|
1639
|
+
var warnedAboutBothApis = false;
|
|
1640
|
+
function warnIfBothApisPresent(callbackPresent, flatPresent) {
|
|
1641
|
+
if (!callbackPresent || !flatPresent || warnedAboutBothApis) return;
|
|
1642
|
+
warnedAboutBothApis = true;
|
|
1643
|
+
console.warn(
|
|
1644
|
+
"[AllStak] Both `screenshot.provider` (deprecated) and the flat `captureScreenshotOnError` API are configured. The flat API takes precedence. Remove `screenshot.provider` to silence this warning."
|
|
1645
|
+
);
|
|
1646
|
+
}
|
|
1647
|
+
function isViewShotAvailable() {
|
|
1648
|
+
return tryRequire("react-native-view-shot") !== null;
|
|
1649
|
+
}
|
|
1650
|
+
async function captureViaViewShot(config) {
|
|
1651
|
+
if (config.screenshotNativeMode === "disabled") return null;
|
|
1652
|
+
const viewShot = tryRequire("react-native-view-shot");
|
|
1653
|
+
if (!viewShot) return null;
|
|
1654
|
+
const captureRef = viewShot.captureRef ?? viewShot.default?.captureRef;
|
|
1655
|
+
if (typeof captureRef !== "function") return null;
|
|
1656
|
+
const refTarget = rootViewRef?.current;
|
|
1657
|
+
if (!refTarget) return null;
|
|
1658
|
+
const format = config.screenshotFormat === "jpg" ? "jpg" : config.screenshotFormat;
|
|
1659
|
+
const dimensions = readDimensions();
|
|
1660
|
+
__setCapturing(true);
|
|
1661
|
+
await new Promise((r) => setTimeout(r, 16));
|
|
1662
|
+
try {
|
|
1663
|
+
const captured = await Promise.race([
|
|
1664
|
+
Promise.resolve(captureRef(refTarget, {
|
|
1665
|
+
format,
|
|
1666
|
+
quality: config.screenshotQuality,
|
|
1667
|
+
result: "base64"
|
|
1668
|
+
})),
|
|
1669
|
+
new Promise((resolve) => setTimeout(() => resolve(null), config.screenshotCaptureTimeoutMs))
|
|
1670
|
+
]);
|
|
1671
|
+
if (!captured || typeof captured !== "string") return null;
|
|
1672
|
+
const sizeBytes = estimateBase64Size(captured);
|
|
1673
|
+
if (sizeBytes > config.screenshotMaxBytes) {
|
|
1674
|
+
if (__DEV__) {
|
|
1675
|
+
console.warn(`[AllStak] Screenshot ${sizeBytes}B exceeds limit ${config.screenshotMaxBytes}B; dropping.`);
|
|
1676
|
+
}
|
|
1677
|
+
return null;
|
|
1678
|
+
}
|
|
1679
|
+
const contentType = format === "png" ? "image/png" : format === "webp" ? "image/webp" : "image/jpeg";
|
|
1680
|
+
return {
|
|
1681
|
+
dataBase64: captured,
|
|
1682
|
+
contentType,
|
|
1683
|
+
width: dimensions.width,
|
|
1684
|
+
height: dimensions.height,
|
|
1685
|
+
sizeBytes
|
|
1686
|
+
};
|
|
1687
|
+
} catch (err) {
|
|
1688
|
+
if (__DEV__) {
|
|
1689
|
+
console.warn("[AllStak] view-shot capture failed:", err?.message);
|
|
1690
|
+
}
|
|
1691
|
+
return null;
|
|
1692
|
+
} finally {
|
|
1693
|
+
__setCapturing(false);
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
function estimateBase64Size(base64) {
|
|
1697
|
+
const padding = base64.endsWith("==") ? 2 : base64.endsWith("=") ? 1 : 0;
|
|
1698
|
+
return Math.floor(base64.length * 3 / 4) - padding;
|
|
1699
|
+
}
|
|
1700
|
+
function readDimensions() {
|
|
1701
|
+
try {
|
|
1702
|
+
const RN = tryRequire("react-native");
|
|
1703
|
+
const dims = RN?.Dimensions?.get?.("window");
|
|
1704
|
+
if (dims && typeof dims.width === "number" && typeof dims.height === "number") {
|
|
1705
|
+
return { width: Math.round(dims.width), height: Math.round(dims.height) };
|
|
1706
|
+
}
|
|
1707
|
+
} catch {
|
|
1708
|
+
}
|
|
1709
|
+
return { width: 0, height: 0 };
|
|
1710
|
+
}
|
|
1711
|
+
async function maybeCaptureScreenshot(config, ctx) {
|
|
1712
|
+
try {
|
|
1713
|
+
if (!config.captureScreenshotOnError) return null;
|
|
1714
|
+
if (config.screenshotOnUnhandledOnly && ctx.unhandled === false) return null;
|
|
1715
|
+
if (config.screenshotSampleRate < 1 && Math.random() >= config.screenshotSampleRate) return null;
|
|
1716
|
+
if (!runtimeAllowsScreenshot(ctx.runtimeMode)) return null;
|
|
1717
|
+
if (config.isScreenshotAllowed) {
|
|
1718
|
+
try {
|
|
1719
|
+
const allowed = await config.isScreenshotAllowed(ctx);
|
|
1720
|
+
if (!allowed) return null;
|
|
1721
|
+
} catch {
|
|
1722
|
+
return null;
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
if (config.beforeScreenshotCapture) {
|
|
1726
|
+
try {
|
|
1727
|
+
const cont = await config.beforeScreenshotCapture(ctx);
|
|
1728
|
+
if (!cont) return null;
|
|
1729
|
+
} catch {
|
|
1730
|
+
return null;
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
const upload = await captureViaViewShot(config);
|
|
1734
|
+
if (!upload) return null;
|
|
1735
|
+
const metadata = {
|
|
1736
|
+
captureMethod: "react-native-view-shot",
|
|
1737
|
+
redactionMode: config.screenshotRedaction,
|
|
1738
|
+
maskStyle: config.screenshotMaskStyle,
|
|
1739
|
+
format: config.screenshotFormat,
|
|
1740
|
+
width: upload.width,
|
|
1741
|
+
height: upload.height,
|
|
1742
|
+
sizeBytes: upload.sizeBytes,
|
|
1743
|
+
privacyComponentsDetected: sensitiveRefCount(),
|
|
1744
|
+
runtimeMode: ctx.runtimeMode
|
|
1745
|
+
};
|
|
1746
|
+
if (config.beforeScreenshotUpload) {
|
|
1747
|
+
try {
|
|
1748
|
+
const filtered = await config.beforeScreenshotUpload(upload, metadata);
|
|
1749
|
+
if (!filtered) return null;
|
|
1750
|
+
return { upload: filtered, metadata };
|
|
1751
|
+
} catch {
|
|
1752
|
+
return null;
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
return { upload, metadata };
|
|
1756
|
+
} catch (err) {
|
|
1757
|
+
if (__DEV__) {
|
|
1758
|
+
console.warn("[AllStak] maybeCaptureScreenshot fail-open:", err?.message);
|
|
1759
|
+
}
|
|
1760
|
+
return null;
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
function pickScreenshotConfig(source) {
|
|
1764
|
+
const out = {};
|
|
1765
|
+
const pick = (key) => {
|
|
1766
|
+
if (source[key] !== void 0) out[key] = source[key];
|
|
1767
|
+
};
|
|
1768
|
+
pick("captureScreenshotOnError");
|
|
1769
|
+
pick("screenshotRedaction");
|
|
1770
|
+
pick("screenshotMaskStyle");
|
|
1771
|
+
pick("screenshotMaxBytes");
|
|
1772
|
+
pick("screenshotQuality");
|
|
1773
|
+
pick("screenshotFormat");
|
|
1774
|
+
pick("screenshotSampleRate");
|
|
1775
|
+
pick("screenshotOnUnhandledOnly");
|
|
1776
|
+
pick("screenshotUploadTimeoutMs");
|
|
1777
|
+
pick("screenshotCaptureTimeoutMs");
|
|
1778
|
+
pick("screenshotNativeMode");
|
|
1779
|
+
pick("screenshotFailPolicy");
|
|
1780
|
+
pick("beforeScreenshotCapture");
|
|
1781
|
+
pick("beforeScreenshotUpload");
|
|
1782
|
+
pick("isScreenshotAllowed");
|
|
1783
|
+
return out;
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1386
1786
|
// src/client.ts
|
|
1387
1787
|
var INGEST_HOST = "https://api.allstak.sa";
|
|
1388
1788
|
var SDK_NAME = "allstak-react-native";
|
|
1389
|
-
var SDK_VERSION = "0.
|
|
1789
|
+
var SDK_VERSION = "0.4.1";
|
|
1390
1790
|
var ERRORS_PATH = "/ingest/v1/errors";
|
|
1391
1791
|
var LOGS_PATH = "/ingest/v1/logs";
|
|
1392
1792
|
var VALID_BREADCRUMB_TYPES = /* @__PURE__ */ new Set(["http", "log", "ui", "navigation", "query", "default"]);
|
|
@@ -1565,6 +1965,18 @@ var AllStakClient = class {
|
|
|
1565
1965
|
breadcrumbs: currentBreadcrumbs,
|
|
1566
1966
|
fingerprint: eff.fingerprint
|
|
1567
1967
|
};
|
|
1968
|
+
const flatPresent = this.config.captureScreenshotOnError === true;
|
|
1969
|
+
const callbackPresent = Boolean(this.config.screenshot?.provider);
|
|
1970
|
+
warnIfBothApisPresent(callbackPresent, flatPresent);
|
|
1971
|
+
if (flatPresent) {
|
|
1972
|
+
void this.runFlatScreenshotPipeline(error, payload).catch(() => {
|
|
1973
|
+
void this.sendThroughBeforeSend({
|
|
1974
|
+
...payload,
|
|
1975
|
+
metadata: { ...payload.metadata ?? {}, "screenshot.status": "failed" }
|
|
1976
|
+
});
|
|
1977
|
+
});
|
|
1978
|
+
return;
|
|
1979
|
+
}
|
|
1568
1980
|
if (this.shouldCaptureScreenshot()) {
|
|
1569
1981
|
void this.withScreenshotMetadata(error, payload).then((enriched) => this.sendThroughBeforeSend(enriched)).catch(() => this.sendThroughBeforeSend({
|
|
1570
1982
|
...payload,
|
|
@@ -1580,6 +1992,86 @@ var AllStakClient = class {
|
|
|
1580
1992
|
}
|
|
1581
1993
|
});
|
|
1582
1994
|
}
|
|
1995
|
+
/**
|
|
1996
|
+
* Flat screenshot API path. Capture first (so masking primitives can
|
|
1997
|
+
* swap), send the event with a `screenshot.status` metadata tag, read
|
|
1998
|
+
* back the server-assigned errorId, then upload the attachment.
|
|
1999
|
+
*
|
|
2000
|
+
* Fail-open at every step.
|
|
2001
|
+
*/
|
|
2002
|
+
async runFlatScreenshotPipeline(error, payload) {
|
|
2003
|
+
const sc = resolveScreenshotConfig(pickScreenshotConfig(this.config));
|
|
2004
|
+
const runtimeMode = detectRuntimeMode();
|
|
2005
|
+
const ctx = { error, unhandled: true, runtimeMode };
|
|
2006
|
+
let captured = null;
|
|
2007
|
+
try {
|
|
2008
|
+
captured = await maybeCaptureScreenshot(sc, ctx);
|
|
2009
|
+
} catch {
|
|
2010
|
+
captured = null;
|
|
2011
|
+
}
|
|
2012
|
+
const status = !sc.captureScreenshotOnError ? "disabled" : captured ? "captured" : runtimeMode === "expo-go" ? "unsupported_runtime" : "unavailable";
|
|
2013
|
+
const eventPayload = {
|
|
2014
|
+
...payload,
|
|
2015
|
+
metadata: {
|
|
2016
|
+
...payload.metadata ?? {},
|
|
2017
|
+
"screenshot.status": status,
|
|
2018
|
+
"screenshot.runtimeMode": runtimeMode,
|
|
2019
|
+
...captured ? {
|
|
2020
|
+
"screenshot.contentType": captured.upload.contentType,
|
|
2021
|
+
"screenshot.width": captured.upload.width,
|
|
2022
|
+
"screenshot.height": captured.upload.height,
|
|
2023
|
+
"screenshot.sizeBytes": captured.upload.sizeBytes,
|
|
2024
|
+
"screenshot.redactionMode": captured.metadata.redactionMode,
|
|
2025
|
+
"screenshot.maskStyle": captured.metadata.maskStyle,
|
|
2026
|
+
"screenshot.captureMethod": captured.metadata.captureMethod
|
|
2027
|
+
} : {}
|
|
2028
|
+
}
|
|
2029
|
+
};
|
|
2030
|
+
let finalPayload = eventPayload;
|
|
2031
|
+
if (this.config.beforeSend) {
|
|
2032
|
+
try {
|
|
2033
|
+
finalPayload = await this.config.beforeSend(eventPayload);
|
|
2034
|
+
} catch {
|
|
2035
|
+
finalPayload = eventPayload;
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
if (!finalPayload) return;
|
|
2039
|
+
let eventId = null;
|
|
2040
|
+
try {
|
|
2041
|
+
const resp = await this.transport.sendAndRead(
|
|
2042
|
+
ERRORS_PATH,
|
|
2043
|
+
finalPayload,
|
|
2044
|
+
{ timeoutMs: 5e3, retries: 1 }
|
|
2045
|
+
);
|
|
2046
|
+
eventId = resp?.data?.id ?? resp?.id ?? null;
|
|
2047
|
+
} catch {
|
|
2048
|
+
}
|
|
2049
|
+
if (!captured || !eventId) return;
|
|
2050
|
+
try {
|
|
2051
|
+
await this.transport.sendAndRead(
|
|
2052
|
+
`/ingest/v1/errors/${encodeURIComponent(eventId)}/attachments`,
|
|
2053
|
+
{
|
|
2054
|
+
kind: "screenshot",
|
|
2055
|
+
contentType: captured.upload.contentType,
|
|
2056
|
+
dataBase64: captured.upload.dataBase64,
|
|
2057
|
+
width: captured.upload.width,
|
|
2058
|
+
height: captured.upload.height,
|
|
2059
|
+
redactionMode: captured.metadata.redactionMode,
|
|
2060
|
+
captureMethod: captured.metadata.captureMethod,
|
|
2061
|
+
sizeBytes: captured.upload.sizeBytes,
|
|
2062
|
+
metadata: {
|
|
2063
|
+
maskStyle: captured.metadata.maskStyle,
|
|
2064
|
+
format: captured.metadata.format,
|
|
2065
|
+
runtimeMode: captured.metadata.runtimeMode,
|
|
2066
|
+
privacyComponentsDetected: captured.metadata.privacyComponentsDetected ?? 0,
|
|
2067
|
+
sdkVersion: SDK_VERSION
|
|
2068
|
+
}
|
|
2069
|
+
},
|
|
2070
|
+
{ timeoutMs: sc.screenshotUploadTimeoutMs, retries: 2 }
|
|
2071
|
+
);
|
|
2072
|
+
} catch {
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
1583
2075
|
/** Start a new span. Auto-parented to any currently-active span. */
|
|
1584
2076
|
startSpan(operation, options) {
|
|
1585
2077
|
return this.tracing.startSpan(operation, options);
|
|
@@ -2104,7 +2596,7 @@ function byteSize(value) {
|
|
|
2104
2596
|
}
|
|
2105
2597
|
|
|
2106
2598
|
// src/provider.tsx
|
|
2107
|
-
import * as
|
|
2599
|
+
import * as React2 from "react";
|
|
2108
2600
|
|
|
2109
2601
|
// src/auto-breadcrumbs.ts
|
|
2110
2602
|
var FETCH_FLAG2 = "__allstak_fetch_patched__";
|
|
@@ -2341,17 +2833,17 @@ function tryAutoInstrumentNavigation() {
|
|
|
2341
2833
|
const rnav = require("@react-navigation/native");
|
|
2342
2834
|
if (!rnav || !rnav.NavigationContainer) return false;
|
|
2343
2835
|
if (rnav[NAV_AUTO_PATCH_FLAG]) return true;
|
|
2344
|
-
const
|
|
2345
|
-
if (!
|
|
2836
|
+
const React3 = require("react");
|
|
2837
|
+
if (!React3 || typeof React3.forwardRef !== "function") return false;
|
|
2346
2838
|
const OrigContainer = rnav.NavigationContainer;
|
|
2347
|
-
const Wrapped =
|
|
2348
|
-
const internalRef =
|
|
2349
|
-
const setRef =
|
|
2839
|
+
const Wrapped = React3.forwardRef(function AllStakNavigationContainer(props, userRef) {
|
|
2840
|
+
const internalRef = React3.useRef(null);
|
|
2841
|
+
const setRef = React3.useCallback((r) => {
|
|
2350
2842
|
internalRef.current = r;
|
|
2351
2843
|
if (typeof userRef === "function") userRef(r);
|
|
2352
2844
|
else if (userRef) userRef.current = r;
|
|
2353
2845
|
}, [userRef]);
|
|
2354
|
-
|
|
2846
|
+
React3.useEffect(() => {
|
|
2355
2847
|
if (internalRef.current) {
|
|
2356
2848
|
try {
|
|
2357
2849
|
instrumentReactNavigation(internalRef.current);
|
|
@@ -2359,7 +2851,7 @@ function tryAutoInstrumentNavigation() {
|
|
|
2359
2851
|
}
|
|
2360
2852
|
}
|
|
2361
2853
|
}, []);
|
|
2362
|
-
return
|
|
2854
|
+
return React3.createElement(OrigContainer, { ...props, ref: setRef });
|
|
2363
2855
|
});
|
|
2364
2856
|
Wrapped.displayName = "AllStakNavigationContainer";
|
|
2365
2857
|
try {
|
|
@@ -2589,9 +3081,16 @@ function installReactNative(options = {}) {
|
|
|
2589
3081
|
}
|
|
2590
3082
|
|
|
2591
3083
|
// src/provider.tsx
|
|
2592
|
-
|
|
3084
|
+
function getRN2() {
|
|
3085
|
+
return tryRequire("react-native");
|
|
3086
|
+
}
|
|
3087
|
+
function getRootView() {
|
|
3088
|
+
const RN = getRN2();
|
|
3089
|
+
return RN?.View;
|
|
3090
|
+
}
|
|
3091
|
+
var AllStakContext = React2.createContext(null);
|
|
2593
3092
|
var __providerOwnedInstance = null;
|
|
2594
|
-
var AllStakErrorBoundary = class extends
|
|
3093
|
+
var AllStakErrorBoundary = class extends React2.Component {
|
|
2595
3094
|
constructor() {
|
|
2596
3095
|
super(...arguments);
|
|
2597
3096
|
this.state = { error: null };
|
|
@@ -2659,9 +3158,31 @@ function AllStakProvider({
|
|
|
2659
3158
|
autoNetworkCapture,
|
|
2660
3159
|
autoFetchBreadcrumbs,
|
|
2661
3160
|
autoConsoleBreadcrumbs,
|
|
2662
|
-
autoNavigationBreadcrumbs
|
|
3161
|
+
autoNavigationBreadcrumbs,
|
|
3162
|
+
captureScreenshotOnError,
|
|
3163
|
+
screenshotRedaction,
|
|
3164
|
+
screenshotMaskStyle,
|
|
3165
|
+
screenshotMaxBytes,
|
|
3166
|
+
screenshotQuality,
|
|
3167
|
+
screenshotFormat,
|
|
3168
|
+
screenshotSampleRate,
|
|
3169
|
+
screenshotOnUnhandledOnly,
|
|
3170
|
+
screenshotUploadTimeoutMs,
|
|
3171
|
+
screenshotCaptureTimeoutMs,
|
|
3172
|
+
screenshotNativeMode,
|
|
3173
|
+
screenshotFailPolicy,
|
|
3174
|
+
beforeScreenshotCapture,
|
|
3175
|
+
beforeScreenshotUpload,
|
|
3176
|
+
isScreenshotAllowed
|
|
2663
3177
|
}) {
|
|
2664
|
-
const clientRef =
|
|
3178
|
+
const clientRef = React2.useRef(null);
|
|
3179
|
+
const rootRef = React2.useRef(null);
|
|
3180
|
+
React2.useEffect(() => {
|
|
3181
|
+
__setRootViewRef(rootRef);
|
|
3182
|
+
return () => {
|
|
3183
|
+
__setRootViewRef(null);
|
|
3184
|
+
};
|
|
3185
|
+
}, []);
|
|
2665
3186
|
if (!clientRef.current) {
|
|
2666
3187
|
const existing = AllStak._getInstance();
|
|
2667
3188
|
if (existing && __providerOwnedInstance === existing) {
|
|
@@ -2685,7 +3206,22 @@ function AllStakProvider({
|
|
|
2685
3206
|
replay,
|
|
2686
3207
|
tracesSampleRate,
|
|
2687
3208
|
service,
|
|
2688
|
-
dist
|
|
3209
|
+
dist,
|
|
3210
|
+
captureScreenshotOnError,
|
|
3211
|
+
screenshotRedaction,
|
|
3212
|
+
screenshotMaskStyle,
|
|
3213
|
+
screenshotMaxBytes,
|
|
3214
|
+
screenshotQuality,
|
|
3215
|
+
screenshotFormat,
|
|
3216
|
+
screenshotSampleRate,
|
|
3217
|
+
screenshotOnUnhandledOnly,
|
|
3218
|
+
screenshotUploadTimeoutMs,
|
|
3219
|
+
screenshotCaptureTimeoutMs,
|
|
3220
|
+
screenshotNativeMode,
|
|
3221
|
+
screenshotFailPolicy,
|
|
3222
|
+
beforeScreenshotCapture,
|
|
3223
|
+
beforeScreenshotUpload,
|
|
3224
|
+
isScreenshotAllowed
|
|
2689
3225
|
};
|
|
2690
3226
|
clientRef.current = AllStak.init(config);
|
|
2691
3227
|
__providerOwnedInstance = clientRef.current;
|
|
@@ -2705,7 +3241,7 @@ function AllStakProvider({
|
|
|
2705
3241
|
}
|
|
2706
3242
|
}
|
|
2707
3243
|
}
|
|
2708
|
-
|
|
3244
|
+
React2.useEffect(() => {
|
|
2709
3245
|
return () => {
|
|
2710
3246
|
if (destroyOnUnmount) {
|
|
2711
3247
|
AllStak.destroy();
|
|
@@ -2715,10 +3251,17 @@ function AllStakProvider({
|
|
|
2715
3251
|
}
|
|
2716
3252
|
};
|
|
2717
3253
|
}, [destroyOnUnmount, debug]);
|
|
2718
|
-
|
|
3254
|
+
const boundary = /* @__PURE__ */ React2.createElement(AllStakErrorBoundary, { fallback, onError, debug }, children);
|
|
3255
|
+
const RootView = getRootView();
|
|
3256
|
+
const wrapped = RootView ? React2.createElement(
|
|
3257
|
+
RootView,
|
|
3258
|
+
{ ref: rootRef, style: { flex: 1 }, collapsable: false },
|
|
3259
|
+
boundary
|
|
3260
|
+
) : boundary;
|
|
3261
|
+
return /* @__PURE__ */ React2.createElement(AllStakContext.Provider, { value: clientRef.current }, wrapped);
|
|
2719
3262
|
}
|
|
2720
3263
|
function useAllStak() {
|
|
2721
|
-
return
|
|
3264
|
+
return React2.useMemo(
|
|
2722
3265
|
() => ({
|
|
2723
3266
|
captureException: (error, ctx) => AllStak.captureException(error, ctx),
|
|
2724
3267
|
captureMessage: (msg, level = "info") => AllStak.captureMessage(msg, level),
|
|
@@ -2788,8 +3331,13 @@ export {
|
|
|
2788
3331
|
ALWAYS_REDACT_QUERY,
|
|
2789
3332
|
AllStak,
|
|
2790
3333
|
AllStakClient,
|
|
3334
|
+
AllStakMaskedView,
|
|
3335
|
+
AllStakPrivacyView,
|
|
2791
3336
|
AllStakProvider,
|
|
3337
|
+
AllStakSensitiveText,
|
|
3338
|
+
AllStakTextInput,
|
|
2792
3339
|
DEFAULT_REDACT_BODY_FIELDS,
|
|
3340
|
+
DEFAULT_SCREENSHOT_CONFIG,
|
|
2793
3341
|
HttpRequestModule,
|
|
2794
3342
|
INGEST_HOST,
|
|
2795
3343
|
REDACTED,
|
|
@@ -2800,18 +3348,30 @@ export {
|
|
|
2800
3348
|
__devTriggerNativeCrash,
|
|
2801
3349
|
__resetAutoNavigationFlagForTest,
|
|
2802
3350
|
__resetConsoleInstrumentationFlagForTest,
|
|
3351
|
+
__resetPrivacyStateForTest,
|
|
2803
3352
|
__resetProviderInstanceForTest,
|
|
3353
|
+
__resetRuntimeModeForTest,
|
|
2804
3354
|
__setNativeModuleForTest,
|
|
2805
3355
|
applyArchitectureTags,
|
|
2806
3356
|
captureBodyResult,
|
|
3357
|
+
captureViaViewShot,
|
|
2807
3358
|
detectArchitecture,
|
|
3359
|
+
detectRuntimeMode,
|
|
2808
3360
|
drainPendingNativeCrashes,
|
|
2809
3361
|
installReactNative,
|
|
2810
3362
|
instrumentNavigationFromLinking,
|
|
2811
3363
|
instrumentReactNavigation,
|
|
3364
|
+
isCapturingScreenshot,
|
|
3365
|
+
isViewShotAvailable,
|
|
3366
|
+
maybeCaptureScreenshot,
|
|
3367
|
+
pickScreenshotConfig,
|
|
2812
3368
|
redactUrl,
|
|
3369
|
+
registerSensitiveRef,
|
|
3370
|
+
resolveScreenshotConfig,
|
|
3371
|
+
runtimeAllowsScreenshot,
|
|
2813
3372
|
sanitizeHeaders,
|
|
2814
3373
|
tryAutoInstrumentNavigation,
|
|
2815
|
-
useAllStak
|
|
3374
|
+
useAllStak,
|
|
3375
|
+
useAllStakPrivacy
|
|
2816
3376
|
};
|
|
2817
3377
|
//# sourceMappingURL=index.mjs.map
|