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

@cowliss/sdk

v0.21.0

Published

TypeScript client for the Cowliss ingestion API: identify, track and batch.

Readme

@cowliss/sdk

The TypeScript client for the Cowliss ingestion API. Send identify and track from your app, and Cowliss keeps the profiles, the events, the segments, and the journeys that follow from them.

Runs anywhere fetch does: Node 22 and up, Bun, Deno, edge runtimes and browsers. Zero dependencies, ESM only, fully typed.

Install

npm install @cowliss/sdk

Use it

import { Cow } from "@cowliss/sdk";

const cow = new Cow({
  apiKey: process.env.COW_API_KEY!,
  sourceId: process.env.COW_SOURCE_ID!,
  baseUrl: "https://api.cowliss.com",
});

await cow.identify({
  identifiers: { userId: "u_1", email: "[email protected]" },
  traits: { plan: "pro" },
});

await cow.track({
  identifiers: { userId: "u_1" },
  event: "checkout.completed",
  properties: { amount: 4900, currency: "usd" },
});

Send a message from your own code, with a template you pushed or a body written inline. Address an email address for mail nobody can turn off, such as a receipt, or a profile for mail its recipient can, such as a weekly report:

await cow.emails.send({
  to: "[email protected]",
  from: "Acme <[email protected]>",
  template: { key: "receipt", props: { orderId: "A-2291" } },
});

await cow.emails.send({
  to: { userId: "shop_1" },
  from: "Acme <[email protected]>",
  template: { key: "weekly-report", props: { visits: 120 } },
});

It resolves once the message has been handed over, and answers with the delivery id. A message to a profile goes to its email trait, skips anyone who turned that purpose off, and carries an unsubscribe link. Mail that follows a rule or a wait belongs in a journey.

Render a pushed template with your own props, exactly as a send to an address would, without sending it:

const { subject, html } = await cow.templates.preview({ key: "receipt", props: { name: "Ada" } });

Send someone to their hosted email preferences page from your own "Manage email preferences" button. The link works for an hour, so ask for it on the click:

const { url, expiresAt } = await cow.preferences.link({ to: { userId: "shop_1" } });

Every call carries identifiers, a map from kind to value. Cowliss generates the profile id and resolves the map to it, so a call that carries two identifiers is what links two profiles. There is no alias call.

identify returns the profile, track returns the stored event, and batch imports many of both in one request for backfills. Each call names the source it arrives through: set sourceId once on the client, or pass one per call when a single process writes into more than one app.

Retries are handled for you. A call that fails to reach Cowliss, or that Cowliss could not complete, backs off and tries twice more; a generated messageId travels with every track, so a retry after a lost response never writes the event twice. Each attempt has a deadline of its own (timeoutMs, ten seconds by default, thirty for a batch), so a stalled Cowliss cannot hold up your code. A call Cowliss rejected is not retried: it throws a CowError carrying the reason.

You do not have to wait for a call. await cow.track(...) throws on failure, as it always has; cow.track(...) on its own returns immediately and can never take your process down with an unhandled rejection. Pass onError to see the failures a call you did not await would otherwise swallow.

Docs

docs.cowliss.com

License

MIT