@reactor-cloud/analytics 0.2.0 → 0.3.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.
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -25,7 +25,7 @@ __export(index_exports, {
|
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(index_exports);
|
|
27
27
|
var SDK_NAME = "@reactor-cloud/analytics";
|
|
28
|
-
var SDK_VERSION = "0.
|
|
28
|
+
var SDK_VERSION = "0.2.0";
|
|
29
29
|
var DEFAULT_BATCH_SIZE = 20;
|
|
30
30
|
var DEFAULT_FLUSH_INTERVAL = 5e3;
|
|
31
31
|
var DEFAULT_STORAGE_KEY = "reactor_anon_id";
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @reactor-cloud/analytics - Product analytics SDK for Reactor\n *\n * @example\n * ```ts\n * import { ReactorAnalytics } from '@reactor-cloud/analytics';\n *\n * const analytics = new ReactorAnalytics({\n * projectKey: 'pk_...',\n * endpoint: 'https://api.reactor.cloud/analytics/v1',\n * });\n *\n * analytics.track('button_clicked', { button_id: 'signup' });\n * analytics.identify('user_123', { email: 'user@example.com' });\n * ```\n */\n\n// Types\nexport interface AnalyticsConfig {\n /** Project key (X-Reactor-Project-Key header). */\n projectKey: string;\n /** Analytics API endpoint. */\n endpoint: string;\n /** Batch events before sending. */\n batchSize?: number;\n /** Flush interval in milliseconds. */\n flushInterval?: number;\n /** Auto-capture pageviews. */\n autoPageview?: boolean;\n /** Auto-capture errors with fingerprint coalescing. */\n autoErrors?: boolean;\n /** Auto-capture click interactions (opt-in). */\n autoCapture?: boolean;\n /** CSS selector for auto-capture targets. */\n autoCaptureSelector?: string;\n /** Error deduplication window in ms (default 5000). */\n errorDedupeWindow?: number;\n /** Persist anonymous ID to localStorage. */\n persistence?: boolean;\n /** Storage key for persistence. */\n storageKey?: string;\n /** Debug mode (logs events to console). */\n debug?: boolean;\n}\n\nexport interface EventProperties {\n [key: string]: string | number | boolean | null | undefined | EventProperties | EventProperties[];\n}\n\nexport interface UserTraits {\n email?: string;\n name?: string;\n [key: string]: string | number | boolean | null | undefined;\n}\n\nexport interface PageContext {\n path?: string;\n url?: string;\n referrer?: string;\n title?: string;\n}\n\nexport interface ClientContext {\n library?: { name: string; version: string };\n userAgent?: string;\n locale?: string;\n timezone?: string;\n screen?: { width: number; height: number };\n}\n\nexport interface QueuedEvent {\n event: string;\n properties: EventProperties;\n timestamp: string;\n anonymousId: string;\n userId?: string;\n sessionId?: string;\n context: ClientContext & PageContext;\n}\n\n// Constants\nconst SDK_NAME = \"@reactor-cloud/analytics\";\nconst SDK_VERSION = \"0.1.0\";\nconst DEFAULT_BATCH_SIZE = 20;\nconst DEFAULT_FLUSH_INTERVAL = 5000; // 5 seconds\nconst DEFAULT_STORAGE_KEY = \"reactor_anon_id\";\nconst DEFAULT_ERROR_DEDUPE_WINDOW = 5000; // 5 seconds\nconst DEFAULT_AUTOCAPTURE_SELECTOR = \"a, button, input[type='submit'], [data-reactor-capture]\";\n\n// Utility functions\nfunction generateId(): string {\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older browsers\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\nfunction getAnonymousId(storageKey: string, persistence: boolean): string {\n if (\n persistence &&\n typeof localStorage !== \"undefined\" &&\n typeof localStorage.getItem === \"function\" &&\n typeof localStorage.setItem === \"function\"\n ) {\n try {\n const stored = localStorage.getItem(storageKey);\n if (stored) return stored;\n const newId = generateId();\n localStorage.setItem(storageKey, newId);\n return newId;\n } catch {\n // localStorage present but unusable (e.g. partial Web Storage shim)\n }\n }\n return generateId();\n}\n\nfunction getClientContext(): ClientContext {\n const ctx: ClientContext = {\n library: { name: SDK_NAME, version: SDK_VERSION },\n };\n\n if (typeof navigator !== \"undefined\") {\n ctx.userAgent = navigator.userAgent;\n ctx.locale = navigator.language;\n }\n\n if (typeof Intl !== \"undefined\") {\n try {\n ctx.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n } catch {}\n }\n\n if (typeof screen !== \"undefined\") {\n ctx.screen = { width: screen.width, height: screen.height };\n }\n\n return ctx;\n}\n\nfunction getPageContext(): PageContext {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return {};\n }\n\n return {\n path: window.location.pathname,\n url: window.location.href,\n referrer: document.referrer || undefined,\n title: document.title || undefined,\n };\n}\n\nfunction simpleHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash; // Convert to 32bit integer\n }\n return hash.toString(36);\n}\n\nfunction getElementSelector(el: Element): string {\n const parts: string[] = [];\n \n if (el.id) {\n parts.push(`#${el.id}`);\n }\n \n if (el.className && typeof el.className === 'string') {\n const classes = el.className.trim().split(/\\s+/).filter(c => c.length > 0);\n if (classes.length > 0) {\n parts.push(`.${classes.slice(0, 2).join('.')}`);\n }\n }\n \n const tagName = el.tagName?.toLowerCase() || 'unknown';\n return parts.length > 0 ? `${tagName}${parts.join('')}` : tagName;\n}\n\nfunction getElementText(el: Element): string {\n const text = (el.textContent || '').trim().slice(0, 100);\n return text || (el as HTMLElement).getAttribute?.('aria-label') || '';\n}\n\n/**\n * Reactor Analytics client.\n *\n * Provides methods for tracking events, identifying users, and managing consent.\n */\nexport class ReactorAnalytics {\n private config: Required<AnalyticsConfig>;\n private anonymousId: string;\n private userId: string | undefined;\n private sessionId: string;\n private queue: QueuedEvent[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private optedOut: boolean = false;\n private errorFingerprints: Map<string, number> = new Map();\n\n constructor(config: AnalyticsConfig) {\n this.config = {\n projectKey: config.projectKey,\n endpoint: config.endpoint,\n batchSize: config.batchSize ?? DEFAULT_BATCH_SIZE,\n flushInterval: config.flushInterval ?? DEFAULT_FLUSH_INTERVAL,\n autoPageview: config.autoPageview ?? false,\n autoErrors: config.autoErrors ?? false,\n autoCapture: config.autoCapture ?? false,\n autoCaptureSelector: config.autoCaptureSelector ?? DEFAULT_AUTOCAPTURE_SELECTOR,\n errorDedupeWindow: config.errorDedupeWindow ?? DEFAULT_ERROR_DEDUPE_WINDOW,\n persistence: config.persistence ?? true,\n storageKey: config.storageKey ?? DEFAULT_STORAGE_KEY,\n debug: config.debug ?? false,\n };\n\n this.anonymousId = getAnonymousId(\n this.config.storageKey,\n this.config.persistence\n );\n this.sessionId = generateId();\n\n // Start flush timer\n this.startFlushTimer();\n\n // Auto-capture setup\n if (typeof window !== \"undefined\") {\n if (this.config.autoPageview) {\n this.setupAutoPageview();\n }\n if (this.config.autoErrors) {\n this.setupAutoErrors();\n }\n if (this.config.autoCapture) {\n this.setupAutoCapture();\n }\n // Flush on page unload\n window.addEventListener(\"beforeunload\", () => this.flush());\n }\n }\n\n /**\n * Track an event.\n *\n * @param event - Event name.\n * @param properties - Event properties.\n */\n track(event: string, properties: EventProperties = {}): void {\n if (this.optedOut) return;\n\n const queuedEvent: QueuedEvent = {\n event,\n properties,\n timestamp: new Date().toISOString(),\n anonymousId: this.anonymousId,\n userId: this.userId,\n sessionId: this.sessionId,\n context: { ...getClientContext(), ...getPageContext() },\n };\n\n this.enqueue(queuedEvent);\n }\n\n /**\n * Track a page view.\n *\n * @param name - Page name (optional).\n * @param properties - Additional properties.\n */\n page(name?: string, properties: EventProperties = {}): void {\n const pageContext = getPageContext();\n this.track(\"$pageview\", {\n ...properties,\n name: name || pageContext.title,\n path: pageContext.path,\n url: pageContext.url,\n referrer: pageContext.referrer,\n });\n }\n\n /**\n * Identify a user.\n *\n * @param userId - User ID.\n * @param traits - User traits (email, name, etc.).\n */\n identify(userId: string, traits: UserTraits = {}): void {\n if (this.optedOut) return;\n\n this.userId = userId;\n\n // Send identify event\n const queuedEvent: QueuedEvent = {\n event: \"$identify\",\n properties: traits as EventProperties,\n timestamp: new Date().toISOString(),\n anonymousId: this.anonymousId,\n userId,\n sessionId: this.sessionId,\n context: getClientContext(),\n };\n\n this.enqueue(queuedEvent);\n\n // Also send to identify endpoint\n this.sendIdentify(userId, traits);\n }\n\n /**\n * Alias an anonymous ID to a user ID.\n *\n * @param previousId - Previous anonymous ID.\n * @param userId - User ID to alias to.\n */\n alias(previousId: string, userId: string): void {\n if (this.optedOut) return;\n\n const queuedEvent: QueuedEvent = {\n event: \"$alias\",\n properties: { previousId, userId },\n timestamp: new Date().toISOString(),\n anonymousId: previousId,\n userId,\n sessionId: this.sessionId,\n context: getClientContext(),\n };\n\n this.enqueue(queuedEvent);\n\n // Also send to alias endpoint\n this.sendAlias(previousId, userId);\n }\n\n /**\n * Reset the user identity.\n * Generates a new anonymous ID and clears the user ID.\n */\n reset(): void {\n this.userId = undefined;\n this.anonymousId = generateId();\n this.sessionId = generateId();\n\n if (\n this.config.persistence &&\n typeof localStorage !== \"undefined\" &&\n typeof localStorage.setItem === \"function\"\n ) {\n try {\n localStorage.setItem(this.config.storageKey, this.anonymousId);\n } catch {\n // localStorage present but unusable\n }\n }\n }\n\n /**\n * Flush the event queue.\n */\n async flush(): Promise<void> {\n if (this.queue.length === 0) return;\n\n const events = this.queue.splice(0, this.queue.length);\n\n if (this.config.debug) {\n console.log(\"[ReactorAnalytics] Flushing events:\", events);\n }\n\n try {\n const response = await this.sendBatch(events);\n if (!response.ok && this.config.debug) {\n console.error(\"[ReactorAnalytics] Flush failed:\", response.status);\n // Put events back in queue for retry\n this.queue.unshift(...events);\n }\n } catch (error) {\n if (this.config.debug) {\n console.error(\"[ReactorAnalytics] Flush error:\", error);\n }\n // Put events back in queue for retry\n this.queue.unshift(...events);\n }\n }\n\n /**\n * Opt out of tracking.\n */\n optOut(): void {\n this.optedOut = true;\n this.queue = [];\n this.sendConsentOptOut();\n }\n\n /**\n * Opt in to tracking.\n */\n optIn(): void {\n this.optedOut = false;\n this.sendConsentOptIn();\n }\n\n /**\n * Get the current anonymous ID.\n */\n getAnonymousId(): string {\n return this.anonymousId;\n }\n\n /**\n * Get the current user ID.\n */\n getUserId(): string | undefined {\n return this.userId;\n }\n\n /**\n * Get the current session ID.\n */\n getSessionId(): string {\n return this.sessionId;\n }\n\n // --- Private methods ---\n\n private enqueue(event: QueuedEvent): void {\n this.queue.push(event);\n\n if (this.config.debug) {\n console.log(\"[ReactorAnalytics] Event queued:\", event);\n }\n\n if (this.queue.length >= this.config.batchSize) {\n this.flush();\n }\n }\n\n private startFlushTimer(): void {\n if (this.flushTimer) return;\n this.flushTimer = setInterval(() => {\n this.flush();\n }, this.config.flushInterval);\n }\n\n private async sendBatch(events: QueuedEvent[]): Promise<Response> {\n const body = {\n events: events.map((e) => ({\n event: e.event,\n anonymous_id: e.anonymousId,\n user_id: e.userId,\n session_id: e.sessionId,\n timestamp: e.timestamp,\n properties: e.properties,\n context: e.context,\n })),\n };\n\n // Use sendBeacon if available and page is unloading\n if (\n typeof navigator !== \"undefined\" &&\n navigator.sendBeacon &&\n document?.visibilityState === \"hidden\"\n ) {\n const blob = new Blob([JSON.stringify(body)], {\n type: \"application/json\",\n });\n navigator.sendBeacon(`${this.config.endpoint}/batch`, blob);\n return new Response(null, { status: 202 });\n }\n\n return fetch(`${this.config.endpoint}/batch`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Reactor-Project-Key\": this.config.projectKey,\n },\n body: JSON.stringify(body),\n keepalive: true,\n });\n }\n\n private async sendIdentify(userId: string, traits: UserTraits): Promise<void> {\n try {\n await fetch(`${this.config.endpoint}/identify`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Reactor-Project-Key\": this.config.projectKey,\n },\n body: JSON.stringify({\n anonymous_id: this.anonymousId,\n user_id: userId,\n traits,\n }),\n });\n } catch (error) {\n if (this.config.debug) {\n console.error(\"[ReactorAnalytics] Identify error:\", error);\n }\n }\n }\n\n private async sendAlias(previousId: string, userId: string): Promise<void> {\n try {\n await fetch(`${this.config.endpoint}/alias`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Reactor-Project-Key\": this.config.projectKey,\n },\n body: JSON.stringify({\n anonymous_id: previousId,\n user_id: userId,\n }),\n });\n } catch (error) {\n if (this.config.debug) {\n console.error(\"[ReactorAnalytics] Alias error:\", error);\n }\n }\n }\n\n private async sendConsentOptOut(): Promise<void> {\n try {\n await fetch(`${this.config.endpoint}/consent/opt-out`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Reactor-Project-Key\": this.config.projectKey,\n },\n body: JSON.stringify({ anonymous_id: this.anonymousId }),\n });\n } catch {}\n }\n\n private async sendConsentOptIn(): Promise<void> {\n try {\n await fetch(`${this.config.endpoint}/consent/opt-in`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Reactor-Project-Key\": this.config.projectKey,\n },\n body: JSON.stringify({ anonymous_id: this.anonymousId }),\n });\n } catch {}\n }\n\n private setupAutoPageview(): void {\n // Initial pageview\n this.page();\n\n // SPA navigation hooks\n if (typeof window !== \"undefined\") {\n const originalPushState = history.pushState;\n const originalReplaceState = history.replaceState;\n\n history.pushState = (...args) => {\n originalPushState.apply(history, args);\n this.page();\n };\n\n history.replaceState = (...args) => {\n originalReplaceState.apply(history, args);\n this.page();\n };\n\n window.addEventListener(\"popstate\", () => this.page());\n }\n }\n\n private setupAutoErrors(): void {\n if (typeof window === \"undefined\") return;\n\n window.addEventListener(\"error\", (event) => {\n const fingerprint = this.generateErrorFingerprint(\n event.message,\n event.filename,\n event.lineno\n );\n\n if (this.shouldTrackError(fingerprint)) {\n this.track(\"$error\", {\n message: event.message,\n filename: event.filename,\n lineno: event.lineno,\n colno: event.colno,\n error: event.error?.toString(),\n fingerprint,\n });\n }\n });\n\n window.addEventListener(\"unhandledrejection\", (event) => {\n const reason = String(event.reason);\n const fingerprint = this.generateErrorFingerprint(\n \"Unhandled Promise Rejection\",\n reason,\n 0\n );\n\n if (this.shouldTrackError(fingerprint)) {\n this.track(\"$error\", {\n message: \"Unhandled Promise Rejection\",\n reason,\n fingerprint,\n });\n }\n });\n }\n\n private generateErrorFingerprint(\n message: string,\n filename: string | undefined,\n lineno: number | undefined\n ): string {\n const key = `${message}|${filename || ''}|${lineno || 0}`;\n return simpleHash(key);\n }\n\n private shouldTrackError(fingerprint: string): boolean {\n const now = Date.now();\n const lastSeen = this.errorFingerprints.get(fingerprint);\n\n if (lastSeen && now - lastSeen < this.config.errorDedupeWindow) {\n if (this.config.debug) {\n console.log(\"[ReactorAnalytics] Deduped error:\", fingerprint);\n }\n return false;\n }\n\n this.errorFingerprints.set(fingerprint, now);\n\n // Cleanup old fingerprints periodically\n if (this.errorFingerprints.size > 100) {\n const cutoff = now - this.config.errorDedupeWindow * 2;\n for (const [key, time] of this.errorFingerprints.entries()) {\n if (time < cutoff) {\n this.errorFingerprints.delete(key);\n }\n }\n }\n\n return true;\n }\n\n private setupAutoCapture(): void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return;\n\n document.addEventListener(\"click\", (event) => {\n const target = event.target as Element | null;\n if (!target) return;\n\n // Find the closest matching element\n const el = target.closest(this.config.autoCaptureSelector);\n if (!el) return;\n\n const tagName = el.tagName?.toLowerCase() || 'unknown';\n const selector = getElementSelector(el);\n const text = getElementText(el);\n\n this.track(\"$autocapture\", {\n event_type: \"click\",\n tag_name: tagName,\n selector,\n text: text.slice(0, 100),\n href: (el as HTMLAnchorElement).href || undefined,\n name: el.getAttribute(\"name\") || undefined,\n id: el.id || undefined,\n classes: el.className || undefined,\n });\n }, { capture: true, passive: true });\n }\n}\n\n// Default export\nexport default ReactorAnalytics;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiFA,IAAM,WAAW;AACjB,IAAM,cAAc;AACpB,IAAM,qBAAqB;AAC3B,IAAM,yBAAyB;AAC/B,IAAM,sBAAsB;AAC5B,IAAM,8BAA8B;AACpC,IAAM,+BAA+B;AAGrC,SAAS,aAAqB;AAC5B,MAAI,OAAO,WAAW,eAAe,OAAO,YAAY;AACtD,WAAO,OAAO,WAAW;AAAA,EAC3B;AAEA,SAAO,uCAAuC,QAAQ,SAAS,CAAC,MAAM;AACpE,UAAM,IAAK,KAAK,OAAO,IAAI,KAAM;AACjC,UAAM,IAAI,MAAM,MAAM,IAAK,IAAI,IAAO;AACtC,WAAO,EAAE,SAAS,EAAE;AAAA,EACtB,CAAC;AACH;AAEA,SAAS,eAAe,YAAoB,aAA8B;AACxE,MACE,eACA,OAAO,iBAAiB,eACxB,OAAO,aAAa,YAAY,cAChC,OAAO,aAAa,YAAY,YAChC;AACA,QAAI;AACF,YAAM,SAAS,aAAa,QAAQ,UAAU;AAC9C,UAAI,OAAQ,QAAO;AACnB,YAAM,QAAQ,WAAW;AACzB,mBAAa,QAAQ,YAAY,KAAK;AACtC,aAAO;AAAA,IACT,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO,WAAW;AACpB;AAEA,SAAS,mBAAkC;AACzC,QAAM,MAAqB;AAAA,IACzB,SAAS,EAAE,MAAM,UAAU,SAAS,YAAY;AAAA,EAClD;AAEA,MAAI,OAAO,cAAc,aAAa;AACpC,QAAI,YAAY,UAAU;AAC1B,QAAI,SAAS,UAAU;AAAA,EACzB;AAEA,MAAI,OAAO,SAAS,aAAa;AAC/B,QAAI;AACF,UAAI,WAAW,KAAK,eAAe,EAAE,gBAAgB,EAAE;AAAA,IACzD,QAAQ;AAAA,IAAC;AAAA,EACX;AAEA,MAAI,OAAO,WAAW,aAAa;AACjC,QAAI,SAAS,EAAE,OAAO,OAAO,OAAO,QAAQ,OAAO,OAAO;AAAA,EAC5D;AAEA,SAAO;AACT;AAEA,SAAS,iBAA8B;AACrC,MAAI,OAAO,WAAW,eAAe,OAAO,aAAa,aAAa;AACpE,WAAO,CAAC;AAAA,EACV;AAEA,SAAO;AAAA,IACL,MAAM,OAAO,SAAS;AAAA,IACtB,KAAK,OAAO,SAAS;AAAA,IACrB,UAAU,SAAS,YAAY;AAAA,IAC/B,OAAO,SAAS,SAAS;AAAA,EAC3B;AACF;AAEA,SAAS,WAAW,KAAqB;AACvC,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAM,OAAO,IAAI,WAAW,CAAC;AAC7B,YAAS,QAAQ,KAAK,OAAQ;AAC9B,WAAO,OAAO;AAAA,EAChB;AACA,SAAO,KAAK,SAAS,EAAE;AACzB;AAEA,SAAS,mBAAmB,IAAqB;AAC/C,QAAM,QAAkB,CAAC;AAEzB,MAAI,GAAG,IAAI;AACT,UAAM,KAAK,IAAI,GAAG,EAAE,EAAE;AAAA,EACxB;AAEA,MAAI,GAAG,aAAa,OAAO,GAAG,cAAc,UAAU;AACpD,UAAM,UAAU,GAAG,UAAU,KAAK,EAAE,MAAM,KAAK,EAAE,OAAO,OAAK,EAAE,SAAS,CAAC;AACzE,QAAI,QAAQ,SAAS,GAAG;AACtB,YAAM,KAAK,IAAI,QAAQ,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE;AAAA,IAChD;AAAA,EACF;AAEA,QAAM,UAAU,GAAG,SAAS,YAAY,KAAK;AAC7C,SAAO,MAAM,SAAS,IAAI,GAAG,OAAO,GAAG,MAAM,KAAK,EAAE,CAAC,KAAK;AAC5D;AAEA,SAAS,eAAe,IAAqB;AAC3C,QAAM,QAAQ,GAAG,eAAe,IAAI,KAAK,EAAE,MAAM,GAAG,GAAG;AACvD,SAAO,QAAS,GAAmB,eAAe,YAAY,KAAK;AACrE;AAOO,IAAM,mBAAN,MAAuB;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAuB,CAAC;AAAA,EACxB,aAAmD;AAAA,EACnD,WAAoB;AAAA,EACpB,oBAAyC,oBAAI,IAAI;AAAA,EAEzD,YAAY,QAAyB;AACnC,SAAK,SAAS;AAAA,MACZ,YAAY,OAAO;AAAA,MACnB,UAAU,OAAO;AAAA,MACjB,WAAW,OAAO,aAAa;AAAA,MAC/B,eAAe,OAAO,iBAAiB;AAAA,MACvC,cAAc,OAAO,gBAAgB;AAAA,MACrC,YAAY,OAAO,cAAc;AAAA,MACjC,aAAa,OAAO,eAAe;AAAA,MACnC,qBAAqB,OAAO,uBAAuB;AAAA,MACnD,mBAAmB,OAAO,qBAAqB;AAAA,MAC/C,aAAa,OAAO,eAAe;AAAA,MACnC,YAAY,OAAO,cAAc;AAAA,MACjC,OAAO,OAAO,SAAS;AAAA,IACzB;AAEA,SAAK,cAAc;AAAA,MACjB,KAAK,OAAO;AAAA,MACZ,KAAK,OAAO;AAAA,IACd;AACA,SAAK,YAAY,WAAW;AAG5B,SAAK,gBAAgB;AAGrB,QAAI,OAAO,WAAW,aAAa;AACjC,UAAI,KAAK,OAAO,cAAc;AAC5B,aAAK,kBAAkB;AAAA,MACzB;AACA,UAAI,KAAK,OAAO,YAAY;AAC1B,aAAK,gBAAgB;AAAA,MACvB;AACA,UAAI,KAAK,OAAO,aAAa;AAC3B,aAAK,iBAAiB;AAAA,MACxB;AAEA,aAAO,iBAAiB,gBAAgB,MAAM,KAAK,MAAM,CAAC;AAAA,IAC5D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,OAAe,aAA8B,CAAC,GAAS;AAC3D,QAAI,KAAK,SAAU;AAEnB,UAAM,cAA2B;AAAA,MAC/B;AAAA,MACA;AAAA,MACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,aAAa,KAAK;AAAA,MAClB,QAAQ,KAAK;AAAA,MACb,WAAW,KAAK;AAAA,MAChB,SAAS,EAAE,GAAG,iBAAiB,GAAG,GAAG,eAAe,EAAE;AAAA,IACxD;AAEA,SAAK,QAAQ,WAAW;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,KAAK,MAAe,aAA8B,CAAC,GAAS;AAC1D,UAAM,cAAc,eAAe;AACnC,SAAK,MAAM,aAAa;AAAA,MACtB,GAAG;AAAA,MACH,MAAM,QAAQ,YAAY;AAAA,MAC1B,MAAM,YAAY;AAAA,MAClB,KAAK,YAAY;AAAA,MACjB,UAAU,YAAY;AAAA,IACxB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,SAAS,QAAgB,SAAqB,CAAC,GAAS;AACtD,QAAI,KAAK,SAAU;AAEnB,SAAK,SAAS;AAGd,UAAM,cAA2B;AAAA,MAC/B,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,aAAa,KAAK;AAAA,MAClB;AAAA,MACA,WAAW,KAAK;AAAA,MAChB,SAAS,iBAAiB;AAAA,IAC5B;AAEA,SAAK,QAAQ,WAAW;AAGxB,SAAK,aAAa,QAAQ,MAAM;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,YAAoB,QAAsB;AAC9C,QAAI,KAAK,SAAU;AAEnB,UAAM,cAA2B;AAAA,MAC/B,OAAO;AAAA,MACP,YAAY,EAAE,YAAY,OAAO;AAAA,MACjC,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,aAAa;AAAA,MACb;AAAA,MACA,WAAW,KAAK;AAAA,MAChB,SAAS,iBAAiB;AAAA,IAC5B;AAEA,SAAK,QAAQ,WAAW;AAGxB,SAAK,UAAU,YAAY,MAAM;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,QAAc;AACZ,SAAK,SAAS;AACd,SAAK,cAAc,WAAW;AAC9B,SAAK,YAAY,WAAW;AAE5B,QACE,KAAK,OAAO,eACZ,OAAO,iBAAiB,eACxB,OAAO,aAAa,YAAY,YAChC;AACA,UAAI;AACF,qBAAa,QAAQ,KAAK,OAAO,YAAY,KAAK,WAAW;AAAA,MAC/D,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AAC3B,QAAI,KAAK,MAAM,WAAW,EAAG;AAE7B,UAAM,SAAS,KAAK,MAAM,OAAO,GAAG,KAAK,MAAM,MAAM;AAErD,QAAI,KAAK,OAAO,OAAO;AACrB,cAAQ,IAAI,uCAAuC,MAAM;AAAA,IAC3D;AAEA,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,UAAU,MAAM;AAC5C,UAAI,CAAC,SAAS,MAAM,KAAK,OAAO,OAAO;AACrC,gBAAQ,MAAM,oCAAoC,SAAS,MAAM;AAEjE,aAAK,MAAM,QAAQ,GAAG,MAAM;AAAA,MAC9B;AAAA,IACF,SAAS,OAAO;AACd,UAAI,KAAK,OAAO,OAAO;AACrB,gBAAQ,MAAM,mCAAmC,KAAK;AAAA,MACxD;AAEA,WAAK,MAAM,QAAQ,GAAG,MAAM;AAAA,IAC9B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,SAAe;AACb,SAAK,WAAW;AAChB,SAAK,QAAQ,CAAC;AACd,SAAK,kBAAkB;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,SAAK,WAAW;AAChB,SAAK,iBAAiB;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAyB;AACvB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,YAAgC;AAC9B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,eAAuB;AACrB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAIQ,QAAQ,OAA0B;AACxC,SAAK,MAAM,KAAK,KAAK;AAErB,QAAI,KAAK,OAAO,OAAO;AACrB,cAAQ,IAAI,oCAAoC,KAAK;AAAA,IACvD;AAEA,QAAI,KAAK,MAAM,UAAU,KAAK,OAAO,WAAW;AAC9C,WAAK,MAAM;AAAA,IACb;AAAA,EACF;AAAA,EAEQ,kBAAwB;AAC9B,QAAI,KAAK,WAAY;AACrB,SAAK,aAAa,YAAY,MAAM;AAClC,WAAK,MAAM;AAAA,IACb,GAAG,KAAK,OAAO,aAAa;AAAA,EAC9B;AAAA,EAEA,MAAc,UAAU,QAA0C;AAChE,UAAM,OAAO;AAAA,MACX,QAAQ,OAAO,IAAI,CAAC,OAAO;AAAA,QACzB,OAAO,EAAE;AAAA,QACT,cAAc,EAAE;AAAA,QAChB,SAAS,EAAE;AAAA,QACX,YAAY,EAAE;AAAA,QACd,WAAW,EAAE;AAAA,QACb,YAAY,EAAE;AAAA,QACd,SAAS,EAAE;AAAA,MACb,EAAE;AAAA,IACJ;AAGA,QACE,OAAO,cAAc,eACrB,UAAU,cACV,UAAU,oBAAoB,UAC9B;AACA,YAAM,OAAO,IAAI,KAAK,CAAC,KAAK,UAAU,IAAI,CAAC,GAAG;AAAA,QAC5C,MAAM;AAAA,MACR,CAAC;AACD,gBAAU,WAAW,GAAG,KAAK,OAAO,QAAQ,UAAU,IAAI;AAC1D,aAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC3C;AAEA,WAAO,MAAM,GAAG,KAAK,OAAO,QAAQ,UAAU;AAAA,MAC5C,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,yBAAyB,KAAK,OAAO;AAAA,MACvC;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB,WAAW;AAAA,IACb,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,aAAa,QAAgB,QAAmC;AAC5E,QAAI;AACF,YAAM,MAAM,GAAG,KAAK,OAAO,QAAQ,aAAa;AAAA,QAC9C,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,yBAAyB,KAAK,OAAO;AAAA,QACvC;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB,cAAc,KAAK;AAAA,UACnB,SAAS;AAAA,UACT;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAAA,IACH,SAAS,OAAO;AACd,UAAI,KAAK,OAAO,OAAO;AACrB,gBAAQ,MAAM,sCAAsC,KAAK;AAAA,MAC3D;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,UAAU,YAAoB,QAA+B;AACzE,QAAI;AACF,YAAM,MAAM,GAAG,KAAK,OAAO,QAAQ,UAAU;AAAA,QAC3C,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,yBAAyB,KAAK,OAAO;AAAA,QACvC;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB,cAAc;AAAA,UACd,SAAS;AAAA,QACX,CAAC;AAAA,MACH,CAAC;AAAA,IACH,SAAS,OAAO;AACd,UAAI,KAAK,OAAO,OAAO;AACrB,gBAAQ,MAAM,mCAAmC,KAAK;AAAA,MACxD;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,oBAAmC;AAC/C,QAAI;AACF,YAAM,MAAM,GAAG,KAAK,OAAO,QAAQ,oBAAoB;AAAA,QACrD,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,yBAAyB,KAAK,OAAO;AAAA,QACvC;AAAA,QACA,MAAM,KAAK,UAAU,EAAE,cAAc,KAAK,YAAY,CAAC;AAAA,MACzD,CAAC;AAAA,IACH,QAAQ;AAAA,IAAC;AAAA,EACX;AAAA,EAEA,MAAc,mBAAkC;AAC9C,QAAI;AACF,YAAM,MAAM,GAAG,KAAK,OAAO,QAAQ,mBAAmB;AAAA,QACpD,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,yBAAyB,KAAK,OAAO;AAAA,QACvC;AAAA,QACA,MAAM,KAAK,UAAU,EAAE,cAAc,KAAK,YAAY,CAAC;AAAA,MACzD,CAAC;AAAA,IACH,QAAQ;AAAA,IAAC;AAAA,EACX;AAAA,EAEQ,oBAA0B;AAEhC,SAAK,KAAK;AAGV,QAAI,OAAO,WAAW,aAAa;AACjC,YAAM,oBAAoB,QAAQ;AAClC,YAAM,uBAAuB,QAAQ;AAErC,cAAQ,YAAY,IAAI,SAAS;AAC/B,0BAAkB,MAAM,SAAS,IAAI;AACrC,aAAK,KAAK;AAAA,MACZ;AAEA,cAAQ,eAAe,IAAI,SAAS;AAClC,6BAAqB,MAAM,SAAS,IAAI;AACxC,aAAK,KAAK;AAAA,MACZ;AAEA,aAAO,iBAAiB,YAAY,MAAM,KAAK,KAAK,CAAC;AAAA,IACvD;AAAA,EACF;AAAA,EAEQ,kBAAwB;AAC9B,QAAI,OAAO,WAAW,YAAa;AAEnC,WAAO,iBAAiB,SAAS,CAAC,UAAU;AAC1C,YAAM,cAAc,KAAK;AAAA,QACvB,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,MACR;AAEA,UAAI,KAAK,iBAAiB,WAAW,GAAG;AACtC,aAAK,MAAM,UAAU;AAAA,UACnB,SAAS,MAAM;AAAA,UACf,UAAU,MAAM;AAAA,UAChB,QAAQ,MAAM;AAAA,UACd,OAAO,MAAM;AAAA,UACb,OAAO,MAAM,OAAO,SAAS;AAAA,UAC7B;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAED,WAAO,iBAAiB,sBAAsB,CAAC,UAAU;AACvD,YAAM,SAAS,OAAO,MAAM,MAAM;AAClC,YAAM,cAAc,KAAK;AAAA,QACvB;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,UAAI,KAAK,iBAAiB,WAAW,GAAG;AACtC,aAAK,MAAM,UAAU;AAAA,UACnB,SAAS;AAAA,UACT;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,yBACN,SACA,UACA,QACQ;AACR,UAAM,MAAM,GAAG,OAAO,IAAI,YAAY,EAAE,IAAI,UAAU,CAAC;AACvD,WAAO,WAAW,GAAG;AAAA,EACvB;AAAA,EAEQ,iBAAiB,aAA8B;AACrD,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,WAAW,KAAK,kBAAkB,IAAI,WAAW;AAEvD,QAAI,YAAY,MAAM,WAAW,KAAK,OAAO,mBAAmB;AAC9D,UAAI,KAAK,OAAO,OAAO;AACrB,gBAAQ,IAAI,qCAAqC,WAAW;AAAA,MAC9D;AACA,aAAO;AAAA,IACT;AAEA,SAAK,kBAAkB,IAAI,aAAa,GAAG;AAG3C,QAAI,KAAK,kBAAkB,OAAO,KAAK;AACrC,YAAM,SAAS,MAAM,KAAK,OAAO,oBAAoB;AACrD,iBAAW,CAAC,KAAK,IAAI,KAAK,KAAK,kBAAkB,QAAQ,GAAG;AAC1D,YAAI,OAAO,QAAQ;AACjB,eAAK,kBAAkB,OAAO,GAAG;AAAA,QACnC;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,mBAAyB;AAC/B,QAAI,OAAO,WAAW,eAAe,OAAO,aAAa,YAAa;AAEtE,aAAS,iBAAiB,SAAS,CAAC,UAAU;AAC5C,YAAM,SAAS,MAAM;AACrB,UAAI,CAAC,OAAQ;AAGb,YAAM,KAAK,OAAO,QAAQ,KAAK,OAAO,mBAAmB;AACzD,UAAI,CAAC,GAAI;AAET,YAAM,UAAU,GAAG,SAAS,YAAY,KAAK;AAC7C,YAAM,WAAW,mBAAmB,EAAE;AACtC,YAAM,OAAO,eAAe,EAAE;AAE9B,WAAK,MAAM,gBAAgB;AAAA,QACzB,YAAY;AAAA,QACZ,UAAU;AAAA,QACV;AAAA,QACA,MAAM,KAAK,MAAM,GAAG,GAAG;AAAA,QACvB,MAAO,GAAyB,QAAQ;AAAA,QACxC,MAAM,GAAG,aAAa,MAAM,KAAK;AAAA,QACjC,IAAI,GAAG,MAAM;AAAA,QACb,SAAS,GAAG,aAAa;AAAA,MAC3B,CAAC;AAAA,IACH,GAAG,EAAE,SAAS,MAAM,SAAS,KAAK,CAAC;AAAA,EACrC;AACF;AAGA,IAAO,gBAAQ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @reactor-cloud/analytics - Product analytics SDK for Reactor\n *\n * @example\n * ```ts\n * import { ReactorAnalytics } from '@reactor-cloud/analytics';\n *\n * const analytics = new ReactorAnalytics({\n * projectKey: 'pk_...',\n * endpoint: 'https://api.reactor.cloud/analytics/v1',\n * });\n *\n * analytics.track('button_clicked', { button_id: 'signup' });\n * analytics.identify('user_123', { email: 'user@example.com' });\n * ```\n */\n\n// Types\nexport interface AnalyticsConfig {\n /** Project key (X-Reactor-Project-Key header). */\n projectKey: string;\n /** Analytics API endpoint. */\n endpoint: string;\n /** Batch events before sending. */\n batchSize?: number;\n /** Flush interval in milliseconds. */\n flushInterval?: number;\n /** Auto-capture pageviews. */\n autoPageview?: boolean;\n /** Auto-capture errors with fingerprint coalescing. */\n autoErrors?: boolean;\n /** Auto-capture click interactions (opt-in). */\n autoCapture?: boolean;\n /** CSS selector for auto-capture targets. */\n autoCaptureSelector?: string;\n /** Error deduplication window in ms (default 5000). */\n errorDedupeWindow?: number;\n /** Persist anonymous ID to localStorage. */\n persistence?: boolean;\n /** Storage key for persistence. */\n storageKey?: string;\n /** Debug mode (logs events to console). */\n debug?: boolean;\n}\n\nexport interface EventProperties {\n [key: string]: string | number | boolean | null | undefined | EventProperties | EventProperties[];\n}\n\nexport interface UserTraits {\n email?: string;\n name?: string;\n [key: string]: string | number | boolean | null | undefined;\n}\n\nexport interface PageContext {\n path?: string;\n url?: string;\n referrer?: string;\n title?: string;\n}\n\nexport interface ClientContext {\n library?: { name: string; version: string };\n userAgent?: string;\n locale?: string;\n timezone?: string;\n screen?: { width: number; height: number };\n}\n\nexport interface QueuedEvent {\n event: string;\n properties: EventProperties;\n timestamp: string;\n anonymousId: string;\n userId?: string;\n sessionId?: string;\n context: ClientContext & PageContext;\n}\n\n// Constants\nconst SDK_NAME = \"@reactor-cloud/analytics\";\nconst SDK_VERSION = \"0.2.0\";\nconst DEFAULT_BATCH_SIZE = 20;\nconst DEFAULT_FLUSH_INTERVAL = 5000; // 5 seconds\nconst DEFAULT_STORAGE_KEY = \"reactor_anon_id\";\nconst DEFAULT_ERROR_DEDUPE_WINDOW = 5000; // 5 seconds\nconst DEFAULT_AUTOCAPTURE_SELECTOR = \"a, button, input[type='submit'], [data-reactor-capture]\";\n\n// Utility functions\nfunction generateId(): string {\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older browsers\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\nfunction getAnonymousId(storageKey: string, persistence: boolean): string {\n if (\n persistence &&\n typeof localStorage !== \"undefined\" &&\n typeof localStorage.getItem === \"function\" &&\n typeof localStorage.setItem === \"function\"\n ) {\n try {\n const stored = localStorage.getItem(storageKey);\n if (stored) return stored;\n const newId = generateId();\n localStorage.setItem(storageKey, newId);\n return newId;\n } catch {\n // localStorage present but unusable (e.g. partial Web Storage shim)\n }\n }\n return generateId();\n}\n\nfunction getClientContext(): ClientContext {\n const ctx: ClientContext = {\n library: { name: SDK_NAME, version: SDK_VERSION },\n };\n\n if (typeof navigator !== \"undefined\") {\n ctx.userAgent = navigator.userAgent;\n ctx.locale = navigator.language;\n }\n\n if (typeof Intl !== \"undefined\") {\n try {\n ctx.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n } catch {}\n }\n\n if (typeof screen !== \"undefined\") {\n ctx.screen = { width: screen.width, height: screen.height };\n }\n\n return ctx;\n}\n\nfunction getPageContext(): PageContext {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return {};\n }\n\n return {\n path: window.location.pathname,\n url: window.location.href,\n referrer: document.referrer || undefined,\n title: document.title || undefined,\n };\n}\n\nfunction simpleHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash; // Convert to 32bit integer\n }\n return hash.toString(36);\n}\n\nfunction getElementSelector(el: Element): string {\n const parts: string[] = [];\n \n if (el.id) {\n parts.push(`#${el.id}`);\n }\n \n if (el.className && typeof el.className === 'string') {\n const classes = el.className.trim().split(/\\s+/).filter(c => c.length > 0);\n if (classes.length > 0) {\n parts.push(`.${classes.slice(0, 2).join('.')}`);\n }\n }\n \n const tagName = el.tagName?.toLowerCase() || 'unknown';\n return parts.length > 0 ? `${tagName}${parts.join('')}` : tagName;\n}\n\nfunction getElementText(el: Element): string {\n const text = (el.textContent || '').trim().slice(0, 100);\n return text || (el as HTMLElement).getAttribute?.('aria-label') || '';\n}\n\n/**\n * Reactor Analytics client.\n *\n * Provides methods for tracking events, identifying users, and managing consent.\n */\nexport class ReactorAnalytics {\n private config: Required<AnalyticsConfig>;\n private anonymousId: string;\n private userId: string | undefined;\n private sessionId: string;\n private queue: QueuedEvent[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private optedOut: boolean = false;\n private errorFingerprints: Map<string, number> = new Map();\n\n constructor(config: AnalyticsConfig) {\n this.config = {\n projectKey: config.projectKey,\n endpoint: config.endpoint,\n batchSize: config.batchSize ?? DEFAULT_BATCH_SIZE,\n flushInterval: config.flushInterval ?? DEFAULT_FLUSH_INTERVAL,\n autoPageview: config.autoPageview ?? false,\n autoErrors: config.autoErrors ?? false,\n autoCapture: config.autoCapture ?? false,\n autoCaptureSelector: config.autoCaptureSelector ?? DEFAULT_AUTOCAPTURE_SELECTOR,\n errorDedupeWindow: config.errorDedupeWindow ?? DEFAULT_ERROR_DEDUPE_WINDOW,\n persistence: config.persistence ?? true,\n storageKey: config.storageKey ?? DEFAULT_STORAGE_KEY,\n debug: config.debug ?? false,\n };\n\n this.anonymousId = getAnonymousId(\n this.config.storageKey,\n this.config.persistence\n );\n this.sessionId = generateId();\n\n // Start flush timer\n this.startFlushTimer();\n\n // Auto-capture setup\n if (typeof window !== \"undefined\") {\n if (this.config.autoPageview) {\n this.setupAutoPageview();\n }\n if (this.config.autoErrors) {\n this.setupAutoErrors();\n }\n if (this.config.autoCapture) {\n this.setupAutoCapture();\n }\n // Flush on page unload\n window.addEventListener(\"beforeunload\", () => this.flush());\n }\n }\n\n /**\n * Track an event.\n *\n * @param event - Event name.\n * @param properties - Event properties.\n */\n track(event: string, properties: EventProperties = {}): void {\n if (this.optedOut) return;\n\n const queuedEvent: QueuedEvent = {\n event,\n properties,\n timestamp: new Date().toISOString(),\n anonymousId: this.anonymousId,\n userId: this.userId,\n sessionId: this.sessionId,\n context: { ...getClientContext(), ...getPageContext() },\n };\n\n this.enqueue(queuedEvent);\n }\n\n /**\n * Track a page view.\n *\n * @param name - Page name (optional).\n * @param properties - Additional properties.\n */\n page(name?: string, properties: EventProperties = {}): void {\n const pageContext = getPageContext();\n this.track(\"$pageview\", {\n ...properties,\n name: name || pageContext.title,\n path: pageContext.path,\n url: pageContext.url,\n referrer: pageContext.referrer,\n });\n }\n\n /**\n * Identify a user.\n *\n * @param userId - User ID.\n * @param traits - User traits (email, name, etc.).\n */\n identify(userId: string, traits: UserTraits = {}): void {\n if (this.optedOut) return;\n\n this.userId = userId;\n\n // Send identify event\n const queuedEvent: QueuedEvent = {\n event: \"$identify\",\n properties: traits as EventProperties,\n timestamp: new Date().toISOString(),\n anonymousId: this.anonymousId,\n userId,\n sessionId: this.sessionId,\n context: getClientContext(),\n };\n\n this.enqueue(queuedEvent);\n\n // Also send to identify endpoint\n this.sendIdentify(userId, traits);\n }\n\n /**\n * Alias an anonymous ID to a user ID.\n *\n * @param previousId - Previous anonymous ID.\n * @param userId - User ID to alias to.\n */\n alias(previousId: string, userId: string): void {\n if (this.optedOut) return;\n\n const queuedEvent: QueuedEvent = {\n event: \"$alias\",\n properties: { previousId, userId },\n timestamp: new Date().toISOString(),\n anonymousId: previousId,\n userId,\n sessionId: this.sessionId,\n context: getClientContext(),\n };\n\n this.enqueue(queuedEvent);\n\n // Also send to alias endpoint\n this.sendAlias(previousId, userId);\n }\n\n /**\n * Reset the user identity.\n * Generates a new anonymous ID and clears the user ID.\n */\n reset(): void {\n this.userId = undefined;\n this.anonymousId = generateId();\n this.sessionId = generateId();\n\n if (\n this.config.persistence &&\n typeof localStorage !== \"undefined\" &&\n typeof localStorage.setItem === \"function\"\n ) {\n try {\n localStorage.setItem(this.config.storageKey, this.anonymousId);\n } catch {\n // localStorage present but unusable\n }\n }\n }\n\n /**\n * Flush the event queue.\n */\n async flush(): Promise<void> {\n if (this.queue.length === 0) return;\n\n const events = this.queue.splice(0, this.queue.length);\n\n if (this.config.debug) {\n console.log(\"[ReactorAnalytics] Flushing events:\", events);\n }\n\n try {\n const response = await this.sendBatch(events);\n if (!response.ok && this.config.debug) {\n console.error(\"[ReactorAnalytics] Flush failed:\", response.status);\n // Put events back in queue for retry\n this.queue.unshift(...events);\n }\n } catch (error) {\n if (this.config.debug) {\n console.error(\"[ReactorAnalytics] Flush error:\", error);\n }\n // Put events back in queue for retry\n this.queue.unshift(...events);\n }\n }\n\n /**\n * Opt out of tracking.\n */\n optOut(): void {\n this.optedOut = true;\n this.queue = [];\n this.sendConsentOptOut();\n }\n\n /**\n * Opt in to tracking.\n */\n optIn(): void {\n this.optedOut = false;\n this.sendConsentOptIn();\n }\n\n /**\n * Get the current anonymous ID.\n */\n getAnonymousId(): string {\n return this.anonymousId;\n }\n\n /**\n * Get the current user ID.\n */\n getUserId(): string | undefined {\n return this.userId;\n }\n\n /**\n * Get the current session ID.\n */\n getSessionId(): string {\n return this.sessionId;\n }\n\n // --- Private methods ---\n\n private enqueue(event: QueuedEvent): void {\n this.queue.push(event);\n\n if (this.config.debug) {\n console.log(\"[ReactorAnalytics] Event queued:\", event);\n }\n\n if (this.queue.length >= this.config.batchSize) {\n this.flush();\n }\n }\n\n private startFlushTimer(): void {\n if (this.flushTimer) return;\n this.flushTimer = setInterval(() => {\n this.flush();\n }, this.config.flushInterval);\n }\n\n private async sendBatch(events: QueuedEvent[]): Promise<Response> {\n const body = {\n events: events.map((e) => ({\n event: e.event,\n anonymous_id: e.anonymousId,\n user_id: e.userId,\n session_id: e.sessionId,\n timestamp: e.timestamp,\n properties: e.properties,\n context: e.context,\n })),\n };\n\n // Use sendBeacon if available and page is unloading\n if (\n typeof navigator !== \"undefined\" &&\n navigator.sendBeacon &&\n document?.visibilityState === \"hidden\"\n ) {\n const blob = new Blob([JSON.stringify(body)], {\n type: \"application/json\",\n });\n navigator.sendBeacon(`${this.config.endpoint}/batch`, blob);\n return new Response(null, { status: 202 });\n }\n\n return fetch(`${this.config.endpoint}/batch`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Reactor-Project-Key\": this.config.projectKey,\n },\n body: JSON.stringify(body),\n keepalive: true,\n });\n }\n\n private async sendIdentify(userId: string, traits: UserTraits): Promise<void> {\n try {\n await fetch(`${this.config.endpoint}/identify`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Reactor-Project-Key\": this.config.projectKey,\n },\n body: JSON.stringify({\n anonymous_id: this.anonymousId,\n user_id: userId,\n traits,\n }),\n });\n } catch (error) {\n if (this.config.debug) {\n console.error(\"[ReactorAnalytics] Identify error:\", error);\n }\n }\n }\n\n private async sendAlias(previousId: string, userId: string): Promise<void> {\n try {\n await fetch(`${this.config.endpoint}/alias`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Reactor-Project-Key\": this.config.projectKey,\n },\n body: JSON.stringify({\n anonymous_id: previousId,\n user_id: userId,\n }),\n });\n } catch (error) {\n if (this.config.debug) {\n console.error(\"[ReactorAnalytics] Alias error:\", error);\n }\n }\n }\n\n private async sendConsentOptOut(): Promise<void> {\n try {\n await fetch(`${this.config.endpoint}/consent/opt-out`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Reactor-Project-Key\": this.config.projectKey,\n },\n body: JSON.stringify({ anonymous_id: this.anonymousId }),\n });\n } catch {}\n }\n\n private async sendConsentOptIn(): Promise<void> {\n try {\n await fetch(`${this.config.endpoint}/consent/opt-in`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Reactor-Project-Key\": this.config.projectKey,\n },\n body: JSON.stringify({ anonymous_id: this.anonymousId }),\n });\n } catch {}\n }\n\n private setupAutoPageview(): void {\n // Initial pageview\n this.page();\n\n // SPA navigation hooks\n if (typeof window !== \"undefined\") {\n const originalPushState = history.pushState;\n const originalReplaceState = history.replaceState;\n\n history.pushState = (...args) => {\n originalPushState.apply(history, args);\n this.page();\n };\n\n history.replaceState = (...args) => {\n originalReplaceState.apply(history, args);\n this.page();\n };\n\n window.addEventListener(\"popstate\", () => this.page());\n }\n }\n\n private setupAutoErrors(): void {\n if (typeof window === \"undefined\") return;\n\n window.addEventListener(\"error\", (event) => {\n const fingerprint = this.generateErrorFingerprint(\n event.message,\n event.filename,\n event.lineno\n );\n\n if (this.shouldTrackError(fingerprint)) {\n this.track(\"$error\", {\n message: event.message,\n filename: event.filename,\n lineno: event.lineno,\n colno: event.colno,\n error: event.error?.toString(),\n fingerprint,\n });\n }\n });\n\n window.addEventListener(\"unhandledrejection\", (event) => {\n const reason = String(event.reason);\n const fingerprint = this.generateErrorFingerprint(\n \"Unhandled Promise Rejection\",\n reason,\n 0\n );\n\n if (this.shouldTrackError(fingerprint)) {\n this.track(\"$error\", {\n message: \"Unhandled Promise Rejection\",\n reason,\n fingerprint,\n });\n }\n });\n }\n\n private generateErrorFingerprint(\n message: string,\n filename: string | undefined,\n lineno: number | undefined\n ): string {\n const key = `${message}|${filename || ''}|${lineno || 0}`;\n return simpleHash(key);\n }\n\n private shouldTrackError(fingerprint: string): boolean {\n const now = Date.now();\n const lastSeen = this.errorFingerprints.get(fingerprint);\n\n if (lastSeen && now - lastSeen < this.config.errorDedupeWindow) {\n if (this.config.debug) {\n console.log(\"[ReactorAnalytics] Deduped error:\", fingerprint);\n }\n return false;\n }\n\n this.errorFingerprints.set(fingerprint, now);\n\n // Cleanup old fingerprints periodically\n if (this.errorFingerprints.size > 100) {\n const cutoff = now - this.config.errorDedupeWindow * 2;\n for (const [key, time] of this.errorFingerprints.entries()) {\n if (time < cutoff) {\n this.errorFingerprints.delete(key);\n }\n }\n }\n\n return true;\n }\n\n private setupAutoCapture(): void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return;\n\n document.addEventListener(\"click\", (event) => {\n const target = event.target as Element | null;\n if (!target) return;\n\n // Find the closest matching element\n const el = target.closest(this.config.autoCaptureSelector);\n if (!el) return;\n\n const tagName = el.tagName?.toLowerCase() || 'unknown';\n const selector = getElementSelector(el);\n const text = getElementText(el);\n\n this.track(\"$autocapture\", {\n event_type: \"click\",\n tag_name: tagName,\n selector,\n text: text.slice(0, 100),\n href: (el as HTMLAnchorElement).href || undefined,\n name: el.getAttribute(\"name\") || undefined,\n id: el.id || undefined,\n classes: el.className || undefined,\n });\n }, { capture: true, passive: true });\n }\n}\n\n// Default export\nexport default ReactorAnalytics;\n"],"mappings":"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","names":[]}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @reactor-cloud/analytics - Product analytics SDK for Reactor\n *\n * @example\n * ```ts\n * import { ReactorAnalytics } from '@reactor-cloud/analytics';\n *\n * const analytics = new ReactorAnalytics({\n * projectKey: 'pk_...',\n * endpoint: 'https://api.reactor.cloud/analytics/v1',\n * });\n *\n * analytics.track('button_clicked', { button_id: 'signup' });\n * analytics.identify('user_123', { email: 'user@example.com' });\n * ```\n */\n\n// Types\nexport interface AnalyticsConfig {\n /** Project key (X-Reactor-Project-Key header). */\n projectKey: string;\n /** Analytics API endpoint. */\n endpoint: string;\n /** Batch events before sending. */\n batchSize?: number;\n /** Flush interval in milliseconds. */\n flushInterval?: number;\n /** Auto-capture pageviews. */\n autoPageview?: boolean;\n /** Auto-capture errors with fingerprint coalescing. */\n autoErrors?: boolean;\n /** Auto-capture click interactions (opt-in). */\n autoCapture?: boolean;\n /** CSS selector for auto-capture targets. */\n autoCaptureSelector?: string;\n /** Error deduplication window in ms (default 5000). */\n errorDedupeWindow?: number;\n /** Persist anonymous ID to localStorage. */\n persistence?: boolean;\n /** Storage key for persistence. */\n storageKey?: string;\n /** Debug mode (logs events to console). */\n debug?: boolean;\n}\n\nexport interface EventProperties {\n [key: string]: string | number | boolean | null | undefined | EventProperties | EventProperties[];\n}\n\nexport interface UserTraits {\n email?: string;\n name?: string;\n [key: string]: string | number | boolean | null | undefined;\n}\n\nexport interface PageContext {\n path?: string;\n url?: string;\n referrer?: string;\n title?: string;\n}\n\nexport interface ClientContext {\n library?: { name: string; version: string };\n userAgent?: string;\n locale?: string;\n timezone?: string;\n screen?: { width: number; height: number };\n}\n\nexport interface QueuedEvent {\n event: string;\n properties: EventProperties;\n timestamp: string;\n anonymousId: string;\n userId?: string;\n sessionId?: string;\n context: ClientContext & PageContext;\n}\n\n// Constants\nconst SDK_NAME = \"@reactor-cloud/analytics\";\nconst SDK_VERSION = \"0.1.0\";\nconst DEFAULT_BATCH_SIZE = 20;\nconst DEFAULT_FLUSH_INTERVAL = 5000; // 5 seconds\nconst DEFAULT_STORAGE_KEY = \"reactor_anon_id\";\nconst DEFAULT_ERROR_DEDUPE_WINDOW = 5000; // 5 seconds\nconst DEFAULT_AUTOCAPTURE_SELECTOR = \"a, button, input[type='submit'], [data-reactor-capture]\";\n\n// Utility functions\nfunction generateId(): string {\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older browsers\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\nfunction getAnonymousId(storageKey: string, persistence: boolean): string {\n if (\n persistence &&\n typeof localStorage !== \"undefined\" &&\n typeof localStorage.getItem === \"function\" &&\n typeof localStorage.setItem === \"function\"\n ) {\n try {\n const stored = localStorage.getItem(storageKey);\n if (stored) return stored;\n const newId = generateId();\n localStorage.setItem(storageKey, newId);\n return newId;\n } catch {\n // localStorage present but unusable (e.g. partial Web Storage shim)\n }\n }\n return generateId();\n}\n\nfunction getClientContext(): ClientContext {\n const ctx: ClientContext = {\n library: { name: SDK_NAME, version: SDK_VERSION },\n };\n\n if (typeof navigator !== \"undefined\") {\n ctx.userAgent = navigator.userAgent;\n ctx.locale = navigator.language;\n }\n\n if (typeof Intl !== \"undefined\") {\n try {\n ctx.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n } catch {}\n }\n\n if (typeof screen !== \"undefined\") {\n ctx.screen = { width: screen.width, height: screen.height };\n }\n\n return ctx;\n}\n\nfunction getPageContext(): PageContext {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return {};\n }\n\n return {\n path: window.location.pathname,\n url: window.location.href,\n referrer: document.referrer || undefined,\n title: document.title || undefined,\n };\n}\n\nfunction simpleHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash; // Convert to 32bit integer\n }\n return hash.toString(36);\n}\n\nfunction getElementSelector(el: Element): string {\n const parts: string[] = [];\n \n if (el.id) {\n parts.push(`#${el.id}`);\n }\n \n if (el.className && typeof el.className === 'string') {\n const classes = el.className.trim().split(/\\s+/).filter(c => c.length > 0);\n if (classes.length > 0) {\n parts.push(`.${classes.slice(0, 2).join('.')}`);\n }\n }\n \n const tagName = el.tagName?.toLowerCase() || 'unknown';\n return parts.length > 0 ? `${tagName}${parts.join('')}` : tagName;\n}\n\nfunction getElementText(el: Element): string {\n const text = (el.textContent || '').trim().slice(0, 100);\n return text || (el as HTMLElement).getAttribute?.('aria-label') || '';\n}\n\n/**\n * Reactor Analytics client.\n *\n * Provides methods for tracking events, identifying users, and managing consent.\n */\nexport class ReactorAnalytics {\n private config: Required<AnalyticsConfig>;\n private anonymousId: string;\n private userId: string | undefined;\n private sessionId: string;\n private queue: QueuedEvent[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private optedOut: boolean = false;\n private errorFingerprints: Map<string, number> = new Map();\n\n constructor(config: AnalyticsConfig) {\n this.config = {\n projectKey: config.projectKey,\n endpoint: config.endpoint,\n batchSize: config.batchSize ?? DEFAULT_BATCH_SIZE,\n flushInterval: config.flushInterval ?? DEFAULT_FLUSH_INTERVAL,\n autoPageview: config.autoPageview ?? false,\n autoErrors: config.autoErrors ?? false,\n autoCapture: config.autoCapture ?? false,\n autoCaptureSelector: config.autoCaptureSelector ?? DEFAULT_AUTOCAPTURE_SELECTOR,\n errorDedupeWindow: config.errorDedupeWindow ?? DEFAULT_ERROR_DEDUPE_WINDOW,\n persistence: config.persistence ?? true,\n storageKey: config.storageKey ?? DEFAULT_STORAGE_KEY,\n debug: config.debug ?? false,\n };\n\n this.anonymousId = getAnonymousId(\n this.config.storageKey,\n this.config.persistence\n );\n this.sessionId = generateId();\n\n // Start flush timer\n this.startFlushTimer();\n\n // Auto-capture setup\n if (typeof window !== \"undefined\") {\n if (this.config.autoPageview) {\n this.setupAutoPageview();\n }\n if (this.config.autoErrors) {\n this.setupAutoErrors();\n }\n if (this.config.autoCapture) {\n this.setupAutoCapture();\n }\n // Flush on page unload\n window.addEventListener(\"beforeunload\", () => this.flush());\n }\n }\n\n /**\n * Track an event.\n *\n * @param event - Event name.\n * @param properties - Event properties.\n */\n track(event: string, properties: EventProperties = {}): void {\n if (this.optedOut) return;\n\n const queuedEvent: QueuedEvent = {\n event,\n properties,\n timestamp: new Date().toISOString(),\n anonymousId: this.anonymousId,\n userId: this.userId,\n sessionId: this.sessionId,\n context: { ...getClientContext(), ...getPageContext() },\n };\n\n this.enqueue(queuedEvent);\n }\n\n /**\n * Track a page view.\n *\n * @param name - Page name (optional).\n * @param properties - Additional properties.\n */\n page(name?: string, properties: EventProperties = {}): void {\n const pageContext = getPageContext();\n this.track(\"$pageview\", {\n ...properties,\n name: name || pageContext.title,\n path: pageContext.path,\n url: pageContext.url,\n referrer: pageContext.referrer,\n });\n }\n\n /**\n * Identify a user.\n *\n * @param userId - User ID.\n * @param traits - User traits (email, name, etc.).\n */\n identify(userId: string, traits: UserTraits = {}): void {\n if (this.optedOut) return;\n\n this.userId = userId;\n\n // Send identify event\n const queuedEvent: QueuedEvent = {\n event: \"$identify\",\n properties: traits as EventProperties,\n timestamp: new Date().toISOString(),\n anonymousId: this.anonymousId,\n userId,\n sessionId: this.sessionId,\n context: getClientContext(),\n };\n\n this.enqueue(queuedEvent);\n\n // Also send to identify endpoint\n this.sendIdentify(userId, traits);\n }\n\n /**\n * Alias an anonymous ID to a user ID.\n *\n * @param previousId - Previous anonymous ID.\n * @param userId - User ID to alias to.\n */\n alias(previousId: string, userId: string): void {\n if (this.optedOut) return;\n\n const queuedEvent: QueuedEvent = {\n event: \"$alias\",\n properties: { previousId, userId },\n timestamp: new Date().toISOString(),\n anonymousId: previousId,\n userId,\n sessionId: this.sessionId,\n context: getClientContext(),\n };\n\n this.enqueue(queuedEvent);\n\n // Also send to alias endpoint\n this.sendAlias(previousId, userId);\n }\n\n /**\n * Reset the user identity.\n * Generates a new anonymous ID and clears the user ID.\n */\n reset(): void {\n this.userId = undefined;\n this.anonymousId = generateId();\n this.sessionId = generateId();\n\n if (\n this.config.persistence &&\n typeof localStorage !== \"undefined\" &&\n typeof localStorage.setItem === \"function\"\n ) {\n try {\n localStorage.setItem(this.config.storageKey, this.anonymousId);\n } catch {\n // localStorage present but unusable\n }\n }\n }\n\n /**\n * Flush the event queue.\n */\n async flush(): Promise<void> {\n if (this.queue.length === 0) return;\n\n const events = this.queue.splice(0, this.queue.length);\n\n if (this.config.debug) {\n console.log(\"[ReactorAnalytics] Flushing events:\", events);\n }\n\n try {\n const response = await this.sendBatch(events);\n if (!response.ok && this.config.debug) {\n console.error(\"[ReactorAnalytics] Flush failed:\", response.status);\n // Put events back in queue for retry\n this.queue.unshift(...events);\n }\n } catch (error) {\n if (this.config.debug) {\n console.error(\"[ReactorAnalytics] Flush error:\", error);\n }\n // Put events back in queue for retry\n this.queue.unshift(...events);\n }\n }\n\n /**\n * Opt out of tracking.\n */\n optOut(): void {\n this.optedOut = true;\n this.queue = [];\n this.sendConsentOptOut();\n }\n\n /**\n * Opt in to tracking.\n */\n optIn(): void {\n this.optedOut = false;\n this.sendConsentOptIn();\n }\n\n /**\n * Get the current anonymous ID.\n */\n getAnonymousId(): string {\n return this.anonymousId;\n }\n\n /**\n * Get the current user ID.\n */\n getUserId(): string | undefined {\n return this.userId;\n }\n\n /**\n * Get the current session ID.\n */\n getSessionId(): string {\n return this.sessionId;\n }\n\n // --- Private methods ---\n\n private enqueue(event: QueuedEvent): void {\n this.queue.push(event);\n\n if (this.config.debug) {\n console.log(\"[ReactorAnalytics] Event queued:\", event);\n }\n\n if (this.queue.length >= this.config.batchSize) {\n this.flush();\n }\n }\n\n private startFlushTimer(): void {\n if (this.flushTimer) return;\n this.flushTimer = setInterval(() => {\n this.flush();\n }, this.config.flushInterval);\n }\n\n private async sendBatch(events: QueuedEvent[]): Promise<Response> {\n const body = {\n events: events.map((e) => ({\n event: e.event,\n anonymous_id: e.anonymousId,\n user_id: e.userId,\n session_id: e.sessionId,\n timestamp: e.timestamp,\n properties: e.properties,\n context: e.context,\n })),\n };\n\n // Use sendBeacon if available and page is unloading\n if (\n typeof navigator !== \"undefined\" &&\n navigator.sendBeacon &&\n document?.visibilityState === \"hidden\"\n ) {\n const blob = new Blob([JSON.stringify(body)], {\n type: \"application/json\",\n });\n navigator.sendBeacon(`${this.config.endpoint}/batch`, blob);\n return new Response(null, { status: 202 });\n }\n\n return fetch(`${this.config.endpoint}/batch`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Reactor-Project-Key\": this.config.projectKey,\n },\n body: JSON.stringify(body),\n keepalive: true,\n });\n }\n\n private async sendIdentify(userId: string, traits: UserTraits): Promise<void> {\n try {\n await fetch(`${this.config.endpoint}/identify`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Reactor-Project-Key\": this.config.projectKey,\n },\n body: JSON.stringify({\n anonymous_id: this.anonymousId,\n user_id: userId,\n traits,\n }),\n });\n } catch (error) {\n if (this.config.debug) {\n console.error(\"[ReactorAnalytics] Identify error:\", error);\n }\n }\n }\n\n private async sendAlias(previousId: string, userId: string): Promise<void> {\n try {\n await fetch(`${this.config.endpoint}/alias`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Reactor-Project-Key\": this.config.projectKey,\n },\n body: JSON.stringify({\n anonymous_id: previousId,\n user_id: userId,\n }),\n });\n } catch (error) {\n if (this.config.debug) {\n console.error(\"[ReactorAnalytics] Alias error:\", error);\n }\n }\n }\n\n private async sendConsentOptOut(): Promise<void> {\n try {\n await fetch(`${this.config.endpoint}/consent/opt-out`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Reactor-Project-Key\": this.config.projectKey,\n },\n body: JSON.stringify({ anonymous_id: this.anonymousId }),\n });\n } catch {}\n }\n\n private async sendConsentOptIn(): Promise<void> {\n try {\n await fetch(`${this.config.endpoint}/consent/opt-in`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Reactor-Project-Key\": this.config.projectKey,\n },\n body: JSON.stringify({ anonymous_id: this.anonymousId }),\n });\n } catch {}\n }\n\n private setupAutoPageview(): void {\n // Initial pageview\n this.page();\n\n // SPA navigation hooks\n if (typeof window !== \"undefined\") {\n const originalPushState = history.pushState;\n const originalReplaceState = history.replaceState;\n\n history.pushState = (...args) => {\n originalPushState.apply(history, args);\n this.page();\n };\n\n history.replaceState = (...args) => {\n originalReplaceState.apply(history, args);\n this.page();\n };\n\n window.addEventListener(\"popstate\", () => this.page());\n }\n }\n\n private setupAutoErrors(): void {\n if (typeof window === \"undefined\") return;\n\n window.addEventListener(\"error\", (event) => {\n const fingerprint = this.generateErrorFingerprint(\n event.message,\n event.filename,\n event.lineno\n );\n\n if (this.shouldTrackError(fingerprint)) {\n this.track(\"$error\", {\n message: event.message,\n filename: event.filename,\n lineno: event.lineno,\n colno: event.colno,\n error: event.error?.toString(),\n fingerprint,\n });\n }\n });\n\n window.addEventListener(\"unhandledrejection\", (event) => {\n const reason = String(event.reason);\n const fingerprint = this.generateErrorFingerprint(\n \"Unhandled Promise Rejection\",\n reason,\n 0\n );\n\n if (this.shouldTrackError(fingerprint)) {\n this.track(\"$error\", {\n message: \"Unhandled Promise Rejection\",\n reason,\n fingerprint,\n });\n }\n });\n }\n\n private generateErrorFingerprint(\n message: string,\n filename: string | undefined,\n lineno: number | undefined\n ): string {\n const key = `${message}|${filename || ''}|${lineno || 0}`;\n return simpleHash(key);\n }\n\n private shouldTrackError(fingerprint: string): boolean {\n const now = Date.now();\n const lastSeen = this.errorFingerprints.get(fingerprint);\n\n if (lastSeen && now - lastSeen < this.config.errorDedupeWindow) {\n if (this.config.debug) {\n console.log(\"[ReactorAnalytics] Deduped error:\", fingerprint);\n }\n return false;\n }\n\n this.errorFingerprints.set(fingerprint, now);\n\n // Cleanup old fingerprints periodically\n if (this.errorFingerprints.size > 100) {\n const cutoff = now - this.config.errorDedupeWindow * 2;\n for (const [key, time] of this.errorFingerprints.entries()) {\n if (time < cutoff) {\n this.errorFingerprints.delete(key);\n }\n }\n }\n\n return true;\n }\n\n private setupAutoCapture(): void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return;\n\n document.addEventListener(\"click\", (event) => {\n const target = event.target as Element | null;\n if (!target) return;\n\n // Find the closest matching element\n const el = target.closest(this.config.autoCaptureSelector);\n if (!el) return;\n\n const tagName = el.tagName?.toLowerCase() || 'unknown';\n const selector = getElementSelector(el);\n const text = getElementText(el);\n\n this.track(\"$autocapture\", {\n event_type: \"click\",\n tag_name: tagName,\n selector,\n text: text.slice(0, 100),\n href: (el as HTMLAnchorElement).href || undefined,\n name: el.getAttribute(\"name\") || undefined,\n id: el.id || undefined,\n classes: el.className || undefined,\n });\n }, { capture: true, passive: true });\n }\n}\n\n// Default export\nexport default ReactorAnalytics;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @reactor-cloud/analytics - Product analytics SDK for Reactor\n *\n * @example\n * ```ts\n * import { ReactorAnalytics } from '@reactor-cloud/analytics';\n *\n * const analytics = new ReactorAnalytics({\n * projectKey: 'pk_...',\n * endpoint: 'https://api.reactor.cloud/analytics/v1',\n * });\n *\n * analytics.track('button_clicked', { button_id: 'signup' });\n * analytics.identify('user_123', { email: 'user@example.com' });\n * ```\n */\n\n// Types\nexport interface AnalyticsConfig {\n /** Project key (X-Reactor-Project-Key header). */\n projectKey: string;\n /** Analytics API endpoint. */\n endpoint: string;\n /** Batch events before sending. */\n batchSize?: number;\n /** Flush interval in milliseconds. */\n flushInterval?: number;\n /** Auto-capture pageviews. */\n autoPageview?: boolean;\n /** Auto-capture errors with fingerprint coalescing. */\n autoErrors?: boolean;\n /** Auto-capture click interactions (opt-in). */\n autoCapture?: boolean;\n /** CSS selector for auto-capture targets. */\n autoCaptureSelector?: string;\n /** Error deduplication window in ms (default 5000). */\n errorDedupeWindow?: number;\n /** Persist anonymous ID to localStorage. */\n persistence?: boolean;\n /** Storage key for persistence. */\n storageKey?: string;\n /** Debug mode (logs events to console). */\n debug?: boolean;\n}\n\nexport interface EventProperties {\n [key: string]: string | number | boolean | null | undefined | EventProperties | EventProperties[];\n}\n\nexport interface UserTraits {\n email?: string;\n name?: string;\n [key: string]: string | number | boolean | null | undefined;\n}\n\nexport interface PageContext {\n path?: string;\n url?: string;\n referrer?: string;\n title?: string;\n}\n\nexport interface ClientContext {\n library?: { name: string; version: string };\n userAgent?: string;\n locale?: string;\n timezone?: string;\n screen?: { width: number; height: number };\n}\n\nexport interface QueuedEvent {\n event: string;\n properties: EventProperties;\n timestamp: string;\n anonymousId: string;\n userId?: string;\n sessionId?: string;\n context: ClientContext & PageContext;\n}\n\n// Constants\nconst SDK_NAME = \"@reactor-cloud/analytics\";\nconst SDK_VERSION = \"0.2.0\";\nconst DEFAULT_BATCH_SIZE = 20;\nconst DEFAULT_FLUSH_INTERVAL = 5000; // 5 seconds\nconst DEFAULT_STORAGE_KEY = \"reactor_anon_id\";\nconst DEFAULT_ERROR_DEDUPE_WINDOW = 5000; // 5 seconds\nconst DEFAULT_AUTOCAPTURE_SELECTOR = \"a, button, input[type='submit'], [data-reactor-capture]\";\n\n// Utility functions\nfunction generateId(): string {\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older browsers\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\nfunction getAnonymousId(storageKey: string, persistence: boolean): string {\n if (\n persistence &&\n typeof localStorage !== \"undefined\" &&\n typeof localStorage.getItem === \"function\" &&\n typeof localStorage.setItem === \"function\"\n ) {\n try {\n const stored = localStorage.getItem(storageKey);\n if (stored) return stored;\n const newId = generateId();\n localStorage.setItem(storageKey, newId);\n return newId;\n } catch {\n // localStorage present but unusable (e.g. partial Web Storage shim)\n }\n }\n return generateId();\n}\n\nfunction getClientContext(): ClientContext {\n const ctx: ClientContext = {\n library: { name: SDK_NAME, version: SDK_VERSION },\n };\n\n if (typeof navigator !== \"undefined\") {\n ctx.userAgent = navigator.userAgent;\n ctx.locale = navigator.language;\n }\n\n if (typeof Intl !== \"undefined\") {\n try {\n ctx.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n } catch {}\n }\n\n if (typeof screen !== \"undefined\") {\n ctx.screen = { width: screen.width, height: screen.height };\n }\n\n return ctx;\n}\n\nfunction getPageContext(): PageContext {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return {};\n }\n\n return {\n path: window.location.pathname,\n url: window.location.href,\n referrer: document.referrer || undefined,\n title: document.title || undefined,\n };\n}\n\nfunction simpleHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash; // Convert to 32bit integer\n }\n return hash.toString(36);\n}\n\nfunction getElementSelector(el: Element): string {\n const parts: string[] = [];\n \n if (el.id) {\n parts.push(`#${el.id}`);\n }\n \n if (el.className && typeof el.className === 'string') {\n const classes = el.className.trim().split(/\\s+/).filter(c => c.length > 0);\n if (classes.length > 0) {\n parts.push(`.${classes.slice(0, 2).join('.')}`);\n }\n }\n \n const tagName = el.tagName?.toLowerCase() || 'unknown';\n return parts.length > 0 ? `${tagName}${parts.join('')}` : tagName;\n}\n\nfunction getElementText(el: Element): string {\n const text = (el.textContent || '').trim().slice(0, 100);\n return text || (el as HTMLElement).getAttribute?.('aria-label') || '';\n}\n\n/**\n * Reactor Analytics client.\n *\n * Provides methods for tracking events, identifying users, and managing consent.\n */\nexport class ReactorAnalytics {\n private config: Required<AnalyticsConfig>;\n private anonymousId: string;\n private userId: string | undefined;\n private sessionId: string;\n private queue: QueuedEvent[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private optedOut: boolean = false;\n private errorFingerprints: Map<string, number> = new Map();\n\n constructor(config: AnalyticsConfig) {\n this.config = {\n projectKey: config.projectKey,\n endpoint: config.endpoint,\n batchSize: config.batchSize ?? DEFAULT_BATCH_SIZE,\n flushInterval: config.flushInterval ?? DEFAULT_FLUSH_INTERVAL,\n autoPageview: config.autoPageview ?? false,\n autoErrors: config.autoErrors ?? false,\n autoCapture: config.autoCapture ?? false,\n autoCaptureSelector: config.autoCaptureSelector ?? DEFAULT_AUTOCAPTURE_SELECTOR,\n errorDedupeWindow: config.errorDedupeWindow ?? DEFAULT_ERROR_DEDUPE_WINDOW,\n persistence: config.persistence ?? true,\n storageKey: config.storageKey ?? DEFAULT_STORAGE_KEY,\n debug: config.debug ?? false,\n };\n\n this.anonymousId = getAnonymousId(\n this.config.storageKey,\n this.config.persistence\n );\n this.sessionId = generateId();\n\n // Start flush timer\n this.startFlushTimer();\n\n // Auto-capture setup\n if (typeof window !== \"undefined\") {\n if (this.config.autoPageview) {\n this.setupAutoPageview();\n }\n if (this.config.autoErrors) {\n this.setupAutoErrors();\n }\n if (this.config.autoCapture) {\n this.setupAutoCapture();\n }\n // Flush on page unload\n window.addEventListener(\"beforeunload\", () => this.flush());\n }\n }\n\n /**\n * Track an event.\n *\n * @param event - Event name.\n * @param properties - Event properties.\n */\n track(event: string, properties: EventProperties = {}): void {\n if (this.optedOut) return;\n\n const queuedEvent: QueuedEvent = {\n event,\n properties,\n timestamp: new Date().toISOString(),\n anonymousId: this.anonymousId,\n userId: this.userId,\n sessionId: this.sessionId,\n context: { ...getClientContext(), ...getPageContext() },\n };\n\n this.enqueue(queuedEvent);\n }\n\n /**\n * Track a page view.\n *\n * @param name - Page name (optional).\n * @param properties - Additional properties.\n */\n page(name?: string, properties: EventProperties = {}): void {\n const pageContext = getPageContext();\n this.track(\"$pageview\", {\n ...properties,\n name: name || pageContext.title,\n path: pageContext.path,\n url: pageContext.url,\n referrer: pageContext.referrer,\n });\n }\n\n /**\n * Identify a user.\n *\n * @param userId - User ID.\n * @param traits - User traits (email, name, etc.).\n */\n identify(userId: string, traits: UserTraits = {}): void {\n if (this.optedOut) return;\n\n this.userId = userId;\n\n // Send identify event\n const queuedEvent: QueuedEvent = {\n event: \"$identify\",\n properties: traits as EventProperties,\n timestamp: new Date().toISOString(),\n anonymousId: this.anonymousId,\n userId,\n sessionId: this.sessionId,\n context: getClientContext(),\n };\n\n this.enqueue(queuedEvent);\n\n // Also send to identify endpoint\n this.sendIdentify(userId, traits);\n }\n\n /**\n * Alias an anonymous ID to a user ID.\n *\n * @param previousId - Previous anonymous ID.\n * @param userId - User ID to alias to.\n */\n alias(previousId: string, userId: string): void {\n if (this.optedOut) return;\n\n const queuedEvent: QueuedEvent = {\n event: \"$alias\",\n properties: { previousId, userId },\n timestamp: new Date().toISOString(),\n anonymousId: previousId,\n userId,\n sessionId: this.sessionId,\n context: getClientContext(),\n };\n\n this.enqueue(queuedEvent);\n\n // Also send to alias endpoint\n this.sendAlias(previousId, userId);\n }\n\n /**\n * Reset the user identity.\n * Generates a new anonymous ID and clears the user ID.\n */\n reset(): void {\n this.userId = undefined;\n this.anonymousId = generateId();\n this.sessionId = generateId();\n\n if (\n this.config.persistence &&\n typeof localStorage !== \"undefined\" &&\n typeof localStorage.setItem === \"function\"\n ) {\n try {\n localStorage.setItem(this.config.storageKey, this.anonymousId);\n } catch {\n // localStorage present but unusable\n }\n }\n }\n\n /**\n * Flush the event queue.\n */\n async flush(): Promise<void> {\n if (this.queue.length === 0) return;\n\n const events = this.queue.splice(0, this.queue.length);\n\n if (this.config.debug) {\n console.log(\"[ReactorAnalytics] Flushing events:\", events);\n }\n\n try {\n const response = await this.sendBatch(events);\n if (!response.ok && this.config.debug) {\n console.error(\"[ReactorAnalytics] Flush failed:\", response.status);\n // Put events back in queue for retry\n this.queue.unshift(...events);\n }\n } catch (error) {\n if (this.config.debug) {\n console.error(\"[ReactorAnalytics] Flush error:\", error);\n }\n // Put events back in queue for retry\n this.queue.unshift(...events);\n }\n }\n\n /**\n * Opt out of tracking.\n */\n optOut(): void {\n this.optedOut = true;\n this.queue = [];\n this.sendConsentOptOut();\n }\n\n /**\n * Opt in to tracking.\n */\n optIn(): void {\n this.optedOut = false;\n this.sendConsentOptIn();\n }\n\n /**\n * Get the current anonymous ID.\n */\n getAnonymousId(): string {\n return this.anonymousId;\n }\n\n /**\n * Get the current user ID.\n */\n getUserId(): string | undefined {\n return this.userId;\n }\n\n /**\n * Get the current session ID.\n */\n getSessionId(): string {\n return this.sessionId;\n }\n\n // --- Private methods ---\n\n private enqueue(event: QueuedEvent): void {\n this.queue.push(event);\n\n if (this.config.debug) {\n console.log(\"[ReactorAnalytics] Event queued:\", event);\n }\n\n if (this.queue.length >= this.config.batchSize) {\n this.flush();\n }\n }\n\n private startFlushTimer(): void {\n if (this.flushTimer) return;\n this.flushTimer = setInterval(() => {\n this.flush();\n }, this.config.flushInterval);\n }\n\n private async sendBatch(events: QueuedEvent[]): Promise<Response> {\n const body = {\n events: events.map((e) => ({\n event: e.event,\n anonymous_id: e.anonymousId,\n user_id: e.userId,\n session_id: e.sessionId,\n timestamp: e.timestamp,\n properties: e.properties,\n context: e.context,\n })),\n };\n\n // Use sendBeacon if available and page is unloading\n if (\n typeof navigator !== \"undefined\" &&\n navigator.sendBeacon &&\n document?.visibilityState === \"hidden\"\n ) {\n const blob = new Blob([JSON.stringify(body)], {\n type: \"application/json\",\n });\n navigator.sendBeacon(`${this.config.endpoint}/batch`, blob);\n return new Response(null, { status: 202 });\n }\n\n return fetch(`${this.config.endpoint}/batch`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Reactor-Project-Key\": this.config.projectKey,\n },\n body: JSON.stringify(body),\n keepalive: true,\n });\n }\n\n private async sendIdentify(userId: string, traits: UserTraits): Promise<void> {\n try {\n await fetch(`${this.config.endpoint}/identify`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Reactor-Project-Key\": this.config.projectKey,\n },\n body: JSON.stringify({\n anonymous_id: this.anonymousId,\n user_id: userId,\n traits,\n }),\n });\n } catch (error) {\n if (this.config.debug) {\n console.error(\"[ReactorAnalytics] Identify error:\", error);\n }\n }\n }\n\n private async sendAlias(previousId: string, userId: string): Promise<void> {\n try {\n await fetch(`${this.config.endpoint}/alias`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Reactor-Project-Key\": this.config.projectKey,\n },\n body: JSON.stringify({\n anonymous_id: previousId,\n user_id: userId,\n }),\n });\n } catch (error) {\n if (this.config.debug) {\n console.error(\"[ReactorAnalytics] Alias error:\", error);\n }\n }\n }\n\n private async sendConsentOptOut(): Promise<void> {\n try {\n await fetch(`${this.config.endpoint}/consent/opt-out`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Reactor-Project-Key\": this.config.projectKey,\n },\n body: JSON.stringify({ anonymous_id: this.anonymousId }),\n });\n } catch {}\n }\n\n private async sendConsentOptIn(): Promise<void> {\n try {\n await fetch(`${this.config.endpoint}/consent/opt-in`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Reactor-Project-Key\": this.config.projectKey,\n },\n body: JSON.stringify({ anonymous_id: this.anonymousId }),\n });\n } catch {}\n }\n\n private setupAutoPageview(): void {\n // Initial pageview\n this.page();\n\n // SPA navigation hooks\n if (typeof window !== \"undefined\") {\n const originalPushState = history.pushState;\n const originalReplaceState = history.replaceState;\n\n history.pushState = (...args) => {\n originalPushState.apply(history, args);\n this.page();\n };\n\n history.replaceState = (...args) => {\n originalReplaceState.apply(history, args);\n this.page();\n };\n\n window.addEventListener(\"popstate\", () => this.page());\n }\n }\n\n private setupAutoErrors(): void {\n if (typeof window === \"undefined\") return;\n\n window.addEventListener(\"error\", (event) => {\n const fingerprint = this.generateErrorFingerprint(\n event.message,\n event.filename,\n event.lineno\n );\n\n if (this.shouldTrackError(fingerprint)) {\n this.track(\"$error\", {\n message: event.message,\n filename: event.filename,\n lineno: event.lineno,\n colno: event.colno,\n error: event.error?.toString(),\n fingerprint,\n });\n }\n });\n\n window.addEventListener(\"unhandledrejection\", (event) => {\n const reason = String(event.reason);\n const fingerprint = this.generateErrorFingerprint(\n \"Unhandled Promise Rejection\",\n reason,\n 0\n );\n\n if (this.shouldTrackError(fingerprint)) {\n this.track(\"$error\", {\n message: \"Unhandled Promise Rejection\",\n reason,\n fingerprint,\n });\n }\n });\n }\n\n private generateErrorFingerprint(\n message: string,\n filename: string | undefined,\n lineno: number | undefined\n ): string {\n const key = `${message}|${filename || ''}|${lineno || 0}`;\n return simpleHash(key);\n }\n\n private shouldTrackError(fingerprint: string): boolean {\n const now = Date.now();\n const lastSeen = this.errorFingerprints.get(fingerprint);\n\n if (lastSeen && now - lastSeen < this.config.errorDedupeWindow) {\n if (this.config.debug) {\n console.log(\"[ReactorAnalytics] Deduped error:\", fingerprint);\n }\n return false;\n }\n\n this.errorFingerprints.set(fingerprint, now);\n\n // Cleanup old fingerprints periodically\n if (this.errorFingerprints.size > 100) {\n const cutoff = now - this.config.errorDedupeWindow * 2;\n for (const [key, time] of this.errorFingerprints.entries()) {\n if (time < cutoff) {\n this.errorFingerprints.delete(key);\n }\n }\n }\n\n return true;\n }\n\n private setupAutoCapture(): void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return;\n\n document.addEventListener(\"click\", (event) => {\n const target = event.target as Element | null;\n if (!target) return;\n\n // Find the closest matching element\n const el = target.closest(this.config.autoCaptureSelector);\n if (!el) return;\n\n const tagName = el.tagName?.toLowerCase() || 'unknown';\n const selector = getElementSelector(el);\n const text = getElementText(el);\n\n this.track(\"$autocapture\", {\n event_type: \"click\",\n tag_name: tagName,\n selector,\n text: text.slice(0, 100),\n href: (el as HTMLAnchorElement).href || undefined,\n name: el.getAttribute(\"name\") || undefined,\n id: el.id || undefined,\n classes: el.className || undefined,\n });\n }, { capture: true, passive: true });\n }\n}\n\n// Default export\nexport default ReactorAnalytics;\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reactor-cloud/analytics",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Analytics client for Reactor JS SDK - product analytics, event tracking, user identification",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"reactor",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"dist"
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@reactor-cloud/shared": "0.
|
|
36
|
+
"@reactor-cloud/shared": "0.3.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@playwright/test": "^1.52.0",
|