Initial commit: ssessesse ki battle
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
import { ACTIVITY_INTERVAL_MS, ANALYTICS_BATCH_SIZE, RETRY_INTERVAL_MS, RETRY_MAX_AGE_MS, RETRY_QUEUE_LIMIT } from '../../constants/analytics.js';
|
||||
interface AnalyticsTransport { events(events: Record<string, unknown>[]): Promise<unknown>; activity(visible: boolean): Promise<unknown> }
|
||||
export function createClientAnalytics(transport: AnalyticsTransport) {
|
||||
const queue = new Map<string, { data: Record<string, unknown>; at: number; attempts: number }>();
|
||||
let sending = false, pageSent = false, enabled = false, destroyed = false, lastVisible = -Infinity, lastHidden = -Infinity;
|
||||
function event(eventType: string, fields: Record<string, unknown> = {}) {
|
||||
if (queue.size >= RETRY_QUEUE_LIMIT) return;
|
||||
const eventId = crypto.randomUUID(); queue.set(eventId, { data: { eventId, eventType, ...fields }, at: Date.now(), attempts: 0 });
|
||||
if (queue.size >= ANALYTICS_BATCH_SIZE) void flush();
|
||||
}
|
||||
async function flush() {
|
||||
if (sending || !enabled || destroyed) return;
|
||||
sending = true;
|
||||
for (const [id, item] of queue) if (Date.now() - item.at > RETRY_MAX_AGE_MS || item.attempts >= 6) queue.delete(id);
|
||||
while (queue.size && !destroyed) {
|
||||
const batch = [...queue.entries()].slice(0, ANALYTICS_BATCH_SIZE);
|
||||
try {
|
||||
await transport.events(batch.map(([, item]) => ({ ...item.data, delayed: ++item.attempts > 1 })));
|
||||
batch.forEach(([id]) => queue.delete(id));
|
||||
} catch (error) {
|
||||
if ((error as { code?: string }).code === 'INVALID_ANALYTICS_EVENT') batch.forEach(([id]) => queue.delete(id));
|
||||
break;
|
||||
}
|
||||
if (queue.size < ANALYTICS_BATCH_SIZE) break;
|
||||
}
|
||||
sending = false;
|
||||
}
|
||||
function activity() {
|
||||
if (!enabled || destroyed) return;
|
||||
const visible = !document.hidden, now = Date.now();
|
||||
if (now - (visible ? lastVisible : lastHidden) < ACTIVITY_INTERVAL_MS) return;
|
||||
if (visible) lastVisible = now; else lastHidden = now;
|
||||
void transport.activity(visible).catch(() => {});
|
||||
}
|
||||
const heartbeat = setInterval(() => { if (!document.hidden) activity(); }, ACTIVITY_INTERVAL_MS);
|
||||
const retry = setInterval(() => { void flush(); }, RETRY_INTERVAL_MS);
|
||||
document.addEventListener('visibilitychange', activity);
|
||||
return {
|
||||
ready() {
|
||||
enabled = true;
|
||||
activity();
|
||||
if (!pageSent) {
|
||||
pageSent = true;
|
||||
const ua = navigator.userAgent;
|
||||
const browser = /Edg\/(\d+)/.exec(ua) ?? /Firefox\/(\d+)/.exec(ua) ?? /Chrome\/(\d+)/.exec(ua) ?? /Version\/(\d+).*Safari/.exec(ua);
|
||||
const name = /Edg\//.test(ua) ? 'Edge' : /Firefox\//.test(ua) ? 'Firefox' : /Chrome\//.test(ua) ? 'Chrome' : /Safari\//.test(ua) ? 'Safari' : 'Other';
|
||||
const params = new URLSearchParams(location.search);
|
||||
event('page_view', { screen: 'home', metadata: {
|
||||
device: /iPad|Tablet/.test(ua) || (/Macintosh/.test(ua) && navigator.maxTouchPoints > 1) ? 'tablet' : /Mobi|Android|iPhone/.test(ua) ? 'mobile' : 'pc',
|
||||
browser: `${name}${browser ? ` ${browser[1]}` : ''}`, referrer: document.referrer,
|
||||
source: params.get('utm_source'), medium: params.get('utm_medium'), campaign: params.get('utm_campaign'), invitation: params.has('room'),
|
||||
} });
|
||||
}
|
||||
},
|
||||
screen(screen: string) { event('screen_view', { screen }); },
|
||||
click(action: string) { const flowId = crypto.randomUUID(); event('ui_action', { action, flowId }); return flowId; },
|
||||
error(code: string, screen: string) { event('client_error', { code, screen }); },
|
||||
gameStarted(matchId: string, mode: string) { event('game_started', { matchId, mode }); },
|
||||
gameFinished(matchId: string, mode: string, outcome: string, reason: string) { event('game_finished', { matchId, mode, outcome, reason }); },
|
||||
destroy() { destroyed = true; clearInterval(heartbeat); clearInterval(retry); document.removeEventListener('visibilitychange', activity); queue.clear(); },
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user