@ensnode/ensrainbow-sdk
Version:
ENSRainbow SDK for interacting with the ENSRainbow API.
341 lines (338 loc) • 14.2 kB
TypeScript
import { LabelHash, EncodedLabelHash, Label } from 'enssdk';
import { EnsRainbowClientLabelSet, EnsRainbowPublicConfig } from '@ensnode/ensnode-sdk';
/**
* Error thrown by {@link EnsRainbowApiClient} methods when the ENSRainbow service responds
* with a non-2xx HTTP status code.
*
* Carries the HTTP status code as a structured property (rather than only embedding it in the
* error message) so callers can branch their retry/abort logic on the status — e.g. retry on
* `503 Service Unavailable` while ENSRainbow bootstraps, but abort immediately on `404`/`500`,
* which usually indicate a misconfigured base URL or a hard server failure.
*
* Network-level failures (DNS, ECONNREFUSED, fetch parse errors) are *not* wrapped in this
* class — they propagate as their original `Error` (typically a `TypeError` from `fetch`),
* because such failures are commonly transient during cold start and should remain retryable
* by callers.
*/
declare class EnsRainbowHttpError extends Error {
readonly name = "EnsRainbowHttpError";
/**
* The HTTP status code returned by the ENSRainbow service.
*/
readonly status: number;
/**
* The HTTP status text returned by the ENSRainbow service, if any.
*/
readonly statusText: string;
constructor(message: string, status: number, statusText?: string);
}
declare namespace EnsRainbow {
export type ApiClientOptions = EnsRainbowApiClientOptions;
export interface ApiClient {
count(): Promise<CountResponse>;
/**
* Get the public configuration of the ENSRainbow service
*/
config(): Promise<ENSRainbowPublicConfig>;
/**
* Heal a labelHash to its original label.
* Accepts a strict `LabelHash`, an `EncodedLabelHash` (bracket-enclosed), or any string
* that can be normalized (missing `0x` prefix, uppercase hex chars, or 63-char hex).
* Returns a `HealBadRequestError` if the input cannot be normalized to a valid labelHash.
*/
heal(labelHash: LabelHash | EncodedLabelHash | string): Promise<HealResponse>;
health(): Promise<HealthResponse>;
/**
* Check whether the ENSRainbow service has finished bootstrapping and is ready to serve requests.
*
* Throws when the service is not ready (e.g. 503 while the database is still being downloaded
* or validated) so callers can retry.
*/
ready(): Promise<ReadyResponse>;
getOptions(): Readonly<EnsRainbowApiClientOptions>;
}
type StatusCode = (typeof StatusCode)[keyof typeof StatusCode];
type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];
export interface HealthResponse {
status: "ok";
}
/**
* Response returned by `GET /ready` when the ENSRainbow service is ready to serve requests.
*/
export interface ReadyResponse {
status: "ok";
}
/**
* Generic error shape used by endpoints that return 503 Service Unavailable while the
* database is still bootstrapping (downloading, extracting, or validating).
*/
export interface ServiceUnavailableError {
status: typeof StatusCode.Error;
error: string;
errorCode: typeof ErrorCode.ServiceUnavailable;
}
export interface BaseHealResponse<Status extends StatusCode, Error extends ErrorCode> {
status: Status;
label?: Label | never;
error?: string | never;
errorCode?: Error | never;
}
export interface HealSuccess extends BaseHealResponse<typeof StatusCode.Success, never> {
status: typeof StatusCode.Success;
label: Label;
error?: never;
errorCode?: never;
}
export interface HealNotFoundError extends BaseHealResponse<typeof StatusCode.Error, typeof ErrorCode.NotFound> {
status: typeof StatusCode.Error;
label?: never;
error: string;
errorCode: typeof ErrorCode.NotFound;
}
export interface HealServerError extends BaseHealResponse<typeof StatusCode.Error, typeof ErrorCode.ServerError> {
status: typeof StatusCode.Error;
label?: never;
error: string;
errorCode: typeof ErrorCode.ServerError;
}
export interface HealBadRequestError extends BaseHealResponse<typeof StatusCode.Error, typeof ErrorCode.BadRequest> {
status: typeof StatusCode.Error;
label?: never;
error: string;
errorCode: typeof ErrorCode.BadRequest;
}
export interface HealServiceUnavailableError extends BaseHealResponse<typeof StatusCode.Error, typeof ErrorCode.ServiceUnavailable> {
status: typeof StatusCode.Error;
label?: never;
error: string;
errorCode: typeof ErrorCode.ServiceUnavailable;
}
export type HealResponse = HealSuccess | HealNotFoundError | HealServerError | HealBadRequestError | HealServiceUnavailableError;
export type HealError = Exclude<HealResponse, HealSuccess>;
/**
* Server errors and transient bootstrap errors should not be cached.
*/
export type CacheableHealResponse = Exclude<HealResponse, HealServerError | HealServiceUnavailableError>;
export interface BaseCountResponse<Status extends StatusCode, Error extends ErrorCode> {
status: Status;
count?: number | never;
timestamp?: string | never;
error?: string | never;
errorCode?: Error | never;
}
export interface CountSuccess extends BaseCountResponse<typeof StatusCode.Success, never> {
status: typeof StatusCode.Success;
/** The total count of labels that can be healed by the ENSRainbow instance. Always a
* non-negative integer. */
count: number;
timestamp: string;
error?: never;
errorCode?: never;
}
export interface CountServerError extends BaseCountResponse<typeof StatusCode.Error, typeof ErrorCode.ServerError> {
status: typeof StatusCode.Error;
count?: never;
timestamp?: never;
error: string;
errorCode: typeof ErrorCode.ServerError;
}
export interface CountServiceUnavailableError extends BaseCountResponse<typeof StatusCode.Error, typeof ErrorCode.ServiceUnavailable> {
status: typeof StatusCode.Error;
count?: never;
timestamp?: never;
error: string;
errorCode: typeof ErrorCode.ServiceUnavailable;
}
export type CountResponse = CountSuccess | CountServerError | CountServiceUnavailableError;
/**
* Complete public configuration object for ENSRainbow.
*
* Contains all public configuration information about the ENSRainbow service instance,
* including version, label set information, and record counts.
*/
export type ENSRainbowPublicConfig = EnsRainbowPublicConfig;
export { };
}
interface EnsRainbowApiClientOptions {
/**
* The maximum number of `HealResponse` values to cache.
* Must be a non-negative integer.
* Setting to 0 will disable caching.
*/
cacheCapacity: number;
/**
* The URL of an ENSRainbow API endpoint.
*/
endpointUrl: URL;
/**
* Optional client label set preferences that the ENSRainbow server at endpointUrl is expected to
* support. If provided, enables deterministic heal results across time, such that only
* labels from label sets with versions less than or equal to this value will be returned.
* Therefore, even if the ENSRainbow server later ingests label sets with greater versions
* than this value, the results returned across time can be deterministic. If
* provided, heal operations with this EnsRainbowApiClient will validate the ENSRainbow
* server manages a compatible label set. If not provided no specific labelSetId validation
* will be performed during heal operations.
* If `labelSetId` is provided without `labelSetVersion`, the server will use the latest
* available version.
* If `labelSetVersion` is defined, only labels from sets less than or equal to this value
* will be returned.
* When `labelSetVersion` is defined, `labelSetId` must also be defined.
*/
clientLabelSet?: EnsRainbowClientLabelSet;
}
/**
* ENSRainbow API client
*
* @example
* ```typescript
* // default options
* const client = new EnsRainbowApiClient();
* // custom options
* const client = new EnsRainbowApiClient({
* endpointUrl: new URL("https://api.ensrainbow.io"),
* });
* ```
*/
declare class EnsRainbowApiClient implements EnsRainbow.ApiClient {
private readonly options;
private readonly cache;
private readonly clientLabelSetSearchParams;
static readonly DEFAULT_CACHE_CAPACITY = 1000;
/**
* Create default client options.
*
* @returns default options
*/
static defaultOptions(): EnsRainbow.ApiClientOptions;
constructor(options?: Partial<EnsRainbow.ApiClientOptions>);
/**
* Attempt to [heal](https://ensnode.io/ensrainbow/concepts/glossary#heal) a labelHash to its original label.
*
* Note on returned labels: ENSRainbow returns labels exactly as they are
* represented in source rainbow table data. This means:
*
* - Labels may or may not be ENS-normalized
* - Labels can contain any valid string, including dots, null bytes, or be empty
* - Clients should handle all possible string values appropriately
*
* @param labelHash - A labelHash to heal, either as a strict `LabelHash`, an `EncodedLabelHash`
* (bracket-enclosed), or any string that can be normalized (missing `0x` prefix, uppercase hex
* chars, or 63-char hex are all accepted and normalized automatically).
* @returns a `HealResponse` indicating the result of the request and the healed label if successful.
* Returns a `HealBadRequestError` if the input cannot be normalized to a valid labelHash.
* @throws if the request fails due to network failures, DNS lookup failures, request timeouts,
* CORS violations, or Invalid URLs
* @example
* ```typescript
* const response = await client.heal(
* "0xaf2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc"
* );
*
* console.log(response);
*
* // Output:
* // {
* // status: "success",
* // label: "vitalik"
* // }
*
* const notFoundResponse = await client.heal(
* "0xf64dc17ae2e2b9b16dbcb8cb05f35a2e6080a5ff1dc53ac0bc48f0e79111f264"
* );
*
* console.log(notFoundResponse);
*
* // Output:
* // {
* // status: "error",
* // error: "Label not found",
* // errorCode: 404
* // }
* ```
*/
heal(labelHash: LabelHash | EncodedLabelHash | string): Promise<EnsRainbow.HealResponse>;
/**
* Get Count of Healable Labels
*
* @returns a `CountResponse` indicating the result and the timestamp of the request and the
* number of healable labels if successful
* @throws if the request fails due to network failures, DNS lookup failures, request timeouts,
* CORS violations, or Invalid URLs
* @example
*
* const response = await client.count();
*
* console.log(response);
*
* // {
* // "status": "success",
* // "count": 133856894,
* // "timestamp": "2024-01-30T11:18:56Z"
* // }
*
*/
count(): Promise<EnsRainbow.CountResponse>;
/**
*
* Simple verification that the service is running, either in your local setup or for the
* provided hosted instance.
* @returns a status of ENS Rainbow service
* @example
*
* const response = await client.health();
*
* console.log(response);
*
* // {
* // "status": "ok",
* // }
*/
health(): Promise<EnsRainbow.HealthResponse>;
/**
* Check whether the ENSRainbow service is ready (database is downloaded, validated, and open).
*
* Unlike {@link EnsRainbowApiClient.health}, which is a pure liveness probe that succeeds as soon
* as the HTTP server is accepting requests, `ready()` only resolves once the service has finished
* bootstrapping its database. Clients that require a usable database (e.g. ENSIndexer) should
* poll this method instead of `health()` during startup.
*
* @throws {EnsRainbowHttpError} if the service responds with a non-2xx status. The thrown
* error carries the HTTP `status` so callers can distinguish the retryable bootstrap case
* (`503 Service Unavailable`) from likely-non-retryable misconfiguration / server failures
* (e.g. `404`, `500`) and abort retries early in the latter cases.
* @throws Network/fetch errors (DNS, ECONNREFUSED, etc.) propagate as their original error
* type and should generally remain retryable, since they are common during cold start before
* the ENSRainbow HTTP server has bound its port.
*/
ready(): Promise<EnsRainbow.ReadyResponse>;
/**
* Get the public configuration of the ENSRainbow service.
*
* @throws {EnsRainbowHttpError} if the service responds with a non-2xx status.
*/
config(): Promise<EnsRainbow.ENSRainbowPublicConfig>;
/**
* Get a copy of the current client options.
*
* @returns a copy of the current client options.
*/
getOptions(): Readonly<EnsRainbowApiClientOptions>;
}
/**
* Determine if a heal response is an error.
*
* @param response the heal response to check
* @returns true if the response is an error, false otherwise
*/
declare const isHealError: (response: EnsRainbow.HealResponse) => response is EnsRainbow.HealError;
/**
* Determine if a heal response is cacheable.
*
* Server errors at not cachable and should be retried.
*
* @param response the heal response to check
* @returns true if the response is cacheable, false otherwise
*/
declare const isCacheableHealResponse: (response: EnsRainbow.HealResponse) => response is EnsRainbow.CacheableHealResponse;
export { EnsRainbow, EnsRainbowApiClient, type EnsRainbowApiClientOptions, EnsRainbowHttpError, isCacheableHealResponse, isHealError };