UNPKG

@sky-mavis/tanto-widget

Version:
328 lines (324 loc) 9.28 kB
'use strict'; var uuid = require('uuid'); var index = require('./constants/index.cjs'); var analytic$1 = require('./types/analytic.cjs'); var index$1 = require('./utils/index.cjs'); class AnalyticStorage { static instance; static MA_CONFIG = '__TANTO_MA_CONFIG'; static MA_DATA = '__TANTO_MA_DATA'; constructor() {} static getInstance() { if (!AnalyticStorage.instance) { AnalyticStorage.instance = new AnalyticStorage(); } return AnalyticStorage.instance; } setConfig(data) { try { sessionStorage.setItem(AnalyticStorage.MA_CONFIG, JSON.stringify(data)); } catch (error) { console.debug('Failed to set config in session storage:', error); } } getConfig() { try { const data = sessionStorage.getItem(AnalyticStorage.MA_CONFIG); if (!data) { return this.getDefaultConfig(); } return JSON.parse(data); } catch (error) { console.debug('Failed to get config from session storage:', error); return this.getDefaultConfig(); } } setData(data) { try { sessionStorage.setItem(AnalyticStorage.MA_DATA, JSON.stringify(data)); } catch (error) { console.debug('Failed to set data in session storage:', error); } } getData() { try { const data = sessionStorage.getItem(AnalyticStorage.MA_DATA); if (!data) { return {}; } return JSON.parse(data); } catch (error) { console.debug('Failed to get data from session storage:', error); return {}; } } clear() { try { sessionStorage.removeItem(AnalyticStorage.MA_CONFIG); sessionStorage.removeItem(AnalyticStorage.MA_DATA); } catch (error) { console.debug('Failed to clear storage:', error); } } getDefaultConfig() { return { sessionId: uuid.v4() }; } } class PlatformDataCollector { static instance; userAgent; constructor() { this.userAgent = index$1.getUserAgent(); } static getInstance() { if (!PlatformDataCollector.instance) { PlatformDataCollector.instance = new PlatformDataCollector(); } return PlatformDataCollector.instance; } getPlatformData() { return { build_version: [this.userAgent?.browser.name, this.userAgent?.browser.version].filter(Boolean).join(' - ') || 'Unknown', device_name: [this.userAgent?.device.vendor, this.userAgent?.device.model].filter(Boolean).join(' - ') || 'Unknown', platform_name: this.userAgent?.os.name || 'Unknown', platform_version: this.userAgent?.os.version || 'Unknown' }; } } class Analytic { static INTERNAL_EVENTS = ['heartbeat', 'identify']; static BATCH_SIZE = 20; static HEARTBEAT_INTERVAL = 2000; static DEVICE_FINGERPRINT_KEY = '__TANTO_MA_DFP'; static FIRST_PARTY_DOMAINS = ['skymavis.com', 'skymavis.one', 'roninchain.com', 'axieinfinity.com']; intervalId = null; apiKey; events; storage; platformDataCollector; constructor(apiKey) { if (!index$1.isClient()) { return; } this.apiKey = apiKey; this.events = []; this.storage = AnalyticStorage.getInstance(); this.platformDataCollector = PlatformDataCollector.getInstance(); if (!localStorage.getItem(Analytic.DEVICE_FINGERPRINT_KEY)) { localStorage.setItem(Analytic.DEVICE_FINGERPRINT_KEY, uuid.v4()); } } isFirstPartyDomain() { if (!index$1.isClient()) { return false; } if (Analytic.FIRST_PARTY_DOMAINS.includes(window.location.hostname)) { return true; } return Analytic.FIRST_PARTY_DOMAINS.some(domain => window.location.hostname.endsWith(`.${domain}`)); } updateSession(options) { if (this.isFirstPartyDomain()) { return; } const { sessionId: currentSessionId, userAddress: currentUserAddress } = this.storage.getConfig(); const data = this.storage.getData(); const shouldUseCurrentSessionId = !options?.force && !!currentSessionId; const sessionId = shouldUseCurrentSessionId ? currentSessionId : options?.sessionId || uuid.v4(); this.storage.setConfig({ userAddress: currentUserAddress || options?.userAddress, sessionTimeout: options?.sessionTimeout, sessionId }); if (options?.resetLastEventRef) { this.storage.setData({ ...data, lastEvent: undefined }); } if (!shouldUseCurrentSessionId) { this.handleNewSession(options); } this.startHeartbeat(); } handleNewSession(options) { try { if (!this.validate()) { return; } this.resetSession(); this.trackEvents([{ type: analytic$1.AnalyticEventType.IDENTIFY, data: { event: 'identify', ...this.platformDataCollector.getPlatformData(), user_properties: { ...(options?.commonProperties ?? {}), build_version: '0.0.1', app_origin: index$1.isClient() ? window.location.origin : undefined }, ...this.storage.getData(), device_id: options?.deviceId || localStorage.getItem(Analytic.DEVICE_FINGERPRINT_KEY) || undefined } }], { force: true }); if (options?.sessionTimeout) { this.storage.setData({ sessionCreatedAt: Date.now() }); } } catch (error) { console.debug('Failed to handle new session:', error); } } revoke() { this.storage.clear(); this.stopHeartbeat(); } startHeartbeat() { if (!this.intervalId) { this.intervalId = setInterval(() => { this.sendEvent('heartbeat'); }, Analytic.HEARTBEAT_INTERVAL); } } stopHeartbeat() { if (this.intervalId) { clearInterval(this.intervalId); this.intervalId = null; } } async sendEvent(eventName, data = {}) { try { if (!this.validate()) return; await this.trackEvents([{ type: analytic$1.AnalyticEventType.TRACK, data: { action_properties: { ...data, build_version: '0.0.1', app_origin: index$1.isClient() ? window.location.origin : undefined }, action: eventName, event: eventName } }], { force: eventName !== 'heartbeat' }); } catch (error) { console.debug('Failed to send event:', error); } } sendScreen(screen, data = {}) { try { if (!this.validate()) return; this.trackEvents([{ type: analytic$1.AnalyticEventType.SCREEN, data: { screen, screen_properties: { ...data, build_version: '0.0.1', app_origin: index$1.isClient() ? window.location.origin : undefined } } }]); } catch (error) { console.debug('Failed to send screen:', error); } } getBaseData() { const data = this.storage.getData(); const config = this.storage.getConfig(); return { uuid: uuid.v4(), ref: data.lastEvent, timestamp: new Date().toISOString().slice(0, 19).replace('T', ' '), session_id: config.sessionId, user_id: config.userAddress }; } async trackEvents(eventsData, options) { if (this.isFirstPartyDomain()) { return; } try { const { force = true } = options || {}; const data = this.storage.getData(); const events = eventsData.map(event => ({ type: event.type, data: { ...this.getBaseData(), ...this.platformDataCollector.getPlatformData(), ...event.data, device_id: localStorage.getItem(Analytic.DEVICE_FINGERPRINT_KEY) } })); this.events.push(...events); if (force || this.events.length >= Analytic.BATCH_SIZE) { await this.send(this.events); this.events.length = 0; } const lastEvent = eventsData[eventsData.length - 1]?.data.event; if (lastEvent && !Analytic.INTERNAL_EVENTS.includes(lastEvent)) { this.storage.setData({ ...data, lastEvent }); } } catch (error) { console.debug('Failed to track events:', error); } } async send(events) { return fetch('https://x.skymavis.com/track', { method: 'POST', headers: [['Authorization', `Basic ${btoa(`${this.apiKey}:`)}`], ['Content-Type', 'application/json']], body: JSON.stringify({ events }) }); } resetSession() { const config = this.storage.getConfig(); const data = this.storage.getData(); const { sessionTimeout } = config; const { sessionCreatedAt = 0 } = data; if (sessionTimeout) { const sessionExpiredAt = sessionCreatedAt + sessionTimeout * 1000; const now = Date.now(); if (now >= sessionExpiredAt) { this.storage.setData({ ...data, sessionCreatedAt: now }); this.storage.setConfig({ ...config, sessionId: uuid.v4() }); } } else if (sessionCreatedAt) { this.storage.setData({ ...data, sessionCreatedAt: undefined }); } } validate() { return Boolean(this.apiKey); } } const analytic = new Analytic(index.ANALYTIC_PUBLIC_KEY); exports.analytic = analytic;