@hot-updater/react-native 0.30.12 → 0.31.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 (119) hide show
  1. package/HotUpdater.podspec +1 -0
  2. package/android/build.gradle +1 -0
  3. package/android/src/main/java/com/hotupdater/BsdiffPatch.kt +284 -0
  4. package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +885 -5
  5. package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +13 -2
  6. package/android/src/main/java/com/hotupdater/OkHttpDownloadService.kt +37 -8
  7. package/android/src/main/java/com/hotupdater/ReactNativeValueConverters.kt +48 -12
  8. package/android/src/main/java/com/hotupdater/SignatureVerifier.kt +60 -22
  9. package/android/src/newarch/HotUpdater.kt +14 -2
  10. package/android/src/newarch/HotUpdaterModule.kt +112 -1
  11. package/android/src/oldarch/HotUpdater.kt +14 -2
  12. package/android/src/oldarch/HotUpdaterModule.kt +107 -3
  13. package/android/src/test/java/com/hotupdater/BsdiffPatchTest.kt +46 -0
  14. package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +29 -1
  15. package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +4 -1
  16. package/ios/HotUpdater/Internal/BsdiffPatchBridge.h +16 -0
  17. package/ios/HotUpdater/Internal/BsdiffPatchBridge.mm +343 -0
  18. package/ios/HotUpdater/Internal/BundleFileStorageService.swift +1093 -47
  19. package/ios/HotUpdater/Internal/HotUpdater-Bridging-Header.h +2 -0
  20. package/ios/HotUpdater/Internal/HotUpdater.mm +12 -2
  21. package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +64 -5
  22. package/ios/HotUpdater/Internal/NotificationExtension.swift +2 -1
  23. package/ios/HotUpdater/Internal/SignatureVerifier.swift +28 -5
  24. package/ios/HotUpdater/Internal/StreamingTarArchiveExtractor.swift +11 -0
  25. package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +11 -5
  26. package/lib/commonjs/DefaultResolver.js +6 -2
  27. package/lib/commonjs/DefaultResolver.js.map +1 -1
  28. package/lib/commonjs/DefaultResolver.spec.js +79 -3
  29. package/lib/commonjs/DefaultResolver.spec.js.map +1 -1
  30. package/lib/commonjs/checkForUpdate.js +6 -0
  31. package/lib/commonjs/checkForUpdate.js.map +1 -1
  32. package/lib/commonjs/index.js +53 -18
  33. package/lib/commonjs/index.js.map +1 -1
  34. package/lib/commonjs/index.spec.js +117 -1
  35. package/lib/commonjs/index.spec.js.map +1 -1
  36. package/lib/commonjs/native.js +40 -16
  37. package/lib/commonjs/native.js.map +1 -1
  38. package/lib/commonjs/native.spec.js +119 -3
  39. package/lib/commonjs/native.spec.js.map +1 -1
  40. package/lib/commonjs/sdkVersion.js +8 -0
  41. package/lib/commonjs/sdkVersion.js.map +1 -0
  42. package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
  43. package/lib/commonjs/store.js +72 -5
  44. package/lib/commonjs/store.js.map +1 -1
  45. package/lib/commonjs/store.spec.js +265 -8
  46. package/lib/commonjs/store.spec.js.map +1 -1
  47. package/lib/commonjs/wrap.js +19 -5
  48. package/lib/commonjs/wrap.js.map +1 -1
  49. package/lib/commonjs/wrap.spec.js +74 -0
  50. package/lib/commonjs/wrap.spec.js.map +1 -1
  51. package/lib/module/DefaultResolver.js +6 -2
  52. package/lib/module/DefaultResolver.js.map +1 -1
  53. package/lib/module/DefaultResolver.spec.js +78 -3
  54. package/lib/module/DefaultResolver.spec.js.map +1 -1
  55. package/lib/module/checkForUpdate.js +6 -0
  56. package/lib/module/checkForUpdate.js.map +1 -1
  57. package/lib/module/index.js +53 -18
  58. package/lib/module/index.js.map +1 -1
  59. package/lib/module/index.spec.js +117 -1
  60. package/lib/module/index.spec.js.map +1 -1
  61. package/lib/module/native.js +40 -16
  62. package/lib/module/native.js.map +1 -1
  63. package/lib/module/native.spec.js +119 -3
  64. package/lib/module/native.spec.js.map +1 -1
  65. package/lib/module/sdkVersion.js +4 -0
  66. package/lib/module/sdkVersion.js.map +1 -0
  67. package/lib/module/specs/NativeHotUpdater.js.map +1 -1
  68. package/lib/module/store.js +72 -5
  69. package/lib/module/store.js.map +1 -1
  70. package/lib/module/store.spec.js +265 -8
  71. package/lib/module/store.spec.js.map +1 -1
  72. package/lib/module/wrap.js +19 -5
  73. package/lib/module/wrap.js.map +1 -1
  74. package/lib/module/wrap.spec.js +74 -0
  75. package/lib/module/wrap.spec.js.map +1 -1
  76. package/lib/typescript/commonjs/DefaultResolver.d.ts.map +1 -1
  77. package/lib/typescript/commonjs/checkForUpdate.d.ts +2 -2
  78. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  79. package/lib/typescript/commonjs/index.d.ts +36 -8
  80. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  81. package/lib/typescript/commonjs/native.d.ts +56 -3
  82. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  83. package/lib/typescript/commonjs/sdkVersion.d.ts +2 -0
  84. package/lib/typescript/commonjs/sdkVersion.d.ts.map +1 -0
  85. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +27 -0
  86. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
  87. package/lib/typescript/commonjs/store.d.ts +5 -0
  88. package/lib/typescript/commonjs/store.d.ts.map +1 -1
  89. package/lib/typescript/commonjs/wrap.d.ts +43 -29
  90. package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
  91. package/lib/typescript/module/DefaultResolver.d.ts.map +1 -1
  92. package/lib/typescript/module/checkForUpdate.d.ts +2 -2
  93. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  94. package/lib/typescript/module/index.d.ts +36 -8
  95. package/lib/typescript/module/index.d.ts.map +1 -1
  96. package/lib/typescript/module/native.d.ts +56 -3
  97. package/lib/typescript/module/native.d.ts.map +1 -1
  98. package/lib/typescript/module/sdkVersion.d.ts +2 -0
  99. package/lib/typescript/module/sdkVersion.d.ts.map +1 -0
  100. package/lib/typescript/module/specs/NativeHotUpdater.d.ts +27 -0
  101. package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
  102. package/lib/typescript/module/store.d.ts +5 -0
  103. package/lib/typescript/module/store.d.ts.map +1 -1
  104. package/lib/typescript/module/wrap.d.ts +43 -29
  105. package/lib/typescript/module/wrap.d.ts.map +1 -1
  106. package/package.json +20 -10
  107. package/src/DefaultResolver.spec.ts +103 -3
  108. package/src/DefaultResolver.ts +9 -2
  109. package/src/checkForUpdate.ts +9 -2
  110. package/src/index.spec.ts +149 -1
  111. package/src/index.ts +99 -40
  112. package/src/native.spec.ts +139 -1
  113. package/src/native.ts +145 -31
  114. package/src/sdkVersion.ts +1 -0
  115. package/src/specs/NativeHotUpdater.ts +28 -0
  116. package/src/store.spec.ts +324 -9
  117. package/src/store.ts +133 -7
  118. package/src/wrap.spec.ts +91 -0
  119. package/src/wrap.tsx +79 -42
package/src/wrap.tsx CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  notifyAppReady as nativeNotifyAppReady,
10
10
  reload,
11
11
  } from "./native";
12
- import { useHotUpdaterStore } from "./store";
12
+ import { type HotUpdaterState, useHotUpdaterStore } from "./store";
13
13
  import type { HotUpdaterBaseURL, HotUpdaterResolver } from "./types";
14
14
 
15
15
  export interface RunUpdateProcessResponse {
@@ -24,8 +24,18 @@ type UpdateStatus =
24
24
  | "UPDATING"
25
25
  | "UPDATE_PROCESS_COMPLETED";
26
26
 
27
+ export type HotUpdaterFallbackComponentProps = {
28
+ status: Exclude<UpdateStatus, "UPDATE_PROCESS_COMPLETED">;
29
+ progress: number;
30
+ downloadedBytes: HotUpdaterState["downloadedBytes"];
31
+ totalBytes: HotUpdaterState["totalBytes"];
32
+ message: string | null;
33
+ artifactType: HotUpdaterState["artifactType"];
34
+ details: HotUpdaterState["details"];
35
+ };
36
+
27
37
  /**
28
- * Common options shared between auto and manual update modes
38
+ * Common options shared by HotUpdater initialization APIs.
29
39
  */
30
40
  interface CommonHotUpdaterOptions {
31
41
  /**
@@ -51,18 +61,24 @@ interface CommonHotUpdaterOptions {
51
61
  *
52
62
  * @example
53
63
  * ```tsx
54
- * HotUpdater.wrap({
64
+ * HotUpdater.init({
55
65
  * baseURL: "https://api.example.com",
56
- * updateMode: "manual",
57
66
  * onNotifyAppReady: ({ status, crashedBundleId }) => {
58
67
  * if (status === "RECOVERED") {
59
68
  * analytics.track("bundle_rollback", { crashedBundleId });
60
69
  * }
61
- * }
62
- * })(App);
70
+ * },
71
+ * });
63
72
  * ```
64
73
  */
65
74
  onNotifyAppReady?: (result: NotifyAppReadyResult) => void;
75
+
76
+ /**
77
+ * Default callback invoked when Hot Updater initialization or update checks
78
+ * fail. Per-call `HotUpdater.checkForUpdate({ onError })` handlers override
79
+ * this callback for that check.
80
+ */
81
+ onError?: (error: HotUpdaterError | Error | unknown) => void;
66
82
  }
67
83
 
68
84
  /**
@@ -105,6 +121,11 @@ type NetworkConfig = BaseURLConfig | ResolverConfig;
105
121
 
106
122
  export type AutoUpdateOptions = CommonHotUpdaterOptions &
107
123
  NetworkConfig & {
124
+ /**
125
+ * Automatic update mode. This is the default and can be omitted.
126
+ */
127
+ updateMode?: "auto";
128
+
108
129
  /**
109
130
  * Update strategy
110
131
  * - "fingerprint": Use fingerprint hash to check for updates
@@ -112,14 +133,6 @@ export type AutoUpdateOptions = CommonHotUpdaterOptions &
112
133
  */
113
134
  updateStrategy: "fingerprint" | "appVersion";
114
135
 
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
136
  /**
124
137
  * Component to show while downloading a new bundle update.
125
138
  *
@@ -142,11 +155,7 @@ export type AutoUpdateOptions = CommonHotUpdaterOptions &
142
155
  *
143
156
  * If not defined, the bundle will download in the background without blocking the screen.
144
157
  */
145
- fallbackComponent?: React.FC<{
146
- status: Exclude<UpdateStatus, "UPDATE_PROCESS_COMPLETED">;
147
- progress: number;
148
- message: string | null;
149
- }>;
158
+ fallbackComponent?: React.FC<HotUpdaterFallbackComponentProps>;
150
159
 
151
160
  onProgress?: (progress: number) => void;
152
161
 
@@ -169,21 +178,25 @@ export type AutoUpdateOptions = CommonHotUpdaterOptions &
169
178
  export type ManualUpdateOptions = CommonHotUpdaterOptions &
170
179
  NetworkConfig & {
171
180
  /**
172
- * Update mode
173
- * - "manual": Only notify app ready, user manually calls checkForUpdate()
181
+ * @deprecated Replace manual `HotUpdater.wrap` with `HotUpdater.init`.
182
+ *
183
+ * ```tsx
184
+ * import { HotUpdater } from "@hot-updater/react-native";
185
+ *
186
+ * HotUpdater.init({
187
+ * baseURL: "<your-update-server-url>",
188
+ * });
189
+ *
190
+ * export default App;
191
+ * ```
192
+ *
193
+ * Then call `HotUpdater.checkForUpdate(...)` from your manual update flow.
194
+ * See https://hot-updater.dev/docs/guides/custom-update
174
195
  */
175
196
  updateMode: "manual";
176
197
  };
177
198
 
178
- type ManualBaseURLUpdateOptions = CommonHotUpdaterOptions &
179
- BaseURLConfig & {
180
- updateMode: "manual";
181
- };
182
-
183
- export type HotUpdaterInitOptions = Omit<
184
- ManualBaseURLUpdateOptions,
185
- "updateMode"
186
- >;
199
+ export type HotUpdaterInitOptions = CommonHotUpdaterOptions & NetworkConfig;
187
200
 
188
201
  export type HotUpdaterOptions = AutoUpdateOptions | ManualUpdateOptions;
189
202
 
@@ -196,17 +209,13 @@ type InternalCommonOptions = {
196
209
  requestHeaders?: Record<string, string>;
197
210
  requestTimeout?: number;
198
211
  onNotifyAppReady?: (result: NotifyAppReadyResult) => void;
212
+ onError?: (error: HotUpdaterError | Error | unknown) => void;
199
213
  };
200
214
 
201
215
  type InternalAutoUpdateOptions = InternalCommonOptions & {
202
216
  updateStrategy: "fingerprint" | "appVersion";
203
217
  updateMode: "auto";
204
- onError?: (error: HotUpdaterError | Error | unknown) => void;
205
- fallbackComponent?: React.FC<{
206
- status: Exclude<UpdateStatus, "UPDATE_PROCESS_COMPLETED">;
207
- progress: number;
208
- message: string | null;
209
- }>;
218
+ fallbackComponent?: React.FC<HotUpdaterFallbackComponentProps>;
210
219
  onProgress?: (progress: number) => void;
211
220
  reloadOnForceUpdate?: boolean;
212
221
  onUpdateProcessCompleted?: (response: RunUpdateProcessResponse) => void;
@@ -224,6 +233,23 @@ export type InternalWrapOptions =
224
233
 
225
234
  type RequestAnimationFrame = (callback: (timestamp: number) => void) => number;
226
235
 
236
+ let didWarnManualWrapDeprecation = false;
237
+
238
+ const warnManualWrapDeprecation = () => {
239
+ if (didWarnManualWrapDeprecation) {
240
+ return;
241
+ }
242
+
243
+ didWarnManualWrapDeprecation = true;
244
+ console.warn(
245
+ '[HotUpdater] HotUpdater.wrap({ updateMode: "manual" }) is deprecated. ' +
246
+ "Move the same baseURL/resolver options to HotUpdater.init({ ... }), " +
247
+ "export your root component directly, and call " +
248
+ "HotUpdater.checkForUpdate(...) from your manual update flow. " +
249
+ "See https://hot-updater.dev/docs/guides/custom-update",
250
+ );
251
+ };
252
+
227
253
  const waitForNextFrame = () =>
228
254
  new Promise<void>((resolve) => {
229
255
  const requestAnimationFrame = (
@@ -248,6 +274,7 @@ const handleNotifyAppReady = async (options: {
248
274
  requestHeaders?: Record<string, string>;
249
275
  requestTimeout?: number;
250
276
  onNotifyAppReady?: (result: NotifyAppReadyResult) => void;
277
+ onError?: (error: HotUpdaterError | Error | unknown) => void;
251
278
  }): Promise<void> => {
252
279
  await waitForNextFrame();
253
280
 
@@ -264,12 +291,14 @@ const handleNotifyAppReady = async (options: {
264
291
  requestTimeout: options.requestTimeout,
265
292
  })
266
293
  .catch((e: unknown) => {
294
+ options.onError?.(e);
267
295
  console.warn("[HotUpdater] Resolver notifyAppReady failed:", e);
268
296
  });
269
297
  }
270
298
 
271
299
  options.onNotifyAppReady?.(nativeResult);
272
300
  } catch (e) {
301
+ options.onError?.(e);
273
302
  console.warn("[HotUpdater] Failed to notify app ready:", e);
274
303
  }
275
304
  };
@@ -278,11 +307,15 @@ export function init(options: InternalInitOptions): void {
278
307
  void handleNotifyAppReady(options);
279
308
  }
280
309
 
281
- export function wrap<P extends React.JSX.IntrinsicAttributes = object>(
310
+ export function wrap(
282
311
  options: InternalWrapOptions,
283
- ): (WrappedComponent: React.ComponentType<P>) => React.ComponentType<P> {
312
+ ): <P extends object>(
313
+ WrappedComponent: React.ComponentType<P>,
314
+ ) => React.ComponentType<P> {
284
315
  if (options.updateMode === "manual") {
285
- return (WrappedComponent: React.ComponentType<P>) => {
316
+ warnManualWrapDeprecation();
317
+
318
+ return <P extends object>(WrappedComponent: React.ComponentType<P>) => {
286
319
  const ManualHOC: React.FC<P> = (props: P) => {
287
320
  useEffect(() => {
288
321
  void handleNotifyAppReady(options);
@@ -295,12 +328,12 @@ export function wrap<P extends React.JSX.IntrinsicAttributes = object>(
295
328
  };
296
329
  }
297
330
 
298
- // updateMode: "auto"
299
331
  const { reloadOnForceUpdate = true, ...restOptions } = options;
300
332
 
301
- return (WrappedComponent: React.ComponentType<P>) => {
333
+ return <P extends object>(WrappedComponent: React.ComponentType<P>) => {
302
334
  const HotUpdaterHOC: React.FC<P> = (props: P) => {
303
- const progress = useHotUpdaterStore((state) => state.progress);
335
+ const progressState = useHotUpdaterStore((state) => state);
336
+ const progress = progressState.progress;
304
337
 
305
338
  const [message, setMessage] = useState<string | null>(null);
306
339
  const [updateStatus, setUpdateStatus] =
@@ -394,9 +427,13 @@ export function wrap<P extends React.JSX.IntrinsicAttributes = object>(
394
427
  const Fallback = restOptions.fallbackComponent;
395
428
  return (
396
429
  <Fallback
430
+ artifactType={progressState.artifactType}
431
+ details={progressState.details}
432
+ downloadedBytes={progressState.downloadedBytes}
397
433
  progress={progress}
398
434
  status={updateStatus}
399
435
  message={message}
436
+ totalBytes={progressState.totalBytes}
400
437
  />
401
438
  );
402
439
  }