@agentuity/analytics 3.0.0-alpha.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 +116 -0
- package/dist/beacon.d.ts +12 -0
- package/dist/beacon.d.ts.map +1 -0
- package/dist/beacon.js +300 -0
- package/dist/beacon.js.map +1 -0
- package/dist/client.d.ts +46 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +171 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +32 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +33 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +92 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +120 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +37 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +125 -0
- package/dist/util.js.map +1 -0
- package/package.json +54 -0
- package/src/beacon.ts +345 -0
- package/src/client.ts +189 -0
- package/src/config.ts +48 -0
- package/src/index.ts +124 -0
- package/src/types.ts +126 -0
- package/src/util.ts +123 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics type definitions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/** Scroll depth milestone event */
|
|
6
|
+
export interface ScrollEvent {
|
|
7
|
+
depth: number;
|
|
8
|
+
timestamp: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Custom analytics event */
|
|
12
|
+
export interface AnalyticsCustomEvent {
|
|
13
|
+
timestamp: number;
|
|
14
|
+
name: string;
|
|
15
|
+
data: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Geo location data from IP */
|
|
19
|
+
export interface GeoLocation {
|
|
20
|
+
country?: string;
|
|
21
|
+
country_latitude?: string | number;
|
|
22
|
+
country_longitude?: string | number;
|
|
23
|
+
region?: string;
|
|
24
|
+
region_latitude?: string | number;
|
|
25
|
+
region_longitude?: string | number;
|
|
26
|
+
city?: string;
|
|
27
|
+
city_latitude?: string | number;
|
|
28
|
+
city_longitude?: string | number;
|
|
29
|
+
timezone?: string;
|
|
30
|
+
latitude?: string | number;
|
|
31
|
+
longitude?: string | number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Page view data collected by the beacon */
|
|
35
|
+
export interface PageViewData {
|
|
36
|
+
id: string;
|
|
37
|
+
timestamp: number;
|
|
38
|
+
timezone_offset: number;
|
|
39
|
+
url: string;
|
|
40
|
+
path: string;
|
|
41
|
+
referrer: string;
|
|
42
|
+
title: string;
|
|
43
|
+
screen_width: number;
|
|
44
|
+
screen_height: number;
|
|
45
|
+
viewport_width: number;
|
|
46
|
+
viewport_height: number;
|
|
47
|
+
device_pixel_ratio: number;
|
|
48
|
+
user_agent: string;
|
|
49
|
+
language: string;
|
|
50
|
+
scroll_depth: number;
|
|
51
|
+
time_on_page: number;
|
|
52
|
+
scroll_events: ScrollEvent[];
|
|
53
|
+
custom_events: AnalyticsCustomEvent[];
|
|
54
|
+
load_time?: number;
|
|
55
|
+
dom_ready?: number;
|
|
56
|
+
ttfb?: number;
|
|
57
|
+
fcp?: number;
|
|
58
|
+
lcp?: number;
|
|
59
|
+
cls?: number;
|
|
60
|
+
inp?: number;
|
|
61
|
+
country?: string;
|
|
62
|
+
country_latitude?: number;
|
|
63
|
+
country_longitude?: number;
|
|
64
|
+
region?: string;
|
|
65
|
+
region_latitude?: number;
|
|
66
|
+
region_longitude?: number;
|
|
67
|
+
city?: string;
|
|
68
|
+
city_latitude?: number;
|
|
69
|
+
city_longitude?: number;
|
|
70
|
+
timezone?: string;
|
|
71
|
+
latitude?: number;
|
|
72
|
+
longitude?: number;
|
|
73
|
+
utm_source?: string;
|
|
74
|
+
utm_medium?: string;
|
|
75
|
+
utm_campaign?: string;
|
|
76
|
+
utm_term?: string;
|
|
77
|
+
utm_content?: string;
|
|
78
|
+
[key: string]: unknown;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Analytics configuration */
|
|
82
|
+
export interface AnalyticsConfig {
|
|
83
|
+
/** Enable/disable tracking */
|
|
84
|
+
enabled: boolean;
|
|
85
|
+
/** Organization ID */
|
|
86
|
+
orgId: string;
|
|
87
|
+
/** Project ID */
|
|
88
|
+
projectId: string;
|
|
89
|
+
/** Running in development mode */
|
|
90
|
+
isDevmode?: boolean;
|
|
91
|
+
/** Track clicks on [data-analytics] elements */
|
|
92
|
+
trackClicks?: boolean;
|
|
93
|
+
/** Track scroll depth */
|
|
94
|
+
trackScroll?: boolean;
|
|
95
|
+
/** Track Web Vitals (FCP, LCP, CLS, INP) */
|
|
96
|
+
trackWebVitals?: boolean;
|
|
97
|
+
/** Track JS errors */
|
|
98
|
+
trackErrors?: boolean;
|
|
99
|
+
/** Track SPA navigation */
|
|
100
|
+
trackSPANavigation?: boolean;
|
|
101
|
+
/** Sampling rate (0-1) */
|
|
102
|
+
sampleRate?: number;
|
|
103
|
+
/** Custom collect endpoint */
|
|
104
|
+
endpoint?: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Analytics client API */
|
|
108
|
+
export interface AnalyticsClient {
|
|
109
|
+
/** Track a custom event */
|
|
110
|
+
track: (name: string, properties?: Record<string, unknown>) => void;
|
|
111
|
+
/** Identify a user */
|
|
112
|
+
identify: (userId: string, traits?: Record<string, unknown>) => void;
|
|
113
|
+
/** Flush pending events */
|
|
114
|
+
flush: () => void;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Payload sent to collect endpoint */
|
|
118
|
+
export interface AnalyticsPayload {
|
|
119
|
+
org_id: string;
|
|
120
|
+
project_id: string;
|
|
121
|
+
visitor_id: string;
|
|
122
|
+
user_id: string;
|
|
123
|
+
user_traits: Record<string, string>;
|
|
124
|
+
is_devmode: boolean;
|
|
125
|
+
pageview: PageViewData;
|
|
126
|
+
}
|
package/src/util.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics utility functions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { GeoLocation } from './types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Generate a unique ID
|
|
9
|
+
*/
|
|
10
|
+
export function generateId(): string {
|
|
11
|
+
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
12
|
+
return crypto.randomUUID();
|
|
13
|
+
}
|
|
14
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Safely stringify an object, handling circular references
|
|
19
|
+
*/
|
|
20
|
+
export function safeStringify(obj: unknown): string {
|
|
21
|
+
if (obj === undefined || obj === null) {
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const seen = new WeakSet();
|
|
26
|
+
return JSON.stringify(obj, (_key, value) => {
|
|
27
|
+
if (typeof value === 'object' && value !== null) {
|
|
28
|
+
if (seen.has(value)) {
|
|
29
|
+
return '[Circular]';
|
|
30
|
+
}
|
|
31
|
+
seen.add(value);
|
|
32
|
+
}
|
|
33
|
+
return value;
|
|
34
|
+
});
|
|
35
|
+
} catch (err) {
|
|
36
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
37
|
+
console.warn('[Agentuity Analytics] Failed to stringify:', message);
|
|
38
|
+
return `[unserializable: ${message}]`;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Strip query string from URL to prevent sensitive data leakage
|
|
44
|
+
*/
|
|
45
|
+
export function stripQueryString(url: string): string {
|
|
46
|
+
if (!url) return '';
|
|
47
|
+
try {
|
|
48
|
+
const parsed = new URL(url);
|
|
49
|
+
return parsed.origin + parsed.pathname;
|
|
50
|
+
} catch {
|
|
51
|
+
return url.split('?')[0] ?? url;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get UTM parameters from URL
|
|
57
|
+
*/
|
|
58
|
+
export function getUTMParams(): Record<string, string> {
|
|
59
|
+
const params = new URLSearchParams(location.search);
|
|
60
|
+
const utm: Record<string, string> = {};
|
|
61
|
+
for (const key of ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content']) {
|
|
62
|
+
const value = params.get(key);
|
|
63
|
+
if (value) utm[key] = value;
|
|
64
|
+
}
|
|
65
|
+
return utm;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get or create visitor ID from localStorage
|
|
70
|
+
*/
|
|
71
|
+
export function getVisitorId(): string {
|
|
72
|
+
try {
|
|
73
|
+
const stored = localStorage.getItem('agentuity_visitor_id');
|
|
74
|
+
if (stored) return stored;
|
|
75
|
+
} catch {
|
|
76
|
+
// localStorage not available
|
|
77
|
+
}
|
|
78
|
+
const id = 'vid_' + generateId();
|
|
79
|
+
try {
|
|
80
|
+
localStorage.setItem('agentuity_visitor_id', id);
|
|
81
|
+
} catch {
|
|
82
|
+
// localStorage not available
|
|
83
|
+
}
|
|
84
|
+
return id;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Get cached geo location
|
|
89
|
+
*/
|
|
90
|
+
export function getCachedGeo(): GeoLocation | null {
|
|
91
|
+
try {
|
|
92
|
+
const cached = sessionStorage.getItem('agentuity_geo');
|
|
93
|
+
if (cached) return JSON.parse(cached);
|
|
94
|
+
} catch {
|
|
95
|
+
// sessionStorage not available
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Cache geo location
|
|
102
|
+
*/
|
|
103
|
+
export function setCachedGeo(geo: GeoLocation): void {
|
|
104
|
+
try {
|
|
105
|
+
sessionStorage.setItem('agentuity_geo', JSON.stringify(geo));
|
|
106
|
+
} catch {
|
|
107
|
+
// sessionStorage not available
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Fetch geo location from service
|
|
113
|
+
*/
|
|
114
|
+
export async function fetchGeo(): Promise<GeoLocation | null> {
|
|
115
|
+
try {
|
|
116
|
+
const response = await fetch('https://agentuity.sh/location');
|
|
117
|
+
const geo = await response.json();
|
|
118
|
+
setCachedGeo(geo);
|
|
119
|
+
return geo;
|
|
120
|
+
} catch {
|
|
121
|
+
return getCachedGeo();
|
|
122
|
+
}
|
|
123
|
+
}
|