Skip to content

The packages

Affordant is a small family in one npm-workspaces monorepo. They share a single wire contract and stay symmetric across the wire: the server builds what the client consumes.

How the Affordant packages fit together: @affordant/contract feeds both the server and the client; @affordant/server builds the _self / _actions envelope, which the affordant client reads; @affordant/express sits alongside the server, while @affordant/react and @affordant/vue sit alongside the client.

PackageSideDepends onWhat it does
@affordant/contractsharedThe wire-contract types. Zero runtime, zero deps. Everything else depends on it.
affordantclientcontractcan / actionFor / follow. Zero runtime deps — runs anywhere fetch exists.
@affordant/reactclientcontract, affordant, reactReact adapter: gate UI on affordances and follow them with hooks.
@affordant/vueclientcontract, affordant, vueVue adapter: gate UI on affordances and follow them with composables.
@affordant/serverservercontractA builder for the _self / _actions envelope. Framework-agnostic.
@affordant/expressserverserver, expressExpress adapter: send the envelope, build URLs from the request.

Italic dependencies are peer dependencies — you bring your own React / Vue / Express. The client core (affordant) and the server core (@affordant/server) carry no runtime dependencies at all.

On Effect (and other effect systems)

follow is a plain async function returning a Promise<Response>. If you work with Effect — or any other effect system — you can wrap it yourself in a line, e.g. Effect.tryPromise(() => follow(action, init)). Affordant stays Effect-compatible without shipping an Effect dependency: the interop is yours to add when you want it, never imposed.

Develop

sh
npm install        # installs all workspaces
npm run build      # builds every package, contract first
npm test           # unit tests + end-to-end demo suites
npm run typecheck  # type-checks every package

Released under the MIT License.