• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

elbywan / wretch / 18629703339

19 Oct 2025 11:22AM UTC coverage: 97.863% (+10.3%) from 87.552%
18629703339

Pull #273

github

web-flow
Merge ec9278851 into 0e0b30f4b
Pull Request #273: V3

317 of 333 branches covered (95.2%)

Branch coverage included in aggregate %.

288 of 293 new or added lines in 13 files covered. (98.29%)

8 existing lines in 5 files now uncovered.

1652 of 1679 relevant lines covered (98.39%)

59.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.73
/src/resolver.ts
1
import { middlewareHelper } from "./middleware.js"
11✔
2
import type { Wretch, WretchResponse, WretchResponseChain, WretchError as WretchErrorType } from "./types.js"
11✔
3
import { CATCHER_FALLBACK, FETCH_ERROR } from "./constants.js"
11✔
4

11✔
5
/**
11✔
6
 * This class inheriting from Error is thrown when the fetch response is not "ok".
11✔
7
 * It extends Error and adds status, text and body fields.
11✔
8
 */
11✔
9
export class WretchError extends Error implements WretchErrorType {
11✔
10
  status: number
7✔
11
  response: WretchResponse
7✔
12
  url: string
7✔
13
}
7✔
14

3✔
15
export const resolver = <T, Chain, R, E>(wretch: T & Wretch<T, Chain, R, E>) => {
3✔
16
  const sharedState = Object.create(null)
561✔
17

561✔
18
  wretch = wretch._addons.reduce((w, addon) =>
561✔
19
    addon.beforeRequest &&
90✔
20
    addon.beforeRequest(w, wretch._options, sharedState)
42✔
21
    || w,
48✔
22
  wretch)
561✔
23

561✔
24
  const {
561✔
25
    _url: url,
561✔
26
    _options: opts,
561✔
27
    _fetch: customFetch,
561✔
28
    _errorTransformer: errorTransformer,
561✔
29
    _catchers: _catchers,
561✔
30
    _resolvers: resolvers,
561✔
31
    _middlewares: middlewares,
561✔
32
    _addons: addons
561✔
33
  } = wretch
561✔
34

561✔
35
  const catchers = new Map(_catchers)
561✔
36
  const finalOptions = opts
561✔
37

561✔
38
  // The generated fetch request
561✔
39
  let finalUrl = url
561✔
40
  const _fetchReq = middlewareHelper(middlewares)((url, options) => {
561✔
41
    finalUrl = url
738✔
42
    const fetchImpl = customFetch || fetch
738✔
43
    return fetchImpl(url, options)
738✔
44
  })(url, finalOptions)
561✔
45
  // Throws on an http error
561✔
46
  const referenceError = new Error()
561✔
47
  const throwingPromise: Promise<void | WretchResponse> = _fetchReq
561✔
48
    .then(async response => {
561✔
49
      if (!response.ok) {
519✔
50
        const err = new WretchError()
102✔
51
        err["cause"] = referenceError
102✔
52
        err.stack += "\nCAUSE: " + referenceError.stack
102✔
53
        err.response = response
102✔
54
        err.status = response.status
102✔
55
        err.url = finalUrl
102✔
56

102✔
57
        if (response.type === "opaque" || errorTransformer) {
102✔
58
          err.message = response.statusText
6✔
59
        } else {
102✔
60
          try {
96✔
61
            err.message = await response.text()
96✔
62
          } catch {
96!
NEW
63
            err.message = response.statusText
×
NEW
64
          }
×
65
        }
96✔
66

102✔
67
        throw err
102✔
68
      }
102✔
69
      return response
519✔
70
    })
561✔
71
  // Wraps the Promise in order to dispatch the error to a matching catcher
561✔
72
  const catchersWrapper = <T>(promise: Promise<T>): Promise<void | T> =>
561✔
73
    promise.catch(async error => {
561✔
74

147✔
75
      const catcher =
147✔
76
        catchers.get(error?.status) ||
147✔
77
        catchers.get(error?.name) ||
84✔
78
        (!(error instanceof WretchError) && catchers.get(FETCH_ERROR)) ||
78✔
79
        catchers.get(CATCHER_FALLBACK)
72✔
80

147✔
81
      if(error.response && errorTransformer) {
147✔
82
        error = await errorTransformer(error, error.response)
6✔
83
      }
6✔
84

147✔
85
      if (catcher)
147✔
86
        return catcher(error, wretch)
147✔
87

147✔
88
      throw error
147✔
89
    })
561✔
90
  // Enforces the proper promise type when a body parsing method is called.
561✔
91
  type BodyParser =
561✔
92
    <Type>(funName: "json" | "blob" | "formData" | "arrayBuffer" | "text" | null)
561✔
93
    => <Result = void>(cb?: (type: Type) => Result)
561✔
94
    => Promise<Awaited<Result>>
561✔
95
  const bodyParser: BodyParser = funName => cb => {
561✔
96
    const promise = funName ?
561✔
97
    // If a callback is provided, then callback with the body result otherwise return the parsed body itself.
273✔
98
      throwingPromise.then(_ => _?.[funName]()) :
273✔
99
    // No body parsing method - return the response
288✔
100
      throwingPromise
288✔
101
    return catchersWrapper(cb ? promise.then(cb) : promise)
561✔
102
  }
561✔
103

561✔
104
  const responseChain: WretchResponseChain<T, Chain, R, E> = {
561✔
105
    _wretchReq: wretch,
561✔
106
    _fetchReq,
561✔
107
    _sharedState: sharedState,
561✔
108
    res: bodyParser<WretchResponse>(null),
561✔
109
    json: bodyParser<any>("json"),
561✔
110
    blob: bodyParser<Blob>("blob"),
561✔
111
    formData: bodyParser<FormData>("formData"),
561✔
112
    arrayBuffer: bodyParser<ArrayBuffer>("arrayBuffer"),
561✔
113
    text: bodyParser<string>("text"),
561✔
114
    error(errorId, cb) {
561✔
115
      catchers.set(errorId, cb)
87✔
116
      return this
87✔
117
    },
87✔
118
    badRequest(cb) { return this.error(400, cb) },
561✔
119
    unauthorized(cb) { return this.error(401, cb) },
561✔
120
    forbidden(cb) { return this.error(403, cb) },
561✔
121
    notFound(cb) { return this.error(404, cb) },
561✔
122
    timeout(cb) { return this.error(408, cb) },
561✔
123
    internalError(cb) { return this.error(500, cb) },
561✔
124
    fetchError(cb) { return this.error(FETCH_ERROR, cb) },
561✔
125
  }
561✔
126

561✔
127
  const enhancedResponseChain: R extends undefined ? Chain & WretchResponseChain<T, Chain, undefined> : R = addons.reduce((chain, addon) => ({
561✔
128
    ...chain,
90✔
129
    ...(typeof addon.resolver === "function" ? (addon.resolver as (_: WretchResponseChain<T, Chain, R, E>) => any)(chain) : addon.resolver)
90!
130
  }), responseChain)
561✔
131

561✔
132
  return resolvers.reduce((chain, r) => r(chain, wretch), enhancedResponseChain)
561✔
133
}
561✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc