@flowspine/core
v0.1.1-beta.20260407.1
Published
Flowspine core DSL and step runtime contracts (beta, not production-ready)
Maintainers
Readme
@flowspine/core
Flowspine core package for:
- defining process topology with
defineProcess - defining executable step contracts with
defineStep - executing individual steps with
runStep
This package is the semantic foundation of Flowspine.
Use it when you need to:
- describe a business flow as a graph
- define step input/output contracts
- implement step logic
- add retry / timeout / idempotency behavior to step execution
Do not use this package for:
- repository-wide extraction of process files
- catalog generation
- validation/export/watch workflows
For those tasks, use @flowspine/extractor or @flowspine/cli.
Installation
pnpm add @flowspine/core@betaMental Model
Flowspine has two different concerns inside this package:
1. Process topology
defineProcess(...) describes a process graph:
- nodes
- edges
- branch conditions
- start/end structure
It does not execute the graph.
2. Step runtime
defineStep(...) and runStep(...) define and execute a single step:
- validate input
- run handler
- validate output
- enforce allowed outcomes
- apply retry/timeout/idempotency options
If you need full graph execution/orchestration, that is outside the scope of this package.
API Overview
Process DSL
defineProcess(processId, build)Builder methods:
step(id, metadata?)branch(id, metadata?)event(id, metadata?)action(id, metadata?)start(targetNode)end(targetNode)link(from, to, condition?)when(branchNode, condition)
Step Runtime
defineStep({
id,
validateInput,
validateOutput,
allowedOutcomes,
handler,
timeoutMs?,
retry?
})runStep(step, input, options?)runStep(...) options include:
processIdtraceIdtimeoutMsretryidempotencyKeycacheonEvent
Example: Define Process Topology
import { defineProcess } from "@flowspine/core";
export default defineProcess("order-fulfillment", ({ step, branch, action, event, start, end, link, when }) => {
const validateOrder = step("ValidateOrder", {
owner: "CheckoutService",
description: "Validate payload, customer status, and line items"
});
const paymentRequired = branch("PaymentRequired", {
description: "Branch by order total and payment method"
});
const chargePayment = action("ChargePayment", {
description: "Authorize and capture payment"
});
const reserveInventory = action("ReserveInventory", {
description: "Reserve items for shipment"
});
const orderConfirmed = event("OrderConfirmed");
const orderRejected = event("OrderRejected");
start(validateOrder);
link(validateOrder, paymentRequired);
link(when(paymentRequired, "yes"), chargePayment);
link(when(paymentRequired, "no"), reserveInventory);
link(chargePayment, reserveInventory, "success");
link(chargePayment, orderRejected, "failed");
link(reserveInventory, orderConfirmed);
end(orderConfirmed);
end(orderRejected);
});Example: Define and Run a Step
import { defineStep, runStep } from "@flowspine/core";
const ValidateOrder = defineStep({
id: "ValidateOrder",
validateInput: (v): v is { orderId: string; itemsCount: number } =>
Boolean(v && typeof v === "object" && typeof (v as { orderId?: unknown }).orderId === "string"),
validateOutput: (v): v is { orderId: string; valid: boolean } =>
Boolean(v && typeof v === "object" && typeof (v as { orderId?: unknown }).orderId === "string"),
allowedOutcomes: ["success", "invalid_order"] as const,
handler: async (input) => {
if (input.itemsCount <= 0) {
return {
outcome: "invalid_order",
output: { orderId: input.orderId, valid: false }
};
}
return {
outcome: "success",
output: { orderId: input.orderId, valid: true }
};
}
});
const result = await runStep(ValidateOrder, {
orderId: "ord-1",
itemsCount: 2
});
if (result.outcome !== "success") {
throw new Error("Order is invalid.");
}Runtime Guarantees
runStep(...) enforces:
- input validation before handler execution
- outcome membership in
allowedOutcomes - output validation after handler execution
- timeout handling
- retry policy handling
- optional idempotency cache handling
This makes defineStep useful as a strongly-guarded step contract, even outside full Flowspine process graphs.
Important Constraints
allowedOutcomesmust not be emptyretry.maxAttemptsmust be>= 1- timeout must be
> 0 when(...)is only valid for branch nodes- duplicate node IDs in a process throw an error
Also note:
action("X")anddefineStep({ id: "X" })may be used together by convention, but they are not automatically linked by the package- metadata is optional and not globally schema-enforced
When To Reach For Other Packages
Use @flowspine/extractor when you need to read process definitions from TypeScript files and build a catalog.
Use @flowspine/cli when you need operational workflows such as:
- scaffold
- build
- validate
- export
- watch
Related Packages
@flowspine/extractor@flowspine/cli
License
Apache 2.0
Copyright
Copyright (c) 2026 Flowspine contributors.
