Package gojaprotobuf provides Protocol Buffers support for the Goja JavaScript runtime.


License
MIT
Install
go get github.com/joeycumines/goja-protobuf

Documentation

goja-protobuf

Go Reference

Protocol Buffers support for the goja JavaScript runtime. Create, manipulate, serialize, and deserialize protobuf messages from JavaScript running in Go.

Features

  • Full proto3 support: All scalar types, enums, nested messages, repeated fields, map fields, oneof groups
  • Binary serialization: Encode/decode protobuf wire format via Uint8Array
  • JSON serialization: Proto3 canonical JSON including well-known type formats (Timestamp, Duration, Any, etc.)
  • Dynamic descriptors: Load .proto definitions at runtime from serialized FileDescriptorSet or FileDescriptorProto
  • One runtime identity: Generated and dynamic messages, files, types, and extensions share one runtime-scoped registry graph
  • Type-safe wrappers: JavaScript message objects with get/set/has/clear methods
  • Lossless integers: 64-bit values use safe numbers or BigInt; setters also accept exact decimal strings
  • require() integration: Standard goja module loading via require('protobuf')

Installation

go get github.com/joeycumines/goja-protobuf

Quick Start

package main

import (
    "os"

    "github.com/joeycumines/goja"
    "github.com/joeycumines/goja_nodejs/require"
    gojaprotobuf "github.com/joeycumines/goja-protobuf"
)

func main() {
    registry := require.NewRegistry()
    registry.RegisterNativeModule("protobuf", gojaprotobuf.Require())

    rt := goja.New()
    registry.Enable(rt)

    // Load pre-compiled descriptor set
    descBytes, _ := os.ReadFile("myproto.pb")
    rt.Set("__descriptorBytes", rt.NewArrayBuffer(descBytes))

    rt.RunString(`
        const pb = require('protobuf');

        // Load proto definitions
        pb.loadDescriptorSet(__descriptorBytes);

        // Create and populate a message
        const MyMsg = pb.messageType('mypackage.MyMessage');
        const msg = new MyMsg();
        msg.set('name', 'hello');
        msg.set('count', 42);

        // Binary serialization
        const encoded = pb.encode(msg);
        const decoded = pb.decode(MyMsg, encoded);
        console.log(decoded.get('name')); // "hello"

        // JSON serialization
        const json = pb.toJSON(msg);
        console.log(JSON.stringify(json));
        const fromJson = pb.fromJSON(MyMsg, json);
    `)
}

JavaScript API

Descriptor Loading

const pb = require('protobuf');

// Load a serialized FileDescriptorSet (protoc --descriptor_set_out)
pb.loadDescriptorSet(uint8ArrayOrArrayBuffer);

// Load a single FileDescriptorProto
pb.loadFileDescriptorProto(uint8ArrayOrArrayBuffer);

Descriptor-set installation is an atomic, order-independent transaction. Reloading byte-equivalent files is idempotent; conflicting paths or symbols fail without changing the active registry snapshot.

Message Types

// Look up a message type by fully-qualified name
const MyMsg = pb.messageType('mypackage.MyMessage');
const msg = new MyMsg();

// Field access
msg.set('field_name', value);
msg.get('field_name');
msg.has('field_name');    // boolean
msg.clear('field_name');

// Oneof support
msg.whichOneof('oneof_name');  // returns field name or undefined
msg.clearOneof('oneof_name');

// Type information
msg.$type;  // fully-qualified type name

Enum Types

const Status = pb.enumType('mypackage.Status');
// Status is a frozen object: { UNKNOWN: 0, ACTIVE: 1, 0: "UNKNOWN", 1: "ACTIVE" }

Repeated Fields

const list = msg.get('items');
list.length;          // number of elements
list.get(0);          // get by index
list.set(0, value);   // set by index
list.add(value);      // append
list.clear();         // remove all
list.forEach((val, i) => { ... });

// Set from array
msg.set('items', [value1, value2]);

Map Fields

const map = msg.get('labels');
map.size;              // number of entries
map.get('key');        // lookup
map.set('key', value); // insert/update
map.has('key');        // boolean
map.delete('key');     // remove
map.forEach((value, key) => { ... });
map.entries();         // iterable iterator
Array.from(map);       // [key, value] pairs

// Set from object or Map
msg.set('labels', { key1: 'val1', key2: 'val2' });
msg.set('labels', new Map([['key1', 'val1']]));

Serialization

// Binary (wire format)
const bytes = pb.encode(msg);      // returns Uint8Array
const msg2 = pb.decode(MyMsg, bytes);

// JSON (proto3 canonical)
const json = pb.toJSON(msg);       // returns plain JS object
const msg3 = pb.fromJSON(MyMsg, json);

Type Conversions

Protobuf Type JavaScript Type
int32, sint32, sfixed32 number
int64, sint64, sfixed64 safe number or BigInt
uint32, fixed32 number
uint64, fixed64 safe number or BigInt
float, double number
bool boolean
string string
bytes Uint8Array
enum number (set accepts number or string name)
message wrapped message object
repeated array-like object
map Map-like object

Go API

For programmatic use from Go code, the Module type provides direct access:

mod, err := gojaprotobuf.New(runtime, gojaprotobuf.WithResolver(myTypes))
if err != nil {
    log.Fatal(err)
}

// Load descriptors from Go
names, err := mod.LoadDescriptorSetBytes(data)

// Wrap/unwrap messages for Go↔JS interop
jsObj, err := mod.WrapMessage(dynamicMsg)
goMsg, err := mod.UnwrapMessage(jsValue)

// Install the direct-Go API atomically.
err = mod.SetupExports(exports)

// Find descriptors
desc, err := mod.FindDescriptor("mypackage.MyMessage")

WrapMessage preserves the supplied generated or dynamic message instance and rejects messages whose descriptor is not the exact descriptor owned by this runtime. All module and JavaScript operations must run on the owning Goja goroutine. OwnsRuntime is the non-exposing ownership predicate for composed modules.

The first module created for a runtime snapshots the current membership of the configured type and file registries (or the global registries by default). Later caller registrations are intentionally invisible; use LoadDescriptorSetBytes for atomic additions to the runtime's live shared graph. Registry mutation must not race construction.

License

MIT