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

@littlefriend/node

v0.1.1

Published

Little Friend for Node.js: send server-confirmed outcomes (orders, signups, upgrades) with a secret server key.

Readme

@littlefriend/node

Send server-confirmed outcomes to Little Friend: the order that was paid, the signup that was verified, the plan that was upgraded. A click in the browser is intent; this is the result.

No dependencies. Node 18.17 or later.

Install

pnpm add @littlefriend/node
import { LittleFriend } from '@littlefriend/node';

const lf = new LittleFriend({ key: process.env.LF_SERVER_KEY! }); // lfs_...

lf.track({
  id: order.id, // optional; reuse it on resend so the outcome counts once
  name: 'order.completed',
  route: '/checkout',
  props: { plan: 'pro', seats: 5 },
  value: { amount: 4900, currency: 'USD' }, // minor units: $49.00
});

Call await lf.shutdown() before the process exits. In a serverless function, call await lf.flush() before returning.

API

new LittleFriend(options)

| Option | Default | | |---|---|---| | key | required | Secret server key, lfs_.... Throws for any other kind of key. | | endpoint | https://in.littlefriend.io | Collector origin. Events go to {endpoint}/v1/server. | | flushAt | 20 | Send once this many events are queued (at most 50). | | flushInterval | 5000 | Send queued events at least this often, in ms. The timer never keeps the process alive. | | maxQueue | 10000 | Most events held in memory. New events beyond it are dropped and reported. | | retries | 3 | Retries for network errors, timeouts, 429 and 5xx. | | timeout | 10000 | Timeout for one request, in ms. | | onError | console.warn | Receives every LittleFriendError. | | fetch | global fetch | Custom fetch, for example with a proxy agent. | | disabled | false | Validate but send nothing, for tests and local development. |

lf.track(event): string | null

| Field | | |---|---| | name | Required. Lowercase, such as order.completed: ^[a-z][a-z0-9_.:-]{0,63}$. | | id | 8 to 32 URL-safe characters. Generated when absent. | | at | Date, epoch ms or ISO string. Default: now. | | route | Path or template. The query and fragment are removed, and id-like or personal segments become :id or :redacted, before the event is queued. | | sessionId | Journey session id handed over from the browser (journey mode projects only). | | correlationId | Links this outcome to the browser event sent with the same id, from track(name, props, correlationId) in @littlefriend/tracker (journey mode). Use an opaque value the browser accepts too: 16 to 64 characters of A-Z a-z 0-9 _ -, such as a base64url or hex SHA-256 digest of the order id, or a UUID. Values that look like an email address or a long number, including a card number with no separators, are dropped. | | props | Up to 8 props: snake_case keys, strings (cut to 64 characters), numbers under 100,000,000, booleans. | | value | { amount, currency }: whole minor units and an ISO 4217 code. |

Returns the event id, or null when the event was not queued. track never throws. A bad name, id or at rejects the event; a bad optional field is dropped, the event still goes, and onError names the field.

lf.flush(): Promise<FlushResult>

Sends everything queued now and resolves when those events are delivered or given up on: { accepted, dropped, failed }. It never rejects.

lf.shutdown({ timeout? }): Promise<FlushResult>

Stops accepting events and flushes. After timeout ms (default 10000) in-flight requests are cancelled and what is left counts as failed.

Errors

onError receives a LittleFriendError with a code, the eventIds concerned and, for delivery errors, the HTTP status.

| Code | Meaning | |---|---| | invalid_event | Not queued: bad name, id or time. | | invalid_field | Queued without the fields in fields. | | queue_full | Not queued: maxQueue reached. | | closed | track after shutdown. | | rejected | The collector refused the batch (400, 401, 403, 413). Not retried. | | send_failed | Network errors, timeouts, 429 or 5xx outlasted the retries. | | dropped | The collector accepted the batch but dropped some events. |

Delivery

Batches hold up to 50 events and stay under 60,000 bytes. They go out one at a time, in order. A retry resends the exact same body, so event ids stay the same and the collector counts each event once. Retries back off exponentially with jitter and follow Retry-After, capped at 30 s. Nothing is written to disk: events still queued when the process dies are lost.

Development

pnpm --filter @littlefriend/node test
pnpm --filter @littlefriend/node typecheck

Tests run against a stub collector on a real socket and check every body with the rules in @littlefriend/contract.