alphana-sdk
v1.6.5
Published
Client-side tracking SDK — navigation, time-on-page, and heatmap data collection.
Maintainers
Readme
alphana-sdk
Client-side analytics SDK for React, Next.js, Vite, and vanilla JS/TS. Tracks SPA navigation, time-on-page, heatmaps (with optional path allowlists), console and runtime errors, session heartbeats, feature flags (with user targeting), behavioral signals such as rage clicks and quick U-turns, first/last-touch UTM attribution, revenue events, and a one-time client_context snapshot per session (device category, OS, browser, language, viewport, time zone, SDK version) for catalog parity with the dashboard — without third-party scripts.
Note: client_context does not record DOM for full visual replay; that still requires a dedicated capture pipeline (e.g. rrweb) if you add it later.
- Package: alphana-sdk on npm
- Exports:
alphana-sdk(core) andalphana-sdk/react(provider + hooks)
Installation
npm install alphana-sdk
pnpm add alphana-sdk
yarn add alphana-sdk
bun add alphana-sdkQuick 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: import.meta.env.VITE_TRACKER_APP_ID,
secretKey: import.meta.env.VITE_TRACKER_SECRET,
}}
>
<App />
</UserTrackerProvider>
</StrictMode>,
);# .env.local
VITE_TRACKER_APP_ID=your_app_id
VITE_TRACKER_SECRET=your_secret_keyNext.js App Router
Wrap the provider in a Client Component, then use usePageView with usePathname() so App Router navigations are recorded (History API hooks alone do not always fire).
// 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/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 JS / TypeScript
import { UserTracker } from "alphana-sdk";
const tracker = new UserTracker({
appId: "YOUR_APP_ID",
secretKey: "YOUR_SECRET_KEY",
});
tracker.init(); // safe no-op during SSR (`window` undefined)
tracker.destroy(); // teardown, timers, best-effort final sendScript tag / Google Tag Manager
Use the CDN build when you cannot install npm packages. Add this to <head> or
as a GTM Custom HTML tag:
<script
async
src="https://storage.alphana.ir/cdn/alphana-sdk/latest/alphana-sdk.js"
data-app-id="YOUR_APP_ID"
data-secret-key="YOUR_SECRET_KEY"
></script>Optional config can also be added as data-* attributes on the same script:
<script
async
src="https://storage.alphana.ir/cdn/alphana-sdk/latest/alphana-sdk.js"
data-app-id="YOUR_APP_ID"
data-secret-key="YOUR_SECRET_KEY"
data-track-heatmap="true"
data-session-replay="false"
></script>After the script loads, manual calls are available through window.alphana,
for example window.alphana("identify", { email: "[email protected]" }).
Configuration reference
| Option | Type | Default | Description |
| ------------------ | ------------------------------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| appId | string | — | Required for Alphana Cloud. App ID from the dashboard; included in batch payloads. |
| secretKey | string | — | Required for Alphana Cloud (and for feature flags). Sent as Authorization: Bearer … on API calls. |
| endpoint | string | https://api.alphana.ir/api/events | Events base URL for Alphana Cloud. Batching uses {endpoint}/batch, heartbeats {endpoint}/heartbeat, logs derived under the same API prefix. |
| sessionId | string | auto UUID | Optional fixed session id. |
| trackNavigation | boolean | true | Intercept pushState / replaceState / popstate for SPA routes; also emits U-turn detection. |
| trackTime | boolean | true | Cumulative time per path. |
| trackHeatmap | boolean | true | Mouse move, click, scroll sampling; rage-click bursts. |
| heatmapPages | string[] | — | Optional client allowlist: heatmap capture is limited to these paths (values are normalized like dashboard rows). If omitted, the SDK may still record moves; the backend only persists heatmap points when the app has ≥1 registered heatmap page and the path matches. |
| trackLogs | boolean | true | Patches console.info/warn/error, window.onerror, unhandledrejection; sends to /logs/ingest. |
| mouseSampleRate | number (0–1) | 0.3 | Fraction of move/scroll events kept. |
| maxHeatmapPoints | number | 2000 | Max in-memory heatmap points per path in the session snapshot. |
| batchSize | number | 20 | Queue size before an automatic flush. |
| flushInterval | number (ms) | 5000 | Timer-based flush when the queue is non-empty. |
| onEvent | (event: TrackerEvent) => void | — | Synchronous callback for every emitted event. |
TypeScript marks appId / secretKey as optional on TrackerConfig, but you should always pass them when using the hosted Alphana API.
API
UserTracker
import { UserTracker } from "alphana-sdk";
const tracker = new UserTracker({ appId: "id", secretKey: "sk" });
tracker.init();
tracker.destroy();
tracker.trackPageView(path?: string);
tracker.trackRevenue({
eventName: "purchase",
amount: 4900,
currency: "USD",
transactionId: "ord_123",
productId: "starter-plan",
});
tracker.flush(); // POST queued events to `/batch` (fire-and-forget)
tracker.getSession(); // read-only SessionData
tracker.getPageViews();
tracker.getTimeSpent(); // Record<path, milliseconds>
tracker.getHeatmapData(path: string): HeatmapPoint[];
tracker.getHeatmapData(): Record<string, HeatmapPoint[]>;
tracker.subscribe(fn); // returns unsubscribe
// Feature flags (requires `secretKey` + valid `endpoint`)
tracker.identify({ email: "[email protected]", plan: "pro" });
tracker.getFlags();
tracker.isFeatureEnabled("my-flag");
tracker.onFlagsChange((flags) => { /* ... */ }); // unsubscribe fn
void tracker.fetchFlags();
// A/B tests (requires `secretKey` + valid `endpoint`)
tracker.getAbVariants();
tracker.getAbVariant("checkout-cta-test");
tracker.isAbVariant("checkout-cta-test", "variant-b");
tracker.onAbTestsChange((variants) => { /* ... */ });
void tracker.fetchAbTests(["checkout-cta-test"]); // optional keys filterAttribution: Pageviews automatically read utm_source, utm_medium, utm_campaign, utm_term, utm_content, plus click IDs (gclid, fbclid, ttclid, msclkid) from the current URL. The SDK persists first-touch and last-touch context in localStorage and attaches it to pageview and revenue events.
Revenue: trackRevenue(payload) records purchases, subscriptions, renewals, upgrades, refunds, or custom monetary events. Include a stable transactionId for idempotent backend upserts.
Heartbeat: Every 30s while the tab is visible, a POST is sent to {endpoint}/heartbeat with session and visitor ids.
Deactivate: On tab hide / pagehide, a beacon marks the session inactive and flushes queued analytics via sendBeacon when possible.
Batching: Analytics events are POSTed as JSON to {endpoint}/batch with visitorId, optional location, and an events array.
LogCapture
Used internally when trackLogs: true. Sends structured entries to {apiBase}/logs/ingest (API base is derived by stripping the last path segment from your endpoint, e.g. …/api/events → …/api).
import { LogCapture } from "alphana-sdk";
const capture = new LogCapture({
endpoint: "https://api.alphana.ir/api/events",
sessionId: "ses_abc123",
appId: "YOUR_APP_ID",
secretKey: "sk_...",
});
capture.init();
capture.capture("error", "Something failed", { stack: err.stack });
capture.destroy();renderHeatmap
Draws HeatmapPoint[] on a <canvas> (blue → red).
import { renderHeatmap } from "alphana-sdk";
const canvas = document.getElementById("heatmap") as HTMLCanvasElement;
canvas.width = 1280;
canvas.height = 720;
renderHeatmap(canvas, points, {
radius: 25,
maxOpacity: 0.85,
minOpacity: 0,
});DEFAULT_ENDPOINT
import { DEFAULT_ENDPOINT } from "alphana-sdk";
// "https://api.alphana.ir/api/events"React hooks
All hooks are exported from alphana-sdk/react.
useTracker() returns UserTracker | null (null outside a provider).
| Hook | Returns | Description |
| ----------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------ |
| useTracker() | UserTracker \| null | Tracker from context. |
| usePageView(path?) | void | When path is defined, calls trackPageView(path) on change. No-op if path is omitted. |
| useHeatmapData(path?, refreshMs?) | HeatmapPoint[] | Live points for a path; optional debounced refresh (default 500 ms). |
| usePageViews() | PageView[] | Page views in the current session. |
| useTrackRevenue() | (payload: RevenueEventPayload) => void | Stable hook for recording purchases/subscriptions/refunds through the current tracker. |
| useTimeSpent() | Record<string, number> | Cumulative milliseconds per path. |
| useFeatureFlags() | Record<string, boolean> | Evaluated flags; updates after identify(). |
| useFeatureFlagEnabled(key) | boolean | Whether key is enabled. |
| useAbTests() | Record<string, string \| null> | Assigned variant per experiment key. |
| useAbVariant(experimentKey) | string \| null | Variant key for one experiment. |
| useIsAbVariant(experiment, variant) | boolean | Whether visitor is in that variant. |
import { useTracker, useTimeSpent, useHeatmapData } from "alphana-sdk/react";
export function DebugPanel() {
const tracker = useTracker();
const timeByPath = useTimeSpent();
const points = useHeatmapData();
if (!tracker) return null;
return (
<div>
<p>Paths tracked: {Object.keys(timeByPath).length}</p>
<p>Heatmap points (this page): {points.length}</p>
<button type="button" onClick={() => tracker.flush()}>
Flush now
</button>
</div>
);
}TypeScript types
import type {
TrackerConfig,
TrackerEvent,
UtmParams,
AttributionContext,
PageView,
TimeSpent,
HeatmapPoint,
SessionData,
GeoLocation,
HeatmapRenderOptions,
RageClick,
UTurn,
RevenueEvent,
RevenueEventPayload,
RevenueEventStatus,
RevenueLineItem,
LogLevel,
LogEntry,
} from "alphana-sdk";WordPress
For WordPress sites, use the Alphana Tracker plugin (ZIP):
Download: https://storage.alphana.ir/cdn/alphana-tracker.zip
- In WordPress admin: Plugins → Add New → Upload Plugin and choose the ZIP, or unzip into
wp-content/plugins/alphana-tracker/. - Activate Alphana Tracker.
- Open Settings → Alphana Tracker, enter App ID and Secret Key from the Alphana dashboard, enable tracking, and save.
