{"ast":null,"code":"import _asyncToGenerator from \"/home/mleku/src/orly.dev/next/signer/node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js\";\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { NostrHelper } from '@common';\nimport { finalizeEvent, nip04, nip44, getPublicKey } from 'nostr-tools';\nimport { NWC_METHODS } from './types';\n/**\n * NWC Client for communicating with NIP-47 wallet services\n */\nexport class NwcClient {\n  connectionData;\n  ws = null;\n  connected = false;\n  pendingRequests = new Map();\n  subscriptionId = null;\n  conversationKey;\n  clientPubkey;\n  encryptionMode = 'nip44';\n  logCallback = null;\n  constructor(connectionData, logCallback) {\n    this.connectionData = connectionData;\n    this.logCallback = logCallback ?? null;\n    // Derive the conversation key for NIP-44 encryption\n    this.conversationKey = nip44.v2.utils.getConversationKey(NostrHelper.hex2bytes(connectionData.secret), connectionData.walletPubkey);\n    // Derive our public key from the secret\n    this.clientPubkey = getPublicKey(NostrHelper.hex2bytes(connectionData.secret));\n  }\n  log(level, message) {\n    if (this.logCallback) {\n      this.logCallback(level, message);\n    }\n  }\n  /**\n   * Connect to the NWC relay\n   */\n  connect() {\n    var _this = this;\n    return _asyncToGenerator(function* () {\n      if (_this.connected) {\n        return;\n      }\n      return new Promise((resolve, reject) => {\n        try {\n          _this.log('info', `Connecting to ${_this.connectionData.relayUrl}...`);\n          _this.ws = new WebSocket(_this.connectionData.relayUrl);\n          const timeout = setTimeout(() => {\n            _this.log('error', 'Connection timeout');\n            reject(new Error('Connection timeout'));\n            _this.disconnect();\n          }, 10000);\n          _this.ws.onopen = () => {\n            clearTimeout(timeout);\n            _this.connected = true;\n            _this.log('info', 'Connected to relay');\n            _this.subscribe();\n            resolve();\n          };\n          _this.ws.onerror = () => {\n            clearTimeout(timeout);\n            _this.log('error', 'WebSocket error');\n            reject(new Error('WebSocket error'));\n          };\n          _this.ws.onclose = () => {\n            _this.connected = false;\n            _this.subscriptionId = null;\n            // Reject all pending requests\n            for (const [, pending] of _this.pendingRequests) {\n              clearTimeout(pending.timeout);\n              pending.reject(new Error('Connection closed'));\n            }\n            _this.pendingRequests.clear();\n          };\n          _this.ws.onmessage = event => {\n            _this.handleMessage(event.data);\n          };\n        } catch (error) {\n          reject(error);\n        }\n      });\n    })();\n  }\n  /**\n   * Disconnect from the relay\n   */\n  disconnect() {\n    if (this.ws) {\n      if (this.subscriptionId) {\n        this.ws.send(JSON.stringify(['CLOSE', this.subscriptionId]));\n      }\n      this.ws.close();\n      this.ws = null;\n    }\n    this.connected = false;\n    this.subscriptionId = null;\n  }\n  /**\n   * Check if connected\n   */\n  isConnected() {\n    return this.connected && this.ws?.readyState === WebSocket.OPEN;\n  }\n  /**\n   * Get wallet info\n   */\n  getInfo() {\n    var _this2 = this;\n    return _asyncToGenerator(function* () {\n      const response = yield _this2.sendRequest({\n        method: NWC_METHODS.GET_INFO\n      });\n      if (response.error) {\n        throw new Error(response.error.message);\n      }\n      return response.result;\n    })();\n  }\n  /**\n   * Get wallet balance\n   */\n  getBalance() {\n    var _this3 = this;\n    return _asyncToGenerator(function* () {\n      const response = yield _this3.sendRequest({\n        method: NWC_METHODS.GET_BALANCE\n      });\n      if (response.error) {\n        throw new Error(response.error.message);\n      }\n      return response.result;\n    })();\n  }\n  /**\n   * Pay a Lightning invoice\n   */\n  payInvoice(params) {\n    var _this4 = this;\n    return _asyncToGenerator(function* () {\n      const response = yield _this4.sendRequest({\n        method: NWC_METHODS.PAY_INVOICE,\n        params: params\n      });\n      if (response.error) {\n        throw new Error(response.error.message);\n      }\n      return response.result;\n    })();\n  }\n  /**\n   * Create a Lightning invoice\n   */\n  makeInvoice(params) {\n    var _this5 = this;\n    return _asyncToGenerator(function* () {\n      const response = yield _this5.sendRequest({\n        method: NWC_METHODS.MAKE_INVOICE,\n        params: params\n      });\n      if (response.error) {\n        throw new Error(response.error.message);\n      }\n      return response.result;\n    })();\n  }\n  /**\n   * List transaction history\n   */\n  listTransactions(params) {\n    var _this6 = this;\n    return _asyncToGenerator(function* () {\n      const response = yield _this6.sendRequest({\n        method: NWC_METHODS.LIST_TRANSACTIONS,\n        params: params\n      });\n      if (response.error) {\n        throw new Error(response.error.message);\n      }\n      return response.result;\n    })();\n  }\n  /**\n   * Encrypt content using current encryption mode\n   */\n  encryptContent(plaintext) {\n    var _this7 = this;\n    return _asyncToGenerator(function* () {\n      if (_this7.encryptionMode === 'nip04') {\n        return nip04.encrypt(_this7.connectionData.secret, _this7.connectionData.walletPubkey, plaintext);\n      } else {\n        return nip44.v2.encrypt(plaintext, _this7.conversationKey);\n      }\n    })();\n  }\n  /**\n   * Send a request to the wallet\n   */\n  sendRequest(_x) {\n    var _this8 = this;\n    return _asyncToGenerator(function* (request, timeoutMs = 30000, isRetry = false) {\n      if (!_this8.isConnected()) {\n        yield _this8.connect();\n      }\n      // Encrypt the request content\n      const plaintext = JSON.stringify(request);\n      _this8.log('info', `Sending ${request.method} request (using ${_this8.encryptionMode.toUpperCase()})`);\n      const ciphertext = yield _this8.encryptContent(plaintext);\n      // Create the NIP-47 request event (kind 23194)\n      const eventTemplate = {\n        kind: 23194,\n        created_at: Math.floor(Date.now() / 1000),\n        tags: [['p', _this8.connectionData.walletPubkey]],\n        content: ciphertext\n      };\n      // Sign with the client secret\n      const signedEvent = finalizeEvent(eventTemplate, NostrHelper.hex2bytes(_this8.connectionData.secret));\n      return new Promise((resolve, reject) => {\n        const timeout = setTimeout(() => {\n          _this8.pendingRequests.delete(signedEvent.id);\n          _this8.log('error', `Request timeout for ${request.method}`);\n          reject(new Error('Request timeout'));\n        }, timeoutMs);\n        _this8.pendingRequests.set(signedEvent.id, {\n          resolve,\n          reject,\n          timeout,\n          request,\n          isRetry\n        });\n        // Send the event\n        _this8.ws.send(JSON.stringify(['EVENT', signedEvent]));\n      });\n    }).apply(this, arguments);\n  }\n  /**\n   * Retry a request with NIP-04 encryption\n   */\n  retryWithNip04(request) {\n    var _this9 = this;\n    return _asyncToGenerator(function* () {\n      _this9.log('warn', 'Retrying with NIP-04 encryption...');\n      _this9.encryptionMode = 'nip04';\n      return _this9.sendRequest(request, 30000, true);\n    })();\n  }\n  /**\n   * Subscribe to response events from the wallet\n   */\n  subscribe() {\n    if (!this.ws || !this.connected) {\n      return;\n    }\n    // Generate a subscription ID\n    this.subscriptionId = Math.random().toString(36).substring(2, 15);\n    // Subscribe to kind 23195 (response) events addressed to us\n    const filter = {\n      kinds: [23195],\n      '#p': [this.clientPubkey],\n      since: Math.floor(Date.now() / 1000) - 10 // Last 10 seconds\n    };\n    this.ws.send(JSON.stringify(['REQ', this.subscriptionId, filter]));\n  }\n  /**\n   * Handle incoming WebSocket messages\n   */\n  handleMessage(data) {\n    try {\n      const message = JSON.parse(data);\n      if (!Array.isArray(message)) {\n        return;\n      }\n      const [type, ...rest] = message;\n      switch (type) {\n        case 'EVENT':\n          this.handleEvent(rest[1]);\n          break;\n        case 'OK':\n          // Event was received by relay\n          break;\n        case 'EOSE':\n          // End of stored events\n          break;\n        case 'NOTICE':\n          this.log('warn', `Relay notice: ${rest[0]}`);\n          break;\n      }\n    } catch (error) {\n      this.log('error', `Error parsing message: ${error.message}`);\n    }\n  }\n  /**\n   * Check if an error indicates a decryption/encryption problem\n   */\n  isEncryptionError(errorMsg) {\n    const lowerMsg = errorMsg.toLowerCase();\n    return lowerMsg.includes('decrypt') || lowerMsg.includes('initialization vector') || lowerMsg.includes('iv') || lowerMsg.includes('encrypt') || lowerMsg.includes('cipher') || lowerMsg.includes('parse');\n  }\n  /**\n   * Handle an incoming event (response from wallet)\n   */\n  handleEvent(event) {\n    var _this0 = this;\n    return _asyncToGenerator(function* () {\n      if (!event || event.kind !== 23195) {\n        return;\n      }\n      // Check if this event is from the wallet\n      if (event.pubkey !== _this0.connectionData.walletPubkey) {\n        return;\n      }\n      // Find the request ID from the 'e' tag\n      const eTag = event.tags?.find(t => t[0] === 'e');\n      if (!eTag) {\n        return;\n      }\n      const requestId = eTag[1];\n      const pending = _this0.pendingRequests.get(requestId);\n      if (!pending) {\n        // Response for unknown request (might be old or from another session)\n        return;\n      }\n      // Clear the timeout and remove from pending\n      clearTimeout(pending.timeout);\n      _this0.pendingRequests.delete(requestId);\n      try {\n        // Try to decrypt the response\n        let decrypted;\n        // First, check if content looks like plain JSON (unencrypted error)\n        if (event.content.startsWith('{') || event.content.startsWith('\"')) {\n          // Might be unencrypted error response\n          try {\n            const parsed = JSON.parse(event.content);\n            // If it has an error field, this is an unencrypted error response\n            if (parsed.error) {\n              _this0.log('error', `Wallet error: ${parsed.error.message || JSON.stringify(parsed.error)}`);\n              // Check if it's an encryption error and we haven't retried yet\n              const errorMsg = parsed.error.message || JSON.stringify(parsed.error);\n              if (!pending.isRetry && _this0.encryptionMode === 'nip44' && _this0.isEncryptionError(errorMsg)) {\n                _this0.log('warn', 'Wallet returned encryption error, switching to NIP-04');\n                try {\n                  const retryResponse = yield _this0.retryWithNip04(pending.request);\n                  pending.resolve(retryResponse);\n                  return;\n                } catch (retryError) {\n                  pending.reject(retryError);\n                  return;\n                }\n              }\n              pending.resolve(parsed);\n              return;\n            }\n          } catch {\n            // Not valid JSON, continue with decryption\n          }\n        }\n        // Detect encryption format and decrypt\n        // NIP-04 format contains \"?iv=\" in the ciphertext\n        if (event.content.includes('?iv=')) {\n          _this0.log('info', 'Decrypting response (NIP-04 format)');\n          decrypted = yield nip04.decrypt(_this0.connectionData.secret, _this0.connectionData.walletPubkey, event.content);\n        } else {\n          _this0.log('info', 'Decrypting response (NIP-44 format)');\n          try {\n            decrypted = nip44.v2.decrypt(event.content, _this0.conversationKey);\n          } catch (nip44Error) {\n            // NIP-44 decryption failed, maybe it's NIP-04 without standard format?\n            // Try NIP-04 as fallback\n            _this0.log('warn', `NIP-44 decryption failed: ${nip44Error.message}, trying NIP-04...`);\n            try {\n              decrypted = yield nip04.decrypt(_this0.connectionData.secret, _this0.connectionData.walletPubkey, event.content);\n            } catch {\n              // Both failed, throw original error\n              throw nip44Error;\n            }\n          }\n        }\n        const response = JSON.parse(decrypted);\n        // Check if the decrypted response contains an encryption error\n        if (response.error) {\n          const errorMsg = response.error.message || '';\n          if (!pending.isRetry && _this0.encryptionMode === 'nip44' && _this0.isEncryptionError(errorMsg)) {\n            _this0.log('warn', `Wallet returned encryption error: ${errorMsg}, retrying with NIP-04`);\n            try {\n              const retryResponse = yield _this0.retryWithNip04(pending.request);\n              pending.resolve(retryResponse);\n              return;\n            } catch (retryError) {\n              pending.reject(retryError);\n              return;\n            }\n          }\n          _this0.log('error', `Wallet error: ${errorMsg}`);\n        } else {\n          _this0.log('info', 'Request successful');\n        }\n        pending.resolve(response);\n      } catch (error) {\n        const errorMsg = error.message;\n        _this0.log('error', `Failed to decrypt response: ${errorMsg}`);\n        // If this is an encryption error and we haven't retried, try NIP-04\n        if (!pending.isRetry && _this0.encryptionMode === 'nip44' && _this0.isEncryptionError(errorMsg)) {\n          _this0.log('warn', 'Decryption failed, retrying with NIP-04 encryption');\n          try {\n            const retryResponse = yield _this0.retryWithNip04(pending.request);\n            pending.resolve(retryResponse);\n            return;\n          } catch (retryError) {\n            pending.reject(retryError);\n            return;\n          }\n        }\n        pending.reject(new Error(`Failed to decrypt response: ${errorMsg}`));\n      }\n    })();\n  }\n}","map":null,"metadata":{},"sourceType":"module","externalDependencies":[]}