react-native-update 10.38.3 → 10.38.5
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/harmony/pushy/src/main/cpp/CMakeLists.txt +70 -0
- package/harmony/pushy/src/main/cpp/PushyPackage.h +55 -0
- package/harmony/pushy/src/main/cpp/PushyTurboModule.cpp +142 -0
- package/harmony/pushy/src/main/cpp/PushyTurboModule.h +38 -0
- package/harmony/pushy/src/main/cpp/pushy.c +117 -0
- package/harmony/pushy/src/main/cpp/pushy.cpp +856 -0
- package/harmony/pushy/src/main/cpp/pushy.h +8 -0
- package/harmony/pushy/src/main/ets/DownloadTask.ts +610 -0
- package/harmony/pushy/src/main/ets/DownloadTaskParams.ts +19 -0
- package/harmony/pushy/src/main/ets/EventHub.ts +39 -0
- package/harmony/pushy/src/main/ets/Logger.ts +52 -0
- package/harmony/pushy/src/main/ets/NativePatchCore.ts +87 -0
- package/harmony/pushy/src/main/ets/PushyFileJSBundleProvider.ets +50 -0
- package/harmony/pushy/src/main/ets/PushyPackage.ts +22 -0
- package/harmony/pushy/src/main/ets/PushyTurboModule.ts +139 -0
- package/harmony/pushy/src/main/ets/SaveFile.ts +34 -0
- package/harmony/pushy/src/main/ets/UpdateContext.ts +346 -0
- package/harmony/pushy/src/main/ets/UpdateModuleImpl.ts +123 -0
- package/harmony/pushy/src/main/module.json5 +7 -0
- package/harmony/pushy/src/main/resources/base/element/string.json +8 -0
- package/harmony/pushy/src/main/resources/en_US/element/string.json +8 -0
- package/harmony/pushy/src/main/resources/zh_CN/element/string.json +8 -0
- package/harmony/pushy.har +0 -0
- package/package.json +1 -1
- package/src/client.ts +19 -1
- package/src/provider.tsx +1 -0
- package/src/type.ts +9 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import preferences from '@ohos.data.preferences';
|
|
2
|
+
import fileIo from '@ohos.file.fs';
|
|
3
|
+
import { DownloadTask } from './DownloadTask';
|
|
4
|
+
import common from '@ohos.app.ability.common';
|
|
5
|
+
import { DownloadTaskParams } from './DownloadTaskParams';
|
|
6
|
+
import NativePatchCore, {
|
|
7
|
+
STATE_OP_CLEAR_FIRST_TIME,
|
|
8
|
+
STATE_OP_CLEAR_ROLLBACK_MARK,
|
|
9
|
+
STATE_OP_MARK_SUCCESS,
|
|
10
|
+
STATE_OP_RESOLVE_LAUNCH,
|
|
11
|
+
STATE_OP_ROLLBACK,
|
|
12
|
+
STATE_OP_SWITCH_VERSION,
|
|
13
|
+
StateCoreResult,
|
|
14
|
+
} from './NativePatchCore';
|
|
15
|
+
|
|
16
|
+
export class UpdateContext {
|
|
17
|
+
private context: common.UIAbilityContext;
|
|
18
|
+
private rootDir: string;
|
|
19
|
+
private preferences: preferences.Preferences;
|
|
20
|
+
private static DEBUG: boolean = false;
|
|
21
|
+
private static isUsingBundleUrl: boolean = false;
|
|
22
|
+
|
|
23
|
+
constructor(context: common.UIAbilityContext) {
|
|
24
|
+
this.context = context;
|
|
25
|
+
this.rootDir = context.filesDir + '/_update';
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
if (!fileIo.accessSync(this.rootDir)) {
|
|
29
|
+
fileIo.mkdirSync(this.rootDir);
|
|
30
|
+
}
|
|
31
|
+
} catch (e) {
|
|
32
|
+
console.error('Failed to create root directory:', e);
|
|
33
|
+
}
|
|
34
|
+
this.initPreferences();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private initPreferences() {
|
|
38
|
+
try {
|
|
39
|
+
this.preferences = preferences.getPreferencesSync(this.context, {
|
|
40
|
+
name: 'update',
|
|
41
|
+
});
|
|
42
|
+
} catch (e) {
|
|
43
|
+
console.error('Failed to init preferences:', e);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private readString(key: string): string {
|
|
48
|
+
const value = this.preferences.getSync(key, '') as
|
|
49
|
+
| string
|
|
50
|
+
| boolean
|
|
51
|
+
| number
|
|
52
|
+
| null
|
|
53
|
+
| undefined;
|
|
54
|
+
if (typeof value === 'string') {
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
if (typeof value === 'number') {
|
|
58
|
+
return String(value);
|
|
59
|
+
}
|
|
60
|
+
if (typeof value === 'boolean') {
|
|
61
|
+
return value ? 'true' : 'false';
|
|
62
|
+
}
|
|
63
|
+
return '';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private readBoolean(key: string, defaultValue: boolean): boolean {
|
|
67
|
+
const value = this.preferences.getSync(key, defaultValue) as
|
|
68
|
+
| string
|
|
69
|
+
| boolean
|
|
70
|
+
| number
|
|
71
|
+
| null
|
|
72
|
+
| undefined;
|
|
73
|
+
if (typeof value === 'boolean') {
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
if (typeof value === 'string') {
|
|
77
|
+
if (value === 'true') {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
if (value === 'false') {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (typeof value === 'number') {
|
|
85
|
+
return value !== 0;
|
|
86
|
+
}
|
|
87
|
+
return defaultValue;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private putNullableString(key: string, value?: string): void {
|
|
91
|
+
if (value) {
|
|
92
|
+
this.preferences.putSync(key, value);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
this.preferences.deleteSync(key);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private getBundlePath(hash: string): string {
|
|
99
|
+
return `${this.rootDir}/${hash}/bundle.harmony.js`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private getStateSnapshot(): StateCoreResult {
|
|
103
|
+
return {
|
|
104
|
+
packageVersion: this.readString('packageVersion'),
|
|
105
|
+
buildTime: this.readString('buildTime'),
|
|
106
|
+
currentVersion: this.readString('currentVersion'),
|
|
107
|
+
lastVersion: this.readString('lastVersion'),
|
|
108
|
+
firstTime: this.readBoolean('firstTime', false),
|
|
109
|
+
firstTimeOk: this.readBoolean('firstTimeOk', true),
|
|
110
|
+
rolledBackVersion: this.readString('rolledBackVersion'),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private applyState(state: StateCoreResult): void {
|
|
115
|
+
this.putNullableString('packageVersion', state.packageVersion);
|
|
116
|
+
this.putNullableString('buildTime', state.buildTime);
|
|
117
|
+
this.putNullableString('currentVersion', state.currentVersion);
|
|
118
|
+
this.putNullableString('lastVersion', state.lastVersion);
|
|
119
|
+
this.preferences.putSync('firstTime', !!state.firstTime);
|
|
120
|
+
this.preferences.putSync('firstTimeOk', state.firstTimeOk !== false);
|
|
121
|
+
this.putNullableString('rolledBackVersion', state.rolledBackVersion);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
public syncStateWithBinaryVersion(
|
|
125
|
+
packageVersion: string,
|
|
126
|
+
buildTime: string,
|
|
127
|
+
): void {
|
|
128
|
+
const currentState = this.getStateSnapshot();
|
|
129
|
+
const nextState = NativePatchCore.syncStateWithBinaryVersion(
|
|
130
|
+
packageVersion,
|
|
131
|
+
buildTime,
|
|
132
|
+
currentState,
|
|
133
|
+
);
|
|
134
|
+
if (!nextState.changed) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
this.cleanUp();
|
|
139
|
+
this.preferences.clear();
|
|
140
|
+
this.applyState(nextState);
|
|
141
|
+
this.preferences.flush();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
public setKv(key: string, value: string): void {
|
|
145
|
+
this.preferences.putSync(key, value);
|
|
146
|
+
this.preferences.flush();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
public getKv(key: string): string {
|
|
150
|
+
return this.readString(key);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
public isFirstTime(): boolean {
|
|
154
|
+
return this.getStateSnapshot().firstTime;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
public rolledBackVersion(): string {
|
|
158
|
+
return this.getStateSnapshot().rolledBackVersion || '';
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
public markSuccess(): void {
|
|
162
|
+
if (UpdateContext.DEBUG) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const nextState = NativePatchCore.runStateCore(
|
|
167
|
+
STATE_OP_MARK_SUCCESS,
|
|
168
|
+
this.getStateSnapshot(),
|
|
169
|
+
);
|
|
170
|
+
this.applyState(nextState);
|
|
171
|
+
if (nextState.staleVersionToDelete) {
|
|
172
|
+
this.preferences.deleteSync(`hash_${nextState.staleVersionToDelete}`);
|
|
173
|
+
}
|
|
174
|
+
this.preferences.flush();
|
|
175
|
+
this.cleanUp();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
public clearFirstTime(): void {
|
|
179
|
+
const nextState = NativePatchCore.runStateCore(
|
|
180
|
+
STATE_OP_CLEAR_FIRST_TIME,
|
|
181
|
+
this.getStateSnapshot(),
|
|
182
|
+
);
|
|
183
|
+
this.applyState(nextState);
|
|
184
|
+
this.preferences.flush();
|
|
185
|
+
this.cleanUp();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
public clearRollbackMark(): void {
|
|
189
|
+
const nextState = NativePatchCore.runStateCore(
|
|
190
|
+
STATE_OP_CLEAR_ROLLBACK_MARK,
|
|
191
|
+
this.getStateSnapshot(),
|
|
192
|
+
);
|
|
193
|
+
this.applyState(nextState);
|
|
194
|
+
this.preferences.flush();
|
|
195
|
+
this.cleanUp();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
public async downloadFullUpdate(url: string, hash: string): Promise<void> {
|
|
199
|
+
try {
|
|
200
|
+
const params = new DownloadTaskParams();
|
|
201
|
+
params.type = DownloadTaskParams.TASK_TYPE_PATCH_FULL;
|
|
202
|
+
params.url = url;
|
|
203
|
+
params.hash = hash;
|
|
204
|
+
params.targetFile = `${this.rootDir}/${hash}.ppk`;
|
|
205
|
+
params.unzipDirectory = `${this.rootDir}/${hash}`;
|
|
206
|
+
const downloadTask = new DownloadTask(this.context);
|
|
207
|
+
await downloadTask.execute(params);
|
|
208
|
+
} catch (e) {
|
|
209
|
+
console.error('Failed to download full update:', e);
|
|
210
|
+
throw e;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
public async downloadFile(
|
|
215
|
+
url: string,
|
|
216
|
+
hash: string,
|
|
217
|
+
fileName: string,
|
|
218
|
+
): Promise<void> {
|
|
219
|
+
const params = new DownloadTaskParams();
|
|
220
|
+
params.type = DownloadTaskParams.TASK_TYPE_PLAIN_DOWNLOAD;
|
|
221
|
+
params.url = url;
|
|
222
|
+
params.hash = hash;
|
|
223
|
+
params.targetFile = this.rootDir + '/' + fileName;
|
|
224
|
+
|
|
225
|
+
const downloadTask = new DownloadTask(this.context);
|
|
226
|
+
await downloadTask.execute(params);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
public async downloadPatchFromPpk(
|
|
230
|
+
url: string,
|
|
231
|
+
hash: string,
|
|
232
|
+
originHash: string,
|
|
233
|
+
): Promise<void> {
|
|
234
|
+
const params = new DownloadTaskParams();
|
|
235
|
+
params.type = DownloadTaskParams.TASK_TYPE_PATCH_FROM_PPK;
|
|
236
|
+
params.url = url;
|
|
237
|
+
params.hash = hash;
|
|
238
|
+
params.originHash = originHash;
|
|
239
|
+
params.targetFile = `${this.rootDir}/${originHash}_${hash}.ppk.patch`;
|
|
240
|
+
params.unzipDirectory = `${this.rootDir}/${hash}`;
|
|
241
|
+
params.originDirectory = `${this.rootDir}/${params.originHash}`;
|
|
242
|
+
|
|
243
|
+
const downloadTask = new DownloadTask(this.context);
|
|
244
|
+
await downloadTask.execute(params);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
public async downloadPatchFromPackage(
|
|
248
|
+
url: string,
|
|
249
|
+
hash: string,
|
|
250
|
+
): Promise<void> {
|
|
251
|
+
try {
|
|
252
|
+
const params = new DownloadTaskParams();
|
|
253
|
+
params.type = DownloadTaskParams.TASK_TYPE_PATCH_FROM_APP;
|
|
254
|
+
params.url = url;
|
|
255
|
+
params.hash = hash;
|
|
256
|
+
params.targetFile = `${this.rootDir}/${hash}.app.patch`;
|
|
257
|
+
params.unzipDirectory = `${this.rootDir}/${hash}`;
|
|
258
|
+
|
|
259
|
+
const downloadTask = new DownloadTask(this.context);
|
|
260
|
+
return await downloadTask.execute(params);
|
|
261
|
+
} catch (e) {
|
|
262
|
+
console.error('Failed to download package patch:', e);
|
|
263
|
+
throw e;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
public switchVersion(hash: string): void {
|
|
268
|
+
try {
|
|
269
|
+
const bundlePath = this.getBundlePath(hash);
|
|
270
|
+
if (!fileIo.accessSync(bundlePath)) {
|
|
271
|
+
throw Error(`Bundle version ${hash} not found.`);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const nextState = NativePatchCore.runStateCore(
|
|
275
|
+
STATE_OP_SWITCH_VERSION,
|
|
276
|
+
this.getStateSnapshot(),
|
|
277
|
+
hash,
|
|
278
|
+
);
|
|
279
|
+
this.applyState(nextState);
|
|
280
|
+
this.preferences.flush();
|
|
281
|
+
} catch (e) {
|
|
282
|
+
console.error('Failed to switch version:', e);
|
|
283
|
+
throw e;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
public getBundleUrl() {
|
|
288
|
+
UpdateContext.isUsingBundleUrl = true;
|
|
289
|
+
const launchState = NativePatchCore.runStateCore(
|
|
290
|
+
STATE_OP_RESOLVE_LAUNCH,
|
|
291
|
+
this.getStateSnapshot(),
|
|
292
|
+
'',
|
|
293
|
+
false,
|
|
294
|
+
false,
|
|
295
|
+
);
|
|
296
|
+
if (launchState.didRollback || launchState.consumedFirstTime) {
|
|
297
|
+
this.applyState(launchState);
|
|
298
|
+
this.preferences.flush();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
let version = launchState.loadVersion || '';
|
|
302
|
+
while (version) {
|
|
303
|
+
const bundleFile = this.getBundlePath(version);
|
|
304
|
+
try {
|
|
305
|
+
if (!fileIo.accessSync(bundleFile)) {
|
|
306
|
+
console.error(`Bundle version ${version} not found.`);
|
|
307
|
+
version = this.rollBack();
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
return bundleFile;
|
|
311
|
+
} catch (e) {
|
|
312
|
+
console.error('Failed to access bundle file:', e);
|
|
313
|
+
version = this.rollBack();
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return '';
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
public getCurrentVersion(): string {
|
|
320
|
+
return this.getStateSnapshot().currentVersion || '';
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
private rollBack(): string {
|
|
324
|
+
const nextState = NativePatchCore.runStateCore(
|
|
325
|
+
STATE_OP_ROLLBACK,
|
|
326
|
+
this.getStateSnapshot(),
|
|
327
|
+
);
|
|
328
|
+
this.applyState(nextState);
|
|
329
|
+
this.preferences.flush();
|
|
330
|
+
return nextState.currentVersion || '';
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
public cleanUp(): void {
|
|
334
|
+
const state = this.getStateSnapshot();
|
|
335
|
+
NativePatchCore.cleanupOldEntries(
|
|
336
|
+
this.rootDir,
|
|
337
|
+
state.currentVersion || '',
|
|
338
|
+
state.lastVersion || '',
|
|
339
|
+
7,
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
public getIsUsingBundleUrl(): boolean {
|
|
344
|
+
return UpdateContext.isUsingBundleUrl;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import bundleManager from '@ohos.bundle.bundleManager';
|
|
2
|
+
import common from '@ohos.app.ability.common';
|
|
3
|
+
import { UpdateContext } from './UpdateContext';
|
|
4
|
+
import logger from './Logger';
|
|
5
|
+
|
|
6
|
+
const TAG = 'UpdateModuleImpl';
|
|
7
|
+
|
|
8
|
+
export class UpdateModuleImpl {
|
|
9
|
+
static readonly NAME = 'Pushy';
|
|
10
|
+
|
|
11
|
+
static async downloadFullUpdate(
|
|
12
|
+
updateContext: UpdateContext,
|
|
13
|
+
options: { updateUrl: string; hash: string },
|
|
14
|
+
): Promise<void> {
|
|
15
|
+
return updateContext.downloadFullUpdate(options.updateUrl, options.hash);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static async downloadPatchFromPackage(
|
|
19
|
+
updateContext: UpdateContext,
|
|
20
|
+
options: { updateUrl: string; hash: string },
|
|
21
|
+
): Promise<void> {
|
|
22
|
+
return updateContext.downloadPatchFromPackage(
|
|
23
|
+
options.updateUrl,
|
|
24
|
+
options.hash,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static async downloadPatchFromPpk(
|
|
29
|
+
updateContext: UpdateContext,
|
|
30
|
+
options: { updateUrl: string; hash: string; originHash: string },
|
|
31
|
+
): Promise<void> {
|
|
32
|
+
return updateContext.downloadPatchFromPpk(
|
|
33
|
+
options.updateUrl,
|
|
34
|
+
options.hash,
|
|
35
|
+
options.originHash,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static async reloadUpdate(
|
|
40
|
+
updateContext: UpdateContext,
|
|
41
|
+
context: common.UIAbilityContext,
|
|
42
|
+
options: { hash: string },
|
|
43
|
+
): Promise<void> {
|
|
44
|
+
const hash = options.hash;
|
|
45
|
+
if (!hash) {
|
|
46
|
+
throw Error('hash不能为空');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
await updateContext.switchVersion(hash);
|
|
51
|
+
const bundleInfo = await bundleManager.getBundleInfoForSelf(
|
|
52
|
+
bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION,
|
|
53
|
+
);
|
|
54
|
+
await context.terminateSelf();
|
|
55
|
+
const want = {
|
|
56
|
+
bundleName: bundleInfo.name,
|
|
57
|
+
abilityName: context.abilityInfo?.name,
|
|
58
|
+
};
|
|
59
|
+
await context.startAbility(want);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
logger.error(TAG, `reloadUpdate failed: ${error}`);
|
|
62
|
+
throw Error(`switchVersion failed ${error.message}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static async setNeedUpdate(
|
|
67
|
+
updateContext: UpdateContext,
|
|
68
|
+
options: { hash: string },
|
|
69
|
+
): Promise<boolean> {
|
|
70
|
+
const hash = options.hash;
|
|
71
|
+
if (!hash) {
|
|
72
|
+
throw Error('empty hash');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
await updateContext.switchVersion(hash);
|
|
77
|
+
return true;
|
|
78
|
+
} catch (error) {
|
|
79
|
+
logger.error(TAG, `setNeedUpdate failed: ${error}`);
|
|
80
|
+
throw Error(`switchVersionLater failed: ${error.message}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static async markSuccess(updateContext: UpdateContext): Promise<boolean> {
|
|
85
|
+
try {
|
|
86
|
+
await updateContext.markSuccess();
|
|
87
|
+
return true;
|
|
88
|
+
} catch (error) {
|
|
89
|
+
logger.error(TAG, `markSuccess failed: ${error}`);
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
static async setUuid(
|
|
95
|
+
updateContext: UpdateContext,
|
|
96
|
+
uuid: string,
|
|
97
|
+
): Promise<void> {
|
|
98
|
+
return updateContext.setKv('uuid', uuid);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
static checkJson(json: string): boolean {
|
|
102
|
+
try {
|
|
103
|
+
JSON.parse(json);
|
|
104
|
+
return true;
|
|
105
|
+
} catch {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
static setLocalHashInfo(
|
|
111
|
+
updateContext: UpdateContext,
|
|
112
|
+
hash: string,
|
|
113
|
+
info: string,
|
|
114
|
+
): boolean {
|
|
115
|
+
updateContext.setKv(`hash_${hash}`, info);
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
static getLocalHashInfo(updateContext: UpdateContext, hash: string): string {
|
|
120
|
+
const value = updateContext.getKv(`hash_${hash}`);
|
|
121
|
+
return value;
|
|
122
|
+
}
|
|
123
|
+
}
|
package/harmony/pushy.har
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
ClientOptions,
|
|
23
23
|
EventType,
|
|
24
24
|
ProgressData,
|
|
25
|
+
UpdateCheckState,
|
|
25
26
|
UpdateServerConfig,
|
|
26
27
|
} from './type';
|
|
27
28
|
import {
|
|
@@ -207,6 +208,16 @@ export class Pushy {
|
|
|
207
208
|
throw e;
|
|
208
209
|
}
|
|
209
210
|
};
|
|
211
|
+
notifyAfterCheckUpdate = (state: UpdateCheckState) => {
|
|
212
|
+
const { afterCheckUpdate } = this.options;
|
|
213
|
+
if (!afterCheckUpdate) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
// 这里仅做状态通知,不阻塞原有检查流程
|
|
217
|
+
Promise.resolve(afterCheckUpdate(state)).catch((error: any) => {
|
|
218
|
+
log('afterCheckUpdate failed:', error?.message || error);
|
|
219
|
+
});
|
|
220
|
+
};
|
|
210
221
|
getCheckUrl = (endpoint: string) => {
|
|
211
222
|
return `${endpoint}/checkUpdate/${this.options.appKey}`;
|
|
212
223
|
};
|
|
@@ -329,9 +340,11 @@ export class Pushy {
|
|
|
329
340
|
};
|
|
330
341
|
checkUpdate = async (extra?: Record<string, any>) => {
|
|
331
342
|
if (!this.assertDebug('checkUpdate()')) {
|
|
343
|
+
this.notifyAfterCheckUpdate({ status: 'skipped' });
|
|
332
344
|
return;
|
|
333
345
|
}
|
|
334
346
|
if (!assertWeb()) {
|
|
347
|
+
this.notifyAfterCheckUpdate({ status: 'skipped' });
|
|
335
348
|
return;
|
|
336
349
|
}
|
|
337
350
|
if (
|
|
@@ -339,6 +352,7 @@ export class Pushy {
|
|
|
339
352
|
(await this.options.beforeCheckUpdate()) === false
|
|
340
353
|
) {
|
|
341
354
|
log('beforeCheckUpdate returned false, skipping check');
|
|
355
|
+
this.notifyAfterCheckUpdate({ status: 'skipped' });
|
|
342
356
|
return;
|
|
343
357
|
}
|
|
344
358
|
const now = Date.now();
|
|
@@ -347,7 +361,9 @@ export class Pushy {
|
|
|
347
361
|
this.lastChecking &&
|
|
348
362
|
now - this.lastChecking < 1000 * 5
|
|
349
363
|
) {
|
|
350
|
-
|
|
364
|
+
const result = await this.lastRespJson;
|
|
365
|
+
this.notifyAfterCheckUpdate({ status: 'completed', result });
|
|
366
|
+
return result;
|
|
351
367
|
}
|
|
352
368
|
this.lastChecking = now;
|
|
353
369
|
const fetchBody = {
|
|
@@ -387,6 +403,7 @@ export class Pushy {
|
|
|
387
403
|
|
|
388
404
|
log('checking result:', result);
|
|
389
405
|
|
|
406
|
+
this.notifyAfterCheckUpdate({ status: 'completed', result });
|
|
390
407
|
return result;
|
|
391
408
|
} catch (e: any) {
|
|
392
409
|
this.lastRespJson = previousRespJson;
|
|
@@ -396,6 +413,7 @@ export class Pushy {
|
|
|
396
413
|
type: 'errorChecking',
|
|
397
414
|
message: errorMessage,
|
|
398
415
|
});
|
|
416
|
+
this.notifyAfterCheckUpdate({ status: 'error', error: e });
|
|
399
417
|
this.throwIfEnabled(e);
|
|
400
418
|
return previousRespJson ? await previousRespJson : emptyObj;
|
|
401
419
|
}
|
package/src/provider.tsx
CHANGED
|
@@ -165,6 +165,7 @@ export const UpdateProvider = ({
|
|
|
165
165
|
async ({ extra }: { extra?: Partial<{ toHash: string }> } = {}) => {
|
|
166
166
|
const now = Date.now();
|
|
167
167
|
if (lastChecking.current && now - lastChecking.current < 1000) {
|
|
168
|
+
client.notifyAfterCheckUpdate({ status: 'skipped' });
|
|
168
169
|
return;
|
|
169
170
|
}
|
|
170
171
|
lastChecking.current = now;
|
package/src/type.ts
CHANGED
|
@@ -35,6 +35,13 @@ export interface ProgressData {
|
|
|
35
35
|
total: number;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
// 用于描述一次检查结束后的最终状态,便于业务侧感知成功、跳过或失败
|
|
39
|
+
export interface UpdateCheckState {
|
|
40
|
+
status: 'completed' | 'skipped' | 'error';
|
|
41
|
+
result?: CheckResult;
|
|
42
|
+
error?: Error;
|
|
43
|
+
}
|
|
44
|
+
|
|
38
45
|
export type EventType =
|
|
39
46
|
| 'rollback'
|
|
40
47
|
| 'errorChecking'
|
|
@@ -98,6 +105,8 @@ export interface ClientOptions {
|
|
|
98
105
|
debug?: boolean;
|
|
99
106
|
throwError?: boolean;
|
|
100
107
|
beforeCheckUpdate?: () => Promise<boolean> | boolean;
|
|
108
|
+
// 每次检查结束后都会触发,不影响原有检查流程
|
|
109
|
+
afterCheckUpdate?: (state: UpdateCheckState) => Promise<void> | void;
|
|
101
110
|
beforeDownloadUpdate?: (info: CheckResult) => Promise<boolean> | boolean;
|
|
102
111
|
afterDownloadUpdate?: (info: CheckResult) => Promise<boolean> | boolean;
|
|
103
112
|
onPackageExpired?: (info: CheckResult) => Promise<boolean> | boolean;
|