title: Message transport with @jeeboard/protocol description: Explains the endpoint abstraction and the in-process endpoint pair from the @jeeboard/protocol package. #
Message transport with @jeeboard/protocol #
The @jeeboard/protocol package defines a small message-transport abstraction.
A message transport is a channel that moves messages between two parts of an application.
The replication layer in @jeeboard/automerge uses this abstraction.
It sends document changes and presence data through an endpoint.
The package does not contain a network implementation. You supply the transport, or you use the in-process pair that the package provides.
Key concepts #
- A message is a value that an endpoint delivers to listeners. You select the message type with a generic type parameter.
- A message listener is a function that receives a message. The type is
MessageListener<Message>. - An endpoint is one side of a message transport. It has a
sendmethod and asubscribemethod. The type isEndpoint<Message>. - An endpoint pair is a set of two connected endpoints,
aandb. The type isEndpointPair<Message>.
An endpoint has two operations:
send(message)delivers a message to the listeners of the opposite endpoint.subscribe(listener)registers a message listener. It returns a function that removes the listener.
NOTE: An endpoint does not receive its own messages. A call to a.send notifies only the listeners of b.
Entry points #
The package exports three types and one function:
MessageListener<Message>— the message listener type.Endpoint<Message>— the endpoint interface.EndpointPair<Message>— the endpoint pair interface with fieldsaandb.createInProcessEndpointPair<Message>()— creates a connected endpoint pair in the current process.
Typical usage #
This example creates an endpoint pair and sends messages in both directions.
It matches the tests in packages/protocol/test/protocol.test.mjs.
import { createInProcessEndpointPair } from "@jeeboard/protocol";
const { a, b } = createInProcessEndpointPair<{ type: string; revision: number }>();
a.subscribe((message) => {
console.log("a received", message);
});
b.subscribe((message) => {
console.log("b received", message);
});
a.send({ type: "patch", revision: 1 }); // delivered to b
b.send({ type: "ack", revision: 1 }); // delivered to a
Remove a listener #
To remove a listener, call the function that subscribe returns.
- Call
subscribeand keep the returned function. - Call the returned function to remove the listener.
- Send again to confirm that the listener does not run.
const { a, b } = createInProcessEndpointPair<{ type: string }>();
const unsubscribe = a.subscribe((message) => {
console.log(message);
});
b.send({ type: "first" }); // the listener runs
unsubscribe();
b.send({ type: "second" }); // the listener does not run
Custom transports #
The in-process pair delivers messages synchronously in the same process.
For network communication, you implement the Endpoint<Message> interface yourself.
For example, you can wrap a WebSocket or a MessagePort in an endpoint.
The createBoardRealtimeSession function in @jeeboard/automerge accepts any Endpoint<BoardRealtimeMessage>.
This design keeps the replication logic independent from the transport.
WARNING: The in-process pair does not copy messages. The listener receives the same object reference. Do not mutate a message after you send it.
API reference #
See the generated API documentation at API reference.