@scalebun/react-native 1.10.6 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/scalebun/replaysdk/tracking/InteractionTracker.kt +25 -24
- package/android/src/main/java/com/scalebun/rn/ota/ScaleBunOtaModule.kt +75 -0
- package/android/src/oldarch/java/com/scalebun/rn/ota/ScaleBunOtaSpec.kt +12 -0
- package/dist/scalebun.full.js +653 -223
- package/dist/scalebun.slim.js +652 -222
- package/ios/Capture/InteractionTracker.swift +8 -4
- package/ios/Ota/ScaleBunOtaBridge.mm +6 -0
- package/ios/Ota/ScaleBunOtaModule.swift +67 -0
- package/lib/commonjs/analytics/EventTracker.js +5 -5
- package/lib/commonjs/analytics/automaticEvents.js +3 -2
- package/lib/commonjs/core/config/schema.js +34 -1
- package/lib/commonjs/core/constants/version.js +7 -2
- package/lib/commonjs/features/journey/ScaleBunDebugRoot.js +80 -79
- package/lib/commonjs/features/journey/interactionProtocol.js +47 -0
- package/lib/commonjs/features/journey/uiState.js +94 -0
- package/lib/commonjs/features/ota/crypto/builtinVerifier.js +248 -0
- package/lib/commonjs/features/ota/crypto/loadEd25519.js +40 -0
- package/lib/commonjs/features/ota/crypto/loadSha512.js +40 -0
- package/lib/commonjs/features/ota/crypto/nativeVerifier.js +121 -0
- package/lib/commonjs/features/ota/signature.js +87 -27
- package/lib/commonjs/features/session/JourneyEventPipeline.js +6 -5
- package/lib/commonjs/features/session/SessionManager.js +37 -38
- package/lib/commonjs/metro/serializerCompose.js +32 -0
- package/lib/commonjs/public/ScaleBunFacade.js +74 -11
- package/lib/module/analytics/EventTracker.js +5 -5
- package/lib/module/analytics/automaticEvents.js +3 -2
- package/lib/module/core/config/schema.js +34 -1
- package/lib/module/core/constants/version.js +7 -2
- package/lib/module/features/journey/ScaleBunDebugRoot.js +80 -79
- package/lib/module/features/journey/interactionProtocol.js +38 -0
- package/lib/module/features/journey/uiState.js +86 -0
- package/lib/module/features/ota/crypto/builtinVerifier.js +240 -0
- package/lib/module/features/ota/crypto/loadEd25519.js +34 -0
- package/lib/module/features/ota/crypto/loadSha512.js +34 -0
- package/lib/module/features/ota/crypto/nativeVerifier.js +113 -0
- package/lib/module/features/ota/signature.js +87 -27
- package/lib/module/features/session/JourneyEventPipeline.js +6 -5
- package/lib/module/features/session/SessionManager.js +37 -38
- package/lib/module/metro/serializerCompose.js +32 -0
- package/lib/module/public/ScaleBunFacade.js +74 -11
- package/lib/typescript/analytics/EventTracker.d.ts +1 -1
- package/lib/typescript/analytics/automaticEvents.d.ts +3 -1
- package/lib/typescript/core/config/schema.d.ts +2 -0
- package/lib/typescript/core/constants/version.d.ts +7 -2
- package/lib/typescript/features/journey/interactionProtocol.d.ts +21 -0
- package/lib/typescript/features/journey/uiState.d.ts +53 -0
- package/lib/typescript/features/ota/OtaTypes.d.ts +50 -0
- package/lib/typescript/features/ota/crypto/builtinVerifier.d.ts +53 -0
- package/lib/typescript/features/ota/crypto/loadEd25519.d.ts +30 -0
- package/lib/typescript/features/ota/crypto/loadSha512.d.ts +15 -0
- package/lib/typescript/features/ota/crypto/nativeVerifier.d.ts +35 -0
- package/lib/typescript/features/ota/signature.d.ts +22 -7
- 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 +35 -6
- package/lib/typescript/specs/NativeScaleBunOta.d.ts +23 -0
- package/package.json +19 -3
- package/src/analytics/EventTracker.ts +5 -5
- package/src/analytics/automaticEvents.ts +4 -0
- package/src/core/config/schema.ts +30 -3
- package/src/core/constants/version.ts +7 -2
- package/src/features/journey/ScaleBunDebugRoot.tsx +96 -75
- package/src/features/journey/interactionProtocol.ts +65 -0
- package/src/features/journey/uiState.ts +89 -0
- package/src/features/ota/OtaTypes.ts +51 -0
- package/src/features/ota/crypto/builtinVerifier.ts +257 -0
- package/src/features/ota/crypto/loadEd25519.ts +41 -0
- package/src/features/ota/crypto/loadSha512.ts +35 -0
- package/src/features/ota/crypto/nativeVerifier.ts +117 -0
- package/src/features/ota/signature.ts +108 -25
- package/src/features/session/JourneyEventPipeline.ts +7 -5
- package/src/features/session/SessionManager.ts +75 -38
- package/src/metro/serializerCompose.ts +38 -2
- package/src/public/ScaleBunFacade.ts +87 -13
- package/src/specs/NativeScaleBunOta.ts +24 -0
package/dist/scalebun.full.js
CHANGED
|
@@ -89,9 +89,9 @@ var init_internalLogger = __esm({
|
|
|
89
89
|
}
|
|
90
90
|
/** Set level from the public string name used in config (e.g. 'warn'). */
|
|
91
91
|
setLevelByName(name) {
|
|
92
|
-
const
|
|
93
|
-
if (
|
|
94
|
-
this.level =
|
|
92
|
+
const resolved3 = LEVEL_BY_NAME[name];
|
|
93
|
+
if (resolved3 !== void 0) {
|
|
94
|
+
this.level = resolved3;
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
/** Current numeric level — exposed for callers that branch on dev verbosity. */
|
|
@@ -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.0";
|
|
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
|
}
|
|
@@ -10933,58 +11035,6 @@ var init_batching = __esm({
|
|
|
10933
11035
|
}
|
|
10934
11036
|
});
|
|
10935
11037
|
|
|
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
11038
|
// lib/module/analytics/EventTracker.js
|
|
10989
11039
|
function uuid() {
|
|
10990
11040
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
@@ -11053,7 +11103,7 @@ var init_EventTracker = __esm({
|
|
|
11053
11103
|
this.started = true;
|
|
11054
11104
|
if (this.cfg.automaticEventTracking) {
|
|
11055
11105
|
this.automaticEventsUnsubscribe = subscribeAutomaticEvents((event) => {
|
|
11056
|
-
this.track(event.name, event.properties);
|
|
11106
|
+
this.track(event.name, event.properties, event.timestamp);
|
|
11057
11107
|
});
|
|
11058
11108
|
}
|
|
11059
11109
|
if (this.cfg.autoLifecycleEvents) {
|
|
@@ -11157,9 +11207,9 @@ var init_EventTracker = __esm({
|
|
|
11157
11207
|
}
|
|
11158
11208
|
}
|
|
11159
11209
|
// ─── tracking ────────────────────────────────────────────────────────────
|
|
11160
|
-
track(eventName, properties) {
|
|
11210
|
+
track(eventName, properties, timestamp) {
|
|
11161
11211
|
try {
|
|
11162
|
-
this.enqueue(this.buildEnvelope(eventName, properties));
|
|
11212
|
+
this.enqueue(this.buildEnvelope(eventName, properties, timestamp));
|
|
11163
11213
|
try {
|
|
11164
11214
|
this.cfg.onEvent?.(eventName);
|
|
11165
11215
|
} catch {
|
|
@@ -11263,7 +11313,7 @@ var init_EventTracker = __esm({
|
|
|
11263
11313
|
};
|
|
11264
11314
|
}
|
|
11265
11315
|
// ─── internals ─────────────────────────────────────────────────────────────
|
|
11266
|
-
buildEnvelope(eventName, properties) {
|
|
11316
|
+
buildEnvelope(eventName, properties, timestamp) {
|
|
11267
11317
|
const ctx = this.cfg.context ?? {};
|
|
11268
11318
|
let canonicalSessionId;
|
|
11269
11319
|
try {
|
|
@@ -11274,7 +11324,7 @@ var init_EventTracker = __esm({
|
|
|
11274
11324
|
const env = {
|
|
11275
11325
|
event_id: uuid(),
|
|
11276
11326
|
event_name: eventName,
|
|
11277
|
-
event_time: Date.now(),
|
|
11327
|
+
event_time: timestamp ?? Date.now(),
|
|
11278
11328
|
app_id: this.cfg.appId,
|
|
11279
11329
|
platform: this.cfg.platform ?? resolveEventPlatform(),
|
|
11280
11330
|
installation_id: this.installationId,
|
|
@@ -11611,10 +11661,249 @@ var init_deviceAttributes = __esm({
|
|
|
11611
11661
|
}
|
|
11612
11662
|
});
|
|
11613
11663
|
|
|
11664
|
+
// lib/module/features/ota/crypto/loadEd25519.js
|
|
11665
|
+
function loadEd25519() {
|
|
11666
|
+
try {
|
|
11667
|
+
const mod = require("@noble/ed25519");
|
|
11668
|
+
const ed = mod?.default ?? mod;
|
|
11669
|
+
if (typeof ed?.verifyAsync === "function" || typeof ed?.verify === "function") {
|
|
11670
|
+
return ed;
|
|
11671
|
+
}
|
|
11672
|
+
return null;
|
|
11673
|
+
} catch {
|
|
11674
|
+
return null;
|
|
11675
|
+
}
|
|
11676
|
+
}
|
|
11677
|
+
var init_loadEd25519 = __esm({
|
|
11678
|
+
"lib/module/features/ota/crypto/loadEd25519.js"() {
|
|
11679
|
+
"use strict";
|
|
11680
|
+
}
|
|
11681
|
+
});
|
|
11682
|
+
|
|
11683
|
+
// lib/module/features/ota/crypto/loadSha512.js
|
|
11684
|
+
function loadSha512() {
|
|
11685
|
+
try {
|
|
11686
|
+
const mod = require("@noble/hashes/sha2.js");
|
|
11687
|
+
const sha512 = mod?.sha512 ?? mod?.default?.sha512;
|
|
11688
|
+
return typeof sha512 === "function" ? sha512 : null;
|
|
11689
|
+
} catch {
|
|
11690
|
+
return null;
|
|
11691
|
+
}
|
|
11692
|
+
}
|
|
11693
|
+
var init_loadSha512 = __esm({
|
|
11694
|
+
"lib/module/features/ota/crypto/loadSha512.js"() {
|
|
11695
|
+
"use strict";
|
|
11696
|
+
}
|
|
11697
|
+
});
|
|
11698
|
+
|
|
11699
|
+
// lib/module/features/ota/crypto/builtinVerifier.js
|
|
11700
|
+
var builtinVerifier_exports = {};
|
|
11701
|
+
__export(builtinVerifier_exports, {
|
|
11702
|
+
_resetBuiltinVerifier: () => _resetBuiltinVerifier,
|
|
11703
|
+
decodeSignature: () => decodeSignature,
|
|
11704
|
+
getBuiltinVerifier: () => getBuiltinVerifier
|
|
11705
|
+
});
|
|
11706
|
+
function hexToBytes(hex) {
|
|
11707
|
+
const clean2 = hex.trim().toLowerCase().replace(/^0x/, "");
|
|
11708
|
+
if (clean2.length === 0 || clean2.length % 2 !== 0 || /[^0-9a-f]/.test(clean2)) return null;
|
|
11709
|
+
const out = new Uint8Array(clean2.length / 2);
|
|
11710
|
+
for (let i = 0; i < out.length; i++) {
|
|
11711
|
+
out[i] = parseInt(clean2.slice(i * 2, i * 2 + 2), 16);
|
|
11712
|
+
}
|
|
11713
|
+
return out;
|
|
11714
|
+
}
|
|
11715
|
+
function base64ToBytes2(b64) {
|
|
11716
|
+
const s = b64.trim().replace(/-/g, "+").replace(/_/g, "/").replace(/=+$/, "");
|
|
11717
|
+
if (s.length === 0) return null;
|
|
11718
|
+
const out = [];
|
|
11719
|
+
let buffer = 0;
|
|
11720
|
+
let bits = 0;
|
|
11721
|
+
for (const ch of s) {
|
|
11722
|
+
const idx = B64_ALPHABET2.indexOf(ch);
|
|
11723
|
+
if (idx === -1) return null;
|
|
11724
|
+
buffer = buffer << 6 | idx;
|
|
11725
|
+
bits += 6;
|
|
11726
|
+
if (bits >= 8) {
|
|
11727
|
+
bits -= 8;
|
|
11728
|
+
out.push(buffer >> bits & 255);
|
|
11729
|
+
}
|
|
11730
|
+
}
|
|
11731
|
+
return Uint8Array.from(out);
|
|
11732
|
+
}
|
|
11733
|
+
function decodeSignature(sig) {
|
|
11734
|
+
const trimmed = sig.trim();
|
|
11735
|
+
if (/^(0x)?[0-9a-fA-F]{128}$/.test(trimmed)) {
|
|
11736
|
+
const hex2 = hexToBytes(trimmed);
|
|
11737
|
+
if (hex2 && hex2.length === ED25519_SIGNATURE_BYTES) return hex2;
|
|
11738
|
+
}
|
|
11739
|
+
const b64 = base64ToBytes2(trimmed);
|
|
11740
|
+
if (b64 && b64.length === ED25519_SIGNATURE_BYTES) return b64;
|
|
11741
|
+
const hex = hexToBytes(trimmed);
|
|
11742
|
+
if (hex && hex.length === ED25519_SIGNATURE_BYTES) return hex;
|
|
11743
|
+
return null;
|
|
11744
|
+
}
|
|
11745
|
+
function wireSha512(ed, sha512) {
|
|
11746
|
+
const concat2 = (parts) => {
|
|
11747
|
+
if (parts.length === 1) return parts[0];
|
|
11748
|
+
let total = 0;
|
|
11749
|
+
for (const p of parts) total += p.length;
|
|
11750
|
+
const out = new Uint8Array(total);
|
|
11751
|
+
let off = 0;
|
|
11752
|
+
for (const p of parts) {
|
|
11753
|
+
out.set(p, off);
|
|
11754
|
+
off += p.length;
|
|
11755
|
+
}
|
|
11756
|
+
return out;
|
|
11757
|
+
};
|
|
11758
|
+
const sync = (...msgs) => sha512(concat2(msgs));
|
|
11759
|
+
const asyncFn = async (...msgs) => sha512(concat2(msgs));
|
|
11760
|
+
let wired = false;
|
|
11761
|
+
try {
|
|
11762
|
+
if (ed.hashes && typeof ed.hashes === "object") {
|
|
11763
|
+
if (typeof ed.hashes.sha512 !== "function") ed.hashes.sha512 = sync;
|
|
11764
|
+
if (typeof ed.hashes.sha512Async !== "function") ed.hashes.sha512Async = asyncFn;
|
|
11765
|
+
wired = typeof ed.hashes.sha512 === "function";
|
|
11766
|
+
}
|
|
11767
|
+
} catch {
|
|
11768
|
+
}
|
|
11769
|
+
try {
|
|
11770
|
+
if (ed.etc && typeof ed.etc === "object") {
|
|
11771
|
+
if (typeof ed.etc.sha512Sync !== "function") ed.etc.sha512Sync = sync;
|
|
11772
|
+
if (typeof ed.etc.sha512Async !== "function") ed.etc.sha512Async = asyncFn;
|
|
11773
|
+
wired = wired || typeof ed.etc.sha512Sync === "function";
|
|
11774
|
+
}
|
|
11775
|
+
} catch {
|
|
11776
|
+
}
|
|
11777
|
+
return wired;
|
|
11778
|
+
}
|
|
11779
|
+
function getBuiltinVerifier() {
|
|
11780
|
+
if (resolved !== void 0) return resolved;
|
|
11781
|
+
const ed = loadEd25519();
|
|
11782
|
+
const sha512 = loadSha512();
|
|
11783
|
+
if (!ed || !sha512) {
|
|
11784
|
+
resolved = null;
|
|
11785
|
+
return null;
|
|
11786
|
+
}
|
|
11787
|
+
if (!wireSha512(ed, sha512)) {
|
|
11788
|
+
logger.error("[OTA] @noble/ed25519 is installed but its SHA-512 provider could not be wired (unrecognised version shape). Supply `ota.verifySignature` yourself, or pin a @noble/ed25519 version this SDK supports.");
|
|
11789
|
+
resolved = null;
|
|
11790
|
+
return null;
|
|
11791
|
+
}
|
|
11792
|
+
resolved = async ({
|
|
11793
|
+
messageHex,
|
|
11794
|
+
signature,
|
|
11795
|
+
publicKey
|
|
11796
|
+
}) => {
|
|
11797
|
+
const message = hexToBytes(messageHex);
|
|
11798
|
+
const pub = hexToBytes(publicKey);
|
|
11799
|
+
const sig = decodeSignature(signature);
|
|
11800
|
+
if (!message) {
|
|
11801
|
+
logger.error("[OTA] Bundle SHA-256 is not valid hex \u2014 refusing to stage.");
|
|
11802
|
+
return false;
|
|
11803
|
+
}
|
|
11804
|
+
if (!pub || pub.length !== ED25519_PUBLIC_KEY_BYTES) {
|
|
11805
|
+
logger.error(`[OTA] ota.publicSigningKey must be ${ED25519_PUBLIC_KEY_BYTES} bytes of hex (${ED25519_PUBLIC_KEY_BYTES * 2} characters) \u2014 got ${pub ? `${pub.length} bytes` : "a value that is not hex"}.`);
|
|
11806
|
+
return false;
|
|
11807
|
+
}
|
|
11808
|
+
if (!sig) {
|
|
11809
|
+
logger.error("[OTA] Bundle signature is not a 64-byte hex/base64 value.");
|
|
11810
|
+
return false;
|
|
11811
|
+
}
|
|
11812
|
+
try {
|
|
11813
|
+
if (typeof ed.verify === "function") return ed.verify(sig, message, pub);
|
|
11814
|
+
if (typeof ed.verifyAsync === "function") return await ed.verifyAsync(sig, message, pub);
|
|
11815
|
+
return false;
|
|
11816
|
+
} catch {
|
|
11817
|
+
return false;
|
|
11818
|
+
}
|
|
11819
|
+
};
|
|
11820
|
+
__DEV__ && logger.debug("[OTA] Using built-in ed25519 verifier (@noble/ed25519 detected).");
|
|
11821
|
+
return resolved;
|
|
11822
|
+
}
|
|
11823
|
+
function _resetBuiltinVerifier() {
|
|
11824
|
+
resolved = void 0;
|
|
11825
|
+
}
|
|
11826
|
+
var ED25519_SIGNATURE_BYTES, ED25519_PUBLIC_KEY_BYTES, B64_ALPHABET2, resolved;
|
|
11827
|
+
var init_builtinVerifier = __esm({
|
|
11828
|
+
"lib/module/features/ota/crypto/builtinVerifier.js"() {
|
|
11829
|
+
"use strict";
|
|
11830
|
+
init_loadEd25519();
|
|
11831
|
+
init_loadSha512();
|
|
11832
|
+
init_internalLogger();
|
|
11833
|
+
ED25519_SIGNATURE_BYTES = 64;
|
|
11834
|
+
ED25519_PUBLIC_KEY_BYTES = 32;
|
|
11835
|
+
B64_ALPHABET2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
11836
|
+
}
|
|
11837
|
+
});
|
|
11838
|
+
|
|
11839
|
+
// lib/module/features/ota/crypto/nativeVerifier.js
|
|
11840
|
+
function bytesToHex(bytes) {
|
|
11841
|
+
let out = "";
|
|
11842
|
+
for (const b of bytes) out += b.toString(16).padStart(2, "0");
|
|
11843
|
+
return out;
|
|
11844
|
+
}
|
|
11845
|
+
function normalizeHex(value, expectedChars) {
|
|
11846
|
+
const clean2 = value.trim().toLowerCase().replace(/^0x/, "");
|
|
11847
|
+
if (clean2.length !== expectedChars || /[^0-9a-f]/.test(clean2)) return null;
|
|
11848
|
+
return clean2;
|
|
11849
|
+
}
|
|
11850
|
+
function getNativeVerifier() {
|
|
11851
|
+
if (resolved2 !== void 0) return resolved2;
|
|
11852
|
+
const native = NativeScaleBunOta_default;
|
|
11853
|
+
if (!native || typeof native.verifyEd25519 !== "function") {
|
|
11854
|
+
resolved2 = null;
|
|
11855
|
+
return null;
|
|
11856
|
+
}
|
|
11857
|
+
resolved2 = async ({
|
|
11858
|
+
messageHex,
|
|
11859
|
+
signature,
|
|
11860
|
+
publicKey
|
|
11861
|
+
}) => {
|
|
11862
|
+
const message = normalizeHex(messageHex, 64);
|
|
11863
|
+
const key = normalizeHex(publicKey, ED25519_PUBLIC_KEY_HEX_CHARS);
|
|
11864
|
+
const sigBytes = decodeSignature(signature);
|
|
11865
|
+
if (!message || !key || !sigBytes) {
|
|
11866
|
+
logger.error("[OTA] Signature inputs malformed (bundle hash, signature, or public key) \u2014 refusing to stage.");
|
|
11867
|
+
return false;
|
|
11868
|
+
}
|
|
11869
|
+
let verdict;
|
|
11870
|
+
try {
|
|
11871
|
+
verdict = await native.verifyEd25519(message, bytesToHex(sigBytes), key);
|
|
11872
|
+
} catch (err) {
|
|
11873
|
+
logger.warn(`[OTA] Native ed25519 verify failed to run: ${err?.message ?? err}`);
|
|
11874
|
+
verdict = "unavailable";
|
|
11875
|
+
}
|
|
11876
|
+
if (verdict === "valid") return true;
|
|
11877
|
+
if (verdict === "invalid") return false;
|
|
11878
|
+
const builtin = getBuiltinVerifier();
|
|
11879
|
+
if (builtin) {
|
|
11880
|
+
return builtin({
|
|
11881
|
+
messageHex,
|
|
11882
|
+
signature,
|
|
11883
|
+
publicKey
|
|
11884
|
+
});
|
|
11885
|
+
}
|
|
11886
|
+
throw new Error("ed25519 unavailable: this OS has no native implementation (Android < 13) and the optional peers @noble/ed25519 + @noble/hashes are not installed.");
|
|
11887
|
+
};
|
|
11888
|
+
__DEV__ && logger.debug("[OTA] Using native ed25519 verifier (platform crypto).");
|
|
11889
|
+
return resolved2;
|
|
11890
|
+
}
|
|
11891
|
+
var ED25519_PUBLIC_KEY_HEX_CHARS, resolved2;
|
|
11892
|
+
var init_nativeVerifier = __esm({
|
|
11893
|
+
"lib/module/features/ota/crypto/nativeVerifier.js"() {
|
|
11894
|
+
"use strict";
|
|
11895
|
+
init_NativeScaleBunOta();
|
|
11896
|
+
init_internalLogger();
|
|
11897
|
+
init_builtinVerifier();
|
|
11898
|
+
ED25519_PUBLIC_KEY_HEX_CHARS = 64;
|
|
11899
|
+
}
|
|
11900
|
+
});
|
|
11901
|
+
|
|
11614
11902
|
// lib/module/features/ota/signature.js
|
|
11615
11903
|
async function verifyBundleSignature(bundleSha256, signature, config) {
|
|
11616
|
-
const
|
|
11617
|
-
|
|
11904
|
+
const rawKey = config?.publicKey;
|
|
11905
|
+
const keys = (Array.isArray(rawKey) ? rawKey : rawKey ? [rawKey] : []).filter((k) => typeof k === "string" && k.trim().length > 0);
|
|
11906
|
+
if (rawKey === void 0 || rawKey === "" || rawKey === null) {
|
|
11618
11907
|
if (!warnedNotConfigured) {
|
|
11619
11908
|
warnedNotConfigured = true;
|
|
11620
11909
|
logger.warn("[OTA] No signing public key configured \u2014 bundles are accepted on SHA-256 integrity alone. Configure `ota.publicSigningKey` to enforce authenticity.");
|
|
@@ -11624,6 +11913,13 @@ async function verifyBundleSignature(bundleSha256, signature, config) {
|
|
|
11624
11913
|
reason: "not_configured"
|
|
11625
11914
|
};
|
|
11626
11915
|
}
|
|
11916
|
+
if (keys.length === 0) {
|
|
11917
|
+
logger.error("[OTA] Signing key config resolves to zero usable keys (empty array or blank strings) \u2014 refusing to stage. Remove the config to disable signing, or pin at least one real key.");
|
|
11918
|
+
return {
|
|
11919
|
+
ok: false,
|
|
11920
|
+
reason: "no_keys"
|
|
11921
|
+
};
|
|
11922
|
+
}
|
|
11627
11923
|
if (!signature) {
|
|
11628
11924
|
logger.error("[OTA] Bundle has no signature but a signing key is configured \u2014 refusing to stage.");
|
|
11629
11925
|
return {
|
|
@@ -11631,43 +11927,52 @@ async function verifyBundleSignature(bundleSha256, signature, config) {
|
|
|
11631
11927
|
reason: "missing_signature"
|
|
11632
11928
|
};
|
|
11633
11929
|
}
|
|
11634
|
-
|
|
11635
|
-
|
|
11930
|
+
const verifier = typeof config?.verifier === "function" ? config.verifier : getNativeVerifier() ?? getBuiltinVerifier();
|
|
11931
|
+
if (!verifier) {
|
|
11932
|
+
logger.error("[OTA] A signing key is configured but no signature verifier is available \u2014 refusing to stage. Either install the optional peers `@noble/ed25519` and `@noble/hashes` (the SDK then verifies with no further code), or provide your own `ota.verifySignature`.");
|
|
11636
11933
|
return {
|
|
11637
11934
|
ok: false,
|
|
11638
11935
|
reason: "no_verifier"
|
|
11639
11936
|
};
|
|
11640
11937
|
}
|
|
11641
|
-
|
|
11642
|
-
|
|
11643
|
-
|
|
11644
|
-
|
|
11645
|
-
|
|
11646
|
-
|
|
11647
|
-
|
|
11648
|
-
|
|
11649
|
-
|
|
11650
|
-
|
|
11651
|
-
|
|
11938
|
+
let sawThrow = false;
|
|
11939
|
+
let sawCompletion = false;
|
|
11940
|
+
for (const key of keys) {
|
|
11941
|
+
try {
|
|
11942
|
+
const valid = await verifier({
|
|
11943
|
+
messageHex: bundleSha256,
|
|
11944
|
+
signature,
|
|
11945
|
+
publicKey: key
|
|
11946
|
+
});
|
|
11947
|
+
sawCompletion = true;
|
|
11948
|
+
if (valid) return {
|
|
11949
|
+
ok: true,
|
|
11950
|
+
reason: "verified"
|
|
11652
11951
|
};
|
|
11952
|
+
} catch (err) {
|
|
11953
|
+
sawThrow = true;
|
|
11954
|
+
logger.error(`[OTA] Signature verifier threw: ${err?.message ?? err}`);
|
|
11653
11955
|
}
|
|
11654
|
-
|
|
11655
|
-
|
|
11656
|
-
reason: "verified"
|
|
11657
|
-
};
|
|
11658
|
-
} catch (err) {
|
|
11659
|
-
logger.error(`[OTA] Signature verifier threw: ${err?.message ?? err}`);
|
|
11956
|
+
}
|
|
11957
|
+
if (sawThrow && !sawCompletion) {
|
|
11660
11958
|
return {
|
|
11661
11959
|
ok: false,
|
|
11662
11960
|
reason: "verifier_threw"
|
|
11663
11961
|
};
|
|
11664
11962
|
}
|
|
11963
|
+
logger.error(keys.length > 1 ? `[OTA] Bundle signature is INVALID under all ${keys.length} pinned keys \u2014 refusing to stage.` : "[OTA] Bundle signature is INVALID \u2014 refusing to stage.");
|
|
11964
|
+
return {
|
|
11965
|
+
ok: false,
|
|
11966
|
+
reason: "invalid_signature"
|
|
11967
|
+
};
|
|
11665
11968
|
}
|
|
11666
11969
|
var warnedNotConfigured;
|
|
11667
11970
|
var init_signature = __esm({
|
|
11668
11971
|
"lib/module/features/ota/signature.js"() {
|
|
11669
11972
|
"use strict";
|
|
11670
11973
|
init_internalLogger();
|
|
11974
|
+
init_builtinVerifier();
|
|
11975
|
+
init_nativeVerifier();
|
|
11671
11976
|
warnedNotConfigured = false;
|
|
11672
11977
|
}
|
|
11673
11978
|
});
|
|
@@ -12487,6 +12792,42 @@ var init_OtaOrchestrator = __esm({
|
|
|
12487
12792
|
}
|
|
12488
12793
|
});
|
|
12489
12794
|
|
|
12795
|
+
// lib/module/features/journey/uiState.js
|
|
12796
|
+
var uiState_exports = {};
|
|
12797
|
+
__export(uiState_exports, {
|
|
12798
|
+
clearUiState: () => clearUiState,
|
|
12799
|
+
setUiState: () => setUiState,
|
|
12800
|
+
uiStateSignature: () => uiStateSignature
|
|
12801
|
+
});
|
|
12802
|
+
function setUiState(name, value) {
|
|
12803
|
+
const n = clean(name);
|
|
12804
|
+
const v = clean(value);
|
|
12805
|
+
if (n && v) declared.set(n, v);
|
|
12806
|
+
}
|
|
12807
|
+
function clearUiState(name) {
|
|
12808
|
+
if (name === void 0) declared.clear();
|
|
12809
|
+
else declared.delete(clean(name));
|
|
12810
|
+
}
|
|
12811
|
+
function uiStateSignature() {
|
|
12812
|
+
if (!declared.size) return void 0;
|
|
12813
|
+
const pairs = [...declared.keys()].sort().map((k) => `${k}:${declared.get(k)}`);
|
|
12814
|
+
let signature = "";
|
|
12815
|
+
for (const pair of pairs) {
|
|
12816
|
+
const next = signature ? `${signature};${pair}` : pair;
|
|
12817
|
+
if (next.length > 96) break;
|
|
12818
|
+
signature = next;
|
|
12819
|
+
}
|
|
12820
|
+
return signature || void 0;
|
|
12821
|
+
}
|
|
12822
|
+
var declared, clean;
|
|
12823
|
+
var init_uiState = __esm({
|
|
12824
|
+
"lib/module/features/journey/uiState.js"() {
|
|
12825
|
+
"use strict";
|
|
12826
|
+
declared = /* @__PURE__ */ new Map();
|
|
12827
|
+
clean = (s) => typeof s === "string" ? s.replace(/[;:|]/g, "").trim().slice(0, 32) : "";
|
|
12828
|
+
}
|
|
12829
|
+
});
|
|
12830
|
+
|
|
12490
12831
|
// lib/module/features/engage/gestureTriggerDetector.js
|
|
12491
12832
|
var gestureTriggerDetector_exports = {};
|
|
12492
12833
|
__export(gestureTriggerDetector_exports, {
|
|
@@ -14404,6 +14745,14 @@ var SHAPE = {
|
|
|
14404
14745
|
kind: "str",
|
|
14405
14746
|
opt: true
|
|
14406
14747
|
},
|
|
14748
|
+
// Rotation support: several pinned keys, any of which may verify a
|
|
14749
|
+
// bundle. Ship a build pinning [old, new], re-sign server-side with
|
|
14750
|
+
// new, drop old next release — no emergency store submission when a
|
|
14751
|
+
// key must be replaced. Merged with publicSigningKey by the facade.
|
|
14752
|
+
publicSigningKeys: {
|
|
14753
|
+
kind: "strArr",
|
|
14754
|
+
opt: true
|
|
14755
|
+
},
|
|
14407
14756
|
mandatoryBlocksUi: bool(false)
|
|
14408
14757
|
}
|
|
14409
14758
|
}
|
|
@@ -14439,7 +14788,7 @@ function parseField(field, input, path, issues) {
|
|
|
14439
14788
|
value: field.def,
|
|
14440
14789
|
present: true
|
|
14441
14790
|
};
|
|
14442
|
-
return fail(field.kind === "bool" ? "Expected boolean, received undefined" : field.kind === "num" ? "Expected number, received undefined" : field.kind === "str" ? "Expected string, received undefined" : field.kind === "enum" ? "Invalid enum value. Expected " + field.values.map((v) => `'${v}'`).join(" | ") : "Expected object, received undefined");
|
|
14791
|
+
return fail(field.kind === "bool" ? "Expected boolean, received undefined" : field.kind === "num" ? "Expected number, received undefined" : field.kind === "str" ? "Expected string, received undefined" : field.kind === "strArr" ? "Expected array, received undefined" : field.kind === "enum" ? "Invalid enum value. Expected " + field.values.map((v) => `'${v}'`).join(" | ") : "Expected object, received undefined");
|
|
14443
14792
|
}
|
|
14444
14793
|
switch (field.kind) {
|
|
14445
14794
|
case "any":
|
|
@@ -14465,6 +14814,27 @@ function parseField(field, input, path, issues) {
|
|
|
14465
14814
|
value: input,
|
|
14466
14815
|
present: true
|
|
14467
14816
|
};
|
|
14817
|
+
case "strArr": {
|
|
14818
|
+
if (!Array.isArray(input)) return fail(`Expected array, received ${typeofName2(input)}`);
|
|
14819
|
+
let allOk = true;
|
|
14820
|
+
for (let i = 0; i < input.length; i++) {
|
|
14821
|
+
if (typeof input[i] !== "string") {
|
|
14822
|
+
issues.push({
|
|
14823
|
+
path: [...path, i],
|
|
14824
|
+
message: `Expected string, received ${typeofName2(input[i])}`
|
|
14825
|
+
});
|
|
14826
|
+
allOk = false;
|
|
14827
|
+
}
|
|
14828
|
+
}
|
|
14829
|
+
return allOk ? {
|
|
14830
|
+
ok: true,
|
|
14831
|
+
value: [...input],
|
|
14832
|
+
present: true
|
|
14833
|
+
} : {
|
|
14834
|
+
ok: false,
|
|
14835
|
+
present: true
|
|
14836
|
+
};
|
|
14837
|
+
}
|
|
14468
14838
|
case "num":
|
|
14469
14839
|
if (typeof input !== "number" || Number.isNaN(input)) {
|
|
14470
14840
|
return fail(`Expected number, received ${typeofName2(input)}`);
|
|
@@ -15355,14 +15725,14 @@ function installRejectionHandler(onRejection, deps) {
|
|
|
15355
15725
|
allRejections: true,
|
|
15356
15726
|
onUnhandled: (id, error) => {
|
|
15357
15727
|
const err = toError(error);
|
|
15358
|
-
|
|
15728
|
+
pending3.set(id, err);
|
|
15359
15729
|
onRejection(err);
|
|
15360
15730
|
if (isDev) {
|
|
15361
15731
|
console.warn(`Possible unhandled promise rejection (id: ${id}):`, err?.message ?? err);
|
|
15362
15732
|
}
|
|
15363
15733
|
},
|
|
15364
15734
|
onHandled: (id) => {
|
|
15365
|
-
|
|
15735
|
+
pending3.delete(id);
|
|
15366
15736
|
if (isDev) {
|
|
15367
15737
|
console.warn(`Promise rejection handled late (id: ${id}) \u2014 it was already reported.`);
|
|
15368
15738
|
}
|
|
@@ -15372,7 +15742,7 @@ function installRejectionHandler(onRejection, deps) {
|
|
|
15372
15742
|
return true;
|
|
15373
15743
|
}) ?? false;
|
|
15374
15744
|
}
|
|
15375
|
-
var
|
|
15745
|
+
var pending3 = /* @__PURE__ */ new Map();
|
|
15376
15746
|
|
|
15377
15747
|
// lib/module/features/crash/CrashFeature.js
|
|
15378
15748
|
init_CrashReporter();
|
|
@@ -15668,16 +16038,16 @@ function base64ToBytes(base64) {
|
|
|
15668
16038
|
return decodeManual(base64);
|
|
15669
16039
|
}
|
|
15670
16040
|
function decodeManual(base64) {
|
|
15671
|
-
const
|
|
15672
|
-
const pad =
|
|
15673
|
-
const outLen = Math.floor(
|
|
16041
|
+
const clean2 = base64.replace(/[^A-Za-z0-9+/=]/g, "");
|
|
16042
|
+
const pad = clean2.endsWith("==") ? 2 : clean2.endsWith("=") ? 1 : 0;
|
|
16043
|
+
const outLen = Math.floor(clean2.length * 3 / 4) - pad;
|
|
15674
16044
|
const out = new Uint8Array(outLen);
|
|
15675
16045
|
let p = 0;
|
|
15676
|
-
for (let i = 0; i <
|
|
15677
|
-
const c0 = B64_ALPHABET.indexOf(
|
|
15678
|
-
const c1 = B64_ALPHABET.indexOf(
|
|
15679
|
-
const c2 = B64_ALPHABET.indexOf(
|
|
15680
|
-
const c3 = B64_ALPHABET.indexOf(
|
|
16046
|
+
for (let i = 0; i < clean2.length; i += 4) {
|
|
16047
|
+
const c0 = B64_ALPHABET.indexOf(clean2[i]);
|
|
16048
|
+
const c1 = B64_ALPHABET.indexOf(clean2[i + 1]);
|
|
16049
|
+
const c2 = B64_ALPHABET.indexOf(clean2[i + 2]);
|
|
16050
|
+
const c3 = B64_ALPHABET.indexOf(clean2[i + 3]);
|
|
15681
16051
|
const n = c0 << 18 | c1 << 12 | (c2 & 63) << 6 | c3 & 63;
|
|
15682
16052
|
if (p < outLen) out[p++] = n >> 16 & 255;
|
|
15683
16053
|
if (p < outLen) out[p++] = n >> 8 & 255;
|
|
@@ -19865,12 +20235,17 @@ var ScaleBunFacade = class {
|
|
|
19865
20235
|
/**
|
|
19866
20236
|
* Boot the OTA orchestrator when the init config asks for it.
|
|
19867
20237
|
*
|
|
19868
|
-
* `publicSigningKey`
|
|
19869
|
-
* from configuration alone. The
|
|
19870
|
-
*
|
|
19871
|
-
*
|
|
19872
|
-
*
|
|
19873
|
-
*
|
|
20238
|
+
* `publicSigningKey` / `publicSigningKeys` are honoured here so signature
|
|
20239
|
+
* enforcement is reachable from configuration alone. The list form exists
|
|
20240
|
+
* for key ROTATION: a build pinning [old, new] keeps verifying while the
|
|
20241
|
+
* server moves to the new key, so replacing a key never needs an emergency
|
|
20242
|
+
* store release. Both fields merge (deduplicated) into one pinned set.
|
|
20243
|
+
*
|
|
20244
|
+
* Verification comes from `ota.verifySignature` when supplied, else the
|
|
20245
|
+
* built-in @noble-based verifier (optional peers). Pinning keys with
|
|
20246
|
+
* NEITHER available is fail-CLOSED by design (signature.ts) — an update
|
|
20247
|
+
* that cannot be verified is not installed. We say that out loud rather
|
|
20248
|
+
* than letting the app discover it as a silent no-update condition.
|
|
19874
20249
|
*/
|
|
19875
20250
|
_maybeStartOta(rawConfig) {
|
|
19876
20251
|
const ota = rawConfig?.ota;
|
|
@@ -19879,15 +20254,25 @@ var ScaleBunFacade = class {
|
|
|
19879
20254
|
const {
|
|
19880
20255
|
otaOrchestrator: otaOrchestrator2
|
|
19881
20256
|
} = (init_OtaOrchestrator(), __toCommonJS(OtaOrchestrator_exports));
|
|
19882
|
-
const
|
|
20257
|
+
const rawSingle = ota.publicSigningKey;
|
|
20258
|
+
const rawList = ota.publicSigningKeys;
|
|
20259
|
+
const publicKeys = Array.from(new Set([...typeof rawSingle === "string" && rawSingle ? [rawSingle] : [], ...Array.isArray(rawList) ? rawList : []].filter((k) => typeof k === "string" && k.trim().length > 0)));
|
|
20260
|
+
const signingRequested = publicKeys.length > 0 || Array.isArray(rawList);
|
|
19883
20261
|
const verifier = ota.verifySignature;
|
|
19884
|
-
if (
|
|
19885
|
-
|
|
20262
|
+
if (signingRequested && typeof verifier !== "function") {
|
|
20263
|
+
const {
|
|
20264
|
+
getBuiltinVerifier: getBuiltinVerifier2
|
|
20265
|
+
} = (init_builtinVerifier(), __toCommonJS(builtinVerifier_exports));
|
|
20266
|
+
if (!getBuiltinVerifier2()) {
|
|
20267
|
+
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`.");
|
|
20268
|
+
}
|
|
19886
20269
|
}
|
|
19887
20270
|
otaOrchestrator2.init({
|
|
19888
|
-
...
|
|
20271
|
+
...signingRequested ? {
|
|
19889
20272
|
signature: {
|
|
19890
|
-
|
|
20273
|
+
// Single key stays a plain string — the shape every
|
|
20274
|
+
// existing consumer and test already handles.
|
|
20275
|
+
publicKey: publicKeys.length === 1 ? publicKeys[0] : publicKeys,
|
|
19891
20276
|
verifier
|
|
19892
20277
|
}
|
|
19893
20278
|
} : {},
|
|
@@ -20099,6 +20484,46 @@ var ScaleBunFacade = class {
|
|
|
20099
20484
|
};
|
|
20100
20485
|
}
|
|
20101
20486
|
/** Convenience: emit a Phase 1 purchase event (revenue + currency + transaction_id). */
|
|
20487
|
+
/**
|
|
20488
|
+
* Declare which UI state the user is looking at, so taps are attributed to the surface they
|
|
20489
|
+
* happened on rather than averaged across every variant of the screen.
|
|
20490
|
+
*
|
|
20491
|
+
* ScaleBun.setUiState('filter-sheet', 'open');
|
|
20492
|
+
* ScaleBun.setUiState('checkout-step', 'payment');
|
|
20493
|
+
*
|
|
20494
|
+
* Call it when the state CHANGES — the value is read at tap time, so it only has to be right by
|
|
20495
|
+
* the time the next tap lands. On React Native this is the ONLY source of UI state: unlike the web,
|
|
20496
|
+
* there is no queryable accessibility tree that says a sheet is up, and guessing would produce a
|
|
20497
|
+
* state key that is right on some apps and silently wrong on others.
|
|
20498
|
+
*
|
|
20499
|
+
* Names and values are identifiers, not content: they become query keys in the dashboard, so a
|
|
20500
|
+
* person's name or a cart total does not belong in one. `;`, `:` and `|` are stripped and both
|
|
20501
|
+
* halves are capped at 32 characters.
|
|
20502
|
+
*/
|
|
20503
|
+
setUiState(name, value) {
|
|
20504
|
+
try {
|
|
20505
|
+
const {
|
|
20506
|
+
setUiState: setUiState2
|
|
20507
|
+
} = (init_uiState(), __toCommonJS(uiState_exports));
|
|
20508
|
+
setUiState2(name, value);
|
|
20509
|
+
} catch {
|
|
20510
|
+
}
|
|
20511
|
+
}
|
|
20512
|
+
/**
|
|
20513
|
+
* Stop reporting a UI state dimension — or, with no argument, all of them.
|
|
20514
|
+
*
|
|
20515
|
+
* Clear on screen unmount. A declaration left behind follows the user onto a surface where it means
|
|
20516
|
+
* nothing, and every tap there is filed under a state that was not on screen.
|
|
20517
|
+
*/
|
|
20518
|
+
clearUiState(name) {
|
|
20519
|
+
try {
|
|
20520
|
+
const {
|
|
20521
|
+
clearUiState: clearUiState2
|
|
20522
|
+
} = (init_uiState(), __toCommonJS(uiState_exports));
|
|
20523
|
+
clearUiState2(name);
|
|
20524
|
+
} catch {
|
|
20525
|
+
}
|
|
20526
|
+
}
|
|
20102
20527
|
trackPurchase(input) {
|
|
20103
20528
|
noThrow(() => this._eventTracker?.trackPurchase(input));
|
|
20104
20529
|
}
|
|
@@ -23722,11 +24147,11 @@ var normalizedWheelValue = (value) => value.toLowerCase().replace(/\s+/g, " ").t
|
|
|
23722
24147
|
function resolveWheelOutcome(segments, source) {
|
|
23723
24148
|
const identity = wheelOutcomeIdentity(source);
|
|
23724
24149
|
const index = findWheelOutcomeIndex(segments, identity);
|
|
23725
|
-
const
|
|
24150
|
+
const resolved3 = index >= 0;
|
|
23726
24151
|
return {
|
|
23727
|
-
index:
|
|
23728
|
-
label: identity.resultLabel || (
|
|
23729
|
-
resolved
|
|
24152
|
+
index: resolved3 ? index : 0,
|
|
24153
|
+
label: identity.resultLabel || (resolved3 ? segments[index]?.fullLabel ?? "" : ""),
|
|
24154
|
+
resolved: resolved3
|
|
23730
24155
|
};
|
|
23731
24156
|
}
|
|
23732
24157
|
function SpinWheelInApp(props) {
|
|
@@ -32585,8 +33010,8 @@ function EngagePromptProvider({
|
|
|
32585
33010
|
__DEV__ && logger.debug("[Engage] no eligible in-app message to render");
|
|
32586
33011
|
return false;
|
|
32587
33012
|
}
|
|
32588
|
-
const
|
|
32589
|
-
activeInAppVariantRef.current = message.experiment?.enabled ?
|
|
33013
|
+
const resolved3 = selectInAppVariant(message, getDeviceId());
|
|
33014
|
+
activeInAppVariantRef.current = message.experiment?.enabled ? resolved3 : null;
|
|
32590
33015
|
store.recordImpression(message.id, Date.now(), message.revision);
|
|
32591
33016
|
setActiveInApp(message);
|
|
32592
33017
|
return true;
|
|
@@ -32826,7 +33251,6 @@ function useEngagePrompt() {
|
|
|
32826
33251
|
// lib/module/features/journey/ScaleBunDebugRoot.js
|
|
32827
33252
|
var import_react9 = __toESM(require("react"));
|
|
32828
33253
|
var import_react_native43 = require("react-native");
|
|
32829
|
-
init_automaticEvents();
|
|
32830
33254
|
|
|
32831
33255
|
// lib/module/features/journey/touchTarget.js
|
|
32832
33256
|
var MAX_DEPTH = 12;
|
|
@@ -32898,6 +33322,7 @@ function describeTouchTarget(t) {
|
|
|
32898
33322
|
|
|
32899
33323
|
// lib/module/features/journey/ScaleBunDebugRoot.js
|
|
32900
33324
|
init_device();
|
|
33325
|
+
init_interactionProtocol();
|
|
32901
33326
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
32902
33327
|
var _bootstrap = null;
|
|
32903
33328
|
function getBootstrap() {
|
|
@@ -33087,7 +33512,9 @@ function ScaleBunDebugRoot({
|
|
|
33087
33512
|
}, [navigationRef]);
|
|
33088
33513
|
const lastNativeTouchTsRef = (0, import_react9.useRef)(0);
|
|
33089
33514
|
const nativeTrackingConfirmedRef = (0, import_react9.useRef)(false);
|
|
33090
|
-
const
|
|
33515
|
+
const touchStartRef = (0, import_react9.useRef)(null);
|
|
33516
|
+
const interactionStartsRef = (0, import_react9.useRef)([]);
|
|
33517
|
+
const pendingJsEmitsRef = (0, import_react9.useRef)(/* @__PURE__ */ new Map());
|
|
33091
33518
|
(0, import_react9.useEffect)(() => {
|
|
33092
33519
|
let subscription = null;
|
|
33093
33520
|
try {
|
|
@@ -33098,9 +33525,12 @@ function ScaleBunDebugRoot({
|
|
|
33098
33525
|
try {
|
|
33099
33526
|
lastNativeTouchTsRef.current = Date.now();
|
|
33100
33527
|
nativeTrackingConfirmedRef.current = true;
|
|
33101
|
-
|
|
33102
|
-
|
|
33103
|
-
|
|
33528
|
+
const nativeOccurredAt = typeof event.occurredAt === "number" ? event.occurredAt : (typeof event.timestamp === "number" ? event.timestamp : Date.now()) - (typeof event.durationMs === "number" ? event.durationMs : 0);
|
|
33529
|
+
const start = nearestInteractionStart(interactionStartsRef.current, nativeOccurredAt);
|
|
33530
|
+
if (start) {
|
|
33531
|
+
const pending4 = pendingJsEmitsRef.current.get(start.interactionId);
|
|
33532
|
+
if (pending4 !== void 0) clearTimeout(pending4);
|
|
33533
|
+
pendingJsEmitsRef.current.delete(start.interactionId);
|
|
33104
33534
|
}
|
|
33105
33535
|
const {
|
|
33106
33536
|
SessionManager: SessionManager2
|
|
@@ -33119,6 +33549,15 @@ function ScaleBunDebugRoot({
|
|
|
33119
33549
|
} catch {
|
|
33120
33550
|
}
|
|
33121
33551
|
sm.onGestureDetected(event.gestureType || "tap", {
|
|
33552
|
+
interactionId: event.interactionId || start?.interactionId || generateInteractionId(),
|
|
33553
|
+
interactionProtocol: event.interactionProtocol || INTERACTION_PROTOCOL_VERSION,
|
|
33554
|
+
occurredAt: nativeOccurredAt,
|
|
33555
|
+
ui: start?.ui,
|
|
33556
|
+
stateStatus: start?.stateStatus ?? "not_captured",
|
|
33557
|
+
target: start?.target,
|
|
33558
|
+
targetId: start?.targetId,
|
|
33559
|
+
screenName: start?.screenName,
|
|
33560
|
+
emitAutomaticAnalytics: start?.emitAutomaticAnalytics ?? captureAutomaticInteractions,
|
|
33122
33561
|
x: event.rawX,
|
|
33123
33562
|
y: event.rawY,
|
|
33124
33563
|
// Use the actual native end coords when present, not the down
|
|
@@ -33151,9 +33590,10 @@ function ScaleBunDebugRoot({
|
|
|
33151
33590
|
subscription?.remove();
|
|
33152
33591
|
} catch {
|
|
33153
33592
|
}
|
|
33593
|
+
for (const timer of pendingJsEmitsRef.current.values()) clearTimeout(timer);
|
|
33594
|
+
pendingJsEmitsRef.current.clear();
|
|
33154
33595
|
};
|
|
33155
|
-
}, []);
|
|
33156
|
-
const touchStartRef = (0, import_react9.useRef)(null);
|
|
33596
|
+
}, [captureAutomaticInteractions]);
|
|
33157
33597
|
const rootRectRef = (0, import_react9.useRef)(null);
|
|
33158
33598
|
const measureRoot = () => {
|
|
33159
33599
|
try {
|
|
@@ -33185,11 +33625,39 @@ function ScaleBunDebugRoot({
|
|
|
33185
33625
|
const handleTouchStart = (e) => {
|
|
33186
33626
|
try {
|
|
33187
33627
|
const touch = e.nativeEvent;
|
|
33188
|
-
|
|
33628
|
+
const target = resolveTouchTarget(e);
|
|
33629
|
+
let ui;
|
|
33630
|
+
let screenName;
|
|
33631
|
+
try {
|
|
33632
|
+
const {
|
|
33633
|
+
uiStateSignature: uiStateSignature2
|
|
33634
|
+
} = (init_uiState(), __toCommonJS(uiState_exports));
|
|
33635
|
+
ui = uiStateSignature2();
|
|
33636
|
+
} catch {
|
|
33637
|
+
}
|
|
33638
|
+
try {
|
|
33639
|
+
const {
|
|
33640
|
+
AutoScreenDetector: AutoScreenDetector2
|
|
33641
|
+
} = (init_AutoScreenDetector(), __toCommonJS(AutoScreenDetector_exports));
|
|
33642
|
+
screenName = AutoScreenDetector2.getInstance().getCurrentScreen() || void 0;
|
|
33643
|
+
} catch {
|
|
33644
|
+
}
|
|
33645
|
+
const start = {
|
|
33646
|
+
interactionId: generateInteractionId(),
|
|
33647
|
+
occurredAt: Date.now(),
|
|
33189
33648
|
x: touch.pageX,
|
|
33190
33649
|
y: touch.pageY,
|
|
33191
|
-
|
|
33650
|
+
locationX: touch.locationX,
|
|
33651
|
+
locationY: touch.locationY,
|
|
33652
|
+
target: describeTouchTarget(target),
|
|
33653
|
+
targetId: target?.testID,
|
|
33654
|
+
screenName,
|
|
33655
|
+
ui,
|
|
33656
|
+
stateStatus: ui ? "captured_nonempty" : "not_instrumented",
|
|
33657
|
+
emitAutomaticAnalytics: captureAutomaticInteractions
|
|
33192
33658
|
};
|
|
33659
|
+
touchStartRef.current = start;
|
|
33660
|
+
interactionStartsRef.current = interactionStartsRef.current.filter((candidate) => start.occurredAt - candidate.occurredAt < 5e3).concat(start).slice(-8);
|
|
33193
33661
|
} catch {
|
|
33194
33662
|
}
|
|
33195
33663
|
};
|
|
@@ -33220,21 +33688,29 @@ function ScaleBunDebugRoot({
|
|
|
33220
33688
|
}
|
|
33221
33689
|
measureRoot();
|
|
33222
33690
|
const rootRect = rootRectRef.current;
|
|
33223
|
-
const tapped = describeTouchTarget(resolveTouchTarget(e));
|
|
33691
|
+
const tapped = start?.target ?? describeTouchTarget(resolveTouchTarget(e));
|
|
33224
33692
|
const gestureDetails = {
|
|
33225
33693
|
target: tapped,
|
|
33226
|
-
|
|
33227
|
-
|
|
33228
|
-
|
|
33229
|
-
|
|
33230
|
-
|
|
33231
|
-
|
|
33694
|
+
interactionId: start?.interactionId ?? generateInteractionId(),
|
|
33695
|
+
interactionProtocol: INTERACTION_PROTOCOL_VERSION,
|
|
33696
|
+
occurredAt: start?.occurredAt ?? Date.now(),
|
|
33697
|
+
ui: start?.ui,
|
|
33698
|
+
stateStatus: start?.stateStatus ?? "not_captured",
|
|
33699
|
+
targetId: start?.targetId,
|
|
33700
|
+
screenName: start?.screenName,
|
|
33701
|
+
emitAutomaticAnalytics: start?.emitAutomaticAnalytics ?? captureAutomaticInteractions,
|
|
33702
|
+
x: start?.x ?? touch.pageX,
|
|
33703
|
+
y: start?.y ?? touch.pageY,
|
|
33704
|
+
pageX: start?.x ?? touch.pageX,
|
|
33705
|
+
pageY: start?.y ?? touch.pageY,
|
|
33706
|
+
locationX: start?.locationX ?? touch.locationX,
|
|
33707
|
+
locationY: start?.locationY ?? touch.locationY,
|
|
33232
33708
|
viewportWidth: vpW > 0 ? vpW : void 0,
|
|
33233
33709
|
viewportHeight: vpH > 0 ? vpH : void 0
|
|
33234
33710
|
};
|
|
33235
33711
|
if (rootRect && rootRect.w > 0 && rootRect.h > 0) {
|
|
33236
|
-
const nx = Math.max(0, Math.min(1, touch.pageX / rootRect.w));
|
|
33237
|
-
const ny = Math.max(0, Math.min(1, touch.pageY / rootRect.h));
|
|
33712
|
+
const nx = Math.max(0, Math.min(1, (start?.x ?? touch.pageX) / rootRect.w));
|
|
33713
|
+
const ny = Math.max(0, Math.min(1, (start?.y ?? touch.pageY) / rootRect.h));
|
|
33238
33714
|
gestureDetails.normalizedX = nx;
|
|
33239
33715
|
gestureDetails.normalizedY = ny;
|
|
33240
33716
|
gestureDetails.normalizedPrecomputed = true;
|
|
@@ -33265,7 +33741,7 @@ function ScaleBunDebugRoot({
|
|
|
33265
33741
|
const dx = touch.pageX - start.x;
|
|
33266
33742
|
const dy = touch.pageY - start.y;
|
|
33267
33743
|
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
33268
|
-
const duration = Date.now() - start.
|
|
33744
|
+
const duration = Date.now() - start.occurredAt;
|
|
33269
33745
|
gestureDetails.duration = duration;
|
|
33270
33746
|
if (dist >= 15) {
|
|
33271
33747
|
gestureDetails.endX = touch.pageX;
|
|
@@ -33283,47 +33759,16 @@ function ScaleBunDebugRoot({
|
|
|
33283
33759
|
gestureType = "long_press";
|
|
33284
33760
|
}
|
|
33285
33761
|
}
|
|
33286
|
-
const targetInfo = _extractTarget(e);
|
|
33287
|
-
gestureDetails.target = targetInfo?.testId || targetInfo?.accessibilityLabel;
|
|
33288
|
-
if (captureAutomaticInteractions) {
|
|
33289
|
-
let screen;
|
|
33290
|
-
try {
|
|
33291
|
-
const {
|
|
33292
|
-
AutoScreenDetector: AutoScreenDetector2
|
|
33293
|
-
} = (init_AutoScreenDetector(), __toCommonJS(AutoScreenDetector_exports));
|
|
33294
|
-
screen = AutoScreenDetector2.getInstance().getCurrentScreen() || void 0;
|
|
33295
|
-
} catch {
|
|
33296
|
-
}
|
|
33297
|
-
emitAutomaticEvent("element_interacted", {
|
|
33298
|
-
gesture_type: gestureType,
|
|
33299
|
-
screen_name: screen,
|
|
33300
|
-
// testID/nativeID is an author-controlled stable identifier.
|
|
33301
|
-
// Accessibility labels and rendered text are deliberately omitted.
|
|
33302
|
-
target_id: targetInfo?.testId,
|
|
33303
|
-
normalized_x: gestureDetails.normalizedX,
|
|
33304
|
-
normalized_y: gestureDetails.normalizedY,
|
|
33305
|
-
direction: gestureDetails.direction,
|
|
33306
|
-
duration_ms: gestureDetails.duration
|
|
33307
|
-
});
|
|
33308
|
-
}
|
|
33309
33762
|
if (gestureType === "tap") {
|
|
33310
33763
|
try {
|
|
33311
33764
|
const {
|
|
33312
33765
|
gestureTriggerDetector: gestureTriggerDetector2
|
|
33313
33766
|
} = (init_gestureTriggerDetector(), __toCommonJS(gestureTriggerDetector_exports));
|
|
33314
|
-
let screen;
|
|
33315
|
-
try {
|
|
33316
|
-
const {
|
|
33317
|
-
AutoScreenDetector: AutoScreenDetector2
|
|
33318
|
-
} = (init_AutoScreenDetector(), __toCommonJS(AutoScreenDetector_exports));
|
|
33319
|
-
screen = AutoScreenDetector2.getInstance().getCurrentScreen() || void 0;
|
|
33320
|
-
} catch {
|
|
33321
|
-
}
|
|
33322
33767
|
gestureTriggerDetector2.recordTap({
|
|
33323
|
-
x: touch.pageX,
|
|
33324
|
-
y: touch.pageY,
|
|
33325
|
-
hasTarget: !!(
|
|
33326
|
-
screen
|
|
33768
|
+
x: start?.x ?? touch.pageX,
|
|
33769
|
+
y: start?.y ?? touch.pageY,
|
|
33770
|
+
hasTarget: !!(start?.targetId || start?.target),
|
|
33771
|
+
screen: start?.screenName
|
|
33327
33772
|
});
|
|
33328
33773
|
} catch {
|
|
33329
33774
|
}
|
|
@@ -33336,11 +33781,9 @@ function ScaleBunDebugRoot({
|
|
|
33336
33781
|
const capturedDetails = {
|
|
33337
33782
|
...gestureDetails
|
|
33338
33783
|
};
|
|
33339
|
-
|
|
33340
|
-
|
|
33341
|
-
|
|
33342
|
-
pendingJsEmitRef.current = setTimeout(() => {
|
|
33343
|
-
pendingJsEmitRef.current = null;
|
|
33784
|
+
const interactionId = capturedDetails.interactionId;
|
|
33785
|
+
const timer = setTimeout(() => {
|
|
33786
|
+
pendingJsEmitsRef.current.delete(interactionId);
|
|
33344
33787
|
if (nativeTrackingConfirmedRef.current) return;
|
|
33345
33788
|
try {
|
|
33346
33789
|
const {
|
|
@@ -33357,6 +33800,7 @@ function ScaleBunDebugRoot({
|
|
|
33357
33800
|
} catch {
|
|
33358
33801
|
}
|
|
33359
33802
|
}, 600);
|
|
33803
|
+
pendingJsEmitsRef.current.set(interactionId, timer);
|
|
33360
33804
|
}
|
|
33361
33805
|
touchStartRef.current = null;
|
|
33362
33806
|
} catch {
|
|
@@ -33432,20 +33876,6 @@ function useScaleBunScreen(name) {
|
|
|
33432
33876
|
}
|
|
33433
33877
|
}, [name, manager]);
|
|
33434
33878
|
}
|
|
33435
|
-
function _extractTarget(e) {
|
|
33436
|
-
try {
|
|
33437
|
-
const target = e?.target;
|
|
33438
|
-
if (!target) return void 0;
|
|
33439
|
-
const props = target._internalFiberInstanceHandleDEV?.memoizedProps ?? {};
|
|
33440
|
-
return {
|
|
33441
|
-
testId: props.testID || props.nativeID || void 0,
|
|
33442
|
-
accessibilityLabel: props.accessibilityLabel || void 0,
|
|
33443
|
-
text: void 0
|
|
33444
|
-
};
|
|
33445
|
-
} catch {
|
|
33446
|
-
return void 0;
|
|
33447
|
-
}
|
|
33448
|
-
}
|
|
33449
33879
|
var styles4 = {
|
|
33450
33880
|
root: {
|
|
33451
33881
|
flex: 1
|