@scalebun/react-native 1.10.7 → 1.11.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/replaysdk/tracking/InteractionTracker.kt +25 -25
- package/android/src/main/java/com/scalebun/rn/ota/SlotManager.kt +15 -3
- package/dist/scalebun.full.js +467 -255
- package/dist/scalebun.slim.js +466 -254
- package/ios/Capture/InteractionTracker.swift +8 -4
- package/ios/Ota/OtaSlotManager.swift +19 -5
- package/lib/commonjs/analytics/EventTracker.js +5 -5
- package/lib/commonjs/analytics/automaticEvents.js +3 -2
- package/lib/commonjs/core/constants/version.js +7 -2
- package/lib/commonjs/features/journey/ScaleBunDebugRoot.js +80 -103
- package/lib/commonjs/features/journey/interactionProtocol.js +47 -0
- package/lib/commonjs/features/journey/uiState.js +8 -1
- package/lib/commonjs/features/ota/OtaOrchestrator.js +174 -48
- package/lib/commonjs/features/ota/OtaTypes.js +4 -0
- package/lib/commonjs/features/ota/useOtaUpdate.js +11 -2
- package/lib/commonjs/features/session/JourneyEventPipeline.js +6 -5
- package/lib/commonjs/features/session/SessionManager.js +37 -38
- package/lib/commonjs/public/ScaleBunFacade.js +115 -2
- package/lib/module/analytics/EventTracker.js +5 -5
- package/lib/module/analytics/automaticEvents.js +3 -2
- package/lib/module/core/constants/version.js +7 -2
- package/lib/module/features/journey/ScaleBunDebugRoot.js +80 -103
- package/lib/module/features/journey/interactionProtocol.js +38 -0
- package/lib/module/features/journey/uiState.js +8 -1
- package/lib/module/features/ota/OtaOrchestrator.js +174 -48
- package/lib/module/features/ota/OtaTypes.js +1 -1
- package/lib/module/features/ota/useOtaUpdate.js +11 -2
- package/lib/module/features/session/JourneyEventPipeline.js +6 -5
- package/lib/module/features/session/SessionManager.js +37 -38
- package/lib/module/public/ScaleBunFacade.js +115 -2
- package/lib/typescript/analytics/EventTracker.d.ts +1 -1
- package/lib/typescript/analytics/automaticEvents.d.ts +3 -1
- package/lib/typescript/core/constants/version.d.ts +7 -2
- package/lib/typescript/features/journey/interactionProtocol.d.ts +21 -0
- package/lib/typescript/features/ota/OtaEventEmitter.d.ts +15 -1
- package/lib/typescript/features/ota/OtaOrchestrator.d.ts +22 -3
- package/lib/typescript/features/ota/OtaTypes.d.ts +29 -32
- package/lib/typescript/features/session/JourneyEventPipeline.d.ts +1 -0
- package/lib/typescript/features/session/SessionManager.d.ts +15 -10
- package/lib/typescript/public/ScaleBunFacade.d.ts +27 -0
- package/package.json +4 -3
- package/src/analytics/EventTracker.ts +5 -5
- package/src/analytics/automaticEvents.ts +4 -0
- package/src/core/constants/version.ts +7 -2
- package/src/features/journey/ScaleBunDebugRoot.tsx +96 -97
- package/src/features/journey/interactionProtocol.ts +65 -0
- package/src/features/journey/uiState.ts +9 -4
- package/src/features/ota/OtaEventEmitter.ts +12 -0
- package/src/features/ota/OtaOrchestrator.ts +209 -62
- package/src/features/ota/OtaTypes.ts +37 -39
- package/src/features/ota/useOtaUpdate.ts +11 -2
- package/src/features/session/JourneyEventPipeline.ts +7 -5
- package/src/features/session/SessionManager.ts +75 -38
- package/src/public/ScaleBunFacade.ts +127 -3
package/dist/scalebun.full.js
CHANGED
|
@@ -605,7 +605,7 @@ var SDK_VERSION;
|
|
|
605
605
|
var init_version = __esm({
|
|
606
606
|
"lib/module/core/constants/version.js"() {
|
|
607
607
|
"use strict";
|
|
608
|
-
SDK_VERSION = "1.
|
|
608
|
+
SDK_VERSION = "1.11.1";
|
|
609
609
|
}
|
|
610
610
|
});
|
|
611
611
|
|
|
@@ -4567,22 +4567,23 @@ var init_JourneyEventPipeline = __esm({
|
|
|
4567
4567
|
emit(type, opts) {
|
|
4568
4568
|
try {
|
|
4569
4569
|
const key = `${type}:${opts?.subtype ?? ""}`;
|
|
4570
|
-
const
|
|
4571
|
-
|
|
4570
|
+
const receivedAt = Date.now();
|
|
4571
|
+
const occurredAt = opts?.timestamp ?? receivedAt;
|
|
4572
|
+
if (key === this.lastEventKey && receivedAt - this.lastEventTs < this.config.dedupeWindowMs) {
|
|
4572
4573
|
const incomingConfidence = opts?.payload?.confidence;
|
|
4573
4574
|
if (incomingConfidence === "high" && this.lastEventConfidence !== "high") {
|
|
4574
|
-
this._replaceLastEvent(key,
|
|
4575
|
+
this._replaceLastEvent(key, receivedAt, opts);
|
|
4575
4576
|
}
|
|
4576
4577
|
return null;
|
|
4577
4578
|
}
|
|
4578
4579
|
this.lastEventKey = key;
|
|
4579
|
-
this.lastEventTs =
|
|
4580
|
+
this.lastEventTs = receivedAt;
|
|
4580
4581
|
this.lastEventConfidence = opts?.payload?.confidence ?? null;
|
|
4581
4582
|
const event = {
|
|
4582
4583
|
eventId: generateEventId2(),
|
|
4583
4584
|
sessionId: this.sessionId,
|
|
4584
4585
|
journeyId: opts?.journeyId,
|
|
4585
|
-
ts:
|
|
4586
|
+
ts: occurredAt,
|
|
4586
4587
|
type,
|
|
4587
4588
|
subtype: opts?.subtype,
|
|
4588
4589
|
severity: opts?.severity ?? inferSeverity(type),
|
|
@@ -5682,6 +5683,99 @@ var init_calibrationContext = __esm({
|
|
|
5682
5683
|
}
|
|
5683
5684
|
});
|
|
5684
5685
|
|
|
5686
|
+
// lib/module/analytics/automaticEvents.js
|
|
5687
|
+
function compactProperties(properties) {
|
|
5688
|
+
const out = {
|
|
5689
|
+
capture_source: "automatic"
|
|
5690
|
+
};
|
|
5691
|
+
for (const [key, value] of Object.entries(properties ?? {})) {
|
|
5692
|
+
if (value !== void 0) out[key] = value;
|
|
5693
|
+
}
|
|
5694
|
+
return out;
|
|
5695
|
+
}
|
|
5696
|
+
function emitAutomaticEvent(name, properties, timestamp) {
|
|
5697
|
+
const event = {
|
|
5698
|
+
name,
|
|
5699
|
+
properties: compactProperties(properties),
|
|
5700
|
+
timestamp
|
|
5701
|
+
};
|
|
5702
|
+
if (listeners.size === 0) {
|
|
5703
|
+
pending2.push(event);
|
|
5704
|
+
if (pending2.length > MAX_PENDING) pending2.shift();
|
|
5705
|
+
return;
|
|
5706
|
+
}
|
|
5707
|
+
for (const listener of listeners) {
|
|
5708
|
+
try {
|
|
5709
|
+
listener(event);
|
|
5710
|
+
} catch {
|
|
5711
|
+
}
|
|
5712
|
+
}
|
|
5713
|
+
}
|
|
5714
|
+
function subscribeAutomaticEvents(listener) {
|
|
5715
|
+
listeners.add(listener);
|
|
5716
|
+
if (pending2.length > 0) {
|
|
5717
|
+
const buffered = pending2.splice(0, pending2.length);
|
|
5718
|
+
for (const event of buffered) {
|
|
5719
|
+
try {
|
|
5720
|
+
listener(event);
|
|
5721
|
+
} catch {
|
|
5722
|
+
}
|
|
5723
|
+
}
|
|
5724
|
+
}
|
|
5725
|
+
return () => {
|
|
5726
|
+
listeners.delete(listener);
|
|
5727
|
+
};
|
|
5728
|
+
}
|
|
5729
|
+
var listeners, pending2, MAX_PENDING;
|
|
5730
|
+
var init_automaticEvents = __esm({
|
|
5731
|
+
"lib/module/analytics/automaticEvents.js"() {
|
|
5732
|
+
"use strict";
|
|
5733
|
+
listeners = /* @__PURE__ */ new Set();
|
|
5734
|
+
pending2 = [];
|
|
5735
|
+
MAX_PENDING = 50;
|
|
5736
|
+
}
|
|
5737
|
+
});
|
|
5738
|
+
|
|
5739
|
+
// lib/module/features/journey/interactionProtocol.js
|
|
5740
|
+
function generateInteractionId() {
|
|
5741
|
+
return `ixj-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
5742
|
+
}
|
|
5743
|
+
function nearestInteractionStart(starts, occurredAt, toleranceMs = 1500) {
|
|
5744
|
+
let best;
|
|
5745
|
+
let bestDelta = toleranceMs + 1;
|
|
5746
|
+
for (const start of starts) {
|
|
5747
|
+
const delta = Math.abs(start.occurredAt - occurredAt);
|
|
5748
|
+
if (delta < bestDelta) {
|
|
5749
|
+
best = start;
|
|
5750
|
+
bestDelta = delta;
|
|
5751
|
+
}
|
|
5752
|
+
}
|
|
5753
|
+
return bestDelta <= toleranceMs ? best : void 0;
|
|
5754
|
+
}
|
|
5755
|
+
function automaticInteractionProperties(payload, screenName, canonicalMirror) {
|
|
5756
|
+
return {
|
|
5757
|
+
gesture_type: payload.gestureType,
|
|
5758
|
+
screen_name: screenName,
|
|
5759
|
+
interaction_id: payload.interaction_id,
|
|
5760
|
+
interaction_protocol: payload.interaction_protocol,
|
|
5761
|
+
state_status: payload.state_status,
|
|
5762
|
+
ui: payload.ui,
|
|
5763
|
+
target_id: payload.target_id,
|
|
5764
|
+
normalized_x: payload.normalizedX,
|
|
5765
|
+
normalized_y: payload.normalizedY,
|
|
5766
|
+
direction: payload.direction,
|
|
5767
|
+
duration_ms: payload.durationMs,
|
|
5768
|
+
canonical_mirror: canonicalMirror || void 0
|
|
5769
|
+
};
|
|
5770
|
+
}
|
|
5771
|
+
var INTERACTION_PROTOCOL_VERSION;
|
|
5772
|
+
var init_interactionProtocol = __esm({
|
|
5773
|
+
"lib/module/features/journey/interactionProtocol.js"() {
|
|
5774
|
+
"use strict";
|
|
5775
|
+
INTERACTION_PROTOCOL_VERSION = 1;
|
|
5776
|
+
}
|
|
5777
|
+
});
|
|
5778
|
+
|
|
5685
5779
|
// lib/module/features/session/SessionManager.js
|
|
5686
5780
|
var SessionManager_exports = {};
|
|
5687
5781
|
__export(SessionManager_exports, {
|
|
@@ -5690,7 +5784,7 @@ __export(SessionManager_exports, {
|
|
|
5690
5784
|
function generateSessionId2() {
|
|
5691
5785
|
return `ses-${Date.now().toString(36)}-${Math.random().toString(36).substring(2, 8)}`;
|
|
5692
5786
|
}
|
|
5693
|
-
var
|
|
5787
|
+
var _instance2, SessionManager;
|
|
5694
5788
|
var init_SessionManager = __esm({
|
|
5695
5789
|
"lib/module/features/session/SessionManager.js"() {
|
|
5696
5790
|
"use strict";
|
|
@@ -5707,7 +5801,8 @@ var init_SessionManager = __esm({
|
|
|
5707
5801
|
init_redaction();
|
|
5708
5802
|
init_calibrationContext();
|
|
5709
5803
|
init_device();
|
|
5710
|
-
|
|
5804
|
+
init_automaticEvents();
|
|
5805
|
+
init_interactionProtocol();
|
|
5711
5806
|
_instance2 = null;
|
|
5712
5807
|
SessionManager = class _SessionManager {
|
|
5713
5808
|
desktopTransport = null;
|
|
@@ -5718,15 +5813,6 @@ var init_SessionManager = __esm({
|
|
|
5718
5813
|
* lane even when no replay recording is active. Additive, opt-in (default off).
|
|
5719
5814
|
*/
|
|
5720
5815
|
_captureInteractionHeatmap = false;
|
|
5721
|
-
/**
|
|
5722
|
-
* Sampling cap for the analytics-lane heatmap emission. Now that capture is
|
|
5723
|
-
* ON by default, an unbounded one-event-per-gesture stream could materially
|
|
5724
|
-
* inflate ingest volume. We cap emitted interactions per analytics-session
|
|
5725
|
-
* window (finalize-scoped per foreground): the first N gestures define the
|
|
5726
|
-
* hotspot shape; the long tail is dropped. Resets when the window changes.
|
|
5727
|
-
*/
|
|
5728
|
-
_heatmapWindowSessionId = null;
|
|
5729
|
-
_heatmapWindowCount = 0;
|
|
5730
5816
|
session = null;
|
|
5731
5817
|
active = false;
|
|
5732
5818
|
timeoutTimer = null;
|
|
@@ -6207,18 +6293,11 @@ var init_SessionManager = __esm({
|
|
|
6207
6293
|
* injected sensitive keys (e.g. a label) are stripped before transport.
|
|
6208
6294
|
* - Never throws.
|
|
6209
6295
|
*/
|
|
6210
|
-
_emitInteractionToAnalytics(gestureType, payload) {
|
|
6211
|
-
if (!this._captureInteractionHeatmap) return;
|
|
6212
|
-
if (this.active) return;
|
|
6296
|
+
_emitInteractionToAnalytics(gestureType, payload, occurredAt, screenName) {
|
|
6297
|
+
if (!this._captureInteractionHeatmap) return false;
|
|
6298
|
+
if (this.active) return false;
|
|
6213
6299
|
const adapter = this._backendTransport;
|
|
6214
|
-
if (!adapter) return;
|
|
6215
|
-
const windowId = adapter.analyticsSessionId ?? "";
|
|
6216
|
-
if (windowId !== this._heatmapWindowSessionId) {
|
|
6217
|
-
this._heatmapWindowSessionId = windowId;
|
|
6218
|
-
this._heatmapWindowCount = 0;
|
|
6219
|
-
}
|
|
6220
|
-
if (this._heatmapWindowCount >= HEATMAP_MAX_INTERACTIONS_PER_WINDOW) return;
|
|
6221
|
-
this._heatmapWindowCount++;
|
|
6300
|
+
if (!adapter) return false;
|
|
6222
6301
|
try {
|
|
6223
6302
|
this._normalizeInteractionPayload(payload);
|
|
6224
6303
|
const safePayload = redactBody(payload);
|
|
@@ -6226,16 +6305,18 @@ var init_SessionManager = __esm({
|
|
|
6226
6305
|
eventId: generateEventId(),
|
|
6227
6306
|
// sessionId is (re)stamped by the analytics lane at flush time.
|
|
6228
6307
|
sessionId: adapter.analyticsSessionId ?? "",
|
|
6229
|
-
ts:
|
|
6308
|
+
ts: occurredAt,
|
|
6230
6309
|
type: "USER_ACTION",
|
|
6231
6310
|
subtype: `gesture:${gestureType}`,
|
|
6232
|
-
screen: this._lastKnownScreen ?? void 0,
|
|
6311
|
+
screen: screenName ?? this._lastKnownScreen ?? void 0,
|
|
6233
6312
|
payload: safePayload,
|
|
6234
6313
|
source: "user"
|
|
6235
6314
|
};
|
|
6236
6315
|
adapter.trackEvent(event);
|
|
6316
|
+
return true;
|
|
6237
6317
|
} catch (err) {
|
|
6238
6318
|
logger.error("[SessionManager] heatmap analytics emit failed:", err);
|
|
6319
|
+
return false;
|
|
6239
6320
|
}
|
|
6240
6321
|
}
|
|
6241
6322
|
/**
|
|
@@ -6401,13 +6482,14 @@ var init_SessionManager = __esm({
|
|
|
6401
6482
|
* Notify of a user interaction. Called by ScaleBunDebugRoot touch handlers.
|
|
6402
6483
|
* Also triggers frame capture for desktop-initiated recordings.
|
|
6403
6484
|
*/
|
|
6404
|
-
onUserAction(subtype, payload) {
|
|
6485
|
+
onUserAction(subtype, payload, context) {
|
|
6405
6486
|
if (!this.active) return;
|
|
6406
6487
|
this.emitEvent("USER_ACTION", {
|
|
6407
6488
|
subtype,
|
|
6408
|
-
screen: this._lastKnownScreen ?? void 0,
|
|
6489
|
+
screen: context?.screen ?? this._lastKnownScreen ?? void 0,
|
|
6409
6490
|
payload,
|
|
6410
|
-
source: "user"
|
|
6491
|
+
source: "user",
|
|
6492
|
+
timestamp: context?.timestamp
|
|
6411
6493
|
});
|
|
6412
6494
|
}
|
|
6413
6495
|
/**
|
|
@@ -6415,7 +6497,8 @@ var init_SessionManager = __esm({
|
|
|
6415
6497
|
* Emits a USER_ACTION event with gesture-specific subtype and payload.
|
|
6416
6498
|
*/
|
|
6417
6499
|
onGestureDetected(gestureType, details) {
|
|
6418
|
-
const
|
|
6500
|
+
const suppliedInteractionId = details?.interactionId;
|
|
6501
|
+
const dedupSig = suppliedInteractionId ? `id:${suppliedInteractionId}` : `${gestureType}|${String(details?.x)}|${String(details?.y)}|${String(details?.endX)}|${String(details?.endY)}`;
|
|
6419
6502
|
const nowTs = Date.now();
|
|
6420
6503
|
if (dedupSig === this._lastGestureSig && nowTs - this._lastGestureTs <= _SessionManager.GESTURE_DEDUP_WINDOW_MS) {
|
|
6421
6504
|
this._lastGestureTs = nowTs;
|
|
@@ -6423,7 +6506,15 @@ var init_SessionManager = __esm({
|
|
|
6423
6506
|
}
|
|
6424
6507
|
this._lastGestureSig = dedupSig;
|
|
6425
6508
|
this._lastGestureTs = nowTs;
|
|
6509
|
+
const interactionId = suppliedInteractionId ?? generateInteractionId();
|
|
6510
|
+
const occurredAt = details?.occurredAt ?? Date.now();
|
|
6511
|
+
const stateStatus = details?.stateStatus ?? (details?.ui ? "captured_nonempty" : "not_captured");
|
|
6426
6512
|
const payload = {
|
|
6513
|
+
interaction_id: interactionId,
|
|
6514
|
+
interaction_protocol: details?.interactionProtocol ?? INTERACTION_PROTOCOL_VERSION,
|
|
6515
|
+
state_status: stateStatus,
|
|
6516
|
+
ui: details?.ui,
|
|
6517
|
+
target_id: details?.targetId,
|
|
6427
6518
|
gestureType,
|
|
6428
6519
|
x: details?.x,
|
|
6429
6520
|
y: details?.y,
|
|
@@ -6461,12 +6552,23 @@ var init_SessionManager = __esm({
|
|
|
6461
6552
|
screenHeight: details?.screenHeight,
|
|
6462
6553
|
platform: details?.platform
|
|
6463
6554
|
};
|
|
6464
|
-
this._emitInteractionToAnalytics(gestureType, {
|
|
6555
|
+
const analyticsReplayCarrier = this._emitInteractionToAnalytics(gestureType, {
|
|
6465
6556
|
...payload
|
|
6466
|
-
});
|
|
6557
|
+
}, occurredAt, details?.screenName);
|
|
6558
|
+
const replayCarrier = !!this._backendTransport && (this.active || analyticsReplayCarrier);
|
|
6559
|
+
if (details?.emitAutomaticAnalytics) {
|
|
6560
|
+
try {
|
|
6561
|
+
const safePayload = redactBody(payload);
|
|
6562
|
+
emitAutomaticEvent("element_interacted", automaticInteractionProperties(safePayload, details?.screenName ?? this._lastKnownScreen ?? void 0, replayCarrier), occurredAt);
|
|
6563
|
+
} catch {
|
|
6564
|
+
}
|
|
6565
|
+
}
|
|
6467
6566
|
if (!this.active) return;
|
|
6468
6567
|
const target = typeof details?.target === "string" ? details.target.trim() : "";
|
|
6469
|
-
this.onUserAction(target ? `gesture:${gestureType} \xB7 ${target}` : `gesture:${gestureType}`, payload
|
|
6568
|
+
this.onUserAction(target ? `gesture:${gestureType} \xB7 ${target}` : `gesture:${gestureType}`, payload, {
|
|
6569
|
+
screen: details?.screenName,
|
|
6570
|
+
timestamp: occurredAt
|
|
6571
|
+
});
|
|
6470
6572
|
}
|
|
6471
6573
|
/**
|
|
6472
6574
|
* Manual frame capture — triggered by Desktop "Capture Step" button.
|
|
@@ -7781,12 +7883,12 @@ var init_AppLaunchCollector = __esm({
|
|
|
7781
7883
|
});
|
|
7782
7884
|
|
|
7783
7885
|
// lib/module/features/performance/collectors/ScreenLoadCollector.js
|
|
7784
|
-
var
|
|
7886
|
+
var MAX_PENDING2, ScreenLoadCollector;
|
|
7785
7887
|
var init_ScreenLoadCollector = __esm({
|
|
7786
7888
|
"lib/module/features/performance/collectors/ScreenLoadCollector.js"() {
|
|
7787
7889
|
"use strict";
|
|
7788
7890
|
init_models();
|
|
7789
|
-
|
|
7891
|
+
MAX_PENDING2 = 20;
|
|
7790
7892
|
ScreenLoadCollector = class {
|
|
7791
7893
|
pending = /* @__PURE__ */ new Map();
|
|
7792
7894
|
lastScreen = null;
|
|
@@ -7801,7 +7903,7 @@ var init_ScreenLoadCollector = __esm({
|
|
|
7801
7903
|
*/
|
|
7802
7904
|
markStart(screenName) {
|
|
7803
7905
|
if (!this.config.screenLoad) return;
|
|
7804
|
-
if (this.pending.size >=
|
|
7906
|
+
if (this.pending.size >= MAX_PENDING2) {
|
|
7805
7907
|
const oldest = this.pending.keys().next().value;
|
|
7806
7908
|
if (oldest) this.pending.delete(oldest);
|
|
7807
7909
|
}
|
|
@@ -10490,6 +10592,42 @@ var init_bootstrap = __esm({
|
|
|
10490
10592
|
}
|
|
10491
10593
|
});
|
|
10492
10594
|
|
|
10595
|
+
// lib/module/core/lifecycle/appLifecycle.js
|
|
10596
|
+
var appLifecycle_exports = {};
|
|
10597
|
+
__export(appLifecycle_exports, {
|
|
10598
|
+
appLifecycle: () => appLifecycle
|
|
10599
|
+
});
|
|
10600
|
+
var import_react_native13, AppLifecycle, appLifecycle;
|
|
10601
|
+
var init_appLifecycle = __esm({
|
|
10602
|
+
"lib/module/core/lifecycle/appLifecycle.js"() {
|
|
10603
|
+
"use strict";
|
|
10604
|
+
import_react_native13 = require("react-native");
|
|
10605
|
+
init_internalLogger();
|
|
10606
|
+
AppLifecycle = class {
|
|
10607
|
+
listeners = [];
|
|
10608
|
+
currentState = import_react_native13.AppState.currentState;
|
|
10609
|
+
constructor() {
|
|
10610
|
+
import_react_native13.AppState.addEventListener("change", this.handleStateChange);
|
|
10611
|
+
}
|
|
10612
|
+
handleStateChange = (nextState) => {
|
|
10613
|
+
__DEV__ && logger.debug(`App state changed: ${this.currentState} -> ${nextState}`);
|
|
10614
|
+
this.currentState = nextState;
|
|
10615
|
+
this.listeners.forEach((l) => l(nextState));
|
|
10616
|
+
};
|
|
10617
|
+
addListener(listener) {
|
|
10618
|
+
this.listeners.push(listener);
|
|
10619
|
+
}
|
|
10620
|
+
removeListener(listener) {
|
|
10621
|
+
this.listeners = this.listeners.filter((l) => l !== listener);
|
|
10622
|
+
}
|
|
10623
|
+
getCurrentState() {
|
|
10624
|
+
return this.currentState;
|
|
10625
|
+
}
|
|
10626
|
+
};
|
|
10627
|
+
appLifecycle = new AppLifecycle();
|
|
10628
|
+
}
|
|
10629
|
+
});
|
|
10630
|
+
|
|
10493
10631
|
// lib/module/features/crash/CrashReporter.js
|
|
10494
10632
|
function getCrashReporter() {
|
|
10495
10633
|
if (!_reporter) _reporter = new CrashReporter();
|
|
@@ -10933,58 +11071,6 @@ var init_batching = __esm({
|
|
|
10933
11071
|
}
|
|
10934
11072
|
});
|
|
10935
11073
|
|
|
10936
|
-
// lib/module/analytics/automaticEvents.js
|
|
10937
|
-
function compactProperties(properties) {
|
|
10938
|
-
const out = {
|
|
10939
|
-
capture_source: "automatic"
|
|
10940
|
-
};
|
|
10941
|
-
for (const [key, value] of Object.entries(properties ?? {})) {
|
|
10942
|
-
if (value !== void 0) out[key] = value;
|
|
10943
|
-
}
|
|
10944
|
-
return out;
|
|
10945
|
-
}
|
|
10946
|
-
function emitAutomaticEvent(name, properties) {
|
|
10947
|
-
const event = {
|
|
10948
|
-
name,
|
|
10949
|
-
properties: compactProperties(properties)
|
|
10950
|
-
};
|
|
10951
|
-
if (listeners.size === 0) {
|
|
10952
|
-
pending3.push(event);
|
|
10953
|
-
if (pending3.length > MAX_PENDING2) pending3.shift();
|
|
10954
|
-
return;
|
|
10955
|
-
}
|
|
10956
|
-
for (const listener of listeners) {
|
|
10957
|
-
try {
|
|
10958
|
-
listener(event);
|
|
10959
|
-
} catch {
|
|
10960
|
-
}
|
|
10961
|
-
}
|
|
10962
|
-
}
|
|
10963
|
-
function subscribeAutomaticEvents(listener) {
|
|
10964
|
-
listeners.add(listener);
|
|
10965
|
-
if (pending3.length > 0) {
|
|
10966
|
-
const buffered = pending3.splice(0, pending3.length);
|
|
10967
|
-
for (const event of buffered) {
|
|
10968
|
-
try {
|
|
10969
|
-
listener(event);
|
|
10970
|
-
} catch {
|
|
10971
|
-
}
|
|
10972
|
-
}
|
|
10973
|
-
}
|
|
10974
|
-
return () => {
|
|
10975
|
-
listeners.delete(listener);
|
|
10976
|
-
};
|
|
10977
|
-
}
|
|
10978
|
-
var listeners, pending3, MAX_PENDING2;
|
|
10979
|
-
var init_automaticEvents = __esm({
|
|
10980
|
-
"lib/module/analytics/automaticEvents.js"() {
|
|
10981
|
-
"use strict";
|
|
10982
|
-
listeners = /* @__PURE__ */ new Set();
|
|
10983
|
-
pending3 = [];
|
|
10984
|
-
MAX_PENDING2 = 50;
|
|
10985
|
-
}
|
|
10986
|
-
});
|
|
10987
|
-
|
|
10988
11074
|
// lib/module/analytics/EventTracker.js
|
|
10989
11075
|
function uuid() {
|
|
10990
11076
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
@@ -11053,7 +11139,7 @@ var init_EventTracker = __esm({
|
|
|
11053
11139
|
this.started = true;
|
|
11054
11140
|
if (this.cfg.automaticEventTracking) {
|
|
11055
11141
|
this.automaticEventsUnsubscribe = subscribeAutomaticEvents((event) => {
|
|
11056
|
-
this.track(event.name, event.properties);
|
|
11142
|
+
this.track(event.name, event.properties, event.timestamp);
|
|
11057
11143
|
});
|
|
11058
11144
|
}
|
|
11059
11145
|
if (this.cfg.autoLifecycleEvents) {
|
|
@@ -11157,9 +11243,9 @@ var init_EventTracker = __esm({
|
|
|
11157
11243
|
}
|
|
11158
11244
|
}
|
|
11159
11245
|
// ─── tracking ────────────────────────────────────────────────────────────
|
|
11160
|
-
track(eventName, properties) {
|
|
11246
|
+
track(eventName, properties, timestamp) {
|
|
11161
11247
|
try {
|
|
11162
|
-
this.enqueue(this.buildEnvelope(eventName, properties));
|
|
11248
|
+
this.enqueue(this.buildEnvelope(eventName, properties, timestamp));
|
|
11163
11249
|
try {
|
|
11164
11250
|
this.cfg.onEvent?.(eventName);
|
|
11165
11251
|
} catch {
|
|
@@ -11263,7 +11349,7 @@ var init_EventTracker = __esm({
|
|
|
11263
11349
|
};
|
|
11264
11350
|
}
|
|
11265
11351
|
// ─── internals ─────────────────────────────────────────────────────────────
|
|
11266
|
-
buildEnvelope(eventName, properties) {
|
|
11352
|
+
buildEnvelope(eventName, properties, timestamp) {
|
|
11267
11353
|
const ctx = this.cfg.context ?? {};
|
|
11268
11354
|
let canonicalSessionId;
|
|
11269
11355
|
try {
|
|
@@ -11274,7 +11360,7 @@ var init_EventTracker = __esm({
|
|
|
11274
11360
|
const env = {
|
|
11275
11361
|
event_id: uuid(),
|
|
11276
11362
|
event_name: eventName,
|
|
11277
|
-
event_time: Date.now(),
|
|
11363
|
+
event_time: timestamp ?? Date.now(),
|
|
11278
11364
|
app_id: this.cfg.appId,
|
|
11279
11365
|
platform: this.cfg.platform ?? resolveEventPlatform(),
|
|
11280
11366
|
installation_id: this.installationId,
|
|
@@ -12126,6 +12212,11 @@ async function deliverOtaEvents(params) {
|
|
|
12126
12212
|
kind: "ota_event",
|
|
12127
12213
|
type: e.type,
|
|
12128
12214
|
bundleId: e.bundleId,
|
|
12215
|
+
// Without this the backend stored a null releaseId on every row it ingested,
|
|
12216
|
+
// while serving the release id on every check — so the delivery funnel could
|
|
12217
|
+
// only ever be grouped by bundle, and a bundle re-promoted under a second
|
|
12218
|
+
// release merged the two into one indistinguishable series.
|
|
12219
|
+
releaseId: e.releaseId,
|
|
12129
12220
|
installationId: params.installationId,
|
|
12130
12221
|
// OTA bundles are compiled per platform, so this genuinely is ios|android. Narrowed via
|
|
12131
12222
|
// resolveMobileOS so a non-mobile RN target is skipped rather than served an Android bundle.
|
|
@@ -12227,20 +12318,31 @@ var init_OtaOrchestrator = __esm({
|
|
|
12227
12318
|
this.bootGuardConfig = config ?? {};
|
|
12228
12319
|
this.signatureConfig = config?.signature;
|
|
12229
12320
|
__DEV__ && logger.debug(`[OTA] Orchestrator initialized (RN ${this.environment.rnVersionString ?? "unknown"}${this.environment.bridgeless ? ", bridgeless" : ""}${this.environment.hermes ? `, Hermes HBC v${this.environment.hermesBytecodeVersion ?? "?"}` : ""})`);
|
|
12230
|
-
this.
|
|
12321
|
+
const slotState = this.readSlotState();
|
|
12322
|
+
this.hydrateCurrentBundleFromSlots(slotState);
|
|
12231
12323
|
void prefetchDeviceCountry();
|
|
12324
|
+
this.checkBootGuardRecovery(slotState);
|
|
12232
12325
|
this.verifyRunningBundleIdentity();
|
|
12233
|
-
this.checkBootGuardRecovery();
|
|
12234
12326
|
});
|
|
12235
12327
|
}
|
|
12236
12328
|
/**
|
|
12237
12329
|
* Read the active slot back into `currentBundle` so the next check reports
|
|
12238
12330
|
* what this device is genuinely running.
|
|
12239
12331
|
*/
|
|
12240
|
-
|
|
12241
|
-
|
|
12332
|
+
/**
|
|
12333
|
+
* Parse the native slot state once. Returns null when the module is absent or
|
|
12334
|
+
* the payload is unreadable — every caller treats that as "factory bundle".
|
|
12335
|
+
*/
|
|
12336
|
+
readSlotState() {
|
|
12337
|
+
if (!NativeScaleBunOta_default) return null;
|
|
12338
|
+
try {
|
|
12339
|
+
return JSON.parse(NativeScaleBunOta_default.getSlotState());
|
|
12340
|
+
} catch {
|
|
12341
|
+
return null;
|
|
12342
|
+
}
|
|
12343
|
+
}
|
|
12344
|
+
hydrateCurrentBundleFromSlots(state) {
|
|
12242
12345
|
try {
|
|
12243
|
-
const state = JSON.parse(NativeScaleBunOta_default.getSlotState());
|
|
12244
12346
|
const current = state?.current;
|
|
12245
12347
|
if (!current?.sha256) return;
|
|
12246
12348
|
const record4 = this.readInstallRecord();
|
|
@@ -12248,6 +12350,7 @@ var init_OtaOrchestrator = __esm({
|
|
|
12248
12350
|
this.currentBundle = {
|
|
12249
12351
|
id: record4.bundleId,
|
|
12250
12352
|
version: record4.version,
|
|
12353
|
+
releaseId: record4.releaseId ?? void 0,
|
|
12251
12354
|
sha256: record4.sha256
|
|
12252
12355
|
};
|
|
12253
12356
|
__DEV__ && logger.debug(`[OTA] Running bundle v${record4.version} (${record4.bundleId})`);
|
|
@@ -12278,29 +12381,23 @@ var init_OtaOrchestrator = __esm({
|
|
|
12278
12381
|
verifyRunningBundleIdentity() {
|
|
12279
12382
|
if (!this.currentBundle) return;
|
|
12280
12383
|
const running = readRunningBundleMarker();
|
|
12281
|
-
const
|
|
12282
|
-
if (
|
|
12384
|
+
const record4 = this.readInstallRecord();
|
|
12385
|
+
if (!record4) return;
|
|
12386
|
+
if (record4.sha256 !== this.currentBundle.sha256) {
|
|
12283
12387
|
this.clearInstallExpectation();
|
|
12284
12388
|
return;
|
|
12285
12389
|
}
|
|
12286
|
-
if (
|
|
12287
|
-
|
|
12288
|
-
|
|
12289
|
-
|
|
12290
|
-
return;
|
|
12291
|
-
}
|
|
12292
|
-
logger.error(`[OTA] INSTALL DID NOT TAKE EFFECT \u2014 bundle ${this.currentBundle.id} was installed and carries a known identity marker, but the running bundle reports ${running ?? "no marker at all"}. The app is executing different code than the slot manager believes. Check that the host app resolves the OTA bundle path at launch (see the ScaleBunOta integration for your React Native version).`);
|
|
12293
|
-
otaEventEmitter.emitSimple("APPLY_FAILED", this.currentBundle.id, {
|
|
12294
|
-
error: `install_not_effective \u2014 expected ${expected.identityToken}, running ${running ?? "none"}`
|
|
12295
|
-
});
|
|
12390
|
+
if (!record4.identityToken) return;
|
|
12391
|
+
if (running === record4.identityToken) {
|
|
12392
|
+
__DEV__ && logger.debug("[OTA] Install verified \u2014 running bundle matches what was installed.");
|
|
12393
|
+
this.retireIdentityToken(record4);
|
|
12296
12394
|
return;
|
|
12297
12395
|
}
|
|
12298
|
-
|
|
12299
|
-
|
|
12300
|
-
|
|
12301
|
-
|
|
12302
|
-
|
|
12303
|
-
}
|
|
12396
|
+
logger.error(`[OTA] INSTALL DID NOT TAKE EFFECT \u2014 bundle ${this.currentBundle.id} was installed and carries a known identity marker, but the running bundle reports ${running ?? "no marker at all"}. The app is executing different code than the slot manager believes. Check that the host app resolves the OTA bundle path at launch (see the ScaleBunOta integration for your React Native version).`);
|
|
12397
|
+
otaEventEmitter.emitSimple("APPLY_FAILED", this.currentBundle.id, {
|
|
12398
|
+
releaseId: this.currentBundle.releaseId,
|
|
12399
|
+
error: `install_not_effective \u2014 expected ${record4.identityToken}, running ${running ?? "none"}`
|
|
12400
|
+
});
|
|
12304
12401
|
}
|
|
12305
12402
|
// ── Install expectation ────────────────────────────────────────────────────
|
|
12306
12403
|
// Written just before the restart that activates a bundle, read on the next
|
|
@@ -12312,6 +12409,11 @@ var init_OtaOrchestrator = __esm({
|
|
|
12312
12409
|
this.storage().set(_OtaOrchestrator.INSTALL_EXPECTATION_KEY, JSON.stringify({
|
|
12313
12410
|
bundleId: bundle.id,
|
|
12314
12411
|
version: bundle.version,
|
|
12412
|
+
// Carried so telemetry emitted on a LATER launch (a boot-guard
|
|
12413
|
+
// rollback, an ineffective install) can still be attributed to the
|
|
12414
|
+
// release, not merely the bundle. The check response is long gone by
|
|
12415
|
+
// then; this record is the only thing that remembers.
|
|
12416
|
+
releaseId: bundle.releaseId ?? null,
|
|
12315
12417
|
// The join key back to the native slot, which records sha256 and
|
|
12316
12418
|
// nothing else identifying.
|
|
12317
12419
|
sha256: bundle.sha256,
|
|
@@ -12330,12 +12432,26 @@ var init_OtaOrchestrator = __esm({
|
|
|
12330
12432
|
return null;
|
|
12331
12433
|
}
|
|
12332
12434
|
}
|
|
12333
|
-
|
|
12334
|
-
|
|
12335
|
-
|
|
12336
|
-
|
|
12337
|
-
|
|
12338
|
-
|
|
12435
|
+
/**
|
|
12436
|
+
* Drop the identity token once the install has been proven, keeping the rest
|
|
12437
|
+
* of the record.
|
|
12438
|
+
*
|
|
12439
|
+
* The record does two jobs: it proves an install took effect (once), and it
|
|
12440
|
+
* maps the native slot's sha256 back to a bundle id (for the life of that
|
|
12441
|
+
* bundle). Only the first job is finished after a successful verification, so
|
|
12442
|
+
* only the token is retired.
|
|
12443
|
+
*/
|
|
12444
|
+
retireIdentityToken(record4) {
|
|
12445
|
+
try {
|
|
12446
|
+
this.storage().set(_OtaOrchestrator.INSTALL_EXPECTATION_KEY, JSON.stringify({
|
|
12447
|
+
bundleId: record4.bundleId,
|
|
12448
|
+
version: record4.version,
|
|
12449
|
+
releaseId: record4.releaseId ?? null,
|
|
12450
|
+
sha256: record4.sha256,
|
|
12451
|
+
identityToken: null
|
|
12452
|
+
}));
|
|
12453
|
+
} catch {
|
|
12454
|
+
}
|
|
12339
12455
|
}
|
|
12340
12456
|
clearInstallExpectation() {
|
|
12341
12457
|
try {
|
|
@@ -12352,17 +12468,24 @@ var init_OtaOrchestrator = __esm({
|
|
|
12352
12468
|
* If getSlotState() shows bootMarkerPresent=false but we have a 'previous' slot
|
|
12353
12469
|
* and no 'current' OTA bundle, the native layer already reverted.
|
|
12354
12470
|
*/
|
|
12355
|
-
checkBootGuardRecovery() {
|
|
12356
|
-
if (!NativeScaleBunOta_default) return;
|
|
12471
|
+
checkBootGuardRecovery(state) {
|
|
12357
12472
|
try {
|
|
12358
|
-
|
|
12359
|
-
const
|
|
12360
|
-
|
|
12361
|
-
|
|
12362
|
-
|
|
12363
|
-
|
|
12473
|
+
if (!state?.bootGuardReverted) return;
|
|
12474
|
+
const record4 = this.readInstallRecord();
|
|
12475
|
+
const revertedSha = state.bootGuardRevertedSha256;
|
|
12476
|
+
const matchesRecord = !!record4 && (!revertedSha || record4.sha256 === revertedSha);
|
|
12477
|
+
const reason = state.bootGuardRevertReason || "boot_crash_guard";
|
|
12478
|
+
if (matchesRecord && record4) {
|
|
12479
|
+
logger.warn(`[OTA] Boot guard fired \u2014 reverted away from bundle ${record4.bundleId} (v${record4.version}); reason: ${reason}`);
|
|
12480
|
+
otaEventEmitter.emitSimple("AUTO_ROLLBACK", record4.bundleId, {
|
|
12481
|
+
releaseId: record4.releaseId ?? void 0,
|
|
12482
|
+
version: record4.version,
|
|
12483
|
+
reason
|
|
12364
12484
|
});
|
|
12485
|
+
this.clearInstallExpectation();
|
|
12486
|
+
return;
|
|
12365
12487
|
}
|
|
12488
|
+
logger.warn(`[OTA] Boot guard fired (reason: ${reason}) but the rolled-back bundle could not be identified locally \u2014 no install record. The rollback is not reported to the server.`);
|
|
12366
12489
|
} catch {
|
|
12367
12490
|
}
|
|
12368
12491
|
}
|
|
@@ -12453,6 +12576,12 @@ var init_OtaOrchestrator = __esm({
|
|
|
12453
12576
|
};
|
|
12454
12577
|
}
|
|
12455
12578
|
__DEV__ && logger.debug("[OTA] Sync started\u2026");
|
|
12579
|
+
if (this.currentBundle?.id) {
|
|
12580
|
+
otaEventEmitter.emitSimple("CHECK", this.currentBundle.id, {
|
|
12581
|
+
releaseId: this.currentBundle.releaseId,
|
|
12582
|
+
version: this.currentBundle.version
|
|
12583
|
+
});
|
|
12584
|
+
}
|
|
12456
12585
|
const checkRes = await this.checkForUpdate(params);
|
|
12457
12586
|
if (checkRes.action === "NONE") {
|
|
12458
12587
|
__DEV__ && logger.debug("[OTA] App is up to date");
|
|
@@ -12463,7 +12592,9 @@ var init_OtaOrchestrator = __esm({
|
|
|
12463
12592
|
}
|
|
12464
12593
|
if (checkRes.action === "ROLLBACK") {
|
|
12465
12594
|
logger.warn("[OTA] Server requested ROLLBACK \u2014 reverting to previous bundle");
|
|
12466
|
-
otaEventEmitter.emitSimple("MANUAL_ROLLBACK", this.currentBundle?.id ?? "unknown"
|
|
12595
|
+
otaEventEmitter.emitSimple("MANUAL_ROLLBACK", this.currentBundle?.id ?? "unknown", {
|
|
12596
|
+
releaseId: this.currentBundle?.releaseId
|
|
12597
|
+
});
|
|
12467
12598
|
const reverted = await NativeScaleBunOta_default.revertToPrevious();
|
|
12468
12599
|
if (reverted) {
|
|
12469
12600
|
this.currentBundle = null;
|
|
@@ -12487,9 +12618,14 @@ var init_OtaOrchestrator = __esm({
|
|
|
12487
12618
|
}
|
|
12488
12619
|
const bundle = checkRes.bundle;
|
|
12489
12620
|
let patchUsed = false;
|
|
12621
|
+
otaEventEmitter.emitSimple("OFFERED", bundle.id, {
|
|
12622
|
+
releaseId: bundle.releaseId,
|
|
12623
|
+
version: bundle.version
|
|
12624
|
+
});
|
|
12490
12625
|
const signatureOutcome = await verifyBundleSignature(bundle.sha256, bundle.signature, this.signatureConfig);
|
|
12491
12626
|
if (!signatureOutcome.ok) {
|
|
12492
12627
|
otaEventEmitter.emitSimple("APPLY_FAILED", bundle.id, {
|
|
12628
|
+
releaseId: bundle.releaseId,
|
|
12493
12629
|
error: `Signature check failed: ${signatureOutcome.reason}`,
|
|
12494
12630
|
version: bundle.version
|
|
12495
12631
|
});
|
|
@@ -12500,6 +12636,7 @@ var init_OtaOrchestrator = __esm({
|
|
|
12500
12636
|
};
|
|
12501
12637
|
}
|
|
12502
12638
|
otaEventEmitter.emitSimple("DOWNLOAD_STARTED", bundle.id, {
|
|
12639
|
+
releaseId: bundle.releaseId,
|
|
12503
12640
|
version: bundle.version
|
|
12504
12641
|
});
|
|
12505
12642
|
const downloadStart = Date.now();
|
|
@@ -12585,6 +12722,7 @@ var init_OtaOrchestrator = __esm({
|
|
|
12585
12722
|
postProgress(0, "FAILED");
|
|
12586
12723
|
logger.error("[OTA] Staging bundle failed after retries");
|
|
12587
12724
|
otaEventEmitter.emitSimple("APPLY_FAILED", bundle.id, {
|
|
12725
|
+
releaseId: bundle.releaseId,
|
|
12588
12726
|
error: "Staging failed \u2014 SHA-256 mismatch or download error",
|
|
12589
12727
|
version: bundle.version
|
|
12590
12728
|
});
|
|
@@ -12597,6 +12735,7 @@ var init_OtaOrchestrator = __esm({
|
|
|
12597
12735
|
postProgress(bundle.size, "COMPLETED");
|
|
12598
12736
|
const downloadDuration = Date.now() - downloadStart;
|
|
12599
12737
|
otaEventEmitter.emitSimple("DOWNLOAD_COMPLETE", bundle.id, {
|
|
12738
|
+
releaseId: bundle.releaseId,
|
|
12600
12739
|
version: bundle.version,
|
|
12601
12740
|
durationMs: downloadDuration,
|
|
12602
12741
|
patchUsed
|
|
@@ -12605,6 +12744,7 @@ var init_OtaOrchestrator = __esm({
|
|
|
12605
12744
|
if (!applied) {
|
|
12606
12745
|
logger.error("[OTA] Applying update failed");
|
|
12607
12746
|
otaEventEmitter.emitSimple("APPLY_FAILED", bundle.id, {
|
|
12747
|
+
releaseId: bundle.releaseId,
|
|
12608
12748
|
error: "Atomic slot swap failed",
|
|
12609
12749
|
version: bundle.version
|
|
12610
12750
|
});
|
|
@@ -12618,6 +12758,7 @@ var init_OtaOrchestrator = __esm({
|
|
|
12618
12758
|
this.isRestartRequiredState = true;
|
|
12619
12759
|
this.recordInstallExpectation(bundle);
|
|
12620
12760
|
otaEventEmitter.emitSimple("INSTALLED", bundle.id, {
|
|
12761
|
+
releaseId: bundle.releaseId,
|
|
12621
12762
|
version: bundle.version
|
|
12622
12763
|
});
|
|
12623
12764
|
__DEV__ && logger.debug(`[OTA] Update v${bundle.version} installed successfully!`);
|
|
@@ -12670,6 +12811,14 @@ var init_OtaOrchestrator = __esm({
|
|
|
12670
12811
|
NativeScaleBunOta_default?.markHealthy();
|
|
12671
12812
|
__DEV__ && logger.info("[OTA] Boot guard cleared \u2014 bundle marked healthy \u2713");
|
|
12672
12813
|
this.healthyTimer = null;
|
|
12814
|
+
const running = this.currentBundle;
|
|
12815
|
+
if (running?.id) {
|
|
12816
|
+
otaEventEmitter.emitSimple("BOOT_SUCCESS", running.id, {
|
|
12817
|
+
releaseId: running.releaseId,
|
|
12818
|
+
version: running.version,
|
|
12819
|
+
durationMs: healthyMs
|
|
12820
|
+
});
|
|
12821
|
+
}
|
|
12673
12822
|
});
|
|
12674
12823
|
}, healthyMs);
|
|
12675
12824
|
} catch {
|
|
@@ -12760,7 +12909,14 @@ function clearUiState(name) {
|
|
|
12760
12909
|
}
|
|
12761
12910
|
function uiStateSignature() {
|
|
12762
12911
|
if (!declared.size) return void 0;
|
|
12763
|
-
|
|
12912
|
+
const pairs = [...declared.keys()].sort().map((k) => `${k}:${declared.get(k)}`);
|
|
12913
|
+
let signature = "";
|
|
12914
|
+
for (const pair of pairs) {
|
|
12915
|
+
const next = signature ? `${signature};${pair}` : pair;
|
|
12916
|
+
if (next.length > 96) break;
|
|
12917
|
+
signature = next;
|
|
12918
|
+
}
|
|
12919
|
+
return signature || void 0;
|
|
12764
12920
|
}
|
|
12765
12921
|
var declared, clean;
|
|
12766
12922
|
var init_uiState = __esm({
|
|
@@ -14984,31 +15140,8 @@ var FlushScheduler = class {
|
|
|
14984
15140
|
}
|
|
14985
15141
|
};
|
|
14986
15142
|
|
|
14987
|
-
// lib/module/
|
|
14988
|
-
|
|
14989
|
-
init_internalLogger();
|
|
14990
|
-
var AppLifecycle = class {
|
|
14991
|
-
listeners = [];
|
|
14992
|
-
currentState = import_react_native13.AppState.currentState;
|
|
14993
|
-
constructor() {
|
|
14994
|
-
import_react_native13.AppState.addEventListener("change", this.handleStateChange);
|
|
14995
|
-
}
|
|
14996
|
-
handleStateChange = (nextState) => {
|
|
14997
|
-
__DEV__ && logger.debug(`App state changed: ${this.currentState} -> ${nextState}`);
|
|
14998
|
-
this.currentState = nextState;
|
|
14999
|
-
this.listeners.forEach((l) => l(nextState));
|
|
15000
|
-
};
|
|
15001
|
-
addListener(listener) {
|
|
15002
|
-
this.listeners.push(listener);
|
|
15003
|
-
}
|
|
15004
|
-
removeListener(listener) {
|
|
15005
|
-
this.listeners = this.listeners.filter((l) => l !== listener);
|
|
15006
|
-
}
|
|
15007
|
-
getCurrentState() {
|
|
15008
|
-
return this.currentState;
|
|
15009
|
-
}
|
|
15010
|
-
};
|
|
15011
|
-
var appLifecycle = new AppLifecycle();
|
|
15143
|
+
// lib/module/bootstrap/SDKBootstrapper.js
|
|
15144
|
+
init_appLifecycle();
|
|
15012
15145
|
|
|
15013
15146
|
// lib/module/bootstrap/FeatureRegistry.js
|
|
15014
15147
|
init_internalLogger();
|
|
@@ -15668,14 +15801,14 @@ function installRejectionHandler(onRejection, deps) {
|
|
|
15668
15801
|
allRejections: true,
|
|
15669
15802
|
onUnhandled: (id, error) => {
|
|
15670
15803
|
const err = toError(error);
|
|
15671
|
-
|
|
15804
|
+
pending3.set(id, err);
|
|
15672
15805
|
onRejection(err);
|
|
15673
15806
|
if (isDev) {
|
|
15674
15807
|
console.warn(`Possible unhandled promise rejection (id: ${id}):`, err?.message ?? err);
|
|
15675
15808
|
}
|
|
15676
15809
|
},
|
|
15677
15810
|
onHandled: (id) => {
|
|
15678
|
-
|
|
15811
|
+
pending3.delete(id);
|
|
15679
15812
|
if (isDev) {
|
|
15680
15813
|
console.warn(`Promise rejection handled late (id: ${id}) \u2014 it was already reported.`);
|
|
15681
15814
|
}
|
|
@@ -15685,7 +15818,7 @@ function installRejectionHandler(onRejection, deps) {
|
|
|
15685
15818
|
return true;
|
|
15686
15819
|
}) ?? false;
|
|
15687
15820
|
}
|
|
15688
|
-
var
|
|
15821
|
+
var pending3 = /* @__PURE__ */ new Map();
|
|
15689
15822
|
|
|
15690
15823
|
// lib/module/features/crash/CrashFeature.js
|
|
15691
15824
|
init_CrashReporter();
|
|
@@ -20050,7 +20183,7 @@ async function captureInstallReferrerOnce(sink) {
|
|
|
20050
20183
|
}
|
|
20051
20184
|
|
|
20052
20185
|
// lib/module/public/ScaleBunFacade.js
|
|
20053
|
-
var ScaleBunFacade = class {
|
|
20186
|
+
var ScaleBunFacade = class _ScaleBunFacade {
|
|
20054
20187
|
initialized = false;
|
|
20055
20188
|
/** In-flight init promise — guards against a second init() racing before the first resolves. */
|
|
20056
20189
|
_initInFlight = null;
|
|
@@ -20210,6 +20343,7 @@ var ScaleBunFacade = class {
|
|
|
20210
20343
|
logger.warn("[ScaleBun] A signing key is pinned but no signature verifier is available. Signature checking is fail-closed: updates will be REJECTED until one exists. Install the optional peers `@noble/ed25519` + `@noble/hashes` (no further code needed), or supply `ota.verifySignature`.");
|
|
20211
20344
|
}
|
|
20212
20345
|
}
|
|
20346
|
+
const healthyAfterMs = typeof ota.healthyAfterMs === "number" ? ota.healthyAfterMs : typeof ota.healthyTimeoutMs === "number" ? ota.healthyTimeoutMs : void 0;
|
|
20213
20347
|
otaOrchestrator2.init({
|
|
20214
20348
|
...signingRequested ? {
|
|
20215
20349
|
signature: {
|
|
@@ -20219,15 +20353,99 @@ var ScaleBunFacade = class {
|
|
|
20219
20353
|
verifier
|
|
20220
20354
|
}
|
|
20221
20355
|
} : {},
|
|
20222
|
-
...
|
|
20223
|
-
|
|
20356
|
+
...healthyAfterMs !== void 0 ? {
|
|
20357
|
+
healthyAfterMs
|
|
20224
20358
|
} : {}
|
|
20225
20359
|
});
|
|
20226
20360
|
logger.info("[ScaleBun] OTA enabled from init config.");
|
|
20361
|
+
this._startOtaChecks(ota);
|
|
20227
20362
|
} catch (err) {
|
|
20228
20363
|
logger.warn(`[ScaleBun] OTA init failed: ${err?.message ?? err}`);
|
|
20229
20364
|
}
|
|
20230
20365
|
}
|
|
20366
|
+
/** Guards against overlapping config-driven OTA checks. */
|
|
20367
|
+
_otaCheckInFlight = false;
|
|
20368
|
+
/** Wall clock of the last config-driven check, for the foreground floor. */
|
|
20369
|
+
_otaLastCheckAt = 0;
|
|
20370
|
+
_otaForegroundListener = null;
|
|
20371
|
+
/**
|
|
20372
|
+
* Minimum gap between config-driven checks.
|
|
20373
|
+
*
|
|
20374
|
+
* A foreground transition is cheap to trigger — app switchers, permission
|
|
20375
|
+
* dialogs and share sheets all produce one — so an unthrottled check would
|
|
20376
|
+
* put a request on the hot path every time the user glanced away. Ten
|
|
20377
|
+
* minutes is well below any realistic release cadence and well above that
|
|
20378
|
+
* noise. A host that wants a check on demand calls `useOtaUpdate().sync()`,
|
|
20379
|
+
* which is never throttled.
|
|
20380
|
+
*/
|
|
20381
|
+
static OTA_MIN_CHECK_INTERVAL_MS = 10 * 60 * 1e3;
|
|
20382
|
+
/**
|
|
20383
|
+
* Drive OTA checks from init config: once at startup, then on each
|
|
20384
|
+
* foreground when `checkOnForeground` is on (the schema default).
|
|
20385
|
+
*
|
|
20386
|
+
* `appVersion` is resolved from the native bridge rather than asked of the
|
|
20387
|
+
* integrator, because it gates the server's `targetAppVersion` semver check
|
|
20388
|
+
* — sending a wrong or invented value is worse than sending none, and there
|
|
20389
|
+
* is no honest default. If it cannot be resolved, the check is skipped with
|
|
20390
|
+
* a warning instead of guessing.
|
|
20391
|
+
*/
|
|
20392
|
+
_startOtaChecks(ota) {
|
|
20393
|
+
if (__DEV__) {
|
|
20394
|
+
logger.info("[ScaleBun] OTA checks are skipped in debug builds (Metro owns the bundle).");
|
|
20395
|
+
return;
|
|
20396
|
+
}
|
|
20397
|
+
const runCheck = async (trigger) => {
|
|
20398
|
+
if (this._otaCheckInFlight) return;
|
|
20399
|
+
if (trigger === "foreground" && Date.now() - this._otaLastCheckAt < _ScaleBunFacade.OTA_MIN_CHECK_INTERVAL_MS) {
|
|
20400
|
+
return;
|
|
20401
|
+
}
|
|
20402
|
+
const clientKey = this._clientKey;
|
|
20403
|
+
const apiUrl = this._apiBaseUrl;
|
|
20404
|
+
if (!clientKey || !apiUrl) return;
|
|
20405
|
+
this._otaCheckInFlight = true;
|
|
20406
|
+
try {
|
|
20407
|
+
const info = await bridgeAdapter.getDeviceInfo();
|
|
20408
|
+
const appVersion = info?.appVersion;
|
|
20409
|
+
if (!appVersion) {
|
|
20410
|
+
logger.warn("[ScaleBun] OTA check skipped \u2014 the app version could not be read from the native bridge. Rebuild the native app, or drive checks yourself with useOtaUpdate({ appVersion }).");
|
|
20411
|
+
return;
|
|
20412
|
+
}
|
|
20413
|
+
this._otaLastCheckAt = Date.now();
|
|
20414
|
+
const {
|
|
20415
|
+
otaOrchestrator: otaOrchestrator2
|
|
20416
|
+
} = (init_OtaOrchestrator(), __toCommonJS(OtaOrchestrator_exports));
|
|
20417
|
+
await otaOrchestrator2.sync({
|
|
20418
|
+
apiUrl,
|
|
20419
|
+
clientKey,
|
|
20420
|
+
appVersion,
|
|
20421
|
+
// The documented option, finally connected. Omitted means the
|
|
20422
|
+
// server's `default` channel, exactly as before.
|
|
20423
|
+
channelName: typeof ota.channelOverride === "string" ? ota.channelOverride : void 0
|
|
20424
|
+
// Never forced from config: the release's own installMode
|
|
20425
|
+
// decides when the app restarts, and yanking the screen out
|
|
20426
|
+
// from under a user is not a decision this switch should make.
|
|
20427
|
+
});
|
|
20428
|
+
} catch (err) {
|
|
20429
|
+
logger.warn(`[ScaleBun] OTA check failed: ${err?.message ?? err}`);
|
|
20430
|
+
} finally {
|
|
20431
|
+
this._otaCheckInFlight = false;
|
|
20432
|
+
}
|
|
20433
|
+
};
|
|
20434
|
+
void runCheck("startup");
|
|
20435
|
+
if (ota.checkOnForeground === false) return;
|
|
20436
|
+
if (this._otaForegroundListener) return;
|
|
20437
|
+
try {
|
|
20438
|
+
const {
|
|
20439
|
+
appLifecycle: appLifecycle2
|
|
20440
|
+
} = (init_appLifecycle(), __toCommonJS(appLifecycle_exports));
|
|
20441
|
+
this._otaForegroundListener = (state) => {
|
|
20442
|
+
if (state === "active") void runCheck("foreground");
|
|
20443
|
+
};
|
|
20444
|
+
appLifecycle2.addListener(this._otaForegroundListener);
|
|
20445
|
+
} catch {
|
|
20446
|
+
this._otaForegroundListener = null;
|
|
20447
|
+
}
|
|
20448
|
+
}
|
|
20231
20449
|
_autoEnableDebug(debugConfig) {
|
|
20232
20450
|
try {
|
|
20233
20451
|
const dbgConfig = buildDebugConfigFromInitConfig(debugConfig);
|
|
@@ -21639,6 +21857,7 @@ init_internalLogger();
|
|
|
21639
21857
|
|
|
21640
21858
|
// lib/module/features/engage/engageTriggerEngine.js
|
|
21641
21859
|
init_internalLogger();
|
|
21860
|
+
init_appLifecycle();
|
|
21642
21861
|
init_AutoScreenDetector();
|
|
21643
21862
|
init_StorageBackend();
|
|
21644
21863
|
init_engageSignals();
|
|
@@ -21946,6 +22165,7 @@ function installEngageTriggerEngine(deps) {
|
|
|
21946
22165
|
}
|
|
21947
22166
|
|
|
21948
22167
|
// lib/module/features/engage/EngagePromptProvider.js
|
|
22168
|
+
init_appLifecycle();
|
|
21949
22169
|
init_deviceId();
|
|
21950
22170
|
init_api();
|
|
21951
22171
|
init_SessionManager();
|
|
@@ -33194,7 +33414,6 @@ function useEngagePrompt() {
|
|
|
33194
33414
|
// lib/module/features/journey/ScaleBunDebugRoot.js
|
|
33195
33415
|
var import_react9 = __toESM(require("react"));
|
|
33196
33416
|
var import_react_native43 = require("react-native");
|
|
33197
|
-
init_automaticEvents();
|
|
33198
33417
|
|
|
33199
33418
|
// lib/module/features/journey/touchTarget.js
|
|
33200
33419
|
var MAX_DEPTH = 12;
|
|
@@ -33266,6 +33485,7 @@ function describeTouchTarget(t) {
|
|
|
33266
33485
|
|
|
33267
33486
|
// lib/module/features/journey/ScaleBunDebugRoot.js
|
|
33268
33487
|
init_device();
|
|
33488
|
+
init_interactionProtocol();
|
|
33269
33489
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
33270
33490
|
var _bootstrap = null;
|
|
33271
33491
|
function getBootstrap() {
|
|
@@ -33455,7 +33675,9 @@ function ScaleBunDebugRoot({
|
|
|
33455
33675
|
}, [navigationRef]);
|
|
33456
33676
|
const lastNativeTouchTsRef = (0, import_react9.useRef)(0);
|
|
33457
33677
|
const nativeTrackingConfirmedRef = (0, import_react9.useRef)(false);
|
|
33458
|
-
const
|
|
33678
|
+
const touchStartRef = (0, import_react9.useRef)(null);
|
|
33679
|
+
const interactionStartsRef = (0, import_react9.useRef)([]);
|
|
33680
|
+
const pendingJsEmitsRef = (0, import_react9.useRef)(/* @__PURE__ */ new Map());
|
|
33459
33681
|
(0, import_react9.useEffect)(() => {
|
|
33460
33682
|
let subscription = null;
|
|
33461
33683
|
try {
|
|
@@ -33466,9 +33688,12 @@ function ScaleBunDebugRoot({
|
|
|
33466
33688
|
try {
|
|
33467
33689
|
lastNativeTouchTsRef.current = Date.now();
|
|
33468
33690
|
nativeTrackingConfirmedRef.current = true;
|
|
33469
|
-
|
|
33470
|
-
|
|
33471
|
-
|
|
33691
|
+
const nativeOccurredAt = typeof event.occurredAt === "number" ? event.occurredAt : (typeof event.timestamp === "number" ? event.timestamp : Date.now()) - (typeof event.durationMs === "number" ? event.durationMs : 0);
|
|
33692
|
+
const start = nearestInteractionStart(interactionStartsRef.current, nativeOccurredAt);
|
|
33693
|
+
if (start) {
|
|
33694
|
+
const pending4 = pendingJsEmitsRef.current.get(start.interactionId);
|
|
33695
|
+
if (pending4 !== void 0) clearTimeout(pending4);
|
|
33696
|
+
pendingJsEmitsRef.current.delete(start.interactionId);
|
|
33472
33697
|
}
|
|
33473
33698
|
const {
|
|
33474
33699
|
SessionManager: SessionManager2
|
|
@@ -33487,6 +33712,15 @@ function ScaleBunDebugRoot({
|
|
|
33487
33712
|
} catch {
|
|
33488
33713
|
}
|
|
33489
33714
|
sm.onGestureDetected(event.gestureType || "tap", {
|
|
33715
|
+
interactionId: event.interactionId || start?.interactionId || generateInteractionId(),
|
|
33716
|
+
interactionProtocol: event.interactionProtocol || INTERACTION_PROTOCOL_VERSION,
|
|
33717
|
+
occurredAt: nativeOccurredAt,
|
|
33718
|
+
ui: start?.ui,
|
|
33719
|
+
stateStatus: start?.stateStatus ?? "not_captured",
|
|
33720
|
+
target: start?.target,
|
|
33721
|
+
targetId: start?.targetId,
|
|
33722
|
+
screenName: start?.screenName,
|
|
33723
|
+
emitAutomaticAnalytics: start?.emitAutomaticAnalytics ?? captureAutomaticInteractions,
|
|
33490
33724
|
x: event.rawX,
|
|
33491
33725
|
y: event.rawY,
|
|
33492
33726
|
// Use the actual native end coords when present, not the down
|
|
@@ -33519,9 +33753,10 @@ function ScaleBunDebugRoot({
|
|
|
33519
33753
|
subscription?.remove();
|
|
33520
33754
|
} catch {
|
|
33521
33755
|
}
|
|
33756
|
+
for (const timer of pendingJsEmitsRef.current.values()) clearTimeout(timer);
|
|
33757
|
+
pendingJsEmitsRef.current.clear();
|
|
33522
33758
|
};
|
|
33523
|
-
}, []);
|
|
33524
|
-
const touchStartRef = (0, import_react9.useRef)(null);
|
|
33759
|
+
}, [captureAutomaticInteractions]);
|
|
33525
33760
|
const rootRectRef = (0, import_react9.useRef)(null);
|
|
33526
33761
|
const measureRoot = () => {
|
|
33527
33762
|
try {
|
|
@@ -33553,11 +33788,39 @@ function ScaleBunDebugRoot({
|
|
|
33553
33788
|
const handleTouchStart = (e) => {
|
|
33554
33789
|
try {
|
|
33555
33790
|
const touch = e.nativeEvent;
|
|
33556
|
-
|
|
33791
|
+
const target = resolveTouchTarget(e);
|
|
33792
|
+
let ui;
|
|
33793
|
+
let screenName;
|
|
33794
|
+
try {
|
|
33795
|
+
const {
|
|
33796
|
+
uiStateSignature: uiStateSignature2
|
|
33797
|
+
} = (init_uiState(), __toCommonJS(uiState_exports));
|
|
33798
|
+
ui = uiStateSignature2();
|
|
33799
|
+
} catch {
|
|
33800
|
+
}
|
|
33801
|
+
try {
|
|
33802
|
+
const {
|
|
33803
|
+
AutoScreenDetector: AutoScreenDetector2
|
|
33804
|
+
} = (init_AutoScreenDetector(), __toCommonJS(AutoScreenDetector_exports));
|
|
33805
|
+
screenName = AutoScreenDetector2.getInstance().getCurrentScreen() || void 0;
|
|
33806
|
+
} catch {
|
|
33807
|
+
}
|
|
33808
|
+
const start = {
|
|
33809
|
+
interactionId: generateInteractionId(),
|
|
33810
|
+
occurredAt: Date.now(),
|
|
33557
33811
|
x: touch.pageX,
|
|
33558
33812
|
y: touch.pageY,
|
|
33559
|
-
|
|
33813
|
+
locationX: touch.locationX,
|
|
33814
|
+
locationY: touch.locationY,
|
|
33815
|
+
target: describeTouchTarget(target),
|
|
33816
|
+
targetId: target?.testID,
|
|
33817
|
+
screenName,
|
|
33818
|
+
ui,
|
|
33819
|
+
stateStatus: ui ? "captured_nonempty" : "not_instrumented",
|
|
33820
|
+
emitAutomaticAnalytics: captureAutomaticInteractions
|
|
33560
33821
|
};
|
|
33822
|
+
touchStartRef.current = start;
|
|
33823
|
+
interactionStartsRef.current = interactionStartsRef.current.filter((candidate) => start.occurredAt - candidate.occurredAt < 5e3).concat(start).slice(-8);
|
|
33561
33824
|
} catch {
|
|
33562
33825
|
}
|
|
33563
33826
|
};
|
|
@@ -33588,21 +33851,29 @@ function ScaleBunDebugRoot({
|
|
|
33588
33851
|
}
|
|
33589
33852
|
measureRoot();
|
|
33590
33853
|
const rootRect = rootRectRef.current;
|
|
33591
|
-
const tapped = describeTouchTarget(resolveTouchTarget(e));
|
|
33854
|
+
const tapped = start?.target ?? describeTouchTarget(resolveTouchTarget(e));
|
|
33592
33855
|
const gestureDetails = {
|
|
33593
33856
|
target: tapped,
|
|
33594
|
-
|
|
33595
|
-
|
|
33596
|
-
|
|
33597
|
-
|
|
33598
|
-
|
|
33599
|
-
|
|
33857
|
+
interactionId: start?.interactionId ?? generateInteractionId(),
|
|
33858
|
+
interactionProtocol: INTERACTION_PROTOCOL_VERSION,
|
|
33859
|
+
occurredAt: start?.occurredAt ?? Date.now(),
|
|
33860
|
+
ui: start?.ui,
|
|
33861
|
+
stateStatus: start?.stateStatus ?? "not_captured",
|
|
33862
|
+
targetId: start?.targetId,
|
|
33863
|
+
screenName: start?.screenName,
|
|
33864
|
+
emitAutomaticAnalytics: start?.emitAutomaticAnalytics ?? captureAutomaticInteractions,
|
|
33865
|
+
x: start?.x ?? touch.pageX,
|
|
33866
|
+
y: start?.y ?? touch.pageY,
|
|
33867
|
+
pageX: start?.x ?? touch.pageX,
|
|
33868
|
+
pageY: start?.y ?? touch.pageY,
|
|
33869
|
+
locationX: start?.locationX ?? touch.locationX,
|
|
33870
|
+
locationY: start?.locationY ?? touch.locationY,
|
|
33600
33871
|
viewportWidth: vpW > 0 ? vpW : void 0,
|
|
33601
33872
|
viewportHeight: vpH > 0 ? vpH : void 0
|
|
33602
33873
|
};
|
|
33603
33874
|
if (rootRect && rootRect.w > 0 && rootRect.h > 0) {
|
|
33604
|
-
const nx = Math.max(0, Math.min(1, touch.pageX / rootRect.w));
|
|
33605
|
-
const ny = Math.max(0, Math.min(1, touch.pageY / rootRect.h));
|
|
33875
|
+
const nx = Math.max(0, Math.min(1, (start?.x ?? touch.pageX) / rootRect.w));
|
|
33876
|
+
const ny = Math.max(0, Math.min(1, (start?.y ?? touch.pageY) / rootRect.h));
|
|
33606
33877
|
gestureDetails.normalizedX = nx;
|
|
33607
33878
|
gestureDetails.normalizedY = ny;
|
|
33608
33879
|
gestureDetails.normalizedPrecomputed = true;
|
|
@@ -33633,7 +33904,7 @@ function ScaleBunDebugRoot({
|
|
|
33633
33904
|
const dx = touch.pageX - start.x;
|
|
33634
33905
|
const dy = touch.pageY - start.y;
|
|
33635
33906
|
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
33636
|
-
const duration = Date.now() - start.
|
|
33907
|
+
const duration = Date.now() - start.occurredAt;
|
|
33637
33908
|
gestureDetails.duration = duration;
|
|
33638
33909
|
if (dist >= 15) {
|
|
33639
33910
|
gestureDetails.endX = touch.pageX;
|
|
@@ -33651,60 +33922,16 @@ function ScaleBunDebugRoot({
|
|
|
33651
33922
|
gestureType = "long_press";
|
|
33652
33923
|
}
|
|
33653
33924
|
}
|
|
33654
|
-
const targetInfo = _extractTarget(e);
|
|
33655
|
-
gestureDetails.target = targetInfo?.testId || targetInfo?.accessibilityLabel;
|
|
33656
|
-
if (captureAutomaticInteractions) {
|
|
33657
|
-
let screen;
|
|
33658
|
-
try {
|
|
33659
|
-
const {
|
|
33660
|
-
AutoScreenDetector: AutoScreenDetector2
|
|
33661
|
-
} = (init_AutoScreenDetector(), __toCommonJS(AutoScreenDetector_exports));
|
|
33662
|
-
screen = AutoScreenDetector2.getInstance().getCurrentScreen() || void 0;
|
|
33663
|
-
} catch {
|
|
33664
|
-
}
|
|
33665
|
-
let ui;
|
|
33666
|
-
try {
|
|
33667
|
-
const {
|
|
33668
|
-
uiStateSignature: uiStateSignature2
|
|
33669
|
-
} = (init_uiState(), __toCommonJS(uiState_exports));
|
|
33670
|
-
ui = uiStateSignature2();
|
|
33671
|
-
} catch {
|
|
33672
|
-
}
|
|
33673
|
-
emitAutomaticEvent("element_interacted", {
|
|
33674
|
-
gesture_type: gestureType,
|
|
33675
|
-
screen_name: screen,
|
|
33676
|
-
/* THIS MAP ENUMERATES. A field added to the payload and forgotten here reaches the
|
|
33677
|
-
backend as undefined with no error anywhere — the recurring defect class in this
|
|
33678
|
-
codebase. `ui` is the key the grid aggregate reads for state, byte-identical to
|
|
33679
|
-
the web SDK's, so one dashboard control queries both platforms. */
|
|
33680
|
-
ui,
|
|
33681
|
-
// testID/nativeID is an author-controlled stable identifier.
|
|
33682
|
-
// Accessibility labels and rendered text are deliberately omitted.
|
|
33683
|
-
target_id: targetInfo?.testId,
|
|
33684
|
-
normalized_x: gestureDetails.normalizedX,
|
|
33685
|
-
normalized_y: gestureDetails.normalizedY,
|
|
33686
|
-
direction: gestureDetails.direction,
|
|
33687
|
-
duration_ms: gestureDetails.duration
|
|
33688
|
-
});
|
|
33689
|
-
}
|
|
33690
33925
|
if (gestureType === "tap") {
|
|
33691
33926
|
try {
|
|
33692
33927
|
const {
|
|
33693
33928
|
gestureTriggerDetector: gestureTriggerDetector2
|
|
33694
33929
|
} = (init_gestureTriggerDetector(), __toCommonJS(gestureTriggerDetector_exports));
|
|
33695
|
-
let screen;
|
|
33696
|
-
try {
|
|
33697
|
-
const {
|
|
33698
|
-
AutoScreenDetector: AutoScreenDetector2
|
|
33699
|
-
} = (init_AutoScreenDetector(), __toCommonJS(AutoScreenDetector_exports));
|
|
33700
|
-
screen = AutoScreenDetector2.getInstance().getCurrentScreen() || void 0;
|
|
33701
|
-
} catch {
|
|
33702
|
-
}
|
|
33703
33930
|
gestureTriggerDetector2.recordTap({
|
|
33704
|
-
x: touch.pageX,
|
|
33705
|
-
y: touch.pageY,
|
|
33706
|
-
hasTarget: !!(
|
|
33707
|
-
screen
|
|
33931
|
+
x: start?.x ?? touch.pageX,
|
|
33932
|
+
y: start?.y ?? touch.pageY,
|
|
33933
|
+
hasTarget: !!(start?.targetId || start?.target),
|
|
33934
|
+
screen: start?.screenName
|
|
33708
33935
|
});
|
|
33709
33936
|
} catch {
|
|
33710
33937
|
}
|
|
@@ -33717,11 +33944,9 @@ function ScaleBunDebugRoot({
|
|
|
33717
33944
|
const capturedDetails = {
|
|
33718
33945
|
...gestureDetails
|
|
33719
33946
|
};
|
|
33720
|
-
|
|
33721
|
-
|
|
33722
|
-
|
|
33723
|
-
pendingJsEmitRef.current = setTimeout(() => {
|
|
33724
|
-
pendingJsEmitRef.current = null;
|
|
33947
|
+
const interactionId = capturedDetails.interactionId;
|
|
33948
|
+
const timer = setTimeout(() => {
|
|
33949
|
+
pendingJsEmitsRef.current.delete(interactionId);
|
|
33725
33950
|
if (nativeTrackingConfirmedRef.current) return;
|
|
33726
33951
|
try {
|
|
33727
33952
|
const {
|
|
@@ -33738,6 +33963,7 @@ function ScaleBunDebugRoot({
|
|
|
33738
33963
|
} catch {
|
|
33739
33964
|
}
|
|
33740
33965
|
}, 600);
|
|
33966
|
+
pendingJsEmitsRef.current.set(interactionId, timer);
|
|
33741
33967
|
}
|
|
33742
33968
|
touchStartRef.current = null;
|
|
33743
33969
|
} catch {
|
|
@@ -33813,20 +34039,6 @@ function useScaleBunScreen(name) {
|
|
|
33813
34039
|
}
|
|
33814
34040
|
}, [name, manager]);
|
|
33815
34041
|
}
|
|
33816
|
-
function _extractTarget(e) {
|
|
33817
|
-
try {
|
|
33818
|
-
const target = e?.target;
|
|
33819
|
-
if (!target) return void 0;
|
|
33820
|
-
const props = target._internalFiberInstanceHandleDEV?.memoizedProps ?? {};
|
|
33821
|
-
return {
|
|
33822
|
-
testId: props.testID || props.nativeID || void 0,
|
|
33823
|
-
accessibilityLabel: props.accessibilityLabel || void 0,
|
|
33824
|
-
text: void 0
|
|
33825
|
-
};
|
|
33826
|
-
} catch {
|
|
33827
|
-
return void 0;
|
|
33828
|
-
}
|
|
33829
|
-
}
|
|
33830
34042
|
var styles4 = {
|
|
33831
34043
|
root: {
|
|
33832
34044
|
flex: 1
|
|
@@ -34187,7 +34399,7 @@ function useOtaUpdate(options) {
|
|
|
34187
34399
|
setIsSyncing(true);
|
|
34188
34400
|
setDownloadProgress(0);
|
|
34189
34401
|
try {
|
|
34190
|
-
const result = await otaOrchestrator.sync(
|
|
34402
|
+
const result = await otaOrchestrator.sync(optionsRef.current);
|
|
34191
34403
|
setSyncResult(result);
|
|
34192
34404
|
if (result.isMandatory && result.status === "UPDATE_INSTALLED" && optionsRef.current.mandatoryBlocksUi) {
|
|
34193
34405
|
setMandatoryUpdatePending(true);
|
|
@@ -34196,7 +34408,7 @@ function useOtaUpdate(options) {
|
|
|
34196
34408
|
} finally {
|
|
34197
34409
|
setIsSyncing(false);
|
|
34198
34410
|
}
|
|
34199
|
-
}, [
|
|
34411
|
+
}, []);
|
|
34200
34412
|
const restart = (0, import_react16.useCallback)(() => {
|
|
34201
34413
|
setMandatoryUpdatePending(false);
|
|
34202
34414
|
otaOrchestrator.restart();
|