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

@hellyeah/x-ray

v0.2.0

Published

Multi-platform analytics SDK for browser, React, Next.js, Remix, Vue, Svelte, Nuxt, Astro, and server environments

Readme

X-Ray Analytics

Overview

@hellyeah/x-ray allows you to track page views, custom events, and user identity across any JavaScript framework or server environment.

All page views are automatically tracked by default. Custom events and user identification are available via a simple API.

Quickstart

  1. Install the package

    npm install @hellyeah/x-ray
  2. Add analytics to your app

    Next.js

    import { Analytics } from "@hellyeah/x-ray/next";
    
    export default function RootLayout({ children }) {
      return (
        <html>
          <body>
            {children}
            <Analytics websiteId="your-website-id" />
          </body>
        </html>
      );
    }

    React

    import { Analytics } from "@hellyeah/x-ray/react";
    
    function App() {
      return (
        <>
          <Analytics websiteId="your-website-id" />
          {/* your app */}
        </>
      );
    }

    Vue / Nuxt / Svelte / Remix / Astro — see Framework Guides below.

    Vanilla JS

    import { inject } from "@hellyeah/x-ray";
    
    inject({ websiteId: "your-website-id" });

    Server (Node.js / Bun)

    import { XRay } from "@hellyeah/x-ray/server";
    
    const xray = new XRay("your-website-id");
    xray.track("signup", { distinctId: "user_42" });
    
    // Before process exit, flush remaining events
    await xray.shutdown();

    Serverless / Edge (Vercel, AWS Lambda, Cloudflare Workers)

    In short-lived environments, set flushAt: 1 and flushInterval: 0 so events are sent immediately rather than batched:

    import { XRay } from "@hellyeah/x-ray/server";
    
    const xray = new XRay("your-website-id", {
      flushAt: 1,
      flushInterval: 0,
    });
    
    xray.track("signup", { distinctId: "user_42" });

    Or use trackImmediate to bypass the queue entirely and await delivery:

    await xray.trackImmediate("signup", { distinctId: "user_42" });
  3. Deploy your app and see data flowing in.

Frameworks

| Framework | Import path | Usage | | --------- | ------------------------ | ----------------------------------------------------- | | Next.js | @hellyeah/x-ray/next | <Analytics /> component with Suspense boundary | | React | @hellyeah/x-ray/react | <Analytics /> component | | Remix | @hellyeah/x-ray/remix | <Analytics /> component | | Vue | @hellyeah/x-ray/vue | <Analytics /> component | | Svelte | @hellyeah/x-ray/svelte | injectAnalytics() helper | | Nuxt | @hellyeah/x-ray/nuxt | injectAnalytics() + <Analytics /> | | Astro | @hellyeah/x-ray/astro | Re-exports inject and track | | Server | @hellyeah/x-ray/server | XRay class with batching, retry, and immediate send | | Vanilla | @hellyeah/x-ray | inject(), track(), identify(), pageview() |

Custom Events

import { track } from "@hellyeah/x-ray";

track("signup", { plan: "pro" });

For the full list of predefined conversion events and ad platform mappings, see the Event Catalog.

User Identification

import { identify } from "@hellyeah/x-ray";

identify("user_42", { name: "Jane" });

For Enhanced Conversions (ad platform server-to-server attribution), pass email and/or phone to the browser identify():

identify("user_42", { email: "[email protected]", phone: "+15555551234" });

The remote tracker emits an identify item on the next batch; the server hashes the values with SHA-256 before storing them on the session.

Server-side identity is a different shape — see Server-side identity.

Cookie consent

The browser tracker writes a first-party hy_attr cookie (base64url JSON, 90-day TTL) that captures visitor ID and attribution click IDs across page loads. Control it via <Analytics> props:

<Analytics
  websiteId="your-website-id"
  cookies={false}              // default: true (cookie enabled)
  cookieDomain=".example.com"  // share across subdomains
/>

| Prop | Type | Default | Meaning | | -------------- | --------- | ---------- | ------------------------------------------------------------- | | cookies | boolean | true | Set false to disable the cookie (e.g. pre-consent). | | cookieDomain | string | host-only | Scope the cookie, e.g. .example.com to share subdomains. |

Consent helpers

Toggle the cookie at runtime after the user grants or revokes consent. All helpers are safe to call before the tracker script has loaded — mutators are buffered, readers return a safe sentinel.

import {
  enableCookies,
  disableCookies,
  cookiesEnabled,
  getCookies,
} from "@hellyeah/x-ray";

// On consent granted:
enableCookies();

// On consent revoked:
disableCookies();

// Synchronous check (false until the tracker loads):
if (cookiesEnabled()) {
  /* … */
}

// Read the current cookie payload (null until the tracker loads):
const attr = getCookies();

The helpers are re-exported from every framework entrypoint (@hellyeah/x-ray/{react,next,remix,vue,nuxt,svelte,astro}).

HyAttrCookie shape

getCookies() returns HyAttrCookie | null:

type HyAttrCookie = {
  vid?: string;       // visitor ID
  ts?: number;        // first-seen timestamp
  gclid?: string;     // Google Ads
  gbraid?: string;    // Google Ads (web→app)
  wbraid?: string;    // Google Ads (app→web)
  fbclid?: string;    // Meta
  msclkid?: string;   // Microsoft Ads
  ttclid?: string;    // TikTok
  li_fat_id?: string; // LinkedIn
  twclid?: string;    // X (Twitter)
  utm_source?: string;
  utm_medium?: string;
  utm_campaign?: string;
  utm_content?: string;
  utm_term?: string;
};

Server-side identity

The server SDK splits identity into two orthogonal pieces:

identify({ distinctId }) — process-local default. It sets the distinctId used when track() is called without one. It does not send any wire item and does not accept PII.

xray.identify({ distinctId: "user_42" });
xray.track("signup"); // uses user_42 from above

Concurrency warning. In shared-singleton server contexts (one XRay instance serving many requests), identify() races across requests — whoever called it last wins for every subsequent track(). Always pass distinctId per-call in multi-user handlers.

track(name, { distinctId, identity, visitorId }) — per-call PII and visitor context. identity carries Enhanced Conversions email / phone; visitorId forwards a cookie-derived UUID so events are attributable to the visitor's session.

await xray.trackImmediate("cv_purchase", {
  distinctId: "user_42",
  identity: { email: "[email protected]", phone: "+15555551234" },
  visitorId: req.cookies.hy_vid,
  revenue: 49.99,
  currency: "USD",
});

Notes:

  • identity requires an explicit distinctId on the same call. The identify() default is not applied when identity is present — this prevents PII from being attached to the wrong user in concurrent-server environments.
  • identity: {} or empty/whitespace-only fields are treated as absent (no identify item emitted).
  • visitor_id is session-scoped and COALESCE first-write-wins: once a value is recorded for a given distinctId, subsequent mixed visitorIds resolve to the original. This is backend design, not data loss.

Attribution

Constructor-level context is applied to every event from the instance. In addition to UTMs, the SDK carries the following click IDs:

| Key | Platform | | ----------- | ----------------------- | | gclid | Google Ads | | gbraid | Google Ads (web → app) | | wbraid | Google Ads (app → web) | | fbclid | Meta | | msclkid | Microsoft Ads | | ttclid | TikTok | | li_fat_id | LinkedIn | | twclid | X (Twitter) |

const xray = new XRay("your-website-id", {
  context: {
    utm_source: "google",
    gclid: req.query.gclid,
    gbraid: req.query.gbraid,
    wbraid: req.query.wbraid,
  },
});

Because context is constructor-scoped, per-request click IDs require a per-request XRay instance. A shared singleton will apply the same click IDs to every event.

Error handling

The SDK is fire-and-forget by convention. User-facing surfaces never reject on HTTP failure:

| Surface | Contract | | ------------------ | -------------------------------------------------------------------------------- | | track() | Returns void. Enqueues synchronously, never throws. | | trackImmediate() | Returns Promise<void>. Resolves even on HTTP failure (errors logged). | | flush() | Returns Promise<void>. Failed partitions are re-queued; does not reject. | | shutdown() | Returns Promise<void>. Races flush() against timeoutMs; does not reject. |

Broken analytics must not break your request path. To observe drops (4xx malformed payloads, exhausted 5xx/network retries, queue overflow, identity misuse), opt in with the onError hook:

import { XRay, type XRayError } from "@hellyeah/x-ray/server";

const xray = new XRay("your-website-id", {
  onError: (err: XRayError) => {
    switch (err.type) {
      case "http_4xx":
        // Malformed payload — items dropped, will not retry.
        Sentry.captureMessage("xray 4xx", { extra: err });
        break;
      case "http_5xx_exhausted":
        // Server errors after all retries — items re-queued.
        Sentry.captureMessage("xray 5xx exhausted", { extra: err });
        break;
      case "network_exhausted":
        // Network failure after all retries — items re-queued.
        Sentry.captureException(err.cause, { extra: err });
        break;
      case "queue_overflow":
        // maxQueueSize exceeded — oldest item dropped.
        Sentry.captureMessage("xray queue overflow", { extra: err });
        break;
      case "identity_missing_distinct_id":
        // track() with identity but no distinctId — item dropped.
        Sentry.captureMessage(`xray dropped ${err.event}`, { extra: err });
        break;
    }
  },
});

The hook fires in addition to structured log entries (via options.logger). Throws from the hook are caught and logged — they will not crash the SDK.

License

MIT