Skip to content

Commit 5b81b4d

Browse files
authored
增加: 支持 afterCheckUpdate 回调通知检查状态 (#531)
1 parent c2c66b0 commit 5b81b4d

4 files changed

Lines changed: 93 additions & 1 deletion

File tree

src/__tests__/client.test.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,68 @@ describe('Pushy server config', () => {
135135
]);
136136
expect(client.options.server?.queryUrls).toEqual(['https://q.example.com']);
137137
});
138+
139+
test('calls afterCheckUpdate with skipped when beforeCheckUpdate returns false', async () => {
140+
setupClientMocks();
141+
const beforeCheckUpdate = mock(() => false);
142+
const afterCheckUpdate = mock(() => {});
143+
144+
const { Pushy } = await importFreshClient('after-check-update-skipped');
145+
const client = new Pushy({
146+
appKey: 'demo-app',
147+
beforeCheckUpdate,
148+
afterCheckUpdate,
149+
});
150+
151+
expect(await client.checkUpdate()).toBeUndefined();
152+
expect(afterCheckUpdate).toHaveBeenCalledWith({
153+
status: 'skipped',
154+
});
155+
});
156+
157+
test('calls afterCheckUpdate with completed and result when check succeeds', async () => {
158+
setupClientMocks();
159+
const afterCheckUpdate = mock(() => {});
160+
const checkResult = {
161+
update: true as const,
162+
name: '1.0.1',
163+
hash: 'next-hash',
164+
description: 'bugfix',
165+
};
166+
(globalThis as any).fetch = mock(async () => createJsonResponse(checkResult));
167+
168+
const { Pushy } = await importFreshClient('after-check-update-completed');
169+
const client = new Pushy({
170+
appKey: 'demo-app',
171+
afterCheckUpdate,
172+
});
173+
174+
expect(await client.checkUpdate()).toEqual(checkResult);
175+
expect(afterCheckUpdate).toHaveBeenCalledWith({
176+
status: 'completed',
177+
result: checkResult,
178+
});
179+
});
180+
181+
test('calls afterCheckUpdate with error before rethrowing when throwError is enabled', async () => {
182+
setupClientMocks();
183+
const afterCheckUpdate = mock(() => {});
184+
const fetchError = new Error('boom');
185+
(globalThis as any).fetch = mock(async () => {
186+
throw fetchError;
187+
});
188+
189+
const { Pushy } = await importFreshClient('after-check-update-error');
190+
const client = new Pushy({
191+
appKey: 'demo-app',
192+
throwError: true,
193+
afterCheckUpdate,
194+
});
195+
196+
await expect(client.checkUpdate()).rejects.toThrow('boom');
197+
expect(afterCheckUpdate).toHaveBeenCalledWith({
198+
status: 'error',
199+
error: fetchError,
200+
});
201+
});
138202
});

src/client.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
ClientOptions,
2323
EventType,
2424
ProgressData,
25+
UpdateCheckState,
2526
UpdateServerConfig,
2627
} from './type';
2728
import {
@@ -207,6 +208,16 @@ export class Pushy {
207208
throw e;
208209
}
209210
};
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+
};
210221
getCheckUrl = (endpoint: string) => {
211222
return `${endpoint}/checkUpdate/${this.options.appKey}`;
212223
};
@@ -329,16 +340,19 @@ export class Pushy {
329340
};
330341
checkUpdate = async (extra?: Record<string, any>) => {
331342
if (!this.assertDebug('checkUpdate()')) {
343+
this.notifyAfterCheckUpdate({ status: 'skipped' });
332344
return;
333345
}
334346
if (!assertWeb()) {
347+
this.notifyAfterCheckUpdate({ status: 'skipped' });
335348
return;
336349
}
337350
if (
338351
this.options.beforeCheckUpdate &&
339352
(await this.options.beforeCheckUpdate()) === false
340353
) {
341354
log('beforeCheckUpdate returned false, skipping check');
355+
this.notifyAfterCheckUpdate({ status: 'skipped' });
342356
return;
343357
}
344358
const now = Date.now();
@@ -347,7 +361,9 @@ export class Pushy {
347361
this.lastChecking &&
348362
now - this.lastChecking < 1000 * 5
349363
) {
350-
return await this.lastRespJson;
364+
const result = await this.lastRespJson;
365+
this.notifyAfterCheckUpdate({ status: 'completed', result });
366+
return result;
351367
}
352368
this.lastChecking = now;
353369
const fetchBody = {
@@ -387,6 +403,7 @@ export class Pushy {
387403

388404
log('checking result:', result);
389405

406+
this.notifyAfterCheckUpdate({ status: 'completed', result });
390407
return result;
391408
} catch (e: any) {
392409
this.lastRespJson = previousRespJson;
@@ -396,6 +413,7 @@ export class Pushy {
396413
type: 'errorChecking',
397414
message: errorMessage,
398415
});
416+
this.notifyAfterCheckUpdate({ status: 'error', error: e });
399417
this.throwIfEnabled(e);
400418
return previousRespJson ? await previousRespJson : emptyObj;
401419
}

src/provider.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ export const UpdateProvider = ({
165165
async ({ extra }: { extra?: Partial<{ toHash: string }> } = {}) => {
166166
const now = Date.now();
167167
if (lastChecking.current && now - lastChecking.current < 1000) {
168+
client.notifyAfterCheckUpdate({ status: 'skipped' });
168169
return;
169170
}
170171
lastChecking.current = now;

src/type.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ export interface ProgressData {
3535
total: number;
3636
}
3737

38+
// 用于描述一次检查结束后的最终状态,便于业务侧感知成功、跳过或失败
39+
export interface UpdateCheckState {
40+
status: 'completed' | 'skipped' | 'error';
41+
result?: CheckResult;
42+
error?: Error;
43+
}
44+
3845
export type EventType =
3946
| 'rollback'
4047
| 'errorChecking'
@@ -98,6 +105,8 @@ export interface ClientOptions {
98105
debug?: boolean;
99106
throwError?: boolean;
100107
beforeCheckUpdate?: () => Promise<boolean> | boolean;
108+
// 每次检查结束后都会触发,不影响原有检查流程
109+
afterCheckUpdate?: (state: UpdateCheckState) => Promise<void> | void;
101110
beforeDownloadUpdate?: (info: CheckResult) => Promise<boolean> | boolean;
102111
afterDownloadUpdate?: (info: CheckResult) => Promise<boolean> | boolean;
103112
onPackageExpired?: (info: CheckResult) => Promise<boolean> | boolean;

0 commit comments

Comments
 (0)