/** * Serla Analytics - JavaScript SDK (Node.js) * * Usage: * const { Serla } = require('./lib/sdk/serla'); * const serla = new Serla('sk_live_YOUR_API_KEY'); * await serla.send('user_signup', { plan: 'pro' }); */ class Serla { constructor(apiKey, options = {}) { if (!apiKey) { throw new Error('Serla: API key is required'); } this.apiKey = apiKey; this.endpoint = options.endpoint || 'https://serla.dev/api/events/ingest'; this.pageviewEndpoint = 'https://serla.dev/api/pageviews/ingest'; this.debug = options.debug || false; this.userId = null; this.sessionId = null; this.log('Initialized with endpoint:', this.endpoint); } /** * Send an event to Serla * @param {string} eventName - Name of the event * @param {Object} metadata - Optional metadata object * @param {string|null} userId - Optional userId for this event (overrides identify()) * @param {string|null} sessionId - Optional sessionId for this event (overrides setSession()) * @returns {Promise} */ async send(eventName, metadata = {}, userId = undefined, sessionId = undefined) { if (!eventName || typeof eventName !== 'string') { throw new Error('Serla: Event name is required and must be a string'); } const payload = { eventName, metadata: metadata || {}, }; // Use parameter if provided, otherwise fall back to instance variable if (userId !== undefined) { if (userId !== null) payload.userId = userId; } else if (this.userId) { payload.userId = this.userId; } if (sessionId !== undefined) { if (sessionId !== null) payload.sessionId = sessionId; } else if (this.sessionId) { payload.sessionId = this.sessionId; } this.log('Sending event:', eventName, metadata); try { const response = await fetch(this.endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.apiKey}`, }, body: JSON.stringify(payload), }); if (!response.ok) { const errorText = await response.text(); throw new Error(`Serla API error (${response.status}): ${errorText}`); } const data = await response.json(); this.log('Event sent successfully:', data); return data; } catch (error) { this.log('Error sending event:', error); throw error; } } /** * Track a pageview * @param {string} path - Page path * @param {string} referrer - Referrer URL * @returns {Promise} */ async pageview(path, referrer) { // Ensure we have a session ID if (!this.sessionId) { const timestamp = Date.now().toString(36); const randomStr = Math.random().toString(36).substring(2, 15); this.sessionId = `${timestamp}-${randomStr}`; } // Construct URL - required field let url; if (typeof window !== 'undefined') { // Browser environment if (path) { url = `${window.location.origin}${path}`; } else { url = window.location.href; } } else { // Server environment url = path || '/'; } const payload = { url, sessionId: this.sessionId, }; if (this.userId) { payload.userId = this.userId; } if (referrer) { payload.referrer = referrer; } else if (typeof document !== 'undefined' && document.referrer) { payload.referrer = document.referrer; } this.log('Tracking pageview:', url); try { const response = await fetch(this.pageviewEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.apiKey}`, }, body: JSON.stringify(payload), }); if (!response.ok) { const errorText = await response.text(); throw new Error(`Serla API error (${response.status}): ${errorText}`); } const data = await response.json(); this.log('Pageview tracked successfully:', data); return data; } catch (error) { this.log('Error tracking pageview:', error); throw error; } } /** * Identify a user for all subsequent events * @param {string|null} userId - User identifier */ identify(userId) { this.userId = userId; this.log('User identified:', userId); } /** * Set a session ID for all subsequent events * @param {string|null} sessionId - Session identifier */ setSession(sessionId) { this.sessionId = sessionId; this.log('Session set:', sessionId); } /** * Get the current user ID * @returns {string|null} */ getUserId() { return this.userId; } /** * Get the current session ID * @returns {string|null} */ getSessionId() { return this.sessionId; } /** * Log debug messages if debug mode is enabled * @private */ log(...args) { if (this.debug) { console.log('[Serla]', ...args); } } } // Factory function function createSerla(apiKey, options) { return new Serla(apiKey, options); } // Exports module.exports = { Serla, createSerla }; module.exports.default = Serla;