protobuf
js/format/protobuf.ts
fino:format/protobuf - TypeScript-defined Protocol Buffers messages.
This module implements the Protocol Buffers binary wire format directly. A message schema is an ordinary TypeScript object whose keys are checked against the application's TypeScript type and whose field descriptors carry the stable numeric indexes written on the wire. Encoded messages therefore contain field numbers and values, never JavaScript property-name strings.
The schema object is the intermediate representation: a future TypeScript
AST extractor or .proto parser can generate the same MessageSchema<T>
without changing the encoder, decoder, or callers.
Supported wire behavior:
- VARINT: bool, enum, signed/unsigned integers, and ZigZag integers.
- I32/I64: fixed integers and IEEE-754 float/double values.
- LEN: UTF-8 strings, bytes, nested messages, and packed repeated scalars.
- Unknown fields are skipped, singular scalar fields use last-one-wins, and repeated numeric fields accept both packed and expanded representations.
- Groups and maps have no dedicated schema sugar. Unknown groups are skipped, and maps can be represented by their standard repeated entry message.
import { defineMessage } from 'fino:format/protobuf';
interface Metric {
name: string;
values: number[];
}
const Metric = defineMessage<Metric>({
name: { number: 1, type: 'string' },
values: { number: 2, type: 'double', repeated: true },
});
const bytes = Metric.encode({ name: 'latency', values: [1.5, 2.25] });
Metric.decode(bytes);
Conformance map:
- tags and wire types:
_wireType,Writer.tag, andReader.tag - base-128 varints and ZigZag:
Writer.varint,Reader.varint,_encodeScalar, and_decodeScalar - length-delimited strings, bytes, and submessages:
_encodeValueand_decodeValue - packed/expanded repeated fields and last-one-wins:
_decodeMessage - unknown-field skipping, including deprecated groups:
Reader.skip
Authoritative references:
- Protocol Buffers encoding: https://protobuf.dev/programming-guides/encoding/
- Protocol Buffers language specification: https://protobuf.dev/reference/protobuf/proto3-spec/
Types
type ScalarType = NumberType | BigIntType | 'bool' | 'string' | 'bytes'
Scalar field names understood by the Protocol Buffers wire codec.
The 64-bit integer forms use bigint; all other numeric forms use
number. Enum values are their numeric wire values.
type FieldSchema<T> =
Present<T> extends readonly (infer Item)[]
? { number: number; type: SchemaType<Item>; repeated: true; packed?: boolean; optional?: false }
: {
number: number;
type: SchemaType<Present<T>>;
repeated?: false;
packed?: never;
optional?: boolean;
}
One named TypeScript property and its stable Protocol Buffers field number.
Repeated properties must set repeated: true. Packable repeated scalar
fields are packed by default; set packed: false to emit expanded records.
Set optional: true to preserve absence instead of materializing the scalar
type's protobuf default during decoding.
type MessageSchema<T extends object> = { [Key in keyof T]-?: FieldSchema<T[Key]> }
Runtime schema for a TypeScript message type.
Every property in T has one descriptor, so renaming or adding a property
requires updating the shared schema at compile time. Field numbers are the
stable compatibility boundary and must not be reused after publication.
Interfaces
interface MessageCodec<T extends object> {
Compiled encoder and decoder for one TypeScript message type.
Readonly Properties
readonly schema: Readonly<MessageSchema<T>>
The checked TypeScript schema used to build this codec.
Methods
encode(value: T): Uint8Array
Encode a value as a protobuf binary message.
decode(bytes: Uint8Array | ArrayBuffer): T
Decode a protobuf binary message.
Unknown fields are ignored. Missing required-looking scalar properties receive protobuf defaults because wire format presence alone cannot enforce application-level requiredness.
Classes
class ProtobufError extends Error {
Error thrown for an invalid schema, unsupported value, or malformed wire message.
Properties
override name
Error name reported by ProtobufError instances.
Readonly Properties
readonly offset?: number
Byte offset associated with malformed input, when available.
Constructors
constructor(message: string, offset?: number)
Functions
function defineMessage<T extends object>(schema: MessageSchema<T>): MessageCodec<T>
Compile a TypeScript-keyed schema into a protobuf encoder and decoder.
The schema is validated once and fields are encoded in ascending numeric order for predictable local output. Protocol Buffers does not define canonical serialization, so callers must not use the resulting bytes as a cross-implementation content hash.
interface Ready {
owner: number;
descriptors: number[];
}
const Ready = defineMessage<Ready>({
owner: { number: 1, type: 'uint32' },
descriptors: { number: 2, type: 'int32', repeated: true },
});