@hot-updater/react-native 1.0.0-rc.4 → 1.0.0-rc.41

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 (317) hide show
  1. package/android/build.gradle +13 -2
  2. package/android/proguard-rules.pro +8 -2
  3. package/android/src/bridge/BridgeReactInstance.kt +38 -0
  4. package/android/src/bridgeless/BridgeReactInstance.kt +24 -0
  5. package/android/src/main/java/com/hotupdater/ActivityStartGate.kt +94 -0
  6. package/android/src/main/java/com/hotupdater/BuiltInAssetResolver.kt +286 -0
  7. package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +1064 -844
  8. package/android/src/main/java/com/hotupdater/BundleMetadata.kt +50 -11
  9. package/android/src/main/java/com/hotupdater/FileManagerService.kt +45 -0
  10. package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +287 -22
  11. package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +46 -30
  12. package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryManager.kt +75 -192
  13. package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryState.kt +64 -0
  14. package/android/src/main/java/com/hotupdater/InstallIdentityService.kt +37 -0
  15. package/android/src/main/java/com/hotupdater/KeyValueStorageService.kt +86 -0
  16. package/android/src/main/java/com/hotupdater/NetworkState.kt +31 -0
  17. package/android/src/main/java/com/hotupdater/NoBackupStorage.kt +17 -0
  18. package/android/src/main/java/com/hotupdater/OkHttpDownloadService.kt +127 -50
  19. package/android/src/main/java/com/hotupdater/ReactNativeValueConverters.kt +34 -0
  20. package/android/src/main/java/com/hotupdater/TarArchiveInputStream.kt +214 -375
  21. package/android/src/main/java/com/hotupdater/TarBrArchiveExtractor.kt +119 -0
  22. package/android/src/newarch/HotUpdater.kt +7 -9
  23. package/android/src/newarch/HotUpdaterModule.kt +109 -84
  24. package/android/src/newarch/JavaScriptExceptionHooks.kt +48 -0
  25. package/android/src/newarch/ReactIntegrationManager.kt +6 -32
  26. package/android/src/oldarch/HotUpdater.kt +7 -9
  27. package/android/src/oldarch/HotUpdaterModule.kt +98 -74
  28. package/android/src/oldarch/HotUpdaterSpec.kt +4 -6
  29. package/android/src/oldarch/JavaScriptExceptionHooks.kt +71 -0
  30. package/ios/HotUpdater/Internal/BrotliFileDecompressor.swift +167 -0
  31. package/ios/HotUpdater/Internal/BuiltInAssetResolver.swift +196 -0
  32. package/ios/HotUpdater/Internal/BundleFileStorageService.swift +1032 -1116
  33. package/ios/HotUpdater/Internal/BundleMetadata.swift +61 -54
  34. package/ios/HotUpdater/Internal/FileManagerService.swift +48 -0
  35. package/ios/HotUpdater/Internal/FileUtilities.swift +64 -0
  36. package/ios/HotUpdater/Internal/HotUpdater-Bridging-Header.h +0 -1
  37. package/ios/HotUpdater/Internal/HotUpdater.mm +32 -67
  38. package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +229 -73
  39. package/ios/HotUpdater/Internal/KeyValueStorageService.swift +71 -0
  40. package/ios/HotUpdater/Internal/NotificationExtension.swift +0 -2
  41. package/ios/HotUpdater/Internal/TarArchiveExtractor.swift +182 -311
  42. package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +119 -71
  43. package/ios/HotUpdater/Internal/UpdateFailure.swift +390 -0
  44. package/ios/HotUpdater/Recovery/HotUpdaterRecovery.h +16 -0
  45. package/ios/HotUpdater/Recovery/HotUpdaterRecovery.mm +83 -0
  46. package/ios/HotUpdater/Recovery/HotUpdaterRecoveryHooks.mm +49 -0
  47. package/lib/commonjs/appReady.js +96 -0
  48. package/lib/commonjs/appReady.js.map +1 -0
  49. package/lib/commonjs/checkForUpdate.js +121 -69
  50. package/lib/commonjs/checkForUpdate.js.map +1 -1
  51. package/lib/commonjs/clientPlugin.js +13 -0
  52. package/lib/commonjs/clientPlugin.js.map +1 -0
  53. package/lib/commonjs/error.js +5 -14
  54. package/lib/commonjs/error.js.map +1 -1
  55. package/lib/commonjs/fetchJSON.js +16 -27
  56. package/lib/commonjs/fetchJSON.js.map +1 -1
  57. package/lib/commonjs/fetchUpdateResponse.js +60 -0
  58. package/lib/commonjs/fetchUpdateResponse.js.map +1 -0
  59. package/lib/commonjs/httpClient.js +64 -95
  60. package/lib/commonjs/httpClient.js.map +1 -1
  61. package/lib/commonjs/index.js +355 -324
  62. package/lib/commonjs/index.js.map +1 -1
  63. package/lib/commonjs/init.types.js +6 -0
  64. package/lib/commonjs/init.types.js.map +1 -0
  65. package/lib/commonjs/native.js +77 -63
  66. package/lib/commonjs/native.js.map +1 -1
  67. package/lib/commonjs/pluginHost.js +41 -0
  68. package/lib/commonjs/pluginHost.js.map +1 -0
  69. package/lib/commonjs/releaseCatalogCache.js +36 -87
  70. package/lib/commonjs/releaseCatalogCache.js.map +1 -1
  71. package/lib/commonjs/sdkVersion.js +1 -1
  72. package/lib/commonjs/sdkVersion.js.map +1 -1
  73. package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
  74. package/lib/commonjs/store.js +3 -10
  75. package/lib/commonjs/store.js.map +1 -1
  76. package/lib/commonjs/updateError.js +145 -0
  77. package/lib/commonjs/updateError.js.map +1 -0
  78. package/lib/commonjs/wrap.js +46 -38
  79. package/lib/commonjs/wrap.js.map +1 -1
  80. package/lib/module/appReady.js +90 -0
  81. package/lib/module/appReady.js.map +1 -0
  82. package/lib/module/checkForUpdate.js +114 -64
  83. package/lib/module/checkForUpdate.js.map +1 -1
  84. package/lib/module/clientPlugin.js +4 -0
  85. package/lib/module/clientPlugin.js.map +1 -0
  86. package/lib/module/error.js +5 -14
  87. package/lib/module/error.js.map +1 -1
  88. package/lib/module/fetchJSON.js +14 -26
  89. package/lib/module/fetchJSON.js.map +1 -1
  90. package/lib/module/fetchUpdateResponse.js +55 -0
  91. package/lib/module/fetchUpdateResponse.js.map +1 -0
  92. package/lib/module/httpClient.js +63 -94
  93. package/lib/module/httpClient.js.map +1 -1
  94. package/lib/module/index.js +335 -327
  95. package/lib/module/index.js.map +1 -1
  96. package/lib/module/init.types.js +4 -0
  97. package/lib/module/init.types.js.map +1 -0
  98. package/lib/module/native.js +70 -56
  99. package/lib/module/native.js.map +1 -1
  100. package/lib/module/pluginHost.js +36 -0
  101. package/lib/module/pluginHost.js.map +1 -0
  102. package/lib/module/releaseCatalogCache.js +32 -82
  103. package/lib/module/releaseCatalogCache.js.map +1 -1
  104. package/lib/module/sdkVersion.js +1 -1
  105. package/lib/module/sdkVersion.js.map +1 -1
  106. package/lib/module/specs/NativeHotUpdater.js.map +1 -1
  107. package/lib/module/store.js +3 -10
  108. package/lib/module/store.js.map +1 -1
  109. package/lib/module/updateError.js +139 -0
  110. package/lib/module/updateError.js.map +1 -0
  111. package/lib/module/wrap.js +47 -38
  112. package/lib/module/wrap.js.map +1 -1
  113. package/lib/typescript/commonjs/appReady.d.ts +30 -0
  114. package/lib/typescript/commonjs/appReady.d.ts.map +1 -0
  115. package/lib/typescript/commonjs/checkForUpdate.d.ts +20 -6
  116. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  117. package/lib/typescript/commonjs/clientPlugin.d.ts +2 -0
  118. package/lib/typescript/commonjs/clientPlugin.d.ts.map +1 -0
  119. package/lib/typescript/commonjs/error.d.ts +5 -14
  120. package/lib/typescript/commonjs/error.d.ts.map +1 -1
  121. package/lib/typescript/commonjs/fetchJSON.d.ts +5 -5
  122. package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
  123. package/lib/typescript/commonjs/fetchUpdateResponse.d.ts +14 -0
  124. package/lib/typescript/commonjs/fetchUpdateResponse.d.ts.map +1 -0
  125. package/lib/typescript/commonjs/httpClient.d.ts +5 -34
  126. package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
  127. package/lib/typescript/commonjs/index.d.ts +124 -116
  128. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  129. package/lib/typescript/commonjs/init.types.d.ts +21 -0
  130. package/lib/typescript/commonjs/init.types.d.ts.map +1 -0
  131. package/lib/typescript/commonjs/native.d.ts +32 -25
  132. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  133. package/lib/typescript/commonjs/pluginHost.d.ts +8 -0
  134. package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -0
  135. package/lib/typescript/commonjs/releaseCatalogCache.d.ts +8 -14
  136. package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -1
  137. package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
  138. package/lib/typescript/commonjs/sdkVersion.d.ts.map +1 -1
  139. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +35 -47
  140. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
  141. package/lib/typescript/commonjs/store.d.ts +1 -3
  142. package/lib/typescript/commonjs/store.d.ts.map +1 -1
  143. package/lib/typescript/commonjs/types.d.ts +1 -1
  144. package/lib/typescript/commonjs/types.d.ts.map +1 -1
  145. package/lib/typescript/commonjs/updateError.d.ts +26 -0
  146. package/lib/typescript/commonjs/updateError.d.ts.map +1 -0
  147. package/lib/typescript/commonjs/wrap.d.ts +2 -3
  148. package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
  149. package/lib/typescript/commonjs/wrap.types.d.ts +13 -41
  150. package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
  151. package/lib/typescript/module/appReady.d.ts +30 -0
  152. package/lib/typescript/module/appReady.d.ts.map +1 -0
  153. package/lib/typescript/module/checkForUpdate.d.ts +20 -6
  154. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  155. package/lib/typescript/module/clientPlugin.d.ts +2 -0
  156. package/lib/typescript/module/clientPlugin.d.ts.map +1 -0
  157. package/lib/typescript/module/error.d.ts +5 -14
  158. package/lib/typescript/module/error.d.ts.map +1 -1
  159. package/lib/typescript/module/fetchJSON.d.ts +5 -5
  160. package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
  161. package/lib/typescript/module/fetchUpdateResponse.d.ts +14 -0
  162. package/lib/typescript/module/fetchUpdateResponse.d.ts.map +1 -0
  163. package/lib/typescript/module/httpClient.d.ts +5 -34
  164. package/lib/typescript/module/httpClient.d.ts.map +1 -1
  165. package/lib/typescript/module/index.d.ts +124 -116
  166. package/lib/typescript/module/index.d.ts.map +1 -1
  167. package/lib/typescript/module/init.types.d.ts +21 -0
  168. package/lib/typescript/module/init.types.d.ts.map +1 -0
  169. package/lib/typescript/module/native.d.ts +32 -25
  170. package/lib/typescript/module/native.d.ts.map +1 -1
  171. package/lib/typescript/module/pluginHost.d.ts +8 -0
  172. package/lib/typescript/module/pluginHost.d.ts.map +1 -0
  173. package/lib/typescript/module/releaseCatalogCache.d.ts +8 -14
  174. package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -1
  175. package/lib/typescript/module/sdkVersion.d.ts +1 -1
  176. package/lib/typescript/module/sdkVersion.d.ts.map +1 -1
  177. package/lib/typescript/module/specs/NativeHotUpdater.d.ts +35 -47
  178. package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
  179. package/lib/typescript/module/store.d.ts +1 -3
  180. package/lib/typescript/module/store.d.ts.map +1 -1
  181. package/lib/typescript/module/types.d.ts +1 -1
  182. package/lib/typescript/module/types.d.ts.map +1 -1
  183. package/lib/typescript/module/updateError.d.ts +26 -0
  184. package/lib/typescript/module/updateError.d.ts.map +1 -0
  185. package/lib/typescript/module/wrap.d.ts +2 -3
  186. package/lib/typescript/module/wrap.d.ts.map +1 -1
  187. package/lib/typescript/module/wrap.types.d.ts +13 -41
  188. package/lib/typescript/module/wrap.types.d.ts.map +1 -1
  189. package/package.json +8 -4
  190. package/src/appReady.ts +147 -0
  191. package/src/checkForUpdate.ts +156 -89
  192. package/src/clientPlugin.ts +21 -0
  193. package/src/error.ts +5 -15
  194. package/src/fetchJSON.ts +14 -30
  195. package/src/fetchUpdateResponse.ts +60 -0
  196. package/src/httpClient.ts +89 -160
  197. package/src/index.ts +413 -425
  198. package/src/init.types.ts +24 -0
  199. package/src/native.ts +101 -105
  200. package/src/pluginHost.ts +52 -0
  201. package/src/releaseCatalogCache.ts +43 -172
  202. package/src/sdkVersion.ts +1 -1
  203. package/src/specs/NativeHotUpdater.ts +34 -48
  204. package/src/store.ts +2 -23
  205. package/src/types.ts +1 -1
  206. package/src/updateError.ts +167 -0
  207. package/src/wrap.tsx +60 -43
  208. package/src/wrap.types.ts +18 -46
  209. package/android/src/main/java/com/hotupdater/DecompressService.kt +0 -89
  210. package/android/src/main/java/com/hotupdater/DecompressionStrategy.kt +0 -26
  211. package/android/src/main/java/com/hotupdater/TarBrDecompressionStrategy.kt +0 -117
  212. package/android/src/main/java/com/hotupdater/TarGzDecompressionStrategy.kt +0 -117
  213. package/android/src/main/java/com/hotupdater/ZipDecompressionStrategy.kt +0 -192
  214. package/android/src/test/java/com/hotupdater/BsdiffPatchTest.kt +0 -46
  215. package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +0 -799
  216. package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +0 -273
  217. package/android/src/test/java/com/hotupdater/NativeConfigUtilsTest.kt +0 -20
  218. package/android/src/test/java/com/hotupdater/OkHttpDownloadServiceTest.kt +0 -111
  219. package/android/src/test/java/com/hotupdater/RelativePathResolverTest.kt +0 -41
  220. package/android/src/test/java/com/hotupdater/ReleaseCatalogCacheServiceTest.kt +0 -118
  221. package/android/src/test/java/com/hotupdater/TarArchiveInputStreamTest.kt +0 -68
  222. package/ios/HotUpdater/Internal/ArchiveExtractionUtilities.swift +0 -178
  223. package/ios/HotUpdater/Internal/DecompressService.swift +0 -112
  224. package/ios/HotUpdater/Internal/DecompressionStrategy.swift +0 -22
  225. package/ios/HotUpdater/Internal/StreamingTarArchiveExtractor.swift +0 -370
  226. package/ios/HotUpdater/Internal/TarBrDecompressionStrategy.swift +0 -39
  227. package/ios/HotUpdater/Internal/TarGzDecompressionStrategy.swift +0 -59
  228. package/ios/HotUpdater/Internal/ZipArchiveExtractor.swift +0 -482
  229. package/ios/HotUpdater/Internal/ZipDecompressionStrategy.swift +0 -56
  230. package/lib/commonjs/catalogCacheNative.spec.js +0 -31
  231. package/lib/commonjs/catalogCacheNative.spec.js.map +0 -1
  232. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +0 -616
  233. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  234. package/lib/commonjs/httpClient.spec.js +0 -102
  235. package/lib/commonjs/httpClient.spec.js.map +0 -1
  236. package/lib/commonjs/index.spec.js +0 -302
  237. package/lib/commonjs/index.spec.js.map +0 -1
  238. package/lib/commonjs/iosInterop.spec.js +0 -18
  239. package/lib/commonjs/iosInterop.spec.js.map +0 -1
  240. package/lib/commonjs/native.spec.js +0 -883
  241. package/lib/commonjs/native.spec.js.map +0 -1
  242. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js +0 -218
  243. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  244. package/lib/commonjs/notifyAppReadyInsights.js +0 -133
  245. package/lib/commonjs/notifyAppReadyInsights.js.map +0 -1
  246. package/lib/commonjs/notifyAppReadyInsights.spec.js +0 -246
  247. package/lib/commonjs/notifyAppReadyInsights.spec.js.map +0 -1
  248. package/lib/commonjs/notifyAppReadyInsights.test-utils.js +0 -24
  249. package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +0 -1
  250. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js +0 -117
  251. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +0 -1
  252. package/lib/commonjs/releaseCatalogCache.spec.js +0 -195
  253. package/lib/commonjs/releaseCatalogCache.spec.js.map +0 -1
  254. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js +0 -64
  255. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  256. package/lib/commonjs/store.notification.spec.js +0 -76
  257. package/lib/commonjs/store.notification.spec.js.map +0 -1
  258. package/lib/commonjs/store.spec.js +0 -316
  259. package/lib/commonjs/store.spec.js.map +0 -1
  260. package/lib/commonjs/wrap.spec.js +0 -329
  261. package/lib/commonjs/wrap.spec.js.map +0 -1
  262. package/lib/module/catalogCacheNative.spec.js +0 -31
  263. package/lib/module/catalogCacheNative.spec.js.map +0 -1
  264. package/lib/module/checkForUpdate.releaseCatalog.spec.js +0 -615
  265. package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  266. package/lib/module/httpClient.spec.js +0 -102
  267. package/lib/module/httpClient.spec.js.map +0 -1
  268. package/lib/module/index.spec.js +0 -301
  269. package/lib/module/index.spec.js.map +0 -1
  270. package/lib/module/iosInterop.spec.js +0 -18
  271. package/lib/module/iosInterop.spec.js.map +0 -1
  272. package/lib/module/native.spec.js +0 -882
  273. package/lib/module/native.spec.js.map +0 -1
  274. package/lib/module/notifyAppReadyInsights.boundary.spec.js +0 -217
  275. package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  276. package/lib/module/notifyAppReadyInsights.js +0 -128
  277. package/lib/module/notifyAppReadyInsights.js.map +0 -1
  278. package/lib/module/notifyAppReadyInsights.spec.js +0 -245
  279. package/lib/module/notifyAppReadyInsights.spec.js.map +0 -1
  280. package/lib/module/notifyAppReadyInsights.test-utils.js +0 -18
  281. package/lib/module/notifyAppReadyInsights.test-utils.js.map +0 -1
  282. package/lib/module/notifyAppReadyInsights.transport.spec.js +0 -116
  283. package/lib/module/notifyAppReadyInsights.transport.spec.js.map +0 -1
  284. package/lib/module/releaseCatalogCache.spec.js +0 -195
  285. package/lib/module/releaseCatalogCache.spec.js.map +0 -1
  286. package/lib/module/releaseCatalogCacheNativeContract.spec.js +0 -64
  287. package/lib/module/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  288. package/lib/module/store.notification.spec.js +0 -77
  289. package/lib/module/store.notification.spec.js.map +0 -1
  290. package/lib/module/store.spec.js +0 -315
  291. package/lib/module/store.spec.js.map +0 -1
  292. package/lib/module/wrap.spec.js +0 -330
  293. package/lib/module/wrap.spec.js.map +0 -1
  294. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +0 -13
  295. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +0 -1
  296. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +0 -8
  297. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  298. package/lib/typescript/module/notifyAppReadyInsights.d.ts +0 -13
  299. package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +0 -1
  300. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +0 -8
  301. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  302. package/src/catalogCacheNative.spec.ts +0 -63
  303. package/src/checkForUpdate.releaseCatalog.spec.ts +0 -612
  304. package/src/httpClient.spec.ts +0 -132
  305. package/src/index.spec.ts +0 -337
  306. package/src/iosInterop.spec.ts +0 -30
  307. package/src/native.spec.ts +0 -945
  308. package/src/notifyAppReadyInsights.boundary.spec.ts +0 -234
  309. package/src/notifyAppReadyInsights.spec.ts +0 -243
  310. package/src/notifyAppReadyInsights.test-utils.ts +0 -31
  311. package/src/notifyAppReadyInsights.transport.spec.ts +0 -130
  312. package/src/notifyAppReadyInsights.ts +0 -194
  313. package/src/releaseCatalogCache.spec.ts +0 -282
  314. package/src/releaseCatalogCacheNativeContract.spec.ts +0 -80
  315. package/src/store.notification.spec.ts +0 -92
  316. package/src/store.spec.ts +0 -372
  317. package/src/wrap.spec.ts +0 -406
@@ -1,178 +0,0 @@
1
- import Foundation
2
-
3
- enum ArchiveExtractionUtilities {
4
- static let bufferSize = 64 * 1024
5
-
6
- static func readUpToCount(from handle: FileHandle, count: Int) throws -> Data? {
7
- guard count >= 0 else {
8
- throw NSError(
9
- domain: "ArchiveExtractionUtilities",
10
- code: 5,
11
- userInfo: [NSLocalizedDescriptionKey: "Invalid read size: \(count)"]
12
- )
13
- }
14
-
15
- if count == 0 {
16
- return Data()
17
- }
18
-
19
- if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) {
20
- return try handle.read(upToCount: count)
21
- }
22
-
23
- return handle.readData(ofLength: count)
24
- }
25
-
26
- static func readExactly(from handle: FileHandle, count: Int) throws -> Data {
27
- guard count >= 0 else {
28
- throw NSError(
29
- domain: "ArchiveExtractionUtilities",
30
- code: 1,
31
- userInfo: [NSLocalizedDescriptionKey: "Invalid read size: \(count)"]
32
- )
33
- }
34
-
35
- if count == 0 {
36
- return Data()
37
- }
38
-
39
- guard let data = try readUpToCount(from: handle, count: count), data.count == count else {
40
- throw NSError(
41
- domain: "ArchiveExtractionUtilities",
42
- code: 2,
43
- userInfo: [NSLocalizedDescriptionKey: "Unexpected end of archive while reading \(count) bytes"]
44
- )
45
- }
46
-
47
- return data
48
- }
49
-
50
- static func currentOffset(for handle: FileHandle) -> UInt64 {
51
- if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) {
52
- return (try? handle.offset()) ?? 0
53
- }
54
-
55
- return handle.offsetInFile
56
- }
57
-
58
- static func seek(_ handle: FileHandle, to offset: UInt64) throws {
59
- if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) {
60
- try handle.seek(toOffset: offset)
61
- return
62
- }
63
-
64
- handle.seek(toFileOffset: offset)
65
- }
66
-
67
- static func skipBytes(_ byteCount: UInt64, in handle: FileHandle) throws {
68
- guard byteCount > 0 else {
69
- return
70
- }
71
-
72
- let offset = currentOffset(for: handle)
73
- let (targetOffset, overflowed) = offset.addingReportingOverflow(byteCount)
74
-
75
- guard !overflowed else {
76
- throw NSError(
77
- domain: "ArchiveExtractionUtilities",
78
- code: 6,
79
- userInfo: [NSLocalizedDescriptionKey: "Archive offset overflow while skipping \(byteCount) bytes from offset \(offset)"]
80
- )
81
- }
82
-
83
- try seek(handle, to: targetOffset)
84
- }
85
-
86
- static func normalizedRelativePath(from rawPath: String) -> String? {
87
- let candidate = rawPath.trimmingCharacters(in: .whitespacesAndNewlines)
88
- guard !candidate.isEmpty,
89
- !candidate.contains("\0"),
90
- !candidate.contains("\\"),
91
- !candidate.hasPrefix("/"),
92
- candidate.range(of: #"^[A-Za-z]:"#, options: .regularExpression) == nil
93
- else {
94
- return nil
95
- }
96
-
97
- let components = candidate
98
- .split(separator: "/")
99
- .map(String.init)
100
-
101
- guard !components.isEmpty else {
102
- return nil
103
- }
104
-
105
- guard components.count == candidate.split(separator: "/", omittingEmptySubsequences: false).count,
106
- !components.contains(".."),
107
- !components.contains(".") else {
108
- return nil
109
- }
110
-
111
- return components.joined(separator: "/")
112
- }
113
-
114
- static func extractionURL(for relativePath: String, destinationRoot: String) throws -> URL {
115
- let rootURL = URL(fileURLWithPath: destinationRoot, isDirectory: true)
116
- let targetURL = rootURL.appendingPathComponent(relativePath)
117
- let standardizedRoot = rootURL.standardizedFileURL.path
118
- let standardizedTarget = targetURL.standardizedFileURL.path
119
-
120
- guard standardizedTarget == standardizedRoot ||
121
- standardizedTarget.hasPrefix(standardizedRoot + "/") else {
122
- throw NSError(
123
- domain: "ArchiveExtractionUtilities",
124
- code: 3,
125
- userInfo: [NSLocalizedDescriptionKey: "Path traversal attempt detected: \(relativePath)"]
126
- )
127
- }
128
-
129
- return targetURL
130
- }
131
-
132
- static func ensureDirectory(at url: URL, fileManager: FileManager = .default) throws {
133
- var isDirectory = ObjCBool(false)
134
- if fileManager.fileExists(atPath: url.path, isDirectory: &isDirectory) {
135
- if isDirectory.boolValue {
136
- return
137
- }
138
-
139
- try fileManager.removeItem(at: url)
140
- }
141
-
142
- try fileManager.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
143
- }
144
-
145
- static func createOutputFile(at url: URL, fileManager: FileManager = .default) throws -> FileHandle {
146
- try ensureDirectory(at: url.deletingLastPathComponent(), fileManager: fileManager)
147
-
148
- if fileManager.fileExists(atPath: url.path) {
149
- try fileManager.removeItem(at: url)
150
- }
151
-
152
- guard fileManager.createFile(atPath: url.path, contents: nil) else {
153
- throw NSError(
154
- domain: "ArchiveExtractionUtilities",
155
- code: 4,
156
- userInfo: [NSLocalizedDescriptionKey: "Failed to create output file at \(url.path)"]
157
- )
158
- }
159
-
160
- return try FileHandle(forWritingTo: url)
161
- }
162
- }
163
-
164
- extension Data {
165
- func archiveUInt16LE(at offset: Int) -> UInt16 {
166
- let byte0 = UInt16(self[offset])
167
- let byte1 = UInt16(self[offset + 1]) << 8
168
- return byte0 | byte1
169
- }
170
-
171
- func archiveUInt32LE(at offset: Int) -> UInt32 {
172
- let byte0 = UInt32(self[offset])
173
- let byte1 = UInt32(self[offset + 1]) << 8
174
- let byte2 = UInt32(self[offset + 2]) << 16
175
- let byte3 = UInt32(self[offset + 3]) << 24
176
- return byte0 | byte1 | byte2 | byte3
177
- }
178
- }
@@ -1,112 +0,0 @@
1
- import Foundation
2
-
3
- /**
4
- * Unified decompression service that uses Strategy pattern to handle multiple compression formats.
5
- * Automatically detects format by trying each strategy's validation and delegates to appropriate decompression strategy.
6
- */
7
- class DecompressService {
8
- /// Strategies with reliable file signatures that can be validated cheaply.
9
- private let signatureStrategies: [DecompressionStrategy]
10
- /// TAR.BR has no reliable magic bytes, so it is attempted as the final fallback.
11
- private let tarBrStrategy: DecompressionStrategy
12
-
13
- init() {
14
- // Order matters: Try ZIP first (clear magic bytes), then TAR.GZ (GZIP magic bytes).
15
- // TAR.BR is attempted only after signature-based formats are ruled out.
16
- self.signatureStrategies = [
17
- ZipDecompressionStrategy(),
18
- TarGzDecompressionStrategy()
19
- ]
20
- self.tarBrStrategy = TarBrDecompressionStrategy()
21
- }
22
-
23
- /**
24
- * Extracts a compressed file to the destination directory.
25
- * Automatically detects compression format by trying each strategy's validation.
26
- * @param file Path to the compressed file
27
- * @param destination Path to the destination directory
28
- * @param progressHandler Callback for progress updates (0.0 - 1.0)
29
- * @throws Error if decompression fails or no valid strategy found
30
- */
31
- func unzip(file: String, to destination: String, progressHandler: @escaping (Double) -> Void) throws {
32
- // Collect file information for better error messages
33
- let fileURL = URL(fileURLWithPath: file)
34
- let fileName = fileURL.lastPathComponent
35
- let fileSize = (try? FileManager.default.attributesOfItem(atPath: file)[.size] as? UInt64) ?? 0
36
-
37
- // Try each signature-based strategy first.
38
- for strategy in signatureStrategies {
39
- if strategy.isValid(file: file) {
40
- NSLog("[DecompressService] Using strategy for \(fileName)")
41
- try strategy.decompress(file: file, to: destination, progressHandler: progressHandler)
42
- return
43
- }
44
- }
45
-
46
- NSLog("[DecompressService] No ZIP/TAR.GZ signature matched for \(fileName), trying TAR.BR fallback")
47
-
48
- do {
49
- try tarBrStrategy.decompress(file: file, to: destination, progressHandler: progressHandler)
50
- NSLog("[DecompressService] Using TAR.BR fallback for \(fileName)")
51
- return
52
- } catch {
53
- let invalidArchiveError = createInvalidArchiveError(
54
- fileName: fileName,
55
- fileSize: fileSize,
56
- underlyingError: error
57
- )
58
- NSLog("[DecompressService] \(invalidArchiveError.localizedDescription)")
59
- throw invalidArchiveError
60
- }
61
- }
62
-
63
- /**
64
- * Extracts a compressed file to the destination directory (without progress tracking).
65
- * @param file Path to the compressed file
66
- * @param destination Path to the destination directory
67
- * @throws Error if decompression fails or no valid strategy found
68
- */
69
- func unzip(file: String, to destination: String) throws {
70
- try unzip(file: file, to: destination, progressHandler: { _ in })
71
- }
72
-
73
- /**
74
- * Validates if a file matches one of the signature-based archive formats.
75
- * @param file Path to the file to validate
76
- * @return true if the file is valid for any strategy
77
- */
78
- func isValid(file: String) -> Bool {
79
- for strategy in signatureStrategies {
80
- if strategy.isValid(file: file) {
81
- return true
82
- }
83
- }
84
- NSLog("[DecompressService] No ZIP/TAR.GZ signature matched for file: \(file). TAR.BR is handled during extraction fallback.")
85
- return false
86
- }
87
-
88
- private func createInvalidArchiveError(fileName: String, fileSize: UInt64, underlyingError: Error? = nil) -> NSError {
89
- let errorMessage = """
90
- The downloaded bundle file is not a valid compressed archive: \(fileName) (\(fileSize) bytes)
91
-
92
- Supported formats:
93
- - ZIP archives (.zip)
94
- - GZIP compressed TAR archives (.tar.gz)
95
- - Brotli compressed TAR archives (.tar.br)
96
- """
97
-
98
- var userInfo: [String: Any] = [
99
- NSLocalizedDescriptionKey: errorMessage
100
- ]
101
-
102
- if let underlyingError {
103
- userInfo[NSUnderlyingErrorKey] = underlyingError
104
- }
105
-
106
- return NSError(
107
- domain: "DecompressService",
108
- code: 1,
109
- userInfo: userInfo
110
- )
111
- }
112
- }
@@ -1,22 +0,0 @@
1
- import Foundation
2
-
3
- /**
4
- * Protocol for decompression strategies
5
- */
6
- protocol DecompressionStrategy {
7
- /**
8
- * Validates if a file can be decompressed by this strategy
9
- * @param file Path to the file to validate
10
- * @return true if the file is valid for this strategy
11
- */
12
- func isValid(file: String) -> Bool
13
-
14
- /**
15
- * Decompresses a file to the destination directory
16
- * @param file Path to the compressed file
17
- * @param destination Path to the destination directory
18
- * @param progressHandler Callback for progress updates (0.0 - 1.0)
19
- * @throws Error if decompression fails
20
- */
21
- func decompress(file: String, to destination: String, progressHandler: @escaping (Double) -> Void) throws
22
- }
@@ -1,370 +0,0 @@
1
- import Compression
2
- import Foundation
3
- import zlib
4
-
5
- enum CompressedTarAlgorithm {
6
- case gzip
7
- case brotli
8
- }
9
-
10
- enum StreamingTarArchiveExtractor {
11
- private static let bufferSize = 64 * 1024
12
- private static let decompressionProgressWeight = 0.45
13
-
14
- static func extractCompressedTar(
15
- file: String,
16
- to destination: String,
17
- algorithm: CompressedTarAlgorithm,
18
- progressHandler: @escaping (Double) -> Void
19
- ) throws {
20
- try withTemporaryTarFile { temporaryTarURL in
21
- switch algorithm {
22
- case .gzip:
23
- try decompressGzipArchive(
24
- from: file,
25
- to: temporaryTarURL.path,
26
- progressHandler: { progress in
27
- progressHandler(progress * decompressionProgressWeight)
28
- }
29
- )
30
- case .brotli:
31
- try decompressBrotliArchive(
32
- from: file,
33
- to: temporaryTarURL.path,
34
- progressHandler: { progress in
35
- progressHandler(progress * decompressionProgressWeight)
36
- }
37
- )
38
- }
39
-
40
- try extractTarArchive(
41
- from: temporaryTarURL.path,
42
- to: destination,
43
- progressHandler: { progress in
44
- let extractionStart = decompressionProgressWeight
45
- let extractionWeight = 1.0 - decompressionProgressWeight
46
- progressHandler(extractionStart + (progress * extractionWeight))
47
- }
48
- )
49
- }
50
- }
51
-
52
- static func decompressBrotliFile(
53
- from sourcePath: String,
54
- to outputPath: String
55
- ) throws {
56
- try decompressBrotliArchive(
57
- from: sourcePath,
58
- to: outputPath,
59
- progressHandler: { _ in }
60
- )
61
- }
62
-
63
- static func containsTarEntries(file: String, algorithm: CompressedTarAlgorithm) -> Bool {
64
- do {
65
- return try withTemporaryTarFile { temporaryTarURL in
66
- switch algorithm {
67
- case .gzip:
68
- try decompressGzipArchive(
69
- from: file,
70
- to: temporaryTarURL.path,
71
- progressHandler: { _ in }
72
- )
73
- case .brotli:
74
- try decompressBrotliArchive(
75
- from: file,
76
- to: temporaryTarURL.path,
77
- progressHandler: { _ in }
78
- )
79
- }
80
-
81
- return try tarArchiveHasEntries(at: temporaryTarURL.path)
82
- }
83
- } catch {
84
- NSLog("[TarStreamExtractor] Validation failed: \(error.localizedDescription)")
85
- return false
86
- }
87
- }
88
-
89
- private static func withTemporaryTarFile<T>(
90
- perform: (URL) throws -> T
91
- ) throws -> T {
92
- let temporaryTarURL = FileManager.default.temporaryDirectory
93
- .appendingPathComponent("hot-updater-\(UUID().uuidString)")
94
- .appendingPathExtension("tar")
95
-
96
- defer {
97
- try? FileManager.default.removeItem(at: temporaryTarURL)
98
- }
99
-
100
- return try perform(temporaryTarURL)
101
- }
102
-
103
- private static func tarArchiveHasEntries(at tarPath: String) throws -> Bool {
104
- try TarArchiveExtractor.containsEntries(at: tarPath)
105
- }
106
-
107
- private static func extractTarArchive(
108
- from tarPath: String,
109
- to destination: String,
110
- progressHandler: @escaping (Double) -> Void
111
- ) throws {
112
- try TarArchiveExtractor.extract(
113
- from: tarPath,
114
- to: destination,
115
- progressHandler: progressHandler
116
- )
117
- }
118
-
119
- private static func decompressGzipArchive(
120
- from sourcePath: String,
121
- to outputPath: String,
122
- progressHandler: @escaping (Double) -> Void
123
- ) throws {
124
- let totalSourceSize = try fileSize(atPath: sourcePath)
125
- try prepareEmptyFile(at: outputPath)
126
-
127
- let outputHandle = try FileHandle(forWritingTo: URL(fileURLWithPath: outputPath))
128
-
129
- defer {
130
- try? outputHandle.close()
131
- }
132
-
133
- guard let gzipFile = sourcePath.withCString({ pathPointer in
134
- "rb".withCString { modePointer in
135
- gzopen(pathPointer, modePointer)
136
- }
137
- }) else {
138
- throw NSError(
139
- domain: "StreamingTarArchiveExtractor",
140
- code: 5,
141
- userInfo: [NSLocalizedDescriptionKey: "Failed to open gzip archive"]
142
- )
143
- }
144
-
145
- defer {
146
- gzclose(gzipFile)
147
- }
148
-
149
- var buffer = [UInt8](repeating: 0, count: bufferSize)
150
-
151
- while true {
152
- let bytesRead = gzread(gzipFile, &buffer, UInt32(buffer.count))
153
-
154
- if bytesRead > 0 {
155
- outputHandle.write(Data(buffer[0..<Int(bytesRead)]))
156
-
157
- if totalSourceSize > 0 {
158
- let compressedOffset = max(Int64(gzoffset(gzipFile)), 0)
159
- let progress = min(Double(compressedOffset) / Double(totalSourceSize), 1.0)
160
- progressHandler(progress)
161
- }
162
-
163
- continue
164
- }
165
-
166
- if bytesRead == 0 {
167
- progressHandler(1.0)
168
- return
169
- }
170
-
171
- var errorCode: Int32 = 0
172
- let messagePointer = gzerror(gzipFile, &errorCode)
173
- let message = messagePointer.map { String(cString: $0) } ?? "Unknown gzip error"
174
-
175
- throw NSError(
176
- domain: "StreamingTarArchiveExtractor",
177
- code: Int(errorCode),
178
- userInfo: [NSLocalizedDescriptionKey: "GZIP decompression failed: \(message)"]
179
- )
180
- }
181
- }
182
-
183
- private static func decompressBrotliArchive(
184
- from sourcePath: String,
185
- to outputPath: String,
186
- progressHandler: @escaping (Double) -> Void
187
- ) throws {
188
- let totalSourceSize = try fileSize(atPath: sourcePath)
189
- try prepareEmptyFile(at: outputPath)
190
-
191
- let inputHandle = try FileHandle(forReadingFrom: URL(fileURLWithPath: sourcePath))
192
- let outputHandle = try FileHandle(forWritingTo: URL(fileURLWithPath: outputPath))
193
-
194
- defer {
195
- try? inputHandle.close()
196
- try? outputHandle.close()
197
- }
198
-
199
- var stream = compression_stream(
200
- dst_ptr: UnsafeMutablePointer<UInt8>(bitPattern: 1)!,
201
- dst_size: 0,
202
- src_ptr: UnsafePointer<UInt8>(bitPattern: 1)!,
203
- src_size: 0,
204
- state: nil
205
- )
206
- let status = compression_stream_init(&stream, COMPRESSION_STREAM_DECODE, COMPRESSION_BROTLI)
207
-
208
- guard status == COMPRESSION_STATUS_OK else {
209
- throw NSError(
210
- domain: "StreamingTarArchiveExtractor",
211
- code: 6,
212
- userInfo: [NSLocalizedDescriptionKey: "Failed to initialize Brotli decompression stream"]
213
- )
214
- }
215
-
216
- defer {
217
- compression_stream_destroy(&stream)
218
- }
219
-
220
- let outputBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
221
-
222
- defer {
223
- outputBuffer.deallocate()
224
- }
225
-
226
- var processedSourceBytes: UInt64 = 0
227
- var reachedStreamEnd = false
228
-
229
- while !reachedStreamEnd {
230
- let chunk = try ArchiveExtractionUtilities.readUpToCount(
231
- from: inputHandle,
232
- count: bufferSize
233
- ) ?? Data()
234
- processedSourceBytes += UInt64(chunk.count)
235
-
236
- let streamStatus: compression_status
237
- if chunk.isEmpty {
238
- stream.src_ptr = UnsafePointer<UInt8>(bitPattern: 1)!
239
- stream.src_size = 0
240
- streamStatus = try flushCompressionStream(
241
- &stream,
242
- into: outputHandle,
243
- outputBuffer: outputBuffer,
244
- finalize: true
245
- )
246
- } else {
247
- streamStatus = try chunk.withUnsafeBytes { rawBuffer in
248
- guard let baseAddress = rawBuffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
249
- return COMPRESSION_STATUS_OK
250
- }
251
-
252
- stream.src_ptr = baseAddress
253
- stream.src_size = chunk.count
254
-
255
- return try flushCompressionStream(
256
- &stream,
257
- into: outputHandle,
258
- outputBuffer: outputBuffer,
259
- finalize: false
260
- )
261
- }
262
- }
263
-
264
- if totalSourceSize > 0 {
265
- let progress = min(Double(processedSourceBytes) / Double(totalSourceSize), 1.0)
266
- progressHandler(progress)
267
- }
268
-
269
- if streamStatus == COMPRESSION_STATUS_END {
270
- reachedStreamEnd = true
271
- } else if chunk.isEmpty {
272
- throw NSError(
273
- domain: "StreamingTarArchiveExtractor",
274
- code: 7,
275
- userInfo: [NSLocalizedDescriptionKey: "Brotli decompression ended before reaching the end of stream"]
276
- )
277
- }
278
- }
279
-
280
- progressHandler(1.0)
281
- }
282
-
283
- private static func flushCompressionStream(
284
- _ stream: inout compression_stream,
285
- into outputHandle: FileHandle,
286
- outputBuffer: UnsafeMutablePointer<UInt8>,
287
- finalize: Bool
288
- ) throws -> compression_status {
289
- let flags = finalize ? Int32(COMPRESSION_STREAM_FINALIZE.rawValue) : 0
290
- var lastStatus = COMPRESSION_STATUS_OK
291
-
292
- repeat {
293
- let previousSourceSize = stream.src_size
294
-
295
- stream.dst_ptr = outputBuffer
296
- stream.dst_size = bufferSize
297
-
298
- lastStatus = compression_stream_process(&stream, flags)
299
-
300
- switch lastStatus {
301
- case COMPRESSION_STATUS_OK, COMPRESSION_STATUS_END:
302
- let producedBytes = bufferSize - stream.dst_size
303
- if producedBytes > 0 {
304
- outputHandle.write(
305
- Data(bytes: outputBuffer, count: producedBytes)
306
- )
307
- }
308
-
309
- if finalize,
310
- lastStatus == COMPRESSION_STATUS_OK,
311
- producedBytes == 0,
312
- previousSourceSize == stream.src_size {
313
- throw NSError(
314
- domain: "StreamingTarArchiveExtractor",
315
- code: 8,
316
- userInfo: [NSLocalizedDescriptionKey: "Brotli decompression stalled before reaching the end of stream"]
317
- )
318
- }
319
-
320
- default:
321
- throw NSError(
322
- domain: "StreamingTarArchiveExtractor",
323
- code: 9,
324
- userInfo: [NSLocalizedDescriptionKey: "Brotli decompression failed"]
325
- )
326
- }
327
- } while stream.src_size > 0 || stream.dst_size == 0 || (finalize && lastStatus == COMPRESSION_STATUS_OK)
328
-
329
- return lastStatus
330
- }
331
-
332
- private static func prepareEmptyFile(at path: String) throws {
333
- let fileManager = FileManager.default
334
- let parentDirectory = (path as NSString).deletingLastPathComponent
335
-
336
- if !fileManager.fileExists(atPath: parentDirectory) {
337
- try fileManager.createDirectory(
338
- atPath: parentDirectory,
339
- withIntermediateDirectories: true,
340
- attributes: nil
341
- )
342
- }
343
-
344
- if fileManager.fileExists(atPath: path) {
345
- try fileManager.removeItem(atPath: path)
346
- }
347
-
348
- guard fileManager.createFile(atPath: path, contents: nil) else {
349
- throw NSError(
350
- domain: "StreamingTarArchiveExtractor",
351
- code: 10,
352
- userInfo: [NSLocalizedDescriptionKey: "Failed to create temporary archive file"]
353
- )
354
- }
355
- }
356
-
357
- private static func fileSize(atPath path: String) throws -> UInt64 {
358
- let attributes = try FileManager.default.attributesOfItem(atPath: path)
359
- if let value = attributes[.size] as? NSNumber {
360
- return value.uint64Value
361
- }
362
-
363
- if let value = attributes[.size] as? UInt64 {
364
- return value
365
- }
366
-
367
- return 0
368
- }
369
-
370
- }