@hot-updater/react-native 0.27.0 → 0.28.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 (62) hide show
  1. package/android/build.gradle +12 -0
  2. package/android/src/main/AndroidManifest.xml +3 -0
  3. package/android/src/main/AndroidManifestNew.xml +3 -0
  4. package/android/src/main/cpp/CMakeLists.txt +9 -0
  5. package/android/src/main/cpp/HotUpdaterRecovery.cpp +143 -0
  6. package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +170 -204
  7. package/android/src/main/java/com/hotupdater/BundleMetadata.kt +73 -16
  8. package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +39 -13
  9. package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryManager.kt +533 -0
  10. package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryReceiver.kt +14 -0
  11. package/android/src/newarch/HotUpdaterModule.kt +2 -8
  12. package/android/src/oldarch/HotUpdaterModule.kt +2 -8
  13. package/android/src/oldarch/HotUpdaterSpec.kt +1 -1
  14. package/ios/HotUpdater/Internal/BundleFileStorageService.swift +189 -203
  15. package/ios/HotUpdater/Internal/BundleMetadata.swift +61 -8
  16. package/ios/HotUpdater/Internal/HotUpdater-Bridging-Header.h +9 -1
  17. package/ios/HotUpdater/Internal/HotUpdater.mm +265 -11
  18. package/ios/HotUpdater/Internal/HotUpdaterCrashHandler.h +7 -0
  19. package/ios/HotUpdater/Internal/HotUpdaterCrashHandler.mm +4 -0
  20. package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +293 -9
  21. package/lib/commonjs/native.js +18 -21
  22. package/lib/commonjs/native.js.map +1 -1
  23. package/lib/commonjs/native.spec.js +86 -0
  24. package/lib/commonjs/native.spec.js.map +1 -0
  25. package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
  26. package/lib/commonjs/types.js.map +1 -1
  27. package/lib/commonjs/wrap.js +4 -5
  28. package/lib/commonjs/wrap.js.map +1 -1
  29. package/lib/module/native.js +17 -20
  30. package/lib/module/native.js.map +1 -1
  31. package/lib/module/native.spec.js +85 -0
  32. package/lib/module/native.spec.js.map +1 -0
  33. package/lib/module/specs/NativeHotUpdater.js.map +1 -1
  34. package/lib/module/types.js.map +1 -1
  35. package/lib/module/wrap.js +5 -6
  36. package/lib/module/wrap.js.map +1 -1
  37. package/lib/typescript/commonjs/native.d.ts +4 -15
  38. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  39. package/lib/typescript/commonjs/native.spec.d.ts +2 -0
  40. package/lib/typescript/commonjs/native.spec.d.ts.map +1 -0
  41. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +4 -8
  42. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
  43. package/lib/typescript/commonjs/types.d.ts +2 -3
  44. package/lib/typescript/commonjs/types.d.ts.map +1 -1
  45. package/lib/typescript/commonjs/wrap.d.ts +2 -5
  46. package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
  47. package/lib/typescript/module/native.d.ts +4 -15
  48. package/lib/typescript/module/native.d.ts.map +1 -1
  49. package/lib/typescript/module/native.spec.d.ts +2 -0
  50. package/lib/typescript/module/native.spec.d.ts.map +1 -0
  51. package/lib/typescript/module/specs/NativeHotUpdater.d.ts +4 -8
  52. package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
  53. package/lib/typescript/module/types.d.ts +2 -3
  54. package/lib/typescript/module/types.d.ts.map +1 -1
  55. package/lib/typescript/module/wrap.d.ts +2 -5
  56. package/lib/typescript/module/wrap.d.ts.map +1 -1
  57. package/package.json +6 -6
  58. package/src/native.spec.ts +84 -0
  59. package/src/native.ts +20 -19
  60. package/src/specs/NativeHotUpdater.ts +4 -6
  61. package/src/types.ts +2 -3
  62. package/src/wrap.tsx +7 -11
@@ -3,6 +3,14 @@
3
3
 
4
4
  #import "React/RCTBridgeModule.h"
5
5
  #import "React/RCTEventEmitter.h"
6
+ #import "React/RCTAssert.h"
7
+ #import "React/RCTConstants.h"
8
+ #import "React/RCTRootView.h"
6
9
  #import "React/RCTUtils.h" // Needed for RCTPromiseResolveBlock/RejectBlock in Swift
7
10
  #import <SSZipArchive/SSZipArchive.h>
8
- #endif /* HotUpdater_Bridging_Header_h */
11
+
12
+ @interface HotUpdaterRecoverySignalBridge : NSObject
13
+ + (void)installSignalHandlers:(NSString *)crashMarkerPath;
14
+ + (void)updateLaunchState:(NSString * _Nullable)bundleId shouldRollback:(BOOL)shouldRollback;
15
+ @end
16
+ #endif /* HotUpdater_Bridging_Header_h */
@@ -1,7 +1,13 @@
1
1
  #import "HotUpdater.h"
2
+ #import <React/RCTExceptionsManager.h>
3
+ #import <React/RCTInitializing.h>
2
4
  #import <React/RCTReloadCommand.h>
3
5
  #import <React/RCTLog.h>
4
6
 
7
+ #include <fcntl.h>
8
+ #include <limits.h>
9
+ #include <signal.h>
10
+ #include <unistd.h>
5
11
 
6
12
  #if __has_include("HotUpdater/HotUpdater-Swift.h")
7
13
  #import "HotUpdater/HotUpdater-Swift.h"
@@ -9,17 +15,196 @@
9
15
  #import "HotUpdater-Swift.h"
10
16
  #endif
11
17
 
18
+ @interface HotUpdater (InternalSharedImpl)
19
+ + (HotUpdaterImpl *)sharedImpl;
20
+ @end
21
+
22
+ namespace {
23
+ constexpr size_t kHotUpdaterMaxBundleIdLength = 128;
24
+ const int kHotUpdaterSignals[] = {SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP};
25
+
26
+ char gHotUpdaterCrashMarkerPath[PATH_MAX] = {0};
27
+ char gHotUpdaterBundleId[kHotUpdaterMaxBundleIdLength] = {0};
28
+ volatile sig_atomic_t gHotUpdaterShouldRollback = 0;
29
+ struct sigaction gHotUpdaterPreviousActions[NSIG];
30
+ __weak RCTBridge *gHotUpdaterBridge = nil;
31
+
32
+ size_t HotUpdaterSafeStringLength(const char *value, size_t maxLength)
33
+ {
34
+ size_t length = 0;
35
+ while (length < maxLength && value[length] != '\0') {
36
+ ++length;
37
+ }
38
+ return length;
39
+ }
40
+
41
+ void HotUpdaterSafeCopy(char *destination, size_t destinationSize, const char *source)
42
+ {
43
+ if (destinationSize == 0) {
44
+ return;
45
+ }
46
+
47
+ size_t index = 0;
48
+ while (index + 1 < destinationSize && source[index] != '\0') {
49
+ destination[index] = source[index];
50
+ ++index;
51
+ }
52
+ destination[index] = '\0';
53
+ }
54
+
55
+ void HotUpdaterWriteCrashMarker()
56
+ {
57
+ if (gHotUpdaterCrashMarkerPath[0] == '\0') {
58
+ return;
59
+ }
60
+
61
+ int fd = open(gHotUpdaterCrashMarkerPath, O_WRONLY | O_CREAT | O_TRUNC, 0644);
62
+ if (fd < 0) {
63
+ return;
64
+ }
65
+
66
+ constexpr char prefix[] = "{\"bundleId\":\"";
67
+ constexpr char middle[] = "\",\"shouldRollback\":";
68
+ constexpr char trueLiteral[] = "true";
69
+ constexpr char falseLiteral[] = "false";
70
+ constexpr char suffix[] = "}\n";
71
+
72
+ write(fd, prefix, sizeof(prefix) - 1);
73
+ if (gHotUpdaterBundleId[0] != '\0') {
74
+ write(fd, gHotUpdaterBundleId, HotUpdaterSafeStringLength(gHotUpdaterBundleId, kHotUpdaterMaxBundleIdLength));
75
+ }
76
+ write(fd, middle, sizeof(middle) - 1);
77
+ if (gHotUpdaterShouldRollback != 0) {
78
+ write(fd, trueLiteral, sizeof(trueLiteral) - 1);
79
+ } else {
80
+ write(fd, falseLiteral, sizeof(falseLiteral) - 1);
81
+ }
82
+ write(fd, suffix, sizeof(suffix) - 1);
83
+ close(fd);
84
+ }
85
+
86
+ void HotUpdaterSignalHandler(int signum, siginfo_t *info, void *context);
87
+
88
+ void HotUpdaterForwardToPreviousHandler(int signum, siginfo_t *info, void *context)
89
+ {
90
+ const struct sigaction &previousAction = gHotUpdaterPreviousActions[signum];
91
+
92
+ if ((previousAction.sa_flags & SA_SIGINFO) != 0 && previousAction.sa_sigaction != nullptr &&
93
+ previousAction.sa_sigaction != HotUpdaterSignalHandler) {
94
+ previousAction.sa_sigaction(signum, info, context);
95
+ return;
96
+ }
97
+
98
+ if (previousAction.sa_handler == SIG_IGN) {
99
+ return;
100
+ }
101
+
102
+ if (previousAction.sa_handler != nullptr && previousAction.sa_handler != SIG_DFL &&
103
+ previousAction.sa_handler != SIG_ERR) {
104
+ previousAction.sa_handler(signum);
105
+ return;
106
+ }
107
+
108
+ struct sigaction defaultAction {};
109
+ defaultAction.sa_handler = SIG_DFL;
110
+ sigemptyset(&defaultAction.sa_mask);
111
+ sigaction(signum, &defaultAction, nullptr);
112
+ raise(signum);
113
+ }
114
+
115
+ void HotUpdaterSignalHandler(int signum, siginfo_t *info, void *context)
116
+ {
117
+ HotUpdaterWriteCrashMarker();
118
+ HotUpdaterForwardToPreviousHandler(signum, info, context);
119
+ }
120
+ } // namespace
121
+
122
+ extern "C" void HotUpdaterInstallSignalHandlers(NSString *crashMarkerPath)
123
+ {
124
+ HotUpdaterSafeCopy(gHotUpdaterCrashMarkerPath, sizeof(gHotUpdaterCrashMarkerPath), crashMarkerPath.UTF8String ?: "");
125
+
126
+ struct sigaction action {};
127
+ action.sa_sigaction = HotUpdaterSignalHandler;
128
+ action.sa_flags = SA_SIGINFO | SA_ONSTACK;
129
+ sigemptyset(&action.sa_mask);
130
+
131
+ for (int signum : kHotUpdaterSignals) {
132
+ sigaction(signum, &action, &gHotUpdaterPreviousActions[signum]);
133
+ }
134
+ }
135
+
136
+ extern "C" void HotUpdaterUpdateSignalLaunchState(NSString * _Nullable bundleId, BOOL shouldRollback)
137
+ {
138
+ HotUpdaterSafeCopy(gHotUpdaterBundleId, sizeof(gHotUpdaterBundleId), bundleId.UTF8String ?: "");
139
+ gHotUpdaterShouldRollback = shouldRollback ? 1 : 0;
140
+ }
141
+
142
+ extern "C" BOOL HotUpdaterPerformRecoveryReload(void)
143
+ {
144
+ __block BOOL didTriggerReload = NO;
145
+
146
+ void (^reloadBlock)(void) = ^{
147
+ HotUpdaterImpl *impl = [HotUpdater sharedImpl];
148
+ [impl resetLaunchPreparation];
149
+
150
+ NSURL *bundleURL = [impl bundleURLWithBundle:[NSBundle mainBundle]];
151
+ if (!bundleURL) {
152
+ RCTLogWarn(@"[HotUpdater.mm] Failed to resolve bundle URL for recovery reload");
153
+ return;
154
+ }
155
+
156
+ RCTReloadCommandSetBundleURL(bundleURL);
157
+ RCTBridge *bridge = gHotUpdaterBridge;
158
+ if (bridge) {
159
+ [bridge setValue:bundleURL forKey:@"bundleURL"];
160
+ }
161
+ RCTTriggerReloadCommandListeners(@"HotUpdater recovery reload");
162
+ didTriggerReload = YES;
163
+ };
164
+
165
+ if ([NSThread isMainThread]) {
166
+ reloadBlock();
167
+ } else {
168
+ dispatch_sync(dispatch_get_main_queue(), reloadBlock);
169
+ }
170
+
171
+ return didTriggerReload;
172
+ }
173
+
12
174
 
13
175
  // Define Notification names used for observing Swift Core
14
176
  NSNotificationName const HotUpdaterDownloadProgressUpdateNotification = @"HotUpdaterDownloadProgressUpdate";
15
177
  NSNotificationName const HotUpdaterDownloadDidFinishNotification = @"HotUpdaterDownloadDidFinish";
16
178
 
179
+ @interface HotUpdaterRecoverySignalBridge : NSObject
180
+ @end
181
+
182
+ @implementation HotUpdaterRecoverySignalBridge
183
+
184
+ + (void)installSignalHandlers:(NSString *)crashMarkerPath
185
+ {
186
+ HotUpdaterInstallSignalHandlers(crashMarkerPath);
187
+ }
188
+
189
+ + (void)updateLaunchState:(NSString * _Nullable)bundleId shouldRollback:(BOOL)shouldRollback
190
+ {
191
+ HotUpdaterUpdateSignalLaunchState(bundleId, shouldRollback);
192
+ }
193
+
194
+ @end
195
+
196
+ @interface HotUpdater () <RCTInitializing>
197
+ @end
198
+
17
199
  @implementation HotUpdater {
18
200
  bool hasListeners;
19
201
  // Keep track of tasks ONLY for removing observers when this ObjC instance is invalidated
20
202
  NSMutableSet<NSURLSessionTask *> *observedTasks; // Changed to NSURLSessionTask for broader compatibility if needed
21
203
  }
22
204
 
205
+ @synthesize bridge = _bridge;
206
+ @synthesize moduleRegistry = _moduleRegistry;
207
+
23
208
  + (BOOL)requiresMainQueueSetup {
24
209
  return YES;
25
210
  }
@@ -43,10 +228,25 @@ NSNotificationName const HotUpdaterDownloadDidFinishNotification = @"HotUpdaterD
43
228
  return self;
44
229
  }
45
230
 
231
+ - (void)initialize
232
+ {
233
+ [self configureExceptionsManagerWithModuleRegistry:self.moduleRegistry];
234
+ }
235
+
236
+ - (void)setBridge:(RCTBridge *)bridge
237
+ {
238
+ [super setBridge:bridge];
239
+ gHotUpdaterBridge = bridge;
240
+ [self configureExceptionsManagerWithBridge:bridge];
241
+ }
242
+
46
243
  // Clean up observers when module is invalidated or deallocated
47
244
  - (void)invalidate {
48
245
  RCTLogInfo(@"[HotUpdater.mm] invalidate called, removing observers.");
49
246
  [[NSNotificationCenter defaultCenter] removeObserver:self];
247
+ if (gHotUpdaterBridge == self.bridge) {
248
+ gHotUpdaterBridge = nil;
249
+ }
50
250
  // Swift side should handle KVO observer removal for its tasks
51
251
  [super invalidate];
52
252
  }
@@ -54,6 +254,57 @@ NSNotificationName const HotUpdaterDownloadDidFinishNotification = @"HotUpdaterD
54
254
  - (void)dealloc {
55
255
  RCTLogInfo(@"[HotUpdater.mm] dealloc called, removing observers.");
56
256
  [[NSNotificationCenter defaultCenter] removeObserver:self];
257
+ if (gHotUpdaterBridge == self.bridge) {
258
+ gHotUpdaterBridge = nil;
259
+ }
260
+ }
261
+
262
+ - (void)configureExceptionsManagerWithBridge:(RCTBridge *)bridge
263
+ {
264
+ if (!bridge) {
265
+ return;
266
+ }
267
+
268
+ id exceptionsManager = [bridge moduleForClass:[RCTExceptionsManager class]];
269
+ [self applyExceptionsManagerReloadLimit:exceptionsManager];
270
+ }
271
+
272
+ - (void)configureExceptionsManagerWithModuleRegistry:(id)moduleRegistry
273
+ {
274
+ if (!moduleRegistry || ![moduleRegistry respondsToSelector:@selector(moduleForName:lazilyLoadIfNecessary:)]) {
275
+ return;
276
+ }
277
+
278
+ #pragma clang diagnostic push
279
+ #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
280
+ SEL selector = @selector(moduleForName:lazilyLoadIfNecessary:);
281
+ NSMethodSignature *signature = [moduleRegistry methodSignatureForSelector:selector];
282
+ if (!signature) {
283
+ return;
284
+ }
285
+
286
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
287
+ invocation.target = moduleRegistry;
288
+ invocation.selector = selector;
289
+
290
+ const char *moduleName = "ExceptionsManager";
291
+ BOOL lazilyLoad = YES;
292
+ [invocation setArgument:&moduleName atIndex:2];
293
+ [invocation setArgument:&lazilyLoad atIndex:3];
294
+ [invocation invoke];
295
+
296
+ __unsafe_unretained id exceptionsManager = nil;
297
+ [invocation getReturnValue:&exceptionsManager];
298
+ #pragma clang diagnostic pop
299
+
300
+ [self applyExceptionsManagerReloadLimit:exceptionsManager];
301
+ }
302
+
303
+ - (void)applyExceptionsManagerReloadLimit:(id)exceptionsManager
304
+ {
305
+ if ([exceptionsManager isKindOfClass:[RCTExceptionsManager class]]) {
306
+ ((RCTExceptionsManager *)exceptionsManager).maxReloadAttempts = 0;
307
+ }
57
308
  }
58
309
 
59
310
 
@@ -257,8 +508,12 @@ RCT_EXPORT_MODULE();
257
508
  dispatch_async(dispatch_get_main_queue(), ^{
258
509
  @try {
259
510
  HotUpdaterImpl *impl = [HotUpdater sharedImpl];
511
+ [impl resetLaunchPreparation];
260
512
  NSURL *bundleURL = [impl bundleURLWithBundle:[NSBundle mainBundle]];
261
513
  RCTLogInfo(@"[HotUpdater.mm] Reloading with bundle URL: %@", bundleURL);
514
+ if (bundleURL) {
515
+ RCTReloadCommandSetBundleURL(bundleURL);
516
+ }
262
517
  if (bundleURL && super.bridge) {
263
518
  [super.bridge setValue:bundleURL forKey:@"bundleURL"];
264
519
  } else if (!super.bridge) {
@@ -300,14 +555,10 @@ RCT_EXPORT_MODULE();
300
555
  [impl updateBundle:paramDict resolver:resolve rejecter:reject];
301
556
  }
302
557
 
303
- - (NSDictionary *)notifyAppReady:(JS::NativeHotUpdater::SpecNotifyAppReadyParams &)params {
304
- NSString *bundleId = nil;
305
- if (params.bundleId()) {
306
- bundleId = params.bundleId();
307
- }
308
- NSLog(@"[HotUpdater.mm] notifyAppReady called with bundleId: %@", bundleId);
558
+ - (NSDictionary *)notifyAppReady {
559
+ NSLog(@"[HotUpdater.mm] notifyAppReady called");
309
560
  HotUpdaterImpl *impl = [HotUpdater sharedImpl];
310
- return [impl notifyAppReadyWithBundleId:bundleId];
561
+ return [impl notifyAppReady];
311
562
  }
312
563
 
313
564
  - (NSArray<NSString *> *)getCrashHistory {
@@ -362,8 +613,12 @@ RCT_EXPORT_METHOD(reload:(RCTPromiseResolveBlock)resolve
362
613
  dispatch_async(dispatch_get_main_queue(), ^{
363
614
  @try {
364
615
  HotUpdaterImpl *impl = [HotUpdater sharedImpl];
616
+ [impl resetLaunchPreparation];
365
617
  NSURL *bundleURL = [impl bundleURLWithBundle:[NSBundle mainBundle]];
366
618
  RCTLogInfo(@"[HotUpdater.mm] Reloading with bundle URL: %@", bundleURL);
619
+ if (bundleURL) {
620
+ RCTReloadCommandSetBundleURL(bundleURL);
621
+ }
367
622
  if (bundleURL && super.bridge) {
368
623
  [super.bridge setValue:bundleURL forKey:@"bundleURL"];
369
624
  } else if (!super.bridge) {
@@ -391,11 +646,10 @@ RCT_EXPORT_METHOD(updateBundle:(NSDictionary *)params
391
646
  [impl updateBundle:params resolver:resolve rejecter:reject];
392
647
  }
393
648
 
394
- RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(notifyAppReady:(NSDictionary *)params) {
395
- NSString *bundleId = params[@"bundleId"];
396
- NSLog(@"[HotUpdater.mm] notifyAppReady called with bundleId: %@", bundleId);
649
+ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(notifyAppReady) {
650
+ NSLog(@"[HotUpdater.mm] notifyAppReady called");
397
651
  HotUpdaterImpl *impl = [HotUpdater sharedImpl];
398
- return [impl notifyAppReadyWithBundleId:bundleId];
652
+ return [impl notifyAppReady];
399
653
  }
400
654
 
401
655
  RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getCrashHistory) {
@@ -0,0 +1,7 @@
1
+ #ifndef HotUpdaterCrashHandler_h
2
+ #define HotUpdaterCrashHandler_h
3
+
4
+ // Compatibility shim for stale CocoaPods projects that still reference the
5
+ // old crash-handler file names before `pod install` refreshes the source list.
6
+
7
+ #endif /* HotUpdaterCrashHandler_h */
@@ -0,0 +1,4 @@
1
+ #import "HotUpdaterCrashHandler.h"
2
+
3
+ // Compatibility shim for stale CocoaPods projects that still compile the old
4
+ // crash-handler translation unit. Recovery logic now lives in `HotUpdater.mm`.