Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

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.events

An 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.