react-native-update 10.48.2 → 10.49.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 (46) hide show
  1. package/BUNDLEHASH_MIGRATION.md +335 -0
  2. package/NATIVE_CHECKUPDATE_DESIGN.md +192 -0
  3. package/README-CN.md +40 -4
  4. package/README.md +40 -4
  5. package/android/jni/Android.mk +1 -0
  6. package/android/lib/arm64-v8a/librnupdate.so +0 -0
  7. package/android/lib/armeabi-v7a/librnupdate.so +0 -0
  8. package/android/lib/x86/librnupdate.so +0 -0
  9. package/android/lib/x86_64/librnupdate.so +0 -0
  10. package/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +89 -0
  11. package/android/src/main/java/cn/reactnative/modules/update/UpdateEventEmitter.java +13 -4
  12. package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleImpl.java +11 -0
  13. package/android/src/newarch/cn/reactnative/modules/update/UpdateModule.java +5 -0
  14. package/android/src/oldarch/cn/reactnative/modules/update/UpdateModule.java +5 -0
  15. package/biome.json +59 -0
  16. package/cpp/patch_core/digest.cpp +177 -0
  17. package/cpp/patch_core/digest.h +43 -0
  18. package/cpp/patch_core/tests/patch_core_test.cpp +70 -0
  19. package/harmony/pushy/src/main/cpp/CMakeLists.txt +1 -0
  20. package/harmony/pushy/src/main/cpp/pushy.cpp +47 -0
  21. package/harmony/pushy/src/main/ets/DownloadTask.ts +2 -2
  22. package/harmony/pushy/src/main/ets/NativePatchCore.ts +2 -0
  23. package/harmony/pushy/src/main/ets/PushyTurboModule.ts +11 -0
  24. package/harmony/pushy/src/main/ets/UpdateContext.ts +54 -0
  25. package/harmony/pushy.har +0 -0
  26. package/ios/RCTPushy/RCTPushy.mm +55 -0
  27. package/package.json +5 -6
  28. package/react-native-update.podspec +1 -0
  29. package/src/NativePushy.ts +7 -1
  30. package/src/client.ts +83 -57
  31. package/src/context.ts +7 -7
  32. package/src/core.ts +78 -51
  33. package/src/endpoint.ts +7 -7
  34. package/src/error.ts +3 -3
  35. package/src/i18n.ts +2 -2
  36. package/src/index.ts +4 -4
  37. package/src/isInRollout.ts +10 -1
  38. package/src/locales/en.ts +2 -0
  39. package/src/locales/zh.ts +2 -0
  40. package/src/provider.tsx +46 -56
  41. package/src/resolveCheckResult.ts +1 -1
  42. package/src/telemetry.ts +1 -1
  43. package/src/type.ts +31 -5
  44. package/src/utils.ts +30 -30
  45. package/.eslintrc.js +0 -4
  46. package/.prettierrc.js +0 -6
package/src/endpoint.ts CHANGED
@@ -28,7 +28,7 @@ const normalizeError = (error: unknown) => {
28
28
  };
29
29
 
30
30
  export const dedupeEndpoints = (
31
- endpoints: Array<string | null | undefined>,
31
+ endpoints: Array<string | null | undefined>
32
32
  ): string[] => {
33
33
  const result: string[] = [];
34
34
  const visited = new Set<string>();
@@ -46,7 +46,7 @@ export const dedupeEndpoints = (
46
46
 
47
47
  export const pickRandomEndpoint = (
48
48
  endpoints: string[],
49
- random: () => number = Math.random,
49
+ random: () => number = Math.random
50
50
  ) => {
51
51
  if (!endpoints.length) {
52
52
  throw new UpdateError('No endpoints configured', 'NO_ENDPOINTS');
@@ -57,13 +57,13 @@ export const pickRandomEndpoint = (
57
57
  export async function selectFastestSuccessfulEndpoint<T>(
58
58
  endpoints: string[],
59
59
  tryEndpoint: (endpoint: string) => Promise<T>,
60
- now: () => number = Date.now,
60
+ now: () => number = Date.now
61
61
  ): Promise<{
62
62
  successes: EndpointAttemptSuccess<T>[];
63
63
  failures: EndpointAttemptFailure[];
64
64
  }> {
65
65
  const attempts = await Promise.all(
66
- endpoints.map(async endpoint => {
66
+ endpoints.map(async (endpoint) => {
67
67
  const start = now();
68
68
  try {
69
69
  const value = await tryEndpoint(endpoint);
@@ -80,7 +80,7 @@ export async function selectFastestSuccessfulEndpoint<T>(
80
80
  error: normalizeError(error),
81
81
  };
82
82
  }
83
- }),
83
+ })
84
84
  );
85
85
 
86
86
  const successes: EndpointAttemptSuccess<T>[] = [];
@@ -147,7 +147,7 @@ export async function executeEndpointFallback<T>({
147
147
  ? await getRemoteEndpoints().catch(() => [])
148
148
  : [];
149
149
  candidates = dedupeEndpoints([...candidates, ...remoteEndpoints]).filter(
150
- endpoint => !excludedEndpoints.has(endpoint),
150
+ (endpoint) => !excludedEndpoints.has(endpoint)
151
151
  );
152
152
 
153
153
  if (!candidates.length) {
@@ -157,7 +157,7 @@ export async function executeEndpointFallback<T>({
157
157
  const { successes, failures } = await selectFastestSuccessfulEndpoint(
158
158
  candidates,
159
159
  tryEndpoint,
160
- now,
160
+ now
161
161
  );
162
162
 
163
163
  if (successes.length) {
package/src/error.ts CHANGED
@@ -62,7 +62,7 @@ const KNOWN_CODES = new Set<string>([
62
62
  * else is treated as absent so it never leaks into telemetry aggregation.
63
63
  */
64
64
  export const asUpdateErrorCode = (
65
- code: unknown,
65
+ code: unknown
66
66
  ): UpdateErrorCode | undefined =>
67
67
  typeof code === 'string' && KNOWN_CODES.has(code)
68
68
  ? (code as UpdateErrorCode)
@@ -76,7 +76,7 @@ export class UpdateError extends Error {
76
76
  constructor(
77
77
  message: string,
78
78
  code: UpdateErrorCode,
79
- options?: { cause?: unknown; extra?: Record<string, string | number> },
79
+ options?: { cause?: unknown; extra?: Record<string, string | number> }
80
80
  ) {
81
81
  super(message);
82
82
  this.name = 'UpdateError';
@@ -96,7 +96,7 @@ export class UpdateError extends Error {
96
96
  */
97
97
  export const toUpdateError = (
98
98
  e: unknown,
99
- code: UpdateErrorCode,
99
+ code: UpdateErrorCode
100
100
  ): UpdateError => {
101
101
  if (e instanceof Error) {
102
102
  const err = e as UpdateError;
package/src/i18n.ts CHANGED
@@ -1,5 +1,5 @@
1
- import zhTranslations from './locales/zh';
2
1
  import enTranslations from './locales/en';
2
+ import zhTranslations from './locales/zh';
3
3
 
4
4
  type TranslationKey = keyof typeof zhTranslations | keyof typeof enTranslations;
5
5
  type TranslationValues = Record<string, string | number>;
@@ -92,7 +92,7 @@ class I18n {
92
92
  const i18n = new I18n();
93
93
 
94
94
  // Export both the instance and the class for flexibility
95
- export { i18n, I18n };
95
+ export { I18n, i18n };
96
96
  export default i18n;
97
97
 
98
98
  /**
package/src/index.ts CHANGED
@@ -1,7 +1,5 @@
1
- export { Pushy, Cresc } from './client';
2
1
  export type { UpdateErrorListener } from './client';
3
- export { UpdateError } from './error';
4
- export type { UpdateErrorCode } from './error';
2
+ export { Cresc, Pushy } from './client';
5
3
  export {
6
4
  ProgressContext,
7
5
  UpdateContext,
@@ -9,5 +7,7 @@ export {
9
7
  useUpdate,
10
8
  useUpdateProgress,
11
9
  } from './context';
12
- export { PushyProvider, UpdateProvider } from './provider';
13
10
  export { PushyModule, UpdateModule } from './core';
11
+ export type { UpdateErrorCode } from './error';
12
+ export { UpdateError } from './error';
13
+ export { PushyProvider, UpdateProvider } from './provider';
@@ -4,7 +4,14 @@ import { cInfo } from './core';
4
4
 
5
5
  /* eslint-disable no-bitwise */
6
6
  export function murmurhash3_32_gc(key: string, seed = 0) {
7
- let remainder, bytes, h1, h1b, c1, c2, k1, i;
7
+ let remainder: number,
8
+ bytes: number,
9
+ h1: number,
10
+ h1b: number,
11
+ c1: number,
12
+ c2: number,
13
+ k1: number,
14
+ i: number;
8
15
 
9
16
  remainder = key.length & 3; // key.length % 4
10
17
  bytes = key.length - remainder;
@@ -36,8 +43,10 @@ export function murmurhash3_32_gc(key: string, seed = 0) {
36
43
  k1 = 0;
37
44
 
38
45
  switch (remainder) {
46
+ // biome-ignore lint/suspicious/noFallthroughSwitchClause: MurmurHash fallthrough
39
47
  case 3:
40
48
  k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16;
49
+ // biome-ignore lint/suspicious/noFallthroughSwitchClause: MurmurHash fallthrough
41
50
  case 2:
42
51
  k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8;
43
52
  case 1:
package/src/locales/en.ts CHANGED
@@ -44,6 +44,8 @@ export default {
44
44
  // Error messages
45
45
  error_appkey_required: 'appKey is required',
46
46
  error_parse_version_info: 'Failed to parse currentVersionInfo: {{info}}',
47
+ warn_unknown_bundle:
48
+ 'The bundle embedded in this binary is not registered on the update platform; incremental updates are degraded to full downloads. Upload the package with pushy uploadIpa / uploadApk / uploadApp.',
47
49
  error_update_check_failed: 'Update check failed',
48
50
  error_cannot_connect_server:
49
51
  'Can not connect to update server. Please check your network.',
package/src/locales/zh.ts CHANGED
@@ -42,6 +42,8 @@ export default {
42
42
  // Error messages
43
43
  error_appkey_required: '需要提供 appKey',
44
44
  error_parse_version_info: '解析 currentVersionInfo 失败: {{info}}',
45
+ warn_unknown_bundle:
46
+ '当前二进制内嵌 bundle 未在更新平台注册,增量更新已降级为全量。请使用 pushy uploadIpa / uploadApk / uploadApp 上传对应安装包。',
45
47
  error_update_check_failed: '更新检查失败',
46
48
  error_cannot_connect_server: '无法连接到更新服务器。请检查网络连接。',
47
49
  error_cannot_connect_backup:
package/src/provider.tsx CHANGED
@@ -1,5 +1,6 @@
1
+ // biome-ignore lint/correctness/noUnusedImports: preserve React import for compatibility with older React versions
1
2
  import React, {
2
- ReactNode,
3
+ type ReactNode,
3
4
  useCallback,
4
5
  useEffect,
5
6
  useMemo,
@@ -8,26 +9,22 @@ import React, {
8
9
  } from 'react';
9
10
  import {
10
11
  Alert,
11
- NativeEventSubscription,
12
12
  AppState,
13
- Platform,
14
13
  Linking,
14
+ type NativeEventSubscription,
15
+ Platform,
15
16
  } from 'react-native';
16
- import { Pushy, Cresc, sharedState } from './client';
17
+ import { URL } from 'react-native-url-polyfill';
18
+ import { type Cresc, type Pushy, sharedState } from './client';
19
+ import { ProgressContext, UpdateContext } from './context';
17
20
  import {
18
21
  currentVersion,
19
- packageVersion,
20
- getCurrentVersionInfo,
21
22
  currentVersionInfo,
23
+ getCurrentVersionInfo,
24
+ packageVersion,
22
25
  } from './core';
23
- import {
24
- CheckResult,
25
- ProgressData,
26
- UpdateTestPayload,
27
- } from './type';
28
- import { ProgressContext, UpdateContext } from './context';
29
- import { URL } from 'react-native-url-polyfill';
30
26
  import { resolveCheckResult } from './resolveCheckResult';
27
+ import type { CheckResult, ProgressData, UpdateTestPayload } from './type';
31
28
  import { assertWeb, log, noop } from './utils';
32
29
 
33
30
  export const UpdateProvider = ({
@@ -59,7 +56,7 @@ export const UpdateProvider = ({
59
56
  Alert.alert(...args);
60
57
  }
61
58
  },
62
- [options.updateStrategy],
59
+ [options.updateStrategy]
63
60
  );
64
61
 
65
62
  const alertError = useCallback(
@@ -68,7 +65,7 @@ export const UpdateProvider = ({
68
65
  Alert.alert(...args);
69
66
  }
70
67
  },
71
- [options.updateStrategy],
68
+ [options.updateStrategy]
72
69
  );
73
70
 
74
71
  // All client errors flow through this single subscription (regardless of
@@ -82,35 +79,35 @@ export const UpdateProvider = ({
82
79
  client.t(
83
80
  eventType === 'errorChecking'
84
81
  ? 'error_update_check_failed'
85
- : 'update_failed',
82
+ : 'update_failed'
86
83
  ),
87
- e.message,
84
+ e.message
88
85
  );
89
86
  }),
90
- [client, alertError],
87
+ [client, alertError]
91
88
  );
92
89
 
93
90
  const switchVersion = useCallback(
94
91
  async (info: CheckResult | undefined = updateInfoRef.current) => {
95
- if (info && info.hash) {
92
+ if (info?.hash) {
96
93
  return client.switchVersion(info.hash);
97
94
  }
98
95
  },
99
- [client],
96
+ [client]
100
97
  );
101
98
 
102
99
  const switchVersionLater = useCallback(
103
100
  async (info: CheckResult | undefined = updateInfoRef.current) => {
104
- if (info && info.hash) {
101
+ if (info?.hash) {
105
102
  return client.switchVersionLater(info.hash);
106
103
  }
107
104
  },
108
- [client],
105
+ [client]
109
106
  );
110
107
 
111
108
  const downloadUpdate = useCallback(
112
109
  async (info: CheckResult | undefined = updateInfoRef.current) => {
113
- if (!info || !info.update) {
110
+ if (!info?.update) {
114
111
  return false;
115
112
  }
116
113
  try {
@@ -167,7 +164,7 @@ export const UpdateProvider = ({
167
164
  return false;
168
165
  }
169
166
  },
170
- [client, options, alertUpdate, alertError],
167
+ [client, options, alertUpdate, alertError]
171
168
  );
172
169
 
173
170
  const downloadAndInstallApk = useCallback(
@@ -176,7 +173,7 @@ export const UpdateProvider = ({
176
173
  await client.downloadAndInstallApk(downloadUrl, setProgress);
177
174
  }
178
175
  },
179
- [client],
176
+ [client]
180
177
  );
181
178
 
182
179
  const checkUpdate = useCallback(
@@ -229,25 +226,18 @@ export const UpdateProvider = ({
229
226
  }
230
227
  return info;
231
228
  }
232
- alertUpdate(
233
- client.t('alert_title'),
234
- client.t('alert_app_updated'),
235
- [
236
- {
237
- text: client.t('alert_update_button'),
238
- onPress: () => {
239
- if (
240
- Platform.OS === 'android' &&
241
- downloadUrl.endsWith('.apk')
242
- ) {
243
- downloadAndInstallApk(downloadUrl).catch(noop);
244
- } else {
245
- Linking.openURL(downloadUrl);
246
- }
247
- },
229
+ alertUpdate(client.t('alert_title'), client.t('alert_app_updated'), [
230
+ {
231
+ text: client.t('alert_update_button'),
232
+ onPress: () => {
233
+ if (Platform.OS === 'android' && downloadUrl.endsWith('.apk')) {
234
+ downloadAndInstallApk(downloadUrl).catch(noop);
235
+ } else {
236
+ Linking.openURL(downloadUrl);
237
+ }
248
238
  },
249
- ],
250
- );
239
+ },
240
+ ]);
251
241
  }
252
242
  } else if (info.update) {
253
243
  if (
@@ -272,7 +262,7 @@ export const UpdateProvider = ({
272
262
  downloadUpdate().catch(noop);
273
263
  },
274
264
  },
275
- ],
265
+ ]
276
266
  );
277
267
  }
278
268
  return info;
@@ -284,7 +274,7 @@ export const UpdateProvider = ({
284
274
  alertError,
285
275
  downloadAndInstallApk,
286
276
  downloadUpdate,
287
- ],
277
+ ]
288
278
  );
289
279
 
290
280
  const markSuccess = client.markSuccess;
@@ -307,11 +297,11 @@ export const UpdateProvider = ({
307
297
  if (checkStrategy === 'both' || checkStrategy === 'onAppResume') {
308
298
  stateListener.current = AppState.addEventListener(
309
299
  'change',
310
- nextAppState => {
300
+ (nextAppState) => {
311
301
  if (nextAppState === 'active') {
312
302
  checkUpdate().catch(noop);
313
303
  }
314
- },
304
+ }
315
305
  );
316
306
  }
317
307
  if (checkStrategy === 'both' || checkStrategy === 'onAppStart') {
@@ -321,9 +311,9 @@ export const UpdateProvider = ({
321
311
  if (markSuccessTimer) {
322
312
  clearTimeout(markSuccessTimer);
323
313
  }
324
- stateListener.current && stateListener.current.remove();
314
+ stateListener.current?.remove();
325
315
  };
326
- }, [checkUpdate, options, dismissError, markSuccess, client]);
316
+ }, [checkUpdate, options, markSuccess, client]);
327
317
 
328
318
  useEffect(() => {
329
319
  const { dismissErrorAfter } = options;
@@ -343,7 +333,7 @@ export const UpdateProvider = ({
343
333
 
344
334
  const parseTestPayload = useCallback(
345
335
  (payload: UpdateTestPayload) => {
346
- if (payload && payload.type && payload.type.startsWith('__rnPushy')) {
336
+ if (payload?.type?.startsWith('__rnPushy')) {
347
337
  const logger = options.logger || (() => {});
348
338
  options.logger = ({ type, data }) => {
349
339
  logger({ type, data });
@@ -354,10 +344,10 @@ export const UpdateProvider = ({
354
344
  sharedState.toHash = toHash;
355
345
  checkUpdate({ extra: { toHash } })
356
346
  .then(() => {
357
- if (updateInfoRef.current && updateInfoRef.current.upToDate) {
347
+ if (updateInfoRef.current?.upToDate) {
358
348
  Alert.alert(
359
349
  client.t('alert_info'),
360
- client.t('alert_no_update_wait'),
350
+ client.t('alert_no_update_wait')
361
351
  );
362
352
  }
363
353
  })
@@ -370,7 +360,7 @@ export const UpdateProvider = ({
370
360
  }
371
361
  return false;
372
362
  },
373
- [checkUpdate, options, client],
363
+ [checkUpdate, options, client]
374
364
  );
375
365
 
376
366
  const parseTestQrCode = useCallback(
@@ -383,7 +373,7 @@ export const UpdateProvider = ({
383
373
  return false;
384
374
  }
385
375
  },
386
- [parseTestPayload],
376
+ [parseTestPayload]
387
377
  );
388
378
 
389
379
  const restartApp = useCallback(async () => {
@@ -394,7 +384,7 @@ export const UpdateProvider = ({
394
384
  async (resetOptions?: { restart?: boolean }) => {
395
385
  return client.resetToPackagedBundle(resetOptions);
396
386
  },
397
- [client],
387
+ [client]
398
388
  );
399
389
 
400
390
  useEffect(() => {
@@ -469,7 +459,7 @@ export const UpdateProvider = ({
469
459
  parseTestQrCode,
470
460
  restartApp,
471
461
  resetToPackagedBundle,
472
- ],
462
+ ]
473
463
  );
474
464
 
475
465
  return (
@@ -1,6 +1,6 @@
1
1
  import { currentVersion, packageVersion } from './core';
2
2
  import { isInRollout } from './isInRollout';
3
- import { CheckResult } from './type';
3
+ import type { CheckResult } from './type';
4
4
  import { log } from './utils';
5
5
 
6
6
  export function resolveCheckResult(rootInfo: CheckResult): CheckResult {
package/src/telemetry.ts CHANGED
@@ -35,7 +35,7 @@ export interface ServerReportPayload {
35
35
  */
36
36
  export const resolveServerEventType = (
37
37
  type: EventType,
38
- code?: UpdateErrorCode,
38
+ code?: UpdateErrorCode
39
39
  ): ServerEventType | undefined => {
40
40
  switch (type) {
41
41
  case 'downloadSuccess':
package/src/type.ts CHANGED
@@ -17,15 +17,37 @@ export interface VersionInfo {
17
17
  }
18
18
 
19
19
  interface RootResult {
20
- upToDate?: true;
21
- expired?: true;
20
+ upToDate?: boolean;
21
+ expired?: boolean;
22
22
  downloadUrl?: string;
23
- update?: true;
23
+ update?: boolean;
24
24
  paused?: 'app' | 'package';
25
25
  message?: string;
26
26
  paths?: string[];
27
+ /**
28
+ * Server verdict on the reported bundleHash: 'matched' — the installed
29
+ * binary is registered; 'rebuiltSameJs' — repackaged binary with identical
30
+ * JS (no upload needed); 'unknownBundle' — unregistered binary, incremental
31
+ * updates degraded to full downloads until the package is uploaded.
32
+ */
33
+ bundleStatus?: 'matched' | 'rebuiltSameJs' | 'unknownBundle';
27
34
  }
28
35
 
36
+ export type UpToDateCheckResult = RootResult & {
37
+ upToDate: true;
38
+ };
39
+
40
+ export type UpdateAvailableCheckResult = RootResult &
41
+ Partial<VersionInfo> & {
42
+ update: true;
43
+ expVersion?: VersionInfo;
44
+ };
45
+
46
+ export type ExpiredCheckResult = RootResult & {
47
+ expired: true;
48
+ downloadUrl: string;
49
+ };
50
+
29
51
  export type CheckResult = RootResult &
30
52
  Partial<VersionInfo> & {
31
53
  expVersion?: VersionInfo;
@@ -57,6 +79,10 @@ export type EventType =
57
79
  | 'errorMarkSuccess'
58
80
  | 'reset'
59
81
  | 'errorReset'
82
+ // Server reported bundleStatus: unknownBundle — the installed binary is not
83
+ // registered on the update platform (developer-facing only, local logger;
84
+ // not a server telemetry event).
85
+ | 'bundleMismatch'
60
86
  | 'errorRestart'
61
87
  | 'errorSwitchVersion'
62
88
  | 'downloadingApk'
@@ -126,8 +152,8 @@ export interface ClientOptions {
126
152
  beforeDownloadUpdate?: (info: CheckResult) => Promise<boolean> | boolean;
127
153
  afterDownloadUpdate?: (info: CheckResult) => Promise<boolean> | boolean;
128
154
  beforeReload?: (
129
- context: BeforeReloadContext,
130
- ) => Promise<boolean | void> | boolean | void;
155
+ context: BeforeReloadContext
156
+ ) => Promise<boolean | undefined> | boolean | undefined;
131
157
  onPackageExpired?: (info: CheckResult) => Promise<boolean> | boolean;
132
158
  overridePackageVersion?: string;
133
159
  /** Maximum number of retry attempts for failed downloads (default: 3) */
package/src/utils.ts CHANGED
@@ -28,7 +28,7 @@ export function promiseAny<T>(promises: Promise<T>[]) {
28
28
  }
29
29
  let count = 0;
30
30
 
31
- promises.forEach(promise => {
31
+ promises.forEach((promise) => {
32
32
  Promise.resolve(promise)
33
33
  .then(resolve)
34
34
  .catch(() => {
@@ -44,25 +44,26 @@ export function promiseAny<T>(promises: Promise<T>[]) {
44
44
  export const emptyObj = {};
45
45
  export const noop = () => {};
46
46
  const emptyModuleTarget: Record<string, typeof noop> = {};
47
- export const emptyModule = new Proxy(
48
- emptyModuleTarget,
49
- {
50
- get(_target, _prop) {
51
- return noop;
52
- },
47
+ export const emptyModule = new Proxy(emptyModuleTarget, {
48
+ get(_target, _prop) {
49
+ return noop;
53
50
  },
54
- );
51
+ });
55
52
 
56
53
  const ping = isWeb
57
54
  ? Promise.resolve
58
55
  : async (url: string) => {
59
56
  try {
60
- const { status, statusText, url: finalUrl } = await fetchWithTimeout(
57
+ const {
58
+ status,
59
+ statusText,
60
+ url: finalUrl,
61
+ } = await fetchWithTimeout(
61
62
  url,
62
63
  {
63
64
  method: 'HEAD',
64
65
  },
65
- DEFAULT_FETCH_TIMEOUT_MS,
66
+ DEFAULT_FETCH_TIMEOUT_MS
66
67
  );
67
68
  if (status === 200) {
68
69
  return finalUrl;
@@ -77,7 +78,7 @@ const ping = isWeb
77
78
 
78
79
  export function joinUrls(paths: string[], fileName?: string) {
79
80
  if (fileName) {
80
- return paths.map(path => {
81
+ return paths.map((path) => {
81
82
  const normalizedPath = path.replace(/\/+$/, '');
82
83
  // Keep explicit http(s) URLs for local/self-hosted update sources.
83
84
  const baseUrl = /^[a-z][a-z0-9+.-]*:\/\//i.test(normalizedPath)
@@ -120,7 +121,7 @@ export const computeProgress = (received: number, total: number): number =>
120
121
  export const fetchWithTimeout = (
121
122
  url: string,
122
123
  params: Parameters<typeof fetch>[1],
123
- timeoutMs = DEFAULT_FETCH_TIMEOUT_MS,
124
+ timeoutMs = DEFAULT_FETCH_TIMEOUT_MS
124
125
  ): Promise<Response> => {
125
126
  // AbortController landed in the RN fetch polyfill around 0.60; we support
126
127
  // older peers, so fall back to a plain timer race when it is unavailable
@@ -151,7 +152,7 @@ export const fetchWithTimeout = (
151
152
  controller.abort();
152
153
  }, timeoutMs);
153
154
 
154
- return enhancedFetch(url, { ...params, signal: controller.signal })
155
+ return enhancedFetch(url, { ...params, signal: controller.signal as any })
155
156
  .catch((e: any) => {
156
157
  if (controller.signal.aborted) {
157
158
  throw Error(i18n.t('error_ping_timeout'));
@@ -171,22 +172,21 @@ const isIdempotentRequest = (params: Parameters<typeof fetch>[1]) => {
171
172
  export const enhancedFetch = async (
172
173
  url: string,
173
174
  params: Parameters<typeof fetch>[1],
174
- isRetry = false,
175
+ isRetry = false
175
176
  ): Promise<Response> => {
176
- return fetch(url, params)
177
- .catch(e => {
178
- log('fetch error', url, e);
179
- if (
180
- isRetry ||
181
- (params as any)?.signal?.aborted ||
182
- !url.startsWith('https:') ||
183
- // Never replay non-idempotent requests (e.g. the checkUpdate POST)
184
- // over plaintext http: the server may have processed the original.
185
- !isIdempotentRequest(params)
186
- ) {
187
- throw e;
188
- }
189
- log('trying fallback to http');
190
- return enhancedFetch(url.replace(/^https:/, 'http:'), params, true);
191
- });
177
+ return fetch(url, params).catch((e) => {
178
+ log('fetch error', url, e);
179
+ if (
180
+ isRetry ||
181
+ (params as any)?.signal?.aborted ||
182
+ !url.startsWith('https:') ||
183
+ // Never replay non-idempotent requests (e.g. the checkUpdate POST)
184
+ // over plaintext http: the server may have processed the original.
185
+ !isIdempotentRequest(params)
186
+ ) {
187
+ throw e;
188
+ }
189
+ log('trying fallback to http');
190
+ return enhancedFetch(url.replace(/^https:/, 'http:'), params, true);
191
+ });
192
192
  };
package/.eslintrc.js DELETED
@@ -1,4 +0,0 @@
1
- module.exports = {
2
- root: true,
3
- extends: '@react-native',
4
- };
package/.prettierrc.js DELETED
@@ -1,6 +0,0 @@
1
- module.exports = {
2
- arrowParens: 'avoid',
3
- bracketSameLine: true,
4
- singleQuote: true,
5
- trailingComma: 'all',
6
- };