@electrum-cash/web-socket 1.2.0 → 1.2.1-development.12943431539

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -51,10 +51,7 @@ var ElectrumWebSocket = class extends EventEmitter {
51
51
  * Connect to host:port using the specified transport
52
52
  */
53
53
  connect() {
54
- if (this.webSocket) {
55
- debug.warning("Ignoring connect() request due to connection already existing.");
56
- return;
57
- }
54
+ if (this.webSocket) throw /* @__PURE__ */ new Error("Cannot initiate a new socket connection when an existing connection exists");
58
55
  this.disconnectTimer = setTimeout(() => this.disconnectOnTimeout(), this.timeout);
59
56
  this.once("connected", this.clearDisconnectTimerOnTimeout);
60
57
  const connectionType = this.encrypted ? "an encrypted WebSocket" : "a WebSocket";
@@ -106,10 +103,6 @@ var ElectrumWebSocket = class extends EventEmitter {
106
103
  window.removeEventListener("offline", this.boundHandleConnectivityChange);
107
104
  }
108
105
  }
109
- if (!this.webSocket) {
110
- debug.warning("Ignoring disconnect() request due to connection already existing.");
111
- return;
112
- }
113
106
  this.clearDisconnectTimerOnTimeout();
114
107
  try {
115
108
  this.webSocket.removeEventListener("close", this.eventForwarders.disconnect);
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../source/constants.ts","../source/web.ts"],"sourcesContent":["// Export a default timeout value of 30 seconds.\nexport const defaultTimeout = 30 * 1000;\n","import { WebSocket } from '@monsterbitar/isomorphic-ws';\nimport { EventEmitter } from 'eventemitter3';\nimport debug from '@electrum-cash/debug-logs';\nimport { defaultTimeout } from './constants.ts';\n\nimport type { MessageEvent, ErrorEvent } from '@monsterbitar/isomorphic-ws';\nimport type { ElectrumSocket, ElectrumSocketEvents } from '@electrum-cash/network';\n\nexport type ElectrumWebSocketReconnectionOptions = {\n\t/** If true, will not handle visibility changes when run in a browser environment. */\n\tdisableBrowserVisibilityHandling: boolean,\n\n\t/** If true, will not handle connectivity changes when run in a browser environment. */\n\tdisableBrowserConnectivityHandling: boolean,\n};\n\n/**\n * Web Socket used when communicating with Electrum servers.\n */\nexport class ElectrumWebSocket extends EventEmitter<ElectrumSocketEvents> implements ElectrumSocket\n{\n\t// Declare an empty WebSocket.\n\tprivate webSocket: WebSocket;\n\n\t// Used to disconnect after some time if initial connection is too slow.\n\tprivate disconnectTimer?: number;\n\n\t// Initialize boolean that indicates whether the onConnect function has run (initialize to false).\n\tprivate onConnectHasRun = false;\n\n\t// Initialize event forwarding functions.\n\tprivate eventForwarders =\n\t{\n\t\tdisconnect: () => this.emit('disconnected'),\n\t\twsData: (event: MessageEvent) => this.emit('data', `${event.data}\\n`),\n\t\twsError: (event: ErrorEvent) => this.emit('error', new Error(event.error)),\n\t};\n\n\t// Store bound visibility change handler so we can properly remove it later.\n\t// NOTE: .bind() creates a new function each time, so we need to store the reference.\n\tprivate boundHandleVisibilityChange = this.handleVisibilityChange.bind(this);\n\n\tprivate boundHandleConnectivityChange = this.handleConnectivityChange.bind(this);\n\n\t/**\n\t * Creates a socket configured with connection information for a given Electrum server.\n\t *\n\t * @param host Fully qualified domain name or IP address of the host\n\t * @param port Network port for the host to connect to, defaults to the standard TLS port\n\t * @param encrypted If false, uses an unencrypted connection instead of the default on TLS\n\t * @param timeout If no connection is established after `timeout` ms, the connection is terminated\n\t * @param reconnectionOptions Options to disable the automatic reconnection behavior when browser visibility or connectivity changes\n\t */\n\tpublic constructor\n\t(\n\t\tpublic host: string,\n\t\tpublic port: number = 50004,\n\t\tpublic encrypted: boolean = true,\n\t\tpublic timeout: number = defaultTimeout,\n\t\tpublic reconnectionOptions: ElectrumWebSocketReconnectionOptions = {\n\t\t\tdisableBrowserVisibilityHandling: false,\n\t\t\tdisableBrowserConnectivityHandling: false,\n\t\t},\n\t)\n\t{\n\t\t// Initialize the event emitter.\n\t\tsuper();\n\t}\n\n\t/**\n\t * Returns a string for the host identifier for usage in debug messages.\n\t */\n\tget hostIdentifier(): string\n\t{\n\t\treturn `${this.host}:${this.port}`;\n\t}\n\n\t/**\n\t * Connect to host:port using the specified transport\n\t */\n\tconnect(): void\n\t{\n\t\t// Do nothing if we are already connected.\n\t\tif(this.webSocket)\n\t\t{\n\t\t\tdebug.warning('Ignoring connect() request due to connection already existing.');\n\n\t\t\treturn;\n\t\t}\n\n\t\t// Set a timer to force disconnect after `timeout` seconds\n\t\tthis.disconnectTimer = setTimeout(() => this.disconnectOnTimeout(), this.timeout) as unknown as number;\n\n\t\t// Remove the timer if a connection is successfully established\n\t\tthis.once('connected', this.clearDisconnectTimerOnTimeout);\n\n\t\t// Set a named connection type for logging purposes.\n\t\tconst connectionType = (this.encrypted ? 'an encrypted WebSocket' : 'a WebSocket');\n\n\t\t// Log that we are trying to establish a connection.\n\t\tdebug.network(`Initiating ${connectionType} connection to '${this.host}:${this.port}'.`);\n\n\t\tif(this.encrypted)\n\t\t{\n\t\t\t// Initialize this.webSocket (rejecting self-signed certificates).\n\t\t\t// We reject self-signed certificates to match functionality of browsers.\n\t\t\tthis.webSocket = new WebSocket(`wss://${this.host}:${this.port}`);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Initialize this.webSocket.\n\t\t\tthis.webSocket = new WebSocket(`ws://${this.host}:${this.port}`);\n\t\t}\n\n\t\t// Trigger successful connection events.\n\t\tthis.webSocket.addEventListener('open', this.onConnect.bind(this));\n\n\t\t// Forward the encountered errors.\n\t\tthis.webSocket.addEventListener('error', this.eventForwarders.wsError);\n\n\t\tconst { disableBrowserVisibilityHandling, disableBrowserConnectivityHandling } = this.reconnectionOptions;\n\n\t\t// Handle visibility changes when run in a browser environment.\n\t\t// Only add listener if the document is currently visible to avoid immediate disconnect loops.\n\t\tif(typeof document !== 'undefined' && !disableBrowserVisibilityHandling)\n\t\t{\n\t\t\t// Remove the listener if it exists so we don't create multiple listeners.\n\t\t\tdocument.removeEventListener('visibilitychange', this.boundHandleVisibilityChange);\n\t\t\t\n\t\t\t// Add the listener again.\n\t\t\tdocument.addEventListener('visibilitychange', this.boundHandleVisibilityChange);\n\t\t}\n\n\t\t// Handle connectivity changes when run in a browser environment.\n\t\tif(typeof window !== 'undefined' && !disableBrowserConnectivityHandling)\n\t\t{\n\t\t\t// Remove the listeners if it exists so we don't create multiple listeners.\n\t\t\twindow.removeEventListener('online', this.boundHandleConnectivityChange);\n\t\t\twindow.removeEventListener('offline', this.boundHandleConnectivityChange);\n\t\t\t\n\t\t\t// Add the listeners again.\n\t\t\twindow.addEventListener('online', this.boundHandleConnectivityChange);\n\t\t\twindow.addEventListener('offline', this.boundHandleConnectivityChange);\n\t\t}\n\t}\n\n\t/**\n\t * Sets up forwarding of events related to the connection.\n\t */\n\tprivate onConnect(): void\n\t{\n\t\t// If the onConnect function has already run, do not execute it again.\n\t\tif(this.onConnectHasRun) return;\n\n\t\t// Set a named connection type for logging purposes.\n\t\tconst connectionType = (this.encrypted ? 'an encrypted WebSocket' : 'a WebSocket');\n\n\t\t// Log that the connection has been established.\n\t\tdebug.network(`Established ${connectionType} connection with '${this.host}:${this.port}'.`);\n\n\t\t// Forward the socket events\n\t\tthis.webSocket.addEventListener('close', this.eventForwarders.disconnect);\n\t\tthis.webSocket.addEventListener('message', this.eventForwarders.wsData);\n\n\t\t// Indicate that the onConnect function has run.\n\t\tthis.onConnectHasRun = true;\n\n\t\t// Emit the connect event.\n\t\tthis.emit('connected');\n\t}\n\n\t/**\n\t * Clears the disconnect timer if it is still active.\n\t */\n\tprivate clearDisconnectTimerOnTimeout(): void\n\t{\n\t\t// Clear the retry timer if it is still active.\n\t\tif(this.disconnectTimer)\n\t\t{\n\t\t\tclearTimeout(this.disconnectTimer);\n\t\t}\n\t}\n\n\t/**\n\t * Forcibly terminate the connection.\n\t *\n\t * @throws {Error} if no connection was found\n\t */\n\tpublic disconnect(suspend: boolean = false): void\n\t{\n\t\t// Remove connectivity and visibility handling listeners if the connection should be fully removed, instead of suspended.\n\t\tif(!suspend)\n\t\t{\n\t\t\t// Remove the visibility change listener\n\t\t\tif(typeof document !== 'undefined')\n\t\t\t{\n\t\t\t\tdocument.removeEventListener('visibilitychange', this.boundHandleVisibilityChange);\n\t\t\t}\n\n\t\t\t// Remove the connectivity change listeners\n\t\t\tif(typeof window !== 'undefined')\n\t\t\t{\n\t\t\t\twindow.removeEventListener('online', this.boundHandleConnectivityChange);\n\t\t\t\twindow.removeEventListener('offline', this.boundHandleConnectivityChange);\n\t\t\t}\n\t\t}\n\n\t\t// Do nothing if we are already disconnected.\n\t\tif(!this.webSocket)\n\t\t{\n\t\t\tdebug.warning('Ignoring disconnect() request due to connection already existing.');\n\n\t\t\treturn;\n\t\t}\n\n\t\t// Clear the disconnect timer so that the socket does not try to disconnect again later.\n\t\tthis.clearDisconnectTimerOnTimeout();\n\n\t\ttry\n\t\t{\n\t\t\t// Remove all event forwarders.\n\t\t\tthis.webSocket.removeEventListener('close', this.eventForwarders.disconnect);\n\t\t\tthis.webSocket.removeEventListener('message', this.eventForwarders.wsData);\n\t\t\tthis.webSocket.removeEventListener('error', this.eventForwarders.wsError);\n\n\t\t\t// Add a one-time event listener for potential error events after closing the socket.\n\t\t\t// NOTE: This is needed since the close() call might not result in an error itself, but the\n\t\t\t// underlying network packets that are sent in order to do a graceful termination can fail.\n\t\t\tthis.webSocket.addEventListener('error', (_ignored) => {}, { once: true });\n\n\t\t\t// Gracefully terminate the connection\n\t\t\tthis.webSocket.close();\n\t\t}\n\t\tcatch (_ignored)\n\t\t{\n\t\t\t// close() will throw an error if the connection has not been established yet.\n\t\t\t// We ignore this error, since no similar error gets thrown in the TLS Socket.\n\t\t}\n\t\tfinally\n\t\t{\n\t\t\t// Remove the stored socket regardless of any thrown errors.\n\t\t\tthis.webSocket = undefined;\n\t\t}\n\n\t\tthis.onConnectHasRun = false;\n\n\t\tthis.emit('disconnected');\n\t}\n\n\t/**\n\t * Write data to the socket\n\t *\n\t * @param data Data to be written to the socket\n\t * @param callback Callback function to be called when the write has completed\n\t *\n\t * @throws {Error} if no connection was found\n\t * @returns true if the message was fully flushed to the socket, false if part of the message\n\t * is queued in the user memory\n\t */\n\tpublic write(data: Uint8Array | string, callback?: (err?: Error) => void): boolean\n\t{\n\t\t// Throw an error if no active connection is found\n\t\tif(!this.webSocket)\n\t\t{\n\t\t\tthrow(new Error('Cannot write to socket when there is no active connection'));\n\t\t}\n\n\t\t// Write data to the WebSocket\n\t\tthis.webSocket.send(data, callback);\n\n\t\t// WebSockets always fit everything in a single request, so we return true\n\t\treturn true;\n\t}\n\n\t/**\n\t * Force a disconnection if no connection is established after `timeout` milliseconds.\n\t */\n\tprivate disconnectOnTimeout(): void\n\t{\n\t\t// Remove the connect listener.\n\t\tthis.removeListener('connected', this.clearDisconnectTimerOnTimeout);\n\n\t\t// Emit an error event so that connect is rejected upstream.\n\t\tthis.emit('error', new Error(`Connection to '${this.host}:${this.port}' timed out after ${this.timeout} milliseconds`));\n\n\t\t// Forcibly disconnect to clean up the connection on timeout\n\t\tthis.disconnect();\n\t}\n\n\t/**\n\t * Handles visibility changes when run in a browser environment.\n\t */\n\tprivate handleVisibilityChange(): void\n\t{\n\t\t// If the document is hiding, we should disconnect the connection.\n\t\tif(document?.visibilityState === 'hidden')\n\t\t{\n\t\t\t// Suspend is used to indicate that we should not remove the handleVisibilityChange listener.\n\t\t\tconst suspend = true;\n\n\t\t\t// Disconnect the connection.\n\t\t\treturn this.disconnect(suspend);\n\t\t}\n\n\t\t// If the webSocket has already been connected, we don't need to connect again.\n\t\t// This could occur from a higher layer manually reconnecting\n\t\tif(this.webSocket)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t// Connect the connection.\n\t\treturn this.connect();\n\t}\n\n\t/**\n\t * Handles connectivity changes when run in a browser environment.\n\t */\n\tprivate handleConnectivityChange(): void\n\t{\n\t\t// If we are offline, we should disconnect the connection.\n\t\tif(window.navigator.onLine === false)\n\t\t{\n\t\t\t// Suspend is used to indicate that we should not remove the handleConnectivityChange listener.\n\t\t\tconst suspend = true;\n\n\t\t\t// disconnect the connection.\n\t\t\treturn this.disconnect(suspend);\n\t\t}\n\n\t\t// If the webSocket has already been connected, we don't need to connect again.\n\t\t// This could occur from a higher layer manually reconnecting\n\t\tif(this.webSocket)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t// Connect the connection.\n\t\treturn this.connect();\n\t}\n\n\t// Add magic glue that makes typedoc happy so that we can have the events listed on the class.\n\tpublic readonly connected: [];\n\tpublic readonly disconnected: [];\n\tpublic readonly data: [ string ];\n\tpublic readonly error: [ Error ];\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../source/constants.ts","../source/web.ts"],"sourcesContent":["// Export a default timeout value of 30 seconds.\nexport const defaultTimeout = 30 * 1000;\n","import { WebSocket } from '@monsterbitar/isomorphic-ws';\nimport { EventEmitter } from 'eventemitter3';\nimport debug from '@electrum-cash/debug-logs';\nimport { defaultTimeout } from './constants.ts';\n\nimport type { MessageEvent, ErrorEvent } from '@monsterbitar/isomorphic-ws';\nimport type { ElectrumSocket, ElectrumSocketEvents } from '@electrum-cash/network';\n\nexport type ElectrumWebSocketReconnectionOptions = {\n\t/** If true, will not handle visibility changes when run in a browser environment. */\n\tdisableBrowserVisibilityHandling: boolean,\n\n\t/** If true, will not handle connectivity changes when run in a browser environment. */\n\tdisableBrowserConnectivityHandling: boolean,\n};\n\n/**\n * Web Socket used when communicating with Electrum servers.\n */\nexport class ElectrumWebSocket extends EventEmitter<ElectrumSocketEvents> implements ElectrumSocket\n{\n\t// Declare an empty WebSocket.\n\tprivate webSocket: WebSocket;\n\n\t// Used to disconnect after some time if initial connection is too slow.\n\tprivate disconnectTimer?: number;\n\n\t// Initialize boolean that indicates whether the onConnect function has run (initialize to false).\n\tprivate onConnectHasRun = false;\n\n\t// Initialize event forwarding functions.\n\tprivate eventForwarders =\n\t{\n\t\tdisconnect: () => this.emit('disconnected'),\n\t\twsData: (event: MessageEvent) => this.emit('data', `${event.data}\\n`),\n\t\twsError: (event: ErrorEvent) => this.emit('error', new Error(event.error)),\n\t};\n\n\t// Store bound visibility change handler so we can properly remove it later.\n\t// NOTE: .bind() creates a new function each time, so we need to store the reference.\n\tprivate boundHandleVisibilityChange = this.handleVisibilityChange.bind(this);\n\n\tprivate boundHandleConnectivityChange = this.handleConnectivityChange.bind(this);\n\n\t/**\n\t * Creates a socket configured with connection information for a given Electrum server.\n\t *\n\t * @param host Fully qualified domain name or IP address of the host\n\t * @param port Network port for the host to connect to, defaults to the standard TLS port\n\t * @param encrypted If false, uses an unencrypted connection instead of the default on TLS\n\t * @param timeout If no connection is established after `timeout` ms, the connection is terminated\n\t * @param reconnectionOptions Options to disable the automatic reconnection behavior when browser visibility or connectivity changes\n\t */\n\tpublic constructor\n\t(\n\t\tpublic host: string,\n\t\tpublic port: number = 50004,\n\t\tpublic encrypted: boolean = true,\n\t\tpublic timeout: number = defaultTimeout,\n\t\tpublic reconnectionOptions: ElectrumWebSocketReconnectionOptions = {\n\t\t\tdisableBrowserVisibilityHandling: false,\n\t\t\tdisableBrowserConnectivityHandling: false,\n\t\t},\n\t)\n\t{\n\t\t// Initialize the event emitter.\n\t\tsuper();\n\t}\n\n\t/**\n\t * Returns a string for the host identifier for usage in debug messages.\n\t */\n\tget hostIdentifier(): string\n\t{\n\t\treturn `${this.host}:${this.port}`;\n\t}\n\n\t/**\n\t * Connect to host:port using the specified transport\n\t */\n\tconnect(): void\n\t{\n\t\t// Check that no existing socket exists before initiating a new connection.\n\t\tif(this.webSocket)\n\t\t{\n\t\t\tthrow(new Error('Cannot initiate a new socket connection when an existing connection exists'));\n\t\t}\n\n\t\t// Set a timer to force disconnect after `timeout` seconds\n\t\tthis.disconnectTimer = setTimeout(() => this.disconnectOnTimeout(), this.timeout) as unknown as number;\n\n\t\t// Remove the timer if a connection is successfully established\n\t\tthis.once('connected', this.clearDisconnectTimerOnTimeout);\n\n\t\t// Set a named connection type for logging purposes.\n\t\tconst connectionType = (this.encrypted ? 'an encrypted WebSocket' : 'a WebSocket');\n\n\t\t// Log that we are trying to establish a connection.\n\t\tdebug.network(`Initiating ${connectionType} connection to '${this.host}:${this.port}'.`);\n\n\t\tif(this.encrypted)\n\t\t{\n\t\t\t// Initialize this.webSocket (rejecting self-signed certificates).\n\t\t\t// We reject self-signed certificates to match functionality of browsers.\n\t\t\tthis.webSocket = new WebSocket(`wss://${this.host}:${this.port}`);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Initialize this.webSocket.\n\t\t\tthis.webSocket = new WebSocket(`ws://${this.host}:${this.port}`);\n\t\t}\n\n\t\t// Trigger successful connection events.\n\t\tthis.webSocket.addEventListener('open', this.onConnect.bind(this));\n\n\t\t// Forward the encountered errors.\n\t\tthis.webSocket.addEventListener('error', this.eventForwarders.wsError);\n\n\t\tconst { disableBrowserVisibilityHandling, disableBrowserConnectivityHandling } = this.reconnectionOptions;\n\n\t\t// Handle visibility changes when run in a browser environment.\n\t\t// Only add listener if the document is currently visible to avoid immediate disconnect loops.\n\t\tif(typeof document !== 'undefined' && !disableBrowserVisibilityHandling)\n\t\t{\n\t\t\t// Remove the listener if it exists so we don't create multiple listeners.\n\t\t\tdocument.removeEventListener('visibilitychange', this.boundHandleVisibilityChange);\n\t\t\t\n\t\t\t// Add the listener again.\n\t\t\tdocument.addEventListener('visibilitychange', this.boundHandleVisibilityChange);\n\t\t}\n\n\t\t// Handle connectivity changes when run in a browser environment.\n\t\tif(typeof window !== 'undefined' && !disableBrowserConnectivityHandling)\n\t\t{\n\t\t\t// Remove the listeners if it exists so we don't create multiple listeners.\n\t\t\twindow.removeEventListener('online', this.boundHandleConnectivityChange);\n\t\t\twindow.removeEventListener('offline', this.boundHandleConnectivityChange);\n\t\t\t\n\t\t\t// Add the listeners again.\n\t\t\twindow.addEventListener('online', this.boundHandleConnectivityChange);\n\t\t\twindow.addEventListener('offline', this.boundHandleConnectivityChange);\n\t\t}\n\t}\n\n\t/**\n\t * Sets up forwarding of events related to the connection.\n\t */\n\tprivate onConnect(): void\n\t{\n\t\t// If the onConnect function has already run, do not execute it again.\n\t\tif(this.onConnectHasRun) return;\n\n\t\t// Set a named connection type for logging purposes.\n\t\tconst connectionType = (this.encrypted ? 'an encrypted WebSocket' : 'a WebSocket');\n\n\t\t// Log that the connection has been established.\n\t\tdebug.network(`Established ${connectionType} connection with '${this.host}:${this.port}'.`);\n\n\t\t// Forward the socket events\n\t\tthis.webSocket.addEventListener('close', this.eventForwarders.disconnect);\n\t\tthis.webSocket.addEventListener('message', this.eventForwarders.wsData);\n\n\t\t// Indicate that the onConnect function has run.\n\t\tthis.onConnectHasRun = true;\n\n\t\t// Emit the connect event.\n\t\tthis.emit('connected');\n\t}\n\n\t/**\n\t * Clears the disconnect timer if it is still active.\n\t */\n\tprivate clearDisconnectTimerOnTimeout(): void\n\t{\n\t\t// Clear the retry timer if it is still active.\n\t\tif(this.disconnectTimer)\n\t\t{\n\t\t\tclearTimeout(this.disconnectTimer);\n\t\t}\n\t}\n\n\t/**\n\t * Forcibly terminate the connection.\n\t *\n\t * @throws {Error} if no connection was found\n\t */\n\tpublic disconnect(suspend: boolean = false): void\n\t{\n\t\t// Remove connectivity and visibility handling listeners if the connection should be fully removed, instead of suspended.\n\t\tif(!suspend)\n\t\t{\n\t\t\t// Remove the visibility change listener\n\t\t\tif(typeof document !== 'undefined')\n\t\t\t{\n\t\t\t\tdocument.removeEventListener('visibilitychange', this.boundHandleVisibilityChange);\n\t\t\t}\n\n\t\t\t// Remove the connectivity change listeners\n\t\t\tif(typeof window !== 'undefined')\n\t\t\t{\n\t\t\t\twindow.removeEventListener('online', this.boundHandleConnectivityChange);\n\t\t\t\twindow.removeEventListener('offline', this.boundHandleConnectivityChange);\n\t\t\t}\n\t\t}\n\n\t\t// Clear the disconnect timer so that the socket does not try to disconnect again later.\n\t\tthis.clearDisconnectTimerOnTimeout();\n\n\t\ttry\n\t\t{\n\t\t\t// Remove all event forwarders.\n\t\t\tthis.webSocket.removeEventListener('close', this.eventForwarders.disconnect);\n\t\t\tthis.webSocket.removeEventListener('message', this.eventForwarders.wsData);\n\t\t\tthis.webSocket.removeEventListener('error', this.eventForwarders.wsError);\n\n\t\t\t// Add a one-time event listener for potential error events after closing the socket.\n\t\t\t// NOTE: This is needed since the close() call might not result in an error itself, but the\n\t\t\t// underlying network packets that are sent in order to do a graceful termination can fail.\n\t\t\tthis.webSocket.addEventListener('error', (_ignored) => {}, { once: true });\n\n\t\t\t// Gracefully terminate the connection\n\t\t\tthis.webSocket.close();\n\t\t}\n\t\tcatch (_ignored)\n\t\t{\n\t\t\t// close() will throw an error if the connection has not been established yet.\n\t\t\t// We ignore this error, since no similar error gets thrown in the TLS Socket.\n\t\t}\n\t\tfinally\n\t\t{\n\t\t\t// Remove the stored socket regardless of any thrown errors.\n\t\t\tthis.webSocket = undefined;\n\t\t}\n\n\t\tthis.onConnectHasRun = false;\n\n\t\tthis.emit('disconnected');\n\t}\n\n\t/**\n\t * Write data to the socket\n\t *\n\t * @param data Data to be written to the socket\n\t * @param callback Callback function to be called when the write has completed\n\t *\n\t * @throws {Error} if no connection was found\n\t * @returns true if the message was fully flushed to the socket, false if part of the message\n\t * is queued in the user memory\n\t */\n\tpublic write(data: Uint8Array | string, callback?: (err?: Error) => void): boolean\n\t{\n\t\t// Throw an error if no active connection is found\n\t\tif(!this.webSocket)\n\t\t{\n\t\t\tthrow(new Error('Cannot write to socket when there is no active connection'));\n\t\t}\n\n\t\t// Write data to the WebSocket\n\t\tthis.webSocket.send(data, callback);\n\n\t\t// WebSockets always fit everything in a single request, so we return true\n\t\treturn true;\n\t}\n\n\t/**\n\t * Force a disconnection if no connection is established after `timeout` milliseconds.\n\t */\n\tprivate disconnectOnTimeout(): void\n\t{\n\t\t// Remove the connect listener.\n\t\tthis.removeListener('connected', this.clearDisconnectTimerOnTimeout);\n\n\t\t// Emit an error event so that connect is rejected upstream.\n\t\tthis.emit('error', new Error(`Connection to '${this.host}:${this.port}' timed out after ${this.timeout} milliseconds`));\n\n\t\t// Forcibly disconnect to clean up the connection on timeout\n\t\tthis.disconnect();\n\t}\n\n\t/**\n\t * Handles visibility changes when run in a browser environment.\n\t */\n\tprivate handleVisibilityChange(): void\n\t{\n\t\t// If the document is hiding, we should disconnect the connection.\n\t\tif(document?.visibilityState === 'hidden')\n\t\t{\n\t\t\t// Suspend is used to indicate that we should not remove the handleVisibilityChange listener.\n\t\t\tconst suspend = true;\n\n\t\t\t// Disconnect the connection.\n\t\t\treturn this.disconnect(suspend);\n\t\t}\n\n\t\t// If the webSocket has already been connected, we don't need to connect again.\n\t\t// This could occur from a higher layer manually reconnecting\n\t\tif(this.webSocket)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t// Connect the connection.\n\t\treturn this.connect();\n\t}\n\n\t/**\n\t * Handles connectivity changes when run in a browser environment.\n\t */\n\tprivate handleConnectivityChange(): void\n\t{\n\t\t// If we are offline, we should disconnect the connection.\n\t\tif(window.navigator.onLine === false)\n\t\t{\n\t\t\t// Suspend is used to indicate that we should not remove the handleConnectivityChange listener.\n\t\t\tconst suspend = true;\n\n\t\t\t// disconnect the connection.\n\t\t\treturn this.disconnect(suspend);\n\t\t}\n\n\t\t// If the webSocket has already been connected, we don't need to connect again.\n\t\t// This could occur from a higher layer manually reconnecting\n\t\tif(this.webSocket)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t// Connect the connection.\n\t\treturn this.connect();\n\t}\n\n\t// Add magic glue that makes typedoc happy so that we can have the events listed on the class.\n\tpublic readonly connected: [];\n\tpublic readonly disconnected: [];\n\tpublic readonly data: [ string ];\n\tpublic readonly error: [ Error ];\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electrum-cash/web-socket",
3
- "version": "1.2.0",
3
+ "version": "1.2.1-development.12943431539",
4
4
  "description": "@electrum-cash/web-socket implements the ElectrumSocket interface using web sockets.",
5
5
  "keywords": [
6
6
  "electrum",