react-native-update 10.59.1 → 10.60.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 +7 -7
- package/README.md +7 -6
- package/android/build.gradle +25 -33
- package/android/jni/Android.mk +16 -24
- package/android/lib/arm64-v8a/librnpushy.so +0 -0
- package/android/lib/armeabi-v7a/librnpushy.so +0 -0
- package/android/lib/x86/librnpushy.so +0 -0
- package/android/lib/x86_64/librnpushy.so +0 -0
- package/android/proguard.pro +9 -7
- package/android/src/main/java/cn/reactnative/modules/update/ApkInstaller.java +11 -12
- package/android/src/main/java/cn/reactnative/modules/update/ArchiveLimits.java +23 -6
- package/android/src/main/java/cn/reactnative/modules/update/BundledResourceCopier.java +50 -51
- package/android/src/main/java/cn/reactnative/modules/update/CleanupKeepNames.java +12 -13
- package/android/src/main/java/cn/reactnative/modules/update/CopyGroupResult.java +3 -0
- package/android/src/main/java/cn/reactnative/modules/update/CrashHold.java +22 -30
- package/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +181 -332
- package/android/src/main/java/cn/reactnative/modules/update/DownloadTaskParams.java +3 -1
- package/android/src/main/java/cn/reactnative/modules/update/ErrorCodes.java +27 -0
- package/android/src/main/java/cn/reactnative/modules/update/FileOperationException.java +1 -1
- package/android/src/main/java/cn/reactnative/modules/update/FlowBridge.java +32 -8
- package/android/src/main/java/cn/reactnative/modules/update/HttpClients.java +63 -0
- package/android/src/main/java/cn/reactnative/modules/update/HttpUtils.java +0 -11
- package/android/src/main/java/cn/reactnative/modules/update/InstallRecord.java +66 -116
- package/android/src/main/java/cn/reactnative/modules/update/NativeCore.java +9 -1
- package/android/src/main/java/cn/reactnative/modules/update/PatchFailedException.java +1 -1
- package/android/src/main/java/cn/reactnative/modules/update/PushyRuntime.java +32 -60
- package/android/src/main/java/cn/reactnative/modules/update/ReactReloadManager.java +8 -23
- package/android/src/main/java/cn/reactnative/modules/update/SafeZipFile.java +42 -80
- package/android/src/main/java/cn/reactnative/modules/update/StateSerialRunner.java +22 -39
- package/android/src/main/java/cn/reactnative/modules/update/SyncCoordinator.java +176 -299
- package/android/src/main/java/cn/reactnative/modules/update/Threads.java +35 -0
- package/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +242 -174
- package/android/src/main/java/cn/reactnative/modules/update/UpdateEventEmitter.java +1 -1
- package/android/src/main/java/cn/reactnative/modules/update/UpdateFileUtils.java +75 -17
- package/android/src/main/java/cn/reactnative/modules/update/{UpdateModuleImpl.java → UpdateModule.java} +113 -95
- package/android/src/main/java/cn/reactnative/modules/update/UpdatePackage.java +16 -19
- package/android/src/oldarch/cn/reactnative/modules/update/NativePushySpec.java +69 -0
- package/cpp/android_jni/archive_limits_jni.cpp +83 -0
- package/cpp/android_jni/install_record_jni.cpp +89 -0
- package/cpp/android_jni/jni_log.h +31 -0
- package/cpp/android_jni/jni_natives.h +40 -0
- package/cpp/android_jni/jni_registration.cpp +133 -0
- package/cpp/android_jni/jni_util.h +160 -0
- package/cpp/android_jni/update_core_android.cpp +450 -0
- package/cpp/android_jni/update_flow_jni.cpp +133 -0
- package/{android/jni → cpp/hpatch}/hpatch.c +5 -5
- package/{android/jni → cpp/hpatch}/hpatch.h +4 -1
- package/cpp/patch_core/archive_limits.cpp +194 -0
- package/cpp/patch_core/archive_limits.h +99 -10
- package/cpp/patch_core/archive_patch_core.cpp +96 -12
- package/cpp/patch_core/archive_patch_core.h +49 -0
- package/cpp/patch_core/digest.h +5 -4
- package/cpp/patch_core/error_codes.h +5 -3
- package/cpp/patch_core/install_record.cpp +430 -0
- package/cpp/patch_core/install_record.h +163 -9
- package/cpp/patch_core/patch_core.cpp +62 -16
- package/cpp/patch_core/patch_core.h +27 -7
- package/cpp/patch_core/patch_manifest.cpp +201 -0
- package/cpp/patch_core/patch_manifest.h +74 -0
- package/cpp/patch_core/state_ops.h +3 -3
- package/cpp/update_flow_core/flow_json.cpp +82 -8
- package/cpp/update_flow_core/flow_json.h +21 -0
- package/cpp/update_flow_core/native_config.cpp +456 -0
- package/cpp/update_flow_core/native_config.h +84 -0
- package/cpp/update_flow_core/native_round.cpp +326 -0
- package/cpp/update_flow_core/native_round.h +90 -0
- package/cpp/update_flow_core/update_flow_core.h +2 -2
- package/harmony/hvigor-plugin.ts +11 -19
- package/harmony/pushy/src/main/cpp/CMakeLists.txt +19 -26
- package/harmony/pushy/src/main/cpp/PushyPackage.h +2 -2
- package/harmony/pushy/src/main/cpp/PushyTurboModule.cpp +55 -94
- package/harmony/pushy/src/main/cpp/pushy.cpp +694 -517
- package/harmony/pushy/src/main/ets/ArchiveLimits.ts +48 -72
- package/harmony/pushy/src/main/ets/DownloadTask.ts +164 -404
- package/harmony/pushy/src/main/ets/DownloadTaskParams.ts +1 -3
- package/harmony/pushy/src/main/ets/ErrorCodes.ts +1 -14
- package/harmony/pushy/src/main/ets/EventHub.ts +1 -1
- package/harmony/pushy/src/main/ets/InstallRecord.ts +52 -151
- package/harmony/pushy/src/main/ets/Logger.ts +20 -34
- package/harmony/pushy/src/main/ets/NativePatchCore.ts +116 -42
- package/harmony/pushy/src/main/ets/PushyConfiguration.ts +8 -85
- package/harmony/pushy/src/main/ets/PushyTurboModule.ts +11 -25
- package/harmony/pushy/src/main/ets/SaveFile.ts +99 -18
- package/harmony/pushy/src/main/ets/SyncCoordinator.ts +136 -335
- package/harmony/pushy/src/main/ets/Texts.ts +0 -4
- package/harmony/pushy/src/main/ets/UpdateContext.ts +106 -119
- package/harmony/pushy.har +0 -0
- package/ios/RCTPushy/RCTPushy+Launch.mm +185 -0
- package/ios/RCTPushy/RCTPushy.h +12 -0
- package/ios/RCTPushy/RCTPushy.mm +87 -2628
- package/ios/RCTPushy/RCTPushyCommon.h +34 -0
- package/ios/RCTPushy/RCTPushyConfiguration.h +0 -8
- package/ios/RCTPushy/RCTPushyConfiguration.mm +40 -121
- package/ios/RCTPushy/RCTPushyDownloader.mm +40 -37
- package/ios/RCTPushy/RCTPushyInstallRecord.mm +61 -0
- package/ios/RCTPushy/RCTPushyInstaller.mm +689 -0
- package/ios/RCTPushy/RCTPushyInternal.h +144 -0
- package/ios/RCTPushy/RCTPushyOrchestrator.mm +1001 -0
- package/ios/RCTPushy/RCTPushyState.mm +314 -0
- package/package.json +25 -23
- package/react-native-update.podspec +74 -83
- package/src/client.ts +361 -451
- package/src/context.ts +1 -1
- package/src/core.ts +35 -8
- package/src/endpoint.ts +0 -2
- package/src/error.ts +13 -32
- package/src/i18n.ts +18 -83
- package/src/locales/en.ts +0 -4
- package/src/locales/zh.ts +0 -4
- package/src/provider.tsx +60 -53
- package/src/telemetry.ts +0 -13
- package/src/type.ts +0 -15
- package/src/updateFlowCore.ts +116 -10
- package/src/utils.ts +49 -42
- package/android/src/main/java/cn/reactnative/modules/update/ArchivePatchPlanResult.java +0 -6
- package/android/src/main/java/cn/reactnative/modules/update/PushyConfiguration.java +0 -104
- package/android/src/main/java/cn/reactnative/modules/update/UiThreadRunner.java +0 -37
- package/android/src/newarch/cn/reactnative/modules/update/UpdateModule.java +0 -121
- package/android/src/oldarch/cn/reactnative/modules/update/UpdateModule.java +0 -125
- package/cpp/patch_core/jni_natives.h +0 -27
- package/cpp/patch_core/jni_registration.cpp +0 -103
- package/cpp/patch_core/jni_util.h +0 -60
- package/cpp/patch_core/patch_core_android.cpp +0 -97
- package/cpp/patch_core/update_core_android.cpp +0 -485
- package/cpp/update_flow_core/update_flow_jni.cpp +0 -150
package/src/updateFlowCore.ts
CHANGED
|
@@ -118,6 +118,117 @@ export const dedupeEndpoints = (
|
|
|
118
118
|
return result;
|
|
119
119
|
};
|
|
120
120
|
|
|
121
|
+
// Values the natives assume for keys a persisted native check config omits.
|
|
122
|
+
const NATIVE_CONFIG_DEFAULTS: Record<string, unknown> = {
|
|
123
|
+
disabled: false,
|
|
124
|
+
queryUrls: [],
|
|
125
|
+
rnu: '',
|
|
126
|
+
rn: '',
|
|
127
|
+
afterDownload: 'none',
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Deep equality of JSON values, key order ignored. Object members whose value
|
|
132
|
+
* is undefined count as absent, matching JSON.stringify (they do not exist on
|
|
133
|
+
* the wire).
|
|
134
|
+
*/
|
|
135
|
+
export const jsonDeepEqual = (a: unknown, b: unknown): boolean => {
|
|
136
|
+
if (a === b) {
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
140
|
+
return (
|
|
141
|
+
Array.isArray(a) &&
|
|
142
|
+
Array.isArray(b) &&
|
|
143
|
+
a.length === b.length &&
|
|
144
|
+
a.every((value, index) => jsonDeepEqual(value, b[index]))
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
if (!a || !b || typeof a !== 'object' || typeof b !== 'object') {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
const left = a as Record<string, unknown>;
|
|
151
|
+
const right = b as Record<string, unknown>;
|
|
152
|
+
const definedKeys = (object: Record<string, unknown>) =>
|
|
153
|
+
Object.keys(object).filter((key) => object[key] !== undefined);
|
|
154
|
+
const leftKeys = definedKeys(left);
|
|
155
|
+
return (
|
|
156
|
+
leftKeys.length === definedKeys(right).length &&
|
|
157
|
+
leftKeys.every(
|
|
158
|
+
(key) =>
|
|
159
|
+
// biome-ignore lint/suspicious/noPrototypeBuiltins: Object.hasOwn is missing from JSC before iOS 15.4
|
|
160
|
+
Object.prototype.hasOwnProperty.call(right, key) &&
|
|
161
|
+
jsonDeepEqual(left[key], right[key])
|
|
162
|
+
)
|
|
163
|
+
);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// Each string loses every trailing '/', then entries (strings or not) are
|
|
167
|
+
// deduplicated by JSON value, keeping the first occurrence.
|
|
168
|
+
const canonicalUrlList = (value: unknown): unknown => {
|
|
169
|
+
if (!Array.isArray(value)) {
|
|
170
|
+
return value;
|
|
171
|
+
}
|
|
172
|
+
const result: unknown[] = [];
|
|
173
|
+
const visited = new Set<string>();
|
|
174
|
+
for (const item of value) {
|
|
175
|
+
if (typeof item === 'string') {
|
|
176
|
+
const url = item.replace(/\/+$/, '');
|
|
177
|
+
if (!visited.has(url)) {
|
|
178
|
+
visited.add(url);
|
|
179
|
+
result.push(url);
|
|
180
|
+
}
|
|
181
|
+
} else if (!result.some((existing) => jsonDeepEqual(existing, item))) {
|
|
182
|
+
result.push(item);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return result;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Canonical form of a persisted native check config (shared contract C1 with
|
|
190
|
+
* the Android/iOS/Harmony natives): absent keys take their native defaults,
|
|
191
|
+
* `endpoints`/`queryUrls` lose trailing '/' and duplicates (first occurrence
|
|
192
|
+
* kept). Undefined when the JSON is unparseable or not an object.
|
|
193
|
+
*/
|
|
194
|
+
export function canonicalizeNativeConfig(
|
|
195
|
+
configJson: string
|
|
196
|
+
): Record<string, unknown> | undefined {
|
|
197
|
+
let parsed: unknown;
|
|
198
|
+
try {
|
|
199
|
+
parsed = JSON.parse(configJson);
|
|
200
|
+
} catch {
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
const canonical: Record<string, unknown> = {
|
|
207
|
+
...NATIVE_CONFIG_DEFAULTS,
|
|
208
|
+
...(parsed as Record<string, unknown>),
|
|
209
|
+
};
|
|
210
|
+
for (const key of ['endpoints', 'queryUrls']) {
|
|
211
|
+
if (key in canonical) {
|
|
212
|
+
canonical[key] = canonicalUrlList(canonical[key]);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return canonical;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Whether two native check config JSON strings describe the same config
|
|
220
|
+
* (contract C1): their canonical forms are deep-equal, key order ignored.
|
|
221
|
+
* Anything unparseable is never equivalent.
|
|
222
|
+
*/
|
|
223
|
+
export function nativeConfigsEquivalent(
|
|
224
|
+
leftJson: string,
|
|
225
|
+
rightJson: string
|
|
226
|
+
): boolean {
|
|
227
|
+
const left = canonicalizeNativeConfig(leftJson);
|
|
228
|
+
const right = canonicalizeNativeConfig(rightJson);
|
|
229
|
+
return !!left && !!right && jsonDeepEqual(left, right);
|
|
230
|
+
}
|
|
231
|
+
|
|
121
232
|
/**
|
|
122
233
|
* The endpoint plan: dedupe, then move the sampled pick to the front (load
|
|
123
234
|
* spreading), keeping the rest in configured order as the fallback sequence.
|
|
@@ -223,8 +334,10 @@ export interface CheckFingerprintInput {
|
|
|
223
334
|
appKey?: string;
|
|
224
335
|
endpoints?: string[];
|
|
225
336
|
queryUrls?: string[];
|
|
226
|
-
|
|
227
|
-
|
|
337
|
+
/**
|
|
338
|
+
* The exact serialized request body (identity fields + extra; the client
|
|
339
|
+
* uuid travels inside its cInfo).
|
|
340
|
+
*/
|
|
228
341
|
body: string;
|
|
229
342
|
}
|
|
230
343
|
|
|
@@ -236,16 +349,9 @@ export function buildCheckFingerprint({
|
|
|
236
349
|
appKey,
|
|
237
350
|
endpoints,
|
|
238
351
|
queryUrls,
|
|
239
|
-
uuid,
|
|
240
352
|
body,
|
|
241
353
|
}: CheckFingerprintInput): string {
|
|
242
|
-
return JSON.stringify([
|
|
243
|
-
appKey ?? '',
|
|
244
|
-
endpoints ?? [],
|
|
245
|
-
queryUrls ?? [],
|
|
246
|
-
uuid ?? '',
|
|
247
|
-
body,
|
|
248
|
-
]);
|
|
354
|
+
return JSON.stringify([appKey ?? '', endpoints ?? [], queryUrls ?? [], body]);
|
|
249
355
|
}
|
|
250
356
|
|
|
251
357
|
/**
|
package/src/utils.ts
CHANGED
|
@@ -57,6 +57,23 @@ export function promiseAny<T>(promises: Promise<T>[]) {
|
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
// Same alphabet and length as nanoid's default (URL-safe, 21 chars), so ids
|
|
61
|
+
// stay format-compatible with the ones nanoid/non-secure used to generate.
|
|
62
|
+
const ID_ALPHABET =
|
|
63
|
+
'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict';
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Non-cryptographic random id (installation uuid). Math.random is enough: it
|
|
67
|
+
* only buckets gray releases, it is not a secret.
|
|
68
|
+
*/
|
|
69
|
+
export const createNonSecureId = (size = 21): string => {
|
|
70
|
+
let id = '';
|
|
71
|
+
for (let i = 0; i < size; i++) {
|
|
72
|
+
id += ID_ALPHABET[Math.floor(Math.random() * 64)];
|
|
73
|
+
}
|
|
74
|
+
return id;
|
|
75
|
+
};
|
|
76
|
+
|
|
60
77
|
export const noop = () => {};
|
|
61
78
|
const emptyModuleTarget: Record<string, typeof noop> = {};
|
|
62
79
|
export const emptyModule = new Proxy(emptyModuleTarget, {
|
|
@@ -65,40 +82,32 @@ export const emptyModule = new Proxy(emptyModuleTarget, {
|
|
|
65
82
|
},
|
|
66
83
|
});
|
|
67
84
|
|
|
68
|
-
const ping =
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
);
|
|
83
|
-
if (status === 200) {
|
|
84
|
-
if (isProtocolDowngrade(url, finalUrl)) {
|
|
85
|
-
// The probe was redirected from https to plaintext http. Never
|
|
86
|
-
// let a downgraded final URL win the race: the winner is what
|
|
87
|
-
// the real artifact download uses.
|
|
88
|
-
log('ping rejected: https redirected to http', url, finalUrl);
|
|
89
|
-
throw Error(i18n.t('error_ping_failed'));
|
|
90
|
-
}
|
|
91
|
-
return finalUrl;
|
|
92
|
-
}
|
|
93
|
-
log('ping failed', url, status, statusText);
|
|
85
|
+
const ping = async (url: string) => {
|
|
86
|
+
try {
|
|
87
|
+
const {
|
|
88
|
+
status,
|
|
89
|
+
statusText,
|
|
90
|
+
url: finalUrl,
|
|
91
|
+
} = await fetchWithTimeout(url, {
|
|
92
|
+
method: 'HEAD',
|
|
93
|
+
});
|
|
94
|
+
if (status === 200) {
|
|
95
|
+
if (isProtocolDowngrade(url, finalUrl)) {
|
|
96
|
+
// The probe was redirected from https to plaintext http. Never let a
|
|
97
|
+
// downgraded final URL win the race: the winner is what the real
|
|
98
|
+
// artifact download uses.
|
|
99
|
+
log('ping rejected: https redirected to http', url, finalUrl);
|
|
94
100
|
throw Error(i18n.t('error_ping_failed'));
|
|
95
|
-
} catch (e) {
|
|
96
|
-
log('ping error', url, e);
|
|
97
|
-
throw e;
|
|
98
101
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
+
return finalUrl;
|
|
103
|
+
}
|
|
104
|
+
log('ping failed', url, status, statusText);
|
|
105
|
+
throw Error(i18n.t('error_ping_failed'));
|
|
106
|
+
} catch (e) {
|
|
107
|
+
log('ping error', url, e);
|
|
108
|
+
throw e;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
102
111
|
|
|
103
112
|
export const testUrls = async (urls?: string[]): Promise<string | null> => {
|
|
104
113
|
if (!urls?.length) {
|
|
@@ -162,16 +171,14 @@ export const fetchWithTimeout = (
|
|
|
162
171
|
// Promise.race the losing fetch kept running (and kept the connection busy)
|
|
163
172
|
// long after the caller had already moved on.
|
|
164
173
|
const controller = new AbortController();
|
|
165
|
-
// The timeout controller
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
const externalSignal =
|
|
169
|
-
signal ?? ((params as any)?.signal as AbortSignal | undefined);
|
|
174
|
+
// The timeout controller is the fetch call's signal, so a caller-provided
|
|
175
|
+
// signal (e.g. the hedged endpoint race cancelling losers) must be chained
|
|
176
|
+
// onto it manually.
|
|
170
177
|
const onExternalAbort = () => controller.abort();
|
|
171
|
-
if (
|
|
178
|
+
if (signal?.aborted) {
|
|
172
179
|
controller.abort();
|
|
173
|
-
} else if (
|
|
174
|
-
|
|
180
|
+
} else if (signal) {
|
|
181
|
+
signal.addEventListener('abort', onExternalAbort);
|
|
175
182
|
}
|
|
176
183
|
const timeoutId = setTimeout(() => {
|
|
177
184
|
log('fetch timeout', url);
|
|
@@ -180,7 +187,7 @@ export const fetchWithTimeout = (
|
|
|
180
187
|
|
|
181
188
|
return enhancedFetch(url, { ...params, signal: controller.signal as any })
|
|
182
189
|
.catch((e: any) => {
|
|
183
|
-
if (
|
|
190
|
+
if (signal?.aborted) {
|
|
184
191
|
// Cancelled by the caller, not timed out; keep the abort semantics.
|
|
185
192
|
throw e;
|
|
186
193
|
}
|
|
@@ -193,7 +200,7 @@ export const fetchWithTimeout = (
|
|
|
193
200
|
clearTimeout(timeoutId);
|
|
194
201
|
// A long-lived caller signal (one per endpoint race) would otherwise
|
|
195
202
|
// keep every finished request's listener — and its controller — alive.
|
|
196
|
-
|
|
203
|
+
signal?.removeEventListener('abort', onExternalAbort);
|
|
197
204
|
});
|
|
198
205
|
};
|
|
199
206
|
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
package cn.reactnative.modules.update;
|
|
2
|
-
|
|
3
|
-
import java.util.Arrays;
|
|
4
|
-
import java.util.HashSet;
|
|
5
|
-
import java.util.Iterator;
|
|
6
|
-
import java.util.Set;
|
|
7
|
-
import java.util.regex.Pattern;
|
|
8
|
-
import org.json.JSONArray;
|
|
9
|
-
import org.json.JSONException;
|
|
10
|
-
import org.json.JSONObject;
|
|
11
|
-
|
|
12
|
-
/** Internal normalizer for the JSONObject accepted by PushyRuntime.configure. */
|
|
13
|
-
final class PushyConfiguration {
|
|
14
|
-
private static final Set<String> KEYS = new HashSet<>(Arrays.asList(
|
|
15
|
-
"appKey", "endpoints", "queryUrls", "afterDownload", "disabled",
|
|
16
|
-
"packageVersion", "rnu", "rn"));
|
|
17
|
-
private static final Pattern URL = Pattern.compile(
|
|
18
|
-
"^https?://(\\[[0-9a-fA-F:]+\\]|[a-zA-Z0-9.-]+)(:[0-9]+)?([/?#]|$)");
|
|
19
|
-
// Default service addresses, encoded by scripts/encode-native-text.ts.
|
|
20
|
-
private static final String[] ENDPOINTS = {
|
|
21
|
-
Texts.reveal("3203e0c1bdd1270a372f18f8c2b6de7f4f2607f5b3d5b9817b592947e5cdefbc8a7e"),
|
|
22
|
-
Texts.reveal("3203e0c1bdd1270a372f18f8c2b6de7f4f2607f5f0daac9c644a620ae88ca1ad93")
|
|
23
|
-
};
|
|
24
|
-
private static final String[] QUERY_URLS = {
|
|
25
|
-
Texts.reveal("3203e0c1bdd1270a253608fcd3fd9362476817f4f0d5a1996342631be3c2a3a9d779552507fdcde8928a6f512f5ce2ccbdc869404d2f1de79daa826d562c0913eec4fa9b7d4426"),
|
|
26
|
-
Texts.reveal("3203e0c1bdd1270a213b12b7dca09468462e12f3b0d5bd813d482446f4c6a1be8e79552507fdcda68cd06e5c3710e480a4867048483e55e0c2ab8d7d43030d1ce9c3b183214e2601f2f0d5b782601e2719e8ca")
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
// afterDownload value that selects the bundle for the next launch, encoded.
|
|
30
|
-
static final String POLICY_NEXT_LAUNCH = Texts.reveal("2912e0ffab8e6c70323b1dedd3");
|
|
31
|
-
|
|
32
|
-
private PushyConfiguration() {}
|
|
33
|
-
|
|
34
|
-
static String normalize(String json) throws JSONException {
|
|
35
|
-
JSONObject options = new JSONObject(json);
|
|
36
|
-
for (Iterator<String> keys = options.keys(); keys.hasNext();) {
|
|
37
|
-
String key = keys.next();
|
|
38
|
-
if (!KEYS.contains(key)) {
|
|
39
|
-
throw invalid("unknown option " + key);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
String appKey = string(options.opt("appKey"), "appKey", false);
|
|
43
|
-
boolean customEndpoints = options.has("endpoints");
|
|
44
|
-
JSONArray endpoints = urls(customEndpoints ? options.get("endpoints")
|
|
45
|
-
: new JSONArray(Arrays.asList(ENDPOINTS)), "endpoints", true);
|
|
46
|
-
JSONArray queryUrls = urls(options.has("queryUrls") ? options.get("queryUrls")
|
|
47
|
-
: new JSONArray(Arrays.asList(customEndpoints ? new String[0] : QUERY_URLS)),
|
|
48
|
-
"queryUrls", false);
|
|
49
|
-
String afterDownload = options.has("afterDownload")
|
|
50
|
-
? string(options.get("afterDownload"), "afterDownload", false) : "none";
|
|
51
|
-
if (!"none".equals(afterDownload) && !POLICY_NEXT_LAUNCH.equals(afterDownload)) {
|
|
52
|
-
throw invalid("afterDownload must be none or " + POLICY_NEXT_LAUNCH);
|
|
53
|
-
}
|
|
54
|
-
Object disabled = options.has("disabled") ? options.get("disabled") : Boolean.FALSE;
|
|
55
|
-
if (!(disabled instanceof Boolean)) {
|
|
56
|
-
throw invalid("disabled must be a boolean");
|
|
57
|
-
}
|
|
58
|
-
JSONObject result = new JSONObject();
|
|
59
|
-
result.put("appKey", appKey);
|
|
60
|
-
result.put("endpoints", endpoints);
|
|
61
|
-
result.put("queryUrls", queryUrls);
|
|
62
|
-
result.put("afterDownload", afterDownload);
|
|
63
|
-
result.put("disabled", disabled);
|
|
64
|
-
result.put("rnu", options.has("rnu") ? string(options.get("rnu"), "rnu", true) : "");
|
|
65
|
-
result.put("rn", options.has("rn") ? string(options.get("rn"), "rn", true) : "");
|
|
66
|
-
if (options.has("packageVersion")) {
|
|
67
|
-
result.put("packageVersion", string(options.get("packageVersion"), "packageVersion", false));
|
|
68
|
-
}
|
|
69
|
-
return result.toString();
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
private static String string(Object value, String name, boolean allowEmpty) {
|
|
73
|
-
if (!(value instanceof String) || (!allowEmpty && ((String) value).trim().isEmpty())) {
|
|
74
|
-
throw invalid(name + " must be a string" + (allowEmpty ? "" : " and must not be blank"));
|
|
75
|
-
}
|
|
76
|
-
return (String) value;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
private static JSONArray urls(Object value, String name, boolean base) throws JSONException {
|
|
80
|
-
if (!(value instanceof JSONArray) || (base && ((JSONArray) value).length() == 0)) {
|
|
81
|
-
throw invalid(name + " must be " + (base ? "a non-empty" : "an") + " array");
|
|
82
|
-
}
|
|
83
|
-
JSONArray values = (JSONArray) value;
|
|
84
|
-
JSONArray result = new JSONArray();
|
|
85
|
-
Set<String> seen = new HashSet<>();
|
|
86
|
-
for (int i = 0; i < values.length(); i++) {
|
|
87
|
-
String url = string(values.get(i), name, false).trim();
|
|
88
|
-
if (!URL.matcher(url).find() || url.matches("(?s).*\\s.*") || url.contains("\\")
|
|
89
|
-
|| (base && (url.contains("?") || url.contains("#")))) {
|
|
90
|
-
throw invalid(name + " requires absolute HTTP(S) URLs without credentials"
|
|
91
|
-
+ (base ? ", queries or fragments" : ""));
|
|
92
|
-
}
|
|
93
|
-
String normalized = base ? url.replaceAll("/+$", "") : url;
|
|
94
|
-
if (seen.add(normalized)) {
|
|
95
|
-
result.put(normalized);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return result;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
private static IllegalArgumentException invalid(String message) {
|
|
102
|
-
return new IllegalArgumentException("Invalid native configuration: " + message);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
package cn.reactnative.modules.update;
|
|
2
|
-
|
|
3
|
-
import android.util.Log;
|
|
4
|
-
import androidx.annotation.Nullable;
|
|
5
|
-
import com.facebook.react.bridge.Promise;
|
|
6
|
-
import com.facebook.react.bridge.UiThreadUtil;
|
|
7
|
-
|
|
8
|
-
final class UiThreadRunner {
|
|
9
|
-
interface Operation {
|
|
10
|
-
void run() throws Throwable;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
private UiThreadRunner() {
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
static void run(
|
|
17
|
-
@Nullable final Promise promise,
|
|
18
|
-
final String errorCode,
|
|
19
|
-
final String operationName,
|
|
20
|
-
final Operation operation
|
|
21
|
-
) {
|
|
22
|
-
UiThreadUtil.runOnUiThread(new Runnable() {
|
|
23
|
-
@Override
|
|
24
|
-
public void run() {
|
|
25
|
-
try {
|
|
26
|
-
operation.run();
|
|
27
|
-
} catch (Throwable error) {
|
|
28
|
-
if (promise != null) {
|
|
29
|
-
promise.reject(errorCode, operationName + " failed", error);
|
|
30
|
-
} else {
|
|
31
|
-
Log.e(Texts.LOG_TAG, operationName + " failed", error);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
}
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
package cn.reactnative.modules.update;
|
|
2
|
-
|
|
3
|
-
import com.facebook.react.bridge.Promise;
|
|
4
|
-
import com.facebook.react.bridge.ReactApplicationContext;
|
|
5
|
-
import com.facebook.react.bridge.ReadableMap;
|
|
6
|
-
import java.util.Map;
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* New-architecture bridge: the codegen spec overrides only, every method
|
|
10
|
-
* forwards to UpdateModuleImpl.
|
|
11
|
-
*/
|
|
12
|
-
public class UpdateModule extends NativePushySpec {
|
|
13
|
-
private final UpdateModuleImpl impl;
|
|
14
|
-
|
|
15
|
-
public UpdateModule(ReactApplicationContext reactContext, UpdateContext updateContext) {
|
|
16
|
-
super(reactContext);
|
|
17
|
-
this.impl = new UpdateModuleImpl(reactContext, updateContext);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
public UpdateModule(ReactApplicationContext reactContext) {
|
|
21
|
-
this(reactContext, UpdateContext.getInstance(reactContext));
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
@Override
|
|
25
|
-
protected Map<String, Object> getTypedExportedConstants() {
|
|
26
|
-
return impl.getConstants();
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
@Override
|
|
30
|
-
public String getName() {
|
|
31
|
-
return UpdateModuleImpl.NAME;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
@Override
|
|
35
|
-
public void downloadFullUpdate(ReadableMap options, Promise promise) {
|
|
36
|
-
impl.downloadFullUpdate(options, promise);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
@Override
|
|
40
|
-
public void downloadAndInstallApk(ReadableMap options, Promise promise) {
|
|
41
|
-
impl.downloadAndInstallApk(options, promise);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
@Override
|
|
45
|
-
public void downloadPatchFromPackage(ReadableMap options, Promise promise) {
|
|
46
|
-
impl.downloadPatchFromPackage(options, promise);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
@Override
|
|
50
|
-
public void downloadPatchFromPpk(ReadableMap options, Promise promise) {
|
|
51
|
-
impl.downloadPatchFromPpk(options, promise);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
@Override
|
|
55
|
-
public void reloadUpdate(ReadableMap options, Promise promise) {
|
|
56
|
-
impl.reloadUpdate(options, promise);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
@Override
|
|
60
|
-
public void restartApp(Promise promise) {
|
|
61
|
-
impl.restartApp(null, promise);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
@Override
|
|
65
|
-
public void setNeedUpdate(ReadableMap options, Promise promise) {
|
|
66
|
-
impl.setNeedUpdate(options, promise);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
@Override
|
|
70
|
-
public void markSuccess(Promise promise) {
|
|
71
|
-
impl.markSuccess(promise);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
@Override
|
|
75
|
-
public void getBundleHash(Promise promise) {
|
|
76
|
-
impl.getBundleHash(promise);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
@Override
|
|
80
|
-
public void resetToPackagedBundle(Promise promise) {
|
|
81
|
-
impl.resetToPackagedBundle(promise);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
@Override
|
|
85
|
-
public void setUuid(String uuid, Promise promise) {
|
|
86
|
-
impl.setUuid(uuid, promise);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
@Override
|
|
90
|
-
public void syncNativeConfig(String config, Promise promise) {
|
|
91
|
-
impl.syncNativeConfig(config, promise);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
@Override
|
|
95
|
-
public void getNativeCheckCache(Promise promise) {
|
|
96
|
-
impl.getNativeCheckCache(promise);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
@Override
|
|
100
|
-
public void markJsCheckCompleted(String config, Promise promise) {
|
|
101
|
-
impl.markJsCheckCompleted(config, promise);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
@Override
|
|
105
|
-
public void setLocalHashInfo(String hash, String info, Promise promise) {
|
|
106
|
-
impl.setLocalHashInfo(hash, info, promise);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
@Override
|
|
110
|
-
public void getLocalHashInfo(String hash, Promise promise) {
|
|
111
|
-
impl.getLocalHashInfo(hash, promise);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
@Override
|
|
115
|
-
public void addListener(String eventName) {
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
@Override
|
|
119
|
-
public void removeListeners(double count) {
|
|
120
|
-
}
|
|
121
|
-
}
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
package cn.reactnative.modules.update;
|
|
2
|
-
|
|
3
|
-
import com.facebook.react.bridge.Promise;
|
|
4
|
-
import com.facebook.react.bridge.ReactApplicationContext;
|
|
5
|
-
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
6
|
-
import com.facebook.react.bridge.ReactMethod;
|
|
7
|
-
import com.facebook.react.bridge.ReadableMap;
|
|
8
|
-
import java.util.Map;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Old-architecture bridge: @ReactMethod declarations only, every method
|
|
12
|
-
* forwards to UpdateModuleImpl. Each state-changing method takes a trailing
|
|
13
|
-
* Promise, like the TurboModule spec, so a failed persistence rejects instead
|
|
14
|
-
* of resolving early (CODE_AUDIT 2.3).
|
|
15
|
-
*/
|
|
16
|
-
public class UpdateModule extends ReactContextBaseJavaModule {
|
|
17
|
-
private final UpdateModuleImpl impl;
|
|
18
|
-
|
|
19
|
-
public UpdateModule(ReactApplicationContext reactContext, UpdateContext updateContext) {
|
|
20
|
-
super(reactContext);
|
|
21
|
-
this.impl = new UpdateModuleImpl(reactContext, updateContext);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
public UpdateModule(ReactApplicationContext reactContext) {
|
|
25
|
-
this(reactContext, UpdateContext.getInstance(reactContext));
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
@Override
|
|
29
|
-
public Map<String, Object> getConstants() {
|
|
30
|
-
return impl.getConstants();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
@Override
|
|
34
|
-
public String getName() {
|
|
35
|
-
return UpdateModuleImpl.NAME;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
@ReactMethod
|
|
39
|
-
public void downloadFullUpdate(ReadableMap options, Promise promise) {
|
|
40
|
-
impl.downloadFullUpdate(options, promise);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
@ReactMethod
|
|
44
|
-
public void downloadAndInstallApk(ReadableMap options, Promise promise) {
|
|
45
|
-
impl.downloadAndInstallApk(options, promise);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
@ReactMethod
|
|
49
|
-
public void downloadPatchFromPackage(ReadableMap options, Promise promise) {
|
|
50
|
-
impl.downloadPatchFromPackage(options, promise);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
@ReactMethod
|
|
54
|
-
public void downloadPatchFromPpk(ReadableMap options, Promise promise) {
|
|
55
|
-
impl.downloadPatchFromPpk(options, promise);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
@ReactMethod
|
|
59
|
-
public void reloadUpdate(ReadableMap options, Promise promise) {
|
|
60
|
-
impl.reloadUpdate(options, promise);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
@ReactMethod
|
|
64
|
-
public void restartApp(Promise promise) {
|
|
65
|
-
impl.restartApp(null, promise);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
@ReactMethod
|
|
69
|
-
public void setNeedUpdate(ReadableMap options, Promise promise) {
|
|
70
|
-
impl.setNeedUpdate(options, promise);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
@ReactMethod
|
|
74
|
-
public void markSuccess(Promise promise) {
|
|
75
|
-
impl.markSuccess(promise);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
@ReactMethod
|
|
79
|
-
public void getBundleHash(Promise promise) {
|
|
80
|
-
impl.getBundleHash(promise);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
@ReactMethod
|
|
84
|
-
public void resetToPackagedBundle(Promise promise) {
|
|
85
|
-
impl.resetToPackagedBundle(promise);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
@ReactMethod
|
|
89
|
-
public void setUuid(String uuid, Promise promise) {
|
|
90
|
-
impl.setUuid(uuid, promise);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
@ReactMethod
|
|
94
|
-
public void syncNativeConfig(String config, Promise promise) {
|
|
95
|
-
impl.syncNativeConfig(config, promise);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
@ReactMethod
|
|
99
|
-
public void getNativeCheckCache(Promise promise) {
|
|
100
|
-
impl.getNativeCheckCache(promise);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
@ReactMethod
|
|
104
|
-
public void markJsCheckCompleted(String config, Promise promise) {
|
|
105
|
-
impl.markJsCheckCompleted(config, promise);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
@ReactMethod
|
|
109
|
-
public void setLocalHashInfo(String hash, String info, Promise promise) {
|
|
110
|
-
impl.setLocalHashInfo(hash, info, promise);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
@ReactMethod
|
|
114
|
-
public void getLocalHashInfo(String hash, Promise promise) {
|
|
115
|
-
impl.getLocalHashInfo(hash, promise);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
@ReactMethod
|
|
119
|
-
public void addListener(String eventName) {
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
@ReactMethod
|
|
123
|
-
public void removeListeners(Integer count) {
|
|
124
|
-
}
|
|
125
|
-
}
|