top-analyze-sdk
v1.2.2
Published
SDK for TOP analytics
Maintainers
Readme
Top Analyze SDK
Automatic analytics for TG Mini Apps with minimal integration.
Quick Start
npm install top-analyze-sdkimport { TopAnalyzeSDK } from 'top-analyze-sdk';
// Initialize with required apiToken
const analytics = new TopAnalyzeSDK({
appName: 'MyTonApp',
apiToken: 'your-api-token',
});
analytics.start();The core SDK is enough for sessions, clicks, wallet, Telegram WebApp events, and custom events. Optional modules (e.g. backend request tracking) are documented at the end — see Optional modules.
What's Collected Automatically
Sessions
- Start/end session
- Duration, idle timeouts
- Page URL, referrer, User-Agent
Clicks and Taps
- Coordinates (page/client)
- DOM snapshot of target (tag, id, classes, attributes)
- Safe CSS selector and XPath
- Truncated text (with PII masking)
Telegram WebApp
Inside a Telegram Mini App, the SDK subscribes to Telegram.WebApp.onEvent and sends analytics events automatically — no extra setup.
Native Telegram UI buttons are tracked as separate events (not as regular DOM clicks):
| Event | Telegram UI |
|-------|-------------|
| backButtonClicked | Back button (←) in the header |
| settingsButtonClicked | Menu button (⋮) in the header |
| mainButtonClicked | Main bottom button |
| secondaryButtonClicked | Secondary bottom button |
Other WebApp events (popups, invoices, fullscreen, clipboard, etc.) are tracked too. Each event includes the original Telegram payload in telegram_event_data.
TON Connect
- Wallet connection request
- Connect/disconnect
- Wallet info (name, address)
Transactions
transaction_sent_for_signaturetransaction_signedtransaction_signing_failed- Amount, recipient, transaction ID
Configuration
const analytics = new TopAnalyzeSDK({
appName: 'MyTonApp',
apiToken: 'your-api-token', // Required
debug: true, // Detailed logs
onlyImportantClicks: false, // Track only important clicks (buttons, links, etc.)
});TON Connect Integration
To track TON Connect events, you need to attach your TON Connect instance using the attachTonConnect method.
With @tonconnect/ui-react
import { useEffect } from 'react';
import { useTonConnectUI } from '@tonconnect/ui-react';
import { TopAnalyzeSDK } from 'top-analyze-sdk';
// Initialize analytics
const analytics = new TopAnalyzeSDK({
appName: 'MyTonApp',
apiToken: 'your-api-token',
});
// Hook for TON Connect integration
export const useTonConnectAnalytics = () => {
const [tonConnectUI] = useTonConnectUI();
useEffect(() => {
if (!analytics || !tonConnectUI) return;
// Attach TON Connect to analytics
analytics.attachTonConnect(tonConnectUI);
}, [tonConnectUI]);
};With custom adapter
import { TopAnalyzeSDK } from 'top-analyze-sdk';
const analytics = new TopAnalyzeSDK({
appName: 'MyTonApp',
apiToken: 'your-api-token',
});
// Create adapter for compatibility
const tonConnectAdapter = {
sendTransaction: async (transaction) => {
return await tonConnectUI.sendTransaction(transaction);
},
connect: async () => {
return tonConnectUI.connect();
},
disconnect: async () => {
await tonConnectUI.disconnect();
},
onStatusChange: (callback) => {
tonConnectUI.onStatusChange(callback);
},
wallet: tonConnectUI.wallet,
};
// Attach TON Connect to analytics
analytics.attachTonConnect(tonConnectAdapter);Custom Events
// Game events
analytics.track('game_start', { level: 1, mode: 'normal' });
analytics.track('game_score', { score: 1500, level: 3 });
analytics.track('game_end', { score: 1500, duration: 120 });
// Business events
analytics.track('purchase_intent', { item: 'premium', price: '0.1' });
analytics.track('feature_used', { feature: 'dark_mode' });Getting Information
// Session info
const sessionInfo = analytics.getSessionInfo();
console.log(sessionInfo.sessionId, sessionInfo.duration);
// Wallet info
const walletInfo = analytics.getTonWalletInfo();
console.log(walletInfo?.address, walletInfo?.name);
// SDK status
const status = analytics.getStatus();
console.log(status.isStarted, status.isTonConnectAttached);Lifecycle Management
const analytics = new TopAnalyzeSDK({
appName: 'MyTonApp',
apiToken: 'your-api-token',
});
analytics.start(); // Start tracking
analytics.stop(); // Stop (with sending remaining events)Event Structure
All events have a unified structure:
interface AnalyticsEvent {
type: EventType; // Event type
timestamp: number; // Unix timestamp
sessionId: string; // Session ID
appName: string; // App name
data: EventData; // Specific data
}Optional modules
Everything above is the core SDK — sessions, clicks, Telegram WebApp events, TON Connect, custom events. It works on its own; no extra packages or subpath imports required.
Optional modules extend the SDK with additional behaviour. Each module:
- is imported from a separate subpath (e.g.
top-analyze-sdk/request-interceptor); - is not enabled by default — you opt in explicitly;
- connects via
analytics.useModule(create…Module(config)); - returns
detach()— call it on SPA unmount to restore patched globals; - sends events through the same batch queue as core analytics (when
analytics.start()is active).
import { TopAnalyzeSDK } from 'top-analyze-sdk';
// Optional — only if you need this module:
import { createRequestInterceptorModule } from 'top-analyze-sdk/request-interceptor';
const analytics = new TopAnalyzeSDK({ appName: 'MyTonApp', apiToken: '…' });
const detach = analytics.useModule(createRequestInterceptorModule({ /* config */ }));
analytics.start();
// on unmount:
detach();Modules can be attached before or after start(). Patching (fetch, XMLHttpRequest, …) happens on useModule(); events are queued only while the SDK is started.
Request Interceptor Module
Package: top-analyze-sdk/request-interceptor
Tracks product backend requests (REST/GraphQL) inside TMA activity: endpoint, method, request params, response time, HTTP status. Static assets, CDN, telemetry, and SDK analytics traffic are not tracked.
Quick start
import { createRequestInterceptorModule } from 'top-analyze-sdk/request-interceptor';
analytics.useModule(
createRequestInterceptorModule({
apiBaseUrl: import.meta.env.VITE_API_URL,
})
);createRequestInterceptorModule(config)
| Option | Type | Required | Description |
|--------|------|----------|-------------|
| apiBaseUrl | string | No* | Sugar: adds includeOrigins: [new URL(apiBaseUrl).origin]. Handy when API lives on a dedicated domain. |
| includeOrigins | string[] | No* | Whitelist of backend origins, e.g. ['https://api.myapp.com']. All paths on these origins are candidates for tracking. |
| includePathPrefixes | string[] | No* | Whitelist of path prefixes on the current page origin, e.g. ['/api/', '/graphql']. Use for same-origin BFF. |
| excludePathPrefixes | string[] | No | Extra path prefixes to skip (applied after built-in static excludes). |
| excludeOrigins | string[] | No | Extra origins to skip. |
| shouldTrack | (url, method) => boolean | No | Final per-request filter — see below. |
* Deny-by-default: at least one of apiBaseUrl, includeOrigins, or includePathPrefixes must be set. Otherwise the module attaches but tracks nothing (warning in debug mode).
Options can be combined, e.g. apiBaseUrl + includePathPrefixes for API on a separate domain and GraphQL on the app origin.
How request filtering works
A request is tracked only if all checks pass (in order):
- Hard exclude (built-in, not configurable) — always skipped:
- SDK analytics endpoint (
analyze.playdeck.io); - static extensions (
.js,.css,.map, images, fonts, video, …); - common static paths (
/_next/,/static/,/assets/, …).
- SDK analytics endpoint (
- Whitelist — URL must match
includeOriginsorincludePathPrefixes. - Config exclude — not in
excludeOrigins/excludePathPrefixes. shouldTrack(url, method)— if provided, must returntrue.
shouldTrack runs last, only for requests that already passed whitelist and excludes. Use it for edge cases that are awkward to express with path/origin lists:
createRequestInterceptorModule({
includePathPrefixes: ['/api/', '/graphql'],
// Skip GraphQL introspection and healthchecks
shouldTrack: (url, method) => {
if (url.pathname.startsWith('/graphql/introspection')) return false;
if (url.pathname === '/api/health') return false;
return true;
},
});If shouldTrack is omitted, every whitelisted non-excluded request is tracked.
Configuration examples
// API on a separate domain (most common)
createRequestInterceptorModule({
apiBaseUrl: import.meta.env.VITE_API_URL,
});
// Same-origin BFF
createRequestInterceptorModule({
includePathPrefixes: ['/api/', '/graphql'],
});
// Full control
createRequestInterceptorModule({
includeOrigins: ['https://api.myapp.com'],
includePathPrefixes: ['/graphql'],
excludePathPrefixes: ['/graphql/introspection'],
shouldTrack: (url) => !url.pathname.includes('/internal/'),
});intercepted_request event
Each tracked fetch / XMLHttpRequest call emits one intercepted_request event (category NETWORK).
| Field | Description |
|-------|-------------|
| method | HTTP method |
| endpoint | URL pathname only, e.g. /graphql |
| request_params | POST JSON body or GET query params as-is; null for non-JSON body |
| page_path | Page URL at request time |
| duration_ms | Round-trip ms (performance.now() before/after the call) |
| status_code | HTTP status or null on network error |
| is_success | true if status 2xx/3xx |
| error_code | Status string, network_error, or aborted |
GraphQL operationName is available inside request_params when the client sends it.
Contact
Telegram: @Ektomorphine
