@rlanz/socket 0.0.1-0 → 0.0.1-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -11
- package/build/{chunk-4UPUCRVG.js → chunk-5X4SKZQS.js} +39 -24
- package/build/chunk-5X4SKZQS.js.map +1 -0
- package/build/index.js +4 -4
- package/build/providers/socket_provider.js +1 -1
- package/build/src/client/index.d.ts +12 -0
- package/build/src/client/index.js +29 -0
- package/build/src/client/index.js.map +1 -1
- package/package.json +1 -1
- package/build/chunk-4UPUCRVG.js.map +0 -1
package/README.md
CHANGED
|
@@ -263,13 +263,11 @@ Incoming client messages are handled through the `handlers` map, the `@onMessage
|
|
|
263
263
|
|
|
264
264
|
```ts
|
|
265
265
|
import { BaseChannel } from '@rlanz/socket'
|
|
266
|
-
import { onMessage } from '@rlanz/socket/decorators'
|
|
267
266
|
import type { AuthenticatedSocket } from '@rlanz/socket/types'
|
|
268
267
|
|
|
269
268
|
type User = { id: string; name: string }
|
|
270
269
|
type Events = {
|
|
271
270
|
'chat:send': { body: string }
|
|
272
|
-
'chat:typing': { typing: boolean }
|
|
273
271
|
}
|
|
274
272
|
|
|
275
273
|
export default class ChatChannel extends BaseChannel<User, Events> {
|
|
@@ -288,14 +286,6 @@ export default class ChatChannel extends BaseChannel<User, Events> {
|
|
|
288
286
|
return { delivered: true }
|
|
289
287
|
}
|
|
290
288
|
|
|
291
|
-
@onMessage('chat:typing')
|
|
292
|
-
async typing(socket: AuthenticatedSocket<User>, data: Events['chat:typing']) {
|
|
293
|
-
this.broadcastExcept(socket.id, 'chat:typing', {
|
|
294
|
-
user: socket.user,
|
|
295
|
-
typing: data.typing,
|
|
296
|
-
})
|
|
297
|
-
}
|
|
298
|
-
|
|
299
289
|
async onMessage(socket: AuthenticatedSocket<User>, event: string, data: unknown) {
|
|
300
290
|
console.log('Unhandled channel message', event, data)
|
|
301
291
|
}
|
|
@@ -304,6 +294,25 @@ export default class ChatChannel extends BaseChannel<User, Events> {
|
|
|
304
294
|
|
|
305
295
|
Returning a value from a handler resolves `sendWithAck()` on the client.
|
|
306
296
|
|
|
297
|
+
### Client-to-client events
|
|
298
|
+
|
|
299
|
+
Use `whisper()` for ephemeral events that should be relayed to the other members of the same
|
|
300
|
+
channel without writing a channel message handler. Whispers are only accepted from subscribed
|
|
301
|
+
sockets and are delivered to everyone else on the channel.
|
|
302
|
+
|
|
303
|
+
```ts
|
|
304
|
+
channel.listenForWhisper('typing', (payload) => {
|
|
305
|
+
console.log('typing status', payload)
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
channel.whisper('typing', {
|
|
309
|
+
typing: true,
|
|
310
|
+
})
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
Whispered events are delivered under an internal `client:` prefix, so they cannot impersonate
|
|
314
|
+
server-emitted channel events.
|
|
315
|
+
|
|
307
316
|
## Client
|
|
308
317
|
|
|
309
318
|
Import the browser client from `@rlanz/socket/client`.
|
|
@@ -343,10 +352,13 @@ channel
|
|
|
343
352
|
.listen('chat:message', (message) => {
|
|
344
353
|
console.log('new message', message)
|
|
345
354
|
})
|
|
355
|
+
.listenForWhisper('typing', (payload) => {
|
|
356
|
+
console.log('typing', payload)
|
|
357
|
+
})
|
|
346
358
|
|
|
347
359
|
await channel.subscribe()
|
|
348
360
|
|
|
349
|
-
channel.
|
|
361
|
+
channel.whisper('typing', { typing: true })
|
|
350
362
|
|
|
351
363
|
const ack = await channel.sendWithAck('chat:send', {
|
|
352
364
|
body: 'Hello from the client',
|
|
@@ -355,6 +367,7 @@ const ack = await channel.sendWithAck('chat:send', {
|
|
|
355
367
|
console.log(ack)
|
|
356
368
|
|
|
357
369
|
channel.stopListening('chat:message')
|
|
370
|
+
channel.stopListeningForWhisper('typing')
|
|
358
371
|
await channel.unsubscribe()
|
|
359
372
|
await socket.leave('chat/general')
|
|
360
373
|
socket.disconnect()
|
|
@@ -607,6 +607,13 @@ var ChannelSubscriptions = class {
|
|
|
607
607
|
};
|
|
608
608
|
}
|
|
609
609
|
}
|
|
610
|
+
relayWhisper(socket, payload) {
|
|
611
|
+
if (!this.#storage.has(socket.id, payload.channel)) {
|
|
612
|
+
return { ok: false, error: "Not subscribed" };
|
|
613
|
+
}
|
|
614
|
+
this.socketService.to(payload.channel).except(socket.id).emit(`client:${payload.event}`, payload.data);
|
|
615
|
+
return { ok: true };
|
|
616
|
+
}
|
|
610
617
|
deleteSocket(socketId) {
|
|
611
618
|
this.#storage.deleteSocket(socketId);
|
|
612
619
|
}
|
|
@@ -722,8 +729,8 @@ var Message = class _Message {
|
|
|
722
729
|
if (value.type === "unsubscribe" && typeof value.channel === "string") {
|
|
723
730
|
return new UnsubscribeMessage(id, value.channel);
|
|
724
731
|
}
|
|
725
|
-
if (value.type === "message" && typeof value.channel === "string" && typeof value.event === "string") {
|
|
726
|
-
return new ChannelProtocolMessage(id, value.channel, value.event, value.data);
|
|
732
|
+
if ((value.type === "message" || value.type === "whisper") && typeof value.channel === "string" && typeof value.event === "string") {
|
|
733
|
+
return new ChannelProtocolMessage(id, value.type, value.channel, value.event, value.data);
|
|
727
734
|
}
|
|
728
735
|
return new InvalidMessage(id);
|
|
729
736
|
}
|
|
@@ -766,15 +773,15 @@ var UnsubscribeMessage = class extends Message {
|
|
|
766
773
|
type = "unsubscribe";
|
|
767
774
|
};
|
|
768
775
|
var ChannelProtocolMessage = class extends Message {
|
|
769
|
-
constructor(id, channel, event, data) {
|
|
776
|
+
constructor(id, type, channel, event, data) {
|
|
770
777
|
super();
|
|
771
778
|
this.id = id;
|
|
779
|
+
this.type = type;
|
|
772
780
|
this.channel = channel;
|
|
773
781
|
this.event = event;
|
|
774
782
|
this.data = data;
|
|
775
783
|
}
|
|
776
784
|
valid = true;
|
|
777
|
-
type = "message";
|
|
778
785
|
toChannelMessage() {
|
|
779
786
|
return {
|
|
780
787
|
channel: this.channel,
|
|
@@ -784,7 +791,7 @@ var ChannelProtocolMessage = class extends Message {
|
|
|
784
791
|
}
|
|
785
792
|
};
|
|
786
793
|
function isClientMessageType(value) {
|
|
787
|
-
return value === "ping" || value === "subscribe" || value === "unsubscribe" || value === "message";
|
|
794
|
+
return value === "ping" || value === "subscribe" || value === "unsubscribe" || value === "message" || value === "whisper";
|
|
788
795
|
}
|
|
789
796
|
|
|
790
797
|
// src/message_queue.ts
|
|
@@ -1257,29 +1264,37 @@ var SocketService = class extends Emittery {
|
|
|
1257
1264
|
}, traceMessage);
|
|
1258
1265
|
return;
|
|
1259
1266
|
}
|
|
1260
|
-
case "message":
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
channel: payload.channel,
|
|
1265
|
-
event: payload.event
|
|
1266
|
-
};
|
|
1267
|
-
await channelMessageChannel.tracePromise(async () => {
|
|
1268
|
-
const ack = await this.#handleChannelMessage(socket, payload);
|
|
1269
|
-
traceMessage.ok = ack.ok;
|
|
1270
|
-
if (!ack.ok) {
|
|
1271
|
-
traceMessage.error = ack.error ?? "Handler error";
|
|
1272
|
-
}
|
|
1273
|
-
sendFrame(socket.raw.connection, Reply.fromChannelAck(message.id, ack).toFrame());
|
|
1274
|
-
}, traceMessage);
|
|
1267
|
+
case "message":
|
|
1268
|
+
case "whisper": {
|
|
1269
|
+
await this.#handleChannelFrame(socket, message);
|
|
1270
|
+
return;
|
|
1275
1271
|
}
|
|
1276
1272
|
}
|
|
1277
1273
|
}
|
|
1278
|
-
async #
|
|
1274
|
+
async #handleChannelFrame(socket, message) {
|
|
1275
|
+
const payload = message.toChannelMessage();
|
|
1276
|
+
const traceMessage = {
|
|
1277
|
+
socketId: socket.id,
|
|
1278
|
+
channel: payload.channel,
|
|
1279
|
+
event: payload.event
|
|
1280
|
+
};
|
|
1281
|
+
await channelMessageChannel.tracePromise(async () => {
|
|
1282
|
+
const ack = await this.#handleChannelPayload(socket, message.type, payload);
|
|
1283
|
+
traceMessage.ok = ack.ok;
|
|
1284
|
+
if (!ack.ok) {
|
|
1285
|
+
traceMessage.error = ack.error ?? "Handler error";
|
|
1286
|
+
}
|
|
1287
|
+
sendFrame(socket.raw.connection, Reply.fromChannelAck(message.id, ack).toFrame());
|
|
1288
|
+
}, traceMessage);
|
|
1289
|
+
}
|
|
1290
|
+
async #handleChannelPayload(socket, type, payload) {
|
|
1279
1291
|
if (typeof payload.channel !== "string" || typeof payload.event !== "string") {
|
|
1280
|
-
return { ok: false, error:
|
|
1292
|
+
return { ok: false, error: `Invalid channel ${type}` };
|
|
1293
|
+
}
|
|
1294
|
+
if (type === "message") {
|
|
1295
|
+
return this.#channelSubscriptions.handleMessage(socket, payload);
|
|
1281
1296
|
}
|
|
1282
|
-
return this.#channelSubscriptions.
|
|
1297
|
+
return this.#channelSubscriptions.relayWhisper(socket, payload);
|
|
1283
1298
|
}
|
|
1284
1299
|
#wrapSocket(connection, request, upgrade) {
|
|
1285
1300
|
const raw = {
|
|
@@ -1769,4 +1784,4 @@ export {
|
|
|
1769
1784
|
SocketService,
|
|
1770
1785
|
ChannelRouter
|
|
1771
1786
|
};
|
|
1772
|
-
//# sourceMappingURL=chunk-
|
|
1787
|
+
//# sourceMappingURL=chunk-5X4SKZQS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/presence_manager.ts","../src/base_channel.ts","../src/fake_socket.ts","../src/socket_service.ts","../src/channel_subscription_storage.ts","../src/channel_subscriptions.ts","../src/protocol/reply.ts","../src/protocol/message.ts","../src/message_queue.ts","../src/broadcasting.ts","../src/socket_bus.ts","../src/socket_upgrader.ts","../src/channel_pattern.ts","../src/channel_router.ts"],"sourcesContent":["import type { PresenceMember } from './types.js'\n\nexport const PRESENCE_DATA_KEY = '__rlanzSocketPresence'\n\ntype PresenceSocketData = Record<string, unknown> & {\n [PRESENCE_DATA_KEY]?: Record<string, PresenceUser>\n}\n\nexport interface PresenceUser {\n id: string\n name: string\n joinedAt: Date\n [key: string]: unknown\n}\n\nexport interface PresenceData {\n channel: string\n users: PresenceUser[]\n count: number\n}\n\nexport interface PresenceSocket {\n id: string\n data: PresenceSocketData\n}\n\n/**\n * Tracks user presence in channels.\n *\n * channel -> socketId -> PresenceUser\n */\nexport class PresenceManager {\n #presence = new Map<string, Map<string, PresenceUser>>()\n #fetchSockets: ((channel: string) => Promise<PresenceSocket[]>) | null = null\n\n /**\n * Sets the socket fetcher used to build distributed snapshots.\n */\n setSocketFetcher(fetchSockets: (channel: string) => Promise<PresenceSocket[]>): void {\n this.#fetchSockets = fetchSockets\n }\n\n /**\n * Adds a user to a channel.\n */\n join(channel: string, socket: PresenceSocket, member: string | PresenceMember): void {\n if (!this.#presence.has(channel)) {\n this.#presence.set(channel, new Map())\n }\n\n const presenceUser =\n typeof member === 'string'\n ? { id: socket.id, name: member }\n : {\n ...member,\n id: member.id || socket.id,\n }\n\n const user = {\n ...presenceUser,\n joinedAt: new Date(),\n }\n\n this.#presence.get(channel)!.set(socket.id, user)\n this.#setSocketPresence(socket, channel, user)\n }\n\n /**\n * Removes a user from a channel.\n */\n leave(channel: string, socket: PresenceSocket): void {\n const channelPresence = this.#presence.get(channel)\n\n if (channelPresence) {\n channelPresence.delete(socket.id)\n\n if (channelPresence.size === 0) {\n this.#presence.delete(channel)\n }\n }\n\n this.#deleteSocketPresence(socket, channel)\n }\n\n /**\n * Removes a user from every channel.\n * Returns the list of channels the user left.\n */\n leaveAll(socket: PresenceSocket): string[] {\n const leftChannels: string[] = []\n\n for (const [channel, users] of this.#presence) {\n if (users.has(socket.id)) {\n users.delete(socket.id)\n leftChannels.push(channel)\n\n if (users.size === 0) {\n this.#presence.delete(channel)\n }\n\n this.#deleteSocketPresence(socket, channel)\n }\n }\n\n return leftChannels\n }\n\n /**\n * Gets presence data for a channel.\n */\n async snapshot(channel: string): Promise<PresenceData> {\n if (this.#fetchSockets) {\n return this.#getFromSockets(channel)\n }\n\n return this.#localSnapshot(channel)\n }\n\n /**\n * Gets the number of users in a channel.\n */\n async count(channel: string): Promise<number> {\n return (await this.snapshot(channel)).count\n }\n\n /**\n * Checks whether a socket is locally present in a channel.\n */\n hasLocal(channel: string, socketId: string): boolean {\n return this.#presence.get(channel)?.has(socketId) || false\n }\n\n /**\n * Lists every local channel where a socket is present.\n */\n getLocalChannelsForSocket(socketId: string): string[] {\n const channels: string[] = []\n\n for (const [channel, users] of this.#presence) {\n if (users.has(socketId)) {\n channels.push(channel)\n }\n }\n\n return channels\n }\n\n /**\n * Lists sockets that are locally present in a channel.\n */\n getLocalSockets(channel: string): PresenceSocket[] {\n const users = this.#presence.get(channel)\n\n if (!users) {\n return []\n }\n\n return Array.from(users.entries()).map(([socketId, user]) => ({\n id: socketId,\n data: {\n [PRESENCE_DATA_KEY]: {\n [channel]: user,\n },\n },\n }))\n }\n\n #localSnapshot(channel: string): PresenceData {\n const users = this.#presence.get(channel)\n\n return {\n channel,\n users: users ? Array.from(users.values()) : [],\n count: users?.size || 0,\n }\n }\n\n async #getFromSockets(channel: string): Promise<PresenceData> {\n const sockets = await this.#fetchSockets!(channel)\n const users = sockets\n .map((socket) => socket.data?.[PRESENCE_DATA_KEY]?.[channel])\n .filter((user): user is PresenceUser => Boolean(user))\n\n return {\n channel,\n users,\n count: users.length,\n }\n }\n\n #setSocketPresence(socket: PresenceSocket, channel: string, user: PresenceUser): void {\n socket.data[PRESENCE_DATA_KEY] ??= {}\n socket.data[PRESENCE_DATA_KEY][channel] = user\n }\n\n #deleteSocketPresence(socket: PresenceSocket, channel: string): void {\n if (!socket.data[PRESENCE_DATA_KEY]) {\n return\n }\n\n delete socket.data[PRESENCE_DATA_KEY][channel]\n\n if (Object.keys(socket.data[PRESENCE_DATA_KEY]).length === 0) {\n delete socket.data[PRESENCE_DATA_KEY]\n }\n }\n}\n","import type {\n AuthenticatedSocket,\n ChannelOptions,\n PresenceMember,\n Middleware,\n MiddlewareContext,\n ChannelEventMap,\n ChannelHandlerMap,\n} from './types.js'\nimport type { PresenceManager } from './presence_manager.js'\nimport { getDecoratedChannelHandlers } from './decorators.js'\n\ninterface ChannelBroadcaster {\n to(channel: string): {\n emit(event: string, data: unknown): void\n except(socketId: string): {\n emit(event: string, data: unknown): void\n }\n }\n}\n\nexport class MissingPresenceInfoError extends Error {\n constructor() {\n super('Presence channels must implement getPresenceInfo(socket)')\n }\n}\n\n/**\n * Abstract class used to define a channel.\n *\n * @example\n * ```ts\n * export default class ChatChannel extends BaseChannel {\n * static pattern = 'chat/:roomId'\n * static middlewares = [\n * async (ctx, next) => {\n * // Authorize access to the channel through middleware.\n * // if (!Room.isMember(ctx.params.roomId, ctx.socket.user.id)) throw new Error('Forbidden')\n * await next()\n * },\n * ]\n * }\n * ```\n *\n * @example With presence and decorated handlers\n * ```ts\n * import { onMessage } from '@rlanz/socket/decorators'\n *\n * export default class ChatChannel extends BaseChannel {\n * static pattern = 'chat/:roomId'\n * static options = { presence: true }\n *\n * static middlewares = [\n * async (ctx, next) => {\n * if (!ctx.socket.user.isVerified) throw new Error('Not verified')\n * await next()\n * }\n * ]\n *\n * @onMessage('chat:send')\n * async handleSend(socket: AuthenticatedSocket<User>, data: unknown) {\n * // Handle chat:send event\n * }\n *\n * @onMessage('chat:typing')\n * async handleTyping(socket: AuthenticatedSocket<User>, data: unknown) {\n * // Handle chat:typing event\n * }\n * }\n * ```\n */\nexport class BaseChannel<\n User = unknown,\n Events extends ChannelEventMap = ChannelEventMap,\n HttpContext = unknown,\n> {\n /**\n * URL-like channel pattern, for example: 'chat/:roomId'.\n */\n static pattern: string\n\n /**\n * Channel options.\n */\n static options?: ChannelOptions\n\n /**\n * Middlewares to run before subscription.\n */\n static middlewares?: Middleware<any, any>[]\n\n /**\n * Event handler map.\n * Ex: { 'chat:send': 'handleSend' }\n */\n protected handlers: ChannelHandlerMap<User, Events, HttpContext> = {}\n\n /**\n * Injected socket service.\n */\n protected socket!: ChannelBroadcaster\n\n /**\n * Full channel name, for example: 'chat/general'.\n */\n protected channelName!: string\n\n /**\n * Parameters extracted from the pattern, for example: { roomId: 'general' }.\n */\n protected params!: Record<string, string>\n\n /**\n * Injected presence manager when presence is enabled.\n */\n protected presence!: PresenceManager\n\n /**\n * Injects dependencies.\n */\n $setContext(\n socket: ChannelBroadcaster,\n channelName: string,\n params: Record<string, string>,\n presence?: PresenceManager\n ): void {\n this.socket = socket\n this.channelName = channelName\n this.params = params\n if (presence) {\n this.presence = presence\n }\n }\n\n /**\n * Emits to every channel member.\n */\n protected broadcast(event: string, data: unknown): void {\n this.socket.to(this.channelName).emit(event, data)\n }\n\n /**\n * Emits to every channel member except one socket.\n */\n protected broadcastExcept(socketId: string, event: string, data: unknown): void {\n this.socket.to(this.channelName).except(socketId).emit(event, data)\n }\n\n /**\n * Runs the middleware chain.\n *\n * middleware[0] -> middleware[1] -> ... -> subscribe\n */\n async $runMiddlewares(ctx: MiddlewareContext<User, HttpContext>): Promise<void> {\n const middlewares = (this.constructor as typeof BaseChannel).middlewares || []\n\n let index = 0\n\n const next = async (): Promise<void> => {\n if (index >= middlewares.length) return\n\n const middleware = middlewares[index++]\n\n if (typeof middleware === 'function') {\n await middleware(ctx, next)\n } else {\n await middleware.handle(ctx, next)\n }\n }\n\n await next()\n }\n\n /**\n * Returns member information for presence.\n * Implement when options.presence = true.\n */\n getPresenceInfo?(socket: AuthenticatedSocket<User, HttpContext>): PresenceMember\n\n /**\n * Hook called when a socket joins the channel.\n */\n onJoin?(socket: AuthenticatedSocket<User, HttpContext>, ...params: string[]): Promise<void>\n\n /**\n * Hook called when a socket leaves the channel.\n */\n onLeave?(socket: AuthenticatedSocket<User, HttpContext>, ...params: string[]): Promise<void>\n\n /**\n * Hook called when a message is received on the channel.\n * Used as fallback when no event handler matches.\n */\n onMessage?(\n socket: AuthenticatedSocket<User, HttpContext>,\n event: string,\n data: unknown\n ): Promise<void>\n\n /**\n * Hook called when a member joins a presence channel.\n */\n onMemberJoin?(\n socket: AuthenticatedSocket<User, HttpContext>,\n member: PresenceMember,\n ...params: string[]\n ): Promise<void>\n\n /**\n * Hook called when a member leaves a presence channel.\n */\n onMemberLeave?(\n socket: AuthenticatedSocket<User, HttpContext>,\n member: PresenceMember,\n ...params: string[]\n ): Promise<void>\n\n /**\n * Dispatches a message to the matching handler.\n * Called by SocketService. Do not override.\n */\n async $handleMessage(\n socket: AuthenticatedSocket<User, HttpContext>,\n event: string,\n data: unknown\n ): Promise<unknown> {\n const handlers = {\n ...(this.handlers as Record<string, unknown>),\n ...(getDecoratedChannelHandlers<User, Events, HttpContext>(this) as Record<string, unknown>),\n }\n const handler = handlers[event]\n\n if (typeof handler === 'function') {\n return (\n handler as (socket: AuthenticatedSocket<User, HttpContext>, data: unknown) => unknown\n ).call(this, socket, data)\n }\n\n // Fall back to onMessage when it is defined.\n return this.onMessage?.(socket, event, data)\n }\n\n /**\n * Runs the generic join hook. ChannelSubscriptions owns subscription lifecycle.\n */\n async $handleJoin(\n socket: AuthenticatedSocket<User, HttpContext>,\n ...params: string[]\n ): Promise<void> {\n await this.onJoin?.(socket, ...params)\n }\n\n /**\n * Runs the generic leave hook. ChannelSubscriptions owns subscription lifecycle.\n */\n async $handleLeave(\n socket: AuthenticatedSocket<User, HttpContext>,\n ...params: string[]\n ): Promise<void> {\n await this.onLeave?.(socket, ...params)\n }\n\n $getPresenceInfo(socket: AuthenticatedSocket<User, HttpContext>): PresenceMember {\n if (!this.getPresenceInfo) {\n throw new MissingPresenceInfoError()\n }\n\n return this.getPresenceInfo(socket)\n }\n}\n","import { AssertionError } from 'node:assert'\nimport { isDeepStrictEqual } from 'node:util'\nimport type { SocketEmission, SocketEmissionSink, SocketEmissionTarget } from './broadcasting.js'\n\nexport type SocketFakeTarget = SocketEmissionTarget\nexport type SocketFakeEmission = SocketEmission\n\nexport interface SocketFakeEmissionQuery {\n data?: unknown | ((data: unknown) => boolean)\n except?: string[] | ((except: string[] | undefined) => boolean)\n}\n\nexport interface SocketFakeCountQuery extends SocketFakeEmissionQuery {\n target?: SocketFakeTarget\n event?: string\n channel?: string\n userId?: string | number\n}\n\nexport class SocketFake implements SocketEmissionSink {\n #emissions: SocketFakeEmission[] = []\n #restore: () => void\n\n constructor(restore: () => void) {\n this.#restore = restore\n }\n\n get emissions(): SocketFakeEmission[] {\n return [...this.#emissions]\n }\n\n record(emission: SocketFakeEmission): void {\n this.#emissions.push({\n ...emission,\n ...('except' in emission && emission.except ? { except: [...emission.except] } : {}),\n })\n }\n\n dispatch(emission: SocketFakeEmission): void {\n this.record(emission)\n }\n\n clear(): void {\n this.#emissions = []\n }\n\n restore(): void {\n this.#restore()\n }\n\n [Symbol.dispose](): void {\n this.restore()\n }\n\n assertBroadcasted(event: string, query?: SocketFakeEmissionQuery): void {\n this.#assertEmitted(\n (emission) => emission.target === 'global' && emission.event === event,\n query,\n `Expected socket event \"${event}\" to have been broadcasted`\n )\n }\n\n assertNotBroadcasted(event: string, query?: SocketFakeEmissionQuery): void {\n this.#assertNotEmitted(\n (emission) => emission.target === 'global' && emission.event === event,\n query,\n `Expected socket event \"${event}\" not to have been broadcasted`\n )\n }\n\n assertEmittedTo(channel: string, event: string, query?: SocketFakeEmissionQuery): void {\n this.#assertEmitted(\n (emission) =>\n emission.target === 'channel' && emission.channel === channel && emission.event === event,\n query,\n `Expected socket event \"${event}\" to have been emitted to channel \"${channel}\"`\n )\n }\n\n assertNotEmittedTo(channel: string, event: string, query?: SocketFakeEmissionQuery): void {\n this.#assertNotEmitted(\n (emission) =>\n emission.target === 'channel' && emission.channel === channel && emission.event === event,\n query,\n `Expected socket event \"${event}\" not to have been emitted to channel \"${channel}\"`\n )\n }\n\n assertEmittedToUser(\n userId: string | number,\n event: string,\n query?: SocketFakeEmissionQuery\n ): void {\n this.#assertEmitted(\n (emission) =>\n emission.target === 'user' && emission.userId === userId && emission.event === event,\n query,\n `Expected socket event \"${event}\" to have been emitted to user \"${String(userId)}\"`\n )\n }\n\n assertNotEmittedToUser(\n userId: string | number,\n event: string,\n query?: SocketFakeEmissionQuery\n ): void {\n this.#assertNotEmitted(\n (emission) =>\n emission.target === 'user' && emission.userId === userId && emission.event === event,\n query,\n `Expected socket event \"${event}\" not to have been emitted to user \"${String(userId)}\"`\n )\n }\n\n assertEmittedCount(count: number, query?: SocketFakeCountQuery): void {\n const actual = this.#matching(query).length\n\n if (actual !== count) {\n throw new AssertionError({\n message: `Expected ${count} socket event(s) to have been emitted, but found ${actual}`,\n actual,\n expected: count,\n operator: 'strictEqual',\n })\n }\n }\n\n assertNothingEmitted(): void {\n this.assertEmittedCount(0)\n }\n\n #assertEmitted(\n predicate: (emission: SocketFakeEmission) => boolean,\n query: SocketFakeEmissionQuery | undefined,\n message: string\n ): void {\n const matching = this.#emissions.filter((emission) => {\n return predicate(emission) && this.#matchesQuery(emission, query)\n })\n\n if (matching.length === 0) {\n throw new AssertionError({\n message,\n actual: this.#emissions,\n expected: query,\n operator: 'includes',\n })\n }\n }\n\n #assertNotEmitted(\n predicate: (emission: SocketFakeEmission) => boolean,\n query: SocketFakeEmissionQuery | undefined,\n message: string\n ): void {\n const matching = this.#emissions.filter((emission) => {\n return predicate(emission) && this.#matchesQuery(emission, query)\n })\n\n if (matching.length > 0) {\n throw new AssertionError({\n message,\n actual: matching,\n expected: [],\n operator: 'doesNotInclude',\n })\n }\n }\n\n #matching(query: SocketFakeCountQuery | undefined): SocketFakeEmission[] {\n return this.#emissions.filter((emission) => {\n if (query?.target && emission.target !== query.target) {\n return false\n }\n\n if (query?.event && emission.event !== query.event) {\n return false\n }\n\n if (query?.channel && (!('channel' in emission) || emission.channel !== query.channel)) {\n return false\n }\n\n if (\n query?.userId !== undefined &&\n (!('userId' in emission) || emission.userId !== query.userId)\n ) {\n return false\n }\n\n return this.#matchesQuery(emission, query)\n })\n }\n\n #matchesQuery(emission: SocketFakeEmission, query: SocketFakeEmissionQuery | undefined): boolean {\n if (!query) {\n return true\n }\n\n if ('data' in query) {\n if (typeof query.data === 'function') {\n if (!query.data(emission.data)) {\n return false\n }\n } else if (!isDeepStrictEqual(emission.data, query.data)) {\n return false\n }\n }\n\n if ('except' in query) {\n if (typeof query.except === 'function') {\n if (!query.except('except' in emission ? emission.except : undefined)) {\n return false\n }\n } else if (\n !isDeepStrictEqual(('except' in emission ? emission.except : undefined) ?? [], query.except)\n ) {\n return false\n }\n }\n\n return true\n }\n}\n","import { randomUUID } from 'node:crypto'\nimport type { IncomingMessage } from 'node:http'\nimport type { Server as HttpServer } from 'node:http'\nimport type { Logger } from '@adonisjs/logger'\nimport Emittery from 'emittery'\nimport { WebSocket, WebSocketServer } from 'ws'\nimport type {\n AuthenticatedSocket,\n SocketConfig,\n SocketEvents,\n ChannelAck,\n RawSocket,\n SocketHealthSnapshot,\n SocketServiceStatus,\n} from './types.js'\nimport type { ChannelMessage, ServerProtocolMessage } from './shared_types.js'\nimport type { ChannelRouter } from './channel_router.js'\nimport { PRESENCE_DATA_KEY, type PresenceManager, type PresenceSocket } from './presence_manager.js'\nimport { ChannelSubscriptions } from './channel_subscriptions.js'\nimport { Message, type TransportMessage } from './protocol/message.js'\nimport { MessageQueue } from './message_queue.js'\nimport { Reply } from './protocol/reply.js'\nimport { userRoom, type SocketEmission, type SocketEmissionSink } from './broadcasting.js'\nimport { SocketFake } from './fake_socket.js'\nimport { SocketBus, type SerializablePresenceSocket } from './socket_bus.js'\nimport {\n SocketUpgrader,\n type AcceptedUpgrade,\n type SocketUpgradeHandler,\n} from './socket_upgrader.js'\nimport {\n broadcastChannel,\n channelMessageChannel,\n connectChannel,\n disconnectChannel,\n subscribeChannel,\n unsubscribeChannel,\n} from './tracing_channels.js'\nimport type {\n SocketBroadcastMessage,\n SocketChannelMessage,\n SocketSubscribeMessage,\n SocketUnsubscribeMessage,\n} from './types/tracing_channels.js'\n\nconst DEFAULT_PING_INTERVAL = 25_000\nconst DEFAULT_PING_TIMEOUT = 5_000\n\nfunction sendFrame(connection: WebSocket, frame: ServerProtocolMessage): void {\n if (connection.readyState !== WebSocket.OPEN) {\n return\n }\n\n connection.send(JSON.stringify(frame))\n}\n\nfunction validateDuration(name: string, value: number | undefined): number | undefined {\n if (value === undefined) {\n return undefined\n }\n\n if (!Number.isFinite(value) || value <= 0) {\n throw new Error(`websocket.${name} must be a positive number of milliseconds`)\n }\n\n return value\n}\n\nfunction resolveHeartbeatConfig(\n config: { pingInterval?: number; pingTimeout?: number } | undefined\n): { interval: number; timeout: number } | null {\n const pingInterval = validateDuration('pingInterval', config?.pingInterval)\n const pingTimeout = validateDuration('pingTimeout', config?.pingTimeout)\n\n if (pingInterval === undefined && pingTimeout === undefined) {\n return null\n }\n\n return {\n interval: pingInterval ?? DEFAULT_PING_INTERVAL,\n timeout: pingTimeout ?? DEFAULT_PING_TIMEOUT,\n }\n}\n\n/**\n * Main service for managing WebSocket connections.\n */\nexport class SocketService<User = unknown, HttpContext = unknown> extends Emittery<\n SocketEvents<User, HttpContext>\n> {\n #server: WebSocketServer | null = null\n #httpServer: HttpServer | null = null\n #upgradeHandler: SocketUpgradeHandler | null = null\n #sockets = new Map<string, AuthenticatedSocket<User, HttpContext>>()\n #userRooms = new Map<string, Set<string>>()\n #socketUserRoom = new Map<string, string>()\n #channelSubscriptions: ChannelSubscriptions<User, HttpContext> | null = null\n #presenceManager: PresenceManager | null = null\n #bus: SocketBus | null = null\n #fake: SocketFake | null = null\n #deliverySink: SocketEmissionSink = {\n dispatch: (emission) => {\n this.#deliverBroadcast(emission)\n },\n }\n #broadcastSink: SocketEmissionSink = this.#deliverySink\n #status: SocketServiceStatus = 'stopped'\n #lastError: Error | null = null\n #heartbeatInterval: ReturnType<typeof setInterval> | null = null\n #heartbeatTimeouts = new Map<string, ReturnType<typeof setTimeout>>()\n #messageQueue = new MessageQueue()\n\n /**\n * Accesses the underlying ws server.\n */\n get server(): WebSocketServer {\n if (!this.#server) {\n throw new Error('WebSocket server is not initialized. Call boot() first.')\n }\n\n return this.#server\n }\n\n setPresenceManager(presenceManager: PresenceManager): void {\n this.#presenceManager = presenceManager\n this.#channelSubscriptions?.setPresenceManager(presenceManager)\n this.#configurePresenceFetcher()\n }\n\n async boot(\n httpServer: HttpServer,\n config: SocketConfig<User, HttpContext>,\n channelRouter: ChannelRouter,\n logger: Logger\n ): Promise<void> {\n this.#status = 'starting'\n this.#lastError = null\n\n this.#channelSubscriptions = new ChannelSubscriptions(\n this,\n channelRouter,\n logger,\n this.#presenceManager ?? undefined\n )\n\n const websocketConfig = config.websocket\n const heartbeatConfig = resolveHeartbeatConfig(websocketConfig)\n\n try {\n if (config.transport) {\n this.#bus = new SocketBus(config.transport, {\n channel: (message) => {\n this.#traceBroadcast(\n {\n target: 'channel',\n channel: message.channel,\n event: message.event,\n via: 'bus',\n except: message.except,\n },\n () =>\n this.#emitToChannelLocally(\n message.channel,\n message.event,\n message.data,\n message.except\n )\n )\n },\n user: (message) => {\n this.#traceBroadcast(\n { target: 'user', room: message.room, event: message.event, via: 'bus' },\n () => this.#emitToUserRoomLocally(message.room, message.event, message.data)\n )\n },\n broadcast: (message) => {\n this.#traceBroadcast({ target: 'global', event: message.event, via: 'bus' }, () =>\n this.#broadcastLocally(message.event, message.data)\n )\n },\n presenceSockets: (channel) => {\n return this.#getLocalPresenceSockets(channel)\n },\n })\n\n await this.#bus.start()\n this.#configurePresenceFetcher()\n }\n\n const websocketServer = new WebSocketServer({\n noServer: true,\n })\n\n this.#server = websocketServer\n this.#httpServer = httpServer\n const upgrader = new SocketUpgrader<User, HttpContext>(websocketServer, websocketConfig)\n\n this.#upgradeHandler = (request, socket, head) => {\n upgrader\n .handle(request, socket, head, (connection, upgradeRequest, accepted) => {\n this.#handleConnection(connection, upgradeRequest, accepted)\n })\n .catch((error) => {\n logger.warn('failed to upgrade socket connection: %s', error)\n SocketUpgrader.reject(socket, 500, 'Internal Server Error')\n })\n }\n httpServer.on('upgrade', this.#upgradeHandler)\n\n this.#startHeartbeat(heartbeatConfig)\n this.#status = 'ready'\n } catch (error) {\n this.#lastError = error instanceof Error ? error : new Error('Socket service boot failed')\n this.#status = 'failed'\n await this.close()\n throw error\n }\n }\n\n #handleConnection(\n connection: WebSocket,\n request: IncomingMessage,\n upgrade: AcceptedUpgrade<User, HttpContext>\n ): void {\n const socket = this.#wrapSocket(connection, request, upgrade)\n\n connectChannel.traceSync(\n () => {\n this.#sockets.set(socket.id, socket)\n this.emit('connect', { socket })\n },\n { socketId: socket.id }\n )\n\n connection.on('message', (data) => {\n const message = Message.fromTransport(data.toString())\n\n if (message.type === 'ping') {\n sendFrame(socket.raw.connection, { id: message.id, type: 'pong' })\n return\n }\n\n this.#messageQueue.enqueue(socket.id, async () => {\n await this.#handleMessage(socket, message).catch((error) => {\n sendFrame(connection, {\n type: 'error',\n error: error instanceof Error ? error.message : 'Unexpected socket error',\n })\n })\n })\n })\n\n connection.on('pong', () => {\n this.#clearHeartbeatTimeout(socket.id)\n })\n\n connection.on('close', async () => {\n const traceMessage = {\n socketId: socket.id,\n reason: 'close',\n subscriptions: socket.channels.size,\n }\n\n await disconnectChannel.tracePromise(async () => {\n this.#clearHeartbeatTimeout(socket.id)\n this.#messageQueue.delete(socket.id)\n await this.#channelSubscriptions?.leaveAll(socket)\n this.#channelSubscriptions?.deleteSocket(socket.id)\n this.#sockets.delete(socket.id)\n this.#leaveUserRoom(socket.id)\n this.emit('disconnect', { socket, reason: 'close' })\n }, traceMessage)\n })\n }\n\n async #handleMessage(\n socket: AuthenticatedSocket<User, HttpContext>,\n message: TransportMessage\n ): Promise<void> {\n if (!message.valid) {\n sendFrame(socket.raw.connection, message.toRejectionFrame())\n return\n }\n\n switch (message.type) {\n case 'ping':\n return\n case 'subscribe': {\n const traceMessage: SocketSubscribeMessage = {\n socketId: socket.id,\n channel: message.channel,\n }\n\n await subscribeChannel.tracePromise(async () => {\n const result = await this.#channelSubscriptions?.subscribe(socket, message.channel)\n\n traceMessage.created = result?.created ?? false\n traceMessage.ok = result?.ack.ok ?? false\n if (!result?.ack.ok) {\n traceMessage.error = result?.ack.error ?? 'Socket service is not initialized'\n }\n\n if (result?.created) {\n this.emit('subscribe', { socket, channel: message.channel })\n }\n\n sendFrame(socket.raw.connection, Reply.fromSubscribeResult(message.id, result).toFrame())\n }, traceMessage)\n return\n }\n case 'unsubscribe': {\n const traceMessage: SocketUnsubscribeMessage = {\n socketId: socket.id,\n channel: message.channel,\n }\n\n await unsubscribeChannel.tracePromise(async () => {\n traceMessage.removed = socket.channels.has(message.channel)\n await this.#channelSubscriptions?.leave(socket, message.channel)\n traceMessage.ok = true\n this.emit('unsubscribe', { socket, channel: message.channel })\n sendFrame(socket.raw.connection, Reply.ok(message.id).toFrame())\n }, traceMessage)\n return\n }\n case 'message':\n case 'whisper': {\n await this.#handleChannelFrame(socket, message)\n return\n }\n }\n }\n\n async #handleChannelFrame(\n socket: AuthenticatedSocket<User, HttpContext>,\n message: Extract<TransportMessage, { type: 'message' | 'whisper' }>\n ): Promise<void> {\n const payload = message.toChannelMessage()\n const traceMessage: SocketChannelMessage = {\n socketId: socket.id,\n channel: payload.channel,\n event: payload.event,\n }\n\n await channelMessageChannel.tracePromise(async () => {\n const ack = await this.#handleChannelPayload(socket, message.type, payload)\n traceMessage.ok = ack.ok\n if (!ack.ok) {\n traceMessage.error = ack.error ?? 'Handler error'\n }\n\n sendFrame(socket.raw.connection, Reply.fromChannelAck(message.id, ack).toFrame())\n }, traceMessage)\n }\n\n async #handleChannelPayload(\n socket: AuthenticatedSocket<User, HttpContext>,\n type: 'message' | 'whisper',\n payload: ChannelMessage\n ): Promise<ChannelAck> {\n if (typeof payload.channel !== 'string' || typeof payload.event !== 'string') {\n return { ok: false, error: `Invalid channel ${type}` }\n }\n\n if (type === 'message') {\n return this.#channelSubscriptions!.handleMessage(socket, payload)\n }\n\n return this.#channelSubscriptions!.relayWhisper(socket, payload)\n }\n\n #wrapSocket(\n connection: WebSocket,\n request: IncomingMessage,\n upgrade: AcceptedUpgrade<User, HttpContext>\n ): AuthenticatedSocket<User, HttpContext> {\n const raw: RawSocket<HttpContext> = {\n id: randomUUID(),\n data: upgrade.data ?? {},\n auth: upgrade.auth,\n connection,\n request,\n httpContext: upgrade.httpContext,\n }\n\n return {\n id: raw.id,\n user: upgrade.user,\n channels: new Set(),\n\n emit(event, data) {\n sendFrame(connection, { type: 'event', event, data })\n },\n\n joinUserRoom: (userId) => {\n this.#joinUserRoom(raw.id, userRoom(userId))\n return Promise.resolve()\n },\n\n leaveUserRoom: () => {\n this.#leaveUserRoom(raw.id)\n return Promise.resolve()\n },\n\n disconnect() {\n connection.close()\n },\n\n raw,\n }\n }\n\n #joinUserRoom(socketId: string, room: string): void {\n this.#leaveUserRoom(socketId)\n\n if (!this.#userRooms.has(room)) {\n this.#userRooms.set(room, new Set())\n }\n\n this.#userRooms.get(room)!.add(socketId)\n this.#socketUserRoom.set(socketId, room)\n }\n\n #leaveUserRoom(socketId: string): void {\n const room = this.#socketUserRoom.get(socketId)\n\n if (!room) {\n return\n }\n\n const sockets = this.#userRooms.get(room)\n sockets?.delete(socketId)\n if (sockets?.size === 0) {\n this.#userRooms.delete(room)\n }\n\n this.#socketUserRoom.delete(socketId)\n }\n\n to(channel: string) {\n return {\n emit: (event: string, data: unknown) => {\n this.#broadcastSink.dispatch({ target: 'channel', channel, event, data })\n },\n except: (socketId: string) => ({\n emit: (event: string, data: unknown) => {\n this.#broadcastSink.dispatch({\n target: 'channel',\n channel,\n event,\n data,\n except: [socketId],\n })\n },\n }),\n }\n }\n\n toUser(userId: string | number) {\n const room = userRoom(userId)\n\n return {\n emit: (event: string, data: unknown) => {\n this.#broadcastSink.dispatch({ target: 'user', userId, room, event, data })\n },\n }\n }\n\n broadcast(event: string, data: unknown): void {\n this.#broadcastSink.dispatch({ target: 'global', event, data })\n }\n\n #deliverBroadcast(emission: SocketEmission): void {\n switch (emission.target) {\n case 'channel': {\n const channel = emission.channel\n this.#bus?.publishChannel(channel, emission.event, emission.data, emission.except)\n\n const traceMessage: SocketBroadcastMessage = {\n target: 'channel',\n channel,\n event: emission.event,\n via: 'local',\n }\n if (emission.except) {\n traceMessage.except = emission.except\n }\n\n this.#traceBroadcast(traceMessage, () =>\n this.#emitToChannelLocally(channel, emission.event, emission.data, emission.except)\n )\n return\n }\n case 'user': {\n const room = emission.room\n this.#bus?.publishUser(room, emission.event, emission.data)\n this.#traceBroadcast({ target: 'user', room, event: emission.event, via: 'local' }, () =>\n this.#emitToUserRoomLocally(room, emission.event, emission.data)\n )\n return\n }\n case 'global':\n this.#bus?.publishBroadcast(emission.event, emission.data)\n this.#traceBroadcast({ target: 'global', event: emission.event, via: 'local' }, () =>\n this.#broadcastLocally(emission.event, emission.data)\n )\n }\n }\n\n #traceBroadcast(message: SocketBroadcastMessage, deliver: () => number): void {\n broadcastChannel.traceSync(() => {\n message.delivered = deliver()\n }, message)\n }\n\n #emitToChannelLocally(\n channel: string,\n event: string,\n data: unknown,\n except: string[] = []\n ): number {\n let delivered = 0\n\n for (const socketId of this.#channelSubscriptions?.getSocketIds(channel) ?? []) {\n if (except.includes(socketId)) {\n continue\n }\n\n const socket = this.#sockets.get(socketId)\n if (socket) {\n sendFrame(socket.raw.connection, { type: 'event', channel, event, data })\n delivered += 1\n }\n }\n\n return delivered\n }\n\n #emitToUserRoomLocally(room: string, event: string, data: unknown): number {\n let delivered = 0\n\n for (const socketId of this.#userRooms.get(room) ?? []) {\n const socket = this.#sockets.get(socketId)\n if (socket) {\n sendFrame(socket.raw.connection, { type: 'event', event, data })\n delivered += 1\n }\n }\n\n return delivered\n }\n\n #broadcastLocally(event: string, data: unknown): number {\n let delivered = 0\n\n for (const socket of this.#sockets.values()) {\n sendFrame(socket.raw.connection, { type: 'event', event, data })\n delivered += 1\n }\n\n return delivered\n }\n\n #configurePresenceFetcher(): void {\n if (!this.#presenceManager || !this.#bus) {\n return\n }\n\n this.#presenceManager.setSocketFetcher(async (channel) => {\n return [\n ...this.#presenceManager!.getLocalSockets(channel),\n ...(await this.#bus!.fetchPresenceSockets(channel)),\n ]\n })\n }\n\n #getLocalPresenceSockets(channel: string): SerializablePresenceSocket[] {\n return (this.#presenceManager?.getLocalSockets(channel) ?? []).map((socket) => {\n return this.#serializePresenceSocket(channel, socket)\n })\n }\n\n #serializePresenceSocket(channel: string, socket: PresenceSocket): SerializablePresenceSocket {\n const user = socket.data[PRESENCE_DATA_KEY]![channel]\n\n return {\n id: socket.id,\n data: {\n [PRESENCE_DATA_KEY]: {\n [channel]: {\n ...user,\n joinedAt: user.joinedAt.toISOString(),\n },\n },\n },\n }\n }\n\n getSocket(socketId: string): AuthenticatedSocket<User, HttpContext> | undefined {\n return this.#sockets.get(socketId)\n }\n\n get connectionsCount(): number {\n return this.#sockets.size\n }\n\n get status(): SocketServiceStatus {\n return this.#status\n }\n\n get ready(): boolean {\n return this.#status === 'ready' && Boolean(this.#server)\n }\n\n fake(): SocketFake {\n const fake = new SocketFake(() => {\n if (this.#fake === fake) {\n this.restore()\n }\n })\n\n this.#fake = fake\n this.#broadcastSink = fake\n return fake\n }\n\n restore(): void {\n this.#fake = null\n this.#broadcastSink = this.#deliverySink\n }\n\n markFailed(error: unknown): void {\n this.#lastError = error instanceof Error ? error : new Error('Socket service failed')\n this.#status = 'failed'\n }\n\n health(): SocketHealthSnapshot {\n return {\n status: this.#status,\n ready: this.ready,\n connections: this.#sockets.size,\n channels: this.#channelSubscriptions?.channelsCount ?? 0,\n ...(this.#lastError ? { lastError: this.#lastError.message } : {}),\n }\n }\n\n #startHeartbeat(config: { interval: number; timeout: number } | null): void {\n this.#stopHeartbeat()\n\n if (!config) {\n return\n }\n\n this.#heartbeatInterval = setInterval(() => {\n this.#pingSockets(config.timeout)\n }, config.interval)\n }\n\n #pingSockets(timeout: number): void {\n for (const socket of this.#sockets.values()) {\n const { connection } = socket.raw\n\n if (connection.readyState !== WebSocket.OPEN) {\n this.#clearHeartbeatTimeout(socket.id)\n continue\n }\n\n if (this.#heartbeatTimeouts.has(socket.id)) {\n continue\n }\n\n const timeoutId = setTimeout(() => {\n this.#heartbeatTimeouts.delete(socket.id)\n connection.terminate()\n }, timeout)\n\n this.#heartbeatTimeouts.set(socket.id, timeoutId)\n\n try {\n connection.ping()\n } catch {\n this.#clearHeartbeatTimeout(socket.id)\n connection.terminate()\n }\n }\n }\n\n #clearHeartbeatTimeout(socketId: string): void {\n const timeout = this.#heartbeatTimeouts.get(socketId)\n\n if (!timeout) {\n return\n }\n\n clearTimeout(timeout)\n this.#heartbeatTimeouts.delete(socketId)\n }\n\n #stopHeartbeat(): void {\n if (this.#heartbeatInterval) {\n clearInterval(this.#heartbeatInterval)\n this.#heartbeatInterval = null\n }\n\n for (const timeout of this.#heartbeatTimeouts.values()) {\n clearTimeout(timeout)\n }\n\n this.#heartbeatTimeouts.clear()\n }\n\n #cleanup(): void {\n this.#stopHeartbeat()\n\n for (const socket of this.#sockets.values()) {\n socket.raw.connection.terminate()\n }\n\n this.#sockets.clear()\n this.#userRooms.clear()\n this.#socketUserRoom.clear()\n this.#messageQueue.clear()\n this.#channelSubscriptions?.clear()\n }\n\n async close(): Promise<void> {\n const server = this.#server\n const bus = this.#bus\n const httpServer = this.#httpServer\n const upgradeHandler = this.#upgradeHandler\n if (this.#status !== 'failed') {\n this.#status = 'stopping'\n }\n\n this.#server = null\n this.#bus = null\n this.#httpServer = null\n this.#upgradeHandler = null\n if (httpServer && upgradeHandler) {\n httpServer.off('upgrade', upgradeHandler)\n }\n this.#cleanup()\n\n try {\n await bus?.close()\n\n if (!server) {\n if (this.#status !== 'failed') {\n this.#status = 'stopped'\n }\n return\n }\n\n await new Promise<void>((resolve) => {\n server.close(() => {\n resolve()\n })\n })\n\n if (this.#status !== 'failed') {\n this.#status = 'stopped'\n }\n } catch (error) {\n this.markFailed(error)\n throw error\n }\n }\n}\n","import type { BaseChannel } from './base_channel.js'\n\nexport interface StoredChannelSubscription {\n channelName: string\n instance: BaseChannel<any, any, any>\n paramValues: string[]\n}\n\n/**\n * Stores durable Channel subscriptions and keeps Socket/Channel indexes in sync.\n */\nexport class ChannelSubscriptionStorage {\n #subscriptions = new Map<string, Map<string, StoredChannelSubscription>>()\n #subscribers = new Map<string, Set<string>>()\n\n has(socketId: string, channelName: string): boolean {\n return this.#subscriptions.get(socketId)?.has(channelName) ?? false\n }\n\n get(socketId: string, channelName: string): StoredChannelSubscription | undefined {\n return this.#subscriptions.get(socketId)?.get(channelName)\n }\n\n set(socketId: string, channelName: string, subscription: StoredChannelSubscription): void {\n if (!this.#subscriptions.has(socketId)) {\n this.#subscriptions.set(socketId, new Map())\n }\n\n this.#subscriptions.get(socketId)!.set(channelName, subscription)\n\n if (!this.#subscribers.has(channelName)) {\n this.#subscribers.set(channelName, new Set())\n }\n\n this.#subscribers.get(channelName)!.add(socketId)\n }\n\n delete(socketId: string, channelName: string): void {\n const subscriptions = this.#subscriptions.get(socketId)\n\n if (!subscriptions) {\n return\n }\n\n subscriptions.delete(channelName)\n if (subscriptions.size === 0) {\n this.#subscriptions.delete(socketId)\n }\n\n const subscribers = this.#subscribers.get(channelName)\n subscribers?.delete(socketId)\n if (subscribers?.size === 0) {\n this.#subscribers.delete(channelName)\n }\n }\n\n deleteSocket(socketId: string): void {\n for (const channelName of this.channelNamesFor(socketId)) {\n this.delete(socketId, channelName)\n }\n }\n\n channelNamesFor(socketId: string): string[] {\n return [...(this.#subscriptions.get(socketId)?.keys() ?? [])]\n }\n\n socketIdsFor(channelName: string): string[] {\n return [...(this.#subscribers.get(channelName) ?? [])]\n }\n\n get channelsCount(): number {\n return this.#subscribers.size\n }\n\n clear(): void {\n this.#subscriptions.clear()\n this.#subscribers.clear()\n }\n}\n","import type { Logger } from '@adonisjs/logger'\nimport { MissingPresenceInfoError } from './base_channel.js'\nimport type { BaseChannel } from './base_channel.js'\nimport { ChannelSubscriptionStorage } from './channel_subscription_storage.js'\nimport type { ChannelRouter } from './channel_router.js'\nimport type { PresenceData, PresenceManager } from './presence_manager.js'\nimport type { SocketService } from './socket_service.js'\nimport type { AuthenticatedSocket, ChannelAck, ChannelMessage, PresenceMember } from './types.js'\n\nexport interface ChannelSubscribeAck {\n ok: boolean\n presenceData?: Record<string, unknown> | PresenceData | null\n error?: string\n}\n\nexport interface ChannelSubscribeResult {\n ack: ChannelSubscribeAck\n created: boolean\n}\n\n/**\n * Durable channel subscriptions for sockets.\n */\nexport class ChannelSubscriptions<User = unknown, HttpContext = unknown> {\n #storage = new ChannelSubscriptionStorage()\n\n constructor(\n private socketService: SocketService<User, HttpContext>,\n private channelRouter: ChannelRouter,\n private logger: Logger,\n private presenceManager?: PresenceManager\n ) {}\n\n setPresenceManager(presenceManager: PresenceManager): void {\n this.presenceManager = presenceManager\n }\n\n async subscribe(\n socket: AuthenticatedSocket<User, HttpContext>,\n channelName: string\n ): Promise<ChannelSubscribeResult> {\n if (this.#storage.has(socket.id, channelName)) {\n return {\n ack: {\n ok: true,\n presenceData: await this.#getPresenceData(socket, channelName),\n },\n created: false,\n }\n }\n\n const result = await this.channelRouter.authorize(socket, channelName)\n\n if (!result.success) {\n return {\n ack: { ok: false, error: result.error },\n created: false,\n }\n }\n\n result.instance.$setContext(\n this.socketService,\n channelName,\n result.params,\n this.presenceManager\n )\n\n let presenceData: Record<string, unknown> | PresenceData | null = result.presenceData\n let presenceJoined = false\n\n try {\n if (this.#usesPresence(result.instance)) {\n const member = this.#getPresenceMember(result.instance, socket)\n\n this.presenceManager!.join(channelName, socket.raw, member)\n presenceJoined = true\n\n await result.instance.onMemberJoin?.(socket, member, ...result.paramValues)\n await result.instance.onJoin?.(socket, ...result.paramValues)\n\n presenceData = await this.presenceManager!.snapshot(channelName)\n } else {\n await result.instance.onJoin?.(socket, ...result.paramValues)\n }\n\n this.#storage.set(socket.id, channelName, {\n channelName,\n instance: result.instance,\n paramValues: result.paramValues,\n })\n socket.channels.add(channelName)\n\n if (this.#usesPresence(result.instance) && presenceData) {\n this.socketService.to(channelName).except(socket.id).emit('presence:update', presenceData)\n }\n } catch (error) {\n if (presenceJoined) {\n this.presenceManager!.leave(channelName, socket.raw)\n\n const rollbackData = await this.presenceManager!.snapshot(channelName).catch(() => null)\n if (rollbackData) {\n this.socketService.to(channelName).emit('presence:update', rollbackData)\n }\n }\n\n this.#storage.delete(socket.id, channelName)\n socket.channels.delete(channelName)\n\n return {\n ack: {\n ok: false,\n error: error instanceof MissingPresenceInfoError ? error.message : 'Join failed',\n },\n created: false,\n }\n }\n\n return {\n ack: { ok: true, presenceData },\n created: true,\n }\n }\n\n async leave(socket: AuthenticatedSocket<User, HttpContext>, channelName: string): Promise<void> {\n const subscription = this.#storage.get(socket.id, channelName)\n\n if (subscription) {\n this.#storage.delete(socket.id, channelName)\n socket.channels.delete(channelName)\n\n try {\n if (this.#usesPresence(subscription.instance)) {\n const member = this.#getPresenceMember(subscription.instance, socket)\n\n this.presenceManager!.leave(channelName, socket.raw)\n const presenceData = await this.presenceManager!.snapshot(channelName)\n\n this.socketService.to(channelName).emit('presence:update', presenceData)\n\n await subscription.instance.onMemberLeave?.(socket, member, ...subscription.paramValues)\n await subscription.instance.onLeave?.(socket, ...subscription.paramValues)\n } else {\n await subscription.instance.onLeave?.(socket, ...subscription.paramValues)\n }\n } catch (error) {\n this.logger.warn(`failed to leave channel \"${channelName}\": %s`, error)\n }\n } else {\n socket.channels.delete(channelName)\n }\n }\n\n async leaveAll(socket: AuthenticatedSocket<User, HttpContext>): Promise<void> {\n for (const channelName of this.getChannelNames(socket.id)) {\n await this.leave(socket, channelName)\n }\n }\n\n async handleMessage(\n socket: AuthenticatedSocket<User, HttpContext>,\n payload: ChannelMessage\n ): Promise<ChannelAck> {\n const subscription = this.#storage.get(socket.id, payload.channel)\n\n if (!subscription) {\n return { ok: false, error: 'Not subscribed' }\n }\n\n try {\n const result = await subscription.instance.$handleMessage(socket, payload.event, payload.data)\n return { ok: true, data: result }\n } catch (error) {\n return {\n ok: false,\n error: error instanceof Error ? error.message : 'Handler error',\n }\n }\n }\n\n relayWhisper(socket: AuthenticatedSocket<User, HttpContext>, payload: ChannelMessage): ChannelAck {\n if (!this.#storage.has(socket.id, payload.channel)) {\n return { ok: false, error: 'Not subscribed' }\n }\n\n this.socketService\n .to(payload.channel)\n .except(socket.id)\n .emit(`client:${payload.event}`, payload.data)\n\n return { ok: true }\n }\n\n deleteSocket(socketId: string): void {\n this.#storage.deleteSocket(socketId)\n }\n\n clear(): void {\n this.#storage.clear()\n }\n\n getSocketIds(channelName: string): string[] {\n return this.#storage.socketIdsFor(channelName)\n }\n\n getChannelNames(socketId: string): string[] {\n return this.#storage.channelNamesFor(socketId)\n }\n\n get channelsCount(): number {\n return this.#storage.channelsCount\n }\n\n #usesPresence(instance: BaseChannel<any, any, any>): boolean {\n return Boolean(\n (instance.constructor as typeof BaseChannel).options?.presence && this.presenceManager\n )\n }\n\n #getPresenceMember(\n instance: BaseChannel<any, any, any>,\n socket: AuthenticatedSocket<User, HttpContext>\n ): PresenceMember {\n return instance.$getPresenceInfo(socket)\n }\n\n async #getPresenceData(\n socket: AuthenticatedSocket<User, HttpContext>,\n channelName: string\n ): Promise<PresenceData | undefined> {\n if (!this.presenceManager?.hasLocal(channelName, socket.id)) {\n return undefined\n }\n\n return this.presenceManager.snapshot(channelName)\n }\n}\n","import type { ChannelAck, ServerProtocolMessage } from '../shared_types.js'\nimport type { ChannelSubscribeResult } from '../channel_subscriptions.js'\n\nconst INVALID_SOCKET_MESSAGE = 'Invalid socket message'\n\nexport class Reply {\n private constructor(\n private id: string | undefined,\n private ok: boolean,\n private data: unknown | undefined,\n private error: string | undefined\n ) {}\n\n static ok(id: string | undefined, data?: unknown): Reply {\n return new Reply(id, true, data, undefined)\n }\n\n static error(id: string | undefined, error: string): Reply {\n return new Reply(id, false, undefined, error)\n }\n\n static invalidMessage(id: string | undefined): ServerProtocolMessage {\n if (id === undefined) {\n return { type: 'error', error: INVALID_SOCKET_MESSAGE }\n }\n\n return Reply.error(id, INVALID_SOCKET_MESSAGE).toFrame()\n }\n\n static fromSubscribeResult(\n id: string | undefined,\n result: ChannelSubscribeResult | undefined\n ): Reply {\n if (!result) {\n return Reply.error(id, 'Socket service is not initialized')\n }\n\n if (!result.ack.ok) {\n return Reply.error(id, result.ack.error ?? 'Subscribe failed')\n }\n\n const data = result.ack.presenceData\n ? {\n presenceData: result.ack.presenceData,\n }\n : undefined\n\n return Reply.ok(id, data)\n }\n\n static fromChannelAck(id: string | undefined, ack: ChannelAck): Reply {\n return ack.ok ? Reply.ok(id, ack.data) : Reply.error(id, ack.error ?? 'Handler error')\n }\n\n toFrame(): Extract<ServerProtocolMessage, { type: 'ack' }> {\n const frame: Extract<ServerProtocolMessage, { type: 'ack' }> = {\n id: this.id,\n type: 'ack',\n ok: this.ok,\n }\n\n if (this.data !== undefined) {\n frame.data = this.data\n }\n\n if (this.error !== undefined) {\n frame.error = this.error\n }\n\n return frame\n }\n}\n","import type {\n ChannelMessage,\n ClientProtocolMessage,\n ServerProtocolMessage,\n} from '../shared_types.js'\nimport { Reply } from './reply.js'\n\ntype ClientMessageType = ClientProtocolMessage['type']\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nexport abstract class Message {\n abstract readonly valid: boolean\n abstract readonly type: ClientMessageType | 'invalid'\n\n static fromTransport(raw: string): TransportMessage {\n try {\n return Message.fromParsedTransport(JSON.parse(raw))\n } catch {\n return new InvalidMessage(undefined)\n }\n }\n\n private static fromParsedTransport(value: unknown): TransportMessage {\n if (!isRecord(value)) {\n return new InvalidMessage(undefined)\n }\n\n const id = typeof value.id === 'string' ? value.id : undefined\n\n if ('id' in value && typeof value.id !== 'string') {\n return new InvalidMessage(undefined)\n }\n\n if (!isClientMessageType(value.type)) {\n return new InvalidMessage(id)\n }\n\n if (value.type === 'ping') {\n return new PingMessage(id)\n }\n\n if (value.type === 'subscribe' && typeof value.channel === 'string') {\n return new SubscribeMessage(id, value.channel)\n }\n\n if (value.type === 'unsubscribe' && typeof value.channel === 'string') {\n return new UnsubscribeMessage(id, value.channel)\n }\n\n if (\n (value.type === 'message' || value.type === 'whisper') &&\n typeof value.channel === 'string' &&\n typeof value.event === 'string'\n ) {\n return new ChannelProtocolMessage(id, value.type, value.channel, value.event, value.data)\n }\n\n return new InvalidMessage(id)\n }\n}\n\nexport class InvalidMessage extends Message {\n readonly valid = false\n readonly type = 'invalid'\n\n constructor(readonly id: string | undefined) {\n super()\n }\n\n toRejectionFrame(): ServerProtocolMessage {\n return Reply.invalidMessage(this.id)\n }\n}\n\nexport class PingMessage extends Message {\n readonly valid = true\n readonly type = 'ping'\n\n constructor(readonly id: string | undefined) {\n super()\n }\n}\n\nexport class SubscribeMessage extends Message {\n readonly valid = true\n readonly type = 'subscribe'\n\n constructor(\n readonly id: string | undefined,\n readonly channel: string\n ) {\n super()\n }\n}\n\nexport class UnsubscribeMessage extends Message {\n readonly valid = true\n readonly type = 'unsubscribe'\n\n constructor(\n readonly id: string | undefined,\n readonly channel: string\n ) {\n super()\n }\n}\n\nexport class ChannelProtocolMessage extends Message {\n readonly valid = true\n\n constructor(\n readonly id: string | undefined,\n readonly type: 'message' | 'whisper',\n readonly channel: string,\n readonly event: string,\n readonly data: unknown\n ) {\n super()\n }\n\n toChannelMessage(): ChannelMessage {\n return {\n channel: this.channel,\n event: this.event,\n data: this.data,\n }\n }\n}\n\nexport type TransportMessage =\n | InvalidMessage\n | PingMessage\n | SubscribeMessage\n | UnsubscribeMessage\n | ChannelProtocolMessage\n\nfunction isClientMessageType(value: unknown): value is ClientMessageType {\n return (\n value === 'ping' ||\n value === 'subscribe' ||\n value === 'unsubscribe' ||\n value === 'message' ||\n value === 'whisper'\n )\n}\n","export class MessageQueue<Key = string> {\n #queues = new Map<Key, Promise<void>>()\n\n enqueue(key: Key, task: () => Promise<void>): void {\n const previous = this.#queues.get(key) ?? Promise.resolve()\n const next = previous\n .catch(() => {})\n .then(task)\n .catch(() => {})\n .finally(() => {\n if (this.#queues.get(key) === next) {\n this.#queues.delete(key)\n }\n })\n\n this.#queues.set(key, next)\n }\n\n delete(key: Key): void {\n this.#queues.delete(key)\n }\n\n clear(): void {\n this.#queues.clear()\n }\n}\n","export type SocketEmission =\n | {\n target: 'global'\n event: string\n data: unknown\n }\n | {\n target: 'channel'\n channel: string\n event: string\n data: unknown\n except?: string[]\n }\n | {\n target: 'user'\n userId: string | number\n room: string\n event: string\n data: unknown\n }\n\nexport type SocketEmissionTarget = SocketEmission['target']\n\nexport interface SocketEmissionSink {\n dispatch(emission: SocketEmission): void\n}\n\nexport function userRoom(userId: string | number): string {\n return `socket:user:${String(userId)}`\n}\n","import { randomUUID } from 'node:crypto'\nimport { Bus } from '@boringnode/bus'\nimport type { Serializable, TransportFactory } from '@boringnode/bus/types/main'\nimport { PRESENCE_DATA_KEY, type PresenceSocket, type PresenceUser } from './presence_manager.js'\n\nconst DEFAULT_CHANNEL = 'socket::broadcast'\nconst DEFAULT_PRESENCE_TIMEOUT = 100\n\ntype SerializablePresenceUser = Omit<PresenceUser, 'joinedAt'> & {\n [key: string]: Serializable\n joinedAt: string\n}\n\nexport interface SerializablePresenceSocket {\n [key: string]: Serializable\n id: string\n data: {\n [key: string]: Serializable\n }\n}\n\nexport type SocketBusMessage =\n | {\n type: 'channel:event'\n origin: string\n channel: string\n event: string\n data: Serializable\n except?: string[]\n }\n | {\n type: 'user:event'\n origin: string\n room: string\n event: string\n data: Serializable\n }\n | {\n type: 'broadcast:event'\n origin: string\n event: string\n data: Serializable\n }\n | {\n type: 'presence:sockets:request'\n origin: string\n requestId: string\n channel: string\n }\n | {\n type: 'presence:sockets:response'\n origin: string\n target: string\n requestId: string\n sockets: SerializablePresenceSocket[]\n }\n\nexport interface SocketTransportConfig {\n driver: TransportFactory\n channel?: string\n presenceTimeout?: number\n}\n\ninterface SocketBusHandlers {\n channel(message: Extract<SocketBusMessage, { type: 'channel:event' }>): void\n user(message: Extract<SocketBusMessage, { type: 'user:event' }>): void\n broadcast(message: Extract<SocketBusMessage, { type: 'broadcast:event' }>): void\n presenceSockets(channel: string): SerializablePresenceSocket[]\n}\n\ninterface PendingPresenceRequest {\n sockets: SerializablePresenceSocket[]\n resolve(sockets: PresenceSocket[]): void\n timeout: ReturnType<typeof setTimeout>\n}\n\nexport class SocketBus {\n #origin = randomUUID()\n #channel: string\n #bus: Bus\n #presenceTimeout: number\n #pendingPresenceRequests = new Map<string, PendingPresenceRequest>()\n\n constructor(\n transport: SocketTransportConfig,\n private handlers: SocketBusHandlers\n ) {\n this.#channel = transport.channel ?? DEFAULT_CHANNEL\n this.#presenceTimeout = transport.presenceTimeout ?? DEFAULT_PRESENCE_TIMEOUT\n this.#bus = new Bus(transport.driver(), { retryQueue: { enabled: true } })\n }\n\n async start(): Promise<void> {\n await this.#bus.subscribe(this.#channel, async (payload) => {\n const message = payload as SocketBusMessage\n\n if (message.origin === this.#origin) {\n return\n }\n\n switch (message.type) {\n case 'channel:event':\n this.handlers.channel(message)\n return\n case 'user:event':\n this.handlers.user(message)\n return\n case 'broadcast:event':\n this.handlers.broadcast(message)\n return\n case 'presence:sockets:request':\n await this.#respondToPresenceRequest(message)\n return\n case 'presence:sockets:response':\n this.#handlePresenceResponse(message)\n return\n }\n })\n }\n\n publishChannel(channel: string, event: string, data: unknown, except?: string[]): void {\n const message: SocketBusMessage = {\n type: 'channel:event',\n origin: this.#origin,\n channel,\n event,\n data: data as Serializable,\n }\n\n if (except) {\n message.except = except\n }\n\n void this.#bus.publish(this.#channel, message as Serializable)\n }\n\n publishUser(room: string, event: string, data: unknown): void {\n void this.#bus.publish(this.#channel, {\n type: 'user:event',\n origin: this.#origin,\n room,\n event,\n data: data as Serializable,\n } as Serializable)\n }\n\n publishBroadcast(event: string, data: unknown): void {\n void this.#bus.publish(this.#channel, {\n type: 'broadcast:event',\n origin: this.#origin,\n event,\n data: data as Serializable,\n } as Serializable)\n }\n\n fetchPresenceSockets(channel: string): Promise<PresenceSocket[]> {\n const requestId = randomUUID()\n\n return new Promise((resolve) => {\n const timeout = setTimeout(() => {\n const request = this.#pendingPresenceRequests.get(requestId)\n\n if (!request) {\n return\n }\n\n this.#pendingPresenceRequests.delete(requestId)\n resolve(request.sockets.map((socket) => this.#deserializePresenceSocket(socket)))\n }, this.#presenceTimeout)\n\n this.#pendingPresenceRequests.set(requestId, {\n sockets: [],\n resolve,\n timeout,\n })\n\n void this.#bus.publish(this.#channel, {\n type: 'presence:sockets:request',\n origin: this.#origin,\n requestId,\n channel,\n } as Serializable)\n })\n }\n\n async close(): Promise<void> {\n for (const [requestId, request] of this.#pendingPresenceRequests) {\n clearTimeout(request.timeout)\n request.resolve(request.sockets.map((socket) => this.#deserializePresenceSocket(socket)))\n this.#pendingPresenceRequests.delete(requestId)\n }\n\n await this.#bus.disconnect()\n }\n\n async #respondToPresenceRequest(\n message: Extract<SocketBusMessage, { type: 'presence:sockets:request' }>\n ): Promise<void> {\n await this.#bus.publish(this.#channel, {\n type: 'presence:sockets:response',\n origin: this.#origin,\n target: message.origin,\n requestId: message.requestId,\n sockets: this.handlers.presenceSockets(message.channel),\n } as unknown as Serializable)\n }\n\n #handlePresenceResponse(\n message: Extract<SocketBusMessage, { type: 'presence:sockets:response' }>\n ): void {\n if (message.target !== this.#origin) {\n return\n }\n\n const request = this.#pendingPresenceRequests.get(message.requestId)\n\n if (!request) {\n return\n }\n\n request.sockets.push(...message.sockets)\n }\n\n #deserializePresenceSocket(socket: SerializablePresenceSocket): PresenceSocket {\n const presence = socket.data[PRESENCE_DATA_KEY] as\n | Record<string, SerializablePresenceUser>\n | undefined\n\n return {\n id: socket.id,\n data: {\n [PRESENCE_DATA_KEY]: presence\n ? Object.fromEntries(\n Object.entries(presence).map(([channel, user]) => [\n channel,\n {\n ...user,\n joinedAt: new Date(user.joinedAt),\n } as PresenceUser,\n ])\n )\n : undefined,\n },\n }\n }\n}\n","import type { IncomingMessage } from 'node:http'\nimport type { Duplex } from 'node:stream'\nimport type { WebSocket, WebSocketServer } from 'ws'\nimport type { SocketAuthenticationResult, SocketConfig, SocketUpgradeContext } from './types.js'\n\nexport const DEFAULT_WEBSOCKET_PATH = '/socket'\n\nexport interface AcceptedUpgrade<User, HttpContext> {\n auth?: Record<string, unknown>\n data?: Record<string, unknown>\n httpContext?: HttpContext\n user?: User\n}\n\nexport type SocketUpgradeHandler = (request: IncomingMessage, socket: Duplex, head: Buffer) => void\n\ntype AcceptUpgrade<User, HttpContext> = (\n connection: WebSocket,\n request: IncomingMessage,\n upgrade: AcceptedUpgrade<User, HttpContext>\n) => void\n\nexport class SocketUpgrader<User = unknown, HttpContext = unknown> {\n #path: string\n\n constructor(\n private server: WebSocketServer,\n private config: SocketConfig<User, HttpContext>['websocket'] | undefined\n ) {\n this.#path = config?.path ?? DEFAULT_WEBSOCKET_PATH\n }\n\n async handle(\n request: IncomingMessage,\n socket: Duplex,\n head: Buffer,\n accept: AcceptUpgrade<User, HttpContext>\n ): Promise<boolean> {\n if (!this.#matchesPath(request)) {\n return false\n }\n\n const accepted = await this.#authenticate(request)\n\n if (!accepted) {\n SocketUpgrader.reject(socket, 401, 'Unauthorized')\n return true\n }\n\n this.server.handleUpgrade(request, socket, head, (connection) => {\n accept(connection, request, accepted)\n })\n\n return true\n }\n\n static reject(socket: Duplex, statusCode: number, reason: string): void {\n if (!socket.writableEnded) {\n socket.write(`HTTP/1.1 ${statusCode} ${reason}\\r\\n\\r\\n`)\n }\n\n socket.destroy()\n }\n\n #matchesPath(request: IncomingMessage): boolean {\n const url = new URL(request.url ?? '/', 'ws://localhost')\n return url.pathname === this.#path\n }\n\n async #authenticate(\n request: IncomingMessage\n ): Promise<AcceptedUpgrade<User, HttpContext> | null> {\n const auth = this.#parseAuth(request)\n\n if (!this.config?.authenticate) {\n return { auth }\n }\n\n const httpContext = await this.config.createContext?.(request)\n const ctx: SocketUpgradeContext<HttpContext> = {\n request,\n auth,\n httpContext,\n }\n\n let result: SocketAuthenticationResult<User>\n try {\n result = await this.config.authenticate(ctx)\n } catch {\n return null\n }\n\n if (result === false || result === null) {\n return null\n }\n\n return {\n auth: result.auth ?? auth,\n data: result.data,\n httpContext,\n user: result.user,\n }\n }\n\n #parseAuth(request: IncomingMessage): Record<string, unknown> | undefined {\n const url = new URL(request.url ?? '/', 'ws://localhost')\n const auth = url.searchParams.get('auth')\n\n if (!auth) {\n return undefined\n }\n\n try {\n const parsed = JSON.parse(auth)\n return parsed && typeof parsed === 'object' ? parsed : undefined\n } catch {\n return undefined\n }\n }\n}\n","import type { BaseChannel } from './base_channel.js'\nimport { exec, match, parse, type ParsedRoute } from '@poppinss/matchit'\n\ntype MatchParams = Record<string, string>\n\nexport interface ChannelPatternMatch {\n pattern: ChannelPattern\n channel: typeof BaseChannel\n params: MatchParams\n}\n\ninterface MatchCandidate {\n pattern: ChannelPattern\n channel: typeof BaseChannel\n}\n\nexport class ChannelPattern {\n readonly route: ParsedRoute\n readonly score: number[]\n\n private constructor(readonly value: string) {\n this.route = parse(value)\n this.score = this.route.map((segment) => {\n switch (segment.type) {\n case 0:\n return 40\n case 1:\n return 30\n case 3:\n return 20\n case 2:\n return 0\n }\n })\n }\n\n static from(value: string): ChannelPattern {\n return new ChannelPattern(value)\n }\n\n match(channelName: string): MatchParams | null {\n const matched = match(channelName, [this.route])\n if (matched.length === 0) {\n return null\n }\n\n return exec(channelName, matched, true) as MatchParams\n }\n\n /**\n * Returns the most specific matching candidate. Static segments outrank params,\n * params outrank optional params, and optional params outrank wildcards.\n * Insertion order is preserved for ties.\n */\n static firstMatch(\n channelName: string,\n candidates: Iterable<MatchCandidate>\n ): ChannelPatternMatch | null {\n const sorted = [...candidates].sort((left, right) => {\n return ChannelPattern.#compareSpecificity(left.pattern, right.pattern)\n })\n const routes = sorted.map((candidate) => candidate.pattern.route)\n const matched = match(channelName, routes)\n\n if (matched.length === 0) {\n return null\n }\n\n for (const { pattern, channel } of sorted) {\n if (pattern.route !== matched) {\n continue\n }\n\n const params = pattern.match(channelName)\n if (params) {\n return { pattern, channel, params }\n }\n }\n\n return null\n }\n\n static #compareSpecificity(left: ChannelPattern, right: ChannelPattern): number {\n const length = Math.max(left.score.length, right.score.length)\n\n for (let index = 0; index < length; index++) {\n const scoreDifference = (right.score[index] ?? -1) - (left.score[index] ?? -1)\n if (scoreDifference !== 0) {\n return scoreDifference\n }\n }\n\n return right.score.length - left.score.length\n }\n}\n","import type { BaseChannel } from './base_channel.js'\nimport { ChannelPattern } from './channel_pattern.js'\nimport type {\n AuthenticatedSocket,\n ChannelEventMap,\n ChannelMatch,\n MiddlewareContext,\n} from './types.js'\n\ninterface RegisteredChannel {\n pattern: ChannelPattern\n channel: typeof BaseChannel\n}\n\n/**\n * Matches channel names against registered patterns.\n *\n * Pattern: chat/:roomId\n * Channel: chat/general\n *\n * chat / :roomId\n * | |\n * chat / general => { roomId: \"general\" }\n */\nexport class ChannelRouter {\n /**\n * Registered channels keyed by their pattern.\n */\n #channels = new Map<string, RegisteredChannel>()\n\n /**\n * Registers a channel.\n */\n register(channel: typeof BaseChannel): void {\n const patternValue = channel.pattern\n\n if (!patternValue) {\n throw new Error(`Channel ${channel.name} must define a static pattern`)\n }\n\n if (this.#channels.has(patternValue)) {\n throw new Error(`Channel pattern \"${patternValue}\" is already registered`)\n }\n\n const pattern = ChannelPattern.from(patternValue)\n\n this.#channels.set(pattern.value, { pattern, channel })\n }\n\n /**\n * Matches a channel name against registered patterns.\n */\n match(channelName: string): ChannelMatch | null {\n const matched = ChannelPattern.firstMatch(channelName, this.#channels.values())\n\n if (!matched) {\n return null\n }\n\n return {\n channel: matched.channel,\n pattern: matched.pattern.value,\n params: matched.params,\n }\n }\n\n /**\n * Authorizes a socket before it joins a channel.\n */\n async authorize<User = unknown, HttpContext = unknown>(\n socket: AuthenticatedSocket<User, HttpContext>,\n channelName: string\n ) {\n const matched = this.match(channelName)\n\n if (!matched) {\n return {\n success: false,\n error: 'Channel not found',\n } as const\n }\n\n const instance = new matched.channel() as BaseChannel<User, ChannelEventMap, HttpContext>\n const paramValues = Object.values(matched.params)\n\n const ctx: MiddlewareContext<User, HttpContext> = {\n socket,\n channel: channelName,\n params: matched.params,\n presenceData: undefined,\n setPresenceData(data) {\n ctx.presenceData = data\n },\n }\n\n // Run authorization middlewares.\n try {\n await instance.$runMiddlewares(ctx)\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Middleware rejected',\n } as const\n }\n\n return {\n success: true,\n instance,\n params: matched.params,\n paramValues,\n presenceData: ctx.presenceData ?? null,\n } as const\n }\n\n /**\n * Lists all registered channels.\n */\n list() {\n return [...this.#channels.values()].map(({ pattern, channel }) => ({\n pattern: pattern.value,\n name: channel.name,\n options: channel.options,\n }))\n }\n\n /**\n * Number of registered channels.\n */\n get size(): number {\n return this.#channels.size\n }\n}\n"],"mappings":"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","names":["randomUUID","randomUUID"]}
|
package/build/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SocketHealthCheck
|
|
3
|
+
} from "./chunk-HK7Z65DA.js";
|
|
1
4
|
import {
|
|
2
5
|
BaseChannel,
|
|
3
6
|
ChannelRouter,
|
|
4
7
|
PresenceManager,
|
|
5
8
|
SocketFake,
|
|
6
9
|
SocketService
|
|
7
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-5X4SKZQS.js";
|
|
8
11
|
import "./chunk-XUDFDJME.js";
|
|
9
|
-
import {
|
|
10
|
-
SocketHealthCheck
|
|
11
|
-
} from "./chunk-HK7Z65DA.js";
|
|
12
12
|
import "./chunk-GKAD2UOA.js";
|
|
13
13
|
export {
|
|
14
14
|
BaseChannel,
|
|
@@ -60,6 +60,14 @@ declare class Channel {
|
|
|
60
60
|
* Removes an event listener.
|
|
61
61
|
*/
|
|
62
62
|
stopListening(event: string, handler?: EventHandler): this;
|
|
63
|
+
/**
|
|
64
|
+
* Listens for a client event whispered by another channel member.
|
|
65
|
+
*/
|
|
66
|
+
listenForWhisper<T = unknown>(event: string, handler: EventHandler<T>): this;
|
|
67
|
+
/**
|
|
68
|
+
* Removes a whispered client event listener.
|
|
69
|
+
*/
|
|
70
|
+
stopListeningForWhisper(event: string, handler?: EventHandler): this;
|
|
63
71
|
/**
|
|
64
72
|
* Subscribes to the channel.
|
|
65
73
|
*
|
|
@@ -77,6 +85,10 @@ declare class Channel {
|
|
|
77
85
|
* Sends an event to the server.
|
|
78
86
|
*/
|
|
79
87
|
send<T = unknown>(event: string, data?: T): this;
|
|
88
|
+
/**
|
|
89
|
+
* Relays a client event to other members of this channel.
|
|
90
|
+
*/
|
|
91
|
+
whisper<T = unknown>(event: string, data?: T): this;
|
|
80
92
|
/**
|
|
81
93
|
* Sends an event and waits for a response.
|
|
82
94
|
*/
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/client/channel.ts
|
|
2
|
+
var CLIENT_EVENT_PREFIX = "client:";
|
|
2
3
|
var Channel = class {
|
|
3
4
|
/**
|
|
4
5
|
* Channel name.
|
|
@@ -117,6 +118,18 @@ var Channel = class {
|
|
|
117
118
|
}
|
|
118
119
|
return this;
|
|
119
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Listens for a client event whispered by another channel member.
|
|
123
|
+
*/
|
|
124
|
+
listenForWhisper(event, handler) {
|
|
125
|
+
return this.listen(`${CLIENT_EVENT_PREFIX}${event}`, handler);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Removes a whispered client event listener.
|
|
129
|
+
*/
|
|
130
|
+
stopListeningForWhisper(event, handler) {
|
|
131
|
+
return this.stopListening(`${CLIENT_EVENT_PREFIX}${event}`, handler);
|
|
132
|
+
}
|
|
120
133
|
/**
|
|
121
134
|
* Subscribes to the channel.
|
|
122
135
|
*
|
|
@@ -225,6 +238,22 @@ var Channel = class {
|
|
|
225
238
|
this.#transport.send({ type: "message", ...payload });
|
|
226
239
|
return this;
|
|
227
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* Relays a client event to other members of this channel.
|
|
243
|
+
*/
|
|
244
|
+
whisper(event, data) {
|
|
245
|
+
if (!this.#active) {
|
|
246
|
+
console.warn(`Cannot whisper to channel ${this.name}: not subscribed`);
|
|
247
|
+
return this;
|
|
248
|
+
}
|
|
249
|
+
const payload = {
|
|
250
|
+
channel: this.name,
|
|
251
|
+
event,
|
|
252
|
+
data
|
|
253
|
+
};
|
|
254
|
+
this.#transport.send({ type: "whisper", ...payload });
|
|
255
|
+
return this;
|
|
256
|
+
}
|
|
228
257
|
/**
|
|
229
258
|
* Sends an event and waits for a response.
|
|
230
259
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/client/channel.ts","../../../src/client/socket_session.ts","../../../src/client/socket.ts"],"sourcesContent":["import type {\n SocketClientTransport,\n SubscribeOptions,\n SubscribeResult,\n UnsubscribeOptions,\n PresenceData,\n PresenceUser,\n EventHandler,\n ChannelMessage,\n ChannelAck,\n} from './types.js'\n\ninterface PendingSubscribe {\n promise: Promise<Channel>\n resolve: (channel: Channel) => void\n reject: (error: Error) => void\n token: number\n}\n\n/**\n * Represents a subscribed channel.\n * Chainable API inspired by Laravel Echo.\n *\n * @example\n * ```ts\n * socket.channel('chat/general')\n * .here((users) => setOnlineUsers(users))\n * .joining((user) => console.log(`${user.name} joined`))\n * .leaving((user) => console.log(`${user.name} left`))\n * .listen('chat:message', (msg) => addMessage(msg))\n * .subscribe()\n * ```\n */\nexport class Channel {\n /**\n * Channel name.\n */\n readonly name: string\n\n /**\n * Socket client transport.\n */\n #transport: SocketClientTransport\n\n /**\n * Whether the channel should stay subscribed.\n */\n #subscribed = false\n #active = false\n\n /**\n * Event handlers grouped by event type.\n */\n #eventHandlers = new Map<string, Set<EventHandler>>()\n\n /**\n * Current presence data.\n */\n #presence: PresenceData | null = null\n\n /**\n * Present user IDs used to detect join and leave events.\n */\n #presentUserIds = new Set<string>()\n\n /**\n * Handler registered through .here().\n */\n #hereHandler: EventHandler<PresenceUser[]> | null = null\n\n /**\n * Handler registered through .joining().\n */\n #joiningHandler: EventHandler<PresenceUser> | null = null\n\n /**\n * Handler registered through .leaving().\n */\n #leavingHandler: EventHandler<PresenceUser> | null = null\n\n /**\n * Internal transport handlers to clean up on unsubscribe.\n */\n #boundPresenceHandler: ((data: unknown) => void) | null = null\n #boundEventHandler: ((data: unknown) => void) | null = null\n #boundDisconnectHandler: (() => void) | null = null\n #listenersAttached = false\n #pendingSubscribe: PendingSubscribe | null = null\n #subscribeToken = 0\n\n constructor(name: string, transport: SocketClientTransport) {\n this.name = name\n this.#transport = transport\n }\n\n /**\n * Whether the channel should stay subscribed.\n */\n get subscribed(): boolean {\n return this.#subscribed\n }\n\n /**\n * Whether the channel is active on the server.\n */\n get active(): boolean {\n return this.#active\n }\n\n /**\n * List of present users.\n */\n get users(): PresenceUser[] {\n return this.#presence?.users ?? []\n }\n\n /**\n * Number of present users.\n */\n get count(): number {\n return this.#presence?.count ?? 0\n }\n\n /**\n * Registers a handler called with the present users.\n * Called immediately on subscribe and after each presence change.\n */\n here(handler: EventHandler<PresenceUser[]>): this {\n this.#hereHandler = handler\n return this\n }\n\n /**\n * Registers a handler called when a user joins.\n */\n joining(handler: EventHandler<PresenceUser>): this {\n this.#joiningHandler = handler\n return this\n }\n\n /**\n * Registers a handler called when a user leaves.\n */\n leaving(handler: EventHandler<PresenceUser>): this {\n this.#leavingHandler = handler\n return this\n }\n\n /**\n * Listens for an event on this channel.\n */\n listen<T = unknown>(event: string, handler: EventHandler<T>): this {\n if (!this.#eventHandlers.has(event)) {\n this.#eventHandlers.set(event, new Set())\n }\n this.#eventHandlers.get(event)!.add(handler as EventHandler)\n return this\n }\n\n /**\n * Removes an event listener.\n */\n stopListening(event: string, handler?: EventHandler): this {\n if (handler) {\n this.#eventHandlers.get(event)?.delete(handler)\n } else {\n this.#eventHandlers.delete(event)\n }\n return this\n }\n\n /**\n * Subscribes to the channel.\n *\n * client listeners -> subscribe ack -> active channel\n * | | |\n * v v v\n * events ready presence sync send/listen\n */\n async subscribe(options: SubscribeOptions = {}): Promise<this> {\n this.#subscribed = true\n\n if (this.#pendingSubscribe) {\n return this.#pendingSubscribe.promise as Promise<this>\n }\n\n if (this.#active) {\n return this\n }\n\n const timeout = options.timeout ?? 5000\n\n // Attach listeners before subscribing so the first server events are not missed.\n if (!this.#listenersAttached) {\n this.#setupSocketListeners()\n }\n\n const token = ++this.#subscribeToken\n\n let resolvePromise: (channel: Channel) => void\n let rejectPromise: (error: Error) => void\n\n const promise = new Promise<Channel>((resolve, reject) => {\n resolvePromise = resolve\n rejectPromise = reject\n })\n\n this.#pendingSubscribe = {\n promise,\n resolve: resolvePromise!,\n reject: rejectPromise!,\n token,\n }\n\n const { resolve, reject } = this.#pendingSubscribe\n\n const timer = setTimeout(() => {\n if (token !== this.#subscribeToken) return\n this.#pendingSubscribe = null\n this.#resetConnectionState()\n reject(new Error(`Subscribe timeout for channel: ${this.name}`))\n }, timeout)\n\n this.#transport\n .sendRequest<SubscribeResult>({ type: 'subscribe', channel: this.name }, timeout)\n .then((response) => {\n if (token !== this.#subscribeToken) return\n clearTimeout(timer)\n this.#pendingSubscribe = null\n\n if (response.ok) {\n this.#active = true\n\n const presenceData = response.data?.presenceData ?? response.presenceData\n\n // Apply the initial presence snapshot.\n if (presenceData) {\n this.#handleInitialPresence(presenceData)\n }\n\n resolve(this)\n } else {\n this.#resetConnectionState()\n reject(new Error(response.error ?? 'Subscribe failed'))\n }\n })\n .catch((error) => {\n if (token !== this.#subscribeToken) return\n clearTimeout(timer)\n this.#pendingSubscribe = null\n this.#resetConnectionState()\n reject(error instanceof Error ? error : new Error('Subscribe failed'))\n })\n\n return promise as Promise<this>\n }\n\n /**\n * Unsubscribes from the channel.\n */\n async unsubscribe(options: UnsubscribeOptions = {}): Promise<void> {\n if (!this.#subscribed && !this.#active) return\n this.#subscribed = false\n this.#subscribeToken++\n if (this.#pendingSubscribe) {\n this.#pendingSubscribe.reject(new Error(`Unsubscribed from channel: ${this.name}`))\n this.#pendingSubscribe = null\n }\n\n return new Promise((resolve) => {\n const timeout = options.timeout ?? 5000\n const timer = setTimeout(() => {\n this.#cleanupTerminal()\n resolve()\n }, timeout)\n\n this.#transport\n .sendRequest({ type: 'unsubscribe', channel: this.name }, timeout)\n .then(() => {\n clearTimeout(timer)\n this.#cleanupTerminal()\n resolve()\n })\n .catch(() => {\n clearTimeout(timer)\n this.#cleanupTerminal()\n resolve()\n })\n })\n }\n\n /**\n * Sends an event to the server.\n */\n send<T = unknown>(event: string, data?: T): this {\n if (!this.#active) {\n console.warn(`Cannot send to channel ${this.name}: not subscribed`)\n return this\n }\n const payload: ChannelMessage = {\n channel: this.name,\n event,\n data,\n }\n this.#transport.send({ type: 'message', ...payload })\n return this\n }\n\n /**\n * Sends an event and waits for a response.\n */\n async sendWithAck<T = unknown, R = unknown>(event: string, data?: T): Promise<R> {\n if (!this.#active) {\n throw new Error(`Cannot send to channel ${this.name}: not subscribed`)\n }\n const payload: ChannelMessage = {\n channel: this.name,\n event,\n data,\n }\n const response = await this.#transport.sendRequest<ChannelAck<R>>({\n type: 'message',\n ...payload,\n })\n if (!response?.ok) {\n throw new Error(response?.error ?? `Channel handler failed: ${event}`)\n }\n return response.data as R\n }\n\n /**\n * Configures transport listeners.\n */\n #setupSocketListeners(): void {\n // Handler for presence:update.\n this.#boundPresenceHandler = (data: unknown) => {\n if (!data || typeof data !== 'object') return\n this.#handlePresenceUpdate(data as PresenceData)\n }\n this.#transport.on(`channel:${this.name}:presence:update`, this.#boundPresenceHandler)\n\n this.#boundEventHandler = (data: unknown) => {\n if (!data || typeof data !== 'object') return\n\n const payload = data as { event?: string; data?: unknown }\n if (!payload.event) return\n\n const handlers = this.#eventHandlers.get(payload.event)\n handlers?.forEach((handler) => handler(payload.data))\n }\n this.#transport.on(`channel:${this.name}:event`, this.#boundEventHandler)\n\n // Mark the channel inactive when the socket disconnects.\n this.#boundDisconnectHandler = () => {\n this.#resetConnectionState()\n }\n this.#transport.on('disconnect', this.#boundDisconnectHandler)\n\n this.#listenersAttached = true\n }\n\n /**\n * Handles the initial presence snapshot.\n */\n #handleInitialPresence(data: PresenceData): void {\n this.#presence = data\n this.#presentUserIds = new Set(data.users.map((u) => u.id))\n\n // Call here() with all users.\n this.#hereHandler?.(data.users)\n }\n\n /**\n * Handles a presence update.\n *\n * previous IDs current IDs\n * | |\n * +-- diff in -----+--> joining / leaving callbacks\n */\n #handlePresenceUpdate(data: PresenceData): void {\n const previousIds = this.#presentUserIds\n const currentIds = new Set(data.users.map((u) => u.id))\n\n // Detect new users.\n for (const user of data.users) {\n if (!previousIds.has(user.id)) {\n this.#joiningHandler?.(user)\n }\n }\n\n // Detect users that left.\n if (this.#presence) {\n for (const user of this.#presence.users) {\n if (!currentIds.has(user.id)) {\n this.#leavingHandler?.(user)\n }\n }\n }\n\n // Update local state.\n this.#presence = data\n this.#presentUserIds = currentIds\n\n // Call here() with the updated list.\n this.#hereHandler?.(data.users)\n }\n\n #resetConnectionState(): void {\n this.#active = false\n this.#presence = null\n this.#presentUserIds.clear()\n }\n\n #detachTransportListeners(): void {\n if (this.#boundPresenceHandler) {\n this.#transport.off(`channel:${this.name}:presence:update`, this.#boundPresenceHandler)\n this.#boundPresenceHandler = null\n }\n\n if (this.#boundEventHandler) {\n this.#transport.off(`channel:${this.name}:event`, this.#boundEventHandler)\n this.#boundEventHandler = null\n }\n\n if (this.#boundDisconnectHandler) {\n this.#transport.off('disconnect', this.#boundDisconnectHandler)\n this.#boundDisconnectHandler = null\n }\n\n this.#listenersAttached = false\n }\n\n #clearUserCallbacks(): void {\n this.#eventHandlers.clear()\n this.#hereHandler = null\n this.#joiningHandler = null\n this.#leavingHandler = null\n }\n\n /**\n * Performs terminal cleanup after the user leaves or the socket is disposed.\n */\n #cleanupTerminal(): void {\n this.#subscribed = false\n this.#resetConnectionState()\n this.#detachTransportListeners()\n this.#clearUserCallbacks()\n }\n}\n","import type { ConnectionState, EventHandler } from './types.js'\nimport type { ServerProtocolMessage } from '../shared_types.js'\n\ninterface PendingRequest {\n resolve: (value: unknown) => void\n reject: (error: Error) => void\n timer: ReturnType<typeof setTimeout>\n}\n\ninterface ClientSocketSessionOptions {\n buildUrl: () => string\n createWebSocket?: (url: string) => WebSocket\n autoReconnect?: boolean\n reconnectDelay?: number\n reconnectMaxDelay?: number\n onConnected?: () => void\n}\n\nexport class ClientSocketSession {\n #ws: WebSocket | null = null\n #state: ConnectionState = 'disconnected'\n #connectPromise: Promise<void> | null = null\n #cancelConnect: ((error: Error) => void) | null = null\n #stateHandlers = new Set<EventHandler<ConnectionState>>()\n #eventHandlers = new Map<string, Set<EventHandler>>()\n #pendingRequests = new Map<string, PendingRequest>()\n #requestId = 0\n #reconnectAttempts = 0\n #reconnectTimer: ReturnType<typeof setTimeout> | null = null\n #manualDisconnect = false\n #buildUrl: () => string\n #createWebSocket: (url: string) => WebSocket\n #autoReconnect: boolean\n #reconnectDelay: number\n #reconnectMaxDelay: number\n #onConnected?: () => void\n\n constructor(options: ClientSocketSessionOptions) {\n this.#buildUrl = options.buildUrl\n this.#createWebSocket = options.createWebSocket ?? ((url) => new WebSocket(url))\n this.#autoReconnect = options.autoReconnect ?? true\n this.#reconnectDelay = Math.max(0, options.reconnectDelay ?? 250)\n this.#reconnectMaxDelay = Math.max(this.#reconnectDelay, options.reconnectMaxDelay ?? 5000)\n this.#onConnected = options.onConnected\n }\n\n get state(): ConnectionState {\n return this.#state\n }\n\n get connected(): boolean {\n return this.#state === 'connected'\n }\n\n connect(): Promise<void> {\n if (this.#state === 'connected') {\n return Promise.resolve()\n }\n\n if (this.#connectPromise) {\n return this.#connectPromise\n }\n\n this.#manualDisconnect = false\n this.#setState('connecting')\n\n const connectPromise = new Promise<void>((resolve, reject) => {\n const ws = this.#createWebSocket(this.#buildUrl())\n this.#ws = ws\n let settled = false\n\n const cleanup = () => {\n ws.removeEventListener('open', handleOpen)\n ws.removeEventListener('error', handleError)\n if (this.#connectPromise === connectPromise) {\n this.#connectPromise = null\n }\n if (this.#cancelConnect === cancelConnect) {\n this.#cancelConnect = null\n }\n }\n\n const cancelConnect = (error: Error) => {\n if (settled) {\n return\n }\n\n settled = true\n cleanup()\n reject(error)\n }\n\n const handleOpen = () => {\n settled = true\n cleanup()\n this.#reconnectAttempts = 0\n this.#setState('connected')\n this.#emitLocal('connect')\n this.#onConnected?.()\n resolve()\n }\n\n const handleError = () => {\n cancelConnect(new Error('WebSocket connection failed'))\n if (this.#ws === ws) {\n this.#setState('disconnected')\n }\n }\n\n const handleClose = () => {\n cancelConnect(new Error('WebSocket connection failed'))\n\n if (this.#ws === ws) {\n this.#handleClose()\n }\n }\n\n this.#cancelConnect = cancelConnect\n ws.addEventListener('open', handleOpen)\n ws.addEventListener('error', handleError)\n ws.addEventListener('message', (event) => this.#handleIncoming(event.data))\n ws.addEventListener('close', handleClose)\n })\n\n this.#connectPromise = connectPromise\n return connectPromise\n }\n\n disconnect(): void {\n this.#manualDisconnect = true\n if (this.#reconnectTimer) {\n clearTimeout(this.#reconnectTimer)\n this.#reconnectTimer = null\n }\n\n this.#cancelConnect?.(new Error('Socket disconnected'))\n this.#reconnectAttempts = 0\n this.#ws?.close()\n this.#rejectPending(new Error('Socket disconnected'))\n }\n\n onStateChange(handler: EventHandler<ConnectionState>): () => void {\n this.#stateHandlers.add(handler)\n\n return () => {\n this.#stateHandlers.delete(handler)\n }\n }\n\n on<T = unknown>(event: string, handler: EventHandler<T>): () => void {\n if (!this.#eventHandlers.has(event)) {\n this.#eventHandlers.set(event, new Set())\n }\n\n this.#eventHandlers.get(event)!.add(handler as EventHandler)\n\n return () => {\n this.off(event, handler as EventHandler)\n }\n }\n\n off(event: string, handler: EventHandler): void {\n this.#eventHandlers.get(event)?.delete(handler)\n }\n\n send(message: Record<string, unknown>): void {\n if (this.#ws?.readyState !== WebSocket.OPEN) {\n return\n }\n\n this.#ws.send(JSON.stringify(message))\n }\n\n sendRequest<T = unknown>(message: Record<string, unknown>, timeout = 5000): Promise<T> {\n if (this.#ws?.readyState !== WebSocket.OPEN) {\n return Promise.reject(new Error('Socket is not connected'))\n }\n\n const id = String(++this.#requestId)\n const payload = { id, ...message }\n\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n this.#pendingRequests.delete(id)\n reject(new Error('Socket request timed out'))\n }, timeout)\n\n this.#pendingRequests.set(id, { resolve: resolve as (value: unknown) => void, reject, timer })\n this.#ws!.send(JSON.stringify(payload))\n })\n }\n\n #handleIncoming(raw: unknown): void {\n const message = this.#parseMessage(raw)\n if (!message) {\n return\n }\n\n if (message.type === 'ack') {\n this.#resolveAck(message)\n return\n }\n\n if (message.type === 'event') {\n if (message.channel) {\n if (message.event === 'presence:update') {\n this.#emitLocal(`channel:${message.channel}:presence:update`, message.data)\n return\n }\n\n this.#emitLocal(`channel:${message.channel}:event`, {\n event: message.event,\n data: message.data,\n })\n return\n }\n\n this.#emitLocal(message.event, message.data)\n }\n }\n\n #parseMessage(raw: unknown): ServerProtocolMessage | null {\n try {\n const data = typeof raw === 'string' ? raw : String(raw)\n const message = JSON.parse(data)\n return message && typeof message === 'object' ? (message as ServerProtocolMessage) : null\n } catch {\n return null\n }\n }\n\n #resolveAck(message: Extract<ServerProtocolMessage, { type: 'ack' }>): void {\n if (!message.id) {\n return\n }\n\n const pending = this.#pendingRequests.get(message.id)\n if (!pending) {\n return\n }\n\n clearTimeout(pending.timer)\n this.#pendingRequests.delete(message.id)\n\n if (message.ok) {\n pending.resolve({ ok: true, data: message.data })\n } else {\n pending.resolve({ ok: false, error: message.error })\n }\n }\n\n #handleClose(): void {\n const shouldEmitDisconnect = this.#state !== 'disconnected'\n\n this.#setState('disconnected')\n if (shouldEmitDisconnect) {\n this.#emitLocal('disconnect')\n }\n this.#rejectPending(new Error('Socket disconnected'))\n\n this.#scheduleReconnect()\n }\n\n #scheduleReconnect(): void {\n if (this.#manualDisconnect || !this.#autoReconnect || this.#reconnectTimer) {\n return\n }\n\n const delay = Math.min(\n this.#reconnectDelay * 2 ** this.#reconnectAttempts,\n this.#reconnectMaxDelay\n )\n this.#reconnectAttempts += 1\n\n this.#reconnectTimer = setTimeout(() => {\n this.#reconnectTimer = null\n this.connect().catch(() => this.#handleClose())\n }, delay)\n }\n\n #rejectPending(error: Error): void {\n for (const [id, pending] of this.#pendingRequests) {\n clearTimeout(pending.timer)\n pending.reject(error)\n this.#pendingRequests.delete(id)\n }\n }\n\n #emitLocal(event: string, data?: unknown): void {\n this.#eventHandlers.get(event)?.forEach((handler) => handler(data))\n }\n\n #setState(state: ConnectionState): void {\n if (this.#state === state) {\n return\n }\n\n this.#state = state\n this.#stateHandlers.forEach((handler) => handler(state))\n }\n}\n","import { Channel } from './channel.js'\nimport { ClientSocketSession } from './socket_session.js'\nimport type {\n SocketOptions,\n ConnectionState,\n EventHandler,\n SocketClientTransport,\n} from './types.js'\n\n/**\n * Main Socket client.\n */\nexport class Socket implements SocketClientTransport {\n #channels = new Map<string, Channel>()\n #url: string\n #path: string\n #auth?: Record<string, unknown>\n #session: ClientSocketSession\n\n constructor(options: SocketOptions = {}) {\n const browserWindow = globalThis as typeof globalThis & {\n window?: { location?: { origin?: string } }\n }\n const browserOrigin =\n typeof globalThis === 'object' && browserWindow.window?.location?.origin\n ? browserWindow.window.location.origin\n : ''\n\n this.#url = options.url ?? browserOrigin\n this.#path = options.path ?? '/socket'\n this.#auth = options.auth\n this.#session = new ClientSocketSession({\n buildUrl: () => this.#buildUrl(),\n autoReconnect: options.autoReconnect,\n reconnectDelay: options.reconnectDelay,\n reconnectMaxDelay: options.reconnectMaxDelay,\n onConnected: () => {\n void this.#resubscribeChannels()\n },\n })\n }\n\n get state(): ConnectionState {\n return this.#session.state\n }\n\n get connected(): boolean {\n return this.#session.connected\n }\n\n get id(): string | undefined {\n return undefined\n }\n\n connect(): Promise<void> {\n return this.#session.connect()\n }\n\n disconnect(): void {\n for (const channel of this.#channels.values()) {\n channel.unsubscribe().catch(() => {})\n }\n this.#channels.clear()\n\n this.#session.disconnect()\n }\n\n channel(name: string): Channel {\n if (!this.#channels.has(name)) {\n this.#channels.set(name, new Channel(name, this))\n }\n\n return this.#channels.get(name)!\n }\n\n async leave(name: string): Promise<void> {\n const channel = this.#channels.get(name)\n if (channel) {\n await channel.unsubscribe()\n this.#channels.delete(name)\n }\n }\n\n onStateChange(handler: EventHandler<ConnectionState>): () => void {\n return this.#session.onStateChange(handler)\n }\n\n on<T = unknown>(event: string, handler: EventHandler<T>): () => void {\n return this.#session.on(event, handler)\n }\n\n off(event: string, handler: EventHandler): void {\n this.#session.off(event, handler)\n }\n\n send(message: Record<string, unknown>): void {\n this.#session.send(message)\n }\n\n sendRequest<T = unknown>(message: Record<string, unknown>, timeout = 5000): Promise<T> {\n return this.#session.sendRequest(message, timeout)\n }\n\n #buildUrl(): string {\n const browserGlobal = globalThis as typeof globalThis & {\n location?: { origin?: string }\n }\n const url = new URL(this.#url || browserGlobal.location?.origin || 'http://localhost')\n url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:'\n url.pathname = this.#path\n\n if (this.#auth) {\n url.searchParams.set('auth', JSON.stringify(this.#auth))\n }\n\n return url.toString()\n }\n\n async #resubscribeChannels(): Promise<void> {\n for (const channel of this.#channels.values()) {\n if (channel.subscribed && !channel.active) {\n await channel.subscribe().catch(console.error)\n }\n }\n }\n}\n"],"mappings":";AAiCO,IAAM,UAAN,MAAc;AAAA;AAAA;AAAA;AAAA,EAIV;AAAA;AAAA;AAAA;AAAA,EAKT;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc;AAAA,EACd,UAAU;AAAA;AAAA;AAAA;AAAA,EAKV,iBAAiB,oBAAI,IAA+B;AAAA;AAAA;AAAA;AAAA,EAKpD,YAAiC;AAAA;AAAA;AAAA;AAAA,EAKjC,kBAAkB,oBAAI,IAAY;AAAA;AAAA;AAAA;AAAA,EAKlC,eAAoD;AAAA;AAAA;AAAA;AAAA,EAKpD,kBAAqD;AAAA;AAAA;AAAA;AAAA,EAKrD,kBAAqD;AAAA;AAAA;AAAA;AAAA,EAKrD,wBAA0D;AAAA,EAC1D,qBAAuD;AAAA,EACvD,0BAA+C;AAAA,EAC/C,qBAAqB;AAAA,EACrB,oBAA6C;AAAA,EAC7C,kBAAkB;AAAA,EAElB,YAAY,MAAc,WAAkC;AAC1D,SAAK,OAAO;AACZ,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,aAAsB;AACxB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,SAAkB;AACpB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,QAAwB;AAC1B,WAAO,KAAK,WAAW,SAAS,CAAC;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,QAAgB;AAClB,WAAO,KAAK,WAAW,SAAS;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,KAAK,SAA6C;AAChD,SAAK,eAAe;AACpB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ,SAA2C;AACjD,SAAK,kBAAkB;AACvB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ,SAA2C;AACjD,SAAK,kBAAkB;AACvB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,OAAoB,OAAe,SAAgC;AACjE,QAAI,CAAC,KAAK,eAAe,IAAI,KAAK,GAAG;AACnC,WAAK,eAAe,IAAI,OAAO,oBAAI,IAAI,CAAC;AAAA,IAC1C;AACA,SAAK,eAAe,IAAI,KAAK,EAAG,IAAI,OAAuB;AAC3D,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,OAAe,SAA8B;AACzD,QAAI,SAAS;AACX,WAAK,eAAe,IAAI,KAAK,GAAG,OAAO,OAAO;AAAA,IAChD,OAAO;AACL,WAAK,eAAe,OAAO,KAAK;AAAA,IAClC;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,UAAU,UAA4B,CAAC,GAAkB;AAC7D,SAAK,cAAc;AAEnB,QAAI,KAAK,mBAAmB;AAC1B,aAAO,KAAK,kBAAkB;AAAA,IAChC;AAEA,QAAI,KAAK,SAAS;AAChB,aAAO;AAAA,IACT;AAEA,UAAM,UAAU,QAAQ,WAAW;AAGnC,QAAI,CAAC,KAAK,oBAAoB;AAC5B,WAAK,sBAAsB;AAAA,IAC7B;AAEA,UAAM,QAAQ,EAAE,KAAK;AAErB,QAAI;AACJ,QAAI;AAEJ,UAAM,UAAU,IAAI,QAAiB,CAACA,UAASC,YAAW;AACxD,uBAAiBD;AACjB,sBAAgBC;AAAA,IAClB,CAAC;AAED,SAAK,oBAAoB;AAAA,MACvB;AAAA,MACA,SAAS;AAAA,MACT,QAAQ;AAAA,MACR;AAAA,IACF;AAEA,UAAM,EAAE,SAAS,OAAO,IAAI,KAAK;AAEjC,UAAM,QAAQ,WAAW,MAAM;AAC7B,UAAI,UAAU,KAAK,gBAAiB;AACpC,WAAK,oBAAoB;AACzB,WAAK,sBAAsB;AAC3B,aAAO,IAAI,MAAM,kCAAkC,KAAK,IAAI,EAAE,CAAC;AAAA,IACjE,GAAG,OAAO;AAEV,SAAK,WACF,YAA6B,EAAE,MAAM,aAAa,SAAS,KAAK,KAAK,GAAG,OAAO,EAC/E,KAAK,CAAC,aAAa;AAClB,UAAI,UAAU,KAAK,gBAAiB;AACpC,mBAAa,KAAK;AAClB,WAAK,oBAAoB;AAEzB,UAAI,SAAS,IAAI;AACf,aAAK,UAAU;AAEf,cAAM,eAAe,SAAS,MAAM,gBAAgB,SAAS;AAG7D,YAAI,cAAc;AAChB,eAAK,uBAAuB,YAAY;AAAA,QAC1C;AAEA,gBAAQ,IAAI;AAAA,MACd,OAAO;AACL,aAAK,sBAAsB;AAC3B,eAAO,IAAI,MAAM,SAAS,SAAS,kBAAkB,CAAC;AAAA,MACxD;AAAA,IACF,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,UAAI,UAAU,KAAK,gBAAiB;AACpC,mBAAa,KAAK;AAClB,WAAK,oBAAoB;AACzB,WAAK,sBAAsB;AAC3B,aAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,kBAAkB,CAAC;AAAA,IACvE,CAAC;AAEH,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,UAA8B,CAAC,GAAkB;AACjE,QAAI,CAAC,KAAK,eAAe,CAAC,KAAK,QAAS;AACxC,SAAK,cAAc;AACnB,SAAK;AACL,QAAI,KAAK,mBAAmB;AAC1B,WAAK,kBAAkB,OAAO,IAAI,MAAM,8BAA8B,KAAK,IAAI,EAAE,CAAC;AAClF,WAAK,oBAAoB;AAAA,IAC3B;AAEA,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,YAAM,UAAU,QAAQ,WAAW;AACnC,YAAM,QAAQ,WAAW,MAAM;AAC7B,aAAK,iBAAiB;AACtB,gBAAQ;AAAA,MACV,GAAG,OAAO;AAEV,WAAK,WACF,YAAY,EAAE,MAAM,eAAe,SAAS,KAAK,KAAK,GAAG,OAAO,EAChE,KAAK,MAAM;AACV,qBAAa,KAAK;AAClB,aAAK,iBAAiB;AACtB,gBAAQ;AAAA,MACV,CAAC,EACA,MAAM,MAAM;AACX,qBAAa,KAAK;AAClB,aAAK,iBAAiB;AACtB,gBAAQ;AAAA,MACV,CAAC;AAAA,IACL,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,KAAkB,OAAe,MAAgB;AAC/C,QAAI,CAAC,KAAK,SAAS;AACjB,cAAQ,KAAK,0BAA0B,KAAK,IAAI,kBAAkB;AAClE,aAAO;AAAA,IACT;AACA,UAAM,UAA0B;AAAA,MAC9B,SAAS,KAAK;AAAA,MACd;AAAA,MACA;AAAA,IACF;AACA,SAAK,WAAW,KAAK,EAAE,MAAM,WAAW,GAAG,QAAQ,CAAC;AACpD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAsC,OAAe,MAAsB;AAC/E,QAAI,CAAC,KAAK,SAAS;AACjB,YAAM,IAAI,MAAM,0BAA0B,KAAK,IAAI,kBAAkB;AAAA,IACvE;AACA,UAAM,UAA0B;AAAA,MAC9B,SAAS,KAAK;AAAA,MACd;AAAA,MACA;AAAA,IACF;AACA,UAAM,WAAW,MAAM,KAAK,WAAW,YAA2B;AAAA,MAChE,MAAM;AAAA,MACN,GAAG;AAAA,IACL,CAAC;AACD,QAAI,CAAC,UAAU,IAAI;AACjB,YAAM,IAAI,MAAM,UAAU,SAAS,2BAA2B,KAAK,EAAE;AAAA,IACvE;AACA,WAAO,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,wBAA8B;AAE5B,SAAK,wBAAwB,CAAC,SAAkB;AAC9C,UAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,WAAK,sBAAsB,IAAoB;AAAA,IACjD;AACA,SAAK,WAAW,GAAG,WAAW,KAAK,IAAI,oBAAoB,KAAK,qBAAqB;AAErF,SAAK,qBAAqB,CAAC,SAAkB;AAC3C,UAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AAEvC,YAAM,UAAU;AAChB,UAAI,CAAC,QAAQ,MAAO;AAEpB,YAAM,WAAW,KAAK,eAAe,IAAI,QAAQ,KAAK;AACtD,gBAAU,QAAQ,CAAC,YAAY,QAAQ,QAAQ,IAAI,CAAC;AAAA,IACtD;AACA,SAAK,WAAW,GAAG,WAAW,KAAK,IAAI,UAAU,KAAK,kBAAkB;AAGxE,SAAK,0BAA0B,MAAM;AACnC,WAAK,sBAAsB;AAAA,IAC7B;AACA,SAAK,WAAW,GAAG,cAAc,KAAK,uBAAuB;AAE7D,SAAK,qBAAqB;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKA,uBAAuB,MAA0B;AAC/C,SAAK,YAAY;AACjB,SAAK,kBAAkB,IAAI,IAAI,KAAK,MAAM,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AAG1D,SAAK,eAAe,KAAK,KAAK;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,sBAAsB,MAA0B;AAC9C,UAAM,cAAc,KAAK;AACzB,UAAM,aAAa,IAAI,IAAI,KAAK,MAAM,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AAGtD,eAAW,QAAQ,KAAK,OAAO;AAC7B,UAAI,CAAC,YAAY,IAAI,KAAK,EAAE,GAAG;AAC7B,aAAK,kBAAkB,IAAI;AAAA,MAC7B;AAAA,IACF;AAGA,QAAI,KAAK,WAAW;AAClB,iBAAW,QAAQ,KAAK,UAAU,OAAO;AACvC,YAAI,CAAC,WAAW,IAAI,KAAK,EAAE,GAAG;AAC5B,eAAK,kBAAkB,IAAI;AAAA,QAC7B;AAAA,MACF;AAAA,IACF;AAGA,SAAK,YAAY;AACjB,SAAK,kBAAkB;AAGvB,SAAK,eAAe,KAAK,KAAK;AAAA,EAChC;AAAA,EAEA,wBAA8B;AAC5B,SAAK,UAAU;AACf,SAAK,YAAY;AACjB,SAAK,gBAAgB,MAAM;AAAA,EAC7B;AAAA,EAEA,4BAAkC;AAChC,QAAI,KAAK,uBAAuB;AAC9B,WAAK,WAAW,IAAI,WAAW,KAAK,IAAI,oBAAoB,KAAK,qBAAqB;AACtF,WAAK,wBAAwB;AAAA,IAC/B;AAEA,QAAI,KAAK,oBAAoB;AAC3B,WAAK,WAAW,IAAI,WAAW,KAAK,IAAI,UAAU,KAAK,kBAAkB;AACzE,WAAK,qBAAqB;AAAA,IAC5B;AAEA,QAAI,KAAK,yBAAyB;AAChC,WAAK,WAAW,IAAI,cAAc,KAAK,uBAAuB;AAC9D,WAAK,0BAA0B;AAAA,IACjC;AAEA,SAAK,qBAAqB;AAAA,EAC5B;AAAA,EAEA,sBAA4B;AAC1B,SAAK,eAAe,MAAM;AAC1B,SAAK,eAAe;AACpB,SAAK,kBAAkB;AACvB,SAAK,kBAAkB;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAyB;AACvB,SAAK,cAAc;AACnB,SAAK,sBAAsB;AAC3B,SAAK,0BAA0B;AAC/B,SAAK,oBAAoB;AAAA,EAC3B;AACF;;;AC9aO,IAAM,sBAAN,MAA0B;AAAA,EAC/B,MAAwB;AAAA,EACxB,SAA0B;AAAA,EAC1B,kBAAwC;AAAA,EACxC,iBAAkD;AAAA,EAClD,iBAAiB,oBAAI,IAAmC;AAAA,EACxD,iBAAiB,oBAAI,IAA+B;AAAA,EACpD,mBAAmB,oBAAI,IAA4B;AAAA,EACnD,aAAa;AAAA,EACb,qBAAqB;AAAA,EACrB,kBAAwD;AAAA,EACxD,oBAAoB;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,YAAY,SAAqC;AAC/C,SAAK,YAAY,QAAQ;AACzB,SAAK,mBAAmB,QAAQ,oBAAoB,CAAC,QAAQ,IAAI,UAAU,GAAG;AAC9E,SAAK,iBAAiB,QAAQ,iBAAiB;AAC/C,SAAK,kBAAkB,KAAK,IAAI,GAAG,QAAQ,kBAAkB,GAAG;AAChE,SAAK,qBAAqB,KAAK,IAAI,KAAK,iBAAiB,QAAQ,qBAAqB,GAAI;AAC1F,SAAK,eAAe,QAAQ;AAAA,EAC9B;AAAA,EAEA,IAAI,QAAyB;AAC3B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,YAAqB;AACvB,WAAO,KAAK,WAAW;AAAA,EACzB;AAAA,EAEA,UAAyB;AACvB,QAAI,KAAK,WAAW,aAAa;AAC/B,aAAO,QAAQ,QAAQ;AAAA,IACzB;AAEA,QAAI,KAAK,iBAAiB;AACxB,aAAO,KAAK;AAAA,IACd;AAEA,SAAK,oBAAoB;AACzB,SAAK,UAAU,YAAY;AAE3B,UAAM,iBAAiB,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5D,YAAM,KAAK,KAAK,iBAAiB,KAAK,UAAU,CAAC;AACjD,WAAK,MAAM;AACX,UAAI,UAAU;AAEd,YAAM,UAAU,MAAM;AACpB,WAAG,oBAAoB,QAAQ,UAAU;AACzC,WAAG,oBAAoB,SAAS,WAAW;AAC3C,YAAI,KAAK,oBAAoB,gBAAgB;AAC3C,eAAK,kBAAkB;AAAA,QACzB;AACA,YAAI,KAAK,mBAAmB,eAAe;AACzC,eAAK,iBAAiB;AAAA,QACxB;AAAA,MACF;AAEA,YAAM,gBAAgB,CAAC,UAAiB;AACtC,YAAI,SAAS;AACX;AAAA,QACF;AAEA,kBAAU;AACV,gBAAQ;AACR,eAAO,KAAK;AAAA,MACd;AAEA,YAAM,aAAa,MAAM;AACvB,kBAAU;AACV,gBAAQ;AACR,aAAK,qBAAqB;AAC1B,aAAK,UAAU,WAAW;AAC1B,aAAK,WAAW,SAAS;AACzB,aAAK,eAAe;AACpB,gBAAQ;AAAA,MACV;AAEA,YAAM,cAAc,MAAM;AACxB,sBAAc,IAAI,MAAM,6BAA6B,CAAC;AACtD,YAAI,KAAK,QAAQ,IAAI;AACnB,eAAK,UAAU,cAAc;AAAA,QAC/B;AAAA,MACF;AAEA,YAAM,cAAc,MAAM;AACxB,sBAAc,IAAI,MAAM,6BAA6B,CAAC;AAEtD,YAAI,KAAK,QAAQ,IAAI;AACnB,eAAK,aAAa;AAAA,QACpB;AAAA,MACF;AAEA,WAAK,iBAAiB;AACtB,SAAG,iBAAiB,QAAQ,UAAU;AACtC,SAAG,iBAAiB,SAAS,WAAW;AACxC,SAAG,iBAAiB,WAAW,CAAC,UAAU,KAAK,gBAAgB,MAAM,IAAI,CAAC;AAC1E,SAAG,iBAAiB,SAAS,WAAW;AAAA,IAC1C,CAAC;AAED,SAAK,kBAAkB;AACvB,WAAO;AAAA,EACT;AAAA,EAEA,aAAmB;AACjB,SAAK,oBAAoB;AACzB,QAAI,KAAK,iBAAiB;AACxB,mBAAa,KAAK,eAAe;AACjC,WAAK,kBAAkB;AAAA,IACzB;AAEA,SAAK,iBAAiB,IAAI,MAAM,qBAAqB,CAAC;AACtD,SAAK,qBAAqB;AAC1B,SAAK,KAAK,MAAM;AAChB,SAAK,eAAe,IAAI,MAAM,qBAAqB,CAAC;AAAA,EACtD;AAAA,EAEA,cAAc,SAAoD;AAChE,SAAK,eAAe,IAAI,OAAO;AAE/B,WAAO,MAAM;AACX,WAAK,eAAe,OAAO,OAAO;AAAA,IACpC;AAAA,EACF;AAAA,EAEA,GAAgB,OAAe,SAAsC;AACnE,QAAI,CAAC,KAAK,eAAe,IAAI,KAAK,GAAG;AACnC,WAAK,eAAe,IAAI,OAAO,oBAAI,IAAI,CAAC;AAAA,IAC1C;AAEA,SAAK,eAAe,IAAI,KAAK,EAAG,IAAI,OAAuB;AAE3D,WAAO,MAAM;AACX,WAAK,IAAI,OAAO,OAAuB;AAAA,IACzC;AAAA,EACF;AAAA,EAEA,IAAI,OAAe,SAA6B;AAC9C,SAAK,eAAe,IAAI,KAAK,GAAG,OAAO,OAAO;AAAA,EAChD;AAAA,EAEA,KAAK,SAAwC;AAC3C,QAAI,KAAK,KAAK,eAAe,UAAU,MAAM;AAC3C;AAAA,IACF;AAEA,SAAK,IAAI,KAAK,KAAK,UAAU,OAAO,CAAC;AAAA,EACvC;AAAA,EAEA,YAAyB,SAAkC,UAAU,KAAkB;AACrF,QAAI,KAAK,KAAK,eAAe,UAAU,MAAM;AAC3C,aAAO,QAAQ,OAAO,IAAI,MAAM,yBAAyB,CAAC;AAAA,IAC5D;AAEA,UAAM,KAAK,OAAO,EAAE,KAAK,UAAU;AACnC,UAAM,UAAU,EAAE,IAAI,GAAG,QAAQ;AAEjC,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,QAAQ,WAAW,MAAM;AAC7B,aAAK,iBAAiB,OAAO,EAAE;AAC/B,eAAO,IAAI,MAAM,0BAA0B,CAAC;AAAA,MAC9C,GAAG,OAAO;AAEV,WAAK,iBAAiB,IAAI,IAAI,EAAE,SAA8C,QAAQ,MAAM,CAAC;AAC7F,WAAK,IAAK,KAAK,KAAK,UAAU,OAAO,CAAC;AAAA,IACxC,CAAC;AAAA,EACH;AAAA,EAEA,gBAAgB,KAAoB;AAClC,UAAM,UAAU,KAAK,cAAc,GAAG;AACtC,QAAI,CAAC,SAAS;AACZ;AAAA,IACF;AAEA,QAAI,QAAQ,SAAS,OAAO;AAC1B,WAAK,YAAY,OAAO;AACxB;AAAA,IACF;AAEA,QAAI,QAAQ,SAAS,SAAS;AAC5B,UAAI,QAAQ,SAAS;AACnB,YAAI,QAAQ,UAAU,mBAAmB;AACvC,eAAK,WAAW,WAAW,QAAQ,OAAO,oBAAoB,QAAQ,IAAI;AAC1E;AAAA,QACF;AAEA,aAAK,WAAW,WAAW,QAAQ,OAAO,UAAU;AAAA,UAClD,OAAO,QAAQ;AAAA,UACf,MAAM,QAAQ;AAAA,QAChB,CAAC;AACD;AAAA,MACF;AAEA,WAAK,WAAW,QAAQ,OAAO,QAAQ,IAAI;AAAA,IAC7C;AAAA,EACF;AAAA,EAEA,cAAc,KAA4C;AACxD,QAAI;AACF,YAAM,OAAO,OAAO,QAAQ,WAAW,MAAM,OAAO,GAAG;AACvD,YAAM,UAAU,KAAK,MAAM,IAAI;AAC/B,aAAO,WAAW,OAAO,YAAY,WAAY,UAAoC;AAAA,IACvF,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,YAAY,SAAgE;AAC1E,QAAI,CAAC,QAAQ,IAAI;AACf;AAAA,IACF;AAEA,UAAM,UAAU,KAAK,iBAAiB,IAAI,QAAQ,EAAE;AACpD,QAAI,CAAC,SAAS;AACZ;AAAA,IACF;AAEA,iBAAa,QAAQ,KAAK;AAC1B,SAAK,iBAAiB,OAAO,QAAQ,EAAE;AAEvC,QAAI,QAAQ,IAAI;AACd,cAAQ,QAAQ,EAAE,IAAI,MAAM,MAAM,QAAQ,KAAK,CAAC;AAAA,IAClD,OAAO;AACL,cAAQ,QAAQ,EAAE,IAAI,OAAO,OAAO,QAAQ,MAAM,CAAC;AAAA,IACrD;AAAA,EACF;AAAA,EAEA,eAAqB;AACnB,UAAM,uBAAuB,KAAK,WAAW;AAE7C,SAAK,UAAU,cAAc;AAC7B,QAAI,sBAAsB;AACxB,WAAK,WAAW,YAAY;AAAA,IAC9B;AACA,SAAK,eAAe,IAAI,MAAM,qBAAqB,CAAC;AAEpD,SAAK,mBAAmB;AAAA,EAC1B;AAAA,EAEA,qBAA2B;AACzB,QAAI,KAAK,qBAAqB,CAAC,KAAK,kBAAkB,KAAK,iBAAiB;AAC1E;AAAA,IACF;AAEA,UAAM,QAAQ,KAAK;AAAA,MACjB,KAAK,kBAAkB,KAAK,KAAK;AAAA,MACjC,KAAK;AAAA,IACP;AACA,SAAK,sBAAsB;AAE3B,SAAK,kBAAkB,WAAW,MAAM;AACtC,WAAK,kBAAkB;AACvB,WAAK,QAAQ,EAAE,MAAM,MAAM,KAAK,aAAa,CAAC;AAAA,IAChD,GAAG,KAAK;AAAA,EACV;AAAA,EAEA,eAAe,OAAoB;AACjC,eAAW,CAAC,IAAI,OAAO,KAAK,KAAK,kBAAkB;AACjD,mBAAa,QAAQ,KAAK;AAC1B,cAAQ,OAAO,KAAK;AACpB,WAAK,iBAAiB,OAAO,EAAE;AAAA,IACjC;AAAA,EACF;AAAA,EAEA,WAAW,OAAe,MAAsB;AAC9C,SAAK,eAAe,IAAI,KAAK,GAAG,QAAQ,CAAC,YAAY,QAAQ,IAAI,CAAC;AAAA,EACpE;AAAA,EAEA,UAAU,OAA8B;AACtC,QAAI,KAAK,WAAW,OAAO;AACzB;AAAA,IACF;AAEA,SAAK,SAAS;AACd,SAAK,eAAe,QAAQ,CAAC,YAAY,QAAQ,KAAK,CAAC;AAAA,EACzD;AACF;;;AChSO,IAAM,SAAN,MAA8C;AAAA,EACnD,YAAY,oBAAI,IAAqB;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,YAAY,UAAyB,CAAC,GAAG;AACvC,UAAM,gBAAgB;AAGtB,UAAM,gBACJ,OAAO,eAAe,YAAY,cAAc,QAAQ,UAAU,SAC9D,cAAc,OAAO,SAAS,SAC9B;AAEN,SAAK,OAAO,QAAQ,OAAO;AAC3B,SAAK,QAAQ,QAAQ,QAAQ;AAC7B,SAAK,QAAQ,QAAQ;AACrB,SAAK,WAAW,IAAI,oBAAoB;AAAA,MACtC,UAAU,MAAM,KAAK,UAAU;AAAA,MAC/B,eAAe,QAAQ;AAAA,MACvB,gBAAgB,QAAQ;AAAA,MACxB,mBAAmB,QAAQ;AAAA,MAC3B,aAAa,MAAM;AACjB,aAAK,KAAK,qBAAqB;AAAA,MACjC;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,IAAI,QAAyB;AAC3B,WAAO,KAAK,SAAS;AAAA,EACvB;AAAA,EAEA,IAAI,YAAqB;AACvB,WAAO,KAAK,SAAS;AAAA,EACvB;AAAA,EAEA,IAAI,KAAyB;AAC3B,WAAO;AAAA,EACT;AAAA,EAEA,UAAyB;AACvB,WAAO,KAAK,SAAS,QAAQ;AAAA,EAC/B;AAAA,EAEA,aAAmB;AACjB,eAAW,WAAW,KAAK,UAAU,OAAO,GAAG;AAC7C,cAAQ,YAAY,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IACtC;AACA,SAAK,UAAU,MAAM;AAErB,SAAK,SAAS,WAAW;AAAA,EAC3B;AAAA,EAEA,QAAQ,MAAuB;AAC7B,QAAI,CAAC,KAAK,UAAU,IAAI,IAAI,GAAG;AAC7B,WAAK,UAAU,IAAI,MAAM,IAAI,QAAQ,MAAM,IAAI,CAAC;AAAA,IAClD;AAEA,WAAO,KAAK,UAAU,IAAI,IAAI;AAAA,EAChC;AAAA,EAEA,MAAM,MAAM,MAA6B;AACvC,UAAM,UAAU,KAAK,UAAU,IAAI,IAAI;AACvC,QAAI,SAAS;AACX,YAAM,QAAQ,YAAY;AAC1B,WAAK,UAAU,OAAO,IAAI;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,cAAc,SAAoD;AAChE,WAAO,KAAK,SAAS,cAAc,OAAO;AAAA,EAC5C;AAAA,EAEA,GAAgB,OAAe,SAAsC;AACnE,WAAO,KAAK,SAAS,GAAG,OAAO,OAAO;AAAA,EACxC;AAAA,EAEA,IAAI,OAAe,SAA6B;AAC9C,SAAK,SAAS,IAAI,OAAO,OAAO;AAAA,EAClC;AAAA,EAEA,KAAK,SAAwC;AAC3C,SAAK,SAAS,KAAK,OAAO;AAAA,EAC5B;AAAA,EAEA,YAAyB,SAAkC,UAAU,KAAkB;AACrF,WAAO,KAAK,SAAS,YAAY,SAAS,OAAO;AAAA,EACnD;AAAA,EAEA,YAAoB;AAClB,UAAM,gBAAgB;AAGtB,UAAM,MAAM,IAAI,IAAI,KAAK,QAAQ,cAAc,UAAU,UAAU,kBAAkB;AACrF,QAAI,WAAW,IAAI,aAAa,WAAW,SAAS;AACpD,QAAI,WAAW,KAAK;AAEpB,QAAI,KAAK,OAAO;AACd,UAAI,aAAa,IAAI,QAAQ,KAAK,UAAU,KAAK,KAAK,CAAC;AAAA,IACzD;AAEA,WAAO,IAAI,SAAS;AAAA,EACtB;AAAA,EAEA,MAAM,uBAAsC;AAC1C,eAAW,WAAW,KAAK,UAAU,OAAO,GAAG;AAC7C,UAAI,QAAQ,cAAc,CAAC,QAAQ,QAAQ;AACzC,cAAM,QAAQ,UAAU,EAAE,MAAM,QAAQ,KAAK;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AACF;","names":["resolve","reject"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/client/channel.ts","../../../src/client/socket_session.ts","../../../src/client/socket.ts"],"sourcesContent":["import type {\n SocketClientTransport,\n SubscribeOptions,\n SubscribeResult,\n UnsubscribeOptions,\n PresenceData,\n PresenceUser,\n EventHandler,\n ChannelMessage,\n ChannelAck,\n} from './types.js'\n\nconst CLIENT_EVENT_PREFIX = 'client:'\n\ninterface PendingSubscribe {\n promise: Promise<Channel>\n resolve: (channel: Channel) => void\n reject: (error: Error) => void\n token: number\n}\n\n/**\n * Represents a subscribed channel.\n * Chainable API inspired by Laravel Echo.\n *\n * @example\n * ```ts\n * socket.channel('chat/general')\n * .here((users) => setOnlineUsers(users))\n * .joining((user) => console.log(`${user.name} joined`))\n * .leaving((user) => console.log(`${user.name} left`))\n * .listen('chat:message', (msg) => addMessage(msg))\n * .subscribe()\n * ```\n */\nexport class Channel {\n /**\n * Channel name.\n */\n readonly name: string\n\n /**\n * Socket client transport.\n */\n #transport: SocketClientTransport\n\n /**\n * Whether the channel should stay subscribed.\n */\n #subscribed = false\n #active = false\n\n /**\n * Event handlers grouped by event type.\n */\n #eventHandlers = new Map<string, Set<EventHandler>>()\n\n /**\n * Current presence data.\n */\n #presence: PresenceData | null = null\n\n /**\n * Present user IDs used to detect join and leave events.\n */\n #presentUserIds = new Set<string>()\n\n /**\n * Handler registered through .here().\n */\n #hereHandler: EventHandler<PresenceUser[]> | null = null\n\n /**\n * Handler registered through .joining().\n */\n #joiningHandler: EventHandler<PresenceUser> | null = null\n\n /**\n * Handler registered through .leaving().\n */\n #leavingHandler: EventHandler<PresenceUser> | null = null\n\n /**\n * Internal transport handlers to clean up on unsubscribe.\n */\n #boundPresenceHandler: ((data: unknown) => void) | null = null\n #boundEventHandler: ((data: unknown) => void) | null = null\n #boundDisconnectHandler: (() => void) | null = null\n #listenersAttached = false\n #pendingSubscribe: PendingSubscribe | null = null\n #subscribeToken = 0\n\n constructor(name: string, transport: SocketClientTransport) {\n this.name = name\n this.#transport = transport\n }\n\n /**\n * Whether the channel should stay subscribed.\n */\n get subscribed(): boolean {\n return this.#subscribed\n }\n\n /**\n * Whether the channel is active on the server.\n */\n get active(): boolean {\n return this.#active\n }\n\n /**\n * List of present users.\n */\n get users(): PresenceUser[] {\n return this.#presence?.users ?? []\n }\n\n /**\n * Number of present users.\n */\n get count(): number {\n return this.#presence?.count ?? 0\n }\n\n /**\n * Registers a handler called with the present users.\n * Called immediately on subscribe and after each presence change.\n */\n here(handler: EventHandler<PresenceUser[]>): this {\n this.#hereHandler = handler\n return this\n }\n\n /**\n * Registers a handler called when a user joins.\n */\n joining(handler: EventHandler<PresenceUser>): this {\n this.#joiningHandler = handler\n return this\n }\n\n /**\n * Registers a handler called when a user leaves.\n */\n leaving(handler: EventHandler<PresenceUser>): this {\n this.#leavingHandler = handler\n return this\n }\n\n /**\n * Listens for an event on this channel.\n */\n listen<T = unknown>(event: string, handler: EventHandler<T>): this {\n if (!this.#eventHandlers.has(event)) {\n this.#eventHandlers.set(event, new Set())\n }\n this.#eventHandlers.get(event)!.add(handler as EventHandler)\n return this\n }\n\n /**\n * Removes an event listener.\n */\n stopListening(event: string, handler?: EventHandler): this {\n if (handler) {\n this.#eventHandlers.get(event)?.delete(handler)\n } else {\n this.#eventHandlers.delete(event)\n }\n return this\n }\n\n /**\n * Listens for a client event whispered by another channel member.\n */\n listenForWhisper<T = unknown>(event: string, handler: EventHandler<T>): this {\n return this.listen(`${CLIENT_EVENT_PREFIX}${event}`, handler)\n }\n\n /**\n * Removes a whispered client event listener.\n */\n stopListeningForWhisper(event: string, handler?: EventHandler): this {\n return this.stopListening(`${CLIENT_EVENT_PREFIX}${event}`, handler)\n }\n\n /**\n * Subscribes to the channel.\n *\n * client listeners -> subscribe ack -> active channel\n * | | |\n * v v v\n * events ready presence sync send/listen\n */\n async subscribe(options: SubscribeOptions = {}): Promise<this> {\n this.#subscribed = true\n\n if (this.#pendingSubscribe) {\n return this.#pendingSubscribe.promise as Promise<this>\n }\n\n if (this.#active) {\n return this\n }\n\n const timeout = options.timeout ?? 5000\n\n // Attach listeners before subscribing so the first server events are not missed.\n if (!this.#listenersAttached) {\n this.#setupSocketListeners()\n }\n\n const token = ++this.#subscribeToken\n\n let resolvePromise: (channel: Channel) => void\n let rejectPromise: (error: Error) => void\n\n const promise = new Promise<Channel>((resolve, reject) => {\n resolvePromise = resolve\n rejectPromise = reject\n })\n\n this.#pendingSubscribe = {\n promise,\n resolve: resolvePromise!,\n reject: rejectPromise!,\n token,\n }\n\n const { resolve, reject } = this.#pendingSubscribe\n\n const timer = setTimeout(() => {\n if (token !== this.#subscribeToken) return\n this.#pendingSubscribe = null\n this.#resetConnectionState()\n reject(new Error(`Subscribe timeout for channel: ${this.name}`))\n }, timeout)\n\n this.#transport\n .sendRequest<SubscribeResult>({ type: 'subscribe', channel: this.name }, timeout)\n .then((response) => {\n if (token !== this.#subscribeToken) return\n clearTimeout(timer)\n this.#pendingSubscribe = null\n\n if (response.ok) {\n this.#active = true\n\n const presenceData = response.data?.presenceData ?? response.presenceData\n\n // Apply the initial presence snapshot.\n if (presenceData) {\n this.#handleInitialPresence(presenceData)\n }\n\n resolve(this)\n } else {\n this.#resetConnectionState()\n reject(new Error(response.error ?? 'Subscribe failed'))\n }\n })\n .catch((error) => {\n if (token !== this.#subscribeToken) return\n clearTimeout(timer)\n this.#pendingSubscribe = null\n this.#resetConnectionState()\n reject(error instanceof Error ? error : new Error('Subscribe failed'))\n })\n\n return promise as Promise<this>\n }\n\n /**\n * Unsubscribes from the channel.\n */\n async unsubscribe(options: UnsubscribeOptions = {}): Promise<void> {\n if (!this.#subscribed && !this.#active) return\n this.#subscribed = false\n this.#subscribeToken++\n if (this.#pendingSubscribe) {\n this.#pendingSubscribe.reject(new Error(`Unsubscribed from channel: ${this.name}`))\n this.#pendingSubscribe = null\n }\n\n return new Promise((resolve) => {\n const timeout = options.timeout ?? 5000\n const timer = setTimeout(() => {\n this.#cleanupTerminal()\n resolve()\n }, timeout)\n\n this.#transport\n .sendRequest({ type: 'unsubscribe', channel: this.name }, timeout)\n .then(() => {\n clearTimeout(timer)\n this.#cleanupTerminal()\n resolve()\n })\n .catch(() => {\n clearTimeout(timer)\n this.#cleanupTerminal()\n resolve()\n })\n })\n }\n\n /**\n * Sends an event to the server.\n */\n send<T = unknown>(event: string, data?: T): this {\n if (!this.#active) {\n console.warn(`Cannot send to channel ${this.name}: not subscribed`)\n return this\n }\n const payload: ChannelMessage = {\n channel: this.name,\n event,\n data,\n }\n this.#transport.send({ type: 'message', ...payload })\n return this\n }\n\n /**\n * Relays a client event to other members of this channel.\n */\n whisper<T = unknown>(event: string, data?: T): this {\n if (!this.#active) {\n console.warn(`Cannot whisper to channel ${this.name}: not subscribed`)\n return this\n }\n const payload: ChannelMessage = {\n channel: this.name,\n event,\n data,\n }\n this.#transport.send({ type: 'whisper', ...payload })\n return this\n }\n\n /**\n * Sends an event and waits for a response.\n */\n async sendWithAck<T = unknown, R = unknown>(event: string, data?: T): Promise<R> {\n if (!this.#active) {\n throw new Error(`Cannot send to channel ${this.name}: not subscribed`)\n }\n const payload: ChannelMessage = {\n channel: this.name,\n event,\n data,\n }\n const response = await this.#transport.sendRequest<ChannelAck<R>>({\n type: 'message',\n ...payload,\n })\n if (!response?.ok) {\n throw new Error(response?.error ?? `Channel handler failed: ${event}`)\n }\n return response.data as R\n }\n\n /**\n * Configures transport listeners.\n */\n #setupSocketListeners(): void {\n // Handler for presence:update.\n this.#boundPresenceHandler = (data: unknown) => {\n if (!data || typeof data !== 'object') return\n this.#handlePresenceUpdate(data as PresenceData)\n }\n this.#transport.on(`channel:${this.name}:presence:update`, this.#boundPresenceHandler)\n\n this.#boundEventHandler = (data: unknown) => {\n if (!data || typeof data !== 'object') return\n\n const payload = data as { event?: string; data?: unknown }\n if (!payload.event) return\n\n const handlers = this.#eventHandlers.get(payload.event)\n handlers?.forEach((handler) => handler(payload.data))\n }\n this.#transport.on(`channel:${this.name}:event`, this.#boundEventHandler)\n\n // Mark the channel inactive when the socket disconnects.\n this.#boundDisconnectHandler = () => {\n this.#resetConnectionState()\n }\n this.#transport.on('disconnect', this.#boundDisconnectHandler)\n\n this.#listenersAttached = true\n }\n\n /**\n * Handles the initial presence snapshot.\n */\n #handleInitialPresence(data: PresenceData): void {\n this.#presence = data\n this.#presentUserIds = new Set(data.users.map((u) => u.id))\n\n // Call here() with all users.\n this.#hereHandler?.(data.users)\n }\n\n /**\n * Handles a presence update.\n *\n * previous IDs current IDs\n * | |\n * +-- diff in -----+--> joining / leaving callbacks\n */\n #handlePresenceUpdate(data: PresenceData): void {\n const previousIds = this.#presentUserIds\n const currentIds = new Set(data.users.map((u) => u.id))\n\n // Detect new users.\n for (const user of data.users) {\n if (!previousIds.has(user.id)) {\n this.#joiningHandler?.(user)\n }\n }\n\n // Detect users that left.\n if (this.#presence) {\n for (const user of this.#presence.users) {\n if (!currentIds.has(user.id)) {\n this.#leavingHandler?.(user)\n }\n }\n }\n\n // Update local state.\n this.#presence = data\n this.#presentUserIds = currentIds\n\n // Call here() with the updated list.\n this.#hereHandler?.(data.users)\n }\n\n #resetConnectionState(): void {\n this.#active = false\n this.#presence = null\n this.#presentUserIds.clear()\n }\n\n #detachTransportListeners(): void {\n if (this.#boundPresenceHandler) {\n this.#transport.off(`channel:${this.name}:presence:update`, this.#boundPresenceHandler)\n this.#boundPresenceHandler = null\n }\n\n if (this.#boundEventHandler) {\n this.#transport.off(`channel:${this.name}:event`, this.#boundEventHandler)\n this.#boundEventHandler = null\n }\n\n if (this.#boundDisconnectHandler) {\n this.#transport.off('disconnect', this.#boundDisconnectHandler)\n this.#boundDisconnectHandler = null\n }\n\n this.#listenersAttached = false\n }\n\n #clearUserCallbacks(): void {\n this.#eventHandlers.clear()\n this.#hereHandler = null\n this.#joiningHandler = null\n this.#leavingHandler = null\n }\n\n /**\n * Performs terminal cleanup after the user leaves or the socket is disposed.\n */\n #cleanupTerminal(): void {\n this.#subscribed = false\n this.#resetConnectionState()\n this.#detachTransportListeners()\n this.#clearUserCallbacks()\n }\n}\n","import type { ConnectionState, EventHandler } from './types.js'\nimport type { ServerProtocolMessage } from '../shared_types.js'\n\ninterface PendingRequest {\n resolve: (value: unknown) => void\n reject: (error: Error) => void\n timer: ReturnType<typeof setTimeout>\n}\n\ninterface ClientSocketSessionOptions {\n buildUrl: () => string\n createWebSocket?: (url: string) => WebSocket\n autoReconnect?: boolean\n reconnectDelay?: number\n reconnectMaxDelay?: number\n onConnected?: () => void\n}\n\nexport class ClientSocketSession {\n #ws: WebSocket | null = null\n #state: ConnectionState = 'disconnected'\n #connectPromise: Promise<void> | null = null\n #cancelConnect: ((error: Error) => void) | null = null\n #stateHandlers = new Set<EventHandler<ConnectionState>>()\n #eventHandlers = new Map<string, Set<EventHandler>>()\n #pendingRequests = new Map<string, PendingRequest>()\n #requestId = 0\n #reconnectAttempts = 0\n #reconnectTimer: ReturnType<typeof setTimeout> | null = null\n #manualDisconnect = false\n #buildUrl: () => string\n #createWebSocket: (url: string) => WebSocket\n #autoReconnect: boolean\n #reconnectDelay: number\n #reconnectMaxDelay: number\n #onConnected?: () => void\n\n constructor(options: ClientSocketSessionOptions) {\n this.#buildUrl = options.buildUrl\n this.#createWebSocket = options.createWebSocket ?? ((url) => new WebSocket(url))\n this.#autoReconnect = options.autoReconnect ?? true\n this.#reconnectDelay = Math.max(0, options.reconnectDelay ?? 250)\n this.#reconnectMaxDelay = Math.max(this.#reconnectDelay, options.reconnectMaxDelay ?? 5000)\n this.#onConnected = options.onConnected\n }\n\n get state(): ConnectionState {\n return this.#state\n }\n\n get connected(): boolean {\n return this.#state === 'connected'\n }\n\n connect(): Promise<void> {\n if (this.#state === 'connected') {\n return Promise.resolve()\n }\n\n if (this.#connectPromise) {\n return this.#connectPromise\n }\n\n this.#manualDisconnect = false\n this.#setState('connecting')\n\n const connectPromise = new Promise<void>((resolve, reject) => {\n const ws = this.#createWebSocket(this.#buildUrl())\n this.#ws = ws\n let settled = false\n\n const cleanup = () => {\n ws.removeEventListener('open', handleOpen)\n ws.removeEventListener('error', handleError)\n if (this.#connectPromise === connectPromise) {\n this.#connectPromise = null\n }\n if (this.#cancelConnect === cancelConnect) {\n this.#cancelConnect = null\n }\n }\n\n const cancelConnect = (error: Error) => {\n if (settled) {\n return\n }\n\n settled = true\n cleanup()\n reject(error)\n }\n\n const handleOpen = () => {\n settled = true\n cleanup()\n this.#reconnectAttempts = 0\n this.#setState('connected')\n this.#emitLocal('connect')\n this.#onConnected?.()\n resolve()\n }\n\n const handleError = () => {\n cancelConnect(new Error('WebSocket connection failed'))\n if (this.#ws === ws) {\n this.#setState('disconnected')\n }\n }\n\n const handleClose = () => {\n cancelConnect(new Error('WebSocket connection failed'))\n\n if (this.#ws === ws) {\n this.#handleClose()\n }\n }\n\n this.#cancelConnect = cancelConnect\n ws.addEventListener('open', handleOpen)\n ws.addEventListener('error', handleError)\n ws.addEventListener('message', (event) => this.#handleIncoming(event.data))\n ws.addEventListener('close', handleClose)\n })\n\n this.#connectPromise = connectPromise\n return connectPromise\n }\n\n disconnect(): void {\n this.#manualDisconnect = true\n if (this.#reconnectTimer) {\n clearTimeout(this.#reconnectTimer)\n this.#reconnectTimer = null\n }\n\n this.#cancelConnect?.(new Error('Socket disconnected'))\n this.#reconnectAttempts = 0\n this.#ws?.close()\n this.#rejectPending(new Error('Socket disconnected'))\n }\n\n onStateChange(handler: EventHandler<ConnectionState>): () => void {\n this.#stateHandlers.add(handler)\n\n return () => {\n this.#stateHandlers.delete(handler)\n }\n }\n\n on<T = unknown>(event: string, handler: EventHandler<T>): () => void {\n if (!this.#eventHandlers.has(event)) {\n this.#eventHandlers.set(event, new Set())\n }\n\n this.#eventHandlers.get(event)!.add(handler as EventHandler)\n\n return () => {\n this.off(event, handler as EventHandler)\n }\n }\n\n off(event: string, handler: EventHandler): void {\n this.#eventHandlers.get(event)?.delete(handler)\n }\n\n send(message: Record<string, unknown>): void {\n if (this.#ws?.readyState !== WebSocket.OPEN) {\n return\n }\n\n this.#ws.send(JSON.stringify(message))\n }\n\n sendRequest<T = unknown>(message: Record<string, unknown>, timeout = 5000): Promise<T> {\n if (this.#ws?.readyState !== WebSocket.OPEN) {\n return Promise.reject(new Error('Socket is not connected'))\n }\n\n const id = String(++this.#requestId)\n const payload = { id, ...message }\n\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n this.#pendingRequests.delete(id)\n reject(new Error('Socket request timed out'))\n }, timeout)\n\n this.#pendingRequests.set(id, { resolve: resolve as (value: unknown) => void, reject, timer })\n this.#ws!.send(JSON.stringify(payload))\n })\n }\n\n #handleIncoming(raw: unknown): void {\n const message = this.#parseMessage(raw)\n if (!message) {\n return\n }\n\n if (message.type === 'ack') {\n this.#resolveAck(message)\n return\n }\n\n if (message.type === 'event') {\n if (message.channel) {\n if (message.event === 'presence:update') {\n this.#emitLocal(`channel:${message.channel}:presence:update`, message.data)\n return\n }\n\n this.#emitLocal(`channel:${message.channel}:event`, {\n event: message.event,\n data: message.data,\n })\n return\n }\n\n this.#emitLocal(message.event, message.data)\n }\n }\n\n #parseMessage(raw: unknown): ServerProtocolMessage | null {\n try {\n const data = typeof raw === 'string' ? raw : String(raw)\n const message = JSON.parse(data)\n return message && typeof message === 'object' ? (message as ServerProtocolMessage) : null\n } catch {\n return null\n }\n }\n\n #resolveAck(message: Extract<ServerProtocolMessage, { type: 'ack' }>): void {\n if (!message.id) {\n return\n }\n\n const pending = this.#pendingRequests.get(message.id)\n if (!pending) {\n return\n }\n\n clearTimeout(pending.timer)\n this.#pendingRequests.delete(message.id)\n\n if (message.ok) {\n pending.resolve({ ok: true, data: message.data })\n } else {\n pending.resolve({ ok: false, error: message.error })\n }\n }\n\n #handleClose(): void {\n const shouldEmitDisconnect = this.#state !== 'disconnected'\n\n this.#setState('disconnected')\n if (shouldEmitDisconnect) {\n this.#emitLocal('disconnect')\n }\n this.#rejectPending(new Error('Socket disconnected'))\n\n this.#scheduleReconnect()\n }\n\n #scheduleReconnect(): void {\n if (this.#manualDisconnect || !this.#autoReconnect || this.#reconnectTimer) {\n return\n }\n\n const delay = Math.min(\n this.#reconnectDelay * 2 ** this.#reconnectAttempts,\n this.#reconnectMaxDelay\n )\n this.#reconnectAttempts += 1\n\n this.#reconnectTimer = setTimeout(() => {\n this.#reconnectTimer = null\n this.connect().catch(() => this.#handleClose())\n }, delay)\n }\n\n #rejectPending(error: Error): void {\n for (const [id, pending] of this.#pendingRequests) {\n clearTimeout(pending.timer)\n pending.reject(error)\n this.#pendingRequests.delete(id)\n }\n }\n\n #emitLocal(event: string, data?: unknown): void {\n this.#eventHandlers.get(event)?.forEach((handler) => handler(data))\n }\n\n #setState(state: ConnectionState): void {\n if (this.#state === state) {\n return\n }\n\n this.#state = state\n this.#stateHandlers.forEach((handler) => handler(state))\n }\n}\n","import { Channel } from './channel.js'\nimport { ClientSocketSession } from './socket_session.js'\nimport type {\n SocketOptions,\n ConnectionState,\n EventHandler,\n SocketClientTransport,\n} from './types.js'\n\n/**\n * Main Socket client.\n */\nexport class Socket implements SocketClientTransport {\n #channels = new Map<string, Channel>()\n #url: string\n #path: string\n #auth?: Record<string, unknown>\n #session: ClientSocketSession\n\n constructor(options: SocketOptions = {}) {\n const browserWindow = globalThis as typeof globalThis & {\n window?: { location?: { origin?: string } }\n }\n const browserOrigin =\n typeof globalThis === 'object' && browserWindow.window?.location?.origin\n ? browserWindow.window.location.origin\n : ''\n\n this.#url = options.url ?? browserOrigin\n this.#path = options.path ?? '/socket'\n this.#auth = options.auth\n this.#session = new ClientSocketSession({\n buildUrl: () => this.#buildUrl(),\n autoReconnect: options.autoReconnect,\n reconnectDelay: options.reconnectDelay,\n reconnectMaxDelay: options.reconnectMaxDelay,\n onConnected: () => {\n void this.#resubscribeChannels()\n },\n })\n }\n\n get state(): ConnectionState {\n return this.#session.state\n }\n\n get connected(): boolean {\n return this.#session.connected\n }\n\n get id(): string | undefined {\n return undefined\n }\n\n connect(): Promise<void> {\n return this.#session.connect()\n }\n\n disconnect(): void {\n for (const channel of this.#channels.values()) {\n channel.unsubscribe().catch(() => {})\n }\n this.#channels.clear()\n\n this.#session.disconnect()\n }\n\n channel(name: string): Channel {\n if (!this.#channels.has(name)) {\n this.#channels.set(name, new Channel(name, this))\n }\n\n return this.#channels.get(name)!\n }\n\n async leave(name: string): Promise<void> {\n const channel = this.#channels.get(name)\n if (channel) {\n await channel.unsubscribe()\n this.#channels.delete(name)\n }\n }\n\n onStateChange(handler: EventHandler<ConnectionState>): () => void {\n return this.#session.onStateChange(handler)\n }\n\n on<T = unknown>(event: string, handler: EventHandler<T>): () => void {\n return this.#session.on(event, handler)\n }\n\n off(event: string, handler: EventHandler): void {\n this.#session.off(event, handler)\n }\n\n send(message: Record<string, unknown>): void {\n this.#session.send(message)\n }\n\n sendRequest<T = unknown>(message: Record<string, unknown>, timeout = 5000): Promise<T> {\n return this.#session.sendRequest(message, timeout)\n }\n\n #buildUrl(): string {\n const browserGlobal = globalThis as typeof globalThis & {\n location?: { origin?: string }\n }\n const url = new URL(this.#url || browserGlobal.location?.origin || 'http://localhost')\n url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:'\n url.pathname = this.#path\n\n if (this.#auth) {\n url.searchParams.set('auth', JSON.stringify(this.#auth))\n }\n\n return url.toString()\n }\n\n async #resubscribeChannels(): Promise<void> {\n for (const channel of this.#channels.values()) {\n if (channel.subscribed && !channel.active) {\n await channel.subscribe().catch(console.error)\n }\n }\n }\n}\n"],"mappings":"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","names":["resolve","reject"]}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/presence_manager.ts","../src/base_channel.ts","../src/fake_socket.ts","../src/socket_service.ts","../src/channel_subscription_storage.ts","../src/channel_subscriptions.ts","../src/protocol/reply.ts","../src/protocol/message.ts","../src/message_queue.ts","../src/broadcasting.ts","../src/socket_bus.ts","../src/socket_upgrader.ts","../src/channel_pattern.ts","../src/channel_router.ts"],"sourcesContent":["import type { PresenceMember } from './types.js'\n\nexport const PRESENCE_DATA_KEY = '__rlanzSocketPresence'\n\ntype PresenceSocketData = Record<string, unknown> & {\n [PRESENCE_DATA_KEY]?: Record<string, PresenceUser>\n}\n\nexport interface PresenceUser {\n id: string\n name: string\n joinedAt: Date\n [key: string]: unknown\n}\n\nexport interface PresenceData {\n channel: string\n users: PresenceUser[]\n count: number\n}\n\nexport interface PresenceSocket {\n id: string\n data: PresenceSocketData\n}\n\n/**\n * Tracks user presence in channels.\n *\n * channel -> socketId -> PresenceUser\n */\nexport class PresenceManager {\n #presence = new Map<string, Map<string, PresenceUser>>()\n #fetchSockets: ((channel: string) => Promise<PresenceSocket[]>) | null = null\n\n /**\n * Sets the socket fetcher used to build distributed snapshots.\n */\n setSocketFetcher(fetchSockets: (channel: string) => Promise<PresenceSocket[]>): void {\n this.#fetchSockets = fetchSockets\n }\n\n /**\n * Adds a user to a channel.\n */\n join(channel: string, socket: PresenceSocket, member: string | PresenceMember): void {\n if (!this.#presence.has(channel)) {\n this.#presence.set(channel, new Map())\n }\n\n const presenceUser =\n typeof member === 'string'\n ? { id: socket.id, name: member }\n : {\n ...member,\n id: member.id || socket.id,\n }\n\n const user = {\n ...presenceUser,\n joinedAt: new Date(),\n }\n\n this.#presence.get(channel)!.set(socket.id, user)\n this.#setSocketPresence(socket, channel, user)\n }\n\n /**\n * Removes a user from a channel.\n */\n leave(channel: string, socket: PresenceSocket): void {\n const channelPresence = this.#presence.get(channel)\n\n if (channelPresence) {\n channelPresence.delete(socket.id)\n\n if (channelPresence.size === 0) {\n this.#presence.delete(channel)\n }\n }\n\n this.#deleteSocketPresence(socket, channel)\n }\n\n /**\n * Removes a user from every channel.\n * Returns the list of channels the user left.\n */\n leaveAll(socket: PresenceSocket): string[] {\n const leftChannels: string[] = []\n\n for (const [channel, users] of this.#presence) {\n if (users.has(socket.id)) {\n users.delete(socket.id)\n leftChannels.push(channel)\n\n if (users.size === 0) {\n this.#presence.delete(channel)\n }\n\n this.#deleteSocketPresence(socket, channel)\n }\n }\n\n return leftChannels\n }\n\n /**\n * Gets presence data for a channel.\n */\n async snapshot(channel: string): Promise<PresenceData> {\n if (this.#fetchSockets) {\n return this.#getFromSockets(channel)\n }\n\n return this.#localSnapshot(channel)\n }\n\n /**\n * Gets the number of users in a channel.\n */\n async count(channel: string): Promise<number> {\n return (await this.snapshot(channel)).count\n }\n\n /**\n * Checks whether a socket is locally present in a channel.\n */\n hasLocal(channel: string, socketId: string): boolean {\n return this.#presence.get(channel)?.has(socketId) || false\n }\n\n /**\n * Lists every local channel where a socket is present.\n */\n getLocalChannelsForSocket(socketId: string): string[] {\n const channels: string[] = []\n\n for (const [channel, users] of this.#presence) {\n if (users.has(socketId)) {\n channels.push(channel)\n }\n }\n\n return channels\n }\n\n /**\n * Lists sockets that are locally present in a channel.\n */\n getLocalSockets(channel: string): PresenceSocket[] {\n const users = this.#presence.get(channel)\n\n if (!users) {\n return []\n }\n\n return Array.from(users.entries()).map(([socketId, user]) => ({\n id: socketId,\n data: {\n [PRESENCE_DATA_KEY]: {\n [channel]: user,\n },\n },\n }))\n }\n\n #localSnapshot(channel: string): PresenceData {\n const users = this.#presence.get(channel)\n\n return {\n channel,\n users: users ? Array.from(users.values()) : [],\n count: users?.size || 0,\n }\n }\n\n async #getFromSockets(channel: string): Promise<PresenceData> {\n const sockets = await this.#fetchSockets!(channel)\n const users = sockets\n .map((socket) => socket.data?.[PRESENCE_DATA_KEY]?.[channel])\n .filter((user): user is PresenceUser => Boolean(user))\n\n return {\n channel,\n users,\n count: users.length,\n }\n }\n\n #setSocketPresence(socket: PresenceSocket, channel: string, user: PresenceUser): void {\n socket.data[PRESENCE_DATA_KEY] ??= {}\n socket.data[PRESENCE_DATA_KEY][channel] = user\n }\n\n #deleteSocketPresence(socket: PresenceSocket, channel: string): void {\n if (!socket.data[PRESENCE_DATA_KEY]) {\n return\n }\n\n delete socket.data[PRESENCE_DATA_KEY][channel]\n\n if (Object.keys(socket.data[PRESENCE_DATA_KEY]).length === 0) {\n delete socket.data[PRESENCE_DATA_KEY]\n }\n }\n}\n","import type {\n AuthenticatedSocket,\n ChannelOptions,\n PresenceMember,\n Middleware,\n MiddlewareContext,\n ChannelEventMap,\n ChannelHandlerMap,\n} from './types.js'\nimport type { PresenceManager } from './presence_manager.js'\nimport { getDecoratedChannelHandlers } from './decorators.js'\n\ninterface ChannelBroadcaster {\n to(channel: string): {\n emit(event: string, data: unknown): void\n except(socketId: string): {\n emit(event: string, data: unknown): void\n }\n }\n}\n\nexport class MissingPresenceInfoError extends Error {\n constructor() {\n super('Presence channels must implement getPresenceInfo(socket)')\n }\n}\n\n/**\n * Abstract class used to define a channel.\n *\n * @example\n * ```ts\n * export default class ChatChannel extends BaseChannel {\n * static pattern = 'chat/:roomId'\n * static middlewares = [\n * async (ctx, next) => {\n * // Authorize access to the channel through middleware.\n * // if (!Room.isMember(ctx.params.roomId, ctx.socket.user.id)) throw new Error('Forbidden')\n * await next()\n * },\n * ]\n * }\n * ```\n *\n * @example With presence and decorated handlers\n * ```ts\n * import { onMessage } from '@rlanz/socket/decorators'\n *\n * export default class ChatChannel extends BaseChannel {\n * static pattern = 'chat/:roomId'\n * static options = { presence: true }\n *\n * static middlewares = [\n * async (ctx, next) => {\n * if (!ctx.socket.user.isVerified) throw new Error('Not verified')\n * await next()\n * }\n * ]\n *\n * @onMessage('chat:send')\n * async handleSend(socket: AuthenticatedSocket<User>, data: unknown) {\n * // Handle chat:send event\n * }\n *\n * @onMessage('chat:typing')\n * async handleTyping(socket: AuthenticatedSocket<User>, data: unknown) {\n * // Handle chat:typing event\n * }\n * }\n * ```\n */\nexport class BaseChannel<\n User = unknown,\n Events extends ChannelEventMap = ChannelEventMap,\n HttpContext = unknown,\n> {\n /**\n * URL-like channel pattern, for example: 'chat/:roomId'.\n */\n static pattern: string\n\n /**\n * Channel options.\n */\n static options?: ChannelOptions\n\n /**\n * Middlewares to run before subscription.\n */\n static middlewares?: Middleware<any, any>[]\n\n /**\n * Event handler map.\n * Ex: { 'chat:send': 'handleSend' }\n */\n protected handlers: ChannelHandlerMap<User, Events, HttpContext> = {}\n\n /**\n * Injected socket service.\n */\n protected socket!: ChannelBroadcaster\n\n /**\n * Full channel name, for example: 'chat/general'.\n */\n protected channelName!: string\n\n /**\n * Parameters extracted from the pattern, for example: { roomId: 'general' }.\n */\n protected params!: Record<string, string>\n\n /**\n * Injected presence manager when presence is enabled.\n */\n protected presence!: PresenceManager\n\n /**\n * Injects dependencies.\n */\n $setContext(\n socket: ChannelBroadcaster,\n channelName: string,\n params: Record<string, string>,\n presence?: PresenceManager\n ): void {\n this.socket = socket\n this.channelName = channelName\n this.params = params\n if (presence) {\n this.presence = presence\n }\n }\n\n /**\n * Emits to every channel member.\n */\n protected broadcast(event: string, data: unknown): void {\n this.socket.to(this.channelName).emit(event, data)\n }\n\n /**\n * Emits to every channel member except one socket.\n */\n protected broadcastExcept(socketId: string, event: string, data: unknown): void {\n this.socket.to(this.channelName).except(socketId).emit(event, data)\n }\n\n /**\n * Runs the middleware chain.\n *\n * middleware[0] -> middleware[1] -> ... -> subscribe\n */\n async $runMiddlewares(ctx: MiddlewareContext<User, HttpContext>): Promise<void> {\n const middlewares = (this.constructor as typeof BaseChannel).middlewares || []\n\n let index = 0\n\n const next = async (): Promise<void> => {\n if (index >= middlewares.length) return\n\n const middleware = middlewares[index++]\n\n if (typeof middleware === 'function') {\n await middleware(ctx, next)\n } else {\n await middleware.handle(ctx, next)\n }\n }\n\n await next()\n }\n\n /**\n * Returns member information for presence.\n * Implement when options.presence = true.\n */\n getPresenceInfo?(socket: AuthenticatedSocket<User, HttpContext>): PresenceMember\n\n /**\n * Hook called when a socket joins the channel.\n */\n onJoin?(socket: AuthenticatedSocket<User, HttpContext>, ...params: string[]): Promise<void>\n\n /**\n * Hook called when a socket leaves the channel.\n */\n onLeave?(socket: AuthenticatedSocket<User, HttpContext>, ...params: string[]): Promise<void>\n\n /**\n * Hook called when a message is received on the channel.\n * Used as fallback when no event handler matches.\n */\n onMessage?(\n socket: AuthenticatedSocket<User, HttpContext>,\n event: string,\n data: unknown\n ): Promise<void>\n\n /**\n * Hook called when a member joins a presence channel.\n */\n onMemberJoin?(\n socket: AuthenticatedSocket<User, HttpContext>,\n member: PresenceMember,\n ...params: string[]\n ): Promise<void>\n\n /**\n * Hook called when a member leaves a presence channel.\n */\n onMemberLeave?(\n socket: AuthenticatedSocket<User, HttpContext>,\n member: PresenceMember,\n ...params: string[]\n ): Promise<void>\n\n /**\n * Dispatches a message to the matching handler.\n * Called by SocketService. Do not override.\n */\n async $handleMessage(\n socket: AuthenticatedSocket<User, HttpContext>,\n event: string,\n data: unknown\n ): Promise<unknown> {\n const handlers = {\n ...(this.handlers as Record<string, unknown>),\n ...(getDecoratedChannelHandlers<User, Events, HttpContext>(this) as Record<string, unknown>),\n }\n const handler = handlers[event]\n\n if (typeof handler === 'function') {\n return (\n handler as (socket: AuthenticatedSocket<User, HttpContext>, data: unknown) => unknown\n ).call(this, socket, data)\n }\n\n // Fall back to onMessage when it is defined.\n return this.onMessage?.(socket, event, data)\n }\n\n /**\n * Runs the generic join hook. ChannelSubscriptions owns subscription lifecycle.\n */\n async $handleJoin(\n socket: AuthenticatedSocket<User, HttpContext>,\n ...params: string[]\n ): Promise<void> {\n await this.onJoin?.(socket, ...params)\n }\n\n /**\n * Runs the generic leave hook. ChannelSubscriptions owns subscription lifecycle.\n */\n async $handleLeave(\n socket: AuthenticatedSocket<User, HttpContext>,\n ...params: string[]\n ): Promise<void> {\n await this.onLeave?.(socket, ...params)\n }\n\n $getPresenceInfo(socket: AuthenticatedSocket<User, HttpContext>): PresenceMember {\n if (!this.getPresenceInfo) {\n throw new MissingPresenceInfoError()\n }\n\n return this.getPresenceInfo(socket)\n }\n}\n","import { AssertionError } from 'node:assert'\nimport { isDeepStrictEqual } from 'node:util'\nimport type { SocketEmission, SocketEmissionSink, SocketEmissionTarget } from './broadcasting.js'\n\nexport type SocketFakeTarget = SocketEmissionTarget\nexport type SocketFakeEmission = SocketEmission\n\nexport interface SocketFakeEmissionQuery {\n data?: unknown | ((data: unknown) => boolean)\n except?: string[] | ((except: string[] | undefined) => boolean)\n}\n\nexport interface SocketFakeCountQuery extends SocketFakeEmissionQuery {\n target?: SocketFakeTarget\n event?: string\n channel?: string\n userId?: string | number\n}\n\nexport class SocketFake implements SocketEmissionSink {\n #emissions: SocketFakeEmission[] = []\n #restore: () => void\n\n constructor(restore: () => void) {\n this.#restore = restore\n }\n\n get emissions(): SocketFakeEmission[] {\n return [...this.#emissions]\n }\n\n record(emission: SocketFakeEmission): void {\n this.#emissions.push({\n ...emission,\n ...('except' in emission && emission.except ? { except: [...emission.except] } : {}),\n })\n }\n\n dispatch(emission: SocketFakeEmission): void {\n this.record(emission)\n }\n\n clear(): void {\n this.#emissions = []\n }\n\n restore(): void {\n this.#restore()\n }\n\n [Symbol.dispose](): void {\n this.restore()\n }\n\n assertBroadcasted(event: string, query?: SocketFakeEmissionQuery): void {\n this.#assertEmitted(\n (emission) => emission.target === 'global' && emission.event === event,\n query,\n `Expected socket event \"${event}\" to have been broadcasted`\n )\n }\n\n assertNotBroadcasted(event: string, query?: SocketFakeEmissionQuery): void {\n this.#assertNotEmitted(\n (emission) => emission.target === 'global' && emission.event === event,\n query,\n `Expected socket event \"${event}\" not to have been broadcasted`\n )\n }\n\n assertEmittedTo(channel: string, event: string, query?: SocketFakeEmissionQuery): void {\n this.#assertEmitted(\n (emission) =>\n emission.target === 'channel' && emission.channel === channel && emission.event === event,\n query,\n `Expected socket event \"${event}\" to have been emitted to channel \"${channel}\"`\n )\n }\n\n assertNotEmittedTo(channel: string, event: string, query?: SocketFakeEmissionQuery): void {\n this.#assertNotEmitted(\n (emission) =>\n emission.target === 'channel' && emission.channel === channel && emission.event === event,\n query,\n `Expected socket event \"${event}\" not to have been emitted to channel \"${channel}\"`\n )\n }\n\n assertEmittedToUser(\n userId: string | number,\n event: string,\n query?: SocketFakeEmissionQuery\n ): void {\n this.#assertEmitted(\n (emission) =>\n emission.target === 'user' && emission.userId === userId && emission.event === event,\n query,\n `Expected socket event \"${event}\" to have been emitted to user \"${String(userId)}\"`\n )\n }\n\n assertNotEmittedToUser(\n userId: string | number,\n event: string,\n query?: SocketFakeEmissionQuery\n ): void {\n this.#assertNotEmitted(\n (emission) =>\n emission.target === 'user' && emission.userId === userId && emission.event === event,\n query,\n `Expected socket event \"${event}\" not to have been emitted to user \"${String(userId)}\"`\n )\n }\n\n assertEmittedCount(count: number, query?: SocketFakeCountQuery): void {\n const actual = this.#matching(query).length\n\n if (actual !== count) {\n throw new AssertionError({\n message: `Expected ${count} socket event(s) to have been emitted, but found ${actual}`,\n actual,\n expected: count,\n operator: 'strictEqual',\n })\n }\n }\n\n assertNothingEmitted(): void {\n this.assertEmittedCount(0)\n }\n\n #assertEmitted(\n predicate: (emission: SocketFakeEmission) => boolean,\n query: SocketFakeEmissionQuery | undefined,\n message: string\n ): void {\n const matching = this.#emissions.filter((emission) => {\n return predicate(emission) && this.#matchesQuery(emission, query)\n })\n\n if (matching.length === 0) {\n throw new AssertionError({\n message,\n actual: this.#emissions,\n expected: query,\n operator: 'includes',\n })\n }\n }\n\n #assertNotEmitted(\n predicate: (emission: SocketFakeEmission) => boolean,\n query: SocketFakeEmissionQuery | undefined,\n message: string\n ): void {\n const matching = this.#emissions.filter((emission) => {\n return predicate(emission) && this.#matchesQuery(emission, query)\n })\n\n if (matching.length > 0) {\n throw new AssertionError({\n message,\n actual: matching,\n expected: [],\n operator: 'doesNotInclude',\n })\n }\n }\n\n #matching(query: SocketFakeCountQuery | undefined): SocketFakeEmission[] {\n return this.#emissions.filter((emission) => {\n if (query?.target && emission.target !== query.target) {\n return false\n }\n\n if (query?.event && emission.event !== query.event) {\n return false\n }\n\n if (query?.channel && (!('channel' in emission) || emission.channel !== query.channel)) {\n return false\n }\n\n if (\n query?.userId !== undefined &&\n (!('userId' in emission) || emission.userId !== query.userId)\n ) {\n return false\n }\n\n return this.#matchesQuery(emission, query)\n })\n }\n\n #matchesQuery(emission: SocketFakeEmission, query: SocketFakeEmissionQuery | undefined): boolean {\n if (!query) {\n return true\n }\n\n if ('data' in query) {\n if (typeof query.data === 'function') {\n if (!query.data(emission.data)) {\n return false\n }\n } else if (!isDeepStrictEqual(emission.data, query.data)) {\n return false\n }\n }\n\n if ('except' in query) {\n if (typeof query.except === 'function') {\n if (!query.except('except' in emission ? emission.except : undefined)) {\n return false\n }\n } else if (\n !isDeepStrictEqual(('except' in emission ? emission.except : undefined) ?? [], query.except)\n ) {\n return false\n }\n }\n\n return true\n }\n}\n","import { randomUUID } from 'node:crypto'\nimport type { IncomingMessage } from 'node:http'\nimport type { Server as HttpServer } from 'node:http'\nimport type { Logger } from '@adonisjs/logger'\nimport Emittery from 'emittery'\nimport { WebSocket, WebSocketServer } from 'ws'\nimport type {\n AuthenticatedSocket,\n SocketConfig,\n SocketEvents,\n ChannelAck,\n RawSocket,\n SocketHealthSnapshot,\n SocketServiceStatus,\n} from './types.js'\nimport type { ChannelMessage, ServerProtocolMessage } from './shared_types.js'\nimport type { ChannelRouter } from './channel_router.js'\nimport { PRESENCE_DATA_KEY, type PresenceManager, type PresenceSocket } from './presence_manager.js'\nimport { ChannelSubscriptions } from './channel_subscriptions.js'\nimport { Message, type TransportMessage } from './protocol/message.js'\nimport { MessageQueue } from './message_queue.js'\nimport { Reply } from './protocol/reply.js'\nimport { userRoom, type SocketEmission, type SocketEmissionSink } from './broadcasting.js'\nimport { SocketFake } from './fake_socket.js'\nimport { SocketBus, type SerializablePresenceSocket } from './socket_bus.js'\nimport {\n SocketUpgrader,\n type AcceptedUpgrade,\n type SocketUpgradeHandler,\n} from './socket_upgrader.js'\nimport {\n broadcastChannel,\n channelMessageChannel,\n connectChannel,\n disconnectChannel,\n subscribeChannel,\n unsubscribeChannel,\n} from './tracing_channels.js'\nimport type {\n SocketBroadcastMessage,\n SocketChannelMessage,\n SocketSubscribeMessage,\n SocketUnsubscribeMessage,\n} from './types/tracing_channels.js'\n\nconst DEFAULT_PING_INTERVAL = 25_000\nconst DEFAULT_PING_TIMEOUT = 5_000\n\nfunction sendFrame(connection: WebSocket, frame: ServerProtocolMessage): void {\n if (connection.readyState !== WebSocket.OPEN) {\n return\n }\n\n connection.send(JSON.stringify(frame))\n}\n\nfunction validateDuration(name: string, value: number | undefined): number | undefined {\n if (value === undefined) {\n return undefined\n }\n\n if (!Number.isFinite(value) || value <= 0) {\n throw new Error(`websocket.${name} must be a positive number of milliseconds`)\n }\n\n return value\n}\n\nfunction resolveHeartbeatConfig(\n config: { pingInterval?: number; pingTimeout?: number } | undefined\n): { interval: number; timeout: number } | null {\n const pingInterval = validateDuration('pingInterval', config?.pingInterval)\n const pingTimeout = validateDuration('pingTimeout', config?.pingTimeout)\n\n if (pingInterval === undefined && pingTimeout === undefined) {\n return null\n }\n\n return {\n interval: pingInterval ?? DEFAULT_PING_INTERVAL,\n timeout: pingTimeout ?? DEFAULT_PING_TIMEOUT,\n }\n}\n\n/**\n * Main service for managing WebSocket connections.\n */\nexport class SocketService<User = unknown, HttpContext = unknown> extends Emittery<\n SocketEvents<User, HttpContext>\n> {\n #server: WebSocketServer | null = null\n #httpServer: HttpServer | null = null\n #upgradeHandler: SocketUpgradeHandler | null = null\n #sockets = new Map<string, AuthenticatedSocket<User, HttpContext>>()\n #userRooms = new Map<string, Set<string>>()\n #socketUserRoom = new Map<string, string>()\n #channelSubscriptions: ChannelSubscriptions<User, HttpContext> | null = null\n #presenceManager: PresenceManager | null = null\n #bus: SocketBus | null = null\n #fake: SocketFake | null = null\n #deliverySink: SocketEmissionSink = {\n dispatch: (emission) => {\n this.#deliverBroadcast(emission)\n },\n }\n #broadcastSink: SocketEmissionSink = this.#deliverySink\n #status: SocketServiceStatus = 'stopped'\n #lastError: Error | null = null\n #heartbeatInterval: ReturnType<typeof setInterval> | null = null\n #heartbeatTimeouts = new Map<string, ReturnType<typeof setTimeout>>()\n #messageQueue = new MessageQueue()\n\n /**\n * Accesses the underlying ws server.\n */\n get server(): WebSocketServer {\n if (!this.#server) {\n throw new Error('WebSocket server is not initialized. Call boot() first.')\n }\n\n return this.#server\n }\n\n setPresenceManager(presenceManager: PresenceManager): void {\n this.#presenceManager = presenceManager\n this.#channelSubscriptions?.setPresenceManager(presenceManager)\n this.#configurePresenceFetcher()\n }\n\n async boot(\n httpServer: HttpServer,\n config: SocketConfig<User, HttpContext>,\n channelRouter: ChannelRouter,\n logger: Logger\n ): Promise<void> {\n this.#status = 'starting'\n this.#lastError = null\n\n this.#channelSubscriptions = new ChannelSubscriptions(\n this,\n channelRouter,\n logger,\n this.#presenceManager ?? undefined\n )\n\n const websocketConfig = config.websocket\n const heartbeatConfig = resolveHeartbeatConfig(websocketConfig)\n\n try {\n if (config.transport) {\n this.#bus = new SocketBus(config.transport, {\n channel: (message) => {\n this.#traceBroadcast(\n {\n target: 'channel',\n channel: message.channel,\n event: message.event,\n via: 'bus',\n except: message.except,\n },\n () =>\n this.#emitToChannelLocally(\n message.channel,\n message.event,\n message.data,\n message.except\n )\n )\n },\n user: (message) => {\n this.#traceBroadcast(\n { target: 'user', room: message.room, event: message.event, via: 'bus' },\n () => this.#emitToUserRoomLocally(message.room, message.event, message.data)\n )\n },\n broadcast: (message) => {\n this.#traceBroadcast({ target: 'global', event: message.event, via: 'bus' }, () =>\n this.#broadcastLocally(message.event, message.data)\n )\n },\n presenceSockets: (channel) => {\n return this.#getLocalPresenceSockets(channel)\n },\n })\n\n await this.#bus.start()\n this.#configurePresenceFetcher()\n }\n\n const websocketServer = new WebSocketServer({\n noServer: true,\n })\n\n this.#server = websocketServer\n this.#httpServer = httpServer\n const upgrader = new SocketUpgrader<User, HttpContext>(websocketServer, websocketConfig)\n\n this.#upgradeHandler = (request, socket, head) => {\n upgrader\n .handle(request, socket, head, (connection, upgradeRequest, accepted) => {\n this.#handleConnection(connection, upgradeRequest, accepted)\n })\n .catch((error) => {\n logger.warn('failed to upgrade socket connection: %s', error)\n SocketUpgrader.reject(socket, 500, 'Internal Server Error')\n })\n }\n httpServer.on('upgrade', this.#upgradeHandler)\n\n this.#startHeartbeat(heartbeatConfig)\n this.#status = 'ready'\n } catch (error) {\n this.#lastError = error instanceof Error ? error : new Error('Socket service boot failed')\n this.#status = 'failed'\n await this.close()\n throw error\n }\n }\n\n #handleConnection(\n connection: WebSocket,\n request: IncomingMessage,\n upgrade: AcceptedUpgrade<User, HttpContext>\n ): void {\n const socket = this.#wrapSocket(connection, request, upgrade)\n\n connectChannel.traceSync(\n () => {\n this.#sockets.set(socket.id, socket)\n this.emit('connect', { socket })\n },\n { socketId: socket.id }\n )\n\n connection.on('message', (data) => {\n const message = Message.fromTransport(data.toString())\n\n if (message.type === 'ping') {\n sendFrame(socket.raw.connection, { id: message.id, type: 'pong' })\n return\n }\n\n this.#messageQueue.enqueue(socket.id, async () => {\n await this.#handleMessage(socket, message).catch((error) => {\n sendFrame(connection, {\n type: 'error',\n error: error instanceof Error ? error.message : 'Unexpected socket error',\n })\n })\n })\n })\n\n connection.on('pong', () => {\n this.#clearHeartbeatTimeout(socket.id)\n })\n\n connection.on('close', async () => {\n const traceMessage = {\n socketId: socket.id,\n reason: 'close',\n subscriptions: socket.channels.size,\n }\n\n await disconnectChannel.tracePromise(async () => {\n this.#clearHeartbeatTimeout(socket.id)\n this.#messageQueue.delete(socket.id)\n await this.#channelSubscriptions?.leaveAll(socket)\n this.#channelSubscriptions?.deleteSocket(socket.id)\n this.#sockets.delete(socket.id)\n this.#leaveUserRoom(socket.id)\n this.emit('disconnect', { socket, reason: 'close' })\n }, traceMessage)\n })\n }\n\n async #handleMessage(\n socket: AuthenticatedSocket<User, HttpContext>,\n message: TransportMessage\n ): Promise<void> {\n if (!message.valid) {\n sendFrame(socket.raw.connection, message.toRejectionFrame())\n return\n }\n\n switch (message.type) {\n case 'ping':\n return\n case 'subscribe': {\n const traceMessage: SocketSubscribeMessage = {\n socketId: socket.id,\n channel: message.channel,\n }\n\n await subscribeChannel.tracePromise(async () => {\n const result = await this.#channelSubscriptions?.subscribe(socket, message.channel)\n\n traceMessage.created = result?.created ?? false\n traceMessage.ok = result?.ack.ok ?? false\n if (!result?.ack.ok) {\n traceMessage.error = result?.ack.error ?? 'Socket service is not initialized'\n }\n\n if (result?.created) {\n this.emit('subscribe', { socket, channel: message.channel })\n }\n\n sendFrame(socket.raw.connection, Reply.fromSubscribeResult(message.id, result).toFrame())\n }, traceMessage)\n return\n }\n case 'unsubscribe': {\n const traceMessage: SocketUnsubscribeMessage = {\n socketId: socket.id,\n channel: message.channel,\n }\n\n await unsubscribeChannel.tracePromise(async () => {\n traceMessage.removed = socket.channels.has(message.channel)\n await this.#channelSubscriptions?.leave(socket, message.channel)\n traceMessage.ok = true\n this.emit('unsubscribe', { socket, channel: message.channel })\n sendFrame(socket.raw.connection, Reply.ok(message.id).toFrame())\n }, traceMessage)\n return\n }\n case 'message': {\n const payload = message.toChannelMessage()\n const traceMessage: SocketChannelMessage = {\n socketId: socket.id,\n channel: payload.channel,\n event: payload.event,\n }\n\n await channelMessageChannel.tracePromise(async () => {\n const ack = await this.#handleChannelMessage(socket, payload)\n traceMessage.ok = ack.ok\n if (!ack.ok) {\n traceMessage.error = ack.error ?? 'Handler error'\n }\n\n sendFrame(socket.raw.connection, Reply.fromChannelAck(message.id, ack).toFrame())\n }, traceMessage)\n }\n }\n }\n\n async #handleChannelMessage(\n socket: AuthenticatedSocket<User, HttpContext>,\n payload: ChannelMessage\n ): Promise<ChannelAck> {\n if (typeof payload.channel !== 'string' || typeof payload.event !== 'string') {\n return { ok: false, error: 'Invalid channel message' }\n }\n\n return this.#channelSubscriptions!.handleMessage(socket, payload)\n }\n\n #wrapSocket(\n connection: WebSocket,\n request: IncomingMessage,\n upgrade: AcceptedUpgrade<User, HttpContext>\n ): AuthenticatedSocket<User, HttpContext> {\n const raw: RawSocket<HttpContext> = {\n id: randomUUID(),\n data: upgrade.data ?? {},\n auth: upgrade.auth,\n connection,\n request,\n httpContext: upgrade.httpContext,\n }\n\n return {\n id: raw.id,\n user: upgrade.user,\n channels: new Set(),\n\n emit(event, data) {\n sendFrame(connection, { type: 'event', event, data })\n },\n\n joinUserRoom: (userId) => {\n this.#joinUserRoom(raw.id, userRoom(userId))\n return Promise.resolve()\n },\n\n leaveUserRoom: () => {\n this.#leaveUserRoom(raw.id)\n return Promise.resolve()\n },\n\n disconnect() {\n connection.close()\n },\n\n raw,\n }\n }\n\n #joinUserRoom(socketId: string, room: string): void {\n this.#leaveUserRoom(socketId)\n\n if (!this.#userRooms.has(room)) {\n this.#userRooms.set(room, new Set())\n }\n\n this.#userRooms.get(room)!.add(socketId)\n this.#socketUserRoom.set(socketId, room)\n }\n\n #leaveUserRoom(socketId: string): void {\n const room = this.#socketUserRoom.get(socketId)\n\n if (!room) {\n return\n }\n\n const sockets = this.#userRooms.get(room)\n sockets?.delete(socketId)\n if (sockets?.size === 0) {\n this.#userRooms.delete(room)\n }\n\n this.#socketUserRoom.delete(socketId)\n }\n\n to(channel: string) {\n return {\n emit: (event: string, data: unknown) => {\n this.#broadcastSink.dispatch({ target: 'channel', channel, event, data })\n },\n except: (socketId: string) => ({\n emit: (event: string, data: unknown) => {\n this.#broadcastSink.dispatch({\n target: 'channel',\n channel,\n event,\n data,\n except: [socketId],\n })\n },\n }),\n }\n }\n\n toUser(userId: string | number) {\n const room = userRoom(userId)\n\n return {\n emit: (event: string, data: unknown) => {\n this.#broadcastSink.dispatch({ target: 'user', userId, room, event, data })\n },\n }\n }\n\n broadcast(event: string, data: unknown): void {\n this.#broadcastSink.dispatch({ target: 'global', event, data })\n }\n\n #deliverBroadcast(emission: SocketEmission): void {\n switch (emission.target) {\n case 'channel': {\n const channel = emission.channel\n this.#bus?.publishChannel(channel, emission.event, emission.data, emission.except)\n\n const traceMessage: SocketBroadcastMessage = {\n target: 'channel',\n channel,\n event: emission.event,\n via: 'local',\n }\n if (emission.except) {\n traceMessage.except = emission.except\n }\n\n this.#traceBroadcast(traceMessage, () =>\n this.#emitToChannelLocally(channel, emission.event, emission.data, emission.except)\n )\n return\n }\n case 'user': {\n const room = emission.room\n this.#bus?.publishUser(room, emission.event, emission.data)\n this.#traceBroadcast({ target: 'user', room, event: emission.event, via: 'local' }, () =>\n this.#emitToUserRoomLocally(room, emission.event, emission.data)\n )\n return\n }\n case 'global':\n this.#bus?.publishBroadcast(emission.event, emission.data)\n this.#traceBroadcast({ target: 'global', event: emission.event, via: 'local' }, () =>\n this.#broadcastLocally(emission.event, emission.data)\n )\n }\n }\n\n #traceBroadcast(message: SocketBroadcastMessage, deliver: () => number): void {\n broadcastChannel.traceSync(() => {\n message.delivered = deliver()\n }, message)\n }\n\n #emitToChannelLocally(\n channel: string,\n event: string,\n data: unknown,\n except: string[] = []\n ): number {\n let delivered = 0\n\n for (const socketId of this.#channelSubscriptions?.getSocketIds(channel) ?? []) {\n if (except.includes(socketId)) {\n continue\n }\n\n const socket = this.#sockets.get(socketId)\n if (socket) {\n sendFrame(socket.raw.connection, { type: 'event', channel, event, data })\n delivered += 1\n }\n }\n\n return delivered\n }\n\n #emitToUserRoomLocally(room: string, event: string, data: unknown): number {\n let delivered = 0\n\n for (const socketId of this.#userRooms.get(room) ?? []) {\n const socket = this.#sockets.get(socketId)\n if (socket) {\n sendFrame(socket.raw.connection, { type: 'event', event, data })\n delivered += 1\n }\n }\n\n return delivered\n }\n\n #broadcastLocally(event: string, data: unknown): number {\n let delivered = 0\n\n for (const socket of this.#sockets.values()) {\n sendFrame(socket.raw.connection, { type: 'event', event, data })\n delivered += 1\n }\n\n return delivered\n }\n\n #configurePresenceFetcher(): void {\n if (!this.#presenceManager || !this.#bus) {\n return\n }\n\n this.#presenceManager.setSocketFetcher(async (channel) => {\n return [\n ...this.#presenceManager!.getLocalSockets(channel),\n ...(await this.#bus!.fetchPresenceSockets(channel)),\n ]\n })\n }\n\n #getLocalPresenceSockets(channel: string): SerializablePresenceSocket[] {\n return (this.#presenceManager?.getLocalSockets(channel) ?? []).map((socket) => {\n return this.#serializePresenceSocket(channel, socket)\n })\n }\n\n #serializePresenceSocket(channel: string, socket: PresenceSocket): SerializablePresenceSocket {\n const user = socket.data[PRESENCE_DATA_KEY]![channel]\n\n return {\n id: socket.id,\n data: {\n [PRESENCE_DATA_KEY]: {\n [channel]: {\n ...user,\n joinedAt: user.joinedAt.toISOString(),\n },\n },\n },\n }\n }\n\n getSocket(socketId: string): AuthenticatedSocket<User, HttpContext> | undefined {\n return this.#sockets.get(socketId)\n }\n\n get connectionsCount(): number {\n return this.#sockets.size\n }\n\n get status(): SocketServiceStatus {\n return this.#status\n }\n\n get ready(): boolean {\n return this.#status === 'ready' && Boolean(this.#server)\n }\n\n fake(): SocketFake {\n const fake = new SocketFake(() => {\n if (this.#fake === fake) {\n this.restore()\n }\n })\n\n this.#fake = fake\n this.#broadcastSink = fake\n return fake\n }\n\n restore(): void {\n this.#fake = null\n this.#broadcastSink = this.#deliverySink\n }\n\n markFailed(error: unknown): void {\n this.#lastError = error instanceof Error ? error : new Error('Socket service failed')\n this.#status = 'failed'\n }\n\n health(): SocketHealthSnapshot {\n return {\n status: this.#status,\n ready: this.ready,\n connections: this.#sockets.size,\n channels: this.#channelSubscriptions?.channelsCount ?? 0,\n ...(this.#lastError ? { lastError: this.#lastError.message } : {}),\n }\n }\n\n #startHeartbeat(config: { interval: number; timeout: number } | null): void {\n this.#stopHeartbeat()\n\n if (!config) {\n return\n }\n\n this.#heartbeatInterval = setInterval(() => {\n this.#pingSockets(config.timeout)\n }, config.interval)\n }\n\n #pingSockets(timeout: number): void {\n for (const socket of this.#sockets.values()) {\n const { connection } = socket.raw\n\n if (connection.readyState !== WebSocket.OPEN) {\n this.#clearHeartbeatTimeout(socket.id)\n continue\n }\n\n if (this.#heartbeatTimeouts.has(socket.id)) {\n continue\n }\n\n const timeoutId = setTimeout(() => {\n this.#heartbeatTimeouts.delete(socket.id)\n connection.terminate()\n }, timeout)\n\n this.#heartbeatTimeouts.set(socket.id, timeoutId)\n\n try {\n connection.ping()\n } catch {\n this.#clearHeartbeatTimeout(socket.id)\n connection.terminate()\n }\n }\n }\n\n #clearHeartbeatTimeout(socketId: string): void {\n const timeout = this.#heartbeatTimeouts.get(socketId)\n\n if (!timeout) {\n return\n }\n\n clearTimeout(timeout)\n this.#heartbeatTimeouts.delete(socketId)\n }\n\n #stopHeartbeat(): void {\n if (this.#heartbeatInterval) {\n clearInterval(this.#heartbeatInterval)\n this.#heartbeatInterval = null\n }\n\n for (const timeout of this.#heartbeatTimeouts.values()) {\n clearTimeout(timeout)\n }\n\n this.#heartbeatTimeouts.clear()\n }\n\n #cleanup(): void {\n this.#stopHeartbeat()\n\n for (const socket of this.#sockets.values()) {\n socket.raw.connection.terminate()\n }\n\n this.#sockets.clear()\n this.#userRooms.clear()\n this.#socketUserRoom.clear()\n this.#messageQueue.clear()\n this.#channelSubscriptions?.clear()\n }\n\n async close(): Promise<void> {\n const server = this.#server\n const bus = this.#bus\n const httpServer = this.#httpServer\n const upgradeHandler = this.#upgradeHandler\n if (this.#status !== 'failed') {\n this.#status = 'stopping'\n }\n\n this.#server = null\n this.#bus = null\n this.#httpServer = null\n this.#upgradeHandler = null\n if (httpServer && upgradeHandler) {\n httpServer.off('upgrade', upgradeHandler)\n }\n this.#cleanup()\n\n try {\n await bus?.close()\n\n if (!server) {\n if (this.#status !== 'failed') {\n this.#status = 'stopped'\n }\n return\n }\n\n await new Promise<void>((resolve) => {\n server.close(() => {\n resolve()\n })\n })\n\n if (this.#status !== 'failed') {\n this.#status = 'stopped'\n }\n } catch (error) {\n this.markFailed(error)\n throw error\n }\n }\n}\n","import type { BaseChannel } from './base_channel.js'\n\nexport interface StoredChannelSubscription {\n channelName: string\n instance: BaseChannel<any, any, any>\n paramValues: string[]\n}\n\n/**\n * Stores durable Channel subscriptions and keeps Socket/Channel indexes in sync.\n */\nexport class ChannelSubscriptionStorage {\n #subscriptions = new Map<string, Map<string, StoredChannelSubscription>>()\n #subscribers = new Map<string, Set<string>>()\n\n has(socketId: string, channelName: string): boolean {\n return this.#subscriptions.get(socketId)?.has(channelName) ?? false\n }\n\n get(socketId: string, channelName: string): StoredChannelSubscription | undefined {\n return this.#subscriptions.get(socketId)?.get(channelName)\n }\n\n set(socketId: string, channelName: string, subscription: StoredChannelSubscription): void {\n if (!this.#subscriptions.has(socketId)) {\n this.#subscriptions.set(socketId, new Map())\n }\n\n this.#subscriptions.get(socketId)!.set(channelName, subscription)\n\n if (!this.#subscribers.has(channelName)) {\n this.#subscribers.set(channelName, new Set())\n }\n\n this.#subscribers.get(channelName)!.add(socketId)\n }\n\n delete(socketId: string, channelName: string): void {\n const subscriptions = this.#subscriptions.get(socketId)\n\n if (!subscriptions) {\n return\n }\n\n subscriptions.delete(channelName)\n if (subscriptions.size === 0) {\n this.#subscriptions.delete(socketId)\n }\n\n const subscribers = this.#subscribers.get(channelName)\n subscribers?.delete(socketId)\n if (subscribers?.size === 0) {\n this.#subscribers.delete(channelName)\n }\n }\n\n deleteSocket(socketId: string): void {\n for (const channelName of this.channelNamesFor(socketId)) {\n this.delete(socketId, channelName)\n }\n }\n\n channelNamesFor(socketId: string): string[] {\n return [...(this.#subscriptions.get(socketId)?.keys() ?? [])]\n }\n\n socketIdsFor(channelName: string): string[] {\n return [...(this.#subscribers.get(channelName) ?? [])]\n }\n\n get channelsCount(): number {\n return this.#subscribers.size\n }\n\n clear(): void {\n this.#subscriptions.clear()\n this.#subscribers.clear()\n }\n}\n","import type { Logger } from '@adonisjs/logger'\nimport { MissingPresenceInfoError } from './base_channel.js'\nimport type { BaseChannel } from './base_channel.js'\nimport { ChannelSubscriptionStorage } from './channel_subscription_storage.js'\nimport type { ChannelRouter } from './channel_router.js'\nimport type { PresenceData, PresenceManager } from './presence_manager.js'\nimport type { SocketService } from './socket_service.js'\nimport type { AuthenticatedSocket, ChannelAck, ChannelMessage, PresenceMember } from './types.js'\n\nexport interface ChannelSubscribeAck {\n ok: boolean\n presenceData?: Record<string, unknown> | PresenceData | null\n error?: string\n}\n\nexport interface ChannelSubscribeResult {\n ack: ChannelSubscribeAck\n created: boolean\n}\n\n/**\n * Durable channel subscriptions for sockets.\n */\nexport class ChannelSubscriptions<User = unknown, HttpContext = unknown> {\n #storage = new ChannelSubscriptionStorage()\n\n constructor(\n private socketService: SocketService<User, HttpContext>,\n private channelRouter: ChannelRouter,\n private logger: Logger,\n private presenceManager?: PresenceManager\n ) {}\n\n setPresenceManager(presenceManager: PresenceManager): void {\n this.presenceManager = presenceManager\n }\n\n async subscribe(\n socket: AuthenticatedSocket<User, HttpContext>,\n channelName: string\n ): Promise<ChannelSubscribeResult> {\n if (this.#storage.has(socket.id, channelName)) {\n return {\n ack: {\n ok: true,\n presenceData: await this.#getPresenceData(socket, channelName),\n },\n created: false,\n }\n }\n\n const result = await this.channelRouter.authorize(socket, channelName)\n\n if (!result.success) {\n return {\n ack: { ok: false, error: result.error },\n created: false,\n }\n }\n\n result.instance.$setContext(\n this.socketService,\n channelName,\n result.params,\n this.presenceManager\n )\n\n let presenceData: Record<string, unknown> | PresenceData | null = result.presenceData\n let presenceJoined = false\n\n try {\n if (this.#usesPresence(result.instance)) {\n const member = this.#getPresenceMember(result.instance, socket)\n\n this.presenceManager!.join(channelName, socket.raw, member)\n presenceJoined = true\n\n await result.instance.onMemberJoin?.(socket, member, ...result.paramValues)\n await result.instance.onJoin?.(socket, ...result.paramValues)\n\n presenceData = await this.presenceManager!.snapshot(channelName)\n } else {\n await result.instance.onJoin?.(socket, ...result.paramValues)\n }\n\n this.#storage.set(socket.id, channelName, {\n channelName,\n instance: result.instance,\n paramValues: result.paramValues,\n })\n socket.channels.add(channelName)\n\n if (this.#usesPresence(result.instance) && presenceData) {\n this.socketService.to(channelName).except(socket.id).emit('presence:update', presenceData)\n }\n } catch (error) {\n if (presenceJoined) {\n this.presenceManager!.leave(channelName, socket.raw)\n\n const rollbackData = await this.presenceManager!.snapshot(channelName).catch(() => null)\n if (rollbackData) {\n this.socketService.to(channelName).emit('presence:update', rollbackData)\n }\n }\n\n this.#storage.delete(socket.id, channelName)\n socket.channels.delete(channelName)\n\n return {\n ack: {\n ok: false,\n error: error instanceof MissingPresenceInfoError ? error.message : 'Join failed',\n },\n created: false,\n }\n }\n\n return {\n ack: { ok: true, presenceData },\n created: true,\n }\n }\n\n async leave(socket: AuthenticatedSocket<User, HttpContext>, channelName: string): Promise<void> {\n const subscription = this.#storage.get(socket.id, channelName)\n\n if (subscription) {\n this.#storage.delete(socket.id, channelName)\n socket.channels.delete(channelName)\n\n try {\n if (this.#usesPresence(subscription.instance)) {\n const member = this.#getPresenceMember(subscription.instance, socket)\n\n this.presenceManager!.leave(channelName, socket.raw)\n const presenceData = await this.presenceManager!.snapshot(channelName)\n\n this.socketService.to(channelName).emit('presence:update', presenceData)\n\n await subscription.instance.onMemberLeave?.(socket, member, ...subscription.paramValues)\n await subscription.instance.onLeave?.(socket, ...subscription.paramValues)\n } else {\n await subscription.instance.onLeave?.(socket, ...subscription.paramValues)\n }\n } catch (error) {\n this.logger.warn(`failed to leave channel \"${channelName}\": %s`, error)\n }\n } else {\n socket.channels.delete(channelName)\n }\n }\n\n async leaveAll(socket: AuthenticatedSocket<User, HttpContext>): Promise<void> {\n for (const channelName of this.getChannelNames(socket.id)) {\n await this.leave(socket, channelName)\n }\n }\n\n async handleMessage(\n socket: AuthenticatedSocket<User, HttpContext>,\n payload: ChannelMessage\n ): Promise<ChannelAck> {\n const subscription = this.#storage.get(socket.id, payload.channel)\n\n if (!subscription) {\n return { ok: false, error: 'Not subscribed' }\n }\n\n try {\n const result = await subscription.instance.$handleMessage(socket, payload.event, payload.data)\n return { ok: true, data: result }\n } catch (error) {\n return {\n ok: false,\n error: error instanceof Error ? error.message : 'Handler error',\n }\n }\n }\n\n deleteSocket(socketId: string): void {\n this.#storage.deleteSocket(socketId)\n }\n\n clear(): void {\n this.#storage.clear()\n }\n\n getSocketIds(channelName: string): string[] {\n return this.#storage.socketIdsFor(channelName)\n }\n\n getChannelNames(socketId: string): string[] {\n return this.#storage.channelNamesFor(socketId)\n }\n\n get channelsCount(): number {\n return this.#storage.channelsCount\n }\n\n #usesPresence(instance: BaseChannel<any, any, any>): boolean {\n return Boolean(\n (instance.constructor as typeof BaseChannel).options?.presence && this.presenceManager\n )\n }\n\n #getPresenceMember(\n instance: BaseChannel<any, any, any>,\n socket: AuthenticatedSocket<User, HttpContext>\n ): PresenceMember {\n return instance.$getPresenceInfo(socket)\n }\n\n async #getPresenceData(\n socket: AuthenticatedSocket<User, HttpContext>,\n channelName: string\n ): Promise<PresenceData | undefined> {\n if (!this.presenceManager?.hasLocal(channelName, socket.id)) {\n return undefined\n }\n\n return this.presenceManager.snapshot(channelName)\n }\n}\n","import type { ChannelAck, ServerProtocolMessage } from '../shared_types.js'\nimport type { ChannelSubscribeResult } from '../channel_subscriptions.js'\n\nconst INVALID_SOCKET_MESSAGE = 'Invalid socket message'\n\nexport class Reply {\n private constructor(\n private id: string | undefined,\n private ok: boolean,\n private data: unknown | undefined,\n private error: string | undefined\n ) {}\n\n static ok(id: string | undefined, data?: unknown): Reply {\n return new Reply(id, true, data, undefined)\n }\n\n static error(id: string | undefined, error: string): Reply {\n return new Reply(id, false, undefined, error)\n }\n\n static invalidMessage(id: string | undefined): ServerProtocolMessage {\n if (id === undefined) {\n return { type: 'error', error: INVALID_SOCKET_MESSAGE }\n }\n\n return Reply.error(id, INVALID_SOCKET_MESSAGE).toFrame()\n }\n\n static fromSubscribeResult(\n id: string | undefined,\n result: ChannelSubscribeResult | undefined\n ): Reply {\n if (!result) {\n return Reply.error(id, 'Socket service is not initialized')\n }\n\n if (!result.ack.ok) {\n return Reply.error(id, result.ack.error ?? 'Subscribe failed')\n }\n\n const data = result.ack.presenceData\n ? {\n presenceData: result.ack.presenceData,\n }\n : undefined\n\n return Reply.ok(id, data)\n }\n\n static fromChannelAck(id: string | undefined, ack: ChannelAck): Reply {\n return ack.ok ? Reply.ok(id, ack.data) : Reply.error(id, ack.error ?? 'Handler error')\n }\n\n toFrame(): Extract<ServerProtocolMessage, { type: 'ack' }> {\n const frame: Extract<ServerProtocolMessage, { type: 'ack' }> = {\n id: this.id,\n type: 'ack',\n ok: this.ok,\n }\n\n if (this.data !== undefined) {\n frame.data = this.data\n }\n\n if (this.error !== undefined) {\n frame.error = this.error\n }\n\n return frame\n }\n}\n","import type {\n ChannelMessage,\n ClientProtocolMessage,\n ServerProtocolMessage,\n} from '../shared_types.js'\nimport { Reply } from './reply.js'\n\ntype ClientMessageType = ClientProtocolMessage['type']\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nexport abstract class Message {\n abstract readonly valid: boolean\n abstract readonly type: ClientMessageType | 'invalid'\n\n static fromTransport(raw: string): TransportMessage {\n try {\n return Message.fromParsedTransport(JSON.parse(raw))\n } catch {\n return new InvalidMessage(undefined)\n }\n }\n\n private static fromParsedTransport(value: unknown): TransportMessage {\n if (!isRecord(value)) {\n return new InvalidMessage(undefined)\n }\n\n const id = typeof value.id === 'string' ? value.id : undefined\n\n if ('id' in value && typeof value.id !== 'string') {\n return new InvalidMessage(undefined)\n }\n\n if (!isClientMessageType(value.type)) {\n return new InvalidMessage(id)\n }\n\n if (value.type === 'ping') {\n return new PingMessage(id)\n }\n\n if (value.type === 'subscribe' && typeof value.channel === 'string') {\n return new SubscribeMessage(id, value.channel)\n }\n\n if (value.type === 'unsubscribe' && typeof value.channel === 'string') {\n return new UnsubscribeMessage(id, value.channel)\n }\n\n if (\n value.type === 'message' &&\n typeof value.channel === 'string' &&\n typeof value.event === 'string'\n ) {\n return new ChannelProtocolMessage(id, value.channel, value.event, value.data)\n }\n\n return new InvalidMessage(id)\n }\n}\n\nexport class InvalidMessage extends Message {\n readonly valid = false\n readonly type = 'invalid'\n\n constructor(readonly id: string | undefined) {\n super()\n }\n\n toRejectionFrame(): ServerProtocolMessage {\n return Reply.invalidMessage(this.id)\n }\n}\n\nexport class PingMessage extends Message {\n readonly valid = true\n readonly type = 'ping'\n\n constructor(readonly id: string | undefined) {\n super()\n }\n}\n\nexport class SubscribeMessage extends Message {\n readonly valid = true\n readonly type = 'subscribe'\n\n constructor(\n readonly id: string | undefined,\n readonly channel: string\n ) {\n super()\n }\n}\n\nexport class UnsubscribeMessage extends Message {\n readonly valid = true\n readonly type = 'unsubscribe'\n\n constructor(\n readonly id: string | undefined,\n readonly channel: string\n ) {\n super()\n }\n}\n\nexport class ChannelProtocolMessage extends Message {\n readonly valid = true\n readonly type = 'message'\n\n constructor(\n readonly id: string | undefined,\n readonly channel: string,\n readonly event: string,\n readonly data: unknown\n ) {\n super()\n }\n\n toChannelMessage(): ChannelMessage {\n return {\n channel: this.channel,\n event: this.event,\n data: this.data,\n }\n }\n}\n\nexport type TransportMessage =\n | InvalidMessage\n | PingMessage\n | SubscribeMessage\n | UnsubscribeMessage\n | ChannelProtocolMessage\n\nfunction isClientMessageType(value: unknown): value is ClientMessageType {\n return value === 'ping' || value === 'subscribe' || value === 'unsubscribe' || value === 'message'\n}\n","export class MessageQueue<Key = string> {\n #queues = new Map<Key, Promise<void>>()\n\n enqueue(key: Key, task: () => Promise<void>): void {\n const previous = this.#queues.get(key) ?? Promise.resolve()\n const next = previous\n .catch(() => {})\n .then(task)\n .catch(() => {})\n .finally(() => {\n if (this.#queues.get(key) === next) {\n this.#queues.delete(key)\n }\n })\n\n this.#queues.set(key, next)\n }\n\n delete(key: Key): void {\n this.#queues.delete(key)\n }\n\n clear(): void {\n this.#queues.clear()\n }\n}\n","export type SocketEmission =\n | {\n target: 'global'\n event: string\n data: unknown\n }\n | {\n target: 'channel'\n channel: string\n event: string\n data: unknown\n except?: string[]\n }\n | {\n target: 'user'\n userId: string | number\n room: string\n event: string\n data: unknown\n }\n\nexport type SocketEmissionTarget = SocketEmission['target']\n\nexport interface SocketEmissionSink {\n dispatch(emission: SocketEmission): void\n}\n\nexport function userRoom(userId: string | number): string {\n return `socket:user:${String(userId)}`\n}\n","import { randomUUID } from 'node:crypto'\nimport { Bus } from '@boringnode/bus'\nimport type { Serializable, TransportFactory } from '@boringnode/bus/types/main'\nimport { PRESENCE_DATA_KEY, type PresenceSocket, type PresenceUser } from './presence_manager.js'\n\nconst DEFAULT_CHANNEL = 'socket::broadcast'\nconst DEFAULT_PRESENCE_TIMEOUT = 100\n\ntype SerializablePresenceUser = Omit<PresenceUser, 'joinedAt'> & {\n [key: string]: Serializable\n joinedAt: string\n}\n\nexport interface SerializablePresenceSocket {\n [key: string]: Serializable\n id: string\n data: {\n [key: string]: Serializable\n }\n}\n\nexport type SocketBusMessage =\n | {\n type: 'channel:event'\n origin: string\n channel: string\n event: string\n data: Serializable\n except?: string[]\n }\n | {\n type: 'user:event'\n origin: string\n room: string\n event: string\n data: Serializable\n }\n | {\n type: 'broadcast:event'\n origin: string\n event: string\n data: Serializable\n }\n | {\n type: 'presence:sockets:request'\n origin: string\n requestId: string\n channel: string\n }\n | {\n type: 'presence:sockets:response'\n origin: string\n target: string\n requestId: string\n sockets: SerializablePresenceSocket[]\n }\n\nexport interface SocketTransportConfig {\n driver: TransportFactory\n channel?: string\n presenceTimeout?: number\n}\n\ninterface SocketBusHandlers {\n channel(message: Extract<SocketBusMessage, { type: 'channel:event' }>): void\n user(message: Extract<SocketBusMessage, { type: 'user:event' }>): void\n broadcast(message: Extract<SocketBusMessage, { type: 'broadcast:event' }>): void\n presenceSockets(channel: string): SerializablePresenceSocket[]\n}\n\ninterface PendingPresenceRequest {\n sockets: SerializablePresenceSocket[]\n resolve(sockets: PresenceSocket[]): void\n timeout: ReturnType<typeof setTimeout>\n}\n\nexport class SocketBus {\n #origin = randomUUID()\n #channel: string\n #bus: Bus\n #presenceTimeout: number\n #pendingPresenceRequests = new Map<string, PendingPresenceRequest>()\n\n constructor(\n transport: SocketTransportConfig,\n private handlers: SocketBusHandlers\n ) {\n this.#channel = transport.channel ?? DEFAULT_CHANNEL\n this.#presenceTimeout = transport.presenceTimeout ?? DEFAULT_PRESENCE_TIMEOUT\n this.#bus = new Bus(transport.driver(), { retryQueue: { enabled: true } })\n }\n\n async start(): Promise<void> {\n await this.#bus.subscribe(this.#channel, async (payload) => {\n const message = payload as SocketBusMessage\n\n if (message.origin === this.#origin) {\n return\n }\n\n switch (message.type) {\n case 'channel:event':\n this.handlers.channel(message)\n return\n case 'user:event':\n this.handlers.user(message)\n return\n case 'broadcast:event':\n this.handlers.broadcast(message)\n return\n case 'presence:sockets:request':\n await this.#respondToPresenceRequest(message)\n return\n case 'presence:sockets:response':\n this.#handlePresenceResponse(message)\n return\n }\n })\n }\n\n publishChannel(channel: string, event: string, data: unknown, except?: string[]): void {\n const message: SocketBusMessage = {\n type: 'channel:event',\n origin: this.#origin,\n channel,\n event,\n data: data as Serializable,\n }\n\n if (except) {\n message.except = except\n }\n\n void this.#bus.publish(this.#channel, message as Serializable)\n }\n\n publishUser(room: string, event: string, data: unknown): void {\n void this.#bus.publish(this.#channel, {\n type: 'user:event',\n origin: this.#origin,\n room,\n event,\n data: data as Serializable,\n } as Serializable)\n }\n\n publishBroadcast(event: string, data: unknown): void {\n void this.#bus.publish(this.#channel, {\n type: 'broadcast:event',\n origin: this.#origin,\n event,\n data: data as Serializable,\n } as Serializable)\n }\n\n fetchPresenceSockets(channel: string): Promise<PresenceSocket[]> {\n const requestId = randomUUID()\n\n return new Promise((resolve) => {\n const timeout = setTimeout(() => {\n const request = this.#pendingPresenceRequests.get(requestId)\n\n if (!request) {\n return\n }\n\n this.#pendingPresenceRequests.delete(requestId)\n resolve(request.sockets.map((socket) => this.#deserializePresenceSocket(socket)))\n }, this.#presenceTimeout)\n\n this.#pendingPresenceRequests.set(requestId, {\n sockets: [],\n resolve,\n timeout,\n })\n\n void this.#bus.publish(this.#channel, {\n type: 'presence:sockets:request',\n origin: this.#origin,\n requestId,\n channel,\n } as Serializable)\n })\n }\n\n async close(): Promise<void> {\n for (const [requestId, request] of this.#pendingPresenceRequests) {\n clearTimeout(request.timeout)\n request.resolve(request.sockets.map((socket) => this.#deserializePresenceSocket(socket)))\n this.#pendingPresenceRequests.delete(requestId)\n }\n\n await this.#bus.disconnect()\n }\n\n async #respondToPresenceRequest(\n message: Extract<SocketBusMessage, { type: 'presence:sockets:request' }>\n ): Promise<void> {\n await this.#bus.publish(this.#channel, {\n type: 'presence:sockets:response',\n origin: this.#origin,\n target: message.origin,\n requestId: message.requestId,\n sockets: this.handlers.presenceSockets(message.channel),\n } as unknown as Serializable)\n }\n\n #handlePresenceResponse(\n message: Extract<SocketBusMessage, { type: 'presence:sockets:response' }>\n ): void {\n if (message.target !== this.#origin) {\n return\n }\n\n const request = this.#pendingPresenceRequests.get(message.requestId)\n\n if (!request) {\n return\n }\n\n request.sockets.push(...message.sockets)\n }\n\n #deserializePresenceSocket(socket: SerializablePresenceSocket): PresenceSocket {\n const presence = socket.data[PRESENCE_DATA_KEY] as\n | Record<string, SerializablePresenceUser>\n | undefined\n\n return {\n id: socket.id,\n data: {\n [PRESENCE_DATA_KEY]: presence\n ? Object.fromEntries(\n Object.entries(presence).map(([channel, user]) => [\n channel,\n {\n ...user,\n joinedAt: new Date(user.joinedAt),\n } as PresenceUser,\n ])\n )\n : undefined,\n },\n }\n }\n}\n","import type { IncomingMessage } from 'node:http'\nimport type { Duplex } from 'node:stream'\nimport type { WebSocket, WebSocketServer } from 'ws'\nimport type { SocketAuthenticationResult, SocketConfig, SocketUpgradeContext } from './types.js'\n\nexport const DEFAULT_WEBSOCKET_PATH = '/socket'\n\nexport interface AcceptedUpgrade<User, HttpContext> {\n auth?: Record<string, unknown>\n data?: Record<string, unknown>\n httpContext?: HttpContext\n user?: User\n}\n\nexport type SocketUpgradeHandler = (request: IncomingMessage, socket: Duplex, head: Buffer) => void\n\ntype AcceptUpgrade<User, HttpContext> = (\n connection: WebSocket,\n request: IncomingMessage,\n upgrade: AcceptedUpgrade<User, HttpContext>\n) => void\n\nexport class SocketUpgrader<User = unknown, HttpContext = unknown> {\n #path: string\n\n constructor(\n private server: WebSocketServer,\n private config: SocketConfig<User, HttpContext>['websocket'] | undefined\n ) {\n this.#path = config?.path ?? DEFAULT_WEBSOCKET_PATH\n }\n\n async handle(\n request: IncomingMessage,\n socket: Duplex,\n head: Buffer,\n accept: AcceptUpgrade<User, HttpContext>\n ): Promise<boolean> {\n if (!this.#matchesPath(request)) {\n return false\n }\n\n const accepted = await this.#authenticate(request)\n\n if (!accepted) {\n SocketUpgrader.reject(socket, 401, 'Unauthorized')\n return true\n }\n\n this.server.handleUpgrade(request, socket, head, (connection) => {\n accept(connection, request, accepted)\n })\n\n return true\n }\n\n static reject(socket: Duplex, statusCode: number, reason: string): void {\n if (!socket.writableEnded) {\n socket.write(`HTTP/1.1 ${statusCode} ${reason}\\r\\n\\r\\n`)\n }\n\n socket.destroy()\n }\n\n #matchesPath(request: IncomingMessage): boolean {\n const url = new URL(request.url ?? '/', 'ws://localhost')\n return url.pathname === this.#path\n }\n\n async #authenticate(\n request: IncomingMessage\n ): Promise<AcceptedUpgrade<User, HttpContext> | null> {\n const auth = this.#parseAuth(request)\n\n if (!this.config?.authenticate) {\n return { auth }\n }\n\n const httpContext = await this.config.createContext?.(request)\n const ctx: SocketUpgradeContext<HttpContext> = {\n request,\n auth,\n httpContext,\n }\n\n let result: SocketAuthenticationResult<User>\n try {\n result = await this.config.authenticate(ctx)\n } catch {\n return null\n }\n\n if (result === false || result === null) {\n return null\n }\n\n return {\n auth: result.auth ?? auth,\n data: result.data,\n httpContext,\n user: result.user,\n }\n }\n\n #parseAuth(request: IncomingMessage): Record<string, unknown> | undefined {\n const url = new URL(request.url ?? '/', 'ws://localhost')\n const auth = url.searchParams.get('auth')\n\n if (!auth) {\n return undefined\n }\n\n try {\n const parsed = JSON.parse(auth)\n return parsed && typeof parsed === 'object' ? parsed : undefined\n } catch {\n return undefined\n }\n }\n}\n","import type { BaseChannel } from './base_channel.js'\nimport { exec, match, parse, type ParsedRoute } from '@poppinss/matchit'\n\ntype MatchParams = Record<string, string>\n\nexport interface ChannelPatternMatch {\n pattern: ChannelPattern\n channel: typeof BaseChannel\n params: MatchParams\n}\n\ninterface MatchCandidate {\n pattern: ChannelPattern\n channel: typeof BaseChannel\n}\n\nexport class ChannelPattern {\n readonly route: ParsedRoute\n readonly score: number[]\n\n private constructor(readonly value: string) {\n this.route = parse(value)\n this.score = this.route.map((segment) => {\n switch (segment.type) {\n case 0:\n return 40\n case 1:\n return 30\n case 3:\n return 20\n case 2:\n return 0\n }\n })\n }\n\n static from(value: string): ChannelPattern {\n return new ChannelPattern(value)\n }\n\n match(channelName: string): MatchParams | null {\n const matched = match(channelName, [this.route])\n if (matched.length === 0) {\n return null\n }\n\n return exec(channelName, matched, true) as MatchParams\n }\n\n /**\n * Returns the most specific matching candidate. Static segments outrank params,\n * params outrank optional params, and optional params outrank wildcards.\n * Insertion order is preserved for ties.\n */\n static firstMatch(\n channelName: string,\n candidates: Iterable<MatchCandidate>\n ): ChannelPatternMatch | null {\n const sorted = [...candidates].sort((left, right) => {\n return ChannelPattern.#compareSpecificity(left.pattern, right.pattern)\n })\n const routes = sorted.map((candidate) => candidate.pattern.route)\n const matched = match(channelName, routes)\n\n if (matched.length === 0) {\n return null\n }\n\n for (const { pattern, channel } of sorted) {\n if (pattern.route !== matched) {\n continue\n }\n\n const params = pattern.match(channelName)\n if (params) {\n return { pattern, channel, params }\n }\n }\n\n return null\n }\n\n static #compareSpecificity(left: ChannelPattern, right: ChannelPattern): number {\n const length = Math.max(left.score.length, right.score.length)\n\n for (let index = 0; index < length; index++) {\n const scoreDifference = (right.score[index] ?? -1) - (left.score[index] ?? -1)\n if (scoreDifference !== 0) {\n return scoreDifference\n }\n }\n\n return right.score.length - left.score.length\n }\n}\n","import type { BaseChannel } from './base_channel.js'\nimport { ChannelPattern } from './channel_pattern.js'\nimport type {\n AuthenticatedSocket,\n ChannelEventMap,\n ChannelMatch,\n MiddlewareContext,\n} from './types.js'\n\ninterface RegisteredChannel {\n pattern: ChannelPattern\n channel: typeof BaseChannel\n}\n\n/**\n * Matches channel names against registered patterns.\n *\n * Pattern: chat/:roomId\n * Channel: chat/general\n *\n * chat / :roomId\n * | |\n * chat / general => { roomId: \"general\" }\n */\nexport class ChannelRouter {\n /**\n * Registered channels keyed by their pattern.\n */\n #channels = new Map<string, RegisteredChannel>()\n\n /**\n * Registers a channel.\n */\n register(channel: typeof BaseChannel): void {\n const patternValue = channel.pattern\n\n if (!patternValue) {\n throw new Error(`Channel ${channel.name} must define a static pattern`)\n }\n\n if (this.#channels.has(patternValue)) {\n throw new Error(`Channel pattern \"${patternValue}\" is already registered`)\n }\n\n const pattern = ChannelPattern.from(patternValue)\n\n this.#channels.set(pattern.value, { pattern, channel })\n }\n\n /**\n * Matches a channel name against registered patterns.\n */\n match(channelName: string): ChannelMatch | null {\n const matched = ChannelPattern.firstMatch(channelName, this.#channels.values())\n\n if (!matched) {\n return null\n }\n\n return {\n channel: matched.channel,\n pattern: matched.pattern.value,\n params: matched.params,\n }\n }\n\n /**\n * Authorizes a socket before it joins a channel.\n */\n async authorize<User = unknown, HttpContext = unknown>(\n socket: AuthenticatedSocket<User, HttpContext>,\n channelName: string\n ) {\n const matched = this.match(channelName)\n\n if (!matched) {\n return {\n success: false,\n error: 'Channel not found',\n } as const\n }\n\n const instance = new matched.channel() as BaseChannel<User, ChannelEventMap, HttpContext>\n const paramValues = Object.values(matched.params)\n\n const ctx: MiddlewareContext<User, HttpContext> = {\n socket,\n channel: channelName,\n params: matched.params,\n presenceData: undefined,\n setPresenceData(data) {\n ctx.presenceData = data\n },\n }\n\n // Run authorization middlewares.\n try {\n await instance.$runMiddlewares(ctx)\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Middleware rejected',\n } as const\n }\n\n return {\n success: true,\n instance,\n params: matched.params,\n paramValues,\n presenceData: ctx.presenceData ?? null,\n } as const\n }\n\n /**\n * Lists all registered channels.\n */\n list() {\n return [...this.#channels.values()].map(({ pattern, channel }) => ({\n pattern: pattern.value,\n name: channel.name,\n options: channel.options,\n }))\n }\n\n /**\n * Number of registered channels.\n */\n get size(): number {\n return this.#channels.size\n }\n}\n"],"mappings":"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","names":["randomUUID","randomUUID"]}
|