(options: HttpAdapterOptions = {})
| 305 | * network-level `TypeError` says nothing a reader can act on. |
| 306 | */ |
| 307 | export function createHttpAdapter(options: HttpAdapterOptions = {}): GraphAdapter { |
| 308 | const base = options.baseUrl ?? ''; |
| 309 | const doFetch = options.fetch ?? ((...args: Parameters<typeof globalThis.fetch>) => |
| 310 | globalThis.fetch(...args)); |
| 311 | |
| 312 | async function call<T>(path: string, init: RequestInit, signal?: AbortSignal): Promise<T> { |
| 313 | let response: Response; |
| 314 | try { |
| 315 | response = await doFetch(`${base}${path}`, { ...init, signal }); |
| 316 | } catch (cause) { |
| 317 | if (signal?.aborted) throw cause; |
| 318 | throw new ApiFailure( |
| 319 | 0, |
| 320 | 'unreachable', |
| 321 | 'The codegraph ui server is not answering.', |
| 322 | 'It may have been stopped — restart it with `codegraph ui` and reload this page.' |
| 323 | ); |
| 324 | } |
| 325 | |
| 326 | const body = (await response.json().catch(() => null)) as unknown; |
| 327 | if (!response.ok) { |
| 328 | const failure = (body as ApiErrorBody | null) ?? {}; |
| 329 | throw new ApiFailure( |
| 330 | response.status, |
| 331 | failure.code ?? 'error', |
| 332 | failure.error ?? `The server answered ${response.status}.`, |
| 333 | failure.hint ?? null |
| 334 | ); |
| 335 | } |
| 336 | return body as T; |
| 337 | } |
| 338 | |
| 339 | function getJson<T>(path: string, signal?: AbortSignal): Promise<T> { |
| 340 | return call<T>(path, { headers: { accept: 'application/json' } }, signal); |
| 341 | } |
| 342 | |
| 343 | /** A write: the marker header, and a JSON body when there is one to send. */ |
| 344 | function write<T>(path: string, method: string, body?: unknown, signal?: AbortSignal): Promise<T> { |
| 345 | const headers: Record<string, string> = { |
| 346 | accept: 'application/json', |
| 347 | [WRITE_HEADER]: '1', |
| 348 | }; |
| 349 | if (body !== undefined) headers['content-type'] = 'application/json'; |
| 350 | return call<T>( |
| 351 | path, |
| 352 | { method, headers, ...(body === undefined ? {} : { body: JSON.stringify(body) }) }, |
| 353 | signal |
| 354 | ); |
| 355 | } |
| 356 | |
| 357 | return { |
| 358 | stats: (signal) => getJson<WireStats>('api/stats', signal), |
| 359 | |
| 360 | search(text, opts = {}, signal) { |
| 361 | const params = new URLSearchParams({ q: text }); |
| 362 | if (opts.limit) params.set('limit', String(opts.limit)); |
| 363 | return getJson<WireSearch>(`api/search${query(params)}`, signal); |
| 364 | }, |
no test coverage detected