@snovasys/usage-analytics-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/buffer/pre-init-buffer.ts","../src/dispatcher/registry.ts","../src/enricher/session.ts","../src/enricher/enricher.ts","../src/dispatcher/dispatcher.ts","../src/listeners/noop.ts","../src/listeners/clarity.ts","../src/listeners/ga.ts","../src/listeners/custom-api.ts","../src/listeners/registry.ts","../src/validate-config.ts","../src/version.ts","../src/facade.ts"],"sourcesContent":["import type { BufferedItem, BufferedTrack, BufferedIdentify } from './types.js';\n\nexport interface PreInitBufferOptions {\n /** 'drop' = no-op; 'queue' = FIFO queue. In SSR (no window) always drop. */\n mode: 'drop' | 'queue';\n maxSize: number;\n ttlMs: number;\n /** For testing: override browser check so queue works in Node. */\n isBrowserOverride?: boolean;\n}\n\nexport type FlushTrackHandler = (payload: { name: string; properties?: Record<string, unknown>; timestamp?: string }) => void;\nexport type FlushIdentifyHandler = (userId: string, traits?: Record<string, unknown>) => void;\n\n/**\n * Pre-init buffer: either drops events or queues them (FIFO) until flush.\n * In Node/SSR (typeof window === 'undefined') always behaves as drop.\n */\nexport class PreInitBuffer {\n private readonly options: PreInitBufferOptions;\n private readonly queue: BufferedItem[] = [];\n private readonly isBrowser: boolean;\n\n constructor(options: PreInitBufferOptions) {\n this.options = options;\n this.isBrowser =\n options.isBrowserOverride !== undefined ? options.isBrowserOverride : typeof window !== 'undefined';\n }\n\n /**\n * Push a track call. No-op if mode is drop or not browser (SSR).\n */\n pushTrack(name: string, properties?: Record<string, unknown>, timestamp?: string): void {\n if (this.options.mode === 'drop' || !this.isBrowser) return;\n const item: BufferedTrack = {\n type: 'track',\n name,\n properties,\n timestamp,\n createdAt: Date.now(),\n };\n this.push(item);\n }\n\n /**\n * Push an identify call. No-op if mode is drop or not browser (SSR).\n */\n pushIdentify(userId: string, traits?: Record<string, unknown>): void {\n if (this.options.mode === 'drop' || !this.isBrowser) return;\n const item: BufferedIdentify = {\n type: 'identify',\n userId,\n traits,\n createdAt: Date.now(),\n };\n this.push(item);\n }\n\n private push(item: BufferedItem): void {\n while (this.queue.length >= this.options.maxSize && this.queue.length > 0) {\n this.queue.shift(); // drop oldest (FIFO)\n }\n this.queue.push(item);\n }\n\n /**\n * Replay queued items: all identify first (order), then all track (order).\n * Items older than ttlMs are dropped. Then clear the queue.\n */\n flush(\n onTrack: FlushTrackHandler,\n onIdentify: FlushIdentifyHandler\n ): void {\n if (!this.isBrowser || this.queue.length === 0) {\n this.queue.length = 0;\n return;\n }\n const now = Date.now();\n const cutoff = now - this.options.ttlMs;\n const identifyItems = this.queue.filter(\n (item): item is BufferedIdentify => item.type === 'identify' && item.createdAt >= cutoff\n );\n const trackItems = this.queue.filter(\n (item): item is BufferedTrack => item.type === 'track' && item.createdAt >= cutoff\n );\n for (const item of identifyItems) {\n onIdentify(item.userId, item.traits);\n }\n for (const item of trackItems) {\n onTrack({\n name: item.name,\n properties: item.properties,\n timestamp: item.timestamp,\n });\n }\n this.queue.length = 0;\n }\n\n /** Number of items currently queued (for tests). */\n get length(): number {\n return this.queue.length;\n }\n}\n","import type { AnalyticsListener } from '../types/listener.js';\n\nexport interface RegisteredListener {\n id?: string;\n listener: AnalyticsListener;\n}\n\n/**\n * Holds enabled listener instances. Populated at init.\n */\nexport class ListenerRegistry {\n private readonly listeners: RegisteredListener[] = [];\n\n add(id: string | undefined, listener: AnalyticsListener): void {\n this.listeners.push({ id, listener });\n }\n\n getAll(): RegisteredListener[] {\n return [...this.listeners];\n }\n\n clear(): void {\n this.listeners.length = 0;\n }\n}\n","const STORAGE_KEY_PREFIX = 'snovasys_usage_analytics_session_';\n\n/**\n * Generate a UUID v4 (uses crypto.randomUUID when available, else fallback).\n */\nexport function generateId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return fallbackUuidV4();\n}\n\nfunction fallbackUuidV4(): string {\n const hex = '0123456789abcdef';\n let result = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';\n result = result.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return hex[v];\n });\n return result;\n}\n\n/**\n * Get or create session id. Optionally persist in sessionStorage keyed by appId.\n */\nexport function getOrCreateSessionId(appId: string, providedSessionId?: string): string {\n if (providedSessionId) return providedSessionId;\n const key = `${STORAGE_KEY_PREFIX}${appId}`;\n if (typeof window !== 'undefined' && typeof sessionStorage !== 'undefined') {\n try {\n const existing = sessionStorage.getItem(key);\n if (existing) return existing;\n const newId = generateId();\n sessionStorage.setItem(key, newId);\n return newId;\n } catch {\n return generateId();\n }\n }\n return generateId();\n}\n","import type { EventEnvelope } from '../types/envelope.js';\nimport { generateId, getOrCreateSessionId } from './session.js';\n\n/** Raw track payload from app (before enrichment). */\nexport interface TrackPayload {\n name: string;\n properties?: Record<string, unknown>;\n timestamp?: string;\n}\n\nexport interface EnricherState {\n appId: string;\n environment: string;\n sdkVersion: string;\n sessionId?: string;\n userId?: string;\n /** Provided at init or generated per session. */\n resolvedSessionId?: string;\n}\n\nconst ENVELOPE_VERSION = 1;\n\n/**\n * Creates enricher state from init config. Call once at init.\n */\nexport function createEnricherState(\n appId: string,\n environment: string,\n sdkVersion: string,\n sessionId?: string\n): EnricherState {\n const resolvedSessionId = getOrCreateSessionId(appId, sessionId);\n return {\n appId,\n environment,\n sdkVersion,\n sessionId,\n resolvedSessionId,\n };\n}\n\n/**\n * Enrich a raw track payload into a full EventEnvelope.\n */\nexport function enrich(\n payload: TrackPayload,\n state: EnricherState\n): EventEnvelope {\n const timestamp = payload.timestamp ?? new Date().toISOString();\n const envelope: EventEnvelope = {\n name: payload.name,\n timestamp,\n eventId: generateId(),\n appId: state.appId,\n environment: state.environment,\n sdkVersion: state.sdkVersion,\n version: ENVELOPE_VERSION,\n };\n if (payload.properties != null) envelope.properties = payload.properties;\n if (state.resolvedSessionId) envelope.sessionId = state.resolvedSessionId;\n if (state.userId) envelope.userId = state.userId;\n return envelope;\n}\n\n/**\n * Set userId (and optional traits) on state after identify. Cleared on reset.\n */\nexport function setUserId(state: EnricherState, userId: string): void {\n state.userId = userId;\n}\n\n/**\n * Clear userId from state (e.g. on reset).\n */\nexport function clearUserId(state: EnricherState): void {\n state.userId = undefined;\n}\n","import type { EventEnvelope } from '../types/envelope.js';\nimport type { OnErrorContext } from '../types/config.js';\nimport type { ListenerRegistry } from './registry.js';\nimport type { EnricherState } from '../enricher/enricher.js';\nimport { enrich, type TrackPayload } from '../enricher/enricher.js';\n\nexport interface DispatcherDeps {\n registry: ListenerRegistry;\n getEnricherState: () => EnricherState;\n onError?: (error: Error, context?: OnErrorContext) => void;\n}\n\n/**\n * Normalize payload → enrich → dispatch to all listeners (try/catch per listener).\n */\nexport function dispatchTrack(\n deps: DispatcherDeps,\n payload: TrackPayload\n): void {\n const state = deps.getEnricherState();\n const envelope = enrich(payload, state);\n dispatchEnvelope(deps, envelope, 'track');\n}\n\n/**\n * Dispatch a pre-built envelope to all listeners (e.g. after enrich).\n */\nfunction dispatchEnvelope(\n deps: DispatcherDeps,\n envelope: EventEnvelope,\n kind: 'track'\n): void {\n const { registry, onError } = deps;\n for (const { id, listener } of registry.getAll()) {\n try {\n listener.track(envelope);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error, { listenerId: id });\n }\n }\n}\n\n/**\n * Call identify on all listeners that implement it (try/catch per listener).\n */\nexport function dispatchIdentify(\n deps: DispatcherDeps,\n userId: string,\n traits?: Record<string, unknown>\n): void {\n const { registry, onError } = deps;\n for (const { id, listener } of registry.getAll()) {\n if (typeof listener.identify !== 'function') continue;\n try {\n listener.identify(userId, traits);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error, { listenerId: id });\n }\n }\n}\n","import type { AnalyticsListener } from '../types/listener.js';\nimport type { EventEnvelope } from '../types/envelope.js';\n\n/**\n * No-op listener: implements the interface with no side effects.\n * Use when analytics is disabled or in tests.\n */\nexport function createNoopListener(): AnalyticsListener {\n return {\n init(_config: unknown): void {\n // no-op\n },\n track(_envelope: EventEnvelope): void {\n // no-op\n },\n };\n}\n\n/** Singleton no-op listener instance. */\nexport const noopListener: AnalyticsListener = createNoopListener();\n","import type { AnalyticsListener } from '../types/listener.js';\nimport type { EventEnvelope } from '../types/envelope.js';\n\nexport interface ClarityListenerConfig {\n projectId: string;\n}\n\nconst CLARITY_SCRIPT_BASE = 'https://www.clarity.ms/tag/';\n\ndeclare global {\n interface Window {\n clarity?: (cmd: string, ...args: unknown[]) => void;\n }\n}\n\nfunction isBrowser(): boolean {\n return typeof window !== 'undefined';\n}\n\nfunction loadClarityScript(projectId: string): Promise<void> {\n return new Promise((resolve) => {\n if (!isBrowser()) {\n resolve();\n return;\n }\n if (typeof window.clarity === 'function') {\n resolve();\n return;\n }\n const script = document.createElement('script');\n script.async = true;\n script.src = `${CLARITY_SCRIPT_BASE}${encodeURIComponent(projectId)}`;\n script.onload = () => resolve();\n script.onerror = () => resolve();\n document.head.appendChild(script);\n });\n}\n\n/**\n * Listener that sends events to Microsoft Clarity via script injection.\n * Loads Clarity script with projectId; track → clarity(\"event\", name); identify → clarity(\"identify\", userId, ...).\n * No-op when typeof window === 'undefined' (SSR).\n */\nexport function createClarityListener(config: ClarityListenerConfig): AnalyticsListener {\n const projectId = config.projectId;\n let ready = false;\n\n return {\n async init(cfg: unknown): Promise<void> {\n try {\n const c = (cfg ?? config) as ClarityListenerConfig;\n const id = c.projectId;\n if (!id || !isBrowser()) return;\n await loadClarityScript(id);\n ready = true;\n } catch {\n // swallow\n }\n },\n track(envelope: EventEnvelope): void {\n try {\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\n window.clarity('event', envelope.name);\n if (envelope.properties && typeof window.clarity === 'function') {\n for (const [key, value] of Object.entries(envelope.properties)) {\n if (typeof value === 'string' || Array.isArray(value)) {\n window.clarity('set', key, value);\n }\n }\n }\n } catch {\n // swallow\n }\n },\n identify(userId: string, _traits?: Record<string, unknown>): void {\n try {\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\n window.clarity('identify', userId, undefined, undefined, undefined);\n } catch {\n // swallow\n }\n },\n };\n}\n","import type { AnalyticsListener } from '../types/listener.js';\nimport type { EventEnvelope } from '../types/envelope.js';\n\nexport interface GAListenerConfig {\n measurementId: string;\n}\n\nconst GTAG_SCRIPT_URL = 'https://www.googletagmanager.com/gtag/js';\n\ndeclare global {\n interface Window {\n dataLayer?: unknown[];\n gtag?: (...args: unknown[]) => void;\n }\n}\n\nfunction isBrowser(): boolean {\n return typeof window !== 'undefined';\n}\n\nfunction loadGtagScript(measurementId: string): Promise<void> {\n return new Promise((resolve) => {\n if (!isBrowser()) {\n resolve();\n return;\n }\n if (window.gtag) {\n window.gtag('config', measurementId);\n resolve();\n return;\n }\n window.dataLayer = window.dataLayer ?? [];\n const gtag = (...args: unknown[]) => {\n window.dataLayer!.push(args);\n };\n window.gtag = gtag;\n gtag('js', new Date());\n const script = document.createElement('script');\n script.async = true;\n script.src = `${GTAG_SCRIPT_URL}?id=${encodeURIComponent(measurementId)}`;\n script.onload = () => {\n gtag('config', measurementId);\n resolve();\n };\n script.onerror = () => resolve();\n document.head.appendChild(script);\n });\n}\n\n/**\n * Listener that sends events to Google Analytics 4 via gtag.\n * Loads gtag script with measurementId; track → gtag('event', name, properties).\n * No-op when typeof window === 'undefined' (SSR).\n */\nexport function createGAListener(config: GAListenerConfig): AnalyticsListener {\n const measurementId = config.measurementId;\n let ready = false;\n\n return {\n async init(cfg: unknown): Promise<void> {\n try {\n const c = (cfg ?? config) as GAListenerConfig;\n const id = c.measurementId;\n if (!id || !isBrowser()) return;\n await loadGtagScript(id);\n ready = true;\n } catch {\n // swallow\n }\n },\n track(envelope: EventEnvelope): void {\n try {\n if (!isBrowser() || !ready || typeof window.gtag !== 'function') return;\n const params = envelope.properties ?? {};\n if (envelope.userId) (params as Record<string, unknown>)['user_id'] = envelope.userId;\n window.gtag('event', envelope.name, params);\n } catch {\n // swallow\n }\n },\n identify(userId: string, traits?: Record<string, unknown>): void {\n try {\n if (!isBrowser() || typeof window.gtag !== 'function') return;\n window.gtag('set', 'user_properties', { user_id: userId, ...traits });\n } catch {\n // swallow\n }\n },\n };\n}\n","import type { AnalyticsListener } from '../types/listener.js';\nimport type { EventEnvelope } from '../types/envelope.js';\n\nexport interface CustomApiListenerConfig {\n /** Endpoint URL for POST (e.g. https://api.example.com/analytics/events). */\n endpoint: string;\n /** Optional API key or bearer token. */\n apiKey?: string;\n /** Optional: batch size before sending. Default 1 (no batching). */\n batchSize?: number;\n /** Optional: max ms to wait before sending a partial batch. Default 5000. */\n flushIntervalMs?: number;\n}\n\nconst DEFAULT_BATCH_SIZE = 1;\nconst DEFAULT_FLUSH_INTERVAL_MS = 5000;\n\n/**\n * Listener that POSTs events to a configurable endpoint.\n * Optional batching: accumulates events and sends when batchSize or flushIntervalMs is reached.\n */\nexport function createCustomApiListener(config: CustomApiListenerConfig): AnalyticsListener {\n const endpoint = config.endpoint;\n const apiKey = config.apiKey;\n const batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE;\n const flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n\n let batch: EventEnvelope[] = [];\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n\n function scheduleFlush(): void {\n if (flushTimer != null) return;\n flushTimer = setTimeout(() => {\n flushTimer = null;\n sendBatch();\n }, flushIntervalMs);\n }\n\n function sendBatch(): void {\n if (batch.length === 0) return;\n const toSend = [...batch];\n batch = [];\n const body = JSON.stringify(toSend.length === 1 ? toSend[0] : { events: toSend });\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;\n if (typeof fetch !== 'undefined') {\n fetch(endpoint, {\n method: 'POST',\n headers,\n body,\n keepalive: true,\n }).catch(() => {});\n }\n }\n\n return {\n init(_config: unknown): void {\n // config was passed at createCustomApiListener\n },\n track(envelope: EventEnvelope): void {\n try {\n batch.push(envelope);\n if (batch.length >= batchSize) {\n sendBatch();\n } else if (flushIntervalMs > 0) {\n scheduleFlush();\n }\n } catch {\n // swallow\n }\n },\n flush(): Promise<void> {\n if (flushTimer != null) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n sendBatch();\n return Promise.resolve();\n },\n teardown(): void {\n if (flushTimer != null) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n batch.length = 0;\n },\n };\n}\n","import type { ListenerEntry, ListenerFactory, RemoteAnalyticsConfig } from '../types/config.js';\nimport type { AnalyticsListener } from '../types/listener.js';\nimport { createNoopListener } from './noop.js';\nimport { createClarityListener } from './clarity.js';\nimport { createGAListener } from './ga.js';\nimport { createCustomApiListener } from './custom-api.js';\n\n/** Built-in listener IDs supported by the SDK (noop, clarity, ga, custom-api). */\nexport const BUILT_IN_LISTENER_IDS = ['noop', 'clarity', 'ga', 'custom-api'] as const;\n\nconst BUILT_IN_FACTORIES: Record<string, ListenerFactory> = {\n noop: () => createNoopListener(),\n clarity: (config) => createClarityListener(config as { projectId: string }),\n ga: (config) => createGAListener(config as { measurementId: string }),\n 'custom-api': (config) => createCustomApiListener(config as Parameters<typeof createCustomApiListener>[0]),\n};\n\n/**\n * Returns the built-in listener factory for the given id, or undefined if unknown.\n */\nexport function getBuiltInListenerFactory(id: string): ListenerFactory | undefined {\n return BUILT_IN_FACTORIES[id];\n}\n\nexport interface BuildListenersFromRemoteConfigOptions {\n /** App override: map listener id to factory. When omitted, built-in registry is used. */\n factories?: Record<string, ListenerFactory>;\n /** Called when a remote listener id has no factory (unknown id). */\n onError?: (error: Error, context?: { listenerId?: string }) => void;\n}\n\n/**\n * Builds ListenerEntry[] from remote config. Uses options.factories when provided,\n * otherwise the built-in registry. Skips entries with enabled === false or unknown id.\n */\nexport function buildListenersFromRemoteConfig(\n remote: RemoteAnalyticsConfig,\n options?: BuildListenersFromRemoteConfigOptions\n): ListenerEntry[] {\n const factories = options?.factories ?? BUILT_IN_FACTORIES;\n const onError = options?.onError;\n const entries: ListenerEntry[] = [];\n\n if (!Array.isArray(remote.listeners)) {\n return entries;\n }\n\n for (const entry of remote.listeners) {\n if (entry.enabled === false) continue;\n const id = entry.id;\n if (id == null || typeof id !== 'string') continue;\n\n const factory = factories[id];\n if (factory == null) {\n onError?.(new Error(`Unknown listener id: ${id}`), { listenerId: id });\n continue;\n }\n\n let listener: AnalyticsListener;\n try {\n listener = factory(entry.config);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error, { listenerId: id });\n continue;\n }\n\n entries.push({\n id,\n enabled: entry.enabled,\n listener,\n config: entry.config,\n });\n }\n\n return entries;\n}\n","import type {\n AnalyticsInitConfig,\n ListenerEntry,\n PreInitBehavior,\n RemoteListenerEntry,\n} from './types/config.js';\nimport type { AnalyticsListener } from './types/listener.js';\n\nconst VALID_PRE_INIT_BEHAVIORS: PreInitBehavior[] = ['drop', 'queue'];\n\nfunction isRemoteListenerEntry(entry: unknown): entry is RemoteListenerEntry {\n if (entry == null || typeof entry !== 'object') return false;\n const o = entry as Record<string, unknown>;\n return (\n typeof o.id === 'string' &&\n o.id.trim() !== '' &&\n 'config' in o &&\n !('listener' in o && o.listener != null)\n );\n}\n\n/**\n * Validate init config. Throws with clear message on invalid config.\n * appId and environment are always taken from init options (e.g. from env files); when configUrl/getConfig is set, only listener details come from the remote source.\n */\nexport function validateConfig(options: AnalyticsInitConfig): void {\n const useRemoteConfig = Boolean(options.configUrl ?? options.getConfig);\n if (typeof options.appId !== 'string' || options.appId.trim() === '') {\n throw new Error('Analytics init failed: appId is required and must be a non-empty string (e.g. from environment variables).');\n }\n if (options.listeners != null && !Array.isArray(options.listeners)) {\n throw new Error('Analytics init failed: listeners must be an array.');\n }\n const behavior = options.preInitBehavior ?? 'drop';\n if (!VALID_PRE_INIT_BEHAVIORS.includes(behavior)) {\n throw new Error(\n `Analytics init failed: preInitBehavior must be 'queue' or 'drop', got '${behavior}'.`\n );\n }\n if (behavior === 'queue') {\n const maxSize = options.queueMaxSize ?? 100;\n const ttlMs = options.queueTtlMs ?? 5000;\n if (typeof maxSize !== 'number' || maxSize <= 0) {\n throw new Error('Analytics init failed: queueMaxSize must be a positive number.');\n }\n if (typeof ttlMs !== 'number' || ttlMs <= 0) {\n throw new Error('Analytics init failed: queueTtlMs must be a positive number.');\n }\n }\n if (useRemoteConfig) {\n return;\n }\n const listeners = options.listeners ?? [];\n const isRemoteShape = listeners.length > 0 && listeners.every(isRemoteListenerEntry);\n if (isRemoteShape) {\n for (let i = 0; i < listeners.length; i++) {\n const entry = listeners[i] as RemoteListenerEntry;\n if (entry.enabled === false) continue;\n if (typeof entry.id !== 'string' || entry.id.trim() === '') {\n throw new Error(`Analytics init failed: listeners[${i}].id is required and must be a non-empty string.`);\n }\n }\n return;\n }\n for (let i = 0; i < listeners.length; i++) {\n const entry = listeners[i] as ListenerEntry;\n if (entry.enabled === false) continue;\n if (entry.listener == null || typeof entry.listener !== 'object') {\n throw new Error(`Analytics init failed: listeners[${i}].listener is required.`);\n }\n const listener = entry.listener as AnalyticsListener;\n if (typeof listener.track !== 'function') {\n throw new Error(`Analytics init failed: listeners[${i}].listener must have a track method.`);\n }\n }\n}\n","/** SDK version; set at build time or from package.json. */\nexport const SDK_VERSION = '1.0.0';\n","import type {\n AnalyticsInitConfig,\n ListenerEntry,\n OnErrorContext,\n RemoteAnalyticsConfig,\n RemoteListenerEntry,\n} from './types/config.js';\nimport { PreInitBuffer } from './buffer/pre-init-buffer.js';\nimport { ListenerRegistry } from './dispatcher/registry.js';\nimport { dispatchTrack, dispatchIdentify } from './dispatcher/dispatcher.js';\nimport type { DispatcherDeps } from './dispatcher/dispatcher.js';\nimport {\n createEnricherState,\n setUserId,\n clearUserId,\n type EnricherState,\n} from './enricher/enricher.js';\nimport { buildListenersFromRemoteConfig } from './listeners/registry.js';\nimport { validateConfig } from './validate-config.js';\nimport { SDK_VERSION } from './version.js';\n\nlet enricherState: EnricherState | null = null;\nlet registry: ListenerRegistry | null = null;\n/** Pre-init buffer. Lazy-created on first track/identify (queue mode, defaults); replaced at init() with user options, then flushed and set null after init. */\nlet preInitBuffer: PreInitBuffer | null = null;\nlet onError: ((error: Error, context?: OnErrorContext) => void) | undefined;\nlet initialized = false;\n\n/** Default buffer before init: queue mode so events before init can be replayed. */\nfunction getOrCreatePreInitBuffer(): PreInitBuffer {\n if (preInitBuffer == null) {\n preInitBuffer = new PreInitBuffer({\n mode: 'queue',\n maxSize: 100,\n ttlMs: 5000,\n });\n }\n return preInitBuffer;\n}\n\nfunction getEnricherState(): EnricherState {\n if (enricherState == null) {\n throw new Error('Analytics not initialized.');\n }\n return enricherState;\n}\n\nasync function fetchRemoteConfig(options: AnalyticsInitConfig): Promise<RemoteAnalyticsConfig | null> {\n if (options.getConfig) {\n return options.getConfig();\n }\n if (options.configUrl) {\n const fetchFn = options.fetch ?? globalThis.fetch;\n const res = await fetchFn(options.configUrl);\n if (!res.ok) {\n throw new Error(`Analytics config fetch failed: ${res.status} ${res.statusText}`);\n }\n const json = await res.json();\n if (json == null || typeof json !== 'object') {\n throw new Error('Analytics config response must be a JSON object.');\n }\n return json as RemoteAnalyticsConfig;\n }\n return null;\n}\n\nfunction isInlineRemoteShape(listeners: unknown): listeners is RemoteListenerEntry[] {\n if (!Array.isArray(listeners) || listeners.length === 0) return false;\n return listeners.every(\n (entry): entry is RemoteListenerEntry =>\n entry != null &&\n typeof entry === 'object' &&\n typeof (entry as RemoteListenerEntry).id === 'string' &&\n 'config' in entry &&\n !('listener' in entry && (entry as ListenerEntry).listener != null)\n );\n}\n\n/**\n * Initialize the SDK. Idempotent; calling again re-applies config.\n * Pre-queued events (if queue mode) are flushed after listeners are ready.\n * When configUrl or getConfig is set, SDK fetches config and auto-configures listeners from the built-in registry (or listenerFactories).\n */\nexport async function init(options: AnalyticsInitConfig): Promise<void> {\n validateConfig(options);\n\n let appId: string;\n let environment: string;\n let listeners: ListenerEntry[];\n\n const remote = await fetchRemoteConfig(options);\n if (remote != null) {\n appId = (options.appId ?? '').toString().trim();\n environment = options.environment ?? 'production';\n if (!appId) {\n throw new Error('Analytics init failed: appId is required (set in init options, e.g. from environment variables).');\n }\n listeners = buildListenersFromRemoteConfig(remote, {\n factories: options.listenerFactories,\n onError: options.onError,\n });\n } else {\n appId = options.appId.trim();\n environment = options.environment ?? 'production';\n const rawListeners = options.listeners ?? [];\n if (isInlineRemoteShape(rawListeners)) {\n listeners = buildListenersFromRemoteConfig(\n { listeners: rawListeners },\n {\n factories: options.listenerFactories,\n onError: options.onError,\n }\n );\n } else {\n listeners = rawListeners as ListenerEntry[];\n }\n }\n\n const preInitBehavior = options.preInitBehavior ?? 'drop';\n const queueMaxSize = options.queueMaxSize ?? 100;\n const queueTtlMs = options.queueTtlMs ?? 5000;\n onError = options.onError;\n\n const mode = preInitBehavior === 'queue' ? 'queue' : 'drop';\n preInitBuffer = new PreInitBuffer({ mode, maxSize: queueMaxSize, ttlMs: queueTtlMs });\n\n enricherState = createEnricherState(appId, environment, SDK_VERSION, options.sessionId);\n registry = new ListenerRegistry();\n\n for (const entry of listeners) {\n if (entry.enabled === false) continue;\n try {\n const initResult = entry.listener.init(entry.config);\n if (initResult instanceof Promise) {\n await initResult;\n }\n registry.add(entry.id, entry.listener);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error, { listenerId: entry.id });\n }\n }\n\n const deps: DispatcherDeps = {\n registry,\n getEnricherState,\n onError,\n };\n\n if (preInitBuffer) {\n if (preInitBehavior === 'queue') {\n preInitBuffer.flush(\n (payload) => dispatchTrack(deps, payload),\n (userId, traits) => {\n setUserId(enricherState!, userId);\n dispatchIdentify(deps, userId, traits);\n }\n );\n } else {\n preInitBuffer.flush(() => {}, () => {});\n }\n preInitBuffer = null;\n }\n\n initialized = true;\n}\n\n/**\n * Track an event. Fire-and-forget. Safe before init (queued or dropped per config).\n */\nexport function track(event: { name: string; properties?: Record<string, unknown>; timestamp?: string }): void {\n if (!initialized) {\n getOrCreatePreInitBuffer().pushTrack(event.name, event.properties, event.timestamp);\n return;\n }\n const deps: DispatcherDeps = {\n registry: registry!,\n getEnricherState,\n onError,\n };\n dispatchTrack(deps, event);\n}\n\n/**\n * Identify the user. Fire-and-forget. Safe before init (queued or dropped per config).\n */\nexport function identify(userId: string, traits?: Record<string, unknown>): void {\n if (!initialized) {\n getOrCreatePreInitBuffer().pushIdentify(userId, traits);\n return;\n }\n if (enricherState) setUserId(enricherState, userId);\n const deps: DispatcherDeps = {\n registry: registry!,\n getEnricherState,\n onError,\n };\n dispatchIdentify(deps, userId, traits);\n}\n\n/**\n * Flush listeners that batch (e.g. before page unload).\n */\nexport async function flush(): Promise<void> {\n if (!initialized || registry == null) return;\n const promises: Promise<void>[] = [];\n for (const { listener } of registry.getAll()) {\n if (typeof listener.flush === 'function') {\n const result = listener.flush();\n if (result instanceof Promise) promises.push(result);\n }\n }\n await Promise.all(promises);\n}\n\n/**\n * Clear queue and teardown listeners. Useful in tests or when identity changes.\n */\nexport async function reset(): Promise<void> {\n if (preInitBuffer) {\n preInitBuffer.flush(() => {}, () => {});\n }\n if (enricherState) clearUserId(enricherState);\n if (registry) {\n for (const { listener } of registry.getAll()) {\n if (typeof listener.teardown === 'function') {\n try {\n const result = listener.teardown();\n if (result instanceof Promise) await result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error, {});\n }\n }\n }\n registry.clear();\n }\n preInitBuffer = null;\n enricherState = null;\n registry = null;\n onError = undefined;\n initialized = false;\n}\n\nexport function isInitialized(): boolean {\n return initialized;\n}\n"],"mappings":"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","names":["registry","onError","isBrowser","onError"]}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Normalized event envelope passed to all listeners.
3
+ * All required fields are set by the enricher; optional fields may come from app or enricher.
4
+ */
5
+ interface EventEnvelope {
6
+ /** Event name (from track payload). */
7
+ name: string;
8
+ /** ISO 8601 timestamp. */
9
+ timestamp: string;
10
+ /** Unique event id (e.g. UUID v4). */
11
+ eventId: string;
12
+ /** Application id (from init config). */
13
+ appId: string;
14
+ /** Environment (e.g. production, staging, dev). */
15
+ environment: string;
16
+ /** SDK package version. */
17
+ sdkVersion: string;
18
+ /** Envelope schema version for evolution. */
19
+ version: number;
20
+ /** Optional event properties (JSON-serializable). */
21
+ properties?: Record<string, unknown>;
22
+ /** Optional session id (from config or generated). */
23
+ sessionId?: string;
24
+ /** Optional user id (from last identify). */
25
+ userId?: string;
26
+ }
27
+
28
+ /**
29
+ * Contract for analytics listeners (adapters).
30
+ * Listeners must not throw from track/identify; core still wraps each call in try/catch.
31
+ */
32
+ interface AnalyticsListener {
33
+ /**
34
+ * Initialize the listener with its config. Called once per listener at init.
35
+ * May throw; core will not register this listener and will call onError.
36
+ */
37
+ init(config: unknown): void | Promise<void>;
38
+ /**
39
+ * Receive a normalized event. Fire-and-forget; core does not await.
40
+ * Must not throw; catch internally and log or report.
41
+ */
42
+ track(envelope: EventEnvelope): void | Promise<void>;
43
+ /**
44
+ * Optional: set user identity for backends that support it.
45
+ * Must not throw. If not implemented, core skips calling.
46
+ */
47
+ identify?(userId: string, traits?: Record<string, unknown>): void | Promise<void>;
48
+ /**
49
+ * Optional: flush any buffered events (e.g. before page unload).
50
+ */
51
+ flush?(): void | Promise<void>;
52
+ /**
53
+ * Optional: teardown; called on reset(). Clear state, stop timers.
54
+ */
55
+ teardown?(): void | Promise<void>;
56
+ }
57
+
58
+ export type { AnalyticsListener as A, EventEnvelope as E };
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Normalized event envelope passed to all listeners.
3
+ * All required fields are set by the enricher; optional fields may come from app or enricher.
4
+ */
5
+ interface EventEnvelope {
6
+ /** Event name (from track payload). */
7
+ name: string;
8
+ /** ISO 8601 timestamp. */
9
+ timestamp: string;
10
+ /** Unique event id (e.g. UUID v4). */
11
+ eventId: string;
12
+ /** Application id (from init config). */
13
+ appId: string;
14
+ /** Environment (e.g. production, staging, dev). */
15
+ environment: string;
16
+ /** SDK package version. */
17
+ sdkVersion: string;
18
+ /** Envelope schema version for evolution. */
19
+ version: number;
20
+ /** Optional event properties (JSON-serializable). */
21
+ properties?: Record<string, unknown>;
22
+ /** Optional session id (from config or generated). */
23
+ sessionId?: string;
24
+ /** Optional user id (from last identify). */
25
+ userId?: string;
26
+ }
27
+
28
+ /**
29
+ * Contract for analytics listeners (adapters).
30
+ * Listeners must not throw from track/identify; core still wraps each call in try/catch.
31
+ */
32
+ interface AnalyticsListener {
33
+ /**
34
+ * Initialize the listener with its config. Called once per listener at init.
35
+ * May throw; core will not register this listener and will call onError.
36
+ */
37
+ init(config: unknown): void | Promise<void>;
38
+ /**
39
+ * Receive a normalized event. Fire-and-forget; core does not await.
40
+ * Must not throw; catch internally and log or report.
41
+ */
42
+ track(envelope: EventEnvelope): void | Promise<void>;
43
+ /**
44
+ * Optional: set user identity for backends that support it.
45
+ * Must not throw. If not implemented, core skips calling.
46
+ */
47
+ identify?(userId: string, traits?: Record<string, unknown>): void | Promise<void>;
48
+ /**
49
+ * Optional: flush any buffered events (e.g. before page unload).
50
+ */
51
+ flush?(): void | Promise<void>;
52
+ /**
53
+ * Optional: teardown; called on reset(). Clear state, stop timers.
54
+ */
55
+ teardown?(): void | Promise<void>;
56
+ }
57
+
58
+ export type { AnalyticsListener as A, EventEnvelope as E };
package/dist/noop.cjs ADDED
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // noop.ts
21
+ var noop_exports = {};
22
+ __export(noop_exports, {
23
+ createNoopListener: () => createNoopListener,
24
+ noopListener: () => noopListener
25
+ });
26
+ module.exports = __toCommonJS(noop_exports);
27
+
28
+ // src/listeners/noop.ts
29
+ function createNoopListener() {
30
+ return {
31
+ init(_config) {
32
+ },
33
+ track(_envelope) {
34
+ }
35
+ };
36
+ }
37
+ var noopListener = createNoopListener();
38
+ // Annotate the CommonJS export names for ESM import in node:
39
+ 0 && (module.exports = {
40
+ createNoopListener,
41
+ noopListener
42
+ });
43
+ //# sourceMappingURL=noop.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../noop.ts","../src/listeners/noop.ts"],"sourcesContent":["/**\n * Entry point: @snovasys/usage-analytics-sdk/noop\n * Export no-op listener factory and instance for apps that want this listener.\n */\nexport { createNoopListener, noopListener } from './src/listeners/noop.js';\nexport type { AnalyticsListener, EventEnvelope } from './src/types/index.js';\n","import type { AnalyticsListener } from '../types/listener.js';\nimport type { EventEnvelope } from '../types/envelope.js';\n\n/**\n * No-op listener: implements the interface with no side effects.\n * Use when analytics is disabled or in tests.\n */\nexport function createNoopListener(): AnalyticsListener {\n return {\n init(_config: unknown): void {\n // no-op\n },\n track(_envelope: EventEnvelope): void {\n // no-op\n },\n };\n}\n\n/** Singleton no-op listener instance. */\nexport const noopListener: AnalyticsListener = createNoopListener();\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACOO,SAAS,qBAAwC;AACtD,SAAO;AAAA,IACL,KAAK,SAAwB;AAAA,IAE7B;AAAA,IACA,MAAM,WAAgC;AAAA,IAEtC;AAAA,EACF;AACF;AAGO,IAAM,eAAkC,mBAAmB;","names":[]}
@@ -0,0 +1,12 @@
1
+ import { A as AnalyticsListener } from './listener-CrNaKh1a.cjs';
2
+ export { E as EventEnvelope } from './listener-CrNaKh1a.cjs';
3
+
4
+ /**
5
+ * No-op listener: implements the interface with no side effects.
6
+ * Use when analytics is disabled or in tests.
7
+ */
8
+ declare function createNoopListener(): AnalyticsListener;
9
+ /** Singleton no-op listener instance. */
10
+ declare const noopListener: AnalyticsListener;
11
+
12
+ export { AnalyticsListener, createNoopListener, noopListener };
package/dist/noop.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ import { A as AnalyticsListener } from './listener-CrNaKh1a.js';
2
+ export { E as EventEnvelope } from './listener-CrNaKh1a.js';
3
+
4
+ /**
5
+ * No-op listener: implements the interface with no side effects.
6
+ * Use when analytics is disabled or in tests.
7
+ */
8
+ declare function createNoopListener(): AnalyticsListener;
9
+ /** Singleton no-op listener instance. */
10
+ declare const noopListener: AnalyticsListener;
11
+
12
+ export { AnalyticsListener, createNoopListener, noopListener };
package/dist/noop.js ADDED
@@ -0,0 +1,15 @@
1
+ // src/listeners/noop.ts
2
+ function createNoopListener() {
3
+ return {
4
+ init(_config) {
5
+ },
6
+ track(_envelope) {
7
+ }
8
+ };
9
+ }
10
+ var noopListener = createNoopListener();
11
+ export {
12
+ createNoopListener,
13
+ noopListener
14
+ };
15
+ //# sourceMappingURL=noop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/listeners/noop.ts"],"sourcesContent":["import type { AnalyticsListener } from '../types/listener.js';\nimport type { EventEnvelope } from '../types/envelope.js';\n\n/**\n * No-op listener: implements the interface with no side effects.\n * Use when analytics is disabled or in tests.\n */\nexport function createNoopListener(): AnalyticsListener {\n return {\n init(_config: unknown): void {\n // no-op\n },\n track(_envelope: EventEnvelope): void {\n // no-op\n },\n };\n}\n\n/** Singleton no-op listener instance. */\nexport const noopListener: AnalyticsListener = createNoopListener();\n"],"mappings":";AAOO,SAAS,qBAAwC;AACtD,SAAO;AAAA,IACL,KAAK,SAAwB;AAAA,IAE7B;AAAA,IACA,MAAM,WAAgC;AAAA,IAEtC;AAAA,EACF;AACF;AAGO,IAAM,eAAkC,mBAAmB;","names":[]}
@@ -0,0 +1,2 @@
1
+ "use strict";var UsageAnalytics=(()=>{var h=class{constructor(t){this.queue=[];this.options=t,this.isBrowser=t.isBrowserOverride!==void 0?t.isBrowserOverride:typeof window<"u"}pushTrack(t,n,r){if(this.options.mode==="drop"||!this.isBrowser)return;let o={type:"track",name:t,properties:n,timestamp:r,createdAt:Date.now()};this.push(o)}pushIdentify(t,n){if(this.options.mode==="drop"||!this.isBrowser)return;let r={type:"identify",userId:t,traits:n,createdAt:Date.now()};this.push(r)}push(t){for(;this.queue.length>=this.options.maxSize&&this.queue.length>0;)this.queue.shift();this.queue.push(t)}flush(t,n){if(!this.isBrowser||this.queue.length===0){this.queue.length=0;return}let o=Date.now()-this.options.ttlMs,i=this.queue.filter(a=>a.type==="identify"&&a.createdAt>=o),s=this.queue.filter(a=>a.type==="track"&&a.createdAt>=o);for(let a of i)n(a.userId,a.traits);for(let a of s)t({name:a.name,properties:a.properties,timestamp:a.timestamp});this.queue.length=0}get length(){return this.queue.length}};var E=class{constructor(){this.listeners=[]}add(t,n){this.listeners.push({id:t,listener:n})}getAll(){return[...this.listeners]}clear(){this.listeners.length=0}};var G="snovasys_usage_analytics_session_";function w(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():H()}function H(){let e="0123456789abcdef",t="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";return t=t.replace(/[xy]/g,n=>{let r=Math.random()*16|0,o=n==="x"?r:r&3|8;return e[o]}),t}function b(e,t){if(t)return t;let n=`${G}${e}`;if(typeof window<"u"&&typeof sessionStorage<"u")try{let r=sessionStorage.getItem(n);if(r)return r;let o=w();return sessionStorage.setItem(n,o),o}catch{return w()}return w()}var K=1;function T(e,t,n,r){let o=b(e,r);return{appId:e,environment:t,sdkVersion:n,sessionId:r,resolvedSessionId:o}}function _(e,t){let n=e.timestamp??new Date().toISOString(),r={name:e.name,timestamp:n,eventId:w(),appId:t.appId,environment:t.environment,sdkVersion:t.sdkVersion,version:K};return e.properties!=null&&(r.properties=e.properties),t.resolvedSessionId&&(r.sessionId=t.resolvedSessionId),t.userId&&(r.userId=t.userId),r}function A(e,t){e.userId=t}function B(e){e.userId=void 0}function x(e,t){let n=e.getEnricherState(),r=_(t,n);Y(e,r,"track")}function Y(e,t,n){let{registry:r,onError:o}=e;for(let{id:i,listener:s}of r.getAll())try{s.track(t)}catch(a){let l=a instanceof Error?a:new Error(String(a));o?.(l,{listenerId:i})}}function L(e,t,n){let{registry:r,onError:o}=e;for(let{id:i,listener:s}of r.getAll())if(typeof s.identify=="function")try{s.identify(t,n)}catch(a){let l=a instanceof Error?a:new Error(String(a));o?.(l,{listenerId:i})}}function S(){return{init(e){},track(e){}}}var ye=S();var J="https://www.clarity.ms/tag/";function I(){return typeof window<"u"}function W(e){return new Promise(t=>{if(!I()){t();return}if(typeof window.clarity=="function"){t();return}let n=document.createElement("script");n.async=!0,n.src=`${J}${encodeURIComponent(e)}`,n.onload=()=>t(),n.onerror=()=>t(),document.head.appendChild(n)})}function j(e){let t=e.projectId,n=!1;return{async init(r){try{let i=(r??e).projectId;if(!i||!I())return;await W(i),n=!0}catch{}},track(r){try{if(!I()||!n||typeof window.clarity!="function")return;if(window.clarity("event",r.name),r.properties&&typeof window.clarity=="function")for(let[o,i]of Object.entries(r.properties))(typeof i=="string"||Array.isArray(i))&&window.clarity("set",o,i)}catch{}},identify(r,o){try{if(!I()||!n||typeof window.clarity!="function")return;window.clarity("identify",r,void 0,void 0,void 0)}catch{}}}}var Z="https://www.googletagmanager.com/gtag/js";function v(){return typeof window<"u"}function X(e){return new Promise(t=>{if(!v()){t();return}if(window.gtag){window.gtag("config",e),t();return}window.dataLayer=window.dataLayer??[];let n=(...o)=>{window.dataLayer.push(o)};window.gtag=n,n("js",new Date);let r=document.createElement("script");r.async=!0,r.src=`${Z}?id=${encodeURIComponent(e)}`,r.onload=()=>{n("config",e),t()},r.onerror=()=>t(),document.head.appendChild(r)})}function P(e){let t=e.measurementId,n=!1;return{async init(r){try{let i=(r??e).measurementId;if(!i||!v())return;await X(i),n=!0}catch{}},track(r){try{if(!v()||!n||typeof window.gtag!="function")return;let o=r.properties??{};r.userId&&(o.user_id=r.userId),window.gtag("event",r.name,o)}catch{}},identify(r,o){try{if(!v()||typeof window.gtag!="function")return;window.gtag("set","user_properties",{user_id:r,...o})}catch{}}}}function q(e){let t=e.endpoint,n=e.apiKey,r=e.batchSize??1,o=e.flushIntervalMs??5e3,i=[],s=null;function a(){s==null&&(s=setTimeout(()=>{s=null,l()},o))}function l(){if(i.length===0)return;let f=[...i];i=[];let c=JSON.stringify(f.length===1?f[0]:{events:f}),u={"Content-Type":"application/json"};n&&(u.Authorization=`Bearer ${n}`),typeof fetch<"u"&&fetch(t,{method:"POST",headers:u,body:c,keepalive:!0}).catch(()=>{})}return{init(f){},track(f){try{i.push(f),i.length>=r?l():o>0&&a()}catch{}},flush(){return s!=null&&(clearTimeout(s),s=null),l(),Promise.resolve()},teardown(){s!=null&&(clearTimeout(s),s=null),i.length=0}}}var Q={noop:()=>S(),clarity:e=>j(e),ga:e=>P(e),"custom-api":e=>q(e)};function C(e,t){let n=t?.factories??Q,r=t?.onError,o=[];if(!Array.isArray(e.listeners))return o;for(let i of e.listeners){if(i.enabled===!1)continue;let s=i.id;if(s==null||typeof s!="string")continue;let a=n[s];if(a==null){r?.(new Error(`Unknown listener id: ${s}`),{listenerId:s});continue}let l;try{l=a(i.config)}catch(f){let c=f instanceof Error?f:new Error(String(f));r?.(c,{listenerId:s});continue}o.push({id:s,enabled:i.enabled,listener:l,config:i.config})}return o}var ee=["drop","queue"];function te(e){if(e==null||typeof e!="object")return!1;let t=e;return typeof t.id=="string"&&t.id.trim()!==""&&"config"in t&&!("listener"in t&&t.listener!=null)}function D(e){let t=!!(e.configUrl??e.getConfig);if(typeof e.appId!="string"||e.appId.trim()==="")throw new Error("Analytics init failed: appId is required and must be a non-empty string (e.g. from environment variables).");if(e.listeners!=null&&!Array.isArray(e.listeners))throw new Error("Analytics init failed: listeners must be an array.");let n=e.preInitBehavior??"drop";if(!ee.includes(n))throw new Error(`Analytics init failed: preInitBehavior must be 'queue' or 'drop', got '${n}'.`);if(n==="queue"){let i=e.queueMaxSize??100,s=e.queueTtlMs??5e3;if(typeof i!="number"||i<=0)throw new Error("Analytics init failed: queueMaxSize must be a positive number.");if(typeof s!="number"||s<=0)throw new Error("Analytics init failed: queueTtlMs must be a positive number.")}if(t)return;let r=e.listeners??[];if(r.length>0&&r.every(te)){for(let i=0;i<r.length;i++){let s=r[i];if(s.enabled!==!1&&(typeof s.id!="string"||s.id.trim()===""))throw new Error(`Analytics init failed: listeners[${i}].id is required and must be a non-empty string.`)}return}for(let i=0;i<r.length;i++){let s=r[i];if(s.enabled===!1)continue;if(s.listener==null||typeof s.listener!="object")throw new Error(`Analytics init failed: listeners[${i}].listener is required.`);if(typeof s.listener.track!="function")throw new Error(`Analytics init failed: listeners[${i}].listener must have a track method.`)}}var O="1.0.0";var y=null,p=null,d=null,m,g=!1;function F(){return d==null&&(d=new h({mode:"queue",maxSize:100,ttlMs:5e3})),d}function k(){if(y==null)throw new Error("Analytics not initialized.");return y}async function re(e){if(e.getConfig)return e.getConfig();if(e.configUrl){let n=await(e.fetch??globalThis.fetch)(e.configUrl);if(!n.ok)throw new Error(`Analytics config fetch failed: ${n.status} ${n.statusText}`);let r=await n.json();if(r==null||typeof r!="object")throw new Error("Analytics config response must be a JSON object.");return r}return null}function ne(e){return!Array.isArray(e)||e.length===0?!1:e.every(t=>t!=null&&typeof t=="object"&&typeof t.id=="string"&&"config"in t&&!("listener"in t&&t.listener!=null))}async function R(e){D(e);let t,n,r,o=await re(e);if(o!=null){if(t=(e.appId??"").toString().trim(),n=e.environment??"production",!t)throw new Error("Analytics init failed: appId is required (set in init options, e.g. from environment variables).");r=C(o,{factories:e.listenerFactories,onError:e.onError})}else{t=e.appId.trim(),n=e.environment??"production";let c=e.listeners??[];ne(c)?r=C({listeners:c},{factories:e.listenerFactories,onError:e.onError}):r=c}let i=e.preInitBehavior??"drop",s=e.queueMaxSize??100,a=e.queueTtlMs??5e3;m=e.onError;let l=i==="queue"?"queue":"drop";d=new h({mode:l,maxSize:s,ttlMs:a}),y=T(t,n,O,e.sessionId),p=new E;for(let c of r)if(c.enabled!==!1)try{let u=c.listener.init(c.config);u instanceof Promise&&await u,p.add(c.id,c.listener)}catch(u){let V=u instanceof Error?u:new Error(String(u));m?.(V,{listenerId:c.id})}let f={registry:p,getEnricherState:k,onError:m};d&&(i==="queue"?d.flush(c=>x(f,c),(c,u)=>{A(y,c),L(f,c,u)}):d.flush(()=>{},()=>{}),d=null),g=!0}function U(e){if(!g){F().pushTrack(e.name,e.properties,e.timestamp);return}x({registry:p,getEnricherState:k,onError:m},e)}function M(e,t){if(!g){F().pushIdentify(e,t);return}y&&A(y,e),L({registry:p,getEnricherState:k,onError:m},e,t)}async function z(){if(!g||p==null)return;let e=[];for(let{listener:t}of p.getAll())if(typeof t.flush=="function"){let n=t.flush();n instanceof Promise&&e.push(n)}await Promise.all(e)}async function N(){if(d&&d.flush(()=>{},()=>{}),y&&B(y),p){for(let{listener:e}of p.getAll())if(typeof e.teardown=="function")try{let t=e.teardown();t instanceof Promise&&await t}catch(t){let n=t instanceof Error?t:new Error(String(t));m?.(n,{})}p.clear()}d=null,y=null,p=null,m=void 0,g=!1}function $(){return g}var ie={init:R,track:U,identify:M,flush:z,reset:N,isInitialized:$};typeof globalThis<"u"&&(globalThis.UsageAnalytics=ie);function oe(){if(typeof document>"u")return null;let e=document.currentScript;if(!e)return null;let t=e.getAttribute("data-config");if(!t||typeof t!="string")return null;try{let n=JSON.parse(t);if(n&&typeof n.appId=="string"&&n.appId.trim()!=="")return n}catch{}return null}function se(){if(typeof window>"u")return;let e=oe()??window.__ANALYTICS_CONFIG__;e&&R(e).catch(()=>{})}se();})();
2
+ //# sourceMappingURL=usage-analytics.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/buffer/pre-init-buffer.ts","../src/dispatcher/registry.ts","../src/enricher/session.ts","../src/enricher/enricher.ts","../src/dispatcher/dispatcher.ts","../src/listeners/noop.ts","../src/listeners/clarity.ts","../src/listeners/ga.ts","../src/listeners/custom-api.ts","../src/listeners/registry.ts","../src/validate-config.ts","../src/version.ts","../src/facade.ts","../src/script-entry.ts"],"sourcesContent":["import type { BufferedItem, BufferedTrack, BufferedIdentify } from './types.js';\n\nexport interface PreInitBufferOptions {\n /** 'drop' = no-op; 'queue' = FIFO queue. In SSR (no window) always drop. */\n mode: 'drop' | 'queue';\n maxSize: number;\n ttlMs: number;\n /** For testing: override browser check so queue works in Node. */\n isBrowserOverride?: boolean;\n}\n\nexport type FlushTrackHandler = (payload: { name: string; properties?: Record<string, unknown>; timestamp?: string }) => void;\nexport type FlushIdentifyHandler = (userId: string, traits?: Record<string, unknown>) => void;\n\n/**\n * Pre-init buffer: either drops events or queues them (FIFO) until flush.\n * In Node/SSR (typeof window === 'undefined') always behaves as drop.\n */\nexport class PreInitBuffer {\n private readonly options: PreInitBufferOptions;\n private readonly queue: BufferedItem[] = [];\n private readonly isBrowser: boolean;\n\n constructor(options: PreInitBufferOptions) {\n this.options = options;\n this.isBrowser =\n options.isBrowserOverride !== undefined ? options.isBrowserOverride : typeof window !== 'undefined';\n }\n\n /**\n * Push a track call. No-op if mode is drop or not browser (SSR).\n */\n pushTrack(name: string, properties?: Record<string, unknown>, timestamp?: string): void {\n if (this.options.mode === 'drop' || !this.isBrowser) return;\n const item: BufferedTrack = {\n type: 'track',\n name,\n properties,\n timestamp,\n createdAt: Date.now(),\n };\n this.push(item);\n }\n\n /**\n * Push an identify call. No-op if mode is drop or not browser (SSR).\n */\n pushIdentify(userId: string, traits?: Record<string, unknown>): void {\n if (this.options.mode === 'drop' || !this.isBrowser) return;\n const item: BufferedIdentify = {\n type: 'identify',\n userId,\n traits,\n createdAt: Date.now(),\n };\n this.push(item);\n }\n\n private push(item: BufferedItem): void {\n while (this.queue.length >= this.options.maxSize && this.queue.length > 0) {\n this.queue.shift(); // drop oldest (FIFO)\n }\n this.queue.push(item);\n }\n\n /**\n * Replay queued items: all identify first (order), then all track (order).\n * Items older than ttlMs are dropped. Then clear the queue.\n */\n flush(\n onTrack: FlushTrackHandler,\n onIdentify: FlushIdentifyHandler\n ): void {\n if (!this.isBrowser || this.queue.length === 0) {\n this.queue.length = 0;\n return;\n }\n const now = Date.now();\n const cutoff = now - this.options.ttlMs;\n const identifyItems = this.queue.filter(\n (item): item is BufferedIdentify => item.type === 'identify' && item.createdAt >= cutoff\n );\n const trackItems = this.queue.filter(\n (item): item is BufferedTrack => item.type === 'track' && item.createdAt >= cutoff\n );\n for (const item of identifyItems) {\n onIdentify(item.userId, item.traits);\n }\n for (const item of trackItems) {\n onTrack({\n name: item.name,\n properties: item.properties,\n timestamp: item.timestamp,\n });\n }\n this.queue.length = 0;\n }\n\n /** Number of items currently queued (for tests). */\n get length(): number {\n return this.queue.length;\n }\n}\n","import type { AnalyticsListener } from '../types/listener.js';\n\nexport interface RegisteredListener {\n id?: string;\n listener: AnalyticsListener;\n}\n\n/**\n * Holds enabled listener instances. Populated at init.\n */\nexport class ListenerRegistry {\n private readonly listeners: RegisteredListener[] = [];\n\n add(id: string | undefined, listener: AnalyticsListener): void {\n this.listeners.push({ id, listener });\n }\n\n getAll(): RegisteredListener[] {\n return [...this.listeners];\n }\n\n clear(): void {\n this.listeners.length = 0;\n }\n}\n","const STORAGE_KEY_PREFIX = 'snovasys_usage_analytics_session_';\n\n/**\n * Generate a UUID v4 (uses crypto.randomUUID when available, else fallback).\n */\nexport function generateId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return fallbackUuidV4();\n}\n\nfunction fallbackUuidV4(): string {\n const hex = '0123456789abcdef';\n let result = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';\n result = result.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return hex[v];\n });\n return result;\n}\n\n/**\n * Get or create session id. Optionally persist in sessionStorage keyed by appId.\n */\nexport function getOrCreateSessionId(appId: string, providedSessionId?: string): string {\n if (providedSessionId) return providedSessionId;\n const key = `${STORAGE_KEY_PREFIX}${appId}`;\n if (typeof window !== 'undefined' && typeof sessionStorage !== 'undefined') {\n try {\n const existing = sessionStorage.getItem(key);\n if (existing) return existing;\n const newId = generateId();\n sessionStorage.setItem(key, newId);\n return newId;\n } catch {\n return generateId();\n }\n }\n return generateId();\n}\n","import type { EventEnvelope } from '../types/envelope.js';\nimport { generateId, getOrCreateSessionId } from './session.js';\n\n/** Raw track payload from app (before enrichment). */\nexport interface TrackPayload {\n name: string;\n properties?: Record<string, unknown>;\n timestamp?: string;\n}\n\nexport interface EnricherState {\n appId: string;\n environment: string;\n sdkVersion: string;\n sessionId?: string;\n userId?: string;\n /** Provided at init or generated per session. */\n resolvedSessionId?: string;\n}\n\nconst ENVELOPE_VERSION = 1;\n\n/**\n * Creates enricher state from init config. Call once at init.\n */\nexport function createEnricherState(\n appId: string,\n environment: string,\n sdkVersion: string,\n sessionId?: string\n): EnricherState {\n const resolvedSessionId = getOrCreateSessionId(appId, sessionId);\n return {\n appId,\n environment,\n sdkVersion,\n sessionId,\n resolvedSessionId,\n };\n}\n\n/**\n * Enrich a raw track payload into a full EventEnvelope.\n */\nexport function enrich(\n payload: TrackPayload,\n state: EnricherState\n): EventEnvelope {\n const timestamp = payload.timestamp ?? new Date().toISOString();\n const envelope: EventEnvelope = {\n name: payload.name,\n timestamp,\n eventId: generateId(),\n appId: state.appId,\n environment: state.environment,\n sdkVersion: state.sdkVersion,\n version: ENVELOPE_VERSION,\n };\n if (payload.properties != null) envelope.properties = payload.properties;\n if (state.resolvedSessionId) envelope.sessionId = state.resolvedSessionId;\n if (state.userId) envelope.userId = state.userId;\n return envelope;\n}\n\n/**\n * Set userId (and optional traits) on state after identify. Cleared on reset.\n */\nexport function setUserId(state: EnricherState, userId: string): void {\n state.userId = userId;\n}\n\n/**\n * Clear userId from state (e.g. on reset).\n */\nexport function clearUserId(state: EnricherState): void {\n state.userId = undefined;\n}\n","import type { EventEnvelope } from '../types/envelope.js';\nimport type { OnErrorContext } from '../types/config.js';\nimport type { ListenerRegistry } from './registry.js';\nimport type { EnricherState } from '../enricher/enricher.js';\nimport { enrich, type TrackPayload } from '../enricher/enricher.js';\n\nexport interface DispatcherDeps {\n registry: ListenerRegistry;\n getEnricherState: () => EnricherState;\n onError?: (error: Error, context?: OnErrorContext) => void;\n}\n\n/**\n * Normalize payload → enrich → dispatch to all listeners (try/catch per listener).\n */\nexport function dispatchTrack(\n deps: DispatcherDeps,\n payload: TrackPayload\n): void {\n const state = deps.getEnricherState();\n const envelope = enrich(payload, state);\n dispatchEnvelope(deps, envelope, 'track');\n}\n\n/**\n * Dispatch a pre-built envelope to all listeners (e.g. after enrich).\n */\nfunction dispatchEnvelope(\n deps: DispatcherDeps,\n envelope: EventEnvelope,\n kind: 'track'\n): void {\n const { registry, onError } = deps;\n for (const { id, listener } of registry.getAll()) {\n try {\n listener.track(envelope);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error, { listenerId: id });\n }\n }\n}\n\n/**\n * Call identify on all listeners that implement it (try/catch per listener).\n */\nexport function dispatchIdentify(\n deps: DispatcherDeps,\n userId: string,\n traits?: Record<string, unknown>\n): void {\n const { registry, onError } = deps;\n for (const { id, listener } of registry.getAll()) {\n if (typeof listener.identify !== 'function') continue;\n try {\n listener.identify(userId, traits);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error, { listenerId: id });\n }\n }\n}\n","import type { AnalyticsListener } from '../types/listener.js';\nimport type { EventEnvelope } from '../types/envelope.js';\n\n/**\n * No-op listener: implements the interface with no side effects.\n * Use when analytics is disabled or in tests.\n */\nexport function createNoopListener(): AnalyticsListener {\n return {\n init(_config: unknown): void {\n // no-op\n },\n track(_envelope: EventEnvelope): void {\n // no-op\n },\n };\n}\n\n/** Singleton no-op listener instance. */\nexport const noopListener: AnalyticsListener = createNoopListener();\n","import type { AnalyticsListener } from '../types/listener.js';\nimport type { EventEnvelope } from '../types/envelope.js';\n\nexport interface ClarityListenerConfig {\n projectId: string;\n}\n\nconst CLARITY_SCRIPT_BASE = 'https://www.clarity.ms/tag/';\n\ndeclare global {\n interface Window {\n clarity?: (cmd: string, ...args: unknown[]) => void;\n }\n}\n\nfunction isBrowser(): boolean {\n return typeof window !== 'undefined';\n}\n\nfunction loadClarityScript(projectId: string): Promise<void> {\n return new Promise((resolve) => {\n if (!isBrowser()) {\n resolve();\n return;\n }\n if (typeof window.clarity === 'function') {\n resolve();\n return;\n }\n const script = document.createElement('script');\n script.async = true;\n script.src = `${CLARITY_SCRIPT_BASE}${encodeURIComponent(projectId)}`;\n script.onload = () => resolve();\n script.onerror = () => resolve();\n document.head.appendChild(script);\n });\n}\n\n/**\n * Listener that sends events to Microsoft Clarity via script injection.\n * Loads Clarity script with projectId; track → clarity(\"event\", name); identify → clarity(\"identify\", userId, ...).\n * No-op when typeof window === 'undefined' (SSR).\n */\nexport function createClarityListener(config: ClarityListenerConfig): AnalyticsListener {\n const projectId = config.projectId;\n let ready = false;\n\n return {\n async init(cfg: unknown): Promise<void> {\n try {\n const c = (cfg ?? config) as ClarityListenerConfig;\n const id = c.projectId;\n if (!id || !isBrowser()) return;\n await loadClarityScript(id);\n ready = true;\n } catch {\n // swallow\n }\n },\n track(envelope: EventEnvelope): void {\n try {\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\n window.clarity('event', envelope.name);\n if (envelope.properties && typeof window.clarity === 'function') {\n for (const [key, value] of Object.entries(envelope.properties)) {\n if (typeof value === 'string' || Array.isArray(value)) {\n window.clarity('set', key, value);\n }\n }\n }\n } catch {\n // swallow\n }\n },\n identify(userId: string, _traits?: Record<string, unknown>): void {\n try {\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\n window.clarity('identify', userId, undefined, undefined, undefined);\n } catch {\n // swallow\n }\n },\n };\n}\n","import type { AnalyticsListener } from '../types/listener.js';\nimport type { EventEnvelope } from '../types/envelope.js';\n\nexport interface GAListenerConfig {\n measurementId: string;\n}\n\nconst GTAG_SCRIPT_URL = 'https://www.googletagmanager.com/gtag/js';\n\ndeclare global {\n interface Window {\n dataLayer?: unknown[];\n gtag?: (...args: unknown[]) => void;\n }\n}\n\nfunction isBrowser(): boolean {\n return typeof window !== 'undefined';\n}\n\nfunction loadGtagScript(measurementId: string): Promise<void> {\n return new Promise((resolve) => {\n if (!isBrowser()) {\n resolve();\n return;\n }\n if (window.gtag) {\n window.gtag('config', measurementId);\n resolve();\n return;\n }\n window.dataLayer = window.dataLayer ?? [];\n const gtag = (...args: unknown[]) => {\n window.dataLayer!.push(args);\n };\n window.gtag = gtag;\n gtag('js', new Date());\n const script = document.createElement('script');\n script.async = true;\n script.src = `${GTAG_SCRIPT_URL}?id=${encodeURIComponent(measurementId)}`;\n script.onload = () => {\n gtag('config', measurementId);\n resolve();\n };\n script.onerror = () => resolve();\n document.head.appendChild(script);\n });\n}\n\n/**\n * Listener that sends events to Google Analytics 4 via gtag.\n * Loads gtag script with measurementId; track → gtag('event', name, properties).\n * No-op when typeof window === 'undefined' (SSR).\n */\nexport function createGAListener(config: GAListenerConfig): AnalyticsListener {\n const measurementId = config.measurementId;\n let ready = false;\n\n return {\n async init(cfg: unknown): Promise<void> {\n try {\n const c = (cfg ?? config) as GAListenerConfig;\n const id = c.measurementId;\n if (!id || !isBrowser()) return;\n await loadGtagScript(id);\n ready = true;\n } catch {\n // swallow\n }\n },\n track(envelope: EventEnvelope): void {\n try {\n if (!isBrowser() || !ready || typeof window.gtag !== 'function') return;\n const params = envelope.properties ?? {};\n if (envelope.userId) (params as Record<string, unknown>)['user_id'] = envelope.userId;\n window.gtag('event', envelope.name, params);\n } catch {\n // swallow\n }\n },\n identify(userId: string, traits?: Record<string, unknown>): void {\n try {\n if (!isBrowser() || typeof window.gtag !== 'function') return;\n window.gtag('set', 'user_properties', { user_id: userId, ...traits });\n } catch {\n // swallow\n }\n },\n };\n}\n","import type { AnalyticsListener } from '../types/listener.js';\nimport type { EventEnvelope } from '../types/envelope.js';\n\nexport interface CustomApiListenerConfig {\n /** Endpoint URL for POST (e.g. https://api.example.com/analytics/events). */\n endpoint: string;\n /** Optional API key or bearer token. */\n apiKey?: string;\n /** Optional: batch size before sending. Default 1 (no batching). */\n batchSize?: number;\n /** Optional: max ms to wait before sending a partial batch. Default 5000. */\n flushIntervalMs?: number;\n}\n\nconst DEFAULT_BATCH_SIZE = 1;\nconst DEFAULT_FLUSH_INTERVAL_MS = 5000;\n\n/**\n * Listener that POSTs events to a configurable endpoint.\n * Optional batching: accumulates events and sends when batchSize or flushIntervalMs is reached.\n */\nexport function createCustomApiListener(config: CustomApiListenerConfig): AnalyticsListener {\n const endpoint = config.endpoint;\n const apiKey = config.apiKey;\n const batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE;\n const flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n\n let batch: EventEnvelope[] = [];\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n\n function scheduleFlush(): void {\n if (flushTimer != null) return;\n flushTimer = setTimeout(() => {\n flushTimer = null;\n sendBatch();\n }, flushIntervalMs);\n }\n\n function sendBatch(): void {\n if (batch.length === 0) return;\n const toSend = [...batch];\n batch = [];\n const body = JSON.stringify(toSend.length === 1 ? toSend[0] : { events: toSend });\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;\n if (typeof fetch !== 'undefined') {\n fetch(endpoint, {\n method: 'POST',\n headers,\n body,\n keepalive: true,\n }).catch(() => {});\n }\n }\n\n return {\n init(_config: unknown): void {\n // config was passed at createCustomApiListener\n },\n track(envelope: EventEnvelope): void {\n try {\n batch.push(envelope);\n if (batch.length >= batchSize) {\n sendBatch();\n } else if (flushIntervalMs > 0) {\n scheduleFlush();\n }\n } catch {\n // swallow\n }\n },\n flush(): Promise<void> {\n if (flushTimer != null) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n sendBatch();\n return Promise.resolve();\n },\n teardown(): void {\n if (flushTimer != null) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n batch.length = 0;\n },\n };\n}\n","import type { ListenerEntry, ListenerFactory, RemoteAnalyticsConfig } from '../types/config.js';\nimport type { AnalyticsListener } from '../types/listener.js';\nimport { createNoopListener } from './noop.js';\nimport { createClarityListener } from './clarity.js';\nimport { createGAListener } from './ga.js';\nimport { createCustomApiListener } from './custom-api.js';\n\n/** Built-in listener IDs supported by the SDK (noop, clarity, ga, custom-api). */\nexport const BUILT_IN_LISTENER_IDS = ['noop', 'clarity', 'ga', 'custom-api'] as const;\n\nconst BUILT_IN_FACTORIES: Record<string, ListenerFactory> = {\n noop: () => createNoopListener(),\n clarity: (config) => createClarityListener(config as { projectId: string }),\n ga: (config) => createGAListener(config as { measurementId: string }),\n 'custom-api': (config) => createCustomApiListener(config as Parameters<typeof createCustomApiListener>[0]),\n};\n\n/**\n * Returns the built-in listener factory for the given id, or undefined if unknown.\n */\nexport function getBuiltInListenerFactory(id: string): ListenerFactory | undefined {\n return BUILT_IN_FACTORIES[id];\n}\n\nexport interface BuildListenersFromRemoteConfigOptions {\n /** App override: map listener id to factory. When omitted, built-in registry is used. */\n factories?: Record<string, ListenerFactory>;\n /** Called when a remote listener id has no factory (unknown id). */\n onError?: (error: Error, context?: { listenerId?: string }) => void;\n}\n\n/**\n * Builds ListenerEntry[] from remote config. Uses options.factories when provided,\n * otherwise the built-in registry. Skips entries with enabled === false or unknown id.\n */\nexport function buildListenersFromRemoteConfig(\n remote: RemoteAnalyticsConfig,\n options?: BuildListenersFromRemoteConfigOptions\n): ListenerEntry[] {\n const factories = options?.factories ?? BUILT_IN_FACTORIES;\n const onError = options?.onError;\n const entries: ListenerEntry[] = [];\n\n if (!Array.isArray(remote.listeners)) {\n return entries;\n }\n\n for (const entry of remote.listeners) {\n if (entry.enabled === false) continue;\n const id = entry.id;\n if (id == null || typeof id !== 'string') continue;\n\n const factory = factories[id];\n if (factory == null) {\n onError?.(new Error(`Unknown listener id: ${id}`), { listenerId: id });\n continue;\n }\n\n let listener: AnalyticsListener;\n try {\n listener = factory(entry.config);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error, { listenerId: id });\n continue;\n }\n\n entries.push({\n id,\n enabled: entry.enabled,\n listener,\n config: entry.config,\n });\n }\n\n return entries;\n}\n","import type {\n AnalyticsInitConfig,\n ListenerEntry,\n PreInitBehavior,\n RemoteListenerEntry,\n} from './types/config.js';\nimport type { AnalyticsListener } from './types/listener.js';\n\nconst VALID_PRE_INIT_BEHAVIORS: PreInitBehavior[] = ['drop', 'queue'];\n\nfunction isRemoteListenerEntry(entry: unknown): entry is RemoteListenerEntry {\n if (entry == null || typeof entry !== 'object') return false;\n const o = entry as Record<string, unknown>;\n return (\n typeof o.id === 'string' &&\n o.id.trim() !== '' &&\n 'config' in o &&\n !('listener' in o && o.listener != null)\n );\n}\n\n/**\n * Validate init config. Throws with clear message on invalid config.\n * appId and environment are always taken from init options (e.g. from env files); when configUrl/getConfig is set, only listener details come from the remote source.\n */\nexport function validateConfig(options: AnalyticsInitConfig): void {\n const useRemoteConfig = Boolean(options.configUrl ?? options.getConfig);\n if (typeof options.appId !== 'string' || options.appId.trim() === '') {\n throw new Error('Analytics init failed: appId is required and must be a non-empty string (e.g. from environment variables).');\n }\n if (options.listeners != null && !Array.isArray(options.listeners)) {\n throw new Error('Analytics init failed: listeners must be an array.');\n }\n const behavior = options.preInitBehavior ?? 'drop';\n if (!VALID_PRE_INIT_BEHAVIORS.includes(behavior)) {\n throw new Error(\n `Analytics init failed: preInitBehavior must be 'queue' or 'drop', got '${behavior}'.`\n );\n }\n if (behavior === 'queue') {\n const maxSize = options.queueMaxSize ?? 100;\n const ttlMs = options.queueTtlMs ?? 5000;\n if (typeof maxSize !== 'number' || maxSize <= 0) {\n throw new Error('Analytics init failed: queueMaxSize must be a positive number.');\n }\n if (typeof ttlMs !== 'number' || ttlMs <= 0) {\n throw new Error('Analytics init failed: queueTtlMs must be a positive number.');\n }\n }\n if (useRemoteConfig) {\n return;\n }\n const listeners = options.listeners ?? [];\n const isRemoteShape = listeners.length > 0 && listeners.every(isRemoteListenerEntry);\n if (isRemoteShape) {\n for (let i = 0; i < listeners.length; i++) {\n const entry = listeners[i] as RemoteListenerEntry;\n if (entry.enabled === false) continue;\n if (typeof entry.id !== 'string' || entry.id.trim() === '') {\n throw new Error(`Analytics init failed: listeners[${i}].id is required and must be a non-empty string.`);\n }\n }\n return;\n }\n for (let i = 0; i < listeners.length; i++) {\n const entry = listeners[i] as ListenerEntry;\n if (entry.enabled === false) continue;\n if (entry.listener == null || typeof entry.listener !== 'object') {\n throw new Error(`Analytics init failed: listeners[${i}].listener is required.`);\n }\n const listener = entry.listener as AnalyticsListener;\n if (typeof listener.track !== 'function') {\n throw new Error(`Analytics init failed: listeners[${i}].listener must have a track method.`);\n }\n }\n}\n","/** SDK version; set at build time or from package.json. */\nexport const SDK_VERSION = '1.0.0';\n","import type {\n AnalyticsInitConfig,\n ListenerEntry,\n OnErrorContext,\n RemoteAnalyticsConfig,\n RemoteListenerEntry,\n} from './types/config.js';\nimport { PreInitBuffer } from './buffer/pre-init-buffer.js';\nimport { ListenerRegistry } from './dispatcher/registry.js';\nimport { dispatchTrack, dispatchIdentify } from './dispatcher/dispatcher.js';\nimport type { DispatcherDeps } from './dispatcher/dispatcher.js';\nimport {\n createEnricherState,\n setUserId,\n clearUserId,\n type EnricherState,\n} from './enricher/enricher.js';\nimport { buildListenersFromRemoteConfig } from './listeners/registry.js';\nimport { validateConfig } from './validate-config.js';\nimport { SDK_VERSION } from './version.js';\n\nlet enricherState: EnricherState | null = null;\nlet registry: ListenerRegistry | null = null;\n/** Pre-init buffer. Lazy-created on first track/identify (queue mode, defaults); replaced at init() with user options, then flushed and set null after init. */\nlet preInitBuffer: PreInitBuffer | null = null;\nlet onError: ((error: Error, context?: OnErrorContext) => void) | undefined;\nlet initialized = false;\n\n/** Default buffer before init: queue mode so events before init can be replayed. */\nfunction getOrCreatePreInitBuffer(): PreInitBuffer {\n if (preInitBuffer == null) {\n preInitBuffer = new PreInitBuffer({\n mode: 'queue',\n maxSize: 100,\n ttlMs: 5000,\n });\n }\n return preInitBuffer;\n}\n\nfunction getEnricherState(): EnricherState {\n if (enricherState == null) {\n throw new Error('Analytics not initialized.');\n }\n return enricherState;\n}\n\nasync function fetchRemoteConfig(options: AnalyticsInitConfig): Promise<RemoteAnalyticsConfig | null> {\n if (options.getConfig) {\n return options.getConfig();\n }\n if (options.configUrl) {\n const fetchFn = options.fetch ?? globalThis.fetch;\n const res = await fetchFn(options.configUrl);\n if (!res.ok) {\n throw new Error(`Analytics config fetch failed: ${res.status} ${res.statusText}`);\n }\n const json = await res.json();\n if (json == null || typeof json !== 'object') {\n throw new Error('Analytics config response must be a JSON object.');\n }\n return json as RemoteAnalyticsConfig;\n }\n return null;\n}\n\nfunction isInlineRemoteShape(listeners: unknown): listeners is RemoteListenerEntry[] {\n if (!Array.isArray(listeners) || listeners.length === 0) return false;\n return listeners.every(\n (entry): entry is RemoteListenerEntry =>\n entry != null &&\n typeof entry === 'object' &&\n typeof (entry as RemoteListenerEntry).id === 'string' &&\n 'config' in entry &&\n !('listener' in entry && (entry as ListenerEntry).listener != null)\n );\n}\n\n/**\n * Initialize the SDK. Idempotent; calling again re-applies config.\n * Pre-queued events (if queue mode) are flushed after listeners are ready.\n * When configUrl or getConfig is set, SDK fetches config and auto-configures listeners from the built-in registry (or listenerFactories).\n */\nexport async function init(options: AnalyticsInitConfig): Promise<void> {\n validateConfig(options);\n\n let appId: string;\n let environment: string;\n let listeners: ListenerEntry[];\n\n const remote = await fetchRemoteConfig(options);\n if (remote != null) {\n appId = (options.appId ?? '').toString().trim();\n environment = options.environment ?? 'production';\n if (!appId) {\n throw new Error('Analytics init failed: appId is required (set in init options, e.g. from environment variables).');\n }\n listeners = buildListenersFromRemoteConfig(remote, {\n factories: options.listenerFactories,\n onError: options.onError,\n });\n } else {\n appId = options.appId.trim();\n environment = options.environment ?? 'production';\n const rawListeners = options.listeners ?? [];\n if (isInlineRemoteShape(rawListeners)) {\n listeners = buildListenersFromRemoteConfig(\n { listeners: rawListeners },\n {\n factories: options.listenerFactories,\n onError: options.onError,\n }\n );\n } else {\n listeners = rawListeners as ListenerEntry[];\n }\n }\n\n const preInitBehavior = options.preInitBehavior ?? 'drop';\n const queueMaxSize = options.queueMaxSize ?? 100;\n const queueTtlMs = options.queueTtlMs ?? 5000;\n onError = options.onError;\n\n const mode = preInitBehavior === 'queue' ? 'queue' : 'drop';\n preInitBuffer = new PreInitBuffer({ mode, maxSize: queueMaxSize, ttlMs: queueTtlMs });\n\n enricherState = createEnricherState(appId, environment, SDK_VERSION, options.sessionId);\n registry = new ListenerRegistry();\n\n for (const entry of listeners) {\n if (entry.enabled === false) continue;\n try {\n const initResult = entry.listener.init(entry.config);\n if (initResult instanceof Promise) {\n await initResult;\n }\n registry.add(entry.id, entry.listener);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error, { listenerId: entry.id });\n }\n }\n\n const deps: DispatcherDeps = {\n registry,\n getEnricherState,\n onError,\n };\n\n if (preInitBuffer) {\n if (preInitBehavior === 'queue') {\n preInitBuffer.flush(\n (payload) => dispatchTrack(deps, payload),\n (userId, traits) => {\n setUserId(enricherState!, userId);\n dispatchIdentify(deps, userId, traits);\n }\n );\n } else {\n preInitBuffer.flush(() => {}, () => {});\n }\n preInitBuffer = null;\n }\n\n initialized = true;\n}\n\n/**\n * Track an event. Fire-and-forget. Safe before init (queued or dropped per config).\n */\nexport function track(event: { name: string; properties?: Record<string, unknown>; timestamp?: string }): void {\n if (!initialized) {\n getOrCreatePreInitBuffer().pushTrack(event.name, event.properties, event.timestamp);\n return;\n }\n const deps: DispatcherDeps = {\n registry: registry!,\n getEnricherState,\n onError,\n };\n dispatchTrack(deps, event);\n}\n\n/**\n * Identify the user. Fire-and-forget. Safe before init (queued or dropped per config).\n */\nexport function identify(userId: string, traits?: Record<string, unknown>): void {\n if (!initialized) {\n getOrCreatePreInitBuffer().pushIdentify(userId, traits);\n return;\n }\n if (enricherState) setUserId(enricherState, userId);\n const deps: DispatcherDeps = {\n registry: registry!,\n getEnricherState,\n onError,\n };\n dispatchIdentify(deps, userId, traits);\n}\n\n/**\n * Flush listeners that batch (e.g. before page unload).\n */\nexport async function flush(): Promise<void> {\n if (!initialized || registry == null) return;\n const promises: Promise<void>[] = [];\n for (const { listener } of registry.getAll()) {\n if (typeof listener.flush === 'function') {\n const result = listener.flush();\n if (result instanceof Promise) promises.push(result);\n }\n }\n await Promise.all(promises);\n}\n\n/**\n * Clear queue and teardown listeners. Useful in tests or when identity changes.\n */\nexport async function reset(): Promise<void> {\n if (preInitBuffer) {\n preInitBuffer.flush(() => {}, () => {});\n }\n if (enricherState) clearUserId(enricherState);\n if (registry) {\n for (const { listener } of registry.getAll()) {\n if (typeof listener.teardown === 'function') {\n try {\n const result = listener.teardown();\n if (result instanceof Promise) await result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error, {});\n }\n }\n }\n registry.clear();\n }\n preInitBuffer = null;\n enricherState = null;\n registry = null;\n onError = undefined;\n initialized = false;\n}\n\nexport function isInitialized(): boolean {\n return initialized;\n}\n","/**\r\n * Script entry for browser <script src=\"...\"> usage.\r\n * Exposes UsageAnalytics on globalThis (window). Auto-inits from config in the script tag (data-config) or window.__ANALYTICS_CONFIG__.\r\n */\r\nimport { init, track, identify, flush, reset, isInitialized } from './facade.js';\r\nimport type { AnalyticsInitConfig } from './types/config.js';\r\n\r\ndeclare global {\r\n interface Window {\r\n UsageAnalytics: {\r\n init: typeof init;\r\n track: typeof track;\r\n identify: typeof identify;\r\n flush: typeof flush;\r\n reset: typeof reset;\r\n isInitialized: typeof isInitialized;\r\n };\r\n __ANALYTICS_CONFIG__?: AnalyticsInitConfig;\r\n }\r\n}\r\n\r\nconst api = {\r\n init,\r\n track,\r\n identify,\r\n flush,\r\n reset,\r\n isInitialized,\r\n};\r\n\r\nif (typeof globalThis !== 'undefined') {\r\n (globalThis as unknown as { UsageAnalytics: typeof api }).UsageAnalytics = api;\r\n}\r\n\r\nfunction getConfigFromScriptTag(): AnalyticsInitConfig | null {\r\n if (typeof document === 'undefined') return null;\r\n const script = document.currentScript as HTMLScriptElement | null;\r\n if (!script) return null;\r\n const raw = script.getAttribute('data-config');\r\n if (!raw || typeof raw !== 'string') return null;\r\n try {\r\n const parsed = JSON.parse(raw) as AnalyticsInitConfig;\r\n if (parsed && typeof parsed.appId === 'string' && parsed.appId.trim() !== '') {\r\n return parsed;\r\n }\r\n } catch {\r\n // Invalid JSON\r\n }\r\n return null;\r\n}\r\n\r\nfunction autoInit(): void {\r\n if (typeof window === 'undefined') return;\r\n const config = getConfigFromScriptTag() ?? window.__ANALYTICS_CONFIG__;\r\n if (config) {\r\n init(config).catch(() => {\r\n // Silent fail for auto-init; app can call init() manually with onError if needed\r\n });\r\n }\r\n}\r\n\r\nautoInit();\r\n"],"mappings":"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","names":["PreInitBuffer","options","name","properties","timestamp","item","userId","traits","onTrack","onIdentify","cutoff","identifyItems","trackItems","ListenerRegistry","id","listener","STORAGE_KEY_PREFIX","generateId","fallbackUuidV4","hex","result","c","v","getOrCreateSessionId","appId","providedSessionId","key","existing","newId","ENVELOPE_VERSION","createEnricherState","appId","environment","sdkVersion","sessionId","resolvedSessionId","getOrCreateSessionId","enrich","payload","state","timestamp","envelope","generateId","setUserId","userId","clearUserId","dispatchTrack","deps","payload","state","envelope","enrich","dispatchEnvelope","kind","registry","onError","id","listener","err","error","dispatchIdentify","userId","traits","createNoopListener","_config","_envelope","noopListener","CLARITY_SCRIPT_BASE","isBrowser","loadClarityScript","projectId","resolve","script","createClarityListener","config","ready","cfg","id","envelope","key","value","userId","_traits","GTAG_SCRIPT_URL","isBrowser","loadGtagScript","measurementId","resolve","gtag","args","script","createGAListener","config","ready","cfg","id","envelope","params","userId","traits","createCustomApiListener","config","endpoint","apiKey","batchSize","flushIntervalMs","batch","flushTimer","scheduleFlush","sendBatch","toSend","body","headers","_config","envelope","BUILT_IN_FACTORIES","createNoopListener","config","createClarityListener","createGAListener","createCustomApiListener","buildListenersFromRemoteConfig","remote","options","factories","BUILT_IN_FACTORIES","onError","entries","entry","id","factory","listener","err","error","VALID_PRE_INIT_BEHAVIORS","isRemoteListenerEntry","entry","o","validateConfig","options","useRemoteConfig","behavior","maxSize","ttlMs","listeners","SDK_VERSION","enricherState","registry","preInitBuffer","onError","initialized","getOrCreatePreInitBuffer","PreInitBuffer","getEnricherState","fetchRemoteConfig","options","res","json","isInlineRemoteShape","listeners","entry","init","validateConfig","appId","environment","remote","buildListenersFromRemoteConfig","rawListeners","preInitBehavior","queueMaxSize","queueTtlMs","mode","createEnricherState","SDK_VERSION","ListenerRegistry","initResult","err","error","deps","payload","dispatchTrack","userId","traits","setUserId","dispatchIdentify","track","event","identify","flush","promises","listener","result","reset","clearUserId","isInitialized","api","init","track","identify","flush","reset","isInitialized","getConfigFromScriptTag","script","raw","parsed","autoInit","config"]}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@snovasys/usage-analytics-sdk",
3
+ "version": "1.0.0",
4
+ "description": "Lightweight, extensible usage analytics SDK for browser and Node",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "script": "./dist/usage-analytics.min.js",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ },
16
+ "./noop": {
17
+ "types": "./dist/noop.d.ts",
18
+ "import": "./dist/noop.js",
19
+ "require": "./dist/noop.cjs"
20
+ },
21
+ "./custom-api": {
22
+ "types": "./dist/custom-api.d.ts",
23
+ "import": "./dist/custom-api.js",
24
+ "require": "./dist/custom-api.cjs"
25
+ },
26
+ "./clarity": {
27
+ "types": "./dist/clarity.d.ts",
28
+ "import": "./dist/clarity.js",
29
+ "require": "./dist/clarity.cjs"
30
+ },
31
+ "./ga": {
32
+ "types": "./dist/ga.d.ts",
33
+ "import": "./dist/ga.js",
34
+ "require": "./dist/ga.cjs"
35
+ }
36
+ },
37
+ "files": [
38
+ "dist"
39
+ ],
40
+ "scripts": {
41
+ "build": "tsup",
42
+ "test": "vitest run",
43
+ "test:watch": "vitest",
44
+ "lint": "eslint src --ext .ts"
45
+ },
46
+ "keywords": [
47
+ "analytics",
48
+ "usage",
49
+ "sdk",
50
+ "events"
51
+ ],
52
+ "license": "MIT",
53
+ "devDependencies": {
54
+ "@types/node": "^20.10.0",
55
+ "tsup": "^8.0.0",
56
+ "typescript": "^5.3.0",
57
+ "vitest": "^1.0.0"
58
+ }
59
+ }