@hot-updater/react-native 0.36.6 → 1.0.0-rc.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 (239) hide show
  1. package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +357 -6
  2. package/android/src/main/java/com/hotupdater/BundleMetadata.kt +268 -34
  3. package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +75 -9
  4. package/android/src/main/java/com/hotupdater/NativeConfigUtils.kt +2 -16
  5. package/android/src/main/java/com/hotupdater/ReleaseCatalogCacheService.kt +166 -0
  6. package/android/src/main/java/com/hotupdater/SignatureVerifier.kt +1 -2
  7. package/android/src/newarch/HotUpdater.kt +1 -1
  8. package/android/src/newarch/HotUpdaterModule.kt +116 -0
  9. package/android/src/oldarch/HotUpdater.kt +1 -1
  10. package/android/src/oldarch/HotUpdaterModule.kt +128 -0
  11. package/android/src/oldarch/HotUpdaterSpec.kt +38 -0
  12. package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +280 -2
  13. package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +57 -2
  14. package/android/src/test/java/com/hotupdater/ReleaseCatalogCacheServiceTest.kt +118 -0
  15. package/ios/HotUpdater/Internal/BundleFileStorageService.swift +491 -30
  16. package/ios/HotUpdater/Internal/BundleMetadata.swift +229 -6
  17. package/ios/HotUpdater/Internal/HotUpdater.mm +122 -0
  18. package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +176 -7
  19. package/ios/HotUpdater/Internal/ReleaseCatalogCacheService.swift +213 -0
  20. package/lib/commonjs/catalogCacheNative.js +44 -0
  21. package/lib/commonjs/catalogCacheNative.js.map +1 -0
  22. package/lib/commonjs/catalogCacheNative.spec.js +31 -0
  23. package/lib/commonjs/catalogCacheNative.spec.js.map +1 -0
  24. package/lib/commonjs/checkForUpdate.js +231 -60
  25. package/lib/commonjs/checkForUpdate.js.map +1 -1
  26. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +604 -0
  27. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +1 -0
  28. package/lib/commonjs/error.js +8 -1
  29. package/lib/commonjs/error.js.map +1 -1
  30. package/lib/commonjs/{fetchUpdateInfo.js → fetchJSON.js} +11 -14
  31. package/lib/commonjs/fetchJSON.js.map +1 -0
  32. package/lib/commonjs/httpClient.js +153 -0
  33. package/lib/commonjs/httpClient.js.map +1 -0
  34. package/lib/commonjs/httpClient.spec.js +102 -0
  35. package/lib/commonjs/httpClient.spec.js.map +1 -0
  36. package/lib/commonjs/index.js +48 -71
  37. package/lib/commonjs/index.js.map +1 -1
  38. package/lib/commonjs/index.spec.js +117 -161
  39. package/lib/commonjs/index.spec.js.map +1 -1
  40. package/lib/commonjs/iosInterop.spec.js +18 -0
  41. package/lib/commonjs/iosInterop.spec.js.map +1 -0
  42. package/lib/commonjs/native.js +222 -55
  43. package/lib/commonjs/native.js.map +1 -1
  44. package/lib/commonjs/native.spec.js +226 -12
  45. package/lib/commonjs/native.spec.js.map +1 -1
  46. package/lib/commonjs/notifyAppReadyAnalytics.boundary.spec.js +218 -0
  47. package/lib/commonjs/notifyAppReadyAnalytics.boundary.spec.js.map +1 -0
  48. package/lib/commonjs/notifyAppReadyAnalytics.js +133 -0
  49. package/lib/commonjs/notifyAppReadyAnalytics.js.map +1 -0
  50. package/lib/commonjs/notifyAppReadyAnalytics.spec.js +246 -0
  51. package/lib/commonjs/notifyAppReadyAnalytics.spec.js.map +1 -0
  52. package/lib/commonjs/notifyAppReadyAnalytics.test-utils.js +24 -0
  53. package/lib/commonjs/notifyAppReadyAnalytics.test-utils.js.map +1 -0
  54. package/lib/commonjs/notifyAppReadyAnalytics.transport.spec.js +117 -0
  55. package/lib/commonjs/notifyAppReadyAnalytics.transport.spec.js.map +1 -0
  56. package/lib/commonjs/releaseCatalogCache.js +179 -0
  57. package/lib/commonjs/releaseCatalogCache.js.map +1 -0
  58. package/lib/commonjs/releaseCatalogCache.spec.js +195 -0
  59. package/lib/commonjs/releaseCatalogCache.spec.js.map +1 -0
  60. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js +64 -0
  61. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js.map +1 -0
  62. package/lib/commonjs/sdkVersion.js +1 -1
  63. package/lib/commonjs/sdkVersion.js.map +1 -1
  64. package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
  65. package/lib/commonjs/types.js +0 -12
  66. package/lib/commonjs/types.js.map +1 -1
  67. package/lib/commonjs/wrap.js +7 -82
  68. package/lib/commonjs/wrap.js.map +1 -1
  69. package/lib/commonjs/wrap.spec.js +203 -56
  70. package/lib/commonjs/wrap.spec.js.map +1 -1
  71. package/lib/commonjs/wrap.types.js +6 -0
  72. package/lib/commonjs/wrap.types.js.map +1 -0
  73. package/lib/module/catalogCacheNative.js +36 -0
  74. package/lib/module/catalogCacheNative.js.map +1 -0
  75. package/lib/module/catalogCacheNative.spec.js +31 -0
  76. package/lib/module/catalogCacheNative.spec.js.map +1 -0
  77. package/lib/module/checkForUpdate.js +233 -62
  78. package/lib/module/checkForUpdate.js.map +1 -1
  79. package/lib/module/checkForUpdate.releaseCatalog.spec.js +603 -0
  80. package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +1 -0
  81. package/lib/module/error.js +6 -0
  82. package/lib/module/error.js.map +1 -1
  83. package/lib/module/{fetchUpdateInfo.js → fetchJSON.js} +9 -12
  84. package/lib/module/fetchJSON.js.map +1 -0
  85. package/lib/module/httpClient.js +148 -0
  86. package/lib/module/httpClient.js.map +1 -0
  87. package/lib/module/httpClient.spec.js +102 -0
  88. package/lib/module/httpClient.spec.js.map +1 -0
  89. package/lib/module/index.js +49 -66
  90. package/lib/module/index.js.map +1 -1
  91. package/lib/module/index.spec.js +117 -161
  92. package/lib/module/index.spec.js.map +1 -1
  93. package/lib/module/iosInterop.spec.js +18 -0
  94. package/lib/module/iosInterop.spec.js.map +1 -0
  95. package/lib/module/native.js +206 -51
  96. package/lib/module/native.js.map +1 -1
  97. package/lib/module/native.spec.js +226 -12
  98. package/lib/module/native.spec.js.map +1 -1
  99. package/lib/module/notifyAppReadyAnalytics.boundary.spec.js +217 -0
  100. package/lib/module/notifyAppReadyAnalytics.boundary.spec.js.map +1 -0
  101. package/lib/module/notifyAppReadyAnalytics.js +128 -0
  102. package/lib/module/notifyAppReadyAnalytics.js.map +1 -0
  103. package/lib/module/notifyAppReadyAnalytics.spec.js +245 -0
  104. package/lib/module/notifyAppReadyAnalytics.spec.js.map +1 -0
  105. package/lib/module/notifyAppReadyAnalytics.test-utils.js +18 -0
  106. package/lib/module/notifyAppReadyAnalytics.test-utils.js.map +1 -0
  107. package/lib/module/notifyAppReadyAnalytics.transport.spec.js +116 -0
  108. package/lib/module/notifyAppReadyAnalytics.transport.spec.js.map +1 -0
  109. package/lib/module/releaseCatalogCache.js +171 -0
  110. package/lib/module/releaseCatalogCache.js.map +1 -0
  111. package/lib/module/releaseCatalogCache.spec.js +195 -0
  112. package/lib/module/releaseCatalogCache.spec.js.map +1 -0
  113. package/lib/module/releaseCatalogCacheNativeContract.spec.js +64 -0
  114. package/lib/module/releaseCatalogCacheNativeContract.spec.js.map +1 -0
  115. package/lib/module/sdkVersion.js +1 -1
  116. package/lib/module/sdkVersion.js.map +1 -1
  117. package/lib/module/specs/NativeHotUpdater.js.map +1 -1
  118. package/lib/module/types.js +0 -12
  119. package/lib/module/types.js.map +1 -1
  120. package/lib/module/wrap.js +6 -81
  121. package/lib/module/wrap.js.map +1 -1
  122. package/lib/module/wrap.spec.js +203 -56
  123. package/lib/module/wrap.spec.js.map +1 -1
  124. package/lib/module/wrap.types.js +4 -0
  125. package/lib/module/wrap.types.js.map +1 -0
  126. package/lib/typescript/commonjs/catalogCacheNative.d.ts +4 -0
  127. package/lib/typescript/commonjs/catalogCacheNative.d.ts.map +1 -0
  128. package/lib/typescript/commonjs/checkForUpdate.d.ts +14 -4
  129. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  130. package/lib/typescript/commonjs/error.d.ts +3 -0
  131. package/lib/typescript/commonjs/error.d.ts.map +1 -1
  132. package/lib/typescript/commonjs/fetchJSON.d.ts +6 -0
  133. package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -0
  134. package/lib/typescript/commonjs/httpClient.d.ts +56 -0
  135. package/lib/typescript/commonjs/httpClient.d.ts.map +1 -0
  136. package/lib/typescript/commonjs/index.d.ts +32 -41
  137. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  138. package/lib/typescript/commonjs/native.d.ts +83 -18
  139. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  140. package/lib/typescript/commonjs/notifyAppReadyAnalytics.d.ts +13 -0
  141. package/lib/typescript/commonjs/notifyAppReadyAnalytics.d.ts.map +1 -0
  142. package/lib/typescript/commonjs/notifyAppReadyAnalytics.test-utils.d.ts +8 -0
  143. package/lib/typescript/commonjs/notifyAppReadyAnalytics.test-utils.d.ts.map +1 -0
  144. package/lib/typescript/commonjs/releaseCatalogCache.d.ts +25 -0
  145. package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -0
  146. package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
  147. package/lib/typescript/commonjs/sdkVersion.d.ts.map +1 -1
  148. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +40 -5
  149. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
  150. package/lib/typescript/commonjs/types.d.ts +0 -118
  151. package/lib/typescript/commonjs/types.d.ts.map +1 -1
  152. package/lib/typescript/commonjs/wrap.d.ts +2 -191
  153. package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
  154. package/lib/typescript/commonjs/wrap.types.d.ts +65 -0
  155. package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -0
  156. package/lib/typescript/module/catalogCacheNative.d.ts +4 -0
  157. package/lib/typescript/module/catalogCacheNative.d.ts.map +1 -0
  158. package/lib/typescript/module/checkForUpdate.d.ts +14 -4
  159. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  160. package/lib/typescript/module/error.d.ts +3 -0
  161. package/lib/typescript/module/error.d.ts.map +1 -1
  162. package/lib/typescript/module/fetchJSON.d.ts +6 -0
  163. package/lib/typescript/module/fetchJSON.d.ts.map +1 -0
  164. package/lib/typescript/module/httpClient.d.ts +56 -0
  165. package/lib/typescript/module/httpClient.d.ts.map +1 -0
  166. package/lib/typescript/module/index.d.ts +32 -41
  167. package/lib/typescript/module/index.d.ts.map +1 -1
  168. package/lib/typescript/module/native.d.ts +83 -18
  169. package/lib/typescript/module/native.d.ts.map +1 -1
  170. package/lib/typescript/module/notifyAppReadyAnalytics.d.ts +13 -0
  171. package/lib/typescript/module/notifyAppReadyAnalytics.d.ts.map +1 -0
  172. package/lib/typescript/module/notifyAppReadyAnalytics.test-utils.d.ts +8 -0
  173. package/lib/typescript/module/notifyAppReadyAnalytics.test-utils.d.ts.map +1 -0
  174. package/lib/typescript/module/releaseCatalogCache.d.ts +25 -0
  175. package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -0
  176. package/lib/typescript/module/sdkVersion.d.ts +1 -1
  177. package/lib/typescript/module/sdkVersion.d.ts.map +1 -1
  178. package/lib/typescript/module/specs/NativeHotUpdater.d.ts +40 -5
  179. package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
  180. package/lib/typescript/module/types.d.ts +0 -118
  181. package/lib/typescript/module/types.d.ts.map +1 -1
  182. package/lib/typescript/module/wrap.d.ts +2 -191
  183. package/lib/typescript/module/wrap.d.ts.map +1 -1
  184. package/lib/typescript/module/wrap.types.d.ts +65 -0
  185. package/lib/typescript/module/wrap.types.d.ts.map +1 -0
  186. package/package.json +6 -24
  187. package/src/catalogCacheNative.spec.ts +63 -0
  188. package/src/catalogCacheNative.ts +74 -0
  189. package/src/checkForUpdate.releaseCatalog.spec.ts +596 -0
  190. package/src/checkForUpdate.ts +368 -97
  191. package/src/error.ts +7 -0
  192. package/src/{fetchUpdateInfo.ts → fetchJSON.ts} +8 -15
  193. package/src/httpClient.spec.ts +132 -0
  194. package/src/httpClient.ts +261 -0
  195. package/src/index.spec.ts +110 -203
  196. package/src/index.ts +70 -93
  197. package/src/iosInterop.spec.ts +30 -0
  198. package/src/native.spec.ts +226 -10
  199. package/src/native.ts +385 -83
  200. package/src/notifyAppReadyAnalytics.boundary.spec.ts +234 -0
  201. package/src/notifyAppReadyAnalytics.spec.ts +243 -0
  202. package/src/notifyAppReadyAnalytics.test-utils.ts +31 -0
  203. package/src/notifyAppReadyAnalytics.transport.spec.ts +130 -0
  204. package/src/notifyAppReadyAnalytics.ts +194 -0
  205. package/src/releaseCatalogCache.spec.ts +282 -0
  206. package/src/releaseCatalogCache.ts +341 -0
  207. package/src/releaseCatalogCacheNativeContract.spec.ts +80 -0
  208. package/src/sdkVersion.ts +1 -1
  209. package/src/specs/NativeHotUpdater.ts +51 -5
  210. package/src/types.ts +0 -140
  211. package/src/wrap.spec.ts +251 -56
  212. package/src/wrap.tsx +23 -318
  213. package/src/wrap.types.ts +78 -0
  214. package/android/src/main/java/com/hotupdater/StringResourceUtils.kt +0 -45
  215. package/app.plugin.js +0 -1
  216. package/ios/HotUpdater/Internal/HotUpdaterCrashHandler.h +0 -7
  217. package/ios/HotUpdater/Internal/HotUpdaterCrashHandler.mm +0 -4
  218. package/lib/commonjs/DefaultResolver.js +0 -50
  219. package/lib/commonjs/DefaultResolver.js.map +0 -1
  220. package/lib/commonjs/DefaultResolver.spec.js +0 -144
  221. package/lib/commonjs/DefaultResolver.spec.js.map +0 -1
  222. package/lib/commonjs/fetchUpdateInfo.js.map +0 -1
  223. package/lib/module/DefaultResolver.js +0 -46
  224. package/lib/module/DefaultResolver.js.map +0 -1
  225. package/lib/module/DefaultResolver.spec.js +0 -144
  226. package/lib/module/DefaultResolver.spec.js.map +0 -1
  227. package/lib/module/fetchUpdateInfo.js.map +0 -1
  228. package/lib/typescript/commonjs/DefaultResolver.d.ts +0 -11
  229. package/lib/typescript/commonjs/DefaultResolver.d.ts.map +0 -1
  230. package/lib/typescript/commonjs/fetchUpdateInfo.d.ts +0 -7
  231. package/lib/typescript/commonjs/fetchUpdateInfo.d.ts.map +0 -1
  232. package/lib/typescript/module/DefaultResolver.d.ts +0 -11
  233. package/lib/typescript/module/DefaultResolver.d.ts.map +0 -1
  234. package/lib/typescript/module/fetchUpdateInfo.d.ts +0 -7
  235. package/lib/typescript/module/fetchUpdateInfo.d.ts.map +0 -1
  236. package/plugin/build/transformers.js +0 -314
  237. package/plugin/build/withHotUpdater.js +0 -228
  238. package/src/DefaultResolver.spec.ts +0 -193
  239. package/src/DefaultResolver.ts +0 -63
@@ -17,6 +17,9 @@ private func hotUpdaterUpdateSignalLaunchStateSymbol(
17
17
  @_silgen_name("HotUpdaterPerformRecoveryReload")
18
18
  private func hotUpdaterPerformRecoveryReloadSymbol() -> ObjCBool
19
19
 
20
+ @_silgen_name("HotUpdaterCopyMinBundleId")
21
+ private func hotUpdaterCopyMinBundleIdSymbol() -> UnsafeMutableRawPointer
22
+
20
23
  private func hotUpdaterInstallSignalHandlers(_ crashMarkerPath: String) {
21
24
  hotUpdaterInstallSignalHandlersSymbol(crashMarkerPath as NSString)
22
25
  }
@@ -29,11 +32,18 @@ private func hotUpdaterPerformRecoveryReload() -> Bool {
29
32
  return hotUpdaterPerformRecoveryReloadSymbol().boolValue
30
33
  }
31
34
 
35
+ private func hotUpdaterGetMinBundleId() -> String {
36
+ Unmanaged<NSString>
37
+ .fromOpaque(hotUpdaterCopyMinBundleIdSymbol())
38
+ .takeRetainedValue() as String
39
+ }
40
+
32
41
  @objcMembers public class HotUpdaterImpl: NSObject {
33
42
  private let bundleStorage: BundleStorageService
34
43
  private let preferences: PreferencesService
35
44
  private let cohortService: CohortService
36
45
  private let recoveryManager: HotUpdaterRecoveryManager
46
+ private let releaseCatalogCache: ReleaseCatalogCacheService
37
47
  private var currentLaunchSelection: LaunchSelection?
38
48
 
39
49
  private static let DEFAULT_CHANNEL = "production"
@@ -56,11 +66,17 @@ private func hotUpdaterPerformRecoveryReload() -> Bool {
56
66
  downloadService: downloadService,
57
67
  decompressService: decompressService,
58
68
  preferences: preferences,
59
- isolationKey: isolationKey
69
+ isolationKey: isolationKey,
70
+ builtInBundleIdProvider: { HotUpdaterImpl.minBundleId() }
60
71
  )
61
72
  let recoveryManager = HotUpdaterRecoveryManager.shared
62
73
 
63
- self.init(bundleStorage: bundleStorage, preferences: preferences, recoveryManager: recoveryManager)
74
+ self.init(
75
+ bundleStorage: bundleStorage,
76
+ preferences: preferences,
77
+ recoveryManager: recoveryManager,
78
+ releaseCatalogCache: ReleaseCatalogCacheService()
79
+ )
64
80
  }
65
81
 
66
82
  /**
@@ -68,11 +84,17 @@ private func hotUpdaterPerformRecoveryReload() -> Bool {
68
84
  * @param bundleStorage Service for bundle storage operations
69
85
  * @param preferences Service for preference storage
70
86
  */
71
- internal init(bundleStorage: BundleStorageService, preferences: PreferencesService, recoveryManager: HotUpdaterRecoveryManager) {
87
+ internal init(
88
+ bundleStorage: BundleStorageService,
89
+ preferences: PreferencesService,
90
+ recoveryManager: HotUpdaterRecoveryManager,
91
+ releaseCatalogCache: ReleaseCatalogCacheService = ReleaseCatalogCacheService()
92
+ ) {
72
93
  self.bundleStorage = bundleStorage
73
94
  self.preferences = preferences
74
95
  self.cohortService = CohortService()
75
96
  self.recoveryManager = recoveryManager
97
+ self.releaseCatalogCache = releaseCatalogCache
76
98
  super.init()
77
99
 
78
100
  // Configure preferences with isolation key
@@ -98,6 +120,10 @@ private func hotUpdaterPerformRecoveryReload() -> Bool {
98
120
  }
99
121
  return Bundle.main.object(forInfoDictionaryKey: "HOT_UPDATER_CHANNEL") as? String ?? Self.DEFAULT_CHANNEL
100
122
  }
123
+
124
+ public static func minBundleId() -> String {
125
+ hotUpdaterGetMinBundleId()
126
+ }
101
127
 
102
128
  /**
103
129
  * Gets the complete isolation key for preferences storage.
@@ -136,6 +162,11 @@ private func hotUpdaterPerformRecoveryReload() -> Bool {
136
162
  * @return The channel name or nil if not set
137
163
  */
138
164
  public func getChannel() -> String {
165
+ if let activeSelection = bundleStorage.getActiveUpdateState()["activeSelection"] as? [String: Any],
166
+ let channel = activeSelection["channel"] as? String,
167
+ !channel.isEmpty {
168
+ return channel
169
+ }
139
170
  if let override = HotUpdaterConfig.shared.channel, !override.isEmpty {
140
171
  return override
141
172
  }
@@ -282,6 +313,16 @@ private func hotUpdaterPerformRecoveryReload() -> Bool {
282
313
  patch: patch
283
314
  )
284
315
  }
316
+ let selection = (data["selection"] as? [String: Any]).flatMap(Self.parseSelection)
317
+ if let selection, !bundleStorage.stageReleaseSelection(selection) {
318
+ let error = NSError(
319
+ domain: "HotUpdater",
320
+ code: 0,
321
+ userInfo: [NSLocalizedDescriptionKey: "Release catalog selection is stale"]
322
+ )
323
+ reject("UNKNOWN_ERROR", error.localizedDescription, error)
324
+ return
325
+ }
285
326
 
286
327
  // Extract progress callback if provided
287
328
  let progressCallback = data["progressCallback"] as? RCTResponseSenderBlock
@@ -345,6 +386,122 @@ private func hotUpdaterPerformRecoveryReload() -> Bool {
345
386
  }
346
387
  }
347
388
 
389
+ private static func parseSelection(_ data: [String: Any]) -> PersistedSelection? {
390
+ guard let kind = data["kind"] as? String,
391
+ let bundleId = data["bundleId"] as? String,
392
+ let channel = data["channel"] as? String else {
393
+ return nil
394
+ }
395
+ return PersistedSelection(
396
+ kind: kind,
397
+ releaseId: data["releaseId"] as? String,
398
+ bundleId: bundleId,
399
+ catalogId: data["catalogId"] as? String,
400
+ scopeKey: data["scopeKey"] as? String,
401
+ generation: (data["generation"] as? NSNumber)?.int64Value,
402
+ catalogHash: data["catalogHash"] as? String,
403
+ channel: channel,
404
+ selectionContextHash: data["selectionContextHash"] as? String
405
+ )
406
+ }
407
+
408
+ public func acceptReleaseCatalog(_ params: NSDictionary?) -> Bool {
409
+ guard let data = params as? [String: Any],
410
+ let catalogId = data["catalogId"] as? String,
411
+ let scopeKey = data["scopeKey"] as? String,
412
+ let generation = data["generation"] as? NSNumber,
413
+ let catalogHash = data["catalogHash"] as? String,
414
+ let channel = data["channel"] as? String,
415
+ let selectionContextHash = data["selectionContextHash"] as? String else {
416
+ return false
417
+ }
418
+ return bundleStorage.acceptReleaseCatalog(
419
+ catalogId: catalogId,
420
+ scopeKey: scopeKey,
421
+ generation: generation.int64Value,
422
+ catalogHash: catalogHash,
423
+ channel: channel,
424
+ selectionContextHash: selectionContextHash
425
+ )
426
+ }
427
+
428
+ public func getActiveUpdateState() -> [String: Any] {
429
+ bundleStorage.getActiveUpdateState()
430
+ }
431
+
432
+ public func getReleaseCatalogCache(
433
+ _ partition: String,
434
+ resolver resolve: @escaping RCTPromiseResolveBlock,
435
+ rejecter _: @escaping RCTPromiseRejectBlock
436
+ ) {
437
+ DispatchQueue.global(qos: .utility).async {
438
+ guard let value = self.releaseCatalogCache.get(partition: partition) else {
439
+ resolve(NSNull())
440
+ return
441
+ }
442
+ resolve(value)
443
+ }
444
+ }
445
+
446
+ public func setReleaseCatalogCache(
447
+ _ partition: String,
448
+ payload: String,
449
+ resolver resolve: @escaping RCTPromiseResolveBlock,
450
+ rejecter _: @escaping RCTPromiseRejectBlock
451
+ ) {
452
+ DispatchQueue.global(qos: .utility).async {
453
+ resolve(self.releaseCatalogCache.set(partition: partition, value: payload))
454
+ }
455
+ }
456
+
457
+ public func removeReleaseCatalogCache(
458
+ _ partition: String,
459
+ resolver resolve: @escaping RCTPromiseResolveBlock,
460
+ rejecter _: @escaping RCTPromiseRejectBlock
461
+ ) {
462
+ DispatchQueue.global(qos: .utility).async {
463
+ resolve(self.releaseCatalogCache.remove(partition: partition))
464
+ }
465
+ }
466
+
467
+ public func isReleaseSelectionCurrent(_ params: NSDictionary?) -> Bool {
468
+ guard let data = params as? [String: Any],
469
+ let catalogId = data["catalogId"] as? String,
470
+ let scopeKey = data["scopeKey"] as? String,
471
+ let generation = data["generation"] as? NSNumber,
472
+ let catalogHash = data["catalogHash"] as? String,
473
+ let channel = data["channel"] as? String,
474
+ let selectionContextHash = data["selectionContextHash"] as? String else {
475
+ return false
476
+ }
477
+ return bundleStorage.isReleaseSelectionCurrent(
478
+ catalogId: catalogId,
479
+ scopeKey: scopeKey,
480
+ generation: generation.int64Value,
481
+ catalogHash: catalogHash,
482
+ channel: channel,
483
+ selectionContextHash: selectionContextHash
484
+ )
485
+ }
486
+
487
+ public func commitReleaseSelection(
488
+ _ params: NSDictionary?,
489
+ resolver resolve: @escaping RCTPromiseResolveBlock,
490
+ rejecter reject: @escaping RCTPromiseRejectBlock
491
+ ) {
492
+ guard let data = params?["selection"] as? [String: Any],
493
+ let selection = Self.parseSelection(data) else {
494
+ let error = NSError(
495
+ domain: "HotUpdater",
496
+ code: 0,
497
+ userInfo: [NSLocalizedDescriptionKey: "Invalid Release selection"]
498
+ )
499
+ reject("INVALID_SELECTION", error.localizedDescription, error)
500
+ return
501
+ }
502
+ resolve(bundleStorage.commitReleaseSelection(selection))
503
+ }
504
+
348
505
  /**
349
506
  * Normalizes native errors to a small, predictable set of JS-facing error codes.
350
507
  * Rare or platform-specific codes are collapsed to UNKNOWN_ERROR to reduce surface area.
@@ -387,10 +544,6 @@ private func hotUpdaterPerformRecoveryReload() -> Bool {
387
544
 
388
545
  // MARK: - Rollback Support
389
546
 
390
- /**
391
- * Returns the native launch report for the current process.
392
- * This is read-only; startup success and rollback are finalized before JS reads it.
393
- */
394
547
  public func notifyAppReady() -> [String: Any] {
395
548
  return bundleStorage.notifyAppReady()
396
549
  }
@@ -411,6 +564,22 @@ private func hotUpdaterPerformRecoveryReload() -> Bool {
411
564
  return bundleStorage.clearCrashHistory()
412
565
  }
413
566
 
567
+ public func getInstallId() -> String {
568
+ return bundleStorage.getInstallId()
569
+ }
570
+
571
+ public func getUserId() -> String? {
572
+ return bundleStorage.getUserId()
573
+ }
574
+
575
+ public func getUsername() -> String? {
576
+ return bundleStorage.getUsername()
577
+ }
578
+
579
+ public func setUser(_ userId: String?, username: String?) {
580
+ bundleStorage.setUser(userId: userId, username: username)
581
+ }
582
+
414
583
  /**
415
584
  * Gets the base URL for the current active bundle directory.
416
585
  * Returns the file:// URL to the bundle directory with trailing slash.
@@ -0,0 +1,213 @@
1
+ import CryptoKit
2
+ import Foundation
3
+
4
+ final class ReleaseCatalogCacheService {
5
+ private static let maxCompiledCatalogBytes = 256 * 1024
6
+ private static let maxWireEnvelopeBytes = 4 * 1024
7
+ private static let maxETagBytes = 1024
8
+ private static let cacheFormatBytes = 3
9
+ static let defaultMaxEntries = 8
10
+ static let defaultMaxEntryBytes =
11
+ maxCompiledCatalogBytes + maxWireEnvelopeBytes + maxETagBytes + cacheFormatBytes
12
+ static let defaultMaxTotalBytes = defaultMaxEntries * (defaultMaxEntryBytes + 65)
13
+
14
+ private let fileManager: FileManager
15
+ private let rootDirectory: URL
16
+ private let maxEntries: Int
17
+ private let maxEntryBytes: Int
18
+ private let maxTotalBytes: Int
19
+ private let atomicWriter: (Data, URL) throws -> Void
20
+ private let lock = NSLock()
21
+
22
+ init(
23
+ rootDirectory: URL = ReleaseCatalogCacheService.defaultRootDirectory(),
24
+ fileManager: FileManager = .default,
25
+ maxEntries: Int = defaultMaxEntries,
26
+ maxEntryBytes: Int = defaultMaxEntryBytes,
27
+ maxTotalBytes: Int = defaultMaxTotalBytes,
28
+ atomicWriter: @escaping (Data, URL) throws -> Void = ReleaseCatalogCacheService.writeAtomically
29
+ ) {
30
+ self.rootDirectory = rootDirectory
31
+ self.fileManager = fileManager
32
+ self.maxEntries = maxEntries
33
+ self.maxEntryBytes = maxEntryBytes
34
+ self.maxTotalBytes = maxTotalBytes
35
+ self.atomicWriter = atomicWriter
36
+ recoverInterruptedWrites()
37
+ trim()
38
+ }
39
+
40
+ func get(partition: String) -> String? {
41
+ lock.lock()
42
+ defer { lock.unlock() }
43
+
44
+ let file = entryURL(partition: partition)
45
+ guard let attributes = try? fileManager.attributesOfItem(atPath: file.path),
46
+ let size = attributes[.size] as? NSNumber,
47
+ size.intValue <= maxEntryBytes + 65,
48
+ let stored = try? Data(contentsOf: file),
49
+ let separator = stored.firstIndex(of: 0x0a),
50
+ separator == 64 else {
51
+ try? fileManager.removeItem(at: file)
52
+ return nil
53
+ }
54
+
55
+ let expectedDigest = String(data: stored[..<separator], encoding: .ascii)
56
+ let payload = stored[stored.index(after: separator)...]
57
+ guard payload.count <= maxEntryBytes,
58
+ expectedDigest == sha256(Data(payload)),
59
+ let value = String(data: payload, encoding: .utf8) else {
60
+ try? fileManager.removeItem(at: file)
61
+ return nil
62
+ }
63
+
64
+ try? fileManager.setAttributes(
65
+ [.modificationDate: Date()],
66
+ ofItemAtPath: file.path
67
+ )
68
+ return value
69
+ }
70
+
71
+ @discardableResult
72
+ func set(partition: String, value: String) -> Bool {
73
+ guard let payload = value.data(using: .utf8), payload.count <= maxEntryBytes else {
74
+ return false
75
+ }
76
+
77
+ lock.lock()
78
+ defer { lock.unlock() }
79
+
80
+ do {
81
+ try fileManager.createDirectory(
82
+ at: rootDirectory,
83
+ withIntermediateDirectories: true
84
+ )
85
+ var cacheDirectory = rootDirectory
86
+ var resourceValues = URLResourceValues()
87
+ resourceValues.isExcludedFromBackup = true
88
+ try? cacheDirectory.setResourceValues(resourceValues)
89
+ var stored = Data(sha256(payload).utf8)
90
+ stored.append(0x0a)
91
+ stored.append(payload)
92
+ let destination = entryURL(partition: partition)
93
+ try atomicWriter(stored, destination)
94
+ try? fileManager.setAttributes(
95
+ [.modificationDate: Date()],
96
+ ofItemAtPath: destination.path
97
+ )
98
+ trim()
99
+ return true
100
+ } catch {
101
+ return false
102
+ }
103
+ }
104
+
105
+ @discardableResult
106
+ func remove(partition: String) -> Bool {
107
+ lock.lock()
108
+ defer { lock.unlock() }
109
+
110
+ let file = entryURL(partition: partition)
111
+ guard fileManager.fileExists(atPath: file.path) else { return true }
112
+ do {
113
+ try fileManager.removeItem(at: file)
114
+ return true
115
+ } catch {
116
+ return false
117
+ }
118
+ }
119
+
120
+ private func entryURL(partition: String) -> URL {
121
+ rootDirectory
122
+ .appendingPathComponent(sha256(Data(partition.utf8)))
123
+ .appendingPathExtension("catalog")
124
+ }
125
+
126
+ private func recoverInterruptedWrites() {
127
+ guard let entries = try? fileManager.contentsOfDirectory(
128
+ at: rootDirectory,
129
+ includingPropertiesForKeys: nil,
130
+ options: []
131
+ ) else {
132
+ return
133
+ }
134
+ for entry in entries
135
+ where entry.lastPathComponent.hasPrefix(".catalog-") && entry.pathExtension == "tmp" {
136
+ try? fileManager.removeItem(at: entry)
137
+ }
138
+ }
139
+
140
+ private func trim() {
141
+ let keys: Set<URLResourceKey> = [
142
+ .contentModificationDateKey,
143
+ .fileSizeKey,
144
+ .isRegularFileKey,
145
+ ]
146
+ let entries = (try? fileManager.contentsOfDirectory(
147
+ at: rootDirectory,
148
+ includingPropertiesForKeys: Array(keys),
149
+ options: [.skipsHiddenFiles]
150
+ ))?
151
+ .filter { url in
152
+ guard url.pathExtension == "catalog",
153
+ let values = try? url.resourceValues(forKeys: keys) else {
154
+ return false
155
+ }
156
+ return values.isRegularFile == true
157
+ }
158
+ .sorted { first, second in
159
+ let firstDate = (try? first.resourceValues(forKeys: keys))?.contentModificationDate ?? .distantPast
160
+ let secondDate = (try? second.resourceValues(forKeys: keys))?.contentModificationDate ?? .distantPast
161
+ return firstDate > secondDate
162
+ } ?? []
163
+
164
+ var retainedBytes = 0
165
+ for (index, entry) in entries.enumerated() {
166
+ let size = (try? entry.resourceValues(forKeys: keys))?.fileSize ?? 0
167
+ retainedBytes += size
168
+ if index >= maxEntries || retainedBytes > maxTotalBytes {
169
+ try? fileManager.removeItem(at: entry)
170
+ }
171
+ }
172
+ }
173
+
174
+ private func sha256(_ value: Data) -> String {
175
+ SHA256.hash(data: value).map { String(format: "%02x", $0) }.joined()
176
+ }
177
+
178
+ private static func writeAtomically(_ data: Data, to destination: URL) throws {
179
+ let temporary = destination
180
+ .deletingLastPathComponent()
181
+ .appendingPathComponent(".catalog-\(UUID().uuidString).tmp")
182
+ do {
183
+ try data.write(to: temporary, options: .withoutOverwriting)
184
+ let handle = try FileHandle(forWritingTo: temporary)
185
+ try handle.synchronize()
186
+ try handle.close()
187
+
188
+ if FileManager.default.fileExists(atPath: destination.path) {
189
+ _ = try FileManager.default.replaceItemAt(
190
+ destination,
191
+ withItemAt: temporary,
192
+ backupItemName: nil,
193
+ options: []
194
+ )
195
+ } else {
196
+ try FileManager.default.moveItem(at: temporary, to: destination)
197
+ }
198
+ } catch {
199
+ try? FileManager.default.removeItem(at: temporary)
200
+ throw error
201
+ }
202
+ }
203
+
204
+ private static func defaultRootDirectory() -> URL {
205
+ let base = FileManager.default.urls(
206
+ for: .applicationSupportDirectory,
207
+ in: .userDomainMask
208
+ ).first ?? FileManager.default.temporaryDirectory
209
+ return base
210
+ .appendingPathComponent("HotUpdater", isDirectory: true)
211
+ .appendingPathComponent("ReleaseCatalogCache", isDirectory: true)
212
+ }
213
+ }
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.writeNativeReleaseCatalogCache = exports.removeNativeReleaseCatalogCache = exports.readNativeReleaseCatalogCache = void 0;
7
+ var _NativeHotUpdater = _interopRequireDefault(require("./specs/NativeHotUpdater.js"));
8
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9
+ const getModule = () => _NativeHotUpdater.default;
10
+ const requireMethod = (method, name) => {
11
+ if (typeof method !== "function") {
12
+ throw new Error(`[HotUpdater] Native module is missing '${name}()'. Rebuild the native app before using Release catalogs.`);
13
+ }
14
+ return method;
15
+ };
16
+ const readNativeReleaseCatalogCache = async partition => {
17
+ const method = requireMethod(getModule().getReleaseCatalogCache, "getReleaseCatalogCache");
18
+ try {
19
+ const value = await method.call(_NativeHotUpdater.default, partition);
20
+ return typeof value === "string" ? value : null;
21
+ } catch {
22
+ return null;
23
+ }
24
+ };
25
+ exports.readNativeReleaseCatalogCache = readNativeReleaseCatalogCache;
26
+ const writeNativeReleaseCatalogCache = async (partition, payload) => {
27
+ const method = requireMethod(getModule().setReleaseCatalogCache, "setReleaseCatalogCache");
28
+ try {
29
+ return await method.call(_NativeHotUpdater.default, partition, payload);
30
+ } catch {
31
+ return false;
32
+ }
33
+ };
34
+ exports.writeNativeReleaseCatalogCache = writeNativeReleaseCatalogCache;
35
+ const removeNativeReleaseCatalogCache = async partition => {
36
+ const method = requireMethod(getModule().removeReleaseCatalogCache, "removeReleaseCatalogCache");
37
+ try {
38
+ await method.call(_NativeHotUpdater.default, partition);
39
+ } catch {
40
+ // Cache maintenance must not prevent a network update check.
41
+ }
42
+ };
43
+ exports.removeNativeReleaseCatalogCache = removeNativeReleaseCatalogCache;
44
+ //# sourceMappingURL=catalogCacheNative.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_NativeHotUpdater","_interopRequireDefault","require","e","__esModule","default","getModule","HotUpdaterNative","requireMethod","method","name","Error","readNativeReleaseCatalogCache","partition","getReleaseCatalogCache","value","call","exports","writeNativeReleaseCatalogCache","payload","setReleaseCatalogCache","removeNativeReleaseCatalogCache","removeReleaseCatalogCache"],"sourceRoot":"../../src","sources":["catalogCacheNative.ts"],"mappings":";;;;;;AAAA,IAAAA,iBAAA,GAAAC,sBAAA,CAAAC,OAAA;AAAwD,SAAAD,uBAAAE,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAaxD,MAAMG,SAAS,GAAGA,CAAA,KAChBC,yBAAsE;AAExE,MAAMC,aAAa,GAAGA,CACpBC,MAAqB,EACrBC,IAAkC,KAC5B;EACN,IAAI,OAAOD,MAAM,KAAK,UAAU,EAAE;IAChC,MAAM,IAAIE,KAAK,CACb,0CAA0CD,IAAI,4DAChD,CAAC;EACH;EACA,OAAOD,MAAM;AACf,CAAC;AAEM,MAAMG,6BAA6B,GAAG,MAC3CC,SAAiB,IACU;EAC3B,MAAMJ,MAAM,GAAGD,aAAa,CAC1BF,SAAS,CAAC,CAAC,CAACQ,sBAAsB,EAClC,wBACF,CAAC;EAED,IAAI;IACF,MAAMC,KAAK,GAAG,MAAMN,MAAM,CAACO,IAAI,CAACT,yBAAgB,EAAEM,SAAS,CAAC;IAC5D,OAAO,OAAOE,KAAK,KAAK,QAAQ,GAAGA,KAAK,GAAG,IAAI;EACjD,CAAC,CAAC,MAAM;IACN,OAAO,IAAI;EACb;AACF,CAAC;AAACE,OAAA,CAAAL,6BAAA,GAAAA,6BAAA;AAEK,MAAMM,8BAA8B,GAAG,MAAAA,CAC5CL,SAAiB,EACjBM,OAAe,KACM;EACrB,MAAMV,MAAM,GAAGD,aAAa,CAC1BF,SAAS,CAAC,CAAC,CAACc,sBAAsB,EAClC,wBACF,CAAC;EAED,IAAI;IACF,OAAO,MAAMX,MAAM,CAACO,IAAI,CAACT,yBAAgB,EAAEM,SAAS,EAAEM,OAAO,CAAC;EAChE,CAAC,CAAC,MAAM;IACN,OAAO,KAAK;EACd;AACF,CAAC;AAACF,OAAA,CAAAC,8BAAA,GAAAA,8BAAA;AAEK,MAAMG,+BAA+B,GAAG,MAC7CR,SAAiB,IACC;EAClB,MAAMJ,MAAM,GAAGD,aAAa,CAC1BF,SAAS,CAAC,CAAC,CAACgB,yBAAyB,EACrC,2BACF,CAAC;EAED,IAAI;IACF,MAAMb,MAAM,CAACO,IAAI,CAACT,yBAAgB,EAAEM,SAAS,CAAC;EAChD,CAAC,CAAC,MAAM;IACN;EAAA;AAEJ,CAAC;AAACI,OAAA,CAAAI,+BAAA,GAAAA,+BAAA","ignoreList":[]}
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+
3
+ var _vitest = require("vitest");
4
+ var _catalogCacheNative = require("./catalogCacheNative.js");
5
+ const nativeModule = _vitest.vi.hoisted(() => ({}));
6
+ _vitest.vi.mock("./specs/NativeHotUpdater", () => ({
7
+ default: nativeModule
8
+ }));
9
+ (0, _vitest.describe)("Release Catalog native cache", () => {
10
+ (0, _vitest.beforeEach)(() => {
11
+ for (const key of Object.keys(nativeModule)) delete nativeModule[key];
12
+ });
13
+ (0, _vitest.it)("fails fast when the v1 native cache API is missing", async () => {
14
+ await (0, _vitest.expect)((0, _catalogCacheNative.readNativeReleaseCatalogCache)("partition")).rejects.toThrow("Native module is missing 'getReleaseCatalogCache()'. Rebuild the native app before using Release catalogs.");
15
+ await (0, _vitest.expect)((0, _catalogCacheNative.writeNativeReleaseCatalogCache)("partition", "payload")).rejects.toThrow("Native module is missing 'setReleaseCatalogCache()'. Rebuild the native app before using Release catalogs.");
16
+ await (0, _vitest.expect)((0, _catalogCacheNative.removeNativeReleaseCatalogCache)("partition")).rejects.toThrow("Native module is missing 'removeReleaseCatalogCache()'. Rebuild the native app before using Release catalogs.");
17
+ });
18
+ (0, _vitest.it)("normalizes an undefined iOS TurboModule cache miss to null", async () => {
19
+ nativeModule.getReleaseCatalogCache = _vitest.vi.fn().mockResolvedValue(undefined);
20
+ await (0, _vitest.expect)((0, _catalogCacheNative.readNativeReleaseCatalogCache)("partition")).resolves.toBeNull();
21
+ });
22
+ (0, _vitest.it)("treats native cache I/O failures as misses without hiding network work", async () => {
23
+ nativeModule.getReleaseCatalogCache = _vitest.vi.fn().mockRejectedValue(new Error("read"));
24
+ nativeModule.setReleaseCatalogCache = _vitest.vi.fn().mockRejectedValue(new Error("write"));
25
+ nativeModule.removeReleaseCatalogCache = _vitest.vi.fn().mockRejectedValue(new Error("remove"));
26
+ await (0, _vitest.expect)((0, _catalogCacheNative.readNativeReleaseCatalogCache)("partition")).resolves.toBeNull();
27
+ await (0, _vitest.expect)((0, _catalogCacheNative.writeNativeReleaseCatalogCache)("partition", "payload")).resolves.toBe(false);
28
+ await (0, _vitest.expect)((0, _catalogCacheNative.removeNativeReleaseCatalogCache)("partition")).resolves.toBeUndefined();
29
+ });
30
+ });
31
+ //# sourceMappingURL=catalogCacheNative.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_vitest","require","_catalogCacheNative","nativeModule","vi","hoisted","mock","default","describe","beforeEach","key","Object","keys","it","expect","readNativeReleaseCatalogCache","rejects","toThrow","writeNativeReleaseCatalogCache","removeNativeReleaseCatalogCache","getReleaseCatalogCache","fn","mockResolvedValue","undefined","resolves","toBeNull","mockRejectedValue","Error","setReleaseCatalogCache","removeReleaseCatalogCache","toBe","toBeUndefined"],"sourceRoot":"../../src","sources":["catalogCacheNative.spec.ts"],"mappings":";;AAAA,IAAAA,OAAA,GAAAC,OAAA;AAQA,IAAAC,mBAAA,GAAAD,OAAA;AANA,MAAME,YAAY,GAAGC,UAAE,CAACC,OAAO,CAC7B,OAAO,CAAC,CAAC,CACX,CAAC;AAEDD,UAAE,CAACE,IAAI,CAAC,0BAA0B,EAAE,OAAO;EAAEC,OAAO,EAAEJ;AAAa,CAAC,CAAC,CAAC;AAQtE,IAAAK,gBAAQ,EAAC,8BAA8B,EAAE,MAAM;EAC7C,IAAAC,kBAAU,EAAC,MAAM;IACf,KAAK,MAAMC,GAAG,IAAIC,MAAM,CAACC,IAAI,CAACT,YAAY,CAAC,EAAE,OAAOA,YAAY,CAACO,GAAG,CAAC;EACvE,CAAC,CAAC;EAEF,IAAAG,UAAE,EAAC,oDAAoD,EAAE,YAAY;IACnE,MAAM,IAAAC,cAAM,EAAC,IAAAC,iDAA6B,EAAC,WAAW,CAAC,CAAC,CAACC,OAAO,CAACC,OAAO,CACtE,4GACF,CAAC;IACD,MAAM,IAAAH,cAAM,EACV,IAAAI,kDAA8B,EAAC,WAAW,EAAE,SAAS,CACvD,CAAC,CAACF,OAAO,CAACC,OAAO,CACf,4GACF,CAAC;IACD,MAAM,IAAAH,cAAM,EAAC,IAAAK,mDAA+B,EAAC,WAAW,CAAC,CAAC,CAACH,OAAO,CAACC,OAAO,CACxE,+GACF,CAAC;EACH,CAAC,CAAC;EAEF,IAAAJ,UAAE,EAAC,4DAA4D,EAAE,YAAY;IAC3EV,YAAY,CAACiB,sBAAsB,GAAGhB,UAAE,CAACiB,EAAE,CAAC,CAAC,CAACC,iBAAiB,CAACC,SAAS,CAAC;IAE1E,MAAM,IAAAT,cAAM,EACV,IAAAC,iDAA6B,EAAC,WAAW,CAC3C,CAAC,CAACS,QAAQ,CAACC,QAAQ,CAAC,CAAC;EACvB,CAAC,CAAC;EAEF,IAAAZ,UAAE,EAAC,wEAAwE,EAAE,YAAY;IACvFV,YAAY,CAACiB,sBAAsB,GAAGhB,UAAE,CACrCiB,EAAE,CAAC,CAAC,CACJK,iBAAiB,CAAC,IAAIC,KAAK,CAAC,MAAM,CAAC,CAAC;IACvCxB,YAAY,CAACyB,sBAAsB,GAAGxB,UAAE,CACrCiB,EAAE,CAAC,CAAC,CACJK,iBAAiB,CAAC,IAAIC,KAAK,CAAC,OAAO,CAAC,CAAC;IACxCxB,YAAY,CAAC0B,yBAAyB,GAAGzB,UAAE,CACxCiB,EAAE,CAAC,CAAC,CACJK,iBAAiB,CAAC,IAAIC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAEzC,MAAM,IAAAb,cAAM,EACV,IAAAC,iDAA6B,EAAC,WAAW,CAC3C,CAAC,CAACS,QAAQ,CAACC,QAAQ,CAAC,CAAC;IACrB,MAAM,IAAAX,cAAM,EACV,IAAAI,kDAA8B,EAAC,WAAW,EAAE,SAAS,CACvD,CAAC,CAACM,QAAQ,CAACM,IAAI,CAAC,KAAK,CAAC;IACtB,MAAM,IAAAhB,cAAM,EACV,IAAAK,mDAA+B,EAAC,WAAW,CAC7C,CAAC,CAACK,QAAQ,CAACO,aAAa,CAAC,CAAC;EAC5B,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}