@rlanz/socket 0.0.1-0 → 0.0.1-2
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 +40 -13
- package/build/{chunk-4UPUCRVG.js → chunk-NRWRLZAO.js} +64 -42
- package/build/chunk-NRWRLZAO.js.map +1 -0
- package/build/index.d.ts +3 -2
- package/build/index.js +1 -1
- package/build/providers/socket_provider.d.ts +3 -2
- package/build/providers/socket_provider.js +39 -4
- package/build/providers/socket_provider.js.map +1 -1
- package/build/services/socket.d.ts +3 -2
- package/build/{socket_service-jxIaH5Rs.d.ts → socket_service-BN1g3ENk.d.ts} +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/build/src/decorators.d.ts +2 -1
- package/build/src/health_check.d.ts +3 -2
- package/build/src/types.d.ts +2 -1
- package/build/{types-C0rDwbry.d.ts → types-BnOi46bE.d.ts} +19 -1
- package/package.json +1 -1
- package/build/chunk-4UPUCRVG.js.map +0 -1
|
@@ -2,12 +2,15 @@ import {
|
|
|
2
2
|
ChannelRouter,
|
|
3
3
|
PresenceManager,
|
|
4
4
|
SocketService
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-NRWRLZAO.js";
|
|
6
6
|
import "../chunk-XUDFDJME.js";
|
|
7
7
|
import "../chunk-GKAD2UOA.js";
|
|
8
8
|
|
|
9
9
|
// providers/socket_provider.ts
|
|
10
10
|
import { ServerResponse } from "http";
|
|
11
|
+
function isMiddlewareClass(middleware) {
|
|
12
|
+
return typeof middleware === "function" && "handle" in middleware.prototype;
|
|
13
|
+
}
|
|
11
14
|
var SocketProvider = class {
|
|
12
15
|
constructor(app) {
|
|
13
16
|
this.app = app;
|
|
@@ -78,26 +81,58 @@ var SocketProvider = class {
|
|
|
78
81
|
* Creates an AdonisJS HTTP context for WebSocket upgrade requests.
|
|
79
82
|
*/
|
|
80
83
|
#withHttpContextFactory(config, server) {
|
|
81
|
-
|
|
84
|
+
const middleware = config.websocket?.middleware ?? [];
|
|
85
|
+
if (!config.websocket?.authenticate && middleware.length === 0) {
|
|
82
86
|
return config;
|
|
83
87
|
}
|
|
88
|
+
const createContext = config.websocket?.createContext;
|
|
84
89
|
return {
|
|
85
90
|
...config,
|
|
86
91
|
websocket: {
|
|
87
92
|
...config.websocket,
|
|
88
|
-
|
|
93
|
+
createContextOnUpgrade: middleware.length > 0,
|
|
94
|
+
createContext: async (request) => {
|
|
95
|
+
if (createContext) {
|
|
96
|
+
const httpContext2 = await createContext(request);
|
|
97
|
+
await this.#runUpgradeMiddleware(server, middleware, httpContext2);
|
|
98
|
+
return httpContext2;
|
|
99
|
+
}
|
|
89
100
|
const response = new ServerResponse(request);
|
|
90
101
|
const adonisRequest = server.createRequest(request, response);
|
|
91
102
|
const adonisResponse = server.createResponse(request, response);
|
|
92
|
-
|
|
103
|
+
const httpContext = server.createHttpContext(
|
|
93
104
|
adonisRequest,
|
|
94
105
|
adonisResponse,
|
|
95
106
|
this.app.container.createResolver()
|
|
96
107
|
);
|
|
108
|
+
await this.#runUpgradeMiddleware(server, middleware, httpContext);
|
|
109
|
+
return httpContext;
|
|
97
110
|
}
|
|
98
111
|
}
|
|
99
112
|
};
|
|
100
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Runs middleware attached to WebSocket upgrade requests.
|
|
116
|
+
*/
|
|
117
|
+
async #runUpgradeMiddleware(server, middleware, httpContext) {
|
|
118
|
+
if (middleware.length === 0) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const ctx = httpContext;
|
|
122
|
+
const middlewareClasses = await Promise.all(
|
|
123
|
+
middleware.map(async (one) => {
|
|
124
|
+
if (isMiddlewareClass(one)) {
|
|
125
|
+
return one;
|
|
126
|
+
}
|
|
127
|
+
const moduleExports = await one();
|
|
128
|
+
return moduleExports.default;
|
|
129
|
+
})
|
|
130
|
+
);
|
|
131
|
+
await server.pipeline(middlewareClasses).errorHandler((error) => {
|
|
132
|
+
throw error;
|
|
133
|
+
}).finalHandler(async () => {
|
|
134
|
+
}).run(ctx);
|
|
135
|
+
}
|
|
101
136
|
/**
|
|
102
137
|
* Scans and registers channel classes.
|
|
103
138
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../providers/socket_provider.ts"],"sourcesContent":["import type { ApplicationService } from '@adonisjs/core/types'\nimport type { Server as AdonisServer } from '@adonisjs/core/http'\nimport type { Logger } from '@adonisjs/logger'\nimport { ServerResponse } from 'node:http'\nimport { SocketService } from '../src/socket_service.js'\nimport { ChannelRouter } from '../src/channel_router.js'\nimport { PresenceManager } from '../src/presence_manager.js'\nimport type { SocketConfig } from '../src/types.js'\n\ndeclare module '@adonisjs/core/types' {\n interface ContainerBindings {\n 'socket': SocketService\n 'socket.router': ChannelRouter\n 'socket.presence': PresenceManager\n }\n}\n\nexport default class SocketProvider {\n constructor(protected app: ApplicationService) {}\n\n /**\n * Registers the bindings in the container.\n */\n register(): void {\n this.app.container.singleton('socket.router', () => {\n return new ChannelRouter()\n })\n\n this.app.container.singleton('socket.presence', () => {\n return new PresenceManager()\n })\n\n this.app.container.singleton('socket', () => {\n return new SocketService()\n })\n }\n\n /**\n * Boots the provider.\n */\n async boot(): Promise<void> {\n const router = await this.app.container.make('socket.router')\n\n // Discover and register channel classes.\n await this.#registerChannels(router)\n }\n\n /**\n * Called when the app is ready and the HTTP server is running.\n */\n async ready(): Promise<void> {\n const socket = await this.app.container.make('socket')\n const router = await this.app.container.make('socket.router')\n const presence = await this.app.container.make('socket.presence')\n const logger = await this.#resolveLogger()\n const config = this.app.config.get<SocketConfig>('socket', {})\n\n socket.setPresenceManager(presence)\n\n logger.info('router has %d channels registered', router.size)\n\n const server = await this.app.container.make('server')\n const httpServer = server.getNodeServer()\n\n if (!httpServer) {\n const error = new Error('HTTP server not available.')\n socket.markFailed(error)\n logger.warn('HTTP server not available; socket server was not started')\n return\n }\n\n try {\n await socket.boot(httpServer, this.#withHttpContextFactory(config, server), router, logger)\n logger.info('server started')\n } catch (error) {\n socket.markFailed(error)\n throw error\n }\n }\n\n /**\n * Performs a clean shutdown.\n */\n async shutdown(): Promise<void> {\n const socket = await this.app.container.make('socket')\n await socket.close()\n }\n\n /**\n * Resolves the AdonisJS logger and creates a child logger for the socket context.\n */\n async #resolveLogger(): Promise<Logger> {\n const logger = await this.app.container.make('logger')\n return logger.child({ module: 'socket' })\n }\n\n /**\n * Creates an AdonisJS HTTP context for WebSocket upgrade requests.\n */\n #withHttpContextFactory(config: SocketConfig, server: AdonisServer): SocketConfig {\n if (!config.websocket?.authenticate
|
|
1
|
+
{"version":3,"sources":["../../providers/socket_provider.ts"],"sourcesContent":["import type { ApplicationService } from '@adonisjs/core/types'\nimport type { HttpContext, Server as AdonisServer } from '@adonisjs/core/http'\nimport type { Logger } from '@adonisjs/logger'\nimport type { MiddlewareAsClass } from '@adonisjs/core/types/http'\nimport { ServerResponse } from 'node:http'\nimport { SocketService } from '../src/socket_service.js'\nimport { ChannelRouter } from '../src/channel_router.js'\nimport { PresenceManager } from '../src/presence_manager.js'\nimport type { SocketConfig, SocketHttpMiddleware } from '../src/types.js'\n\nfunction isMiddlewareClass(middleware: SocketHttpMiddleware): middleware is MiddlewareAsClass {\n return typeof middleware === 'function' && 'handle' in middleware.prototype\n}\n\ndeclare module '@adonisjs/core/types' {\n interface ContainerBindings {\n 'socket': SocketService\n 'socket.router': ChannelRouter\n 'socket.presence': PresenceManager\n }\n}\n\nexport default class SocketProvider {\n constructor(protected app: ApplicationService) {}\n\n /**\n * Registers the bindings in the container.\n */\n register(): void {\n this.app.container.singleton('socket.router', () => {\n return new ChannelRouter()\n })\n\n this.app.container.singleton('socket.presence', () => {\n return new PresenceManager()\n })\n\n this.app.container.singleton('socket', () => {\n return new SocketService()\n })\n }\n\n /**\n * Boots the provider.\n */\n async boot(): Promise<void> {\n const router = await this.app.container.make('socket.router')\n\n // Discover and register channel classes.\n await this.#registerChannels(router)\n }\n\n /**\n * Called when the app is ready and the HTTP server is running.\n */\n async ready(): Promise<void> {\n const socket = await this.app.container.make('socket')\n const router = await this.app.container.make('socket.router')\n const presence = await this.app.container.make('socket.presence')\n const logger = await this.#resolveLogger()\n const config = this.app.config.get<SocketConfig>('socket', {})\n\n socket.setPresenceManager(presence)\n\n logger.info('router has %d channels registered', router.size)\n\n const server = await this.app.container.make('server')\n const httpServer = server.getNodeServer()\n\n if (!httpServer) {\n const error = new Error('HTTP server not available.')\n socket.markFailed(error)\n logger.warn('HTTP server not available; socket server was not started')\n return\n }\n\n try {\n await socket.boot(httpServer, this.#withHttpContextFactory(config, server), router, logger)\n logger.info('server started')\n } catch (error) {\n socket.markFailed(error)\n throw error\n }\n }\n\n /**\n * Performs a clean shutdown.\n */\n async shutdown(): Promise<void> {\n const socket = await this.app.container.make('socket')\n await socket.close()\n }\n\n /**\n * Resolves the AdonisJS logger and creates a child logger for the socket context.\n */\n async #resolveLogger(): Promise<Logger> {\n const logger = await this.app.container.make('logger')\n return logger.child({ module: 'socket' })\n }\n\n /**\n * Creates an AdonisJS HTTP context for WebSocket upgrade requests.\n */\n #withHttpContextFactory(config: SocketConfig, server: AdonisServer): SocketConfig {\n const middleware = config.websocket?.middleware ?? []\n\n if (!config.websocket?.authenticate && middleware.length === 0) {\n return config\n }\n\n const createContext = config.websocket?.createContext\n\n return {\n ...config,\n websocket: {\n ...config.websocket,\n createContextOnUpgrade: middleware.length > 0,\n createContext: async (request) => {\n if (createContext) {\n const httpContext = await createContext(request)\n await this.#runUpgradeMiddleware(server, middleware, httpContext)\n return httpContext\n }\n\n const response = new ServerResponse(request)\n const adonisRequest = server.createRequest(request, response)\n const adonisResponse = server.createResponse(request, response)\n\n const httpContext = server.createHttpContext(\n adonisRequest,\n adonisResponse,\n this.app.container.createResolver()\n )\n\n await this.#runUpgradeMiddleware(server, middleware, httpContext)\n\n return httpContext\n },\n },\n }\n }\n\n /**\n * Runs middleware attached to WebSocket upgrade requests.\n */\n async #runUpgradeMiddleware(\n server: AdonisServer,\n middleware: NonNullable<NonNullable<SocketConfig['websocket']>['middleware']>,\n httpContext: unknown\n ): Promise<void> {\n if (middleware.length === 0) {\n return\n }\n\n const ctx = httpContext as HttpContext\n const middlewareClasses = await Promise.all(\n middleware.map(async (one) => {\n if (isMiddlewareClass(one)) {\n return one\n }\n\n const moduleExports = await one()\n return moduleExports.default\n })\n )\n\n await server\n .pipeline(middlewareClasses as MiddlewareAsClass[])\n .errorHandler((error) => {\n throw error\n })\n .finalHandler(async () => {})\n .run(ctx)\n }\n\n /**\n * Scans and registers channel classes.\n */\n async #registerChannels(router: ChannelRouter): Promise<void> {\n const logger = await this.#resolveLogger()\n const { glob } = await import('node:fs/promises')\n const { resolve } = await import('node:path')\n\n const config = this.app.config.get<SocketConfig>('socket', {})\n const patterns = config.channels?.patterns ?? ['app/channels/**/*_channel.{ts,js}']\n\n for (const channelPattern of patterns) {\n const pattern = this.app.makePath(channelPattern)\n\n for await (const file of glob(pattern)) {\n try {\n const absolutePath = resolve(file)\n const module = await import(`file://${absolutePath}`)\n const channelClass = module.default\n\n if (channelClass?.pattern) {\n router.register(channelClass)\n logger.info('registered channel: %s', channelClass.pattern)\n }\n } catch (error) {\n logger.warn('failed to load channel from %s: %s', file, error)\n }\n }\n }\n\n logger.info('total channels registered: %d', router.size)\n }\n}\n"],"mappings":"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","names":["httpContext"]}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { S as SocketService } from '../socket_service-
|
|
1
|
+
import { S as SocketService } from '../socket_service-BN1g3ENk.js';
|
|
2
2
|
import 'node:http';
|
|
3
3
|
import '@adonisjs/logger';
|
|
4
4
|
import 'emittery';
|
|
5
5
|
import 'ws';
|
|
6
|
-
import '../types-
|
|
6
|
+
import '../types-BnOi46bE.js';
|
|
7
|
+
import '@adonisjs/core/types/http';
|
|
7
8
|
import '@boringnode/bus/types/main';
|
|
8
9
|
import '../shared_types-Dw9AphfO.js';
|
|
9
10
|
|
|
@@ -2,7 +2,7 @@ import { Server } from 'node:http';
|
|
|
2
2
|
import { Logger } from '@adonisjs/logger';
|
|
3
3
|
import Emittery from 'emittery';
|
|
4
4
|
import { WebSocketServer } from 'ws';
|
|
5
|
-
import { B as BaseChannel, b as ChannelMatch, A as AuthenticatedSocket, C as ChannelEventMap, c as PresenceData, d as ChannelOptions, S as SocketEvents, P as PresenceManager, e as SocketConfig, f as SocketServiceStatus, g as SocketHealthSnapshot } from './types-
|
|
5
|
+
import { B as BaseChannel, b as ChannelMatch, A as AuthenticatedSocket, C as ChannelEventMap, c as PresenceData, d as ChannelOptions, S as SocketEvents, P as PresenceManager, e as SocketConfig, f as SocketServiceStatus, g as SocketHealthSnapshot } from './types-BnOi46bE.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Matches channel names against registered patterns.
|
|
@@ -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":"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","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"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { C as ChannelEventMap, a as ChannelHandlerMap } from '../types-
|
|
1
|
+
import { C as ChannelEventMap, a as ChannelHandlerMap } from '../types-BnOi46bE.js';
|
|
2
2
|
import 'ws';
|
|
3
3
|
import 'node:http';
|
|
4
|
+
import '@adonisjs/core/types/http';
|
|
4
5
|
import '@boringnode/bus/types/main';
|
|
5
6
|
import '../shared_types-Dw9AphfO.js';
|
|
6
7
|
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { S as SocketService } from '../socket_service-
|
|
2
|
-
import '../types-
|
|
1
|
+
import { S as SocketService } from '../socket_service-BN1g3ENk.js';
|
|
2
|
+
import '../types-BnOi46bE.js';
|
|
3
3
|
import { BaseCheck, Result } from '@adonisjs/core/health';
|
|
4
4
|
import '../shared_types-Dw9AphfO.js';
|
|
5
5
|
import 'node:http';
|
|
6
6
|
import '@adonisjs/logger';
|
|
7
7
|
import 'emittery';
|
|
8
8
|
import 'ws';
|
|
9
|
+
import '@adonisjs/core/types/http';
|
|
9
10
|
import '@boringnode/bus/types/main';
|
|
10
11
|
|
|
11
12
|
declare class SocketHealthCheck extends BaseCheck {
|
package/build/src/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export { A as AuthenticatedSocket, C as ChannelEventMap, h as ChannelHandlerFn, a as ChannelHandlerMap, b as ChannelMatch, d as ChannelOptions, M as Middleware, i as MiddlewareClass, j as MiddlewareContext, k as MiddlewareFn, l as PresenceMember, R as RawSocket, m as SocketAuthenticationDetails, n as SocketAuthenticationHandler, o as SocketAuthenticationResult, e as SocketConfig, S as SocketEvents, g as SocketHealthSnapshot, p as SocketHttpContextFactory, f as SocketServiceStatus,
|
|
1
|
+
export { A as AuthenticatedSocket, C as ChannelEventMap, h as ChannelHandlerFn, a as ChannelHandlerMap, b as ChannelMatch, d as ChannelOptions, M as Middleware, i as MiddlewareClass, j as MiddlewareContext, k as MiddlewareFn, l as PresenceMember, R as RawSocket, m as SocketAuthenticationDetails, n as SocketAuthenticationHandler, o as SocketAuthenticationResult, e as SocketConfig, S as SocketEvents, g as SocketHealthSnapshot, p as SocketHttpContextFactory, q as SocketHttpMiddleware, f as SocketServiceStatus, r as SocketUpgradeContext } from '../types-BnOi46bE.js';
|
|
2
2
|
import 'ws';
|
|
3
3
|
import 'node:http';
|
|
4
|
+
import '@adonisjs/core/types/http';
|
|
4
5
|
export { C as ChannelAck, a as ChannelMessage } from '../shared_types-Dw9AphfO.js';
|
|
5
6
|
import '@boringnode/bus/types/main';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { WebSocket } from 'ws';
|
|
2
2
|
import { IncomingMessage } from 'node:http';
|
|
3
|
+
import { MiddlewareAsClass } from '@adonisjs/core/types/http';
|
|
3
4
|
import { TransportFactory } from '@boringnode/bus/types/main';
|
|
4
5
|
import './shared_types-Dw9AphfO.js';
|
|
5
6
|
|
|
@@ -278,6 +279,12 @@ interface SocketAuthenticationDetails<User = unknown> {
|
|
|
278
279
|
type SocketAuthenticationResult<User = unknown> = SocketAuthenticationDetails<User> | false | null;
|
|
279
280
|
type SocketAuthenticationHandler<User = unknown, HttpContext = unknown> = (ctx: SocketUpgradeContext<HttpContext>) => SocketAuthenticationResult<User> | Promise<SocketAuthenticationResult<User>>;
|
|
280
281
|
type SocketHttpContextFactory<HttpContext = unknown> = (request: IncomingMessage) => HttpContext | Promise<HttpContext>;
|
|
282
|
+
/**
|
|
283
|
+
* AdonisJS HTTP middleware executed for the WebSocket upgrade request.
|
|
284
|
+
*/
|
|
285
|
+
type SocketHttpMiddleware = (() => Promise<{
|
|
286
|
+
default: MiddlewareAsClass;
|
|
287
|
+
}>) | MiddlewareAsClass;
|
|
281
288
|
/**
|
|
282
289
|
* Result of matching a channel.
|
|
283
290
|
*/
|
|
@@ -342,6 +349,17 @@ interface SocketConfig<User = unknown, HttpContext = unknown> {
|
|
|
342
349
|
websocket?: {
|
|
343
350
|
path?: string;
|
|
344
351
|
createContext?: SocketHttpContextFactory<HttpContext>;
|
|
352
|
+
/**
|
|
353
|
+
* HTTP middleware executed only for the WebSocket upgrade request.
|
|
354
|
+
*
|
|
355
|
+
* Use this to initialize request-scoped services such as session or auth before
|
|
356
|
+
* websocket.authenticate. Channel middleware should be declared on channel classes.
|
|
357
|
+
*/
|
|
358
|
+
middleware?: SocketHttpMiddleware[];
|
|
359
|
+
/**
|
|
360
|
+
* @internal
|
|
361
|
+
*/
|
|
362
|
+
createContextOnUpgrade?: boolean;
|
|
345
363
|
authenticate?: SocketAuthenticationHandler<User, HttpContext>;
|
|
346
364
|
/**
|
|
347
365
|
* Sends a WebSocket ping every configured number of milliseconds.
|
|
@@ -390,4 +408,4 @@ interface SocketEvents<User = unknown, HttpContext = unknown> {
|
|
|
390
408
|
};
|
|
391
409
|
}
|
|
392
410
|
|
|
393
|
-
export { type AuthenticatedSocket as A, BaseChannel as B, type ChannelEventMap as C, type Middleware as M, PresenceManager as P, type RawSocket as R, type SocketEvents as S, type ChannelHandlerMap as a, type ChannelMatch as b, type PresenceData as c, type ChannelOptions as d, type SocketConfig as e, type SocketServiceStatus as f, type SocketHealthSnapshot as g, type ChannelHandlerFn as h, type MiddlewareClass as i, type MiddlewareContext as j, type MiddlewareFn as k, type PresenceMember as l, type SocketAuthenticationDetails as m, type SocketAuthenticationHandler as n, type SocketAuthenticationResult as o, type SocketHttpContextFactory as p, type
|
|
411
|
+
export { type AuthenticatedSocket as A, BaseChannel as B, type ChannelEventMap as C, type Middleware as M, PresenceManager as P, type RawSocket as R, type SocketEvents as S, type ChannelHandlerMap as a, type ChannelMatch as b, type PresenceData as c, type ChannelOptions as d, type SocketConfig as e, type SocketServiceStatus as f, type SocketHealthSnapshot as g, type ChannelHandlerFn as h, type MiddlewareClass as i, type MiddlewareContext as j, type MiddlewareFn as k, type PresenceMember as l, type SocketAuthenticationDetails as m, type SocketAuthenticationHandler as n, type SocketAuthenticationResult as o, type SocketHttpContextFactory as p, type SocketHttpMiddleware as q, type SocketUpgradeContext as r };
|