@cratestack/cbor-node
v0.15.4
Published
Native N-API CBOR codec for CrateStack's generated TypeScript clients, wrapping the framework's own cratestack-codec-cbor Rust crate (crates/cratestack-cbor-napi) for byte-identical wire behavior with the server and Rust client.
Maintainers
Readme
@cratestack/cbor-node
Native N-API CBOR codec for CrateStack's generated TypeScript RPC clients — a
CratestackRpcCodec
backed by the framework's own cratestack-codec-cbor
Rust crate via native bindings (crates/cratestack-cbor-napi),
not a JS reimplementation. Encode/decode output is byte-identical to what the
CrateStack server and Rust client already produce for application/cbor.
Ships prebuilt per-platform binaries via optionalDependencies (the same pattern
esbuild/@swc/core use) — no Rust toolchain needed to install or use this package.
See epic #285 and issue #286 for the full design context.
Usage
import { cborCodec } from "@cratestack/cbor-node";
const bytes = cborCodec.encode({ hello: "world", count: 1 });
const value = cborCodec.decode(bytes);cborCodec structurally satisfies CratestackRpcCodec from @cratestack/ts-types, so it
can be passed anywhere a generated client's runtime expects a codec — no explicit
dependency on @cratestack/ts-types required at runtime, since it's checked via
satisfies, not a type re-export.
Null handling
CBOR has a real null (RFC 8949 §3.3, simple value 22, byte 0xf6). cratestack-codec-cbor's
underlying minicbor-serde backend has a documented quirk where a bare Rust unit type ()
encodes as the CBOR empty array marker (0x80) instead — this package's encode translates
every JSON null (top-level or nested inside an object/array) through the code path that
produces real CBOR null, matching CborCodec's own Option::None behavior byte-for-byte. See
crates/cratestack-cbor-napi/src/json_value.rs for the detail.
Error handling
Malformed CBOR bytes passed to decode throw a catchable JS Error — never a native crash.
Both native entry points also use napi-rs's catch_unwind to convert any unexpected Rust panic
into a catchable exception instead of aborting the Node process.
Scope
Single-item encode/decode only — no CBOR-seq/streaming support (that's a separate concern; see
epic #285). Since issue #746, @cratestack/cbor (this package's Node half, via the umbrella
@cratestack/cbor re-export) is the default codec for a generated TypeScript RPC client —
--no-native-cbor (TypeScriptGeneratorConfig::native_cbor: false) falls back to the
pure-TypeScript jsonRpcCodec, needed on platforms this package doesn't ship a napi binary for
(win32-arm64 — see napi.targets in package.json). REST-transport
generated clients are unaffected either way: the REST runtime has no codec seam at all.
Supported platforms
napi.targets in package.json is the source of truth; as of cratestack#850 it is:
| Platform | Target triple | npm platform package |
| --- | --- | --- |
| macOS x64 | x86_64-apple-darwin | @cratestack/cbor-node-darwin-x64 |
| macOS arm64 | aarch64-apple-darwin | @cratestack/cbor-node-darwin-arm64 |
| Linux x64 (glibc) | x86_64-unknown-linux-gnu | @cratestack/cbor-node-linux-x64-gnu |
| Linux arm64 (glibc) | aarch64-unknown-linux-gnu | @cratestack/cbor-node-linux-arm64-gnu |
| Linux x64 (musl / Alpine) | x86_64-unknown-linux-musl | @cratestack/cbor-node-linux-x64-musl |
| Linux arm64 (musl / Alpine) | aarch64-unknown-linux-musl | @cratestack/cbor-node-linux-arm64-musl |
| Windows x64 | x86_64-pc-windows-msvc | @cratestack/cbor-node-win32-x64-msvc |
Both musl binaries are load-tested in CI, not just built: each build-cbor-node musl leg runs on
the runner architecture it targets and, before uploading anything, loads the .node it just
produced under node:22-alpine and asserts a fixed encode/decode vector through this package's own
native.mjs. So "works on Alpine" is checked on x64 and arm64, on real hardware rather than
emulation, on every release build.
Still not covered: win32-arm64. There is also no wasm/wasi fallback — the generated
native.mjs loader contains a wasm32-wasi branch, but nothing publishes a .wasi.cjs or a
@cratestack/cbor-node-wasm32-wasi package, so that branch can only ever add another
Cannot find module to the error chain. On an unsupported platform the loader raises the generic
"Cannot find native binding. npm has a bug related to optional dependencies…" error, which
blames npm rather than naming the real cause; --no-native-cbor is the escape hatch.
The per-platform packages above are an optimisation, not a requirement: this package's own tarball
bundles every .node binary (files: ["*.node"]), and the loader tries the bundled
./cratestack-cbor-node.<platform>.node before the @cratestack/cbor-node-<platform> dependency.
A platform whose subpackage has not been published yet still resolves a working binding.
