react-native-update 10.43.3 → 10.45.0
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/android/jni/Android.mk +0 -1
- package/android/lib/arm64-v8a/librnupdate.so +0 -0
- package/android/lib/armeabi-v7a/librnupdate.so +0 -0
- package/android/lib/x86/librnupdate.so +0 -0
- package/android/lib/x86_64/librnupdate.so +0 -0
- package/android/src/main/java/cn/reactnative/modules/update/BundledResourceCopier.java +24 -12
- package/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +33 -9
- package/android/src/main/java/cn/reactnative/modules/update/ReactReloadManager.java +9 -1
- package/android/src/main/java/cn/reactnative/modules/update/StateSerialRunner.java +64 -0
- package/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +8 -4
- package/android/src/main/java/cn/reactnative/modules/update/UpdateEventEmitter.java +13 -1
- package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleImpl.java +8 -8
- package/cpp/patch_core/archive_patch_core.cpp +16 -0
- package/cpp/patch_core/archive_patch_core.h +5 -0
- package/cpp/patch_core/jni_util.h +56 -0
- package/cpp/patch_core/patch_core.cpp +16 -1
- package/cpp/patch_core/patch_core.h +6 -0
- package/cpp/patch_core/patch_core_android.cpp +4 -37
- package/cpp/patch_core/state_ops.h +24 -0
- package/cpp/patch_core/tests/patch_core_test.cpp +109 -2
- package/cpp/patch_core/update_core_android.cpp +43 -69
- package/harmony/pushy/src/main/cpp/pushy.cpp +163 -128
- package/harmony/pushy/src/main/ets/DownloadTask.ts +72 -19
- package/harmony/pushy/src/main/ets/NativePatchCore.ts +2 -6
- package/harmony/pushy/src/main/ets/PushyTurboModule.ts +10 -10
- package/harmony/pushy/src/main/ets/UpdateContext.ts +26 -9
- package/harmony/pushy.har +0 -0
- package/ios/RCTPushy/RCTPushy.mm +142 -111
- package/ios/RCTPushy/RCTPushyDownloader.mm +41 -2
- package/package.json +7 -3
- package/src/client.ts +161 -101
- package/src/context.ts +21 -7
- package/src/core.ts +5 -1
- package/src/index.ts +7 -1
- package/src/provider.tsx +91 -56
- package/src/utils.ts +58 -18
- package/android/jni/DownloadTask.c +0 -56
- package/android/jni/cn_reactnative_modules_update_DownloadTask.h +0 -21
- package/harmony/pushy/src/main/cpp/pushy.c +0 -117
- package/harmony/pushy/src/main/cpp/pushy.h +0 -8
- package/src/__tests__/setup.ts +0 -44
package/src/client.ts
CHANGED
|
@@ -30,7 +30,6 @@ import {
|
|
|
30
30
|
assertWeb,
|
|
31
31
|
computeProgress,
|
|
32
32
|
DEFAULT_FETCH_TIMEOUT_MS,
|
|
33
|
-
emptyObj,
|
|
34
33
|
fetchWithTimeout,
|
|
35
34
|
info,
|
|
36
35
|
joinUrls,
|
|
@@ -87,6 +86,7 @@ const defaultClientOptions: ClientOptions = {
|
|
|
87
86
|
|
|
88
87
|
export const sharedState: {
|
|
89
88
|
progressHandlers: Record<string, EmitterSubscription>;
|
|
89
|
+
downloadingTasks: Record<string, Promise<string | undefined>>;
|
|
90
90
|
downloadedHash?: string;
|
|
91
91
|
toHash?: string;
|
|
92
92
|
apkStatus: 'downloading' | 'downloaded' | null;
|
|
@@ -94,6 +94,7 @@ export const sharedState: {
|
|
|
94
94
|
applyingUpdate: boolean;
|
|
95
95
|
} = {
|
|
96
96
|
progressHandlers: {},
|
|
97
|
+
downloadingTasks: {},
|
|
97
98
|
downloadedHash: undefined,
|
|
98
99
|
apkStatus: null,
|
|
99
100
|
marked: false,
|
|
@@ -102,6 +103,7 @@ export const sharedState: {
|
|
|
102
103
|
|
|
103
104
|
const assertHash = (hash: string) => {
|
|
104
105
|
if (!sharedState.downloadedHash) {
|
|
106
|
+
log(`no downloaded hash yet, ignore switch to ${hash}`);
|
|
105
107
|
return;
|
|
106
108
|
}
|
|
107
109
|
if (hash !== sharedState.downloadedHash) {
|
|
@@ -113,7 +115,7 @@ const assertHash = (hash: string) => {
|
|
|
113
115
|
|
|
114
116
|
// for China users
|
|
115
117
|
export class Pushy {
|
|
116
|
-
options = defaultClientOptions;
|
|
118
|
+
options = { ...defaultClientOptions };
|
|
117
119
|
clientType: 'Pushy' | 'Cresc' = 'Pushy';
|
|
118
120
|
lastChecking?: number;
|
|
119
121
|
lastRespJson?: Promise<CheckResult>;
|
|
@@ -134,7 +136,9 @@ export class Pushy {
|
|
|
134
136
|
this.clientType = clientType || 'Pushy';
|
|
135
137
|
this.options.server = cloneServerConfig(SERVER_PRESETS[this.clientType]);
|
|
136
138
|
|
|
137
|
-
i18n.setLocale(
|
|
139
|
+
i18n.setLocale(
|
|
140
|
+
options.locale ?? (this.clientType === 'Pushy' ? 'zh' : 'en'),
|
|
141
|
+
);
|
|
138
142
|
|
|
139
143
|
if (Platform.OS === 'ios' || Platform.OS === 'android') {
|
|
140
144
|
if (!options.appKey) {
|
|
@@ -187,23 +191,39 @@ export class Pushy {
|
|
|
187
191
|
data?: Record<string, string | number>;
|
|
188
192
|
}) => {
|
|
189
193
|
log(`${type} ${message}`);
|
|
190
|
-
|
|
194
|
+
if (this.options.logger === noop) {
|
|
195
|
+
// Wait briefly for a logger to arrive via setOptions (e.g. the rollback
|
|
196
|
+
// report fires in the constructor before the user configures one), but
|
|
197
|
+
// give up after a bound instead of retaining the closure forever when
|
|
198
|
+
// no logger is ever provided.
|
|
199
|
+
await Promise.race([
|
|
200
|
+
this.loggerPromise.promise,
|
|
201
|
+
new Promise(resolve => setTimeout(resolve, 10 * 1000)),
|
|
202
|
+
]);
|
|
203
|
+
}
|
|
191
204
|
const { logger = noop, appKey } = this.options;
|
|
192
205
|
const overridePackageVersion = this.options.overridePackageVersion;
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
206
|
+
try {
|
|
207
|
+
logger({
|
|
208
|
+
type,
|
|
209
|
+
data: {
|
|
210
|
+
appKey,
|
|
211
|
+
currentVersion,
|
|
212
|
+
cInfo,
|
|
213
|
+
packageVersion,
|
|
214
|
+
overridePackageVersion,
|
|
215
|
+
buildTime,
|
|
216
|
+
message,
|
|
217
|
+
...currentVersionInfo,
|
|
218
|
+
...data,
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
} catch (e: any) {
|
|
222
|
+
// A user-provided logger must never break the update flow, and report()
|
|
223
|
+
// calls are fire-and-forget so a throw here would be an unhandled
|
|
224
|
+
// rejection.
|
|
225
|
+
log('logger error:', e?.message || e);
|
|
226
|
+
}
|
|
207
227
|
};
|
|
208
228
|
throwIfEnabled = (e: Error) => {
|
|
209
229
|
if (this.options.throwError) {
|
|
@@ -348,7 +368,14 @@ export class Pushy {
|
|
|
348
368
|
sharedState.applyingUpdate = false;
|
|
349
369
|
throw e;
|
|
350
370
|
}
|
|
351
|
-
|
|
371
|
+
try {
|
|
372
|
+
return await PushyModule.reloadUpdate({ hash });
|
|
373
|
+
} catch (e) {
|
|
374
|
+
// reloadUpdate can reject (e.g. bundle missing); reset the flag so a
|
|
375
|
+
// later retry is not permanently blocked by a stuck applyingUpdate.
|
|
376
|
+
sharedState.applyingUpdate = false;
|
|
377
|
+
throw e;
|
|
378
|
+
}
|
|
352
379
|
}
|
|
353
380
|
};
|
|
354
381
|
|
|
@@ -437,34 +464,17 @@ export class Pushy {
|
|
|
437
464
|
});
|
|
438
465
|
this.notifyAfterCheckUpdate({ status: 'error', error: e });
|
|
439
466
|
this.throwIfEnabled(e);
|
|
440
|
-
|
|
467
|
+
// Fall back to the previous successful response if we have one; otherwise
|
|
468
|
+
// return undefined so callers can distinguish "check failed" from a real
|
|
469
|
+
// empty result and avoid overwriting the last good updateInfo.
|
|
470
|
+
return previousRespJson ? await previousRespJson : undefined;
|
|
441
471
|
}
|
|
442
472
|
};
|
|
443
|
-
getBackupEndpoints = async () => {
|
|
444
|
-
const { server } = this.options;
|
|
445
|
-
if (!server) {
|
|
446
|
-
return [];
|
|
447
|
-
}
|
|
448
|
-
const remoteEndpoints = await this.getRemoteEndpoints();
|
|
449
|
-
return excludeConfiguredEndpoints(
|
|
450
|
-
dedupeEndpoints(remoteEndpoints),
|
|
451
|
-
this.getConfiguredCheckEndpoints(),
|
|
452
|
-
);
|
|
453
|
-
};
|
|
454
473
|
downloadUpdate = async (
|
|
455
474
|
updateInfo: CheckResult,
|
|
456
475
|
onDownloadProgress?: (data: ProgressData) => void,
|
|
457
476
|
) => {
|
|
458
|
-
const {
|
|
459
|
-
hash,
|
|
460
|
-
diff,
|
|
461
|
-
pdiff,
|
|
462
|
-
full,
|
|
463
|
-
paths = [],
|
|
464
|
-
name,
|
|
465
|
-
description = '',
|
|
466
|
-
metaInfo,
|
|
467
|
-
} = updateInfo;
|
|
477
|
+
const { hash } = updateInfo;
|
|
468
478
|
if (
|
|
469
479
|
this.options.beforeDownloadUpdate &&
|
|
470
480
|
(await this.options.beforeDownloadUpdate(updateInfo)) === false
|
|
@@ -487,6 +497,39 @@ export class Pushy {
|
|
|
487
497
|
log(`duplicated downloaded hash ${sharedState.downloadedHash}, ignored`);
|
|
488
498
|
return sharedState.downloadedHash;
|
|
489
499
|
}
|
|
500
|
+
// Deduplicate concurrent downloads of the same hash regardless of whether a
|
|
501
|
+
// progress callback was passed: all callers await the single in-flight
|
|
502
|
+
// promise instead of triggering parallel native downloads.
|
|
503
|
+
const existingTask = sharedState.downloadingTasks[hash];
|
|
504
|
+
if (existingTask) {
|
|
505
|
+
log(`download for hash ${hash} already in progress, reusing it`);
|
|
506
|
+
return existingTask;
|
|
507
|
+
}
|
|
508
|
+
const task = this.performDownload(updateInfo, onDownloadProgress);
|
|
509
|
+
sharedState.downloadingTasks[hash] = task;
|
|
510
|
+
try {
|
|
511
|
+
return await task;
|
|
512
|
+
} finally {
|
|
513
|
+
delete sharedState.downloadingTasks[hash];
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
private performDownload = async (
|
|
517
|
+
updateInfo: CheckResult,
|
|
518
|
+
onDownloadProgress?: (data: ProgressData) => void,
|
|
519
|
+
) => {
|
|
520
|
+
const {
|
|
521
|
+
hash,
|
|
522
|
+
diff,
|
|
523
|
+
pdiff,
|
|
524
|
+
full,
|
|
525
|
+
paths = [],
|
|
526
|
+
name,
|
|
527
|
+
description = '',
|
|
528
|
+
metaInfo,
|
|
529
|
+
} = updateInfo;
|
|
530
|
+
if (!hash) {
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
490
533
|
if (sharedState.progressHandlers[hash]) {
|
|
491
534
|
return;
|
|
492
535
|
}
|
|
@@ -525,6 +568,59 @@ export class Pushy {
|
|
|
525
568
|
let lastError: any;
|
|
526
569
|
const errorMessages: string[] = [];
|
|
527
570
|
|
|
571
|
+
// Ordered download strategies, tried in sequence until one succeeds. Each
|
|
572
|
+
// resolves its candidate URL lazily (testUrls) and runs the matching native
|
|
573
|
+
// download. diff/pdiff are incremental and skipped entirely in dev; full is
|
|
574
|
+
// attempted whenever a URL exists, and in dev with no URL it is treated as a
|
|
575
|
+
// no-op success so the flow can proceed.
|
|
576
|
+
type DownloadStrategy = {
|
|
577
|
+
name: string;
|
|
578
|
+
candidate: string | undefined;
|
|
579
|
+
errorKey: 'error_diff_failed' | 'error_pdiff_failed' | 'error_full_patch_failed';
|
|
580
|
+
skipInDev: boolean;
|
|
581
|
+
devNoopWhenNoUrl: boolean;
|
|
582
|
+
run: (url: string) => Promise<void>;
|
|
583
|
+
};
|
|
584
|
+
const strategies: DownloadStrategy[] = [
|
|
585
|
+
{
|
|
586
|
+
name: 'diff',
|
|
587
|
+
candidate: diff,
|
|
588
|
+
errorKey: 'error_diff_failed',
|
|
589
|
+
skipInDev: true,
|
|
590
|
+
devNoopWhenNoUrl: false,
|
|
591
|
+
run: url =>
|
|
592
|
+
PushyModule.downloadPatchFromPpk({
|
|
593
|
+
updateUrl: url,
|
|
594
|
+
hash,
|
|
595
|
+
originHash: currentVersion,
|
|
596
|
+
}),
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
name: 'pdiff',
|
|
600
|
+
candidate: pdiff,
|
|
601
|
+
errorKey: 'error_pdiff_failed',
|
|
602
|
+
skipInDev: true,
|
|
603
|
+
devNoopWhenNoUrl: false,
|
|
604
|
+
run: url =>
|
|
605
|
+
PushyModule.downloadPatchFromPackage({
|
|
606
|
+
updateUrl: url,
|
|
607
|
+
hash,
|
|
608
|
+
}),
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
name: 'full',
|
|
612
|
+
candidate: full,
|
|
613
|
+
errorKey: 'error_full_patch_failed',
|
|
614
|
+
skipInDev: false,
|
|
615
|
+
devNoopWhenNoUrl: true,
|
|
616
|
+
run: url =>
|
|
617
|
+
PushyModule.downloadFullUpdate({
|
|
618
|
+
updateUrl: url,
|
|
619
|
+
hash,
|
|
620
|
+
}),
|
|
621
|
+
},
|
|
622
|
+
];
|
|
623
|
+
|
|
528
624
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
529
625
|
if (attempt > 0) {
|
|
530
626
|
const backoffMs = Math.min(1000 * 2 ** (attempt - 1), 10000);
|
|
@@ -541,66 +637,27 @@ export class Pushy {
|
|
|
541
637
|
attempt,
|
|
542
638
|
},
|
|
543
639
|
});
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
try {
|
|
548
|
-
await PushyModule.downloadPatchFromPpk({
|
|
549
|
-
updateUrl: diffUrl,
|
|
550
|
-
hash,
|
|
551
|
-
originHash: currentVersion,
|
|
552
|
-
});
|
|
553
|
-
succeeded = 'diff';
|
|
554
|
-
} catch (e: any) {
|
|
555
|
-
const errorMessage = this.t('error_diff_failed', {
|
|
556
|
-
message: e.message,
|
|
557
|
-
});
|
|
558
|
-
errorMessages.push(errorMessage);
|
|
559
|
-
lastError = Error(errorMessage);
|
|
560
|
-
log(errorMessage);
|
|
640
|
+
for (const strategy of strategies) {
|
|
641
|
+
if (succeeded) {
|
|
642
|
+
break;
|
|
561
643
|
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
if (pdiffUrl && !__DEV__) {
|
|
566
|
-
log('downloading pdiff');
|
|
644
|
+
const url = await testUrls(joinUrls(paths, strategy.candidate));
|
|
645
|
+
if (url && !(strategy.skipInDev && __DEV__)) {
|
|
646
|
+
log(`downloading ${strategy.name}`);
|
|
567
647
|
try {
|
|
568
|
-
await
|
|
569
|
-
|
|
570
|
-
hash,
|
|
571
|
-
});
|
|
572
|
-
succeeded = 'pdiff';
|
|
648
|
+
await strategy.run(url);
|
|
649
|
+
succeeded = strategy.name;
|
|
573
650
|
} catch (e: any) {
|
|
574
|
-
const errorMessage = this.t(
|
|
651
|
+
const errorMessage = this.t(strategy.errorKey, {
|
|
575
652
|
message: e.message,
|
|
576
653
|
});
|
|
577
654
|
errorMessages.push(errorMessage);
|
|
578
655
|
lastError = Error(errorMessage);
|
|
579
656
|
log(errorMessage);
|
|
580
657
|
}
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
if (!succeeded) {
|
|
584
|
-
const fullUrl = await testUrls(joinUrls(paths, full));
|
|
585
|
-
if (fullUrl) {
|
|
586
|
-
log('downloading full patch');
|
|
587
|
-
try {
|
|
588
|
-
await PushyModule.downloadFullUpdate({
|
|
589
|
-
updateUrl: fullUrl,
|
|
590
|
-
hash,
|
|
591
|
-
});
|
|
592
|
-
succeeded = 'full';
|
|
593
|
-
} catch (e: any) {
|
|
594
|
-
const errorMessage = this.t('error_full_patch_failed', {
|
|
595
|
-
message: e.message,
|
|
596
|
-
});
|
|
597
|
-
errorMessages.push(errorMessage);
|
|
598
|
-
lastError = Error(errorMessage);
|
|
599
|
-
log(errorMessage);
|
|
600
|
-
}
|
|
601
|
-
} else if (__DEV__) {
|
|
658
|
+
} else if (!url && strategy.devNoopWhenNoUrl && __DEV__) {
|
|
602
659
|
log(this.t('dev_incremental_update_disabled'));
|
|
603
|
-
succeeded =
|
|
660
|
+
succeeded = strategy.name;
|
|
604
661
|
}
|
|
605
662
|
}
|
|
606
663
|
if (succeeded) {
|
|
@@ -697,19 +754,22 @@ export class Pushy {
|
|
|
697
754
|
},
|
|
698
755
|
);
|
|
699
756
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
757
|
+
try {
|
|
758
|
+
await PushyModule.downloadAndInstallApk({
|
|
759
|
+
url,
|
|
760
|
+
target: 'update.apk',
|
|
761
|
+
hash: progressKey,
|
|
762
|
+
});
|
|
763
|
+
sharedState.apkStatus = 'downloaded';
|
|
764
|
+
} catch {
|
|
705
765
|
sharedState.apkStatus = null;
|
|
706
766
|
this.report({ type: 'errorDownloadAndInstallApk' });
|
|
707
767
|
this.throwIfEnabled(Error('errorDownloadAndInstallApk'));
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
768
|
+
} finally {
|
|
769
|
+
if (sharedState.progressHandlers[progressKey]) {
|
|
770
|
+
sharedState.progressHandlers[progressKey].remove();
|
|
771
|
+
delete sharedState.progressHandlers[progressKey];
|
|
772
|
+
}
|
|
713
773
|
}
|
|
714
774
|
};
|
|
715
775
|
restartApp = async () => {
|
package/src/context.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createContext, useContext } from 'react';
|
|
1
|
+
import { createContext, useContext, useMemo } from 'react';
|
|
2
2
|
import { CheckResult, ProgressData } from './type';
|
|
3
3
|
import { Pushy, Cresc } from './client';
|
|
4
4
|
import i18n from './i18n';
|
|
@@ -46,21 +46,35 @@ export const UpdateContext = createContext<{
|
|
|
46
46
|
currentHash: string;
|
|
47
47
|
packageVersion: string;
|
|
48
48
|
client?: Pushy | Cresc;
|
|
49
|
-
progress?: ProgressData;
|
|
50
49
|
updateInfo?: CheckResult;
|
|
51
50
|
lastError?: Error;
|
|
52
51
|
}>(defaultContext);
|
|
53
52
|
|
|
54
|
-
|
|
53
|
+
// Download progress ticks at high frequency, so it lives in its own context;
|
|
54
|
+
// otherwise every tick would re-render all useUpdate() consumers even when
|
|
55
|
+
// they never read progress.
|
|
56
|
+
export const ProgressContext = createContext<ProgressData | undefined>(
|
|
57
|
+
undefined,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Subscribe to download progress only. Components that render a progress bar
|
|
62
|
+
* should prefer this over useUpdate() so the rest of the tree is not
|
|
63
|
+
* re-rendered on every progress event.
|
|
64
|
+
*/
|
|
65
|
+
export const useUpdateProgress = () => useContext(ProgressContext);
|
|
66
|
+
|
|
67
|
+
export const useUpdate = () => {
|
|
55
68
|
const context = useContext(UpdateContext);
|
|
69
|
+
const progress = useContext(ProgressContext);
|
|
56
70
|
|
|
57
|
-
|
|
58
|
-
|
|
71
|
+
if (__DEV__ && !context.client) {
|
|
72
|
+
// 检查是否在 UpdateProvider 内部使用
|
|
59
73
|
throw new Error(i18n.t('error_use_update_outside_provider'));
|
|
60
74
|
}
|
|
61
75
|
|
|
62
|
-
return context;
|
|
63
|
-
}
|
|
76
|
+
return useMemo(() => ({ ...context, progress }), [context, progress]);
|
|
77
|
+
};
|
|
64
78
|
|
|
65
79
|
/** @deprecated Please use `useUpdate` instead */
|
|
66
80
|
export const usePushy = useUpdate;
|
package/src/core.ts
CHANGED
|
@@ -82,7 +82,11 @@ export const pushyNativeEventEmitter = new NativeEventEmitter(PushyModule);
|
|
|
82
82
|
|
|
83
83
|
if (!uuid) {
|
|
84
84
|
uuid = require('nanoid/non-secure').nanoid();
|
|
85
|
-
|
|
85
|
+
// If persisting fails the uuid drifts on every launch, which skews gray
|
|
86
|
+
// release bucketing and inflates stats — log it instead of failing silently.
|
|
87
|
+
Promise.resolve(PushyModule.setUuid(uuid)).catch((e: any) => {
|
|
88
|
+
log('setUuid error:', e?.message || e);
|
|
89
|
+
});
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
export const cInfo = {
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
export { Pushy, Cresc } from './client';
|
|
2
|
-
export {
|
|
2
|
+
export {
|
|
3
|
+
ProgressContext,
|
|
4
|
+
UpdateContext,
|
|
5
|
+
usePushy,
|
|
6
|
+
useUpdate,
|
|
7
|
+
useUpdateProgress,
|
|
8
|
+
} from './context';
|
|
3
9
|
export { PushyProvider, UpdateProvider } from './provider';
|
|
4
10
|
export { PushyModule, UpdateModule } from './core';
|