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 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.2
30
+ ## Breaking Changes in v0.2.4
30
31
 
31
- - ๐Ÿ”ฅ Removed all DOM-related code for better server-side compatibility
32
- - ๐Ÿ†” Added UUID support for message tracking and correlation
33
- - ๐Ÿ”Œ Introduced `WebSocketImpl` option for custom WebSocket implementations
34
- - ๐Ÿ›ก๏ธ Enhanced TypeScript type safety and validation
35
- - ๐Ÿ”„ Improved reconnection and error handling logic
36
- - ๐Ÿ“ Added comprehensive logging support
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 { WebSocketServer } from 'ws';
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 wss = new WebSocketServer({ port: 8080 });
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 (ws, message) => {
57
- switch (message.type) {
58
- case 'subscribe':
59
- // Handle subscription
60
- ws.subscriptions?.add(message.data.channel);
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 'event':
63
- // Broadcast to relevant subscribers
64
- server.broadcastToChannel(message.data.channel, message);
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: (ws, error) => {
70
- logger.error('WebSocket error:', error);
66
+ error: (socket, error) => {
67
+ console.error('WebSocket error:', error);
71
68
  },
72
69
  // Optional: Handle client disconnection
73
- close: (ws) => {
74
- logger.info(`Client ${ws.clientId} disconnected`);
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
- // Connect to server
115
- client.connect();
76
+ // Start listening
77
+ server.listen();
116
78
  ```
117
79
 
118
- ## API Reference
119
-
120
- ### NostrWSServer
121
-
122
- The server-side WebSocket handler with support for channels and broadcasting.
80
+ ### Types
123
81
 
124
82
  ```typescript
125
- class NostrWSServer {
126
- constructor(wss: WebSocketServer, options: NostrWSOptions);
127
-
128
- // Broadcast to all connected clients
129
- broadcast(message: NostrWSMessage): void;
130
-
131
- // Broadcast to specific channel subscribers
132
- broadcastToChannel(channel: string, message: NostrWSMessage): void;
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
- ### NostrWSClient
140
-
141
- The client-side WebSocket handler with automatic reconnection and message queueing.
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
- ```typescript
144
- class NostrWSClient {
145
- constructor(url: string, options: NostrWSOptions);
146
-
147
- // Connect to the server
148
- connect(): void;
149
-
150
- // Subscribe to a channel
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
- ### NostrWSMessage
117
+ ## Advanced Configuration
165
118
 
166
- The standard message format for communication.
119
+ ### Server Options
167
120
 
168
121
  ```typescript
169
- interface NostrWSMessage {
170
- id?: string; // Auto-generated UUID if not provided
171
- type: string; // Message type (e.g., 'subscribe', 'event')
172
- data: {
173
- channel?: string; // Target channel for subscription/broadcast
174
- [key: string]: any; // Additional message data
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
- We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
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",
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",