nostr-websocket-utils 0.2.3 โ†’ 0.2.5

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
@@ -1,24 +1,23 @@
1
- # Nostr Websocket Utils
1
+ # nostr-websocket-utils
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/nostr-websocket-utils.svg)](https://www.npmjs.com/package/nostr-websocket-utils)
4
- [![License](https://img.shields.io/npm/l/nostr-websocket-utils.svg)](https://github.com/HumanjavaEnterprises/nostr-websocket-utils/blob/main/LICENSE)
3
+ [![npm version](https://img.shields.io/npm/v/@humanjavaenterprises/nostr-websocket-utils.svg)](https://www.npmjs.com/package/@humanjavaenterprises/nostr-websocket-utils)
4
+ [![License](https://img.shields.io/npm/l/@humanjavaenterprises/nostr-websocket-utils.svg)](https://github.com/HumanjavaEnterprises/nostr-websocket-utils/blob/main/LICENSE)
5
5
  [![Build Status](https://github.com/HumanjavaEnterprises/nostr-websocket-utils/workflows/CI/badge.svg)](https://github.com/HumanjavaEnterprises/nostr-websocket-utils/actions)
6
+ [![Documentation](https://github.com/HumanjavaEnterprises/nostr-websocket-utils/workflows/Documentation/badge.svg)](https://humanjavaenterprises.github.io/nostr-websocket-utils/)
6
7
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org)
7
8
  [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
8
9
 
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
+ A TypeScript library for building Nostr protocol WebSocket clients and servers.
10
11
 
11
12
  ## Features
12
13
 
13
- - ๐Ÿ”„ Automatic reconnection with configurable attempts
14
- - ๐Ÿ’“ Heartbeat monitoring with configurable intervals
15
- - ๐Ÿ“จ Message queueing during disconnections
16
- - ๐Ÿ“ข Channel-based broadcasting with filtered subscriptions
17
- - ๐Ÿ”’ Type-safe message handling with required handlers
18
- - ๐Ÿ“ Built-in logging with Winston integration
19
- - ๐Ÿ›ก๏ธ Comprehensive error handling and validation
20
- - ๐Ÿงช 100% test coverage with Jest
21
- - ๐Ÿ“ฆ Zero DOM dependencies
14
+ - ๐Ÿš€ Full Nostr protocol support
15
+ - ๐Ÿ”’ Secure WebSocket connections
16
+ - โ™ฅ๏ธ Heartbeat mechanism for connection health
17
+ - ๐Ÿ”„ Automatic reconnection handling
18
+ - ๐Ÿ“ Comprehensive logging
19
+ - ๐ŸŽฏ Type-safe message handling
20
+ - ๐Ÿ“ฆ Easy to use API
22
21
 
23
22
  ## Installation
24
23
 
@@ -26,179 +25,108 @@ A TypeScript library providing WebSocket utilities for Nostr applications, focus
26
25
  npm install nostr-websocket-utils
27
26
  ```
28
27
 
29
- ## Breaking Changes in v0.2.2
28
+ ## Quick Start
30
29
 
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
37
-
38
- ## Usage
39
-
40
- ### Server Example
30
+ ### Creating a Nostr WebSocket Client
41
31
 
42
32
  ```typescript
43
- import { NostrWSServer } from 'nostr-websocket-utils';
44
- import { WebSocketServer } from 'ws';
45
- import { getLogger } from './utils/logger';
46
-
47
- // Create WebSocket server
48
- const wss = new WebSocketServer({ port: 8080 });
33
+ import { NostrWSClient } from 'nostr-websocket-utils';
49
34
 
50
- // Initialize NostrWSServer with handlers
51
- const server = new NostrWSServer(wss, {
52
- logger: getLogger('nostr-server'),
53
- heartbeatInterval: 30000, // Optional: 30 seconds
35
+ const client = new NostrWSClient('wss://relay.example.com', {
36
+ logger: console,
37
+ heartbeatInterval: 30000,
54
38
  handlers: {
55
- // 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);
61
- break;
62
- case 'event':
63
- // Broadcast to relevant subscribers
64
- server.broadcastToChannel(message.data.channel, message);
65
- break;
66
- }
67
- },
68
- // Optional: Handle errors
69
- error: (ws, error) => {
70
- logger.error('WebSocket error:', error);
71
- },
72
- // Optional: Handle client disconnection
73
- close: (ws) => {
74
- logger.info(`Client ${ws.clientId} disconnected`);
75
- }
39
+ message: async (msg) => console.log('Received:', msg),
40
+ error: (err) => console.error('Error:', err),
41
+ close: () => console.log('Connection closed')
76
42
  }
77
43
  });
44
+
45
+ await client.connect();
78
46
  ```
79
47
 
80
- ### Client Example
48
+ ### Creating a Nostr WebSocket Server
81
49
 
82
50
  ```typescript
83
- import { NostrWSClient } from 'nostr-websocket-utils';
84
- import { getLogger } from './utils/logger';
51
+ import { createNostrServer } from '@humanjavaenterprises/nostr-websocket-utils';
85
52
 
86
- const client = new NostrWSClient('ws://localhost:8080', {
87
- logger: getLogger('nostr-client'),
53
+ const server = await createNostrServer(8080, {
54
+ logger: console,
88
55
  heartbeatInterval: 30000,
89
56
  handlers: {
90
- // Required: Handle incoming messages
91
- message: async (ws, message) => {
92
- console.log('Received:', message);
57
+ message: async (ws, msg) => {
58
+ console.log('Received message:', msg);
59
+ // Handle the message
93
60
  }
94
61
  }
95
62
  });
63
+ ```
96
64
 
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
- });
65
+ ## Documentation
113
66
 
114
- // Connect to server
115
- client.connect();
116
- ```
67
+ Comprehensive API documentation is available in our [documentation site](https://humanjavaenterprises.github.io/nostr-websocket-utils/). Here's what you'll find:
117
68
 
118
- ## API Reference
69
+ ### Core Components
70
+ - [NostrWSClient](docs/classes/NostrWSClient.md) - WebSocket client implementation
71
+ - [NostrWSServer](docs/classes/NostrWSServer.md) - WebSocket server implementation
72
+ - [NostrServer](docs/classes/NostrServer.md) - High-level Nostr server
119
73
 
120
- ### NostrWSServer
74
+ ### Types and Interfaces
75
+ - [NostrWSMessage](docs/interfaces/NostrWSMessage.md) - Message structure
76
+ - [NostrWSOptions](docs/interfaces/NostrWSOptions.md) - Configuration options
77
+ - [NostrWSSubscription](docs/interfaces/NostrWSSubscription.md) - Subscription interface
78
+ - [ExtendedWebSocket](docs/interfaces/ExtendedWebSocket.md) - Enhanced WebSocket interface
121
79
 
122
- The server-side WebSocket handler with support for channels and broadcasting.
80
+ ### Utility Functions
81
+ - [createServer](docs/functions/createServer.md) - Server creation helper
82
+ - [getLogger](docs/functions/getLogger.md) - Logging utility
123
83
 
124
- ```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;
136
- }
137
- ```
84
+ ### Type Definitions
85
+ - [MessageType](docs/enumerations/NostrWSMessageType.md) - Message type enumeration
86
+ - [Global Types](docs/globals.md) - Global type definitions
138
87
 
139
- ### NostrWSClient
88
+ ## Examples
140
89
 
141
- The client-side WebSocket handler with automatic reconnection and message queueing.
90
+ ### Subscribe to a Channel
142
91
 
143
92
  ```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;
161
- }
93
+ client.subscribe('my-channel', {
94
+ filter: {
95
+ authors: ['pubkey1', 'pubkey2'],
96
+ kinds: [1]
97
+ }
98
+ });
162
99
  ```
163
100
 
164
- ### NostrWSMessage
165
-
166
- The standard message format for communication.
101
+ ### Broadcast a Message
167
102
 
168
103
  ```typescript
169
- interface NostrWSMessage {
170
- id?: string; // Auto-generated UUID if not provided
171
- type: string; // Message type (e.g., 'subscribe', 'event')
104
+ server.broadcast({
105
+ type: 'event',
172
106
  data: {
173
- channel?: string; // Target channel for subscription/broadcast
174
- [key: string]: any; // Additional message data
175
- };
176
- }
107
+ content: 'Hello everyone!',
108
+ kind: 1
109
+ }
110
+ });
177
111
  ```
178
112
 
179
- ## Why Choose This Library
113
+ ## Contributing
114
+
115
+ Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
180
116
 
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
117
+ ## License
185
118
 
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
119
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
191
120
 
192
- ### 3. Developer-Friendly
193
- - Clear TypeScript definitions
194
- - Flexible configuration options
195
- - Detailed documentation
196
- - Active maintenance
121
+ ## Related Projects
197
122
 
198
- ## Contributing
123
+ - [nostr-protocol](https://github.com/nostr-protocol/nostr)
124
+ - [nostr-tools](https://github.com/nbd-wtf/nostr-tools)
199
125
 
200
- We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
126
+ ## Support
201
127
 
202
- ## License
128
+ If you have any questions or need help, please:
203
129
 
204
- MIT License - see the [LICENSE](LICENSE) file for details.
130
+ 1. Check the [documentation](https://humanjavaenterprises.github.io/nostr-websocket-utils/)
131
+ 2. Open an [issue](https://github.com/HumanjavaEnterprises/nostr-websocket-utils/issues)
132
+ 3. Join our [Discord community](https://discord.gg/your-discord)
package/dist/client.d.ts CHANGED
@@ -1,6 +1,27 @@
1
1
  /// <reference types="node" />
2
2
  import { EventEmitter } from 'events';
3
3
  import type { NostrWSOptions, NostrWSMessage } from './types/index.js';
4
+ /**
5
+ * WebSocket client implementation for Nostr protocol communication
6
+ * Extends EventEmitter to provide event-based message handling
7
+ *
8
+ * @extends EventEmitter
9
+ * @example
10
+ * ```typescript
11
+ * const client = new NostrWSClient('wss://relay.example.com', {
12
+ * logger: console,
13
+ * heartbeatInterval: 30000,
14
+ * handlers: {
15
+ * message: async (msg) => console.log('Received:', msg),
16
+ * error: (err) => console.error('Error:', err),
17
+ * close: () => console.log('Connection closed')
18
+ * }
19
+ * });
20
+ *
21
+ * await client.connect();
22
+ * client.send({ type: 'EVENT', data: { ... } });
23
+ * ```
24
+ */
4
25
  export declare class NostrWSClient extends EventEmitter {
5
26
  private url;
6
27
  private ws;
@@ -10,15 +31,99 @@ export declare class NostrWSClient extends EventEmitter {
10
31
  private reconnectAttempts;
11
32
  private messageQueue;
12
33
  private clientId;
34
+ /**
35
+ * Creates a new NostrWSClient instance
36
+ *
37
+ * @param {string} url - The WebSocket server URL to connect to
38
+ * @param {Partial<NostrWSOptions>} options - Configuration options
39
+ * @param {number} [options.heartbeatInterval=30000] - Interval for sending heartbeat messages in milliseconds
40
+ * @param {object} options.logger - Logger instance (required)
41
+ * @param {Function} [options.WebSocketImpl=WebSocket] - WebSocket implementation to use
42
+ * @param {object} [options.handlers] - Event handlers
43
+ * @param {Function} [options.handlers.message] - Message handler function
44
+ * @param {Function} [options.handlers.error] - Error handler function
45
+ * @param {Function} [options.handlers.close] - Connection close handler function
46
+ * @throws {Error} If logger is not provided
47
+ */
13
48
  constructor(url: string, options?: Partial<NostrWSOptions>);
49
+ /**
50
+ * Establishes a connection to the WebSocket server
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * client.connect();
55
+ * ```
56
+ */
14
57
  connect(): void;
58
+ /**
59
+ * Sets up event handlers for the WebSocket connection
60
+ *
61
+ * @private
62
+ */
15
63
  private setupEventHandlers;
64
+ /**
65
+ * Starts sending heartbeat messages at the specified interval
66
+ *
67
+ * @private
68
+ */
16
69
  private startHeartbeat;
70
+ /**
71
+ * Stops sending heartbeat messages
72
+ *
73
+ * @private
74
+ */
17
75
  private stopHeartbeat;
76
+ /**
77
+ * Handles reconnecting to the WebSocket server after a disconnection
78
+ *
79
+ * @private
80
+ */
18
81
  private handleReconnect;
82
+ /**
83
+ * Subscribes to a channel with optional filter
84
+ *
85
+ * @param {string} channel - Channel name
86
+ * @param {unknown} [filter] - Filter data
87
+ * @example
88
+ * ```typescript
89
+ * client.subscribe('channel-name', { ...filterData });
90
+ * ```
91
+ */
19
92
  subscribe(channel: string, filter?: unknown): void;
93
+ /**
94
+ * Unsubscribes from a channel
95
+ *
96
+ * @param {string} channel - Channel name
97
+ * @example
98
+ * ```typescript
99
+ * client.unsubscribe('channel-name');
100
+ * ```
101
+ */
20
102
  unsubscribe(channel: string): void;
103
+ /**
104
+ * Flushes the message queue by sending pending messages
105
+ *
106
+ * @private
107
+ */
21
108
  private flushMessageQueue;
109
+ /**
110
+ * Sends a message to the WebSocket server
111
+ *
112
+ * @param {NostrWSMessage} message - Message to send
113
+ * @returns {Promise<void>}
114
+ * @example
115
+ * ```typescript
116
+ * client.send({ type: 'EVENT', data: { ... } });
117
+ * ```
118
+ */
22
119
  send(message: NostrWSMessage): Promise<void>;
120
+ /**
121
+ * Closes the WebSocket connection
122
+ *
123
+ * @example
124
+ * ```typescript
125
+ * client.close();
126
+ * ```
127
+ */
23
128
  close(): void;
24
129
  }
package/dist/client.js CHANGED
@@ -1,7 +1,42 @@
1
1
  import { v4 as uuidv4 } from 'uuid';
2
2
  import WebSocket from 'ws';
3
3
  import { EventEmitter } from 'events';
4
+ /**
5
+ * WebSocket client implementation for Nostr protocol communication
6
+ * Extends EventEmitter to provide event-based message handling
7
+ *
8
+ * @extends EventEmitter
9
+ * @example
10
+ * ```typescript
11
+ * const client = new NostrWSClient('wss://relay.example.com', {
12
+ * logger: console,
13
+ * heartbeatInterval: 30000,
14
+ * handlers: {
15
+ * message: async (msg) => console.log('Received:', msg),
16
+ * error: (err) => console.error('Error:', err),
17
+ * close: () => console.log('Connection closed')
18
+ * }
19
+ * });
20
+ *
21
+ * await client.connect();
22
+ * client.send({ type: 'EVENT', data: { ... } });
23
+ * ```
24
+ */
4
25
  export class NostrWSClient extends EventEmitter {
26
+ /**
27
+ * Creates a new NostrWSClient instance
28
+ *
29
+ * @param {string} url - The WebSocket server URL to connect to
30
+ * @param {Partial<NostrWSOptions>} options - Configuration options
31
+ * @param {number} [options.heartbeatInterval=30000] - Interval for sending heartbeat messages in milliseconds
32
+ * @param {object} options.logger - Logger instance (required)
33
+ * @param {Function} [options.WebSocketImpl=WebSocket] - WebSocket implementation to use
34
+ * @param {object} [options.handlers] - Event handlers
35
+ * @param {Function} [options.handlers.message] - Message handler function
36
+ * @param {Function} [options.handlers.error] - Error handler function
37
+ * @param {Function} [options.handlers.close] - Connection close handler function
38
+ * @throws {Error} If logger is not provided
39
+ */
5
40
  constructor(url, options = {}) {
6
41
  super();
7
42
  this.url = url;
@@ -25,6 +60,14 @@ export class NostrWSClient extends EventEmitter {
25
60
  }
26
61
  };
27
62
  }
63
+ /**
64
+ * Establishes a connection to the WebSocket server
65
+ *
66
+ * @example
67
+ * ```typescript
68
+ * client.connect();
69
+ * ```
70
+ */
28
71
  connect() {
29
72
  if (this.ws) {
30
73
  this.options.logger.info('WebSocket connection already exists');
@@ -41,6 +84,11 @@ export class NostrWSClient extends EventEmitter {
41
84
  this.handleReconnect();
42
85
  }
43
86
  }
87
+ /**
88
+ * Sets up event handlers for the WebSocket connection
89
+ *
90
+ * @private
91
+ */
44
92
  setupEventHandlers() {
45
93
  if (!this.ws)
46
94
  return;
@@ -81,6 +129,11 @@ export class NostrWSClient extends EventEmitter {
81
129
  }
82
130
  });
83
131
  }
132
+ /**
133
+ * Starts sending heartbeat messages at the specified interval
134
+ *
135
+ * @private
136
+ */
84
137
  startHeartbeat() {
85
138
  if (this.heartbeatInterval)
86
139
  return;
@@ -90,12 +143,22 @@ export class NostrWSClient extends EventEmitter {
90
143
  }
91
144
  }, this.options.heartbeatInterval || 30000);
92
145
  }
146
+ /**
147
+ * Stops sending heartbeat messages
148
+ *
149
+ * @private
150
+ */
93
151
  stopHeartbeat() {
94
152
  if (this.heartbeatInterval) {
95
153
  clearInterval(this.heartbeatInterval);
96
154
  this.heartbeatInterval = null;
97
155
  }
98
156
  }
157
+ /**
158
+ * Handles reconnecting to the WebSocket server after a disconnection
159
+ *
160
+ * @private
161
+ */
99
162
  handleReconnect() {
100
163
  if (this.reconnectTimeout)
101
164
  return;
@@ -106,18 +169,42 @@ export class NostrWSClient extends EventEmitter {
106
169
  this.emit('reconnect');
107
170
  }, 5000);
108
171
  }
172
+ /**
173
+ * Subscribes to a channel with optional filter
174
+ *
175
+ * @param {string} channel - Channel name
176
+ * @param {unknown} [filter] - Filter data
177
+ * @example
178
+ * ```typescript
179
+ * client.subscribe('channel-name', { ...filterData });
180
+ * ```
181
+ */
109
182
  subscribe(channel, filter) {
110
183
  this.send({
111
184
  type: 'subscribe',
112
185
  data: { channel, filter }
113
186
  });
114
187
  }
188
+ /**
189
+ * Unsubscribes from a channel
190
+ *
191
+ * @param {string} channel - Channel name
192
+ * @example
193
+ * ```typescript
194
+ * client.unsubscribe('channel-name');
195
+ * ```
196
+ */
115
197
  unsubscribe(channel) {
116
198
  this.send({
117
199
  type: 'unsubscribe',
118
200
  data: { channel }
119
201
  });
120
202
  }
203
+ /**
204
+ * Flushes the message queue by sending pending messages
205
+ *
206
+ * @private
207
+ */
121
208
  flushMessageQueue() {
122
209
  while (this.messageQueue.length > 0 && this.ws?.readyState === WebSocket.OPEN) {
123
210
  const message = this.messageQueue.shift();
@@ -126,6 +213,16 @@ export class NostrWSClient extends EventEmitter {
126
213
  }
127
214
  }
128
215
  }
216
+ /**
217
+ * Sends a message to the WebSocket server
218
+ *
219
+ * @param {NostrWSMessage} message - Message to send
220
+ * @returns {Promise<void>}
221
+ * @example
222
+ * ```typescript
223
+ * client.send({ type: 'EVENT', data: { ... } });
224
+ * ```
225
+ */
129
226
  async send(message) {
130
227
  message.id = message.id || uuidv4();
131
228
  if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
@@ -140,6 +237,14 @@ export class NostrWSClient extends EventEmitter {
140
237
  this.handleReconnect();
141
238
  }
142
239
  }
240
+ /**
241
+ * Closes the WebSocket connection
242
+ *
243
+ * @example
244
+ * ```typescript
245
+ * client.close();
246
+ * ```
247
+ */
143
248
  close() {
144
249
  this.stopHeartbeat();
145
250
  if (this.reconnectTimeout) {
package/dist/index.d.ts CHANGED
@@ -1,4 +1,11 @@
1
+ /**
2
+ * @file Main entry point for the nostr-websocket-utils library
3
+ * @module nostr-websocket-utils
4
+ */
1
5
  export { NostrWSClient } from './client.js';
2
6
  export { NostrWSServer } from './server.js';
7
+ export { NostrWSServer as NostrServer } from './nostr-server.js';
8
+ export { createWSServer as createServer } from './nostr-server.js';
3
9
  export { getLogger } from './utils/logger.js';
4
10
  export type { NostrWSOptions, NostrWSMessage, NostrWSSubscription, NostrWSClientEvents, NostrWSServerEvents, ExtendedWebSocket, NostrWSValidationResult, NostrWSConnectionState } from './types/index.js';
11
+ 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,9 @@
1
+ /**
2
+ * @file Main entry point for the nostr-websocket-utils library
3
+ * @module nostr-websocket-utils
4
+ */
1
5
  export { NostrWSClient } from './client.js';
2
6
  export { NostrWSServer } from './server.js';
7
+ export { NostrWSServer as NostrServer } from './nostr-server.js';
8
+ export { createWSServer as createServer } from './nostr-server.js';
3
9
  export { getLogger } from './utils/logger.js';
@@ -0,0 +1,31 @@
1
+ import { NostrWSServerOptions } from './types/nostr';
2
+ /**
3
+ * Represents a Nostr WebSocket server
4
+ */
5
+ export declare class NostrWSServer {
6
+ /**
7
+ * The underlying WebSocket server instance
8
+ */
9
+ private server;
10
+ /**
11
+ * Logger instance for this server
12
+ */
13
+ private logger;
14
+ /**
15
+ * Creates a new Nostr WebSocket server instance
16
+ *
17
+ * @param {NostrWSServerOptions} options - Server configuration options
18
+ */
19
+ constructor(options: NostrWSServerOptions);
20
+ /**
21
+ * Closes the WebSocket server
22
+ */
23
+ close(): void;
24
+ }
25
+ /**
26
+ * Creates a new Nostr WebSocket server instance
27
+ *
28
+ * @param {NostrWSServerOptions} options - Server configuration options
29
+ * @returns {NostrWSServer} The created server instance
30
+ */
31
+ export declare function createWSServer(options: NostrWSServerOptions): NostrWSServer;
@@ -0,0 +1,107 @@
1
+ import { Server as WebSocketServer } from 'ws';
2
+ import { getLogger } from './utils/logger';
3
+ /**
4
+ * Represents a Nostr WebSocket server
5
+ */
6
+ export class NostrWSServer {
7
+ /**
8
+ * Creates a new Nostr WebSocket server instance
9
+ *
10
+ * @param {NostrWSServerOptions} options - Server configuration options
11
+ */
12
+ constructor(options) {
13
+ /**
14
+ * Logger instance for this server
15
+ */
16
+ this.logger = getLogger('NostrWSServer');
17
+ this.server = new WebSocketServer({ port: options.port });
18
+ /**
19
+ * Handles incoming WebSocket connections
20
+ *
21
+ * @param {NostrWSSocket} socket - The connected WebSocket client
22
+ */
23
+ this.server.on('connection', (socket) => {
24
+ socket.subscriptions = new Set();
25
+ socket.isAlive = true;
26
+ if (options.onConnection) {
27
+ options.onConnection(socket);
28
+ }
29
+ /**
30
+ * Handles incoming messages from the client
31
+ *
32
+ * @param {Buffer | ArrayBuffer | Buffer[]} data - The incoming message data
33
+ */
34
+ const handleMessage = async (data) => {
35
+ try {
36
+ const message = JSON.parse(data.toString());
37
+ if (options.handlers?.message) {
38
+ await options.handlers.message(socket, message);
39
+ }
40
+ }
41
+ catch (error) {
42
+ if (options.handlers?.error) {
43
+ options.handlers.error(socket, error);
44
+ }
45
+ }
46
+ };
47
+ socket.on('message', handleMessage);
48
+ /**
49
+ * Handles client disconnections
50
+ */
51
+ socket.on('close', () => {
52
+ if (options.handlers?.close) {
53
+ options.handlers.close(socket);
54
+ }
55
+ });
56
+ /**
57
+ * Handles WebSocket errors
58
+ *
59
+ * @param {Error} error - The error that occurred
60
+ */
61
+ socket.on('error', (error) => {
62
+ if (options.handlers?.error) {
63
+ options.handlers.error(socket, error);
64
+ }
65
+ });
66
+ // Setup heartbeat
67
+ if (options.heartbeatInterval) {
68
+ const interval = setInterval(() => {
69
+ if (!socket.isAlive) {
70
+ socket.terminate();
71
+ clearInterval(interval);
72
+ return;
73
+ }
74
+ socket.isAlive = false;
75
+ socket.ping();
76
+ }, options.heartbeatInterval);
77
+ /**
78
+ * Handles WebSocket pong responses
79
+ */
80
+ socket.on('pong', () => {
81
+ socket.isAlive = true;
82
+ });
83
+ /**
84
+ * Handles client disconnections (again, to clear the interval)
85
+ */
86
+ socket.on('close', () => {
87
+ clearInterval(interval);
88
+ });
89
+ }
90
+ });
91
+ }
92
+ /**
93
+ * Closes the WebSocket server
94
+ */
95
+ close() {
96
+ this.server.close();
97
+ }
98
+ }
99
+ /**
100
+ * Creates a new Nostr WebSocket server instance
101
+ *
102
+ * @param {NostrWSServerOptions} options - Server configuration options
103
+ * @returns {NostrWSServer} The created server instance
104
+ */
105
+ export function createWSServer(options) {
106
+ return new NostrWSServer(options);
107
+ }
package/dist/server.d.ts CHANGED
@@ -1,12 +1,16 @@
1
1
  /// <reference types="node" />
2
- import { EventEmitter } from 'events';
3
2
  import { WebSocketServer } from 'ws';
4
- import type { NostrWSOptions, NostrWSMessage, ExtendedWebSocket } from './types/index.js';
3
+ import { EventEmitter } from 'events';
4
+ import type { NostrWSOptions, NostrWSMessage } from './types/index.js';
5
+ /**
6
+ * WebSocket server implementation for Nostr protocol
7
+ * Extends EventEmitter to provide event-based message handling
8
+ */
5
9
  export declare class NostrWSServer extends EventEmitter {
6
10
  private wss;
7
11
  private options;
12
+ private clients;
8
13
  private heartbeatInterval;
9
- clients: Map<string, ExtendedWebSocket>;
10
14
  constructor(wss: WebSocketServer, options?: Partial<NostrWSOptions>);
11
15
  private setupServer;
12
16
  private handleConnection;
@@ -14,4 +18,10 @@ export declare class NostrWSServer extends EventEmitter {
14
18
  broadcast(message: NostrWSMessage): void;
15
19
  broadcastToChannel(channel: string, message: NostrWSMessage): void;
16
20
  close(): void;
21
+ /**
22
+ * Check if a client with the given ID exists
23
+ * @param clientId - The ID of the client to check
24
+ * @returns boolean indicating if the client exists
25
+ */
26
+ hasClient(clientId: string): boolean;
17
27
  }
package/dist/server.js CHANGED
@@ -1,50 +1,59 @@
1
- import { EventEmitter } from 'events';
2
1
  import { WebSocket } from 'ws';
2
+ import { EventEmitter } from 'events';
3
3
  import { v4 as uuidv4 } from 'uuid';
4
+ /**
5
+ * WebSocket server implementation for Nostr protocol
6
+ * Extends EventEmitter to provide event-based message handling
7
+ */
4
8
  export class NostrWSServer extends EventEmitter {
5
9
  constructor(wss, options = {}) {
6
10
  super();
7
- this.heartbeatInterval = null;
11
+ this.wss = null;
8
12
  this.clients = new Map();
13
+ this.heartbeatInterval = null;
9
14
  if (!options.logger) {
10
15
  throw new Error('Logger is required');
11
16
  }
12
- if (!options.handlers?.message) {
13
- throw new Error('Message handler is required');
14
- }
17
+ this.wss = wss;
15
18
  this.options = {
16
- heartbeatInterval: options.heartbeatInterval || 30000,
19
+ heartbeatInterval: 30000,
17
20
  logger: options.logger,
18
- WebSocketImpl: options.WebSocketImpl || WebSocket,
21
+ WebSocketImpl: WebSocket,
22
+ ...options,
19
23
  handlers: {
20
- message: options.handlers.message,
21
- error: options.handlers.error || (() => { }),
22
- close: options.handlers.close || (() => { })
23
- }
24
+ message: async (_ws, _message) => { },
25
+ ...options.handlers,
26
+ },
24
27
  };
25
- this.wss = wss;
26
28
  this.setupServer();
27
29
  }
28
30
  setupServer() {
31
+ if (!this.wss)
32
+ return;
29
33
  this.wss.on('connection', (ws) => {
30
- this.handleConnection(ws);
34
+ const extWs = ws;
35
+ this.handleConnection(extWs);
31
36
  });
32
- if (this.options.heartbeatInterval && this.options.heartbeatInterval > 0) {
37
+ if (this.options.heartbeatInterval) {
33
38
  this.startHeartbeat();
34
39
  }
35
40
  }
36
41
  handleConnection(ws) {
37
42
  ws.isAlive = true;
38
43
  ws.subscriptions = new Set();
39
- ws.clientId = ws.clientId || uuidv4();
44
+ ws.clientId = uuidv4();
45
+ ws.messageQueue = [];
40
46
  this.clients.set(ws.clientId, ws);
41
47
  ws.on('message', async (data) => {
42
48
  try {
43
49
  const message = JSON.parse(data.toString());
44
- await this.options.handlers.message(ws, message);
50
+ if (this.options.handlers?.message) {
51
+ await this.options.handlers.message(ws, message);
52
+ }
45
53
  }
46
54
  catch (error) {
47
- if (this.options.handlers.error) {
55
+ this.options.logger.error('Error handling message:', error);
56
+ if (this.options.handlers?.error) {
48
57
  this.options.handlers.error(ws, error);
49
58
  }
50
59
  }
@@ -53,35 +62,39 @@ export class NostrWSServer extends EventEmitter {
53
62
  if (ws.clientId) {
54
63
  this.clients.delete(ws.clientId);
55
64
  }
56
- if (this.options.handlers.close) {
65
+ if (this.options.handlers?.close) {
57
66
  this.options.handlers.close(ws);
58
67
  }
59
68
  });
60
69
  ws.on('error', (error) => {
61
- if (ws.clientId) {
62
- this.clients.delete(ws.clientId);
63
- }
64
- if (this.options.handlers.error) {
70
+ this.options.logger.error('WebSocket error:', error);
71
+ if (this.options.handlers?.error) {
65
72
  this.options.handlers.error(ws, error);
66
73
  }
67
74
  });
68
75
  }
69
76
  startHeartbeat() {
77
+ if (this.heartbeatInterval)
78
+ return;
70
79
  this.heartbeatInterval = setInterval(() => {
71
- this.wss.clients.forEach((ws) => {
72
- const extWs = ws;
73
- if (!extWs.isAlive) {
74
- extWs.terminate();
75
- return;
76
- }
77
- extWs.isAlive = false;
78
- if (ws.readyState === WebSocket.OPEN) {
79
- ws.ping();
80
+ if (!this.wss)
81
+ return;
82
+ this.wss.clients.forEach((client) => {
83
+ const extClient = client;
84
+ if (extClient.isAlive === false) {
85
+ if (extClient.clientId) {
86
+ this.clients.delete(extClient.clientId);
87
+ }
88
+ return extClient.terminate();
80
89
  }
90
+ extClient.isAlive = false;
91
+ extClient.ping();
81
92
  });
82
93
  }, this.options.heartbeatInterval);
83
94
  }
84
95
  broadcast(message) {
96
+ if (!this.wss)
97
+ return;
85
98
  this.wss.clients.forEach((client) => {
86
99
  if (client.readyState === WebSocket.OPEN) {
87
100
  client.send(JSON.stringify(message));
@@ -89,17 +102,31 @@ export class NostrWSServer extends EventEmitter {
89
102
  });
90
103
  }
91
104
  broadcastToChannel(channel, message) {
105
+ if (!this.wss)
106
+ return;
92
107
  this.wss.clients.forEach((ws) => {
93
108
  const extWs = ws;
94
109
  if (extWs.readyState === WebSocket.OPEN && extWs.subscriptions?.has(channel)) {
95
- ws.send(JSON.stringify(message));
110
+ extWs.send(JSON.stringify(message));
96
111
  }
97
112
  });
98
113
  }
99
114
  close() {
100
115
  if (this.heartbeatInterval) {
101
116
  clearInterval(this.heartbeatInterval);
117
+ this.heartbeatInterval = null;
102
118
  }
103
- this.wss.close();
119
+ if (this.wss) {
120
+ this.wss.close();
121
+ this.wss = null;
122
+ }
123
+ }
124
+ /**
125
+ * Check if a client with the given ID exists
126
+ * @param clientId - The ID of the client to check
127
+ * @returns boolean indicating if the client exists
128
+ */
129
+ hasClient(clientId) {
130
+ return this.clients.has(clientId);
104
131
  }
105
132
  }
@@ -1,58 +1,233 @@
1
1
  import type { WebSocket } from 'ws';
2
+ /**
3
+ * Type of message that can be sent through the WebSocket connection
4
+ * @type {('subscribe' | 'unsubscribe' | 'event' | 'request' | 'response' | 'error' | 'status')}
5
+ */
2
6
  export type MessageType = 'subscribe' | 'unsubscribe' | 'event' | 'request' | 'response' | 'error' | 'status';
7
+ /**
8
+ * Configuration options for the NostrWSClient
9
+ * @interface NostrWSOptions
10
+ */
3
11
  export interface NostrWSOptions {
12
+ /**
13
+ * Interval in milliseconds between heartbeat messages
14
+ * @default 30000
15
+ */
4
16
  heartbeatInterval?: number;
17
+ /**
18
+ * Interval in milliseconds between reconnect attempts
19
+ * @default 5000
20
+ */
5
21
  reconnectInterval?: number;
22
+ /**
23
+ * Maximum number of reconnect attempts
24
+ * @default 10
25
+ */
6
26
  maxReconnectAttempts?: number;
27
+ /**
28
+ * Logger instance for handling log messages
29
+ * Must implement debug, info, error, and warn methods
30
+ */
7
31
  logger: Logger;
32
+ /**
33
+ * WebSocket implementation to use
34
+ * Defaults to the native WebSocket implementation
35
+ */
8
36
  WebSocketImpl: typeof WebSocket;
37
+ /**
38
+ * Event handlers for WebSocket events
39
+ */
9
40
  handlers: {
41
+ /**
42
+ * Handler for incoming messages
43
+ * @param ws - The WebSocket instance
44
+ * @param message - The received message
45
+ */
10
46
  message: (ws: ExtendedWebSocket, message: NostrWSMessage) => Promise<void> | void;
47
+ /**
48
+ * Handler for WebSocket errors
49
+ * @param ws - The WebSocket instance
50
+ * @param error - The error object
51
+ */
11
52
  error?: (ws: WebSocket, error: Error) => void;
53
+ /**
54
+ * Handler for WebSocket connection close
55
+ * @param ws - The WebSocket instance
56
+ */
12
57
  close?: (ws: WebSocket) => void;
13
58
  };
14
59
  }
60
+ /**
61
+ * Structure of messages sent through the WebSocket connection
62
+ * @interface NostrWSMessage
63
+ */
15
64
  export interface NostrWSMessage {
16
- type: string;
65
+ /**
66
+ * Type of the message (e.g., 'EVENT', 'subscribe', 'unsubscribe')
67
+ */
68
+ type: MessageType;
69
+ /**
70
+ * Unique identifier for the message
71
+ */
17
72
  id?: string;
73
+ /**
74
+ * Data payload of the message
75
+ */
18
76
  data: Record<string, unknown>;
19
77
  }
78
+ /**
79
+ * Represents a subscription to a Nostr relay
80
+ * @interface NostrWSSubscription
81
+ */
20
82
  export interface NostrWSSubscription {
83
+ /**
84
+ * Channel identifier for the subscription
85
+ */
21
86
  channel: string;
87
+ /**
88
+ * Filter criteria for the subscription
89
+ */
22
90
  filter?: Record<string, unknown>;
23
91
  }
92
+ /**
93
+ * Events emitted by the NostrWSClient
94
+ * @interface NostrWSClientEvents
95
+ */
24
96
  export interface NostrWSClientEvents {
97
+ /**
98
+ * Emitted when the client connects to the relay
99
+ */
25
100
  connect: () => void;
101
+ /**
102
+ * Emitted when the client disconnects from the relay
103
+ */
26
104
  disconnect: () => void;
105
+ /**
106
+ * Emitted when the client reconnects to the relay
107
+ */
27
108
  reconnect: () => void;
109
+ /**
110
+ * Emitted when a message is received from the relay
111
+ * @param message - The received message
112
+ */
28
113
  message: (message: NostrWSMessage) => void;
114
+ /**
115
+ * Emitted when an error occurs
116
+ * @param error - The error object
117
+ */
29
118
  error: (error: Error) => void;
30
119
  }
120
+ /**
121
+ * Events emitted by the NostrWSServer
122
+ * @interface NostrWSServerEvents
123
+ */
31
124
  export interface NostrWSServerEvents {
125
+ /**
126
+ * Emitted when a client connects to the server
127
+ * @param client - The connected client
128
+ */
32
129
  connection: (client: ExtendedWebSocket) => void;
130
+ /**
131
+ * Emitted when a message is received from a client
132
+ * @param message - The received message
133
+ * @param client - The client that sent the message
134
+ */
33
135
  message: (message: NostrWSMessage, client: ExtendedWebSocket) => void;
136
+ /**
137
+ * Emitted when an error occurs
138
+ * @param error - The error object
139
+ */
34
140
  error: (error: Error) => void;
35
141
  }
142
+ /**
143
+ * Extended WebSocket interface with additional properties
144
+ * @interface ExtendedWebSocket
145
+ */
36
146
  export interface ExtendedWebSocket extends WebSocket {
147
+ /**
148
+ * Whether the WebSocket connection is alive
149
+ */
37
150
  isAlive?: boolean;
151
+ /**
152
+ * Set of subscription channels
153
+ */
38
154
  subscriptions?: Set<string>;
155
+ /**
156
+ * Unique client identifier
157
+ */
39
158
  clientId?: string;
159
+ /**
160
+ * Queue of messages to be sent
161
+ */
40
162
  messageQueue?: NostrWSMessage[];
163
+ /**
164
+ * Timestamp of the last ping message
165
+ */
41
166
  lastPing?: number;
167
+ /**
168
+ * Number of reconnect attempts
169
+ */
42
170
  reconnectAttempts?: number;
43
171
  }
172
+ /**
173
+ * Result of validating a NostrWSMessage
174
+ * @interface NostrWSValidationResult
175
+ */
44
176
  export interface NostrWSValidationResult {
177
+ /**
178
+ * Whether the message is valid
179
+ */
45
180
  isValid: boolean;
181
+ /**
182
+ * Error message if the message is invalid
183
+ */
46
184
  error?: string;
47
185
  }
186
+ /**
187
+ * State of the NostrWSClient connection
188
+ * @interface NostrWSConnectionState
189
+ */
48
190
  export interface NostrWSConnectionState {
191
+ /**
192
+ * Whether the client is connected to the relay
193
+ */
49
194
  isConnected: boolean;
195
+ /**
196
+ * Number of reconnect attempts
197
+ */
50
198
  reconnectAttempts: number;
199
+ /**
200
+ * Last error message
201
+ */
51
202
  lastError?: string;
52
203
  }
204
+ /**
205
+ * Logger interface
206
+ * @interface Logger
207
+ */
53
208
  export interface Logger {
209
+ /**
210
+ * Logs a debug message
211
+ * @param message - The message to log
212
+ * @param args - Additional arguments to log
213
+ */
54
214
  debug: (message: string, ...args: unknown[]) => void;
215
+ /**
216
+ * Logs an info message
217
+ * @param message - The message to log
218
+ * @param args - Additional arguments to log
219
+ */
55
220
  info: (message: string, ...args: unknown[]) => void;
221
+ /**
222
+ * Logs a warning message
223
+ * @param message - The message to log
224
+ * @param args - Additional arguments to log
225
+ */
56
226
  warn: (message: string, ...args: unknown[]) => void;
227
+ /**
228
+ * Logs an error message
229
+ * @param message - The message to log
230
+ * @param args - Additional arguments to log
231
+ */
57
232
  error: (message: string, ...args: unknown[]) => void;
58
233
  }
@@ -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 = {}));
@@ -1,2 +1,6 @@
1
+ /**
2
+ * @file Logger utility for Nostr WebSocket operations
3
+ * @module logger
4
+ */
1
5
  import winston from 'winston';
2
- export declare function getLogger(name?: string): winston.Logger;
6
+ export declare function getLogger(context: string): winston.Logger;
@@ -1,4 +1,21 @@
1
+ /**
2
+ * @file Logger utility for Nostr WebSocket operations
3
+ * @module logger
4
+ */
1
5
  import winston from 'winston';
6
+ /**
7
+ * Creates a logger instance with a specific context
8
+ *
9
+ * @param {string} context - The context identifier for the logger
10
+ * @returns {Logger} A logger instance with debug, info, warn, and error methods
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const logger = getLogger('MyComponent');
15
+ * logger.info('Component initialized');
16
+ * logger.error('An error occurred', new Error('Failed to connect'));
17
+ * ```
18
+ */
2
19
  const logger = winston.createLogger({
3
20
  level: process.env.LOG_LEVEL || 'info',
4
21
  format: winston.format.combine(winston.format.timestamp(), winston.format.json()),
@@ -8,9 +25,6 @@ const logger = winston.createLogger({
8
25
  })
9
26
  ]
10
27
  });
11
- export function getLogger(name) {
12
- if (name) {
13
- return logger.child({ service: name });
14
- }
15
- return logger;
28
+ export function getLogger(context) {
29
+ return logger.child({ service: context });
16
30
  }
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.5",
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",
@@ -12,7 +12,10 @@
12
12
  "test:coverage": "jest --coverage",
13
13
  "lint": "eslint src --ext .ts",
14
14
  "prepare": "npm run build",
15
- "prepublishOnly": "npm test && npm run lint"
15
+ "prepublishOnly": "npm test && npm run lint",
16
+ "docs": "typedoc",
17
+ "docs:watch": "typedoc --watch",
18
+ "predeploy": "npm run build && npm run docs"
16
19
  },
17
20
  "keywords": [
18
21
  "nostr",
@@ -37,7 +40,7 @@
37
40
  "bugs": {
38
41
  "url": "https://github.com/HumanjavaEnterprises/nostr-websocket-utils/issues"
39
42
  },
40
- "homepage": "https://github.com/HumanjavaEnterprises/nostr-websocket-utils#readme",
43
+ "homepage": "https://humanjavaenterprises.github.io/nostr-websocket-utils",
41
44
  "dependencies": {
42
45
  "@noble/curves": "^1.3.0",
43
46
  "@noble/hashes": "^1.3.3",
@@ -51,6 +54,7 @@
51
54
  "nostr-tools": "^2.1.4"
52
55
  },
53
56
  "devDependencies": {
57
+ "@jest/globals": "^29.7.0",
54
58
  "@types/jest": "^29.5.14",
55
59
  "@types/node": "^20.11.0",
56
60
  "@types/ws": "^8.5.10",
@@ -59,6 +63,8 @@
59
63
  "eslint": "^8.56.0",
60
64
  "jest": "^29.7.0",
61
65
  "ts-jest": "^29.1.1",
66
+ "typedoc": "^0.27.5",
67
+ "typedoc-plugin-markdown": "^4.3.2",
62
68
  "typescript": "^5.3.3"
63
69
  },
64
70
  "files": [