@rpckit/websocket 0.9.99
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/README.md +128 -0
- package/dist/electrum-cash/index.d.ts +3 -0
- package/dist/electrum-cash/index.d.ts.map +1 -0
- package/dist/electrum-cash/index.js +2 -0
- package/dist/electrum-cash/index.js.map +1 -0
- package/dist/electrum-cash/webSocket.d.ts +10 -0
- package/dist/electrum-cash/webSocket.d.ts.map +1 -0
- package/dist/electrum-cash/webSocket.js +51 -0
- package/dist/electrum-cash/webSocket.js.map +1 -0
- package/dist/ethereum/index.d.ts +3 -0
- package/dist/ethereum/index.d.ts.map +1 -0
- package/dist/ethereum/index.js +2 -0
- package/dist/ethereum/index.js.map +1 -0
- package/dist/ethereum/webSocket.d.ts +17 -0
- package/dist/ethereum/webSocket.d.ts.map +1 -0
- package/dist/ethereum/webSocket.js +95 -0
- package/dist/ethereum/webSocket.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/webSocket.d.ts +4 -0
- package/dist/webSocket.d.ts.map +1 -0
- package/dist/webSocket.js +429 -0
- package/dist/webSocket.js.map +1 -0
- package/package.json +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# @rpckit/websocket
|
|
2
|
+
|
|
3
|
+
WebSocket transport for rpckit. Supports subscriptions, keep-alive, reconnection, and request batching.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @rpckit/core @rpckit/websocket
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { webSocket } from '@rpckit/websocket'
|
|
15
|
+
|
|
16
|
+
const transport = webSocket('wss://example.com', {
|
|
17
|
+
timeout: 10000,
|
|
18
|
+
keepAlive: { interval: 30000, method: 'server.ping' },
|
|
19
|
+
reconnect: { delay: 1000, attempts: 5 }
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
await transport.connect()
|
|
23
|
+
const result = await transport.request('my.method', 'param1')
|
|
24
|
+
|
|
25
|
+
// Subscriptions
|
|
26
|
+
const unsub = await transport.subscribe('events.subscribe', (data) => {
|
|
27
|
+
console.log('Event:', data)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
await transport.close()
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Electrum Cash Variant
|
|
34
|
+
|
|
35
|
+
For Electrum Cash servers, use the `electrum-cash` subpath which pre-configures handshake, keep-alive method, and unsubscribe conventions:
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { webSocket } from '@rpckit/websocket/electrum-cash'
|
|
39
|
+
|
|
40
|
+
const transport = webSocket('wss://electrum.example.com:50004', {
|
|
41
|
+
keepAlive: 30000 // Automatically uses server.ping
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
// Handshake (server.version) sent automatically on connect
|
|
45
|
+
const tip = await transport.request('blockchain.headers.get_tip')
|
|
46
|
+
|
|
47
|
+
// Unsubscribe automatically calls blockchain.headers.unsubscribe
|
|
48
|
+
const unsub = await transport.subscribe('blockchain.headers.subscribe', (header) => {
|
|
49
|
+
console.log('New block:', header)
|
|
50
|
+
})
|
|
51
|
+
await unsub()
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The electrum-cash variant also accepts:
|
|
55
|
+
|
|
56
|
+
- `clientName` - Client name sent in server.version handshake (default: `'rpckit'`)
|
|
57
|
+
- `protocolVersion` - Protocol version (default: `'1.6'`)
|
|
58
|
+
|
|
59
|
+
## Ethereum Variant
|
|
60
|
+
|
|
61
|
+
For Ethereum JSON-RPC nodes, use the `ethereum` subpath which handles subscription routing:
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import { webSocket } from '@rpckit/websocket/ethereum'
|
|
65
|
+
|
|
66
|
+
const transport = webSocket('wss://ethereum-rpc.publicnode.com')
|
|
67
|
+
|
|
68
|
+
// Standard requests
|
|
69
|
+
const blockNumber = await transport.request('eth_blockNumber')
|
|
70
|
+
|
|
71
|
+
// Subscriptions - eth_subscription notifications are routed automatically
|
|
72
|
+
const unsub = await transport.subscribe('eth_subscribe', 'newHeads', (header) => {
|
|
73
|
+
console.log('New block:', header.number)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
// eth_unsubscribe called automatically
|
|
77
|
+
await unsub()
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The ethereum variant automatically:
|
|
81
|
+
|
|
82
|
+
- Routes `eth_subscription` notifications to the correct callback by subscription ID
|
|
83
|
+
- Calls `eth_unsubscribe` on cleanup
|
|
84
|
+
- Suppresses subscription IDs from callbacks (handled internally)
|
|
85
|
+
|
|
86
|
+
## Subscription Sharing
|
|
87
|
+
|
|
88
|
+
Multiple callers subscribing to the same method+params will share a single server subscription. New subscribers receive the most recent notification data (not stale initial data). The server unsubscribe is only sent when the last listener unsubscribes.
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
// Both callbacks share one server subscription
|
|
92
|
+
const unsub1 = await transport.subscribe('events', callback1)
|
|
93
|
+
const unsub2 = await transport.subscribe('events', callback2)
|
|
94
|
+
|
|
95
|
+
await unsub1() // callback1 removed, server subscription stays active
|
|
96
|
+
await unsub2() // callback2 removed, NOW server unsubscribe is sent
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Options
|
|
100
|
+
|
|
101
|
+
- `timeout` - Request timeout in ms
|
|
102
|
+
- `connectTimeout` - Connection timeout in ms
|
|
103
|
+
- `retryCount` - Max retry attempts (default: 3)
|
|
104
|
+
- `retryDelay` - Base delay between retries in ms (default: 150, exponential backoff applied)
|
|
105
|
+
- `keepAlive` - Keep-alive ping interval in ms, or `{ interval, method, params }`
|
|
106
|
+
- `reconnect` - `{ delay: number, attempts: number }` for auto-reconnect after disconnect
|
|
107
|
+
- `headers` - Custom headers for the WebSocket connection
|
|
108
|
+
- `handshake` - `{ method, params }` to send on connect
|
|
109
|
+
- `batch` - `true` (default), `false`, or `{ wait, batchSize }`
|
|
110
|
+
- `onUnsubscribe` - Cleanup callback when the last listener unsubscribes (receives `{ request, method, params, initialResult }`)
|
|
111
|
+
- `notificationFilter` - Filter notifications before delivery; receives `(subscriptionParams, notificationParams)` and returns `boolean`
|
|
112
|
+
- `transformInitialResult` - Transform the initial subscription result before delivery; return `undefined` to suppress
|
|
113
|
+
|
|
114
|
+
## Socket Access
|
|
115
|
+
|
|
116
|
+
Access the underlying WebSocket for advanced use cases:
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
// Synchronous - returns null if not yet connected
|
|
120
|
+
const socket = transport.getSocket()
|
|
121
|
+
|
|
122
|
+
// Async - connects first if needed, then returns socket
|
|
123
|
+
const socket = await transport.getSocketAsync()
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Lazy Initialization
|
|
127
|
+
|
|
128
|
+
Connections are established lazily on first use. Creating a transport is cheap - no resources are allocated until the first `request()`, `subscribe()`, or `connect()` call.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/electrum-cash/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAA;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/electrum-cash/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AnySchema, Schema, WebSocketTransport, WebSocketTransportConfig } from '@rpckit/core';
|
|
2
|
+
export interface ElectrumWebSocketConfig extends WebSocketTransportConfig {
|
|
3
|
+
/** Client name sent in server.version handshake (default: 'rpckit') */
|
|
4
|
+
clientName?: string;
|
|
5
|
+
/** Electrum protocol version (default: '1.6') */
|
|
6
|
+
protocolVersion?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function webSocket<S extends Schema = AnySchema>(url: string, options?: Omit<ElectrumWebSocketConfig, 'url'>): WebSocketTransport<S>;
|
|
9
|
+
export declare function webSocket<S extends Schema = AnySchema>(config: ElectrumWebSocketConfig): WebSocketTransport<S>;
|
|
10
|
+
//# sourceMappingURL=webSocket.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webSocket.d.ts","sourceRoot":"","sources":["../../src/electrum-cash/webSocket.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EAET,MAAM,EACN,kBAAkB,EAClB,wBAAwB,EACzB,MAAM,cAAc,CAAA;AAGrB,MAAM,WAAW,uBAAwB,SAAQ,wBAAwB;IACvE,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iDAAiD;IACjD,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,SAAS,EACpD,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,IAAI,CAAC,uBAAuB,EAAE,KAAK,CAAC,GAC7C,kBAAkB,CAAC,CAAC,CAAC,CAAA;AACxB,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,SAAS,EACpD,MAAM,EAAE,uBAAuB,GAC9B,kBAAkB,CAAC,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { webSocket as baseWebSocket } from '../webSocket.js';
|
|
2
|
+
export function webSocket(configOrUrl, options) {
|
|
3
|
+
const base = typeof configOrUrl === 'string'
|
|
4
|
+
? { ...options, url: configOrUrl }
|
|
5
|
+
: configOrUrl;
|
|
6
|
+
const { clientName = 'rpckit', protocolVersion = '1.6', ...rest } = base;
|
|
7
|
+
const keepAlive = normalizeKeepAlive(rest.keepAlive);
|
|
8
|
+
return baseWebSocket({
|
|
9
|
+
handshake: {
|
|
10
|
+
method: 'server.version',
|
|
11
|
+
params: [clientName, protocolVersion],
|
|
12
|
+
},
|
|
13
|
+
onUnsubscribe: ({ request, method, params }) => request(method.replace('subscribe', 'unsubscribe'), ...params),
|
|
14
|
+
transformInitialResult: (_method, params, result) => [...params, ...result],
|
|
15
|
+
notificationFilter: electrumParamsMatch,
|
|
16
|
+
...rest,
|
|
17
|
+
...(keepAlive !== undefined ? { keepAlive } : {}),
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
function normalizeKeepAlive(ka) {
|
|
21
|
+
if (ka === undefined)
|
|
22
|
+
return undefined;
|
|
23
|
+
if (typeof ka === 'number') {
|
|
24
|
+
return { interval: ka, method: 'server.ping' };
|
|
25
|
+
}
|
|
26
|
+
const ping = { ...ka };
|
|
27
|
+
if (!ping.method)
|
|
28
|
+
ping.method = 'server.ping';
|
|
29
|
+
return ping;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Electrum protocol notification filter.
|
|
33
|
+
* Notifications include subscription params as prefix: subscribe([address]) → notification([address, status])
|
|
34
|
+
*/
|
|
35
|
+
function electrumParamsMatch(subscriptionParams, notificationParams) {
|
|
36
|
+
return subscriptionParams.every((p, i) => {
|
|
37
|
+
if (i >= notificationParams.length)
|
|
38
|
+
return false;
|
|
39
|
+
const np = notificationParams[i];
|
|
40
|
+
// Simple types can be compared directly
|
|
41
|
+
if (typeof p !== 'object' ||
|
|
42
|
+
p === null ||
|
|
43
|
+
typeof np !== 'object' ||
|
|
44
|
+
np === null) {
|
|
45
|
+
return p === np;
|
|
46
|
+
}
|
|
47
|
+
// Complex types need JSON comparison
|
|
48
|
+
return JSON.stringify(p) === JSON.stringify(np);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=webSocket.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webSocket.js","sourceRoot":"","sources":["../../src/electrum-cash/webSocket.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAgB5D,MAAM,UAAU,SAAS,CACvB,WAA6C,EAC7C,OAA8C;IAE9C,MAAM,IAAI,GACR,OAAO,WAAW,KAAK,QAAQ;QAC7B,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE;QAClC,CAAC,CAAC,WAAW,CAAA;IAEjB,MAAM,EAAE,UAAU,GAAG,QAAQ,EAAE,eAAe,GAAG,KAAK,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAA;IACxE,MAAM,SAAS,GAAG,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IAEpD,OAAO,aAAa,CAAI;QACtB,SAAS,EAAE;YACT,MAAM,EAAE,gBAAgB;YACxB,MAAM,EAAE,CAAC,UAAU,EAAE,eAAe,CAAC;SACtC;QACD,aAAa,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,CAC7C,OAAO,CACL,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,aAAa,CAAC,EAC1C,GAAG,MAAM,CACO;QACpB,sBAAsB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC;QAC3E,kBAAkB,EAAE,mBAAmB;QACvC,GAAG,IAAI;QACP,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAClD,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,kBAAkB,CACzB,EAAyC;IAEzC,IAAI,EAAE,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACtC,IAAI,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC;QAC3B,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,CAAA;IAChD,CAAC;IACD,MAAM,IAAI,GAAe,EAAE,GAAG,EAAE,EAAE,CAAA;IAClC,IAAI,CAAC,IAAI,CAAC,MAAM;QAAE,IAAI,CAAC,MAAM,GAAG,aAAa,CAAA;IAC7C,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;GAGG;AACH,SAAS,mBAAmB,CAC1B,kBAA6B,EAC7B,kBAA6B;IAE7B,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACvC,IAAI,CAAC,IAAI,kBAAkB,CAAC,MAAM;YAAE,OAAO,KAAK,CAAA;QAChD,MAAM,EAAE,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAA;QAChC,wCAAwC;QACxC,IACE,OAAO,CAAC,KAAK,QAAQ;YACrB,CAAC,KAAK,IAAI;YACV,OAAO,EAAE,KAAK,QAAQ;YACtB,EAAE,KAAK,IAAI,EACX,CAAC;YACD,OAAO,CAAC,KAAK,EAAE,CAAA;QACjB,CAAC;QACD,qCAAqC;QACrC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAA;IACjD,CAAC,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ethereum/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAA;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ethereum/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AnySchema, Schema, WebSocketTransport, WebSocketTransportConfig } from '@rpckit/core';
|
|
2
|
+
export interface EthereumWebSocketConfig extends WebSocketTransportConfig {
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* WebSocket transport configured for Ethereum JSON-RPC.
|
|
6
|
+
*
|
|
7
|
+
* Handles Ethereum's subscription model where:
|
|
8
|
+
* - `eth_subscribe` returns a subscription ID
|
|
9
|
+
* - Notifications arrive via `eth_subscription` method with the subscription ID in params
|
|
10
|
+
* - `eth_unsubscribe` is called with the subscription ID to clean up
|
|
11
|
+
*
|
|
12
|
+
* The subscription ID is captured internally and not delivered to the user callback.
|
|
13
|
+
* Notifications are routed by matching the subscription ID.
|
|
14
|
+
*/
|
|
15
|
+
export declare function webSocket<S extends Schema = AnySchema>(url: string, options?: Omit<EthereumWebSocketConfig, 'url'>): WebSocketTransport<S>;
|
|
16
|
+
export declare function webSocket<S extends Schema = AnySchema>(config: EthereumWebSocketConfig): WebSocketTransport<S>;
|
|
17
|
+
//# sourceMappingURL=webSocket.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webSocket.d.ts","sourceRoot":"","sources":["../../src/ethereum/webSocket.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,MAAM,EACN,kBAAkB,EAClB,wBAAwB,EACzB,MAAM,cAAc,CAAA;AAGrB,MAAM,WAAW,uBAAwB,SAAQ,wBAAwB;CAAG;AAW5E;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,SAAS,EACpD,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,IAAI,CAAC,uBAAuB,EAAE,KAAK,CAAC,GAC7C,kBAAkB,CAAC,CAAC,CAAC,CAAA;AACxB,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,SAAS,EACpD,MAAM,EAAE,uBAAuB,GAC9B,kBAAkB,CAAC,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { webSocket as baseWebSocket } from '../webSocket.js';
|
|
2
|
+
export function webSocket(configOrUrl, options) {
|
|
3
|
+
const config = typeof configOrUrl === 'string'
|
|
4
|
+
? { ...options, url: configOrUrl }
|
|
5
|
+
: configOrUrl;
|
|
6
|
+
// Track subscription IDs to callback mappings
|
|
7
|
+
const subscriptionCallbacks = new Map();
|
|
8
|
+
// Track notification handlers for cleanup
|
|
9
|
+
const notificationHandlers = new Map();
|
|
10
|
+
const transport = baseWebSocket({
|
|
11
|
+
...config,
|
|
12
|
+
// Suppress the subscription ID from being delivered to the callback for eth_subscribe
|
|
13
|
+
transformInitialResult: (method, _params, result) => {
|
|
14
|
+
if (method === 'eth_subscribe') {
|
|
15
|
+
return undefined; // Suppress - we handle it in the wrapper
|
|
16
|
+
}
|
|
17
|
+
return result[0];
|
|
18
|
+
},
|
|
19
|
+
// Unsubscribe by calling eth_unsubscribe with the subscription ID
|
|
20
|
+
onUnsubscribe: async ({ request, method, initialResult }) => {
|
|
21
|
+
if (method === 'eth_subscribe' && typeof initialResult === 'string') {
|
|
22
|
+
const handler = notificationHandlers.get(initialResult);
|
|
23
|
+
if (handler) {
|
|
24
|
+
const ws = transport.getSocket();
|
|
25
|
+
if (ws) {
|
|
26
|
+
ws.removeEventListener('message', handler);
|
|
27
|
+
}
|
|
28
|
+
notificationHandlers.delete(initialResult);
|
|
29
|
+
}
|
|
30
|
+
subscriptionCallbacks.delete(initialResult);
|
|
31
|
+
await request('eth_unsubscribe', initialResult);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
// Wrap subscribe to set up Ethereum notification routing
|
|
36
|
+
const originalSubscribe = transport.subscribe.bind(transport);
|
|
37
|
+
transport.subscribe = async (method, ...args) => {
|
|
38
|
+
// For non-eth_subscribe methods, use the original behavior
|
|
39
|
+
if (method !== 'eth_subscribe') {
|
|
40
|
+
return originalSubscribe(method, ...args);
|
|
41
|
+
}
|
|
42
|
+
const callback = args.pop();
|
|
43
|
+
const params = args;
|
|
44
|
+
// Call the original subscribe - it will make the RPC call and get the subscription ID
|
|
45
|
+
// We pass a dummy callback since transformInitialResult will suppress the initial result
|
|
46
|
+
let subscriptionId = null;
|
|
47
|
+
const unsub = await new Promise((resolve, reject) => {
|
|
48
|
+
// We need to intercept the initial result to get the subscription ID
|
|
49
|
+
// Since transformInitialResult suppresses it, we use a request to get it
|
|
50
|
+
transport
|
|
51
|
+
.request('eth_subscribe', ...params)
|
|
52
|
+
.then((id) => {
|
|
53
|
+
subscriptionId = id;
|
|
54
|
+
subscriptionCallbacks.set(subscriptionId, callback);
|
|
55
|
+
// Set up notification handler
|
|
56
|
+
const ws = transport.getSocket();
|
|
57
|
+
if (ws) {
|
|
58
|
+
const notificationHandler = (event) => {
|
|
59
|
+
try {
|
|
60
|
+
const msg = JSON.parse(typeof event.data === 'string' ? event.data : '');
|
|
61
|
+
if (msg.method === 'eth_subscription' &&
|
|
62
|
+
msg.params?.subscription === subscriptionId) {
|
|
63
|
+
callback(msg.params.result);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Ignore parse errors
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
ws.addEventListener('message', notificationHandler);
|
|
71
|
+
notificationHandlers.set(subscriptionId, notificationHandler);
|
|
72
|
+
}
|
|
73
|
+
// Return unsubscribe function
|
|
74
|
+
resolve(async () => {
|
|
75
|
+
if (subscriptionId) {
|
|
76
|
+
const handler = notificationHandlers.get(subscriptionId);
|
|
77
|
+
if (handler) {
|
|
78
|
+
const socket = transport.getSocket();
|
|
79
|
+
if (socket) {
|
|
80
|
+
socket.removeEventListener('message', handler);
|
|
81
|
+
}
|
|
82
|
+
notificationHandlers.delete(subscriptionId);
|
|
83
|
+
}
|
|
84
|
+
subscriptionCallbacks.delete(subscriptionId);
|
|
85
|
+
await transport.request('eth_unsubscribe', subscriptionId);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
})
|
|
89
|
+
.catch(reject);
|
|
90
|
+
});
|
|
91
|
+
return unsub;
|
|
92
|
+
};
|
|
93
|
+
return transport;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=webSocket.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webSocket.js","sourceRoot":"","sources":["../../src/ethereum/webSocket.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type AnySchema, type Schema, type WebSocketTransport, type WebSocketTransportConfig } from '@rpckit/core';
|
|
2
|
+
export declare function webSocket<S extends Schema = AnySchema>(url: string, options?: Omit<WebSocketTransportConfig, 'url'>): WebSocketTransport<S>;
|
|
3
|
+
export declare function webSocket<S extends Schema = AnySchema>(config: WebSocketTransportConfig): WebSocketTransport<S>;
|
|
4
|
+
//# sourceMappingURL=webSocket.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webSocket.d.ts","sourceRoot":"","sources":["../src/webSocket.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EAId,KAAK,MAAM,EAGX,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAE9B,MAAM,cAAc,CAAA;AAucrB,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,SAAS,EACpD,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,IAAI,CAAC,wBAAwB,EAAE,KAAK,CAAC,GAC9C,kBAAkB,CAAC,CAAC,CAAC,CAAA;AACxB,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,SAAS,EACpD,MAAM,EAAE,wBAAwB,GAC/B,kBAAkB,CAAC,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
import { BatchScheduler, withRetry, } from '@rpckit/core';
|
|
2
|
+
import { WebSocket } from 'isows';
|
|
3
|
+
const socketCache = new Map();
|
|
4
|
+
function getCacheKey(config) {
|
|
5
|
+
return JSON.stringify({
|
|
6
|
+
url: config.url,
|
|
7
|
+
headers: config.headers,
|
|
8
|
+
keepAlive: config.keepAlive,
|
|
9
|
+
reconnect: config.reconnect,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
function getOrCreateSocketClient(config) {
|
|
13
|
+
const cacheKey = getCacheKey(config);
|
|
14
|
+
let client = socketCache.get(cacheKey);
|
|
15
|
+
if (client) {
|
|
16
|
+
client.refCount++;
|
|
17
|
+
return client;
|
|
18
|
+
}
|
|
19
|
+
let ws = null;
|
|
20
|
+
let nextId = 1;
|
|
21
|
+
let closed = false;
|
|
22
|
+
let connectPromise = null;
|
|
23
|
+
let reconnectCount = 0;
|
|
24
|
+
const pending = new Map();
|
|
25
|
+
const subscriptions = new Map();
|
|
26
|
+
// Track pending subscribe requests to prevent race conditions
|
|
27
|
+
const pendingSubscriptions = new Map();
|
|
28
|
+
let keepAliveTimer = null;
|
|
29
|
+
let batchScheduler = null;
|
|
30
|
+
if (config.batch !== false) {
|
|
31
|
+
batchScheduler = new BatchScheduler(typeof config.batch === 'object' ? config.batch : {}, sendBatch);
|
|
32
|
+
}
|
|
33
|
+
function getSubscriptionKey(method, params) {
|
|
34
|
+
return `${method}:${JSON.stringify(params)}`;
|
|
35
|
+
}
|
|
36
|
+
function connect() {
|
|
37
|
+
if (connectPromise)
|
|
38
|
+
return connectPromise;
|
|
39
|
+
connectPromise = new Promise((resolve, reject) => {
|
|
40
|
+
ws = config.headers
|
|
41
|
+
? // biome-ignore lint/suspicious/noExplicitAny: isows headers typing
|
|
42
|
+
new WebSocket(config.url, { headers: config.headers })
|
|
43
|
+
: new WebSocket(config.url);
|
|
44
|
+
let connectTimer;
|
|
45
|
+
if (config.connectTimeout) {
|
|
46
|
+
connectTimer = setTimeout(() => {
|
|
47
|
+
reject(new Error('Connection timeout'));
|
|
48
|
+
ws?.close();
|
|
49
|
+
void handleDisconnect();
|
|
50
|
+
}, config.connectTimeout);
|
|
51
|
+
}
|
|
52
|
+
ws.onopen = async () => {
|
|
53
|
+
try {
|
|
54
|
+
reconnectCount = 0;
|
|
55
|
+
if (config.handshake) {
|
|
56
|
+
const id = nextId++;
|
|
57
|
+
const req = {
|
|
58
|
+
jsonrpc: '2.0',
|
|
59
|
+
method: config.handshake.method,
|
|
60
|
+
params: config.handshake.params ?? [],
|
|
61
|
+
id,
|
|
62
|
+
};
|
|
63
|
+
await new Promise((res, rej) => {
|
|
64
|
+
pending.set(id, { resolve: () => res(), reject: rej });
|
|
65
|
+
sendRaw(req);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
startKeepAlive();
|
|
69
|
+
// Restore subscriptions after reconnect
|
|
70
|
+
if (subscriptions.size > 0) {
|
|
71
|
+
for (const [_key, entry] of subscriptions) {
|
|
72
|
+
const id = nextId++;
|
|
73
|
+
const req = {
|
|
74
|
+
jsonrpc: '2.0',
|
|
75
|
+
method: entry.method,
|
|
76
|
+
params: entry.params,
|
|
77
|
+
id,
|
|
78
|
+
};
|
|
79
|
+
sendRaw(req);
|
|
80
|
+
// We don't await the response - just re-establish
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (connectTimer)
|
|
84
|
+
clearTimeout(connectTimer);
|
|
85
|
+
resolve();
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
reject(err);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
ws.onerror = (event) => {
|
|
92
|
+
if (connectTimer)
|
|
93
|
+
clearTimeout(connectTimer);
|
|
94
|
+
const message = event && typeof event === 'object' && 'message' in event
|
|
95
|
+
? `WebSocket error: ${event.message}`
|
|
96
|
+
: 'WebSocket error';
|
|
97
|
+
reject(new Error(message));
|
|
98
|
+
void handleDisconnect();
|
|
99
|
+
};
|
|
100
|
+
ws.onclose = () => {
|
|
101
|
+
void handleDisconnect();
|
|
102
|
+
};
|
|
103
|
+
ws.onmessage = (event) => {
|
|
104
|
+
const raw = String(event.data);
|
|
105
|
+
if (!raw.trim())
|
|
106
|
+
return;
|
|
107
|
+
const messages = raw.startsWith('[') ? JSON.parse(raw) : [JSON.parse(raw)];
|
|
108
|
+
for (const msg of messages) {
|
|
109
|
+
if ('id' in msg && msg.id != null) {
|
|
110
|
+
const p = pending.get(msg.id);
|
|
111
|
+
if (p) {
|
|
112
|
+
pending.delete(msg.id);
|
|
113
|
+
if (p.timer)
|
|
114
|
+
clearTimeout(p.timer);
|
|
115
|
+
const resp = msg;
|
|
116
|
+
if (resp.error)
|
|
117
|
+
p.reject(resp.error);
|
|
118
|
+
else
|
|
119
|
+
p.resolve(resp.result);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Subscription notification
|
|
124
|
+
const notif = msg;
|
|
125
|
+
for (const [, entry] of subscriptions) {
|
|
126
|
+
if (entry.method === notif.method) {
|
|
127
|
+
// Apply notification filter if configured
|
|
128
|
+
if (config.notificationFilter) {
|
|
129
|
+
const notifParams = notif.params;
|
|
130
|
+
if (!config.notificationFilter(entry.params, notifParams)) {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
entry.lastNotification = notif.params;
|
|
135
|
+
for (const handler of entry.listeners)
|
|
136
|
+
handler(notif.params);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
});
|
|
143
|
+
return connectPromise;
|
|
144
|
+
}
|
|
145
|
+
function startKeepAlive() {
|
|
146
|
+
stopKeepAlive();
|
|
147
|
+
const ka = config.keepAlive;
|
|
148
|
+
if (!ka)
|
|
149
|
+
return;
|
|
150
|
+
const interval = typeof ka === 'number' ? ka : ka.interval;
|
|
151
|
+
const method = typeof ka === 'number' ? undefined : ka.method;
|
|
152
|
+
const params = typeof ka === 'number' ? [] : (ka.params ?? []);
|
|
153
|
+
if (interval > 0 && method) {
|
|
154
|
+
keepAliveTimer = setInterval(() => {
|
|
155
|
+
sendRaw({ jsonrpc: '2.0', method, params, id: nextId++ });
|
|
156
|
+
}, interval);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function stopKeepAlive() {
|
|
160
|
+
if (keepAliveTimer) {
|
|
161
|
+
clearInterval(keepAliveTimer);
|
|
162
|
+
keepAliveTimer = null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
async function handleDisconnect() {
|
|
166
|
+
stopKeepAlive();
|
|
167
|
+
connectPromise = null;
|
|
168
|
+
ws = null;
|
|
169
|
+
// Reject all pending requests (but keep subscriptions for restore)
|
|
170
|
+
for (const [, p] of pending) {
|
|
171
|
+
if (p.timer)
|
|
172
|
+
clearTimeout(p.timer);
|
|
173
|
+
p.reject(new Error('WebSocket disconnected'));
|
|
174
|
+
}
|
|
175
|
+
pending.clear();
|
|
176
|
+
if (!closed &&
|
|
177
|
+
config.reconnect &&
|
|
178
|
+
reconnectCount < config.reconnect.attempts) {
|
|
179
|
+
reconnectCount++;
|
|
180
|
+
await new Promise((r) => setTimeout(r, config.reconnect?.delay));
|
|
181
|
+
if (!closed)
|
|
182
|
+
await connect();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function sendRaw(request) {
|
|
186
|
+
ws?.send(JSON.stringify(request));
|
|
187
|
+
}
|
|
188
|
+
async function sendBatch(requests) {
|
|
189
|
+
await connect();
|
|
190
|
+
return new Promise((resolve, reject) => {
|
|
191
|
+
const ids = new Set(requests.map((r) => r.id));
|
|
192
|
+
const results = [];
|
|
193
|
+
const timer = config.timeout
|
|
194
|
+
? setTimeout(() => {
|
|
195
|
+
for (const id of ids)
|
|
196
|
+
pending.delete(id);
|
|
197
|
+
reject(new Error('Batch timeout'));
|
|
198
|
+
}, config.timeout)
|
|
199
|
+
: undefined;
|
|
200
|
+
for (const req of requests) {
|
|
201
|
+
pending.set(req.id, {
|
|
202
|
+
resolve: (result) => {
|
|
203
|
+
results.push({ jsonrpc: '2.0', result, id: req.id });
|
|
204
|
+
ids.delete(req.id);
|
|
205
|
+
if (ids.size === 0) {
|
|
206
|
+
if (timer)
|
|
207
|
+
clearTimeout(timer);
|
|
208
|
+
resolve(results);
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
reject: (error) => {
|
|
212
|
+
for (const id of ids)
|
|
213
|
+
pending.delete(id);
|
|
214
|
+
if (timer)
|
|
215
|
+
clearTimeout(timer);
|
|
216
|
+
reject(error);
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
ws?.send(JSON.stringify(requests));
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
async function request(method, params = []) {
|
|
224
|
+
await connect();
|
|
225
|
+
const id = nextId++;
|
|
226
|
+
const req = { jsonrpc: '2.0', method, params, id };
|
|
227
|
+
if (batchScheduler) {
|
|
228
|
+
return batchScheduler.enqueue(req);
|
|
229
|
+
}
|
|
230
|
+
return new Promise((resolve, reject) => {
|
|
231
|
+
const timer = config.timeout
|
|
232
|
+
? setTimeout(() => {
|
|
233
|
+
pending.delete(id);
|
|
234
|
+
reject(new Error('Request timeout'));
|
|
235
|
+
}, config.timeout)
|
|
236
|
+
: undefined;
|
|
237
|
+
pending.set(id, { resolve, reject, timer });
|
|
238
|
+
sendRaw(req);
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
async function subscribe(method, params, onData) {
|
|
242
|
+
await connect();
|
|
243
|
+
const subKey = getSubscriptionKey(method, params);
|
|
244
|
+
// Helper to create unsubscribe function for an entry
|
|
245
|
+
const createUnsubscribe = (e) => () => {
|
|
246
|
+
e.listeners.delete(onData);
|
|
247
|
+
if (e.listeners.size === 0) {
|
|
248
|
+
subscriptions.delete(subKey);
|
|
249
|
+
return true; // was the last listener
|
|
250
|
+
}
|
|
251
|
+
return false; // other listeners remain
|
|
252
|
+
};
|
|
253
|
+
// Check for existing subscription
|
|
254
|
+
let entry = subscriptions.get(subKey);
|
|
255
|
+
if (entry) {
|
|
256
|
+
entry.listeners.add(onData);
|
|
257
|
+
const hasNotification = entry.lastNotification !== undefined;
|
|
258
|
+
return {
|
|
259
|
+
initialResult: hasNotification
|
|
260
|
+
? entry.lastNotification
|
|
261
|
+
: entry.initialResult,
|
|
262
|
+
unsubscribe: createUnsubscribe(entry),
|
|
263
|
+
fromNotification: hasNotification,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
// Check for pending subscription request (race condition prevention)
|
|
267
|
+
const pendingPromise = pendingSubscriptions.get(subKey);
|
|
268
|
+
if (pendingPromise) {
|
|
269
|
+
// Wait for the in-flight subscription to complete, then tap in
|
|
270
|
+
entry = await pendingPromise;
|
|
271
|
+
entry.listeners.add(onData);
|
|
272
|
+
const hasNotification = entry.lastNotification !== undefined;
|
|
273
|
+
return {
|
|
274
|
+
initialResult: hasNotification
|
|
275
|
+
? entry.lastNotification
|
|
276
|
+
: entry.initialResult,
|
|
277
|
+
unsubscribe: createUnsubscribe(entry),
|
|
278
|
+
fromNotification: hasNotification,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
// Create new subscription - store promise to prevent race conditions
|
|
282
|
+
const subscriptionPromise = (async () => {
|
|
283
|
+
const id = nextId++;
|
|
284
|
+
const req = { jsonrpc: '2.0', method, params, id };
|
|
285
|
+
const initialResult = await new Promise((resolve, reject) => {
|
|
286
|
+
const timer = config.timeout
|
|
287
|
+
? setTimeout(() => {
|
|
288
|
+
pending.delete(id);
|
|
289
|
+
reject(new Error('Request timeout'));
|
|
290
|
+
}, config.timeout)
|
|
291
|
+
: undefined;
|
|
292
|
+
pending.set(id, { resolve, reject, timer });
|
|
293
|
+
sendRaw(req);
|
|
294
|
+
});
|
|
295
|
+
const newEntry = {
|
|
296
|
+
method,
|
|
297
|
+
params,
|
|
298
|
+
listeners: new Set([onData]),
|
|
299
|
+
initialResult,
|
|
300
|
+
lastNotification: undefined,
|
|
301
|
+
};
|
|
302
|
+
subscriptions.set(subKey, newEntry);
|
|
303
|
+
return newEntry;
|
|
304
|
+
})();
|
|
305
|
+
pendingSubscriptions.set(subKey, subscriptionPromise);
|
|
306
|
+
try {
|
|
307
|
+
entry = await subscriptionPromise;
|
|
308
|
+
return {
|
|
309
|
+
initialResult: entry.initialResult,
|
|
310
|
+
unsubscribe: createUnsubscribe(entry),
|
|
311
|
+
fromNotification: false,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
finally {
|
|
315
|
+
pendingSubscriptions.delete(subKey);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
async function close() {
|
|
319
|
+
closed = true;
|
|
320
|
+
stopKeepAlive();
|
|
321
|
+
if (batchScheduler)
|
|
322
|
+
await batchScheduler.flush();
|
|
323
|
+
for (const [, p] of pending) {
|
|
324
|
+
if (p.timer)
|
|
325
|
+
clearTimeout(p.timer);
|
|
326
|
+
p.reject(new Error('Transport closed'));
|
|
327
|
+
}
|
|
328
|
+
pending.clear();
|
|
329
|
+
subscriptions.clear();
|
|
330
|
+
ws?.close();
|
|
331
|
+
ws = null;
|
|
332
|
+
connectPromise = null;
|
|
333
|
+
socketCache.delete(cacheKey);
|
|
334
|
+
}
|
|
335
|
+
client = {
|
|
336
|
+
refCount: 1,
|
|
337
|
+
get ws() {
|
|
338
|
+
return ws;
|
|
339
|
+
},
|
|
340
|
+
nextId,
|
|
341
|
+
connectPromise,
|
|
342
|
+
reconnectCount,
|
|
343
|
+
closed,
|
|
344
|
+
pending,
|
|
345
|
+
subscriptions,
|
|
346
|
+
keepAliveTimer,
|
|
347
|
+
batchScheduler,
|
|
348
|
+
connect,
|
|
349
|
+
sendRaw,
|
|
350
|
+
request,
|
|
351
|
+
subscribe,
|
|
352
|
+
close,
|
|
353
|
+
};
|
|
354
|
+
socketCache.set(cacheKey, client);
|
|
355
|
+
return client;
|
|
356
|
+
}
|
|
357
|
+
export function webSocket(configOrUrl, options) {
|
|
358
|
+
const config = typeof configOrUrl === 'string'
|
|
359
|
+
? { ...options, url: configOrUrl }
|
|
360
|
+
: configOrUrl;
|
|
361
|
+
// Lazy client initialization - only created on first use
|
|
362
|
+
let client = null;
|
|
363
|
+
const getClient = () => {
|
|
364
|
+
if (!client)
|
|
365
|
+
client = getOrCreateSocketClient(config);
|
|
366
|
+
return client;
|
|
367
|
+
};
|
|
368
|
+
const retryOpts = {
|
|
369
|
+
retryCount: config.retryCount,
|
|
370
|
+
retryDelay: config.retryDelay,
|
|
371
|
+
};
|
|
372
|
+
const self = {
|
|
373
|
+
url: config.url,
|
|
374
|
+
connect: () => withRetry(() => getClient().connect(), retryOpts),
|
|
375
|
+
request: (method, ...params) => withRetry(() => getClient().request(method, params), retryOpts),
|
|
376
|
+
async subscribe(method, ...args) {
|
|
377
|
+
const onData = args.pop();
|
|
378
|
+
const params = args;
|
|
379
|
+
const { initialResult, unsubscribe, fromNotification } = await withRetry(() => getClient().subscribe(method, params, onData), retryOpts);
|
|
380
|
+
// Deliver initial result if we got one
|
|
381
|
+
if (initialResult !== undefined) {
|
|
382
|
+
if (fromNotification) {
|
|
383
|
+
// Reused subscription: lastNotification is already in notification format
|
|
384
|
+
onData(initialResult);
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
const transformed = config.transformInitialResult
|
|
388
|
+
? config.transformInitialResult(method, params, [initialResult])
|
|
389
|
+
: initialResult;
|
|
390
|
+
// Allow transformInitialResult to return undefined to suppress delivery
|
|
391
|
+
if (transformed !== undefined) {
|
|
392
|
+
onData(transformed);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
const unsub = async (cleanup) => {
|
|
397
|
+
const wasLastListener = unsubscribe();
|
|
398
|
+
// Only call onUnsubscribe when the last listener is removed
|
|
399
|
+
if (wasLastListener) {
|
|
400
|
+
const fn = cleanup ?? config.onUnsubscribe;
|
|
401
|
+
if (fn) {
|
|
402
|
+
await fn({ request: self.request, method, params, initialResult });
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
return unsub;
|
|
407
|
+
},
|
|
408
|
+
async close() {
|
|
409
|
+
if (!client)
|
|
410
|
+
return;
|
|
411
|
+
client.refCount--;
|
|
412
|
+
if (client.refCount <= 0) {
|
|
413
|
+
await client.close();
|
|
414
|
+
}
|
|
415
|
+
},
|
|
416
|
+
getSocket() {
|
|
417
|
+
return client?.ws ?? null;
|
|
418
|
+
},
|
|
419
|
+
async getSocketAsync() {
|
|
420
|
+
await getClient().connect();
|
|
421
|
+
const c = getClient();
|
|
422
|
+
if (!c.ws)
|
|
423
|
+
throw new Error('WebSocket not connected');
|
|
424
|
+
return c.ws;
|
|
425
|
+
},
|
|
426
|
+
};
|
|
427
|
+
return self;
|
|
428
|
+
}
|
|
429
|
+
//# sourceMappingURL=webSocket.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webSocket.js","sourceRoot":"","sources":["../src/webSocket.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rpckit/websocket",
|
|
3
|
+
"version": "0.9.99",
|
|
4
|
+
"description": "WebSocket transport for rpckit with subscriptions, keep-alive, and reconnection",
|
|
5
|
+
"author": "mainnet_pat",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"types": "dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./electrum-cash": {
|
|
17
|
+
"types": "./dist/electrum-cash/index.d.ts",
|
|
18
|
+
"default": "./dist/electrum-cash/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./ethereum": {
|
|
21
|
+
"types": "./dist/ethereum/index.d.ts",
|
|
22
|
+
"default": "./dist/ethereum/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"keywords": [
|
|
29
|
+
"json-rpc",
|
|
30
|
+
"rpc",
|
|
31
|
+
"websocket",
|
|
32
|
+
"electrum",
|
|
33
|
+
"electrum-cash",
|
|
34
|
+
"ethereum",
|
|
35
|
+
"typescript"
|
|
36
|
+
],
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "git+https://github.com/mainnet-pat/rpckit.git",
|
|
40
|
+
"directory": "packages/websocket"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://rpckit.dev",
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/mainnet-pat/rpckit/issues"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsc"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@rpckit/core": "*",
|
|
51
|
+
"isows": "^1.0.6"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"typescript": "^5.7.0"
|
|
55
|
+
}
|
|
56
|
+
}
|