react-native-bspatch 2.0.1 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +127 -151
- package/android/src/main/java/vn/reactnativebspatch/BsPatchModule.kt +0 -25
- package/android/src/main/java/vn/reactnativebspatch/OtaBundleResolver.kt +14 -6
- package/ios/OtaBundleResolver.swift +14 -6
- package/ios/RNBsPatch.mm +1 -28
- package/lib/commonjs/NativeBsPatch.js.map +1 -1
- package/lib/commonjs/ota/OtaProvider.js +29 -16
- package/lib/commonjs/ota/OtaProvider.js.map +1 -1
- package/lib/commonjs/ota/otaAssetResolver.js +0 -20
- package/lib/commonjs/ota/otaAssetResolver.js.map +1 -1
- package/lib/commonjs/ota/otaCore.js +200 -330
- package/lib/commonjs/ota/otaCore.js.map +1 -1
- package/lib/commonjs/ota/otaMetadata.js +21 -21
- package/lib/commonjs/ota/otaMetadata.js.map +1 -1
- package/lib/commonjs/ota/storage/GitHubRawStorage.js +13 -15
- package/lib/commonjs/ota/storage/GitHubRawStorage.js.map +1 -1
- package/lib/commonjs/ota/types.js.map +1 -1
- package/lib/module/NativeBsPatch.js.map +1 -1
- package/lib/module/ota/OtaProvider.js +30 -17
- package/lib/module/ota/OtaProvider.js.map +1 -1
- package/lib/module/ota/otaAssetResolver.js +1 -21
- package/lib/module/ota/otaAssetResolver.js.map +1 -1
- package/lib/module/ota/otaCore.js +197 -329
- package/lib/module/ota/otaCore.js.map +1 -1
- package/lib/module/ota/otaMetadata.js +19 -19
- package/lib/module/ota/otaMetadata.js.map +1 -1
- package/lib/module/ota/storage/GitHubRawStorage.js +13 -15
- package/lib/module/ota/storage/GitHubRawStorage.js.map +1 -1
- package/lib/module/ota/types.js.map +1 -1
- package/lib/typescript/NativeBsPatch.d.ts +0 -1
- package/lib/typescript/NativeBsPatch.d.ts.map +1 -1
- package/lib/typescript/ota/OtaProvider.d.ts.map +1 -1
- package/lib/typescript/ota/otaAssetResolver.d.ts.map +1 -1
- package/lib/typescript/ota/otaCore.d.ts +13 -3
- package/lib/typescript/ota/otaCore.d.ts.map +1 -1
- package/lib/typescript/ota/otaMetadata.d.ts +9 -7
- package/lib/typescript/ota/otaMetadata.d.ts.map +1 -1
- package/lib/typescript/ota/storage/GitHubRawStorage.d.ts.map +1 -1
- package/lib/typescript/ota/types.d.ts +5 -19
- package/lib/typescript/ota/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/NativeBsPatch.ts +0 -5
- package/src/ota/OtaProvider.tsx +30 -23
- package/src/ota/otaAssetResolver.ts +1 -17
- package/src/ota/otaCore.ts +203 -457
- package/src/ota/otaMetadata.ts +30 -20
- package/src/ota/storage/GitHubRawStorage.ts +13 -21
- package/src/ota/types.ts +5 -20
- package/tools/build_ota.sh +206 -516
- package/tools/cli.js +9 -0
- package/tools/setup_project.js +17 -115
- package/tools/update_registry.js +31 -32
- package/tools/upload_ota.sh +61 -14
package/src/ota/OtaProvider.tsx
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
import ReactNativeBlobUtil from 'react-native-blob-util';
|
|
20
20
|
import NativeBsPatch from '../NativeBsPatch';
|
|
21
21
|
import { initOtaAssetResolver } from './otaAssetResolver';
|
|
22
|
-
import { applyOtaPatchInternal, fetchOtaManifest } from './otaCore';
|
|
22
|
+
import { applyOtaPatchInternal, fetchOtaManifest, selectUpdate, validateOtaConfig } from './otaCore';
|
|
23
23
|
import { createOtaLogger, OtaEvent } from './otaLogger';
|
|
24
24
|
import { loadMetadata, markHealthy } from './otaMetadata';
|
|
25
25
|
import {
|
|
@@ -65,9 +65,21 @@ export function OtaProvider({
|
|
|
65
65
|
const isCheckingRef = useRef(false);
|
|
66
66
|
const pendingReloadRef = useRef(false);
|
|
67
67
|
const bootstrapDone = useRef(false);
|
|
68
|
+
/** Version the native resolver loaded for this JS session */
|
|
69
|
+
const runningVersionRef = useRef(config.baseAppVersion);
|
|
68
70
|
const progressRef = useRef(0);
|
|
69
71
|
const isDebugUpdateEnabled = !__DEV__ || config.allowDebugUpdates === true;
|
|
70
72
|
|
|
73
|
+
// No fallbacks: a missing env value disables OTA instead of silently pointing elsewhere.
|
|
74
|
+
// Dev: throw during render (red box). Release: the app runs, OTA never checks or downloads.
|
|
75
|
+
const configError = useMemo(() => {
|
|
76
|
+
const e = validateOtaConfig(config);
|
|
77
|
+
return e && `[react-native-bspatch] OTA disabled: ${e}`;
|
|
78
|
+
}, [config]);
|
|
79
|
+
if (__DEV__ && configError) {
|
|
80
|
+
throw new Error(configError);
|
|
81
|
+
}
|
|
82
|
+
|
|
71
83
|
const setProgressIfChanged = useCallback((nextProgress: number) => {
|
|
72
84
|
if (progressRef.current === nextProgress) {
|
|
73
85
|
return;
|
|
@@ -99,16 +111,12 @@ export function OtaProvider({
|
|
|
99
111
|
const executeApplyUpdate = useCallback(
|
|
100
112
|
async (patchList: OtaPatch[], currentVer: number, targetVer: number) => {
|
|
101
113
|
console.log('[OtaProvider] executeApplyUpdate | targetV:', targetVer, 'currentV:', currentVer);
|
|
102
|
-
const directPatch = patchList
|
|
103
|
-
p => p.fromVersion === currentVer &&
|
|
104
|
-
p.toVersion === targetVer &&
|
|
105
|
-
(!p.platform || p.platform === Platform.OS),
|
|
106
|
-
);
|
|
114
|
+
const directPatch = selectUpdate(patchList, currentVer, targetVer);
|
|
107
115
|
|
|
108
116
|
if (!directPatch) {
|
|
109
|
-
console.warn('[OtaProvider] No
|
|
117
|
+
console.warn('[OtaProvider] No update entry found for', currentVer, '->', targetVer);
|
|
110
118
|
setStatus(OtaStatus.ERROR);
|
|
111
|
-
setMessage('No
|
|
119
|
+
setMessage('No upgrade path found.');
|
|
112
120
|
return;
|
|
113
121
|
}
|
|
114
122
|
|
|
@@ -167,11 +175,7 @@ export function OtaProvider({
|
|
|
167
175
|
localV: number,
|
|
168
176
|
targetV: number,
|
|
169
177
|
) => {
|
|
170
|
-
const directPatch = patchList
|
|
171
|
-
p => p.fromVersion === localV &&
|
|
172
|
-
p.toVersion === targetV &&
|
|
173
|
-
(!p.platform || p.platform === Platform.OS),
|
|
174
|
-
);
|
|
178
|
+
const directPatch = selectUpdate(patchList, localV, targetV);
|
|
175
179
|
if (!directPatch) return;
|
|
176
180
|
|
|
177
181
|
try {
|
|
@@ -185,10 +189,8 @@ export function OtaProvider({
|
|
|
185
189
|
pendingReloadRef.current = true;
|
|
186
190
|
setStatus(OtaStatus.UP_TO_DATE);
|
|
187
191
|
setMessage(`Update V${targetV} ready for next launch`);
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
initOtaAssetResolver(targetV);
|
|
191
|
-
}
|
|
192
|
+
// Not initOtaAssetResolver: the running bundle still needs its own manifest
|
|
193
|
+
if (m) setMetrics(m);
|
|
192
194
|
}
|
|
193
195
|
},
|
|
194
196
|
() => {}, // silent: background doesn't spam progress
|
|
@@ -208,7 +210,7 @@ export function OtaProvider({
|
|
|
208
210
|
* Uses a ref to guard against concurrent calls (avoids stale closure).
|
|
209
211
|
*/
|
|
210
212
|
const checkUpdates = useCallback(async () => {
|
|
211
|
-
if (isCheckingRef.current) return;
|
|
213
|
+
if (isCheckingRef.current || configError) return;
|
|
212
214
|
if (!isDebugUpdateEnabled) {
|
|
213
215
|
setPendingCount(0);
|
|
214
216
|
setIsChecking(false);
|
|
@@ -274,7 +276,7 @@ export function OtaProvider({
|
|
|
274
276
|
isCheckingRef.current = false;
|
|
275
277
|
setIsChecking(false);
|
|
276
278
|
}
|
|
277
|
-
}, [config, executeApplyUpdate, isDebugUpdateEnabled, downloadBackgroundUpdate, getEffectiveConfig]);
|
|
279
|
+
}, [config, configError, executeApplyUpdate, isDebugUpdateEnabled, downloadBackgroundUpdate, getEffectiveConfig]);
|
|
278
280
|
|
|
279
281
|
/**
|
|
280
282
|
* Bootstrap: runs once on mount.
|
|
@@ -299,6 +301,7 @@ export function OtaProvider({
|
|
|
299
301
|
}).catch(() => {});
|
|
300
302
|
|
|
301
303
|
const metadata = await loadMetadata(config.baseAppVersion);
|
|
304
|
+
runningVersionRef.current = metadata.currentVersion;
|
|
302
305
|
|
|
303
306
|
// Display current version immediately
|
|
304
307
|
setCurrentVersion(metadata.currentVersion);
|
|
@@ -309,7 +312,12 @@ export function OtaProvider({
|
|
|
309
312
|
}
|
|
310
313
|
|
|
311
314
|
// (Native layer handled crash detection and any necessary rollback)
|
|
312
|
-
|
|
315
|
+
if (configError) {
|
|
316
|
+
console.error(configError);
|
|
317
|
+
setMessage(configError);
|
|
318
|
+
setStatus(OtaStatus.ERROR);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
313
321
|
bootstrapDone.current = true;
|
|
314
322
|
|
|
315
323
|
// Mode-based auto-check
|
|
@@ -373,9 +381,8 @@ export function OtaProvider({
|
|
|
373
381
|
const markAsHealthy = async () => {
|
|
374
382
|
const log = createOtaLogger(config.onEvent);
|
|
375
383
|
log.emit(OtaEvent.MARK_HEALTHY);
|
|
376
|
-
const
|
|
377
|
-
await markHealthy(config.baseAppVersion, appVersion);
|
|
378
|
-
NativeBsPatch.markAsHealthy();
|
|
384
|
+
const appVersion = await NativeBsPatch.getAppVersion().catch(() => config.appVersion);
|
|
385
|
+
await markHealthy(config.baseAppVersion, runningVersionRef.current, appVersion);
|
|
379
386
|
};
|
|
380
387
|
|
|
381
388
|
const resetToFactory = async () => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Image
|
|
1
|
+
import { Image } from 'react-native';
|
|
2
2
|
import ReactNativeBlobUtil from 'react-native-blob-util';
|
|
3
3
|
import NativeBsPatch from '../NativeBsPatch';
|
|
4
4
|
|
|
@@ -8,7 +8,6 @@ interface AssetMetadata {
|
|
|
8
8
|
originalPath: string;
|
|
9
9
|
hash: string;
|
|
10
10
|
ext: string;
|
|
11
|
-
isBase?: boolean;
|
|
12
11
|
_normPath: string;
|
|
13
12
|
}
|
|
14
13
|
|
|
@@ -143,21 +142,6 @@ const patchAssetResolver = (): void => {
|
|
|
143
142
|
}
|
|
144
143
|
|
|
145
144
|
if (found) {
|
|
146
|
-
if (found.isBase) {
|
|
147
|
-
if (Platform.OS === 'ios') {
|
|
148
|
-
// iOS: OTA bundle generates wrong relative paths for base assets, redirect to MainBundleDir
|
|
149
|
-
const finalUri = `file://${ReactNativeBlobUtil.fs.dirs.MainBundleDir}/${found.originalPath}`;
|
|
150
|
-
log(`REDIRECTED (BASE): ${uri} -> (MainBundle: ${finalUri})`);
|
|
151
|
-
return { ...res, uri: finalUri };
|
|
152
|
-
} else {
|
|
153
|
-
// Android: with a file-based bundle RN points at ota/vN/drawable-*/x.png,
|
|
154
|
-
// which doesn't exist. Base assets live in the APK as drawable/raw
|
|
155
|
-
// resources, addressed by name (originalPath: "drawable-mdpi/name.png").
|
|
156
|
-
const resName = found.originalPath.split('/').pop()!.replace(/\.[^.]+$/, '');
|
|
157
|
-
return { ...res, uri: resName };
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
145
|
const localPath = `${ASSET_STORE_DIR}/${found.hash}.${found.ext}`;
|
|
162
146
|
// iOS & Android: requires file:// prefix for absolute filesystem paths
|
|
163
147
|
const finalUri = `file://${localPath}`;
|