UNPKG

@lodestar/api

Version:

A Typescript REST client for the Ethereum Consensus API

134 lines (121 loc) 4.08 kB
import {Type} from "@chainsafe/ssz"; import {ForkName} from "@lodestar/params"; import {objectToExpectedCase} from "@lodestar/utils"; import { Endpoint, RequestWithBodyCodec, RequestWithoutBodyCodec, ResponseCodec, ResponseDataCodec, ResponseMetadataCodec, SszRequestMethods, } from "./types.js"; import {WireFormat} from "./wireFormat.js"; // Utility types / codecs export type EmptyArgs = void; export type EmptyRequest = Record<never, never>; export type EmptyResponseData = void; export type EmptyMeta = void; // biome-ignore lint/suspicious/noExplicitAny: We can not use `unknown` type here export type AnyEndpoint = Endpoint<any, any, any, any, any>; // biome-ignore lint/suspicious/noExplicitAny: We can not use `unknown` type here export type EmptyRequestEndpoint = Endpoint<any, EmptyArgs, EmptyRequest, any, any>; // biome-ignore lint/suspicious/noExplicitAny: We can not use `unknown` type here export type EmptyResponseEndpoint = Endpoint<any, any, any, EmptyResponseData, EmptyMeta>; /** Shortcut for routes that have no params, query */ export const EmptyRequestCodec: RequestWithoutBodyCodec<EmptyRequestEndpoint> = { writeReq: () => ({}), parseReq: () => {}, schema: {}, }; export function JsonOnlyReq<E extends Endpoint>( req: Omit<RequestWithBodyCodec<E>, keyof SszRequestMethods<E>> ): RequestWithBodyCodec<E> { return { ...req, writeReqSsz: () => { throw Error("Not implemented"); }, parseReqSsz: () => { throw Error("Not implemented"); }, onlySupport: WireFormat.json, }; } export const EmptyResponseDataCodec: ResponseDataCodec<EmptyResponseData, EmptyMeta> = { toJson: () => {}, fromJson: () => {}, serialize: () => new Uint8Array(), deserialize: () => {}, }; export const EmptyMetaCodec: ResponseMetadataCodec<EmptyMeta> = { toJson: () => {}, fromJson: () => {}, toHeadersObject: () => ({}), fromHeaders: () => {}, }; export const EmptyResponseCodec: ResponseCodec<EmptyResponseEndpoint> = { data: EmptyResponseDataCodec, meta: EmptyMetaCodec, isEmpty: true, }; export function WithMeta<T, M extends {version: ForkName}>(getType: (m: M) => Type<T>): ResponseDataCodec<T, M> { return { toJson: (data, meta: M) => getType(meta).toJson(data), fromJson: (data, meta: M) => getType(meta).fromJson(data), serialize: (data, meta: M) => getType(meta).serialize(data), deserialize: (data, meta: M) => getType(meta).deserialize(data), }; } export function WithVersion<T, M extends {version: ForkName}>( getType: (v: ForkName) => Type<T> ): ResponseDataCodec<T, M> { return { toJson: (data, meta: M) => getType(meta.version).toJson(data), fromJson: (data, meta: M) => getType(meta.version).fromJson(data), serialize: (data, meta: M) => getType(meta.version).serialize(data), deserialize: (data, meta: M) => getType(meta.version).deserialize(data), }; } export function JsonOnlyResp<E extends Endpoint>( resp: Omit<ResponseCodec<E>, "data"> & { data: Omit<ResponseCodec<E>["data"], "serialize" | "deserialize">; } ): ResponseCodec<E> { return { ...resp, data: { ...resp.data, serialize: () => { throw Error("Not implemented"); }, deserialize: () => { throw Error("Not implemented"); }, }, onlySupport: WireFormat.json, }; } export const JsonOnlyResponseCodec: ResponseCodec<AnyEndpoint> = { data: { toJson: (data: Record<string, unknown>) => { // JSON fields use snake case across all existing routes return objectToExpectedCase(data, "snake"); }, fromJson: (data) => { if (typeof data !== "object" || data === null) { throw Error("JSON must be of type object"); } // All JSON inside the JS code must be camel case return objectToExpectedCase(data as Record<string, unknown>, "camel"); }, serialize: () => { throw Error("Not implemented"); }, deserialize: () => { throw Error("Not implemented"); }, }, meta: EmptyMetaCodec, onlySupport: WireFormat.json, };