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

@artymclabin/qa-review

v0.3.9

Published

Interactive on-page QA review overlay (spotlight walkthrough, approve/reject verdict ledger) with a framework-agnostic server handler factory and a self-provisioning Postgres store.

Downloads

77

Readme

qa-review

Interactive on-page QA review for React apps: a spotlight walkthrough overlay that steps a reviewer through the elements of a real rendered page (approve / reject / note / undo / design-variation picking), plus a durable server-side verdict ledger with a self-provisioning Postgres store.

Built for "founder reviews the page element by element" workflows: the reviewer opens the live page, the overlay dims everything except the current item, and every verdict is persisted immediately - refreshes, storage wipes, and device switches never lose progress.

Features

  • Spotlight walkthrough - dims the page except the current [data-qa] target; card shows title/subtitle + Approve/Reject/Prev/Next.
  • Round-based review - the set of actionable (not-yet-approved) items is frozen at load with a fixed denominator ("1 of N"), so approved items never re-appear mid-round.
  • Durable verdict ledger - verdicts are saved per item as you go (localStorage cache + server ledger). No bulk reset exists; a single item is re-queued by invalidating just it (verdict: null).
  • Undo, Peek (3s clean-page view), element-picker notes, keyboard driving (A/R/arrows/U/Esc), draggable panel.
  • Design variations - an item can expose N live-swappable variants; the chosen variant is recorded with the approval.
  • Session snapshots - optional "Save review to database" submit that stores the full run (summary + per-item results) for auditability.
  • BYO auth - the server handlers take an authorize(req) callback; plug in any gate (SSO, password, none for local tools). Fail-closed.
  • Self-provisioning Postgres storage - the adapter creates its own qa_review_* tables on first use (versioned, advisory-locked). A site scope column lets one database serve many installs.
  • Cross-page journey - an ordered multi-page review: finishing a page navigates immediately to the next page with pending items (activation query params survive the hop); a completion panel shows only when the whole journey is clean.
  • Task items - selectorless items render as a centered card with an optional action-link button, for visit-this-page checks and decisions.
  • NOT-ALTERED poka-yoke - every verdict stores a content fingerprint; a re-shown rejected item whose content still hashes identical gets a system-computed "NOT ALTERED since your rejection" badge.
  • Device-split approvals - items can require per-device sign-off (PC/mobile); approved only when every required device approved. Plain historical approvals are grandfathered as fully approved.
  • Sub-highlights - highlightWords marks specific words inside the spotlighted element, replacing "where to look" prose.
  • Codenames - a deterministic two-word codename per item ("red-apple") with a Copy-ref button and an exported resolver, so humans and agents can reference items by name. Included in state GET responses.
  • Minimize bubble - the panel collapses to a draggable floating bubble (mouse + touch); tap to restore.
  • No CSS toolchain required - the overlay injects its own stylesheet; brand colors come from a small theme prop.

Install

npm install @artymclabin/qa-review
# or
pnpm add @artymclabin/qa-review

Install straight from git if you want an unreleased commit:

npm install github:ArtyMcLabin/qa-review

For CI environments without registry access, vendor a tarball:

# in this repo
npm pack   # -> artymclabin-qa-review-<version>.tgz
# in the consumer
pnpm add ./vendor/artymclabin-qa-review-<version>.tgz

Peer dependencies: react, react-dom, lucide-react.

Quick start (Next.js App Router)

1. Server: mount the handlers

// src/lib/qa-review.ts
import { createQAReviewHandlers } from "@artymclabin/qa-review/server";

export const qaHandlers = createQAReviewHandlers({
  site: "example-site", // scope for this install (one DB can serve many)
  authorize: async (req) => {
    const user = await verifyMySession(req); // your gate; null -> 401
    return user ? { reviewer: user.name, displayName: user.name } : null;
  },
});
// src/app/api/qa/state/route.ts
import { qaHandlers } from "@/lib/qa-review";
export const runtime = "nodejs";
export const dynamic = "force-dynamic";
export const GET = qaHandlers.stateGET;
export const POST = qaHandlers.statePOST;

Mount submitPOST, sessionsGET, and accessGET the same way on their own routes as needed.

2. Database

Set the connection string (any Postgres - local, Neon, Supabase, RDS):

QA_REVIEW_DATABASE_URL=postgresql://user:[email protected]/mydb

No migrations to write: on the first request the adapter provisions qa_review_state (the per-item ledger), qa_review_sessions (session snapshots), and qa_review_migrations (its own version bookkeeping).

3. Client: mark targets and mount the overlay

// Any element you want reviewed gets a stable [data-qa] anchor:
<section data-qa="hero">...</section>
"use client";
import { QAReviewOverlay } from "@artymclabin/qa-review";

const ITEMS = [
  { id: "hero", title: "Hero headline", selector: '[data-qa="hero"]' },
  { id: "pricing", title: "Pricing table", sub: "Check the currency.", selector: '[data-qa="pricing"]' },
];

export function PageQA() {
  return (
    <QAReviewOverlay
      items={ITEMS}
      target="example-site:/pricing" // one ledger bucket per reviewed surface
      gateParam="qaReview"           // omit if activation is gated upstream
      submitUrl="/api/qa/submit"     // omit to hide the session-save button
      theme={{ accent: "#ffde4d" }}
    />
  );
}

API sketch

// client
import {
  QAReviewOverlay, // the overlay component
  createQAStore,   // per-target verdict store (localStorage + server mirror)
  targetFromLocation, // ?target= override helper (E2E isolation)
} from "@artymclabin/qa-review";

// server
import {
  createQAReviewHandlers, // { stateGET, statePOST, submitPOST, sessionsGET, accessGET }
  createPostgresStorage,  // default storage; implement QAReviewStorage to swap
} from "@artymclabin/qa-review/server";

State endpoint semantics (the ledger):

  • GET ?target=... -> { ok, verdicts: { itemId: { verdict, note, variant } } }
  • POST { target, itemId, verdict | note | variant } -> merge-upsert one item
  • POST { target, itemId, verdict: null } -> delete that one item (re-queues it on the next round). There is deliberately no reset-all.

License

MIT