Home > Engineering > Infrastructure > bare-for-pear > avsc-rpc > Barification
avsc-rpc Barification
What was changed from the upstream avsc v5 services code to run as a standalone module on Bare.
Two Operations
avsc-rpc required two distinct adaptations:
- Extraction — separating the RPC layer from the avsc monolith into a standalone module
- Barification — replacing Node.js dependencies with Bare equivalents
Both happened together. The result is a standalone module that depends on avsc as a peer for types and utils, and on Bare platform modules for runtime primitives.
Extraction from avsc
The upstream services.js lived inside avsc and
imported its siblings with relative paths:
// upstream (inside avsc)
let types = require('./types')
let utils = require('./utils')
let platform = require('./platform')
The fork imports from the avsc sibling:
// fork (standalone)
let types = require('../avsc/lib/types')
let utils = require('../avsc/lib/utils')
let avscPlatform = require('../avsc/lib/platform')
This works because both libraries sit under lib/
in the spl codebase. The relative path resolves at
the filesystem level — no package manager involved.
Stream Adaptation
The most significant change. Upstream used Node.js
stream.Transform. The fork uses streamx.Transform.
API Differences
| Concern | Node stream.Transform | streamx.Transform |
|---|---|---|
| Constructor | { readableObjectMode: true } |
{ ... } |
| Transform | _transform(buf, encoding, cb) |
_transform(buf, cb) |
| Unpipe | .unpipe() available |
Not available |
| Error on close | Emits error | Suppressed |
Affected Classes
Four internal stream classes were changed:
FrameDecoder— standard Avro frame decoderFrameEncoder— standard Avro frame encoderNettyDecoder— Netty-compatible frame decoderNettyEncoder— Netty-compatible frame encoder
All extend streamx.Transform instead of
stream.Transform.
Error Handling
Node streams emit errors on unexpected close. streamx
does not. The fork adds decoder.on('error', () => {})
to suppress expected errors when connections close
during frame decoding. This is not error swallowing —
channel-level error handling still operates.
The v5/v6 Bridge
avsc-rpc was written against avsc v5. The fork pairs
it with avsc v6 (the barified fork). The compatibility
module (compat.js) bridges the differences.
Hash Results
avsc v6’s platform.getHash() returns Uint8Array.
The services code calls .toString('binary') and
.readInt16BE() on hash results — methods that exist
on Buffer but not Uint8Array.
function wrapGetHash (originalGetHash) {
return function getHash (str, algorithm) {
let arr = originalGetHash(str, algorithm)
return Buffer.from(arr.buffer, arr.byteOffset, arr.length)
}
}
Removed Utilities
avsc v6 removed several utility functions that services code still references:
| Function | Purpose | Shim |
|---|---|---|
newBuffer(size) |
Allocate buffer | Buffer.alloc(size) |
bufferFrom(data, enc) |
Create buffer from data | Buffer.from(data, enc) |
addDeprecatedGetters |
Attach deprecated properties | No-op |
debuglog |
Debug logger factory | Returns no-op |
deprecate |
Mark function deprecated | Identity (returns fn) |
process.nextTick
Bare does not provide a global process. The services
code uses process.nextTick for deferred execution.
function ensureProcess () {
if (typeof process === 'undefined') {
globalThis.process = { nextTick: queueMicrotask }
} else if (!process.nextTick) {
process.nextTick = queueMicrotask
}
}
Called once at module load.
Module Dependencies
From avsc (peer, constitutive)
types— Avro type system, schema resolutionutils— buffer operations, Tap, helpersplatform— hash computation
From Bare (platform)
| Module | Purpose |
|---|---|
| bare-buffer | Buffer API for hash wrapping, message framing |
| bare-events | EventEmitter for Service, Client, Server |
| bare-stream | Stream base classes |
| streamx | Transform streams for frame encoding/decoding |
Module Structure
services.js — 2470 lines. Service, Client, Server,
channels (stateful/stateless, client/server),
message framing, handshake, multiplexing,
middleware chain, protocol negotiation.
compat.js — 55 lines. v5/v6 bridge: hash wrapping,
removed utils, process.nextTick polyfill,
debug/deprecation no-ops.
One large module — same structure as upstream. The extraction preserved the original file organisation. Internal classes (channels, adapters, registries) are not factored out because the upstream kept them together and the coupling is deliberate — channels depend intimately on the handshake and framing logic.
See Code Implementation for the full dependency management and subtree workflow. See the API Reference for the complete method and option documentation.