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.
Files changed (53) hide show
  1. package/README.md +127 -151
  2. package/android/src/main/java/vn/reactnativebspatch/BsPatchModule.kt +0 -25
  3. package/android/src/main/java/vn/reactnativebspatch/OtaBundleResolver.kt +14 -6
  4. package/ios/OtaBundleResolver.swift +14 -6
  5. package/ios/RNBsPatch.mm +1 -28
  6. package/lib/commonjs/NativeBsPatch.js.map +1 -1
  7. package/lib/commonjs/ota/OtaProvider.js +29 -16
  8. package/lib/commonjs/ota/OtaProvider.js.map +1 -1
  9. package/lib/commonjs/ota/otaAssetResolver.js +0 -20
  10. package/lib/commonjs/ota/otaAssetResolver.js.map +1 -1
  11. package/lib/commonjs/ota/otaCore.js +200 -330
  12. package/lib/commonjs/ota/otaCore.js.map +1 -1
  13. package/lib/commonjs/ota/otaMetadata.js +21 -21
  14. package/lib/commonjs/ota/otaMetadata.js.map +1 -1
  15. package/lib/commonjs/ota/storage/GitHubRawStorage.js +13 -15
  16. package/lib/commonjs/ota/storage/GitHubRawStorage.js.map +1 -1
  17. package/lib/commonjs/ota/types.js.map +1 -1
  18. package/lib/module/NativeBsPatch.js.map +1 -1
  19. package/lib/module/ota/OtaProvider.js +30 -17
  20. package/lib/module/ota/OtaProvider.js.map +1 -1
  21. package/lib/module/ota/otaAssetResolver.js +1 -21
  22. package/lib/module/ota/otaAssetResolver.js.map +1 -1
  23. package/lib/module/ota/otaCore.js +197 -329
  24. package/lib/module/ota/otaCore.js.map +1 -1
  25. package/lib/module/ota/otaMetadata.js +19 -19
  26. package/lib/module/ota/otaMetadata.js.map +1 -1
  27. package/lib/module/ota/storage/GitHubRawStorage.js +13 -15
  28. package/lib/module/ota/storage/GitHubRawStorage.js.map +1 -1
  29. package/lib/module/ota/types.js.map +1 -1
  30. package/lib/typescript/NativeBsPatch.d.ts +0 -1
  31. package/lib/typescript/NativeBsPatch.d.ts.map +1 -1
  32. package/lib/typescript/ota/OtaProvider.d.ts.map +1 -1
  33. package/lib/typescript/ota/otaAssetResolver.d.ts.map +1 -1
  34. package/lib/typescript/ota/otaCore.d.ts +13 -3
  35. package/lib/typescript/ota/otaCore.d.ts.map +1 -1
  36. package/lib/typescript/ota/otaMetadata.d.ts +9 -7
  37. package/lib/typescript/ota/otaMetadata.d.ts.map +1 -1
  38. package/lib/typescript/ota/storage/GitHubRawStorage.d.ts.map +1 -1
  39. package/lib/typescript/ota/types.d.ts +5 -19
  40. package/lib/typescript/ota/types.d.ts.map +1 -1
  41. package/package.json +2 -2
  42. package/src/NativeBsPatch.ts +0 -5
  43. package/src/ota/OtaProvider.tsx +30 -23
  44. package/src/ota/otaAssetResolver.ts +1 -17
  45. package/src/ota/otaCore.ts +203 -457
  46. package/src/ota/otaMetadata.ts +30 -20
  47. package/src/ota/storage/GitHubRawStorage.ts +13 -21
  48. package/src/ota/types.ts +5 -20
  49. package/tools/build_ota.sh +206 -516
  50. package/tools/cli.js +9 -0
  51. package/tools/setup_project.js +17 -115
  52. package/tools/update_registry.js +31 -32
  53. package/tools/upload_ota.sh +61 -14
@@ -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.find(
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 direct patch found for', currentVer, '->', targetVer);
117
+ console.warn('[OtaProvider] No update entry found for', currentVer, '->', targetVer);
110
118
  setStatus(OtaStatus.ERROR);
111
- setMessage('No direct upgrade path found.');
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.find(
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
- if (m) {
189
- setMetrics(m);
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
- // We just mark bootstrap as done
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 { appVersion } = await getEffectiveConfig().catch(() => config);
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, Platform } from 'react-native';
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}`;