react-native-update 10.43.3 → 10.45.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/android/jni/Android.mk +0 -1
- 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/BundledResourceCopier.java +24 -12
- package/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +33 -9
- package/android/src/main/java/cn/reactnative/modules/update/ReactReloadManager.java +9 -1
- package/android/src/main/java/cn/reactnative/modules/update/StateSerialRunner.java +64 -0
- package/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +8 -4
- package/android/src/main/java/cn/reactnative/modules/update/UpdateEventEmitter.java +13 -1
- package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleImpl.java +8 -8
- package/cpp/patch_core/archive_patch_core.cpp +16 -0
- package/cpp/patch_core/archive_patch_core.h +5 -0
- package/cpp/patch_core/jni_util.h +56 -0
- package/cpp/patch_core/patch_core.cpp +16 -1
- package/cpp/patch_core/patch_core.h +6 -0
- package/cpp/patch_core/patch_core_android.cpp +4 -37
- package/cpp/patch_core/state_ops.h +24 -0
- package/cpp/patch_core/tests/patch_core_test.cpp +109 -2
- package/cpp/patch_core/update_core_android.cpp +43 -69
- package/harmony/pushy/src/main/cpp/pushy.cpp +163 -128
- package/harmony/pushy/src/main/ets/DownloadTask.ts +72 -19
- package/harmony/pushy/src/main/ets/NativePatchCore.ts +2 -6
- package/harmony/pushy/src/main/ets/PushyTurboModule.ts +10 -10
- package/harmony/pushy/src/main/ets/UpdateContext.ts +26 -9
- package/harmony/pushy.har +0 -0
- package/ios/RCTPushy/RCTPushy.mm +142 -111
- package/ios/RCTPushy/RCTPushyDownloader.mm +41 -2
- package/package.json +7 -3
- package/src/client.ts +161 -101
- package/src/context.ts +21 -7
- package/src/core.ts +5 -1
- package/src/index.ts +7 -1
- package/src/provider.tsx +91 -56
- package/src/utils.ts +58 -18
- package/android/jni/DownloadTask.c +0 -56
- package/android/jni/cn_reactnative_modules_update_DownloadTask.h +0 -21
- package/harmony/pushy/src/main/cpp/pushy.c +0 -117
- package/harmony/pushy/src/main/cpp/pushy.h +0 -8
- package/src/__tests__/setup.ts +0 -44
package/src/provider.tsx
CHANGED
|
@@ -2,6 +2,7 @@ import React, {
|
|
|
2
2
|
ReactNode,
|
|
3
3
|
useCallback,
|
|
4
4
|
useEffect,
|
|
5
|
+
useMemo,
|
|
5
6
|
useRef,
|
|
6
7
|
useState,
|
|
7
8
|
} from 'react';
|
|
@@ -24,10 +25,10 @@ import {
|
|
|
24
25
|
ProgressData,
|
|
25
26
|
UpdateTestPayload,
|
|
26
27
|
} from './type';
|
|
27
|
-
import { UpdateContext } from './context';
|
|
28
|
+
import { ProgressContext, UpdateContext } from './context';
|
|
28
29
|
import { URL } from 'react-native-url-polyfill';
|
|
29
30
|
import { resolveCheckResult } from './resolveCheckResult';
|
|
30
|
-
import { assertWeb, log } from './utils';
|
|
31
|
+
import { assertWeb, log, noop } from './utils';
|
|
31
32
|
|
|
32
33
|
export const UpdateProvider = ({
|
|
33
34
|
client,
|
|
@@ -44,7 +45,6 @@ export const UpdateProvider = ({
|
|
|
44
45
|
const updateInfoRef = useRef(updateInfo);
|
|
45
46
|
const [progress, setProgress] = useState<ProgressData>();
|
|
46
47
|
const [lastError, setLastError] = useState<Error>();
|
|
47
|
-
const lastChecking = useRef(0);
|
|
48
48
|
|
|
49
49
|
const throwErrorIfEnabled = useCallback(
|
|
50
50
|
(e: Error) => {
|
|
@@ -108,7 +108,6 @@ export const UpdateProvider = ({
|
|
|
108
108
|
if (!hash) {
|
|
109
109
|
return false;
|
|
110
110
|
}
|
|
111
|
-
stateListener.current && stateListener.current.remove();
|
|
112
111
|
|
|
113
112
|
if (
|
|
114
113
|
options.afterDownloadUpdate &&
|
|
@@ -162,15 +161,12 @@ export const UpdateProvider = ({
|
|
|
162
161
|
|
|
163
162
|
const checkUpdate = useCallback(
|
|
164
163
|
async ({ extra }: { extra?: Partial<{ toHash: string }> } = {}) => {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
lastChecking.current = now;
|
|
164
|
+
// No throttle here: the client already dedupes checks via its 5s
|
|
165
|
+
// response cache, and a second throttle layer silently returned
|
|
166
|
+
// undefined, indistinguishable from a failed check.
|
|
171
167
|
let rootInfo: CheckResult | undefined;
|
|
172
168
|
try {
|
|
173
|
-
rootInfo =
|
|
169
|
+
rootInfo = await client.checkUpdate(extra);
|
|
174
170
|
} catch (e: any) {
|
|
175
171
|
setLastError(e);
|
|
176
172
|
alertError(client.t('error_update_check_failed'), e.message);
|
|
@@ -178,6 +174,8 @@ export const UpdateProvider = ({
|
|
|
178
174
|
return;
|
|
179
175
|
}
|
|
180
176
|
if (!rootInfo) {
|
|
177
|
+
// Check was skipped or failed with no cached result; keep the last
|
|
178
|
+
// known updateInfo instead of overwriting it with an empty object.
|
|
181
179
|
return;
|
|
182
180
|
}
|
|
183
181
|
const info = resolveCheckResult(rootInfo);
|
|
@@ -198,7 +196,7 @@ export const UpdateProvider = ({
|
|
|
198
196
|
if (downloadUrl && sharedState.apkStatus === null) {
|
|
199
197
|
if (options.updateStrategy === 'silentAndNow') {
|
|
200
198
|
if (Platform.OS === 'android' && downloadUrl.endsWith('.apk')) {
|
|
201
|
-
downloadAndInstallApk(downloadUrl);
|
|
199
|
+
downloadAndInstallApk(downloadUrl).catch(noop);
|
|
202
200
|
} else {
|
|
203
201
|
Linking.openURL(downloadUrl);
|
|
204
202
|
}
|
|
@@ -215,7 +213,7 @@ export const UpdateProvider = ({
|
|
|
215
213
|
Platform.OS === 'android' &&
|
|
216
214
|
downloadUrl.endsWith('.apk')
|
|
217
215
|
) {
|
|
218
|
-
downloadAndInstallApk(downloadUrl);
|
|
216
|
+
downloadAndInstallApk(downloadUrl).catch(noop);
|
|
219
217
|
} else {
|
|
220
218
|
Linking.openURL(downloadUrl);
|
|
221
219
|
}
|
|
@@ -229,7 +227,7 @@ export const UpdateProvider = ({
|
|
|
229
227
|
options.updateStrategy === 'silentAndNow' ||
|
|
230
228
|
options.updateStrategy === 'silentAndLater'
|
|
231
229
|
) {
|
|
232
|
-
downloadUpdate(info);
|
|
230
|
+
downloadUpdate(info).catch(noop);
|
|
233
231
|
return info;
|
|
234
232
|
}
|
|
235
233
|
alertUpdate(
|
|
@@ -244,7 +242,7 @@ export const UpdateProvider = ({
|
|
|
244
242
|
text: client.t('alert_confirm'),
|
|
245
243
|
style: 'default',
|
|
246
244
|
onPress: () => {
|
|
247
|
-
downloadUpdate();
|
|
245
|
+
downloadUpdate().catch(noop);
|
|
248
246
|
},
|
|
249
247
|
},
|
|
250
248
|
],
|
|
@@ -272,9 +270,10 @@ export const UpdateProvider = ({
|
|
|
272
270
|
if (!assertWeb()) {
|
|
273
271
|
return;
|
|
274
272
|
}
|
|
275
|
-
const { checkStrategy,
|
|
273
|
+
const { checkStrategy, autoMarkSuccess } = options;
|
|
274
|
+
let markSuccessTimer: ReturnType<typeof setTimeout> | undefined;
|
|
276
275
|
if (autoMarkSuccess) {
|
|
277
|
-
setTimeout(() => {
|
|
276
|
+
markSuccessTimer = setTimeout(() => {
|
|
278
277
|
markSuccess();
|
|
279
278
|
}, 1000);
|
|
280
279
|
}
|
|
@@ -283,26 +282,38 @@ export const UpdateProvider = ({
|
|
|
283
282
|
'change',
|
|
284
283
|
nextAppState => {
|
|
285
284
|
if (nextAppState === 'active') {
|
|
286
|
-
checkUpdate();
|
|
285
|
+
checkUpdate().catch(noop);
|
|
287
286
|
}
|
|
288
287
|
},
|
|
289
288
|
);
|
|
290
289
|
}
|
|
291
290
|
if (checkStrategy === 'both' || checkStrategy === 'onAppStart') {
|
|
292
|
-
checkUpdate();
|
|
293
|
-
}
|
|
294
|
-
let dismissErrorTimer: ReturnType<typeof setTimeout>;
|
|
295
|
-
if (typeof dismissErrorAfter === 'number' && dismissErrorAfter > 0) {
|
|
296
|
-
dismissErrorTimer = setTimeout(() => {
|
|
297
|
-
dismissError();
|
|
298
|
-
}, dismissErrorAfter);
|
|
291
|
+
checkUpdate().catch(noop);
|
|
299
292
|
}
|
|
300
293
|
return () => {
|
|
294
|
+
if (markSuccessTimer) {
|
|
295
|
+
clearTimeout(markSuccessTimer);
|
|
296
|
+
}
|
|
301
297
|
stateListener.current && stateListener.current.remove();
|
|
302
|
-
clearTimeout(dismissErrorTimer);
|
|
303
298
|
};
|
|
304
299
|
}, [checkUpdate, options, dismissError, markSuccess, client]);
|
|
305
300
|
|
|
301
|
+
useEffect(() => {
|
|
302
|
+
const { dismissErrorAfter } = options;
|
|
303
|
+
if (
|
|
304
|
+
lastError &&
|
|
305
|
+
typeof dismissErrorAfter === 'number' &&
|
|
306
|
+
dismissErrorAfter > 0
|
|
307
|
+
) {
|
|
308
|
+
const dismissErrorTimer = setTimeout(() => {
|
|
309
|
+
dismissError();
|
|
310
|
+
}, dismissErrorAfter);
|
|
311
|
+
return () => {
|
|
312
|
+
clearTimeout(dismissErrorTimer);
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
}, [lastError, options, dismissError]);
|
|
316
|
+
|
|
306
317
|
const parseTestPayload = useCallback(
|
|
307
318
|
(payload: UpdateTestPayload) => {
|
|
308
319
|
if (payload && payload.type && payload.type.startsWith('__rnPushy')) {
|
|
@@ -314,15 +325,19 @@ export const UpdateProvider = ({
|
|
|
314
325
|
if (payload.type === '__rnPushyVersionHash') {
|
|
315
326
|
const toHash = payload.data;
|
|
316
327
|
sharedState.toHash = toHash;
|
|
317
|
-
checkUpdate({ extra: { toHash } })
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
328
|
+
checkUpdate({ extra: { toHash } })
|
|
329
|
+
.then(() => {
|
|
330
|
+
if (updateInfoRef.current && updateInfoRef.current.upToDate) {
|
|
331
|
+
Alert.alert(
|
|
332
|
+
client.t('alert_info'),
|
|
333
|
+
client.t('alert_no_update_wait'),
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
})
|
|
337
|
+
.catch(noop)
|
|
338
|
+
.finally(() => {
|
|
339
|
+
options.logger = logger;
|
|
340
|
+
});
|
|
326
341
|
}
|
|
327
342
|
return true;
|
|
328
343
|
}
|
|
@@ -377,28 +392,48 @@ export const UpdateProvider = ({
|
|
|
377
392
|
};
|
|
378
393
|
}, [parseTestPayload]);
|
|
379
394
|
|
|
395
|
+
// progress lives in its own context (see context.ts), so this value only
|
|
396
|
+
// changes when the update state itself changes, not on every progress tick.
|
|
397
|
+
const contextValue = useMemo(
|
|
398
|
+
() => ({
|
|
399
|
+
checkUpdate,
|
|
400
|
+
switchVersion,
|
|
401
|
+
switchVersionLater,
|
|
402
|
+
dismissError,
|
|
403
|
+
updateInfo,
|
|
404
|
+
lastError,
|
|
405
|
+
markSuccess,
|
|
406
|
+
client,
|
|
407
|
+
downloadUpdate,
|
|
408
|
+
packageVersion,
|
|
409
|
+
currentHash: currentVersion,
|
|
410
|
+
downloadAndInstallApk,
|
|
411
|
+
getCurrentVersionInfo,
|
|
412
|
+
currentVersionInfo,
|
|
413
|
+
parseTestQrCode,
|
|
414
|
+
restartApp,
|
|
415
|
+
}),
|
|
416
|
+
[
|
|
417
|
+
checkUpdate,
|
|
418
|
+
switchVersion,
|
|
419
|
+
switchVersionLater,
|
|
420
|
+
dismissError,
|
|
421
|
+
updateInfo,
|
|
422
|
+
lastError,
|
|
423
|
+
markSuccess,
|
|
424
|
+
client,
|
|
425
|
+
downloadUpdate,
|
|
426
|
+
downloadAndInstallApk,
|
|
427
|
+
parseTestQrCode,
|
|
428
|
+
restartApp,
|
|
429
|
+
],
|
|
430
|
+
);
|
|
431
|
+
|
|
380
432
|
return (
|
|
381
|
-
<UpdateContext.Provider
|
|
382
|
-
value={
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
switchVersionLater,
|
|
386
|
-
dismissError,
|
|
387
|
-
updateInfo,
|
|
388
|
-
lastError,
|
|
389
|
-
markSuccess,
|
|
390
|
-
client,
|
|
391
|
-
downloadUpdate,
|
|
392
|
-
packageVersion,
|
|
393
|
-
currentHash: currentVersion,
|
|
394
|
-
progress,
|
|
395
|
-
downloadAndInstallApk,
|
|
396
|
-
getCurrentVersionInfo,
|
|
397
|
-
currentVersionInfo,
|
|
398
|
-
parseTestQrCode,
|
|
399
|
-
restartApp,
|
|
400
|
-
}}>
|
|
401
|
-
{children}
|
|
433
|
+
<UpdateContext.Provider value={contextValue}>
|
|
434
|
+
<ProgressContext.Provider value={progress}>
|
|
435
|
+
{children}
|
|
436
|
+
</ProgressContext.Provider>
|
|
402
437
|
</UpdateContext.Provider>
|
|
403
438
|
);
|
|
404
439
|
};
|
package/src/utils.ts
CHANGED
|
@@ -22,6 +22,10 @@ export const DEFAULT_FETCH_TIMEOUT_MS = 5000;
|
|
|
22
22
|
|
|
23
23
|
export function promiseAny<T>(promises: Promise<T>[]) {
|
|
24
24
|
return new Promise<T>((resolve, reject) => {
|
|
25
|
+
if (!promises.length) {
|
|
26
|
+
reject(Error(i18n.t('error_all_promises_rejected')));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
25
29
|
let count = 0;
|
|
26
30
|
|
|
27
31
|
promises.forEach(promise => {
|
|
@@ -84,7 +88,7 @@ export function joinUrls(paths: string[], fileName?: string) {
|
|
|
84
88
|
}
|
|
85
89
|
}
|
|
86
90
|
|
|
87
|
-
export const testUrls = async (urls?: string[]) => {
|
|
91
|
+
export const testUrls = async (urls?: string[]): Promise<string | null> => {
|
|
88
92
|
if (!urls?.length) {
|
|
89
93
|
return null;
|
|
90
94
|
}
|
|
@@ -93,7 +97,7 @@ export const testUrls = async (urls?: string[]) => {
|
|
|
93
97
|
const ret = await promiseAny(urls.map(ping));
|
|
94
98
|
if (ret) {
|
|
95
99
|
log('ping success, use url:', ret);
|
|
96
|
-
return ret;
|
|
100
|
+
return ret as string;
|
|
97
101
|
}
|
|
98
102
|
} catch {}
|
|
99
103
|
log('all ping failed, use first url:', urls[0]);
|
|
@@ -118,21 +122,50 @@ export const fetchWithTimeout = (
|
|
|
118
122
|
params: Parameters<typeof fetch>[1],
|
|
119
123
|
timeoutMs = DEFAULT_FETCH_TIMEOUT_MS,
|
|
120
124
|
): Promise<Response> => {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
125
|
+
// AbortController landed in the RN fetch polyfill around 0.60; we support
|
|
126
|
+
// older peers, so fall back to a plain timer race when it is unavailable
|
|
127
|
+
// (the losing fetch keeps running there — old runtimes can't do better).
|
|
128
|
+
if (typeof AbortController === 'undefined') {
|
|
129
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
130
|
+
return Promise.race([
|
|
131
|
+
enhancedFetch(url, params),
|
|
132
|
+
new Promise<Response>((_, reject) => {
|
|
133
|
+
timeoutId = setTimeout(() => {
|
|
134
|
+
log('fetch timeout', url);
|
|
135
|
+
reject(Error(i18n.t('error_ping_timeout')));
|
|
136
|
+
}, timeoutMs);
|
|
137
|
+
}),
|
|
138
|
+
]).finally(() => {
|
|
139
|
+
if (timeoutId) {
|
|
140
|
+
clearTimeout(timeoutId);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Abort the underlying request on timeout instead of racing a timer: with
|
|
146
|
+
// Promise.race the losing fetch kept running (and kept the connection busy)
|
|
147
|
+
// long after the caller had already moved on.
|
|
148
|
+
const controller = new AbortController();
|
|
149
|
+
const timeoutId = setTimeout(() => {
|
|
150
|
+
log('fetch timeout', url);
|
|
151
|
+
controller.abort();
|
|
152
|
+
}, timeoutMs);
|
|
153
|
+
|
|
154
|
+
return enhancedFetch(url, { ...params, signal: controller.signal })
|
|
155
|
+
.catch((e: any) => {
|
|
156
|
+
if (controller.signal.aborted) {
|
|
157
|
+
throw Error(i18n.t('error_ping_timeout'));
|
|
158
|
+
}
|
|
159
|
+
throw e;
|
|
160
|
+
})
|
|
161
|
+
.finally(() => {
|
|
133
162
|
clearTimeout(timeoutId);
|
|
134
|
-
}
|
|
135
|
-
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const isIdempotentRequest = (params: Parameters<typeof fetch>[1]) => {
|
|
167
|
+
const method = params?.method?.toUpperCase() ?? 'GET';
|
|
168
|
+
return method === 'GET' || method === 'HEAD';
|
|
136
169
|
};
|
|
137
170
|
|
|
138
171
|
export const enhancedFetch = async (
|
|
@@ -143,10 +176,17 @@ export const enhancedFetch = async (
|
|
|
143
176
|
return fetch(url, params)
|
|
144
177
|
.catch(e => {
|
|
145
178
|
log('fetch error', url, e);
|
|
146
|
-
if (
|
|
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
|
+
) {
|
|
147
187
|
throw e;
|
|
148
188
|
}
|
|
149
189
|
log('trying fallback to http');
|
|
150
|
-
return enhancedFetch(url.replace(
|
|
190
|
+
return enhancedFetch(url.replace(/^https:/, 'http:'), params, true);
|
|
151
191
|
});
|
|
152
192
|
};
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Created by DengYun on 3/31/16.
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
#include "cn_reactnative_modules_update_DownloadTask.h"
|
|
6
|
-
|
|
7
|
-
#include "hpatch.h"
|
|
8
|
-
#define _check(v,errInfo) do{ if (!(v)) { _isError=hpatch_TRUE; _errInfo=errInfo; goto _clear; } }while(0)
|
|
9
|
-
|
|
10
|
-
JNIEXPORT jbyteArray JNICALL Java_cn_reactnative_modules_update_DownloadTask_hdiffPatch
|
|
11
|
-
(JNIEnv *env, jobject self, jbyteArray origin, jbyteArray patch){
|
|
12
|
-
hpatch_BOOL _isError=hpatch_FALSE;
|
|
13
|
-
const char* _errInfo="";
|
|
14
|
-
|
|
15
|
-
jbyte* originPtr = (*env)->GetByteArrayElements(env, origin, NULL);
|
|
16
|
-
size_t originLength = (*env)->GetArrayLength(env, origin);
|
|
17
|
-
jbyte* patchPtr = (*env)->GetByteArrayElements(env, patch, NULL);
|
|
18
|
-
size_t patchLength = (*env)->GetArrayLength(env, patch);
|
|
19
|
-
jbyteArray ret = NULL;
|
|
20
|
-
jbyte* outPtr = NULL;
|
|
21
|
-
size_t newsize = 0;
|
|
22
|
-
hpatch_singleCompressedDiffInfo patInfo;
|
|
23
|
-
|
|
24
|
-
_check(((originLength==0)||originPtr) && patchPtr && (patchLength>0),"Corrupt patch");
|
|
25
|
-
_check(kHPatch_ok==hpatch_getInfo_by_mem(&patInfo,(const uint8_t*)patchPtr,patchLength),"Error info in hpatch");
|
|
26
|
-
_check(originLength==patInfo.oldDataSize,"Error oldDataSize in hpatch");
|
|
27
|
-
newsize=(size_t)patInfo.newDataSize;
|
|
28
|
-
if (sizeof(size_t)!=sizeof(hpatch_StreamPos_t))
|
|
29
|
-
_check(newsize==patInfo.newDataSize,"Error newDataSize in hpatch");
|
|
30
|
-
|
|
31
|
-
ret = (*env)->NewByteArray(env,newsize);
|
|
32
|
-
_check(ret,"Error JNIEnv::NewByteArray()");
|
|
33
|
-
if (newsize>0) {
|
|
34
|
-
outPtr = (*env)->GetByteArrayElements(env, ret, NULL);
|
|
35
|
-
_check(outPtr,"Corrupt JNIEnv::GetByteArrayElements");
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
_check(kHPatch_ok==hpatch_by_mem((const uint8_t*)originPtr,originLength,(uint8_t*)outPtr,newsize,
|
|
39
|
-
(const uint8_t*)patchPtr,patchLength,&patInfo),"hpacth");
|
|
40
|
-
|
|
41
|
-
_clear:
|
|
42
|
-
if (outPtr) (*env)->ReleaseByteArrayElements(env, ret, outPtr, (_isError?JNI_ABORT:0));
|
|
43
|
-
if (originPtr) (*env)->ReleaseByteArrayElements(env, origin, originPtr, JNI_ABORT);
|
|
44
|
-
if (patchPtr) (*env)->ReleaseByteArrayElements(env, patch, patchPtr, JNI_ABORT);
|
|
45
|
-
if (_isError){
|
|
46
|
-
jclass newExcCls = NULL;
|
|
47
|
-
if (ret){
|
|
48
|
-
(*env)->DeleteLocalRef(env, ret);
|
|
49
|
-
ret = NULL;
|
|
50
|
-
}
|
|
51
|
-
newExcCls = (*env)->FindClass(env, "java/lang/Error");
|
|
52
|
-
if (newExcCls != NULL) // Unable to find the new exception class, give up.
|
|
53
|
-
(*env)->ThrowNew(env, newExcCls, _errInfo);
|
|
54
|
-
}
|
|
55
|
-
return ret;
|
|
56
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/* DO NOT EDIT THIS FILE - it is machine generated */
|
|
2
|
-
#include <jni.h>
|
|
3
|
-
/* Header for class cn_reactnative_modules_update_DownloadTask */
|
|
4
|
-
|
|
5
|
-
#ifndef _Included_cn_reactnative_modules_update_DownloadTask
|
|
6
|
-
#define _Included_cn_reactnative_modules_update_DownloadTask
|
|
7
|
-
#ifdef __cplusplus
|
|
8
|
-
extern "C" {
|
|
9
|
-
#endif
|
|
10
|
-
/*
|
|
11
|
-
* Class: cn_reactnative_modules_update_DownloadTask
|
|
12
|
-
* Method: hdiffPatch
|
|
13
|
-
* Signature: ([B[B)[B
|
|
14
|
-
*/
|
|
15
|
-
JNIEXPORT jbyteArray JNICALL Java_cn_reactnative_modules_update_DownloadTask_hdiffPatch
|
|
16
|
-
(JNIEnv *, jclass, jbyteArray, jbyteArray);
|
|
17
|
-
|
|
18
|
-
#ifdef __cplusplus
|
|
19
|
-
}
|
|
20
|
-
#endif
|
|
21
|
-
#endif
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
#include "pushy.h"
|
|
2
|
-
#include "hpatch.h"
|
|
3
|
-
#include <napi/native_api.h>
|
|
4
|
-
#include <js_native_api.h>
|
|
5
|
-
#include <js_native_api_types.h>
|
|
6
|
-
|
|
7
|
-
#define _check(v,errInfo) do { \
|
|
8
|
-
if (!(v)) { \
|
|
9
|
-
_isError = hpatch_TRUE; \
|
|
10
|
-
_errInfo = errInfo; \
|
|
11
|
-
goto _clear; \
|
|
12
|
-
} \
|
|
13
|
-
} while(0)
|
|
14
|
-
|
|
15
|
-
napi_value HdiffPatch(napi_env env, napi_callback_info info) {
|
|
16
|
-
napi_status status;
|
|
17
|
-
size_t argc = 2;
|
|
18
|
-
napi_value args[2];
|
|
19
|
-
hpatch_BOOL _isError = hpatch_FALSE;
|
|
20
|
-
const char* _errInfo = "";
|
|
21
|
-
|
|
22
|
-
// 获取参数
|
|
23
|
-
status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
|
|
24
|
-
if (status != napi_ok || argc < 2) {
|
|
25
|
-
napi_throw_error(env, NULL, "Wrong number of arguments");
|
|
26
|
-
return NULL;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// 获取origin buffer
|
|
30
|
-
bool isTypedArray;
|
|
31
|
-
status = napi_is_typedarray(env, args[0], &isTypedArray);
|
|
32
|
-
if (status != napi_ok || !isTypedArray) {
|
|
33
|
-
napi_throw_error(env, NULL, "First argument must be a TypedArray");
|
|
34
|
-
return NULL;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
uint8_t* originPtr;
|
|
38
|
-
size_t originLength;
|
|
39
|
-
status = napi_get_typedarray_info(env, args[0], NULL, &originLength,
|
|
40
|
-
(void**)&originPtr, NULL, NULL);
|
|
41
|
-
if (status != napi_ok) {
|
|
42
|
-
napi_throw_error(env, NULL, "Failed to get origin buffer");
|
|
43
|
-
return NULL;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// 获取patch buffer
|
|
47
|
-
status = napi_is_typedarray(env, args[1], &isTypedArray);
|
|
48
|
-
if (status != napi_ok || !isTypedArray) {
|
|
49
|
-
napi_throw_error(env, NULL, "Second argument must be a TypedArray");
|
|
50
|
-
return NULL;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
uint8_t* patchPtr;
|
|
54
|
-
size_t patchLength;
|
|
55
|
-
status = napi_get_typedarray_info(env, args[1], NULL, &patchLength,
|
|
56
|
-
(void**)&patchPtr, NULL, NULL);
|
|
57
|
-
if (status != napi_ok) {
|
|
58
|
-
napi_throw_error(env, NULL, "Failed to get patch buffer");
|
|
59
|
-
return NULL;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// 执行patch操作
|
|
63
|
-
hpatch_singleCompressedDiffInfo patInfo;
|
|
64
|
-
|
|
65
|
-
_check(((originLength==0)||originPtr) && patchPtr && (patchLength>0), "Corrupt patch");
|
|
66
|
-
_check(kHPatch_ok==hpatch_getInfo_by_mem(&patInfo, patchPtr, patchLength), "Error info in hpatch");
|
|
67
|
-
_check(originLength==patInfo.oldDataSize, "Error oldDataSize in hpatch");
|
|
68
|
-
size_t newsize = (size_t)patInfo.newDataSize;
|
|
69
|
-
if (sizeof(size_t)!=sizeof(hpatch_StreamPos_t))
|
|
70
|
-
_check(newsize==patInfo.newDataSize, "Error newDataSize in hpatch");
|
|
71
|
-
|
|
72
|
-
// 创建结果buffer
|
|
73
|
-
napi_value resultBuffer;
|
|
74
|
-
uint8_t* outPtr;
|
|
75
|
-
void* data;
|
|
76
|
-
|
|
77
|
-
status = napi_create_arraybuffer(env, newsize, &data, &resultBuffer);
|
|
78
|
-
if (status != napi_ok) {
|
|
79
|
-
napi_throw_error(env, NULL, "Failed to create result buffer");
|
|
80
|
-
return NULL;
|
|
81
|
-
}
|
|
82
|
-
outPtr = (uint8_t*)data;
|
|
83
|
-
|
|
84
|
-
// 执行patch
|
|
85
|
-
_check(kHPatch_ok==hpatch_by_mem(originPtr, originLength, outPtr, newsize,
|
|
86
|
-
patchPtr, patchLength, &patInfo), "hpatch");
|
|
87
|
-
return resultBuffer;
|
|
88
|
-
|
|
89
|
-
_clear:
|
|
90
|
-
if (_isError) {
|
|
91
|
-
napi_throw_error(env, NULL, _errInfo);
|
|
92
|
-
return NULL;
|
|
93
|
-
}
|
|
94
|
-
return NULL;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// 模块初始化
|
|
98
|
-
napi_value Init(napi_env env, napi_value exports) {
|
|
99
|
-
napi_status status;
|
|
100
|
-
napi_value fn;
|
|
101
|
-
|
|
102
|
-
status = napi_create_function(env, NULL, 0, HdiffPatch, NULL, &fn);
|
|
103
|
-
if (status != napi_ok) {
|
|
104
|
-
napi_throw_error(env, NULL, "Unable to wrap native function");
|
|
105
|
-
return NULL;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
status = napi_set_named_property(env, exports, "hdiffPatch", fn);
|
|
109
|
-
if (status != napi_ok) {
|
|
110
|
-
napi_throw_error(env, NULL, "Unable to populate exports");
|
|
111
|
-
return NULL;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return exports;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
|
package/src/__tests__/setup.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { mock } from 'bun:test';
|
|
2
|
-
|
|
3
|
-
mock.module('react-native', () => {
|
|
4
|
-
return {
|
|
5
|
-
Platform: {
|
|
6
|
-
OS: 'ios',
|
|
7
|
-
Version: 13,
|
|
8
|
-
},
|
|
9
|
-
DeviceEventEmitter: {
|
|
10
|
-
addListener: () => ({ remove: () => {} }),
|
|
11
|
-
},
|
|
12
|
-
NativeModules: {
|
|
13
|
-
Pushy: {
|
|
14
|
-
currentVersionInfo: '{}',
|
|
15
|
-
downloadRootDir: '/tmp',
|
|
16
|
-
packageVersion: '1.0.0',
|
|
17
|
-
currentVersion: 'hash',
|
|
18
|
-
isFirstTime: false,
|
|
19
|
-
rolledBackVersion: '',
|
|
20
|
-
buildTime: '2023-01-01',
|
|
21
|
-
uuid: 'uuid',
|
|
22
|
-
setLocalHashInfo: () => {},
|
|
23
|
-
getLocalHashInfo: () => Promise.resolve('{}'),
|
|
24
|
-
setUuid: () => {},
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
NativeEventEmitter: class {
|
|
28
|
-
addListener = () => ({ remove: () => {} });
|
|
29
|
-
removeAllListeners = () => {};
|
|
30
|
-
},
|
|
31
|
-
};
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
mock.module('../i18n', () => {
|
|
35
|
-
return {
|
|
36
|
-
default: {
|
|
37
|
-
t: (key: string, params?: any) => `${key}${params ? JSON.stringify(params) : ''}`,
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
mock.module('react-native/Libraries/Core/ReactNativeVersion', () => ({
|
|
43
|
-
version: { major: 0, minor: 73, patch: 0 },
|
|
44
|
-
}));
|