| 149 | ).pipe(Effect.ignore); |
| 150 | |
| 151 | const request = async <T>( |
| 152 | target: TargetShape, |
| 153 | identity: Identity, |
| 154 | path: string, |
| 155 | init: RequestInit = {}, |
| 156 | expectedStatus = 200, |
| 157 | timeoutMs = 30_000, |
| 158 | ): Promise<T> => { |
| 159 | const controller = new AbortController(); |
| 160 | const timeout = setTimeout(() => controller.abort(), timeoutMs); |
| 161 | try { |
| 162 | const headers = new Headers(init.headers); |
| 163 | for (const [name, value] of Object.entries(identity.headers ?? {})) headers.set(name, value); |
| 164 | if (init.body !== undefined && !headers.has("content-type")) { |
| 165 | headers.set("content-type", "application/json"); |
| 166 | } |
| 167 | const response = await fetch(new URL(path, target.baseUrl), { |
| 168 | ...init, |
| 169 | headers, |
| 170 | signal: controller.signal, |
| 171 | }); |
| 172 | const text = await response.text(); |
| 173 | expect(response.status, `${init.method ?? "GET"} ${path}: ${text}`).toBe(expectedStatus); |
| 174 | return (text.length > 0 ? JSON.parse(text) : null) as T; |
| 175 | } finally { |
| 176 | clearTimeout(timeout); |
| 177 | } |
| 178 | }; |
| 179 | |
| 180 | const postJson = <T>( |
| 181 | target: TargetShape, |