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
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file NIP-01: Basic protocol flow implementation
|
|
3
|
+
* @module nips/nip-01
|
|
4
|
+
*/
|
|
5
|
+
import type { NostrWSMessage } from '../types/messages';
|
|
6
|
+
interface NostrEventValidationResult {
|
|
7
|
+
valid: boolean;
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Validates a message according to NIP-01 specifications
|
|
12
|
+
*/
|
|
13
|
+
export declare function validateMessage(message: NostrWSMessage): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Creates an EVENT message
|
|
16
|
+
*/
|
|
17
|
+
export declare function createEventMessage(event: Record<string, unknown>): NostrWSMessage;
|
|
18
|
+
/**
|
|
19
|
+
* Creates a REQ message
|
|
20
|
+
*/
|
|
21
|
+
export declare function createReqMessage(subscriptionId: string, filters: Array<Record<string, unknown>>): NostrWSMessage;
|
|
22
|
+
/**
|
|
23
|
+
* Creates a CLOSE message
|
|
24
|
+
*/
|
|
25
|
+
export declare function createCloseMessage(subscriptionId: string): NostrWSMessage;
|
|
26
|
+
/**
|
|
27
|
+
* Creates a NOTICE message
|
|
28
|
+
*/
|
|
29
|
+
export declare function createNoticeMessage(message: string): NostrWSMessage;
|
|
30
|
+
/**
|
|
31
|
+
* Validates a Nostr event according to NIP-01
|
|
32
|
+
*/
|
|
33
|
+
export declare function validateEvent(event: unknown): NostrEventValidationResult;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file NIP-01: Basic protocol flow implementation
|
|
3
|
+
* @module nips/nip-01
|
|
4
|
+
*/
|
|
5
|
+
import { getLogger } from '../utils/logger';
|
|
6
|
+
import { MESSAGE_TYPES } from '../types/messages';
|
|
7
|
+
const logger = getLogger('NIP-01');
|
|
8
|
+
/**
|
|
9
|
+
* Validates a message according to NIP-01 specifications
|
|
10
|
+
*/
|
|
11
|
+
export function validateMessage(message) {
|
|
12
|
+
if (!message || typeof message !== 'object') {
|
|
13
|
+
logger.debug('Invalid message format');
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
if (!message.type || !Object.values(MESSAGE_TYPES).includes(message.type)) {
|
|
17
|
+
logger.debug(`Invalid message type: ${message.type}`);
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
if (!message.data || typeof message.data !== 'object') {
|
|
21
|
+
logger.debug('Invalid message data');
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
// Message type specific validation
|
|
25
|
+
if (message.type === MESSAGE_TYPES.EVENT) {
|
|
26
|
+
if (!validateEvent(message.data).valid) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (message.type === MESSAGE_TYPES.REQ) {
|
|
31
|
+
if (!validateReqMessage(message)) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (message.type === MESSAGE_TYPES.CLOSE) {
|
|
36
|
+
if (!validateCloseMessage(message)) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Creates an EVENT message
|
|
44
|
+
*/
|
|
45
|
+
export function createEventMessage(event) {
|
|
46
|
+
return {
|
|
47
|
+
type: MESSAGE_TYPES.EVENT,
|
|
48
|
+
data: event
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Creates a REQ message
|
|
53
|
+
*/
|
|
54
|
+
export function createReqMessage(subscriptionId, filters) {
|
|
55
|
+
return {
|
|
56
|
+
type: MESSAGE_TYPES.REQ,
|
|
57
|
+
data: {
|
|
58
|
+
subscription_id: subscriptionId,
|
|
59
|
+
filters
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Creates a CLOSE message
|
|
65
|
+
*/
|
|
66
|
+
export function createCloseMessage(subscriptionId) {
|
|
67
|
+
return {
|
|
68
|
+
type: MESSAGE_TYPES.CLOSE,
|
|
69
|
+
data: {
|
|
70
|
+
subscription_id: subscriptionId
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Creates a NOTICE message
|
|
76
|
+
*/
|
|
77
|
+
export function createNoticeMessage(message) {
|
|
78
|
+
return {
|
|
79
|
+
type: MESSAGE_TYPES.NOTICE,
|
|
80
|
+
data: {
|
|
81
|
+
message
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Validates a Nostr event according to NIP-01
|
|
87
|
+
*/
|
|
88
|
+
export function validateEvent(event) {
|
|
89
|
+
if (typeof event !== 'object' || !event) {
|
|
90
|
+
return { valid: false, error: 'Event must be an object' };
|
|
91
|
+
}
|
|
92
|
+
const typedEvent = event;
|
|
93
|
+
if (!typedEvent.id || typeof typedEvent.id !== 'string') {
|
|
94
|
+
return { valid: false, error: 'Event must have a string id' };
|
|
95
|
+
}
|
|
96
|
+
if (!typedEvent.pubkey || typeof typedEvent.pubkey !== 'string') {
|
|
97
|
+
return { valid: false, error: 'Event must have a string pubkey' };
|
|
98
|
+
}
|
|
99
|
+
if (!typedEvent.created_at || typeof typedEvent.created_at !== 'number') {
|
|
100
|
+
return { valid: false, error: 'Event must have a number created_at' };
|
|
101
|
+
}
|
|
102
|
+
if (!typedEvent.kind || typeof typedEvent.kind !== 'number') {
|
|
103
|
+
return { valid: false, error: 'Event must have a number kind' };
|
|
104
|
+
}
|
|
105
|
+
if (!Array.isArray(typedEvent.tags)) {
|
|
106
|
+
return { valid: false, error: 'Event must have an array of tags' };
|
|
107
|
+
}
|
|
108
|
+
if (!typedEvent.content || typeof typedEvent.content !== 'string') {
|
|
109
|
+
return { valid: false, error: 'Event must have a string content' };
|
|
110
|
+
}
|
|
111
|
+
if (!typedEvent.sig || typeof typedEvent.sig !== 'string') {
|
|
112
|
+
return { valid: false, error: 'Event must have a string sig' };
|
|
113
|
+
}
|
|
114
|
+
// Validate tag structure
|
|
115
|
+
for (const tag of typedEvent.tags) {
|
|
116
|
+
if (!Array.isArray(tag) || tag.length === 0) {
|
|
117
|
+
return { valid: false, error: 'Each tag must be a non-empty array' };
|
|
118
|
+
}
|
|
119
|
+
if (typeof tag[0] !== 'string') {
|
|
120
|
+
return { valid: false, error: 'Tag identifier must be a string' };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return { valid: true };
|
|
124
|
+
}
|
|
125
|
+
// Private helper functions
|
|
126
|
+
function validateReqMessage(message) {
|
|
127
|
+
const { subscription_id, filters } = message.data;
|
|
128
|
+
if (!subscription_id || typeof subscription_id !== 'string') {
|
|
129
|
+
logger.debug('Invalid subscription_id');
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
if (!Array.isArray(filters)) {
|
|
133
|
+
logger.debug('Invalid filters format');
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
function validateCloseMessage(message) {
|
|
139
|
+
const { subscription_id } = message.data;
|
|
140
|
+
if (!subscription_id || typeof subscription_id !== 'string') {
|
|
141
|
+
logger.debug('Invalid subscription_id');
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file NIP-02: Contact List and Petnames
|
|
3
|
+
* @module nips/nip-02
|
|
4
|
+
* @see https://github.com/nostr-protocol/nips/blob/master/02.md
|
|
5
|
+
*/
|
|
6
|
+
import type { NostrWSMessage } from '../types/messages';
|
|
7
|
+
import type { Logger } from '../types/logger';
|
|
8
|
+
/**
|
|
9
|
+
* Contact list event kind
|
|
10
|
+
*/
|
|
11
|
+
export declare const CONTACT_LIST_KIND = 3;
|
|
12
|
+
/**
|
|
13
|
+
* Contact entry structure
|
|
14
|
+
*/
|
|
15
|
+
export interface Contact {
|
|
16
|
+
pubkey: string;
|
|
17
|
+
relay?: string;
|
|
18
|
+
petname?: string;
|
|
19
|
+
metadata?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Creates a contact list event
|
|
23
|
+
* @param contacts - List of contacts
|
|
24
|
+
* @param metadata - Optional metadata for the contact list
|
|
25
|
+
* @returns {NostrWSMessage} Contact list event
|
|
26
|
+
*/
|
|
27
|
+
export declare function createContactListEvent(contacts: Contact[], metadata?: Record<string, unknown>): NostrWSMessage;
|
|
28
|
+
/**
|
|
29
|
+
* Extracts contacts from a contact list event
|
|
30
|
+
* @param message - Contact list message
|
|
31
|
+
* @param _logger - Logger instance
|
|
32
|
+
* @returns {Contact[]} Array of contacts
|
|
33
|
+
*/
|
|
34
|
+
export declare function extractContacts(message: NostrWSMessage, _logger: Logger): Contact[];
|
|
35
|
+
/**
|
|
36
|
+
* Creates a contact list subscription message
|
|
37
|
+
* @param pubkey - Public key to subscribe to
|
|
38
|
+
* @returns {NostrWSMessage} Subscription message
|
|
39
|
+
*/
|
|
40
|
+
export declare function createContactListSubscription(pubkey: string): NostrWSMessage;
|
|
41
|
+
/**
|
|
42
|
+
* Contact list manager interface
|
|
43
|
+
*/
|
|
44
|
+
export interface ContactListManager {
|
|
45
|
+
/**
|
|
46
|
+
* Adds or updates a contact
|
|
47
|
+
* @param contact - Contact to add/update
|
|
48
|
+
*/
|
|
49
|
+
addContact(contact: Contact): void;
|
|
50
|
+
/**
|
|
51
|
+
* Removes a contact
|
|
52
|
+
* @param pubkey - Public key of contact to remove
|
|
53
|
+
*/
|
|
54
|
+
removeContact(pubkey: string): void;
|
|
55
|
+
/**
|
|
56
|
+
* Gets a contact by public key
|
|
57
|
+
* @param pubkey - Public key to look up
|
|
58
|
+
* @returns {Contact | undefined} Contact if found
|
|
59
|
+
*/
|
|
60
|
+
getContact(pubkey: string): Contact | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* Gets all contacts
|
|
63
|
+
* @returns {Contact[]} Array of all contacts
|
|
64
|
+
*/
|
|
65
|
+
getAllContacts(): Contact[];
|
|
66
|
+
/**
|
|
67
|
+
* Updates contact metadata
|
|
68
|
+
* @param pubkey - Public key of contact
|
|
69
|
+
* @param metadata - New metadata
|
|
70
|
+
*/
|
|
71
|
+
updateContactMetadata(pubkey: string, metadata: Record<string, unknown>): void;
|
|
72
|
+
/**
|
|
73
|
+
* Creates a contact list event
|
|
74
|
+
* @returns {NostrWSMessage} Contact list event
|
|
75
|
+
*/
|
|
76
|
+
createEvent(): NostrWSMessage;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Creates a contact list manager
|
|
80
|
+
* @param _logger - Logger instance
|
|
81
|
+
* @returns {ContactListManager} Contact list manager
|
|
82
|
+
*/
|
|
83
|
+
export declare function createContactListManager(_logger: Logger): ContactListManager;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file NIP-02: Contact List and Petnames
|
|
3
|
+
* @module nips/nip-02
|
|
4
|
+
* @see https://github.com/nostr-protocol/nips/blob/master/02.md
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Contact list event kind
|
|
8
|
+
*/
|
|
9
|
+
export const CONTACT_LIST_KIND = 3;
|
|
10
|
+
/**
|
|
11
|
+
* Creates a contact list event
|
|
12
|
+
* @param contacts - List of contacts
|
|
13
|
+
* @param metadata - Optional metadata for the contact list
|
|
14
|
+
* @returns {NostrWSMessage} Contact list event
|
|
15
|
+
*/
|
|
16
|
+
export function createContactListEvent(contacts, metadata = {}) {
|
|
17
|
+
const tags = contacts.map(contact => {
|
|
18
|
+
const tag = ['p', contact.pubkey];
|
|
19
|
+
if (contact.relay)
|
|
20
|
+
tag.push(contact.relay);
|
|
21
|
+
if (contact.petname)
|
|
22
|
+
tag.push(contact.petname);
|
|
23
|
+
return tag;
|
|
24
|
+
});
|
|
25
|
+
return {
|
|
26
|
+
type: 'EVENT',
|
|
27
|
+
data: {
|
|
28
|
+
kind: CONTACT_LIST_KIND,
|
|
29
|
+
tags,
|
|
30
|
+
content: JSON.stringify(metadata)
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Extracts contacts from a contact list event
|
|
36
|
+
* @param message - Contact list message
|
|
37
|
+
* @param _logger - Logger instance
|
|
38
|
+
* @returns {Contact[]} Array of contacts
|
|
39
|
+
*/
|
|
40
|
+
export function extractContacts(message, _logger) {
|
|
41
|
+
try {
|
|
42
|
+
if (message.type !== 'EVENT' || !message.data) {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
const event = message.data;
|
|
46
|
+
if (event.kind !== CONTACT_LIST_KIND || !Array.isArray(event.tags)) {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
let metadata = {};
|
|
50
|
+
try {
|
|
51
|
+
metadata = JSON.parse(event.content);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
_logger.debug('Failed to parse contact list metadata');
|
|
55
|
+
}
|
|
56
|
+
return event.tags
|
|
57
|
+
.filter(tag => Array.isArray(tag) && tag[0] === 'p' && tag[1])
|
|
58
|
+
.map(tag => ({
|
|
59
|
+
pubkey: tag[1],
|
|
60
|
+
relay: tag[2],
|
|
61
|
+
petname: tag[3],
|
|
62
|
+
metadata
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
_logger.error('Error extracting contacts:', error);
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Creates a contact list subscription message
|
|
72
|
+
* @param pubkey - Public key to subscribe to
|
|
73
|
+
* @returns {NostrWSMessage} Subscription message
|
|
74
|
+
*/
|
|
75
|
+
export function createContactListSubscription(pubkey) {
|
|
76
|
+
return {
|
|
77
|
+
type: 'REQ',
|
|
78
|
+
data: {
|
|
79
|
+
filter: {
|
|
80
|
+
authors: [pubkey],
|
|
81
|
+
kinds: [CONTACT_LIST_KIND],
|
|
82
|
+
limit: 1
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Creates a contact list manager
|
|
89
|
+
* @param _logger - Logger instance
|
|
90
|
+
* @returns {ContactListManager} Contact list manager
|
|
91
|
+
*/
|
|
92
|
+
export function createContactListManager(_logger) {
|
|
93
|
+
const contacts = new Map();
|
|
94
|
+
return {
|
|
95
|
+
addContact(contact) {
|
|
96
|
+
contacts.set(contact.pubkey, contact);
|
|
97
|
+
},
|
|
98
|
+
removeContact(pubkey) {
|
|
99
|
+
contacts.delete(pubkey);
|
|
100
|
+
},
|
|
101
|
+
getContact(pubkey) {
|
|
102
|
+
return contacts.get(pubkey);
|
|
103
|
+
},
|
|
104
|
+
getAllContacts() {
|
|
105
|
+
return Array.from(contacts.values());
|
|
106
|
+
},
|
|
107
|
+
updateContactMetadata(pubkey, metadata) {
|
|
108
|
+
const contact = contacts.get(pubkey);
|
|
109
|
+
if (contact) {
|
|
110
|
+
contacts.set(pubkey, {
|
|
111
|
+
...contact,
|
|
112
|
+
metadata: {
|
|
113
|
+
...contact.metadata,
|
|
114
|
+
...metadata
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
createEvent() {
|
|
120
|
+
return createContactListEvent(Array.from(contacts.values()));
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file NIP-04: Encrypted Direct Message Support
|
|
3
|
+
* @module nips/nip-04
|
|
4
|
+
* @see https://github.com/nostr-protocol/nips/blob/master/04.md
|
|
5
|
+
*/
|
|
6
|
+
import type { NostrWSMessage } from '../types/messages';
|
|
7
|
+
import type { Logger } from '../types/logger';
|
|
8
|
+
/**
|
|
9
|
+
* Kind value for encrypted direct messages
|
|
10
|
+
*/
|
|
11
|
+
export declare const ENCRYPTED_DM_KIND = 4;
|
|
12
|
+
/**
|
|
13
|
+
* Creates an encrypted direct message event
|
|
14
|
+
* @param content - Message content to encrypt
|
|
15
|
+
* @param recipientPubkey - Recipient's public key
|
|
16
|
+
* @param senderPrivkey - Sender's private key
|
|
17
|
+
* @param tags - Additional tags for the event
|
|
18
|
+
* @returns {Promise<NostrWSMessage>} Encrypted message event
|
|
19
|
+
*/
|
|
20
|
+
export declare function createEncryptedDM(content: string, recipientPubkey: string, senderPrivkey: string, tags?: string[][]): Promise<NostrWSMessage>;
|
|
21
|
+
/**
|
|
22
|
+
* Decrypts a received direct message event
|
|
23
|
+
* @param message - Received message
|
|
24
|
+
* @param recipientPrivkey - Recipient's private key
|
|
25
|
+
* @param senderPubkey - Sender's public key
|
|
26
|
+
* @param logger - Logger instance
|
|
27
|
+
* @returns {Promise<string>} Decrypted message content
|
|
28
|
+
*/
|
|
29
|
+
export declare function decryptDM(message: NostrWSMessage, recipientPrivkey: string, senderPubkey: string, logger: Logger): Promise<string>;
|
|
30
|
+
/**
|
|
31
|
+
* Validates an encrypted DM event format
|
|
32
|
+
* @param message - Message to validate
|
|
33
|
+
* @param logger - Logger instance
|
|
34
|
+
* @returns {boolean} True if message follows NIP-04 format
|
|
35
|
+
*/
|
|
36
|
+
export declare function validateEncryptedDM(message: NostrWSMessage, logger: Logger): boolean;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file NIP-04: Encrypted Direct Message Support
|
|
3
|
+
* @module nips/nip-04
|
|
4
|
+
* @see https://github.com/nostr-protocol/nips/blob/master/04.md
|
|
5
|
+
*/
|
|
6
|
+
import { encryptMessage, decryptMessage } from 'nostr-crypto-utils';
|
|
7
|
+
/**
|
|
8
|
+
* Kind value for encrypted direct messages
|
|
9
|
+
*/
|
|
10
|
+
export const ENCRYPTED_DM_KIND = 4;
|
|
11
|
+
/**
|
|
12
|
+
* Creates an encrypted direct message event
|
|
13
|
+
* @param content - Message content to encrypt
|
|
14
|
+
* @param recipientPubkey - Recipient's public key
|
|
15
|
+
* @param senderPrivkey - Sender's private key
|
|
16
|
+
* @param tags - Additional tags for the event
|
|
17
|
+
* @returns {Promise<NostrWSMessage>} Encrypted message event
|
|
18
|
+
*/
|
|
19
|
+
export async function createEncryptedDM(content, recipientPubkey, senderPrivkey, tags = []) {
|
|
20
|
+
try {
|
|
21
|
+
const encryptedContent = await encryptMessage(content, recipientPubkey, senderPrivkey);
|
|
22
|
+
return {
|
|
23
|
+
type: 'EVENT',
|
|
24
|
+
data: {
|
|
25
|
+
kind: ENCRYPTED_DM_KIND,
|
|
26
|
+
content: encryptedContent,
|
|
27
|
+
tags: [
|
|
28
|
+
['p', recipientPubkey],
|
|
29
|
+
...tags
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
36
|
+
throw new Error(`Failed to create encrypted DM: ${errorMessage}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Decrypts a received direct message event
|
|
41
|
+
* @param message - Received message
|
|
42
|
+
* @param recipientPrivkey - Recipient's private key
|
|
43
|
+
* @param senderPubkey - Sender's public key
|
|
44
|
+
* @param logger - Logger instance
|
|
45
|
+
* @returns {Promise<string>} Decrypted message content
|
|
46
|
+
*/
|
|
47
|
+
export async function decryptDM(message, recipientPrivkey, senderPubkey, logger) {
|
|
48
|
+
try {
|
|
49
|
+
if (message.type !== 'EVENT' || !message.data) {
|
|
50
|
+
throw new Error('Invalid message format');
|
|
51
|
+
}
|
|
52
|
+
const event = message.data;
|
|
53
|
+
if (event.kind !== ENCRYPTED_DM_KIND) {
|
|
54
|
+
throw new Error('Not an encrypted DM event');
|
|
55
|
+
}
|
|
56
|
+
const content = event.content;
|
|
57
|
+
if (!content) {
|
|
58
|
+
throw new Error('No encrypted content found');
|
|
59
|
+
}
|
|
60
|
+
return await decryptMessage(content, senderPubkey, recipientPrivkey);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
64
|
+
logger.error(`Failed to decrypt DM: ${errorMessage}`);
|
|
65
|
+
throw new Error(errorMessage);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Validates an encrypted DM event format
|
|
70
|
+
* @param message - Message to validate
|
|
71
|
+
* @param logger - Logger instance
|
|
72
|
+
* @returns {boolean} True if message follows NIP-04 format
|
|
73
|
+
*/
|
|
74
|
+
export function validateEncryptedDM(message, logger) {
|
|
75
|
+
try {
|
|
76
|
+
if (message.type !== 'EVENT' || !message.data) {
|
|
77
|
+
logger.debug('Not an event message');
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
const event = message.data;
|
|
81
|
+
if (event.kind !== ENCRYPTED_DM_KIND) {
|
|
82
|
+
logger.debug('Not an encrypted DM event');
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
if (!event.content || typeof event.content !== 'string') {
|
|
86
|
+
logger.debug('Invalid or missing content');
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
if (!Array.isArray(event.tags)) {
|
|
90
|
+
logger.debug('Missing tags array');
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
const pTag = event.tags.find(tag => Array.isArray(tag) && tag[0] === 'p' && tag[1]);
|
|
94
|
+
if (!pTag) {
|
|
95
|
+
logger.debug('Missing recipient pubkey tag');
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
102
|
+
logger.error(`Error validating encrypted DM: ${errorMessage}`);
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file NIP-05: DNS-Based Verification
|
|
3
|
+
* @module nips/nip-05
|
|
4
|
+
* @see https://github.com/nostr-protocol/nips/blob/master/05.md
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* NIP-05 verification result
|
|
8
|
+
*/
|
|
9
|
+
export interface NIP05VerificationResult {
|
|
10
|
+
valid: boolean;
|
|
11
|
+
pubkey?: string;
|
|
12
|
+
relays?: string[];
|
|
13
|
+
error?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Verifies a NIP-05 identifier
|
|
17
|
+
* @param identifier - Internet identifier (user@domain.com)
|
|
18
|
+
* @param pubkey - Public key to verify
|
|
19
|
+
* @param logger - Logger instance
|
|
20
|
+
* @returns {Promise<NIP05VerificationResult>} Verification result
|
|
21
|
+
*/
|
|
22
|
+
export declare function verifyNIP05Identifier(identifier: string, pubkey: string, logger: any): Promise<NIP05VerificationResult>;
|
|
23
|
+
/**
|
|
24
|
+
* Creates metadata event with NIP-05 identifier
|
|
25
|
+
* @param identifier - Internet identifier
|
|
26
|
+
* @param metadata - Additional metadata
|
|
27
|
+
* @returns {Record<string, unknown>} Metadata event content
|
|
28
|
+
*/
|
|
29
|
+
export declare function createNIP05Metadata(identifier: string, metadata?: Record<string, unknown>): Record<string, unknown>;
|
|
30
|
+
/**
|
|
31
|
+
* NIP-05 verification cache interface
|
|
32
|
+
*/
|
|
33
|
+
export interface NIP05VerificationCache {
|
|
34
|
+
/**
|
|
35
|
+
* Gets cached verification result
|
|
36
|
+
* @param identifier - Internet identifier
|
|
37
|
+
* @param pubkey - Public key
|
|
38
|
+
* @returns {NIP05VerificationResult | undefined} Cached result
|
|
39
|
+
*/
|
|
40
|
+
get(identifier: string, pubkey: string): NIP05VerificationResult | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Sets verification result in cache
|
|
43
|
+
* @param identifier - Internet identifier
|
|
44
|
+
* @param pubkey - Public key
|
|
45
|
+
* @param result - Verification result
|
|
46
|
+
* @param ttl - Time to live in seconds
|
|
47
|
+
*/
|
|
48
|
+
set(identifier: string, pubkey: string, result: NIP05VerificationResult, ttl: number): void;
|
|
49
|
+
/**
|
|
50
|
+
* Clears expired entries
|
|
51
|
+
*/
|
|
52
|
+
cleanup(): void;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Creates a NIP-05 verification cache
|
|
56
|
+
* @param defaultTTL - Default TTL in seconds
|
|
57
|
+
* @returns {NIP05VerificationCache} Verification cache
|
|
58
|
+
*/
|
|
59
|
+
export declare function createNIP05VerificationCache(defaultTTL?: number): NIP05VerificationCache;
|
|
60
|
+
/**
|
|
61
|
+
* Batch verification interface for multiple identifiers
|
|
62
|
+
*/
|
|
63
|
+
export interface NIP05BatchVerifier {
|
|
64
|
+
/**
|
|
65
|
+
* Adds identifier to verification queue
|
|
66
|
+
* @param identifier - Internet identifier
|
|
67
|
+
* @param pubkey - Public key
|
|
68
|
+
*/
|
|
69
|
+
addToQueue(identifier: string, pubkey: string): void;
|
|
70
|
+
/**
|
|
71
|
+
* Verifies all queued identifiers
|
|
72
|
+
* @returns {Promise<Map<string, NIP05VerificationResult>>} Verification results
|
|
73
|
+
*/
|
|
74
|
+
verifyAll(): Promise<Map<string, NIP05VerificationResult>>;
|
|
75
|
+
/**
|
|
76
|
+
* Clears verification queue
|
|
77
|
+
*/
|
|
78
|
+
clearQueue(): void;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Creates a NIP-05 batch verifier
|
|
82
|
+
* @param logger - Logger instance
|
|
83
|
+
* @param cache - Optional verification cache
|
|
84
|
+
* @returns {NIP05BatchVerifier} Batch verifier
|
|
85
|
+
*/
|
|
86
|
+
export declare function createNIP05BatchVerifier(logger: any, cache?: NIP05VerificationCache): NIP05BatchVerifier;
|