-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrequest-types.ts
More file actions
244 lines (237 loc) · 7.47 KB
/
request-types.ts
File metadata and controls
244 lines (237 loc) · 7.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
/**
* @fileoverview Types for HTTP request configuration — options, hooks,
* and Node `IncomingMessage` aliases. Split out of `http-request/types.ts`
* for size hygiene.
*
* - `IncomingResponse` / `IncomingRequest` — Node IncomingMessage aliases
* - `HttpHookRequestInfo` / `HttpHookResponseInfo` / `HttpHooks` — observability
* - `HttpRequestOptions` — the main request configuration interface
*/
import type { IncomingMessage } from 'node:http'
import type { Readable } from 'node:stream'
/** IncomingMessage received as a response to a client request (http.request callback). */
export type IncomingResponse = IncomingMessage
/** IncomingMessage received as a request in a server handler (http.createServer callback). */
export type IncomingRequest = IncomingMessage
/**
* Information passed to the onRequest hook before each request attempt.
*/
export interface HttpHookRequestInfo {
headers: Record<string, string>
method: string
timeout: number
url: string
}
/**
* Information passed to the onResponse hook after each request attempt.
*/
export interface HttpHookResponseInfo {
duration: number
error?: Error | undefined
headers?: import('node:http').IncomingHttpHeaders | undefined
method: string
status?: number | undefined
statusText?: string | undefined
url: string
}
/**
* Lifecycle hooks for observing HTTP request/response events.
* Hooks fire per-attempt (retries produce multiple hook calls).
*/
export interface HttpHooks {
onRequest?: ((info: HttpHookRequestInfo) => void) | undefined
onResponse?: ((info: HttpHookResponseInfo) => void) | undefined
}
/**
* Configuration options for HTTP/HTTPS requests.
*/
export interface HttpRequestOptions {
/**
* Request body to send.
* Can be a string, Buffer, or Readable stream.
*
* When a Readable stream is provided, it is piped directly to the request.
* If the stream has a `getHeaders()` method (duck-typed, e.g., the `form-data`
* npm package), its headers (Content-Type with boundary) are automatically
* merged into the request headers.
*
* **Note:** Streaming bodies are one-shot — they cannot be replayed. Using a
* Readable body with `retries > 0` throws an error. Buffer the body as a
* string/Buffer if retries are needed. Redirects are also disabled for
* streaming bodies since the stream is consumed on the first request.
*
* @example
* ```ts
* // Send JSON data
* await httpRequest('https://api.example.com/data', {
* method: 'POST',
* body: JSON.stringify({ name: 'Alice' }),
* headers: { 'Content-Type': 'application/json' }
* })
*
* // Send binary data
* const buffer = Buffer.from([0x00, 0x01, 0x02])
* await httpRequest('https://api.example.com/upload', {
* method: 'POST',
* body: buffer
* })
*
* // Stream form-data (npm package, not native FormData)
* import FormData from 'form-data'
* const form = new FormData()
* form.append('file', createReadStream('data.json'))
* await httpRequest('https://api.example.com/upload', {
* method: 'POST',
* body: form // auto-merges form.getHeaders()
* })
* ```
*/
body?: Buffer | Readable | string | undefined
/**
* Custom CA certificates for TLS connections.
* When provided, these certificates are combined with the default trust
* store via an HTTPS agent. Useful when SSL_CERT_FILE is set but
* NODE_EXTRA_CA_CERTS was not available at process startup.
*
* @example
* ```ts
* import { rootCertificates } from 'node:tls'
* import { readFileSync } from 'node:fs'
*
* const extraCerts = readFileSync('/path/to/cert.pem', 'utf-8')
* await httpRequest('https://api.example.com', {
* ca: [...rootCertificates, extraCerts]
* })
* ```
*/
ca?: string[] | undefined
/**
* Whether to automatically follow HTTP redirects (3xx status codes).
*
* @default true
*
* @example
* ```ts
* // Follow redirects (default)
* await httpRequest('https://example.com/redirect')
*
* // Don't follow redirects
* const response = await httpRequest('https://example.com/redirect', {
* followRedirects: false
* })
* console.log(response.status) // 301 or 302
* ```
*/
followRedirects?: boolean | undefined
/**
* Lifecycle hooks for observing request/response events.
* Hooks fire per-attempt — retries and redirects each trigger separate hook calls.
*/
hooks?: HttpHooks | undefined
/**
* HTTP headers to send with the request.
* A `User-Agent` header is automatically added if not provided.
*
* @example
* ```ts
* await httpRequest('https://api.example.com/data', {
* headers: {
* 'Authorization': 'Bearer token123',
* 'Content-Type': 'application/json',
* 'Accept': 'application/json'
* }
* })
* ```
*/
headers?: Record<string, string> | undefined
/**
* Maximum number of redirects to follow before throwing an error.
* Only relevant when `followRedirects` is `true`.
*
* @default 5
*/
maxRedirects?: number | undefined
/**
* Maximum response body size in bytes. Responses exceeding this limit
* will be rejected with an error. Prevents memory exhaustion from
* unexpectedly large responses.
*
* @default undefined (no limit)
*/
maxResponseSize?: number | undefined
/**
* HTTP method to use for the request.
*
* @default 'GET'
*
* @example
* ```ts
* await httpRequest('https://api.example.com/data', {
* method: 'POST',
* body: JSON.stringify({ name: 'Alice' })
* })
* ```
*/
method?: string | undefined
/**
* Callback invoked before each retry attempt.
* Allows customizing retry behavior per-attempt (e.g., skip 4xx, honor Retry-After).
*
* @param attempt - Current retry attempt number (1-based)
* @param error - The error that triggered the retry (HttpResponseError for HTTP errors)
* @param delay - The calculated delay in ms before next retry
* @returns `false` to stop retrying and rethrow,
* a `number` to override the delay (ms),
* or `undefined` to use the calculated delay
*/
onRetry?:
| ((
attempt: number,
error: unknown,
delay: number,
) => boolean | number | undefined)
| undefined
/**
* Number of retry attempts for failed requests.
* Uses exponential backoff: delay = `retryDelay` * 2^attempt.
*
* @default 0
*/
retries?: number | undefined
/**
* Initial delay in milliseconds before first retry.
* Subsequent retries use exponential backoff.
*
* @default 1000
*/
retryDelay?: number | undefined
/**
* When true, resolve with an HttpResponse whose body is NOT buffered.
* The `rawResponse` property contains the unconsumed IncomingResponse
* stream for piping to files or other destinations.
*
* `body`, `text()`, `json()`, and `arrayBuffer()` return empty/zero
* values since the stream has not been read.
*
* Incompatible with `maxResponseSize` (size enforcement requires
* reading the body).
*
* @default false
*/
stream?: boolean | undefined
/**
* When true, non-2xx HTTP responses throw an `HttpResponseError` instead
* of resolving with `response.ok === false`. This makes HTTP error
* responses eligible for retry via the `retries` option.
*
* @default false
*/
throwOnError?: boolean | undefined
/**
* Request timeout in milliseconds.
* If the request takes longer than this, it will be aborted.
*
* @default 30000
*/
timeout?: number | undefined
}