Skip to content

Commit 46610cf

Browse files
committed
🏭 Add fallback catcher method
should resolve #192
1 parent 238d17a commit 46610cf

File tree

5 files changed

+63
-9
lines changed

5 files changed

+63
-9
lines changed

src/constants.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export const JSON_MIME = "application/json"
22
export const CONTENT_TYPE_HEADER = "Content-Type"
3-
export const FETCH_ERROR = Symbol()
3+
export const FETCH_ERROR = Symbol()
4+
export const CATCHER_FALLBACK = Symbol()

src/core.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { mix, extractContentType, isLikelyJsonMime } from "./utils.js"
2-
import { JSON_MIME, CONTENT_TYPE_HEADER } from "./constants.js"
2+
import { JSON_MIME, CONTENT_TYPE_HEADER, CATCHER_FALLBACK } from "./constants.js"
33
import { resolver } from "./resolver.js"
44
import config from "./config.js"
55
import type { Wretch } from "./types.js"
@@ -65,6 +65,9 @@ export const core: Wretch = {
6565
newMap.set(errorId, catcher)
6666
return { ...this, _catchers: newMap }
6767
},
68+
catcherFallback(catcher) {
69+
return this.catcher(CATCHER_FALLBACK, catcher)
70+
},
6871
resolve<R = unknown>(resolver, clear: boolean = false) {
6972
return { ...this, _resolvers: clear ? [resolver] : [...this._resolvers, resolver] }
7073
},

src/resolver.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { middlewareHelper } from "./middleware.js"
22
import { mix } from "./utils.js"
33
import type { Wretch, WretchResponse, WretchResponseChain, WretchError as WretchErrorType } from "./types.js"
4-
import { FETCH_ERROR } from "./constants.js"
4+
import { FETCH_ERROR, CATCHER_FALLBACK } from "./constants.js"
55

66
/**
77
* This class inheriting from Error is thrown when the fetch response is not "ok".
@@ -47,7 +47,7 @@ export const resolver = <T, Chain, R>(wretch: T & Wretch<T, Chain, R>) => {
4747
const referenceError = new Error()
4848
const throwingPromise: Promise<void | WretchResponse> = _fetchReq
4949
.catch(error => {
50-
throw { __wrap: error }
50+
throw { [FETCH_ERROR]: error }
5151
})
5252
.then(response => {
5353
if (!response.ok) {
@@ -75,17 +75,22 @@ export const resolver = <T, Chain, R>(wretch: T & Wretch<T, Chain, R>) => {
7575
// Wraps the Promise in order to dispatch the error to a matching catcher
7676
const catchersWrapper = <T>(promise: Promise<T>): Promise<void | T> => {
7777
return promise.catch(err => {
78-
const error = err.__wrap || err
78+
const fetchErrorFlag = err.hasOwnProperty(FETCH_ERROR)
79+
const error = fetchErrorFlag ? err[FETCH_ERROR] : err
7980

8081
const catcher =
81-
(error.status && catchers.get(error.status)) ||
82-
catchers.get(error.name) || (
83-
err.__wrap && catchers.has(FETCH_ERROR) && catchers.get(FETCH_ERROR)
82+
(error?.status && catchers.get(error.status)) ||
83+
catchers.get(error?.name) || (
84+
fetchErrorFlag && catchers.has(FETCH_ERROR) && catchers.get(FETCH_ERROR)
8485
)
8586

8687
if (catcher)
8788
return catcher(error, wretch)
8889

90+
const catcherFallback = catchers.get(CATCHER_FALLBACK)
91+
if (catcherFallback)
92+
return catcherFallback(error, wretch)
93+
8994
throw error
9095
})
9196
}

src/types.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,27 @@ export interface Wretch<Self = unknown, Chain = unknown, Resolver = undefined> {
281281
* @param errorId - Error code or name
282282
* @param catcher - The catcher method
283283
*/
284-
catcher(this: Self & Wretch<Self, Chain, Resolver>, errorId: number | string, catcher: (error: WretchError, originalRequest: this) => any): this
284+
catcher(this: Self & Wretch<Self, Chain, Resolver>, errorId: number | string | symbol, catcher: (error: WretchError, originalRequest: this) => any): this
285+
286+
/**
287+
* A fallback catcher that will be called for any error thrown - if uncaught by other means.
288+
*
289+
* ```js
290+
* wretch(url)
291+
* .catcher(404, err => redirect("/routes/notfound", err.message))
292+
* .catcher(500, err => flashMessage("internal.server.error"))
293+
* // this fallback will trigger for any error except the ones caught above (404 and 505)
294+
* .catcherFallback(err => {
295+
* log("Uncaught error:", err)
296+
* throw err
297+
* })
298+
* ```
299+
*
300+
* @category Helpers
301+
* @see {@link Wretch.catcher} for more details.
302+
* @param catcher - The catcher method
303+
*/
304+
catcherFallback(this: Self & Wretch<Self, Chain, Resolver>, catcher: (error: WretchError, originalRequest: this) => any): this
285305

286306
/**
287307
* Defer one or multiple request chain methods that will get called just before the request is sent.

test/node/wretch.spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,31 @@ describe("Wretch", function () {
361361
expect(check).toBe(7)
362362
})
363363

364+
it("should use the fallback catcher", async function () {
365+
let _404 = 0
366+
let fetchError = 0
367+
let fallback = 0
368+
369+
const w = wretch(_URL)
370+
.polyfills({
371+
fetch: fetchPolyfill()
372+
})
373+
.catcher(404, _ => _404++)
374+
.catcherFallback(_ => fallback++)
375+
376+
await w.url("/404").get().res(_ => fallback--)
377+
await w
378+
.polyfills({ fetch: () => Promise.reject() })
379+
.get()
380+
.fetchError(_ => fetchError++)
381+
.res(_ => fallback--)
382+
await w.url("/401").get().res(_ => fallback--)
383+
384+
expect(_404).toBe(1)
385+
expect(fetchError).toBe(1)
386+
expect(fallback).toBe(1)
387+
})
388+
364389
it("should capture the original request with resolvers/catchers", async function () {
365390
let check = 0
366391
const redirectedNotFound = await wretch(`${_URL}/404`)

0 commit comments

Comments
 (0)