Turns an %Statifier.Effect.Invoke{}'s content/src pair into a
Statifier.Machine.t() ready to start as a child session - the seam
ADR-0038 decided: the library never dereferences src.
Spec 6.4.2 treats the two identically ("these services MUST treat values
specified by 'src' and <content> identically"), but they cannot share
one resolution path. content reaching this module has already been
resolved by the pure core (Statifier.Effect.Invoke's own moduledoc);
when it is a binary, 6.4.2 requires an SCXML-typed service to "interpret
[it]... as markup to be executed", and Statifier.compile/2 is exactly
that interpretation - called here with invoke_content_markup: true
(ADR-0042), the one call site in the pipeline that sets it, since this is
the one place a Document.Content markup slice or expr-delivered markup
binary is ever compiled standalone. src names a resource the core never
fetches
(ADR-0031); resolving it is handed to an embedder-supplied function,
never performed here, on the same security posture ADR-0024 already
applies to <data src> - a document-named URI dereferenced by the engine
by default is a request-forgery surface handed to whoever writes the
document.
content is checked before src when a document specifies both: 6.4
treats the two as interchangeable, so a document naming both is already
non-conformant, and there is no ordering rule this module could violate
by picking one over the other.
Every {:error, _} this module returns is the session's cue to raise
error.communication (spec 3.12.2) on the parent and abandon the
invocation without writing a table entry - this module has no opinion on
how that error is delivered; it only decides whether a Machine.t() can
be produced at all.
Summary
Functions
Resolves invoke's content/src into a Machine.t(). opts[:invoke_source],
when given, is a (src :: String.t() -> {:ok, Machine.t()} | {:error, term()})
function an embedder supplies to Statifier.start_session/2; with no
resolver configured, a src-only invocation is {:error, :src_not_resolved} rather than a fetch attempt.
Types
@type reason() :: {:compile, [Statifier.error()]} | :src_not_resolved | {:content_not_markup, term()} | :no_source | term()
Why a source could not be resolved into a Machine.t():
{:compile, errors}-contentwas markup that failed to compile (Statifier.compile/2's own error list).:src_not_resolved-srcwas present but noinvoke_sourceresolver was configured.{:content_not_markup, content}-contentwas present but neithernilnor a binary (a value-shaped<content>, 5.6's other case, naming nothing an SCXML-typed service can start).:no_source- neithersrcnorcontentyielded a usable source: both absent, orcontentabsent andsrcnot a binary (asrcexprthat evaluated to a non-string).
An embedder-supplied invoke_source resolver's own {:error, reason} is
returned unchanged, so its reason shape is not enumerated here.
Functions
@spec resolve(invoke :: Statifier.Effect.Invoke.t(), opts :: keyword()) :: {:ok, Statifier.Machine.t()} | {:error, reason()}
Resolves invoke's content/src into a Machine.t(). opts[:invoke_source],
when given, is a (src :: String.t() -> {:ok, Machine.t()} | {:error, term()})
function an embedder supplies to Statifier.start_session/2; with no
resolver configured, a src-only invocation is {:error, :src_not_resolved} rather than a fetch attempt.
content, when a binary, compiles with invoke_content_markup: true
(ADR-0042) - the one call site in the whole pipeline that sets this
Statifier.compile/2 option, so a namespace-less <content> markup root
compiles here exactly as G.6 places it, and nowhere else relaxes that
check.