gennet.js 0.1.0 → 0.2.0
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/LICENSE +21 -0
- package/README.md +8 -0
- package/dist/index.cjs +95 -13
- package/dist/index.d.cts +41 -12
- package/dist/index.d.mts +41 -12
- package/dist/index.d.ts +41 -12
- package/dist/index.mjs +95 -13
- package/package.json +5 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 cryptagoEU
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# gennet.js
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/gennet.js)
|
|
4
|
+
[](https://www.npmjs.com/package/gennet.js)
|
|
5
|
+
[](https://github.com/cryptagoEU/gennet.js/actions/workflows/ci.yml)
|
|
6
|
+
[](https://github.com/cryptagoEU/gennet.js/blob/main/LICENSE)
|
|
7
|
+
|
|
8
|
+
[](https://github.com/cryptagoEU/gennet.js)
|
|
9
|
+
[](https://github.com/cryptagoEU/gennet.js)
|
|
10
|
+
|
|
3
11
|
Client library for [GenNet](https://github.com/cryptagoEU/gennet.js) — interact with GenNet nodes via JSON-RPC.
|
|
4
12
|
|
|
5
13
|
- Zero runtime dependencies
|
package/dist/index.cjs
CHANGED
|
@@ -38,7 +38,7 @@ class HttpProvider {
|
|
|
38
38
|
}
|
|
39
39
|
return json.result;
|
|
40
40
|
}
|
|
41
|
-
// HTTP unterstützt keine
|
|
41
|
+
// HTTP unterstützt keine Events
|
|
42
42
|
on(_event, _listener) {
|
|
43
43
|
}
|
|
44
44
|
off(_event, _listener) {
|
|
@@ -48,17 +48,32 @@ class HttpProvider {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
const DEFAULT_TIMEOUT = 3e4;
|
|
51
|
+
const DEFAULT_RECONNECT = {
|
|
52
|
+
enabled: true,
|
|
53
|
+
maxRetries: 5,
|
|
54
|
+
delay: 1e3,
|
|
55
|
+
maxDelay: 3e4
|
|
56
|
+
};
|
|
51
57
|
class WebSocketProvider {
|
|
52
58
|
url;
|
|
53
59
|
timeout;
|
|
60
|
+
reconnectOpts;
|
|
54
61
|
ws = null;
|
|
55
62
|
requestId = 0;
|
|
63
|
+
reconnectAttempts = 0;
|
|
64
|
+
reconnectTimer = null;
|
|
65
|
+
manualDisconnect = false;
|
|
56
66
|
pending = /* @__PURE__ */ new Map();
|
|
57
|
-
|
|
67
|
+
// Typisierte Event-Listener
|
|
68
|
+
notificationListeners = /* @__PURE__ */ new Set();
|
|
69
|
+
connectListeners = /* @__PURE__ */ new Set();
|
|
70
|
+
disconnectListeners = /* @__PURE__ */ new Set();
|
|
71
|
+
errorListeners = /* @__PURE__ */ new Set();
|
|
58
72
|
connectPromise = null;
|
|
59
|
-
constructor(url,
|
|
73
|
+
constructor(url, options) {
|
|
60
74
|
this.url = url;
|
|
61
|
-
this.timeout = timeout;
|
|
75
|
+
this.timeout = options?.timeout ?? DEFAULT_TIMEOUT;
|
|
76
|
+
this.reconnectOpts = { ...DEFAULT_RECONNECT, ...options?.reconnect };
|
|
62
77
|
}
|
|
63
78
|
get connected() {
|
|
64
79
|
return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
|
|
@@ -66,26 +81,36 @@ class WebSocketProvider {
|
|
|
66
81
|
async connect() {
|
|
67
82
|
if (this.connected) return;
|
|
68
83
|
if (this.connectPromise) return this.connectPromise;
|
|
84
|
+
this.manualDisconnect = false;
|
|
69
85
|
this.connectPromise = new Promise((resolve, reject) => {
|
|
70
86
|
this.ws = new WebSocket(this.url);
|
|
71
87
|
this.ws.onopen = () => {
|
|
72
88
|
this.connectPromise = null;
|
|
89
|
+
this.reconnectAttempts = 0;
|
|
90
|
+
this.emit("connect");
|
|
73
91
|
resolve();
|
|
74
92
|
};
|
|
75
93
|
this.ws.onerror = (ev) => {
|
|
76
94
|
this.connectPromise = null;
|
|
77
|
-
|
|
95
|
+
const error = new Error(`WebSocket-Verbindung fehlgeschlagen: ${this.url} (${ev})`);
|
|
96
|
+
this.emit("error", error);
|
|
97
|
+
reject(error);
|
|
78
98
|
};
|
|
79
99
|
this.ws.onmessage = (ev) => {
|
|
80
100
|
this.handleMessage(typeof ev.data === "string" ? ev.data : String(ev.data));
|
|
81
101
|
};
|
|
82
102
|
this.ws.onclose = () => {
|
|
83
103
|
this.connectPromise = null;
|
|
104
|
+
this.ws = null;
|
|
84
105
|
for (const [id, req] of this.pending) {
|
|
85
106
|
clearTimeout(req.timer);
|
|
86
107
|
req.reject(new Error("WebSocket-Verbindung geschlossen"));
|
|
87
108
|
this.pending.delete(id);
|
|
88
109
|
}
|
|
110
|
+
this.emit("disconnect");
|
|
111
|
+
if (!this.manualDisconnect && this.reconnectOpts.enabled) {
|
|
112
|
+
this.scheduleReconnect();
|
|
113
|
+
}
|
|
89
114
|
};
|
|
90
115
|
});
|
|
91
116
|
return this.connectPromise;
|
|
@@ -105,19 +130,71 @@ class WebSocketProvider {
|
|
|
105
130
|
this.ws.send(payload);
|
|
106
131
|
});
|
|
107
132
|
}
|
|
108
|
-
on(
|
|
109
|
-
this.
|
|
133
|
+
on(event, listener) {
|
|
134
|
+
this.getListenerSet(event).add(listener);
|
|
110
135
|
}
|
|
111
|
-
off(
|
|
112
|
-
this.
|
|
136
|
+
off(event, listener) {
|
|
137
|
+
this.getListenerSet(event).delete(listener);
|
|
113
138
|
}
|
|
114
139
|
disconnect() {
|
|
140
|
+
this.manualDisconnect = true;
|
|
141
|
+
if (this.reconnectTimer) {
|
|
142
|
+
clearTimeout(this.reconnectTimer);
|
|
143
|
+
this.reconnectTimer = null;
|
|
144
|
+
}
|
|
115
145
|
if (this.ws) {
|
|
116
146
|
this.ws.close();
|
|
117
147
|
this.ws = null;
|
|
118
148
|
}
|
|
119
149
|
}
|
|
120
150
|
// ── Private ────────────────────────────────────────────────
|
|
151
|
+
scheduleReconnect() {
|
|
152
|
+
if (this.reconnectAttempts >= this.reconnectOpts.maxRetries) {
|
|
153
|
+
this.emit("error", new Error(
|
|
154
|
+
`Reconnect fehlgeschlagen nach ${this.reconnectOpts.maxRetries} Versuchen`
|
|
155
|
+
));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const delay = Math.min(
|
|
159
|
+
this.reconnectOpts.delay * Math.pow(2, this.reconnectAttempts),
|
|
160
|
+
this.reconnectOpts.maxDelay
|
|
161
|
+
);
|
|
162
|
+
this.reconnectAttempts++;
|
|
163
|
+
this.reconnectTimer = setTimeout(() => {
|
|
164
|
+
this.reconnectTimer = null;
|
|
165
|
+
this.connect().catch((err) => {
|
|
166
|
+
this.emit("error", err instanceof Error ? err : new Error(String(err)));
|
|
167
|
+
});
|
|
168
|
+
}, delay);
|
|
169
|
+
}
|
|
170
|
+
emit(event, data) {
|
|
171
|
+
switch (event) {
|
|
172
|
+
case "connect":
|
|
173
|
+
for (const l of this.connectListeners) l();
|
|
174
|
+
break;
|
|
175
|
+
case "disconnect":
|
|
176
|
+
for (const l of this.disconnectListeners) l();
|
|
177
|
+
break;
|
|
178
|
+
case "error":
|
|
179
|
+
for (const l of this.errorListeners) l(data);
|
|
180
|
+
break;
|
|
181
|
+
case "notification":
|
|
182
|
+
for (const l of this.notificationListeners) l(data);
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
getListenerSet(event) {
|
|
187
|
+
switch (event) {
|
|
188
|
+
case "notification":
|
|
189
|
+
return this.notificationListeners;
|
|
190
|
+
case "connect":
|
|
191
|
+
return this.connectListeners;
|
|
192
|
+
case "disconnect":
|
|
193
|
+
return this.disconnectListeners;
|
|
194
|
+
case "error":
|
|
195
|
+
return this.errorListeners;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
121
198
|
handleMessage(raw) {
|
|
122
199
|
let msg;
|
|
123
200
|
try {
|
|
@@ -141,10 +218,7 @@ class WebSocketProvider {
|
|
|
141
218
|
return;
|
|
142
219
|
}
|
|
143
220
|
if ("method" in msg && "params" in msg) {
|
|
144
|
-
|
|
145
|
-
for (const listener of this.listeners) {
|
|
146
|
-
listener(notification);
|
|
147
|
-
}
|
|
221
|
+
this.emit("notification", msg);
|
|
148
222
|
}
|
|
149
223
|
}
|
|
150
224
|
}
|
|
@@ -284,6 +358,14 @@ class GenNet {
|
|
|
284
358
|
}
|
|
285
359
|
};
|
|
286
360
|
}
|
|
361
|
+
/** Event-Listener registrieren (connect, disconnect, error). */
|
|
362
|
+
on(event, listener) {
|
|
363
|
+
this.provider.on(event, listener);
|
|
364
|
+
}
|
|
365
|
+
/** Event-Listener entfernen. */
|
|
366
|
+
off(event, listener) {
|
|
367
|
+
this.provider.off(event, listener);
|
|
368
|
+
}
|
|
287
369
|
/** Raw JSON-RPC Request (für erweiterte Nutzung). */
|
|
288
370
|
async request(method, params) {
|
|
289
371
|
return this.provider.request(method, params);
|
package/dist/index.d.cts
CHANGED
|
@@ -82,13 +82,15 @@ interface Subscription {
|
|
|
82
82
|
id: string;
|
|
83
83
|
unsubscribe: () => Promise<boolean>;
|
|
84
84
|
}
|
|
85
|
+
type ProviderEvent = 'notification' | 'connect' | 'disconnect' | 'error';
|
|
86
|
+
type ProviderEventListener<E extends ProviderEvent> = E extends 'notification' ? (notification: JsonRpcNotification) => void : E extends 'connect' ? () => void : E extends 'disconnect' ? () => void : E extends 'error' ? (error: Error) => void : never;
|
|
85
87
|
interface Provider {
|
|
86
88
|
/** JSON-RPC Request senden und auf Response warten. */
|
|
87
89
|
request(method: string, params?: Record<string, unknown> | unknown[]): Promise<unknown>;
|
|
88
|
-
/** Listener
|
|
89
|
-
on(event:
|
|
90
|
-
/** Listener entfernen. */
|
|
91
|
-
off(event:
|
|
90
|
+
/** Event-Listener registrieren. */
|
|
91
|
+
on<E extends ProviderEvent>(event: E, listener: ProviderEventListener<E>): void;
|
|
92
|
+
/** Event-Listener entfernen. */
|
|
93
|
+
off<E extends ProviderEvent>(event: E, listener: ProviderEventListener<E>): void;
|
|
92
94
|
/** Verbindung schließen. */
|
|
93
95
|
disconnect(): void;
|
|
94
96
|
/** Verbindung herstellen (bei WebSocket). */
|
|
@@ -96,6 +98,16 @@ interface Provider {
|
|
|
96
98
|
/** Ob eine aktive Verbindung besteht. */
|
|
97
99
|
readonly connected: boolean;
|
|
98
100
|
}
|
|
101
|
+
interface ReconnectOptions {
|
|
102
|
+
/** Auto-Reconnect aktivieren. Default: true */
|
|
103
|
+
enabled?: boolean;
|
|
104
|
+
/** Maximale Anzahl Reconnect-Versuche. Default: 5 */
|
|
105
|
+
maxRetries?: number;
|
|
106
|
+
/** Initiale Wartezeit in ms (verdoppelt sich pro Versuch). Default: 1000 */
|
|
107
|
+
delay?: number;
|
|
108
|
+
/** Maximale Wartezeit in ms. Default: 30000 */
|
|
109
|
+
maxDelay?: number;
|
|
110
|
+
}
|
|
99
111
|
|
|
100
112
|
/** admin Namespace — Node-Administration. */
|
|
101
113
|
declare class Admin {
|
|
@@ -216,6 +228,10 @@ declare class GenNet {
|
|
|
216
228
|
* Topics: 'logs', 'messages', 'mempool'.
|
|
217
229
|
*/
|
|
218
230
|
subscribe(topic: SubscriptionTopic, callback: (data: unknown) => void): Promise<Subscription>;
|
|
231
|
+
/** Event-Listener registrieren (connect, disconnect, error). */
|
|
232
|
+
on<E extends ProviderEvent>(event: E, listener: ProviderEventListener<E>): void;
|
|
233
|
+
/** Event-Listener entfernen. */
|
|
234
|
+
off<E extends ProviderEvent>(event: E, listener: ProviderEventListener<E>): void;
|
|
219
235
|
/** Raw JSON-RPC Request (für erweiterte Nutzung). */
|
|
220
236
|
request(method: string, params?: Record<string, unknown> | unknown[]): Promise<unknown>;
|
|
221
237
|
private static createProvider;
|
|
@@ -223,23 +239,36 @@ declare class GenNet {
|
|
|
223
239
|
|
|
224
240
|
/**
|
|
225
241
|
* WebSocket Provider — nutzt native WebSocket API (Browser + Node 22+).
|
|
226
|
-
* Unterstützt Subscriptions via Push-Notifications.
|
|
242
|
+
* Unterstützt Subscriptions via Push-Notifications und Auto-Reconnect.
|
|
227
243
|
*/
|
|
228
244
|
declare class WebSocketProvider implements Provider {
|
|
229
245
|
private readonly url;
|
|
230
246
|
private readonly timeout;
|
|
247
|
+
private readonly reconnectOpts;
|
|
231
248
|
private ws;
|
|
232
249
|
private requestId;
|
|
250
|
+
private reconnectAttempts;
|
|
251
|
+
private reconnectTimer;
|
|
252
|
+
private manualDisconnect;
|
|
233
253
|
private pending;
|
|
234
|
-
private
|
|
254
|
+
private notificationListeners;
|
|
255
|
+
private connectListeners;
|
|
256
|
+
private disconnectListeners;
|
|
257
|
+
private errorListeners;
|
|
235
258
|
private connectPromise;
|
|
236
|
-
constructor(url: string,
|
|
259
|
+
constructor(url: string, options?: {
|
|
260
|
+
timeout?: number;
|
|
261
|
+
reconnect?: ReconnectOptions;
|
|
262
|
+
});
|
|
237
263
|
get connected(): boolean;
|
|
238
264
|
connect(): Promise<void>;
|
|
239
265
|
request(method: string, params?: Record<string, unknown> | unknown[]): Promise<unknown>;
|
|
240
|
-
on(
|
|
241
|
-
off(
|
|
266
|
+
on<E extends ProviderEvent>(event: E, listener: ProviderEventListener<E>): void;
|
|
267
|
+
off<E extends ProviderEvent>(event: E, listener: ProviderEventListener<E>): void;
|
|
242
268
|
disconnect(): void;
|
|
269
|
+
private scheduleReconnect;
|
|
270
|
+
private emit;
|
|
271
|
+
private getListenerSet;
|
|
243
272
|
private handleMessage;
|
|
244
273
|
}
|
|
245
274
|
|
|
@@ -252,10 +281,10 @@ declare class HttpProvider implements Provider {
|
|
|
252
281
|
constructor(url: string);
|
|
253
282
|
get connected(): boolean;
|
|
254
283
|
request(method: string, params?: Record<string, unknown> | unknown[]): Promise<unknown>;
|
|
255
|
-
on(_event:
|
|
256
|
-
off(_event:
|
|
284
|
+
on<E extends ProviderEvent>(_event: E, _listener: ProviderEventListener<E>): void;
|
|
285
|
+
off<E extends ProviderEvent>(_event: E, _listener: ProviderEventListener<E>): void;
|
|
257
286
|
disconnect(): void;
|
|
258
287
|
}
|
|
259
288
|
|
|
260
289
|
export { Admin, Agent, GenNet, HttpProvider, Mempool, Net, Personal, RpcError, WebSocketProvider };
|
|
261
|
-
export type { AgentResult, GatewayState, IdentityInfo, JsonRpcErrorResponse, JsonRpcNotification, JsonRpcRequest, JsonRpcResponse, JsonRpcSuccessResponse, ModuleInfo, NodeInfo, PeerInfo, Provider, Subscription, SubscriptionTopic };
|
|
290
|
+
export type { AgentResult, GatewayState, IdentityInfo, JsonRpcErrorResponse, JsonRpcNotification, JsonRpcRequest, JsonRpcResponse, JsonRpcSuccessResponse, ModuleInfo, NodeInfo, PeerInfo, Provider, ProviderEvent, ProviderEventListener, ReconnectOptions, Subscription, SubscriptionTopic };
|
package/dist/index.d.mts
CHANGED
|
@@ -82,13 +82,15 @@ interface Subscription {
|
|
|
82
82
|
id: string;
|
|
83
83
|
unsubscribe: () => Promise<boolean>;
|
|
84
84
|
}
|
|
85
|
+
type ProviderEvent = 'notification' | 'connect' | 'disconnect' | 'error';
|
|
86
|
+
type ProviderEventListener<E extends ProviderEvent> = E extends 'notification' ? (notification: JsonRpcNotification) => void : E extends 'connect' ? () => void : E extends 'disconnect' ? () => void : E extends 'error' ? (error: Error) => void : never;
|
|
85
87
|
interface Provider {
|
|
86
88
|
/** JSON-RPC Request senden und auf Response warten. */
|
|
87
89
|
request(method: string, params?: Record<string, unknown> | unknown[]): Promise<unknown>;
|
|
88
|
-
/** Listener
|
|
89
|
-
on(event:
|
|
90
|
-
/** Listener entfernen. */
|
|
91
|
-
off(event:
|
|
90
|
+
/** Event-Listener registrieren. */
|
|
91
|
+
on<E extends ProviderEvent>(event: E, listener: ProviderEventListener<E>): void;
|
|
92
|
+
/** Event-Listener entfernen. */
|
|
93
|
+
off<E extends ProviderEvent>(event: E, listener: ProviderEventListener<E>): void;
|
|
92
94
|
/** Verbindung schließen. */
|
|
93
95
|
disconnect(): void;
|
|
94
96
|
/** Verbindung herstellen (bei WebSocket). */
|
|
@@ -96,6 +98,16 @@ interface Provider {
|
|
|
96
98
|
/** Ob eine aktive Verbindung besteht. */
|
|
97
99
|
readonly connected: boolean;
|
|
98
100
|
}
|
|
101
|
+
interface ReconnectOptions {
|
|
102
|
+
/** Auto-Reconnect aktivieren. Default: true */
|
|
103
|
+
enabled?: boolean;
|
|
104
|
+
/** Maximale Anzahl Reconnect-Versuche. Default: 5 */
|
|
105
|
+
maxRetries?: number;
|
|
106
|
+
/** Initiale Wartezeit in ms (verdoppelt sich pro Versuch). Default: 1000 */
|
|
107
|
+
delay?: number;
|
|
108
|
+
/** Maximale Wartezeit in ms. Default: 30000 */
|
|
109
|
+
maxDelay?: number;
|
|
110
|
+
}
|
|
99
111
|
|
|
100
112
|
/** admin Namespace — Node-Administration. */
|
|
101
113
|
declare class Admin {
|
|
@@ -216,6 +228,10 @@ declare class GenNet {
|
|
|
216
228
|
* Topics: 'logs', 'messages', 'mempool'.
|
|
217
229
|
*/
|
|
218
230
|
subscribe(topic: SubscriptionTopic, callback: (data: unknown) => void): Promise<Subscription>;
|
|
231
|
+
/** Event-Listener registrieren (connect, disconnect, error). */
|
|
232
|
+
on<E extends ProviderEvent>(event: E, listener: ProviderEventListener<E>): void;
|
|
233
|
+
/** Event-Listener entfernen. */
|
|
234
|
+
off<E extends ProviderEvent>(event: E, listener: ProviderEventListener<E>): void;
|
|
219
235
|
/** Raw JSON-RPC Request (für erweiterte Nutzung). */
|
|
220
236
|
request(method: string, params?: Record<string, unknown> | unknown[]): Promise<unknown>;
|
|
221
237
|
private static createProvider;
|
|
@@ -223,23 +239,36 @@ declare class GenNet {
|
|
|
223
239
|
|
|
224
240
|
/**
|
|
225
241
|
* WebSocket Provider — nutzt native WebSocket API (Browser + Node 22+).
|
|
226
|
-
* Unterstützt Subscriptions via Push-Notifications.
|
|
242
|
+
* Unterstützt Subscriptions via Push-Notifications und Auto-Reconnect.
|
|
227
243
|
*/
|
|
228
244
|
declare class WebSocketProvider implements Provider {
|
|
229
245
|
private readonly url;
|
|
230
246
|
private readonly timeout;
|
|
247
|
+
private readonly reconnectOpts;
|
|
231
248
|
private ws;
|
|
232
249
|
private requestId;
|
|
250
|
+
private reconnectAttempts;
|
|
251
|
+
private reconnectTimer;
|
|
252
|
+
private manualDisconnect;
|
|
233
253
|
private pending;
|
|
234
|
-
private
|
|
254
|
+
private notificationListeners;
|
|
255
|
+
private connectListeners;
|
|
256
|
+
private disconnectListeners;
|
|
257
|
+
private errorListeners;
|
|
235
258
|
private connectPromise;
|
|
236
|
-
constructor(url: string,
|
|
259
|
+
constructor(url: string, options?: {
|
|
260
|
+
timeout?: number;
|
|
261
|
+
reconnect?: ReconnectOptions;
|
|
262
|
+
});
|
|
237
263
|
get connected(): boolean;
|
|
238
264
|
connect(): Promise<void>;
|
|
239
265
|
request(method: string, params?: Record<string, unknown> | unknown[]): Promise<unknown>;
|
|
240
|
-
on(
|
|
241
|
-
off(
|
|
266
|
+
on<E extends ProviderEvent>(event: E, listener: ProviderEventListener<E>): void;
|
|
267
|
+
off<E extends ProviderEvent>(event: E, listener: ProviderEventListener<E>): void;
|
|
242
268
|
disconnect(): void;
|
|
269
|
+
private scheduleReconnect;
|
|
270
|
+
private emit;
|
|
271
|
+
private getListenerSet;
|
|
243
272
|
private handleMessage;
|
|
244
273
|
}
|
|
245
274
|
|
|
@@ -252,10 +281,10 @@ declare class HttpProvider implements Provider {
|
|
|
252
281
|
constructor(url: string);
|
|
253
282
|
get connected(): boolean;
|
|
254
283
|
request(method: string, params?: Record<string, unknown> | unknown[]): Promise<unknown>;
|
|
255
|
-
on(_event:
|
|
256
|
-
off(_event:
|
|
284
|
+
on<E extends ProviderEvent>(_event: E, _listener: ProviderEventListener<E>): void;
|
|
285
|
+
off<E extends ProviderEvent>(_event: E, _listener: ProviderEventListener<E>): void;
|
|
257
286
|
disconnect(): void;
|
|
258
287
|
}
|
|
259
288
|
|
|
260
289
|
export { Admin, Agent, GenNet, HttpProvider, Mempool, Net, Personal, RpcError, WebSocketProvider };
|
|
261
|
-
export type { AgentResult, GatewayState, IdentityInfo, JsonRpcErrorResponse, JsonRpcNotification, JsonRpcRequest, JsonRpcResponse, JsonRpcSuccessResponse, ModuleInfo, NodeInfo, PeerInfo, Provider, Subscription, SubscriptionTopic };
|
|
290
|
+
export type { AgentResult, GatewayState, IdentityInfo, JsonRpcErrorResponse, JsonRpcNotification, JsonRpcRequest, JsonRpcResponse, JsonRpcSuccessResponse, ModuleInfo, NodeInfo, PeerInfo, Provider, ProviderEvent, ProviderEventListener, ReconnectOptions, Subscription, SubscriptionTopic };
|
package/dist/index.d.ts
CHANGED
|
@@ -82,13 +82,15 @@ interface Subscription {
|
|
|
82
82
|
id: string;
|
|
83
83
|
unsubscribe: () => Promise<boolean>;
|
|
84
84
|
}
|
|
85
|
+
type ProviderEvent = 'notification' | 'connect' | 'disconnect' | 'error';
|
|
86
|
+
type ProviderEventListener<E extends ProviderEvent> = E extends 'notification' ? (notification: JsonRpcNotification) => void : E extends 'connect' ? () => void : E extends 'disconnect' ? () => void : E extends 'error' ? (error: Error) => void : never;
|
|
85
87
|
interface Provider {
|
|
86
88
|
/** JSON-RPC Request senden und auf Response warten. */
|
|
87
89
|
request(method: string, params?: Record<string, unknown> | unknown[]): Promise<unknown>;
|
|
88
|
-
/** Listener
|
|
89
|
-
on(event:
|
|
90
|
-
/** Listener entfernen. */
|
|
91
|
-
off(event:
|
|
90
|
+
/** Event-Listener registrieren. */
|
|
91
|
+
on<E extends ProviderEvent>(event: E, listener: ProviderEventListener<E>): void;
|
|
92
|
+
/** Event-Listener entfernen. */
|
|
93
|
+
off<E extends ProviderEvent>(event: E, listener: ProviderEventListener<E>): void;
|
|
92
94
|
/** Verbindung schließen. */
|
|
93
95
|
disconnect(): void;
|
|
94
96
|
/** Verbindung herstellen (bei WebSocket). */
|
|
@@ -96,6 +98,16 @@ interface Provider {
|
|
|
96
98
|
/** Ob eine aktive Verbindung besteht. */
|
|
97
99
|
readonly connected: boolean;
|
|
98
100
|
}
|
|
101
|
+
interface ReconnectOptions {
|
|
102
|
+
/** Auto-Reconnect aktivieren. Default: true */
|
|
103
|
+
enabled?: boolean;
|
|
104
|
+
/** Maximale Anzahl Reconnect-Versuche. Default: 5 */
|
|
105
|
+
maxRetries?: number;
|
|
106
|
+
/** Initiale Wartezeit in ms (verdoppelt sich pro Versuch). Default: 1000 */
|
|
107
|
+
delay?: number;
|
|
108
|
+
/** Maximale Wartezeit in ms. Default: 30000 */
|
|
109
|
+
maxDelay?: number;
|
|
110
|
+
}
|
|
99
111
|
|
|
100
112
|
/** admin Namespace — Node-Administration. */
|
|
101
113
|
declare class Admin {
|
|
@@ -216,6 +228,10 @@ declare class GenNet {
|
|
|
216
228
|
* Topics: 'logs', 'messages', 'mempool'.
|
|
217
229
|
*/
|
|
218
230
|
subscribe(topic: SubscriptionTopic, callback: (data: unknown) => void): Promise<Subscription>;
|
|
231
|
+
/** Event-Listener registrieren (connect, disconnect, error). */
|
|
232
|
+
on<E extends ProviderEvent>(event: E, listener: ProviderEventListener<E>): void;
|
|
233
|
+
/** Event-Listener entfernen. */
|
|
234
|
+
off<E extends ProviderEvent>(event: E, listener: ProviderEventListener<E>): void;
|
|
219
235
|
/** Raw JSON-RPC Request (für erweiterte Nutzung). */
|
|
220
236
|
request(method: string, params?: Record<string, unknown> | unknown[]): Promise<unknown>;
|
|
221
237
|
private static createProvider;
|
|
@@ -223,23 +239,36 @@ declare class GenNet {
|
|
|
223
239
|
|
|
224
240
|
/**
|
|
225
241
|
* WebSocket Provider — nutzt native WebSocket API (Browser + Node 22+).
|
|
226
|
-
* Unterstützt Subscriptions via Push-Notifications.
|
|
242
|
+
* Unterstützt Subscriptions via Push-Notifications und Auto-Reconnect.
|
|
227
243
|
*/
|
|
228
244
|
declare class WebSocketProvider implements Provider {
|
|
229
245
|
private readonly url;
|
|
230
246
|
private readonly timeout;
|
|
247
|
+
private readonly reconnectOpts;
|
|
231
248
|
private ws;
|
|
232
249
|
private requestId;
|
|
250
|
+
private reconnectAttempts;
|
|
251
|
+
private reconnectTimer;
|
|
252
|
+
private manualDisconnect;
|
|
233
253
|
private pending;
|
|
234
|
-
private
|
|
254
|
+
private notificationListeners;
|
|
255
|
+
private connectListeners;
|
|
256
|
+
private disconnectListeners;
|
|
257
|
+
private errorListeners;
|
|
235
258
|
private connectPromise;
|
|
236
|
-
constructor(url: string,
|
|
259
|
+
constructor(url: string, options?: {
|
|
260
|
+
timeout?: number;
|
|
261
|
+
reconnect?: ReconnectOptions;
|
|
262
|
+
});
|
|
237
263
|
get connected(): boolean;
|
|
238
264
|
connect(): Promise<void>;
|
|
239
265
|
request(method: string, params?: Record<string, unknown> | unknown[]): Promise<unknown>;
|
|
240
|
-
on(
|
|
241
|
-
off(
|
|
266
|
+
on<E extends ProviderEvent>(event: E, listener: ProviderEventListener<E>): void;
|
|
267
|
+
off<E extends ProviderEvent>(event: E, listener: ProviderEventListener<E>): void;
|
|
242
268
|
disconnect(): void;
|
|
269
|
+
private scheduleReconnect;
|
|
270
|
+
private emit;
|
|
271
|
+
private getListenerSet;
|
|
243
272
|
private handleMessage;
|
|
244
273
|
}
|
|
245
274
|
|
|
@@ -252,10 +281,10 @@ declare class HttpProvider implements Provider {
|
|
|
252
281
|
constructor(url: string);
|
|
253
282
|
get connected(): boolean;
|
|
254
283
|
request(method: string, params?: Record<string, unknown> | unknown[]): Promise<unknown>;
|
|
255
|
-
on(_event:
|
|
256
|
-
off(_event:
|
|
284
|
+
on<E extends ProviderEvent>(_event: E, _listener: ProviderEventListener<E>): void;
|
|
285
|
+
off<E extends ProviderEvent>(_event: E, _listener: ProviderEventListener<E>): void;
|
|
257
286
|
disconnect(): void;
|
|
258
287
|
}
|
|
259
288
|
|
|
260
289
|
export { Admin, Agent, GenNet, HttpProvider, Mempool, Net, Personal, RpcError, WebSocketProvider };
|
|
261
|
-
export type { AgentResult, GatewayState, IdentityInfo, JsonRpcErrorResponse, JsonRpcNotification, JsonRpcRequest, JsonRpcResponse, JsonRpcSuccessResponse, ModuleInfo, NodeInfo, PeerInfo, Provider, Subscription, SubscriptionTopic };
|
|
290
|
+
export type { AgentResult, GatewayState, IdentityInfo, JsonRpcErrorResponse, JsonRpcNotification, JsonRpcRequest, JsonRpcResponse, JsonRpcSuccessResponse, ModuleInfo, NodeInfo, PeerInfo, Provider, ProviderEvent, ProviderEventListener, ReconnectOptions, Subscription, SubscriptionTopic };
|
package/dist/index.mjs
CHANGED
|
@@ -36,7 +36,7 @@ class HttpProvider {
|
|
|
36
36
|
}
|
|
37
37
|
return json.result;
|
|
38
38
|
}
|
|
39
|
-
// HTTP unterstützt keine
|
|
39
|
+
// HTTP unterstützt keine Events
|
|
40
40
|
on(_event, _listener) {
|
|
41
41
|
}
|
|
42
42
|
off(_event, _listener) {
|
|
@@ -46,17 +46,32 @@ class HttpProvider {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
const DEFAULT_TIMEOUT = 3e4;
|
|
49
|
+
const DEFAULT_RECONNECT = {
|
|
50
|
+
enabled: true,
|
|
51
|
+
maxRetries: 5,
|
|
52
|
+
delay: 1e3,
|
|
53
|
+
maxDelay: 3e4
|
|
54
|
+
};
|
|
49
55
|
class WebSocketProvider {
|
|
50
56
|
url;
|
|
51
57
|
timeout;
|
|
58
|
+
reconnectOpts;
|
|
52
59
|
ws = null;
|
|
53
60
|
requestId = 0;
|
|
61
|
+
reconnectAttempts = 0;
|
|
62
|
+
reconnectTimer = null;
|
|
63
|
+
manualDisconnect = false;
|
|
54
64
|
pending = /* @__PURE__ */ new Map();
|
|
55
|
-
|
|
65
|
+
// Typisierte Event-Listener
|
|
66
|
+
notificationListeners = /* @__PURE__ */ new Set();
|
|
67
|
+
connectListeners = /* @__PURE__ */ new Set();
|
|
68
|
+
disconnectListeners = /* @__PURE__ */ new Set();
|
|
69
|
+
errorListeners = /* @__PURE__ */ new Set();
|
|
56
70
|
connectPromise = null;
|
|
57
|
-
constructor(url,
|
|
71
|
+
constructor(url, options) {
|
|
58
72
|
this.url = url;
|
|
59
|
-
this.timeout = timeout;
|
|
73
|
+
this.timeout = options?.timeout ?? DEFAULT_TIMEOUT;
|
|
74
|
+
this.reconnectOpts = { ...DEFAULT_RECONNECT, ...options?.reconnect };
|
|
60
75
|
}
|
|
61
76
|
get connected() {
|
|
62
77
|
return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
|
|
@@ -64,26 +79,36 @@ class WebSocketProvider {
|
|
|
64
79
|
async connect() {
|
|
65
80
|
if (this.connected) return;
|
|
66
81
|
if (this.connectPromise) return this.connectPromise;
|
|
82
|
+
this.manualDisconnect = false;
|
|
67
83
|
this.connectPromise = new Promise((resolve, reject) => {
|
|
68
84
|
this.ws = new WebSocket(this.url);
|
|
69
85
|
this.ws.onopen = () => {
|
|
70
86
|
this.connectPromise = null;
|
|
87
|
+
this.reconnectAttempts = 0;
|
|
88
|
+
this.emit("connect");
|
|
71
89
|
resolve();
|
|
72
90
|
};
|
|
73
91
|
this.ws.onerror = (ev) => {
|
|
74
92
|
this.connectPromise = null;
|
|
75
|
-
|
|
93
|
+
const error = new Error(`WebSocket-Verbindung fehlgeschlagen: ${this.url} (${ev})`);
|
|
94
|
+
this.emit("error", error);
|
|
95
|
+
reject(error);
|
|
76
96
|
};
|
|
77
97
|
this.ws.onmessage = (ev) => {
|
|
78
98
|
this.handleMessage(typeof ev.data === "string" ? ev.data : String(ev.data));
|
|
79
99
|
};
|
|
80
100
|
this.ws.onclose = () => {
|
|
81
101
|
this.connectPromise = null;
|
|
102
|
+
this.ws = null;
|
|
82
103
|
for (const [id, req] of this.pending) {
|
|
83
104
|
clearTimeout(req.timer);
|
|
84
105
|
req.reject(new Error("WebSocket-Verbindung geschlossen"));
|
|
85
106
|
this.pending.delete(id);
|
|
86
107
|
}
|
|
108
|
+
this.emit("disconnect");
|
|
109
|
+
if (!this.manualDisconnect && this.reconnectOpts.enabled) {
|
|
110
|
+
this.scheduleReconnect();
|
|
111
|
+
}
|
|
87
112
|
};
|
|
88
113
|
});
|
|
89
114
|
return this.connectPromise;
|
|
@@ -103,19 +128,71 @@ class WebSocketProvider {
|
|
|
103
128
|
this.ws.send(payload);
|
|
104
129
|
});
|
|
105
130
|
}
|
|
106
|
-
on(
|
|
107
|
-
this.
|
|
131
|
+
on(event, listener) {
|
|
132
|
+
this.getListenerSet(event).add(listener);
|
|
108
133
|
}
|
|
109
|
-
off(
|
|
110
|
-
this.
|
|
134
|
+
off(event, listener) {
|
|
135
|
+
this.getListenerSet(event).delete(listener);
|
|
111
136
|
}
|
|
112
137
|
disconnect() {
|
|
138
|
+
this.manualDisconnect = true;
|
|
139
|
+
if (this.reconnectTimer) {
|
|
140
|
+
clearTimeout(this.reconnectTimer);
|
|
141
|
+
this.reconnectTimer = null;
|
|
142
|
+
}
|
|
113
143
|
if (this.ws) {
|
|
114
144
|
this.ws.close();
|
|
115
145
|
this.ws = null;
|
|
116
146
|
}
|
|
117
147
|
}
|
|
118
148
|
// ── Private ────────────────────────────────────────────────
|
|
149
|
+
scheduleReconnect() {
|
|
150
|
+
if (this.reconnectAttempts >= this.reconnectOpts.maxRetries) {
|
|
151
|
+
this.emit("error", new Error(
|
|
152
|
+
`Reconnect fehlgeschlagen nach ${this.reconnectOpts.maxRetries} Versuchen`
|
|
153
|
+
));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const delay = Math.min(
|
|
157
|
+
this.reconnectOpts.delay * Math.pow(2, this.reconnectAttempts),
|
|
158
|
+
this.reconnectOpts.maxDelay
|
|
159
|
+
);
|
|
160
|
+
this.reconnectAttempts++;
|
|
161
|
+
this.reconnectTimer = setTimeout(() => {
|
|
162
|
+
this.reconnectTimer = null;
|
|
163
|
+
this.connect().catch((err) => {
|
|
164
|
+
this.emit("error", err instanceof Error ? err : new Error(String(err)));
|
|
165
|
+
});
|
|
166
|
+
}, delay);
|
|
167
|
+
}
|
|
168
|
+
emit(event, data) {
|
|
169
|
+
switch (event) {
|
|
170
|
+
case "connect":
|
|
171
|
+
for (const l of this.connectListeners) l();
|
|
172
|
+
break;
|
|
173
|
+
case "disconnect":
|
|
174
|
+
for (const l of this.disconnectListeners) l();
|
|
175
|
+
break;
|
|
176
|
+
case "error":
|
|
177
|
+
for (const l of this.errorListeners) l(data);
|
|
178
|
+
break;
|
|
179
|
+
case "notification":
|
|
180
|
+
for (const l of this.notificationListeners) l(data);
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
getListenerSet(event) {
|
|
185
|
+
switch (event) {
|
|
186
|
+
case "notification":
|
|
187
|
+
return this.notificationListeners;
|
|
188
|
+
case "connect":
|
|
189
|
+
return this.connectListeners;
|
|
190
|
+
case "disconnect":
|
|
191
|
+
return this.disconnectListeners;
|
|
192
|
+
case "error":
|
|
193
|
+
return this.errorListeners;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
119
196
|
handleMessage(raw) {
|
|
120
197
|
let msg;
|
|
121
198
|
try {
|
|
@@ -139,10 +216,7 @@ class WebSocketProvider {
|
|
|
139
216
|
return;
|
|
140
217
|
}
|
|
141
218
|
if ("method" in msg && "params" in msg) {
|
|
142
|
-
|
|
143
|
-
for (const listener of this.listeners) {
|
|
144
|
-
listener(notification);
|
|
145
|
-
}
|
|
219
|
+
this.emit("notification", msg);
|
|
146
220
|
}
|
|
147
221
|
}
|
|
148
222
|
}
|
|
@@ -282,6 +356,14 @@ class GenNet {
|
|
|
282
356
|
}
|
|
283
357
|
};
|
|
284
358
|
}
|
|
359
|
+
/** Event-Listener registrieren (connect, disconnect, error). */
|
|
360
|
+
on(event, listener) {
|
|
361
|
+
this.provider.on(event, listener);
|
|
362
|
+
}
|
|
363
|
+
/** Event-Listener entfernen. */
|
|
364
|
+
off(event, listener) {
|
|
365
|
+
this.provider.off(event, listener);
|
|
366
|
+
}
|
|
285
367
|
/** Raw JSON-RPC Request (für erweiterte Nutzung). */
|
|
286
368
|
async request(method, params) {
|
|
287
369
|
return this.provider.request(method, params);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gennet.js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Client Library for GenNet — interact with GenNet nodes via JSON-RPC",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"dev": "unbuild --stub",
|
|
28
28
|
"test": "vitest",
|
|
29
29
|
"lint": "eslint src/",
|
|
30
|
+
"changeset": "changeset",
|
|
30
31
|
"check": "publint && attw --pack",
|
|
31
32
|
"prepublishOnly": "npm run build && npm run check"
|
|
32
33
|
},
|
|
@@ -52,9 +53,11 @@
|
|
|
52
53
|
"homepage": "https://github.com/cryptagoEU/gennet.js#readme",
|
|
53
54
|
"devDependencies": {
|
|
54
55
|
"@arethetypeswrong/cli": "^0.17.0",
|
|
56
|
+
"@changesets/changelog-github": "^0.6.0",
|
|
57
|
+
"@changesets/cli": "^2.30.0",
|
|
55
58
|
"publint": "^0.3.0",
|
|
56
59
|
"typescript": "^5.4.0",
|
|
57
60
|
"unbuild": "^3.0.0",
|
|
58
61
|
"vitest": "^3.0.0"
|
|
59
62
|
}
|
|
60
|
-
}
|
|
63
|
+
}
|