nostr-websocket-utils 0.2.1 → 0.2.2
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 +23 -54
- package/dist/client.d.ts +2 -0
- package/dist/client.js +7 -1
- package/dist/server.d.ts +6 -3
- package/dist/server.js +40 -28
- package/dist/types/index.d.ts +3 -0
- package/package.json +16 -6
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
Nostr Websocket Utils
|
|
1
|
+
# Nostr Websocket Utils
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/nostr-websocket-utils)
|
|
4
|
+
[](https://github.com/HumanjavaEnterprises/nostr-websocket-utils/blob/main/LICENSE)
|
|
5
5
|
[](https://github.com/HumanjavaEnterprises/nostr-websocket-utils/actions)
|
|
6
6
|
[](https://www.typescriptlang.org)
|
|
7
7
|
[](https://github.com/prettier/prettier)
|
|
8
8
|
|
|
9
|
-
A TypeScript library providing WebSocket utilities for Nostr applications,
|
|
9
|
+
A TypeScript library providing WebSocket utilities for Nostr applications, focusing on robust connection handling, automatic reconnection, and channel-based messaging. This library has been streamlined to remove any DOM-related code, enhancing performance and maintainability.
|
|
10
10
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
@@ -22,15 +22,16 @@ A TypeScript library providing WebSocket utilities for Nostr applications, with
|
|
|
22
22
|
## Installation
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
-
npm install
|
|
25
|
+
npm install nostr-websocket-utils
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
## Breaking Changes in v0.2.
|
|
28
|
+
## Breaking Changes in v0.2.2
|
|
29
29
|
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
30
|
+
- Removed all DOM-related code to focus solely on WebSocket functionality.
|
|
31
|
+
- Added UUID support for message tracking and correlation.
|
|
32
|
+
- Introduced a new `WebSocketImpl` option for custom WebSocket implementations.
|
|
33
|
+
- Improved TypeScript type safety across the codebase.
|
|
34
|
+
- Enhanced error handling for WebSocket connections.
|
|
34
35
|
|
|
35
36
|
## Usage
|
|
36
37
|
|
|
@@ -39,7 +40,7 @@ npm install @humanjavaenterprises/nostr-websocket-utils
|
|
|
39
40
|
```typescript
|
|
40
41
|
import express from 'express';
|
|
41
42
|
import { createServer } from 'http';
|
|
42
|
-
import { NostrWSServer } from '
|
|
43
|
+
import { NostrWSServer } from 'nostr-websocket-utils';
|
|
43
44
|
import winston from 'winston';
|
|
44
45
|
|
|
45
46
|
const app = express();
|
|
@@ -52,36 +53,19 @@ const logger = winston.createLogger({
|
|
|
52
53
|
transports: [new winston.transports.Console()]
|
|
53
54
|
});
|
|
54
55
|
|
|
55
|
-
// Initialize WebSocket server with
|
|
56
|
-
const
|
|
57
|
-
heartbeatInterval: 30000,
|
|
56
|
+
// Initialize the WebSocket server with custom WebSocket implementation
|
|
57
|
+
const wsServer = new NostrWSServer(server, {
|
|
58
58
|
logger,
|
|
59
59
|
handlers: {
|
|
60
|
-
// Required message handler
|
|
61
60
|
message: async (ws, message) => {
|
|
62
61
|
logger.info('Received message:', message);
|
|
63
|
-
|
|
64
|
-
// Example: Handle different message types
|
|
65
|
-
switch (message.type) {
|
|
66
|
-
case 'subscribe':
|
|
67
|
-
// Handle subscription
|
|
68
|
-
break;
|
|
69
|
-
case 'event':
|
|
70
|
-
// Broadcast to specific channel
|
|
71
|
-
wss.broadcastToChannel('my-channel', {
|
|
72
|
-
type: 'event',
|
|
73
|
-
data: { content: 'Hello channel!' }
|
|
74
|
-
});
|
|
75
|
-
break;
|
|
76
|
-
}
|
|
62
|
+
// Handle message
|
|
77
63
|
},
|
|
78
|
-
// Optional error handler
|
|
79
64
|
error: (ws, error) => {
|
|
80
65
|
logger.error('WebSocket error:', error);
|
|
81
66
|
},
|
|
82
|
-
// Optional close handler
|
|
83
67
|
close: (ws) => {
|
|
84
|
-
logger.info('
|
|
68
|
+
logger.info('Connection closed');
|
|
85
69
|
}
|
|
86
70
|
}
|
|
87
71
|
});
|
|
@@ -92,39 +76,22 @@ server.listen(3000);
|
|
|
92
76
|
### Client Example
|
|
93
77
|
|
|
94
78
|
```typescript
|
|
95
|
-
import { NostrWSClient } from '
|
|
79
|
+
import { NostrWSClient } from 'nostr-websocket-utils';
|
|
96
80
|
import winston from 'winston';
|
|
97
81
|
|
|
98
|
-
// Create a logger
|
|
99
82
|
const logger = winston.createLogger({
|
|
100
83
|
level: 'info',
|
|
101
84
|
format: winston.format.json(),
|
|
102
85
|
transports: [new winston.transports.Console()]
|
|
103
86
|
});
|
|
104
87
|
|
|
105
|
-
const client = new NostrWSClient('
|
|
106
|
-
heartbeatInterval: 30000,
|
|
107
|
-
reconnectInterval: 5000,
|
|
108
|
-
maxReconnectAttempts: 5,
|
|
88
|
+
const client = new NostrWSClient('ws://localhost:3000', {
|
|
109
89
|
logger,
|
|
110
|
-
|
|
111
|
-
message: async (ws, message) => {
|
|
112
|
-
logger.info('Received message:', message);
|
|
113
|
-
// Handle message
|
|
114
|
-
},
|
|
115
|
-
error: (ws, error) => {
|
|
116
|
-
logger.error('Connection error:', error);
|
|
117
|
-
},
|
|
118
|
-
close: (ws) => {
|
|
119
|
-
logger.info('Connection closed');
|
|
120
|
-
}
|
|
121
|
-
}
|
|
90
|
+
WebSocketImpl: CustomWebSocketImplementation // Optional custom WebSocket implementation
|
|
122
91
|
});
|
|
123
92
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
logger.info('Connected!');
|
|
127
|
-
client.subscribe('my-channel');
|
|
93
|
+
client.on('message', (message) => {
|
|
94
|
+
console.log('Received message:', message);
|
|
128
95
|
});
|
|
129
96
|
|
|
130
97
|
client.connect();
|
|
@@ -153,6 +120,8 @@ interface NostrWSOptions {
|
|
|
153
120
|
// Optional close handler
|
|
154
121
|
close?: (ws: WebSocket) => void;
|
|
155
122
|
};
|
|
123
|
+
// Optional custom WebSocket implementation
|
|
124
|
+
WebSocketImpl?: WebSocketImpl;
|
|
156
125
|
}
|
|
157
126
|
```
|
|
158
127
|
|
package/dist/client.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import { EventEmitter } from 'events';
|
|
2
3
|
import type { NostrWSOptions, NostrWSMessage } from './types/index.js';
|
|
3
4
|
export declare class NostrWSClient extends EventEmitter {
|
|
@@ -8,6 +9,7 @@ export declare class NostrWSClient extends EventEmitter {
|
|
|
8
9
|
private heartbeatInterval;
|
|
9
10
|
private reconnectAttempts;
|
|
10
11
|
private messageQueue;
|
|
12
|
+
private clientId;
|
|
11
13
|
constructor(url: string, options?: Partial<NostrWSOptions>);
|
|
12
14
|
connect(): void;
|
|
13
15
|
private setupEventHandlers;
|
package/dist/client.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
1
2
|
import WebSocket from 'ws';
|
|
2
3
|
import { EventEmitter } from 'events';
|
|
3
4
|
export class NostrWSClient extends EventEmitter {
|
|
@@ -12,9 +13,11 @@ export class NostrWSClient extends EventEmitter {
|
|
|
12
13
|
if (!options.logger) {
|
|
13
14
|
throw new Error('Logger is required');
|
|
14
15
|
}
|
|
16
|
+
this.clientId = uuidv4();
|
|
15
17
|
this.options = {
|
|
16
18
|
heartbeatInterval: options.heartbeatInterval || 30000,
|
|
17
19
|
logger: options.logger,
|
|
20
|
+
WebSocketImpl: options.WebSocketImpl || WebSocket,
|
|
18
21
|
handlers: {
|
|
19
22
|
message: options.handlers?.message || (async () => { }),
|
|
20
23
|
error: options.handlers?.error || (() => { }),
|
|
@@ -28,7 +31,9 @@ export class NostrWSClient extends EventEmitter {
|
|
|
28
31
|
return;
|
|
29
32
|
}
|
|
30
33
|
try {
|
|
31
|
-
this.
|
|
34
|
+
this.options.logger.debug('Creating new WebSocket connection');
|
|
35
|
+
this.ws = new this.options.WebSocketImpl(this.url);
|
|
36
|
+
this.options.logger.debug('WebSocket created successfully');
|
|
32
37
|
this.setupEventHandlers();
|
|
33
38
|
}
|
|
34
39
|
catch (error) {
|
|
@@ -122,6 +127,7 @@ export class NostrWSClient extends EventEmitter {
|
|
|
122
127
|
}
|
|
123
128
|
}
|
|
124
129
|
async send(message) {
|
|
130
|
+
message.id = message.id || uuidv4();
|
|
125
131
|
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
126
132
|
this.messageQueue.push(message);
|
|
127
133
|
return;
|
package/dist/server.d.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import { EventEmitter } from 'events';
|
|
2
|
-
import {
|
|
3
|
-
import type { NostrWSOptions, NostrWSMessage } from './types/index.js';
|
|
3
|
+
import { WebSocketServer } from 'ws';
|
|
4
|
+
import type { NostrWSOptions, NostrWSMessage, ExtendedWebSocket } from './types/index.js';
|
|
4
5
|
export declare class NostrWSServer extends EventEmitter {
|
|
5
6
|
private wss;
|
|
6
7
|
private options;
|
|
7
8
|
private heartbeatInterval;
|
|
8
|
-
|
|
9
|
+
clients: Map<string, ExtendedWebSocket>;
|
|
10
|
+
constructor(wss: WebSocketServer, options?: Partial<NostrWSOptions>);
|
|
9
11
|
private setupServer;
|
|
12
|
+
private handleConnection;
|
|
10
13
|
private startHeartbeat;
|
|
11
14
|
broadcast(message: NostrWSMessage): void;
|
|
12
15
|
broadcastToChannel(channel: string, message: NostrWSMessage): void;
|
package/dist/server.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
|
-
import {
|
|
2
|
+
import { WebSocket } from 'ws';
|
|
3
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
3
4
|
export class NostrWSServer extends EventEmitter {
|
|
4
|
-
constructor(
|
|
5
|
+
constructor(wss, options = {}) {
|
|
5
6
|
super();
|
|
6
7
|
this.heartbeatInterval = null;
|
|
8
|
+
this.clients = new Map();
|
|
7
9
|
if (!options.logger) {
|
|
8
10
|
throw new Error('Logger is required');
|
|
9
11
|
}
|
|
@@ -13,47 +15,57 @@ export class NostrWSServer extends EventEmitter {
|
|
|
13
15
|
this.options = {
|
|
14
16
|
heartbeatInterval: options.heartbeatInterval || 30000,
|
|
15
17
|
logger: options.logger,
|
|
18
|
+
WebSocketImpl: options.WebSocketImpl || WebSocket,
|
|
16
19
|
handlers: {
|
|
17
20
|
message: options.handlers.message,
|
|
18
21
|
error: options.handlers.error || (() => { }),
|
|
19
22
|
close: options.handlers.close || (() => { })
|
|
20
23
|
}
|
|
21
24
|
};
|
|
22
|
-
this.wss =
|
|
25
|
+
this.wss = wss;
|
|
23
26
|
this.setupServer();
|
|
24
27
|
}
|
|
25
28
|
setupServer() {
|
|
26
29
|
this.wss.on('connection', (ws) => {
|
|
27
|
-
|
|
28
|
-
extWs.subscriptions = new Set();
|
|
29
|
-
extWs.isAlive = true;
|
|
30
|
-
ws.on('message', async (data) => {
|
|
31
|
-
try {
|
|
32
|
-
const message = JSON.parse(data.toString());
|
|
33
|
-
await this.options.handlers.message(extWs, message);
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
if (this.options.handlers.error) {
|
|
37
|
-
this.options.handlers.error(ws, error);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
ws.on('close', () => {
|
|
42
|
-
extWs.isAlive = false;
|
|
43
|
-
if (this.options.handlers.close) {
|
|
44
|
-
this.options.handlers.close(ws);
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
ws.on('error', (error) => {
|
|
48
|
-
if (this.options.handlers.error) {
|
|
49
|
-
this.options.handlers.error(ws, error);
|
|
50
|
-
}
|
|
51
|
-
});
|
|
30
|
+
this.handleConnection(ws);
|
|
52
31
|
});
|
|
53
32
|
if (this.options.heartbeatInterval && this.options.heartbeatInterval > 0) {
|
|
54
33
|
this.startHeartbeat();
|
|
55
34
|
}
|
|
56
35
|
}
|
|
36
|
+
handleConnection(ws) {
|
|
37
|
+
ws.isAlive = true;
|
|
38
|
+
ws.subscriptions = new Set();
|
|
39
|
+
ws.clientId = ws.clientId || uuidv4();
|
|
40
|
+
this.clients.set(ws.clientId, ws);
|
|
41
|
+
ws.on('message', async (data) => {
|
|
42
|
+
try {
|
|
43
|
+
const message = JSON.parse(data.toString());
|
|
44
|
+
await this.options.handlers.message(ws, message);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (this.options.handlers.error) {
|
|
48
|
+
this.options.handlers.error(ws, error);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
ws.on('close', () => {
|
|
53
|
+
if (ws.clientId) {
|
|
54
|
+
this.clients.delete(ws.clientId);
|
|
55
|
+
}
|
|
56
|
+
if (this.options.handlers.close) {
|
|
57
|
+
this.options.handlers.close(ws);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
ws.on('error', (error) => {
|
|
61
|
+
if (ws.clientId) {
|
|
62
|
+
this.clients.delete(ws.clientId);
|
|
63
|
+
}
|
|
64
|
+
if (this.options.handlers.error) {
|
|
65
|
+
this.options.handlers.error(ws, error);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
57
69
|
startHeartbeat() {
|
|
58
70
|
this.heartbeatInterval = setInterval(() => {
|
|
59
71
|
this.wss.clients.forEach((ws) => {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export interface NostrWSOptions {
|
|
|
5
5
|
reconnectInterval?: number;
|
|
6
6
|
maxReconnectAttempts?: number;
|
|
7
7
|
logger: Logger;
|
|
8
|
+
WebSocketImpl: typeof WebSocket;
|
|
8
9
|
handlers: {
|
|
9
10
|
message: (ws: ExtendedWebSocket, message: NostrWSMessage) => Promise<void> | void;
|
|
10
11
|
error?: (ws: WebSocket, error: Error) => void;
|
|
@@ -35,6 +36,7 @@ export interface NostrWSServerEvents {
|
|
|
35
36
|
export interface ExtendedWebSocket extends WebSocket {
|
|
36
37
|
isAlive?: boolean;
|
|
37
38
|
subscriptions?: Set<string>;
|
|
39
|
+
clientId?: string;
|
|
38
40
|
messageQueue?: NostrWSMessage[];
|
|
39
41
|
lastPing?: number;
|
|
40
42
|
reconnectAttempts?: number;
|
|
@@ -51,5 +53,6 @@ export interface NostrWSConnectionState {
|
|
|
51
53
|
export interface Logger {
|
|
52
54
|
debug: (message: string, ...args: unknown[]) => void;
|
|
53
55
|
info: (message: string, ...args: unknown[]) => void;
|
|
56
|
+
warn: (message: string, ...args: unknown[]) => void;
|
|
54
57
|
error: (message: string, ...args: unknown[]) => void;
|
|
55
58
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nostr-websocket-utils",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "WebSocket utilities for Nostr applications",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -20,20 +20,30 @@
|
|
|
20
20
|
"utils",
|
|
21
21
|
"maiqr"
|
|
22
22
|
],
|
|
23
|
-
"author": "
|
|
23
|
+
"author": "vveerrgg",
|
|
24
24
|
"license": "MIT",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/HumanjavaEnterprises/nostr-websocket-utils.git"
|
|
28
|
+
},
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/HumanjavaEnterprises/nostr-websocket-utils/issues"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/HumanjavaEnterprises/nostr-websocket-utils#readme",
|
|
25
33
|
"dependencies": {
|
|
26
|
-
"ws": "^8.16.0",
|
|
27
|
-
"winston": "^3.11.0",
|
|
28
34
|
"@noble/curves": "^1.3.0",
|
|
29
35
|
"@noble/hashes": "^1.3.3",
|
|
30
|
-
"
|
|
36
|
+
"@types/uuid": "^10.0.0",
|
|
37
|
+
"nostr-tools": "^2.1.4",
|
|
38
|
+
"uuid": "^11.0.3",
|
|
39
|
+
"winston": "^3.11.0",
|
|
40
|
+
"ws": "^8.16.0"
|
|
31
41
|
},
|
|
32
42
|
"peerDependencies": {
|
|
33
43
|
"nostr-tools": "^2.1.4"
|
|
34
44
|
},
|
|
35
45
|
"devDependencies": {
|
|
36
|
-
"@types/jest": "^29.5.
|
|
46
|
+
"@types/jest": "^29.5.14",
|
|
37
47
|
"@types/node": "^20.11.0",
|
|
38
48
|
"@types/ws": "^8.5.10",
|
|
39
49
|
"@typescript-eslint/eslint-plugin": "^6.18.1",
|