/** * State-machine invariant preset. * * Builds on the run-scoped `InvariantContext.state` Map shared by every * invariant on every page. The preset compiles down to an ordinary * `Invariant` whose `check()`: * * 1. Reads the previous state label from `ctx.state` (under a key derived * from the SM's `name`). Falls back to `initial` on the first call of * a run. * 2. Calls `derive({ page, url, prev, errors })` to get the new label. * 3. Returns `void` when the label hasn't changed (still in the same * state). * 4. When the label changed, validates the transition against the * `transitions` map — `prev` must list `next` in its allowed-next list. * 5. On a legal transition, updates the state and returns `void`. * 6. On an illegal transition, returns a one-line failure string. The * crawler turns that into an `invariant-violation` PageError as usual. * * Use this for discrete app modes (anonymous → logged-in → in-checkout → * purchased). For non-discrete trans-page properties (monotonic counters, * set-membership), drop down to a plain `Invariant` and use `ctx.state` * directly. */ import type { Invariant, InvariantContext, UrlMatcher } from "./types.js"; export interface StateMachineDeriveContext { /** Playwright Page. */ page: InvariantContext["page"]; /** Current page URL. */ url: string; /** State label from the previous derive — `initial` on the first call. */ prev: S; /** Errors collected on this page so far. */ errors: InvariantContext["errors"]; } export interface StateMachineInvariantOptions { /** Identifier — used as the failure name and the state-bag key. */ name: string; /** State label assumed before the first derive in a run. */ initial: S; /** * Map from each state label to the labels you can legally transition to. * Self-loops (`next === prev`) are always allowed and don't need to appear. * A state with no outgoing edges (terminal) is fine — list it as `[]` or * leave it out entirely; arriving there is legal, leaving is not. */ transitions: Partial>; /** * Compute the current state label from the page. Throws / rejects fail the * invariant with the error message, same as any other invariant. */ derive: (ctx: StateMachineDeriveContext) => S | Promise; /** Phase to evaluate. Default: `afterActions`. */ when?: Invariant["when"]; /** Restrict to URLs matching this matcher. */ urlPattern?: UrlMatcher; } export interface TransitionVerdict { ok: boolean; prev: S; next: S; /** When `ok === false`, a one-line explanation. */ reason?: string; } /** * Pure helper: decide whether a transition `prev → next` is legal under the * given `transitions` map. Self-loops (`next === prev`) are always legal. */ export declare function validateTransition(prev: S, next: S, transitions: Partial>): TransitionVerdict; /** * Build the `ctx.state` key under which a state machine stores its current * label. Exposed so user code or tests can read the same bag the invariant * writes to. */ export declare function stateMachineKey(name: string): string; /** * Read the current state label for a state machine from a state bag, or * `initial` when nothing has been recorded yet. */ export declare function stateMachineCurrent(name: string, initial: S, state: Map): S; /** * Compile a `StateMachineInvariantOptions` down to an ordinary `Invariant`. * The resulting invariant is a regular value — the user can put it in their * `invariants` array alongside any other Invariant. */ export declare function stateMachineInvariant(opts: StateMachineInvariantOptions): Invariant; //# sourceMappingURL=state-machine-invariants.d.ts.map