@electrum-cash/network 4.2.2 → 4.3.0-development.16529028959

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.d.mts CHANGED
@@ -1,8 +1,6 @@
1
- import { ElectrumWebSocketReconnectionOptions } from "@electrum-cash/web-socket";
2
1
  import { EventEmitter } from "eventemitter3";
3
-
2
+ import { ElectrumSocket, ElectrumSocketOptions } from "@electrum-cash/socket";
4
3
  //#region source/enums.d.ts
5
-
6
4
  /**
7
5
  * Enum that denotes the connection status of an ElectrumConnection.
8
6
  * @enum {number}
@@ -12,56 +10,56 @@ import { EventEmitter } from "eventemitter3";
12
10
  * @property {3} CONNECTING The connection is connecting.
13
11
  * @property {4} RECONNECTING The connection is restarting.
14
12
  */
15
- declare enum ConnectionStatus {
13
+ export declare enum ConnectionStatus {
16
14
  DISCONNECTED = 0,
17
15
  CONNECTED = 1,
18
16
  DISCONNECTING = 2,
19
17
  CONNECTING = 3,
20
- RECONNECTING = 4,
18
+ RECONNECTING = 4
21
19
  }
22
20
  //#endregion
23
21
  //#region source/rpc-interfaces.d.ts
24
- type RPCParameter = string | number | boolean | object | null;
25
- type RCPIdentifier = number | string | null;
26
- interface RPCBase {
22
+ export type RPCParameter = string | number | boolean | object | null;
23
+ export type RCPIdentifier = number | string | null;
24
+ export interface RPCBase {
27
25
  jsonrpc: string;
28
26
  }
29
- interface RPCNotification extends RPCBase {
27
+ export interface RPCNotification extends RPCBase {
30
28
  method: string;
31
29
  params?: RPCParameter[];
32
30
  }
33
- interface RPCRequest extends RPCBase {
31
+ export interface RPCRequest extends RPCBase {
34
32
  id: RCPIdentifier;
35
33
  method: string;
36
34
  params?: RPCParameter[];
37
35
  }
38
- interface RPCStatement extends RPCBase {
36
+ export interface RPCStatement extends RPCBase {
39
37
  id: RCPIdentifier;
40
38
  result: string;
41
39
  }
42
- interface RPCError {
40
+ export interface RPCError {
43
41
  code: number;
44
42
  message: string;
45
43
  data?: unknown;
46
44
  }
47
- interface RPCErrorResponse extends RPCBase {
45
+ export interface RPCErrorResponse extends RPCBase {
48
46
  id: RCPIdentifier;
49
47
  error: RPCError;
50
48
  }
51
- type RPCResponse = RPCErrorResponse | RPCStatement | RPCNotification;
52
- type RPCMessage = RPCNotification | RPCRequest | RPCResponse;
53
- type RPCResponseBatch = RPCResponse[];
54
- type RPCRequestBatch = RPCRequest[];
55
- declare const isRPCErrorResponse: (message: RPCBase) => message is RPCErrorResponse;
56
- declare const isRPCStatement: (message: RPCBase) => message is RPCStatement;
57
- declare const isRPCNotification: (message: RPCBase) => message is RPCNotification;
58
- declare const isRPCRequest: (message: RPCBase) => message is RPCRequest;
49
+ export type RPCResponse = RPCErrorResponse | RPCStatement | RPCNotification;
50
+ export type RPCMessage = RPCNotification | RPCRequest | RPCResponse;
51
+ export type RPCResponseBatch = RPCResponse[];
52
+ export type RPCRequestBatch = RPCRequest[];
53
+ export declare const isRPCErrorResponse: (message: RPCBase) => message is RPCErrorResponse;
54
+ export declare const isRPCStatement: (message: RPCBase) => message is RPCStatement;
55
+ export declare const isRPCNotification: (message: RPCBase) => message is RPCNotification;
56
+ export declare const isRPCRequest: (message: RPCBase) => message is RPCRequest;
59
57
  //#endregion
60
58
  //#region source/interfaces.d.ts
61
59
  /**
62
60
  * Optional settings that change the default behavior of the network connection.
63
61
  */
64
- interface ElectrumNetworkOptions extends Partial<ElectrumWebSocketReconnectionOptions> {
62
+ export interface ElectrumNetworkOptions extends Partial<ElectrumSocketOptions> {
65
63
  /** If set to true, numbers that can safely be parsed as integers will be `BigInt` rather than `Number`. */
66
64
  useBigInt?: boolean;
67
65
  /** When connected, send a keep-alive Ping message this often. */
@@ -71,96 +69,29 @@ interface ElectrumNetworkOptions extends Partial<ElectrumWebSocketReconnectionOp
71
69
  /** After every send, verify that we have received data after this amount of time. */
72
70
  verifyConnectionTimeoutInMilliSeconds?: number;
73
71
  }
74
- /**
75
- * List of events emitted by the ElectrumSocket.
76
- * @event
77
- * @ignore
78
- */
79
- interface ElectrumSocketEvents {
80
- /**
81
- * Emitted when data has been received over the socket.
82
- * @eventProperty
83
- */
84
- 'data': [string];
85
- /**
86
- * Emitted when a socket connects.
87
- * @eventProperty
88
- */
89
- 'connected': [];
90
- /**
91
- * Emitted when a socket disconnects.
92
- * @eventProperty
93
- */
94
- 'disconnected': [];
95
- /**
96
- * Emitted when the socket has failed in some way.
97
- * @eventProperty
98
- */
99
- 'error': [Error];
100
- }
101
- /**
102
- * Abstract socket used when communicating with Electrum servers.
103
- */
104
- interface ElectrumSocket extends EventEmitter<ElectrumSocketEvents>, ElectrumSocketEvents {
105
- /**
106
- * Utility function to provide a human accessible host identifier.
107
- */
108
- get hostIdentifier(): string;
109
- /**
110
- * Fully qualified domain name or IP address of the host
111
- */
112
- host: string;
113
- /**
114
- * Network port for the host to connect to, defaults to the standard TLS port
115
- */
116
- port: number;
117
- /**
118
- * If false, uses an unencrypted connection instead of the default on TLS
119
- */
120
- encrypted: boolean;
121
- /**
122
- * If no connection is established after `timeout` ms, the connection is terminated
123
- */
124
- timeout: number;
125
- /**
126
- * Connects to an Electrum server using the socket.
127
- */
128
- connect(): void;
129
- /**
130
- * Disconnects from the Electrum server from the socket.
131
- */
132
- disconnect(): void;
133
- /**
134
- * Write data to the Electrum server on the socket.
135
- *
136
- * @param data - Data to be written to the socket
137
- * @param callback - Callback function to be called when the write has completed
138
- */
139
- write(data: Uint8Array | string, callback?: (err?: Error) => void): boolean;
140
- }
141
72
  /**
142
73
  * @ignore
143
74
  */
144
- interface VersionRejected {
75
+ export interface VersionRejected {
145
76
  error: RPCError;
146
77
  }
147
78
  /**
148
79
  * @ignore
149
80
  */
150
- interface VersionNegotiated {
81
+ export interface VersionNegotiated {
151
82
  software: string;
152
83
  protocol: string;
153
84
  }
154
85
  /**
155
86
  * @ignore
156
87
  */
157
- type VersionNegotiationResponse = VersionNegotiated | VersionRejected;
88
+ export type VersionNegotiationResponse = VersionNegotiated | VersionRejected;
158
89
  /**
159
90
  * List of events emitted by the ElectrumConnection.
160
91
  * @event
161
92
  * @ignore
162
93
  */
163
- interface ElectrumConnectionEvents {
94
+ export interface ElectrumConnectionEvents {
164
95
  /**
165
96
  * Emitted when any data has been received over the network.
166
97
  * @eventProperty
@@ -212,7 +143,7 @@ interface ElectrumConnectionEvents {
212
143
  * @event
213
144
  * @ignore
214
145
  */
215
- interface ElectrumClientEvents {
146
+ export interface ElectrumClientEvents {
216
147
  /**
217
148
  * Emitted when an electrum subscription statement has been received over the network.
218
149
  * @eventProperty
@@ -253,31 +184,31 @@ interface ElectrumClientEvents {
253
184
  * A list of possible responses to requests.
254
185
  * @ignore
255
186
  */
256
- type RequestResponse = RPCParameter | RPCParameter[];
187
+ export type RequestResponse = RPCParameter | RPCParameter[];
257
188
  /**
258
189
  * Request resolvers are used to process the response of a request. This takes either
259
190
  * an error object or any stringified data, while the other parameter is omitted.
260
191
  * @ignore
261
192
  */
262
- type RequestResolver = (error?: Error, data?: string) => void;
193
+ export type RequestResolver = (error?: Error, data?: string) => void;
263
194
  /**
264
195
  * Typing for promise resolution.
265
196
  * @ignore
266
197
  */
267
- type ResolveFunction<T> = (value: T | PromiseLike<T>) => void;
198
+ export type ResolveFunction<T> = (value: T | PromiseLike<T>) => void;
268
199
  /**
269
200
  * Typing for promise rejection.
270
201
  * @ignore
271
202
  */
272
- type RejectFunction = (reason?: unknown) => void;
203
+ export type RejectFunction = (reason?: unknown) => void;
273
204
  /**
274
205
  * @ignore
275
206
  */
276
- declare const isVersionRejected: (object: VersionNegotiationResponse) => object is VersionRejected;
207
+ export declare const isVersionRejected: (object: VersionNegotiationResponse) => object is VersionRejected;
277
208
  /**
278
209
  * @ignore
279
210
  */
280
- declare const isVersionNegotiated: (object: VersionNegotiationResponse) => object is VersionNegotiated;
211
+ export declare const isVersionNegotiated: (object: VersionNegotiationResponse) => object is VersionNegotiated;
281
212
  //#endregion
282
213
  //#region source/electrum-client.d.ts
283
214
  /**
@@ -287,7 +218,7 @@ declare class ElectrumClient<ElectrumEvents extends ElectrumClientEvents> extend
287
218
  application: string;
288
219
  version: string;
289
220
  socketOrHostname: ElectrumSocket | string;
290
- options: ElectrumNetworkOptions;
221
+ options: Partial<ElectrumNetworkOptions>;
291
222
  /**
292
223
  * The name and version of the server software indexing the blockchain.
293
224
  */
@@ -325,7 +256,7 @@ declare class ElectrumClient<ElectrumEvents extends ElectrumClientEvents> extend
325
256
  *
326
257
  * @throws {Error} if `version` is not a valid version string.
327
258
  */
328
- constructor(application: string, version: string, socketOrHostname: ElectrumSocket | string, options?: ElectrumNetworkOptions);
259
+ constructor(application: string, version: string, socketOrHostname: ElectrumSocket | string, options?: Partial<ElectrumNetworkOptions>);
329
260
  get hostIdentifier(): string;
330
261
  get encrypted(): boolean;
331
262
  /**
@@ -443,5 +374,5 @@ declare class ElectrumClient<ElectrumEvents extends ElectrumClientEvents> extend
443
374
  readonly error: [Error];
444
375
  }
445
376
  //#endregion
446
- export { ConnectionStatus, ElectrumClient, ElectrumClientEvents, ElectrumConnectionEvents, ElectrumNetworkOptions, ElectrumSocket, ElectrumSocketEvents, RCPIdentifier, RPCBase, RPCError, RPCErrorResponse, RPCMessage, RPCNotification, RPCParameter, RPCRequest, RPCRequestBatch, RPCResponse, RPCResponseBatch, RPCStatement, RejectFunction, RequestResolver, RequestResponse, ResolveFunction, VersionNegotiated, VersionNegotiationResponse, VersionRejected, isRPCErrorResponse, isRPCNotification, isRPCRequest, isRPCStatement, isVersionNegotiated, isVersionRejected };
377
+ export { ElectrumClient };
447
378
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../source/enums.ts","../source/rpc-interfaces.ts","../source/interfaces.ts","../source/electrum-client.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../source/enums.ts","../source/rpc-interfaces.ts","../source/interfaces.ts","../source/electrum-client.ts"],"mappings":";;;;;;;;;;;;oBASY;EAEX;EACA;EACA;EACA;EACA;;;;YCdW;YAGA;iBAGK;EAEhB;;iBAIgB,wBAAwB;EAExC;EACA,SAAS;;iBAIO,mBAAmB;EAEnC,IAAI;EACJ;EACA,SAAS;;iBAIO,qBAAqB;EAErC,IAAI;EACJ;;iBAGgB;EAEhB;EACA;EACA;;iBAIgB,yBAAyB;EAEzC,IAAI;EACJ,OAAO;;YAII,cAAc,mBAAmB,eAAe;YAGhD,aAAa,kBAAkB,aAAa;YAG5C,mBAAmB;YACnB,kBAAkB;qBAEjB,qBAAkB,SAAqB,YAAU,WAAW;qBAK5D,iBAAc,SAAqB,YAAU,WAAW;qBAKxD,oBAAiB,SAAqB,YAAU,WAAW;qBAK3D,eAAY,SAAqB,YAAU,WAAW;;;;;;iBCnElD,+BAA+B,QAAQ;;EAGvD;;EAGA;;EAGA;;EAGA;;;;;iBAMgB;EAEhB,OAAO;;;;;iBAMS;EAEhB;EACA;;;;;YAMW,6BAA6B,oBAAoB;;;;;;iBAO5C;;;;;EAMhB;;;;;EAMA,aAAc;;;;;EAMd,YAAa;;;;;EAMb;;;;;EAMA;;;;;EAMA;;;;;EAMA;;;;;EAMA;;;;;EAMA,UAAW;;;;;;;iBAQK;;;;;EAMhB,iBAAkB;;;;;EAMlB;;;;;EAMA;;;;;EAMA;;;;;EAMA;;;;;EAMA;;;;;EAMA,UAAW;;;;;;YAOA,kBAAkB,eAAe;;;;;;YAOjC,mBAAmB,QAAQ,OAAO;;;;;YAMlC,gBAAgB,MAAM,OAAO,IAAI,YAAY;;;;;YAM7C,kBAAkB;;;;qBAKjB,oBAAiB,QAAoB,+BAA6B,UAAU;;;;qBAQ5E,sBAAmB,QAAoB,+BAA6B,UAAU;;;;;;cChLrF,eAAe,uBAAuB,8BAA8B,aAAa,uBAAuB,2BAA2B;EA0DhI;EACA;EACA,kBAAkB;EAClB,SAAS,QAAQ;;;;EAxDlB;;;;;EAMA;;;;;EAMA;;;;EAKA;;;;MAKI,UAAU;UAMb;UAGA;UAGA;UAGA;UAGA;;;;;;;;;;;EAaA,YAAA,qBACA,iBACA,kBAAkB,yBAClB,UAAS,QAAQ;MAcrB;MAMA;;;;;;;EAWE,WAAW;;;;;;;;;EA2CX,WAAW,iBAAwB,gCAAuC;;;;;;;;;;EA4B1E,QAAQ,mBAAmB,YAAY,iBAAiB,QAAQ,QAAQ;;;;;;;;;;;;EA2DxE,UAAU,mBAAmB,YAAY,iBAAiB;;;;;;;;;;;;EAqD1D,YAAY,mBAAmB,YAAY,iBAAiB;;;;;;;;;;UA6CpD;;;;;;;;;EA2Cd,SAAS,SAAS;;;;;;;EAgEZ,0BAA0B;;;;;;EAwB1B,qBAAqB,wBAAmB;;;;;;EAkBxC,+BAA+B;;;;;;;EAY/B,0CAA0C,eAAU;;;;;;;EAgBpD,qCAAqC,eAAU;;;;EAoB/C,8BAA8B,iBAAY;WAOhC;WACA;WACA;WACA;WACA;WACA,eAAgB;WAChB,QAAS"}
package/dist/index.mjs CHANGED
@@ -3,7 +3,6 @@ import { ElectrumWebSocket } from "@electrum-cash/web-socket";
3
3
  import { EventEmitter } from "eventemitter3";
4
4
  import { parse, parseNumberAndBigInt } from "lossless-json";
5
5
  import { Mutex } from "async-mutex";
6
-
7
6
  //#region source/electrum-protocol.ts
8
7
  /**
9
8
  * Grouping of utilities that simplifies implementation of the Electrum protocol.
@@ -44,7 +43,6 @@ var ElectrumProtocol = class {
44
43
  return "\n";
45
44
  }
46
45
  };
47
-
48
46
  //#endregion
49
47
  //#region source/rpc-interfaces.ts
50
48
  const isRPCErrorResponse = function(message) {
@@ -59,7 +57,6 @@ const isRPCNotification = function(message) {
59
57
  const isRPCRequest = function(message) {
60
58
  return "id" in message && "method" in message;
61
59
  };
62
-
63
60
  //#endregion
64
61
  //#region source/enums.ts
65
62
  /**
@@ -71,15 +68,14 @@ const isRPCRequest = function(message) {
71
68
  * @property {3} CONNECTING The connection is connecting.
72
69
  * @property {4} RECONNECTING The connection is restarting.
73
70
  */
74
- let ConnectionStatus = /* @__PURE__ */ function(ConnectionStatus$1) {
75
- ConnectionStatus$1[ConnectionStatus$1["DISCONNECTED"] = 0] = "DISCONNECTED";
76
- ConnectionStatus$1[ConnectionStatus$1["CONNECTED"] = 1] = "CONNECTED";
77
- ConnectionStatus$1[ConnectionStatus$1["DISCONNECTING"] = 2] = "DISCONNECTING";
78
- ConnectionStatus$1[ConnectionStatus$1["CONNECTING"] = 3] = "CONNECTING";
79
- ConnectionStatus$1[ConnectionStatus$1["RECONNECTING"] = 4] = "RECONNECTING";
80
- return ConnectionStatus$1;
71
+ let ConnectionStatus = /* @__PURE__ */ function(ConnectionStatus) {
72
+ ConnectionStatus[ConnectionStatus["DISCONNECTED"] = 0] = "DISCONNECTED";
73
+ ConnectionStatus[ConnectionStatus["CONNECTED"] = 1] = "CONNECTED";
74
+ ConnectionStatus[ConnectionStatus["DISCONNECTING"] = 2] = "DISCONNECTING";
75
+ ConnectionStatus[ConnectionStatus["CONNECTING"] = 3] = "CONNECTING";
76
+ ConnectionStatus[ConnectionStatus["RECONNECTING"] = 4] = "RECONNECTING";
77
+ return ConnectionStatus;
81
78
  }({});
82
-
83
79
  //#endregion
84
80
  //#region source/interfaces.ts
85
81
  /**
@@ -94,14 +90,17 @@ const isVersionRejected = function(object) {
94
90
  const isVersionNegotiated = function(object) {
95
91
  return "software" in object && "protocol" in object;
96
92
  };
97
-
98
93
  //#endregion
99
94
  //#region source/electrum-connection.ts
100
95
  /**
101
96
  * Wrapper around TLS/WSS sockets that gracefully separates a network stream into Electrum protocol messages.
102
97
  */
103
98
  var ElectrumConnection = class extends EventEmitter {
104
- status = ConnectionStatus.DISCONNECTED;
99
+ application;
100
+ version;
101
+ socketOrHostname;
102
+ options;
103
+ status = 0;
105
104
  lastReceivedTimestamp;
106
105
  socket;
107
106
  keepAliveTimer;
@@ -124,18 +123,18 @@ var ElectrumConnection = class extends EventEmitter {
124
123
  this.version = version;
125
124
  this.socketOrHostname = socketOrHostname;
126
125
  this.options = options;
127
- if (!ElectrumProtocol.versionRegexp.test(version)) throw /* @__PURE__ */ new Error(`Provided version string (${version}) is not a valid protocol version number.`);
128
- if (typeof socketOrHostname === "string") this.socket = new ElectrumWebSocket(socketOrHostname, void 0, void 0, void 0, this.options);
126
+ if (!ElectrumProtocol.versionRegexp.test(version)) throw new Error(`Provided version string (${version}) is not a valid protocol version number.`);
127
+ if (typeof socketOrHostname === "string") this.socket = new ElectrumWebSocket(socketOrHostname, this.options);
129
128
  else this.socket = socketOrHostname;
130
129
  this.socket.on("connected", this.onSocketConnect.bind(this));
131
130
  this.socket.on("disconnected", this.onSocketDisconnect.bind(this));
132
131
  this.socket.on("data", this.parseMessageChunk.bind(this));
133
132
  }
134
133
  get hostIdentifier() {
135
- return this.socket.hostIdentifier;
134
+ return this.socket.host;
136
135
  }
137
136
  get encrypted() {
138
- return this.socket.encrypted;
137
+ return this.socket.options.encrypted;
139
138
  }
140
139
  /**
141
140
  * Assembles incoming data into statements and hands them off to the message parser.
@@ -153,7 +152,8 @@ var ElectrumConnection = class extends EventEmitter {
153
152
  while (this.messageBuffer.includes(ElectrumProtocol.statementDelimiter)) {
154
153
  const statementParts = this.messageBuffer.split(ElectrumProtocol.statementDelimiter);
155
154
  while (statementParts.length > 1) {
156
- let statementList = parse(String(statementParts.shift()), null, this.options.useBigInt ? parseNumberAndBigInt : parseFloat);
155
+ const currentStatementList = String(statementParts.shift());
156
+ let statementList = parse(currentStatementList, null, this.options.useBigInt ? parseNumberAndBigInt : parseFloat);
157
157
  if (!Array.isArray(statementList)) statementList = [statementList];
158
158
  while (statementList.length > 0) {
159
159
  const currentStatement = statementList.shift();
@@ -185,7 +185,7 @@ var ElectrumConnection = class extends EventEmitter {
185
185
  * @returns true if the ping message was fully flushed to the socket, false if
186
186
  * part of the message is queued in the user memory
187
187
  */
188
- ping() {
188
+ async ping() {
189
189
  debug.ping(`Sending keep-alive ping to '${this.hostIdentifier}'`);
190
190
  const message = ElectrumProtocol.buildRequestObject("server.ping", [], "keepAlive");
191
191
  return this.send(message);
@@ -197,8 +197,8 @@ var ElectrumConnection = class extends EventEmitter {
197
197
  * @returns a promise resolving when the connection is established
198
198
  */
199
199
  async connect() {
200
- if (this.status === ConnectionStatus.CONNECTED) return;
201
- this.status = ConnectionStatus.CONNECTING;
200
+ if (this.status === 1) return;
201
+ this.status = 3;
202
202
  this.emit("connecting");
203
203
  const connectionResolver = (resolve, reject) => {
204
204
  this.once("connected", () => {
@@ -219,7 +219,7 @@ var ElectrumConnection = class extends EventEmitter {
219
219
  async reconnect() {
220
220
  await this.clearReconnectTimer();
221
221
  debug.network(`Trying to reconnect to '${this.hostIdentifier}'..`);
222
- this.status = ConnectionStatus.RECONNECTING;
222
+ this.status = 4;
223
223
  this.emit("reconnecting");
224
224
  this.socket.disconnect();
225
225
  try {
@@ -255,8 +255,8 @@ var ElectrumConnection = class extends EventEmitter {
255
255
  * @returns true if successfully disconnected, or false if there was no connection.
256
256
  */
257
257
  async disconnect(force = false, intentional = true) {
258
- if (this.status === ConnectionStatus.DISCONNECTED && !force) return false;
259
- if (intentional) this.status = ConnectionStatus.DISCONNECTING;
258
+ if (this.status === 0 && !force) return false;
259
+ if (intentional) this.status = 2;
260
260
  this.emit("disconnecting");
261
261
  await this.clearKeepAliveTimer();
262
262
  await this.clearReconnectTimer();
@@ -274,10 +274,10 @@ var ElectrumConnection = class extends EventEmitter {
274
274
  * @returns true if the message was fully flushed to the socket, false if part of the message
275
275
  * is queued in the user memory
276
276
  */
277
- send(message) {
277
+ async send(message) {
278
278
  this.clearKeepAliveTimer();
279
279
  const currentTime = Date.now();
280
- const verificationTimer = setTimeout(this.verifySend.bind(this, currentTime), this.socket.timeout);
280
+ const verificationTimer = setTimeout(this.verifySend.bind(this, currentTime), this.socket.options.timeoutInMilliSeconds);
281
281
  this.verifications.push(verificationTimer);
282
282
  this.setupKeepAliveTimer();
283
283
  return this.socket.write(message + ElectrumProtocol.statementDelimiter);
@@ -288,7 +288,7 @@ var ElectrumConnection = class extends EventEmitter {
288
288
  */
289
289
  verifySend(sentTimestamp) {
290
290
  if (Number(this.lastReceivedTimestamp) < sentTimestamp) {
291
- if (this.status === ConnectionStatus.DISCONNECTED || this.status === ConnectionStatus.DISCONNECTING) return;
291
+ if (this.status === 0 || this.status === 2) return;
292
292
  this.clearKeepAliveTimer();
293
293
  debug.network(`Connection to '${this.hostIdentifier}' timed out.`);
294
294
  this.socket.disconnect();
@@ -303,23 +303,21 @@ var ElectrumConnection = class extends EventEmitter {
303
303
  this.setupKeepAliveTimer();
304
304
  await new Promise(this.negotiateVersion.bind(this));
305
305
  this.emit("connected");
306
- this.socket.removeAllListeners("error");
307
- this.socket.on("error", this.onSocketError.bind(this));
308
306
  }
309
307
  /**
310
308
  * Updates the connection status when a connection is ended.
311
309
  */
312
310
  onSocketDisconnect() {
313
311
  this.clearKeepAliveTimer();
314
- if (this.status === ConnectionStatus.DISCONNECTING) {
315
- this.status = ConnectionStatus.DISCONNECTED;
312
+ if (this.status === 2) {
313
+ this.status = 0;
316
314
  this.emit("disconnected");
317
315
  this.clearReconnectTimer();
318
316
  this.removeAllListeners();
319
317
  debug.network(`Disconnected from '${this.hostIdentifier}'.`);
320
318
  } else {
321
- if (this.status === ConnectionStatus.CONNECTED) debug.errors(`Connection with '${this.hostIdentifier}' was closed, trying to reconnect in ${this.options.reconnectAfterMilliSeconds / 1e3} seconds.`);
322
- this.status = ConnectionStatus.DISCONNECTED;
319
+ if (this.status === 1) debug.errors(`Connection with '${this.hostIdentifier}' was closed, trying to reconnect in ${this.options.reconnectAfterMilliSeconds / 1e3} seconds.`);
320
+ this.status = 0;
323
321
  this.emit("disconnected");
324
322
  if (!this.reconnectTimer) this.reconnectTimer = setTimeout(this.reconnect.bind(this), this.options.reconnectAfterMilliSeconds);
325
323
  }
@@ -338,13 +336,13 @@ var ElectrumConnection = class extends EventEmitter {
338
336
  * @param reject
339
337
  */
340
338
  async negotiateVersion(resolve, reject) {
341
- const rejector = (error) => {
342
- this.status = ConnectionStatus.DISCONNECTED;
339
+ const rejector = () => {
340
+ this.status = 0;
343
341
  this.emit("disconnected");
344
- reject(error);
342
+ reject(`Version negotiation with ${this.hostIdentifier} failed.`);
345
343
  };
346
344
  debug.network(`Requesting protocol version ${this.version} with '${this.hostIdentifier}'.`);
347
- this.socket.once("error", rejector);
345
+ this.socket.once("disconnected", rejector);
348
346
  const versionMessage = ElectrumProtocol.buildRequestObject("server.version", [this.application, this.version], "versionNegotiation");
349
347
  const versionValidator = (version) => {
350
348
  if (isVersionRejected(version)) {
@@ -359,7 +357,7 @@ var ElectrumConnection = class extends EventEmitter {
359
357
  reject(errorMessage);
360
358
  } else {
361
359
  debug.network(`Negotiated protocol version ${version.protocol} with '${this.hostIdentifier}', powered by ${version.software}.`);
362
- this.status = ConnectionStatus.CONNECTED;
360
+ this.status = 1;
363
361
  resolve();
364
362
  }
365
363
  };
@@ -367,7 +365,6 @@ var ElectrumConnection = class extends EventEmitter {
367
365
  this.send(versionMessage);
368
366
  }
369
367
  };
370
-
371
368
  //#endregion
372
369
  //#region source/constants.ts
373
370
  const MILLI_SECONDS_PER_SECOND = 1e3;
@@ -378,17 +375,18 @@ const defaultNetworkOptions = {
378
375
  useBigInt: false,
379
376
  sendKeepAliveIntervalInMilliSeconds: 1 * MILLI_SECONDS_PER_SECOND,
380
377
  reconnectAfterMilliSeconds: 5 * MILLI_SECONDS_PER_SECOND,
381
- verifyConnectionTimeoutInMilliSeconds: 5 * MILLI_SECONDS_PER_SECOND,
382
- disableBrowserVisibilityHandling: false,
383
- disableBrowserConnectivityHandling: false
378
+ verifyConnectionTimeoutInMilliSeconds: 5 * MILLI_SECONDS_PER_SECOND
384
379
  };
385
-
386
380
  //#endregion
387
381
  //#region source/electrum-client.ts
388
382
  /**
389
383
  * High-level Electrum client that lets applications send requests and subscribe to notification events from a server.
390
384
  */
391
385
  var ElectrumClient = class extends EventEmitter {
386
+ application;
387
+ version;
388
+ socketOrHostname;
389
+ options;
392
390
  /**
393
391
  * The name and version of the server software indexing the blockchain.
394
392
  */
@@ -434,10 +432,11 @@ var ElectrumClient = class extends EventEmitter {
434
432
  this.version = version;
435
433
  this.socketOrHostname = socketOrHostname;
436
434
  this.options = options;
437
- this.connection = new ElectrumConnection(application, version, socketOrHostname, {
435
+ const networkOptions = {
438
436
  ...defaultNetworkOptions,
439
437
  ...options
440
- });
438
+ };
439
+ this.connection = new ElectrumConnection(application, version, socketOrHostname, networkOptions);
441
440
  }
442
441
  get hostIdentifier() {
443
442
  return this.connection.hostIdentifier;
@@ -452,9 +451,8 @@ var ElectrumClient = class extends EventEmitter {
452
451
  * @returns a promise resolving when the connection is established.
453
452
  */
454
453
  async connect() {
455
- const unlock = await this.connectionLock.acquire();
456
- try {
457
- if (this.connection.status === ConnectionStatus.CONNECTED) return;
454
+ return this.connectionLock.runExclusive(async () => {
455
+ if (this.connection.status === 1) return;
458
456
  this.connection.on("response", this.response.bind(this));
459
457
  this.connection.on("connected", this.resubscribeOnConnect.bind(this));
460
458
  this.connection.on("disconnected", this.onConnectionDisconnect.bind(this));
@@ -465,9 +463,7 @@ var ElectrumClient = class extends EventEmitter {
465
463
  this.connection.on("received", this.updateLastReceivedTimestamp.bind(this));
466
464
  this.connection.on("error", this.emit.bind(this, "error"));
467
465
  await this.connection.connect();
468
- } finally {
469
- unlock();
470
- }
466
+ });
471
467
  }
472
468
  /**
473
469
  * Disconnects from the remote server and removes all event listeners/subscriptions and open requests.
@@ -478,11 +474,13 @@ var ElectrumClient = class extends EventEmitter {
478
474
  * @returns true if successfully disconnected, or false if there was no connection.
479
475
  */
480
476
  async disconnect(force = false, retainSubscriptions = false) {
481
- if (!retainSubscriptions) {
482
- this.removeAllListeners();
483
- this.subscriptionMethods = {};
484
- }
485
- return this.connection.disconnect(force);
477
+ return this.connectionLock.runExclusive(async () => {
478
+ if (!retainSubscriptions) {
479
+ this.removeAllListeners();
480
+ this.subscriptionMethods = {};
481
+ }
482
+ return this.connection.disconnect(force);
483
+ });
486
484
  }
487
485
  /**
488
486
  * Calls a method on the remote server with the supplied parameters.
@@ -494,7 +492,7 @@ var ElectrumClient = class extends EventEmitter {
494
492
  * @returns a promise that resolves with the result of the method or an Error.
495
493
  */
496
494
  async request(method, ...parameters) {
497
- if (this.connection.status !== ConnectionStatus.CONNECTED) throw /* @__PURE__ */ new Error(`Unable to send request to a disconnected server '${this.hostIdentifier}'.`);
495
+ if (this.connection.status !== 1) throw new Error(`Unable to send request to a disconnected server '${this.hostIdentifier}'.`);
498
496
  this.requestId += 1;
499
497
  const id = this.requestId;
500
498
  const message = ElectrumProtocol.buildRequestObject(method, parameters, id);
@@ -524,7 +522,7 @@ var ElectrumClient = class extends EventEmitter {
524
522
  this.subscriptionMethods[method].add(JSON.stringify(parameters));
525
523
  const requestData = await this.request(method, ...parameters);
526
524
  if (requestData instanceof Error) throw requestData;
527
- if (Array.isArray(requestData)) throw /* @__PURE__ */ new Error("Subscription request returned an more than one data point.");
525
+ if (Array.isArray(requestData)) throw new Error("Subscription request returned an more than one data point.");
528
526
  const notification = {
529
527
  jsonrpc: "2.0",
530
528
  method,
@@ -545,10 +543,10 @@ var ElectrumClient = class extends EventEmitter {
545
543
  * @returns a promise resolving when the subscription is removed.
546
544
  */
547
545
  async unsubscribe(method, ...parameters) {
548
- if (this.connection.status !== ConnectionStatus.CONNECTED) throw /* @__PURE__ */ new Error(`Unable to send unsubscribe request to a disconnected server '${this.hostIdentifier}'.`);
549
- if (!this.subscriptionMethods[method]) throw /* @__PURE__ */ new Error(`Cannot unsubscribe from '${method}' since the method has no subscriptions.`);
546
+ if (this.connection.status !== 1) throw new Error(`Unable to send unsubscribe request to a disconnected server '${this.hostIdentifier}'.`);
547
+ if (!this.subscriptionMethods[method]) throw new Error(`Cannot unsubscribe from '${method}' since the method has no subscriptions.`);
550
548
  const subscriptionParameters = JSON.stringify(parameters);
551
- if (!this.subscriptionMethods[method].has(subscriptionParameters)) throw /* @__PURE__ */ new Error(`Cannot unsubscribe from '${method}' since it has no subscription with the given parameters.`);
549
+ if (!this.subscriptionMethods[method].has(subscriptionParameters)) throw new Error(`Cannot unsubscribe from '${method}' since it has no subscription with the given parameters.`);
552
550
  this.subscriptionMethods[method].delete(subscriptionParameters);
553
551
  await this.request(method.replace(".subscribe", ".unsubscribe"), ...parameters);
554
552
  debug.client(`Unsubscribed from '${String(method)}' for the '${subscriptionParameters}' parameters.`);
@@ -590,7 +588,7 @@ var ElectrumClient = class extends EventEmitter {
590
588
  this.updateChainHeightFromHeadersNotifications(message);
591
589
  return;
592
590
  }
593
- if (message.id === null) throw /* @__PURE__ */ new Error("Internal error: Received an RPC response with ID null.");
591
+ if (message.id === null) throw new Error("Internal error: Received an RPC response with ID null.");
594
592
  const requestResolver = this.requestResolvers[message.id];
595
593
  if (!requestResolver) {
596
594
  debug.warning(`Ignoring response #${message.id} as the request has already been rejected.`);
@@ -668,8 +666,7 @@ var ElectrumClient = class extends EventEmitter {
668
666
  notification;
669
667
  error;
670
668
  };
671
- var electrum_client_default = ElectrumClient;
672
-
673
669
  //#endregion
674
- export { ConnectionStatus, electrum_client_default as ElectrumClient, isRPCErrorResponse, isRPCNotification, isRPCRequest, isRPCStatement, isVersionNegotiated, isVersionRejected };
670
+ export { ConnectionStatus, ElectrumClient, isRPCErrorResponse, isRPCNotification, isRPCRequest, isRPCStatement, isVersionNegotiated, isVersionRejected };
671
+
675
672
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../source/electrum-protocol.ts","../source/rpc-interfaces.ts","../source/enums.ts","../source/interfaces.ts","../source/electrum-connection.ts","../source/constants.ts","../source/electrum-client.ts"],"sourcesContent":["import type { RPCParameter } from './rpc-interfaces.ts';\n\n/**\n * Grouping of utilities that simplifies implementation of the Electrum protocol.\n *\n * @ignore\n */\nexport class ElectrumProtocol\n{\n\t/**\n\t * Helper function that builds an Electrum request object.\n\t *\n\t * @param method - method to call.\n\t * @param parameters - method parameters for the call.\n\t * @param requestId - unique string or number referencing this request.\n\t *\n\t * @returns a properly formatted Electrum request string.\n\t */\n\tstatic buildRequestObject(method: string, parameters: RPCParameter[], requestId: string | number): string\n\t{\n\t\t// Return the formatted request object.\n\t\t// NOTE: Electrum either uses JsonRPC strictly or loosely.\n\t\t// If we specify protocol identifier without being 100% compliant, we risk being disconnected/blacklisted.\n\t\t// For this reason, we omit the protocol identifier to avoid issues.\n\t\treturn JSON.stringify({ method: method, params: parameters, id: requestId });\n\t}\n\n\t/**\n\t * Constant used to verify if a provided string is a valid version number.\n\t *\n\t * @returns a regular expression that matches valid version numbers.\n\t */\n\tstatic get versionRegexp(): RegExp\n\t{\n\t\treturn /^\\d+(\\.\\d+)+$/;\n\t}\n\n\t/**\n\t * Constant used to separate statements/messages in a stream of data.\n\t *\n\t * @returns the delimiter used by Electrum to separate statements.\n\t */\n\tstatic get statementDelimiter(): string\n\t{\n\t\treturn '\\n';\n\t}\n}\n","// Acceptable parameter types for RPC messages\nexport type RPCParameter = string | number | boolean | object | null;\n\n// Acceptable identifier types for RCP messages.\nexport type RCPIdentifier = number | string | null;\n\n// The base type for all RPC messages\nexport interface RPCBase\n{\n\tjsonrpc: string;\n}\n\n// An RPC message that sends a notification requiring no response\nexport interface RPCNotification extends RPCBase\n{\n\tmethod: string;\n\tparams?: RPCParameter[];\n}\n\n// An RPC message that sends a request requiring a response\nexport interface RPCRequest extends RPCBase\n{\n\tid: RCPIdentifier;\n\tmethod: string;\n\tparams?: RPCParameter[];\n}\n\n// An RPC message that returns the response to a successful request\nexport interface RPCStatement extends RPCBase\n{\n\tid: RCPIdentifier;\n\tresult: string;\n}\n\nexport interface RPCError\n{\n\tcode: number;\n\tmessage: string;\n\tdata?: unknown;\n}\n\n// An RPC message that returns the error to an unsuccessful request\nexport interface RPCErrorResponse extends RPCBase\n{\n\tid: RCPIdentifier;\n\terror: RPCError;\n}\n\n// A response to a request is either a statement (successful) or an error (unsuccessful)\nexport type RPCResponse = RPCErrorResponse | RPCStatement | RPCNotification;\n\n// RPC messages are notifications, requests, or responses\nexport type RPCMessage = RPCNotification | RPCRequest | RPCResponse;\n\n// Requests and responses can also be sent in batches\nexport type RPCResponseBatch = RPCResponse[];\nexport type RPCRequestBatch = RPCRequest[];\n\nexport const isRPCErrorResponse = function(message: RPCBase): message is RPCErrorResponse\n{\n\treturn 'id' in message && 'error' in message;\n};\n\nexport const isRPCStatement = function(message: RPCBase): message is RPCStatement\n{\n\treturn 'id' in message && 'result' in message;\n};\n\nexport const isRPCNotification = function(message: RPCBase): message is RPCNotification\n{\n\treturn !('id' in message) && 'method' in message;\n};\n\nexport const isRPCRequest = function(message: RPCBase): message is RPCRequest\n{\n\treturn 'id' in message && 'method' in message;\n};\n","/**\n * Enum that denotes the connection status of an ElectrumConnection.\n * @enum {number}\n * @property {0} DISCONNECTED The connection is disconnected.\n * @property {1} AVAILABLE The connection is connected.\n * @property {2} DISCONNECTING The connection is disconnecting.\n * @property {3} CONNECTING The connection is connecting.\n * @property {4} RECONNECTING The connection is restarting.\n */\nexport enum ConnectionStatus\n{\n\tDISCONNECTED = 0,\n\tCONNECTED = 1,\n\tDISCONNECTING = 2,\n\tCONNECTING = 3,\n\tRECONNECTING = 4,\n}\n","import type { RPCError, RPCParameter, RPCResponse, RPCNotification } from './rpc-interfaces';\nimport type { ElectrumWebSocketReconnectionOptions } from '@electrum-cash/web-socket';\nimport type { EventEmitter } from 'eventemitter3';\n\n/**\n * Optional settings that change the default behavior of the network connection.\n */\nexport interface ElectrumNetworkOptions extends Partial<ElectrumWebSocketReconnectionOptions>\n{\n\t/** If set to true, numbers that can safely be parsed as integers will be `BigInt` rather than `Number`. */\n\tuseBigInt?: boolean;\n\n\t/** When connected, send a keep-alive Ping message this often. */\n\tsendKeepAliveIntervalInMilliSeconds?: number;\n\n\t/** When disconnected, attempt to reconnect after this amount of time. */\n\treconnectAfterMilliSeconds?: number;\n\n\t/** After every send, verify that we have received data after this amount of time. */\n\tverifyConnectionTimeoutInMilliSeconds?: number;\n}\n\n/**\n * List of events emitted by the ElectrumSocket.\n * @event\n * @ignore\n */\nexport interface ElectrumSocketEvents\n{\n\t/**\n\t * Emitted when data has been received over the socket.\n\t * @eventProperty\n\t */\n\t'data': [ string ];\n\n\t/**\n\t * Emitted when a socket connects.\n\t * @eventProperty\n\t */\n\t'connected': [];\n\n\t/**\n\t * Emitted when a socket disconnects.\n\t * @eventProperty\n\t */\n\t'disconnected': [];\n\n\t/**\n\t * Emitted when the socket has failed in some way.\n\t * @eventProperty\n\t */\n\t'error': [ Error ];\n}\n\n/**\n * Abstract socket used when communicating with Electrum servers.\n */\nexport interface ElectrumSocket extends EventEmitter<ElectrumSocketEvents>, ElectrumSocketEvents\n{\n\t/**\n\t * Utility function to provide a human accessible host identifier.\n\t */\n\tget hostIdentifier(): string;\n\n\t/**\n\t * Fully qualified domain name or IP address of the host\n\t */\n\thost: string;\n\n\t/**\n\t * Network port for the host to connect to, defaults to the standard TLS port\n\t */\n\tport: number;\n\n\t/**\n\t * If false, uses an unencrypted connection instead of the default on TLS\n\t */\n\tencrypted: boolean;\n\n\t/**\n\t * If no connection is established after `timeout` ms, the connection is terminated\n\t */\n\ttimeout: number;\n\n\t/**\n\t * Connects to an Electrum server using the socket.\n\t */\n\tconnect(): void;\n\n\t/**\n\t * Disconnects from the Electrum server from the socket.\n\t */\n\tdisconnect(): void;\n\n\t/**\n\t * Write data to the Electrum server on 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\twrite(data: Uint8Array | string, callback?: (err?: Error) => void): boolean;\n}\n\n/**\n * @ignore\n */\nexport interface VersionRejected\n{\n\terror: RPCError;\n}\n\n/**\n * @ignore\n */\nexport interface VersionNegotiated\n{\n\tsoftware: string;\n\tprotocol: string;\n}\n\n/**\n * @ignore\n */\nexport type VersionNegotiationResponse = VersionNegotiated | VersionRejected;\n\n/**\n * List of events emitted by the ElectrumConnection.\n * @event\n * @ignore\n */\nexport interface ElectrumConnectionEvents\n{\n\t/**\n\t * Emitted when any data has been received over the network.\n\t * @eventProperty\n\t */\n\t'received': [];\n\n\t/**\n\t * Emitted when a complete electrum message has been received over the network.\n\t * @eventProperty\n\t */\n\t'response': [ RPCResponse ];\n\n\t/**\n\t * Emitted when the connection has completed version negotiation.\n\t * @eventProperty\n\t */\n\t'version': [ VersionNegotiationResponse ];\n\n\t/**\n\t * Emitted when a network connection is initiated.\n\t * @eventProperty\n\t */\n\t'connecting': [];\n\n\t/**\n\t * Emitted when a network connection is successful.\n\t * @eventProperty\n\t */\n\t'connected': [];\n\n\t/**\n\t * Emitted when a network disconnection is initiated.\n\t * @eventProperty\n\t */\n\t'disconnecting': [];\n\n\t/**\n\t * Emitted when a network disconnection is successful.\n\t * @eventProperty\n\t */\n\t'disconnected': [];\n\n\t/**\n\t * Emitted when a network connect attempts to automatically reconnect.\n\t * @eventProperty\n\t */\n\t'reconnecting': [];\n\n\t/**\n\t * Emitted when the network has failed in some way.\n\t * @eventProperty\n\t */\n\t'error': [ Error ];\n}\n\n/**\n * List of events emitted by the ElectrumClient.\n * @event\n * @ignore\n */\nexport interface ElectrumClientEvents\n{\n\t/**\n\t * Emitted when an electrum subscription statement has been received over the network.\n\t * @eventProperty\n\t */\n\t'notification': [ RPCNotification ];\n\n\t/**\n\t * Emitted when a network connection is initiated.\n\t * @eventProperty\n\t */\n\t'connecting': [];\n\n\t/**\n\t * Emitted when a network connection is successful.\n\t * @eventProperty\n\t */\n\t'connected': [];\n\n\t/**\n\t * Emitted when a network disconnection is initiated.\n\t * @eventProperty\n\t */\n\t'disconnecting': [];\n\n\t/**\n\t * Emitted when a network disconnection is successful.\n\t * @eventProperty\n\t */\n\t'disconnected': [];\n\n\t/**\n\t * Emitted when a network connect attempts to automatically reconnect.\n\t * @eventProperty\n\t */\n\t'reconnecting': [];\n\n\t/**\n\t * Emitted when the network has failed in some way.\n\t * @eventProperty\n\t */\n\t'error': [ Error ];\n}\n\n/**\n * A list of possible responses to requests.\n * @ignore\n */\nexport type RequestResponse = RPCParameter | RPCParameter[];\n\n/**\n * Request resolvers are used to process the response of a request. This takes either\n * an error object or any stringified data, while the other parameter is omitted.\n * @ignore\n */\nexport type RequestResolver = (error?: Error, data?: string) => void;\n\n/**\n * Typing for promise resolution.\n * @ignore\n */\nexport type ResolveFunction<T> = (value: T | PromiseLike<T>) => void;\n\n/**\n * Typing for promise rejection.\n * @ignore\n */\nexport type RejectFunction = (reason?: unknown) => void;\n\n/**\n * @ignore\n */\nexport const isVersionRejected = function(object: VersionNegotiationResponse): object is VersionRejected\n{\n\treturn 'error' in object;\n};\n\n/**\n * @ignore\n */\nexport const isVersionNegotiated = function(object: VersionNegotiationResponse): object is VersionNegotiated\n{\n\treturn 'software' in object && 'protocol' in object;\n};\n","import debug from '@electrum-cash/debug-logs';\nimport { ElectrumWebSocket } from '@electrum-cash/web-socket';\nimport { ElectrumProtocol } from './electrum-protocol.ts';\nimport { isRPCNotification, isRPCErrorResponse } from './rpc-interfaces.ts';\nimport { EventEmitter } from 'eventemitter3';\nimport { ConnectionStatus } from './enums.ts';\nimport { parse, parseNumberAndBigInt } from 'lossless-json';\nimport { isVersionRejected } from './interfaces.ts';\nimport type { ElectrumNetworkOptions, ElectrumConnectionEvents, ElectrumSocket, ResolveFunction, RejectFunction, VersionNegotiationResponse } from './interfaces.ts';\nimport type { RPCResponse } from './rpc-interfaces.ts';\n\n/**\n * Wrapper around TLS/WSS sockets that gracefully separates a network stream into Electrum protocol messages.\n */\nexport class ElectrumConnection extends EventEmitter<ElectrumConnectionEvents>\n{\n\t// Initialize the connected flag to false to indicate that there is no connection\n\tpublic status: ConnectionStatus = ConnectionStatus.DISCONNECTED;\n\n\t// Declare empty timestamps\n\tprivate lastReceivedTimestamp: number;\n\n\t// Declare an empty socket.\n\tprivate socket: ElectrumSocket;\n\n\t// Declare timers for keep-alive pings and reconnection\n\tprivate keepAliveTimer?: number;\n\tprivate reconnectTimer?: number;\n\n\t// Initialize an empty array of connection verification timers.\n\tprivate verifications: Array<number> = [];\n\n\t// Initialize messageBuffer to an empty string\n\tprivate messageBuffer = '';\n\n\t/**\n\t * Sets up network configuration for an Electrum client connection.\n\t *\n\t * @param application - your application name, used to identify to the electrum host.\n\t * @param version - protocol version to use with the host.\n\t * @param socketOrHostname - pre-configured electrum socket or fully qualified domain name or IP number of the host\n\t * @param options - ...\n\t *\n\t * @throws {Error} if `version` is not a valid version string.\n\t */\n\tconstructor(\n\t\tprivate application: string,\n\t\tprivate version: string,\n\t\tprivate socketOrHostname: ElectrumSocket | string,\n\t\tprivate options: ElectrumNetworkOptions,\n\t)\n\t{\n\t\t// Initialize the event emitter.\n\t\tsuper();\n\n\t\t// Check if the provided version is a valid version number.\n\t\tif(!ElectrumProtocol.versionRegexp.test(version))\n\t\t{\n\t\t\t// Throw an error since the version number was not valid.\n\t\t\tthrow(new Error(`Provided version string (${version}) is not a valid protocol version number.`));\n\t\t}\n\n\t\t// If a hostname was provided..\n\t\tif(typeof socketOrHostname === 'string')\n\t\t{\n\t\t\t// Use a web socket with default parameters.\n\t\t\tthis.socket = new ElectrumWebSocket(socketOrHostname, undefined, undefined, undefined, this.options);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Use the provided socket.\n\t\t\tthis.socket = socketOrHostname;\n\t\t}\n\n\t\t// Set up handlers for connection and disconnection.\n\t\tthis.socket.on('connected', this.onSocketConnect.bind(this));\n\t\tthis.socket.on('disconnected', this.onSocketDisconnect.bind(this));\n\n\t\t// Set up handler for incoming data.\n\t\tthis.socket.on('data', this.parseMessageChunk.bind(this));\n\t}\n\n\t// Expose hostIdentifier from the socket.\n\tget hostIdentifier(): string\n\t{\n\t\treturn this.socket.hostIdentifier;\n\t}\n\n\t// Expose port from the socket.\n\tget encrypted(): boolean\n\t{\n\t\treturn this.socket.encrypted;\n\t}\n\n\t/**\n\t * Assembles incoming data into statements and hands them off to the message parser.\n\t *\n\t * @param data - data to append to the current message buffer, as a string.\n\t *\n\t * @throws {SyntaxError} if the passed statement parts are not valid JSON.\n\t */\n\tparseMessageChunk(data: string): void\n\t{\n\t\t// Update the timestamp for when we last received data.\n\t\tthis.lastReceivedTimestamp = Date.now();\n\n\t\t// Emit a notification indicating that the connection has received data.\n\t\tthis.emit('received');\n\n\t\t// Clear and remove all verification timers.\n\t\tthis.verifications.forEach((timer) => clearTimeout(timer));\n\t\tthis.verifications.length = 0;\n\n\t\t// Add the message to the current message buffer.\n\t\tthis.messageBuffer += data;\n\n\t\t// Check if the new message buffer contains the statement delimiter.\n\t\twhile(this.messageBuffer.includes(ElectrumProtocol.statementDelimiter))\n\t\t{\n\t\t\t// Split message buffer into statements.\n\t\t\tconst statementParts = this.messageBuffer.split(ElectrumProtocol.statementDelimiter);\n\n\t\t\t// For as long as we still have statements to parse..\n\t\t\twhile(statementParts.length > 1)\n\t\t\t{\n\t\t\t\t// Move the first statement to its own variable.\n\t\t\t\tconst currentStatementList = String(statementParts.shift());\n\n\t\t\t\t// Parse the statement into an object or list of objects.\n\t\t\t\tlet statementList = parse(currentStatementList, null, this.options.useBigInt ? parseNumberAndBigInt : parseFloat) as RPCResponse | RPCResponse[];\n\n\t\t\t\t// Wrap the statement in an array if it is not already a batched statement list.\n\t\t\t\tif(!Array.isArray(statementList))\n\t\t\t\t{\n\t\t\t\t\tstatementList = [ statementList ];\n\t\t\t\t}\n\n\t\t\t\t// For as long as there is statements in the result set..\n\t\t\t\twhile(statementList.length > 0)\n\t\t\t\t{\n\t\t\t\t\t// Move the first statement from the batch to its own variable.\n\t\t\t\t\tconst currentStatement = statementList.shift();\n\n\t\t\t\t\t// If the current statement is a subscription notification..\n\t\t\t\t\tif(isRPCNotification(currentStatement))\n\t\t\t\t\t{\n\t\t\t\t\t\t// Emit the notification for handling higher up in the stack.\n\t\t\t\t\t\tthis.emit('response', currentStatement);\n\n\t\t\t\t\t\t// Consider this statement handled.\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the current statement is a version negotiation response..\n\t\t\t\t\tif(currentStatement.id === 'versionNegotiation')\n\t\t\t\t\t{\n\t\t\t\t\t\tif(isRPCErrorResponse(currentStatement))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// Then emit a failed version negotiation response signal.\n\t\t\t\t\t\t\tthis.emit('version', { error: currentStatement.error });\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// Extract the software and protocol version reported.\n\t\t\t\t\t\t\tconst [ software, protocol ] = currentStatement.result;\n\n\t\t\t\t\t\t\t// Emit a successful version negotiation response signal.\n\t\t\t\t\t\t\tthis.emit('version', { software, protocol });\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Consider this statement handled.\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the current statement is a keep-alive response..\n\t\t\t\t\tif(currentStatement.id === 'keepAlive')\n\t\t\t\t\t{\n\t\t\t\t\t\t// Do nothing and consider this statement handled.\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Emit the statements for handling higher up in the stack.\n\t\t\t\t\tthis.emit('response', currentStatement);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Store the remaining statement as the current message buffer.\n\t\t\tthis.messageBuffer = statementParts.shift() || '';\n\t\t}\n\t}\n\n\t/**\n\t * Sends a keep-alive message to the host.\n\t *\n\t * @returns true if the ping message was fully flushed to the socket, false if\n\t * part of the message is queued in the user memory\n\t */\n\tping(): boolean\n\t{\n\t\t// Write a log message.\n\t\tdebug.ping(`Sending keep-alive ping to '${this.hostIdentifier}'`);\n\n\t\t// Craft a keep-alive message.\n\t\tconst message = ElectrumProtocol.buildRequestObject('server.ping', [], 'keepAlive');\n\n\t\t// Send the keep-alive message.\n\t\tconst status = this.send(message);\n\n\t\t// Return the ping status.\n\t\treturn status;\n\t}\n\n\t/**\n\t * Initiates the network connection negotiates a protocol version. Also emits the 'connect' signal if successful.\n\t *\n\t * @throws {Error} if the socket connection fails.\n\t * @returns a promise resolving when the connection is established\n\t */\n\tasync connect(): Promise<void>\n\t{\n\t\t// If we are already connected return true.\n\t\tif(this.status === ConnectionStatus.CONNECTED)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t// Indicate that the connection is connecting\n\t\tthis.status = ConnectionStatus.CONNECTING;\n\n\t\t// Emit a connect event now that the connection is being set up.\n\t\tthis.emit('connecting');\n\n\t\t// Create a function that will resolve once the connection is established.\n\t\t// The connection is established through onSocketConnect\n\t\tconst connectionResolver = (resolve: ResolveFunction<void>, reject: RejectFunction): void =>\n\t\t{\n\t\t\t// Resolve the connection promise once the connection is established. This event is emitted by onSocketConnect.\n\t\t\tthis.once('connected', () =>\n\t\t\t{\n\t\t\t\t// Remove the listener for the disconnected event.\n\t\t\t\tthis.removeListener('disconnected', reject);\n\n\t\t\t\t// Resolve the connection promise.\n\t\t\t\tresolve();\n\t\t\t});\n\n\t\t\t// Reject the connection promise if the connection is disconnected.\n\t\t\tthis.once('disconnected', () =>\n\t\t\t{\n\t\t\t\t// Remove the listener for the connected event.\n\t\t\t\tthis.removeListener('connected', resolve);\n\n\t\t\t\t// Reject the connection promise.\n\t\t\t\treject();\n\t\t\t});\n\n\t\t\t// Start the socket connection process.\n\t\t\tthis.socket.connect();\n\t\t};\n\n\t\t// Wait until connection is established and version negotiation succeeds.\n\t\tawait new Promise<void>(connectionResolver);\n\t}\n\n\t/**\n\t * Restores the network connection.\n\t */\n\tasync reconnect(): Promise<void>\n\t{\n\t\t// If a reconnect timer is set, remove it\n\t\tawait this.clearReconnectTimer();\n\n\t\t// Write a log message.\n\t\tdebug.network(`Trying to reconnect to '${this.hostIdentifier}'..`);\n\n\t\t// Set the status to reconnecting for more accurate log messages.\n\t\tthis.status = ConnectionStatus.RECONNECTING;\n\n\t\t// Emit a connect event now that the connection is usable.\n\t\tthis.emit('reconnecting');\n\n\t\t// Disconnect the underlying socket\n\t\tthis.socket.disconnect();\n\n\t\ttry\n\t\t{\n\t\t\t// Try to connect again.\n\t\t\tawait this.connect();\n\t\t}\n\t\tcatch (_error)\n\t\t{\n\t\t\t// Do nothing as the error should be handled via the disconnect and error signals.\n\t\t}\n\t}\n\n\t/**\n\t * Removes the current reconnect timer.\n\t */\n\tclearReconnectTimer(): void\n\t{\n\t\t// If a reconnect timer is set, remove it\n\t\tif(this.reconnectTimer)\n\t\t{\n\t\t\tclearTimeout(this.reconnectTimer);\n\t\t}\n\n\t\t// Reset the timer reference.\n\t\tthis.reconnectTimer = undefined;\n\t}\n\n\t/**\n\t * Removes the current keep-alive timer.\n\t */\n\tclearKeepAliveTimer(): void\n\t{\n\t\t// If a keep-alive timer is set, remove it\n\t\tif(this.keepAliveTimer)\n\t\t{\n\t\t\tclearTimeout(this.keepAliveTimer);\n\t\t}\n\n\t\t// Reset the timer reference.\n\t\tthis.keepAliveTimer = undefined;\n\t}\n\n\t/**\n\t * Initializes the keep alive timer loop.\n\t */\n\tsetupKeepAliveTimer(): void\n\t{\n\t\t// If the keep-alive timer loop is not currently set up..\n\t\tif(!this.keepAliveTimer)\n\t\t{\n\t\t\t// Set a new keep-alive timer.\n\t\t\tthis.keepAliveTimer = setTimeout(this.ping.bind(this), this.options.sendKeepAliveIntervalInMilliSeconds) as unknown as number;\n\t\t}\n\t}\n\n\t/**\n\t * Tears down the current connection and removes all event listeners on disconnect.\n\t *\n\t * @param force - disconnect even if the connection has not been fully established yet.\n\t * @param intentional - update connection state if disconnect is intentional.\n\t *\n\t * @returns true if successfully disconnected, or false if there was no connection.\n\t */\n\tasync disconnect(force: boolean = false, intentional: boolean = true): Promise<boolean>\n\t{\n\t\t// Return early when there is nothing to disconnect from\n\t\tif(this.status === ConnectionStatus.DISCONNECTED && !force)\n\t\t{\n\t\t\t// Return false to indicate that there was nothing to disconnect from.\n\t\t\treturn false;\n\t\t}\n\n\t\t// Update connection state if the disconnection is intentional.\n\t\t// NOTE: The state is meant to represent what the client is requesting, but\n\t\t// is used internally to handle visibility changes in browsers to ensure functional reconnection.\n\t\tif(intentional)\n\t\t{\n\t\t\t// Set connection status to null to indicate tear-down is currently happening.\n\t\t\tthis.status = ConnectionStatus.DISCONNECTING;\n\t\t}\n\n\t\t// Emit a connect event to indicate that we are disconnecting.\n\t\tthis.emit('disconnecting');\n\n\t\t// If a keep-alive timer is set, remove it.\n\t\tawait this.clearKeepAliveTimer();\n\n\t\t// If a reconnect timer is set, remove it\n\t\tawait this.clearReconnectTimer();\n\n\t\tconst disconnectResolver = (resolve: ResolveFunction<boolean>): void =>\n\t\t{\n\t\t\t// Resolve to true after the connection emits a disconnect\n\t\t\tthis.once('disconnected', () => resolve(true));\n\n\t\t\t// Close the connection on the socket level.\n\t\t\tthis.socket.disconnect();\n\t\t};\n\n\t\t// Return true to indicate that we disconnected.\n\t\treturn new Promise<boolean>(disconnectResolver);\n\t}\n\n\t/**\n\t * Sends an arbitrary message to the server.\n\t *\n\t * @param message - json encoded request object to send to the server, as a string.\n\t *\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\tsend(message: string): boolean\n\t{\n\t\t// Remove the current keep-alive timer if it exists.\n\t\tthis.clearKeepAliveTimer();\n\n\t\t// Get the current timestamp in milliseconds.\n\t\tconst currentTime = Date.now();\n\n\t\t// Follow up and verify that the message got sent..\n\t\tconst verificationTimer = setTimeout(this.verifySend.bind(this, currentTime), this.socket.timeout) as unknown as number;\n\n\t\t// Store the verification timer locally so that it can be cleared when data has been received.\n\t\tthis.verifications.push(verificationTimer);\n\n\t\t// Set a new keep-alive timer.\n\t\tthis.setupKeepAliveTimer();\n\n\t\t// Write the message to the network socket.\n\t\treturn this.socket.write(message + ElectrumProtocol.statementDelimiter);\n\t}\n\n\t// --- Event managers. --- //\n\n\t/**\n\t * Marks the connection as timed out and schedules reconnection if we have not\n\t * received data within the expected time frame.\n\t */\n\tverifySend(sentTimestamp: number): void\n\t{\n\t\t// If we haven't received any data since we last sent data out..\n\t\tif(Number(this.lastReceivedTimestamp) < sentTimestamp)\n\t\t{\n\t\t\t// If this connection is already disconnected, we do not change anything\n\t\t\tif((this.status === ConnectionStatus.DISCONNECTED) || (this.status === ConnectionStatus.DISCONNECTING))\n\t\t\t{\n\t\t\t\t// debug.warning(`Tried to verify already disconnected connection to '${this.hostIdentifier}'`);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Remove the current keep-alive timer if it exists.\n\t\t\tthis.clearKeepAliveTimer();\n\n\t\t\t// Write a notification to the logs.\n\t\t\tdebug.network(`Connection to '${this.hostIdentifier}' timed out.`);\n\n\t\t\t// Close the connection to avoid re-use.\n\t\t\t// NOTE: This initiates reconnection routines if the connection has not\n\t\t\t// been marked as intentionally disconnected.\n\t\t\tthis.socket.disconnect();\n\t\t}\n\t}\n\n\t/**\n\t * Updates the connection status when a connection is confirmed.\n\t */\n\tasync onSocketConnect(): Promise<void>\n\t{\n\t\t// If a reconnect timer is set, remove it.\n\t\tthis.clearReconnectTimer();\n\n\t\t// Set up the initial timestamp for when we last received data from the server.\n\t\tthis.lastReceivedTimestamp = Date.now();\n\n\t\t// Set up the initial keep-alive timer.\n\t\tthis.setupKeepAliveTimer();\n\t\t\n\t\t// Wait for the version to be negotiated.\n\t\tawait new Promise<void>(this.negotiateVersion.bind(this));\n\n\t\t// Emit a connect event now that the connection is established and version negotiated.\n\t\tthis.emit('connected');\n\n\t\t// Clear all temporary error listeners.\n\t\tthis.socket.removeAllListeners('error');\n\n\t\t// Set up handler for network errors.\n\t\tthis.socket.on('error', this.onSocketError.bind(this));\n\t}\n\n\t/**\n\t * Updates the connection status when a connection is ended.\n\t */\n\tonSocketDisconnect(): void\n\t{\n\t\t// Remove the current keep-alive timer if it exists.\n\t\tthis.clearKeepAliveTimer();\n\n\t\t// If this is a connection we're trying to tear down..\n\t\tif(this.status === ConnectionStatus.DISCONNECTING)\n\t\t{\n\t\t\t// Mark the connection as disconnected.\n\t\t\tthis.status = ConnectionStatus.DISCONNECTED;\n\n\t\t\t// Send a disconnect signal higher up the stack.\n\t\t\tthis.emit('disconnected');\n\n\t\t\t// If a reconnect timer is set, remove it.\n\t\t\tthis.clearReconnectTimer();\n\n\t\t\t// Remove all event listeners\n\t\t\tthis.removeAllListeners();\n\n\t\t\t// Write a log message.\n\t\t\tdebug.network(`Disconnected from '${this.hostIdentifier}'.`);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// If this is for an established connection..\n\t\t\tif(this.status === ConnectionStatus.CONNECTED)\n\t\t\t{\n\t\t\t\t// Write a notification to the logs.\n\t\t\t\tdebug.errors(`Connection with '${this.hostIdentifier}' was closed, trying to reconnect in ${this.options.reconnectAfterMilliSeconds / 1000} seconds.`);\n\t\t\t}\n\t\t\t// If this is a connection that is currently connecting, reconnecting or already disconnected..\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Do nothing\n\n\t\t\t\t// NOTE: This error message is useful during manual debugging of reconnections.\n\t\t\t\t// debug.errors(`Lost connection with reconnecting or already disconnected server '${this.hostIdentifier}'.`);\n\t\t\t}\n\n\t\t\t// Mark the connection as disconnected for now..\n\t\t\tthis.status = ConnectionStatus.DISCONNECTED;\n\n\t\t\t// Send a disconnect signal higher up the stack.\n\t\t\tthis.emit('disconnected');\n\n\t\t\t// If we don't have a pending reconnection timer..\n\t\t\tif(!this.reconnectTimer)\n\t\t\t{\n\t\t\t\t// Attempt to reconnect after one keep-alive duration.\n\t\t\t\tthis.reconnectTimer = setTimeout(this.reconnect.bind(this), this.options.reconnectAfterMilliSeconds) as unknown as number;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Notify administrator of any unexpected errors.\n\t */\n\tonSocketError(error: unknown | undefined): void\n\t{\n\t\t// Report a generic error if no error information is present.\n\t\t// NOTE: When using WSS, the error event explicitly\n\t\t// only allows to send a \"simple\" event without data.\n\t\t// https://stackoverflow.com/a/18804298\n\t\tif(typeof error === 'undefined')\n\t\t{\n\t\t\t// Do nothing, and instead rely on the socket disconnect event for further information.\n\t\t\treturn;\n\t\t}\n\n\t\t// Log the error, as there is nothing we can do to actually handle it.\n\t\tdebug.errors(`Network error ('${this.hostIdentifier}'): `, error);\n\t}\n\n\t/**\n\t * Negotiate the protocol version with the server.\n\t * Disconnect the connection if the version negotiation fails.\n\t * @param resolve \n\t * @param reject \n\t */\n\tasync negotiateVersion(resolve: ResolveFunction<void>, reject: RejectFunction): Promise<void>\n\t{\n\t\tconst rejector = (error: Error): void =>\n\t\t{\n\t\t\t// Set the status back to disconnected\n\t\t\tthis.status = ConnectionStatus.DISCONNECTED;\n\n\t\t\t// Emit a connect event indicating that we failed to connect.\n\t\t\tthis.emit('disconnected');\n\n\t\t\t// Reject with the error as reason\n\t\t\treject(error);\n\t\t};\n\n\t\t// Write a log message to show that we have started version negotiation.\n\t\tdebug.network(`Requesting protocol version ${this.version} with '${this.hostIdentifier}'.`);\n\t\t\t\t\n\t\t// Add error handler for one-time error.\n\t\tthis.socket.once('error', rejector);\n\n\t\t// Build a version negotiation message.\n\t\tconst versionMessage = ElectrumProtocol.buildRequestObject('server.version', [ this.application, this.version ], 'versionNegotiation');\n\n\t\t// Define a function to wrap version validation as a function.\n\t\tconst versionValidator = (version: VersionNegotiationResponse): void =>\n\t\t{\n\t\t\t// Check if version negotiation failed.\n\t\t\tif(isVersionRejected(version))\n\t\t\t{\n\t\t\t\t// Disconnect from the host.\n\t\t\t\tthis.disconnect(true);\n\n\t\t\t\t// Declare an error message.\n\t\t\t\tconst errorMessage = 'unsupported protocol version.';\n\n\t\t\t\t// Log the error.\n\t\t\t\tdebug.errors(`Failed to connect with ${this.hostIdentifier} due to ${errorMessage}`);\n\n\t\t\t\t// Reject the connection with false since version negotiation failed.\n\t\t\t\treject(errorMessage);\n\t\t\t}\n\t\t\t// Check if the host supports our requested protocol version.\n\t\t\t// NOTE: the server responds with version numbers that truncate 0's, so 1.5.0 turns into 1.5.\n\t\t\telse if((version.protocol !== this.version) && (`${version.protocol}.0` !== this.version) && (`${version.protocol}.0.0` !== this.version))\n\t\t\t{\n\t\t\t\t// Disconnect from the host.\n\t\t\t\tthis.disconnect(true);\n\n\t\t\t\t// Declare an error message.\n\t\t\t\tconst errorMessage = `incompatible protocol version negotiated (${version.protocol} !== ${this.version}).`;\n\n\t\t\t\t// Log the error.\n\t\t\t\tdebug.errors(`Failed to connect with ${this.hostIdentifier} due to ${errorMessage}`);\n\n\t\t\t\t// Reject the connection with false since version negotiation failed.\n\t\t\t\treject(errorMessage);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Write a log message.\n\t\t\t\tdebug.network(`Negotiated protocol version ${version.protocol} with '${this.hostIdentifier}', powered by ${version.software}.`);\n\n\t\t\t\t// Set connection status to connected\n\t\t\t\tthis.status = ConnectionStatus.CONNECTED;\n\n\t\t\t\t// Resolve the connection promise since we successfully connected and negotiated protocol version.\n\t\t\t\tresolve();\n\t\t\t}\n\t\t};\n\n\t\t// Listen for version negotiation once.\n\t\tthis.once('version', versionValidator);\n\n\t\t// Send the version negotiation message.\n\t\tthis.send(versionMessage);\n\t}\n}\n","import type { ElectrumNetworkOptions } from './interfaces.ts';\n\n// Define number of milliseconds per second for legibility.\nconst MILLI_SECONDS_PER_SECOND = 1000;\n\n/**\n * Configure default options.\n */\nexport const defaultNetworkOptions: ElectrumNetworkOptions =\n{\n\t// By default, all numbers including integers are parsed as regular JavaScript numbers.\n\tuseBigInt: false,\n\n\t// Send a ping message every seconds, to detect network problem as early as possible.\n\tsendKeepAliveIntervalInMilliSeconds: 1 * MILLI_SECONDS_PER_SECOND,\n\n\t// Try to reconnect 5 seconds after unintentional disconnects.\n\treconnectAfterMilliSeconds: 5 * MILLI_SECONDS_PER_SECOND,\n\n\t// Try to detect stale connections 5 seconds after every send.\n\tverifyConnectionTimeoutInMilliSeconds: 5 * MILLI_SECONDS_PER_SECOND,\n\n\t// Automatically manage the connection for a consistent behavior across browsers and devices.\n\tdisableBrowserVisibilityHandling: false,\n\tdisableBrowserConnectivityHandling: false,\n};\n","import debug from '@electrum-cash/debug-logs';\nimport { ElectrumConnection } from './electrum-connection.ts';\nimport { ElectrumProtocol } from './electrum-protocol.ts';\nimport { defaultNetworkOptions } from './constants.ts';\nimport { ConnectionStatus } from './enums.ts';\nimport { EventEmitter } from 'eventemitter3';\nimport { Mutex } from 'async-mutex';\nimport { isRPCNotification, isRPCErrorResponse } from './rpc-interfaces.ts';\nimport type { RPCParameter, RPCNotification, RPCResponse } from './rpc-interfaces.ts';\nimport type { ElectrumNetworkOptions, ElectrumClientEvents, ElectrumSocket, ResolveFunction, RequestResolver, RequestResponse } from './interfaces.ts';\n\n/**\n * High-level Electrum client that lets applications send requests and subscribe to notification events from a server.\n */\nclass ElectrumClient<ElectrumEvents extends ElectrumClientEvents> extends EventEmitter<ElectrumClientEvents | ElectrumEvents> implements ElectrumClientEvents\n{\n\t/**\n\t * The name and version of the server software indexing the blockchain.\n\t */\n\tpublic software: string;\n\n\t/**\n\t * The genesis hash of the blockchain indexed by the server.\n\t * @remarks This is only available after a 'server.features' call.\n\t */\n\tpublic genesisHash: string;\n\n\t/**\n\t * The chain height of the blockchain indexed by the server.\n\t * @remarks This is only available after a 'blockchain.headers.subscribe' call.\n\t */\n\tpublic chainHeight: number;\n\n\t/**\n\t * Timestamp of when we last received data from the server indexing the blockchain.\n\t */\n\tpublic lastReceivedTimestamp: number;\n\n\t/**\n\t * Number corresponding to the underlying connection status.\n\t */\n\tpublic get status(): ConnectionStatus\n\t{\n\t\treturn this.connection.status;\n\t}\n\n\t// Declare instance variables\n\tprivate connection: ElectrumConnection;\n\n\t// Initialize an empty list of subscription metadata.\n\tprivate subscriptionMethods: Record<string, Set<string>> = {};\n\n\t// Start counting the request IDs from 0\n\tprivate requestId = 0;\n\n\t// Initialize an empty dictionary for keeping track of request resolvers\n\tprivate requestResolvers: { [index: number]: RequestResolver } = {};\n\n\t// Mutex lock used to prevent simultaneous connect() and disconnect() calls.\n\tprivate connectionLock = new Mutex();\n\n\t/**\n\t * Initializes an Electrum client.\n\t *\n\t * @param application - your application name, used to identify to the electrum host.\n\t * @param version - protocol version to use with the host.\n\t * @param socketOrHostname - pre-configured electrum socket or fully qualified domain name or IP number of the host\n\t * @param options - ...\n\t *\n\t * @throws {Error} if `version` is not a valid version string.\n\t */\n\tconstructor(\n\t\tpublic application: string,\n\t\tpublic version: string,\n\t\tpublic socketOrHostname: ElectrumSocket | string,\n\t\tpublic options: ElectrumNetworkOptions = {},\n\t)\n\t{\n\t\t// Initialize the event emitter.\n\t\tsuper();\n\n\t\t// Update default options with the provided values.\n\t\tconst networkOptions: ElectrumNetworkOptions = { ...defaultNetworkOptions, ...options };\n\n\t\t// Set up a connection to an electrum server.\n\t\tthis.connection = new ElectrumConnection(application, version, socketOrHostname, networkOptions);\n\t}\n\n\t// Expose hostIdentifier from the connection.\n\tget hostIdentifier(): string\n\t{\n\t\treturn this.connection.hostIdentifier;\n\t}\n\n\t// Expose port from the connection.\n\tget encrypted(): boolean\n\t{\n\t\treturn this.connection.encrypted;\n\t}\n\n\t/**\n\t * Connects to the remote server.\n\t *\n\t * @throws {Error} if the socket connection fails.\n\t * @returns a promise resolving when the connection is established.\n\t */\n\tasync connect(): Promise<void>\n\t{\n\t\t// Create a lock so that multiple connects/disconnects cannot race each other.\n\t\tconst unlock = await this.connectionLock.acquire();\n\n\t\ttry\n\t\t{\n\t\t\t// If we are already connected, do not attempt to connect again.\n\t\t\tif(this.connection.status === ConnectionStatus.CONNECTED)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Listen for parsed statements.\n\t\t\tthis.connection.on('response', this.response.bind(this));\n\n\t\t\t// Hook up handles for the connected and disconnected events.\n\t\t\tthis.connection.on('connected', this.resubscribeOnConnect.bind(this));\n\t\t\tthis.connection.on('disconnected', this.onConnectionDisconnect.bind(this));\n\n\t\t\t// Relay connecting and reconnecting events.\n\t\t\tthis.connection.on('connecting', this.handleConnectionStatusChanges.bind(this, 'connecting'));\n\t\t\tthis.connection.on('disconnecting', this.handleConnectionStatusChanges.bind(this, 'disconnecting'));\n\t\t\tthis.connection.on('reconnecting', this.handleConnectionStatusChanges.bind(this, 'reconnecting'));\n\n\t\t\t// Hook up client metadata gathering functions.\n\t\t\tthis.connection.on('version', this.storeSoftwareVersion.bind(this));\n\t\t\tthis.connection.on('received', this.updateLastReceivedTimestamp.bind(this));\n\n\t\t\t// Relay error events.\n\t\t\tthis.connection.on('error', this.emit.bind(this, 'error'));\n\n\t\t\t// Connect with the server.\n\t\t\tawait this.connection.connect();\n\t\t}\n\t\t// Always release our lock so that we do not end up in a stuck-state.\n\t\tfinally\n\t\t{\n\t\t\tunlock();\n\t\t}\n\t}\n\n\t/**\n\t * Disconnects from the remote server and removes all event listeners/subscriptions and open requests.\n\t *\n\t * @param force - disconnect even if the connection has not been fully established yet.\n\t * @param retainSubscriptions - retain subscription data so they will be restored on reconnection.\n\t *\n\t * @returns true if successfully disconnected, or false if there was no connection.\n\t */\n\tasync disconnect(force: boolean = false, retainSubscriptions: boolean = false): Promise<boolean>\n\t{\n\t\tif(!retainSubscriptions)\n\t\t{\n\t\t\t// Cancel all event listeners.\n\t\t\tthis.removeAllListeners();\n\n\t\t\t// Remove all subscription data\n\t\t\tthis.subscriptionMethods = {};\n\t\t}\n\n\t\t// Disconnect from the remote server.\n\t\treturn this.connection.disconnect(force);\n\t}\n\n\t/**\n\t * Calls a method on the remote server with the supplied parameters.\n\t *\n\t * @param method - name of the method to call.\n\t * @param parameters - one or more parameters for the method.\n\t *\n\t * @throws {Error} if the client is disconnected.\n\t * @returns a promise that resolves with the result of the method or an Error.\n\t */\n\tasync request(method: string, ...parameters: RPCParameter[]): Promise<Error | RequestResponse>\n\t{\n\t\t// If we are not connected to a server..\n\t\tif(this.connection.status !== ConnectionStatus.CONNECTED)\n\t\t{\n\t\t\t// Reject the request with a disconnected error message.\n\t\t\tthrow(new Error(`Unable to send request to a disconnected server '${this.hostIdentifier}'.`));\n\t\t}\n\n\t\t// Increase the request ID by one.\n\t\tthis.requestId += 1;\n\n\t\t// Store a copy of the request id.\n\t\tconst id = this.requestId;\n\n\t\t// Format the arguments as an electrum request object.\n\t\tconst message = ElectrumProtocol.buildRequestObject(method, parameters, id);\n\n\t\t// Define a function to wrap the request in a promise.\n\t\tconst requestResolver = (resolve: ResolveFunction<Error | RequestResponse>): void =>\n\t\t{\n\t\t\t// Add a request resolver for this promise to the list of requests.\n\t\t\tthis.requestResolvers[id] = (error?: Error, data?: RequestResponse) =>\n\t\t\t{\n\t\t\t\t// If the resolution failed..\n\t\t\t\tif(error)\n\t\t\t\t{\n\t\t\t\t\t// Resolve the promise with the error for the application to handle.\n\t\t\t\t\tresolve(error);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// Resolve the promise with the request results.\n\t\t\t\t\tresolve(data);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// Send the request message to the remote server.\n\t\t\tthis.connection.send(message);\n\t\t};\n\n\t\t// Write a log message.\n\t\tdebug.network(`Sending request '${method}' to '${this.hostIdentifier}'`);\n\n\t\t// return a promise to deliver results later.\n\t\treturn new Promise<Error | RequestResponse>(requestResolver);\n\t}\n\n\t/**\n\t * Subscribes to the method and payload at the server.\n\t *\n\t * @remarks the response for the subscription request is issued as a notification event.\n\t *\n\t * @param method - one of the subscribable methods the server supports.\n\t * @param parameters - one or more parameters for the method.\n\t *\n\t * @throws {Error} if the client is disconnected.\n\t * @returns a promise resolving when the subscription is established.\n\t */\n\tasync subscribe(method: string, ...parameters: RPCParameter[]): Promise<void>\n\t{\n\t\t// Initialize an empty list of subscription payloads, if needed.\n\t\tif(!this.subscriptionMethods[method])\n\t\t{\n\t\t\tthis.subscriptionMethods[method] = new Set<string>();\n\t\t}\n\n\t\t// Store the subscription parameters to track what data we have subscribed to.\n\t\tthis.subscriptionMethods[method].add(JSON.stringify(parameters));\n\n\t\t// Send initial subscription request.\n\t\tconst requestData = await this.request(method, ...parameters);\n\n\t\t// If the request failed, throw it as an error.\n\t\tif(requestData instanceof Error)\n\t\t{\n\t\t\tthrow(requestData);\n\t\t}\n\n\t\t// If the request returned more than one data point..\n\t\tif(Array.isArray(requestData))\n\t\t{\n\t\t\t// .. throw an error, as this breaks our expectation for subscriptions.\n\t\t\tthrow(new Error('Subscription request returned an more than one data point.'));\n\t\t}\n\n\t\t// Construct a notification structure to package the initial result as a notification.\n\t\tconst notification: RPCNotification =\n\t\t{\n\t\t\tjsonrpc: '2.0',\n\t\t\tmethod: method,\n\t\t\tparams: [ ...parameters, requestData ],\n\t\t};\n\n\t\t// Manually emit an event for the initial response.\n\t\tthis.emit('notification', notification);\n\n\t\t// Try to update the chain height.\n\t\tthis.updateChainHeightFromHeadersNotifications(notification);\n\t}\n\n\t/**\n\t * Unsubscribes to the method at the server and removes any callback functions\n\t * when there are no more subscriptions for the method.\n\t *\n\t * @param method - a previously subscribed to method.\n\t * @param parameters - one or more parameters for the method.\n\t *\n\t * @throws {Error} if no subscriptions exist for the combination of the provided `method` and `parameters.\n\t * @throws {Error} if the client is disconnected.\n\t * @returns a promise resolving when the subscription is removed.\n\t */\n\tasync unsubscribe(method: string, ...parameters: RPCParameter[]): Promise<void>\n\t{\n\t\t// Throw an error if the client is disconnected.\n\t\tif(this.connection.status !== ConnectionStatus.CONNECTED)\n\t\t{\n\t\t\tthrow(new Error(`Unable to send unsubscribe request to a disconnected server '${this.hostIdentifier}'.`));\n\t\t}\n\n\t\t// If this method has no subscriptions..\n\t\tif(!this.subscriptionMethods[method])\n\t\t{\n\t\t\t// Reject this promise with an explanation.\n\t\t\tthrow(new Error(`Cannot unsubscribe from '${method}' since the method has no subscriptions.`));\n\t\t}\n\n\t\t// Pack up the parameters as a long string.\n\t\tconst subscriptionParameters = JSON.stringify(parameters);\n\n\t\t// If the method payload could not be located..\n\t\tif(!this.subscriptionMethods[method].has(subscriptionParameters))\n\t\t{\n\t\t\t// Reject this promise with an explanation.\n\t\t\tthrow(new Error(`Cannot unsubscribe from '${method}' since it has no subscription with the given parameters.`));\n\t\t}\n\n\t\t// Remove this specific subscription payload from internal tracking.\n\t\tthis.subscriptionMethods[method].delete(subscriptionParameters);\n\n\t\t// Send unsubscription request to the server\n\t\t// NOTE: As a convenience we allow users to define the method as the subscribe or unsubscribe version.\n\t\tawait this.request(method.replace('.subscribe', '.unsubscribe'), ...parameters);\n\n\t\t// Write a log message.\n\t\tdebug.client(`Unsubscribed from '${String(method)}' for the '${subscriptionParameters}' parameters.`);\n\t}\n\n\t/**\n\t * Restores existing subscriptions without updating status or triggering manual callbacks.\n\t *\n\t * @throws {Error} if subscription data cannot be found for all stored event names.\n\t * @throws {Error} if the client is disconnected.\n\t * @returns a promise resolving to true when the subscriptions are restored.\n\t *\n\t * @ignore\n\t */\n\tprivate async resubscribeOnConnect(): Promise<void>\n\t{\n\t\t// Write a log message.\n\t\tdebug.client(`Connected to '${this.hostIdentifier}'.`);\n\n\t\t// Synchronize with the underlying connection status.\n\t\tthis.handleConnectionStatusChanges('connected');\n\n\t\t// Initialize an empty list of resubscription promises.\n\t\tconst resubscriptionPromises = [];\n\n\t\t// For each method we have a subscription for..\n\t\tfor(const method in this.subscriptionMethods)\n\t\t{\n\t\t\t// .. and for each parameter we have previously been subscribed to..\n\t\t\tfor(const parameterJSON of this.subscriptionMethods[method].values())\n\t\t\t{\n\t\t\t\t// restore the parameters from JSON.\n\t\t\t\tconst parameters = JSON.parse(parameterJSON);\n\n\t\t\t\t// Send a subscription request.\n\t\t\t\tresubscriptionPromises.push(this.subscribe(method, ...parameters));\n\t\t\t}\n\n\t\t\t// Wait for all re-subscriptions to complete.\n\t\t\tawait Promise.all(resubscriptionPromises);\n\t\t}\n\n\t\t// Write a log message if there was any subscriptions to restore.\n\t\tif(resubscriptionPromises.length > 0)\n\t\t{\n\t\t\tdebug.client(`Restored ${resubscriptionPromises.length} previous subscriptions for '${this.hostIdentifier}'`);\n\t\t}\n\t}\n\n\t/**\n\t * Parser messages from the remote server to resolve request promises and emit subscription events.\n\t *\n\t * @param message - the response message\n\t *\n\t * @throws {Error} if the message ID does not match an existing request.\n\t * @ignore\n\t */\n\tresponse(message: RPCResponse): void\n\t{\n\t\t// If the received message is a notification, we forward it to all event listeners\n\t\tif(isRPCNotification(message))\n\t\t{\n\t\t\t// Write a log message.\n\t\t\tdebug.client(`Received notification for '${message.method}' from '${this.hostIdentifier}'`);\n\n\t\t\t// Forward the message content to all event listeners.\n\t\t\tthis.emit('notification', message);\n\n\t\t\t// Try to update the chain height.\n\t\t\tthis.updateChainHeightFromHeadersNotifications(message);\n\n\t\t\t// Return since it does not have an associated request resolver\n\t\t\treturn;\n\t\t}\n\n\t\t// If the response ID is null we cannot use it to index our request resolvers\n\t\tif(message.id === null)\n\t\t{\n\t\t\t// Throw an internal error, this should not happen.\n\t\t\tthrow(new Error('Internal error: Received an RPC response with ID null.'));\n\t\t}\n\n\t\t// Look up which request promise we should resolve this.\n\t\tconst requestResolver = this.requestResolvers[message.id];\n\n\t\t// If we do not have a request resolver for this response message..\n\t\tif(!requestResolver)\n\t\t{\n\t\t\t// Log that a message was ignored since the request has already been rejected.\n\t\t\tdebug.warning(`Ignoring response #${message.id} as the request has already been rejected.`);\n\n\t\t\t// Return as this has now been fully handled.\n\t\t\treturn;\n\t\t}\n\n\t\t// Remove the promise from the request list.\n\t\tdelete this.requestResolvers[message.id];\n\n\t\t// If the message contains an error..\n\t\tif(isRPCErrorResponse(message))\n\t\t{\n\t\t\t// Forward the message error to the request resolver and omit the `result` parameter.\n\t\t\trequestResolver(new Error(message.error.message));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Forward the message content to the request resolver and omit the `error` parameter\n\t\t\t// (by setting it to undefined).\n\t\t\trequestResolver(undefined, message.result);\n\n\t\t\t// Attempt to extract genesis hash from feature requests.\n\t\t\tthis.storeGenesisHashFromFeaturesResponse(message);\n\t\t}\n\t}\n\n\t/**\n\t * Callback function that is called when connection to the Electrum server is lost.\n\t * Aborts all active requests with an error message indicating that connection was lost.\n\t *\n\t * @ignore\n\t */\n\tasync onConnectionDisconnect(): Promise<void>\n\t{\n\t\t// Loop over active requests\n\t\tfor(const resolverId in this.requestResolvers)\n\t\t{\n\t\t\t// Extract request resolver for readability\n\t\t\tconst requestResolver = this.requestResolvers[resolverId];\n\n\t\t\t// Resolve the active request with an error indicating that the connection was lost.\n\t\t\trequestResolver(new Error('Connection lost'));\n\n\t\t\t// Remove the promise from the request list.\n\t\t\tdelete this.requestResolvers[resolverId];\n\t\t}\n\n\t\t// Synchronize with the underlying connection status.\n\t\tthis.handleConnectionStatusChanges('disconnected');\n\t}\n\n\t/**\n\t * Stores the server provider software version field on successful version negotiation.\n\t *\n\t * @ignore\n\t */\n\tasync storeSoftwareVersion(versionStatement): Promise<void>\n\t{\n\t\t// TODO: handle failed version negotiation better.\n\t\tif(versionStatement.error)\n\t\t{\n\t\t\t// Do nothing.\n\t\t\treturn;\n\t\t}\n\n\t\t// Store the software version.\n\t\tthis.software = versionStatement.software;\n\t}\n\n\t/**\n\t * Updates the last received timestamp.\n\t *\n\t * @ignore\n\t */\n\tasync updateLastReceivedTimestamp(): Promise<void>\n\t{\n\t\t// Update the timestamp for when we last received data.\n\t\tthis.lastReceivedTimestamp = Date.now();\n\t}\n\n\t/**\n\t * Checks if the provided message is a response to a headers subscription,\n\t * and if so updates the locally stored chain height value for this client.\n\t *\n\t * @ignore\n\t */\n\tasync updateChainHeightFromHeadersNotifications(message): Promise<void>\n\t{\n\t\t// If the message is a notification for a new chain height..\n\t\tif(message.method === 'blockchain.headers.subscribe')\n\t\t{\n\t\t\t// ..also store the updated chain height locally.\n\t\t\tthis.chainHeight = message.params[0].height;\n\t\t}\n\t}\n\n\t/**\n\t * Checks if the provided message is a response to a server.features request,\n\t * and if so stores the genesis hash for this client locally.\n\t *\n\t * @ignore\n\t */\n\tasync storeGenesisHashFromFeaturesResponse(message): Promise<void>\n\t{\n\t\ttry\n\t\t{\n\t\t\t// If the message is a response to a features request..\n\t\t\tif(typeof message.result.genesis_hash !== 'undefined')\n\t\t\t{\n\t\t\t\t// ..store the genesis hash locally.\n\t\t\t\tthis.genesisHash = message.result.genesis_hash;\n\t\t\t}\n\t\t}\n\t\tcatch (_ignored)\n\t\t{\n\t\t\t// Do nothing.\n\t\t}\n\t}\n\n\t/**\n\t * Helper function to synchronize state and events with the underlying connection.\n\t */\n\tasync handleConnectionStatusChanges(eventName): Promise<void>\n\t{\n\t\t// Re-emit the event.\n\t\tthis.emit(eventName);\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 connecting: [];\n\tpublic readonly connected: [];\n\tpublic readonly disconnecting: [];\n\tpublic readonly disconnected: [];\n\tpublic readonly reconnecting: [];\n\tpublic readonly notification: [ RPCNotification ];\n\tpublic readonly error: [ Error ];\n}\n\n// Export the client.\nexport default ElectrumClient;\n"],"mappings":";;;;;;;;;;;;AAOA,IAAa,mBAAb,MACA;;;;;;;;;;CAUC,OAAO,mBAAmB,QAAgB,YAA4B,WACtE;AAKC,SAAO,KAAK,UAAU;GAAU;GAAQ,QAAQ;GAAY,IAAI;GAAW,CAAC;;;;;;;CAQ7E,WAAW,gBACX;AACC,SAAO;;;;;;;CAQR,WAAW,qBACX;AACC,SAAO;;;;;;ACcT,MAAa,qBAAqB,SAAS,SAC3C;AACC,QAAO,QAAQ,WAAW,WAAW;;AAGtC,MAAa,iBAAiB,SAAS,SACvC;AACC,QAAO,QAAQ,WAAW,YAAY;;AAGvC,MAAa,oBAAoB,SAAS,SAC1C;AACC,QAAO,EAAE,QAAQ,YAAY,YAAY;;AAG1C,MAAa,eAAe,SAAS,SACrC;AACC,QAAO,QAAQ,WAAW,YAAY;;;;;;;;;;;;;;AClEvC,IAAY,gEAAL;AAEN;AACA;AACA;AACA;AACA;;;;;;;;;AC0PD,MAAa,oBAAoB,SAAS,QAC1C;AACC,QAAO,WAAW;;;;;AAMnB,MAAa,sBAAsB,SAAS,QAC5C;AACC,QAAO,cAAc,UAAU,cAAc;;;;;;;;ACrQ9C,IAAa,qBAAb,cAAwC,aACxC;CAEC,AAAO,SAA2B,iBAAiB;CAGnD,AAAQ;CAGR,AAAQ;CAGR,AAAQ;CACR,AAAQ;CAGR,AAAQ,gBAA+B,EAAE;CAGzC,AAAQ,gBAAgB;;;;;;;;;;;CAYxB,YACC,AAAQ,aACR,AAAQ,SACR,AAAQ,kBACR,AAAQ,SAET;AAEC,SAAO;EAPC;EACA;EACA;EACA;AAOR,MAAG,CAAC,iBAAiB,cAAc,KAAK,QAAQ,CAG/C,uBAAM,IAAI,MAAM,4BAA4B,QAAQ,2CAA2C;AAIhG,MAAG,OAAO,qBAAqB,SAG9B,MAAK,SAAS,IAAI,kBAAkB,kBAAkB,QAAW,QAAW,QAAW,KAAK,QAAQ;MAKpG,MAAK,SAAS;AAIf,OAAK,OAAO,GAAG,aAAa,KAAK,gBAAgB,KAAK,KAAK,CAAC;AAC5D,OAAK,OAAO,GAAG,gBAAgB,KAAK,mBAAmB,KAAK,KAAK,CAAC;AAGlE,OAAK,OAAO,GAAG,QAAQ,KAAK,kBAAkB,KAAK,KAAK,CAAC;;CAI1D,IAAI,iBACJ;AACC,SAAO,KAAK,OAAO;;CAIpB,IAAI,YACJ;AACC,SAAO,KAAK,OAAO;;;;;;;;;CAUpB,kBAAkB,MAClB;AAEC,OAAK,wBAAwB,KAAK,KAAK;AAGvC,OAAK,KAAK,WAAW;AAGrB,OAAK,cAAc,SAAS,UAAU,aAAa,MAAM,CAAC;AAC1D,OAAK,cAAc,SAAS;AAG5B,OAAK,iBAAiB;AAGtB,SAAM,KAAK,cAAc,SAAS,iBAAiB,mBAAmB,EACtE;GAEC,MAAM,iBAAiB,KAAK,cAAc,MAAM,iBAAiB,mBAAmB;AAGpF,UAAM,eAAe,SAAS,GAC9B;IAKC,IAAI,gBAAgB,MAHS,OAAO,eAAe,OAAO,CAAC,EAGX,MAAM,KAAK,QAAQ,YAAY,uBAAuB,WAAW;AAGjH,QAAG,CAAC,MAAM,QAAQ,cAAc,CAE/B,iBAAgB,CAAE,cAAe;AAIlC,WAAM,cAAc,SAAS,GAC7B;KAEC,MAAM,mBAAmB,cAAc,OAAO;AAG9C,SAAG,kBAAkB,iBAAiB,EACtC;AAEC,WAAK,KAAK,YAAY,iBAAiB;AAGvC;;AAID,SAAG,iBAAiB,OAAO,sBAC3B;AACC,UAAG,mBAAmB,iBAAiB,CAGtC,MAAK,KAAK,WAAW,EAAE,OAAO,iBAAiB,OAAO,CAAC;WAGxD;OAEC,MAAM,CAAE,UAAU,YAAa,iBAAiB;AAGhD,YAAK,KAAK,WAAW;QAAE;QAAU;QAAU,CAAC;;AAI7C;;AAID,SAAG,iBAAiB,OAAO,YAG1B;AAID,UAAK,KAAK,YAAY,iBAAiB;;;AAKzC,QAAK,gBAAgB,eAAe,OAAO,IAAI;;;;;;;;;CAUjD,OACA;AAEC,QAAM,KAAK,+BAA+B,KAAK,eAAe,GAAG;EAGjE,MAAM,UAAU,iBAAiB,mBAAmB,eAAe,EAAE,EAAE,YAAY;AAMnF,SAHe,KAAK,KAAK,QAAQ;;;;;;;;CAYlC,MAAM,UACN;AAEC,MAAG,KAAK,WAAW,iBAAiB,UAEnC;AAID,OAAK,SAAS,iBAAiB;AAG/B,OAAK,KAAK,aAAa;EAIvB,MAAM,sBAAsB,SAAgC,WAC5D;AAEC,QAAK,KAAK,mBACV;AAEC,SAAK,eAAe,gBAAgB,OAAO;AAG3C,aAAS;KACR;AAGF,QAAK,KAAK,sBACV;AAEC,SAAK,eAAe,aAAa,QAAQ;AAGzC,YAAQ;KACP;AAGF,QAAK,OAAO,SAAS;;AAItB,QAAM,IAAI,QAAc,mBAAmB;;;;;CAM5C,MAAM,YACN;AAEC,QAAM,KAAK,qBAAqB;AAGhC,QAAM,QAAQ,2BAA2B,KAAK,eAAe,KAAK;AAGlE,OAAK,SAAS,iBAAiB;AAG/B,OAAK,KAAK,eAAe;AAGzB,OAAK,OAAO,YAAY;AAExB,MACA;AAEC,SAAM,KAAK,SAAS;WAEd,QACP;;;;;CAQD,sBACA;AAEC,MAAG,KAAK,eAEP,cAAa,KAAK,eAAe;AAIlC,OAAK,iBAAiB;;;;;CAMvB,sBACA;AAEC,MAAG,KAAK,eAEP,cAAa,KAAK,eAAe;AAIlC,OAAK,iBAAiB;;;;;CAMvB,sBACA;AAEC,MAAG,CAAC,KAAK,eAGR,MAAK,iBAAiB,WAAW,KAAK,KAAK,KAAK,KAAK,EAAE,KAAK,QAAQ,oCAAoC;;;;;;;;;;CAY1G,MAAM,WAAW,QAAiB,OAAO,cAAuB,MAChE;AAEC,MAAG,KAAK,WAAW,iBAAiB,gBAAgB,CAAC,MAGpD,QAAO;AAMR,MAAG,YAGF,MAAK,SAAS,iBAAiB;AAIhC,OAAK,KAAK,gBAAgB;AAG1B,QAAM,KAAK,qBAAqB;AAGhC,QAAM,KAAK,qBAAqB;EAEhC,MAAM,sBAAsB,YAC5B;AAEC,QAAK,KAAK,sBAAsB,QAAQ,KAAK,CAAC;AAG9C,QAAK,OAAO,YAAY;;AAIzB,SAAO,IAAI,QAAiB,mBAAmB;;;;;;;;;;CAWhD,KAAK,SACL;AAEC,OAAK,qBAAqB;EAG1B,MAAM,cAAc,KAAK,KAAK;EAG9B,MAAM,oBAAoB,WAAW,KAAK,WAAW,KAAK,MAAM,YAAY,EAAE,KAAK,OAAO,QAAQ;AAGlG,OAAK,cAAc,KAAK,kBAAkB;AAG1C,OAAK,qBAAqB;AAG1B,SAAO,KAAK,OAAO,MAAM,UAAU,iBAAiB,mBAAmB;;;;;;CASxE,WAAW,eACX;AAEC,MAAG,OAAO,KAAK,sBAAsB,GAAG,eACxC;AAEC,OAAI,KAAK,WAAW,iBAAiB,gBAAkB,KAAK,WAAW,iBAAiB,cAIvF;AAID,QAAK,qBAAqB;AAG1B,SAAM,QAAQ,kBAAkB,KAAK,eAAe,cAAc;AAKlE,QAAK,OAAO,YAAY;;;;;;CAO1B,MAAM,kBACN;AAEC,OAAK,qBAAqB;AAG1B,OAAK,wBAAwB,KAAK,KAAK;AAGvC,OAAK,qBAAqB;AAG1B,QAAM,IAAI,QAAc,KAAK,iBAAiB,KAAK,KAAK,CAAC;AAGzD,OAAK,KAAK,YAAY;AAGtB,OAAK,OAAO,mBAAmB,QAAQ;AAGvC,OAAK,OAAO,GAAG,SAAS,KAAK,cAAc,KAAK,KAAK,CAAC;;;;;CAMvD,qBACA;AAEC,OAAK,qBAAqB;AAG1B,MAAG,KAAK,WAAW,iBAAiB,eACpC;AAEC,QAAK,SAAS,iBAAiB;AAG/B,QAAK,KAAK,eAAe;AAGzB,QAAK,qBAAqB;AAG1B,QAAK,oBAAoB;AAGzB,SAAM,QAAQ,sBAAsB,KAAK,eAAe,IAAI;SAG7D;AAEC,OAAG,KAAK,WAAW,iBAAiB,UAGnC,OAAM,OAAO,oBAAoB,KAAK,eAAe,uCAAuC,KAAK,QAAQ,6BAA6B,IAAK,WAAW;AAYvJ,QAAK,SAAS,iBAAiB;AAG/B,QAAK,KAAK,eAAe;AAGzB,OAAG,CAAC,KAAK,eAGR,MAAK,iBAAiB,WAAW,KAAK,UAAU,KAAK,KAAK,EAAE,KAAK,QAAQ,2BAA2B;;;;;;CAQvG,cAAc,OACd;AAKC,MAAG,OAAO,UAAU,YAGnB;AAID,QAAM,OAAO,mBAAmB,KAAK,eAAe,OAAO,MAAM;;;;;;;;CASlE,MAAM,iBAAiB,SAAgC,QACvD;EACC,MAAM,YAAY,UAClB;AAEC,QAAK,SAAS,iBAAiB;AAG/B,QAAK,KAAK,eAAe;AAGzB,UAAO,MAAM;;AAId,QAAM,QAAQ,+BAA+B,KAAK,QAAQ,SAAS,KAAK,eAAe,IAAI;AAG3F,OAAK,OAAO,KAAK,SAAS,SAAS;EAGnC,MAAM,iBAAiB,iBAAiB,mBAAmB,kBAAkB,CAAE,KAAK,aAAa,KAAK,QAAS,EAAE,qBAAqB;EAGtI,MAAM,oBAAoB,YAC1B;AAEC,OAAG,kBAAkB,QAAQ,EAC7B;AAEC,SAAK,WAAW,KAAK;IAGrB,MAAM,eAAe;AAGrB,UAAM,OAAO,0BAA0B,KAAK,eAAe,UAAU,eAAe;AAGpF,WAAO,aAAa;cAIZ,QAAQ,aAAa,KAAK,WAAa,GAAG,QAAQ,SAAS,QAAQ,KAAK,WAAa,GAAG,QAAQ,SAAS,UAAU,KAAK,SACjI;AAEC,SAAK,WAAW,KAAK;IAGrB,MAAM,eAAe,6CAA6C,QAAQ,SAAS,OAAO,KAAK,QAAQ;AAGvG,UAAM,OAAO,0BAA0B,KAAK,eAAe,UAAU,eAAe;AAGpF,WAAO,aAAa;UAGrB;AAEC,UAAM,QAAQ,+BAA+B,QAAQ,SAAS,SAAS,KAAK,eAAe,gBAAgB,QAAQ,SAAS,GAAG;AAG/H,SAAK,SAAS,iBAAiB;AAG/B,aAAS;;;AAKX,OAAK,KAAK,WAAW,iBAAiB;AAGtC,OAAK,KAAK,eAAe;;;;;;ACpnB3B,MAAM,2BAA2B;;;;AAKjC,MAAa,wBACb;CAEC,WAAW;CAGX,qCAAqC,IAAI;CAGzC,4BAA4B,IAAI;CAGhC,uCAAuC,IAAI;CAG3C,kCAAkC;CAClC,oCAAoC;CACpC;;;;;;;ACXD,IAAM,iBAAN,cAA0E,aAC1E;;;;CAIC,AAAO;;;;;CAMP,AAAO;;;;;CAMP,AAAO;;;;CAKP,AAAO;;;;CAKP,IAAW,SACX;AACC,SAAO,KAAK,WAAW;;CAIxB,AAAQ;CAGR,AAAQ,sBAAmD,EAAE;CAG7D,AAAQ,YAAY;CAGpB,AAAQ,mBAAyD,EAAE;CAGnE,AAAQ,iBAAiB,IAAI,OAAO;;;;;;;;;;;CAYpC,YACC,AAAO,aACP,AAAO,SACP,AAAO,kBACP,AAAO,UAAkC,EAAE,EAE5C;AAEC,SAAO;EAPA;EACA;EACA;EACA;AAUP,OAAK,aAAa,IAAI,mBAAmB,aAAa,SAAS,kBAHhB;GAAE,GAAG;GAAuB,GAAG;GAAS,CAGS;;CAIjG,IAAI,iBACJ;AACC,SAAO,KAAK,WAAW;;CAIxB,IAAI,YACJ;AACC,SAAO,KAAK,WAAW;;;;;;;;CASxB,MAAM,UACN;EAEC,MAAM,SAAS,MAAM,KAAK,eAAe,SAAS;AAElD,MACA;AAEC,OAAG,KAAK,WAAW,WAAW,iBAAiB,UAE9C;AAID,QAAK,WAAW,GAAG,YAAY,KAAK,SAAS,KAAK,KAAK,CAAC;AAGxD,QAAK,WAAW,GAAG,aAAa,KAAK,qBAAqB,KAAK,KAAK,CAAC;AACrE,QAAK,WAAW,GAAG,gBAAgB,KAAK,uBAAuB,KAAK,KAAK,CAAC;AAG1E,QAAK,WAAW,GAAG,cAAc,KAAK,8BAA8B,KAAK,MAAM,aAAa,CAAC;AAC7F,QAAK,WAAW,GAAG,iBAAiB,KAAK,8BAA8B,KAAK,MAAM,gBAAgB,CAAC;AACnG,QAAK,WAAW,GAAG,gBAAgB,KAAK,8BAA8B,KAAK,MAAM,eAAe,CAAC;AAGjG,QAAK,WAAW,GAAG,WAAW,KAAK,qBAAqB,KAAK,KAAK,CAAC;AACnE,QAAK,WAAW,GAAG,YAAY,KAAK,4BAA4B,KAAK,KAAK,CAAC;AAG3E,QAAK,WAAW,GAAG,SAAS,KAAK,KAAK,KAAK,MAAM,QAAQ,CAAC;AAG1D,SAAM,KAAK,WAAW,SAAS;YAIhC;AACC,WAAQ;;;;;;;;;;;CAYV,MAAM,WAAW,QAAiB,OAAO,sBAA+B,OACxE;AACC,MAAG,CAAC,qBACJ;AAEC,QAAK,oBAAoB;AAGzB,QAAK,sBAAsB,EAAE;;AAI9B,SAAO,KAAK,WAAW,WAAW,MAAM;;;;;;;;;;;CAYzC,MAAM,QAAQ,QAAgB,GAAG,YACjC;AAEC,MAAG,KAAK,WAAW,WAAW,iBAAiB,UAG9C,uBAAM,IAAI,MAAM,oDAAoD,KAAK,eAAe,IAAI;AAI7F,OAAK,aAAa;EAGlB,MAAM,KAAK,KAAK;EAGhB,MAAM,UAAU,iBAAiB,mBAAmB,QAAQ,YAAY,GAAG;EAG3E,MAAM,mBAAmB,YACzB;AAEC,QAAK,iBAAiB,OAAO,OAAe,SAC5C;AAEC,QAAG,MAGF,SAAQ,MAAM;QAKd,SAAQ,KAAK;;AAKf,QAAK,WAAW,KAAK,QAAQ;;AAI9B,QAAM,QAAQ,oBAAoB,OAAO,QAAQ,KAAK,eAAe,GAAG;AAGxE,SAAO,IAAI,QAAiC,gBAAgB;;;;;;;;;;;;;CAc7D,MAAM,UAAU,QAAgB,GAAG,YACnC;AAEC,MAAG,CAAC,KAAK,oBAAoB,QAE5B,MAAK,oBAAoB,0BAAU,IAAI,KAAa;AAIrD,OAAK,oBAAoB,QAAQ,IAAI,KAAK,UAAU,WAAW,CAAC;EAGhE,MAAM,cAAc,MAAM,KAAK,QAAQ,QAAQ,GAAG,WAAW;AAG7D,MAAG,uBAAuB,MAEzB,OAAM;AAIP,MAAG,MAAM,QAAQ,YAAY,CAG5B,uBAAM,IAAI,MAAM,6DAA6D;EAI9E,MAAM,eACN;GACC,SAAS;GACD;GACR,QAAQ,CAAE,GAAG,YAAY,YAAa;GACtC;AAGD,OAAK,KAAK,gBAAgB,aAAa;AAGvC,OAAK,0CAA0C,aAAa;;;;;;;;;;;;;CAc7D,MAAM,YAAY,QAAgB,GAAG,YACrC;AAEC,MAAG,KAAK,WAAW,WAAW,iBAAiB,UAE9C,uBAAM,IAAI,MAAM,gEAAgE,KAAK,eAAe,IAAI;AAIzG,MAAG,CAAC,KAAK,oBAAoB,QAG5B,uBAAM,IAAI,MAAM,4BAA4B,OAAO,0CAA0C;EAI9F,MAAM,yBAAyB,KAAK,UAAU,WAAW;AAGzD,MAAG,CAAC,KAAK,oBAAoB,QAAQ,IAAI,uBAAuB,CAG/D,uBAAM,IAAI,MAAM,4BAA4B,OAAO,2DAA2D;AAI/G,OAAK,oBAAoB,QAAQ,OAAO,uBAAuB;AAI/D,QAAM,KAAK,QAAQ,OAAO,QAAQ,cAAc,eAAe,EAAE,GAAG,WAAW;AAG/E,QAAM,OAAO,sBAAsB,OAAO,OAAO,CAAC,aAAa,uBAAuB,eAAe;;;;;;;;;;;CAYtG,MAAc,uBACd;AAEC,QAAM,OAAO,iBAAiB,KAAK,eAAe,IAAI;AAGtD,OAAK,8BAA8B,YAAY;EAG/C,MAAM,yBAAyB,EAAE;AAGjC,OAAI,MAAM,UAAU,KAAK,qBACzB;AAEC,QAAI,MAAM,iBAAiB,KAAK,oBAAoB,QAAQ,QAAQ,EACpE;IAEC,MAAM,aAAa,KAAK,MAAM,cAAc;AAG5C,2BAAuB,KAAK,KAAK,UAAU,QAAQ,GAAG,WAAW,CAAC;;AAInE,SAAM,QAAQ,IAAI,uBAAuB;;AAI1C,MAAG,uBAAuB,SAAS,EAElC,OAAM,OAAO,YAAY,uBAAuB,OAAO,+BAA+B,KAAK,eAAe,GAAG;;;;;;;;;;CAY/G,SAAS,SACT;AAEC,MAAG,kBAAkB,QAAQ,EAC7B;AAEC,SAAM,OAAO,8BAA8B,QAAQ,OAAO,UAAU,KAAK,eAAe,GAAG;AAG3F,QAAK,KAAK,gBAAgB,QAAQ;AAGlC,QAAK,0CAA0C,QAAQ;AAGvD;;AAID,MAAG,QAAQ,OAAO,KAGjB,uBAAM,IAAI,MAAM,yDAAyD;EAI1E,MAAM,kBAAkB,KAAK,iBAAiB,QAAQ;AAGtD,MAAG,CAAC,iBACJ;AAEC,SAAM,QAAQ,sBAAsB,QAAQ,GAAG,4CAA4C;AAG3F;;AAID,SAAO,KAAK,iBAAiB,QAAQ;AAGrC,MAAG,mBAAmB,QAAQ,CAG7B,iBAAgB,IAAI,MAAM,QAAQ,MAAM,QAAQ,CAAC;OAGlD;AAGC,mBAAgB,QAAW,QAAQ,OAAO;AAG1C,QAAK,qCAAqC,QAAQ;;;;;;;;;CAUpD,MAAM,yBACN;AAEC,OAAI,MAAM,cAAc,KAAK,kBAC7B;GAEC,MAAM,kBAAkB,KAAK,iBAAiB;AAG9C,mCAAgB,IAAI,MAAM,kBAAkB,CAAC;AAG7C,UAAO,KAAK,iBAAiB;;AAI9B,OAAK,8BAA8B,eAAe;;;;;;;CAQnD,MAAM,qBAAqB,kBAC3B;AAEC,MAAG,iBAAiB,MAGnB;AAID,OAAK,WAAW,iBAAiB;;;;;;;CAQlC,MAAM,8BACN;AAEC,OAAK,wBAAwB,KAAK,KAAK;;;;;;;;CASxC,MAAM,0CAA0C,SAChD;AAEC,MAAG,QAAQ,WAAW,+BAGrB,MAAK,cAAc,QAAQ,OAAO,GAAG;;;;;;;;CAUvC,MAAM,qCAAqC,SAC3C;AACC,MACA;AAEC,OAAG,OAAO,QAAQ,OAAO,iBAAiB,YAGzC,MAAK,cAAc,QAAQ,OAAO;WAG7B,UACP;;;;;CAQD,MAAM,8BAA8B,WACpC;AAEC,OAAK,KAAK,UAAU;;CAIrB,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAChB,AAAgB;;AAIjB,8BAAe"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../source/electrum-protocol.ts","../source/rpc-interfaces.ts","../source/enums.ts","../source/interfaces.ts","../source/electrum-connection.ts","../source/constants.ts","../source/electrum-client.ts"],"sourcesContent":["import type { RPCParameter } from './rpc-interfaces.ts';\n\n/**\n * Grouping of utilities that simplifies implementation of the Electrum protocol.\n *\n * @ignore\n */\nexport class ElectrumProtocol\n{\n\t/**\n\t * Helper function that builds an Electrum request object.\n\t *\n\t * @param method - method to call.\n\t * @param parameters - method parameters for the call.\n\t * @param requestId - unique string or number referencing this request.\n\t *\n\t * @returns a properly formatted Electrum request string.\n\t */\n\tstatic buildRequestObject(method: string, parameters: RPCParameter[], requestId: string | number): string\n\t{\n\t\t// Return the formatted request object.\n\t\t// NOTE: Electrum either uses JsonRPC strictly or loosely.\n\t\t// If we specify protocol identifier without being 100% compliant, we risk being disconnected/blacklisted.\n\t\t// For this reason, we omit the protocol identifier to avoid issues.\n\t\treturn JSON.stringify({ method: method, params: parameters, id: requestId });\n\t}\n\n\t/**\n\t * Constant used to verify if a provided string is a valid version number.\n\t *\n\t * @returns a regular expression that matches valid version numbers.\n\t */\n\tstatic get versionRegexp(): RegExp\n\t{\n\t\treturn /^\\d+(\\.\\d+)+$/;\n\t}\n\n\t/**\n\t * Constant used to separate statements/messages in a stream of data.\n\t *\n\t * @returns the delimiter used by Electrum to separate statements.\n\t */\n\tstatic get statementDelimiter(): string\n\t{\n\t\treturn '\\n';\n\t}\n}\n","// Acceptable parameter types for RPC messages\nexport type RPCParameter = string | number | boolean | object | null;\n\n// Acceptable identifier types for RCP messages.\nexport type RCPIdentifier = number | string | null;\n\n// The base type for all RPC messages\nexport interface RPCBase\n{\n\tjsonrpc: string;\n}\n\n// An RPC message that sends a notification requiring no response\nexport interface RPCNotification extends RPCBase\n{\n\tmethod: string;\n\tparams?: RPCParameter[];\n}\n\n// An RPC message that sends a request requiring a response\nexport interface RPCRequest extends RPCBase\n{\n\tid: RCPIdentifier;\n\tmethod: string;\n\tparams?: RPCParameter[];\n}\n\n// An RPC message that returns the response to a successful request\nexport interface RPCStatement extends RPCBase\n{\n\tid: RCPIdentifier;\n\tresult: string;\n}\n\nexport interface RPCError\n{\n\tcode: number;\n\tmessage: string;\n\tdata?: unknown;\n}\n\n// An RPC message that returns the error to an unsuccessful request\nexport interface RPCErrorResponse extends RPCBase\n{\n\tid: RCPIdentifier;\n\terror: RPCError;\n}\n\n// A response to a request is either a statement (successful) or an error (unsuccessful)\nexport type RPCResponse = RPCErrorResponse | RPCStatement | RPCNotification;\n\n// RPC messages are notifications, requests, or responses\nexport type RPCMessage = RPCNotification | RPCRequest | RPCResponse;\n\n// Requests and responses can also be sent in batches\nexport type RPCResponseBatch = RPCResponse[];\nexport type RPCRequestBatch = RPCRequest[];\n\nexport const isRPCErrorResponse = function(message: RPCBase): message is RPCErrorResponse\n{\n\treturn 'id' in message && 'error' in message;\n};\n\nexport const isRPCStatement = function(message: RPCBase): message is RPCStatement\n{\n\treturn 'id' in message && 'result' in message;\n};\n\nexport const isRPCNotification = function(message: RPCBase): message is RPCNotification\n{\n\treturn !('id' in message) && 'method' in message;\n};\n\nexport const isRPCRequest = function(message: RPCBase): message is RPCRequest\n{\n\treturn 'id' in message && 'method' in message;\n};\n","/**\n * Enum that denotes the connection status of an ElectrumConnection.\n * @enum {number}\n * @property {0} DISCONNECTED The connection is disconnected.\n * @property {1} AVAILABLE The connection is connected.\n * @property {2} DISCONNECTING The connection is disconnecting.\n * @property {3} CONNECTING The connection is connecting.\n * @property {4} RECONNECTING The connection is restarting.\n */\nexport enum ConnectionStatus\n{\n\tDISCONNECTED = 0,\n\tCONNECTED = 1,\n\tDISCONNECTING = 2,\n\tCONNECTING = 3,\n\tRECONNECTING = 4,\n}\n","import type { RPCError, RPCParameter, RPCResponse, RPCNotification } from './rpc-interfaces';\nimport type { ElectrumSocketOptions } from '@electrum-cash/socket';\n\n/**\n * Optional settings that change the default behavior of the network connection.\n */\nexport interface ElectrumNetworkOptions extends Partial<ElectrumSocketOptions>\n{\n\t/** If set to true, numbers that can safely be parsed as integers will be `BigInt` rather than `Number`. */\n\tuseBigInt?: boolean;\n\n\t/** When connected, send a keep-alive Ping message this often. */\n\tsendKeepAliveIntervalInMilliSeconds?: number;\n\n\t/** When disconnected, attempt to reconnect after this amount of time. */\n\treconnectAfterMilliSeconds?: number;\n\n\t/** After every send, verify that we have received data after this amount of time. */\n\tverifyConnectionTimeoutInMilliSeconds?: number;\n}\n\n/**\n * @ignore\n */\nexport interface VersionRejected\n{\n\terror: RPCError;\n}\n\n/**\n * @ignore\n */\nexport interface VersionNegotiated\n{\n\tsoftware: string;\n\tprotocol: string;\n}\n\n/**\n * @ignore\n */\nexport type VersionNegotiationResponse = VersionNegotiated | VersionRejected;\n\n/**\n * List of events emitted by the ElectrumConnection.\n * @event\n * @ignore\n */\nexport interface ElectrumConnectionEvents\n{\n\t/**\n\t * Emitted when any data has been received over the network.\n\t * @eventProperty\n\t */\n\t'received': [];\n\n\t/**\n\t * Emitted when a complete electrum message has been received over the network.\n\t * @eventProperty\n\t */\n\t'response': [ RPCResponse ];\n\n\t/**\n\t * Emitted when the connection has completed version negotiation.\n\t * @eventProperty\n\t */\n\t'version': [ VersionNegotiationResponse ];\n\n\t/**\n\t * Emitted when a network connection is initiated.\n\t * @eventProperty\n\t */\n\t'connecting': [];\n\n\t/**\n\t * Emitted when a network connection is successful.\n\t * @eventProperty\n\t */\n\t'connected': [];\n\n\t/**\n\t * Emitted when a network disconnection is initiated.\n\t * @eventProperty\n\t */\n\t'disconnecting': [];\n\n\t/**\n\t * Emitted when a network disconnection is successful.\n\t * @eventProperty\n\t */\n\t'disconnected': [];\n\n\t/**\n\t * Emitted when a network connect attempts to automatically reconnect.\n\t * @eventProperty\n\t */\n\t'reconnecting': [];\n\n\t/**\n\t * Emitted when the network has failed in some way.\n\t * @eventProperty\n\t */\n\t'error': [ Error ];\n}\n\n/**\n * List of events emitted by the ElectrumClient.\n * @event\n * @ignore\n */\nexport interface ElectrumClientEvents\n{\n\t/**\n\t * Emitted when an electrum subscription statement has been received over the network.\n\t * @eventProperty\n\t */\n\t'notification': [ RPCNotification ];\n\n\t/**\n\t * Emitted when a network connection is initiated.\n\t * @eventProperty\n\t */\n\t'connecting': [];\n\n\t/**\n\t * Emitted when a network connection is successful.\n\t * @eventProperty\n\t */\n\t'connected': [];\n\n\t/**\n\t * Emitted when a network disconnection is initiated.\n\t * @eventProperty\n\t */\n\t'disconnecting': [];\n\n\t/**\n\t * Emitted when a network disconnection is successful.\n\t * @eventProperty\n\t */\n\t'disconnected': [];\n\n\t/**\n\t * Emitted when a network connect attempts to automatically reconnect.\n\t * @eventProperty\n\t */\n\t'reconnecting': [];\n\n\t/**\n\t * Emitted when the network has failed in some way.\n\t * @eventProperty\n\t */\n\t'error': [ Error ];\n}\n\n/**\n * A list of possible responses to requests.\n * @ignore\n */\nexport type RequestResponse = RPCParameter | RPCParameter[];\n\n/**\n * Request resolvers are used to process the response of a request. This takes either\n * an error object or any stringified data, while the other parameter is omitted.\n * @ignore\n */\nexport type RequestResolver = (error?: Error, data?: string) => void;\n\n/**\n * Typing for promise resolution.\n * @ignore\n */\nexport type ResolveFunction<T> = (value: T | PromiseLike<T>) => void;\n\n/**\n * Typing for promise rejection.\n * @ignore\n */\nexport type RejectFunction = (reason?: unknown) => void;\n\n/**\n * @ignore\n */\nexport const isVersionRejected = function(object: VersionNegotiationResponse): object is VersionRejected\n{\n\treturn 'error' in object;\n};\n\n/**\n * @ignore\n */\nexport const isVersionNegotiated = function(object: VersionNegotiationResponse): object is VersionNegotiated\n{\n\treturn 'software' in object && 'protocol' in object;\n};\n","import debug from '@electrum-cash/debug-logs';\nimport { ElectrumWebSocket } from '@electrum-cash/web-socket';\nimport { ElectrumProtocol } from './electrum-protocol.ts';\nimport { isRPCNotification, isRPCErrorResponse } from './rpc-interfaces.ts';\nimport { EventEmitter } from 'eventemitter3';\nimport { ConnectionStatus } from './enums.ts';\nimport { parse, parseNumberAndBigInt } from 'lossless-json';\nimport { isVersionRejected } from './interfaces.ts';\nimport type { ElectrumNetworkOptions, ElectrumConnectionEvents, ResolveFunction, RejectFunction, VersionNegotiationResponse } from './interfaces.ts';\nimport type { RPCResponse } from './rpc-interfaces.ts';\nimport type { ElectrumSocket } from '@electrum-cash/socket';\n\n/**\n * Wrapper around TLS/WSS sockets that gracefully separates a network stream into Electrum protocol messages.\n */\nexport class ElectrumConnection extends EventEmitter<ElectrumConnectionEvents>\n{\n\t// Initialize the connected flag to false to indicate that there is no connection\n\tpublic status: ConnectionStatus = ConnectionStatus.DISCONNECTED;\n\n\t// Declare empty timestamps\n\tprivate lastReceivedTimestamp: number;\n\n\t// Declare an empty socket.\n\tprivate socket: ElectrumSocket;\n\n\t// Declare timers for keep-alive pings and reconnection\n\tprivate keepAliveTimer?: number;\n\tprivate reconnectTimer?: number;\n\n\t// Initialize an empty array of connection verification timers.\n\tprivate verifications: Array<number> = [];\n\n\t// Initialize messageBuffer to an empty string\n\tprivate messageBuffer = '';\n\n\t/**\n\t * Sets up network configuration for an Electrum client connection.\n\t *\n\t * @param application - your application name, used to identify to the electrum host.\n\t * @param version - protocol version to use with the host.\n\t * @param socketOrHostname - pre-configured electrum socket or fully qualified domain name or IP number of the host\n\t * @param options - ...\n\t *\n\t * @throws {Error} if `version` is not a valid version string.\n\t */\n\tconstructor(\n\t\tprivate application: string,\n\t\tprivate version: string,\n\t\tprivate socketOrHostname: ElectrumSocket | string,\n\t\tprivate options: ElectrumNetworkOptions,\n\t)\n\t{\n\t\t// Initialize the event emitter.\n\t\tsuper();\n\n\t\t// Check if the provided version is a valid version number.\n\t\tif(!ElectrumProtocol.versionRegexp.test(version))\n\t\t{\n\t\t\t// Throw an error since the version number was not valid.\n\t\t\tthrow(new Error(`Provided version string (${version}) is not a valid protocol version number.`));\n\t\t}\n\n\t\t// If a hostname was provided..\n\t\tif(typeof socketOrHostname === 'string')\n\t\t{\n\t\t\t// Use a web socket with default parameters.\n\t\t\tthis.socket = new ElectrumWebSocket(socketOrHostname, this.options);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Use the provided socket.\n\t\t\tthis.socket = socketOrHostname;\n\t\t}\n\n\t\t// Set up handlers for connection and disconnection.\n\t\tthis.socket.on('connected', this.onSocketConnect.bind(this));\n\t\tthis.socket.on('disconnected', this.onSocketDisconnect.bind(this));\n\n\t\t// Set up handler for incoming data.\n\t\tthis.socket.on('data', this.parseMessageChunk.bind(this));\n\t}\n\n\t// Expose hostIdentifier from the socket.\n\tget hostIdentifier(): string\n\t{\n\t\treturn this.socket.host;\n\t}\n\n\t// Expose port from the socket.\n\tget encrypted(): boolean\n\t{\n\t\treturn this.socket.options.encrypted;\n\t}\n\n\t/**\n\t * Assembles incoming data into statements and hands them off to the message parser.\n\t *\n\t * @param data - data to append to the current message buffer, as a string.\n\t *\n\t * @throws {SyntaxError} if the passed statement parts are not valid JSON.\n\t */\n\tparseMessageChunk(data: string): void\n\t{\n\t\t// Update the timestamp for when we last received data.\n\t\tthis.lastReceivedTimestamp = Date.now();\n\n\t\t// Emit a notification indicating that the connection has received data.\n\t\tthis.emit('received');\n\n\t\t// Clear and remove all verification timers.\n\t\tthis.verifications.forEach((timer) => clearTimeout(timer));\n\t\tthis.verifications.length = 0;\n\n\t\t// Add the message to the current message buffer.\n\t\tthis.messageBuffer += data;\n\n\t\t// Check if the new message buffer contains the statement delimiter.\n\t\twhile(this.messageBuffer.includes(ElectrumProtocol.statementDelimiter))\n\t\t{\n\t\t\t// Split message buffer into statements.\n\t\t\tconst statementParts = this.messageBuffer.split(ElectrumProtocol.statementDelimiter);\n\n\t\t\t// For as long as we still have statements to parse..\n\t\t\twhile(statementParts.length > 1)\n\t\t\t{\n\t\t\t\t// Move the first statement to its own variable.\n\t\t\t\tconst currentStatementList = String(statementParts.shift());\n\n\t\t\t\t// Parse the statement into an object or list of objects.\n\t\t\t\tlet statementList = parse(currentStatementList, null, this.options.useBigInt ? parseNumberAndBigInt : parseFloat) as RPCResponse | RPCResponse[];\n\n\t\t\t\t// Wrap the statement in an array if it is not already a batched statement list.\n\t\t\t\tif(!Array.isArray(statementList))\n\t\t\t\t{\n\t\t\t\t\tstatementList = [ statementList ];\n\t\t\t\t}\n\n\t\t\t\t// For as long as there is statements in the result set..\n\t\t\t\twhile(statementList.length > 0)\n\t\t\t\t{\n\t\t\t\t\t// Move the first statement from the batch to its own variable.\n\t\t\t\t\tconst currentStatement = statementList.shift();\n\n\t\t\t\t\t// If the current statement is a subscription notification..\n\t\t\t\t\tif(isRPCNotification(currentStatement))\n\t\t\t\t\t{\n\t\t\t\t\t\t// Emit the notification for handling higher up in the stack.\n\t\t\t\t\t\tthis.emit('response', currentStatement);\n\n\t\t\t\t\t\t// Consider this statement handled.\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the current statement is a version negotiation response..\n\t\t\t\t\tif(currentStatement.id === 'versionNegotiation')\n\t\t\t\t\t{\n\t\t\t\t\t\tif(isRPCErrorResponse(currentStatement))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// Then emit a failed version negotiation response signal.\n\t\t\t\t\t\t\tthis.emit('version', { error: currentStatement.error });\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// Extract the software and protocol version reported.\n\t\t\t\t\t\t\tconst [ software, protocol ] = currentStatement.result;\n\n\t\t\t\t\t\t\t// Emit a successful version negotiation response signal.\n\t\t\t\t\t\t\tthis.emit('version', { software, protocol });\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Consider this statement handled.\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the current statement is a keep-alive response..\n\t\t\t\t\tif(currentStatement.id === 'keepAlive')\n\t\t\t\t\t{\n\t\t\t\t\t\t// Do nothing and consider this statement handled.\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Emit the statements for handling higher up in the stack.\n\t\t\t\t\tthis.emit('response', currentStatement);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Store the remaining statement as the current message buffer.\n\t\t\tthis.messageBuffer = statementParts.shift() || '';\n\t\t}\n\t}\n\n\t/**\n\t * Sends a keep-alive message to the host.\n\t *\n\t * @returns true if the ping message was fully flushed to the socket, false if\n\t * part of the message is queued in the user memory\n\t */\n\tasync ping(): Promise<boolean>\n\t{\n\t\t// Write a log message.\n\t\tdebug.ping(`Sending keep-alive ping to '${this.hostIdentifier}'`);\n\n\t\t// Craft a keep-alive message.\n\t\tconst message = ElectrumProtocol.buildRequestObject('server.ping', [], 'keepAlive');\n\n\t\t// Send the keep-alive message.\n\t\tconst status = this.send(message);\n\n\t\t// Return the ping status.\n\t\treturn status;\n\t}\n\n\t/**\n\t * Initiates the network connection negotiates a protocol version. Also emits the 'connect' signal if successful.\n\t *\n\t * @throws {Error} if the socket connection fails.\n\t * @returns a promise resolving when the connection is established\n\t */\n\tasync connect(): Promise<void>\n\t{\n\t\t// If we are already connected return true.\n\t\tif(this.status === ConnectionStatus.CONNECTED)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t// Indicate that the connection is connecting\n\t\tthis.status = ConnectionStatus.CONNECTING;\n\n\t\t// Emit a connect event now that the connection is being set up.\n\t\tthis.emit('connecting');\n\n\t\t// Create a function that will resolve once the connection is established.\n\t\t// The connection is established through onSocketConnect\n\t\tconst connectionResolver = (resolve: ResolveFunction<void>, reject: RejectFunction): void =>\n\t\t{\n\t\t\t// Resolve the connection promise once the connection is established. This event is emitted by onSocketConnect.\n\t\t\tthis.once('connected', () =>\n\t\t\t{\n\t\t\t\t// Remove the listener for the disconnected event.\n\t\t\t\tthis.removeListener('disconnected', reject);\n\n\t\t\t\t// Resolve the connection promise.\n\t\t\t\tresolve();\n\t\t\t});\n\n\t\t\t// Reject the connection promise if the connection is disconnected.\n\t\t\tthis.once('disconnected', () =>\n\t\t\t{\n\t\t\t\t// Remove the listener for the connected event.\n\t\t\t\tthis.removeListener('connected', resolve);\n\n\t\t\t\t// Reject the connection promise.\n\t\t\t\treject();\n\t\t\t});\n\n\t\t\t// Start the socket connection process.\n\t\t\tthis.socket.connect();\n\t\t};\n\n\t\t// Wait until connection is established and version negotiation succeeds.\n\t\tawait new Promise<void>(connectionResolver);\n\t}\n\n\t/**\n\t * Restores the network connection.\n\t */\n\tasync reconnect(): Promise<void>\n\t{\n\t\t// If a reconnect timer is set, remove it\n\t\tawait this.clearReconnectTimer();\n\n\t\t// Write a log message.\n\t\tdebug.network(`Trying to reconnect to '${this.hostIdentifier}'..`);\n\n\t\t// Set the status to reconnecting for more accurate log messages.\n\t\tthis.status = ConnectionStatus.RECONNECTING;\n\n\t\t// Emit a connect event now that the connection is usable.\n\t\tthis.emit('reconnecting');\n\n\t\t// Disconnect the underlying socket\n\t\tthis.socket.disconnect();\n\n\t\ttry\n\t\t{\n\t\t\t// Try to connect again.\n\t\t\tawait this.connect();\n\t\t}\n\t\tcatch (_error)\n\t\t{\n\t\t\t// Do nothing as the error should be handled via the disconnect and error signals.\n\t\t}\n\t}\n\n\t/**\n\t * Removes the current reconnect timer.\n\t */\n\tclearReconnectTimer(): void\n\t{\n\t\t// If a reconnect timer is set, remove it\n\t\tif(this.reconnectTimer)\n\t\t{\n\t\t\tclearTimeout(this.reconnectTimer);\n\t\t}\n\n\t\t// Reset the timer reference.\n\t\tthis.reconnectTimer = undefined;\n\t}\n\n\t/**\n\t * Removes the current keep-alive timer.\n\t */\n\tclearKeepAliveTimer(): void\n\t{\n\t\t// If a keep-alive timer is set, remove it\n\t\tif(this.keepAliveTimer)\n\t\t{\n\t\t\tclearTimeout(this.keepAliveTimer);\n\t\t}\n\n\t\t// Reset the timer reference.\n\t\tthis.keepAliveTimer = undefined;\n\t}\n\n\t/**\n\t * Initializes the keep alive timer loop.\n\t */\n\tsetupKeepAliveTimer(): void\n\t{\n\t\t// If the keep-alive timer loop is not currently set up..\n\t\tif(!this.keepAliveTimer)\n\t\t{\n\t\t\t// Set a new keep-alive timer.\n\t\t\tthis.keepAliveTimer = setTimeout(this.ping.bind(this), this.options.sendKeepAliveIntervalInMilliSeconds) as unknown as number;\n\t\t}\n\t}\n\n\t/**\n\t * Tears down the current connection and removes all event listeners on disconnect.\n\t *\n\t * @param force - disconnect even if the connection has not been fully established yet.\n\t * @param intentional - update connection state if disconnect is intentional.\n\t *\n\t * @returns true if successfully disconnected, or false if there was no connection.\n\t */\n\tasync disconnect(force: boolean = false, intentional: boolean = true): Promise<boolean>\n\t{\n\t\t// Return early when there is nothing to disconnect from\n\t\tif(this.status === ConnectionStatus.DISCONNECTED && !force)\n\t\t{\n\t\t\t// Return false to indicate that there was nothing to disconnect from.\n\t\t\treturn false;\n\t\t}\n\n\t\t// Update connection state if the disconnection is intentional.\n\t\t// NOTE: The state is meant to represent what the client is requesting, but\n\t\t// is used internally to handle visibility changes in browsers to ensure functional reconnection.\n\t\tif(intentional)\n\t\t{\n\t\t\t// Set connection status to null to indicate tear-down is currently happening.\n\t\t\tthis.status = ConnectionStatus.DISCONNECTING;\n\t\t}\n\n\t\t// Emit a connect event to indicate that we are disconnecting.\n\t\tthis.emit('disconnecting');\n\n\t\t// If a keep-alive timer is set, remove it.\n\t\tawait this.clearKeepAliveTimer();\n\n\t\t// If a reconnect timer is set, remove it\n\t\tawait this.clearReconnectTimer();\n\n\t\tconst disconnectResolver = (resolve: ResolveFunction<boolean>): void =>\n\t\t{\n\t\t\t// Resolve to true after the connection emits a disconnect\n\t\t\tthis.once('disconnected', () => resolve(true));\n\n\t\t\t// Close the connection on the socket level.\n\t\t\tthis.socket.disconnect();\n\t\t};\n\n\t\t// Return true to indicate that we disconnected.\n\t\treturn new Promise<boolean>(disconnectResolver);\n\t}\n\n\t/**\n\t * Sends an arbitrary message to the server.\n\t *\n\t * @param message - json encoded request object to send to the server, as a string.\n\t *\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\tasync send(message: string): Promise<boolean>\n\t{\n\t\t// Remove the current keep-alive timer if it exists.\n\t\tthis.clearKeepAliveTimer();\n\n\t\t// Get the current timestamp in milliseconds.\n\t\tconst currentTime = Date.now();\n\n\t\t// Follow up and verify that the message got sent..\n\t\tconst verificationTimer = setTimeout(this.verifySend.bind(this, currentTime), this.socket.options.timeoutInMilliSeconds) as unknown as number;\n\n\t\t// Store the verification timer locally so that it can be cleared when data has been received.\n\t\tthis.verifications.push(verificationTimer);\n\n\t\t// Set a new keep-alive timer.\n\t\tthis.setupKeepAliveTimer();\n\n\t\t// Write the message to the network socket.\n\t\treturn this.socket.write(message + ElectrumProtocol.statementDelimiter);\n\t}\n\n\t// --- Event managers. --- //\n\n\t/**\n\t * Marks the connection as timed out and schedules reconnection if we have not\n\t * received data within the expected time frame.\n\t */\n\tverifySend(sentTimestamp: number): void\n\t{\n\t\t// If we haven't received any data since we last sent data out..\n\t\tif(Number(this.lastReceivedTimestamp) < sentTimestamp)\n\t\t{\n\t\t\t// If this connection is already disconnected, we do not change anything\n\t\t\tif((this.status === ConnectionStatus.DISCONNECTED) || (this.status === ConnectionStatus.DISCONNECTING))\n\t\t\t{\n\t\t\t\t// debug.warning(`Tried to verify already disconnected connection to '${this.hostIdentifier}'`);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Remove the current keep-alive timer if it exists.\n\t\t\tthis.clearKeepAliveTimer();\n\n\t\t\t// Write a notification to the logs.\n\t\t\tdebug.network(`Connection to '${this.hostIdentifier}' timed out.`);\n\n\t\t\t// Close the connection to avoid re-use.\n\t\t\t// NOTE: This initiates reconnection routines if the connection has not\n\t\t\t// been marked as intentionally disconnected.\n\t\t\tthis.socket.disconnect();\n\t\t}\n\t}\n\n\t/**\n\t * Updates the connection status when a connection is confirmed.\n\t */\n\tasync onSocketConnect(): Promise<void>\n\t{\n\t\t// If a reconnect timer is set, remove it.\n\t\tthis.clearReconnectTimer();\n\n\t\t// Set up the initial timestamp for when we last received data from the server.\n\t\tthis.lastReceivedTimestamp = Date.now();\n\n\t\t// Set up the initial keep-alive timer.\n\t\tthis.setupKeepAliveTimer();\n\t\t\n\t\t// Wait for the version to be negotiated.\n\t\tawait new Promise<void>(this.negotiateVersion.bind(this));\n\n\t\t// Emit a connect event now that the connection is established and version negotiated.\n\t\tthis.emit('connected');\n\t}\n\n\t/**\n\t * Updates the connection status when a connection is ended.\n\t */\n\tonSocketDisconnect(): void\n\t{\n\t\t// Remove the current keep-alive timer if it exists.\n\t\tthis.clearKeepAliveTimer();\n\n\t\t// If this is a connection we're trying to tear down..\n\t\tif(this.status === ConnectionStatus.DISCONNECTING)\n\t\t{\n\t\t\t// Mark the connection as disconnected.\n\t\t\tthis.status = ConnectionStatus.DISCONNECTED;\n\n\t\t\t// Send a disconnect signal higher up the stack.\n\t\t\tthis.emit('disconnected');\n\n\t\t\t// If a reconnect timer is set, remove it.\n\t\t\tthis.clearReconnectTimer();\n\n\t\t\t// Remove all event listeners\n\t\t\tthis.removeAllListeners();\n\n\t\t\t// Write a log message.\n\t\t\tdebug.network(`Disconnected from '${this.hostIdentifier}'.`);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// If this is for an established connection..\n\t\t\tif(this.status === ConnectionStatus.CONNECTED)\n\t\t\t{\n\t\t\t\t// Write a notification to the logs.\n\t\t\t\tdebug.errors(`Connection with '${this.hostIdentifier}' was closed, trying to reconnect in ${this.options.reconnectAfterMilliSeconds / 1000} seconds.`);\n\t\t\t}\n\t\t\t// If this is a connection that is currently connecting, reconnecting or already disconnected..\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Do nothing\n\n\t\t\t\t// NOTE: This error message is useful during manual debugging of reconnections.\n\t\t\t\t// debug.errors(`Lost connection with reconnecting or already disconnected server '${this.hostIdentifier}'.`);\n\t\t\t}\n\n\t\t\t// Mark the connection as disconnected for now..\n\t\t\tthis.status = ConnectionStatus.DISCONNECTED;\n\n\t\t\t// Send a disconnect signal higher up the stack.\n\t\t\tthis.emit('disconnected');\n\n\t\t\t// If we don't have a pending reconnection timer..\n\t\t\tif(!this.reconnectTimer)\n\t\t\t{\n\t\t\t\t// Attempt to reconnect after one keep-alive duration.\n\t\t\t\tthis.reconnectTimer = setTimeout(this.reconnect.bind(this), this.options.reconnectAfterMilliSeconds) as unknown as number;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Notify administrator of any unexpected errors.\n\t */\n\tonSocketError(error: unknown | undefined): void\n\t{\n\t\t// Report a generic error if no error information is present.\n\t\t// NOTE: When using WSS, the error event explicitly\n\t\t// only allows to send a \"simple\" event without data.\n\t\t// https://stackoverflow.com/a/18804298\n\t\tif(typeof error === 'undefined')\n\t\t{\n\t\t\t// Do nothing, and instead rely on the socket disconnect event for further information.\n\t\t\treturn;\n\t\t}\n\n\t\t// Log the error, as there is nothing we can do to actually handle it.\n\t\tdebug.errors(`Network error ('${this.hostIdentifier}'): `, error);\n\t}\n\n\t/**\n\t * Negotiate the protocol version with the server.\n\t * Disconnect the connection if the version negotiation fails.\n\t * @param resolve \n\t * @param reject \n\t */\n\tasync negotiateVersion(resolve: ResolveFunction<void>, reject: RejectFunction): Promise<void>\n\t{\n\t\tconst rejector = (): void =>\n\t\t{\n\t\t\t// Set the status back to disconnected\n\t\t\tthis.status = ConnectionStatus.DISCONNECTED;\n\n\t\t\t// Emit a connect event indicating that we failed to connect.\n\t\t\tthis.emit('disconnected');\n\n\t\t\t// Reject with the error as reason\n\t\t\treject(`Version negotiation with ${this.hostIdentifier} failed.`);\n\t\t};\n\n\t\t// Write a log message to show that we have started version negotiation.\n\t\tdebug.network(`Requesting protocol version ${this.version} with '${this.hostIdentifier}'.`);\n\t\t\t\t\n\t\t// Add error handler for one-time error.\n\t\tthis.socket.once('disconnected', rejector);\n\n\t\t// Build a version negotiation message.\n\t\tconst versionMessage = ElectrumProtocol.buildRequestObject('server.version', [ this.application, this.version ], 'versionNegotiation');\n\n\t\t// Define a function to wrap version validation as a function.\n\t\tconst versionValidator = (version: VersionNegotiationResponse): void =>\n\t\t{\n\t\t\t// Check if version negotiation failed.\n\t\t\tif(isVersionRejected(version))\n\t\t\t{\n\t\t\t\t// Disconnect from the host.\n\t\t\t\tthis.disconnect(true);\n\n\t\t\t\t// Declare an error message.\n\t\t\t\tconst errorMessage = 'unsupported protocol version.';\n\n\t\t\t\t// Log the error.\n\t\t\t\tdebug.errors(`Failed to connect with ${this.hostIdentifier} due to ${errorMessage}`);\n\n\t\t\t\t// Reject the connection with false since version negotiation failed.\n\t\t\t\treject(errorMessage);\n\t\t\t}\n\t\t\t// Check if the host supports our requested protocol version.\n\t\t\t// NOTE: the server responds with version numbers that truncate 0's, so 1.5.0 turns into 1.5.\n\t\t\telse if((version.protocol !== this.version) && (`${version.protocol}.0` !== this.version) && (`${version.protocol}.0.0` !== this.version))\n\t\t\t{\n\t\t\t\t// Disconnect from the host.\n\t\t\t\tthis.disconnect(true);\n\n\t\t\t\t// Declare an error message.\n\t\t\t\tconst errorMessage = `incompatible protocol version negotiated (${version.protocol} !== ${this.version}).`;\n\n\t\t\t\t// Log the error.\n\t\t\t\tdebug.errors(`Failed to connect with ${this.hostIdentifier} due to ${errorMessage}`);\n\n\t\t\t\t// Reject the connection with false since version negotiation failed.\n\t\t\t\treject(errorMessage);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Write a log message.\n\t\t\t\tdebug.network(`Negotiated protocol version ${version.protocol} with '${this.hostIdentifier}', powered by ${version.software}.`);\n\n\t\t\t\t// Set connection status to connected\n\t\t\t\tthis.status = ConnectionStatus.CONNECTED;\n\n\t\t\t\t// Resolve the connection promise since we successfully connected and negotiated protocol version.\n\t\t\t\tresolve();\n\t\t\t}\n\t\t};\n\n\t\t// Listen for version negotiation once.\n\t\tthis.once('version', versionValidator);\n\n\t\t// Send the version negotiation message.\n\t\tthis.send(versionMessage);\n\t}\n}\n","import type { ElectrumNetworkOptions } from './interfaces.ts';\n\n// Define number of milliseconds per second for legibility.\nconst MILLI_SECONDS_PER_SECOND = 1000;\n\n/**\n * Configure default options.\n */\nexport const defaultNetworkOptions: ElectrumNetworkOptions =\n{\n\t// By default, all numbers including integers are parsed as regular JavaScript numbers.\n\tuseBigInt: false,\n\n\t// Send a ping message every seconds, to detect network problem as early as possible.\n\tsendKeepAliveIntervalInMilliSeconds: 1 * MILLI_SECONDS_PER_SECOND,\n\n\t// Try to reconnect 5 seconds after unintentional disconnects.\n\treconnectAfterMilliSeconds: 5 * MILLI_SECONDS_PER_SECOND,\n\n\t// Try to detect stale connections 5 seconds after every send.\n\tverifyConnectionTimeoutInMilliSeconds: 5 * MILLI_SECONDS_PER_SECOND,\n};\n","import debug from '@electrum-cash/debug-logs';\nimport { ElectrumConnection } from './electrum-connection.ts';\nimport { ElectrumProtocol } from './electrum-protocol.ts';\nimport { defaultNetworkOptions } from './constants.ts';\nimport { ConnectionStatus } from './enums.ts';\nimport { EventEmitter } from 'eventemitter3';\nimport { Mutex } from 'async-mutex';\nimport { isRPCNotification, isRPCErrorResponse } from './rpc-interfaces.ts';\nimport type { RPCParameter, RPCNotification, RPCResponse } from './rpc-interfaces.ts';\nimport type { ElectrumNetworkOptions, ElectrumClientEvents, ResolveFunction, RequestResolver, RequestResponse } from './interfaces.ts';\nimport type { ElectrumSocket } from '@electrum-cash/socket';\n\n/**\n * High-level Electrum client that lets applications send requests and subscribe to notification events from a server.\n */\nclass ElectrumClient<ElectrumEvents extends ElectrumClientEvents> extends EventEmitter<ElectrumClientEvents | ElectrumEvents> implements ElectrumClientEvents\n{\n\t/**\n\t * The name and version of the server software indexing the blockchain.\n\t */\n\tpublic software: string;\n\n\t/**\n\t * The genesis hash of the blockchain indexed by the server.\n\t * @remarks This is only available after a 'server.features' call.\n\t */\n\tpublic genesisHash: string;\n\n\t/**\n\t * The chain height of the blockchain indexed by the server.\n\t * @remarks This is only available after a 'blockchain.headers.subscribe' call.\n\t */\n\tpublic chainHeight: number;\n\n\t/**\n\t * Timestamp of when we last received data from the server indexing the blockchain.\n\t */\n\tpublic lastReceivedTimestamp: number;\n\n\t/**\n\t * Number corresponding to the underlying connection status.\n\t */\n\tpublic get status(): ConnectionStatus\n\t{\n\t\treturn this.connection.status;\n\t}\n\n\t// Declare instance variables\n\tprivate connection: ElectrumConnection;\n\n\t// Initialize an empty list of subscription metadata.\n\tprivate subscriptionMethods: Record<string, Set<string>> = {};\n\n\t// Start counting the request IDs from 0\n\tprivate requestId = 0;\n\n\t// Initialize an empty dictionary for keeping track of request resolvers\n\tprivate requestResolvers: { [index: number]: RequestResolver } = {};\n\n\t// Mutex lock used to prevent simultaneous connect() and disconnect() calls.\n\tprivate connectionLock = new Mutex();\n\n\t/**\n\t * Initializes an Electrum client.\n\t *\n\t * @param application - your application name, used to identify to the electrum host.\n\t * @param version - protocol version to use with the host.\n\t * @param socketOrHostname - pre-configured electrum socket or fully qualified domain name or IP number of the host\n\t * @param options - ...\n\t *\n\t * @throws {Error} if `version` is not a valid version string.\n\t */\n\tconstructor(\n\t\tpublic application: string,\n\t\tpublic version: string,\n\t\tpublic socketOrHostname: ElectrumSocket | string,\n\t\tpublic options: Partial<ElectrumNetworkOptions> = {},\n\t)\n\t{\n\t\t// Initialize the event emitter.\n\t\tsuper();\n\n\t\t// Update default options with the provided values.\n\t\tconst networkOptions: ElectrumNetworkOptions = { ...defaultNetworkOptions, ...options };\n\n\t\t// Set up a connection to an electrum server.\n\t\tthis.connection = new ElectrumConnection(application, version, socketOrHostname, networkOptions);\n\t}\n\n\t// Expose hostIdentifier from the connection.\n\tget hostIdentifier(): string\n\t{\n\t\treturn this.connection.hostIdentifier;\n\t}\n\n\t// Expose port from the connection.\n\tget encrypted(): boolean\n\t{\n\t\treturn this.connection.encrypted;\n\t}\n\n\t/**\n\t * Connects to the remote server.\n\t *\n\t * @throws {Error} if the socket connection fails.\n\t * @returns a promise resolving when the connection is established.\n\t */\n\tasync connect(): Promise<void>\n\t{\n\t\t// Force concurrent calls to this function to be run serially.\n\t\treturn this.connectionLock.runExclusive(async () =>\n\t\t{\n\t\t\t// If we are already connected, do not attempt to connect again.\n\t\t\tif(this.connection.status === ConnectionStatus.CONNECTED)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Listen for parsed statements.\n\t\t\tthis.connection.on('response', this.response.bind(this));\n\n\t\t\t// Hook up handles for the connected and disconnected events.\n\t\t\tthis.connection.on('connected', this.resubscribeOnConnect.bind(this));\n\t\t\tthis.connection.on('disconnected', this.onConnectionDisconnect.bind(this));\n\n\t\t\t// Relay connecting and reconnecting events.\n\t\t\tthis.connection.on('connecting', this.handleConnectionStatusChanges.bind(this, 'connecting'));\n\t\t\tthis.connection.on('disconnecting', this.handleConnectionStatusChanges.bind(this, 'disconnecting'));\n\t\t\tthis.connection.on('reconnecting', this.handleConnectionStatusChanges.bind(this, 'reconnecting'));\n\n\t\t\t// Hook up client metadata gathering functions.\n\t\t\tthis.connection.on('version', this.storeSoftwareVersion.bind(this));\n\t\t\tthis.connection.on('received', this.updateLastReceivedTimestamp.bind(this));\n\n\t\t\t// Relay error events.\n\t\t\tthis.connection.on('error', this.emit.bind(this, 'error'));\n\n\t\t\t// Connect with the server.\n\t\t\tawait this.connection.connect();\n\t\t});\n\t}\n\n\t/**\n\t * Disconnects from the remote server and removes all event listeners/subscriptions and open requests.\n\t *\n\t * @param force - disconnect even if the connection has not been fully established yet.\n\t * @param retainSubscriptions - retain subscription data so they will be restored on reconnection.\n\t *\n\t * @returns true if successfully disconnected, or false if there was no connection.\n\t */\n\tasync disconnect(force: boolean = false, retainSubscriptions: boolean = false): Promise<boolean>\n\t{\n\t\t// Force concurrent calls to this function to be run serially.\n\t\treturn this.connectionLock.runExclusive(async () =>\n\t\t{\n\t\t\tif(!retainSubscriptions)\n\t\t\t{\n\t\t\t\t// Cancel all event listeners.\n\t\t\t\tthis.removeAllListeners();\n\n\t\t\t\t// Remove all subscription data\n\t\t\t\tthis.subscriptionMethods = {};\n\t\t\t}\n\n\t\t\t// Disconnect from the remote server.\n\t\t\treturn this.connection.disconnect(force);\n\t\t});\n\t}\n\n\t/**\n\t * Calls a method on the remote server with the supplied parameters.\n\t *\n\t * @param method - name of the method to call.\n\t * @param parameters - one or more parameters for the method.\n\t *\n\t * @throws {Error} if the client is disconnected.\n\t * @returns a promise that resolves with the result of the method or an Error.\n\t */\n\tasync request(method: string, ...parameters: RPCParameter[]): Promise<Error | RequestResponse>\n\t{\n\t\t// If we are not connected to a server..\n\t\tif(this.connection.status !== ConnectionStatus.CONNECTED)\n\t\t{\n\t\t\t// Reject the request with a disconnected error message.\n\t\t\tthrow(new Error(`Unable to send request to a disconnected server '${this.hostIdentifier}'.`));\n\t\t}\n\n\t\t// Increase the request ID by one.\n\t\tthis.requestId += 1;\n\n\t\t// Store a copy of the request id.\n\t\tconst id = this.requestId;\n\n\t\t// Format the arguments as an electrum request object.\n\t\tconst message = ElectrumProtocol.buildRequestObject(method, parameters, id);\n\n\t\t// Define a function to wrap the request in a promise.\n\t\tconst requestResolver = (resolve: ResolveFunction<Error | RequestResponse>): void =>\n\t\t{\n\t\t\t// Add a request resolver for this promise to the list of requests.\n\t\t\tthis.requestResolvers[id] = (error?: Error, data?: RequestResponse) =>\n\t\t\t{\n\t\t\t\t// If the resolution failed..\n\t\t\t\tif(error)\n\t\t\t\t{\n\t\t\t\t\t// Resolve the promise with the error for the application to handle.\n\t\t\t\t\tresolve(error);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// Resolve the promise with the request results.\n\t\t\t\t\tresolve(data);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// Send the request message to the remote server.\n\t\t\tthis.connection.send(message);\n\t\t};\n\n\t\t// Write a log message.\n\t\tdebug.network(`Sending request '${method}' to '${this.hostIdentifier}'`);\n\n\t\t// return a promise to deliver results later.\n\t\treturn new Promise<Error | RequestResponse>(requestResolver);\n\t}\n\n\t/**\n\t * Subscribes to the method and payload at the server.\n\t *\n\t * @remarks the response for the subscription request is issued as a notification event.\n\t *\n\t * @param method - one of the subscribable methods the server supports.\n\t * @param parameters - one or more parameters for the method.\n\t *\n\t * @throws {Error} if the client is disconnected.\n\t * @returns a promise resolving when the subscription is established.\n\t */\n\tasync subscribe(method: string, ...parameters: RPCParameter[]): Promise<void>\n\t{\n\t\t// Initialize an empty list of subscription payloads, if needed.\n\t\tif(!this.subscriptionMethods[method])\n\t\t{\n\t\t\tthis.subscriptionMethods[method] = new Set<string>();\n\t\t}\n\n\t\t// Store the subscription parameters to track what data we have subscribed to.\n\t\tthis.subscriptionMethods[method].add(JSON.stringify(parameters));\n\n\t\t// Send initial subscription request.\n\t\tconst requestData = await this.request(method, ...parameters);\n\n\t\t// If the request failed, throw it as an error.\n\t\tif(requestData instanceof Error)\n\t\t{\n\t\t\tthrow(requestData);\n\t\t}\n\n\t\t// If the request returned more than one data point..\n\t\tif(Array.isArray(requestData))\n\t\t{\n\t\t\t// .. throw an error, as this breaks our expectation for subscriptions.\n\t\t\tthrow(new Error('Subscription request returned an more than one data point.'));\n\t\t}\n\n\t\t// Construct a notification structure to package the initial result as a notification.\n\t\tconst notification: RPCNotification =\n\t\t{\n\t\t\tjsonrpc: '2.0',\n\t\t\tmethod: method,\n\t\t\tparams: [ ...parameters, requestData ],\n\t\t};\n\n\t\t// Manually emit an event for the initial response.\n\t\tthis.emit('notification', notification);\n\n\t\t// Try to update the chain height.\n\t\tthis.updateChainHeightFromHeadersNotifications(notification);\n\t}\n\n\t/**\n\t * Unsubscribes to the method at the server and removes any callback functions\n\t * when there are no more subscriptions for the method.\n\t *\n\t * @param method - a previously subscribed to method.\n\t * @param parameters - one or more parameters for the method.\n\t *\n\t * @throws {Error} if no subscriptions exist for the combination of the provided `method` and `parameters.\n\t * @throws {Error} if the client is disconnected.\n\t * @returns a promise resolving when the subscription is removed.\n\t */\n\tasync unsubscribe(method: string, ...parameters: RPCParameter[]): Promise<void>\n\t{\n\t\t// Throw an error if the client is disconnected.\n\t\tif(this.connection.status !== ConnectionStatus.CONNECTED)\n\t\t{\n\t\t\tthrow(new Error(`Unable to send unsubscribe request to a disconnected server '${this.hostIdentifier}'.`));\n\t\t}\n\n\t\t// If this method has no subscriptions..\n\t\tif(!this.subscriptionMethods[method])\n\t\t{\n\t\t\t// Reject this promise with an explanation.\n\t\t\tthrow(new Error(`Cannot unsubscribe from '${method}' since the method has no subscriptions.`));\n\t\t}\n\n\t\t// Pack up the parameters as a long string.\n\t\tconst subscriptionParameters = JSON.stringify(parameters);\n\n\t\t// If the method payload could not be located..\n\t\tif(!this.subscriptionMethods[method].has(subscriptionParameters))\n\t\t{\n\t\t\t// Reject this promise with an explanation.\n\t\t\tthrow(new Error(`Cannot unsubscribe from '${method}' since it has no subscription with the given parameters.`));\n\t\t}\n\n\t\t// Remove this specific subscription payload from internal tracking.\n\t\tthis.subscriptionMethods[method].delete(subscriptionParameters);\n\n\t\t// Send unsubscription request to the server\n\t\t// NOTE: As a convenience we allow users to define the method as the subscribe or unsubscribe version.\n\t\tawait this.request(method.replace('.subscribe', '.unsubscribe'), ...parameters);\n\n\t\t// Write a log message.\n\t\tdebug.client(`Unsubscribed from '${String(method)}' for the '${subscriptionParameters}' parameters.`);\n\t}\n\n\t/**\n\t * Restores existing subscriptions without updating status or triggering manual callbacks.\n\t *\n\t * @throws {Error} if subscription data cannot be found for all stored event names.\n\t * @throws {Error} if the client is disconnected.\n\t * @returns a promise resolving to true when the subscriptions are restored.\n\t *\n\t * @ignore\n\t */\n\tprivate async resubscribeOnConnect(): Promise<void>\n\t{\n\t\t// Write a log message.\n\t\tdebug.client(`Connected to '${this.hostIdentifier}'.`);\n\n\t\t// Synchronize with the underlying connection status.\n\t\tthis.handleConnectionStatusChanges('connected');\n\n\t\t// Initialize an empty list of resubscription promises.\n\t\tconst resubscriptionPromises = [];\n\n\t\t// For each method we have a subscription for..\n\t\tfor(const method in this.subscriptionMethods)\n\t\t{\n\t\t\t// .. and for each parameter we have previously been subscribed to..\n\t\t\tfor(const parameterJSON of this.subscriptionMethods[method].values())\n\t\t\t{\n\t\t\t\t// restore the parameters from JSON.\n\t\t\t\tconst parameters = JSON.parse(parameterJSON);\n\n\t\t\t\t// Send a subscription request.\n\t\t\t\tresubscriptionPromises.push(this.subscribe(method, ...parameters));\n\t\t\t}\n\n\t\t\t// Wait for all re-subscriptions to complete.\n\t\t\tawait Promise.all(resubscriptionPromises);\n\t\t}\n\n\t\t// Write a log message if there was any subscriptions to restore.\n\t\tif(resubscriptionPromises.length > 0)\n\t\t{\n\t\t\tdebug.client(`Restored ${resubscriptionPromises.length} previous subscriptions for '${this.hostIdentifier}'`);\n\t\t}\n\t}\n\n\t/**\n\t * Parser messages from the remote server to resolve request promises and emit subscription events.\n\t *\n\t * @param message - the response message\n\t *\n\t * @throws {Error} if the message ID does not match an existing request.\n\t * @ignore\n\t */\n\tresponse(message: RPCResponse): void\n\t{\n\t\t// If the received message is a notification, we forward it to all event listeners\n\t\tif(isRPCNotification(message))\n\t\t{\n\t\t\t// Write a log message.\n\t\t\tdebug.client(`Received notification for '${message.method}' from '${this.hostIdentifier}'`);\n\n\t\t\t// Forward the message content to all event listeners.\n\t\t\tthis.emit('notification', message);\n\n\t\t\t// Try to update the chain height.\n\t\t\tthis.updateChainHeightFromHeadersNotifications(message);\n\n\t\t\t// Return since it does not have an associated request resolver\n\t\t\treturn;\n\t\t}\n\n\t\t// If the response ID is null we cannot use it to index our request resolvers\n\t\tif(message.id === null)\n\t\t{\n\t\t\t// Throw an internal error, this should not happen.\n\t\t\tthrow(new Error('Internal error: Received an RPC response with ID null.'));\n\t\t}\n\n\t\t// Look up which request promise we should resolve this.\n\t\tconst requestResolver = this.requestResolvers[message.id];\n\n\t\t// If we do not have a request resolver for this response message..\n\t\tif(!requestResolver)\n\t\t{\n\t\t\t// Log that a message was ignored since the request has already been rejected.\n\t\t\tdebug.warning(`Ignoring response #${message.id} as the request has already been rejected.`);\n\n\t\t\t// Return as this has now been fully handled.\n\t\t\treturn;\n\t\t}\n\n\t\t// Remove the promise from the request list.\n\t\tdelete this.requestResolvers[message.id];\n\n\t\t// If the message contains an error..\n\t\tif(isRPCErrorResponse(message))\n\t\t{\n\t\t\t// Forward the message error to the request resolver and omit the `result` parameter.\n\t\t\trequestResolver(new Error(message.error.message));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Forward the message content to the request resolver and omit the `error` parameter\n\t\t\t// (by setting it to undefined).\n\t\t\trequestResolver(undefined, message.result);\n\n\t\t\t// Attempt to extract genesis hash from feature requests.\n\t\t\tthis.storeGenesisHashFromFeaturesResponse(message);\n\t\t}\n\t}\n\n\t/**\n\t * Callback function that is called when connection to the Electrum server is lost.\n\t * Aborts all active requests with an error message indicating that connection was lost.\n\t *\n\t * @ignore\n\t */\n\tasync onConnectionDisconnect(): Promise<void>\n\t{\n\t\t// Loop over active requests\n\t\tfor(const resolverId in this.requestResolvers)\n\t\t{\n\t\t\t// Extract request resolver for readability\n\t\t\tconst requestResolver = this.requestResolvers[resolverId];\n\n\t\t\t// Resolve the active request with an error indicating that the connection was lost.\n\t\t\trequestResolver(new Error('Connection lost'));\n\n\t\t\t// Remove the promise from the request list.\n\t\t\tdelete this.requestResolvers[resolverId];\n\t\t}\n\n\t\t// Synchronize with the underlying connection status.\n\t\tthis.handleConnectionStatusChanges('disconnected');\n\t}\n\n\t/**\n\t * Stores the server provider software version field on successful version negotiation.\n\t *\n\t * @ignore\n\t */\n\tasync storeSoftwareVersion(versionStatement): Promise<void>\n\t{\n\t\t// TODO: handle failed version negotiation better.\n\t\tif(versionStatement.error)\n\t\t{\n\t\t\t// Do nothing.\n\t\t\treturn;\n\t\t}\n\n\t\t// Store the software version.\n\t\tthis.software = versionStatement.software;\n\t}\n\n\t/**\n\t * Updates the last received timestamp.\n\t *\n\t * @ignore\n\t */\n\tasync updateLastReceivedTimestamp(): Promise<void>\n\t{\n\t\t// Update the timestamp for when we last received data.\n\t\tthis.lastReceivedTimestamp = Date.now();\n\t}\n\n\t/**\n\t * Checks if the provided message is a response to a headers subscription,\n\t * and if so updates the locally stored chain height value for this client.\n\t *\n\t * @ignore\n\t */\n\tasync updateChainHeightFromHeadersNotifications(message): Promise<void>\n\t{\n\t\t// If the message is a notification for a new chain height..\n\t\tif(message.method === 'blockchain.headers.subscribe')\n\t\t{\n\t\t\t// ..also store the updated chain height locally.\n\t\t\tthis.chainHeight = message.params[0].height;\n\t\t}\n\t}\n\n\t/**\n\t * Checks if the provided message is a response to a server.features request,\n\t * and if so stores the genesis hash for this client locally.\n\t *\n\t * @ignore\n\t */\n\tasync storeGenesisHashFromFeaturesResponse(message): Promise<void>\n\t{\n\t\ttry\n\t\t{\n\t\t\t// If the message is a response to a features request..\n\t\t\tif(typeof message.result.genesis_hash !== 'undefined')\n\t\t\t{\n\t\t\t\t// ..store the genesis hash locally.\n\t\t\t\tthis.genesisHash = message.result.genesis_hash;\n\t\t\t}\n\t\t}\n\t\tcatch (_ignored)\n\t\t{\n\t\t\t// Do nothing.\n\t\t}\n\t}\n\n\t/**\n\t * Helper function to synchronize state and events with the underlying connection.\n\t */\n\tasync handleConnectionStatusChanges(eventName): Promise<void>\n\t{\n\t\t// Re-emit the event.\n\t\tthis.emit(eventName);\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 connecting: [];\n\tpublic readonly connected: [];\n\tpublic readonly disconnecting: [];\n\tpublic readonly disconnected: [];\n\tpublic readonly reconnecting: [];\n\tpublic readonly notification: [ RPCNotification ];\n\tpublic readonly error: [ Error ];\n}\n\n// Export the client.\nexport default ElectrumClient;\n"],"mappings":";;;;;;;;;;;AAOA,IAAa,mBAAb,MACA;;;;;;;;;;CAUC,OAAO,mBAAmB,QAAgB,YAA4B,WACtE;EAKC,OAAO,KAAK,UAAU;GAAU;GAAQ,QAAQ;GAAY,IAAI;EAAU,CAAC;CAC5E;;;;;;CAOA,WAAW,gBACX;EACC,OAAO;CACR;;;;;;CAOA,WAAW,qBACX;EACC,OAAO;CACR;AACD;;;ACYA,MAAa,qBAAqB,SAAS,SAC3C;CACC,OAAO,QAAQ,WAAW,WAAW;AACtC;AAEA,MAAa,iBAAiB,SAAS,SACvC;CACC,OAAO,QAAQ,WAAW,YAAY;AACvC;AAEA,MAAa,oBAAoB,SAAS,SAC1C;CACC,OAAO,EAAE,QAAQ,YAAY,YAAY;AAC1C;AAEA,MAAa,eAAe,SAAS,SACrC;CACC,OAAO,QAAQ,WAAW,YAAY;AACvC;;;;;;;;;;;;ACnEA,IAAY,mBAAL,yBAAA,kBAAA;CAEN,iBAAA,iBAAA,kBAAA,KAAA;CACA,iBAAA,iBAAA,eAAA,KAAA;CACA,iBAAA,iBAAA,mBAAA,KAAA;CACA,iBAAA,iBAAA,gBAAA,KAAA;CACA,iBAAA,iBAAA,kBAAA,KAAA;;AACD,EAAA,CAAA,CAAA;;;;;;ACuKA,MAAa,oBAAoB,SAAS,QAC1C;CACC,OAAO,WAAW;AACnB;;;;AAKA,MAAa,sBAAsB,SAAS,QAC5C;CACC,OAAO,cAAc,UAAU,cAAc;AAC9C;;;;;;ACnLA,IAAa,qBAAb,cAAwC,aACxC;CA+BU;CACA;CACA;CACA;CAhCT,SAAO;CAGP;CAGA;CAGA;CACA;CAGA,gBAAuC,CAAC;CAGxC,gBAAwB;;;;;;;;;;;CAYxB,YACC,aACA,SACA,kBACA,SAED;EAEC,MAAM;EAPE,KAAA,cAAA;EACA,KAAA,UAAA;EACA,KAAA,mBAAA;EACA,KAAA,UAAA;EAOR,IAAG,CAAC,iBAAiB,cAAc,KAAK,OAAO,GAG9C,MAAM,IAAI,MAAM,4BAA4B,QAAQ,0CAA0C;EAI/F,IAAG,OAAO,qBAAqB,UAG9B,KAAK,SAAS,IAAI,kBAAkB,kBAAkB,KAAK,OAAO;OAKlE,KAAK,SAAS;EAIf,KAAK,OAAO,GAAG,aAAa,KAAK,gBAAgB,KAAK,IAAI,CAAC;EAC3D,KAAK,OAAO,GAAG,gBAAgB,KAAK,mBAAmB,KAAK,IAAI,CAAC;EAGjE,KAAK,OAAO,GAAG,QAAQ,KAAK,kBAAkB,KAAK,IAAI,CAAC;CACzD;CAGA,IAAI,iBACJ;EACC,OAAO,KAAK,OAAO;CACpB;CAGA,IAAI,YACJ;EACC,OAAO,KAAK,OAAO,QAAQ;CAC5B;;;;;;;;CASA,kBAAkB,MAClB;EAEC,KAAK,wBAAwB,KAAK,IAAI;EAGtC,KAAK,KAAK,UAAU;EAGpB,KAAK,cAAc,SAAS,UAAU,aAAa,KAAK,CAAC;EACzD,KAAK,cAAc,SAAS;EAG5B,KAAK,iBAAiB;EAGtB,OAAM,KAAK,cAAc,SAAS,iBAAiB,kBAAkB,GACrE;GAEC,MAAM,iBAAiB,KAAK,cAAc,MAAM,iBAAiB,kBAAkB;GAGnF,OAAM,eAAe,SAAS,GAC9B;IAEC,MAAM,uBAAuB,OAAO,eAAe,MAAM,CAAC;IAG1D,IAAI,gBAAgB,MAAM,sBAAsB,MAAM,KAAK,QAAQ,YAAY,uBAAuB,UAAU;IAGhH,IAAG,CAAC,MAAM,QAAQ,aAAa,GAE9B,gBAAgB,CAAE,aAAc;IAIjC,OAAM,cAAc,SAAS,GAC7B;KAEC,MAAM,mBAAmB,cAAc,MAAM;KAG7C,IAAG,kBAAkB,gBAAgB,GACrC;MAEC,KAAK,KAAK,YAAY,gBAAgB;MAGtC;KACD;KAGA,IAAG,iBAAiB,OAAO,sBAC3B;MACC,IAAG,mBAAmB,gBAAgB,GAGrC,KAAK,KAAK,WAAW,EAAE,OAAO,iBAAiB,MAAM,CAAC;WAGvD;OAEC,MAAM,CAAE,UAAU,YAAa,iBAAiB;OAGhD,KAAK,KAAK,WAAW;QAAE;QAAU;OAAS,CAAC;MAC5C;MAGA;KACD;KAGA,IAAG,iBAAiB,OAAO,aAG1B;KAID,KAAK,KAAK,YAAY,gBAAgB;IACvC;GACD;GAGA,KAAK,gBAAgB,eAAe,MAAM,KAAK;EAChD;CACD;;;;;;;CAQA,MAAM,OACN;EAEC,MAAM,KAAK,+BAA+B,KAAK,eAAe,EAAE;EAGhE,MAAM,UAAU,iBAAiB,mBAAmB,eAAe,CAAC,GAAG,WAAW;EAMlF,OAHe,KAAK,KAAK,OAGb;CACb;;;;;;;CAQA,MAAM,UACN;EAEC,IAAG,KAAK,WAAA,GAEP;EAID,KAAK,SAAA;EAGL,KAAK,KAAK,YAAY;EAItB,MAAM,sBAAsB,SAAgC,WAC5D;GAEC,KAAK,KAAK,mBACV;IAEC,KAAK,eAAe,gBAAgB,MAAM;IAG1C,QAAQ;GACT,CAAC;GAGD,KAAK,KAAK,sBACV;IAEC,KAAK,eAAe,aAAa,OAAO;IAGxC,OAAO;GACR,CAAC;GAGD,KAAK,OAAO,QAAQ;EACrB;EAGA,MAAM,IAAI,QAAc,kBAAkB;CAC3C;;;;CAKA,MAAM,YACN;EAEC,MAAM,KAAK,oBAAoB;EAG/B,MAAM,QAAQ,2BAA2B,KAAK,eAAe,IAAI;EAGjE,KAAK,SAAA;EAGL,KAAK,KAAK,cAAc;EAGxB,KAAK,OAAO,WAAW;EAEvB,IACA;GAEC,MAAM,KAAK,QAAQ;EACpB,SACO,QACP,CAEA;CACD;;;;CAKA,sBACA;EAEC,IAAG,KAAK,gBAEP,aAAa,KAAK,cAAc;EAIjC,KAAK,iBAAiB,KAAA;CACvB;;;;CAKA,sBACA;EAEC,IAAG,KAAK,gBAEP,aAAa,KAAK,cAAc;EAIjC,KAAK,iBAAiB,KAAA;CACvB;;;;CAKA,sBACA;EAEC,IAAG,CAAC,KAAK,gBAGR,KAAK,iBAAiB,WAAW,KAAK,KAAK,KAAK,IAAI,GAAG,KAAK,QAAQ,mCAAmC;CAEzG;;;;;;;;;CAUA,MAAM,WAAW,QAAiB,OAAO,cAAuB,MAChE;EAEC,IAAG,KAAK,WAAA,KAA4C,CAAC,OAGpD,OAAO;EAMR,IAAG,aAGF,KAAK,SAAA;EAIN,KAAK,KAAK,eAAe;EAGzB,MAAM,KAAK,oBAAoB;EAG/B,MAAM,KAAK,oBAAoB;EAE/B,MAAM,sBAAsB,YAC5B;GAEC,KAAK,KAAK,sBAAsB,QAAQ,IAAI,CAAC;GAG7C,KAAK,OAAO,WAAW;EACxB;EAGA,OAAO,IAAI,QAAiB,kBAAkB;CAC/C;;;;;;;;;CAUA,MAAM,KAAK,SACX;EAEC,KAAK,oBAAoB;EAGzB,MAAM,cAAc,KAAK,IAAI;EAG7B,MAAM,oBAAoB,WAAW,KAAK,WAAW,KAAK,MAAM,WAAW,GAAG,KAAK,OAAO,QAAQ,qBAAqB;EAGvH,KAAK,cAAc,KAAK,iBAAiB;EAGzC,KAAK,oBAAoB;EAGzB,OAAO,KAAK,OAAO,MAAM,UAAU,iBAAiB,kBAAkB;CACvE;;;;;CAQA,WAAW,eACX;EAEC,IAAG,OAAO,KAAK,qBAAqB,IAAI,eACxC;GAEC,IAAI,KAAK,WAAA,KAA8C,KAAK,WAAA,GAI3D;GAID,KAAK,oBAAoB;GAGzB,MAAM,QAAQ,kBAAkB,KAAK,eAAe,aAAa;GAKjE,KAAK,OAAO,WAAW;EACxB;CACD;;;;CAKA,MAAM,kBACN;EAEC,KAAK,oBAAoB;EAGzB,KAAK,wBAAwB,KAAK,IAAI;EAGtC,KAAK,oBAAoB;EAGzB,MAAM,IAAI,QAAc,KAAK,iBAAiB,KAAK,IAAI,CAAC;EAGxD,KAAK,KAAK,WAAW;CACtB;;;;CAKA,qBACA;EAEC,KAAK,oBAAoB;EAGzB,IAAG,KAAK,WAAA,GACR;GAEC,KAAK,SAAA;GAGL,KAAK,KAAK,cAAc;GAGxB,KAAK,oBAAoB;GAGzB,KAAK,mBAAmB;GAGxB,MAAM,QAAQ,sBAAsB,KAAK,eAAe,GAAG;EAC5D,OAEA;GAEC,IAAG,KAAK,WAAA,GAGP,MAAM,OAAO,oBAAoB,KAAK,eAAe,uCAAuC,KAAK,QAAQ,6BAA6B,IAAK,UAAU;GAYtJ,KAAK,SAAA;GAGL,KAAK,KAAK,cAAc;GAGxB,IAAG,CAAC,KAAK,gBAGR,KAAK,iBAAiB,WAAW,KAAK,UAAU,KAAK,IAAI,GAAG,KAAK,QAAQ,0BAA0B;EAErG;CACD;;;;CAKA,cAAc,OACd;EAKC,IAAG,OAAO,UAAU,aAGnB;EAID,MAAM,OAAO,mBAAmB,KAAK,eAAe,OAAO,KAAK;CACjE;;;;;;;CAQA,MAAM,iBAAiB,SAAgC,QACvD;EACC,MAAM,iBACN;GAEC,KAAK,SAAA;GAGL,KAAK,KAAK,cAAc;GAGxB,OAAO,4BAA4B,KAAK,eAAe,SAAS;EACjE;EAGA,MAAM,QAAQ,+BAA+B,KAAK,QAAQ,SAAS,KAAK,eAAe,GAAG;EAG1F,KAAK,OAAO,KAAK,gBAAgB,QAAQ;EAGzC,MAAM,iBAAiB,iBAAiB,mBAAmB,kBAAkB,CAAE,KAAK,aAAa,KAAK,OAAQ,GAAG,oBAAoB;EAGrI,MAAM,oBAAoB,YAC1B;GAEC,IAAG,kBAAkB,OAAO,GAC5B;IAEC,KAAK,WAAW,IAAI;IAGpB,MAAM,eAAe;IAGrB,MAAM,OAAO,0BAA0B,KAAK,eAAe,UAAU,cAAc;IAGnF,OAAO,YAAY;GACpB,OAGK,IAAI,QAAQ,aAAa,KAAK,WAAa,GAAG,QAAQ,SAAS,QAAQ,KAAK,WAAa,GAAG,QAAQ,SAAS,UAAU,KAAK,SACjI;IAEC,KAAK,WAAW,IAAI;IAGpB,MAAM,eAAe,6CAA6C,QAAQ,SAAS,OAAO,KAAK,QAAQ;IAGvG,MAAM,OAAO,0BAA0B,KAAK,eAAe,UAAU,cAAc;IAGnF,OAAO,YAAY;GACpB,OAEA;IAEC,MAAM,QAAQ,+BAA+B,QAAQ,SAAS,SAAS,KAAK,eAAe,gBAAgB,QAAQ,SAAS,EAAE;IAG9H,KAAK,SAAA;IAGL,QAAQ;GACT;EACD;EAGA,KAAK,KAAK,WAAW,gBAAgB;EAGrC,KAAK,KAAK,cAAc;CACzB;AACD;;;ACjnBA,MAAM,2BAA2B;;;;AAKjC,MAAa,wBACb;CAEC,WAAW;CAGX,qCAAqC,IAAI;CAGzC,4BAA4B,IAAI;CAGhC,uCAAuC,IAAI;AAC5C;;;;;;ACNA,IAAM,iBAAN,cAA0E,aAC1E;CAyDS;CACA;CACA;CACA;;;;CAxDR;;;;;CAMA;;;;;CAMA;;;;CAKA;;;;CAKA,IAAW,SACX;EACC,OAAO,KAAK,WAAW;CACxB;CAGA;CAGA,sBAA2D,CAAC;CAG5D,YAAoB;CAGpB,mBAAiE,CAAC;CAGlE,iBAAyB,IAAI,MAAM;;;;;;;;;;;CAYnC,YACC,aACA,SACA,kBACA,UAAkD,CAAC,GAEpD;EAEC,MAAM;EAPC,KAAA,cAAA;EACA,KAAA,UAAA;EACA,KAAA,mBAAA;EACA,KAAA,UAAA;EAOP,MAAM,iBAAyC;GAAE,GAAG;GAAuB,GAAG;EAAQ;EAGtF,KAAK,aAAa,IAAI,mBAAmB,aAAa,SAAS,kBAAkB,cAAc;CAChG;CAGA,IAAI,iBACJ;EACC,OAAO,KAAK,WAAW;CACxB;CAGA,IAAI,YACJ;EACC,OAAO,KAAK,WAAW;CACxB;;;;;;;CAQA,MAAM,UACN;EAEC,OAAO,KAAK,eAAe,aAAa,YACxC;GAEC,IAAG,KAAK,WAAW,WAAA,GAElB;GAID,KAAK,WAAW,GAAG,YAAY,KAAK,SAAS,KAAK,IAAI,CAAC;GAGvD,KAAK,WAAW,GAAG,aAAa,KAAK,qBAAqB,KAAK,IAAI,CAAC;GACpE,KAAK,WAAW,GAAG,gBAAgB,KAAK,uBAAuB,KAAK,IAAI,CAAC;GAGzE,KAAK,WAAW,GAAG,cAAc,KAAK,8BAA8B,KAAK,MAAM,YAAY,CAAC;GAC5F,KAAK,WAAW,GAAG,iBAAiB,KAAK,8BAA8B,KAAK,MAAM,eAAe,CAAC;GAClG,KAAK,WAAW,GAAG,gBAAgB,KAAK,8BAA8B,KAAK,MAAM,cAAc,CAAC;GAGhG,KAAK,WAAW,GAAG,WAAW,KAAK,qBAAqB,KAAK,IAAI,CAAC;GAClE,KAAK,WAAW,GAAG,YAAY,KAAK,4BAA4B,KAAK,IAAI,CAAC;GAG1E,KAAK,WAAW,GAAG,SAAS,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;GAGzD,MAAM,KAAK,WAAW,QAAQ;EAC/B,CAAC;CACF;;;;;;;;;CAUA,MAAM,WAAW,QAAiB,OAAO,sBAA+B,OACxE;EAEC,OAAO,KAAK,eAAe,aAAa,YACxC;GACC,IAAG,CAAC,qBACJ;IAEC,KAAK,mBAAmB;IAGxB,KAAK,sBAAsB,CAAC;GAC7B;GAGA,OAAO,KAAK,WAAW,WAAW,KAAK;EACxC,CAAC;CACF;;;;;;;;;;CAWA,MAAM,QAAQ,QAAgB,GAAG,YACjC;EAEC,IAAG,KAAK,WAAW,WAAA,GAGlB,MAAM,IAAI,MAAM,oDAAoD,KAAK,eAAe,GAAG;EAI5F,KAAK,aAAa;EAGlB,MAAM,KAAK,KAAK;EAGhB,MAAM,UAAU,iBAAiB,mBAAmB,QAAQ,YAAY,EAAE;EAG1E,MAAM,mBAAmB,YACzB;GAEC,KAAK,iBAAiB,OAAO,OAAe,SAC5C;IAEC,IAAG,OAGF,QAAQ,KAAK;SAKb,QAAQ,IAAI;GAEd;GAGA,KAAK,WAAW,KAAK,OAAO;EAC7B;EAGA,MAAM,QAAQ,oBAAoB,OAAO,QAAQ,KAAK,eAAe,EAAE;EAGvE,OAAO,IAAI,QAAiC,eAAe;CAC5D;;;;;;;;;;;;CAaA,MAAM,UAAU,QAAgB,GAAG,YACnC;EAEC,IAAG,CAAC,KAAK,oBAAoB,SAE5B,KAAK,oBAAoB,0BAAU,IAAI,IAAY;EAIpD,KAAK,oBAAoB,OAAO,CAAC,IAAI,KAAK,UAAU,UAAU,CAAC;EAG/D,MAAM,cAAc,MAAM,KAAK,QAAQ,QAAQ,GAAG,UAAU;EAG5D,IAAG,uBAAuB,OAEzB,MAAM;EAIP,IAAG,MAAM,QAAQ,WAAW,GAG3B,MAAM,IAAI,MAAM,4DAA4D;EAI7E,MAAM,eACN;GACC,SAAS;GACD;GACR,QAAQ,CAAE,GAAG,YAAY,WAAY;EACtC;EAGA,KAAK,KAAK,gBAAgB,YAAY;EAGtC,KAAK,0CAA0C,YAAY;CAC5D;;;;;;;;;;;;CAaA,MAAM,YAAY,QAAgB,GAAG,YACrC;EAEC,IAAG,KAAK,WAAW,WAAA,GAElB,MAAM,IAAI,MAAM,gEAAgE,KAAK,eAAe,GAAG;EAIxG,IAAG,CAAC,KAAK,oBAAoB,SAG5B,MAAM,IAAI,MAAM,4BAA4B,OAAO,yCAAyC;EAI7F,MAAM,yBAAyB,KAAK,UAAU,UAAU;EAGxD,IAAG,CAAC,KAAK,oBAAoB,OAAO,CAAC,IAAI,sBAAsB,GAG9D,MAAM,IAAI,MAAM,4BAA4B,OAAO,0DAA0D;EAI9G,KAAK,oBAAoB,OAAO,CAAC,OAAO,sBAAsB;EAI9D,MAAM,KAAK,QAAQ,OAAO,QAAQ,cAAc,cAAc,GAAG,GAAG,UAAU;EAG9E,MAAM,OAAO,sBAAsB,OAAO,MAAM,EAAE,aAAa,uBAAuB,cAAc;CACrG;;;;;;;;;;CAWA,MAAc,uBACd;EAEC,MAAM,OAAO,iBAAiB,KAAK,eAAe,GAAG;EAGrD,KAAK,8BAA8B,WAAW;EAG9C,MAAM,yBAAyB,CAAC;EAGhC,KAAI,MAAM,UAAU,KAAK,qBACzB;GAEC,KAAI,MAAM,iBAAiB,KAAK,oBAAoB,OAAO,CAAC,OAAO,GACnE;IAEC,MAAM,aAAa,KAAK,MAAM,aAAa;IAG3C,uBAAuB,KAAK,KAAK,UAAU,QAAQ,GAAG,UAAU,CAAC;GAClE;GAGA,MAAM,QAAQ,IAAI,sBAAsB;EACzC;EAGA,IAAG,uBAAuB,SAAS,GAElC,MAAM,OAAO,YAAY,uBAAuB,OAAO,+BAA+B,KAAK,eAAe,EAAE;CAE9G;;;;;;;;;CAUA,SAAS,SACT;EAEC,IAAG,kBAAkB,OAAO,GAC5B;GAEC,MAAM,OAAO,8BAA8B,QAAQ,OAAO,UAAU,KAAK,eAAe,EAAE;GAG1F,KAAK,KAAK,gBAAgB,OAAO;GAGjC,KAAK,0CAA0C,OAAO;GAGtD;EACD;EAGA,IAAG,QAAQ,OAAO,MAGjB,MAAM,IAAI,MAAM,wDAAwD;EAIzE,MAAM,kBAAkB,KAAK,iBAAiB,QAAQ;EAGtD,IAAG,CAAC,iBACJ;GAEC,MAAM,QAAQ,sBAAsB,QAAQ,GAAG,2CAA2C;GAG1F;EACD;EAGA,OAAO,KAAK,iBAAiB,QAAQ;EAGrC,IAAG,mBAAmB,OAAO,GAG5B,gBAAgB,IAAI,MAAM,QAAQ,MAAM,OAAO,CAAC;OAGjD;GAGC,gBAAgB,KAAA,GAAW,QAAQ,MAAM;GAGzC,KAAK,qCAAqC,OAAO;EAClD;CACD;;;;;;;CAQA,MAAM,yBACN;EAEC,KAAI,MAAM,cAAc,KAAK,kBAC7B;GAEC,MAAM,kBAAkB,KAAK,iBAAiB;GAG9C,gCAAgB,IAAI,MAAM,iBAAiB,CAAC;GAG5C,OAAO,KAAK,iBAAiB;EAC9B;EAGA,KAAK,8BAA8B,cAAc;CAClD;;;;;;CAOA,MAAM,qBAAqB,kBAC3B;EAEC,IAAG,iBAAiB,OAGnB;EAID,KAAK,WAAW,iBAAiB;CAClC;;;;;;CAOA,MAAM,8BACN;EAEC,KAAK,wBAAwB,KAAK,IAAI;CACvC;;;;;;;CAQA,MAAM,0CAA0C,SAChD;EAEC,IAAG,QAAQ,WAAW,gCAGrB,KAAK,cAAc,QAAQ,OAAO,EAAE,CAAC;CAEvC;;;;;;;CAQA,MAAM,qCAAqC,SAC3C;EACC,IACA;GAEC,IAAG,OAAO,QAAQ,OAAO,iBAAiB,aAGzC,KAAK,cAAc,QAAQ,OAAO;EAEpC,SACO,UACP,CAEA;CACD;;;;CAKA,MAAM,8BAA8B,WACpC;EAEC,KAAK,KAAK,SAAS;CACpB;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;AACD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electrum-cash/network",
3
- "version": "4.2.2",
3
+ "version": "4.3.0-development.16529028959",
4
4
  "description": "@electrum-cash/network is a lightweight JavaScript library that lets you connect with one or more Electrum servers.",
5
5
  "keywords": [
6
6
  "electrum",
@@ -37,7 +37,7 @@
37
37
  ],
38
38
  "scripts": {
39
39
  "build": "tsdown --clean --sourcemap source/index.ts",
40
- "analyze": "tsdown --clean --no-fixed-extension --sourcemap source/index.ts && esbuild-analyzer dist/",
40
+ "analyze": "node tools/analyze_bundle.ts",
41
41
  "docs": "typedoc --hideGenerator --categorizeByGroup",
42
42
  "style": "eslint",
43
43
  "syntax": "tsc --noEmit",
@@ -45,28 +45,28 @@
45
45
  "test": "vitest --dir test/ --test-timeout=15000 --run --coverage"
46
46
  },
47
47
  "devDependencies": {
48
- "@chalp/eslint-airbnb": "^1.3.0",
49
48
  "@electrum-cash/eslint-config": "gitlab:electrum-cash/eslint-config",
50
- "@electrum-cash/tcp-socket": "^1.0.0",
49
+ "@electrum-cash/tcp-socket": "^4.1.1",
51
50
  "@generalprotocols/cspell-dictionary": "git+https://gitlab.com/GeneralProtocols/cspell-dictionary.git",
52
51
  "@stylistic/eslint-plugin": "^5.7.0",
53
52
  "@types/debug": "^4.1.6",
54
53
  "@typescript-eslint/eslint-plugin": "^8.53.0",
55
54
  "@typescript-eslint/parser": "^8.53.0",
56
- "@vitest/coverage-v8": "^3.2.4",
57
- "@viz-kit/esbuild-analyzer": "^1.0.0",
58
- "cspell": "^8.6.0",
59
- "eslint": "^9.39.2",
60
- "tsdown": "^0.20.0-beta.1",
55
+ "@vitest/coverage-v8": "^5.0.0",
56
+ "cspell": "^10.3.0",
57
+ "eslint": "^10.10.0",
58
+ "sonda": "^0.14.0",
59
+ "tsdown": "^0.23.0",
61
60
  "typedoc": "^0.26.8",
62
61
  "typedoc-plugin-coverage": "^3.1.0",
63
62
  "typescript": "^5.1.3",
64
63
  "typescript-eslint": "^8.53.0",
65
- "vitest": "^3.2.4"
64
+ "vitest": "^5.0.0"
66
65
  },
67
66
  "dependencies": {
68
67
  "@electrum-cash/debug-logs": "^1.0.0",
69
- "@electrum-cash/web-socket": "^1.2.3",
68
+ "@electrum-cash/socket": "^4.0.1",
69
+ "@electrum-cash/web-socket": "^4.0.2",
70
70
  "async-mutex": "^0.5.0",
71
71
  "debug": "^4.3.2",
72
72
  "eventemitter3": "^5.0.1",