nrc-types.ts raw

   1  import { Filter, Event } from 'nostr-tools'
   2  import { ISigner } from '@/types'
   3  
   4  // NRC Event Kinds
   5  export const KIND_NRC_REQUEST = 24891
   6  export const KIND_NRC_RESPONSE = 24892
   7  
   8  // Session types
   9  export interface NRCSession {
  10    id: string
  11    clientPubkey: string
  12    conversationKey?: Uint8Array // Optional - only set when using direct key access
  13    deviceName?: string
  14    createdAt: number
  15    lastActivity: number
  16    subscriptions: Map<string, NRCSubscription>
  17  }
  18  
  19  export interface NRCSubscription {
  20    id: string
  21    filters: Filter[]
  22    createdAt: number
  23    eventCount: number
  24    eoseSent: boolean
  25  }
  26  
  27  // Message types (encrypted content)
  28  export interface RequestMessage {
  29    type: 'REQ' | 'CLOSE' | 'EVENT' | 'COUNT' | 'IDS'
  30    payload: unknown[]
  31  }
  32  
  33  export interface ResponseMessage {
  34    type: 'EVENT' | 'EOSE' | 'OK' | 'NOTICE' | 'CLOSED' | 'COUNT' | 'CHUNK' | 'IDS'
  35    payload: unknown[]
  36  }
  37  
  38  // ===== Sync Types =====
  39  
  40  /**
  41   * Event manifest entry - describes an event we have
  42   * Used by IDS request/response for diffing
  43   */
  44  export interface EventManifestEntry {
  45    kind: number
  46    id: string
  47    created_at: number
  48    d?: string // For parameterized replaceable events (kinds 30000-39999)
  49  }
  50  
  51  // Chunked message for large payloads
  52  export interface ChunkMessage {
  53    type: 'CHUNK'
  54    messageId: string  // Unique ID for this chunked message
  55    index: number      // 0-based chunk index
  56    total: number      // Total number of chunks
  57    data: string       // Base64 encoded chunk data
  58  }
  59  
  60  // Helper to check if a message is a chunk
  61  export function isChunkMessage(msg: ResponseMessage): msg is ResponseMessage & { payload: [ChunkMessage] } {
  62    return msg.type === 'CHUNK'
  63  }
  64  
  65  // Connection management
  66  export interface NRCConnection {
  67    id: string
  68    label: string
  69    secret?: string // For secret-based auth
  70    clientPubkey?: string // Derived from secret
  71    createdAt: number
  72    lastUsed?: number
  73  }
  74  
  75  // Listener configuration
  76  export interface NRCListenerConfig {
  77    rendezvousUrl: string
  78    signer: ISigner
  79    authorizedSecrets: Map<string, string> // clientPubkey → deviceName
  80    sessionTimeout?: number // Session inactivity timeout in ms (default 30 min)
  81    maxSubscriptionsPerSession?: number // Max subscriptions per session (default 100)
  82  }
  83  
  84  // Authorization result
  85  export interface AuthResult {
  86    conversationKey?: Uint8Array // Optional - only set when using direct key access
  87    deviceName: string
  88  }
  89  
  90  // Parsed connection URI
  91  export interface ParsedConnectionURI {
  92    relayPubkey: string // Hex pubkey of the listening relay/client
  93    rendezvousUrl: string // URL of the rendezvous relay
  94    // For secret-based auth
  95    secret?: string // 32-byte hex secret
  96    clientPubkey?: string // Derived pubkey from secret
  97    clientPrivkey?: Uint8Array // Derived private key from secret
  98    // Optional
  99    deviceName?: string
 100  }
 101  
 102  // Listener state for React context
 103  export interface NRCListenerState {
 104    isEnabled: boolean
 105    isListening: boolean
 106    connections: NRCConnection[]
 107    activeSessions: number
 108    rendezvousUrl: string
 109  }
 110  
 111  // Event with simplified typing for storage queries
 112  export type StoredEvent = Event
 113  
 114  // Device-specific event check
 115  export function isDeviceSpecificEvent(event: Event): boolean {
 116    const dTag = event.tags.find((t) => t[0] === 'd')?.[1]
 117    return dTag?.startsWith('device:') ?? false
 118  }
 119