@hot-updater/react-native 0.16.7-0 → 0.18.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.
Files changed (143) hide show
  1. package/HotUpdater.podspec +7 -11
  2. package/android/{generated/java/com/hotupdater → app/build/generated/source/codegen/java/com/facebook/fbreact/specs}/NativeHotUpdaterSpec.java +3 -2
  3. package/android/app/build/generated/source/codegen/jni/HotUpdater-generated.cpp +68 -0
  4. package/android/app/build/generated/source/codegen/jni/HotUpdater.h +31 -0
  5. package/android/{generated → app/build/generated/source/codegen}/jni/HotUpdaterSpec-generated.cpp +2 -2
  6. package/android/{generated/jni/react/renderer/components/HotUpdaterSpec/HotUpdaterSpecJSI-generated.cpp → app/build/generated/source/codegen/jni/react/renderer/components/HotUpdater/HotUpdaterJSI-generated.cpp} +3 -4
  7. package/{ios/generated/HotUpdaterSpecJSI.h → android/app/build/generated/source/codegen/jni/react/renderer/components/HotUpdater/HotUpdaterJSI.h} +53 -6
  8. package/{ios/generated → android/app/build/generated/source/codegen/jni/react/renderer/components/HotUpdaterSpec}/HotUpdaterSpecJSI-generated.cpp +2 -3
  9. package/android/{generated → app/build/generated/source/codegen}/jni/react/renderer/components/HotUpdaterSpec/HotUpdaterSpecJSI.h +59 -8
  10. package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +200 -0
  11. package/android/src/main/java/com/hotupdater/FileManagerService.kt +104 -0
  12. package/android/src/main/java/com/hotupdater/HotUpdater.kt +62 -305
  13. package/android/src/main/java/com/hotupdater/HotUpdaterFactory.kt +49 -0
  14. package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +176 -0
  15. package/android/src/main/java/com/hotupdater/HttpDownloadService.kt +98 -0
  16. package/android/src/main/java/com/hotupdater/VersionedPreferencesService.kt +69 -0
  17. package/android/src/main/java/com/hotupdater/ZipFileUnzipService.kt +52 -0
  18. package/android/src/newarch/HotUpdaterModule.kt +31 -34
  19. package/android/src/newarch/ReactIntegrationManager.kt +17 -3
  20. package/android/src/oldarch/HotUpdaterModule.kt +32 -34
  21. package/android/src/oldarch/HotUpdaterSpec.kt +2 -9
  22. package/android/src/oldarch/ReactIntegrationManager.kt +0 -2
  23. package/ios/HotUpdater/Internal/BundleFileStorageService.swift +593 -0
  24. package/ios/HotUpdater/Internal/FileManagerService.swift +97 -0
  25. package/ios/HotUpdater/Internal/HotUpdater-Bridging-Header.h +8 -0
  26. package/ios/HotUpdater/Internal/HotUpdater.mm +241 -0
  27. package/ios/HotUpdater/Internal/HotUpdaterFactory.swift +24 -0
  28. package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +143 -0
  29. package/ios/HotUpdater/Internal/NotificationExtension.swift +6 -0
  30. package/ios/HotUpdater/Internal/SSZipArchiveUnzipService.swift +25 -0
  31. package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +101 -0
  32. package/ios/HotUpdater/Internal/VersionedPreferencesService.swift +82 -0
  33. package/ios/HotUpdater/Package.resolved +15 -0
  34. package/ios/HotUpdater/Public/HotUpdater.h +29 -0
  35. package/lib/commonjs/checkForUpdate.js +70 -0
  36. package/lib/commonjs/checkForUpdate.js.map +1 -0
  37. package/lib/commonjs/error.js +14 -0
  38. package/lib/commonjs/error.js.map +1 -0
  39. package/lib/commonjs/fetchUpdateInfo.js +74 -0
  40. package/lib/commonjs/fetchUpdateInfo.js.map +1 -0
  41. package/lib/commonjs/hooks/useEventCallback.js +17 -0
  42. package/lib/commonjs/hooks/useEventCallback.js.map +1 -0
  43. package/lib/commonjs/index.js +234 -0
  44. package/lib/commonjs/index.js.map +1 -0
  45. package/lib/commonjs/native.js +132 -0
  46. package/lib/commonjs/native.js.map +1 -0
  47. package/lib/commonjs/package.json +1 -0
  48. package/lib/commonjs/runUpdateProcess.js +69 -0
  49. package/lib/commonjs/runUpdateProcess.js.map +1 -0
  50. package/lib/commonjs/specs/NativeHotUpdater.js +9 -0
  51. package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -0
  52. package/lib/commonjs/store.js +48 -0
  53. package/lib/commonjs/store.js.map +1 -0
  54. package/lib/commonjs/wrap.js +98 -0
  55. package/lib/commonjs/wrap.js.map +1 -0
  56. package/lib/module/checkForUpdate.js +64 -0
  57. package/lib/module/checkForUpdate.js.map +1 -0
  58. package/lib/module/error.js +9 -0
  59. package/lib/module/error.js.map +1 -0
  60. package/lib/module/fetchUpdateInfo.js +69 -0
  61. package/lib/module/fetchUpdateInfo.js.map +1 -0
  62. package/lib/module/hooks/useEventCallback.js +13 -0
  63. package/lib/module/hooks/useEventCallback.js.map +1 -0
  64. package/lib/module/index.js +211 -0
  65. package/lib/module/index.js.map +1 -0
  66. package/lib/module/native.js +119 -0
  67. package/lib/module/native.js.map +1 -0
  68. package/lib/module/package.json +1 -0
  69. package/lib/module/runUpdateProcess.js +64 -0
  70. package/lib/module/runUpdateProcess.js.map +1 -0
  71. package/lib/module/specs/NativeHotUpdater.js +5 -0
  72. package/lib/module/specs/NativeHotUpdater.js.map +1 -0
  73. package/lib/module/store.js +42 -0
  74. package/lib/module/store.js.map +1 -0
  75. package/lib/module/wrap.js +94 -0
  76. package/lib/module/wrap.js.map +1 -0
  77. package/lib/typescript/commonjs/checkForUpdate.d.ts +22 -0
  78. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -0
  79. package/{dist → lib/typescript/commonjs}/error.d.ts +1 -0
  80. package/lib/typescript/commonjs/error.d.ts.map +1 -0
  81. package/lib/typescript/commonjs/fetchUpdateInfo.d.ts +4 -0
  82. package/lib/typescript/commonjs/fetchUpdateInfo.d.ts.map +1 -0
  83. package/{dist → lib/typescript/commonjs}/hooks/useEventCallback.d.ts +1 -0
  84. package/lib/typescript/commonjs/hooks/useEventCallback.d.ts.map +1 -0
  85. package/{dist → lib/typescript/commonjs}/index.d.ts +38 -12
  86. package/lib/typescript/commonjs/index.d.ts.map +1 -0
  87. package/lib/typescript/commonjs/native.d.ts +64 -0
  88. package/lib/typescript/commonjs/native.d.ts.map +1 -0
  89. package/lib/typescript/commonjs/package.json +1 -0
  90. package/{dist → lib/typescript/commonjs}/runUpdateProcess.d.ts +1 -0
  91. package/lib/typescript/commonjs/runUpdateProcess.d.ts.map +1 -0
  92. package/{dist → lib/typescript/commonjs}/specs/NativeHotUpdater.d.ts +8 -9
  93. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -0
  94. package/{dist → lib/typescript/commonjs}/store.d.ts +1 -0
  95. package/lib/typescript/commonjs/store.d.ts.map +1 -0
  96. package/{dist → lib/typescript/commonjs}/wrap.d.ts +3 -2
  97. package/lib/typescript/commonjs/wrap.d.ts.map +1 -0
  98. package/lib/typescript/module/checkForUpdate.d.ts +22 -0
  99. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -0
  100. package/lib/typescript/module/error.d.ts +4 -0
  101. package/lib/typescript/module/error.d.ts.map +1 -0
  102. package/lib/typescript/module/fetchUpdateInfo.d.ts +4 -0
  103. package/lib/typescript/module/fetchUpdateInfo.d.ts.map +1 -0
  104. package/lib/typescript/module/hooks/useEventCallback.d.ts +5 -0
  105. package/lib/typescript/module/hooks/useEventCallback.d.ts.map +1 -0
  106. package/lib/typescript/module/index.d.ts +202 -0
  107. package/lib/typescript/module/index.d.ts.map +1 -0
  108. package/lib/typescript/module/native.d.ts +64 -0
  109. package/lib/typescript/module/native.d.ts.map +1 -0
  110. package/lib/typescript/module/package.json +1 -0
  111. package/lib/typescript/module/runUpdateProcess.d.ts +49 -0
  112. package/lib/typescript/module/runUpdateProcess.d.ts.map +1 -0
  113. package/lib/typescript/module/specs/NativeHotUpdater.d.ts +19 -0
  114. package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -0
  115. package/lib/typescript/module/store.d.ts +11 -0
  116. package/lib/typescript/module/store.d.ts.map +1 -0
  117. package/lib/typescript/module/wrap.d.ts +51 -0
  118. package/lib/typescript/module/wrap.d.ts.map +1 -0
  119. package/package.json +59 -30
  120. package/src/checkForUpdate.ts +59 -9
  121. package/src/fetchUpdateInfo.ts +40 -12
  122. package/src/index.ts +37 -11
  123. package/src/native.ts +87 -41
  124. package/src/runUpdateProcess.ts +2 -2
  125. package/src/specs/NativeHotUpdater.ts +8 -10
  126. package/src/wrap.tsx +9 -13
  127. package/android/src/main/java/com/hotupdater/HotUpdaterPrefs.kt +0 -42
  128. package/dist/checkForUpdate.d.ts +0 -12
  129. package/dist/fetchUpdateInfo.d.ts +0 -3
  130. package/dist/index.js +0 -341
  131. package/dist/index.mjs +0 -301
  132. package/dist/native.d.ts +0 -41
  133. package/ios/HotUpdater/HotUpdater.h +0 -15
  134. package/ios/HotUpdater/HotUpdater.mm +0 -468
  135. package/ios/HotUpdater/HotUpdater.modulemap +0 -6
  136. package/ios/HotUpdater/HotUpdaterPrefs.h +0 -9
  137. package/ios/HotUpdater/HotUpdaterPrefs.mm +0 -45
  138. package/ios/generated/HotUpdaterSpec/HotUpdaterSpec-generated.mm +0 -81
  139. package/ios/generated/HotUpdaterSpec/HotUpdaterSpec.h +0 -112
  140. package/react-native.config.js +0 -12
  141. package/src/global.d.ts +0 -3
  142. /package/android/{generated → app/build/generated/source/codegen}/jni/CMakeLists.txt +0 -0
  143. /package/android/{generated → app/build/generated/source/codegen}/jni/HotUpdaterSpec.h +0 -0
@@ -1,11 +1,14 @@
1
+ import type { AppUpdateInfo, GetBundlesArgs } from "@hot-updater/core";
1
2
  import { Platform } from "react-native";
2
3
  import { HotUpdaterError } from "./error";
3
4
  import { type UpdateSource, fetchUpdateInfo } from "./fetchUpdateInfo";
4
5
  import {
6
+ HotUpdaterConstants,
5
7
  getAppVersion,
6
8
  getBundleId,
7
9
  getChannel,
8
10
  getMinBundleId,
11
+ updateBundle,
9
12
  } from "./native";
10
13
 
11
14
  export interface CheckForUpdateOptions {
@@ -19,7 +22,17 @@ export interface CheckForUpdateOptions {
19
22
  requestTimeout?: number;
20
23
  }
21
24
 
22
- export async function checkForUpdate(options: CheckForUpdateOptions) {
25
+ export type CheckForUpdateResult = AppUpdateInfo & {
26
+ /**
27
+ * Updates the bundle.
28
+ * This method is equivalent to `HotUpdater.updateBundle()` but with all required arguments pre-filled.
29
+ */
30
+ updateBundle: () => Promise<boolean>;
31
+ };
32
+
33
+ export async function checkForUpdate(
34
+ options: CheckForUpdateOptions,
35
+ ): Promise<CheckForUpdateResult | null> {
23
36
  if (__DEV__) {
24
37
  return null;
25
38
  }
@@ -42,17 +55,54 @@ export async function checkForUpdate(options: CheckForUpdateOptions) {
42
55
  return null;
43
56
  }
44
57
 
58
+ const baseArgs = {
59
+ bundleId: currentBundleId,
60
+ platform,
61
+ minBundleId,
62
+ channel: channel ?? undefined,
63
+ };
64
+
45
65
  return fetchUpdateInfo(
46
66
  options.source,
47
- {
48
- appVersion: currentAppVersion,
49
- bundleId: currentBundleId,
50
- platform,
51
- minBundleId,
52
- channel: channel ?? undefined,
53
- },
67
+ HotUpdaterConstants.UPDATE_STRATEGY === "appVersion"
68
+ ? {
69
+ _updateStrategy: HotUpdaterConstants.UPDATE_STRATEGY,
70
+ appVersion: currentAppVersion,
71
+ ...baseArgs,
72
+ }
73
+ : {
74
+ _updateStrategy: HotUpdaterConstants.UPDATE_STRATEGY,
75
+ fingerprintHash: HotUpdaterConstants.FINGERPRINT_HASH!,
76
+ ...baseArgs,
77
+ },
54
78
  options.requestHeaders,
55
79
  options.onError,
56
80
  options.requestTimeout,
57
- );
81
+ ).then((updateInfo) => {
82
+ if (!updateInfo) {
83
+ return null;
84
+ }
85
+
86
+ return {
87
+ ...updateInfo,
88
+ updateBundle: async () => {
89
+ return updateBundle({
90
+ bundleId: updateInfo.id,
91
+ fileUrl: updateInfo.fileUrl,
92
+ status: updateInfo.status,
93
+ });
94
+ },
95
+ };
96
+ });
58
97
  }
98
+
99
+ export const getUpdateSource = (baseUrl: string) => (args: GetBundlesArgs) => {
100
+ switch (args._updateStrategy) {
101
+ case "appVersion":
102
+ return `${baseUrl}/app-version/${args.platform}/${args.appVersion}/${args.channel}/${args.minBundleId}/${args.bundleId}`;
103
+ case "fingerprint":
104
+ return `${baseUrl}/fingerprint/${args.platform}/${args.fingerprintHash}/${args.channel}/${args.minBundleId}/${args.bundleId}`;
105
+ default:
106
+ return baseUrl;
107
+ }
108
+ };
@@ -1,16 +1,23 @@
1
1
  import type { AppUpdateInfo, GetBundlesArgs } from "@hot-updater/core";
2
2
 
3
- export type UpdateSource = string | (() => Promise<AppUpdateInfo | null>);
3
+ export type UpdateSource =
4
+ | string
5
+ | ((args: GetBundlesArgs) => Promise<AppUpdateInfo | null>)
6
+ | ((args: GetBundlesArgs) => string);
4
7
 
5
8
  export const fetchUpdateInfo = async (
6
9
  source: UpdateSource,
7
- { appVersion, bundleId, platform, minBundleId, channel }: GetBundlesArgs,
10
+ args: GetBundlesArgs,
8
11
  requestHeaders?: Record<string, string>,
9
12
  onError?: (error: Error) => void,
10
13
  requestTimeout = 5000,
11
14
  ): Promise<AppUpdateInfo | null> => {
12
15
  if (typeof source === "function") {
13
- return source();
16
+ const url = source(args);
17
+ if (typeof url !== "string") {
18
+ return null;
19
+ }
20
+ source = url;
14
21
  }
15
22
 
16
23
  const controller = new AbortController();
@@ -19,17 +26,38 @@ export const fetchUpdateInfo = async (
19
26
  }, requestTimeout);
20
27
 
21
28
  try {
29
+ let headers: Record<string, string> = {};
30
+
31
+ switch (args._updateStrategy) {
32
+ case "fingerprint":
33
+ headers = {
34
+ "Content-Type": "application/json",
35
+ "x-app-platform": args.platform,
36
+ "x-bundle-id": args.bundleId,
37
+ "x-fingerprint-hash": args.fingerprintHash,
38
+ ...(args.minBundleId ? { "x-min-bundle-id": args.minBundleId } : {}),
39
+ ...(args.channel ? { "x-channel": args.channel } : {}),
40
+ ...requestHeaders,
41
+ };
42
+ break;
43
+ case "appVersion":
44
+ headers = {
45
+ "Content-Type": "application/json",
46
+ "x-app-platform": args.platform,
47
+ "x-bundle-id": args.bundleId,
48
+ "x-app-version": args.appVersion,
49
+ ...(args.minBundleId ? { "x-min-bundle-id": args.minBundleId } : {}),
50
+ ...(args.channel ? { "x-channel": args.channel } : {}),
51
+ ...requestHeaders,
52
+ };
53
+ break;
54
+ default:
55
+ throw new Error("Invalid update strategy");
56
+ }
57
+
22
58
  const response = await fetch(source, {
23
59
  signal: controller.signal,
24
- headers: {
25
- "Content-Type": "application/json",
26
- "x-app-platform": platform,
27
- "x-app-version": appVersion,
28
- "x-bundle-id": bundleId,
29
- ...(minBundleId ? { "x-min-bundle-id": minBundleId } : {}),
30
- ...(channel ? { "x-channel": channel } : {}),
31
- ...requestHeaders,
32
- },
60
+ headers,
33
61
  });
34
62
 
35
63
  clearTimeout(timeoutId);
package/src/index.ts CHANGED
@@ -4,9 +4,10 @@ import {
4
4
  getAppVersion,
5
5
  getBundleId,
6
6
  getChannel,
7
+ getFingerprintHash,
7
8
  getMinBundleId,
9
+ getReleaseChannel,
8
10
  reload,
9
- setChannel,
10
11
  updateBundle,
11
12
  } from "./native";
12
13
  import { runUpdateProcess } from "./runUpdateProcess";
@@ -65,13 +66,12 @@ export const HotUpdater = {
65
66
  */
66
67
  getMinBundleId,
67
68
  /**
68
- * Fetches the current release channel of the app.
69
+ * Fetches the current channel of the app.
69
70
  *
70
- * By default, if no channel is specified, the app is assigned to the 'production' channel.
71
- * If the app is running in development mode, the channel will be `null`.
72
- *
73
- * @returns {string | null} The current release channel of the app
71
+ * If no channel is specified, the app is assigned to the 'production' channel.
74
72
  *
73
+ * @returns {string} The current release channel of the app
74
+ * @default "production"
75
75
  * @example
76
76
  * ```ts
77
77
  * const channel = HotUpdater.getChannel();
@@ -80,9 +80,17 @@ export const HotUpdater = {
80
80
  */
81
81
  getChannel,
82
82
  /**
83
- * Sets the channel for the app.
83
+ * The initial channel of the native app.
84
+ *
85
+ * @returns {string} The current release channel of the app
86
+ * @default "production"
87
+ * @example
88
+ * ```ts
89
+ * const channel = HotUpdater.getReleaseChannel();
90
+ * console.log(`Current release channel: ${channel}`);
91
+ * ```
84
92
  */
85
- setChannel,
93
+ getReleaseChannel,
86
94
  /**
87
95
  * Adds a listener to HotUpdater events.
88
96
  *
@@ -167,8 +175,9 @@ export const HotUpdater = {
167
175
  /**
168
176
  * Updates the bundle of the app.
169
177
  *
170
- * @param {string} bundleId - The bundle ID of the app
171
- * @param {string} zipUrl - The URL of the zip file
178
+ * @param {UpdateBundleParams} params - Parameters object required for bundle update
179
+ * @param {string} params.bundleId - The bundle ID of the app
180
+ * @param {string|null} params.fileUrl - The URL of the zip file
172
181
  *
173
182
  * @returns {Promise<boolean>} Whether the update was successful
174
183
  *
@@ -187,11 +196,28 @@ export const HotUpdater = {
187
196
  * };
188
197
  * }
189
198
  *
190
- * await HotUpdater.updateBundle(updateInfo.id, updateInfo.fileUrl);
199
+ * await HotUpdater.updateBundle({
200
+ * bundleId: updateInfo.id,
201
+ * fileUrl: updateInfo.fileUrl
202
+ * });
191
203
  * if (updateInfo.shouldForceUpdate) {
192
204
  * HotUpdater.reload();
193
205
  * }
194
206
  * ```
195
207
  */
196
208
  updateBundle,
209
+ /**
210
+ * Fetches the fingerprint of the app.
211
+ *
212
+ * @returns {string} The fingerprint of the app
213
+ *
214
+ * @example
215
+ * ```ts
216
+ * const fingerprint = HotUpdater.getFingerprintHash();
217
+ * console.log(`Fingerprint: ${fingerprint}`);
218
+ * ```
219
+ */
220
+ getFingerprintHash,
197
221
  };
222
+
223
+ export { getUpdateSource } from "./checkForUpdate";
package/src/native.ts CHANGED
@@ -1,37 +1,28 @@
1
+ import type { UpdateStatus, UpdateStrategy } from "@hot-updater/core";
1
2
  import { NativeEventEmitter, Platform } from "react-native";
2
- import type { Spec } from "./specs/NativeHotUpdater";
3
+ import HotUpdaterNative, {
4
+ type UpdateBundleParams,
5
+ } from "./specs/NativeHotUpdater";
6
+
3
7
  const NIL_UUID = "00000000-0000-0000-0000-000000000000";
4
8
 
5
9
  declare const __HOT_UPDATER_BUNDLE_ID: string | undefined;
6
- declare const __HOT_UPDATER_CHANNEL: string | undefined;
10
+ declare const __HOT_UPDATER_FINGERPRINT_HASH_IOS: string | null;
11
+ declare const __HOT_UPDATER_FINGERPRINT_HASH_ANDROID: string | null;
12
+ declare const __HOT_UPDATER_UPDATE_STRATEGY: UpdateStrategy;
13
+ declare const __HOT_UPDATER_CHANNEL: string | null;
7
14
 
8
- const HotUpdater = {
15
+ export const HotUpdaterConstants = {
16
+ OVER_THE_AIR_CHANNEL: __HOT_UPDATER_CHANNEL,
9
17
  HOT_UPDATER_BUNDLE_ID: __HOT_UPDATER_BUNDLE_ID || NIL_UUID,
10
- CHANNEL: __HOT_UPDATER_CHANNEL || (!__DEV__ ? "production" : null),
18
+ FINGERPRINT_HASH: Platform.select({
19
+ ios: __HOT_UPDATER_FINGERPRINT_HASH_IOS,
20
+ android: __HOT_UPDATER_FINGERPRINT_HASH_ANDROID,
21
+ default: null,
22
+ }),
23
+ UPDATE_STRATEGY: __HOT_UPDATER_UPDATE_STRATEGY,
11
24
  };
12
25
 
13
- const RCTNativeHotUpdater = require("./specs/NativeHotUpdater").default;
14
-
15
- const LINKING_ERROR =
16
- // biome-ignore lint/style/useTemplate: <explanation>
17
- `The package '@hot-updater/react-native' doesn't seem to be linked. Make sure: \n\n` +
18
- Platform.select({ ios: "- You have run 'pod install'\n", default: "" }) +
19
- "- You rebuilt the app after installing the package\n" +
20
- "- You are not using Expo Go\n";
21
-
22
- const HotUpdaterNative = (
23
- RCTNativeHotUpdater
24
- ? RCTNativeHotUpdater
25
- : new Proxy(
26
- {},
27
- {
28
- get() {
29
- throw new Error(LINKING_ERROR);
30
- },
31
- },
32
- )
33
- ) as Spec;
34
-
35
26
  export type HotUpdaterEvent = {
36
27
  onProgress: {
37
28
  progress: number;
@@ -50,19 +41,59 @@ export const addListener = <T extends keyof HotUpdaterEvent>(
50
41
  };
51
42
  };
52
43
 
44
+ export type UpdateParams = UpdateBundleParams & {
45
+ status: UpdateStatus;
46
+ };
47
+
53
48
  /**
54
- * Downloads files from given URLs.
49
+ * Downloads files and applies them to the app.
55
50
  *
56
- * @param {string} bundleId - identifier for the bundle id.
57
- * @param {string | null} zipUrl - zip file URL. If null, it means rolling back to the built-in bundle
51
+ * @param {UpdateParams} params - Parameters object required for bundle update
58
52
  * @returns {Promise<boolean>} Resolves with true if download was successful, otherwise rejects with an error.
59
53
  */
60
- export const updateBundle = (
54
+ export async function updateBundle(params: UpdateParams): Promise<boolean>;
55
+ /**
56
+ * @deprecated Use updateBundle(params: UpdateBundleParamsWithStatus) instead
57
+ */
58
+ export async function updateBundle(
61
59
  bundleId: string,
62
- zipUrl: string | null,
63
- ): Promise<boolean> => {
64
- return HotUpdaterNative.updateBundle(bundleId, zipUrl);
65
- };
60
+ fileUrl: string | null,
61
+ ): Promise<boolean>;
62
+ export async function updateBundle(
63
+ paramsOrBundleId: UpdateParams | string,
64
+ fileUrl?: string | null,
65
+ ): Promise<boolean> {
66
+ const updateBundleId =
67
+ typeof paramsOrBundleId === "string"
68
+ ? paramsOrBundleId
69
+ : paramsOrBundleId.bundleId;
70
+
71
+ const status =
72
+ typeof paramsOrBundleId === "string" ? "UPDATE" : paramsOrBundleId.status;
73
+
74
+ const currentBundleId = getBundleId();
75
+
76
+ // updateBundleId <= currentBundleId
77
+ if (
78
+ status === "UPDATE" &&
79
+ updateBundleId.localeCompare(currentBundleId) <= 0
80
+ ) {
81
+ throw new Error(
82
+ "Update bundle id is the same as the current bundle id. Preventing infinite update loop.",
83
+ );
84
+ }
85
+
86
+ if (typeof paramsOrBundleId === "string") {
87
+ return HotUpdaterNative.updateBundle({
88
+ bundleId: updateBundleId,
89
+ fileUrl: fileUrl || null,
90
+ });
91
+ }
92
+ return HotUpdaterNative.updateBundle({
93
+ bundleId: updateBundleId,
94
+ fileUrl: paramsOrBundleId.fileUrl,
95
+ });
96
+ }
66
97
 
67
98
  /**
68
99
  * Fetches the current app version.
@@ -99,19 +130,34 @@ export const getMinBundleId = (): string => {
99
130
  * @returns {Promise<string>} Resolves with the current version id or null if not available.
100
131
  */
101
132
  export const getBundleId = (): string => {
102
- return HotUpdater.HOT_UPDATER_BUNDLE_ID === NIL_UUID
133
+ return HotUpdaterConstants.HOT_UPDATER_BUNDLE_ID === NIL_UUID
103
134
  ? getMinBundleId()
104
- : HotUpdater.HOT_UPDATER_BUNDLE_ID;
135
+ : HotUpdaterConstants.HOT_UPDATER_BUNDLE_ID;
105
136
  };
106
137
 
107
138
  /**
108
- * Sets the channel for the app.
139
+ * Fetches the channel for the app.
140
+ *
141
+ * @returns {string} Resolves with the channel or null if not available.
109
142
  */
110
- export const setChannel = async (channel: string) => {
111
- return HotUpdaterNative.setChannel(channel);
143
+ export const getChannel = (): string => {
144
+ if (HotUpdaterConstants.OVER_THE_AIR_CHANNEL) {
145
+ return HotUpdaterConstants.OVER_THE_AIR_CHANNEL;
146
+ }
147
+ const constants = HotUpdaterNative.getConstants();
148
+ return constants.CHANNEL;
112
149
  };
113
150
 
114
- export const getChannel = (): string | null => {
151
+ export const getReleaseChannel = (): string => {
115
152
  const constants = HotUpdaterNative.getConstants();
116
- return constants?.CHANNEL ?? HotUpdater.CHANNEL ?? null;
153
+ return constants.CHANNEL;
154
+ };
155
+
156
+ /**
157
+ * Fetches the fingerprint for the app.
158
+ *
159
+ * @returns {string | null} Resolves with the fingerprint hash
160
+ */
161
+ export const getFingerprintHash = (): string | null => {
162
+ return HotUpdaterConstants.FINGERPRINT_HASH;
117
163
  };
@@ -1,5 +1,5 @@
1
1
  import { type CheckForUpdateOptions, checkForUpdate } from "./checkForUpdate";
2
- import { getBundleId, reload, updateBundle } from "./native";
2
+ import { getBundleId, reload } from "./native";
3
3
 
4
4
  export interface RunUpdateProcessResponse {
5
5
  status: "ROLLBACK" | "UPDATE" | "UP_TO_DATE";
@@ -63,7 +63,7 @@ export const runUpdateProcess = async ({
63
63
  };
64
64
  }
65
65
 
66
- const isUpdated = await updateBundle(updateInfo.id, updateInfo.fileUrl);
66
+ const isUpdated = await updateInfo.updateBundle();
67
67
  if (isUpdated && updateInfo.shouldForceUpdate && reloadOnForceUpdate) {
68
68
  reload();
69
69
  }
@@ -1,17 +1,15 @@
1
1
  import type { TurboModule } from "react-native";
2
2
  import { TurboModuleRegistry } from "react-native";
3
3
 
4
+ export interface UpdateBundleParams {
5
+ bundleId: string;
6
+ fileUrl: string | null;
7
+ }
8
+
4
9
  export interface Spec extends TurboModule {
5
10
  // Methods
6
11
  reload(): void;
7
- updateBundle(bundleId: string, zipUrl: string | null): Promise<boolean>;
8
- /**
9
- * @deprecated
10
- * use getConstants().APP_VERSION instead
11
- */
12
- getAppVersion(): Promise<string | null>;
13
-
14
- setChannel(channel: string): Promise<void>;
12
+ updateBundle(params: UpdateBundleParams): Promise<boolean>;
15
13
 
16
14
  // EventEmitter
17
15
  addListener(eventName: string): void;
@@ -19,8 +17,8 @@ export interface Spec extends TurboModule {
19
17
  readonly getConstants: () => {
20
18
  MIN_BUNDLE_ID: string;
21
19
  APP_VERSION: string | null;
22
- CHANNEL: string | null;
20
+ CHANNEL: string;
23
21
  };
24
22
  }
25
23
 
26
- export default TurboModuleRegistry.get<Spec>("HotUpdater");
24
+ export default TurboModuleRegistry.getEnforcing<Spec>("HotUpdater");
package/src/wrap.tsx CHANGED
@@ -1,9 +1,9 @@
1
1
  import React from "react";
2
2
  import { useEffect, useLayoutEffect, useState } from "react";
3
3
  import { type CheckForUpdateOptions, checkForUpdate } from "./checkForUpdate";
4
- import { HotUpdaterError } from "./error";
4
+ import type { HotUpdaterError } from "./error";
5
5
  import { useEventCallback } from "./hooks/useEventCallback";
6
- import { getBundleId, reload, updateBundle } from "./native";
6
+ import { getBundleId, reload } from "./native";
7
7
  import type { RunUpdateProcessResponse } from "./runUpdateProcess";
8
8
  import { useHotUpdaterStore } from "./store";
9
9
 
@@ -39,7 +39,7 @@ export interface HotUpdaterOptions extends CheckForUpdateOptions {
39
39
  progress: number;
40
40
  message: string | null;
41
41
  }>;
42
- onError?: (error: HotUpdaterError) => void;
42
+ onError?: (error: HotUpdaterError | Error | unknown) => void;
43
43
  onProgress?: (progress: number) => void;
44
44
  /**
45
45
  * When a force update exists, the app will automatically reload.
@@ -93,7 +93,10 @@ export function wrap<P extends React.JSX.IntrinsicAttributes = object>(
93
93
  }
94
94
 
95
95
  if (updateInfo.shouldForceUpdate === false) {
96
- void updateBundle(updateInfo.id, updateInfo.fileUrl);
96
+ void updateInfo.updateBundle().catch((error) => {
97
+ restOptions.onError?.(error);
98
+ });
99
+
97
100
  restOptions.onUpdateProcessCompleted?.({
98
101
  id: updateInfo.id,
99
102
  status: updateInfo.status,
@@ -103,13 +106,9 @@ export function wrap<P extends React.JSX.IntrinsicAttributes = object>(
103
106
  setUpdateStatus("UPDATE_PROCESS_COMPLETED");
104
107
  return;
105
108
  }
106
-
107
109
  // Force Update Scenario
108
110
  setUpdateStatus("UPDATING");
109
- const isSuccess = await updateBundle(
110
- updateInfo.id,
111
- updateInfo.fileUrl,
112
- );
111
+ const isSuccess = await updateInfo.updateBundle();
113
112
 
114
113
  if (!isSuccess) {
115
114
  throw new Error(
@@ -130,11 +129,8 @@ export function wrap<P extends React.JSX.IntrinsicAttributes = object>(
130
129
 
131
130
  setUpdateStatus("UPDATE_PROCESS_COMPLETED");
132
131
  } catch (error) {
133
- if (error instanceof HotUpdaterError) {
134
- restOptions.onError?.(error);
135
- }
132
+ restOptions.onError?.(error);
136
133
  setUpdateStatus("UPDATE_PROCESS_COMPLETED");
137
- throw error;
138
134
  }
139
135
  });
140
136
 
@@ -1,42 +0,0 @@
1
- package com.hotupdater
2
-
3
- import android.content.Context
4
- import android.content.SharedPreferences
5
- import java.io.File
6
-
7
- /**
8
- * A class that manages SharedPreferences based on the app version.
9
- * Externally, only getItem(key) and setItem(key, value) can be used.
10
- * It constructs the prefs filename based on the app version passed in the constructor,
11
- * and deletes previous files that don't match the current version during initialization.
12
- */
13
- class HotUpdaterPrefs(
14
- private val context: Context,
15
- private val appVersion: String,
16
- ) {
17
- private val prefs: SharedPreferences
18
-
19
- init {
20
- val prefsName = "HotUpdaterPrefs_$appVersion"
21
-
22
- val sharedPrefsDir = File(context.applicationInfo.dataDir, "shared_prefs")
23
- if (sharedPrefsDir.exists() && sharedPrefsDir.isDirectory) {
24
- sharedPrefsDir.listFiles()?.forEach { file ->
25
- if (file.name.startsWith("HotUpdaterPrefs_") && file.name != "$prefsName.xml") {
26
- file.delete()
27
- }
28
- }
29
- }
30
-
31
- prefs = context.getSharedPreferences(prefsName, Context.MODE_PRIVATE)
32
- }
33
-
34
- fun getItem(key: String): String? = prefs.getString(key, null)
35
-
36
- fun setItem(
37
- key: String,
38
- value: String?,
39
- ) {
40
- prefs.edit().putString(key, value).apply()
41
- }
42
- }
@@ -1,12 +0,0 @@
1
- import { type UpdateSource } from "./fetchUpdateInfo";
2
- export interface CheckForUpdateOptions {
3
- source: UpdateSource;
4
- requestHeaders?: Record<string, string>;
5
- onError?: (error: Error) => void;
6
- /**
7
- * The timeout duration for the request.
8
- * @default 5000
9
- */
10
- requestTimeout?: number;
11
- }
12
- export declare function checkForUpdate(options: CheckForUpdateOptions): Promise<import("@hot-updater/core").AppUpdateInfo | null>;
@@ -1,3 +0,0 @@
1
- import type { AppUpdateInfo, GetBundlesArgs } from "@hot-updater/core";
2
- export type UpdateSource = string | (() => Promise<AppUpdateInfo | null>);
3
- export declare const fetchUpdateInfo: (source: UpdateSource, { appVersion, bundleId, platform, minBundleId, channel }: GetBundlesArgs, requestHeaders?: Record<string, string>, onError?: (error: Error) => void, requestTimeout?: number) => Promise<AppUpdateInfo | null>;