react-native-update 10.59.0 → 10.59.2

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 (68) hide show
  1. package/android/build.gradle +46 -14
  2. package/android/jni/Android.mk +2 -1
  3. package/android/lib/arm64-v8a/librnpushy.so +0 -0
  4. package/android/lib/armeabi-v7a/librnpushy.so +0 -0
  5. package/android/lib/x86/librnpushy.so +0 -0
  6. package/android/lib/x86_64/librnpushy.so +0 -0
  7. package/android/src/main/java/cn/reactnative/modules/update/ApkInstaller.java +20 -16
  8. package/android/src/main/java/cn/reactnative/modules/update/BundledResourceCopier.java +4 -4
  9. package/android/src/main/java/cn/reactnative/modules/update/{CrashRescue.java → CrashHold.java} +12 -12
  10. package/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +28 -24
  11. package/android/src/main/java/cn/reactnative/modules/update/ErrorCodes.java +2 -1
  12. package/android/src/main/java/cn/reactnative/modules/update/{NativeUpdateFlow.java → FlowBridge.java} +6 -6
  13. package/android/src/main/java/cn/reactnative/modules/update/HttpUtils.java +6 -3
  14. package/android/src/main/java/cn/reactnative/modules/update/{NativeUpdateCore.java → NativeCore.java} +4 -4
  15. package/android/src/main/java/cn/reactnative/modules/update/PushyConfiguration.java +10 -5
  16. package/android/src/main/java/cn/reactnative/modules/update/PushyRuntime.java +4 -4
  17. package/android/src/main/java/cn/reactnative/modules/update/ReactReloadManager.java +7 -7
  18. package/android/src/main/java/cn/reactnative/modules/update/StateSerialRunner.java +1 -1
  19. package/android/src/main/java/cn/reactnative/modules/update/{NativeCheckOrchestrator.java → SyncCoordinator.java} +79 -75
  20. package/android/src/main/java/cn/reactnative/modules/update/Texts.java +28 -0
  21. package/android/src/main/java/cn/reactnative/modules/update/UiThreadRunner.java +1 -1
  22. package/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +28 -28
  23. package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleImpl.java +12 -9
  24. package/cpp/patch_core/archive_limits.h +5 -0
  25. package/cpp/patch_core/archive_patch_core.cpp +26 -19
  26. package/cpp/patch_core/archive_patch_core.h +17 -8
  27. package/cpp/patch_core/digest.h +6 -1
  28. package/cpp/patch_core/error_codes.h +8 -1
  29. package/cpp/patch_core/hbc_transform.h +5 -0
  30. package/cpp/patch_core/hbc_transform_wire.h +5 -0
  31. package/cpp/patch_core/install_record.h +5 -0
  32. package/cpp/patch_core/jni_natives.h +27 -0
  33. package/cpp/patch_core/jni_registration.cpp +103 -0
  34. package/cpp/patch_core/obscured_text.h +44 -0
  35. package/cpp/patch_core/patch_core.cpp +13 -13
  36. package/cpp/patch_core/patch_core.h +11 -6
  37. package/cpp/patch_core/patch_core_android.cpp +10 -10
  38. package/cpp/patch_core/state_core.h +5 -0
  39. package/cpp/patch_core/state_ops.h +5 -0
  40. package/cpp/patch_core/update_core_android.cpp +23 -22
  41. package/cpp/update_flow_core/flow_json.h +6 -1
  42. package/cpp/update_flow_core/update_flow_core.cpp +35 -18
  43. package/cpp/update_flow_core/update_flow_core.h +13 -8
  44. package/cpp/update_flow_core/update_flow_jni.cpp +11 -13
  45. package/harmony/pushy/src/main/cpp/CMakeLists.txt +10 -8
  46. package/harmony/pushy/src/main/cpp/pushy.cpp +33 -33
  47. package/harmony/pushy/src/main/ets/ArchiveLimits.ts +15 -15
  48. package/harmony/pushy/src/main/ets/DownloadTask.ts +60 -60
  49. package/harmony/pushy/src/main/ets/ErrorCodes.ts +13 -11
  50. package/harmony/pushy/src/main/ets/InstallRecord.ts +4 -4
  51. package/harmony/pushy/src/main/ets/NativePatchCore.ts +9 -9
  52. package/harmony/pushy/src/main/ets/PathUtils.ts +2 -2
  53. package/harmony/pushy/src/main/ets/PushyConfiguration.ts +8 -6
  54. package/harmony/pushy/src/main/ets/PushyFileJSBundleProvider.ets +1 -1
  55. package/harmony/pushy/src/main/ets/PushyTurboModule.ts +42 -41
  56. package/harmony/pushy/src/main/ets/{NativeCheckOrchestrator.ts → SyncCoordinator.ts} +33 -32
  57. package/harmony/pushy/src/main/ets/Texts.ts +28 -0
  58. package/harmony/pushy/src/main/ets/UpdateContext.ts +31 -30
  59. package/harmony/pushy.har +0 -0
  60. package/ios/RCTPushy/RCTPushy.mm +154 -140
  61. package/ios/RCTPushy/RCTPushyConfiguration.h +8 -0
  62. package/ios/RCTPushy/RCTPushyConfiguration.mm +32 -5
  63. package/package.json +1 -1
  64. package/react-native-update.podspec +16 -4
  65. package/android/lib/arm64-v8a/librnupdate.so +0 -0
  66. package/android/lib/armeabi-v7a/librnupdate.so +0 -0
  67. package/android/lib/x86/librnupdate.so +0 -0
  68. package/android/lib/x86_64/librnupdate.so +0 -0
@@ -10,9 +10,9 @@ import { UpdateContext } from './UpdateContext';
10
10
  import { EventHub } from './EventHub';
11
11
  import {
12
12
  KEY_CONFIG,
13
- markJsCheckCompleted,
13
+ recordJsRound,
14
14
  KEY_RESP_CACHE,
15
- } from './NativeCheckOrchestrator';
15
+ } from './SyncCoordinator';
16
16
  import {
17
17
  ERROR_FILE_OPERATION_FAILED,
18
18
  ERROR_INVALID_HASH_INFO,
@@ -22,10 +22,11 @@ import {
22
22
  ERROR_RESTART_FAILED,
23
23
  ERROR_SWITCH_VERSION_FAILED,
24
24
  ERROR_UNSUPPORTED_PLATFORM,
25
- createUpdateError,
25
+ createPushyError,
26
26
  getErrorMessage,
27
- toUpdateError,
27
+ toPushyError,
28
28
  } from './ErrorCodes';
29
+ import { DEVTOOLS_RESTART_EVENT, DEVTOOLS_RESTART_REASON, STORAGE_DIR_NAME } from './Texts';
29
30
 
30
31
  export { getErrorMessage } from './ErrorCodes';
31
32
 
@@ -41,13 +42,13 @@ interface RestartableApplicationContext {
41
42
  restartApp?: (want: RestartWant) => void;
42
43
  }
43
44
 
44
- interface ReloadEventEmitter {
45
+ interface RestartEventEmitter {
45
46
  emit(event: string, payload: Object): void;
46
47
  }
47
48
 
48
49
  // RNOH 的 devToolsController 不在公开的 UITurboModuleContext 类型里。
49
50
  interface DevToolsControllerHolder {
50
- devToolsController?: { eventEmitter: ReloadEventEmitter };
51
+ devToolsController?: { eventEmitter: RestartEventEmitter };
51
52
  }
52
53
 
53
54
  export function validateHashInfo(info: string): void {
@@ -59,7 +60,7 @@ export function validateHashInfo(info: string): void {
59
60
  valid = false;
60
61
  }
61
62
  if (!valid) {
62
- throw createUpdateError(ERROR_INVALID_HASH_INFO, 'invalid json string');
63
+ throw createPushyError(ERROR_INVALID_HASH_INFO, 'invalid json string');
63
64
  }
64
65
  }
65
66
 
@@ -96,7 +97,7 @@ export class PushyTurboModule extends UITurboModule {
96
97
 
97
98
  private requireHash(hash: string, methodName: string): string {
98
99
  if (!hash) {
99
- throw createUpdateError(
100
+ throw createPushyError(
100
101
  ERROR_INVALID_OPTIONS,
101
102
  `${methodName}: empty hash`,
102
103
  );
@@ -104,11 +105,11 @@ export class PushyTurboModule extends UITurboModule {
104
105
  return hash;
105
106
  }
106
107
 
107
- private softReload(): void {
108
+ private softRestart(): void {
108
109
  const holder = this.ctx as unknown as DevToolsControllerHolder;
109
110
  const devToolsController = holder.devToolsController;
110
111
  if (devToolsController) {
111
- devToolsController.eventEmitter.emit('RELOAD', { reason: 'HotReload2' });
112
+ devToolsController.eventEmitter.emit(DEVTOOLS_RESTART_EVENT, { reason: DEVTOOLS_RESTART_REASON });
112
113
  }
113
114
  }
114
115
 
@@ -146,10 +147,10 @@ export class PushyTurboModule extends UITurboModule {
146
147
 
147
148
  private async reloadBridge(): Promise<void> {
148
149
  if (this.ctx.isDebugModeEnabled) {
149
- logger.debug(TAG, 'reloadBridge via devToolsController RELOAD (debug mode)');
150
- this.softReload();
150
+ logger.debug(TAG, 'restart via devToolsController (debug mode)');
151
+ this.softRestart();
151
152
  } else {
152
- logger.debug(TAG, 'reloadBridge via restartAbility (release mode)');
153
+ logger.debug(TAG, 'restart via restartAbility (release mode)');
153
154
  // If the process truly restarts, this timer dies with it. It only fires
154
155
  // when the app is still alive after 1.5s — i.e. restartApp resolved but
155
156
  // was silently suppressed (HarmonyOS rate-limits restarts within a few
@@ -157,16 +158,16 @@ export class PushyTurboModule extends UITurboModule {
157
158
  // soft reload must take over. So the timer is NOT cleared on the success
158
159
  // path, only in the catch branch where the soft reload runs immediately.
159
160
  const fallbackTimer = setTimeout(() => {
160
- logger.warn(TAG, 'restartAbility did not restart the app within 1.5s, triggering soft reload fallback');
161
- this.softReload();
161
+ logger.warn(TAG, 'restartAbility did not restart the app within 1.5s, triggering soft restart fallback');
162
+ this.softRestart();
162
163
  }, 1500);
163
164
 
164
165
  try {
165
166
  await this.restartAbility();
166
167
  } catch (error) {
167
168
  clearTimeout(fallbackTimer);
168
- logger.error(TAG, `restartAbility failed: ${getErrorMessage(error)}, triggering soft reload fallback`);
169
- this.softReload();
169
+ logger.error(TAG, `restartAbility failed: ${getErrorMessage(error)}, triggering soft restart fallback`);
170
+ this.softRestart();
170
171
  }
171
172
  }
172
173
  }
@@ -193,7 +194,7 @@ export class PushyTurboModule extends UITurboModule {
193
194
  }
194
195
 
195
196
  const result = {
196
- downloadRootDir: `${this.mUiCtx.filesDir}/_update`,
197
+ downloadRootDir: `${this.mUiCtx.filesDir}/${STORAGE_DIR_NAME}`,
197
198
  currentVersionInfo,
198
199
  currentBundleSha256,
199
200
  packageVersion,
@@ -232,7 +233,7 @@ export class PushyTurboModule extends UITurboModule {
232
233
  try {
233
234
  await this.context.setKv(`hash_${hash}`, info);
234
235
  } catch (error) {
235
- throw toUpdateError(error, ERROR_FILE_OPERATION_FAILED);
236
+ throw toPushyError(error, ERROR_FILE_OPERATION_FAILED);
236
237
  }
237
238
  }
238
239
 
@@ -253,7 +254,7 @@ export class PushyTurboModule extends UITurboModule {
253
254
  try {
254
255
  await this.context.setKv('uuid', uuid);
255
256
  } catch (error) {
256
- throw toUpdateError(error, ERROR_FILE_OPERATION_FAILED);
257
+ throw toPushyError(error, ERROR_FILE_OPERATION_FAILED);
257
258
  }
258
259
  }
259
260
 
@@ -267,7 +268,7 @@ export class PushyTurboModule extends UITurboModule {
267
268
  try {
268
269
  JSON.parse(config);
269
270
  } catch (e) {
270
- throw createUpdateError(
271
+ throw createPushyError(
271
272
  ERROR_INVALID_OPTIONS,
272
273
  `syncNativeConfig: config is not valid JSON: ${getErrorMessage(e)}`,
273
274
  );
@@ -275,7 +276,7 @@ export class PushyTurboModule extends UITurboModule {
275
276
  try {
276
277
  await this.context.setNativeConfig(config);
277
278
  } catch (error) {
278
- throw toUpdateError(error, ERROR_FILE_OPERATION_FAILED);
279
+ throw toPushyError(error, ERROR_FILE_OPERATION_FAILED);
279
280
  }
280
281
  }
281
282
 
@@ -283,12 +284,12 @@ export class PushyTurboModule extends UITurboModule {
283
284
  async markJsCheckCompleted(config: string): Promise<void> {
284
285
  logger.debug(TAG, ',call markJsCheckCompleted');
285
286
  if (typeof config !== 'string' || config.length === 0) {
286
- throw createUpdateError(
287
+ throw createPushyError(
287
288
  ERROR_INVALID_OPTIONS,
288
289
  'config must be a non-empty string',
289
290
  );
290
291
  }
291
- markJsCheckCompleted(config);
292
+ recordJsRound(config);
292
293
  }
293
294
 
294
295
  // 原生冷启动检测落盘的原始响应缓存,JS 侧新鲜期内直接复用免二次请求
@@ -299,22 +300,22 @@ export class PushyTurboModule extends UITurboModule {
299
300
  }
300
301
 
301
302
  async reloadUpdate(options: { hash: string }): Promise<void> {
302
- logger.debug(TAG, ',call reloadUpdate');
303
- const hash = this.requireHash(options.hash, 'reloadUpdate');
303
+ logger.debug(TAG, ',call switch and restart');
304
+ const hash = this.requireHash(options.hash, 'switch and restart');
304
305
 
305
306
  // 切换必须真正落盘(switchVersion 内 await flush)后才重启:重启会立刻
306
307
  // 杀进程,未落盘的切换就是"重启回旧 bundle"。
307
308
  try {
308
309
  await this.context.switchVersion(hash);
309
310
  } catch (error) {
310
- logger.error(TAG, `reloadUpdate switch failed: ${getErrorMessage(error)}`);
311
- throw toUpdateError(error, ERROR_SWITCH_VERSION_FAILED);
311
+ logger.error(TAG, `switch failed: ${getErrorMessage(error)}`);
312
+ throw toPushyError(error, ERROR_SWITCH_VERSION_FAILED);
312
313
  }
313
314
  try {
314
315
  await this.reloadBridge();
315
316
  } catch (error) {
316
- logger.error(TAG, `reloadUpdate restart failed: ${getErrorMessage(error)}`);
317
- throw toUpdateError(error, ERROR_RESTART_FAILED);
317
+ logger.error(TAG, `restart failed: ${getErrorMessage(error)}`);
318
+ throw toPushyError(error, ERROR_RESTART_FAILED);
318
319
  }
319
320
  }
320
321
 
@@ -324,19 +325,19 @@ export class PushyTurboModule extends UITurboModule {
324
325
  await this.reloadBridge();
325
326
  } catch (error) {
326
327
  logger.error(TAG, `restartApp failed: ${getErrorMessage(error)}`);
327
- throw toUpdateError(error, ERROR_RESTART_FAILED);
328
+ throw toPushyError(error, ERROR_RESTART_FAILED);
328
329
  }
329
330
  }
330
331
 
331
332
  async setNeedUpdate(options: { hash: string }): Promise<void> {
332
- logger.debug(TAG, ',call setNeedUpdate');
333
- const hash = this.requireHash(options.hash, 'setNeedUpdate');
333
+ logger.debug(TAG, ',call select for next launch');
334
+ const hash = this.requireHash(options.hash, 'select for next launch');
334
335
 
335
336
  try {
336
337
  await this.context.switchVersion(hash);
337
338
  } catch (error) {
338
- logger.error(TAG, `setNeedUpdate failed: ${getErrorMessage(error)}`);
339
- throw toUpdateError(error, ERROR_SWITCH_VERSION_FAILED);
339
+ logger.error(TAG, `select for next launch failed: ${getErrorMessage(error)}`);
340
+ throw toPushyError(error, ERROR_SWITCH_VERSION_FAILED);
340
341
  }
341
342
  }
342
343
 
@@ -346,7 +347,7 @@ export class PushyTurboModule extends UITurboModule {
346
347
  await this.context.markSuccess();
347
348
  } catch (error) {
348
349
  logger.error(TAG, `markSuccess failed: ${getErrorMessage(error)}`);
349
- throw toUpdateError(error, ERROR_MARK_SUCCESS_FAILED);
350
+ throw toPushyError(error, ERROR_MARK_SUCCESS_FAILED);
350
351
  }
351
352
  }
352
353
 
@@ -367,7 +368,7 @@ export class PushyTurboModule extends UITurboModule {
367
368
  await this.context.resetToPackagedBundle();
368
369
  } catch (error) {
369
370
  logger.error(TAG, `resetToPackagedBundle failed: ${getErrorMessage(error)}`);
370
- throw toUpdateError(error, ERROR_RESET_FAILED);
371
+ throw toPushyError(error, ERROR_RESET_FAILED);
371
372
  }
372
373
  }
373
374
 
@@ -376,7 +377,7 @@ export class PushyTurboModule extends UITurboModule {
376
377
  hash: string;
377
378
  originHash: string;
378
379
  }): Promise<void> {
379
- logger.debug(TAG, ',call downloadPatchFromPpk');
380
+ logger.debug(TAG, ',call fetch ppk delta');
380
381
  return this.context.downloadPatchFromPpk(
381
382
  options.updateUrl,
382
383
  options.hash,
@@ -388,7 +389,7 @@ export class PushyTurboModule extends UITurboModule {
388
389
  updateUrl: string;
389
390
  hash: string;
390
391
  }): Promise<void> {
391
- logger.debug(TAG, ',call downloadPatchFromPackage');
392
+ logger.debug(TAG, ',call fetch package delta');
392
393
  return this.context.downloadPatchFromPackage(
393
394
  options.updateUrl,
394
395
  options.hash,
@@ -399,7 +400,7 @@ export class PushyTurboModule extends UITurboModule {
399
400
  updateUrl: string;
400
401
  hash: string;
401
402
  }): Promise<void> {
402
- logger.debug(TAG, ',call downloadFullUpdate');
403
+ logger.debug(TAG, ',call fetch full package');
403
404
  return this.context.downloadFullUpdate(options.updateUrl, options.hash);
404
405
  }
405
406
 
@@ -409,7 +410,7 @@ export class PushyTurboModule extends UITurboModule {
409
410
  hash: string;
410
411
  }): Promise<void> {
411
412
  logger.debug(TAG, ',call downloadAndInstallApk');
412
- throw createUpdateError(
413
+ throw createPushyError(
413
414
  ERROR_UNSUPPORTED_PLATFORM,
414
415
  'downloadAndInstallApk is only supported on Android',
415
416
  );
@@ -6,10 +6,11 @@ import type { UpdateContext } from './UpdateContext';
6
6
  import { BundlePreparationRound, bundlePreparationResult } from './BundlePreparationResult';
7
7
  import type { BundlePreparationResult } from './BundlePreparationResult';
8
8
  import { isSafePathComponent } from './PathUtils';
9
+ import { QUERY_PATH, STATUS_NONE } from './Texts';
9
10
  import { monotonicNowMs } from './MonotonicClock';
10
11
  import {
11
12
  ERROR_DOWNLOAD_FAILED,
12
- createUpdateError,
13
+ createPushyError,
13
14
  getErrorMessage,
14
15
  } from './ErrorCodes';
15
16
 
@@ -20,7 +21,7 @@ import {
20
21
  // 风暴,不拉黑版本。
21
22
  // 鸿蒙的 debug 门控由触发点天然承担:dev 走 MetroJSBundleProvider,
22
23
  // PushyFileJSBundleProvider.getBundleUrl 不会被调用。
23
- const TAG = 'NativeCheck';
24
+ const TAG = 'NativeSync';
24
25
  export const KEY_CONFIG = 'nativeConfig';
25
26
  // 供 JS 侧复用的原始响应缓存(§10.3),同时记录请求与配置指纹以限定命中范围。
26
27
  export const KEY_RESP_CACHE = 'nativeCheckResp';
@@ -31,7 +32,7 @@ export const KEY_RESP_CACHE = 'nativeCheckResp';
31
32
  export const KEY_ROUND_INCOMPLETE = 'nativeCheckIncomplete';
32
33
  const REQUEST_TIMEOUT_MS = 10000;
33
34
  const REQUEST_CALL_TIMEOUT_MS = 15000;
34
- const MAX_CHECK_HTTP_ATTEMPTS = 8;
35
+ const MAX_QUERY_HTTP_ATTEMPTS = 8;
35
36
  const START_DELAY_MS = 5000;
36
37
  const DOWNLOAD_PHASE_TIMEOUT_MS = 10 * 60 * 1000;
37
38
  const DOWNLOAD_TYPE_DIFF = 'diff';
@@ -62,7 +63,7 @@ interface FlowCInfo {
62
63
  uuid: string;
63
64
  }
64
65
 
65
- interface FlowCheckInput {
66
+ interface FlowInput {
66
67
  packageVersion: string;
67
68
  currentVersion?: string;
68
69
  buildTime: string;
@@ -129,7 +130,7 @@ function startNativeRound(
129
130
  try {
130
131
  await runOnce(context, launchRolledBackVersion);
131
132
  } catch (e) {
132
- logger.error(TAG, `native check failed: ${getErrorMessage(e)}`);
133
+ logger.error(TAG, `native sync failed: ${getErrorMessage(e)}`);
133
134
  roundResult = bundlePreparationResult('failed', 'internal_error');
134
135
  }
135
136
  return roundResult;
@@ -200,20 +201,20 @@ export async function prepareBundleNative(
200
201
  // 进程级:下次启动无信号,冷启动轮次照常运行。
201
202
  let jsCompletedConfig: string | undefined;
202
203
 
203
- export function markJsCheckCompleted(config: string): void {
204
+ export function recordJsRound(config: string): void {
204
205
  jsCompletedConfig = config;
205
206
  }
206
207
 
207
208
  // JS 已用与原生完全相同的配置拿到有效响应(§10.3):延迟轮次就是一次重复请求。
208
209
  // 只有计划内的轮次会问这个问题——JS 没起来、启动即崩、检查失败时都不会有信号。
209
- function isJsCheckCompleted(context: UpdateContext): boolean {
210
+ function hasJsRound(context: UpdateContext): boolean {
210
211
  return (
211
212
  jsCompletedConfig !== undefined &&
212
213
  jsCompletedConfig === context.getKv(KEY_CONFIG)
213
214
  );
214
215
  }
215
216
 
216
- export function scheduleNativeCheck(
217
+ export function scheduleNativeSync(
217
218
  context: UpdateContext,
218
219
  launchRolledBackVersion: string,
219
220
  ): void {
@@ -227,16 +228,16 @@ export function scheduleNativeCheck(
227
228
  // 除非上个进程死于轮中(残留标记),那时每一秒启动时间都要用来续传。
228
229
  const delayMs = context.getKv(KEY_ROUND_INCOMPLETE) ? 0 : START_DELAY_MS;
229
230
  setTimeout(() => {
230
- if (isJsCheckCompleted(context)) {
231
+ if (hasJsRound(context)) {
231
232
  logger.info(
232
233
  TAG,
233
- 'native check skipped: JS check completed in this process',
234
+ 'native sync skipped: JS already queried in this process',
234
235
  );
235
236
  return;
236
237
  }
237
238
  startNativeRound(context, launchRolledBackVersion).catch((e: Object) => {
238
239
  // 救援路径自身绝不能把应用拖垮。
239
- logger.error(TAG, `native check failed: ${getErrorMessage(e)}`);
240
+ logger.error(TAG, `native sync failed: ${getErrorMessage(e)}`);
240
241
  });
241
242
  }, delayMs);
242
243
  }
@@ -361,7 +362,7 @@ async function runConfiguredRound(
361
362
  uuid,
362
363
  };
363
364
 
364
- const input: FlowCheckInput = {
365
+ const input: FlowInput = {
365
366
  packageVersion: identity.packageVersion,
366
367
  currentVersion,
367
368
  buildTime: context.getBuildTime(),
@@ -369,14 +370,14 @@ async function runConfiguredRound(
369
370
  supportedDiffVersion: NativePatchCore.getSupportedDiffVersion(),
370
371
  bundleHash: await context.getBundleHash(),
371
372
  };
372
- const body = NativePatchCore.buildCheckRequestBody(JSON.stringify(input));
373
+ const body = NativePatchCore.buildRequestBody(JSON.stringify(input));
373
374
  if (!body) {
374
375
  roundResult = bundlePreparationResult('failed', 'invalid_request');
375
376
  return;
376
377
  }
377
378
 
378
379
  const httpsOnly = isHttpsOnly(config);
379
- const responseText = await runCheckRequest(config, appKey, body, httpsOnly);
380
+ const responseText = await runQueryRequest(config, appKey, body, httpsOnly);
380
381
  if (!responseText) {
381
382
  logger.warn(TAG, 'no endpoint reachable, giving up until next launch');
382
383
  return;
@@ -384,7 +385,7 @@ async function runConfiguredRound(
384
385
  // Anchor cache freshness to response arrival, before download/patch work.
385
386
  const responseAtSeconds = Math.floor(Date.now() / 1000);
386
387
 
387
- const decisionJson = NativePatchCore.handleCheckResponse(
388
+ const decisionJson = NativePatchCore.handleResponse(
388
389
  responseText,
389
390
  JSON.stringify(identity),
390
391
  config.afterDownload ?? '',
@@ -395,7 +396,7 @@ async function runConfiguredRound(
395
396
  }
396
397
  const decision = JSON.parse(decisionJson) as Decision;
397
398
  if (decision.action !== 'download') {
398
- const committed = await context.commitNativeCheckResult(
399
+ const committed = await context.commitSyncResult(
399
400
  resetGeneration,
400
401
  '',
401
402
  '',
@@ -403,7 +404,7 @@ async function runConfiguredRound(
403
404
  buildResponseCacheJson(configJson, body, responseText, responseAtSeconds),
404
405
  );
405
406
  roundResult = committed
406
- ? bundlePreparationResult('noUpdate', decision.reason ?? '')
407
+ ? bundlePreparationResult(STATUS_NONE, decision.reason ?? '')
407
408
  : bundlePreparationResult('cancelled', 'reset');
408
409
  logger.info(TAG, `nothing to do (${decision.reason ?? ''})`);
409
410
  return;
@@ -430,7 +431,7 @@ async function runConfiguredRound(
430
431
  }
431
432
  if (!downloaded) {
432
433
  logger.warn(TAG, `all download attempts for ${hash} failed`);
433
- const committed = await context.commitNativeCheckResult(
434
+ const committed = await context.commitSyncResult(
434
435
  resetGeneration,
435
436
  '',
436
437
  '',
@@ -466,13 +467,13 @@ async function runConfiguredRound(
466
467
  hashInfoJson = JSON.stringify(hashInfo);
467
468
  }
468
469
 
469
- // 版本元信息、激活与响应缓存一次性原子提交(见 commitNativeCheckResult);
470
+ // 版本元信息、激活与响应缓存一次性原子提交(见 commitSyncResult);
470
471
  // 缓存只在原生文件/状态工作结束后公开,避免 JS 观察到响应后并发下载。
471
472
  // 静默策略、或服务端按版本标记的 forceBoot(远程覆盖,救砖指令)才激活。
472
473
  const activate = decision.activate === true;
473
474
  let committed = false;
474
475
  try {
475
- committed = await context.commitNativeCheckResult(
476
+ committed = await context.commitSyncResult(
476
477
  resetGeneration,
477
478
  hash,
478
479
  hashInfoJson,
@@ -511,12 +512,12 @@ function buildResponseCacheJson(
511
512
  return JSON.stringify(cacheEntry);
512
513
  }
513
514
 
514
- function isValidCheckResponse(responseText: string | undefined): boolean {
515
+ function isValidResponse(responseText: string | undefined): boolean {
515
516
  if (responseText === undefined) {
516
517
  return false;
517
518
  }
518
519
  try {
519
- return NativePatchCore.isValidCheckResponse(responseText);
520
+ return NativePatchCore.isValidResponse(responseText);
520
521
  } catch (e) {
521
522
  return false;
522
523
  }
@@ -570,7 +571,7 @@ async function httpRequest(
570
571
  // 顺序回退(§5.1):按纯层给出的候选序逐个请求,单请求超时;整轮失败后经
571
572
  // queryUrls 发现远程候选(排除已试过的)再来一轮。刻意不做 hedged race——
572
573
  // 该路径对延迟不敏感。
573
- async function runCheckRequest(
574
+ async function runQueryRequest(
574
575
  config: NativeConfig,
575
576
  appKey: string,
576
577
  body: string,
@@ -591,12 +592,12 @@ async function runCheckRequest(
591
592
  if (!base || tried.has(base)) {
592
593
  continue;
593
594
  }
594
- if (httpAttempts++ >= MAX_CHECK_HTTP_ATTEMPTS) {
595
+ if (httpAttempts++ >= MAX_QUERY_HTTP_ATTEMPTS) {
595
596
  return undefined;
596
597
  }
597
598
  tried.add(base);
598
- const response = await httpRequest(`${base}/checkUpdate/${appKey}`, body);
599
- if (isValidCheckResponse(response)) {
599
+ const response = await httpRequest(`${base}${QUERY_PATH}${appKey}`, body);
600
+ if (isValidResponse(response)) {
600
601
  return response;
601
602
  }
602
603
  }
@@ -604,7 +605,7 @@ async function runCheckRequest(
604
605
  if (!listUrl) {
605
606
  continue;
606
607
  }
607
- if (httpAttempts++ >= MAX_CHECK_HTTP_ATTEMPTS) {
608
+ if (httpAttempts++ >= MAX_QUERY_HTTP_ATTEMPTS) {
608
609
  return undefined;
609
610
  }
610
611
  const listText = await httpRequest(listUrl);
@@ -634,12 +635,12 @@ async function runCheckRequest(
634
635
  logger.warn(TAG, `ignoring non-https remote endpoint ${base}`);
635
636
  continue;
636
637
  }
637
- if (httpAttempts++ >= MAX_CHECK_HTTP_ATTEMPTS) {
638
+ if (httpAttempts++ >= MAX_QUERY_HTTP_ATTEMPTS) {
638
639
  return undefined;
639
640
  }
640
641
  tried.add(base);
641
- const response = await httpRequest(`${base}/checkUpdate/${appKey}`, body);
642
- if (isValidCheckResponse(response)) {
642
+ const response = await httpRequest(`${base}${QUERY_PATH}${appKey}`, body);
643
+ if (isValidResponse(response)) {
643
644
  return response;
644
645
  }
645
646
  }
@@ -659,7 +660,7 @@ async function runWithinDeadline(
659
660
  ): Promise<void> {
660
661
  const remainingMs = deadlineUptimeMs - monotonicNowMs();
661
662
  if (remainingMs <= 0) {
662
- throw createUpdateError(
663
+ throw createPushyError(
663
664
  ERROR_DOWNLOAD_FAILED,
664
665
  'Download phase deadline expired before start',
665
666
  );
@@ -668,7 +669,7 @@ async function runWithinDeadline(
668
669
  const deadlinePromise = new Promise<void>((_, reject) => {
669
670
  deadlineTimer = setTimeout(() => {
670
671
  reject(
671
- createUpdateError(
672
+ createPushyError(
672
673
  ERROR_DOWNLOAD_FAILED,
673
674
  'Download phase deadline exceeded',
674
675
  ),
@@ -0,0 +1,28 @@
1
+ // Encoded text shared by the Harmony module. No imports, so any file can use it
2
+ // without forming an import cycle.
3
+
4
+ // Decodes text produced by scripts/encode-native-text.ts (byte i XORed with
5
+ // (0x5A + 0x1D * i) & 0xFF), keeping service addresses and paths out of static
6
+ // string scans of the package. Not a secret.
7
+ export function revealText(hex: string): string {
8
+ let text = '';
9
+ for (let i = 0; i < hex.length / 2; i++) {
10
+ const byte = parseInt(hex.substring(i * 2, i * 2 + 2), 16);
11
+ text += String.fromCharCode(byte ^ ((0x5a + 0x1d * i) & 0xff));
12
+ }
13
+ return text;
14
+ }
15
+
16
+ // Request path appended to an endpoint base.
17
+ export const QUERY_PATH: string = revealText('7514fcd4ad805d55263e08fc99');
18
+
19
+ // Protocol values, version-info flags and storage names, stored encoded.
20
+ export const STATUS_NONE: string = revealText('3418c1c1aa8a7c40');
21
+ export const POLICY_NEXT_LAUNCH: string = revealText('2912e0ffab8e6c70323b1dedd3');
22
+ export const STORAGE_DIR_NAME: string = revealText('0502e4d5af9f6d');
23
+ export const PREFERENCES_NAME: string = revealText('2f07f0d0ba8e');
24
+ export const PPK_DELTA_SUFFIX: string = revealText('7407e4dae09b69512137');
25
+ export const APP_DELTA_SUFFIX: string = revealText('7416e4c1e09b69512137');
26
+ export const BUNDLE_DELTA_ENTRY: string = revealText('3802fad5a28e264d232d11f6d8aade67596914e0ead8b0');
27
+ export const DEVTOOLS_RESTART_EVENT: string = revealText('0832d8fe8faf');
28
+ export const DEVTOOLS_RESTART_REASON: string = revealText('1218e0e3ab876744266d');