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.
- package/LICENSE +1 -1
- package/README.md +151 -103
- package/dist/__mocks__/extendedWsMock.d.ts +35 -0
- package/dist/__mocks__/extendedWsMock.js +156 -0
- package/dist/__mocks__/logger.d.ts +9 -0
- package/dist/__mocks__/logger.js +6 -0
- package/dist/__mocks__/mockLogger.d.ts +41 -0
- package/dist/__mocks__/mockLogger.js +47 -0
- package/dist/__mocks__/mockserver.d.ts +31 -0
- package/dist/__mocks__/mockserver.js +39 -0
- package/dist/__mocks__/wsMock.d.ts +26 -0
- package/dist/__mocks__/wsMock.js +120 -0
- package/dist/client.d.ts +105 -0
- package/dist/client.js +105 -0
- package/dist/core/client.d.ts +94 -0
- package/dist/core/client.js +360 -0
- package/dist/core/nostr-server.d.ts +27 -0
- package/dist/core/nostr-server.js +95 -0
- package/dist/core/queue.d.ts +61 -0
- package/dist/core/queue.js +108 -0
- package/dist/core/server.d.ts +27 -0
- package/dist/core/server.js +114 -0
- package/dist/crypto/bech32.d.ts +26 -0
- package/dist/crypto/bech32.js +163 -0
- package/dist/crypto/handlers.d.ts +11 -0
- package/dist/crypto/handlers.js +36 -0
- package/dist/crypto/index.d.ts +5 -0
- package/dist/crypto/index.js +5 -0
- package/dist/crypto/schnorr.d.ts +16 -0
- package/dist/crypto/schnorr.js +51 -0
- package/dist/endpoints/metrics.d.ts +29 -0
- package/dist/endpoints/metrics.js +101 -0
- package/dist/index.d.ts +11 -6
- package/dist/index.js +16 -4
- package/dist/nips/index.d.ts +19 -0
- package/dist/nips/index.js +34 -0
- package/dist/nips/nip-01.d.ts +34 -0
- package/dist/nips/nip-01.js +145 -0
- package/dist/nips/nip-02.d.ts +83 -0
- package/dist/nips/nip-02.js +123 -0
- package/dist/nips/nip-04.d.ts +36 -0
- package/dist/nips/nip-04.js +105 -0
- package/dist/nips/nip-05.d.ts +86 -0
- package/dist/nips/nip-05.js +151 -0
- package/dist/nips/nip-09.d.ts +92 -0
- package/dist/nips/nip-09.js +190 -0
- package/dist/nips/nip-11.d.ts +64 -0
- package/dist/nips/nip-11.js +154 -0
- package/dist/nips/nip-13.d.ts +73 -0
- package/dist/nips/nip-13.js +128 -0
- package/dist/nips/nip-15.d.ts +83 -0
- package/dist/nips/nip-15.js +101 -0
- package/dist/nips/nip-16.d.ts +88 -0
- package/dist/nips/nip-16.js +150 -0
- package/dist/nips/nip-19.d.ts +28 -0
- package/dist/nips/nip-19.js +103 -0
- package/dist/nips/nip-20.d.ts +59 -0
- package/dist/nips/nip-20.js +95 -0
- package/dist/nips/nip-22.d.ts +89 -0
- package/dist/nips/nip-22.js +142 -0
- package/dist/nips/nip-26.d.ts +52 -0
- package/dist/nips/nip-26.js +139 -0
- package/dist/nips/nip-28.d.ts +103 -0
- package/dist/nips/nip-28.js +170 -0
- package/dist/nips/nip-33.d.ts +94 -0
- package/dist/nips/nip-33.js +133 -0
- package/dist/nostr-server.d.ts +23 -0
- package/dist/nostr-server.js +44 -0
- package/dist/server.d.ts +13 -3
- package/dist/server.js +60 -33
- package/dist/transport/base.d.ts +54 -0
- package/dist/transport/base.js +104 -0
- package/dist/transport/websocket.d.ts +22 -0
- package/dist/transport/websocket.js +122 -0
- package/dist/types/events.d.ts +63 -0
- package/dist/types/events.js +5 -0
- package/dist/types/filters.d.ts +19 -0
- package/dist/types/filters.js +5 -0
- package/dist/types/handlers.d.ts +80 -0
- package/dist/types/handlers.js +5 -0
- package/dist/types/index.d.ts +118 -39
- package/dist/types/index.js +21 -1
- package/dist/types/logger.d.ts +40 -0
- package/dist/types/logger.js +5 -0
- package/dist/types/messages.d.ts +135 -0
- package/dist/types/messages.js +40 -0
- package/dist/types/nostr.d.ts +120 -39
- package/dist/types/nostr.js +5 -10
- package/dist/types/options.d.ts +154 -0
- package/dist/types/options.js +5 -0
- package/dist/types/relays.d.ts +26 -0
- package/dist/types/relays.js +5 -0
- package/dist/types/scoring.d.ts +47 -0
- package/dist/types/scoring.js +29 -0
- package/dist/types/socket.d.ts +99 -0
- package/dist/types/socket.js +5 -0
- package/dist/types/transport.d.ts +97 -0
- package/dist/types/transport.js +5 -0
- package/dist/types/validation.d.ts +50 -0
- package/dist/types/validation.js +5 -0
- package/dist/types/websocket.d.ts +172 -0
- package/dist/types/websocket.js +5 -0
- package/dist/utils/http.d.ts +10 -0
- package/dist/utils/http.js +24 -0
- package/dist/utils/logger.d.ts +11 -2
- package/dist/utils/logger.js +18 -13
- package/dist/utils/metrics.d.ts +81 -0
- package/dist/utils/metrics.js +206 -0
- package/dist/utils/rate-limiter.d.ts +85 -0
- package/dist/utils/rate-limiter.js +175 -0
- package/package.json +18 -21
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,25 +1,77 @@
|
|
|
1
|
-
#
|
|
1
|
+
# nostr-websocket-utils
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/nostr-websocket-utils)
|
|
4
|
-
[](https://github.com/HumanjavaEnterprises/nostr-websocket-utils/blob/main/LICENSE)
|
|
3
|
+
[](https://www.npmjs.com/package/@humanjavaenterprises/nostr-websocket-utils)
|
|
4
|
+
[](https://github.com/HumanjavaEnterprises/nostr-websocket-utils/blob/main/LICENSE)
|
|
5
5
|
[](https://github.com/HumanjavaEnterprises/nostr-websocket-utils/actions)
|
|
6
|
+
[](https://humanjavaenterprises.github.io/nostr-websocket-utils/)
|
|
6
7
|
[](https://www.typescriptlang.org)
|
|
7
8
|
[](https://github.com/prettier/prettier)
|
|
8
9
|
|
|
9
|
-
A TypeScript library
|
|
10
|
+
A TypeScript library for building Nostr protocol WebSocket clients and servers.
|
|
10
11
|
|
|
11
12
|
## Features
|
|
12
13
|
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
|
21
|
+
|
|
22
|
+
## NIPs Support Status
|
|
23
|
+
|
|
24
|
+
🟢 Fully implemented 🟡 Partially implemented 🔴 Not implemented
|
|
25
|
+
|
|
26
|
+
| NIP | Status | Description |
|
|
27
|
+
|-----|--------|-------------|
|
|
28
|
+
| 01 | 🟢 | Basic protocol flow & WebSocket connections |
|
|
29
|
+
| 02 | 🟢 | Contact List and Petnames |
|
|
30
|
+
| 11 | 🟢 | Relay Information Document |
|
|
31
|
+
| 15 | 🟢 | End of Stored Events Notice |
|
|
32
|
+
| 16 | 🟢 | Event Treatment |
|
|
33
|
+
| 20 | 🟢 | Command Results |
|
|
34
|
+
| 42 | 🟢 | Authentication of clients to relays |
|
|
35
|
+
|
|
36
|
+
### WebSocket Protocol Implementation Details
|
|
37
|
+
|
|
38
|
+
This package implements the Nostr WebSocket protocol with full support for the core NIPs that define WebSocket behavior. Here's how it works:
|
|
39
|
+
|
|
40
|
+
#### Key Features & Compliance
|
|
41
|
+
|
|
42
|
+
1. **Protocol Implementation**:
|
|
43
|
+
- Full implementation of Nostr WebSocket protocol
|
|
44
|
+
- Support for all standard message types (EVENT, REQ, CLOSE, etc.)
|
|
45
|
+
- Robust error handling and status reporting
|
|
46
|
+
|
|
47
|
+
2. **Connection Management**:
|
|
48
|
+
- Automatic reconnection with configurable backoff
|
|
49
|
+
- Heartbeat mechanism for connection health
|
|
50
|
+
- Connection pooling and load balancing
|
|
51
|
+
|
|
52
|
+
3. **Message Handling**:
|
|
53
|
+
- Type-safe message processing
|
|
54
|
+
- Support for subscription management
|
|
55
|
+
- Efficient event filtering
|
|
56
|
+
|
|
57
|
+
4. **Security & Best Practices**:
|
|
58
|
+
- Secure WebSocket connections (WSS)
|
|
59
|
+
- Implementation of authentication protocols
|
|
60
|
+
- Rate limiting and protection mechanisms
|
|
61
|
+
|
|
62
|
+
#### Interoperability
|
|
63
|
+
|
|
64
|
+
This implementation ensures compatibility with:
|
|
65
|
+
- All major Nostr relays
|
|
66
|
+
- Other Nostr clients and libraries
|
|
67
|
+
- Standard WebSocket tooling and infrastructure
|
|
68
|
+
|
|
69
|
+
#### Validation & Testing
|
|
70
|
+
|
|
71
|
+
The package includes:
|
|
72
|
+
- Comprehensive test suites for protocol compliance
|
|
73
|
+
- Connection reliability testing
|
|
74
|
+
- Performance benchmarks for message handling
|
|
23
75
|
|
|
24
76
|
## Installation
|
|
25
77
|
|
|
@@ -27,118 +79,114 @@ A TypeScript library providing WebSocket utilities for Nostr applications, focus
|
|
|
27
79
|
npm install nostr-websocket-utils
|
|
28
80
|
```
|
|
29
81
|
|
|
30
|
-
##
|
|
82
|
+
## Quick Start
|
|
83
|
+
|
|
84
|
+
### Creating a Nostr WebSocket Client
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import { NostrWSClient } from 'nostr-websocket-utils';
|
|
31
88
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
89
|
+
const client = new NostrWSClient('wss://relay.example.com', {
|
|
90
|
+
logger: console,
|
|
91
|
+
heartbeatInterval: 30000,
|
|
92
|
+
handlers: {
|
|
93
|
+
message: async (msg) => console.log('Received:', msg),
|
|
94
|
+
error: (err) => console.error('Error:', err),
|
|
95
|
+
close: () => console.log('Connection closed')
|
|
96
|
+
}
|
|
97
|
+
});
|
|
38
98
|
|
|
39
|
-
|
|
99
|
+
await client.connect();
|
|
100
|
+
```
|
|
40
101
|
|
|
41
|
-
### Nostr Server
|
|
102
|
+
### Creating a Nostr WebSocket Server
|
|
42
103
|
|
|
43
104
|
```typescript
|
|
44
|
-
import {
|
|
45
|
-
import { NostrWSMessageType, NostrWSEvent } from 'nostr-websocket-utils/types/nostr';
|
|
105
|
+
import { createNostrServer } from '@humanjavaenterprises/nostr-websocket-utils';
|
|
46
106
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
heartbeatInterval: 30000, // Optional: 30 seconds
|
|
107
|
+
const server = await createNostrServer(8080, {
|
|
108
|
+
logger: console,
|
|
109
|
+
heartbeatInterval: 30000,
|
|
51
110
|
handlers: {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
case NostrWSMessageType.EVENT:
|
|
56
|
-
const event = message[1] as NostrWSEvent;
|
|
57
|
-
// Handle Nostr event
|
|
58
|
-
break;
|
|
59
|
-
case NostrWSMessageType.REQ:
|
|
60
|
-
const [_type, subscriptionId, filter] = message;
|
|
61
|
-
// Handle subscription request
|
|
62
|
-
break;
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
// Optional: Handle errors
|
|
66
|
-
error: (socket, error) => {
|
|
67
|
-
console.error('WebSocket error:', error);
|
|
68
|
-
},
|
|
69
|
-
// Optional: Handle client disconnection
|
|
70
|
-
close: (socket) => {
|
|
71
|
-
console.info('Client disconnected');
|
|
111
|
+
message: async (ws, msg) => {
|
|
112
|
+
console.log('Received message:', msg);
|
|
113
|
+
// Handle the message
|
|
72
114
|
}
|
|
73
115
|
}
|
|
74
116
|
});
|
|
75
|
-
|
|
76
|
-
// Start listening
|
|
77
|
-
server.listen();
|
|
78
117
|
```
|
|
79
118
|
|
|
80
|
-
|
|
119
|
+
## Dependencies
|
|
120
|
+
|
|
121
|
+
This package relies on:
|
|
122
|
+
- [nostr-crypto-utils](https://github.com/HumanjavaEnterprises/nostr-crypto-utils) - For all cryptographic operations
|
|
123
|
+
- [ws](https://github.com/websockets/ws) - For WebSocket functionality
|
|
124
|
+
- [pino](https://github.com/pinojs/pino) - For logging
|
|
125
|
+
- [uuid](https://github.com/uuidjs/uuid) - For unique identifiers
|
|
126
|
+
|
|
127
|
+
## Documentation
|
|
128
|
+
|
|
129
|
+
Comprehensive API documentation is available in our [documentation site](https://humanjavaenterprises.github.io/nostr-websocket-utils/). Here's what you'll find:
|
|
130
|
+
|
|
131
|
+
### Core Components
|
|
132
|
+
- [NostrWSClient](docs/classes/NostrWSClient.md) - WebSocket client implementation
|
|
133
|
+
- [NostrWSServer](docs/classes/NostrWSServer.md) - WebSocket server implementation
|
|
134
|
+
- [NostrServer](docs/classes/NostrServer.md) - High-level Nostr server
|
|
135
|
+
|
|
136
|
+
### Types and Interfaces
|
|
137
|
+
- [NostrWSMessage](docs/interfaces/NostrWSMessage.md) - Message structure
|
|
138
|
+
- [NostrWSOptions](docs/interfaces/NostrWSOptions.md) - Configuration options
|
|
139
|
+
- [NostrWSSubscription](docs/interfaces/NostrWSSubscription.md) - Subscription interface
|
|
140
|
+
- [ExtendedWebSocket](docs/interfaces/ExtendedWebSocket.md) - Enhanced WebSocket interface
|
|
141
|
+
|
|
142
|
+
### Utility Functions
|
|
143
|
+
- [createServer](docs/functions/createServer.md) - Server creation helper
|
|
144
|
+
- [getLogger](docs/functions/getLogger.md) - Logging utility
|
|
145
|
+
|
|
146
|
+
### Type Definitions
|
|
147
|
+
- [MessageType](docs/enumerations/NostrWSMessageType.md) - Message type enumeration
|
|
148
|
+
- [Global Types](docs/globals.md) - Global type definitions
|
|
149
|
+
|
|
150
|
+
## Examples
|
|
151
|
+
|
|
152
|
+
### Subscribe to a Channel
|
|
81
153
|
|
|
82
154
|
```typescript
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
tags: string[][];
|
|
90
|
-
content: string;
|
|
91
|
-
sig: string;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Nostr Filter Type
|
|
95
|
-
interface NostrWSFilter {
|
|
96
|
-
ids?: string[];
|
|
97
|
-
authors?: string[];
|
|
98
|
-
kinds?: number[];
|
|
99
|
-
'#e'?: string[];
|
|
100
|
-
'#p'?: string[];
|
|
101
|
-
since?: number;
|
|
102
|
-
until?: number;
|
|
103
|
-
limit?: number;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Message Types
|
|
107
|
-
enum NostrWSMessageType {
|
|
108
|
-
EVENT = 'EVENT',
|
|
109
|
-
REQ = 'REQ',
|
|
110
|
-
CLOSE = 'CLOSE',
|
|
111
|
-
NOTICE = 'NOTICE',
|
|
112
|
-
AUTH = 'AUTH',
|
|
113
|
-
EOSE = 'EOSE'
|
|
114
|
-
}
|
|
155
|
+
client.subscribe('my-channel', {
|
|
156
|
+
filter: {
|
|
157
|
+
authors: ['pubkey1', 'pubkey2'],
|
|
158
|
+
kinds: [1]
|
|
159
|
+
}
|
|
160
|
+
});
|
|
115
161
|
```
|
|
116
162
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
### Server Options
|
|
163
|
+
### Broadcast a Message
|
|
120
164
|
|
|
121
165
|
```typescript
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
};
|
|
130
|
-
handlers: {
|
|
131
|
-
message: MessageHandler;
|
|
132
|
-
error?: ErrorHandler;
|
|
133
|
-
close?: CloseHandler;
|
|
134
|
-
};
|
|
135
|
-
}
|
|
166
|
+
server.broadcast({
|
|
167
|
+
type: 'event',
|
|
168
|
+
data: {
|
|
169
|
+
content: 'Hello everyone!',
|
|
170
|
+
kind: 1
|
|
171
|
+
}
|
|
172
|
+
});
|
|
136
173
|
```
|
|
137
174
|
|
|
138
175
|
## Contributing
|
|
139
176
|
|
|
140
|
-
Contributions are welcome! Please
|
|
177
|
+
Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
|
|
141
178
|
|
|
142
179
|
## License
|
|
143
180
|
|
|
144
181
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
182
|
+
|
|
183
|
+
## Related Projects
|
|
184
|
+
|
|
185
|
+
- [nostr-protocol](https://github.com/nostr-protocol/nostr)
|
|
186
|
+
|
|
187
|
+
## Support
|
|
188
|
+
|
|
189
|
+
If you have any questions or need help, please:
|
|
190
|
+
|
|
191
|
+
1. Check the [documentation](https://humanjavaenterprises.github.io/nostr-websocket-utils/)
|
|
192
|
+
2. Open an [issue](https://github.com/HumanjavaEnterprises/nostr-websocket-utils/issues)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
/// <reference types="node" />
|
|
4
|
+
import { EventEmitter } from 'events';
|
|
5
|
+
export declare class ExtendedWsMock extends EventEmitter {
|
|
6
|
+
readonly CONNECTING: 0;
|
|
7
|
+
readonly OPEN: 1;
|
|
8
|
+
readonly CLOSING: 2;
|
|
9
|
+
readonly CLOSED: 3;
|
|
10
|
+
readyState: 0 | 1 | 2 | 3;
|
|
11
|
+
protocol: string;
|
|
12
|
+
url: string;
|
|
13
|
+
bufferedAmount: number;
|
|
14
|
+
extensions: string;
|
|
15
|
+
binaryType: 'nodebuffer' | 'arraybuffer' | 'fragments';
|
|
16
|
+
isPaused: boolean;
|
|
17
|
+
onopen: ((event: any) => void) | null;
|
|
18
|
+
onclose: ((event: any) => void) | null;
|
|
19
|
+
onerror: ((event: any) => void) | null;
|
|
20
|
+
onmessage: ((event: any) => void) | null;
|
|
21
|
+
constructor();
|
|
22
|
+
private bindMethods;
|
|
23
|
+
private createWsEvent;
|
|
24
|
+
addEventListener(type: string, listener: (event: any) => void): void;
|
|
25
|
+
removeEventListener(type: string, listener: (event: any) => void): void;
|
|
26
|
+
send: import("@vitest/spy").Mock<[data: any], this>;
|
|
27
|
+
ping: import("@vitest/spy").Mock<[data?: Buffer | Uint8Array | undefined], this>;
|
|
28
|
+
pong: import("@vitest/spy").Mock<[data?: Buffer | Uint8Array | undefined], this>;
|
|
29
|
+
close: import("@vitest/spy").Mock<[code?: number | undefined, reason?: string | undefined], this>;
|
|
30
|
+
terminate: import("@vitest/spy").Mock<[], this>;
|
|
31
|
+
pause(): this;
|
|
32
|
+
resume(): this;
|
|
33
|
+
setMaxListeners(n: number): this;
|
|
34
|
+
}
|
|
35
|
+
export declare const mockWebSocket: ExtendedWsMock;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { vi } from 'vitest';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
export class ExtendedWsMock extends EventEmitter {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
// WebSocket state constants
|
|
7
|
+
this.CONNECTING = 0;
|
|
8
|
+
this.OPEN = 1;
|
|
9
|
+
this.CLOSING = 2;
|
|
10
|
+
this.CLOSED = 3;
|
|
11
|
+
// Required WebSocket properties
|
|
12
|
+
this.readyState = 1;
|
|
13
|
+
this.protocol = '';
|
|
14
|
+
this.url = 'ws://test.com';
|
|
15
|
+
this.bufferedAmount = 0;
|
|
16
|
+
this.extensions = '';
|
|
17
|
+
this.binaryType = 'nodebuffer';
|
|
18
|
+
this.isPaused = false;
|
|
19
|
+
// Event handlers
|
|
20
|
+
this.onopen = null;
|
|
21
|
+
this.onclose = null;
|
|
22
|
+
this.onerror = null;
|
|
23
|
+
this.onmessage = null;
|
|
24
|
+
this.send = vi.fn((data) => {
|
|
25
|
+
if (this.isPaused)
|
|
26
|
+
return this;
|
|
27
|
+
const isBinary = !(typeof data === 'string');
|
|
28
|
+
const event = this.createWsEvent('message', {
|
|
29
|
+
data: data
|
|
30
|
+
});
|
|
31
|
+
this.emit('message', data, isBinary);
|
|
32
|
+
this.onmessage?.apply(this, [event]);
|
|
33
|
+
return this;
|
|
34
|
+
});
|
|
35
|
+
this.ping = vi.fn((data) => {
|
|
36
|
+
if (!this.isPaused) {
|
|
37
|
+
this.emit('ping', data);
|
|
38
|
+
}
|
|
39
|
+
return this;
|
|
40
|
+
});
|
|
41
|
+
this.pong = vi.fn((data) => {
|
|
42
|
+
if (!this.isPaused) {
|
|
43
|
+
this.emit('pong', data);
|
|
44
|
+
}
|
|
45
|
+
return this;
|
|
46
|
+
});
|
|
47
|
+
this.close = vi.fn((code, reason) => {
|
|
48
|
+
this.readyState = this.CLOSED;
|
|
49
|
+
const event = this.createWsEvent('close', {
|
|
50
|
+
code,
|
|
51
|
+
reason,
|
|
52
|
+
wasClean: true
|
|
53
|
+
});
|
|
54
|
+
this.emit('close', code, Buffer.from(reason || ''));
|
|
55
|
+
this.onclose?.apply(this, [event]);
|
|
56
|
+
return this;
|
|
57
|
+
});
|
|
58
|
+
this.terminate = vi.fn(() => {
|
|
59
|
+
this.readyState = this.CLOSED;
|
|
60
|
+
const event = this.createWsEvent('close', {
|
|
61
|
+
code: 1006,
|
|
62
|
+
reason: 'Connection terminated',
|
|
63
|
+
wasClean: false
|
|
64
|
+
});
|
|
65
|
+
this.emit('close', 1006, Buffer.from('Connection terminated'));
|
|
66
|
+
this.onclose?.apply(this, [event]);
|
|
67
|
+
return this;
|
|
68
|
+
});
|
|
69
|
+
this.bindMethods();
|
|
70
|
+
}
|
|
71
|
+
bindMethods() {
|
|
72
|
+
// Bind all methods that need 'this' context
|
|
73
|
+
this.send = this.send.bind(this);
|
|
74
|
+
this.ping = this.ping.bind(this);
|
|
75
|
+
this.pong = this.pong.bind(this);
|
|
76
|
+
this.close = this.close.bind(this);
|
|
77
|
+
this.terminate = this.terminate.bind(this);
|
|
78
|
+
this.pause = this.pause.bind(this);
|
|
79
|
+
this.resume = this.resume.bind(this);
|
|
80
|
+
}
|
|
81
|
+
createWsEvent(type, data) {
|
|
82
|
+
const event = {
|
|
83
|
+
type,
|
|
84
|
+
target: this,
|
|
85
|
+
currentTarget: this,
|
|
86
|
+
...data
|
|
87
|
+
};
|
|
88
|
+
return event;
|
|
89
|
+
}
|
|
90
|
+
addEventListener(type, listener) {
|
|
91
|
+
const boundListener = ((event) => {
|
|
92
|
+
listener.apply(this, [event]);
|
|
93
|
+
});
|
|
94
|
+
this.on(type, boundListener);
|
|
95
|
+
}
|
|
96
|
+
removeEventListener(type, listener) {
|
|
97
|
+
this.removeListener(type, listener);
|
|
98
|
+
}
|
|
99
|
+
pause() {
|
|
100
|
+
this.isPaused = true;
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
resume() {
|
|
104
|
+
this.isPaused = false;
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
107
|
+
// Additional required methods
|
|
108
|
+
setMaxListeners(n) {
|
|
109
|
+
super.setMaxListeners(n);
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Create mock event classes for Node.js environment
|
|
114
|
+
class MockEvent {
|
|
115
|
+
constructor(type, target) {
|
|
116
|
+
this.type = type;
|
|
117
|
+
this.target = target || mockWebSocket;
|
|
118
|
+
this.currentTarget = this.target;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
class MockMessageEvent extends MockEvent {
|
|
122
|
+
constructor(type, init) {
|
|
123
|
+
super(type, init?.target);
|
|
124
|
+
this.data = init?.data ?? '';
|
|
125
|
+
this.isBinary = init?.isBinary ?? false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
class MockCloseEvent extends MockEvent {
|
|
129
|
+
constructor(type, init) {
|
|
130
|
+
super(type, init?.target);
|
|
131
|
+
this.code = init?.code ?? 1000;
|
|
132
|
+
this.reason = init?.reason ?? '';
|
|
133
|
+
this.wasClean = init?.wasClean ?? true;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
class MockErrorEvent extends MockEvent {
|
|
137
|
+
constructor(type, init) {
|
|
138
|
+
super(type, init?.target);
|
|
139
|
+
this.error = init?.error ?? new Error('WebSocket Error');
|
|
140
|
+
this.message = init?.message ?? 'WebSocket Error';
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (typeof Event === 'undefined') {
|
|
144
|
+
global.Event = MockEvent;
|
|
145
|
+
}
|
|
146
|
+
if (typeof MessageEvent === 'undefined') {
|
|
147
|
+
global.MessageEvent = MockMessageEvent;
|
|
148
|
+
}
|
|
149
|
+
if (typeof CloseEvent === 'undefined') {
|
|
150
|
+
global.CloseEvent = MockCloseEvent;
|
|
151
|
+
}
|
|
152
|
+
if (typeof ErrorEvent === 'undefined') {
|
|
153
|
+
global.ErrorEvent = MockErrorEvent;
|
|
154
|
+
}
|
|
155
|
+
// Export a singleton instance
|
|
156
|
+
export const mockWebSocket = new ExtendedWsMock();
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type LoggerFunction = (message: string, ...args: unknown[]) => void;
|
|
2
|
+
interface MockLogger {
|
|
3
|
+
debug: LoggerFunction;
|
|
4
|
+
info: LoggerFunction;
|
|
5
|
+
warn: LoggerFunction;
|
|
6
|
+
error: LoggerFunction;
|
|
7
|
+
}
|
|
8
|
+
export declare const getLogger: (_name: string) => MockLogger;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Logger, ChildLoggerOptions, Bindings } from 'pino';
|
|
2
|
+
export declare class MockPinoLogger implements Partial<Logger> {
|
|
3
|
+
level: string;
|
|
4
|
+
levelVal: number;
|
|
5
|
+
version: string;
|
|
6
|
+
LOG_VERSION: number;
|
|
7
|
+
useLevelLabels: boolean;
|
|
8
|
+
useOnlyCustomLevels: boolean;
|
|
9
|
+
customLevels: {};
|
|
10
|
+
debug: import("@vitest/spy").Mock<any, any>;
|
|
11
|
+
info: import("@vitest/spy").Mock<any, any>;
|
|
12
|
+
warn: import("@vitest/spy").Mock<any, any>;
|
|
13
|
+
error: import("@vitest/spy").Mock<any, any>;
|
|
14
|
+
fatal: import("@vitest/spy").Mock<any, any>;
|
|
15
|
+
trace: import("@vitest/spy").Mock<any, any>;
|
|
16
|
+
silent: import("@vitest/spy").Mock<any, any>;
|
|
17
|
+
levels: {
|
|
18
|
+
values: {
|
|
19
|
+
fatal: number;
|
|
20
|
+
error: number;
|
|
21
|
+
warn: number;
|
|
22
|
+
info: number;
|
|
23
|
+
debug: number;
|
|
24
|
+
trace: number;
|
|
25
|
+
};
|
|
26
|
+
labels: {
|
|
27
|
+
10: string;
|
|
28
|
+
20: string;
|
|
29
|
+
30: string;
|
|
30
|
+
40: string;
|
|
31
|
+
50: string;
|
|
32
|
+
60: string;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
bindings: () => {};
|
|
36
|
+
flush: () => Promise<void>;
|
|
37
|
+
isLevelEnabled: () => boolean;
|
|
38
|
+
child<ChildCustomLevels extends string = never>(_bindings: Bindings, _options?: ChildLoggerOptions<ChildCustomLevels>): Logger<ChildCustomLevels>;
|
|
39
|
+
static create(): Logger;
|
|
40
|
+
}
|
|
41
|
+
export declare const createMockLogger: () => Logger;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { vi } from 'vitest';
|
|
2
|
+
export class MockPinoLogger {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.level = 'debug';
|
|
5
|
+
this.levelVal = 20;
|
|
6
|
+
this.version = '8.0.0';
|
|
7
|
+
this.LOG_VERSION = 1;
|
|
8
|
+
this.useLevelLabels = true;
|
|
9
|
+
this.useOnlyCustomLevels = false;
|
|
10
|
+
this.customLevels = {};
|
|
11
|
+
this.debug = vi.fn();
|
|
12
|
+
this.info = vi.fn();
|
|
13
|
+
this.warn = vi.fn();
|
|
14
|
+
this.error = vi.fn();
|
|
15
|
+
this.fatal = vi.fn();
|
|
16
|
+
this.trace = vi.fn();
|
|
17
|
+
this.silent = vi.fn();
|
|
18
|
+
this.levels = {
|
|
19
|
+
values: {
|
|
20
|
+
fatal: 60,
|
|
21
|
+
error: 50,
|
|
22
|
+
warn: 40,
|
|
23
|
+
info: 30,
|
|
24
|
+
debug: 20,
|
|
25
|
+
trace: 10
|
|
26
|
+
},
|
|
27
|
+
labels: {
|
|
28
|
+
10: 'trace',
|
|
29
|
+
20: 'debug',
|
|
30
|
+
30: 'info',
|
|
31
|
+
40: 'warn',
|
|
32
|
+
50: 'error',
|
|
33
|
+
60: 'fatal'
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
this.bindings = () => ({});
|
|
37
|
+
this.flush = () => Promise.resolve();
|
|
38
|
+
this.isLevelEnabled = () => true;
|
|
39
|
+
}
|
|
40
|
+
child(_bindings, _options) {
|
|
41
|
+
return new MockPinoLogger();
|
|
42
|
+
}
|
|
43
|
+
static create() {
|
|
44
|
+
return new MockPinoLogger();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export const createMockLogger = () => MockPinoLogger.create();
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
/// <reference types="node" />
|
|
4
|
+
/// <reference types="node" />
|
|
5
|
+
/// <reference types="node" />
|
|
6
|
+
import { EventEmitter } from 'events';
|
|
7
|
+
import type { NostrWSMessage } from '../types/messages';
|
|
8
|
+
import type { NostrWSSocket } from '../types/socket';
|
|
9
|
+
import type { IncomingMessage } from 'http';
|
|
10
|
+
import type { Socket } from 'net';
|
|
11
|
+
import type { WebSocket } from 'ws';
|
|
12
|
+
interface MockServerOptions {
|
|
13
|
+
port?: number;
|
|
14
|
+
host?: string;
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
}
|
|
17
|
+
export declare class MockServer extends EventEmitter {
|
|
18
|
+
clients: Set<NostrWSSocket>;
|
|
19
|
+
options: MockServerOptions;
|
|
20
|
+
path: string;
|
|
21
|
+
address: string;
|
|
22
|
+
constructor(options?: MockServerOptions);
|
|
23
|
+
listen(port: number, cb?: () => void): void;
|
|
24
|
+
handleConnection: import("@vitest/spy").Mock<[socket: NostrWSSocket], void>;
|
|
25
|
+
handleMessage: import("@vitest/spy").Mock<[socket: NostrWSSocket, message: NostrWSMessage], void>;
|
|
26
|
+
handleClose: import("@vitest/spy").Mock<[socket: NostrWSSocket], void>;
|
|
27
|
+
broadcast: import("@vitest/spy").Mock<[message: NostrWSMessage], void>;
|
|
28
|
+
handleUpgrade: import("@vitest/spy").Mock<[request: IncomingMessage, socket: Socket, head: Buffer, callback: (ws: WebSocket) => void], void>;
|
|
29
|
+
shouldHandle: import("@vitest/spy").Mock<[_request: IncomingMessage], boolean>;
|
|
30
|
+
}
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { vi } from 'vitest';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import { ExtendedWsMock } from './extendedWsMock';
|
|
4
|
+
export class MockServer extends EventEmitter {
|
|
5
|
+
constructor(options) {
|
|
6
|
+
super();
|
|
7
|
+
this.clients = new Set();
|
|
8
|
+
this.handleConnection = vi.fn((socket) => {
|
|
9
|
+
this.clients.add(socket);
|
|
10
|
+
this.emit('connection', socket);
|
|
11
|
+
});
|
|
12
|
+
this.handleMessage = vi.fn((socket, message) => {
|
|
13
|
+
this.emit('message', socket, message);
|
|
14
|
+
});
|
|
15
|
+
this.handleClose = vi.fn((socket) => {
|
|
16
|
+
this.clients.delete(socket);
|
|
17
|
+
this.emit('close', socket);
|
|
18
|
+
});
|
|
19
|
+
this.broadcast = vi.fn((message) => {
|
|
20
|
+
this.clients.forEach(client => {
|
|
21
|
+
client.send(JSON.stringify(message));
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
this.handleUpgrade = vi.fn((request, socket, head, callback) => {
|
|
25
|
+
const ws = new ExtendedWsMock();
|
|
26
|
+
callback(ws);
|
|
27
|
+
});
|
|
28
|
+
this.shouldHandle = vi.fn((_request) => {
|
|
29
|
+
return true;
|
|
30
|
+
});
|
|
31
|
+
this.options = options || {};
|
|
32
|
+
this.path = '/';
|
|
33
|
+
this.address = 'localhost';
|
|
34
|
+
}
|
|
35
|
+
listen(port, cb) {
|
|
36
|
+
if (cb)
|
|
37
|
+
cb();
|
|
38
|
+
}
|
|
39
|
+
}
|