Skip to content
Liminal

Clients

A Client defines the wire contract between browser clients and actors.

Everything else in Liminal hangs off that contract:

  • actors point at a client definition
  • external handlers implement the methods callable by connected clients
  • Auditions can merge multiple clients into one event stream and call surface
  • reducers derive local state from events after the initial hydration

Define a client

A client is a named state schema, external method table, and event table.

import { Schema as S } from "effect"
import { Client } from "liminal"
 
export const Player = S.Literals(["X", "O"])
export const Coordinate = S.Literals([0, 1, 2])
export const Coordinates = S.Tuple([Coordinate, Coordinate])
 
export class TicTacToeClient extends Client.Service<TicTacToeClient>()(
  "examples/TicTacToeClient",
  {
    events: {
      GameStarted: {},
      MoveMade: {
        player: Player,
        position: Coordinates,
      },
      GameEnded: {
        winner: S.optional(Player),
      },
    },
    external: {
      Move: {
        payload: S.Struct({
          position: Coordinates,
        }),
        success: S.Void,
        failure: S.Never,
      },
    },
    state: {
      awaitingPartner: S.Boolean,
      name: Player,
    },
  },
) {}

Shape

  • state is the struct-fields record hydrated when the client connects.
  • external is a record of { payload, success, failure } methods callable through Client.fn(...).
  • events is a record of plain struct fields.
  • Liminal tags events automatically with _tag.

Next concepts

  • Methods covers reusable method definitions.
  • Client Calls covers Client.fn(...).
  • Events covers Client.events and replay.
  • Client State covers hydration and reducers.
  • Actors covers the server-side runtime for this protocol.