@samveys/http-client

Retries, timeouts, concurrency pool, fakes and SSRF protection

Version
1.0.2Nov 5, 2026
Install
pnpm add @samveys/http-client
Import
import { createHttpClient, isForbiddenAddress, vetUserUrl } from '@samveys/http-client'

Function (3)

createHttpClient Function

Creates an HTTP client over `fetch`-style requests. See the package README.

function createHttpClient(options?: HttpClientOptions): HttpClient;

Exported from packages/http-client/src/client.ts

isForbiddenAddress Function

True when the literal IP address must never be contacted on behalf of a user.

function isForbiddenAddress(address: string): boolean;

Exported from packages/http-client/src/ssrf.ts

vetUserUrl Function

Vets a user-supplied URL and returns the address the connection must be pinned to. Every address the name resolves to has to be public, so a mixed answer is refused too.

function vetUserUrl(url: URL, resolver?: HostResolver): Promise<ResolvedAddress>;

Exported from packages/http-client/src/ssrf.ts

Interface (6)

HttpClient Interface

No documentation comment.

interface HttpClient {
    request(url: string, options?: RequestOptions): Promise<Response>;
    get(url: string, options?: RequestOptions): Promise<Response>;
    post(url: string, options?: RequestOptions): Promise<Response>;
    put(url: string, options?: RequestOptions): Promise<Response>;
    patch(url: string, options?: RequestOptions): Promise<Response>;
    delete(url: string, options?: RequestOptions): Promise<Response>;
    /** Sends the request and parses a successful JSON response. */
    json<T = unknown>(url: string, options?: RequestOptions): Promise<T>;
    /** A copy of this client that treats every URL as user supplied. */
  …
Show the whole declaration (20 lines)
interface HttpClient {
    request(url: string, options?: RequestOptions): Promise<Response>;
    get(url: string, options?: RequestOptions): Promise<Response>;
    post(url: string, options?: RequestOptions): Promise<Response>;
    put(url: string, options?: RequestOptions): Promise<Response>;
    patch(url: string, options?: RequestOptions): Promise<Response>;
    delete(url: string, options?: RequestOptions): Promise<Response>;
    /** Sends the request and parses a successful JSON response. */
    json<T = unknown>(url: string, options?: RequestOptions): Promise<T>;
    /** A copy of this client that treats every URL as user supplied. */
    userUrl(url: string, options?: RequestOptions): Promise<Response>;
    /** Registers fakes. Requests matching a pattern never touch the network. */
    fake(table?: FakeTable): HttpClient;
    /** Unfaked requests fail with SVS_E106 while faking is active. */
    preventStrayRequests(): HttpClient;
    allowStrayRequests(): HttpClient;
    /** Requests handled since the client was created (or {@link clearRecorded}). */
    recorded(): readonly RecordedRequest[];
    clearRecorded(): void;
}

Exported from packages/http-client/src/contracts.ts

HttpClientOptions Interface

No documentation comment.

interface HttpClientOptions {
    /** Prefix for relative request URLs. */
    readonly baseUrl?: string;
    /** Per-attempt timeout. Default 10 000 ms. */
    readonly timeoutMs?: number;
    readonly retries?: Partial<RetryOptions>;
    /** Maximum simultaneous in-flight requests. Default 8. */
    readonly concurrency?: number;
    /** Headers sent with every request. */
    readonly headers?: Readonly<Record<string, string>>;
  …
Show the whole declaration (19 lines)
interface HttpClientOptions {
    /** Prefix for relative request URLs. */
    readonly baseUrl?: string;
    /** Per-attempt timeout. Default 10 000 ms. */
    readonly timeoutMs?: number;
    readonly retries?: Partial<RetryOptions>;
    /** Maximum simultaneous in-flight requests. Default 8. */
    readonly concurrency?: number;
    /** Headers sent with every request. */
    readonly headers?: Readonly<Record<string, string>>;
    /** Treat every URL as user supplied (SSRF protection). Default false. */
    readonly untrusted?: boolean;
    /** Redirects followed (each hop is re-vetted in untrusted mode). Default 5. */
    readonly maxRedirects?: number;
    /** Host resolver; defaults to `node:dns/promises` `lookup(all)`. */
    readonly resolver?: HostResolver;
    /** Transport override (tests, custom agents). */
    readonly transport?: HttpTransport;
}

Exported from packages/http-client/src/contracts.ts

RecordedRequest Interface

A request seen by the client, kept for assertions in tests.

interface RecordedRequest {
    readonly method: string;
    readonly url: string;
    readonly headers: Readonly<Record<string, string>>;
    readonly body: string | null;
    readonly faked: boolean;
}

Exported from packages/http-client/src/contracts.ts

RequestOptions Interface

No documentation comment.

interface RequestOptions {
    readonly method?: string;
    readonly headers?: Readonly<Record<string, string>>;
    readonly query?: Readonly<Record<string, string | number | boolean | undefined>>;
    /** Raw body. */
    readonly body?: RequestBody | null;
    /** JSON body; sets `content-type: application/json`. */
    readonly json?: unknown;
    readonly timeoutMs?: number;
    readonly retries?: Partial<RetryOptions>;
  …
Show the whole declaration (14 lines)
interface RequestOptions {
    readonly method?: string;
    readonly headers?: Readonly<Record<string, string>>;
    readonly query?: Readonly<Record<string, string | number | boolean | undefined>>;
    /** Raw body. */
    readonly body?: RequestBody | null;
    /** JSON body; sets `content-type: application/json`. */
    readonly json?: unknown;
    readonly timeoutMs?: number;
    readonly retries?: Partial<RetryOptions>;
    readonly untrusted?: boolean;
    readonly maxRedirects?: number;
    readonly signal?: AbortSignal;
}

Exported from packages/http-client/src/contracts.ts

ResolvedAddress Interface

A single resolved address of a host.

interface ResolvedAddress {
    readonly address: string;
    readonly family: 4 | 6;
}

Exported from packages/http-client/src/contracts.ts

RetryOptions Interface

Retry policy: `count` extra attempts, waiting `backoffMs[attempt]` between them.

interface RetryOptions {
    readonly count: number;
    readonly backoffMs: readonly number[];
    /** Response status codes that trigger a retry. Network errors always do. */
    readonly on: readonly number[];
}

Exported from packages/http-client/src/contracts.ts

Type (6)

Awaitable Type

No documentation comment.

type Awaitable<T> = T | Promise<T>;

Exported from packages/http-client/src/contracts.ts

FakeResponder Type

A canned reply for {@link HttpClient.fake}.

type FakeResponder = Response | ((request: Request) => Awaitable<Response>) | {

Exported from packages/http-client/src/contracts.ts

FakeTable Type

Fake table: URL pattern (a `*` matches any run of characters) to reply.

type FakeTable = Readonly<Record<string, FakeResponder>>;

Exported from packages/http-client/src/contracts.ts

HostResolver Type

Resolves a host name to every address it points to.

type HostResolver = (hostname: string) => Promise<readonly ResolvedAddress[]>;

Exported from packages/http-client/src/contracts.ts

HttpTransport Type

Performs one HTTP exchange without following redirects. `pinned` is set in untrusted mode: the socket MUST connect to that address.

type HttpTransport = (request: Request, pinned?: ResolvedAddress) => Promise<Response>;

Exported from packages/http-client/src/contracts.ts

RequestBody Type

Anything `fetch` accepts as a request body.

type RequestBody = NonNullable<RequestInit['body']>;

Exported from packages/http-client/src/contracts.ts