Skip to content

Rooms and Presence

Problem

You need server-confirmed room membership and reactive member state that survives a reconnect without exposing stale membership.

Solution

Define room schemas at construction. Create a room scope, connect, then publish state.

ts
import { createPulse } from '@vielzeug/pulse';
import { effect, fromSubscribable } from '@vielzeug/ripple';

type Schema = {
  rooms: {
    lobby: { presence: { name: string; status: 'online' | 'away' } };
    announcements: {};
  };
};

const pulse = createPulse<Schema>('wss://api.example.com/ws', { reconnect: true });
const lobby = pulse.room('lobby');

try {
  await pulse.connect();
  await lobby.joined;
  lobby.updatePresence({ name: 'Ada', status: 'online' });
} catch (error) {
  console.error('Pulse connection failed:', error);
}

const presence = fromSubscribable(lobby.presence, { signal: lobby.disposalSignal });
const presenceEffect = effect(() => {
  for (const [memberId, member] of presence.value) console.log(memberId, member);
});

lobby.onLeave((memberId) => console.log('left', memberId));

const announcements = pulse.room('announcements');
await announcements.joined;
console.log('rooms:', [...pulse.rooms.getSnapshot()]);

presenceEffect.dispose();
announcements.dispose();
lobby.dispose();
pulse.dispose();

Pitfalls

  • Room schemas belong to createPulse(). room() infers its types from the schema.
  • rooms is server-confirmed. It clears on a disconnected transport and returns after joined frames.
  • A room remains active while any scope owns it. Dispose every scope before expecting a leave frame.