Home Getting Started Server Client Shared Types GitHub ↗
sockr-shared@1.3.0

Shared Types

Zero-dependency TypeScript type definitions. The single source of truth for the Sockr protocol.

Shared Types Reference

Complete type reference for sockr-shared. Imported by both sockr-server and sockr-client — zero runtime dependencies.

Single source of truth: The SocketEvent enum and EventPayloads interface define the entire Sockr protocol. Importing from sockr-shared ensures your server and client never drift out of sync.

SocketEvent Enum

All 63 event strings used across the Sockr protocol. Import and use these everywhere instead of raw strings.

import { SocketEvent } from 'sockr-shared';

Connection

CONNECT'connect'
DISCONNECT'disconnect'
ERROR'error'

Authentication

AUTHENTICATE'authenticate'
AUTHENTICATED'authenticated'
AUTH_ERROR'auth_error'

Presence

USER_ONLINE'user_online'
USER_OFFLINE'user_offline'
GET_ONLINE_STATUS'get_online_status'
ONLINE_STATUS'online_status'

Direct Messaging

SEND_MESSAGE'send_message'
RECEIVE_MESSAGE'receive_message'
MESSAGE_DELIVERED'message_delivered'
MESSAGE_READ'message_read'
MESSAGE_ERROR'message_error'
TYPING_START'typing_start'
TYPING_STOP'typing_stop'
GET_MESSAGE_HISTORY'get_message_history'
MESSAGE_HISTORY'message_history'

Group Management

GROUP_CREATE'group_create'
GROUP_CREATED'group_created'
GROUP_CREATE_ERROR'group_create_error'
GROUP_JOIN'group_join'
GROUP_JOINED'group_joined'
GROUP_JOIN_ERROR'group_join_error'
GROUP_LEAVE'group_leave'
GROUP_LEFT'group_left'
GROUP_MEMBER_JOINED'group_member_joined'
GROUP_MEMBER_LEFT'group_member_left'
GET_GROUP_MEMBERS'get_group_members'
GROUP_MEMBERS'group_members'
GET_USER_GROUPS'get_user_groups'
USER_GROUPS'user_groups'

Group Messaging

GROUP_SEND_MESSAGE'group_send_message'
GROUP_RECEIVE_MESSAGE'group_receive_message'
GROUP_MESSAGE_DELIVERED'group_message_delivered'
GROUP_MESSAGE_READ'group_message_read'
GROUP_MESSAGE_ERROR'group_message_error'
GROUP_TYPING_START'group_typing_start'
GROUP_TYPING_STOP'group_typing_stop'
GET_GROUP_MESSAGE_HISTORY'get_group_message_history'
GROUP_MESSAGE_HISTORY'group_message_history'

Voice Calling (P2P)

CALL_GET_ICE_SERVERS'call_get_ice_servers'
CALL_ICE_SERVERS'call_ice_servers'
CALL_INITIATE'call_initiate'
CALL_INCOMING'call_incoming'
CALL_RINGING'call_ringing'
CALL_ANSWER'call_answer'
CALL_ANSWERED'call_answered'
CALL_REJECT'call_reject'
CALL_REJECTED'call_rejected'
CALL_HANGUP'call_hangup'
CALL_ENDED'call_ended'
CALL_BUSY'call_busy'
CALL_ICE_CANDIDATE'call_ice_candidate'

Group Conference Calling (SFU)

CONFERENCE_JOIN'conference_join'
CONFERENCE_LEAVE'conference_leave'
CONFERENCE_TOKEN'conference_token'
CONFERENCE_STARTED'conference_started'
CONFERENCE_ENDED'conference_ended'
CONFERENCE_PARTICIPANT_JOINED'conference_participant_joined'
CONFERENCE_PARTICIPANT_LEFT'conference_participant_left'
CONFERENCE_ERROR'conference_error'

EventPayloads Interface

Maps every SocketEvent to its typed payload. Used internally by SocketClient and SocketServer for type-safe emit/listen.

import { EventPayloads, SocketEvent } from 'sockr-shared';

// Example: type-safe event handler
const handler = (data: EventPayloads[SocketEvent.RECEIVE_MESSAGE]) => {
  console.log(data.from, data.content, data.timestamp);
};
interface EventPayloads {
  // Auth
  authenticate:          { token: string };
  authenticated:         { userId: string; socketId: string };
  auth_error:            { message: string };

  // Presence
  user_online:           { userId: string };
  user_offline:          { userId: string };
  get_online_status:     { userIds: string[] };
  online_status:         { statuses: Record<string, boolean> };

  // Direct messaging
  send_message:          { to: string; content: string; metadata?: Record<string, any> };
  receive_message:       { from: string; content: string; timestamp: number; messageId: string; metadata?: Record<string, any> };
  message_delivered:     { messageId: string };
  message_read:          { messageId: string; from?: string };
  message_error:         { messageId: string; error: string };
  typing_start:          { from: string };
  typing_stop:           { from: string };
  get_message_history:   { with: string; limit?: number; before?: number };
  message_history:       { conversationId: string; messages: PersistedMessage[] };

  // Group management
  group_create:          { name: string; members?: string[]; metadata?: Record<string, any> };
  group_created:         { group: Group };
  group_create_error:    { error: string };
  group_join:            { groupId: string };
  group_joined:          { groupId: string; group: Group; queuedMessages?: PersistedMessage[] };
  group_join_error:      { groupId: string; error: string };
  group_leave:           { groupId: string };
  group_left:            { groupId: string };
  group_member_joined:   { groupId: string; member: GroupMember };
  group_member_left:     { groupId: string; userId: string };
  get_group_members:     { groupId: string };
  group_members:         { groupId: string; members: GroupMember[] };
  get_user_groups:       Record<string, never>;
  user_groups:           { groups: Group[] };

  // Group messaging
  group_send_message:         { groupId: string; content: string; metadata?: Record<string, any> };
  group_receive_message:      { groupId: string; messageId: string; from: string; content: string; timestamp: number; metadata?: Record<string, any> };
  group_message_delivered:    { groupId: string; messageId: string };
  group_message_read:         { groupId: string; messageId: string };
  group_message_error:        { groupId: string; messageId?: string; error: string };
  group_typing_start:         { groupId: string; from: string };
  group_typing_stop:          { groupId: string; from: string };
  get_group_message_history:  { groupId: string; limit?: number; before?: number };
  group_message_history:      { groupId: string; messages: PersistedMessage[] };

  // Voice calling (P2P)
  call_get_ice_servers:       Record<string, never>;
  call_ice_servers:           { iceServers: IceServer[] };
  call_initiate:              { to: string; sdpOffer: string };
  call_incoming:              { callId: string; from: string; sdpOffer: string; iceServers: IceServer[] };
  call_ringing:               { callId: string };
  call_answer:                { callId: string; sdpAnswer: string };
  call_answered:              { callId: string; sdpAnswer: string };
  call_reject:                { callId: string };
  call_rejected:              { callId: string };
  call_hangup:                { callId: string };
  call_ended:                 { callId: string };
  call_busy:                  { callId: string };
  call_ice_candidate:         { callId: string; candidate: IceCandidateInit };

  // Group conference calling (SFU)
  conference_join:            { groupId: string };
  conference_leave:           { groupId: string };
  conference_token:           { groupId: string; sfuUrl: string; token: string };
  conference_started:         { groupId: string; startedBy: string; participantCount: number };
  conference_ended:           { groupId: string };
  conference_participant_joined: { groupId: string; userId: string; participantCount: number };
  conference_participant_left:   { groupId: string; userId: string; participantCount: number };
  conference_error:           { groupId: string; error: string };
}

User & UserConnection

User

Represents an authenticated connected user. Used internally by ConnectionManager.

interface User {
  id: string;           // The authenticated userId (from AuthHandler)
  socketId: string;     // Socket.IO socket ID for this connection
  connectedAt: number;  // Unix timestamp (ms) when connected
  metadata?: Record<string, any>;
}

UserConnection

A lightweight snapshot of a user's connection state.

interface UserConnection {
  userId: string;
  socketId: string;
  isOnline: boolean;
}

Message Types

Message

In-flight message shape (before persistence).

interface Message {
  id: string;
  from: string;      // sender userId
  to: string;        // recipient userId
  content: string;
  timestamp: number; // Unix timestamp (ms)
  delivered: boolean;
  metadata?: Record<string, any>;
}

PersistedMessage

The unified persisted message shape used by IMessageStore. Handles both 1:1 and group messages.

interface PersistedMessage {
  id: string;
  conversationId: string; // "${userA}:${userB}" (sorted) for 1:1
                          // groupId for group messages
  from: string;
  to?: string;            // set for 1:1 messages
  groupId?: string;       // set for group messages
  content: string;
  timestamp: number;      // Unix timestamp (ms)
  metadata?: Record<string, any>;
  deliveredTo: string[];  // userIds message was delivered to
  readBy: string[];       // userIds who have read the message
}

MessageOptions

interface MessageOptions {
  requireAcknowledgment?: boolean;
  timeout?: number; // ms
}

PaginationOpts

Used by IMessageStore.getMessages() and passed through get_message_history / get_group_message_history events.

interface PaginationOpts {
  limit: number;
  before?: number; // Unix timestamp — fetch messages before this time
  after?: number;  // Unix timestamp — fetch messages after this time
}

Group Types

Group

interface Group {
  id: string;
  name: string;
  createdBy: string;  // userId of the creator
  createdAt: number;  // Unix timestamp (ms)
  members: string[];  // array of userIds
  metadata?: Record<string, any>;
}

GroupMember

type GroupMemberRole = 'admin' | 'member';

interface GroupMember {
  userId: string;
  role: GroupMemberRole; // 'admin' | 'member'
  joinedAt: number;      // Unix timestamp (ms)
}

GroupMessage

interface GroupMessage {
  id: string;
  groupId: string;
  from: string;      // sender userId
  content: string;
  timestamp: number; // Unix timestamp (ms)
  metadata?: Record<string, any>;
  deliveredTo: string[]; // userIds message was delivered to
  readBy: string[];      // userIds who have read the message
}

Voice Call Types

CallStatus

type CallStatus = 'initiating' | 'ringing' | 'active' | 'ended' | 'rejected' | 'busy';

IceServer

Mirrors RTCIceServer — safe to cast directly on the client. Used in call_incoming and call_ice_servers payloads.

interface IceServer {
  urls: string | string[];
  username?: string;
  credential?: string;
}

IceCandidateInit

Serialisable form of RTCIceCandidateInit — safe to transmit over a socket.

interface IceCandidateInit {
  candidate?: string;
  sdpMid?: string | null;
  sdpMLineIndex?: number | null;
  usernameFragment?: string | null;
}

ActiveCall

Server-side representation of an in-flight call, keyed by callId.

interface ActiveCall {
  callId: string;
  callerId: string;
  calleeId: string;
  status: CallStatus;
  startedAt: number; // Unix timestamp (ms)
}

Conference Types

ConferenceParticipant

interface ConferenceParticipant {
  userId: string;
  joinedAt: number; // Unix timestamp (ms)
}

ConferenceRoom

Server-side representation of an active conference room, keyed by groupId.

interface ConferenceRoom {
  groupId: string;
  participants: ConferenceParticipant[];
  startedAt: number;
  startedBy: string;
}

SFU Provider Types

ISFUProvider (server-side)

Implement this interface to connect Sockr's ConferencePlugin to any SFU. Sockr ships LiveKitSFUProvider as the reference implementation.

interface ISFUProvider {
  connect?(): Promise<void>;
  disconnect?(): Promise<void>;
  generateToken(roomName: string, participantIdentity: string, opts?: TokenOptions): Promise<string>;
  createRoom?(roomName: string, opts?: RoomOptions): Promise<void>;
  deleteRoom?(roomName: string): Promise<void>;
}

TokenOptions

interface TokenOptions {
  ttl?: number;        // Credential lifetime in seconds. Defaults to 3600.
  canPublish?: boolean; // Whether participant can publish audio/video. Defaults to true.
  canSubscribe?: boolean; // Whether participant can subscribe. Defaults to true.
}

RoomOptions

interface RoomOptions {
  emptyTimeout?: number; // Seconds before an empty room is auto-deleted. Defaults to 300.
}

ISFUClientAdapter (client-side)

Client-side SFU abstraction used by useConferenceCall(). Sockr ships LiveKitClientAdapter as the reference implementation.

interface ISFUClientAdapter {
  connect(sfuUrl: string, token: string, opts?: SFUConnectOptions): Promise<void>;
  disconnect(): Promise<void>;
  publishTracks(opts?: SFUPublishOptions): Promise<void>;
  onTrackSubscribed(handler: (track: MediaStreamTrack, participantId: string) => void): void;
  onTrackUnsubscribed(handler: (track: MediaStreamTrack, participantId: string) => void): void;
  onParticipantConnected(handler: (participantId: string) => void): void;
  onParticipantDisconnected(handler: (participantId: string) => void): void;
  onDisconnected(handler: () => void): void;
}

interface SFUConnectOptions {
  audio?: boolean; // Publish audio immediately. Default: true.
  video?: boolean; // Publish video immediately. Default: false.
}

interface SFUPublishOptions {
  audio?: boolean;
  video?: boolean;
}

Config Types

ServerConfig

interface ServerConfig {
  port?: number;
  cors?: {
    origin: string | string[];
    credentials?: boolean;
  };
  pingTimeout?: number;
  pingInterval?: number;
  transports?: ('websocket' | 'polling')[];
  providers?: {
    messageStore?: IMessageStore;
    queue?: IQueueProvider;
    cache?: ICacheProvider;
  };
  /** Voice calling configuration. Required to enable the VoicePlugin. */
  voice?: VoiceConfig;
}

VoiceConfig

interface VoiceConfig {
  /** Static ICE servers (e.g. a public STUN). Always included as-is. */
  iceServers?: IceServer[];
  /** Coturn TURN config. When provided, Sockr generates short-lived HMAC-SHA1 credentials per user. The secret never reaches the client. */
  turn?: TurnConfig;
}

interface TurnConfig {
  urls: string | string[];   // TURN server URL(s), e.g. "turn:turn.example.com:3478"
  secret: string;            // static-auth-secret from turnserver.conf
  ttl?: number;              // Credential lifetime in seconds. Defaults to 3600.
}

ClientConfig

interface ClientConfig {
  url: string;
  autoConnect?: boolean;
  reconnection?: boolean;
  reconnectionAttempts?: number;
  reconnectionDelay?: number;
  timeout?: number;
  transports?: ('websocket' | 'polling')[];
}

IMessageStore

Persistence contract for all messages and group metadata. Implement this interface to use any database with Sockr.

import type { IMessageStore } from 'sockr-shared';

class MyStore implements IMessageStore {
  // implement all methods
}

new SocketServer({
  providers: { messageStore: new MyStore() }
});
interface IMessageStore {
  // ── Messages ──────────────────────────────────────────────
  /** Persist a message. Called before delivery is attempted. */
  saveMessage(message: PersistedMessage): Promise<PersistedMessage>;

  /** Retrieve a single message by ID. */
  getMessage(messageId: string): Promise<PersistedMessage | null>;

  /**
   * Paginated message history for a conversation.
   * conversationId is "${userA}:${userB}" (sorted) for 1:1,
   * or the groupId for group conversations.
   */
  getMessages(
    conversationId: string,
    opts: PaginationOpts
  ): Promise<PersistedMessage[]>;

  /** Return messages not yet delivered to the given userId. */
  getUndeliveredMessages(userId: string): Promise<PersistedMessage[]>;

  /** Mark a message as delivered to a specific user. */
  markDelivered(messageId: string, userId: string): Promise<void>;

  /** Mark a message as read by a specific user. */
  markRead(messageId: string, userId: string): Promise<void>;

  // ── Groups ────────────────────────────────────────────────
  saveGroup(group: Group): Promise<Group>;
  getGroup(groupId: string): Promise<Group | null>;
  updateGroup(
    groupId: string,
    updates: Partial<Pick<Group, 'name' | 'metadata'>>
  ): Promise<Group>;
  deleteGroup(groupId: string): Promise<void>;
  addGroupMember(groupId: string, member: GroupMember): Promise<void>;
  removeGroupMember(groupId: string, userId: string): Promise<void>;
  getGroupMembers(groupId: string): Promise<GroupMember[]>;
  getUserGroups(userId: string): Promise<Group[]>;
}

IQueueProvider

Offline message queue contract. Used to enqueue messages for offline users and flush them on reconnect.

interface IQueueProvider {
  /** Enqueue a message for an offline user. */
  enqueue(userId: string, message: PersistedMessage): Promise<void>;

  /** Dequeue and return all pending messages for a user. */
  dequeue(userId: string): Promise<PersistedMessage[]>;

  /** Optional: called by Sockr to open a connection (e.g. Redis). */
  connect?(): Promise<void>;

  /** Optional: called by Sockr to close the connection. */
  disconnect?(): Promise<void>;
}

ICacheProvider

General-purpose cache contract. Implement for any key-value store.

interface ICacheProvider {
  /** Retrieve a cached value by key. Returns null if not found. */
  get(key: string): Promise<string | null>;

  /** Store a value. Optional TTL in seconds. */
  set(key: string, value: string, ttl?: number): Promise<void>;

  /** Delete a cached value. */
  delete(key: string): Promise<void>;

  /** Optional: called by Sockr to open a connection. */
  connect?(): Promise<void>;

  /** Optional: called by Sockr to close the connection. */
  disconnect?(): Promise<void>;
}
Implementing custom providers: All optional connect() and disconnect() methods are called automatically by SocketServer during listen() / initialize() and close(). In-memory implementations can safely omit them.
← Getting Started Server Reference