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

stillalive

v3.0.0

Published

Dead man's switch email alert server

Readme

still alive

Confirm a process is still alive, and if it isn't, send an email about it.

This works with any SMTP provider, Mandrill, Resend, or SendGrid.

Installation

stillalive can be used two ways (see Usage): embedded in your own Node app as a library, or run directly as a CLI. Install accordingly:

| How you'll use it | Install | | --- | --- | | As a library (import it in your code) | npm install stillalive | | As a CLI (run the stillalive command) | npm install -g stillalive |

Resend and SendGrid additionally need their official SDK, which ships as an optional peer dependency so it only gets installed if you actually use it (SMTP and Mandrill need nothing extra):

| Provider | Extra install | | --- | --- | | SMTP / Mandrill | none | | Resend | npm install resend | | SendGrid | npm install @sendgrid/mail |

The SDK is loaded via a dynamic import() only when its service is selected, so the package works fine with neither installed. If you configure resend or sendgrid without installing its SDK, stillalive fails fast at startup with a clear error telling you the exact npm install command to run.

Usage

Both strategies take the same two pieces of configuration: a key (a shared secret callers must present) and a provider config object (see Configuring an email provider).

As a library

import stillalive from 'stillalive' returns an async factory: await stillalive(key, provider, port). It starts an Express server and returns the app, so you can add your own routes:

import stillalive from 'stillalive';
import config from './config.emailProvider.json' with { type: 'json' };

const port = process.env.PORT || 8080;

const app = await stillalive(config.key, config.provider, port);

app.get('/health', (_req, res) => {
  res.status(200).send('OK');
});

port is optional and defaults to process.env.PORT, then 3000.

As a CLI

Point the stillalive command at a JSON config file:

stillalive ./path/to/config.json [port]

port is optional and defaults to process.env.PORT, then 3000. The config file holds the key and provider config (the provider object goes under provider):

{
  "key": "my-secret-key",
  "provider": {
    "service": "smtp-mail.outlook.com",
    "auth": {
      "user": "[email protected]",
      "pass": "insert_password_here"
    }
  }
}

Configuring an email provider

The provider config object selects the email service via its service field. The examples below show it under the provider key of a CLI config file; when used as a library, pass the inner object as the second argument to stillalive(key, provider, port). Ready-to-copy config files for each provider live in the examples/ folder.

If using SMTP, name your SMTP host as the service (see examples/config.smtp.json):

{
  "key": "my-secret-key",
  "provider": {
    "service": "smtp-mail.outlook.com",
    "auth": {
      "user": "[email protected]",
      "pass": "insert_password_here"
    }
  }
}

If using Mandrill (see examples/config.mandrill.json):

{
  "key": "my-secret-key",
  "provider": {
    "service": "mandrill",
    "apiKey": "md-EgWVMWEjZF2KdSlocGs2Aw"
  }
}

If using Resend (requires npm install resend; see examples/config.resend.json):

{
  "key": "my-secret-key",
  "provider": {
    "service": "resend",
    "apiKey": "re_xxxxxxxxxxxx"
  }
}

If using SendGrid (requires npm install @sendgrid/mail; see examples/config.sendgrid.json):

{
  "key": "my-secret-key",
  "provider": {
    "service": "sendgrid",
    "apiKey": "SG.xxxxxxxxxxxx"
  }
}

Whatever provider you configure, requests use the same canonical email object (see email object below) -- stillalive maps it to each provider's native format for you.

usage

send a put to host/still/alive/:id where id is your app specific timeout's name

The body of your request should be json as follows. The email object uses a single canonical shape that works the same no matter which provider you've configured -- stillalive maps it internally to SMTP, Mandrill, Resend or SendGrid:

{
  "key": "server key (set in your config file)",
  "email": {
    "from": "[email protected]",
    "to": "[email protected]",
    "subject": "subject line",
    "text": "text body of email"
  },
  "interval": {
    "minutes": 5
  }
}

email object

Every address field (from, to, cc, bcc, replyTo) accepts any of:

Provide text, html, or both. A fuller example:

{
  "from": { "email": "[email protected]", "name": "You" },
  "to": [
    "[email protected]",
    { "email": "[email protected]", "name": "Second Person" }
  ],
  "cc": "[email protected]",
  "bcc": "[email protected]",
  "replyTo": "[email protected]",
  "subject": "subject line",
  "text": "text body of email",
  "html": "<p>html body of email</p>"
}

The interval field accepts a number of milliseconds, or an object with any of weeks, days, hours, minutes, seconds, and milliseconds (which are summed). For example, { "minutes": 5 } or { "hours": 1, "minutes": 30 }.

validation and error responses

Arming a timer (PUT /still/alive/:id) validates the request and responds with 400 and a JSON body when something is wrong:

| Condition | Response | | --- | --- | | Missing or incorrect key | { "error": "bad request" } | | Invalid email payload | { "error": "invalid email", "details": [ ... ] } | | Missing or invalid interval | { "error": "invalid interval" } |

The details array lists every problem found in the email object, for example:

{
  "error": "invalid email",
  "details": [
    "`from` must be a valid email address",
    "`to` must include at least one recipient",
    "`subject` must be a non-empty string",
    "either `text` or `html` body is required"
  ]
}

A valid payload needs a syntactically valid from, at least one valid to, a non-empty subject, and a text or html body. Provider config is validated when the server starts: a missing/invalid key, an unknown provider shape, a missing API apiKey, or missing SMTP auth.user/auth.pass throws a clear TypeError at startup.

listing active timers

Send a POST to host/active with the server key to list every currently-armed timer. The key is checked the same way as the other routes -- an incorrect key responds with 400 { "error": "bad request" }.

{ "key": "server key (set in your config file)" }

The response lists each active timer by id, when it will fire (expiresAt, ISO 8601), and how long until it does (msRemaining):

{
  "active": [
    { "id": "nightly-export", "expiresAt": "2026-06-23T20:29:14.509Z", "msRemaining": 599984 }
  ]
}

Migrating to v3

v3 removes the legacy aliases that earlier versions silently accepted, in favor of a single canonical shape. Update any of the following:

| Removed (pre-v3) | Use instead | | --- | --- | | from_email / from_name on the email object | from (a string or { email, name }) | | reply_to (snake_case) | replyTo | | address key on an address object | email | | Mandrill-style to entries with a type field | a string or { email, name } | | accessKeyId in provider config | apiKey | | smtp / api keys in a CLI config file | provider |

In addition, requests and config are now validated up front (see validation and error responses). Payloads or configs that previously "worked" but were incomplete will now be rejected with an explicit error instead of failing silently.