Packages

The shared sans-io core of the gleam-atproto client packages: XRPC error vocabulary and response handling, plus the OAuth effect kernel.

Current section

Files

Jump to
atproto_core src atproto_core oauth effect.gleam
Raw

src/atproto_core/oauth/effect.gleam

//// A sans-IO effect kernel for the OAuth orchestration. An `Effect(a)` is a
//// pure description of the HTTP + DPoP steps a flow needs; a per-target runner
//// interprets it (sync `xrpc.Client` + `gose.Key` on erlang, async `Promise`
//// `Client` + WebCrypto `CryptoKey` in the browser). Keys never appear here:
//// `DpopProof` asks the runner for a proof and each runner closes over its own
//// platform key. Bodies come in two shapes: `Fetch` for the string-shaped OAuth
//// endpoints, `FetchBits` for the binary resource path (blobs).
import atproto_core/xrpc.{type TransportError}
import gleam/http/request.{type Request}
import gleam/http/response.{type Response}
import gleam/option.{type Option}
pub type Effect(a) {
Fetch(
request: Request(String),
next: fn(Result(Response(String), TransportError)) -> Effect(a),
)
FetchBits(
request: Request(BitArray),
next: fn(Result(Response(BitArray), TransportError)) -> Effect(a),
)
DpopProof(
method: String,
url: String,
nonce: Option(String),
ath: Option(String),
next: fn(Result(String, String)) -> Effect(a),
)
Done(a)
}
pub fn done(value: a) -> Effect(a) {
Done(value)
}
/// Monadic bind, `use`-syntax friendly: `use x <- effect.then(some_effect)`.
pub fn then(effect: Effect(a), f: fn(a) -> Effect(b)) -> Effect(b) {
case effect {
Done(value) -> f(value)
Fetch(request, next) -> Fetch(request, fn(r) { then(next(r), f) })
FetchBits(request, next) -> FetchBits(request, fn(r) { then(next(r), f) })
DpopProof(method, url, nonce, ath, next) ->
DpopProof(method, url, nonce, ath, fn(r) { then(next(r), f) })
}
}
pub fn map(effect: Effect(a), f: fn(a) -> b) -> Effect(b) {
then(effect, fn(a) { Done(f(a)) })
}
/// A string-bodied HTTP request, yielding the response (or a transport error).
pub fn fetch(
request: Request(String),
) -> Effect(Result(Response(String), TransportError)) {
Fetch(request, Done)
}
/// A binary HTTP request (the resource path, for blobs), yielding the response.
pub fn fetch_bits(
request: Request(BitArray),
) -> Effect(Result(Response(BitArray), TransportError)) {
FetchBits(request, Done)
}
/// Ask the runner for a DPoP proof bound to this method+URL (and optional nonce
/// and access-token hash). The runner signs it with its platform key.
pub fn proof(
method method: String,
url url: String,
nonce nonce: Option(String),
ath ath: Option(String),
) -> Effect(Result(String, String)) {
DpopProof(method:, url:, nonce:, ath:, next: Done)
}