/* █████████ █████ ███░░░░░███ ░░███ ███ ░░░ ██████ ███████ ██████ ██████ ░███ ███░░███ ███░░███ ███░░███ ███░░███ ░███ ░███ ░███░███ ░███ ░███████ ░███ ░░░ ░░███ ███░███ ░███░███ ░███ ░███░░░ ░███ ███ ░░█████████ ░░██████ ░░████████░░██████ ░░██████ ░░░░░░░░░ ░░░░░░ ░░░░░░░░ ░░░░░░ ░░░░░░ */ /** Codec to encode and decode protobuf messages */ export type Codec = { encode(app: TApp): TProto; decode(proto: TProto): TApp; }; /* Helper to get the high-level type of a codec */ export type CodecType = ReturnType; /* Helper to get the protobuf type of a codec */ export type CodecProto = ReturnType; /* ██████ ██████ ░░██████ ██████ ░███░█████░███ ██████ █████ █████ ██████ ███████ ██████ ░███░░███ ░███ ███░░███ ███░░ ███░░ ░░░░░███ ███░░███ ███░░███ ░███ ░░░ ░███ ░███████ ░░█████ ░░█████ ███████ ░███ ░███░███████ ░███ ░███ ░███░░░ ░░░░███ ░░░░███ ███░░███ ░███ ░███░███░░░ █████ █████░░██████ ██████ ██████ ░░████████░░███████░░██████ ░░░░░ ░░░░░ ░░░░░░ ░░░░░░ ░░░░░░ ░░░░░░░░ ░░░░░███ ░░░░░░ ███ ░███ ░░██████ ░░░░░░ */ export type Evaluate = T extends infer O ? { [K in keyof O]: O[K] } : never; type TPropertyKey = string | symbol; // Message properties as codec. type TProperties = Record; // Optional properties in an object. // Properties are optional when they have the `undefined` type. type OptionalPropertyKeys = { [K in keyof T]: undefined extends T[K] ? K : never; }[keyof T]; // Properties that are not optional are required. type RequiredPropertyKeys = keyof Omit>; // Helper to get the app type of a message codec. type _MessageCodecType = { [K in keyof T]: CodecType; }; // Helper to get the protobuf type of a message codec. type _MessageCodecProto = { [K in keyof T]: CodecProto; }; // Adjust the app type of the codec so that optional properties are optional. type MessageCodecType = _MessageCodecType extends infer R ? Evaluate & Required>>> : never; // Adjust the protobuf type of the codec so that optional properties are optional. type MessageCodecProto = _MessageCodecProto extends infer R ? Evaluate & Required>>> : never; export type MessageCodec = Codec< MessageCodecType, MessageCodecProto >; export function MessageCodec( schema: T, ): MessageCodec { return { encode(app) { return new Proxy(app, { get(target, property) { if (!Object.hasOwn(target, property)) { return Reflect.get(target, property); } const v = Reflect.get(target, property); return schema[property].encode(v); }, }); }, decode(proto) { return new Proxy(proto, { get(target, property) { if (!Object.hasOwn(target, property)) { return Reflect.get(target, property); } const v = Reflect.get(target, property); return schema[property].decode(v); }, }); }, }; } /* █████████ ███░░░░░███ ░███ ░███ ████████ ████████ ██████ █████ ████ ░███████████ ░░███░░███░░███░░███ ░░░░░███ ░░███ ░███ ░███░░░░░███ ░███ ░░░ ░███ ░░░ ███████ ░███ ░███ ░███ ░███ ░███ ░███ ███░░███ ░███ ░███ █████ █████ █████ █████ ░░████████ ░░███████ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░ ░░░░░███ ███ ░███ ░░██████ ░░░░░░ */ export type ArrayCodec = T extends Codec< infer TApp, infer TProto > ? Codec : never; export function ArrayCodec, TApp, TProto>( t: T, ): ArrayCodec { return { encode(app) { return app.map(t.encode) as readonly TProto[]; }, decode(proto) { if (proto === undefined) return []; return proto.map(t.decode) as readonly TApp[]; }, } as ArrayCodec; } /* ██████ ██████ █████ █████ ████ █████████ █████ ░░██████ ██████ ░░███ ░░███ ░░███ ███░░░░░███ ░░███ ░███░█████░███ █████ ████ ███████ ██████ ░███████ ░███ ██████ ███ ░░░ ██████ ███████ ██████ ██████ ░███░░███ ░███ ░░███ ░███ ░░░███░ ░░░░░███ ░███░░███ ░███ ███░░███░███ ███░░███ ███░░███ ███░░███ ███░░███ ░███ ░░░ ░███ ░███ ░███ ░███ ███████ ░███ ░███ ░███ ░███████ ░███ ░███ ░███░███ ░███ ░███████ ░███ ░░░ ░███ ░███ ░███ ░███ ░███ ███ ███░░███ ░███ ░███ ░███ ░███░░░ ░░███ ███░███ ░███░███ ░███ ░███░░░ ░███ ███ █████ █████ ░░████████ ░░█████ ░░████████ ████████ █████░░██████ ░░█████████ ░░██████ ░░████████░░██████ ░░██████ ░░░░░ ░░░░░ ░░░░░░░░ ░░░░░ ░░░░░░░░ ░░░░░░░░ ░░░░░ ░░░░░░ ░░░░░░░░░ ░░░░░░ ░░░░░░░░ ░░░░░░ ░░░░░░ */ export type MutableArrayCodec< T extends Codec, TApp, TProto, > = T extends Codec ? Codec : never; export function MutableArrayCodec, TApp, TProto>( t: T, ): MutableArrayCodec { return { encode(app) { return app.map(t.encode) as TProto[]; }, decode(proto) { if (proto === undefined) return []; return proto.map(t.decode) as TApp[]; }, } as MutableArrayCodec; } /* ███████ █████ ███ ████ ███░░░░░███ ░░███ ░░░ ░░███ ███ ░░███ ████████ ███████ ████ ██████ ████████ ██████ ░███ ░███ ░███░░███░░███░░░███░ ░░███ ███░░███░░███░░███ ░░░░░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ███████ ░███ ░░███ ███ ░███ ░███ ░███ ███ ░███ ░███ ░███ ░███ ░███ ███░░███ ░███ ░░░███████░ ░███████ ░░█████ █████░░██████ ████ █████░░████████ █████ ░░░░░░░ ░███░░░ ░░░░░ ░░░░░ ░░░░░░ ░░░░ ░░░░░ ░░░░░░░░ ░░░░░ ░███ █████ ░░░░░ */ export type OptionalCodec = T extends Codec< infer TApp, infer TProto > ? Codec : never; export function OptionalCodec(t: T): OptionalCodec { return { encode(app) { if (app === undefined) return undefined; return t.encode(app); }, decode(proto) { if (proto === undefined) return undefined; return t.decode(proto); }, } as OptionalCodec; } /* ███████████ ███ █████ ░░███░░░░░███ ░░░ ░░███ ░███ ░███ ██████ ████████ █████ ████ ████ ████████ ██████ ███████ ░██████████ ███░░███ ███░░███ ░░███ ░███ ░░███ ░░███░░███ ███░░███ ███░░███ ░███░░░░░███ ░███████ ░███ ░███ ░███ ░███ ░███ ░███ ░░░ ░███████ ░███ ░███ ░███ ░███ ░███░░░ ░███ ░███ ░███ ░███ ░███ ░███ ░███░░░ ░███ ░███ █████ █████░░██████ ░░███████ ░░████████ █████ █████ ░░██████ ░░████████ ░░░░░ ░░░░░ ░░░░░░ ░░░░░███ ░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░ ░░░░░░░░ ░███ █████ ░░░░░ */ export type RequiredCodec = T extends Codec< infer TApp, infer TProto > ? TApp extends undefined ? never : Codec : never; export function RequiredCodec(t: T): RequiredCodec { return { encode(app) { if (app === undefined) throw new Error("Value is required but undefined"); return t.encode(app); }, decode(proto) { if (proto === undefined) throw new Error("Value is required but undefined"); return t.decode(proto); }, } as RequiredCodec; } /* ██████ █████ ████ ████ ███████ ░░██████ ░░███ ░░███ ░░███ ███░░░░░███ ░███░███ ░███ █████ ████ ░███ ░███ ███ ░░███ ████████ ░███░░███░███ ░░███ ░███ ░███ ░███ ░███ ░███░░███░░███ ░███ ░░██████ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░░░ ░███ ░░█████ ░███ ░███ ░███ ░███ ░░███ ███ ░███ █████ ░░█████ ░░████████ █████ █████ ░░░███████░ █████ ░░░░░ ░░░░░ ░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░ ░░░░░ */ export type NullOrCodec = T extends Codec< infer TApp, infer TProto > ? Codec : never; export function NullOrCodec(t: T): NullOrCodec { return { encode(app) { if (app === null) return null; return t.encode(app); }, decode(proto) { if (proto === null) return null; return t.decode(proto); }, } as NullOrCodec; } /* ███████████ ███ █████ █████ ░░███░░░░░███ ░░░ ░░███ ░░███ ░███ ░███ ████ ███████ ░███ ████████ ███████ ░██████████ ░░███ ███░░███ ░███ ░░███░░███ ░░░███░ ░███░░░░░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ███ ███████████ █████░░███████ █████ ████ █████ ░░█████ ░░░░░░░░░░░ ░░░░░ ░░░░░███░░░░░ ░░░░ ░░░░░ ░░░░░ ███ ░███ ░░██████ ░░░░░░ */ export type BigIntCodec = CodecType; export const BigIntCodec: Codec = { encode(app) { return app; }, decode(proto) { return proto; }, }; /* ██████ █████ █████ ░░██████ ░░███ ░░███ ░███░███ ░███ █████ ████ █████████████ ░███████ ██████ ████████ ░███░░███░███ ░░███ ░███ ░░███░░███░░███ ░███░░███ ███░░███░░███░░███ ░███ ░░██████ ░███ ░███ ░███ ░███ ░███ ░███ ░███░███████ ░███ ░░░ ░███ ░░█████ ░███ ░███ ░███ ░███ ░███ ░███ ░███░███░░░ ░███ █████ ░░█████ ░░████████ █████░███ █████ ████████ ░░██████ █████ ░░░░░ ░░░░░ ░░░░░░░░ ░░░░░ ░░░ ░░░░░ ░░░░░░░░ ░░░░░░ ░░░░░ */ export type NumberCodec = CodecType; export const NumberCodec: Codec = { encode(app) { return app; }, decode(proto) { return proto; }, }; /* █████ █████ ███ █████ ████████ █████████ ░░███ ░░███ ░░░ ░░███ ███░░░░███ ███░░░░░███ ░███ ░███ ████ ████████ ███████ ░███ ░███ ░███ ░███ ████████ ████████ ██████ █████ ████ ░███ ░███ ░░███ ░░███░░███ ░░░███░ ░░████████ ░███████████ ░░███░░███░░███░░███ ░░░░░███ ░░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ███░░░░███ ░███░░░░░███ ░███ ░░░ ░███ ░░░ ███████ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ███░███ ░███ ░███ ░███ ░███ ░███ ███░░███ ░███ ░███ ░░████████ █████ ████ █████ ░░█████ ░░████████ █████ █████ █████ █████ ░░████████ ░░███████ ░░░░░░░░ ░░░░░ ░░░░ ░░░░░ ░░░░░ ░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░ ░░░░░███ ███ ░███ ░░██████ ░░░░░░ */ export type Uint8ArrayCodec = CodecType; export const Uint8ArrayCodec: Codec = { encode(app) { return app; }, decode(proto) { return proto; }, }; /* ██████████ █████ ░░███░░░░███ ░░███ ░███ ░░███ ██████ ███████ ██████ ░███ ░███ ░░░░░███ ░░░███░ ███░░███ ░███ ░███ ███████ ░███ ░███████ ░███ ███ ███░░███ ░███ ███░███░░░ ██████████ ░░████████ ░░█████ ░░██████ ░░░░░░░░░░ ░░░░░░░░ ░░░░░ ░░░░░░ */ export type DateCodec = CodecType; export const DateCodec: Codec = { encode(app) { return new Date(app); }, decode(proto) { return new Date(proto); }, }; /* ███████████ ████ ░░███░░░░░███ ░░███ ░███ ░███ ██████ ██████ ░███ ██████ ██████ ████████ ░██████████ ███░░███ ███░░███ ░███ ███░░███ ░░░░░███ ░░███░░███ ░███░░░░░███░███ ░███░███ ░███ ░███ ░███████ ███████ ░███ ░███ ░███ ░███░███ ░███░███ ░███ ░███ ░███░░░ ███░░███ ░███ ░███ ███████████ ░░██████ ░░██████ █████░░██████ ░░████████ ████ █████ ░░░░░░░░░░░ ░░░░░░ ░░░░░░ ░░░░░ ░░░░░░ ░░░░░░░░ ░░░░ ░░░░░ */ export type BooleanCodec = CodecType; export const BooleanCodec: Codec = { encode(app) { return app; }, decode(proto) { return proto; }, }; /* █████████ █████ ███ ███░░░░░███ ░░███ ░░░ ░███ ░░░ ███████ ████████ ████ ████████ ███████ ░░█████████ ░░░███░ ░░███░░███░░███ ░░███░░███ ███░░███ ░░░░░░░░███ ░███ ░███ ░░░ ░███ ░███ ░███ ░███ ░███ ███ ░███ ░███ ███ ░███ ░███ ░███ ░███ ░███ ░███ ░░█████████ ░░█████ █████ █████ ████ █████░░███████ ░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░ ░░░░░ ░░░░░███ ███ ░███ ░░██████ ░░░░░░ */ export type StringCodec = CodecType; export const StringCodec: Codec = { encode(app) { return app; }, decode(proto) { return proto; }, }; /* █████ █████ █████ ██████ ███ █████ ░░███ ░░███ ░░███ ███░░███ ░░░ ░░███ ░███ ░███ ████████ ███████ ██████ ░███ ░░░ ████ ████████ ██████ ███████ ░███ ░███ ░░███░░███ ███░░███ ███░░███ ███████ ░░███ ░░███░░███ ███░░███ ███░░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███████ ░░░███░ ░███ ░███ ░███ ░███████ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███░░░ ░███ ░███ ░███ ░███ ░███░░░ ░███ ░███ ░░████████ ████ █████░░████████░░██████ █████ █████ ████ █████░░██████ ░░████████ ░░░░░░░░ ░░░░ ░░░░░ ░░░░░░░░ ░░░░░░ ░░░░░ ░░░░░ ░░░░ ░░░░░ ░░░░░░ ░░░░░░░░ */ export type UndefinedCodec = CodecType; export const UndefinedCodec: Codec = { encode(app) { return undefined; }, decode(proto) { return undefined; }, }; /* █████ ███ █████ ████ ░░███ ░░░ ░░███ ░░███ ░███ ████ ███████ ██████ ████████ ██████ ░███ ░███ ░░███ ░░░███░ ███░░███░░███░░███ ░░░░░███ ░███ ░███ ░███ ░███ ░███████ ░███ ░░░ ███████ ░███ ░███ █ ░███ ░███ ███░███░░░ ░███ ███░░███ ░███ ███████████ █████ ░░█████ ░░██████ █████ ░░████████ █████ ░░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░ ░░░░░ ░░░░░░░░ ░░░░░ */ type Literal = string | number | boolean | null | undefined; export type LiteralCodec = T extends Codec< infer TApp, infer TProto > ? Codec : never; export const LiteralCodec = ( value: L, ): LiteralCodec, L> => { return { encode(app) { if (app !== value) { throw new Error(`Expected ${String(value)}, got ${String(app)}`); } return app; }, decode(proto) { if (proto !== value) { throw new Error(`Expected ${String(value)}, got ${String(proto)}`); } return proto; }, } as LiteralCodec, L>; }; /* █████ ███ █████ ████ █████ █████ ███ ░░███ ░░░ ░░███ ░░███ ░░███ ░░███ ░░░ ░███ ████ ███████ ██████ ████████ ██████ ░███ ░███ ░███ ████████ ████ ██████ ████████ ░███ ░░███ ░░░███░ ███░░███░░███░░███ ░░░░░███ ░███ ░███ ░███ ░░███░░███ ░░███ ███░░███░░███░░███ ░███ ░███ ░███ ░███████ ░███ ░░░ ███████ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ █ ░███ ░███ ███░███░░░ ░███ ███░░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ███████████ █████ ░░█████ ░░██████ █████ ░░████████ █████ ░░████████ ████ █████ █████░░██████ ████ █████ ░░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░ ░░░░░ ░░░░░░░░ ░░░░░ ░░░░░░░░ ░░░░ ░░░░░ ░░░░░ ░░░░░░ ░░░░ ░░░░░ */ export type LiteralUnionCodec< T extends Codec, L extends readonly Literal[], > = T extends Codec ? Codec : never; export const LiteralUnionCodec = ( values: L, ): LiteralUnionCodec, L> => { return { encode(app) { if (!values.includes(app as L[number])) { throw new Error( `Expected one of [${values.join(", ")}], got ${String(app)}`, ); } return app; }, decode(proto) { if (!values.includes(proto as L[number])) { throw new Error( `Expected one of [${values.join(", ")}], got ${String(proto)}`, ); } return proto; }, } as LiteralUnionCodec, L>; }; /* █████ █████ ███ █████ ░░███ ░░███ ░░░ ░░███ ░███ ░███ ██████ ████████ ████ ██████ ████████ ███████ ░███ ░███ ░░░░░███ ░░███░░███░░███ ░░░░░███ ░░███░░███ ░░░███░ ░░███ ███ ███████ ░███ ░░░ ░███ ███████ ░███ ░███ ░███ ░░░█████░ ███░░███ ░███ ░███ ███░░███ ░███ ░███ ░███ ███ ░░███ ░░████████ █████ █████░░████████ ████ █████ ░░█████ ░░░ ░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░ ░░░░ ░░░░░ ░░░░░ */ // Maps variant keys to their corresponding decoded types, adding a tag field // For example: { _tag: "declareV1", declareV1: { data: string } } // if the variant is undefined type, it will be just the tag - { _tag: "heartbeat" } type AppVariantMap = { [K in keyof TVariants]: { [P in TTag]: K; } & (CodecType extends UndefinedCodec ? // biome-ignore lint/complexity/noBannedTypes: had to return empty object to satisfy type {} : { [P in K & TPropertyKey]: CodecType }); }; type VariantCodecType< TTag extends TPropertyKey, TVariants extends TProperties, > = AppVariantMap[keyof TVariants]; // Maps variant keys to their corresponding encoded types, adding a discriminator field // For example: { $case: "declareV1", declareV1: { data: string } } type ProtoVariantMap< TDiscriminator extends TPropertyKey, TVariants extends TProperties, > = { [K in keyof TVariants]: { [P in TDiscriminator]: K; } & { [P in K & TPropertyKey]: CodecProto; }; }; type VariantCodecProto< TDiscriminator extends TPropertyKey, TVariants extends TProperties, > = ProtoVariantMap[keyof TVariants]; // Type helper for VariantCodec that preserves the input/output types export type VariantCodec< T extends Codec, TTag extends TPropertyKey, TDiscriminator extends TPropertyKey, > = T extends Codec ? Codec : never; export const VariantCodec = < TTag extends TPropertyKey, TDiscriminator extends TPropertyKey, TVariants extends TProperties, TCodec extends Codec< VariantCodecType, VariantCodecProto >, >(options: { tag: TTag; discriminator: TDiscriminator; variants: TVariants; }): VariantCodec => { return { encode(app) { const tag = app[options.tag]; const codec = options.variants[tag]; if (!codec) { throw new Error(`Unknown variant: ${String(tag)}`); } const variantData = app[tag as keyof typeof app]; const encodedData = codec.encode(variantData); return { [options.discriminator]: tag, [tag]: encodedData, }; }, decode(proto) { const tag = proto[options.discriminator]; const codec = options.variants[tag]; if (!codec) { throw new Error(`Unknown variant: ${String(tag)}`); } const variantData = proto[tag as keyof typeof proto]; const decodedData = codec.decode(variantData); return { [options.tag]: tag, [tag]: decodedData, }; }, } as VariantCodec; }; /* ███████ ███████ ██████ ███░░░░░███ ███░░░░░███ ███░░███ ███ ░░███ ████████ ██████ ███ ░░███ ░███ ░░░ ░███ ░███░░███░░███ ███░░███░███ ░███ ███████ ░███ ░███ ░███ ░███ ░███████ ░███ ░███░░░███░ ░░███ ███ ░███ ░███ ░███░░░ ░░███ ███ ░███ ░░░███████░ ████ █████░░██████ ░░░███████░ █████ ░░░░░░░ ░░░░ ░░░░░ ░░░░░░ ░░░░░░░ ░░░░░ */ export type OneOfCodec = VariantCodec< Codec< VariantCodecType<"_tag", TVariants>, VariantCodecProto<"$case", TVariants> >, "_tag", "$case" >; export function OneOfCodec( variants: TVariants, ): OneOfCodec { return VariantCodec({ tag: "_tag", discriminator: "$case", variants, }); }