overcentric
Version:
Overcentric watches your website, product, and users - and tells you what matters and what to do about it.
115 lines (114 loc) • 4.23 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EventQueue = void 0;
class EventQueue {
constructor(config) {
this.eventQueue = [];
this.flushTimeoutId = null;
this.MAX_BATCH_SIZE = 10;
this.FLUSH_INTERVAL = 2000; // 2 seconds
this.MAX_RETRIES = 3;
this.DEFAULT_ENDPOINT = 'http://localhost:4000/api/events';
this.config = config;
this.setupEventListeners();
}
static getInstance(config) {
if (!EventQueue.instance) {
EventQueue.instance = new EventQueue(config);
}
return EventQueue.instance;
}
logDebug(message) {
var _a, _b;
(_b = (_a = this.config).onDebugLog) === null || _b === void 0 ? void 0 : _b.call(_a, message);
}
setupEventListeners() {
window.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
this.flushEvents();
}
});
window.addEventListener('beforeunload', () => {
this.flushEvents();
});
}
addEvent(eventName, properties) {
this.eventQueue.push({
eventName,
properties,
timestamp: Date.now()
});
this.logDebug(`overcentric: Tracking event: ${eventName}`);
this.logDebug(`overcentric: Event properties: ${JSON.stringify(properties)}`);
if (this.eventQueue.length >= this.MAX_BATCH_SIZE) {
this.flushEvents();
}
else {
this.scheduleFlush();
}
}
scheduleFlush() {
if (this.flushTimeoutId)
return;
this.flushTimeoutId = setTimeout(() => {
this.flushTimeoutId = null;
this.flushEvents();
}, this.FLUSH_INTERVAL);
}
async flushEvents() {
if (this.eventQueue.length === 0)
return;
const events = [...this.eventQueue];
this.eventQueue = [];
const payload = {
projectId: this.config.projectId,
events,
clientTimestamp: Date.now()
};
const endpoint = this.config.endpoint || this.DEFAULT_ENDPOINT;
try {
// Try sendBeacon first (most efficient, works even on page unload)
if (navigator.sendBeacon) {
const blob = new Blob([JSON.stringify(payload)], { type: 'application/json' });
const success = navigator.sendBeacon(endpoint, blob);
if (success) {
this.logDebug(`overcentric: Sent ${events.length} events via beacon`);
return;
}
}
// Fall back to fetch if sendBeacon fails or isn't available
let retries = 0;
while (retries < this.MAX_RETRIES) {
try {
const response = await fetch(endpoint, {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json'
},
keepalive: true
});
if (response.ok) {
this.logDebug(`overcentric: Sent ${events.length} events via fetch`);
return;
}
}
catch (error) {
this.logDebug(`overcentric: Error sending events (attempt ${retries + 1}): ${error}`);
retries++;
if (retries < this.MAX_RETRIES) {
await new Promise(resolve => setTimeout(resolve, 1000 * retries));
}
}
}
// If all retries failed, queue the events again
this.eventQueue.push(...events);
this.logDebug(`overcentric: Failed to send events after ${this.MAX_RETRIES} retries. Requeued ${events.length} events.`);
}
catch (error) {
this.logDebug(`overcentric: Error in flushEvents: ${error}`);
this.eventQueue.push(...events);
}
}
}
exports.EventQueue = EventQueue;