CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-protobufjs

Protocol Buffers for JavaScript with TypeScript support, providing pure JavaScript implementation for serializing and deserializing structured data using Google's Protocol Buffer format.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

serialization.mddocs/

Message Serialization

High-performance binary serialization and deserialization with full type safety, validation, and conversion capabilities for protobuf messages.

Capabilities

Message Class

Base message class providing static methods for all serialization operations and instance methods for message manipulation.

class Message<T extends object = object> {
  /**
   * Message type reference
   */
  $type: Type;
  
  /**
   * Creates message instance from properties
   * @param properties - Message properties
   * @returns Message instance
   */
  static create<T extends Message<T>>(this: Constructor<T>, properties?: { [k: string]: any }): T;
  
  /**
   * Encodes message to writer
   * @param message - Message instance or plain object
   * @param writer - Writer to encode to, creates new if omitted
   * @returns Writer with encoded data
   */
  static encode<T extends Message<T>>(this: Constructor<T>, message: T | { [k: string]: any }, writer?: Writer): Writer;
  
  /**
   * Encodes message with length delimiter
   * @param message - Message instance or plain object
   * @param writer - Writer to encode to, creates new if omitted
   * @returns Writer with encoded data
   */
  static encodeDelimited<T extends Message<T>>(this: Constructor<T>, message: T | { [k: string]: any }, writer?: Writer): Writer;
  
  /**
   * Decodes message from reader or buffer
   * @param reader - Reader or buffer to decode from
   * @param length - Message length if known
   * @returns Decoded message instance
   */
  static decode<T extends Message<T>>(this: Constructor<T>, reader: Reader | Uint8Array, length?: number): T;
  
  /**
   * Decodes length-delimited message
   * @param reader - Reader or buffer to decode from
   * @returns Decoded message instance
   */
  static decodeDelimited<T extends Message<T>>(this: Constructor<T>, reader: Reader | Uint8Array): T;
  
  /**
   * Verifies message properties
   * @param message - Message properties to verify
   * @returns Error message or null if valid
   */
  static verify(message: { [k: string]: any }): string | null;
  
  /**
   * Creates message from plain object
   * @param object - Plain object to convert
   * @returns Message instance
   */
  static fromObject<T extends Message<T>>(this: Constructor<T>, object: { [k: string]: any }): T;
  
  /**
   * Converts message to plain object
   * @param message - Message instance to convert
   * @param options - Conversion options
   * @returns Plain object representation
   */
  static toObject<T extends Message<T>>(message: T, options?: IConversionOptions): { [k: string]: any };
  
  /**
   * Converts message to JSON representation
   * @returns JSON object
   */
  toJSON(): { [k: string]: any };
  
  /**
   * Checks if message equals another message
   * @param message - Message to compare with
   * @returns True if messages are equal
   */
  equals(message: Message): boolean;
}

Usage Examples:

const protobuf = require("protobufjs");

// Load schema and get message type
protobuf.load("schema.proto", function(err, root) {
    const User = root.lookupType("User");
    
    // Create message
    const user = User.create({
        id: 123,
        name: "John Doe",
        email: "john@example.com"
    });
    
    // Encode to binary
    const buffer = User.encode(user).finish();
    console.log("Encoded size:", buffer.length);
    
    // Decode from binary
    const decoded = User.decode(buffer);
    console.log("Decoded:", decoded);
    
    // Verify message structure
    const errorMsg = User.verify({
        id: "invalid",  // Should be number
        name: "Valid Name"
    });
    if (errorMsg) console.log("Validation error:", errorMsg);
});

Type-Based Operations

Operations available on Type instances for message manipulation.

class Type extends Namespace {
  /**
   * Creates message instance with type validation
   * @param properties - Message properties
   * @returns Message instance
   */
  create(properties?: { [k: string]: any }): Message;
  
  /**
   * Encodes message with type information
   * @param message - Message to encode
   * @param writer - Optional writer
   * @returns Writer with encoded data
   */
  encode(message: Message | { [k: string]: any }, writer?: Writer): Writer;
  
  /**
   * Encodes message with length delimiter
   * @param message - Message to encode
   * @param writer - Optional writer
   * @returns Writer with encoded data
   */
  encodeDelimited(message: Message | { [k: string]: any }, writer?: Writer): Writer;
  
  /**
   * Decodes message using type schema
   * @param reader - Reader or buffer
   * @param length - Optional message length
   * @returns Decoded message
   */
  decode(reader: Reader | Uint8Array, length?: number): Message;
  
  /**
   * Decodes length-delimited message
   * @param reader - Reader or buffer
   * @returns Decoded message
   */
  decodeDelimited(reader: Reader | Uint8Array): Message;
  
  /**
   * Verifies message against type schema
   * @param message - Message to verify
   * @returns Error message or null
   */
  verify(message: { [k: string]: any }): string | null;
  
  /**
   * Creates message from plain object with type conversion
   * @param object - Plain object
   * @returns Message instance
   */
  fromObject(object: { [k: string]: any }): Message;
  
  /**
   * Converts message to plain object with type information
   * @param message - Message to convert
   * @param options - Conversion options
   * @returns Plain object
   */
  toObject(message: Message, options?: IConversionOptions): { [k: string]: any };
}

Usage Examples:

// Using Type methods directly
const MessageType = root.lookupType("package.Message");

// Create and encode
const message = MessageType.create({ field1: "value", field2: 42 });
const encoded = MessageType.encode(message).finish();

// Decode and verify
const decoded = MessageType.decode(encoded);
const valid = MessageType.verify(decoded);
if (valid === null) {
    console.log("Message is valid");
}

// Object conversion
const plainObject = MessageType.toObject(message, {
    longs: String,
    enums: String,
    bytes: String
});

Conversion Options

Configuration options for controlling serialization behavior and data type representation.

interface IConversionOptions {
  /**
   * Long number representation
   * - String: Convert to string representation
   * - Number: Convert to JavaScript number (may lose precision)
   * - Long: Keep as Long object
   */
  longs?: typeof String | typeof Number | typeof Long;
  
  /**
   * Enum value representation
   * - String: Use enum names
   * - Number: Use enum numeric values
   */
  enums?: typeof String | typeof Number;
  
  /**
   * Byte array representation
   * - Array: Convert to number array
   * - String: Convert to base64 string
   * - Uint8Array: Keep as Uint8Array
   */
  bytes?: typeof Array | typeof String | typeof Uint8Array;
  
  /**
   * Whether to include default values
   */
  defaults?: boolean;
  
  /**
   * Whether to include empty arrays
   */
  arrays?: boolean;
  
  /**
   * Whether to include empty objects
   */
  objects?: boolean;
  
  /**
   * Whether to include oneof properties that aren't set
   */
  oneofs?: boolean;
}

Usage Examples:

const message = MessageType.create({
    id: Long.fromNumber(123456789012345),
    status: StatusEnum.values.ACTIVE,
    data: new Uint8Array([1, 2, 3, 4])
});

// Convert with different options
const jsonFriendly = MessageType.toObject(message, {
    longs: String,      // "123456789012345"
    enums: String,      // "ACTIVE"
    bytes: String,      // base64 encoded
    defaults: true      // include default values
});

const numeric = MessageType.toObject(message, {
    longs: Number,      // 123456789012345 (may lose precision)
    enums: Number,      // 1
    bytes: Array        // [1, 2, 3, 4]
});

Validation

Message validation capabilities for ensuring data integrity and type safety.

/**
 * Validates message properties against schema
 * @param message - Message properties to validate
 * @returns Error message string or null if valid
 */
function verify(message: { [k: string]: any }): string | null;

// Validation error types
interface ValidationErrors {
  missingField: string;        // "missing required field"
  invalidType: string;         // "invalid type for field"
  outOfRange: string;          // "value out of range"
  invalidEnum: string;         // "invalid enum value"
  invalidOneOf: string;        // "multiple oneof fields set"
}

Usage Examples:

// Validate before encoding
const messageData = {
    id: "not-a-number",  // Invalid type
    name: "",            // Empty string
    age: -5              // Invalid value
};

const error = MessageType.verify(messageData);
if (error) {
    console.log("Validation failed:", error);
    // Handle validation error
} else {
    // Safe to create and encode
    const message = MessageType.create(messageData);
    const encoded = MessageType.encode(message).finish();
}

// Validate after decoding
const decoded = MessageType.decode(someBuffer);
const valid = MessageType.verify(decoded);
if (valid !== null) {
    console.warn("Decoded message validation warning:", valid);
}

Binary Format Operations

Low-level binary format operations for custom serialization needs.

/**
 * Finishes writer operations and returns encoded buffer
 * @returns Encoded binary data
 */
Writer.prototype.finish(): Uint8Array;

/**
 * Gets current writer length
 * @returns Number of bytes written
 */
Writer.prototype.len: number;

/**
 * Creates reader from binary data
 * @param buffer - Binary data to read
 * @returns Reader instance
 */
Reader.create(buffer: Uint8Array): Reader;

/**
 * Current reader position
 */
Reader.prototype.pos: number;

/**
 * Total buffer length
 */
Reader.prototype.len: number;

Usage Examples:

// Custom encoding with length tracking
const writer = protobuf.Writer.create();
const message1 = Type1.encode(data1, writer);
const pos1 = writer.len;

const message2 = Type2.encode(data2, writer);
const pos2 = writer.len;

const buffer = writer.finish();
console.log(`Total size: ${buffer.length}, Message1: ${pos1}, Message2: ${pos2 - pos1}`);

// Custom decoding with position tracking
const reader = protobuf.Reader.create(buffer);
const decoded1 = Type1.decode(reader, pos1);
const decoded2 = Type2.decode(reader);
console.log(`Reader position: ${reader.pos}/${reader.len}`);

Error Handling

Common error conditions and handling patterns in serialization operations.

interface SerializationError extends Error {
  name: string;           // Error type
  message: string;        // Error description
  path?: string;          // Field path that caused error
  instance?: any;         // Problematic value
}

// Common error types
interface ErrorTypes {
  TypeError: "invalid type";
  RangeError: "value out of range";
  Error: "general serialization error";
}

Usage Examples:

try {
    // Encoding errors
    const message = MessageType.create({
        requiredField: null,  // Missing required field
        enumField: 999        // Invalid enum value
    });
    const encoded = MessageType.encode(message).finish();
} catch (err) {
    console.error("Encoding error:", err.message);
    if (err.path) console.error("Field path:", err.path);
}

try {
    // Decoding errors
    const corrupted = new Uint8Array([0xFF, 0xFF, 0xFF]);
    const decoded = MessageType.decode(corrupted);
} catch (err) {
    console.error("Decoding error:", err.message);
}

Types

interface Constructor<T> extends Function {
  new (...args: any[]): T;
}

interface IConversionOptions {
  longs?: typeof String | typeof Number | typeof Long;
  enums?: typeof String | typeof Number;
  bytes?: typeof Array | typeof String | typeof Uint8Array;
  defaults?: boolean;
  arrays?: boolean;
  objects?: boolean;
  oneofs?: boolean;
}

interface Long {
  low: number;
  high: number;
  unsigned: boolean;
  toNumber(): number;
  toString(): string;
  equals(other: Long): boolean;
  static fromNumber(value: number): Long;
  static fromString(value: string): Long;
}

Install with Tessl CLI

npx tessl i tessl/npm-protobufjs

docs

binary-io.md

cli-tools.md

code-generation.md

index.md

proto-loading.md

reflection.md

rpc-services.md

serialization.md

tile.json