@onekeyfe/react-native-bundle-update 3.0.30 → 3.0.32

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.
@@ -409,10 +409,12 @@ object BundleUpdateStoreAndroid {
409
409
  /**
410
410
  * Subtype of the most recent calculateSHA256 failure on this thread, or
411
411
  * null if the last call succeeded. Surfaces the specific reason —
412
- * FILE_NOT_FOUND / FILE_TRUNCATED / OOM / IO_<class> /
413
- * UNEXPECTED_<class> — so analytics can split the previously opaque
414
- * "Failed to calculate SHA256" bucket (mixpanel: 91.3 percent of
415
- * verifyPackage failures) into actionable categories.
412
+ * FILE_NOT_FOUND / FILE_DISAPPEARED / FILE_TRUNCATED /
413
+ * PERMISSION_DENIED / OOM / IO_<class> / UNEXPECTED_<class> — so
414
+ * analytics can split the previously opaque "Failed to calculate
415
+ * SHA256" bucket (mixpanel: 91.3 percent of verifyPackage failures)
416
+ * into actionable categories. Keep this list in sync with the
417
+ * lastSHA256Failure.set(...) call sites in calculateSHA256 below.
416
418
  *
417
419
  * Note: 0-byte files are NOT treated as a failure. They hash to the
418
420
  * well-known empty-content SHA-256 and the caller's expected/actual
@@ -1487,7 +1489,14 @@ class ReactNativeBundleUpdate : HybridReactNativeBundleUpdateSpec() {
1487
1489
  partialBytes = 0L
1488
1490
  }
1489
1491
 
1490
- val body = response.body ?: throw Exception("Empty response body")
1492
+ // Close the response before throwing on a null body — OkHttp
1493
+ // holds connection resources on the response wrapper itself,
1494
+ // and `throw` here exits the function before any byteStream()
1495
+ // consumption would close it for us.
1496
+ val body = response.body ?: run {
1497
+ response.close()
1498
+ throw Exception("Empty response body")
1499
+ }
1491
1500
  val contentLength = body.contentLength()
1492
1501
  // Total size of the whole resource (not the slice). On 206 prefer
1493
1502
  // Content-Range's "/total" tail; fall back to partial+contentLength.
@@ -1576,8 +1585,16 @@ class ReactNativeBundleUpdate : HybridReactNativeBundleUpdateSpec() {
1576
1585
  // matches the verbatim guarantees documented on
1577
1586
  // sanitizeErrorMessageForEvent.
1578
1587
  OneKeyLog.error("BundleUpdate", "downloadBundle: failed: ${e.javaClass.simpleName}: ${e.message}")
1579
- sendEvent("update/error", message = sanitizeErrorMessageForEvent(e))
1580
- throw e
1588
+ val sanitized = sanitizeErrorMessageForEvent(e)
1589
+ sendEvent("update/error", message = sanitized)
1590
+ // Rethrow with the same sanitized message so the Promise
1591
+ // rejection surfacing to JS carries no /data/user/<u>/<pkg>/
1592
+ // paths. Without this rewrap, FileNotFoundException etc.
1593
+ // would re-leak via Promise.reject's message channel even
1594
+ // though the event payload was already sanitized. Keep
1595
+ // the original exception as `cause` so OneKeyLog (and any
1596
+ // native crash reporter) still sees the full chain.
1597
+ throw Exception(sanitized, e)
1581
1598
  } finally {
1582
1599
  isDownloading.set(false)
1583
1600
  }
@@ -315,11 +315,15 @@ public class BundleUpdateStore: NSObject {
315
315
 
316
316
  /// Subtype of the most recent calculateSHA256 failure on this thread, or
317
317
  /// nil if the last call succeeded. Surfaces FILE_NOT_FOUND /
318
- /// FILE_DISAPPEARED / IO_<NSError code> / UNEXPECTED so analytics can
319
- /// split the previously opaque "Failed to calculate SHA256" bucket
320
- /// (mixpanel: 91.3% of verifyPackage failures are Android; iOS shares
321
- /// the calculator and inherits the same blind spot for its 14 ASC +
322
- /// 2 verifyPackage Promise-destroyed cases).
318
+ /// FILE_DISAPPEARED / IO_<NSError code> so analytics can split the
319
+ /// previously opaque "Failed to calculate SHA256" bucket (mixpanel:
320
+ /// 91.3% of verifyPackage failures are Android; iOS shares the
321
+ /// calculator and inherits the same blind spot for its 14 ASC + 2
322
+ /// verifyPackage Promise-destroyed cases). Keep this list in sync
323
+ /// with the setSHA256Failure(...) call sites in calculateSHA256
324
+ /// below — Android's wider taxonomy (FILE_TRUNCATED / OOM /
325
+ /// UNEXPECTED_<class>) does not apply here because the iOS reader
326
+ /// surfaces every disk error as a single `IO_<NSError code>`.
323
327
  ///
324
328
  /// Note: 0-byte files are NOT treated as a failure. They hash to the
325
329
  /// well-known empty-content SHA256 and the caller's expected/actual
@@ -359,9 +363,15 @@ public class BundleUpdateStore: NSObject {
359
363
  var context = CC_SHA256_CTX()
360
364
  CC_SHA256_Init(&context)
361
365
  var threwError: Error?
362
- // Wrap reads in try/catch via NSException bridge: FileHandle.readData
363
- // can raise on read failure (NSFileHandleOperationException);
364
- // ObjCRuntime catches those when bridged through NSObject methods.
366
+ // safeRead routes reads through FileHandle.read(upToCount:) on
367
+ // iOS 13.4+ that variant surfaces disk failures as throwing
368
+ // NSError, which we catch here as Swift `Error`. On pre-13.4
369
+ // OS versions safeRead falls back to readData(ofLength:),
370
+ // which raises NSFileHandleOperationException; Swift cannot
371
+ // catch ObjC NSExceptions via try/catch, so on those legacy
372
+ // versions a read failure will still abort the process. We
373
+ // accept that on the floor since 13.4+ has been the deployment
374
+ // target for years.
365
375
  while autoreleasepool(invoking: {
366
376
  do {
367
377
  let data = try Self.safeRead(fileHandle: fileHandle, length: 8192)
@@ -1283,9 +1293,14 @@ class ReactNativeBundleUpdate: HybridReactNativeBundleUpdateSpec {
1283
1293
  private func snapshotResumeDataForBackgrounding() {
1284
1294
  guard let session = self.urlSession else { return }
1285
1295
  // Read both isDownloading AND activeDownloadFilePath inside the
1286
- // same stateQueue.sync block so a concurrent downloadBundle entry
1287
- // (which writes filePath BEFORE flipping isDownloading) cannot
1288
- // produce a torn read where isDownloading=true but filePath=nil.
1296
+ // same stateQueue.sync block. A concurrent downloadBundle entry
1297
+ // flips isDownloading=true at the start of the Promise body but
1298
+ // only writes activeDownloadFilePath later (after the early
1299
+ // guards / version + URL validation). Without this paired read,
1300
+ // didEnterBackground could observe isDownloading=true while
1301
+ // activeDownloadFilePath is still nil from a previous run, or —
1302
+ // on the unwind via defer — see isDownloading=false alongside a
1303
+ // not-yet-cleared path.
1289
1304
  let snapshot: (Bool, String?) = self.stateQueue.sync {
1290
1305
  (self.isDownloading, self.activeDownloadFilePath)
1291
1306
  }
@@ -1542,7 +1557,19 @@ class ReactNativeBundleUpdate: HybridReactNativeBundleUpdateSpec {
1542
1557
  let nsError = error as NSError
1543
1558
  OneKeyLog.error("BundleUpdate", "downloadBundle: download failed: \(nsError.domain) \(nsError.code) \(nsError.localizedDescription)")
1544
1559
  self.sendEvent(type: "update/error", message: "\(nsError.domain) \(nsError.code)")
1545
- throw error
1560
+ // Rethrow a sanitized NSError. The original `error` is a
1561
+ // URLSession/system error whose `localizedDescription` may
1562
+ // include the temp file path (e.g. NSURLErrorFailingURLString
1563
+ // userInfo, or the ~/Library/.../CFNetworkDownload_*.tmp
1564
+ // path on cancel-with-resume) — Promise rejections surface
1565
+ // that string to JS, which would re-leak the paths the
1566
+ // event-payload sanitization just stripped. Keep the full
1567
+ // detail in OneKeyLog (above) and emit only `domain code`.
1568
+ throw NSError(
1569
+ domain: nsError.domain,
1570
+ code: nsError.code,
1571
+ userInfo: [NSLocalizedDescriptionKey: "Download failed: \(nsError.domain) \(nsError.code)"]
1572
+ )
1546
1573
 
1547
1574
  case .success(let (tempURL, response)):
1548
1575
  // Successful completion ⇒ no longer need the resume blob.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-bundle-update",
3
- "version": "3.0.30",
3
+ "version": "3.0.32",
4
4
  "description": "react-native-bundle-update",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",