npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@causal-order/transport

v0.2.2

Published

WebSocket + JSON transport layer for normalizing node traffic into the event contract expected by the causal-order stack.

Readme

@causal-order/transport

WebSocket + JSON ingress with acknowledged delivery, bounded shutdown, and event normalization for the causal-order stack.

Published package version: v0.2.2

Stack Position

nodes -> @causal-order/transport -> @causal-order/dedupe -> causal-order -> application

Transport owns WebSocket connection lifecycle, the versioned JSON protocol, delivery acknowledgment, peer evidence, and normalization into the canonical event contract. Deduplication and causal ordering remain downstream concerns.

Deployments requiring bounded outage buffering, health-aware routing, controlled recovery, and replay place @causal-order/monitor between transport and dedupe:

nodes -> @causal-order/transport -> @causal-order/monitor -> @causal-order/dedupe -> causal-order -> application

Monitor owns buffering and replay. Replayed events return through dedupe before causal ordering.

Install and Requirements

npm install @causal-order/transport @causal-order/dedupe causal-order
  • Supported Node.js release lines: 22 and 24
  • ESM-only output

The monitor-enabled stack requires Node.js 22.13.0 or newer because @causal-order/monitor uses the built-in node:sqlite module.

Server

Register one primary delivery authority when downstream acceptance must control the acknowledgment returned to the sender.

import { WebSocketJsonTransport } from "@causal-order/transport";

const transport = new WebSocketJsonTransport({
  mode: "server",
  host: "127.0.0.1",
  port: 8080,
});

transport.onDelivery(async (event, context) => {
  await deliverToNextStage(event, context);
  return { outcome: "accepted" };
});

transport.onEvent((event, context) => {
  observeTransportEvent(event, context);
});

transport.onPeerState((state) => {
  console.log("peer", state.peerId, state.status);
});

transport.onError((error) => {
  console.error("transport", error.detail);
});

await transport.start();

onDelivery() is the delivery authority. onEvent() is optional observation and does not control acknowledgment. deliverToNextStage is application-provided; it normally hands the event to monitor or directly to dedupe. Without an onDelivery() handler, successful normalization and observer dispatch form the acknowledgment boundary.

The default normalizer is used when normalizeMessage is omitted. A custom normalizer may adapt another inbound JSON event shape:

const transport = new WebSocketJsonTransport({
  mode: "server",
  port: 8080,
  normalizeMessage(message) {
    return normalizeApplicationMessage(message);
  },
});

Client

Client transports accept normalized events containing bigint-backed causal metadata. Known bigint fields are encoded as decimal strings on the JSON wire. send() resolves only after the receiving transport's primary delivery handler acknowledges the transmission. A WebSocket write callback alone is not treated as remote delivery.

import { WebSocketJsonTransport } from "@causal-order/transport";

const client = new WebSocketJsonTransport({
  mode: "client",
  url: "ws://127.0.0.1:8080",
  peerId: "ingress",
});

await client.start();

await client.send({
  id: "edge-a-000000000042",
  nodeId: "edge-a",
  sequence: 42n,
  clock: {
    physicalTimeMs: 1781000000000n,
    logicalCounter: 0,
    nodeId: "edge-a",
  },
  parentEventId: "edge-a-000000000041",
  dependencyEventIds: ["edge-b-000000000017"],
  partition: "factory-1",
  payload: { temperature: 21 },
});

Payloads must be JSON-serializable. Arbitrary bigint values inside payload are not converted automatically.

In server mode, send(event) broadcasts to all connected peers. send(event, peerId) targets one connected peer.

Every unicast target receives a unique transmission ID. Broadcasts snapshot their target peers and wait for every target to acknowledge or fail. A partial broadcast failure rejects with a TransportOperationError whose cause retains the individual peer failures.

Default Wire Format

Transport 0.2.x sends a versioned event envelope:

{
  "schema": "causal-order/transport",
  "version": 1,
  "type": "event",
  "transmissionId": "tx-018f-example",
  "event": {
    "id": "edge-a-000000000042",
    "nodeId": "edge-a",
    "sequence": "42",
    "clock": {
      "physicalTimeMs": "1781000000000",
      "logicalCounter": 0,
      "nodeId": "edge-a"
    },
    "parentEventId": "edge-a-000000000041",
    "dependencyEventIds": ["edge-b-000000000017"],
    "partition": "factory-1",
    "payload": {
      "temperature": 21
    }
  }
}

The receiver returns a correlated result after primary delivery:

{
  "schema": "causal-order/transport",
  "version": 1,
  "type": "delivery-result",
  "transmissionId": "tx-018f-example",
  "outcome": "accepted"
}

Refusal and failure results carry the same transmissionId, a non-accepted outcome, and a machine-readable code. Application event IDs do not serve as transmission IDs, so retries and intentional duplicates remain independently accountable.

The receiver continues to accept the legacy flat 0.1.x event object as input, but legacy messages cannot participate in acknowledged root send() semantics.

The following aliases are also accepted:

| Canonical field | Accepted aliases | | --- | --- | | nodeId | node, sourceNodeId | | sequence | seq | | clock.physicalTimeMs | ts, timestampMs, physicalTimeMs | | clock.logicalCounter | logical, lc | | payload | body |

sequence, timestamps, and ingestedAt accept bigint values, safe integer numbers, or canonical decimal integer strings. Unsafe numbers and malformed integer strings are rejected.

Normalized events contain:

  • id
  • nodeId
  • optional sequence
  • clock.physicalTimeMs, clock.logicalCounter, and clock.nodeId
  • payload
  • optional partition, parentEventId, and dependencyEventIds
  • optional traceId and ingestedAt

API

The main entry point, @causal-order/transport, exports:

  • WebSocketJsonTransport
  • normalizeTransportEventMessage()
  • createEventId()
  • createHarnessAdapter()
  • transport, event, context, peer-state, and option types

The published testing adapter supports deterministic node disconnect/reconnect and accepts the root transport's bounded per-peer send capacity:

const adapter = await createHarnessAdapter({
  nodeIds: ["edge-a", "edge-b"],
  maxInFlightSendsPerPeer: 4096,
});

await adapter.setNodeConnectivity("edge-a", "disconnected");
const bufferedSend = adapter.send(event);
await adapter.setNodeConnectivity("edge-a", "connected");
await bufferedSend;

WebSocketJsonTransport implements this lifecycle:

interface TransportContract<T> {
  readonly state: TransportLifecycleState;
  start(): Promise<void>;
  stop(): Promise<void>;
  send(event: NormalizedTransportEvent<T>, targetPeerId?: string): Promise<void>;
  onDelivery(handler: TransportDeliveryHandler<T>): () => void;
  onEvent(handler: TransportEventHandler<T>): () => void;
  onPeerState(handler: TransportPeerStateHandler): () => void;
  onError(handler: TransportErrorHandler): () => void;
}

onDelivery() registers one primary delivery authority. Its result determines the correlated acknowledgment returned to the sender. It may be asynchronous and may accept or refuse delivery. onEvent() remains an observational notification; observer failures are reported without changing the primary delivery result.

Lifecycle state is idle, starting, started, stopping, stopped, or failed. Shutdown atomically rejects new sends, drains accepted sends and receive work, closes sockets, and resolves only when no transport callback can arrive later. A bounded drain failure rejects explicitly.

Timeout and pressure options include connectionTimeoutMs, acknowledgmentTimeoutMs, deliveryTimeoutMs, shutdownTimeoutMs, maxInFlightSends, maxInFlightSendsPerPeer, maxInFlightReceives, maxInFlightReceivesPerPeer, and maxBufferedAmountBytes.

Instances may restart after a completed stop. Automatic reconnect is not part of 0.2.1; applications must create or restart a client according to their own retry policy.

Testing Subpath

The package exports @causal-order/transport/testing for @causal-order/testing. It is validation tooling, not an application runtime API.

npm install --save-dev @causal-order/testing

The adapter uses root transport clients and exercises the same acknowledgment, pressure, and stop-barrier contract as the production API. Repository test profiles and release evidence are documented in VALIDATION.md.

Runtime Boundary

  • This package provides transport and normalization. Deduplication and causal ordering remain the responsibility of @causal-order/dedupe and causal-order.
  • WebSocket + JSON is the supported ingress path. Other wire protocols should use separate adapters built against the same normalized event contract.
  • The required ws runtime is vendored behind an internal wrapper, so consumers do not install ws as an additional runtime dependency.
  • Vendored source, license, version, and local-modification details are recorded in THIRD_PARTY_NOTICES.md.

License

MIT