From 20f8cf009aeb029c8ae5ba83862ebedb6272a262 Mon Sep 17 00:00:00 2001 From: Andrei Kirkouski Date: Thu, 20 Oct 2022 13:23:08 +0400 Subject: [PATCH 1/2] retry strategy --- build/Channel.d.ts | 66 ++++++------ build/SFSocket.d.ts | 110 ++++++++++---------- build/autobind.d.ts | 10 +- build/connection/Connection.d.ts | 68 ++++++------ build/connection/ConnectionManager.d.ts | 96 ++++++++--------- build/connection/types.d.ts | 36 +++---- build/constants.d.ts | 4 +- build/eventdispatcher/CallbackRegistry.d.ts | 18 ++-- build/eventdispatcher/EventsDispatcher.d.ts | 26 ++--- build/eventdispatcher/events.d.ts | 28 ++--- build/index.d.ts | 20 ++-- build/messageCodingUtils.d.ts | 28 ++--- build/mock-data.d.ts | 8 +- build/transport/Transport.d.ts | 36 +++---- build/transport/TransportConnection.d.ts | 70 ++++++------- build/types.d.ts | 42 ++++---- lib/SFSocket.ts | 3 + lib/connection/ConnectionManager.ts | 25 +++-- lib/connection/types.ts | 4 +- lib/constants.ts | 4 +- lib/retrystrategy/index.ts | 35 +++++++ 21 files changed, 396 insertions(+), 341 deletions(-) create mode 100644 lib/retrystrategy/index.ts diff --git a/build/Channel.d.ts b/build/Channel.d.ts index d7c483b..56d2695 100644 --- a/build/Channel.d.ts +++ b/build/Channel.d.ts @@ -1,34 +1,34 @@ -import { UEventCallback } from './types'; -import { SFSocket } from './SFSocket'; -import { ConnectionManagerEventMap } from './connection/ConnectionManager'; -export declare enum ChannelStatus { - CLOSED = "closed", - JOINING = "joining", - JOINED = "joined", - LEAVING = "leaving", - ERROR = "error" -} -export default class Channel { - private readonly selfName; - private channelStatus; - private socket; - private cMgr; - private enabled; - constructor(name: string, socket: SFSocket); - get status(): ChannelStatus; - get name(): string; - get isActive(): boolean; - join(): void; - subscribe(eventName: K, callback: UEventCallback): void; - unsubscribe(eventName: K, callback: UEventCallback): void; - leave(): void; - private onConnect; - private onDisconnect; - private onJoin; - private onLeft; - private onJoinFailed; - private sendJoinCommand; - private startListening; - private stopListening; -} +import { UEventCallback } from './types'; +import { SFSocket } from './SFSocket'; +import { ConnectionManagerEventMap } from './connection/ConnectionManager'; +export declare enum ChannelStatus { + CLOSED = "closed", + JOINING = "joining", + JOINED = "joined", + LEAVING = "leaving", + ERROR = "error" +} +export default class Channel { + private readonly selfName; + private channelStatus; + private socket; + private cMgr; + private enabled; + constructor(name: string, socket: SFSocket); + get status(): ChannelStatus; + get name(): string; + get isActive(): boolean; + join(): void; + subscribe(eventName: K, callback: UEventCallback): void; + unsubscribe(eventName: K, callback: UEventCallback): void; + leave(): void; + private onConnect; + private onDisconnect; + private onJoin; + private onLeft; + private onJoinFailed; + private sendJoinCommand; + private startListening; + private stopListening; +} //# sourceMappingURL=Channel.d.ts.map \ No newline at end of file diff --git a/build/SFSocket.d.ts b/build/SFSocket.d.ts index aefc8c8..aa061b4 100644 --- a/build/SFSocket.d.ts +++ b/build/SFSocket.d.ts @@ -1,56 +1,56 @@ -import { UEventCallback } from './types'; -import Channel from './Channel'; -import ConnectionManager, { ConnectionManagerEventMap } from './connection/ConnectionManager'; -export interface IChannels { - [name: string]: Channel; -} -export declare enum SFSocketEventType { - CONNECTING = "sfSocket:connecting", - MESSAGE = "sfSocket:message", - CHANNEL_JOINED = "channel_joined", - CHANNEL_JOIN_FAILED = "channel_join_failed", - CHANNEL_LEFT = "channel_left", - CHANNEL_LEAVE_FAILED = "channel_leave_failed", - ERROR = "sfSocket:error", - CLOSED = "sfSocket:closed" -} -export interface ISFSocketConfig { - host: string; - port: string | number; - path: string; - queryParams?: { - [key: string]: string; - }; - unavailableTimeout?: number; - useTLS?: boolean; - retryTimeout?: number; -} -export interface ISFSocketEvent { - type: SFSocketEventType; - data: string | null; - error: string | null; - context?: { - channel?: string; - code?: string | number; - } | null; -} -export declare class SFSocket { - static instances: SFSocket[]; - static isReady: boolean; - static ready(): void; - private config; - private channels; - cMgr: ConnectionManager; - constructor(options?: ISFSocketConfig); - connect(): void; - disconnect(): void; - sendCommand(cmdName: string, data: any): boolean; - joinChannelList(channelsNames: string[]): void; - leaveChannelList(channelNames: string[]): void; - subscribe(eventName: K, callback: UEventCallback, channel?: string): ConnectionManager; - unsubscribe(eventName: K, callback: UEventCallback, channel?: string): ConnectionManager; - joinChannel(chanelName: string, dontJoin?: boolean): Channel; - leaveChannel(chanelName: string): Channel; - getChannel(name: string): Channel; -} +import { UEventCallback } from './types'; +import Channel from './Channel'; +import ConnectionManager, { ConnectionManagerEventMap } from './connection/ConnectionManager'; +export interface IChannels { + [name: string]: Channel; +} +export declare enum SFSocketEventType { + CONNECTING = "sfSocket:connecting", + MESSAGE = "sfSocket:message", + CHANNEL_JOINED = "channel_joined", + CHANNEL_JOIN_FAILED = "channel_join_failed", + CHANNEL_LEFT = "channel_left", + CHANNEL_LEAVE_FAILED = "channel_leave_failed", + ERROR = "sfSocket:error", + CLOSED = "sfSocket:closed" +} +export interface ISFSocketConfig { + host: string; + port: string | number; + path: string; + queryParams?: { + [key: string]: string; + }; + unavailableTimeout?: number; + useTLS?: boolean; + retryTimeout?: number; +} +export interface ISFSocketEvent { + type: SFSocketEventType; + data: string | null; + error: string | null; + context?: { + channel?: string; + code?: string | number; + } | null; +} +export declare class SFSocket { + static instances: SFSocket[]; + static isReady: boolean; + static ready(): void; + private config; + private channels; + cMgr: ConnectionManager; + constructor(options?: ISFSocketConfig); + connect(): void; + disconnect(): void; + sendCommand(cmdName: string, data: any): boolean; + joinChannelList(channelsNames: string[]): void; + leaveChannelList(channelNames: string[]): void; + subscribe(eventName: K, callback: UEventCallback, channel?: string): ConnectionManager; + unsubscribe(eventName: K, callback: UEventCallback, channel?: string): ConnectionManager; + joinChannel(chanelName: string, dontJoin?: boolean): Channel; + leaveChannel(chanelName: string): Channel; + getChannel(name: string): Channel; +} //# sourceMappingURL=SFSocket.d.ts.map \ No newline at end of file diff --git a/build/autobind.d.ts b/build/autobind.d.ts index 674a19d..4ca74e4 100644 --- a/build/autobind.d.ts +++ b/build/autobind.d.ts @@ -1,6 +1,6 @@ -export declare function autobind(target: any, key: string | symbol, descriptor: PropertyDescriptor): { - configurable: boolean; - get(): any; - set(value: any): void; -}; +export declare function autobind(target: any, key: string | symbol, descriptor: PropertyDescriptor): { + configurable: boolean; + get(): any; + set(value: any): void; +}; //# sourceMappingURL=autobind.d.ts.map \ No newline at end of file diff --git a/build/connection/Connection.d.ts b/build/connection/Connection.d.ts index 097b543..0b35717 100644 --- a/build/connection/Connection.d.ts +++ b/build/connection/Connection.d.ts @@ -1,35 +1,35 @@ -import EventsDispatcher from '../eventdispatcher/EventsDispatcher'; -import { ISFSocketEvent } from '../SFSocket'; -import TransportConnection from '../transport/TransportConnection'; -import { NamesDict } from '../eventdispatcher/events'; -export interface ConnectionEventMap { - [NamesDict.CLOSED]: ISFSocketEvent; - [NamesDict.ERROR]: ISFSocketEvent; - [NamesDict.MESSAGE]: ISFSocketEvent; - [NamesDict.CHANNEL_JOIN_FAILED]: string[]; - [NamesDict.CHANNEL_JOINED]: string[]; - [NamesDict.CHANNEL_LEFT]: string[]; -} -export declare enum ConnectionCommands { - JOIN = "join", - LEAVE = "leave" -} -export declare const SystemCommands: Set; -export interface EventWithCode { - context: { - code: string; - }; -} -export default class Connection extends EventsDispatcher { - id: string; - transport: TransportConnection | null; - constructor(id: string, transport: TransportConnection); - send(data: string): boolean; - sendCommand(commandName: string, payload: any): boolean; - sendJoin(channels: string[]): void; - sendLeave(channels: string[]): void; - close(): void; - private bindListeners; - private handleCloseEvent; -} +import EventsDispatcher from '../eventdispatcher/EventsDispatcher'; +import { ISFSocketEvent } from '../SFSocket'; +import TransportConnection from '../transport/TransportConnection'; +import { NamesDict } from '../eventdispatcher/events'; +export interface ConnectionEventMap { + [NamesDict.CLOSED]: ISFSocketEvent; + [NamesDict.ERROR]: ISFSocketEvent; + [NamesDict.MESSAGE]: ISFSocketEvent; + [NamesDict.CHANNEL_JOIN_FAILED]: string[]; + [NamesDict.CHANNEL_JOINED]: string[]; + [NamesDict.CHANNEL_LEFT]: string[]; +} +export declare enum ConnectionCommands { + JOIN = "join", + LEAVE = "leave" +} +export declare const SystemCommands: Set; +export interface EventWithCode { + context: { + code: string; + }; +} +export default class Connection extends EventsDispatcher { + id: string; + transport: TransportConnection | null; + constructor(id: string, transport: TransportConnection); + send(data: string): boolean; + sendCommand(commandName: string, payload: any): boolean; + sendJoin(channels: string[]): void; + sendLeave(channels: string[]): void; + close(): void; + private bindListeners; + private handleCloseEvent; +} //# sourceMappingURL=Connection.d.ts.map \ No newline at end of file diff --git a/build/connection/ConnectionManager.d.ts b/build/connection/ConnectionManager.d.ts index da99f65..4643111 100644 --- a/build/connection/ConnectionManager.d.ts +++ b/build/connection/ConnectionManager.d.ts @@ -1,49 +1,49 @@ -import EventsDispatcher from '../eventdispatcher/EventsDispatcher'; -import { ISFSocketConfig, ISFSocketEvent } from '../SFSocket'; -import { NamesDict } from '../eventdispatcher/events'; -export declare type ConnectionState = 'initialized' | NamesDict.UNAVAILABLE | NamesDict.CONNECTING | NamesDict.CONNECTED | NamesDict.DISCONNECTED; -export interface ConnectionManagerEventMap { - [NamesDict.CONNECTING]: ISFSocketEvent; - [NamesDict.DISCONNECTED]: undefined; - [NamesDict.CONNECTED]: undefined; - [NamesDict.CHANNEL_JOINED]: string[]; - [NamesDict.CHANNEL_JOIN_FAILED]: string[]; - [NamesDict.CHANNEL_LEFT]: string[]; - [NamesDict.ERROR]: ISFSocketEvent; - [NamesDict.MESSAGE]: ISFSocketEvent; - [NamesDict.CLOSED]: ISFSocketEvent; - [NamesDict.UNAVAILABLE]: undefined; -} -export default class ConnectionManager extends EventsDispatcher { - private options; - state: ConnectionState; - private connection; - private unavailableTimer; - private retryTimer; - private transport; - private runner; - private errorCallbacks; - private connectionCallbacks; - constructor(options: ISFSocketConfig); - connect(): void; - send(data: string): boolean; - sendCommand(name: string, data: any): boolean; - sendJoin(channels: string[]): boolean; - sendLeave(channels: string[]): boolean; - disconnect(): void; - isConnected(): boolean; - private startConnecting; - private abortConnecting; - private disconnectInternally; - private retryIn; - private clearRetryTimer; - private setUnavailableTimer; - private clearUnavailableTimer; - private buildConnectionCallbacks; - private buildErrorCallbacks; - private setConnection; - private abandonConnection; - private updateState; - private shouldRetry; -} +import EventsDispatcher from '../eventdispatcher/EventsDispatcher'; +import { ISFSocketConfig, ISFSocketEvent } from '../SFSocket'; +import { NamesDict } from '../eventdispatcher/events'; +export declare type ConnectionState = 'initialized' | NamesDict.UNAVAILABLE | NamesDict.CONNECTING | NamesDict.CONNECTED | NamesDict.DISCONNECTED; +export interface ConnectionManagerEventMap { + [NamesDict.CONNECTING]: ISFSocketEvent; + [NamesDict.DISCONNECTED]: undefined; + [NamesDict.CONNECTED]: undefined; + [NamesDict.CHANNEL_JOINED]: string[]; + [NamesDict.CHANNEL_JOIN_FAILED]: string[]; + [NamesDict.CHANNEL_LEFT]: string[]; + [NamesDict.ERROR]: ISFSocketEvent; + [NamesDict.MESSAGE]: ISFSocketEvent; + [NamesDict.CLOSED]: ISFSocketEvent; + [NamesDict.UNAVAILABLE]: undefined; +} +export default class ConnectionManager extends EventsDispatcher { + private options; + state: ConnectionState; + private connection; + private unavailableTimer; + private retryTimer; + private transport; + private runner; + private errorCallbacks; + private connectionCallbacks; + constructor(options: ISFSocketConfig); + connect(): void; + send(data: string): boolean; + sendCommand(name: string, data: any): boolean; + sendJoin(channels: string[]): boolean; + sendLeave(channels: string[]): boolean; + disconnect(): void; + isConnected(): boolean; + private startConnecting; + private abortConnecting; + private disconnectInternally; + private retryIn; + private clearRetryTimer; + private setUnavailableTimer; + private clearUnavailableTimer; + private buildConnectionCallbacks; + private buildErrorCallbacks; + private setConnection; + private abandonConnection; + private updateState; + private shouldRetry; +} //# sourceMappingURL=ConnectionManager.d.ts.map \ No newline at end of file diff --git a/build/connection/types.d.ts b/build/connection/types.d.ts index 83ec975..9d805ed 100644 --- a/build/connection/types.d.ts +++ b/build/connection/types.d.ts @@ -1,19 +1,19 @@ -import { ISFSocketEvent } from '../SFSocket'; -export interface IAction { - action: string; - id?: string; - error?: any; -} -export interface IErrorCallbacks { - refused: (result: IAction) => void; - unavailable: (result: IAction) => void; -} -export interface IConnectionCallbacks { - channelJoined: (channels: string[]) => void; - channelJoinFailed: (channels: string[]) => void; - channelLeft: (channels: string[]) => void; - message: (message: ISFSocketEvent) => void; - error: (error: ISFSocketEvent) => void; - closed: (reason: any) => void; -} +import { ISFSocketEvent } from '../SFSocket'; +export interface IAction { + action: string; + id?: string; + error?: any; +} +export interface IErrorCallbacks { + refused: (result: IAction) => void; + unavailable: (result: IAction) => void; +} +export interface IConnectionCallbacks { + channelJoined: (channels: string[]) => void; + channelJoinFailed: (channels: string[]) => void; + channelLeft: (channels: string[]) => void; + message: (message: ISFSocketEvent) => void; + error: (error: ISFSocketEvent) => void; + closed: (reason: any) => void; +} //# sourceMappingURL=types.d.ts.map \ No newline at end of file diff --git a/build/constants.d.ts b/build/constants.d.ts index c629ce8..1acc782 100644 --- a/build/constants.d.ts +++ b/build/constants.d.ts @@ -1,3 +1,3 @@ -import { ISFSocketConfig } from './SFSocket'; -export declare const defaultConfig: ISFSocketConfig; +import { ISFSocketConfig } from './SFSocket'; +export declare const defaultConfig: ISFSocketConfig; //# sourceMappingURL=constants.d.ts.map \ No newline at end of file diff --git a/build/eventdispatcher/CallbackRegistry.d.ts b/build/eventdispatcher/CallbackRegistry.d.ts index 4246b43..54190a0 100644 --- a/build/eventdispatcher/CallbackRegistry.d.ts +++ b/build/eventdispatcher/CallbackRegistry.d.ts @@ -1,10 +1,10 @@ -import { ICallback, ICallbackTable, UEventCallback } from '../types'; -export default class CallbackRegistry { - callbacks: ICallbackTable; - get(name: K): ICallback[]; - add(name: K, callback: UEventCallback, channel?: string): void; - remove(name: K, callback?: UEventCallback, channel?: string): void; - private removeCallback; - private removeAllCallbacks; -} +import { ICallback, ICallbackTable, UEventCallback } from '../types'; +export default class CallbackRegistry { + callbacks: ICallbackTable; + get(name: K): ICallback[]; + add(name: K, callback: UEventCallback, channel?: string): void; + remove(name: K, callback?: UEventCallback, channel?: string): void; + private removeCallback; + private removeAllCallbacks; +} //# sourceMappingURL=CallbackRegistry.d.ts.map \ No newline at end of file diff --git a/build/eventdispatcher/EventsDispatcher.d.ts b/build/eventdispatcher/EventsDispatcher.d.ts index 828c906..f96b58e 100644 --- a/build/eventdispatcher/EventsDispatcher.d.ts +++ b/build/eventdispatcher/EventsDispatcher.d.ts @@ -1,14 +1,14 @@ -import { UEventCallback } from '../types'; -import CallbackRegistry from './CallbackRegistry'; -export interface EventWithChannel { - context: { - channel: string; - }; -} -export default class EventsDispatcher { - callbacks: CallbackRegistry; - bind(eventName: K, callback: UEventCallback, channel?: string): this; - unbind(eventName: K, callback: UEventCallback, channel?: string): this; - emit(eventName: K, event: EventMap[K]): EventsDispatcher; -} +import { UEventCallback } from '../types'; +import CallbackRegistry from './CallbackRegistry'; +export interface EventWithChannel { + context: { + channel: string; + }; +} +export default class EventsDispatcher { + callbacks: CallbackRegistry; + bind(eventName: K, callback: UEventCallback, channel?: string): this; + unbind(eventName: K, callback: UEventCallback, channel?: string): this; + emit(eventName: K, event: EventMap[K]): EventsDispatcher; +} //# sourceMappingURL=EventsDispatcher.d.ts.map \ No newline at end of file diff --git a/build/eventdispatcher/events.d.ts b/build/eventdispatcher/events.d.ts index a333cfd..94dbad7 100644 --- a/build/eventdispatcher/events.d.ts +++ b/build/eventdispatcher/events.d.ts @@ -1,15 +1,15 @@ -export declare enum NamesDict { - CONNECTED = "connected", - MESSAGE = "message", - CONNECTING = "connecting", - DISCONNECTED = "disconnected", - ERROR = "error", - OPEN = "open", - INITIALIZED = "initialized", - CLOSED = "closed", - UNAVAILABLE = "unavailable", - CHANNEL_JOINED = "channel_joined", - CHANNEL_JOIN_FAILED = "channel_join_failed", - CHANNEL_LEFT = "channel_left" -} +export declare enum NamesDict { + CONNECTED = "connected", + MESSAGE = "message", + CONNECTING = "connecting", + DISCONNECTED = "disconnected", + ERROR = "error", + OPEN = "open", + INITIALIZED = "initialized", + CLOSED = "closed", + UNAVAILABLE = "unavailable", + CHANNEL_JOINED = "channel_joined", + CHANNEL_JOIN_FAILED = "channel_join_failed", + CHANNEL_LEFT = "channel_left" +} //# sourceMappingURL=events.d.ts.map \ No newline at end of file diff --git a/build/index.d.ts b/build/index.d.ts index 89b4eaa..0a9d880 100644 --- a/build/index.d.ts +++ b/build/index.d.ts @@ -1,11 +1,11 @@ -import { SFSocket, ISFSocketEvent, ISFSocketConfig, SFSocketEventType } from './SFSocket'; -import { NamesDict, NamesDict as eventNames } from './eventdispatcher/events'; -import Channel, { ChannelStatus } from './Channel'; -import { ICallback, ICallbackTable, SFEventMap, UEventCallback, UndescribedCallbackFunction } from './types'; -declare const makeSocketOptions: (wsUrl: string) => { - host: string; - port: string; - path: string; -} | null; -export { SFSocket, makeSocketOptions, eventNames, Channel, ChannelStatus, ICallback, ICallbackTable, ISFSocketEvent, NamesDict, ISFSocketConfig, SFSocketEventType, SFEventMap, UEventCallback, UndescribedCallbackFunction, }; +import { SFSocket, ISFSocketEvent, ISFSocketConfig, SFSocketEventType } from './SFSocket'; +import { NamesDict, NamesDict as eventNames } from './eventdispatcher/events'; +import Channel, { ChannelStatus } from './Channel'; +import { ICallback, ICallbackTable, SFEventMap, UEventCallback, UndescribedCallbackFunction } from './types'; +declare const makeSocketOptions: (wsUrl: string) => { + host: string; + port: string; + path: string; +} | null; +export { SFSocket, makeSocketOptions, eventNames, Channel, ChannelStatus, ICallback, ICallbackTable, ISFSocketEvent, NamesDict, ISFSocketConfig, SFSocketEventType, SFEventMap, UEventCallback, UndescribedCallbackFunction, }; //# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/build/messageCodingUtils.d.ts b/build/messageCodingUtils.d.ts index 2b4eaac..f556f89 100644 --- a/build/messageCodingUtils.d.ts +++ b/build/messageCodingUtils.d.ts @@ -1,15 +1,15 @@ -import { ISFSocketEvent } from './SFSocket'; -export declare enum SystemEvents { - CHANNEL_JOINED = "@join", - CHANNEL_JOIN_FAILED = "#join", - CHANNEL_LEFT = "@leave", - CHANNEL_LEAVE_FAILED = "#leave" -} -export declare const SystemTopics: Set; -export declare const decodeMessage: (messageEvent: string | null) => ISFSocketEvent; -export declare const encodeMessage: (event: { - type: string; - payload: any; -}) => string; -export declare const prepareCloseAction: (closeEvent: ISFSocketEvent) => ISFSocketEvent; +import { ISFSocketEvent } from './SFSocket'; +export declare enum SystemEvents { + CHANNEL_JOINED = "@join", + CHANNEL_JOIN_FAILED = "#join", + CHANNEL_LEFT = "@leave", + CHANNEL_LEAVE_FAILED = "#leave" +} +export declare const SystemTopics: Set; +export declare const decodeMessage: (messageEvent: string | null) => ISFSocketEvent; +export declare const encodeMessage: (event: { + type: string; + payload: any; +}) => string; +export declare const prepareCloseAction: (closeEvent: ISFSocketEvent) => ISFSocketEvent; //# sourceMappingURL=messageCodingUtils.d.ts.map \ No newline at end of file diff --git a/build/mock-data.d.ts b/build/mock-data.d.ts index b8ec50a..626636c 100644 --- a/build/mock-data.d.ts +++ b/build/mock-data.d.ts @@ -1,5 +1,5 @@ -import { ISFSocketConfig } from './SFSocket'; -declare const socketOptions: ISFSocketConfig; -declare const makeTestSocketUrl: (options: ISFSocketConfig) => string; -export { socketOptions, makeTestSocketUrl }; +import { ISFSocketConfig } from './SFSocket'; +declare const socketOptions: ISFSocketConfig; +declare const makeTestSocketUrl: (options: ISFSocketConfig) => string; +export { socketOptions, makeTestSocketUrl }; //# sourceMappingURL=mock-data.d.ts.map \ No newline at end of file diff --git a/build/transport/Transport.d.ts b/build/transport/Transport.d.ts index b80f6fa..cc40eec 100644 --- a/build/transport/Transport.d.ts +++ b/build/transport/Transport.d.ts @@ -1,19 +1,19 @@ -import { UndescribedCallbackFunction } from '../types'; -import { ITransportHooks } from './TransportConnection'; -import { ISFSocketConfig } from '../SFSocket'; -export interface IRunner { - abort: () => void; -} -export interface ITransport { - connect(callback: UndescribedCallbackFunction): IRunner; -} -export default class Transport implements ITransport { - hooks: ITransportHooks; - name: string; - options: ISFSocketConfig; - constructor(name: string, options: ISFSocketConfig); - connect(callback: UndescribedCallbackFunction): { - abort: () => void; - }; -} +import { UndescribedCallbackFunction } from '../types'; +import { ITransportHooks } from './TransportConnection'; +import { ISFSocketConfig } from '../SFSocket'; +export interface IRunner { + abort: () => void; +} +export interface ITransport { + connect(callback: UndescribedCallbackFunction): IRunner; +} +export default class Transport implements ITransport { + hooks: ITransportHooks; + name: string; + options: ISFSocketConfig; + constructor(name: string, options: ISFSocketConfig); + connect(callback: UndescribedCallbackFunction): { + abort: () => void; + }; +} //# sourceMappingURL=Transport.d.ts.map \ No newline at end of file diff --git a/build/transport/TransportConnection.d.ts b/build/transport/TransportConnection.d.ts index a8561f8..426975a 100644 --- a/build/transport/TransportConnection.d.ts +++ b/build/transport/TransportConnection.d.ts @@ -1,36 +1,36 @@ -import EventsDispatcher from '../eventdispatcher/EventsDispatcher'; -import { ISFSocketConfig, ISFSocketEvent } from '../SFSocket'; -import { NamesDict } from '../eventdispatcher/events'; -export interface ITransportHooks { - url: string; - isInitialized(): boolean; - getSocket(url: string, options?: ISFSocketConfig): WebSocket; -} -export interface TransportEventMap { - [NamesDict.INITIALIZED]: undefined; - [NamesDict.ERROR]: ISFSocketEvent; - [NamesDict.MESSAGE]: ISFSocketEvent; - [NamesDict.CLOSED]: ISFSocketEvent; - [NamesDict.OPEN]: undefined; - [NamesDict.CONNECTING]: undefined; -} -export default class TransportConnection extends EventsDispatcher { - hooks: ITransportHooks; - name: string; - state: string; - socket?: WebSocket; - initialize: Function; - constructor(hooks: ITransportHooks, name: string); - connect(): boolean; - close(): boolean; - send(data: any): boolean; - private unbindListeners; - private onOpen; - private onError; - private onClose; - private onMessage; - private bindListeners; - private changeState; - private onClosed; -} +import EventsDispatcher from '../eventdispatcher/EventsDispatcher'; +import { ISFSocketConfig, ISFSocketEvent } from '../SFSocket'; +import { NamesDict } from '../eventdispatcher/events'; +export interface ITransportHooks { + url: string; + isInitialized(): boolean; + getSocket(url: string, options?: ISFSocketConfig): WebSocket; +} +export interface TransportEventMap { + [NamesDict.INITIALIZED]: undefined; + [NamesDict.ERROR]: ISFSocketEvent; + [NamesDict.MESSAGE]: ISFSocketEvent; + [NamesDict.CLOSED]: ISFSocketEvent; + [NamesDict.OPEN]: undefined; + [NamesDict.CONNECTING]: undefined; +} +export default class TransportConnection extends EventsDispatcher { + hooks: ITransportHooks; + name: string; + state: string; + socket?: WebSocket; + initialize: Function; + constructor(hooks: ITransportHooks, name: string); + connect(): boolean; + close(): boolean; + send(data: any): boolean; + private unbindListeners; + private onOpen; + private onError; + private onClose; + private onMessage; + private bindListeners; + private changeState; + private onClosed; +} //# sourceMappingURL=TransportConnection.d.ts.map \ No newline at end of file diff --git a/build/types.d.ts b/build/types.d.ts index de10aa3..20c562e 100644 --- a/build/types.d.ts +++ b/build/types.d.ts @@ -1,22 +1,22 @@ -import { NamesDict } from './eventdispatcher/events'; -import { ISFSocketEvent } from './SFSocket'; -export declare type UndescribedCallbackFunction = Function; -export interface ICallback { - fn: F; - channel: string | null; -} -export declare type UEventCallback = (data: EventMap[K]) => any; -export declare type ICallbackTable = { - [key in keyof EventMap]?: Array>>; -}; -export interface SFEventMap { - [NamesDict.CONNECTING]: ISFSocketEvent; - [NamesDict.CONNECTED]: undefined; - [NamesDict.DISCONNECTED]: undefined; - [NamesDict.UNAVAILABLE]: undefined; - [NamesDict.MESSAGE]: ISFSocketEvent; - [NamesDict.ERROR]: ISFSocketEvent; - [NamesDict.CLOSED]: ISFSocketEvent; - [NamesDict.INITIALIZED]: undefined; -} +import { NamesDict } from './eventdispatcher/events'; +import { ISFSocketEvent } from './SFSocket'; +export declare type UndescribedCallbackFunction = Function; +export interface ICallback { + fn: F; + channel: string | null; +} +export declare type UEventCallback = (data: EventMap[K]) => any; +export declare type ICallbackTable = { + [key in keyof EventMap]?: Array>>; +}; +export interface SFEventMap { + [NamesDict.CONNECTING]: ISFSocketEvent; + [NamesDict.CONNECTED]: undefined; + [NamesDict.DISCONNECTED]: undefined; + [NamesDict.UNAVAILABLE]: undefined; + [NamesDict.MESSAGE]: ISFSocketEvent; + [NamesDict.ERROR]: ISFSocketEvent; + [NamesDict.CLOSED]: ISFSocketEvent; + [NamesDict.INITIALIZED]: undefined; +} //# sourceMappingURL=types.d.ts.map \ No newline at end of file diff --git a/lib/SFSocket.ts b/lib/SFSocket.ts index 46cccb8..30a7072 100644 --- a/lib/SFSocket.ts +++ b/lib/SFSocket.ts @@ -3,6 +3,7 @@ import Channel from './Channel'; import ConnectionManager, { ConnectionManagerEventMap } from './connection/ConnectionManager'; import { defaultConfig } from './constants'; import { NamesDict } from './eventdispatcher/events'; +import { RetryStrategy } from './connection/types'; export interface IChannels { [name: string]: Channel; @@ -28,6 +29,8 @@ export interface ISFSocketConfig { unavailableTimeout?: number; useTLS?: boolean; retryTimeout?: number; + // eslint-disable-next-line no-use-before-define + retryStrategy?: RetryStrategy; } export interface ISFSocketEvent { diff --git a/lib/connection/ConnectionManager.ts b/lib/connection/ConnectionManager.ts index da01bb6..74c96fe 100644 --- a/lib/connection/ConnectionManager.ts +++ b/lib/connection/ConnectionManager.ts @@ -42,6 +42,8 @@ export default class ConnectionManager extends EventsDispatcher { // TODO + const callback: UndescribedCallbackFunction = async (closeEvent: ISFSocketEvent | undefined, connection: Connection) => { // TODO if (closeEvent) { this.abandonConnection(); - if (this.shouldRetry(closeEvent)) { + if (await this.shouldRetry(closeEvent)) { this.retryIn(this.options.retryTimeout || 0); } this.emit(NamesDict.CLOSED, closeEvent); @@ -199,9 +201,9 @@ export default class ConnectionManager extends EventsDispatcher { + closed: async (closeEvent: ISFSocketEvent) => { this.abandonConnection(); - if (this.shouldRetry(closeEvent)) { + if (await this.shouldRetry(closeEvent)) { this.retryIn(this.options.retryTimeout || 0); } this.emit(NamesDict.CLOSED, closeEvent); @@ -245,6 +247,8 @@ export default class ConnectionManager extends EventsDispatcher { + if (this.options.retryStrategy) { + const { retry, state } = await this.options.retryStrategy(closeEvent, this.retryState); + if (retry) { + this.retryState = state; + } else { + this.retryState = undefined; + } + return retry; + } + return false; } } diff --git a/lib/connection/types.ts b/lib/connection/types.ts index 7ad8848..b6214c2 100644 --- a/lib/connection/types.ts +++ b/lib/connection/types.ts @@ -1,4 +1,4 @@ -import { ISFSocketEvent } from '../SFSocket'; +import { ISFSocketEvent, SFSocket } from '../SFSocket'; export interface IAction { action: string; @@ -19,3 +19,5 @@ export interface IConnectionCallbacks { error: (error: ISFSocketEvent) => void; // TODO closed: (reason: any) => void; // TODO } + +export type RetryStrategy = (event: ISFSocketEvent, prevState: unknown) => Promise<({ state: unknown, retry: boolean })> diff --git a/lib/constants.ts b/lib/constants.ts index fb8227f..c39b675 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -1,10 +1,12 @@ import { ISFSocketConfig } from './SFSocket'; +import { retryStrategy } from './retrystrategy'; export const defaultConfig: ISFSocketConfig = { host: '', port: 80, path: '', unavailableTimeout: 10000, - retryTimeout: 1000, + retryTimeout: 10, useTLS: false, + retryStrategy: retryStrategy({ retries: 3, delay: 1000, multiplier: 5 }), }; diff --git a/lib/retrystrategy/index.ts b/lib/retrystrategy/index.ts new file mode 100644 index 0000000..b6233e0 --- /dev/null +++ b/lib/retrystrategy/index.ts @@ -0,0 +1,35 @@ +import { ISFSocketEvent } from '../SFSocket'; +import { RetryStrategy } from '../connection/types'; + +export interface IRetryState { + delay: number; + retry: number; +} + +export const INFINITE_RETRIES = -1; + +export const delay = async (d: number) => new Promise((resolve) => { + setTimeout(resolve, d); +}); + +export const retryStrategy = ( + options: { retries?: number, delay?: number, multiplier?: number }, +): RetryStrategy => async (event: ISFSocketEvent, prevState?: unknown) => { + const state = prevState as (IRetryState | undefined); + if (options.retries === INFINITE_RETRIES) { + return Promise.resolve({ retry: true, state: undefined }); + } + if (options.retries === 0) { + return Promise.resolve({ retry: false, state: undefined }); + } + const multiplier = options.multiplier ?? 1; + const retriesAllowed = options.retries ?? 1; + const retriesDone = state?.retry ?? 0; + const newDelay = (state?.delay) ? (state.delay * multiplier) : (options.delay ?? 0); + + if (retriesDone < retriesAllowed) { + await delay(newDelay); + return Promise.resolve({ retry: true, state: { delay: newDelay, retry: retriesDone + 1 } }); + } + return Promise.resolve({ retry: true, state: undefined }); +}; From 626f697f0e697c897edcab69ef336d2938ee92a3 Mon Sep 17 00:00:00 2001 From: Andrei Kirkouski Date: Thu, 20 Oct 2022 13:23:52 +0400 Subject: [PATCH 2/2] retry strategy --- build/SFSocket.d.ts | 2 ++ build/SFSocket.d.ts.map | 2 +- build/connection/ConnectionManager.d.ts | 1 + build/connection/ConnectionManager.d.ts.map | 2 +- build/connection/types.d.ts | 4 ++++ build/connection/types.d.ts.map | 2 +- build/constants.d.ts.map | 2 +- build/retrystrategy/index.d.ts | 13 +++++++++++++ build/retrystrategy/index.d.ts.map | 1 + build/socket.js | 2 +- build/socket.js.map | 2 +- 11 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 build/retrystrategy/index.d.ts create mode 100644 build/retrystrategy/index.d.ts.map diff --git a/build/SFSocket.d.ts b/build/SFSocket.d.ts index aa061b4..8eaa1b3 100644 --- a/build/SFSocket.d.ts +++ b/build/SFSocket.d.ts @@ -1,6 +1,7 @@ import { UEventCallback } from './types'; import Channel from './Channel'; import ConnectionManager, { ConnectionManagerEventMap } from './connection/ConnectionManager'; +import { RetryStrategy } from './connection/types'; export interface IChannels { [name: string]: Channel; } @@ -24,6 +25,7 @@ export interface ISFSocketConfig { unavailableTimeout?: number; useTLS?: boolean; retryTimeout?: number; + retryStrategy?: RetryStrategy; } export interface ISFSocketEvent { type: SFSocketEventType; diff --git a/build/SFSocket.d.ts.map b/build/SFSocket.d.ts.map index 573899a..83884b3 100644 --- a/build/SFSocket.d.ts.map +++ b/build/SFSocket.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"SFSocket.d.ts","sourceRoot":"","sources":["../lib/SFSocket.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,OAAO,MAAM,WAAW,CAAC;AAChC,OAAO,iBAAiB,EAAE,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAI9F,MAAM,WAAW,SAAS;IACxB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CACzB;AAGD,oBAAY,iBAAiB;IAC3B,UAAU,wBAAwB;IAClC,OAAO,qBAAqB;IAC5B,cAAc,mBAAmB;IACjC,mBAAmB,wBAAwB;IAC3C,YAAY,iBAAiB;IAC7B,oBAAoB,yBAAyB;IAC7C,KAAK,mBAAmB;IACxB,MAAM,oBAAoB;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACxC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,iBAAiB,CAAC;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;KACxB,GAAG,IAAI,CAAA;CACT;AAED,qBAAa,QAAQ;IAEnB,MAAM,CAAC,SAAS,EAAE,QAAQ,EAAE,CAAM;IAElC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAS;IAEhC,MAAM,CAAC,KAAK;IAQZ,OAAO,CAAC,MAAM,CAAkB;IAEhC,OAAO,CAAC,QAAQ,CAAiB;IAEjC,IAAI,EAAE,iBAAiB,CAAC;gBAEZ,OAAO,CAAC,EAAE,eAAe;IAoCrC,OAAO;IAIP,UAAU;IASV,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG;IAItC,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE;IAMvC,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE;IAavC,SAAS,CAAC,CAAC,SAAS,MAAM,yBAAyB,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,cAAc,CAAC,yBAAyB,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM;IAW3I,WAAW,CAAC,CAAC,SAAS,MAAM,yBAAyB,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,cAAc,CAAC,yBAAyB,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM;IAI7I,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,GAAE,OAAe;IAWzD,YAAY,CAAC,UAAU,EAAE,MAAM;IAU/B,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;CAGlC"} \ No newline at end of file +{"version":3,"file":"SFSocket.d.ts","sourceRoot":"","sources":["../lib/SFSocket.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,OAAO,MAAM,WAAW,CAAC;AAChC,OAAO,iBAAiB,EAAE,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAG9F,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,MAAM,WAAW,SAAS;IACxB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CACzB;AAGD,oBAAY,iBAAiB;IAC3B,UAAU,wBAAwB;IAClC,OAAO,qBAAqB;IAC5B,cAAc,mBAAmB;IACjC,mBAAmB,wBAAwB;IAC3C,YAAY,iBAAiB;IAC7B,oBAAoB,yBAAyB;IAC7C,KAAK,mBAAmB;IACxB,MAAM,oBAAoB;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACxC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,iBAAiB,CAAC;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;KACxB,GAAG,IAAI,CAAA;CACT;AAED,qBAAa,QAAQ;IAEnB,MAAM,CAAC,SAAS,EAAE,QAAQ,EAAE,CAAM;IAElC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAS;IAEhC,MAAM,CAAC,KAAK;IAQZ,OAAO,CAAC,MAAM,CAAkB;IAEhC,OAAO,CAAC,QAAQ,CAAiB;IAEjC,IAAI,EAAE,iBAAiB,CAAC;gBAEZ,OAAO,CAAC,EAAE,eAAe;IAoCrC,OAAO;IAIP,UAAU;IASV,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG;IAItC,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE;IAMvC,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE;IAavC,SAAS,CAAC,CAAC,SAAS,MAAM,yBAAyB,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,cAAc,CAAC,yBAAyB,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM;IAW3I,WAAW,CAAC,CAAC,SAAS,MAAM,yBAAyB,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,cAAc,CAAC,yBAAyB,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM;IAI7I,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,GAAE,OAAe;IAWzD,YAAY,CAAC,UAAU,EAAE,MAAM;IAU/B,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;CAGlC"} \ No newline at end of file diff --git a/build/connection/ConnectionManager.d.ts b/build/connection/ConnectionManager.d.ts index 4643111..958ff38 100644 --- a/build/connection/ConnectionManager.d.ts +++ b/build/connection/ConnectionManager.d.ts @@ -20,6 +20,7 @@ export default class ConnectionManager extends EventsDispatcher void; closed: (reason: any) => void; } +export declare type RetryStrategy = (event: ISFSocketEvent, prevState: unknown) => Promise<({ + state: unknown; + retry: boolean; +})>; //# sourceMappingURL=types.d.ts.map \ No newline at end of file diff --git a/build/connection/types.d.ts.map b/build/connection/types.d.ts.map index c8a5ec0..48e3af2 100644 --- a/build/connection/types.d.ts.map +++ b/build/connection/types.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../lib/connection/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,WAAW,OAAO;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,GAAG,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC5B,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,WAAW,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CAC1C;AAED,MAAM,WAAW,oBAAoB;IACjC,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC5C,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAChD,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC1C,OAAO,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAC3C,KAAK,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IACvC,MAAM,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;CACjC"} \ No newline at end of file +{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../lib/connection/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAY,MAAM,aAAa,CAAC;AAEvD,MAAM,WAAW,OAAO;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,GAAG,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC5B,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,WAAW,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CAC1C;AAED,MAAM,WAAW,oBAAoB;IACjC,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC5C,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAChD,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC1C,OAAO,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAC3C,KAAK,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IACvC,MAAM,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;CACjC;AAED,oBAAY,aAAa,GAAG,CAAC,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC,CAAA"} \ No newline at end of file diff --git a/build/constants.d.ts.map b/build/constants.d.ts.map index 6a0f779..cd08d91 100644 --- a/build/constants.d.ts.map +++ b/build/constants.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../lib/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,eAAO,MAAM,aAAa,EAAE,eAO3B,CAAC"} \ No newline at end of file +{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../lib/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG7C,eAAO,MAAM,aAAa,EAAE,eAQ3B,CAAC"} \ No newline at end of file diff --git a/build/retrystrategy/index.d.ts b/build/retrystrategy/index.d.ts new file mode 100644 index 0000000..4ae43c1 --- /dev/null +++ b/build/retrystrategy/index.d.ts @@ -0,0 +1,13 @@ +import { RetryStrategy } from '../connection/types'; +export interface IRetryState { + delay: number; + retry: number; +} +export declare const INFINITE_RETRIES = -1; +export declare const delay: (d: number) => Promise; +export declare const retryStrategy: (options: { + retries?: number; + delay?: number; + multiplier?: number; +}) => RetryStrategy; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/build/retrystrategy/index.d.ts.map b/build/retrystrategy/index.d.ts.map new file mode 100644 index 0000000..a4d59f7 --- /dev/null +++ b/build/retrystrategy/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/retrystrategy/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC,eAAO,MAAM,KAAK,MAAa,MAAM,qBAEnC,CAAC;AAEH,eAAO,MAAM,aAAa,YACf;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,KACjE,aAkBF,CAAC"} \ No newline at end of file diff --git a/build/socket.js b/build/socket.js index 7a6b0bc..714db97 100644 --- a/build/socket.js +++ b/build/socket.js @@ -1,2 +1,2 @@ -!function(t,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.SFSocket=n():t.SFSocket=n()}(window,(function(){return function(t){var n={};function e(s){if(n[s])return n[s].exports;var i=n[s]={i:s,l:!1,exports:{}};return t[s].call(i.exports,i,i.exports,e),i.l=!0,i.exports}return e.m=t,e.c=n,e.d=function(t,n,s){e.o(t,n)||Object.defineProperty(t,n,{enumerable:!0,get:s})},e.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},e.t=function(t,n){if(1&n&&(t=e(t)),8&n)return t;if(4&n&&"object"==typeof t&&t&&t.__esModule)return t;var s=Object.create(null);if(e.r(s),Object.defineProperty(s,"default",{enumerable:!0,value:t}),2&n&&"string"!=typeof t)for(var i in t)e.d(s,i,function(n){return t[n]}.bind(null,i));return s},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},e.p="",e(e.s=0)}([function(t,n,e){"use strict";var s;function i(t,n,e){let s=e.value;if("function"!=typeof s)throw new TypeError(`@autobind decorator can only be applied to methods not: ${typeof s}`);let i=!1;return{configurable:!0,get(){if(i||this===t.prototype||this.hasOwnProperty(n)||"function"!=typeof s)return s;const e=s.bind(this);return i=!0,Object.defineProperty(this,n,{configurable:!0,get:()=>e,set(t){s=t,delete this[n]}}),i=!1,e},set(t){s=t}}}e.r(n),e.d(n,"SFSocket",(function(){return m})),e.d(n,"makeSocketOptions",(function(){return y})),e.d(n,"eventNames",(function(){return s})),e.d(n,"Channel",(function(){return a})),e.d(n,"ChannelStatus",(function(){return o})),e.d(n,"NamesDict",(function(){return s})),e.d(n,"SFSocketEventType",(function(){return f})),function(t){t.CONNECTED="connected",t.MESSAGE="message",t.CONNECTING="connecting",t.DISCONNECTED="disconnected",t.ERROR="error",t.OPEN="open",t.INITIALIZED="initialized",t.CLOSED="closed",t.UNAVAILABLE="unavailable",t.CHANNEL_JOINED="channel_joined",t.CHANNEL_JOIN_FAILED="channel_join_failed",t.CHANNEL_LEFT="channel_left"}(s||(s={}));var o,r=function(t,n,e,s){var i,o=arguments.length,r=o<3?n:null===s?s=Object.getOwnPropertyDescriptor(n,e):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(t,n,e,s);else for(var a=t.length-1;a>=0;a--)(i=t[a])&&(r=(o<3?i(r):o>3?i(n,e,r):i(n,e))||r);return o>3&&r&&Object.defineProperty(n,e,r),r};!function(t){t.CLOSED="closed",t.JOINING="joining",t.JOINED="joined",t.LEAVING="leaving",t.ERROR="error"}(o||(o={}));class a{constructor(t,n){this.channelStatus=o.CLOSED,this.selfName=t,this.socket=n,this.cMgr=n.cMgr,this.enabled=!1}get status(){return this.channelStatus}get name(){return this.selfName}get isActive(){return this.cMgr.isConnected()&&this.channelStatus===o.JOINED}join(){this.enabled||(this.enabled=!0,this.startListening(),this.cMgr.isConnected()&&this.sendJoinCommand())}subscribe(t,n){this.cMgr.bind(t,n,this.name)}unsubscribe(t,n){this.cMgr.unbind(t,n,this.name)}leave(){this.enabled&&(this.enabled=!1,this.channelStatus=o.LEAVING,this.cMgr.isConnected()&&this.cMgr.sendLeave([this.name]),this.stopListening())}onConnect(){this.enabled&&this.sendJoinCommand()}onDisconnect(){this.channelStatus=o.CLOSED}onJoin(t){t.indexOf(this.name)>=0&&(this.channelStatus=o.JOINED)}onLeft(t){t.indexOf(this.name)>=0&&(this.channelStatus=o.CLOSED)}onJoinFailed(t){t.indexOf(this.name)>=0&&(this.channelStatus=o.ERROR)}sendJoinCommand(){this.channelStatus!==o.JOINING&&(this.channelStatus=o.JOINING,this.cMgr.sendJoin([this.name]))}startListening(){this.cMgr.bind(s.DISCONNECTED,this.onDisconnect),this.cMgr.bind(s.CHANNEL_JOIN_FAILED,this.onJoinFailed),this.cMgr.bind(s.CHANNEL_JOINED,this.onJoin),this.cMgr.bind(s.CHANNEL_LEFT,this.onLeft),this.cMgr.bind(s.CONNECTED,this.onConnect)}stopListening(){this.cMgr.unbind(s.DISCONNECTED,this.onDisconnect),this.cMgr.unbind(s.CHANNEL_JOIN_FAILED,this.onJoinFailed),this.cMgr.unbind(s.CHANNEL_JOINED,this.onJoin),this.cMgr.unbind(s.CHANNEL_LEFT,this.onLeft),this.cMgr.unbind(s.CONNECTED,this.onConnect)}}r([i],a.prototype,"onConnect",null),r([i],a.prototype,"onDisconnect",null),r([i],a.prototype,"onJoin",null),r([i],a.prototype,"onLeft",null),r([i],a.prototype,"onJoinFailed",null);const c={host:"",port:80,path:"",unavailableTimeout:1e4,retryTimeout:1e3,useTLS:!1};class l{constructor(){this.callbacks={}}get(t){return this.callbacks[t]||[]}add(t,n,e){this.callbacks[t]||(this.callbacks[t]=[]),this.callbacks[t].push({fn:n,channel:e||null})}remove(t,n,e){if(!t&&!n&&!e)return void(this.callbacks={});const s=t?[t]:Object.keys(this.callbacks);n||e?this.removeCallback(s,n,e):this.removeAllCallbacks(s)}removeCallback(t,n,e){t.forEach(t=>{const s=this.callbacks[t]||[];this.callbacks[t]=s.filter(t=>{const s=n&&n===t.fn,i=e&&e===t.channel;return!s&&!i})})}removeAllCallbacks(t){t.forEach(t=>{delete this.callbacks[t]})}}class h{constructor(){this.callbacks=new l}bind(t,n,e){return this.callbacks.add(t,n,e),this}unbind(t,n,e){return this.callbacks.remove(t,n,e),this}emit(t,n){const e=this.callbacks.get(t),s=(i=n)&&i.context&&"string"==typeof i.context.channel?n.context.channel:null;var i;return e&&e.length>0&&e.forEach(t=>{const e=t.channel===s;(!t.channel||!s||e)&&t.fn(n)}),this}}var d;!function(t){t.CHANNEL_JOINED="@join",t.CHANNEL_JOIN_FAILED="#join",t.CHANNEL_LEFT="@leave",t.CHANNEL_LEAVE_FAILED="#leave"}(d||(d={}));const u=new Set([d.CHANNEL_JOINED,d.CHANNEL_JOIN_FAILED,d.CHANNEL_LEFT,d.CHANNEL_LEAVE_FAILED]),E=t=>{const n={command:t.type,topics:t.payload};return JSON.stringify(n)};var N;!function(t){t.JOIN="join",t.LEAVE="leave"}(N||(N={}));const b=new Set([N.JOIN,N.LEAVE]);class C extends h{constructor(t,n){super(),this.id=t,this.transport=n,this.bindListeners()}send(t){return!!this.transport&&this.transport.send(t)}sendCommand(t,n){if(b.has(t))throw new Error(`${t} is a reserved command, cant be sent manually`);return this.send(E({type:t,payload:n}))}sendJoin(t){this.send(E({type:N.JOIN,payload:t}))}sendLeave(t){this.send(E({type:N.LEAVE,payload:t}))}close(){this.transport&&this.transport.close()}bindListeners(){const t=t=>{this.transport&&(this.transport.unbind(s.MESSAGE,t.message),this.transport.unbind(s.ERROR,t.error),this.transport.unbind(s.CLOSED,t.closed))},n={message:t=>{let n=null;try{n=(t=>{if(t){const n=JSON.parse(t);if(u.has(n.topic)&&!n.payload&&Array.isArray(n.payload))return{type:f.ERROR,error:"WS event system event payload is invalid. Should be a string array",data:"MessageParseError"};switch(n.topic){case d.CHANNEL_JOINED:return{type:f.CHANNEL_JOINED,error:null,data:n.payload};case d.CHANNEL_JOIN_FAILED:return{type:f.CHANNEL_JOIN_FAILED,error:null,data:n.payload};case d.CHANNEL_LEFT:return{type:f.CHANNEL_LEFT,error:null,data:n.payload};case d.CHANNEL_LEAVE_FAILED:return{type:f.CHANNEL_LEAVE_FAILED,error:null,data:n.payload};default:return{type:f.MESSAGE,error:null,data:n.payload||null,context:Object.assign({},n.topic?{channel:n.topic}:{})}}}return{type:f.ERROR,error:"MessageEvent has no data property",data:"MessageParseError"}})(t.data)}catch(n){this.emit(s.ERROR,{type:f.ERROR,error:n.toString(),data:JSON.stringify(t)})}if(n)switch(n.type){case f.ERROR:this.emit(s.ERROR,n);break;case f.CHANNEL_JOIN_FAILED:this.emit(s.CHANNEL_JOIN_FAILED,n.data);break;case f.CHANNEL_JOINED:this.emit(s.CHANNEL_JOINED,n.data);break;case f.CHANNEL_LEFT:this.emit(s.CHANNEL_LEFT,n.data);break;case f.CHANNEL_LEAVE_FAILED:this.emit(s.ERROR,n);break;default:this.emit(s.MESSAGE,n)}},error:t=>{this.emit(s.ERROR,Object.assign(Object.assign({},t),{type:f.ERROR,data:null}))},closed:e=>{var i;t(n),(i=e).context&&void 0!==i.context.code&&this.handleCloseEvent(e),this.transport=null,this.emit(s.CLOSED,e)}};this.transport&&(this.transport.bind(s.MESSAGE,n.message),this.transport.bind(s.ERROR,n.error),this.transport.bind(s.CLOSED,n.closed))}handleCloseEvent(t){const n=(t=>t.context&&t.context.code?t.context.code<4e3&&t.context.code>=1002&&t.context.code<=1004?Object.assign(Object.assign({},t),{error:"Socket is unavailable"}):t:(console.error("Socket event do not contain close code"),Object.assign(Object.assign({},t),{error:"Connection refused"})))(t);n.type===f.CLOSED?this.emit(s.CLOSED,n):this.emit(s.ERROR,n)}}class p extends h{constructor(t,n){super(),this.initialize=()=>{const t=this;t.hooks.isInitialized()?t.changeState(s.INITIALIZED):t.onClose()},this.hooks=t,this.name=n,this.state="new"}connect(){if(this.socket||"initialized"!==this.state)return!1;const{url:t}=this.hooks;try{this.socket=this.hooks.getSocket(t)}catch(t){return setTimeout(()=>{this.onError(t),this.onClosed({type:f.ERROR,data:t,error:t.toString(),context:{code:0}})}),!1}return this.bindListeners(),this.changeState(s.CONNECTING),!0}close(){return!!this.socket&&(this.socket.close(),!0)}send(t){return"open"===this.state&&(setTimeout(()=>{this.socket&&this.socket.send(t)}),!0)}unbindListeners(){this.socket&&(this.socket.onopen=null,this.socket.onerror=null,this.socket.onclose=null,this.socket.onmessage=null)}onOpen(){this.changeState(s.OPEN),this.socket&&(this.socket.onopen=null)}onError(t){var n;(n=t)&&"string"==typeof n.type&&void 0!==n.currentTarget?this.emit(s.ERROR,{type:f.ERROR,error:"Websocket connection error",data:null}):this.emit(s.ERROR,{type:f.ERROR,error:t?t.message:"Websocket connection error",data:t?t.name:null})}onClose(t){t?this.onClosed({type:t.wasClean?f.CLOSED:f.ERROR,data:t.wasClean?t.reason:null,error:t.wasClean?null:t.reason,context:{code:t.code}}):this.onClosed({type:f.ERROR,data:null,error:"Closed for unknown reason",context:{code:0}}),this.unbindListeners(),this.socket=void 0}onMessage(t){this.emit(s.MESSAGE,{type:f.MESSAGE,data:"string"==typeof t.data?t.data:JSON.stringify(t.data),error:null})}bindListeners(){this.socket&&(this.socket.onopen=()=>{this.onOpen()},this.socket.onerror=t=>{this.onError(t)},this.socket.onclose=t=>{this.onClose(t)},this.socket.onmessage=t=>{this.onMessage(t)})}changeState(t){this.state=t,this.emit(t,void 0)}onClosed(t){this.state=s.CLOSED,this.emit(s.CLOSED,t)}}class O{constructor(t,n){this.options=n||{};const e=`ws${n.useTLS?"s":""}`,s=`${n.host}:${n.port}`,i=n.queryParams?Object.entries(n.queryParams).map(([t,n])=>`${encodeURIComponent(t)}=${encodeURIComponent(n)}`).join("&"):null,o=`${e}://${s}/${n.path}${i?`?${i}`:""}`;this.hooks={url:o,isInitialized:()=>!!window.WebSocket,getSocket:t=>new WebSocket(t)},this.name=t}connect(t){let n=!1;const e=new p(this.hooks,this.name),i=()=>{e.unbind(s.INITIALIZED,i),e.connect()},o=()=>{e.unbind(s.INITIALIZED,i),e.unbind(s.OPEN,r),e.unbind(s.ERROR,a),e.unbind(s.CLOSED,c)},r=()=>{n=!0,o();const s=new C("",e);t(null,s)},a=()=>{},c=n=>{o(),t(n)};return e.bind(s.INITIALIZED,i),e.bind(s.OPEN,r),e.bind(s.ERROR,a),e.bind(s.CLOSED,c),e.initialize(),{abort:()=>{n||(o(),e.close())}}}}class L extends h{constructor(t){super(),this.options=Object.assign(Object.assign({},c),t),this.state="initialized",this.connection=null,this.errorCallbacks=this.buildErrorCallbacks(),this.connectionCallbacks=this.buildConnectionCallbacks(this.errorCallbacks),this.transport=new O("ws",t),this.runner=null,this.unavailableTimer=null,this.retryTimer=null}connect(){this.connection||this.runner||(this.updateState(s.CONNECTING),this.startConnecting(),this.setUnavailableTimer())}send(t){return!!this.connection&&this.connection.send(t)}sendCommand(t,n){return!!this.connection&&this.connection.sendCommand(t,n)}sendJoin(t){return this.connection&&this.connection.sendJoin(t),!1}sendLeave(t){return this.connection&&this.connection.sendLeave(t),!1}disconnect(){this.disconnectInternally(),this.updateState(s.DISCONNECTED)}isConnected(){return this.state===s.CONNECTED}startConnecting(){this.runner=this.transport.connect((t,n)=>{t?(this.abandonConnection(),this.shouldRetry(t)&&this.retryIn(this.options.retryTimeout||0),this.emit(s.CLOSED,t)):(this.abortConnecting(),this.clearUnavailableTimer(),this.setConnection(n),this.updateState(s.CONNECTED))})}abortConnecting(){this.runner&&(this.runner.abort(),this.runner=null)}disconnectInternally(){if(this.abortConnecting(),this.clearRetryTimer(),this.clearUnavailableTimer(),this.connection){const t=this.abandonConnection();t&&t.close()}}retryIn(t){t>0&&this.emit(s.CONNECTING,{type:f.CONNECTING,data:String(Math.round(t/1e3)),error:null}),this.retryTimer=setTimeout(()=>{this.disconnectInternally(),this.connect()},t||0)}clearRetryTimer(){null!==this.retryTimer&&clearTimeout(this.retryTimer),this.retryTimer=null}setUnavailableTimer(){this.unavailableTimer=setTimeout(()=>{this.updateState(s.UNAVAILABLE)},this.options.unavailableTimeout)}clearUnavailableTimer(){null!==this.unavailableTimer&&clearTimeout(this.unavailableTimer),this.unavailableTimer=null}buildConnectionCallbacks(t){return Object.assign(Object.assign({},t),{message:t=>{this.emit(s.MESSAGE,t)},error:t=>{this.emit(s.ERROR,t)},closed:t=>{this.abandonConnection(),this.shouldRetry(t)&&this.retryIn(this.options.retryTimeout||0),this.emit(s.CLOSED,t)},channelJoined:t=>this.emit(s.CHANNEL_JOINED,t),channelJoinFailed:t=>this.emit(s.CHANNEL_JOIN_FAILED,t),channelLeft:t=>this.emit(s.CHANNEL_LEFT,t)})}buildErrorCallbacks(){const t=t=>n=>{n.error&&this.emit(s.ERROR,{type:f.ERROR,data:null,error:n.error}),t(n)};return{refused:t(()=>{this.disconnect()}),unavailable:t(()=>{this.retryIn(1e3)})}}setConnection(t){this.connection=t,this.connection&&(this.connection.bind(s.MESSAGE,this.connectionCallbacks.message),this.connection.bind(s.CHANNEL_LEFT,this.connectionCallbacks.channelLeft),this.connection.bind(s.CHANNEL_JOIN_FAILED,this.connectionCallbacks.channelJoinFailed),this.connection.bind(s.CHANNEL_JOINED,this.connectionCallbacks.channelJoined),this.connection.bind(s.ERROR,this.connectionCallbacks.error),this.connection.bind(s.CLOSED,this.connectionCallbacks.closed))}abandonConnection(){if(!this.connection)return null;this.connection.unbind(s.MESSAGE,this.connectionCallbacks.message),this.connection.unbind(s.CHANNEL_LEFT,this.connectionCallbacks.channelLeft),this.connection.unbind(s.CHANNEL_JOIN_FAILED,this.connectionCallbacks.channelJoinFailed),this.connection.unbind(s.CHANNEL_JOINED,this.connectionCallbacks.channelJoined),this.connection.unbind(s.ERROR,this.connectionCallbacks.error),this.connection.unbind(s.CLOSED,this.connectionCallbacks.closed);const{connection:t}=this;return this.connection=null,t}updateState(t){const n=this.state;this.state=t,n!==t&&this.emit(t,void 0)}shouldRetry(t){return this.state===s.CONNECTING||this.state===s.CONNECTED}}var f;!function(t){t.CONNECTING="sfSocket:connecting",t.MESSAGE="sfSocket:message",t.CHANNEL_JOINED="channel_joined",t.CHANNEL_JOIN_FAILED="channel_join_failed",t.CHANNEL_LEFT="channel_left",t.CHANNEL_LEAVE_FAILED="channel_leave_failed",t.ERROR="sfSocket:error",t.CLOSED="sfSocket:closed"}(f||(f={}));class m{constructor(t){if(this.channels={},!t||"object"!=typeof t)throw new Error("sfSocket options should be an object");const n=t||{};this.config=Object.assign(Object.assign({},c),n),this.config.port=n.useTLS?443:80,n.port&&(this.config.port=n.port),this.cMgr=new L(this.config),this.cMgr.bind(s.CONNECTED,()=>{Object.values(this.channels).forEach(t=>{t.join()})}),this.cMgr.bind(s.ERROR,t=>{console.error(t)}),m.instances.push(this),m.isReady&&this.connect()}static ready(){m.isReady=!0,m.instances.forEach(t=>{t.connect()})}connect(){this.cMgr.connect()}disconnect(){this.cMgr.disconnect()}sendCommand(t,n){return this.cMgr.sendCommand(t,n)}joinChannelList(t){t.forEach(t=>{this.joinChannel(t)})}leaveChannelList(t){t.forEach(t=>{this.leaveChannel(t)})}subscribe(t,n,e){return this.cMgr.bind(t,n,e)}unsubscribe(t,n,e){return this.cMgr.unbind(t,n,e)}joinChannel(t,n=!1){if(this.channels[t])throw new Error(`Channel ${t} already exists`);return this.channels[t]=new a(t,this),n||this.channels[t].join(),this.channels[t]}leaveChannel(t){if(!this.channels[t])throw new Error(`Channel ${t} does not exist`);const n=this.channels[t];return n.leave(),delete this.channels[t],n}getChannel(t){return this.channels[t]}}m.instances=[],m.isReady=!1;const y=t=>{const n=new URL(t),e=n.protocol?n.protocol.replace(":",""):null;return n.hostname&&n.port&&e?{host:n.hostname,port:n.port,path:e}:null}}])})); +!function(t,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.SFSocket=n():t.SFSocket=n()}(window,(function(){return function(t){var n={};function e(i){if(n[i])return n[i].exports;var s=n[i]={i:i,l:!1,exports:{}};return t[i].call(s.exports,s,s.exports,e),s.l=!0,s.exports}return e.m=t,e.c=n,e.d=function(t,n,i){e.o(t,n)||Object.defineProperty(t,n,{enumerable:!0,get:i})},e.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},e.t=function(t,n){if(1&n&&(t=e(t)),8&n)return t;if(4&n&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(e.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&n&&"string"!=typeof t)for(var s in t)e.d(i,s,function(n){return t[n]}.bind(null,s));return i},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},e.p="",e(e.s=0)}([function(t,n,e){"use strict";var i;function s(t,n,e){let i=e.value;if("function"!=typeof i)throw new TypeError(`@autobind decorator can only be applied to methods not: ${typeof i}`);let s=!1;return{configurable:!0,get(){if(s||this===t.prototype||this.hasOwnProperty(n)||"function"!=typeof i)return i;const e=i.bind(this);return s=!0,Object.defineProperty(this,n,{configurable:!0,get:()=>e,set(t){i=t,delete this[n]}}),s=!1,e},set(t){i=t}}}e.r(n),e.d(n,"SFSocket",(function(){return S})),e.d(n,"makeSocketOptions",(function(){return v})),e.d(n,"eventNames",(function(){return i})),e.d(n,"Channel",(function(){return a})),e.d(n,"ChannelStatus",(function(){return o})),e.d(n,"NamesDict",(function(){return i})),e.d(n,"SFSocketEventType",(function(){return O})),function(t){t.CONNECTED="connected",t.MESSAGE="message",t.CONNECTING="connecting",t.DISCONNECTED="disconnected",t.ERROR="error",t.OPEN="open",t.INITIALIZED="initialized",t.CLOSED="closed",t.UNAVAILABLE="unavailable",t.CHANNEL_JOINED="channel_joined",t.CHANNEL_JOIN_FAILED="channel_join_failed",t.CHANNEL_LEFT="channel_left"}(i||(i={}));var o,r=function(t,n,e,i){var s,o=arguments.length,r=o<3?n:null===i?i=Object.getOwnPropertyDescriptor(n,e):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(t,n,e,i);else for(var a=t.length-1;a>=0;a--)(s=t[a])&&(r=(o<3?s(r):o>3?s(n,e,r):s(n,e))||r);return o>3&&r&&Object.defineProperty(n,e,r),r};!function(t){t.CLOSED="closed",t.JOINING="joining",t.JOINED="joined",t.LEAVING="leaving",t.ERROR="error"}(o||(o={}));class a{constructor(t,n){this.channelStatus=o.CLOSED,this.selfName=t,this.socket=n,this.cMgr=n.cMgr,this.enabled=!1}get status(){return this.channelStatus}get name(){return this.selfName}get isActive(){return this.cMgr.isConnected()&&this.channelStatus===o.JOINED}join(){this.enabled||(this.enabled=!0,this.startListening(),this.cMgr.isConnected()&&this.sendJoinCommand())}subscribe(t,n){this.cMgr.bind(t,n,this.name)}unsubscribe(t,n){this.cMgr.unbind(t,n,this.name)}leave(){this.enabled&&(this.enabled=!1,this.channelStatus=o.LEAVING,this.cMgr.isConnected()&&this.cMgr.sendLeave([this.name]),this.stopListening())}onConnect(){this.enabled&&this.sendJoinCommand()}onDisconnect(){this.channelStatus=o.CLOSED}onJoin(t){t.indexOf(this.name)>=0&&(this.channelStatus=o.JOINED)}onLeft(t){t.indexOf(this.name)>=0&&(this.channelStatus=o.CLOSED)}onJoinFailed(t){t.indexOf(this.name)>=0&&(this.channelStatus=o.ERROR)}sendJoinCommand(){this.channelStatus!==o.JOINING&&(this.channelStatus=o.JOINING,this.cMgr.sendJoin([this.name]))}startListening(){this.cMgr.bind(i.DISCONNECTED,this.onDisconnect),this.cMgr.bind(i.CHANNEL_JOIN_FAILED,this.onJoinFailed),this.cMgr.bind(i.CHANNEL_JOINED,this.onJoin),this.cMgr.bind(i.CHANNEL_LEFT,this.onLeft),this.cMgr.bind(i.CONNECTED,this.onConnect)}stopListening(){this.cMgr.unbind(i.DISCONNECTED,this.onDisconnect),this.cMgr.unbind(i.CHANNEL_JOIN_FAILED,this.onJoinFailed),this.cMgr.unbind(i.CHANNEL_JOINED,this.onJoin),this.cMgr.unbind(i.CHANNEL_LEFT,this.onLeft),this.cMgr.unbind(i.CONNECTED,this.onConnect)}}r([s],a.prototype,"onConnect",null),r([s],a.prototype,"onDisconnect",null),r([s],a.prototype,"onJoin",null),r([s],a.prototype,"onLeft",null),r([s],a.prototype,"onJoinFailed",null);var c=function(t,n,e,i){return new(e||(e=Promise))((function(s,o){function r(t){try{c(i.next(t))}catch(t){o(t)}}function a(t){try{c(i.throw(t))}catch(t){o(t)}}function c(t){var n;t.done?s(t.value):(n=t.value,n instanceof e?n:new e((function(t){t(n)}))).then(r,a)}c((i=i.apply(t,n||[])).next())}))};const l={host:"",port:80,path:"",unavailableTimeout:1e4,retryTimeout:10,useTLS:!1,retryStrategy:(h={retries:3,delay:1e3,multiplier:5},(t,n)=>c(void 0,void 0,void 0,(function*(){var t,e,i,s;const o=n;if(-1===h.retries)return Promise.resolve({retry:!0,state:void 0});if(0===h.retries)return Promise.resolve({retry:!1,state:void 0});const r=null!==(t=h.multiplier)&&void 0!==t?t:1,a=null!==(e=h.retries)&&void 0!==e?e:1,l=null!==(i=null==o?void 0:o.retry)&&void 0!==i?i:0,d=(null==o?void 0:o.delay)?o.delay*r:null!==(s=h.delay)&&void 0!==s?s:0;return l{setTimeout(t,u)})}))),Promise.resolve({retry:!0,state:{delay:d,retry:l+1}})):Promise.resolve({retry:!0,state:void 0});var u})))};var h;class d{constructor(){this.callbacks={}}get(t){return this.callbacks[t]||[]}add(t,n,e){this.callbacks[t]||(this.callbacks[t]=[]),this.callbacks[t].push({fn:n,channel:e||null})}remove(t,n,e){if(!t&&!n&&!e)return void(this.callbacks={});const i=t?[t]:Object.keys(this.callbacks);n||e?this.removeCallback(i,n,e):this.removeAllCallbacks(i)}removeCallback(t,n,e){t.forEach(t=>{const i=this.callbacks[t]||[];this.callbacks[t]=i.filter(t=>{const i=n&&n===t.fn,s=e&&e===t.channel;return!i&&!s})})}removeAllCallbacks(t){t.forEach(t=>{delete this.callbacks[t]})}}class u{constructor(){this.callbacks=new d}bind(t,n,e){return this.callbacks.add(t,n,e),this}unbind(t,n,e){return this.callbacks.remove(t,n,e),this}emit(t,n){const e=this.callbacks.get(t),i=(s=n)&&s.context&&"string"==typeof s.context.channel?n.context.channel:null;var s;return e&&e.length>0&&e.forEach(t=>{const e=t.channel===i;(!t.channel||!i||e)&&t.fn(n)}),this}}var E;!function(t){t.CHANNEL_JOINED="@join",t.CHANNEL_JOIN_FAILED="#join",t.CHANNEL_LEFT="@leave",t.CHANNEL_LEAVE_FAILED="#leave"}(E||(E={}));const N=new Set([E.CHANNEL_JOINED,E.CHANNEL_JOIN_FAILED,E.CHANNEL_LEFT,E.CHANNEL_LEAVE_FAILED]),b=t=>{const n={command:t.type,topics:t.payload};return JSON.stringify(n)};var C;!function(t){t.JOIN="join",t.LEAVE="leave"}(C||(C={}));const p=new Set([C.JOIN,C.LEAVE]);class f extends u{constructor(t,n){super(),this.id=t,this.transport=n,this.bindListeners()}send(t){return!!this.transport&&this.transport.send(t)}sendCommand(t,n){if(p.has(t))throw new Error(`${t} is a reserved command, cant be sent manually`);return this.send(b({type:t,payload:n}))}sendJoin(t){this.send(b({type:C.JOIN,payload:t}))}sendLeave(t){this.send(b({type:C.LEAVE,payload:t}))}close(){this.transport&&this.transport.close()}bindListeners(){const t=t=>{this.transport&&(this.transport.unbind(i.MESSAGE,t.message),this.transport.unbind(i.ERROR,t.error),this.transport.unbind(i.CLOSED,t.closed))},n={message:t=>{let n=null;try{n=(t=>{if(t){const n=JSON.parse(t);if(N.has(n.topic)&&!n.payload&&Array.isArray(n.payload))return{type:O.ERROR,error:"WS event system event payload is invalid. Should be a string array",data:"MessageParseError"};switch(n.topic){case E.CHANNEL_JOINED:return{type:O.CHANNEL_JOINED,error:null,data:n.payload};case E.CHANNEL_JOIN_FAILED:return{type:O.CHANNEL_JOIN_FAILED,error:null,data:n.payload};case E.CHANNEL_LEFT:return{type:O.CHANNEL_LEFT,error:null,data:n.payload};case E.CHANNEL_LEAVE_FAILED:return{type:O.CHANNEL_LEAVE_FAILED,error:null,data:n.payload};default:return{type:O.MESSAGE,error:null,data:n.payload||null,context:Object.assign({},n.topic?{channel:n.topic}:{})}}}return{type:O.ERROR,error:"MessageEvent has no data property",data:"MessageParseError"}})(t.data)}catch(n){this.emit(i.ERROR,{type:O.ERROR,error:n.toString(),data:JSON.stringify(t)})}if(n)switch(n.type){case O.ERROR:this.emit(i.ERROR,n);break;case O.CHANNEL_JOIN_FAILED:this.emit(i.CHANNEL_JOIN_FAILED,n.data);break;case O.CHANNEL_JOINED:this.emit(i.CHANNEL_JOINED,n.data);break;case O.CHANNEL_LEFT:this.emit(i.CHANNEL_LEFT,n.data);break;case O.CHANNEL_LEAVE_FAILED:this.emit(i.ERROR,n);break;default:this.emit(i.MESSAGE,n)}},error:t=>{this.emit(i.ERROR,Object.assign(Object.assign({},t),{type:O.ERROR,data:null}))},closed:e=>{var s;t(n),(s=e).context&&void 0!==s.context.code&&this.handleCloseEvent(e),this.transport=null,this.emit(i.CLOSED,e)}};this.transport&&(this.transport.bind(i.MESSAGE,n.message),this.transport.bind(i.ERROR,n.error),this.transport.bind(i.CLOSED,n.closed))}handleCloseEvent(t){const n=(t=>t.context&&t.context.code?t.context.code<4e3&&t.context.code>=1002&&t.context.code<=1004?Object.assign(Object.assign({},t),{error:"Socket is unavailable"}):t:(console.error("Socket event do not contain close code"),Object.assign(Object.assign({},t),{error:"Connection refused"})))(t);n.type===O.CLOSED?this.emit(i.CLOSED,n):this.emit(i.ERROR,n)}}class y extends u{constructor(t,n){super(),this.initialize=()=>{const t=this;t.hooks.isInitialized()?t.changeState(i.INITIALIZED):t.onClose()},this.hooks=t,this.name=n,this.state="new"}connect(){if(this.socket||"initialized"!==this.state)return!1;const{url:t}=this.hooks;try{this.socket=this.hooks.getSocket(t)}catch(t){return setTimeout(()=>{this.onError(t),this.onClosed({type:O.ERROR,data:t,error:t.toString(),context:{code:0}})}),!1}return this.bindListeners(),this.changeState(i.CONNECTING),!0}close(){return!!this.socket&&(this.socket.close(),!0)}send(t){return"open"===this.state&&(setTimeout(()=>{this.socket&&this.socket.send(t)}),!0)}unbindListeners(){this.socket&&(this.socket.onopen=null,this.socket.onerror=null,this.socket.onclose=null,this.socket.onmessage=null)}onOpen(){this.changeState(i.OPEN),this.socket&&(this.socket.onopen=null)}onError(t){var n;(n=t)&&"string"==typeof n.type&&void 0!==n.currentTarget?this.emit(i.ERROR,{type:O.ERROR,error:"Websocket connection error",data:null}):this.emit(i.ERROR,{type:O.ERROR,error:t?t.message:"Websocket connection error",data:t?t.name:null})}onClose(t){t?this.onClosed({type:t.wasClean?O.CLOSED:O.ERROR,data:t.wasClean?t.reason:null,error:t.wasClean?null:t.reason,context:{code:t.code}}):this.onClosed({type:O.ERROR,data:null,error:"Closed for unknown reason",context:{code:0}}),this.unbindListeners(),this.socket=void 0}onMessage(t){this.emit(i.MESSAGE,{type:O.MESSAGE,data:"string"==typeof t.data?t.data:JSON.stringify(t.data),error:null})}bindListeners(){this.socket&&(this.socket.onopen=()=>{this.onOpen()},this.socket.onerror=t=>{this.onError(t)},this.socket.onclose=t=>{this.onClose(t)},this.socket.onmessage=t=>{this.onMessage(t)})}changeState(t){this.state=t,this.emit(t,void 0)}onClosed(t){this.state=i.CLOSED,this.emit(i.CLOSED,t)}}class L{constructor(t,n){this.options=n||{};const e=`ws${n.useTLS?"s":""}`,i=`${n.host}:${n.port}`,s=n.queryParams?Object.entries(n.queryParams).map(([t,n])=>`${encodeURIComponent(t)}=${encodeURIComponent(n)}`).join("&"):null,o=`${e}://${i}/${n.path}${s?`?${s}`:""}`;this.hooks={url:o,isInitialized:()=>!!window.WebSocket,getSocket:t=>new WebSocket(t)},this.name=t}connect(t){let n=!1;const e=new y(this.hooks,this.name),s=()=>{e.unbind(i.INITIALIZED,s),e.connect()},o=()=>{e.unbind(i.INITIALIZED,s),e.unbind(i.OPEN,r),e.unbind(i.ERROR,a),e.unbind(i.CLOSED,c)},r=()=>{n=!0,o();const i=new f("",e);t(null,i)},a=()=>{},c=n=>{o(),t(n)};return e.bind(i.INITIALIZED,s),e.bind(i.OPEN,r),e.bind(i.ERROR,a),e.bind(i.CLOSED,c),e.initialize(),{abort:()=>{n||(o(),e.close())}}}}var O,m=function(t,n,e,i){return new(e||(e=Promise))((function(s,o){function r(t){try{c(i.next(t))}catch(t){o(t)}}function a(t){try{c(i.throw(t))}catch(t){o(t)}}function c(t){var n;t.done?s(t.value):(n=t.value,n instanceof e?n:new e((function(t){t(n)}))).then(r,a)}c((i=i.apply(t,n||[])).next())}))};class g extends u{constructor(t){super(),this.options=Object.assign(Object.assign({},l),t),this.state="initialized",this.connection=null,this.errorCallbacks=this.buildErrorCallbacks(),this.connectionCallbacks=this.buildConnectionCallbacks(this.errorCallbacks),this.transport=new L("ws",t),this.runner=null,this.unavailableTimer=null,this.retryTimer=null}connect(){this.connection||this.runner||(this.updateState(i.CONNECTING),this.startConnecting(),this.setUnavailableTimer())}send(t){return!!this.connection&&this.connection.send(t)}sendCommand(t,n){return!!this.connection&&this.connection.sendCommand(t,n)}sendJoin(t){return this.connection&&this.connection.sendJoin(t),!1}sendLeave(t){return this.connection&&this.connection.sendLeave(t),!1}disconnect(){this.disconnectInternally(),this.updateState(i.DISCONNECTED)}isConnected(){return this.state===i.CONNECTED}startConnecting(){this.runner=this.transport.connect((t,n)=>m(this,void 0,void 0,(function*(){t?(this.abandonConnection(),(yield this.shouldRetry(t))&&this.retryIn(this.options.retryTimeout||0),this.emit(i.CLOSED,t)):(this.abortConnecting(),this.clearUnavailableTimer(),this.setConnection(n),this.updateState(i.CONNECTED))})))}abortConnecting(){this.runner&&(this.runner.abort(),this.runner=null)}disconnectInternally(){if(this.abortConnecting(),this.clearRetryTimer(),this.clearUnavailableTimer(),this.connection){const t=this.abandonConnection();t&&t.close()}}retryIn(t){t>0&&this.emit(i.CONNECTING,{type:O.CONNECTING,data:String(Math.round(t/1e3)),error:null}),this.retryTimer=setTimeout(()=>{this.disconnectInternally(),this.connect()},t||0)}clearRetryTimer(){null!==this.retryTimer&&clearTimeout(this.retryTimer),this.retryTimer=null}setUnavailableTimer(){this.unavailableTimer=setTimeout(()=>{this.updateState(i.UNAVAILABLE)},this.options.unavailableTimeout)}clearUnavailableTimer(){null!==this.unavailableTimer&&clearTimeout(this.unavailableTimer),this.unavailableTimer=null}buildConnectionCallbacks(t){return Object.assign(Object.assign({},t),{message:t=>{this.emit(i.MESSAGE,t)},error:t=>{this.emit(i.ERROR,t)},closed:t=>m(this,void 0,void 0,(function*(){this.abandonConnection(),(yield this.shouldRetry(t))&&this.retryIn(this.options.retryTimeout||0),this.emit(i.CLOSED,t)})),channelJoined:t=>this.emit(i.CHANNEL_JOINED,t),channelJoinFailed:t=>this.emit(i.CHANNEL_JOIN_FAILED,t),channelLeft:t=>this.emit(i.CHANNEL_LEFT,t)})}buildErrorCallbacks(){const t=t=>n=>{n.error&&this.emit(i.ERROR,{type:O.ERROR,data:null,error:n.error}),t(n)};return{refused:t(()=>{this.disconnect()}),unavailable:t(()=>{this.retryIn(1e3)})}}setConnection(t){this.connection=t,this.connection&&(this.connection.bind(i.MESSAGE,this.connectionCallbacks.message),this.connection.bind(i.CHANNEL_LEFT,this.connectionCallbacks.channelLeft),this.connection.bind(i.CHANNEL_JOIN_FAILED,this.connectionCallbacks.channelJoinFailed),this.connection.bind(i.CHANNEL_JOINED,this.connectionCallbacks.channelJoined),this.connection.bind(i.ERROR,this.connectionCallbacks.error),this.connection.bind(i.CLOSED,this.connectionCallbacks.closed),this.retryState=void 0)}abandonConnection(){if(!this.connection)return null;this.connection.unbind(i.MESSAGE,this.connectionCallbacks.message),this.connection.unbind(i.CHANNEL_LEFT,this.connectionCallbacks.channelLeft),this.connection.unbind(i.CHANNEL_JOIN_FAILED,this.connectionCallbacks.channelJoinFailed),this.connection.unbind(i.CHANNEL_JOINED,this.connectionCallbacks.channelJoined),this.connection.unbind(i.ERROR,this.connectionCallbacks.error),this.connection.unbind(i.CLOSED,this.connectionCallbacks.closed);const{connection:t}=this;return this.connection=null,t}updateState(t){const n=this.state;this.state=t,n!==t&&this.emit(t,void 0)}shouldRetry(t){return m(this,void 0,void 0,(function*(){if(this.options.retryStrategy){const{retry:n,state:e}=yield this.options.retryStrategy(t,this.retryState);return this.retryState=n?e:void 0,n}return!1}))}}!function(t){t.CONNECTING="sfSocket:connecting",t.MESSAGE="sfSocket:message",t.CHANNEL_JOINED="channel_joined",t.CHANNEL_JOIN_FAILED="channel_join_failed",t.CHANNEL_LEFT="channel_left",t.CHANNEL_LEAVE_FAILED="channel_leave_failed",t.ERROR="sfSocket:error",t.CLOSED="sfSocket:closed"}(O||(O={}));class S{constructor(t){if(this.channels={},!t||"object"!=typeof t)throw new Error("sfSocket options should be an object");const n=t||{};this.config=Object.assign(Object.assign({},l),n),this.config.port=n.useTLS?443:80,n.port&&(this.config.port=n.port),this.cMgr=new g(this.config),this.cMgr.bind(i.CONNECTED,()=>{Object.values(this.channels).forEach(t=>{t.join()})}),this.cMgr.bind(i.ERROR,t=>{console.error(t)}),S.instances.push(this),S.isReady&&this.connect()}static ready(){S.isReady=!0,S.instances.forEach(t=>{t.connect()})}connect(){this.cMgr.connect()}disconnect(){this.cMgr.disconnect()}sendCommand(t,n){return this.cMgr.sendCommand(t,n)}joinChannelList(t){t.forEach(t=>{this.joinChannel(t)})}leaveChannelList(t){t.forEach(t=>{this.leaveChannel(t)})}subscribe(t,n,e){return this.cMgr.bind(t,n,e)}unsubscribe(t,n,e){return this.cMgr.unbind(t,n,e)}joinChannel(t,n=!1){if(this.channels[t])throw new Error(`Channel ${t} already exists`);return this.channels[t]=new a(t,this),n||this.channels[t].join(),this.channels[t]}leaveChannel(t){if(!this.channels[t])throw new Error(`Channel ${t} does not exist`);const n=this.channels[t];return n.leave(),delete this.channels[t],n}getChannel(t){return this.channels[t]}}S.instances=[],S.isReady=!1;const v=t=>{const n=new URL(t),e=n.protocol?n.protocol.replace(":",""):null;return n.hostname&&n.port&&e?{host:n.hostname,port:n.port,path:e}:null}}])})); //# sourceMappingURL=socket.js.map \ No newline at end of file diff --git a/build/socket.js.map b/build/socket.js.map index dee0a37..eacb6cb 100644 --- a/build/socket.js.map +++ b/build/socket.js.map @@ -1 +1 @@ -{"version":3,"sources":["webpack://SFSocket/webpack/universalModuleDefinition","webpack://SFSocket/webpack/bootstrap","webpack://SFSocket/./lib/eventdispatcher/events.ts","webpack://SFSocket/./lib/autobind.ts","webpack://SFSocket/./lib/Channel.ts","webpack://SFSocket/./lib/constants.ts","webpack://SFSocket/./lib/eventdispatcher/CallbackRegistry.ts","webpack://SFSocket/./lib/eventdispatcher/EventsDispatcher.ts","webpack://SFSocket/./lib/messageCodingUtils.ts","webpack://SFSocket/./lib/connection/Connection.ts","webpack://SFSocket/./lib/transport/TransportConnection.ts","webpack://SFSocket/./lib/transport/Transport.ts","webpack://SFSocket/./lib/connection/ConnectionManager.ts","webpack://SFSocket/./lib/SFSocket.ts","webpack://SFSocket/./lib/index.ts"],"names":["root","factory","exports","module","define","amd","window","installedModules","__webpack_require__","moduleId","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","NamesDict","autobind","target","descriptor","fn","TypeError","definingProperty","configurable","this","boundFn","ChannelStatus","socket","channelStatus","CLOSED","selfName","cMgr","enabled","isConnected","JOINED","startListening","sendJoinCommand","eventName","callback","unbind","LEAVING","sendLeave","stopListening","channels","indexOf","ERROR","JOINING","sendJoin","DISCONNECTED","onDisconnect","CHANNEL_JOIN_FAILED","onJoinFailed","CHANNEL_JOINED","onJoin","CHANNEL_LEFT","onLeft","CONNECTED","onConnect","defaultConfig","host","port","path","unavailableTimeout","retryTimeout","useTLS","CallbackRegistry","callbacks","channel","push","names","keys","removeCallback","removeAllCallbacks","forEach","filter","existedCallback","isEqualCallback","isEqualChannel","add","remove","event","channelEvent","variableToCheck","context","length","isChannelCallback","SystemEvents","SystemTopics","Set","CHANNEL_LEAVE_FAILED","encodeMessage","sfEvent","command","type","topics","payload","JSON","stringify","ConnectionCommands","SystemCommands","JOIN","LEAVE","id","transport","super","bindListeners","data","send","commandName","has","Error","close","unbindListeners","listeners","MESSAGE","message","error","closed","messageEvent","sfSocketEvent","messageData","parse","topic","Array","isArray","SFSocketEventType","decodeMessage","e","emit","toString","closeEvent","code","handleCloseEvent","action","console","prepareCloseAction","hooks","initialize","self","isInitialized","changeState","INITIALIZED","onClose","state","url","getSocket","setTimeout","onError","onClosed","CONNECTING","onopen","onerror","onclose","onmessage","OPEN","currentTarget","wasClean","reason","undefined","onOpen","socketError","onMessage","params","options","scheme","paramStr","queryParams","entries","map","encodeURIComponent","join","WebSocket","socketUrl","connected","onInitialized","connect","result","abort","connection","errorCallbacks","buildErrorCallbacks","connectionCallbacks","buildConnectionCallbacks","runner","unavailableTimer","retryTimer","updateState","startConnecting","setUnavailableTimer","sendCommand","disconnectInternally","abandonConnection","shouldRetry","retryIn","abortConnecting","clearUnavailableTimer","setConnection","clearRetryTimer","delay","String","Math","round","clearTimeout","UNAVAILABLE","socketEvent","errorEvent","channelJoined","channelJoinFailed","channelLeft","withErrorEmitted","refused","disconnect","unavailable","newState","previousState","constructorOptions","config","values","err","instances","isReady","instance","cmdName","channelsNames","channelsName","joinChannel","channelNames","channelName","leaveChannel","chanelName","dontJoin","leave","makeSocketOptions","wsUrl","URL","urlProtocol","protocol","replace","hostname"],"mappings":"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","file":"socket.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"SFSocket\"] = factory();\n\telse\n\t\troot[\"SFSocket\"] = factory();\n})(window, function() {\nreturn "," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","/**\n * Dictionary of event names used across everywhere\n */\nexport enum NamesDict {\n CONNECTED= 'connected',\n MESSAGE='message',\n CONNECTING='connecting',\n DISCONNECTED='disconnected',\n ERROR='error',\n OPEN='open',\n INITIALIZED='initialized',\n CLOSED='closed',\n UNAVAILABLE='unavailable',\n CHANNEL_JOINED='channel_joined',\n CHANNEL_JOIN_FAILED='channel_join_failed',\n CHANNEL_LEFT='channel_left',\n}\n","/**\n * Return a descriptor removing the value and returning a getter\n * The getter will return a .bind version of the function\n * and memoize the result against a symbol on the instance\n */\nexport function autobind(target: any, key: string | symbol, descriptor: PropertyDescriptor) {\n let fn = descriptor.value;\n\n if (typeof fn !== 'function') {\n throw new TypeError(`@autobind decorator can only be applied to methods not: ${typeof fn}`);\n }\n\n // In IE11 calling Object.defineProperty has a side-effect of evaluating the\n // getter for the property which is being replaced. This causes infinite\n // recursion and an \"Out of stack space\" error.\n let definingProperty = false;\n\n return {\n configurable: true,\n get() {\n // eslint-disable-next-line no-prototype-builtins\n if (definingProperty || this === target.prototype || this.hasOwnProperty(key)\n || typeof fn !== 'function') {\n return fn;\n }\n\n const boundFn = fn.bind(this);\n definingProperty = true;\n Object.defineProperty(this, key, {\n configurable: true,\n get() {\n return boundFn;\n },\n set(value) {\n fn = value;\n delete this[key];\n },\n });\n definingProperty = false;\n return boundFn;\n },\n set(value: any) {\n fn = value;\n },\n };\n}\n","import { UEventCallback } from './types';\nimport { SFSocket } from './SFSocket';\nimport ConnectionManager, { ConnectionManagerEventMap } from './connection/ConnectionManager';\nimport { NamesDict } from './eventdispatcher/events';\nimport { autobind } from './autobind';\n\nexport enum ChannelStatus {\n CLOSED = 'closed', // Connection command not yet sent or connection was interrupted or server closed channel\n JOINING = 'joining', // Socket sent join command, server has not responded with OK\n JOINED = 'joined', // Server returned OK for join command\n LEAVING = 'leaving', // Socket sent leave command\n ERROR = 'error', // Join command produced an error\n}\n\nexport default class Channel {\n private readonly selfName: string;\n\n private channelStatus: ChannelStatus = ChannelStatus.CLOSED;\n\n private socket: SFSocket;\n\n private cMgr: ConnectionManager;\n\n /**\n * Flag to indicate if channel should be joined\n */\n private enabled: boolean;\n\n constructor(name: string, socket: SFSocket) {\n this.selfName = name;\n this.socket = socket;\n this.cMgr = socket.cMgr;\n this.enabled = false;\n }\n\n get status() {\n return this.channelStatus;\n }\n\n get name() {\n return this.selfName;\n }\n\n /**\n * Channel is active and working rn\n */\n get isActive() {\n return this.cMgr.isConnected() && this.channelStatus === ChannelStatus.JOINED;\n }\n\n join() {\n if (this.enabled) return;\n this.enabled = true;\n this.startListening();\n if (this.cMgr.isConnected()) {\n this.sendJoinCommand();\n }\n }\n\n subscribe(eventName: K, callback: UEventCallback) {\n this.cMgr.bind(eventName, callback, this.name);\n }\n\n unsubscribe(eventName: K, callback: UEventCallback) {\n this.cMgr.unbind(eventName, callback, this.name);\n }\n\n leave() {\n if (this.enabled) {\n this.enabled = false;\n this.channelStatus = ChannelStatus.LEAVING;\n if (this.cMgr.isConnected()) {\n this.cMgr.sendLeave([this.name]);\n }\n this.stopListening();\n }\n }\n\n @autobind\n private onConnect() {\n if (this.enabled) { // if should join/re-join\n this.sendJoinCommand();\n }\n }\n\n @autobind\n private onDisconnect() {\n this.channelStatus = ChannelStatus.CLOSED; // Channel has been closed by external event\n }\n\n @autobind\n private onJoin(channels: string[]) {\n if (channels.indexOf(this.name) >= 0) {\n this.channelStatus = ChannelStatus.JOINED; // Channel has failed to join\n }\n }\n\n @autobind\n private onLeft(channels: string[]) {\n if (channels.indexOf(this.name) >= 0) {\n this.channelStatus = ChannelStatus.CLOSED; // Channel was left\n }\n }\n\n @autobind\n private onJoinFailed(channels: string[]) {\n if (channels.indexOf(this.name) >= 0) {\n this.channelStatus = ChannelStatus.ERROR; // Failed to join\n }\n }\n\n private sendJoinCommand() {\n if (this.channelStatus !== ChannelStatus.JOINING) {\n this.channelStatus = ChannelStatus.JOINING;\n this.cMgr.sendJoin([this.name]);\n }\n }\n\n private startListening() {\n this.cMgr.bind(NamesDict.DISCONNECTED, this.onDisconnect);\n this.cMgr.bind(NamesDict.CHANNEL_JOIN_FAILED, this.onJoinFailed);\n this.cMgr.bind(NamesDict.CHANNEL_JOINED, this.onJoin);\n this.cMgr.bind(NamesDict.CHANNEL_LEFT, this.onLeft);\n this.cMgr.bind(NamesDict.CONNECTED, this.onConnect);\n }\n\n private stopListening() {\n this.cMgr.unbind(NamesDict.DISCONNECTED, this.onDisconnect);\n this.cMgr.unbind(NamesDict.CHANNEL_JOIN_FAILED, this.onJoinFailed);\n this.cMgr.unbind(NamesDict.CHANNEL_JOINED, this.onJoin);\n this.cMgr.unbind(NamesDict.CHANNEL_LEFT, this.onLeft);\n this.cMgr.unbind(NamesDict.CONNECTED, this.onConnect);\n }\n}\n","import { ISFSocketConfig } from './SFSocket';\n\nexport const defaultConfig: ISFSocketConfig = {\n host: '',\n port: 80,\n path: '',\n unavailableTimeout: 10000,\n retryTimeout: 1000,\n useTLS: false,\n};\n","import {\n ICallback, ICallbackTable, UEventCallback,\n} from '../types';\n\nexport default class CallbackRegistry {\n callbacks: ICallbackTable = {};\n\n get(name: K): ICallback[] {\n const results = this.callbacks[name];\n return (results || [])!; // TODO: Why TS wants '!' here?\n }\n\n add(name: K, callback: UEventCallback, channel?: string) {\n if (!this.callbacks[name]) {\n this.callbacks[name] = [];\n }\n this.callbacks[name]!.push({\n fn: callback,\n channel: channel || null,\n });\n }\n\n remove(name: K, callback?: UEventCallback, channel?: string) {\n if (!name && !callback && !channel) {\n this.callbacks = {};\n return;\n }\n\n const names: K[] = name ? [name] : Object.keys(this.callbacks) as K[];\n\n if (callback || channel) {\n this.removeCallback(names, callback, channel);\n } else {\n this.removeAllCallbacks(names);\n }\n }\n\n private removeCallback(names: K[], callback?: UEventCallback, channel?: string) {\n names.forEach((name) => {\n const callbacks: Array>> = (this.callbacks[name] || [])!; // TODO: Why TS wants '!' here?\n this.callbacks[name] = callbacks.filter(\n (existedCallback: ICallback) => {\n const isEqualCallback = callback && callback === existedCallback.fn;\n const isEqualChannel = channel && channel === existedCallback.channel;\n return !isEqualCallback && !isEqualChannel;\n },\n );\n });\n }\n\n private removeAllCallbacks(names: K[]) {\n names.forEach((name) => {\n delete this.callbacks[name];\n });\n }\n}\n","import { UEventCallback, ICallback } from '../types';\nimport CallbackRegistry from './CallbackRegistry';\n\nexport interface EventWithChannel {\n context: { channel: string }\n}\n\nconst isEventWithChannel = (variableToCheck: any): variableToCheck is EventWithChannel => (\n variableToCheck && variableToCheck.context\n && typeof variableToCheck.context.channel === 'string'\n);\n\nexport default class EventsDispatcher {\n callbacks: CallbackRegistry = new CallbackRegistry();\n\n bind(eventName: K, callback: UEventCallback, channel?: string) {\n this.callbacks.add(eventName, callback, channel);\n return this;\n }\n\n unbind(eventName: K, callback: UEventCallback, channel?: string) {\n this.callbacks.remove(eventName, callback, channel);\n return this;\n }\n\n emit(eventName: K, event: EventMap[K]) : EventsDispatcher {\n const callbacks = this.callbacks.get(eventName);\n\n const channelEvent: string | null = isEventWithChannel(event) ? event.context.channel\n : null;\n\n const hasCallbacks = callbacks && callbacks.length > 0;\n\n if (hasCallbacks) {\n callbacks.forEach((callback: ICallback) => {\n const isChannelCallback = callback.channel === channelEvent;\n const isGlobalCallback = !callback.channel || !channelEvent;\n\n if (isGlobalCallback || isChannelCallback) {\n callback.fn(event);\n }\n });\n }\n\n return this;\n }\n}\n","import { ISFSocketEvent, SFSocketEventType } from './SFSocket';\n\nexport enum SystemEvents {\n CHANNEL_JOINED = '@join', // server payload is string[] = channel list that were successfully joined\n CHANNEL_JOIN_FAILED = '#join', // server payload is string[] = channel list that failed to join\n CHANNEL_LEFT = '@leave', // server payload is string[] = channel list that were successfully left\n CHANNEL_LEAVE_FAILED = '#leave', // server payload is string[] = channel list that failed to leave\n}\n\nexport const SystemTopics = new Set([\n SystemEvents.CHANNEL_JOINED,\n SystemEvents.CHANNEL_JOIN_FAILED,\n SystemEvents.CHANNEL_LEFT,\n SystemEvents.CHANNEL_LEAVE_FAILED,\n]);\n\nexport const decodeMessage = (messageEvent: string | null): ISFSocketEvent => {\n if (messageEvent) {\n const messageData = JSON.parse(messageEvent);\n if (SystemTopics.has(messageData.topic)) {\n if (!messageData.payload && Array.isArray(messageData.payload)) {\n return {\n type: SFSocketEventType.ERROR,\n error: 'WS event system event payload is invalid. Should be a string array',\n data: 'MessageParseError',\n };\n }\n }\n switch (messageData.topic) {\n case SystemEvents.CHANNEL_JOINED:\n return {\n type: SFSocketEventType.CHANNEL_JOINED,\n error: null,\n data: messageData.payload,\n };\n case SystemEvents.CHANNEL_JOIN_FAILED:\n return {\n type: SFSocketEventType.CHANNEL_JOIN_FAILED,\n error: null,\n data: messageData.payload,\n };\n case SystemEvents.CHANNEL_LEFT:\n return {\n type: SFSocketEventType.CHANNEL_LEFT,\n error: null,\n data: messageData.payload,\n };\n case SystemEvents.CHANNEL_LEAVE_FAILED:\n return {\n type: SFSocketEventType.CHANNEL_LEAVE_FAILED,\n error: null,\n data: messageData.payload,\n };\n default:\n return {\n type: SFSocketEventType.MESSAGE,\n error: null,\n data: messageData.payload || null,\n context: {\n ...(messageData.topic ? { channel: messageData.topic } : {}),\n },\n };\n }\n }\n\n return {\n type: SFSocketEventType.ERROR,\n error: 'MessageEvent has no data property',\n data: 'MessageParseError',\n };\n};\n\nexport const encodeMessage = (event: { type: string, payload: any }): string => {\n const sfEvent = {\n command: event.type,\n topics: event.payload,\n };\n\n return JSON.stringify(sfEvent);\n};\n\n/**\n * See:\n * 1. https://developer.mozilla.org/en-US/docs/WebSockets/WebSockets_reference/CloseEvent\n */\nexport const prepareCloseAction = (closeEvent: ISFSocketEvent): ISFSocketEvent => {\n if (!closeEvent.context || !closeEvent.context.code) {\n console.error('Socket event do not contain close code'); // eslint-disable-line no-console\n\n return {\n ...closeEvent,\n error: 'Connection refused',\n };\n }\n\n if (closeEvent.context.code < 4000) {\n // ignore 1000 CLOSE_NORMAL, 1001 CLOSE_GOING_AWAY,\n // 1005 CLOSE_NO_STATUS, 1006 CLOSE_ABNORMAL\n // ignore 1007...3999\n // handle 1002 CLOSE_PROTOCOL_ERROR, 1003 CLOSE_UNSUPPORTED,\n // 1004 CLOSE_TOO_LARGE\n if (closeEvent.context.code >= 1002 && closeEvent.context.code <= 1004) {\n return {\n ...closeEvent,\n error: 'Socket is unavailable',\n };\n }\n }\n\n return closeEvent;\n};\n","import EventsDispatcher from '../eventdispatcher/EventsDispatcher';\nimport { ISFSocketEvent, SFSocketEventType } from '../SFSocket';\nimport TransportConnection from '../transport/TransportConnection';\nimport { decodeMessage, encodeMessage, prepareCloseAction } from '../messageCodingUtils';\nimport { NamesDict } from '../eventdispatcher/events';\n\n/**\n * Lists events that can be emitted by `Connection` class\n */\nexport interface ConnectionEventMap {\n [NamesDict.CLOSED]: ISFSocketEvent,\n [NamesDict.ERROR]: ISFSocketEvent,\n [NamesDict.MESSAGE]: ISFSocketEvent,\n [NamesDict.CHANNEL_JOIN_FAILED]: string[],\n [NamesDict.CHANNEL_JOINED]: string[],\n [NamesDict.CHANNEL_LEFT]: string[],\n}\n\nexport enum ConnectionCommands {\n JOIN = 'join',\n LEAVE = 'leave',\n}\n\nexport const SystemCommands = new Set([ConnectionCommands.JOIN, ConnectionCommands.LEAVE]);\n\nexport interface EventWithCode {\n context: { code: string }\n}\n\nconst isEventWithCode = (variableToCheck: any): variableToCheck is EventWithCode => (\n variableToCheck.context\n && typeof variableToCheck.context.code !== 'undefined'\n);\n\nexport default class Connection extends EventsDispatcher {\n id: string;\n\n transport: TransportConnection | null;\n\n constructor(id : string, transport : TransportConnection) {\n super();\n this.id = id;\n this.transport = transport;\n this.bindListeners();\n }\n\n send(data : string) : boolean {\n if (!this.transport) return false;\n return this.transport.send(data);\n }\n\n /**\n * Sends custom command to ws connection\n * @param commandName - command that has custom handler on server\n * @param payload - any serializable data\n */\n sendCommand(commandName: string, payload: any) : boolean {\n if (SystemCommands.has(commandName)) {\n throw new Error(`${commandName} is a reserved command, cant be sent manually`);\n }\n\n return this.send(encodeMessage({ type: commandName, payload }));\n }\n\n /**\n * Sends command to join specific channels\n * @param channels\n */\n sendJoin(channels: string[]) {\n this.send(encodeMessage({\n type: ConnectionCommands.JOIN,\n payload: channels,\n }));\n }\n\n sendLeave(channels: string[]) {\n this.send(encodeMessage({\n type: ConnectionCommands.LEAVE,\n payload: channels,\n }));\n }\n\n close() {\n if (this.transport) {\n this.transport.close();\n }\n }\n\n private bindListeners() {\n const unbindListeners = (listeners: any) => { // TODO\n if (!this.transport) return;\n this.transport.unbind(NamesDict.MESSAGE, listeners.message);\n this.transport.unbind(NamesDict.ERROR, listeners.error);\n this.transport.unbind(NamesDict.CLOSED, listeners.closed);\n };\n\n const listeners = {\n message: (messageEvent: ISFSocketEvent) => {\n let sfSocketEvent = null;\n try {\n sfSocketEvent = decodeMessage(messageEvent.data);\n } catch (e: any) {\n this.emit(NamesDict.ERROR, {\n type: SFSocketEventType.ERROR,\n error: e.toString(),\n data: JSON.stringify(messageEvent),\n });\n }\n\n if (sfSocketEvent) {\n switch (sfSocketEvent.type) {\n case SFSocketEventType.ERROR:\n this.emit(NamesDict.ERROR, sfSocketEvent);\n break;\n case SFSocketEventType.CHANNEL_JOIN_FAILED:\n this.emit(NamesDict.CHANNEL_JOIN_FAILED, sfSocketEvent.data as any); // TODO:\n break;\n case SFSocketEventType.CHANNEL_JOINED:\n this.emit(NamesDict.CHANNEL_JOINED, sfSocketEvent.data as any); // TODO:\n break;\n case SFSocketEventType.CHANNEL_LEFT:\n this.emit(NamesDict.CHANNEL_LEFT, sfSocketEvent.data as any); // TODO:\n break;\n case SFSocketEventType.CHANNEL_LEAVE_FAILED:\n this.emit(NamesDict.ERROR, sfSocketEvent);\n break;\n default:\n this.emit(NamesDict.MESSAGE, sfSocketEvent);\n }\n }\n },\n error: (error: ISFSocketEvent) => {\n this.emit(NamesDict.ERROR, {\n ...error,\n type: SFSocketEventType.ERROR,\n data: null, // TODO: Are these overrides needed? Check what's being sent here\n });\n },\n closed: (closeEvent: ISFSocketEvent) => { // TODO\n unbindListeners(listeners);\n\n if (isEventWithCode(closeEvent)) {\n this.handleCloseEvent(closeEvent);\n }\n\n this.transport = null;\n this.emit(NamesDict.CLOSED, closeEvent);\n },\n };\n\n if (!this.transport) return;\n this.transport.bind(NamesDict.MESSAGE, listeners.message);\n this.transport.bind(NamesDict.ERROR, listeners.error);\n this.transport.bind(NamesDict.CLOSED, listeners.closed);\n }\n\n private handleCloseEvent(closeEvent : ISFSocketEvent) {\n const action = prepareCloseAction(closeEvent);\n\n if (action.type === SFSocketEventType.CLOSED) {\n this.emit(NamesDict.CLOSED, action);\n } else {\n this.emit(NamesDict.ERROR, action);\n }\n }\n}\n","import EventsDispatcher from '../eventdispatcher/EventsDispatcher';\nimport { ISFSocketConfig, ISFSocketEvent, SFSocketEventType } from '../SFSocket';\nimport { NamesDict } from '../eventdispatcher/events';\n\nexport interface ITransportHooks {\n url: string;\n\n isInitialized(): boolean;\n\n getSocket(url: string, options?: ISFSocketConfig): WebSocket;\n}\n\nconst isEvent = (variableToCheck: any): variableToCheck is Event => (\n variableToCheck && typeof variableToCheck.type === 'string' && typeof variableToCheck.currentTarget !== 'undefined'\n);\n\n/**\n * Lists events that can be emitted by `TransportConnection` class\n */\nexport interface TransportEventMap {\n [NamesDict.INITIALIZED]: undefined,\n [NamesDict.ERROR]: ISFSocketEvent,\n [NamesDict.MESSAGE]: ISFSocketEvent,\n [NamesDict.CLOSED]: ISFSocketEvent,\n [NamesDict.OPEN]: undefined,\n [NamesDict.CONNECTING]: undefined,\n}\n\nexport default class TransportConnection extends EventsDispatcher {\n hooks: ITransportHooks;\n\n name: string;\n\n state: string;\n\n socket?: WebSocket;\n\n initialize: Function;\n\n constructor(hooks: ITransportHooks, name: string) {\n super();\n this.initialize = () => {\n const self = this;\n\n if (self.hooks.isInitialized()) {\n self.changeState(NamesDict.INITIALIZED);\n } else {\n self.onClose();\n }\n };\n this.hooks = hooks;\n this.name = name;\n\n this.state = 'new';\n }\n\n connect(): boolean {\n if (this.socket || this.state !== 'initialized') {\n return false;\n }\n\n const { url } = this.hooks;\n try {\n this.socket = this.hooks.getSocket(url);\n } catch (e: any) {\n // Workaround for MobileSafari bug (see https://gist.github.com/2052006)\n setTimeout(() => {\n this.onError(e);\n this.onClosed({\n type: SFSocketEventType.ERROR,\n data: e,\n error: e.toString(),\n context: {\n code: 0,\n },\n });\n });\n return false;\n }\n\n this.bindListeners();\n this.changeState(NamesDict.CONNECTING);\n return true;\n }\n\n /** Closes the connection.\n *\n * @return {Boolean} true if there was a connection to close\n */\n close(): boolean {\n if (this.socket) {\n this.socket.close();\n return true;\n }\n return false;\n }\n\n send(data: any): boolean { // TODO\n if (this.state === 'open') {\n // Workaround for MobileSafari bug (see https://gist.github.com/2052006)\n setTimeout(() => {\n if (this.socket) {\n this.socket.send(data);\n }\n });\n return true;\n }\n return false;\n }\n\n private unbindListeners() {\n if (!this.socket) return;\n this.socket.onopen = null;\n this.socket.onerror = null;\n this.socket.onclose = null;\n this.socket.onmessage = null;\n }\n\n private onOpen() {\n this.changeState(NamesDict.OPEN);\n if (!this.socket) return;\n this.socket.onopen = null;\n }\n\n private onError(error?: Event | Error) {\n if (isEvent(error)) {\n this.emit(NamesDict.ERROR, {\n type: SFSocketEventType.ERROR,\n error: 'Websocket connection error',\n data: null,\n });\n } else {\n this.emit(NamesDict.ERROR, {\n type: SFSocketEventType.ERROR,\n error: error ? error.message : 'Websocket connection error',\n data: error ? error.name : null,\n });\n }\n }\n\n private onClose(closeEvent?: CloseEvent) {\n if (closeEvent) {\n this.onClosed({\n type: closeEvent.wasClean ? SFSocketEventType.CLOSED : SFSocketEventType.ERROR,\n data: closeEvent.wasClean ? closeEvent.reason : null,\n error: closeEvent.wasClean ? null : closeEvent.reason,\n context: {\n code: closeEvent.code,\n },\n });\n } else {\n this.onClosed({\n type: SFSocketEventType.ERROR,\n data: null,\n error: 'Closed for unknown reason',\n context: {\n code: 0,\n },\n });\n }\n this.unbindListeners();\n this.socket = undefined;\n }\n\n private onMessage(message: MessageEvent) {\n this.emit(NamesDict.MESSAGE, {\n type: SFSocketEventType.MESSAGE,\n data: typeof message.data === 'string' ? message.data : JSON.stringify(message.data),\n error: null,\n });\n }\n\n private bindListeners() {\n if (!this.socket) return;\n this.socket.onopen = () => {\n this.onOpen();\n };\n this.socket.onerror = (socketError: Event) => {\n this.onError(socketError);\n };\n this.socket.onclose = (closeEvent: CloseEvent) => {\n this.onClose(closeEvent);\n };\n this.socket.onmessage = (message: MessageEvent) => {\n this.onMessage(message);\n };\n }\n\n private changeState(state: NamesDict.OPEN | NamesDict.CONNECTING | NamesDict.INITIALIZED) {\n this.state = state;\n this.emit(state, undefined);\n }\n\n private onClosed(params: ISFSocketEvent) {\n this.state = NamesDict.CLOSED;\n this.emit(NamesDict.CLOSED, params);\n }\n}\n","import { UndescribedCallbackFunction } from '../types';\nimport Connection from '../connection/Connection';\nimport TransportConnection, { ITransportHooks } from './TransportConnection';\nimport { ISFSocketConfig, ISFSocketEvent } from '../SFSocket';\nimport { NamesDict } from '../eventdispatcher/events';\n\nexport interface IRunner {\n abort: () => void;\n}\n\nexport interface ITransport {\n connect(callback: UndescribedCallbackFunction): IRunner;\n}\n\nexport default class Transport implements ITransport {\n hooks: ITransportHooks;\n\n name: string;\n\n options: ISFSocketConfig;\n\n constructor(name: string, options: ISFSocketConfig) {\n this.options = options || {};\n\n const scheme = `ws${options.useTLS ? 's' : ''}`;\n const host = `${options.host}:${options.port}`;\n // eslint-disable-next-line max-len\n const paramStr = options.queryParams ? Object.entries(options.queryParams).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join('&') : null;\n\n const url = `${scheme}://${host}/${options.path}${paramStr ? (`?${paramStr}`) : ''}`;\n\n this.hooks = {\n url,\n isInitialized() {\n return !!window.WebSocket;\n },\n getSocket(socketUrl) {\n return new WebSocket(socketUrl);\n },\n };\n this.name = name;\n }\n\n connect(callback: UndescribedCallbackFunction) {\n let connected = false;\n\n const transport = new TransportConnection(\n this.hooks, this.name,\n );\n\n const onInitialized = () => {\n transport.unbind(NamesDict.INITIALIZED, onInitialized);\n transport.connect();\n };\n\n const unbindListeners = () => {\n transport.unbind(NamesDict.INITIALIZED, onInitialized);\n // eslint-disable-next-line no-use-before-define\n transport.unbind(NamesDict.OPEN, onOpen);\n // eslint-disable-next-line no-use-before-define\n transport.unbind(NamesDict.ERROR, onError);\n // eslint-disable-next-line no-use-before-define\n transport.unbind(NamesDict.CLOSED, onClosed);\n };\n\n const onOpen = () => {\n connected = true;\n unbindListeners();\n const result = new Connection('', transport);\n callback(null, result);\n };\n\n const onError = () => {\n // Note errors are bound and handled above\n };\n\n const onClosed = (closeEvent: ISFSocketEvent) => {\n unbindListeners();\n callback(closeEvent);\n };\n\n transport.bind(NamesDict.INITIALIZED, onInitialized);\n transport.bind(NamesDict.OPEN, onOpen);\n transport.bind(NamesDict.ERROR, onError);\n transport.bind(NamesDict.CLOSED, onClosed);\n\n transport.initialize();\n\n return {\n abort: () => {\n if (connected) {\n return;\n }\n unbindListeners();\n transport.close();\n },\n };\n }\n}\n","import { defaultConfig } from '../constants';\nimport { UndescribedCallbackFunction } from '../types';\nimport {\n IAction,\n IConnectionCallbacks,\n IErrorCallbacks,\n} from './types';\nimport EventsDispatcher from '../eventdispatcher/EventsDispatcher';\nimport Transport, { IRunner, ITransport } from '../transport/Transport';\nimport Connection from './Connection';\nimport { ISFSocketConfig, ISFSocketEvent, SFSocketEventType } from '../SFSocket';\n\nimport { NamesDict } from '../eventdispatcher/events';\n\nexport type ConnectionState = 'initialized'\n | NamesDict.UNAVAILABLE\n | NamesDict.CONNECTING\n | NamesDict.CONNECTED\n | NamesDict.DISCONNECTED;\n\nexport interface ConnectionManagerEventMap {\n [NamesDict.CONNECTING]: ISFSocketEvent,\n [NamesDict.DISCONNECTED]: undefined,\n [NamesDict.CONNECTED]: undefined,\n [NamesDict.CHANNEL_JOINED]: string[],\n [NamesDict.CHANNEL_JOIN_FAILED]: string[],\n [NamesDict.CHANNEL_LEFT]: string[],\n [NamesDict.ERROR]: ISFSocketEvent,\n [NamesDict.MESSAGE]: ISFSocketEvent,\n [NamesDict.CLOSED]: ISFSocketEvent,\n [NamesDict.UNAVAILABLE]: undefined,\n}\n\nexport default class ConnectionManager extends EventsDispatcher {\n private options: ISFSocketConfig;\n\n state: ConnectionState;\n\n private connection: Connection | null;\n\n private unavailableTimer: NodeJS.Timeout | null;\n\n private retryTimer: NodeJS.Timeout | null;\n\n private transport: ITransport;\n\n private runner: IRunner | null;\n\n private errorCallbacks: IErrorCallbacks;\n\n private connectionCallbacks: IConnectionCallbacks;\n\n constructor(options: ISFSocketConfig) {\n super();\n this.options = { ...defaultConfig, ...options };\n this.state = 'initialized';\n this.connection = null;\n\n this.errorCallbacks = this.buildErrorCallbacks();\n this.connectionCallbacks = this.buildConnectionCallbacks(this.errorCallbacks);\n\n this.transport = new Transport(\n 'ws',\n options,\n );\n this.runner = null;\n\n this.unavailableTimer = null;\n this.retryTimer = null;\n }\n\n connect() {\n if (this.connection || this.runner) {\n return;\n }\n this.updateState(NamesDict.CONNECTING);\n this.startConnecting();\n this.setUnavailableTimer();\n }\n\n send(data: string) {\n if (this.connection) {\n return this.connection.send(data);\n }\n return false;\n }\n\n sendCommand(name: string, data: any) {\n if (this.connection) {\n return this.connection.sendCommand(name, data);\n }\n return false;\n }\n\n sendJoin(channels: string[]) {\n if (this.connection) {\n this.connection.sendJoin(channels);\n }\n return false;\n }\n\n sendLeave(channels: string[]) {\n if (this.connection) {\n this.connection.sendLeave(channels);\n }\n return false;\n }\n\n disconnect() {\n this.disconnectInternally();\n this.updateState(NamesDict.DISCONNECTED);\n }\n\n public isConnected() {\n return this.state === NamesDict.CONNECTED;\n }\n\n private startConnecting() {\n const callback: UndescribedCallbackFunction = (closeEvent: ISFSocketEvent | undefined, connection: Connection) => { // TODO\n if (closeEvent) {\n this.abandonConnection();\n if (this.shouldRetry(closeEvent)) {\n this.retryIn(this.options.retryTimeout || 0);\n }\n this.emit(NamesDict.CLOSED, closeEvent);\n } else {\n this.abortConnecting();\n\n this.clearUnavailableTimer();\n this.setConnection(connection);\n this.updateState(NamesDict.CONNECTED);\n }\n };\n this.runner = this.transport.connect(callback);\n }\n\n private abortConnecting() {\n if (this.runner) {\n this.runner.abort();\n this.runner = null;\n }\n }\n\n private disconnectInternally() {\n this.abortConnecting();\n this.clearRetryTimer();\n this.clearUnavailableTimer();\n if (this.connection) {\n const connection = this.abandonConnection();\n if (connection) connection.close();\n }\n }\n\n private retryIn(delay: number) {\n if (delay > 0) {\n this.emit(NamesDict.CONNECTING, {\n type: SFSocketEventType.CONNECTING,\n data: String(Math.round(delay / 1000)),\n error: null,\n });\n }\n this.retryTimer = setTimeout(() => {\n this.disconnectInternally();\n this.connect();\n }, delay || 0);\n }\n\n private clearRetryTimer() {\n if (this.retryTimer !== null) {\n clearTimeout(this.retryTimer);\n }\n this.retryTimer = null;\n }\n\n private setUnavailableTimer() {\n this.unavailableTimer = setTimeout(\n () => {\n this.updateState(NamesDict.UNAVAILABLE);\n },\n this.options.unavailableTimeout,\n );\n }\n\n private clearUnavailableTimer() {\n if (this.unavailableTimer !== null) {\n clearTimeout(this.unavailableTimer);\n }\n this.unavailableTimer = null;\n }\n\n private buildConnectionCallbacks(errorCallbacks: IErrorCallbacks): IConnectionCallbacks {\n return {\n ...errorCallbacks,\n message: (socketEvent: ISFSocketEvent) => {\n // includes pong messages from server\n this.emit(NamesDict.MESSAGE, socketEvent);\n },\n error: (errorEvent: ISFSocketEvent) => {\n // just emit error to user - socket will already be closed by browser\n this.emit(NamesDict.ERROR, errorEvent);\n },\n closed: (closeEvent: ISFSocketEvent) => {\n this.abandonConnection();\n if (this.shouldRetry(closeEvent)) {\n this.retryIn(this.options.retryTimeout || 0);\n }\n this.emit(NamesDict.CLOSED, closeEvent);\n },\n channelJoined: ((channels) => this.emit(NamesDict.CHANNEL_JOINED, channels)),\n channelJoinFailed: ((channels) => this.emit(NamesDict.CHANNEL_JOIN_FAILED, channels)),\n channelLeft: ((channels) => this.emit(NamesDict.CHANNEL_LEFT, channels)),\n };\n }\n\n private buildErrorCallbacks(): IErrorCallbacks {\n const withErrorEmitted = (callback: UndescribedCallbackFunction) => (result: IAction) => {\n if (result.error) {\n this.emit(NamesDict.ERROR, {\n type: SFSocketEventType.ERROR,\n data: null,\n error: result.error,\n });\n }\n callback(result);\n };\n\n return {\n refused: withErrorEmitted(() => {\n this.disconnect();\n }),\n unavailable: withErrorEmitted(() => {\n this.retryIn(1000);\n }),\n };\n }\n\n private setConnection(connection: Connection | null) {\n this.connection = connection;\n if (!this.connection) {\n return;\n }\n this.connection.bind(NamesDict.MESSAGE, this.connectionCallbacks.message);\n this.connection.bind(NamesDict.CHANNEL_LEFT, this.connectionCallbacks.channelLeft);\n this.connection.bind(NamesDict.CHANNEL_JOIN_FAILED, this.connectionCallbacks.channelJoinFailed);\n this.connection.bind(NamesDict.CHANNEL_JOINED, this.connectionCallbacks.channelJoined);\n this.connection.bind(NamesDict.ERROR, this.connectionCallbacks.error);\n this.connection.bind(NamesDict.CLOSED, this.connectionCallbacks.closed);\n }\n\n private abandonConnection() {\n if (!this.connection) {\n return null;\n }\n this.connection.unbind(NamesDict.MESSAGE, this.connectionCallbacks.message);\n this.connection.unbind(NamesDict.CHANNEL_LEFT, this.connectionCallbacks.channelLeft);\n this.connection.unbind(NamesDict.CHANNEL_JOIN_FAILED, this.connectionCallbacks.channelJoinFailed);\n this.connection.unbind(NamesDict.CHANNEL_JOINED, this.connectionCallbacks.channelJoined);\n this.connection.unbind(NamesDict.ERROR, this.connectionCallbacks.error);\n this.connection.unbind(NamesDict.CLOSED, this.connectionCallbacks.closed);\n\n const { connection } = this;\n this.connection = null;\n\n return connection;\n }\n\n private updateState(newState: NamesDict.UNAVAILABLE\n | NamesDict.CONNECTING\n | NamesDict.CONNECTED\n | NamesDict.DISCONNECTED) {\n const previousState = this.state;\n this.state = newState;\n if (previousState !== newState) {\n this.emit(newState, undefined);\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n private shouldRetry(closeEvent: ISFSocketEvent): boolean {\n return this.state === NamesDict.CONNECTING || this.state === NamesDict.CONNECTED;\n }\n}\n","import { UEventCallback } from './types';\nimport Channel from './Channel';\nimport ConnectionManager, { ConnectionManagerEventMap } from './connection/ConnectionManager';\nimport { defaultConfig } from './constants';\nimport { NamesDict } from './eventdispatcher/events';\n\nexport interface IChannels {\n [name: string]: Channel;\n}\n\n// TODO: why do we even need 'sfSocket' prefix and not just reuse type\nexport enum SFSocketEventType {\n CONNECTING = 'sfSocket:connecting',\n MESSAGE = 'sfSocket:message',\n CHANNEL_JOINED = 'channel_joined',\n CHANNEL_JOIN_FAILED = 'channel_join_failed',\n CHANNEL_LEFT = 'channel_left',\n CHANNEL_LEAVE_FAILED = 'channel_leave_failed',\n ERROR = 'sfSocket:error',\n CLOSED = 'sfSocket:closed',\n}\n\nexport interface ISFSocketConfig {\n host: string,\n port: string | number;\n path: string;\n queryParams?: { [key: string]: string };\n unavailableTimeout?: number;\n useTLS?: boolean;\n retryTimeout?: number;\n}\n\nexport interface ISFSocketEvent {\n type: SFSocketEventType,\n data: string | null,\n error: string | null,\n context?: {\n channel?: string,\n code?: string | number,\n } | null\n}\n\nexport class SFSocket {\n // eslint-disable-next-line no-use-before-define\n static instances: SFSocket[] = [];\n\n static isReady: boolean = false;\n\n static ready() {\n SFSocket.isReady = true;\n\n SFSocket.instances.forEach((instance) => {\n instance.connect();\n });\n }\n\n private config: ISFSocketConfig;\n\n private channels: IChannels = {};\n\n cMgr: ConnectionManager;\n\n constructor(options?: ISFSocketConfig) {\n if (!options || typeof options !== 'object') {\n throw new Error('sfSocket options should be an object');\n }\n\n const constructorOptions = options || {};\n\n this.config = {\n ...defaultConfig,\n ...constructorOptions,\n };\n\n this.config.port = constructorOptions.useTLS ? 443 : 80;\n if (constructorOptions.port) {\n this.config.port = constructorOptions.port;\n }\n\n this.cMgr = new ConnectionManager(this.config);\n\n this.cMgr.bind(NamesDict.CONNECTED, () => {\n Object.values(this.channels).forEach((channel) => {\n channel.join(); // Send join command again on connect\n });\n });\n\n this.cMgr.bind(NamesDict.ERROR, (err: ISFSocketEvent) => {\n console.error(err); // eslint-disable-line no-console\n });\n\n SFSocket.instances.push(this);\n\n if (SFSocket.isReady) {\n this.connect();\n }\n }\n\n connect() {\n this.cMgr.connect();\n }\n\n disconnect() {\n this.cMgr.disconnect();\n }\n\n /**\n * Send custom command to server\n * @param cmdName - string name of command\n * @param data - serializable payload for data\n */\n sendCommand(cmdName: string, data: any) {\n return this.cMgr.sendCommand(cmdName, data);\n }\n\n joinChannelList(channelsNames: string[]) {\n channelsNames.forEach((channelsName) => {\n this.joinChannel(channelsName);\n });\n }\n\n leaveChannelList(channelNames: string[]) {\n channelNames.forEach((channelName) => {\n this.leaveChannel(channelName);\n });\n }\n\n /**\n * Subscribe to event globally\n * @param eventName\n * @param callback\n * @param channel - optional channel to monitor. Note subscribing to channel here is not creating auto-rejoinable channel.\n * Join channel explicitly to make it auto-rejoinable\n */\n subscribe(eventName: K, callback: UEventCallback, channel?: string) {\n return this.cMgr.bind(eventName, callback, channel);\n }\n\n /**\n * Unsubscribe from event globally\n * @param eventName\n * @param callback\n * @param channel - optional channel to monitor. Note subscribing to channel here is not creating auto-rejoinable channel.\n * Join channel explicitly to make it auto-rejoinable\n */\n unsubscribe(eventName: K, callback: UEventCallback, channel?: string) {\n return this.cMgr.unbind(eventName, callback, channel);\n }\n\n joinChannel(chanelName: string, dontJoin: boolean = false) {\n if (this.channels[chanelName]) {\n throw new Error(`Channel ${chanelName} already exists`);\n }\n this.channels[chanelName] = new Channel(chanelName, this);\n if (!dontJoin) {\n this.channels[chanelName].join();\n }\n return this.channels[chanelName];\n }\n\n leaveChannel(chanelName: string) {\n if (!this.channels[chanelName]) {\n throw new Error(`Channel ${chanelName} does not exist`);\n }\n const channel = this.channels[chanelName];\n channel.leave();\n delete this.channels[chanelName];\n return channel;\n }\n\n getChannel(name: string): Channel {\n return this.channels[name];\n }\n}\n","import {\n SFSocket,\n ISFSocketEvent,\n ISFSocketConfig,\n SFSocketEventType,\n} from './SFSocket';\n\nimport { NamesDict, NamesDict as eventNames } from './eventdispatcher/events';\nimport Channel, { ChannelStatus } from './Channel';\nimport {\n ICallback, ICallbackTable, SFEventMap, UEventCallback, UndescribedCallbackFunction,\n} from './types';\n\nconst makeSocketOptions = (wsUrl: string) => {\n const url = new URL(wsUrl);\n const urlProtocol = url.protocol ? url.protocol.replace(':', '') : null;\n\n if (url.hostname && url.port && urlProtocol) {\n return {\n host: url.hostname, // host: 'localhost',\n port: url.port, // port: '8080',\n path: urlProtocol, // path: 'ws',\n };\n }\n\n return null;\n};\n\nexport {\n SFSocket,\n makeSocketOptions,\n eventNames,\n Channel,\n ChannelStatus,\n ICallback,\n ICallbackTable,\n ISFSocketEvent,\n NamesDict,\n ISFSocketConfig,\n SFSocketEventType,\n SFEventMap,\n UEventCallback,\n UndescribedCallbackFunction,\n};\n"],"sourceRoot":""} \ No newline at end of file +{"version":3,"sources":["webpack://SFSocket/webpack/universalModuleDefinition","webpack://SFSocket/webpack/bootstrap","webpack://SFSocket/./lib/eventdispatcher/events.ts","webpack://SFSocket/./lib/autobind.ts","webpack://SFSocket/./lib/Channel.ts","webpack://SFSocket/./lib/retrystrategy/index.ts","webpack://SFSocket/./lib/constants.ts","webpack://SFSocket/./lib/eventdispatcher/CallbackRegistry.ts","webpack://SFSocket/./lib/eventdispatcher/EventsDispatcher.ts","webpack://SFSocket/./lib/messageCodingUtils.ts","webpack://SFSocket/./lib/connection/Connection.ts","webpack://SFSocket/./lib/transport/TransportConnection.ts","webpack://SFSocket/./lib/transport/Transport.ts","webpack://SFSocket/./lib/SFSocket.ts","webpack://SFSocket/./lib/connection/ConnectionManager.ts","webpack://SFSocket/./lib/index.ts"],"names":["root","factory","exports","module","define","amd","window","installedModules","__webpack_require__","moduleId","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","NamesDict","autobind","target","descriptor","fn","TypeError","definingProperty","configurable","this","boundFn","ChannelStatus","socket","channelStatus","CLOSED","selfName","cMgr","enabled","isConnected","JOINED","startListening","sendJoinCommand","eventName","callback","unbind","LEAVING","sendLeave","stopListening","channels","indexOf","ERROR","JOINING","sendJoin","DISCONNECTED","onDisconnect","CHANNEL_JOIN_FAILED","onJoinFailed","CHANNEL_JOINED","onJoin","CHANNEL_LEFT","onLeft","CONNECTED","onConnect","defaultConfig","host","port","path","unavailableTimeout","retryTimeout","useTLS","retryStrategy","options","retries","delay","multiplier","event","prevState","state","Promise","resolve","retry","undefined","retriesAllowed","retriesDone","newDelay","setTimeout","CallbackRegistry","callbacks","channel","push","names","keys","removeCallback","removeAllCallbacks","forEach","filter","existedCallback","isEqualCallback","isEqualChannel","add","remove","channelEvent","variableToCheck","context","length","isChannelCallback","SystemEvents","SystemTopics","Set","CHANNEL_LEAVE_FAILED","encodeMessage","sfEvent","command","type","topics","payload","JSON","stringify","ConnectionCommands","SystemCommands","JOIN","LEAVE","id","transport","super","bindListeners","data","send","commandName","has","Error","close","unbindListeners","listeners","MESSAGE","message","error","closed","messageEvent","sfSocketEvent","messageData","parse","topic","Array","isArray","SFSocketEventType","decodeMessage","e","emit","toString","closeEvent","code","handleCloseEvent","action","console","prepareCloseAction","hooks","initialize","self","isInitialized","changeState","INITIALIZED","onClose","url","getSocket","onError","onClosed","CONNECTING","onopen","onerror","onclose","onmessage","OPEN","currentTarget","wasClean","reason","onOpen","socketError","onMessage","params","scheme","paramStr","queryParams","entries","map","encodeURIComponent","join","WebSocket","socketUrl","connected","onInitialized","connect","result","abort","connection","errorCallbacks","buildErrorCallbacks","connectionCallbacks","buildConnectionCallbacks","runner","unavailableTimer","retryTimer","updateState","startConnecting","setUnavailableTimer","sendCommand","disconnectInternally","abandonConnection","shouldRetry","retryIn","abortConnecting","clearUnavailableTimer","setConnection","clearRetryTimer","String","Math","round","clearTimeout","UNAVAILABLE","socketEvent","errorEvent","channelJoined","channelJoinFailed","channelLeft","withErrorEmitted","refused","disconnect","unavailable","retryState","newState","previousState","constructorOptions","config","values","err","instances","isReady","instance","cmdName","channelsNames","channelsName","joinChannel","channelNames","channelName","leaveChannel","chanelName","dontJoin","leave","makeSocketOptions","wsUrl","URL","urlProtocol","protocol","replace","hostname"],"mappings":"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","file":"socket.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"SFSocket\"] = factory();\n\telse\n\t\troot[\"SFSocket\"] = factory();\n})(window, function() {\nreturn "," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","/**\n * Dictionary of event names used across everywhere\n */\nexport enum NamesDict {\n CONNECTED= 'connected',\n MESSAGE='message',\n CONNECTING='connecting',\n DISCONNECTED='disconnected',\n ERROR='error',\n OPEN='open',\n INITIALIZED='initialized',\n CLOSED='closed',\n UNAVAILABLE='unavailable',\n CHANNEL_JOINED='channel_joined',\n CHANNEL_JOIN_FAILED='channel_join_failed',\n CHANNEL_LEFT='channel_left',\n}\n","/**\n * Return a descriptor removing the value and returning a getter\n * The getter will return a .bind version of the function\n * and memoize the result against a symbol on the instance\n */\nexport function autobind(target: any, key: string | symbol, descriptor: PropertyDescriptor) {\n let fn = descriptor.value;\n\n if (typeof fn !== 'function') {\n throw new TypeError(`@autobind decorator can only be applied to methods not: ${typeof fn}`);\n }\n\n // In IE11 calling Object.defineProperty has a side-effect of evaluating the\n // getter for the property which is being replaced. This causes infinite\n // recursion and an \"Out of stack space\" error.\n let definingProperty = false;\n\n return {\n configurable: true,\n get() {\n // eslint-disable-next-line no-prototype-builtins\n if (definingProperty || this === target.prototype || this.hasOwnProperty(key)\n || typeof fn !== 'function') {\n return fn;\n }\n\n const boundFn = fn.bind(this);\n definingProperty = true;\n Object.defineProperty(this, key, {\n configurable: true,\n get() {\n return boundFn;\n },\n set(value) {\n fn = value;\n delete this[key];\n },\n });\n definingProperty = false;\n return boundFn;\n },\n set(value: any) {\n fn = value;\n },\n };\n}\n","import { UEventCallback } from './types';\nimport { SFSocket } from './SFSocket';\nimport ConnectionManager, { ConnectionManagerEventMap } from './connection/ConnectionManager';\nimport { NamesDict } from './eventdispatcher/events';\nimport { autobind } from './autobind';\n\nexport enum ChannelStatus {\n CLOSED = 'closed', // Connection command not yet sent or connection was interrupted or server closed channel\n JOINING = 'joining', // Socket sent join command, server has not responded with OK\n JOINED = 'joined', // Server returned OK for join command\n LEAVING = 'leaving', // Socket sent leave command\n ERROR = 'error', // Join command produced an error\n}\n\nexport default class Channel {\n private readonly selfName: string;\n\n private channelStatus: ChannelStatus = ChannelStatus.CLOSED;\n\n private socket: SFSocket;\n\n private cMgr: ConnectionManager;\n\n /**\n * Flag to indicate if channel should be joined\n */\n private enabled: boolean;\n\n constructor(name: string, socket: SFSocket) {\n this.selfName = name;\n this.socket = socket;\n this.cMgr = socket.cMgr;\n this.enabled = false;\n }\n\n get status() {\n return this.channelStatus;\n }\n\n get name() {\n return this.selfName;\n }\n\n /**\n * Channel is active and working rn\n */\n get isActive() {\n return this.cMgr.isConnected() && this.channelStatus === ChannelStatus.JOINED;\n }\n\n join() {\n if (this.enabled) return;\n this.enabled = true;\n this.startListening();\n if (this.cMgr.isConnected()) {\n this.sendJoinCommand();\n }\n }\n\n subscribe(eventName: K, callback: UEventCallback) {\n this.cMgr.bind(eventName, callback, this.name);\n }\n\n unsubscribe(eventName: K, callback: UEventCallback) {\n this.cMgr.unbind(eventName, callback, this.name);\n }\n\n leave() {\n if (this.enabled) {\n this.enabled = false;\n this.channelStatus = ChannelStatus.LEAVING;\n if (this.cMgr.isConnected()) {\n this.cMgr.sendLeave([this.name]);\n }\n this.stopListening();\n }\n }\n\n @autobind\n private onConnect() {\n if (this.enabled) { // if should join/re-join\n this.sendJoinCommand();\n }\n }\n\n @autobind\n private onDisconnect() {\n this.channelStatus = ChannelStatus.CLOSED; // Channel has been closed by external event\n }\n\n @autobind\n private onJoin(channels: string[]) {\n if (channels.indexOf(this.name) >= 0) {\n this.channelStatus = ChannelStatus.JOINED; // Channel has failed to join\n }\n }\n\n @autobind\n private onLeft(channels: string[]) {\n if (channels.indexOf(this.name) >= 0) {\n this.channelStatus = ChannelStatus.CLOSED; // Channel was left\n }\n }\n\n @autobind\n private onJoinFailed(channels: string[]) {\n if (channels.indexOf(this.name) >= 0) {\n this.channelStatus = ChannelStatus.ERROR; // Failed to join\n }\n }\n\n private sendJoinCommand() {\n if (this.channelStatus !== ChannelStatus.JOINING) {\n this.channelStatus = ChannelStatus.JOINING;\n this.cMgr.sendJoin([this.name]);\n }\n }\n\n private startListening() {\n this.cMgr.bind(NamesDict.DISCONNECTED, this.onDisconnect);\n this.cMgr.bind(NamesDict.CHANNEL_JOIN_FAILED, this.onJoinFailed);\n this.cMgr.bind(NamesDict.CHANNEL_JOINED, this.onJoin);\n this.cMgr.bind(NamesDict.CHANNEL_LEFT, this.onLeft);\n this.cMgr.bind(NamesDict.CONNECTED, this.onConnect);\n }\n\n private stopListening() {\n this.cMgr.unbind(NamesDict.DISCONNECTED, this.onDisconnect);\n this.cMgr.unbind(NamesDict.CHANNEL_JOIN_FAILED, this.onJoinFailed);\n this.cMgr.unbind(NamesDict.CHANNEL_JOINED, this.onJoin);\n this.cMgr.unbind(NamesDict.CHANNEL_LEFT, this.onLeft);\n this.cMgr.unbind(NamesDict.CONNECTED, this.onConnect);\n }\n}\n","import { ISFSocketEvent } from '../SFSocket';\nimport { RetryStrategy } from '../connection/types';\n\nexport interface IRetryState {\n delay: number;\n retry: number;\n}\n\nexport const INFINITE_RETRIES = -1;\n\nexport const delay = async (d: number) => new Promise((resolve) => {\n setTimeout(resolve, d);\n});\n\nexport const retryStrategy = (\n options: { retries?: number, delay?: number, multiplier?: number },\n): RetryStrategy => async (event: ISFSocketEvent, prevState?: unknown) => {\n const state = prevState as (IRetryState | undefined);\n if (options.retries === INFINITE_RETRIES) {\n return Promise.resolve({ retry: true, state: undefined });\n }\n if (options.retries === 0) {\n return Promise.resolve({ retry: false, state: undefined });\n }\n const multiplier = options.multiplier ?? 1;\n const retriesAllowed = options.retries ?? 1;\n const retriesDone = state?.retry ?? 0;\n const newDelay = (state?.delay) ? (state.delay * multiplier) : (options.delay ?? 0);\n\n if (retriesDone < retriesAllowed) {\n await delay(newDelay);\n return Promise.resolve({ retry: true, state: { delay: newDelay, retry: retriesDone + 1 } });\n }\n return Promise.resolve({ retry: true, state: undefined });\n};\n","import { ISFSocketConfig } from './SFSocket';\nimport { retryStrategy } from './retrystrategy';\n\nexport const defaultConfig: ISFSocketConfig = {\n host: '',\n port: 80,\n path: '',\n unavailableTimeout: 10000,\n retryTimeout: 10,\n useTLS: false,\n retryStrategy: retryStrategy({ retries: 3, delay: 1000, multiplier: 5 }),\n};\n","import {\n ICallback, ICallbackTable, UEventCallback,\n} from '../types';\n\nexport default class CallbackRegistry {\n callbacks: ICallbackTable = {};\n\n get(name: K): ICallback[] {\n const results = this.callbacks[name];\n return (results || [])!; // TODO: Why TS wants '!' here?\n }\n\n add(name: K, callback: UEventCallback, channel?: string) {\n if (!this.callbacks[name]) {\n this.callbacks[name] = [];\n }\n this.callbacks[name]!.push({\n fn: callback,\n channel: channel || null,\n });\n }\n\n remove(name: K, callback?: UEventCallback, channel?: string) {\n if (!name && !callback && !channel) {\n this.callbacks = {};\n return;\n }\n\n const names: K[] = name ? [name] : Object.keys(this.callbacks) as K[];\n\n if (callback || channel) {\n this.removeCallback(names, callback, channel);\n } else {\n this.removeAllCallbacks(names);\n }\n }\n\n private removeCallback(names: K[], callback?: UEventCallback, channel?: string) {\n names.forEach((name) => {\n const callbacks: Array>> = (this.callbacks[name] || [])!; // TODO: Why TS wants '!' here?\n this.callbacks[name] = callbacks.filter(\n (existedCallback: ICallback) => {\n const isEqualCallback = callback && callback === existedCallback.fn;\n const isEqualChannel = channel && channel === existedCallback.channel;\n return !isEqualCallback && !isEqualChannel;\n },\n );\n });\n }\n\n private removeAllCallbacks(names: K[]) {\n names.forEach((name) => {\n delete this.callbacks[name];\n });\n }\n}\n","import { UEventCallback, ICallback } from '../types';\nimport CallbackRegistry from './CallbackRegistry';\n\nexport interface EventWithChannel {\n context: { channel: string }\n}\n\nconst isEventWithChannel = (variableToCheck: any): variableToCheck is EventWithChannel => (\n variableToCheck && variableToCheck.context\n && typeof variableToCheck.context.channel === 'string'\n);\n\nexport default class EventsDispatcher {\n callbacks: CallbackRegistry = new CallbackRegistry();\n\n bind(eventName: K, callback: UEventCallback, channel?: string) {\n this.callbacks.add(eventName, callback, channel);\n return this;\n }\n\n unbind(eventName: K, callback: UEventCallback, channel?: string) {\n this.callbacks.remove(eventName, callback, channel);\n return this;\n }\n\n emit(eventName: K, event: EventMap[K]) : EventsDispatcher {\n const callbacks = this.callbacks.get(eventName);\n\n const channelEvent: string | null = isEventWithChannel(event) ? event.context.channel\n : null;\n\n const hasCallbacks = callbacks && callbacks.length > 0;\n\n if (hasCallbacks) {\n callbacks.forEach((callback: ICallback) => {\n const isChannelCallback = callback.channel === channelEvent;\n const isGlobalCallback = !callback.channel || !channelEvent;\n\n if (isGlobalCallback || isChannelCallback) {\n callback.fn(event);\n }\n });\n }\n\n return this;\n }\n}\n","import { ISFSocketEvent, SFSocketEventType } from './SFSocket';\n\nexport enum SystemEvents {\n CHANNEL_JOINED = '@join', // server payload is string[] = channel list that were successfully joined\n CHANNEL_JOIN_FAILED = '#join', // server payload is string[] = channel list that failed to join\n CHANNEL_LEFT = '@leave', // server payload is string[] = channel list that were successfully left\n CHANNEL_LEAVE_FAILED = '#leave', // server payload is string[] = channel list that failed to leave\n}\n\nexport const SystemTopics = new Set([\n SystemEvents.CHANNEL_JOINED,\n SystemEvents.CHANNEL_JOIN_FAILED,\n SystemEvents.CHANNEL_LEFT,\n SystemEvents.CHANNEL_LEAVE_FAILED,\n]);\n\nexport const decodeMessage = (messageEvent: string | null): ISFSocketEvent => {\n if (messageEvent) {\n const messageData = JSON.parse(messageEvent);\n if (SystemTopics.has(messageData.topic)) {\n if (!messageData.payload && Array.isArray(messageData.payload)) {\n return {\n type: SFSocketEventType.ERROR,\n error: 'WS event system event payload is invalid. Should be a string array',\n data: 'MessageParseError',\n };\n }\n }\n switch (messageData.topic) {\n case SystemEvents.CHANNEL_JOINED:\n return {\n type: SFSocketEventType.CHANNEL_JOINED,\n error: null,\n data: messageData.payload,\n };\n case SystemEvents.CHANNEL_JOIN_FAILED:\n return {\n type: SFSocketEventType.CHANNEL_JOIN_FAILED,\n error: null,\n data: messageData.payload,\n };\n case SystemEvents.CHANNEL_LEFT:\n return {\n type: SFSocketEventType.CHANNEL_LEFT,\n error: null,\n data: messageData.payload,\n };\n case SystemEvents.CHANNEL_LEAVE_FAILED:\n return {\n type: SFSocketEventType.CHANNEL_LEAVE_FAILED,\n error: null,\n data: messageData.payload,\n };\n default:\n return {\n type: SFSocketEventType.MESSAGE,\n error: null,\n data: messageData.payload || null,\n context: {\n ...(messageData.topic ? { channel: messageData.topic } : {}),\n },\n };\n }\n }\n\n return {\n type: SFSocketEventType.ERROR,\n error: 'MessageEvent has no data property',\n data: 'MessageParseError',\n };\n};\n\nexport const encodeMessage = (event: { type: string, payload: any }): string => {\n const sfEvent = {\n command: event.type,\n topics: event.payload,\n };\n\n return JSON.stringify(sfEvent);\n};\n\n/**\n * See:\n * 1. https://developer.mozilla.org/en-US/docs/WebSockets/WebSockets_reference/CloseEvent\n */\nexport const prepareCloseAction = (closeEvent: ISFSocketEvent): ISFSocketEvent => {\n if (!closeEvent.context || !closeEvent.context.code) {\n console.error('Socket event do not contain close code'); // eslint-disable-line no-console\n\n return {\n ...closeEvent,\n error: 'Connection refused',\n };\n }\n\n if (closeEvent.context.code < 4000) {\n // ignore 1000 CLOSE_NORMAL, 1001 CLOSE_GOING_AWAY,\n // 1005 CLOSE_NO_STATUS, 1006 CLOSE_ABNORMAL\n // ignore 1007...3999\n // handle 1002 CLOSE_PROTOCOL_ERROR, 1003 CLOSE_UNSUPPORTED,\n // 1004 CLOSE_TOO_LARGE\n if (closeEvent.context.code >= 1002 && closeEvent.context.code <= 1004) {\n return {\n ...closeEvent,\n error: 'Socket is unavailable',\n };\n }\n }\n\n return closeEvent;\n};\n","import EventsDispatcher from '../eventdispatcher/EventsDispatcher';\nimport { ISFSocketEvent, SFSocketEventType } from '../SFSocket';\nimport TransportConnection from '../transport/TransportConnection';\nimport { decodeMessage, encodeMessage, prepareCloseAction } from '../messageCodingUtils';\nimport { NamesDict } from '../eventdispatcher/events';\n\n/**\n * Lists events that can be emitted by `Connection` class\n */\nexport interface ConnectionEventMap {\n [NamesDict.CLOSED]: ISFSocketEvent,\n [NamesDict.ERROR]: ISFSocketEvent,\n [NamesDict.MESSAGE]: ISFSocketEvent,\n [NamesDict.CHANNEL_JOIN_FAILED]: string[],\n [NamesDict.CHANNEL_JOINED]: string[],\n [NamesDict.CHANNEL_LEFT]: string[],\n}\n\nexport enum ConnectionCommands {\n JOIN = 'join',\n LEAVE = 'leave',\n}\n\nexport const SystemCommands = new Set([ConnectionCommands.JOIN, ConnectionCommands.LEAVE]);\n\nexport interface EventWithCode {\n context: { code: string }\n}\n\nconst isEventWithCode = (variableToCheck: any): variableToCheck is EventWithCode => (\n variableToCheck.context\n && typeof variableToCheck.context.code !== 'undefined'\n);\n\nexport default class Connection extends EventsDispatcher {\n id: string;\n\n transport: TransportConnection | null;\n\n constructor(id : string, transport : TransportConnection) {\n super();\n this.id = id;\n this.transport = transport;\n this.bindListeners();\n }\n\n send(data : string) : boolean {\n if (!this.transport) return false;\n return this.transport.send(data);\n }\n\n /**\n * Sends custom command to ws connection\n * @param commandName - command that has custom handler on server\n * @param payload - any serializable data\n */\n sendCommand(commandName: string, payload: any) : boolean {\n if (SystemCommands.has(commandName)) {\n throw new Error(`${commandName} is a reserved command, cant be sent manually`);\n }\n\n return this.send(encodeMessage({ type: commandName, payload }));\n }\n\n /**\n * Sends command to join specific channels\n * @param channels\n */\n sendJoin(channels: string[]) {\n this.send(encodeMessage({\n type: ConnectionCommands.JOIN,\n payload: channels,\n }));\n }\n\n sendLeave(channels: string[]) {\n this.send(encodeMessage({\n type: ConnectionCommands.LEAVE,\n payload: channels,\n }));\n }\n\n close() {\n if (this.transport) {\n this.transport.close();\n }\n }\n\n private bindListeners() {\n const unbindListeners = (listeners: any) => { // TODO\n if (!this.transport) return;\n this.transport.unbind(NamesDict.MESSAGE, listeners.message);\n this.transport.unbind(NamesDict.ERROR, listeners.error);\n this.transport.unbind(NamesDict.CLOSED, listeners.closed);\n };\n\n const listeners = {\n message: (messageEvent: ISFSocketEvent) => {\n let sfSocketEvent = null;\n try {\n sfSocketEvent = decodeMessage(messageEvent.data);\n } catch (e: any) {\n this.emit(NamesDict.ERROR, {\n type: SFSocketEventType.ERROR,\n error: e.toString(),\n data: JSON.stringify(messageEvent),\n });\n }\n\n if (sfSocketEvent) {\n switch (sfSocketEvent.type) {\n case SFSocketEventType.ERROR:\n this.emit(NamesDict.ERROR, sfSocketEvent);\n break;\n case SFSocketEventType.CHANNEL_JOIN_FAILED:\n this.emit(NamesDict.CHANNEL_JOIN_FAILED, sfSocketEvent.data as any); // TODO:\n break;\n case SFSocketEventType.CHANNEL_JOINED:\n this.emit(NamesDict.CHANNEL_JOINED, sfSocketEvent.data as any); // TODO:\n break;\n case SFSocketEventType.CHANNEL_LEFT:\n this.emit(NamesDict.CHANNEL_LEFT, sfSocketEvent.data as any); // TODO:\n break;\n case SFSocketEventType.CHANNEL_LEAVE_FAILED:\n this.emit(NamesDict.ERROR, sfSocketEvent);\n break;\n default:\n this.emit(NamesDict.MESSAGE, sfSocketEvent);\n }\n }\n },\n error: (error: ISFSocketEvent) => {\n this.emit(NamesDict.ERROR, {\n ...error,\n type: SFSocketEventType.ERROR,\n data: null, // TODO: Are these overrides needed? Check what's being sent here\n });\n },\n closed: (closeEvent: ISFSocketEvent) => { // TODO\n unbindListeners(listeners);\n\n if (isEventWithCode(closeEvent)) {\n this.handleCloseEvent(closeEvent);\n }\n\n this.transport = null;\n this.emit(NamesDict.CLOSED, closeEvent);\n },\n };\n\n if (!this.transport) return;\n this.transport.bind(NamesDict.MESSAGE, listeners.message);\n this.transport.bind(NamesDict.ERROR, listeners.error);\n this.transport.bind(NamesDict.CLOSED, listeners.closed);\n }\n\n private handleCloseEvent(closeEvent : ISFSocketEvent) {\n const action = prepareCloseAction(closeEvent);\n\n if (action.type === SFSocketEventType.CLOSED) {\n this.emit(NamesDict.CLOSED, action);\n } else {\n this.emit(NamesDict.ERROR, action);\n }\n }\n}\n","import EventsDispatcher from '../eventdispatcher/EventsDispatcher';\nimport { ISFSocketConfig, ISFSocketEvent, SFSocketEventType } from '../SFSocket';\nimport { NamesDict } from '../eventdispatcher/events';\n\nexport interface ITransportHooks {\n url: string;\n\n isInitialized(): boolean;\n\n getSocket(url: string, options?: ISFSocketConfig): WebSocket;\n}\n\nconst isEvent = (variableToCheck: any): variableToCheck is Event => (\n variableToCheck && typeof variableToCheck.type === 'string' && typeof variableToCheck.currentTarget !== 'undefined'\n);\n\n/**\n * Lists events that can be emitted by `TransportConnection` class\n */\nexport interface TransportEventMap {\n [NamesDict.INITIALIZED]: undefined,\n [NamesDict.ERROR]: ISFSocketEvent,\n [NamesDict.MESSAGE]: ISFSocketEvent,\n [NamesDict.CLOSED]: ISFSocketEvent,\n [NamesDict.OPEN]: undefined,\n [NamesDict.CONNECTING]: undefined,\n}\n\nexport default class TransportConnection extends EventsDispatcher {\n hooks: ITransportHooks;\n\n name: string;\n\n state: string;\n\n socket?: WebSocket;\n\n initialize: Function;\n\n constructor(hooks: ITransportHooks, name: string) {\n super();\n this.initialize = () => {\n const self = this;\n\n if (self.hooks.isInitialized()) {\n self.changeState(NamesDict.INITIALIZED);\n } else {\n self.onClose();\n }\n };\n this.hooks = hooks;\n this.name = name;\n\n this.state = 'new';\n }\n\n connect(): boolean {\n if (this.socket || this.state !== 'initialized') {\n return false;\n }\n\n const { url } = this.hooks;\n try {\n this.socket = this.hooks.getSocket(url);\n } catch (e: any) {\n // Workaround for MobileSafari bug (see https://gist.github.com/2052006)\n setTimeout(() => {\n this.onError(e);\n this.onClosed({\n type: SFSocketEventType.ERROR,\n data: e,\n error: e.toString(),\n context: {\n code: 0,\n },\n });\n });\n return false;\n }\n\n this.bindListeners();\n this.changeState(NamesDict.CONNECTING);\n return true;\n }\n\n /** Closes the connection.\n *\n * @return {Boolean} true if there was a connection to close\n */\n close(): boolean {\n if (this.socket) {\n this.socket.close();\n return true;\n }\n return false;\n }\n\n send(data: any): boolean { // TODO\n if (this.state === 'open') {\n // Workaround for MobileSafari bug (see https://gist.github.com/2052006)\n setTimeout(() => {\n if (this.socket) {\n this.socket.send(data);\n }\n });\n return true;\n }\n return false;\n }\n\n private unbindListeners() {\n if (!this.socket) return;\n this.socket.onopen = null;\n this.socket.onerror = null;\n this.socket.onclose = null;\n this.socket.onmessage = null;\n }\n\n private onOpen() {\n this.changeState(NamesDict.OPEN);\n if (!this.socket) return;\n this.socket.onopen = null;\n }\n\n private onError(error?: Event | Error) {\n if (isEvent(error)) {\n this.emit(NamesDict.ERROR, {\n type: SFSocketEventType.ERROR,\n error: 'Websocket connection error',\n data: null,\n });\n } else {\n this.emit(NamesDict.ERROR, {\n type: SFSocketEventType.ERROR,\n error: error ? error.message : 'Websocket connection error',\n data: error ? error.name : null,\n });\n }\n }\n\n private onClose(closeEvent?: CloseEvent) {\n if (closeEvent) {\n this.onClosed({\n type: closeEvent.wasClean ? SFSocketEventType.CLOSED : SFSocketEventType.ERROR,\n data: closeEvent.wasClean ? closeEvent.reason : null,\n error: closeEvent.wasClean ? null : closeEvent.reason,\n context: {\n code: closeEvent.code,\n },\n });\n } else {\n this.onClosed({\n type: SFSocketEventType.ERROR,\n data: null,\n error: 'Closed for unknown reason',\n context: {\n code: 0,\n },\n });\n }\n this.unbindListeners();\n this.socket = undefined;\n }\n\n private onMessage(message: MessageEvent) {\n this.emit(NamesDict.MESSAGE, {\n type: SFSocketEventType.MESSAGE,\n data: typeof message.data === 'string' ? message.data : JSON.stringify(message.data),\n error: null,\n });\n }\n\n private bindListeners() {\n if (!this.socket) return;\n this.socket.onopen = () => {\n this.onOpen();\n };\n this.socket.onerror = (socketError: Event) => {\n this.onError(socketError);\n };\n this.socket.onclose = (closeEvent: CloseEvent) => {\n this.onClose(closeEvent);\n };\n this.socket.onmessage = (message: MessageEvent) => {\n this.onMessage(message);\n };\n }\n\n private changeState(state: NamesDict.OPEN | NamesDict.CONNECTING | NamesDict.INITIALIZED) {\n this.state = state;\n this.emit(state, undefined);\n }\n\n private onClosed(params: ISFSocketEvent) {\n this.state = NamesDict.CLOSED;\n this.emit(NamesDict.CLOSED, params);\n }\n}\n","import { UndescribedCallbackFunction } from '../types';\nimport Connection from '../connection/Connection';\nimport TransportConnection, { ITransportHooks } from './TransportConnection';\nimport { ISFSocketConfig, ISFSocketEvent } from '../SFSocket';\nimport { NamesDict } from '../eventdispatcher/events';\n\nexport interface IRunner {\n abort: () => void;\n}\n\nexport interface ITransport {\n connect(callback: UndescribedCallbackFunction): IRunner;\n}\n\nexport default class Transport implements ITransport {\n hooks: ITransportHooks;\n\n name: string;\n\n options: ISFSocketConfig;\n\n constructor(name: string, options: ISFSocketConfig) {\n this.options = options || {};\n\n const scheme = `ws${options.useTLS ? 's' : ''}`;\n const host = `${options.host}:${options.port}`;\n // eslint-disable-next-line max-len\n const paramStr = options.queryParams ? Object.entries(options.queryParams).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join('&') : null;\n\n const url = `${scheme}://${host}/${options.path}${paramStr ? (`?${paramStr}`) : ''}`;\n\n this.hooks = {\n url,\n isInitialized() {\n return !!window.WebSocket;\n },\n getSocket(socketUrl) {\n return new WebSocket(socketUrl);\n },\n };\n this.name = name;\n }\n\n connect(callback: UndescribedCallbackFunction) {\n let connected = false;\n\n const transport = new TransportConnection(\n this.hooks, this.name,\n );\n\n const onInitialized = () => {\n transport.unbind(NamesDict.INITIALIZED, onInitialized);\n transport.connect();\n };\n\n const unbindListeners = () => {\n transport.unbind(NamesDict.INITIALIZED, onInitialized);\n // eslint-disable-next-line no-use-before-define\n transport.unbind(NamesDict.OPEN, onOpen);\n // eslint-disable-next-line no-use-before-define\n transport.unbind(NamesDict.ERROR, onError);\n // eslint-disable-next-line no-use-before-define\n transport.unbind(NamesDict.CLOSED, onClosed);\n };\n\n const onOpen = () => {\n connected = true;\n unbindListeners();\n const result = new Connection('', transport);\n callback(null, result);\n };\n\n const onError = () => {\n // Note errors are bound and handled above\n };\n\n const onClosed = (closeEvent: ISFSocketEvent) => {\n unbindListeners();\n callback(closeEvent);\n };\n\n transport.bind(NamesDict.INITIALIZED, onInitialized);\n transport.bind(NamesDict.OPEN, onOpen);\n transport.bind(NamesDict.ERROR, onError);\n transport.bind(NamesDict.CLOSED, onClosed);\n\n transport.initialize();\n\n return {\n abort: () => {\n if (connected) {\n return;\n }\n unbindListeners();\n transport.close();\n },\n };\n }\n}\n","import { UEventCallback } from './types';\nimport Channel from './Channel';\nimport ConnectionManager, { ConnectionManagerEventMap } from './connection/ConnectionManager';\nimport { defaultConfig } from './constants';\nimport { NamesDict } from './eventdispatcher/events';\nimport { RetryStrategy } from './connection/types';\n\nexport interface IChannels {\n [name: string]: Channel;\n}\n\n// TODO: why do we even need 'sfSocket' prefix and not just reuse type\nexport enum SFSocketEventType {\n CONNECTING = 'sfSocket:connecting',\n MESSAGE = 'sfSocket:message',\n CHANNEL_JOINED = 'channel_joined',\n CHANNEL_JOIN_FAILED = 'channel_join_failed',\n CHANNEL_LEFT = 'channel_left',\n CHANNEL_LEAVE_FAILED = 'channel_leave_failed',\n ERROR = 'sfSocket:error',\n CLOSED = 'sfSocket:closed',\n}\n\nexport interface ISFSocketConfig {\n host: string,\n port: string | number;\n path: string;\n queryParams?: { [key: string]: string };\n unavailableTimeout?: number;\n useTLS?: boolean;\n retryTimeout?: number;\n // eslint-disable-next-line no-use-before-define\n retryStrategy?: RetryStrategy;\n}\n\nexport interface ISFSocketEvent {\n type: SFSocketEventType,\n data: string | null,\n error: string | null,\n context?: {\n channel?: string,\n code?: string | number,\n } | null\n}\n\nexport class SFSocket {\n // eslint-disable-next-line no-use-before-define\n static instances: SFSocket[] = [];\n\n static isReady: boolean = false;\n\n static ready() {\n SFSocket.isReady = true;\n\n SFSocket.instances.forEach((instance) => {\n instance.connect();\n });\n }\n\n private config: ISFSocketConfig;\n\n private channels: IChannels = {};\n\n cMgr: ConnectionManager;\n\n constructor(options?: ISFSocketConfig) {\n if (!options || typeof options !== 'object') {\n throw new Error('sfSocket options should be an object');\n }\n\n const constructorOptions = options || {};\n\n this.config = {\n ...defaultConfig,\n ...constructorOptions,\n };\n\n this.config.port = constructorOptions.useTLS ? 443 : 80;\n if (constructorOptions.port) {\n this.config.port = constructorOptions.port;\n }\n\n this.cMgr = new ConnectionManager(this.config);\n\n this.cMgr.bind(NamesDict.CONNECTED, () => {\n Object.values(this.channels).forEach((channel) => {\n channel.join(); // Send join command again on connect\n });\n });\n\n this.cMgr.bind(NamesDict.ERROR, (err: ISFSocketEvent) => {\n console.error(err); // eslint-disable-line no-console\n });\n\n SFSocket.instances.push(this);\n\n if (SFSocket.isReady) {\n this.connect();\n }\n }\n\n connect() {\n this.cMgr.connect();\n }\n\n disconnect() {\n this.cMgr.disconnect();\n }\n\n /**\n * Send custom command to server\n * @param cmdName - string name of command\n * @param data - serializable payload for data\n */\n sendCommand(cmdName: string, data: any) {\n return this.cMgr.sendCommand(cmdName, data);\n }\n\n joinChannelList(channelsNames: string[]) {\n channelsNames.forEach((channelsName) => {\n this.joinChannel(channelsName);\n });\n }\n\n leaveChannelList(channelNames: string[]) {\n channelNames.forEach((channelName) => {\n this.leaveChannel(channelName);\n });\n }\n\n /**\n * Subscribe to event globally\n * @param eventName\n * @param callback\n * @param channel - optional channel to monitor. Note subscribing to channel here is not creating auto-rejoinable channel.\n * Join channel explicitly to make it auto-rejoinable\n */\n subscribe(eventName: K, callback: UEventCallback, channel?: string) {\n return this.cMgr.bind(eventName, callback, channel);\n }\n\n /**\n * Unsubscribe from event globally\n * @param eventName\n * @param callback\n * @param channel - optional channel to monitor. Note subscribing to channel here is not creating auto-rejoinable channel.\n * Join channel explicitly to make it auto-rejoinable\n */\n unsubscribe(eventName: K, callback: UEventCallback, channel?: string) {\n return this.cMgr.unbind(eventName, callback, channel);\n }\n\n joinChannel(chanelName: string, dontJoin: boolean = false) {\n if (this.channels[chanelName]) {\n throw new Error(`Channel ${chanelName} already exists`);\n }\n this.channels[chanelName] = new Channel(chanelName, this);\n if (!dontJoin) {\n this.channels[chanelName].join();\n }\n return this.channels[chanelName];\n }\n\n leaveChannel(chanelName: string) {\n if (!this.channels[chanelName]) {\n throw new Error(`Channel ${chanelName} does not exist`);\n }\n const channel = this.channels[chanelName];\n channel.leave();\n delete this.channels[chanelName];\n return channel;\n }\n\n getChannel(name: string): Channel {\n return this.channels[name];\n }\n}\n","import { defaultConfig } from '../constants';\nimport { UndescribedCallbackFunction } from '../types';\nimport {\n IAction,\n IConnectionCallbacks,\n IErrorCallbacks,\n} from './types';\nimport EventsDispatcher from '../eventdispatcher/EventsDispatcher';\nimport Transport, { IRunner, ITransport } from '../transport/Transport';\nimport Connection from './Connection';\nimport { ISFSocketConfig, ISFSocketEvent, SFSocketEventType } from '../SFSocket';\n\nimport { NamesDict } from '../eventdispatcher/events';\n\nexport type ConnectionState = 'initialized'\n | NamesDict.UNAVAILABLE\n | NamesDict.CONNECTING\n | NamesDict.CONNECTED\n | NamesDict.DISCONNECTED;\n\nexport interface ConnectionManagerEventMap {\n [NamesDict.CONNECTING]: ISFSocketEvent,\n [NamesDict.DISCONNECTED]: undefined,\n [NamesDict.CONNECTED]: undefined,\n [NamesDict.CHANNEL_JOINED]: string[],\n [NamesDict.CHANNEL_JOIN_FAILED]: string[],\n [NamesDict.CHANNEL_LEFT]: string[],\n [NamesDict.ERROR]: ISFSocketEvent,\n [NamesDict.MESSAGE]: ISFSocketEvent,\n [NamesDict.CLOSED]: ISFSocketEvent,\n [NamesDict.UNAVAILABLE]: undefined,\n}\n\nexport default class ConnectionManager extends EventsDispatcher {\n private options: ISFSocketConfig;\n\n state: ConnectionState;\n\n private connection: Connection | null;\n\n private unavailableTimer: NodeJS.Timeout | null;\n\n private retryTimer: NodeJS.Timeout | null;\n\n private retryState: unknown;\n\n private transport: ITransport;\n\n private runner: IRunner | null;\n\n private errorCallbacks: IErrorCallbacks;\n\n private connectionCallbacks: IConnectionCallbacks;\n\n constructor(options: ISFSocketConfig) {\n super();\n this.options = { ...defaultConfig, ...options };\n this.state = 'initialized';\n this.connection = null;\n\n this.errorCallbacks = this.buildErrorCallbacks();\n this.connectionCallbacks = this.buildConnectionCallbacks(this.errorCallbacks);\n\n this.transport = new Transport(\n 'ws',\n options,\n );\n this.runner = null;\n\n this.unavailableTimer = null;\n this.retryTimer = null;\n }\n\n connect() {\n if (this.connection || this.runner) {\n return;\n }\n this.updateState(NamesDict.CONNECTING);\n this.startConnecting();\n this.setUnavailableTimer();\n }\n\n send(data: string) {\n if (this.connection) {\n return this.connection.send(data);\n }\n return false;\n }\n\n sendCommand(name: string, data: any) {\n if (this.connection) {\n return this.connection.sendCommand(name, data);\n }\n return false;\n }\n\n sendJoin(channels: string[]) {\n if (this.connection) {\n this.connection.sendJoin(channels);\n }\n return false;\n }\n\n sendLeave(channels: string[]) {\n if (this.connection) {\n this.connection.sendLeave(channels);\n }\n return false;\n }\n\n disconnect() {\n this.disconnectInternally();\n this.updateState(NamesDict.DISCONNECTED);\n }\n\n public isConnected() {\n return this.state === NamesDict.CONNECTED;\n }\n\n private startConnecting() {\n const callback: UndescribedCallbackFunction = async (closeEvent: ISFSocketEvent | undefined, connection: Connection) => { // TODO\n if (closeEvent) {\n this.abandonConnection();\n if (await this.shouldRetry(closeEvent)) {\n this.retryIn(this.options.retryTimeout || 0);\n }\n this.emit(NamesDict.CLOSED, closeEvent);\n } else {\n this.abortConnecting();\n\n this.clearUnavailableTimer();\n this.setConnection(connection);\n this.updateState(NamesDict.CONNECTED);\n }\n };\n this.runner = this.transport.connect(callback);\n }\n\n private abortConnecting() {\n if (this.runner) {\n this.runner.abort();\n this.runner = null;\n }\n }\n\n private disconnectInternally() {\n this.abortConnecting();\n this.clearRetryTimer();\n this.clearUnavailableTimer();\n if (this.connection) {\n const connection = this.abandonConnection();\n if (connection) connection.close();\n }\n }\n\n private retryIn(delay: number) {\n if (delay > 0) {\n this.emit(NamesDict.CONNECTING, {\n type: SFSocketEventType.CONNECTING,\n data: String(Math.round(delay / 1000)),\n error: null,\n });\n }\n this.retryTimer = setTimeout(() => {\n this.disconnectInternally();\n this.connect();\n }, delay || 0);\n }\n\n private clearRetryTimer() {\n if (this.retryTimer !== null) {\n clearTimeout(this.retryTimer);\n }\n this.retryTimer = null;\n }\n\n private setUnavailableTimer() {\n this.unavailableTimer = setTimeout(\n () => {\n this.updateState(NamesDict.UNAVAILABLE);\n },\n this.options.unavailableTimeout,\n );\n }\n\n private clearUnavailableTimer() {\n if (this.unavailableTimer !== null) {\n clearTimeout(this.unavailableTimer);\n }\n this.unavailableTimer = null;\n }\n\n private buildConnectionCallbacks(errorCallbacks: IErrorCallbacks): IConnectionCallbacks {\n return {\n ...errorCallbacks,\n message: (socketEvent: ISFSocketEvent) => {\n // includes pong messages from server\n this.emit(NamesDict.MESSAGE, socketEvent);\n },\n error: (errorEvent: ISFSocketEvent) => {\n // just emit error to user - socket will already be closed by browser\n this.emit(NamesDict.ERROR, errorEvent);\n },\n closed: async (closeEvent: ISFSocketEvent) => {\n this.abandonConnection();\n if (await this.shouldRetry(closeEvent)) {\n this.retryIn(this.options.retryTimeout || 0);\n }\n this.emit(NamesDict.CLOSED, closeEvent);\n },\n channelJoined: ((channels) => this.emit(NamesDict.CHANNEL_JOINED, channels)),\n channelJoinFailed: ((channels) => this.emit(NamesDict.CHANNEL_JOIN_FAILED, channels)),\n channelLeft: ((channels) => this.emit(NamesDict.CHANNEL_LEFT, channels)),\n };\n }\n\n private buildErrorCallbacks(): IErrorCallbacks {\n const withErrorEmitted = (callback: UndescribedCallbackFunction) => (result: IAction) => {\n if (result.error) {\n this.emit(NamesDict.ERROR, {\n type: SFSocketEventType.ERROR,\n data: null,\n error: result.error,\n });\n }\n callback(result);\n };\n\n return {\n refused: withErrorEmitted(() => {\n this.disconnect();\n }),\n unavailable: withErrorEmitted(() => {\n this.retryIn(1000);\n }),\n };\n }\n\n private setConnection(connection: Connection | null) {\n this.connection = connection;\n if (!this.connection) {\n return;\n }\n this.connection.bind(NamesDict.MESSAGE, this.connectionCallbacks.message);\n this.connection.bind(NamesDict.CHANNEL_LEFT, this.connectionCallbacks.channelLeft);\n this.connection.bind(NamesDict.CHANNEL_JOIN_FAILED, this.connectionCallbacks.channelJoinFailed);\n this.connection.bind(NamesDict.CHANNEL_JOINED, this.connectionCallbacks.channelJoined);\n this.connection.bind(NamesDict.ERROR, this.connectionCallbacks.error);\n this.connection.bind(NamesDict.CLOSED, this.connectionCallbacks.closed);\n\n this.retryState = undefined;\n }\n\n private abandonConnection() {\n if (!this.connection) {\n return null;\n }\n this.connection.unbind(NamesDict.MESSAGE, this.connectionCallbacks.message);\n this.connection.unbind(NamesDict.CHANNEL_LEFT, this.connectionCallbacks.channelLeft);\n this.connection.unbind(NamesDict.CHANNEL_JOIN_FAILED, this.connectionCallbacks.channelJoinFailed);\n this.connection.unbind(NamesDict.CHANNEL_JOINED, this.connectionCallbacks.channelJoined);\n this.connection.unbind(NamesDict.ERROR, this.connectionCallbacks.error);\n this.connection.unbind(NamesDict.CLOSED, this.connectionCallbacks.closed);\n\n const { connection } = this;\n this.connection = null;\n\n return connection;\n }\n\n private updateState(newState: NamesDict.UNAVAILABLE\n | NamesDict.CONNECTING\n | NamesDict.CONNECTED\n | NamesDict.DISCONNECTED) {\n const previousState = this.state;\n this.state = newState;\n if (previousState !== newState) {\n this.emit(newState, undefined);\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n private async shouldRetry(closeEvent: ISFSocketEvent): Promise {\n if (this.options.retryStrategy) {\n const { retry, state } = await this.options.retryStrategy(closeEvent, this.retryState);\n if (retry) {\n this.retryState = state;\n } else {\n this.retryState = undefined;\n }\n return retry;\n }\n return false;\n }\n}\n","import {\n SFSocket,\n ISFSocketEvent,\n ISFSocketConfig,\n SFSocketEventType,\n} from './SFSocket';\n\nimport { NamesDict, NamesDict as eventNames } from './eventdispatcher/events';\nimport Channel, { ChannelStatus } from './Channel';\nimport {\n ICallback, ICallbackTable, SFEventMap, UEventCallback, UndescribedCallbackFunction,\n} from './types';\n\nconst makeSocketOptions = (wsUrl: string) => {\n const url = new URL(wsUrl);\n const urlProtocol = url.protocol ? url.protocol.replace(':', '') : null;\n\n if (url.hostname && url.port && urlProtocol) {\n return {\n host: url.hostname, // host: 'localhost',\n port: url.port, // port: '8080',\n path: urlProtocol, // path: 'ws',\n };\n }\n\n return null;\n};\n\nexport {\n SFSocket,\n makeSocketOptions,\n eventNames,\n Channel,\n ChannelStatus,\n ICallback,\n ICallbackTable,\n ISFSocketEvent,\n NamesDict,\n ISFSocketConfig,\n SFSocketEventType,\n SFEventMap,\n UEventCallback,\n UndescribedCallbackFunction,\n};\n"],"sourceRoot":""} \ No newline at end of file