@rlanz/socket 0.1.0 → 0.2.0
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 +9 -5
- package/build/{base_channel-CasBQV5I.d.ts → base_channel-DFyd3hVE.d.ts} +0 -3
- package/build/{chunk-DR6GWQUW.js → chunk-3FY7EIZF.js} +11 -2
- package/build/{chunk-DR6GWQUW.js.map → chunk-3FY7EIZF.js.map} +1 -1
- package/build/{chunk-UE75J3JE.js → chunk-OAI6SAER.js} +2 -2
- package/build/index.d.ts +2 -3
- package/build/providers/socket_provider.d.ts +2 -2
- package/build/providers/socket_provider.js +8 -11
- package/build/providers/socket_provider.js.map +1 -1
- package/build/services/socket.d.ts +2 -2
- package/build/{socket_service-C2YRbhrO.d.ts → socket_service-to3tlbrV.d.ts} +1 -1
- package/build/src/client/index.d.ts +1 -1
- package/build/src/client/index.js +1 -1
- package/build/src/client/react.js +2 -2
- package/build/src/client/types.d.ts +10 -1
- package/build/src/client/vue.js +2 -2
- package/build/src/decorators.d.ts +1 -2
- package/build/src/health_check.d.ts +2 -2
- package/build/src/otel.js +1 -1
- package/build/src/otel.js.map +1 -1
- package/build/src/types.d.ts +1 -2
- package/package.json +1 -1
- /package/build/{chunk-UE75J3JE.js.map → chunk-OAI6SAER.js.map} +0 -0
package/README.md
CHANGED
|
@@ -206,10 +206,8 @@ export default defineConfig({
|
|
|
206
206
|
Upgrade middleware and channel middleware have different jobs. Upgrade middleware prepares the HTTP
|
|
207
207
|
handshake. Channel middleware authorizes one subscription.
|
|
208
208
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
async-local storage. Put mutable socket-lifetime state in `socket.raw.data`. Copy durable identity
|
|
212
|
-
into `socket.user` instead of retaining response-bound resources.
|
|
209
|
+
Socket releases the upgrade request and HTTP context after authentication. Copy durable identity
|
|
210
|
+
into `socket.user`, and put mutable socket-lifetime state in `socket.raw.data`.
|
|
213
211
|
|
|
214
212
|
## Define channels
|
|
215
213
|
|
|
@@ -524,6 +522,10 @@ const socket = new Socket<AppSocket>({
|
|
|
524
522
|
autoReconnect: true,
|
|
525
523
|
reconnectDelay: 250,
|
|
526
524
|
reconnectMaxDelay: 5000,
|
|
525
|
+
onResubscribeError({ channel, error }) {
|
|
526
|
+
if (error.message === 'Session expired') return
|
|
527
|
+
console.error(`Could not resubscribe to ${channel}`, error)
|
|
528
|
+
},
|
|
527
529
|
})
|
|
528
530
|
|
|
529
531
|
socket.onStateChange((state) => {
|
|
@@ -565,7 +567,9 @@ socket.disconnect()
|
|
|
565
567
|
|
|
566
568
|
Automatic reconnection starts at `250ms` and doubles up to `5s`. After reconnecting, the client
|
|
567
569
|
subscribes again to desired channels. Pending acknowledgements fail and authentication and channel
|
|
568
|
-
middleware run again. Events sent while disconnected are not replayed.
|
|
570
|
+
middleware run again. Events sent while disconnected are not replayed. A rejected automatic
|
|
571
|
+
subscription calls `onResubscribeError` with the channel name and error. The client does not report
|
|
572
|
+
this protocol outcome as a global JavaScript error.
|
|
569
573
|
|
|
570
574
|
Set `autoReconnect: false` to disable retries. A deliberate server disconnect is terminal by
|
|
571
575
|
default. Use `shouldReconnect(closeEvent)` to override the decision for any close code or reason.
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { WebSocket } from 'ws';
|
|
2
|
-
import { IncomingMessage } from 'node:http';
|
|
3
2
|
import { MiddlewareAsClass } from '@adonisjs/core/types/http';
|
|
4
3
|
import { HttpContext } from '@adonisjs/core/http';
|
|
5
4
|
import { TransportFactory } from '@boringnode/bus/types/main';
|
|
@@ -104,8 +103,6 @@ interface RawSocket {
|
|
|
104
103
|
id: string;
|
|
105
104
|
data: Record<string, unknown>;
|
|
106
105
|
connection: WebSocket;
|
|
107
|
-
request: IncomingMessage;
|
|
108
|
-
httpContext: HttpContext;
|
|
109
106
|
}
|
|
110
107
|
/**
|
|
111
108
|
* Socket exposed to channels after the HTTP upgrade.
|
|
@@ -842,11 +842,13 @@ var Socket = class {
|
|
|
842
842
|
#url;
|
|
843
843
|
#path;
|
|
844
844
|
#session;
|
|
845
|
+
#onResubscribeError;
|
|
845
846
|
constructor(options = {}) {
|
|
846
847
|
const browserWindow = globalThis;
|
|
847
848
|
const browserOrigin = typeof globalThis === "object" && browserWindow.window?.location?.origin ? browserWindow.window.location.origin : "";
|
|
848
849
|
this.#url = options.url ?? browserOrigin;
|
|
849
850
|
this.#path = options.path ?? "/socket";
|
|
851
|
+
this.#onResubscribeError = options.onResubscribeError;
|
|
850
852
|
this.#session = new ClientSocketSession({
|
|
851
853
|
buildUrl: () => this.#buildUrl(),
|
|
852
854
|
autoReconnect: options.autoReconnect,
|
|
@@ -927,7 +929,14 @@ var Socket = class {
|
|
|
927
929
|
async #resubscribeChannels() {
|
|
928
930
|
for (const channel of this.#channels.values()) {
|
|
929
931
|
if (channel.subscribed && !channel.active) {
|
|
930
|
-
await channel.$resubscribe().catch(
|
|
932
|
+
await channel.$resubscribe().catch((error) => {
|
|
933
|
+
if (this.#onResubscribeError) {
|
|
934
|
+
callClientHandler(this.#onResubscribeError, {
|
|
935
|
+
channel: channel.name,
|
|
936
|
+
error: error instanceof Error ? error : new Error("Resubscribe failed")
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
});
|
|
931
940
|
}
|
|
932
941
|
}
|
|
933
942
|
}
|
|
@@ -939,4 +948,4 @@ export {
|
|
|
939
948
|
buildChannelName,
|
|
940
949
|
Socket
|
|
941
950
|
};
|
|
942
|
-
//# sourceMappingURL=chunk-
|
|
951
|
+
//# sourceMappingURL=chunk-3FY7EIZF.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client/callback.ts","../src/client/presence_snapshot.ts","../src/client/channel.ts","../src/client/socket_session.ts","../src/client/socket.ts"],"sourcesContent":["type Callable = (...args: never[]) => unknown\n\nexport function reportClientError(error: unknown): void {\n try {\n const reportError = (\n globalThis as typeof globalThis & { reportError?: (error: unknown) => void }\n ).reportError\n\n if (typeof reportError === 'function') {\n reportError(error)\n } else {\n console.error(error)\n }\n } catch {\n // Error reporting must never affect socket state or create another rejected promise.\n }\n}\n\nexport function callClientHandler(handler: Callable, data?: unknown): void {\n try {\n const result = Reflect.apply(handler, undefined, [data])\n if (result && typeof (result as PromiseLike<unknown>).then === 'function') {\n void Promise.resolve(result).catch(reportClientError)\n }\n } catch (error) {\n reportClientError(error)\n }\n}\n","import type { PresenceUser } from './types.js'\n\nexport interface ClientPresenceDiff {\n readonly joining: readonly PresenceUser[]\n readonly leaving: readonly PresenceUser[]\n}\n\n/**\n * Immutable, validated client view of one channel's presence.\n */\nexport class ClientPresenceSnapshot {\n readonly count: number\n readonly users: readonly PresenceUser[]\n readonly #usersById: ReadonlyMap<string, PresenceUser>\n\n private constructor(users: PresenceUser[]) {\n // Copy and freeze each user. Consumer code cannot change the snapshot.\n this.users = Object.freeze(users.map((user) => Object.freeze({ ...user }) as PresenceUser))\n this.count = this.users.length\n\n // Build the ID index once. Diff operations do not need a second channel state.\n this.#usersById = new Map(this.users.map((user) => [user.id, user]))\n }\n\n /**\n * Validate one transport snapshot for the expected channel.\n */\n static fromTransport(value: unknown, expectedChannel: string): ClientPresenceSnapshot | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return null\n }\n\n const data = value as Record<string, unknown>\n\n // The transport count must be an integer and must match the user list.\n if (\n data.channel !== expectedChannel ||\n !Array.isArray(data.users) ||\n !Number.isInteger(data.count) ||\n data.count !== data.users.length\n ) {\n return null\n }\n\n const users: PresenceUser[] = []\n const userIds = new Set<string>()\n for (const candidate of data.users) {\n // Each user needs a unique ID and one transport timestamp.\n if (\n !candidate ||\n typeof candidate !== 'object' ||\n Array.isArray(candidate) ||\n typeof candidate.id !== 'string' ||\n typeof candidate.joinedAt !== 'string' ||\n userIds.has(candidate.id)\n ) {\n return null\n }\n userIds.add(candidate.id)\n users.push(candidate as PresenceUser)\n }\n\n return new ClientPresenceSnapshot(users)\n }\n\n /**\n * Return users that joined or left since the previous snapshot.\n */\n diff(previous: ClientPresenceSnapshot | null): ClientPresenceDiff {\n if (!previous) {\n return { joining: this.users, leaving: [] }\n }\n\n // Keep the order from the snapshot that owns each user.\n return {\n joining: this.users.filter((user) => !previous.#usersById.has(user.id)),\n leaving: previous.users.filter((user) => !this.#usersById.has(user.id)),\n }\n }\n}\n","import type {\n SocketClientTransport,\n SubscribeOptions,\n SubscribeResult,\n UnsubscribeOptions,\n PresenceUser,\n EventHandler,\n ChannelMessage,\n ChannelAck,\n ChannelContract,\n} from './types.js'\nimport { callClientHandler } from './callback.js'\nimport { ClientPresenceSnapshot } from './presence_snapshot.js'\n\nconst CLIENT_EVENT_PREFIX = 'client:'\n\ntype Callable = (...args: never[]) => unknown\n\n/**\n * Represents a subscribed channel.\n * Chainable API inspired by Laravel Echo.\n *\n * @example\n * ```ts\n * socket.channel('chat/:roomId', { roomId: '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 */\ntype ClientMap<C> = C extends ChannelContract<infer E, unknown> ? E : unknown\ntype ServerMap<C> = C extends ChannelContract<unknown, infer E> ? E : unknown\ntype IsUnknown<T> = unknown extends T ? true : false\ntype StringKey<T> = Extract<keyof T, string>\ntype EventDataArgs<Payload> = undefined extends Payload ? [data?: Payload] : [data: Payload]\n\ninterface PendingSubscribe<Contract> {\n promise: Promise<Channel<Contract>>\n resolve: (channel: Channel<Contract>) => void\n reject: (error: Error) => void\n}\n\ntype RemoteSynchronization<Contract> =\n | { status: 'idle' }\n | { status: 'subscribing'; pending: PendingSubscribe<Contract>; connected: boolean }\n | { status: 'active' }\n | { status: 'unsubscribing'; promise: Promise<void> }\n | { status: 'waitingForReconnect' }\n | { status: 'subscriptionUncertain' }\n | { status: 'remoteSubscriptionUncertain' }\n\nexport class Channel<Contract> {\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 #remoteSynchronization: RemoteSynchronization<Contract> = { status: 'idle' }\n\n /**\n * Event handlers grouped by event type.\n */\n #eventHandlers = new Map<string, Set<Callable>>()\n #managedEventHandlers = new Map<string, Set<Callable>>()\n\n /**\n * Current presence data.\n */\n #presence: ClientPresenceSnapshot | null = null\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 #managedSubscriptions = 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 || this.#managedSubscriptions > 0\n }\n\n /** Whether a framework lifecycle currently owns this channel. @internal */\n get $managed(): boolean {\n return this.#managedSubscriptions > 0\n }\n\n /**\n * Whether the channel is active on the server.\n */\n get active(): boolean {\n return this.#remoteSynchronization.status === '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>(\n this: IsUnknown<ServerMap<Contract>> extends true ? Channel<Contract> : never,\n event: string,\n handler: EventHandler<T>\n ): this\n listen<Event extends StringKey<ServerMap<Contract>>>(\n this: Channel<Contract>,\n event: Event,\n handler: EventHandler<ServerMap<Contract>[Event]>\n ): this\n listen(event: string, handler: EventHandler<any>): this {\n const handlers = this.#eventHandlers.get(event) ?? new Set<Callable>()\n handlers.add(handler)\n this.#eventHandlers.set(event, handlers)\n return this\n }\n\n /**\n * Removes an event listener.\n */\n stopListening<T = unknown>(\n this: IsUnknown<ServerMap<Contract>> extends true ? Channel<Contract> : never,\n event: string,\n handler?: EventHandler<T>\n ): this\n stopListening<Event extends StringKey<ServerMap<Contract>>>(\n this: Channel<Contract>,\n event: Event,\n handler?: EventHandler<ServerMap<Contract>[Event]>\n ): this\n stopListening(event: string, handler?: EventHandler<any>): 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 /** Registers a lifecycle-owned event handler. @internal */\n $listen(event: string, handler: EventHandler<any>): void {\n const handlers = this.#managedEventHandlers.get(event) ?? new Set<Callable>()\n handlers.add(handler)\n this.#managedEventHandlers.set(event, handlers)\n }\n\n /** Removes a lifecycle-owned event handler. @internal */\n $stopListening(event: string, handler: EventHandler<any>): void {\n const handlers = this.#managedEventHandlers.get(event)\n handlers?.delete(handler)\n if (handlers?.size === 0) this.#managedEventHandlers.delete(event)\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 const handlers =\n this.#eventHandlers.get(`${CLIENT_EVENT_PREFIX}${event}`) ?? new Set<Callable>()\n handlers.add(handler)\n this.#eventHandlers.set(`${CLIENT_EVENT_PREFIX}${event}`, handlers)\n return this\n }\n\n /**\n * Removes a whispered client event listener.\n */\n stopListeningForWhisper<T = unknown>(event: string, handler?: EventHandler<T>): this {\n const name = `${CLIENT_EVENT_PREFIX}${event}`\n if (handler) this.#eventHandlers.get(name)?.delete(handler)\n else this.#eventHandlers.delete(name)\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 return this.#ensureSubscribed(options)\n }\n\n /** Acquires subscription intent without overriding a direct consumer. @internal */\n $acquire(options: SubscribeOptions = {}): {\n ready: Promise<Channel<Contract>>\n release: () => Promise<void>\n } {\n this.#managedSubscriptions++\n const ready = this.#transport.connected\n ? this.#ensureSubscribed(options)\n : Promise.resolve(this)\n let released = false\n\n return {\n ready,\n release: () => {\n if (released) {\n return this.#remoteSynchronization.status === 'unsubscribing'\n ? this.#remoteSynchronization.promise\n : Promise.resolve()\n }\n released = true\n this.#managedSubscriptions--\n return this.#unsubscribeIfUnused()\n },\n }\n }\n\n /** Restores desired subscriptions after the transport reconnects. @internal */\n $resubscribe(options: SubscribeOptions = {}): Promise<this> {\n return this.#ensureSubscribed(options)\n }\n\n async #ensureSubscribed(options: SubscribeOptions): Promise<this> {\n if (this.#remoteSynchronization.status === 'unsubscribing') {\n await this.#remoteSynchronization.promise\n }\n\n if (!this.subscribed) return this\n\n if (this.#remoteSynchronization.status === 'subscribing') {\n await this.#remoteSynchronization.pending.promise\n return this\n }\n\n if (this.#remoteSynchronization.status === '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 (\n this.#remoteSynchronization.status === 'idle' ||\n this.#remoteSynchronization.status === 'remoteSubscriptionUncertain'\n ) {\n this.#setupSocketListeners()\n }\n\n const deferred = Promise.withResolvers<Channel<Contract>>()\n const synchronization: RemoteSynchronization<Contract> = {\n status: 'subscribing',\n pending: deferred,\n connected: true,\n }\n this.#remoteSynchronization = synchronization\n\n const timer = setTimeout(() => {\n if (this.#remoteSynchronization !== synchronization) return\n this.#remoteSynchronization = { status: 'waitingForReconnect' }\n this.#resetConnectionState()\n synchronization.pending.reject(new Error(`Subscribe timeout for channel: ${this.name}`))\n void this.#transport\n .sendRequest({ type: 'unsubscribe', channel: this.name }, timeout)\n .catch(() => {})\n }, timeout)\n\n this.#transport\n .sendRequest<SubscribeResult>({ type: 'subscribe', channel: this.name }, timeout)\n .then((response) => {\n if (this.#remoteSynchronization !== synchronization) return\n clearTimeout(timer)\n\n if (response.ok) {\n this.#remoteSynchronization = { status: 'active' }\n\n const presenceData = ClientPresenceSnapshot.fromTransport(\n response.data?.presenceData,\n this.name\n )\n\n // Apply the initial presence snapshot.\n if (presenceData) {\n this.#handleInitialPresence(presenceData)\n }\n\n synchronization.pending.resolve(this)\n } else {\n this.#remoteSynchronization = { status: 'waitingForReconnect' }\n this.#resetConnectionState()\n synchronization.pending.reject(new Error(response.error))\n }\n })\n .catch((error) => {\n if (this.#remoteSynchronization !== synchronization) return\n clearTimeout(timer)\n this.#remoteSynchronization = synchronization.connected\n ? { status: 'subscriptionUncertain' }\n : { status: 'waitingForReconnect' }\n this.#resetConnectionState()\n synchronization.pending.reject(\n error instanceof Error ? error : new Error('Subscribe failed')\n )\n })\n\n await deferred.promise\n return this\n }\n\n /**\n * Unsubscribes from the channel.\n */\n async unsubscribe(options: UnsubscribeOptions = {}): Promise<void> {\n this.#subscribed = false\n this.#clearUserCallbacks()\n return this.#unsubscribeIfUnused(options)\n }\n\n #unsubscribeIfUnused(options: UnsubscribeOptions = {}): Promise<void> {\n if (this.subscribed) return Promise.resolve()\n if (this.#remoteSynchronization.status === 'unsubscribing') {\n return this.#remoteSynchronization.promise\n }\n\n const previousSynchronization = this.#remoteSynchronization\n const hadPendingSubscribe = previousSynchronization.status === 'subscribing'\n if (previousSynchronization.status === 'subscribing') {\n previousSynchronization.pending.reject(new Error(`Unsubscribed from channel: ${this.name}`))\n }\n\n const remoteSubscriptionPossible =\n previousSynchronization.status === 'active' ||\n previousSynchronization.status === 'subscriptionUncertain' ||\n previousSynchronization.status === 'remoteSubscriptionUncertain'\n\n if (!hadPendingSubscribe && !remoteSubscriptionPossible) {\n this.#cleanupTerminal()\n return Promise.resolve()\n }\n\n const timeout = options.timeout ?? 5000\n const deferred = Promise.withResolvers<void>()\n const synchronization: RemoteSynchronization<Contract> = {\n status: 'unsubscribing',\n promise: deferred.promise,\n }\n this.#remoteSynchronization = synchronization\n let remoteMayRemain = true\n let terminalSynchronization: RemoteSynchronization<Contract> | null = null\n const timer = setTimeout(() => deferred.resolve(), timeout)\n\n this.#transport\n .sendRequest({ type: 'unsubscribe', channel: this.name }, timeout)\n .then(() => {\n remoteMayRemain = false\n clearTimeout(timer)\n deferred.resolve()\n if (this.#remoteSynchronization === terminalSynchronization) {\n this.#remoteSynchronization = { status: 'idle' }\n }\n })\n .catch(() => {\n clearTimeout(timer)\n deferred.resolve()\n })\n\n const pending = deferred.promise\n void pending.finally(() => {\n if (this.#remoteSynchronization !== synchronization) return\n terminalSynchronization = remoteMayRemain\n ? { status: 'remoteSubscriptionUncertain' }\n : { status: 'idle' }\n this.#remoteSynchronization = terminalSynchronization\n this.#resetConnectionState()\n this.#detachTransportListeners()\n if (!this.subscribed) this.#clearCallbacks()\n })\n return pending\n }\n\n /**\n * Sends an event to the server.\n */\n send<T = unknown>(\n this: IsUnknown<ClientMap<Contract>> extends true ? Channel<Contract> : never,\n event: string,\n data?: T\n ): this\n send<Event extends StringKey<ClientMap<Contract>>>(\n this: Channel<Contract>,\n event: Event,\n ...[data]: ClientMap<Contract>[Event] extends { payload: infer Payload }\n ? EventDataArgs<Payload>\n : never\n ): this\n send(event: string, data?: unknown): 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 sendWithAck<T = unknown, Result = unknown>(\n this: IsUnknown<ClientMap<Contract>> extends true ? Channel<Contract> : never,\n event: string,\n data?: T\n ): Promise<Result>\n sendWithAck<Event extends StringKey<ClientMap<Contract>>>(\n this: Channel<Contract>,\n event: Event,\n ...[data]: ClientMap<Contract>[Event] extends { payload: infer Payload }\n ? EventDataArgs<Payload>\n : never\n ): Promise<\n ClientMap<Contract>[Event] extends { ack: infer Acknowledgement } ? Acknowledgement : unknown\n >\n async sendWithAck(event: string, data?: unknown): Promise<unknown> {\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>({\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\n }\n\n /**\n * Configures transport listeners.\n */\n #setupSocketListeners(): void {\n // Handler for presence:update.\n this.#boundPresenceHandler = (data: unknown) => {\n const presenceData = ClientPresenceSnapshot.fromTransport(data, this.name)\n if (!presenceData) return\n this.#handlePresenceUpdate(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) => callClientHandler(handler, payload.data))\n const managedHandlers = this.#managedEventHandlers.get(payload.event)\n managedHandlers?.forEach((handler) => callClientHandler(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 if (this.#remoteSynchronization.status === 'subscribing') {\n this.#remoteSynchronization.connected = false\n } else if (\n this.#remoteSynchronization.status === 'active' ||\n this.#remoteSynchronization.status === 'subscriptionUncertain'\n ) {\n this.#remoteSynchronization = { status: 'waitingForReconnect' }\n } else if (this.#remoteSynchronization.status === 'remoteSubscriptionUncertain') {\n this.#remoteSynchronization = { status: 'idle' }\n }\n this.#resetConnectionState()\n }\n this.#transport.on('disconnect', this.#boundDisconnectHandler)\n }\n\n /**\n * Handles the initial presence snapshot.\n */\n #handleInitialPresence(data: ClientPresenceSnapshot): void {\n this.#presence = data\n\n // Call here() with all users.\n if (this.#hereHandler) {\n callClientHandler(this.#hereHandler, [...data.users])\n }\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: ClientPresenceSnapshot): void {\n const { joining, leaving } = data.diff(this.#presence)\n\n // Commit the validated update before invoking user code.\n this.#presence = data\n\n // Detect new users.\n for (const user of joining) {\n if (this.#joiningHandler) {\n callClientHandler(this.#joiningHandler, user)\n }\n }\n\n // Detect users that left.\n for (const user of leaving) {\n if (this.#leavingHandler) {\n callClientHandler(this.#leavingHandler, user)\n }\n }\n\n // Call here() with the updated list.\n if (this.#hereHandler) {\n callClientHandler(this.#hereHandler, [...data.users])\n }\n }\n\n #resetConnectionState(): void {\n this.#presence = null\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\n #clearUserCallbacks(): void {\n this.#eventHandlers.clear()\n this.#hereHandler = null\n this.#joiningHandler = null\n this.#leavingHandler = null\n }\n\n #clearCallbacks(): void {\n this.#clearUserCallbacks()\n this.#managedEventHandlers.clear()\n }\n\n /**\n * Performs terminal cleanup after the user leaves or the socket is disposed.\n */\n #cleanupTerminal(): void {\n this.#remoteSynchronization = { status: 'idle' }\n this.#resetConnectionState()\n this.#detachTransportListeners()\n this.#clearCallbacks()\n }\n}\n","import type { ConnectionState, EventHandler } from './types.js'\nimport { SERVER_DISCONNECT_CODE, type ServerProtocolMessage } from '../shared_types.js'\nimport { callClientHandler } from './callback.js'\n\ntype Callable = (...args: never[]) => unknown\ntype IncomingMessage = Extract<ServerProtocolMessage, { type: 'ack' | 'event' }>\n\ninterface PendingRequest {\n resolve: (value: unknown) => void\n reject: (error: Error) => void\n timer: ReturnType<typeof setTimeout>\n}\n\ninterface ConnectDeferred extends PromiseWithResolvers<void> {}\n\ninterface SocketConnection {\n socket: WebSocket\n}\n\ntype ClientSocketLifecycle =\n | { kind: 'disconnected'; reconnectAttempt: number }\n | { kind: 'waiting'; timer: ReturnType<typeof setTimeout>; reconnectAttempt: number }\n | { kind: 'opening'; deferred: ConnectDeferred; reconnectAttempt: number }\n | {\n kind: 'connecting'\n connection: SocketConnection\n deferred: ConnectDeferred\n reconnectAttempt: number\n }\n | {\n kind: 'opened'\n connection: SocketConnection\n deferred: ConnectDeferred\n reconnectAttempt: number\n }\n | { kind: 'connected'; connection: SocketConnection }\n | { kind: 'failed'; connection: SocketConnection; reconnectAttempt: number }\n\ntype DisconnectedLifecycle = Extract<ClientSocketLifecycle, { kind: 'disconnected' }>\ntype WaitingLifecycle = Extract<ClientSocketLifecycle, { kind: 'waiting' }>\n\ninterface ClientSocketSessionOptions {\n buildUrl: () => string\n createWebSocket?: (url: string) => WebSocket\n autoReconnect?: boolean\n reconnectDelay?: number\n reconnectMaxDelay?: number\n shouldReconnect?: (event: CloseEvent) => boolean\n onConnected?: () => void\n}\n\nexport class ClientSocketSession {\n #lifecycle: ClientSocketLifecycle = { kind: 'disconnected', reconnectAttempt: 0 }\n #stateHandlers = new Set<Callable>()\n #eventHandlers = new Map<string, Set<Callable>>()\n #pendingRequests = new Map<string, PendingRequest>()\n #requestId = 0\n #buildUrl: () => string\n #createWebSocket: (url: string) => WebSocket\n #autoReconnect: boolean\n #reconnectDelay: number\n #reconnectMaxDelay: number\n #shouldReconnect: (event: CloseEvent) => boolean\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.#shouldReconnect =\n options.shouldReconnect ?? ((event) => event.code !== SERVER_DISCONNECT_CODE)\n this.#onConnected = options.onConnected\n }\n\n get state(): ConnectionState {\n switch (this.#lifecycle.kind) {\n case 'opening':\n case 'connecting':\n return 'connecting'\n case 'opened':\n case 'connected':\n return 'connected'\n case 'disconnected':\n case 'waiting':\n case 'failed':\n return 'disconnected'\n }\n }\n\n get connected(): boolean {\n return this.state === 'connected'\n }\n\n connect(): Promise<void> {\n if (this.#lifecycle.kind === 'connected') return Promise.resolve()\n if (this.#lifecycle.kind === 'opening' || this.#lifecycle.kind === 'connecting') {\n return this.#lifecycle.deferred.promise\n }\n if (this.#lifecycle.kind === 'opened') return this.#lifecycle.deferred.promise\n\n const reconnectAttempt = this.#reconnectAttempt\n const failedSocket =\n this.#lifecycle.kind === 'failed' ? this.#lifecycle.connection.socket : null\n if (this.#lifecycle.kind === 'waiting') clearTimeout(this.#lifecycle.timer)\n\n const connection = this.#startConnection(reconnectAttempt)\n failedSocket?.close()\n return connection\n }\n\n #startConnection(reconnectAttempt: number): Promise<void> {\n const deferred = Promise.withResolvers<void>()\n const opening: ClientSocketLifecycle = { kind: 'opening', deferred, reconnectAttempt }\n this.#setLifecycle(opening)\n if (this.#lifecycle !== opening) return deferred.promise\n\n let socket: WebSocket\n try {\n socket = this.#createWebSocket(this.#buildUrl())\n } catch (error) {\n this.#setLifecycle({ kind: 'disconnected', reconnectAttempt })\n deferred.reject(error)\n return deferred.promise\n }\n\n const connection = { socket }\n const connecting: ClientSocketLifecycle = {\n kind: 'connecting',\n connection,\n deferred,\n reconnectAttempt,\n }\n this.#lifecycle = connecting\n\n const cleanup = () => {\n socket.removeEventListener('open', handleOpen)\n socket.removeEventListener('error', handleError)\n }\n\n const handleOpen = () => {\n if (this.#lifecycle !== connecting) return\n\n cleanup()\n const opened: ClientSocketLifecycle = {\n kind: 'opened',\n connection,\n deferred,\n reconnectAttempt,\n }\n this.#setLifecycle(opened)\n if (!this.#isCurrent(opened)) return\n\n this.#lifecycle = { kind: 'connected', connection }\n this.#emitLocal('connect')\n if (this.#onConnected) this.#callHandler(this.#onConnected)\n deferred.resolve()\n }\n\n const handleError = () => {\n if (this.#lifecycle !== connecting) return\n\n cleanup()\n deferred.reject(new Error('WebSocket connection failed'))\n this.#setLifecycle({ kind: 'failed', connection, reconnectAttempt })\n }\n\n socket.addEventListener('open', handleOpen)\n socket.addEventListener('error', handleError)\n socket.addEventListener('message', (event) => {\n if (this.#owns(connection)) this.#handleIncoming(event.data)\n })\n socket.addEventListener('close', (event) => {\n cleanup()\n if (this.#owns(connection)) this.#handleClose(event)\n })\n\n return deferred.promise\n }\n\n disconnect(): void {\n const lifecycle = this.#lifecycle\n const shouldEmitDisconnect = this.state !== 'disconnected'\n if (lifecycle.kind === 'waiting') clearTimeout(lifecycle.timer)\n if (\n lifecycle.kind === 'opening' ||\n lifecycle.kind === 'connecting' ||\n lifecycle.kind === 'opened'\n ) {\n lifecycle.deferred.reject(new Error('Socket disconnected'))\n }\n\n this.#setLifecycle({ kind: 'disconnected', reconnectAttempt: 0 })\n if (shouldEmitDisconnect) {\n this.#emitLocal('disconnect')\n }\n if ('connection' in lifecycle) lifecycle.connection.socket.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)\n\n return () => {\n this.off(event, handler)\n }\n }\n\n off<T = unknown>(event: string, handler: EventHandler<T>): void {\n this.#eventHandlers.get(event)?.delete(handler)\n }\n\n send(message: Record<string, unknown>): void {\n const socket = this.#connection?.socket\n if (socket?.readyState !== WebSocket.OPEN) {\n return\n }\n\n socket.send(JSON.stringify(message))\n }\n\n sendRequest(message: Record<string, unknown>, timeout = 5000): Promise<unknown> {\n const socket = this.#connection?.socket\n if (socket?.readyState !== WebSocket.OPEN) {\n return Promise.reject(new Error('Socket is not connected'))\n }\n\n const id = String(++this.#requestId)\n const payload = { ...message, id }\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, {\n resolve,\n reject,\n timer,\n })\n socket.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): IncomingMessage | null {\n try {\n const data = typeof raw === 'string' ? raw : String(raw)\n const message = JSON.parse(data) as Record<string, unknown> | null\n\n if (!message || typeof message !== 'object' || Array.isArray(message)) {\n return null\n }\n\n if (\n message.type === 'ack' &&\n typeof message.id === 'string' &&\n ((message.ok === true && message.error === undefined) ||\n (message.ok === false && typeof message.error === 'string' && message.data === undefined))\n ) {\n return message as IncomingMessage\n }\n\n if (\n message.type === 'event' &&\n typeof message.event === 'string' &&\n (message.channel === undefined || typeof message.channel === 'string')\n ) {\n return message as IncomingMessage\n }\n\n return 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(event: CloseEvent): void {\n const lifecycle = this.#lifecycle\n const reconnectAttempt = 'reconnectAttempt' in lifecycle ? lifecycle.reconnectAttempt : 0\n const shouldEmitDisconnect = this.state !== 'disconnected'\n if (lifecycle.kind === 'connecting') {\n lifecycle.deferred.reject(new Error('WebSocket connection failed'))\n }\n\n const disconnected: DisconnectedLifecycle = { kind: 'disconnected', reconnectAttempt }\n this.#setLifecycle(disconnected)\n if (shouldEmitDisconnect) {\n this.#emitLocal('disconnect')\n }\n this.#rejectPending(new Error('Socket disconnected'))\n\n if (this.#shouldReconnect(event) && this.#lifecycle === disconnected) {\n this.#scheduleReconnect(disconnected)\n }\n }\n\n #scheduleReconnect(disconnected: DisconnectedLifecycle): void {\n if (!this.#autoReconnect || this.#lifecycle !== disconnected) return\n\n const delay = Math.min(\n this.#reconnectDelay * 2 ** disconnected.reconnectAttempt,\n this.#reconnectMaxDelay\n )\n const waiting: WaitingLifecycle = {\n kind: 'waiting',\n reconnectAttempt: disconnected.reconnectAttempt + 1,\n timer: setTimeout(() => {\n if (this.#lifecycle !== waiting) return\n\n this.#startConnection(waiting.reconnectAttempt).catch(() => {\n if (this.#lifecycle.kind === 'failed') {\n const failed = this.#lifecycle\n const disconnected: DisconnectedLifecycle = {\n kind: 'disconnected',\n reconnectAttempt: failed.reconnectAttempt,\n }\n this.#setLifecycle(disconnected)\n failed.connection.socket.close()\n if (this.#isCurrent(disconnected)) this.#scheduleReconnect(disconnected)\n return\n }\n\n if (this.#lifecycle.kind === 'disconnected') {\n this.#scheduleReconnect(this.#lifecycle)\n }\n })\n }, delay),\n }\n this.#setLifecycle(waiting)\n }\n\n get #connection(): SocketConnection | undefined {\n const lifecycle = this.#lifecycle\n return 'connection' in lifecycle ? lifecycle.connection : undefined\n }\n\n get #reconnectAttempt(): number {\n const lifecycle = this.#lifecycle\n return 'reconnectAttempt' in lifecycle ? lifecycle.reconnectAttempt : 0\n }\n\n #owns(connection: SocketConnection): boolean {\n return this.#connection === connection\n }\n\n #isCurrent(lifecycle: ClientSocketLifecycle): boolean {\n return this.#lifecycle === lifecycle\n }\n\n #setLifecycle(lifecycle: ClientSocketLifecycle): void {\n const previousState = this.state\n this.#lifecycle = lifecycle\n const state = this.state\n\n if (previousState !== state) {\n this.#stateHandlers.forEach((handler) => this.#callHandler(handler, state))\n }\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) => this.#callHandler(handler, data))\n }\n\n #callHandler(handler: Callable, data?: unknown): void {\n callClientHandler(handler, data)\n }\n}\n","import { Channel } from './channel.js'\nimport { ClientSocketSession } from './socket_session.js'\nimport { reportClientError } from './callback.js'\nimport { BindableChannelPattern } from '../bindable_channel_pattern.js'\nimport type {\n SocketOptions,\n ConnectionState,\n EventHandler,\n SocketClientTransport,\n ChannelContractFor,\n ChannelContract,\n ChannelCallArgs,\n} from './types.js'\n\nexport const resolveChannel = Symbol('resolveChannel')\n\n/**\n * Main Socket client.\n */\ntype IsUntypedRegistry<Registry> = unknown extends Registry\n ? true\n : [Registry] extends [undefined]\n ? true\n : false\n\ntype SocketChannelArgs<Registry> =\n IsUntypedRegistry<Registry> extends true ? [name: string] : ChannelCallArgs<Registry>\n\ntype ChannelResult<Registry, Pattern extends string> =\n IsUntypedRegistry<Registry> extends true\n ? Channel<ChannelContract>\n : Channel<ChannelContractFor<Registry, Pattern>>\n\ntype RuntimeChannelParameters = object\n\n/** Constructs a concrete channel name from a supported generated pattern. @internal */\nexport function buildChannelName(\n pattern: string,\n parameters: RuntimeChannelParameters = {}\n): string {\n const bindablePattern = BindableChannelPattern.parse(pattern)\n if (!bindablePattern) {\n throw new Error(`Unsupported generated channel pattern: ${pattern}`)\n }\n return bindablePattern.bind(parameters)\n}\n\nexport class Socket<Registry = undefined> implements SocketClientTransport {\n #channels = new Map<string, Channel<any>>()\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 shouldReconnect: options.shouldReconnect,\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 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\n this.#session.disconnect()\n }\n\n channel<const Args extends SocketChannelArgs<Registry>>(\n ...args: Args\n ): ChannelResult<Registry, Args[0]> {\n const [pattern, parameters] = args as [string, object?]\n const name = args.length > 1 ? buildChannelName(pattern, parameters) : pattern\n return this[resolveChannel](name) as ChannelResult<Registry, Args[0]>\n }\n\n /** Resolves a channel for framework adapters after their public type checks. @internal */\n [resolveChannel](name: string): Channel<any> {\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 if (this.#channels.get(name) === channel && !channel.subscribed) {\n this.#channels.delete(name)\n }\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<T = unknown>(event: string, handler: EventHandler<T>): 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) as Promise<T>\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 if (url.protocol === 'http:') {\n url.protocol = 'ws:'\n } else if (url.protocol === 'https:') {\n url.protocol = 'wss:'\n } else if (url.protocol !== 'ws:' && url.protocol !== 'wss:') {\n throw new Error(`Unsupported socket URL protocol: ${url.protocol}`)\n }\n url.pathname = this.#path\n url.hash = ''\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.$resubscribe().catch(reportClientError)\n }\n }\n }\n}\n"],"mappings":"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","names":["disconnected"]}
|
|
1
|
+
{"version":3,"sources":["../src/client/callback.ts","../src/client/presence_snapshot.ts","../src/client/channel.ts","../src/client/socket_session.ts","../src/client/socket.ts"],"sourcesContent":["type Callable = (...args: never[]) => unknown\n\nexport function reportClientError(error: unknown): void {\n try {\n const reportError = (\n globalThis as typeof globalThis & { reportError?: (error: unknown) => void }\n ).reportError\n\n if (typeof reportError === 'function') {\n reportError(error)\n } else {\n console.error(error)\n }\n } catch {\n // Error reporting must never affect socket state or create another rejected promise.\n }\n}\n\nexport function callClientHandler(handler: Callable, data?: unknown): void {\n try {\n const result = Reflect.apply(handler, undefined, [data])\n if (result && typeof (result as PromiseLike<unknown>).then === 'function') {\n void Promise.resolve(result).catch(reportClientError)\n }\n } catch (error) {\n reportClientError(error)\n }\n}\n","import type { PresenceUser } from './types.js'\n\nexport interface ClientPresenceDiff {\n readonly joining: readonly PresenceUser[]\n readonly leaving: readonly PresenceUser[]\n}\n\n/**\n * Immutable, validated client view of one channel's presence.\n */\nexport class ClientPresenceSnapshot {\n readonly count: number\n readonly users: readonly PresenceUser[]\n readonly #usersById: ReadonlyMap<string, PresenceUser>\n\n private constructor(users: PresenceUser[]) {\n // Copy and freeze each user. Consumer code cannot change the snapshot.\n this.users = Object.freeze(users.map((user) => Object.freeze({ ...user }) as PresenceUser))\n this.count = this.users.length\n\n // Build the ID index once. Diff operations do not need a second channel state.\n this.#usersById = new Map(this.users.map((user) => [user.id, user]))\n }\n\n /**\n * Validate one transport snapshot for the expected channel.\n */\n static fromTransport(value: unknown, expectedChannel: string): ClientPresenceSnapshot | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return null\n }\n\n const data = value as Record<string, unknown>\n\n // The transport count must be an integer and must match the user list.\n if (\n data.channel !== expectedChannel ||\n !Array.isArray(data.users) ||\n !Number.isInteger(data.count) ||\n data.count !== data.users.length\n ) {\n return null\n }\n\n const users: PresenceUser[] = []\n const userIds = new Set<string>()\n for (const candidate of data.users) {\n // Each user needs a unique ID and one transport timestamp.\n if (\n !candidate ||\n typeof candidate !== 'object' ||\n Array.isArray(candidate) ||\n typeof candidate.id !== 'string' ||\n typeof candidate.joinedAt !== 'string' ||\n userIds.has(candidate.id)\n ) {\n return null\n }\n userIds.add(candidate.id)\n users.push(candidate as PresenceUser)\n }\n\n return new ClientPresenceSnapshot(users)\n }\n\n /**\n * Return users that joined or left since the previous snapshot.\n */\n diff(previous: ClientPresenceSnapshot | null): ClientPresenceDiff {\n if (!previous) {\n return { joining: this.users, leaving: [] }\n }\n\n // Keep the order from the snapshot that owns each user.\n return {\n joining: this.users.filter((user) => !previous.#usersById.has(user.id)),\n leaving: previous.users.filter((user) => !this.#usersById.has(user.id)),\n }\n }\n}\n","import type {\n SocketClientTransport,\n SubscribeOptions,\n SubscribeResult,\n UnsubscribeOptions,\n PresenceUser,\n EventHandler,\n ChannelMessage,\n ChannelAck,\n ChannelContract,\n} from './types.js'\nimport { callClientHandler } from './callback.js'\nimport { ClientPresenceSnapshot } from './presence_snapshot.js'\n\nconst CLIENT_EVENT_PREFIX = 'client:'\n\ntype Callable = (...args: never[]) => unknown\n\n/**\n * Represents a subscribed channel.\n * Chainable API inspired by Laravel Echo.\n *\n * @example\n * ```ts\n * socket.channel('chat/:roomId', { roomId: '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 */\ntype ClientMap<C> = C extends ChannelContract<infer E, unknown> ? E : unknown\ntype ServerMap<C> = C extends ChannelContract<unknown, infer E> ? E : unknown\ntype IsUnknown<T> = unknown extends T ? true : false\ntype StringKey<T> = Extract<keyof T, string>\ntype EventDataArgs<Payload> = undefined extends Payload ? [data?: Payload] : [data: Payload]\n\ninterface PendingSubscribe<Contract> {\n promise: Promise<Channel<Contract>>\n resolve: (channel: Channel<Contract>) => void\n reject: (error: Error) => void\n}\n\ntype RemoteSynchronization<Contract> =\n | { status: 'idle' }\n | { status: 'subscribing'; pending: PendingSubscribe<Contract>; connected: boolean }\n | { status: 'active' }\n | { status: 'unsubscribing'; promise: Promise<void> }\n | { status: 'waitingForReconnect' }\n | { status: 'subscriptionUncertain' }\n | { status: 'remoteSubscriptionUncertain' }\n\nexport class Channel<Contract> {\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 #remoteSynchronization: RemoteSynchronization<Contract> = { status: 'idle' }\n\n /**\n * Event handlers grouped by event type.\n */\n #eventHandlers = new Map<string, Set<Callable>>()\n #managedEventHandlers = new Map<string, Set<Callable>>()\n\n /**\n * Current presence data.\n */\n #presence: ClientPresenceSnapshot | null = null\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 #managedSubscriptions = 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 || this.#managedSubscriptions > 0\n }\n\n /** Whether a framework lifecycle currently owns this channel. @internal */\n get $managed(): boolean {\n return this.#managedSubscriptions > 0\n }\n\n /**\n * Whether the channel is active on the server.\n */\n get active(): boolean {\n return this.#remoteSynchronization.status === '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>(\n this: IsUnknown<ServerMap<Contract>> extends true ? Channel<Contract> : never,\n event: string,\n handler: EventHandler<T>\n ): this\n listen<Event extends StringKey<ServerMap<Contract>>>(\n this: Channel<Contract>,\n event: Event,\n handler: EventHandler<ServerMap<Contract>[Event]>\n ): this\n listen(event: string, handler: EventHandler<any>): this {\n const handlers = this.#eventHandlers.get(event) ?? new Set<Callable>()\n handlers.add(handler)\n this.#eventHandlers.set(event, handlers)\n return this\n }\n\n /**\n * Removes an event listener.\n */\n stopListening<T = unknown>(\n this: IsUnknown<ServerMap<Contract>> extends true ? Channel<Contract> : never,\n event: string,\n handler?: EventHandler<T>\n ): this\n stopListening<Event extends StringKey<ServerMap<Contract>>>(\n this: Channel<Contract>,\n event: Event,\n handler?: EventHandler<ServerMap<Contract>[Event]>\n ): this\n stopListening(event: string, handler?: EventHandler<any>): 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 /** Registers a lifecycle-owned event handler. @internal */\n $listen(event: string, handler: EventHandler<any>): void {\n const handlers = this.#managedEventHandlers.get(event) ?? new Set<Callable>()\n handlers.add(handler)\n this.#managedEventHandlers.set(event, handlers)\n }\n\n /** Removes a lifecycle-owned event handler. @internal */\n $stopListening(event: string, handler: EventHandler<any>): void {\n const handlers = this.#managedEventHandlers.get(event)\n handlers?.delete(handler)\n if (handlers?.size === 0) this.#managedEventHandlers.delete(event)\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 const handlers =\n this.#eventHandlers.get(`${CLIENT_EVENT_PREFIX}${event}`) ?? new Set<Callable>()\n handlers.add(handler)\n this.#eventHandlers.set(`${CLIENT_EVENT_PREFIX}${event}`, handlers)\n return this\n }\n\n /**\n * Removes a whispered client event listener.\n */\n stopListeningForWhisper<T = unknown>(event: string, handler?: EventHandler<T>): this {\n const name = `${CLIENT_EVENT_PREFIX}${event}`\n if (handler) this.#eventHandlers.get(name)?.delete(handler)\n else this.#eventHandlers.delete(name)\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 return this.#ensureSubscribed(options)\n }\n\n /** Acquires subscription intent without overriding a direct consumer. @internal */\n $acquire(options: SubscribeOptions = {}): {\n ready: Promise<Channel<Contract>>\n release: () => Promise<void>\n } {\n this.#managedSubscriptions++\n const ready = this.#transport.connected\n ? this.#ensureSubscribed(options)\n : Promise.resolve(this)\n let released = false\n\n return {\n ready,\n release: () => {\n if (released) {\n return this.#remoteSynchronization.status === 'unsubscribing'\n ? this.#remoteSynchronization.promise\n : Promise.resolve()\n }\n released = true\n this.#managedSubscriptions--\n return this.#unsubscribeIfUnused()\n },\n }\n }\n\n /** Restores desired subscriptions after the transport reconnects. @internal */\n $resubscribe(options: SubscribeOptions = {}): Promise<this> {\n return this.#ensureSubscribed(options)\n }\n\n async #ensureSubscribed(options: SubscribeOptions): Promise<this> {\n if (this.#remoteSynchronization.status === 'unsubscribing') {\n await this.#remoteSynchronization.promise\n }\n\n if (!this.subscribed) return this\n\n if (this.#remoteSynchronization.status === 'subscribing') {\n await this.#remoteSynchronization.pending.promise\n return this\n }\n\n if (this.#remoteSynchronization.status === '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 (\n this.#remoteSynchronization.status === 'idle' ||\n this.#remoteSynchronization.status === 'remoteSubscriptionUncertain'\n ) {\n this.#setupSocketListeners()\n }\n\n const deferred = Promise.withResolvers<Channel<Contract>>()\n const synchronization: RemoteSynchronization<Contract> = {\n status: 'subscribing',\n pending: deferred,\n connected: true,\n }\n this.#remoteSynchronization = synchronization\n\n const timer = setTimeout(() => {\n if (this.#remoteSynchronization !== synchronization) return\n this.#remoteSynchronization = { status: 'waitingForReconnect' }\n this.#resetConnectionState()\n synchronization.pending.reject(new Error(`Subscribe timeout for channel: ${this.name}`))\n void this.#transport\n .sendRequest({ type: 'unsubscribe', channel: this.name }, timeout)\n .catch(() => {})\n }, timeout)\n\n this.#transport\n .sendRequest<SubscribeResult>({ type: 'subscribe', channel: this.name }, timeout)\n .then((response) => {\n if (this.#remoteSynchronization !== synchronization) return\n clearTimeout(timer)\n\n if (response.ok) {\n this.#remoteSynchronization = { status: 'active' }\n\n const presenceData = ClientPresenceSnapshot.fromTransport(\n response.data?.presenceData,\n this.name\n )\n\n // Apply the initial presence snapshot.\n if (presenceData) {\n this.#handleInitialPresence(presenceData)\n }\n\n synchronization.pending.resolve(this)\n } else {\n this.#remoteSynchronization = { status: 'waitingForReconnect' }\n this.#resetConnectionState()\n synchronization.pending.reject(new Error(response.error))\n }\n })\n .catch((error) => {\n if (this.#remoteSynchronization !== synchronization) return\n clearTimeout(timer)\n this.#remoteSynchronization = synchronization.connected\n ? { status: 'subscriptionUncertain' }\n : { status: 'waitingForReconnect' }\n this.#resetConnectionState()\n synchronization.pending.reject(\n error instanceof Error ? error : new Error('Subscribe failed')\n )\n })\n\n await deferred.promise\n return this\n }\n\n /**\n * Unsubscribes from the channel.\n */\n async unsubscribe(options: UnsubscribeOptions = {}): Promise<void> {\n this.#subscribed = false\n this.#clearUserCallbacks()\n return this.#unsubscribeIfUnused(options)\n }\n\n #unsubscribeIfUnused(options: UnsubscribeOptions = {}): Promise<void> {\n if (this.subscribed) return Promise.resolve()\n if (this.#remoteSynchronization.status === 'unsubscribing') {\n return this.#remoteSynchronization.promise\n }\n\n const previousSynchronization = this.#remoteSynchronization\n const hadPendingSubscribe = previousSynchronization.status === 'subscribing'\n if (previousSynchronization.status === 'subscribing') {\n previousSynchronization.pending.reject(new Error(`Unsubscribed from channel: ${this.name}`))\n }\n\n const remoteSubscriptionPossible =\n previousSynchronization.status === 'active' ||\n previousSynchronization.status === 'subscriptionUncertain' ||\n previousSynchronization.status === 'remoteSubscriptionUncertain'\n\n if (!hadPendingSubscribe && !remoteSubscriptionPossible) {\n this.#cleanupTerminal()\n return Promise.resolve()\n }\n\n const timeout = options.timeout ?? 5000\n const deferred = Promise.withResolvers<void>()\n const synchronization: RemoteSynchronization<Contract> = {\n status: 'unsubscribing',\n promise: deferred.promise,\n }\n this.#remoteSynchronization = synchronization\n let remoteMayRemain = true\n let terminalSynchronization: RemoteSynchronization<Contract> | null = null\n const timer = setTimeout(() => deferred.resolve(), timeout)\n\n this.#transport\n .sendRequest({ type: 'unsubscribe', channel: this.name }, timeout)\n .then(() => {\n remoteMayRemain = false\n clearTimeout(timer)\n deferred.resolve()\n if (this.#remoteSynchronization === terminalSynchronization) {\n this.#remoteSynchronization = { status: 'idle' }\n }\n })\n .catch(() => {\n clearTimeout(timer)\n deferred.resolve()\n })\n\n const pending = deferred.promise\n void pending.finally(() => {\n if (this.#remoteSynchronization !== synchronization) return\n terminalSynchronization = remoteMayRemain\n ? { status: 'remoteSubscriptionUncertain' }\n : { status: 'idle' }\n this.#remoteSynchronization = terminalSynchronization\n this.#resetConnectionState()\n this.#detachTransportListeners()\n if (!this.subscribed) this.#clearCallbacks()\n })\n return pending\n }\n\n /**\n * Sends an event to the server.\n */\n send<T = unknown>(\n this: IsUnknown<ClientMap<Contract>> extends true ? Channel<Contract> : never,\n event: string,\n data?: T\n ): this\n send<Event extends StringKey<ClientMap<Contract>>>(\n this: Channel<Contract>,\n event: Event,\n ...[data]: ClientMap<Contract>[Event] extends { payload: infer Payload }\n ? EventDataArgs<Payload>\n : never\n ): this\n send(event: string, data?: unknown): 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 sendWithAck<T = unknown, Result = unknown>(\n this: IsUnknown<ClientMap<Contract>> extends true ? Channel<Contract> : never,\n event: string,\n data?: T\n ): Promise<Result>\n sendWithAck<Event extends StringKey<ClientMap<Contract>>>(\n this: Channel<Contract>,\n event: Event,\n ...[data]: ClientMap<Contract>[Event] extends { payload: infer Payload }\n ? EventDataArgs<Payload>\n : never\n ): Promise<\n ClientMap<Contract>[Event] extends { ack: infer Acknowledgement } ? Acknowledgement : unknown\n >\n async sendWithAck(event: string, data?: unknown): Promise<unknown> {\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>({\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\n }\n\n /**\n * Configures transport listeners.\n */\n #setupSocketListeners(): void {\n // Handler for presence:update.\n this.#boundPresenceHandler = (data: unknown) => {\n const presenceData = ClientPresenceSnapshot.fromTransport(data, this.name)\n if (!presenceData) return\n this.#handlePresenceUpdate(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) => callClientHandler(handler, payload.data))\n const managedHandlers = this.#managedEventHandlers.get(payload.event)\n managedHandlers?.forEach((handler) => callClientHandler(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 if (this.#remoteSynchronization.status === 'subscribing') {\n this.#remoteSynchronization.connected = false\n } else if (\n this.#remoteSynchronization.status === 'active' ||\n this.#remoteSynchronization.status === 'subscriptionUncertain'\n ) {\n this.#remoteSynchronization = { status: 'waitingForReconnect' }\n } else if (this.#remoteSynchronization.status === 'remoteSubscriptionUncertain') {\n this.#remoteSynchronization = { status: 'idle' }\n }\n this.#resetConnectionState()\n }\n this.#transport.on('disconnect', this.#boundDisconnectHandler)\n }\n\n /**\n * Handles the initial presence snapshot.\n */\n #handleInitialPresence(data: ClientPresenceSnapshot): void {\n this.#presence = data\n\n // Call here() with all users.\n if (this.#hereHandler) {\n callClientHandler(this.#hereHandler, [...data.users])\n }\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: ClientPresenceSnapshot): void {\n const { joining, leaving } = data.diff(this.#presence)\n\n // Commit the validated update before invoking user code.\n this.#presence = data\n\n // Detect new users.\n for (const user of joining) {\n if (this.#joiningHandler) {\n callClientHandler(this.#joiningHandler, user)\n }\n }\n\n // Detect users that left.\n for (const user of leaving) {\n if (this.#leavingHandler) {\n callClientHandler(this.#leavingHandler, user)\n }\n }\n\n // Call here() with the updated list.\n if (this.#hereHandler) {\n callClientHandler(this.#hereHandler, [...data.users])\n }\n }\n\n #resetConnectionState(): void {\n this.#presence = null\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\n #clearUserCallbacks(): void {\n this.#eventHandlers.clear()\n this.#hereHandler = null\n this.#joiningHandler = null\n this.#leavingHandler = null\n }\n\n #clearCallbacks(): void {\n this.#clearUserCallbacks()\n this.#managedEventHandlers.clear()\n }\n\n /**\n * Performs terminal cleanup after the user leaves or the socket is disposed.\n */\n #cleanupTerminal(): void {\n this.#remoteSynchronization = { status: 'idle' }\n this.#resetConnectionState()\n this.#detachTransportListeners()\n this.#clearCallbacks()\n }\n}\n","import type { ConnectionState, EventHandler } from './types.js'\nimport { SERVER_DISCONNECT_CODE, type ServerProtocolMessage } from '../shared_types.js'\nimport { callClientHandler } from './callback.js'\n\ntype Callable = (...args: never[]) => unknown\ntype IncomingMessage = Extract<ServerProtocolMessage, { type: 'ack' | 'event' }>\n\ninterface PendingRequest {\n resolve: (value: unknown) => void\n reject: (error: Error) => void\n timer: ReturnType<typeof setTimeout>\n}\n\ninterface ConnectDeferred extends PromiseWithResolvers<void> {}\n\ninterface SocketConnection {\n socket: WebSocket\n}\n\ntype ClientSocketLifecycle =\n | { kind: 'disconnected'; reconnectAttempt: number }\n | { kind: 'waiting'; timer: ReturnType<typeof setTimeout>; reconnectAttempt: number }\n | { kind: 'opening'; deferred: ConnectDeferred; reconnectAttempt: number }\n | {\n kind: 'connecting'\n connection: SocketConnection\n deferred: ConnectDeferred\n reconnectAttempt: number\n }\n | {\n kind: 'opened'\n connection: SocketConnection\n deferred: ConnectDeferred\n reconnectAttempt: number\n }\n | { kind: 'connected'; connection: SocketConnection }\n | { kind: 'failed'; connection: SocketConnection; reconnectAttempt: number }\n\ntype DisconnectedLifecycle = Extract<ClientSocketLifecycle, { kind: 'disconnected' }>\ntype WaitingLifecycle = Extract<ClientSocketLifecycle, { kind: 'waiting' }>\n\ninterface ClientSocketSessionOptions {\n buildUrl: () => string\n createWebSocket?: (url: string) => WebSocket\n autoReconnect?: boolean\n reconnectDelay?: number\n reconnectMaxDelay?: number\n shouldReconnect?: (event: CloseEvent) => boolean\n onConnected?: () => void\n}\n\nexport class ClientSocketSession {\n #lifecycle: ClientSocketLifecycle = { kind: 'disconnected', reconnectAttempt: 0 }\n #stateHandlers = new Set<Callable>()\n #eventHandlers = new Map<string, Set<Callable>>()\n #pendingRequests = new Map<string, PendingRequest>()\n #requestId = 0\n #buildUrl: () => string\n #createWebSocket: (url: string) => WebSocket\n #autoReconnect: boolean\n #reconnectDelay: number\n #reconnectMaxDelay: number\n #shouldReconnect: (event: CloseEvent) => boolean\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.#shouldReconnect =\n options.shouldReconnect ?? ((event) => event.code !== SERVER_DISCONNECT_CODE)\n this.#onConnected = options.onConnected\n }\n\n get state(): ConnectionState {\n switch (this.#lifecycle.kind) {\n case 'opening':\n case 'connecting':\n return 'connecting'\n case 'opened':\n case 'connected':\n return 'connected'\n case 'disconnected':\n case 'waiting':\n case 'failed':\n return 'disconnected'\n }\n }\n\n get connected(): boolean {\n return this.state === 'connected'\n }\n\n connect(): Promise<void> {\n if (this.#lifecycle.kind === 'connected') return Promise.resolve()\n if (this.#lifecycle.kind === 'opening' || this.#lifecycle.kind === 'connecting') {\n return this.#lifecycle.deferred.promise\n }\n if (this.#lifecycle.kind === 'opened') return this.#lifecycle.deferred.promise\n\n const reconnectAttempt = this.#reconnectAttempt\n const failedSocket =\n this.#lifecycle.kind === 'failed' ? this.#lifecycle.connection.socket : null\n if (this.#lifecycle.kind === 'waiting') clearTimeout(this.#lifecycle.timer)\n\n const connection = this.#startConnection(reconnectAttempt)\n failedSocket?.close()\n return connection\n }\n\n #startConnection(reconnectAttempt: number): Promise<void> {\n const deferred = Promise.withResolvers<void>()\n const opening: ClientSocketLifecycle = { kind: 'opening', deferred, reconnectAttempt }\n this.#setLifecycle(opening)\n if (this.#lifecycle !== opening) return deferred.promise\n\n let socket: WebSocket\n try {\n socket = this.#createWebSocket(this.#buildUrl())\n } catch (error) {\n this.#setLifecycle({ kind: 'disconnected', reconnectAttempt })\n deferred.reject(error)\n return deferred.promise\n }\n\n const connection = { socket }\n const connecting: ClientSocketLifecycle = {\n kind: 'connecting',\n connection,\n deferred,\n reconnectAttempt,\n }\n this.#lifecycle = connecting\n\n const cleanup = () => {\n socket.removeEventListener('open', handleOpen)\n socket.removeEventListener('error', handleError)\n }\n\n const handleOpen = () => {\n if (this.#lifecycle !== connecting) return\n\n cleanup()\n const opened: ClientSocketLifecycle = {\n kind: 'opened',\n connection,\n deferred,\n reconnectAttempt,\n }\n this.#setLifecycle(opened)\n if (!this.#isCurrent(opened)) return\n\n this.#lifecycle = { kind: 'connected', connection }\n this.#emitLocal('connect')\n if (this.#onConnected) this.#callHandler(this.#onConnected)\n deferred.resolve()\n }\n\n const handleError = () => {\n if (this.#lifecycle !== connecting) return\n\n cleanup()\n deferred.reject(new Error('WebSocket connection failed'))\n this.#setLifecycle({ kind: 'failed', connection, reconnectAttempt })\n }\n\n socket.addEventListener('open', handleOpen)\n socket.addEventListener('error', handleError)\n socket.addEventListener('message', (event) => {\n if (this.#owns(connection)) this.#handleIncoming(event.data)\n })\n socket.addEventListener('close', (event) => {\n cleanup()\n if (this.#owns(connection)) this.#handleClose(event)\n })\n\n return deferred.promise\n }\n\n disconnect(): void {\n const lifecycle = this.#lifecycle\n const shouldEmitDisconnect = this.state !== 'disconnected'\n if (lifecycle.kind === 'waiting') clearTimeout(lifecycle.timer)\n if (\n lifecycle.kind === 'opening' ||\n lifecycle.kind === 'connecting' ||\n lifecycle.kind === 'opened'\n ) {\n lifecycle.deferred.reject(new Error('Socket disconnected'))\n }\n\n this.#setLifecycle({ kind: 'disconnected', reconnectAttempt: 0 })\n if (shouldEmitDisconnect) {\n this.#emitLocal('disconnect')\n }\n if ('connection' in lifecycle) lifecycle.connection.socket.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)\n\n return () => {\n this.off(event, handler)\n }\n }\n\n off<T = unknown>(event: string, handler: EventHandler<T>): void {\n this.#eventHandlers.get(event)?.delete(handler)\n }\n\n send(message: Record<string, unknown>): void {\n const socket = this.#connection?.socket\n if (socket?.readyState !== WebSocket.OPEN) {\n return\n }\n\n socket.send(JSON.stringify(message))\n }\n\n sendRequest(message: Record<string, unknown>, timeout = 5000): Promise<unknown> {\n const socket = this.#connection?.socket\n if (socket?.readyState !== WebSocket.OPEN) {\n return Promise.reject(new Error('Socket is not connected'))\n }\n\n const id = String(++this.#requestId)\n const payload = { ...message, id }\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, {\n resolve,\n reject,\n timer,\n })\n socket.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): IncomingMessage | null {\n try {\n const data = typeof raw === 'string' ? raw : String(raw)\n const message = JSON.parse(data) as Record<string, unknown> | null\n\n if (!message || typeof message !== 'object' || Array.isArray(message)) {\n return null\n }\n\n if (\n message.type === 'ack' &&\n typeof message.id === 'string' &&\n ((message.ok === true && message.error === undefined) ||\n (message.ok === false && typeof message.error === 'string' && message.data === undefined))\n ) {\n return message as IncomingMessage\n }\n\n if (\n message.type === 'event' &&\n typeof message.event === 'string' &&\n (message.channel === undefined || typeof message.channel === 'string')\n ) {\n return message as IncomingMessage\n }\n\n return 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(event: CloseEvent): void {\n const lifecycle = this.#lifecycle\n const reconnectAttempt = 'reconnectAttempt' in lifecycle ? lifecycle.reconnectAttempt : 0\n const shouldEmitDisconnect = this.state !== 'disconnected'\n if (lifecycle.kind === 'connecting') {\n lifecycle.deferred.reject(new Error('WebSocket connection failed'))\n }\n\n const disconnected: DisconnectedLifecycle = { kind: 'disconnected', reconnectAttempt }\n this.#setLifecycle(disconnected)\n if (shouldEmitDisconnect) {\n this.#emitLocal('disconnect')\n }\n this.#rejectPending(new Error('Socket disconnected'))\n\n if (this.#shouldReconnect(event) && this.#lifecycle === disconnected) {\n this.#scheduleReconnect(disconnected)\n }\n }\n\n #scheduleReconnect(disconnected: DisconnectedLifecycle): void {\n if (!this.#autoReconnect || this.#lifecycle !== disconnected) return\n\n const delay = Math.min(\n this.#reconnectDelay * 2 ** disconnected.reconnectAttempt,\n this.#reconnectMaxDelay\n )\n const waiting: WaitingLifecycle = {\n kind: 'waiting',\n reconnectAttempt: disconnected.reconnectAttempt + 1,\n timer: setTimeout(() => {\n if (this.#lifecycle !== waiting) return\n\n this.#startConnection(waiting.reconnectAttempt).catch(() => {\n if (this.#lifecycle.kind === 'failed') {\n const failed = this.#lifecycle\n const disconnected: DisconnectedLifecycle = {\n kind: 'disconnected',\n reconnectAttempt: failed.reconnectAttempt,\n }\n this.#setLifecycle(disconnected)\n failed.connection.socket.close()\n if (this.#isCurrent(disconnected)) this.#scheduleReconnect(disconnected)\n return\n }\n\n if (this.#lifecycle.kind === 'disconnected') {\n this.#scheduleReconnect(this.#lifecycle)\n }\n })\n }, delay),\n }\n this.#setLifecycle(waiting)\n }\n\n get #connection(): SocketConnection | undefined {\n const lifecycle = this.#lifecycle\n return 'connection' in lifecycle ? lifecycle.connection : undefined\n }\n\n get #reconnectAttempt(): number {\n const lifecycle = this.#lifecycle\n return 'reconnectAttempt' in lifecycle ? lifecycle.reconnectAttempt : 0\n }\n\n #owns(connection: SocketConnection): boolean {\n return this.#connection === connection\n }\n\n #isCurrent(lifecycle: ClientSocketLifecycle): boolean {\n return this.#lifecycle === lifecycle\n }\n\n #setLifecycle(lifecycle: ClientSocketLifecycle): void {\n const previousState = this.state\n this.#lifecycle = lifecycle\n const state = this.state\n\n if (previousState !== state) {\n this.#stateHandlers.forEach((handler) => this.#callHandler(handler, state))\n }\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) => this.#callHandler(handler, data))\n }\n\n #callHandler(handler: Callable, data?: unknown): void {\n callClientHandler(handler, data)\n }\n}\n","import { Channel } from './channel.js'\nimport { ClientSocketSession } from './socket_session.js'\nimport { callClientHandler } from './callback.js'\nimport { BindableChannelPattern } from '../bindable_channel_pattern.js'\nimport type {\n SocketOptions,\n ConnectionState,\n EventHandler,\n SocketClientTransport,\n ChannelContractFor,\n ChannelContract,\n ChannelCallArgs,\n} from './types.js'\n\nexport const resolveChannel = Symbol('resolveChannel')\n\n/**\n * Main Socket client.\n */\ntype IsUntypedRegistry<Registry> = unknown extends Registry\n ? true\n : [Registry] extends [undefined]\n ? true\n : false\n\ntype SocketChannelArgs<Registry> =\n IsUntypedRegistry<Registry> extends true ? [name: string] : ChannelCallArgs<Registry>\n\ntype ChannelResult<Registry, Pattern extends string> =\n IsUntypedRegistry<Registry> extends true\n ? Channel<ChannelContract>\n : Channel<ChannelContractFor<Registry, Pattern>>\n\ntype RuntimeChannelParameters = object\n\n/** Constructs a concrete channel name from a supported generated pattern. @internal */\nexport function buildChannelName(\n pattern: string,\n parameters: RuntimeChannelParameters = {}\n): string {\n const bindablePattern = BindableChannelPattern.parse(pattern)\n if (!bindablePattern) {\n throw new Error(`Unsupported generated channel pattern: ${pattern}`)\n }\n return bindablePattern.bind(parameters)\n}\n\nexport class Socket<Registry = undefined> implements SocketClientTransport {\n #channels = new Map<string, Channel<any>>()\n #url: string\n #path: string\n #session: ClientSocketSession\n #onResubscribeError: SocketOptions['onResubscribeError']\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.#onResubscribeError = options.onResubscribeError\n this.#session = new ClientSocketSession({\n buildUrl: () => this.#buildUrl(),\n autoReconnect: options.autoReconnect,\n reconnectDelay: options.reconnectDelay,\n reconnectMaxDelay: options.reconnectMaxDelay,\n shouldReconnect: options.shouldReconnect,\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 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\n this.#session.disconnect()\n }\n\n channel<const Args extends SocketChannelArgs<Registry>>(\n ...args: Args\n ): ChannelResult<Registry, Args[0]> {\n const [pattern, parameters] = args as [string, object?]\n const name = args.length > 1 ? buildChannelName(pattern, parameters) : pattern\n return this[resolveChannel](name) as ChannelResult<Registry, Args[0]>\n }\n\n /** Resolves a channel for framework adapters after their public type checks. @internal */\n [resolveChannel](name: string): Channel<any> {\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 if (this.#channels.get(name) === channel && !channel.subscribed) {\n this.#channels.delete(name)\n }\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<T = unknown>(event: string, handler: EventHandler<T>): 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) as Promise<T>\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 if (url.protocol === 'http:') {\n url.protocol = 'ws:'\n } else if (url.protocol === 'https:') {\n url.protocol = 'wss:'\n } else if (url.protocol !== 'ws:' && url.protocol !== 'wss:') {\n throw new Error(`Unsupported socket URL protocol: ${url.protocol}`)\n }\n url.pathname = this.#path\n url.hash = ''\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.$resubscribe().catch((error) => {\n if (this.#onResubscribeError) {\n callClientHandler(this.#onResubscribeError, {\n channel: channel.name,\n error: error instanceof Error ? error : new Error('Resubscribe failed'),\n })\n }\n })\n }\n }\n }\n}\n"],"mappings":"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","names":["disconnected"]}
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
buildChannelName,
|
|
3
3
|
callClientHandler,
|
|
4
4
|
resolveChannel
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-3FY7EIZF.js";
|
|
6
6
|
|
|
7
7
|
// src/client/framework.ts
|
|
8
8
|
function createChannelCallArgs(pattern, parameters) {
|
|
@@ -149,4 +149,4 @@ export {
|
|
|
149
149
|
acquireChannel,
|
|
150
150
|
acquireSocketOwnership
|
|
151
151
|
};
|
|
152
|
-
//# sourceMappingURL=chunk-
|
|
152
|
+
//# sourceMappingURL=chunk-OAI6SAER.js.map
|
package/build/index.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { S as SocketAuthenticationHandler, a as SocketConfig } from './base_channel-
|
|
2
|
-
export { A as AuthenticatedSocket, B as BaseChannel, C as ChannelOptions, D as Duration, M as Middleware, b as MiddlewareClass, c as MiddlewareConstructor, d as MiddlewareContext, e as MiddlewareFn, f as PresenceInfo, g as PresenceMember, R as RawSocket, h as SocketAuthenticationResult, i as SocketEvents, j as SocketHealthSnapshot, k as SocketHttpMiddleware, l as SocketOriginResolver, m as SocketOriginValues, n as SocketResponseError, o as SocketRetryQueueConfig, p as SocketServiceStatus, q as SocketTransportConfig, r as SocketUpgradeContext } from './base_channel-
|
|
1
|
+
import { S as SocketAuthenticationHandler, a as SocketConfig } from './base_channel-DFyd3hVE.js';
|
|
2
|
+
export { A as AuthenticatedSocket, B as BaseChannel, C as ChannelOptions, D as Duration, M as Middleware, b as MiddlewareClass, c as MiddlewareConstructor, d as MiddlewareContext, e as MiddlewareFn, f as PresenceInfo, g as PresenceMember, R as RawSocket, h as SocketAuthenticationResult, i as SocketEvents, j as SocketHealthSnapshot, k as SocketHttpMiddleware, l as SocketOriginResolver, m as SocketOriginValues, n as SocketResponseError, o as SocketRetryQueueConfig, p as SocketServiceStatus, q as SocketTransportConfig, r as SocketUpgradeContext } from './base_channel-DFyd3hVE.js';
|
|
3
3
|
import ConfigureCommand from '@adonisjs/core/commands/configure';
|
|
4
4
|
export { a as ChannelAck, b as ChannelMessage, C as ChannelServerEventsOf } from './shared_types-B11CaOr5.js';
|
|
5
5
|
import 'ws';
|
|
6
|
-
import 'node:http';
|
|
7
6
|
import '@adonisjs/core/types/http';
|
|
8
7
|
import '@adonisjs/core/http';
|
|
9
8
|
import '@boringnode/bus/types/main';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ApplicationService } from '@adonisjs/core/types';
|
|
2
|
-
import { S as SocketService, C as ChannelRouter } from '../socket_service-
|
|
3
|
-
import { P as PresenceManager } from '../base_channel-
|
|
2
|
+
import { S as SocketService, C as ChannelRouter } from '../socket_service-to3tlbrV.js';
|
|
3
|
+
import { P as PresenceManager } from '../base_channel-DFyd3hVE.js';
|
|
4
4
|
import 'node:http';
|
|
5
5
|
import '@adonisjs/logger';
|
|
6
6
|
import 'emittery';
|
|
@@ -1019,7 +1019,7 @@ var SocketUpgrader = class _SocketUpgrader {
|
|
|
1019
1019
|
return true;
|
|
1020
1020
|
}
|
|
1021
1021
|
this.server.handleUpgrade(request, socket, head, (connection) => {
|
|
1022
|
-
accept(connection,
|
|
1022
|
+
accept(connection, result.accepted);
|
|
1023
1023
|
});
|
|
1024
1024
|
return true;
|
|
1025
1025
|
}
|
|
@@ -1087,7 +1087,7 @@ var SocketUpgrader = class _SocketUpgrader {
|
|
|
1087
1087
|
async #authenticate(httpContext) {
|
|
1088
1088
|
const config = this.config;
|
|
1089
1089
|
if (!config?.authenticate) {
|
|
1090
|
-
return {
|
|
1090
|
+
return {};
|
|
1091
1091
|
}
|
|
1092
1092
|
try {
|
|
1093
1093
|
const ctx = {
|
|
@@ -1098,7 +1098,6 @@ var SocketUpgrader = class _SocketUpgrader {
|
|
|
1098
1098
|
return null;
|
|
1099
1099
|
}
|
|
1100
1100
|
return {
|
|
1101
|
-
httpContext,
|
|
1102
1101
|
user: result
|
|
1103
1102
|
};
|
|
1104
1103
|
} catch (error) {
|
|
@@ -1411,8 +1410,8 @@ var SocketService = class extends Emittery {
|
|
|
1411
1410
|
(message, error) => this.#warn(message, error)
|
|
1412
1411
|
);
|
|
1413
1412
|
const upgradeHandler = (request, socket, head) => {
|
|
1414
|
-
upgrader.handle(request, socket, head, (connection,
|
|
1415
|
-
this.#handleConnection(connection,
|
|
1413
|
+
upgrader.handle(request, socket, head, (connection, accepted) => {
|
|
1414
|
+
this.#handleConnection(connection, accepted, inboundMessageConfig);
|
|
1416
1415
|
}).catch((error) => {
|
|
1417
1416
|
this.#warn("failed to upgrade socket connection: %s", error);
|
|
1418
1417
|
SocketUpgrader.reject(socket, 500, "Internal Server Error");
|
|
@@ -1450,12 +1449,12 @@ var SocketService = class extends Emittery {
|
|
|
1450
1449
|
throw error;
|
|
1451
1450
|
}
|
|
1452
1451
|
}
|
|
1453
|
-
#handleConnection(connection,
|
|
1452
|
+
#handleConnection(connection, upgrade, inboundMessageConfig) {
|
|
1454
1453
|
if (this.#lifecycle.status !== "ready") {
|
|
1455
1454
|
connection.terminate();
|
|
1456
1455
|
return;
|
|
1457
1456
|
}
|
|
1458
|
-
const socket = this.#wrapSocket(connection,
|
|
1457
|
+
const socket = this.#wrapSocket(connection, upgrade);
|
|
1459
1458
|
connectChannel.traceSync(
|
|
1460
1459
|
() => {
|
|
1461
1460
|
this.#sockets.set(socket.id, socket);
|
|
@@ -1628,13 +1627,11 @@ var SocketService = class extends Emittery {
|
|
|
1628
1627
|
}
|
|
1629
1628
|
return this.#channelSubscriptions.relayWhisper(socket, payload);
|
|
1630
1629
|
}
|
|
1631
|
-
#wrapSocket(connection,
|
|
1630
|
+
#wrapSocket(connection, upgrade) {
|
|
1632
1631
|
const raw = {
|
|
1633
1632
|
id: randomUUID2(),
|
|
1634
1633
|
data: {},
|
|
1635
|
-
connection
|
|
1636
|
-
request,
|
|
1637
|
-
httpContext: upgrade.httpContext
|
|
1634
|
+
connection
|
|
1638
1635
|
};
|
|
1639
1636
|
return {
|
|
1640
1637
|
id: raw.id,
|