@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.slim.js
CHANGED
|
@@ -92,9 +92,9 @@ var init_internalLogger = __esm({
|
|
|
92
92
|
}
|
|
93
93
|
/** Set level from the public string name used in config (e.g. 'warn'). */
|
|
94
94
|
setLevelByName(name) {
|
|
95
|
-
const
|
|
96
|
-
if (
|
|
97
|
-
this.level =
|
|
95
|
+
const resolved3 = LEVEL_BY_NAME[name];
|
|
96
|
+
if (resolved3 !== void 0) {
|
|
97
|
+
this.level = resolved3;
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
/** Current numeric level — exposed for callers that branch on dev verbosity. */
|
|
@@ -600,7 +600,7 @@ var SDK_VERSION;
|
|
|
600
600
|
var init_version = __esm({
|
|
601
601
|
"lib/module/core/constants/version.js"() {
|
|
602
602
|
"use strict";
|
|
603
|
-
SDK_VERSION = "1.
|
|
603
|
+
SDK_VERSION = "1.11.0";
|
|
604
604
|
}
|
|
605
605
|
});
|
|
606
606
|
|
|
@@ -2459,22 +2459,23 @@ var init_JourneyEventPipeline = __esm({
|
|
|
2459
2459
|
emit(type, opts) {
|
|
2460
2460
|
try {
|
|
2461
2461
|
const key = `${type}:${opts?.subtype ?? ""}`;
|
|
2462
|
-
const
|
|
2463
|
-
|
|
2462
|
+
const receivedAt = Date.now();
|
|
2463
|
+
const occurredAt = opts?.timestamp ?? receivedAt;
|
|
2464
|
+
if (key === this.lastEventKey && receivedAt - this.lastEventTs < this.config.dedupeWindowMs) {
|
|
2464
2465
|
const incomingConfidence = opts?.payload?.confidence;
|
|
2465
2466
|
if (incomingConfidence === "high" && this.lastEventConfidence !== "high") {
|
|
2466
|
-
this._replaceLastEvent(key,
|
|
2467
|
+
this._replaceLastEvent(key, receivedAt, opts);
|
|
2467
2468
|
}
|
|
2468
2469
|
return null;
|
|
2469
2470
|
}
|
|
2470
2471
|
this.lastEventKey = key;
|
|
2471
|
-
this.lastEventTs =
|
|
2472
|
+
this.lastEventTs = receivedAt;
|
|
2472
2473
|
this.lastEventConfidence = opts?.payload?.confidence ?? null;
|
|
2473
2474
|
const event = {
|
|
2474
2475
|
eventId: generateEventId2(),
|
|
2475
2476
|
sessionId: this.sessionId,
|
|
2476
2477
|
journeyId: opts?.journeyId,
|
|
2477
|
-
ts:
|
|
2478
|
+
ts: occurredAt,
|
|
2478
2479
|
type,
|
|
2479
2480
|
subtype: opts?.subtype,
|
|
2480
2481
|
severity: opts?.severity ?? inferSeverity(type),
|
|
@@ -3574,6 +3575,99 @@ var init_calibrationContext = __esm({
|
|
|
3574
3575
|
}
|
|
3575
3576
|
});
|
|
3576
3577
|
|
|
3578
|
+
// lib/module/analytics/automaticEvents.js
|
|
3579
|
+
function compactProperties(properties) {
|
|
3580
|
+
const out = {
|
|
3581
|
+
capture_source: "automatic"
|
|
3582
|
+
};
|
|
3583
|
+
for (const [key, value] of Object.entries(properties ?? {})) {
|
|
3584
|
+
if (value !== void 0) out[key] = value;
|
|
3585
|
+
}
|
|
3586
|
+
return out;
|
|
3587
|
+
}
|
|
3588
|
+
function emitAutomaticEvent(name, properties, timestamp) {
|
|
3589
|
+
const event = {
|
|
3590
|
+
name,
|
|
3591
|
+
properties: compactProperties(properties),
|
|
3592
|
+
timestamp
|
|
3593
|
+
};
|
|
3594
|
+
if (listeners.size === 0) {
|
|
3595
|
+
pending2.push(event);
|
|
3596
|
+
if (pending2.length > MAX_PENDING) pending2.shift();
|
|
3597
|
+
return;
|
|
3598
|
+
}
|
|
3599
|
+
for (const listener of listeners) {
|
|
3600
|
+
try {
|
|
3601
|
+
listener(event);
|
|
3602
|
+
} catch {
|
|
3603
|
+
}
|
|
3604
|
+
}
|
|
3605
|
+
}
|
|
3606
|
+
function subscribeAutomaticEvents(listener) {
|
|
3607
|
+
listeners.add(listener);
|
|
3608
|
+
if (pending2.length > 0) {
|
|
3609
|
+
const buffered = pending2.splice(0, pending2.length);
|
|
3610
|
+
for (const event of buffered) {
|
|
3611
|
+
try {
|
|
3612
|
+
listener(event);
|
|
3613
|
+
} catch {
|
|
3614
|
+
}
|
|
3615
|
+
}
|
|
3616
|
+
}
|
|
3617
|
+
return () => {
|
|
3618
|
+
listeners.delete(listener);
|
|
3619
|
+
};
|
|
3620
|
+
}
|
|
3621
|
+
var listeners, pending2, MAX_PENDING;
|
|
3622
|
+
var init_automaticEvents = __esm({
|
|
3623
|
+
"lib/module/analytics/automaticEvents.js"() {
|
|
3624
|
+
"use strict";
|
|
3625
|
+
listeners = /* @__PURE__ */ new Set();
|
|
3626
|
+
pending2 = [];
|
|
3627
|
+
MAX_PENDING = 50;
|
|
3628
|
+
}
|
|
3629
|
+
});
|
|
3630
|
+
|
|
3631
|
+
// lib/module/features/journey/interactionProtocol.js
|
|
3632
|
+
function generateInteractionId() {
|
|
3633
|
+
return `ixj-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
3634
|
+
}
|
|
3635
|
+
function nearestInteractionStart(starts, occurredAt, toleranceMs = 1500) {
|
|
3636
|
+
let best;
|
|
3637
|
+
let bestDelta = toleranceMs + 1;
|
|
3638
|
+
for (const start of starts) {
|
|
3639
|
+
const delta = Math.abs(start.occurredAt - occurredAt);
|
|
3640
|
+
if (delta < bestDelta) {
|
|
3641
|
+
best = start;
|
|
3642
|
+
bestDelta = delta;
|
|
3643
|
+
}
|
|
3644
|
+
}
|
|
3645
|
+
return bestDelta <= toleranceMs ? best : void 0;
|
|
3646
|
+
}
|
|
3647
|
+
function automaticInteractionProperties(payload, screenName, canonicalMirror) {
|
|
3648
|
+
return {
|
|
3649
|
+
gesture_type: payload.gestureType,
|
|
3650
|
+
screen_name: screenName,
|
|
3651
|
+
interaction_id: payload.interaction_id,
|
|
3652
|
+
interaction_protocol: payload.interaction_protocol,
|
|
3653
|
+
state_status: payload.state_status,
|
|
3654
|
+
ui: payload.ui,
|
|
3655
|
+
target_id: payload.target_id,
|
|
3656
|
+
normalized_x: payload.normalizedX,
|
|
3657
|
+
normalized_y: payload.normalizedY,
|
|
3658
|
+
direction: payload.direction,
|
|
3659
|
+
duration_ms: payload.durationMs,
|
|
3660
|
+
canonical_mirror: canonicalMirror || void 0
|
|
3661
|
+
};
|
|
3662
|
+
}
|
|
3663
|
+
var INTERACTION_PROTOCOL_VERSION;
|
|
3664
|
+
var init_interactionProtocol = __esm({
|
|
3665
|
+
"lib/module/features/journey/interactionProtocol.js"() {
|
|
3666
|
+
"use strict";
|
|
3667
|
+
INTERACTION_PROTOCOL_VERSION = 1;
|
|
3668
|
+
}
|
|
3669
|
+
});
|
|
3670
|
+
|
|
3577
3671
|
// lib/module/features/session/SessionManager.js
|
|
3578
3672
|
var SessionManager_exports = {};
|
|
3579
3673
|
__export(SessionManager_exports, {
|
|
@@ -3582,7 +3676,7 @@ __export(SessionManager_exports, {
|
|
|
3582
3676
|
function generateSessionId2() {
|
|
3583
3677
|
return `ses-${Date.now().toString(36)}-${Math.random().toString(36).substring(2, 8)}`;
|
|
3584
3678
|
}
|
|
3585
|
-
var
|
|
3679
|
+
var _instance2, SessionManager;
|
|
3586
3680
|
var init_SessionManager = __esm({
|
|
3587
3681
|
"lib/module/features/session/SessionManager.js"() {
|
|
3588
3682
|
"use strict";
|
|
@@ -3599,7 +3693,8 @@ var init_SessionManager = __esm({
|
|
|
3599
3693
|
init_redaction();
|
|
3600
3694
|
init_calibrationContext();
|
|
3601
3695
|
init_device();
|
|
3602
|
-
|
|
3696
|
+
init_automaticEvents();
|
|
3697
|
+
init_interactionProtocol();
|
|
3603
3698
|
_instance2 = null;
|
|
3604
3699
|
SessionManager = class _SessionManager {
|
|
3605
3700
|
desktopTransport = null;
|
|
@@ -3610,15 +3705,6 @@ var init_SessionManager = __esm({
|
|
|
3610
3705
|
* lane even when no replay recording is active. Additive, opt-in (default off).
|
|
3611
3706
|
*/
|
|
3612
3707
|
_captureInteractionHeatmap = false;
|
|
3613
|
-
/**
|
|
3614
|
-
* Sampling cap for the analytics-lane heatmap emission. Now that capture is
|
|
3615
|
-
* ON by default, an unbounded one-event-per-gesture stream could materially
|
|
3616
|
-
* inflate ingest volume. We cap emitted interactions per analytics-session
|
|
3617
|
-
* window (finalize-scoped per foreground): the first N gestures define the
|
|
3618
|
-
* hotspot shape; the long tail is dropped. Resets when the window changes.
|
|
3619
|
-
*/
|
|
3620
|
-
_heatmapWindowSessionId = null;
|
|
3621
|
-
_heatmapWindowCount = 0;
|
|
3622
3708
|
session = null;
|
|
3623
3709
|
active = false;
|
|
3624
3710
|
timeoutTimer = null;
|
|
@@ -4099,18 +4185,11 @@ var init_SessionManager = __esm({
|
|
|
4099
4185
|
* injected sensitive keys (e.g. a label) are stripped before transport.
|
|
4100
4186
|
* - Never throws.
|
|
4101
4187
|
*/
|
|
4102
|
-
_emitInteractionToAnalytics(gestureType, payload) {
|
|
4103
|
-
if (!this._captureInteractionHeatmap) return;
|
|
4104
|
-
if (this.active) return;
|
|
4188
|
+
_emitInteractionToAnalytics(gestureType, payload, occurredAt, screenName) {
|
|
4189
|
+
if (!this._captureInteractionHeatmap) return false;
|
|
4190
|
+
if (this.active) return false;
|
|
4105
4191
|
const adapter = this._backendTransport;
|
|
4106
|
-
if (!adapter) return;
|
|
4107
|
-
const windowId = adapter.analyticsSessionId ?? "";
|
|
4108
|
-
if (windowId !== this._heatmapWindowSessionId) {
|
|
4109
|
-
this._heatmapWindowSessionId = windowId;
|
|
4110
|
-
this._heatmapWindowCount = 0;
|
|
4111
|
-
}
|
|
4112
|
-
if (this._heatmapWindowCount >= HEATMAP_MAX_INTERACTIONS_PER_WINDOW) return;
|
|
4113
|
-
this._heatmapWindowCount++;
|
|
4192
|
+
if (!adapter) return false;
|
|
4114
4193
|
try {
|
|
4115
4194
|
this._normalizeInteractionPayload(payload);
|
|
4116
4195
|
const safePayload = redactBody(payload);
|
|
@@ -4118,16 +4197,18 @@ var init_SessionManager = __esm({
|
|
|
4118
4197
|
eventId: generateEventId(),
|
|
4119
4198
|
// sessionId is (re)stamped by the analytics lane at flush time.
|
|
4120
4199
|
sessionId: adapter.analyticsSessionId ?? "",
|
|
4121
|
-
ts:
|
|
4200
|
+
ts: occurredAt,
|
|
4122
4201
|
type: "USER_ACTION",
|
|
4123
4202
|
subtype: `gesture:${gestureType}`,
|
|
4124
|
-
screen: this._lastKnownScreen ?? void 0,
|
|
4203
|
+
screen: screenName ?? this._lastKnownScreen ?? void 0,
|
|
4125
4204
|
payload: safePayload,
|
|
4126
4205
|
source: "user"
|
|
4127
4206
|
};
|
|
4128
4207
|
adapter.trackEvent(event);
|
|
4208
|
+
return true;
|
|
4129
4209
|
} catch (err) {
|
|
4130
4210
|
logger.error("[SessionManager] heatmap analytics emit failed:", err);
|
|
4211
|
+
return false;
|
|
4131
4212
|
}
|
|
4132
4213
|
}
|
|
4133
4214
|
/**
|
|
@@ -4293,13 +4374,14 @@ var init_SessionManager = __esm({
|
|
|
4293
4374
|
* Notify of a user interaction. Called by ScaleBunDebugRoot touch handlers.
|
|
4294
4375
|
* Also triggers frame capture for desktop-initiated recordings.
|
|
4295
4376
|
*/
|
|
4296
|
-
onUserAction(subtype, payload) {
|
|
4377
|
+
onUserAction(subtype, payload, context) {
|
|
4297
4378
|
if (!this.active) return;
|
|
4298
4379
|
this.emitEvent("USER_ACTION", {
|
|
4299
4380
|
subtype,
|
|
4300
|
-
screen: this._lastKnownScreen ?? void 0,
|
|
4381
|
+
screen: context?.screen ?? this._lastKnownScreen ?? void 0,
|
|
4301
4382
|
payload,
|
|
4302
|
-
source: "user"
|
|
4383
|
+
source: "user",
|
|
4384
|
+
timestamp: context?.timestamp
|
|
4303
4385
|
});
|
|
4304
4386
|
}
|
|
4305
4387
|
/**
|
|
@@ -4307,7 +4389,8 @@ var init_SessionManager = __esm({
|
|
|
4307
4389
|
* Emits a USER_ACTION event with gesture-specific subtype and payload.
|
|
4308
4390
|
*/
|
|
4309
4391
|
onGestureDetected(gestureType, details) {
|
|
4310
|
-
const
|
|
4392
|
+
const suppliedInteractionId = details?.interactionId;
|
|
4393
|
+
const dedupSig = suppliedInteractionId ? `id:${suppliedInteractionId}` : `${gestureType}|${String(details?.x)}|${String(details?.y)}|${String(details?.endX)}|${String(details?.endY)}`;
|
|
4311
4394
|
const nowTs = Date.now();
|
|
4312
4395
|
if (dedupSig === this._lastGestureSig && nowTs - this._lastGestureTs <= _SessionManager.GESTURE_DEDUP_WINDOW_MS) {
|
|
4313
4396
|
this._lastGestureTs = nowTs;
|
|
@@ -4315,7 +4398,15 @@ var init_SessionManager = __esm({
|
|
|
4315
4398
|
}
|
|
4316
4399
|
this._lastGestureSig = dedupSig;
|
|
4317
4400
|
this._lastGestureTs = nowTs;
|
|
4401
|
+
const interactionId = suppliedInteractionId ?? generateInteractionId();
|
|
4402
|
+
const occurredAt = details?.occurredAt ?? Date.now();
|
|
4403
|
+
const stateStatus = details?.stateStatus ?? (details?.ui ? "captured_nonempty" : "not_captured");
|
|
4318
4404
|
const payload = {
|
|
4405
|
+
interaction_id: interactionId,
|
|
4406
|
+
interaction_protocol: details?.interactionProtocol ?? INTERACTION_PROTOCOL_VERSION,
|
|
4407
|
+
state_status: stateStatus,
|
|
4408
|
+
ui: details?.ui,
|
|
4409
|
+
target_id: details?.targetId,
|
|
4319
4410
|
gestureType,
|
|
4320
4411
|
x: details?.x,
|
|
4321
4412
|
y: details?.y,
|
|
@@ -4353,12 +4444,23 @@ var init_SessionManager = __esm({
|
|
|
4353
4444
|
screenHeight: details?.screenHeight,
|
|
4354
4445
|
platform: details?.platform
|
|
4355
4446
|
};
|
|
4356
|
-
this._emitInteractionToAnalytics(gestureType, {
|
|
4447
|
+
const analyticsReplayCarrier = this._emitInteractionToAnalytics(gestureType, {
|
|
4357
4448
|
...payload
|
|
4358
|
-
});
|
|
4449
|
+
}, occurredAt, details?.screenName);
|
|
4450
|
+
const replayCarrier = !!this._backendTransport && (this.active || analyticsReplayCarrier);
|
|
4451
|
+
if (details?.emitAutomaticAnalytics) {
|
|
4452
|
+
try {
|
|
4453
|
+
const safePayload = redactBody(payload);
|
|
4454
|
+
emitAutomaticEvent("element_interacted", automaticInteractionProperties(safePayload, details?.screenName ?? this._lastKnownScreen ?? void 0, replayCarrier), occurredAt);
|
|
4455
|
+
} catch {
|
|
4456
|
+
}
|
|
4457
|
+
}
|
|
4359
4458
|
if (!this.active) return;
|
|
4360
4459
|
const target = typeof details?.target === "string" ? details.target.trim() : "";
|
|
4361
|
-
this.onUserAction(target ? `gesture:${gestureType} \xB7 ${target}` : `gesture:${gestureType}`, payload
|
|
4460
|
+
this.onUserAction(target ? `gesture:${gestureType} \xB7 ${target}` : `gesture:${gestureType}`, payload, {
|
|
4461
|
+
screen: details?.screenName,
|
|
4462
|
+
timestamp: occurredAt
|
|
4463
|
+
});
|
|
4362
4464
|
}
|
|
4363
4465
|
/**
|
|
4364
4466
|
* Manual frame capture — triggered by Desktop "Capture Step" button.
|
|
@@ -6229,58 +6331,6 @@ var init_batching = __esm({
|
|
|
6229
6331
|
}
|
|
6230
6332
|
});
|
|
6231
6333
|
|
|
6232
|
-
// lib/module/analytics/automaticEvents.js
|
|
6233
|
-
function compactProperties(properties) {
|
|
6234
|
-
const out = {
|
|
6235
|
-
capture_source: "automatic"
|
|
6236
|
-
};
|
|
6237
|
-
for (const [key, value] of Object.entries(properties ?? {})) {
|
|
6238
|
-
if (value !== void 0) out[key] = value;
|
|
6239
|
-
}
|
|
6240
|
-
return out;
|
|
6241
|
-
}
|
|
6242
|
-
function emitAutomaticEvent(name, properties) {
|
|
6243
|
-
const event = {
|
|
6244
|
-
name,
|
|
6245
|
-
properties: compactProperties(properties)
|
|
6246
|
-
};
|
|
6247
|
-
if (listeners.size === 0) {
|
|
6248
|
-
pending3.push(event);
|
|
6249
|
-
if (pending3.length > MAX_PENDING2) pending3.shift();
|
|
6250
|
-
return;
|
|
6251
|
-
}
|
|
6252
|
-
for (const listener of listeners) {
|
|
6253
|
-
try {
|
|
6254
|
-
listener(event);
|
|
6255
|
-
} catch {
|
|
6256
|
-
}
|
|
6257
|
-
}
|
|
6258
|
-
}
|
|
6259
|
-
function subscribeAutomaticEvents(listener) {
|
|
6260
|
-
listeners.add(listener);
|
|
6261
|
-
if (pending3.length > 0) {
|
|
6262
|
-
const buffered = pending3.splice(0, pending3.length);
|
|
6263
|
-
for (const event of buffered) {
|
|
6264
|
-
try {
|
|
6265
|
-
listener(event);
|
|
6266
|
-
} catch {
|
|
6267
|
-
}
|
|
6268
|
-
}
|
|
6269
|
-
}
|
|
6270
|
-
return () => {
|
|
6271
|
-
listeners.delete(listener);
|
|
6272
|
-
};
|
|
6273
|
-
}
|
|
6274
|
-
var listeners, pending3, MAX_PENDING2;
|
|
6275
|
-
var init_automaticEvents = __esm({
|
|
6276
|
-
"lib/module/analytics/automaticEvents.js"() {
|
|
6277
|
-
"use strict";
|
|
6278
|
-
listeners = /* @__PURE__ */ new Set();
|
|
6279
|
-
pending3 = [];
|
|
6280
|
-
MAX_PENDING2 = 50;
|
|
6281
|
-
}
|
|
6282
|
-
});
|
|
6283
|
-
|
|
6284
6334
|
// lib/module/analytics/EventTracker.js
|
|
6285
6335
|
function uuid() {
|
|
6286
6336
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
@@ -6349,7 +6399,7 @@ var init_EventTracker = __esm({
|
|
|
6349
6399
|
this.started = true;
|
|
6350
6400
|
if (this.cfg.automaticEventTracking) {
|
|
6351
6401
|
this.automaticEventsUnsubscribe = subscribeAutomaticEvents((event) => {
|
|
6352
|
-
this.track(event.name, event.properties);
|
|
6402
|
+
this.track(event.name, event.properties, event.timestamp);
|
|
6353
6403
|
});
|
|
6354
6404
|
}
|
|
6355
6405
|
if (this.cfg.autoLifecycleEvents) {
|
|
@@ -6453,9 +6503,9 @@ var init_EventTracker = __esm({
|
|
|
6453
6503
|
}
|
|
6454
6504
|
}
|
|
6455
6505
|
// ─── tracking ────────────────────────────────────────────────────────────
|
|
6456
|
-
track(eventName, properties) {
|
|
6506
|
+
track(eventName, properties, timestamp) {
|
|
6457
6507
|
try {
|
|
6458
|
-
this.enqueue(this.buildEnvelope(eventName, properties));
|
|
6508
|
+
this.enqueue(this.buildEnvelope(eventName, properties, timestamp));
|
|
6459
6509
|
try {
|
|
6460
6510
|
this.cfg.onEvent?.(eventName);
|
|
6461
6511
|
} catch {
|
|
@@ -6559,7 +6609,7 @@ var init_EventTracker = __esm({
|
|
|
6559
6609
|
};
|
|
6560
6610
|
}
|
|
6561
6611
|
// ─── internals ─────────────────────────────────────────────────────────────
|
|
6562
|
-
buildEnvelope(eventName, properties) {
|
|
6612
|
+
buildEnvelope(eventName, properties, timestamp) {
|
|
6563
6613
|
const ctx = this.cfg.context ?? {};
|
|
6564
6614
|
let canonicalSessionId;
|
|
6565
6615
|
try {
|
|
@@ -6570,7 +6620,7 @@ var init_EventTracker = __esm({
|
|
|
6570
6620
|
const env = {
|
|
6571
6621
|
event_id: uuid(),
|
|
6572
6622
|
event_name: eventName,
|
|
6573
|
-
event_time: Date.now(),
|
|
6623
|
+
event_time: timestamp ?? Date.now(),
|
|
6574
6624
|
app_id: this.cfg.appId,
|
|
6575
6625
|
platform: this.cfg.platform ?? resolveEventPlatform(),
|
|
6576
6626
|
installation_id: this.installationId,
|
|
@@ -6907,10 +6957,249 @@ var init_deviceAttributes = __esm({
|
|
|
6907
6957
|
}
|
|
6908
6958
|
});
|
|
6909
6959
|
|
|
6960
|
+
// lib/module/features/ota/crypto/loadEd25519.js
|
|
6961
|
+
function loadEd25519() {
|
|
6962
|
+
try {
|
|
6963
|
+
const mod = require("@noble/ed25519");
|
|
6964
|
+
const ed = mod?.default ?? mod;
|
|
6965
|
+
if (typeof ed?.verifyAsync === "function" || typeof ed?.verify === "function") {
|
|
6966
|
+
return ed;
|
|
6967
|
+
}
|
|
6968
|
+
return null;
|
|
6969
|
+
} catch {
|
|
6970
|
+
return null;
|
|
6971
|
+
}
|
|
6972
|
+
}
|
|
6973
|
+
var init_loadEd25519 = __esm({
|
|
6974
|
+
"lib/module/features/ota/crypto/loadEd25519.js"() {
|
|
6975
|
+
"use strict";
|
|
6976
|
+
}
|
|
6977
|
+
});
|
|
6978
|
+
|
|
6979
|
+
// lib/module/features/ota/crypto/loadSha512.js
|
|
6980
|
+
function loadSha512() {
|
|
6981
|
+
try {
|
|
6982
|
+
const mod = require("@noble/hashes/sha2.js");
|
|
6983
|
+
const sha512 = mod?.sha512 ?? mod?.default?.sha512;
|
|
6984
|
+
return typeof sha512 === "function" ? sha512 : null;
|
|
6985
|
+
} catch {
|
|
6986
|
+
return null;
|
|
6987
|
+
}
|
|
6988
|
+
}
|
|
6989
|
+
var init_loadSha512 = __esm({
|
|
6990
|
+
"lib/module/features/ota/crypto/loadSha512.js"() {
|
|
6991
|
+
"use strict";
|
|
6992
|
+
}
|
|
6993
|
+
});
|
|
6994
|
+
|
|
6995
|
+
// lib/module/features/ota/crypto/builtinVerifier.js
|
|
6996
|
+
var builtinVerifier_exports = {};
|
|
6997
|
+
__export(builtinVerifier_exports, {
|
|
6998
|
+
_resetBuiltinVerifier: () => _resetBuiltinVerifier,
|
|
6999
|
+
decodeSignature: () => decodeSignature,
|
|
7000
|
+
getBuiltinVerifier: () => getBuiltinVerifier
|
|
7001
|
+
});
|
|
7002
|
+
function hexToBytes(hex) {
|
|
7003
|
+
const clean2 = hex.trim().toLowerCase().replace(/^0x/, "");
|
|
7004
|
+
if (clean2.length === 0 || clean2.length % 2 !== 0 || /[^0-9a-f]/.test(clean2)) return null;
|
|
7005
|
+
const out = new Uint8Array(clean2.length / 2);
|
|
7006
|
+
for (let i = 0; i < out.length; i++) {
|
|
7007
|
+
out[i] = parseInt(clean2.slice(i * 2, i * 2 + 2), 16);
|
|
7008
|
+
}
|
|
7009
|
+
return out;
|
|
7010
|
+
}
|
|
7011
|
+
function base64ToBytes2(b64) {
|
|
7012
|
+
const s = b64.trim().replace(/-/g, "+").replace(/_/g, "/").replace(/=+$/, "");
|
|
7013
|
+
if (s.length === 0) return null;
|
|
7014
|
+
const out = [];
|
|
7015
|
+
let buffer = 0;
|
|
7016
|
+
let bits = 0;
|
|
7017
|
+
for (const ch of s) {
|
|
7018
|
+
const idx = B64_ALPHABET2.indexOf(ch);
|
|
7019
|
+
if (idx === -1) return null;
|
|
7020
|
+
buffer = buffer << 6 | idx;
|
|
7021
|
+
bits += 6;
|
|
7022
|
+
if (bits >= 8) {
|
|
7023
|
+
bits -= 8;
|
|
7024
|
+
out.push(buffer >> bits & 255);
|
|
7025
|
+
}
|
|
7026
|
+
}
|
|
7027
|
+
return Uint8Array.from(out);
|
|
7028
|
+
}
|
|
7029
|
+
function decodeSignature(sig) {
|
|
7030
|
+
const trimmed = sig.trim();
|
|
7031
|
+
if (/^(0x)?[0-9a-fA-F]{128}$/.test(trimmed)) {
|
|
7032
|
+
const hex2 = hexToBytes(trimmed);
|
|
7033
|
+
if (hex2 && hex2.length === ED25519_SIGNATURE_BYTES) return hex2;
|
|
7034
|
+
}
|
|
7035
|
+
const b64 = base64ToBytes2(trimmed);
|
|
7036
|
+
if (b64 && b64.length === ED25519_SIGNATURE_BYTES) return b64;
|
|
7037
|
+
const hex = hexToBytes(trimmed);
|
|
7038
|
+
if (hex && hex.length === ED25519_SIGNATURE_BYTES) return hex;
|
|
7039
|
+
return null;
|
|
7040
|
+
}
|
|
7041
|
+
function wireSha512(ed, sha512) {
|
|
7042
|
+
const concat2 = (parts) => {
|
|
7043
|
+
if (parts.length === 1) return parts[0];
|
|
7044
|
+
let total = 0;
|
|
7045
|
+
for (const p of parts) total += p.length;
|
|
7046
|
+
const out = new Uint8Array(total);
|
|
7047
|
+
let off = 0;
|
|
7048
|
+
for (const p of parts) {
|
|
7049
|
+
out.set(p, off);
|
|
7050
|
+
off += p.length;
|
|
7051
|
+
}
|
|
7052
|
+
return out;
|
|
7053
|
+
};
|
|
7054
|
+
const sync = (...msgs) => sha512(concat2(msgs));
|
|
7055
|
+
const asyncFn = async (...msgs) => sha512(concat2(msgs));
|
|
7056
|
+
let wired = false;
|
|
7057
|
+
try {
|
|
7058
|
+
if (ed.hashes && typeof ed.hashes === "object") {
|
|
7059
|
+
if (typeof ed.hashes.sha512 !== "function") ed.hashes.sha512 = sync;
|
|
7060
|
+
if (typeof ed.hashes.sha512Async !== "function") ed.hashes.sha512Async = asyncFn;
|
|
7061
|
+
wired = typeof ed.hashes.sha512 === "function";
|
|
7062
|
+
}
|
|
7063
|
+
} catch {
|
|
7064
|
+
}
|
|
7065
|
+
try {
|
|
7066
|
+
if (ed.etc && typeof ed.etc === "object") {
|
|
7067
|
+
if (typeof ed.etc.sha512Sync !== "function") ed.etc.sha512Sync = sync;
|
|
7068
|
+
if (typeof ed.etc.sha512Async !== "function") ed.etc.sha512Async = asyncFn;
|
|
7069
|
+
wired = wired || typeof ed.etc.sha512Sync === "function";
|
|
7070
|
+
}
|
|
7071
|
+
} catch {
|
|
7072
|
+
}
|
|
7073
|
+
return wired;
|
|
7074
|
+
}
|
|
7075
|
+
function getBuiltinVerifier() {
|
|
7076
|
+
if (resolved !== void 0) return resolved;
|
|
7077
|
+
const ed = loadEd25519();
|
|
7078
|
+
const sha512 = loadSha512();
|
|
7079
|
+
if (!ed || !sha512) {
|
|
7080
|
+
resolved = null;
|
|
7081
|
+
return null;
|
|
7082
|
+
}
|
|
7083
|
+
if (!wireSha512(ed, sha512)) {
|
|
7084
|
+
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.");
|
|
7085
|
+
resolved = null;
|
|
7086
|
+
return null;
|
|
7087
|
+
}
|
|
7088
|
+
resolved = async ({
|
|
7089
|
+
messageHex,
|
|
7090
|
+
signature,
|
|
7091
|
+
publicKey
|
|
7092
|
+
}) => {
|
|
7093
|
+
const message = hexToBytes(messageHex);
|
|
7094
|
+
const pub = hexToBytes(publicKey);
|
|
7095
|
+
const sig = decodeSignature(signature);
|
|
7096
|
+
if (!message) {
|
|
7097
|
+
logger.error("[OTA] Bundle SHA-256 is not valid hex \u2014 refusing to stage.");
|
|
7098
|
+
return false;
|
|
7099
|
+
}
|
|
7100
|
+
if (!pub || pub.length !== ED25519_PUBLIC_KEY_BYTES) {
|
|
7101
|
+
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"}.`);
|
|
7102
|
+
return false;
|
|
7103
|
+
}
|
|
7104
|
+
if (!sig) {
|
|
7105
|
+
logger.error("[OTA] Bundle signature is not a 64-byte hex/base64 value.");
|
|
7106
|
+
return false;
|
|
7107
|
+
}
|
|
7108
|
+
try {
|
|
7109
|
+
if (typeof ed.verify === "function") return ed.verify(sig, message, pub);
|
|
7110
|
+
if (typeof ed.verifyAsync === "function") return await ed.verifyAsync(sig, message, pub);
|
|
7111
|
+
return false;
|
|
7112
|
+
} catch {
|
|
7113
|
+
return false;
|
|
7114
|
+
}
|
|
7115
|
+
};
|
|
7116
|
+
__DEV__ && logger.debug("[OTA] Using built-in ed25519 verifier (@noble/ed25519 detected).");
|
|
7117
|
+
return resolved;
|
|
7118
|
+
}
|
|
7119
|
+
function _resetBuiltinVerifier() {
|
|
7120
|
+
resolved = void 0;
|
|
7121
|
+
}
|
|
7122
|
+
var ED25519_SIGNATURE_BYTES, ED25519_PUBLIC_KEY_BYTES, B64_ALPHABET2, resolved;
|
|
7123
|
+
var init_builtinVerifier = __esm({
|
|
7124
|
+
"lib/module/features/ota/crypto/builtinVerifier.js"() {
|
|
7125
|
+
"use strict";
|
|
7126
|
+
init_loadEd25519();
|
|
7127
|
+
init_loadSha512();
|
|
7128
|
+
init_internalLogger();
|
|
7129
|
+
ED25519_SIGNATURE_BYTES = 64;
|
|
7130
|
+
ED25519_PUBLIC_KEY_BYTES = 32;
|
|
7131
|
+
B64_ALPHABET2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
7132
|
+
}
|
|
7133
|
+
});
|
|
7134
|
+
|
|
7135
|
+
// lib/module/features/ota/crypto/nativeVerifier.js
|
|
7136
|
+
function bytesToHex(bytes) {
|
|
7137
|
+
let out = "";
|
|
7138
|
+
for (const b of bytes) out += b.toString(16).padStart(2, "0");
|
|
7139
|
+
return out;
|
|
7140
|
+
}
|
|
7141
|
+
function normalizeHex(value, expectedChars) {
|
|
7142
|
+
const clean2 = value.trim().toLowerCase().replace(/^0x/, "");
|
|
7143
|
+
if (clean2.length !== expectedChars || /[^0-9a-f]/.test(clean2)) return null;
|
|
7144
|
+
return clean2;
|
|
7145
|
+
}
|
|
7146
|
+
function getNativeVerifier() {
|
|
7147
|
+
if (resolved2 !== void 0) return resolved2;
|
|
7148
|
+
const native = NativeScaleBunOta_default;
|
|
7149
|
+
if (!native || typeof native.verifyEd25519 !== "function") {
|
|
7150
|
+
resolved2 = null;
|
|
7151
|
+
return null;
|
|
7152
|
+
}
|
|
7153
|
+
resolved2 = async ({
|
|
7154
|
+
messageHex,
|
|
7155
|
+
signature,
|
|
7156
|
+
publicKey
|
|
7157
|
+
}) => {
|
|
7158
|
+
const message = normalizeHex(messageHex, 64);
|
|
7159
|
+
const key = normalizeHex(publicKey, ED25519_PUBLIC_KEY_HEX_CHARS);
|
|
7160
|
+
const sigBytes = decodeSignature(signature);
|
|
7161
|
+
if (!message || !key || !sigBytes) {
|
|
7162
|
+
logger.error("[OTA] Signature inputs malformed (bundle hash, signature, or public key) \u2014 refusing to stage.");
|
|
7163
|
+
return false;
|
|
7164
|
+
}
|
|
7165
|
+
let verdict;
|
|
7166
|
+
try {
|
|
7167
|
+
verdict = await native.verifyEd25519(message, bytesToHex(sigBytes), key);
|
|
7168
|
+
} catch (err) {
|
|
7169
|
+
logger.warn(`[OTA] Native ed25519 verify failed to run: ${err?.message ?? err}`);
|
|
7170
|
+
verdict = "unavailable";
|
|
7171
|
+
}
|
|
7172
|
+
if (verdict === "valid") return true;
|
|
7173
|
+
if (verdict === "invalid") return false;
|
|
7174
|
+
const builtin = getBuiltinVerifier();
|
|
7175
|
+
if (builtin) {
|
|
7176
|
+
return builtin({
|
|
7177
|
+
messageHex,
|
|
7178
|
+
signature,
|
|
7179
|
+
publicKey
|
|
7180
|
+
});
|
|
7181
|
+
}
|
|
7182
|
+
throw new Error("ed25519 unavailable: this OS has no native implementation (Android < 13) and the optional peers @noble/ed25519 + @noble/hashes are not installed.");
|
|
7183
|
+
};
|
|
7184
|
+
__DEV__ && logger.debug("[OTA] Using native ed25519 verifier (platform crypto).");
|
|
7185
|
+
return resolved2;
|
|
7186
|
+
}
|
|
7187
|
+
var ED25519_PUBLIC_KEY_HEX_CHARS, resolved2;
|
|
7188
|
+
var init_nativeVerifier = __esm({
|
|
7189
|
+
"lib/module/features/ota/crypto/nativeVerifier.js"() {
|
|
7190
|
+
"use strict";
|
|
7191
|
+
init_NativeScaleBunOta();
|
|
7192
|
+
init_internalLogger();
|
|
7193
|
+
init_builtinVerifier();
|
|
7194
|
+
ED25519_PUBLIC_KEY_HEX_CHARS = 64;
|
|
7195
|
+
}
|
|
7196
|
+
});
|
|
7197
|
+
|
|
6910
7198
|
// lib/module/features/ota/signature.js
|
|
6911
7199
|
async function verifyBundleSignature(bundleSha256, signature, config) {
|
|
6912
|
-
const
|
|
6913
|
-
|
|
7200
|
+
const rawKey = config?.publicKey;
|
|
7201
|
+
const keys = (Array.isArray(rawKey) ? rawKey : rawKey ? [rawKey] : []).filter((k) => typeof k === "string" && k.trim().length > 0);
|
|
7202
|
+
if (rawKey === void 0 || rawKey === "" || rawKey === null) {
|
|
6914
7203
|
if (!warnedNotConfigured) {
|
|
6915
7204
|
warnedNotConfigured = true;
|
|
6916
7205
|
logger.warn("[OTA] No signing public key configured \u2014 bundles are accepted on SHA-256 integrity alone. Configure `ota.publicSigningKey` to enforce authenticity.");
|
|
@@ -6920,6 +7209,13 @@ async function verifyBundleSignature(bundleSha256, signature, config) {
|
|
|
6920
7209
|
reason: "not_configured"
|
|
6921
7210
|
};
|
|
6922
7211
|
}
|
|
7212
|
+
if (keys.length === 0) {
|
|
7213
|
+
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.");
|
|
7214
|
+
return {
|
|
7215
|
+
ok: false,
|
|
7216
|
+
reason: "no_keys"
|
|
7217
|
+
};
|
|
7218
|
+
}
|
|
6923
7219
|
if (!signature) {
|
|
6924
7220
|
logger.error("[OTA] Bundle has no signature but a signing key is configured \u2014 refusing to stage.");
|
|
6925
7221
|
return {
|
|
@@ -6927,43 +7223,52 @@ async function verifyBundleSignature(bundleSha256, signature, config) {
|
|
|
6927
7223
|
reason: "missing_signature"
|
|
6928
7224
|
};
|
|
6929
7225
|
}
|
|
6930
|
-
|
|
6931
|
-
|
|
7226
|
+
const verifier = typeof config?.verifier === "function" ? config.verifier : getNativeVerifier() ?? getBuiltinVerifier();
|
|
7227
|
+
if (!verifier) {
|
|
7228
|
+
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`.");
|
|
6932
7229
|
return {
|
|
6933
7230
|
ok: false,
|
|
6934
7231
|
reason: "no_verifier"
|
|
6935
7232
|
};
|
|
6936
7233
|
}
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
6946
|
-
|
|
6947
|
-
|
|
7234
|
+
let sawThrow = false;
|
|
7235
|
+
let sawCompletion = false;
|
|
7236
|
+
for (const key of keys) {
|
|
7237
|
+
try {
|
|
7238
|
+
const valid = await verifier({
|
|
7239
|
+
messageHex: bundleSha256,
|
|
7240
|
+
signature,
|
|
7241
|
+
publicKey: key
|
|
7242
|
+
});
|
|
7243
|
+
sawCompletion = true;
|
|
7244
|
+
if (valid) return {
|
|
7245
|
+
ok: true,
|
|
7246
|
+
reason: "verified"
|
|
6948
7247
|
};
|
|
7248
|
+
} catch (err) {
|
|
7249
|
+
sawThrow = true;
|
|
7250
|
+
logger.error(`[OTA] Signature verifier threw: ${err?.message ?? err}`);
|
|
6949
7251
|
}
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
reason: "verified"
|
|
6953
|
-
};
|
|
6954
|
-
} catch (err) {
|
|
6955
|
-
logger.error(`[OTA] Signature verifier threw: ${err?.message ?? err}`);
|
|
7252
|
+
}
|
|
7253
|
+
if (sawThrow && !sawCompletion) {
|
|
6956
7254
|
return {
|
|
6957
7255
|
ok: false,
|
|
6958
7256
|
reason: "verifier_threw"
|
|
6959
7257
|
};
|
|
6960
7258
|
}
|
|
7259
|
+
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.");
|
|
7260
|
+
return {
|
|
7261
|
+
ok: false,
|
|
7262
|
+
reason: "invalid_signature"
|
|
7263
|
+
};
|
|
6961
7264
|
}
|
|
6962
7265
|
var warnedNotConfigured;
|
|
6963
7266
|
var init_signature = __esm({
|
|
6964
7267
|
"lib/module/features/ota/signature.js"() {
|
|
6965
7268
|
"use strict";
|
|
6966
7269
|
init_internalLogger();
|
|
7270
|
+
init_builtinVerifier();
|
|
7271
|
+
init_nativeVerifier();
|
|
6967
7272
|
warnedNotConfigured = false;
|
|
6968
7273
|
}
|
|
6969
7274
|
});
|
|
@@ -7783,6 +8088,42 @@ var init_OtaOrchestrator = __esm({
|
|
|
7783
8088
|
}
|
|
7784
8089
|
});
|
|
7785
8090
|
|
|
8091
|
+
// lib/module/features/journey/uiState.js
|
|
8092
|
+
var uiState_exports = {};
|
|
8093
|
+
__export(uiState_exports, {
|
|
8094
|
+
clearUiState: () => clearUiState,
|
|
8095
|
+
setUiState: () => setUiState,
|
|
8096
|
+
uiStateSignature: () => uiStateSignature
|
|
8097
|
+
});
|
|
8098
|
+
function setUiState(name, value) {
|
|
8099
|
+
const n = clean(name);
|
|
8100
|
+
const v = clean(value);
|
|
8101
|
+
if (n && v) declared.set(n, v);
|
|
8102
|
+
}
|
|
8103
|
+
function clearUiState(name) {
|
|
8104
|
+
if (name === void 0) declared.clear();
|
|
8105
|
+
else declared.delete(clean(name));
|
|
8106
|
+
}
|
|
8107
|
+
function uiStateSignature() {
|
|
8108
|
+
if (!declared.size) return void 0;
|
|
8109
|
+
const pairs = [...declared.keys()].sort().map((k) => `${k}:${declared.get(k)}`);
|
|
8110
|
+
let signature = "";
|
|
8111
|
+
for (const pair of pairs) {
|
|
8112
|
+
const next = signature ? `${signature};${pair}` : pair;
|
|
8113
|
+
if (next.length > 96) break;
|
|
8114
|
+
signature = next;
|
|
8115
|
+
}
|
|
8116
|
+
return signature || void 0;
|
|
8117
|
+
}
|
|
8118
|
+
var declared, clean;
|
|
8119
|
+
var init_uiState = __esm({
|
|
8120
|
+
"lib/module/features/journey/uiState.js"() {
|
|
8121
|
+
"use strict";
|
|
8122
|
+
declared = /* @__PURE__ */ new Map();
|
|
8123
|
+
clean = (s) => typeof s === "string" ? s.replace(/[;:|]/g, "").trim().slice(0, 32) : "";
|
|
8124
|
+
}
|
|
8125
|
+
});
|
|
8126
|
+
|
|
7786
8127
|
// lib/module/features/engage/gestureTriggerDetector.js
|
|
7787
8128
|
var gestureTriggerDetector_exports = {};
|
|
7788
8129
|
__export(gestureTriggerDetector_exports, {
|
|
@@ -9750,6 +10091,14 @@ var SHAPE = {
|
|
|
9750
10091
|
kind: "str",
|
|
9751
10092
|
opt: true
|
|
9752
10093
|
},
|
|
10094
|
+
// Rotation support: several pinned keys, any of which may verify a
|
|
10095
|
+
// bundle. Ship a build pinning [old, new], re-sign server-side with
|
|
10096
|
+
// new, drop old next release — no emergency store submission when a
|
|
10097
|
+
// key must be replaced. Merged with publicSigningKey by the facade.
|
|
10098
|
+
publicSigningKeys: {
|
|
10099
|
+
kind: "strArr",
|
|
10100
|
+
opt: true
|
|
10101
|
+
},
|
|
9753
10102
|
mandatoryBlocksUi: bool(false)
|
|
9754
10103
|
}
|
|
9755
10104
|
}
|
|
@@ -9785,7 +10134,7 @@ function parseField(field, input, path, issues) {
|
|
|
9785
10134
|
value: field.def,
|
|
9786
10135
|
present: true
|
|
9787
10136
|
};
|
|
9788
|
-
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");
|
|
10137
|
+
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");
|
|
9789
10138
|
}
|
|
9790
10139
|
switch (field.kind) {
|
|
9791
10140
|
case "any":
|
|
@@ -9811,6 +10160,27 @@ function parseField(field, input, path, issues) {
|
|
|
9811
10160
|
value: input,
|
|
9812
10161
|
present: true
|
|
9813
10162
|
};
|
|
10163
|
+
case "strArr": {
|
|
10164
|
+
if (!Array.isArray(input)) return fail(`Expected array, received ${typeofName(input)}`);
|
|
10165
|
+
let allOk = true;
|
|
10166
|
+
for (let i = 0; i < input.length; i++) {
|
|
10167
|
+
if (typeof input[i] !== "string") {
|
|
10168
|
+
issues.push({
|
|
10169
|
+
path: [...path, i],
|
|
10170
|
+
message: `Expected string, received ${typeofName(input[i])}`
|
|
10171
|
+
});
|
|
10172
|
+
allOk = false;
|
|
10173
|
+
}
|
|
10174
|
+
}
|
|
10175
|
+
return allOk ? {
|
|
10176
|
+
ok: true,
|
|
10177
|
+
value: [...input],
|
|
10178
|
+
present: true
|
|
10179
|
+
} : {
|
|
10180
|
+
ok: false,
|
|
10181
|
+
present: true
|
|
10182
|
+
};
|
|
10183
|
+
}
|
|
9814
10184
|
case "num":
|
|
9815
10185
|
if (typeof input !== "number" || Number.isNaN(input)) {
|
|
9816
10186
|
return fail(`Expected number, received ${typeofName(input)}`);
|
|
@@ -10700,14 +11070,14 @@ function installRejectionHandler(onRejection, deps) {
|
|
|
10700
11070
|
allRejections: true,
|
|
10701
11071
|
onUnhandled: (id, error) => {
|
|
10702
11072
|
const err = toError(error);
|
|
10703
|
-
|
|
11073
|
+
pending3.set(id, err);
|
|
10704
11074
|
onRejection(err);
|
|
10705
11075
|
if (isDev) {
|
|
10706
11076
|
console.warn(`Possible unhandled promise rejection (id: ${id}):`, err?.message ?? err);
|
|
10707
11077
|
}
|
|
10708
11078
|
},
|
|
10709
11079
|
onHandled: (id) => {
|
|
10710
|
-
|
|
11080
|
+
pending3.delete(id);
|
|
10711
11081
|
if (isDev) {
|
|
10712
11082
|
console.warn(`Promise rejection handled late (id: ${id}) \u2014 it was already reported.`);
|
|
10713
11083
|
}
|
|
@@ -10717,7 +11087,7 @@ function installRejectionHandler(onRejection, deps) {
|
|
|
10717
11087
|
return true;
|
|
10718
11088
|
}) ?? false;
|
|
10719
11089
|
}
|
|
10720
|
-
var
|
|
11090
|
+
var pending3 = /* @__PURE__ */ new Map();
|
|
10721
11091
|
|
|
10722
11092
|
// lib/module/features/crash/CrashFeature.js
|
|
10723
11093
|
init_CrashReporter();
|
|
@@ -11034,7 +11404,7 @@ var AppLaunchCollector = class {
|
|
|
11034
11404
|
};
|
|
11035
11405
|
|
|
11036
11406
|
// lib/module/features/performance/collectors/ScreenLoadCollector.js
|
|
11037
|
-
var
|
|
11407
|
+
var MAX_PENDING2 = 20;
|
|
11038
11408
|
var ScreenLoadCollector = class {
|
|
11039
11409
|
pending = /* @__PURE__ */ new Map();
|
|
11040
11410
|
lastScreen = null;
|
|
@@ -11049,7 +11419,7 @@ var ScreenLoadCollector = class {
|
|
|
11049
11419
|
*/
|
|
11050
11420
|
markStart(screenName) {
|
|
11051
11421
|
if (!this.config.screenLoad) return;
|
|
11052
|
-
if (this.pending.size >=
|
|
11422
|
+
if (this.pending.size >= MAX_PENDING2) {
|
|
11053
11423
|
const oldest = this.pending.keys().next().value;
|
|
11054
11424
|
if (oldest) this.pending.delete(oldest);
|
|
11055
11425
|
}
|
|
@@ -12458,16 +12828,16 @@ function base64ToBytes(base64) {
|
|
|
12458
12828
|
return decodeManual(base64);
|
|
12459
12829
|
}
|
|
12460
12830
|
function decodeManual(base64) {
|
|
12461
|
-
const
|
|
12462
|
-
const pad =
|
|
12463
|
-
const outLen = Math.floor(
|
|
12831
|
+
const clean2 = base64.replace(/[^A-Za-z0-9+/=]/g, "");
|
|
12832
|
+
const pad = clean2.endsWith("==") ? 2 : clean2.endsWith("=") ? 1 : 0;
|
|
12833
|
+
const outLen = Math.floor(clean2.length * 3 / 4) - pad;
|
|
12464
12834
|
const out = new Uint8Array(outLen);
|
|
12465
12835
|
let p = 0;
|
|
12466
|
-
for (let i = 0; i <
|
|
12467
|
-
const c0 = B64_ALPHABET.indexOf(
|
|
12468
|
-
const c1 = B64_ALPHABET.indexOf(
|
|
12469
|
-
const c2 = B64_ALPHABET.indexOf(
|
|
12470
|
-
const c3 = B64_ALPHABET.indexOf(
|
|
12836
|
+
for (let i = 0; i < clean2.length; i += 4) {
|
|
12837
|
+
const c0 = B64_ALPHABET.indexOf(clean2[i]);
|
|
12838
|
+
const c1 = B64_ALPHABET.indexOf(clean2[i + 1]);
|
|
12839
|
+
const c2 = B64_ALPHABET.indexOf(clean2[i + 2]);
|
|
12840
|
+
const c3 = B64_ALPHABET.indexOf(clean2[i + 3]);
|
|
12471
12841
|
const n = c0 << 18 | c1 << 12 | (c2 & 63) << 6 | c3 & 63;
|
|
12472
12842
|
if (p < outLen) out[p++] = n >> 16 & 255;
|
|
12473
12843
|
if (p < outLen) out[p++] = n >> 8 & 255;
|
|
@@ -16654,12 +17024,17 @@ var ScaleBunFacade = class {
|
|
|
16654
17024
|
/**
|
|
16655
17025
|
* Boot the OTA orchestrator when the init config asks for it.
|
|
16656
17026
|
*
|
|
16657
|
-
* `publicSigningKey`
|
|
16658
|
-
* from configuration alone. The
|
|
16659
|
-
*
|
|
16660
|
-
*
|
|
16661
|
-
*
|
|
16662
|
-
*
|
|
17027
|
+
* `publicSigningKey` / `publicSigningKeys` are honoured here so signature
|
|
17028
|
+
* enforcement is reachable from configuration alone. The list form exists
|
|
17029
|
+
* for key ROTATION: a build pinning [old, new] keeps verifying while the
|
|
17030
|
+
* server moves to the new key, so replacing a key never needs an emergency
|
|
17031
|
+
* store release. Both fields merge (deduplicated) into one pinned set.
|
|
17032
|
+
*
|
|
17033
|
+
* Verification comes from `ota.verifySignature` when supplied, else the
|
|
17034
|
+
* built-in @noble-based verifier (optional peers). Pinning keys with
|
|
17035
|
+
* NEITHER available is fail-CLOSED by design (signature.ts) — an update
|
|
17036
|
+
* that cannot be verified is not installed. We say that out loud rather
|
|
17037
|
+
* than letting the app discover it as a silent no-update condition.
|
|
16663
17038
|
*/
|
|
16664
17039
|
_maybeStartOta(rawConfig) {
|
|
16665
17040
|
const ota = rawConfig?.ota;
|
|
@@ -16668,15 +17043,25 @@ var ScaleBunFacade = class {
|
|
|
16668
17043
|
const {
|
|
16669
17044
|
otaOrchestrator: otaOrchestrator2
|
|
16670
17045
|
} = (init_OtaOrchestrator(), __toCommonJS(OtaOrchestrator_exports));
|
|
16671
|
-
const
|
|
17046
|
+
const rawSingle = ota.publicSigningKey;
|
|
17047
|
+
const rawList = ota.publicSigningKeys;
|
|
17048
|
+
const publicKeys = Array.from(new Set([...typeof rawSingle === "string" && rawSingle ? [rawSingle] : [], ...Array.isArray(rawList) ? rawList : []].filter((k) => typeof k === "string" && k.trim().length > 0)));
|
|
17049
|
+
const signingRequested = publicKeys.length > 0 || Array.isArray(rawList);
|
|
16672
17050
|
const verifier = ota.verifySignature;
|
|
16673
|
-
if (
|
|
16674
|
-
|
|
17051
|
+
if (signingRequested && typeof verifier !== "function") {
|
|
17052
|
+
const {
|
|
17053
|
+
getBuiltinVerifier: getBuiltinVerifier2
|
|
17054
|
+
} = (init_builtinVerifier(), __toCommonJS(builtinVerifier_exports));
|
|
17055
|
+
if (!getBuiltinVerifier2()) {
|
|
17056
|
+
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`.");
|
|
17057
|
+
}
|
|
16675
17058
|
}
|
|
16676
17059
|
otaOrchestrator2.init({
|
|
16677
|
-
...
|
|
17060
|
+
...signingRequested ? {
|
|
16678
17061
|
signature: {
|
|
16679
|
-
|
|
17062
|
+
// Single key stays a plain string — the shape every
|
|
17063
|
+
// existing consumer and test already handles.
|
|
17064
|
+
publicKey: publicKeys.length === 1 ? publicKeys[0] : publicKeys,
|
|
16680
17065
|
verifier
|
|
16681
17066
|
}
|
|
16682
17067
|
} : {},
|
|
@@ -16888,6 +17273,46 @@ var ScaleBunFacade = class {
|
|
|
16888
17273
|
};
|
|
16889
17274
|
}
|
|
16890
17275
|
/** Convenience: emit a Phase 1 purchase event (revenue + currency + transaction_id). */
|
|
17276
|
+
/**
|
|
17277
|
+
* Declare which UI state the user is looking at, so taps are attributed to the surface they
|
|
17278
|
+
* happened on rather than averaged across every variant of the screen.
|
|
17279
|
+
*
|
|
17280
|
+
* ScaleBun.setUiState('filter-sheet', 'open');
|
|
17281
|
+
* ScaleBun.setUiState('checkout-step', 'payment');
|
|
17282
|
+
*
|
|
17283
|
+
* Call it when the state CHANGES — the value is read at tap time, so it only has to be right by
|
|
17284
|
+
* the time the next tap lands. On React Native this is the ONLY source of UI state: unlike the web,
|
|
17285
|
+
* there is no queryable accessibility tree that says a sheet is up, and guessing would produce a
|
|
17286
|
+
* state key that is right on some apps and silently wrong on others.
|
|
17287
|
+
*
|
|
17288
|
+
* Names and values are identifiers, not content: they become query keys in the dashboard, so a
|
|
17289
|
+
* person's name or a cart total does not belong in one. `;`, `:` and `|` are stripped and both
|
|
17290
|
+
* halves are capped at 32 characters.
|
|
17291
|
+
*/
|
|
17292
|
+
setUiState(name, value) {
|
|
17293
|
+
try {
|
|
17294
|
+
const {
|
|
17295
|
+
setUiState: setUiState2
|
|
17296
|
+
} = (init_uiState(), __toCommonJS(uiState_exports));
|
|
17297
|
+
setUiState2(name, value);
|
|
17298
|
+
} catch {
|
|
17299
|
+
}
|
|
17300
|
+
}
|
|
17301
|
+
/**
|
|
17302
|
+
* Stop reporting a UI state dimension — or, with no argument, all of them.
|
|
17303
|
+
*
|
|
17304
|
+
* Clear on screen unmount. A declaration left behind follows the user onto a surface where it means
|
|
17305
|
+
* nothing, and every tap there is filed under a state that was not on screen.
|
|
17306
|
+
*/
|
|
17307
|
+
clearUiState(name) {
|
|
17308
|
+
try {
|
|
17309
|
+
const {
|
|
17310
|
+
clearUiState: clearUiState2
|
|
17311
|
+
} = (init_uiState(), __toCommonJS(uiState_exports));
|
|
17312
|
+
clearUiState2(name);
|
|
17313
|
+
} catch {
|
|
17314
|
+
}
|
|
17315
|
+
}
|
|
16891
17316
|
trackPurchase(input) {
|
|
16892
17317
|
noThrow(() => this._eventTracker?.trackPurchase(input));
|
|
16893
17318
|
}
|
|
@@ -20511,11 +20936,11 @@ var normalizedWheelValue = (value) => value.toLowerCase().replace(/\s+/g, " ").t
|
|
|
20511
20936
|
function resolveWheelOutcome(segments, source) {
|
|
20512
20937
|
const identity = wheelOutcomeIdentity(source);
|
|
20513
20938
|
const index = findWheelOutcomeIndex(segments, identity);
|
|
20514
|
-
const
|
|
20939
|
+
const resolved3 = index >= 0;
|
|
20515
20940
|
return {
|
|
20516
|
-
index:
|
|
20517
|
-
label: identity.resultLabel || (
|
|
20518
|
-
resolved
|
|
20941
|
+
index: resolved3 ? index : 0,
|
|
20942
|
+
label: identity.resultLabel || (resolved3 ? segments[index]?.fullLabel ?? "" : ""),
|
|
20943
|
+
resolved: resolved3
|
|
20519
20944
|
};
|
|
20520
20945
|
}
|
|
20521
20946
|
function SpinWheelInApp(props) {
|
|
@@ -29374,8 +29799,8 @@ function EngagePromptProvider({
|
|
|
29374
29799
|
__DEV__ && logger.debug("[Engage] no eligible in-app message to render");
|
|
29375
29800
|
return false;
|
|
29376
29801
|
}
|
|
29377
|
-
const
|
|
29378
|
-
activeInAppVariantRef.current = message.experiment?.enabled ?
|
|
29802
|
+
const resolved3 = selectInAppVariant(message, getDeviceId());
|
|
29803
|
+
activeInAppVariantRef.current = message.experiment?.enabled ? resolved3 : null;
|
|
29379
29804
|
store.recordImpression(message.id, Date.now(), message.revision);
|
|
29380
29805
|
setActiveInApp(message);
|
|
29381
29806
|
return true;
|
|
@@ -29615,7 +30040,6 @@ function useEngagePrompt() {
|
|
|
29615
30040
|
// lib/module/features/journey/ScaleBunDebugRoot.js
|
|
29616
30041
|
var import_react9 = __toESM(require("react"));
|
|
29617
30042
|
var import_react_native42 = require("react-native");
|
|
29618
|
-
init_automaticEvents();
|
|
29619
30043
|
|
|
29620
30044
|
// lib/module/features/journey/touchTarget.js
|
|
29621
30045
|
var MAX_DEPTH = 12;
|
|
@@ -29687,6 +30111,7 @@ function describeTouchTarget(t) {
|
|
|
29687
30111
|
|
|
29688
30112
|
// lib/module/features/journey/ScaleBunDebugRoot.js
|
|
29689
30113
|
init_device();
|
|
30114
|
+
init_interactionProtocol();
|
|
29690
30115
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
29691
30116
|
var _bootstrap = null;
|
|
29692
30117
|
function getBootstrap() {
|
|
@@ -29876,7 +30301,9 @@ function ScaleBunDebugRoot({
|
|
|
29876
30301
|
}, [navigationRef]);
|
|
29877
30302
|
const lastNativeTouchTsRef = (0, import_react9.useRef)(0);
|
|
29878
30303
|
const nativeTrackingConfirmedRef = (0, import_react9.useRef)(false);
|
|
29879
|
-
const
|
|
30304
|
+
const touchStartRef = (0, import_react9.useRef)(null);
|
|
30305
|
+
const interactionStartsRef = (0, import_react9.useRef)([]);
|
|
30306
|
+
const pendingJsEmitsRef = (0, import_react9.useRef)(/* @__PURE__ */ new Map());
|
|
29880
30307
|
(0, import_react9.useEffect)(() => {
|
|
29881
30308
|
let subscription = null;
|
|
29882
30309
|
try {
|
|
@@ -29887,9 +30314,12 @@ function ScaleBunDebugRoot({
|
|
|
29887
30314
|
try {
|
|
29888
30315
|
lastNativeTouchTsRef.current = Date.now();
|
|
29889
30316
|
nativeTrackingConfirmedRef.current = true;
|
|
29890
|
-
|
|
29891
|
-
|
|
29892
|
-
|
|
30317
|
+
const nativeOccurredAt = typeof event.occurredAt === "number" ? event.occurredAt : (typeof event.timestamp === "number" ? event.timestamp : Date.now()) - (typeof event.durationMs === "number" ? event.durationMs : 0);
|
|
30318
|
+
const start = nearestInteractionStart(interactionStartsRef.current, nativeOccurredAt);
|
|
30319
|
+
if (start) {
|
|
30320
|
+
const pending4 = pendingJsEmitsRef.current.get(start.interactionId);
|
|
30321
|
+
if (pending4 !== void 0) clearTimeout(pending4);
|
|
30322
|
+
pendingJsEmitsRef.current.delete(start.interactionId);
|
|
29893
30323
|
}
|
|
29894
30324
|
const {
|
|
29895
30325
|
SessionManager: SessionManager2
|
|
@@ -29908,6 +30338,15 @@ function ScaleBunDebugRoot({
|
|
|
29908
30338
|
} catch {
|
|
29909
30339
|
}
|
|
29910
30340
|
sm.onGestureDetected(event.gestureType || "tap", {
|
|
30341
|
+
interactionId: event.interactionId || start?.interactionId || generateInteractionId(),
|
|
30342
|
+
interactionProtocol: event.interactionProtocol || INTERACTION_PROTOCOL_VERSION,
|
|
30343
|
+
occurredAt: nativeOccurredAt,
|
|
30344
|
+
ui: start?.ui,
|
|
30345
|
+
stateStatus: start?.stateStatus ?? "not_captured",
|
|
30346
|
+
target: start?.target,
|
|
30347
|
+
targetId: start?.targetId,
|
|
30348
|
+
screenName: start?.screenName,
|
|
30349
|
+
emitAutomaticAnalytics: start?.emitAutomaticAnalytics ?? captureAutomaticInteractions,
|
|
29911
30350
|
x: event.rawX,
|
|
29912
30351
|
y: event.rawY,
|
|
29913
30352
|
// Use the actual native end coords when present, not the down
|
|
@@ -29940,9 +30379,10 @@ function ScaleBunDebugRoot({
|
|
|
29940
30379
|
subscription?.remove();
|
|
29941
30380
|
} catch {
|
|
29942
30381
|
}
|
|
30382
|
+
for (const timer of pendingJsEmitsRef.current.values()) clearTimeout(timer);
|
|
30383
|
+
pendingJsEmitsRef.current.clear();
|
|
29943
30384
|
};
|
|
29944
|
-
}, []);
|
|
29945
|
-
const touchStartRef = (0, import_react9.useRef)(null);
|
|
30385
|
+
}, [captureAutomaticInteractions]);
|
|
29946
30386
|
const rootRectRef = (0, import_react9.useRef)(null);
|
|
29947
30387
|
const measureRoot = () => {
|
|
29948
30388
|
try {
|
|
@@ -29974,11 +30414,39 @@ function ScaleBunDebugRoot({
|
|
|
29974
30414
|
const handleTouchStart = (e) => {
|
|
29975
30415
|
try {
|
|
29976
30416
|
const touch = e.nativeEvent;
|
|
29977
|
-
|
|
30417
|
+
const target = resolveTouchTarget(e);
|
|
30418
|
+
let ui;
|
|
30419
|
+
let screenName;
|
|
30420
|
+
try {
|
|
30421
|
+
const {
|
|
30422
|
+
uiStateSignature: uiStateSignature2
|
|
30423
|
+
} = (init_uiState(), __toCommonJS(uiState_exports));
|
|
30424
|
+
ui = uiStateSignature2();
|
|
30425
|
+
} catch {
|
|
30426
|
+
}
|
|
30427
|
+
try {
|
|
30428
|
+
const {
|
|
30429
|
+
AutoScreenDetector: AutoScreenDetector2
|
|
30430
|
+
} = (init_AutoScreenDetector(), __toCommonJS(AutoScreenDetector_exports));
|
|
30431
|
+
screenName = AutoScreenDetector2.getInstance().getCurrentScreen() || void 0;
|
|
30432
|
+
} catch {
|
|
30433
|
+
}
|
|
30434
|
+
const start = {
|
|
30435
|
+
interactionId: generateInteractionId(),
|
|
30436
|
+
occurredAt: Date.now(),
|
|
29978
30437
|
x: touch.pageX,
|
|
29979
30438
|
y: touch.pageY,
|
|
29980
|
-
|
|
30439
|
+
locationX: touch.locationX,
|
|
30440
|
+
locationY: touch.locationY,
|
|
30441
|
+
target: describeTouchTarget(target),
|
|
30442
|
+
targetId: target?.testID,
|
|
30443
|
+
screenName,
|
|
30444
|
+
ui,
|
|
30445
|
+
stateStatus: ui ? "captured_nonempty" : "not_instrumented",
|
|
30446
|
+
emitAutomaticAnalytics: captureAutomaticInteractions
|
|
29981
30447
|
};
|
|
30448
|
+
touchStartRef.current = start;
|
|
30449
|
+
interactionStartsRef.current = interactionStartsRef.current.filter((candidate) => start.occurredAt - candidate.occurredAt < 5e3).concat(start).slice(-8);
|
|
29982
30450
|
} catch {
|
|
29983
30451
|
}
|
|
29984
30452
|
};
|
|
@@ -30009,21 +30477,29 @@ function ScaleBunDebugRoot({
|
|
|
30009
30477
|
}
|
|
30010
30478
|
measureRoot();
|
|
30011
30479
|
const rootRect = rootRectRef.current;
|
|
30012
|
-
const tapped = describeTouchTarget(resolveTouchTarget(e));
|
|
30480
|
+
const tapped = start?.target ?? describeTouchTarget(resolveTouchTarget(e));
|
|
30013
30481
|
const gestureDetails = {
|
|
30014
30482
|
target: tapped,
|
|
30015
|
-
|
|
30016
|
-
|
|
30017
|
-
|
|
30018
|
-
|
|
30019
|
-
|
|
30020
|
-
|
|
30483
|
+
interactionId: start?.interactionId ?? generateInteractionId(),
|
|
30484
|
+
interactionProtocol: INTERACTION_PROTOCOL_VERSION,
|
|
30485
|
+
occurredAt: start?.occurredAt ?? Date.now(),
|
|
30486
|
+
ui: start?.ui,
|
|
30487
|
+
stateStatus: start?.stateStatus ?? "not_captured",
|
|
30488
|
+
targetId: start?.targetId,
|
|
30489
|
+
screenName: start?.screenName,
|
|
30490
|
+
emitAutomaticAnalytics: start?.emitAutomaticAnalytics ?? captureAutomaticInteractions,
|
|
30491
|
+
x: start?.x ?? touch.pageX,
|
|
30492
|
+
y: start?.y ?? touch.pageY,
|
|
30493
|
+
pageX: start?.x ?? touch.pageX,
|
|
30494
|
+
pageY: start?.y ?? touch.pageY,
|
|
30495
|
+
locationX: start?.locationX ?? touch.locationX,
|
|
30496
|
+
locationY: start?.locationY ?? touch.locationY,
|
|
30021
30497
|
viewportWidth: vpW > 0 ? vpW : void 0,
|
|
30022
30498
|
viewportHeight: vpH > 0 ? vpH : void 0
|
|
30023
30499
|
};
|
|
30024
30500
|
if (rootRect && rootRect.w > 0 && rootRect.h > 0) {
|
|
30025
|
-
const nx = Math.max(0, Math.min(1, touch.pageX / rootRect.w));
|
|
30026
|
-
const ny = Math.max(0, Math.min(1, touch.pageY / rootRect.h));
|
|
30501
|
+
const nx = Math.max(0, Math.min(1, (start?.x ?? touch.pageX) / rootRect.w));
|
|
30502
|
+
const ny = Math.max(0, Math.min(1, (start?.y ?? touch.pageY) / rootRect.h));
|
|
30027
30503
|
gestureDetails.normalizedX = nx;
|
|
30028
30504
|
gestureDetails.normalizedY = ny;
|
|
30029
30505
|
gestureDetails.normalizedPrecomputed = true;
|
|
@@ -30054,7 +30530,7 @@ function ScaleBunDebugRoot({
|
|
|
30054
30530
|
const dx = touch.pageX - start.x;
|
|
30055
30531
|
const dy = touch.pageY - start.y;
|
|
30056
30532
|
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
30057
|
-
const duration = Date.now() - start.
|
|
30533
|
+
const duration = Date.now() - start.occurredAt;
|
|
30058
30534
|
gestureDetails.duration = duration;
|
|
30059
30535
|
if (dist >= 15) {
|
|
30060
30536
|
gestureDetails.endX = touch.pageX;
|
|
@@ -30072,47 +30548,16 @@ function ScaleBunDebugRoot({
|
|
|
30072
30548
|
gestureType = "long_press";
|
|
30073
30549
|
}
|
|
30074
30550
|
}
|
|
30075
|
-
const targetInfo = _extractTarget(e);
|
|
30076
|
-
gestureDetails.target = targetInfo?.testId || targetInfo?.accessibilityLabel;
|
|
30077
|
-
if (captureAutomaticInteractions) {
|
|
30078
|
-
let screen;
|
|
30079
|
-
try {
|
|
30080
|
-
const {
|
|
30081
|
-
AutoScreenDetector: AutoScreenDetector2
|
|
30082
|
-
} = (init_AutoScreenDetector(), __toCommonJS(AutoScreenDetector_exports));
|
|
30083
|
-
screen = AutoScreenDetector2.getInstance().getCurrentScreen() || void 0;
|
|
30084
|
-
} catch {
|
|
30085
|
-
}
|
|
30086
|
-
emitAutomaticEvent("element_interacted", {
|
|
30087
|
-
gesture_type: gestureType,
|
|
30088
|
-
screen_name: screen,
|
|
30089
|
-
// testID/nativeID is an author-controlled stable identifier.
|
|
30090
|
-
// Accessibility labels and rendered text are deliberately omitted.
|
|
30091
|
-
target_id: targetInfo?.testId,
|
|
30092
|
-
normalized_x: gestureDetails.normalizedX,
|
|
30093
|
-
normalized_y: gestureDetails.normalizedY,
|
|
30094
|
-
direction: gestureDetails.direction,
|
|
30095
|
-
duration_ms: gestureDetails.duration
|
|
30096
|
-
});
|
|
30097
|
-
}
|
|
30098
30551
|
if (gestureType === "tap") {
|
|
30099
30552
|
try {
|
|
30100
30553
|
const {
|
|
30101
30554
|
gestureTriggerDetector: gestureTriggerDetector2
|
|
30102
30555
|
} = (init_gestureTriggerDetector(), __toCommonJS(gestureTriggerDetector_exports));
|
|
30103
|
-
let screen;
|
|
30104
|
-
try {
|
|
30105
|
-
const {
|
|
30106
|
-
AutoScreenDetector: AutoScreenDetector2
|
|
30107
|
-
} = (init_AutoScreenDetector(), __toCommonJS(AutoScreenDetector_exports));
|
|
30108
|
-
screen = AutoScreenDetector2.getInstance().getCurrentScreen() || void 0;
|
|
30109
|
-
} catch {
|
|
30110
|
-
}
|
|
30111
30556
|
gestureTriggerDetector2.recordTap({
|
|
30112
|
-
x: touch.pageX,
|
|
30113
|
-
y: touch.pageY,
|
|
30114
|
-
hasTarget: !!(
|
|
30115
|
-
screen
|
|
30557
|
+
x: start?.x ?? touch.pageX,
|
|
30558
|
+
y: start?.y ?? touch.pageY,
|
|
30559
|
+
hasTarget: !!(start?.targetId || start?.target),
|
|
30560
|
+
screen: start?.screenName
|
|
30116
30561
|
});
|
|
30117
30562
|
} catch {
|
|
30118
30563
|
}
|
|
@@ -30125,11 +30570,9 @@ function ScaleBunDebugRoot({
|
|
|
30125
30570
|
const capturedDetails = {
|
|
30126
30571
|
...gestureDetails
|
|
30127
30572
|
};
|
|
30128
|
-
|
|
30129
|
-
|
|
30130
|
-
|
|
30131
|
-
pendingJsEmitRef.current = setTimeout(() => {
|
|
30132
|
-
pendingJsEmitRef.current = null;
|
|
30573
|
+
const interactionId = capturedDetails.interactionId;
|
|
30574
|
+
const timer = setTimeout(() => {
|
|
30575
|
+
pendingJsEmitsRef.current.delete(interactionId);
|
|
30133
30576
|
if (nativeTrackingConfirmedRef.current) return;
|
|
30134
30577
|
try {
|
|
30135
30578
|
const {
|
|
@@ -30146,6 +30589,7 @@ function ScaleBunDebugRoot({
|
|
|
30146
30589
|
} catch {
|
|
30147
30590
|
}
|
|
30148
30591
|
}, 600);
|
|
30592
|
+
pendingJsEmitsRef.current.set(interactionId, timer);
|
|
30149
30593
|
}
|
|
30150
30594
|
touchStartRef.current = null;
|
|
30151
30595
|
} catch {
|
|
@@ -30221,20 +30665,6 @@ function useScaleBunScreen(name) {
|
|
|
30221
30665
|
}
|
|
30222
30666
|
}, [name, manager]);
|
|
30223
30667
|
}
|
|
30224
|
-
function _extractTarget(e) {
|
|
30225
|
-
try {
|
|
30226
|
-
const target = e?.target;
|
|
30227
|
-
if (!target) return void 0;
|
|
30228
|
-
const props = target._internalFiberInstanceHandleDEV?.memoizedProps ?? {};
|
|
30229
|
-
return {
|
|
30230
|
-
testId: props.testID || props.nativeID || void 0,
|
|
30231
|
-
accessibilityLabel: props.accessibilityLabel || void 0,
|
|
30232
|
-
text: void 0
|
|
30233
|
-
};
|
|
30234
|
-
} catch {
|
|
30235
|
-
return void 0;
|
|
30236
|
-
}
|
|
30237
|
-
}
|
|
30238
30668
|
var styles4 = {
|
|
30239
30669
|
root: {
|
|
30240
30670
|
flex: 1
|