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 CHANGED
@@ -1,12 +1,12 @@
1
- Nostr Websocket Utils
1
+ # Nostr Websocket Utils
2
2
 
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)
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)
5
5
  [![Build Status](https://github.com/HumanjavaEnterprises/nostr-websocket-utils/workflows/CI/badge.svg)](https://github.com/HumanjavaEnterprises/nostr-websocket-utils/actions)
6
6
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org)
7
7
  [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
8
8
 
9
- A TypeScript library providing WebSocket utilities for Nostr applications, with robust connection handling, automatic reconnection, and channel-based messaging.
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 @humanjavaenterprises/nostr-websocket-utils
25
+ npm install nostr-websocket-utils
26
26
  ```
27
27
 
28
- ## Breaking Changes in v0.2.0
28
+ ## Breaking Changes in v0.2.2
29
29
 
30
- - Introduced required handlers pattern for better type safety
31
- - Removed individual event handler properties (onMessage, onError, onClose)
32
- - Message handler is now required in server options
33
- - Client updated to match server interface
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 '@humanjavaenterprises/nostr-websocket-utils';
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 required handlers
56
- const wss = new NostrWSServer(server, {
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('Client disconnected');
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 '@humanjavaenterprises/nostr-websocket-utils';
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('wss://your-server.com', {
106
- heartbeatInterval: 30000,
107
- reconnectInterval: 5000,
108
- maxReconnectAttempts: 5,
88
+ const client = new NostrWSClient('ws://localhost:3000', {
109
89
  logger,
110
- handlers: {
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
- // Listen to events
125
- client.on('connect', () => {
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.ws = new WebSocket(this.url);
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 { Server as HttpServer } from 'http';
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
- constructor(server: HttpServer, options?: Partial<NostrWSOptions>);
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 { WebSocketServer, WebSocket } from 'ws';
2
+ import { WebSocket } from 'ws';
3
+ import { v4 as uuidv4 } from 'uuid';
3
4
  export class NostrWSServer extends EventEmitter {
4
- constructor(server, options = {}) {
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 = new WebSocketServer({ server });
25
+ this.wss = wss;
23
26
  this.setupServer();
24
27
  }
25
28
  setupServer() {
26
29
  this.wss.on('connection', (ws) => {
27
- const extWs = ws;
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) => {
@@ -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.1",
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": "Human Java Enterprises",
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
- "nostr-tools": "^2.1.4"
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.11",
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",