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

@headlessly/node

v0.1.0

Published

Node.js SDK for headless.ly - server-side analytics, errors, and feature flags

Readme

@headlessly/node

The same unified SDK, server-side. Analytics, errors, and feature flags in one client.

import { createClient } from '@headlessly/node'

const headless = createClient({ apiKey: 'hl_xxx', environment: 'production' })

headless.track('order_placed', { total: 9900 }, 'user_fX9bL5nRd')
headless.captureException(err, 'user_fX9bL5nRd')
const enabled = await headless.isFeatureEnabled('new-billing', 'user_fX9bL5nRd')

The Problem

Your backend has the same fragmentation problem as your frontend:

// Three clients, three configs, three sets of credentials
import * as Segment from 'analytics-node'
import * as Sentry from '@sentry/node'
import { init as initLD } from '@launchdarkly/node-server-sdk'

const analytics = new Segment({ writeKey: 'seg_xxx' })
const sentry = Sentry.init({ dsn: 'https://[email protected]/123' })
const ld = initLD('ld_xxx')

// Three identity calls for the same user
analytics.identify({ userId: '123', traits: { plan: 'pro' } })
Sentry.setUser({ id: '123', email: '[email protected]' })
const flags = await ld.variation('new-billing', { key: '123' }, false)

Three SDKs. Three initialization flows. Three places where user identity can drift out of sync.

The Fix

import { createClient } from '@headlessly/node'

const headless = createClient({
  apiKey: 'hl_xxx',
  environment: 'production',
  release: '2.1.0',
})

One client. One API key. Server-side analytics, error capture, and feature flag evaluation share the same identity graph. When you capture an exception for a user, you already know their feature flags and recent events.

Install

npm install @headlessly/node

Analytics

// Track server-side events with user context
headless.track('api_called', { endpoint: '/users', method: 'GET' }, 'user_fX9bL5nRd')
headless.track('order_placed', { total: 9900, items: 3 }, 'user_fX9bL5nRd')

// Identify users with server-side traits
headless.identify('user_fX9bL5nRd', { email: '[email protected]', plan: 'pro' })

// Group users into organizations
headless.group('org_k7TmPvQx', { name: 'Acme', tier: 'enterprise' }, 'user_fX9bL5nRd')

Error Tracking

try {
  await processPayment(order)
} catch (err) {
  headless.captureException(err, 'user_fX9bL5nRd', {
    tags: { service: 'billing', severity: 'critical' },
    extra: { orderId: 'order_e5JhLzXc', amount: 9900 },
  })
}

headless.captureMessage('Queue depth exceeded 10,000', 'warning')

Stack traces are parsed automatically. Because identity is shared, every error includes the user's current feature flags and experiment variants without additional instrumentation.

Feature Flags

// Server-side evaluation with user context
const enabled = await headless.isFeatureEnabled('new-billing', 'user_fX9bL5nRd')

// Typed variants
const variant = await headless.getFeatureFlag('pricing-tier', 'user_fX9bL5nRd')
// → 'control' | 'variant_a' | 'variant_b'

Flags are cached for 5 minutes by default. Evaluations are automatically tracked as events -- correlate flag rollouts with error rates and conversion metrics without manual instrumentation.

Middleware

Automatically track HTTP requests and capture unhandled errors:

import express from 'express'

const app = express()
app.use(headless.middleware())

// Every request is tracked: method, path, status, duration
// Unhandled errors are captured with full request context
app.get('/api/users', async (req, res) => {
  const users = await getUsers()
  res.json(users)
})
import { Hono } from 'hono'

const app = new Hono()
app.use('*', headless.middleware())

The middleware adds request context to every event and error -- method, path, status code, response time -- without any manual instrumentation.

Context & Tags

// Global tags applied to every event and error
headless.setTag('region', 'us-east-1')
headless.setTags({ service: 'api', version: '2.1.0' })

Batching & Reliability

Events are batched automatically and flushed on an interval. Retry logic handles transient failures. No events lost during deploys.

// Manually flush when needed
await headless.flush()

Graceful Shutdown

process.on('SIGTERM', async () => {
  await headless.shutdown() // Flushes all pending events, stops timers
  process.exit(0)
})

Configuration

const headless = createClient({
  apiKey: 'hl_xxx', // Required -- API key
  endpoint: 'https://headless.ly/e', // Event endpoint (default)
  environment: 'production', // Environment name
  release: '2.1.0', // Release/version identifier
  serverName: 'api-1', // Server hostname
  batchSize: 20, // Events per batch (default: 20)
  flushInterval: 10000, // Auto-flush interval in ms (default: 10s)
  maxRetries: 3, // Max retry attempts (default: 3)
  timeout: 30000, // Request timeout in ms (default: 30s)
  debug: false, // Enable debug logging
  tags: { service: 'api' }, // Default tags for all events
  onError: (err) => log(err), // Error callback
})

API Reference

Analytics

| Method | Description | | ---------------------------------------- | ----------------------------- | | track(event, properties?, distinctId?) | Track a server-side event | | identify(userId, traits?) | Identify a user | | group(groupId, traits?, distinctId?) | Associate a user with a group |

Errors

| Method | Description | | ------------------------------------------------ | ------------------------------------------------------- | | captureException(error, distinctId?, context?) | Capture an error with stack trace, returns event ID | | captureMessage(message, level?, distinctId?) | Capture a message at a severity level, returns event ID |

Feature Flags

| Method | Description | | ----------------------------------- | -------------------------------------- | | getFeatureFlag(key, distinctId) | Get a flag value with 5-minute caching | | isFeatureEnabled(key, distinctId) | Check if a flag is enabled |

Context

| Method | Description | | -------------------- | ------------------------ | | setTag(key, value) | Set a global tag | | setTags(tags) | Set multiple global tags |

Middleware

| Method | Description | | -------------- | ------------------------------------------------------------------------ | | middleware() | Express/Hono middleware for automatic request tracking and error capture |

Lifecycle

| Method | Description | | ------------ | ----------------------------------------------- | | flush() | Flush pending events immediately | | shutdown() | Stop the flush timer and flush remaining events |

License

MIT