@hot-updater/react-native 0.20.11 → 0.20.13

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 (60) hide show
  1. package/android/src/main/java/com/hotupdater/HotUpdater.kt +1 -1
  2. package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +6 -6
  3. package/android/src/newarch/HotUpdaterModule.kt +11 -5
  4. package/android/src/newarch/ReactIntegrationManager.kt +40 -2
  5. package/android/src/oldarch/HotUpdaterModule.kt +11 -5
  6. package/android/src/oldarch/HotUpdaterSpec.kt +1 -1
  7. package/android/src/oldarch/ReactIntegrationManager.kt +36 -2
  8. package/ios/HotUpdater/Internal/HotUpdater.mm +18 -15
  9. package/lib/commonjs/index.js +21 -3
  10. package/lib/commonjs/index.js.map +1 -1
  11. package/lib/commonjs/native.js +34 -15
  12. package/lib/commonjs/native.js.map +1 -1
  13. package/lib/commonjs/runUpdateProcess.js +2 -2
  14. package/lib/commonjs/runUpdateProcess.js.map +1 -1
  15. package/lib/commonjs/store.js +13 -2
  16. package/lib/commonjs/store.js.map +1 -1
  17. package/lib/commonjs/wrap.js +1 -1
  18. package/lib/commonjs/wrap.js.map +1 -1
  19. package/lib/module/checkForUpdate.js.map +1 -1
  20. package/lib/module/index.js +21 -3
  21. package/lib/module/index.js.map +1 -1
  22. package/lib/module/native.js +32 -14
  23. package/lib/module/native.js.map +1 -1
  24. package/lib/module/runUpdateProcess.js +2 -2
  25. package/lib/module/runUpdateProcess.js.map +1 -1
  26. package/lib/module/store.js +13 -2
  27. package/lib/module/store.js.map +1 -1
  28. package/lib/module/wrap.js +2 -3
  29. package/lib/module/wrap.js.map +1 -1
  30. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  31. package/lib/typescript/commonjs/index.d.ts +23 -5
  32. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  33. package/lib/typescript/commonjs/native.d.ts +1 -1
  34. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  35. package/lib/typescript/commonjs/runUpdateProcess.d.ts +1 -1
  36. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +1 -1
  37. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
  38. package/lib/typescript/commonjs/store.d.ts +1 -1
  39. package/lib/typescript/commonjs/store.d.ts.map +1 -1
  40. package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
  41. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  42. package/lib/typescript/module/index.d.ts +23 -5
  43. package/lib/typescript/module/index.d.ts.map +1 -1
  44. package/lib/typescript/module/native.d.ts +1 -1
  45. package/lib/typescript/module/native.d.ts.map +1 -1
  46. package/lib/typescript/module/runUpdateProcess.d.ts +1 -1
  47. package/lib/typescript/module/specs/NativeHotUpdater.d.ts +1 -1
  48. package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
  49. package/lib/typescript/module/store.d.ts +1 -1
  50. package/lib/typescript/module/store.d.ts.map +1 -1
  51. package/lib/typescript/module/wrap.d.ts.map +1 -1
  52. package/package.json +5 -5
  53. package/plugin/build/withHotUpdater.js +0 -1
  54. package/src/checkForUpdate.ts +1 -1
  55. package/src/index.ts +22 -5
  56. package/src/native.ts +38 -14
  57. package/src/runUpdateProcess.ts +2 -2
  58. package/src/specs/NativeHotUpdater.ts +1 -1
  59. package/src/store.ts +18 -3
  60. package/src/wrap.tsx +2 -3
package/src/native.ts CHANGED
@@ -34,6 +34,11 @@ export type UpdateParams = UpdateBundleParams & {
34
34
  status: UpdateStatus;
35
35
  };
36
36
 
37
+ // In-flight update deduplication by bundleId (session-scoped).
38
+ const inflightUpdates = new Map<string, Promise<boolean>>();
39
+ // Tracks the last successfully installed bundleId for this session.
40
+ let lastInstalledBundleId: string | null = null;
41
+
37
42
  /**
38
43
  * Downloads files and applies them to the app.
39
44
  *
@@ -60,6 +65,11 @@ export async function updateBundle(
60
65
  const status =
61
66
  typeof paramsOrBundleId === "string" ? "UPDATE" : paramsOrBundleId.status;
62
67
 
68
+ // If we have already installed this bundle in this session, skip re-download.
69
+ if (status === "UPDATE" && lastInstalledBundleId === updateBundleId) {
70
+ return true;
71
+ }
72
+
63
73
  const currentBundleId = getBundleId();
64
74
 
65
75
  // updateBundleId <= currentBundleId
@@ -72,16 +82,32 @@ export async function updateBundle(
72
82
  );
73
83
  }
74
84
 
75
- if (typeof paramsOrBundleId === "string") {
76
- return HotUpdaterNative.updateBundle({
77
- bundleId: updateBundleId,
78
- fileUrl: fileUrl || null,
79
- });
80
- }
81
- return HotUpdaterNative.updateBundle({
82
- bundleId: updateBundleId,
83
- fileUrl: paramsOrBundleId.fileUrl,
84
- });
85
+ // In-flight guard: return the same promise if the same bundle is already updating.
86
+ const existing = inflightUpdates.get(updateBundleId);
87
+ if (existing) return existing;
88
+
89
+ const targetFileUrl =
90
+ typeof paramsOrBundleId === "string"
91
+ ? (fileUrl ?? null)
92
+ : paramsOrBundleId.fileUrl;
93
+
94
+ const promise = (async () => {
95
+ try {
96
+ const ok = await HotUpdaterNative.updateBundle({
97
+ bundleId: updateBundleId,
98
+ fileUrl: targetFileUrl,
99
+ });
100
+ if (ok) {
101
+ lastInstalledBundleId = updateBundleId;
102
+ }
103
+ return ok;
104
+ } finally {
105
+ inflightUpdates.delete(updateBundleId);
106
+ }
107
+ })();
108
+
109
+ inflightUpdates.set(updateBundleId, promise);
110
+ return promise;
85
111
  }
86
112
 
87
113
  /**
@@ -95,10 +121,8 @@ export const getAppVersion = (): string | null => {
95
121
  /**
96
122
  * Reloads the app.
97
123
  */
98
- export const reload = () => {
99
- requestAnimationFrame(() => {
100
- HotUpdaterNative.reload();
101
- });
124
+ export const reload = async () => {
125
+ await HotUpdaterNative.reload();
102
126
  };
103
127
 
104
128
  /**
@@ -43,7 +43,7 @@ export interface RunUpdateProcessOptions extends CheckForUpdateOptions {
43
43
  * });
44
44
  *
45
45
  * if(result.status !== "UP_TO_DATE" && result.shouldForceUpdate) {
46
- * HotUpdater.reload();
46
+ * await HotUpdater.reload();
47
47
  * }
48
48
  * ```
49
49
  *
@@ -65,7 +65,7 @@ export const runUpdateProcess = async ({
65
65
 
66
66
  const isUpdated = await updateInfo.updateBundle();
67
67
  if (isUpdated && updateInfo.shouldForceUpdate && reloadOnForceUpdate) {
68
- reload();
68
+ await reload();
69
69
  }
70
70
 
71
71
  if (!isUpdated) {
@@ -8,7 +8,7 @@ export interface UpdateBundleParams {
8
8
 
9
9
  export interface Spec extends TurboModule {
10
10
  // Methods
11
- reload(): void;
11
+ reload(): Promise<void>;
12
12
  updateBundle(params: UpdateBundleParams): Promise<boolean>;
13
13
 
14
14
  // EventEmitter
package/src/store.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import useSyncExternalStoreExports from "use-sync-external-store/shim/with-selector";
2
2
  export type HotUpdaterState = {
3
3
  progress: number;
4
- isBundleUpdated: boolean;
4
+ isUpdateDownloaded: boolean;
5
5
  };
6
6
 
7
7
  const { useSyncExternalStoreWithSelector } = useSyncExternalStoreExports;
@@ -9,7 +9,7 @@ const { useSyncExternalStoreWithSelector } = useSyncExternalStoreExports;
9
9
  const createHotUpdaterStore = () => {
10
10
  let state: HotUpdaterState = {
11
11
  progress: 0,
12
- isBundleUpdated: false,
12
+ isUpdateDownloaded: false,
13
13
  };
14
14
 
15
15
  const getSnapshot = () => {
@@ -25,10 +25,25 @@ const createHotUpdaterStore = () => {
25
25
  };
26
26
 
27
27
  const setState = (newState: Partial<HotUpdaterState>) => {
28
- state = {
28
+ // Merge first, then normalize derived fields
29
+ const nextState: HotUpdaterState = {
29
30
  ...state,
30
31
  ...newState,
31
32
  };
33
+
34
+ // Derive `isUpdateDownloaded` from `progress` if provided.
35
+ // If `progress` is not provided but `isUpdateDownloaded` is,
36
+ // honor the explicit value.
37
+ if ("progress" in newState && typeof newState.progress === "number") {
38
+ nextState.isUpdateDownloaded = newState.progress >= 1;
39
+ } else if (
40
+ "isUpdateDownloaded" in newState &&
41
+ typeof newState.isUpdateDownloaded === "boolean"
42
+ ) {
43
+ nextState.isUpdateDownloaded = newState.isUpdateDownloaded;
44
+ }
45
+
46
+ state = nextState;
32
47
  emitChange();
33
48
  };
34
49
 
package/src/wrap.tsx CHANGED
@@ -1,5 +1,4 @@
1
- import React from "react";
2
- import { useEffect, useLayoutEffect, useState } from "react";
1
+ import React, { useEffect, useLayoutEffect, useState } from "react";
3
2
  import { type CheckForUpdateOptions, checkForUpdate } from "./checkForUpdate";
4
3
  import type { HotUpdaterError } from "./error";
5
4
  import { useEventCallback } from "./hooks/useEventCallback";
@@ -117,7 +116,7 @@ export function wrap<P extends React.JSX.IntrinsicAttributes = object>(
117
116
  }
118
117
 
119
118
  if (reloadOnForceUpdate) {
120
- reload();
119
+ await reload();
121
120
  }
122
121
 
123
122
  restOptions.onUpdateProcessCompleted?.({