From f231bc5c0b6c095c3624998376575bd3ff0fb8c2 Mon Sep 17 00:00:00 2001 From: Victor Ribeiro Date: Thu, 2 Oct 2025 16:08:46 +0100 Subject: [PATCH 1/3] feat: add optional retryOnError property to RequestConfig interface --- src/main/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/types.ts b/src/main/types.ts index 145cf00..6aed220 100644 --- a/src/main/types.ts +++ b/src/main/types.ts @@ -28,6 +28,7 @@ export interface RequestConfig { authInvalidateStatusCodes: number[]; headers: RequestHeaders; fetch: Fetch; + retryOnError?: (error: Error) => boolean; } export interface Fetch { From e18bab4b1452a75bf1975cf1889c591031715cee Mon Sep 17 00:00:00 2001 From: Victor Ribeiro Date: Thu, 2 Oct 2025 16:09:11 +0100 Subject: [PATCH 2/3] feat: enhance error handling by allowing custom retry logic in Request class --- src/main/request.ts | 2 +- src/test/request.test.ts | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/main/request.ts b/src/main/request.ts index 63c2a23..ca874cb 100644 --- a/src/main/request.ts +++ b/src/main/request.ts @@ -108,7 +108,7 @@ export class Request { const status = err.details?.status; const statusText = err.details?.statusText; const info: RequestDebugInfo = { method, url, headers: options.headers ?? {}, status, statusText }; - const retry = shouldRetry || NETWORK_ERRORS.includes(err.code); + const retry = shouldRetry || NETWORK_ERRORS.includes(err.code) || this.config.retryOnError?.(err); if (retry) { lastError = err; lastInfo = info; diff --git a/src/test/request.test.ts b/src/test/request.test.ts index 78a95a8..3f42540 100644 --- a/src/test/request.test.ts +++ b/src/test/request.test.ts @@ -84,6 +84,27 @@ describe('Request', () => { assert.strictEqual(thrownError.details.status, 504); }); + it('retry if retryOnError returns true', async () => { + let thrownError: any; + const fetch = fetchMock({ status: 500 }); + const request = new Request({ + fetch, + retryAttempts, + retryDelay: 0, + retryDelayIncrement: 10, + retryStatusCodes: [504], + retryOnError: err => { + err.message = 'should-retry'; + return true; + }, + }); + request.onRetry = err => { thrownError = err; }; + await request.send('get', 'http://example.com').catch(() => {}); + assert.ok(thrownError); + assert.strictEqual(thrownError.message, 'should-retry'); + assert.strictEqual(thrownError.details.status, 500); + }); + }); describe('mergeHeaders', () => { From ca19e857e830c443c881a8417d50c41a48c9271c Mon Sep 17 00:00:00 2001 From: Victor Ribeiro Date: Fri, 3 Oct 2025 10:46:21 +0100 Subject: [PATCH 3/3] refactor: rename retryOnError to handleShouldRetry for clarity --- src/main/request.ts | 3 ++- src/main/types.ts | 2 +- src/test/request.test.ts | 16 +++++++++++++--- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/main/request.ts b/src/main/request.ts index ca874cb..41096c9 100644 --- a/src/main/request.ts +++ b/src/main/request.ts @@ -27,6 +27,7 @@ export const DEFAULT_REQUEST_CONFIG: RequestConfig = { retryDelayIncrement: 500, retryStatusCodes: [429, 502, 503, 504], authInvalidateStatusCodes: [401, 403], + handleShouldRetry: () => false, headers: {}, fetch, }; @@ -108,7 +109,7 @@ export class Request { const status = err.details?.status; const statusText = err.details?.statusText; const info: RequestDebugInfo = { method, url, headers: options.headers ?? {}, status, statusText }; - const retry = shouldRetry || NETWORK_ERRORS.includes(err.code) || this.config.retryOnError?.(err); + const retry = shouldRetry || NETWORK_ERRORS.includes(err.code) || this.config.handleShouldRetry?.(err); if (retry) { lastError = err; lastInfo = info; diff --git a/src/main/types.ts b/src/main/types.ts index 6aed220..1cec5d2 100644 --- a/src/main/types.ts +++ b/src/main/types.ts @@ -28,7 +28,7 @@ export interface RequestConfig { authInvalidateStatusCodes: number[]; headers: RequestHeaders; fetch: Fetch; - retryOnError?: (error: Error) => boolean; + handleShouldRetry?: (error: Error) => boolean; } export interface Fetch { diff --git a/src/test/request.test.ts b/src/test/request.test.ts index 3f42540..875ff06 100644 --- a/src/test/request.test.ts +++ b/src/test/request.test.ts @@ -84,7 +84,7 @@ describe('Request', () => { assert.strictEqual(thrownError.details.status, 504); }); - it('retry if retryOnError returns true', async () => { + it('retry if handleShouldRetry returns true', async () => { let thrownError: any; const fetch = fetchMock({ status: 500 }); const request = new Request({ @@ -92,8 +92,7 @@ describe('Request', () => { retryAttempts, retryDelay: 0, retryDelayIncrement: 10, - retryStatusCodes: [504], - retryOnError: err => { + handleShouldRetry: err => { err.message = 'should-retry'; return true; }, @@ -105,6 +104,17 @@ describe('Request', () => { assert.strictEqual(thrownError.details.status, 500); }); + it('do not retry by default config', async () => { + let retried = false; + const fetch = fetchMock({ status: 500 }); + const request = new Request({ + fetch + }); + request.onRetry = () => { retried = true; }; + await request.send('get', 'http://example.com').catch(() => {}); + assert.ok(!retried); + }); + }); describe('mergeHeaders', () => {