forked from FoundKeyGang/FoundKey
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.
This commit is contained in:
parent
9a34e0b2b1
commit
dde6f5eb55
3 changed files with 170 additions and 26 deletions
144
packages/foundkey-js/src/@types/reconnecting-websocket.d.ts
vendored
Normal file
144
packages/foundkey-js/src/@types/reconnecting-websocket.d.ts
vendored
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
/*!
|
||||||
|
* Reconnecting WebSocket
|
||||||
|
* by Pedro Ladaria <pedro.ladaria@gmail.com>
|
||||||
|
* 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<string>);
|
||||||
|
export declare type Message = string | ArrayBuffer | Blob | ArrayBufferView;
|
||||||
|
export declare type ListenersMap = {
|
||||||
|
error: Array<Events.WebSocketEventListenerMap['error']>;
|
||||||
|
message: Array<Events.WebSocketEventListenerMap['message']>;
|
||||||
|
open: Array<Events.WebSocketEventListenerMap['open']>;
|
||||||
|
close: Array<Events.WebSocketEventListenerMap['close']>;
|
||||||
|
};
|
||||||
|
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<T extends keyof Events.WebSocketEventListenerMap>(type: T, listener: Events.WebSocketEventListenerMap[T]): void;
|
||||||
|
dispatchEvent(event: Event): boolean;
|
||||||
|
/**
|
||||||
|
* Removes an event listener
|
||||||
|
*/
|
||||||
|
removeEventListener<T extends keyof Events.WebSocketEventListenerMap>(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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import autobind from 'autobind-decorator';
|
import { boundMethod } from 'autobind-decorator';
|
||||||
import EventEmitter from 'eventemitter3';
|
import EventEmitter from 'eventemitter3';
|
||||||
import ReconnectingWebsocket from 'reconnecting-websocket';
|
import ReconnectingWebSocket from 'reconnecting-websocket';
|
||||||
import { BroadcastEvents, Channels } from './streaming.types.js';
|
import { BroadcastEvents, Channels } from './streaming.types.js';
|
||||||
|
|
||||||
export function urlQuery(obj: Record<string, string | number | boolean | undefined>): string {
|
export function urlQuery(obj: Record<string, string | number | boolean | undefined>): string {
|
||||||
|
@ -25,7 +25,7 @@ type StreamEvents = {
|
||||||
* Misskey stream connection
|
* Misskey stream connection
|
||||||
*/
|
*/
|
||||||
export default class Stream extends EventEmitter<StreamEvents> {
|
export default class Stream extends EventEmitter<StreamEvents> {
|
||||||
private stream: ReconnectingWebsocket;
|
private stream: ReconnectingWebSocket;
|
||||||
public state: 'initializing' | 'reconnecting' | 'connected' = 'initializing';
|
public state: 'initializing' | 'reconnecting' | 'connected' = 'initializing';
|
||||||
private sharedConnectionPools: Pool[] = [];
|
private sharedConnectionPools: Pool[] = [];
|
||||||
private sharedConnections: SharedConnection[] = [];
|
private sharedConnections: SharedConnection[] = [];
|
||||||
|
@ -47,7 +47,7 @@ export default class Stream extends EventEmitter<StreamEvents> {
|
||||||
|
|
||||||
const wsOrigin = origin.replace('http://', 'ws://').replace('https://', 'wss://');
|
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
|
minReconnectionDelay: 1, // https://github.com/pladaria/reconnecting-websocket/issues/91
|
||||||
WebSocket: options.WebSocket,
|
WebSocket: options.WebSocket,
|
||||||
});
|
});
|
||||||
|
@ -56,12 +56,12 @@ export default class Stream extends EventEmitter<StreamEvents> {
|
||||||
this.stream.addEventListener('message', this.onMessage);
|
this.stream.addEventListener('message', this.onMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@boundMethod
|
||||||
private genId(): string {
|
private genId(): string {
|
||||||
return (++this.idCounter).toString();
|
return (++this.idCounter).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@boundMethod
|
||||||
public useChannel<C extends keyof Channels>(channel: C, params?: Channels[C]['params'], name?: string): Connection<Channels[C]> {
|
public useChannel<C extends keyof Channels>(channel: C, params?: Channels[C]['params'], name?: string): Connection<Channels[C]> {
|
||||||
if (params) {
|
if (params) {
|
||||||
return this.connectToChannel(channel, params);
|
return this.connectToChannel(channel, params);
|
||||||
|
@ -70,7 +70,7 @@ export default class Stream extends EventEmitter<StreamEvents> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@boundMethod
|
||||||
private useSharedConnection<C extends keyof Channels>(channel: C, name?: string): SharedConnection<Channels[C]> {
|
private useSharedConnection<C extends keyof Channels>(channel: C, name?: string): SharedConnection<Channels[C]> {
|
||||||
let pool = this.sharedConnectionPools.find(p => p.channel === channel);
|
let pool = this.sharedConnectionPools.find(p => p.channel === channel);
|
||||||
|
|
||||||
|
@ -84,24 +84,24 @@ export default class Stream extends EventEmitter<StreamEvents> {
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@boundMethod
|
||||||
public removeSharedConnection(connection: SharedConnection): void {
|
public removeSharedConnection(connection: SharedConnection): void {
|
||||||
this.sharedConnections = this.sharedConnections.filter(c => c !== connection);
|
this.sharedConnections = this.sharedConnections.filter(c => c !== connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@boundMethod
|
||||||
public removeSharedConnectionPool(pool: Pool): void {
|
public removeSharedConnectionPool(pool: Pool): void {
|
||||||
this.sharedConnectionPools = this.sharedConnectionPools.filter(p => p !== pool);
|
this.sharedConnectionPools = this.sharedConnectionPools.filter(p => p !== pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@boundMethod
|
||||||
private connectToChannel<C extends keyof Channels>(channel: C, params: Channels[C]['params']): NonSharedConnection<Channels[C]> {
|
private connectToChannel<C extends keyof Channels>(channel: C, params: Channels[C]['params']): NonSharedConnection<Channels[C]> {
|
||||||
const connection = new NonSharedConnection(this, channel, this.genId(), params);
|
const connection = new NonSharedConnection(this, channel, this.genId(), params);
|
||||||
this.nonSharedConnections.push(connection);
|
this.nonSharedConnections.push(connection);
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@boundMethod
|
||||||
public disconnectToChannel(connection: NonSharedConnection): void {
|
public disconnectToChannel(connection: NonSharedConnection): void {
|
||||||
this.nonSharedConnections = this.nonSharedConnections.filter(c => c !== connection);
|
this.nonSharedConnections = this.nonSharedConnections.filter(c => c !== connection);
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ export default class Stream extends EventEmitter<StreamEvents> {
|
||||||
/**
|
/**
|
||||||
* Callback of when open connection
|
* Callback of when open connection
|
||||||
*/
|
*/
|
||||||
@autobind
|
@boundMethod
|
||||||
private onOpen(): void {
|
private onOpen(): void {
|
||||||
const isReconnect = this.state === 'reconnecting';
|
const isReconnect = this.state === 'reconnecting';
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ export default class Stream extends EventEmitter<StreamEvents> {
|
||||||
/**
|
/**
|
||||||
* Callback of when close connection
|
* Callback of when close connection
|
||||||
*/
|
*/
|
||||||
@autobind
|
@boundMethod
|
||||||
private onClose(): void {
|
private onClose(): void {
|
||||||
if (this.state === 'connected') {
|
if (this.state === 'connected') {
|
||||||
this.state = 'reconnecting';
|
this.state = 'reconnecting';
|
||||||
|
@ -137,7 +137,7 @@ export default class Stream extends EventEmitter<StreamEvents> {
|
||||||
/**
|
/**
|
||||||
* Callback of when received a message from connection
|
* Callback of when received a message from connection
|
||||||
*/
|
*/
|
||||||
@autobind
|
@boundMethod
|
||||||
private onMessage(message: { data: string; }): void {
|
private onMessage(message: { data: string; }): void {
|
||||||
const { type, body } = JSON.parse(message.data);
|
const { type, body } = JSON.parse(message.data);
|
||||||
|
|
||||||
|
@ -167,7 +167,7 @@ export default class Stream extends EventEmitter<StreamEvents> {
|
||||||
/**
|
/**
|
||||||
* Send a message to connection
|
* Send a message to connection
|
||||||
*/
|
*/
|
||||||
@autobind
|
@boundMethod
|
||||||
public send(typeOrPayload: any, payload?: any): void {
|
public send(typeOrPayload: any, payload?: any): void {
|
||||||
const data = payload === undefined ? typeOrPayload : {
|
const data = payload === undefined ? typeOrPayload : {
|
||||||
type: typeOrPayload,
|
type: typeOrPayload,
|
||||||
|
@ -180,7 +180,7 @@ export default class Stream extends EventEmitter<StreamEvents> {
|
||||||
/**
|
/**
|
||||||
* Close this connection
|
* Close this connection
|
||||||
*/
|
*/
|
||||||
@autobind
|
@boundMethod
|
||||||
public close(): void {
|
public close(): void {
|
||||||
this.stream.close();
|
this.stream.close();
|
||||||
}
|
}
|
||||||
|
@ -204,12 +204,12 @@ class Pool {
|
||||||
this.stream.on('_disconnected_', this.onStreamDisconnected);
|
this.stream.on('_disconnected_', this.onStreamDisconnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@boundMethod
|
||||||
private onStreamDisconnected(): void {
|
private onStreamDisconnected(): void {
|
||||||
this.isConnected = false;
|
this.isConnected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@boundMethod
|
||||||
public inc(): void {
|
public inc(): void {
|
||||||
if (this.users === 0 && !this.isConnected) {
|
if (this.users === 0 && !this.isConnected) {
|
||||||
this.connect();
|
this.connect();
|
||||||
|
@ -224,7 +224,7 @@ class Pool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@boundMethod
|
||||||
public dec(): void {
|
public dec(): void {
|
||||||
this.users--;
|
this.users--;
|
||||||
|
|
||||||
|
@ -238,7 +238,7 @@ class Pool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@boundMethod
|
||||||
public connect(): void {
|
public connect(): void {
|
||||||
if (this.isConnected) return;
|
if (this.isConnected) return;
|
||||||
this.isConnected = true;
|
this.isConnected = true;
|
||||||
|
@ -248,7 +248,7 @@ class Pool {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@boundMethod
|
||||||
private disconnect(): void {
|
private disconnect(): void {
|
||||||
this.stream.off('_disconnected_', this.onStreamDisconnected);
|
this.stream.off('_disconnected_', this.onStreamDisconnected);
|
||||||
this.stream.send('disconnect', { id: this.id });
|
this.stream.send('disconnect', { id: this.id });
|
||||||
|
@ -273,7 +273,7 @@ export abstract class Connection<Channel extends AnyOf<Channels> = any> extends
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@boundMethod
|
||||||
public send<T extends keyof Channel['receives']>(type: T, body: Channel['receives'][T]): void {
|
public send<T extends keyof Channel['receives']>(type: T, body: Channel['receives'][T]): void {
|
||||||
this.stream.send('ch', {
|
this.stream.send('ch', {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
|
@ -301,7 +301,7 @@ class SharedConnection<Channel extends AnyOf<Channels> = any> extends Connection
|
||||||
this.pool.inc();
|
this.pool.inc();
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@boundMethod
|
||||||
public dispose(): void {
|
public dispose(): void {
|
||||||
this.pool.dec();
|
this.pool.dec();
|
||||||
this.removeAllListeners();
|
this.removeAllListeners();
|
||||||
|
@ -322,7 +322,7 @@ class NonSharedConnection<Channel extends AnyOf<Channels> = any> extends Connect
|
||||||
this.connect();
|
this.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@boundMethod
|
||||||
public connect(): void {
|
public connect(): void {
|
||||||
this.stream.send('connect', {
|
this.stream.send('connect', {
|
||||||
channel: this.channel,
|
channel: this.channel,
|
||||||
|
@ -331,7 +331,7 @@ class NonSharedConnection<Channel extends AnyOf<Channels> = any> extends Connect
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@boundMethod
|
||||||
public dispose(): void {
|
public dispose(): void {
|
||||||
this.removeAllListeners();
|
this.removeAllListeners();
|
||||||
this.stream.send('disconnect', { id: this.id });
|
this.stream.send('disconnect', { id: this.id });
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"moduleResolution": "node"
|
"moduleResolution": "Node16"
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src/**/*"
|
"src/**/*"
|
||||||
|
|
Loading…
Reference in a new issue