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

@edgeplus/sdk

v0.0.19

Published

EdgePlus browser SDK - Prescriptive web analytics

Readme

EdgePlus

Find what's broken on your site, and what to do about it.

Dashboard

Most analytics tools show you charts and let you figure things out. EdgePlus tells you which button broke for which users, and the first thing to check. Like a teammate dropping you a note, not a chart to interpret.


Quick start

npm install @edgeplus/sdk
import { init } from '@edgeplus/sdk';

init({ siteKey: 'ep_site_your_key' });

Get your siteKey at dashboard.edgeplus.pro. First data shows up within 5 minutes; first alert (if there's something to fix) within 24 hours.


What you'll get

When users get stuck, you'll see alerts like this:

Rage clicks detected on /checkout.

· Target: "Pay now" (button)
· 12 users clicked an average of 6.4 times in the last hour, max 9
· Likely cause: slow click handler, no visual feedback, or routing failed

What to check:
· DevTools Network tab — is the click response slow?
· Add a loading state to prevent re-clicks

Auto-detected: rage clicks, dead clicks (clicks on non-interactive elements), scroll dropoff, JavaScript errors, Google Web Vitals (LCP, CLS, INP), and page views (SPA-aware).


init() runs in the browser, so use a small client component:

// app/edgeplus-init.tsx
'use client';

import { useEffect } from 'react';
import { init } from '@edgeplus/sdk';

export function EdgePlusInit() {
  useEffect(() => {
    if (process.env.NODE_ENV !== 'production') return;
    init({ siteKey: process.env.NEXT_PUBLIC_EDGEPLUS_SITE_KEY! });
  }, []);
  return null;
}

Mount it once in your root layout:

// app/layout.tsx
import { EdgePlusInit } from './edgeplus-init';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        <EdgePlusInit />
        {children}
      </body>
    </html>
  );
}

The NODE_ENV guard keeps the SDK off during local dev and tests.

If you use Tailwind, CSS Modules, or styled-components, your CSS class names change every build:

<!-- Build 1 --> <button class="btn-primary-x9k2_a">Pay now</button>
<!-- Build 2 --> <button class="btn-primary-3m7f_b">Pay now</button>

EdgePlus might count these as two different buttons, splitting your data.

Fix it by giving important elements a stable id:

<button data-ep-id="checkout-pay" className="btn-primary-x9k2_a">
  Pay now
</button>

Where to use it: Checkout buttons, signup CTAs, main nav links, A/B test targets. You don't need it on every element — only the ones that matter for business decisions.

Bonus: Elements with data-ep-id are excluded from dead-click detection. If you mark a <div> as a clickable card, EdgePlus respects that.

Example: SaaS landing nav

const navItems = [
  { href: '/features', label: 'Features', epId: 'nav-features' },
  { href: '/pricing', label: 'Pricing', epId: 'nav-pricing' },
  { href: '/docs', label: 'Docs', epId: 'nav-docs' },
  { href: '/signup', label: 'Sign up', epId: 'nav-signup' },
];

<nav>
  {navItems.map(({ href, label, epId }) => (
    <Link key={href} href={href} data-ep-id={epId}>
      {label}
    </Link>
  ))}
</nav>

label may change with marketing copy or i18n, but epId stays the same. EdgePlus aggregates rage clicks correctly across builds and locales.

Auto-detection covers user behavior. For business milestones (signup, purchase, content read), call track():

import { track } from '@edgeplus/sdk';

track('signup_completed', { plan: 'pro', source: 'landing' });

Signup funnel

track('signup_started', { source: 'landing-hero' });
track('signup_email_submitted');
track('signup_email_verified');
track('signup_completed', { plan: 'free' });

EdgePlus measures conversion at each step and tells you where users drop off.

E-commerce checkout

track('cart_added', { product_id: 'sku-123', price: 29000 });
track('checkout_started', { total: 87000 });
track('checkout_completed', { order_id: 'ord-456' });

Search

track('search', { query: 'laptop', results: 42 });

Don't send PII — no names, emails, phone numbers. Stick to short strings, numbers, and booleans.

By default EdgePlus tracks anonymous visitors by a stable visitor_id stored in localStorage (13-month lifetime). When a user signs in, call identify() to link that visitor to your user id:

import { identify, reset, optOut, optIn, setConsent } from '@edgeplus/sdk';

// on login
identify('user_12345', { plan: 'pro' });

// on logout — keeps the visitor_id, drops the user binding
reset();

All events after identify() include user_id. Events before it (the anonymous session) stay anonymous but share the same visitor_id, so the dashboard can join them.

Opt-out / consent

// user disabled analytics in settings
optOut();

// user re-enabled
optIn();

// integrate with a consent banner
setConsent({ analytics: false });   // stops collection until toggled back on

optOut() clears all stored identifiers. setConsent({ analytics: false }) keeps identifiers but pauses event transmission.

Sessions

A session ends after 30 minutes of inactivity. Reopening the site within 30 minutes continues the same session.

Don't send PII in traits

Stick to short strings, numbers, booleans — plan, country, tier, etc. Not names, emails, phone numbers.

Will it slow down my site? No. The SDK is ~18 KB gzipped (ESM) / ~21 KB (CDN IIFE), loads with defer, and sends events in async batches. Zero impact on page rendering.

Does it work with my framework? Yes — React, Next.js, Vue, Svelte, Astro, plain HTML. Page navigation is auto-detected for SPAs.

Do I need a cookie consent banner? No cookies are used. EdgePlus stores a random visitor_id in localStorage for 13 months. GDPR / CCPA compliant out of the box.

Can I disable it in dev/test? Yes — wrap init() in a NODE_ENV check (see the Next.js example).

What if I don't add data-ep-id? Everything still works. EdgePlus auto-detects elements. data-ep-id is optional, for tracking key elements with extra precision.

Calling init() twice? Safe — it's idempotent. The second call is ignored.

TypeScript? Yes. Type definitions included.

What language is the dashboard in? Currently Korean. English support is on the roadmap. The SDK and the data it collects are language-agnostic.

Privacy

  • No cookies
  • No PII collected automatically (names, emails, phone numbers)
  • IP addresses are hashed with a daily-rotating salt and discarded immediately
  • <input> and <textarea> clicks are excluded automatically
  • visitor_id is a random UUID in localStorage with a 13-month lifetime — not shared across sites or services
  • session_id rotates after 30 minutes of inactivity
  • optOut() / setConsent({ analytics: false }) are available for user control

SDK options

// browser mode (default)
init({
  siteKey: 'ep_site_your_key',     // required in browser mode
  endpoint: 'https://...',          // optional, defaults to collect.edgeplus.pro
  batchSize: 10,                    // optional, events per batch
  batchIntervalMs: 5000,            // optional, batch interval
  debug: false,                     // optional, console logging
});

// server-proxy mode (0.0.15+) — siteKey not exposed in the browser
init({ endpoint: '/api/edgeplus.pro' });

In server-proxy mode, your server route forwards requests to https://collect.edgeplus.pro/api/collect with an X-Secret-Key header.

Server route — one-liner per framework (0.0.19+)

handleProxy accepts any Web-Request-based framework input and auto-unwraps it. nodeProxy covers Node http (Express / Pages Router / Angular SSR).

// Next.js App Router · Vercel Edge · Bun · Deno · CF Workers
export const POST = (req: Request) =>
  handleProxy(req, { secretKey: process.env.EDGEPLUS_SECRET_KEY! });

// SvelteKit · Remix · Astro — pass the event/args/context directly
export const POST = (event) =>
  handleProxy(event, { secretKey: process.env.EDGEPLUS_SECRET_KEY! });

// Hono — pass the Context directly
app.post('/api/edgeplus.pro', (c) =>
  handleProxy(c, { secretKey: c.env.EDGEPLUS_SECRET_KEY }));

// Nuxt 3 — pass the H3Event directly
export default defineEventHandler((event) =>
  handleProxy(event, { secretKey: process.env.EDGEPLUS_SECRET_KEY! }));

// Express · Next.js Pages Router · Angular Universal SSR — Node http
app.post('/api/edgeplus.pro',
  nodeProxy({ secretKey: process.env.EDGEPLUS_SECRET_KEY! }));

// Vite dev (SPA) — plugin auto-mounts /api/edgeplus.pro
import { edgeplusVitePlugin } from '@edgeplus/sdk/vite';
export default defineConfig({ plugins: [edgeplusVitePlugin()] });

The dashboard's install wizard generates ready-to-paste snippets for 11 frameworks (TS/JS + Python / Go / Java / C# / Rust manual fetch).


License: Proprietary. All rights reserved.