DOCS
v0.4

@cognivo/analytics

Opt-in, zero-dependency telemetry for Cognivo components. Captures 243 known cg-*, ai-*, and bias-* custom events, sanitizes the payload, and routes to one of four pluggable sinks. Does nothing until enableAnalytics() is called.

Installation

pnpm add @cognivo/analytics

Quick start

import { enableAnalytics, consoleSink } from '@cognivo/analytics';

const dispose = enableAnalytics({ sink: consoleSink });

// …later, when the app unmounts:
dispose();

Every Cognivo custom event bubbling to window is now logged. No listeners are attached and no data is captured until you call enableAnalytics.

API Reference

ExportDescription
enableAnalytics(config)Start capture. Returns a disposer that removes every listener.
disableAnalytics()Tear down capture. Idempotent.
extendEventRegistry(events)Add consumer-defined event names to the runtime registry.
getSessionId()Stable anonymous session id for the page lifetime.
sanitizeDetail(detail)Privacy filter — truncates long strings, caps nesting depth at 2, drops functions and symbols.
KNOWN_EVENTSReadonly registry of all 243 Cognivo event names.
createMatcher(patterns)Glob matcher for tag / event filtering ('bias-*', 'ai-thinking-*').
consoleSink, fetchSink, batchSink, localStorageSinkBuilt-in sinks. See Configuration below.

The AnalyticsEvent shape

interface AnalyticsEvent {
  timestamp: string;                 // ISO-8601 UTC
  component: string;                 // e.g. 'cg-button'
  event: string;                     // e.g. 'cg-button-click'
  detail: Record<string, unknown>;   // sanitized shallow payload
  page: string;                      // pathname, or full href if includeUrlQuery
  sessionId: string;                 // anonymous, stable per page load
}

Configuration

enableAnalytics({
  sink: consoleSink,
  trackComponents: ['cg-button', 'bias-*'],  // default: ['cg-*','ai-*','bias-*']
  trackEvents: ['cg-button-click'],          // default: all registered
  sampleRate: 0.25,                          // default: 1
  includeUrlQuery: false,                    // default: false
  anonymizeDetail: true,                     // default: true
});

Built-in sinks

consoleSink — dev logging

import { enableAnalytics, consoleSink } from '@cognivo/analytics';
enableAnalytics({ sink: consoleSink });

fetchSink — fire-and-forget POST

import { enableAnalytics, fetchSink } from '@cognivo/analytics';
enableAnalytics({ sink: fetchSink('/api/events') });

batchSink — buffered, flushes on pagehide

import { enableAnalytics, batchSink } from '@cognivo/analytics';
enableAnalytics({
  sink: batchSink('/api/events', { batchSize: 20, flushMs: 5000 }),
});
// Uses navigator.sendBeacon when the page unloads.

localStorageSink — persist locally

import { enableAnalytics, localStorageSink } from '@cognivo/analytics';
enableAnalytics({ sink: localStorageSink('cognivo:events') });
// Trimmed to the last 500 events.

Use cases

Funnel events from a pricing page

enableAnalytics({
  sink: fetchSink('/api/events'),
  trackComponents: ['cg-button', 'bias-anchoring', 'bias-scarcity'],
});

Custom sink — route to your own collector

import { enableAnalytics, type AnalyticsEvent } from '@cognivo/analytics';

function mySink(ev: AnalyticsEvent) {
  window.myCollector?.push({ t: ev.timestamp, e: ev.event, p: ev.page });
}

enableAnalytics({ sink: mySink });

Extend the event registry

import { enableAnalytics, extendEventRegistry, consoleSink } from '@cognivo/analytics';

extendEventRegistry(['cg-mycustom-ready', 'cg-mycustom-error']);
enableAnalytics({ sink: consoleSink });

Caveats

  • Opt-in, always. Importing the package has no side effects — capture only starts after enableAnalytics().
  • Privacy defaults. Only the pathname is captured (not query or hash). Strings over 40 chars are truncated. Nested objects are dropped beyond depth 2. Functions, symbols, and bigint values are dropped.
  • No PII inference. There is no fingerprinting, IP capture, or cookie — just a random session id per page load.
  • Sink errors are swallowed. A throwing sink is warned once to the console and never interrupts capture.
  • Single active capture. Calling enableAnalytics() twice replaces the previous configuration.