npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@protobuf-x/codegen

v0.1.4

Published

Code generator CLI + programmatic API for protobuf-x. Generates fast, type-safe TypeScript/JavaScript from .proto files.

Readme

@protobuf-x/codegen

Code generator CLI + programmatic API for protobuf-x. Generates fast, type-safe TypeScript/JavaScript from .proto files.

  • 🚀 Generates the fastest protobuf code in JS — wins 9-10/10 benchmarks vs protobufjs
  • 📦 Smaller output — defaults are tuned for size; --minimal strips JSON, create, and typeurl for smallest binary-only output
  • 🔒 Type-safe — full TypeScript types with deep nested dot notation (User.Profile.Theme.DARK)
  • 🛠 Programmatic API — use from build scripts or bundler plugins, no CLI required
  • Zero runtime deps — depends only on @protobuf-x/parser

Installation

npm install -D @protobuf-x/codegen
npm install @protobuf-x/runtime

CLI

npx protobuf-x [options] <file.proto ...>
# or use the short alias:
npx pbx [options] <file.proto ...>

| Option | Description | | -------------------------- | ----------------------------------------------------------------- | | -o, --out <dir> | Output directory (required) | | -t, --target <type> | ts (default), js, or both | | --import-path <path> | Add a directory to the proto import search path (repeatable) | | --runtime-package <name> | Override runtime package (default @protobuf-x/runtime) | | --no-json | Skip toJSON/fromJSON + JSON interfaces | | --no-create | Skip Message.create() factory (use new Message() instead) | | --no-typeurl | Skip getTypeUrl helper | | --minimal | Enable all --no-* flags (smallest binary-only output) | | --int64-as <repr> | 64-bit int representation: bigint (default), number, string | | -h, --help | Show help | | -v, --version | Show version |

Target reference

| Target | Files generated | | -------------- | ---------------------------------------------------------------------------- | | ts (default) | schema_pb.ts — TypeScript source (classes + types + methods) | | js | schema_pb.js + schema_pb.d.ts — runnable JS + separate type declarations | | both | All three: .ts, .js, .d.ts |

Use ts if your project compiles TypeScript (Vite, tsx, esbuild, swc, ts-node). Use js to ship as a regular npm library.

Examples

# Generate TypeScript (default)
protobuf-x --out gen schema.proto

# Generate JavaScript + .d.ts files
protobuf-x --target js --out gen schema.proto

# Minimal mode: skip create(), getTypeUrl(), and JSON methods
protobuf-x --minimal --out gen schema.proto

# Use the minimal runtime (auto-enables --minimal)
protobuf-x --runtime-package @protobuf-x/runtime/minimal --out gen schema.proto

# Compile a tree of files with import paths
protobuf-x --import-path ./schemas --import-path ./vendor --out gen schemas/main.proto

# Use plain `number` for int64 fields (protobufjs-style, loses precision above 2^53)
protobuf-x --int64-as number --out gen schema.proto

Programmatic API

For build scripts, bundler plugins, or anywhere you'd rather not shell out to a CLI.

import { generate, generateToDisk } from '@protobuf-x/codegen'

// Generate in-memory — returns { files, errors }
const result = await generate({
    files: ['./schema.proto'],
    target: 'ts',
    runtimePackage: '@protobuf-x/runtime',
    noJson: false
})

for (const file of result.files) {
    console.log(file.path, file.content.length)
}

// Or write directly to disk
await generateToDisk({
    files: ['./schema.proto'],
    target: 'both',
    outDir: './gen'
})

// Generate from an in-memory source string (no disk needed)
const inMem = await generate({
    sources: [
        {
            name: 'user.proto',
            content: 'syntax = "proto3"; message User { string name = 1; }'
        }
    ],
    target: 'ts'
})
console.log(inMem.files[0].content)

GenerateOptions

interface GenerateOptions {
    files?: string[] // .proto file paths
    sources?: ProtoSource[] // in-memory { name, content } pairs
    target?: 'ts' | 'js' | 'both' // default 'ts'
    runtimePackage?: string // default '@protobuf-x/runtime'
    noJson?: boolean // default false; auto-true for /minimal runtime
    noCreate?: boolean // default false; skip Message.create() factory
    noTypeurl?: boolean // default false; skip getTypeUrl helper
    minimal?: boolean // default false; enables all --no-* flags
    int64As?: 'bigint' | 'number' | 'string' // default 'bigint'
    importPaths?: string[] // additional proto import search paths
    outDir?: string // joined with relative output paths
    parser?: ParserModuleLike // override the parser module
}

GenerateResult

interface GenerateResult {
    files: GeneratedFile[] // { path: string, content: string }[]
    errors: GenerateError[] // { file: string, message: string }[] — collected, never thrown
}

The function does not throw on a single bad file — it collects per-file errors and still returns whatever generated successfully. It only throws if you pass no files or sources at all.

Bundler plugin example (Vite)

import { generate } from '@protobuf-x/codegen'

export function protobufXPlugin() {
    return {
        name: 'protobuf-x',
        async load(id: string) {
            if (!id.endsWith('.proto')) return null
            const result = await generate({ files: [id], target: 'ts' })
            return result.files[0]?.content ?? null
        }
    }
}

Custom build script example

import { generateToDisk } from '@protobuf-x/codegen'
import { glob } from 'glob'

const protoFiles = await glob('./schemas/**/*.proto')

const result = await generateToDisk({
    files: protoFiles,
    target: 'ts',
    outDir: './src/gen',
    importPaths: ['./schemas']
})

if (result.errors.length > 0) {
    for (const err of result.errors) {
        console.error(`${err.file}: ${err.message}`)
    }
    process.exit(1)
}
console.log(`Generated ${result.files.length} files`)

Generated code highlights

Type-safe dot notation for nested types

message User {
  enum Role { GUEST = 0; ADMIN = 1; }
  message Profile { string bio = 1; }
}
import { User } from './gen/user_pb.js'

// Both work — class+namespace merge means User.Role is also a value
const role: User.Role = User.Role.ADMIN
const profile = new User.Profile({ bio: 'hi' })
console.log(profile instanceof User.Profile) // true

Real classes with instanceof + Partial constructors

const user = new User({ name: 'Alice', age: 30 }) // type-checked Partial<User>
console.log(user instanceof User) // true
const bytes = user.toBinary() // Uint8Array
const decoded = User.decode(bytes) // typed

64-bit integer representation (--int64-as)

By default, int64/uint64/sint64/fixed64/sfixed64 fields use native JavaScript bigint — full precision, no Long wrapper, fastest path on the wire.

// proto: int64 timestamp = 1;
const evt = new Event({ timestamp: 1735689600000n }) // bigint

Three modes are supported:

| Flag | Class field type | Tradeoff | | ------------------- | ---------------- | ------------------------------------------------------------------- | | --int64-as bigint | bigint | Default. Full precision, fastest, no conversion overhead | | --int64-as number | number | Drop-in for protobufjs numeric users; loses precision above 2^53 | | --int64-as string | string | Safe for JSON interop, no precision loss, slowest (BigInt wrapping) |

Both number and string modes wrap with BigInt(...) internally on encode and unwrap with Number(...) / String(...) on decode, so the storage type matches what your code expects without any Long shim.

protobuf-x --int64-as number --out gen schema.proto
// With --int64-as number:
const evt = new Event({ timestamp: 1735689600000 }) // plain number

POJO input interface (IFoo)

For every generated class Foo, a POJO-shaped interface IFoo is also emitted (zero runtime cost — interfaces are erased at compile time). This mirrors the protobufjs static-module shape, where every field is optional and nullable, and nested message references use the I-prefixed peer.

// Generated:
export interface IUser {
    id?: string | null
    name?: string | null
    profile?: IUser_Profile | null // ← I-prefixed nested type
}
export class User extends Message<User> implements IUser {
    /* ... */
}

// Use IUser anywhere you want POJO input typing without forcing a class
// instance — useful for API request bodies, form state, etc.
function saveUser(input: IUser) {
    return User.encode(new User(input)).finish()
}

The class Foo always satisfies IFoo, so plain objects matching the interface can be passed straight to the constructor.

proto3 implicit-presence warning

Generated .ts files include a header listing every proto3 scalar field that uses implicit presence (i.e. int32 count = 1; rather than optional int32 count = 1;). These decode to their zero value (0, "", false, empty bytes) when missing on the wire — not undefined.

This is a common footgun when migrating from protobufjs, where code like if (msg.count) { ... } silently misbehaves when count is legitimately zero. To distinguish "absent" from "default", mark the field optional in your .proto file. The warning is capped to 30 entries with an overflow line for larger schemas.

Performance of generated code

| Operation | protobuf-x | protobufjs | Ratio | | ------------- | --------------: | ---------: | ----: | | Encode small | 9.0M ops/s | 5.3M | 1.71x | | Encode medium | 1.7M ops/s | 1.2M | 1.43x | | Decode small | 10.6M ops/s | 8.8M | 1.21x | | Create small | 27M ops/s | 12M | 2.16x | | toJSON small | 150M ops/s | 41M | 3.68x | | Clone medium | 833K ops/s | ~500K | 1.65x |

Bundle size of generated code

For bench.proto (4 messages: Small/Address/Medium/Large):

| Library | Generated (min, brotli) | | ----------------------- | ----------------------: | | @protobuf-x/codegen | 2.6 KB | | protobufjs | 2.2 KB |

When combined with the runtime, total shipped (brotli) is significantly smaller:

| | runtime + gen | | ---------------------- | ------------: | | protobuf-x minimal | 6.0 KB | | protobufjs minimal | 8.4 KB | | protobuf-x full | 10.0 KB | | protobufjs full | 23.7 KB |

Compatibility

  • Wire format: 100% compatible with protoc, protobufjs, @bufbuild/protobuf, Google's reference implementations, and any other compliant protobuf library
  • Node.js: 18+
  • Cross-decode verified against protobufjs

License

MIT