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

@fingerprintchecker/node

v0.5.0

Published

FingerPrintChecker server SDK for Node.js: API client, sealed result decryption, webhook signature verification.

Readme

@fingerprintchecker/node

Server SDK for FingerPrintChecker. Zero runtime dependencies (built-in fetch and node:crypto), Node 18+, ESM and CommonJS.

import { FpChecker, unsealResult, verifyWebhookSignature, FpCheckerApiError } from '@fingerprintchecker/node';

const fp = new FpChecker({ secretKey: process.env.FPCHECKER_SECRET_KEY! });

const verdict = await fp.checkEligibility({ visitorId: 'v_…' });
if (verdict.ok === false) console.log('refused:', verdict.error ?? verdict.reason);

Client

One async method per OpenAPI operationId: createIntent, getChallenge, identify, botCheck, checkEligibility, releaseEligibility, checkQuota, bindSubject, relateSubjects, sendFeedback, getClaimFlags, searchEvents, getEvent, updateEvent, getVisitor, deleteVisitor, getHealth, getStatus.

  • The secret key goes in the X-Secret-Key header, never in a URL. getChallenge and identify use the publishable key (publishableKey option).
  • 2xx responses are returned as data, including refusals such as { ok: false, error }. Check ok.
  • Non-2xx throws FpCheckerApiError with status, code (error ?? reason from the body) and retryAfter (seconds).
  • Network failures and timeouts also throw FpCheckerApiError, with status 0, code network_error or timeout, and the original error as cause.
  • Redirects are never followed (they would forward the secret key to another origin). baseUrl must be https, except localhost, 127.0.0.1 and [::1].
  • Only GET requests retry (including after network failures and timeouts) (429 and 5xx, at most 2 retries, honouring Retry-After, wait capped at 10 s). POST, PATCH and DELETE are never retried.

Bot check

botCheck classifies a request your backend received (header-only: no script ran in the sender). It is not charged and writes no event. Forward the original URL (the host and scheme the visitor used, not your origin behind a CDN or load balancer) and the client IP. botCheckHeaders keeps only the headers the check needs and drops Cookie, Authorization and everything else.

import express from 'express';
import { FpChecker, botCheckHeaders } from '@fingerprintchecker/node';

const fp = new FpChecker({ secretKey: process.env.FPCHECKER_SECRET_KEY! });
const app = express();
app.set('trust proxy', 1); // number of proxy hops in front of you (or a CIDR list), never `true`: req.ip must not be spoofable

app.use(async (req, res, next) => {
  try {
    const verdict = await fp.botCheck({
      method: req.method,
      url: `${req.protocol}://${req.get('host')}${req.originalUrl}`, // original host and scheme
      ip: req.ip!,
      headers: botCheckHeaders(req.headers),
    });
    if (verdict.bot.result === 'bad') return res.status(403).end();
  } catch {
    // Failure policy: fail open. An outage or rate limit of the check must not take your site down.
  }
  next(); // 'good' = a verified or signed directory bot; verdict.bot.info says which
});

bot.result is good, bad or not_detected; bot.info (id, name, provider, category, identity) is set when the request named a directory bot. A 400 throws FpCheckerApiError with code invalid_request or invalid_ip and the offending field in body.

Rules of use:

  • Call botCheck once per request and reuse the answer; do not call it again for the same request.
  • Never retry a signed request: a retry replays the signature nonce and the request is then judged spoofed (bad).
  • ip must be the trusted client IP, not an address a client can set. With Express set trust proxy to your proxy hop count or CIDR list (never true), then use req.ip.
  • Limits: 3,000 checks per minute per client IP and 1,200 per minute per project (429 beyond that).
  • botCheckHeaders also forwards any header named in the request's signature-input, so signed agents verify; Cookie and Authorization are never forwarded.

Event signals come in two versions: signals.version 1 (before server 0.48.0, no good, no bot.info) and 2. searchEvents also filters by botResult, botCategory, botIdentity and botId.

Sealed results

const result = unsealResult(identifyResponse.sealedResult, [process.env.FPC_SEALED_KEY!]);

Pass every active key (active and previous during rotation). Throws SealedResultError with code of bad_format, unknown_key, decrypt_failed, invalid_key or unsupported_version.

Webhooks

// rawBody must be the exact bytes received, not a re-serialised object.
verifyWebhookSignature({ rawBody, header: req.headers['fpc-signature'] as string, secret: process.env.FPC_WEBHOOK_SECRET! });

Throws WebhookSignatureError with code of malformed_header, bad_signature, expired or future (default tolerance 300 s; toleranceSec must be a finite number >= 0 or a TypeError is thrown).

License

Apache-2.0