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 🙏

© 2024 – Pkg Stats / Ryan Hefner

bplus-protobuf

v1.0.4

Published

A protocol buffer parser

Downloads

1

Readme

BPLUS-PROTOBUF

A Protocol Buffer 3 compliant parser written in TypeScript.

pipeline coverage

Note

This project is maintained over at GitLab: https://gitlab.com/adleatherwood/bplus-protobuf

Install

https://www.npmjs.com/package/bplus-protobuf

npm i bplus-protobuf

Read a single proto file

const content = fs.readFileSync("./samples/root/Entities.proto", { encoding: "utf8" })
const actual = Proto3.read(content)

if (!actual.proto)
    fail("proto not parsed")

expect(actual.success).toBe(true)
expect(actual.proto.messages[0].name).toBe("Person")

Read multiple proto and recursively resolve dependent types

The I/O is abstracted from the logic so that it can be run from any runtime. The ContentReader abstraction is what is needed by the extractor to read files.

export interface ContentReader {
    exists(filename: string): boolean
    read(filename: string): string
}

function createReader(): ContentReader {
    return {
        exists: (fn) => fs.existsSync(fn),
        read: (fn) => fs.readFileSync(fn, { encoding: "utf8" }),
    }
}

Now that we have a "reader" established, We can set up two protos to work with.

Entities.proto

syntax = "proto3";

package examples.entities;

import "Components.proto";

message Person {
    string id = 1;
    int Age = 2;
    examples.components.Name Name = 3;
    examples.components.Address Address = 4;
}

Components.proto

syntax = "proto3";

package examples.components;

message Name {
    string first = 1;
    string last = 2;
}

message Address {
    string line1 = 1;
    string line2 = 2;
    string street = 3;
    string city = 4;
    string zip = 5;
}

message Unrelated {
    string value1 = 1;
}

This will parse all types from Entities.proto and the walk through the imports list to resolve the field types from other proto files.

const reader = createReader()
const result = Extract.protos(reader, "./samples/root", ["Entities.proto"])
const actual = result.protos
    .reduce((r, p) => r.concat(p.messages), [] as MessageInfo[])
    .map(m => m.name)
    .sort()

// the "Unrelated" type is excluded because it isn't referenced by anything from "Entities.proto"
expect(actual).toEqual(["Address", "Name", "Person"])

You can also pass a list of things to include or exclude. These can be expressed as a string[] | RegExp[] | "all" | "none". Includes only work on the top-level protos passed into the second parameter. They are not applied recursively. Excludes work on all levels. This may seem a little inconsistent, but the premise is this:

  • Includes: "Only pull these things from these files & pull anything else they depend on"
  • Excludes: "Filter out these things from my end result"
// include "all", exclude "none"
const result = Extract.protos(reader, "./samples/root", ["Entities.proto"], "all", "none")
// implied include "all", exclude "none"
const result = Extract.protos(reader, "./samples/root", ["Entities.proto"])
// include only the "Person" type from the "Entities.proto"
const result = Extract.protos(reader, "./samples/root", ["Entities.proto"], ["examples.entities.Person"])
// include things like /Person/ from the "Entities.proto"
const result = Extract.protos(reader, "./samples/root", ["Entities.proto"], [/Person/])
// include "all" from "Entities.proto" and filter out the "Name" type
const result = Extract.protos(reader, "./samples/root", ["Entities.proto"], "all", ["examples.components.Name"])
// include "all" from "Entities.proto" and filter out types like /Name/
const result = Extract.protos(reader, "./samples/root", ["Entities.proto"], "all", [/Name/])

The output schema

The full schema of what is return from either a single proto read or an extraction.

export type ProtoInfo = {
    kind: "proto"
    syntax: string
    package: string
    imports: ImportInfo[]
    enums: EnumInfo[]
    messages: MessageInfo[]
    options: OptionInfo[]
}

export type SyntaxInfo = {
    kind: "syntax"
    value: string
}

export type PackageInfo = {
    kind: "package"
    value: string
}

export type ImportInfo = {
    kind: "import"
    isPublic: boolean
    isWeak: boolean
    value: string
}

export type OptionInfo = {
    kind: "option"
    name: string
    value: string
}

export type ValueInfo = {
    kind: "value"
    name: string
    value: number
    options: OptionInfo[]
}

export type EnumInfo = {
    kind: "enum"
    name: string
    values: ValueInfo[]
    options: OptionInfo[]
}

export type FieldInfo = {
    kind: "field"
    repeated: boolean
    type: string
    name: string
    index: number
    options: OptionInfo[]
}

export type RangeInfo = {
    kind: "range"
    from: number
    to: number | "max"
}

export type ReservedInfo = {
    kind: "reserved"
    ranges: RangeInfo[]
    names: string[]
}

export type OneofInfo = {
    kind: "oneof"
    name: string
    fields: FieldInfo[]
    options: OptionInfo[]
}

export type MapInfo = {
    kind: "map"
    name: string
    keyType: string
    type: string
    index: number
    options: OptionInfo[]
}

export type MessageInfo = {
    kind: "message"
    name: string
    reserved: ReservedInfo[]
    fields: FieldInfo[]
    oneofs: OneofInfo[]
    maps: MapInfo[]
    enums: EnumInfo[]
    messages: MessageInfo[]
    options: OptionInfo[]
}

export type RequestInfo = {
    kind: "request"
    stream: boolean
    type: string
}

export type ResponseInfo = {
    kind: "response"
    stream: boolean
    type: string
}

export type RpcInfo = {
    kind: "rpc"
    name: string
    request: RequestInfo,
    response: ResponseInfo,
    options: OptionInfo[]
}

export type ServiceInfo = {
    kind: "service"
    name: string
    rpcs: RpcInfo[]
}

export type ReadResult = {
    success: boolean
    message: string
    proto: ProtoInfo | undefined
}

export type ExtractResult = {
    protos: ProtoInfo[]
    found: string[] // this a sorted list of types that were found
}

Icons made by Freepik from www.flaticon.com