I created the greatest TypeScript Relay Pool implementation of all time: https://gitlab.com/soapbox-pub/NSpec/-/blob/main/src/NPool.ts

It's designed to work for Inbox/Outbox model. Instead of passing a list of explicit relays into it, you pass functions that determine the best relays to use based on the requested filters (or event being published):

/** Get best relays to request filters. */
reqRelays(filters: NostrFilter[]): Promise<WebSocket['url'][]>;

/** Get best relays to publish event. */
eventRelays(event: NostrEvent): Promise<WebSocket['url'][]>;

It's simple. It's flexible. You can use any underlying relay implementation as long as it matches the interfaces.

I am basically rebuilding NDK from the ground-up in a modern and highly flexible way. So you don't have to commit to the whole stack. The pieces are all separate instead of a big monolith.