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.
Files changed (60) hide show
  1. package/android/jni/Android.mk +2 -0
  2. package/android/jni/hpatch.c +57 -2
  3. package/android/lib/arm64-v8a/librnupdate.so +0 -0
  4. package/android/lib/armeabi-v7a/librnupdate.so +0 -0
  5. package/android/lib/x86/librnupdate.so +0 -0
  6. package/android/lib/x86_64/librnupdate.so +0 -0
  7. package/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +19 -4
  8. package/android/src/main/java/cn/reactnative/modules/update/DownloadTaskParams.java +2 -0
  9. package/android/src/main/java/cn/reactnative/modules/update/ErrorCodes.java +1 -0
  10. package/android/src/main/java/cn/reactnative/modules/update/NativeUpdateCore.java +12 -0
  11. package/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +30 -0
  12. package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleImpl.java +13 -0
  13. package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleSupport.java +1 -0
  14. package/android/src/newarch/cn/reactnative/modules/update/UpdateModule.java +5 -0
  15. package/android/src/oldarch/cn/reactnative/modules/update/UpdateModule.java +5 -0
  16. package/cpp/patch_core/error_codes.h +3 -0
  17. package/cpp/patch_core/hbc_transform.cpp +143 -0
  18. package/cpp/patch_core/hbc_transform.h +53 -0
  19. package/cpp/patch_core/hbc_transform_wire.cpp +321 -0
  20. package/cpp/patch_core/hbc_transform_wire.h +52 -0
  21. package/cpp/patch_core/patch_core.cpp +126 -4
  22. package/cpp/patch_core/patch_core.h +5 -0
  23. package/cpp/patch_core/patch_core_android.cpp +3 -1
  24. package/cpp/patch_core/tests/fixtures/rawstored.lzma2label.patch.bin +0 -0
  25. package/cpp/patch_core/tests/fixtures/rawstored.new.bin +0 -0
  26. package/cpp/patch_core/tests/fixtures/rawstored.old.bin +0 -0
  27. package/cpp/patch_core/tests/fixtures/v96.hbc +0 -0
  28. package/cpp/patch_core/tests/fixtures/v96.meta.json +1 -0
  29. package/cpp/patch_core/tests/fixtures/v96.streampatch.bin +0 -0
  30. package/cpp/patch_core/tests/fixtures/v96.t.hbc +0 -0
  31. package/cpp/patch_core/tests/fixtures/v96.tpatch.bin +0 -0
  32. package/cpp/patch_core/tests/fixtures/v96.tstreampatch.bin +0 -0
  33. package/cpp/patch_core/tests/fixtures/v96b.hbc +0 -0
  34. package/cpp/patch_core/tests/fixtures/v98.hbc +0 -0
  35. package/cpp/patch_core/tests/fixtures/v98.t.hbc +0 -0
  36. package/cpp/patch_core/tests/fixtures/v98b.hbc +0 -0
  37. package/cpp/patch_core/tests/fixtures/v98b.t.hbc +0 -0
  38. package/cpp/patch_core/tests/hbc_transform_test.cpp +229 -0
  39. package/cpp/patch_core/tests/patch_core_test.cpp +150 -1
  40. package/cpp/patch_core/update_core_android.cpp +10 -0
  41. package/harmony/pushy/src/main/cpp/CMakeLists.txt +2 -0
  42. package/harmony/pushy/src/main/cpp/pushy.cpp +18 -1
  43. package/harmony/pushy/src/main/ets/DownloadTask.ts +21 -0
  44. package/harmony/pushy/src/main/ets/NativePatchCore.ts +4 -0
  45. package/harmony/pushy/src/main/ets/PushyTurboModule.ts +12 -0
  46. package/harmony/pushy/src/main/ets/UpdateContext.ts +46 -0
  47. package/harmony/pushy.har +0 -0
  48. package/ios/RCTPushy/RCTPushy.mm +64 -0
  49. package/package.json +1 -1
  50. package/react-native-update.podspec +2 -0
  51. package/src/NativePushy.ts +1 -0
  52. package/src/client.ts +123 -0
  53. package/src/context.ts +4 -0
  54. package/src/core.ts +4 -0
  55. package/src/error.ts +1 -0
  56. package/src/locales/en.ts +2 -0
  57. package/src/locales/zh.ts +2 -0
  58. package/src/provider.tsx +9 -0
  59. package/src/telemetry.ts +83 -0
  60. package/src/type.ts +8 -0
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-update",
3
- "version": "10.46.0",
3
+ "version": "10.48.0",
4
4
  "description": "react-native hot update",
5
5
  "main": "src/index",
6
6
  "types": "src/index.ts",
@@ -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}',
@@ -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
@@ -26,6 +26,7 @@ export type UpdateErrorCode =
26
26
  | 'PATCH_FAILED'
27
27
  | 'FILE_OPERATION_FAILED'
28
28
  | 'RESTART_FAILED'
29
+ | 'RESET_FAILED'
29
30
  | 'INVALID_HASH_INFO'
30
31
  | 'UNSUPPORTED_PLATFORM';
31
32
 
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
 
@@ -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 {