zerobus-sdk / typescript
CLAUDE.md94 starsChanged 10 days agoApache-2.0View on GitHub
Node.js wrapper around the Rust core via NAPI-RS. When writing or reviewing client code that uses this SDK, prefer the idiomatic ingest-then-flush flow. ingestRecordOffset() (and ingestRecordsOffset()) resolves as soon as the record is queued — the SDK sends it and tracks its acknowledgment in the background. Ingest in a loop, then call flush() once to confirm durability (once for a bounded batch, or periodically for a long-running stream). Each ingest returns the record's offset, and waitForOffset(offset) confirms a specific record…
In this repository6 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- cpp/CLAUDE.mdCLAUDE.md
- go/CLAUDE.mdCLAUDE.md
- java/CLAUDE.mdCLAUDE.md
- python/CLAUDE.mdCLAUDE.md
- rust/CLAUDE.mdCLAUDE.md
The file
We split this CLAUDE.md into the rules it gives an agent
4 rules. About 0 are common, 1 is distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 7
When writing or reviewing client code that uses this SDK, prefer the idiomatic ingest-then-flush flow.
- Very specific: line 86
Every PR must update typescript/NEXT_CHANGELOG.md under the appropriate section if it changes user-facing behavior.
- Very specific: line 87
Update typescript/README.md if the change affects usage, setup, or API surface.
- Very specific: line 88
Add or update examples in typescript/examples/ for new or modified APIs.
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
TypeScript SDK
Node.js wrapper around the Rust core via NAPI-RS.
Client code patterns
When writing or reviewing client code that uses this SDK, prefer the idiomatic
ingest-then-flush flow. ingestRecordOffset() (and ingestRecordsOffset()) resolves as
soon as the record is queued — the SDK sends it and tracks its acknowledgment in the
background. Ingest in a loop, then call flush() once to confirm durability (once for a
bounded batch, or periodically for a long-running stream).
Each ingest returns the record's offset, and waitForOffset(offset) confirms a specific
record when one must be acknowledged before continuing (acks are ordered, so the last
offset confirms the whole run); prefer flush() for bulk. Note that ack callbacks are NOT
part of the TypeScript API — the async confirmation tools are flush() and waitForOffset().
The one thing to avoid is calling waitForOffset() after every record in a tight loop,
since that limits throughput to one record per round-trip.
Structure
typescript/
├── src/lib.rs # NAPI-RS bindings (Rust ↔ Node.js bridge)
├── index.js/.d.ts # NAPI-generated native binding (gitignored build output)
├── wrapper.js/.d.ts # Public entry point: thin JS wrapper over the native binding; encodes
│ # Avro record objects (via the optional `avsc` dependency)
├── Cargo.toml # Rust crate config for NAPI-RS
├── package.json # npm package config (main → wrapper.js)
├── test/ # Node.js test runner tests
├── examples/ # Usage examples
├── schemas/ # Proto schema files for examples
└── utils/ # Helper utilitiesThe native addon compiles to a platform-specific .node binary. NAPI-RS generates TypeScript
type definitions automatically. The package entry point is wrapper.js (not the generated
index.js): it encodes Avro record objects to bytes and forwards everything else unchanged.
The avro and arrow-flight Cargo features are off by default for a from-source npm run build,
but CI and release builds enable both (npm run build:all), so published binaries include them.
Build commands
Run from typescript/:
npm install— Install dependenciesnpm run build— Release buildnpm run build:debug— Debug build (faster iteration)npm run build:arrow/npm run build:avro— Same builds with Arrow Flight / Avronpm run build:all— Release build with both Arrow Flight and Avro (what CI and release use)npm test— Run all testsnpm run test:unit/npm run test:integration— Targeted test runscargo fmt --allandcargo clippy --all-targets --all-features— Lint/format Rust code
FFI boundary: NAPI-RS
NAPI-RS bridges Rust types to JavaScript with automatic type conversions. Key considerations:
- Memory: NAPI external objects are ref-counted by Node.js GC. Cleanup triggers Rust
Drop. No explicit free needed, butclose()should be called to flush. - Async: Rust futures become JavaScript Promises. The tokio runtime runs on a separate thread pool — Node.js event loop is never blocked.
- Payload conversion: The binding coerces payloads by the stream's record type:
- Proto / JSON streams:
Buffer→ proto bytes,string→ JSON, objects with.encode()→ protobuf message (calls encode, passes bytes), plain objects → JSON.stringify'd. - Avro streams:
Buffer→ Avro datum. Record objects are encoded to bytes JS-side (by the wrapper, viaavsc) before they reach the native binding — the native layer only sees bytes.
- Proto / JSON streams:
- Error mapping:
ZerobusErrorstruct withmessageandisRetryablefields, thrown as JS exceptions.
Breaking change rules
The public API is what TypeScript consumers see — the generated .d.ts types and exported classes:
- Removing or renaming exported classes/methods is breaking.
- Changing a method signature (parameter types, return type) is breaking.
- The NAPI-RS binding layer (
src/lib.rs) is internal; refactoring is safe as long as the JS-facing API is preserved. - Deprecate by adding JSDoc
@deprecatedannotations and console warnings before removal.
Performance notes
- NAPI-RS has low overhead for Buffer payloads (shared memory, no copy).
- String payloads (JSON) require UTF-8 validation at the boundary.
- The automatic
JSON.stringifypath for plain objects adds serialization cost — encourage users to pass pre-serialized strings or Buffers for hot paths. - Batch ingestion amortizes the JS→Rust crossing overhead.
Changelog and documentation
- Every PR must update
typescript/NEXT_CHANGELOG.mdunder the appropriate section if it changes user-facing behavior. - Update
typescript/README.mdif the change affects usage, setup, or API surface. - Add or update examples in
typescript/examples/for new or modified APIs. - NAPI-RS auto-generates
.d.tstype definitions — verify the generated types after API changes.
Release
- Version source:
typescript/package.json("version": "x.y.z"). - Tag:
typescript/v<semver>→ triggersrelease-typescript.yml→ builds Linux and Windows.nodebinaries with Arrow Flight and Avro (npm run build:all) → publishes to npm. - macOS
.nodebinaries are supplied via themacos-binaries-releaseworkflow input. They must be built withnpm run build:all;npm run buildomits both features. - On version bump PR: update version in
package.json, moveNEXT_CHANGELOG.mdcontents toCHANGELOG.md, resetNEXT_CHANGELOG.md.
Platform targets
Pre-built binaries for: Linux x86_64, Linux aarch64, macOS x86_64, macOS arm64, Windows x86_64. Node.js >= 16 required.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

