@capgo/capacitor-updater 7.0.18 → 7.0.22

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.
@@ -235,6 +235,7 @@ public class CapacitorUpdater {
235
235
 
236
236
  boolean success = finishDownload(id, dest, version, sessionKey, checksum, true, isManifest);
237
237
  if (!success) {
238
+ Log.e(TAG, "Finish download failed: " + version);
238
239
  saveBundleInfo(
239
240
  id,
240
241
  new BundleInfo(id, version, BundleStatus.ERROR, new Date(System.currentTimeMillis()), "")
@@ -249,6 +250,7 @@ public class CapacitorUpdater {
249
250
  case FAILED:
250
251
  Data failedData = workInfo.getOutputData();
251
252
  String error = failedData.getString(DownloadService.ERROR);
253
+ Log.e(TAG, "Download failed: " + error + " " + workInfo.getState());
252
254
  String failedVersion = failedData.getString(DownloadService.VERSION);
253
255
  saveBundleInfo(
254
256
  id,
@@ -57,7 +57,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
57
57
  private static final String statsUrlDefault = "https://plugin.capgo.app/stats";
58
58
  private static final String channelUrlDefault = "https://plugin.capgo.app/channel_self";
59
59
 
60
- private final String PLUGIN_VERSION = "7.0.18";
60
+ private final String PLUGIN_VERSION = "7.0.22";
61
61
  private static final String DELAY_CONDITION_PREFERENCES = "";
62
62
 
63
63
  private SharedPreferences.Editor editor;
@@ -109,7 +109,6 @@ public class DownloadService extends Worker {
109
109
  return createSuccessResult(dest, version, sessionKey, checksum, false);
110
110
  }
111
111
  } catch (Exception e) {
112
- Log.e(TAG, "Error in doWork", e);
113
112
  return createFailureResult(e.getMessage());
114
113
  }
115
114
  }
@@ -219,7 +218,7 @@ public class DownloadService extends Worker {
219
218
  throw new IOException("One or more files failed to download");
220
219
  }
221
220
  } catch (Exception e) {
222
- Log.e(TAG, "Error in handleManifestDownload", e);
221
+ throw new RuntimeException(e.getLocalizedMessage());
223
222
  }
224
223
  }
225
224
 
@@ -317,10 +316,8 @@ public class DownloadService extends Worker {
317
316
  }
318
317
  }
319
318
  } catch (OutOfMemoryError e) {
320
- e.printStackTrace();
321
319
  throw new RuntimeException("low_mem_fail");
322
320
  } catch (Exception e) {
323
- e.printStackTrace();
324
321
  throw new RuntimeException(e.getLocalizedMessage());
325
322
  }
326
323
  }
@@ -334,7 +331,8 @@ public class DownloadService extends Worker {
334
331
  infoFile.createNewFile();
335
332
  tempFile.createNewFile();
336
333
  } catch (IOException e) {
337
- e.printStackTrace();
334
+ Log.e(TAG, "Error in clearDownloadData", e);
335
+ // not a fatal error, so we don't throw an exception
338
336
  }
339
337
  }
340
338
 
@@ -417,10 +415,18 @@ public class DownloadService extends Worker {
417
415
  copyFile(targetFile, cacheFile);
418
416
  } else {
419
417
  targetFile.delete();
420
- throw new IOException("Checksum verification failed for " + targetFile.getName());
418
+ throw new IOException(
419
+ "Checksum verification failed for: " +
420
+ downloadUrl +
421
+ " " +
422
+ targetFile.getName() +
423
+ " expected: " +
424
+ decryptedExpectedHash +
425
+ " calculated: " +
426
+ calculatedHash
427
+ );
421
428
  }
422
429
  } catch (Exception e) {
423
- Log.e(TAG, "Error in downloadAndVerify", e);
424
430
  throw new IOException("Error in downloadAndVerify: " + e.getMessage());
425
431
  }
426
432
  }
@@ -347,7 +347,7 @@ import UIKit
347
347
  return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("update.dat")
348
348
  }
349
349
  private var tempData = Data()
350
-
350
+
351
351
  private func verifyChecksum(file: URL, expectedHash: String) -> Bool {
352
352
  let actualHash = CryptoCipherV2.calcChecksum(filePath: file)
353
353
  return actualHash == expectedHash
@@ -446,9 +446,9 @@ import UIKit
446
446
  let statusCode = response.response?.statusCode ?? 200
447
447
  if statusCode < 200 || statusCode >= 300 {
448
448
  if let stringData = String(data: data, encoding: .utf8) {
449
- throw NSError(domain: "StatusCodeError", code: 2, userInfo: [NSLocalizedDescriptionKey: "Failed to fetch. Status code (\(statusCode)) invalid. Data: \(stringData)"])
449
+ throw NSError(domain: "StatusCodeError", code: 2, userInfo: [NSLocalizedDescriptionKey: "Failed to fetch. Status code (\(statusCode)) invalid. Data: \(stringData) for file \(fileName) at url \(downloadUrl)"])
450
450
  } else {
451
- throw NSError(domain: "StatusCodeError", code: 2, userInfo: [NSLocalizedDescriptionKey: "Failed to fetch. Status code (\(statusCode)) invalid"])
451
+ throw NSError(domain: "StatusCodeError", code: 2, userInfo: [NSLocalizedDescriptionKey: "Failed to fetch. Status code (\(statusCode)) invalid for file \(fileName) at url \(downloadUrl)"])
452
452
  }
453
453
  }
454
454
 
@@ -470,7 +470,7 @@ import UIKit
470
470
 
471
471
  // Decompress the Brotli data
472
472
  guard let decompressedData = self.decompressBrotli(data: finalData, fileName: fileName) else {
473
- throw NSError(domain: "BrotliDecompressionError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Failed to decompress Brotli data"])
473
+ throw NSError(domain: "BrotliDecompressionError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Failed to decompress Brotli data for file \(fileName) at url \(downloadUrl)"])
474
474
  }
475
475
  finalData = decompressedData
476
476
 
@@ -479,7 +479,7 @@ import UIKit
479
479
  // assume that calcChecksum != null
480
480
  let calculatedChecksum = CryptoCipherV2.calcChecksum(filePath: destFilePath)
481
481
  if calculatedChecksum != fileHash {
482
- throw NSError(domain: "ChecksumError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Computed checksum is not equal to required checksum (\(calculatedChecksum) != \(fileHash))"])
482
+ throw NSError(domain: "ChecksumError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Computed checksum is not equal to required checksum (\(calculatedChecksum) != \(fileHash)) for file \(fileName) at url \(downloadUrl)"])
483
483
  }
484
484
  }
485
485
 
@@ -491,10 +491,10 @@ import UIKit
491
491
  print("\(CapacitorUpdater.TAG) downloadManifest \(id) \(fileName) downloaded, decompressed\(!self.publicKey.isEmpty && !sessionKey.isEmpty ? ", decrypted" : ""), and cached")
492
492
  } catch {
493
493
  downloadError = error
494
- print("\(CapacitorUpdater.TAG) downloadManifest \(id) \(fileName) error: \(error)")
494
+ NSLog("\(CapacitorUpdater.TAG) downloadManifest \(id) \(fileName) error: \(error.localizedDescription)")
495
495
  }
496
496
  case .failure(let error):
497
- print("\(CapacitorUpdater.TAG) downloadManifest \(id) \(fileName) download error: \(error). Debug response: \(response.debugDescription).")
497
+ NSLog("\(CapacitorUpdater.TAG) downloadManifest \(id) \(fileName) download error: \(error.localizedDescription). Debug response: \(response.debugDescription).")
498
498
  }
499
499
  }
500
500
  }
@@ -518,14 +518,43 @@ import UIKit
518
518
  }
519
519
 
520
520
  private func decompressBrotli(data: Data, fileName: String) -> Data? {
521
+ // Handle empty files
522
+ if data.count == 0 {
523
+ return data
524
+ }
525
+
526
+ // Handle the special EMPTY_BROTLI_STREAM case
527
+ if data.count == 3 && data[0] == 0x1B && data[1] == 0x00 && data[2] == 0x06 {
528
+ return Data()
529
+ }
530
+
531
+ // For small files, check if it's a minimal Brotli wrapper
532
+ if data.count > 3 {
533
+ let maxBytes = min(32, data.count)
534
+ let hexDump = data.prefix(maxBytes).map { String(format: "%02x", $0) }.joined(separator: " ")
535
+ // Handle our minimal wrapper pattern
536
+ if data[0] == 0x1B && data[1] == 0x00 && data[2] == 0x06 && data.last == 0x03 {
537
+ let range = data.index(data.startIndex, offsetBy: 3)..<data.index(data.endIndex, offsetBy: -1)
538
+ return data[range]
539
+ }
540
+
541
+ // Handle brotli.compress minimal wrapper (quality 0)
542
+ if data[0] == 0x0b && data[1] == 0x02 && data[2] == 0x80 && data.last == 0x03 {
543
+ let range = data.index(data.startIndex, offsetBy: 3)..<data.index(data.endIndex, offsetBy: -1)
544
+ return data[range]
545
+ }
546
+ }
547
+
548
+ // For all other cases, try standard decompression
521
549
  let outputBufferSize = 65536
522
550
  var outputBuffer = [UInt8](repeating: 0, count: outputBufferSize)
523
551
  var decompressedData = Data()
524
552
 
525
553
  let streamPointer = UnsafeMutablePointer<compression_stream>.allocate(capacity: 1)
526
554
  var status = compression_stream_init(streamPointer, COMPRESSION_STREAM_DECODE, COMPRESSION_BROTLI)
555
+
527
556
  guard status != COMPRESSION_STATUS_ERROR else {
528
- print("\(CapacitorUpdater.TAG) Unable to initialize the decompression stream. \(fileName)")
557
+ print("\(CapacitorUpdater.TAG) Error: Failed to initialize Brotli stream for \(fileName). Status: \(status)")
529
558
  return nil
530
559
  }
531
560
 
@@ -547,7 +576,7 @@ import UIKit
547
576
  if let baseAddress = rawBufferPointer.baseAddress {
548
577
  streamPointer.pointee.src_ptr = baseAddress.assumingMemoryBound(to: UInt8.self)
549
578
  } else {
550
- print("\(CapacitorUpdater.TAG) Error: Unable to get base address of input data. \(fileName)")
579
+ print("\(CapacitorUpdater.TAG) Error: Failed to get base address for \(fileName)")
551
580
  status = COMPRESSION_STATUS_ERROR
552
581
  return
553
582
  }
@@ -555,6 +584,9 @@ import UIKit
555
584
  }
556
585
 
557
586
  if status == COMPRESSION_STATUS_ERROR {
587
+ let maxBytes = min(32, data.count)
588
+ let hexDump = data.prefix(maxBytes).map { String(format: "%02x", $0) }.joined(separator: " ")
589
+ print("\(CapacitorUpdater.TAG) Error: Brotli decompression failed for \(fileName). First \(maxBytes) bytes: \(hexDump)")
558
590
  break
559
591
  }
560
592
 
@@ -568,15 +600,19 @@ import UIKit
568
600
  if status == COMPRESSION_STATUS_END {
569
601
  break
570
602
  } else if status == COMPRESSION_STATUS_ERROR {
571
- print("\(CapacitorUpdater.TAG) Error during Brotli decompression. \(fileName)")
572
- // Try to decode as text if mostly ASCII
603
+ print("\(CapacitorUpdater.TAG) Error: Brotli process failed for \(fileName). Status: \(status)")
573
604
  if let text = String(data: data, encoding: .utf8) {
574
605
  let asciiCount = text.unicodeScalars.filter { $0.isASCII }.count
575
606
  let totalCount = text.unicodeScalars.count
576
607
  if totalCount > 0 && Double(asciiCount) / Double(totalCount) >= 0.8 {
577
- print("\(CapacitorUpdater.TAG) Compressed data as text: \(text)")
608
+ print("\(CapacitorUpdater.TAG) Error: Input appears to be plain text: \(text)")
578
609
  }
579
610
  }
611
+
612
+ let maxBytes = min(32, data.count)
613
+ let hexDump = data.prefix(maxBytes).map { String(format: "%02x", $0) }.joined(separator: " ")
614
+ print("\(CapacitorUpdater.TAG) Error: Raw data (\(fileName)): \(hexDump)")
615
+
580
616
  return nil
581
617
  }
582
618
 
@@ -586,6 +622,7 @@ import UIKit
586
622
  }
587
623
 
588
624
  if input.count == 0 {
625
+ print("\(CapacitorUpdater.TAG) Error: Zero input size for \(fileName)")
589
626
  break
590
627
  }
591
628
  }
@@ -45,7 +45,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
45
45
  CAPPluginMethod(name: "getNextBundle", returnType: CAPPluginReturnPromise)
46
46
  ]
47
47
  public var implementation = CapacitorUpdater()
48
- private let PLUGIN_VERSION: String = "7.0.18"
48
+ private let PLUGIN_VERSION: String = "7.0.22"
49
49
  static let updateUrlDefault = "https://plugin.capgo.app/updates"
50
50
  static let statsUrlDefault = "https://plugin.capgo.app/stats"
51
51
  static let channelUrlDefault = "https://plugin.capgo.app/channel_self"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-updater",
3
- "version": "7.0.18",
3
+ "version": "7.0.22",
4
4
  "license": "MPL-2.0",
5
5
  "description": "Live update for capacitor apps",
6
6
  "main": "dist/plugin.cjs.js",