@agentuity/frontend 0.1.14 → 0.1.16

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.
Files changed (82) hide show
  1. package/dist/analytics/beacon-standalone.d.ts +106 -0
  2. package/dist/analytics/beacon-standalone.d.ts.map +1 -0
  3. package/dist/analytics/beacon-standalone.js +584 -0
  4. package/dist/analytics/beacon-standalone.js.map +1 -0
  5. package/dist/analytics/index.d.ts +22 -0
  6. package/dist/analytics/index.d.ts.map +1 -0
  7. package/dist/analytics/index.js +28 -0
  8. package/dist/analytics/index.js.map +1 -0
  9. package/dist/analytics/types.d.ts +141 -0
  10. package/dist/analytics/types.d.ts.map +1 -0
  11. package/dist/analytics/types.js +2 -0
  12. package/dist/analytics/types.js.map +1 -0
  13. package/dist/analytics/utils/storage.d.ts +13 -0
  14. package/dist/analytics/utils/storage.d.ts.map +1 -0
  15. package/dist/analytics/utils/storage.js +63 -0
  16. package/dist/analytics/utils/storage.js.map +1 -0
  17. package/dist/analytics/utils/utm.d.ts +12 -0
  18. package/dist/analytics/utils/utm.d.ts.map +1 -0
  19. package/dist/analytics/utils/utm.js +27 -0
  20. package/dist/analytics/utils/utm.js.map +1 -0
  21. package/dist/beacon-script.d.ts +16 -0
  22. package/dist/beacon-script.d.ts.map +1 -0
  23. package/dist/beacon-script.js +1 -1
  24. package/dist/beacon-script.js.map +1 -0
  25. package/dist/beacon.js +1 -1
  26. package/dist/client/eventstream.d.ts +12 -0
  27. package/dist/client/eventstream.d.ts.map +1 -0
  28. package/dist/client/eventstream.js +39 -0
  29. package/dist/client/eventstream.js.map +1 -0
  30. package/dist/client/index.d.ts +33 -0
  31. package/dist/client/index.d.ts.map +1 -0
  32. package/dist/client/index.js +227 -0
  33. package/dist/client/index.js.map +1 -0
  34. package/dist/client/stream.d.ts +6 -0
  35. package/dist/client/stream.d.ts.map +1 -0
  36. package/dist/client/stream.js +49 -0
  37. package/dist/client/stream.js.map +1 -0
  38. package/dist/client/types.d.ts +168 -0
  39. package/dist/client/types.d.ts.map +1 -0
  40. package/dist/client/types.js +5 -0
  41. package/dist/client/types.js.map +1 -0
  42. package/dist/client/websocket.d.ts +6 -0
  43. package/dist/client/websocket.d.ts.map +1 -0
  44. package/dist/client/websocket.js +49 -0
  45. package/dist/client/websocket.js.map +1 -0
  46. package/dist/env.d.ts +2 -0
  47. package/dist/env.d.ts.map +1 -0
  48. package/dist/env.js +10 -0
  49. package/dist/env.js.map +1 -0
  50. package/dist/eventstream-manager.d.ts +85 -0
  51. package/dist/eventstream-manager.d.ts.map +1 -0
  52. package/dist/eventstream-manager.js +137 -0
  53. package/dist/eventstream-manager.js.map +1 -0
  54. package/dist/index.d.ts +13 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +15 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/memo.d.ts +6 -0
  59. package/dist/memo.d.ts.map +1 -0
  60. package/dist/memo.js +20 -0
  61. package/dist/memo.js.map +1 -0
  62. package/dist/reconnect.d.ts +22 -0
  63. package/dist/reconnect.d.ts.map +1 -0
  64. package/dist/reconnect.js +47 -0
  65. package/dist/reconnect.js.map +1 -0
  66. package/dist/serialization.d.ts +6 -0
  67. package/dist/serialization.d.ts.map +1 -0
  68. package/dist/serialization.js +16 -0
  69. package/dist/serialization.js.map +1 -0
  70. package/dist/types.d.ts +42 -0
  71. package/dist/types.d.ts.map +1 -0
  72. package/dist/types.js +2 -0
  73. package/dist/types.js.map +1 -0
  74. package/dist/url.d.ts +3 -0
  75. package/dist/url.d.ts.map +1 -0
  76. package/dist/url.js +24 -0
  77. package/dist/url.js.map +1 -0
  78. package/dist/websocket-manager.d.ts +90 -0
  79. package/dist/websocket-manager.d.ts.map +1 -0
  80. package/dist/websocket-manager.js +163 -0
  81. package/dist/websocket-manager.js.map +1 -0
  82. package/package.json +4 -4
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Standalone beacon script - this file is bundled and minified
3
+ * to create the production analytics.js served at /_agentuity/webanalytics/analytics.js
4
+ *
5
+ * This is the single source of truth for the beacon logic.
6
+ */
7
+ interface ScrollEvent {
8
+ depth: number;
9
+ timestamp: number;
10
+ }
11
+ interface AnalyticsCustomEvent {
12
+ timestamp: number;
13
+ name: string;
14
+ data: string;
15
+ }
16
+ interface GeoLocation {
17
+ country?: string;
18
+ country_latitude?: string | number;
19
+ country_longitude?: string | number;
20
+ region?: string;
21
+ region_latitude?: string | number;
22
+ region_longitude?: string | number;
23
+ city?: string;
24
+ city_latitude?: string | number;
25
+ city_longitude?: string | number;
26
+ timezone?: string;
27
+ latitude?: string | number;
28
+ longitude?: string | number;
29
+ }
30
+ interface PageViewData {
31
+ id: string;
32
+ timestamp: number;
33
+ timezone_offset: number;
34
+ url: string;
35
+ path: string;
36
+ referrer: string;
37
+ title: string;
38
+ screen_width: number;
39
+ screen_height: number;
40
+ viewport_width: number;
41
+ viewport_height: number;
42
+ device_pixel_ratio: number;
43
+ user_agent: string;
44
+ language: string;
45
+ scroll_depth: number;
46
+ time_on_page: number;
47
+ scroll_events: ScrollEvent[];
48
+ custom_events: AnalyticsCustomEvent[];
49
+ load_time?: number;
50
+ dom_ready?: number;
51
+ ttfb?: number;
52
+ fcp?: number;
53
+ lcp?: number;
54
+ cls?: number;
55
+ inp?: number;
56
+ country?: string;
57
+ country_latitude?: number;
58
+ country_longitude?: number;
59
+ region?: string;
60
+ region_latitude?: number;
61
+ region_longitude?: number;
62
+ city?: string;
63
+ city_latitude?: number;
64
+ city_longitude?: number;
65
+ timezone?: string;
66
+ latitude?: number;
67
+ longitude?: number;
68
+ utm_source?: string;
69
+ utm_medium?: string;
70
+ utm_campaign?: string;
71
+ utm_term?: string;
72
+ utm_content?: string;
73
+ [key: string]: unknown;
74
+ }
75
+ interface AnalyticsConfig {
76
+ enabled: boolean;
77
+ orgId: string;
78
+ projectId: string;
79
+ isDevmode: boolean;
80
+ trackClicks?: boolean;
81
+ trackScroll?: boolean;
82
+ trackWebVitals?: boolean;
83
+ trackErrors?: boolean;
84
+ trackSPANavigation?: boolean;
85
+ sampleRate?: number;
86
+ }
87
+ interface SessionData {
88
+ threadId?: string;
89
+ }
90
+ interface AgentuityWindow {
91
+ __AGENTUITY_ANALYTICS__?: AnalyticsConfig;
92
+ __AGENTUITY_SESSION__?: SessionData;
93
+ agentuityAnalytics?: {
94
+ track: (name: string, properties?: Record<string, unknown>) => void;
95
+ identify: (userId: string, traits?: Record<string, unknown>) => void;
96
+ flush: () => void;
97
+ };
98
+ }
99
+ declare const COLLECT_ENDPOINT = "/_agentuity/webanalytics/collect";
100
+ declare const MAX_CUSTOM_EVENTS = 1000;
101
+ /**
102
+ * Safely stringify an object, handling circular references and other errors.
103
+ * Returns the JSON string on success, or a fallback string on failure.
104
+ */
105
+ declare function safeStringify(obj: unknown): string;
106
+ //# sourceMappingURL=beacon-standalone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"beacon-standalone.d.ts","sourceRoot":"","sources":["../../src/analytics/beacon-standalone.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,584 @@
1
+ "use strict";
2
+ /**
3
+ * Standalone beacon script - this file is bundled and minified
4
+ * to create the production analytics.js served at /_agentuity/webanalytics/analytics.js
5
+ *
6
+ * This is the single source of truth for the beacon logic.
7
+ */
8
+ const COLLECT_ENDPOINT = '/_agentuity/webanalytics/collect';
9
+ const MAX_CUSTOM_EVENTS = 1000;
10
+ /**
11
+ * Safely stringify an object, handling circular references and other errors.
12
+ * Returns the JSON string on success, or a fallback string on failure.
13
+ */
14
+ function safeStringify(obj) {
15
+ if (obj === undefined || obj === null) {
16
+ return '';
17
+ }
18
+ try {
19
+ const seen = new WeakSet();
20
+ return JSON.stringify(obj, (_key, value) => {
21
+ if (typeof value === 'object' && value !== null) {
22
+ if (seen.has(value)) {
23
+ return '[Circular]';
24
+ }
25
+ seen.add(value);
26
+ }
27
+ return value;
28
+ });
29
+ }
30
+ catch (err) {
31
+ const message = err instanceof Error ? err.message : String(err);
32
+ console.warn('[Agentuity Analytics] Failed to stringify properties:', message);
33
+ return `[unserializable: ${message}]`;
34
+ }
35
+ }
36
+ (function () {
37
+ const w = window;
38
+ const d = document;
39
+ const configRaw = w.__AGENTUITY_ANALYTICS__;
40
+ if (!configRaw || !configRaw.enabled)
41
+ return;
42
+ // Prevent duplicate initialization (e.g., from HMR)
43
+ const initFlag = w;
44
+ if (configRaw.isDevmode) {
45
+ console.debug('[Agentuity Analytics] Script loaded, init flag:', initFlag.__AGENTUITY_BEACON_INIT__, 'path:', location.pathname);
46
+ }
47
+ if (initFlag.__AGENTUITY_BEACON_INIT__) {
48
+ if (configRaw.isDevmode) {
49
+ console.debug('[Agentuity Analytics] Already initialized, skipping');
50
+ }
51
+ return;
52
+ }
53
+ initFlag.__AGENTUITY_BEACON_INIT__ = true;
54
+ // Store in a non-nullable variable after the guard
55
+ const c = configRaw;
56
+ let geo = null;
57
+ let sent = false;
58
+ let pageStart = Date.now();
59
+ let userId = '';
60
+ let userTraits = {};
61
+ const pv = {
62
+ id: '',
63
+ timestamp: 0,
64
+ timezone_offset: 0,
65
+ url: '',
66
+ path: '',
67
+ referrer: '',
68
+ title: '',
69
+ screen_width: 0,
70
+ screen_height: 0,
71
+ viewport_width: 0,
72
+ viewport_height: 0,
73
+ device_pixel_ratio: 1,
74
+ user_agent: '',
75
+ language: '',
76
+ scroll_depth: 0,
77
+ time_on_page: 0,
78
+ scroll_events: [],
79
+ custom_events: [],
80
+ };
81
+ function generateId() {
82
+ return crypto.randomUUID
83
+ ? crypto.randomUUID()
84
+ : `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
85
+ }
86
+ function getUTMParams() {
87
+ const params = new URLSearchParams(location.search);
88
+ const utm = {};
89
+ ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'].forEach((key) => {
90
+ const value = params.get(key);
91
+ if (value)
92
+ utm[key] = value;
93
+ });
94
+ return utm;
95
+ }
96
+ // Strip query string from URL to prevent sensitive data leakage
97
+ function stripQueryString(url) {
98
+ if (!url)
99
+ return '';
100
+ try {
101
+ const parsed = new URL(url);
102
+ return parsed.origin + parsed.pathname;
103
+ }
104
+ catch {
105
+ // If URL parsing fails, try simple string split
106
+ return url.split('?')[0];
107
+ }
108
+ }
109
+ // Full init - called on page load and SPA navigation
110
+ function init() {
111
+ pv.id = generateId();
112
+ pv.timestamp = Date.now();
113
+ pv.timezone_offset = new Date().getTimezoneOffset();
114
+ pv.url = stripQueryString(location.href);
115
+ pv.path = location.pathname;
116
+ pv.referrer = stripQueryString(d.referrer);
117
+ pv.title = d.title || '';
118
+ pv.screen_width = screen.width || 0;
119
+ pv.screen_height = screen.height || 0;
120
+ pv.viewport_width = innerWidth || 0;
121
+ pv.viewport_height = innerHeight || 0;
122
+ pv.device_pixel_ratio = devicePixelRatio || 1;
123
+ pv.user_agent = navigator.userAgent || '';
124
+ pv.language = navigator.language || '';
125
+ const utm = getUTMParams();
126
+ for (const k in utm) {
127
+ pv[k] = utm[k];
128
+ }
129
+ pv.scroll_events = [];
130
+ pv.custom_events = [];
131
+ pv.scroll_depth = 0;
132
+ sent = false;
133
+ pageStart = Date.now();
134
+ if (typeof performance !== 'undefined' && performance.getEntriesByType) {
135
+ const nav = performance.getEntriesByType('navigation')[0];
136
+ if (nav) {
137
+ pv.dom_ready = Math.round(nav.domContentLoadedEventEnd - nav.startTime);
138
+ pv.ttfb = Math.round(nav.responseStart - nav.requestStart);
139
+ // loadEventEnd is 0 during the load event, so defer reading it
140
+ if (nav.loadEventEnd > 0) {
141
+ pv.load_time = Math.round(nav.loadEventEnd - nav.startTime);
142
+ }
143
+ else {
144
+ setTimeout(() => {
145
+ const navAfter = performance.getEntriesByType('navigation')[0];
146
+ if (navAfter && navAfter.loadEventEnd > 0) {
147
+ pv.load_time = Math.round(navAfter.loadEventEnd - navAfter.startTime);
148
+ }
149
+ }, 0);
150
+ }
151
+ }
152
+ }
153
+ if (c.isDevmode) {
154
+ console.debug('[Agentuity Analytics] Session started (full init):', pv.id);
155
+ }
156
+ }
157
+ // Soft reset - called when user returns to page (keeps page-level metrics)
158
+ function resetSession() {
159
+ pv.id = generateId();
160
+ pv.timestamp = Date.now();
161
+ pv.scroll_events = [];
162
+ pv.custom_events = [];
163
+ pv.scroll_depth = 0;
164
+ pv.time_on_page = 0;
165
+ sent = false;
166
+ pageStart = Date.now();
167
+ if (c.isDevmode) {
168
+ console.debug('[Agentuity Analytics] Session started (soft reset):', pv.id);
169
+ }
170
+ }
171
+ // Fetch geo data
172
+ fetch('https://agentuity.sh/location')
173
+ .then((r) => r.json())
174
+ .then((g) => {
175
+ geo = g;
176
+ try {
177
+ sessionStorage.setItem('agentuity_geo', JSON.stringify(g));
178
+ }
179
+ catch {
180
+ // Ignore
181
+ }
182
+ })
183
+ .catch(() => {
184
+ try {
185
+ const cached = sessionStorage.getItem('agentuity_geo');
186
+ if (cached)
187
+ geo = JSON.parse(cached);
188
+ }
189
+ catch {
190
+ // Ignore
191
+ }
192
+ });
193
+ // Try to load cached geo immediately
194
+ try {
195
+ const cached = sessionStorage.getItem('agentuity_geo');
196
+ if (cached)
197
+ geo = JSON.parse(cached);
198
+ }
199
+ catch {
200
+ // Ignore
201
+ }
202
+ function getSession() {
203
+ return w.__AGENTUITY_SESSION__;
204
+ }
205
+ function send(force = false) {
206
+ if (sent && !force) {
207
+ if (c.isDevmode) {
208
+ console.debug('[Agentuity Analytics] send() skipped - already sent');
209
+ }
210
+ return;
211
+ }
212
+ if (c.sampleRate !== undefined && c.sampleRate < 1 && Math.random() > c.sampleRate)
213
+ return;
214
+ sent = true;
215
+ pv.time_on_page = Date.now() - pageStart;
216
+ if (geo) {
217
+ pv.country = geo.country || '';
218
+ if (geo.country_latitude)
219
+ pv.country_latitude = parseFloat(String(geo.country_latitude));
220
+ if (geo.country_longitude)
221
+ pv.country_longitude = parseFloat(String(geo.country_longitude));
222
+ pv.region = geo.region || '';
223
+ if (geo.region_latitude)
224
+ pv.region_latitude = parseFloat(String(geo.region_latitude));
225
+ if (geo.region_longitude)
226
+ pv.region_longitude = parseFloat(String(geo.region_longitude));
227
+ pv.city = geo.city || '';
228
+ if (geo.city_latitude)
229
+ pv.city_latitude = parseFloat(String(geo.city_latitude));
230
+ if (geo.city_longitude)
231
+ pv.city_longitude = parseFloat(String(geo.city_longitude));
232
+ pv.timezone = geo.timezone || '';
233
+ if (geo.latitude)
234
+ pv.latitude = parseFloat(String(geo.latitude));
235
+ if (geo.longitude)
236
+ pv.longitude = parseFloat(String(geo.longitude));
237
+ }
238
+ if (pv.cls) {
239
+ pv.cls = Math.round(pv.cls * 1000) / 1000;
240
+ }
241
+ const s = getSession();
242
+ const vid = localStorage.getItem('agentuity_visitor_id') || 'vid_' + generateId();
243
+ try {
244
+ localStorage.setItem('agentuity_visitor_id', vid);
245
+ }
246
+ catch {
247
+ // Ignore
248
+ }
249
+ const payload = {
250
+ org_id: c.orgId,
251
+ project_id: c.projectId,
252
+ thread_id: s?.threadId || '',
253
+ visitor_id: vid,
254
+ user_id: userId,
255
+ user_traits: userTraits,
256
+ is_devmode: c.isDevmode,
257
+ pageview: pv,
258
+ };
259
+ // Clear pending data since we're sending now
260
+ try {
261
+ sessionStorage.removeItem('agentuity_pending_pageview');
262
+ }
263
+ catch {
264
+ // Storage may be unavailable
265
+ }
266
+ if (c.isDevmode) {
267
+ console.debug('[Agentuity Analytics]', JSON.stringify(payload, null, 2));
268
+ return;
269
+ }
270
+ const body = JSON.stringify(payload);
271
+ if (navigator.sendBeacon) {
272
+ navigator.sendBeacon(COLLECT_ENDPOINT, body);
273
+ }
274
+ else {
275
+ fetch(COLLECT_ENDPOINT, {
276
+ method: 'POST',
277
+ body,
278
+ keepalive: true,
279
+ }).catch(() => {
280
+ // Silent failure
281
+ });
282
+ }
283
+ }
284
+ // Send on page hide, reset session on page visible
285
+ d.addEventListener('visibilitychange', () => {
286
+ if (c.isDevmode) {
287
+ console.debug('[Agentuity Analytics] visibilitychange:', d.visibilityState, 'sent:', sent);
288
+ }
289
+ if (d.visibilityState === 'hidden') {
290
+ send();
291
+ }
292
+ else if (d.visibilityState === 'visible') {
293
+ // User returned to the page - start a new attention session
294
+ // Keep page-level metrics (url, geo, vitals) but reset session-level metrics
295
+ resetSession();
296
+ }
297
+ });
298
+ w.addEventListener('pagehide', () => {
299
+ if (c.isDevmode) {
300
+ console.debug('[Agentuity Analytics] pagehide event');
301
+ try {
302
+ sessionStorage.setItem('agentuity_last_event', `pagehide:${Date.now()}:${pv.path}`);
303
+ }
304
+ catch {
305
+ // Storage may be unavailable
306
+ }
307
+ }
308
+ send();
309
+ });
310
+ // Catch hard navigations (URL change, link click to new page, refresh)
311
+ w.addEventListener('beforeunload', () => {
312
+ if (c.isDevmode) {
313
+ console.debug('[Agentuity Analytics] beforeunload event');
314
+ try {
315
+ sessionStorage.setItem('agentuity_last_event', `beforeunload:${Date.now()}:${pv.path}`);
316
+ }
317
+ catch {
318
+ // Storage may be unavailable
319
+ }
320
+ }
321
+ send();
322
+ });
323
+ // In devmode, check if previous page sent data (helps verify unload events work)
324
+ if (c.isDevmode) {
325
+ try {
326
+ const lastEvent = sessionStorage.getItem('agentuity_last_event');
327
+ if (lastEvent) {
328
+ console.debug('[Agentuity Analytics] Previous page event:', lastEvent);
329
+ sessionStorage.removeItem('agentuity_last_event');
330
+ }
331
+ }
332
+ catch {
333
+ // Storage may be unavailable
334
+ }
335
+ }
336
+ // Fallback: check for unsent data from previous page (in case unload events didn't fire)
337
+ try {
338
+ const pendingData = sessionStorage.getItem('agentuity_pending_pageview');
339
+ if (pendingData) {
340
+ sessionStorage.removeItem('agentuity_pending_pageview');
341
+ const pending = JSON.parse(pendingData);
342
+ // Only send if it's from a different page
343
+ if (pending.pageview?.path !== location.pathname) {
344
+ if (c.isDevmode) {
345
+ console.debug('[Agentuity Analytics] Sending unsent data from previous page:', pending.pageview?.path);
346
+ console.debug('[Agentuity Analytics]', JSON.stringify(pending, null, 2));
347
+ }
348
+ else {
349
+ const body = JSON.stringify(pending);
350
+ if (navigator.sendBeacon) {
351
+ navigator.sendBeacon(COLLECT_ENDPOINT, body);
352
+ }
353
+ }
354
+ }
355
+ }
356
+ }
357
+ catch {
358
+ // Storage or JSON parsing may fail
359
+ }
360
+ // Store current pageview data periodically so it can be recovered if unload events don't fire
361
+ function savePendingData() {
362
+ try {
363
+ pv.time_on_page = Date.now() - pageStart;
364
+ const s = getSession();
365
+ const vid = localStorage.getItem('agentuity_visitor_id') || 'vid_' + generateId();
366
+ const payload = {
367
+ org_id: c.orgId,
368
+ project_id: c.projectId,
369
+ thread_id: s?.threadId || '',
370
+ visitor_id: vid,
371
+ user_id: userId,
372
+ user_traits: userTraits,
373
+ is_devmode: c.isDevmode,
374
+ pageview: { ...pv },
375
+ };
376
+ sessionStorage.setItem('agentuity_pending_pageview', JSON.stringify(payload));
377
+ }
378
+ catch {
379
+ // Storage may be unavailable
380
+ }
381
+ }
382
+ // Save pending data every 2 seconds
383
+ setInterval(savePendingData, 2000);
384
+ // Also save on any interaction
385
+ d.addEventListener('click', savePendingData, { passive: true });
386
+ d.addEventListener('scroll', savePendingData, { passive: true, once: true });
387
+ if (c.isDevmode) {
388
+ console.debug('[Agentuity Analytics] Beacon initialized, visibility:', d.visibilityState);
389
+ }
390
+ // Scroll tracking
391
+ if (c.trackScroll !== false) {
392
+ const scrolled = new Set();
393
+ function getScrollDepth() {
394
+ const st = w.scrollY || d.documentElement.scrollTop;
395
+ const sh = d.documentElement.scrollHeight - d.documentElement.clientHeight;
396
+ return sh <= 0 ? 100 : Math.min(100, Math.round((st / sh) * 100));
397
+ }
398
+ w.addEventListener('scroll', () => {
399
+ const dp = getScrollDepth();
400
+ if (dp > pv.scroll_depth)
401
+ pv.scroll_depth = dp;
402
+ [25, 50, 75, 100].forEach((m) => {
403
+ if (dp >= m && !scrolled.has(m)) {
404
+ scrolled.add(m);
405
+ pv.scroll_events.push({
406
+ depth: m,
407
+ timestamp: Date.now() - pageStart,
408
+ });
409
+ }
410
+ });
411
+ }, { passive: true });
412
+ }
413
+ // Web Vitals tracking
414
+ if (c.trackWebVitals !== false && typeof PerformanceObserver !== 'undefined') {
415
+ // FCP
416
+ try {
417
+ const fcpObs = new PerformanceObserver((list) => {
418
+ list.getEntries().forEach((entry) => {
419
+ if (entry.name === 'first-contentful-paint') {
420
+ pv.fcp = Math.round(entry.startTime);
421
+ fcpObs.disconnect();
422
+ }
423
+ });
424
+ });
425
+ fcpObs.observe({ type: 'paint', buffered: true });
426
+ }
427
+ catch {
428
+ // Not supported
429
+ }
430
+ // LCP
431
+ try {
432
+ new PerformanceObserver((list) => {
433
+ const entries = list.getEntries();
434
+ if (entries.length) {
435
+ pv.lcp = Math.round(entries[entries.length - 1].startTime);
436
+ }
437
+ }).observe({ type: 'largest-contentful-paint', buffered: true });
438
+ }
439
+ catch {
440
+ // Not supported
441
+ }
442
+ // CLS
443
+ try {
444
+ new PerformanceObserver((list) => {
445
+ list.getEntries().forEach((entry) => {
446
+ const layoutShift = entry;
447
+ if (!layoutShift.hadRecentInput && layoutShift.value) {
448
+ pv.cls = (pv.cls || 0) + layoutShift.value;
449
+ }
450
+ });
451
+ }).observe({ type: 'layout-shift', buffered: true });
452
+ }
453
+ catch {
454
+ // Not supported
455
+ }
456
+ // INP
457
+ try {
458
+ new PerformanceObserver((list) => {
459
+ list.getEntries().forEach((entry) => {
460
+ const eventEntry = entry;
461
+ if (eventEntry.duration && eventEntry.duration > (pv.inp || 0)) {
462
+ pv.inp = Math.round(eventEntry.duration);
463
+ }
464
+ });
465
+ }).observe({ type: 'event', buffered: true });
466
+ }
467
+ catch {
468
+ // Not supported
469
+ }
470
+ }
471
+ // SPA navigation tracking
472
+ if (c.trackSPANavigation !== false) {
473
+ const origPush = history.pushState;
474
+ const origReplace = history.replaceState;
475
+ let currentPath = location.pathname + location.search;
476
+ let lastHref = location.href;
477
+ if (c.isDevmode) {
478
+ console.debug('[Agentuity Analytics] SPA tracking enabled, initial path:', currentPath);
479
+ }
480
+ function handleNav() {
481
+ const newPath = location.pathname + location.search;
482
+ if (newPath !== currentPath) {
483
+ if (c.isDevmode) {
484
+ console.debug('[Agentuity Analytics] SPA navigation:', currentPath, '->', newPath);
485
+ }
486
+ send(true); // Force send on SPA navigation
487
+ currentPath = newPath;
488
+ lastHref = location.href;
489
+ init();
490
+ }
491
+ }
492
+ history.pushState = function (...args) {
493
+ origPush.apply(this, args);
494
+ setTimeout(handleNav, 0);
495
+ };
496
+ history.replaceState = function (...args) {
497
+ origReplace.apply(this, args);
498
+ setTimeout(handleNav, 0);
499
+ };
500
+ w.addEventListener('popstate', handleNav);
501
+ // Fallback: poll for URL changes in case router bypasses history API
502
+ setInterval(() => {
503
+ if (location.href !== lastHref) {
504
+ lastHref = location.href;
505
+ handleNav();
506
+ }
507
+ }, 200);
508
+ }
509
+ // Click tracking
510
+ if (c.trackClicks !== false) {
511
+ d.addEventListener('click', (e) => {
512
+ const target = e.target;
513
+ if (!target)
514
+ return;
515
+ const analyticsEl = target.closest('[data-analytics]');
516
+ if (!analyticsEl)
517
+ return;
518
+ if (pv.custom_events.length < MAX_CUSTOM_EVENTS) {
519
+ pv.custom_events.push({
520
+ timestamp: Date.now(),
521
+ name: 'click:' + analyticsEl.getAttribute('data-analytics'),
522
+ data: '',
523
+ });
524
+ }
525
+ }, true);
526
+ }
527
+ // Error tracking
528
+ if (c.trackErrors !== false) {
529
+ w.addEventListener('error', (e) => {
530
+ if (pv.custom_events.length < MAX_CUSTOM_EVENTS) {
531
+ pv.custom_events.push({
532
+ timestamp: Date.now(),
533
+ name: 'error:js_error',
534
+ data: JSON.stringify({
535
+ message: e.message || 'Unknown',
536
+ filename: e.filename || '',
537
+ lineno: e.lineno || 0,
538
+ }),
539
+ });
540
+ }
541
+ });
542
+ w.addEventListener('unhandledrejection', (e) => {
543
+ if (pv.custom_events.length < MAX_CUSTOM_EVENTS) {
544
+ pv.custom_events.push({
545
+ timestamp: Date.now(),
546
+ name: 'error:unhandled_rejection',
547
+ data: JSON.stringify({
548
+ message: e.reason instanceof Error ? e.reason.message : String(e.reason),
549
+ }),
550
+ });
551
+ }
552
+ });
553
+ }
554
+ // Initialize on load
555
+ if (d.readyState === 'complete') {
556
+ init();
557
+ }
558
+ else {
559
+ w.addEventListener('load', init);
560
+ }
561
+ // Public API
562
+ w.agentuityAnalytics = {
563
+ track(name, properties) {
564
+ if (pv.custom_events.length < MAX_CUSTOM_EVENTS) {
565
+ pv.custom_events.push({
566
+ timestamp: Date.now(),
567
+ name,
568
+ data: safeStringify(properties),
569
+ });
570
+ }
571
+ },
572
+ identify(id, traits) {
573
+ userId = id;
574
+ if (traits) {
575
+ userTraits = {};
576
+ for (const [key, value] of Object.entries(traits)) {
577
+ userTraits[key] = String(value);
578
+ }
579
+ }
580
+ },
581
+ flush: () => send(true),
582
+ };
583
+ })();
584
+ //# sourceMappingURL=beacon-standalone.js.map