@scalebun/react-native 2.0.0 → 2.0.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/android/src/main/java/com/scalebun/rn/ota/BundleDownloader.kt +10 -0
- package/android/src/main/java/com/scalebun/rn/ota/DeviceIntegrity.kt +75 -0
- package/android/src/main/java/com/scalebun/rn/ota/ScaleBunOtaModule.kt +18 -3
- package/android/src/main/java/com/scalebun/rn/ota/SlotManager.kt +62 -1
- package/android/src/main/java/com/scalebun/rn/ota/TlsPinning.kt +118 -0
- package/android/src/oldarch/java/com/scalebun/rn/ota/ScaleBunOtaSpec.kt +2 -0
- package/dist/scalebun.full.js +840 -114
- package/dist/scalebun.slim.js +838 -112
- package/ios/ReplaySdk.swift +65 -2
- package/lib/commonjs/bootstrap/SDKBootstrapper.js +30 -0
- package/lib/commonjs/core/config/schema.js +16 -0
- package/lib/commonjs/core/constants/version.js +1 -1
- package/lib/commonjs/features/journey/ScaleBunDebugRoot.js +293 -5
- package/lib/commonjs/features/journey/ScaleBunScrollView.js +28 -0
- package/lib/commonjs/features/journey/autoInstrumentScroll.js +161 -0
- package/lib/commonjs/features/journey/interactionProtocol.js +131 -10
- package/lib/commonjs/features/journey/scrollContext.js +146 -0
- package/lib/commonjs/features/journey/targetGeometry.js +164 -0
- package/lib/commonjs/features/journey/touchTarget.js +152 -24
- package/lib/commonjs/features/journey/uiState.js +81 -1
- package/lib/commonjs/features/navigation/AutoScreenDetector.js +74 -5
- package/lib/commonjs/features/ota/OtaOrchestrator.js +69 -22
- package/lib/commonjs/features/session/SessionManager.js +63 -0
- package/lib/commonjs/public/ScaleBunFacade.js +18 -16
- package/lib/module/bootstrap/SDKBootstrapper.js +30 -0
- package/lib/module/core/config/schema.js +16 -0
- package/lib/module/core/constants/version.js +1 -1
- package/lib/module/features/journey/ScaleBunDebugRoot.js +295 -7
- package/lib/module/features/journey/ScaleBunScrollView.js +28 -0
- package/lib/module/features/journey/autoInstrumentScroll.js +155 -0
- package/lib/module/features/journey/interactionProtocol.js +128 -8
- package/lib/module/features/journey/scrollContext.js +135 -0
- package/lib/module/features/journey/targetGeometry.js +155 -0
- package/lib/module/features/journey/touchTarget.js +151 -24
- package/lib/module/features/journey/uiState.js +78 -1
- package/lib/module/features/navigation/AutoScreenDetector.js +74 -5
- package/lib/module/features/ota/OtaOrchestrator.js +69 -22
- package/lib/module/features/session/SessionManager.js +63 -0
- package/lib/module/public/ScaleBunFacade.js +18 -16
- package/lib/typescript/core/config/schema.d.ts +2 -0
- package/lib/typescript/core/constants/version.d.ts +1 -1
- package/lib/typescript/features/journey/autoInstrumentScroll.d.ts +46 -0
- package/lib/typescript/features/journey/interactionProtocol.d.ts +139 -1
- package/lib/typescript/features/journey/scrollContext.d.ts +85 -0
- package/lib/typescript/features/journey/targetGeometry.d.ts +114 -0
- package/lib/typescript/features/journey/touchTarget.d.ts +148 -15
- package/lib/typescript/features/journey/uiState.d.ts +39 -0
- package/lib/typescript/features/navigation/AutoScreenDetector.d.ts +48 -2
- package/lib/typescript/features/ota/OtaOrchestrator.d.ts +7 -0
- package/lib/typescript/features/session/SessionManager.d.ts +55 -0
- package/lib/typescript/public/types.d.ts +10 -0
- package/lib/typescript/specs/NativeScaleBunOta.d.ts +2 -0
- package/package.json +2 -2
- package/src/bootstrap/SDKBootstrapper.ts +40 -0
- package/src/core/config/schema.ts +18 -0
- package/src/core/constants/version.ts +1 -1
- package/src/features/journey/ScaleBunDebugRoot.tsx +288 -7
- package/src/features/journey/ScaleBunScrollView.tsx +29 -0
- package/src/features/journey/autoInstrumentScroll.ts +155 -0
- package/src/features/journey/interactionProtocol.ts +188 -10
- package/src/features/journey/scrollContext.ts +137 -0
- package/src/features/journey/targetGeometry.ts +176 -0
- package/src/features/journey/touchTarget.ts +237 -26
- package/src/features/journey/uiState.ts +80 -1
- package/src/features/navigation/AutoScreenDetector.ts +65 -5
- package/src/features/ota/OtaOrchestrator.ts +76 -22
- package/src/features/session/SessionManager.ts +113 -0
- package/src/public/ScaleBunFacade.ts +18 -16
- package/src/public/types.ts +10 -0
- package/src/specs/NativeScaleBunOta.ts +3 -0
package/dist/scalebun.slim.js
CHANGED
|
@@ -600,7 +600,7 @@ var SDK_VERSION;
|
|
|
600
600
|
var init_version = __esm({
|
|
601
601
|
"lib/module/core/constants/version.js"() {
|
|
602
602
|
"use strict";
|
|
603
|
-
SDK_VERSION = "2.0.
|
|
603
|
+
SDK_VERSION = "2.0.1";
|
|
604
604
|
}
|
|
605
605
|
});
|
|
606
606
|
|
|
@@ -1353,6 +1353,15 @@ var init_AutoScreenDetector = __esm({
|
|
|
1353
1353
|
_manualScreen = null;
|
|
1354
1354
|
_manualScreenTs = 0;
|
|
1355
1355
|
_autoScreen = null;
|
|
1356
|
+
/**
|
|
1357
|
+
* When `_autoScreen` was last written.
|
|
1358
|
+
*
|
|
1359
|
+
* Needed so `getCurrentScreen()` can prefer whichever source spoke MOST RECENTLY.
|
|
1360
|
+
* Without a timestamp the only comparison available was "is the manual value still
|
|
1361
|
+
* fresh", which is a different question and gave the wrong answer — see the note on
|
|
1362
|
+
* `_currentScreen()`.
|
|
1363
|
+
*/
|
|
1364
|
+
_autoScreenTs = 0;
|
|
1356
1365
|
/** `name#key` of the last auto-detected screen VISIT — see _extractActiveVisit. */
|
|
1357
1366
|
_autoVisitSig = null;
|
|
1358
1367
|
_navigationRef = null;
|
|
@@ -1420,6 +1429,7 @@ var init_AutoScreenDetector = __esm({
|
|
|
1420
1429
|
if (this.isManualScreenActive()) return;
|
|
1421
1430
|
if (name !== this._autoScreen) {
|
|
1422
1431
|
this._autoScreen = name;
|
|
1432
|
+
this._autoScreenTs = Date.now();
|
|
1423
1433
|
this._autoVisitSig = name;
|
|
1424
1434
|
this._emitScreenChange(name, "heuristic");
|
|
1425
1435
|
}
|
|
@@ -1427,8 +1437,19 @@ var init_AutoScreenDetector = __esm({
|
|
|
1427
1437
|
}
|
|
1428
1438
|
}
|
|
1429
1439
|
/**
|
|
1430
|
-
*
|
|
1431
|
-
*
|
|
1440
|
+
* Should a WRITER defer to the manual screen right now?
|
|
1441
|
+
*
|
|
1442
|
+
* THIS IS A PRECEDENCE GATE, NOT A VALIDITY TEST, and conflating the two was a real
|
|
1443
|
+
* bug. Its job is what the cooldown constant says: for a short window after a manual
|
|
1444
|
+
* beacon fires, an auto source must not overwrite it — the poller runs every 1500 ms
|
|
1445
|
+
* and would otherwise clobber a fresh declaration with whatever it inferred.
|
|
1446
|
+
*
|
|
1447
|
+
* `getCurrentScreen()` used to reuse this as "is the manual screen still the current
|
|
1448
|
+
* screen", which made a declared screen READABLE for only 2000 ms. Nothing called
|
|
1449
|
+
* `setManualScreen` in production, so the effect was latent — but the moment the
|
|
1450
|
+
* `<ScaleBunScreen>` beacon was wired to it (RN-8), every tap more than two seconds
|
|
1451
|
+
* after a screen mounted would have lost its name, which is very nearly every tap. A
|
|
1452
|
+
* fix that introduced that would have been worse than the gap it closed.
|
|
1432
1453
|
*/
|
|
1433
1454
|
isManualScreenActive() {
|
|
1434
1455
|
if (!this._manualScreen) return false;
|
|
@@ -1474,6 +1495,7 @@ var init_AutoScreenDetector = __esm({
|
|
|
1474
1495
|
const visit = this._extractActiveVisit(state);
|
|
1475
1496
|
if (visit && this._visitSig(visit) !== this._autoVisitSig) {
|
|
1476
1497
|
this._autoScreen = visit.name;
|
|
1498
|
+
this._autoScreenTs = Date.now();
|
|
1477
1499
|
this._autoVisitSig = this._visitSig(visit);
|
|
1478
1500
|
this._emitScreenChange(visit.name, "navRef");
|
|
1479
1501
|
}
|
|
@@ -1492,6 +1514,7 @@ var init_AutoScreenDetector = __esm({
|
|
|
1492
1514
|
const visit = this._extractActiveVisit(state);
|
|
1493
1515
|
if (visit) {
|
|
1494
1516
|
this._autoScreen = visit.name;
|
|
1517
|
+
this._autoScreenTs = Date.now();
|
|
1495
1518
|
this._autoVisitSig = this._visitSig(visit);
|
|
1496
1519
|
this._emitScreenChange(visit.name, "navRef");
|
|
1497
1520
|
}
|
|
@@ -1522,6 +1545,7 @@ var init_AutoScreenDetector = __esm({
|
|
|
1522
1545
|
const visit = this._extractActiveVisit(state);
|
|
1523
1546
|
if (visit && this._visitSig(visit) !== this._autoVisitSig) {
|
|
1524
1547
|
this._autoScreen = visit.name;
|
|
1548
|
+
this._autoScreenTs = Date.now();
|
|
1525
1549
|
this._autoVisitSig = this._visitSig(visit);
|
|
1526
1550
|
this._emitScreenChange(visit.name, "heuristic");
|
|
1527
1551
|
}
|
|
@@ -1620,7 +1644,7 @@ var init_AutoScreenDetector = __esm({
|
|
|
1620
1644
|
if (!reactNav || reactNav.__scalebunAutoHooked) return;
|
|
1621
1645
|
const detector = this;
|
|
1622
1646
|
let installed2 = 0;
|
|
1623
|
-
const
|
|
1647
|
+
const redefine2 = (key, value) => {
|
|
1624
1648
|
try {
|
|
1625
1649
|
Object.defineProperty(reactNav, key, {
|
|
1626
1650
|
value,
|
|
@@ -1670,7 +1694,7 @@ var init_AutoScreenDetector = __esm({
|
|
|
1670
1694
|
});
|
|
1671
1695
|
});
|
|
1672
1696
|
Wrapped.displayName = "ScaleBunNavigationContainer";
|
|
1673
|
-
if (
|
|
1697
|
+
if (redefine2("NavigationContainer", Wrapped)) installed2++;
|
|
1674
1698
|
}
|
|
1675
1699
|
if (typeof reactNav.createNavigationContainerRef === "function") {
|
|
1676
1700
|
const originalCreate = reactNav.createNavigationContainerRef;
|
|
@@ -1682,7 +1706,7 @@ var init_AutoScreenDetector = __esm({
|
|
|
1682
1706
|
}
|
|
1683
1707
|
return ref;
|
|
1684
1708
|
};
|
|
1685
|
-
if (
|
|
1709
|
+
if (redefine2("createNavigationContainerRef", patchedCreate)) installed2++;
|
|
1686
1710
|
}
|
|
1687
1711
|
if (typeof reactNav.useNavigationContainerRef === "function") {
|
|
1688
1712
|
const originalHook = reactNav.useNavigationContainerRef;
|
|
@@ -1694,7 +1718,7 @@ var init_AutoScreenDetector = __esm({
|
|
|
1694
1718
|
}
|
|
1695
1719
|
return ref;
|
|
1696
1720
|
};
|
|
1697
|
-
if (
|
|
1721
|
+
if (redefine2("useNavigationContainerRef", patchedHook)) installed2++;
|
|
1698
1722
|
}
|
|
1699
1723
|
this._autoHookInstalled = installed2 > 0;
|
|
1700
1724
|
try {
|
|
@@ -1743,15 +1767,58 @@ var init_AutoScreenDetector = __esm({
|
|
|
1743
1767
|
this._listeners = [];
|
|
1744
1768
|
this._manualScreen = null;
|
|
1745
1769
|
this._autoScreen = null;
|
|
1770
|
+
this._autoScreenTs = 0;
|
|
1746
1771
|
this._navigationRef = null;
|
|
1747
1772
|
_instance = null;
|
|
1748
1773
|
}
|
|
1749
1774
|
/** Get the current best-known screen name */
|
|
1750
1775
|
getCurrentScreen() {
|
|
1751
|
-
|
|
1752
|
-
|
|
1776
|
+
return this._currentScreen().name;
|
|
1777
|
+
}
|
|
1778
|
+
/**
|
|
1779
|
+
* WHERE the current screen name came from, or 'none'.
|
|
1780
|
+
*
|
|
1781
|
+
* Recorded on every interaction (RN-8) so low screen coverage is DIAGNOSABLE rather
|
|
1782
|
+
* than merely visible. Production shows 13.8% of Android interactions carrying a
|
|
1783
|
+
* screen and 0.5% on iOS, and those numbers cannot today distinguish three different
|
|
1784
|
+
* causes: the app never instrumented anything, the app instrumented manually and the
|
|
1785
|
+
* value never reached interactions, or auto-detection ran and failed. Those have
|
|
1786
|
+
* different owners and different fixes, and guessing between them is how a P0 stays
|
|
1787
|
+
* open.
|
|
1788
|
+
*/
|
|
1789
|
+
getCurrentScreenSource() {
|
|
1790
|
+
return this._currentScreen().source;
|
|
1791
|
+
}
|
|
1792
|
+
/**
|
|
1793
|
+
* LAST WRITER WINS between the manual beacon and auto-detection.
|
|
1794
|
+
*
|
|
1795
|
+
* A screen declared by `<ScaleBunScreen name>` stays current until something else
|
|
1796
|
+
* declares a different one — that is what mounting a screen means. It does not
|
|
1797
|
+
* expire, which is the correction here: the previous rule returned the manual name
|
|
1798
|
+
* only while it was under 2000 ms old and then silently fell back to the auto value,
|
|
1799
|
+
* which in a manually-instrumented app is `null`.
|
|
1800
|
+
*
|
|
1801
|
+
* Comparing timestamps rather than hard-coding a priority also handles the real
|
|
1802
|
+
* interleaving: an app can use React Navigation for most screens AND drop a manual
|
|
1803
|
+
* beacon on one modal, and whichever spoke last is the truth.
|
|
1804
|
+
*/
|
|
1805
|
+
_currentScreen() {
|
|
1806
|
+
const manual = this._manualScreen;
|
|
1807
|
+
const auto = this._autoScreen;
|
|
1808
|
+
if (manual && (!auto || this._manualScreenTs >= this._autoScreenTs)) {
|
|
1809
|
+
return {
|
|
1810
|
+
name: manual,
|
|
1811
|
+
source: "manual"
|
|
1812
|
+
};
|
|
1753
1813
|
}
|
|
1754
|
-
return
|
|
1814
|
+
if (auto) return {
|
|
1815
|
+
name: auto,
|
|
1816
|
+
source: "navigation"
|
|
1817
|
+
};
|
|
1818
|
+
return {
|
|
1819
|
+
name: null,
|
|
1820
|
+
source: "none"
|
|
1821
|
+
};
|
|
1755
1822
|
}
|
|
1756
1823
|
// ─── Internal ───────────────────────────────────────────────────────
|
|
1757
1824
|
_emitScreenChange(name, source) {
|
|
@@ -3632,17 +3699,54 @@ var init_automaticEvents = __esm({
|
|
|
3632
3699
|
function generateInteractionId() {
|
|
3633
3700
|
return `ixj-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
3634
3701
|
}
|
|
3635
|
-
function
|
|
3636
|
-
|
|
3637
|
-
let
|
|
3638
|
-
|
|
3702
|
+
function correlateInteraction(starts, occurredAt, toleranceMs = CORRELATION_TOLERANCE_MS, marginMs = CORRELATION_MARGIN_MS) {
|
|
3703
|
+
const candidates = [];
|
|
3704
|
+
for (let i = 0; i < starts.length; i++) {
|
|
3705
|
+
const start = starts[i];
|
|
3706
|
+
if (!start) continue;
|
|
3639
3707
|
const delta = Math.abs(start.occurredAt - occurredAt);
|
|
3640
|
-
if (delta
|
|
3641
|
-
|
|
3642
|
-
|
|
3708
|
+
if (delta <= toleranceMs) candidates.push({
|
|
3709
|
+
index: i,
|
|
3710
|
+
delta
|
|
3711
|
+
});
|
|
3712
|
+
}
|
|
3713
|
+
if (candidates.length === 0) {
|
|
3714
|
+
return {
|
|
3715
|
+
index: -1,
|
|
3716
|
+
outcome: "none",
|
|
3717
|
+
deltaMs: null,
|
|
3718
|
+
runnerUpDeltaMs: null
|
|
3719
|
+
};
|
|
3720
|
+
}
|
|
3721
|
+
candidates.sort((a, b) => a.delta - b.delta || a.index - b.index);
|
|
3722
|
+
const best = candidates[0];
|
|
3723
|
+
const runnerUp = candidates[1];
|
|
3724
|
+
if (runnerUp && runnerUp.delta - best.delta < marginMs) {
|
|
3725
|
+
return {
|
|
3726
|
+
index: -1,
|
|
3727
|
+
outcome: "ambiguous",
|
|
3728
|
+
deltaMs: best.delta,
|
|
3729
|
+
runnerUpDeltaMs: runnerUp.delta
|
|
3730
|
+
};
|
|
3731
|
+
}
|
|
3732
|
+
return {
|
|
3733
|
+
start: starts[best.index],
|
|
3734
|
+
index: best.index,
|
|
3735
|
+
outcome: "exact",
|
|
3736
|
+
deltaMs: best.delta,
|
|
3737
|
+
runnerUpDeltaMs: runnerUp ? runnerUp.delta : null
|
|
3738
|
+
};
|
|
3739
|
+
}
|
|
3740
|
+
function pruneInteractionStarts(starts, now2, toleranceMs = CORRELATION_TOLERANCE_MS) {
|
|
3741
|
+
let removed = 0;
|
|
3742
|
+
for (let i = starts.length - 1; i >= 0; i--) {
|
|
3743
|
+
const start = starts[i];
|
|
3744
|
+
if (!start || now2 - start.occurredAt > toleranceMs) {
|
|
3745
|
+
starts.splice(i, 1);
|
|
3746
|
+
removed++;
|
|
3643
3747
|
}
|
|
3644
3748
|
}
|
|
3645
|
-
return
|
|
3749
|
+
return removed;
|
|
3646
3750
|
}
|
|
3647
3751
|
function automaticInteractionProperties(payload, screenName, canonicalMirror) {
|
|
3648
3752
|
return {
|
|
@@ -3660,11 +3764,13 @@ function automaticInteractionProperties(payload, screenName, canonicalMirror) {
|
|
|
3660
3764
|
canonical_mirror: canonicalMirror || void 0
|
|
3661
3765
|
};
|
|
3662
3766
|
}
|
|
3663
|
-
var INTERACTION_PROTOCOL_VERSION;
|
|
3767
|
+
var INTERACTION_PROTOCOL_VERSION, CORRELATION_TOLERANCE_MS, CORRELATION_MARGIN_MS;
|
|
3664
3768
|
var init_interactionProtocol = __esm({
|
|
3665
3769
|
"lib/module/features/journey/interactionProtocol.js"() {
|
|
3666
3770
|
"use strict";
|
|
3667
3771
|
INTERACTION_PROTOCOL_VERSION = 1;
|
|
3772
|
+
CORRELATION_TOLERANCE_MS = 1500;
|
|
3773
|
+
CORRELATION_MARGIN_MS = 100;
|
|
3668
3774
|
}
|
|
3669
3775
|
});
|
|
3670
3776
|
|
|
@@ -4407,6 +4513,69 @@ var init_SessionManager = __esm({
|
|
|
4407
4513
|
state_status: stateStatus,
|
|
4408
4514
|
ui: details?.ui,
|
|
4409
4515
|
target_id: details?.targetId,
|
|
4516
|
+
/**
|
|
4517
|
+
* RN-6 — shipped with the row so the join is auditable instead of assumed.
|
|
4518
|
+
*
|
|
4519
|
+
* Without it, an interaction with no `target_id` is ambiguous in the worst way:
|
|
4520
|
+
* it could mean the control genuinely had no identity, or that the correlation
|
|
4521
|
+
* refused and the identity exists but could not be attributed. Those are
|
|
4522
|
+
* different defects with different owners, and the accounting lane cannot give
|
|
4523
|
+
* an honest unplaceable REASON without knowing which.
|
|
4524
|
+
*
|
|
4525
|
+
* Undefined on the JS-fallback path (no native event, so nothing was joined),
|
|
4526
|
+
* which is itself the correct answer there rather than a fabricated 'exact'.
|
|
4527
|
+
*/
|
|
4528
|
+
correlation: details?.correlation,
|
|
4529
|
+
correlation_delta_ms: details?.correlationDeltaMs,
|
|
4530
|
+
correlation_runner_up_delta_ms: details?.correlationRunnerUpDeltaMs,
|
|
4531
|
+
/**
|
|
4532
|
+
* RN-1/RN-2 — stored alongside `target_id` so a reader can tell an authored
|
|
4533
|
+
* identifier from a structural inference. 'path' means the app authored no testID
|
|
4534
|
+
* and the identity was derived from component names, which is useful but will
|
|
4535
|
+
* change under refactoring; a dashboard that presents the two identically invites
|
|
4536
|
+
* someone to trust a key that is about to move.
|
|
4537
|
+
*/
|
|
4538
|
+
target_source: details?.targetSource,
|
|
4539
|
+
/**
|
|
4540
|
+
* RN-8 — stored so "13.8% of interactions have a screen" becomes answerable.
|
|
4541
|
+
* 'none' means the app instrumented nothing the SDK could see; 'manual' means a
|
|
4542
|
+
* <ScaleBunScreen> beacon; 'navigation' means the React Navigation hook. Three
|
|
4543
|
+
* different owners behind one number that today has none.
|
|
4544
|
+
*/
|
|
4545
|
+
screen_source: details?.screenSource,
|
|
4546
|
+
/**
|
|
4547
|
+
* RN-13 — what makes "the heat is on this control" checkable instead of assumed.
|
|
4548
|
+
* Absent when the control could not be measured synchronously, which is a fact worth
|
|
4549
|
+
* storing: an unmeasurable control and a mis-placed tap are different findings.
|
|
4550
|
+
*/
|
|
4551
|
+
target_rect: details?.targetRect,
|
|
4552
|
+
/**
|
|
4553
|
+
* RN-3 — the backend already reads scrollX/scrollY off the payload into the fact
|
|
4554
|
+
* table's own columns (it has done since the web SDK, which sends `sx`/`sy`), so
|
|
4555
|
+
* these need no migration. Mobile simply never sent them.
|
|
4556
|
+
*
|
|
4557
|
+
* `scroll_source` says whether an offset was available at all: 'none' means the app
|
|
4558
|
+
* uses a plain <ScrollView> rather than the SDK's wrapper, which is a coverage gap in
|
|
4559
|
+
* the integration and not a tap at the top of a list.
|
|
4560
|
+
*/
|
|
4561
|
+
scrollX: details?.scrollX,
|
|
4562
|
+
scrollY: details?.scrollY,
|
|
4563
|
+
scroll_source: details?.scrollSource,
|
|
4564
|
+
/**
|
|
4565
|
+
* RN-4 — what stops a recycled native view merging two different rows into one
|
|
4566
|
+
* hotspot. The key is the app's own keyExtractor output, so it is stable across
|
|
4567
|
+
* scrolling and across sessions; the index is carried so a recycled slot (same index,
|
|
4568
|
+
* different key) can be proven rather than inferred.
|
|
4569
|
+
*/
|
|
4570
|
+
item_key: details?.itemKey,
|
|
4571
|
+
item_index: details?.itemIndex,
|
|
4572
|
+
/**
|
|
4573
|
+
* RN-15 — where in the control, not just which control. Turns "they tapped Add to
|
|
4574
|
+
* Cart" into "they tapped its left edge", which is what tells you whether a hit area
|
|
4575
|
+
* is right. Outside 0..1 is a hitSlop tap and is kept as such.
|
|
4576
|
+
*/
|
|
4577
|
+
local_u: details?.localU,
|
|
4578
|
+
local_v: details?.localV,
|
|
4410
4579
|
gestureType,
|
|
4411
4580
|
x: details?.x,
|
|
4412
4581
|
y: details?.y,
|
|
@@ -5498,10 +5667,10 @@ function requestExport(transport, enabled, presetOrOptions) {
|
|
|
5498
5667
|
return;
|
|
5499
5668
|
}
|
|
5500
5669
|
const timer = setTimeout(() => {
|
|
5501
|
-
const
|
|
5502
|
-
if (
|
|
5670
|
+
const pending5 = _pendingExports.get(requestId);
|
|
5671
|
+
if (pending5) {
|
|
5503
5672
|
_pendingExports.delete(requestId);
|
|
5504
|
-
|
|
5673
|
+
pending5.resolve({
|
|
5505
5674
|
success: false,
|
|
5506
5675
|
error: `Export timed out after ${EXPORT_TIMEOUT_MS / 1e3}s`
|
|
5507
5676
|
});
|
|
@@ -5543,14 +5712,14 @@ function handleExportResult(args) {
|
|
|
5543
5712
|
try {
|
|
5544
5713
|
const requestId = args.requestId;
|
|
5545
5714
|
if (!requestId) return;
|
|
5546
|
-
const
|
|
5547
|
-
if (!
|
|
5715
|
+
const pending5 = _pendingExports.get(requestId);
|
|
5716
|
+
if (!pending5) {
|
|
5548
5717
|
logger.warn(`[ExportBridge] Received result for unknown requestId: ${requestId}`);
|
|
5549
5718
|
return;
|
|
5550
5719
|
}
|
|
5551
|
-
clearTimeout(
|
|
5720
|
+
clearTimeout(pending5.timer);
|
|
5552
5721
|
_pendingExports.delete(requestId);
|
|
5553
|
-
|
|
5722
|
+
pending5.resolve({
|
|
5554
5723
|
success: args.success ?? false,
|
|
5555
5724
|
filePath: args.filePath,
|
|
5556
5725
|
error: args.error,
|
|
@@ -5561,9 +5730,9 @@ function handleExportResult(args) {
|
|
|
5561
5730
|
}
|
|
5562
5731
|
}
|
|
5563
5732
|
function teardownExports() {
|
|
5564
|
-
for (const [,
|
|
5565
|
-
clearTimeout(
|
|
5566
|
-
|
|
5733
|
+
for (const [, pending5] of _pendingExports) {
|
|
5734
|
+
clearTimeout(pending5.timer);
|
|
5735
|
+
pending5.reject(new Error("Debug disabled \u2014 export cancelled"));
|
|
5567
5736
|
}
|
|
5568
5737
|
_pendingExports.clear();
|
|
5569
5738
|
}
|
|
@@ -6785,6 +6954,174 @@ var init_installationId = __esm({
|
|
|
6785
6954
|
}
|
|
6786
6955
|
});
|
|
6787
6956
|
|
|
6957
|
+
// lib/module/features/journey/scrollContext.js
|
|
6958
|
+
var scrollContext_exports = {};
|
|
6959
|
+
__export(scrollContext_exports, {
|
|
6960
|
+
SCROLL_OFFSET_TTL_MS: () => SCROLL_OFFSET_TTL_MS,
|
|
6961
|
+
clearScrollOffsets: () => clearScrollOffsets,
|
|
6962
|
+
nativeTagOf: () => nativeTagOf,
|
|
6963
|
+
nearestRecordedOffset: () => nearestRecordedOffset,
|
|
6964
|
+
recordScrollOffset: () => recordScrollOffset,
|
|
6965
|
+
trackedScrollerCount: () => trackedScrollerCount
|
|
6966
|
+
});
|
|
6967
|
+
function nativeTagOf(node) {
|
|
6968
|
+
if (!node || typeof node !== "object") return null;
|
|
6969
|
+
const n = node;
|
|
6970
|
+
const tag = typeof n.__nativeTag === "number" ? n.__nativeTag : n._nativeTag;
|
|
6971
|
+
return typeof tag === "number" && Number.isFinite(tag) ? tag : null;
|
|
6972
|
+
}
|
|
6973
|
+
function recordScrollOffset(tag, x, y, now2 = Date.now()) {
|
|
6974
|
+
if (tag === null || !Number.isFinite(x) || !Number.isFinite(y)) return;
|
|
6975
|
+
offsets.set(tag, {
|
|
6976
|
+
x,
|
|
6977
|
+
y,
|
|
6978
|
+
at: now2
|
|
6979
|
+
});
|
|
6980
|
+
if (offsets.size > MAX_TRACKED_SCROLLERS) {
|
|
6981
|
+
let oldestTag = null;
|
|
6982
|
+
let oldestAt = Infinity;
|
|
6983
|
+
for (const [k, v] of offsets) {
|
|
6984
|
+
if (v.at < oldestAt) {
|
|
6985
|
+
oldestAt = v.at;
|
|
6986
|
+
oldestTag = k;
|
|
6987
|
+
}
|
|
6988
|
+
}
|
|
6989
|
+
if (oldestTag !== null) offsets.delete(oldestTag);
|
|
6990
|
+
}
|
|
6991
|
+
}
|
|
6992
|
+
function nearestRecordedOffset(ancestorTags, now2 = Date.now()) {
|
|
6993
|
+
for (const tag of ancestorTags) {
|
|
6994
|
+
if (tag === null) continue;
|
|
6995
|
+
const hit = offsets.get(tag);
|
|
6996
|
+
if (!hit) continue;
|
|
6997
|
+
if (now2 - hit.at > SCROLL_OFFSET_TTL_MS) {
|
|
6998
|
+
offsets.delete(tag);
|
|
6999
|
+
continue;
|
|
7000
|
+
}
|
|
7001
|
+
return {
|
|
7002
|
+
x: hit.x,
|
|
7003
|
+
y: hit.y,
|
|
7004
|
+
tag
|
|
7005
|
+
};
|
|
7006
|
+
}
|
|
7007
|
+
return null;
|
|
7008
|
+
}
|
|
7009
|
+
function clearScrollOffsets() {
|
|
7010
|
+
offsets.clear();
|
|
7011
|
+
}
|
|
7012
|
+
function trackedScrollerCount() {
|
|
7013
|
+
return offsets.size;
|
|
7014
|
+
}
|
|
7015
|
+
var SCROLL_OFFSET_TTL_MS, MAX_TRACKED_SCROLLERS, offsets;
|
|
7016
|
+
var init_scrollContext = __esm({
|
|
7017
|
+
"lib/module/features/journey/scrollContext.js"() {
|
|
7018
|
+
"use strict";
|
|
7019
|
+
SCROLL_OFFSET_TTL_MS = 5 * 6e4;
|
|
7020
|
+
MAX_TRACKED_SCROLLERS = 32;
|
|
7021
|
+
offsets = /* @__PURE__ */ new Map();
|
|
7022
|
+
}
|
|
7023
|
+
});
|
|
7024
|
+
|
|
7025
|
+
// lib/module/features/journey/autoInstrumentScroll.js
|
|
7026
|
+
var autoInstrumentScroll_exports = {};
|
|
7027
|
+
__export(autoInstrumentScroll_exports, {
|
|
7028
|
+
autoInstrumentScrollViews: () => autoInstrumentScrollViews,
|
|
7029
|
+
resetAutoInstrumentForTests: () => resetAutoInstrumentForTests
|
|
7030
|
+
});
|
|
7031
|
+
function redefine(target, key, value) {
|
|
7032
|
+
try {
|
|
7033
|
+
Object.defineProperty(target, key, {
|
|
7034
|
+
configurable: true,
|
|
7035
|
+
enumerable: true,
|
|
7036
|
+
writable: true,
|
|
7037
|
+
value
|
|
7038
|
+
});
|
|
7039
|
+
return target[key] === value;
|
|
7040
|
+
} catch {
|
|
7041
|
+
return false;
|
|
7042
|
+
}
|
|
7043
|
+
}
|
|
7044
|
+
function instrument(Original, React14) {
|
|
7045
|
+
const Wrapped = React14.forwardRef((props, ref) => {
|
|
7046
|
+
const onScroll = (event) => {
|
|
7047
|
+
try {
|
|
7048
|
+
const target = event?.nativeEvent?.target;
|
|
7049
|
+
const offset = event?.nativeEvent?.contentOffset;
|
|
7050
|
+
if (offset) {
|
|
7051
|
+
recordScrollOffset(typeof target === "number" ? target : nativeTagOf(target), offset.x, offset.y);
|
|
7052
|
+
}
|
|
7053
|
+
} catch {
|
|
7054
|
+
}
|
|
7055
|
+
try {
|
|
7056
|
+
props?.onScroll?.(event);
|
|
7057
|
+
} catch (e) {
|
|
7058
|
+
setTimeout(() => {
|
|
7059
|
+
throw e;
|
|
7060
|
+
}, 0);
|
|
7061
|
+
}
|
|
7062
|
+
};
|
|
7063
|
+
const throttle = props?.scrollEventThrottle ?? 16;
|
|
7064
|
+
return React14.createElement(Original, {
|
|
7065
|
+
...props,
|
|
7066
|
+
scrollEventThrottle: throttle,
|
|
7067
|
+
onScroll,
|
|
7068
|
+
ref
|
|
7069
|
+
});
|
|
7070
|
+
});
|
|
7071
|
+
Wrapped.displayName = "ScaleBunAutoScrollView";
|
|
7072
|
+
return Wrapped;
|
|
7073
|
+
}
|
|
7074
|
+
function autoInstrumentScrollViews() {
|
|
7075
|
+
if (alreadyPatched) return {
|
|
7076
|
+
installed: true
|
|
7077
|
+
};
|
|
7078
|
+
try {
|
|
7079
|
+
const RN = require("react-native");
|
|
7080
|
+
const React14 = require("react");
|
|
7081
|
+
const Original = RN.ScrollView;
|
|
7082
|
+
if (typeof Original !== "function" && typeof Original !== "object") {
|
|
7083
|
+
return {
|
|
7084
|
+
installed: false,
|
|
7085
|
+
reason: "react-native has no ScrollView export to wrap"
|
|
7086
|
+
};
|
|
7087
|
+
}
|
|
7088
|
+
if (Original.__scalebunInstrumented) {
|
|
7089
|
+
alreadyPatched = true;
|
|
7090
|
+
return {
|
|
7091
|
+
installed: true
|
|
7092
|
+
};
|
|
7093
|
+
}
|
|
7094
|
+
const Wrapped = instrument(Original, React14);
|
|
7095
|
+
Wrapped.__scalebunInstrumented = true;
|
|
7096
|
+
if (!redefine(RN, "ScrollView", Wrapped)) {
|
|
7097
|
+
return {
|
|
7098
|
+
installed: false,
|
|
7099
|
+
reason: "react-native's ScrollView export could not be replaced \u2014 it is a getter with no configurable descriptor on this bundler. Use ScaleBunScrollView explicitly instead."
|
|
7100
|
+
};
|
|
7101
|
+
}
|
|
7102
|
+
alreadyPatched = true;
|
|
7103
|
+
return {
|
|
7104
|
+
installed: true
|
|
7105
|
+
};
|
|
7106
|
+
} catch (e) {
|
|
7107
|
+
return {
|
|
7108
|
+
installed: false,
|
|
7109
|
+
reason: e?.message ?? "unknown failure"
|
|
7110
|
+
};
|
|
7111
|
+
}
|
|
7112
|
+
}
|
|
7113
|
+
function resetAutoInstrumentForTests() {
|
|
7114
|
+
alreadyPatched = false;
|
|
7115
|
+
}
|
|
7116
|
+
var alreadyPatched;
|
|
7117
|
+
var init_autoInstrumentScroll = __esm({
|
|
7118
|
+
"lib/module/features/journey/autoInstrumentScroll.js"() {
|
|
7119
|
+
"use strict";
|
|
7120
|
+
init_scrollContext();
|
|
7121
|
+
alreadyPatched = false;
|
|
7122
|
+
}
|
|
7123
|
+
});
|
|
7124
|
+
|
|
6788
7125
|
// lib/module/features/engage/engageSignals.js
|
|
6789
7126
|
var EngageSignalBus, engageSignals;
|
|
6790
7127
|
var init_engageSignals = __esm({
|
|
@@ -7220,10 +7557,10 @@ async function deliverOtaEvents(params) {
|
|
|
7220
7557
|
})
|
|
7221
7558
|
});
|
|
7222
7559
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
7223
|
-
|
|
7560
|
+
logger.debug(`[OTA] Delivered ${items.length} telemetry event(s)`);
|
|
7224
7561
|
} catch (err) {
|
|
7225
7562
|
for (const e of events) otaEventEmitter.emit(e);
|
|
7226
|
-
|
|
7563
|
+
logger.debug(`[OTA] Telemetry delivery failed (will retry next sync): ${err?.message}`);
|
|
7227
7564
|
}
|
|
7228
7565
|
}
|
|
7229
7566
|
var import_react_native33, PROGRESS_POST_INTERVAL_MS, DEFAULT_HEALTHY_AFTER_MS, OtaOrchestrator, otaOrchestrator;
|
|
@@ -7256,6 +7593,8 @@ var init_OtaOrchestrator = __esm({
|
|
|
7256
7593
|
return this.currentBundle;
|
|
7257
7594
|
}
|
|
7258
7595
|
isRestartRequiredState = false;
|
|
7596
|
+
/** Root/jailbreak indicators observed at init; empty when none found. */
|
|
7597
|
+
integrityIndicators = [];
|
|
7259
7598
|
healthyTimer = null;
|
|
7260
7599
|
bootGuardConfig = {};
|
|
7261
7600
|
environment = null;
|
|
@@ -7290,17 +7629,27 @@ var init_OtaOrchestrator = __esm({
|
|
|
7290
7629
|
logger.warn("[OTA] Running bridgeless but the React Native version could not be determined. If this app is below RN 0.76.1, OTA updates will install and never load.");
|
|
7291
7630
|
}
|
|
7292
7631
|
if (this.environment.requiresReactHostIntegration) {
|
|
7293
|
-
|
|
7632
|
+
logger.debug("[OTA] React Native >= 0.82 detected. The only valid Android integration is getDefaultReactHost(..., jsBundleFilePath = ScaleBunOtaModule.getJSBundleFile(ctx)) \u2014 ReactNativeHost and its getJSBundleFile() override no longer exist.");
|
|
7294
7633
|
}
|
|
7295
7634
|
this.initialized = true;
|
|
7296
7635
|
this.enabled = true;
|
|
7636
|
+
try {
|
|
7637
|
+
const raw = NativeScaleBunOta_default?.getDeviceIntegrity?.();
|
|
7638
|
+
this.integrityIndicators = raw ? JSON.parse(raw) : [];
|
|
7639
|
+
if (this.integrityIndicators.length > 0) {
|
|
7640
|
+
logger.warn(`[OTA] Device integrity indicators present: ${this.integrityIndicators.join(", ")}. Updates still install and are still signature-verified, but an installed bundle on this device can be modified by other privileged processes.`);
|
|
7641
|
+
}
|
|
7642
|
+
} catch {
|
|
7643
|
+
this.integrityIndicators = [];
|
|
7644
|
+
}
|
|
7297
7645
|
this.bootGuardConfig = config ?? {};
|
|
7298
|
-
|
|
7646
|
+
logger.debug(`[OTA] Orchestrator initialized (RN ${this.environment.rnVersionString ?? "unknown"}${this.environment.bridgeless ? ", bridgeless" : ""}${this.environment.hermes ? `, Hermes HBC v${this.environment.hermesBytecodeVersion ?? "?"}` : ""})`);
|
|
7299
7647
|
const slotState = this.readSlotState();
|
|
7300
7648
|
this.hydrateCurrentBundleFromSlots(slotState);
|
|
7301
7649
|
void prefetchDeviceCountry();
|
|
7302
7650
|
this.checkBootGuardRecovery(slotState);
|
|
7303
7651
|
this.verifyRunningBundleIdentity();
|
|
7652
|
+
this.startBootGuardHeartbeat(slotState);
|
|
7304
7653
|
});
|
|
7305
7654
|
}
|
|
7306
7655
|
/**
|
|
@@ -7331,13 +7680,13 @@ var init_OtaOrchestrator = __esm({
|
|
|
7331
7680
|
releaseId: record4.releaseId ?? void 0,
|
|
7332
7681
|
sha256: record4.sha256
|
|
7333
7682
|
};
|
|
7334
|
-
|
|
7683
|
+
logger.debug(`[OTA] Running bundle v${record4.version} (${record4.bundleId})`);
|
|
7335
7684
|
return;
|
|
7336
7685
|
}
|
|
7337
7686
|
this.currentBundle = {
|
|
7338
7687
|
sha256: current.sha256
|
|
7339
7688
|
};
|
|
7340
|
-
|
|
7689
|
+
logger.debug(`[OTA] Running an OTA bundle (sha ${String(current.sha256).slice(0, 12)}\u2026) with no local install record.`);
|
|
7341
7690
|
} catch {
|
|
7342
7691
|
}
|
|
7343
7692
|
}
|
|
@@ -7367,7 +7716,7 @@ var init_OtaOrchestrator = __esm({
|
|
|
7367
7716
|
}
|
|
7368
7717
|
if (!record4.identityToken) return;
|
|
7369
7718
|
if (running === record4.identityToken) {
|
|
7370
|
-
|
|
7719
|
+
logger.debug("[OTA] Install verified \u2014 running bundle matches what was installed.");
|
|
7371
7720
|
this.retireIdentityToken(record4);
|
|
7372
7721
|
return;
|
|
7373
7722
|
}
|
|
@@ -7470,6 +7819,13 @@ var init_OtaOrchestrator = __esm({
|
|
|
7470
7819
|
isEnabled() {
|
|
7471
7820
|
return this.enabled;
|
|
7472
7821
|
}
|
|
7822
|
+
/**
|
|
7823
|
+
* Root/jailbreak indicators detected on this device, as stable reason strings.
|
|
7824
|
+
* Empty means nothing was detected — not proof the device is clean.
|
|
7825
|
+
*/
|
|
7826
|
+
deviceIntegrityIndicators() {
|
|
7827
|
+
return [...this.integrityIndicators];
|
|
7828
|
+
}
|
|
7473
7829
|
isRestartRequired() {
|
|
7474
7830
|
return this.isRestartRequiredState;
|
|
7475
7831
|
}
|
|
@@ -7563,7 +7919,7 @@ var init_OtaOrchestrator = __esm({
|
|
|
7563
7919
|
error: "OTA native module disabled or missing"
|
|
7564
7920
|
};
|
|
7565
7921
|
}
|
|
7566
|
-
|
|
7922
|
+
logger.debug("[OTA] Sync started\u2026");
|
|
7567
7923
|
if (this.currentBundle?.id) {
|
|
7568
7924
|
otaEventEmitter.emitSimple("CHECK", this.currentBundle.id, {
|
|
7569
7925
|
releaseId: this.currentBundle.releaseId,
|
|
@@ -7572,7 +7928,7 @@ var init_OtaOrchestrator = __esm({
|
|
|
7572
7928
|
}
|
|
7573
7929
|
const checkRes = await this.checkForUpdate(params);
|
|
7574
7930
|
if (checkRes.action === "NONE") {
|
|
7575
|
-
|
|
7931
|
+
logger.debug("[OTA] App is up to date");
|
|
7576
7932
|
this.startBootGuardHeartbeat();
|
|
7577
7933
|
return {
|
|
7578
7934
|
status: "UP_TO_DATE"
|
|
@@ -7615,7 +7971,7 @@ var init_OtaOrchestrator = __esm({
|
|
|
7615
7971
|
version: bundle.version
|
|
7616
7972
|
});
|
|
7617
7973
|
const downloadStart = Date.now();
|
|
7618
|
-
|
|
7974
|
+
logger.debug(`[OTA] Downloading update v${bundle.version}\u2026`);
|
|
7619
7975
|
let lastPostAt = 0;
|
|
7620
7976
|
const postProgress = (bytesRead, status) => {
|
|
7621
7977
|
const total = bundle.size > 0 ? bundle.size : Math.max(bytesRead, 1);
|
|
@@ -7693,7 +8049,7 @@ var init_OtaOrchestrator = __esm({
|
|
|
7693
8049
|
staged = true;
|
|
7694
8050
|
} else {
|
|
7695
8051
|
if (bundle.patch && NativeScaleBunOta_default.stagePatch) {
|
|
7696
|
-
|
|
8052
|
+
logger.debug(`[OTA] Patch available (${(bundle.patch.size / 1024).toFixed(0)} KB vs ${(bundle.size / 1024).toFixed(0)} KB full) \u2014 trying patch\u2026`);
|
|
7697
8053
|
try {
|
|
7698
8054
|
staged = await NativeScaleBunOta_default.stagePatch(
|
|
7699
8055
|
bundle.patch.url,
|
|
@@ -7703,7 +8059,7 @@ var init_OtaOrchestrator = __esm({
|
|
|
7703
8059
|
);
|
|
7704
8060
|
if (staged) {
|
|
7705
8061
|
patchUsed = true;
|
|
7706
|
-
|
|
8062
|
+
logger.debug("[OTA] Patch applied successfully");
|
|
7707
8063
|
} else {
|
|
7708
8064
|
logger.warn("[OTA] Patch staging returned false \u2014 falling back to full bundle");
|
|
7709
8065
|
}
|
|
@@ -7772,7 +8128,7 @@ var init_OtaOrchestrator = __esm({
|
|
|
7772
8128
|
releaseId: bundle.releaseId,
|
|
7773
8129
|
version: bundle.version
|
|
7774
8130
|
});
|
|
7775
|
-
|
|
8131
|
+
logger.debug(`[OTA] Update v${bundle.version} installed successfully!`);
|
|
7776
8132
|
await deliverOtaEvents(params);
|
|
7777
8133
|
const installMode = bundle.installMode ?? "ON_NEXT_RESTART";
|
|
7778
8134
|
if (params.autoRestart || installMode === "IMMEDIATE") {
|
|
@@ -7803,24 +8159,23 @@ var init_OtaOrchestrator = __esm({
|
|
|
7803
8159
|
* native boot-guard marker. If JS crashes before the timer fires, the
|
|
7804
8160
|
* marker stays set and native code reverts on next cold start.
|
|
7805
8161
|
*/
|
|
7806
|
-
startBootGuardHeartbeat() {
|
|
8162
|
+
startBootGuardHeartbeat(preRead) {
|
|
7807
8163
|
if (!NativeScaleBunOta_default) return;
|
|
7808
8164
|
if (this.healthyTimer) {
|
|
7809
8165
|
clearTimeout(this.healthyTimer);
|
|
7810
8166
|
this.healthyTimer = null;
|
|
7811
8167
|
}
|
|
7812
8168
|
try {
|
|
7813
|
-
const
|
|
7814
|
-
|
|
7815
|
-
if (!state.bootMarkerPresent) {
|
|
8169
|
+
const state = preRead !== void 0 ? preRead : JSON.parse(NativeScaleBunOta_default.getSlotState());
|
|
8170
|
+
if (!state?.bootMarkerPresent) {
|
|
7816
8171
|
return;
|
|
7817
8172
|
}
|
|
7818
8173
|
const healthyMs = this.bootGuardConfig.healthyAfterMs ?? DEFAULT_HEALTHY_AFTER_MS;
|
|
7819
|
-
|
|
8174
|
+
logger.debug(`[OTA] Boot guard active \u2014 marking healthy after ${healthyMs}ms`);
|
|
7820
8175
|
this.healthyTimer = setTimeout(() => {
|
|
7821
8176
|
noThrow(() => {
|
|
7822
8177
|
NativeScaleBunOta_default?.markHealthy();
|
|
7823
|
-
|
|
8178
|
+
logger.info("[OTA] Boot guard cleared \u2014 bundle marked healthy \u2713");
|
|
7824
8179
|
this.healthyTimer = null;
|
|
7825
8180
|
const running = this.currentBundle;
|
|
7826
8181
|
if (running?.id) {
|
|
@@ -7866,11 +8221,11 @@ var init_OtaOrchestrator = __esm({
|
|
|
7866
8221
|
} catch {
|
|
7867
8222
|
}
|
|
7868
8223
|
this.resumeRestartArmed = false;
|
|
7869
|
-
|
|
8224
|
+
logger.info("[OTA] App resumed \u2014 activating the staged update now");
|
|
7870
8225
|
this.restart();
|
|
7871
8226
|
}
|
|
7872
8227
|
});
|
|
7873
|
-
|
|
8228
|
+
logger.debug("[OTA] Update staged \u2014 will activate on next resume (ON_NEXT_RESUME)");
|
|
7874
8229
|
} catch {
|
|
7875
8230
|
this.resumeRestartArmed = false;
|
|
7876
8231
|
}
|
|
@@ -7887,7 +8242,7 @@ var init_OtaOrchestrator = __esm({
|
|
|
7887
8242
|
*/
|
|
7888
8243
|
restart() {
|
|
7889
8244
|
noThrow(() => {
|
|
7890
|
-
|
|
8245
|
+
logger.info("[OTA] Triggering app restart\u2026");
|
|
7891
8246
|
if (NativeScaleBunOta_default && typeof NativeScaleBunOta_default.restartApp === "function") {
|
|
7892
8247
|
NativeScaleBunOta_default.restartApp();
|
|
7893
8248
|
} else if (import_react_native33.DevSettings && typeof import_react_native33.DevSettings.reload === "function") {
|
|
@@ -7906,17 +8261,25 @@ var init_OtaOrchestrator = __esm({
|
|
|
7906
8261
|
var uiState_exports = {};
|
|
7907
8262
|
__export(uiState_exports, {
|
|
7908
8263
|
clearUiState: () => clearUiState,
|
|
8264
|
+
onUiStateChange: () => onUiStateChange,
|
|
8265
|
+
resetUiStateListeners: () => resetUiStateListeners,
|
|
7909
8266
|
setUiState: () => setUiState,
|
|
8267
|
+
uiStateListenerCount: () => uiStateListenerCount,
|
|
7910
8268
|
uiStateSignature: () => uiStateSignature
|
|
7911
8269
|
});
|
|
7912
8270
|
function setUiState(name, value) {
|
|
7913
8271
|
const n = clean(name);
|
|
7914
8272
|
const v = clean(value);
|
|
7915
|
-
if (n
|
|
8273
|
+
if (!n || !v) return;
|
|
8274
|
+
if (declared.get(n) === v) return;
|
|
8275
|
+
declared.set(n, v);
|
|
8276
|
+
notifyChanged();
|
|
7916
8277
|
}
|
|
7917
8278
|
function clearUiState(name) {
|
|
8279
|
+
const before = declared.size;
|
|
7918
8280
|
if (name === void 0) declared.clear();
|
|
7919
8281
|
else declared.delete(clean(name));
|
|
8282
|
+
if (declared.size !== before) notifyChanged();
|
|
7920
8283
|
}
|
|
7921
8284
|
function uiStateSignature() {
|
|
7922
8285
|
if (!declared.size) return void 0;
|
|
@@ -7929,12 +8292,42 @@ function uiStateSignature() {
|
|
|
7929
8292
|
}
|
|
7930
8293
|
return signature || void 0;
|
|
7931
8294
|
}
|
|
7932
|
-
|
|
8295
|
+
function onUiStateChange(listener) {
|
|
8296
|
+
listeners2.add(listener);
|
|
8297
|
+
return () => {
|
|
8298
|
+
listeners2.delete(listener);
|
|
8299
|
+
};
|
|
8300
|
+
}
|
|
8301
|
+
function resetUiStateListeners() {
|
|
8302
|
+
listeners2.clear();
|
|
8303
|
+
if (pending4 !== null) {
|
|
8304
|
+
clearTimeout(pending4);
|
|
8305
|
+
pending4 = null;
|
|
8306
|
+
}
|
|
8307
|
+
}
|
|
8308
|
+
function uiStateListenerCount() {
|
|
8309
|
+
return listeners2.size;
|
|
8310
|
+
}
|
|
8311
|
+
function notifyChanged() {
|
|
8312
|
+
if (pending4 !== null) return;
|
|
8313
|
+
pending4 = setTimeout(() => {
|
|
8314
|
+
pending4 = null;
|
|
8315
|
+
for (const listener of listeners2) {
|
|
8316
|
+
try {
|
|
8317
|
+
listener();
|
|
8318
|
+
} catch {
|
|
8319
|
+
}
|
|
8320
|
+
}
|
|
8321
|
+
}, 0);
|
|
8322
|
+
}
|
|
8323
|
+
var declared, clean, listeners2, pending4;
|
|
7933
8324
|
var init_uiState = __esm({
|
|
7934
8325
|
"lib/module/features/journey/uiState.js"() {
|
|
7935
8326
|
"use strict";
|
|
7936
8327
|
declared = /* @__PURE__ */ new Map();
|
|
7937
8328
|
clean = (s) => typeof s === "string" ? s.replace(/[;:|]/g, "").trim().slice(0, 32) : "";
|
|
8329
|
+
listeners2 = /* @__PURE__ */ new Set();
|
|
8330
|
+
pending4 = null;
|
|
7938
8331
|
}
|
|
7939
8332
|
});
|
|
7940
8333
|
|
|
@@ -9885,6 +10278,22 @@ var SHAPE = {
|
|
|
9885
10278
|
}
|
|
9886
10279
|
},
|
|
9887
10280
|
captureInteractionHeatmap: bool(true),
|
|
10281
|
+
/**
|
|
10282
|
+
* RN-3 zero-config: patch React Native's ScrollView export so EVERY scroll view reports its
|
|
10283
|
+
* offset, not just ones wrapped in ScaleBunScrollView.
|
|
10284
|
+
*
|
|
10285
|
+
* DEFAULT FALSE, unlike captureInteractionHeatmap above, and the difference is deliberate.
|
|
10286
|
+
* That flag changes what the SDK records about the host; this one changes what the HOST
|
|
10287
|
+
* RENDERS — every scroll view in the app, including ones inside third-party libraries the app
|
|
10288
|
+
* did not write, starts routing through an SDK component. The failure mode is not a wrong
|
|
10289
|
+
* number in a dashboard, it is the host's UI behaving differently because an analytics SDK
|
|
10290
|
+
* replaced a primitive. That is a decision an integrator makes deliberately, not one they
|
|
10291
|
+
* discover afterwards.
|
|
10292
|
+
*
|
|
10293
|
+
* With it off, a plain <ScrollView> yields no content depth and the tap says so
|
|
10294
|
+
* (scroll_source: 'none') rather than defaulting its offset to 0.
|
|
10295
|
+
*/
|
|
10296
|
+
autoInstrumentScrollViews: bool(false),
|
|
9888
10297
|
// Opt-in: intercept console.* and ship the lines to the Diagnose → Logs lane in SaaS
|
|
9889
10298
|
// mode. OFF by default — console output can carry PII/secrets and adds ingest volume,
|
|
9890
10299
|
// so capturing it is the host's explicit choice, not a silent default. When true and a
|
|
@@ -11827,8 +12236,8 @@ var PerformanceTransport = class {
|
|
|
11827
12236
|
setSink(sink) {
|
|
11828
12237
|
this.sink = sink;
|
|
11829
12238
|
if (sink && this.buffer.length > 0) {
|
|
11830
|
-
const
|
|
11831
|
-
for (const msg of
|
|
12239
|
+
const pending5 = this.buffer.splice(0);
|
|
12240
|
+
for (const msg of pending5) {
|
|
11832
12241
|
try {
|
|
11833
12242
|
sink.send(msg);
|
|
11834
12243
|
} catch {
|
|
@@ -13543,22 +13952,22 @@ var BackendSessionAdapter = class _BackendSessionAdapter {
|
|
|
13543
13952
|
* lose the snapshot entirely. `imageData.length` is the base64 length, which is within ~1% of the
|
|
13544
13953
|
* serialized cost and needs no extra pass.
|
|
13545
13954
|
*/
|
|
13546
|
-
static _selectFramesToPersist(
|
|
13955
|
+
static _selectFramesToPersist(pending5) {
|
|
13547
13956
|
const isKeyframe = (b) => b.frame.frameType !== "delta";
|
|
13548
13957
|
const size = (b) => b.imageData?.length ?? 0;
|
|
13549
13958
|
const keep = [];
|
|
13550
13959
|
let budget = _BackendSessionAdapter.MAX_PERSIST_BYTES;
|
|
13551
13960
|
const maxCount = _BackendSessionAdapter.MAX_FRAMES_PER_BATCH;
|
|
13552
|
-
for (let i =
|
|
13553
|
-
const b =
|
|
13961
|
+
for (let i = pending5.length - 1; i >= 0; i--) {
|
|
13962
|
+
const b = pending5[i];
|
|
13554
13963
|
if (!isKeyframe(b)) continue;
|
|
13555
13964
|
const cost = size(b);
|
|
13556
13965
|
if (cost > budget || keep.length >= maxCount) continue;
|
|
13557
13966
|
budget -= cost;
|
|
13558
13967
|
keep.push(b);
|
|
13559
13968
|
}
|
|
13560
|
-
for (let i =
|
|
13561
|
-
const b =
|
|
13969
|
+
for (let i = pending5.length - 1; i >= 0; i--) {
|
|
13970
|
+
const b = pending5[i];
|
|
13562
13971
|
if (isKeyframe(b)) continue;
|
|
13563
13972
|
const cost = size(b);
|
|
13564
13973
|
if (cost > budget || keep.length >= maxCount) continue;
|
|
@@ -13991,6 +14400,10 @@ var SDKBootstrapper = class {
|
|
|
13991
14400
|
return null;
|
|
13992
14401
|
}
|
|
13993
14402
|
this.config = result.data;
|
|
14403
|
+
const requestedApiBaseUrl = rawConfig?.apiBaseUrl;
|
|
14404
|
+
if (typeof requestedApiBaseUrl === "string" && requestedApiBaseUrl && requestedApiBaseUrl !== DEFAULT_API_BASE_URL) {
|
|
14405
|
+
logger.warn(`[ScaleBun] Ignoring apiBaseUrl '${requestedApiBaseUrl}' \u2014 the endpoint is locked at build time and this build targets ${DEFAULT_API_BASE_URL}. To target another backend, change DEFAULT_API_BASE_URL in the SDK source and rebuild.`);
|
|
14406
|
+
}
|
|
13994
14407
|
this.config.apiBaseUrl = DEFAULT_API_BASE_URL;
|
|
13995
14408
|
if (this.config.logLevel) {
|
|
13996
14409
|
logger.setLevelByName(this.config.logLevel);
|
|
@@ -14155,6 +14568,20 @@ var SDKBootstrapper = class {
|
|
|
14155
14568
|
}));
|
|
14156
14569
|
}
|
|
14157
14570
|
await this.featureRegistry.initializeAll(featureContext);
|
|
14571
|
+
if (this.config.autoInstrumentScrollViews === true) {
|
|
14572
|
+
try {
|
|
14573
|
+
const {
|
|
14574
|
+
autoInstrumentScrollViews: autoInstrumentScrollViews2
|
|
14575
|
+
} = (init_autoInstrumentScroll(), __toCommonJS(autoInstrumentScroll_exports));
|
|
14576
|
+
const outcome = autoInstrumentScrollViews2();
|
|
14577
|
+
if (!outcome.installed) {
|
|
14578
|
+
logger.warn("[Bootstrap] autoInstrumentScrollViews was requested but could not be installed: " + outcome.reason + " \u2014 taps will carry no content depth.");
|
|
14579
|
+
} else {
|
|
14580
|
+
__DEV__ && logger.debug("[Bootstrap] ScrollView auto-instrumentation installed");
|
|
14581
|
+
}
|
|
14582
|
+
} catch {
|
|
14583
|
+
}
|
|
14584
|
+
}
|
|
14158
14585
|
if (features.replay !== false) {
|
|
14159
14586
|
try {
|
|
14160
14587
|
__DEV__ && logger.debug("[Bootstrap] Initializing unified SessionManager");
|
|
@@ -16797,14 +17224,14 @@ var ScaleBunFacade = class _ScaleBunFacade {
|
|
|
16797
17224
|
this._apiBaseUrl = result.config.apiBaseUrl;
|
|
16798
17225
|
this._maybeStartEventTracker(rawConfig);
|
|
16799
17226
|
if (this._pendingIdentity) {
|
|
16800
|
-
const
|
|
17227
|
+
const pending5 = this._pendingIdentity;
|
|
16801
17228
|
this._pendingIdentity = null;
|
|
16802
|
-
this.identify(
|
|
17229
|
+
this.identify(pending5.userId, pending5.traits);
|
|
16803
17230
|
}
|
|
16804
17231
|
const realQueue = coreContainer.get("PersistentQueue");
|
|
16805
17232
|
const buffered = this._preInitBuffer.popBatch(this._preInitBuffer.length());
|
|
16806
17233
|
if (buffered.length > 0) {
|
|
16807
|
-
|
|
17234
|
+
logger.debug(`[ScaleBun] Draining ${buffered.length} pre-init events into persistent queue`);
|
|
16808
17235
|
for (const envelope of buffered) {
|
|
16809
17236
|
realQueue.add(envelope);
|
|
16810
17237
|
}
|
|
@@ -16895,7 +17322,9 @@ var ScaleBunFacade = class _ScaleBunFacade {
|
|
|
16895
17322
|
}
|
|
16896
17323
|
const clientKey = this._clientKey;
|
|
16897
17324
|
const apiUrl = this._apiBaseUrl;
|
|
16898
|
-
if (!clientKey || !apiUrl)
|
|
17325
|
+
if (!clientKey || !apiUrl) {
|
|
17326
|
+
return;
|
|
17327
|
+
}
|
|
16899
17328
|
this._otaCheckInFlight = true;
|
|
16900
17329
|
try {
|
|
16901
17330
|
const info = await bridgeAdapter.getDeviceInfo();
|
|
@@ -16946,7 +17375,7 @@ var ScaleBunFacade = class _ScaleBunFacade {
|
|
|
16946
17375
|
if (!dbgConfig) return;
|
|
16947
17376
|
dbgConfig.__skipDeprecationWarning = true;
|
|
16948
17377
|
this.enableDebug(dbgConfig);
|
|
16949
|
-
|
|
17378
|
+
logger.info("[ScaleBun] Debug auto-connected via init({ debug }).");
|
|
16950
17379
|
} catch (err) {
|
|
16951
17380
|
logger.warn("[ScaleBun] Auto debug connection failed:", err?.message);
|
|
16952
17381
|
}
|
|
@@ -16960,7 +17389,7 @@ var ScaleBunFacade = class _ScaleBunFacade {
|
|
|
16960
17389
|
const apiBaseUrl = this._apiBaseUrl;
|
|
16961
17390
|
if (!enabled || !appId || !clientKey || !apiBaseUrl) {
|
|
16962
17391
|
if (enabled && !appId) {
|
|
16963
|
-
|
|
17392
|
+
logger.info("[ScaleBun] Envelope event tracking disabled \u2014 no appId in init config.");
|
|
16964
17393
|
}
|
|
16965
17394
|
return;
|
|
16966
17395
|
}
|
|
@@ -17010,7 +17439,7 @@ var ScaleBunFacade = class _ScaleBunFacade {
|
|
|
17010
17439
|
onEvent: (eventName, revenue) => this._skanManager?.onEvent(eventName, revenue)
|
|
17011
17440
|
});
|
|
17012
17441
|
this._eventTracker.start();
|
|
17013
|
-
|
|
17442
|
+
logger.info("[ScaleBun] Envelope event tracking started \u2192 /v1/batch.");
|
|
17014
17443
|
this._applyNativeDeviceContext(this._eventTracker);
|
|
17015
17444
|
this._captureInstallReferrer(this._eventTracker);
|
|
17016
17445
|
const tracker = this._eventTracker;
|
|
@@ -17644,7 +18073,7 @@ var ScaleBunFacade = class _ScaleBunFacade {
|
|
|
17644
18073
|
const cfg = await (this._getEngageTransport()?.fetchEngageConfig(context) ?? Promise.resolve(null));
|
|
17645
18074
|
const messages = cfg?.inAppMessages ?? [];
|
|
17646
18075
|
const previews = messages.filter((m) => m.preview === true || m.isTest === true);
|
|
17647
|
-
|
|
18076
|
+
logger.info(`[Engage] debugFetchInAppMessages \u2192 ${messages.length} message(s), ${previews.length} test preview(s)`);
|
|
17648
18077
|
return messages;
|
|
17649
18078
|
},
|
|
17650
18079
|
/** Upload a survey/NPS/feedback response (idempotent, rides the outbox). */
|
|
@@ -17920,7 +18349,7 @@ var ScaleBunFacade = class _ScaleBunFacade {
|
|
|
17920
18349
|
logger.warn("SDK not initialized. Event dropped:", name);
|
|
17921
18350
|
return;
|
|
17922
18351
|
}
|
|
17923
|
-
|
|
18352
|
+
logger.debug("Tracking event:", name);
|
|
17924
18353
|
engageSignals.emit(name, payload);
|
|
17925
18354
|
const adapter = SessionManager.getExistingInstance()?.getBackendTransport();
|
|
17926
18355
|
if (!isUploadedSessionEvent(name)) {
|
|
@@ -18049,7 +18478,7 @@ var ScaleBunFacade = class _ScaleBunFacade {
|
|
|
18049
18478
|
await Promise.all([this._eventTracker?.flush() ?? Promise.resolve(), adapter?.flushAnalytics() ?? Promise.resolve()]);
|
|
18050
18479
|
return;
|
|
18051
18480
|
}
|
|
18052
|
-
|
|
18481
|
+
logger.debug("Flushing events from persistent queue...");
|
|
18053
18482
|
const httpClient = coreContainer.get("HttpClient");
|
|
18054
18483
|
await flushQueue(this._getQueue(), httpClient, getDeviceId());
|
|
18055
18484
|
});
|
|
@@ -18071,9 +18500,9 @@ var ScaleBunFacade = class _ScaleBunFacade {
|
|
|
18071
18500
|
const bootstrapPerfFeature = getPerformanceFeature();
|
|
18072
18501
|
if (bootstrapPerfFeature) {
|
|
18073
18502
|
this._performanceFeature = bootstrapPerfFeature;
|
|
18074
|
-
|
|
18503
|
+
logger.debug("Performance feature linked from bootstrap");
|
|
18075
18504
|
}
|
|
18076
|
-
|
|
18505
|
+
logger.info("Debug enabled");
|
|
18077
18506
|
});
|
|
18078
18507
|
}
|
|
18079
18508
|
/** Disable the debug subsystem and clean up */
|
|
@@ -18216,14 +18645,14 @@ var ScaleBunFacade = class _ScaleBunFacade {
|
|
|
18216
18645
|
return false;
|
|
18217
18646
|
}
|
|
18218
18647
|
sm.startSession(metadata);
|
|
18219
|
-
|
|
18648
|
+
logger.info("session.start() \u2705 sessionId:", sm.currentSessionId);
|
|
18220
18649
|
return true;
|
|
18221
18650
|
},
|
|
18222
18651
|
end: (reason) => {
|
|
18223
18652
|
const sm = SessionManager.getExistingInstance();
|
|
18224
18653
|
if (!sm || !sm.isActive) return false;
|
|
18225
18654
|
sm.endSession(reason);
|
|
18226
|
-
|
|
18655
|
+
logger.info("session.end() \u2705");
|
|
18227
18656
|
return true;
|
|
18228
18657
|
},
|
|
18229
18658
|
emitEvent: (type, data) => {
|
|
@@ -18276,7 +18705,7 @@ var ScaleBunFacade = class _ScaleBunFacade {
|
|
|
18276
18705
|
...this._privacyConfig,
|
|
18277
18706
|
...config
|
|
18278
18707
|
};
|
|
18279
|
-
|
|
18708
|
+
logger.debug("[report] Privacy config updated");
|
|
18280
18709
|
},
|
|
18281
18710
|
/** Set branding for exported reports. */
|
|
18282
18711
|
setBranding: (config) => {
|
|
@@ -18284,7 +18713,7 @@ var ScaleBunFacade = class _ScaleBunFacade {
|
|
|
18284
18713
|
...this._brandingConfig,
|
|
18285
18714
|
...config
|
|
18286
18715
|
};
|
|
18287
|
-
|
|
18716
|
+
logger.debug("[report] Branding config updated");
|
|
18288
18717
|
},
|
|
18289
18718
|
/** Get current privacy config. */
|
|
18290
18719
|
getPrivacy: () => ({
|
|
@@ -18331,9 +18760,9 @@ var ScaleBunFacade = class _ScaleBunFacade {
|
|
|
18331
18760
|
AutoScreenDetector: AutoScreenDetector2
|
|
18332
18761
|
} = (init_AutoScreenDetector(), __toCommonJS(AutoScreenDetector_exports));
|
|
18333
18762
|
AutoScreenDetector2.getInstance().setNavigationRef(ref);
|
|
18334
|
-
|
|
18763
|
+
logger.info("[ScaleBun] Navigation ref set \u2192 auto screen detection active");
|
|
18335
18764
|
} catch (err) {
|
|
18336
|
-
|
|
18765
|
+
logger.debug("[ScaleBun] Navigation ref stashed (detector not ready):", err?.message);
|
|
18337
18766
|
}
|
|
18338
18767
|
});
|
|
18339
18768
|
}
|
|
@@ -18640,8 +19069,8 @@ function installEngageTriggerEngine(deps) {
|
|
|
18640
19069
|
if (!cachedConfig) return;
|
|
18641
19070
|
configReady = true;
|
|
18642
19071
|
const now2 = Date.now();
|
|
18643
|
-
const
|
|
18644
|
-
for (const e of
|
|
19072
|
+
const pending5 = recent.splice(0);
|
|
19073
|
+
for (const e of pending5) {
|
|
18645
19074
|
const age = now2 - e.at;
|
|
18646
19075
|
if (age > RECENT_TTL_MS) continue;
|
|
18647
19076
|
const remaining = delayMsForTrigger(cachedConfig, e.options.trigger) - age;
|
|
@@ -19906,11 +20335,11 @@ var keyOf = (value) => value.trim();
|
|
|
19906
20335
|
var validRect = (rect) => [rect.x, rect.y, rect.width, rect.height].every(Number.isFinite) && rect.width > 0 && rect.height > 0;
|
|
19907
20336
|
function createEngageAnchorRegistry() {
|
|
19908
20337
|
const entries = /* @__PURE__ */ new Map();
|
|
19909
|
-
const
|
|
20338
|
+
const listeners3 = /* @__PURE__ */ new Map();
|
|
19910
20339
|
const notify = (rawKey) => {
|
|
19911
20340
|
const key = keyOf(rawKey);
|
|
19912
20341
|
if (!key) return;
|
|
19913
|
-
|
|
20342
|
+
listeners3.get(key)?.forEach((listener) => listener());
|
|
19914
20343
|
};
|
|
19915
20344
|
return {
|
|
19916
20345
|
register(rawKey, node) {
|
|
@@ -19952,12 +20381,12 @@ function createEngageAnchorRegistry() {
|
|
|
19952
20381
|
const key = keyOf(rawKey);
|
|
19953
20382
|
if (!key) return () => {
|
|
19954
20383
|
};
|
|
19955
|
-
const set =
|
|
20384
|
+
const set = listeners3.get(key) ?? /* @__PURE__ */ new Set();
|
|
19956
20385
|
set.add(listener);
|
|
19957
|
-
|
|
20386
|
+
listeners3.set(key, set);
|
|
19958
20387
|
return () => {
|
|
19959
20388
|
set.delete(listener);
|
|
19960
|
-
if (set.size === 0)
|
|
20389
|
+
if (set.size === 0) listeners3.delete(key);
|
|
19961
20390
|
};
|
|
19962
20391
|
},
|
|
19963
20392
|
notify
|
|
@@ -29066,10 +29495,10 @@ var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
|
29066
29495
|
var keyOf2 = (value) => value.trim();
|
|
29067
29496
|
function createEngageInlinePlacementRegistry() {
|
|
29068
29497
|
const slots = /* @__PURE__ */ new Map();
|
|
29069
|
-
const
|
|
29070
|
-
const
|
|
29498
|
+
const pending5 = /* @__PURE__ */ new Map();
|
|
29499
|
+
const listeners3 = /* @__PURE__ */ new Map();
|
|
29071
29500
|
const notify = (key) => {
|
|
29072
|
-
|
|
29501
|
+
listeners3.get(key)?.forEach((listener) => listener());
|
|
29073
29502
|
};
|
|
29074
29503
|
return {
|
|
29075
29504
|
register(rawKey, sink) {
|
|
@@ -29086,7 +29515,7 @@ function createEngageInlinePlacementRegistry() {
|
|
|
29086
29515
|
sink,
|
|
29087
29516
|
token
|
|
29088
29517
|
});
|
|
29089
|
-
sink(
|
|
29518
|
+
sink(pending5.get(key) ?? null);
|
|
29090
29519
|
if (!previous) notify(key);
|
|
29091
29520
|
return () => {
|
|
29092
29521
|
if (slots.get(key)?.token !== token) return;
|
|
@@ -29097,7 +29526,7 @@ function createEngageInlinePlacementRegistry() {
|
|
|
29097
29526
|
deliver(rawKey, delivery) {
|
|
29098
29527
|
const key = keyOf2(rawKey);
|
|
29099
29528
|
if (!key) return false;
|
|
29100
|
-
|
|
29529
|
+
pending5.set(key, delivery);
|
|
29101
29530
|
const slot = slots.get(key);
|
|
29102
29531
|
slot?.sink(delivery);
|
|
29103
29532
|
return slot !== void 0;
|
|
@@ -29105,9 +29534,9 @@ function createEngageInlinePlacementRegistry() {
|
|
|
29105
29534
|
clear(rawKey, messageId) {
|
|
29106
29535
|
const key = keyOf2(rawKey);
|
|
29107
29536
|
if (!key) return;
|
|
29108
|
-
const current =
|
|
29537
|
+
const current = pending5.get(key);
|
|
29109
29538
|
if (!current || messageId !== void 0 && current.message?.id !== messageId) return;
|
|
29110
|
-
|
|
29539
|
+
pending5.delete(key);
|
|
29111
29540
|
slots.get(key)?.sink(null);
|
|
29112
29541
|
},
|
|
29113
29542
|
has(rawKey) {
|
|
@@ -29118,12 +29547,12 @@ function createEngageInlinePlacementRegistry() {
|
|
|
29118
29547
|
const key = keyOf2(rawKey);
|
|
29119
29548
|
if (!key) return () => {
|
|
29120
29549
|
};
|
|
29121
|
-
const set =
|
|
29550
|
+
const set = listeners3.get(key) ?? /* @__PURE__ */ new Set();
|
|
29122
29551
|
set.add(listener);
|
|
29123
|
-
|
|
29552
|
+
listeners3.set(key, set);
|
|
29124
29553
|
return () => {
|
|
29125
29554
|
set.delete(listener);
|
|
29126
|
-
if (set.size === 0)
|
|
29555
|
+
if (set.size === 0) listeners3.delete(key);
|
|
29127
29556
|
};
|
|
29128
29557
|
}
|
|
29129
29558
|
};
|
|
@@ -29912,6 +30341,7 @@ var import_react_native42 = require("react-native");
|
|
|
29912
30341
|
// lib/module/features/journey/touchTarget.js
|
|
29913
30342
|
var MAX_DEPTH = 12;
|
|
29914
30343
|
var MAX_LEN = 60;
|
|
30344
|
+
var MAX_PATH_SEGMENTS = 4;
|
|
29915
30345
|
function str2(v) {
|
|
29916
30346
|
if (typeof v !== "string") return void 0;
|
|
29917
30347
|
const s = v.trim();
|
|
@@ -29943,41 +30373,136 @@ function fiberOf(event) {
|
|
|
29943
30373
|
const e = event;
|
|
29944
30374
|
const direct = e._targetInst;
|
|
29945
30375
|
if (direct && typeof direct === "object") return direct;
|
|
29946
|
-
const target = e.target;
|
|
29947
|
-
if (target
|
|
29948
|
-
const handle = target.
|
|
30376
|
+
const target = e.target ?? e.nativeEvent?.target;
|
|
30377
|
+
if (target) {
|
|
30378
|
+
const handle = target._internalInstanceHandle ?? target._targetInst;
|
|
29949
30379
|
if (handle && typeof handle === "object") return handle;
|
|
29950
30380
|
}
|
|
29951
30381
|
return null;
|
|
29952
30382
|
}
|
|
29953
|
-
function resolveTouchTarget(event,
|
|
30383
|
+
function resolveTouchTarget(event, optsOrMaxDepth = {}) {
|
|
30384
|
+
const opts = typeof optsOrMaxDepth === "number" ? {
|
|
30385
|
+
maxDepth: optsOrMaxDepth
|
|
30386
|
+
} : optsOrMaxDepth;
|
|
30387
|
+
const maxDepth = opts.maxDepth ?? MAX_DEPTH;
|
|
29954
30388
|
let f = fiberOf(event);
|
|
29955
30389
|
if (!f) return null;
|
|
29956
30390
|
const out = {};
|
|
30391
|
+
const segments = [];
|
|
30392
|
+
const ancestors = [];
|
|
29957
30393
|
let depth = 0;
|
|
29958
30394
|
try {
|
|
29959
30395
|
while (f && depth < maxDepth) {
|
|
29960
30396
|
const props = f.memoizedProps;
|
|
29961
30397
|
if (props) {
|
|
29962
|
-
|
|
30398
|
+
const beforeTestId = out.testID;
|
|
29963
30399
|
out.testID ??= str2(props.testID);
|
|
29964
|
-
out.
|
|
30400
|
+
if (!beforeTestId && out.testID) out.node = f.stateNode;
|
|
30401
|
+
if (typeof props.cellKey === "string" && props.cellKey) {
|
|
30402
|
+
out.itemKey ??= str2(props.cellKey);
|
|
30403
|
+
if (typeof props.index === "number" && Number.isFinite(props.index)) {
|
|
30404
|
+
out.itemIndex ??= props.index;
|
|
30405
|
+
}
|
|
30406
|
+
}
|
|
30407
|
+
if (opts.includeText) {
|
|
30408
|
+
out.accessibilityLabel ??= str2(props.accessibilityLabel);
|
|
30409
|
+
out.text ??= textChild(props);
|
|
30410
|
+
}
|
|
30411
|
+
}
|
|
30412
|
+
if (f.stateNode) ancestors.push(f.stateNode);
|
|
30413
|
+
const name = componentName(f);
|
|
30414
|
+
if (name) {
|
|
30415
|
+
out.component ??= name;
|
|
30416
|
+
if (segments[segments.length - 1] !== name && segments.length < MAX_PATH_SEGMENTS) {
|
|
30417
|
+
segments.push(name);
|
|
30418
|
+
}
|
|
29965
30419
|
}
|
|
29966
|
-
out.
|
|
29967
|
-
if (out.accessibilityLabel && out.testID) break;
|
|
30420
|
+
if (out.testID && segments.length >= MAX_PATH_SEGMENTS) break;
|
|
29968
30421
|
f = f.return ?? null;
|
|
29969
30422
|
depth++;
|
|
29970
30423
|
}
|
|
29971
30424
|
} catch {
|
|
29972
30425
|
}
|
|
29973
|
-
|
|
30426
|
+
if (segments.length) out.path = segments.join("<");
|
|
30427
|
+
if (!out.node) out.node = fiberOf(event)?.stateNode;
|
|
30428
|
+
if (ancestors.length) out.ancestors = ancestors;
|
|
30429
|
+
return out.testID || out.path || out.component || out.accessibilityLabel || out.text || out.itemKey ? out : null;
|
|
29974
30430
|
}
|
|
29975
30431
|
function describeTouchTarget(t) {
|
|
29976
30432
|
if (!t) return void 0;
|
|
29977
|
-
return t.
|
|
30433
|
+
return t.testID || t.path || t.component;
|
|
30434
|
+
}
|
|
30435
|
+
function targetIdentity(t) {
|
|
30436
|
+
if (!t) return {
|
|
30437
|
+
source: "none"
|
|
30438
|
+
};
|
|
30439
|
+
if (t.testID) return {
|
|
30440
|
+
id: t.testID,
|
|
30441
|
+
source: "testID"
|
|
30442
|
+
};
|
|
30443
|
+
if (t.path) return {
|
|
30444
|
+
id: t.path,
|
|
30445
|
+
source: "path"
|
|
30446
|
+
};
|
|
30447
|
+
if (t.component) return {
|
|
30448
|
+
id: t.component,
|
|
30449
|
+
source: "component"
|
|
30450
|
+
};
|
|
30451
|
+
return {
|
|
30452
|
+
source: "none"
|
|
30453
|
+
};
|
|
30454
|
+
}
|
|
30455
|
+
|
|
30456
|
+
// lib/module/features/journey/targetGeometry.js
|
|
30457
|
+
function readRectSync(node) {
|
|
30458
|
+
if (!node || typeof node !== "object") return null;
|
|
30459
|
+
const n = node;
|
|
30460
|
+
const fn = typeof n.unstable_getBoundingClientRect === "function" ? n.unstable_getBoundingClientRect : typeof n.getBoundingClientRect === "function" ? n.getBoundingClientRect : null;
|
|
30461
|
+
if (!fn) return null;
|
|
30462
|
+
try {
|
|
30463
|
+
const r = fn.call(n);
|
|
30464
|
+
if (!r) return null;
|
|
30465
|
+
const {
|
|
30466
|
+
x,
|
|
30467
|
+
y,
|
|
30468
|
+
width,
|
|
30469
|
+
height
|
|
30470
|
+
} = r;
|
|
30471
|
+
if (![x, y, width, height].every((v) => typeof v === "number" && Number.isFinite(v))) return null;
|
|
30472
|
+
if (width <= 0 || height <= 0) return null;
|
|
30473
|
+
return {
|
|
30474
|
+
x,
|
|
30475
|
+
y,
|
|
30476
|
+
width,
|
|
30477
|
+
height
|
|
30478
|
+
};
|
|
30479
|
+
} catch {
|
|
30480
|
+
return null;
|
|
30481
|
+
}
|
|
30482
|
+
}
|
|
30483
|
+
function normalizeRect(rect, base) {
|
|
30484
|
+
if (!rect || !base || base.w <= 0 || base.h <= 0) return null;
|
|
30485
|
+
const out = {
|
|
30486
|
+
x: rect.x / base.w,
|
|
30487
|
+
y: rect.y / base.h,
|
|
30488
|
+
width: rect.width / base.w,
|
|
30489
|
+
height: rect.height / base.h
|
|
30490
|
+
};
|
|
30491
|
+
if (![out.x, out.y, out.width, out.height].every((v) => Number.isFinite(v))) return null;
|
|
30492
|
+
return out;
|
|
30493
|
+
}
|
|
30494
|
+
function localPoint(rect, nx, ny) {
|
|
30495
|
+
if (!rect || rect.width <= 0 || rect.height <= 0) return null;
|
|
30496
|
+
if (typeof nx !== "number" || typeof ny !== "number") return null;
|
|
30497
|
+
if (!Number.isFinite(nx) || !Number.isFinite(ny)) return null;
|
|
30498
|
+
return {
|
|
30499
|
+
u: (nx - rect.x) / rect.width,
|
|
30500
|
+
v: (ny - rect.y) / rect.height
|
|
30501
|
+
};
|
|
29978
30502
|
}
|
|
29979
30503
|
|
|
29980
30504
|
// lib/module/features/journey/ScaleBunDebugRoot.js
|
|
30505
|
+
init_scrollContext();
|
|
29981
30506
|
init_device();
|
|
29982
30507
|
init_interactionProtocol();
|
|
29983
30508
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
@@ -29989,6 +30514,7 @@ function getBootstrap() {
|
|
|
29989
30514
|
return _bootstrap;
|
|
29990
30515
|
}
|
|
29991
30516
|
var JourneyContext = /* @__PURE__ */ (0, import_react9.createContext)(null);
|
|
30517
|
+
var UI_STATE_CAPTURE_MIN_INTERVAL_MS = 1e3;
|
|
29992
30518
|
function ScaleBunDebugRoot({
|
|
29993
30519
|
children,
|
|
29994
30520
|
navigationRef,
|
|
@@ -30167,6 +30693,28 @@ function ScaleBunDebugRoot({
|
|
|
30167
30693
|
clearTimeout(timer);
|
|
30168
30694
|
};
|
|
30169
30695
|
}, [navigationRef]);
|
|
30696
|
+
(0, import_react9.useEffect)(() => {
|
|
30697
|
+
let lastCaptureTs = 0;
|
|
30698
|
+
const {
|
|
30699
|
+
onUiStateChange: onUiStateChange2
|
|
30700
|
+
} = (init_uiState(), __toCommonJS(uiState_exports));
|
|
30701
|
+
const unsubscribe = onUiStateChange2(() => {
|
|
30702
|
+
const now2 = Date.now();
|
|
30703
|
+
if (now2 - lastCaptureTs < UI_STATE_CAPTURE_MIN_INTERVAL_MS) return;
|
|
30704
|
+
lastCaptureTs = now2;
|
|
30705
|
+
try {
|
|
30706
|
+
const {
|
|
30707
|
+
SessionManager: SessionManager2
|
|
30708
|
+
} = (init_SessionManager(), __toCommonJS(SessionManager_exports));
|
|
30709
|
+
const sm = SessionManager2.getExistingInstance();
|
|
30710
|
+
if (sm && typeof sm.captureManualFrame === "function") {
|
|
30711
|
+
void sm.captureManualFrame();
|
|
30712
|
+
}
|
|
30713
|
+
} catch {
|
|
30714
|
+
}
|
|
30715
|
+
});
|
|
30716
|
+
return unsubscribe;
|
|
30717
|
+
}, []);
|
|
30170
30718
|
const lastNativeTouchTsRef = (0, import_react9.useRef)(0);
|
|
30171
30719
|
const nativeTrackingConfirmedRef = (0, import_react9.useRef)(false);
|
|
30172
30720
|
const touchStartRef = (0, import_react9.useRef)(null);
|
|
@@ -30183,10 +30731,15 @@ function ScaleBunDebugRoot({
|
|
|
30183
30731
|
lastNativeTouchTsRef.current = Date.now();
|
|
30184
30732
|
nativeTrackingConfirmedRef.current = true;
|
|
30185
30733
|
const nativeOccurredAt = typeof event.occurredAt === "number" ? event.occurredAt : (typeof event.timestamp === "number" ? event.timestamp : Date.now()) - (typeof event.durationMs === "number" ? event.durationMs : 0);
|
|
30186
|
-
|
|
30734
|
+
pruneInteractionStarts(interactionStartsRef.current, nativeOccurredAt);
|
|
30735
|
+
const correlation = correlateInteraction(interactionStartsRef.current, nativeOccurredAt);
|
|
30736
|
+
const start = correlation.start;
|
|
30737
|
+
if (correlation.index >= 0) {
|
|
30738
|
+
interactionStartsRef.current.splice(correlation.index, 1);
|
|
30739
|
+
}
|
|
30187
30740
|
if (start) {
|
|
30188
|
-
const
|
|
30189
|
-
if (
|
|
30741
|
+
const pending5 = pendingJsEmitsRef.current.get(start.interactionId);
|
|
30742
|
+
if (pending5 !== void 0) clearTimeout(pending5);
|
|
30190
30743
|
pendingJsEmitsRef.current.delete(start.interactionId);
|
|
30191
30744
|
}
|
|
30192
30745
|
const {
|
|
@@ -30209,11 +30762,62 @@ function ScaleBunDebugRoot({
|
|
|
30209
30762
|
interactionId: event.interactionId || start?.interactionId || generateInteractionId(),
|
|
30210
30763
|
interactionProtocol: event.interactionProtocol || INTERACTION_PROTOCOL_VERSION,
|
|
30211
30764
|
occurredAt: nativeOccurredAt,
|
|
30765
|
+
/**
|
|
30766
|
+
* RN-6 — THESE FIVE FIELDS ARE THE ONLY ONES THE JOIN CARRIES, AND
|
|
30767
|
+
* THEREFORE THE ONLY ONES A BAD JOIN CAN FALSIFY.
|
|
30768
|
+
*
|
|
30769
|
+
* Coordinates below come from native and are right regardless. So a
|
|
30770
|
+
* mispairing does not produce visibly broken data: it produces heat
|
|
30771
|
+
* in the correct position attributed to the wrong control, in the
|
|
30772
|
+
* wrong UI state, on the wrong screen. Every field is individually
|
|
30773
|
+
* well-formed and nothing downstream can tell.
|
|
30774
|
+
*
|
|
30775
|
+
* When the correlation is not 'exact' they are left UNDEFINED rather
|
|
30776
|
+
* than filled from a best guess. An interaction with no target is
|
|
30777
|
+
* honest and visibly incomplete — the accounting lane can count it as
|
|
30778
|
+
* unplaceable and say why. An interaction with the WRONG target is
|
|
30779
|
+
* indistinguishable from a correct one and silently corrupts every
|
|
30780
|
+
* aggregate it enters. `null` is not `0`, and a guess is not a
|
|
30781
|
+
* measurement.
|
|
30782
|
+
*/
|
|
30212
30783
|
ui: start?.ui,
|
|
30213
30784
|
stateStatus: start?.stateStatus ?? "not_captured",
|
|
30214
30785
|
target: start?.target,
|
|
30215
30786
|
targetId: start?.targetId,
|
|
30787
|
+
targetSource: start?.targetSource,
|
|
30216
30788
|
screenName: start?.screenName,
|
|
30789
|
+
screenSource: start?.screenSource ?? "none",
|
|
30790
|
+
targetRect: start?.targetRect,
|
|
30791
|
+
/**
|
|
30792
|
+
* RN-15 — WHERE IN THE CONTROL, computed where both halves exist: the
|
|
30793
|
+
* point comes from the native event and the rect was measured at
|
|
30794
|
+
* finger-down, in the same capture base. Pairing them here rather than
|
|
30795
|
+
* downstream stops anything later reaching for a different rect.
|
|
30796
|
+
*/
|
|
30797
|
+
...(() => {
|
|
30798
|
+
const lp = localPoint(start?.targetRect ?? null, event.normalizedX, event.normalizedY);
|
|
30799
|
+
return lp ? {
|
|
30800
|
+
localU: lp.u,
|
|
30801
|
+
localV: lp.v
|
|
30802
|
+
} : {};
|
|
30803
|
+
})(),
|
|
30804
|
+
scrollX: start?.scrollX,
|
|
30805
|
+
scrollY: start?.scrollY,
|
|
30806
|
+
scrollSource: start?.scrollSource ?? "none",
|
|
30807
|
+
itemKey: start?.itemKey,
|
|
30808
|
+
itemIndex: start?.itemIndex,
|
|
30809
|
+
/**
|
|
30810
|
+
* The join's own verdict, shipped with the row so it is auditable
|
|
30811
|
+
* rather than a claim made in a comment. 'ambiguous' and 'none' are
|
|
30812
|
+
* deliberately distinguished: 'none' means the JS handler never fired
|
|
30813
|
+
* (a real capture gap on this platform), 'ambiguous' means it fired
|
|
30814
|
+
* more than once too close together to attribute. Those have
|
|
30815
|
+
* different fixes, and collapsing them would hide which one is
|
|
30816
|
+
* actually happening in production.
|
|
30817
|
+
*/
|
|
30818
|
+
correlation: correlation.outcome,
|
|
30819
|
+
correlationDeltaMs: correlation.deltaMs,
|
|
30820
|
+
correlationRunnerUpDeltaMs: correlation.runnerUpDeltaMs,
|
|
30217
30821
|
emitAutomaticAnalytics: start?.emitAutomaticAnalytics ?? captureAutomaticInteractions,
|
|
30218
30822
|
x: event.rawX,
|
|
30219
30823
|
y: event.rawY,
|
|
@@ -30285,6 +30889,7 @@ function ScaleBunDebugRoot({
|
|
|
30285
30889
|
const target = resolveTouchTarget(e);
|
|
30286
30890
|
let ui;
|
|
30287
30891
|
let screenName;
|
|
30892
|
+
let screenSource = "none";
|
|
30288
30893
|
try {
|
|
30289
30894
|
const {
|
|
30290
30895
|
uiStateSignature: uiStateSignature2
|
|
@@ -30296,7 +30901,9 @@ function ScaleBunDebugRoot({
|
|
|
30296
30901
|
const {
|
|
30297
30902
|
AutoScreenDetector: AutoScreenDetector2
|
|
30298
30903
|
} = (init_AutoScreenDetector(), __toCommonJS(AutoScreenDetector_exports));
|
|
30299
|
-
|
|
30904
|
+
const detector = AutoScreenDetector2.getInstance();
|
|
30905
|
+
screenName = detector.getCurrentScreen() || void 0;
|
|
30906
|
+
screenSource = screenName ? detector.getCurrentScreenSource() : "none";
|
|
30300
30907
|
} catch {
|
|
30301
30908
|
}
|
|
30302
30909
|
const start = {
|
|
@@ -30306,9 +30913,91 @@ function ScaleBunDebugRoot({
|
|
|
30306
30913
|
y: touch.pageY,
|
|
30307
30914
|
locationX: touch.locationX,
|
|
30308
30915
|
locationY: touch.locationY,
|
|
30916
|
+
/**
|
|
30917
|
+
* RN-1 + RN-2 — the identity that is persisted, and it is now code-derived.
|
|
30918
|
+
*
|
|
30919
|
+
* `describeTouchTarget` returns `testID` -> structural path -> component name,
|
|
30920
|
+
* and never a caption or an accessibilityLabel. Before RN-2 it preferred free
|
|
30921
|
+
* text, so this line was writing user data ("Pay EGP 1,442", an order row
|
|
30922
|
+
* labelled with an email address, a contact's name) into the analytics store —
|
|
30923
|
+
* and, because `target` is an aggregation key, splitting one button into one
|
|
30924
|
+
* bucket per person at the same time.
|
|
30925
|
+
*
|
|
30926
|
+
* `targetId` used to be `target?.testID` alone, which is `undefined` in the
|
|
30927
|
+
* overwhelming majority of apps — no testIDs authored — so the field the
|
|
30928
|
+
* backend keys on was empty almost always. It now falls back to the structural
|
|
30929
|
+
* path, with `targetSource` recording WHICH it got.
|
|
30930
|
+
*/
|
|
30309
30931
|
target: describeTouchTarget(target),
|
|
30310
|
-
targetId: target
|
|
30932
|
+
targetId: targetIdentity(target).id,
|
|
30933
|
+
/**
|
|
30934
|
+
* WHICH KIND of identity this is, because they are not equally trustworthy: a
|
|
30935
|
+
* `testID` is a promise from the app's authors that this control is this
|
|
30936
|
+
* control; a `path` is an inference that breaks when the tree is refactored.
|
|
30937
|
+
* Without this the accounting lane cannot distinguish "the control had no
|
|
30938
|
+
* identity" from "the identity is a structural guess", and both would render
|
|
30939
|
+
* as an equally confident target.
|
|
30940
|
+
*/
|
|
30941
|
+
targetSource: targetIdentity(target).source,
|
|
30942
|
+
/* RN-4 — row identity from the app's own keyExtractor; see touchTarget.ts. */
|
|
30943
|
+
itemKey: target?.itemKey,
|
|
30944
|
+
itemIndex: target?.itemIndex,
|
|
30945
|
+
/**
|
|
30946
|
+
* RN-13 — the control's rectangle, so `heat ∈ target` is decidable.
|
|
30947
|
+
*
|
|
30948
|
+
* Read SYNCHRONOUSLY here, in the same tick as the touch, and normalized against
|
|
30949
|
+
* the SAME measured root the tap's own coordinates use. Both halves matter:
|
|
30950
|
+
*
|
|
30951
|
+
* - A callback measurement (`measureInWindow`) would resolve after this handler
|
|
30952
|
+
* returns, so the rect attached to a tap could be the previous tap's, with
|
|
30953
|
+
* nothing afterwards able to tell. `targetGeometry` refuses to guess: it reads
|
|
30954
|
+
* now or reports null.
|
|
30955
|
+
* - Normalizing against a window size instead of the root view is off by the
|
|
30956
|
+
* status bar on most devices, which would read as "the heat is just outside
|
|
30957
|
+
* the button" everywhere rather than as a measurement bug.
|
|
30958
|
+
*
|
|
30959
|
+
* `measureRoot()` is called first because the cached root rect is populated
|
|
30960
|
+
* asynchronously at mount; without it the first taps of a session normalize
|
|
30961
|
+
* against nothing and get a null rect for a reason that has nothing to do with
|
|
30962
|
+
* the control.
|
|
30963
|
+
*/
|
|
30964
|
+
/**
|
|
30965
|
+
* RN-3 — WHERE IN THE CONTENT, not just where on the glass.
|
|
30966
|
+
*
|
|
30967
|
+
* Two taps at the same screen coordinate one viewport apart are two different
|
|
30968
|
+
* controls. Without an offset they merge into one hotspot, and the heatmap shows
|
|
30969
|
+
* heavy engagement at a position where, in content terms, nothing in particular
|
|
30970
|
+
* is. The offset is also a CACHE dimension — two scroll states of one route must
|
|
30971
|
+
* not share a backdrop — so its absence corrupts the picture as well as the count.
|
|
30972
|
+
*
|
|
30973
|
+
* `ancestors` is innermost-first, so this takes the NEAREST scroller that has a
|
|
30974
|
+
* recorded position: a tap inside a horizontal carousel belongs to the carousel's
|
|
30975
|
+
* content, not to how far the page behind it had scrolled.
|
|
30976
|
+
*
|
|
30977
|
+
* Null when no ancestor has one — never zero. Zero is a real scroll position (the
|
|
30978
|
+
* top of a list), and reporting it for "unknown" would place every unmeasured tap
|
|
30979
|
+
* at the top of its content, indistinguishable from a genuine tap there.
|
|
30980
|
+
*/
|
|
30981
|
+
...(() => {
|
|
30982
|
+
const hit = nearestRecordedOffset((target?.ancestors ?? []).map((n) => nativeTagOf(n)));
|
|
30983
|
+
return hit ? {
|
|
30984
|
+
scrollX: hit.x,
|
|
30985
|
+
scrollY: hit.y,
|
|
30986
|
+
scrollSource: "wrapper"
|
|
30987
|
+
} : {
|
|
30988
|
+
scrollSource: "none"
|
|
30989
|
+
};
|
|
30990
|
+
})(),
|
|
30991
|
+
targetRect: (() => {
|
|
30992
|
+
measureRoot();
|
|
30993
|
+
const root = rootRectRef.current;
|
|
30994
|
+
return normalizeRect(readRectSync(target?.node), root ? {
|
|
30995
|
+
w: root.w,
|
|
30996
|
+
h: root.h
|
|
30997
|
+
} : null) ?? void 0;
|
|
30998
|
+
})(),
|
|
30311
30999
|
screenName,
|
|
31000
|
+
screenSource,
|
|
30312
31001
|
ui,
|
|
30313
31002
|
stateStatus: ui ? "captured_nonempty" : "not_instrumented",
|
|
30314
31003
|
emitAutomaticAnalytics: captureAutomaticInteractions
|
|
@@ -30345,7 +31034,12 @@ function ScaleBunDebugRoot({
|
|
|
30345
31034
|
}
|
|
30346
31035
|
measureRoot();
|
|
30347
31036
|
const rootRect = rootRectRef.current;
|
|
30348
|
-
const
|
|
31037
|
+
const freshTarget = start ? null : resolveTouchTarget(e);
|
|
31038
|
+
const tapped = start?.target ?? describeTouchTarget(freshTarget);
|
|
31039
|
+
const identity = start ? {
|
|
31040
|
+
id: start.targetId,
|
|
31041
|
+
source: start.targetSource
|
|
31042
|
+
} : targetIdentity(freshTarget);
|
|
30349
31043
|
const gestureDetails = {
|
|
30350
31044
|
target: tapped,
|
|
30351
31045
|
interactionId: start?.interactionId ?? generateInteractionId(),
|
|
@@ -30353,8 +31047,16 @@ function ScaleBunDebugRoot({
|
|
|
30353
31047
|
occurredAt: start?.occurredAt ?? Date.now(),
|
|
30354
31048
|
ui: start?.ui,
|
|
30355
31049
|
stateStatus: start?.stateStatus ?? "not_captured",
|
|
30356
|
-
targetId:
|
|
31050
|
+
targetId: identity.id,
|
|
31051
|
+
targetSource: identity.source,
|
|
30357
31052
|
screenName: start?.screenName,
|
|
31053
|
+
screenSource: start?.screenSource ?? "none",
|
|
31054
|
+
targetRect: start?.targetRect,
|
|
31055
|
+
scrollX: start?.scrollX,
|
|
31056
|
+
scrollY: start?.scrollY,
|
|
31057
|
+
scrollSource: start?.scrollSource ?? "none",
|
|
31058
|
+
itemKey: start?.itemKey,
|
|
31059
|
+
itemIndex: start?.itemIndex,
|
|
30358
31060
|
emitAutomaticAnalytics: start?.emitAutomaticAnalytics ?? captureAutomaticInteractions,
|
|
30359
31061
|
x: start?.x ?? touch.pageX,
|
|
30360
31062
|
y: start?.y ?? touch.pageY,
|
|
@@ -30370,6 +31072,11 @@ function ScaleBunDebugRoot({
|
|
|
30370
31072
|
const ny = Math.max(0, Math.min(1, (start?.y ?? touch.pageY) / rootRect.h));
|
|
30371
31073
|
gestureDetails.normalizedX = nx;
|
|
30372
31074
|
gestureDetails.normalizedY = ny;
|
|
31075
|
+
const lp = localPoint(start?.targetRect ?? null, nx, ny);
|
|
31076
|
+
if (lp) {
|
|
31077
|
+
gestureDetails.localU = lp.u;
|
|
31078
|
+
gestureDetails.localV = lp.v;
|
|
31079
|
+
}
|
|
30373
31080
|
gestureDetails.normalizedPrecomputed = true;
|
|
30374
31081
|
gestureDetails.viewportWidth = rootRect.w;
|
|
30375
31082
|
gestureDetails.viewportHeight = rootRect.h;
|
|
@@ -30498,6 +31205,13 @@ function useScaleBunScreen(name) {
|
|
|
30498
31205
|
}
|
|
30499
31206
|
} catch {
|
|
30500
31207
|
}
|
|
31208
|
+
try {
|
|
31209
|
+
const {
|
|
31210
|
+
AutoScreenDetector: AutoScreenDetector2
|
|
31211
|
+
} = (init_AutoScreenDetector(), __toCommonJS(AutoScreenDetector_exports));
|
|
31212
|
+
AutoScreenDetector2.getInstance().setManualScreen(name);
|
|
31213
|
+
} catch {
|
|
31214
|
+
}
|
|
30501
31215
|
try {
|
|
30502
31216
|
const bs = getBootstrap();
|
|
30503
31217
|
const perfFeature = bs?.getPerformanceFeature?.();
|
|
@@ -30648,6 +31362,18 @@ var ScaleBunScrollView = /* @__PURE__ */ import_react12.default.forwardRef(funct
|
|
|
30648
31362
|
}
|
|
30649
31363
|
} catch {
|
|
30650
31364
|
}
|
|
31365
|
+
try {
|
|
31366
|
+
const {
|
|
31367
|
+
recordScrollOffset: recordScrollOffset2,
|
|
31368
|
+
nativeTagOf: nativeTagOf2
|
|
31369
|
+
} = (init_scrollContext(), __toCommonJS(scrollContext_exports));
|
|
31370
|
+
const {
|
|
31371
|
+
contentOffset,
|
|
31372
|
+
target
|
|
31373
|
+
} = event.nativeEvent;
|
|
31374
|
+
recordScrollOffset2(typeof target === "number" ? target : nativeTagOf2(target), contentOffset.x, contentOffset.y);
|
|
31375
|
+
} catch {
|
|
31376
|
+
}
|
|
30651
31377
|
if (userOnScroll) {
|
|
30652
31378
|
userOnScroll(event);
|
|
30653
31379
|
}
|