Skip to content

Commit 8af3e58

Browse files
committed
feat: improved NIP-19 implementation
- Updated nostr-crypto-utils to version 0.4.5 - Improved NIP-19 implementation using updated exports - Enhanced error messages and logging - Streamlined type imports - Added CHANGELOG.md - Version bump to 0.6.2
1 parent bd5f9e8 commit 8af3e58

File tree

5 files changed

+82
-76
lines changed

5 files changed

+82
-76
lines changed

CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
## [0.6.2] - 2024-12-29
11+
12+
### Changed
13+
- Updated nostr-crypto-utils to version 0.4.5
14+
- Improved NIP-19 implementation using updated nostr-crypto-utils exports
15+
- Enhanced error messages with more detailed logging
16+
- Streamlined type imports from nostr-crypto-utils
17+
18+
### Fixed
19+
- Removed duplicate Nip19Data type definition
20+
- Improved error handling in NIP-19 functions
21+
- Better error messages in NIP-19 encoding/decoding functions

package-lock.json

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "nostr-nsec-seedphrase",
3-
"version": "0.6.1",
3+
"version": "0.6.2",
44
"description": "A comprehensive TypeScript library for Nostr key management with BIP39 seed phrases, supporting NIP-01, NIP-06, NIP-19, and NIP-26. Features include key generation, event signing, bech32 encoding/decoding, and secure cryptographic operations.",
55
"type": "module",
66
"main": "./dist/index.js",
@@ -60,7 +60,7 @@
6060
"@scure/base": "^1.2.1",
6161
"bech32": "^2.0.0",
6262
"bip39": "^3.1.0",
63-
"nostr-crypto-utils": "^0.4.2",
63+
"nostr-crypto-utils": "^0.4.5",
6464
"pino": "^8.17.2"
6565
},
6666
"devDependencies": {

src/nips/nip-19.ts

Lines changed: 34 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,9 @@
77
* @see https://github.com/nostr-protocol/nips/blob/master/19.md
88
*/
99

10-
import { bech32 } from '@scure/base';
11-
import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
12-
import { sha256 } from '@noble/hashes/sha256';
1310
import { logger } from '../utils/logger.js';
14-
import type { Bech32Result } from '../types/index.js';
15-
16-
/**
17-
* Valid bech32 prefix types for Nostr entities
18-
* @typedef {('npub1' | 'nsec1' | 'note1')} Bech32Prefix
19-
*/
20-
type Bech32Prefix = 'npub1' | 'nsec1' | 'note1';
21-
22-
/**
23-
* Ensures the input string has a valid bech32 format by adding '1' if missing
24-
* @param {string} str - Input string to format
25-
* @returns {string} Properly formatted bech32 string
26-
* @example
27-
* ensureBech32Format('npub') // returns 'npub1'
28-
* ensureBech32Format('npub1abc') // returns 'npub1abc'
29-
*/
30-
function ensureBech32Format(str: string): `${string}1${string}` {
31-
return str.includes('1') ? str as `${string}1${string}` : `${str}1`;
32-
}
11+
import { npubEncode, nsecEncode, noteEncode, decode as nip19Decode } from 'nostr-crypto-utils';
12+
import type { Nip19Data } from 'nostr-crypto-utils';
3313

3414
/**
3515
* Encodes a public key into npub format
@@ -38,17 +18,14 @@ function ensureBech32Format(str: string): `${string}1${string}` {
3818
* @throws {Error} If encoding fails
3919
* @example
4020
* const npub = hexToNpub('3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d');
41-
* // returns 'npub1...'
21+
* returns 'npub1...'
4222
*/
4323
export function hexToNpub(hex: string): string {
4424
try {
45-
const bytes = hexToBytes(hex);
46-
const words = bech32.toWords(bytes);
47-
const encoded = bech32.encode('npub1' as Bech32Prefix, words);
48-
return encoded;
25+
return npubEncode(hex);
4926
} catch (error) {
50-
logger.error('Failed to encode npub:', error);
51-
throw new Error('Failed to encode npub');
27+
logger.error({ error, hex }, 'Failed to encode public key to npub');
28+
throw error;
5229
}
5330
}
5431

@@ -60,17 +37,14 @@ export function hexToNpub(hex: string): string {
6037
* @security This function handles sensitive private key data
6138
* @example
6239
* const nsec = hexToNsec('3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d');
63-
* // returns 'nsec1...'
40+
* returns 'nsec1...'
6441
*/
6542
export function hexToNsec(hex: string): string {
6643
try {
67-
const bytes = hexToBytes(hex);
68-
const words = bech32.toWords(bytes);
69-
const encoded = bech32.encode('nsec1' as Bech32Prefix, words);
70-
return encoded;
44+
return nsecEncode(hex);
7145
} catch (error) {
72-
logger.error('Failed to encode nsec:', error);
73-
throw new Error('Failed to encode nsec');
46+
logger.error({ error, hex }, 'Failed to encode private key to nsec');
47+
throw error;
7448
}
7549
}
7650

@@ -81,38 +55,32 @@ export function hexToNsec(hex: string): string {
8155
* @throws {Error} If encoding fails
8256
* @example
8357
* const note = hexToNote('3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d');
84-
* // returns 'note1...'
58+
* returns 'note1...'
8559
*/
8660
export function hexToNote(hex: string): string {
8761
try {
88-
const bytes = hexToBytes(hex);
89-
const words = bech32.toWords(bytes);
90-
const encoded = bech32.encode('note1' as Bech32Prefix, words);
91-
return encoded;
62+
return noteEncode(hex);
9263
} catch (error) {
93-
logger.error('Failed to encode note:', error);
94-
throw new Error('Failed to encode note');
64+
logger.error({ error, hex }, 'Failed to encode event ID to note');
65+
throw error;
9566
}
9667
}
9768

9869
/**
9970
* Decodes a bech32-encoded Nostr entity
10071
* @param {string} str - The bech32-encoded string (npub1, nsec1, or note1)
101-
* @returns {Bech32Result} Object containing the decoded type and data
72+
* @returns {Nip19Data} Object containing the decoded type and data
10273
* @throws {Error} If decoding fails
10374
* @example
10475
* const result = decode('npub1...');
105-
* // returns { type: 'npub', data: Uint8Array }
76+
* returns { type: 'npub', data: '...' }
10677
*/
107-
export function decode(str: string): Bech32Result {
78+
export function decode(str: string): Nip19Data {
10879
try {
109-
const formattedStr = ensureBech32Format(str);
110-
const { prefix, words } = bech32.decode(formattedStr);
111-
const data = new Uint8Array(bech32.fromWords(words));
112-
return { type: prefix.replace(/1$/, ''), data };
80+
return nip19Decode(str);
11381
} catch (error) {
114-
logger.error('Failed to decode bech32:', error);
115-
throw new Error('Failed to decode bech32');
82+
logger.error({ error, str }, 'Failed to decode bech32-encoded string');
83+
throw error;
11684
}
11785
}
11886

@@ -124,18 +92,18 @@ export function decode(str: string): Bech32Result {
12492
* @security This function handles sensitive private key data
12593
* @example
12694
* const hex = nsecToHex('nsec1...');
127-
* // returns '3bf0c63f...'
95+
* returns '3bf0c63f...'
12896
*/
12997
export function nsecToHex(nsec: string): string {
13098
try {
131-
const { type, data } = decode(nsec);
132-
if (type !== 'nsec') {
133-
throw new Error('Invalid nsec format');
99+
const decoded = decode(nsec);
100+
if (decoded.type !== 'nsec') {
101+
throw new Error(`Invalid nsec format: ${nsec}`);
134102
}
135-
return bytesToHex(data);
103+
return decoded.data;
136104
} catch (error) {
137-
logger.error('Failed to convert nsec to hex:', error);
138-
throw new Error('Failed to convert nsec to hex');
105+
logger.error({ error, nsec }, 'Failed to convert nsec to hex');
106+
throw error;
139107
}
140108
}
141109

@@ -146,17 +114,17 @@ export function nsecToHex(nsec: string): string {
146114
* @throws {Error} If conversion fails or input is invalid
147115
* @example
148116
* const hex = npubToHex('npub1...');
149-
* // returns '3bf0c63f...'
117+
* returns '3bf0c63f...'
150118
*/
151119
export function npubToHex(npub: string): string {
152120
try {
153-
const { type, data } = decode(npub);
154-
if (type !== 'npub') {
155-
throw new Error('Invalid npub format');
121+
const decoded = decode(npub);
122+
if (decoded.type !== 'npub') {
123+
throw new Error(`Invalid npub format: ${npub}`);
156124
}
157-
return bytesToHex(data);
125+
return decoded.data;
158126
} catch (error) {
159-
logger.error('Failed to convert npub to hex:', error);
160-
throw new Error('Failed to convert npub to hex');
127+
logger.error({ error, npub }, 'Failed to convert npub to hex');
128+
throw error;
161129
}
162130
}

src/types/index.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,19 @@
1-
export * from './keys.js';
2-
export type { Tag, UnsignedEvent, NostrEvent } from './events.js';
1+
/**
2+
* @module types
3+
* @description Type definitions for the nostr-nsec-seedphrase library
4+
*/
5+
6+
// Export events types
7+
export type {
8+
NostrEvent,
9+
ValidationResult
10+
} from './events.js';
11+
12+
// Export key types
13+
export type {
14+
KeyPair,
15+
PublicKeyDetails
16+
} from './keys.js';
17+
18+
// Import and re-export Nip19Data type from nostr-crypto-utils
19+
export type { Nip19Data } from 'nostr-crypto-utils';

0 commit comments

Comments
 (0)