@levo-so/insights 0.1.64 → 0.1.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,71 @@
1
+ import { ILevoClient } from '@levo-so/core';
2
+ import { AnalyticsInstance } from 'analytics';
3
+ import { AnalyticsEventType, ILevoAudience } from '../types/analytics';
4
+ import { IInsightOptions } from '../types/options';
5
+ /**
6
+ * Creates the Levo Audience analytics module.
7
+ *
8
+ * This factory function creates a new audience module instance tied to
9
+ * a specific Levo control instance and HTTP client. The module manages
10
+ * all analytics tracking for the page.
11
+ *
12
+ * @param core - The Levo control instance containing workspace config
13
+ * @param httpClient - HTTP client for making API requests
14
+ * @returns The audience module with tracking methods
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const audience = createLevoAudienceModule(levoControl, httpClient);
19
+ *
20
+ * // Initialize on page load
21
+ * await audience.initiate({ id: 'page-123' });
22
+ *
23
+ * // Track custom events
24
+ * audience.track('button.click', { buttonId: 'cta-signup' });
25
+ *
26
+ * // Cleanup on unmount (optional, for SPAs)
27
+ * audience.destroy();
28
+ * ```
29
+ */
30
+ export declare const createLevoInsights: (client: ILevoClient, options: IInsightOptions) => {
31
+ /** Get the underlying analytics instance (read-only) */
32
+ readonly instance: AnalyticsInstance | null;
33
+ /** Check if session has been established (read-only) */
34
+ readonly is_identified: boolean;
35
+ /** Storage keys used by the module */
36
+ storage_keys: {
37
+ readonly identify: "lock:lv_aud_identify";
38
+ readonly open_tabs: "lv_insights_ot";
39
+ readonly current_tab: "lv_insights_ct";
40
+ readonly all_tabs: "lv_insights_at";
41
+ readonly device_token: "lv_aud_device";
42
+ readonly session_token: "lv_aud_session";
43
+ readonly mode: "lv_aud_mode";
44
+ readonly session: "lv_insights_session";
45
+ };
46
+ /** Initialize the module for a page */
47
+ initiate: (properties: ILevoAudience.Properties) => Promise<any>;
48
+ /** Identify/establish session */
49
+ identify: (options?: {
50
+ withLock?: boolean;
51
+ }) => Promise<void>;
52
+ /** Track a custom event */
53
+ track: <T extends ILevoAudience.Properties>(event: AnalyticsEventType, properties: T & ILevoAudience.Properties) => Promise<any>;
54
+ /** Track a bounce event */
55
+ bounce: (properties: ILevoAudience.Properties) => Promise<void>;
56
+ /** Cleanup the module */
57
+ destroy: () => void;
58
+ };
59
+ /**
60
+ * Type representing the audience module instance.
61
+ * Use this when you need to type a variable holding the module.
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * let audience: ILevoAudienceModule;
66
+ * audience = createLevoAudienceModule(core, httpClient);
67
+ * ```
68
+ */
69
+ export type ILevoInsights = ReturnType<typeof createLevoInsights>;
70
+ export * from '../types/analytics';
71
+ export * from '../types/options';
@@ -0,0 +1,10 @@
1
+ import { ILevoClient } from '@levo-so/core';
2
+ import { TrackFn } from '../types/analytics';
3
+ /**
4
+ * Creates the analytics instance with our custom plugin.
5
+ *
6
+ * @param client - The Levo client instance
7
+ * @param sendEvent - Function to handle event tracking
8
+ * @param isValid - Function to check if the current instance is still valid/active
9
+ */
10
+ export declare const createAnalytics: (client: ILevoClient, sendEvent: TrackFn, isValid: () => boolean) => import('analytics').AnalyticsInstance | null;
@@ -0,0 +1,24 @@
1
+ import { ILevoClient } from '@levo-so/core';
2
+ import { TrackFn } from '../types/analytics';
3
+ import { IInsightOptions } from '../types/options';
4
+ /**
5
+ * Creates the Levo Audience analytics module.
6
+ *
7
+ * This factory function creates a new audience module instance tied to
8
+ * a specific Levo control instance and HTTP client. The module manages
9
+ * all analytics tracking for the page.
10
+ *
11
+ * @param client - The Levo control instance containing workspace config
12
+ * @param options - Configuration options for the insights module
13
+ * @returns The audience module with tracking methods
14
+ */
15
+ declare const createLevoInsights: (client: ILevoClient, options: IInsightOptions) => {
16
+ init: (initOptions?: {
17
+ pageId?: string | null;
18
+ }) => void;
19
+ destroy: () => void;
20
+ track: TrackFn;
21
+ readonly instance: import('analytics').AnalyticsInstance | null;
22
+ };
23
+ type ILevoInsights = ReturnType<typeof createLevoInsights>;
24
+ export { createLevoInsights, type ILevoInsights };
@@ -0,0 +1,11 @@
1
+ interface IInsightsConfig {
2
+ workspace: string | null;
3
+ page_id: string | null;
4
+ site_id: string | null;
5
+ debug: boolean;
6
+ }
7
+ declare const $config: import('nanostores').PreinitializedMapStore<IInsightsConfig> & object;
8
+ declare const getConfig: () => IInsightsConfig;
9
+ declare const setConfig: (v: Partial<IInsightsConfig>) => void;
10
+ declare const updateConfigKey: <T extends keyof IInsightsConfig>(key: T, value: IInsightsConfig[T]) => void;
11
+ export { $config, getConfig, setConfig, updateConfigKey, type IInsightsConfig };
@@ -0,0 +1,45 @@
1
+ import { ILevoAudience } from '../types/analytics';
2
+ type IQueuedEvent = Partial<ILevoAudience.CollectInput> & {
3
+ event: string;
4
+ created_at: string;
5
+ };
6
+ declare const $eventQueue: import('nanostores').PreinitializedWritableAtom<IQueuedEvent[]> & object;
7
+ declare const $lifetimePageCount: import('nanostores').PreinitializedWritableAtom<number> & object;
8
+ declare const $totalEventCount: import('nanostores').ReadableAtom<number>;
9
+ /**
10
+ * Initialize a listener that flushes the event queue immediately
11
+ * when the session becomes identified.
12
+ *
13
+ * @returns Unsubscribe function
14
+ */
15
+ declare const initSessionFlushListener: () => () => void;
16
+ declare const getEventQueue: () => IQueuedEvent[];
17
+ /**
18
+ * Flush all queued events to the server.
19
+ *
20
+ * This function:
21
+ * 1. Takes all events from the queue
22
+ * 2. Merges context from stores (workspace, session, tab info)
23
+ * 3. Sends to /v1/insights/event/bulk via sendBeacon (preferred) or fetch
24
+ *
25
+ * sendBeacon is preferred because:
26
+ * - It survives page unload (critical for page exit events)
27
+ * - It doesn't block the main thread
28
+ * - Browser queues it and sends when convenient
29
+ *
30
+ * fetch with keepalive is the fallback for when sendBeacon fails or
31
+ * isn't available.
32
+ */
33
+ declare const flushEventQueue: (_eventQueue: IQueuedEvent[]) => void;
34
+ /**
35
+ * Add an event to the queue for batched sending.
36
+ * Only stores in-time data (event, properties, page context).
37
+ * Context from stores is merged at flush time.
38
+ */
39
+ declare const queueEvent: (event: IQueuedEvent) => void;
40
+ declare const resetRateLimit: () => void;
41
+ /**
42
+ * Clear all pending events and reset state.
43
+ */
44
+ export declare const clearEventQueue: () => void;
45
+ export { $eventQueue, $lifetimePageCount, $totalEventCount, queueEvent, resetRateLimit, initSessionFlushListener, getEventQueue, flushEventQueue, type IQueuedEvent, };
@@ -0,0 +1,21 @@
1
+ import { IIdentity } from '../types/identity';
2
+ interface ISession {
3
+ isIdentified: boolean;
4
+ isIdentifying: boolean;
5
+ sessionId: string | null;
6
+ deviceId: string | null;
7
+ deviceToken: string | null;
8
+ sessionToken: string | null;
9
+ allTabs: string[];
10
+ mode: "proxy" | "direct";
11
+ insightsBaseUrl: string | null;
12
+ identity: IIdentity | null;
13
+ }
14
+ declare const $session: import('nanostores').PreinitializedWritableAtom<ISession> & object;
15
+ declare const onSessionReady: (cb: () => void) => void;
16
+ declare const isSessionReady: () => boolean;
17
+ declare const getSession: () => ISession;
18
+ declare const updateSession: (v: Partial<ISession>) => void;
19
+ export { $session, getSession, updateSession, onSessionReady, isSessionReady, type ISession };
20
+ declare const $isContextReady: import('nanostores').ReadableAtom<boolean>;
21
+ export { $isContextReady };
@@ -0,0 +1,8 @@
1
+ export { $config } from './stores/config';
2
+ export { $eventQueue, $lifetimePageCount, $totalEventCount } from './stores/events';
3
+ export { $isContextReady, $session } from './stores/session';
4
+ export { $activity } from './tracking/activity';
5
+ export { $pageLifecycle } from './tracking/bounce';
6
+ export { $currentTab } from './tracking/currentTab';
7
+ export { $scrollBehavior } from './tracking/scrollBehavior';
8
+ export { $selectedText } from './tracking/textSelection';
package/dist/stores.js ADDED
@@ -0,0 +1,14 @@
1
+ import { f as s, $ as t, h as $, a as i, d as o, b as c, g as n, i as l, j as r, e as f, c as u } from "./S_TwsxGkG8z9.js";
2
+ export {
3
+ s as $activity,
4
+ t as $config,
5
+ $ as $currentTab,
6
+ i as $eventQueue,
7
+ o as $isContextReady,
8
+ c as $lifetimePageCount,
9
+ n as $pageLifecycle,
10
+ l as $scrollBehavior,
11
+ r as $selectedText,
12
+ f as $session,
13
+ u as $totalEventCount
14
+ };
@@ -0,0 +1,21 @@
1
+ import { AnalyticsInstance } from 'analytics';
2
+ /**
3
+ * Activity state interface.
4
+ */
5
+ interface IActivityState {
6
+ status: "active" | "idle";
7
+ seconds: number;
8
+ }
9
+ /**
10
+ * Reactive store for user activity.
11
+ */
12
+ declare const $activity: import('nanostores').PreinitializedMapStore<IActivityState> & object;
13
+ /**
14
+ * Initialize activity tracking.
15
+ *
16
+ * @param instance - Analytics instance
17
+ * @returns Cleanup function
18
+ */
19
+ declare const initActivityTracker: (instance: AnalyticsInstance) => () => void;
20
+ declare const getActivityStatus: () => IActivityState;
21
+ export { $activity, initActivityTracker, getActivityStatus };
@@ -0,0 +1,24 @@
1
+ import { AnalyticsInstance } from 'analytics';
2
+ /**
3
+ * Page lifecycle states.
4
+ */
5
+ declare enum LifecycleState {
6
+ ACTIVE = "active",
7
+ PASSIVE = "passive",
8
+ HIDDEN = "hidden",
9
+ FROZEN = "frozen",
10
+ TERMINATED = "terminated"
11
+ }
12
+ /**
13
+ * Reactive store for the current page lifecycle state.
14
+ */
15
+ declare const $pageLifecycle: import('nanostores').PreinitializedWritableAtom<LifecycleState> & object;
16
+ /**
17
+ * Initialize bounce tracking.
18
+ *
19
+ * @param instance - Analytics instance
20
+ * @param options - Configuration options (e.g., page ID)
21
+ * @returns Cleanup function
22
+ */
23
+ declare const initBounceTracker: (instance: AnalyticsInstance) => () => void;
24
+ export { $pageLifecycle, initBounceTracker, LifecycleState };
@@ -0,0 +1,9 @@
1
+ import { AnalyticsInstance } from 'analytics';
2
+ /**
3
+ * Create a clicks tracker instance.
4
+ *
5
+ * @param instance - Analytics instance for tracking events
6
+ * @returns Cleanup function
7
+ */
8
+ declare const initClicksTracker: (instance: AnalyticsInstance) => () => void;
9
+ export { initClicksTracker };
@@ -0,0 +1,8 @@
1
+ import { AnalyticsInstance } from 'analytics';
2
+ /**
3
+ * Initialize clipboard tracking.
4
+ *
5
+ * @param instance - Analytics instance
6
+ * @returns Cleanup function
7
+ */
8
+ export declare const initClipboardTracker: (instance: AnalyticsInstance) => () => void;
@@ -0,0 +1,16 @@
1
+ import { AnalyticsInstance } from 'analytics';
2
+ /**
3
+ * Reactive store for the local unique tab ID.
4
+ * This ID is NOT shared across tabs (unlike the session store).
5
+ */
6
+ declare const $currentTab: import('nanostores').PreinitializedWritableAtom<string | null> & object;
7
+ /**
8
+ * Initialize current tab tracking.
9
+ * Assumes the session store is already synchronized (isReady).
10
+ *
11
+ * @param _instance - Analytics instance
12
+ * @returns Cleanup function
13
+ */
14
+ declare const initCurrentTabTracker: (_instance: AnalyticsInstance) => () => void;
15
+ declare const getCurrentTab: () => string | null;
16
+ export { $currentTab, initCurrentTabTracker, getCurrentTab };
@@ -0,0 +1,8 @@
1
+ import { AnalyticsInstance } from 'analytics';
2
+ /**
3
+ * Initialize form tracking.
4
+ *
5
+ * @param instance - Analytics instance
6
+ * @returns Cleanup function
7
+ */
8
+ export declare const initFormsTracker: (instance: AnalyticsInstance) => () => void;
@@ -0,0 +1,8 @@
1
+ import { AnalyticsInstance } from 'analytics';
2
+ /**
3
+ * Initialize page tracking.
4
+ *
5
+ * @param instance - Analytics instance
6
+ * @returns Cleanup function
7
+ */
8
+ export declare const initPageTracker: (instance: AnalyticsInstance) => () => void;
@@ -0,0 +1,21 @@
1
+ import { AnalyticsInstance } from 'analytics';
2
+ /**
3
+ * Scroll state interface.
4
+ */
5
+ interface IScrollState {
6
+ depth: number;
7
+ speedCategory: "read" | "scan";
8
+ direction: "down" | "up";
9
+ }
10
+ /**
11
+ * Reactive store for scroll behavior.
12
+ */
13
+ declare const $scrollBehavior: import('nanostores').PreinitializedMapStore<IScrollState> & object;
14
+ /**
15
+ * Initialize scroll behavior tracking.
16
+ *
17
+ * @param instance - Analytics instance
18
+ * @returns Cleanup function
19
+ */
20
+ declare const initScrollBehaviorTracker: (instance: AnalyticsInstance) => () => void;
21
+ export { $scrollBehavior, initScrollBehaviorTracker };
@@ -0,0 +1,13 @@
1
+ import { AnalyticsInstance } from 'analytics';
2
+ /**
3
+ * Reactive store for the currently selected text.
4
+ */
5
+ declare const $selectedText: import('nanostores').PreinitializedWritableAtom<string | null> & object;
6
+ /**
7
+ * Initialize text selection tracking.
8
+ *
9
+ * @param instance - Analytics instance
10
+ * @returns Cleanup function
11
+ */
12
+ declare const initTextSelectionTracker: (instance: AnalyticsInstance) => () => void;
13
+ export { $selectedText, initTextSelectionTracker };
@@ -0,0 +1,4 @@
1
+ import { ILevoAudience } from '@levo-so/core';
2
+ export type { AnalyticsEventType, ILevoAudience } from '@levo-so/core';
3
+ export { AnalyticsEvents } from '@levo-so/core';
4
+ export type TrackFn = <T extends ILevoAudience.Properties>(event: ILevoAudience.Properties["event"] & string, properties: T & ILevoAudience.Properties) => Promise<void>;
@@ -0,0 +1,12 @@
1
+ import { ILevoAudience } from '@levo-so/core';
2
+ export type IIdentityReferrer = ILevoAudience.Traits["referrer"] & {
3
+ raw: string;
4
+ };
5
+ export interface IIdentity {
6
+ locale: ILevoAudience.Traits["locale"] | null;
7
+ timezone: ILevoAudience.Traits["timezone"] | null;
8
+ darkMode: ILevoAudience.Traits["dark_mode"] | null;
9
+ privateMode: ILevoAudience.Traits["private_mode"] | null;
10
+ referrer: IIdentityReferrer | null;
11
+ properties: ILevoAudience.Traits["properties"] | null;
12
+ }
@@ -0,0 +1,14 @@
1
+ export interface IInsightOptions {
2
+ insightsUrl: string;
3
+ /** Site ID for analytics tracking */
4
+ site?: string | null;
5
+ /** Page ID for analytics tracking */
6
+ pageId?: string | null;
7
+ /** Direct insights API URL for third-party mode (when reverse proxy unavailable) */
8
+ directInsightsUrl?: string;
9
+ /** Insights mode: 'auto' (default) detects via ping, 'proxy' forces reverse proxy, 'direct' forces direct API */
10
+ mode?: "auto" | "proxy" | "direct";
11
+ /** Timeout for proxy ping detection in ms (default: 3000) */
12
+ pingTimeout?: number;
13
+ debug?: boolean;
14
+ }
@@ -0,0 +1,22 @@
1
+ export declare const isTextNode: (el: Element | undefined | null) => el is HTMLElement;
2
+ export declare const makeSafeText: (s: string | null | undefined, length?: number) => string;
3
+ export declare const getSafeText: (el: Element) => string;
4
+ export declare const getEventTarget: (e: Event) => Element | null;
5
+ export declare const shouldCaptureDomEvent: (el: Element, event: Event) => boolean;
6
+ export declare const shouldCaptureValue: (value: string, anchorRegexes?: boolean) => boolean;
7
+ export declare const isAngularStyleAttr: (attributeName: string) => boolean;
8
+ export declare const getDirectAndNestedSpanText: (target: Element) => string;
9
+ export type Property = any;
10
+ export type Properties = Record<string, Property>;
11
+ /** Text source indicates where the text was found */
12
+ export type TextSource = "text" | "aria-label" | "title" | "data-track-label" | "alt" | "placeholder" | "innerText" | "none";
13
+ /**
14
+ * Get element text with comprehensive fallback chain.
15
+ * Priority: direct text → aria-label → title → data-track-label → alt (for images) → placeholder → innerText
16
+ */
17
+ export declare const getElementTextWithFallback: (el: Element) => {
18
+ text: string;
19
+ source: TextSource;
20
+ };
21
+ export declare const getAttributes: (element: Element) => Properties;
22
+ export declare const getPropertiesFromEvent: (e: Event, eventName: string) => Properties | null;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Check if localStorage is available (SSR-safe).
3
+ */
4
+ export declare const checkLocalStorageAvailable: () => boolean;
@@ -0,0 +1,66 @@
1
+ import { STORAGE_KEYS } from '../constants/storageKeys';
2
+ import { AnalyticsEventType, ILevoAudience } from '../types/analytics';
3
+ export interface PluginContext {
4
+ workspace: string;
5
+ insightsUrl: string;
6
+ site?: string | null;
7
+ session: {
8
+ get: () => string;
9
+ set: (v: string) => void;
10
+ };
11
+ device: {
12
+ get: () => string;
13
+ set: (v: string) => void;
14
+ };
15
+ isIdentified: {
16
+ get: () => boolean;
17
+ set: (v: boolean) => void;
18
+ };
19
+ pendingIdentify: {
20
+ get: () => boolean;
21
+ set: (v: boolean) => void;
22
+ };
23
+ insightsMode: {
24
+ get: () => "proxy" | "direct";
25
+ };
26
+ getCurrentTabId: () => string;
27
+ getTabCount: () => number;
28
+ queueEvent: (event: ILevoAudience.CollectInput) => void;
29
+ broadcastSession: () => void;
30
+ request: <R, T>(url: string, data: T) => Promise<R | null>;
31
+ setInStorage: (key: string, value: string) => void;
32
+ resetEventCount: () => void;
33
+ storage_keys: typeof STORAGE_KEYS;
34
+ properties: ILevoAudience.Properties;
35
+ track: (event: AnalyticsEventType, props: any) => void;
36
+ debugEvents?: {
37
+ input: Partial<ILevoAudience.CollectInput>;
38
+ payload: ILevoAudience.CollectInput;
39
+ queued: boolean;
40
+ }[];
41
+ }
42
+ /**
43
+ * Factory function to create the analytics plugin.
44
+ * Extracted from initiate() to improve maintainability.
45
+ */
46
+ export declare const createPlugin: (context: PluginContext) => {
47
+ name: string;
48
+ /**
49
+ * Handle track events (custom events like clicks, scrolls, etc.)
50
+ */
51
+ track: ({ payload }: {
52
+ payload: ILevoAudience.Properties;
53
+ }) => void;
54
+ /**
55
+ * Handle page view events.
56
+ */
57
+ page: ({ payload }: {
58
+ payload: ILevoAudience.EventProperties;
59
+ }) => void;
60
+ /**
61
+ * Handle identify events (session establishment).
62
+ */
63
+ identify: ({ payload }: {
64
+ payload: ILevoAudience.EventProperties;
65
+ }) => void;
66
+ };
@@ -0,0 +1,10 @@
1
+ import { IInsightOptions } from '../types/options';
2
+ type DetectionResult = {
3
+ mode: "proxy" | "direct";
4
+ url: string | null;
5
+ };
6
+ /**
7
+ * Detect the insights mode (proxy vs direct).
8
+ */
9
+ export declare const detectMode: (options: IInsightOptions) => Promise<DetectionResult>;
10
+ export {};
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Get authentication headers for direct mode.
3
+ * Returns empty object in proxy mode (cookies handle auth).
4
+ */
5
+ export declare const getAuthHeaders: ({ deviceToken, sessionToken, mode, }: {
6
+ deviceToken?: string | null;
7
+ sessionToken?: string | null;
8
+ mode?: string | null;
9
+ }) => Record<string, string>;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Extract referrer information for the current page.
3
+ *
4
+ * Uses @analytics/visitor-source to parse:
5
+ * - Referrer type (direct, organic, paid, social, etc.)
6
+ * - UTM parameters (source, medium, campaign, content, term)
7
+ * - Raw referrer URL
8
+ *
9
+ * Handles array-to-string conversion for UTM params that may come
10
+ * as arrays from the parser.
11
+ *
12
+ * @returns Referrer information object
13
+ */
14
+ export declare const getReferrer: () => {
15
+ type?: undefined;
16
+ referrer?: undefined;
17
+ data?: undefined;
18
+ raw?: undefined;
19
+ } | {
20
+ type: string;
21
+ referrer: Record<string, string>;
22
+ data: Record<string, string>;
23
+ raw: string;
24
+ };
@@ -0,0 +1,7 @@
1
+ export declare const getUserProperties: () => Promise<{
2
+ locale: string;
3
+ timezone: string;
4
+ dark_mode: boolean;
5
+ private_mode: boolean;
6
+ properties: Record<string, any>;
7
+ }>;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @link {https://www.npmjs.com/package/detectincognitojs}
3
+ */
4
+ declare const isIncognito: () => Promise<{
5
+ isPrivate: boolean;
6
+ browserName: string;
7
+ }>;
8
+ export default isIncognito;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @example
3
+ *
4
+ * const lock = new LocalStorageLock("myResource");
5
+ * if (lock.acquireLock()) {
6
+ * console.log("Lock acquired");
7
+ * // Do some work...
8
+ * lock.releaseLock();
9
+ * } else {
10
+ * console.log("Lock is already held by another instance");
11
+ * }
12
+ *
13
+ */
14
+ export declare class LocalStorageLock {
15
+ private key;
16
+ constructor(lockKey: string);
17
+ acquireLock(expiryMs?: number): boolean;
18
+ releaseLock(): void;
19
+ isLocked(expiryMs?: number): boolean;
20
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Ping an endpoint to check if it's reachable.
3
+ */
4
+ export declare const pingEndpoint: (url: string, timeout: number) => Promise<boolean>;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Rage click detection: If mouse has not moved further than RAGE_CLICK_THRESHOLD_PX
3
+ * over RAGE_CLICK_CLICK_COUNT+ clicks with max RAGE_CLICK_TIMEOUT_MS between clicks,
4
+ * it's counted as a rage click.
5
+ */
6
+ /**
7
+ * Check if the current click is part of a rage click sequence.
8
+ * Returns true for 3+ consecutive clicks within threshold.
9
+ *
10
+ * @param x - Click X coordinate
11
+ * @param y - Click Y coordinate
12
+ * @param timestamp - Click timestamp
13
+ * @returns True if this click completes a rage click sequence
14
+ */
15
+ declare const isRageClick: (x: number, y: number, timestamp: number) => boolean;
16
+ /**
17
+ * Reset the click sequence (useful for testing)
18
+ */
19
+ declare const reset: () => void;
20
+ export { isRageClick, reset };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@levo-so/insights",
3
- "version": "0.1.64",
3
+ "version": "0.1.71",
4
4
  "author": "Levo Engineering <devs@theinternetfolks.com>",
5
5
  "description": "Levo analytics",
6
6
  "type": "module",
@@ -17,15 +17,18 @@
17
17
  "@analytics/visitor-source": "0.0.7",
18
18
  "analytics": "0.8.14",
19
19
  "get-user-locale": "2.3.2",
20
- "lodash-es": "4.17.21"
20
+ "lodash-es": "4.17.21",
21
+ "nanostores": "0.11.4"
21
22
  },
22
23
  "peerDependencies": {
23
- "@levo-so/core": "0.1.64"
24
+ "@levo-so/core": "0.1.71"
24
25
  },
25
26
  "devDependencies": {
27
+ "@playwright/test": "1.58.2",
26
28
  "@types/lodash-es": "4.17.12",
27
- "tsup": "8.2.4",
28
29
  "typescript": "5.9.3",
30
+ "vite": "7.2.4",
31
+ "vite-plugin-dts": "4.1.0",
29
32
  "@levo/ts-config": "0.0.0"
30
33
  },
31
34
  "main": "./dist/index.js",
@@ -35,14 +38,17 @@
35
38
  ".": {
36
39
  "types": "./dist/index.d.ts",
37
40
  "default": "./dist/index.js"
41
+ },
42
+ "./stores": {
43
+ "types": "./dist/stores.d.ts",
44
+ "default": "./dist/stores.js"
38
45
  }
39
46
  },
40
47
  "scripts": {
41
48
  "clean": "rimraf dist node_modules .turbo",
42
49
  "check-types": "tsc --noEmit",
43
- "build": "tsup",
44
- "dev": "tsup --watch",
45
- "demo:serve": "python3 -m http.server 8080",
46
- "demo": "pnpm build && pnpm demo:serve"
50
+ "build": "vite build",
51
+ "dev": "vite build --watch",
52
+ "test:e2e": "playwright test"
47
53
  }
48
54
  }