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

telesense

v0.2.0

Published

Lightweight, zero-dependency behavioural telemetry SDK. Drop one script tag — get clicks, scrolls, keypresses, heatmap data, and a clean JS API.

Readme

Telesense

Lightweight, zero-dependency behavioural telemetry SDK. Drop one script tag — get clicks, scrolls, keypresses, heatmap data, and a clean JS API.

CI npm version bundle size license TypeScript

Live demo →


Why Telesense?

| | Telesense | Heap | Mixpanel | Hotjar | |---|---|---|---|---| | Self-hosted | ✅ | ❌ | ❌ | ❌ | | Zero dependencies | ✅ | ❌ | ❌ | ❌ | | Bundle size | <20 kB | ~120 kB | ~80 kB | ~60 kB | | Custom transport | ✅ | ❌ | ❌ | ❌ | | TypeScript types | ✅ | partial | partial | ❌ | | Open source | ✅ | ❌ | ❌ | ❌ |


Quick start

Script tag (no build step)

<!-- 1. Optional config -->
<script>
  window.TELE_CONFIG = {
    endpoint: '/telemetry',   // your backend
    flushInterval: 5000       // ms between auto-flushes
  };
</script>

<!-- 2. Load SDK -->
<script src="https://cdn.jsdelivr.net/npm/telesense/dist/tele.umd.min.js"></script>

<!-- 3. tele is globally available -->
<script>
  tele.on('click', ev => console.log('click at', ev.x, ev.y));
</script>

npm / bundler

npm install telesense
import tele from 'telesense';

tele.config({ endpoint: '/telemetry' });

tele.on('*', ev => console.log(ev));
tele.track('signup_complete', { plan: 'pro' });

Captured events

| Type | Payload fields | |------|---------------| | click | button, x, y, element | | mousemove | x, y (throttled 100 ms) | | scroll | scrollX, scrollY, percentX, percentY | | keydown / keyup | key, code, target — sensitive fields auto-masked | | visibility | state (active | inactive) | | resize | width, height | | custom | anything you pass to tele.track() |

Sensitive inputs (type="password", name="card*", autocomplete="cc-*") are detected automatically — key is replaced with [masked].


API reference

tele.on(type, fn)this

Subscribe to an event type. Use '*' to receive all events.

tele.on('click', ev => { /* ev.x, ev.y, ev.element */ });
tele.on('*',     ev => sendToAnalytics(ev));

tele.off(type, fn)this

Remove a specific listener.

tele.track(type, data?)this

Record a custom event. Merged with sessionId, ts, page.

tele.track('video_play',  { videoId: 'abc123', position: 0 });
tele.track('form_submit', { formId: 'checkout', valid: true });

tele.flush()Promise<void>

Send the in-memory queue immediately (outside the normal interval).

tele.getQueue()TeleEvent[]

Snapshot of all queued events not yet flushed.

tele.reset()this

Clear the queue without sending (useful for logout / session change).

tele.config(patch)this

Update config at runtime — safe to call after the SDK loads.

// Set endpoint after user authenticates
tele.config({ endpoint: `/telemetry?userId=${user.id}` });

tele.start() / tele.stop()

Attach or detach all DOM listeners and the auto-flush timer.

tele.create(config?)TeleInstance

Create a completely isolated second instance — separate queue, session, and config.

const adminTele = tele.create({ sessionId: `admin-${uid}`, endpoint: '/admin-tele' });

tele.sessionId (read-only)

The session ID for this instance (auto-generated or from config.sessionId).

tele.version (read-only)

Current version string, e.g. "1.0.0".


Configuration

window.TELE_CONFIG = {
  // ── transports (pick one, or use onFlush for a custom transport)
  endpoint:        '',     // POST URL
  supabaseUrl:     '',     // Supabase project URL
  supabaseAnonKey: '',     // Supabase anon/public key

  // ── behaviour
  flushInterval:   5000,   // ms between auto-flushes
  maxQueue:        500,    // max in-memory events before forced flush
  maxStored:       20000,  // max events in localStorage fallback
  mouseThrottle:   100,    // ms between mousemove samples
  sessionId:       null,   // supply your own or one is generated

  // ── toggle individual event types
  capture: {
    click:      true,
    mousemove:  true,
    scroll:     true,
    keydown:    true,
    keyup:      true,
    visibility: true,
    resize:     true,
  },

  // ── custom transport (overrides endpoint + supabase)
  onFlush: null,   // (events: TeleEvent[]) => void

  // ── per-event hook (fires synchronously, before queue)
  onEvent: null,   // (event: TeleEvent) => void
};

Transport options

1. Your own backend

tele.config({ endpoint: 'https://your-api.com/telemetry' });

Expected shape: POST with Content-Type: application/json, body { sessionId, events[] }.

2. Supabase (serverless, no backend required)

-- Run once in Supabase SQL editor
create table telemetry_events (
  id         bigint generated always as identity primary key,
  session_id text    not null,
  event_json jsonb   not null,
  created_at timestamptz default now()
);
alter table telemetry_events enable row level security;
create policy "anon_insert" on telemetry_events for insert to anon with check (true);
create policy "anon_select" on telemetry_events for select to anon using (true);
tele.config({
  supabaseUrl:     'https://xxxx.supabase.co',
  supabaseAnonKey: 'your-anon-key',
});

3. Custom transport

tele.config({
  onFlush: async (events) => {
    await fetch('/my-endpoint', {
      method: 'POST',
      body: JSON.stringify(events),
    });
  },
});

4. localStorage (offline / zero config)

If no transport is configured, events are stored in localStorage under tele_events (up to maxStored entries). This is the default fallback when any transport fails.


Backend (Node.js / Express)

A minimal reference backend is included in demo/server.js. It exposes:

| Method | Path | Description | |--------|------|-------------| | POST | /telemetry | Receive a batch of events | | GET | /events | Retrieve all stored events | | GET | /events/stats | Event counts by type + session count | | DELETE | /events/reset | Clear the store |

node demo/server.js
# or with a custom port
PORT=4000 node demo/server.js

TypeScript

Full types ship with the package — no @types/ install needed.

import tele, { TeleEvent, TeleConfig, TeleInstance } from 'telesense';

tele.on('click', (ev: TeleEvent) => {
  console.log(ev.x, ev.y, ev.element?.tag);
});

const cfg: TeleConfig = {
  endpoint: '/telemetry',
  onEvent: (ev: TeleEvent) => updateUI(ev),
};
tele.config(cfg);

Local development

git clone https://github.com/EbParsa/telesense
cd telesense
npm install

# Build (produces dist/)
npm run build

# Build + watch
npm run build:watch

# Tests (23 tests, <1 s)
npm test

# Run the demo with a live backend
npm run demo
# → http://localhost:3000

Contributing

  1. Fork → branch → PR against main.
  2. Tests must pass: npm test.
  3. Keep the minified build under 20 kB.
  4. One feature / fix per PR — keep diffs focused.

License

MIT © EbParsa