adapters.ts raw

   1  /**
   2   * Adapter functions for gradual migration from legacy code to Relay domain objects.
   3   */
   4  
   5  import { Event } from 'nostr-tools'
   6  import { RelayUrl, tryToRelayUrl } from '../shared'
   7  import { RelayList, RelayScope } from './RelayList'
   8  import { RelaySet } from './RelaySet'
   9  import { FavoriteRelays } from './FavoriteRelays'
  10  
  11  // ============================================================================
  12  // RelayList Adapters
  13  // ============================================================================
  14  
  15  /**
  16   * Convert a Nostr event to a RelayList domain object
  17   */
  18  export const toRelayList = (event: Event, filterOutOnion = false): RelayList => {
  19    return RelayList.fromEvent(event, filterOutOnion)
  20  }
  21  
  22  /**
  23   * Try to create a RelayList from an event, returns null if invalid
  24   */
  25  export const tryToRelayList = (
  26    event: Event | null | undefined,
  27    filterOutOnion = false
  28  ): RelayList | null => {
  29    if (!event) return null
  30    try {
  31      return RelayList.fromEvent(event, filterOutOnion)
  32    } catch {
  33      return null
  34    }
  35  }
  36  
  37  /**
  38   * Convert a RelayList to the legacy TRelayList format
  39   */
  40  export const fromRelayListToLegacy = (
  41    relayList: RelayList
  42  ): {
  43    read: string[]
  44    write: string[]
  45    originalRelays: Array<{ url: string; scope: RelayScope }>
  46  } => {
  47    return relayList.toLegacyFormat()
  48  }
  49  
  50  /**
  51   * Create a RelayList from legacy format
  52   */
  53  export const toRelayListFromLegacy = (
  54    ownerHex: string,
  55    legacy: {
  56      read: string[]
  57      write: string[]
  58      originalRelays?: Array<{ url: string; scope: RelayScope }>
  59    }
  60  ): RelayList | null => {
  61    const { Pubkey } = require('../shared')
  62    const owner = Pubkey.tryFromString(ownerHex)
  63    if (!owner) return null
  64  
  65    const relayList = RelayList.empty(owner)
  66  
  67    if (legacy.originalRelays) {
  68      for (const { url, scope } of legacy.originalRelays) {
  69        relayList.setRelayUrl(url, scope)
  70      }
  71    } else {
  72      // Reconstruct from read/write arrays
  73      const readSet = new Set(legacy.read)
  74      const writeSet = new Set(legacy.write)
  75      const allUrls = new Set([...legacy.read, ...legacy.write])
  76  
  77      for (const url of allUrls) {
  78        const isRead = readSet.has(url)
  79        const isWrite = writeSet.has(url)
  80        let scope: RelayScope = 'both'
  81        if (isRead && !isWrite) scope = 'read'
  82        else if (!isRead && isWrite) scope = 'write'
  83  
  84        relayList.setRelayUrl(url, scope)
  85      }
  86    }
  87  
  88    return relayList
  89  }
  90  
  91  // ============================================================================
  92  // RelaySet Adapters
  93  // ============================================================================
  94  
  95  /**
  96   * Convert a Nostr event to a RelaySet domain object
  97   */
  98  export const toRelaySet = (event: Event): RelaySet => {
  99    return RelaySet.fromEvent(event)
 100  }
 101  
 102  /**
 103   * Try to create a RelaySet from an event, returns null if invalid
 104   */
 105  export const tryToRelaySet = (event: Event | null | undefined): RelaySet | null => {
 106    if (!event) return null
 107    try {
 108      return RelaySet.fromEvent(event)
 109    } catch {
 110      return null
 111    }
 112  }
 113  
 114  /**
 115   * Convert a RelaySet to the legacy TRelaySet format
 116   */
 117  export const fromRelaySetToLegacy = (
 118    relaySet: RelaySet,
 119    pubkey: string
 120  ): {
 121    id: string
 122    aTag: string[]
 123    name: string
 124    relayUrls: string[]
 125  } => {
 126    return {
 127      id: relaySet.id,
 128      aTag: relaySet.toATag(pubkey),
 129      name: relaySet.name,
 130      relayUrls: relaySet.getRelayUrls()
 131    }
 132  }
 133  
 134  /**
 135   * Create a RelaySet from legacy format
 136   */
 137  export const toRelaySetFromLegacy = (legacy: {
 138    id: string
 139    name: string
 140    relayUrls: string[]
 141  }): RelaySet => {
 142    return RelaySet.createWithRelays(legacy.name, legacy.relayUrls, legacy.id)
 143  }
 144  
 145  // ============================================================================
 146  // FavoriteRelays Adapters
 147  // ============================================================================
 148  
 149  /**
 150   * Convert events to a FavoriteRelays domain object
 151   */
 152  export const toFavoriteRelays = (
 153    event: Event,
 154    relaySetEvents: Event[] = []
 155  ): FavoriteRelays => {
 156    const relaySets = relaySetEvents.map((e) => tryToRelaySet(e)).filter(Boolean) as RelaySet[]
 157    return FavoriteRelays.fromEvent(event, relaySets)
 158  }
 159  
 160  /**
 161   * Try to create FavoriteRelays from an event
 162   */
 163  export const tryToFavoriteRelays = (
 164    event: Event | null | undefined,
 165    relaySetEvents: Event[] = []
 166  ): FavoriteRelays | null => {
 167    if (!event) return null
 168    try {
 169      return toFavoriteRelays(event, relaySetEvents)
 170    } catch {
 171      return null
 172    }
 173  }
 174  
 175  // ============================================================================
 176  // Utility Adapters
 177  // ============================================================================
 178  
 179  /**
 180   * Convert an array of URL strings to RelayUrl objects
 181   * Skips invalid URLs
 182   */
 183  export const urlsToRelayUrls = (urls: string[]): RelayUrl[] => {
 184    return urls.map((u) => tryToRelayUrl(u)).filter((r): r is RelayUrl => r !== null)
 185  }
 186  
 187  /**
 188   * Convert an array of RelayUrl objects to URL strings
 189   */
 190  export const relayUrlsToStrings = (relays: RelayUrl[]): string[] => {
 191    return relays.map((r) => r.value)
 192  }
 193  
 194  /**
 195   * Normalize a relay URL string
 196   * Returns null if invalid
 197   */
 198  export const normalizeRelayUrl = (url: string): string | null => {
 199    const relay = tryToRelayUrl(url)
 200    return relay ? relay.value : null
 201  }
 202  
 203  /**
 204   * Check if a string is a valid WebSocket URL
 205   */
 206  export const isValidRelayUrl = (url: string): boolean => {
 207    return RelayUrl.isValid(url)
 208  }
 209