@appsonair.ir/react-native 1.0.6 → 1.0.7
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.
- package/lib/sync.js +114 -7
- package/package.json +1 -1
package/lib/sync.js
CHANGED
|
@@ -456,6 +456,98 @@ function getApkFilePath(label) {
|
|
|
456
456
|
const dirs = ReactNativeBlobUtil.fs.dirs;
|
|
457
457
|
return `${dirs.DocumentDir}/ota-updates/${label}.apk`;
|
|
458
458
|
}
|
|
459
|
+
function getApkZipPath(label) {
|
|
460
|
+
const ReactNativeBlobUtil = require('react-native-blob-util').default;
|
|
461
|
+
const dirs = ReactNativeBlobUtil.fs.dirs;
|
|
462
|
+
return `${dirs.DocumentDir}/ota-updates/${label}-apk.zip`;
|
|
463
|
+
}
|
|
464
|
+
function getApkExtractDir(label) {
|
|
465
|
+
const ReactNativeBlobUtil = require('react-native-blob-util').default;
|
|
466
|
+
const dirs = ReactNativeBlobUtil.fs.dirs;
|
|
467
|
+
return `${dirs.DocumentDir}/ota-updates/${label}-apk`;
|
|
468
|
+
}
|
|
469
|
+
function apkAbiFromName(fileName) {
|
|
470
|
+
const lower = fileName.replace(/^.*[/\\]/, '').toLowerCase();
|
|
471
|
+
if (lower.includes('arm64-v8a') || lower.includes('arm64') || lower.includes('-v8a')) {
|
|
472
|
+
return 'arm64-v8a';
|
|
473
|
+
}
|
|
474
|
+
if (lower.includes('armeabi-v7a') ||
|
|
475
|
+
lower.includes('armeabi') ||
|
|
476
|
+
lower.includes('-v7a')) {
|
|
477
|
+
return 'armeabi-v7a';
|
|
478
|
+
}
|
|
479
|
+
if (lower.includes('x86_64')) {
|
|
480
|
+
return 'x86_64';
|
|
481
|
+
}
|
|
482
|
+
if (lower.includes('universal')) {
|
|
483
|
+
return 'universal';
|
|
484
|
+
}
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
async function listFilesRecursive(dir) {
|
|
488
|
+
const ReactNativeBlobUtil = require('react-native-blob-util').default;
|
|
489
|
+
const entries = await ReactNativeBlobUtil.fs.ls(dir);
|
|
490
|
+
const out = [];
|
|
491
|
+
for (const entry of entries) {
|
|
492
|
+
const path = `${dir}/${entry}`;
|
|
493
|
+
try {
|
|
494
|
+
const entryStat = await ReactNativeBlobUtil.fs.stat(path);
|
|
495
|
+
if (entryStat.type === 'directory') {
|
|
496
|
+
out.push(...(await listFilesRecursive(path)));
|
|
497
|
+
}
|
|
498
|
+
else {
|
|
499
|
+
out.push(path);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
catch {
|
|
503
|
+
out.push(path);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
return out;
|
|
507
|
+
}
|
|
508
|
+
async function pickExtractedApk(dir, deviceAbi) {
|
|
509
|
+
const files = await listFilesRecursive(dir);
|
|
510
|
+
const apks = files.filter((path) => path.toLowerCase().endsWith('.apk'));
|
|
511
|
+
if (apks.length === 0) {
|
|
512
|
+
throw new Error(`No APK found after unzipping ${dir}`);
|
|
513
|
+
}
|
|
514
|
+
if (apks.length === 1) {
|
|
515
|
+
return apks[0];
|
|
516
|
+
}
|
|
517
|
+
const wanted = (deviceAbi ?? (await getDeviceAbi()) ?? '').toLowerCase();
|
|
518
|
+
const scored = apks.map((path) => {
|
|
519
|
+
const abi = apkAbiFromName(path);
|
|
520
|
+
let score = 0;
|
|
521
|
+
if (abi && wanted && abi === wanted) {
|
|
522
|
+
score = 3;
|
|
523
|
+
}
|
|
524
|
+
else if (wanted.includes('arm64') && abi === 'arm64-v8a') {
|
|
525
|
+
score = 3;
|
|
526
|
+
}
|
|
527
|
+
else if ((wanted.includes('v7') || wanted.includes('armeabi')) &&
|
|
528
|
+
abi === 'armeabi-v7a') {
|
|
529
|
+
score = 2;
|
|
530
|
+
}
|
|
531
|
+
else if (abi === 'universal') {
|
|
532
|
+
score = 1;
|
|
533
|
+
}
|
|
534
|
+
return { path, score };
|
|
535
|
+
});
|
|
536
|
+
scored.sort((a, b) => b.score - a.score);
|
|
537
|
+
return scored[0].path;
|
|
538
|
+
}
|
|
539
|
+
async function extractApkUpdate(label, zipPath, deviceAbi) {
|
|
540
|
+
const ReactNativeBlobUtil = require('react-native-blob-util').default;
|
|
541
|
+
const extractDir = getApkExtractDir(label);
|
|
542
|
+
await removePathRecursive(extractDir);
|
|
543
|
+
await ReactNativeBlobUtil.fs.mkdir(extractDir);
|
|
544
|
+
await (0, zipExtract_1.extractZipToDirectory)(zipPath, extractDir);
|
|
545
|
+
const zipExists = await ReactNativeBlobUtil.fs.exists(zipPath);
|
|
546
|
+
if (zipExists) {
|
|
547
|
+
await ReactNativeBlobUtil.fs.unlink(zipPath);
|
|
548
|
+
}
|
|
549
|
+
return pickExtractedApk(extractDir, deviceAbi);
|
|
550
|
+
}
|
|
459
551
|
function getBundleDirectory() {
|
|
460
552
|
const ReactNativeBlobUtil = require('react-native-blob-util').default;
|
|
461
553
|
return `${ReactNativeBlobUtil.fs.dirs.DocumentDir}/ota-updates`;
|
|
@@ -480,10 +572,13 @@ async function cleanupOldUpdates(keepLabel) {
|
|
|
480
572
|
keepLabel,
|
|
481
573
|
`${keepLabel}.zip`,
|
|
482
574
|
`${keepLabel}.bundle`,
|
|
575
|
+
`${keepLabel}.apk`,
|
|
576
|
+
`${keepLabel}-apk`,
|
|
577
|
+
`${keepLabel}-apk.zip`,
|
|
483
578
|
]);
|
|
484
579
|
const entries = await ReactNativeBlobUtil.fs.ls(dir);
|
|
485
580
|
await Promise.all(entries.map(async (entry) => {
|
|
486
|
-
if (keep.has(entry)
|
|
581
|
+
if (keep.has(entry)) {
|
|
487
582
|
return;
|
|
488
583
|
}
|
|
489
584
|
await removePathRecursive(`${dir}/${entry}`);
|
|
@@ -575,7 +670,7 @@ async function cleanupPartialDownload(path) {
|
|
|
575
670
|
* bytesWritten !== Content-Length — including when Content-Length is wrong
|
|
576
671
|
* but the full file actually landed. Recover those cases via size/hash checks.
|
|
577
672
|
*/
|
|
578
|
-
async function tryRecoverCompletedDownload(destPath, extension, totalBytes, expectedHash, packageFormat, label) {
|
|
673
|
+
async function tryRecoverCompletedDownload(destPath, extension, totalBytes, expectedHash, packageFormat, label, deviceAbi) {
|
|
579
674
|
try {
|
|
580
675
|
const ReactNativeBlobUtil = require('react-native-blob-util').default;
|
|
581
676
|
const exists = await ReactNativeBlobUtil.fs.exists(destPath);
|
|
@@ -594,6 +689,9 @@ async function tryRecoverCompletedDownload(destPath, extension, totalBytes, expe
|
|
|
594
689
|
else if (!sizeLooksComplete) {
|
|
595
690
|
return null;
|
|
596
691
|
}
|
|
692
|
+
if (extension === 'apk' && (packageFormat === 'zip' || (await isZipArchive(destPath)))) {
|
|
693
|
+
return extractApkUpdate(label, destPath, deviceAbi);
|
|
694
|
+
}
|
|
597
695
|
if (extension === 'bundle' && (await shouldExtractZipPackage(destPath, packageFormat))) {
|
|
598
696
|
return extractBundleUpdate(label, destPath);
|
|
599
697
|
}
|
|
@@ -606,16 +704,22 @@ async function tryRecoverCompletedDownload(destPath, extension, totalBytes, expe
|
|
|
606
704
|
async function downloadBundle(downloadUrl, deploymentKey, label, expectedSize, onProgress, extension = 'bundle', deviceAbi, packageFormat = 'bundle', expectedHash) {
|
|
607
705
|
const ReactNativeBlobUtil = require('react-native-blob-util').default;
|
|
608
706
|
const isZipBundle = extension === 'bundle' && packageFormat === 'zip';
|
|
707
|
+
const isApkZip = extension === 'apk' && packageFormat === 'zip';
|
|
609
708
|
const destPath = isZipBundle
|
|
610
709
|
? getBundleZipPath(label)
|
|
611
|
-
:
|
|
612
|
-
?
|
|
613
|
-
:
|
|
710
|
+
: isApkZip
|
|
711
|
+
? getApkZipPath(label)
|
|
712
|
+
: extension === 'apk'
|
|
713
|
+
? getApkFilePath(label)
|
|
714
|
+
: getLegacyBundleFilePath(label);
|
|
614
715
|
const totalBytes = await resolveDownloadSize(downloadUrl, deploymentKey, expectedSize, deviceAbi);
|
|
615
716
|
await ensureBundleDirectory();
|
|
616
717
|
if (isZipBundle) {
|
|
617
718
|
await removePathRecursive(getBundleReleaseDir(label));
|
|
618
719
|
}
|
|
720
|
+
if (isApkZip) {
|
|
721
|
+
await removePathRecursive(getApkExtractDir(label));
|
|
722
|
+
}
|
|
619
723
|
let lastError;
|
|
620
724
|
for (let attempt = 1; attempt <= DOWNLOAD_MAX_ATTEMPTS; attempt++) {
|
|
621
725
|
await cleanupPartialDownload(destPath);
|
|
@@ -676,6 +780,9 @@ async function downloadBundle(downloadUrl, deploymentKey, label, expectedSize, o
|
|
|
676
780
|
throw new Error('Package hash verification failed');
|
|
677
781
|
}
|
|
678
782
|
}
|
|
783
|
+
if (extension === 'apk' && (isApkZip || (await isZipArchive(destPath)))) {
|
|
784
|
+
return extractApkUpdate(label, destPath, deviceAbi);
|
|
785
|
+
}
|
|
679
786
|
if (extension === 'bundle' && (await shouldExtractZipPackage(destPath, packageFormat))) {
|
|
680
787
|
return extractBundleUpdate(label, destPath);
|
|
681
788
|
}
|
|
@@ -684,7 +791,7 @@ async function downloadBundle(downloadUrl, deploymentKey, label, expectedSize, o
|
|
|
684
791
|
catch (error) {
|
|
685
792
|
lastError = error;
|
|
686
793
|
if (isRetryableDownloadError(error)) {
|
|
687
|
-
const recovered = await tryRecoverCompletedDownload(destPath, extension, totalBytes, expectedHash, packageFormat, label);
|
|
794
|
+
const recovered = await tryRecoverCompletedDownload(destPath, extension, totalBytes, expectedHash, packageFormat, label, deviceAbi);
|
|
688
795
|
if (recovered) {
|
|
689
796
|
if (onProgress)
|
|
690
797
|
onProgress(1);
|
|
@@ -969,7 +1076,7 @@ async function performApkSync(update, showUI, messages, onProgress, changelog) {
|
|
|
969
1076
|
label: update.label,
|
|
970
1077
|
description: notes,
|
|
971
1078
|
isMandatory: update.isMandatory,
|
|
972
|
-
}), 'apk', update.deviceAbi, 'bundle', update.packageHash);
|
|
1079
|
+
}), 'apk', update.deviceAbi, update.packageFormat ?? 'bundle', update.packageHash);
|
|
973
1080
|
onProgress?.(types_1.SyncStatus.INSTALLING, 1);
|
|
974
1081
|
// Drop the OTA JS path before opening the installer so the new APK does not
|
|
975
1082
|
// keep loading the previous bundle on first launch after install.
|