alphana-sdk 0.2.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 +298 -0
- package/dist/index.d.mts +289 -0
- package/dist/index.d.ts +289 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3 -0
- package/dist/index.mjs.map +1 -0
- package/dist/react/index.d.mts +318 -0
- package/dist/react/index.d.ts +318 -0
- package/dist/react/index.js +2 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/index.mjs +2 -0
- package/dist/react/index.mjs.map +1 -0
- package/package.json +63 -0
package/README.md
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
# alphana-sdk
|
|
2
|
+
|
|
3
|
+
Client-side analytics SDK for React, Next.js, Vite, and vanilla JS/TS projects. Tracks navigation, time-on-page, heatmaps, error logs, and periodic page screenshots — all without cookies or third parties.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install alphana-sdk
|
|
9
|
+
# pnpm
|
|
10
|
+
pnpm add alphana-sdk
|
|
11
|
+
# yarn
|
|
12
|
+
yarn add alphana-sdk
|
|
13
|
+
# bun
|
|
14
|
+
bun add alphana-sdk
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
> **Snapshots optional:** If you want the screenshot feature (`trackSnapshots: true`), also install `html2canvas`:
|
|
18
|
+
> ```bash
|
|
19
|
+
> npm install html2canvas
|
|
20
|
+
> ```
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Quick start
|
|
25
|
+
|
|
26
|
+
### React / Vite
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
// main.tsx
|
|
30
|
+
import { StrictMode } from 'react';
|
|
31
|
+
import { createRoot } from 'react-dom/client';
|
|
32
|
+
import { UserTrackerProvider } from 'alphana-sdk/react';
|
|
33
|
+
import App from './App';
|
|
34
|
+
|
|
35
|
+
createRoot(document.getElementById('root')!).render(
|
|
36
|
+
<StrictMode>
|
|
37
|
+
<UserTrackerProvider
|
|
38
|
+
config={{
|
|
39
|
+
appId: import.meta.env.VITE_TRACKER_APP_ID,
|
|
40
|
+
secretKey: import.meta.env.VITE_TRACKER_SECRET,
|
|
41
|
+
}}
|
|
42
|
+
>
|
|
43
|
+
<App />
|
|
44
|
+
</UserTrackerProvider>
|
|
45
|
+
</StrictMode>,
|
|
46
|
+
);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# .env.local
|
|
51
|
+
VITE_TRACKER_APP_ID=your_app_id
|
|
52
|
+
VITE_TRACKER_SECRET=your_secret_key
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Next.js App Router
|
|
56
|
+
|
|
57
|
+
Because App Router renders server-side by default, wrap the provider in a Client Component:
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
// app/providers.tsx
|
|
61
|
+
'use client';
|
|
62
|
+
import { UserTrackerProvider } from 'alphana-sdk/react';
|
|
63
|
+
import type { ReactNode } from 'react';
|
|
64
|
+
|
|
65
|
+
export function Providers({ children }: { children: ReactNode }) {
|
|
66
|
+
return (
|
|
67
|
+
<UserTrackerProvider
|
|
68
|
+
config={{
|
|
69
|
+
appId: process.env.NEXT_PUBLIC_TRACKER_APP_ID!,
|
|
70
|
+
secretKey: process.env.NEXT_PUBLIC_TRACKER_SECRET!,
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
{children}
|
|
74
|
+
</UserTrackerProvider>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
// app/layout.tsx
|
|
81
|
+
import { Providers } from './providers';
|
|
82
|
+
|
|
83
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
84
|
+
return (
|
|
85
|
+
<html lang="en">
|
|
86
|
+
<body>
|
|
87
|
+
<Providers>{children}</Providers>
|
|
88
|
+
</body>
|
|
89
|
+
</html>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# .env.local
|
|
96
|
+
NEXT_PUBLIC_TRACKER_APP_ID=your_app_id
|
|
97
|
+
NEXT_PUBLIC_TRACKER_SECRET=your_secret_key
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
> **App Router page tracking:** The provider automatically intercepts `pushState` / `popstate`. For App Router you can optionally add an explicit page-view call using the `usePageView` hook in a Client Component:
|
|
101
|
+
>
|
|
102
|
+
> ```tsx
|
|
103
|
+
> // app/components/NavigationTracker.tsx
|
|
104
|
+
> 'use client';
|
|
105
|
+
> import { usePathname } from 'next/navigation';
|
|
106
|
+
> import { usePageView } from 'alphana-sdk/react';
|
|
107
|
+
>
|
|
108
|
+
> export function NavigationTracker() {
|
|
109
|
+
> usePageView(usePathname());
|
|
110
|
+
> return null;
|
|
111
|
+
> }
|
|
112
|
+
> ```
|
|
113
|
+
|
|
114
|
+
### Vanilla JS / TypeScript
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import { UserTracker } from 'alphana-sdk';
|
|
118
|
+
|
|
119
|
+
const tracker = new UserTracker({
|
|
120
|
+
appId: 'YOUR_APP_ID',
|
|
121
|
+
secretKey: 'YOUR_SECRET_KEY',
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
tracker.init(); // attach listeners; no-op in SSR environments
|
|
125
|
+
|
|
126
|
+
// Call on teardown / logout
|
|
127
|
+
tracker.destroy();
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Configuration reference
|
|
133
|
+
|
|
134
|
+
| Option | Type | Default | Description |
|
|
135
|
+
| -------------------- | ------------------------------- | --------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
136
|
+
| `appId` | `string` | — | **Required.** Your app ID from the Alphana dashboard. |
|
|
137
|
+
| `secretKey` | `string` | — | **Required.** SDK secret key — sent as `Authorization: Bearer`. |
|
|
138
|
+
| `endpoint` | `string` | `https://api.alphana.ir/api/events` | Override only when self-hosting. |
|
|
139
|
+
| `sessionId` | `string` | auto UUID | Override the auto-generated session identifier. |
|
|
140
|
+
| `trackNavigation` | `boolean` | `true` | Intercept `pushState` / `popstate` for SPA route changes. |
|
|
141
|
+
| `trackTime` | `boolean` | `true` | Measure time spent on each page. |
|
|
142
|
+
| `trackHeatmap` | `boolean` | `true` | Record mouse-move, click, and scroll positions. |
|
|
143
|
+
| `trackLogs` | `boolean` | `true` | Capture `console.info/warn/error`, `window.onerror`, and `unhandledrejection`. |
|
|
144
|
+
| `trackSnapshots` | `boolean` | `true` | Send a full-page screenshot every interval (requires `html2canvas`). |
|
|
145
|
+
| `snapshotIntervalMs` | `number` (ms) | `300000` (5 min) | How often to capture a snapshot when `trackSnapshots` is enabled. |
|
|
146
|
+
| `mouseSampleRate` | `number` (0–1) | `0.3` | Fraction of mouse/scroll events to record. |
|
|
147
|
+
| `maxHeatmapPoints` | `number` | `2000` | Maximum in-memory heatmap points per page. |
|
|
148
|
+
| `batchSize` | `number` | `20` | Events queued before an automatic batch flush. |
|
|
149
|
+
| `flushInterval` | `number` (ms) | `5000` | Milliseconds between automatic flushes regardless of queue size. |
|
|
150
|
+
| `onEvent` | `(event: TrackerEvent) => void` | — | Callback invoked synchronously for every emitted event. |
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## API
|
|
155
|
+
|
|
156
|
+
### `UserTracker`
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
import { UserTracker } from 'alphana-sdk';
|
|
160
|
+
|
|
161
|
+
const tracker = new UserTracker({ appId: 'id', secretKey: 'sk' });
|
|
162
|
+
|
|
163
|
+
tracker.init(); // attach listeners; no-op in SSR; returns `this`
|
|
164
|
+
tracker.destroy(); // remove all listeners and timers, flush remaining queue
|
|
165
|
+
|
|
166
|
+
tracker.trackPageView(path?: string); // manually record a page view
|
|
167
|
+
|
|
168
|
+
tracker.getSession(); // SessionData snapshot for the current session
|
|
169
|
+
tracker.getPageViews(); // PageView[] recorded this session
|
|
170
|
+
tracker.getTimeSpent(); // Record<path, ms> cumulative time per path
|
|
171
|
+
tracker.getHeatmapData(path?: string); // HeatmapPoint[] for path, or all paths if omitted
|
|
172
|
+
|
|
173
|
+
tracker.flush(); // immediately POST all queued events
|
|
174
|
+
tracker.subscribe(fn); // register an event listener; returns an unsubscribe fn
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Heartbeat:** The SDK emits a `session:heartbeat` event every 30 seconds to keep the session alive.
|
|
178
|
+
|
|
179
|
+
**Page-hide flush:** On `visibilitychange` (tab hidden or browser minimised) the SDK calls `navigator.sendBeacon` to ensure no events are dropped.
|
|
180
|
+
|
|
181
|
+
### `LogCapture`
|
|
182
|
+
|
|
183
|
+
Automatically used by `UserTracker` when `trackLogs: true`. Can also be used standalone:
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
import { LogCapture } from 'alphana-sdk';
|
|
187
|
+
|
|
188
|
+
const capture = new LogCapture({
|
|
189
|
+
endpoint: 'https://your-backend.com/api/events',
|
|
190
|
+
sessionId: 'ses_abc123',
|
|
191
|
+
appId: 'YOUR_APP_ID',
|
|
192
|
+
secretKey: 'sk_...',
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
capture.init(); // patches console.info/warn/error, window.onerror, unhandledrejection
|
|
196
|
+
capture.destroy(); // restores original methods
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### `renderHeatmap`
|
|
200
|
+
|
|
201
|
+
Renders a `HeatmapPoint[]` array onto a `<canvas>` element using a blue → red color palette.
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
import { renderHeatmap } from 'alphana-sdk';
|
|
205
|
+
|
|
206
|
+
const canvas = document.getElementById('heatmap') as HTMLCanvasElement;
|
|
207
|
+
canvas.width = 1280;
|
|
208
|
+
canvas.height = 720;
|
|
209
|
+
|
|
210
|
+
renderHeatmap(canvas, points, {
|
|
211
|
+
radius: 25, // blur radius in px (default: 25)
|
|
212
|
+
maxOpacity: 0.85, // max alpha for hotspots (default: 0.85)
|
|
213
|
+
minOpacity: 0, // min alpha for cold areas (default: 0)
|
|
214
|
+
});
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### `DEFAULT_ENDPOINT`
|
|
218
|
+
|
|
219
|
+
The default API URL is exported if you need it:
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
import { DEFAULT_ENDPOINT } from 'alphana-sdk';
|
|
223
|
+
// "https://api.alphana.ir/api/events"
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## React hooks
|
|
229
|
+
|
|
230
|
+
All hooks are exported from `alphana/react`.
|
|
231
|
+
|
|
232
|
+
| Hook | Returns | Description |
|
|
233
|
+
| ------------------------------------ | ------------------ | ------------------------------------------------------------------ |
|
|
234
|
+
| `useTracker()` | `UserTracker` | Access the tracker instance from context. |
|
|
235
|
+
| `usePageView(path?)` | `void` | Record a page view when `path` changes (App Router helper). |
|
|
236
|
+
| `useHeatmapData(path?, refreshMs?)` | `HeatmapPoint[]` | Live heatmap points for a path, polled every `refreshMs` (500 ms). |
|
|
237
|
+
| `usePageViews()` | `PageView[]` | All page views recorded in the current session. |
|
|
238
|
+
| `useTimeSpent()` | `number` (seconds) | Total time spent on the current page (updates every second). |
|
|
239
|
+
|
|
240
|
+
```tsx
|
|
241
|
+
import { useTracker, useTimeSpent, useHeatmapData } from 'alphana-sdk/react';
|
|
242
|
+
|
|
243
|
+
export function DebugPanel() {
|
|
244
|
+
const tracker = useTracker();
|
|
245
|
+
const timeSpent = useTimeSpent(); // seconds on this page
|
|
246
|
+
const points = useHeatmapData(); // HeatmapPoint[]
|
|
247
|
+
|
|
248
|
+
return (
|
|
249
|
+
<div>
|
|
250
|
+
<p>Time on page: {timeSpent}s</p>
|
|
251
|
+
<p>Heatmap points: {points.length}</p>
|
|
252
|
+
<button onClick={() => tracker.flush()}>Flush now</button>
|
|
253
|
+
</div>
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## TypeScript types
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
import type {
|
|
264
|
+
TrackerConfig,
|
|
265
|
+
TrackerEvent,
|
|
266
|
+
PageView,
|
|
267
|
+
TimeSpent,
|
|
268
|
+
HeatmapPoint,
|
|
269
|
+
SessionData,
|
|
270
|
+
GeoLocation,
|
|
271
|
+
LogLevel,
|
|
272
|
+
LogEntry,
|
|
273
|
+
HeatmapRenderOptions,
|
|
274
|
+
} from 'alphana-sdk';
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Self-hosting
|
|
280
|
+
|
|
281
|
+
The full backend, dashboard, and landing page are open source. To run on your own infrastructure:
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
git clone https://github.com/teokamalipour/alphana-sdk.git
|
|
285
|
+
cd alphana-sdk
|
|
286
|
+
cp .env.example .env # fill in your values
|
|
287
|
+
docker compose up -d # starts MongoDB, NestJS backend, and React dashboard
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Then point the SDK at your server:
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
new UserTracker({
|
|
294
|
+
appId: 'YOUR_APP_ID',
|
|
295
|
+
secretKey: 'YOUR_SECRET_KEY',
|
|
296
|
+
endpoint: 'https://your-server.example.com/api/events',
|
|
297
|
+
});
|
|
298
|
+
```
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
interface TrackerConfig {
|
|
2
|
+
/**
|
|
3
|
+
* URL to POST events to.
|
|
4
|
+
* Defaults to `https://api.alphana.ir/api/events` — override only if self-hosting.
|
|
5
|
+
*/
|
|
6
|
+
endpoint?: string;
|
|
7
|
+
/**
|
|
8
|
+
* App ID obtained from the UserTracker dashboard.
|
|
9
|
+
* Sent in every request body so the backend associates events with the correct app.
|
|
10
|
+
*/
|
|
11
|
+
appId?: string;
|
|
12
|
+
/**
|
|
13
|
+
* App secret key obtained from the UserTracker dashboard.
|
|
14
|
+
* Sent as the `Authorization: Bearer` header on every request.
|
|
15
|
+
*/
|
|
16
|
+
secretKey?: string;
|
|
17
|
+
/** Provide a custom session ID; auto-generated via crypto.randomUUID if omitted. */
|
|
18
|
+
sessionId?: string;
|
|
19
|
+
/** Track SPA route changes (pushState / replaceState / popstate). Default: true */
|
|
20
|
+
trackNavigation?: boolean;
|
|
21
|
+
/** Track time spent on each page. Default: true */
|
|
22
|
+
trackTime?: boolean;
|
|
23
|
+
/** Collect mouse-move, click, and scroll data for heatmap. Default: true */
|
|
24
|
+
trackHeatmap?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Fraction of mousemove / scroll events to record (0–1).
|
|
27
|
+
* 1 = record every event, 0.3 = ~30 % sampled. Default: 0.3
|
|
28
|
+
*/
|
|
29
|
+
mouseSampleRate?: number;
|
|
30
|
+
/** Maximum heatmap points stored in memory per page. Default: 2000 */
|
|
31
|
+
maxHeatmapPoints?: number;
|
|
32
|
+
/**
|
|
33
|
+
* Number of events to accumulate before an automatic flush is triggered.
|
|
34
|
+
* Default: 20
|
|
35
|
+
*/
|
|
36
|
+
batchSize?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Milliseconds between automatic batch flushes regardless of queue size.
|
|
39
|
+
* Default: 5000 (5 s)
|
|
40
|
+
*/
|
|
41
|
+
flushInterval?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Automatically capture console.info/warn/error, window.onerror, and
|
|
44
|
+
* unhandledrejection events and send them to the backend log endpoint.
|
|
45
|
+
* Default: true (when endpoint is provided)
|
|
46
|
+
*/
|
|
47
|
+
trackLogs?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Automatically capture and send full-page screenshots to the backend
|
|
50
|
+
* every 5 minutes for use in the heatmap view.
|
|
51
|
+
* Requires `html2canvas` to be installed in the host application:
|
|
52
|
+
* npm install html2canvas
|
|
53
|
+
* Default: true (when endpoint is provided and html2canvas is installed)
|
|
54
|
+
*/
|
|
55
|
+
trackSnapshots?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Minimum milliseconds between screenshots for the same page path.
|
|
58
|
+
* Defaults to 300 000 (5 minutes). Only used when `trackSnapshots` is true.
|
|
59
|
+
*/
|
|
60
|
+
snapshotIntervalMs?: number;
|
|
61
|
+
/** Called synchronously for every emitted event. */
|
|
62
|
+
onEvent?: (event: TrackerEvent) => void;
|
|
63
|
+
}
|
|
64
|
+
interface GeoLocation {
|
|
65
|
+
/** ISO 3166-1 alpha-2 country code, e.g. "US" */
|
|
66
|
+
country: string;
|
|
67
|
+
/** Human-readable country name, e.g. "United States" */
|
|
68
|
+
countryName: string;
|
|
69
|
+
city?: string;
|
|
70
|
+
region?: string;
|
|
71
|
+
latitude?: number;
|
|
72
|
+
longitude?: number;
|
|
73
|
+
}
|
|
74
|
+
interface PageView {
|
|
75
|
+
path: string;
|
|
76
|
+
title: string;
|
|
77
|
+
timestamp: number;
|
|
78
|
+
sessionId: string;
|
|
79
|
+
referrer?: string;
|
|
80
|
+
}
|
|
81
|
+
interface TimeSpent {
|
|
82
|
+
path: string;
|
|
83
|
+
/** Duration in milliseconds */
|
|
84
|
+
duration: number;
|
|
85
|
+
sessionId: string;
|
|
86
|
+
timestamp: number;
|
|
87
|
+
}
|
|
88
|
+
interface HeatmapPoint {
|
|
89
|
+
/** X position as a percentage of the full page width (0–100) */
|
|
90
|
+
xPct: number;
|
|
91
|
+
/** Y position as a percentage of the full page height (0–100) */
|
|
92
|
+
yPct: number;
|
|
93
|
+
/** Absolute X pixel in the viewport at the time of the event */
|
|
94
|
+
x: number;
|
|
95
|
+
/** Absolute Y pixel from the top of the page (includes scroll) */
|
|
96
|
+
y: number;
|
|
97
|
+
type: "move" | "click" | "scroll";
|
|
98
|
+
path: string;
|
|
99
|
+
timestamp: number;
|
|
100
|
+
}
|
|
101
|
+
type TrackerEvent = {
|
|
102
|
+
type: "pageview";
|
|
103
|
+
data: PageView;
|
|
104
|
+
} | {
|
|
105
|
+
type: "timespent";
|
|
106
|
+
data: TimeSpent;
|
|
107
|
+
} | {
|
|
108
|
+
type: "heatmap";
|
|
109
|
+
data: HeatmapPoint;
|
|
110
|
+
};
|
|
111
|
+
interface SessionData {
|
|
112
|
+
id: string;
|
|
113
|
+
startedAt: number;
|
|
114
|
+
pageViews: PageView[];
|
|
115
|
+
/** Cumulative milliseconds per path */
|
|
116
|
+
timeSpent: Record<string, number>;
|
|
117
|
+
/** Collected points per path */
|
|
118
|
+
heatmap: Record<string, HeatmapPoint[]>;
|
|
119
|
+
/** Approximate visitor location resolved from IP (filled asynchronously) */
|
|
120
|
+
location?: GeoLocation;
|
|
121
|
+
}
|
|
122
|
+
interface HeatmapRenderOptions {
|
|
123
|
+
/** Influence radius of each point in pixels. Default: 25 */
|
|
124
|
+
radius?: number;
|
|
125
|
+
/** Maximum opacity of the hottest areas (0–1). Default: 0.85 */
|
|
126
|
+
maxOpacity?: number;
|
|
127
|
+
/** Minimum opacity for the coolest visible areas (0–1). Default: 0 */
|
|
128
|
+
minOpacity?: number;
|
|
129
|
+
}
|
|
130
|
+
declare global {
|
|
131
|
+
interface WindowEventMap {
|
|
132
|
+
"tracker:navigate": CustomEvent<{
|
|
133
|
+
path: string;
|
|
134
|
+
title: string;
|
|
135
|
+
}>;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
type LogLevel = "info" | "warn" | "error";
|
|
140
|
+
interface LogEntry {
|
|
141
|
+
sessionId?: string;
|
|
142
|
+
appId?: string;
|
|
143
|
+
level: LogLevel;
|
|
144
|
+
message: string;
|
|
145
|
+
url?: string;
|
|
146
|
+
stack?: string;
|
|
147
|
+
meta?: Record<string, unknown>;
|
|
148
|
+
timestamp: number;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Automatically captures console.info/warn/error output and unhandled errors,
|
|
152
|
+
* then ships them to the backend `/logs/ingest` endpoint.
|
|
153
|
+
*
|
|
154
|
+
* All console methods are restored exactly in `destroy()`.
|
|
155
|
+
*/
|
|
156
|
+
declare class LogCapture {
|
|
157
|
+
private readonly endpoint;
|
|
158
|
+
private readonly sessionId;
|
|
159
|
+
private readonly appId?;
|
|
160
|
+
private readonly authHeaders;
|
|
161
|
+
private origInfo;
|
|
162
|
+
private origWarn;
|
|
163
|
+
private origError;
|
|
164
|
+
private prevOnError;
|
|
165
|
+
private prevOnUnhandledRejection;
|
|
166
|
+
private initialized;
|
|
167
|
+
constructor(options: {
|
|
168
|
+
endpoint: string;
|
|
169
|
+
sessionId: string;
|
|
170
|
+
secretKey?: string;
|
|
171
|
+
appId?: string;
|
|
172
|
+
});
|
|
173
|
+
init(): void;
|
|
174
|
+
destroy(): void;
|
|
175
|
+
/** Manually capture a log entry (e.g. from try/catch). */
|
|
176
|
+
capture(level: LogLevel, message: string, extra?: {
|
|
177
|
+
stack?: string;
|
|
178
|
+
meta?: Record<string, unknown>;
|
|
179
|
+
}): void;
|
|
180
|
+
private format;
|
|
181
|
+
private send;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
declare const DEFAULT_ENDPOINT = "https://api.alphana.ir/api/events";
|
|
185
|
+
type SubscriberFn = (event: TrackerEvent) => void;
|
|
186
|
+
declare class UserTracker {
|
|
187
|
+
private readonly cfg;
|
|
188
|
+
private session;
|
|
189
|
+
private navigation?;
|
|
190
|
+
private time?;
|
|
191
|
+
private heatmap?;
|
|
192
|
+
/** Public so consumers can call logCapture.capture() for manual log entries. */
|
|
193
|
+
logCapture?: LogCapture;
|
|
194
|
+
private snapshot?;
|
|
195
|
+
private initialized;
|
|
196
|
+
private readonly subscribers;
|
|
197
|
+
/** In-memory queue of events waiting to be flushed. */
|
|
198
|
+
private queue;
|
|
199
|
+
private flushTimer;
|
|
200
|
+
private heartbeatTimer;
|
|
201
|
+
private location;
|
|
202
|
+
constructor(config?: TrackerConfig);
|
|
203
|
+
/**
|
|
204
|
+
* Attach event listeners and start tracking.
|
|
205
|
+
* Safe to call during SSR — returns `this` immediately if `window` is
|
|
206
|
+
* undefined so it can be chained: `const tracker = new UserTracker(cfg).init()`.
|
|
207
|
+
*/
|
|
208
|
+
init(): this;
|
|
209
|
+
/** Remove all event listeners, flush remaining queue, and reset state. */
|
|
210
|
+
destroy(): void;
|
|
211
|
+
private handleVisibilityChange;
|
|
212
|
+
private handlePageHide;
|
|
213
|
+
private handleNavigate;
|
|
214
|
+
/**
|
|
215
|
+
* Send a keep-alive heartbeat so the backend knows this session is still
|
|
216
|
+
* active. Called every 30 s while the tab is visible.
|
|
217
|
+
*/
|
|
218
|
+
private sendHeartbeat;
|
|
219
|
+
/**
|
|
220
|
+
* Send a synchronous beacon to mark this session as inactive.
|
|
221
|
+
* Called on page unload / tab hidden so the dashboard reflects real-time.
|
|
222
|
+
*/
|
|
223
|
+
private sendDeactivate;
|
|
224
|
+
/** Emit a tracker event. Also used internally by the plugins. */
|
|
225
|
+
emit(event: TrackerEvent): void;
|
|
226
|
+
/**
|
|
227
|
+
* Subscribe to every emitted event. Returns an unsubscribe function.
|
|
228
|
+
*
|
|
229
|
+
* ```ts
|
|
230
|
+
* const unsub = tracker.subscribe(event => console.log(event));
|
|
231
|
+
* // later…
|
|
232
|
+
* unsub();
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
235
|
+
subscribe(fn: SubscriberFn): () => void;
|
|
236
|
+
/**
|
|
237
|
+
* Manually record a page view and dispatch `tracker:navigate`.
|
|
238
|
+
* Required for Next.js App Router — call it inside a `useEffect` that
|
|
239
|
+
* depends on `usePathname()`:
|
|
240
|
+
*
|
|
241
|
+
* ```tsx
|
|
242
|
+
* const pathname = usePathname();
|
|
243
|
+
* useEffect(() => { tracker.trackPageView(pathname); }, [pathname]);
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
trackPageView(path?: string): void;
|
|
247
|
+
/** A read-only snapshot of the current session. */
|
|
248
|
+
getSession(): Readonly<SessionData>;
|
|
249
|
+
/** All page views recorded so far. */
|
|
250
|
+
getPageViews(): PageView[];
|
|
251
|
+
/** Cumulative milliseconds spent per path. */
|
|
252
|
+
getTimeSpent(): Record<string, number>;
|
|
253
|
+
/** Heatmap points for a specific path. */
|
|
254
|
+
getHeatmapData(path: string): HeatmapPoint[];
|
|
255
|
+
/** Heatmap points for all tracked paths. */
|
|
256
|
+
getHeatmapData(): Record<string, HeatmapPoint[]>;
|
|
257
|
+
/** Drain the queue and POST all pending events to the batch endpoint. */
|
|
258
|
+
private flush;
|
|
259
|
+
/**
|
|
260
|
+
* Synchronous best-effort flush via `navigator.sendBeacon`.
|
|
261
|
+
* Used on `pagehide` / `visibilitychange:hidden` where async fetch may be
|
|
262
|
+
* cancelled by the browser before it completes.
|
|
263
|
+
*/
|
|
264
|
+
private flushBeacon;
|
|
265
|
+
private buildBatchBody;
|
|
266
|
+
private sendBatch;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Renders `points` onto `canvas` as a color heatmap.
|
|
271
|
+
*
|
|
272
|
+
* Algorithm:
|
|
273
|
+
* 1. Draw each point as a soft radial gradient on an off-screen canvas,
|
|
274
|
+
* accumulating "heat" in the alpha channel.
|
|
275
|
+
* 2. Map each pixel's accumulated alpha value through the color palette
|
|
276
|
+
* (blue → cyan → green → yellow → orange → red) and write to the
|
|
277
|
+
* destination canvas.
|
|
278
|
+
*
|
|
279
|
+
* Coordinates in `HeatmapPoint` are percentages (0–100) of page dimensions,
|
|
280
|
+
* which are scaled to the canvas size at render time — making it resolution
|
|
281
|
+
* independent.
|
|
282
|
+
*
|
|
283
|
+
* @param canvas Target canvas element (will NOT be resized automatically).
|
|
284
|
+
* @param points Array of heatmap points to render.
|
|
285
|
+
* @param options Visual tuning options.
|
|
286
|
+
*/
|
|
287
|
+
declare function renderHeatmap(canvas: HTMLCanvasElement, points: HeatmapPoint[], options?: HeatmapRenderOptions): void;
|
|
288
|
+
|
|
289
|
+
export { DEFAULT_ENDPOINT, type GeoLocation, type HeatmapPoint, type HeatmapRenderOptions, LogCapture, type LogEntry, type LogLevel, type PageView, type SessionData, type TimeSpent, type TrackerConfig, type TrackerEvent, UserTracker, renderHeatmap };
|