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

@straitlink/node

v0.8.0

Published

Server SDK for Node: create and manage short links and send conversion events from your backend. Zero dependencies.

Readme

@straitlink/node

Server SDK for Strait: create and manage links and send conversion events from your backend. Zero dependencies; uses your tenant API key.

Quick start (60 seconds)

1. Install.

npm install @straitlink/node

2. Make a link from your server. Save this as quickstart.mjs. The secret key (st_live_…) is in Dashboard → Settings → Secret keys; keep it on servers only.

import { StraitClient } from '@straitlink/node';

const strait = new StraitClient({ apiKey: process.env.STRAIT_API_KEY, baseUrl: 'https://strait.link' });
const link = await strait.createLink({ slug: 'hello', longUrl: 'https://shop.example.com/product/42' });
console.log(link.shortUrl);
STRAIT_API_KEY=st_live_… node quickstart.mjs

You should see: https://<your-handle>.strait.link/hello, and the same link in Dashboard → Links. Run it twice and the second run throws a StraitError with status 409, because the path is taken.

Platform features

| Feature | Status | Notes | |---|---|---| | Create, list, update and delete links | ◐ Beta | StraitClient, with your tenant API key. | | Conversion events from your backend (sendEvent) | ◐ Beta | Pass the clickId from the app to tie an event to its tap. | | iPhone install matching | ◐ Beta | Happens in your app (the mobile SDKs), not on the server. Your choice in Dashboard → Settings → iPhone installs: device matching, paste handoff, both or neither, read from Strait at runtime. Device matching is off by default. iPhone matches are labelled Estimated until measured. | | Published on npm | ● Live | npm install @straitlink/node |

● Live · ◐ Beta · ○ Planned · – Not yet. The same words as the platform status page.

Install

Don't install other names that look similar (strait on npm is someone else's package).

npm install @straitlink/node

Use

import { StraitClient } from '@straitlink/node';

const strait = new StraitClient({
  apiKey: process.env.STRAIT_API_KEY!,   // st_live_… from the dashboard
  baseUrl: 'https://strait.link',        // must be https (http only for localhost)
});

// Create a link
const link = await strait.createLink({
  slug: 'launch',
  longUrl: 'https://yourapp.com/launch',
  tags: ['campaign'],
});

// List / update / pause
await strait.listLinks();                                   // every active link
await strait.listLinks({ limit: 100, status: 'paused' });   // one page: { links, nextCursor }
await strait.updateLink('launch', { longUrl: 'https://yourapp.com/launch-v2' });
await strait.deactivateLink('launch');                      // paused, not deleted

// Send a conversion / revenue event (ties revenue to the funnel)
// Authenticated with your secret key — no publishable key needed server-side.
await strait.sendEvent({ event: 'purchase', value: 49.99, currency: 'USD' });
// Optional: the tap it came from (B15), e.g. forwarded by your app, so the
// dashboard places the revenue on that tap's channel / A/B variant.
await strait.sendEvent({ event: 'purchase', value: 49.99, currency: 'USD', clickId });

Failures throw a StraitError with .status and the parsed .body (the raw text when the reply is not JSON, such as a proxy's HTML 502 page).

API

Every secret-key operation of the API has a method named after its OpenAPI operationId (openapi.json, pinned in this repo; test/openapi-coverage.test.ts checks each one's HTTP method and path, and a daily job compares the pin with the live file).

| method | endpoint | |--------|----------| | createLink(input) | POST /v1/links | | batchCreateLinks(links) | POST /v1/links/batch (up to 500) | | getLink(slug) | GET /v1/links/:slug | | listLinks() / listLinks({ limit, cursor, status }) | GET /v1/links | | updateLink(slug, fields) | PATCH /v1/links/:slug | | deactivateLink(slug) (deleteLink still works) | DELETE /v1/links/:slug (pauses it) | | getLinkQr(slug, { format, size, … }) | GET /v1/links/:slug/qr → { contentType, body } | | getAnalytics({ from, to, link, group }) | GET /v1/analytics | | getUsage() | GET /v1/usage | | getReferrals({ from, to }) | GET /v1/referrals | | simulateTap({ url, ua, country }) | GET /v1/simulate | | exportData({ part, from, to }) | GET /v1/export → { contentType, body } | | createExportToken({ part, from, to }) | POST /v1/export/token | | testWebhook(id) | POST /v1/webhooks/:id/test | | listWebhookDeliveries(id, { limit, cursor }) | GET /v1/webhooks/:id/deliveries | | redeliverWebhookDelivery(id) | POST /v1/webhook-deliveries/:id/redeliver | | listDomains() / addDomain(hostname) / removeDomain(hostname) | /v1/domains | | recordEvent(input) (sendEvent returns nothing) | POST /v1/event | | verifyWebhook(rawBody, headers, secret, { toleranceSeconds }) | checks a webhook delivery (no request) |

Verify a webhook

Pass the raw body (before JSON parsing), the headers and your webhook's signing secret. It checks the X-Strait-Signature HMAC in constant time and refuses deliveries more than 5 minutes old.

import express from 'express';
import { verifyWebhook } from '@straitlink/node';

const app = express();
app.post('/webhooks/strait', express.raw({ type: 'application/json' }), (req, res) => {
  if (!verifyWebhook(req.body, req.headers, process.env.STRAIT_WEBHOOK_SECRET!)) return res.status(401).end();
  const delivery = JSON.parse(req.body.toString('utf8')); // { id, event, createdAt, data }
  res.status(204).end();
});

Size and speed

Measured 25 Oct 2026 on 0.7.0.

| What | Size or time | How it was measured | |---|---|---| | npm package | 9.5 KB download, 30 KB unpacked, no dependencies | npm pack --dry-run | | Start-up | 5.9 ms to load the module (most of it is node:crypto, for webhook signatures) + 0.05 ms for new StraitClient(...) | fresh Node 26 process on an M-series Mac, median of 25 |

Example app

examples/node creates a link, sends a purchase event and deletes the link, built against this repo's source (CI builds it on every PR).

  1. npm ci here, then cd examples/node && npm ci.
  2. cp .env.example .env and set your secret key (st_live_…, a test workspace).
  3. npm start

Docs and support