Keeps the set of open sockets and sends them the server's messages.
The WebSocket route (routes/ws.ts) registers each socket and handles what
it sends. The services and routes that change dumps, comments, playlists and
users call the broadcast* functions here after the change is stored. A
broadcast* function sends to everyone unless its doc names an audience, so
its caller must not pass it anything private. Its jobs ping every socket,
closing the ones that stop answering, and expire the typing indicators.
Sends everyone categories_changed, after the category manager created,
edited or deleted one, so that every client fetches them again.
Sends chat_message to every signed-in socket, the sender's included.
nonce comes from the sender's chat_send, and lets it swap its pending
copy for the message.
Sends chat_message_deleted to every signed-in socket.
Sends chat_message_updated to every signed-in socket.
Sends comment_created to those who may see the comment's dump: everyone,
or its owner alone when it is private. The comments route calls it, as it
does the two below.
Sends comment_likes_update after the route stored a like, to everyone, or
with onlyUserId to that user's sockets alone (for a comment on a private
dump).
Sends comment_updated, as broadcastCommentCreated does.
Tells the clients that may see a dump how it changed, as broadcastChange says, for the dump service after every write.
Tells the clients that may see a playlist how it changed, as
broadcastChange says, for the playlist service after every write to
its details. A playlist turned private thus leaves the others' views without
their learning what it became. members are the dumps of after: each
socket gets the playlist with the count of those its user may see and the
date of the latest, the owner's private dumps being theirs alone to count.
Sends playlist_dumps_updated to the playlist's audience, each socket with
only the dumps its user may see, as GET would list them. The private dumps
in a public playlist stay hidden from everyone but their posters, and so
does their number: a client counts the dumps it is sent.
Sends everyone presence_update. The route calls it whenever a socket opens
or closes.
Sends chat_typing_update to every signed-in socket. The route also calls
it when a typing user's socket closes.
Sends everyone user_updated, after a profile edit, a role change or an
avatar upload.
Sends votes_update after the route stored a vote, to everyone, or with
onlyUserId to that user's sockets alone (for a private dump).
Closes every socket of userId without signing anyone out. The user service
calls it on a password change. Each tab reconnects with the token it holds:
the tab that made the change has a fresh one and stays signed in, while the
others connect anonymously and sign out on their next 401.
Returns each signed-in user with a socket open, once. The route sends it in
welcome, and broadcastPresence in presence_update.
Returns each user who sent chat_typing true within the last
TYPING_TTL, once, for broadcastTyping.
Records a client's pong, which keeps its socket open.
Checks whether userId is reading the chat on any of their sockets. The
route passes it to notifyMentions, which skips those users: they saw the
mention live, and their client chimes for it.
Adds a client once its socket opens. From then on, it receives the broadcasts, and is shown online if it is signed in.
Sends data to one socket unless it is closing. The WebSocket route answers
a client's own message with it: a pong, a vote's ack, an error.
Sends data to every socket of userId. The notification service sends
notification_created and notifications_read with it, the follow service
follows_changed, and the route chat_read_update.
Records a client's chat_typing, and broadcasts the typing list when the
client starts or stops. The route also clears it when the client sends a
message. A client that stops without saying so is expired after
TYPING_TTL.
Takes one token from key's bucket, or returns false when it is empty. The
caller picks the key, such as typing:<userId>, and keys by user or account
so that more tabs or parallel requests don't buy more tokens.
Removes a client when its socket closes. The caller rebroadcasts presence.
Records userId's new avatar on their clients, so presence and the typing
list show it, and rebroadcasts presence. routes/avatars.ts calls it on an
upload.
Records userId's new name on their clients, so presence and the typing
list show it, and rebroadcasts presence. The routes that rename a user call
it, along with broadcastUserUpdated.
Passed to takeToken by each limited action, which declares its own as a constant.
-
capacity: number
How many tokens a full bucket holds, and so the largest burst.
-
refill: number
Tokens regained per second.
Created by the WebSocket route for each socket it opens, and kept here until the socket closes. The user fields are read at connect and left undefined for an anonymous socket.
- avatarMime: string
-
avatarVersion: number
Set by updateClientAvatar, for the client's avatar URLs.
-
chatOpen: boolean
Set by
chat_focus: the chatbox is open and the tab visible, so the user is reading the chat and gets no notification for a mention in it. -
pongReceived: boolean
Undefined until the first ping, so a new socket isn't closed at once.
-
role: Role
Read again from the database on each chat edit or delete, so a demoted moderator loses the right at once.
- socket: WebSocket
-
typingAt: number
When the last
chat_typingtrue arrived, in ms. - userId: string
- username: string
Pings every client each PING_INTERVAL, closing the ones that didn't
answer the previous ping. main.ts starts it.
Expires the typing users who went quiet without a false, such as by closing
the tab, every 2 seconds. main.ts starts it.
Usage
import * as mod from "api/services/ws-service.ts";