@capgo/capacitor-updater 8.51.25 → 8.52.1

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.
@@ -0,0 +1,247 @@
1
+ /*
2
+ * This Source Code Form is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
+ */
6
+
7
+ import Compression
8
+ import Foundation
9
+
10
+ enum ZipError: Error, Equatable {
11
+ case unreadableArchive
12
+ case missingEndOfCentralDirectory
13
+ case invalidCentralDirectory
14
+ case invalidLocalFileHeader
15
+ case truncatedEntryData
16
+ case corruptedEntryData
17
+ case encryptedEntry
18
+ case unsupportedCompressionMethod(UInt16)
19
+ case invalidBufferSize
20
+ case entryTooLarge
21
+ }
22
+
23
+ enum ZipEntryType {
24
+ case file
25
+ case directory
26
+ case symlink
27
+ }
28
+
29
+ struct ZipEntry {
30
+ let path: String
31
+ let type: ZipEntryType
32
+ let generalPurposeFlags: UInt16
33
+ let compressionMethod: UInt16
34
+ let compressedSize: UInt64
35
+ let uncompressedSize: UInt64
36
+ let localHeaderOffset: UInt64
37
+
38
+ var isEncrypted: Bool {
39
+ return generalPurposeFlags & 0x0001 != 0
40
+ }
41
+ }
42
+
43
+ /// Small read-only ZIP reader used to extract downloaded bundles.
44
+ ///
45
+ /// Supports stored (0) and deflate (8) entries, ZIP64 archives, and streams entry contents in
46
+ /// `bufferSize` chunks so large files are never fully loaded in memory. Encrypted entries and other
47
+ /// compression methods are rejected. Every offset and size read from the archive is bounds-checked, so a
48
+ /// corrupt archive throws instead of crashing.
49
+ ///
50
+ /// This reader does not validate entry paths. Callers must resolve `ZipEntry.path` against the destination
51
+ /// directory (see `CapgoUpdater.resolvePathInsideDirectory`) before writing anything.
52
+ final class ZipArchiveReader {
53
+ static let compressionMethodStored: UInt16 = 0
54
+ static let compressionMethodDeflate: UInt16 = 8
55
+ private static let localFileHeaderSignature: UInt32 = 0x0403_4b50
56
+ private static let localFileHeaderSize = 30
57
+
58
+ let entries: [ZipEntry]
59
+ private let fileHandle: FileHandle
60
+ private let fileSize: UInt64
61
+
62
+ init(url: URL) throws {
63
+ let handle: FileHandle
64
+ do {
65
+ handle = try FileHandle(forReadingFrom: url)
66
+ } catch {
67
+ throw ZipError.unreadableArchive
68
+ }
69
+ do {
70
+ let size = try handle.seekToEnd()
71
+ self.fileHandle = handle
72
+ self.fileSize = size
73
+ self.entries = try ZipCentralDirectory.readEntries(handle: handle, fileSize: size)
74
+ } catch {
75
+ try? handle.close()
76
+ throw error
77
+ }
78
+ }
79
+
80
+ deinit {
81
+ try? fileHandle.close()
82
+ }
83
+
84
+ /// Streams the uncompressed contents of `entry` to `consumer` in chunks of at most `bufferSize` bytes.
85
+ /// Directory entries produce no data.
86
+ func extract(_ entry: ZipEntry, bufferSize: Int, consumer: (Data) throws -> Void) throws {
87
+ guard bufferSize > 0 else {
88
+ throw ZipError.invalidBufferSize
89
+ }
90
+ if entry.type == .directory {
91
+ return
92
+ }
93
+ if entry.isEncrypted {
94
+ throw ZipError.encryptedEntry
95
+ }
96
+
97
+ let header = try ZipBytes.readChunk(handle: fileHandle, fileSize: fileSize, at: entry.localHeaderOffset, count: Self.localFileHeaderSize)
98
+ guard ZipBytes.uint32(header, 0) == Self.localFileHeaderSignature else {
99
+ throw ZipError.invalidLocalFileHeader
100
+ }
101
+ let localFlags = ZipBytes.uint16(header, 6)
102
+ let localMethod = ZipBytes.uint16(header, 8)
103
+ let nameLength = UInt64(ZipBytes.uint16(header, 26))
104
+ let extraLength = UInt64(ZipBytes.uint16(header, 28))
105
+ if localFlags & 0x0001 != 0 {
106
+ throw ZipError.encryptedEntry
107
+ }
108
+
109
+ let headerLength = UInt64(Self.localFileHeaderSize) + nameLength + extraLength
110
+ let (dataOffset, overflow) = entry.localHeaderOffset.addingReportingOverflow(headerLength)
111
+ guard !overflow, dataOffset <= fileSize else {
112
+ throw ZipError.invalidLocalFileHeader
113
+ }
114
+
115
+ // Like the previous ZIPFoundation-based extraction, the local header decides the compression method
116
+ // while sizes come from the central directory.
117
+ switch localMethod {
118
+ case Self.compressionMethodStored:
119
+ // A stored entry's payload is its content; a size mismatch would read past it into the next records.
120
+ guard entry.compressedSize == entry.uncompressedSize else {
121
+ throw ZipError.corruptedEntryData
122
+ }
123
+ try readStored(from: dataOffset, size: entry.uncompressedSize, bufferSize: bufferSize, consumer: consumer)
124
+ case Self.compressionMethodDeflate:
125
+ if entry.compressedSize == 0 && entry.uncompressedSize == 0 {
126
+ // Some writers store empty files as deflate with no payload at all.
127
+ return
128
+ }
129
+ try readDeflated(
130
+ from: dataOffset,
131
+ compressedSize: entry.compressedSize,
132
+ expectedSize: entry.uncompressedSize,
133
+ bufferSize: bufferSize,
134
+ consumer: consumer
135
+ )
136
+ default:
137
+ throw ZipError.unsupportedCompressionMethod(localMethod)
138
+ }
139
+ }
140
+
141
+ /// Reads the full contents of a small entry (e.g. a symlink target), refusing entries larger than `maxBytes`.
142
+ func readSmallEntry(_ entry: ZipEntry, maxBytes: Int, bufferSize: Int) throws -> Data {
143
+ var result = Data()
144
+ try extract(entry, bufferSize: bufferSize) { chunk in
145
+ guard result.count + chunk.count <= maxBytes else {
146
+ throw ZipError.entryTooLarge
147
+ }
148
+ result.append(chunk)
149
+ }
150
+ return result
151
+ }
152
+
153
+ private func readStored(from offset: UInt64, size: UInt64, bufferSize: Int, consumer: (Data) throws -> Void) throws {
154
+ guard size <= fileSize, offset <= fileSize - size else {
155
+ throw ZipError.truncatedEntryData
156
+ }
157
+ try fileHandle.seek(toOffset: offset)
158
+ var remaining = size
159
+ while remaining > 0 {
160
+ let chunkSize = Int(min(UInt64(bufferSize), remaining))
161
+ try autoreleasepool {
162
+ guard let chunk = try fileHandle.read(upToCount: chunkSize), chunk.count == chunkSize else {
163
+ throw ZipError.truncatedEntryData
164
+ }
165
+ try consumer(chunk)
166
+ }
167
+ remaining -= UInt64(chunkSize)
168
+ }
169
+ }
170
+
171
+ /// Inflates an entry, rejecting output that differs from the central directory's uncompressed size.
172
+ /// This also bounds decompression bombs to the declared size.
173
+ private func readDeflated(
174
+ from offset: UInt64,
175
+ compressedSize: UInt64,
176
+ expectedSize: UInt64,
177
+ bufferSize: Int,
178
+ consumer: (Data) throws -> Void
179
+ ) throws {
180
+ guard compressedSize <= fileSize, offset <= fileSize - compressedSize else {
181
+ throw ZipError.truncatedEntryData
182
+ }
183
+ try fileHandle.seek(toOffset: offset)
184
+
185
+ let stream = UnsafeMutablePointer<compression_stream>.allocate(capacity: 1)
186
+ defer { stream.deallocate() }
187
+ // COMPRESSION_ZLIB decodes raw DEFLATE (RFC 1951), which is what ZIP method 8 stores.
188
+ guard compression_stream_init(stream, COMPRESSION_STREAM_DECODE, COMPRESSION_ZLIB) == COMPRESSION_STATUS_OK else {
189
+ throw ZipError.corruptedEntryData
190
+ }
191
+ defer { compression_stream_destroy(stream) }
192
+
193
+ let sourceBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
194
+ defer { sourceBuffer.deallocate() }
195
+ let destinationBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
196
+ defer { destinationBuffer.deallocate() }
197
+
198
+ var remaining = compressedSize
199
+ stream.pointee.src_ptr = UnsafePointer(sourceBuffer)
200
+ stream.pointee.src_size = 0
201
+
202
+ var producedTotal: UInt64 = 0
203
+ var finished = false
204
+ while !finished {
205
+ try autoreleasepool {
206
+ if stream.pointee.src_size == 0 && remaining > 0 {
207
+ let chunkSize = Int(min(UInt64(bufferSize), remaining))
208
+ guard let chunk = try fileHandle.read(upToCount: chunkSize), chunk.count == chunkSize else {
209
+ throw ZipError.truncatedEntryData
210
+ }
211
+ chunk.copyBytes(to: sourceBuffer, count: chunkSize)
212
+ stream.pointee.src_ptr = UnsafePointer(sourceBuffer)
213
+ stream.pointee.src_size = chunkSize
214
+ remaining -= UInt64(chunkSize)
215
+ }
216
+
217
+ stream.pointee.dst_ptr = destinationBuffer
218
+ stream.pointee.dst_size = bufferSize
219
+ let flags = remaining == 0 ? Int32(COMPRESSION_STREAM_FINALIZE.rawValue) : 0
220
+ let status = compression_stream_process(stream, flags)
221
+ let produced = bufferSize - stream.pointee.dst_size
222
+ if produced > 0 {
223
+ producedTotal += UInt64(produced)
224
+ guard producedTotal <= expectedSize else {
225
+ throw ZipError.corruptedEntryData
226
+ }
227
+ try consumer(Data(bytes: destinationBuffer, count: produced))
228
+ }
229
+
230
+ switch status {
231
+ case COMPRESSION_STATUS_END:
232
+ guard producedTotal == expectedSize else {
233
+ throw ZipError.corruptedEntryData
234
+ }
235
+ finished = true
236
+ case COMPRESSION_STATUS_OK:
237
+ // All input consumed and no output produced: the deflate stream is truncated.
238
+ if remaining == 0 && stream.pointee.src_size == 0 && produced == 0 {
239
+ throw ZipError.truncatedEntryData
240
+ }
241
+ default:
242
+ throw ZipError.corruptedEntryData
243
+ }
244
+ }
245
+ }
246
+ }
247
+ }
@@ -0,0 +1,280 @@
1
+ /*
2
+ * This Source Code Form is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
+ */
6
+
7
+ import Foundation
8
+
9
+ /// Parses the ZIP end of central directory (including ZIP64) and the central directory entries.
10
+ /// Every offset and length is bounds-checked; malformed input throws `ZipError`.
11
+ enum ZipCentralDirectory {
12
+ private static let eocdSignature: UInt32 = 0x0605_4b50
13
+ private static let zip64EocdSignature: UInt32 = 0x0606_4b50
14
+ private static let zip64LocatorSignature: UInt32 = 0x0706_4b50
15
+ private static let entrySignature: UInt32 = 0x0201_4b50
16
+ private static let eocdSize = 22
17
+ private static let zip64LocatorSize = 20
18
+ private static let zip64EocdMinimumSize = 56
19
+ private static let entryMinimumSize = 46
20
+ private static let maxCommentLength = 0xFFFF
21
+ private static let zip64ExtraFieldId: UInt16 = 0x0001
22
+
23
+ private struct EntrySizes {
24
+ var uncompressed: UInt64
25
+ var compressed: UInt64
26
+ var localHeaderOffset: UInt64
27
+ }
28
+
29
+ private struct Location {
30
+ var totalEntries: UInt64
31
+ var size: UInt64
32
+ var offset: UInt64
33
+ /// The central directory must end before this offset (the EOCD or ZIP64 EOCD record).
34
+ var end: UInt64
35
+ }
36
+
37
+ static func readEntries(handle: FileHandle, fileSize: UInt64) throws -> [ZipEntry] {
38
+ let location = try locateCentralDirectory(handle: handle, fileSize: fileSize)
39
+ guard location.offset <= location.end, location.size <= location.end - location.offset else {
40
+ throw ZipError.invalidCentralDirectory
41
+ }
42
+ // Every central directory entry takes at least 46 bytes, which bounds the entry count.
43
+ guard location.totalEntries <= location.size / UInt64(entryMinimumSize), location.size <= UInt64(Int.max) else {
44
+ throw ZipError.invalidCentralDirectory
45
+ }
46
+
47
+ let data = try ZipBytes.readChunk(handle: handle, fileSize: fileSize, at: location.offset, count: Int(location.size))
48
+ var entries: [ZipEntry] = []
49
+ entries.reserveCapacity(Int(location.totalEntries))
50
+ var cursor = 0
51
+ for _ in 0..<location.totalEntries {
52
+ let (entry, next) = try parseEntry(data, at: cursor)
53
+ entries.append(entry)
54
+ cursor = next
55
+ }
56
+ return entries
57
+ }
58
+
59
+ private static func locateCentralDirectory(handle: FileHandle, fileSize: UInt64) throws -> Location {
60
+ guard fileSize >= UInt64(eocdSize) else {
61
+ throw ZipError.missingEndOfCentralDirectory
62
+ }
63
+
64
+ // The EOCD record is at the end of the file, followed by an optional comment of up to 65535 bytes.
65
+ let tailLength = min(fileSize, UInt64(eocdSize + maxCommentLength))
66
+ let tailOffset = fileSize - tailLength
67
+ let tail = try ZipBytes.readChunk(handle: handle, fileSize: fileSize, at: tailOffset, count: Int(tailLength))
68
+
69
+ var eocdIndex: Int?
70
+ var index = tail.count - eocdSize
71
+ while index >= 0 {
72
+ if ZipBytes.uint32(tail, index) == eocdSignature,
73
+ index + eocdSize + Int(ZipBytes.uint16(tail, index + 20)) <= tail.count {
74
+ eocdIndex = index
75
+ break
76
+ }
77
+ index -= 1
78
+ }
79
+ guard let eocdIndex else {
80
+ throw ZipError.missingEndOfCentralDirectory
81
+ }
82
+
83
+ let eocdOffset = tailOffset + UInt64(eocdIndex)
84
+ let location = Location(
85
+ totalEntries: UInt64(ZipBytes.uint16(tail, eocdIndex + 10)),
86
+ size: UInt64(ZipBytes.uint32(tail, eocdIndex + 12)),
87
+ offset: UInt64(ZipBytes.uint32(tail, eocdIndex + 16)),
88
+ end: eocdOffset
89
+ )
90
+ let needsZip64 = location.totalEntries == 0xFFFF || location.size == 0xFFFF_FFFF || location.offset == 0xFFFF_FFFF
91
+ if let zip64Location = try readZip64Location(handle: handle, fileSize: fileSize, eocdOffset: eocdOffset) {
92
+ return zip64Location
93
+ }
94
+ if needsZip64 {
95
+ throw ZipError.invalidCentralDirectory
96
+ }
97
+ return location
98
+ }
99
+
100
+ private static func readZip64Location(handle: FileHandle, fileSize: UInt64, eocdOffset: UInt64) throws -> Location? {
101
+ guard eocdOffset >= UInt64(zip64LocatorSize) else {
102
+ return nil
103
+ }
104
+ let locatorOffset = eocdOffset - UInt64(zip64LocatorSize)
105
+ let locator = try ZipBytes.readChunk(handle: handle, fileSize: fileSize, at: locatorOffset, count: zip64LocatorSize)
106
+ guard ZipBytes.uint32(locator, 0) == zip64LocatorSignature else {
107
+ return nil
108
+ }
109
+ let recordOffset = ZipBytes.uint64(locator, 8)
110
+ guard recordOffset <= locatorOffset, locatorOffset - recordOffset >= UInt64(zip64EocdMinimumSize) else {
111
+ throw ZipError.invalidCentralDirectory
112
+ }
113
+ let record = try ZipBytes.readChunk(handle: handle, fileSize: fileSize, at: recordOffset, count: zip64EocdMinimumSize)
114
+ guard ZipBytes.uint32(record, 0) == zip64EocdSignature else {
115
+ throw ZipError.invalidCentralDirectory
116
+ }
117
+ return Location(
118
+ totalEntries: ZipBytes.uint64(record, 32),
119
+ size: ZipBytes.uint64(record, 40),
120
+ offset: ZipBytes.uint64(record, 48),
121
+ end: recordOffset
122
+ )
123
+ }
124
+
125
+ private static func parseEntry(_ data: Data, at offset: Int) throws -> (ZipEntry, Int) {
126
+ guard offset <= data.count - entryMinimumSize, ZipBytes.uint32(data, offset) == entrySignature else {
127
+ throw ZipError.invalidCentralDirectory
128
+ }
129
+ let versionMadeBy = ZipBytes.uint16(data, offset + 4)
130
+ let flags = ZipBytes.uint16(data, offset + 8)
131
+ let nameLength = Int(ZipBytes.uint16(data, offset + 28))
132
+ let extraLength = Int(ZipBytes.uint16(data, offset + 30))
133
+ let commentLength = Int(ZipBytes.uint16(data, offset + 32))
134
+ let externalAttributes = ZipBytes.uint32(data, offset + 38)
135
+
136
+ let nameStart = offset + entryMinimumSize
137
+ let extraStart = nameStart + nameLength
138
+ let next = extraStart + extraLength + commentLength
139
+ guard next <= data.count else {
140
+ throw ZipError.invalidCentralDirectory
141
+ }
142
+
143
+ var sizes = EntrySizes(
144
+ uncompressed: UInt64(ZipBytes.uint32(data, offset + 24)),
145
+ compressed: UInt64(ZipBytes.uint32(data, offset + 20)),
146
+ localHeaderOffset: UInt64(ZipBytes.uint32(data, offset + 42))
147
+ )
148
+ if sizes.uncompressed == 0xFFFF_FFFF || sizes.compressed == 0xFFFF_FFFF || sizes.localHeaderOffset == 0xFFFF_FFFF {
149
+ sizes = try applyZip64Extra(data, extraRange: extraStart..<(extraStart + extraLength), sizes: sizes)
150
+ }
151
+
152
+ // Same path decoding as ZIPFoundation: UTF-8 when general purpose bit 11 is set, CP437 otherwise.
153
+ let nameData = data.subdata(in: (data.startIndex + nameStart)..<(data.startIndex + extraStart))
154
+ let encoding: String.Encoding = flags & (1 << 11) != 0 ? .utf8 : codePage437
155
+ let path = String(data: nameData, encoding: encoding) ?? ""
156
+
157
+ let entry = ZipEntry(
158
+ path: path,
159
+ type: entryType(path: path, versionMadeBy: versionMadeBy, externalAttributes: externalAttributes),
160
+ generalPurposeFlags: flags,
161
+ compressionMethod: ZipBytes.uint16(data, offset + 10),
162
+ compressedSize: sizes.compressed,
163
+ uncompressedSize: sizes.uncompressed,
164
+ localHeaderOffset: sizes.localHeaderOffset
165
+ )
166
+ return (entry, next)
167
+ }
168
+
169
+ /// ZIP64 extended information: values are present only for fields saturated in the fixed record, in this order.
170
+ private static func applyZip64Extra(
171
+ _ data: Data,
172
+ extraRange: Range<Int>,
173
+ sizes: EntrySizes
174
+ ) throws -> EntrySizes {
175
+ var cursor = extraRange.lowerBound
176
+ while cursor + 4 <= extraRange.upperBound {
177
+ let headerId = ZipBytes.uint16(data, cursor)
178
+ let fieldStart = cursor + 4
179
+ let fieldEnd = fieldStart + Int(ZipBytes.uint16(data, cursor + 2))
180
+ guard fieldEnd <= extraRange.upperBound else {
181
+ throw ZipError.invalidCentralDirectory
182
+ }
183
+ if headerId != zip64ExtraFieldId {
184
+ cursor = fieldEnd
185
+ continue
186
+ }
187
+
188
+ var result = sizes
189
+ var valueCursor = fieldStart
190
+ func nextValue() throws -> UInt64 {
191
+ guard valueCursor + 8 <= fieldEnd else {
192
+ throw ZipError.invalidCentralDirectory
193
+ }
194
+ defer { valueCursor += 8 }
195
+ return ZipBytes.uint64(data, valueCursor)
196
+ }
197
+ if result.uncompressed == 0xFFFF_FFFF {
198
+ result.uncompressed = try nextValue()
199
+ }
200
+ if result.compressed == 0xFFFF_FFFF {
201
+ result.compressed = try nextValue()
202
+ }
203
+ if result.localHeaderOffset == 0xFFFF_FFFF {
204
+ result.localHeaderOffset = try nextValue()
205
+ }
206
+ return result
207
+ }
208
+ throw ZipError.invalidCentralDirectory
209
+ }
210
+
211
+ /// Mirrors ZIPFoundation's `Entry.type` so the same entries are treated as files, directories or symlinks.
212
+ static func entryType(path: String, versionMadeBy: UInt16, externalAttributes: UInt32) -> ZipEntryType {
213
+ let hostSystem = versionMadeBy >> 8
214
+ let hasDirectorySuffix = path.hasSuffix("/")
215
+ switch hostSystem {
216
+ case 3, 19: // UNIX, macOS
217
+ let mode = mode_t(UInt16(truncatingIfNeeded: externalAttributes >> 16)) & S_IFMT
218
+ switch mode {
219
+ case S_IFREG:
220
+ return .file
221
+ case S_IFDIR:
222
+ return .directory
223
+ case S_IFLNK:
224
+ return .symlink
225
+ default:
226
+ return hasDirectorySuffix ? .directory : .file
227
+ }
228
+ case 0: // MS-DOS
229
+ return hasDirectorySuffix || (externalAttributes >> 4) == 0x01 ? .directory : .file
230
+ default:
231
+ return hasDirectorySuffix ? .directory : .file
232
+ }
233
+ }
234
+
235
+ private static let codePage437: String.Encoding = {
236
+ let encoding = CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.dosLatinUS.rawValue))
237
+ return String.Encoding(rawValue: encoding)
238
+ }()
239
+ }
240
+
241
+ /// Little-endian readers and bounded file reads for ZIP structures.
242
+ enum ZipBytes {
243
+ static func readChunk(handle: FileHandle, fileSize: UInt64, at offset: UInt64, count: Int) throws -> Data {
244
+ guard count >= 0, UInt64(count) <= fileSize, offset <= fileSize - UInt64(count) else {
245
+ throw ZipError.invalidCentralDirectory
246
+ }
247
+ if count == 0 {
248
+ return Data()
249
+ }
250
+ try handle.seek(toOffset: offset)
251
+ guard let data = try handle.read(upToCount: count), data.count == count else {
252
+ throw ZipError.unreadableArchive
253
+ }
254
+ return data
255
+ }
256
+
257
+ // Callers bounds-check `offset` against `data.count` before reading.
258
+ static func uint16(_ data: Data, _ offset: Int) -> UInt16 {
259
+ let base = data.startIndex + offset
260
+ return UInt16(data[base]) | (UInt16(data[base + 1]) << 8)
261
+ }
262
+
263
+ static func uint32(_ data: Data, _ offset: Int) -> UInt32 {
264
+ let base = data.startIndex + offset
265
+ var value: UInt32 = 0
266
+ for byteIndex in 0..<4 {
267
+ value |= UInt32(data[base + byteIndex]) << (8 * UInt32(byteIndex))
268
+ }
269
+ return value
270
+ }
271
+
272
+ static func uint64(_ data: Data, _ offset: Int) -> UInt64 {
273
+ let base = data.startIndex + offset
274
+ var value: UInt64 = 0
275
+ for byteIndex in 0..<8 {
276
+ value |= UInt64(data[base + byteIndex]) << (8 * UInt64(byteIndex))
277
+ }
278
+ return value
279
+ }
280
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-updater",
3
- "version": "8.51.25",
3
+ "version": "8.52.1",
4
4
  "license": "MPL-2.0",
5
5
  "description": "Live update for capacitor apps",
6
6
  "main": "dist/plugin.cjs.js",