Skip to content

Channel Multiplexing

Problem

Several features share a WebSocket but need independently disposable listeners without allowing each feature to define a conflicting protocol for the same channel.

Solution

Declare channel maps once at session construction. Each call creates an independent scope and Pulse reference-counts the server subscription.

ts
import { createPulse } from '@vielzeug/pulse';

type Channels = {
  chat: {
    client: { send: { text: string } };
    server: { message: { text: string } };
  };
};

const pulse = createPulse<{}, {}, Channels>('wss://api.example.com/ws');
const composer = pulse.channel('chat');
const transcript = pulse.channel('chat');

await pulse.connect();

composer.send('send', { text: 'Hello!' });
transcript.on('message', ({ text }) => console.log(text));

composer.dispose(); // transcript still owns the subscription
transcript.dispose(); // Pulse now sends unsubscribe
pulse.dispose();

Pitfalls

  • Channel types belong to createPulse(). channel() no longer accepts per-call event map generics.
  • Each call returns a new scope. Do not compare channel objects for identity.
  • A scope can send only while connected. Reconnect restores active subscriptions before messages can be sent.