Bidirectional actor channels with pub/sub, peer relaying, and RPC.
npm install @sovereignbase/actor-channel
# or
pnpm add @sovereignbase/actor-channel
# or
yarn add @sovereignbase/actor-channel
# or
bun add @sovereignbase/actor-channel
# or
deno add jsr:@sovereignbase/actor-channel
# or
vlt install jsr:@sovereignbase/actor-channelimport { ActorChannel } from '@sovereignbase/actor-channel'
const channel = new ActorChannel<
'documents',
{ id: string },
{ method: string },
{ result: string }
>()
channel.addEventListener('message', (event) => {
const [topic, message] = event.detail
console.log(topic, message)
})
channel.addEventListener('response', (event) => {
const [id, response] = event.detail
console.log(id, response)
})
channel.subscribe('documents')
channel.publish('documents', { id: 'document-1' })
const requestId = channel.request({ method: 'sync' })
console.log(requestId)
void channel.addBroker('wss://broker.example', true)import { ChannelBroker } from '@sovereignbase/actor-channel'
const broker = new ChannelBroker<
'documents',
{ id: string },
{ method: string },
{ result: string }
>()
broker.addEventListener('request', (event) => {
const [request, respond, sender] = event.detail
if (!request.method) {
broker.dispatchEvent('violation', {
violator: sender,
description: 'Off protocol.',
})
return
}
respond({ result: request.method })
})
broker.addChannel(socket, { rpcEnabled: true })
// Verify application-level identity using your own protocol and policy.
// Transport authentication is handled separately by HTTPS/WebSocket.
broker.markAuthenticated(socket)
socket.addEventListener('message', (event) => {
broker.handleMessage(socket, event.data)
})
socket.addEventListener('close', () => {
broker.deleteChannel(socket)
})The transport passed to ChannelBroker must expose send(ArrayBuffer) and
readyState. Incoming messages must be provided as ArrayBuffer values.
rpcAvailablereports whether an RPC-enabled broker is available.request(detail)sends a fire-and-forget RPC request and returns its ID.publish(topic, detail)publishes locally and to subscribed broker peers.subscribe(topic)subscribes the current window to a topic.unsubscribe(topic)removes the current window's subscription.addBroker(url, rpcEnabled?)repeatedly attempts to acquire a URL-specific Web Lock and maintains the WebSocket while it owns the lock.
addChannel(channel, attachment?)adds a transport with optional metadata, RPC access, and initial subscriptions.deleteChannel(channel)removes a transport and its subscriptions.markAuthenticated(channel)records that the host application has verified the channel's application-level identity and emits anattachmentevent. The method is intentionally one-way and provides no unset operation.handleMessage(channel, message)handles an encoded client message.dispatchEvent(type, detail)dispatches a typed broker event to registered listeners.attachmentevents receive the channel and its updated attachment.requestevents receive the request, a response callback, and the sender.violationevents receive the violating channel and a description.
ActorChannelis window-only and requires Broadcast Channel, Web Locks, Web Crypto, and WebSocket APIs.- Same-origin tabs share topic subscription counts and elect at most one connection to each broker URL.
- Broker connections are retried every 10 seconds after closing or when their Web Lock is unavailable.
- Topic subscriptions are forwarded to brokers on the first local subscription and removed after the final local unsubscription.
- Requests are not queued or replayed when no RPC-enabled connection is open.
rpcAvailableistruewhen the channel knows of at least one open RPC-enabled broker WebSocket.- Protocol messages are MessagePack-encoded
ArrayBuffervalues. authenticateddoes not describe HTTPS/WebSocket transport authentication. The host sets it throughmarkAuthenticated(channel)after verifying an application-level identity with its own protocol, cryptosuite, and policy.ChannelBrokeruses standard web platform primitives and does not require a specific server framework.
- Unit and integration tests in Vitest with 100% statement, branch, function, and line coverage.
- Browser E2E tests in Chromium, Firefox, WebKit, Mobile Chromium, Mobile Firefox, and Mobile WebKit.
ChannelBrokerruntime tests in Node.js, Bun, Deno, Vercel Edge Runtime, and Cloudflare Workers (workerd).
Apache-2.0