@allstak/react-native 0.3.3 → 0.4.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 +38 -0
- package/README.md +398 -95
- package/app.plugin.js +1 -3
- 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 +556 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +556 -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,342 @@ 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
|
+
var RN = tryRequire("react-native");
|
|
1486
|
+
var state = {
|
|
1487
|
+
isCapturing: false,
|
|
1488
|
+
sensitiveRefs: /* @__PURE__ */ new Set()
|
|
1489
|
+
};
|
|
1490
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
1491
|
+
function __setCapturing(value) {
|
|
1492
|
+
if (state.isCapturing === value) return;
|
|
1493
|
+
state.isCapturing = value;
|
|
1494
|
+
for (const fn of listeners) {
|
|
1495
|
+
try {
|
|
1496
|
+
fn(value);
|
|
1497
|
+
} catch {
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
function __resetPrivacyStateForTest() {
|
|
1502
|
+
state.isCapturing = false;
|
|
1503
|
+
state.sensitiveRefs.clear();
|
|
1504
|
+
listeners.clear();
|
|
1505
|
+
}
|
|
1506
|
+
function isCapturingScreenshot() {
|
|
1507
|
+
return state.isCapturing;
|
|
1508
|
+
}
|
|
1509
|
+
function sensitiveRefCount() {
|
|
1510
|
+
return state.sensitiveRefs.size;
|
|
1511
|
+
}
|
|
1512
|
+
function registerSensitiveRef(ref) {
|
|
1513
|
+
state.sensitiveRefs.add(ref);
|
|
1514
|
+
return () => {
|
|
1515
|
+
state.sensitiveRefs.delete(ref);
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
function useIsCapturing() {
|
|
1519
|
+
const [val, setVal] = React.useState(state.isCapturing);
|
|
1520
|
+
React.useEffect(() => {
|
|
1521
|
+
const fn = (v) => setVal(v);
|
|
1522
|
+
listeners.add(fn);
|
|
1523
|
+
return () => {
|
|
1524
|
+
listeners.delete(fn);
|
|
1525
|
+
};
|
|
1526
|
+
}, []);
|
|
1527
|
+
return val;
|
|
1528
|
+
}
|
|
1529
|
+
function useAllStakPrivacy() {
|
|
1530
|
+
const isCapturing = useIsCapturing();
|
|
1531
|
+
return { isCapturing, registerSensitiveRef };
|
|
1532
|
+
}
|
|
1533
|
+
var View = RN?.View ?? ((props) => React.createElement("View", props));
|
|
1534
|
+
var Text = RN?.Text ?? ((props) => React.createElement("Text", props));
|
|
1535
|
+
var TextInput = RN?.TextInput ?? ((props) => React.createElement("TextInput", props));
|
|
1536
|
+
var DEFAULT_MASK_COLOR = "#d8dde7";
|
|
1537
|
+
var DEFAULT_MASK_LABEL = "\u2022\u2022\u2022\u2022\u2022\u2022";
|
|
1538
|
+
function AllStakMaskedView({
|
|
1539
|
+
children,
|
|
1540
|
+
maskLabel = DEFAULT_MASK_LABEL,
|
|
1541
|
+
maskColor = DEFAULT_MASK_COLOR,
|
|
1542
|
+
hideScreenshot = false,
|
|
1543
|
+
privacy = "mask",
|
|
1544
|
+
style,
|
|
1545
|
+
...rest
|
|
1546
|
+
}) {
|
|
1547
|
+
const isCapturing = useIsCapturing();
|
|
1548
|
+
if (!isCapturing || privacy === "show") {
|
|
1549
|
+
return React.createElement(View, { style, ...rest }, children);
|
|
1550
|
+
}
|
|
1551
|
+
if (privacy === "hide" || hideScreenshot) {
|
|
1552
|
+
return React.createElement(View, { style: [{ backgroundColor: maskColor }, style], ...rest });
|
|
1553
|
+
}
|
|
1554
|
+
return React.createElement(
|
|
1555
|
+
View,
|
|
1556
|
+
{ style: [{ backgroundColor: maskColor, alignItems: "center", justifyContent: "center" }, style], ...rest },
|
|
1557
|
+
React.createElement(Text, { style: { color: "#3f4652", fontSize: 12 } }, maskLabel)
|
|
1558
|
+
);
|
|
1559
|
+
}
|
|
1560
|
+
function AllStakPrivacyView(props) {
|
|
1561
|
+
return React.createElement(AllStakMaskedView, { hideScreenshot: true, ...props });
|
|
1562
|
+
}
|
|
1563
|
+
function AllStakTextInput({
|
|
1564
|
+
privacy = "mask",
|
|
1565
|
+
style,
|
|
1566
|
+
maskColor = DEFAULT_MASK_COLOR,
|
|
1567
|
+
...rest
|
|
1568
|
+
}) {
|
|
1569
|
+
const isCapturing = useIsCapturing();
|
|
1570
|
+
if (isCapturing && privacy !== "show") {
|
|
1571
|
+
return React.createElement(View, {
|
|
1572
|
+
style: [{ minHeight: 40, backgroundColor: maskColor, borderRadius: 4 }, style]
|
|
1573
|
+
});
|
|
1574
|
+
}
|
|
1575
|
+
return React.createElement(TextInput, { style, ...rest });
|
|
1576
|
+
}
|
|
1577
|
+
function AllStakSensitiveText({
|
|
1578
|
+
children,
|
|
1579
|
+
privacy = "mask",
|
|
1580
|
+
style,
|
|
1581
|
+
maskLabel = DEFAULT_MASK_LABEL,
|
|
1582
|
+
...rest
|
|
1583
|
+
}) {
|
|
1584
|
+
const isCapturing = useIsCapturing();
|
|
1585
|
+
if (isCapturing && privacy !== "show") {
|
|
1586
|
+
return React.createElement(Text, { style, ...rest }, maskLabel);
|
|
1587
|
+
}
|
|
1588
|
+
return React.createElement(Text, { style, ...rest }, children);
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
// src/screenshot.ts
|
|
1592
|
+
var DEFAULT_SCREENSHOT_CONFIG = {
|
|
1593
|
+
captureScreenshotOnError: false,
|
|
1594
|
+
screenshotRedaction: "strict",
|
|
1595
|
+
screenshotMaskStyle: "solid",
|
|
1596
|
+
screenshotMaxBytes: 5e5,
|
|
1597
|
+
screenshotQuality: 0.7,
|
|
1598
|
+
screenshotFormat: "jpg",
|
|
1599
|
+
screenshotSampleRate: 1,
|
|
1600
|
+
screenshotOnUnhandledOnly: true,
|
|
1601
|
+
screenshotUploadTimeoutMs: 8e3,
|
|
1602
|
+
screenshotCaptureTimeoutMs: 2e3,
|
|
1603
|
+
screenshotNativeMode: "auto",
|
|
1604
|
+
screenshotFailPolicy: "send-event-only"
|
|
1605
|
+
};
|
|
1606
|
+
function resolveScreenshotConfig(partial) {
|
|
1607
|
+
const c = { ...DEFAULT_SCREENSHOT_CONFIG, ...partial ?? {} };
|
|
1608
|
+
c.screenshotMaxBytes = clamp(c.screenshotMaxBytes, 1024, 5e6);
|
|
1609
|
+
c.screenshotQuality = clamp(c.screenshotQuality, 0, 1);
|
|
1610
|
+
c.screenshotSampleRate = clamp(c.screenshotSampleRate, 0, 1);
|
|
1611
|
+
c.screenshotUploadTimeoutMs = clamp(c.screenshotUploadTimeoutMs, 500, 6e4);
|
|
1612
|
+
c.screenshotCaptureTimeoutMs = clamp(c.screenshotCaptureTimeoutMs, 100, 3e4);
|
|
1613
|
+
return c;
|
|
1614
|
+
}
|
|
1615
|
+
function clamp(n, lo, hi) {
|
|
1616
|
+
if (typeof n !== "number" || !Number.isFinite(n)) return lo;
|
|
1617
|
+
return Math.max(lo, Math.min(hi, n));
|
|
1618
|
+
}
|
|
1619
|
+
var rootViewRef = null;
|
|
1620
|
+
function __setRootViewRef(ref) {
|
|
1621
|
+
rootViewRef = ref;
|
|
1622
|
+
}
|
|
1623
|
+
var warnedAboutBothApis = false;
|
|
1624
|
+
function warnIfBothApisPresent(callbackPresent, flatPresent) {
|
|
1625
|
+
if (!callbackPresent || !flatPresent || warnedAboutBothApis) return;
|
|
1626
|
+
warnedAboutBothApis = true;
|
|
1627
|
+
console.warn(
|
|
1628
|
+
"[AllStak] Both `screenshot.provider` (deprecated) and the flat `captureScreenshotOnError` API are configured. The flat API takes precedence. Remove `screenshot.provider` to silence this warning."
|
|
1629
|
+
);
|
|
1630
|
+
}
|
|
1631
|
+
function isViewShotAvailable() {
|
|
1632
|
+
return tryRequire("react-native-view-shot") !== null;
|
|
1633
|
+
}
|
|
1634
|
+
async function captureViaViewShot(config) {
|
|
1635
|
+
if (config.screenshotNativeMode === "disabled") return null;
|
|
1636
|
+
const viewShot = tryRequire("react-native-view-shot");
|
|
1637
|
+
if (!viewShot) return null;
|
|
1638
|
+
const captureRef = viewShot.captureRef ?? viewShot.default?.captureRef;
|
|
1639
|
+
if (typeof captureRef !== "function") return null;
|
|
1640
|
+
const refTarget = rootViewRef?.current;
|
|
1641
|
+
if (!refTarget) return null;
|
|
1642
|
+
const format = config.screenshotFormat === "jpg" ? "jpg" : config.screenshotFormat;
|
|
1643
|
+
const dimensions = readDimensions();
|
|
1644
|
+
__setCapturing(true);
|
|
1645
|
+
await new Promise((r) => setTimeout(r, 16));
|
|
1646
|
+
try {
|
|
1647
|
+
const captured = await Promise.race([
|
|
1648
|
+
Promise.resolve(captureRef(refTarget, {
|
|
1649
|
+
format,
|
|
1650
|
+
quality: config.screenshotQuality,
|
|
1651
|
+
result: "base64"
|
|
1652
|
+
})),
|
|
1653
|
+
new Promise((resolve) => setTimeout(() => resolve(null), config.screenshotCaptureTimeoutMs))
|
|
1654
|
+
]);
|
|
1655
|
+
if (!captured || typeof captured !== "string") return null;
|
|
1656
|
+
const sizeBytes = estimateBase64Size(captured);
|
|
1657
|
+
if (sizeBytes > config.screenshotMaxBytes) {
|
|
1658
|
+
if (__DEV__) {
|
|
1659
|
+
console.warn(`[AllStak] Screenshot ${sizeBytes}B exceeds limit ${config.screenshotMaxBytes}B; dropping.`);
|
|
1660
|
+
}
|
|
1661
|
+
return null;
|
|
1662
|
+
}
|
|
1663
|
+
const contentType = format === "png" ? "image/png" : format === "webp" ? "image/webp" : "image/jpeg";
|
|
1664
|
+
return {
|
|
1665
|
+
dataBase64: captured,
|
|
1666
|
+
contentType,
|
|
1667
|
+
width: dimensions.width,
|
|
1668
|
+
height: dimensions.height,
|
|
1669
|
+
sizeBytes
|
|
1670
|
+
};
|
|
1671
|
+
} catch (err) {
|
|
1672
|
+
if (__DEV__) {
|
|
1673
|
+
console.warn("[AllStak] view-shot capture failed:", err?.message);
|
|
1674
|
+
}
|
|
1675
|
+
return null;
|
|
1676
|
+
} finally {
|
|
1677
|
+
__setCapturing(false);
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
function estimateBase64Size(base64) {
|
|
1681
|
+
const padding = base64.endsWith("==") ? 2 : base64.endsWith("=") ? 1 : 0;
|
|
1682
|
+
return Math.floor(base64.length * 3 / 4) - padding;
|
|
1683
|
+
}
|
|
1684
|
+
function readDimensions() {
|
|
1685
|
+
try {
|
|
1686
|
+
const RN3 = tryRequire("react-native");
|
|
1687
|
+
const dims = RN3?.Dimensions?.get?.("window");
|
|
1688
|
+
if (dims && typeof dims.width === "number" && typeof dims.height === "number") {
|
|
1689
|
+
return { width: Math.round(dims.width), height: Math.round(dims.height) };
|
|
1690
|
+
}
|
|
1691
|
+
} catch {
|
|
1692
|
+
}
|
|
1693
|
+
return { width: 0, height: 0 };
|
|
1694
|
+
}
|
|
1695
|
+
async function maybeCaptureScreenshot(config, ctx) {
|
|
1696
|
+
try {
|
|
1697
|
+
if (!config.captureScreenshotOnError) return null;
|
|
1698
|
+
if (config.screenshotOnUnhandledOnly && ctx.unhandled === false) return null;
|
|
1699
|
+
if (config.screenshotSampleRate < 1 && Math.random() >= config.screenshotSampleRate) return null;
|
|
1700
|
+
if (!runtimeAllowsScreenshot(ctx.runtimeMode)) return null;
|
|
1701
|
+
if (config.isScreenshotAllowed) {
|
|
1702
|
+
try {
|
|
1703
|
+
const allowed = await config.isScreenshotAllowed(ctx);
|
|
1704
|
+
if (!allowed) return null;
|
|
1705
|
+
} catch {
|
|
1706
|
+
return null;
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
if (config.beforeScreenshotCapture) {
|
|
1710
|
+
try {
|
|
1711
|
+
const cont = await config.beforeScreenshotCapture(ctx);
|
|
1712
|
+
if (!cont) return null;
|
|
1713
|
+
} catch {
|
|
1714
|
+
return null;
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
const upload = await captureViaViewShot(config);
|
|
1718
|
+
if (!upload) return null;
|
|
1719
|
+
const metadata = {
|
|
1720
|
+
captureMethod: "react-native-view-shot",
|
|
1721
|
+
redactionMode: config.screenshotRedaction,
|
|
1722
|
+
maskStyle: config.screenshotMaskStyle,
|
|
1723
|
+
format: config.screenshotFormat,
|
|
1724
|
+
width: upload.width,
|
|
1725
|
+
height: upload.height,
|
|
1726
|
+
sizeBytes: upload.sizeBytes,
|
|
1727
|
+
privacyComponentsDetected: sensitiveRefCount(),
|
|
1728
|
+
runtimeMode: ctx.runtimeMode
|
|
1729
|
+
};
|
|
1730
|
+
if (config.beforeScreenshotUpload) {
|
|
1731
|
+
try {
|
|
1732
|
+
const filtered = await config.beforeScreenshotUpload(upload, metadata);
|
|
1733
|
+
if (!filtered) return null;
|
|
1734
|
+
return { upload: filtered, metadata };
|
|
1735
|
+
} catch {
|
|
1736
|
+
return null;
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
return { upload, metadata };
|
|
1740
|
+
} catch (err) {
|
|
1741
|
+
if (__DEV__) {
|
|
1742
|
+
console.warn("[AllStak] maybeCaptureScreenshot fail-open:", err?.message);
|
|
1743
|
+
}
|
|
1744
|
+
return null;
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
function pickScreenshotConfig(source) {
|
|
1748
|
+
const out = {};
|
|
1749
|
+
const pick = (key) => {
|
|
1750
|
+
if (source[key] !== void 0) out[key] = source[key];
|
|
1751
|
+
};
|
|
1752
|
+
pick("captureScreenshotOnError");
|
|
1753
|
+
pick("screenshotRedaction");
|
|
1754
|
+
pick("screenshotMaskStyle");
|
|
1755
|
+
pick("screenshotMaxBytes");
|
|
1756
|
+
pick("screenshotQuality");
|
|
1757
|
+
pick("screenshotFormat");
|
|
1758
|
+
pick("screenshotSampleRate");
|
|
1759
|
+
pick("screenshotOnUnhandledOnly");
|
|
1760
|
+
pick("screenshotUploadTimeoutMs");
|
|
1761
|
+
pick("screenshotCaptureTimeoutMs");
|
|
1762
|
+
pick("screenshotNativeMode");
|
|
1763
|
+
pick("screenshotFailPolicy");
|
|
1764
|
+
pick("beforeScreenshotCapture");
|
|
1765
|
+
pick("beforeScreenshotUpload");
|
|
1766
|
+
pick("isScreenshotAllowed");
|
|
1767
|
+
return out;
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1386
1770
|
// src/client.ts
|
|
1387
1771
|
var INGEST_HOST = "https://api.allstak.sa";
|
|
1388
1772
|
var SDK_NAME = "allstak-react-native";
|
|
1389
|
-
var SDK_VERSION = "0.
|
|
1773
|
+
var SDK_VERSION = "0.4.0";
|
|
1390
1774
|
var ERRORS_PATH = "/ingest/v1/errors";
|
|
1391
1775
|
var LOGS_PATH = "/ingest/v1/logs";
|
|
1392
1776
|
var VALID_BREADCRUMB_TYPES = /* @__PURE__ */ new Set(["http", "log", "ui", "navigation", "query", "default"]);
|
|
@@ -1565,6 +1949,18 @@ var AllStakClient = class {
|
|
|
1565
1949
|
breadcrumbs: currentBreadcrumbs,
|
|
1566
1950
|
fingerprint: eff.fingerprint
|
|
1567
1951
|
};
|
|
1952
|
+
const flatPresent = this.config.captureScreenshotOnError === true;
|
|
1953
|
+
const callbackPresent = Boolean(this.config.screenshot?.provider);
|
|
1954
|
+
warnIfBothApisPresent(callbackPresent, flatPresent);
|
|
1955
|
+
if (flatPresent) {
|
|
1956
|
+
void this.runFlatScreenshotPipeline(error, payload).catch(() => {
|
|
1957
|
+
void this.sendThroughBeforeSend({
|
|
1958
|
+
...payload,
|
|
1959
|
+
metadata: { ...payload.metadata ?? {}, "screenshot.status": "failed" }
|
|
1960
|
+
});
|
|
1961
|
+
});
|
|
1962
|
+
return;
|
|
1963
|
+
}
|
|
1568
1964
|
if (this.shouldCaptureScreenshot()) {
|
|
1569
1965
|
void this.withScreenshotMetadata(error, payload).then((enriched) => this.sendThroughBeforeSend(enriched)).catch(() => this.sendThroughBeforeSend({
|
|
1570
1966
|
...payload,
|
|
@@ -1580,6 +1976,86 @@ var AllStakClient = class {
|
|
|
1580
1976
|
}
|
|
1581
1977
|
});
|
|
1582
1978
|
}
|
|
1979
|
+
/**
|
|
1980
|
+
* Flat screenshot API path. Capture first (so masking primitives can
|
|
1981
|
+
* swap), send the event with a `screenshot.status` metadata tag, read
|
|
1982
|
+
* back the server-assigned errorId, then upload the attachment.
|
|
1983
|
+
*
|
|
1984
|
+
* Fail-open at every step.
|
|
1985
|
+
*/
|
|
1986
|
+
async runFlatScreenshotPipeline(error, payload) {
|
|
1987
|
+
const sc = resolveScreenshotConfig(pickScreenshotConfig(this.config));
|
|
1988
|
+
const runtimeMode = detectRuntimeMode();
|
|
1989
|
+
const ctx = { error, unhandled: true, runtimeMode };
|
|
1990
|
+
let captured = null;
|
|
1991
|
+
try {
|
|
1992
|
+
captured = await maybeCaptureScreenshot(sc, ctx);
|
|
1993
|
+
} catch {
|
|
1994
|
+
captured = null;
|
|
1995
|
+
}
|
|
1996
|
+
const status = !sc.captureScreenshotOnError ? "disabled" : captured ? "captured" : runtimeMode === "expo-go" ? "unsupported_runtime" : "unavailable";
|
|
1997
|
+
const eventPayload = {
|
|
1998
|
+
...payload,
|
|
1999
|
+
metadata: {
|
|
2000
|
+
...payload.metadata ?? {},
|
|
2001
|
+
"screenshot.status": status,
|
|
2002
|
+
"screenshot.runtimeMode": runtimeMode,
|
|
2003
|
+
...captured ? {
|
|
2004
|
+
"screenshot.contentType": captured.upload.contentType,
|
|
2005
|
+
"screenshot.width": captured.upload.width,
|
|
2006
|
+
"screenshot.height": captured.upload.height,
|
|
2007
|
+
"screenshot.sizeBytes": captured.upload.sizeBytes,
|
|
2008
|
+
"screenshot.redactionMode": captured.metadata.redactionMode,
|
|
2009
|
+
"screenshot.maskStyle": captured.metadata.maskStyle,
|
|
2010
|
+
"screenshot.captureMethod": captured.metadata.captureMethod
|
|
2011
|
+
} : {}
|
|
2012
|
+
}
|
|
2013
|
+
};
|
|
2014
|
+
let finalPayload = eventPayload;
|
|
2015
|
+
if (this.config.beforeSend) {
|
|
2016
|
+
try {
|
|
2017
|
+
finalPayload = await this.config.beforeSend(eventPayload);
|
|
2018
|
+
} catch {
|
|
2019
|
+
finalPayload = eventPayload;
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
if (!finalPayload) return;
|
|
2023
|
+
let eventId = null;
|
|
2024
|
+
try {
|
|
2025
|
+
const resp = await this.transport.sendAndRead(
|
|
2026
|
+
ERRORS_PATH,
|
|
2027
|
+
finalPayload,
|
|
2028
|
+
{ timeoutMs: 5e3, retries: 1 }
|
|
2029
|
+
);
|
|
2030
|
+
eventId = resp?.data?.id ?? resp?.id ?? null;
|
|
2031
|
+
} catch {
|
|
2032
|
+
}
|
|
2033
|
+
if (!captured || !eventId) return;
|
|
2034
|
+
try {
|
|
2035
|
+
await this.transport.sendAndRead(
|
|
2036
|
+
`/ingest/v1/errors/${encodeURIComponent(eventId)}/attachments`,
|
|
2037
|
+
{
|
|
2038
|
+
kind: "screenshot",
|
|
2039
|
+
contentType: captured.upload.contentType,
|
|
2040
|
+
dataBase64: captured.upload.dataBase64,
|
|
2041
|
+
width: captured.upload.width,
|
|
2042
|
+
height: captured.upload.height,
|
|
2043
|
+
redactionMode: captured.metadata.redactionMode,
|
|
2044
|
+
captureMethod: captured.metadata.captureMethod,
|
|
2045
|
+
sizeBytes: captured.upload.sizeBytes,
|
|
2046
|
+
metadata: {
|
|
2047
|
+
maskStyle: captured.metadata.maskStyle,
|
|
2048
|
+
format: captured.metadata.format,
|
|
2049
|
+
runtimeMode: captured.metadata.runtimeMode,
|
|
2050
|
+
privacyComponentsDetected: captured.metadata.privacyComponentsDetected ?? 0,
|
|
2051
|
+
sdkVersion: SDK_VERSION
|
|
2052
|
+
}
|
|
2053
|
+
},
|
|
2054
|
+
{ timeoutMs: sc.screenshotUploadTimeoutMs, retries: 2 }
|
|
2055
|
+
);
|
|
2056
|
+
} catch {
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
1583
2059
|
/** Start a new span. Auto-parented to any currently-active span. */
|
|
1584
2060
|
startSpan(operation, options) {
|
|
1585
2061
|
return this.tracing.startSpan(operation, options);
|
|
@@ -2104,7 +2580,7 @@ function byteSize(value) {
|
|
|
2104
2580
|
}
|
|
2105
2581
|
|
|
2106
2582
|
// src/provider.tsx
|
|
2107
|
-
import * as
|
|
2583
|
+
import * as React2 from "react";
|
|
2108
2584
|
|
|
2109
2585
|
// src/auto-breadcrumbs.ts
|
|
2110
2586
|
var FETCH_FLAG2 = "__allstak_fetch_patched__";
|
|
@@ -2341,17 +2817,17 @@ function tryAutoInstrumentNavigation() {
|
|
|
2341
2817
|
const rnav = require("@react-navigation/native");
|
|
2342
2818
|
if (!rnav || !rnav.NavigationContainer) return false;
|
|
2343
2819
|
if (rnav[NAV_AUTO_PATCH_FLAG]) return true;
|
|
2344
|
-
const
|
|
2345
|
-
if (!
|
|
2820
|
+
const React3 = require("react");
|
|
2821
|
+
if (!React3 || typeof React3.forwardRef !== "function") return false;
|
|
2346
2822
|
const OrigContainer = rnav.NavigationContainer;
|
|
2347
|
-
const Wrapped =
|
|
2348
|
-
const internalRef =
|
|
2349
|
-
const setRef =
|
|
2823
|
+
const Wrapped = React3.forwardRef(function AllStakNavigationContainer(props, userRef) {
|
|
2824
|
+
const internalRef = React3.useRef(null);
|
|
2825
|
+
const setRef = React3.useCallback((r) => {
|
|
2350
2826
|
internalRef.current = r;
|
|
2351
2827
|
if (typeof userRef === "function") userRef(r);
|
|
2352
2828
|
else if (userRef) userRef.current = r;
|
|
2353
2829
|
}, [userRef]);
|
|
2354
|
-
|
|
2830
|
+
React3.useEffect(() => {
|
|
2355
2831
|
if (internalRef.current) {
|
|
2356
2832
|
try {
|
|
2357
2833
|
instrumentReactNavigation(internalRef.current);
|
|
@@ -2359,7 +2835,7 @@ function tryAutoInstrumentNavigation() {
|
|
|
2359
2835
|
}
|
|
2360
2836
|
}
|
|
2361
2837
|
}, []);
|
|
2362
|
-
return
|
|
2838
|
+
return React3.createElement(OrigContainer, { ...props, ref: setRef });
|
|
2363
2839
|
});
|
|
2364
2840
|
Wrapped.displayName = "AllStakNavigationContainer";
|
|
2365
2841
|
try {
|
|
@@ -2589,9 +3065,11 @@ function installReactNative(options = {}) {
|
|
|
2589
3065
|
}
|
|
2590
3066
|
|
|
2591
3067
|
// src/provider.tsx
|
|
2592
|
-
var
|
|
3068
|
+
var RN2 = tryRequire("react-native");
|
|
3069
|
+
var RootView = RN2?.View ?? ((props) => React2.createElement("View", props));
|
|
3070
|
+
var AllStakContext = React2.createContext(null);
|
|
2593
3071
|
var __providerOwnedInstance = null;
|
|
2594
|
-
var AllStakErrorBoundary = class extends
|
|
3072
|
+
var AllStakErrorBoundary = class extends React2.Component {
|
|
2595
3073
|
constructor() {
|
|
2596
3074
|
super(...arguments);
|
|
2597
3075
|
this.state = { error: null };
|
|
@@ -2659,9 +3137,31 @@ function AllStakProvider({
|
|
|
2659
3137
|
autoNetworkCapture,
|
|
2660
3138
|
autoFetchBreadcrumbs,
|
|
2661
3139
|
autoConsoleBreadcrumbs,
|
|
2662
|
-
autoNavigationBreadcrumbs
|
|
3140
|
+
autoNavigationBreadcrumbs,
|
|
3141
|
+
captureScreenshotOnError,
|
|
3142
|
+
screenshotRedaction,
|
|
3143
|
+
screenshotMaskStyle,
|
|
3144
|
+
screenshotMaxBytes,
|
|
3145
|
+
screenshotQuality,
|
|
3146
|
+
screenshotFormat,
|
|
3147
|
+
screenshotSampleRate,
|
|
3148
|
+
screenshotOnUnhandledOnly,
|
|
3149
|
+
screenshotUploadTimeoutMs,
|
|
3150
|
+
screenshotCaptureTimeoutMs,
|
|
3151
|
+
screenshotNativeMode,
|
|
3152
|
+
screenshotFailPolicy,
|
|
3153
|
+
beforeScreenshotCapture,
|
|
3154
|
+
beforeScreenshotUpload,
|
|
3155
|
+
isScreenshotAllowed
|
|
2663
3156
|
}) {
|
|
2664
|
-
const clientRef =
|
|
3157
|
+
const clientRef = React2.useRef(null);
|
|
3158
|
+
const rootRef = React2.useRef(null);
|
|
3159
|
+
React2.useEffect(() => {
|
|
3160
|
+
__setRootViewRef(rootRef);
|
|
3161
|
+
return () => {
|
|
3162
|
+
__setRootViewRef(null);
|
|
3163
|
+
};
|
|
3164
|
+
}, []);
|
|
2665
3165
|
if (!clientRef.current) {
|
|
2666
3166
|
const existing = AllStak._getInstance();
|
|
2667
3167
|
if (existing && __providerOwnedInstance === existing) {
|
|
@@ -2685,7 +3185,22 @@ function AllStakProvider({
|
|
|
2685
3185
|
replay,
|
|
2686
3186
|
tracesSampleRate,
|
|
2687
3187
|
service,
|
|
2688
|
-
dist
|
|
3188
|
+
dist,
|
|
3189
|
+
captureScreenshotOnError,
|
|
3190
|
+
screenshotRedaction,
|
|
3191
|
+
screenshotMaskStyle,
|
|
3192
|
+
screenshotMaxBytes,
|
|
3193
|
+
screenshotQuality,
|
|
3194
|
+
screenshotFormat,
|
|
3195
|
+
screenshotSampleRate,
|
|
3196
|
+
screenshotOnUnhandledOnly,
|
|
3197
|
+
screenshotUploadTimeoutMs,
|
|
3198
|
+
screenshotCaptureTimeoutMs,
|
|
3199
|
+
screenshotNativeMode,
|
|
3200
|
+
screenshotFailPolicy,
|
|
3201
|
+
beforeScreenshotCapture,
|
|
3202
|
+
beforeScreenshotUpload,
|
|
3203
|
+
isScreenshotAllowed
|
|
2689
3204
|
};
|
|
2690
3205
|
clientRef.current = AllStak.init(config);
|
|
2691
3206
|
__providerOwnedInstance = clientRef.current;
|
|
@@ -2705,7 +3220,7 @@ function AllStakProvider({
|
|
|
2705
3220
|
}
|
|
2706
3221
|
}
|
|
2707
3222
|
}
|
|
2708
|
-
|
|
3223
|
+
React2.useEffect(() => {
|
|
2709
3224
|
return () => {
|
|
2710
3225
|
if (destroyOnUnmount) {
|
|
2711
3226
|
AllStak.destroy();
|
|
@@ -2715,10 +3230,16 @@ function AllStakProvider({
|
|
|
2715
3230
|
}
|
|
2716
3231
|
};
|
|
2717
3232
|
}, [destroyOnUnmount, debug]);
|
|
2718
|
-
|
|
3233
|
+
const boundary = /* @__PURE__ */ React2.createElement(AllStakErrorBoundary, { fallback, onError, debug }, children);
|
|
3234
|
+
const wrapped = RN2 ? React2.createElement(
|
|
3235
|
+
RootView,
|
|
3236
|
+
{ ref: rootRef, style: { flex: 1 }, collapsable: false },
|
|
3237
|
+
boundary
|
|
3238
|
+
) : boundary;
|
|
3239
|
+
return /* @__PURE__ */ React2.createElement(AllStakContext.Provider, { value: clientRef.current }, wrapped);
|
|
2719
3240
|
}
|
|
2720
3241
|
function useAllStak() {
|
|
2721
|
-
return
|
|
3242
|
+
return React2.useMemo(
|
|
2722
3243
|
() => ({
|
|
2723
3244
|
captureException: (error, ctx) => AllStak.captureException(error, ctx),
|
|
2724
3245
|
captureMessage: (msg, level = "info") => AllStak.captureMessage(msg, level),
|
|
@@ -2788,8 +3309,13 @@ export {
|
|
|
2788
3309
|
ALWAYS_REDACT_QUERY,
|
|
2789
3310
|
AllStak,
|
|
2790
3311
|
AllStakClient,
|
|
3312
|
+
AllStakMaskedView,
|
|
3313
|
+
AllStakPrivacyView,
|
|
2791
3314
|
AllStakProvider,
|
|
3315
|
+
AllStakSensitiveText,
|
|
3316
|
+
AllStakTextInput,
|
|
2792
3317
|
DEFAULT_REDACT_BODY_FIELDS,
|
|
3318
|
+
DEFAULT_SCREENSHOT_CONFIG,
|
|
2793
3319
|
HttpRequestModule,
|
|
2794
3320
|
INGEST_HOST,
|
|
2795
3321
|
REDACTED,
|
|
@@ -2800,18 +3326,30 @@ export {
|
|
|
2800
3326
|
__devTriggerNativeCrash,
|
|
2801
3327
|
__resetAutoNavigationFlagForTest,
|
|
2802
3328
|
__resetConsoleInstrumentationFlagForTest,
|
|
3329
|
+
__resetPrivacyStateForTest,
|
|
2803
3330
|
__resetProviderInstanceForTest,
|
|
3331
|
+
__resetRuntimeModeForTest,
|
|
2804
3332
|
__setNativeModuleForTest,
|
|
2805
3333
|
applyArchitectureTags,
|
|
2806
3334
|
captureBodyResult,
|
|
3335
|
+
captureViaViewShot,
|
|
2807
3336
|
detectArchitecture,
|
|
3337
|
+
detectRuntimeMode,
|
|
2808
3338
|
drainPendingNativeCrashes,
|
|
2809
3339
|
installReactNative,
|
|
2810
3340
|
instrumentNavigationFromLinking,
|
|
2811
3341
|
instrumentReactNavigation,
|
|
3342
|
+
isCapturingScreenshot,
|
|
3343
|
+
isViewShotAvailable,
|
|
3344
|
+
maybeCaptureScreenshot,
|
|
3345
|
+
pickScreenshotConfig,
|
|
2812
3346
|
redactUrl,
|
|
3347
|
+
registerSensitiveRef,
|
|
3348
|
+
resolveScreenshotConfig,
|
|
3349
|
+
runtimeAllowsScreenshot,
|
|
2813
3350
|
sanitizeHeaders,
|
|
2814
3351
|
tryAutoInstrumentNavigation,
|
|
2815
|
-
useAllStak
|
|
3352
|
+
useAllStak,
|
|
3353
|
+
useAllStakPrivacy
|
|
2816
3354
|
};
|
|
2817
3355
|
//# sourceMappingURL=index.mjs.map
|