partysocket
Version:
A better WebSocket that Just Works™
1 lines • 24.8 kB
Source Map (JSON)
{"version":3,"file":"ws.cjs","names":[],"sources":["../src/ws.ts"],"sourcesContent":["// TODO: lose this eslint-disable\n\n/*!\n * Reconnecting WebSocket\n * by Pedro Ladaria <pedro.ladaria@gmail.com>\n * https://github.com/pladaria/reconnecting-websocket\n * License MIT\n */\n\nimport type { TypedEventTarget } from \"./type-helper\";\n\nif (!globalThis.EventTarget || !globalThis.Event) {\n console.error(`\n PartySocket requires a global 'EventTarget' class to be available!\n You can polyfill this global by adding this to your code before any partysocket imports: \n \n \\`\\`\\`\n import 'partysocket/event-target-polyfill';\n \\`\\`\\`\n Please file an issue at https://github.com/partykit/partykit if you're still having trouble.\n`);\n}\n\nexport class ErrorEvent extends Event {\n public message: string;\n public error: Error;\n // oxlint-disable-next-line no-explicit-any\n constructor(error: Error, target: any) {\n super(\"error\", target);\n this.message = error.message;\n this.error = error;\n }\n}\n\nexport class CloseEvent extends Event {\n public code: number;\n public reason: string;\n public wasClean = true;\n // oxlint-disable-next-line no-explicit-any\n constructor(code = 1000, reason = \"\", target: any) {\n super(\"close\", target);\n this.code = code;\n this.reason = reason;\n }\n}\nexport interface WebSocketEventMap {\n close: CloseEvent;\n error: ErrorEvent;\n message: MessageEvent;\n open: Event;\n}\n\nconst Events = {\n Event,\n ErrorEvent,\n CloseEvent\n};\n\nfunction assert(condition: unknown, msg?: string): asserts condition {\n if (!condition) {\n throw new Error(msg);\n }\n}\n\nfunction cloneEventBrowser(e: Event) {\n // oxlint-disable-next-line no-explicit-any\n return new (e as any).constructor(e.type, e) as Event;\n}\n\nfunction cloneEventNode(e: Event) {\n if (\"data\" in e) {\n const evt = new MessageEvent(e.type, e);\n return evt;\n }\n\n if (\"code\" in e || \"reason\" in e) {\n const evt = new CloseEvent(\n // @ts-expect-error we need to fix event/listener types\n (e.code || 1999) as number,\n // @ts-expect-error we need to fix event/listener types\n (e.reason || \"unknown reason\") as string,\n e\n );\n return evt;\n }\n\n if (\"error\" in e) {\n const evt = new ErrorEvent(e.error as Error, e);\n return evt;\n }\n\n const evt = new Event(e.type, e);\n return evt;\n}\n\nconst isNode =\n typeof process !== \"undefined\" &&\n typeof process.versions?.node !== \"undefined\";\n\n// React Native has process and document polyfilled but not process.versions.node\n// It needs Node-style event cloning because browser-style cloning produces\n// events that fail instanceof Event checks in event-target-polyfill\n// See: https://github.com/cloudflare/partykit/issues/257\nconst isReactNative =\n typeof navigator !== \"undefined\" && navigator.product === \"ReactNative\";\n\nconst cloneEvent = isNode || isReactNative ? cloneEventNode : cloneEventBrowser;\n\nexport type Options = {\n // oxlint-disable-next-line no-explicit-any\n WebSocket?: any;\n maxReconnectionDelay?: number;\n minReconnectionDelay?: number;\n reconnectionDelayGrowFactor?: number;\n minUptime?: number;\n connectionTimeout?: number;\n maxRetries?: number;\n maxEnqueuedMessages?: number;\n startClosed?: boolean;\n debug?: boolean;\n // oxlint-disable-next-line no-explicit-any\n debugLogger?: (...args: any[]) => void;\n};\n\nconst DEFAULT = {\n maxReconnectionDelay: 10000,\n minReconnectionDelay: 1000 + Math.random() * 4000,\n minUptime: 5000,\n reconnectionDelayGrowFactor: 1.3,\n connectionTimeout: 4000,\n maxRetries: Number.POSITIVE_INFINITY,\n maxEnqueuedMessages: Number.POSITIVE_INFINITY,\n startClosed: false,\n debug: false\n};\n\nlet didWarnAboutMissingWebSocket = false;\n\nexport type UrlProvider = string | (() => string) | (() => Promise<string>);\nexport type ProtocolsProvider =\n | null\n | string\n | string[]\n | (() => string | string[] | null)\n | (() => Promise<string | string[] | null>);\n\nexport type Message = string | ArrayBuffer | Blob | ArrayBufferView;\n\nexport default class ReconnectingWebSocket extends (EventTarget as TypedEventTarget<WebSocketEventMap>) {\n private _ws: WebSocket | undefined;\n private _retryCount = -1;\n private _uptimeTimeout: ReturnType<typeof setTimeout> | undefined;\n private _connectTimeout: ReturnType<typeof setTimeout> | undefined;\n private _shouldReconnect = true;\n private _connectLock = false;\n private _binaryType: BinaryType = \"blob\";\n private _closeCalled = false;\n private _messageQueue: Message[] = [];\n\n private _debugLogger = console.log.bind(console);\n\n protected _url: UrlProvider;\n protected _protocols?: ProtocolsProvider;\n protected _options: Options;\n\n constructor(\n url: UrlProvider,\n protocols?: ProtocolsProvider,\n options: Options = {}\n ) {\n super();\n this._url = url;\n this._protocols = protocols;\n this._options = options;\n if (this._options.startClosed) {\n this._shouldReconnect = false;\n }\n if (this._options.debugLogger) {\n this._debugLogger = this._options.debugLogger;\n }\n this._connect();\n }\n\n static get CONNECTING() {\n return 0;\n }\n static get OPEN() {\n return 1;\n }\n static get CLOSING() {\n return 2;\n }\n static get CLOSED() {\n return 3;\n }\n\n get CONNECTING() {\n return ReconnectingWebSocket.CONNECTING;\n }\n get OPEN() {\n return ReconnectingWebSocket.OPEN;\n }\n get CLOSING() {\n return ReconnectingWebSocket.CLOSING;\n }\n get CLOSED() {\n return ReconnectingWebSocket.CLOSED;\n }\n\n get binaryType() {\n return this._ws ? this._ws.binaryType : this._binaryType;\n }\n\n set binaryType(value: BinaryType) {\n this._binaryType = value;\n if (this._ws) {\n this._ws.binaryType = value;\n }\n }\n\n /**\n * Returns the number or connection retries\n */\n get retryCount(): number {\n return Math.max(this._retryCount, 0);\n }\n\n /**\n * The number of bytes of data that have been queued using calls to send() but not yet\n * transmitted to the network. This value resets to zero once all queued data has been sent.\n * This value does not reset to zero when the connection is closed; if you keep calling send(),\n * this will continue to climb. Read only\n */\n get bufferedAmount(): number {\n const bytes = this._messageQueue.reduce((acc, message) => {\n if (typeof message === \"string\") {\n acc += message.length; // not byte size\n } else if (message instanceof Blob) {\n acc += message.size;\n } else {\n acc += message.byteLength;\n }\n return acc;\n }, 0);\n return bytes + (this._ws ? this._ws.bufferedAmount : 0);\n }\n\n /**\n * The extensions selected by the server. This is currently only the empty string or a list of\n * extensions as negotiated by the connection\n */\n get extensions(): string {\n return this._ws ? this._ws.extensions : \"\";\n }\n\n /**\n * A string indicating the name of the sub-protocol the server selected;\n * this will be one of the strings specified in the protocols parameter when creating the\n * WebSocket object\n */\n get protocol(): string {\n return this._ws ? this._ws.protocol : \"\";\n }\n\n /**\n * The current state of the connection; this is one of the Ready state constants\n */\n get readyState(): number {\n if (this._ws) {\n return this._ws.readyState;\n }\n return this._options.startClosed\n ? ReconnectingWebSocket.CLOSED\n : ReconnectingWebSocket.CONNECTING;\n }\n\n /**\n * The URL as resolved by the constructor\n */\n get url(): string {\n return this._ws ? this._ws.url : \"\";\n }\n\n /**\n * Whether the websocket object is now in reconnectable state\n */\n get shouldReconnect(): boolean {\n return this._shouldReconnect;\n }\n\n /**\n * An event listener to be called when the WebSocket connection's readyState changes to CLOSED\n */\n public onclose: ((event: CloseEvent) => void) | null = null;\n\n /**\n * An event listener to be called when an error occurs\n */\n public onerror: ((event: ErrorEvent) => void) | null = null;\n\n /**\n * An event listener to be called when a message is received from the server\n */\n public onmessage: ((event: MessageEvent) => void) | null = null;\n\n /**\n * An event listener to be called when the WebSocket connection's readyState changes to OPEN;\n * this indicates that the connection is ready to send and receive data\n */\n public onopen: ((event: Event) => void) | null = null;\n\n /**\n * Closes the WebSocket connection or connection attempt, if any. If the connection is already\n * CLOSED, this method does nothing\n */\n public close(code = 1000, reason?: string) {\n this._closeCalled = true;\n this._shouldReconnect = false;\n this._clearTimeouts();\n if (!this._ws) {\n this._debug(\"close enqueued: no ws instance\");\n return;\n }\n if (this._ws.readyState === this.CLOSED) {\n this._debug(\"close: already closed\");\n return;\n }\n this._ws.close(code, reason);\n }\n\n /**\n * Closes the WebSocket connection or connection attempt and connects again.\n * Resets retry counter;\n */\n public reconnect(code?: number, reason?: string) {\n this._shouldReconnect = true;\n this._closeCalled = false;\n this._retryCount = -1;\n if (!this._ws || this._ws.readyState === this.CLOSED) {\n this._connect();\n } else {\n this._disconnect(code, reason);\n this._connect();\n }\n }\n\n /**\n * Enqueue specified data to be transmitted to the server over the WebSocket connection\n */\n public send(data: Message) {\n if (this._ws && this._ws.readyState === this.OPEN) {\n this._debug(\"send\", data);\n this._ws.send(data);\n } else {\n const { maxEnqueuedMessages = DEFAULT.maxEnqueuedMessages } =\n this._options;\n if (this._messageQueue.length < maxEnqueuedMessages) {\n this._debug(\"enqueue\", data);\n this._messageQueue.push(data);\n }\n }\n }\n\n private _debug(...args: unknown[]) {\n if (this._options.debug) {\n this._debugLogger(\"RWS>\", ...args);\n }\n }\n\n private _getNextDelay() {\n const {\n reconnectionDelayGrowFactor = DEFAULT.reconnectionDelayGrowFactor,\n minReconnectionDelay = DEFAULT.minReconnectionDelay,\n maxReconnectionDelay = DEFAULT.maxReconnectionDelay\n } = this._options;\n let delay = 0;\n if (this._retryCount > 0) {\n delay =\n minReconnectionDelay *\n reconnectionDelayGrowFactor ** (this._retryCount - 1);\n if (delay > maxReconnectionDelay) {\n delay = maxReconnectionDelay;\n }\n }\n this._debug(\"next delay\", delay);\n return delay;\n }\n\n private _wait(): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, this._getNextDelay());\n });\n }\n\n private _getNextProtocols(\n protocolsProvider: ProtocolsProvider | null\n ): Promise<string | string[] | null> {\n if (!protocolsProvider) return Promise.resolve(null);\n\n if (\n typeof protocolsProvider === \"string\" ||\n Array.isArray(protocolsProvider)\n ) {\n return Promise.resolve(protocolsProvider);\n }\n\n if (typeof protocolsProvider === \"function\") {\n const protocols = protocolsProvider();\n if (!protocols) return Promise.resolve(null);\n\n if (typeof protocols === \"string\" || Array.isArray(protocols)) {\n return Promise.resolve(protocols);\n }\n\n // @ts-expect-error redundant check\n if (protocols.then) {\n return protocols;\n }\n }\n\n throw Error(\"Invalid protocols\");\n }\n\n private _getNextUrl(urlProvider: UrlProvider): Promise<string> {\n if (typeof urlProvider === \"string\") {\n return Promise.resolve(urlProvider);\n }\n if (typeof urlProvider === \"function\") {\n const url = urlProvider();\n if (typeof url === \"string\") {\n return Promise.resolve(url);\n }\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-expect-error\n if (url.then) {\n return url;\n }\n\n // return url;\n }\n throw Error(\"Invalid URL\");\n }\n\n private _connect() {\n if (this._connectLock || !this._shouldReconnect) {\n return;\n }\n this._connectLock = true;\n\n const {\n maxRetries = DEFAULT.maxRetries,\n connectionTimeout = DEFAULT.connectionTimeout\n } = this._options;\n\n if (this._retryCount >= maxRetries) {\n this._debug(\"max retries reached\", this._retryCount, \">=\", maxRetries);\n this._connectLock = false;\n return;\n }\n\n this._retryCount++;\n\n this._debug(\"connect\", this._retryCount);\n this._removeListeners();\n\n this._wait()\n .then(() =>\n Promise.all([\n this._getNextUrl(this._url),\n this._getNextProtocols(this._protocols || null)\n ])\n )\n .then(([url, protocols]) => {\n // close could be called before creating the ws\n if (this._closeCalled) {\n this._connectLock = false;\n return;\n }\n if (\n !this._options.WebSocket &&\n typeof WebSocket === \"undefined\" &&\n !didWarnAboutMissingWebSocket\n ) {\n console.error(`‼️ No WebSocket implementation available. You should define options.WebSocket. \n\nFor example, if you're using node.js, run \\`npm install ws\\`, and then in your code:\n\nimport PartySocket from 'partysocket';\nimport WS from 'ws';\n\nconst partysocket = new PartySocket({\n host: \"127.0.0.1:1999\",\n room: \"test-room\",\n WebSocket: WS\n});\n\n`);\n didWarnAboutMissingWebSocket = true;\n }\n const WS: typeof WebSocket = this._options.WebSocket || WebSocket;\n this._debug(\"connect\", { url, protocols });\n this._ws = protocols ? new WS(url, protocols) : new WS(url);\n\n this._ws.binaryType = this._binaryType;\n this._connectLock = false;\n this._addListeners();\n\n this._connectTimeout = setTimeout(\n () => this._handleTimeout(),\n connectionTimeout\n );\n })\n // via https://github.com/pladaria/reconnecting-websocket/pull/166\n .catch((err) => {\n this._connectLock = false;\n this._handleError(new Events.ErrorEvent(Error(err.message), this));\n });\n }\n\n private _handleTimeout() {\n this._debug(\"timeout event\");\n this._handleError(new Events.ErrorEvent(Error(\"TIMEOUT\"), this));\n }\n\n private _disconnect(code = 1000, reason?: string) {\n this._clearTimeouts();\n if (!this._ws) {\n return;\n }\n this._removeListeners();\n try {\n if (\n this._ws.readyState === this.OPEN ||\n this._ws.readyState === this.CONNECTING\n ) {\n this._ws.close(code, reason);\n }\n this._handleClose(new Events.CloseEvent(code, reason, this));\n } catch (_error) {\n // ignore\n }\n }\n\n private _acceptOpen() {\n this._debug(\"accept open\");\n this._retryCount = 0;\n }\n\n private _handleOpen = (event: Event) => {\n this._debug(\"open event\");\n const { minUptime = DEFAULT.minUptime } = this._options;\n\n clearTimeout(this._connectTimeout);\n this._uptimeTimeout = setTimeout(() => this._acceptOpen(), minUptime);\n\n assert(this._ws, \"WebSocket is not defined\");\n\n this._ws.binaryType = this._binaryType;\n\n // send enqueued messages (messages sent before websocket open event)\n this._messageQueue.forEach((message) => {\n this._ws?.send(message);\n });\n this._messageQueue = [];\n\n if (this.onopen) {\n this.onopen(event);\n }\n this.dispatchEvent(cloneEvent(event));\n };\n\n private _handleMessage = (event: MessageEvent) => {\n this._debug(\"message event\");\n\n if (this.onmessage) {\n this.onmessage(event);\n }\n this.dispatchEvent(cloneEvent(event));\n };\n\n private _handleError = (event: ErrorEvent) => {\n this._debug(\"error event\", event.message);\n this._disconnect(\n undefined,\n event.message === \"TIMEOUT\" ? \"timeout\" : undefined\n );\n\n if (this.onerror) {\n this.onerror(event);\n }\n this._debug(\"exec error listeners\");\n this.dispatchEvent(cloneEvent(event));\n\n this._connect();\n };\n\n private _handleClose = (event: CloseEvent) => {\n this._debug(\"close event\");\n this._clearTimeouts();\n\n if (this._shouldReconnect) {\n this._connect();\n }\n\n if (this.onclose) {\n this.onclose(event);\n }\n this.dispatchEvent(cloneEvent(event));\n };\n\n private _removeListeners() {\n if (!this._ws) {\n return;\n }\n this._debug(\"removeListeners\");\n this._ws.removeEventListener(\"open\", this._handleOpen);\n this._ws.removeEventListener(\"close\", this._handleClose);\n this._ws.removeEventListener(\"message\", this._handleMessage);\n // @ts-expect-error we need to fix event/listerner types\n this._ws.removeEventListener(\"error\", this._handleError);\n }\n\n private _addListeners() {\n if (!this._ws) {\n return;\n }\n this._debug(\"addListeners\");\n this._ws.addEventListener(\"open\", this._handleOpen);\n this._ws.addEventListener(\"close\", this._handleClose);\n this._ws.addEventListener(\"message\", this._handleMessage);\n // @ts-expect-error we need to fix event/listener types\n this._ws.addEventListener(\"error\", this._handleError);\n }\n\n private _clearTimeouts() {\n clearTimeout(this._connectTimeout);\n clearTimeout(this._uptimeTimeout);\n }\n}\n"],"mappings":"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"}