nostr-websocket-utils 0.2.4 → 0.3.0

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.
Files changed (111) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +151 -103
  3. package/dist/__mocks__/extendedWsMock.d.ts +35 -0
  4. package/dist/__mocks__/extendedWsMock.js +156 -0
  5. package/dist/__mocks__/logger.d.ts +9 -0
  6. package/dist/__mocks__/logger.js +6 -0
  7. package/dist/__mocks__/mockLogger.d.ts +41 -0
  8. package/dist/__mocks__/mockLogger.js +47 -0
  9. package/dist/__mocks__/mockserver.d.ts +31 -0
  10. package/dist/__mocks__/mockserver.js +39 -0
  11. package/dist/__mocks__/wsMock.d.ts +26 -0
  12. package/dist/__mocks__/wsMock.js +120 -0
  13. package/dist/client.d.ts +105 -0
  14. package/dist/client.js +105 -0
  15. package/dist/core/client.d.ts +94 -0
  16. package/dist/core/client.js +360 -0
  17. package/dist/core/nostr-server.d.ts +27 -0
  18. package/dist/core/nostr-server.js +95 -0
  19. package/dist/core/queue.d.ts +61 -0
  20. package/dist/core/queue.js +108 -0
  21. package/dist/core/server.d.ts +27 -0
  22. package/dist/core/server.js +114 -0
  23. package/dist/crypto/bech32.d.ts +26 -0
  24. package/dist/crypto/bech32.js +163 -0
  25. package/dist/crypto/handlers.d.ts +11 -0
  26. package/dist/crypto/handlers.js +36 -0
  27. package/dist/crypto/index.d.ts +5 -0
  28. package/dist/crypto/index.js +5 -0
  29. package/dist/crypto/schnorr.d.ts +16 -0
  30. package/dist/crypto/schnorr.js +51 -0
  31. package/dist/endpoints/metrics.d.ts +29 -0
  32. package/dist/endpoints/metrics.js +101 -0
  33. package/dist/index.d.ts +11 -6
  34. package/dist/index.js +16 -4
  35. package/dist/nips/index.d.ts +19 -0
  36. package/dist/nips/index.js +34 -0
  37. package/dist/nips/nip-01.d.ts +34 -0
  38. package/dist/nips/nip-01.js +145 -0
  39. package/dist/nips/nip-02.d.ts +83 -0
  40. package/dist/nips/nip-02.js +123 -0
  41. package/dist/nips/nip-04.d.ts +36 -0
  42. package/dist/nips/nip-04.js +105 -0
  43. package/dist/nips/nip-05.d.ts +86 -0
  44. package/dist/nips/nip-05.js +151 -0
  45. package/dist/nips/nip-09.d.ts +92 -0
  46. package/dist/nips/nip-09.js +190 -0
  47. package/dist/nips/nip-11.d.ts +64 -0
  48. package/dist/nips/nip-11.js +154 -0
  49. package/dist/nips/nip-13.d.ts +73 -0
  50. package/dist/nips/nip-13.js +128 -0
  51. package/dist/nips/nip-15.d.ts +83 -0
  52. package/dist/nips/nip-15.js +101 -0
  53. package/dist/nips/nip-16.d.ts +88 -0
  54. package/dist/nips/nip-16.js +150 -0
  55. package/dist/nips/nip-19.d.ts +28 -0
  56. package/dist/nips/nip-19.js +103 -0
  57. package/dist/nips/nip-20.d.ts +59 -0
  58. package/dist/nips/nip-20.js +95 -0
  59. package/dist/nips/nip-22.d.ts +89 -0
  60. package/dist/nips/nip-22.js +142 -0
  61. package/dist/nips/nip-26.d.ts +52 -0
  62. package/dist/nips/nip-26.js +139 -0
  63. package/dist/nips/nip-28.d.ts +103 -0
  64. package/dist/nips/nip-28.js +170 -0
  65. package/dist/nips/nip-33.d.ts +94 -0
  66. package/dist/nips/nip-33.js +133 -0
  67. package/dist/nostr-server.d.ts +23 -0
  68. package/dist/nostr-server.js +44 -0
  69. package/dist/server.d.ts +13 -3
  70. package/dist/server.js +60 -33
  71. package/dist/transport/base.d.ts +54 -0
  72. package/dist/transport/base.js +104 -0
  73. package/dist/transport/websocket.d.ts +22 -0
  74. package/dist/transport/websocket.js +122 -0
  75. package/dist/types/events.d.ts +63 -0
  76. package/dist/types/events.js +5 -0
  77. package/dist/types/filters.d.ts +19 -0
  78. package/dist/types/filters.js +5 -0
  79. package/dist/types/handlers.d.ts +80 -0
  80. package/dist/types/handlers.js +5 -0
  81. package/dist/types/index.d.ts +118 -39
  82. package/dist/types/index.js +21 -1
  83. package/dist/types/logger.d.ts +40 -0
  84. package/dist/types/logger.js +5 -0
  85. package/dist/types/messages.d.ts +135 -0
  86. package/dist/types/messages.js +40 -0
  87. package/dist/types/nostr.d.ts +120 -39
  88. package/dist/types/nostr.js +5 -10
  89. package/dist/types/options.d.ts +154 -0
  90. package/dist/types/options.js +5 -0
  91. package/dist/types/relays.d.ts +26 -0
  92. package/dist/types/relays.js +5 -0
  93. package/dist/types/scoring.d.ts +47 -0
  94. package/dist/types/scoring.js +29 -0
  95. package/dist/types/socket.d.ts +99 -0
  96. package/dist/types/socket.js +5 -0
  97. package/dist/types/transport.d.ts +97 -0
  98. package/dist/types/transport.js +5 -0
  99. package/dist/types/validation.d.ts +50 -0
  100. package/dist/types/validation.js +5 -0
  101. package/dist/types/websocket.d.ts +172 -0
  102. package/dist/types/websocket.js +5 -0
  103. package/dist/utils/http.d.ts +10 -0
  104. package/dist/utils/http.js +24 -0
  105. package/dist/utils/logger.d.ts +11 -2
  106. package/dist/utils/logger.js +18 -13
  107. package/dist/utils/metrics.d.ts +81 -0
  108. package/dist/utils/metrics.js +206 -0
  109. package/dist/utils/rate-limiter.d.ts +85 -0
  110. package/dist/utils/rate-limiter.js +175 -0
  111. package/package.json +18 -21
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
  }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @file Base transport layer for Nostr connections
3
+ * @module transport/base
4
+ */
5
+ /// <reference types="node" />
6
+ import { EventEmitter } from 'events';
7
+ import type { MetricsProvider, ScoringStrategy } from '../types/scoring';
8
+ export interface TransportOptions {
9
+ metricsProvider?: MetricsProvider;
10
+ scoringStrategy?: ScoringStrategy;
11
+ metricsEnabled?: boolean;
12
+ }
13
+ export declare abstract class BaseTransport extends EventEmitter {
14
+ protected scoringStrategy: ScoringStrategy;
15
+ protected metricsProvider?: MetricsProvider;
16
+ protected metricsEnabled: boolean;
17
+ constructor(options?: TransportOptions);
18
+ /**
19
+ * Create a default metrics provider if none is supplied
20
+ */
21
+ protected createDefaultMetricsProvider(): MetricsProvider;
22
+ /**
23
+ * Track a metric update
24
+ */
25
+ protected trackMetric(endpoint: string, metricType: string, value: any): void;
26
+ /**
27
+ * Gets the score for an endpoint
28
+ * @param endpoint - Endpoint to get score for
29
+ * @returns {number} Score between 0 and 100
30
+ */
31
+ getScore(endpoint: string): number;
32
+ /**
33
+ * Gets scores for all endpoints
34
+ * @returns {Map<string, number>} Map of endpoint scores
35
+ */
36
+ getAllScores(): Map<string, number>;
37
+ /**
38
+ * Enable/disable metrics
39
+ */
40
+ setMetricsEnabled(enabled: boolean): void;
41
+ /**
42
+ * Updates the scoring strategy
43
+ * @param strategy - New scoring strategy
44
+ */
45
+ updateScoringStrategy(strategy: ScoringStrategy): void;
46
+ /**
47
+ * Updates the metrics provider
48
+ * @param provider - New metrics provider
49
+ */
50
+ updateMetricsProvider(provider?: MetricsProvider): void;
51
+ abstract connect(endpoint: string): Promise<void>;
52
+ abstract disconnect(endpoint: string): Promise<void>;
53
+ abstract send(endpoint: string, data: any): Promise<void>;
54
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * @file Base transport layer for Nostr connections
3
+ * @module transport/base
4
+ */
5
+ import { EventEmitter } from 'events';
6
+ import { getLogger } from '../utils/logger';
7
+ import { DefaultScoringStrategy } from '../types/scoring';
8
+ const logger = getLogger('BaseTransport');
9
+ export class BaseTransport extends EventEmitter {
10
+ constructor(options = {}) {
11
+ super();
12
+ this.scoringStrategy = options.scoringStrategy ?? new DefaultScoringStrategy();
13
+ this.metricsProvider = options.metricsProvider;
14
+ this.metricsEnabled = !!this.metricsProvider;
15
+ }
16
+ /**
17
+ * Create a default metrics provider if none is supplied
18
+ */
19
+ createDefaultMetricsProvider() {
20
+ return {
21
+ getMetrics: () => ({
22
+ totalConnections: 0,
23
+ activeConnections: 0,
24
+ connectionErrors: 0,
25
+ messagesReceived: 0,
26
+ messagesSent: 0,
27
+ bytesReceived: 0,
28
+ bytesSent: 0,
29
+ averageLatency: 0,
30
+ lastSeen: Date.now()
31
+ }),
32
+ getAllMetrics: () => new Map(),
33
+ trackMetric: () => { }
34
+ };
35
+ }
36
+ /**
37
+ * Track a metric update
38
+ */
39
+ trackMetric(endpoint, metricType, value) {
40
+ if (!this.metricsEnabled)
41
+ return;
42
+ try {
43
+ this.metricsProvider?.trackMetric(endpoint, metricType, value);
44
+ const event = {
45
+ endpoint,
46
+ metricType,
47
+ value,
48
+ timestamp: Date.now()
49
+ };
50
+ this.emit('metric', event);
51
+ }
52
+ catch (error) {
53
+ logger.error({ error, endpoint, metricType }, 'Error tracking metric');
54
+ }
55
+ }
56
+ /**
57
+ * Gets the score for an endpoint
58
+ * @param endpoint - Endpoint to get score for
59
+ * @returns {number} Score between 0 and 100
60
+ */
61
+ getScore(endpoint) {
62
+ if (!this.metricsEnabled)
63
+ return 100;
64
+ const metrics = this.metricsProvider?.getMetrics(endpoint);
65
+ if (!metrics)
66
+ return 100;
67
+ return this.scoringStrategy.calculateScore(metrics);
68
+ }
69
+ /**
70
+ * Gets scores for all endpoints
71
+ * @returns {Map<string, number>} Map of endpoint scores
72
+ */
73
+ getAllScores() {
74
+ if (!this.metricsEnabled || !this.metricsProvider)
75
+ return new Map();
76
+ const scores = new Map();
77
+ const allMetrics = this.metricsProvider.getAllMetrics();
78
+ for (const [endpoint, metrics] of allMetrics) {
79
+ scores.set(endpoint, this.scoringStrategy.calculateScore(metrics));
80
+ }
81
+ return scores;
82
+ }
83
+ /**
84
+ * Enable/disable metrics
85
+ */
86
+ setMetricsEnabled(enabled) {
87
+ this.metricsEnabled = enabled;
88
+ }
89
+ /**
90
+ * Updates the scoring strategy
91
+ * @param strategy - New scoring strategy
92
+ */
93
+ updateScoringStrategy(strategy) {
94
+ this.scoringStrategy = strategy;
95
+ }
96
+ /**
97
+ * Updates the metrics provider
98
+ * @param provider - New metrics provider
99
+ */
100
+ updateMetricsProvider(provider) {
101
+ this.metricsProvider = provider;
102
+ this.metricsEnabled = !!provider;
103
+ }
104
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @file WebSocket transport implementation
3
+ * @module transport/websocket
4
+ */
5
+ import { BaseTransport } from './base';
6
+ import type { TransportOptions } from './base';
7
+ export declare class WebSocketTransport extends BaseTransport {
8
+ private connections;
9
+ constructor(options?: TransportOptions);
10
+ connect(endpoint: string): Promise<void>;
11
+ disconnect(endpoint: string): Promise<void>;
12
+ send(endpoint: string, data: any): Promise<void>;
13
+ private checkEndpointHealth;
14
+ /**
15
+ * Get all active WebSocket connections
16
+ */
17
+ getActiveConnections(): string[];
18
+ /**
19
+ * Check if connected to endpoint
20
+ */
21
+ isConnected(endpoint: string): boolean;
22
+ }
@@ -0,0 +1,122 @@
1
+ /**
2
+ * @file WebSocket transport implementation
3
+ * @module transport/websocket
4
+ */
5
+ import WebSocket from 'ws';
6
+ import { BaseTransport } from './base';
7
+ import { getLogger } from '../utils/logger';
8
+ const logger = getLogger('WebSocketTransport');
9
+ export class WebSocketTransport extends BaseTransport {
10
+ constructor(options = {}) {
11
+ super(options);
12
+ this.connections = new Map();
13
+ }
14
+ async connect(endpoint) {
15
+ if (this.connections.has(endpoint)) {
16
+ logger.warn({ endpoint }, 'Already connected to endpoint');
17
+ return;
18
+ }
19
+ try {
20
+ const ws = new WebSocket(endpoint);
21
+ ws.on('open', () => {
22
+ this.trackMetric(endpoint, 'connection', true);
23
+ this.trackMetric(endpoint, 'openHandshake', Date.now());
24
+ });
25
+ ws.on('close', () => {
26
+ this.trackMetric(endpoint, 'connection', false);
27
+ this.trackMetric(endpoint, 'closeHandshake', Date.now());
28
+ this.connections.delete(endpoint);
29
+ });
30
+ ws.on('message', (data) => {
31
+ const length = data instanceof Buffer ? data.length :
32
+ data instanceof ArrayBuffer ? data.byteLength :
33
+ data instanceof Array ? data.reduce((acc, buf) => acc + buf.length, 0) : 0;
34
+ this.trackMetric(endpoint, 'messageReceived', length);
35
+ });
36
+ ws.on('error', (error) => {
37
+ this.trackMetric(endpoint, 'error', error);
38
+ logger.error({ error, endpoint }, 'WebSocket error');
39
+ });
40
+ // Track ping/pong for latency
41
+ ws.on('ping', () => {
42
+ this.trackMetric(endpoint, 'ping', Date.now());
43
+ });
44
+ ws.on('pong', () => {
45
+ if (!this.metricsEnabled || !this.metricsProvider)
46
+ return;
47
+ const now = Date.now();
48
+ const metrics = this.metricsProvider.getMetrics(endpoint);
49
+ if (!metrics || !metrics.lastSeen)
50
+ return;
51
+ this.trackMetric(endpoint, 'latency', now - metrics.lastSeen);
52
+ });
53
+ this.connections.set(endpoint, ws);
54
+ }
55
+ catch (error) {
56
+ this.trackMetric(endpoint, 'connectionError', error);
57
+ throw error;
58
+ }
59
+ }
60
+ async disconnect(endpoint) {
61
+ const ws = this.connections.get(endpoint);
62
+ if (!ws)
63
+ return;
64
+ return new Promise((resolve, reject) => {
65
+ ws.close();
66
+ ws.on('close', () => {
67
+ this.connections.delete(endpoint);
68
+ resolve();
69
+ });
70
+ ws.on('error', reject);
71
+ });
72
+ }
73
+ async send(endpoint, data) {
74
+ const ws = this.connections.get(endpoint);
75
+ if (!ws)
76
+ throw new Error(`Not connected to ${endpoint}`);
77
+ return new Promise((resolve, reject) => {
78
+ ws.send(data, (error) => {
79
+ if (error) {
80
+ this.trackMetric(endpoint, 'sendError', error);
81
+ reject(error);
82
+ }
83
+ else {
84
+ this.trackMetric(endpoint, 'messageSent', data.length);
85
+ resolve();
86
+ }
87
+ });
88
+ });
89
+ }
90
+ async checkEndpointHealth(endpoint) {
91
+ try {
92
+ if (!this.metricsEnabled || !this.metricsProvider) {
93
+ return true;
94
+ }
95
+ const metrics = this.metricsProvider.getMetrics(endpoint);
96
+ if (!metrics || !metrics.lastSeen) {
97
+ return true;
98
+ }
99
+ const now = Date.now();
100
+ const elapsed = now - metrics.lastSeen;
101
+ // If we haven't seen a ping response in 30 seconds, consider unhealthy
102
+ return elapsed < 30000;
103
+ }
104
+ catch (error) {
105
+ logger.error('Error checking endpoint health:', error);
106
+ return false;
107
+ }
108
+ }
109
+ /**
110
+ * Get all active WebSocket connections
111
+ */
112
+ getActiveConnections() {
113
+ return Array.from(this.connections.keys());
114
+ }
115
+ /**
116
+ * Check if connected to endpoint
117
+ */
118
+ isConnected(endpoint) {
119
+ const ws = this.connections.get(endpoint);
120
+ return ws?.readyState === WebSocket.OPEN;
121
+ }
122
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * @file Event type definitions
3
+ * @module types/events
4
+ */
5
+ /**
6
+ * Base Nostr event interface following NIP-01 specification
7
+ * @see https://github.com/nostr-protocol/nips/blob/master/01.md
8
+ */
9
+ export interface NostrEvent {
10
+ /** Event ID in hex format */
11
+ id: string;
12
+ /** Public key of the event creator in hex format */
13
+ pubkey: string;
14
+ /** Unix timestamp in seconds */
15
+ created_at: number;
16
+ /** Event kind number */
17
+ kind: number;
18
+ /** Array of tags */
19
+ tags: string[][];
20
+ /** Event content */
21
+ content: string;
22
+ /** Signature of the event data in hex format */
23
+ sig: string;
24
+ }
25
+ /**
26
+ * Signed Nostr event with id and signature
27
+ * @extends NostrEvent
28
+ */
29
+ export interface SignedNostrEvent extends NostrEvent {
30
+ /** Event ID in hex format */
31
+ id: string;
32
+ /** Signature of the event data in hex format */
33
+ sig: string;
34
+ }
35
+ /**
36
+ * Event validation result
37
+ */
38
+ export interface NostrEventValidationResult {
39
+ valid: boolean;
40
+ error?: string;
41
+ }
42
+ /**
43
+ * Event subscription filter
44
+ */
45
+ export interface NostrEventFilter {
46
+ ids?: string[];
47
+ authors?: string[];
48
+ kinds?: number[];
49
+ since?: number;
50
+ until?: number;
51
+ limit?: number;
52
+ [key: string]: unknown;
53
+ }
54
+ /**
55
+ * Nostr subscription event interface
56
+ * Used for creating subscription messages to relays
57
+ */
58
+ export interface NostrSubscriptionEvent {
59
+ /** Subscription ID */
60
+ subscriptionId: string;
61
+ /** Array of filters */
62
+ filters: NostrEventFilter[];
63
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @file Event type definitions
3
+ * @module types/events
4
+ */
5
+ export {};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @file Filter type definitions
3
+ * @module types/filters
4
+ */
5
+ import { NostrEventFilter } from './events';
6
+ /**
7
+ * Subscription request filter
8
+ */
9
+ export interface NostrSubscriptionFilter extends NostrEventFilter {
10
+ subscriptionId?: string;
11
+ limit?: number;
12
+ }
13
+ /**
14
+ * Filter validation result
15
+ */
16
+ export interface NostrFilterValidationResult {
17
+ valid: boolean;
18
+ error?: string;
19
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @file Filter type definitions
3
+ * @module types/filters
4
+ */
5
+ export {};
@@ -0,0 +1,80 @@
1
+ /**
2
+ * @file Handler type definitions for WebSocket events
3
+ * @module types/handlers
4
+ */
5
+ import type { WebSocket } from 'ws';
6
+ import type { NostrWSMessage } from './messages';
7
+ import type { ExtendedWebSocket } from './websocket';
8
+ /**
9
+ * Events emitted by the NostrWSClient
10
+ * @interface NostrWSClientEvents
11
+ */
12
+ export interface NostrWSClientEvents {
13
+ /**
14
+ * Emitted when the client connects to the relay
15
+ */
16
+ connect: () => void;
17
+ /**
18
+ * Emitted when the client disconnects from the relay
19
+ */
20
+ disconnect: () => void;
21
+ /**
22
+ * Emitted when the client reconnects to the relay
23
+ */
24
+ reconnect: () => void;
25
+ /**
26
+ * Emitted when a message is received from the relay
27
+ * @param message - The received message
28
+ */
29
+ message: (message: NostrWSMessage) => void;
30
+ /**
31
+ * Emitted when an error occurs
32
+ * @param error - The error object
33
+ */
34
+ error: (error: Error) => void;
35
+ }
36
+ /**
37
+ * Events emitted by the NostrWSServer
38
+ * @interface NostrWSServerEvents
39
+ */
40
+ export interface NostrWSServerEvents {
41
+ /**
42
+ * Emitted when a client connects to the server
43
+ * @param client - The connected client
44
+ */
45
+ connection: (client: ExtendedWebSocket) => void;
46
+ /**
47
+ * Emitted when a message is received from a client
48
+ * @param message - The received message
49
+ * @param client - The client that sent the message
50
+ */
51
+ message: (message: NostrWSMessage, client: ExtendedWebSocket) => void;
52
+ /**
53
+ * Emitted when an error occurs
54
+ * @param error - The error object
55
+ */
56
+ error: (error: Error) => void;
57
+ }
58
+ /**
59
+ * Configuration options for WebSocket handlers
60
+ * @interface NostrWSHandlers
61
+ */
62
+ export interface NostrWSHandlers {
63
+ /**
64
+ * Handler for incoming messages
65
+ * @param ws - The WebSocket instance
66
+ * @param message - The received message
67
+ */
68
+ message: (ws: ExtendedWebSocket, message: NostrWSMessage) => Promise<void> | void;
69
+ /**
70
+ * Handler for WebSocket errors
71
+ * @param ws - The WebSocket instance
72
+ * @param error - The error object
73
+ */
74
+ error?: (ws: WebSocket, error: Error) => void;
75
+ /**
76
+ * Handler for WebSocket connection close
77
+ * @param ws - The WebSocket instance
78
+ */
79
+ close?: (ws: WebSocket) => void;
80
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @file Handler type definitions for WebSocket events
3
+ * @module types/handlers
4
+ */
5
+ export {};