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

kwhitley / itty-fetcher / 12873017028

20 Jan 2025 05:21PM UTC coverage: 92.086% (-2.9%) from 95.0%
12873017028

push

github

Kevin Whitley
testing v2

24 of 32 branches covered (75.0%)

Branch coverage included in aggregate %.

79 of 82 new or added lines in 1 file covered. (96.34%)

104 of 107 relevant lines covered (97.2%)

5.79 hits per line

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

92.09
/src/fetcher.ts
1
type ResponseHandler = (response?: Response, request?: Request) => Promise<Response | void>
1✔
2

1✔
3
// Update FetcherOptions to be more specific about what it accepts
1✔
4
type FetcherOptionsObject = {
1✔
5
  base?: string | URL
1✔
6
  fetch?: typeof fetch
1✔
7
  parse?: boolean
1✔
8
  encode?: boolean
1✔
9
  after?: ResponseHandler[]
1✔
10
} & RequestInit & Record<string, any>
1✔
11

1✔
12
// Create a union type for all possible arguments
1✔
13
type FetcherOptions = string | FetcherOptionsObject
1✔
14

1✔
15
type GetFetchCall = {
1✔
16
  (url?: string, options?: FetcherOptionsObject): Promise<any>
1✔
17
  (options?: FetcherOptionsObject): Promise<any>
1✔
18
}
1✔
19

1✔
20
type FetchCall = {
1✔
21
  (url?: string, payload?: any, options?: FetcherOptionsObject): Promise<any>
1✔
22
  (payload?: any, options?: FetcherOptionsObject): Promise<any>
1✔
23
}
1✔
24

1✔
25
type Fetcher = {
1✔
26
  (options?: FetcherOptions, additionalOptions?: FetcherOptionsObject): Fetcher
1✔
27
  get: GetFetchCall
1✔
28
  post: FetchCall
1✔
29
  put: FetchCall
1✔
30
  patch: FetchCall
1✔
31
  delete: FetchCall
1✔
32
}
1✔
33

1✔
34
// Extract handler logic for better minification
1✔
35
const handleRequest = async (
1✔
36
  method: string, 
10✔
37
  args: any[], 
10✔
38
  options: FetcherOptionsObject, 
10✔
39
  base: string, 
10✔
40
  headers: HeadersInit
10✔
41
) => {
10✔
42
  // console.log({ method, args, options, base, headers })
10✔
43
  let childBase = typeof args[0] == 'string' ? args.shift() : ''
10✔
44
  let payload = method != 'get' ? args.shift() : null
10✔
45
  
10✔
46
  options = { ...options, ...args.shift(), method }
10✔
47
  headers = new Headers(headers)
10✔
48
  // console.log('base headers', Object.fromEntries(headers))
10✔
49
  // console.log('request headers', Object.fromEntries(new Headers(options.headers ?? [])))
10✔
50
  let url = new URL(childBase.indexOf('http') == -1 ? base + childBase : childBase)
10!
51

10✔
52
  // @ts-ignore - combine query params
10✔
53
  Object.entries(options.query || {}).forEach(([k, v]) => url.searchParams.append(k, v))
10✔
54

10✔
55
  // Handle payload
10✔
56
  if (payload && options.encode !== false) {
10✔
57
    options.body = typeof payload == 'string' ? payload : JSON.stringify(payload)
2!
58
    // @ts-ignore - set content-type
2✔
59
    !typeof payload == 'string' && headers.set('content-type', 'application/json')
2!
60
  }
2✔
61

10✔
62
  for (let [k, v] of [...new Headers(options.headers ?? [])]) {
10✔
63
    headers.set(k, v)
1✔
64
  }
1✔
65
  options.headers = headers
10✔
66
  let request = new Request(url, options)
10✔
67
  // console.log({ request, options})
10✔
68
  // console.log('final headers', Object.fromEntries(options.headers))
10✔
69

10✔
70
  let error, response = await (options.fetch ?? fetch)(request)
10!
71

10✔
72
  if (!response.ok) {
10✔
73
    error = Object.assign(new Error(response.statusText), { status: response.status })
2✔
74
  }
2✔
75

10✔
76
  options.parse !== false && (response = await (response.headers.get('content-type')?.includes('json')
10✔
77
    ? response.json()
8✔
78
    : response.text()))
2✔
79

10✔
80
  if (error) return options.onError ? options.onError(error, response) : Promise.reject(error)
10!
81

8✔
82
  for (let handler of options.after || []) {
10!
NEW
83
    response = await handler(response, request) ?? response
×
NEW
84
  }
✔
85

8✔
86
  return response
8✔
87
}
8✔
88

1✔
89
const createEnhancedFunction = (
1✔
90
  optionsOrBase?: FetcherOptions, 
11✔
91
  additionalOptions?: FetcherOptionsObject,
11✔
92
  options = typeof optionsOrBase == 'string'
11!
NEW
93
    ? { base: optionsOrBase, ...additionalOptions }
×
94
    : optionsOrBase || {},
11✔
95
  { 
11✔
96
    base = window?.location?.origin ?? '', 
11!
97
    headers = {}, 
11✔
98
    ...restOptions 
11✔
99
  } = options
11✔
100
): Fetcher =>
11✔
101
  // @ts-ignore
11✔
102
  new Proxy((...args: any) => createEnhancedFunction(...args), {
11✔
103
    // @ts-ignore
11✔
104
    get: (obj, method: any) => obj[method] ?? ((...args) => handleRequest(method, args, restOptions, base, headers))
11✔
105
  })
11✔
106

1✔
107
export const fetcher = createEnhancedFunction()
1✔
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