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

@zenness/cli

v0.3.2

Published

The zenness CLI — scaffold and run full-stack projects built on Vite + Nitro and Astro.

Readme

@zenness/cli

The zenness CLI. Scaffolds and runs full-stack projects built on Vite + Nitro (backend) and Astro (frontend) — on one origin, with no CORS to configure and one thing to deploy.

npm create zn                  # scaffold a new project
npx @zenness/cli init my-app   # the same thing, spelled directly

Flags need a -- in front of them through npm create, which parses the line first and keeps every flag it finds — npm create zn -- ./app --shape backend --yes. pnpm, yarn and bun forward flags either way, and accept the separator too, so that one spelling works everywhere; without it, npm hands the scaffolder the values with no flags attached, and it will say so rather than build the wrong project.

The first form goes through a shim that forwards to zn init here. There are three of them, because the registry resolves each spelling to a different package name — create-zn for npm create zn, create-zenness for npm create zenness, and @zenness/create for the bare scope npm create @zenness. All three call runCreate() exported from this package, so there is one install flow, not three.

Installed into a project, the binary is zn:

npm i -D @zenness/cli
npx zn dev

Commands

| Command | What it does | | --------------- | ------------------------------------------------- | | zn init [dir] | Interactive install flow. Aliased as zn create. | | zn dev | Starts the right dev server for this project | | zn build | Builds for production: Astro, then Nitro | | zn preview | Serves the production build |

dev, build and preview prefer the project's own package.json script when one exists, so a project that customises dev keeps its behaviour. What the project is comes from the answers zn init recorded in its package.json ("zenness": { "shape": [...] }); a project assembled by hand is still recognised from its config files.

The parts

zn init asks which parts you want, and they compose:

| Part | What it adds | | -------------- | ------------------- | | web-frontend | Astro pages | | backend | A Nitro API on Vite |

Either part on its own is a project: Astro alone is a static site with no server to run, and Nitro alone is a headless API. Together they are one project, not two in a folder — see Connections below.

Features

Six things a project decides once, at the start. All are off by default, and each arrives as ordinary files in your project rather than a dependency to configure.

| Feature | What you get | Needs | | -------- | -------------------------------------------------------------------------------------- | ------------------ | | rss | /rss.xml from your content collection, linked from every page for autodiscovery | web frontend | | seo | Open Graph and Twitter tags, JSON-LD, and robots.txt | web frontend | | search | Pagefind, indexed during npm run build — no service to run, no index to keep in sync | web frontend | | auth | Register, log in, log out and "who am I" — scrypt passwords, signed session cookies | backend + database | | media | Uploads keyed by content hash, with a table recording where each asset is used | backend + database | | forms | A validating endpoint that sends mail (SMTP or Resend), with a honeypot | backend |

zn init ./app --features rss,seo,search,auth,media,forms --database sqlite --email smtp --yes

The database is db0 through Nitro's useDatabase(), with .sql migrations in server/migrations/ applied in filename order at boot. db0 rather than an ORM's own driver for one concrete reason: its connectors cover SQLite, Postgres, libSQL and Cloudflare D1, so choosing a database does not cost you the deploy presets. --database sqlite uses better-sqlite3, which is native and cannot run on Workers; the installer says so when you pick that combination.

auth is deliberately small: a sessions table, scrypt from node:crypto, and signed cookies, readable end to end. No OAuth, no MFA, no password reset, no rate limiting. Those are real requirements, and this scaffolds only the part that is boilerplate.

Selling from your content

--commerce shopify adds an editorial section that merchandises real products, and answers the question a CMS and a store on either side of a redirect cannot: which article sold what.

zn init ./shop --commerce shopify --store your-store.myshopify.com --journal journal --yes

| Route or file | What it does | | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | | /journal (or /blog, /guides) | A content collection whose frontmatter carries Shopify product handles | | src/components/ShopTheLook.astro | Renders those products with the page, price and stock filled in at request time | | server/routes/api/products/[handle].ts | The volatile half — price and availability, cached for a minute | | server/routes/api/shopify/webhook.ts | Verifies the HMAC before parsing, answers immediately, deduplicates retries | | server/routes/api/attribution/* | A first-party session, a record of what it read, and a report that publishes its own coverage next to every revenue figure |

The split is the point: an article is copy and images and a list of handles, none of which changes on its own, so it caches hard; a price changes without anyone editing anything, so it never enters the cached page. Attribution is first-party throughout — a cookie this server sets, a call to this server, and the join arriving on a webhook. --no-attribution keeps the storefront without the measurement. Nothing here touches checkout, payment, tax or fraud; Shopify does those.

Non-interactive use

Every prompt has a matching flag, so the same flow works in CI:

zn init ./app --preset vercel --yes
zn init ./site --shape web-frontend --css tailwind --sitemap --site https://example.com --yes
zn init ./api --shape backend --database postgres --features auth,forms --yes
zn init ./app --tools prettier,biome --ui react,svelte --no-install --no-git --yes

--yes accepts defaults for anything not passed. Flags: --shape, --css, --processor, --ui, --tools, --preset, --sitemap, --site, --commerce, --store, --journal, --attribution, --features, --database, --email, --install/--no-install, --git/--no-git. Two more work on every command: --no-color (as does NO_COLOR) and --debug (as does ZN_DEBUG).

--shape, --ui, --tools and --features are the multi-select prompts and take a comma-separated list; none answers one with nothing (--ui none), which is how a script opts out explicitly rather than inheriting whatever the default becomes later. --shape defaults to web-frontend,backend.

A flag that cannot be honoured is refused rather than quietly downgraded: --commerce shopify --shape web-frontend stops and says why, as do --features auth --shape web-frontend and --features media --database none. So does a flag that does not exist — --shpe backend used to scaffold the default shape in silence.

The run narrates itself through Consola: resolved answers first, then each thing that gets wired up, so CI logs say the same as an interactive terminal. Prompts, the spinner and the closing summary stay with @clack/prompts — the two never interleave.

What every project gets

Beyond Astro, Nitro and Partytown, three libraries are installed and already used, so each has a working example in the project rather than a docs tab: Consola (one tagged logger, request-lifecycle logging, a permanently-present debug line behind NITRO_LOG_LEVEL), ofetch (the homepage's API call), and magic-regexp (a slug pattern that validates a dynamic route's param). Deleting the example takes the dependency with it. Zod ships inside Astro as astro/zod.

--css adds Tailwind, Bootstrap, shadcn/ui or Ant Design; --processor adds Sass or PostCSS; --tools adds Prettier, Autoprefixer, Stylelint or Biome — and runs them over the new project once its dependencies are installed, so npm run format:check is green in a project you have not touched yet.

--ui adds Astro's React, Vue and/or Svelte integrations, one example component each under src/components/, and — for React — the jsx/jsxImportSource compiler options a .tsx component needs under Astro's own tsconfig. The components are not imported anywhere: which page wants an island is your call, not the installer's.

--preset writes the Nitro deploy target: Vercel, Firebase App Hosting, Cloudflare Workers or Zephyr, with the host's environment variables named (and left empty) in .env.

Questions that stop applying are not asked. A headless backend is never asked about CSS frameworks, and a project with no Nitro in it is never asked for a deploy preset.

Connections

Templates are bundled into dist/templates at build time, so zn init writes from local disk — it never fetches a tarball, and works offline. Their source is templates/ at the repo root.

One template per part, always. There is no template for a combination. A selection copies each part's template in turn, and the files every template carries (package.json, .env, .gitignore, README.md) are combined rather than overwritten, so the last one copied cannot take the others' dependencies with it.

What a combination needs because the parts are together is a connection, applied after the copy by src/generators/connect.ts. Connections are indexed by the set of parts each one needs, and every connection whose parts are all present applies:

| Connection | What it supplies | | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | web-frontend + backend | the dev proxy, Nitro's publicAssets, the merged scripts, the second tsconfig, scripts/dev.mjs, and one ZN_API_PORT that both halves read |

Indexing by sets rather than by combinations is the whole trick: a scaffolder keyed on combinations needs 2ⁿ templates, and this needs n templates plus however many connections genuinely have something to say. The powerset is traversed, never enumerated. Adding a part costs one template plus the edges you actually care about.

Three rules keep that predictable as the list grows. Smaller sets apply first, so a connection over three parts can build on the pairs inside it; ties go in declaration order; and where two write the same value the later one wins — which is why each connection declares what it writes, so a conflict is something you can see before it happens. The rule is also monotone: a connection names parts that must be present, never absent. Adding a part to a selection can only add connections, never retract one.

The prose problem

Almost every scaffolder generates correct configuration and leaves lying documentation next to it — the README that describes a project you did not scaffold. Each template here is written to be scaffolded alone, so the frontend's home page says there is no server of your own to run. True, until a backend is scaffolded beside it.

So the sentences that stop being true are values a connection overwrites. They sit in named regions:

<!-- zn:region lede -->
Astro renders these pages, and there is no server of your own to run.
<!-- zn:endregion -->

The markers are whole lines, commented in whatever syntax surrounds them, so a template carrying them is still a working file that renders, typechecks and reads normally on its own — which matters, because these templates are meant to be scaffolded and read on their own too. A connection replaces the region's body; anything left unclaimed loses its markers and keeps its content. The replacement copy lives in templates/connections/<parts>/prose/, as markdown, so prose diffs as prose.

Versions

Every version the installer can write lives in one object (src/versions.ts), so what a new project gets is one file to read and one file to change. Nitro is pinned exactly and deliberately — v3 is a moving beta that has already renamed public APIs between releases, and the templates are written against this exact build. Everything else is a caret range.

A weekly job diffs that file against the registry, and a nightly canary scaffolds every shape with nothing pinned and builds it, so a breaking release turns up here before it turns up in your install.

Releases are published from GitHub Actions with npm provenance, so every version on this page carries an attestation of the workflow, repository and commit that built it.

Links

MIT.