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

@revfanc/guard

v0.14.0

Published

A small framework-independent browser Back guard.

Readme

@revfanc/guard

A small framework-independent guard for same-document browser Back.

  • LIFO Handler stack
  • Current-entry and pushState() protection
  • Scoped History management
  • Asynchronous decisions
  • No runtime dependencies
  • ESM, CommonJS, and TypeScript declarations
pnpm add @revfanc/guard

Install before the router performs its first pushState(). The current entry is protected immediately because the default scope matches every URL:

import { create, install } from "@revfanc/guard"

install()

const stop = create(async (resolve) => {
  if (await confirmLeaving()) resolve()
})

stop()

Use scope when only part of the application should create private edges:

install({
  scope: (url) =>
    url.pathname === "/landing" || url.pathname.startsWith("/landing/"),
})

This matches /landing and /landing/**, but not /landing-page. Scope is evaluated for the current URL during the first installation and for the resolved destination URL of every later pushState().

install() protects a matching current entry without changing its visible URL or business state fields. It places one private edge before each matching entry created through history.pushState(). Back first traverses to that edge. Guard immediately restores the original business entry and invokes only the top Handler. Forward transparently skips the edge without invoking business code.

Guard stores versioned private metadata under history.state.__revfanc_guard__ on protected business entries and private edges. Applications must not read, modify, or depend on this field. Public replaceState() calls keep the current business-entry marker while preserving the caller's state fields, structured clone values, URL handling, and native errors without mutating the input object. This lets a new Document adopt the existing edge after a normal reload instead of adding another History entry.

Calling resolve() removes that Handler. If another Handler remains, the page stays in place and the next Back reaches the next layer. If the stack is empty, Guard continues the original Back. Without Handlers, private edges are skipped automatically.

Scope and lifecycle

Only single-step Back across protected entries is guarded. Forward remains navigable but never invokes a Handler. Protecting the current entry adds one History entry. Each matching application pushState() adds two native History entries: one private edge and the requested business entry. Out-of-scope pushState() retains native behavior. On a protected entry, public replaceState() performs the requested native write and then restores only the private marker.

Installation and the History wrapper are permanent for the lifetime of the Document. Scope limits creation of new edges; it does not uninstall the wrapper. Edges already created remain recognizable and are skipped even after navigation leaves scope. Repeated install() calls are ignored and the first successful configuration wins.

Automatic hash entries, history.go(±N) for |N| > 1, long-press history selection, address-bar or other cross-document navigation, tab closing, iframe joint History, and native methods captured before installation are outside the contract. Normal reload of a protected entry is supported. Repeated modules in the same Window reuse the first installed runtime.

scope receives a public URL; relative destinations are resolved against the current location. If it throws, no History write is attempted. The package assumes successful internal History writes after the browser accepts the public pushState() and intentionally has no rollback state machine.

The root History state used for protection must be null, a plain object, or an array. Nested structured-clone values such as Map, Date, arrays, cycles, and shared references are preserved. A missing edge or invalid/unsupported private marker fails open rather than starting speculative History traversal.

Development

pnpm install
pnpm check
pnpm test:e2e
pnpm pack:check

Automated E2E tests exercise Chromium, Firefox, and WebKit. Release validation also requires manual Back checks using the real Chrome toolbar.

License

MIT