| 160 | } |
| 161 | |
| 162 | export class HttpClient { |
| 163 | private readonly baseUrl: string; |
| 164 | private readonly apiKey?: string; |
| 165 | private readonly fetchImpl: FetchImpl; |
| 166 | private readonly sleep: (ms: number) => Promise<void>; |
| 167 | private readonly random: () => number; |
| 168 | private readonly onDebug?: (event: DebugEvent) => void; |
| 169 | private readonly onTransition?: (msg: string) => void; |
| 170 | private readonly requestTimeoutMs: number; |
| 171 | |
| 172 | constructor(options: HttpClientOptions) { |
| 173 | this.baseUrl = trimTrailingSlash(options.baseUrl); |
| 174 | this.apiKey = options.apiKey; |
| 175 | this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis); |
| 176 | this.sleep = options.sleep ?? defaultSleep; |
| 177 | this.random = options.random ?? Math.random; |
| 178 | this.onDebug = options.onDebug; |
| 179 | this.onTransition = options.onTransition; |
| 180 | this.requestTimeoutMs = options.requestTimeoutMs ?? REQUEST_TIMEOUT_DEFAULT_MS; |
| 181 | } |
| 182 | |
| 183 | async get<T>(path: string, options: RequestOptions = {}): Promise<T> { |
| 184 | return this.requestWithMeta<T>('GET', path, options).then(r => r.body); |
| 185 | } |
| 186 | |
| 187 | async post<T>(path: string, options: RequestOptions = {}): Promise<T> { |
| 188 | return this.requestWithMeta<T>('POST', path, options).then(r => r.body); |
| 189 | } |
| 190 | |
| 191 | async put<T>(path: string, options: RequestOptions = {}): Promise<T> { |
| 192 | return this.requestWithMeta<T>('PUT', path, options).then(r => r.body); |
| 193 | } |
| 194 | |
| 195 | async patch<T>(path: string, options: RequestOptions = {}): Promise<T> { |
| 196 | return this.requestWithMeta<T>('PATCH', path, options).then(r => r.body); |
| 197 | } |
| 198 | |
| 199 | async delete<T>(path: string, options: RequestOptions = {}): Promise<T> { |
| 200 | return this.requestWithMeta<T>('DELETE', path, options).then(r => r.body); |
| 201 | } |
| 202 | |
| 203 | /** |
| 204 | * Like `get` / `post` / etc. but returns the full `RequestResult` including |
| 205 | * `requestId` and `status`, so callers can surface the requestId in |
| 206 | * happy-path output (dogfood item 1). |
| 207 | */ |
| 208 | async getWithMeta<T>(path: string, options: RequestOptions = {}): Promise<RequestResult<T>> { |
| 209 | return this.requestWithMeta<T>('GET', path, options); |
| 210 | } |
| 211 | |
| 212 | async postWithMeta<T>(path: string, options: RequestOptions = {}): Promise<RequestResult<T>> { |
| 213 | return this.requestWithMeta<T>('POST', path, options); |
| 214 | } |
| 215 | |
| 216 | async putWithMeta<T>(path: string, options: RequestOptions = {}): Promise<RequestResult<T>> { |
| 217 | return this.requestWithMeta<T>('PUT', path, options); |
| 218 | } |
| 219 |
nothing calls this directly
no outgoing calls
no test coverage detected