Files
zeekay 12b414f972 State a version once
A version written into a manifest is written into every manifest that wants it,
which is how eslint came to exist in a hundred and four forms across this estate
and how a package came to resolve twice at run time. The catalog moves the number
out of the manifest: a manifest names the package, the workspace names the
version.

Four are held below their latest because the newer release cannot be used, and
the reason is recorded rather than the version alone: @noble/hashes 2 renamed
every subpath the web3 stack imports, @splinetool/runtime 2 depends on a package
that was never published, @dynamic-labs 5.4.1 the same, and react-resizable-panels
3 points its types condition at the bundle.
2026-08-29 13:46:58 -07:00
..
2026-08-29 13:46:58 -07:00

@hanzo/o11y

Traces to Hanzo o11y over ZAP. No OpenTelemetry, no OTLP, no gRPC, no protobuf.

import { O11y } from '@hanzo/o11y'

const o11y = new O11y({ appName: 'my-service', host: 'o11y.hanzo.svc' })

const span = o11y.startSpan('handle-request')
span.setAttribute('http.method', 'GET')
const child = o11y.startSpan('db-query', span)
child.end()
span.end()

await o11y.shutdown()

A span here is already the shape that goes on the wire, so export is JSON plus a frame — there is no second span model to translate through.

Wire

src/wire.ts mirrors luxfi/trace.SpanBatch field for field, and the frame matches what hanzo/o11y/pkg/zapreceiver decodes: a luxfi/zap envelope at header version 2, message type in the flags high byte (MSG_SPAN_BATCH << 8), JSON in the root object's first field. Verified byte-identical to the Go emitter's FinishWithFlags(MsgSpanBatch << 8).

Adding a field means adding it to all three — this SDK, luxfi/trace, and pkg/zapreceiver — in one change.

Transport

The collector is a ZAP node, not a socket that takes loose frames — until the NodeID exchange completes it discards what arrives. src/transport.ts is that layer: length-prefixed frames over TCP (little-endian uint32), opened by the handshake luxfi/zap defines in node_codec.go — the ID in bytes [0,60) and its length at offset 60.

@zap-proto/zap supplies the wire format and stops there, so the node/peer layer lives here. If it grows one, this file is what to delete.

Verified end to end against a live pkg/zapreceiver: spans sent from Node arrive decoded, with trace/parent linkage, attributes, events and status intact.