Workflows
@atscript/vue-wf (client) and @atscript/moost-wf (server) implement HTTP round-trip workflow forms: long, multi-step user flows where the server decides which screen comes next.
A single workflow is one long-lived conversation: the server pauses, asks the client for a form, validates the answer, optionally branches, and resumes — all over plain HTTP, no sockets, no client-side state machine. Built on top of @moostjs/event-wf.
What it solves
The flows everyone writes by hand and gets wrong:
- Login + MFA — credentials, then OTP iff
mfaEnabled, then a session cookie. - Sign-up with email verification — register, send code, verify, create user, sign in.
- Invite + register — admin invites by email, invitee gets a magic link, lands on a "finish your account" form.
- Multi-step checkout — address, then payment, then confirm, with per-step server validation and conditional branches (e.g. skip payment for free tier).
- Password change with re-auth — confirm current password, collect new one, save.
These are all the same shape: a server-owned state machine with form steps, branching on server-side state the browser is not allowed to see (mfaEnabled, roleName, …).
The mental model
┌─────────┐ POST /wf {wfid} ┌─────────┐
│ client │ ─────────────────────────► │ server │
│ │ │ │
│ AsWf │ ◄───────────────────────── │ Step │
│ Form │ {inputRequired, wfs} │ handler │
│ │ │ │
│ │ POST /wf {wfs, input} │ │
│ │ ─────────────────────────► │ │
│ │ │ Step │
│ │ ◄───────────────────────── │ handler │
│ │ {inputRequired, wfs} │ │
│ │ ... │ │
│ │ ◄───────────────────────── │ Step │
│ │ {finished: true, ...} │ handler │
└─────────┘ └─────────┘Every round-trip is one POST. The response is one of:
{ inputRequired: { payload, transport, context }, wfs }— render this form next, here is the new state token.{ finished: true, ... }— done; the rest of the body is the flow's result payload (session cookie set, redirect URL, …). Thefinished: truemarker is supplied byhandleAsOutletRequest, so step handlers only return their domain data viauseWfFinished().set({ value }).{ sent: true }/{ outlet: "..." }— flow paused waiting for an external event (email link clicked, webhook fired). The current HTTP session is done; resume happens later via the token.{ error: { message } }— recoverable error; the client surfaces it and lets the user retry.
The client never decides which form comes next. The client just renders whatever the server returns, posts back the answer, and keeps going until finished: true.
The two packages
@atscript/vue-wf — the client
One Vue 3 component (Tier 1, auto-imported by AsResolver):
<AsWfForm path="/api/wf" name="auth/login" :types="types" @finished="onFinished" />Or use the composable useWfForm({ ... }) directly when you want to roll your own UI shell around the round-trip.
See Client: AsWfForm for the full API.
@atscript/moost-wf — the server
A thin layer on top of @moostjs/event-wf that:
- Wraps each step's input/output in the wire format the Vue client expects (
{ inputRequired: { payload, transport, context } }). - Caches the serialized atscript schema per type (so the payload is built once per type, not once per step run).
- Validates the incoming input against the form type before the step handler runs.
- Filters workflow context through a
@wf.context.passwhitelist before sending it to the browser (no accidental state leakage). - Catches
requireInput()signals from step handlers and converts them to outlet responses. - Ships an
AsWfStorefor durable state persistence in any atscript-db table — opt in for flows that must survive process restart (email magic links, multi-day approvals).
Quick map
| Topic | Page |
|---|---|
| Two-step end-to-end example | Hello World |
@Workflow + @Step + @WorkflowSchema | Server-Side Authoring |
@FormInput, requireInput, server-side errors | Form Input & Validation |
| Alt actions (resend code, save draft, …) | Actions |
| Passing context to the client form | Context Passing |
Durable state with AsWfStore | State Persistence |
| Magic links, outlets, resume | Outlets & Resume |
<AsWfForm> + useWfForm() reference | Client: AsWfForm |
| Terminal screens, redirects, choice buttons | Finish Screens |
| Login+MFA, invite+register, checkout | Recipes |
Related
- Forms — single-step forms (the rendering layer the workflow client reuses for each step).
- Tables — annotation-driven smart tables.
- moost.org — the moost framework + the underlying
@moostjs/event-wfworkflow engine. - atscript.dev —
.asfiles, annotations, the type system used to declare every form. - db.atscript.dev — the DB layer used by
AsWfStore.