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

@pylonts/flow

v1.1.3

Published

Flow framework — blackboard slots, named-step runner, ts-morph flow scanning, controller-type generation

Readme

@pylonts/flow

Flow framework — blackboard Flow, named-step runner, ts-morph flow scanning, controller-type generation.

New here? Start with GUIDE.md — end-to-end: generate controller_types, write slots, write flow files, run.

Concepts

  • Blackboard (Flow): each run creates one fresh Flow (a plain shared record) and destroys it afterwards. The slot contract is project-defined and hand-maintained: slots are just the record's fields, e.g. a step writes flow.bd = {...} and a later step reads flow.bd.phone. The library only requires a record of shared data (Flow<TSlots> = TSlots).
  • Flow files: flow/flows/<module>.flow.ts (driver api) or flow/flows/<module>.<driver>.flow.ts (e.g. browser). Each file's default export object lists its flow methods: method name = flow key.
  • Orchestration: a plain name array. The same names run unchanged under any driver — implementations are pure methods (flow, deps) injected by the runner.

Usage

import { scanFlows, runSteps } from '@pylonts/flow';
import type { Flow } from './flow/slots.ts';   // project slot contract

// a step: pure method reading/writing the shared Flow via injected deps
const adminAddBd = async (flow: Flow, deps: ApiDeps) => {
  const bd = await deps.getModule('admin-app').getController(BdController).add({ ... });
  flow.bd = { id: bd.id, name: bd.name, phone: bd.phone };
};

const index = scanFlows('flow/flows');
const flow = await runSteps({
  index,
  driver: 'browser',              // swap to 'api' for the api driver
  deps: page,                     // playwright Page / ApiDeps
  names: ['adminAddBd', 'bdLogin', 'registerMerchant'],
});
console.log(flow.bd);             // the shared blackboard, typed via Flow

The scanner enforces the cross-driver contract: the same module must expose the same flow names under every driver it implements (throws otherwise).

Browser entry

Launching playwright, running the named steps against a fresh page, and closing the browser is provided out of the box (playwright is a peer dependency):

import { runBrowserFlow } from '@pylonts/flow';

const flow = await runBrowserFlow({
  flowsDir: 'flow/flows',
  names: ['adminAddBd', 'bdLogin', 'registerMerchant'],
});
console.log(flow.bd);   // shared blackboard after the run

Controller-type generation

Scans project controllers (@Body/@Response decorators) and emits shared abstract type facades for flow api drivers:

import { generateControllerTypes } from '@pylonts/flow';

generateControllerTypes({
  modulesRoot: 'api-admin/src/modules',
  outputDir: 'flow/controller_types',
});

Or via the CLI (tsx is required as a peer dependency):

pylon-flow gen-types
pylon-flow gen-types --modules-root api-admin/src/modules --output-dir flow/controller_types

--modules-root defaults to ./api-admin/src/modules, --output-dir to ./flow/controller_types.

Running flows (CLI)

Run a named flow without project-side entry files (tsx, playwright and @pylonts/runtime are peer dependencies):

# api driver: local runtime, direct DB (run from the backend root so tsconfig
# path aliases resolve; --cwd defaults to process.cwd())
pylon-flow run --driver api --flows ./flow/flows --names adminAddBd,bdLogin,registerMerchant --cwd . --bootstrap ./flow/api-env.ts

# browser driver: real UI via playwright
pylon-flow run --driver browser --flows ./flow/flows --names adminAddBd

The api driver's --bootstrap module must export bootstrap or bootstrapApi (project singletons like the token Redis driver).

API

  • scanFlows(flowsDir) — ts-morph scan → flows[module][driver] = { file, names }
  • runSteps({ index, driver, deps, names }) — execute named steps against a fresh blackboard
  • runBrowserFlow({ flowsDir, names, headless? }) — browser driver entry (playwright peer dep)
  • generateControllerTypes(options) — regenerate controller type facades
  • scanControllerSkeletons(modulesRoot) — low-level skeleton scan
  • Flow<TSlots> — blackboard type (the shared record itself; Flow<TSlots> = TSlots)

The api driver's local runtime lives in @pylonts/runtime: runLocalFlow({ flowsDir, names, cwd?, bootstrap? }) — scans controllers into a DirectRouter and runs the named steps in-process (direct DB, no HTTP).