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
@@ -0,0 +1,114 @@
1
+ /**
2
+ * @file WebSocket server implementation
3
+ * @module core/server
4
+ */
5
+ import { WebSocketServer, WebSocket } from 'ws';
6
+ import { v4 as uuidv4 } from 'uuid';
7
+ import { getLogger } from '../utils/logger';
8
+ import { createRateLimiter } from '../utils/rate-limiter';
9
+ const logger = getLogger('NostrWSServer');
10
+ /**
11
+ * NostrWSServer class for handling WebSocket connections
12
+ */
13
+ export class NostrWSServer {
14
+ constructor(options) {
15
+ this.options = {
16
+ ...options,
17
+ port: options.port || 8080,
18
+ host: options.host || 'localhost',
19
+ maxConnections: options.maxConnections || 1000,
20
+ pingInterval: options.pingInterval || 30000,
21
+ rateLimits: options.rateLimits
22
+ };
23
+ this.wss = new WebSocketServer({
24
+ port: this.options.port,
25
+ host: this.options.host
26
+ });
27
+ this.setupServer();
28
+ this.startPingInterval();
29
+ if (this.options.rateLimits) {
30
+ this.rateLimiter = createRateLimiter({
31
+ EVENT: this.options.rateLimits
32
+ }, logger);
33
+ }
34
+ }
35
+ /**
36
+ * Set up WebSocket server event handlers
37
+ */
38
+ setupServer() {
39
+ this.wss.on('connection', async (ws) => {
40
+ const socket = ws;
41
+ socket.clientId = uuidv4();
42
+ socket.subscriptions = new Set();
43
+ socket.isAlive = true;
44
+ logger.info(`Client connected: ${socket.clientId}`);
45
+ if (this.wss.clients.size > this.options.maxConnections) {
46
+ logger.warn(`Max connections (${this.options.maxConnections}) reached`);
47
+ socket.close(1008, 'Max connections reached');
48
+ return;
49
+ }
50
+ socket.on('message', async (data) => {
51
+ try {
52
+ const message = JSON.parse(data.toString());
53
+ if (this.rateLimiter && await this.rateLimiter.shouldLimit(socket.clientId, message)) {
54
+ socket.send(JSON.stringify({
55
+ type: 'NOTICE',
56
+ data: { message: 'Rate limit exceeded' }
57
+ }));
58
+ return;
59
+ }
60
+ await this.options.onMessage?.(message, socket);
61
+ }
62
+ catch (error) {
63
+ logger.error('Error processing message:', error);
64
+ this.options.onError?.(error, socket);
65
+ }
66
+ });
67
+ socket.on('error', (error) => {
68
+ logger.error(`Client error (${socket.clientId}):`, error);
69
+ this.options.onError?.(error, socket);
70
+ });
71
+ socket.on('close', () => {
72
+ logger.info(`Client disconnected: ${socket.clientId}`);
73
+ this.options.onClose?.(socket);
74
+ });
75
+ socket.on('pong', () => {
76
+ socket.isAlive = true;
77
+ });
78
+ await this.options.onConnection?.(socket);
79
+ });
80
+ }
81
+ /**
82
+ * Start ping interval to check client connections
83
+ */
84
+ startPingInterval() {
85
+ if (this.options.pingInterval) {
86
+ this.pingInterval = setInterval(() => {
87
+ this.wss.clients.forEach((socket) => {
88
+ const nostrSocket = socket;
89
+ if (!nostrSocket.isAlive) {
90
+ nostrSocket.terminate();
91
+ return;
92
+ }
93
+ nostrSocket.isAlive = false;
94
+ nostrSocket.ping();
95
+ });
96
+ }, this.options.pingInterval);
97
+ }
98
+ }
99
+ /**
100
+ * Stop the server and clean up resources
101
+ */
102
+ stop() {
103
+ if (this.pingInterval) {
104
+ clearInterval(this.pingInterval);
105
+ }
106
+ this.wss.clients.forEach((socket) => {
107
+ const nostrSocket = socket;
108
+ if (nostrSocket.readyState === WebSocket.OPEN) {
109
+ nostrSocket.close();
110
+ }
111
+ });
112
+ this.wss.close();
113
+ }
114
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @file Bech32 encoding/decoding utilities
3
+ * @module crypto/bech32
4
+ */
5
+ /**
6
+ * Encode data to bech32
7
+ */
8
+ export declare function bech32Encode(hrp: string, data: number[]): string;
9
+ /**
10
+ * Decode bech32 string
11
+ */
12
+ export declare function bech32Decode(str: string): {
13
+ hrp: string;
14
+ data: number[];
15
+ };
16
+ /**
17
+ * Encode hex string to bech32
18
+ */
19
+ export declare function encodeToBech32(hrp: string, hex: string): string;
20
+ /**
21
+ * Decode bech32 to hex string
22
+ */
23
+ export declare function decodeFromBech32(str: string): {
24
+ prefix: string;
25
+ hex: string;
26
+ };
@@ -0,0 +1,163 @@
1
+ /**
2
+ * @file Bech32 encoding/decoding utilities
3
+ * @module crypto/bech32
4
+ */
5
+ const CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l';
6
+ const GENERATOR = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3];
7
+ /**
8
+ * Convert 5-bit groups to base32
9
+ */
10
+ function toChars(data) {
11
+ let result = '';
12
+ for (let i = 0; i < data.length; i++) {
13
+ result += CHARSET.charAt(data[i]);
14
+ }
15
+ return result;
16
+ }
17
+ /**
18
+ * Convert string to 5-bit groups
19
+ */
20
+ function fromChars(str) {
21
+ const result = [];
22
+ for (let i = 0; i < str.length; i++) {
23
+ const index = CHARSET.indexOf(str.charAt(i));
24
+ if (index === -1)
25
+ throw new Error(`Invalid character: ${str.charAt(i)}`);
26
+ result.push(index);
27
+ }
28
+ return result;
29
+ }
30
+ /**
31
+ * Compute checksum
32
+ */
33
+ function polymod(values) {
34
+ let chk = 1;
35
+ for (let p = 0; p < values.length; p++) {
36
+ const top = chk >> 25;
37
+ chk = (chk & 0x1ffffff) << 5 ^ values[p];
38
+ for (let i = 0; i < 5; i++) {
39
+ if ((top >> i) & 1) {
40
+ chk ^= GENERATOR[i];
41
+ }
42
+ }
43
+ }
44
+ return chk;
45
+ }
46
+ /**
47
+ * Expand human-readable part
48
+ */
49
+ function expandHRP(hrp) {
50
+ const result = [];
51
+ for (let i = 0; i < hrp.length; i++) {
52
+ result.push(hrp.charCodeAt(i) >> 5);
53
+ }
54
+ result.push(0);
55
+ for (let i = 0; i < hrp.length; i++) {
56
+ result.push(hrp.charCodeAt(i) & 31);
57
+ }
58
+ return result;
59
+ }
60
+ /**
61
+ * Verify checksum
62
+ */
63
+ function verifyChecksum(hrp, data) {
64
+ return polymod(expandHRP(hrp).concat(data)) === 1;
65
+ }
66
+ /**
67
+ * Create checksum
68
+ */
69
+ function createChecksum(hrp, data) {
70
+ const values = expandHRP(hrp).concat(data).concat([0, 0, 0, 0, 0, 0]);
71
+ const mod = polymod(values) ^ 1;
72
+ const result = [];
73
+ for (let p = 0; p < 6; p++) {
74
+ result.push((mod >> 5 * (5 - p)) & 31);
75
+ }
76
+ return result;
77
+ }
78
+ /**
79
+ * Convert data to 5-bit groups
80
+ */
81
+ function convertBits(data, frombits, tobits, pad) {
82
+ let acc = 0;
83
+ let bits = 0;
84
+ const result = [];
85
+ const maxv = (1 << tobits) - 1;
86
+ for (let i = 0; i < data.length; i++) {
87
+ const value = data[i];
88
+ if (value < 0 || value >> frombits !== 0) {
89
+ throw new Error('Invalid value');
90
+ }
91
+ acc = (acc << frombits) | value;
92
+ bits += frombits;
93
+ while (bits >= tobits) {
94
+ bits -= tobits;
95
+ result.push((acc >> bits) & maxv);
96
+ }
97
+ }
98
+ if (pad) {
99
+ if (bits > 0) {
100
+ result.push((acc << (tobits - bits)) & maxv);
101
+ }
102
+ }
103
+ else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) {
104
+ throw new Error('Invalid padding');
105
+ }
106
+ return result;
107
+ }
108
+ /**
109
+ * Encode data to bech32
110
+ */
111
+ export function bech32Encode(hrp, data) {
112
+ const combined = data.concat(createChecksum(hrp, data));
113
+ return hrp + '1' + toChars(combined);
114
+ }
115
+ /**
116
+ * Decode bech32 string
117
+ */
118
+ export function bech32Decode(str) {
119
+ if (str.length < 8 || str.length > 90) {
120
+ throw new Error('Invalid length');
121
+ }
122
+ let lower = false;
123
+ let upper = false;
124
+ for (let i = 0; i < str.length; i++) {
125
+ if (str.charCodeAt(i) < 33 || str.charCodeAt(i) > 126) {
126
+ throw new Error('Invalid character');
127
+ }
128
+ if (str.charCodeAt(i) >= 97 && str.charCodeAt(i) <= 122)
129
+ lower = true;
130
+ if (str.charCodeAt(i) >= 65 && str.charCodeAt(i) <= 90)
131
+ upper = true;
132
+ }
133
+ if (lower && upper) {
134
+ throw new Error('Mixed case');
135
+ }
136
+ const pos = str.lastIndexOf('1');
137
+ if (pos < 1 || pos + 7 > str.length) {
138
+ throw new Error('Invalid separator position');
139
+ }
140
+ const hrp = str.substring(0, pos).toLowerCase();
141
+ const data = fromChars(str.substring(pos + 1));
142
+ if (!verifyChecksum(hrp, data)) {
143
+ throw new Error('Invalid checksum');
144
+ }
145
+ return { hrp, data: data.slice(0, -6) };
146
+ }
147
+ /**
148
+ * Encode hex string to bech32
149
+ */
150
+ export function encodeToBech32(hrp, hex) {
151
+ const data = new Uint8Array(hex.match(/.{1,2}/g)?.map(byte => parseInt(byte, 16)) || []);
152
+ const words = convertBits(Array.from(data), 8, 5, true);
153
+ return bech32Encode(hrp, words);
154
+ }
155
+ /**
156
+ * Decode bech32 to hex string
157
+ */
158
+ export function decodeFromBech32(str) {
159
+ const { hrp, data } = bech32Decode(str);
160
+ const bytes = convertBits(data, 5, 8, false);
161
+ const hex = bytes.map(b => b.toString(16).padStart(2, '0')).join('');
162
+ return { prefix: hrp, hex };
163
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @file Crypto handlers for Nostr messages
3
+ * @module crypto/handlers
4
+ */
5
+ import type { NostrWSMessage } from '../types/messages';
6
+ /**
7
+ * Validates a signed message
8
+ * @param message - Message to validate
9
+ * @returns Promise resolving to true if message is valid
10
+ */
11
+ export declare function validateSignedMessage(message: NostrWSMessage): Promise<boolean>;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @file Crypto handlers for Nostr messages
3
+ * @module crypto/handlers
4
+ */
5
+ import { getLogger } from '../utils/logger';
6
+ import { MESSAGE_TYPES } from '../types/messages';
7
+ import { validateEvent, verifySignature } from 'nostr-crypto-utils';
8
+ const logger = getLogger('crypto');
9
+ /**
10
+ * Validates a signed message
11
+ * @param message - Message to validate
12
+ * @returns Promise resolving to true if message is valid
13
+ */
14
+ export async function validateSignedMessage(message) {
15
+ try {
16
+ if (message.type !== MESSAGE_TYPES.EVENT || !message.data) {
17
+ logger.debug('Invalid message format');
18
+ return false;
19
+ }
20
+ const event = message.data;
21
+ if (!validateEvent(event)) {
22
+ logger.debug('Invalid event format');
23
+ return false;
24
+ }
25
+ const isValid = await verifySignature(event);
26
+ if (!isValid) {
27
+ logger.debug('Invalid signature');
28
+ return false;
29
+ }
30
+ return true;
31
+ }
32
+ catch (error) {
33
+ logger.error('Error validating signed message:', error);
34
+ return false;
35
+ }
36
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @file Crypto operations index
3
+ * @module crypto
4
+ */
5
+ export * from './handlers';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @file Crypto operations index
3
+ * @module crypto
4
+ */
5
+ export * from './handlers';
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @file Schnorr signature implementation for Nostr
3
+ * @module crypto/schnorr
4
+ */
5
+ /**
6
+ * Sign a message using Schnorr signature
7
+ */
8
+ export declare function schnorrSign(message: string, privateKey: Uint8Array): Promise<string>;
9
+ /**
10
+ * Verify a Schnorr signature
11
+ */
12
+ export declare function schnorrVerify(signature: string, message: string, publicKey: string): Promise<boolean>;
13
+ /**
14
+ * Get public key from private key
15
+ */
16
+ export declare function getPublicKey(privateKey: string): string;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * @file Schnorr signature implementation for Nostr
3
+ * @module crypto/schnorr
4
+ */
5
+ import { utils } from '@noble/secp256k1';
6
+ import * as secp from '@noble/secp256k1';
7
+ /**
8
+ * Sign a message using Schnorr signature
9
+ */
10
+ export async function schnorrSign(message, privateKey) {
11
+ try {
12
+ // Hash the message
13
+ const messageHash = utils.sha256(Buffer.from(message));
14
+ // Sign using Schnorr
15
+ const signature = await secp.sign(messageHash, privateKey, { canonical: true });
16
+ // Convert to hex
17
+ return utils.bytesToHex(signature);
18
+ }
19
+ catch (error) {
20
+ throw new Error(`Failed to create Schnorr signature: ${error}`);
21
+ }
22
+ }
23
+ /**
24
+ * Verify a Schnorr signature
25
+ */
26
+ export async function schnorrVerify(signature, message, publicKey) {
27
+ try {
28
+ // Convert inputs to bytes
29
+ const signatureBytes = utils.hexToBytes(signature);
30
+ const messageHash = utils.sha256(Buffer.from(message));
31
+ const publicKeyBytes = utils.hexToBytes(publicKey);
32
+ // Verify using Schnorr
33
+ return await secp.verify(signatureBytes, messageHash, publicKeyBytes, { canonical: true });
34
+ }
35
+ catch (error) {
36
+ throw new Error(`Failed to verify Schnorr signature: ${error}`);
37
+ }
38
+ }
39
+ /**
40
+ * Get public key from private key
41
+ */
42
+ export function getPublicKey(privateKey) {
43
+ try {
44
+ const publicKey = secp.getPublicKey(privateKey, true);
45
+ return Buffer.from(publicKey).toString('hex');
46
+ }
47
+ catch (error) {
48
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
49
+ throw new Error(`Failed to get public key: ${errorMessage}`);
50
+ }
51
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @file Metrics endpoint for Nostr WebSocket server
3
+ * @module endpoints/metrics
4
+ */
5
+ export interface MetricsEndpointOptions {
6
+ port?: number;
7
+ host?: string;
8
+ path?: string;
9
+ auth?: {
10
+ username: string;
11
+ password: string;
12
+ };
13
+ }
14
+ export declare class MetricsEndpoint {
15
+ private server;
16
+ private options;
17
+ constructor(options?: MetricsEndpointOptions);
18
+ private checkAuth;
19
+ private handleRequest;
20
+ /**
21
+ * Start the metrics endpoint server
22
+ */
23
+ start(): Promise<void>;
24
+ /**
25
+ * Stop the metrics endpoint server
26
+ */
27
+ stop(): Promise<void>;
28
+ }
29
+ export declare const metricsEndpoint: MetricsEndpoint;
@@ -0,0 +1,101 @@
1
+ /**
2
+ * @file Metrics endpoint for Nostr WebSocket server
3
+ * @module endpoints/metrics
4
+ */
5
+ import { createServer } from 'http';
6
+ import { metricsTracker } from '../utils/metrics';
7
+ import { getLogger } from '../utils/logger';
8
+ const logger = getLogger('MetricsEndpoint');
9
+ export class MetricsEndpoint {
10
+ constructor(options = {}) {
11
+ this.options = {
12
+ port: options.port || 9100,
13
+ host: options.host || 'localhost',
14
+ path: options.path || '/metrics',
15
+ auth: options.auth || { username: '', password: '' }
16
+ };
17
+ this.server = createServer((req, res) => this.handleRequest(req, res));
18
+ }
19
+ checkAuth(req) {
20
+ if (!this.options.auth.username)
21
+ return true;
22
+ const authHeader = req.headers.authorization || '';
23
+ const [type, credentials] = authHeader.split(' ');
24
+ if (type !== 'Basic')
25
+ return false;
26
+ const [username, password] = Buffer.from(credentials, 'base64')
27
+ .toString()
28
+ .split(':');
29
+ return username === this.options.auth.username &&
30
+ password === this.options.auth.password;
31
+ }
32
+ async handleRequest(req, res) {
33
+ try {
34
+ // Only handle GET requests to metrics path
35
+ if (req.method !== 'GET' || req.url !== this.options.path) {
36
+ res.writeHead(404);
37
+ res.end('Not Found');
38
+ return;
39
+ }
40
+ // Check authentication if configured
41
+ if (!this.checkAuth(req)) {
42
+ res.writeHead(401, {
43
+ 'WWW-Authenticate': 'Basic realm="Nostr Metrics"'
44
+ });
45
+ res.end('Unauthorized');
46
+ return;
47
+ }
48
+ // Get metrics in Prometheus format
49
+ const metrics = metricsTracker.getPrometheusMetrics();
50
+ // Send response
51
+ res.writeHead(200, {
52
+ 'Content-Type': 'text/plain; version=0.0.4',
53
+ 'Cache-Control': 'no-cache'
54
+ });
55
+ res.end(metrics);
56
+ logger.info({
57
+ method: req.method,
58
+ path: req.url,
59
+ status: 200,
60
+ metrics: metrics.split('\n').length
61
+ }, 'Metrics request served');
62
+ }
63
+ catch (error) {
64
+ logger.error({ error }, 'Error serving metrics');
65
+ res.writeHead(500);
66
+ res.end('Internal Server Error');
67
+ }
68
+ }
69
+ /**
70
+ * Start the metrics endpoint server
71
+ */
72
+ start() {
73
+ return new Promise((resolve, reject) => {
74
+ this.server.listen(this.options.port, this.options.host, () => {
75
+ logger.info({
76
+ host: this.options.host,
77
+ port: this.options.port,
78
+ path: this.options.path
79
+ }, 'Metrics endpoint started');
80
+ resolve();
81
+ }).on('error', reject);
82
+ });
83
+ }
84
+ /**
85
+ * Stop the metrics endpoint server
86
+ */
87
+ stop() {
88
+ return new Promise((resolve, reject) => {
89
+ this.server.close((err) => {
90
+ if (err)
91
+ reject(err);
92
+ else {
93
+ logger.info('Metrics endpoint stopped');
94
+ resolve();
95
+ }
96
+ });
97
+ });
98
+ }
99
+ }
100
+ // Export singleton instance with default configuration
101
+ export const metricsEndpoint = new MetricsEndpoint();
package/dist/index.d.ts CHANGED
@@ -1,7 +1,12 @@
1
- export { NostrWSClient } from './client.js';
2
- export { NostrWSServer } from './server.js';
3
- export { NostrWSServer as NostrServer } from './nostr-server.js';
4
- export { createWSServer as createServer } from './nostr-server.js';
1
+ /**
2
+ * @file Main entry point for the nostr-websocket-utils library
3
+ * @module nostr-websocket-utils
4
+ */
5
+ export { NostrWSClient } from './core/client.js';
6
+ export { NostrWSServer } from './core/server.js';
7
+ export { NostrWSServer as NostrServer } from './core/nostr-server.js';
8
+ export { createWSServer as createServer } from './core/nostr-server.js';
5
9
  export { getLogger } from './utils/logger.js';
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';
10
+ export * from './crypto';
11
+ export * from './nips';
12
+ export * from './types';
package/dist/index.js CHANGED
@@ -1,5 +1,17 @@
1
- export { NostrWSClient } from './client.js';
2
- export { NostrWSServer } from './server.js';
3
- export { NostrWSServer as NostrServer } from './nostr-server.js';
4
- export { createWSServer as createServer } from './nostr-server.js';
1
+ /**
2
+ * @file Main entry point for the nostr-websocket-utils library
3
+ * @module nostr-websocket-utils
4
+ */
5
+ // Core functionality
6
+ export { NostrWSClient } from './core/client.js';
7
+ export { NostrWSServer } from './core/server.js';
8
+ export { NostrWSServer as NostrServer } from './core/nostr-server.js';
9
+ export { createWSServer as createServer } from './core/nostr-server.js';
10
+ // Utilities
5
11
  export { getLogger } from './utils/logger.js';
12
+ // Crypto operations
13
+ export * from './crypto';
14
+ // NIP implementations
15
+ export * from './nips';
16
+ // Type definitions
17
+ export * from './types';
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @file NIPs implementation index
3
+ * @module nips
4
+ */
5
+ export * from './nip-01';
6
+ export * from './nip-02';
7
+ export * from './nip-04';
8
+ export * from './nip-05';
9
+ export * from './nip-09';
10
+ export * from './nip-19';
11
+ export * from './nip-26';
12
+ export * from './nip-11';
13
+ export * from './nip-20';
14
+ export * from './nip-13';
15
+ export * from './nip-16';
16
+ export * from './nip-15';
17
+ export * from './nip-22';
18
+ export * from './nip-28';
19
+ export * from './nip-33';
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @file NIPs implementation index
3
+ * @module nips
4
+ */
5
+ // Basic protocol flow
6
+ export * from './nip-01';
7
+ // Contact List and Petnames
8
+ export * from './nip-02';
9
+ // Encrypted Direct Messages
10
+ export * from './nip-04';
11
+ // DNS Identity Verification
12
+ export * from './nip-05';
13
+ // Event Deletion
14
+ export * from './nip-09';
15
+ // bech32-encoded entities
16
+ export * from './nip-19';
17
+ // Delegated Event Signing
18
+ export * from './nip-26';
19
+ // Relay Information Document
20
+ export * from './nip-11';
21
+ // Command Results
22
+ export * from './nip-20';
23
+ // Proof of Work
24
+ export * from './nip-13';
25
+ // Event Treatment
26
+ export * from './nip-16';
27
+ // End of Stored Events Notice
28
+ export * from './nip-15';
29
+ // Event Created At Limits
30
+ export * from './nip-22';
31
+ // Public Chat
32
+ export * from './nip-28';
33
+ // Parameterized Replaceable Events
34
+ export * from './nip-33';