@molecule/app-realtime
Core interface · realtime · App (browser) · v1.0.2 · Apache-2.0
Realtime client core interface for molecule.dev — WebSocket/SSE connections with rooms, presence, events, and auto-reconnection
npm install @molecule/app-realtimeHow it works
@molecule/app-realtime is the realtime core interface on the app (browser) side: the API your app calls, with no vendor inside.
Choose the implementation by bonding one of its 1 provider: @molecule/app-realtime-socketio.
import { setProvider, connect } from '@molecule/app-realtime'
import { provider } from '@molecule/app-realtime-socketio'
setProvider(provider)
const connection = await connect('wss://api.example.com', {
autoReconnect: true,
auth: { token: 'my-jwt' },
})
await connection.joinRoom('chat-room-1')
connection.on('message', (data) => console.log('Received:', data))
connection.sendTo('chat-room-1', 'message', { text: 'Hello!' })Providers (1): @molecule/app-realtime-socketio
Works with: @molecule/app-bond
Reference
Realtime client core interface for molecule.dev.
Provides a framework-agnostic contract for WebSocket / SSE client connections
with rooms, presence tracking, events, and automatic reconnection. Bond a
provider (e.g. @molecule/app-realtime-socketio) at startup, then use
connect anywhere.
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Quick Start
import { setProvider, connect } from '@molecule/app-realtime'
import { provider } from '@molecule/app-realtime-socketio'
setProvider(provider)
const connection = await connect('wss://api.example.com', {
autoReconnect: true,
auth: { token: 'my-jwt' },
})
await connection.joinRoom('chat-room-1')
connection.on('message', (data) => console.log('Received:', data))
connection.sendTo('chat-room-1', 'message', { text: 'Hello!' })
Type
core
Installation
npm install @molecule/app-realtime @molecule/app-bond
API
Interfaces
ConnectionOptions
Options for establishing a realtime connection.
interface ConnectionOptions {
/** Whether to automatically reconnect on disconnection. Defaults to `true`. */
autoReconnect?: boolean
/** Delay in milliseconds before attempting reconnection. Defaults to `1000`. */
reconnectDelay?: number
/** Maximum number of reconnection attempts. Defaults to `10`. */
maxRetries?: number
/** Authentication data sent during the handshake. */
auth?: Record<string, unknown>
}
PresenceInfo
Presence information for a connected client in a room.
interface PresenceInfo {
/** The client's unique identifier. */
clientId: string
/** Arbitrary metadata attached to the client's presence (e.g. username, avatar). */
metadata?: Record<string, unknown>
}
RealtimeConnection
A live realtime connection exposing room, event, and presence methods.
interface RealtimeConnection {
// -- Rooms ---------------------------------------------------------------
/**
* Joins a room by id.
*
* @param roomId - The room to join.
*/
joinRoom(roomId: string): Promise<void>
/**
* Leaves a room by id.
*
* @param roomId - The room to leave.
*/
leaveRoom(roomId: string): Promise<void>
// -- Messaging -----------------------------------------------------------
/**
* Sends an event to the server (broadcast to the default channel).
*
* @param event - The event name.
* @param data - The event payload.
*/
send(event: string, data: unknown): void
/**
* Sends an event to a specific room.
*
* @param roomId - The target room.
* @param event - The event name.
* @param data - The event payload.
*/
sendTo(roomId: string, event: string, data: unknown): void
// -- Event listening -----------------------------------------------------
/**
* Registers a handler for an incoming event.
*
* @param event - The event name to listen for.
* @param handler - The handler callback.
*/
on(event: string, handler: RealtimeEventHandler): void
/**
* Removes a handler for an event. If no handler is provided, all handlers
* for that event are removed.
*
* @param event - The event name.
* @param handler - The specific handler to remove (optional).
*/
off(event: string, handler?: RealtimeEventHandler): void
// -- Presence ------------------------------------------------------------
/**
* Returns the current presence information for all clients in a room.
*
* @param roomId - The room to query.
* @returns Array of presence info for each connected client.
*/
getPresence(roomId: string): PresenceInfo[]
/**
* Registers a handler that fires when presence changes in any joined room.
* The handler receives the room id the update is for (see
* `PresenceChangeHandler`) — a single handler registered once still
* works correctly for a consumer joined to multiple rooms.
*
* @param handler - The presence change handler.
*/
onPresenceChange(handler: PresenceChangeHandler): void
// -- Connection lifecycle ------------------------------------------------
/**
* Disconnects from the server and cleans up resources.
*/
disconnect(): void
/**
* Returns whether the connection is currently active.
*
* @returns `true` if connected to the server.
*/
isConnected(): boolean
/**
* Returns the current connection state.
*
* @returns The current `ConnectionState`.
*/
getState(): ConnectionState
/**
* Registers a handler that fires on successful reconnection.
*
* @param handler - The reconnection handler.
*/
onReconnect(handler: () => void): void
/**
* Registers a handler that fires when the connection state changes.
*
* @param handler - The state change handler.
*/
onStateChange(handler: ConnectionStateHandler): void
}
RealtimeClientProvider
Contract that bond packages must implement to provide realtime client functionality.
interface RealtimeClientProvider {
/**
* Establishes a realtime connection to the given server URL.
*
* @param url - The server URL to connect to.
* @param options - Optional connection configuration.
* @returns A promise resolving to a live realtime connection.
*/
connect(url: string, options?: ConnectionOptions): Promise<RealtimeConnection>
}
Types
RealtimeEventHandler
Handler invoked when a realtime event is received.
type RealtimeEventHandler = (data: unknown) => void
data— The event payload.
PresenceChangeHandler
Handler invoked when presence information changes for a room.
type PresenceChangeHandler = (presence: PresenceInfo[], roomId: string) => void
presence— The updated list of present clients FORroomId(not a merged/global list — a consumer joined to multiple rooms must useroomIdto know which room's member list this update replaces, never assume "the" single room).roomId— The room this presence update is for. Existing handlers written against the pre-roomIdsingle-argument signature keep compiling and running unchanged (extra trailing arguments are both TS- and JS-compatible with a shorter callback) but SHOULD add the parameter before rendering presence for an app with more than one joined room.
ConnectionState
Possible states of a realtime connection.
type ConnectionState = 'connecting' | 'connected' | 'disconnected' | 'reconnecting'
ConnectionStateHandler
Handler invoked when the connection state changes.
type ConnectionStateHandler = (state: ConnectionState) => void
state— The new connection state.
Functions
setProvider(provider)
Registers a realtime client provider as the active singleton. Called by bond
packages (e.g. @molecule/app-realtime-socketio) during app startup.
function setProvider(provider: RealtimeClientProvider): void
provider— The realtime client provider implementation to bond.
getProvider()
Retrieves the bonded realtime client provider, throwing if none is configured.
function getProvider(): RealtimeClientProvider
Returns: The bonded realtime client provider.
hasProvider()
Checks whether a realtime client provider is currently bonded.
function hasProvider(): boolean
Returns: true if a realtime client provider is bonded.
connect(url, options)
Establishes a realtime connection using the bonded provider.
function connect(url: string, options?: ConnectionOptions): Promise<RealtimeConnection>
url— The server URL to connect to.options— Optional connection configuration.
Returns: A promise resolving to a live realtime connection.
Available Providers
| Provider | Package |
|---|---|
| Socket.IO | @molecule/app-realtime-socketio |
Injection Notes
Requirements
Peer dependencies:
@molecule/app-bond^1.0.1
Runtime Dependencies
-
@molecule/app-bond -
The client needs a realtime SERVER counterpart. Wire the matching bond pair across the stack — e.g.
@molecule/app-realtime-socketioin the app with@molecule/api-realtime-socketioin the API — and connect to YOUR API's URL. A client bond alone has nothing to connect to. -
Wiring the bond is NOT consuming. Setting the bond up in
bonds.tsonly opens the connection — a screen must ALSOconnect()→joinRoom(room)→on(event, handler), or it receives nothing while the server broadcasts to an empty room. Theroomandeventnames MUST match the server'sbroadcast(room, event, …)EXACTLY — both are usually template literals (e.g.`listing:${id}`), so build the identical string. A wrong (or never-joined) room, or a wrong event name, is a SILENT no-op: nothing throws, the events just never arrive. Confirm with the live two-session check below. -
Clean up on unmount/screen change. Registering
on(event, handler)in a render/effect without the matchingoff(event, handler)(plusleaveRoom/disconnect) re-registers on every re-render — events then fire N times. -
Anything a client
sends is UNTRUSTED server-side: the API must validate and authorize every event (room membership, ownership) — never trust client-supplied ids or roles. -
Pass credentials via
ConnectionOptions.authat connect time, never in the URL query string (URLs leak into logs).
E2E Tests
Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:
- The client connects when the relevant screen loads (no connection errors in the console) and live data renders without a manual reload.
- Two sessions in the same room see each other's messages/updates appear live (within about a second).
- Presence indicators (if surfaced) update when a participant joins or leaves.
- After a dropped connection (offline/online toggle), the client auto-reconnects, the UI's connection state (if shown) is truthful, and live updates resume.
- Leaving a room/screen stops that room's events from affecting the UI.