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

@flowslens/cli

v1.1.2

Published

Trace any user action from the UI to the database. Maps React/Next + NestJS/Express + Prisma/Mongoose features, shows what a change would break, and serves a local dashboard.

Readme

@flowslens/cli

Trace any user action from the UI to the database.

You join a project. You need to change one screen. So you start clicking through files: which handler runs, which endpoint it calls, which service answers, which collection it writes. Eight tools and an afternoon later you still do not know what else writes that collection.

Flowslens reads your source and answers in one command.

npx @flowslens/cli scan .

It only reads files. It never connects to a database, never runs your code, and never writes anything into your project.


First: will this work on my project?

Flowslens reads a specific set of stacks. Check here before installing — if your stack is on the right, you will get a file count and little else.

| It reads today | Not yet | | --------------------------------------------------------------------------- | ----------------------------------- | | React and Next.js (pages/ and App Router) | Vue, Svelte, Angular, Astro | | NestJS, Express, Fastify | Django, Rails, Go, Java, .NET, PHP | | Next.js pages/api + App Router handlers, Nuxt server/api | GraphQL and tRPC resolvers | | MongoDB via Mongoose, and the native driver | Prisma, TypeORM, Sequelize, raw SQL | | TypeScript or plain JavaScript with JSX (.js, .jsx, .mjs, .cjs) | Queues, cron jobs, websockets |

So the sweet spot is React/Next + NestJS or Express + Mongoose. Any folder layout of those works — Flowslens decides what a file is by reading it, not by which directory it sits in, and a frontend and backend in separate repositories is a first-class case.

Point it at something unsupported and it says so plainly, rather than reporting zero and letting you assume it is broken.


Install

npm install -g @flowslens/cli

Or run it without installing anything:

npx @flowslens/cli scan .

Requires Node 18.18 or newer. Nothing else — no database, no config file, no plugin in your project.

The package is @flowslens/cli. The command is flowlens. The extra s is in the npm scope only.


Your first two minutes

1. Scan

cd ~/code/my-app
flowlens scan

This is the only command you need to remember. It prints what it found, what looks wrong, every feature it can trace, and the command to run next:

Feature flows (6)
─────────────────
id                            action               endpoint                       collections
────────────────────────────  ───────────────────  ─────────────────────────────  ───────────────────────────────────
customerspage-delete          Customers · Delete   DELETE /customers/:param       customers,auditlogs
orderform-submit-order        Submit Order         POST /orders                   customers,products,auditlogs,orders
customerform-create-customer  Create Customer      POST /customers                auditlogs,customers

next:  flowlens flow customerspage-delete
   or:  flowlens serve

2. Pick a feature and follow it

Copy any id from that table:

flowlens flow orderform-submit-order
Submit Order  (orderform-submit-order)
web/src/components/OrderForm.tsx:42
risk high (50)   evidence static

USER ACTION
└── [ui action]   Submit Order
    web/src/components/OrderForm.tsx:42
│
▼
FRONTEND
└── [handler]     OrderForm.handleSubmit
    web/src/components/OrderForm.tsx:15  sets: note, products
│
▼
NETWORK
├── [api call]    POST /orders
│   web/src/components/OrderForm.tsx:16  body: customerId, products, note
└── [route]       POST /orders
    api/src/orders/orders.controller.ts:9  dto: CreateOrderDto
│
▼
BACKEND
├── [method]      OrdersController.create
├── [method]      OrdersService.create
└── [method]      AuditService.record
│
▼
DATABASE
├── [db op]       orders.create        schema: Order      create
├── [db op]       auditlogs.create     schema: AuditLog   create
└── [db op]       customers.findById   schema: Customer   read

3. Or click around instead

flowlens serve

A local dashboard on http://127.0.0.1:4177, which asks six questions about the feature you have open. Each tab carries its own headline number, so the worrying one is visible before you open it:

Flow · 24   APIs · 1   Timing · no runs   Breaks · 2   Tests · none   Changed · 5

| Tab | The question | Where the answer comes from | | ----------- | ----------------------------------------- | -------------------------------------------------------------- | | Flow | What happens when a user does this? | click → handler → request → route → service → collection | | APIs | What exactly does it request? | body, guards, DTO, the code it runs, every collection, callers | | Timing | Where does the time go? | runtime spans only — no spans, no numbers, never an estimate | | Breaks | What else would a change here break? | the graph walked backwards from every step | | Tests | What would catch it if you broke it? | which test files import the files this flow runs through | | Changed | What do my uncommitted edits put at risk? | git status crossed with the graph |

Breaks is the one that changes how you work. A flow read on its own is quietly misleading: most of the chain is shared, and editing one service because one screen needs a field is a five-minute change that breaks four other screens. Breaks splits the same steps into "shared with other features" and "only this one uses", and keeps infrastructure — a toast hook, a cache, an audit trail — out of the way so the real findings are not competing with wallpaper.

Changed needs no instrumentation and no tests, so it works on the first run:

high risk   1 changed file is used by 5 features; 5 of them have no test.

An action that makes several requests is shown as a sequence, and the shapes are told apart rather than lumped together:

| In your code | What the APIs tab says | | --------------------------------------------------------- | --------------------------------------------- | | const a = await get(); post({ id: a.id }) | needs the response from GET … | | post(…).then(() => put(…)) | only after POST … resolves | | Promise.all([get(a), get(b)]) | sent at the same time as GET … | | useEffect(() => get(…), [user]) + a call setting user | re-runs when the state set by GET … arrives | | if (isEdit) put() else post() | one step, two alternatives, joined with or | | a call inside a catch | only when the request fails |

Every file:line in every tab opens your editor (?editor=vscode, cursor, idea, zed, …).

That is the whole workflow. Everything below is for when you want more.


Four more questions it answers

# "What is this file I'm reading?" — which features run through this line
flowlens where src/components/OrderForm.tsx:20

# "If I change this, what breaks?"
flowlens impact AuditService.record

# "What is already wrong here?" — broken calls, dead endpoints, shared writes
flowlens doctor

# "Write the docs for me" — a feature document in markdown
flowlens flow orderform-submit-order --markdown > docs/submit-order.md

impact is the one to reach for before a refactor: it reports the blast radius and every user-visible feature that would be affected.


Something went wrong

flowlens: command not found — either the global install is not on your PATH, or you skipped it. Use npx @flowslens/cli <command> instead, which always works.

"No flows found" or an almost-empty report. Flowslens prints the reason under Notes. The three common ones:

| Note says | Fix | | ------------------------------------- | ------------------------------------------------------------ | | Frontend found, but no backend routes | Your backend is a separate repo: flowlens scan ./web ./api | | No API calls detected | Your requests go through a house-built wrapper — see below | | Contains 40 .vue files, not parsed | Unsupported stack; see the table at the top |

Your team wraps HTTP in its own helpers. Flowslens already reads the common shape — verb in the function name, path in an options object:

getRequest({ url: getUsersList }); //  GET /users
patchRequestNoLoader({ url: getUser, params: `/${id}` }); //  PATCH /users/:id

If yours is named differently, describe it once (capture group 1 is the verb):

flowlens scan --request-fn '^(get|post|put|patch|delete)Api'

Routes and calls match nothing, but both were found. Usually a global prefix. /api is stripped from both sides by default; change it with --api-prefix /v2.

Your terminal draws boxes as garbage. FLOWLENS_ASCII=1 flowlens flows, or NO_COLOR=1 to drop colour.

SyntaxError on an old Node. You need 18.18 or newer; node --version.


Commands

| Command | What it answers | | ------------------------------- | --------------------------------------------------- | | flowlens stack [project] | What is this built with? Frameworks, with versions. | | flowlens scan [project] | Build the graph, and list what it found. | | flowlens flows [project] | Which user actions reach the backend? | | flowlens flow <id> | Everything one click does, end to end. | | flowlens flow <id> --markdown | Generate a living feature document. | | flowlens where <file>:<line> | What is this code for? Features running through it. | | flowlens impact <symbol> | If I change this, what breaks? | | flowlens doctor [project] | Broken API calls, dead endpoints, shared writes. | | flowlens serve [project] | The dashboard. | | flowlens init [project] | Detect the layout and write flowlens.config.json. | | flowlens trace [project] | Merge recorded runtime spans into the graph. |

[project] defaults to the current directory, and scan works from any subdirectory of it. Add --json to any command for machine-readable output.


Two repositories, one graph

A frontend and backend in sibling folders is the case Flowslens is built for — the seam between them is the whole point:

flowlens scan ./my-web ./my-api
flowlens scan ./api ./web ./mobile     # several consumers of one API

Scanning every consumer at once also sharpens the findings: an endpoint that looks dead against one frontend may simply be called by the mobile app.


Save your settings

Tired of retyping flags? flowlens init writes a flowlens.config.json you can commit, so everyone on the team gets the same graph:

{
  // Scanned together when no paths are given on the command line.
  "roots": [".", "../shop-api"],

  // Stripped from BOTH frontend URLs and backend routes.
  "apiPrefixes": ["/api"],

  // Your request layer: capture group 1 is the HTTP verb.
  "requestFunctionPattern": "^(get|post|put|patch|delete)Request",

  "ignore": ["legacy", "generated"],
}

Paths in roots are resolved relative to the config file and should use /, so one committed file works for everyone on any OS.


Where it puts things

Nothing goes into your project. The graph lives in your OS cache, keyed by project path, so git status after a scan is empty:

| OS | Location | | ------- | ---------------------------------------------------- | | Linux | $XDG_CACHE_HOME/flowlens, else ~/.cache/flowlens | | macOS | ~/Library/Caches/flowlens | | Windows | %LOCALAPPDATA%\flowlens\Cache |

scan and serve both print the exact path. FLOWLENS_CACHE moves it. flowlens init is the one command that writes to your project, because writing a config file is what you asked it to do.


Optional: prove it actually ran

Everything above is read from source, so a step means "this path can run". Add @flowslens/runtime to your app and a step becomes confirmed — it did run, and here is how long it took. Entirely optional; the CLI works without it.


Related packages

Documentation

Full README, architecture notes and roadmap: This page is the documentation. Questions and bug reports: [email protected].

Licence

MIT