@tapsioss/client-socket-manager
Version:
<div align="center">
1 lines • 58.7 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts","../src/ClientSocketManager.ts","../src/constants.ts","../src/devtool/devtool.ts","../src/devtool/constants.ts","../src/devtool/FixedQueue.ts","../src/devtool/ScrollPreservor.ts","../src/devtool/utils.ts","../src/utils.ts","../src/ClientSocketManagerStub.ts"],"sourcesContent":["export { default as ClientSocketManager } from \"./ClientSocketManager.ts\";\nexport { default as ClientSocketManagerStub } from \"./ClientSocketManagerStub.ts\";\nexport { ManagerReservedEvents, SocketReservedEvents } from \"./constants.ts\";\nexport type {\n ClientSocketManagerListenerOptions,\n ClientSocketManagerOptions,\n} from \"./types.ts\";\n","import { io, type Socket } from \"socket.io-client\";\nimport { ManagerReservedEvents, SocketReservedEvents } from \"./constants.ts\";\nimport { devtool } from \"./devtool/index.ts\";\nimport type {\n ClientSocketManagerListenerOptions,\n ClientSocketManagerOptions,\n DefaultEventsMap,\n EventNames,\n EventParams,\n EventsMap,\n SubscribeCallback,\n} from \"./types.ts\";\nimport { assertCallbackType, isBrowser, warnDisposedClient } from \"./utils.ts\";\n\nclass ClientSocketManager<\n ListenEvents extends EventsMap = DefaultEventsMap,\n EmitEvents extends EventsMap = ListenEvents,\n> {\n private _disposed = false;\n\n private _socket: Socket<ListenEvents, EmitEvents> | null = null;\n\n private _inputListeners: ClientSocketManagerListenerOptions = {};\n\n constructor(uri: string, options?: ClientSocketManagerOptions) {\n const {\n path = \"/socket.io\",\n reconnectionDelay = 500,\n reconnectionDelayMax = 2000,\n eventHandlers,\n devtool: devtoolOpt,\n ...restOptions\n } = options ?? {};\n\n const { enabled: devtoolEnabled = false, zIndex: devtoolZIndex = 999999 } =\n devtoolOpt ?? {};\n\n try {\n this._socket = io(uri, {\n ...restOptions,\n path,\n reconnectionDelay,\n reconnectionDelayMax,\n });\n this._inputListeners = eventHandlers ?? {};\n\n this._handleVisibilityChange = this._handleVisibilityChange.bind(this);\n\n this._attachPageEvents();\n this._attachSocketEvents();\n this._attachManagerEvents();\n\n this._inputListeners.onInit?.call(this);\n\n devtool.setZIndex(devtoolZIndex);\n if (devtoolEnabled) {\n this.showDevtool();\n }\n } catch (err) {\n // eslint-disable-next-line no-console\n console.error(\"Failed to initialize socket connection\", {\n uri,\n path,\n err,\n });\n }\n }\n\n private _attachPageEvents(): void {\n if (!isBrowser()) return;\n\n document.addEventListener(\"visibilitychange\", this._handleVisibilityChange);\n }\n\n private _attachSocketEvents(): void {\n if (!this._socket) return;\n\n this._socket.on(SocketReservedEvents.CONNECTION, () => {\n this._inputListeners.onSocketConnection?.call(this);\n\n devtool.update(s => {\n s.status = devtool.Status.CONNECTED;\n });\n });\n\n if (this._inputListeners.onSocketConnectionError) {\n this._socket.on(\n SocketReservedEvents.CONNECTION_ERROR,\n this._inputListeners.onSocketConnectionError.bind(this),\n );\n }\n\n this._socket.on(SocketReservedEvents.DISCONNECTION, (reason, details) => {\n this._inputListeners.onSocketDisconnection?.call(this, reason, details);\n\n devtool.update(s => {\n s.status = devtool.Status.DISCONNECTED;\n });\n\n if (!this.autoReconnectable) {\n if (reason === \"io server disconnect\") {\n this.connect();\n }\n }\n });\n }\n\n private _attachManagerEvents(): void {\n const manager = this._socket?.io;\n\n if (!manager) return;\n\n const {\n onServerPing,\n onConnectionError,\n onReconnecting,\n onReconnectingError,\n onReconnectionFailure,\n onSuccessfulReconnection,\n } = this._inputListeners;\n\n manager.on(ManagerReservedEvents.CONNECTION_ERROR, error => {\n onConnectionError?.call(this, error);\n devtool.update(s => {\n s.logs.enqueue({\n type: devtool.LogType.CONNECTION_ERROR,\n date: new Date(),\n detail: error.message,\n });\n });\n });\n\n if (onServerPing) {\n manager.on(ManagerReservedEvents.SERVER_PING, onServerPing.bind(this));\n }\n\n manager.on(ManagerReservedEvents.RECONNECTING, attempt => {\n onReconnecting?.call(this, attempt);\n devtool.update(s => {\n s.status = devtool.Status.RECONNECTING;\n s.logs.enqueue({\n type: devtool.LogType.RECONNECTING,\n date: new Date(),\n detail: `Reconnecting... (${attempt} attempt(s))`,\n });\n });\n });\n\n manager.on(ManagerReservedEvents.RECONNECTING_ERROR, error => {\n onReconnectingError?.call(this, error);\n devtool.update(s => {\n s.logs.enqueue({\n type: devtool.LogType.RECONNECTING_ERROR,\n date: new Date(),\n detail: error.message,\n });\n });\n });\n\n manager.on(ManagerReservedEvents.RECONNECTION_FAILURE, () => {\n onReconnectionFailure?.call(this);\n devtool.update(s => {\n s.logs.enqueue({\n type: devtool.LogType.RECONNECTION_FAILURE,\n date: new Date(),\n detail: `Failed to reconnect.`,\n });\n });\n });\n\n manager.on(ManagerReservedEvents.SUCCESSFUL_RECONNECTION, attempt => {\n onSuccessfulReconnection?.call(this, attempt);\n devtool.update(s => {\n s.logs.enqueue({\n type: devtool.LogType.SUCCESSFUL_RECONNECTION,\n date: new Date(),\n detail: `Successfully connected after ${attempt} attempt(s)`,\n });\n });\n });\n }\n\n private _detachPageEvents(): void {\n if (!isBrowser()) return;\n\n document.removeEventListener(\n \"visibilitychange\",\n this._handleVisibilityChange,\n );\n }\n\n private _detachSocketEvents(): void {\n this._socket?.off();\n }\n\n private _detachManagerEvents(): void {\n this._socket?.io.off();\n }\n\n private _handleVisibilityChange(): void {\n const isPageVisible = document.visibilityState === \"visible\";\n const isPageHidden = document.visibilityState === \"hidden\";\n\n if (!isPageVisible && !isPageHidden) return;\n\n if (isPageVisible) {\n this._inputListeners.onVisiblePage?.call(this);\n\n if (!this.connected) this.connect();\n } else {\n this._inputListeners.onHiddenPage?.call(this);\n\n this.disconnect();\n }\n }\n\n /**\n * Whether the client is disposed.\n */\n public get disposed() {\n return this._disposed;\n }\n\n /**\n * Emits an event to the socket identified by the channel name.\n */\n public emit<Ev extends EventNames<EmitEvents>>(\n channel: Ev,\n ...args: EventParams<EmitEvents, Ev>\n ) {\n warnDisposedClient(this.disposed);\n\n if (!this._socket) return;\n\n this._socket.emit(channel, ...args);\n }\n\n /**\n * A unique identifier for the session.\n *\n * `null` when the socket is not connected.\n */\n public get id(): string | null {\n warnDisposedClient(this.disposed);\n\n return this._socket?.id ?? null;\n }\n\n /**\n * Whether the socket is currently connected to the server.\n */\n public get connected(): boolean {\n warnDisposedClient(this.disposed);\n\n return this._socket?.connected ?? false;\n }\n\n /**\n * Whether the connection state was recovered after a temporary disconnection.\n * In that case, any missed packets will be transmitted by the server.\n */\n public get recovered(): boolean {\n warnDisposedClient(this.disposed);\n\n return this._socket?.recovered ?? false;\n }\n\n /**\n * Whether the Socket will try to reconnect when its Manager connects\n * or reconnects.\n */\n public get autoReconnectable(): boolean {\n warnDisposedClient(this.disposed);\n\n return this._socket?.active ?? false;\n }\n\n /**\n * Subscribes to a specified channel with a callback function.\n */\n public subscribe<Ev extends EventNames<ListenEvents>>(\n /**\n * The name of the channel to subscribe to.\n */\n channel: Ev,\n /**\n * The callback function to invoke when a message is received on the channel.\n */\n cb: ListenEvents[Ev],\n options?: {\n /**\n * The callback function to invoke when the subscription is complete.\n */\n onSubscriptionComplete?: (\n this: ClientSocketManager,\n channel: string,\n ) => void;\n /**\n * The `AbortSignal` to unsubscribe the listener upon abortion.\n */\n signal?: AbortSignal;\n },\n ): void {\n warnDisposedClient(this.disposed);\n\n if (!this._socket) return;\n\n assertCallbackType(\n cb,\n `Expected a valid callback function. Received \\`${typeof cb}\\`.`,\n );\n\n const { onSubscriptionComplete, signal } = options ?? {};\n\n const listener: SubscribeCallback = (...args) => {\n warnDisposedClient(this.disposed);\n\n if (!this._socket) return;\n\n this._inputListeners.onAnySubscribedMessageReceived?.call(\n this,\n channel,\n args,\n );\n\n (cb as SubscribeCallback).apply(this, args);\n };\n\n this._socket.on(channel, listener as ListenEvents[Ev]);\n\n const unsubscribe = () => {\n this.unsubscribe(channel, listener as ListenEvents[Ev]);\n\n signal?.removeEventListener(\"abort\", unsubscribe);\n };\n\n signal?.addEventListener(\"abort\", unsubscribe);\n\n if (signal?.aborted) unsubscribe();\n\n onSubscriptionComplete?.call(this, channel);\n\n devtool.update(s => {\n s.channels.add(channel);\n s.logs.enqueue({\n type: devtool.LogType.SUBSCRIBED,\n date: new Date(),\n detail: `subscribed to \\`${channel}\\` channel`,\n });\n });\n }\n\n /**\n * Removes the listener for the specified channel.\n * If no callback is provided, it removes all listeners for that channel.\n */\n public unsubscribe<Ev extends EventNames<ListenEvents>>(\n /**\n * The name of the channel whose listener should be deleted.\n */\n channel: Ev,\n /**\n * The subscriber callback function to remove.\n */\n cb?: ListenEvents[Ev],\n ): void {\n warnDisposedClient(this.disposed);\n\n if (!this._socket) return;\n\n if (cb) this._socket.off(channel, cb);\n else this._socket.off(channel);\n\n devtool.update(s => {\n s.channels.delete(channel);\n s.logs.enqueue({\n type: devtool.LogType.UNSUBSCRIBED,\n date: new Date(),\n detail: `unsubscribed from \\`${channel}\\` channel`,\n });\n });\n }\n\n /**\n * Manually connects/reconnects the socket.\n */\n public connect(): void {\n warnDisposedClient(this.disposed);\n\n this._socket?.connect();\n\n devtool.update(s => {\n s.logs.enqueue({\n type: devtool.LogType.CONNECTED,\n date: new Date(),\n detail: `socket was conneced manually`,\n });\n });\n }\n\n /**\n * Manually disconnects the socket.\n * In that case, the socket will not try to reconnect.\n *\n * If this is the last active Socket instance of the Manager,\n * the low-level connection will be closed.\n */\n public disconnect(): void {\n warnDisposedClient(this.disposed);\n\n this._socket?.disconnect();\n\n devtool.update(s => {\n s.logs.enqueue({\n type: devtool.LogType.DISCONNECTED,\n date: new Date(),\n detail: `socket was disconneced manually`,\n });\n });\n }\n\n /**\n * Disposes of the socket, manager, and engine, ensuring all connections are\n * closed and cleaned up.\n */\n public dispose(): void {\n warnDisposedClient(this.disposed);\n\n this._inputListeners.onDispose?.call(this);\n\n this._detachPageEvents();\n this._detachSocketEvents();\n this._detachManagerEvents();\n\n this.disconnect();\n this._socket?.io.engine.close();\n\n this._socket = null;\n this._inputListeners = {};\n this._disposed = true;\n\n devtool.dispose();\n }\n\n /**\n * Show devtool in the browser programmatically.\n */\n public showDevtool(): void {\n devtool.show();\n }\n\n /**\n * Hide devtool in the browser programmatically.\n */\n public hideDevtool(): void {\n devtool.hide();\n }\n}\n\nexport default ClientSocketManager;\n","export const SocketReservedEvents = {\n CONNECTION: \"connect\",\n CONNECTION_ERROR: \"connect_error\",\n DISCONNECTION: \"disconnect\",\n} as const;\n\nexport const ManagerReservedEvents = {\n SERVER_PING: \"ping\",\n CONNECTION_ERROR: \"error\",\n RECONNECTING: \"reconnect_attempt\",\n RECONNECTING_ERROR: \"reconnect_error\",\n RECONNECTION_FAILURE: \"reconnect_failed\",\n SUCCESSFUL_RECONNECTION: \"reconnect\",\n} as const;\n","import {\n closeIcon,\n DEVTOOL_BUTTON_ID,\n DEVTOOL_CHANNELS_ID,\n DEVTOOL_CLOSE_ICON_ID,\n DEVTOOL_ID,\n DEVTOOL_INFO_ID,\n DEVTOOL_LOGS_SECTION_ID,\n DEVTOOL_SOCKET_ICON_ID,\n DEVTOOL_STATUS_ID,\n DEVTOOL_WRAPPER_ID,\n LOG_CAPACITY,\n LogType,\n LogTypeColor,\n socketIcon,\n Status,\n StatusColorMap,\n} from \"./constants.ts\";\nimport { FixedQueue } from \"./FixedQueue.ts\";\nimport { ScrollPreservor } from \"./ScrollPreservor.ts\";\nimport { type DevtoolState, type Log } from \"./types.ts\";\nimport {\n formatDate,\n generateAttributes,\n generateInlineStyle,\n makeElementDraggable,\n} from \"./utils.ts\";\n\nconst buttonDefaultStyle = {\n border: \"none\",\n background: \"transparent\",\n cursor: \"pointer\",\n \"-webkit-tap-highlight-color\": \"transparent\",\n};\n\nconst baseThemeStyle = {\n color: \"#fff\",\n background: \"#000c\",\n \"backdrop-filter\": \"blur(0.25rem)\",\n \"box-shadow\": \"0 0 1.25rem 0.5rem #0005\",\n \"font-family\": \"monospace\",\n \"font-size\": \"0.75rem\",\n \"line-height\": \"2\",\n};\n\nconst nonAccessibleAttributes = {\n \"aria-hidden\": \"true\",\n tabindex: \"-1\",\n};\n\nconst devtool: DevtoolState = {\n status: Status.UNKNOWN,\n channels: new Set(),\n logs: new FixedQueue(LOG_CAPACITY),\n};\n\nlet active = false;\nlet expanded = false;\nlet zIndex: number = NaN;\n\nexport const renderDivider = () => {\n return `<hr color=\"#222222\" />`;\n};\n\nexport const renderChipGroup = (items: string[]) => {\n const chipStyle = generateInlineStyle({\n \"background-color\": \"#fff4\",\n \"border-radius\": \"999px\",\n padding: \"0 0.5rem\",\n \"list-style-type\": \"none\",\n overflow: \"hidden\",\n \"white-space\": \"nowrap\",\n \"text-overflow\": \"ellipsis\",\n \"overscroll-behavior-y\": \"contain\",\n });\n\n const chipGroupStyle = generateInlineStyle({\n margin: \"0.5rem 0 0\",\n display: \"flex\",\n gap: \"0.5rem\",\n padding: \"0\",\n \"flex-wrap\": \"wrap\",\n \"overflow-x\": \"hidden\",\n \"overflow-y\": \"auto\",\n \"max-height\": \"6rem\",\n });\n\n return `<ul id=\"${DEVTOOL_CHANNELS_ID}\" style=\"${chipGroupStyle}\">${items.map(item => `<li style=\"${chipStyle}\">${item}</li>`).join(\"\")}</ul>`;\n};\n\nexport const getDevtoolElement = () => document.getElementById(DEVTOOL_ID);\nexport const getDevtoolChannelsElement = () =>\n document.getElementById(DEVTOOL_CHANNELS_ID);\nexport const getDevtoolStatusElement = () =>\n document.getElementById(DEVTOOL_STATUS_ID);\nexport const getDevtoolLogSectionElement = () =>\n document.getElementById(DEVTOOL_LOGS_SECTION_ID);\nexport const getDevtoolWrapperElement = () =>\n document.getElementById(DEVTOOL_WRAPPER_ID);\nexport const getDevtoolInfoElement = () =>\n document.getElementById(DEVTOOL_INFO_ID);\nexport const getDevtoolIconElement = () =>\n document.getElementById(DEVTOOL_BUTTON_ID);\nexport const getDevtoolSocketIconElement = () =>\n document.getElementById(DEVTOOL_SOCKET_ICON_ID);\nexport const getDevtoolCloseIconElement = () =>\n document.getElementById(DEVTOOL_CLOSE_ICON_ID);\n\nconst channelsSectionScroll = new ScrollPreservor();\nconst logSectionScroll = new ScrollPreservor();\n\nexport const renderChannels = () => {\n const { channels } = devtool;\n\n if (channels.size === 0) return \"\";\n return `\n ${renderDivider()}\n <code>Channels:</code>\n ${renderChipGroup(Array.from(devtool.channels))}\n `;\n};\n\nexport const renderStatus = () => {\n const { status } = devtool;\n\n const color = StatusColorMap[status];\n\n const dotStyle = generateInlineStyle({\n display: \"inline-flex\",\n width: \"0.5rem\",\n height: \"0.5rem\",\n \"border-radius\": \"50%\",\n \"background-color\": color,\n \"box-shadow\": `0 0 1.25rem 0.125rem ${color}`,\n });\n\n return `<code>Status: <span id=\"${DEVTOOL_STATUS_ID}\">${status}</span> <span style=\"${dotStyle}\"></span></code>`;\n};\n\nexport const renderLog = (log: Log) => {\n const titleStyle = generateInlineStyle({\n color: LogTypeColor[log.type],\n margin: \"0\",\n });\n\n const detailStyle = generateInlineStyle({\n \"font-size\": \"0.625rem\",\n \"margin-top\": \"0\",\n color: LogTypeColor[log.type],\n \"overflow-wrap\": \"break-word\",\n });\n\n const timeStyle = generateInlineStyle({\n \"margin-bottom\": \"0\",\n \"font-size\": \"0.625rem\",\n color: \"#777\",\n });\n\n return `\n <div class=\"${DEVTOOL_LOGS_SECTION_ID}-item\">\n <p style=\"${timeStyle}\">${formatDate(log.date)}</p>\n <p style=\"${titleStyle}\">${log.type}</p>\n <p style=\"${detailStyle}\">${log.detail}</p>\n </div>\n `;\n};\n\nexport const renderLogs = () => {\n if (devtool.logs.length === 0) return \"\";\n\n const attributes = generateAttributes({\n id: DEVTOOL_LOGS_SECTION_ID,\n style: generateInlineStyle({\n \"max-height\": \"20rem\",\n overflow: \"auto\",\n \"overscroll-behavior-y\": \"contain\",\n }),\n });\n\n return `\n ${renderDivider()}\n <div ${attributes}>${devtool.logs.values.map(renderLog).join(\"\")}</div>\n `;\n};\n\nexport const renderDevtoolIconButton = () => {\n const attributes = generateAttributes({\n id: DEVTOOL_BUTTON_ID,\n \"data-testid\": DEVTOOL_BUTTON_ID,\n ...nonAccessibleAttributes,\n style: generateInlineStyle({\n ...buttonDefaultStyle,\n ...baseThemeStyle,\n width: \"3rem\",\n height: \"3rem\",\n display: \"flex\",\n \"justify-content\": \"center\",\n \"align-items\": \"center\",\n position: \"relative\",\n \"border-radius\": \"1.5rem\",\n }),\n });\n\n return `\n <button ${attributes}>\n ${socketIcon}\n ${closeIcon}\n </button>\n `;\n};\n\nexport const renderDevtoolInfo = () => {\n const attributes = generateAttributes({\n id: DEVTOOL_INFO_ID,\n \"data-open\": \"true\",\n \"data-testid\": DEVTOOL_INFO_ID,\n ...nonAccessibleAttributes,\n style: generateInlineStyle({\n ...baseThemeStyle,\n padding: \"1rem\",\n position: \"absolute\",\n background: \"#000c\",\n \"border-radius\": \"0.25rem 1rem 1rem 1rem\",\n top: \"0\",\n left: \"3.5rem\",\n opacity: \"0\",\n transform: \"scale(0)\",\n \"transform-origin\": \"0 0\",\n transition: \"opacity 0.2s, transform 0.2s\",\n width: \"14rem\",\n }),\n });\n\n return `<div ${attributes}></div>`;\n};\n\nexport const renderDevtool = () => {\n const attributes = generateAttributes({\n id: DEVTOOL_ID,\n \"data-testid\": DEVTOOL_ID,\n ...nonAccessibleAttributes,\n style: generateInlineStyle({\n position: \"relative\",\n \"box-sizing\": \"border-box\",\n }),\n });\n\n return `\n <div ${attributes}>\n ${renderDevtoolIconButton()}\n ${renderDevtoolInfo()}\n </div>\n `;\n};\n\nexport const updateInfoSection = () => {\n // Save scroll positions before updating the content\n logSectionScroll.save();\n channelsSectionScroll.save();\n\n const infoSection = getDevtoolInfoElement()!;\n\n const devtoolInfoStyle = generateInlineStyle({\n background: \"#000c\",\n padding: \"1rem\",\n \"margin-top\": \"0.5rem\",\n \"border-radius\": \"0.5rem\",\n });\n\n const headerStyle = generateInlineStyle({\n display: \"flex\",\n \"align-items\": \"center\",\n });\n\n infoSection.innerHTML = `\n <header style=\"${headerStyle}\">\n <b>Client Socket Manager</b>\n </header>\n <div style=\"${devtoolInfoStyle}\">\n ${renderStatus()}\n ${renderChannels()}\n ${renderLogs()}\n </div>\n `;\n\n logSectionScroll.setTarget(getDevtoolLogSectionElement());\n channelsSectionScroll.setTarget(getDevtoolChannelsElement());\n\n logSectionScroll.restore();\n channelsSectionScroll.restore();\n\n return infoSection;\n};\n\nexport const setZIndex = (z: number) => {\n zIndex = z;\n};\n\nexport const hide = () => {\n getDevtoolWrapperElement()?.remove();\n\n active = false;\n expanded = false;\n};\n\nexport const dispose = () => {\n update(s => {\n s.channels.clear();\n s.logs.clear();\n s.status = Status.UNKNOWN;\n });\n zIndex = NaN;\n hide();\n};\n\nconst toggle = () => {\n const socketIcon = getDevtoolSocketIconElement()!;\n const closeIcon = getDevtoolCloseIconElement()!;\n const info = getDevtoolInfoElement()!;\n\n expanded = !expanded;\n socketIcon.style.opacity = !expanded ? \"1\" : \"0\";\n closeIcon.style.opacity = expanded ? \"1\" : \"0\";\n info.style.opacity = expanded ? \"1\" : \"0\";\n info.style.transform = `scale(${expanded ? \"1\" : \"0\"})`;\n getDevtoolInfoElement()?.setAttribute(\n \"data-open\",\n expanded ? \"true\" : \"false\",\n );\n};\n\nexport const update = (cb: (s: typeof devtool) => void) => {\n cb?.(devtool);\n\n if (active) {\n updateInfoSection();\n }\n};\n\nexport const show = () => {\n if (active) return;\n\n active = true;\n\n const devtoolWrapper = document.createElement(\"div\");\n\n if (Number.isNaN(zIndex)) {\n throw new Error(\"No z-index was set for the devtool.\");\n } else {\n devtoolWrapper.style.zIndex = `${zIndex}`;\n }\n\n devtoolWrapper.style.position = \"fixed\";\n devtoolWrapper.style.top = \"8px\";\n devtoolWrapper.style.left = \"8px\";\n\n devtoolWrapper.id = DEVTOOL_WRAPPER_ID;\n devtoolWrapper.innerHTML = renderDevtool();\n\n document.body.appendChild(devtoolWrapper);\n\n const iconButton = getDevtoolIconElement();\n\n if (iconButton) {\n iconButton.addEventListener(\"click\", toggle);\n makeElementDraggable(iconButton, devtoolWrapper);\n }\n\n if (iconButton) {\n iconButton.addEventListener(\"click\", toggle);\n makeElementDraggable(iconButton, devtoolWrapper);\n }\n\n [DEVTOOL_CLOSE_ICON_ID, DEVTOOL_SOCKET_ICON_ID].forEach(icon => {\n const buttonIcon = document.getElementById(icon);\n\n if (buttonIcon) {\n buttonIcon.style.position = \"absolute\";\n buttonIcon.style.top = \"50%\";\n buttonIcon.style.left = \"50%\";\n buttonIcon.style.transform = \"translate(-50%, -50%)\";\n buttonIcon.style.transition = \"opacity 0.2s\";\n\n if (icon === DEVTOOL_CLOSE_ICON_ID) {\n buttonIcon.style.opacity = \"0\";\n }\n }\n });\n\n updateInfoSection();\n};\n\nexport { LogType, Status };\n","export const LOG_CAPACITY = 20;\nexport const DEVTOOL_ID = \"tapsi-socket-client-devtool\";\nexport const DEVTOOL_WRAPPER_ID = \"tapsi-socket-client-devtool-wrapper\";\nexport const DEVTOOL_BUTTON_ID = `${DEVTOOL_ID}-button`;\nexport const DEVTOOL_INFO_ID = `${DEVTOOL_ID}-info`;\nexport const DEVTOOL_STATUS_ID = `${DEVTOOL_INFO_ID}-status`;\nexport const DEVTOOL_CHANNELS_ID = `${DEVTOOL_INFO_ID}-channels`;\nexport const DEVTOOL_LOGS_SECTION_ID = `${DEVTOOL_INFO_ID}-logs`;\nexport const DEVTOOL_SOCKET_ICON_ID = `${DEVTOOL_BUTTON_ID}-socket`;\nexport const DEVTOOL_CLOSE_ICON_ID = `${DEVTOOL_BUTTON_ID}-close`;\n\nexport const StatusColor = {\n GREEN: \"#3fb950\",\n RED: \"#f85149\",\n YELLOW: \"#d29922\",\n GREY: \"#656c7699\",\n} as const;\n\nexport const Status = {\n RECONNECTING: \"RECONNECTING\",\n CONNECTED: \"CONNECTED\",\n DISCONNECTED: \"DISCONNECTED\",\n UNKNOWN: \"UNKNOWN\",\n} as const;\n\nexport const StatusColorMap: Record<\n (typeof Status)[keyof typeof Status],\n (typeof StatusColor)[keyof typeof StatusColor]\n> = {\n RECONNECTING: StatusColor.YELLOW,\n CONNECTED: StatusColor.GREEN,\n DISCONNECTED: StatusColor.RED,\n UNKNOWN: StatusColor.GREY,\n} as const;\n\nexport const LogType = {\n SUCCESSFUL_RECONNECTION: \"SUCCESSFUL_RECONNECTION\",\n RECONNECTION_FAILURE: \"RECONNECTION_FAILURE\",\n RECONNECTING: \"RECONNECTING\",\n CONNECTION_ERROR: \"CONNECTION_ERROR\",\n RECONNECTING_ERROR: \"RECONNECTING_ERROR\",\n SUBSCRIBED: \"SUBSCRIBED\",\n UNSUBSCRIBED: \"UNSUBSCRIBED\",\n CONNECTED: \"CONNECTED\",\n DISCONNECTED: \"DISCONNECTED\",\n} as const;\n\nexport const LogTypeColor: Record<\n (typeof LogType)[keyof typeof LogType],\n (typeof StatusColorMap)[keyof typeof StatusColorMap]\n> = {\n SUCCESSFUL_RECONNECTION: StatusColor.GREEN,\n SUBSCRIBED: StatusColor.GREEN,\n RECONNECTION_FAILURE: StatusColor.RED,\n RECONNECTING: StatusColor.YELLOW,\n CONNECTION_ERROR: StatusColor.RED,\n RECONNECTING_ERROR: StatusColor.RED,\n UNSUBSCRIBED: StatusColor.RED,\n CONNECTED: StatusColor.GREEN,\n DISCONNECTED: StatusColor.RED,\n} as const;\n\nexport const socketIcon = `\n<svg id=\"${DEVTOOL_SOCKET_ICON_ID}\" width=\"1rem\" height=\"1rem\" viewBox=\"0 -31.5 256 256\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n<path d=\"M192.440223,144.644612 L224.220111,144.644612 L224.220111,68.3393384 L188.415329,32.5345562 L165.943007,55.0068785 L192.440223,81.5040943 L192.440223,144.644612 L192.440223,144.644612 Z M224.303963,160.576482 L178.017688,160.576482 L113.451687,160.576482 L86.954471,134.079266 L98.1906322,122.843105 L120.075991,144.728464 L165.104487,144.728464 L120.746806,100.286931 L132.06682,88.9669178 L176.4245,133.324599 L176.4245,88.2961022 L154.622994,66.4945955 L165.775303,55.3422863 L110.684573,0 L56.3485097,0 L56.3485097,0 L0,0 L31.6960367,31.6960367 L31.6960367,31.7798886 L31.8637406,31.7798886 L97.4359646,31.7798886 L120.662954,55.0068785 L86.7029152,88.9669178 L63.4759253,65.7399279 L63.4759253,47.7117589 L31.6960367,47.7117589 L31.6960367,78.9046839 L86.7029152,133.911562 L64.3144448,156.300033 L100.119227,192.104815 L154.45529,192.104815 L256,192.104815 L256,192.104815 L224.303963,160.576482 L224.303963,160.576482 Z\" fill=\"currentcolor\">\n</path>\n</svg>\n`;\n\nexport const closeIcon = `\n<svg id=\"${DEVTOOL_CLOSE_ICON_ID}\" width=\"1rem\" height=\"1rem\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M5.29289 5.29289C5.68342 4.90237 6.31658 4.90237 6.70711 5.29289L12 10.5858L17.2929 5.29289C17.6834 4.90237 18.3166 4.90237 18.7071 5.29289C19.0976 5.68342 19.0976 6.31658 18.7071 6.70711L13.4142 12L18.7071 17.2929C19.0976 17.6834 19.0976 18.3166 18.7071 18.7071C18.3166 19.0976 17.6834 19.0976 17.2929 18.7071L12 13.4142L6.70711 18.7071C6.31658 19.0976 5.68342 19.0976 5.29289 18.7071C4.90237 18.3166 4.90237 17.6834 5.29289 17.2929L10.5858 12L5.29289 6.70711C4.90237 6.31658 4.90237 5.68342 5.29289 5.29289Z\" fill=\"currentcolor\"/>\n</svg>\n`;\n","/**\n * A fixed-length queue that automatically removes the oldest item\n * when the maximum length is exceeded.\n *\n * This data structure behaves like a FIFO queue but ensures it never\n * grows beyond the specified capacity.\n */\nexport class FixedQueue<T> {\n private _queue: T[] = [];\n private _maxLength: number;\n\n /**\n * Creates a FixedQueue instance with a maximum length.\n *\n * @param maxLength - The maximum number of items the queue can hold.\n * @throws Will throw an error if maxLength is not a positive integer.\n *\n * @example\n * ```ts\n * const queue = new FixedQueue<number>(3);\n * queue.enqueue(1);\n * queue.enqueue(2);\n * queue.enqueue(3);\n * console.log(queue.values); // [1, 2, 3]\n * queue.enqueue(4);\n * console.log(queue.values); // [2, 3, 4]\n * console.log(queue.length); // 3\n * ```\n */\n constructor(maxLength: number) {\n if (!Number.isInteger(maxLength) || maxLength <= 0) {\n throw new Error(\"maxLength must be a positive integer\");\n }\n\n this._maxLength = maxLength;\n }\n\n /**\n * Adds an item to the queue. If the queue exceeds its max length,\n * the oldest item is removed.\n *\n * @param item - The item to enqueue.\n */\n enqueue(item: T): void {\n if (this._queue.length >= this._maxLength) {\n this._queue.shift(); // Remove oldest\n }\n\n this._queue.push(item);\n }\n\n /**\n * Gets a copy of the queue's current values in insertion order.\n */\n get values(): T[] {\n return [...this._queue];\n }\n\n /**\n * Gets the current number of items in the queue.\n */\n get length(): number {\n return this._queue.length;\n }\n\n /**\n * Clears the queue.\n */\n clear(): void {\n this._queue = [];\n }\n}\n","export class ScrollPreservor {\n private _savedScrollPosition: number = 0;\n private _target: HTMLElement | null = null;\n\n constructor(options?: { target?: HTMLElement | null }) {\n const { target } = options ?? {};\n\n if (target) {\n this._target = target;\n }\n }\n\n /**\n * Sets the HTML element to monitor.\n * @param target The HTML element to monitor.\n */\n public setTarget(target: HTMLElement | null): void {\n if (!target) {\n return;\n }\n\n this._target = target;\n }\n\n public get savedScrollPosition(): number {\n return this._savedScrollPosition;\n }\n\n /**\n * Saves the current scroll position of the element.\n * This method stores the `scrollTop` value of the currently set element.\n */\n public save(): void {\n if (!this._target) {\n return;\n }\n\n this._savedScrollPosition = this._target.scrollTop;\n }\n\n /**\n * Restores the saved scroll position to the element.\n * This method sets the `scrollTop` value of the element to the previously saved position.\n */\n public restore(): void {\n if (!this._target) {\n return;\n }\n\n this._target.scrollTop = this._savedScrollPosition;\n }\n}\n","// Drag tracking state\nlet x = 0;\nlet y = 0;\nlet initialX = x;\nlet initialY = y;\nlet startX = 0;\nlet startY = 0;\nlet didMove = false;\nlet isDragStarted = false;\n\nexport const generateInlineStyle = (\n cssObject: Record<string, string>,\n): string => {\n return Object.entries(cssObject)\n .map(([cssKey, cssValue]) => `${cssKey}: ${cssValue}`)\n .join(\"; \");\n};\n\nexport const resetDragState = () => {\n x = 0;\n y = 0;\n initialX = 0;\n initialY = 0;\n didMove = false;\n isDragStarted = false;\n};\n\nexport const generateAttributes = (\n attrObject: Record<string, string | boolean>,\n): string => {\n return Object.entries(attrObject)\n .map(([key, value]) =>\n typeof value === \"boolean\" ? (value ? key : \"\") : `${key}=\"${value}\"`,\n )\n .filter(Boolean) // remove empty strings\n .join(\" \"); // no trailing space now\n};\n\nexport const getClientX = (event: MouseEvent | TouchEvent) => {\n if (event instanceof MouseEvent) return event.clientX;\n if (event instanceof TouchEvent) return event.touches[0]?.clientX ?? 0;\n return 0;\n};\n\nexport const getClientY = (event: MouseEvent | TouchEvent) => {\n if (event instanceof MouseEvent) return event.clientY;\n if (event instanceof TouchEvent) return event.touches[0]?.clientY ?? 0;\n return 0;\n};\n\n/**\n * Makes an HTML element draggable via mouse and touch gestures.\n *\n * This utility:\n * - Supports dragging using both mouse (desktop) and touch (mobile) events.\n * - Prevents synthetic click events from triggering after a drag.\n * - Prevents default browser gestures such as back/forward swipe on mobile.\n * - Applies `transform: translate(x, y)` to move the element instead of `top` and `left` for better performance\n *\n * Usage:\n * ```ts\n * const button = document.createElement('button');\n * button.textContent = 'Drag me';\n * document.body.appendChild(button);\n * makeElementDraggable(button);\n * ```\n *\n * Notes:\n * - The element must be absolutely or fixed-positioned, and should not be constrained by layout flow\n * - Internally sets `element.style.touchAction = 'none'` to disable browser-native touch gestures\n *\n * @param dragHandle - The drag handle.\n * @param dragTarget - The HTMLElement that will move when dragging with the handler.\n */\nexport const makeElementDraggable = (\n dragHandle: HTMLElement,\n dragTarget: HTMLElement,\n) => {\n // Suppress click after drag to avoid unintentional tap\n const suppressClickOnce = (e: MouseEvent) => {\n if (didMove) {\n e.stopPropagation();\n e.preventDefault();\n }\n };\n\n const handleDragStart = (event: MouseEvent | TouchEvent) => {\n const clientX = getClientX(event);\n const clientY = getClientY(event);\n\n startX = clientX;\n startY = clientY;\n initialX = x;\n initialY = y;\n didMove = false;\n isDragStarted = true;\n // Attach temporary click suppressor for post-drag click prevention\n document.addEventListener(\"click\", suppressClickOnce, true);\n };\n\n const handleDragging = (event: MouseEvent | TouchEvent) => {\n if (!isDragStarted) return;\n\n const clientX = getClientX(event);\n const clientY = getClientY(event);\n\n const deltaX = clientX - startX;\n const deltaY = clientY - startY;\n\n // Mark as moved only after threshold (avoid noise from small movements)\n if (!didMove && (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5)) {\n didMove = true;\n }\n\n if (didMove) {\n // Prevent native scrolling or gestures during real drag\n event.preventDefault();\n event.stopPropagation();\n\n x = initialX + deltaX;\n y = initialY + deltaY;\n\n dragTarget.style.transform = `translate(${x}px, ${y}px)`;\n }\n };\n\n const handleDragEnd = () => {\n if (!isDragStarted) return;\n\n isDragStarted = false;\n\n document.removeEventListener(\"click\", suppressClickOnce, true);\n };\n\n // Disable browser-native touch gestures (scroll, nav swipe)\n dragHandle.style.touchAction = \"none\";\n\n dragHandle.addEventListener(\"touchstart\", handleDragStart, false);\n dragHandle.addEventListener(\"mousedown\", handleDragStart, false);\n\n document.addEventListener(\"mousemove\", handleDragging, false);\n document.addEventListener(\"touchmove\", handleDragging, false);\n\n document.addEventListener(\"mouseup\", handleDragEnd, false);\n document.addEventListener(\"touchend\", handleDragEnd, false);\n};\n\nexport const formatDate = (date: Date) => {\n const hours = String(date.getHours()).padStart(2, \"0\");\n const minutes = String(date.getMinutes()).padStart(2, \"0\");\n const seconds = String(date.getSeconds()).padStart(2, \"0\");\n\n return `${hours}:${minutes}:${seconds}`;\n};\n","export const isBrowser = () => typeof document !== \"undefined\";\n\nexport const assertCallbackType = (cb: unknown, message: string) => {\n if (typeof cb !== \"function\") {\n throw new TypeError(`ClientSocketManager: ${message}`);\n }\n\n return true;\n};\n\nexport const warnDisposedClient = (isDisposed: boolean) => {\n if (!isDisposed) return;\n\n // eslint-disable-next-line no-console\n console.warn(\n [\n \"ClientSocketManager: Attempted to use a disposed client.\",\n \"Please reassign the client with a new instance.\",\n ].join(\" \"),\n );\n};\n","import type {\n ClientSocketManagerListenerOptions,\n ClientSocketManagerOptions,\n} from \"./types.ts\";\n\n/**\n * A stub implementation of `ClientSocketManager` intended for use in\n * test environments or server-side rendering (SSR), where actual socket\n * connections are unnecessary or undesired.\n *\n * Provides no-op methods and tracks basic connection/disposal state.\n */\nclass ClientSocketManagerStub {\n private _inputListeners: Partial<ClientSocketManagerListenerOptions>;\n\n private _connected = false;\n private _disposed = false;\n\n /**\n * Indicates this is a mock/stub implementation.\n */\n public static __mock__ = true;\n\n /**\n * Creates a new stubbed ClientSocketManager.\n *\n * @param _uri - Optional URI string, ignored in stub.\n * @param options - Optional configuration object containing event handlers.\n */\n constructor(_uri?: string, options?: Partial<ClientSocketManagerOptions>) {\n this._inputListeners = options?.eventHandlers ?? {};\n }\n\n /**\n * A static session identifier.\n * Returns a mock ID if connected, otherwise null.\n */\n public get id(): string | null {\n return this._connected ? \"__id__\" : null;\n }\n\n /**\n * Whether the stub is considered connected.\n */\n public get connected(): boolean {\n return this._connected;\n }\n\n /**\n * Whether the connection has been recovered after interruption.\n * Always returns false in the stub.\n */\n public get recovered(): boolean {\n return false;\n }\n\n /**\n * Whether the client attempts reconnection automatically.\n * Always returns false in the stub.\n */\n public get autoReconnectable(): boolean {\n return false;\n }\n\n /**\n * Whether this instance has been disposed.\n */\n public get disposed(): boolean {\n return this._disposed;\n }\n\n /**\n * Emits a message to the server.\n * No-op in stub.\n *\n * @param _args - Event name and payload, ignored in stub.\n */\n public emit(): void {}\n\n /**\n * Subscribes to a socket channel.\n * No-op in stub.\n *\n * @param _channel - Channel name.\n * @param _cb - Callback function.\n * @param _options - Optional configuration for signal and subscription completion.\n */\n public subscribe(\n _channel: string,\n _cb: () => void,\n _options?: {\n onSubscriptionComplete?: (channel: string) => void;\n signal?: AbortSignal;\n },\n ): void {}\n\n /**\n * Unsubscribes from a socket channel.\n * No-op in stub.\n *\n * @param _channel - Channel name.\n * @param _cb - Callback function to remove.\n */\n public unsubscribe(_channel: string, _cb: () => void): void {}\n\n /**\n * Simulates connecting to a socket.\n * Triggers the `onSocketConnection` event handler if defined.\n */\n public connect(): void {\n this._connected = true;\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any\n this._inputListeners.onSocketConnection?.call(this as any);\n }\n\n /**\n * Simulates disconnecting the socket.\n * Triggers the `onSocketDisconnection` event handler if defined.\n */\n public disconnect(): void {\n this._connected = false;\n\n this._inputListeners.onSocketDisconnection?.call(\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any\n this as any,\n \"io client disconnect\",\n );\n }\n\n /**\n * Cleans up the instance by disconnecting and clearing handlers.\n */\n public dispose(): void {\n this.disconnect();\n\n this._disposed = true;\n this._inputListeners = {};\n }\n\n /**\n * Show devtool in the browser programmatically.\n */\n public showDevtool(): void {}\n\n /**\n * Show devtool in the browser programmatically.\n */\n public hideDevtool(): void {}\n}\n\nexport default ClientSocketManagerStub;\n"],"mappings":"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