react-native-update 10.37.20 → 10.38.0-beta.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/README-CN.md +4 -4
- package/README.md +2 -12
- package/android/bin/.settings/org.eclipse.buildship.core.prefs +13 -0
- package/android/build.gradle +4 -0
- package/android/jni/Android.mk +14 -1
- package/android/jni/Application.mk +5 -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/ArchivePatchPlanResult.java +6 -0
- package/android/src/main/java/cn/reactnative/modules/update/CopyGroupResult.java +6 -0
- package/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +136 -136
- package/android/src/main/java/cn/reactnative/modules/update/NativeUpdateCore.java +34 -0
- package/android/src/main/java/cn/reactnative/modules/update/StateCoreResult.java +16 -0
- package/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +131 -48
- package/cpp/patch_core/archive_patch_core.cpp +125 -0
- package/cpp/patch_core/archive_patch_core.h +59 -0
- package/cpp/patch_core/patch_core.cpp +533 -0
- package/cpp/patch_core/patch_core.h +68 -0
- package/cpp/patch_core/patch_core_android.cpp +112 -0
- package/cpp/patch_core/state_core.cpp +110 -0
- package/cpp/patch_core/state_core.h +58 -0
- package/cpp/patch_core/tests/patch_core_test.cpp +473 -0
- package/cpp/patch_core/update_core_android.cpp +469 -0
- package/harmony/pushy.har +0 -0
- package/ios/RCTPushy/RCTPushy.mm +233 -143
- package/package.json +17 -15
- package/react-native-update.podspec +3 -0
- package/scripts/build-harmony-har.js +12 -0
- package/scripts/prepublish.ts +49 -3
- package/scripts/prune-host-stl.sh +6 -0
- package/scripts/test-patch-core.sh +39 -0
- package/src/client.ts +129 -76
- package/src/core.ts +2 -1
- package/src/endpoint.ts +171 -0
- package/src/utils.ts +40 -27
- package/android/jni/lzma/DOC/7zC.txt +0 -187
- package/android/jni/lzma/DOC/7zFormat.txt +0 -469
- package/android/jni/lzma/DOC/Methods.txt +0 -173
- package/android/jni/lzma/DOC/installer.txt +0 -166
- package/android/jni/lzma/DOC/lzma-history.txt +0 -446
- package/android/jni/lzma/DOC/lzma-sdk.txt +0 -357
- package/android/jni/lzma/DOC/lzma-specification.txt +0 -1176
- package/android/jni/lzma/DOC/lzma.txt +0 -328
- package/android/jni/lzma/bin/7zS2.sfx +0 -0
- package/android/jni/lzma/bin/7zS2con.sfx +0 -0
- package/android/jni/lzma/bin/7zSD.sfx +0 -0
- package/android/jni/lzma/bin/7zdec.exe +0 -0
- package/android/jni/lzma/bin/7zr.exe +0 -0
- package/android/jni/lzma/bin/installer/config.txt +0 -5
- package/android/jni/lzma/bin/installer/cr.bat +0 -5
- package/android/jni/lzma/bin/lzma.exe +0 -0
- package/android/jni/lzma/bin/x64/7zr.exe +0 -0
- package/error.js +0 -1609
- package/harmony/har-wrapper/AppScope/app.json5 +0 -8
- package/harmony/har-wrapper/build-profile.json5 +0 -35
- package/harmony/har-wrapper/hvigor/hvigor-config.json5 +0 -5
- package/harmony/har-wrapper/hvigorfile.ts +0 -6
- package/harmony/har-wrapper/oh-package.json5 +0 -4
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/cache-v2-77b153ce45aba0ed28ef.json +0 -1415
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/cmakeFiles-v1-b65a07793384e0ce3e08.json +0 -809
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/codemodel-v2-ce0e89410afd8bf3a057.json +0 -60
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/directory-.-Release-f5ebdc15457944623624.json +0 -14
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/index-2026-03-18T12-50-36-0050.json +0 -89
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/target-rnupdate-Release-267153624504c9c3ffdd.json +0 -222
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.ninja_deps +0 -0
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.ninja_log +0 -8
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeCache.txt +0 -415
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CMakeCCompiler.cmake +0 -74
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CMakeCXXCompiler.cmake +0 -85
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CMakeDetermineCompilerABI_C.bin +0 -0
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CMakeDetermineCompilerABI_CXX.bin +0 -0
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CMakeSystem.cmake +0 -15
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CompilerIdC/CMakeCCompilerId.c +0 -880
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CompilerIdC/CMakeCCompilerId.o +0 -0
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CompilerIdCXX/CMakeCXXCompilerId.cpp +0 -869
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CompilerIdCXX/CMakeCXXCompilerId.o +0 -0
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/CMakeConfigureLog.yaml +0 -388
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/TargetDirectories.txt +0 -3
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/cmake.check_cache +0 -1
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rnupdate.dir/__w/react-native-update/react-native-update/android/jni/HDiffPatch/file_for_patch.c.o +0 -0
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rnupdate.dir/__w/react-native-update/react-native-update/android/jni/HDiffPatch/libHDiffPatch/HPatch/patch.c.o +0 -0
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rnupdate.dir/__w/react-native-update/react-native-update/android/jni/hpatch.c.o +0 -0
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rnupdate.dir/__w/react-native-update/react-native-update/android/jni/lzma/C/Lzma2Dec.c.o +0 -0
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rnupdate.dir/__w/react-native-update/react-native-update/android/jni/lzma/C/LzmaDec.c.o +0 -0
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rnupdate.dir/pushy.c.o +0 -0
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rules.ninja +0 -64
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/additional_project_files.txt +0 -0
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/build.ninja +0 -206
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/build_file_index.txt +0 -1
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/cmake_install.cmake +0 -54
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/compile_commands.json +0 -38
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/configure_fingerprint.json +0 -1
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/hvigor_native_config.json +0 -1
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/metadata_generation_command.txt +0 -17
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/native_work_dir.txt +0 -1
- package/harmony/pushy/.cxx/default/default/release/arm64-v8a/output.log +0 -14
- package/harmony/pushy/.cxx/default/default/release/hvigor/arm64-v8a/summary.cmake +0 -0
- package/harmony/pushy/BuildProfile.ets +0 -17
- package/harmony/pushy/OAT.xml +0 -38
- package/harmony/pushy/README.md +0 -0
- package/harmony/pushy/build-profile.json5 +0 -15
- package/harmony/pushy/hvigor-plugin.ts +0 -34
- package/harmony/pushy/hvigorfile.ts +0 -1
- package/harmony/pushy/index.ets +0 -2
- package/harmony/pushy/oh-package-lock.json5 +0 -20
- package/harmony/pushy/oh-package.json5 +0 -13
- package/harmony/pushy/src/main/cpp/CMakeLists.txt +0 -51
- package/harmony/pushy/src/main/cpp/PushyPackage.h +0 -55
- package/harmony/pushy/src/main/cpp/PushyTurboModule.cpp +0 -142
- package/harmony/pushy/src/main/cpp/PushyTurboModule.h +0 -38
- package/harmony/pushy/src/main/cpp/pushy.c +0 -117
- package/harmony/pushy/src/main/cpp/pushy.h +0 -8
- package/harmony/pushy/src/main/ets/DownloadTask.ts +0 -570
- package/harmony/pushy/src/main/ets/DownloadTaskParams.ts +0 -19
- package/harmony/pushy/src/main/ets/EventHub.ts +0 -39
- package/harmony/pushy/src/main/ets/Logger.ts +0 -52
- package/harmony/pushy/src/main/ets/PushyFileJSBundleProvider.ets +0 -50
- package/harmony/pushy/src/main/ets/PushyPackage.ts +0 -22
- package/harmony/pushy/src/main/ets/PushyTurboModule.ts +0 -171
- package/harmony/pushy/src/main/ets/SaveFile.ts +0 -34
- package/harmony/pushy/src/main/ets/UpdateContext.ts +0 -262
- package/harmony/pushy/src/main/ets/UpdateModuleImpl.ts +0 -123
- package/harmony/pushy/src/main/module.json5 +0 -7
- package/harmony/pushy/src/main/resources/base/element/string.json +0 -8
- package/harmony/pushy/src/main/resources/en_US/element/string.json +0 -8
- package/harmony/pushy/src/main/resources/zh_CN/element/string.json +0 -8
- package/harmony/pushy/ts.ts +0 -3
- package/src/__tests__/core.test.ts +0 -103
- package/src/__tests__/utils.test.ts +0 -36
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -eu
|
|
3
|
+
|
|
4
|
+
ROOT_DIR="$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)"
|
|
5
|
+
BUILD_DIR="$ROOT_DIR/.tmp/patch-core-tests"
|
|
6
|
+
|
|
7
|
+
mkdir -p "$BUILD_DIR"
|
|
8
|
+
|
|
9
|
+
COMMON_INCLUDES="
|
|
10
|
+
-I$ROOT_DIR/cpp/patch_core
|
|
11
|
+
-I$ROOT_DIR/android/jni
|
|
12
|
+
-I$ROOT_DIR/android/jni/HDiffPatch
|
|
13
|
+
-I$ROOT_DIR/android/jni/HDiffPatch/libHDiffPatch/HPatch
|
|
14
|
+
-I$ROOT_DIR/android/jni/lzma/C
|
|
15
|
+
"
|
|
16
|
+
|
|
17
|
+
cc -Wall -Wextra $COMMON_INCLUDES -c "$ROOT_DIR/android/jni/hpatch.c" -o "$BUILD_DIR/hpatch.o"
|
|
18
|
+
cc -Wall -Wextra $COMMON_INCLUDES -c "$ROOT_DIR/android/jni/HDiffPatch/libHDiffPatch/HPatch/patch.c" -o "$BUILD_DIR/patch.o"
|
|
19
|
+
cc -Wall -Wextra $COMMON_INCLUDES -c "$ROOT_DIR/android/jni/HDiffPatch/file_for_patch.c" -o "$BUILD_DIR/file_for_patch.o"
|
|
20
|
+
cc -Wall -Wextra $COMMON_INCLUDES -c "$ROOT_DIR/android/jni/lzma/C/LzmaDec.c" -o "$BUILD_DIR/LzmaDec.o"
|
|
21
|
+
cc -Wall -Wextra $COMMON_INCLUDES -c "$ROOT_DIR/android/jni/lzma/C/Lzma2Dec.c" -o "$BUILD_DIR/Lzma2Dec.o"
|
|
22
|
+
|
|
23
|
+
c++ \
|
|
24
|
+
-std=c++17 \
|
|
25
|
+
-Wall \
|
|
26
|
+
-Wextra \
|
|
27
|
+
$COMMON_INCLUDES \
|
|
28
|
+
"$ROOT_DIR/cpp/patch_core/tests/patch_core_test.cpp" \
|
|
29
|
+
"$ROOT_DIR/cpp/patch_core/archive_patch_core.cpp" \
|
|
30
|
+
"$ROOT_DIR/cpp/patch_core/patch_core.cpp" \
|
|
31
|
+
"$ROOT_DIR/cpp/patch_core/state_core.cpp" \
|
|
32
|
+
"$BUILD_DIR/hpatch.o" \
|
|
33
|
+
"$BUILD_DIR/patch.o" \
|
|
34
|
+
"$BUILD_DIR/file_for_patch.o" \
|
|
35
|
+
"$BUILD_DIR/LzmaDec.o" \
|
|
36
|
+
"$BUILD_DIR/Lzma2Dec.o" \
|
|
37
|
+
-o "$BUILD_DIR/patch_core_test"
|
|
38
|
+
|
|
39
|
+
"$BUILD_DIR/patch_core_test"
|
package/src/client.ts
CHANGED
|
@@ -17,11 +17,18 @@ import {
|
|
|
17
17
|
setLocalHashInfo,
|
|
18
18
|
} from './core';
|
|
19
19
|
import { PermissionsAndroid } from './permissions';
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
CheckResult,
|
|
22
|
+
ClientOptions,
|
|
23
|
+
EventType,
|
|
24
|
+
ProgressData,
|
|
25
|
+
UpdateServerConfig,
|
|
26
|
+
} from './type';
|
|
21
27
|
import {
|
|
22
28
|
assertWeb,
|
|
29
|
+
DEFAULT_FETCH_TIMEOUT_MS,
|
|
23
30
|
emptyObj,
|
|
24
|
-
|
|
31
|
+
fetchWithTimeout,
|
|
25
32
|
info,
|
|
26
33
|
joinUrls,
|
|
27
34
|
log,
|
|
@@ -30,6 +37,7 @@ import {
|
|
|
30
37
|
testUrls,
|
|
31
38
|
} from './utils';
|
|
32
39
|
import i18n from './i18n';
|
|
40
|
+
import { dedupeEndpoints, executeEndpointFallback } from './endpoint';
|
|
33
41
|
|
|
34
42
|
const SERVER_PRESETS = {
|
|
35
43
|
// cn
|
|
@@ -63,6 +71,19 @@ const defaultClientOptions: ClientOptions = {
|
|
|
63
71
|
throwError: false,
|
|
64
72
|
};
|
|
65
73
|
|
|
74
|
+
const cloneServerConfig = (
|
|
75
|
+
server?: UpdateServerConfig,
|
|
76
|
+
): UpdateServerConfig | undefined => {
|
|
77
|
+
if (!server) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
main: server.main,
|
|
82
|
+
backups: server.backups ? [...server.backups] : undefined,
|
|
83
|
+
queryUrls: server.queryUrls ? [...server.queryUrls] : undefined,
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
|
|
66
87
|
export const sharedState: {
|
|
67
88
|
progressHandlers: Record<string, EmitterSubscription>;
|
|
68
89
|
downloadedHash?: string;
|
|
@@ -110,7 +131,7 @@ export class Pushy {
|
|
|
110
131
|
|
|
111
132
|
constructor(options: ClientOptions, clientType?: 'Pushy' | 'Cresc') {
|
|
112
133
|
this.clientType = clientType || 'Pushy';
|
|
113
|
-
this.options.server = SERVER_PRESETS[this.clientType];
|
|
134
|
+
this.options.server = cloneServerConfig(SERVER_PRESETS[this.clientType]);
|
|
114
135
|
|
|
115
136
|
i18n.setLocale(options.locale ?? this.clientType === 'Pushy' ? 'zh' : 'en');
|
|
116
137
|
|
|
@@ -134,7 +155,10 @@ export class Pushy {
|
|
|
134
155
|
setOptions = (options: Partial<ClientOptions>) => {
|
|
135
156
|
for (const [key, value] of Object.entries(options)) {
|
|
136
157
|
if (value !== undefined) {
|
|
137
|
-
(this.options as any)[key] =
|
|
158
|
+
(this.options as any)[key] =
|
|
159
|
+
key === 'server'
|
|
160
|
+
? cloneServerConfig(value as UpdateServerConfig)
|
|
161
|
+
: value;
|
|
138
162
|
if (key === 'logger') {
|
|
139
163
|
this.loggerPromise.resolve();
|
|
140
164
|
}
|
|
@@ -188,6 +212,90 @@ export class Pushy {
|
|
|
188
212
|
getCheckUrl = (endpoint: string = this.options.server!.main) => {
|
|
189
213
|
return `${endpoint}/checkUpdate/${this.options.appKey}`;
|
|
190
214
|
};
|
|
215
|
+
getConfiguredCheckEndpoints = () => {
|
|
216
|
+
const { server } = this.options;
|
|
217
|
+
if (!server) {
|
|
218
|
+
return [];
|
|
219
|
+
}
|
|
220
|
+
return dedupeEndpoints([server.main, ...(server.backups || [])]);
|
|
221
|
+
};
|
|
222
|
+
getRemoteEndpoints = async () => {
|
|
223
|
+
const { server } = this.options;
|
|
224
|
+
if (!server?.queryUrls?.length) {
|
|
225
|
+
return [];
|
|
226
|
+
}
|
|
227
|
+
try {
|
|
228
|
+
const resp = await promiseAny(
|
|
229
|
+
server.queryUrls.map(queryUrl =>
|
|
230
|
+
fetchWithTimeout(queryUrl, {}, DEFAULT_FETCH_TIMEOUT_MS),
|
|
231
|
+
),
|
|
232
|
+
);
|
|
233
|
+
const remoteEndpoints = await resp.json();
|
|
234
|
+
log('fetch endpoints:', remoteEndpoints);
|
|
235
|
+
if (Array.isArray(remoteEndpoints)) {
|
|
236
|
+
const normalizedRemoteEndpoints = dedupeEndpoints(
|
|
237
|
+
remoteEndpoints.filter(
|
|
238
|
+
(endpoint): endpoint is string => typeof endpoint === 'string',
|
|
239
|
+
),
|
|
240
|
+
).filter(endpoint => endpoint !== server.main);
|
|
241
|
+
server.backups = dedupeEndpoints([
|
|
242
|
+
...(server.backups || []),
|
|
243
|
+
...normalizedRemoteEndpoints,
|
|
244
|
+
]).filter(endpoint => endpoint !== server.main);
|
|
245
|
+
return normalizedRemoteEndpoints;
|
|
246
|
+
}
|
|
247
|
+
} catch (e) {
|
|
248
|
+
log('failed to fetch endpoints from: ', server.queryUrls, e);
|
|
249
|
+
}
|
|
250
|
+
return [];
|
|
251
|
+
};
|
|
252
|
+
requestCheckResult = async (
|
|
253
|
+
endpoint: string,
|
|
254
|
+
fetchPayload: Parameters<typeof fetch>[1],
|
|
255
|
+
) => {
|
|
256
|
+
const resp = await fetchWithTimeout(
|
|
257
|
+
this.getCheckUrl(endpoint),
|
|
258
|
+
fetchPayload,
|
|
259
|
+
DEFAULT_FETCH_TIMEOUT_MS,
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
if (!resp.ok) {
|
|
263
|
+
const respText = await resp.text();
|
|
264
|
+
throw Error(
|
|
265
|
+
this.t('error_http_status', {
|
|
266
|
+
status: resp.status,
|
|
267
|
+
statusText: respText,
|
|
268
|
+
}),
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return (await resp.json()) as CheckResult;
|
|
273
|
+
};
|
|
274
|
+
fetchCheckResult = async (fetchPayload: Parameters<typeof fetch>[1]) => {
|
|
275
|
+
const { endpoint, value } = await executeEndpointFallback<CheckResult>({
|
|
276
|
+
configuredEndpoints: this.getConfiguredCheckEndpoints(),
|
|
277
|
+
getRemoteEndpoints: this.getRemoteEndpoints,
|
|
278
|
+
tryEndpoint: async currentEndpoint => {
|
|
279
|
+
try {
|
|
280
|
+
return await this.requestCheckResult(currentEndpoint, fetchPayload);
|
|
281
|
+
} catch (e) {
|
|
282
|
+
log('check endpoint failed', currentEndpoint, e);
|
|
283
|
+
throw e;
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
onFirstFailure: ({ error }) => {
|
|
287
|
+
this.report({
|
|
288
|
+
type: 'errorChecking',
|
|
289
|
+
message: this.t('error_cannot_connect_backup', {
|
|
290
|
+
message: error.message,
|
|
291
|
+
}),
|
|
292
|
+
});
|
|
293
|
+
},
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
log('check endpoint success', endpoint);
|
|
297
|
+
return value;
|
|
298
|
+
};
|
|
191
299
|
assertDebug = (matter: string) => {
|
|
192
300
|
if (__DEV__ && !this.options.debug) {
|
|
193
301
|
info(this.t('dev_debug_disabled', { matter }));
|
|
@@ -271,95 +379,40 @@ export class Pushy {
|
|
|
271
379
|
},
|
|
272
380
|
body,
|
|
273
381
|
};
|
|
274
|
-
|
|
382
|
+
const previousRespJson = this.lastRespJson;
|
|
275
383
|
try {
|
|
276
384
|
this.report({
|
|
277
385
|
type: 'checking',
|
|
278
386
|
message: this.options.appKey + ': ' + stringifyBody,
|
|
279
387
|
});
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
type: 'errorChecking',
|
|
284
|
-
message: this.t('error_cannot_connect_backup', { message: e.message }),
|
|
285
|
-
});
|
|
286
|
-
const backupEndpoints = await this.getBackupEndpoints().catch();
|
|
287
|
-
if (backupEndpoints) {
|
|
288
|
-
resp = await promiseAny(
|
|
289
|
-
backupEndpoints.map(endpoint =>
|
|
290
|
-
enhancedFetch(this.getCheckUrl(endpoint), fetchPayload),
|
|
291
|
-
),
|
|
292
|
-
).catch(() => {
|
|
293
|
-
this.report({
|
|
294
|
-
type: 'errorChecking',
|
|
295
|
-
message: this.t('errorCheckingUseBackup'),
|
|
296
|
-
});
|
|
297
|
-
});
|
|
298
|
-
} else {
|
|
299
|
-
this.report({
|
|
300
|
-
type: 'errorChecking',
|
|
301
|
-
message: this.t('errorCheckingGetBackup'),
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
if (!resp) {
|
|
306
|
-
this.report({
|
|
307
|
-
type: 'errorChecking',
|
|
308
|
-
message: this.t('error_cannot_connect_server'),
|
|
309
|
-
});
|
|
310
|
-
this.throwIfEnabled(Error('errorChecking'));
|
|
311
|
-
return this.lastRespJson ? await this.lastRespJson : emptyObj;
|
|
312
|
-
}
|
|
388
|
+
const respJsonPromise = this.fetchCheckResult(fetchPayload);
|
|
389
|
+
this.lastRespJson = respJsonPromise;
|
|
390
|
+
const result: CheckResult = await respJsonPromise;
|
|
313
391
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
392
|
+
log('checking result:', result);
|
|
393
|
+
|
|
394
|
+
return result;
|
|
395
|
+
} catch (e: any) {
|
|
396
|
+
this.lastRespJson = previousRespJson;
|
|
397
|
+
const errorMessage =
|
|
398
|
+
e?.message || this.t('error_cannot_connect_server');
|
|
320
399
|
this.report({
|
|
321
400
|
type: 'errorChecking',
|
|
322
401
|
message: errorMessage,
|
|
323
402
|
});
|
|
324
|
-
this.throwIfEnabled(
|
|
325
|
-
return
|
|
403
|
+
this.throwIfEnabled(e);
|
|
404
|
+
return previousRespJson ? await previousRespJson : emptyObj;
|
|
326
405
|
}
|
|
327
|
-
const respJsonPromise = resp.json() as Promise<CheckResult>;
|
|
328
|
-
this.lastRespJson = respJsonPromise;
|
|
329
|
-
|
|
330
|
-
const result: CheckResult = await respJsonPromise;
|
|
331
|
-
|
|
332
|
-
log('checking result:', result);
|
|
333
|
-
|
|
334
|
-
return result;
|
|
335
406
|
};
|
|
336
407
|
getBackupEndpoints = async () => {
|
|
337
408
|
const { server } = this.options;
|
|
338
409
|
if (!server) {
|
|
339
410
|
return [];
|
|
340
411
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
);
|
|
346
|
-
const remoteEndpoints = await resp.json();
|
|
347
|
-
log('fetch endpoints:', remoteEndpoints);
|
|
348
|
-
if (Array.isArray(remoteEndpoints)) {
|
|
349
|
-
const backups = server.backups || [];
|
|
350
|
-
const set = new Set(backups);
|
|
351
|
-
for (const endpoint of remoteEndpoints) {
|
|
352
|
-
set.add(endpoint);
|
|
353
|
-
}
|
|
354
|
-
if (set.size !== backups.length) {
|
|
355
|
-
server.backups = Array.from(set);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
} catch (e: any) {
|
|
359
|
-
log('failed to fetch endpoints from: ', server.queryUrls);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
return server.backups;
|
|
412
|
+
const remoteEndpoints = await this.getRemoteEndpoints();
|
|
413
|
+
return dedupeEndpoints([...(server.backups || []), ...remoteEndpoints]).filter(
|
|
414
|
+
endpoint => endpoint !== server.main,
|
|
415
|
+
);
|
|
363
416
|
};
|
|
364
417
|
downloadUpdate = async (
|
|
365
418
|
updateInfo: CheckResult,
|
package/src/core.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
|
|
2
2
|
import { emptyModule, error, log } from './utils';
|
|
3
3
|
import i18n from './i18n';
|
|
4
|
+
/* eslint-disable @react-native/no-deep-imports */
|
|
4
5
|
const {
|
|
5
6
|
version: v,
|
|
6
7
|
} = require('react-native/Libraries/Core/ReactNativeVersion');
|
|
@@ -47,7 +48,7 @@ if (currentVersionInfoString) {
|
|
|
47
48
|
delete _currentVersionInfo.debugChannel;
|
|
48
49
|
setLocalHashInfo(currentVersion, _currentVersionInfo);
|
|
49
50
|
}
|
|
50
|
-
} catch
|
|
51
|
+
} catch {
|
|
51
52
|
error(
|
|
52
53
|
i18n.t('error_parse_version_info', { info: currentVersionInfoString }),
|
|
53
54
|
);
|
package/src/endpoint.ts
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
export interface EndpointAttemptSuccess<T> {
|
|
2
|
+
endpoint: string;
|
|
3
|
+
value: T;
|
|
4
|
+
duration: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface EndpointAttemptFailure {
|
|
8
|
+
endpoint: string;
|
|
9
|
+
error: Error;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ExecuteEndpointFallbackOptions<T> {
|
|
13
|
+
configuredEndpoints: string[];
|
|
14
|
+
getRemoteEndpoints?: () => Promise<string[]>;
|
|
15
|
+
tryEndpoint: (endpoint: string) => Promise<T>;
|
|
16
|
+
random?: () => number;
|
|
17
|
+
now?: () => number;
|
|
18
|
+
onFirstFailure?: (failure: EndpointAttemptFailure) => void | Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const normalizeError = (error: unknown) => {
|
|
22
|
+
if (error instanceof Error) {
|
|
23
|
+
return error;
|
|
24
|
+
}
|
|
25
|
+
return new Error(String(error));
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const dedupeEndpoints = (
|
|
29
|
+
endpoints: Array<string | null | undefined>,
|
|
30
|
+
): string[] => {
|
|
31
|
+
const result: string[] = [];
|
|
32
|
+
const visited = new Set<string>();
|
|
33
|
+
|
|
34
|
+
for (const endpoint of endpoints) {
|
|
35
|
+
if (!endpoint || visited.has(endpoint)) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
visited.add(endpoint);
|
|
39
|
+
result.push(endpoint);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const pickRandomEndpoint = (
|
|
46
|
+
endpoints: string[],
|
|
47
|
+
random: () => number = Math.random,
|
|
48
|
+
) => {
|
|
49
|
+
if (!endpoints.length) {
|
|
50
|
+
throw new Error('No endpoints configured');
|
|
51
|
+
}
|
|
52
|
+
return endpoints[Math.floor(random() * endpoints.length)];
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export async function selectFastestSuccessfulEndpoint<T>(
|
|
56
|
+
endpoints: string[],
|
|
57
|
+
tryEndpoint: (endpoint: string) => Promise<T>,
|
|
58
|
+
now: () => number = Date.now,
|
|
59
|
+
): Promise<{
|
|
60
|
+
successes: EndpointAttemptSuccess<T>[];
|
|
61
|
+
failures: EndpointAttemptFailure[];
|
|
62
|
+
}> {
|
|
63
|
+
const attempts = await Promise.all(
|
|
64
|
+
endpoints.map(async endpoint => {
|
|
65
|
+
const start = now();
|
|
66
|
+
try {
|
|
67
|
+
const value = await tryEndpoint(endpoint);
|
|
68
|
+
return {
|
|
69
|
+
ok: true as const,
|
|
70
|
+
endpoint,
|
|
71
|
+
value,
|
|
72
|
+
duration: now() - start,
|
|
73
|
+
};
|
|
74
|
+
} catch (error) {
|
|
75
|
+
return {
|
|
76
|
+
ok: false as const,
|
|
77
|
+
endpoint,
|
|
78
|
+
error: normalizeError(error),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}),
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const successes: EndpointAttemptSuccess<T>[] = [];
|
|
85
|
+
const failures: EndpointAttemptFailure[] = [];
|
|
86
|
+
|
|
87
|
+
for (const attempt of attempts) {
|
|
88
|
+
if (attempt.ok) {
|
|
89
|
+
successes.push({
|
|
90
|
+
endpoint: attempt.endpoint,
|
|
91
|
+
value: attempt.value,
|
|
92
|
+
duration: attempt.duration,
|
|
93
|
+
});
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
failures.push({
|
|
98
|
+
endpoint: attempt.endpoint,
|
|
99
|
+
error: attempt.error,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
successes.sort((left, right) => left.duration - right.duration);
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
successes,
|
|
107
|
+
failures,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function executeEndpointFallback<T>({
|
|
112
|
+
configuredEndpoints,
|
|
113
|
+
getRemoteEndpoints,
|
|
114
|
+
tryEndpoint,
|
|
115
|
+
random = Math.random,
|
|
116
|
+
now = Date.now,
|
|
117
|
+
onFirstFailure,
|
|
118
|
+
}: ExecuteEndpointFallbackOptions<T>): Promise<EndpointAttemptSuccess<T>> {
|
|
119
|
+
const excludedEndpoints = new Set<string>();
|
|
120
|
+
let candidates = dedupeEndpoints(configuredEndpoints);
|
|
121
|
+
|
|
122
|
+
if (!candidates.length) {
|
|
123
|
+
throw new Error('No endpoints configured');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const firstEndpoint = pickRandomEndpoint(candidates, random);
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
return {
|
|
130
|
+
endpoint: firstEndpoint,
|
|
131
|
+
value: await tryEndpoint(firstEndpoint),
|
|
132
|
+
duration: 0,
|
|
133
|
+
};
|
|
134
|
+
} catch (error) {
|
|
135
|
+
const firstFailure = {
|
|
136
|
+
endpoint: firstEndpoint,
|
|
137
|
+
error: normalizeError(error),
|
|
138
|
+
};
|
|
139
|
+
excludedEndpoints.add(firstEndpoint);
|
|
140
|
+
await onFirstFailure?.(firstFailure);
|
|
141
|
+
let lastError = firstFailure.error;
|
|
142
|
+
|
|
143
|
+
while (true) {
|
|
144
|
+
const remoteEndpoints = getRemoteEndpoints
|
|
145
|
+
? await getRemoteEndpoints().catch(() => [])
|
|
146
|
+
: [];
|
|
147
|
+
candidates = dedupeEndpoints([...candidates, ...remoteEndpoints]).filter(
|
|
148
|
+
endpoint => !excludedEndpoints.has(endpoint),
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
if (!candidates.length) {
|
|
152
|
+
throw lastError;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const { successes, failures } = await selectFastestSuccessfulEndpoint(
|
|
156
|
+
candidates,
|
|
157
|
+
tryEndpoint,
|
|
158
|
+
now,
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
if (successes.length) {
|
|
162
|
+
return successes[0];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
for (const failure of failures) {
|
|
166
|
+
excludedEndpoints.add(failure.endpoint);
|
|
167
|
+
lastError = failure.error;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -18,6 +18,7 @@ export function error(...args: any[]) {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export const isWeb = Platform.OS === 'web';
|
|
21
|
+
export const DEFAULT_FETCH_TIMEOUT_MS = 5000;
|
|
21
22
|
|
|
22
23
|
export function promiseAny<T>(promises: Promise<T>[]) {
|
|
23
24
|
return new Promise<T>((resolve, reject) => {
|
|
@@ -52,33 +53,23 @@ export const emptyModule = new EmptyModule();
|
|
|
52
53
|
const ping = isWeb
|
|
53
54
|
? Promise.resolve
|
|
54
55
|
: async (url: string) => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}),
|
|
73
|
-
new Promise((_, reject) =>
|
|
74
|
-
setTimeout(() => {
|
|
75
|
-
reject(Error(i18n.t('error_ping_timeout')));
|
|
76
|
-
if (!pingFinished) {
|
|
77
|
-
log('ping timeout', url);
|
|
78
|
-
}
|
|
79
|
-
}, 5000),
|
|
80
|
-
),
|
|
81
|
-
]);
|
|
56
|
+
try {
|
|
57
|
+
const { status, statusText, url: finalUrl } = await fetchWithTimeout(
|
|
58
|
+
url,
|
|
59
|
+
{
|
|
60
|
+
method: 'HEAD',
|
|
61
|
+
},
|
|
62
|
+
DEFAULT_FETCH_TIMEOUT_MS,
|
|
63
|
+
);
|
|
64
|
+
if (status === 200) {
|
|
65
|
+
return finalUrl;
|
|
66
|
+
}
|
|
67
|
+
log('ping failed', url, status, statusText);
|
|
68
|
+
throw Error(i18n.t('error_ping_failed'));
|
|
69
|
+
} catch (e) {
|
|
70
|
+
log('ping error', url, e);
|
|
71
|
+
throw e;
|
|
72
|
+
}
|
|
82
73
|
};
|
|
83
74
|
|
|
84
75
|
export function joinUrls(paths: string[], fileName?: string) {
|
|
@@ -111,6 +102,28 @@ export const assertWeb = () => {
|
|
|
111
102
|
return true;
|
|
112
103
|
};
|
|
113
104
|
|
|
105
|
+
export const fetchWithTimeout = (
|
|
106
|
+
url: string,
|
|
107
|
+
params: Parameters<typeof fetch>[1],
|
|
108
|
+
timeoutMs = DEFAULT_FETCH_TIMEOUT_MS,
|
|
109
|
+
): Promise<Response> => {
|
|
110
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
111
|
+
|
|
112
|
+
return Promise.race([
|
|
113
|
+
enhancedFetch(url, params),
|
|
114
|
+
new Promise<Response>((_, reject) => {
|
|
115
|
+
timeoutId = setTimeout(() => {
|
|
116
|
+
log('fetch timeout', url);
|
|
117
|
+
reject(Error(i18n.t('error_ping_timeout')));
|
|
118
|
+
}, timeoutMs);
|
|
119
|
+
}),
|
|
120
|
+
]).finally(() => {
|
|
121
|
+
if (timeoutId) {
|
|
122
|
+
clearTimeout(timeoutId);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
|
|
114
127
|
// export const isAndroid70AndBelow = () => {
|
|
115
128
|
// // android 7.0 and below devices do not support letsencrypt cert
|
|
116
129
|
// // https://letsencrypt.org/2023/07/10/cross-sign-expiration/
|