@hardkas/kaspa-rpc 0.5.1-alpha → 0.5.2-alpha
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.ts +34 -2
- package/dist/index.js +176 -6
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -120,7 +120,6 @@ interface RpcHealthResult {
|
|
|
120
120
|
readonly circuitState?: string | undefined;
|
|
121
121
|
readonly stale?: boolean | undefined;
|
|
122
122
|
}
|
|
123
|
-
|
|
124
123
|
declare function checkKaspaRpcHealth(options?: RpcHealthCheckOptions): Promise<RpcHealthResult>;
|
|
125
124
|
declare function waitForKaspaRpcReady(options?: RpcReadinessWaitOptions): Promise<RpcHealthResult>;
|
|
126
125
|
|
|
@@ -171,6 +170,39 @@ declare class LoadBalancedRpcProvider implements KaspaRpcClient {
|
|
|
171
170
|
private withFailover;
|
|
172
171
|
}
|
|
173
172
|
|
|
173
|
+
interface WrpcRequest {
|
|
174
|
+
id: number;
|
|
175
|
+
method: string;
|
|
176
|
+
params?: Record<string, unknown>;
|
|
177
|
+
}
|
|
178
|
+
interface WrpcResponse {
|
|
179
|
+
id?: number;
|
|
180
|
+
result?: unknown;
|
|
181
|
+
params?: unknown;
|
|
182
|
+
error?: {
|
|
183
|
+
message: string;
|
|
184
|
+
code?: number;
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
declare class KaspaWrpcClient {
|
|
188
|
+
private url;
|
|
189
|
+
private ws;
|
|
190
|
+
private requestId;
|
|
191
|
+
private pending;
|
|
192
|
+
onNotification?: (msg: WrpcResponse) => void;
|
|
193
|
+
debug: boolean;
|
|
194
|
+
constructor(url: string);
|
|
195
|
+
getUrl(): string;
|
|
196
|
+
connect(timeoutMs?: number): Promise<void>;
|
|
197
|
+
request(method: string, params?: Record<string, unknown>, timeoutMs?: number): Promise<unknown>;
|
|
198
|
+
getServerInfo(): Promise<unknown>;
|
|
199
|
+
getBlockDagInfo(): Promise<unknown>;
|
|
200
|
+
getVirtualSelectedParentBlueScore(): Promise<unknown>;
|
|
201
|
+
getUtxosByAddresses(addresses: string[]): Promise<unknown>;
|
|
202
|
+
ping(): Promise<boolean>;
|
|
203
|
+
disconnect(): void;
|
|
204
|
+
}
|
|
205
|
+
|
|
174
206
|
interface KaspaNodeInfo {
|
|
175
207
|
serverVersion?: string | undefined;
|
|
176
208
|
isSynced?: boolean | undefined;
|
|
@@ -290,4 +322,4 @@ declare class MockKaspaRpcClient implements KaspaRpcClient {
|
|
|
290
322
|
close(): Promise<void>;
|
|
291
323
|
}
|
|
292
324
|
|
|
293
|
-
export { type BlockDagInfo, type CircuitBreakerOptions, CircuitState, JsonWrpcKaspaClient, type JsonWrpcKaspaClientOptions, type KaspaAddressBalance, KaspaJsonRpcClient, type KaspaNodeInfo, type KaspaRpcClient, type KaspaRpcHealth, type KaspaRpcOutpoint, type KaspaRpcUtxo, type KaspaSubmitTransactionResult, LoadBalancedRpcProvider, type LoadBalancerOptions, type MempoolEntry, MockKaspaRpcClient, type ResilienceReport, type RetryOptions, RpcCircuitOpenError, type RpcClientOptions, type RpcConfidence, RpcError, type RpcHealthCheckOptions, type RpcHealthResult, type RpcHealthState, RpcRateLimitError, type RpcReadinessWaitOptions, RpcTimeoutError, type RpcTrace, RpcUnavailableError, RpcValidationError, type ServerInfo, calculateConfidence, checkKaspaRpcHealth, classifyRpcError, mapKaspaAddressBalance, mapKaspaNodeInfo, mapKaspaRpcUtxos, mapKaspaSubmitTransactionResult, waitForKaspaRpcReady };
|
|
325
|
+
export { type BlockDagInfo, type CircuitBreakerOptions, CircuitState, JsonWrpcKaspaClient, type JsonWrpcKaspaClientOptions, type KaspaAddressBalance, KaspaJsonRpcClient, type KaspaNodeInfo, type KaspaRpcClient, type KaspaRpcHealth, type KaspaRpcOutpoint, type KaspaRpcUtxo, type KaspaSubmitTransactionResult, KaspaWrpcClient, LoadBalancedRpcProvider, type LoadBalancerOptions, type MempoolEntry, MockKaspaRpcClient, type ResilienceReport, type RetryOptions, RpcCircuitOpenError, type RpcClientOptions, type RpcConfidence, RpcError, type RpcHealthCheckOptions, type RpcHealthResult, type RpcHealthState, RpcRateLimitError, type RpcReadinessWaitOptions, RpcTimeoutError, type RpcTrace, RpcUnavailableError, RpcValidationError, type ServerInfo, type WrpcRequest, type WrpcResponse, calculateConfidence, checkKaspaRpcHealth, classifyRpcError, mapKaspaAddressBalance, mapKaspaNodeInfo, mapKaspaRpcUtxos, mapKaspaSubmitTransactionResult, waitForKaspaRpcReady };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import { WebSocket } from "ws";
|
|
2
|
+
import { WebSocket as WebSocket2 } from "ws";
|
|
3
3
|
|
|
4
4
|
// src/errors.ts
|
|
5
5
|
var RpcError = class extends Error {
|
|
@@ -438,11 +438,180 @@ var KaspaJsonRpcClient = class {
|
|
|
438
438
|
}
|
|
439
439
|
};
|
|
440
440
|
|
|
441
|
+
// src/wrpc-client.ts
|
|
442
|
+
import WebSocket from "ws";
|
|
443
|
+
var KaspaWrpcClient = class {
|
|
444
|
+
url;
|
|
445
|
+
ws = null;
|
|
446
|
+
requestId = 0;
|
|
447
|
+
pending = /* @__PURE__ */ new Map();
|
|
448
|
+
onNotification;
|
|
449
|
+
debug = false;
|
|
450
|
+
constructor(url) {
|
|
451
|
+
if (url.startsWith("http://")) {
|
|
452
|
+
this.url = url.replace("http://", "ws://");
|
|
453
|
+
} else if (url.startsWith("https://")) {
|
|
454
|
+
this.url = url.replace("https://", "wss://");
|
|
455
|
+
} else if (!url.startsWith("ws://") && !url.startsWith("wss://")) {
|
|
456
|
+
this.url = `ws://${url}`;
|
|
457
|
+
} else {
|
|
458
|
+
this.url = url;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
getUrl() {
|
|
462
|
+
return this.url;
|
|
463
|
+
}
|
|
464
|
+
async connect(timeoutMs = 5e3) {
|
|
465
|
+
return new Promise((resolve, reject) => {
|
|
466
|
+
const timer = setTimeout(() => {
|
|
467
|
+
this.disconnect();
|
|
468
|
+
reject(new Error(`WebSocket connection timeout after ${timeoutMs}ms to ${this.url}`));
|
|
469
|
+
}, timeoutMs);
|
|
470
|
+
try {
|
|
471
|
+
this.ws = new WebSocket(this.url);
|
|
472
|
+
} catch (err) {
|
|
473
|
+
clearTimeout(timer);
|
|
474
|
+
reject(new Error(`Failed to create WebSocket to ${this.url}: ${err}`));
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
this.ws.on("open", () => {
|
|
478
|
+
clearTimeout(timer);
|
|
479
|
+
resolve();
|
|
480
|
+
});
|
|
481
|
+
this.ws.on("error", (err) => {
|
|
482
|
+
clearTimeout(timer);
|
|
483
|
+
reject(new Error(`WebSocket error: ${err.message}`));
|
|
484
|
+
});
|
|
485
|
+
this.ws.on("message", (data) => {
|
|
486
|
+
try {
|
|
487
|
+
const response = JSON.parse(data.toString());
|
|
488
|
+
if (response.id !== void 0) {
|
|
489
|
+
const pending = this.pending.get(response.id);
|
|
490
|
+
if (pending) {
|
|
491
|
+
clearTimeout(pending.timer);
|
|
492
|
+
this.pending.delete(response.id);
|
|
493
|
+
pending.resolve(response);
|
|
494
|
+
}
|
|
495
|
+
} else {
|
|
496
|
+
if (this.onNotification) {
|
|
497
|
+
this.onNotification(response);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
} catch (parseErr) {
|
|
501
|
+
if (this.debug) {
|
|
502
|
+
console.debug("[wRPC] Non-JSON message received:", data.toString().slice(0, 200));
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
this.ws.on("close", () => {
|
|
507
|
+
for (const [, pending] of this.pending) {
|
|
508
|
+
clearTimeout(pending.timer);
|
|
509
|
+
pending.reject(new Error("WebSocket closed"));
|
|
510
|
+
}
|
|
511
|
+
this.pending.clear();
|
|
512
|
+
});
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
async request(method, params, timeoutMs = 5e3) {
|
|
516
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
517
|
+
throw new Error("WebSocket not connected. Call connect() first.");
|
|
518
|
+
}
|
|
519
|
+
const id = ++this.requestId;
|
|
520
|
+
return new Promise((resolve, reject) => {
|
|
521
|
+
const timer = setTimeout(() => {
|
|
522
|
+
this.pending.delete(id);
|
|
523
|
+
reject(new Error(`wRPC timeout: ${method} (${timeoutMs}ms)`));
|
|
524
|
+
}, timeoutMs);
|
|
525
|
+
this.pending.set(id, {
|
|
526
|
+
resolve: (response) => {
|
|
527
|
+
if (response.error) {
|
|
528
|
+
const errMsg = response.error.message || `wRPC error code ${response.error.code || "unknown"}`;
|
|
529
|
+
reject(new Error(errMsg));
|
|
530
|
+
} else {
|
|
531
|
+
resolve(response.result !== void 0 ? response.result : response.params);
|
|
532
|
+
}
|
|
533
|
+
},
|
|
534
|
+
reject,
|
|
535
|
+
timer
|
|
536
|
+
});
|
|
537
|
+
this.ws.send(JSON.stringify({ id, method, params: params || {} }));
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
async getServerInfo() {
|
|
541
|
+
return this.request("getServerInfo");
|
|
542
|
+
}
|
|
543
|
+
async getBlockDagInfo() {
|
|
544
|
+
return this.request("getBlockDagInfo");
|
|
545
|
+
}
|
|
546
|
+
async getVirtualSelectedParentBlueScore() {
|
|
547
|
+
return this.request("getVirtualSelectedParentBlueScore");
|
|
548
|
+
}
|
|
549
|
+
async getUtxosByAddresses(addresses) {
|
|
550
|
+
return this.request("getUtxosByAddresses", { addresses });
|
|
551
|
+
}
|
|
552
|
+
async ping() {
|
|
553
|
+
try {
|
|
554
|
+
await this.getServerInfo();
|
|
555
|
+
return true;
|
|
556
|
+
} catch {
|
|
557
|
+
return false;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
disconnect() {
|
|
561
|
+
if (this.ws) {
|
|
562
|
+
try {
|
|
563
|
+
this.ws.close();
|
|
564
|
+
} catch {
|
|
565
|
+
}
|
|
566
|
+
this.ws = null;
|
|
567
|
+
}
|
|
568
|
+
for (const [, pending] of this.pending) {
|
|
569
|
+
clearTimeout(pending.timer);
|
|
570
|
+
}
|
|
571
|
+
this.pending.clear();
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
|
|
441
575
|
// src/health.ts
|
|
442
576
|
async function checkKaspaRpcHealth(options) {
|
|
443
|
-
const url = options?.url || "
|
|
444
|
-
const client = new KaspaJsonRpcClient({ url, timeoutMs: options?.timeoutMs });
|
|
577
|
+
const url = options?.url || "ws://127.0.0.1:18210";
|
|
445
578
|
const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
579
|
+
const timeoutMs = options?.timeoutMs || 2e3;
|
|
580
|
+
if (url.startsWith("ws://") || url.startsWith("wss://") || url.includes("18210") || url.includes("18110")) {
|
|
581
|
+
const client2 = new KaspaWrpcClient(url);
|
|
582
|
+
const start = Date.now();
|
|
583
|
+
try {
|
|
584
|
+
await client2.connect(timeoutMs);
|
|
585
|
+
const info = await client2.getServerInfo();
|
|
586
|
+
const dagInfo = await client2.getBlockDagInfo();
|
|
587
|
+
const latencyMs = Date.now() - start;
|
|
588
|
+
client2.disconnect();
|
|
589
|
+
return {
|
|
590
|
+
endpoint: url,
|
|
591
|
+
status: "healthy",
|
|
592
|
+
ready: true,
|
|
593
|
+
checkedAt,
|
|
594
|
+
latencyMs,
|
|
595
|
+
networkId: dagInfo?.networkId || "simnet",
|
|
596
|
+
virtualDaaScore: dagInfo?.virtualDaaScore?.toString() || "0",
|
|
597
|
+
serverVersion: info?.serverVersion || "unknown",
|
|
598
|
+
isSynced: info?.isSynced ?? true,
|
|
599
|
+
lastError: null,
|
|
600
|
+
stale: !(info?.isSynced ?? true)
|
|
601
|
+
};
|
|
602
|
+
} catch (e) {
|
|
603
|
+
client2.disconnect();
|
|
604
|
+
return {
|
|
605
|
+
endpoint: url,
|
|
606
|
+
status: "unreachable",
|
|
607
|
+
ready: false,
|
|
608
|
+
checkedAt,
|
|
609
|
+
error: e.message,
|
|
610
|
+
lastError: e.message
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
const client = new KaspaJsonRpcClient({ url, timeoutMs });
|
|
446
615
|
try {
|
|
447
616
|
const health = await client.healthCheck();
|
|
448
617
|
return {
|
|
@@ -487,7 +656,7 @@ async function waitForKaspaRpcReady(options) {
|
|
|
487
656
|
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
488
657
|
}
|
|
489
658
|
return lastResult || {
|
|
490
|
-
endpoint: options?.url || "
|
|
659
|
+
endpoint: options?.url || "ws://127.0.0.1:18210",
|
|
491
660
|
status: "unreachable",
|
|
492
661
|
ready: false,
|
|
493
662
|
checkedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -766,11 +935,11 @@ var JsonWrpcKaspaClient = class {
|
|
|
766
935
|
});
|
|
767
936
|
}
|
|
768
937
|
async connect() {
|
|
769
|
-
if (this.socket && this.socket.readyState ===
|
|
938
|
+
if (this.socket && this.socket.readyState === WebSocket2.OPEN) {
|
|
770
939
|
return this.socket;
|
|
771
940
|
}
|
|
772
941
|
return new Promise((resolve, reject) => {
|
|
773
|
-
const ws = new
|
|
942
|
+
const ws = new WebSocket2(this.rpcUrl);
|
|
774
943
|
const timeout = setTimeout(() => {
|
|
775
944
|
ws.close();
|
|
776
945
|
reject(new Error(`Cannot connect to Kaspa RPC at ${this.rpcUrl}. Connection timed out.`));
|
|
@@ -916,6 +1085,7 @@ export {
|
|
|
916
1085
|
CircuitState,
|
|
917
1086
|
JsonWrpcKaspaClient,
|
|
918
1087
|
KaspaJsonRpcClient,
|
|
1088
|
+
KaspaWrpcClient,
|
|
919
1089
|
LoadBalancedRpcProvider,
|
|
920
1090
|
MockKaspaRpcClient,
|
|
921
1091
|
RpcCircuitOpenError,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hardkas/kaspa-rpc",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2-alpha",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"ws": "^8.18.0",
|
|
12
|
-
"@hardkas/core": "0.5.
|
|
13
|
-
"@hardkas/tx-builder": "0.5.
|
|
12
|
+
"@hardkas/core": "0.5.2-alpha",
|
|
13
|
+
"@hardkas/tx-builder": "0.5.2-alpha"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@types/ws": "^8.5.13",
|