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

lna-readiness

v0.1.0

Published

Static CI scanner that finds requests Chrome 142 Local Network Access will silently break — before they ship. CLI + ESLint plugin + GitHub Action.

Readme

lna-readiness

Find the requests Chrome 142 will silently break — before your users do.

$ npx lna-readiness src

src/print.ts
  12:3  error  fetch() to a local address (http://localhost:9100/print) will be gated by
               Chrome 142 LNA and fail silently if the user denies the prompt.
               lna/fetch-private-target
               fix: fetch("http://localhost:9100/print", { targetAddressSpace: "local" })

1 error, 0 warnings across 214 files

What changed

Chrome 142 shipped the Local Network Access permission to the stable channel in late 2025. Any request from a web page to a more-private address space now goes behind a one-time prompt:

  • localhost, 127.0.0.0/8, ::1, 0.0.0.0
  • private IPv4: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
  • carrier-grade NAT 100.64.0.0/10 (Tailscale and friends)
  • link-local 169.254.0.0/16, IPv6 fe80::/10
  • IPv6 ULA fc00::/7
  • *.local and *.localhost (mDNS)

If the user denies or dismisses the prompt, the request fails silently — no console error you can catch reliably, no rejected promise you'd expect. The LocalNetworkAccessRestrictionsTemporaryOptOut enterprise escape hatch is removed after M152, so the gate hardens through 2026.

This breaks a specific, real set of apps: POS and label-printer bridges (the QZ Tray pattern), Office/Figma/Photoshop add-ins talking to a desktop companion over localhost, IoT and router config UIs served from 192.168.x.x, camera snapshot <img> tags, and iframe embeds that reach a private-IP backend.

The fix exists in docs — a targetAddressSpace hint on fetch, an allow="local-network-access" attribute on iframes, a delegated permission from the top frame. What's been missing is a way to find which requests in a codebase need it without shipping to production and waiting for the bug reports. That's this tool.

Install

npm i -D lna-readiness
# or run it once, no install:
npx lna-readiness

CLI

lna-readiness                      # scan the source tree from the current dir
lna-readiness src app pages        # scan specific dirs/globs
lna-readiness --json               # machine-readable, for tooling
lna-readiness --markdown           # a table for PR comments / CI summaries
lna-readiness --fail-on-warning    # warnings fail the build too
lna-readiness --max-warnings 5     # allow up to 5 warnings

Exit code is 1 when there are errors (or warnings over your threshold), 0 otherwise — drop it into any CI step.

What it flags

| Rule | What it catches | | --- | --- | | lna/fetch-private-target | fetch() to a private/local host with no targetAddressSpace hint | | lna/xhr-private-target | XMLHttpRequest.open() to a private/local host | | lna/axios-private-target | axios.*() to a private/local host | | lna/websocket-private-target | new WebSocket() to a private/local host | | lna/eventsource-private-target | new EventSource() to a private/local host | | lna/iframe-missing-allow | <iframe> at a private/local URL missing allow="local-network-access" | | lna/{img,script,source,...}-private-src | sub-resource loaded from a private/local host |

It scans .js/.jsx/.ts/.tsx/.mjs/.cjs, .vue, .svelte, and .html. Requests with a URL it can't resolve statically (a variable, a template with an interpolated host) are left alone — no guessing, so no noise.

ESLint plugin

Same rules, live in your editor and existing lint run (flat config):

// eslint.config.js
import lna from 'lna-readiness/eslint-plugin';

export default [
  {
    plugins: { lna },
    rules: { 'lna/no-unguarded-local-network-request': 'error' },
  },
];

GitHub Action

# .github/workflows/lna.yml
name: LNA Readiness
on: [pull_request]
jobs:
  lna:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: fernforge/lna-readiness@v1
        with:
          # paths: 'src'            # optional, defaults to the whole repo
          # fail-on-warning: true   # optional
          # max-warnings: 0         # optional

The action writes a Markdown table to the job summary and exposes errors / warnings outputs.

Programmatic

import { scan, isPrivateNetworkUrl, targetSpaceOf } from 'lna-readiness';

const { findings } = scan(process.cwd());

isPrivateNetworkUrl('http://192.168.1.20/label'); // true
targetSpaceOf('ws://printer.local:8181/');        // 'local'
targetSpaceOf('https://api.stripe.com');          // 'public'

What this does not do

It's static analysis. It catches requests whose target is a literal string in your source. It can't see a host that's only known at runtime (from config, an env var, or user input) — for those, test with the prompt denied and handle the failure explicitly. It also doesn't check that you actually hold the granted permission; a passing scan means "these calls carry the right hints/attributes," not "these calls will succeed."

Background

License

MIT