@hot-updater/react-native 0.28.0 → 0.29.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/hotupdater/BundleFileStorageService.kt +156 -7
- package/android/src/main/java/com/hotupdater/CohortService.kt +73 -0
- package/android/src/main/java/com/hotupdater/DecompressService.kt +28 -22
- package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +1 -1
- package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +12 -0
- package/android/src/main/java/com/hotupdater/ReactNativeValueConverters.kt +55 -0
- package/android/src/main/java/com/hotupdater/TarBrDecompressionStrategy.kt +19 -7
- package/android/src/newarch/HotUpdaterModule.kt +16 -19
- package/android/src/oldarch/HotUpdaterModule.kt +20 -20
- package/android/src/oldarch/HotUpdaterSpec.kt +12 -2
- package/ios/HotUpdater/Internal/BundleFileStorageService.swift +153 -31
- package/ios/HotUpdater/Internal/CohortService.swift +63 -0
- package/ios/HotUpdater/Internal/DecompressService.swift +53 -30
- package/ios/HotUpdater/Internal/HotUpdater.mm +111 -59
- package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +28 -0
- package/ios/HotUpdater/Internal/TarBrDecompressionStrategy.swift +24 -8
- package/lib/commonjs/DefaultResolver.js +3 -5
- package/lib/commonjs/DefaultResolver.js.map +1 -1
- package/lib/commonjs/checkForUpdate.js +2 -0
- package/lib/commonjs/checkForUpdate.js.map +1 -1
- package/lib/commonjs/index.js +13 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/native.js +193 -18
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/native.spec.js +361 -4
- package/lib/commonjs/native.spec.js.map +1 -1
- package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
- package/lib/commonjs/types.js.map +1 -1
- package/lib/module/DefaultResolver.js +3 -5
- package/lib/module/DefaultResolver.js.map +1 -1
- package/lib/module/checkForUpdate.js +3 -1
- package/lib/module/checkForUpdate.js.map +1 -1
- package/lib/module/index.js +14 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/native.js +187 -14
- package/lib/module/native.js.map +1 -1
- package/lib/module/native.spec.js +361 -4
- package/lib/module/native.spec.js.map +1 -1
- package/lib/module/specs/NativeHotUpdater.js.map +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +14 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/native.d.ts +39 -8
- package/lib/typescript/commonjs/native.d.ts.map +1 -1
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +28 -0
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/commonjs/types.d.ts +4 -0
- package/lib/typescript/commonjs/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/wrap.d.ts +1 -1
- package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +14 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/native.d.ts +39 -8
- package/lib/typescript/module/native.d.ts.map +1 -1
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts +28 -0
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/module/types.d.ts +4 -0
- package/lib/typescript/module/types.d.ts.map +1 -1
- package/lib/typescript/module/wrap.d.ts +1 -1
- package/package.json +6 -6
- package/src/DefaultResolver.ts +4 -4
- package/src/checkForUpdate.ts +4 -0
- package/src/index.ts +21 -0
- package/src/native.spec.ts +400 -4
- package/src/native.ts +265 -20
- package/src/specs/NativeHotUpdater.ts +32 -0
- package/src/types.ts +5 -0
- package/src/wrap.tsx +1 -1
- package/lib/typescript/commonjs/native.spec.d.ts +0 -2
- package/lib/typescript/commonjs/native.spec.d.ts.map +0 -1
- package/lib/typescript/module/native.spec.d.ts +0 -2
- package/lib/typescript/module/native.spec.d.ts.map +0 -1
package/src/native.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
INVALID_COHORT_ERROR_MESSAGE,
|
|
3
|
+
isValidCohort,
|
|
4
|
+
normalizeCohortValue,
|
|
5
|
+
type UpdateStatus,
|
|
6
|
+
} from "@hot-updater/core";
|
|
2
7
|
import { NativeEventEmitter, Platform } from "react-native";
|
|
3
8
|
import { HotUpdaterErrorCode, isHotUpdaterError } from "./error";
|
|
4
9
|
import HotUpdaterNative, {
|
|
@@ -8,6 +13,30 @@ import HotUpdaterNative, {
|
|
|
8
13
|
export { HotUpdaterErrorCode, isHotUpdaterError };
|
|
9
14
|
|
|
10
15
|
const NIL_UUID = "00000000-0000-0000-0000-000000000000";
|
|
16
|
+
const normalizeAndValidateCohort = (cohort: string): string => {
|
|
17
|
+
const normalized = normalizeCohortValue(cohort);
|
|
18
|
+
if (!isValidCohort(normalized)) {
|
|
19
|
+
throw new Error(INVALID_COHORT_ERROR_MESSAGE);
|
|
20
|
+
}
|
|
21
|
+
return normalized;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export interface ManifestAsset {
|
|
25
|
+
fileHash: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface Manifest {
|
|
29
|
+
bundleId: string;
|
|
30
|
+
assets: Record<string, ManifestAsset>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type ActiveBundleSnapshotCacheValues = {
|
|
34
|
+
bundleId?: string;
|
|
35
|
+
manifest?: Manifest;
|
|
36
|
+
baseURL?: string | null;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
type ActiveBundleSnapshotCacheKey = keyof ActiveBundleSnapshotCacheValues;
|
|
11
40
|
|
|
12
41
|
/**
|
|
13
42
|
* Built-in reload behaviors used by `HotUpdater.reload()`.
|
|
@@ -34,17 +63,16 @@ export type CustomReloadHandler = () => void | Promise<void>;
|
|
|
34
63
|
*/
|
|
35
64
|
export type ReloadBehaviorSetting = ReloadBehavior | "custom";
|
|
36
65
|
|
|
37
|
-
declare const __HOT_UPDATER_BUNDLE_ID: string | undefined;
|
|
38
|
-
|
|
39
|
-
export const HotUpdaterConstants = {
|
|
40
|
-
HOT_UPDATER_BUNDLE_ID: __HOT_UPDATER_BUNDLE_ID || NIL_UUID,
|
|
41
|
-
};
|
|
42
|
-
|
|
43
66
|
class HotUpdaterSessionState {
|
|
44
67
|
private readonly defaultChannel: string;
|
|
45
68
|
private currentChannel: string;
|
|
69
|
+
private cachedCohort: string | undefined;
|
|
46
70
|
private readonly inflightUpdates = new Map<string, Promise<boolean>>();
|
|
47
71
|
private lastInstalledBundleId: string | null = null;
|
|
72
|
+
private readonly activeBundleSnapshotCache = new Map<
|
|
73
|
+
ActiveBundleSnapshotCacheKey,
|
|
74
|
+
ActiveBundleSnapshotCacheValues[ActiveBundleSnapshotCacheKey]
|
|
75
|
+
>();
|
|
48
76
|
|
|
49
77
|
constructor() {
|
|
50
78
|
const constants = HotUpdaterNative.getConstants();
|
|
@@ -85,12 +113,66 @@ class HotUpdaterSessionState {
|
|
|
85
113
|
if (channel) {
|
|
86
114
|
this.currentChannel = channel;
|
|
87
115
|
}
|
|
116
|
+
this.clearActiveBundleSnapshotCache();
|
|
88
117
|
}
|
|
89
118
|
|
|
90
119
|
resetChannelState() {
|
|
91
120
|
this.currentChannel = this.defaultChannel;
|
|
92
121
|
this.lastInstalledBundleId = null;
|
|
93
122
|
this.inflightUpdates.clear();
|
|
123
|
+
this.clearActiveBundleSnapshotCache();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
getCachedBundleId(): string | undefined {
|
|
127
|
+
return this.getActiveBundleSnapshotValue("bundleId");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
cacheBundleId(bundleId: string) {
|
|
131
|
+
this.setActiveBundleSnapshotValue("bundleId", bundleId);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
getCachedManifest(): Manifest | undefined {
|
|
135
|
+
const manifest = this.getActiveBundleSnapshotValue("manifest");
|
|
136
|
+
return manifest ? cloneManifest(manifest) : undefined;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
cacheManifest(manifest: Manifest) {
|
|
140
|
+
this.setActiveBundleSnapshotValue("manifest", cloneManifest(manifest));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
getCachedBaseURL(): string | null | undefined {
|
|
144
|
+
return this.getActiveBundleSnapshotValue("baseURL");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
cacheBaseURL(baseURL: string | null) {
|
|
148
|
+
this.setActiveBundleSnapshotValue("baseURL", baseURL);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
getCachedCohort(): string | undefined {
|
|
152
|
+
return this.cachedCohort;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
cacheCohort(cohort: string) {
|
|
156
|
+
this.cachedCohort = cohort;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private clearActiveBundleSnapshotCache() {
|
|
160
|
+
this.activeBundleSnapshotCache.clear();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private getActiveBundleSnapshotValue<K extends ActiveBundleSnapshotCacheKey>(
|
|
164
|
+
key: K,
|
|
165
|
+
): ActiveBundleSnapshotCacheValues[K] | undefined {
|
|
166
|
+
return this.activeBundleSnapshotCache.get(key) as
|
|
167
|
+
| ActiveBundleSnapshotCacheValues[K]
|
|
168
|
+
| undefined;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private setActiveBundleSnapshotValue<K extends ActiveBundleSnapshotCacheKey>(
|
|
172
|
+
key: K,
|
|
173
|
+
value: ActiveBundleSnapshotCacheValues[K],
|
|
174
|
+
) {
|
|
175
|
+
this.activeBundleSnapshotCache.set(key, value);
|
|
94
176
|
}
|
|
95
177
|
}
|
|
96
178
|
|
|
@@ -98,6 +180,16 @@ const sessionState = new HotUpdaterSessionState();
|
|
|
98
180
|
let reloadBehavior: ReloadBehaviorSetting = "processRestart";
|
|
99
181
|
let customReloadHandler: CustomReloadHandler | null = null;
|
|
100
182
|
|
|
183
|
+
const cloneManifest = (manifest: Manifest): Manifest => ({
|
|
184
|
+
bundleId: manifest.bundleId,
|
|
185
|
+
assets: Object.fromEntries(
|
|
186
|
+
Object.entries(manifest.assets).map(([key, asset]) => [
|
|
187
|
+
key,
|
|
188
|
+
{ fileHash: asset.fileHash },
|
|
189
|
+
]),
|
|
190
|
+
),
|
|
191
|
+
});
|
|
192
|
+
|
|
101
193
|
const getReloadProcess = (): (() => Promise<void>) | null => {
|
|
102
194
|
const nativeModule = HotUpdaterNative as typeof HotUpdaterNative & {
|
|
103
195
|
reloadProcess?: () => Promise<void>;
|
|
@@ -318,13 +410,72 @@ export const getMinBundleId = (): string => {
|
|
|
318
410
|
/**
|
|
319
411
|
* Fetches the current bundle version id.
|
|
320
412
|
*
|
|
321
|
-
*
|
|
322
|
-
*
|
|
413
|
+
* JS falls back to MIN_BUNDLE_ID only after native confirms there is no active
|
|
414
|
+
* downloaded bundle. When the native module does not expose `getBundleId()`,
|
|
415
|
+
* treat it as a JS/native SDK mismatch instead of silently reporting the
|
|
416
|
+
* built-in bundle.
|
|
417
|
+
*
|
|
418
|
+
* @returns {string} Resolves with the current version id.
|
|
323
419
|
*/
|
|
324
420
|
export const getBundleId = (): string => {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
421
|
+
const cachedBundleId = sessionState.getCachedBundleId();
|
|
422
|
+
if (cachedBundleId !== undefined) {
|
|
423
|
+
return cachedBundleId;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const nativeModule = HotUpdaterNative as typeof HotUpdaterNative & {
|
|
427
|
+
getBundleId?: () => string | null;
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
if (typeof nativeModule.getBundleId !== "function") {
|
|
431
|
+
throw new Error(
|
|
432
|
+
"[HotUpdater] Native module is missing 'getBundleId()'. This JS bundle requires a newer native @hot-updater/react-native SDK. Rebuild and release a new app version before delivering this OTA update.",
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const bundleId = nativeModule.getBundleId();
|
|
437
|
+
|
|
438
|
+
const resolvedBundleId =
|
|
439
|
+
!bundleId || bundleId === NIL_UUID ? getMinBundleId() : bundleId;
|
|
440
|
+
|
|
441
|
+
sessionState.cacheBundleId(resolvedBundleId);
|
|
442
|
+
return resolvedBundleId;
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Fetches the current manifest for the active bundle.
|
|
447
|
+
*
|
|
448
|
+
* Returns a normalized manifest with empty assets when manifest.json is missing
|
|
449
|
+
* or invalid.
|
|
450
|
+
*/
|
|
451
|
+
export const getManifest = (): Manifest => {
|
|
452
|
+
const cachedManifest = sessionState.getCachedManifest();
|
|
453
|
+
if (cachedManifest !== undefined) {
|
|
454
|
+
return cachedManifest;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const nativeModule = HotUpdaterNative as typeof HotUpdaterNative & {
|
|
458
|
+
getManifest?: () => Record<string, unknown> | string;
|
|
459
|
+
};
|
|
460
|
+
const manifest = nativeModule.getManifest?.();
|
|
461
|
+
|
|
462
|
+
let normalizedManifest: Manifest;
|
|
463
|
+
|
|
464
|
+
if (!manifest) {
|
|
465
|
+
normalizedManifest = createEmptyManifest();
|
|
466
|
+
} else if (typeof manifest === "string") {
|
|
467
|
+
try {
|
|
468
|
+
normalizedManifest = normalizeManifest(JSON.parse(manifest));
|
|
469
|
+
} catch {
|
|
470
|
+
normalizedManifest = createEmptyManifest();
|
|
471
|
+
}
|
|
472
|
+
} else {
|
|
473
|
+
normalizedManifest = normalizeManifest(manifest);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
sessionState.cacheBundleId(normalizedManifest.bundleId);
|
|
477
|
+
sessionState.cacheManifest(normalizedManifest);
|
|
478
|
+
return cloneManifest(normalizedManifest);
|
|
328
479
|
};
|
|
329
480
|
|
|
330
481
|
/**
|
|
@@ -383,14 +534,16 @@ export type NotifyAppReadyResult = {
|
|
|
383
534
|
* const result = HotUpdater.notifyAppReady();
|
|
384
535
|
*
|
|
385
536
|
* if (result.status === "RECOVERED") {
|
|
386
|
-
*
|
|
387
|
-
*
|
|
537
|
+
* // Send ROLLBACK analytics event
|
|
538
|
+
* analytics.track("bundle_rollback", {
|
|
539
|
+
* crashedBundleId: result.crashedBundleId,
|
|
540
|
+
* });
|
|
388
541
|
* }
|
|
389
542
|
* ```
|
|
390
543
|
*/
|
|
391
544
|
export const notifyAppReady = (): NotifyAppReadyResult => {
|
|
392
545
|
const result = HotUpdaterNative.notifyAppReady();
|
|
393
|
-
//
|
|
546
|
+
// Older Android old-arch implementations returned JSON strings.
|
|
394
547
|
if (typeof result === "string") {
|
|
395
548
|
try {
|
|
396
549
|
return normalizeNotifyAppReadyResult(JSON.parse(result));
|
|
@@ -414,6 +567,65 @@ const normalizeNotifyAppReadyResult = (
|
|
|
414
567
|
return { status: "STABLE" };
|
|
415
568
|
};
|
|
416
569
|
|
|
570
|
+
const createEmptyManifest = (): Manifest => ({
|
|
571
|
+
bundleId: getBundleId(),
|
|
572
|
+
assets: {},
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
const normalizeManifest = (value: unknown): Manifest => {
|
|
576
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
577
|
+
return createEmptyManifest();
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const bundleIdValue = (value as { bundleId?: unknown }).bundleId;
|
|
581
|
+
const bundleId =
|
|
582
|
+
typeof bundleIdValue === "string" && bundleIdValue.trim()
|
|
583
|
+
? bundleIdValue.trim()
|
|
584
|
+
: getBundleId();
|
|
585
|
+
|
|
586
|
+
return {
|
|
587
|
+
bundleId,
|
|
588
|
+
assets: normalizeManifestAssets((value as { assets?: unknown }).assets),
|
|
589
|
+
};
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
const normalizeManifestAssets = (value: unknown): Manifest["assets"] => {
|
|
593
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
594
|
+
return {};
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
return Object.fromEntries(
|
|
598
|
+
Object.entries(value).flatMap(([key, entry]) => {
|
|
599
|
+
const trimmedKey = key.trim();
|
|
600
|
+
|
|
601
|
+
if (!trimmedKey) {
|
|
602
|
+
return [];
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
if (typeof entry === "string") {
|
|
606
|
+
const fileHash = entry.trim();
|
|
607
|
+
|
|
608
|
+
if (!fileHash) {
|
|
609
|
+
return [];
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
return [[trimmedKey, { fileHash }] as const];
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
616
|
+
return [];
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
const { fileHash } = entry as { fileHash?: unknown };
|
|
620
|
+
if (typeof fileHash !== "string" || !fileHash.trim()) {
|
|
621
|
+
return [];
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
return [[trimmedKey, { fileHash: fileHash.trim() }] as const];
|
|
625
|
+
}),
|
|
626
|
+
);
|
|
627
|
+
};
|
|
628
|
+
|
|
417
629
|
/**
|
|
418
630
|
* Gets the list of bundle IDs that have been marked as crashed.
|
|
419
631
|
* These bundles will be rejected if attempted to install again.
|
|
@@ -422,7 +634,7 @@ const normalizeNotifyAppReadyResult = (
|
|
|
422
634
|
*/
|
|
423
635
|
export const getCrashHistory = (): string[] => {
|
|
424
636
|
const result = HotUpdaterNative.getCrashHistory();
|
|
425
|
-
//
|
|
637
|
+
// Older Android old-arch implementations returned JSON strings.
|
|
426
638
|
if (typeof result === "string") {
|
|
427
639
|
try {
|
|
428
640
|
return JSON.parse(result);
|
|
@@ -451,11 +663,15 @@ export const clearCrashHistory = (): boolean => {
|
|
|
451
663
|
* @returns {string | null} Base URL string (e.g., "file:///data/.../bundle-store/abc123") or null if not available
|
|
452
664
|
*/
|
|
453
665
|
export const getBaseURL = (): string | null => {
|
|
454
|
-
const
|
|
455
|
-
if (
|
|
456
|
-
return
|
|
666
|
+
const cachedBaseURL = sessionState.getCachedBaseURL();
|
|
667
|
+
if (cachedBaseURL !== undefined) {
|
|
668
|
+
return cachedBaseURL;
|
|
457
669
|
}
|
|
458
|
-
|
|
670
|
+
|
|
671
|
+
const result = HotUpdaterNative.getBaseURL();
|
|
672
|
+
const baseURL = typeof result === "string" && result !== "" ? result : null;
|
|
673
|
+
sessionState.cacheBaseURL(baseURL);
|
|
674
|
+
return baseURL;
|
|
459
675
|
};
|
|
460
676
|
|
|
461
677
|
/**
|
|
@@ -472,3 +688,32 @@ export const resetChannel = async (): Promise<boolean> => {
|
|
|
472
688
|
}
|
|
473
689
|
return ok;
|
|
474
690
|
};
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Sets the persisted cohort used for update checks.
|
|
694
|
+
*
|
|
695
|
+
* HotUpdater only derives a device-based cohort when nothing has been stored
|
|
696
|
+
* yet. If you need to restore that initial value later, read it with
|
|
697
|
+
* `getCohort()` before calling `setCohort()`, then store it yourself.
|
|
698
|
+
*/
|
|
699
|
+
export const setCohort = (cohort: string): void => {
|
|
700
|
+
const normalized = normalizeAndValidateCohort(cohort);
|
|
701
|
+
HotUpdaterNative.setCohort(normalized);
|
|
702
|
+
sessionState.cacheCohort(normalized);
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* Gets the persisted cohort used for rollout calculations.
|
|
707
|
+
* If none has been stored yet, native derives the initial value once and
|
|
708
|
+
* persists it before returning.
|
|
709
|
+
*/
|
|
710
|
+
export const getCohort = (): string => {
|
|
711
|
+
const cachedCohort = sessionState.getCachedCohort();
|
|
712
|
+
if (cachedCohort !== undefined) {
|
|
713
|
+
return cachedCohort;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
const cohort = normalizeAndValidateCohort(HotUpdaterNative.getCohort());
|
|
717
|
+
sessionState.cacheCohort(cohort);
|
|
718
|
+
return cohort;
|
|
719
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { TurboModule } from "react-native";
|
|
2
2
|
import { TurboModuleRegistry } from "react-native";
|
|
3
|
+
import type { UnsafeObject } from "react-native/Libraries/Types/CodegenTypes";
|
|
3
4
|
|
|
4
5
|
export interface UpdateBundleParams {
|
|
5
6
|
bundleId: string;
|
|
@@ -105,6 +106,37 @@ export interface Spec extends TurboModule {
|
|
|
105
106
|
*/
|
|
106
107
|
getBaseURL: () => string;
|
|
107
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Gets the current active bundle ID from native bundle storage.
|
|
111
|
+
* Native reads the extracted bundle manifest first and falls back to the
|
|
112
|
+
* legacy BUNDLE_ID file when needed. Built-in bundle fallback is handled in JS.
|
|
113
|
+
*
|
|
114
|
+
* @returns Active bundle ID from bundle storage, or null when unavailable
|
|
115
|
+
*/
|
|
116
|
+
getBundleId: () => string | null;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Gets the current manifest from native bundle storage.
|
|
120
|
+
* Returns an empty object when manifest.json is missing or invalid.
|
|
121
|
+
*/
|
|
122
|
+
getManifest: () => UnsafeObject;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Sets the persisted cohort used for rollout calculations.
|
|
126
|
+
*
|
|
127
|
+
* Native only derives a device-based cohort when nothing has been stored
|
|
128
|
+
* yet. Call `getCohort()` first if the app needs to save that initial value
|
|
129
|
+
* for a later restore.
|
|
130
|
+
*/
|
|
131
|
+
setCohort: (cohort: string) => void;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Gets the persisted cohort used for rollout calculations.
|
|
135
|
+
* If none has been stored yet, native derives the initial value once and
|
|
136
|
+
* persists it before returning.
|
|
137
|
+
*/
|
|
138
|
+
getCohort: () => string;
|
|
139
|
+
|
|
108
140
|
// EventEmitter
|
|
109
141
|
addListener(eventName: string): void;
|
|
110
142
|
removeListeners(count: number): void;
|
package/src/types.ts
CHANGED
package/src/wrap.tsx
CHANGED
|
@@ -55,7 +55,7 @@ interface CommonHotUpdaterOptions {
|
|
|
55
55
|
* updateMode: "manual",
|
|
56
56
|
* onNotifyAppReady: ({ status, crashedBundleId }) => {
|
|
57
57
|
* if (status === "RECOVERED") {
|
|
58
|
-
* analytics.track(
|
|
58
|
+
* analytics.track("bundle_rollback", { crashedBundleId });
|
|
59
59
|
* }
|
|
60
60
|
* }
|
|
61
61
|
* })(App);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"native.spec.d.ts","sourceRoot":"","sources":["../../../src/native.spec.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"native.spec.d.ts","sourceRoot":"","sources":["../../../src/native.spec.ts"],"names":[],"mappings":""}
|