atproto_lexicon/source
Lexicon source specs and network resolution.
A Spec names where a lexicon (or a family of lexicons) comes from:
a local file glob, an exact NSID, an NSID wildcard prefix, or a direct
at-uri. resolve walks the NSID/at-uri resolution chain described in
at-record’s ROADMAP (“codegen: official-method client generation”):
NSID authority -> DNS _lexicon.<domain> TXT (via DoH) -> DID -> PDS
-> com.atproto.lexicon.schema record(s).
Every network call goes through one injected Send function, sharing
atproto_core’s TransportError vocabulary the same way
atproto_client/atproto_browser do, without depending on either
client package itself (just the sans-io core). LocalGlob specs are
not resolved here at all (no filesystem access in this module); a
caller resolving a local-glob spec does so itself and never calls
resolve for it.
Types
One resolved lexicon schema: the NSID it was fetched for, the DID whose repo it lives in, the record’s content CID (for pin provenance), and the schema itself, already validated by an AST decode/encode round trip.
pub type FetchedRecord {
FetchedRecord(
nsid: String,
did: String,
cid: String,
doc: ast.LexiconDoc,
)
}
Constructors
-
FetchedRecord( nsid: String, did: String, cid: String, doc: ast.LexiconDoc, )
Strict resolution: schemas must pass the round-trip fidelity check.
pub type Fidelity {
Strict
Lenient
}
Constructors
-
Strict -
Lenient
pub type NetworkConfig {
NetworkConfig(doh_endpoint: String, plc_directory: String)
}
Constructors
-
NetworkConfig(doh_endpoint: String, plc_directory: String)
pub type ResolveError {
LocalSpecUnsupported(pattern: String)
TransportFailed(error: xrpc.TransportError)
BadStatus(status: Int, body: String)
DecodeFailed(reason: String)
InvalidNsid(nsid: String)
InvalidAtUri(raw: String)
AuthorityNotPublished(domain: String)
UnsupportedDidMethod(did: String)
PdsEndpointMissing(did: String)
SchemaInvalid(nsid: String, errors: List(decoding.DecodeError))
SchemaRoundTripMismatch(nsid: String)
}
Constructors
-
LocalSpecUnsupported(pattern: String)resolveonly handles the network-resolvable spec kinds; aLocalGlobspec has nothing to resolve over the network. -
TransportFailed(error: xrpc.TransportError) -
BadStatus(status: Int, body: String) -
DecodeFailed(reason: String) -
InvalidNsid(nsid: String) -
InvalidAtUri(raw: String) -
AuthorityNotPublished(domain: String)No usable
_lexicon.<domain>TXT record: either the DNS answer was missing/empty, or present but carried no parseabledid=value. Both mean the same thing to a caller: fall back to the local tier. This is the expected, non-exceptional outcome forcom.atproto.*today (see the ROADMAP coverage caveat). -
UnsupportedDidMethod(did: String) -
PdsEndpointMissing(did: String) -
SchemaInvalid(nsid: String, errors: List(decoding.DecodeError))The fetched record’s
valuefailed to decode as a lexicon document. -
SchemaRoundTripMismatch(nsid: String)The document decoded, but re-encoding it did not reproduce the original JSON: the AST lost fidelity on this schema.
The injectable transport: one function from a bit-array request to a bit-array response (or a structured transport-level error). Callers choose the backend (erlang httpc, JS fetch, a test stub).
pub type Send =
fn(request.Request(BitArray)) -> Result(
response.Response(BitArray),
xrpc.TransportError,
)
pub type Spec {
LocalGlob(pattern: String)
NsidExact(nsid: String)
NsidWildcard(prefix: String)
AtUri(uri: String)
}
Constructors
-
LocalGlob(pattern: String) -
NsidExact(nsid: String) -
NsidWildcard(prefix: String) -
AtUri(uri: String)
pub type SpecError {
EmptySpec
}
Constructors
-
EmptySpec
A transport-level failure from the injected send function or from
request construction. Alias for atproto_core/xrpc.TransportError, so a
backend written for atproto_client/atproto_browser maps onto the same
vocabulary here; construct and match its variants (InvalidUrl,
Timeout, ConnectionFailed, Other) via atproto_core/xrpc.
pub type TransportError =
xrpc.TransportError
Values
pub const default_doh_endpoint: String
pub fn default_network_config() -> NetworkConfig
pub const default_plc_directory: String
pub fn describe(error: ResolveError) -> String
pub fn parse_at_uri(
raw: String,
) -> Result(#(String, String, String), ResolveError)
at://<did>/<collection>/<rkey>, exposed for callers (the pinned cache
needs the rkey/nsid out of an AtUri spec offline, without resolving).
pub fn parse_spec(input: String) -> Result(Spec, SpecError)
Parse the string forms: at://... -> AtUri, <nsid-authority>.* ->
NsidWildcard (trailing .* only), a bare valid NSID -> NsidExact,
anything else (a path, a **/* glob, a bare filename) -> LocalGlob.
The only rejected input is empty/whitespace-only text: everything else
has a spec it can degrade to.
pub fn resolve(
send: fn(request.Request(BitArray)) -> Result(
response.Response(BitArray),
xrpc.TransportError,
),
spec: Spec,
network: NetworkConfig,
) -> Result(List(FetchedRecord), ResolveError)
Resolve a spec to its schema(s), strict. LocalGlob is rejected: no
filesystem access here.
pub fn resolve_verified(
send: fn(request.Request(BitArray)) -> Result(
response.Response(BitArray),
xrpc.TransportError,
),
spec: Spec,
network: NetworkConfig,
fidelity: Fidelity,
) -> Result(List(FetchedRecord), ResolveError)
As resolve, but Lenient keeps a schema that decodes cleanly yet does
not byte-round-trip (canonicalisation, or keys the AST does not model).
Decode failures stay fatal. For consumers that need the document, not
byte fidelity.
pub fn to_string(spec: Spec) -> String
The inverse of parse_spec: renders a spec back to its string form, for
provenance (the pinned cache records the source spec string that
produced each pin).
pub fn transport_error_to_string(
error: xrpc.TransportError,
) -> String
A one-line human-readable rendering of a transport error.