fiber_pylon library

The wall between a project and whatever is currently answering it.

Pylon exists so that unplugging a backend and plugging in another changes one line of wiring and nothing else. It is built on one refusal: pylon understands neither side. It does not know what a brand is, what a route is, what a credential looks like, or what can go wrong. It knows how to let something through, or not.

Everything here is therefore either a shape a project fills in, or a policy that is identical whatever fills it in. Nothing infers, nothing recognises a name, nothing assumes a format. Where a decision belongs to the project, it is a required argument, never a default that happens to be right most of the time.

The two halves

The barrier, which the contract sees: Result and its two variants, Fault, FaultMapper, Backend, SdkHandle, Config. This is what a service layer touches, and it does not change when the backend does.

The toolkit, which only adapters see: RestClient and what it needs, CredentialManager, CallGuard, SocketChannel, ChannelKeeper, HealthMonitor, Preference, KeyValueStore, Observable, Reporter, Backoff. Each is a mechanism every backend would otherwise rewrite, and rewrite worse the second time.

package:fiber_pylon/fiber_pylon_io.dart carries the one piece that needs dart:io, a SocketLink over a WebSocket. It is separate so that importing pylon does not stop a project from compiling for the web.

The one thing pylon does assume

That both ends of the swap are REST. RestClient therefore speaks HTTP, and RestMethod is a closed list, because these are a protocol's own words rather than a guess about a project. What stays outside is every judgement the protocol does not make: which statuses are failures, what an error body looks like, how a call is authenticated. Those are asked for, through a RestClassifier and a RestHeaders.

Where the boundary actually is

It is Fault, and what makes it work is that pylon never reads it. A fault carries a signal from the adapter's own vocabulary, an enum the adapter declares:

enum RestSignal { unauthorized, forbidden, vpnRequired, nameEmpty, noRoute }

A FaultMapper then turns that signal into the error one operation declares, through a table the project wrote, with both sides typed and checked by the compiler. Where pylon needs to act on a failure, it is handed a set of signals rather than left to interpret one: CredentialManager is told which signals mean the credential is dead, CallGuard which are worth renewing for, and even the refusal CallGuard issues for a duplicate call is named by the project.

That is the whole discipline. Any list of failure kinds pylon offered would be a guess about the projects it has not met.

What is deliberately absent

No token format, no notion of a session, no list of error kinds, no envelope around a response body, no rule about which status means what, no environment reading, no code generation. Every one of those belongs to one server rather than to REST, and a wall that took a side would stop being a wall.

Classes

Backend
The plug.
Backoff
How long to wait before trying again, growing with each failure.
CallGuard<S extends Object>
Wraps a call in the policy that is the same whatever the call is.
Channel<E>
A live connection carrying events from named subscriptions.
ChannelKeeper<E>
Holds a Channel to the subscriptions it is supposed to have.
Config
What a backend needs before it can start.
CredentialChange<C extends Object>
One transition of the credential state.
CredentialManager<C extends Object, S extends Object>
Keeps a credential alive, and is the reason a backend only has to write one method.
CredentialRefresher<C extends Object>
The one thing a backend supplies for pylon to keep a credential alive.
CredentialStore<C extends Object>
Where the credential survives a restart.
Failure<T, E>
The failure variant of Result.
FaultMapper<S extends Object, E>
Turns a Fault into the error type one operation declares.
HealthMonitor
A yes-or-no condition that is both asked about and reported on.
KeyValueStore
Somewhere small strings survive a restart.
MemoryCredentialStore<C extends Object>
A CredentialStore that forgets everything when the process ends.
MemoryKeyValueStore
A KeyValueStore that keeps everything in memory.
MutableObservable<T>
An Observable whose holder can publish new values.
Observable<T>
A value that can be read now and watched for later changes.
OK<T, E>
The success variant of Result.
Preference<T>
One typed, watchable entry of a KeyValueStore.
Reporter
Where pylon sends its breadcrumbs and its unexpected errors.
Requirement
One value a backend cannot start without.
RestClassifier<S extends Object>
Turns what an HTTP call did into the adapter's own signal.
RestClient<S extends Object>
Talks to one REST API.
RestRequest
One call to the API, described without saying how it is performed.
RestResponse
What the server answered.
RestUpload
A file travelling in a multipart body.
Result<T, E>
Either a successful value (OK) or a typed error (Failure).
SdkHandle<T extends Object>
Holds the one instance an SDK hands out, and says so when there is none yet.
SilentReporter
A Reporter that drops everything.
SocketChannel<E>
A Channel over a framed socket.
SocketEvent<E>
A frame carrying something the caller subscribed for.
SocketFrame<E>
What one incoming frame turned out to be.
SocketIgnored<E>
A frame the channel has nothing to do with.
SocketJoined<E>
A frame confirming that a subscription is in force.
A duplex link carrying text frames.
SocketProtocol<E>
What the frames of one server look like.
SocketRejected<E>
A frame refusing a subscription.
StoredCredential<C extends Object>
A CredentialStore backed by a KeyValueStore.

Enums

ChannelState
Whether the live connection is usable right now.
CredentialEvent
What happened to the credential held by a CredentialManager.
RestMethod
The verb of a request.

Typedefs

RestHeaders = Future<Map<String, String>> Function(RestRequest request)
Builds the headers every call carries.
SocketOpener = Future<SocketLink> Function(Uri endpoint)
Opens a link to endpoint.

Exceptions / Errors

ConfigurationError
Thrown when a backend cannot start because values are missing.
Fault<S extends Object>
A failure, as it crosses the boundary between a backend and the contract built on top of it.