(
path: string,
opts: {
method?: string;
body?: unknown;
query?: Record<string, unknown>;
schema: z.ZodType<T>;
}
)
| 449 | signal?: AbortSignal; |
| 450 | deadlineMs?: number; |
| 451 | } |
| 452 | ): Promise<T> { |
| 453 | const deadline = opts.deadlineMs ?? CONTROL_PLANE_DEADLINE_MS; |
| 454 | return withDeadline( |
| 455 | deadline, |
| 456 | async signal => { |
| 457 | try { |
| 458 | return await this.httpRequestOnce(path, { ...opts, signal }); |
| 459 | } catch (err) { |
| 460 | // Deadline/timeout errors must NOT trigger unauthorized recovery. |
| 461 | const onUnauthorized = this.onUnauthorized; |
| 462 | if (!this.shouldRecoverFromUnauthorized(err) || onUnauthorized === undefined) { |
| 463 | throw err; |
| 464 | } |
| 465 | const decision = await onUnauthorized(); |
| 466 | if (decision !== 'retry') { |
| 467 | throw err; |
| 468 | } |
| 469 | return this.httpRequestOnce(path, { ...opts, signal }); |
| 470 | } |
| 471 | }, |
| 472 | opts.signal |
| 473 | ); |
| 474 | } |
| 475 | |
| 476 | private shouldRecoverFromUnauthorized(err: unknown): err is KiloChatApiError { |
| 477 | return ( |
| 478 | this.onUnauthorized !== undefined && |
| 479 | err instanceof KiloChatApiError && |
| 480 | (err.status === 401 || err.status === 403) |
| 481 | ); |
| 482 | } |
| 483 | |
| 484 | private async httpRequestOnce<T>( |
| 485 | path: string, |
| 486 | opts: { |
| 487 | method?: string; |
| 488 | body?: unknown; |
| 489 | query?: Record<string, unknown>; |
| 490 | schema: z.ZodType<T>; |
| 491 | signal?: AbortSignal; |
| 492 | } |
| 493 | ): Promise<T> { |
no test coverage detected