Pubkey.ts raw
1 import { nip19 } from 'nostr-tools'
2 import { InvalidPubkeyError } from '../errors'
3
4 /**
5 * Value object representing a Nostr public key.
6 * Immutable, self-validating, with equality by value.
7 */
8 export class Pubkey {
9 private constructor(private readonly _value: string) {}
10
11 /**
12 * Create a Pubkey from a 64-character hex string.
13 * @throws InvalidPubkeyError if the hex is invalid
14 */
15 static fromHex(hex: string): Pubkey {
16 if (!/^[0-9a-f]{64}$/.test(hex)) {
17 throw new InvalidPubkeyError(hex)
18 }
19 return new Pubkey(hex)
20 }
21
22 /**
23 * Create a Pubkey from an npub bech32 string.
24 * @throws InvalidPubkeyError if the npub is invalid
25 */
26 static fromNpub(npub: string): Pubkey {
27 try {
28 const { type, data } = nip19.decode(npub)
29 if (type !== 'npub') {
30 throw new InvalidPubkeyError(npub)
31 }
32 return new Pubkey(data)
33 } catch (e) {
34 if (e instanceof InvalidPubkeyError) throw e
35 throw new InvalidPubkeyError(npub)
36 }
37 }
38
39 /**
40 * Create a Pubkey from an nprofile bech32 string.
41 * @throws InvalidPubkeyError if the nprofile is invalid
42 */
43 static fromNprofile(nprofile: string): Pubkey {
44 try {
45 const { type, data } = nip19.decode(nprofile)
46 if (type !== 'nprofile') {
47 throw new InvalidPubkeyError(nprofile)
48 }
49 return new Pubkey(data.pubkey)
50 } catch (e) {
51 if (e instanceof InvalidPubkeyError) throw e
52 throw new InvalidPubkeyError(nprofile)
53 }
54 }
55
56 /**
57 * Try to create a Pubkey from any string format (hex, npub, nprofile).
58 * Returns null if the string is invalid.
59 */
60 static tryFromString(input: string): Pubkey | null {
61 try {
62 if (input.startsWith('npub1')) {
63 return Pubkey.fromNpub(input)
64 }
65 if (input.startsWith('nprofile1')) {
66 return Pubkey.fromNprofile(input)
67 }
68 return Pubkey.fromHex(input)
69 } catch {
70 return null
71 }
72 }
73
74 /**
75 * Check if a string is a valid pubkey (hex format only).
76 */
77 static isValidHex(value: string): boolean {
78 return /^[0-9a-f]{64}$/.test(value)
79 }
80
81 /** The raw 64-character hex value */
82 get hex(): string {
83 return this._value
84 }
85
86 /** The bech32-encoded npub representation */
87 get npub(): string {
88 return nip19.npubEncode(this._value)
89 }
90
91 /** A shortened display format: first 8 + ... + last 4 characters */
92 get formatted(): string {
93 return `${this._value.slice(0, 8)}...${this._value.slice(-4)}`
94 }
95
96 /** A shorter display format for the npub */
97 formatNpub(length = 12): string {
98 const npub = this.npub
99 if (length < 12) length = 12
100 if (length >= 63) return npub
101
102 const prefixLength = Math.floor((length - 5) / 2) + 5
103 const suffixLength = length - prefixLength
104 return npub.slice(0, prefixLength) + '...' + npub.slice(-suffixLength)
105 }
106
107 /** Check equality with another Pubkey */
108 equals(other: Pubkey): boolean {
109 return this._value === other._value
110 }
111
112 /** Returns the hex representation */
113 toString(): string {
114 return this._value
115 }
116
117 /** For JSON serialization */
118 toJSON(): string {
119 return this._value
120 }
121 }
122