@hot-updater/react-native 1.0.0-rc.1 → 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 (227) hide show
  1. package/android/proguard-rules.pro +17 -2
  2. package/android/src/main/java/com/hotupdater/BuiltInAssetResolver.kt +286 -0
  3. package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +651 -637
  4. package/android/src/main/java/com/hotupdater/BundleMetadata.kt +10 -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/HotUpdaterRecoveryManager.kt +37 -54
  8. package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryState.kt +64 -0
  9. package/android/src/main/java/com/hotupdater/OkHttpDownloadService.kt +19 -0
  10. package/android/src/main/java/com/hotupdater/TarArchiveInputStream.kt +214 -375
  11. package/android/src/main/java/com/hotupdater/TarBrArchiveExtractor.kt +119 -0
  12. package/android/src/newarch/HotUpdater.kt +7 -9
  13. package/android/src/newarch/HotUpdaterModule.kt +40 -20
  14. package/android/src/oldarch/HotUpdater.kt +7 -9
  15. package/android/src/oldarch/HotUpdaterModule.kt +40 -20
  16. package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +1126 -74
  17. package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +4 -5
  18. package/android/src/test/java/com/hotupdater/HotUpdaterRecoveryStateTest.kt +97 -0
  19. package/android/src/test/java/com/hotupdater/PendingCrashRecoveryTest.kt +44 -0
  20. package/android/src/test/java/com/hotupdater/TarBrArchiveExtractorTest.kt +206 -0
  21. package/ios/HotUpdater/Internal/BrotliFileDecompressor.swift +167 -0
  22. package/ios/HotUpdater/Internal/BuiltInAssetResolver.swift +196 -0
  23. package/ios/HotUpdater/Internal/BundleFileStorageService.swift +773 -1016
  24. package/ios/HotUpdater/Internal/BundleMetadata.swift +5 -0
  25. package/ios/HotUpdater/Internal/FileUtilities.swift +64 -0
  26. package/ios/HotUpdater/Internal/HotUpdater-Bridging-Header.h +0 -1
  27. package/ios/HotUpdater/Internal/HotUpdater.mm +5 -27
  28. package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +149 -51
  29. package/ios/HotUpdater/Internal/NotificationExtension.swift +0 -2
  30. package/ios/HotUpdater/Internal/TarArchiveExtractor.swift +182 -311
  31. package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +13 -72
  32. package/ios/HotUpdater/Recovery/HotUpdaterRecovery.h +16 -0
  33. package/ios/HotUpdater/Recovery/HotUpdaterRecovery.mm +83 -0
  34. package/ios/HotUpdater/Recovery/HotUpdaterRecoveryHooks.mm +49 -0
  35. package/lib/commonjs/checkForUpdate.js +32 -23
  36. package/lib/commonjs/checkForUpdate.js.map +1 -1
  37. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +97 -28
  38. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +1 -1
  39. package/lib/commonjs/error.js +4 -13
  40. package/lib/commonjs/error.js.map +1 -1
  41. package/lib/commonjs/fetchJSON.js +9 -2
  42. package/lib/commonjs/fetchJSON.js.map +1 -1
  43. package/lib/commonjs/httpClient.js +46 -29
  44. package/lib/commonjs/httpClient.js.map +1 -1
  45. package/lib/commonjs/httpClient.spec.js +57 -5
  46. package/lib/commonjs/httpClient.spec.js.map +1 -1
  47. package/lib/commonjs/index.js +31 -14
  48. package/lib/commonjs/index.js.map +1 -1
  49. package/lib/commonjs/index.spec.js +56 -11
  50. package/lib/commonjs/index.spec.js.map +1 -1
  51. package/lib/commonjs/native.js +15 -14
  52. package/lib/commonjs/native.js.map +1 -1
  53. package/lib/commonjs/native.spec.js +128 -22
  54. package/lib/commonjs/native.spec.js.map +1 -1
  55. package/lib/commonjs/{notifyAppReadyAnalytics.boundary.spec.js → notifyAppReadyInsights.boundary.spec.js} +28 -21
  56. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +1 -0
  57. package/lib/commonjs/notifyAppReadyInsights.js +188 -0
  58. package/lib/commonjs/notifyAppReadyInsights.js.map +1 -0
  59. package/lib/commonjs/{notifyAppReadyAnalytics.spec.js → notifyAppReadyInsights.spec.js} +161 -29
  60. package/lib/commonjs/notifyAppReadyInsights.spec.js.map +1 -0
  61. package/lib/commonjs/{notifyAppReadyAnalytics.test-utils.js → notifyAppReadyInsights.test-utils.js} +3 -3
  62. package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +1 -0
  63. package/lib/commonjs/{notifyAppReadyAnalytics.transport.spec.js → notifyAppReadyInsights.transport.spec.js} +14 -7
  64. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +1 -0
  65. package/lib/commonjs/sdkVersion.js +1 -1
  66. package/lib/commonjs/sdkVersion.js.map +1 -1
  67. package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
  68. package/lib/commonjs/store.js +3 -10
  69. package/lib/commonjs/store.js.map +1 -1
  70. package/lib/commonjs/store.notification.spec.js +12 -4
  71. package/lib/commonjs/store.notification.spec.js.map +1 -1
  72. package/lib/commonjs/store.spec.js +2 -136
  73. package/lib/commonjs/store.spec.js.map +1 -1
  74. package/lib/commonjs/wrap.js +11 -12
  75. package/lib/commonjs/wrap.js.map +1 -1
  76. package/lib/commonjs/wrap.spec.js +97 -25
  77. package/lib/commonjs/wrap.spec.js.map +1 -1
  78. package/lib/module/checkForUpdate.js +32 -23
  79. package/lib/module/checkForUpdate.js.map +1 -1
  80. package/lib/module/checkForUpdate.releaseCatalog.spec.js +97 -28
  81. package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +1 -1
  82. package/lib/module/error.js +4 -13
  83. package/lib/module/error.js.map +1 -1
  84. package/lib/module/fetchJSON.js +7 -1
  85. package/lib/module/fetchJSON.js.map +1 -1
  86. package/lib/module/httpClient.js +48 -31
  87. package/lib/module/httpClient.js.map +1 -1
  88. package/lib/module/httpClient.spec.js +57 -5
  89. package/lib/module/httpClient.spec.js.map +1 -1
  90. package/lib/module/index.js +32 -15
  91. package/lib/module/index.js.map +1 -1
  92. package/lib/module/index.spec.js +56 -11
  93. package/lib/module/index.spec.js.map +1 -1
  94. package/lib/module/native.js +13 -12
  95. package/lib/module/native.js.map +1 -1
  96. package/lib/module/native.spec.js +128 -22
  97. package/lib/module/native.spec.js.map +1 -1
  98. package/lib/module/{notifyAppReadyAnalytics.boundary.spec.js → notifyAppReadyInsights.boundary.spec.js} +23 -16
  99. package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +1 -0
  100. package/lib/module/notifyAppReadyInsights.js +181 -0
  101. package/lib/module/notifyAppReadyInsights.js.map +1 -0
  102. package/lib/module/{notifyAppReadyAnalytics.spec.js → notifyAppReadyInsights.spec.js} +153 -21
  103. package/lib/module/notifyAppReadyInsights.spec.js.map +1 -0
  104. package/lib/module/{notifyAppReadyAnalytics.test-utils.js → notifyAppReadyInsights.test-utils.js} +3 -3
  105. package/lib/module/notifyAppReadyInsights.test-utils.js.map +1 -0
  106. package/lib/module/{notifyAppReadyAnalytics.transport.spec.js → notifyAppReadyInsights.transport.spec.js} +11 -4
  107. package/lib/module/notifyAppReadyInsights.transport.spec.js.map +1 -0
  108. package/lib/module/sdkVersion.js +1 -1
  109. package/lib/module/sdkVersion.js.map +1 -1
  110. package/lib/module/specs/NativeHotUpdater.js.map +1 -1
  111. package/lib/module/store.js +3 -10
  112. package/lib/module/store.js.map +1 -1
  113. package/lib/module/store.notification.spec.js +12 -4
  114. package/lib/module/store.notification.spec.js.map +1 -1
  115. package/lib/module/store.spec.js +2 -136
  116. package/lib/module/store.spec.js.map +1 -1
  117. package/lib/module/wrap.js +11 -12
  118. package/lib/module/wrap.js.map +1 -1
  119. package/lib/module/wrap.spec.js +99 -25
  120. package/lib/module/wrap.spec.js.map +1 -1
  121. package/lib/typescript/commonjs/checkForUpdate.d.ts +2 -3
  122. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  123. package/lib/typescript/commonjs/error.d.ts +4 -13
  124. package/lib/typescript/commonjs/error.d.ts.map +1 -1
  125. package/lib/typescript/commonjs/fetchJSON.d.ts +4 -0
  126. package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
  127. package/lib/typescript/commonjs/httpClient.d.ts +6 -6
  128. package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
  129. package/lib/typescript/commonjs/index.d.ts +4 -7
  130. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  131. package/lib/typescript/commonjs/native.d.ts +5 -9
  132. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  133. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +25 -0
  134. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +1 -0
  135. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +8 -0
  136. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +1 -0
  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 +8 -35
  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/wrap.types.d.ts +5 -8
  144. package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
  145. package/lib/typescript/module/checkForUpdate.d.ts +2 -3
  146. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  147. package/lib/typescript/module/error.d.ts +4 -13
  148. package/lib/typescript/module/error.d.ts.map +1 -1
  149. package/lib/typescript/module/fetchJSON.d.ts +4 -0
  150. package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
  151. package/lib/typescript/module/httpClient.d.ts +6 -6
  152. package/lib/typescript/module/httpClient.d.ts.map +1 -1
  153. package/lib/typescript/module/index.d.ts +4 -7
  154. package/lib/typescript/module/index.d.ts.map +1 -1
  155. package/lib/typescript/module/native.d.ts +5 -9
  156. package/lib/typescript/module/native.d.ts.map +1 -1
  157. package/lib/typescript/module/notifyAppReadyInsights.d.ts +25 -0
  158. package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +1 -0
  159. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +8 -0
  160. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +1 -0
  161. package/lib/typescript/module/sdkVersion.d.ts +1 -1
  162. package/lib/typescript/module/sdkVersion.d.ts.map +1 -1
  163. package/lib/typescript/module/specs/NativeHotUpdater.d.ts +8 -35
  164. package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
  165. package/lib/typescript/module/store.d.ts +1 -3
  166. package/lib/typescript/module/store.d.ts.map +1 -1
  167. package/lib/typescript/module/wrap.types.d.ts +5 -8
  168. package/lib/typescript/module/wrap.types.d.ts.map +1 -1
  169. package/package.json +3 -3
  170. package/src/checkForUpdate.releaseCatalog.spec.ts +137 -61
  171. package/src/checkForUpdate.ts +35 -29
  172. package/src/error.ts +4 -14
  173. package/src/fetchJSON.ts +12 -1
  174. package/src/httpClient.spec.ts +70 -5
  175. package/src/httpClient.ts +78 -64
  176. package/src/index.spec.ts +59 -11
  177. package/src/index.ts +41 -17
  178. package/src/native.spec.ts +133 -22
  179. package/src/native.ts +21 -32
  180. package/src/{notifyAppReadyAnalytics.boundary.spec.ts → notifyAppReadyInsights.boundary.spec.ts} +25 -17
  181. package/src/{notifyAppReadyAnalytics.spec.ts → notifyAppReadyInsights.spec.ts} +141 -22
  182. package/src/{notifyAppReadyAnalytics.test-utils.ts → notifyAppReadyInsights.test-utils.ts} +4 -4
  183. package/src/{notifyAppReadyAnalytics.transport.spec.ts → notifyAppReadyInsights.transport.spec.ts} +13 -5
  184. package/src/notifyAppReadyInsights.ts +293 -0
  185. package/src/sdkVersion.ts +1 -1
  186. package/src/specs/NativeHotUpdater.ts +8 -36
  187. package/src/store.notification.spec.ts +13 -3
  188. package/src/store.spec.ts +0 -157
  189. package/src/store.ts +2 -23
  190. package/src/wrap.spec.ts +109 -31
  191. package/src/wrap.tsx +11 -11
  192. package/src/wrap.types.ts +5 -8
  193. package/android/src/main/java/com/hotupdater/DecompressService.kt +0 -89
  194. package/android/src/main/java/com/hotupdater/DecompressionStrategy.kt +0 -26
  195. package/android/src/main/java/com/hotupdater/TarBrDecompressionStrategy.kt +0 -117
  196. package/android/src/main/java/com/hotupdater/TarGzDecompressionStrategy.kt +0 -117
  197. package/android/src/main/java/com/hotupdater/ZipDecompressionStrategy.kt +0 -192
  198. package/android/src/test/java/com/hotupdater/TarArchiveInputStreamTest.kt +0 -68
  199. package/ios/HotUpdater/Internal/ArchiveExtractionUtilities.swift +0 -178
  200. package/ios/HotUpdater/Internal/DecompressService.swift +0 -112
  201. package/ios/HotUpdater/Internal/DecompressionStrategy.swift +0 -22
  202. package/ios/HotUpdater/Internal/StreamingTarArchiveExtractor.swift +0 -370
  203. package/ios/HotUpdater/Internal/TarBrDecompressionStrategy.swift +0 -39
  204. package/ios/HotUpdater/Internal/TarGzDecompressionStrategy.swift +0 -59
  205. package/ios/HotUpdater/Internal/ZipArchiveExtractor.swift +0 -482
  206. package/ios/HotUpdater/Internal/ZipDecompressionStrategy.swift +0 -56
  207. package/lib/commonjs/notifyAppReadyAnalytics.boundary.spec.js.map +0 -1
  208. package/lib/commonjs/notifyAppReadyAnalytics.js +0 -133
  209. package/lib/commonjs/notifyAppReadyAnalytics.js.map +0 -1
  210. package/lib/commonjs/notifyAppReadyAnalytics.spec.js.map +0 -1
  211. package/lib/commonjs/notifyAppReadyAnalytics.test-utils.js.map +0 -1
  212. package/lib/commonjs/notifyAppReadyAnalytics.transport.spec.js.map +0 -1
  213. package/lib/module/notifyAppReadyAnalytics.boundary.spec.js.map +0 -1
  214. package/lib/module/notifyAppReadyAnalytics.js +0 -128
  215. package/lib/module/notifyAppReadyAnalytics.js.map +0 -1
  216. package/lib/module/notifyAppReadyAnalytics.spec.js.map +0 -1
  217. package/lib/module/notifyAppReadyAnalytics.test-utils.js.map +0 -1
  218. package/lib/module/notifyAppReadyAnalytics.transport.spec.js.map +0 -1
  219. package/lib/typescript/commonjs/notifyAppReadyAnalytics.d.ts +0 -13
  220. package/lib/typescript/commonjs/notifyAppReadyAnalytics.d.ts.map +0 -1
  221. package/lib/typescript/commonjs/notifyAppReadyAnalytics.test-utils.d.ts +0 -8
  222. package/lib/typescript/commonjs/notifyAppReadyAnalytics.test-utils.d.ts.map +0 -1
  223. package/lib/typescript/module/notifyAppReadyAnalytics.d.ts +0 -13
  224. package/lib/typescript/module/notifyAppReadyAnalytics.d.ts.map +0 -1
  225. package/lib/typescript/module/notifyAppReadyAnalytics.test-utils.d.ts +0 -8
  226. package/lib/typescript/module/notifyAppReadyAnalytics.test-utils.d.ts.map +0 -1
  227. package/src/notifyAppReadyAnalytics.ts +0 -194
@@ -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
- }
@@ -1,39 +0,0 @@
1
- import Foundation
2
-
3
- /**
4
- * Strategy for handling TAR+Brotli compressed files
5
- */
6
- class TarBrDecompressionStrategy: DecompressionStrategy {
7
- private static let MIN_FILE_SIZE: UInt64 = 10
8
-
9
- func isValid(file: String) -> Bool {
10
- guard FileManager.default.fileExists(atPath: file) else {
11
- NSLog("[TarBrStrategy] Invalid file: doesn't exist")
12
- return false
13
- }
14
-
15
- do {
16
- let attributes = try FileManager.default.attributesOfItem(atPath: file)
17
- guard let fileSize = attributes[.size] as? UInt64, fileSize >= Self.MIN_FILE_SIZE else {
18
- NSLog("[TarBrStrategy] Invalid file: too small")
19
- return false
20
- }
21
- } catch {
22
- NSLog("[TarBrStrategy] Invalid file: cannot read attributes - \(error.localizedDescription)")
23
- return false
24
- }
25
-
26
- return StreamingTarArchiveExtractor.containsTarEntries(file: file, algorithm: .brotli)
27
- }
28
-
29
- func decompress(file: String, to destination: String, progressHandler: @escaping (Double) -> Void) throws {
30
- NSLog("[TarBrStrategy] Starting extraction of \(file) to \(destination)")
31
- try StreamingTarArchiveExtractor.extractCompressedTar(
32
- file: file,
33
- to: destination,
34
- algorithm: .brotli,
35
- progressHandler: progressHandler
36
- )
37
- NSLog("[TarBrStrategy] Successfully extracted all entries")
38
- }
39
- }
@@ -1,59 +0,0 @@
1
- import Foundation
2
-
3
- /**
4
- * Strategy for handling TAR+GZIP compressed files
5
- */
6
- class TarGzDecompressionStrategy: DecompressionStrategy {
7
- private static let MIN_FILE_SIZE: UInt64 = 10
8
-
9
- func isValid(file: String) -> Bool {
10
- guard FileManager.default.fileExists(atPath: file) else {
11
- NSLog("[TarGzStrategy] Invalid file: doesn't exist")
12
- return false
13
- }
14
-
15
- do {
16
- let attributes = try FileManager.default.attributesOfItem(atPath: file)
17
- guard let fileSize = attributes[.size] as? UInt64, fileSize >= Self.MIN_FILE_SIZE else {
18
- NSLog("[TarGzStrategy] Invalid file: too small")
19
- return false
20
- }
21
- } catch {
22
- NSLog("[TarGzStrategy] Invalid file: cannot read attributes - \(error.localizedDescription)")
23
- return false
24
- }
25
-
26
- // Check GZIP magic bytes (0x1F 0x8B)
27
- guard let fileHandle = FileHandle(forReadingAtPath: file) else {
28
- NSLog("[TarGzStrategy] Invalid file: cannot open file")
29
- return false
30
- }
31
-
32
- defer {
33
- fileHandle.closeFile()
34
- }
35
-
36
- guard let header = try? ArchiveExtractionUtilities.readUpToCount(from: fileHandle, count: 2),
37
- header.count == 2 else {
38
- NSLog("[TarGzStrategy] Invalid file: cannot read header")
39
- return false
40
- }
41
-
42
- let isGzip = header[0] == 0x1F && header[1] == 0x8B
43
- if !isGzip {
44
- NSLog("[TarGzStrategy] Invalid file: wrong magic bytes (expected 0x1F 0x8B, got 0x\(String(format: "%02X", header[0])) 0x\(String(format: "%02X", header[1])))")
45
- }
46
- return isGzip
47
- }
48
-
49
- func decompress(file: String, to destination: String, progressHandler: @escaping (Double) -> Void) throws {
50
- NSLog("[TarGzStrategy] Starting extraction of \(file) to \(destination)")
51
- try StreamingTarArchiveExtractor.extractCompressedTar(
52
- file: file,
53
- to: destination,
54
- algorithm: .gzip,
55
- progressHandler: progressHandler
56
- )
57
- NSLog("[TarGzStrategy] Successfully extracted all entries")
58
- }
59
- }