alphana-sdk
v2.0.7
Published
Client-side tracking SDK — navigation, time-on-page, and heatmap data collection.
Maintainers
Readme
alphana-sdk
Client-side analytics SDK for the Alphana platform. Collects navigation, time-on-page, heatmap interactions, console errors, and feature-flag evaluations, then batches and sends them to your Alphana backend.
NPM: [alphana-sdk](https://www.npmjs.com/package/alphana-sdk) · Version: 2.0.2 · License: MIT
Features
- SPA navigation — Monitors
history.pushState,replaceState, andpopstatefor automatic page views and U-turn detection (quick bounces). - Time on page — Cumulative dwell time per path, updated on tab hide and navigation.
- Heatmaps — Sampled mouse moves, clicks, scrolls, and rage-click bursts; coordinates stored as page percentages for resolution-independent replay.
- Element goals & revenue — Optional dashboard-configured click tracking by HTML element
id(WordPress / no-code friendly). - Cookie consent — Optional dashboard widget; tracking waits for visitor acceptance when enabled.
- Session identity — Per-tab session ID plus a persistent
visitorIdinlocalStorage(after consent when required). - Batched ingest — Events queue locally and flush on batch size, interval, or page unload (
sendBeacon). - Heartbeats — Keep-alive pings every 30s; deactivate signal when the tab is hidden.
- Error logging — Patches
console.info/warn/error,window.onerror, and unhandled rejections (optional). - Feature flags — Evaluate flags server-side with
identify()targeting (requiressecretKey). - React bindings — Optional subpath
alphana-sdk/reactwith provider and hooks (React 17+). - Tree-shakeable —
sideEffects: false; React is an optional peer dependency. - SSR-safe —
init()is a no-op whenwindowis undefined.
Installation
npm install alphana-sdk
# or
pnpm add alphana-sdk
# or
bun add alphana-sdkFor React integration, install React 17+ in your app (peer dependency):
npm install react react-domCredentials
Create an app in the Alphana dashboard and copy:
| Value | Config key | Usage |
| ---------- | ----------- | -------------------------------- |
| App ID | appId | Sent in every request body |
| Secret key | secretKey | Authorization: Bearer … header |
Default API endpoint (cloud):
https://api.alphana.ir/api/eventsQuick start
React / Vite
// main.tsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { UserTrackerProvider } from "alphana-sdk/react";
import App from "./App";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<UserTrackerProvider
config={{
appId: "YOUR_APP_ID",
secretKey: "YOUR_SECRET_KEY",
}}
>
<App />
</UserTrackerProvider>
</StrictMode>,
);// Any child component
import { useTracker, useFeatureFlagEnabled } from "alphana-sdk/react";
export function MyComponent() {
const tracker = useTracker();
const showBeta = useFeatureFlagEnabled("new-checkout");
function handleSignup() {
tracker?.identify({ email: "[email protected]", plan: "pro" });
}
return showBeta ? <BetaCheckout /> : <Checkout />;
}Next.js App Router
The App Router does not trigger history.pushState the same way as classic SPAs. Wrap the app in the provider, then track pathname changes manually.
// app/providers.tsx
"use client";
import { UserTrackerProvider } from "alphana-sdk/react";
import type { ReactNode } from "react";
export function Providers({ children }: { children: ReactNode }) {
return (
<UserTrackerProvider
config={{
appId: process.env.NEXT_PUBLIC_TRACKER_APP_ID!,
secretKey: process.env.NEXT_PUBLIC_TRACKER_SECRET!,
}}
>
{children}
</UserTrackerProvider>
);
}// app/navigation-tracker.tsx
"use client";
import { usePathname } from "next/navigation";
import { usePageView } from "alphana-sdk/react";
export function NavigationTracker() {
usePageView(usePathname());
return null;
}// app/layout.tsx
import { Providers } from "./providers";
import { NavigationTracker } from "./navigation-tracker";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<Providers>
<NavigationTracker />
{children}
</Providers>
</body>
</html>
);
}// app/navigation-tracker.tsx
"use client";
import { usePathname } from "next/navigation";
import { usePageView } from "alphana-sdk/react";
export function NavigationTracker() {
usePageView(usePathname());
return null;
}# .env.local
NEXT_PUBLIC_TRACKER_APP_ID=your_app_id
NEXT_PUBLIC_TRACKER_SECRET=your_secret_keyVanilla TypeScript / JavaScript
import { UserTracker } from "alphana-sdk";
const tracker = new UserTracker({
appId: "YOUR_APP_ID",
secretKey: "YOUR_SECRET_KEY",
});
tracker.init();
// Optional: tear down on logout or SPA teardown
// tracker.destroy();CDN / script tag (IIFE)
The build also emits a self-contained browser bundle:
dist/alphana-sdk.global.js → window.AlphanaSDK<script src="https://your-cdn.example/cdn/alphana-sdk/latest/alphana-sdk.js"></script>
<script>
const tracker = new AlphanaSDK.UserTracker({
appId: "YOUR_APP_ID",
secretKey: "YOUR_SECRET_KEY",
});
tracker.init();
</script>Publish the bundle with MinIO (see CDN upload below). The WordPress plugin and other non-bundler sites load this file.
Configuration
All options are passed to new UserTracker(config) or <UserTrackerProvider config={…} />.
| Option | Type | Default | Description |
| --------------------------- | ---------------------- | ----------------------------------- | -------------------------------------------------------------------------------- |
| endpoint | string | https://api.alphana.ir/api/events | Events API base URL |
| appId | string | — | App identifier from the dashboard |
| secretKey | string | — | Bearer token for authenticated requests |
| sessionId | string | auto (crypto.randomUUID) | Override session ID |
| trackNavigation | boolean | true | SPA route / pageview tracking |
| trackTime | boolean | true | Time-on-page per path |
| trackHeatmap | boolean | true | Mouse, click, scroll sampling |
| trackLogs | boolean | true | Console and uncaught error capture |
| mouseSampleRate | number | 0.3 | Fraction of move/scroll events to record (0–1) |
| maxHeatmapPoints | number | 2000 | Max in-memory points per page |
| batchSize | number | 20 | Auto-flush when queue reaches this size |
| flushInterval | number | 5000 | Auto-flush interval in ms |
| compressPayloads | boolean | true | Gzip-compress large ingest bodies when the browser supports it |
| compressionThresholdBytes | number | 1024 | Minimum JSON size (bytes) before gzip is attempted |
| collectEndpoint | string | — | Override unified ingest URL (default: {apiBase}/collect from endpoint) |
| heatmapPages | string[] | — | Only capture heatmap data on these paths (from dashboard “Heatmap Pages”) |
| onEvent | (event) => void | — | Synchronous callback for every emitted event |
| consentMode | "auto" \| "disabled" | auto | Honor dashboard cookie-consent widget (auto) or track immediately (disabled) |
Example with heatmap path allowlist and custom sampling:
const tracker = new UserTracker({
appId: "YOUR_APP_ID",
secretKey: "YOUR_SECRET_KEY",
heatmapPages: ["/", "/pricing", "/dashboard"],
mouseSampleRate: 0.2,
onEvent: (event) => {
if (event.type === "rageclik") {
console.debug("Rage click", event.data);
}
},
});Core API (UserTracker)
Lifecycle
import { UserTracker, DEFAULT_ENDPOINT } from "alphana-sdk";
const tracker = new UserTracker({ appId: "…", secretKey: "…" });
tracker.init(); // attach listeners; safe on server (no-op)
tracker.destroy(); // remove listeners, flush queue via sendBeaconDEFAULT_ENDPOINT is exported if you need the cloud default URL in your own config layer.
Manual page views
tracker.trackPageView("/products?id=1");Dispatches a pageview event and the custom DOM event tracker:navigate (used internally by time and heatmap plugins).
Session data (in-memory)
tracker.getSession(); // full snapshot: id, visitorId, pageViews, timeSpent, heatmap, location
tracker.getPageViews(); // PageView[]
tracker.getTimeSpent(); // Record<path, milliseconds>
tracker.getHeatmapData("/"); // HeatmapPoint[] for one path
tracker.getHeatmapData(); // Record<path, HeatmapPoint[]>Subscribing to events
const unsub = tracker.subscribe((event) => {
console.log(event.type, event.data);
});
unsub();Feature flags
Requires secretKey. Flags are fetched on init() and after each identify() call.
tracker.identify({ email: "[email protected]", plan: "enterprise" });
tracker.getFlags(); // { "flag-key": true, ... }
tracker.isFeatureEnabled("flag-key"); // boolean
const unsub = tracker.onFlagsChange((flags) => {
console.log("Flags updated", flags);
});
unsub();
await tracker.fetchFlags(); // manual refreshError logging (manual)
When trackLogs is enabled, tracker.logCapture is available after init():
try {
riskyOperation();
} catch (err) {
tracker.logCapture?.capture("error", "riskyOperation failed", {
stack: err instanceof Error ? err.stack : undefined,
meta: { feature: "checkout" },
});
}React API (alphana-sdk/react)
| Export | Description |
| ----------------------------------- | ----------------------------------------------------------------------------- |
| UserTrackerProvider | Creates one UserTracker, calls init() on mount, destroy() on unmount |
| useTracker() | Returns the tracker instance, or null outside a provider |
| usePageView(path?) | Records a page view when path changes (use with usePathname() in Next.js) |
| usePageViews() | Live list of session page views |
| useTimeSpent() | Live Record<path, ms> |
| useHeatmapData(path?, refreshMs?) | Live heatmap points for a path (debounced updates, default 500ms) |
| useFeatureFlags() | Live flag map; re-renders on onFlagsChange |
| useFeatureFlagEnabled(key) | Boolean for a single flag |
UserTrackerProvider captures config only on the first render; changing props later does not reconfigure the tracker.
Event types
Events flow through onEvent, subscribe(), the in-memory session, and the remote batch queue.
| type | Description |
| ----------- | -------------------------------------------------------------------------------- |
| pageview | Path, title, timestamp, referrer |
| timespent | Milliseconds spent on a path before leaving |
| heatmap | Mouse move, click, or scroll point (xPct, yPct, optional target on clicks) |
| rageclik | Rapid repeated clicks in one area |
| uturn | Left a page within ~5s (bounce signal) |
TypeScript definitions:
import type {
TrackerConfig,
TrackerEvent,
UtmParams,
AttributionContext,
PageView,
TimeSpent,
HeatmapPoint,
SessionData,
GeoLocation,
HeatmapRenderOptions,
LogLevel,
LogEntry,
} from "alphana-sdk";Heatmap rendering
Use renderHeatmap to draw collected points on a <canvas> (e.g. in a dashboard preview). Coordinates are percentages of page dimensions, so the overlay scales with canvas size.
import { renderHeatmap } from "alphana-sdk";
const canvas = document.getElementById("heatmap") as HTMLCanvasElement;
const points = tracker.getHeatmapData("/");
renderHeatmap(canvas, points, {
radius: 25,
maxOpacity: 0.85,
minOpacity: 0,
});Network behavior
| Traffic | Endpoint | When |
| ------------------- | --------------------------------------- | ---------------------------------------------------------- |
| Collect (SDK ≥ 1.8) | POST {apiBase}/collect | Event batches, heartbeats, and logs (multiplexed envelope) |
| Event batch | POST {endpoint}/batch | Legacy SDK < 1.8 — still supported on the backend |
| Heartbeat | POST {endpoint}/heartbeat | Legacy SDK < 1.8 |
| Logs | POST {apiBase}/logs/ingest | Legacy SDK < 1.8 |
| Feature flags | POST {apiBase}/feature-flags/evaluate | On init and after identify() |
All authenticated requests send Authorization: Bearer {secretKey}. Analytics failures are silent in production (no thrown errors to end users). Unload uses navigator.sendBeacon when available.
Visitor country/city is resolved asynchronously from IP and attached to batch payloads when available.
Package exports
// Core (framework-agnostic)
import {
UserTracker,
DEFAULT_ENDPOINT,
LogCapture,
renderHeatmap,
} from "alphana-sdk";
// React (optional)
import {
UserTrackerProvider,
useTracker,
usePageView,
useHeatmapData,
usePageViews,
useTimeSpent,
useFeatureFlags,
useFeatureFlagEnabled,
} from "alphana-sdk/react";