@rlanz/socket 0.0.1-5 → 0.0.1-7

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.
Files changed (64) hide show
  1. package/README.md +174 -160
  2. package/build/{types-C7Q36ryo.d.ts → base_channel-Dr40l7CW.d.ts} +178 -169
  3. package/build/chunk-2X6WGCH5.js +120 -0
  4. package/build/chunk-2X6WGCH5.js.map +1 -0
  5. package/build/chunk-6PJ4ALJD.js +115 -0
  6. package/build/chunk-6PJ4ALJD.js.map +1 -0
  7. package/build/chunk-E6ILCRL3.js +182 -0
  8. package/build/chunk-E6ILCRL3.js.map +1 -0
  9. package/build/{chunk-SHH6U4CI.js → chunk-G75J233Z.js} +1 -1
  10. package/build/chunk-G75J233Z.js.map +1 -0
  11. package/build/{chunk-B4Y3TNDI.js → chunk-IUQ3GYZZ.js} +5 -24
  12. package/build/chunk-IUQ3GYZZ.js.map +1 -0
  13. package/build/chunk-QNOOQXYY.js +899 -0
  14. package/build/chunk-QNOOQXYY.js.map +1 -0
  15. package/build/{chunk-ANT2E3LT.js → chunk-YVC7IMDN.js} +21 -5
  16. package/build/chunk-YVC7IMDN.js.map +1 -0
  17. package/build/framework-B-4kGmAw.d.ts +13 -0
  18. package/build/index-Fg-tkHhP.d.ts +150 -0
  19. package/build/index.d.ts +23 -12
  20. package/build/index.js +12 -14
  21. package/build/index.js.map +1 -1
  22. package/build/providers/socket_provider.d.ts +2 -2
  23. package/build/providers/socket_provider.js +474 -215
  24. package/build/providers/socket_provider.js.map +1 -1
  25. package/build/services/socket.d.ts +2 -2
  26. package/build/{socket_service-mybqv1hj.d.ts → socket_service-BYEB8LIN.d.ts} +7 -7
  27. package/build/src/assembler_hook.d.ts +6 -4
  28. package/build/src/assembler_hook.js +49 -63
  29. package/build/src/assembler_hook.js.map +1 -1
  30. package/build/src/client/index.d.ts +3 -147
  31. package/build/src/client/index.js +4 -837
  32. package/build/src/client/index.js.map +1 -1
  33. package/build/src/client/react.d.ts +13 -5
  34. package/build/src/client/react.js +27 -11
  35. package/build/src/client/react.js.map +1 -1
  36. package/build/src/client/types.d.ts +30 -35
  37. package/build/src/client/vue.d.ts +17 -5
  38. package/build/src/client/vue.js +31 -13
  39. package/build/src/client/vue.js.map +1 -1
  40. package/build/src/decorators.d.ts +3 -2
  41. package/build/src/decorators.js +1 -1
  42. package/build/src/health_check.d.ts +2 -2
  43. package/build/src/health_check.js +14 -3
  44. package/build/src/health_check.js.map +1 -1
  45. package/build/src/otel.js +0 -1
  46. package/build/src/otel.js.map +1 -1
  47. package/build/src/testing.d.ts +3 -12
  48. package/build/src/testing.js +1 -1
  49. package/build/src/types/tracing_channels.d.ts +1 -2
  50. package/build/src/types.d.ts +1 -1
  51. package/package.json +2 -9
  52. package/build/chunk-ANT2E3LT.js.map +0 -1
  53. package/build/chunk-B4Y3TNDI.js.map +0 -1
  54. package/build/chunk-HK7Z65DA.js +0 -19
  55. package/build/chunk-HK7Z65DA.js.map +0 -1
  56. package/build/chunk-SFAY2ZA4.js +0 -28
  57. package/build/chunk-SFAY2ZA4.js.map +0 -1
  58. package/build/chunk-SHH6U4CI.js.map +0 -1
  59. package/build/chunk-YAX5EHHB.js +0 -453
  60. package/build/chunk-YAX5EHHB.js.map +0 -1
  61. package/build/framework-CvxgHwbv.d.ts +0 -12
  62. package/build/src/client/svelte.d.ts +0 -22
  63. package/build/src/client/svelte.js +0 -82
  64. package/build/src/client/svelte.js.map +0 -1
@@ -0,0 +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\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 #active = false\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 #listenersAttached = false\n #pendingSubscribe: {\n promise: Promise<Channel<Contract>>\n resolve: (channel: Channel<Contract>) => void\n reject: (error: Error) => void\n token: number\n } | null = null\n #subscribeToken = 0\n #managedSubscriptions = 0\n #pendingUnsubscribe: Promise<void> | null = null\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.#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) return this.#pendingUnsubscribe ?? Promise.resolve()\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.#pendingUnsubscribe) {\n await this.#pendingUnsubscribe\n }\n\n if (!this.subscribed) return this\n\n if (this.#pendingSubscribe) {\n await this.#pendingSubscribe.promise\n return 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 const deferred = Promise.withResolvers<Channel<Contract>>()\n\n this.#pendingSubscribe = {\n promise: deferred.promise,\n resolve: deferred.resolve,\n reject: deferred.reject,\n token,\n }\n\n const { resolve, reject } = this.#pendingSubscribe\n\n const timer = setTimeout(() => {\n if (token !== this.#subscribeToken) return\n this.#subscribeToken++\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 = 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 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 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.#pendingUnsubscribe) return this.#pendingUnsubscribe\n\n const hadPendingSubscribe = this.#pendingSubscribe !== null\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 if (!hadPendingSubscribe && !this.#active) {\n this.#cleanupTerminal()\n return Promise.resolve()\n }\n\n this.#active = false\n const pending = new Promise<void>((resolve) => {\n const timeout = options.timeout ?? 5000\n const timer = setTimeout(() => {\n resolve()\n }, timeout)\n\n this.#transport\n .sendRequest({ type: 'unsubscribe', channel: this.name }, timeout)\n .then(() => {\n clearTimeout(timer)\n resolve()\n })\n .catch(() => {\n clearTimeout(timer)\n resolve()\n })\n })\n this.#pendingUnsubscribe = pending\n void pending.finally(() => {\n if (this.#pendingUnsubscribe !== pending) return\n this.#pendingUnsubscribe = null\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 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: 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.#active = false\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 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 #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.#resetConnectionState()\n this.#detachTransportListeners()\n this.#clearCallbacks()\n }\n}\n","import type { ConnectionState, EventHandler } from './types.js'\nimport 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 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<Callable>()\n #eventHandlers = new Map<string, Set<Callable>>()\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 let setupFailed = false\n const connectPromise = new Promise<void>((resolve, reject) => {\n let ws: WebSocket\n try {\n ws = this.#createWebSocket(this.#buildUrl())\n } catch (error) {\n setupFailed = true\n reject(error)\n return\n }\n\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 if (settled || this.#ws !== ws) {\n return\n }\n\n this.#setState('connected')\n if (settled || this.#ws !== ws || !this.connected) {\n return\n }\n\n settled = true\n cleanup()\n this.#reconnectAttempts = 0\n this.#emitLocal('connect')\n if (this.#onConnected) {\n this.#callHandler(this.#onConnected)\n }\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) => {\n if (this.#ws === ws) {\n this.#handleIncoming(event.data)\n }\n })\n ws.addEventListener('close', handleClose)\n })\n\n this.#connectPromise = connectPromise\n if (setupFailed) {\n this.#connectPromise = null\n this.#setState('disconnected')\n }\n\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 const ws = this.#ws\n const shouldEmitDisconnect = this.#state !== 'disconnected'\n this.#ws = null\n this.#setState('disconnected')\n if (shouldEmitDisconnect) {\n this.#emitLocal('disconnect')\n }\n 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)\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 ws = this.#ws\n if (ws?.readyState !== WebSocket.OPEN) {\n return\n }\n\n ws.send(JSON.stringify(message))\n }\n\n sendRequest(message: Record<string, unknown>, timeout = 5000): Promise<unknown> {\n const ws = this.#ws\n if (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 = { ...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 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): 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 typeof message.ok === 'boolean' &&\n (message.error === undefined || typeof message.error === 'string')\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(): void {\n const shouldEmitDisconnect = this.#state !== 'disconnected'\n\n this.#ws = null\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(() => {\n if (this.#state === 'disconnected') {\n this.#scheduleReconnect()\n }\n })\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) => this.#callHandler(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) => this.#callHandler(handler, state))\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 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\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":[]}
@@ -1,8 +1,20 @@
1
1
  import {
2
- callClientHandler
3
- } from "./chunk-SFAY2ZA4.js";
2
+ buildChannelName,
3
+ callClientHandler,
4
+ resolveChannel
5
+ } from "./chunk-QNOOQXYY.js";
4
6
 
5
7
  // src/client/framework.ts
8
+ function createChannelCallArgs(pattern, parameters) {
9
+ return parameters === void 0 ? [pattern] : [pattern, parameters];
10
+ }
11
+ function resolveChannelEventArgs(args) {
12
+ return {
13
+ parameterInput: args.length === 3 ? args[0] : void 0,
14
+ event: args.at(-2),
15
+ handler: args.at(-1)
16
+ };
17
+ }
6
18
  var socketEntries = /* @__PURE__ */ new WeakMap();
7
19
  var socketOwners = /* @__PURE__ */ new WeakMap();
8
20
  function reportSubscriptionError(error) {
@@ -21,7 +33,7 @@ function getEntry(socket, name) {
21
33
  entries = /* @__PURE__ */ new Map();
22
34
  socketEntries.set(socket, entries);
23
35
  }
24
- const channel = socket.channel(name);
36
+ const channel = socket[resolveChannel](name);
25
37
  let entry = entries.get(name);
26
38
  if (!entry || entry.channel !== channel) {
27
39
  entry = {
@@ -87,7 +99,9 @@ var ChannelLease = class {
87
99
  });
88
100
  }
89
101
  };
90
- function acquireChannel(socket, name) {
102
+ function acquireChannel(socket, ...args) {
103
+ const [pattern, parameters] = args;
104
+ const name = buildChannelName(pattern, parameters);
91
105
  const entry = getEntry(socket, name);
92
106
  const lease = new ChannelLease(entry);
93
107
  entry.leases.add(lease);
@@ -130,7 +144,9 @@ function acquireSocketOwnership(socket) {
130
144
  }
131
145
 
132
146
  export {
147
+ createChannelCallArgs,
148
+ resolveChannelEventArgs,
133
149
  acquireChannel,
134
150
  acquireSocketOwnership
135
151
  };
136
- //# sourceMappingURL=chunk-ANT2E3LT.js.map
152
+ //# sourceMappingURL=chunk-YVC7IMDN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client/framework.ts"],"sourcesContent":["import type { Channel } from './channel.js'\nimport { buildChannelName, resolveChannel, type Socket } from './socket.js'\nimport type {\n ChannelCallArgs,\n ChannelContract,\n ChannelContractFor,\n ChannelParameters,\n ChannelPattern,\n EventHandler,\n} from './types.js'\nimport { callClientHandler } from './callback.js'\n\ntype AnySocket = Socket<any>\ntype ServerEvents<Contract> =\n Contract extends ChannelContract<unknown, infer Events> ? Events : unknown\n\ntype FrameworkChannelMember<Registry, Pattern extends string> =\n ChannelContractFor<Registry, Pattern> extends infer Contract\n ? [Contract] extends [never]\n ? never\n : Channel<Contract>\n : never\n\nexport type FrameworkChannel<Registry, Pattern extends string> = FrameworkChannelMember<\n Registry,\n Pattern\n>\n\nexport type FrameworkChannelPattern<Registry> = ChannelPattern<Registry>\nexport type FrameworkChannelCallArgs<Registry> = ChannelCallArgs<Registry>\nexport type FrameworkChannelParameters<\n Registry,\n Pattern extends ChannelPattern<Registry>,\n> = ChannelParameters<Registry, Pattern>\n\nexport function createChannelCallArgs<Registry>(\n pattern: ChannelPattern<Registry>,\n parameters?: object\n): ChannelCallArgs<Registry> {\n return (parameters === undefined ? [pattern] : [pattern, parameters]) as ChannelCallArgs<Registry>\n}\n\n/** Resolves the shared positional arguments accepted by framework channel-event helpers. @internal */\nexport function resolveChannelEventArgs<ParameterInput, Event extends string, Payload>(\n args: readonly unknown[]\n): {\n parameterInput: ParameterInput | undefined\n event: Event\n handler: EventHandler<Payload>\n} {\n return {\n parameterInput: (args.length === 3 ? args[0] : undefined) as ParameterInput | undefined,\n event: args.at(-2) as Event,\n handler: args.at(-1) as EventHandler<Payload>,\n }\n}\n\nexport type FrameworkChannelEvent<Registry, Pattern extends string> =\n FrameworkChannel<Registry, Pattern> extends Channel<infer Contract>\n ? Extract<keyof ServerEvents<Contract>, string>\n : never\n\nexport type FrameworkChannelEventPayload<\n Registry,\n Pattern extends string,\n Event extends FrameworkChannelEvent<Registry, Pattern>,\n> =\n FrameworkChannel<Registry, Pattern> extends Channel<infer Contract>\n ? Event extends keyof ServerEvents<Contract>\n ? ServerEvents<Contract>[Event]\n : never\n : never\n\ninterface ManagedEvent {\n handlers: Set<EventHandler<any>>\n dispatch: EventHandler<any>\n}\n\ninterface ManagedEntry {\n channel: Channel<any>\n socket: AnySocket\n leases: Set<ChannelLease>\n events: Map<string, ManagedEvent>\n lifecycle: { ready: Promise<Channel<any>>; release: () => Promise<void> } | undefined\n}\n\nconst socketEntries = new WeakMap<AnySocket, Map<string, ManagedEntry>>()\nconst socketOwners = new WeakMap<AnySocket, { count: number; generation: number }>()\n\nfunction reportSubscriptionError(error: unknown): void {\n queueMicrotask(() => {\n console.error(error)\n })\n}\n\nfunction attachEvents(entry: ManagedEntry): void {\n for (const [event, managed] of entry.events) {\n entry.channel.$listen(event, managed.dispatch)\n }\n}\n\nfunction getEntry(socket: AnySocket, name: string): ManagedEntry {\n let entries = socketEntries.get(socket)\n if (!entries) {\n entries = new Map()\n socketEntries.set(socket, entries)\n }\n\n const channel = socket[resolveChannel](name)\n let entry = entries.get(name)\n if (!entry || entry.channel !== channel) {\n entry = {\n channel,\n socket,\n leases: new Set(),\n events: new Map(),\n lifecycle: undefined,\n }\n entries.set(name, entry)\n }\n\n return entry\n}\n\nexport class ChannelLease {\n readonly entry: ManagedEntry\n #released = false\n #listeners: Array<{ event: string; handler: EventHandler<any> }> = []\n\n constructor(entry: ManagedEntry) {\n this.entry = entry\n }\n\n listen(event: string, handler: EventHandler<any>): void {\n if (this.#released) return\n\n let managed = this.entry.events.get(event)\n if (!managed) {\n const handlers = new Set<EventHandler<any>>()\n managed = {\n handlers,\n dispatch: (data) => {\n for (const listener of handlers) callClientHandler(listener, data)\n },\n }\n this.entry.events.set(event, managed)\n this.entry.channel.$listen(event, managed.dispatch)\n }\n const registration: EventHandler<any> = (data) => handler(data)\n managed.handlers.add(registration)\n this.#listeners.push({ event, handler: registration })\n }\n\n release(): void {\n if (this.#released) return\n this.#released = true\n\n for (const { event, handler } of this.#listeners) {\n const managed = this.entry.events.get(event)\n managed?.handlers.delete(handler)\n if (managed?.handlers.size === 0) {\n this.entry.channel.$stopListening(event, managed.dispatch)\n this.entry.events.delete(event)\n }\n }\n this.#listeners = []\n this.entry.leases.delete(this)\n\n queueMicrotask(() => {\n if (this.entry.leases.size > 0) return\n const lifecycle = this.entry.lifecycle\n this.entry.lifecycle = undefined\n void lifecycle?.release().then(() => {\n if (this.entry.leases.size > 0 || this.entry.lifecycle) return\n const entries = socketEntries.get(this.entry.socket)\n if (entries?.get(this.entry.channel.name) === this.entry) {\n entries.delete(this.entry.channel.name)\n }\n })\n })\n }\n}\n\nexport function acquireChannel<Registry, const Args extends ChannelCallArgs<Registry>>(\n socket: Socket<Registry>,\n ...args: Args\n): { channel: FrameworkChannel<Registry, Args[0]>; lease: ChannelLease } {\n const [pattern, parameters] = args as [string, object?]\n const name = buildChannelName(pattern, parameters)\n const entry = getEntry(socket, name)\n const lease = new ChannelLease(entry)\n entry.leases.add(lease)\n\n if (!entry.lifecycle) {\n const lifecycle = entry.channel.$acquire()\n entry.lifecycle = lifecycle\n attachEvents(entry)\n void lifecycle.ready\n .then(() => {\n if (entry.lifecycle === lifecycle) attachEvents(entry)\n })\n .catch((error) => {\n if (entry.lifecycle === lifecycle && entry.leases.size > 0 && entry.socket.connected) {\n reportSubscriptionError(error)\n }\n })\n }\n\n return { channel: entry.channel as FrameworkChannel<Registry, Args[0]>, lease }\n}\n\n/** Connects an exclusively owned socket and coalesces development-mode remounts. */\nexport function acquireSocketOwnership(socket: AnySocket): () => void {\n let owner = socketOwners.get(socket)\n if (!owner) {\n owner = { count: 0, generation: 0 }\n socketOwners.set(socket, owner)\n socket.connect().catch(reportSubscriptionError)\n }\n owner.count++\n owner.generation++\n\n let released = false\n return () => {\n if (released) return\n released = true\n owner.count--\n const generation = ++owner.generation\n queueMicrotask(() => {\n if (owner.count === 0 && owner.generation === generation) {\n socket.disconnect()\n socketOwners.delete(socket)\n }\n })\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,13 @@
1
+ import { C as Channel } from './index-Fg-tkHhP.js';
2
+ import { ChannelCallArgs, ChannelContractFor, ChannelPattern, ChannelContract, ChannelParameters } from './src/client/types.js';
3
+
4
+ type ServerEvents<Contract> = Contract extends ChannelContract<unknown, infer Events> ? Events : unknown;
5
+ type FrameworkChannelMember<Registry, Pattern extends string> = ChannelContractFor<Registry, Pattern> extends infer Contract ? [Contract] extends [never] ? never : Channel<Contract> : never;
6
+ type FrameworkChannel<Registry, Pattern extends string> = FrameworkChannelMember<Registry, Pattern>;
7
+ type FrameworkChannelPattern<Registry> = ChannelPattern<Registry>;
8
+ type FrameworkChannelCallArgs<Registry> = ChannelCallArgs<Registry>;
9
+ type FrameworkChannelParameters<Registry, Pattern extends ChannelPattern<Registry>> = ChannelParameters<Registry, Pattern>;
10
+ type FrameworkChannelEvent<Registry, Pattern extends string> = FrameworkChannel<Registry, Pattern> extends Channel<infer Contract> ? Extract<keyof ServerEvents<Contract>, string> : never;
11
+ type FrameworkChannelEventPayload<Registry, Pattern extends string, Event extends FrameworkChannelEvent<Registry, Pattern>> = FrameworkChannel<Registry, Pattern> extends Channel<infer Contract> ? Event extends keyof ServerEvents<Contract> ? ServerEvents<Contract>[Event] : never : never;
12
+
13
+ export type { FrameworkChannelCallArgs as F, FrameworkChannel as a, FrameworkChannelPattern as b, FrameworkChannelEvent as c, FrameworkChannelParameters as d, FrameworkChannelEventPayload as e };
@@ -0,0 +1,150 @@
1
+ import { SocketClientTransport, PresenceUser, EventHandler, ChannelContract, SubscribeOptions, UnsubscribeOptions, SocketOptions, ConnectionState, ChannelCallArgs, ChannelContractFor } from './src/client/types.js';
2
+ import './base_channel-Dr40l7CW.js';
3
+
4
+ /**
5
+ * Represents a subscribed channel.
6
+ * Chainable API inspired by Laravel Echo.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * socket.channel('chat/:roomId', { roomId: 'general' })
11
+ * .here((users) => setOnlineUsers(users))
12
+ * .joining((user) => console.log(`${user.name} joined`))
13
+ * .leaving((user) => console.log(`${user.name} left`))
14
+ * .listen('chat:message', (msg) => addMessage(msg))
15
+ * .subscribe()
16
+ * ```
17
+ */
18
+ type ClientMap<C> = C extends ChannelContract<infer E, unknown> ? E : unknown;
19
+ type ServerMap<C> = C extends ChannelContract<unknown, infer E> ? E : unknown;
20
+ type IsUnknown<T> = unknown extends T ? true : false;
21
+ type StringKey<T> = Extract<keyof T, string>;
22
+ type EventDataArgs<Payload> = undefined extends Payload ? [data?: Payload] : [data: Payload];
23
+ declare class Channel<Contract> {
24
+ #private;
25
+ /**
26
+ * Channel name.
27
+ */
28
+ readonly name: string;
29
+ constructor(name: string, transport: SocketClientTransport);
30
+ /**
31
+ * Whether the channel should stay subscribed.
32
+ */
33
+ get subscribed(): boolean;
34
+ /** Whether a framework lifecycle currently owns this channel. @internal */
35
+ get $managed(): boolean;
36
+ /**
37
+ * Whether the channel is active on the server.
38
+ */
39
+ get active(): boolean;
40
+ /**
41
+ * List of present users.
42
+ */
43
+ get users(): PresenceUser[];
44
+ /**
45
+ * Number of present users.
46
+ */
47
+ get count(): number;
48
+ /**
49
+ * Registers a handler called with the present users.
50
+ * Called immediately on subscribe and after each presence change.
51
+ */
52
+ here(handler: EventHandler<PresenceUser[]>): this;
53
+ /**
54
+ * Registers a handler called when a user joins.
55
+ */
56
+ joining(handler: EventHandler<PresenceUser>): this;
57
+ /**
58
+ * Registers a handler called when a user leaves.
59
+ */
60
+ leaving(handler: EventHandler<PresenceUser>): this;
61
+ /**
62
+ * Listens for an event on this channel.
63
+ */
64
+ listen<T = unknown>(this: IsUnknown<ServerMap<Contract>> extends true ? Channel<Contract> : never, event: string, handler: EventHandler<T>): this;
65
+ listen<Event extends StringKey<ServerMap<Contract>>>(this: Channel<Contract>, event: Event, handler: EventHandler<ServerMap<Contract>[Event]>): this;
66
+ /**
67
+ * Removes an event listener.
68
+ */
69
+ stopListening<T = unknown>(this: IsUnknown<ServerMap<Contract>> extends true ? Channel<Contract> : never, event: string, handler?: EventHandler<T>): this;
70
+ stopListening<Event extends StringKey<ServerMap<Contract>>>(this: Channel<Contract>, event: Event, handler?: EventHandler<ServerMap<Contract>[Event]>): this;
71
+ /** Registers a lifecycle-owned event handler. @internal */
72
+ $listen(event: string, handler: EventHandler<any>): void;
73
+ /** Removes a lifecycle-owned event handler. @internal */
74
+ $stopListening(event: string, handler: EventHandler<any>): void;
75
+ /**
76
+ * Listens for a client event whispered by another channel member.
77
+ */
78
+ listenForWhisper<T = unknown>(event: string, handler: EventHandler<T>): this;
79
+ /**
80
+ * Removes a whispered client event listener.
81
+ */
82
+ stopListeningForWhisper<T = unknown>(event: string, handler?: EventHandler<T>): this;
83
+ /**
84
+ * Subscribes to the channel.
85
+ *
86
+ * client listeners -> subscribe ack -> active channel
87
+ * | | |
88
+ * v v v
89
+ * events ready presence sync send/listen
90
+ */
91
+ subscribe(options?: SubscribeOptions): Promise<this>;
92
+ /** Acquires subscription intent without overriding a direct consumer. @internal */
93
+ $acquire(options?: SubscribeOptions): {
94
+ ready: Promise<Channel<Contract>>;
95
+ release: () => Promise<void>;
96
+ };
97
+ /** Restores desired subscriptions after the transport reconnects. @internal */
98
+ $resubscribe(options?: SubscribeOptions): Promise<this>;
99
+ /**
100
+ * Unsubscribes from the channel.
101
+ */
102
+ unsubscribe(options?: UnsubscribeOptions): Promise<void>;
103
+ /**
104
+ * Sends an event to the server.
105
+ */
106
+ send<T = unknown>(this: IsUnknown<ClientMap<Contract>> extends true ? Channel<Contract> : never, event: string, data?: T): this;
107
+ send<Event extends StringKey<ClientMap<Contract>>>(this: Channel<Contract>, event: Event, ...[data]: ClientMap<Contract>[Event] extends {
108
+ payload: infer Payload;
109
+ } ? EventDataArgs<Payload> : never): this;
110
+ /**
111
+ * Relays a client event to other members of this channel.
112
+ */
113
+ whisper<T = unknown>(event: string, data?: T): this;
114
+ /**
115
+ * Sends an event and waits for a response.
116
+ */
117
+ sendWithAck<T = unknown, Result = unknown>(this: IsUnknown<ClientMap<Contract>> extends true ? Channel<Contract> : never, event: string, data?: T): Promise<Result>;
118
+ sendWithAck<Event extends StringKey<ClientMap<Contract>>>(this: Channel<Contract>, event: Event, ...[data]: ClientMap<Contract>[Event] extends {
119
+ payload: infer Payload;
120
+ } ? EventDataArgs<Payload> : never): Promise<ClientMap<Contract>[Event] extends {
121
+ ack: infer Acknowledgement;
122
+ } ? Acknowledgement : unknown>;
123
+ }
124
+
125
+ declare const resolveChannel: unique symbol;
126
+ /**
127
+ * Main Socket client.
128
+ */
129
+ type IsUntypedRegistry<Registry> = unknown extends Registry ? true : [Registry] extends [undefined] ? true : false;
130
+ type SocketChannelArgs<Registry> = IsUntypedRegistry<Registry> extends true ? [name: string] : ChannelCallArgs<Registry>;
131
+ type ChannelResult<Registry, Pattern extends string> = IsUntypedRegistry<Registry> extends true ? Channel<ChannelContract> : Channel<ChannelContractFor<Registry, Pattern>>;
132
+ declare class Socket<Registry = undefined> implements SocketClientTransport {
133
+ #private;
134
+ constructor(options?: SocketOptions);
135
+ get state(): ConnectionState;
136
+ get connected(): boolean;
137
+ connect(): Promise<void>;
138
+ disconnect(): void;
139
+ channel<const Args extends SocketChannelArgs<Registry>>(...args: Args): ChannelResult<Registry, Args[0]>;
140
+ /** Resolves a channel for framework adapters after their public type checks. @internal */
141
+ [resolveChannel](name: string): Channel<any>;
142
+ leave(name: string): Promise<void>;
143
+ onStateChange(handler: EventHandler<ConnectionState>): () => void;
144
+ on<T = unknown>(event: string, handler: EventHandler<T>): () => void;
145
+ off<T = unknown>(event: string, handler: EventHandler<T>): void;
146
+ send(message: Record<string, unknown>): void;
147
+ sendRequest<T = unknown>(message: Record<string, unknown>, timeout?: number): Promise<T>;
148
+ }
149
+
150
+ export { Channel as C, Socket as S };
package/build/index.d.ts CHANGED
@@ -1,19 +1,30 @@
1
- export { C as ChannelRouter } from './socket_service-mybqv1hj.js';
2
- import { S as SocketConfig, d as SocketAuthenticationHandler } from './types-C7Q36ryo.js';
3
- export { A as AuthenticatedSocket, B as BaseChannel, e as BaseChannelConstructor, b as ChannelAck, f as ChannelHandlerFn, C as ChannelHandlerMap, g as ChannelMatch, c as ChannelMessage, h as ChannelOptions, a as ChannelServerEventsOf, D as Duration, M as Middleware, i as MiddlewareClass, j as MiddlewareContext, k as MiddlewareFn, l as PresenceInfo, P as PresenceManager, m as PresenceMember, R as RawSocket, n as SocketAuthenticationResult, o as SocketEvents, p as SocketHealthSnapshot, q as SocketHttpMiddleware, r as SocketResponseError, s as SocketServiceStatus, t as SocketUpgradeContext } from './types-C7Q36ryo.js';
4
- export { SocketHealthCheck } from './src/health_check.js';
5
- export { SocketFake, SocketFakeCountQuery, SocketFakeEmission, SocketFakeEmissionQuery, SocketFakeTarget } from './src/testing.js';
6
- import 'node:http';
7
- import '@adonisjs/logger';
8
- import 'emittery';
1
+ import { S as SocketAuthenticationHandler, c as SocketConfig } from './base_channel-Dr40l7CW.js';
2
+ export { A as AuthenticatedSocket, B as BaseChannel, a as ChannelAck, b as ChannelMessage, d as ChannelOptions, C as ChannelServerEventsOf, D as Duration, M as Middleware, e as MiddlewareClass, f as MiddlewareContext, g as MiddlewareFn, h as PresenceInfo, i as PresenceMember, R as RawSocket, j as SocketAuthenticationResult, k as SocketEvents, l as SocketHealthSnapshot, m as SocketHttpMiddleware, n as SocketOriginResolver, o as SocketOriginValues, p as SocketResponseError, q as SocketRetryQueueConfig, r as SocketServiceStatus, s as SocketTransportConfig, t as SocketUpgradeContext } from './base_channel-Dr40l7CW.js';
3
+ import ConfigureCommand from '@adonisjs/core/commands/configure';
9
4
  import 'ws';
10
- import '@adonisjs/core/http';
5
+ import 'node:http';
11
6
  import '@adonisjs/core/types/http';
7
+ import '@adonisjs/core/http';
12
8
  import '@boringnode/bus/types/main';
13
- import '@adonisjs/core/health';
14
9
 
15
- declare function defineConfig<User = unknown, Config extends SocketConfig<User> = SocketConfig<User>>(config: Config): Config;
10
+ type WebSocketConfig<User> = NonNullable<SocketConfig<User>['websocket']>;
11
+ type AuthenticatedUserOf<Authenticate extends SocketAuthenticationHandler> = Exclude<Awaited<ReturnType<Authenticate>>, false | null>;
12
+ type AuthenticatedSocketConfig<Authenticate extends SocketAuthenticationHandler> = Omit<SocketConfig<AuthenticatedUserOf<Authenticate>>, 'websocket'> & {
13
+ websocket: Omit<WebSocketConfig<AuthenticatedUserOf<Authenticate>>, 'authenticate'> & {
14
+ authenticate: Authenticate;
15
+ };
16
+ };
17
+ type UnauthenticatedSocketConfig = Omit<SocketConfig<unknown>, 'websocket'> & {
18
+ websocket?: Omit<WebSocketConfig<unknown>, 'authenticate'> & {
19
+ authenticate?: never;
20
+ };
21
+ };
22
+ declare function defineConfig<Authenticate extends SocketAuthenticationHandler>(config: AuthenticatedSocketConfig<Authenticate>): SocketConfig<AuthenticatedUserOf<Authenticate>>;
23
+ declare function defineConfig(config: UnauthenticatedSocketConfig): SocketConfig<unknown>;
16
24
 
17
25
  declare function authenticateWithAdonisAuth<User = unknown>(): SocketAuthenticationHandler<User>;
18
26
 
19
- export { SocketAuthenticationHandler, SocketConfig, authenticateWithAdonisAuth, defineConfig };
27
+ /** Configures the Socket provider and Assembler hook in an AdonisJS application. */
28
+ declare function configure(command: Pick<ConfigureCommand, 'createCodemods'>): Promise<void>;
29
+
30
+ export { SocketAuthenticationHandler, SocketConfig, authenticateWithAdonisAuth, configure, defineConfig };
package/build/index.js CHANGED
@@ -1,16 +1,8 @@
1
1
  import {
2
2
  BaseChannel,
3
- ChannelRouter,
4
- PresenceManager,
5
3
  SocketResponseError
6
- } from "./chunk-YAX5EHHB.js";
7
- import "./chunk-SHH6U4CI.js";
8
- import {
9
- SocketHealthCheck
10
- } from "./chunk-HK7Z65DA.js";
11
- import {
12
- SocketFake
13
- } from "./chunk-B4Y3TNDI.js";
4
+ } from "./chunk-2X6WGCH5.js";
5
+ import "./chunk-G75J233Z.js";
14
6
 
15
7
  // src/define_config.ts
16
8
  function defineConfig(config) {
@@ -34,14 +26,20 @@ function authenticateWithAdonisAuth() {
34
26
  return authenticator.getUserOrFail();
35
27
  };
36
28
  }
29
+
30
+ // configure.ts
31
+ async function configure(command) {
32
+ const codemods = await command.createCodemods();
33
+ await codemods.updateRcFile((rcFile) => {
34
+ rcFile.addProvider("@rlanz/socket/provider", ["web"]);
35
+ rcFile.addAssemblerHook("init", "@rlanz/socket/assembler_hook");
36
+ });
37
+ }
37
38
  export {
38
39
  BaseChannel,
39
- ChannelRouter,
40
- PresenceManager,
41
- SocketFake,
42
- SocketHealthCheck,
43
40
  SocketResponseError,
44
41
  authenticateWithAdonisAuth,
42
+ configure,
45
43
  defineConfig
46
44
  };
47
45
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/define_config.ts","../src/adonis_auth.ts"],"sourcesContent":["import type { SocketConfig } from './types.js'\n\nexport function defineConfig<\n User = unknown,\n Config extends SocketConfig<User> = SocketConfig<User>,\n>(config: Config): Config {\n return config\n}\n","import type { SocketAuthenticationHandler } from './types.js'\n\ninterface AdonisAuthenticator {\n authenticateUsing: () => unknown\n getUserOrFail: () => unknown\n}\n\nexport function authenticateWithAdonisAuth<User = unknown>(): SocketAuthenticationHandler<User> {\n return async (ctx) => {\n const auth: unknown = Reflect.get(ctx.httpContext, 'auth')\n if (\n typeof auth !== 'object' ||\n auth === null ||\n typeof Reflect.get(auth, 'authenticateUsing') !== 'function' ||\n typeof Reflect.get(auth, 'getUserOrFail') !== 'function'\n ) {\n throw new Error(\n 'AdonisJS Auth must be initialized by websocket middleware before authenticating sockets'\n )\n }\n\n const authenticator: AdonisAuthenticator = {\n authenticateUsing: Reflect.get(auth, 'authenticateUsing').bind(auth),\n getUserOrFail: Reflect.get(auth, 'getUserOrFail').bind(auth),\n }\n await authenticator.authenticateUsing()\n\n // The configured Adonis authenticator owns the application's User type.\n return authenticator.getUserOrFail() as User\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAEO,SAAS,aAGd,QAAwB;AACxB,SAAO;AACT;;;ACAO,SAAS,6BAAgF;AAC9F,SAAO,OAAO,QAAQ;AACpB,UAAM,OAAgB,QAAQ,IAAI,IAAI,aAAa,MAAM;AACzD,QACE,OAAO,SAAS,YAChB,SAAS,QACT,OAAO,QAAQ,IAAI,MAAM,mBAAmB,MAAM,cAClD,OAAO,QAAQ,IAAI,MAAM,eAAe,MAAM,YAC9C;AACA,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,UAAM,gBAAqC;AAAA,MACzC,mBAAmB,QAAQ,IAAI,MAAM,mBAAmB,EAAE,KAAK,IAAI;AAAA,MACnE,eAAe,QAAQ,IAAI,MAAM,eAAe,EAAE,KAAK,IAAI;AAAA,IAC7D;AACA,UAAM,cAAc,kBAAkB;AAGtC,WAAO,cAAc,cAAc;AAAA,EACrC;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/define_config.ts","../src/adonis_auth.ts","../configure.ts"],"sourcesContent":["import type { SocketAuthenticationHandler, SocketConfig } from './types.js'\n\ntype WebSocketConfig<User> = NonNullable<SocketConfig<User>['websocket']>\n\ntype AuthenticatedUserOf<Authenticate extends SocketAuthenticationHandler> = Exclude<\n Awaited<ReturnType<Authenticate>>,\n false | null\n>\n\ntype AuthenticatedSocketConfig<Authenticate extends SocketAuthenticationHandler> = Omit<\n SocketConfig<AuthenticatedUserOf<Authenticate>>,\n 'websocket'\n> & {\n websocket: Omit<WebSocketConfig<AuthenticatedUserOf<Authenticate>>, 'authenticate'> & {\n authenticate: Authenticate\n }\n}\n\ntype UnauthenticatedSocketConfig = Omit<SocketConfig<unknown>, 'websocket'> & {\n websocket?: Omit<WebSocketConfig<unknown>, 'authenticate'> & {\n authenticate?: never\n }\n}\n\nexport function defineConfig<Authenticate extends SocketAuthenticationHandler>(\n config: AuthenticatedSocketConfig<Authenticate>\n): SocketConfig<AuthenticatedUserOf<Authenticate>>\nexport function defineConfig(config: UnauthenticatedSocketConfig): SocketConfig<unknown>\nexport function defineConfig(config: unknown): unknown {\n return config\n}\n","import type { SocketAuthenticationHandler } from './types.js'\n\ninterface AdonisAuthenticator {\n authenticateUsing: () => unknown\n getUserOrFail: () => unknown\n}\n\nexport function authenticateWithAdonisAuth<User = unknown>(): SocketAuthenticationHandler<User> {\n return async (ctx) => {\n const auth: unknown = Reflect.get(ctx.httpContext, 'auth')\n if (\n typeof auth !== 'object' ||\n auth === null ||\n typeof Reflect.get(auth, 'authenticateUsing') !== 'function' ||\n typeof Reflect.get(auth, 'getUserOrFail') !== 'function'\n ) {\n throw new Error(\n 'AdonisJS Auth must be initialized by websocket middleware before authenticating sockets'\n )\n }\n\n const authenticator: AdonisAuthenticator = {\n authenticateUsing: Reflect.get(auth, 'authenticateUsing').bind(auth),\n getUserOrFail: Reflect.get(auth, 'getUserOrFail').bind(auth),\n }\n await authenticator.authenticateUsing()\n\n // The configured Adonis authenticator owns the application's User type.\n return authenticator.getUserOrFail() as User\n }\n}\n","import type ConfigureCommand from '@adonisjs/core/commands/configure'\n\n/** Configures the Socket provider and Assembler hook in an AdonisJS application. */\nexport async function configure(command: Pick<ConfigureCommand, 'createCodemods'>): Promise<void> {\n const codemods = await command.createCodemods()\n\n await codemods.updateRcFile((rcFile) => {\n rcFile.addProvider('@rlanz/socket/provider', ['web'])\n rcFile.addAssemblerHook('init', '@rlanz/socket/assembler_hook')\n })\n}\n"],"mappings":";;;;;;;AA4BO,SAAS,aAAa,QAA0B;AACrD,SAAO;AACT;;;ACvBO,SAAS,6BAAgF;AAC9F,SAAO,OAAO,QAAQ;AACpB,UAAM,OAAgB,QAAQ,IAAI,IAAI,aAAa,MAAM;AACzD,QACE,OAAO,SAAS,YAChB,SAAS,QACT,OAAO,QAAQ,IAAI,MAAM,mBAAmB,MAAM,cAClD,OAAO,QAAQ,IAAI,MAAM,eAAe,MAAM,YAC9C;AACA,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,UAAM,gBAAqC;AAAA,MACzC,mBAAmB,QAAQ,IAAI,MAAM,mBAAmB,EAAE,KAAK,IAAI;AAAA,MACnE,eAAe,QAAQ,IAAI,MAAM,eAAe,EAAE,KAAK,IAAI;AAAA,IAC7D;AACA,UAAM,cAAc,kBAAkB;AAGtC,WAAO,cAAc,cAAc;AAAA,EACrC;AACF;;;AC3BA,eAAsB,UAAU,SAAkE;AAChG,QAAM,WAAW,MAAM,QAAQ,eAAe;AAE9C,QAAM,SAAS,aAAa,CAAC,WAAW;AACtC,WAAO,YAAY,0BAA0B,CAAC,KAAK,CAAC;AACpD,WAAO,iBAAiB,QAAQ,8BAA8B;AAAA,EAChE,CAAC;AACH;","names":[]}
@@ -1,6 +1,6 @@
1
1
  import { ApplicationService } from '@adonisjs/core/types';
2
- import { S as SocketService, C as ChannelRouter } from '../socket_service-mybqv1hj.js';
3
- import { P as PresenceManager } from '../types-C7Q36ryo.js';
2
+ import { S as SocketService, C as ChannelRouter } from '../socket_service-BYEB8LIN.js';
3
+ import { P as PresenceManager } from '../base_channel-Dr40l7CW.js';
4
4
  import 'node:http';
5
5
  import '@adonisjs/logger';
6
6
  import 'emittery';