@scalebun/react-native 1.10.6 → 1.10.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/scalebun/rn/ota/ScaleBunOtaModule.kt +75 -0
- package/android/src/oldarch/java/com/scalebun/rn/ota/ScaleBunOtaSpec.kt +12 -0
- package/dist/scalebun.full.js +432 -51
- package/dist/scalebun.slim.js +432 -51
- package/ios/Ota/ScaleBunOtaBridge.mm +6 -0
- package/ios/Ota/ScaleBunOtaModule.swift +67 -0
- package/lib/commonjs/core/config/schema.js +34 -1
- package/lib/commonjs/core/constants/version.js +1 -1
- package/lib/commonjs/features/journey/ScaleBunDebugRoot.js +24 -0
- package/lib/commonjs/features/journey/uiState.js +87 -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/metro/serializerCompose.js +32 -0
- package/lib/commonjs/public/ScaleBunFacade.js +74 -11
- package/lib/module/core/config/schema.js +34 -1
- package/lib/module/core/constants/version.js +1 -1
- package/lib/module/features/journey/ScaleBunDebugRoot.js +24 -0
- package/lib/module/features/journey/uiState.js +79 -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/metro/serializerCompose.js +32 -0
- package/lib/module/public/ScaleBunFacade.js +74 -11
- package/lib/typescript/core/config/schema.d.ts +2 -0
- package/lib/typescript/core/constants/version.d.ts +1 -1
- package/lib/typescript/features/journey/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/public/ScaleBunFacade.d.ts +35 -6
- package/lib/typescript/specs/NativeScaleBunOta.d.ts +23 -0
- package/package.json +18 -3
- package/src/core/config/schema.ts +30 -3
- package/src/core/constants/version.ts +1 -1
- package/src/features/journey/ScaleBunDebugRoot.tsx +22 -0
- package/src/features/journey/uiState.ts +84 -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/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.10.
|
|
608
|
+
SDK_VERSION = "1.10.7";
|
|
609
609
|
}
|
|
610
610
|
});
|
|
611
611
|
|
|
@@ -11611,10 +11611,249 @@ var init_deviceAttributes = __esm({
|
|
|
11611
11611
|
}
|
|
11612
11612
|
});
|
|
11613
11613
|
|
|
11614
|
+
// lib/module/features/ota/crypto/loadEd25519.js
|
|
11615
|
+
function loadEd25519() {
|
|
11616
|
+
try {
|
|
11617
|
+
const mod = require("@noble/ed25519");
|
|
11618
|
+
const ed = mod?.default ?? mod;
|
|
11619
|
+
if (typeof ed?.verifyAsync === "function" || typeof ed?.verify === "function") {
|
|
11620
|
+
return ed;
|
|
11621
|
+
}
|
|
11622
|
+
return null;
|
|
11623
|
+
} catch {
|
|
11624
|
+
return null;
|
|
11625
|
+
}
|
|
11626
|
+
}
|
|
11627
|
+
var init_loadEd25519 = __esm({
|
|
11628
|
+
"lib/module/features/ota/crypto/loadEd25519.js"() {
|
|
11629
|
+
"use strict";
|
|
11630
|
+
}
|
|
11631
|
+
});
|
|
11632
|
+
|
|
11633
|
+
// lib/module/features/ota/crypto/loadSha512.js
|
|
11634
|
+
function loadSha512() {
|
|
11635
|
+
try {
|
|
11636
|
+
const mod = require("@noble/hashes/sha2.js");
|
|
11637
|
+
const sha512 = mod?.sha512 ?? mod?.default?.sha512;
|
|
11638
|
+
return typeof sha512 === "function" ? sha512 : null;
|
|
11639
|
+
} catch {
|
|
11640
|
+
return null;
|
|
11641
|
+
}
|
|
11642
|
+
}
|
|
11643
|
+
var init_loadSha512 = __esm({
|
|
11644
|
+
"lib/module/features/ota/crypto/loadSha512.js"() {
|
|
11645
|
+
"use strict";
|
|
11646
|
+
}
|
|
11647
|
+
});
|
|
11648
|
+
|
|
11649
|
+
// lib/module/features/ota/crypto/builtinVerifier.js
|
|
11650
|
+
var builtinVerifier_exports = {};
|
|
11651
|
+
__export(builtinVerifier_exports, {
|
|
11652
|
+
_resetBuiltinVerifier: () => _resetBuiltinVerifier,
|
|
11653
|
+
decodeSignature: () => decodeSignature,
|
|
11654
|
+
getBuiltinVerifier: () => getBuiltinVerifier
|
|
11655
|
+
});
|
|
11656
|
+
function hexToBytes(hex) {
|
|
11657
|
+
const clean2 = hex.trim().toLowerCase().replace(/^0x/, "");
|
|
11658
|
+
if (clean2.length === 0 || clean2.length % 2 !== 0 || /[^0-9a-f]/.test(clean2)) return null;
|
|
11659
|
+
const out = new Uint8Array(clean2.length / 2);
|
|
11660
|
+
for (let i = 0; i < out.length; i++) {
|
|
11661
|
+
out[i] = parseInt(clean2.slice(i * 2, i * 2 + 2), 16);
|
|
11662
|
+
}
|
|
11663
|
+
return out;
|
|
11664
|
+
}
|
|
11665
|
+
function base64ToBytes2(b64) {
|
|
11666
|
+
const s = b64.trim().replace(/-/g, "+").replace(/_/g, "/").replace(/=+$/, "");
|
|
11667
|
+
if (s.length === 0) return null;
|
|
11668
|
+
const out = [];
|
|
11669
|
+
let buffer = 0;
|
|
11670
|
+
let bits = 0;
|
|
11671
|
+
for (const ch of s) {
|
|
11672
|
+
const idx = B64_ALPHABET2.indexOf(ch);
|
|
11673
|
+
if (idx === -1) return null;
|
|
11674
|
+
buffer = buffer << 6 | idx;
|
|
11675
|
+
bits += 6;
|
|
11676
|
+
if (bits >= 8) {
|
|
11677
|
+
bits -= 8;
|
|
11678
|
+
out.push(buffer >> bits & 255);
|
|
11679
|
+
}
|
|
11680
|
+
}
|
|
11681
|
+
return Uint8Array.from(out);
|
|
11682
|
+
}
|
|
11683
|
+
function decodeSignature(sig) {
|
|
11684
|
+
const trimmed = sig.trim();
|
|
11685
|
+
if (/^(0x)?[0-9a-fA-F]{128}$/.test(trimmed)) {
|
|
11686
|
+
const hex2 = hexToBytes(trimmed);
|
|
11687
|
+
if (hex2 && hex2.length === ED25519_SIGNATURE_BYTES) return hex2;
|
|
11688
|
+
}
|
|
11689
|
+
const b64 = base64ToBytes2(trimmed);
|
|
11690
|
+
if (b64 && b64.length === ED25519_SIGNATURE_BYTES) return b64;
|
|
11691
|
+
const hex = hexToBytes(trimmed);
|
|
11692
|
+
if (hex && hex.length === ED25519_SIGNATURE_BYTES) return hex;
|
|
11693
|
+
return null;
|
|
11694
|
+
}
|
|
11695
|
+
function wireSha512(ed, sha512) {
|
|
11696
|
+
const concat2 = (parts) => {
|
|
11697
|
+
if (parts.length === 1) return parts[0];
|
|
11698
|
+
let total = 0;
|
|
11699
|
+
for (const p of parts) total += p.length;
|
|
11700
|
+
const out = new Uint8Array(total);
|
|
11701
|
+
let off = 0;
|
|
11702
|
+
for (const p of parts) {
|
|
11703
|
+
out.set(p, off);
|
|
11704
|
+
off += p.length;
|
|
11705
|
+
}
|
|
11706
|
+
return out;
|
|
11707
|
+
};
|
|
11708
|
+
const sync = (...msgs) => sha512(concat2(msgs));
|
|
11709
|
+
const asyncFn = async (...msgs) => sha512(concat2(msgs));
|
|
11710
|
+
let wired = false;
|
|
11711
|
+
try {
|
|
11712
|
+
if (ed.hashes && typeof ed.hashes === "object") {
|
|
11713
|
+
if (typeof ed.hashes.sha512 !== "function") ed.hashes.sha512 = sync;
|
|
11714
|
+
if (typeof ed.hashes.sha512Async !== "function") ed.hashes.sha512Async = asyncFn;
|
|
11715
|
+
wired = typeof ed.hashes.sha512 === "function";
|
|
11716
|
+
}
|
|
11717
|
+
} catch {
|
|
11718
|
+
}
|
|
11719
|
+
try {
|
|
11720
|
+
if (ed.etc && typeof ed.etc === "object") {
|
|
11721
|
+
if (typeof ed.etc.sha512Sync !== "function") ed.etc.sha512Sync = sync;
|
|
11722
|
+
if (typeof ed.etc.sha512Async !== "function") ed.etc.sha512Async = asyncFn;
|
|
11723
|
+
wired = wired || typeof ed.etc.sha512Sync === "function";
|
|
11724
|
+
}
|
|
11725
|
+
} catch {
|
|
11726
|
+
}
|
|
11727
|
+
return wired;
|
|
11728
|
+
}
|
|
11729
|
+
function getBuiltinVerifier() {
|
|
11730
|
+
if (resolved !== void 0) return resolved;
|
|
11731
|
+
const ed = loadEd25519();
|
|
11732
|
+
const sha512 = loadSha512();
|
|
11733
|
+
if (!ed || !sha512) {
|
|
11734
|
+
resolved = null;
|
|
11735
|
+
return null;
|
|
11736
|
+
}
|
|
11737
|
+
if (!wireSha512(ed, sha512)) {
|
|
11738
|
+
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.");
|
|
11739
|
+
resolved = null;
|
|
11740
|
+
return null;
|
|
11741
|
+
}
|
|
11742
|
+
resolved = async ({
|
|
11743
|
+
messageHex,
|
|
11744
|
+
signature,
|
|
11745
|
+
publicKey
|
|
11746
|
+
}) => {
|
|
11747
|
+
const message = hexToBytes(messageHex);
|
|
11748
|
+
const pub = hexToBytes(publicKey);
|
|
11749
|
+
const sig = decodeSignature(signature);
|
|
11750
|
+
if (!message) {
|
|
11751
|
+
logger.error("[OTA] Bundle SHA-256 is not valid hex \u2014 refusing to stage.");
|
|
11752
|
+
return false;
|
|
11753
|
+
}
|
|
11754
|
+
if (!pub || pub.length !== ED25519_PUBLIC_KEY_BYTES) {
|
|
11755
|
+
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"}.`);
|
|
11756
|
+
return false;
|
|
11757
|
+
}
|
|
11758
|
+
if (!sig) {
|
|
11759
|
+
logger.error("[OTA] Bundle signature is not a 64-byte hex/base64 value.");
|
|
11760
|
+
return false;
|
|
11761
|
+
}
|
|
11762
|
+
try {
|
|
11763
|
+
if (typeof ed.verify === "function") return ed.verify(sig, message, pub);
|
|
11764
|
+
if (typeof ed.verifyAsync === "function") return await ed.verifyAsync(sig, message, pub);
|
|
11765
|
+
return false;
|
|
11766
|
+
} catch {
|
|
11767
|
+
return false;
|
|
11768
|
+
}
|
|
11769
|
+
};
|
|
11770
|
+
__DEV__ && logger.debug("[OTA] Using built-in ed25519 verifier (@noble/ed25519 detected).");
|
|
11771
|
+
return resolved;
|
|
11772
|
+
}
|
|
11773
|
+
function _resetBuiltinVerifier() {
|
|
11774
|
+
resolved = void 0;
|
|
11775
|
+
}
|
|
11776
|
+
var ED25519_SIGNATURE_BYTES, ED25519_PUBLIC_KEY_BYTES, B64_ALPHABET2, resolved;
|
|
11777
|
+
var init_builtinVerifier = __esm({
|
|
11778
|
+
"lib/module/features/ota/crypto/builtinVerifier.js"() {
|
|
11779
|
+
"use strict";
|
|
11780
|
+
init_loadEd25519();
|
|
11781
|
+
init_loadSha512();
|
|
11782
|
+
init_internalLogger();
|
|
11783
|
+
ED25519_SIGNATURE_BYTES = 64;
|
|
11784
|
+
ED25519_PUBLIC_KEY_BYTES = 32;
|
|
11785
|
+
B64_ALPHABET2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
11786
|
+
}
|
|
11787
|
+
});
|
|
11788
|
+
|
|
11789
|
+
// lib/module/features/ota/crypto/nativeVerifier.js
|
|
11790
|
+
function bytesToHex(bytes) {
|
|
11791
|
+
let out = "";
|
|
11792
|
+
for (const b of bytes) out += b.toString(16).padStart(2, "0");
|
|
11793
|
+
return out;
|
|
11794
|
+
}
|
|
11795
|
+
function normalizeHex(value, expectedChars) {
|
|
11796
|
+
const clean2 = value.trim().toLowerCase().replace(/^0x/, "");
|
|
11797
|
+
if (clean2.length !== expectedChars || /[^0-9a-f]/.test(clean2)) return null;
|
|
11798
|
+
return clean2;
|
|
11799
|
+
}
|
|
11800
|
+
function getNativeVerifier() {
|
|
11801
|
+
if (resolved2 !== void 0) return resolved2;
|
|
11802
|
+
const native = NativeScaleBunOta_default;
|
|
11803
|
+
if (!native || typeof native.verifyEd25519 !== "function") {
|
|
11804
|
+
resolved2 = null;
|
|
11805
|
+
return null;
|
|
11806
|
+
}
|
|
11807
|
+
resolved2 = async ({
|
|
11808
|
+
messageHex,
|
|
11809
|
+
signature,
|
|
11810
|
+
publicKey
|
|
11811
|
+
}) => {
|
|
11812
|
+
const message = normalizeHex(messageHex, 64);
|
|
11813
|
+
const key = normalizeHex(publicKey, ED25519_PUBLIC_KEY_HEX_CHARS);
|
|
11814
|
+
const sigBytes = decodeSignature(signature);
|
|
11815
|
+
if (!message || !key || !sigBytes) {
|
|
11816
|
+
logger.error("[OTA] Signature inputs malformed (bundle hash, signature, or public key) \u2014 refusing to stage.");
|
|
11817
|
+
return false;
|
|
11818
|
+
}
|
|
11819
|
+
let verdict;
|
|
11820
|
+
try {
|
|
11821
|
+
verdict = await native.verifyEd25519(message, bytesToHex(sigBytes), key);
|
|
11822
|
+
} catch (err) {
|
|
11823
|
+
logger.warn(`[OTA] Native ed25519 verify failed to run: ${err?.message ?? err}`);
|
|
11824
|
+
verdict = "unavailable";
|
|
11825
|
+
}
|
|
11826
|
+
if (verdict === "valid") return true;
|
|
11827
|
+
if (verdict === "invalid") return false;
|
|
11828
|
+
const builtin = getBuiltinVerifier();
|
|
11829
|
+
if (builtin) {
|
|
11830
|
+
return builtin({
|
|
11831
|
+
messageHex,
|
|
11832
|
+
signature,
|
|
11833
|
+
publicKey
|
|
11834
|
+
});
|
|
11835
|
+
}
|
|
11836
|
+
throw new Error("ed25519 unavailable: this OS has no native implementation (Android < 13) and the optional peers @noble/ed25519 + @noble/hashes are not installed.");
|
|
11837
|
+
};
|
|
11838
|
+
__DEV__ && logger.debug("[OTA] Using native ed25519 verifier (platform crypto).");
|
|
11839
|
+
return resolved2;
|
|
11840
|
+
}
|
|
11841
|
+
var ED25519_PUBLIC_KEY_HEX_CHARS, resolved2;
|
|
11842
|
+
var init_nativeVerifier = __esm({
|
|
11843
|
+
"lib/module/features/ota/crypto/nativeVerifier.js"() {
|
|
11844
|
+
"use strict";
|
|
11845
|
+
init_NativeScaleBunOta();
|
|
11846
|
+
init_internalLogger();
|
|
11847
|
+
init_builtinVerifier();
|
|
11848
|
+
ED25519_PUBLIC_KEY_HEX_CHARS = 64;
|
|
11849
|
+
}
|
|
11850
|
+
});
|
|
11851
|
+
|
|
11614
11852
|
// lib/module/features/ota/signature.js
|
|
11615
11853
|
async function verifyBundleSignature(bundleSha256, signature, config) {
|
|
11616
|
-
const
|
|
11617
|
-
|
|
11854
|
+
const rawKey = config?.publicKey;
|
|
11855
|
+
const keys = (Array.isArray(rawKey) ? rawKey : rawKey ? [rawKey] : []).filter((k) => typeof k === "string" && k.trim().length > 0);
|
|
11856
|
+
if (rawKey === void 0 || rawKey === "" || rawKey === null) {
|
|
11618
11857
|
if (!warnedNotConfigured) {
|
|
11619
11858
|
warnedNotConfigured = true;
|
|
11620
11859
|
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 +11863,13 @@ async function verifyBundleSignature(bundleSha256, signature, config) {
|
|
|
11624
11863
|
reason: "not_configured"
|
|
11625
11864
|
};
|
|
11626
11865
|
}
|
|
11866
|
+
if (keys.length === 0) {
|
|
11867
|
+
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.");
|
|
11868
|
+
return {
|
|
11869
|
+
ok: false,
|
|
11870
|
+
reason: "no_keys"
|
|
11871
|
+
};
|
|
11872
|
+
}
|
|
11627
11873
|
if (!signature) {
|
|
11628
11874
|
logger.error("[OTA] Bundle has no signature but a signing key is configured \u2014 refusing to stage.");
|
|
11629
11875
|
return {
|
|
@@ -11631,43 +11877,52 @@ async function verifyBundleSignature(bundleSha256, signature, config) {
|
|
|
11631
11877
|
reason: "missing_signature"
|
|
11632
11878
|
};
|
|
11633
11879
|
}
|
|
11634
|
-
|
|
11635
|
-
|
|
11880
|
+
const verifier = typeof config?.verifier === "function" ? config.verifier : getNativeVerifier() ?? getBuiltinVerifier();
|
|
11881
|
+
if (!verifier) {
|
|
11882
|
+
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
11883
|
return {
|
|
11637
11884
|
ok: false,
|
|
11638
11885
|
reason: "no_verifier"
|
|
11639
11886
|
};
|
|
11640
11887
|
}
|
|
11641
|
-
|
|
11642
|
-
|
|
11643
|
-
|
|
11644
|
-
|
|
11645
|
-
|
|
11646
|
-
|
|
11647
|
-
|
|
11648
|
-
|
|
11649
|
-
|
|
11650
|
-
|
|
11651
|
-
|
|
11888
|
+
let sawThrow = false;
|
|
11889
|
+
let sawCompletion = false;
|
|
11890
|
+
for (const key of keys) {
|
|
11891
|
+
try {
|
|
11892
|
+
const valid = await verifier({
|
|
11893
|
+
messageHex: bundleSha256,
|
|
11894
|
+
signature,
|
|
11895
|
+
publicKey: key
|
|
11896
|
+
});
|
|
11897
|
+
sawCompletion = true;
|
|
11898
|
+
if (valid) return {
|
|
11899
|
+
ok: true,
|
|
11900
|
+
reason: "verified"
|
|
11652
11901
|
};
|
|
11902
|
+
} catch (err) {
|
|
11903
|
+
sawThrow = true;
|
|
11904
|
+
logger.error(`[OTA] Signature verifier threw: ${err?.message ?? err}`);
|
|
11653
11905
|
}
|
|
11654
|
-
|
|
11655
|
-
|
|
11656
|
-
reason: "verified"
|
|
11657
|
-
};
|
|
11658
|
-
} catch (err) {
|
|
11659
|
-
logger.error(`[OTA] Signature verifier threw: ${err?.message ?? err}`);
|
|
11906
|
+
}
|
|
11907
|
+
if (sawThrow && !sawCompletion) {
|
|
11660
11908
|
return {
|
|
11661
11909
|
ok: false,
|
|
11662
11910
|
reason: "verifier_threw"
|
|
11663
11911
|
};
|
|
11664
11912
|
}
|
|
11913
|
+
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.");
|
|
11914
|
+
return {
|
|
11915
|
+
ok: false,
|
|
11916
|
+
reason: "invalid_signature"
|
|
11917
|
+
};
|
|
11665
11918
|
}
|
|
11666
11919
|
var warnedNotConfigured;
|
|
11667
11920
|
var init_signature = __esm({
|
|
11668
11921
|
"lib/module/features/ota/signature.js"() {
|
|
11669
11922
|
"use strict";
|
|
11670
11923
|
init_internalLogger();
|
|
11924
|
+
init_builtinVerifier();
|
|
11925
|
+
init_nativeVerifier();
|
|
11671
11926
|
warnedNotConfigured = false;
|
|
11672
11927
|
}
|
|
11673
11928
|
});
|
|
@@ -12487,6 +12742,35 @@ var init_OtaOrchestrator = __esm({
|
|
|
12487
12742
|
}
|
|
12488
12743
|
});
|
|
12489
12744
|
|
|
12745
|
+
// lib/module/features/journey/uiState.js
|
|
12746
|
+
var uiState_exports = {};
|
|
12747
|
+
__export(uiState_exports, {
|
|
12748
|
+
clearUiState: () => clearUiState,
|
|
12749
|
+
setUiState: () => setUiState,
|
|
12750
|
+
uiStateSignature: () => uiStateSignature
|
|
12751
|
+
});
|
|
12752
|
+
function setUiState(name, value) {
|
|
12753
|
+
const n = clean(name);
|
|
12754
|
+
const v = clean(value);
|
|
12755
|
+
if (n && v) declared.set(n, v);
|
|
12756
|
+
}
|
|
12757
|
+
function clearUiState(name) {
|
|
12758
|
+
if (name === void 0) declared.clear();
|
|
12759
|
+
else declared.delete(clean(name));
|
|
12760
|
+
}
|
|
12761
|
+
function uiStateSignature() {
|
|
12762
|
+
if (!declared.size) return void 0;
|
|
12763
|
+
return [...declared.keys()].sort().map((k) => `${k}:${declared.get(k)}`).join(";").slice(0, 96);
|
|
12764
|
+
}
|
|
12765
|
+
var declared, clean;
|
|
12766
|
+
var init_uiState = __esm({
|
|
12767
|
+
"lib/module/features/journey/uiState.js"() {
|
|
12768
|
+
"use strict";
|
|
12769
|
+
declared = /* @__PURE__ */ new Map();
|
|
12770
|
+
clean = (s) => typeof s === "string" ? s.replace(/[;:|]/g, "").trim().slice(0, 32) : "";
|
|
12771
|
+
}
|
|
12772
|
+
});
|
|
12773
|
+
|
|
12490
12774
|
// lib/module/features/engage/gestureTriggerDetector.js
|
|
12491
12775
|
var gestureTriggerDetector_exports = {};
|
|
12492
12776
|
__export(gestureTriggerDetector_exports, {
|
|
@@ -14404,6 +14688,14 @@ var SHAPE = {
|
|
|
14404
14688
|
kind: "str",
|
|
14405
14689
|
opt: true
|
|
14406
14690
|
},
|
|
14691
|
+
// Rotation support: several pinned keys, any of which may verify a
|
|
14692
|
+
// bundle. Ship a build pinning [old, new], re-sign server-side with
|
|
14693
|
+
// new, drop old next release — no emergency store submission when a
|
|
14694
|
+
// key must be replaced. Merged with publicSigningKey by the facade.
|
|
14695
|
+
publicSigningKeys: {
|
|
14696
|
+
kind: "strArr",
|
|
14697
|
+
opt: true
|
|
14698
|
+
},
|
|
14407
14699
|
mandatoryBlocksUi: bool(false)
|
|
14408
14700
|
}
|
|
14409
14701
|
}
|
|
@@ -14439,7 +14731,7 @@ function parseField(field, input, path, issues) {
|
|
|
14439
14731
|
value: field.def,
|
|
14440
14732
|
present: true
|
|
14441
14733
|
};
|
|
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");
|
|
14734
|
+
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
14735
|
}
|
|
14444
14736
|
switch (field.kind) {
|
|
14445
14737
|
case "any":
|
|
@@ -14465,6 +14757,27 @@ function parseField(field, input, path, issues) {
|
|
|
14465
14757
|
value: input,
|
|
14466
14758
|
present: true
|
|
14467
14759
|
};
|
|
14760
|
+
case "strArr": {
|
|
14761
|
+
if (!Array.isArray(input)) return fail(`Expected array, received ${typeofName2(input)}`);
|
|
14762
|
+
let allOk = true;
|
|
14763
|
+
for (let i = 0; i < input.length; i++) {
|
|
14764
|
+
if (typeof input[i] !== "string") {
|
|
14765
|
+
issues.push({
|
|
14766
|
+
path: [...path, i],
|
|
14767
|
+
message: `Expected string, received ${typeofName2(input[i])}`
|
|
14768
|
+
});
|
|
14769
|
+
allOk = false;
|
|
14770
|
+
}
|
|
14771
|
+
}
|
|
14772
|
+
return allOk ? {
|
|
14773
|
+
ok: true,
|
|
14774
|
+
value: [...input],
|
|
14775
|
+
present: true
|
|
14776
|
+
} : {
|
|
14777
|
+
ok: false,
|
|
14778
|
+
present: true
|
|
14779
|
+
};
|
|
14780
|
+
}
|
|
14468
14781
|
case "num":
|
|
14469
14782
|
if (typeof input !== "number" || Number.isNaN(input)) {
|
|
14470
14783
|
return fail(`Expected number, received ${typeofName2(input)}`);
|
|
@@ -15668,16 +15981,16 @@ function base64ToBytes(base64) {
|
|
|
15668
15981
|
return decodeManual(base64);
|
|
15669
15982
|
}
|
|
15670
15983
|
function decodeManual(base64) {
|
|
15671
|
-
const
|
|
15672
|
-
const pad =
|
|
15673
|
-
const outLen = Math.floor(
|
|
15984
|
+
const clean2 = base64.replace(/[^A-Za-z0-9+/=]/g, "");
|
|
15985
|
+
const pad = clean2.endsWith("==") ? 2 : clean2.endsWith("=") ? 1 : 0;
|
|
15986
|
+
const outLen = Math.floor(clean2.length * 3 / 4) - pad;
|
|
15674
15987
|
const out = new Uint8Array(outLen);
|
|
15675
15988
|
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(
|
|
15989
|
+
for (let i = 0; i < clean2.length; i += 4) {
|
|
15990
|
+
const c0 = B64_ALPHABET.indexOf(clean2[i]);
|
|
15991
|
+
const c1 = B64_ALPHABET.indexOf(clean2[i + 1]);
|
|
15992
|
+
const c2 = B64_ALPHABET.indexOf(clean2[i + 2]);
|
|
15993
|
+
const c3 = B64_ALPHABET.indexOf(clean2[i + 3]);
|
|
15681
15994
|
const n = c0 << 18 | c1 << 12 | (c2 & 63) << 6 | c3 & 63;
|
|
15682
15995
|
if (p < outLen) out[p++] = n >> 16 & 255;
|
|
15683
15996
|
if (p < outLen) out[p++] = n >> 8 & 255;
|
|
@@ -19865,12 +20178,17 @@ var ScaleBunFacade = class {
|
|
|
19865
20178
|
/**
|
|
19866
20179
|
* Boot the OTA orchestrator when the init config asks for it.
|
|
19867
20180
|
*
|
|
19868
|
-
* `publicSigningKey`
|
|
19869
|
-
* from configuration alone. The
|
|
19870
|
-
*
|
|
19871
|
-
*
|
|
19872
|
-
*
|
|
19873
|
-
*
|
|
20181
|
+
* `publicSigningKey` / `publicSigningKeys` are honoured here so signature
|
|
20182
|
+
* enforcement is reachable from configuration alone. The list form exists
|
|
20183
|
+
* for key ROTATION: a build pinning [old, new] keeps verifying while the
|
|
20184
|
+
* server moves to the new key, so replacing a key never needs an emergency
|
|
20185
|
+
* store release. Both fields merge (deduplicated) into one pinned set.
|
|
20186
|
+
*
|
|
20187
|
+
* Verification comes from `ota.verifySignature` when supplied, else the
|
|
20188
|
+
* built-in @noble-based verifier (optional peers). Pinning keys with
|
|
20189
|
+
* NEITHER available is fail-CLOSED by design (signature.ts) — an update
|
|
20190
|
+
* that cannot be verified is not installed. We say that out loud rather
|
|
20191
|
+
* than letting the app discover it as a silent no-update condition.
|
|
19874
20192
|
*/
|
|
19875
20193
|
_maybeStartOta(rawConfig) {
|
|
19876
20194
|
const ota = rawConfig?.ota;
|
|
@@ -19879,15 +20197,25 @@ var ScaleBunFacade = class {
|
|
|
19879
20197
|
const {
|
|
19880
20198
|
otaOrchestrator: otaOrchestrator2
|
|
19881
20199
|
} = (init_OtaOrchestrator(), __toCommonJS(OtaOrchestrator_exports));
|
|
19882
|
-
const
|
|
20200
|
+
const rawSingle = ota.publicSigningKey;
|
|
20201
|
+
const rawList = ota.publicSigningKeys;
|
|
20202
|
+
const publicKeys = Array.from(new Set([...typeof rawSingle === "string" && rawSingle ? [rawSingle] : [], ...Array.isArray(rawList) ? rawList : []].filter((k) => typeof k === "string" && k.trim().length > 0)));
|
|
20203
|
+
const signingRequested = publicKeys.length > 0 || Array.isArray(rawList);
|
|
19883
20204
|
const verifier = ota.verifySignature;
|
|
19884
|
-
if (
|
|
19885
|
-
|
|
20205
|
+
if (signingRequested && typeof verifier !== "function") {
|
|
20206
|
+
const {
|
|
20207
|
+
getBuiltinVerifier: getBuiltinVerifier2
|
|
20208
|
+
} = (init_builtinVerifier(), __toCommonJS(builtinVerifier_exports));
|
|
20209
|
+
if (!getBuiltinVerifier2()) {
|
|
20210
|
+
logger.warn("[ScaleBun] A signing key is pinned but no signature verifier is available. Signature checking is fail-closed: updates will be REJECTED until one exists. Install the optional peers `@noble/ed25519` + `@noble/hashes` (no further code needed), or supply `ota.verifySignature`.");
|
|
20211
|
+
}
|
|
19886
20212
|
}
|
|
19887
20213
|
otaOrchestrator2.init({
|
|
19888
|
-
...
|
|
20214
|
+
...signingRequested ? {
|
|
19889
20215
|
signature: {
|
|
19890
|
-
|
|
20216
|
+
// Single key stays a plain string — the shape every
|
|
20217
|
+
// existing consumer and test already handles.
|
|
20218
|
+
publicKey: publicKeys.length === 1 ? publicKeys[0] : publicKeys,
|
|
19891
20219
|
verifier
|
|
19892
20220
|
}
|
|
19893
20221
|
} : {},
|
|
@@ -20099,6 +20427,46 @@ var ScaleBunFacade = class {
|
|
|
20099
20427
|
};
|
|
20100
20428
|
}
|
|
20101
20429
|
/** Convenience: emit a Phase 1 purchase event (revenue + currency + transaction_id). */
|
|
20430
|
+
/**
|
|
20431
|
+
* Declare which UI state the user is looking at, so taps are attributed to the surface they
|
|
20432
|
+
* happened on rather than averaged across every variant of the screen.
|
|
20433
|
+
*
|
|
20434
|
+
* ScaleBun.setUiState('filter-sheet', 'open');
|
|
20435
|
+
* ScaleBun.setUiState('checkout-step', 'payment');
|
|
20436
|
+
*
|
|
20437
|
+
* Call it when the state CHANGES — the value is read at tap time, so it only has to be right by
|
|
20438
|
+
* the time the next tap lands. On React Native this is the ONLY source of UI state: unlike the web,
|
|
20439
|
+
* there is no queryable accessibility tree that says a sheet is up, and guessing would produce a
|
|
20440
|
+
* state key that is right on some apps and silently wrong on others.
|
|
20441
|
+
*
|
|
20442
|
+
* Names and values are identifiers, not content: they become query keys in the dashboard, so a
|
|
20443
|
+
* person's name or a cart total does not belong in one. `;`, `:` and `|` are stripped and both
|
|
20444
|
+
* halves are capped at 32 characters.
|
|
20445
|
+
*/
|
|
20446
|
+
setUiState(name, value) {
|
|
20447
|
+
try {
|
|
20448
|
+
const {
|
|
20449
|
+
setUiState: setUiState2
|
|
20450
|
+
} = (init_uiState(), __toCommonJS(uiState_exports));
|
|
20451
|
+
setUiState2(name, value);
|
|
20452
|
+
} catch {
|
|
20453
|
+
}
|
|
20454
|
+
}
|
|
20455
|
+
/**
|
|
20456
|
+
* Stop reporting a UI state dimension — or, with no argument, all of them.
|
|
20457
|
+
*
|
|
20458
|
+
* Clear on screen unmount. A declaration left behind follows the user onto a surface where it means
|
|
20459
|
+
* nothing, and every tap there is filed under a state that was not on screen.
|
|
20460
|
+
*/
|
|
20461
|
+
clearUiState(name) {
|
|
20462
|
+
try {
|
|
20463
|
+
const {
|
|
20464
|
+
clearUiState: clearUiState2
|
|
20465
|
+
} = (init_uiState(), __toCommonJS(uiState_exports));
|
|
20466
|
+
clearUiState2(name);
|
|
20467
|
+
} catch {
|
|
20468
|
+
}
|
|
20469
|
+
}
|
|
20102
20470
|
trackPurchase(input) {
|
|
20103
20471
|
noThrow(() => this._eventTracker?.trackPurchase(input));
|
|
20104
20472
|
}
|
|
@@ -23722,11 +24090,11 @@ var normalizedWheelValue = (value) => value.toLowerCase().replace(/\s+/g, " ").t
|
|
|
23722
24090
|
function resolveWheelOutcome(segments, source) {
|
|
23723
24091
|
const identity = wheelOutcomeIdentity(source);
|
|
23724
24092
|
const index = findWheelOutcomeIndex(segments, identity);
|
|
23725
|
-
const
|
|
24093
|
+
const resolved3 = index >= 0;
|
|
23726
24094
|
return {
|
|
23727
|
-
index:
|
|
23728
|
-
label: identity.resultLabel || (
|
|
23729
|
-
resolved
|
|
24095
|
+
index: resolved3 ? index : 0,
|
|
24096
|
+
label: identity.resultLabel || (resolved3 ? segments[index]?.fullLabel ?? "" : ""),
|
|
24097
|
+
resolved: resolved3
|
|
23730
24098
|
};
|
|
23731
24099
|
}
|
|
23732
24100
|
function SpinWheelInApp(props) {
|
|
@@ -32585,8 +32953,8 @@ function EngagePromptProvider({
|
|
|
32585
32953
|
__DEV__ && logger.debug("[Engage] no eligible in-app message to render");
|
|
32586
32954
|
return false;
|
|
32587
32955
|
}
|
|
32588
|
-
const
|
|
32589
|
-
activeInAppVariantRef.current = message.experiment?.enabled ?
|
|
32956
|
+
const resolved3 = selectInAppVariant(message, getDeviceId());
|
|
32957
|
+
activeInAppVariantRef.current = message.experiment?.enabled ? resolved3 : null;
|
|
32590
32958
|
store.recordImpression(message.id, Date.now(), message.revision);
|
|
32591
32959
|
setActiveInApp(message);
|
|
32592
32960
|
return true;
|
|
@@ -33294,9 +33662,22 @@ function ScaleBunDebugRoot({
|
|
|
33294
33662
|
screen = AutoScreenDetector2.getInstance().getCurrentScreen() || void 0;
|
|
33295
33663
|
} catch {
|
|
33296
33664
|
}
|
|
33665
|
+
let ui;
|
|
33666
|
+
try {
|
|
33667
|
+
const {
|
|
33668
|
+
uiStateSignature: uiStateSignature2
|
|
33669
|
+
} = (init_uiState(), __toCommonJS(uiState_exports));
|
|
33670
|
+
ui = uiStateSignature2();
|
|
33671
|
+
} catch {
|
|
33672
|
+
}
|
|
33297
33673
|
emitAutomaticEvent("element_interacted", {
|
|
33298
33674
|
gesture_type: gestureType,
|
|
33299
33675
|
screen_name: screen,
|
|
33676
|
+
/* THIS MAP ENUMERATES. A field added to the payload and forgotten here reaches the
|
|
33677
|
+
backend as undefined with no error anywhere — the recurring defect class in this
|
|
33678
|
+
codebase. `ui` is the key the grid aggregate reads for state, byte-identical to
|
|
33679
|
+
the web SDK's, so one dashboard control queries both platforms. */
|
|
33680
|
+
ui,
|
|
33300
33681
|
// testID/nativeID is an author-controlled stable identifier.
|
|
33301
33682
|
// Accessibility labels and rendered text are deliberately omitted.
|
|
33302
33683
|
target_id: targetInfo?.testId,
|