From dde6f5eb55e6b37ec367b8de6a23036e6a55f738 Mon Sep 17 00:00:00 2001 From: Francis Dinh Date: Fri, 9 Sep 2022 00:04:26 -0400 Subject: [PATCH] foundkey-js: fix build errors The use of `"moduleResolution": "Node16"` is to make TS use the same module resolution as Node does when using ESM modules. `reconnecting-websocket`'s TS definition files were copied over but wrapped in a `declare module` block to make TS properly accept using the default import directly without using `.default`. I also decided to look into `autobind-decorator`'s source code and figured that I could use the `boundMethod` annotation directly instead of using the `autobind` default export. --- .../src/@types/reconnecting-websocket.d.ts | 144 ++++++++++++++++++ packages/foundkey-js/src/streaming.ts | 50 +++--- packages/foundkey-js/tsconfig.json | 2 +- 3 files changed, 170 insertions(+), 26 deletions(-) create mode 100644 packages/foundkey-js/src/@types/reconnecting-websocket.d.ts diff --git a/packages/foundkey-js/src/@types/reconnecting-websocket.d.ts b/packages/foundkey-js/src/@types/reconnecting-websocket.d.ts new file mode 100644 index 000000000..96fc45c25 --- /dev/null +++ b/packages/foundkey-js/src/@types/reconnecting-websocket.d.ts @@ -0,0 +1,144 @@ +/*! + * Reconnecting WebSocket + * by Pedro Ladaria + * https://github.com/pladaria/reconnecting-websocket + * License MIT + */ + +declare module 'reconnecting-websocket' { + import * as Events from './events'; + export declare type Event = Events.Event; + export declare type ErrorEvent = Events.ErrorEvent; + export declare type CloseEvent = Events.CloseEvent; + export declare type Options = { + WebSocket?: any; + maxReconnectionDelay?: number; + minReconnectionDelay?: number; + reconnectionDelayGrowFactor?: number; + minUptime?: number; + connectionTimeout?: number; + maxRetries?: number; + maxEnqueuedMessages?: number; + startClosed?: boolean; + debug?: boolean; + }; + export declare type UrlProvider = string | (() => string) | (() => Promise); + export declare type Message = string | ArrayBuffer | Blob | ArrayBufferView; + export declare type ListenersMap = { + error: Array; + message: Array; + open: Array; + close: Array; + }; + export default class ReconnectingWebSocket { + private _ws?; + private _listeners; + private _retryCount; + private _uptimeTimeout; + private _connectTimeout; + private _shouldReconnect; + private _connectLock; + private _binaryType; + private _closeCalled; + private _messageQueue; + private readonly _url; + private readonly _protocols?; + private readonly _options; + constructor(url: UrlProvider, protocols?: string | string[], options?: Options); + static readonly CONNECTING: number; + static readonly OPEN: number; + static readonly CLOSING: number; + static readonly CLOSED: number; + readonly CONNECTING: number; + readonly OPEN: number; + readonly CLOSING: number; + readonly CLOSED: number; + binaryType: BinaryType; + /** + * Returns the number or connection retries + */ + readonly retryCount: number; + /** + * The number of bytes of data that have been queued using calls to send() but not yet + * transmitted to the network. This value resets to zero once all queued data has been sent. + * This value does not reset to zero when the connection is closed; if you keep calling send(), + * this will continue to climb. Read only + */ + readonly bufferedAmount: number; + /** + * The extensions selected by the server. This is currently only the empty string or a list of + * extensions as negotiated by the connection + */ + readonly extensions: string; + /** + * A string indicating the name of the sub-protocol the server selected; + * this will be one of the strings specified in the protocols parameter when creating the + * WebSocket object + */ + readonly protocol: string; + /** + * The current state of the connection; this is one of the Ready state constants + */ + readonly readyState: number; + /** + * The URL as resolved by the constructor + */ + readonly url: string; + /** + * An event listener to be called when the WebSocket connection's readyState changes to CLOSED + */ + onclose: ((event: Events.CloseEvent) => void) | null; + /** + * An event listener to be called when an error occurs + */ + onerror: ((event: Events.ErrorEvent) => void) | null; + /** + * An event listener to be called when a message is received from the server + */ + onmessage: ((event: MessageEvent) => void) | null; + /** + * An event listener to be called when the WebSocket connection's readyState changes to OPEN; + * this indicates that the connection is ready to send and receive data + */ + onopen: ((event: Event) => void) | null; + /** + * Closes the WebSocket connection or connection attempt, if any. If the connection is already + * CLOSED, this method does nothing + */ + close(code?: number, reason?: string): void; + /** + * Closes the WebSocket connection or connection attempt and connects again. + * Resets retry counter; + */ + reconnect(code?: number, reason?: string): void; + /** + * Enqueue specified data to be transmitted to the server over the WebSocket connection + */ + send(data: Message): void; + /** + * Register an event handler of a specific event type + */ + addEventListener(type: T, listener: Events.WebSocketEventListenerMap[T]): void; + dispatchEvent(event: Event): boolean; + /** + * Removes an event listener + */ + removeEventListener(type: T, listener: Events.WebSocketEventListenerMap[T]): void; + private _debug; + private _getNextDelay; + private _wait; + private _getNextUrl; + private _connect; + private _handleTimeout; + private _disconnect; + private _acceptOpen; + private _callEventListener; + private _handleOpen; + private _handleMessage; + private _handleError; + private _handleClose; + private _removeListeners; + private _addListeners; + private _clearTimeouts; + } +} diff --git a/packages/foundkey-js/src/streaming.ts b/packages/foundkey-js/src/streaming.ts index 465f546f3..0892db9d9 100644 --- a/packages/foundkey-js/src/streaming.ts +++ b/packages/foundkey-js/src/streaming.ts @@ -1,6 +1,6 @@ -import autobind from 'autobind-decorator'; +import { boundMethod } from 'autobind-decorator'; import EventEmitter from 'eventemitter3'; -import ReconnectingWebsocket from 'reconnecting-websocket'; +import ReconnectingWebSocket from 'reconnecting-websocket'; import { BroadcastEvents, Channels } from './streaming.types.js'; export function urlQuery(obj: Record): string { @@ -25,7 +25,7 @@ type StreamEvents = { * Misskey stream connection */ export default class Stream extends EventEmitter { - private stream: ReconnectingWebsocket; + private stream: ReconnectingWebSocket; public state: 'initializing' | 'reconnecting' | 'connected' = 'initializing'; private sharedConnectionPools: Pool[] = []; private sharedConnections: SharedConnection[] = []; @@ -47,7 +47,7 @@ export default class Stream extends EventEmitter { const wsOrigin = origin.replace('http://', 'ws://').replace('https://', 'wss://'); - this.stream = new ReconnectingWebsocket(`${wsOrigin}/streaming?${query}`, '', { + this.stream = new ReconnectingWebSocket(`${wsOrigin}/streaming?${query}`, '', { minReconnectionDelay: 1, // https://github.com/pladaria/reconnecting-websocket/issues/91 WebSocket: options.WebSocket, }); @@ -56,12 +56,12 @@ export default class Stream extends EventEmitter { this.stream.addEventListener('message', this.onMessage); } - @autobind + @boundMethod private genId(): string { return (++this.idCounter).toString(); } - @autobind + @boundMethod public useChannel(channel: C, params?: Channels[C]['params'], name?: string): Connection { if (params) { return this.connectToChannel(channel, params); @@ -70,7 +70,7 @@ export default class Stream extends EventEmitter { } } - @autobind + @boundMethod private useSharedConnection(channel: C, name?: string): SharedConnection { let pool = this.sharedConnectionPools.find(p => p.channel === channel); @@ -84,24 +84,24 @@ export default class Stream extends EventEmitter { return connection; } - @autobind + @boundMethod public removeSharedConnection(connection: SharedConnection): void { this.sharedConnections = this.sharedConnections.filter(c => c !== connection); } - @autobind + @boundMethod public removeSharedConnectionPool(pool: Pool): void { this.sharedConnectionPools = this.sharedConnectionPools.filter(p => p !== pool); } - @autobind + @boundMethod private connectToChannel(channel: C, params: Channels[C]['params']): NonSharedConnection { const connection = new NonSharedConnection(this, channel, this.genId(), params); this.nonSharedConnections.push(connection); return connection; } - @autobind + @boundMethod public disconnectToChannel(connection: NonSharedConnection): void { this.nonSharedConnections = this.nonSharedConnections.filter(c => c !== connection); } @@ -109,7 +109,7 @@ export default class Stream extends EventEmitter { /** * Callback of when open connection */ - @autobind + @boundMethod private onOpen(): void { const isReconnect = this.state === 'reconnecting'; @@ -126,7 +126,7 @@ export default class Stream extends EventEmitter { /** * Callback of when close connection */ - @autobind + @boundMethod private onClose(): void { if (this.state === 'connected') { this.state = 'reconnecting'; @@ -137,7 +137,7 @@ export default class Stream extends EventEmitter { /** * Callback of when received a message from connection */ - @autobind + @boundMethod private onMessage(message: { data: string; }): void { const { type, body } = JSON.parse(message.data); @@ -167,7 +167,7 @@ export default class Stream extends EventEmitter { /** * Send a message to connection */ - @autobind + @boundMethod public send(typeOrPayload: any, payload?: any): void { const data = payload === undefined ? typeOrPayload : { type: typeOrPayload, @@ -180,7 +180,7 @@ export default class Stream extends EventEmitter { /** * Close this connection */ - @autobind + @boundMethod public close(): void { this.stream.close(); } @@ -204,12 +204,12 @@ class Pool { this.stream.on('_disconnected_', this.onStreamDisconnected); } - @autobind + @boundMethod private onStreamDisconnected(): void { this.isConnected = false; } - @autobind + @boundMethod public inc(): void { if (this.users === 0 && !this.isConnected) { this.connect(); @@ -224,7 +224,7 @@ class Pool { } } - @autobind + @boundMethod public dec(): void { this.users--; @@ -238,7 +238,7 @@ class Pool { } } - @autobind + @boundMethod public connect(): void { if (this.isConnected) return; this.isConnected = true; @@ -248,7 +248,7 @@ class Pool { }); } - @autobind + @boundMethod private disconnect(): void { this.stream.off('_disconnected_', this.onStreamDisconnected); this.stream.send('disconnect', { id: this.id }); @@ -273,7 +273,7 @@ export abstract class Connection = any> extends this.name = name; } - @autobind + @boundMethod public send(type: T, body: Channel['receives'][T]): void { this.stream.send('ch', { id: this.id, @@ -301,7 +301,7 @@ class SharedConnection = any> extends Connection this.pool.inc(); } - @autobind + @boundMethod public dispose(): void { this.pool.dec(); this.removeAllListeners(); @@ -322,7 +322,7 @@ class NonSharedConnection = any> extends Connect this.connect(); } - @autobind + @boundMethod public connect(): void { this.stream.send('connect', { channel: this.channel, @@ -331,7 +331,7 @@ class NonSharedConnection = any> extends Connect }); } - @autobind + @boundMethod public dispose(): void { this.removeAllListeners(); this.stream.send('disconnect', { id: this.id }); diff --git a/packages/foundkey-js/tsconfig.json b/packages/foundkey-js/tsconfig.json index 6813e1f57..83de431b1 100644 --- a/packages/foundkey-js/tsconfig.json +++ b/packages/foundkey-js/tsconfig.json @@ -15,7 +15,7 @@ "experimentalDecorators": true, "noImplicitReturns": true, "esModuleInterop": true, - "moduleResolution": "node" + "moduleResolution": "Node16" }, "include": [ "src/**/*"