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

@dregs/sdk

v0.1.0

Published

TypeScript SDK for Dregs, the fraud and abuse scoring service.

Readme

Dregs TypeScript SDK

npm Node License

The official TypeScript client for Dregs, which scores the users of your application for fraud and abuse across four categories: humanity, authenticity, uniqueness, and behavior.

Send events from your backend, read back the scores and the observations behind them.

npm install @dregs/sdk

Node 20 or newer. No runtime dependencies: the SDK calls the runtime's own fetch.

This is the server-side SDK, which authenticates with a secret key and can read scores. The browser tracking script is a separate package, dregs, and uses the public key. You will usually want both: the tracker in the browser, this on your backend.

Getting started

You need the secret key from an API credential, which you will find under Settings → Credentials in the Dregs dashboard. It starts with sk_. The pk_ public key is for the browser tracker and cannot read identities or scores.

import { Dregs } from '@dregs/sdk';

const client = new Dregs({ secretKey: process.env.DREGS_SECRET_KEY });

The key is read from DREGS_SECRET_KEY when you do not pass one, so new Dregs() on its own is usually enough. Build one at startup and keep it; there is nothing to close.

CommonJS works too:

const { Dregs } = require('@dregs/sdk');

Tracking events

await client.track('user.signup', {
  identity: 'user_12345',
  data: { plan: 'pro', referrer: 'partner-x' },
  identityData: { email: '[email protected]', name: 'Ada Lovelace' },
});

identity is your own id for the user — the same one you pass to dregs.identify() in the browser tracker, and the one you look scores up by. It is required: a server-side event carries no device signature, so the identity is the only thing tying the event to a user.

identityData carries attributes of the user rather than the event. The analyzers lean on these heavily, so send them whenever you have them. Name the keys the way your application already does and map them to Dregs's canonical fields under Settings → Mappings; the same goes for event names.

Idempotency

Every event is sent with an id, which makes ingestion idempotent: reposting the same id returns the original event instead of recording a second one. Pass the id your application already has, and a retry after a timeout can never double-count.

await client.track('purchase', { identity: 'user_12345', eventId: `order-${order.id}` });

When you omit it the SDK generates one, which is what makes its own retries safe.

What comes back

const result = await client.track('user.signup', { identity: 'user_12345' });

result.accepted; // true when Dregs recorded the event
result.id; // the event's id

accepted is false in the uncommon case where Dregs accepts the request without recording an event. Failures that are yours to act on throw instead — see Errors.

Reading scores

const scores = await client.identities.scores('user_12345');

scores.humanity; // 85
scores.authenticity; // 72
scores.uniqueness; // 91
scores.behavior; // 68

This is the cheap read and the one most integrations want. A category Dregs has not scored yet reads as null, and a brand-new identity comes back empty. Scores is an array, so you can iterate, map, and destructure it as usual.

Scoring is asynchronous. Scores appear moments after the events that move them, not in the same breath, so read them at a decision point rather than immediately after a track() call.

if (scores.authenticity !== null && scores.authenticity < 40) {
  await holdForReview('user_12345');
}

Seeing exactly why

The scores are the summary; the observations are the evidence. When you need to show or log why an identity scored the way it did, ask for the analysis.

const analysis = await client.identities.analysis('user_12345');

for (const observation of analysis.observations) {
  console.log(`${observation.label}: ${observation.explanation} (value ${observation.value})`);
}

Each observation carries the analyzer that produced it, a value from 0.0 (suspicious) to 1.0 (legitimate), a confidence, a weight, and the counts behind the finding in metadata. analysis() throws NotFoundError until the identity has been analyzed at least once.

The whole identity

const identity = await client.identities.get('user_12345');

identity.displayEmail; // "[email protected]"
identity.humanityScore; // 85
identity.badges; // [{ name: "Account Takeover Suspected", ... }]
identity.data; // every attribute you have sent

Forcing a rescore

await client.identities.analyze('user_12345');

This queues the work and resolves; it does not wait for the cycle to finish. Dregs rescores on its own as events arrive, so you rarely need this outside of a support or backfill flow.

Errors

import { DregsError, NotFoundError, QuotaExceededError, RateLimitError } from '@dregs/sdk';

try {
  await client.track('user.signup', { identity: 'user_12345' });
} catch (error) {
  if (error instanceof QuotaExceededError) {
    // over the monthly event limit; the event was not queued
  } else if (error instanceof RateLimitError) {
    // ingesting too fast; error.retryAfter when the server said how long
  } else if (error instanceof DregsError) {
    // anything else this library throws
  } else {
    throw error;
  }
}

| Error | When | | ----------------------- | -------------------------------------------------- | | BadRequestError | 400, the event was malformed | | AuthenticationError | 401, the secret key was not recognized | | QuotaExceededError | 402, the account is over its monthly event limit | | PermissionDeniedError | 403, the credential may not do this | | NotFoundError | 404, no such identity, or it has not been analyzed | | RateLimitError | 429, too many requests | | ServerError | 5xx | | DregsTimeoutError | the request timed out | | DregsConnectionError | the request never reached Dregs |

All of them derive from DregsError. Those that reached the API also carry statusCode, body, and requestId; error.message is the message the API sent, and error.toString() prefixes it with the status and the request id, which is the form worth putting in a log line.

Arguments the SDK can reject without asking Dregs — a missing identity, an event id over 64 characters, a pk_ key — throw a plain TypeError before anything is sent.

Retries

Connection failures, timeouts, 408s, 429s, and 5xx are retried automatically with exponential backoff and full jitter, honouring Retry-After when the server sends one. Two retries by default:

const client = new Dregs({ maxRetries: 5 }); // or 0 to handle it yourself

Promises, not an async twin

There is one Dregs class and every method returns a promise. JavaScript has no meaningful sync/async split, so unlike the Python SDK there is no async client to choose between — await everything.

Webhooks

Dregs signs every webhook with the channel's signing secret. Verify it against the raw request body before acting on the payload — a re-serialized object will not match, because key order and whitespace change.

import express from 'express';
import { verifyWebhook, WebhookVerificationError } from '@dregs/sdk/webhooks';

app.post('/webhooks/dregs', express.raw({ type: 'application/json' }), (req, res) => {
  let event;

  try {
    event = verifyWebhook({
      payload: req.body, // the Buffer, not req.body parsed as JSON
      signature: req.header('X-Dregs-Signature') ?? '',
      secret: process.env.DREGS_WEBHOOK_SECRET!,
    });
  } catch (error) {
    if (error instanceof WebhookVerificationError) {
      return res.sendStatus(400);
    }

    throw error;
  }

  handle(event);

  res.sendStatus(204);
});

express.raw() matters: the default express.json() hands you a parsed object and the original bytes are gone. The helpers are exported from the package root as well, so import { verifyWebhook } from '@dregs/sdk' works if you would rather not reach for the subpath.

verifyWebhook also rejects payloads older than five minutes as replays; pass tolerance: null to skip that if you are deduplicating on the event id yourself. The signing secret is shown once, when you create the webhook channel, and is not your API secret key.

Configuration

const client = new Dregs({
  secretKey: undefined, // defaults to $DREGS_SECRET_KEY
  baseUrl: undefined, // defaults to $DREGS_BASE_URL, then https://dregs.com/api
  timeout: 10_000, // milliseconds
  maxRetries: 2,
  fetch: undefined, // bring your own fetch for a proxy agent, custom TLS, or instrumentation
});

Type checking

The package ships generated declarations for both the ESM and CommonJS entry points, so there is no @types/dregs to install and every public type is exported. Responses are plain readonly objects; each one also keeps the body it was built from in raw, so a field Dregs adds after this release is reachable without waiting for an SDK upgrade.

import type { Analysis, Category, Identity, Observation, Score, TrackResult } from '@dregs/sdk';

Contributing

See CONTRIBUTING.md. The short version:

npm ci
npm test
npm run lint
npm run typecheck
npm run build

npm ci installs exactly what package-lock.json pins and fails if the lock is out of step, so the same commands produce the same environment locally and in CI.

Links

License

MIT. See LICENSE.