alphana-sdk 1.5.5 → 1.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,24 +1,53 @@
1
1
  # alphana-sdk
2
2
 
3
- [![npm](https://img.shields.io/npm/v/alphana-sdk.svg)](https://www.npmjs.com/package/alphana-sdk)
3
+ Client-side analytics SDK for the [Alphana](https://alphana.ir) platform. Collects navigation, time-on-page, heatmap interactions, console errors, and feature-flag evaluations, then batches and sends them to your Alphana backend.
4
4
 
5
- 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.
5
+ **NPM:** `[alphana-sdk](https://www.npmjs.com/package/alphana-sdk)` · **Version:** 0.5.x · **License:** MIT
6
6
 
7
- **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.
7
+ ## Features
8
8
 
9
- - **Package:** [alphana-sdk on npm](https://www.npmjs.com/package/alphana-sdk)
10
- - **Exports:** `alphana-sdk` (core) and `alphana-sdk/react` (provider + hooks)
9
+ - **SPA navigation** — Monitors `history.pushState`, `replaceState`, and `popstate` for automatic page views and U-turn detection (quick bounces).
10
+ - **Time on page** Cumulative dwell time per path, updated on tab hide and navigation.
11
+ - **Heatmaps** — Sampled mouse moves, clicks, scrolls, and rage-click bursts; coordinates stored as page percentages for resolution-independent replay.
12
+ - **Session identity** — Per-tab session ID plus a persistent `visitorId` in `localStorage`.
13
+ - **Batched ingest** — Events queue locally and flush on batch size, interval, or page unload (`sendBeacon`).
14
+ - **Heartbeats** — Keep-alive pings every 30s; deactivate signal when the tab is hidden.
15
+ - **Error logging** — Patches `console.info/warn/error`, `window.onerror`, and unhandled rejections (optional).
16
+ - **Feature flags** — Evaluate flags server-side with `identify()` targeting (requires `secretKey`).
17
+ - **React bindings** — Optional subpath `alphana-sdk/react` with provider and hooks (React 17+).
18
+ - **Tree-shakeable** — `sideEffects: false`; React is an optional peer dependency.
19
+ - **SSR-safe** — `init()` is a no-op when `window` is undefined.
11
20
 
12
21
  ## Installation
13
22
 
14
23
  ```bash
15
24
  npm install alphana-sdk
25
+ # or
16
26
  pnpm add alphana-sdk
17
- yarn add alphana-sdk
27
+ # or
18
28
  bun add alphana-sdk
19
29
  ```
20
30
 
21
- ---
31
+ For React integration, install React 17+ in your app (peer dependency):
32
+
33
+ ```bash
34
+ npm install react react-dom
35
+ ```
36
+
37
+ ## Credentials
38
+
39
+ Create an app in the Alphana dashboard and copy:
40
+
41
+ | Value | Config key | Usage |
42
+ | ---------- | ----------- | -------------------------------- |
43
+ | App ID | `appId` | Sent in every request body |
44
+ | Secret key | `secretKey` | `Authorization: Bearer …` header |
45
+
46
+ Default API endpoint (cloud):
47
+
48
+ ```
49
+ https://api.alphana.ir/api/events
50
+ ```
22
51
 
23
52
  ## Quick start
24
53
 
@@ -35,8 +64,8 @@ createRoot(document.getElementById("root")!).render(
35
64
  <StrictMode>
36
65
  <UserTrackerProvider
37
66
  config={{
38
- appId: import.meta.env.VITE_TRACKER_APP_ID,
39
- secretKey: import.meta.env.VITE_TRACKER_SECRET,
67
+ appId: "YOUR_APP_ID",
68
+ secretKey: "YOUR_SECRET_KEY",
40
69
  }}
41
70
  >
42
71
  <App />
@@ -45,15 +74,25 @@ createRoot(document.getElementById("root")!).render(
45
74
  );
46
75
  ```
47
76
 
48
- ```bash
49
- # .env.local
50
- VITE_TRACKER_APP_ID=your_app_id
51
- VITE_TRACKER_SECRET=your_secret_key
77
+ ```tsx
78
+ // Any child component
79
+ import { useTracker, useFeatureFlagEnabled } from "alphana-sdk/react";
80
+
81
+ export function MyComponent() {
82
+ const tracker = useTracker();
83
+ const showBeta = useFeatureFlagEnabled("new-checkout");
84
+
85
+ function handleSignup() {
86
+ tracker?.identify({ email: "user@example.com", plan: "pro" });
87
+ }
88
+
89
+ return showBeta ? <BetaCheckout /> : <Checkout />;
90
+ }
52
91
  ```
53
92
 
54
93
  ### Next.js App Router
55
94
 
56
- 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).
95
+ 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.
57
96
 
58
97
  ```tsx
59
98
  // app/providers.tsx
@@ -75,6 +114,19 @@ export function Providers({ children }: { children: ReactNode }) {
75
114
  }
76
115
  ```
77
116
 
117
+ ```tsx
118
+ // app/navigation-tracker.tsx
119
+ "use client";
120
+
121
+ import { usePathname } from "next/navigation";
122
+ import { usePageView } from "alphana-sdk/react";
123
+
124
+ export function NavigationTracker() {
125
+ usePageView(usePathname());
126
+ return null;
127
+ }
128
+ ```
129
+
78
130
  ```tsx
79
131
  // app/layout.tsx
80
132
  import { Providers } from "./providers";
@@ -116,7 +168,7 @@ NEXT_PUBLIC_TRACKER_APP_ID=your_app_id
116
168
  NEXT_PUBLIC_TRACKER_SECRET=your_secret_key
117
169
  ```
118
170
 
119
- ### Vanilla JS / TypeScript
171
+ ### Vanilla TypeScript / JavaScript
120
172
 
121
173
  ```ts
122
174
  import { UserTracker } from "alphana-sdk";
@@ -126,213 +178,173 @@ const tracker = new UserTracker({
126
178
  secretKey: "YOUR_SECRET_KEY",
127
179
  });
128
180
 
129
- tracker.init(); // safe no-op during SSR (`window` undefined)
181
+ tracker.init();
130
182
 
131
- tracker.destroy(); // teardown, timers, best-effort final send
183
+ // Optional: tear down on logout or SPA teardown
184
+ // tracker.destroy();
132
185
  ```
133
186
 
134
- ### Script tag / Google Tag Manager
187
+ ## CDN / script tag (IIFE)
135
188
 
136
- Use the CDN build when you cannot install npm packages. Add this to `<head>` or
137
- as a GTM Custom HTML tag:
189
+ The build also emits a self-contained browser bundle:
138
190
 
139
- ```html
140
- <script
141
- async
142
- src="https://storage.alphana.ir/cdn/alphana-sdk/latest/alphana-sdk.js"
143
- data-app-id="YOUR_APP_ID"
144
- data-secret-key="YOUR_SECRET_KEY"
145
- ></script>
146
191
  ```
147
-
148
- Optional config can also be added as `data-*` attributes on the same script:
192
+ dist/alphana-sdk.global.js → window.AlphanaSDK
193
+ ```
149
194
 
150
195
  ```html
151
- <script
152
- async
153
- src="https://storage.alphana.ir/cdn/alphana-sdk/latest/alphana-sdk.js"
154
- data-app-id="YOUR_APP_ID"
155
- data-secret-key="YOUR_SECRET_KEY"
156
- data-track-heatmap="true"
157
- data-session-replay="false"
158
- ></script>
196
+ <script src="https://your-cdn.example/cdn/alphana-sdk/latest/alphana-sdk.js"></script>
197
+ <script>
198
+ const tracker = new AlphanaSDK.UserTracker({
199
+ appId: "YOUR_APP_ID",
200
+ secretKey: "YOUR_SECRET_KEY",
201
+ });
202
+ tracker.init();
203
+ </script>
159
204
  ```
160
205
 
161
- After the script loads, manual calls are available through `window.alphana`,
162
- for example `window.alphana("identify", { email: "user@example.com" })`.
206
+ Publish the bundle with MinIO (see [CDN upload](#cdn-upload) below). The WordPress plugin and other non-bundler sites load this file.
163
207
 
164
- ---
208
+ ## Configuration
165
209
 
166
- ## Configuration reference
210
+ All options are passed to `new UserTracker(config)` or `<UserTrackerProvider config={…} />`.
167
211
 
168
- | Option | Type | Default | Description |
169
- | ------------------ | ------------------------------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
170
- | `appId` | `string` | | **Required for Alphana Cloud.** App ID from the dashboard; included in batch payloads. |
171
- | `secretKey` | `string` | — | **Required for Alphana Cloud** (and for feature flags). Sent as `Authorization: Bearer …` on API calls. |
172
- | `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. |
173
- | `sessionId` | `string` | auto UUID | Optional fixed session id. |
174
- | `trackNavigation` | `boolean` | `true` | Intercept `pushState` / `replaceState` / `popstate` for SPA routes; also emits U-turn detection. |
175
- | `trackTime` | `boolean` | `true` | Cumulative time per path. |
176
- | `trackHeatmap` | `boolean` | `true` | Mouse move, click, scroll sampling; rage-click bursts. |
177
- | `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. |
178
- | `trackLogs` | `boolean` | `true` | Patches `console.info/warn/error`, `window.onerror`, `unhandledrejection`; sends to `/logs/ingest`. |
179
- | `mouseSampleRate` | `number` (0–1) | `0.3` | Fraction of move/scroll events kept. |
180
- | `maxHeatmapPoints` | `number` | `2000` | Max in-memory heatmap points per path in the session snapshot. |
181
- | `batchSize` | `number` | `20` | Queue size before an automatic flush. |
182
- | `flushInterval` | `number` (ms) | `5000` | Timer-based flush when the queue is non-empty. |
183
- | `onEvent` | `(event: TrackerEvent) => void` | — | Synchronous callback for every emitted event. |
212
+ | Option | Type | Default | Description |
213
+ | ------------------ | ----------------- | ----------------------------------- | ------------------------------------------------------------------------- |
214
+ | `endpoint` | `string` | `https://api.alphana.ir/api/events` | Events API base URL |
215
+ | `appId` | `string` | — | App identifier from the dashboard |
216
+ | `secretKey` | `string` | | Bearer token for authenticated requests |
217
+ | `sessionId` | `string` | auto (`crypto.randomUUID`) | Override session ID |
218
+ | `trackNavigation` | `boolean` | `true` | SPA route / pageview tracking |
219
+ | `trackTime` | `boolean` | `true` | Time-on-page per path |
220
+ | `trackHeatmap` | `boolean` | `true` | Mouse, click, scroll sampling |
221
+ | `trackLogs` | `boolean` | `true` | Console and uncaught error capture |
222
+ | `mouseSampleRate` | `number` | `0.3` | Fraction of move/scroll events to record (0–1) |
223
+ | `maxHeatmapPoints` | `number` | `2000` | Max in-memory points per page |
224
+ | `batchSize` | `number` | `20` | Auto-flush when queue reaches this size |
225
+ | `flushInterval` | `number` | `5000` | Auto-flush interval in ms |
226
+ | `heatmapPages` | `string[]` | | Only capture heatmap data on these paths (from dashboard “Heatmap Pages”) |
227
+ | `onEvent` | `(event) => void` | — | Synchronous callback for every emitted event |
184
228
 
185
- TypeScript marks `appId` / `secretKey` as optional on `TrackerConfig`, but you should always pass them when using the hosted Alphana API.
229
+ Example with heatmap path allowlist and custom sampling:
186
230
 
187
- ---
231
+ ```ts
232
+ const tracker = new UserTracker({
233
+ appId: "YOUR_APP_ID",
234
+ secretKey: "YOUR_SECRET_KEY",
235
+ heatmapPages: ["/", "/pricing", "/dashboard"],
236
+ mouseSampleRate: 0.2,
237
+ onEvent: (event) => {
238
+ if (event.type === "rageclik") {
239
+ console.debug("Rage click", event.data);
240
+ }
241
+ },
242
+ });
243
+ ```
188
244
 
189
- ## API
245
+ ## Core API (`UserTracker`)
190
246
 
191
- ### `UserTracker`
247
+ ### Lifecycle
192
248
 
193
249
  ```ts
194
- import { UserTracker } from "alphana-sdk";
250
+ import { UserTracker, DEFAULT_ENDPOINT } from "alphana-sdk";
195
251
 
196
- const tracker = new UserTracker({ appId: "id", secretKey: "sk" });
252
+ const tracker = new UserTracker({ appId: "", secretKey: "" });
197
253
 
198
- tracker.init();
199
- tracker.destroy();
200
-
201
- tracker.trackPageView(path?: string);
202
- tracker.trackRevenue({
203
- eventName: "purchase",
204
- amount: 4900,
205
- currency: "USD",
206
- transactionId: "ord_123",
207
- productId: "starter-plan",
208
- });
209
-
210
- tracker.flush(); // POST queued events to `/batch` (fire-and-forget)
211
-
212
- tracker.getSession(); // read-only SessionData
213
- tracker.getPageViews();
214
- tracker.getTimeSpent(); // Record<path, milliseconds>
215
- tracker.getHeatmapData(path: string): HeatmapPoint[];
216
- tracker.getHeatmapData(): Record<string, HeatmapPoint[]>;
217
-
218
- tracker.subscribe(fn); // returns unsubscribe
219
-
220
- // Feature flags (requires `secretKey` + valid `endpoint`)
221
- tracker.identify({ email: "u@example.com", plan: "pro" });
222
- tracker.getFlags();
223
- tracker.isFeatureEnabled("my-flag");
224
- tracker.onFlagsChange((flags) => { /* ... */ }); // unsubscribe fn
225
- void tracker.fetchFlags();
226
-
227
- // A/B tests (requires `secretKey` + valid `endpoint`)
228
- tracker.getAbVariants();
229
- tracker.getAbVariant("checkout-cta-test");
230
- tracker.isAbVariant("checkout-cta-test", "variant-b");
231
- tracker.onAbTestsChange((variants) => { /* ... */ });
232
- void tracker.fetchAbTests(["checkout-cta-test"]); // optional keys filter
254
+ tracker.init(); // attach listeners; safe on server (no-op)
255
+ tracker.destroy(); // remove listeners, flush queue via sendBeacon
233
256
  ```
234
257
 
235
- **Attribution:** 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.
258
+ `DEFAULT_ENDPOINT` is exported if you need the cloud default URL in your own config layer.
236
259
 
237
- **Revenue:** `trackRevenue(payload)` records purchases, subscriptions, renewals, upgrades, refunds, or custom monetary events. Include a stable `transactionId` for idempotent backend upserts.
260
+ ### Manual page views
238
261
 
239
- **Heartbeat:** Every 30s while the tab is visible, a POST is sent to `{endpoint}/heartbeat` with session and visitor ids.
262
+ ```ts
263
+ tracker.trackPageView("/products?id=1");
264
+ ```
240
265
 
241
- **Deactivate:** On tab hide / `pagehide`, a beacon marks the session inactive and flushes queued analytics via `sendBeacon` when possible.
266
+ Dispatches a `pageview` event and the custom DOM event `tracker:navigate` (used internally by time and heatmap plugins).
242
267
 
243
- **Batching:** Analytics events are POSTed as JSON to `{endpoint}/batch` with `visitorId`, optional `location`, and an `events` array.
268
+ ### Session data (in-memory)
244
269
 
245
- ### `LogCapture`
270
+ ```ts
271
+ tracker.getSession(); // full snapshot: id, visitorId, pageViews, timeSpent, heatmap, location
272
+ tracker.getPageViews(); // PageView[]
273
+ tracker.getTimeSpent(); // Record<path, milliseconds>
274
+ tracker.getHeatmapData("/"); // HeatmapPoint[] for one path
275
+ tracker.getHeatmapData(); // Record<path, HeatmapPoint[]>
276
+ ```
246
277
 
247
- 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`).
278
+ ### Subscribing to events
248
279
 
249
280
  ```ts
250
- import { LogCapture } from "alphana-sdk";
251
-
252
- const capture = new LogCapture({
253
- endpoint: "https://api.alphana.ir/api/events",
254
- sessionId: "ses_abc123",
255
- appId: "YOUR_APP_ID",
256
- secretKey: "sk_...",
281
+ const unsub = tracker.subscribe((event) => {
282
+ console.log(event.type, event.data);
257
283
  });
258
-
259
- capture.init();
260
- capture.capture("error", "Something failed", { stack: err.stack });
261
- capture.destroy();
284
+ unsub();
262
285
  ```
263
286
 
264
- ### `renderHeatmap`
287
+ ### Feature flags
265
288
 
266
- Draws `HeatmapPoint[]` on a `<canvas>` (blue red).
289
+ Requires `secretKey`. Flags are fetched on `init()` and after each `identify()` call.
267
290
 
268
291
  ```ts
269
- import { renderHeatmap } from "alphana-sdk";
292
+ tracker.identify({ email: "user@example.com", plan: "enterprise" });
270
293
 
271
- const canvas = document.getElementById("heatmap") as HTMLCanvasElement;
272
- canvas.width = 1280;
273
- canvas.height = 720;
294
+ tracker.getFlags(); // { "flag-key": true, ... }
295
+ tracker.isFeatureEnabled("flag-key"); // boolean
274
296
 
275
- renderHeatmap(canvas, points, {
276
- radius: 25,
277
- maxOpacity: 0.85,
278
- minOpacity: 0,
297
+ const unsub = tracker.onFlagsChange((flags) => {
298
+ console.log("Flags updated", flags);
279
299
  });
280
- ```
300
+ unsub();
281
301
 
282
- ### `DEFAULT_ENDPOINT`
283
-
284
- ```ts
285
- import { DEFAULT_ENDPOINT } from "alphana-sdk";
286
- // "https://api.alphana.ir/api/events"
302
+ await tracker.fetchFlags(); // manual refresh
287
303
  ```
288
304
 
289
- ---
290
-
291
- ## React hooks
305
+ ### Error logging (manual)
292
306
 
293
- All hooks are exported from **`alphana-sdk/react`**.
307
+ When `trackLogs` is enabled, `tracker.logCapture` is available after `init()`:
294
308
 
295
- `useTracker()` returns `UserTracker | null` (null outside a provider).
309
+ ```ts
310
+ try {
311
+ riskyOperation();
312
+ } catch (err) {
313
+ tracker.logCapture?.capture("error", "riskyOperation failed", {
314
+ stack: err instanceof Error ? err.stack : undefined,
315
+ meta: { feature: "checkout" },
316
+ });
317
+ }
318
+ ```
296
319
 
297
- | Hook | Returns | Description |
298
- | ----------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------ |
299
- | `useTracker()` | `UserTracker \| null` | Tracker from context. |
300
- | `usePageView(path?)` | `void` | When `path` is defined, calls `trackPageView(path)` on change. No-op if `path` is omitted. |
301
- | `useHeatmapData(path?, refreshMs?)` | `HeatmapPoint[]` | Live points for a path; optional debounced refresh (default 500 ms). |
302
- | `usePageViews()` | `PageView[]` | Page views in the current session. |
303
- | `useTrackRevenue()` | `(payload: RevenueEventPayload) => void` | Stable hook for recording purchases/subscriptions/refunds through the current tracker. |
304
- | `useTimeSpent()` | `Record<string, number>` | Cumulative **milliseconds** per path. |
305
- | `useFeatureFlags()` | `Record<string, boolean>` | Evaluated flags; updates after `identify()`. |
306
- | `useFeatureFlagEnabled(key)` | `boolean` | Whether `key` is enabled. |
307
- | `useAbTests()` | `Record<string, string \| null>` | Assigned variant per experiment key. |
308
- | `useAbVariant(experimentKey)` | `string \| null` | Variant key for one experiment. |
309
- | `useIsAbVariant(experiment, variant)` | `boolean` | Whether visitor is in that variant. |
320
+ ## React API (`alphana-sdk/react`)
310
321
 
311
- ```tsx
312
- import { useTracker, useTimeSpent, useHeatmapData } from "alphana-sdk/react";
322
+ | Export | Description |
323
+ | ----------------------------------- | ----------------------------------------------------------------------------- |
324
+ | `UserTrackerProvider` | Creates one `UserTracker`, calls `init()` on mount, `destroy()` on unmount |
325
+ | `useTracker()` | Returns the tracker instance, or `null` outside a provider |
326
+ | `usePageView(path?)` | Records a page view when `path` changes (use with `usePathname()` in Next.js) |
327
+ | `usePageViews()` | Live list of session page views |
328
+ | `useTimeSpent()` | Live `Record<path, ms>` |
329
+ | `useHeatmapData(path?, refreshMs?)` | Live heatmap points for a path (debounced updates, default 500ms) |
330
+ | `useFeatureFlags()` | Live flag map; re-renders on `onFlagsChange` |
331
+ | `useFeatureFlagEnabled(key)` | Boolean for a single flag |
313
332
 
314
- export function DebugPanel() {
315
- const tracker = useTracker();
316
- const timeByPath = useTimeSpent();
317
- const points = useHeatmapData();
333
+ `UserTrackerProvider` captures `config` only on the first render; changing props later does not reconfigure the tracker.
318
334
 
319
- if (!tracker) return null;
335
+ ## Event types
320
336
 
321
- return (
322
- <div>
323
- <p>Paths tracked: {Object.keys(timeByPath).length}</p>
324
- <p>Heatmap points (this page): {points.length}</p>
325
- <button type="button" onClick={() => tracker.flush()}>
326
- Flush now
327
- </button>
328
- </div>
329
- );
330
- }
331
- ```
337
+ Events flow through `onEvent`, `subscribe()`, the in-memory session, and the remote batch queue.
332
338
 
333
- ---
339
+ | `type` | Description |
340
+ | ----------- | -------------------------------------------------------------------------------- |
341
+ | `pageview` | Path, title, timestamp, referrer |
342
+ | `timespent` | Milliseconds spent on a path before leaving |
343
+ | `heatmap` | Mouse move, click, or scroll point (`xPct`, `yPct`, optional `target` on clicks) |
344
+ | `rageclik` | Rapid repeated clicks in one area |
345
+ | `uturn` | Left a page within ~5s (bounce signal) |
334
346
 
335
- ## TypeScript types
347
+ TypeScript definitions:
336
348
 
337
349
  ```ts
338
350
  import type {
@@ -346,25 +358,61 @@ import type {
346
358
  SessionData,
347
359
  GeoLocation,
348
360
  HeatmapRenderOptions,
349
- RageClick,
350
- UTurn,
351
- RevenueEvent,
352
- RevenueEventPayload,
353
- RevenueEventStatus,
354
- RevenueLineItem,
355
361
  LogLevel,
356
362
  LogEntry,
357
363
  } from "alphana-sdk";
358
364
  ```
359
365
 
360
- ---
366
+ ## Heatmap rendering
361
367
 
362
- ## WordPress
368
+ 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.
363
369
 
364
- For WordPress sites, use the **Alphana Tracker** plugin (ZIP):
370
+ ```ts
371
+ import { renderHeatmap } from "alphana-sdk";
365
372
 
366
- **Download:** [https://storage.alphana.ir/cdn/alphana-tracker.zip](https://storage.alphana.ir/cdn/alphana-tracker.zip)
373
+ const canvas = document.getElementById("heatmap") as HTMLCanvasElement;
374
+ const points = tracker.getHeatmapData("/");
367
375
 
368
- 1. In WordPress admin: **Plugins → Add New → Upload Plugin** and choose the ZIP, or unzip into `wp-content/plugins/alphana-tracker/`.
369
- 2. Activate **Alphana Tracker**.
370
- 3. Open **Settings → Alphana Tracker**, enter **App ID** and **Secret Key** from the Alphana dashboard, enable tracking, and save.
376
+ renderHeatmap(canvas, points, {
377
+ radius: 25,
378
+ maxOpacity: 0.85,
379
+ minOpacity: 0,
380
+ });
381
+ ```
382
+
383
+ ## Network behavior
384
+
385
+ | Traffic | Endpoint | When |
386
+ | ------------- | --------------------------------------- | ----------------------------------------------------------- |
387
+ | Event batch | `POST {endpoint}/batch` | Queue size ≥ `batchSize`, every `flushInterval`, or unload |
388
+ | Heartbeat | `POST {endpoint}/heartbeat` | Every 30s while tab visible; `active: false` on hide/unload |
389
+ | Logs | `POST {apiBase}/logs/ingest` | Each captured log line / error |
390
+ | Feature flags | `POST {apiBase}/feature-flags/evaluate` | On init and after `identify()` |
391
+
392
+ 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.
393
+
394
+ Visitor country/city is resolved asynchronously from IP and attached to batch payloads when available.
395
+
396
+ ## Package exports
397
+
398
+ ```ts
399
+ // Core (framework-agnostic)
400
+ import {
401
+ UserTracker,
402
+ DEFAULT_ENDPOINT,
403
+ LogCapture,
404
+ renderHeatmap,
405
+ } from "alphana-sdk";
406
+
407
+ // React (optional)
408
+ import {
409
+ UserTrackerProvider,
410
+ useTracker,
411
+ usePageView,
412
+ useHeatmapData,
413
+ usePageViews,
414
+ useTimeSpent,
415
+ useFeatureFlags,
416
+ useFeatureFlagEnabled,
417
+ } from "alphana-sdk/react";
418
+ ```