f72ae21b49475ce567732e123c43a70be6549d81044f4c1ac3c68a0d2fc73860.json raw

   1  {"ast":null,"code":"import { bech32 } from '@scure/base';\nimport * as utils from '@noble/curves/abstract/utils';\nimport { getPublicKey, generateSecretKey } from 'nostr-tools';\n/**\n * Value object encapsulating a Nostr keypair.\n * Provides type-safe access to public key operations while protecting the private key.\n *\n * The private key is never exposed directly - all operations that need it\n * are performed through methods on this class.\n */\nexport class NostrKeyPair {\n  _privateKeyHex;\n  _publicKeyHex;\n  constructor(privateKeyHex, publicKeyHex) {\n    this._privateKeyHex = privateKeyHex;\n    this._publicKeyHex = publicKeyHex;\n  }\n  /**\n   * Generate a new random keypair.\n   */\n  static generate() {\n    const privateKeyBytes = generateSecretKey();\n    const privateKeyHex = utils.bytesToHex(privateKeyBytes);\n    const publicKeyHex = getPublicKey(privateKeyBytes);\n    return new NostrKeyPair(privateKeyHex, publicKeyHex);\n  }\n  /**\n   * Create a keypair from an existing private key.\n   * Accepts either hex or nsec format.\n   *\n   * @throws InvalidNostrKeyError if the key is invalid\n   */\n  static fromPrivateKey(privateKey) {\n    try {\n      const hex = NostrKeyPair.normalizeToHex(privateKey);\n      NostrKeyPair.validateHexKey(hex);\n      const publicKeyHex = NostrKeyPair.derivePublicKey(hex);\n      return new NostrKeyPair(hex, publicKeyHex);\n    } catch (error) {\n      throw new InvalidNostrKeyError(`Invalid private key: ${error instanceof Error ? error.message : 'unknown error'}`);\n    }\n  }\n  /**\n   * Reconstitute a keypair from storage.\n   * Assumes the stored hex is valid (from trusted source).\n   */\n  static fromStorage(privateKeyHex) {\n    const publicKeyHex = NostrKeyPair.derivePublicKey(privateKeyHex);\n    return new NostrKeyPair(privateKeyHex, publicKeyHex);\n  }\n  /**\n   * Get the public key in hex format.\n   */\n  get publicKeyHex() {\n    return this._publicKeyHex;\n  }\n  /**\n   * Get the public key in npub (bech32) format.\n   */\n  get npub() {\n    const data = utils.hexToBytes(this._publicKeyHex);\n    const words = bech32.toWords(data);\n    return bech32.encode('npub', words, 5000);\n  }\n  /**\n   * Get the private key in nsec (bech32) format.\n   * Use with caution - only for display/export purposes.\n   */\n  get nsec() {\n    const data = utils.hexToBytes(this._privateKeyHex);\n    const words = bech32.toWords(data);\n    return bech32.encode('nsec', words, 5000);\n  }\n  /**\n   * Get the private key bytes for cryptographic operations.\n   * Internal use only - required for signing and encryption.\n   */\n  getPrivateKeyBytes() {\n    return utils.hexToBytes(this._privateKeyHex);\n  }\n  /**\n   * Get the private key hex for storage.\n   * This should only be used when persisting to encrypted storage.\n   */\n  toStorageHex() {\n    return this._privateKeyHex;\n  }\n  /**\n   * Check if this keypair has the same public key as another.\n   */\n  hasSamePublicKey(other) {\n    return this._publicKeyHex === other._publicKeyHex;\n  }\n  /**\n   * Check if this keypair matches a given public key.\n   */\n  matchesPublicKey(publicKeyHex) {\n    return this._publicKeyHex === publicKeyHex;\n  }\n  /**\n   * Value equality based on public key.\n   * Two keypairs are equal if they represent the same identity.\n   */\n  equals(other) {\n    return this._publicKeyHex === other._publicKeyHex;\n  }\n  // ─────────────────────────────────────────────────────────────────────────\n  // Private helpers\n  // ─────────────────────────────────────────────────────────────────────────\n  static normalizeToHex(privateKey) {\n    if (privateKey.startsWith('nsec')) {\n      return NostrKeyPair.nsecToHex(privateKey);\n    }\n    return privateKey;\n  }\n  static nsecToHex(nsec) {\n    const {\n      prefix,\n      words\n    } = bech32.decode(nsec, 5000);\n    if (prefix !== 'nsec') {\n      throw new Error('Invalid nsec prefix');\n    }\n    const data = new Uint8Array(bech32.fromWords(words));\n    return utils.bytesToHex(data);\n  }\n  static validateHexKey(hex) {\n    if (!/^[0-9a-fA-F]{64}$/.test(hex)) {\n      throw new Error('Private key must be 64 hex characters');\n    }\n  }\n  static derivePublicKey(privateKeyHex) {\n    const privateKeyBytes = utils.hexToBytes(privateKeyHex);\n    return getPublicKey(privateKeyBytes);\n  }\n}\n/**\n * Error thrown when a Nostr key is invalid.\n */\nexport class InvalidNostrKeyError extends Error {\n  constructor(message) {\n    super(message);\n    this.name = 'InvalidNostrKeyError';\n  }\n}\n/**\n * Utility functions for public key operations (no private key needed).\n */\nexport class NostrPublicKey {\n  _hex;\n  constructor(_hex) {\n    this._hex = _hex;\n  }\n  /**\n   * Create from hex or npub format.\n   */\n  static from(publicKey) {\n    if (publicKey.startsWith('npub')) {\n      const hex = NostrPublicKey.npubToHex(publicKey);\n      return new NostrPublicKey(hex);\n    }\n    NostrPublicKey.validateHex(publicKey);\n    return new NostrPublicKey(publicKey);\n  }\n  get hex() {\n    return this._hex;\n  }\n  get npub() {\n    const data = utils.hexToBytes(this._hex);\n    const words = bech32.toWords(data);\n    return bech32.encode('npub', words, 5000);\n  }\n  /**\n   * Get a shortened display version of the public key.\n   */\n  shortened(prefixLength = 8, suffixLength = 4) {\n    const npub = this.npub;\n    return `${npub.slice(0, prefixLength)}...${npub.slice(-suffixLength)}`;\n  }\n  equals(other) {\n    return this._hex === other._hex;\n  }\n  toString() {\n    return this._hex;\n  }\n  static npubToHex(npub) {\n    const {\n      prefix,\n      words\n    } = bech32.decode(npub, 5000);\n    if (prefix !== 'npub') {\n      throw new InvalidNostrKeyError('Invalid npub prefix');\n    }\n    const data = new Uint8Array(bech32.fromWords(words));\n    return utils.bytesToHex(data);\n  }\n  static validateHex(hex) {\n    if (!/^[0-9a-fA-F]{64}$/.test(hex)) {\n      throw new InvalidNostrKeyError('Public key must be 64 hex characters');\n    }\n  }\n}","map":null,"metadata":{},"sourceType":"module","externalDependencies":[]}