UNPKG

cosmic-analytics

Version:

Lightweight analytics for Cosmic projects

345 lines (339 loc) 10.2 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/client/index.ts var client_exports = {}; __export(client_exports, { CosmicAnalyticsProvider: () => CosmicAnalyticsProvider, getAnalytics: () => getAnalytics, initAnalytics: () => initAnalytics, useCosmicAnalytics: () => useCosmicAnalytics }); module.exports = __toCommonJS(client_exports); // src/client/useCosmicAnalytics.ts var import_react = require("react"); var import_navigation = require("next/navigation"); // src/lib/utils.ts function generateSessionId() { return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`; } function isBrowser() { return typeof window !== "undefined" && typeof document !== "undefined"; } function isDoNotTrackEnabled() { if (!isBrowser()) return false; const { doNotTrack, navigator: navigator2 } = window; const dnt = doNotTrack || navigator2.doNotTrack || navigator2.msDoNotTrack; return dnt === "1" || dnt === "yes"; } function getViewport() { if (!isBrowser()) return ""; return `${window.innerWidth}x${window.innerHeight}`; } function getScreenResolution() { if (!isBrowser()) return ""; return `${window.screen.width}x${window.screen.height}`; } function safeJsonParse(json, fallback) { try { return JSON.parse(json); } catch { return fallback; } } // src/lib/analytics.ts var SESSION_TIMEOUT = 30 * 60 * 1e3; var SESSION_STORAGE_KEY = "cosmic_analytics_session"; var CosmicAnalytics = class { constructor(config) { this.session = null; this.pageViewStart = 0; this.isEnabled = true; this.queue = []; this.flushTimer = null; this.config = { apiEndpoint: "https://api.cosmic.new/analytics", debug: false, enabled: true, respectDoNotTrack: true, sendInDevelopment: false, ...config }; this.projectId = this.config.projectId || this.getProjectId(); this.apiEndpoint = this.config.apiEndpoint; this.isEnabled = this.shouldEnableAnalytics(); if (this.isEnabled && isBrowser()) { this.initializeSession(); this.setupEventListeners(); this.startFlushTimer(); } } getProjectId() { return ""; } isDevelopmentMode() { if (typeof this.config.isDevelopment === "boolean") { return this.config.isDevelopment; } if (typeof globalThis.process !== "undefined") { return globalThis.process.env?.NODE_ENV === "development"; } if (isBrowser()) { if (window.__NEXT_DATA__?.buildId === "development") { return true; } const hostname = window.location.hostname; return hostname === "localhost" || hostname === "127.0.0.1" || hostname.includes(".local"); } return false; } shouldEnableAnalytics() { if (!this.config.enabled) return false; if (!this.projectId) { if (this.config.debug) { console.warn("Cosmic Analytics: NEXT_PUBLIC_CLIENT_ID not found"); } return false; } if (this.isDevelopmentMode() && !this.config.sendInDevelopment) { if (this.config.debug) { console.log("Cosmic Analytics: Disabled in development mode. Set sendInDevelopment: true to enable."); } return false; } if (this.config.respectDoNotTrack && isDoNotTrackEnabled()) { if (this.config.debug) { console.log("Cosmic Analytics: Respecting Do Not Track preference"); } return false; } return true; } initializeSession() { const storedSession = this.getStoredSession(); if (storedSession && this.isSessionValid(storedSession)) { this.session = storedSession; this.session.lastActivity = Date.now(); } else { this.session = { id: generateSessionId(), startTime: Date.now(), pageViews: 0, lastActivity: Date.now() }; } this.saveSession(); } getStoredSession() { if (!isBrowser()) return null; try { const stored = sessionStorage.getItem(SESSION_STORAGE_KEY); if (!stored) return null; return safeJsonParse(stored, null); } catch { return null; } } isSessionValid(session) { const now = Date.now(); return now - session.lastActivity < SESSION_TIMEOUT; } saveSession() { if (!isBrowser() || !this.session) return; try { sessionStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(this.session)); } catch (error) { if (this.config.debug) { console.error("Cosmic Analytics: Failed to save session", error); } } } setupEventListeners() { if (!isBrowser()) return; document.addEventListener("visibilitychange", () => { if (document.visibilityState === "hidden") { this.trackPageExit(); this.flush(true); } else if (document.visibilityState === "visible") { this.trackPageView(); } }); window.addEventListener("beforeunload", () => { this.trackPageExit(); this.flush(true); }); } startFlushTimer() { this.flushTimer = setInterval(() => { this.flush(); }, 5e3); } stopFlushTimer() { if (this.flushTimer) { clearInterval(this.flushTimer); this.flushTimer = null; } } addToQueue(event) { this.queue.push(event); if (this.queue.length >= 10) { this.flush(); } } async flush(forceSendNow = false) { if (this.queue.length === 0) return; const events = [...this.queue]; this.queue = []; try { const response = await fetch(`${this.apiEndpoint}/track`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ events }), keepalive: forceSendNow }); if (!response.ok && this.config.debug) { console.error("Cosmic Analytics: Failed to send events", response.status); this.queue.unshift(...events); } } catch (error) { if (this.config.debug) { console.error("Cosmic Analytics: Error sending events", error); } this.queue.unshift(...events); } } trackPageView(overrides) { if (!this.isEnabled || !isBrowser() || !this.session) return; this.pageViewStart = Date.now(); this.session.pageViews++; this.session.lastActivity = Date.now(); this.saveSession(); const event = { event: "pageview", projectId: this.projectId, sessionId: this.session.id, url: window.location.href, title: document.title, referrer: document.referrer || "direct", timestamp: Date.now(), screenResolution: getScreenResolution(), viewport: getViewport(), language: navigator.language, userAgent: navigator.userAgent, ...overrides }; if (this.config.debug) { console.log("Cosmic Analytics: Page view", event); } this.addToQueue(event); } trackPageExit() { if (!this.isEnabled || !isBrowser() || !this.session || !this.pageViewStart) return; const duration = Date.now() - this.pageViewStart; const event = { event: "pageexit", projectId: this.projectId, sessionId: this.session.id, url: window.location.href, duration, timestamp: Date.now() }; if (this.config.debug) { console.log("Cosmic Analytics: Page exit", event); } this.addToQueue(event); } reset() { if (!isBrowser()) return; this.session = null; sessionStorage.removeItem(SESSION_STORAGE_KEY); if (this.isEnabled) { this.initializeSession(); } } disable() { this.isEnabled = false; this.stopFlushTimer(); this.queue = []; } enable() { this.isEnabled = this.shouldEnableAnalytics(); if (this.isEnabled && isBrowser()) { this.initializeSession(); this.startFlushTimer(); } } }; // src/client/useCosmicAnalytics.ts var analyticsInstance = null; function initAnalytics(config) { if (!analyticsInstance) { analyticsInstance = new CosmicAnalytics(config); } return analyticsInstance; } function getAnalytics() { return analyticsInstance; } function useCosmicAnalytics(config) { const pathname = (0, import_navigation.usePathname)(); const initializedRef = (0, import_react.useRef)(false); const prevUrlRef = (0, import_react.useRef)(null); (0, import_react.useEffect)(() => { if (!initializedRef.current) { initAnalytics(config); initializedRef.current = true; } const analytics = getAnalytics(); if (!analytics) return; const currentUrl = typeof window !== "undefined" ? window.location.href : pathname; if (prevUrlRef.current !== currentUrl) { analytics.trackPageView(); prevUrlRef.current = currentUrl; } return () => { analytics.trackPageExit(); }; }, [pathname, config]); } // src/client/CosmicAnalyticsProvider.tsx var import_jsx_runtime = require("react/jsx-runtime"); function CosmicAnalyticsProvider({ children, config }) { const projectId = process.env.NEXT_PUBLIC_CLIENT_ID; const isDevelopment = process.env.NODE_ENV === "development"; const mergedConfig = { projectId, isDevelopment, ...config }; useCosmicAnalytics(mergedConfig); return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children }); } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { CosmicAnalyticsProvider, getAnalytics, initAnalytics, useCosmicAnalytics }); //# sourceMappingURL=index.js.map