Skip to content

Commit 5a571d1

Browse files
sunnylqmclaude
andcommitted
refactor: make resetToPackagedBundle silent by default, returning boolean
Align with the other update-flow APIs (checkUpdate/downloadUpdate): never throw by default — failures resolve to false with the RESET_FAILED error surfaced via lastError/onError, and throwError opts back into throwing. Callers must check the return value: false means the app is still running the hot-updated bundle. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 534e13b commit 5a571d1

4 files changed

Lines changed: 48 additions & 22 deletions

File tree

Example/e2etest/src/index.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,9 @@ function App() {
109109
style={styles.button}
110110
onPress={async () => {
111111
setLastEvent('triggerReset');
112-
try {
113-
await resetToPackagedBundle({ restart: false });
114-
setLastEvent('resetDone');
115-
} catch (e) {
116-
setLastEvent('resetFailed');
117-
setLastEventData(e instanceof Error ? e.message : String(e));
118-
}
112+
// 静默失败语义:失败不抛错而是返回 false,错误进 lastError
113+
const ok = await resetToPackagedBundle({ restart: false });
114+
setLastEvent(ok ? 'resetDone' : 'resetFailed');
119115
}}
120116
>
121117
<Text style={styles.buttonText}>Reset</Text>

src/__tests__/client.test.ts

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -983,8 +983,9 @@ describe('resetToPackagedBundle', () => {
983983
sharedState.marked = true;
984984
const client = new Pushy({ appKey: 'demo-app' });
985985

986-
await client.resetToPackagedBundle();
986+
const result = await client.resetToPackagedBundle();
987987

988+
expect(result).toBe(true);
988989
expect(resetToPackagedBundle).toHaveBeenCalledTimes(1);
989990
expect(sharedState.downloadedHash).toBeUndefined();
990991
expect(sharedState.marked).toBe(false);
@@ -1005,30 +1006,48 @@ describe('resetToPackagedBundle', () => {
10051006
expect(restartApp).toHaveBeenCalledTimes(1);
10061007
});
10071008

1008-
test('throws RESET_FAILED when the native module lacks the method', async () => {
1009-
// Simulates new JS arriving via hot update onto an older binary.
1009+
test('resolves false with RESET_FAILED via onError when the native module lacks the method', async () => {
1010+
// Simulates new JS arriving via hot update onto an older binary. Like the
1011+
// other update-flow APIs this must not throw by default.
10101012
setupClientMocks({ resetToPackagedBundle: null });
10111013
const { Pushy } = await importFreshClient('reset-unsupported');
10121014
const client = new Pushy({ appKey: 'demo-app' });
1015+
const seen: any[] = [];
1016+
client.onError((err: any) => seen.push(err));
1017+
1018+
const result = await client.resetToPackagedBundle();
1019+
1020+
expect(result).toBe(false);
1021+
expect(seen).toHaveLength(1);
1022+
expect(seen[0].code).toBe('RESET_FAILED');
1023+
});
1024+
1025+
test('throwError option makes an unsupported reset throw', async () => {
1026+
setupClientMocks({ resetToPackagedBundle: null });
1027+
const { Pushy } = await importFreshClient('reset-unsupported-throw');
1028+
const client = new Pushy({ appKey: 'demo-app', throwError: true });
10131029

10141030
await expect(client.resetToPackagedBundle()).rejects.toMatchObject({
10151031
code: 'RESET_FAILED',
10161032
});
10171033
});
10181034

1019-
test('propagates native failures with the RESET_FAILED code and keeps state', async () => {
1035+
test('resolves false on native failure and keeps state', async () => {
10201036
const resetToPackagedBundle = mock(() =>
10211037
Promise.reject(Error('disk full')),
10221038
);
10231039
setupClientMocks({ resetToPackagedBundle });
10241040
const { Pushy, sharedState } = await importFreshClient('reset-native-fail');
10251041
sharedState.downloadedHash = 'stale-hash';
10261042
const client = new Pushy({ appKey: 'demo-app' });
1043+
const seen: any[] = [];
1044+
client.onError((err: any) => seen.push(err));
10271045

1028-
await expect(client.resetToPackagedBundle()).rejects.toMatchObject({
1029-
code: 'RESET_FAILED',
1030-
message: 'disk full',
1031-
});
1046+
const result = await client.resetToPackagedBundle();
1047+
1048+
expect(result).toBe(false);
1049+
expect(seen[0].code).toBe('RESET_FAILED');
1050+
expect(seen[0].message).toBe('disk full');
10321051
// The native reset did not happen, so the bookkeeping must not be wiped.
10331052
expect(sharedState.downloadedHash).toBe('stale-hash');
10341053
});

src/client.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -956,34 +956,43 @@ export class Pushy {
956956
* and the whole update state on the native side, so the app loads the
957957
* built-in bundle on the next launch (or immediately with
958958
* `{ restart: true }`). The client uuid is preserved.
959+
*
960+
* Returns whether the reset actually happened. Like the other update-flow
961+
* APIs it never throws by default — failures land in lastError/onError with
962+
* code RESET_FAILED — but the boolean must not be ignored: a false means the
963+
* app is still running the hot-updated bundle. Set `throwError` to throw.
959964
*/
960-
resetToPackagedBundle = async (options?: { restart?: boolean }) => {
965+
resetToPackagedBundle = async (options?: {
966+
restart?: boolean;
967+
}): Promise<boolean> => {
961968
if (typeof PushyModule.resetToPackagedBundle !== 'function') {
962969
// The JS layer can arrive via hot update onto an older binary whose
963-
// native module predates this method; fail loudly instead of pretending
964-
// the reset happened.
970+
// native module predates this method.
965971
const err = new UpdateError(
966972
this.t('error_reset_not_supported'),
967973
'RESET_FAILED',
968974
);
969975
this.emitError(err, 'errorReset');
970-
throw err;
976+
this.throwIfEnabled(err);
977+
return false;
971978
}
972979
try {
973980
await PushyModule.resetToPackagedBundle();
974981
} catch (e) {
975982
const err = toUpdateError(e, 'RESET_FAILED');
976983
this.emitError(err, 'errorReset');
977-
throw err;
984+
this.throwIfEnabled(err);
985+
return false;
978986
}
979987
// The downloaded versions are gone; drop JS bookkeeping referring to them
980988
// so a stale downloadedHash cannot be switched to.
981989
sharedState.downloadedHash = undefined;
982990
sharedState.marked = false;
983991
this.report({ type: 'reset' });
984992
if (options?.restart) {
985-
return this.restartApp();
993+
await this.restartApp();
986994
}
995+
return true;
987996
};
988997
}
989998

src/context.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ export const UpdateContext = createContext<{
4444
} | null;
4545
parseTestQrCode: (code: string) => boolean;
4646
restartApp: () => Promise<void>;
47-
resetToPackagedBundle: (options?: { restart?: boolean }) => Promise<void>;
47+
resetToPackagedBundle: (options?: {
48+
restart?: boolean;
49+
}) => Promise<boolean | void>;
4850
currentHash: string;
4951
packageVersion: string;
5052
client?: Pushy | Cresc;

0 commit comments

Comments
 (0)