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

@yawlabs/lemonsqueezy-webhook-sink

v0.1.1

Published

Durable webhook receiver for LemonSqueezy — verifies signatures, deduplicates events, and persists them for replay and reconciliation

Readme

@yawlabs/lemonsqueezy-webhook-sink

Durable webhook receiver for LemonSqueezy. Verifies HMAC signatures, deduplicates repeat deliveries, and persists every event to SQLite so your downstream services can read from the sink on their own schedule and reconcile state even when their API calls succeed-but-the-response-is-lost.

Designed to sit in front of your business logic — not to replace it.

Why this exists

API writes against LemonSqueezy can succeed upstream and fail to deliver a response to you (timeouts, network partitions, crashes mid-ack). The canonical way to recover is to subscribe to webhooks and reconcile your local state against what LemonSqueezy actually observed. This service is the durable sink for that reconciliation loop.

If you're running an agent or unattended automation against @yawlabs/lemonsqueezy-mcp, pair it with this sink.

Quick start

npx @yawlabs/lemonsqueezy-webhook-sink

Required environment:

| Variable | Purpose | | --- | --- | | LEMONSQUEEZY_SIGNING_SECRET | The signing secret from your LemonSqueezy webhook config. Used to verify X-Signature. |

Optional:

| Variable | Default | Purpose | | --- | --- | --- | | PORT | 8787 | HTTP port to listen on. | | WEBHOOK_SINK_DB | ./events.db | Path to the SQLite file. | | WEBHOOK_SINK_ADMIN_TOKEN | (unset → admin disabled) | Bearer token required to hit /events, /events/:id/processed, /stats. When unset, those endpoints return 404. |

Endpoints

| Method | Path | Auth | Purpose | | --- | --- | --- | --- | | POST | /webhook | HMAC | Receive a LemonSqueezy webhook. Returns 200 for both new and duplicate events. | | GET | /healthz | none | Liveness check. 200 when the DB is reachable, 503 otherwise. | | GET | /events?since=<ts>&type=<name>&limit=<n> | WEBHOOK_SINK_ADMIN_TOKEN | Page events in order of received_at. Use since=<last-seen-ts> to checkpoint. | | POST | /events/:id/processed | WEBHOOK_SINK_ADMIN_TOKEN | Mark an event consumed. | | GET | /stats | WEBHOOK_SINK_ADMIN_TOKEN | Total events, unprocessed count, last-received timestamp. |

Response codes for POST /webhook

| Status | When | | --- | --- | | 200 | Valid signature and parseable payload (new or duplicate event). | | 400 | Signature valid, but body is not JSON or meta.event_name is missing. | | 401 | Missing or invalid X-Signature. | | 413 | Body exceeds 1 MB. |

When WEBHOOK_SINK_ADMIN_TOKEN is unset, the admin endpoints (/events, /events/:id/processed, /stats) respond 404 indistinguishably from any unknown route -- they do not reveal that an admin surface exists.

Using with @yawlabs/lemonsqueezy-mcp

The LemonSqueezy MCP server exposes ls_sink_events_list, ls_sink_event_mark_processed, and ls_sink_stats tools that bridge to this sink over HTTP. Point the MCP server at this sink by setting LEMONSQUEEZY_SINK_URL and LEMONSQUEEZY_SINK_ADMIN_TOKEN in its environment; agents can then query "what events have actually fired" through the same MCP surface they use for LS API calls.

Deduplication

Every event is stored under a stable event_key:

  1. If the sender supplies meta.custom_data.event_id (e.g. you set one on checkout), that wins.
  2. Otherwise, a SHA-256 of event_name + data.type + data.id + data.attributes.created_at. This is stable across LemonSqueezy's retries because the payload itself doesn't change between deliveries.

Duplicate deliveries return 200 { ok: true, duplicate: true } and are not inserted a second time.

Reconciliation pattern

# Your service pulls events and applies them.
last_seen = load_checkpoint()
while True:
    events = GET /events?since=${last_seen}&limit=100
    for e in events:
        apply(e.payload)
        POST /events/${e.id}/processed
        last_seen = e.received_at
    if len(events) < 100: sleep(5)

The sink doesn't push — consumers pull. This makes the sink stateless with respect to business logic and lets consumers retry/backfill freely.

Non-goals

  • Business logic. The sink persists and exposes; it does not decide.
  • Multi-tenant event routing. One sink per LemonSqueezy account.
  • HA replication. SQLite with WAL is fine for tens of events/sec. For higher volume or HA, point WEBHOOK_SINK_DB at an NFS mount or swap the store adapter (future work).

Development

npm install
npm run lint
npm test

License

MIT