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

@saastemly/better-faqs

v0.1.0

Published

Frequently asked questions for Better Auth — ordered, grouped, localized, as a plugin.

Readme

better-faqs

A help centre as a Better Auth plugin. Ordered, grouped, localized — no separate CMS, and every table becomes a screen in a generated admin panel.

import { betterAuth } from "better-auth";
import { faqs } from "@saastemly/better-faqs";

export const auth = betterAuth({
  plugins: [faqs({ defaultGroup: "general", defaultLocale: "en" })],
});

The hard part is the order

Nobody struggles to store a question and an answer. What goes wrong is the sequence — an FAQ is hand-curated, the question everyone asks goes first, so position is data an editor sets rather than something derived from a timestamp. Three consequences follow, and they are most of this plugin.

Reordering is one call for the whole group. Not a PATCH per row: an editor dragging the ninth item to the top would issue nine writes, and a failure halfway leaves the list in an order nobody chose.

A partial list is refused. Accepting one would silently drop whatever it omitted to the end in an arbitrary order, and a reorder that loses entries is worse than a reorder that fails. The request must name every entry in the group, exactly once.

Positions are renumbered 0..n-1 on every write, including after a delete. Storing whatever the client sent is how two entries end up both claiming position 3 and the order becomes whichever row the database returned first. Leaving a gap works fine until something assumes the positions are an index.

Groups

One deployment can serve several help centres — shipping, returns, engraving — without a second install. GET /faqs/groups lists what exists with counts, so a nav can build itself.

Locales

One row per (entry, locale). da-DK falls back through da to the entry's own locale, and the response says which it served.

Routes

| Route | Who | |---|---| | GET /faqs?group=&locale= | anyone — active entries, in order | | GET /faqs/groups | anyone | | GET /faqs/admin/entries | editors — inactive included | | POST /faqs/admin/entries | editors — create, appended last | | POST /faqs/admin/entries/update | editors | | POST /faqs/admin/entries/reorder | editors — the whole group at once | | POST /faqs/admin/entries/delete | editors — closes the gap | | POST /faqs/admin/entries/translate | editors |

Who counts as an editor is canEdit, defaulting to a user whose role contains admin.

bun install
bun run test        # 10 tests
bun run typecheck