nostr-crypto-utils 0.1.6 → 0.2.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/README.md CHANGED
@@ -1,14 +1,17 @@
1
- # @humanjavaenterprises/nostr-crypto-utils
1
+ # nostr-crypto-utils
2
2
 
3
3
  A comprehensive TypeScript library providing cryptographic utilities and protocol-compliant message handling for Nostr applications, designed to work seamlessly with [@humanjavaenterprises/nostr-nsec-seedphrase](https://github.com/HumanjavaEnterprises/nostr-nsec-seedphrase).
4
4
 
5
5
  [![npm version](https://badge.fury.io/js/%40humanjavaenterprises%2Fnostr-crypto-utils.svg)](https://www.npmjs.com/package/@humanjavaenterprises/nostr-crypto-utils)
6
6
  [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/)
7
7
  [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/HumanjavaEnterprises/nostr-crypto-utils/blob/main/LICENSE)
8
+ [![Documentation](https://img.shields.io/badge/docs-TypeDoc-blue.svg)](https://humanjavaenterprises.github.io/nostr-crypto-utils/)
8
9
 
9
- ⚠️ **Important Security Notice**
10
+ ## Security Notice
10
11
 
11
- This library handles cryptographic keys and operations that are critical for securing your Nostr identity and data. All cryptographic operations, including key generation, signing, and encryption, must be handled with appropriate security measures.
12
+ ⚠️ **Important**: This library handles cryptographic keys and operations that are critical for securing your Nostr identity and data. All cryptographic operations, including key generation, signing, and encryption, must be handled with appropriate security measures.
13
+
14
+ If you discover a security vulnerability, please follow our [Security Policy](SECURITY.md) and report it through [GitHub's Security Advisory feature](https://github.com/humanjavaenterprises/nostr-crypto-utils/security/advisories/new).
12
15
 
13
16
  ## Features
14
17
 
@@ -19,117 +22,81 @@ This library handles cryptographic keys and operations that are critical for sec
19
22
  - **Encryption**: Secure encryption and decryption for direct messages (NIP-04)
20
23
  - **Validation**: Comprehensive validation for events, filters, and subscriptions
21
24
 
25
+ ## Features and Capabilities
26
+
27
+ | Feature | Status | Description |
28
+ |---------------------------|--------|-------------------------------------------------------|
29
+ | Key Management | ✅ | Generate, validate, and manage Nostr keypairs |
30
+ | Event Creation | ✅ | Create and validate Nostr events |
31
+ | Event Signing | ✅ | Sign events with schnorr signatures |
32
+ | Event Verification | ✅ | Verify event signatures and validate event structure |
33
+ | Message Encryption | ✅ | NIP-04 compliant message encryption |
34
+ | Message Decryption | ✅ | NIP-04 compliant message decryption |
35
+ | Event Serialization | ✅ | Protocol-compliant event serialization |
36
+ | Event Hashing | ✅ | Generate and verify event IDs |
37
+
22
38
  ## Installation
23
39
 
24
40
  ```bash
25
- npm install @humanjavaenterprises/nostr-crypto-utils
41
+ npm install nostr-crypto-utils
26
42
  ```
27
43
 
28
- ## Usage
29
-
30
- ### Key Management
44
+ ## Quick Start
31
45
 
32
46
  ```typescript
33
- import { generateKeyPair, getPublicKey } from '@humanjavaenterprises/nostr-crypto-utils';
47
+ import { generateKeyPair, signEvent, verifyEvent } from 'nostr-crypto-utils';
34
48
 
35
- // Generate a new key pair
49
+ // Generate a new keypair
36
50
  const keyPair = await generateKeyPair();
37
51
  console.log('Public Key:', keyPair.publicKey);
38
52
  console.log('Private Key:', keyPair.privateKey);
39
53
 
40
- // Get public key from private key
41
- const pubKey = getPublicKey(privateKey);
42
- ```
43
-
44
- ### Event Operations
45
-
46
- ```typescript
47
- import { createEvent, signEvent, validateEvent } from '@humanjavaenterprises/nostr-crypto-utils';
48
-
49
54
  // Create and sign an event
50
- const event = createEvent({
51
- kind: NostrEventKind.TEXT_NOTE,
55
+ const event = await signEvent({
56
+ kind: 1,
52
57
  content: 'Hello Nostr!',
53
58
  tags: []
54
- });
55
- const signedEvent = await signEvent(event, privateKey);
56
-
57
- // Validate an event
58
- const validation = validateEvent(event);
59
- if (validation.isValid) {
60
- console.log('Event is valid');
61
- } else {
62
- console.log('Validation errors:', validation.errors);
63
- }
64
- ```
59
+ }, keyPair.privateKey);
65
60
 
66
- ### Message Handling
67
-
68
- ```typescript
69
- import {
70
- formatEventForRelay,
71
- formatSubscriptionForRelay,
72
- parseNostrMessage
73
- } from '@humanjavaenterprises/nostr-crypto-utils';
74
-
75
- // Format event for relay
76
- const eventMessage = formatEventForRelay(signedEvent);
77
-
78
- // Format subscription request
79
- const subscription = {
80
- id: 'sub1',
81
- filters: [{ kinds: [1], limit: 10 }]
82
- };
83
- const subMessage = formatSubscriptionForRelay(subscription);
84
-
85
- // Parse incoming messages
86
- const message = ['EVENT', signedEvent];
87
- const parsed = parseNostrMessage(message);
61
+ // Verify the event
62
+ const isValidEvent = await verifyEvent(event);
88
63
  ```
89
64
 
90
- ### Encryption (NIP-04)
65
+ ## Documentation
91
66
 
92
- ```typescript
93
- import { encrypt, decrypt } from '@humanjavaenterprises/nostr-crypto-utils';
94
-
95
- // Encrypt a message
96
- const encrypted = await encrypt(
97
- 'Secret message',
98
- recipientPublicKey,
99
- senderPrivateKey
100
- );
101
-
102
- // Decrypt a message
103
- const decrypted = await decrypt(
104
- encrypted,
105
- senderPublicKey,
106
- recipientPrivateKey
107
- );
108
- ```
67
+ For detailed API documentation, visit our [TypeDoc Documentation](https://humanjavaenterprises.github.io/nostr-crypto-utils/).
109
68
 
110
- ## Protocol Compliance
69
+ ## Support & Community
111
70
 
112
- This library implements the following Nostr Implementation Possibilities (NIPs):
71
+ We welcome your feedback and contributions! Here's how you can get involved:
113
72
 
114
- - NIP-01: Basic protocol flow description
115
- - NIP-02: Contact List and Petnames
116
- - NIP-04: Encrypted Direct Message
117
- - NIP-09: Event Deletion
118
- - NIP-25: Reactions
119
- - NIP-28: Public Chat Channels
73
+ - 🐛 [Report bugs](https://github.com/humanjavaenterprises/nostr-crypto-utils/issues/new?labels=bug&template=bug_report.md)
74
+ - 💡 [Request features](https://github.com/humanjavaenterprises/nostr-crypto-utils/issues/new?labels=enhancement&template=feature_request.md)
75
+ - 💬 [Start a discussion](https://github.com/humanjavaenterprises/nostr-crypto-utils/discussions)
76
+ - 📖 [Read documentation](https://humanjavaenterprises.github.io/nostr-crypto-utils/)
77
+ - 🔒 [Report security issues](https://github.com/humanjavaenterprises/nostr-crypto-utils/security/advisories/new)
120
78
 
121
79
  ## Contributing
122
80
 
123
- Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
81
+ We welcome contributions! Please follow these steps:
124
82
 
125
- ## Security
83
+ 1. Fork the repository
84
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
85
+ 3. Commit your changes (`git commit -m 'feat: add amazing feature'`)
86
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
87
+ 5. Open a Pull Request
126
88
 
127
- If you discover a security vulnerability within this library, please send an e-mail to security@humanjavaenterprises.com. All security vulnerabilities will be promptly addressed.
89
+ Before contributing:
90
+ - Read our [Code of Conduct](CODE_OF_CONDUCT.md)
91
+ - Check our [Contributing Guidelines](.github/CONTRIBUTING.md)
92
+ - Review our [Security Policy](SECURITY.md)
93
+ - Search [existing issues](https://github.com/humanjavaenterprises/nostr-crypto-utils/issues) before creating a new one
128
94
 
129
95
  ## License
130
96
 
131
- This project is licensed under the MIT License - see the [LICENSE](https://github.com/HumanjavaEnterprises/nostr-crypto-utils/blob/main/LICENSE) file for details.
97
+ [MIT](LICENSE)
132
98
 
133
99
  ---
134
-
135
- Built with ❤️ by [Human Java Enterprises](https://github.com/HumanjavaEnterprises)
100
+ <div align="center">
101
+ Made with ❤️ by <a href="https://github.com/humanjavaenterprises">Humanjava Enterprises</a>
102
+ </div>
@@ -1,4 +1,4 @@
1
- /// <reference types="jest" />
1
+ import { describe, it, expect } from 'vitest';
2
2
  import { generateKeyPair, getPublicKey, validateKeyPair, signEvent, verifySignature, encrypt, decrypt } from '../index';
3
3
  describe('NOSTR Crypto Utils', () => {
4
4
  describe('Key Management', () => {
@@ -63,7 +63,7 @@ describe('NOSTR Crypto Utils', () => {
63
63
  tags: [],
64
64
  content: 'Hello, World!'
65
65
  };
66
- const privateKey = await generateKeyPair().privateKey;
66
+ const privateKey = await (await generateKeyPair()).privateKey;
67
67
  const signedEvent = await signEvent(event, privateKey);
68
68
  expect(await verifySignature(signedEvent)).toBe(true);
69
69
  });
@@ -74,7 +74,7 @@ describe('NOSTR Crypto Utils', () => {
74
74
  tags: [],
75
75
  content: 'Hello, World!'
76
76
  };
77
- const privateKey = await generateKeyPair().privateKey;
77
+ const privateKey = await (await generateKeyPair()).privateKey;
78
78
  const signedEvent = await signEvent(event, privateKey);
79
79
  // Tamper with the signature
80
80
  signedEvent.sig = signedEvent.sig.replace('a', 'b');
@@ -87,7 +87,7 @@ describe('NOSTR Crypto Utils', () => {
87
87
  tags: [],
88
88
  content: 'Hello, World!'
89
89
  };
90
- const privateKey = await generateKeyPair().privateKey;
90
+ const privateKey = await (await generateKeyPair()).privateKey;
91
91
  const signedEvent = await signEvent(event, privateKey);
92
92
  // Tamper with the content which affects the hash
93
93
  signedEvent.content = 'Modified content';
@@ -100,7 +100,7 @@ describe('NOSTR Crypto Utils', () => {
100
100
  tags: [],
101
101
  content: 'Hello, World!'
102
102
  };
103
- const privateKey = await generateKeyPair().privateKey;
103
+ const privateKey = await (await generateKeyPair()).privateKey;
104
104
  const signedEvent = await signEvent(event, privateKey);
105
105
  // Add invalid hex character
106
106
  signedEvent.sig = 'XYZ' + signedEvent.sig.slice(3);
@@ -109,22 +109,22 @@ describe('NOSTR Crypto Utils', () => {
109
109
  });
110
110
  describe('validateKeyPair', () => {
111
111
  it('should validate a correct key pair', async () => {
112
- const privateKey = await generateKeyPair().privateKey;
112
+ const privateKey = await (await generateKeyPair()).privateKey;
113
113
  const publicKey = await getPublicKey(privateKey);
114
114
  const result = await validateKeyPair(publicKey, privateKey);
115
115
  expect(result.isValid).toBe(true);
116
116
  expect(result.error).toBeUndefined();
117
117
  });
118
118
  it('should reject mismatched key pair', async () => {
119
- const privateKey1 = await generateKeyPair().privateKey;
120
- const privateKey2 = await generateKeyPair().privateKey;
119
+ const privateKey1 = await (await generateKeyPair()).privateKey;
120
+ const privateKey2 = await (await generateKeyPair()).privateKey;
121
121
  const publicKey = await getPublicKey(privateKey1);
122
122
  const result = await validateKeyPair(publicKey, privateKey2);
123
123
  expect(result.isValid).toBe(false);
124
124
  expect(result.error).toBe('Public key does not match private key');
125
125
  });
126
126
  it('should handle invalid private key', async () => {
127
- const publicKey = await getPublicKey(await generateKeyPair().privateKey);
127
+ const publicKey = await getPublicKey(await (await generateKeyPair()).privateKey);
128
128
  const result = await validateKeyPair(publicKey, 'invalid-private-key');
129
129
  expect(result.isValid).toBe(false);
130
130
  expect(result.error).toBe('Invalid key pair');
@@ -138,7 +138,7 @@ describe('NOSTR Crypto Utils', () => {
138
138
  tags: [['p', '1234']],
139
139
  content: 'Hello, World!'
140
140
  };
141
- const privateKey = await generateKeyPair().privateKey;
141
+ const privateKey = await (await generateKeyPair()).privateKey;
142
142
  const signedEvent = await signEvent(event, privateKey);
143
143
  expect(signedEvent.sig).toBeTruthy();
144
144
  expect(signedEvent.id).toBeTruthy();
@@ -151,7 +151,7 @@ describe('NOSTR Crypto Utils', () => {
151
151
  content: 'Hello, World!',
152
152
  tags: [] // Adding the required tags property
153
153
  };
154
- const privateKey = await generateKeyPair().privateKey;
154
+ const privateKey = await (await generateKeyPair()).privateKey;
155
155
  const signedEvent = await signEvent(event, privateKey);
156
156
  expect(signedEvent.sig).toBeTruthy();
157
157
  expect(signedEvent.id).toBeTruthy();
@@ -166,7 +166,7 @@ describe('NOSTR Crypto Utils', () => {
166
166
  content: 'Hello, World!',
167
167
  tags: [] // Add empty tags array to satisfy NostrEvent interface
168
168
  };
169
- const privateKey = await generateKeyPair().privateKey;
169
+ const privateKey = await (await generateKeyPair()).privateKey;
170
170
  const signedEvent = await signEvent(event, privateKey);
171
171
  expect(signedEvent.tags).toEqual([]);
172
172
  });
@@ -177,7 +177,7 @@ describe('NOSTR Crypto Utils', () => {
177
177
  tags: [],
178
178
  created_at: Math.floor(Date.now() / 1000)
179
179
  };
180
- const privateKey = await generateKeyPair().privateKey;
180
+ const privateKey = await (await generateKeyPair()).privateKey;
181
181
  const signedEvent = await signEvent(event, privateKey);
182
182
  expect(signedEvent.created_at).toBeDefined();
183
183
  expect(typeof signedEvent.created_at).toBe('number');
@@ -1,3 +1,4 @@
1
+ import { describe, it, expect } from 'vitest';
1
2
  import { isNostrEvent, isSignedNostrEvent } from '../types/guards';
2
3
  describe('Type Guards', () => {
3
4
  describe('isNostrEvent', () => {
@@ -1,5 +1,6 @@
1
1
  import { formatEventForRelay, formatSubscriptionForRelay, formatCloseForRelay, formatAuthForRelay, parseNostrMessage, createMetadataEvent, createTextNoteEvent, createDirectMessageEvent, createChannelMessageEvent, extractReferencedEvents, extractMentionedPubkeys, createKindFilter, createAuthorFilter, createReplyFilter } from '../integration';
2
2
  import { NOSTR_KIND } from '../constants';
3
+ import { describe, it, expect } from 'vitest';
3
4
  describe('Integration Utilities', () => {
4
5
  const mockSignedEvent = {
5
6
  id: '123',
@@ -36,20 +37,31 @@ describe('Integration Utilities', () => {
36
37
  it('should parse EVENT message', () => {
37
38
  const message = ['EVENT', mockSignedEvent];
38
39
  const parsed = parseNostrMessage(message);
39
- expect(parsed.type).toBe('EVENT');
40
- expect(parsed.payload).toEqual(mockSignedEvent);
40
+ expect(parsed).not.toBeNull();
41
+ if (parsed) {
42
+ expect(parsed.type).toBe('EVENT');
43
+ expect(parsed.payload).toEqual(mockSignedEvent);
44
+ }
41
45
  });
42
46
  it('should parse NOTICE message', () => {
43
47
  const message = ['NOTICE', 'test message'];
44
48
  const parsed = parseNostrMessage(message);
45
- expect(parsed.type).toBe('NOTICE');
46
- expect(parsed.payload).toBe('test message');
49
+ expect(parsed).not.toBeNull();
50
+ if (parsed) {
51
+ expect(parsed.type).toBe('NOTICE');
52
+ expect(parsed.payload).toBe('test message');
53
+ }
47
54
  });
48
55
  it('should parse OK message', () => {
49
56
  const message = ['OK', 'event1', true, 'success'];
50
57
  const parsed = parseNostrMessage(message);
51
- expect(parsed.type).toBe('OK');
52
- expect(parsed.payload).toEqual(['event1', true, 'success']);
58
+ expect(parsed).not.toBeNull();
59
+ if (parsed) {
60
+ expect(parsed.type).toBe('OK');
61
+ if (parsed.payload) {
62
+ expect(parsed.payload).toEqual(['event1', true, 'success']);
63
+ }
64
+ }
53
65
  });
54
66
  it('should throw error for invalid message', () => {
55
67
  expect(() => parseNostrMessage('invalid')).toThrow('Invalid relay message: not an array');
@@ -1,104 +1,105 @@
1
- import { generateKeyPair, signEvent, encrypt, decrypt, createEvent, validateEvent, validateSignedEvent, NostrEventKind, formatSubscriptionForRelay, formatEventForRelay, formatCloseForRelay } from '../index';
1
+ import { describe, it, expect, beforeAll } from 'vitest';
2
+ import { generateKeyPair, createEvent, signEvent, encrypt, decrypt, NostrEventKind, formatSubscriptionForRelay, formatEventForRelay, formatCloseForRelay } from '../index';
2
3
  describe('NIP-01: Basic Protocol Flow', () => {
3
4
  let keyPair;
4
5
  beforeAll(async () => {
5
6
  keyPair = await generateKeyPair();
6
7
  });
7
- test('Event Creation and Signing Flow', async () => {
8
+ it('Event Creation and Signing Flow', async () => {
8
9
  // Create a text note (kind 1) as specified in NIP-01
9
10
  const event = createEvent({
10
11
  kind: NostrEventKind.TEXT_NOTE,
11
- content: 'Hello Nostr!',
12
- tags: [],
13
- created_at: Math.floor(Date.now() / 1000)
12
+ content: 'Hello, Nostr!',
13
+ tags: []
14
14
  });
15
15
  // Sign the event
16
16
  const signedEvent = await signEvent(event, keyPair.privateKey);
17
- // Verify event structure according to NIP-01
18
- const validation = validateSignedEvent(signedEvent);
19
- expect(validation.isValid).toBe(true);
17
+ // Verify the signed event structure
18
+ expect(signedEvent.id).toBeDefined();
19
+ expect(signedEvent.sig).toBeDefined();
20
20
  expect(signedEvent.pubkey).toBe(keyPair.publicKey);
21
- expect(typeof signedEvent.created_at).toBe('number');
21
+ expect(signedEvent.kind).toBe(NostrEventKind.TEXT_NOTE);
22
+ expect(signedEvent.content).toBe('Hello, Nostr!');
23
+ expect(signedEvent.created_at).toBeDefined();
22
24
  expect(Array.isArray(signedEvent.tags)).toBe(true);
23
25
  });
24
- test('Client-Relay Message Format', async () => {
26
+ it('Client-Relay Message Format', async () => {
25
27
  // Test REQ message format
26
- const reqMessage = formatSubscriptionForRelay({ id: 'test_sub', filters: [{ kinds: [1], limit: 10 }] });
28
+ const reqMessage = formatSubscriptionForRelay({
29
+ id: 'test_sub',
30
+ filters: [{ kinds: [1], limit: 10 }]
31
+ });
27
32
  expect(reqMessage[0]).toBe('REQ');
28
- expect(Array.isArray(reqMessage)).toBe(true);
29
- expect(reqMessage.length).toBeGreaterThan(2);
33
+ expect(reqMessage[1]).toBe('test_sub');
34
+ expect(reqMessage[2]).toEqual({ kinds: [1], limit: 10 });
30
35
  // Test EVENT message format
31
- const event2 = createEvent({
36
+ const keyPair = await generateKeyPair();
37
+ const event = createEvent({
32
38
  kind: NostrEventKind.TEXT_NOTE,
33
- content: 'test message',
39
+ content: 'Test message',
34
40
  tags: []
35
41
  });
36
- const signedEvent = await signEvent(event2, keyPair.privateKey);
42
+ const signedEvent = await signEvent(event, keyPair.privateKey);
37
43
  const eventMessage = formatEventForRelay(signedEvent);
38
44
  expect(eventMessage[0]).toBe('EVENT');
39
- expect(eventMessage[1]).toMatchObject({
40
- kind: NostrEventKind.TEXT_NOTE,
41
- content: 'test message'
42
- });
45
+ expect(eventMessage[1]).toEqual(signedEvent);
43
46
  // Test CLOSE message format
44
- const closeMessage = JSON.stringify(formatCloseForRelay('subscription_id'));
45
- const closeParsed = JSON.parse(closeMessage);
46
- expect(closeParsed[0]).toBe('CLOSE');
47
- expect(closeParsed[1]).toBe('subscription_id');
47
+ const closeMessage = formatCloseForRelay('test_sub');
48
+ expect(closeMessage[0]).toBe('CLOSE');
49
+ expect(closeMessage[1]).toBe('test_sub');
48
50
  });
49
51
  });
50
52
  describe('NIP-02: Contact List', () => {
51
- test('Contact List Event Structure', async () => {
53
+ it('Contact List Event Structure', async () => {
52
54
  const keyPair = await generateKeyPair();
53
55
  const contacts = createEvent({
54
56
  kind: NostrEventKind.CONTACTS,
55
- content: JSON.stringify([
56
- {
57
- pubkey: '0123456789abcdef',
58
- relay: 'wss://relay.example.com',
59
- petname: 'friend'
60
- }
61
- ]),
62
- tags: [],
57
+ content: JSON.stringify({
58
+ name: 'Alice',
59
+ about: 'I love Nostr!'
60
+ }),
63
61
  created_at: Math.floor(Date.now() / 1000)
64
62
  });
65
- const validation = validateEvent(contacts);
66
- expect(validation.isValid).toBe(true);
67
- expect(contacts.kind).toBe(NostrEventKind.CONTACTS);
63
+ const signedContacts = await signEvent(contacts, keyPair.privateKey);
64
+ expect(signedContacts).toHaveProperty('id');
65
+ expect(signedContacts).toHaveProperty('sig');
66
+ // Verify the event structure
67
+ expect(signedContacts.kind).toBe(NostrEventKind.CONTACTS);
68
68
  });
69
69
  });
70
70
  describe('NIP-04: Encrypted Direct Messages', () => {
71
- test('Message Encryption and Decryption', async () => {
71
+ let keyPair;
72
+ beforeAll(async () => {
73
+ keyPair = await generateKeyPair();
74
+ });
75
+ it('Message Encryption and Decryption', async () => {
72
76
  const alice = await generateKeyPair();
73
77
  const bob = await generateKeyPair();
74
78
  const message = 'Secret message for testing';
75
79
  // Encrypt message from Alice to Bob
76
80
  const encrypted = await encrypt(message, bob.publicKey, alice.privateKey);
77
81
  expect(encrypted).not.toBe(message);
78
- // Bob decrypts message from Alice
82
+ // Decrypt message
79
83
  const decrypted = await decrypt(encrypted, alice.publicKey, bob.privateKey);
80
84
  expect(decrypted).toBe(message);
81
85
  });
82
- test('Encrypted DM Event Structure', async () => {
83
- const sender = await generateKeyPair();
86
+ it('Encrypted DM Event Structure', async () => {
84
87
  const recipient = await generateKeyPair();
85
- const message = 'Secret message';
86
- const encrypted = await encrypt(message, recipient.publicKey, sender.privateKey);
87
88
  const dmEvent = createEvent({
88
89
  kind: NostrEventKind.ENCRYPTED_DIRECT_MESSAGE,
89
- content: encrypted,
90
- tags: [['p', recipient.publicKey]],
91
- created_at: Math.floor(Date.now() / 1000)
90
+ content: 'Encrypted message',
91
+ tags: [['p', recipient.publicKey]]
92
92
  });
93
- const validation = validateEvent(dmEvent);
94
- expect(validation.isValid).toBe(true);
95
- expect(dmEvent.kind).toBe(NostrEventKind.ENCRYPTED_DIRECT_MESSAGE);
96
- expect(dmEvent.tags[0][0]).toBe('p');
97
- expect(dmEvent.tags[0][1]).toBe(recipient.publicKey);
93
+ const signedDM = await signEvent(dmEvent, keyPair.privateKey);
94
+ expect(signedDM).toHaveProperty('id');
95
+ expect(signedDM).toHaveProperty('sig');
96
+ expect(signedDM.kind).toBe(NostrEventKind.ENCRYPTED_DIRECT_MESSAGE);
97
+ expect(signedDM.tags[0][0]).toBe('p');
98
+ expect(signedDM.tags[0][1]).toBe(recipient.publicKey);
98
99
  });
99
100
  });
100
101
  describe('NIP-09: Event Deletion', () => {
101
- test('Event Deletion Structure', async () => {
102
+ it('Event Deletion Structure', async () => {
102
103
  const keyPair = await generateKeyPair();
103
104
  const originalEvent = await signEvent(createEvent({
104
105
  kind: NostrEventKind.TEXT_NOTE,
@@ -112,8 +113,10 @@ describe('NIP-09: Event Deletion', () => {
112
113
  tags: [['e', originalEvent.id]],
113
114
  created_at: Math.floor(Date.now() / 1000)
114
115
  });
115
- const validation = validateEvent(deleteEvent);
116
- expect(validation.isValid).toBe(true);
116
+ const signedDeleteEvent = await signEvent(deleteEvent, keyPair.privateKey);
117
+ expect(signedDeleteEvent).toHaveProperty('id');
118
+ expect(signedDeleteEvent).toHaveProperty('sig');
119
+ // Verify the event structure
117
120
  expect(deleteEvent.kind).toBe(NostrEventKind.DELETE);
118
121
  expect(deleteEvent.tags[0][0]).toBe('e');
119
122
  expect(deleteEvent.tags[0][1]).toBe(originalEvent.id);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import { webcrypto } from 'node:crypto';
2
+ // Configure crypto for test environment
3
+ if (typeof globalThis.crypto === 'undefined') {
4
+ globalThis.crypto = webcrypto;
5
+ }