nostr-websocket-utils 0.2.2 โ†’ 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
@@ -11,13 +11,15 @@ A TypeScript library providing WebSocket utilities for Nostr applications, focus
11
11
  ## Features
12
12
 
13
13
  - ๐Ÿ”„ Automatic reconnection with configurable attempts
14
- - ๐Ÿ’“ Heartbeat monitoring
14
+ - ๐Ÿ’“ Heartbeat monitoring with configurable intervals
15
15
  - ๐Ÿ“จ Message queueing during disconnections
16
- - ๐Ÿ“ข Channel-based broadcasting
16
+ - ๐Ÿ“ข Channel-based broadcasting with filtered subscriptions
17
17
  - ๐Ÿ”’ Type-safe message handling with required handlers
18
- - ๐Ÿ“ Built-in logging
19
- - ๐Ÿ›ก๏ธ Comprehensive error handling
20
- - ๐Ÿงช Full test coverage
18
+ - ๐Ÿ“ Built-in logging with Winston integration
19
+ - ๐Ÿ›ก๏ธ Comprehensive error handling and validation
20
+ - ๐Ÿงช 100% test coverage with Jest
21
+ - ๐Ÿ“ฆ Zero DOM dependencies
22
+ - ๐Ÿ” Full TypeScript type safety
21
23
 
22
24
  ## Installation
23
25
 
@@ -25,137 +27,118 @@ A TypeScript library providing WebSocket utilities for Nostr applications, focus
25
27
  npm install nostr-websocket-utils
26
28
  ```
27
29
 
28
- ## Breaking Changes in v0.2.2
30
+ ## Breaking Changes in v0.2.4
29
31
 
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.
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
35
38
 
36
39
  ## Usage
37
40
 
38
- ### Server Example
41
+ ### Nostr Server Example
39
42
 
40
43
  ```typescript
41
- import express from 'express';
42
- import { createServer } from 'http';
43
- import { NostrWSServer } from 'nostr-websocket-utils';
44
- import winston from 'winston';
45
-
46
- const app = express();
47
- const server = createServer(app);
48
-
49
- // Create a logger
50
- const logger = winston.createLogger({
51
- level: 'info',
52
- format: winston.format.json(),
53
- transports: [new winston.transports.Console()]
54
- });
44
+ import { NostrWSServer, createWSServer } from 'nostr-websocket-utils';
45
+ import { NostrWSMessageType, NostrWSEvent } from 'nostr-websocket-utils/types/nostr';
55
46
 
56
- // Initialize the WebSocket server with custom WebSocket implementation
57
- const wsServer = new NostrWSServer(server, {
58
- logger,
47
+ // Create Nostr WebSocket server
48
+ const server = createWSServer({
49
+ port: 8080,
50
+ heartbeatInterval: 30000, // Optional: 30 seconds
59
51
  handlers: {
60
- message: async (ws, message) => {
61
- logger.info('Received message:', message);
62
- // Handle message
52
+ // Required: Handle incoming messages
53
+ message: async (socket, message) => {
54
+ switch (message[0]) {
55
+ case NostrWSMessageType.EVENT:
56
+ const event = message[1] as NostrWSEvent;
57
+ // Handle Nostr event
58
+ break;
59
+ case NostrWSMessageType.REQ:
60
+ const [_type, subscriptionId, filter] = message;
61
+ // Handle subscription request
62
+ break;
63
+ }
63
64
  },
64
- error: (ws, error) => {
65
- logger.error('WebSocket error:', error);
65
+ // Optional: Handle errors
66
+ error: (socket, error) => {
67
+ console.error('WebSocket error:', error);
66
68
  },
67
- close: (ws) => {
68
- logger.info('Connection closed');
69
+ // Optional: Handle client disconnection
70
+ close: (socket) => {
71
+ console.info('Client disconnected');
69
72
  }
70
73
  }
71
74
  });
72
75
 
73
- server.listen(3000);
76
+ // Start listening
77
+ server.listen();
74
78
  ```
75
79
 
76
- ### Client Example
80
+ ### Types
77
81
 
78
82
  ```typescript
79
- import { NostrWSClient } from 'nostr-websocket-utils';
80
- import winston from 'winston';
81
-
82
- const logger = winston.createLogger({
83
- level: 'info',
84
- format: winston.format.json(),
85
- transports: [new winston.transports.Console()]
86
- });
87
-
88
- const client = new NostrWSClient('ws://localhost:3000', {
89
- logger,
90
- WebSocketImpl: CustomWebSocketImplementation // Optional custom WebSocket implementation
91
- });
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;
92
+ }
92
93
 
93
- client.on('message', (message) => {
94
- console.log('Received message:', message);
95
- });
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
+ }
96
105
 
97
- client.connect();
106
+ // Message Types
107
+ enum NostrWSMessageType {
108
+ EVENT = 'EVENT',
109
+ REQ = 'REQ',
110
+ CLOSE = 'CLOSE',
111
+ NOTICE = 'NOTICE',
112
+ AUTH = 'AUTH',
113
+ EOSE = 'EOSE'
114
+ }
98
115
  ```
99
116
 
100
- ## Interface Reference
117
+ ## Advanced Configuration
101
118
 
102
- ### NostrWSOptions
119
+ ### Server Options
103
120
 
104
121
  ```typescript
105
- interface NostrWSOptions {
106
- // Interval for sending heartbeat pings (ms)
122
+ interface NostrWSServerOptions {
123
+ port: number;
107
124
  heartbeatInterval?: number;
108
- // Interval between reconnection attempts (ms)
109
- reconnectInterval?: number;
110
- // Maximum number of reconnection attempts
111
- maxReconnectAttempts?: number;
112
- // Required logger instance
113
- logger: Logger;
114
- // Required handlers object
125
+ maxPayloadSize?: number;
126
+ cors?: {
127
+ origin?: string | string[];
128
+ methods?: string[];
129
+ };
115
130
  handlers: {
116
- // Required message handler
117
- message: (ws: ExtendedWebSocket, message: NostrWSMessage) => Promise<void> | void;
118
- // Optional error handler
119
- error?: (ws: WebSocket, error: Error) => void;
120
- // Optional close handler
121
- close?: (ws: WebSocket) => void;
131
+ message: MessageHandler;
132
+ error?: ErrorHandler;
133
+ close?: CloseHandler;
122
134
  };
123
- // Optional custom WebSocket implementation
124
- WebSocketImpl?: WebSocketImpl;
125
135
  }
126
136
  ```
127
137
 
128
- ## Why This Library is Perfect for Nostr Development
129
-
130
- This library is specifically designed to accelerate Nostr application development by providing a robust foundation for WebSocket communication. Here's what makes it particularly valuable:
131
-
132
- ### 1. Nostr-Specific Message Types
133
- - Built-in support for core Nostr protocol message types (`subscribe`, `unsubscribe`, `event`, `request/response`)
134
- - Perfectly aligned with Nostr's pub/sub model
135
- - Type-safe message handling for all Nostr events
136
-
137
- ### 2. Advanced WebSocket Management
138
- - Zero-config connection maintenance with automatic reconnection
139
- - Built-in heartbeat mechanism prevents stale connections
140
- - Smart message queuing ensures no events are lost during disconnections
141
- - Comprehensive connection state tracking
142
-
143
- ### 3. Efficient Event Distribution
144
- - Channel-based broadcasting for targeted event distribution
145
- - Support for filtered subscriptions (crucial for Nostr event filtering)
146
- - Memory-efficient subscription tracking
147
- - Optimized message routing to relevant subscribers
148
-
149
- ### 4. Developer Experience
150
- - Full TypeScript support with comprehensive type definitions
151
- - Event-driven architecture matching Nostr's event-centric nature
152
- - Clear, consistent error handling and validation
153
- - Required handlers pattern ensures type safety
154
-
155
138
  ## Contributing
156
139
 
157
- 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.
158
141
 
159
142
  ## License
160
143
 
161
- 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,7 +1,7 @@
1
1
  {
2
2
  "name": "nostr-websocket-utils",
3
- "version": "0.2.2",
4
- "description": "WebSocket utilities for Nostr applications",
3
+ "version": "0.2.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",
7
7
  "type": "module",
@@ -17,7 +17,15 @@
17
17
  "keywords": [
18
18
  "nostr",
19
19
  "websocket",
20
- "utils",
20
+ "typescript",
21
+ "ws",
22
+ "realtime",
23
+ "pubsub",
24
+ "channel-based",
25
+ "reconnection",
26
+ "heartbeat",
27
+ "message-queue",
28
+ "type-safe",
21
29
  "maiqr"
22
30
  ],
23
31
  "author": "vveerrgg",
@@ -43,6 +51,7 @@
43
51
  "nostr-tools": "^2.1.4"
44
52
  },
45
53
  "devDependencies": {
54
+ "@jest/globals": "^29.7.0",
46
55
  "@types/jest": "^29.5.14",
47
56
  "@types/node": "^20.11.0",
48
57
  "@types/ws": "^8.5.10",