@setu-ts/sdk
v0.11.0
Published
Portable HTTP client with retry, circuit breaker, rate limiting, and OpenAPI codegen
Readme
@setu-ts/sdk
Portable, zero-npm-dependency client SDK for consuming Setu-TS APIs from browsers and servers.
Provides an injectable fetch HTTP client, bearer and API-key authentication interceptors,
per-origin retry with backoff, circuit breaker, and sliding-window rate limiting, plus a pure
generator that turns OpenAPI 3.1 documents into type-checked TypeScript client source.
This package does not register a plugin or resolve capability tokens. It is an external-consumer
library with no dependency on kernel, runtime, or any plugin — its only in-repo import is
type-level from @setu-ts/common.
Realtime clients
createSseClient consumes an SSE endpoint through fetch on every supported runtime. It sends
configured headers on every reconnect, ignores comment-frame heartbeats, parses JSON events, applies
server retry: delays, and sends Last-Event-ID after an identified event. It does not delegate to
EventSource, so bearer authentication works equally in browsers and server runtimes.
import { createSseClient } from '@setu-ts/sdk';
const events = createSseClient({
url: 'https://api.example.com/sse/scores',
headers: { Authorization: 'Bearer token' },
onEvent: ({ event, data }) => console.log(event, data),
});
// later: events.close()createRealtimeClient wraps the global WebSocket with the server's application-level keep-alive
contract. It filters the heartbeat payload ('ping' by default), replies to keep the server's
inbound-idle timer fresh, reconnects with backoff, and re-applies the configured room query value.
import { createRealtimeClient } from '@setu-ts/sdk';
const board = createRealtimeClient({
url: 'wss://api.example.com/ws/board',
room: 'game-7',
onMessage: ({ data }) => console.log(data),
});Installation
# npm / pnpm / yarn
npm install @setu-ts/sdk@^0.11.0
# Bun
bun add @setu-ts/sdk@^0.11.0
# Deno
deno add npm:@setu-ts/sdk@^0.11.0Quick Start
import { createBearerAuthInterceptor, createClient } from '@setu-ts/sdk';
const client = createClient({
baseUrl: 'https://api.example.com',
requestInterceptors: [createBearerAuthInterceptor('my-token')],
retry: {
limit: 3,
delay: 500,
backoff: 'exponential',
},
});
const res = await client.request<User>({
method: 'GET',
path: 'users/123',
});
console.log(res.data); // UserOptions
createClient(options) takes:
| Option | Type | Default | Description |
| ---------------------- | ----------------------------- | --------------------------------------- | ----------------------------------------------------------------------- |
| baseUrl | string | — | Base URL for every request. Required. |
| headers | HeadersInit | — | Headers merged into every request. |
| fetch | typeof fetch | global fetch | Injected transport. |
| timing | IClientTiming | performance.now() + abort-aware sleep | Clock and sleep seam, so tests need no real time. |
| retry | ClientRetryPolicy | off | Retry with fixed or exponential backoff. |
| circuitBreaker | CircuitBreakerPolicy | off | Rolling-window breaker. |
| rateLimit | ClientRateLimitPolicy | off | Sliding-window limiter. |
| idempotency | ClientIdempotencyOptions | off | Generated keys for POST/PATCH; see Keyed retries. |
| requestInterceptors | ClientRequestInterceptor[] | [] | Run in order before the request. |
| responseInterceptors | ClientResponseInterceptor[] | [] | Run in order after the response. |
fetch and timing are the two seams that keep the client testable without a network or real time
— Date.now() appears nowhere in this package. See Resilience for the three policy
shapes and Authentication for the bundled interceptors.
The fetch default needs no browser-specific value: it resolves globalThis.fetch at call
time, with the global as its receiver, so the default transport works in a browser unchanged. (An
injected fetch is always used as-is, which is what tests rely on.)
HTTP Client
createClient(options) returns an IHttpClient whose single method is
request<TResponse, TBody>(request: ClientRequest<TBody>): Promise<ClientResponse<TResponse>>.
ClientOptions
| Option | Type | Description |
| ---------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------ |
| baseUrl | string (required) | Base URL for every request |
| headers | Record<string, string> | Default headers cloned into each request |
| fetch | Function | Injectable fetch seam (default bound to the global realm) |
| timing | IClientTiming | Injectable timing seam (defaults to createDefaultClientTiming()) |
| retry | ClientRetryPolicy | Retry policy (transport failures + 408/425/429/5xx on safe methods; any method, and 409, when keyed) |
| circuitBreaker | CircuitBreakerPolicy | Per-origin circuit breaker policy |
| rateLimit | ClientRateLimitPolicy | Per-origin sliding-window rate limiter |
| idempotency | ClientIdempotencyOptions | Generated idempotency keys; see Keyed retries |
| requestInterceptors | ClientRequestInterceptor[] | Run once before resilient execution |
| responseInterceptors | ClientResponseInterceptor[] | Run after successful parse; skipped on failure |
Request
interface ClientRequest<TBody = never> {
method: string;
path: string;
query?: Record<string, string | string[]>;
headers?: Record<string, string>;
json?: TBody;
idempotencyKey?: string;
signal?: AbortSignal;
}pathmust be relative. A leading slash, a scheme-relative//host/x, and a fully absolutehttps://host/xare all rejected — otherwise a request could leavebaseUrl's origin and bypass the per-origin circuit breaker and rate limiter.- Query values are encoded; arrays repeat the key.
jsonis serialized withJSON.stringify;Content-Type: application/jsonis set automatically.signalaborts fetches, queued waits, and retry attempts.
Response
interface ClientResponse<T> {
status: number;
headers: Headers;
data?: T;
}datais parsed when the responseContent-Typenames JSON:application/jsonor any structured+jsonsuffix (application/problem+json,application/vnd.api+json). Media-type parameters and casing are ignored.dataisundefinedfor204, for an empty body, and for a non-JSON content type.- Non-2xx responses throw
HttpClientErrorrather than resolving.
Authentication
Bearer Token
import { createBearerAuthInterceptor } from '@setu-ts/sdk';
const client = createClient({
baseUrl: 'https://api.example.com',
requestInterceptors: [
// Literal token
createBearerAuthInterceptor('my-token'),
// Or async provider
createBearerAuthInterceptor(() => tokenStore.get()),
],
});API Key
import { createApiKeyAuthInterceptor } from '@setu-ts/sdk';
const client = createClient({
baseUrl: 'https://api.example.com',
requestInterceptors: [
// Default header: X-API-Key
createApiKeyAuthInterceptor('my-key'),
// Custom header
createApiKeyAuthInterceptor('my-key', 'Authorization'),
],
});Both factories accept a literal string or an async value provider. The header is only set when the request has not already supplied that header.
Resilience
Retry
import { BackoffStrategy } from '@setu-ts/sdk';
const client = createClient({
baseUrl: 'https://api.example.com',
retry: {
limit: 3, // total attempts
delay: 500, // base delay in ms
backoff: 'exponential' as BackoffStrategy,
},
});Retries transport rejections and statuses 408, 425, 429, 500-599. By default, only retries safe
methods (GET, HEAD, OPTIONS, PUT, DELETE). When a retryable response carries a Retry-After header
with delta-seconds, that delay replaces the computed backoff only when it is within the policy cap.
Set maxRetryAfterMs explicitly, or omit it to use the policy's largest fixed/exponential backoff.
A larger hint surfaces the original HttpClientError immediately, with its headers intact.
At construction, limit must be a positive safe integer, delay must be finite and non-negative,
and every explicit or derived delay must be at most 2_147_483_647 ms, the portable JavaScript
timer maximum. Runtime strings, non-finite values, and timer-overflowing delays are rejected so they
cannot disable the cap or be clamped into an immediate retry.
Keyed retries
ClientRequest.idempotencyKey supplies a key; ClientOptions.idempotency generates one by default
for POST and PATCH. The default header is Idempotency-Key; configure idempotency.header to
match the server. When ClientOptions.idempotency applies to the request's method and
ClientRequest.idempotencyKey is absent, a request that already carries the configured header uses
its value as the key. Without matching configuration the header is sent but does not make the
request keyed, and a request carrying both idempotencyKey and that header is refused rather than
either value replacing the other. The same key is reused across retry attempts, including a
retryable 409.
Keyed POST/PATCH retries are safe ONLY when the server de-duplicates on that key, for example
with idempotent() or within. Against a server that does not, a retried POST can execute twice.
A keyed request carries a non-safelisted header, so a cross-origin browser call triggers a CORS
preflight; the server must list that header in Access-Control-Allow-Headers. For the page to read
the replay response, Access-Control-Expose-Headers must list Idempotent-Replayed.
A generated operation takes the same key per call in its trailing request parameter
(request?: OperationRequestOptions): api.createOrder({ body }, { idempotencyKey }). The
idempotencyKey and signal fields carry the same meaning as the same-named fields of
ClientRequest, and the generated code forwards only those two fields.
Circuit Breaker
const client = createClient({
baseUrl: 'https://api.example.com',
circuitBreaker: {
threshold: 5,
timeout: 30_000,
resetTimeout: 10_000,
},
});Per-origin rolling-window circuit breaker. An open circuit throws ClientCircuitOpenError before
consuming a rate-limit slot. The breaker counts one failure per exhausted transient sequence (not
one per retry attempt).
The two windows are independent:
timeoutis the rolling window used to decide when the circuit trips —thresholdcounted failures inside it open the circuit.resetTimeoutis how long the circuit stays open, measured from the moment it tripped. It is not shortened bytimeoutelapsing, sotimeoutmay safely be shorter thanresetTimeout.
After resetTimeout elapses, exactly one half-open probe is admitted; concurrent callers get
ClientCircuitOpenError. A successful probe closes the circuit and clears the window. A failed
probe reopens it and restarts resetTimeout, so a dead dependency is probed once per cooldown
rather than on every request.
What counts as a failure: a 5xx response, a transport rejection, or an exhausted retry sequence. A
4xx HttpClientError does not — a bad request means the caller was wrong, not that the origin
is unhealthy — and neither does a caller abort.
Rate Limiting
const client = createClient({
baseUrl: 'https://api.example.com',
rateLimit: {
maxRequests: 100,
windowMs: 60_000,
},
});Per-origin sliding-window limiter. When the window is full, the client waits until the oldest retained timestamp expires.
Interceptors
Request Interceptors
import { ClientRequestContext, ClientRequestInterceptor } from '@setu-ts/sdk';
const loggingInterceptor: ClientRequestInterceptor = (ctx: ClientRequestContext) => {
// The context carries the resolved `url` and the mutable `headers`; the
// method is not part of it.
console.log(ctx.url.href);
ctx.headers.set('x-request-id', crypto.randomUUID());
};
const client = createClient({
baseUrl: 'https://api.example.com',
requestInterceptors: [loggingInterceptor],
});Request interceptors receive a mutable ClientRequestContext (resolved URL and Headers) and
execute once in registration order before the outbound attempt sequence.
Use createTraceContextInterceptor(telemetry) to propagate the active span as a W3C traceparent.
It reads activeSpanContext() per request, preserves a caller-supplied header, and omits invalid or
absent contexts. The telemetry service is structural and imported type-only, so the SDK keeps its
zero-runtime-dependency boundary.
Response Interceptors
import { ClientRequestContext, ClientResponse, ClientResponseInterceptor } from '@setu-ts/sdk';
const timingInterceptor: ClientResponseInterceptor<unknown> = (
response: ClientResponse<unknown>,
request: ClientRequestContext,
) => {
console.log(`${request.url.href} → ${response.status}`);
return response;
};Response interceptors receive a successful ClientResponse<T> and its immutable request
description. They are skipped entirely when the request throws.
OpenAPI Code Generation
generateOpenApiClient(document, options?) is a pure function that turns an OpenAPI 3.1 document
into TypeScript source.
import { generateOpenApiClient } from '@setu-ts/sdk';
import { readFileSync } from 'node:fs';
const document = JSON.parse(readFileSync('openapi.json', 'utf-8'));
const source = generateOpenApiClient(document, {
sdkImport: '@setu-ts/sdk',
factoryName: 'createApi',
apiTypeName: 'Api',
});
// Write `source` to a file, then import the generated factory:
// import { createApi } from './generated-api.js';
// const api = createApi(client);The generated factory accepts an IHttpClient and returns typed operation methods. Schema mapping
covers the M21 vocabulary: primitives, arrays, objects with required, $ref, enum, const,
anyOf, allOf, additionalProperties, null, and integer.
The output is publishable, formatted and lint-clean. createApi has a written-out return type
(export interface Api), so JSR does not reject the file as a slow type; the source round-trips
through deno fmt unchanged and needs no lint pragma. Both properties are load-bearing rather than
cosmetic: the generated file's own header says "Do not edit manually", so a consumer who cannot
publish or format it has nowhere to go.
Generated names and shapes
Operation methods are lower-camelCase and preserve interior casing. An
operationIdis split on every run of non-alphanumeric characters and re-joined, solistUsersstayslistUsersandget-users-{id}becomesgetUsersId. A leading digit run is prefixed withn, a reserved word is prefixed with_, and an id that sanitizes to nothing becomesoperation.Component schemas and argument interfaces are PascalCase. Component
Useremitsexport type User, and operationlistUsersemitsexport interface ListUsersArgs.The factory returns a named interface.
export interface Api { … }lists every operation's signature and is the factory's return type; rename it withapiTypeName.Two names that derive onto one identifier throw
OpenApiCodegenErrorrather than emitting a file with a duplicate declaration, and the diagnostic names both originals. Component schemas,*Argsinterfaces,*Errorunions, the client interface and theapiTypeName/factoryNameoptions all draw from ONE registry, so a component namedListUsersArgsbeside an operationlistUsersis refused rather than silently emitting two declarations of one name. Each of those names derives from something the document or the caller WROTE, so a clash is a real problem you must see.A hoisted alias ALLOCATES its name instead of throwing. The four hoisted aliases — a request body, a parameter, a success response and an error body — name an anonymous inline schema the document never named, so the generator may pick any identifier and a clash is its own problem to solve. Each asks the registry for its preferred name first, so no alias that generates today is ever renamed; a success response then tries
…Response<status>Body, which reads correctly beside a component of the same name; and every arm finally takes a numeric suffix (…2,…3) allocated through the registry, because a suffix is not a namespace and a document may legally declare the suffixed name too. This matters because@setu-ts/openapi-pluginnames a reused response schema${operationId}Response${status}— exactly the derivation used here — so a schema that is both reused and enclosed in an inline body used to abort generation outright.Declared error responses are typed. An operation declaring an observable non-2xx response also emits a union discriminated on the literal
statusand a narrowing guard:try { await api.getUserById('1'); } catch (e) { if (isGetUserByIdError(e) && e.status === 404) { e.body.code; // typed by the document's 404 schema } }Every declared
3xxkeeps its error arm, the auto-follow statuses (301,302,303,307, and308) included: Fetch returns such a response itself when it carries noLocation, and the client then throwsHttpClientErrorwith that status. Any operation declaring an auto-follow redirect or a3XXrange returnsunknown, because the follow target body is selected at runtime and is not described by the source operation.HttpClientErroris generic in its body (HttpClientError<TBody = unknown>), so the bare name means what it always did. The union is discriminated onstatusbecauseHttpClientError<A> | HttpClientError<B>is not —statusisnumberon both arms. Adefaultresponse and range codes such as4XXare skipped: they name no single status.Formatting matches
deno fmt. Two-space indentation, a signature past 100 columns wrapped one parameter per line, and a path template too long for one line emitted as an equivalent[…].join('')— a template literal is not usable there, becausedeno fmtrewraps a long one at whichever${happens to fit, which no generator can predict.No multi-line type is written at a use site. An inline (non-
$ref) body, parameter or success response is hoisted into an exported alias. A rendered type lands at several indentation levels, and a success type lands at two at once — the client interface's signature and theclient.request<…>type argument — so no single indentation is correct for a multi-line object literal anddeno fmtreindents whatever is emitted. A schema that@setu-ts/openapi-pluginderived fromvalidateBodyand used once arrives inline, so this is the ordinary case.No lint pragma. An empty-object schema emits
Record<PropertyKey, never>rather than{}, which is both what the schema means and whatdeno lint'sban-typesaccepts. A narrowed pragma could not be emitted unconditionally either: an ignore matching nothing is itself reported asban-unused-ignore.Path parameters are positional arguments; everything else lives in
opts. Each path placeholder is substituted and percent-encoded, including a placeholder that shares a segment with literal text (/files/{id}.json).Every operation ends with an optional trailing
requestparameter —request?: OperationRequestOptions, after the path parameters and afteropts— whoseidempotencyKeyandsignalfields carry the same meaning as the same-named fields ofClientRequest; the generated code forwards only those two fields, and existing calls are unaffected. A path parameter whose identifier isclient,opts,requestorencodeURIComponentis emitted with a_prefix, so it cannot shadow the factory'sclient, the method's locals or the global its path expression calls; path parameters are positional, so no caller changes. An operation whose method name isclientorencodeURIComponentis emitted as_clientor_encodeURIComponent— a renamed method, but before,clientnever compiled andencodeURIComponentbroke every generated path that encodes a parameter. A document component namedOperationRequestOptionsis refused likeClientResponse, rather than shadowing the import the generated file claims fromsdkImport.optsis required when any of its fields is required. A required query parameter or arequestBodymarkedrequiredmakes theoptsparameter itself required, so a caller cannot omit it and silently skip a mandatory value.Wire names are preserved. The query key and header name sent on the wire are the original OpenAPI names (
user_id,X-API-Key); only the TypeScript field identifier is derived. Header values are stringified, so a non-stringheader schema still compiles.All eight operation slots are generated, including
trace. Parameters declared at the path-item level are merged into every operation on that path; an operation's own parameter overrides a shared one with the samenameandin.Document text can never escape into code. Text emitted into a comment (the
operationIdon each generated method) has comment terminators escaped and line breaks collapsed, and every emitted string literal and path template is escaped for its own context.
Codegen diagnostics
Declared OpenAPI ids name generated methods just like derived ones: listUsers produces
listUsers. Distinct ids may sanitize to one method name: declared getUsers beside undeclared
GET /users (derived get-users) is valid OpenAPI, but code generation refuses it with
Duplicate operation name. Choose ids that yield distinct TypeScript names. Changing an id also
changes its generated argument, error and inline-schema type names; regenerate clients and update
their callers.
generateOpenApiClient throws OpenApiCodegenError — carrying path and method where they apply
— rather than emitting a client that misbehaves or does not compile:
| Condition | Why it is rejected |
| ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Missing operationId | No name to derive a method from |
| Two operations deriving onto one method name | Duplicate declaration; both originals are named |
| Two emitted TYPE names colliding | Duplicate declaration; one registry covers every emitted name. A HOISTED alias is the exception and does NOT throw — it allocates (see the naming rules above) |
| Parameter with in: 'cookie' | Unsupported location |
| Path placeholder with no matching in: 'path' parameter | Emitted source would reference an undeclared argument |
| in: 'path' parameter absent from the path template | The caller's value would be silently dropped |
| Two placeholders in one template deriving onto one argument name | Duplicate parameter in the emitted signature |
| Malformed local $ref | No component name to resolve |
Outbound HTTP observations (devtool)
createObservedFetch wraps a fetch so the local diagnostics connector can count its attempts: how
many started and are in flight, how many resolved or failed, the last response's status class and
its time to headers. It never reads a URL, header, body, signal or error, and it never changes what
the wrapped fetch receives or returns. Build it only when the application factory receives its
devtool composition, so a production entry carries no wrapper at all, and register its plugin in
that one application:
import type { IPlugin } from '@setu-ts/common';
import type { KernelDiagnosticsOptions } from '@setu-ts/kernel';
import { createApplication } from '@setu-ts/kernel';
import { RuntimePlugin } from '@setu-ts/runtime';
import { createClient, createObservedFetch } from '@setu-ts/sdk';
const PAYMENTS_URL = 'https://payments.internal.example';
export function createApp(
_env?: Readonly<Record<string, unknown>>,
devtool?: { plugins?: readonly IPlugin[]; diagnostics?: KernelDiagnosticsOptions },
) {
const observed = devtool ? createObservedFetch({ alias: 'payments-api' }) : undefined;
const payments = createClient({
baseUrl: PAYMENTS_URL,
...(observed ? { fetch: observed.fetch } : {}),
});
void payments; // routes use `payments`
return createApplication({
plugins: [RuntimePlugin(), ...(observed ? [observed.plugin] : []), ...(devtool?.plugins ?? [])],
...(devtool?.diagnostics !== undefined ? { diagnostics: devtool.diagnostics } : {}),
});
}The alias is disclosed to a paired devtool as written — choose a non-secret label, never a hostname.
A wrapped fetch should not depend on its receiver (prefer (input, init) => fetch(input, init)); a
direct observed.fetch(url) call forwards undefined. One helper serves one application:
registering its plugin in a second one throws. The helper needs crypto.getRandomValues (Node ≥
19, Deno, Bun, workerd, browsers).
Errors
| Error | When thrown |
| ------------------------ | ----------------------------------------------------------- |
| HttpClientError | Non-2xx HTTP response (carries status, headers, body) |
| ClientCircuitOpenError | Circuit breaker is open for the request origin |
| OpenApiCodegenError | Invalid OpenAPI document during code generation |
Public Surface
| Export | Kind | Description |
| ----------------------------- | ----------- | ---------------------------------------- |
| createClient | factory | Creates an IHttpClient instance |
| IHttpClient | interface | Client contract (request<T>()) |
| ClientOptions | interface | Factory options |
| ClientRequest | type | Outbound request description |
| ClientResponse | type | Parsed response |
| ClientRequestContext | type | Mutable context for request interceptors |
| ClientRequestInterceptor | type | Request interceptor signature |
| ClientResponseInterceptor | type | Response interceptor signature |
| IClientTiming | interface | Timing seam (now(), sleep()) |
| createDefaultClientTiming | factory | Default timing over performance.now() |
| createObservedFetch | factory | Devtool outbound-attempt counters (M98n) |
| ObservedFetch | interface | { fetch, plugin } returned by it |
| ObservedFetchOptions | interface | { alias, fetch?, timing? } |
| ClientRateLimitPolicy | type | Sliding-window rate limiter config |
| RetryPolicy | re-export | From @setu-ts/common |
| CircuitBreakerPolicy | re-export | From @setu-ts/common |
| BackoffStrategy | re-export | From @setu-ts/common |
| createBearerAuthInterceptor | factory | Bearer-token request interceptor |
| createApiKeyAuthInterceptor | factory | API-key request interceptor |
| HttpClientError | error class | Failed non-2xx response |
| ClientCircuitOpenError | error class | Fail-fast circuit state |
| OpenApiCodegenError | error class | Invalid-spec diagnostic |
| generateOpenApiClient | function | Pure OpenAPI → TypeScript generator |
| OpenApiCodegenOptions | type | Generator options |
| SdkOpenApi* | types | Structural OpenAPI 3.1 subset |
Portability
- Zero npm dependencies — uses only web-standard APIs (
URL,Headers,AbortSignal,performance.now()). - Browser and server — runs in browsers, Node.js, Deno, Bun, and Cloudflare Workers. The default
transport needs no browser-specific value: it resolves
globalThis.fetchat call time with the global as its receiver, so the default works in a browser unchanged. - Injectable
fetch— tests inject a fake; the default is bound to the global realm. - Injectable timing —
IClientTimingmakes backoff, breaker windows, and rate-limit waits testable without real time.
Architecture · Public API · Roadmap
Exports
| Export | Kind |
| ------------------------------- | --------- |
| createApiKeyAuthInterceptor | function |
| createBearerAuthInterceptor | function |
| createClient | function |
| createDefaultClientTiming | function |
| createObservedFetch | function |
| createRealtimeClient | function |
| createSseClient | function |
| createTraceContextInterceptor | function |
| generateOpenApiClient | function |
| ClientCircuitOpenError | class |
| HttpClientError | class |
| OpenApiCodegenError | class |
| CircuitBreakerPolicy | interface |
| ClientIdempotencyOptions | interface |
| ClientOptions | interface |
| ClientRateLimitPolicy | interface |
| ClientRequest | interface |
| ClientRequestContext | interface |
| ClientResponse | interface |
| IClientTiming | interface |
| IHttpClient | interface |
| IRealtimeClient | interface |
| ISseClient | interface |
| IWebSocketTransport | interface |
| ObservedFetch | interface |
| ObservedFetchOptions | interface |
| OpenApiCodegenOptions | interface |
| OperationRequestOptions | interface |
| RawSseEvent | interface |
| RealtimeClientOptions | interface |
| RealtimeMessage | interface |
| RealtimeReconnectOptions | interface |
| RetryPolicy | interface |
| SdkOpenApiDocument | interface |
| SdkOpenApiOperation | interface |
| SdkOpenApiParameter | interface |
| SdkOpenApiPathItem | interface |
| SdkOpenApiRequestBody | interface |
| SdkOpenApiResponse | interface |
| SdkOpenApiSchema | interface |
| SseClientOptions | interface |
| SseEvent | interface |
| SseReconnectOptions | interface |
| BackoffStrategy | type |
| ClientRequestInterceptor | type |
| ClientResponseInterceptor | type |
| ClientRetryPolicy | type |
| RealtimeClientState | type |
| SseClientState | type |
| SseEventMap | type |
| WebSocketFactory | type |
Generated from the package barrel by pnpm run docs:exports; pnpm run check:docs fails when it
drifts.
