Skip to content

Compile-time step ordering

pipeway accumulates the context type as you chain .use(). Each step declares what it needs in context. If the current context doesn't satisfy it, the call is a type error — caught by tsc, not at runtime.

The mistake the compiler catches

ts
import { pipe, ok, type Step } from 'pipeway'

// A step that NEEDS `user` already in context.
const requireAdmin: Step<{ user: { admin: boolean } }, { admin: true }> = (ctx) =>
  ok({ admin: true as const })

pipe()
  .use(requireAdmin) // user is not in context yet
  .handle(() => ({ ok: true }))

Add the producer first, and it type-checks

ts
import { pipe, ok, type Step } from 'pipeway'

const requireAdmin: Step<{ user: { admin: boolean } }, { admin: true }> = (ctx) =>
  ok({ admin: true as const })

pipe()
  .use(() => ok({ user: { admin: true } }))
  .use(requireAdmin) // ✅ user is now in context
  .handle(({ admin }) => ({ admin }))

Released under the MIT License.