npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ablauf

v0.0.4

Published

Workflow engine for Nuxt

Readme

Ablauf

npm version npm downloads License Nuxt

A Nuxt module for defining state machines with typed states, directional transitions, and pluggable handler pipelines.

Features

  • Define workflows as JSON with states, transitions, and directions
  • Transition handler pipelines — run validation, confirmation, or side-effects before a state change
  • Generic conditions bag on transitions for custom domain logic (e.g. nativeOnly, billed)
  • Pluggable storage via WorkflowProvider — built-in file provider (bundles JSON into the server build at compile time, works across every Nitro deploy preset), or bring your own (database, API, etc.)
  • useWorkflow composable with getNextStates, findTransition, transition, and more
  • Auto-imported types, composables, and server utilities
  • Hot-reload for workflow definitions and transition handlers in dev

Quick Setup

Install the module:

npx nuxt module add ablauf

Add it to your nuxt.config.ts:

export default defineNuxtConfig({
  modules: ['ablauf'],

  workflow: {
    provider: 'file',               // 'file' (default) or 'custom'
    workflowsDir: 'server/workflows', // where JSON definitions live
    exposeApi: true,                 // register /api/_workflow routes
  },
})

Defining a Workflow

Create a JSON file in your workflowsDir:

// server/workflows/default.json
{
  "name": "default",
  "description": "Issue Tracker",
  "states": [
    { "slug": "backlog",     "name": "Backlog",     "color": "#6366F1", "category": "start" },
    { "slug": "todo",        "name": "Todo",        "color": "#3B82F6", "category": "in-progress" },
    { "slug": "in-progress", "name": "In Progress", "color": "#F59E0B", "category": "in-progress" },
    { "slug": "review",      "name": "Review",      "color": "#8B5CF6", "category": "in-progress" },
    { "slug": "done",        "name": "Done",        "color": "#10B981", "category": "end" }
  ],
  "transitions": [
    { "from": "backlog", "to": "todo", "direction": "forward" },
    { "from": "todo", "to": "in-progress", "direction": "forward",
      "handler": [{ "name": "permission", "params": { "permission": "start-work" } }] },
    { "from": "in-progress", "to": "review", "direction": "forward" },
    { "from": "review", "to": "done", "direction": "forward",
      "handler": [{ "name": "confirm", "params": { "message": "Mark as done?" } }] },
    { "from": "review", "to": "in-progress", "direction": "backward" },
    { "from": "todo", "to": "backlog", "direction": "backward" }
  ]
}

Each state has a category (start, in-progress, or end) that controls behavior — for example, end states have no outgoing transitions.

Using the Composable

useWorkflow is auto-imported and fetches a workflow by name:

<script setup>
const workflow = await useWorkflow('default')

// Get all states
workflow.getStates()

// Get possible next states from a given state
workflow.getNextStates('todo', 'forward')
// => ['in-progress']

// Find a specific transition rule
workflow.findTransition('todo', 'in-progress')

// Execute a transition (runs handler pipeline)
const result = await workflow.transition('todo', 'in-progress', {
  issue: currentIssue,
})
// Returns the TransitionRule on success, or false if a handler blocked it
</script>

Transition Handlers

Handlers are TypeScript files in app/transitions/ that run during a transition. They can validate, prompt the user, or enrich the transition args.

// app/transitions/confirm.ts
export default defineTransitionHandler({
  name: 'confirm',
  friendlyName: 'Confirm Action',
  description: 'Asks the user to confirm before proceeding.',
  run: async (params, _args) => {
    const message = (params?.message as string) ?? 'Are you sure?'
    if (!window.confirm(message)) {
      return false // blocks the transition
    }
  },
})

Handlers are referenced by name in the workflow JSON:

{ "from": "review", "to": "done", "direction": "forward",
  "handler": [{ "name": "confirm", "params": { "message": "Mark as done?" } }] }

Multiple handlers run in sequence. A handler can:

  • Return false to block the transition
  • Return an object to merge data into the args for subsequent handlers
  • Return nothing to allow the transition to proceed

Set global: true on a handler to run it on every transition automatically.

Custom Workflow Provider

The built-in file provider reads JSON from disk. For database-backed workflows, use the custom provider:

// nuxt.config.ts
export default defineNuxtConfig({
  workflow: {
    provider: 'custom',
  },
})

Then register your provider in a Nitro plugin:

// server/plugins/workflow.ts
export default defineNitroPlugin(() => {
  setWorkflowProvider({
    async getWorkflow(name) {
      return await db.workflows.findOne({ name })
    },
    async listWorkflows() {
      return await db.workflows.findMany()
    },
    // Optional: enable write operations
    async saveWorkflow(workflow) { /* ... */ },
    async deleteWorkflow(name) { /* ... */ },
  })
})

setWorkflowProvider and useWorkflowProvider are auto-imported in server routes.

Typed Conditions

Transitions support a generic conditions bag for domain-specific filtering. Define your conditions shape, then pass a conditionFilter that decides which transitions are available based on runtime context:

interface MyConditions {
  role?: string
  feature?: string
}

const workflow = await useWorkflow<MyConditions>('default', {
  conditionFilter: (rule, context) => {
    if (rule.conditions?.role && rule.conditions.role !== context.userRole) return false
    if (rule.conditions?.feature && !context.enabledFeatures?.includes(rule.conditions.feature)) return false
    return true
  },
})

// Only transitions matching the current context are returned
workflow.getNextStates('todo', 'forward', {
  userRole: 'admin',
  enabledFeatures: ['beta'],
})

In the workflow JSON, attach conditions to any transition:

{ "from": "review", "to": "done", "direction": "forward",
  "conditions": { "role": "admin" } }

API Routes

When exposeApi is enabled (default), two routes are registered:

| Route | Description | | --- | --- | | GET /api/_workflow | List all workflows | | GET /api/_workflow/:name | Get a workflow by name |

Contribution

# Install dependencies
npm install

# Generate type stubs
npm run dev:prepare

# Develop with the playground
npm run dev

# Build the playground
npm run dev:build

# Run ESLint
npm run lint

# Run Vitest
npm run test
npm run test:watch

# Release new version
npm run release