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

@vskstudio/takt-core

v0.5.0

Published

Tiny privacy-friendly analytics SDK for Takt

Readme

@vskstudio/takt-core

Tiny, privacy-friendly analytics SDK for Takt.

npm version snippet size zero deps license


  • Zero dependencies, tree-shakeable ESM module.
  • ≤ 1 kB gzip drop-in snippet — no build step required.
  • Privacy first: honours opt-out and Do Not Track, strips query strings, and excludes localhost / private IPs by default.
  • Hexagonal core: a pure domain wrapped in ports & adapters — easy to test, easy to extend.

Snippet (no build step)

<script defer src="https://cdn.jsdelivr.net/npm/@vskstudio/takt-core/dist/takt.js" data-domain="example.com"></script>

Pin a version in production, e.g. @vskstudio/[email protected]. jsDelivr and unpkg both serve the snippet straight from npm — no extra hosting required.

Then, anywhere on the page:

window.takt('Signup', { props: { plan: 'pro' } })

Calls made before the script finishes loading are queued and replayed — install a tiny stub first if you need that:

<script>
  window.takt = window.takt || function () { (window.takt.q = window.takt.q || []).push(arguments) }
</script>

data-* options

| Attribute | Effect | Default | | --- | --- | --- | | data-domain | Site identifier sent with every event | location.hostname | | data-script-origin | First-party origin to derive the endpoint from ({origin}/api/event) — your Takt domain or a custom domain to dodge ad-blockers | none | | data-endpoint | Ingestion endpoint (wins over data-script-origin) | /api/event | | data-exclude-localhost="false" | Track localhost / private IPs | excluded | | data-enabled="false" | Kill-switch — the tracker does nothing | enabled | | data-respect-dnt="false" | Opt out of the Do Not Track short-circuit | respected | | data-sample-rate="0.5" | Send only this fraction (0–1) of events | 1 (all) | | data-track-query | Keep the full query string and hash (default strips both) | stripped | | data-query-params="utm_source,utm_medium" | Keep only these query params | none |

The base snippet stays under 1 kB gzip: pageviews, SPA navigation, window.takt(), and the privacy guards — nothing more. It respects Do Not Track and strips the query string and hash from URLs by default; the attributes above tune both. For a custom scrubber function, use the npm build (scrubUrl below).

Auto extensions — takt.auto.js

Need outbound clicks, file downloads, HTML-declared events, or 404 detection without writing code? Swap takt.js for the opt-in takt.auto.js bundle and list what you want in data-auto:

<script defer
  src="https://cdn.jsdelivr.net/npm/@vskstudio/takt-core/dist/takt.auto.js"
  data-domain="example.com"
  data-auto="outbound,downloads,tagged,404"></script>

Without data-auto, takt.auto.js behaves exactly like takt.js. Each extension is opt-in.

| data-auto value | Event sent | Property | | --- | --- | --- | | outbound | Outbound Link: Click | url | | downloads | File Download | url | | 404 | 404 | path | | tagged | custom (data-takt-event) | from data-takt-prop-* |

  • downloads default extensions: pdf, xlsx, docx, pptx, csv, zip, gz, rar, 7z, dmg, exe, apk, mp3, mp4, wav, mov, avi, mkv, txt — override with data-downloads-ext="pdf,csv,epub".
  • tagged: add data-takt-event="Cta" to any clickable element; data-takt-prop-<key> attributes become props (empty keys/values are ignored). The reserved name pageview is refused. Identical to init({ tagged: true }) on the SDK.
  • 404: detected at load via the Navigation Timing API, or by adding data-takt-404 to <body> / a <meta name="takt:404"> tag on server-rendered error pages.

npm

pnpm add @vskstudio/takt-core

Quick start — default instance

import { init, track, pageview } from '@vskstudio/takt-core'

init({ domain: 'example.com', outbound: true, files: true, notFound: true })

track('Signup', {
  props: { plan: 'pro' },
  revenue: { amount: '29.00', currency: 'EUR' },
})

init() creates a single shared instance, fires an automatic pageview, and wires SPA navigation. track, pageview, optOut, and optIn delegate to it.

Autocapture toggles — outbound, files, notFound, and tagged — opt into the same extensions as the snippet's data-auto: outbound-link clicks, file downloads, 404 detection, and data-takt-event custom events. tagged: true tracks clicks on elements carrying data-takt-event (with data-takt-prop-* becoming props), matching data-auto=tagged.

Instance API — createTakt

For full control (multiple instances, no globals, explicit teardown), construct an instance directly:

import { createTakt } from '@vskstudio/takt-core'

const takt = createTakt({ domain: 'example.com', endpoint: '/api/event' })

takt.pageview()
takt.track('Signup', { props: { plan: 'pro' } })

// Each enableX returns a disposer for teardown.
const stopSpa = takt.enableSpa()
const stopOutbound = takt.enableOutbound()
const stopFiles = takt.enableFiles(['pdf', 'zip', 'csv'])
const stop404 = takt.enable404() // detects a 404 page once and reports it
const stopTagged = takt.enableTagged() // custom events from data-takt-event

// later…
stopSpa()
stopOutbound()
stopFiles()
stop404()
stopTagged()

createTakt() is a pure factory (no side effects until you call a method), so it tree-shakes cleanly.

Configuration

init() and createTakt() accept the same options:

| Option | Type | Default | Effect | | --- | --- | --- | --- | | domain | string | location.hostname | Site identifier sent with every event | | scriptOrigin | string | none | First-party origin to derive the endpoint from ({origin}/api/event) — your Takt domain or a custom domain to dodge ad-blockers | | endpoint | string | /api/event | Ingestion endpoint (wins over scriptOrigin) | | enabled | boolean | true | Master switch — when false, nothing is sent | | debug | boolean | false | Log each payload to the console before sending | | sampleRate | number | 1 | Keep this fraction of events (e.g. 0.25 ≈ 25%) | | respectDnt | boolean | true | Suppress events when Do Not Track is on | | excludeLocalhost | boolean | true | Suppress events on localhost / private IPs | | trackQuery | boolean | false | Keep the full query string and hash on URLs | | queryParams | string[] | — | Allowlist: keep only these query params, drop the rest | | scrubUrl | (url: string) => string | — | Custom scrubber; overrides trackQuery / queryParams |

Privacy

By default the query string and hash are stripped from every URL (page, referrer, and autocaptured link destinations) before sending — secrets in ?token=… or #access_token=… never leave the browser. Opt back in with trackQuery: true, narrow it with a queryParams allowlist, or take full control with scrubUrl. Props and revenue are sanitized too: props are coerced to strings, capped (30 keys, 64-char keys, 1024-char values), and revenue is dropped unless the amount and 3-letter currency are well-formed.

import { optOut, optIn } from '@vskstudio/takt-core'

optOut() // sets localStorage `takt_ignore` = '1'; no events are sent
optIn()  // resumes tracking

Events are suppressed, in order, when: the visitor has opted out, or Do Not Track is enabled (respectDnt), or the host is localhost / a private IP (excludeLocalhost), or the event is dropped by sampleRate.

Widgets & public stats

Besides tracking, the package ships framework-agnostic helpers for Takt's server-rendered widgets and its public stats API. These are tree-shakeable and re-exported by the framework wrappers (@vskstudio/takt-react, -vue, etc.).

import { badgeUrl, embedUrl, createStats } from '@vskstudio/takt-core'

// URL builders for the server-rendered badge SVG and embed iframe.
badgeUrl('example.com', { variant: 'd', glyph: 'off', lang: 'en' })
// → /public/example.com/badge.svg?variant=d&glyph=off&lang=en
embedUrl('example.com', { theme: 'dark' })
// → /embed/example.com?theme=dark

// Anonymous client for the public stats API. Pass `host` for a remote Takt.
const stats = createStats({ host: 'https://takt.example.com', domain: 'example.com' })
await stats.summary(undefined, { period: '30d', compare: 'previous' })
await stats.timeseries()
await stats.realtime()
await stats.breakdown('page')

host defaults to '' (same-origin), matching the SDK's relative endpoint. When set, it must be an absolute http(s):// origin — anything else (javascript:, data:, protocol-relative //…) is rejected, so a host value can never smuggle a non-http scheme into a widget src or a fetch. The value is reduced to its origin: any path, query, or fragment is dropped (https://takt.example.com/x?a=1https://takt.example.com). Errors surface as PublicApiError (carrying the HTTP status).

Wire payload contract

Every event is posted to the endpoint as a compact JSON object. The keys are frozen — the Takt backend ingestion depends on them:

| Key | Meaning | | --- | --- | | n | event name (pageview for pageviews) | | d | domain | | u | URL (query + hash stripped by default) | | r | referrer (query + hash stripped by default) | | w | viewport width | | p | props (object, omitted if empty) | | $ | revenue { a: amount, c: currency } (currency uppercased) |

Architecture

@vskstudio/takt-core follows a hexagonal (ports & adapters) layout:

domain/          Pure business core, zero I/O. Value objects (EventName, Props,
                 Revenue, AnalyticsEvent), payload mapping, and the URL scrubber.
application/      Use cases: the Analytics service, the TrackingPolicy (consent +
                 sampling), and autocapture trackers — depending only on small
                 single-method port interfaces.
infrastructure/   Driven adapters: a resilient fetch/beacon transport, localStorage
                 consent, and browser providers (DNT, environment, history, clicks).
composition/      createTakt() factory, the ESM entry, and the snippet adapter.

The domain never reaches outward; adapters are injected at the composition root (createTakt). This keeps the core testable with fakes and lets you swap transports or storage without touching business logic.

License

MIT