State machines and Petri net workflows with the same semantics as Symfony's Workflow component. Markings, transitions, guards, and events — all in TypeScript.
Events fire in Symfony's exact order: guard, leave, transition, enter, entered, completed, announce. Subscribe to any event with typed listeners.
Attach guard expressions to transitions and provide your own evaluator. Integrate role checks, feature flags, or any custom authorization logic.
Petri net support with AND-split (parallel fork), AND-join (synchronization), OR-split (exclusive choice), and XOR patterns for state machines.
Mirrors Symfony's Workflow service. Pass your entity to can() and apply() — the marking is read from and written back automatically via marking stores.
Round-trip import and export for all formats. Import existing Symfony configs including !php/const and !php/enum tags. Generate typed TypeScript modules.
Symfony YAML configs using PHP constants and backed enums are resolved automatically. App\Enum\Status::Active becomes "Active".
Catch unreachable places, dead transitions, and orphan nodes before runtime. Analyze patterns to understand your workflow's structure.
Create an engine, validate, apply transitions, listen to events.
import { WorkflowEngine, validateDefinition } from "symflow/engine";
const definition = {
name: "order",
type: "state_machine",
places: [
{ name: "draft" },
{ name: "submitted" },
{ name: "approved" },
{ name: "fulfilled" },
],
transitions: [
{ name: "submit", froms: ["draft"], tos: ["submitted"] },
{ name: "approve", froms: ["submitted"], tos: ["approved"] },
{ name: "fulfill", froms: ["approved"], tos: ["fulfilled"] },
],
initialMarking: ["draft"],
};
// Validate
const { valid, errors } = validateDefinition(definition);
// Run
const engine = new WorkflowEngine(definition);
engine.apply("submit");
engine.getActivePlaces(); // ["submitted"]
// Listen to events
engine.on("entered", (event) => {
console.log(`Entered via ${event.transition.name}`);
});Like Symfony — pass your entity, the marking is managed for you.
import { createWorkflow, propertyMarkingStore } from "symflow/subject";
interface Order {
id: string;
total: number;
status: string;
}
const workflow = createWorkflow(definition, {
markingStore: propertyMarkingStore("status"),
guardEvaluator: (expr, { subject }) => {
if (expr === "subject.total < 10000") {
return subject.total < 10000;
}
return true;
},
});
const order = { id: "ord_1", total: 500, status: "draft" };
workflow.apply(order, "submit");
console.log(order.status); // "submitted"
workflow.on("entered", (event) => {
console.log(event.subject.id, event.transition.name);
});| Import | Contents | Extra deps |
|---|---|---|
| symflow/engine | WorkflowEngine, validateDefinition, analyzeWorkflow | none |
| symflow/subject | Workflow<T>, createWorkflow, marking stores | none |
| symflow/yaml | Symfony YAML import/export, !php/const, !php/enum | js-yaml |
| symflow/json | JSON import/export | none |
| symflow/typescript | TypeScript codegen | none |
| symflow/react-flow | React Flow graph utilities | @xyflow/react |