morph 0.1.0
Asynchronous bridge between GUI thread and business models
Loading...
Searching...
No Matches
morph::bridge::Bridge Class Reference

Central dispatcher that routes typed actions to an IBackend. More...

#include <bridge.hpp>

Public Member Functions

 Bridge (std::unique_ptr<::morph::backend::detail::IBackend > backend, ::morph::exec::IExecutor &owner)
 Constructs a bridge that dispatches through backend, owned by owner.
 ~Bridge ()
 Clears the backend's reconnect handler, rejects every call still waiting for a bind with BridgeDestroyedError, and cancels the backend's pending calls with the same error.
 Bridge (const Bridge &)=delete
Bridge & operator= (const Bridge &)=delete
 Bridge (Bridge &&)=delete
Bridge & operator= (Bridge &&)=delete
::morph::exec::IExecutor & owner () const noexcept
 The executor this bridge belongs to.
bool onOwner () const noexcept
 Whether the calling thread is on this bridge's owner.
template<typename Model>
std::shared_ptr< detail::HandlerBinding > registerHandler ()
 Creates and registers a new HandlerBinding for Model.
void registerHandler (const std::shared_ptr< detail::HandlerBinding > &binding)
 Tracks binding and issues its bind on the active backend.
template<typename Model>
std::shared_ptr< detail::HandlerBinding > registerSharedHandler ()
 Creates a shared, initially unattached binding for Model.
void attach (const std::shared_ptr< detail::HandlerBinding > &binding, std::string primary)
 Attaches (or re-points) a shared binding to the instance for primary, creating it if no live instance holds the key.
template<typename Model>
void attachHandler (const std::shared_ptr< detail::HandlerBinding > &binding, std::string primary)
 attach for a typed caller.
template<typename Model>
void assignHandlerPrimary (const std::shared_ptr< detail::HandlerBinding > &binding, std::string primary)
 Files binding's current instance under primary, in place.
std::string bindingPrimary (const std::shared_ptr< detail::HandlerBinding > &binding) const
 Returns binding's current primary key, or empty if unattached.
template<typename Model>
::morph::async::Completion< std::vector< std::string > > instancesOf (::morph::exec::IExecutor &cbExec)
 Lists the live shared primary keys of Model on the active backend.
void addSubscription (const std::shared_ptr< detail::HandlerBinding > &binding, std::type_index type, std::function< void(const std::any &)> sink, ::morph::exec::IExecutor *exec)
 Registers a result-type subscription for binding.
void removeSubscription (const std::shared_ptr< detail::HandlerBinding > &binding, std::type_index type)
 Removes binding's subscription for type, if any.
bool hasSubscribers () const noexcept
 Whether any subscription is currently registered on this bridge.
void publishResult (::morph::exec::detail::ModelId mid, std::type_index type, const std::any &value)
 Delivers value to every subscriber attached to instance mid.
void setDefaultSession (::morph::session::Context session)
 Installs a default session context that executeVia stamps onto the ActionCall of every subsequent call.
void setExecuteDeadline (std::chrono::milliseconds deadline)
 Sets (or disables) the client-side execute deadline.
std::chrono::milliseconds executeDeadline () const
 Returns the currently installed client-side execute deadline.
::morph::session::Context defaultSession () const
 Returns a copy of the currently installed default session.
void setPrincipal (::morph::session::Principal principal)
 Installs the verified Principal for this Bridge.
::morph::session::Principal currentPrincipal () const
 Returns a copy of the currently installed Principal.
std::size_t pendingCalls () const noexcept
 Returns the number of calls made through the bridge — typed (executeVia() and its keyed variants) or raw (executeRaw(), executeRawOn()) — that have not yet resolved.
template<typename Backend>
requires std::derived_from<Backend, ::morph::backend::detail::IBackend>
void switchBackend (std::unique_ptr< Backend > newBackend)
 Replaces the active backend with newBackend.
void switchBackend (std::shared_ptr<::morph::backend::detail::IBackend > newBackend)
 Replaces the active backend with newBackend.
void deregisterHandler (const std::shared_ptr< detail::HandlerBinding > &binding)
 Deregisters binding from the active backend and removes it from tracking.
template<typename Model, typename Action>
::morph::async::Completion< typename ::morph::model::ActionTraits< Action >::Result > executeVia (const std::shared_ptr< detail::HandlerBinding > &binding, Action action, ::morph::exec::IExecutor *cbExec, std::function< void(const typename ::morph::model::ActionTraits< Action >::Result &)> onResult={})
 Dispatches action against the model identified by binding.
template<typename Model, typename Action>
::morph::async::Completion< typename ::morph::model::ActionTraits< Action >::Result > executeAttachedVia (const std::shared_ptr< detail::HandlerBinding > &binding, Action action, ::morph::exec::IExecutor *cbExec, std::string key)
 Dispatches a payload-keyed action on a shared binding, attaching it to the instance key names first.
template<typename Model, typename Action>
::morph::async::Completion< typename ::morph::model::ActionTraits< Action >::Result > executeCreatingVia (const std::shared_ptr< detail::HandlerBinding > &binding, Action action, ::morph::exec::IExecutor *cbExec)
 Dispatches a result-keyed action on a shared binding, giving it an anonymous instance first if it has none, and promoting that instance under the key its result carries.
std::shared_ptr< detail::HandlerBinding > bindByType (std::string typeId, BindSharing sharing, std::string instanceKey={})
 Binds the model registered as typeId, for a caller compiled without the model's type.
::morph::async::Completion< std::string > executeRaw (const std::shared_ptr< detail::HandlerBinding > &binding, std::string actionId, std::string bodyJson, ::morph::exec::IExecutor *cbExec)
 Dispatches the action registered as actionId with a JSON body through binding, and resolves with the JSON reply.
::morph::async::Completion< std::string > executeRaw (const std::shared_ptr< detail::HandlerBinding > &binding, std::string actionId, std::string bodyJson, ::morph::exec::IExecutor *cbExec, ::core::async::StopToken stop)
 executeRaw, cancellable through stop.
::morph::async::Completion< std::string > executeRawOn (const std::shared_ptr< detail::HandlerBinding > &binding, std::string instanceKey, std::string actionId, std::string bodyJson, ::morph::exec::IExecutor *cbExec)
 executeRaw on the instance for instanceKey: attaches the shared binding to it first when it holds another or none.

Static Public Member Functions

static bool isBound (const std::shared_ptr< detail::HandlerBinding > &binding) noexcept
 Whether binding currently has a live ModelId.

Detailed Description

Central dispatcher that routes typed actions to an IBackend.

Bridge owns exactly one active backend at a time. It tracks all registered HandlerBinding instances and re-registers them when switchBackend() is called or the backend reconnects, enabling seamless local ↔ remote transitions.

One owner
A Bridge, its handlers and its backend belong to the executor given at construction. Every member function is called there — inside a task of the owner, or on the thread that constructed the bridge outside the owner's tasks (a GUI owner's own thread) — and every field is touched only there, so nothing here takes a lock. A call from anywhere else is a contract violation, asserted in a debug build. What reaches the bridge from other threads arrives as a task on the owner: bind and promote replies (the owner is the executor bindModel/promoteModel deliver on), reconnects (the executor the reconnect handler is posted to), and the bridge-side work an action's result triggers. pendingCalls(), isBound() and hasSubscribers() read atomics and may be asked from any thread. See docs/spec/core/bridge.md, "Thread safety — one owner".
The bind rule
The bind is a Completion delivered on the owner; a backend that can settles it before returning, and that outcome is applied at once. execute dispatches immediately when the binding's currentId is set and no bind is in flight, and is otherwise held by the binding and dispatched when the bind settles.

Definition at line 747 of file bridge.hpp.

Constructor & Destructor Documentation

◆ Bridge()

morph::bridge::Bridge::Bridge ( std::unique_ptr<::morph::backend::detail::IBackend > backend,
::morph::exec::IExecutor & owner )
inline

Constructs a bridge that dispatches through backend, owned by owner.

Tells the backend its owner, installs a reconnect handler (posted to owner) so a backend with a recoverable transport re-registers every live handler on the owner after a reconnect, and pushes the (initially empty) default session to the backend.

A bridge always has a backend: every handler binds on construction, so a bridge without one could not serve its first handler.

Parameters
backendInitial backend. Ownership is transferred. Must not be null.
ownerThe executor this bridge, its handlers and its backend belong to. Construct the bridge there: inside one of its tasks, or on the thread that runs them. Borrowed: it must outlive this bridge and every reply still in flight when the bridge is destroyed.
Exceptions
std::invalid_argumentif backend is null.

Definition at line 767 of file bridge.hpp.

◆ ~Bridge()

morph::bridge::Bridge::~Bridge ( )
inline

Clears the backend's reconnect handler, rejects every call still waiting for a bind with BridgeDestroyedError, and cancels the backend's pending calls with the same error.

On the owner. A reply or reconnect still queued there finds the bridge's token expired and touches nothing; a bind reply for a binding the bridge no longer tracks releases its instance on the backend that issued it.

Definition at line 786 of file bridge.hpp.

Member Function Documentation

◆ owner()

::morph::exec::IExecutor & morph::bridge::Bridge::owner ( ) const
inlinenodiscardnoexcept

The executor this bridge belongs to.

Returns
The owner given at construction.

Definition at line 811 of file bridge.hpp.

◆ onOwner()

bool morph::bridge::Bridge::onOwner ( ) const
inlinenodiscardnoexcept

Whether the calling thread is on this bridge's owner.

Returns
True inside one of the owner's tasks, or on the thread that constructed the bridge outside them.

Definition at line 816 of file bridge.hpp.

◆ registerHandler() [1/2]

template<typename Model>
std::shared_ptr< detail::HandlerBinding > morph::bridge::Bridge::registerHandler ( )
inline

Creates and registers a new HandlerBinding for Model.

Uses the default ModelFactory::create<Model>() factory. The bind is issued and this returns; see registerHandler(binding).

Template Parameters
ModelConcrete model type. Must have a registered ModelTraits specialisation.
Returns
Shared pointer to the new binding.

Definition at line 825 of file bridge.hpp.

◆ registerHandler() [2/2]

void morph::bridge::Bridge::registerHandler ( const std::shared_ptr< detail::HandlerBinding > & binding)
inline

Tracks binding and issues its bind on the active backend.

Never waits. A backend that settles the bind before returning has bound binding by the time this returns; otherwise a call made through it is held until the bind settles. A failed bind is logged and rejects the calls held for it with its error.

Parameters
bindingPre-constructed binding. Its typeId, modelFactory and contextKey must be set.

Definition at line 839 of file bridge.hpp.

◆ registerSharedHandler()

template<typename Model>
std::shared_ptr< detail::HandlerBinding > morph::bridge::Bridge::registerSharedHandler ( )
inline

Creates a shared, initially unattached binding for Model.

Registers nothing on the backend: a shared handler has no instance until a keyed action or an explicit attach names one. The binding is tracked from the start so switchBackend() knows about it.

Template Parameters
ModelConcrete model type. Must have a registered ModelTraits.
Returns
Shared pointer to the new, unattached binding.

Definition at line 861 of file bridge.hpp.

◆ attach()

void morph::bridge::Bridge::attach ( const std::shared_ptr< detail::HandlerBinding > & binding,
std::string primary )
inline

Attaches (or re-points) a shared binding to the instance for primary, creating it if no live instance holds the key.

Idempotent: attaching to the primary a binding already holds is a no-op. A different primary re-points the binding — the previous instance is released and survives only if another handler still holds it. The binding's contextKey is set to primary as well, so a keyed model's journal entries carry the entity key.

Issued after any bind already in flight for binding, and never waits: a call made through the binding meanwhile is held until it settles. A refused attach is logged and leaves the binding on the instance it held; a call held behind it is rejected with its error only when the binding has no instance to fall back to.

Needs no model type, so a binding from bindByType attaches through it as a typed one does. The key is matched exactly, so it must be the canonical encoding keyToString gives the model's key type: another spelling of the same key names a different instance.

Parameters
bindingShared binding, from registerSharedHandler<Model>() or bindByType.
primaryCanonical string encoding of the primary key to attach to.
Exceptions
std::invalid_argumentfor an empty primary: the bridge reads an empty primary as "never attached".
std::logic_errorfor a private binding, whose instance is re-created private by every re-bind.

Definition at line 895 of file bridge.hpp.

◆ attachHandler()

template<typename Model>
void morph::bridge::Bridge::attachHandler ( const std::shared_ptr< detail::HandlerBinding > & binding,
std::string primary )
inline

attach for a typed caller.

Skips attach's private-binding check, since a typed handler's sharing is fixed by its type, and refuses an empty primary without throwing: in its turn after any bind in flight, the refusal is logged and leaves the binding on the instance it held. A binding with no instance records the refusal as its bind failure, so a call made through it is rejected with that error.

Template Parameters
ModelConcrete model type.
Parameters
bindingShared binding, as returned by registerSharedHandler<Model>().
primaryCanonical string encoding of the primary key to attach to.

Definition at line 917 of file bridge.hpp.

◆ assignHandlerPrimary()

template<typename Model>
void morph::bridge::Bridge::assignHandlerPrimary ( const std::shared_ptr< detail::HandlerBinding > & binding,
std::string primary )
inline

Files binding's current instance under primary, in place.

The instance keeps everything the creating action just did — nothing is re-created and nothing is stranded. A no-op if binding is unbound or already holds a real primary (the backend refuses to re-key a keyed instance — see IBackend::assignPrimary).

Reaches the backend through IBackend::promoteModel, delivered on the owner. A backend that settles before returning has promoted the binding by the time this returns, which a result-keyed execute relies on: its .then then already sees the new primary. A failure is logged.

Known gap: if the target key is already held by a different instance, the backend declines to promote (the existing holder always wins) but reports success, so the binding caches a primary the backend never filed it under. Closing it needs assignPrimary's outcome to become observable across every backend.

Template Parameters
ModelConcrete model type.
Parameters
bindingShared binding whose instance is being promoted.
primaryCanonical string encoding of the key to file it under.

Definition at line 992 of file bridge.hpp.

◆ bindingPrimary()

std::string morph::bridge::Bridge::bindingPrimary ( const std::shared_ptr< detail::HandlerBinding > & binding) const
inlinenodiscard

Returns binding's current primary key, or empty if unattached.

Parameters
bindingBinding to inspect.
Returns
Canonical key string, or an empty string when unattached.

Definition at line 1026 of file bridge.hpp.

◆ isBound()

bool morph::bridge::Bridge::isBound ( const std::shared_ptr< detail::HandlerBinding > & binding)
inlinestaticnodiscardnoexcept

Whether binding currently has a live ModelId.

A point-in-time atomic read, safe from any thread. Nothing needs to wait on it: a call made through an unbound handler whose bind is in flight is held until the bind settles.

Parameters
bindingBinding to inspect.
Returns
true if currentId != 0.

Definition at line 1038 of file bridge.hpp.

◆ instancesOf()

template<typename Model>
::morph::async::Completion< std::vector< std::string > > morph::bridge::Bridge::instancesOf ( ::morph::exec::IExecutor & cbExec)
inlinenodiscard

Lists the live shared primary keys of Model on the active backend.

Template Parameters
ModelConcrete model type.
Parameters
cbExecExecutor the answer is delivered on. Borrowed: it must outlive the returned Completion.
Returns
A Completion resolved with the canonical key strings, in unspecified order, or rejected with the backend's failure.

Definition at line 1049 of file bridge.hpp.

◆ addSubscription()

void morph::bridge::Bridge::addSubscription ( const std::shared_ptr< detail::HandlerBinding > & binding,
std::type_index type,
std::function< void(const std::any &)> sink,
::morph::exec::IExecutor * exec )
inline

Registers a result-type subscription for binding.

The subscription is stored against the binding, not against a fixed instance id, and is matched at publish time by comparing the binding's current instance, so re-pointing a handler moves its subscriptions with it. See detail::SubscriptionRegistry.

Parameters
bindingHandler binding that owns the subscription.
typeResult type being subscribed to.
sinkType-erased delivery callback; receives the boxed result.
execExecutor the callback is delivered on.

Definition at line 1065 of file bridge.hpp.

◆ removeSubscription()

void morph::bridge::Bridge::removeSubscription ( const std::shared_ptr< detail::HandlerBinding > & binding,
std::type_index type )
inline

Removes binding's subscription for type, if any.

Parameters
bindingHandler binding that owns the subscription.
typeResult type to stop hearing about.

Definition at line 1074 of file bridge.hpp.

◆ hasSubscribers()

bool morph::bridge::Bridge::hasSubscribers ( ) const
inlinenodiscardnoexcept

Whether any subscription is currently registered on this bridge.

A single relaxed atomic load, safe from any thread: a result with no subscriber is never boxed and never sent to the owner.

Returns
true if at least one subscription exists.

Definition at line 1084 of file bridge.hpp.

◆ publishResult()

void morph::bridge::Bridge::publishResult ( ::morph::exec::detail::ModelId mid,
std::type_index type,
const std::any & value )
inline

Delivers value to every subscriber attached to instance mid.

Called, on the owner, for every successful action result that has subscribers. Subscribers are matched on the instance the result was produced on, so a handler hears about work another handler — or, with a shared instance, another screen entirely — did on the model it is attached to. The producing handler is notified too.

Parameters
midInstance the result was produced on.
typeResult type produced.
valueBoxed result.

Definition at line 1097 of file bridge.hpp.

◆ setDefaultSession()

void morph::bridge::Bridge::setDefaultSession ( ::morph::session::Context session)
inline

Installs a default session context that executeVia stamps onto the ActionCall of every subsequent call.

There is no per-call session override — this default is applied to all calls until replaced or cleared. Also pushed to the active backend, so every control envelope it builds afterwards carries it too.

Parameters
sessionThe new default. Pass {} to clear.

Definition at line 1110 of file bridge.hpp.

◆ setExecuteDeadline()

void morph::bridge::Bridge::setExecuteDeadline ( std::chrono::milliseconds deadline)
inline

Sets (or disables) the client-side execute deadline.

Every executeVia() call after this point races the real reply against deadline; whichever settles first wins (CompletionState::setValue/ setException are idempotent — see completion.hpp). If deadline elapses first, the pending Completion fails with morph::backend::ClientTimeoutError; the real reply, if it arrives later, is silently discarded exactly like any other late write to an already-resolved CompletionState.

The clock starts inside executeVia(), when the call is made — before a wait for its bind, if it has to wait — so deadline covers the whole round trip.

Disabled (std::chrono::milliseconds{0}, the default): a dropped frame or a hung server leaves the Completion pending forever.

The backing TimeoutScheduler (and the exec::IoLoop it owns, with that loop's one thread) is created lazily on the first call that enables a deadline, so a Bridge that never opts in spawns no extra thread. Once created it lives until ~Bridge().

Parameters
deadlineMaximum time to wait for any reply. 0 disables the deadline.

Definition at line 1140 of file bridge.hpp.

◆ executeDeadline()

std::chrono::milliseconds morph::bridge::Bridge::executeDeadline ( ) const
inlinenodiscard

Returns the currently installed client-side execute deadline.

Returns
The deadline; std::chrono::milliseconds{0} when disabled.

Definition at line 1150 of file bridge.hpp.

◆ defaultSession()

::morph::session::Context morph::bridge::Bridge::defaultSession ( ) const
inlinenodiscard

Returns a copy of the currently installed default session.

Returns
Snapshot of the default Context.

Definition at line 1157 of file bridge.hpp.

◆ setPrincipal()

void morph::bridge::Bridge::setPrincipal ( ::morph::session::Principal principal)
inline

Installs the verified Principal for this Bridge.

Typically called once right after a successful login dispatch, from data the server actually returned (see session::Principal's doc comment on the trust model). Distinct from setDefaultSession: that installs the per-call Context forwarded with every dispatch; this installs the longer-lived identity UI code reads outside a dispatch via currentPrincipal() to shape itself.

Parameters
principalVerified identity to install. Pass a default-constructed Principal{} (or call this from a sign-out handler) to clear it.

Definition at line 1173 of file bridge.hpp.

◆ currentPrincipal()

::morph::session::Principal morph::bridge::Bridge::currentPrincipal ( ) const
inlinenodiscard

Returns a copy of the currently installed Principal.

Default-constructed (empty id, no roles) if setPrincipal was never called or the application signed out by clearing it.

Returns
Snapshot of the installed Principal.

Definition at line 1183 of file bridge.hpp.

◆ pendingCalls()

std::size_t morph::bridge::Bridge::pendingCalls ( ) const
inlinenodiscardnoexcept

Returns the number of calls made through the bridge — typed (executeVia() and its keyed variants) or raw (executeRaw(), executeRawOn()) — that have not yet resolved.

Incremented once per call, when it is made (a call held for its bind included); decremented exactly once when the returned Completion settles on success or on error (including a cancellation error from switchBackend()/~Bridge()/a dropped transport). A client can poll this to build a "still loading" indicator or gate a feature on quiescence. A single relaxed atomic load, safe from any thread.

Returns
Count of actions made but not yet resolved.

Definition at line 1200 of file bridge.hpp.

◆ switchBackend() [1/2]

template<typename Backend>
requires std::derived_from<Backend, ::morph::backend::detail::IBackend>
void morph::bridge::Bridge::switchBackend ( std::unique_ptr< Backend > newBackend)
inline

Replaces the active backend with newBackend.

Note
Templated on the concrete Backend (rather than taking unique_ptr<IBackend> directly) so it is an exact match for a unique_ptr<Concrete> argument (e.g. std::make_unique<LocalBackend>(...)) and is preferred over the shared_ptr<IBackend> overload during overload resolution — both would otherwise require an equally-ranked user-defined conversion, making every existing call site ambiguous.
Template Parameters
BackendConcrete backend type; must derive from IBackend.
Parameters
newBackendReplacement backend. Ownership is transferred. Must not be null.
Exceptions
std::invalid_argumentif newBackend is null.

Definition at line 1216 of file bridge.hpp.

◆ switchBackend() [2/2]

void morph::bridge::Bridge::switchBackend ( std::shared_ptr<::morph::backend::detail::IBackend > newBackend)
inline

Replaces the active backend with newBackend.

Tells newBackend its owner and the current default session, then issues a bind on it for every live binding (a shared one holding neither a key nor an instance has nothing to re-create; an anonymous one, not yet promoted, re-binds anonymous). Binds the new backend settles before returning decide the switch: if any of them failed, every instance acquired so far is released, and the failure is rethrown with the old backend and every binding untouched. Otherwise the switch commits: the settled ids are published, a bind still in flight leaves its binding unbound with its calls held until it settles, the backend is swapped, notifyBackendChanged() runs, the reconnect handler moves to the new backend, and the old backend's pending calls are cancelled with BackendChangedError (a Task handler still running there is asked to stop). A failure of a bind still in flight at the commit cannot roll the switch back; it is recorded on its binding.

The caller keeps shared ownership of newBackend, so the same backend can be re-installed later without reconstructing it.

Parameters
newBackendReplacement backend, shared with the caller. Must not be null.
Exceptions
std::invalid_argumentif newBackend is null; the bridge keeps its backend and every binding.

Definition at line 1245 of file bridge.hpp.

◆ deregisterHandler()

void morph::bridge::Bridge::deregisterHandler ( const std::shared_ptr< detail::HandlerBinding > & binding)
inline

Deregisters binding from the active backend and removes it from tracking.

Every call held for a bind in flight is rejected with HandlerDestroyedError, and that bind's reply, when it lands, releases its instance rather than binding it. After this, a call made through binding fails with "handler not bound".

Parameters
bindingBinding to remove.

Definition at line 1348 of file bridge.hpp.

◆ executeVia()

template<typename Model, typename Action>
::morph::async::Completion< typename ::morph::model::ActionTraits< Action >::Result > morph::bridge::Bridge::executeVia ( const std::shared_ptr< detail::HandlerBinding > & binding,
Action action,
::morph::exec::IExecutor * cbExec,
std::function< void(const typename ::morph::model::ActionTraits< Action >::Result &)> onResult = {} )
inline

Dispatches action against the model identified by binding.

Counts the call as pending and arms the client-side deadline at once. When binding is bound and no bind is in flight, the action is dispatched here, through IBackend::executeInto, handing the backend a detail::BridgeSink<R> that is both the caller's typed completion state and the backend's settle sink. Otherwise the call is held by the binding and dispatched on the owner when the bind settles; a failed bind rejects it with the bind's error, a destroyed handler with HandlerDestroyedError. An AllowShared binding that was never attached rejects it with "handler not bound".

On LocalBackend, the localOp this method builds first overwrites any declared computed fields from their inputs (morph::forms::recomputeAll, a no-op for actions with no computedFields), then enforces morph::model::ActionValidator<Action>::ready(action) before calling Model::execute, mirroring ActionDispatcher::registerAction's runner (registry.hpp) for the in-process path. A false result resolves the returned Completion through onError with a morph::model::ValidationError instead of executing the action — see docs/spec/core/registry.md and docs/spec/forms/forms.md.

A call whose handler returns a Task carries a stop source, requested by the execute deadline, by a CallbackScope a callback was attached through (see Completion::then), and by the backend's cancelPending.

Template Parameters
ModelModel type that owns the handler.
ActionAction type to dispatch.
Parameters
bindingBinding returned by registerHandler<Model>().
actionAction to execute (moved in).
cbExecExecutor on which the Completion callbacks are posted.
onResultOptional observer run on the owner on the typed result before it is moved into the returned Completion and before the caller's own .then. Used to adopt a result-sourced primary key so the binding is already promoted by the time user code sees the result; empty for every other call.
Returns
Completion that resolves with the typed result or an exception (including ValidationError on LocalBackend when the action fails its validator).

Definition at line 1409 of file bridge.hpp.

◆ executeAttachedVia()

template<typename Model, typename Action>
::morph::async::Completion< typename ::morph::model::ActionTraits< Action >::Result > morph::bridge::Bridge::executeAttachedVia ( const std::shared_ptr< detail::HandlerBinding > & binding,
Action action,
::morph::exec::IExecutor * cbExec,
std::string key )
inline

Dispatches a payload-keyed action on a shared binding, attaching it to the instance key names first.

Waits for any bind in flight, then attaches (or re-points) the binding to key if it does not hold it already, and dispatches once the attach has settled. A refused attach rejects the call with its error; an empty key is refused at once with std::invalid_argument, leaving the binding as it was.

Template Parameters
ModelModel type that owns the handler.
ActionAction type to dispatch.
Parameters
bindingShared binding.
actionAction to execute (moved in).
cbExecExecutor on which the Completion callbacks are posted.
keyCanonical primary key the action names.
Returns
Completion that resolves with the typed result or an exception.

Definition at line 1452 of file bridge.hpp.

◆ executeCreatingVia()

template<typename Model, typename Action>
::morph::async::Completion< typename ::morph::model::ActionTraits< Action >::Result > morph::bridge::Bridge::executeCreatingVia ( const std::shared_ptr< detail::HandlerBinding > & binding,
Action action,
::morph::exec::IExecutor * cbExec )
inline

Dispatches a result-keyed action on a shared binding, giving it an anonymous instance first if it has none, and promoting that instance under the key its result carries.

Template Parameters
ModelModel type that owns the handler.
ActionAction type to dispatch.
Parameters
bindingShared binding.
actionAction to execute (moved in).
cbExecExecutor on which the Completion callbacks are posted.
Returns
Completion that resolves with the typed result or an exception.

Definition at line 1483 of file bridge.hpp.

◆ bindByType()

std::shared_ptr< detail::HandlerBinding > morph::bridge::Bridge::bindByType ( std::string typeId,
BindSharing sharing,
std::string instanceKey = {} )
inline

Binds the model registered as typeId, for a caller compiled without the model's type.

A private binding is bound at once, as registerHandler<Model>() binds one. A shared binding is bound when attached: here when instanceKey is not empty, otherwise by a later attach. The binding is tracked for switchBackend(), deregistered by deregisterHandler, and its instance is owned by the session's principal on a server exactly as a typed handler's is.

Called off the owner — inside a running action, where a BridgeHandler may be constructed too — the registration is posted to the owner, and a call made meanwhile is held. Attaching is the owner's, so a key is refused there.

The bridge cannot tell whether the model is keyed: a shared binding of an unkeyed model is refused, if at all, by the backend when attached.

Parameters
typeIdThe model's registered type id.
sharingPrivate or shared.
instanceKeyFor a shared binding, the key to attach to now, or empty to stay unattached. Must be empty for a private binding, and off the owner.
Returns
The binding.
Exceptions
std::invalid_argumentfor a private binding with a key.
std::logic_errorfor a key given off the owner.

Definition at line 1633 of file bridge.hpp.

◆ executeRaw() [1/2]

::morph::async::Completion< std::string > morph::bridge::Bridge::executeRaw ( const std::shared_ptr< detail::HandlerBinding > & binding,
std::string actionId,
std::string bodyJson,
::morph::exec::IExecutor * cbExec )
inline

Dispatches the action registered as actionId with a JSON body through binding, and resolves with the JSON reply.

The call is stamped with the default session, raced against the execute deadline, counted in pendingCalls(), held while a bind is in flight, and settled by cancelPending exactly as a typed call. It always carries a stop source, since whether the handler is a Task is not known here. Neither the client-side recompute nor the action's validator runs here: they need the action's type, and whoever runs the model runs both. The reply is not offered to typed subscriptions: it is JSON text, not the result a subscriber expects.

The call runs on the instance binding holds now. Unlike a typed call, it does not attach to the key a payload-keyed action names, nor create and promote an instance for a result-keyed one: a keyed action sent through a shared binding attached elsewhere runs on that other instance, and one sent before any attach is rejected as not bound. A keyed action goes through executeRawOn.

Parameters
bindingA binding from bindByType.
actionIdThe action's registered type id.
bodyJsonThe action's JSON body.
cbExecExecutor the Completion callbacks are posted on.
Returns
Completion resolving with the reply's JSON text.

Definition at line 1673 of file bridge.hpp.

◆ executeRaw() [2/2]

::morph::async::Completion< std::string > morph::bridge::Bridge::executeRaw ( const std::shared_ptr< detail::HandlerBinding > & binding,
std::string actionId,
std::string bodyJson,
::morph::exec::IExecutor * cbExec,
::core::async::StopToken stop )
inline

executeRaw, cancellable through stop.

A stop before the call settles rejects it with core::async::OperationCancelled and asks its stop source to stop; a Task handler on a local backend sees that at its next stop-aware co_await. A token already stopped rejects without dispatching.

Parameters
bindingA binding from bindByType.
actionIdThe action's registered type id.
bodyJsonThe action's JSON body.
cbExecExecutor the Completion callbacks are posted on.
stopThe caller's cancel.
Returns
Completion resolving with the reply's JSON text, or rejected with core::async::OperationCancelled.

Definition at line 1712 of file bridge.hpp.

◆ executeRawOn()

::morph::async::Completion< std::string > morph::bridge::Bridge::executeRawOn ( const std::shared_ptr< detail::HandlerBinding > & binding,
std::string instanceKey,
std::string actionId,
std::string bodyJson,
::morph::exec::IExecutor * cbExec )
inline

executeRaw on the instance for instanceKey: attaches the shared binding to it first when it holds another or none.

What a keyed action needs from a caller without its type, which cannot read the key out of the body: the call runs on instanceKey's instance or not at all. An attach that is refused, or superseded by a re-bind, rejects the call with its failure instead of running it on the instance the binding fell back to. The binding stays attached to instanceKey afterwards, as a typed payload-keyed call leaves it. Otherwise as executeRaw.

Parameters
bindingA shared binding from bindByType.
instanceKeyCanonical string encoding of the primary key; see attach.
actionIdThe action's registered type id.
bodyJsonThe action's JSON body.
cbExecExecutor the Completion callbacks are posted on.
Returns
Completion resolving with the reply's JSON text.
Exceptions
std::invalid_argumentfor an empty instanceKey.
std::logic_errorfor a private binding.

Definition at line 1747 of file bridge.hpp.


The documentation for this class was generated from the following file: