From 338363131de9a73c8c51999814968daf3e53c3e3 Mon Sep 17 00:00:00 2001 From: ContextVM Date: Mon, 28 Sep 2026 15:31:39 +0100 Subject: [PATCH] feat(client): expose getOperationalRelayUrls on NostrClientTransport MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Public read surface for the resolved operational relay set. Returns configured URLs before start(); after start() resolves, the final set after relay resolution (hints, kind-10002 discovery, fallback probe) — stable for the transport's lifetime. Lets embedders persist discovery results and construct future sessions as configured clients. --- .changeset/expose-operational-relay-urls.md | 5 +++ docs/contextvm-docs | 2 +- src/transport/nostr-client-transport.test.ts | 41 ++++++++++++++++++++ src/transport/nostr-client-transport.ts | 11 ++++++ 4 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 .changeset/expose-operational-relay-urls.md diff --git a/.changeset/expose-operational-relay-urls.md b/.changeset/expose-operational-relay-urls.md new file mode 100644 index 0000000..602de0a --- /dev/null +++ b/.changeset/expose-operational-relay-urls.md @@ -0,0 +1,5 @@ +--- +'@contextvm/sdk': patch +--- + +`NostrClientTransport` now exposes `getOperationalRelayUrls()`, returning the current handler's relay URLs: configured URLs before `start()`, and the final set after relay resolution (server-identity hints, kind-10002 discovery, fallback probe) once `start()` resolves. Callers can read the resolved set once and persist it, so future sessions construct a plain configured client without re-paying discovery. diff --git a/docs/contextvm-docs b/docs/contextvm-docs index 20d01e2..a268677 160000 --- a/docs/contextvm-docs +++ b/docs/contextvm-docs @@ -1 +1 @@ -Subproject commit 20d01e2cb5cef568fb0fc213842f3c57ba9d2c0f +Subproject commit a2686779dec6db135edb3280a88f65163a2d3bb4 diff --git a/src/transport/nostr-client-transport.test.ts b/src/transport/nostr-client-transport.test.ts index 79dbfa3..8153738 100644 --- a/src/transport/nostr-client-transport.test.ts +++ b/src/transport/nostr-client-transport.test.ts @@ -1245,6 +1245,47 @@ describe('NostrClientTransport instance shape', () => { ]); }); + test('getOperationalRelayUrls returns configured URLs before start and resolved hints after', async () => { + const configuredUrl = 'wss://configured.example.com'; + const relayHintUrl = 'wss://relay.example.com'; + const transport = new NostrClientTransport({ + serverPubkey: nip19.nprofileEncode({ + pubkey: 'b'.repeat(64), + relays: [relayHintUrl], + }), + signer: new PrivateKeySigner('a'.repeat(64)), + relayHandler: [configuredUrl], + }); + + expect(transport.getOperationalRelayUrls()).toEqual([configuredUrl]); + + await transport['resolveOperationalRelayHandler'](); + + // Configured URLs win: resolution must not swap a configured handler. + expect(transport.getOperationalRelayUrls()).toEqual([configuredUrl]); + }); + + test('getOperationalRelayUrls returns resolved URLs after resolution swaps the handler', async () => { + const relayHintUrl = 'wss://relay.example.com'; + const transport = new NostrClientTransport({ + serverPubkey: nip19.nprofileEncode({ + pubkey: 'b'.repeat(64), + relays: [relayHintUrl], + }), + signer: new PrivateKeySigner('a'.repeat(64)), + relayHandler: [], + }); + + expect(transport.getOperationalRelayUrls()).toEqual([]); + + await transport['resolveOperationalRelayHandler'](); + + expect(transport.getOperationalRelayUrls()).toEqual([relayHintUrl]); + // Second resolution is a no-op: the set is stable after the first one. + await transport['resolveOperationalRelayHandler'](); + expect(transport.getOperationalRelayUrls()).toEqual([relayHintUrl]); + }); + test('uses bootstrap discovery relays by default when none are provided', () => { const transport = new NostrClientTransport({ serverPubkey: 'b'.repeat(64), diff --git a/src/transport/nostr-client-transport.ts b/src/transport/nostr-client-transport.ts index 39492a0..159429b 100644 --- a/src/transport/nostr-client-transport.ts +++ b/src/transport/nostr-client-transport.ts @@ -559,6 +559,17 @@ export class NostrClientTransport await this.inboundCoordinator.processIncomingEvent(event); } + /** + * Gets the transport's current operational relay URLs. + * + * Returns configured URLs before `start()`; once `start()` resolves, the + * final set after relay resolution (hints, kind-10002 discovery, fallback + * probe) — stable for the transport's lifetime. + */ + public getOperationalRelayUrls(): string[] { + return this.relayHandler.getRelayUrls?.() ?? []; + } + /** * Gets the server's initialize event if received. * @returns The server initialize event or undefined