@hot-updater/react-native 0.24.0 → 0.24.2

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 (52) hide show
  1. package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +24 -2
  2. package/android/src/main/java/com/hotupdater/BundleMetadata.kt +12 -2
  3. package/android/src/main/java/com/hotupdater/ZipDecompressionStrategy.kt +39 -17
  4. package/android/src/newarch/HotUpdaterModule.kt +33 -21
  5. package/android/src/oldarch/HotUpdaterModule.kt +31 -19
  6. package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +93 -5
  7. package/lib/commonjs/DefaultResolver.js +38 -0
  8. package/lib/commonjs/DefaultResolver.js.map +1 -0
  9. package/lib/commonjs/checkForUpdate.js +35 -50
  10. package/lib/commonjs/checkForUpdate.js.map +1 -1
  11. package/lib/commonjs/index.js +26 -14
  12. package/lib/commonjs/index.js.map +1 -1
  13. package/lib/commonjs/types.js +12 -0
  14. package/lib/commonjs/types.js.map +1 -1
  15. package/lib/commonjs/wrap.js +44 -13
  16. package/lib/commonjs/wrap.js.map +1 -1
  17. package/lib/module/DefaultResolver.js +34 -0
  18. package/lib/module/DefaultResolver.js.map +1 -0
  19. package/lib/module/checkForUpdate.js +35 -50
  20. package/lib/module/checkForUpdate.js.map +1 -1
  21. package/lib/module/index.js +26 -14
  22. package/lib/module/index.js.map +1 -1
  23. package/lib/module/types.js +12 -0
  24. package/lib/module/types.js.map +1 -1
  25. package/lib/module/wrap.js +44 -13
  26. package/lib/module/wrap.js.map +1 -1
  27. package/lib/typescript/commonjs/DefaultResolver.d.ts +10 -0
  28. package/lib/typescript/commonjs/DefaultResolver.d.ts.map +1 -0
  29. package/lib/typescript/commonjs/checkForUpdate.d.ts +2 -1
  30. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  31. package/lib/typescript/commonjs/index.d.ts +3 -3
  32. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  33. package/lib/typescript/commonjs/types.d.ts +115 -0
  34. package/lib/typescript/commonjs/types.d.ts.map +1 -1
  35. package/lib/typescript/commonjs/wrap.d.ts +64 -10
  36. package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
  37. package/lib/typescript/module/DefaultResolver.d.ts +10 -0
  38. package/lib/typescript/module/DefaultResolver.d.ts.map +1 -0
  39. package/lib/typescript/module/checkForUpdate.d.ts +2 -1
  40. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  41. package/lib/typescript/module/index.d.ts +3 -3
  42. package/lib/typescript/module/index.d.ts.map +1 -1
  43. package/lib/typescript/module/types.d.ts +115 -0
  44. package/lib/typescript/module/types.d.ts.map +1 -1
  45. package/lib/typescript/module/wrap.d.ts +64 -10
  46. package/lib/typescript/module/wrap.d.ts.map +1 -1
  47. package/package.json +7 -7
  48. package/src/DefaultResolver.ts +36 -0
  49. package/src/checkForUpdate.ts +43 -59
  50. package/src/index.ts +51 -19
  51. package/src/types.ts +135 -0
  52. package/src/wrap.tsx +161 -72
package/src/wrap.tsx CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  reload,
10
10
  } from "./native";
11
11
  import { useHotUpdaterStore } from "./store";
12
+ import type { HotUpdaterResolver } from "./types";
12
13
 
13
14
  export interface RunUpdateProcessResponse {
14
15
  status: "ROLLBACK" | "UPDATE" | "UP_TO_DATE";
@@ -26,12 +27,6 @@ type UpdateStatus =
26
27
  * Common options shared between auto and manual update modes
27
28
  */
28
29
  interface CommonHotUpdaterOptions {
29
- /**
30
- * Base URL for update server
31
- * @example "https://update.example.com"
32
- */
33
- baseURL: string;
34
-
35
30
  /**
36
31
  * Custom request headers for update checks
37
32
  */
@@ -72,91 +67,190 @@ interface CommonHotUpdaterOptions {
72
67
  onNotifyAppReady?: (result: NotifyAppReadyResult) => void;
73
68
  }
74
69
 
75
- export interface AutoUpdateOptions extends CommonHotUpdaterOptions {
70
+ /**
71
+ * Configuration with baseURL for standard server-based updates
72
+ */
73
+ interface BaseURLConfig {
76
74
  /**
77
- * Update strategy
78
- * - "fingerprint": Use fingerprint hash to check for updates
79
- * - "appVersion": Use app version to check for updates
75
+ * Base URL for update server
76
+ * @example "https://update.example.com"
80
77
  */
81
- updateStrategy: "fingerprint" | "appVersion";
78
+ baseURL: string;
82
79
 
83
80
  /**
84
- * Update mode
85
- * - "auto": Automatically check and download updates
81
+ * Resolver is not allowed when using baseURL
86
82
  */
87
- updateMode: "auto";
83
+ resolver?: never;
84
+ }
88
85
 
89
- onError?: (error: HotUpdaterError | Error | unknown) => void;
86
+ /**
87
+ * Configuration with resolver for custom network operations
88
+ */
89
+ interface ResolverConfig {
90
+ /**
91
+ * Custom resolver for network operations
92
+ */
93
+ resolver: HotUpdaterResolver;
90
94
 
91
95
  /**
92
- * Component to show while downloading a new bundle update.
93
- *
94
- * When an update exists and the bundle is being downloaded, this component will block access
95
- * to the entry point and show download progress.
96
- *
97
- * @see {@link https://hot-updater.dev/docs/react-native-api/wrap#fallback-component}
98
- *
99
- * ```tsx
100
- * HotUpdater.wrap({
101
- * baseURL: "<update-server-url>",
102
- * updateStrategy: "appVersion",
103
- * fallbackComponent: ({ progress = 0 }) => (
104
- * <View style={styles.container}>
105
- * <Text style={styles.text}>Updating... {progress}%</Text>
106
- * </View>
107
- * )
108
- * })(App)
109
- * ```
110
- *
111
- * If not defined, the bundle will download in the background without blocking the screen.
96
+ * baseURL is not allowed when using resolver
112
97
  */
98
+ baseURL?: never;
99
+ }
100
+
101
+ /**
102
+ * Union type ensuring baseURL and resolver are mutually exclusive
103
+ */
104
+ type NetworkConfig = BaseURLConfig | ResolverConfig;
105
+
106
+ export type AutoUpdateOptions = CommonHotUpdaterOptions &
107
+ NetworkConfig & {
108
+ /**
109
+ * Update strategy
110
+ * - "fingerprint": Use fingerprint hash to check for updates
111
+ * - "appVersion": Use app version to check for updates
112
+ */
113
+ updateStrategy: "fingerprint" | "appVersion";
114
+
115
+ /**
116
+ * Update mode
117
+ * - "auto": Automatically check and download updates
118
+ */
119
+ updateMode: "auto";
120
+
121
+ onError?: (error: HotUpdaterError | Error | unknown) => void;
122
+
123
+ /**
124
+ * Component to show while downloading a new bundle update.
125
+ *
126
+ * When an update exists and the bundle is being downloaded, this component will block access
127
+ * to the entry point and show download progress.
128
+ *
129
+ * @see {@link https://hot-updater.dev/docs/react-native-api/wrap#fallback-component}
130
+ *
131
+ * ```tsx
132
+ * HotUpdater.wrap({
133
+ * baseURL: "<update-server-url>",
134
+ * updateStrategy: "appVersion",
135
+ * fallbackComponent: ({ progress = 0 }) => (
136
+ * <View style={styles.container}>
137
+ * <Text style={styles.text}>Updating... {progress}%</Text>
138
+ * </View>
139
+ * )
140
+ * })(App)
141
+ * ```
142
+ *
143
+ * If not defined, the bundle will download in the background without blocking the screen.
144
+ */
145
+ fallbackComponent?: React.FC<{
146
+ status: Exclude<UpdateStatus, "UPDATE_PROCESS_COMPLETED">;
147
+ progress: number;
148
+ message: string | null;
149
+ }>;
150
+
151
+ onProgress?: (progress: number) => void;
152
+
153
+ /**
154
+ * When a force update exists, the app will automatically reload.
155
+ * If `false`, When a force update exists, the app will not reload. `shouldForceUpdate` will be returned as `true` in `onUpdateProcessCompleted`.
156
+ * If `true`, When a force update exists, the app will automatically reload.
157
+ * @default true
158
+ */
159
+ reloadOnForceUpdate?: boolean;
160
+
161
+ /**
162
+ * Callback function that is called when the update process is completed.
163
+ *
164
+ * @see {@link https://hot-updater.dev/docs/react-native-api/wrap#onupdateprocesscompleted}
165
+ */
166
+ onUpdateProcessCompleted?: (response: RunUpdateProcessResponse) => void;
167
+ };
168
+
169
+ export type ManualUpdateOptions = CommonHotUpdaterOptions &
170
+ NetworkConfig & {
171
+ /**
172
+ * Update mode
173
+ * - "manual": Only notify app ready, user manually calls checkForUpdate()
174
+ */
175
+ updateMode: "manual";
176
+ };
177
+
178
+ export type HotUpdaterOptions = AutoUpdateOptions | ManualUpdateOptions;
179
+
180
+ /**
181
+ * Internal options after normalization in index.ts
182
+ * Always has resolver (never baseURL)
183
+ */
184
+ type InternalCommonOptions = {
185
+ resolver: HotUpdaterResolver;
186
+ requestHeaders?: Record<string, string>;
187
+ requestTimeout?: number;
188
+ onNotifyAppReady?: (result: NotifyAppReadyResult) => void;
189
+ };
190
+
191
+ type InternalAutoUpdateOptions = InternalCommonOptions & {
192
+ updateStrategy: "fingerprint" | "appVersion";
193
+ updateMode: "auto";
194
+ onError?: (error: HotUpdaterError | Error | unknown) => void;
113
195
  fallbackComponent?: React.FC<{
114
196
  status: Exclude<UpdateStatus, "UPDATE_PROCESS_COMPLETED">;
115
197
  progress: number;
116
198
  message: string | null;
117
199
  }>;
118
-
119
200
  onProgress?: (progress: number) => void;
120
-
121
- /**
122
- * When a force update exists, the app will automatically reload.
123
- * If `false`, When a force update exists, the app will not reload. `shouldForceUpdate` will be returned as `true` in `onUpdateProcessCompleted`.
124
- * If `true`, When a force update exists, the app will automatically reload.
125
- * @default true
126
- */
127
201
  reloadOnForceUpdate?: boolean;
128
-
129
- /**
130
- * Callback function that is called when the update process is completed.
131
- *
132
- * @see {@link https://hot-updater.dev/docs/react-native-api/wrap#onupdateprocesscompleted}
133
- */
134
202
  onUpdateProcessCompleted?: (response: RunUpdateProcessResponse) => void;
135
- }
203
+ };
136
204
 
137
- export interface ManualUpdateOptions extends CommonHotUpdaterOptions {
138
- /**
139
- * Update mode
140
- * - "manual": Only notify app ready, user manually calls checkForUpdate()
141
- */
205
+ type InternalManualUpdateOptions = InternalCommonOptions & {
142
206
  updateMode: "manual";
143
- }
207
+ };
144
208
 
145
- export type HotUpdaterOptions = AutoUpdateOptions | ManualUpdateOptions;
209
+ export type InternalWrapOptions =
210
+ | InternalAutoUpdateOptions
211
+ | InternalManualUpdateOptions;
212
+
213
+ /**
214
+ * Helper function to handle notifyAppReady flow
215
+ */
216
+ const handleNotifyAppReady = async (options: {
217
+ resolver?: HotUpdaterResolver;
218
+ requestHeaders?: Record<string, string>;
219
+ requestTimeout?: number;
220
+ onNotifyAppReady?: (result: NotifyAppReadyResult) => void;
221
+ }): Promise<void> => {
222
+ try {
223
+ // Always call native notifyAppReady for bundle promotion
224
+ const nativeResult = nativeNotifyAppReady();
225
+
226
+ // If resolver.notifyAppReady exists, call it with simplified params
227
+ if (options.resolver?.notifyAppReady) {
228
+ await options.resolver
229
+ .notifyAppReady({
230
+ status: nativeResult.status,
231
+ crashedBundleId: nativeResult.crashedBundleId,
232
+ requestHeaders: options.requestHeaders,
233
+ requestTimeout: options.requestTimeout,
234
+ })
235
+ .catch((e: unknown) => {
236
+ console.warn("[HotUpdater] Resolver notifyAppReady failed:", e);
237
+ });
238
+ }
239
+
240
+ options.onNotifyAppReady?.(nativeResult);
241
+ } catch (e) {
242
+ console.warn("[HotUpdater] Failed to notify app ready:", e);
243
+ }
244
+ };
146
245
 
147
246
  export function wrap<P extends React.JSX.IntrinsicAttributes = object>(
148
- options: HotUpdaterOptions,
247
+ options: InternalWrapOptions,
149
248
  ): (WrappedComponent: React.ComponentType<P>) => React.ComponentType<P> {
150
249
  if (options.updateMode === "manual") {
151
250
  return (WrappedComponent: React.ComponentType<P>) => {
152
251
  const ManualHOC: React.FC<P> = (props: P) => {
153
252
  useLayoutEffect(() => {
154
- try {
155
- const result = nativeNotifyAppReady();
156
- options.onNotifyAppReady?.(result);
157
- } catch (e) {
158
- console.warn("[HotUpdater] Failed to notify app ready:", e);
159
- }
253
+ void handleNotifyAppReady(options);
160
254
  }, []);
161
255
 
162
256
  return <WrappedComponent {...props} />;
@@ -182,12 +276,12 @@ export function wrap<P extends React.JSX.IntrinsicAttributes = object>(
182
276
  setUpdateStatus("CHECK_FOR_UPDATE");
183
277
 
184
278
  const updateInfo = await checkForUpdate({
185
- baseURL: restOptions.baseURL,
279
+ resolver: restOptions.resolver,
186
280
  updateStrategy: restOptions.updateStrategy,
187
281
  requestHeaders: restOptions.requestHeaders,
188
282
  requestTimeout: restOptions.requestTimeout,
189
283
  onError: restOptions.onError,
190
- } as Parameters<typeof checkForUpdate>[0]);
284
+ });
191
285
 
192
286
  setMessage(updateInfo?.message ?? null);
193
287
 
@@ -250,12 +344,7 @@ export function wrap<P extends React.JSX.IntrinsicAttributes = object>(
250
344
 
251
345
  // Notify native side that app is ready (JS bundle fully loaded)
252
346
  useLayoutEffect(() => {
253
- try {
254
- const result = nativeNotifyAppReady();
255
- restOptions.onNotifyAppReady?.(result);
256
- } catch (e) {
257
- console.warn("[HotUpdater] Failed to notify app ready:", e);
258
- }
347
+ void handleNotifyAppReady(restOptions);
259
348
  }, []);
260
349
 
261
350
  // Start update check