@proyecta-ai/analytics 0.0.4 → 0.0.6
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/index.d.ts +44 -0
- package/index.d.ts.map +1 -0
- package/index.js +9 -14
- package/index.js.map +2 -2
- package/package.json +1 -1
- package/schema.d.ts +58 -0
- package/schema.d.ts.map +1 -0
- package/types.d.ts +53 -0
- package/types.d.ts.map +1 -0
- package/utils.d.ts +21 -0
- package/utils.d.ts.map +1 -0
package/index.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @proyecta-ai/analytics
|
|
3
|
+
* Comprehensive analytics library for Proyecta applications
|
|
4
|
+
*/
|
|
5
|
+
import type { ProyectaEvent } from './schema';
|
|
6
|
+
import type { BrowserInfo, OSInfo, ReferrerInfo, UTMParams, PerformanceMetrics, AnalyticsConfig, AnalyticsProvider } from './types';
|
|
7
|
+
import { generateId, getCookie, setCookie, parseUserAgent, getReferrerInfo, getUTMParams, getPerformanceMetrics, observeWebVitals, debounce, throttle, safeStringify, getBrowserInfo } from './utils';
|
|
8
|
+
export type { BrowserInfo, OSInfo, ReferrerInfo, UTMParams, PerformanceMetrics, AnalyticsConfig, AnalyticsProvider, };
|
|
9
|
+
export { generateId, getCookie, setCookie, parseUserAgent, getReferrerInfo, getUTMParams, getPerformanceMetrics, observeWebVitals, debounce, throttle, safeStringify, getBrowserInfo, };
|
|
10
|
+
export declare class Analytics {
|
|
11
|
+
private config;
|
|
12
|
+
private buffer;
|
|
13
|
+
private providers;
|
|
14
|
+
private flushTimer;
|
|
15
|
+
private visitorId;
|
|
16
|
+
private sessionId;
|
|
17
|
+
private userId;
|
|
18
|
+
private webVitalsMetrics;
|
|
19
|
+
constructor(config: AnalyticsConfig);
|
|
20
|
+
private getVisitorId;
|
|
21
|
+
private getSessionId;
|
|
22
|
+
private createEvent;
|
|
23
|
+
private setupClickTracking;
|
|
24
|
+
private sendToEndpoint;
|
|
25
|
+
private startAutoFlush;
|
|
26
|
+
private stopAutoFlush;
|
|
27
|
+
track(eventName: string, properties?: Record<string, string | number>): void;
|
|
28
|
+
private setupWebVitals;
|
|
29
|
+
trackPageview(properties?: Record<string, string | number>): void;
|
|
30
|
+
private trackWebVitals;
|
|
31
|
+
trackPageleave(): void;
|
|
32
|
+
addProvider(provider: AnalyticsProvider): void;
|
|
33
|
+
removeProvider(name: string): void;
|
|
34
|
+
flush(): Promise<void>;
|
|
35
|
+
identify(userId: string): void;
|
|
36
|
+
reset(): void;
|
|
37
|
+
destroy(): void;
|
|
38
|
+
}
|
|
39
|
+
export declare class ConsoleProvider implements AnalyticsProvider {
|
|
40
|
+
name: string;
|
|
41
|
+
send(events: ProyectaEvent[]): Promise<void>;
|
|
42
|
+
}
|
|
43
|
+
export declare function initAnalytics(config: AnalyticsConfig): Analytics;
|
|
44
|
+
//# sourceMappingURL=index.d.ts.map
|
package/index.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EACV,WAAW,EACX,MAAM,EACN,YAAY,EACZ,SAAS,EACT,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,UAAU,EACV,SAAS,EACT,SAAS,EACT,cAAc,EACd,eAAe,EACf,YAAY,EACZ,qBAAqB,EACrB,gBAAgB,EAChB,QAAQ,EACR,QAAQ,EACR,aAAa,EACb,cAAc,EACf,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,WAAW,EACX,MAAM,EACN,YAAY,EACZ,SAAS,EACT,kBAAkB,EAClB,eAAe,EACf,iBAAiB,GAClB,CAAC;AAEF,OAAO,EACL,UAAU,EACV,SAAS,EACT,SAAS,EACT,cAAc,EACd,eAAe,EACf,YAAY,EACZ,qBAAqB,EACrB,gBAAgB,EAChB,QAAQ,EACR,QAAQ,EACR,aAAa,EACb,cAAc,GACf,CAAC;AAEF,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,SAAS,CAA2B;IAC5C,OAAO,CAAC,UAAU,CAA4C;IAC9D,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,gBAAgB,CAA0B;gBAEtC,MAAM,EAAE,eAAe;IA4CnC,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,YAAY;IAyBpB,OAAO,CAAC,WAAW;IAmDnB,OAAO,CAAC,kBAAkB;YAsBZ,cAAc;IAwB5B,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,aAAa;IAOrB,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,IAAI;IAW5E,OAAO,CAAC,cAAc;IAgCtB,aAAa,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,IAAI;IAmBjE,OAAO,CAAC,cAAc;IAsCtB,cAAc,IAAI,IAAI;IAQtB,WAAW,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI;IAO9C,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI5B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAiC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAW9B,KAAK,IAAI,IAAI;IAOb,OAAO,IAAI,IAAI;CAMhB;AAED,qBAAa,eAAgB,YAAW,iBAAiB;IACvD,IAAI,SAAa;IAEX,IAAI,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAKnD;AAID,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CAehE"}
|
package/index.js
CHANGED
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
safeStringify,
|
|
15
15
|
getBrowserInfo
|
|
16
16
|
} from "./utils";
|
|
17
|
-
const DEFAULT_TINYBIRD_ENDPOINT = "https://api.us-east.tinybird.co/v0/events?name=proyecta_events";
|
|
18
17
|
class Analytics {
|
|
19
18
|
static {
|
|
20
19
|
__name(this, "Analytics");
|
|
@@ -30,8 +29,8 @@ class Analytics {
|
|
|
30
29
|
constructor(config) {
|
|
31
30
|
this.config = {
|
|
32
31
|
appId: config.appId,
|
|
33
|
-
|
|
34
|
-
apiEndpoint: config.apiEndpoint ??
|
|
32
|
+
apiKey: config.apiKey ?? "",
|
|
33
|
+
apiEndpoint: config.apiEndpoint ?? "",
|
|
35
34
|
debug: config.debug ?? false,
|
|
36
35
|
enabled: config.enabled ?? !!config.appId,
|
|
37
36
|
bufferSize: config.bufferSize ?? 10,
|
|
@@ -154,21 +153,17 @@ class Analytics {
|
|
|
154
153
|
});
|
|
155
154
|
}
|
|
156
155
|
async sendToEndpoint(events) {
|
|
157
|
-
if (!this.config.apiEndpoint || !this.config.enabled || !this.config.
|
|
158
|
-
const ndjson = events.map((e) => JSON.stringify(e)).join("\n");
|
|
156
|
+
if (!this.config.apiEndpoint || !this.config.enabled || !this.config.apiKey) return;
|
|
159
157
|
if (this.config.debug) {
|
|
160
158
|
console.log("[Proyecta Analytics] Sending", events.length, "events");
|
|
161
159
|
}
|
|
162
|
-
const
|
|
163
|
-
"Content-Type": "application/x-ndjson"
|
|
164
|
-
};
|
|
165
|
-
if (this.config.token) {
|
|
166
|
-
headers["Authorization"] = `Bearer ${this.config.token}`;
|
|
167
|
-
}
|
|
168
|
-
const response = await fetch(this.config.apiEndpoint, {
|
|
160
|
+
const response = await fetch(`${this.config.apiEndpoint}/v1/analytics/batch`, {
|
|
169
161
|
method: "POST",
|
|
170
|
-
headers
|
|
171
|
-
|
|
162
|
+
headers: {
|
|
163
|
+
"Content-Type": "application/json",
|
|
164
|
+
Authorization: `Bearer ${this.config.apiKey}`
|
|
165
|
+
},
|
|
166
|
+
body: JSON.stringify({ events }),
|
|
172
167
|
keepalive: true
|
|
173
168
|
});
|
|
174
169
|
if (!response.ok) {
|
package/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * @proyecta-ai/analytics\n * Comprehensive analytics library for Proyecta applications\n */\n\nimport type { ProyectaEvent } from './schema';\nimport type {\n BrowserInfo,\n OSInfo,\n ReferrerInfo,\n UTMParams,\n PerformanceMetrics,\n AnalyticsConfig,\n AnalyticsProvider,\n} from './types';\n\nimport {\n generateId,\n getCookie,\n setCookie,\n parseUserAgent,\n getReferrerInfo,\n getUTMParams,\n getPerformanceMetrics,\n observeWebVitals,\n debounce,\n throttle,\n safeStringify,\n getBrowserInfo,\n} from './utils';\n\nexport type {\n BrowserInfo,\n OSInfo,\n ReferrerInfo,\n UTMParams,\n PerformanceMetrics,\n AnalyticsConfig,\n AnalyticsProvider,\n};\n\nexport {\n generateId,\n getCookie,\n setCookie,\n parseUserAgent,\n getReferrerInfo,\n getUTMParams,\n getPerformanceMetrics,\n observeWebVitals,\n debounce,\n throttle,\n safeStringify,\n getBrowserInfo,\n};\n\n/** Default Tinybird Events API endpoint. Override via config.apiEndpoint. */\nconst DEFAULT_TINYBIRD_ENDPOINT = 'https://api.us-east.tinybird.co/v0/events?name=proyecta_events';\n\nexport class Analytics {\n private config: Required<AnalyticsConfig>;\n private buffer: ProyectaEvent[] = [];\n private providers: AnalyticsProvider[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | undefined;\n private visitorId: string;\n private sessionId: string;\n private userId: string | undefined;\n private webVitalsMetrics: PerformanceMetrics = {};\n\n constructor(config: AnalyticsConfig) {\n this.config = {\n appId: config.appId,\n token: config.token ?? '',\n apiEndpoint: config.apiEndpoint ?? DEFAULT_TINYBIRD_ENDPOINT,\n debug: config.debug ?? false,\n enabled: config.enabled ?? !!config.appId,\n bufferSize: config.bufferSize ?? 10,\n flushInterval: config.flushInterval ?? 5000,\n trackClicks: config.trackClicks ?? false,\n trackPerformance: config.trackPerformance ?? true,\n cookieDomain: config.cookieDomain ?? '',\n cookiePath: config.cookiePath ?? '/',\n sessionTimeout: config.sessionTimeout ?? 30,\n };\n\n this.visitorId = this.getVisitorId();\n this.sessionId = this.getSessionId();\n\n if (this.config.flushInterval > 0) {\n this.startAutoFlush();\n }\n\n if (typeof document !== 'undefined') {\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') {\n this.flush();\n }\n });\n\n window.addEventListener('beforeunload', () => {\n this.trackPageleave();\n });\n }\n\n if (this.config.trackClicks && typeof document !== 'undefined') {\n this.setupClickTracking();\n }\n\n if (this.config.trackPerformance) {\n this.setupWebVitals();\n }\n }\n\n private getVisitorId(): string {\n let visitorId = getCookie('_pry_vid');\n if (!visitorId) {\n visitorId = generateId();\n setCookie('_pry_vid', visitorId, 365 * 2, this.config.cookieDomain, this.config.cookiePath);\n }\n return visitorId;\n }\n\n private getSessionId(): string {\n if (typeof sessionStorage === 'undefined') {\n return generateId();\n }\n\n let sessionId = sessionStorage.getItem('_pry_sid');\n const lastActivity = sessionStorage.getItem('_pry_last_activity');\n const now = Date.now();\n\n if (sessionId && lastActivity) {\n const timeSinceLastActivity = now - parseInt(lastActivity, 10);\n if (timeSinceLastActivity > this.config.sessionTimeout * 60 * 1000) {\n sessionId = null;\n }\n }\n\n if (!sessionId) {\n sessionId = generateId();\n sessionStorage.setItem('_pry_sid', sessionId);\n }\n\n sessionStorage.setItem('_pry_last_activity', now.toString());\n return sessionId;\n }\n\n private createEvent(\n eventType: ProyectaEvent['event_type'],\n eventName = '',\n properties?: Record<string, string | number>,\n ): ProyectaEvent {\n const { browser, os } = parseUserAgent();\n const referrerInfo = getReferrerInfo();\n const utmParams = getUTMParams();\n const url = new URL(window.location.href);\n\n const event: ProyectaEvent = {\n app_id: this.config.appId,\n event_id: generateId(),\n event_type: eventType,\n event_name: eventName,\n timestamp: Date.now(),\n visitor_id: this.visitorId,\n session_id: this.sessionId,\n ...(this.userId && { user_id: this.userId }),\n origin: url.origin,\n url: url.href,\n url_path: url.pathname,\n url_query: url.search.substring(1),\n url_hash: url.hash.substring(1),\n referrer: referrerInfo.referrer,\n referrer_domain: referrerInfo.referrer_domain,\n referrer_source: referrerInfo.referrer_source,\n browser: browser.name,\n browser_version: browser.version,\n os: os.name,\n os_version: os.version,\n utm_source: utmParams.utm_source,\n utm_medium: utmParams.utm_medium,\n utm_campaign: utmParams.utm_campaign,\n utm_content: utmParams.utm_content,\n utm_term: utmParams.utm_term,\n properties: {},\n properties_numeric: {},\n };\n\n for (const [key, value] of Object.entries(properties || {})) {\n if (typeof value === 'number') {\n event.properties_numeric![key] = value;\n } else {\n event.properties![key] = value;\n }\n }\n\n return event;\n }\n\n private setupClickTracking(): void {\n document.addEventListener('click', (e) => {\n const target = e.target as HTMLElement;\n const tagName = target.tagName.toLowerCase();\n\n if (tagName === 'a' || tagName === 'button' || target.getAttribute('data-track-click')) {\n const properties: Record<string, string> = {\n element: tagName,\n text: target.textContent?.substring(0, 100) || '',\n class: target.className || '',\n id: target.id || '',\n };\n\n if (tagName === 'a') {\n properties.href = (target as HTMLAnchorElement).href || '';\n }\n\n this.track('click', properties);\n }\n });\n }\n\n private async sendToEndpoint(events: ProyectaEvent[]): Promise<void> {\n if (!this.config.apiEndpoint || !this.config.enabled || !this.config.token) return;\n\n // Tinybird Events API expects NDJSON (newline-delimited JSON)\n const ndjson = events.map((e) => JSON.stringify(e)).join('\\n');\n\n if (this.config.debug) {\n console.log('[Proyecta Analytics] Sending', events.length, 'events');\n }\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/x-ndjson',\n };\n if (this.config.token) {\n headers['Authorization'] = `Bearer ${this.config.token}`;\n }\n\n // Use fetch with keepalive for reliable delivery on page unload.\n // sendBeacon can't set custom headers (needed for Bearer auth).\n const response = await fetch(this.config.apiEndpoint, {\n method: 'POST',\n headers,\n body: ndjson,\n keepalive: true,\n });\n\n if (!response.ok) {\n throw new Error(`Analytics endpoint returned ${response.status}`);\n }\n }\n\n private startAutoFlush(): void {\n this.stopAutoFlush();\n this.flushTimer = setInterval(() => {\n this.flush();\n }, this.config.flushInterval);\n }\n\n private stopAutoFlush(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = undefined;\n }\n }\n\n track(eventName: string, properties?: Record<string, string | number>): void {\n if (!this.config.enabled) return;\n\n const event = this.createEvent('custom', eventName, properties);\n this.buffer.push(event);\n\n if (this.buffer.length >= this.config.bufferSize) {\n this.flush();\n }\n }\n\n private setupWebVitals(): void {\n let webVitalsEventSent = false;\n let webVitalsTimeout: ReturnType<typeof setTimeout> | undefined;\n\n observeWebVitals((metrics) => {\n this.webVitalsMetrics = { ...this.webVitalsMetrics, ...metrics };\n\n if (webVitalsTimeout) {\n clearTimeout(webVitalsTimeout);\n }\n\n const hasCriticalMetrics =\n (this.webVitalsMetrics.largest_contentful_paint !== undefined ||\n this.webVitalsMetrics.first_contentful_paint !== undefined) &&\n this.webVitalsMetrics.cumulative_layout_shift !== undefined;\n\n if (hasCriticalMetrics && !webVitalsEventSent) {\n webVitalsTimeout = setTimeout(() => {\n this.trackWebVitals();\n webVitalsEventSent = true;\n }, 500);\n }\n });\n\n setTimeout(() => {\n if (!webVitalsEventSent) {\n this.trackWebVitals();\n webVitalsEventSent = true;\n }\n }, 5000);\n }\n\n trackPageview(properties?: Record<string, string | number>): void {\n if (!this.config.enabled) return;\n\n const event = this.createEvent('pageview', '', properties);\n\n if (this.config.trackPerformance) {\n const perfMetrics = getPerformanceMetrics();\n if (perfMetrics.page_load_time !== undefined) event.plt = perfMetrics.page_load_time;\n if (perfMetrics.dom_interactive !== undefined) event.di = perfMetrics.dom_interactive;\n if (perfMetrics.time_to_first_byte !== undefined) event.ttfb = perfMetrics.time_to_first_byte;\n }\n\n this.buffer.push(event);\n\n if (this.buffer.length >= this.config.bufferSize) {\n this.flush();\n }\n }\n\n private trackWebVitals(): void {\n if (!this.config.enabled || !this.config.trackPerformance) return;\n\n const hasVitals =\n this.webVitalsMetrics.first_contentful_paint !== undefined ||\n this.webVitalsMetrics.largest_contentful_paint !== undefined ||\n this.webVitalsMetrics.cumulative_layout_shift !== undefined ||\n this.webVitalsMetrics.first_input_delay !== undefined ||\n this.webVitalsMetrics.interaction_to_next_paint !== undefined;\n\n if (!hasVitals) return;\n\n const event = this.createEvent('web_vitals', '');\n\n if (this.webVitalsMetrics.first_contentful_paint !== undefined)\n event.fcp = this.webVitalsMetrics.first_contentful_paint;\n if (this.webVitalsMetrics.largest_contentful_paint !== undefined)\n event.lcp = this.webVitalsMetrics.largest_contentful_paint;\n if (this.webVitalsMetrics.cumulative_layout_shift !== undefined)\n event.cls = this.webVitalsMetrics.cumulative_layout_shift;\n if (this.webVitalsMetrics.first_input_delay !== undefined)\n event.fid = this.webVitalsMetrics.first_input_delay;\n if (this.webVitalsMetrics.interaction_to_next_paint !== undefined)\n event.inp = this.webVitalsMetrics.interaction_to_next_paint;\n if (this.webVitalsMetrics.time_to_first_byte !== undefined)\n event.ttfb = this.webVitalsMetrics.time_to_first_byte;\n if (this.webVitalsMetrics.page_load_time !== undefined)\n event.plt = this.webVitalsMetrics.page_load_time;\n if (this.webVitalsMetrics.dom_interactive !== undefined)\n event.di = this.webVitalsMetrics.dom_interactive;\n\n this.buffer.push(event);\n\n if (this.buffer.length >= this.config.bufferSize) {\n this.flush();\n }\n }\n\n trackPageleave(): void {\n if (!this.config.enabled) return;\n\n const event = this.createEvent('pageleave', '');\n this.buffer.push(event);\n this.flush();\n }\n\n addProvider(provider: AnalyticsProvider): void {\n this.providers.push(provider);\n if (this.config.debug) {\n console.log(`[Proyecta Analytics] Provider '${provider.name}' added`);\n }\n }\n\n removeProvider(name: string): void {\n this.providers = this.providers.filter((p) => p.name !== name);\n }\n\n async flush(): Promise<void> {\n if (this.buffer.length === 0) return;\n\n const events = [...this.buffer];\n this.buffer = [];\n\n try {\n await Promise.all([\n ...this.providers.map((provider) =>\n provider.send(events).catch((err) => {\n if (this.config.debug) {\n console.error(`[Proyecta Analytics] Provider '${provider.name}' error:`, err);\n }\n }),\n ),\n this.sendToEndpoint(events).catch((err) => {\n if (this.config.debug) {\n console.error('[Proyecta Analytics] Endpoint error:', err);\n }\n }),\n ]);\n\n if (this.config.debug) {\n console.log(`[Proyecta Analytics] Flushed ${events.length} events`);\n }\n } catch (error) {\n if (this.config.debug) {\n console.error('[Proyecta Analytics] Flush error:', error);\n }\n this.buffer.unshift(...events);\n }\n }\n\n identify(userId: string): void {\n this.userId = userId;\n if (this.config.enabled) {\n const event = this.createEvent('custom', 'identify', { user_id: userId });\n this.buffer.push(event);\n if (this.buffer.length >= this.config.bufferSize) {\n this.flush();\n }\n }\n }\n\n reset(): void {\n this.buffer = [];\n this.sessionId = this.getSessionId();\n this.userId = undefined;\n this.webVitalsMetrics = {};\n }\n\n destroy(): void {\n this.stopAutoFlush();\n this.flush();\n this.providers = [];\n this.buffer = [];\n }\n}\n\nexport class ConsoleProvider implements AnalyticsProvider {\n name = 'console';\n\n async send(events: ProyectaEvent[]): Promise<void> {\n events.forEach((event) => {\n console.log('[ConsoleProvider]', event);\n });\n }\n}\n\nlet defaultInstance: Analytics | null = null;\n\nexport function initAnalytics(config: AnalyticsConfig): Analytics {\n if (!defaultInstance) {\n defaultInstance = new Analytics(config);\n\n if (typeof window !== 'undefined') {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => {\n defaultInstance?.trackPageview();\n });\n } else {\n defaultInstance?.trackPageview();\n }\n }\n }\n return defaultInstance;\n}\n"],
|
|
5
|
-
"mappings": ";;AAgBA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;
|
|
4
|
+
"sourcesContent": ["/**\n * @proyecta-ai/analytics\n * Comprehensive analytics library for Proyecta applications\n */\n\nimport type { ProyectaEvent } from './schema';\nimport type {\n BrowserInfo,\n OSInfo,\n ReferrerInfo,\n UTMParams,\n PerformanceMetrics,\n AnalyticsConfig,\n AnalyticsProvider,\n} from './types';\n\nimport {\n generateId,\n getCookie,\n setCookie,\n parseUserAgent,\n getReferrerInfo,\n getUTMParams,\n getPerformanceMetrics,\n observeWebVitals,\n debounce,\n throttle,\n safeStringify,\n getBrowserInfo,\n} from './utils';\n\nexport type {\n BrowserInfo,\n OSInfo,\n ReferrerInfo,\n UTMParams,\n PerformanceMetrics,\n AnalyticsConfig,\n AnalyticsProvider,\n};\n\nexport {\n generateId,\n getCookie,\n setCookie,\n parseUserAgent,\n getReferrerInfo,\n getUTMParams,\n getPerformanceMetrics,\n observeWebVitals,\n debounce,\n throttle,\n safeStringify,\n getBrowserInfo,\n};\n\nexport class Analytics {\n private config: Required<AnalyticsConfig>;\n private buffer: ProyectaEvent[] = [];\n private providers: AnalyticsProvider[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | undefined;\n private visitorId: string;\n private sessionId: string;\n private userId: string | undefined;\n private webVitalsMetrics: PerformanceMetrics = {};\n\n constructor(config: AnalyticsConfig) {\n this.config = {\n appId: config.appId,\n apiKey: config.apiKey ?? '',\n apiEndpoint: config.apiEndpoint ?? '',\n debug: config.debug ?? false,\n enabled: config.enabled ?? !!config.appId,\n bufferSize: config.bufferSize ?? 10,\n flushInterval: config.flushInterval ?? 5000,\n trackClicks: config.trackClicks ?? false,\n trackPerformance: config.trackPerformance ?? true,\n cookieDomain: config.cookieDomain ?? '',\n cookiePath: config.cookiePath ?? '/',\n sessionTimeout: config.sessionTimeout ?? 30,\n };\n\n this.visitorId = this.getVisitorId();\n this.sessionId = this.getSessionId();\n\n if (this.config.flushInterval > 0) {\n this.startAutoFlush();\n }\n\n if (typeof document !== 'undefined') {\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') {\n this.flush();\n }\n });\n\n window.addEventListener('beforeunload', () => {\n this.trackPageleave();\n });\n }\n\n if (this.config.trackClicks && typeof document !== 'undefined') {\n this.setupClickTracking();\n }\n\n if (this.config.trackPerformance) {\n this.setupWebVitals();\n }\n }\n\n private getVisitorId(): string {\n let visitorId = getCookie('_pry_vid');\n if (!visitorId) {\n visitorId = generateId();\n setCookie('_pry_vid', visitorId, 365 * 2, this.config.cookieDomain, this.config.cookiePath);\n }\n return visitorId;\n }\n\n private getSessionId(): string {\n if (typeof sessionStorage === 'undefined') {\n return generateId();\n }\n\n let sessionId = sessionStorage.getItem('_pry_sid');\n const lastActivity = sessionStorage.getItem('_pry_last_activity');\n const now = Date.now();\n\n if (sessionId && lastActivity) {\n const timeSinceLastActivity = now - parseInt(lastActivity, 10);\n if (timeSinceLastActivity > this.config.sessionTimeout * 60 * 1000) {\n sessionId = null;\n }\n }\n\n if (!sessionId) {\n sessionId = generateId();\n sessionStorage.setItem('_pry_sid', sessionId);\n }\n\n sessionStorage.setItem('_pry_last_activity', now.toString());\n return sessionId;\n }\n\n private createEvent(\n eventType: ProyectaEvent['event_type'],\n eventName = '',\n properties?: Record<string, string | number>,\n ): ProyectaEvent {\n const { browser, os } = parseUserAgent();\n const referrerInfo = getReferrerInfo();\n const utmParams = getUTMParams();\n const url = new URL(window.location.href);\n\n const event: ProyectaEvent = {\n app_id: this.config.appId,\n event_id: generateId(),\n event_type: eventType,\n event_name: eventName,\n timestamp: Date.now(),\n visitor_id: this.visitorId,\n session_id: this.sessionId,\n ...(this.userId && { user_id: this.userId }),\n origin: url.origin,\n url: url.href,\n url_path: url.pathname,\n url_query: url.search.substring(1),\n url_hash: url.hash.substring(1),\n referrer: referrerInfo.referrer,\n referrer_domain: referrerInfo.referrer_domain,\n referrer_source: referrerInfo.referrer_source,\n browser: browser.name,\n browser_version: browser.version,\n os: os.name,\n os_version: os.version,\n utm_source: utmParams.utm_source,\n utm_medium: utmParams.utm_medium,\n utm_campaign: utmParams.utm_campaign,\n utm_content: utmParams.utm_content,\n utm_term: utmParams.utm_term,\n properties: {},\n properties_numeric: {},\n };\n\n for (const [key, value] of Object.entries(properties || {})) {\n if (typeof value === 'number') {\n event.properties_numeric![key] = value;\n } else {\n event.properties![key] = value;\n }\n }\n\n return event;\n }\n\n private setupClickTracking(): void {\n document.addEventListener('click', (e) => {\n const target = e.target as HTMLElement;\n const tagName = target.tagName.toLowerCase();\n\n if (tagName === 'a' || tagName === 'button' || target.getAttribute('data-track-click')) {\n const properties: Record<string, string> = {\n element: tagName,\n text: target.textContent?.substring(0, 100) || '',\n class: target.className || '',\n id: target.id || '',\n };\n\n if (tagName === 'a') {\n properties.href = (target as HTMLAnchorElement).href || '';\n }\n\n this.track('click', properties);\n }\n });\n }\n\n private async sendToEndpoint(events: ProyectaEvent[]): Promise<void> {\n if (!this.config.apiEndpoint || !this.config.enabled || !this.config.apiKey) return;\n\n if (this.config.debug) {\n console.log('[Proyecta Analytics] Sending', events.length, 'events');\n }\n\n // Use fetch with keepalive for reliable delivery on page unload.\n // sendBeacon can't set custom headers (needed for Bearer auth).\n const response = await fetch(`${this.config.apiEndpoint}/v1/analytics/batch`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config.apiKey}`,\n },\n body: JSON.stringify({ events }),\n keepalive: true,\n });\n\n if (!response.ok) {\n throw new Error(`Analytics endpoint returned ${response.status}`);\n }\n }\n\n private startAutoFlush(): void {\n this.stopAutoFlush();\n this.flushTimer = setInterval(() => {\n this.flush();\n }, this.config.flushInterval);\n }\n\n private stopAutoFlush(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = undefined;\n }\n }\n\n track(eventName: string, properties?: Record<string, string | number>): void {\n if (!this.config.enabled) return;\n\n const event = this.createEvent('custom', eventName, properties);\n this.buffer.push(event);\n\n if (this.buffer.length >= this.config.bufferSize) {\n this.flush();\n }\n }\n\n private setupWebVitals(): void {\n let webVitalsEventSent = false;\n let webVitalsTimeout: ReturnType<typeof setTimeout> | undefined;\n\n observeWebVitals((metrics) => {\n this.webVitalsMetrics = { ...this.webVitalsMetrics, ...metrics };\n\n if (webVitalsTimeout) {\n clearTimeout(webVitalsTimeout);\n }\n\n const hasCriticalMetrics =\n (this.webVitalsMetrics.largest_contentful_paint !== undefined ||\n this.webVitalsMetrics.first_contentful_paint !== undefined) &&\n this.webVitalsMetrics.cumulative_layout_shift !== undefined;\n\n if (hasCriticalMetrics && !webVitalsEventSent) {\n webVitalsTimeout = setTimeout(() => {\n this.trackWebVitals();\n webVitalsEventSent = true;\n }, 500);\n }\n });\n\n setTimeout(() => {\n if (!webVitalsEventSent) {\n this.trackWebVitals();\n webVitalsEventSent = true;\n }\n }, 5000);\n }\n\n trackPageview(properties?: Record<string, string | number>): void {\n if (!this.config.enabled) return;\n\n const event = this.createEvent('pageview', '', properties);\n\n if (this.config.trackPerformance) {\n const perfMetrics = getPerformanceMetrics();\n if (perfMetrics.page_load_time !== undefined) event.plt = perfMetrics.page_load_time;\n if (perfMetrics.dom_interactive !== undefined) event.di = perfMetrics.dom_interactive;\n if (perfMetrics.time_to_first_byte !== undefined) event.ttfb = perfMetrics.time_to_first_byte;\n }\n\n this.buffer.push(event);\n\n if (this.buffer.length >= this.config.bufferSize) {\n this.flush();\n }\n }\n\n private trackWebVitals(): void {\n if (!this.config.enabled || !this.config.trackPerformance) return;\n\n const hasVitals =\n this.webVitalsMetrics.first_contentful_paint !== undefined ||\n this.webVitalsMetrics.largest_contentful_paint !== undefined ||\n this.webVitalsMetrics.cumulative_layout_shift !== undefined ||\n this.webVitalsMetrics.first_input_delay !== undefined ||\n this.webVitalsMetrics.interaction_to_next_paint !== undefined;\n\n if (!hasVitals) return;\n\n const event = this.createEvent('web_vitals', '');\n\n if (this.webVitalsMetrics.first_contentful_paint !== undefined)\n event.fcp = this.webVitalsMetrics.first_contentful_paint;\n if (this.webVitalsMetrics.largest_contentful_paint !== undefined)\n event.lcp = this.webVitalsMetrics.largest_contentful_paint;\n if (this.webVitalsMetrics.cumulative_layout_shift !== undefined)\n event.cls = this.webVitalsMetrics.cumulative_layout_shift;\n if (this.webVitalsMetrics.first_input_delay !== undefined)\n event.fid = this.webVitalsMetrics.first_input_delay;\n if (this.webVitalsMetrics.interaction_to_next_paint !== undefined)\n event.inp = this.webVitalsMetrics.interaction_to_next_paint;\n if (this.webVitalsMetrics.time_to_first_byte !== undefined)\n event.ttfb = this.webVitalsMetrics.time_to_first_byte;\n if (this.webVitalsMetrics.page_load_time !== undefined)\n event.plt = this.webVitalsMetrics.page_load_time;\n if (this.webVitalsMetrics.dom_interactive !== undefined)\n event.di = this.webVitalsMetrics.dom_interactive;\n\n this.buffer.push(event);\n\n if (this.buffer.length >= this.config.bufferSize) {\n this.flush();\n }\n }\n\n trackPageleave(): void {\n if (!this.config.enabled) return;\n\n const event = this.createEvent('pageleave', '');\n this.buffer.push(event);\n this.flush();\n }\n\n addProvider(provider: AnalyticsProvider): void {\n this.providers.push(provider);\n if (this.config.debug) {\n console.log(`[Proyecta Analytics] Provider '${provider.name}' added`);\n }\n }\n\n removeProvider(name: string): void {\n this.providers = this.providers.filter((p) => p.name !== name);\n }\n\n async flush(): Promise<void> {\n if (this.buffer.length === 0) return;\n\n const events = [...this.buffer];\n this.buffer = [];\n\n try {\n await Promise.all([\n ...this.providers.map((provider) =>\n provider.send(events).catch((err) => {\n if (this.config.debug) {\n console.error(`[Proyecta Analytics] Provider '${provider.name}' error:`, err);\n }\n }),\n ),\n this.sendToEndpoint(events).catch((err) => {\n if (this.config.debug) {\n console.error('[Proyecta Analytics] Endpoint error:', err);\n }\n }),\n ]);\n\n if (this.config.debug) {\n console.log(`[Proyecta Analytics] Flushed ${events.length} events`);\n }\n } catch (error) {\n if (this.config.debug) {\n console.error('[Proyecta Analytics] Flush error:', error);\n }\n this.buffer.unshift(...events);\n }\n }\n\n identify(userId: string): void {\n this.userId = userId;\n if (this.config.enabled) {\n const event = this.createEvent('custom', 'identify', { user_id: userId });\n this.buffer.push(event);\n if (this.buffer.length >= this.config.bufferSize) {\n this.flush();\n }\n }\n }\n\n reset(): void {\n this.buffer = [];\n this.sessionId = this.getSessionId();\n this.userId = undefined;\n this.webVitalsMetrics = {};\n }\n\n destroy(): void {\n this.stopAutoFlush();\n this.flush();\n this.providers = [];\n this.buffer = [];\n }\n}\n\nexport class ConsoleProvider implements AnalyticsProvider {\n name = 'console';\n\n async send(events: ProyectaEvent[]): Promise<void> {\n events.forEach((event) => {\n console.log('[ConsoleProvider]', event);\n });\n }\n}\n\nlet defaultInstance: Analytics | null = null;\n\nexport function initAnalytics(config: AnalyticsConfig): Analytics {\n if (!defaultInstance) {\n defaultInstance = new Analytics(config);\n\n if (typeof window !== 'undefined') {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => {\n defaultInstance?.trackPageview();\n });\n } else {\n defaultInstance?.trackPageview();\n }\n }\n }\n return defaultInstance;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
package/schema.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as z from 'zod/mini';
|
|
2
|
+
export declare const EventTypeEnum: z.ZodMiniEnum<{
|
|
3
|
+
pageview: "pageview";
|
|
4
|
+
pageleave: "pageleave";
|
|
5
|
+
web_vitals: "web_vitals";
|
|
6
|
+
click: "click";
|
|
7
|
+
custom: "custom";
|
|
8
|
+
}>;
|
|
9
|
+
export type EventType = z.infer<typeof EventTypeEnum>;
|
|
10
|
+
export declare const ProyectaEventSchema: z.ZodMiniObject<{
|
|
11
|
+
app_id: z.ZodMiniString<string>;
|
|
12
|
+
event_id: z.ZodMiniString<string>;
|
|
13
|
+
event_type: z.ZodMiniEnum<{
|
|
14
|
+
pageview: "pageview";
|
|
15
|
+
pageleave: "pageleave";
|
|
16
|
+
web_vitals: "web_vitals";
|
|
17
|
+
click: "click";
|
|
18
|
+
custom: "custom";
|
|
19
|
+
}>;
|
|
20
|
+
event_name: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
21
|
+
environment: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
22
|
+
timestamp: z.ZodMiniNumber<number>;
|
|
23
|
+
visitor_id: z.ZodMiniString<string>;
|
|
24
|
+
session_id: z.ZodMiniString<string>;
|
|
25
|
+
user_id: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
26
|
+
origin: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
27
|
+
url: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
28
|
+
url_path: z.ZodMiniString<string>;
|
|
29
|
+
url_query: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
30
|
+
url_hash: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
31
|
+
referrer: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
32
|
+
referrer_domain: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
33
|
+
referrer_source: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
34
|
+
browser: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
35
|
+
browser_version: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
36
|
+
os: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
37
|
+
os_version: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
38
|
+
country_code: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
39
|
+
region: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
40
|
+
city: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
41
|
+
utm_source: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
42
|
+
utm_medium: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
43
|
+
utm_campaign: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
44
|
+
utm_content: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
45
|
+
utm_term: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
46
|
+
properties: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>>>;
|
|
47
|
+
properties_numeric: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniNumber<number>>>>;
|
|
48
|
+
plt: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniNumber<number>>>;
|
|
49
|
+
di: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniNumber<number>>>;
|
|
50
|
+
fcp: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniNumber<number>>>;
|
|
51
|
+
lcp: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniNumber<number>>>;
|
|
52
|
+
cls: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniNumber<number>>>;
|
|
53
|
+
fid: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniNumber<number>>>;
|
|
54
|
+
ttfb: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniNumber<number>>>;
|
|
55
|
+
inp: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniNumber<number>>>;
|
|
56
|
+
}, z.core.$strip>;
|
|
57
|
+
export type ProyectaEvent = z.infer<typeof ProyectaEventSchema>;
|
|
58
|
+
//# sourceMappingURL=schema.d.ts.map
|
package/schema.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,UAAU,CAAC;AAE9B,eAAO,MAAM,aAAa;;;;;;EAAqE,CAAC;AAChG,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEtD,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwC9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
|
package/types.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ProyectaEvent } from './schema';
|
|
2
|
+
export interface BrowserInfo {
|
|
3
|
+
name: string;
|
|
4
|
+
version: string;
|
|
5
|
+
}
|
|
6
|
+
export interface OSInfo {
|
|
7
|
+
name: string;
|
|
8
|
+
version: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ReferrerInfo {
|
|
11
|
+
referrer: string;
|
|
12
|
+
referrer_domain: string;
|
|
13
|
+
referrer_source: string;
|
|
14
|
+
}
|
|
15
|
+
export interface UTMParams {
|
|
16
|
+
utm_source: string;
|
|
17
|
+
utm_medium: string;
|
|
18
|
+
utm_campaign: string;
|
|
19
|
+
utm_content: string;
|
|
20
|
+
utm_term: string;
|
|
21
|
+
}
|
|
22
|
+
export interface PerformanceMetrics {
|
|
23
|
+
page_load_time?: number;
|
|
24
|
+
dom_interactive?: number;
|
|
25
|
+
time_to_first_byte?: number;
|
|
26
|
+
first_contentful_paint?: number;
|
|
27
|
+
largest_contentful_paint?: number;
|
|
28
|
+
first_input_delay?: number;
|
|
29
|
+
cumulative_layout_shift?: number;
|
|
30
|
+
interaction_to_next_paint?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface AnalyticsConfig {
|
|
33
|
+
/** Required. The app/prototype ID — used to scope events to a specific Proyecta project. */
|
|
34
|
+
appId: string;
|
|
35
|
+
/** Proyecta Cloud API key (pk_live_*) for authenticating analytics requests. */
|
|
36
|
+
apiKey?: string;
|
|
37
|
+
/** Proyecta Cloud base URL (e.g. https://cloud.proyecta.dev). */
|
|
38
|
+
apiEndpoint?: string;
|
|
39
|
+
debug?: boolean;
|
|
40
|
+
enabled?: boolean;
|
|
41
|
+
bufferSize?: number;
|
|
42
|
+
flushInterval?: number;
|
|
43
|
+
trackClicks?: boolean;
|
|
44
|
+
trackPerformance?: boolean;
|
|
45
|
+
cookieDomain?: string;
|
|
46
|
+
cookiePath?: string;
|
|
47
|
+
sessionTimeout?: number;
|
|
48
|
+
}
|
|
49
|
+
export interface AnalyticsProvider {
|
|
50
|
+
name: string;
|
|
51
|
+
send: (events: ProyectaEvent[]) => Promise<void>;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=types.d.ts.map
|
package/types.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,yBAAyB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,MAAM,WAAW,eAAe;IAC9B,4FAA4F;IAC5F,KAAK,EAAE,MAAM,CAAC;IACd,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClD"}
|
package/utils.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { BrowserInfo, OSInfo, ReferrerInfo, UTMParams, PerformanceMetrics } from './types';
|
|
2
|
+
export declare function generateId(): string;
|
|
3
|
+
export declare function getCookie(name: string): string | null;
|
|
4
|
+
export declare function setCookie(name: string, value: string, days: number, domain?: string, path?: string): void;
|
|
5
|
+
export declare function parseUserAgent(ua?: string): {
|
|
6
|
+
browser: BrowserInfo;
|
|
7
|
+
os: OSInfo;
|
|
8
|
+
deviceType: string;
|
|
9
|
+
deviceVendor?: string;
|
|
10
|
+
deviceModel?: string;
|
|
11
|
+
isBot?: boolean;
|
|
12
|
+
};
|
|
13
|
+
export declare function getReferrerInfo(): ReferrerInfo;
|
|
14
|
+
export declare function getUTMParams(url?: string): UTMParams;
|
|
15
|
+
export declare function getPerformanceMetrics(): PerformanceMetrics;
|
|
16
|
+
export declare function observeWebVitals(callback: (metrics: PerformanceMetrics) => void): void;
|
|
17
|
+
export declare function debounce<T extends (...args: Parameters<T>) => ReturnType<T>>(func: T, wait: number): (...args: Parameters<T>) => void;
|
|
18
|
+
export declare function throttle<T extends (...args: Parameters<T>) => ReturnType<T>>(func: T, limit: number): (...args: Parameters<T>) => void;
|
|
19
|
+
export declare function safeStringify(obj: unknown): string;
|
|
20
|
+
export declare function getBrowserInfo(): Record<string, unknown>;
|
|
21
|
+
//# sourceMappingURL=utils.d.ts.map
|
package/utils.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAEhG,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWrD;AAED,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,SAAM,GACT,IAAI,CAQN;AAED,wBAAgB,cAAc,CAAC,EAAE,SAAsB,GAAG;IACxD,OAAO,EAAE,WAAW,CAAC;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CA2DA;AAED,wBAAgB,eAAe,IAAI,YAAY,CAmD9C;AAED,wBAAgB,YAAY,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CASpD;AAED,wBAAgB,qBAAqB,IAAI,kBAAkB,CAiE1D;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,GAAG,IAAI,CAyFtF;AAED,wBAAgB,QAAQ,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,EAC1E,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,MAAM,GACX,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,CAMlC;AAED,wBAAgB,QAAQ,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,EAC1E,IAAI,EAAE,CAAC,EACP,KAAK,EAAE,MAAM,GACZ,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,CASlC;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAWlD;AAED,wBAAgB,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAwCxD"}
|