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

whitebox-pro-ui

v0.14.0

Published

WhiteBox admin console — a Vue 3 SPA served automatically by whitebox-pro-server when installed.

Readme

whitebox-pro-ui — the operator console

A VS Code-style activity bar over the surface plugins. Seven modules, all sharing one three-pane grammar: left a searchable, paged rail · centre the thing you're looking at · right what you can do to it.

| module | over | needs | |---|---|---| | Live | live | live:read | | Analytics | analytics | analytics:read / analytics:write | | Audiences | audiences | audiences:read / audiences:write | | Campaigns | campaigns | campaigns:read / campaigns:write | | Journeys | journeys | journeys:read / journeys:write | | People | people | people:read / people:write (+ people:erase) | | Users | oauth | users:manage |

A module's icon only appears when the logged-in user holds one of its permissions — which also means it disappears when the plugin isn't registered at all, since the permission catalog is aggregated from registered plugins. One gate covers both cases.

Run

It is not an npm workspace (the root workspaces globs don't match ui/), so it installs on its own.

# 1) the API, from the repo root
cd server && node --env-file-if-exists=.env scripts/serve-analytics.mjs   # :3000
# 2) this SPA — no env file, nothing to register
cd ui && npm install && npm run dev                                       # :9269

Vite proxies /api → the server (stripping the prefix) and /socket.io with websockets, so the SPA is same-origin and live updates flow through. Point it at a server on another port with WB_API_PROXY=http://localhost:3100 npm run dev.

No env vars

There used to be one, VITE_OAUTH_CLIENT_ID, and getting rid of it fixed a real bug. The client_id is now the fixed constant whitebox-console, which the oauth plugin registers for ${WB_APP_URL}/callback on every boot. Nothing to create, nothing to paste.

That matters because Vite loads .env.local in every mode including production, so a build-time client_id meant whoever built the tarball baked their own database's id into it — which is exactly what shipped in 0.4.0, breaking every install with "Unknown client_id". scripts/check-build.mjs now fails the publish if anything install-specific reaches dist/.

The dev server's port must match the server's WB_APP_URL. The console's redirect_uri is ${location.origin}/callback and OAuth matches it exactly, so :9269 here means WB_APP_URL=http://localhost:9269 there. strictPort is on so a busy port fails loudly instead of sliding to :5175 and failing later at /authorize, a long way from the cause.

VITE_WB_API_BASE exists as a build-time override for serving the console from a different origin than the API. Leave it unset — the server serves both.

There is no static API token. Every module calls its plugin with the logged-in user's own session token, and each plugin independently requires its own scopes — which the server computes from that user's real grants at login rather than trusting anything the client sends.

Conventions

Three that are load-bearing, and worth reading before adding a module:

  • Save/discardADR 0001. Every editor follows one fixed interaction: Discard + Save always rendered and disabled rather than hidden when clean, a fading "Unsaved changes" note, Discard reverting to the last-saved state. Don't invent a second one.
  • The rail is a componentcomponents/RailPane.vue. Search at the top with the module's add button inside it, the list, then a foot with the match count and pager. Two paging modes: pass items and it pages them client-side, or pass total + page and handle update:page for a real server query. useRailPage.ts owns the server-side half — debounce, reset-on-new-term, step-back-past-the-end.
  • Success is shown, not toasted. The app confirms an action by rendering its result; only notifyError exists. A receipt line (what actually changed) beats a toast that says "Saved".

Stack

Vue 3 + Vite · PrimeVue (styled mode) + PrimeIcons · Pinia · Vue Router · ECharts (vue-echarts) · Vue Flow (the journey canvas) · TinyMCE + CodeMirror (campaign content) · socket.io-client (live report updates).

PrimeVue injects its theme CSS at runtime, after the bundled stylesheets and at equal specificity. App CSS that has to win therefore doubles its class name — .p-paginator.p-paginator { … }. Applying that to only some of a block's selectors is worse than not at all, because the ones you missed lose.

Demo data

The server's seed populates enough to exercise every module:

cd server && npm run seed:analytics