@rlanz/socket 0.0.1-3 → 0.0.1-4
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 +48 -27
- package/build/{chunk-NRWRLZAO.js → chunk-ILDK672E.js} +180 -53
- package/build/chunk-ILDK672E.js.map +1 -0
- package/build/index.d.ts +16 -2
- package/build/index.js +20 -2
- package/build/index.js.map +1 -1
- package/build/providers/socket_provider.d.ts +2 -2
- package/build/providers/socket_provider.js +1 -1
- package/build/services/socket.d.ts +2 -2
- package/build/{socket_service-BN1g3ENk.d.ts → socket_service-CRmPa74K.d.ts} +2 -2
- package/build/src/client/index.js +0 -5
- package/build/src/client/index.js.map +1 -1
- package/build/src/client/types.d.ts +0 -5
- package/build/src/decorators.d.ts +1 -1
- package/build/src/health_check.d.ts +3 -3
- package/build/src/types.d.ts +1 -1
- package/build/{types-BnOi46bE.d.ts → types-BBLNfcWk.d.ts} +35 -15
- package/package.json +3 -3
- package/build/chunk-NRWRLZAO.js.map +0 -1
|
@@ -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\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
|
+
{"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 #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.#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 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"]}
|
|
@@ -24,11 +24,6 @@ interface SocketOptions {
|
|
|
24
24
|
* Maximum automatic reconnection delay in ms. Defaults to 5000.
|
|
25
25
|
*/
|
|
26
26
|
reconnectMaxDelay?: number;
|
|
27
|
-
/**
|
|
28
|
-
* Authentication data sent during the WebSocket handshake query string.
|
|
29
|
-
* Ex: { token: 'oat_MjE...' }
|
|
30
|
-
*/
|
|
31
|
-
auth?: Record<string, unknown>;
|
|
32
27
|
}
|
|
33
28
|
/**
|
|
34
29
|
* Channel subscription options.
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { S as SocketService } from '../socket_service-BN1g3ENk.js';
|
|
2
|
-
import '../types-BnOi46bE.js';
|
|
3
1
|
import { BaseCheck, Result } from '@adonisjs/core/health';
|
|
4
|
-
import '../
|
|
2
|
+
import { e as SocketService } from '../socket_service-CRmPa74K.js';
|
|
5
3
|
import 'node:http';
|
|
6
4
|
import '@adonisjs/logger';
|
|
7
5
|
import 'emittery';
|
|
8
6
|
import 'ws';
|
|
7
|
+
import '../types-BBLNfcWk.js';
|
|
9
8
|
import '@adonisjs/core/types/http';
|
|
10
9
|
import '@boringnode/bus/types/main';
|
|
10
|
+
import '../shared_types-Dw9AphfO.js';
|
|
11
11
|
|
|
12
12
|
declare class SocketHealthCheck extends BaseCheck {
|
|
13
13
|
private socket;
|
package/build/src/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as AuthenticatedSocket, C as ChannelEventMap,
|
|
1
|
+
export { A as AuthenticatedSocket, C as ChannelEventMap, b as ChannelHandlerFn, c as ChannelHandlerMap, d as ChannelMatch, e as ChannelOptions, D as Duration, M as Middleware, f as MiddlewareClass, g as MiddlewareContext, h as MiddlewareFn, i as PresenceMember, R as RawSocket, a as SocketAuthenticationHandler, j as SocketAuthenticationResult, S as SocketConfig, k as SocketEvents, l as SocketHealthSnapshot, m as SocketHttpContextFactory, n as SocketHttpMiddleware, o as SocketServiceStatus, p as SocketUpgradeContext } from '../types-BBLNfcWk.js';
|
|
2
2
|
import 'ws';
|
|
3
3
|
import 'node:http';
|
|
4
4
|
import '@adonisjs/core/types/http';
|
|
@@ -215,16 +215,23 @@ declare class BaseChannel<User = unknown, Events extends ChannelEventMap = Chann
|
|
|
215
215
|
$getPresenceInfo(socket: AuthenticatedSocket<User, HttpContext>): PresenceMember;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
/**
|
|
219
|
+
* Duration can be specified as milliseconds or as a human-readable string.
|
|
220
|
+
*/
|
|
221
|
+
type Duration = number | string;
|
|
222
|
+
|
|
218
223
|
interface SocketTransportConfig {
|
|
219
224
|
driver: TransportFactory;
|
|
220
225
|
channel?: string;
|
|
221
|
-
|
|
226
|
+
/**
|
|
227
|
+
* How long to wait for other instances to answer a presence snapshot request.
|
|
228
|
+
*/
|
|
229
|
+
presenceTimeout?: Duration;
|
|
222
230
|
}
|
|
223
231
|
|
|
224
232
|
interface RawSocket<HttpContext = unknown> {
|
|
225
233
|
id: string;
|
|
226
234
|
data: Record<string, unknown>;
|
|
227
|
-
auth?: Record<string, unknown>;
|
|
228
235
|
connection: WebSocket;
|
|
229
236
|
request: IncomingMessage;
|
|
230
237
|
httpContext?: HttpContext;
|
|
@@ -267,16 +274,9 @@ interface AuthenticatedSocket<User = unknown, HttpContext = unknown> {
|
|
|
267
274
|
raw: RawSocket<HttpContext>;
|
|
268
275
|
}
|
|
269
276
|
interface SocketUpgradeContext<HttpContext = unknown> {
|
|
270
|
-
request: IncomingMessage;
|
|
271
|
-
auth?: Record<string, unknown>;
|
|
272
277
|
httpContext?: HttpContext;
|
|
273
278
|
}
|
|
274
|
-
|
|
275
|
-
user?: User;
|
|
276
|
-
auth?: Record<string, unknown>;
|
|
277
|
-
data?: Record<string, unknown>;
|
|
278
|
-
}
|
|
279
|
-
type SocketAuthenticationResult<User = unknown> = SocketAuthenticationDetails<User> | false | null;
|
|
279
|
+
type SocketAuthenticationResult<User = unknown> = User | false | null;
|
|
280
280
|
type SocketAuthenticationHandler<User = unknown, HttpContext = unknown> = (ctx: SocketUpgradeContext<HttpContext>) => SocketAuthenticationResult<User> | Promise<SocketAuthenticationResult<User>>;
|
|
281
281
|
type SocketHttpContextFactory<HttpContext = unknown> = (request: IncomingMessage) => HttpContext | Promise<HttpContext>;
|
|
282
282
|
/**
|
|
@@ -362,17 +362,37 @@ interface SocketConfig<User = unknown, HttpContext = unknown> {
|
|
|
362
362
|
createContextOnUpgrade?: boolean;
|
|
363
363
|
authenticate?: SocketAuthenticationHandler<User, HttpContext>;
|
|
364
364
|
/**
|
|
365
|
-
* Sends a WebSocket ping every configured
|
|
365
|
+
* Sends a WebSocket ping every configured duration.
|
|
366
366
|
*
|
|
367
367
|
* When only pingTimeout is configured, the service uses a default 25s interval.
|
|
368
368
|
*/
|
|
369
|
-
pingInterval?:
|
|
369
|
+
pingInterval?: Duration;
|
|
370
370
|
/**
|
|
371
|
-
* Terminates connections that do not answer a ping within the configured
|
|
371
|
+
* Terminates connections that do not answer a ping within the configured duration.
|
|
372
372
|
*
|
|
373
373
|
* When only pingInterval is configured, the service uses a default 5s timeout.
|
|
374
374
|
*/
|
|
375
|
-
pingTimeout?:
|
|
375
|
+
pingTimeout?: Duration;
|
|
376
|
+
/**
|
|
377
|
+
* Maximum inbound WebSocket message payload size in bytes.
|
|
378
|
+
*/
|
|
379
|
+
maxPayload?: number;
|
|
380
|
+
/**
|
|
381
|
+
* Maximum unresolved non-ping protocol messages per socket.
|
|
382
|
+
*/
|
|
383
|
+
maxQueuedMessages?: number;
|
|
384
|
+
/**
|
|
385
|
+
* Maximum inbound protocol messages accepted per rate limit interval.
|
|
386
|
+
*/
|
|
387
|
+
maxMessagesPerInterval?: number;
|
|
388
|
+
/**
|
|
389
|
+
* Duration of the inbound protocol message rate limit window.
|
|
390
|
+
*/
|
|
391
|
+
messageRateInterval?: Duration;
|
|
392
|
+
/**
|
|
393
|
+
* Maximum buffered outbound WebSocket bytes accepted before the service closes the socket.
|
|
394
|
+
*/
|
|
395
|
+
maxBufferedAmount?: number;
|
|
376
396
|
};
|
|
377
397
|
/**
|
|
378
398
|
* Optional transport used to synchronize broadcasts between SocketService instances.
|
|
@@ -408,4 +428,4 @@ interface SocketEvents<User = unknown, HttpContext = unknown> {
|
|
|
408
428
|
};
|
|
409
429
|
}
|
|
410
430
|
|
|
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
|
|
431
|
+
export { type AuthenticatedSocket as A, BaseChannel as B, type ChannelEventMap as C, type Duration as D, type Middleware as M, PresenceManager as P, type RawSocket as R, type SocketConfig as S, type SocketAuthenticationHandler as a, type ChannelHandlerFn as b, type ChannelHandlerMap as c, type ChannelMatch as d, type ChannelOptions as e, type MiddlewareClass as f, type MiddlewareContext as g, type MiddlewareFn as h, type PresenceMember as i, type SocketAuthenticationResult as j, type SocketEvents as k, type SocketHealthSnapshot as l, type SocketHttpContextFactory as m, type SocketHttpMiddleware as n, type SocketServiceStatus as o, type SocketUpgradeContext as p, type PresenceData as q };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rlanz/socket",
|
|
3
|
-
"version": "0.0.1-
|
|
3
|
+
"version": "0.0.1-4",
|
|
4
4
|
"description": "WebSocket integration for AdonisJS",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./build/index.js",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@boringnode/bus": "^0.9.0",
|
|
44
|
+
"@lukeed/ms": "^2.0.2",
|
|
44
45
|
"@poppinss/matchit": "^3.2.0",
|
|
45
46
|
"emittery": "^1.2.0",
|
|
46
47
|
"ws": "^8.21.0"
|
|
@@ -100,6 +101,5 @@
|
|
|
100
101
|
"publishConfig": {
|
|
101
102
|
"access": "public",
|
|
102
103
|
"tag": "latest"
|
|
103
|
-
}
|
|
104
|
-
"packageManager": "yarn@4.11.0"
|
|
104
|
+
}
|
|
105
105
|
}
|