Audition
Audition combines multiple clients into one state stream, call surface, and event stream.
For larger apps, this avoids forcing the UI to swap service references manually when flows shift between contexts, such as moving from a lobby into a chat room.
Merge clients
import { Audition } from "liminal"
import { ChatClient } from "./chat/ChatClient.ts"
import { LobbyClient } from "./lobby/LobbyClient.ts"
export const audition = Audition.empty.pipe(Audition.add(LobbyClient), Audition.add(ChatClient))After that, callers can use one method surface:
Effect.gen(function* () {
yield* audition.fn("JoinRoom")({ roomId })
yield* audition.fn("SendMessage")({ content: "Hello!" })
})The audition.fn(method) signature mirrors Client.fn(method). The effect's required services are the union of
services for every underlying client in the audition.
Merge events
const source = audition.eventsAn app-wide view can listen to merged events, or subscribe to audition.state for the currently active hydrated state.
const source = audition.events.pipe(Stream.forever)
const state = audition.state.pipe(Stream.forever)Method resolution order
Audition.add(...) is ordered.
With:
Audition.empty.pipe(Audition.add(LobbyClient), Audition.add(ChatClient))runtime lookup tries LobbyClient first, then falls back to ChatClient.
Fallback only triggers when the prior client produces an AuditionError. Other errors such as ConnectionError or
UnresolvedError propagate directly without trying the next client.
If multiple clients expose the same method name, keep the method definitions identical. The type-level merge assumes matching shapes for overlapping method keys.