Schema-driven CBOR code generator. Define types once in .cbg schema files, get type-safe encoders and decoders for TypeScript, Rust, and F#.
Generated code produces valid RFC 8949 CBOR — any generic CBOR decoder can read the output. Schema knowledge enables fixed-layout encoding and single-branch validation, so generated serializers are fast and predictable.
Schemas use a compact definition syntax in .cbg files:
/// A user profile
User = struct {
0 id: u64
1 name: string
2 email: ?string
3 role: Role
4 tags: []string
}
Role = enum {
0 Viewer
1 Editor
2 Admin
}
/// API response envelope
Response = union {
0 ok: User
1 notFound
2 error: string
}
| Category | Types |
|---|---|
| Boolean | bool |
| Integers | u8 u16 u32 u64 i8 i16 i32 i64 |
| Varints | uvarint ivarint (minimal CBOR encoding) |
| Floats | f16 f32 f64 |
| Text | string (UTF-8) |
| Optional | ?T |
| Arrays | []T (variable), [N]T (fixed), [.field]T (external length) — []u8 encodes as CBOR byte string |
| Composites | struct enum union |
| Imports | common = @import("common/types.cbg") then common.SomeType |
Field numbers in structs are stable wire identifiers — new fields get new numbers, old numbers are never reused. This gives forward and backward compatibility for free.
See SPEC.md for the full wire format specification.
Key properties:
- Structs encode as CBOR arrays indexed by field number
- Fixed-width integers always use their full width (constant wire size per field)
- Enums encode as unsigned varints
- Unions use CBOR tags for payload variants, plain integers for unit variants
- Optionals are sugar for
union { 0 none, 1 some: T }
npm install -D cboragen
npm install @cboragen/runtimecboragen is a dev dependency (code generation tooling). @cboragen/runtime is a runtime dependency (imported by generated code).
CLI with explicit paths:
npx cboragen generate schema.cbg -o src/schema.gen.tsConfig file (cboragen.config.ts):
import { defineConfig } from "cboragen";
export default defineConfig({
schemas: [
{ schema: "schemas/user.cbg", out: "src/gen/user.ts" },
{ schema: "schemas/api.cbg", out: "src/gen/api.ts", varintAsNumber: true },
],
});Then run:
npx cboragen generate
npx cboragen watch # regenerate on file changesGenerated code exports TypeScript types and encode/decode functions for each schema definition:
import type { User } from "./schema.gen.ts";
import { encodeUser, decodeUser } from "./schema.gen.ts";
const user: User = {
id: 1n,
name: "Alice",
email: "alice@example.com",
role: Role.Admin,
tags: ["staff"],
};
const bytes: Uint8Array = encodeUser(user);
const decoded: User = decodeUser(bytes);Bun:
// bunfig.toml or build script
import { cboragenPlugin } from "cboragen/bun";
Bun.build({
plugins: [cboragenPlugin()],
// ...
});Vite:
// vite.config.ts
import { cboragenPlugin } from "cboragen/vite";
export default {
plugins: [cboragenPlugin()],
};Both plugins transform .cbg imports directly — no separate generate step needed.
| Option | Effect |
|---|---|
varintAsNumber |
Map uvarint/ivarint to number instead of bigint |
The Rust code generator produces types with impl blocks for encoding and decoding, using the cboragen-runtime crate.
cboragen-rs schema.cbg > src/schema.rsGenerated types have encode, encode_with, decode, and decode_with methods:
use cboragen_runtime::{Writer, Reader};
let user = User {
id: 1,
name: "Alice".to_string(),
email: Some("alice@example.com".to_string()),
role: Role::Admin,
tags: vec!["staff".to_string()],
};
let bytes: Vec<u8> = user.encode();
let decoded: User = User::decode(&bytes);
// Or use a shared Writer/Reader for multiple values:
let mut w = Writer::new();
user.encode_with(&mut w);
let data = w.finish();The runtime crate is at languages/rust/runtime/. Add it as a dependency:
[dependencies]
cboragen-runtime = { path = "path/to/cboragen/languages/rust/runtime" }The F# code generator produces modules that use Cboragen.Cbor for encoding and decoding.
cd languages/fsharp/codegen
zig build
zig build run -- --namespace MyApp --config mapping.json schema.cbg > Schema.fsThe F# runtime library is at languages/fsharp/runtime/Cbor.fs.
parser/ Zig library — schema parser (shared across all targets)
languages/
typescript/
codegen/ Zig executable — reads .cbg, emits TypeScript
runtime/ @cboragen/runtime npm package
tools/ cboragen npm package (CLI, config, bundler plugins)
benchmark/ Browser-based encode/decode benchmarks
rust/
codegen/ Zig executable — reads .cbg, emits Rust
runtime/ cboragen-runtime Rust crate
fsharp/
codegen/ Zig executable — reads .cbg, emits F#
runtime/ Cboragen.Cbor F# module
SPEC.md Wire format specification
Requires Zig 0.15+.
# Parser
cd parser && zig build
# TypeScript code generator
cd languages/typescript/codegen && zig build
# Rust code generator
cd languages/rust/codegen && zig build
# F# code generator
cd languages/fsharp/codegen && zig buildRun the TypeScript generator directly:
cd languages/typescript/codegen
zig build run -- path/to/schema.cbg > output.ts# Parser tests
cd parser && zig build test
# TypeScript roundtrip tests (requires Bun)
cd languages/typescript/codegen/test
bun test