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; export type EmptyResponseData = void; export type EmptyMeta = void; // biome-ignore lint/suspicious/noExplicitAny: We can not use `unknown` type here export type AnyEndpoint = Endpoint; // biome-ignore lint/suspicious/noExplicitAny: We can not use `unknown` type here export type EmptyRequestEndpoint = Endpoint; // biome-ignore lint/suspicious/noExplicitAny: We can not use `unknown` type here export type EmptyResponseEndpoint = Endpoint; /** Shortcut for routes that have no params, query */ export const EmptyRequestCodec: RequestWithoutBodyCodec = { writeReq: () => ({}), parseReq: () => {}, schema: {}, }; export function JsonOnlyReq( req: Omit, keyof SszRequestMethods> ): RequestWithBodyCodec { return { ...req, writeReqSsz: () => { throw Error("Not implemented"); }, parseReqSsz: () => { throw Error("Not implemented"); }, onlySupport: WireFormat.json, }; } export const EmptyResponseDataCodec: ResponseDataCodec = { toJson: () => {}, fromJson: () => {}, serialize: () => new Uint8Array(), deserialize: () => {}, }; export const EmptyMetaCodec: ResponseMetadataCodec = { toJson: () => {}, fromJson: () => {}, toHeadersObject: () => ({}), fromHeaders: () => {}, }; export const EmptyResponseCodec: ResponseCodec = { data: EmptyResponseDataCodec, meta: EmptyMetaCodec, isEmpty: true, }; export function WithMeta(getType: (m: M) => Type): ResponseDataCodec { 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( getType: (v: ForkName) => Type ): ResponseDataCodec { 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( resp: Omit, "data"> & { data: Omit["data"], "serialize" | "deserialize">; } ): ResponseCodec { return { ...resp, data: { ...resp.data, serialize: () => { throw Error("Not implemented"); }, deserialize: () => { throw Error("Not implemented"); }, }, onlySupport: WireFormat.json, }; } export const JsonOnlyResponseCodec: ResponseCodec = { data: { toJson: (data: Record) => { // 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, "camel"); }, serialize: () => { throw Error("Not implemented"); }, deserialize: () => { throw Error("Not implemented"); }, }, meta: EmptyMetaCodec, onlySupport: WireFormat.json, };