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

@reveny/node

v0.2.0

Published

Reveny SDK — AI cost tracking plus subscription, payment and usage reporting for real unit economics (MRR, churn, margin).

Readme

@reveny/node

Drop-in AI cost tracking with per-user, per-tenant, per-feature attribution. Pluggable into OpenAI, Anthropic, Vercel AI SDK, ElevenLabs, fal.ai, Replicate, or any HTTP-based LLM provider.

Reveny answers "which user / tenant / feature is bleeding my runway?" — not just "what did I spend?".

Install

pnpm add @reveny/node

The four-tier integration model

Pick the level that fits your stack. They compose — most apps use Tier 3 + Tier 4 together.

| Tier | What it covers | Fidelity | Setup cost | |---|---|---|---| | 1. autoTrack | Any provider with HTTP API. Patches global fetch. | Medium — captures cost, no feature / tenantId unless context is set | One line | | 2. AI SDK middleware | All ~20 providers Vercel AI SDK supports | High when AI SDK exposes the data | One middleware | | 3. Per-provider wrappers | OpenAI / Anthropic SDKs directly | Highest — cache tokens, streaming, full nuance | One Proxy wrap per client | | 4. trackCall + curated helpers | ElevenLabs, fal, Replicate, custom HTTP, anything else | High when extractor matches reality | Per call site | | last resort | reveny.track({...}) manual | Whatever you compute yourself | Free-form |

Tier 1 — autoTrack (safety net)

import { autoTrack } from '@reveny/node';

autoTrack({ apiKey: process.env.REVENY_KEY!, projectId: process.env.REVENY_PROJECT_ID! });

Done. Any fetch to api.openai.com, api.anthropic.com, generativelanguage.googleapis.com, api.mistral.ai, api.groq.com, api.together.xyz, api.perplexity.ai, api.deepseek.com, openrouter.ai, api.x.ai, api.cohere.ai, or your Azure OpenAI is captured automatically.

Pair with revenyContext.run({ userId, tenantId, feature }) blocks for attribution. Without context, events are tracked but unattributed — useful for total cost, not for unit economics.

Streaming responses (SSE) are skipped by default. Disable by env or upgrade to a wrapper.

Tier 2 — Vercel AI SDK middleware

import { wrapLanguageModel } from 'ai';
import { trackerMiddleware } from '@reveny/node/ai-sdk';

const model = wrapLanguageModel({
  model: openai('gpt-4o'),
  middleware: trackerMiddleware({
    apiKey: process.env.REVENY_KEY!,
    projectId: process.env.REVENY_PROJECT_ID!,
  }),
});

Covers OpenAI, Anthropic, Google, Mistral, Cohere, Bedrock, Azure, Groq, Together, Perplexity, DeepInfra, DeepSeek, xAI, and more — anything the AI SDK abstracts.

Tier 3 — Per-provider wrappers (highest fidelity)

import OpenAI from 'openai';
import Anthropic from '@anthropic-ai/sdk';
import { withTracker as wrapOpenAI } from '@reveny/node/openai';
import { withTracker as wrapAnthropic } from '@reveny/node/anthropic';

const config = {
  apiKey: process.env.REVENY_KEY!,
  projectId: process.env.REVENY_PROJECT_ID!,
};

export const openai = wrapOpenAI(new OpenAI(), config);
export const anthropic = wrapAnthropic(new Anthropic(), config);

Captures cache tokens, stream usage chunks, and provider-specific details. Use for your most-trafficked LLM calls.

Tier 4 — trackCall + curated helpers

For media APIs and providers without dedicated wrappers.

Curated (no extractor needed):

import { trackTts } from '@reveny/node/elevenlabs';
import { trackImageGen } from '@reveny/node/fal';
import { trackPrediction } from '@reveny/node/replicate';

const audio = await trackTts(reveny, () => elevenLabs.textToSpeech.convert(voiceId, { text }), {
  model: 'eleven_multilingual_v2',
  text,
});

const img = await trackImageGen(reveny, () => fal.subscribe('veed/fabric-1.0', { input }), {
  model: 'veed/fabric-1.0',
  width: 1024,
  height: 1024,
});

const pred = await trackPrediction(reveny, () => replicate.run('bytedance/latentsync:abc', { input }), {
  model: 'bytedance/latentsync',
  gpuType: 'a100',
});

Generic (you write the extractor):

const result = await reveny.trackCall(
  () => myCustomLlmCall(input),
  {
    provider: 'custom',
    model: 'my-model-v1',
    extractUsage: (res) => ({ inputTokens: res.tokens.in, outputTokens: res.tokens.out }),
  },
);

Attribution: userId, tenantId, feature

Wrap any block of work in revenyContext.run and every event tracked from inside inherits the context:

import { revenyContext } from '@reveny/node';

await revenyContext.run(
  { userId: req.user.id, tenantId: req.user.tenantId, feature: 'scene_suggestion' },
  () => anthropic.messages.create({ model: 'claude-haiku-4-5', messages: [...], metadata: { user_id: req.user.id } }),
);

Compatible with Promise.all — each scope is isolated. Or use reveny.withContext(...) if you have a Reveny instance handy.

identify — give your dashboard real names

Once at login (or whenever you know who the user is):

await reveny.identify(user.id, {
  email: user.email,
  tenantId: user.tenantId,
});

The dashboard shows emails and groups by tenant instead of opaque user IDs.

Unit economics — revenue, MRR & margin

Reveny is payment-gateway agnostic: you report revenue through the SDK, not through a Stripe/Paddle/RevenueCat integration. Call these from your webhook handlers or backend whenever state changes. All money is in micros (1 USD = 1_000_000 micros). Every method is async and never throws into your app.

// Subscription state — idempotent. Reveny computes New / Expansion / Contraction / Churn for you.
await reveny.subscriptions.set(user.id, {
  status: 'active',          // 'trialing' | 'active' | 'past_due' | 'canceled' | 'paused'
  mrrMicros: 1_900_000,      // $19.00, monthly-normalized (annual → divide by 12)
  currency: 'USD',
  planId: 'pro',
});

// Payments — for payment success/failure analytics.
await reveny.payments.record(user.id, {
  amountMicros: 1_900_000,
  status: 'succeeded',       // 'succeeded' | 'failed' | 'refunded'
  invoiceId: 'inv_123',
});

// Per-SKU costs that aren't from a tracked AI provider (needs a server-side cost rate).
await reveny.usage.record(user.id, { sku: 'whatsapp_message', units: 1 });

// Subscriber traits for the dashboard (name, acquisition source, cohort).
await reveny.subscribers.identify(user.id, {
  email: user.email,
  acquisitionSource: 'organic',
});

Wiring from a Stripe webhook, for example:

// customer.subscription.updated
await reveny.subscriptions.set(sub.metadata.appUserId, {
  status: mapStripeStatus(sub.status),
  mrrMicros: normalizeToMonthlyMicros(sub),
  currency: 'MXN',
  planId: sub.items.data[0]?.price.id,
  eventId: stripeEvent.id, // dedupe webhook redeliveries
});

Idempotency. Pass eventId with a stable id from your source system (e.g. the Stripe event id) so a redelivered webhook is deduplicated server-side and a payment is never counted twice. If you omit it, a random id is generated — which only dedupes the SDK's own network retries, not caller-level retries. For subscriptions.set, re-sending identical state is also a no-op. subscriberId is the same identifier as userId; inside a revenyContext.run({ userId }) scope you can pass it implicitly.

What ships in the box

  • @reveny/nodeReveny, autoTrack, revenyContext, trackCall, plus the unit-economics surface (reveny.subscriptions, reveny.payments, reveny.usage, reveny.subscribers)
  • @reveny/node/openai — OpenAI wrapper
  • @reveny/node/anthropic — Anthropic wrapper
  • @reveny/node/ai-sdk — Vercel AI SDK middleware
  • @reveny/node/elevenlabstrackTts, trackVoiceDesign
  • @reveny/node/faltrackImageGen
  • @reveny/node/replicatetrackPrediction

License

MIT