@hot-updater/react-native 1.0.0-rc.14 → 1.0.0-rc.15

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 (181) hide show
  1. package/android/proguard-rules.pro +2 -2
  2. package/android/src/main/java/com/hotupdater/BuiltInAssetResolver.kt +286 -0
  3. package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +650 -636
  4. package/android/src/main/java/com/hotupdater/BundleMetadata.kt +3 -0
  5. package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +0 -13
  6. package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +10 -14
  7. package/android/src/main/java/com/hotupdater/OkHttpDownloadService.kt +19 -0
  8. package/android/src/main/java/com/hotupdater/TarArchiveInputStream.kt +214 -375
  9. package/android/src/main/java/com/hotupdater/TarBrArchiveExtractor.kt +119 -0
  10. package/android/src/newarch/HotUpdater.kt +7 -9
  11. package/android/src/newarch/HotUpdaterModule.kt +40 -20
  12. package/android/src/oldarch/HotUpdater.kt +7 -9
  13. package/android/src/oldarch/HotUpdaterModule.kt +40 -20
  14. package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +1126 -74
  15. package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +4 -5
  16. package/android/src/test/java/com/hotupdater/TarBrArchiveExtractorTest.kt +206 -0
  17. package/ios/HotUpdater/Internal/BrotliFileDecompressor.swift +167 -0
  18. package/ios/HotUpdater/Internal/BuiltInAssetResolver.swift +196 -0
  19. package/ios/HotUpdater/Internal/BundleFileStorageService.swift +773 -1016
  20. package/ios/HotUpdater/Internal/BundleMetadata.swift +5 -0
  21. package/ios/HotUpdater/Internal/FileUtilities.swift +64 -0
  22. package/ios/HotUpdater/Internal/HotUpdater-Bridging-Header.h +0 -1
  23. package/ios/HotUpdater/Internal/HotUpdater.mm +5 -27
  24. package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +47 -31
  25. package/ios/HotUpdater/Internal/NotificationExtension.swift +0 -2
  26. package/ios/HotUpdater/Internal/TarArchiveExtractor.swift +182 -311
  27. package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +13 -72
  28. package/lib/commonjs/checkForUpdate.js +7 -10
  29. package/lib/commonjs/checkForUpdate.js.map +1 -1
  30. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +9 -4
  31. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +1 -1
  32. package/lib/commonjs/error.js +4 -13
  33. package/lib/commonjs/error.js.map +1 -1
  34. package/lib/commonjs/fetchJSON.js +9 -2
  35. package/lib/commonjs/fetchJSON.js.map +1 -1
  36. package/lib/commonjs/httpClient.js +44 -27
  37. package/lib/commonjs/httpClient.js.map +1 -1
  38. package/lib/commonjs/httpClient.spec.js +57 -5
  39. package/lib/commonjs/httpClient.spec.js.map +1 -1
  40. package/lib/commonjs/index.js +2 -6
  41. package/lib/commonjs/index.js.map +1 -1
  42. package/lib/commonjs/native.js +7 -8
  43. package/lib/commonjs/native.js.map +1 -1
  44. package/lib/commonjs/native.spec.js +21 -16
  45. package/lib/commonjs/native.spec.js.map +1 -1
  46. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js +7 -0
  47. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +1 -1
  48. package/lib/commonjs/notifyAppReadyInsights.js +4 -1
  49. package/lib/commonjs/notifyAppReadyInsights.js.map +1 -1
  50. package/lib/commonjs/notifyAppReadyInsights.spec.js +59 -0
  51. package/lib/commonjs/notifyAppReadyInsights.spec.js.map +1 -1
  52. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js +7 -0
  53. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +1 -1
  54. package/lib/commonjs/sdkVersion.js +1 -1
  55. package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
  56. package/lib/commonjs/store.js +3 -10
  57. package/lib/commonjs/store.js.map +1 -1
  58. package/lib/commonjs/store.notification.spec.js +12 -4
  59. package/lib/commonjs/store.notification.spec.js.map +1 -1
  60. package/lib/commonjs/store.spec.js +2 -136
  61. package/lib/commonjs/store.spec.js.map +1 -1
  62. package/lib/commonjs/wrap.js +1 -3
  63. package/lib/commonjs/wrap.js.map +1 -1
  64. package/lib/commonjs/wrap.spec.js +7 -0
  65. package/lib/commonjs/wrap.spec.js.map +1 -1
  66. package/lib/module/checkForUpdate.js +7 -10
  67. package/lib/module/checkForUpdate.js.map +1 -1
  68. package/lib/module/checkForUpdate.releaseCatalog.spec.js +9 -4
  69. package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +1 -1
  70. package/lib/module/error.js +4 -13
  71. package/lib/module/error.js.map +1 -1
  72. package/lib/module/fetchJSON.js +7 -1
  73. package/lib/module/fetchJSON.js.map +1 -1
  74. package/lib/module/httpClient.js +46 -29
  75. package/lib/module/httpClient.js.map +1 -1
  76. package/lib/module/httpClient.spec.js +57 -5
  77. package/lib/module/httpClient.spec.js.map +1 -1
  78. package/lib/module/index.js +2 -6
  79. package/lib/module/index.js.map +1 -1
  80. package/lib/module/native.js +7 -8
  81. package/lib/module/native.js.map +1 -1
  82. package/lib/module/native.spec.js +21 -16
  83. package/lib/module/native.spec.js.map +1 -1
  84. package/lib/module/notifyAppReadyInsights.boundary.spec.js +7 -0
  85. package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +1 -1
  86. package/lib/module/notifyAppReadyInsights.js +5 -2
  87. package/lib/module/notifyAppReadyInsights.js.map +1 -1
  88. package/lib/module/notifyAppReadyInsights.spec.js +59 -0
  89. package/lib/module/notifyAppReadyInsights.spec.js.map +1 -1
  90. package/lib/module/notifyAppReadyInsights.transport.spec.js +7 -0
  91. package/lib/module/notifyAppReadyInsights.transport.spec.js.map +1 -1
  92. package/lib/module/sdkVersion.js +1 -1
  93. package/lib/module/specs/NativeHotUpdater.js.map +1 -1
  94. package/lib/module/store.js +3 -10
  95. package/lib/module/store.js.map +1 -1
  96. package/lib/module/store.notification.spec.js +12 -4
  97. package/lib/module/store.notification.spec.js.map +1 -1
  98. package/lib/module/store.spec.js +2 -136
  99. package/lib/module/store.spec.js.map +1 -1
  100. package/lib/module/wrap.js +1 -3
  101. package/lib/module/wrap.js.map +1 -1
  102. package/lib/module/wrap.spec.js +7 -0
  103. package/lib/module/wrap.spec.js.map +1 -1
  104. package/lib/typescript/commonjs/checkForUpdate.d.ts +1 -2
  105. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  106. package/lib/typescript/commonjs/error.d.ts +4 -13
  107. package/lib/typescript/commonjs/error.d.ts.map +1 -1
  108. package/lib/typescript/commonjs/fetchJSON.d.ts +4 -0
  109. package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
  110. package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
  111. package/lib/typescript/commonjs/index.d.ts +0 -2
  112. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  113. package/lib/typescript/commonjs/native.d.ts +1 -6
  114. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  115. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +1 -1
  116. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +1 -1
  117. package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
  118. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +8 -35
  119. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
  120. package/lib/typescript/commonjs/store.d.ts +1 -3
  121. package/lib/typescript/commonjs/store.d.ts.map +1 -1
  122. package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
  123. package/lib/typescript/commonjs/wrap.types.d.ts +0 -2
  124. package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
  125. package/lib/typescript/module/checkForUpdate.d.ts +1 -2
  126. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  127. package/lib/typescript/module/error.d.ts +4 -13
  128. package/lib/typescript/module/error.d.ts.map +1 -1
  129. package/lib/typescript/module/fetchJSON.d.ts +4 -0
  130. package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
  131. package/lib/typescript/module/httpClient.d.ts.map +1 -1
  132. package/lib/typescript/module/index.d.ts +0 -2
  133. package/lib/typescript/module/index.d.ts.map +1 -1
  134. package/lib/typescript/module/native.d.ts +1 -6
  135. package/lib/typescript/module/native.d.ts.map +1 -1
  136. package/lib/typescript/module/notifyAppReadyInsights.d.ts +1 -1
  137. package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +1 -1
  138. package/lib/typescript/module/sdkVersion.d.ts +1 -1
  139. package/lib/typescript/module/specs/NativeHotUpdater.d.ts +8 -35
  140. package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
  141. package/lib/typescript/module/store.d.ts +1 -3
  142. package/lib/typescript/module/store.d.ts.map +1 -1
  143. package/lib/typescript/module/wrap.d.ts.map +1 -1
  144. package/lib/typescript/module/wrap.types.d.ts +0 -2
  145. package/lib/typescript/module/wrap.types.d.ts.map +1 -1
  146. package/package.json +3 -3
  147. package/src/checkForUpdate.releaseCatalog.spec.ts +10 -4
  148. package/src/checkForUpdate.ts +5 -11
  149. package/src/error.ts +4 -14
  150. package/src/fetchJSON.ts +12 -1
  151. package/src/httpClient.spec.ts +70 -5
  152. package/src/httpClient.ts +67 -53
  153. package/src/index.ts +1 -5
  154. package/src/native.spec.ts +23 -16
  155. package/src/native.ts +12 -24
  156. package/src/notifyAppReadyInsights.boundary.spec.ts +8 -0
  157. package/src/notifyAppReadyInsights.spec.ts +60 -0
  158. package/src/notifyAppReadyInsights.transport.spec.ts +8 -0
  159. package/src/notifyAppReadyInsights.ts +13 -1
  160. package/src/sdkVersion.ts +1 -1
  161. package/src/specs/NativeHotUpdater.ts +8 -36
  162. package/src/store.notification.spec.ts +13 -3
  163. package/src/store.spec.ts +0 -157
  164. package/src/store.ts +2 -23
  165. package/src/wrap.spec.ts +8 -0
  166. package/src/wrap.tsx +0 -2
  167. package/src/wrap.types.ts +0 -2
  168. package/android/src/main/java/com/hotupdater/DecompressService.kt +0 -89
  169. package/android/src/main/java/com/hotupdater/DecompressionStrategy.kt +0 -26
  170. package/android/src/main/java/com/hotupdater/TarBrDecompressionStrategy.kt +0 -117
  171. package/android/src/main/java/com/hotupdater/TarGzDecompressionStrategy.kt +0 -117
  172. package/android/src/main/java/com/hotupdater/ZipDecompressionStrategy.kt +0 -192
  173. package/android/src/test/java/com/hotupdater/TarArchiveInputStreamTest.kt +0 -68
  174. package/ios/HotUpdater/Internal/ArchiveExtractionUtilities.swift +0 -178
  175. package/ios/HotUpdater/Internal/DecompressService.swift +0 -112
  176. package/ios/HotUpdater/Internal/DecompressionStrategy.swift +0 -22
  177. package/ios/HotUpdater/Internal/StreamingTarArchiveExtractor.swift +0 -370
  178. package/ios/HotUpdater/Internal/TarBrDecompressionStrategy.swift +0 -39
  179. package/ios/HotUpdater/Internal/TarGzDecompressionStrategy.swift +0 -59
  180. package/ios/HotUpdater/Internal/ZipArchiveExtractor.swift +0 -482
  181. package/ios/HotUpdater/Internal/ZipDecompressionStrategy.swift +0 -56
@@ -84,6 +84,7 @@ public struct BundleMetadata: Codable {
84
84
  var stableSelection: PersistedSelection?
85
85
  var stagingSelection: PersistedSelection?
86
86
  var verificationPending: Bool
87
+ var launchInProgress: Bool
87
88
  var pendingTransition: PendingBundleTransition?
88
89
  var pendingSelectionTransition: PendingSelectionTransition?
89
90
  var highestSeenCatalogs: [String: CatalogHighWater]
@@ -98,6 +99,7 @@ public struct BundleMetadata: Codable {
98
99
  case stableSelection = "stable_selection"
99
100
  case stagingSelection = "staging_selection"
100
101
  case verificationPending = "verification_pending"
102
+ case launchInProgress = "launch_in_progress"
101
103
  case pendingTransition = "pending_transition"
102
104
  case pendingSelectionTransition = "pending_selection_transition"
103
105
  case highestSeenCatalogs = "highest_seen_catalogs"
@@ -113,6 +115,7 @@ public struct BundleMetadata: Codable {
113
115
  stableSelection: PersistedSelection? = nil,
114
116
  stagingSelection: PersistedSelection? = nil,
115
117
  verificationPending: Bool = false,
118
+ launchInProgress: Bool = false,
116
119
  pendingTransition: PendingBundleTransition? = nil,
117
120
  pendingSelectionTransition: PendingSelectionTransition? = nil,
118
121
  highestSeenCatalogs: [String: CatalogHighWater] = [:],
@@ -126,6 +129,7 @@ public struct BundleMetadata: Codable {
126
129
  self.stableSelection = stableSelection
127
130
  self.stagingSelection = stagingSelection
128
131
  self.verificationPending = verificationPending
132
+ self.launchInProgress = launchInProgress
129
133
  self.pendingTransition = pendingTransition
130
134
  self.pendingSelectionTransition = pendingSelectionTransition
131
135
  self.highestSeenCatalogs = highestSeenCatalogs
@@ -144,6 +148,7 @@ public struct BundleMetadata: Codable {
144
148
  stagingSelection = try values.decodeIfPresent(PersistedSelection.self, forKey: .stagingSelection)
145
149
  ?? stagingBundleId.map(PersistedSelection.legacyBundle)
146
150
  verificationPending = try values.decodeIfPresent(Bool.self, forKey: .verificationPending) ?? false
151
+ launchInProgress = try values.decodeIfPresent(Bool.self, forKey: .launchInProgress) ?? false
147
152
  pendingTransition = try values.decodeIfPresent(PendingBundleTransition.self, forKey: .pendingTransition)
148
153
  pendingSelectionTransition = try values.decodeIfPresent(PendingSelectionTransition.self, forKey: .pendingSelectionTransition)
149
154
  highestSeenCatalogs = try values.decodeIfPresent([String: CatalogHighWater].self, forKey: .highestSeenCatalogs) ?? [:]
@@ -0,0 +1,64 @@
1
+ import Foundation
2
+
3
+ enum FileUtilities {
4
+ static func readUpToCount(from handle: FileHandle, count: Int) throws -> Data? {
5
+ guard count >= 0 else {
6
+ throw NSError(
7
+ domain: "FileUtilities",
8
+ code: 1,
9
+ userInfo: [NSLocalizedDescriptionKey: "Invalid read size: \(count)"]
10
+ )
11
+ }
12
+
13
+ if count == 0 {
14
+ return Data()
15
+ }
16
+
17
+ if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) {
18
+ return try handle.read(upToCount: count)
19
+ }
20
+
21
+ return handle.readData(ofLength: count)
22
+ }
23
+
24
+ static func normalizedRelativePath(from rawPath: String) -> String? {
25
+ let candidate = rawPath.trimmingCharacters(in: .whitespacesAndNewlines)
26
+ guard !candidate.isEmpty,
27
+ !candidate.contains("\0"),
28
+ !candidate.contains("\\"),
29
+ !candidate.hasPrefix("/"),
30
+ candidate.range(of: #"^[A-Za-z]:"#, options: .regularExpression) == nil
31
+ else {
32
+ return nil
33
+ }
34
+
35
+ let components = candidate.split(separator: "/").map(String.init)
36
+ guard !components.isEmpty,
37
+ components.count == candidate.split(separator: "/", omittingEmptySubsequences: false).count,
38
+ !components.contains(".."),
39
+ !components.contains(".")
40
+ else {
41
+ return nil
42
+ }
43
+
44
+ return components.joined(separator: "/")
45
+ }
46
+
47
+ static func fileURL(for relativePath: String, destinationRoot: String) throws -> URL {
48
+ let rootURL = URL(fileURLWithPath: destinationRoot, isDirectory: true)
49
+ let targetURL = rootURL.appendingPathComponent(relativePath)
50
+ let standardizedRoot = rootURL.standardizedFileURL.path
51
+ let standardizedTarget = targetURL.standardizedFileURL.path
52
+
53
+ guard standardizedTarget == standardizedRoot ||
54
+ standardizedTarget.hasPrefix(standardizedRoot + "/") else {
55
+ throw NSError(
56
+ domain: "FileUtilities",
57
+ code: 2,
58
+ userInfo: [NSLocalizedDescriptionKey: "Path traversal attempt detected: \(relativePath)"]
59
+ )
60
+ }
61
+
62
+ return targetURL
63
+ }
64
+ }
@@ -7,7 +7,6 @@
7
7
  #import "React/RCTConstants.h"
8
8
  #import "React/RCTRootView.h"
9
9
  #import "React/RCTUtils.h" // Needed for RCTPromiseResolveBlock/RejectBlock in Swift
10
- #import <SSZipArchive/SSZipArchive.h>
11
10
  #import "BsdiffPatchBridge.h"
12
11
 
13
12
  @interface HotUpdaterRecoverySignalBridge : NSObject
@@ -274,8 +274,6 @@ extern "C" void *HotUpdaterCopyMinBundleId(void)
274
274
 
275
275
 
276
276
  // Define Notification names used for observing Swift Core
277
- NSNotificationName const HotUpdaterDownloadProgressUpdateNotification = @"HotUpdaterDownloadProgressUpdate";
278
- NSNotificationName const HotUpdaterDownloadDidFinishNotification = @"HotUpdaterDownloadDidFinish";
279
277
  NSNotificationName const HotUpdaterUpdateProgressDidChangeNotification = @"HotUpdaterUpdateProgressDidChange";
280
278
 
281
279
  @interface HotUpdaterRecoverySignalBridge : NSObject
@@ -300,8 +298,6 @@ NSNotificationName const HotUpdaterUpdateProgressDidChangeNotification = @"HotUp
300
298
 
301
299
  @implementation HotUpdater {
302
300
  bool hasListeners;
303
- // Keep track of tasks ONLY for removing observers when this ObjC instance is invalidated
304
- NSMutableSet<NSURLSessionTask *> *observedTasks; // Changed to NSURLSessionTask for broader compatibility if needed
305
301
  }
306
302
 
307
303
  @synthesize bridge = _bridge;
@@ -314,16 +310,10 @@ NSNotificationName const HotUpdaterUpdateProgressDidChangeNotification = @"HotUp
314
310
  - (instancetype)init {
315
311
  self = [super init];
316
312
  if (self) {
317
- observedTasks = [NSMutableSet set];
318
-
319
313
  [[NSNotificationCenter defaultCenter] addObserver:self
320
314
  selector:@selector(handleDownloadProgress:)
321
315
  name:HotUpdaterUpdateProgressDidChangeNotification
322
316
  object:nil];
323
- [[NSNotificationCenter defaultCenter] addObserver:self
324
- selector:@selector(handleDownloadCompletion:)
325
- name:HotUpdaterDownloadDidFinishNotification
326
- object:nil];
327
317
 
328
318
  _lastUpdateTime = 0;
329
319
  }
@@ -349,7 +339,6 @@ NSNotificationName const HotUpdaterUpdateProgressDidChangeNotification = @"HotUp
349
339
  if (gHotUpdaterBridge == self.bridge) {
350
340
  gHotUpdaterBridge = nil;
351
341
  }
352
- // Swift side should handle KVO observer removal for its tasks
353
342
  [super invalidate];
354
343
  }
355
344
 
@@ -520,14 +509,6 @@ RCT_EXPORT_MODULE();
520
509
  }
521
510
  }
522
511
 
523
- - (void)handleDownloadCompletion:(NSNotification *)notification {
524
- NSURLSessionTask *task = notification.object; // Task that finished
525
- RCTLogInfo(@"[HotUpdater.mm] Received download completion notification for task: %@", task.originalRequest.URL);
526
- // Swift side handles KVO observer removal internally now when task finishes.
527
- // No specific cleanup needed here based on this notification anymore.
528
- }
529
-
530
-
531
512
  #pragma mark - React Native Events (Keep as is)
532
513
 
533
514
  - (NSArray<NSString *> *)supportedEvents {
@@ -594,20 +575,17 @@ RCT_EXPORT_MODULE();
594
575
  if (params.bundleId()) {
595
576
  paramDict[@"bundleId"] = params.bundleId();
596
577
  }
597
- if (params.fileUrl()) {
598
- paramDict[@"fileUrl"] = params.fileUrl();
599
- }
600
- if (params.fileHash()) {
601
- paramDict[@"fileHash"] = params.fileHash();
602
- }
603
578
  if (params.manifestUrl()) {
604
579
  paramDict[@"manifestUrl"] = params.manifestUrl();
605
580
  }
606
581
  if (params.manifestFileHash()) {
607
582
  paramDict[@"manifestFileHash"] = params.manifestFileHash();
608
583
  }
609
- if (params.changedAssets()) {
610
- paramDict[@"changedAssets"] = params.changedAssets();
584
+ if (params.archiveUrl()) {
585
+ paramDict[@"archiveUrl"] = params.archiveUrl();
586
+ }
587
+ if (params.assets()) {
588
+ paramDict[@"assets"] = params.assets();
611
589
  }
612
590
  if (params.channel()) {
613
591
  paramDict[@"channel"] = params.channel();
@@ -61,6 +61,21 @@ private func hotUpdaterGetMinBundleId() -> String {
61
61
  private static let DEFAULT_CHANNEL = "production"
62
62
  private static let CHANNEL_STORAGE_KEY = "HotUpdaterChannel"
63
63
 
64
+ private static func parseSafeByteSize(_ value: Any?) -> Int64? {
65
+ guard let number = value as? NSNumber,
66
+ String(cString: number.objCType) != "c" else {
67
+ return nil
68
+ }
69
+ let doubleValue = number.doubleValue
70
+ guard doubleValue.isFinite,
71
+ doubleValue >= 0,
72
+ doubleValue <= 9_007_199_254_740_991,
73
+ doubleValue.rounded(.towardZero) == doubleValue else {
74
+ return nil
75
+ }
76
+ return Int64(doubleValue)
77
+ }
78
+
64
79
  // MARK: - Initialization
65
80
 
66
81
  /**
@@ -71,12 +86,9 @@ private func hotUpdaterGetMinBundleId() -> String {
71
86
  let isolationKey = HotUpdaterImpl.getIsolationKey()
72
87
  let preferences = VersionedPreferencesService()
73
88
  let downloadService = URLSessionDownloadService()
74
- let decompressService = DecompressService()
75
-
76
89
  let bundleStorage = BundleFileStorageService(
77
90
  fileSystem: fileSystem,
78
91
  downloadService: downloadService,
79
- decompressService: decompressService,
80
92
  preferences: preferences,
81
93
  isolationKey: isolationKey,
82
94
  builtInBundleIdProvider: { HotUpdaterImpl.minBundleId() }
@@ -231,7 +243,7 @@ private func hotUpdaterGetMinBundleId() -> String {
231
243
  /**
232
244
  * Updates the bundle from JavaScript bridge.
233
245
  * This method acts as the primary error boundary for all bundle operations.
234
- * @param params Dictionary with bundleId and fileUrl parameters
246
+ * @param params Dictionary with bundleId, manifest, and asset parameters
235
247
  * @param resolve Promise resolve callback
236
248
  * @param reject Promise reject callback
237
249
  */
@@ -255,36 +267,40 @@ private func hotUpdaterGetMinBundleId() -> String {
255
267
  return
256
268
  }
257
269
 
258
- let fileUrlString = data["fileUrl"] as? String ?? ""
259
-
260
- var fileUrl: URL? = nil
261
- if !fileUrlString.isEmpty {
262
- guard let url = URL(string: fileUrlString) else {
263
- let error = NSError(domain: "HotUpdater", code: 0,
264
- userInfo: [NSLocalizedDescriptionKey: "Invalid 'fileUrl' provided: \(fileUrlString)"])
265
- reject("INVALID_FILE_URL", error.localizedDescription, error)
266
- return
267
- }
268
- fileUrl = url
270
+ guard let manifestFileHash = data["manifestFileHash"] as? String,
271
+ !manifestFileHash.isEmpty else {
272
+ let error = NSError(domain: "HotUpdater", code: 0,
273
+ userInfo: [NSLocalizedDescriptionKey: "Missing manifest file hash"])
274
+ reject("INVALID_MANIFEST", error.localizedDescription, error)
275
+ return
269
276
  }
270
-
271
- // Extract fileHash if provided
272
- let fileHash = data["fileHash"] as? String
273
- let manifestFileHash = data["manifestFileHash"] as? String
274
277
  let channel = data["channel"] as? String
275
278
  let manifestUrlString = data["manifestUrl"] as? String ?? ""
276
- var manifestUrl: URL? = nil
277
- if !manifestUrlString.isEmpty {
278
- guard let url = URL(string: manifestUrlString) else {
279
+ guard let manifestUrl = URL(string: manifestUrlString) else {
280
+ let error = NSError(domain: "HotUpdater", code: 0,
281
+ userInfo: [NSLocalizedDescriptionKey: "Invalid 'manifestUrl' provided: \(manifestUrlString)"])
282
+ reject("INVALID_FILE_URL", error.localizedDescription, error)
283
+ return
284
+ }
285
+ let archiveUrl: URL?
286
+ if let archiveUrlString = data["archiveUrl"] as? String {
287
+ guard let parsedArchiveUrl = URL(string: archiveUrlString) else {
279
288
  let error = NSError(domain: "HotUpdater", code: 0,
280
- userInfo: [NSLocalizedDescriptionKey: "Invalid 'manifestUrl' provided: \(manifestUrlString)"])
289
+ userInfo: [NSLocalizedDescriptionKey: "Invalid 'archiveUrl' provided: \(archiveUrlString)"])
281
290
  reject("INVALID_FILE_URL", error.localizedDescription, error)
282
291
  return
283
292
  }
284
- manifestUrl = url
293
+ archiveUrl = parsedArchiveUrl
294
+ } else {
295
+ archiveUrl = nil
296
+ }
297
+ guard let assetsPayload = data["assets"] as? [String: [String: Any]] else {
298
+ let error = NSError(domain: "HotUpdater", code: 0,
299
+ userInfo: [NSLocalizedDescriptionKey: "Missing manifest assets"])
300
+ reject("INVALID_MANIFEST", error.localizedDescription, error)
301
+ return
285
302
  }
286
- let changedAssetsPayload = data["changedAssets"] as? [String: [String: Any]]
287
- let changedAssets = changedAssetsPayload?.reduce(into: [String: ChangedAssetDescriptor]()) { partialResult, entry in
303
+ let assets = assetsPayload.reduce(into: [String: ChangedAssetDescriptor]()) { partialResult, entry in
288
304
  guard let fileHash = entry.value["fileHash"] as? String,
289
305
  !fileHash.isEmpty
290
306
  else {
@@ -308,7 +324,8 @@ private func hotUpdaterGetMinBundleId() -> String {
308
324
  baseBundleId: baseBundleId,
309
325
  baseFileHash: baseFileHash,
310
326
  patchFileHash: patchFileHash,
311
- patchUrl: patchUrl
327
+ patchUrl: patchUrl,
328
+ byteSize: Self.parseSafeByteSize(payload["byteSize"])
312
329
  )
313
330
  }
314
331
  let filePayload = entry.value["file"] as? [String: Any]
@@ -339,10 +356,10 @@ private func hotUpdaterGetMinBundleId() -> String {
339
356
  // Extract progress callback if provided
340
357
  let progressCallback = data["progressCallback"] as? RCTResponseSenderBlock
341
358
 
342
- NSLog("[HotUpdaterImpl] updateBundle called with bundleId: \(bundleId), fileUrl: \(fileUrl?.absoluteString ?? "nil"), fileHash: \(fileHash ?? "nil")")
359
+ NSLog("[HotUpdaterImpl] updateBundle called with bundleId: \(bundleId), manifestUrl: \(manifestUrl.absoluteString)")
343
360
 
344
361
  // Heavy work is delegated to bundle storage service with safe error handling
345
- bundleStorage.updateBundle(bundleId: bundleId, fileUrl: fileUrl, fileHash: fileHash, manifestUrl: manifestUrl, manifestFileHash: manifestFileHash, changedAssets: changedAssets, progressHandler: { payload in
362
+ bundleStorage.updateBundle(bundleId: bundleId, manifestUrl: manifestUrl, manifestFileHash: manifestFileHash, archiveUrl: archiveUrl, assets: assets, progressHandler: { payload in
346
363
  DispatchQueue.main.async {
347
364
  NotificationCenter.default.post(
348
365
  name: .updateProgressDidChange,
@@ -544,7 +561,6 @@ private func hotUpdaterGetMinBundleId() -> String {
544
561
  "DIRECTORY_CREATION_FAILED",
545
562
  "DOWNLOAD_FAILED",
546
563
  "INCOMPLETE_DOWNLOAD",
547
- "EXTRACTION_FORMAT_ERROR",
548
564
  "INVALID_BUNDLE",
549
565
  "INSUFFICIENT_DISK_SPACE",
550
566
  "SIGNATURE_VERIFICATION_FAILED",
@@ -614,7 +630,7 @@ private func hotUpdaterGetMinBundleId() -> String {
614
630
 
615
631
  /**
616
632
  * Gets the current active bundle ID from bundle storage.
617
- * Reads manifest.json first and falls back to the legacy BUNDLE_ID file.
633
+ * Reads the required OTA manifest.json.
618
634
  * Built-in bundle fallback is handled in JS.
619
635
  */
620
636
  public func getBundleId() -> String? {
@@ -1,7 +1,5 @@
1
1
  import Foundation
2
2
 
3
3
  extension Notification.Name {
4
- static let downloadProgressUpdate = Notification.Name("HotUpdaterDownloadProgressUpdate")
5
- static let downloadDidFinish = Notification.Name("HotUpdaterDownloadDidFinish")
6
4
  static let updateProgressDidChange = Notification.Name("HotUpdaterUpdateProgressDidChange")
7
5
  }