react-native-update 10.46.0 → 10.48.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 +2 -0
- package/android/jni/hpatch.c +57 -2
- 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/DownloadTask.java +19 -4
- package/android/src/main/java/cn/reactnative/modules/update/DownloadTaskParams.java +2 -0
- package/android/src/main/java/cn/reactnative/modules/update/ErrorCodes.java +1 -0
- package/android/src/main/java/cn/reactnative/modules/update/NativeUpdateCore.java +12 -0
- package/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +30 -0
- package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleImpl.java +13 -0
- package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleSupport.java +1 -0
- package/android/src/newarch/cn/reactnative/modules/update/UpdateModule.java +5 -0
- package/android/src/oldarch/cn/reactnative/modules/update/UpdateModule.java +5 -0
- package/cpp/patch_core/error_codes.h +3 -0
- package/cpp/patch_core/hbc_transform.cpp +143 -0
- package/cpp/patch_core/hbc_transform.h +53 -0
- package/cpp/patch_core/hbc_transform_wire.cpp +321 -0
- package/cpp/patch_core/hbc_transform_wire.h +52 -0
- package/cpp/patch_core/patch_core.cpp +126 -4
- package/cpp/patch_core/patch_core.h +5 -0
- package/cpp/patch_core/patch_core_android.cpp +3 -1
- package/cpp/patch_core/tests/fixtures/rawstored.lzma2label.patch.bin +0 -0
- package/cpp/patch_core/tests/fixtures/rawstored.new.bin +0 -0
- package/cpp/patch_core/tests/fixtures/rawstored.old.bin +0 -0
- package/cpp/patch_core/tests/fixtures/v96.hbc +0 -0
- package/cpp/patch_core/tests/fixtures/v96.meta.json +1 -0
- package/cpp/patch_core/tests/fixtures/v96.streampatch.bin +0 -0
- package/cpp/patch_core/tests/fixtures/v96.t.hbc +0 -0
- package/cpp/patch_core/tests/fixtures/v96.tpatch.bin +0 -0
- package/cpp/patch_core/tests/fixtures/v96.tstreampatch.bin +0 -0
- package/cpp/patch_core/tests/fixtures/v96b.hbc +0 -0
- package/cpp/patch_core/tests/fixtures/v98.hbc +0 -0
- package/cpp/patch_core/tests/fixtures/v98.t.hbc +0 -0
- package/cpp/patch_core/tests/fixtures/v98b.hbc +0 -0
- package/cpp/patch_core/tests/fixtures/v98b.t.hbc +0 -0
- package/cpp/patch_core/tests/hbc_transform_test.cpp +229 -0
- package/cpp/patch_core/tests/patch_core_test.cpp +150 -1
- package/cpp/patch_core/update_core_android.cpp +10 -0
- package/harmony/pushy/src/main/cpp/CMakeLists.txt +2 -0
- package/harmony/pushy/src/main/cpp/pushy.cpp +18 -1
- package/harmony/pushy/src/main/ets/DownloadTask.ts +21 -0
- package/harmony/pushy/src/main/ets/NativePatchCore.ts +4 -0
- package/harmony/pushy/src/main/ets/PushyTurboModule.ts +12 -0
- package/harmony/pushy/src/main/ets/UpdateContext.ts +46 -0
- package/harmony/pushy.har +0 -0
- package/ios/RCTPushy/RCTPushy.mm +64 -0
- package/package.json +1 -1
- package/react-native-update.podspec +2 -0
- package/src/NativePushy.ts +1 -0
- package/src/client.ts +123 -0
- package/src/context.ts +4 -0
- package/src/core.ts +4 -0
- package/src/error.ts +1 -0
- package/src/locales/en.ts +2 -0
- package/src/locales/zh.ts +2 -0
- package/src/provider.tsx +9 -0
- package/src/telemetry.ts +83 -0
- package/src/type.ts +8 -0
package/ios/RCTPushy/RCTPushy.mm
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
#import "RCTPushyDownloader.h"
|
|
3
3
|
#import "ZipArchive.h"
|
|
4
4
|
#include "../../cpp/patch_core/archive_patch_core.h"
|
|
5
|
+
#include "../../cpp/patch_core/hbc_transform_wire.h"
|
|
5
6
|
#include "../../cpp/patch_core/error_codes.h"
|
|
6
7
|
#include "../../cpp/patch_core/patch_core.h"
|
|
7
8
|
#include "../../cpp/patch_core/state_core.h"
|
|
@@ -347,6 +348,9 @@ RCT_EXPORT_MODULE(RCTPushy);
|
|
|
347
348
|
ret[@"rolledBackVersion"] = [defaults objectForKey:keyRolledBackMarked];
|
|
348
349
|
ret[@"isFirstTime"] = [defaults objectForKey:keyFirstLoadMarked];
|
|
349
350
|
ret[@"uuid"] = [defaults objectForKey:keyUuid];
|
|
351
|
+
// 原生 patch 内核可消费的 diff 轨道版本(2 = hdiffv2 轨道),
|
|
352
|
+
// JS 随 checkUpdate 以 diffV 上报,服务端按能力门控下发
|
|
353
|
+
ret[@"supportedDiffVersion"] = @(pushy::hbc::kSupportedDiffVersion);
|
|
350
354
|
NSDictionary *pushyInfo = [defaults dictionaryForKey:keyPushyInfo];
|
|
351
355
|
NSString *currentVersion = [pushyInfo objectForKey:paramCurrentVersion];
|
|
352
356
|
ret[@"currentVersion"] = currentVersion;
|
|
@@ -514,6 +518,51 @@ RCT_EXPORT_METHOD(markSuccess:(RCTPromiseResolveBlock)resolve
|
|
|
514
518
|
#endif
|
|
515
519
|
}
|
|
516
520
|
|
|
521
|
+
RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
|
|
522
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
523
|
+
{
|
|
524
|
+
// Reset to the bundle packaged in the binary: wipe the whole update state
|
|
525
|
+
// (so the next launch resolves to the built-in bundle) and delete every
|
|
526
|
+
// downloaded version. Only the client uuid survives — it identifies the
|
|
527
|
+
// install for gray release bucketing and must not change on reset.
|
|
528
|
+
PushyWithStateLock(^{
|
|
529
|
+
NSUserDefaults *defaults = PushyDefaults();
|
|
530
|
+
|
|
531
|
+
// A default-constructed State is exactly the reset state (no current /
|
|
532
|
+
// last version, first_time=false, first_time_ok=true); keep the binary
|
|
533
|
+
// identity so the next launch does not re-trigger the package-updated
|
|
534
|
+
// sync path.
|
|
535
|
+
pushy::state::State state;
|
|
536
|
+
state.package_version = PushyToStdString([RCTPushy packageVersion]);
|
|
537
|
+
state.build_time = PushyToStdString([RCTPushy buildTime]);
|
|
538
|
+
PushyApplyStateToDefaults(defaults, state);
|
|
539
|
+
|
|
540
|
+
for (NSString *key in [defaults dictionaryRepresentation].allKeys) {
|
|
541
|
+
if ([key hasPrefix:keyHashInfo]) {
|
|
542
|
+
[defaults removeObjectForKey:key];
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
[defaults removeObjectForKey:keyFirstLoadMarked];
|
|
546
|
+
[defaults removeObjectForKey:KeyPackageUpdatedMarked];
|
|
547
|
+
ignoreRollback = false;
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
dispatch_async(_fileQueue, ^{
|
|
551
|
+
// maxAgeDays=0 and no versions to keep: remove every downloaded entry.
|
|
552
|
+
pushy::patch::Status status = pushy::patch::CleanupOldEntries(
|
|
553
|
+
PushyToStdString([RCTPushy downloadDir]),
|
|
554
|
+
"",
|
|
555
|
+
"",
|
|
556
|
+
0
|
|
557
|
+
);
|
|
558
|
+
if (!status.ok) {
|
|
559
|
+
RCTLogWarn(@"Pushy reset cleanup error: %s", status.message.c_str());
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
resolve(@true);
|
|
564
|
+
}
|
|
565
|
+
|
|
517
566
|
|
|
518
567
|
|
|
519
568
|
#pragma mark - private
|
|
@@ -742,6 +791,21 @@ RCT_EXPORT_METHOD(markSuccess:(RCTPromiseResolveBlock)resolve
|
|
|
742
791
|
return;
|
|
743
792
|
}
|
|
744
793
|
|
|
794
|
+
// __diff.json 的 hbcTransform 元数据(HBC 变换域 patch,hdiffv2 轨道):
|
|
795
|
+
// 存在时透传给 patch 内核执行 T(origin) → hpatch → T⁻¹;缺失走现状路径。
|
|
796
|
+
NSDictionary *hbcTransform = json[@"hbcTransform"];
|
|
797
|
+
if ([hbcTransform isKindOfClass:[NSDictionary class]]) {
|
|
798
|
+
NSDictionary *meta = hbcTransform[BUNDLE_PATCH_NAME];
|
|
799
|
+
if ([meta isKindOfClass:[NSDictionary class]]) {
|
|
800
|
+
NSError *metaError = nil;
|
|
801
|
+
NSData *metaData = [NSJSONSerialization dataWithJSONObject:meta options:0 error:&metaError];
|
|
802
|
+
if (metaData != nil && metaError == nil) {
|
|
803
|
+
NSString *metaString = [[NSString alloc] initWithData:metaData encoding:NSUTF8StringEncoding];
|
|
804
|
+
options.bundle_hbc_transform_meta = PushyToStdString(metaString);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
745
809
|
pushy::patch::Status status = pushy::patch::ApplyPatchFromFileSource(options);
|
|
746
810
|
if (!status.ok) {
|
|
747
811
|
callback(PushyNSErrorFromStatus(status));
|
package/package.json
CHANGED
|
@@ -111,6 +111,8 @@ Pod::Spec.new do |s|
|
|
|
111
111
|
s.subspec 'RCTPushy' do |ss|
|
|
112
112
|
ss.source_files = ['ios/RCTPushy/*.{h,m,mm}',
|
|
113
113
|
'cpp/patch_core/archive_patch_core.{h,cpp}',
|
|
114
|
+
'cpp/patch_core/hbc_transform.{h,cpp}',
|
|
115
|
+
'cpp/patch_core/hbc_transform_wire.{h,cpp}',
|
|
114
116
|
'cpp/patch_core/patch_core.{h,cpp}',
|
|
115
117
|
'cpp/patch_core/state_core.{h,cpp}',
|
|
116
118
|
'android/jni/hpatch.{h,c}',
|
package/src/NativePushy.ts
CHANGED
|
@@ -19,6 +19,7 @@ export interface Spec extends TurboModule {
|
|
|
19
19
|
restartApp(): Promise<void>;
|
|
20
20
|
setNeedUpdate(options: { hash: string }): Promise<void>;
|
|
21
21
|
markSuccess(): Promise<void>;
|
|
22
|
+
resetToPackagedBundle(): Promise<void>;
|
|
22
23
|
downloadPatchFromPpk(options: {
|
|
23
24
|
updateUrl: string;
|
|
24
25
|
hash: string;
|
package/src/client.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
cInfo,
|
|
10
10
|
currentVersion,
|
|
11
11
|
currentVersionInfo,
|
|
12
|
+
supportedDiffVersion,
|
|
12
13
|
isFirstTime,
|
|
13
14
|
isRolledBack,
|
|
14
15
|
packageVersion,
|
|
@@ -41,6 +42,11 @@ import {
|
|
|
41
42
|
import i18n from './i18n';
|
|
42
43
|
import { toUpdateError, UpdateError, UpdateErrorCode } from './error';
|
|
43
44
|
import { dedupeEndpoints, executeEndpointFallback } from './endpoint';
|
|
45
|
+
import {
|
|
46
|
+
resolveServerEventHash,
|
|
47
|
+
resolveServerEventType,
|
|
48
|
+
truncateDetail,
|
|
49
|
+
} from './telemetry';
|
|
44
50
|
|
|
45
51
|
/**
|
|
46
52
|
* Receives every error the client reports, alongside the report event type.
|
|
@@ -130,6 +136,9 @@ export class Pushy {
|
|
|
130
136
|
clientType: 'Pushy' | 'Cresc' = 'Pushy';
|
|
131
137
|
lastChecking?: number;
|
|
132
138
|
lastRespJson?: Promise<CheckResult>;
|
|
139
|
+
// Endpoint that most recently served a successful checkUpdate; telemetry
|
|
140
|
+
// reuses it instead of re-running the fallback race.
|
|
141
|
+
private lastWorkingEndpoint?: string;
|
|
133
142
|
|
|
134
143
|
version = cInfo.rnu;
|
|
135
144
|
loggerPromise = (() => {
|
|
@@ -207,6 +216,8 @@ export class Pushy {
|
|
|
207
216
|
data?: Record<string, string | number>;
|
|
208
217
|
}) => {
|
|
209
218
|
log(`${type} ${code ? `[${code}] ` : ''}${message}`);
|
|
219
|
+
// Fire-and-forget server telemetry; must not wait for the logger below.
|
|
220
|
+
this.reportToServer({ type, message, code, data });
|
|
210
221
|
if (this.options.logger === noop) {
|
|
211
222
|
// Wait briefly for a logger to arrive via setOptions (e.g. the rollback
|
|
212
223
|
// report fires in the constructor before the user configures one), but
|
|
@@ -242,6 +253,72 @@ export class Pushy {
|
|
|
242
253
|
log('logger error:', e?.message || e);
|
|
243
254
|
}
|
|
244
255
|
};
|
|
256
|
+
/**
|
|
257
|
+
* Best-effort lifecycle event reporting to the update server (aggregate
|
|
258
|
+
* counts + sampled failure details power the version health view and the
|
|
259
|
+
* rollback safety net server-side). Single POST to the last known working
|
|
260
|
+
* endpoint, no retry, no fallback race; any failure is swallowed — telemetry
|
|
261
|
+
* must never affect the update flow. Opt out with disableTelemetry.
|
|
262
|
+
*/
|
|
263
|
+
private reportToServer = ({
|
|
264
|
+
type,
|
|
265
|
+
message = '',
|
|
266
|
+
code,
|
|
267
|
+
data = {},
|
|
268
|
+
}: {
|
|
269
|
+
type: EventType;
|
|
270
|
+
message?: string;
|
|
271
|
+
code?: UpdateErrorCode;
|
|
272
|
+
data?: Record<string, string | number>;
|
|
273
|
+
}) => {
|
|
274
|
+
try {
|
|
275
|
+
if (__DEV__ || this.options.disableTelemetry) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const serverType = resolveServerEventType(type, code);
|
|
279
|
+
if (!serverType) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const { appKey } = this.options;
|
|
283
|
+
const endpoint =
|
|
284
|
+
this.lastWorkingEndpoint || this.options.server?.main?.[0];
|
|
285
|
+
if (!appKey || !endpoint) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const hash = resolveServerEventHash({ serverType, data, currentVersion });
|
|
289
|
+
if (!hash) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
const send = (payloadType: typeof serverType, detail?: string) =>
|
|
293
|
+
fetchWithTimeout(
|
|
294
|
+
`${endpoint}/report/${appKey}`,
|
|
295
|
+
{
|
|
296
|
+
method: 'POST',
|
|
297
|
+
headers: { 'Content-Type': 'application/json' },
|
|
298
|
+
body: JSON.stringify({
|
|
299
|
+
type: payloadType,
|
|
300
|
+
hash,
|
|
301
|
+
packageVersion:
|
|
302
|
+
this.options.overridePackageVersion || packageVersion,
|
|
303
|
+
cInfo,
|
|
304
|
+
detail: truncateDetail(detail),
|
|
305
|
+
}),
|
|
306
|
+
},
|
|
307
|
+
DEFAULT_FETCH_TIMEOUT_MS,
|
|
308
|
+
).catch((e: any) => {
|
|
309
|
+
log('telemetry report failed:', e?.message || e);
|
|
310
|
+
});
|
|
311
|
+
send(serverType, message || undefined);
|
|
312
|
+
// A download that only succeeded after an incremental patch failed is
|
|
313
|
+
// still a patch_fail signal server-side (diff quality), carried in
|
|
314
|
+
// data.error alongside the downloadSuccess event.
|
|
315
|
+
if (serverType === 'download_success' && data.error) {
|
|
316
|
+
send('patch_fail', String(data.error));
|
|
317
|
+
}
|
|
318
|
+
} catch (e: any) {
|
|
319
|
+
log('telemetry error:', e?.message || e);
|
|
320
|
+
}
|
|
321
|
+
};
|
|
245
322
|
throwIfEnabled = (e: Error) => {
|
|
246
323
|
if (this.options.throwError) {
|
|
247
324
|
throw e;
|
|
@@ -387,6 +464,7 @@ export class Pushy {
|
|
|
387
464
|
});
|
|
388
465
|
|
|
389
466
|
log('check endpoint success', endpoint);
|
|
467
|
+
this.lastWorkingEndpoint = endpoint;
|
|
390
468
|
return value;
|
|
391
469
|
};
|
|
392
470
|
assertDebug = (matter: string) => {
|
|
@@ -495,6 +573,8 @@ export class Pushy {
|
|
|
495
573
|
hash: currentVersion,
|
|
496
574
|
buildTime,
|
|
497
575
|
cInfo,
|
|
576
|
+
// 可消费的 diff 轨道版本(2 = hdiffv2 轨道),服务端据此门控下发
|
|
577
|
+
...(supportedDiffVersion ? { diffV: supportedDiffVersion } : {}),
|
|
498
578
|
...extra,
|
|
499
579
|
};
|
|
500
580
|
if (__DEV__) {
|
|
@@ -871,6 +951,49 @@ export class Pushy {
|
|
|
871
951
|
}
|
|
872
952
|
return PushyModule.restartApp();
|
|
873
953
|
};
|
|
954
|
+
/**
|
|
955
|
+
* Reset to the bundle packaged in the binary: wipes every downloaded update
|
|
956
|
+
* and the whole update state on the native side, so the app loads the
|
|
957
|
+
* built-in bundle on the next launch (or immediately with
|
|
958
|
+
* `{ restart: true }`). The client uuid is preserved.
|
|
959
|
+
*
|
|
960
|
+
* Returns whether the reset actually happened. Like the other update-flow
|
|
961
|
+
* APIs it never throws by default — failures land in lastError/onError with
|
|
962
|
+
* code RESET_FAILED — but the boolean must not be ignored: a false means the
|
|
963
|
+
* app is still running the hot-updated bundle. Set `throwError` to throw.
|
|
964
|
+
*/
|
|
965
|
+
resetToPackagedBundle = async (options?: {
|
|
966
|
+
restart?: boolean;
|
|
967
|
+
}): Promise<boolean> => {
|
|
968
|
+
if (typeof PushyModule.resetToPackagedBundle !== 'function') {
|
|
969
|
+
// The JS layer can arrive via hot update onto an older binary whose
|
|
970
|
+
// native module predates this method.
|
|
971
|
+
const err = new UpdateError(
|
|
972
|
+
this.t('error_reset_not_supported'),
|
|
973
|
+
'RESET_FAILED',
|
|
974
|
+
);
|
|
975
|
+
this.emitError(err, 'errorReset');
|
|
976
|
+
this.throwIfEnabled(err);
|
|
977
|
+
return false;
|
|
978
|
+
}
|
|
979
|
+
try {
|
|
980
|
+
await PushyModule.resetToPackagedBundle();
|
|
981
|
+
} catch (e) {
|
|
982
|
+
const err = toUpdateError(e, 'RESET_FAILED');
|
|
983
|
+
this.emitError(err, 'errorReset');
|
|
984
|
+
this.throwIfEnabled(err);
|
|
985
|
+
return false;
|
|
986
|
+
}
|
|
987
|
+
// The downloaded versions are gone; drop JS bookkeeping referring to them
|
|
988
|
+
// so a stale downloadedHash cannot be switched to.
|
|
989
|
+
sharedState.downloadedHash = undefined;
|
|
990
|
+
sharedState.marked = false;
|
|
991
|
+
this.report({ type: 'reset' });
|
|
992
|
+
if (options?.restart) {
|
|
993
|
+
await this.restartApp();
|
|
994
|
+
}
|
|
995
|
+
return true;
|
|
996
|
+
};
|
|
874
997
|
}
|
|
875
998
|
|
|
876
999
|
// for international users
|
package/src/context.ts
CHANGED
|
@@ -15,6 +15,7 @@ export const defaultContext = {
|
|
|
15
15
|
downloadUpdate: asyncNoop,
|
|
16
16
|
downloadAndInstallApk: asyncNoop,
|
|
17
17
|
restartApp: asyncNoop,
|
|
18
|
+
resetToPackagedBundle: asyncNoop,
|
|
18
19
|
getCurrentVersionInfo: () => Promise.resolve({}),
|
|
19
20
|
parseTestQrCode: () => false,
|
|
20
21
|
currentHash: '',
|
|
@@ -43,6 +44,9 @@ export const UpdateContext = createContext<{
|
|
|
43
44
|
} | null;
|
|
44
45
|
parseTestQrCode: (code: string) => boolean;
|
|
45
46
|
restartApp: () => Promise<void>;
|
|
47
|
+
resetToPackagedBundle: (options?: {
|
|
48
|
+
restart?: boolean;
|
|
49
|
+
}) => Promise<boolean | void>;
|
|
46
50
|
currentHash: string;
|
|
47
51
|
packageVersion: string;
|
|
48
52
|
client?: Pushy | Cresc;
|
package/src/core.ts
CHANGED
|
@@ -65,6 +65,10 @@ export const rolledBackVersion: string = PushyConstants.rolledBackVersion;
|
|
|
65
65
|
export const isRolledBack: boolean = !!rolledBackVersion;
|
|
66
66
|
|
|
67
67
|
export const buildTime: string = PushyConstants.buildTime;
|
|
68
|
+
// 原生 patch 内核可消费的 diff 轨道版本(2 = hdiffv2 轨道:HBC 变换 +
|
|
69
|
+
// 流式容器);旧原生无此常量时为 0(不上报,服务端只发 baseline)
|
|
70
|
+
export const supportedDiffVersion: number =
|
|
71
|
+
PushyConstants.supportedDiffVersion || 0;
|
|
68
72
|
let uuid = PushyConstants.uuid;
|
|
69
73
|
|
|
70
74
|
async function getLocalHashInfo(hash: string) {
|
package/src/error.ts
CHANGED
package/src/locales/en.ts
CHANGED
|
@@ -61,6 +61,8 @@ export default {
|
|
|
61
61
|
error_storage_permission_rejected:
|
|
62
62
|
'Storage permission denied, unable to download the APK',
|
|
63
63
|
error_apk_download_failed: 'Failed to download or install the APK',
|
|
64
|
+
error_reset_not_supported:
|
|
65
|
+
'resetToPackagedBundle is not available in the installed native module. Please rebuild the app with the latest react-native-update.',
|
|
64
66
|
|
|
65
67
|
// Development messages
|
|
66
68
|
dev_debug_disabled:
|
package/src/locales/zh.ts
CHANGED
|
@@ -56,6 +56,8 @@ export default {
|
|
|
56
56
|
error_apk_pending_install: '安装包已下载完成,请在系统安装界面完成安装',
|
|
57
57
|
error_storage_permission_rejected: '存储权限被拒绝,无法下载安装包',
|
|
58
58
|
error_apk_download_failed: '安装包下载或安装失败',
|
|
59
|
+
error_reset_not_supported:
|
|
60
|
+
'当前安装的原生模块不支持 resetToPackagedBundle,请使用最新版 react-native-update 重新打包后再试。',
|
|
59
61
|
|
|
60
62
|
// Development messages
|
|
61
63
|
dev_debug_disabled:
|
package/src/provider.tsx
CHANGED
|
@@ -388,6 +388,13 @@ export const UpdateProvider = ({
|
|
|
388
388
|
return client.restartApp();
|
|
389
389
|
}, [client]);
|
|
390
390
|
|
|
391
|
+
const resetToPackagedBundle = useCallback(
|
|
392
|
+
async (resetOptions?: { restart?: boolean }) => {
|
|
393
|
+
return client.resetToPackagedBundle(resetOptions);
|
|
394
|
+
},
|
|
395
|
+
[client],
|
|
396
|
+
);
|
|
397
|
+
|
|
391
398
|
useEffect(() => {
|
|
392
399
|
if (!assertWeb()) {
|
|
393
400
|
return;
|
|
@@ -438,6 +445,7 @@ export const UpdateProvider = ({
|
|
|
438
445
|
currentVersionInfo,
|
|
439
446
|
parseTestQrCode,
|
|
440
447
|
restartApp,
|
|
448
|
+
resetToPackagedBundle,
|
|
441
449
|
}),
|
|
442
450
|
[
|
|
443
451
|
checkUpdate,
|
|
@@ -452,6 +460,7 @@ export const UpdateProvider = ({
|
|
|
452
460
|
downloadAndInstallApk,
|
|
453
461
|
parseTestQrCode,
|
|
454
462
|
restartApp,
|
|
463
|
+
resetToPackagedBundle,
|
|
455
464
|
],
|
|
456
465
|
);
|
|
457
466
|
|
package/src/telemetry.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { UpdateErrorCode } from './error';
|
|
2
|
+
import type { EventType } from './type';
|
|
3
|
+
|
|
4
|
+
// Server-side lifecycle event intake (POST {endpoint}/report/{appKey}); the
|
|
5
|
+
// enum must stay in sync with the server's client_event_type. Only these five
|
|
6
|
+
// aggregate types exist server-side — everything else stays local-only.
|
|
7
|
+
export type ServerEventType =
|
|
8
|
+
| 'download_success'
|
|
9
|
+
| 'download_fail'
|
|
10
|
+
| 'patch_fail'
|
|
11
|
+
| 'rollback'
|
|
12
|
+
| 'mark_success';
|
|
13
|
+
|
|
14
|
+
// Failure detail is stored server-side in a VARCHAR(512); stay under it.
|
|
15
|
+
export const MAX_DETAIL_LENGTH = 500;
|
|
16
|
+
|
|
17
|
+
export interface ServerReportPayload {
|
|
18
|
+
type: ServerEventType;
|
|
19
|
+
hash: string;
|
|
20
|
+
packageVersion?: string;
|
|
21
|
+
cInfo?: {
|
|
22
|
+
rnu?: string;
|
|
23
|
+
rn?: string;
|
|
24
|
+
os?: string;
|
|
25
|
+
uuid?: string;
|
|
26
|
+
};
|
|
27
|
+
detail?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Maps a local report event to the server-side aggregate type, or undefined
|
|
32
|
+
* for events the server does not collect. A DOWNLOAD_FAILED whose underlying
|
|
33
|
+
* native code was PATCH_FAILED counts as patch_fail (the download itself
|
|
34
|
+
* worked; applying it did not), same for switch-version failures.
|
|
35
|
+
*/
|
|
36
|
+
export const resolveServerEventType = (
|
|
37
|
+
type: EventType,
|
|
38
|
+
code?: UpdateErrorCode,
|
|
39
|
+
): ServerEventType | undefined => {
|
|
40
|
+
switch (type) {
|
|
41
|
+
case 'downloadSuccess':
|
|
42
|
+
return 'download_success';
|
|
43
|
+
case 'errorUpdate':
|
|
44
|
+
return code === 'PATCH_FAILED' ? 'patch_fail' : 'download_fail';
|
|
45
|
+
case 'errorSwitchVersion':
|
|
46
|
+
return 'patch_fail';
|
|
47
|
+
case 'markSuccess':
|
|
48
|
+
return 'mark_success';
|
|
49
|
+
case 'rollback':
|
|
50
|
+
return 'rollback';
|
|
51
|
+
default:
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The version hash a server event is about. Server-side rows key on it, so an
|
|
58
|
+
* event without one is not reportable (returns '').
|
|
59
|
+
*/
|
|
60
|
+
export const resolveServerEventHash = ({
|
|
61
|
+
serverType,
|
|
62
|
+
data,
|
|
63
|
+
currentVersion,
|
|
64
|
+
}: {
|
|
65
|
+
serverType: ServerEventType;
|
|
66
|
+
data: Record<string, string | number>;
|
|
67
|
+
currentVersion: string;
|
|
68
|
+
}): string => {
|
|
69
|
+
if (serverType === 'rollback') {
|
|
70
|
+
return String(data.rolledBackVersion || '');
|
|
71
|
+
}
|
|
72
|
+
if (serverType === 'mark_success') {
|
|
73
|
+
return String(data.newVersion || currentVersion || '');
|
|
74
|
+
}
|
|
75
|
+
return String(data.newVersion || '');
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const truncateDetail = (detail?: string) => {
|
|
79
|
+
if (!detail) {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
return detail.slice(0, MAX_DETAIL_LENGTH);
|
|
83
|
+
};
|
package/src/type.ts
CHANGED
|
@@ -55,6 +55,8 @@ export type EventType =
|
|
|
55
55
|
| 'errorUpdate'
|
|
56
56
|
| 'markSuccess'
|
|
57
57
|
| 'errorMarkSuccess'
|
|
58
|
+
| 'reset'
|
|
59
|
+
| 'errorReset'
|
|
58
60
|
| 'errorSwitchVersion'
|
|
59
61
|
| 'downloadingApk'
|
|
60
62
|
| 'rejectStoragePermission'
|
|
@@ -129,6 +131,12 @@ export interface ClientOptions {
|
|
|
129
131
|
overridePackageVersion?: string;
|
|
130
132
|
/** Maximum number of retry attempts for failed downloads (default: 3) */
|
|
131
133
|
maxRetries?: number;
|
|
134
|
+
/**
|
|
135
|
+
* Disable reporting update lifecycle events (download/patch failures,
|
|
136
|
+
* rollback, mark success) to the update server. These aggregate stats power
|
|
137
|
+
* the version health view in the console. Default: false (enabled).
|
|
138
|
+
*/
|
|
139
|
+
disableTelemetry?: boolean;
|
|
132
140
|
}
|
|
133
141
|
|
|
134
142
|
export interface UpdateTestPayload {
|