alphana-sdk 1.6.5 → 1.8.0

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,176 @@ 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" })`.
163
-
164
- ---
165
-
166
- ## Configuration reference
206
+ Publish the bundle with MinIO (see [CDN upload](#cdn-upload) below). The WordPress plugin and other non-bundler sites load this file.
207
+
208
+ ## Configuration
209
+
210
+ All options are passed to `new UserTracker(config)` or `<UserTrackerProvider config={…} />`.
211
+
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
+ | `compressPayloads` | `boolean` | `true` | Gzip-compress large ingest bodies when the browser supports it |
227
+ | `compressionThresholdBytes` | `number` | `1024` | Minimum JSON size (bytes) before gzip is attempted |
228
+ | `collectEndpoint` | `string` | — | Override unified ingest URL (default: `{apiBase}/collect` from `endpoint`) |
229
+ | `heatmapPages` | `string[]` | — | Only capture heatmap data on these paths (from dashboard “Heatmap Pages”) |
230
+ | `onEvent` | `(event) => void` | — | Synchronous callback for every emitted event |
231
+
232
+ Example with heatmap path allowlist and custom sampling:
167
233
 
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. |
184
-
185
- TypeScript marks `appId` / `secretKey` as optional on `TrackerConfig`, but you should always pass them when using the hosted Alphana API.
186
-
187
- ---
234
+ ```ts
235
+ const tracker = new UserTracker({
236
+ appId: "YOUR_APP_ID",
237
+ secretKey: "YOUR_SECRET_KEY",
238
+ heatmapPages: ["/", "/pricing", "/dashboard"],
239
+ mouseSampleRate: 0.2,
240
+ onEvent: (event) => {
241
+ if (event.type === "rageclik") {
242
+ console.debug("Rage click", event.data);
243
+ }
244
+ },
245
+ });
246
+ ```
188
247
 
189
- ## API
248
+ ## Core API (`UserTracker`)
190
249
 
191
- ### `UserTracker`
250
+ ### Lifecycle
192
251
 
193
252
  ```ts
194
- import { UserTracker } from "alphana-sdk";
253
+ import { UserTracker, DEFAULT_ENDPOINT } from "alphana-sdk";
195
254
 
196
- const tracker = new UserTracker({ appId: "id", secretKey: "sk" });
197
-
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
- });
255
+ const tracker = new UserTracker({ appId: "", secretKey: "" });
209
256
 
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
257
+ tracker.init(); // attach listeners; safe on server (no-op)
258
+ tracker.destroy(); // remove listeners, flush queue via sendBeacon
233
259
  ```
234
260
 
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.
261
+ `DEFAULT_ENDPOINT` is exported if you need the cloud default URL in your own config layer.
236
262
 
237
- **Revenue:** `trackRevenue(payload)` records purchases, subscriptions, renewals, upgrades, refunds, or custom monetary events. Include a stable `transactionId` for idempotent backend upserts.
263
+ ### Manual page views
238
264
 
239
- **Heartbeat:** Every 30s while the tab is visible, a POST is sent to `{endpoint}/heartbeat` with session and visitor ids.
265
+ ```ts
266
+ tracker.trackPageView("/products?id=1");
267
+ ```
240
268
 
241
- **Deactivate:** On tab hide / `pagehide`, a beacon marks the session inactive and flushes queued analytics via `sendBeacon` when possible.
269
+ Dispatches a `pageview` event and the custom DOM event `tracker:navigate` (used internally by time and heatmap plugins).
242
270
 
243
- **Batching:** Analytics events are POSTed as JSON to `{endpoint}/batch` with `visitorId`, optional `location`, and an `events` array.
271
+ ### Session data (in-memory)
244
272
 
245
- ### `LogCapture`
273
+ ```ts
274
+ tracker.getSession(); // full snapshot: id, visitorId, pageViews, timeSpent, heatmap, location
275
+ tracker.getPageViews(); // PageView[]
276
+ tracker.getTimeSpent(); // Record<path, milliseconds>
277
+ tracker.getHeatmapData("/"); // HeatmapPoint[] for one path
278
+ tracker.getHeatmapData(); // Record<path, HeatmapPoint[]>
279
+ ```
246
280
 
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`).
281
+ ### Subscribing to events
248
282
 
249
283
  ```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_...",
284
+ const unsub = tracker.subscribe((event) => {
285
+ console.log(event.type, event.data);
257
286
  });
258
-
259
- capture.init();
260
- capture.capture("error", "Something failed", { stack: err.stack });
261
- capture.destroy();
287
+ unsub();
262
288
  ```
263
289
 
264
- ### `renderHeatmap`
290
+ ### Feature flags
265
291
 
266
- Draws `HeatmapPoint[]` on a `<canvas>` (blue red).
292
+ Requires `secretKey`. Flags are fetched on `init()` and after each `identify()` call.
267
293
 
268
294
  ```ts
269
- import { renderHeatmap } from "alphana-sdk";
295
+ tracker.identify({ email: "user@example.com", plan: "enterprise" });
270
296
 
271
- const canvas = document.getElementById("heatmap") as HTMLCanvasElement;
272
- canvas.width = 1280;
273
- canvas.height = 720;
297
+ tracker.getFlags(); // { "flag-key": true, ... }
298
+ tracker.isFeatureEnabled("flag-key"); // boolean
274
299
 
275
- renderHeatmap(canvas, points, {
276
- radius: 25,
277
- maxOpacity: 0.85,
278
- minOpacity: 0,
300
+ const unsub = tracker.onFlagsChange((flags) => {
301
+ console.log("Flags updated", flags);
279
302
  });
280
- ```
281
-
282
- ### `DEFAULT_ENDPOINT`
303
+ unsub();
283
304
 
284
- ```ts
285
- import { DEFAULT_ENDPOINT } from "alphana-sdk";
286
- // "https://api.alphana.ir/api/events"
305
+ await tracker.fetchFlags(); // manual refresh
287
306
  ```
288
307
 
289
- ---
290
-
291
- ## React hooks
308
+ ### Error logging (manual)
292
309
 
293
- All hooks are exported from **`alphana-sdk/react`**.
310
+ When `trackLogs` is enabled, `tracker.logCapture` is available after `init()`:
294
311
 
295
- `useTracker()` returns `UserTracker | null` (null outside a provider).
312
+ ```ts
313
+ try {
314
+ riskyOperation();
315
+ } catch (err) {
316
+ tracker.logCapture?.capture("error", "riskyOperation failed", {
317
+ stack: err instanceof Error ? err.stack : undefined,
318
+ meta: { feature: "checkout" },
319
+ });
320
+ }
321
+ ```
296
322
 
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. |
323
+ ## React API (`alphana-sdk/react`)
310
324
 
311
- ```tsx
312
- import { useTracker, useTimeSpent, useHeatmapData } from "alphana-sdk/react";
325
+ | Export | Description |
326
+ | ----------------------------------- | ----------------------------------------------------------------------------- |
327
+ | `UserTrackerProvider` | Creates one `UserTracker`, calls `init()` on mount, `destroy()` on unmount |
328
+ | `useTracker()` | Returns the tracker instance, or `null` outside a provider |
329
+ | `usePageView(path?)` | Records a page view when `path` changes (use with `usePathname()` in Next.js) |
330
+ | `usePageViews()` | Live list of session page views |
331
+ | `useTimeSpent()` | Live `Record<path, ms>` |
332
+ | `useHeatmapData(path?, refreshMs?)` | Live heatmap points for a path (debounced updates, default 500ms) |
333
+ | `useFeatureFlags()` | Live flag map; re-renders on `onFlagsChange` |
334
+ | `useFeatureFlagEnabled(key)` | Boolean for a single flag |
313
335
 
314
- export function DebugPanel() {
315
- const tracker = useTracker();
316
- const timeByPath = useTimeSpent();
317
- const points = useHeatmapData();
336
+ `UserTrackerProvider` captures `config` only on the first render; changing props later does not reconfigure the tracker.
318
337
 
319
- if (!tracker) return null;
338
+ ## Event types
320
339
 
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
- ```
340
+ Events flow through `onEvent`, `subscribe()`, the in-memory session, and the remote batch queue.
332
341
 
333
- ---
342
+ | `type` | Description |
343
+ | ----------- | -------------------------------------------------------------------------------- |
344
+ | `pageview` | Path, title, timestamp, referrer |
345
+ | `timespent` | Milliseconds spent on a path before leaving |
346
+ | `heatmap` | Mouse move, click, or scroll point (`xPct`, `yPct`, optional `target` on clicks) |
347
+ | `rageclik` | Rapid repeated clicks in one area |
348
+ | `uturn` | Left a page within ~5s (bounce signal) |
334
349
 
335
- ## TypeScript types
350
+ TypeScript definitions:
336
351
 
337
352
  ```ts
338
353
  import type {
@@ -346,25 +361,62 @@ import type {
346
361
  SessionData,
347
362
  GeoLocation,
348
363
  HeatmapRenderOptions,
349
- RageClick,
350
- UTurn,
351
- RevenueEvent,
352
- RevenueEventPayload,
353
- RevenueEventStatus,
354
- RevenueLineItem,
355
364
  LogLevel,
356
365
  LogEntry,
357
366
  } from "alphana-sdk";
358
367
  ```
359
368
 
360
- ---
369
+ ## Heatmap rendering
370
+
371
+ 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.
372
+
373
+ ```ts
374
+ import { renderHeatmap } from "alphana-sdk";
375
+
376
+ const canvas = document.getElementById("heatmap") as HTMLCanvasElement;
377
+ const points = tracker.getHeatmapData("/");
378
+
379
+ renderHeatmap(canvas, points, {
380
+ radius: 25,
381
+ maxOpacity: 0.85,
382
+ minOpacity: 0,
383
+ });
384
+ ```
385
+
386
+ ## Network behavior
387
+
388
+ | Traffic | Endpoint | When |
389
+ | ------------- | --------------------------------------- | ----------------------------------------------------------- |
390
+ | Collect (SDK ≥ 1.8) | `POST {apiBase}/collect` | Event batches, heartbeats, and logs (multiplexed envelope) |
391
+ | Event batch | `POST {endpoint}/batch` | Legacy SDK &lt; 1.8 — still supported on the backend |
392
+ | Heartbeat | `POST {endpoint}/heartbeat` | Legacy SDK &lt; 1.8 |
393
+ | Logs | `POST {apiBase}/logs/ingest` | Legacy SDK &lt; 1.8 |
394
+ | Feature flags | `POST {apiBase}/feature-flags/evaluate` | On init and after `identify()` |
395
+
396
+ 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.
361
397
 
362
- ## WordPress
398
+ Visitor country/city is resolved asynchronously from IP and attached to batch payloads when available.
363
399
 
364
- For WordPress sites, use the **Alphana Tracker** plugin (ZIP):
400
+ ## Package exports
365
401
 
366
- **Download:** [https://storage.alphana.ir/cdn/alphana-tracker.zip](https://storage.alphana.ir/cdn/alphana-tracker.zip)
402
+ ```ts
403
+ // Core (framework-agnostic)
404
+ import {
405
+ UserTracker,
406
+ DEFAULT_ENDPOINT,
407
+ LogCapture,
408
+ renderHeatmap,
409
+ } from "alphana-sdk";
367
410
 
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.
411
+ // React (optional)
412
+ import {
413
+ UserTrackerProvider,
414
+ useTracker,
415
+ usePageView,
416
+ useHeatmapData,
417
+ usePageViews,
418
+ useTimeSpent,
419
+ useFeatureFlags,
420
+ useFeatureFlagEnabled,
421
+ } from "alphana-sdk/react";
422
+ ```