nostr-websocket-utils 0.2.3 โ 0.2.4
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 +73 -133
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/nostr-server.d.ts +8 -0
- package/dist/nostr-server.js +63 -0
- package/dist/types/nostr.d.ts +52 -0
- package/dist/types/nostr.js +10 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -19,6 +19,7 @@ A TypeScript library providing WebSocket utilities for Nostr applications, focus
|
|
|
19
19
|
- ๐ก๏ธ Comprehensive error handling and validation
|
|
20
20
|
- ๐งช 100% test coverage with Jest
|
|
21
21
|
- ๐ฆ Zero DOM dependencies
|
|
22
|
+
- ๐ Full TypeScript type safety
|
|
22
23
|
|
|
23
24
|
## Installation
|
|
24
25
|
|
|
@@ -26,179 +27,118 @@ A TypeScript library providing WebSocket utilities for Nostr applications, focus
|
|
|
26
27
|
npm install nostr-websocket-utils
|
|
27
28
|
```
|
|
28
29
|
|
|
29
|
-
## Breaking Changes in v0.2.
|
|
30
|
+
## Breaking Changes in v0.2.4
|
|
30
31
|
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
32
|
+
- ๐ Added dedicated Nostr WebSocket server implementation
|
|
33
|
+
- ๐ Enhanced TypeScript type definitions for Nostr messages
|
|
34
|
+
- ๐ Improved message handling with strict type checking
|
|
35
|
+
- ๐งช Added comprehensive test suite for Nostr server
|
|
36
|
+
- ๐ก๏ธ Strengthened type safety with `unknown` types
|
|
37
|
+
- ๐ Added support for Nostr EVENT and REQ messages
|
|
37
38
|
|
|
38
39
|
## Usage
|
|
39
40
|
|
|
40
|
-
### Server Example
|
|
41
|
+
### Nostr Server Example
|
|
41
42
|
|
|
42
43
|
```typescript
|
|
43
|
-
import { NostrWSServer } from 'nostr-websocket-utils';
|
|
44
|
-
import {
|
|
45
|
-
import { getLogger } from './utils/logger';
|
|
44
|
+
import { NostrWSServer, createWSServer } from 'nostr-websocket-utils';
|
|
45
|
+
import { NostrWSMessageType, NostrWSEvent } from 'nostr-websocket-utils/types/nostr';
|
|
46
46
|
|
|
47
|
-
// Create WebSocket server
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
// Initialize NostrWSServer with handlers
|
|
51
|
-
const server = new NostrWSServer(wss, {
|
|
52
|
-
logger: getLogger('nostr-server'),
|
|
47
|
+
// Create Nostr WebSocket server
|
|
48
|
+
const server = createWSServer({
|
|
49
|
+
port: 8080,
|
|
53
50
|
heartbeatInterval: 30000, // Optional: 30 seconds
|
|
54
51
|
handlers: {
|
|
55
52
|
// Required: Handle incoming messages
|
|
56
|
-
message: async (
|
|
57
|
-
switch (message
|
|
58
|
-
case
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
message: async (socket, message) => {
|
|
54
|
+
switch (message[0]) {
|
|
55
|
+
case NostrWSMessageType.EVENT:
|
|
56
|
+
const event = message[1] as NostrWSEvent;
|
|
57
|
+
// Handle Nostr event
|
|
61
58
|
break;
|
|
62
|
-
case
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
case NostrWSMessageType.REQ:
|
|
60
|
+
const [_type, subscriptionId, filter] = message;
|
|
61
|
+
// Handle subscription request
|
|
65
62
|
break;
|
|
66
63
|
}
|
|
67
64
|
},
|
|
68
65
|
// Optional: Handle errors
|
|
69
|
-
error: (
|
|
70
|
-
|
|
66
|
+
error: (socket, error) => {
|
|
67
|
+
console.error('WebSocket error:', error);
|
|
71
68
|
},
|
|
72
69
|
// Optional: Handle client disconnection
|
|
73
|
-
close: (
|
|
74
|
-
|
|
70
|
+
close: (socket) => {
|
|
71
|
+
console.info('Client disconnected');
|
|
75
72
|
}
|
|
76
73
|
}
|
|
77
74
|
});
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### Client Example
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
import { NostrWSClient } from 'nostr-websocket-utils';
|
|
84
|
-
import { getLogger } from './utils/logger';
|
|
85
|
-
|
|
86
|
-
const client = new NostrWSClient('ws://localhost:8080', {
|
|
87
|
-
logger: getLogger('nostr-client'),
|
|
88
|
-
heartbeatInterval: 30000,
|
|
89
|
-
handlers: {
|
|
90
|
-
// Required: Handle incoming messages
|
|
91
|
-
message: async (ws, message) => {
|
|
92
|
-
console.log('Received:', message);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// Listen for connection events
|
|
98
|
-
client.on('connect', () => {
|
|
99
|
-
console.log('Connected to server');
|
|
100
|
-
|
|
101
|
-
// Subscribe to a channel
|
|
102
|
-
client.subscribe('my-channel');
|
|
103
|
-
|
|
104
|
-
// Send an event
|
|
105
|
-
client.send({
|
|
106
|
-
type: 'event',
|
|
107
|
-
data: {
|
|
108
|
-
channel: 'my-channel',
|
|
109
|
-
content: 'Hello, Nostr!'
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
75
|
|
|
114
|
-
//
|
|
115
|
-
|
|
76
|
+
// Start listening
|
|
77
|
+
server.listen();
|
|
116
78
|
```
|
|
117
79
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
### NostrWSServer
|
|
121
|
-
|
|
122
|
-
The server-side WebSocket handler with support for channels and broadcasting.
|
|
80
|
+
### Types
|
|
123
81
|
|
|
124
82
|
```typescript
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
// Close the server and all connections
|
|
135
|
-
close(): void;
|
|
83
|
+
// Nostr Event Type
|
|
84
|
+
interface NostrWSEvent {
|
|
85
|
+
id: string;
|
|
86
|
+
pubkey: string;
|
|
87
|
+
created_at: number;
|
|
88
|
+
kind: number;
|
|
89
|
+
tags: string[][];
|
|
90
|
+
content: string;
|
|
91
|
+
sig: string;
|
|
136
92
|
}
|
|
137
|
-
```
|
|
138
93
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
94
|
+
// Nostr Filter Type
|
|
95
|
+
interface NostrWSFilter {
|
|
96
|
+
ids?: string[];
|
|
97
|
+
authors?: string[];
|
|
98
|
+
kinds?: number[];
|
|
99
|
+
'#e'?: string[];
|
|
100
|
+
'#p'?: string[];
|
|
101
|
+
since?: number;
|
|
102
|
+
until?: number;
|
|
103
|
+
limit?: number;
|
|
104
|
+
}
|
|
142
105
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
subscribe(channel: string, filter?: unknown): void;
|
|
152
|
-
|
|
153
|
-
// Unsubscribe from a channel
|
|
154
|
-
unsubscribe(channel: string): void;
|
|
155
|
-
|
|
156
|
-
// Send a message to the server
|
|
157
|
-
send(message: NostrWSMessage): Promise<void>;
|
|
158
|
-
|
|
159
|
-
// Close the connection
|
|
160
|
-
close(): void;
|
|
106
|
+
// Message Types
|
|
107
|
+
enum NostrWSMessageType {
|
|
108
|
+
EVENT = 'EVENT',
|
|
109
|
+
REQ = 'REQ',
|
|
110
|
+
CLOSE = 'CLOSE',
|
|
111
|
+
NOTICE = 'NOTICE',
|
|
112
|
+
AUTH = 'AUTH',
|
|
113
|
+
EOSE = 'EOSE'
|
|
161
114
|
}
|
|
162
115
|
```
|
|
163
116
|
|
|
164
|
-
|
|
117
|
+
## Advanced Configuration
|
|
165
118
|
|
|
166
|
-
|
|
119
|
+
### Server Options
|
|
167
120
|
|
|
168
121
|
```typescript
|
|
169
|
-
interface
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
122
|
+
interface NostrWSServerOptions {
|
|
123
|
+
port: number;
|
|
124
|
+
heartbeatInterval?: number;
|
|
125
|
+
maxPayloadSize?: number;
|
|
126
|
+
cors?: {
|
|
127
|
+
origin?: string | string[];
|
|
128
|
+
methods?: string[];
|
|
129
|
+
};
|
|
130
|
+
handlers: {
|
|
131
|
+
message: MessageHandler;
|
|
132
|
+
error?: ErrorHandler;
|
|
133
|
+
close?: CloseHandler;
|
|
175
134
|
};
|
|
176
135
|
}
|
|
177
136
|
```
|
|
178
137
|
|
|
179
|
-
## Why Choose This Library
|
|
180
|
-
|
|
181
|
-
### 1. Nostr-Optimized
|
|
182
|
-
- Built specifically for Nostr protocol requirements
|
|
183
|
-
- Efficient pub/sub model with filtered subscriptions
|
|
184
|
-
- Type-safe message handling for all Nostr events
|
|
185
|
-
|
|
186
|
-
### 2. Production-Ready
|
|
187
|
-
- Comprehensive error handling and recovery
|
|
188
|
-
- Memory-efficient subscription management
|
|
189
|
-
- Built-in logging and monitoring
|
|
190
|
-
- Extensive test coverage
|
|
191
|
-
|
|
192
|
-
### 3. Developer-Friendly
|
|
193
|
-
- Clear TypeScript definitions
|
|
194
|
-
- Flexible configuration options
|
|
195
|
-
- Detailed documentation
|
|
196
|
-
- Active maintenance
|
|
197
|
-
|
|
198
138
|
## Contributing
|
|
199
139
|
|
|
200
|
-
|
|
140
|
+
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
|
|
201
141
|
|
|
202
142
|
## License
|
|
203
143
|
|
|
204
|
-
MIT License - see the [LICENSE](LICENSE) file for details.
|
|
144
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export { NostrWSClient } from './client.js';
|
|
2
2
|
export { NostrWSServer } from './server.js';
|
|
3
|
+
export { NostrWSServer as NostrServer } from './nostr-server.js';
|
|
4
|
+
export { createWSServer as createServer } from './nostr-server.js';
|
|
3
5
|
export { getLogger } from './utils/logger.js';
|
|
4
6
|
export type { NostrWSOptions, NostrWSMessage, NostrWSSubscription, NostrWSClientEvents, NostrWSServerEvents, ExtendedWebSocket, NostrWSValidationResult, NostrWSConnectionState } from './types/index.js';
|
|
7
|
+
export type { NostrWSEvent as NostrEvent, NostrWSFilter as NostrFilter, NostrWSSocket as NostrSocket, NostrWSServerOptions, NostrWSServerMessage, NostrWSMessageType } from './types/nostr.js';
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { NostrWSClient } from './client.js';
|
|
2
2
|
export { NostrWSServer } from './server.js';
|
|
3
|
+
export { NostrWSServer as NostrServer } from './nostr-server.js';
|
|
4
|
+
export { createWSServer as createServer } from './nostr-server.js';
|
|
3
5
|
export { getLogger } from './utils/logger.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { NostrWSServerOptions } from './types/nostr';
|
|
2
|
+
export declare class NostrWSServer {
|
|
3
|
+
private server;
|
|
4
|
+
private logger;
|
|
5
|
+
constructor(options: NostrWSServerOptions);
|
|
6
|
+
close(): void;
|
|
7
|
+
}
|
|
8
|
+
export declare function createWSServer(options: NostrWSServerOptions): NostrWSServer;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Server as WebSocketServer } from 'ws';
|
|
2
|
+
import { getLogger } from './utils/logger';
|
|
3
|
+
export class NostrWSServer {
|
|
4
|
+
constructor(options) {
|
|
5
|
+
this.logger = getLogger('NostrWSServer');
|
|
6
|
+
this.server = new WebSocketServer({ port: options.port });
|
|
7
|
+
this.server.on('connection', (socket) => {
|
|
8
|
+
socket.subscriptions = new Set();
|
|
9
|
+
socket.isAlive = true;
|
|
10
|
+
if (options.onConnection) {
|
|
11
|
+
options.onConnection(socket);
|
|
12
|
+
}
|
|
13
|
+
const handleMessage = async (data) => {
|
|
14
|
+
try {
|
|
15
|
+
const message = JSON.parse(data.toString());
|
|
16
|
+
if (options.handlers?.message) {
|
|
17
|
+
await options.handlers.message(socket, message);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
if (options.handlers?.error) {
|
|
22
|
+
options.handlers.error(socket, error);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
socket.on('message', handleMessage);
|
|
27
|
+
socket.on('close', () => {
|
|
28
|
+
if (options.handlers?.close) {
|
|
29
|
+
options.handlers.close(socket);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
socket.on('error', (error) => {
|
|
33
|
+
if (options.handlers?.error) {
|
|
34
|
+
options.handlers.error(socket, error);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
// Setup heartbeat
|
|
38
|
+
if (options.heartbeatInterval) {
|
|
39
|
+
const interval = setInterval(() => {
|
|
40
|
+
if (!socket.isAlive) {
|
|
41
|
+
socket.terminate();
|
|
42
|
+
clearInterval(interval);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
socket.isAlive = false;
|
|
46
|
+
socket.ping();
|
|
47
|
+
}, options.heartbeatInterval);
|
|
48
|
+
socket.on('pong', () => {
|
|
49
|
+
socket.isAlive = true;
|
|
50
|
+
});
|
|
51
|
+
socket.on('close', () => {
|
|
52
|
+
clearInterval(interval);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
close() {
|
|
58
|
+
this.server.close();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export function createWSServer(options) {
|
|
62
|
+
return new NostrWSServer(options);
|
|
63
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { ExtendedWebSocket } from './index';
|
|
2
|
+
export interface NostrWSEvent {
|
|
3
|
+
id: string;
|
|
4
|
+
pubkey: string;
|
|
5
|
+
created_at: number;
|
|
6
|
+
kind: number;
|
|
7
|
+
tags: string[][];
|
|
8
|
+
content: string;
|
|
9
|
+
sig: string;
|
|
10
|
+
}
|
|
11
|
+
export interface NostrWSFilter {
|
|
12
|
+
ids?: string[];
|
|
13
|
+
authors?: string[];
|
|
14
|
+
kinds?: number[];
|
|
15
|
+
'#e'?: string[];
|
|
16
|
+
'#p'?: string[];
|
|
17
|
+
since?: number;
|
|
18
|
+
until?: number;
|
|
19
|
+
limit?: number;
|
|
20
|
+
}
|
|
21
|
+
export interface NostrWSSocket extends ExtendedWebSocket {
|
|
22
|
+
subscriptions?: Set<string>;
|
|
23
|
+
authenticated?: boolean;
|
|
24
|
+
pubkey?: string;
|
|
25
|
+
}
|
|
26
|
+
export declare enum NostrWSMessageType {
|
|
27
|
+
EVENT = "EVENT",
|
|
28
|
+
REQ = "REQ",
|
|
29
|
+
CLOSE = "CLOSE",
|
|
30
|
+
NOTICE = "NOTICE",
|
|
31
|
+
OK = "OK",
|
|
32
|
+
AUTH = "AUTH",
|
|
33
|
+
EOSE = "EOSE"
|
|
34
|
+
}
|
|
35
|
+
export type NostrWSServerMessage = [NostrWSMessageType, ...unknown[]];
|
|
36
|
+
export interface NostrWSServerOptions {
|
|
37
|
+
port: number;
|
|
38
|
+
heartbeatInterval?: number;
|
|
39
|
+
maxPayloadSize?: number;
|
|
40
|
+
cors?: {
|
|
41
|
+
origin: string;
|
|
42
|
+
methods: string[];
|
|
43
|
+
};
|
|
44
|
+
onConnection?: (socket: NostrWSSocket) => void;
|
|
45
|
+
onDisconnect?: (socket: NostrWSSocket) => void;
|
|
46
|
+
onError?: (error: Error, socket?: NostrWSSocket) => void;
|
|
47
|
+
handlers?: {
|
|
48
|
+
message: (socket: NostrWSSocket, message: NostrWSServerMessage) => void | Promise<void>;
|
|
49
|
+
error?: (socket: NostrWSSocket, error: Error) => void;
|
|
50
|
+
close?: (socket: NostrWSSocket) => void;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export var NostrWSMessageType;
|
|
2
|
+
(function (NostrWSMessageType) {
|
|
3
|
+
NostrWSMessageType["EVENT"] = "EVENT";
|
|
4
|
+
NostrWSMessageType["REQ"] = "REQ";
|
|
5
|
+
NostrWSMessageType["CLOSE"] = "CLOSE";
|
|
6
|
+
NostrWSMessageType["NOTICE"] = "NOTICE";
|
|
7
|
+
NostrWSMessageType["OK"] = "OK";
|
|
8
|
+
NostrWSMessageType["AUTH"] = "AUTH";
|
|
9
|
+
NostrWSMessageType["EOSE"] = "EOSE";
|
|
10
|
+
})(NostrWSMessageType || (NostrWSMessageType = {}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nostr-websocket-utils",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Robust WebSocket utilities for Nostr applications with automatic reconnection, channel-based messaging, and type-safe handlers. Features heartbeat monitoring, message queueing, and comprehensive error handling.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"nostr-tools": "^2.1.4"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
+
"@jest/globals": "^29.7.0",
|
|
54
55
|
"@types/jest": "^29.5.14",
|
|
55
56
|
"@types/node": "^20.11.0",
|
|
56
57
|
"@types/ws": "^8.5.10",
|