@kitbase/events 0.1.1 → 0.1.3

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/README.md ADDED
@@ -0,0 +1,332 @@
1
+ # @kitbase/events
2
+
3
+ Official Kitbase SDK for event tracking and web analytics in TypeScript and JavaScript applications.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @kitbase/events
9
+ # or
10
+ pnpm add @kitbase/events
11
+ # or
12
+ yarn add @kitbase/events
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ```typescript
18
+ import { Kitbase } from '@kitbase/events';
19
+
20
+ const kitbase = new Kitbase({
21
+ token: 'your-api-key',
22
+ });
23
+
24
+ // Track a custom event
25
+ await kitbase.track({
26
+ channel: 'payments',
27
+ event: 'Purchase Completed',
28
+ tags: {
29
+ product_id: 'prod_123',
30
+ amount: 4999,
31
+ },
32
+ });
33
+ ```
34
+
35
+ ## Features
36
+
37
+ - **Event Tracking** - Track custom events with metadata
38
+ - **Web Analytics** - Automatic session tracking, pageviews, and bounce rate
39
+ - **User Identification** - Link anonymous users to authenticated users
40
+ - **Offline Support** - Queue events locally when offline, sync when back online
41
+ - **Super Properties** - Set properties that are included with every event
42
+ - **Duration Tracking** - Measure how long actions take
43
+
44
+ ## Configuration
45
+
46
+ ```typescript
47
+ const kitbase = new Kitbase({
48
+ // Required: Your API key
49
+ token: 'your-api-key',
50
+
51
+ // Optional: Custom API endpoint
52
+ baseUrl: 'https://api.kitbase.dev',
53
+
54
+ // Optional: Enable debug logging
55
+ debug: true,
56
+
57
+ // Optional: Offline queue configuration
58
+ offline: {
59
+ enabled: true,
60
+ maxSize: 1000, // Max events to queue
61
+ flushInterval: 30000, // Flush every 30 seconds
62
+ },
63
+
64
+ // Optional: Analytics configuration
65
+ analytics: {
66
+ autoTrackSessions: true, // Track sessions automatically (default: true)
67
+ autoTrackPageViews: false, // Track pageviews on route changes
68
+ sessionTimeout: 1800000, // 30 minutes in ms
69
+ },
70
+ });
71
+ ```
72
+
73
+ ## Event Tracking
74
+
75
+ ### Basic Event
76
+
77
+ ```typescript
78
+ await kitbase.track({
79
+ channel: 'engagement',
80
+ event: 'Button Clicked',
81
+ });
82
+ ```
83
+
84
+ ### Event with Metadata
85
+
86
+ ```typescript
87
+ await kitbase.track({
88
+ channel: 'payments',
89
+ event: 'Subscription Started',
90
+ user_id: 'user_123',
91
+ icon: '💰',
92
+ notify: true,
93
+ description: 'New premium subscription',
94
+ tags: {
95
+ plan: 'premium',
96
+ interval: 'monthly',
97
+ amount: 1999,
98
+ },
99
+ });
100
+ ```
101
+
102
+ ### Duration Tracking
103
+
104
+ Measure how long an action takes:
105
+
106
+ ```typescript
107
+ // Start timing
108
+ kitbase.timeEvent('Video Watched');
109
+
110
+ // ... user watches video ...
111
+
112
+ // Track with automatic duration
113
+ await kitbase.track({
114
+ channel: 'engagement',
115
+ event: 'Video Watched',
116
+ tags: { video_id: '123' },
117
+ });
118
+ // Event includes $duration property in seconds
119
+ ```
120
+
121
+ ## Web Analytics
122
+
123
+ ### Automatic Session Tracking
124
+
125
+ Sessions are tracked automatically by default. A new session starts when:
126
+ - User visits for the first time
127
+ - User returns after 30 minutes of inactivity
128
+
129
+ ### Track Page Views
130
+
131
+ ```typescript
132
+ // Manual pageview tracking
133
+ await kitbase.trackPageView();
134
+
135
+ // With custom path/title
136
+ await kitbase.trackPageView({
137
+ path: '/products/123',
138
+ title: 'Product Details',
139
+ });
140
+ ```
141
+
142
+ ### Auto-track Page Views (SPAs)
143
+
144
+ ```typescript
145
+ // Enable automatic tracking on route changes
146
+ kitbase.enableAutoPageViews();
147
+ ```
148
+
149
+ ### Track Revenue
150
+
151
+ ```typescript
152
+ await kitbase.trackRevenue({
153
+ amount: 4999, // Amount in cents ($49.99)
154
+ currency: 'USD',
155
+ tags: {
156
+ product_id: 'prod_123',
157
+ coupon: 'SAVE20',
158
+ },
159
+ });
160
+ ```
161
+
162
+ ## User Identification
163
+
164
+ ### Identify a User
165
+
166
+ Link anonymous activity to a known user:
167
+
168
+ ```typescript
169
+ kitbase.identify({
170
+ userId: 'user_123',
171
+ traits: {
172
+ email: 'user@example.com',
173
+ plan: 'premium',
174
+ company: 'Acme Inc',
175
+ },
176
+ });
177
+ ```
178
+
179
+ ### Reset on Logout
180
+
181
+ Clear user identity and start fresh:
182
+
183
+ ```typescript
184
+ kitbase.reset();
185
+ ```
186
+
187
+ ## Super Properties
188
+
189
+ Properties included with every event:
190
+
191
+ ```typescript
192
+ // Set super properties
193
+ kitbase.register({
194
+ app_version: '2.1.0',
195
+ platform: 'web',
196
+ });
197
+
198
+ // Set only if not already set
199
+ kitbase.registerOnce({
200
+ first_visit: new Date().toISOString(),
201
+ });
202
+
203
+ // Remove a super property
204
+ kitbase.unregister('platform');
205
+
206
+ // Clear all
207
+ kitbase.clearSuperProperties();
208
+ ```
209
+
210
+ ## Anonymous ID
211
+
212
+ Every user gets a persistent anonymous ID:
213
+
214
+ ```typescript
215
+ const anonymousId = kitbase.getAnonymousId();
216
+ ```
217
+
218
+ ## Session Management
219
+
220
+ ```typescript
221
+ // Get current session ID
222
+ const sessionId = kitbase.getSessionId();
223
+
224
+ // Get full session data
225
+ const session = kitbase.getSession();
226
+ // { id, startedAt, lastActivityAt, screenViewCount, entryPath, entryReferrer }
227
+ ```
228
+
229
+ ## Offline Support
230
+
231
+ Events are queued locally when offline and synced when back online:
232
+
233
+ ```typescript
234
+ const kitbase = new Kitbase({
235
+ token: 'your-api-key',
236
+ offline: {
237
+ enabled: true,
238
+ maxSize: 1000, // Maximum events to store
239
+ flushInterval: 30000, // Sync interval in ms
240
+ },
241
+ });
242
+
243
+ // Check queue status
244
+ const stats = await kitbase.getQueueStats();
245
+ // { size: 5, isFlushing: false }
246
+
247
+ // Manual flush
248
+ await kitbase.flushQueue();
249
+
250
+ // Clear queue
251
+ await kitbase.clearQueue();
252
+ ```
253
+
254
+ ## Debug Mode
255
+
256
+ ```typescript
257
+ // Enable at initialization
258
+ const kitbase = new Kitbase({
259
+ token: 'your-api-key',
260
+ debug: true,
261
+ });
262
+
263
+ // Or toggle at runtime
264
+ kitbase.setDebugMode(true);
265
+ kitbase.setDebugMode(false);
266
+
267
+ // Check status
268
+ const isDebug = kitbase.isDebugMode();
269
+ ```
270
+
271
+ ## Cleanup
272
+
273
+ Properly shutdown when done:
274
+
275
+ ```typescript
276
+ await kitbase.shutdown();
277
+ ```
278
+
279
+ ## API Reference
280
+
281
+ ### Kitbase Class
282
+
283
+ | Method | Description |
284
+ |--------|-------------|
285
+ | `track(options)` | Track a custom event |
286
+ | `trackPageView(options?)` | Track a page view |
287
+ | `trackRevenue(options)` | Track a revenue event |
288
+ | `identify(options)` | Identify the current user |
289
+ | `reset()` | Reset user identity and session |
290
+ | `register(props)` | Set super properties |
291
+ | `registerOnce(props)` | Set super properties if not already set |
292
+ | `unregister(key)` | Remove a super property |
293
+ | `clearSuperProperties()` | Clear all super properties |
294
+ | `timeEvent(name)` | Start timing an event |
295
+ | `cancelTimeEvent(name)` | Cancel timing an event |
296
+ | `getAnonymousId()` | Get the anonymous ID |
297
+ | `getSessionId()` | Get current session ID |
298
+ | `getSession()` | Get current session data |
299
+ | `getUserId()` | Get identified user ID |
300
+ | `enableAutoPageViews()` | Enable automatic pageview tracking |
301
+ | `setDebugMode(enabled)` | Enable/disable debug logging |
302
+ | `getQueueStats()` | Get offline queue statistics |
303
+ | `flushQueue()` | Manually flush the offline queue |
304
+ | `clearQueue()` | Clear the offline queue |
305
+ | `shutdown()` | Cleanup and shutdown the client |
306
+
307
+ ## TypeScript Support
308
+
309
+ Full TypeScript support with exported types:
310
+
311
+ ```typescript
312
+ import {
313
+ Kitbase,
314
+ KitbaseConfig,
315
+ TrackOptions,
316
+ TrackResponse,
317
+ PageViewOptions,
318
+ RevenueOptions,
319
+ IdentifyOptions,
320
+ Session,
321
+ Tags,
322
+ } from '@kitbase/events';
323
+ ```
324
+
325
+ ## Browser Support
326
+
327
+ - Chrome, Firefox, Safari, Edge (latest versions)
328
+ - Works in Node.js 18+
329
+
330
+ ## License
331
+
332
+ MIT
package/dist/index.cjs CHANGED
@@ -502,7 +502,7 @@ var Kitbase = class {
502
502
  throw new ValidationError("API token is required", "token");
503
503
  }
504
504
  this.token = config.token;
505
- this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
505
+ this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
506
506
  this.storageKey = config.storageKey ?? DEFAULT_STORAGE_KEY;
507
507
  this.debugMode = config.debug ?? false;
508
508
  if (config.storage === null) {
@@ -1015,7 +1015,10 @@ var Kitbase = class {
1015
1015
  event: "session_start",
1016
1016
  tags: {
1017
1017
  __session_id: this.session.id,
1018
+ __path: this.session.entryPath ?? "",
1019
+ // For path column in DB
1018
1020
  __entry_path: this.session.entryPath ?? "",
1021
+ // For semantic clarity
1019
1022
  __referrer: this.session.entryReferrer ?? "",
1020
1023
  ...utmParams
1021
1024
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/errors.ts","../src/queue/index.ts"],"sourcesContent":["// Main client\nexport { Kitbase } from './client.js';\n\n// Types\nexport type {\n KitbaseConfig,\n TrackOptions,\n TrackResponse,\n Tags,\n TagValue,\n Storage,\n OfflineConfig,\n // Analytics types\n Session,\n AnalyticsConfig,\n PageViewOptions,\n RevenueOptions,\n IdentifyOptions,\n} from './types.js';\n\n// Queue types\nexport type { QueuedEvent, QueueStats } from './queue/types.js';\n\n// Errors\nexport {\n KitbaseError,\n ApiError,\n AuthenticationError,\n ValidationError,\n TimeoutError,\n} from './errors.js';\n","import { v4 as uuidv4 } from 'uuid';\nimport type {\n KitbaseConfig,\n TrackOptions,\n TrackResponse,\n LogPayload,\n Storage,\n Tags,\n Session,\n AnalyticsConfig,\n PageViewOptions,\n RevenueOptions,\n IdentifyOptions,\n} from './types.js';\nimport {\n ApiError,\n AuthenticationError,\n TimeoutError,\n ValidationError,\n} from './errors.js';\nimport { EventQueue } from './queue/index.js';\nimport type { QueuedEvent, QueueStats } from './queue/types.js';\n\nconst DEFAULT_BASE_URL = 'https://api.kitbase.dev';\nconst TIMEOUT = 30000;\nconst DEFAULT_STORAGE_KEY = 'kitbase_anonymous_id';\nconst DEFAULT_SESSION_STORAGE_KEY = 'kitbase_session';\nconst DEFAULT_SESSION_TIMEOUT = 30 * 60 * 1000; // 30 minutes\nconst ANALYTICS_CHANNEL = '__analytics';\n\n/**\n * In-memory storage fallback for non-browser environments\n */\nclass MemoryStorage implements Storage {\n private data: Map<string, string> = new Map();\n\n getItem(key: string): string | null {\n return this.data.get(key) ?? null;\n }\n\n setItem(key: string, value: string): void {\n this.data.set(key, value);\n }\n\n removeItem(key: string): void {\n this.data.delete(key);\n }\n}\n\n/**\n * Get the default storage (localStorage in browser, memory otherwise)\n */\nfunction getDefaultStorage(): Storage {\n if (typeof window !== 'undefined' && window.localStorage) {\n return window.localStorage;\n }\n return new MemoryStorage();\n}\n\n/**\n * Kitbase client for tracking events\n *\n * @example\n * ```typescript\n * import { Kitbase } from '@kitbase/sdk/events';\n *\n * const kitbase = new Kitbase({\n * token: '<YOUR_API_KEY>',\n * debug: true,\n * offline: { enabled: true },\n * });\n *\n * // Register super properties (included in all events)\n * kitbase.register({ app_version: '2.1.0', platform: 'web' });\n *\n * // Track anonymous events (anonymous_id is automatically included)\n * await kitbase.track({\n * channel: 'payments',\n * event: 'Page Viewed',\n * icon: '👀',\n * });\n *\n * // Time events for duration tracking\n * kitbase.timeEvent('Video Watched');\n * // ... later\n * await kitbase.track({\n * channel: 'engagement',\n * event: 'Video Watched', // $duration automatically included\n * });\n *\n * // Track events for a logged-in user (just pass user_id)\n * await kitbase.track({\n * channel: 'payments',\n * event: 'New Subscription',\n * user_id: 'user-123',\n * icon: '💰',\n * notify: true,\n * tags: {\n * plan: 'premium',\n * cycle: 'monthly',\n * },\n * });\n * ```\n */\nexport class Kitbase {\n private readonly token: string;\n private readonly baseUrl: string;\n private readonly storage: Storage | null;\n private readonly storageKey: string;\n private anonymousId: string | null = null;\n\n // Super properties (memory-only, merged into all events)\n private superProperties: Tags = {};\n\n // Time event tracking\n private timedEvents: Map<string, number> = new Map();\n\n // Debug mode\n private debugMode: boolean;\n\n // Offline queue\n private queue: EventQueue | null = null;\n private offlineEnabled: boolean;\n\n // Analytics & Session tracking\n private session: Session | null = null;\n private sessionTimeout: number;\n private sessionStorageKey: string;\n private analyticsEnabled: boolean;\n private autoTrackPageViews: boolean;\n private userId: string | null = null;\n private unloadListenerAdded = false;\n\n constructor(config: KitbaseConfig) {\n if (!config.token) {\n throw new ValidationError('API token is required', 'token');\n }\n\n this.token = config.token;\n this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n this.storageKey = config.storageKey ?? DEFAULT_STORAGE_KEY;\n this.debugMode = config.debug ?? false;\n\n // Initialize storage (null means disabled)\n if (config.storage === null) {\n this.storage = null;\n } else {\n this.storage = config.storage ?? getDefaultStorage();\n }\n\n // Load or generate anonymous ID\n this.initializeAnonymousId();\n\n // Initialize analytics configuration\n this.sessionTimeout = config.analytics?.sessionTimeout ?? DEFAULT_SESSION_TIMEOUT;\n this.sessionStorageKey = config.analytics?.sessionStorageKey ?? DEFAULT_SESSION_STORAGE_KEY;\n this.analyticsEnabled = config.analytics?.autoTrackSessions ?? true;\n this.autoTrackPageViews = config.analytics?.autoTrackPageViews ?? false;\n\n // Load existing session from storage\n if (this.analyticsEnabled) {\n this.loadSession();\n this.setupUnloadListener();\n\n // Auto-track pageviews if enabled\n if (this.autoTrackPageViews && typeof window !== 'undefined') {\n this.enableAutoPageViews();\n }\n }\n\n // Initialize offline queue if enabled\n this.offlineEnabled = config.offline?.enabled ?? false;\n if (this.offlineEnabled) {\n this.queue = new EventQueue(config.offline);\n this.queue.setDebugMode(this.debugMode, this.log.bind(this));\n this.queue.setSendCallback(this.sendQueuedEvents.bind(this));\n this.queue.startFlushTimer();\n this.log('Offline queueing enabled', {\n storageType: this.queue.getStorageType(),\n });\n }\n }\n\n /**\n * Initialize the anonymous ID from storage or generate a new one\n */\n private initializeAnonymousId(): void {\n if (this.storage) {\n const stored = this.storage.getItem(this.storageKey);\n if (stored) {\n this.anonymousId = stored;\n return;\n }\n }\n\n // Generate new anonymous ID\n this.anonymousId = uuidv4();\n\n // Persist if storage is available\n if (this.storage && this.anonymousId) {\n this.storage.setItem(this.storageKey, this.anonymousId);\n }\n }\n\n /**\n * Get the current anonymous ID\n */\n getAnonymousId(): string | null {\n return this.anonymousId;\n }\n\n // ============================================================\n // Debug Mode\n // ============================================================\n\n /**\n * Enable or disable debug mode\n * When enabled, all SDK operations are logged to the console\n *\n * @param enabled - Whether to enable debug mode\n *\n * @example\n * ```typescript\n * kitbase.setDebugMode(true);\n * // All events and operations will now be logged\n * ```\n */\n setDebugMode(enabled: boolean): void {\n this.debugMode = enabled;\n if (this.queue) {\n this.queue.setDebugMode(enabled, this.log.bind(this));\n }\n this.log(`Debug mode ${enabled ? 'enabled' : 'disabled'}`);\n }\n\n /**\n * Check if debug mode is enabled\n */\n isDebugMode(): boolean {\n return this.debugMode;\n }\n\n /**\n * Internal logging function\n */\n private log(message: string, data?: unknown): void {\n if (!this.debugMode) return;\n\n const prefix = '[Kitbase]';\n if (data !== undefined) {\n console.log(prefix, message, data);\n } else {\n console.log(prefix, message);\n }\n }\n\n // ============================================================\n // Super Properties\n // ============================================================\n\n /**\n * Register super properties that will be included with every event\n * These properties are stored in memory only and reset on page reload\n *\n * @param properties - Properties to register\n *\n * @example\n * ```typescript\n * kitbase.register({\n * app_version: '2.1.0',\n * platform: 'web',\n * environment: 'production',\n * });\n * ```\n */\n register(properties: Tags): void {\n this.superProperties = { ...this.superProperties, ...properties };\n this.log('Super properties registered', properties);\n }\n\n /**\n * Register super properties only if they haven't been set yet\n * Useful for setting default values that shouldn't override existing ones\n *\n * @param properties - Properties to register if not already set\n *\n * @example\n * ```typescript\n * kitbase.registerOnce({ first_visit: new Date().toISOString() });\n * ```\n */\n registerOnce(properties: Tags): void {\n const newProps: Tags = {};\n for (const [key, value] of Object.entries(properties)) {\n if (!(key in this.superProperties)) {\n newProps[key] = value;\n }\n }\n if (Object.keys(newProps).length > 0) {\n this.superProperties = { ...this.superProperties, ...newProps };\n this.log('Super properties registered (once)', newProps);\n }\n }\n\n /**\n * Remove a super property\n *\n * @param key - The property key to remove\n *\n * @example\n * ```typescript\n * kitbase.unregister('platform');\n * ```\n */\n unregister(key: string): void {\n if (key in this.superProperties) {\n delete this.superProperties[key];\n this.log('Super property removed', { key });\n }\n }\n\n /**\n * Get all registered super properties\n *\n * @returns A copy of the current super properties\n *\n * @example\n * ```typescript\n * const props = kitbase.getSuperProperties();\n * console.log(props); // { app_version: '2.1.0', platform: 'web' }\n * ```\n */\n getSuperProperties(): Tags {\n return { ...this.superProperties };\n }\n\n /**\n * Clear all super properties\n *\n * @example\n * ```typescript\n * kitbase.clearSuperProperties();\n * ```\n */\n clearSuperProperties(): void {\n this.superProperties = {};\n this.log('Super properties cleared');\n }\n\n // ============================================================\n // Time Events (Duration Tracking)\n // ============================================================\n\n /**\n * Start timing an event\n * When the same event is tracked later, a $duration property (in seconds)\n * will automatically be included\n *\n * @param eventName - The name of the event to time\n *\n * @example\n * ```typescript\n * kitbase.timeEvent('Video Watched');\n * // ... user watches video ...\n * await kitbase.track({\n * channel: 'engagement',\n * event: 'Video Watched',\n * tags: { video_id: '123' }\n * });\n * // Event will include $duration: 45.2 (seconds)\n * ```\n */\n timeEvent(eventName: string): void {\n this.timedEvents.set(eventName, Date.now());\n this.log('Timer started', { event: eventName });\n }\n\n /**\n * Cancel a timed event without tracking it\n *\n * @param eventName - The name of the event to cancel timing for\n *\n * @example\n * ```typescript\n * kitbase.timeEvent('Checkout Flow');\n * // User abandons checkout\n * kitbase.cancelTimeEvent('Checkout Flow');\n * ```\n */\n cancelTimeEvent(eventName: string): void {\n if (this.timedEvents.has(eventName)) {\n this.timedEvents.delete(eventName);\n this.log('Timer cancelled', { event: eventName });\n }\n }\n\n /**\n * Get all currently timed events\n *\n * @returns Array of event names that are currently being timed\n *\n * @example\n * ```typescript\n * const timedEvents = kitbase.getTimedEvents();\n * console.log(timedEvents); // ['Video Watched', 'Checkout Flow']\n * ```\n */\n getTimedEvents(): string[] {\n return Array.from(this.timedEvents.keys());\n }\n\n /**\n * Get the duration of a timed event (without stopping it)\n *\n * @param eventName - The name of the event\n * @returns Duration in seconds, or null if not being timed\n */\n getEventDuration(eventName: string): number | null {\n const startTime = this.timedEvents.get(eventName);\n if (startTime === undefined) return null;\n return (Date.now() - startTime) / 1000;\n }\n\n // ============================================================\n // Offline Queue\n // ============================================================\n\n /**\n * Get offline queue statistics\n *\n * @returns Queue statistics including size and flush status\n *\n * @example\n * ```typescript\n * const stats = await kitbase.getQueueStats();\n * console.log(stats); // { size: 5, isFlushing: false }\n * ```\n */\n async getQueueStats(): Promise<QueueStats | null> {\n if (!this.queue) return null;\n return this.queue.getStats();\n }\n\n /**\n * Manually flush the offline queue\n * Events are automatically flushed on interval and when coming back online,\n * but this method can be used to trigger an immediate flush\n *\n * @example\n * ```typescript\n * await kitbase.flushQueue();\n * ```\n */\n async flushQueue(): Promise<void> {\n if (!this.queue) return;\n await this.queue.flush();\n }\n\n /**\n * Clear all events from the offline queue\n *\n * @example\n * ```typescript\n * await kitbase.clearQueue();\n * ```\n */\n async clearQueue(): Promise<void> {\n if (!this.queue) return;\n await this.queue.clear();\n }\n\n /**\n * Callback for the queue to send batched events\n */\n private async sendQueuedEvents(events: QueuedEvent[]): Promise<number[]> {\n const sentIds: number[] = [];\n\n for (const event of events) {\n try {\n await this.sendRequest<TrackResponse>('/sdk/v1/logs', event.payload);\n sentIds.push(event.id!);\n } catch (error) {\n this.log('Failed to send queued event', { id: event.id, error });\n // Continue with next event\n }\n }\n\n return sentIds;\n }\n\n // ============================================================\n // Track Event\n // ============================================================\n\n /**\n * Track an event\n *\n * When offline queueing is enabled, events are always written to the local\n * database first (write-ahead), then sent to the server. This ensures no\n * events are lost if the browser crashes or the network fails.\n *\n * @param options - Event tracking options\n * @returns Promise resolving to the track response\n * @throws {ValidationError} When required fields are missing\n * @throws {AuthenticationError} When the API key is invalid (only when offline disabled)\n * @throws {ApiError} When the API returns an error (only when offline disabled)\n * @throws {TimeoutError} When the request times out (only when offline disabled)\n */\n async track(options: TrackOptions): Promise<TrackResponse> {\n this.validateTrackOptions(options);\n\n // Calculate duration if this event was being timed\n let duration: number | undefined;\n const startTime = this.timedEvents.get(options.event);\n if (startTime !== undefined) {\n duration = (Date.now() - startTime) / 1000;\n this.timedEvents.delete(options.event);\n this.log('Timer stopped', { event: options.event, duration });\n }\n\n // Include anonymous_id unless explicitly disabled\n const includeAnonymousId = options.includeAnonymousId !== false;\n\n // Merge super properties with event tags (event tags take precedence)\n const mergedTags: Tags = {\n ...this.superProperties,\n ...(options.tags ?? {}),\n ...(duration !== undefined ? { $duration: duration } : {}),\n };\n\n const payload: LogPayload = {\n channel: options.channel,\n event: options.event,\n timestamp: Date.now(),\n ...(options.user_id && { user_id: options.user_id }),\n ...(includeAnonymousId && this.anonymousId && { anonymous_id: this.anonymousId }),\n ...(options.icon && { icon: options.icon }),\n ...(options.notify !== undefined && { notify: options.notify }),\n ...(options.description && { description: options.description }),\n ...(Object.keys(mergedTags).length > 0 && { tags: mergedTags }),\n };\n\n this.log('Track', { event: options.event, payload });\n\n // If offline queueing is enabled, use write-ahead pattern\n if (this.queue) {\n // Always write to DB first (guaranteed durability)\n await this.queue.enqueue(payload);\n this.log('Event persisted to queue');\n\n // Trigger an immediate flush attempt (non-blocking)\n this.queue.flush().catch((err) => {\n this.log('Background flush failed', err);\n });\n\n // Return immediately after DB write\n return {\n id: `queued-${Date.now()}`,\n event: options.event,\n timestamp: new Date().toISOString(),\n };\n }\n\n // No offline queue - send directly (original behavior)\n const response = await this.sendRequest<TrackResponse>('/sdk/v1/logs', payload);\n this.log('Event sent successfully', { id: response.id });\n return response;\n }\n\n private validateTrackOptions(options: TrackOptions): void {\n if (!options.event) {\n throw new ValidationError('Event is required', 'event');\n }\n }\n\n /**\n * Send a request to the API\n */\n private async sendRequest<T>(endpoint: string, body: unknown): Promise<T> {\n const url = `${this.baseUrl}${endpoint}`;\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), TIMEOUT);\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-sdk-key': `${this.token}`,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorBody = await this.parseResponseBody(response);\n\n if (response.status === 401) {\n throw new AuthenticationError();\n }\n\n throw new ApiError(\n this.getErrorMessage(errorBody, response.statusText),\n response.status,\n errorBody,\n );\n }\n\n return (await response.json()) as T;\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof Error && error.name === 'AbortError') {\n throw new TimeoutError();\n }\n\n throw error;\n }\n }\n\n private async parseResponseBody(response: Response): Promise<unknown> {\n try {\n return await response.json();\n } catch {\n return null;\n }\n }\n\n private getErrorMessage(body: unknown, fallback: string): string {\n if (body && typeof body === 'object' && 'message' in body) {\n return String((body as { message: unknown }).message);\n }\n if (body && typeof body === 'object' && 'error' in body) {\n return String((body as { error: unknown }).error);\n }\n return fallback;\n }\n\n // ============================================================\n // Analytics & Session Management\n // ============================================================\n\n /**\n * Load session from storage\n */\n private loadSession(): void {\n if (!this.storage) return;\n\n try {\n const stored = this.storage.getItem(this.sessionStorageKey);\n if (stored) {\n const session = JSON.parse(stored) as Session;\n const now = Date.now();\n\n // Check if session is still valid (not expired)\n if (now - session.lastActivityAt < this.sessionTimeout) {\n this.session = session;\n this.log('Session restored', { sessionId: session.id });\n } else {\n // Session expired, will create new one on next activity\n this.storage.removeItem(this.sessionStorageKey);\n this.log('Session expired, removed from storage');\n }\n }\n } catch (error) {\n this.log('Failed to load session from storage', error);\n }\n }\n\n /**\n * Save session to storage\n */\n private saveSession(): void {\n if (!this.storage || !this.session) return;\n\n try {\n this.storage.setItem(this.sessionStorageKey, JSON.stringify(this.session));\n } catch (error) {\n this.log('Failed to save session to storage', error);\n }\n }\n\n /**\n * Get or create a session\n */\n private getOrCreateSession(): Session {\n const now = Date.now();\n\n // Check if session expired\n if (this.session && (now - this.session.lastActivityAt) > this.sessionTimeout) {\n this.endSession();\n this.session = null;\n }\n\n // Create new session if needed\n if (!this.session) {\n const referrer = typeof document !== 'undefined' ? document.referrer : undefined;\n const path = typeof window !== 'undefined' ? window.location.pathname : undefined;\n\n this.session = {\n id: uuidv4(),\n startedAt: now,\n lastActivityAt: now,\n screenViewCount: 0,\n entryPath: path,\n entryReferrer: referrer,\n };\n\n this.log('New session created', { sessionId: this.session.id });\n this.trackSessionStart();\n }\n\n // Update last activity\n this.session.lastActivityAt = now;\n this.saveSession();\n\n return this.session;\n }\n\n /**\n * Get the current session ID (or null if no active session)\n */\n getSessionId(): string | null {\n return this.session?.id ?? null;\n }\n\n /**\n * Get the current session data\n */\n getSession(): Session | null {\n return this.session ? { ...this.session } : null;\n }\n\n /**\n * Track session start event\n */\n private trackSessionStart(): void {\n if (!this.session) return;\n\n const utmParams = this.getUtmParams();\n\n this.track({\n channel: ANALYTICS_CHANNEL,\n event: 'session_start',\n tags: {\n __session_id: this.session.id,\n __entry_path: this.session.entryPath ?? '',\n __referrer: this.session.entryReferrer ?? '',\n ...utmParams,\n },\n }).catch((err) => this.log('Failed to track session_start', err));\n }\n\n /**\n * End the current session (clears local state only - server calculates metrics)\n */\n private endSession(): void {\n if (!this.session) return;\n\n this.log('Session ended', { sessionId: this.session.id });\n\n // Clear session from storage (no event sent - server calculates metrics)\n if (this.storage) {\n this.storage.removeItem(this.sessionStorageKey);\n }\n this.session = null;\n }\n\n /**\n * Setup listeners for session lifecycle management\n */\n private setupUnloadListener(): void {\n if (typeof window === 'undefined' || this.unloadListenerAdded) return;\n\n // On visibility change: save session state\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') {\n this.saveSession();\n this.log('Page hidden, session state saved');\n }\n });\n\n // On page unload: end session (clears local state only)\n window.addEventListener('pagehide', () => {\n this.endSession();\n this.log('Page unloading, session ended locally');\n });\n\n this.unloadListenerAdded = true;\n this.log('Session lifecycle listeners added');\n }\n\n /**\n * Get UTM parameters from URL\n */\n private getUtmParams(): Tags {\n if (typeof window === 'undefined') return {};\n\n const params = new URLSearchParams(window.location.search);\n const utmParams: Tags = {};\n\n const utmKeys = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'];\n for (const key of utmKeys) {\n const value = params.get(key);\n if (value) {\n utmParams[`__${key}`] = value;\n }\n }\n\n return utmParams;\n }\n\n /**\n * Track a page view\n *\n * @param options - Page view options\n * @returns Promise resolving to the track response\n *\n * @example\n * ```typescript\n * // Track current page\n * await kitbase.trackPageView();\n *\n * // Track with custom path\n * await kitbase.trackPageView({ path: '/products/123', title: 'Product Details' });\n * ```\n */\n async trackPageView(options: PageViewOptions = {}): Promise<TrackResponse> {\n const session = this.getOrCreateSession();\n session.screenViewCount++;\n this.saveSession();\n\n const path = options.path ?? (typeof window !== 'undefined' ? window.location.pathname : '');\n const title = options.title ?? (typeof document !== 'undefined' ? document.title : '');\n const referrer = options.referrer ?? (typeof document !== 'undefined' ? document.referrer : '');\n\n return this.track({\n channel: ANALYTICS_CHANNEL,\n event: 'screen_view',\n tags: {\n __session_id: session.id,\n __path: path,\n __title: title,\n __referrer: referrer,\n ...this.getUtmParams(),\n ...(options.tags ?? {}),\n },\n });\n }\n\n /**\n * Enable automatic page view tracking\n * Intercepts browser history changes (pushState, replaceState, popstate)\n *\n * @example\n * ```typescript\n * kitbase.enableAutoPageViews();\n * // Now all route changes will automatically be tracked\n * ```\n */\n enableAutoPageViews(): void {\n if (typeof window === 'undefined') {\n this.log('Auto page views not available in non-browser environment');\n return;\n }\n\n // Track initial page view\n this.trackPageView().catch((err) => this.log('Failed to track initial page view', err));\n\n // Intercept pushState\n const originalPushState = history.pushState.bind(history);\n history.pushState = (...args) => {\n originalPushState(...args);\n this.trackPageView().catch((err) => this.log('Failed to track page view (pushState)', err));\n };\n\n // Intercept replaceState\n const originalReplaceState = history.replaceState.bind(history);\n history.replaceState = (...args) => {\n originalReplaceState(...args);\n // Don't track replaceState as it's usually not a navigation\n };\n\n // Listen to popstate (browser back/forward)\n window.addEventListener('popstate', () => {\n this.trackPageView().catch((err) => this.log('Failed to track page view (popstate)', err));\n });\n\n this.log('Auto page view tracking enabled');\n }\n\n /**\n * Track a revenue event\n *\n * @param options - Revenue options\n * @returns Promise resolving to the track response\n *\n * @example\n * ```typescript\n * // Track a $19.99 purchase\n * await kitbase.trackRevenue({\n * amount: 1999,\n * currency: 'USD',\n * tags: { product_id: 'prod_123', plan: 'premium' },\n * });\n * ```\n */\n async trackRevenue(options: RevenueOptions): Promise<TrackResponse> {\n const session = this.getOrCreateSession();\n\n return this.track({\n channel: ANALYTICS_CHANNEL,\n event: 'revenue',\n user_id: options.user_id ?? this.userId ?? undefined,\n tags: {\n __session_id: session.id,\n __revenue: options.amount,\n __currency: options.currency ?? 'USD',\n ...(options.tags ?? {}),\n },\n });\n }\n\n /**\n * Identify a user\n * Links the current anonymous ID to a user ID for future events\n *\n * @param options - Identify options\n *\n * @example\n * ```typescript\n * kitbase.identify({\n * userId: 'user_123',\n * traits: { email: 'user@example.com', plan: 'premium' },\n * });\n * ```\n */\n identify(options: IdentifyOptions): void {\n this.userId = options.userId;\n\n // Register user traits as super properties\n if (options.traits) {\n this.register({\n __user_id: options.userId,\n ...options.traits,\n });\n } else {\n this.register({ __user_id: options.userId });\n }\n\n // Track identify event\n this.track({\n channel: ANALYTICS_CHANNEL,\n event: 'identify',\n user_id: options.userId,\n tags: {\n __session_id: this.session?.id ?? '',\n __anonymous_id: this.anonymousId ?? '',\n ...(options.traits ?? {}),\n },\n }).catch((err) => this.log('Failed to track identify', err));\n\n this.log('User identified', { userId: options.userId });\n }\n\n /**\n * Get the current user ID (set via identify)\n */\n getUserId(): string | null {\n return this.userId;\n }\n\n /**\n * Reset the user identity and session\n * Call this when a user logs out\n *\n * @example\n * ```typescript\n * kitbase.reset();\n * ```\n */\n reset(): void {\n // End current session\n if (this.session) {\n this.endSession();\n this.session = null;\n }\n\n // Clear user ID\n this.userId = null;\n\n // Generate new anonymous ID\n this.anonymousId = uuidv4();\n if (this.storage) {\n this.storage.setItem(this.storageKey, this.anonymousId);\n }\n\n // Clear super properties\n this.clearSuperProperties();\n\n this.log('User reset complete');\n }\n\n // ============================================================\n // Cleanup\n // ============================================================\n\n /**\n * Shutdown the client and cleanup resources\n * Call this when you're done using the client to stop timers and close connections\n *\n * @example\n * ```typescript\n * await kitbase.shutdown();\n * ```\n */\n async shutdown(): Promise<void> {\n this.log('Shutting down');\n\n // Flush any remaining queued events\n if (this.queue) {\n await this.queue.flush();\n await this.queue.close();\n this.queue = null;\n }\n\n // Clear timed events\n this.timedEvents.clear();\n\n this.log('Shutdown complete');\n }\n}\n","/**\n * Base error class for Kitbase SDK errors\n */\nexport class KitbaseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'KitbaseError';\n Object.setPrototypeOf(this, KitbaseError.prototype);\n }\n}\n\n/**\n * Error thrown when API authentication fails\n */\nexport class AuthenticationError extends KitbaseError {\n constructor(message = 'Invalid API key') {\n super(message);\n this.name = 'AuthenticationError';\n Object.setPrototypeOf(this, AuthenticationError.prototype);\n }\n}\n\n/**\n * Error thrown when the API request fails\n */\nexport class ApiError extends KitbaseError {\n public readonly statusCode: number;\n public readonly response?: unknown;\n\n constructor(message: string, statusCode: number, response?: unknown) {\n super(message);\n this.name = 'ApiError';\n this.statusCode = statusCode;\n this.response = response;\n Object.setPrototypeOf(this, ApiError.prototype);\n }\n}\n\n/**\n * Error thrown when request validation fails\n */\nexport class ValidationError extends KitbaseError {\n public readonly field?: string;\n\n constructor(message: string, field?: string) {\n super(message);\n this.name = 'ValidationError';\n this.field = field;\n Object.setPrototypeOf(this, ValidationError.prototype);\n }\n}\n\n/**\n * Error thrown when a request times out\n */\nexport class TimeoutError extends KitbaseError {\n constructor(message = 'Request timed out') {\n super(message);\n this.name = 'TimeoutError';\n Object.setPrototypeOf(this, TimeoutError.prototype);\n }\n}\n\n\n\n\n\n\n\n\n","import Dexie, { type Table } from 'dexie';\nimport type { LogPayload } from '../types.js';\nimport type {\n OfflineConfig,\n QueuedEvent,\n QueueStats,\n EventQueueInterface,\n SendEventsCallback,\n} from './types.js';\n\nconst DEFAULT_CONFIG: Required<OfflineConfig> = {\n enabled: false,\n maxQueueSize: 1000,\n flushInterval: 30000,\n flushBatchSize: 50,\n maxRetries: 3,\n retryBaseDelay: 1000,\n};\n\n/**\n * Check if IndexedDB is available\n */\nfunction isIndexedDBAvailable(): boolean {\n try {\n return (\n typeof window !== 'undefined' &&\n typeof window.indexedDB !== 'undefined' &&\n window.indexedDB !== null\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Check if we're in a browser environment\n */\nfunction isBrowser(): boolean {\n return typeof window !== 'undefined' && typeof document !== 'undefined';\n}\n\n/**\n * Dexie database for event queue\n */\nclass KitbaseQueueDB extends Dexie {\n events!: Table<QueuedEvent, number>;\n\n constructor(dbName: string) {\n super(dbName);\n this.version(1).stores({\n events: '++id, timestamp, retries, lastAttempt',\n });\n }\n}\n\n/**\n * In-memory queue implementation for Node.js or when IndexedDB is unavailable\n */\nclass MemoryQueue {\n private queue: QueuedEvent[] = [];\n private idCounter = 1;\n\n async enqueue(payload: LogPayload): Promise<number> {\n const event: QueuedEvent = {\n id: this.idCounter++,\n payload,\n timestamp: Date.now(),\n retries: 0,\n };\n this.queue.push(event);\n return event.id!;\n }\n\n async dequeue(count: number): Promise<QueuedEvent[]> {\n // Sort by timestamp (oldest first) and get the first `count` events\n this.queue.sort((a, b) => a.timestamp - b.timestamp);\n return this.queue.slice(0, count);\n }\n\n async delete(ids: number[]): Promise<void> {\n this.queue = this.queue.filter((e) => !ids.includes(e.id!));\n }\n\n async updateRetries(ids: number[]): Promise<void> {\n const now = Date.now();\n for (const event of this.queue) {\n if (ids.includes(event.id!)) {\n event.retries++;\n event.lastAttempt = now;\n }\n }\n }\n\n async getStats(): Promise<{ size: number; oldestEvent?: number }> {\n const size = this.queue.length;\n const oldestEvent =\n size > 0\n ? Math.min(...this.queue.map((e) => e.timestamp))\n : undefined;\n return { size, oldestEvent };\n }\n\n async clear(): Promise<void> {\n this.queue = [];\n }\n\n async enforceMaxSize(maxSize: number): Promise<void> {\n if (this.queue.length > maxSize) {\n // Sort by timestamp and keep only the newest events\n this.queue.sort((a, b) => a.timestamp - b.timestamp);\n this.queue = this.queue.slice(-maxSize);\n }\n }\n\n async getEventsExceedingRetries(maxRetries: number): Promise<number[]> {\n return this.queue\n .filter((e) => e.retries >= maxRetries)\n .map((e) => e.id!);\n }\n}\n\n/**\n * Event queue for offline support\n * Uses IndexedDB (via Dexie) in browser, in-memory queue in Node.js\n */\nexport class EventQueue implements EventQueueInterface {\n private readonly config: Required<OfflineConfig>;\n private readonly dbName: string;\n private db: KitbaseQueueDB | null = null;\n private memoryQueue: MemoryQueue | null = null;\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private isFlushing = false;\n private sendEvents: SendEventsCallback | null = null;\n private readonly useIndexedDB: boolean;\n private debugMode = false;\n private debugLogger: ((message: string, data?: unknown) => void) | null = null;\n\n constructor(config: OfflineConfig = {}, dbName = 'kitbase-events') {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.dbName = dbName;\n this.useIndexedDB = isIndexedDBAvailable();\n\n if (this.useIndexedDB) {\n this.db = new KitbaseQueueDB(this.dbName);\n } else {\n this.memoryQueue = new MemoryQueue();\n }\n }\n\n /**\n * Set debug mode and logger\n */\n setDebugMode(enabled: boolean, logger?: (message: string, data?: unknown) => void): void {\n this.debugMode = enabled;\n this.debugLogger = logger ?? null;\n }\n\n private log(message: string, data?: unknown): void {\n if (this.debugMode && this.debugLogger) {\n this.debugLogger(message, data);\n }\n }\n\n /**\n * Set the callback for sending events\n */\n setSendCallback(callback: SendEventsCallback): void {\n this.sendEvents = callback;\n }\n\n /**\n * Check if the queue storage is available\n */\n isAvailable(): boolean {\n return this.useIndexedDB || this.memoryQueue !== null;\n }\n\n /**\n * Get the storage type being used\n */\n getStorageType(): 'indexeddb' | 'memory' {\n return this.useIndexedDB ? 'indexeddb' : 'memory';\n }\n\n /**\n * Add an event to the queue\n */\n async enqueue(payload: LogPayload): Promise<void> {\n const event: Omit<QueuedEvent, 'id'> = {\n payload,\n timestamp: Date.now(),\n retries: 0,\n };\n\n if (this.useIndexedDB && this.db) {\n await this.db.events.add(event as QueuedEvent);\n this.log('Event queued to IndexedDB', payload);\n } else if (this.memoryQueue) {\n await this.memoryQueue.enqueue(payload);\n this.log('Event queued to memory', payload);\n }\n\n // Enforce max queue size\n await this.enforceMaxQueueSize();\n }\n\n /**\n * Get and remove the next batch of events to send\n */\n async dequeue(count: number): Promise<QueuedEvent[]> {\n if (this.useIndexedDB && this.db) {\n // Get events sorted by timestamp (oldest first), excluding those with too many retries\n return this.db.events\n .where('retries')\n .below(this.config.maxRetries)\n .sortBy('timestamp')\n .then((events) => events.slice(0, count));\n } else if (this.memoryQueue) {\n const events = await this.memoryQueue.dequeue(count);\n return events.filter((e) => e.retries < this.config.maxRetries);\n }\n return [];\n }\n\n /**\n * Mark events as successfully sent (remove from queue)\n */\n async markSent(ids: number[]): Promise<void> {\n if (ids.length === 0) return;\n\n if (this.useIndexedDB && this.db) {\n await this.db.events.bulkDelete(ids);\n this.log(`Removed ${ids.length} sent events from queue`);\n } else if (this.memoryQueue) {\n await this.memoryQueue.delete(ids);\n this.log(`Removed ${ids.length} sent events from memory queue`);\n }\n }\n\n /**\n * Mark events as failed and increment retry count\n */\n async markFailed(ids: number[]): Promise<void> {\n if (ids.length === 0) return;\n\n const now = Date.now();\n\n if (this.useIndexedDB && this.db) {\n await this.db.transaction('rw', this.db.events, async () => {\n for (const id of ids) {\n const event = await this.db!.events.get(id);\n if (event) {\n await this.db!.events.update(id, {\n retries: event.retries + 1,\n lastAttempt: now,\n });\n }\n }\n });\n this.log(`Marked ${ids.length} events as failed`);\n } else if (this.memoryQueue) {\n await this.memoryQueue.updateRetries(ids);\n this.log(`Marked ${ids.length} events as failed in memory queue`);\n }\n\n // Remove events that have exceeded max retries\n await this.removeExpiredRetries();\n }\n\n /**\n * Remove events that have exceeded max retry attempts\n */\n private async removeExpiredRetries(): Promise<void> {\n if (this.useIndexedDB && this.db) {\n const expiredIds = await this.db.events\n .where('retries')\n .aboveOrEqual(this.config.maxRetries)\n .primaryKeys();\n if (expiredIds.length > 0) {\n await this.db.events.bulkDelete(expiredIds);\n this.log(`Removed ${expiredIds.length} events that exceeded max retries`);\n }\n } else if (this.memoryQueue) {\n const expiredIds = await this.memoryQueue.getEventsExceedingRetries(\n this.config.maxRetries\n );\n if (expiredIds.length > 0) {\n await this.memoryQueue.delete(expiredIds);\n this.log(`Removed ${expiredIds.length} events that exceeded max retries`);\n }\n }\n }\n\n /**\n * Enforce the maximum queue size by removing oldest events\n */\n private async enforceMaxQueueSize(): Promise<void> {\n if (this.useIndexedDB && this.db) {\n const count = await this.db.events.count();\n if (count > this.config.maxQueueSize) {\n const excess = count - this.config.maxQueueSize;\n const oldestEvents = await this.db.events\n .orderBy('timestamp')\n .limit(excess)\n .primaryKeys();\n await this.db.events.bulkDelete(oldestEvents);\n this.log(`Removed ${excess} oldest events to enforce queue size limit`);\n }\n } else if (this.memoryQueue) {\n await this.memoryQueue.enforceMaxSize(this.config.maxQueueSize);\n }\n }\n\n /**\n * Get queue statistics\n */\n async getStats(): Promise<QueueStats> {\n if (this.useIndexedDB && this.db) {\n const size = await this.db.events.count();\n const oldestEvent = await this.db.events\n .orderBy('timestamp')\n .first()\n .then((e) => e?.timestamp);\n return { size, oldestEvent, isFlushing: this.isFlushing };\n } else if (this.memoryQueue) {\n const stats = await this.memoryQueue.getStats();\n return { ...stats, isFlushing: this.isFlushing };\n }\n return { size: 0, isFlushing: this.isFlushing };\n }\n\n /**\n * Clear all events from the queue\n */\n async clear(): Promise<void> {\n if (this.useIndexedDB && this.db) {\n await this.db.events.clear();\n this.log('Queue cleared (IndexedDB)');\n } else if (this.memoryQueue) {\n await this.memoryQueue.clear();\n this.log('Queue cleared (memory)');\n }\n }\n\n /**\n * Start the automatic flush timer\n */\n startFlushTimer(): void {\n if (this.flushTimer) return;\n\n this.flushTimer = setInterval(() => {\n this.flush().catch((err) => {\n this.log('Flush timer error', err);\n });\n }, this.config.flushInterval);\n\n // Also listen for online events in browser\n if (isBrowser()) {\n window.addEventListener('online', this.handleOnline);\n }\n\n this.log(`Flush timer started (interval: ${this.config.flushInterval}ms)`);\n }\n\n /**\n * Stop the automatic flush timer\n */\n stopFlushTimer(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n if (isBrowser()) {\n window.removeEventListener('online', this.handleOnline);\n }\n\n this.log('Flush timer stopped');\n }\n\n /**\n * Handle coming back online\n */\n private handleOnline = (): void => {\n this.log('Browser came online, triggering flush');\n this.flush().catch((err) => {\n this.log('Online flush error', err);\n });\n };\n\n /**\n * Check if we're currently online\n */\n private isOnline(): boolean {\n if (isBrowser()) {\n return navigator.onLine;\n }\n // Assume online in Node.js\n return true;\n }\n\n /**\n * Manually trigger a flush of queued events\n */\n async flush(): Promise<void> {\n if (this.isFlushing) {\n this.log('Flush already in progress, skipping');\n return;\n }\n\n if (!this.isOnline()) {\n this.log('Offline, skipping flush');\n return;\n }\n\n if (!this.sendEvents) {\n this.log('No send callback configured, skipping flush');\n return;\n }\n\n this.isFlushing = true;\n\n try {\n const stats = await this.getStats();\n if (stats.size === 0) {\n this.log('Queue is empty, nothing to flush');\n return;\n }\n\n this.log(`Flushing queue (${stats.size} events)`);\n\n // Process in batches\n let processed = 0;\n while (true) {\n const events = await this.dequeue(this.config.flushBatchSize);\n if (events.length === 0) break;\n\n this.log(`Sending batch of ${events.length} events`);\n\n try {\n const sentIds = await this.sendEvents(events);\n await this.markSent(sentIds);\n\n // Mark remaining as failed\n const failedIds = events\n .filter((e) => !sentIds.includes(e.id!))\n .map((e) => e.id!);\n if (failedIds.length > 0) {\n await this.markFailed(failedIds);\n }\n\n processed += sentIds.length;\n } catch (error) {\n // Mark all as failed on network error\n const allIds = events.map((e) => e.id!);\n await this.markFailed(allIds);\n this.log('Batch send failed', error);\n break; // Stop flushing on error\n }\n }\n\n this.log(`Flush complete, sent ${processed} events`);\n } finally {\n this.isFlushing = false;\n }\n }\n\n /**\n * Close the database connection\n */\n async close(): Promise<void> {\n this.stopFlushTimer();\n if (this.db) {\n this.db.close();\n this.log('Database connection closed');\n }\n }\n}\n\nexport type { OfflineConfig, QueuedEvent, QueueStats, EventQueueInterface, SendEventsCallback };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAA6B;;;ACGtB,IAAM,eAAN,MAAM,sBAAqB,MAAM;AAAA,EACtC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,cAAa,SAAS;AAAA,EACpD;AACF;AAKO,IAAM,sBAAN,MAAM,6BAA4B,aAAa;AAAA,EACpD,YAAY,UAAU,mBAAmB;AACvC,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,qBAAoB,SAAS;AAAA,EAC3D;AACF;AAKO,IAAM,WAAN,MAAM,kBAAiB,aAAa;AAAA,EACzB;AAAA,EACA;AAAA,EAEhB,YAAY,SAAiB,YAAoB,UAAoB;AACnE,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,aAAa;AAClB,SAAK,WAAW;AAChB,WAAO,eAAe,MAAM,UAAS,SAAS;AAAA,EAChD;AACF;AAKO,IAAM,kBAAN,MAAM,yBAAwB,aAAa;AAAA,EAChC;AAAA,EAEhB,YAAY,SAAiB,OAAgB;AAC3C,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,QAAQ;AACb,WAAO,eAAe,MAAM,iBAAgB,SAAS;AAAA,EACvD;AACF;AAKO,IAAM,eAAN,MAAM,sBAAqB,aAAa;AAAA,EAC7C,YAAY,UAAU,qBAAqB;AACzC,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,cAAa,SAAS;AAAA,EACpD;AACF;;;AC7DA,mBAAkC;AAUlC,IAAM,iBAA0C;AAAA,EAC9C,SAAS;AAAA,EACT,cAAc;AAAA,EACd,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,gBAAgB;AAClB;AAKA,SAAS,uBAAgC;AACvC,MAAI;AACF,WACE,OAAO,WAAW,eAClB,OAAO,OAAO,cAAc,eAC5B,OAAO,cAAc;AAAA,EAEzB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKA,SAAS,YAAqB;AAC5B,SAAO,OAAO,WAAW,eAAe,OAAO,aAAa;AAC9D;AAKA,IAAM,iBAAN,cAA6B,aAAAA,QAAM;AAAA,EACjC;AAAA,EAEA,YAAY,QAAgB;AAC1B,UAAM,MAAM;AACZ,SAAK,QAAQ,CAAC,EAAE,OAAO;AAAA,MACrB,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AACF;AAKA,IAAM,cAAN,MAAkB;AAAA,EACR,QAAuB,CAAC;AAAA,EACxB,YAAY;AAAA,EAEpB,MAAM,QAAQ,SAAsC;AAClD,UAAM,QAAqB;AAAA,MACzB,IAAI,KAAK;AAAA,MACT;AAAA,MACA,WAAW,KAAK,IAAI;AAAA,MACpB,SAAS;AAAA,IACX;AACA,SAAK,MAAM,KAAK,KAAK;AACrB,WAAO,MAAM;AAAA,EACf;AAAA,EAEA,MAAM,QAAQ,OAAuC;AAEnD,SAAK,MAAM,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS;AACnD,WAAO,KAAK,MAAM,MAAM,GAAG,KAAK;AAAA,EAClC;AAAA,EAEA,MAAM,OAAO,KAA8B;AACzC,SAAK,QAAQ,KAAK,MAAM,OAAO,CAAC,MAAM,CAAC,IAAI,SAAS,EAAE,EAAG,CAAC;AAAA,EAC5D;AAAA,EAEA,MAAM,cAAc,KAA8B;AAChD,UAAM,MAAM,KAAK,IAAI;AACrB,eAAW,SAAS,KAAK,OAAO;AAC9B,UAAI,IAAI,SAAS,MAAM,EAAG,GAAG;AAC3B,cAAM;AACN,cAAM,cAAc;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,WAA4D;AAChE,UAAM,OAAO,KAAK,MAAM;AACxB,UAAM,cACJ,OAAO,IACH,KAAK,IAAI,GAAG,KAAK,MAAM,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,IAC9C;AACN,WAAO,EAAE,MAAM,YAAY;AAAA,EAC7B;AAAA,EAEA,MAAM,QAAuB;AAC3B,SAAK,QAAQ,CAAC;AAAA,EAChB;AAAA,EAEA,MAAM,eAAe,SAAgC;AACnD,QAAI,KAAK,MAAM,SAAS,SAAS;AAE/B,WAAK,MAAM,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS;AACnD,WAAK,QAAQ,KAAK,MAAM,MAAM,CAAC,OAAO;AAAA,IACxC;AAAA,EACF;AAAA,EAEA,MAAM,0BAA0B,YAAuC;AACrE,WAAO,KAAK,MACT,OAAO,CAAC,MAAM,EAAE,WAAW,UAAU,EACrC,IAAI,CAAC,MAAM,EAAE,EAAG;AAAA,EACrB;AACF;AAMO,IAAM,aAAN,MAAgD;AAAA,EACpC;AAAA,EACA;AAAA,EACT,KAA4B;AAAA,EAC5B,cAAkC;AAAA,EAClC,aAAoD;AAAA,EACpD,aAAa;AAAA,EACb,aAAwC;AAAA,EAC/B;AAAA,EACT,YAAY;AAAA,EACZ,cAAkE;AAAA,EAE1E,YAAY,SAAwB,CAAC,GAAG,SAAS,kBAAkB;AACjE,SAAK,SAAS,EAAE,GAAG,gBAAgB,GAAG,OAAO;AAC7C,SAAK,SAAS;AACd,SAAK,eAAe,qBAAqB;AAEzC,QAAI,KAAK,cAAc;AACrB,WAAK,KAAK,IAAI,eAAe,KAAK,MAAM;AAAA,IAC1C,OAAO;AACL,WAAK,cAAc,IAAI,YAAY;AAAA,IACrC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,SAAkB,QAA0D;AACvF,SAAK,YAAY;AACjB,SAAK,cAAc,UAAU;AAAA,EAC/B;AAAA,EAEQ,IAAI,SAAiB,MAAsB;AACjD,QAAI,KAAK,aAAa,KAAK,aAAa;AACtC,WAAK,YAAY,SAAS,IAAI;AAAA,IAChC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgB,UAAoC;AAClD,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,cAAuB;AACrB,WAAO,KAAK,gBAAgB,KAAK,gBAAgB;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAyC;AACvC,WAAO,KAAK,eAAe,cAAc;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ,SAAoC;AAChD,UAAM,QAAiC;AAAA,MACrC;AAAA,MACA,WAAW,KAAK,IAAI;AAAA,MACpB,SAAS;AAAA,IACX;AAEA,QAAI,KAAK,gBAAgB,KAAK,IAAI;AAChC,YAAM,KAAK,GAAG,OAAO,IAAI,KAAoB;AAC7C,WAAK,IAAI,6BAA6B,OAAO;AAAA,IAC/C,WAAW,KAAK,aAAa;AAC3B,YAAM,KAAK,YAAY,QAAQ,OAAO;AACtC,WAAK,IAAI,0BAA0B,OAAO;AAAA,IAC5C;AAGA,UAAM,KAAK,oBAAoB;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ,OAAuC;AACnD,QAAI,KAAK,gBAAgB,KAAK,IAAI;AAEhC,aAAO,KAAK,GAAG,OACZ,MAAM,SAAS,EACf,MAAM,KAAK,OAAO,UAAU,EAC5B,OAAO,WAAW,EAClB,KAAK,CAAC,WAAW,OAAO,MAAM,GAAG,KAAK,CAAC;AAAA,IAC5C,WAAW,KAAK,aAAa;AAC3B,YAAM,SAAS,MAAM,KAAK,YAAY,QAAQ,KAAK;AACnD,aAAO,OAAO,OAAO,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,UAAU;AAAA,IAChE;AACA,WAAO,CAAC;AAAA,EACV;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAS,KAA8B;AAC3C,QAAI,IAAI,WAAW,EAAG;AAEtB,QAAI,KAAK,gBAAgB,KAAK,IAAI;AAChC,YAAM,KAAK,GAAG,OAAO,WAAW,GAAG;AACnC,WAAK,IAAI,WAAW,IAAI,MAAM,yBAAyB;AAAA,IACzD,WAAW,KAAK,aAAa;AAC3B,YAAM,KAAK,YAAY,OAAO,GAAG;AACjC,WAAK,IAAI,WAAW,IAAI,MAAM,gCAAgC;AAAA,IAChE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAW,KAA8B;AAC7C,QAAI,IAAI,WAAW,EAAG;AAEtB,UAAM,MAAM,KAAK,IAAI;AAErB,QAAI,KAAK,gBAAgB,KAAK,IAAI;AAChC,YAAM,KAAK,GAAG,YAAY,MAAM,KAAK,GAAG,QAAQ,YAAY;AAC1D,mBAAW,MAAM,KAAK;AACpB,gBAAM,QAAQ,MAAM,KAAK,GAAI,OAAO,IAAI,EAAE;AAC1C,cAAI,OAAO;AACT,kBAAM,KAAK,GAAI,OAAO,OAAO,IAAI;AAAA,cAC/B,SAAS,MAAM,UAAU;AAAA,cACzB,aAAa;AAAA,YACf,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF,CAAC;AACD,WAAK,IAAI,UAAU,IAAI,MAAM,mBAAmB;AAAA,IAClD,WAAW,KAAK,aAAa;AAC3B,YAAM,KAAK,YAAY,cAAc,GAAG;AACxC,WAAK,IAAI,UAAU,IAAI,MAAM,mCAAmC;AAAA,IAClE;AAGA,UAAM,KAAK,qBAAqB;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,uBAAsC;AAClD,QAAI,KAAK,gBAAgB,KAAK,IAAI;AAChC,YAAM,aAAa,MAAM,KAAK,GAAG,OAC9B,MAAM,SAAS,EACf,aAAa,KAAK,OAAO,UAAU,EACnC,YAAY;AACf,UAAI,WAAW,SAAS,GAAG;AACzB,cAAM,KAAK,GAAG,OAAO,WAAW,UAAU;AAC1C,aAAK,IAAI,WAAW,WAAW,MAAM,mCAAmC;AAAA,MAC1E;AAAA,IACF,WAAW,KAAK,aAAa;AAC3B,YAAM,aAAa,MAAM,KAAK,YAAY;AAAA,QACxC,KAAK,OAAO;AAAA,MACd;AACA,UAAI,WAAW,SAAS,GAAG;AACzB,cAAM,KAAK,YAAY,OAAO,UAAU;AACxC,aAAK,IAAI,WAAW,WAAW,MAAM,mCAAmC;AAAA,MAC1E;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,sBAAqC;AACjD,QAAI,KAAK,gBAAgB,KAAK,IAAI;AAChC,YAAM,QAAQ,MAAM,KAAK,GAAG,OAAO,MAAM;AACzC,UAAI,QAAQ,KAAK,OAAO,cAAc;AACpC,cAAM,SAAS,QAAQ,KAAK,OAAO;AACnC,cAAM,eAAe,MAAM,KAAK,GAAG,OAChC,QAAQ,WAAW,EACnB,MAAM,MAAM,EACZ,YAAY;AACf,cAAM,KAAK,GAAG,OAAO,WAAW,YAAY;AAC5C,aAAK,IAAI,WAAW,MAAM,4CAA4C;AAAA,MACxE;AAAA,IACF,WAAW,KAAK,aAAa;AAC3B,YAAM,KAAK,YAAY,eAAe,KAAK,OAAO,YAAY;AAAA,IAChE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAgC;AACpC,QAAI,KAAK,gBAAgB,KAAK,IAAI;AAChC,YAAM,OAAO,MAAM,KAAK,GAAG,OAAO,MAAM;AACxC,YAAM,cAAc,MAAM,KAAK,GAAG,OAC/B,QAAQ,WAAW,EACnB,MAAM,EACN,KAAK,CAAC,MAAM,GAAG,SAAS;AAC3B,aAAO,EAAE,MAAM,aAAa,YAAY,KAAK,WAAW;AAAA,IAC1D,WAAW,KAAK,aAAa;AAC3B,YAAM,QAAQ,MAAM,KAAK,YAAY,SAAS;AAC9C,aAAO,EAAE,GAAG,OAAO,YAAY,KAAK,WAAW;AAAA,IACjD;AACA,WAAO,EAAE,MAAM,GAAG,YAAY,KAAK,WAAW;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AAC3B,QAAI,KAAK,gBAAgB,KAAK,IAAI;AAChC,YAAM,KAAK,GAAG,OAAO,MAAM;AAC3B,WAAK,IAAI,2BAA2B;AAAA,IACtC,WAAW,KAAK,aAAa;AAC3B,YAAM,KAAK,YAAY,MAAM;AAC7B,WAAK,IAAI,wBAAwB;AAAA,IACnC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,kBAAwB;AACtB,QAAI,KAAK,WAAY;AAErB,SAAK,aAAa,YAAY,MAAM;AAClC,WAAK,MAAM,EAAE,MAAM,CAAC,QAAQ;AAC1B,aAAK,IAAI,qBAAqB,GAAG;AAAA,MACnC,CAAC;AAAA,IACH,GAAG,KAAK,OAAO,aAAa;AAG5B,QAAI,UAAU,GAAG;AACf,aAAO,iBAAiB,UAAU,KAAK,YAAY;AAAA,IACrD;AAEA,SAAK,IAAI,kCAAkC,KAAK,OAAO,aAAa,KAAK;AAAA,EAC3E;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAuB;AACrB,QAAI,KAAK,YAAY;AACnB,oBAAc,KAAK,UAAU;AAC7B,WAAK,aAAa;AAAA,IACpB;AAEA,QAAI,UAAU,GAAG;AACf,aAAO,oBAAoB,UAAU,KAAK,YAAY;AAAA,IACxD;AAEA,SAAK,IAAI,qBAAqB;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,MAAY;AACjC,SAAK,IAAI,uCAAuC;AAChD,SAAK,MAAM,EAAE,MAAM,CAAC,QAAQ;AAC1B,WAAK,IAAI,sBAAsB,GAAG;AAAA,IACpC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,WAAoB;AAC1B,QAAI,UAAU,GAAG;AACf,aAAO,UAAU;AAAA,IACnB;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AAC3B,QAAI,KAAK,YAAY;AACnB,WAAK,IAAI,qCAAqC;AAC9C;AAAA,IACF;AAEA,QAAI,CAAC,KAAK,SAAS,GAAG;AACpB,WAAK,IAAI,yBAAyB;AAClC;AAAA,IACF;AAEA,QAAI,CAAC,KAAK,YAAY;AACpB,WAAK,IAAI,6CAA6C;AACtD;AAAA,IACF;AAEA,SAAK,aAAa;AAElB,QAAI;AACF,YAAM,QAAQ,MAAM,KAAK,SAAS;AAClC,UAAI,MAAM,SAAS,GAAG;AACpB,aAAK,IAAI,kCAAkC;AAC3C;AAAA,MACF;AAEA,WAAK,IAAI,mBAAmB,MAAM,IAAI,UAAU;AAGhD,UAAI,YAAY;AAChB,aAAO,MAAM;AACX,cAAM,SAAS,MAAM,KAAK,QAAQ,KAAK,OAAO,cAAc;AAC5D,YAAI,OAAO,WAAW,EAAG;AAEzB,aAAK,IAAI,oBAAoB,OAAO,MAAM,SAAS;AAEnD,YAAI;AACF,gBAAM,UAAU,MAAM,KAAK,WAAW,MAAM;AAC5C,gBAAM,KAAK,SAAS,OAAO;AAG3B,gBAAM,YAAY,OACf,OAAO,CAAC,MAAM,CAAC,QAAQ,SAAS,EAAE,EAAG,CAAC,EACtC,IAAI,CAAC,MAAM,EAAE,EAAG;AACnB,cAAI,UAAU,SAAS,GAAG;AACxB,kBAAM,KAAK,WAAW,SAAS;AAAA,UACjC;AAEA,uBAAa,QAAQ;AAAA,QACvB,SAAS,OAAO;AAEd,gBAAM,SAAS,OAAO,IAAI,CAAC,MAAM,EAAE,EAAG;AACtC,gBAAM,KAAK,WAAW,MAAM;AAC5B,eAAK,IAAI,qBAAqB,KAAK;AACnC;AAAA,QACF;AAAA,MACF;AAEA,WAAK,IAAI,wBAAwB,SAAS,SAAS;AAAA,IACrD,UAAE;AACA,WAAK,aAAa;AAAA,IACpB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AAC3B,SAAK,eAAe;AACpB,QAAI,KAAK,IAAI;AACX,WAAK,GAAG,MAAM;AACd,WAAK,IAAI,4BAA4B;AAAA,IACvC;AAAA,EACF;AACF;;;AFtcA,IAAM,mBAAmB;AACzB,IAAM,UAAU;AAChB,IAAM,sBAAsB;AAC5B,IAAM,8BAA8B;AACpC,IAAM,0BAA0B,KAAK,KAAK;AAC1C,IAAM,oBAAoB;AAK1B,IAAM,gBAAN,MAAuC;AAAA,EAC7B,OAA4B,oBAAI,IAAI;AAAA,EAE5C,QAAQ,KAA4B;AAClC,WAAO,KAAK,KAAK,IAAI,GAAG,KAAK;AAAA,EAC/B;AAAA,EAEA,QAAQ,KAAa,OAAqB;AACxC,SAAK,KAAK,IAAI,KAAK,KAAK;AAAA,EAC1B;AAAA,EAEA,WAAW,KAAmB;AAC5B,SAAK,KAAK,OAAO,GAAG;AAAA,EACtB;AACF;AAKA,SAAS,oBAA6B;AACpC,MAAI,OAAO,WAAW,eAAe,OAAO,cAAc;AACxD,WAAO,OAAO;AAAA,EAChB;AACA,SAAO,IAAI,cAAc;AAC3B;AA+CO,IAAM,UAAN,MAAc;AAAA,EACF;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACT,cAA6B;AAAA;AAAA,EAG7B,kBAAwB,CAAC;AAAA;AAAA,EAGzB,cAAmC,oBAAI,IAAI;AAAA;AAAA,EAG3C;AAAA;AAAA,EAGA,QAA2B;AAAA,EAC3B;AAAA;AAAA,EAGA,UAA0B;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAwB;AAAA,EACxB,sBAAsB;AAAA,EAE9B,YAAY,QAAuB;AACjC,QAAI,CAAC,OAAO,OAAO;AACjB,YAAM,IAAI,gBAAgB,yBAAyB,OAAO;AAAA,IAC5D;AAEA,SAAK,QAAQ,OAAO;AACpB,SAAK,UAAU,OAAO,WAAW;AACjC,SAAK,aAAa,OAAO,cAAc;AACvC,SAAK,YAAY,OAAO,SAAS;AAGjC,QAAI,OAAO,YAAY,MAAM;AAC3B,WAAK,UAAU;AAAA,IACjB,OAAO;AACL,WAAK,UAAU,OAAO,WAAW,kBAAkB;AAAA,IACrD;AAGA,SAAK,sBAAsB;AAG3B,SAAK,iBAAiB,OAAO,WAAW,kBAAkB;AAC1D,SAAK,oBAAoB,OAAO,WAAW,qBAAqB;AAChE,SAAK,mBAAmB,OAAO,WAAW,qBAAqB;AAC/D,SAAK,qBAAqB,OAAO,WAAW,sBAAsB;AAGlE,QAAI,KAAK,kBAAkB;AACzB,WAAK,YAAY;AACjB,WAAK,oBAAoB;AAGzB,UAAI,KAAK,sBAAsB,OAAO,WAAW,aAAa;AAC5D,aAAK,oBAAoB;AAAA,MAC3B;AAAA,IACF;AAGA,SAAK,iBAAiB,OAAO,SAAS,WAAW;AACjD,QAAI,KAAK,gBAAgB;AACvB,WAAK,QAAQ,IAAI,WAAW,OAAO,OAAO;AAC1C,WAAK,MAAM,aAAa,KAAK,WAAW,KAAK,IAAI,KAAK,IAAI,CAAC;AAC3D,WAAK,MAAM,gBAAgB,KAAK,iBAAiB,KAAK,IAAI,CAAC;AAC3D,WAAK,MAAM,gBAAgB;AAC3B,WAAK,IAAI,4BAA4B;AAAA,QACnC,aAAa,KAAK,MAAM,eAAe;AAAA,MACzC,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,wBAA8B;AACpC,QAAI,KAAK,SAAS;AAChB,YAAM,SAAS,KAAK,QAAQ,QAAQ,KAAK,UAAU;AACnD,UAAI,QAAQ;AACV,aAAK,cAAc;AACnB;AAAA,MACF;AAAA,IACF;AAGA,SAAK,kBAAc,YAAAC,IAAO;AAG1B,QAAI,KAAK,WAAW,KAAK,aAAa;AACpC,WAAK,QAAQ,QAAQ,KAAK,YAAY,KAAK,WAAW;AAAA,IACxD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAgC;AAC9B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,aAAa,SAAwB;AACnC,SAAK,YAAY;AACjB,QAAI,KAAK,OAAO;AACd,WAAK,MAAM,aAAa,SAAS,KAAK,IAAI,KAAK,IAAI,CAAC;AAAA,IACtD;AACA,SAAK,IAAI,cAAc,UAAU,YAAY,UAAU,EAAE;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA,EAKA,cAAuB;AACrB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKQ,IAAI,SAAiB,MAAsB;AACjD,QAAI,CAAC,KAAK,UAAW;AAErB,UAAM,SAAS;AACf,QAAI,SAAS,QAAW;AACtB,cAAQ,IAAI,QAAQ,SAAS,IAAI;AAAA,IACnC,OAAO;AACL,cAAQ,IAAI,QAAQ,OAAO;AAAA,IAC7B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,SAAS,YAAwB;AAC/B,SAAK,kBAAkB,EAAE,GAAG,KAAK,iBAAiB,GAAG,WAAW;AAChE,SAAK,IAAI,+BAA+B,UAAU;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,aAAa,YAAwB;AACnC,UAAM,WAAiB,CAAC;AACxB,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,UAAU,GAAG;AACrD,UAAI,EAAE,OAAO,KAAK,kBAAkB;AAClC,iBAAS,GAAG,IAAI;AAAA,MAClB;AAAA,IACF;AACA,QAAI,OAAO,KAAK,QAAQ,EAAE,SAAS,GAAG;AACpC,WAAK,kBAAkB,EAAE,GAAG,KAAK,iBAAiB,GAAG,SAAS;AAC9D,WAAK,IAAI,sCAAsC,QAAQ;AAAA,IACzD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,WAAW,KAAmB;AAC5B,QAAI,OAAO,KAAK,iBAAiB;AAC/B,aAAO,KAAK,gBAAgB,GAAG;AAC/B,WAAK,IAAI,0BAA0B,EAAE,IAAI,CAAC;AAAA,IAC5C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,qBAA2B;AACzB,WAAO,EAAE,GAAG,KAAK,gBAAgB;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,uBAA6B;AAC3B,SAAK,kBAAkB,CAAC;AACxB,SAAK,IAAI,0BAA0B;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBA,UAAU,WAAyB;AACjC,SAAK,YAAY,IAAI,WAAW,KAAK,IAAI,CAAC;AAC1C,SAAK,IAAI,iBAAiB,EAAE,OAAO,UAAU,CAAC;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,gBAAgB,WAAyB;AACvC,QAAI,KAAK,YAAY,IAAI,SAAS,GAAG;AACnC,WAAK,YAAY,OAAO,SAAS;AACjC,WAAK,IAAI,mBAAmB,EAAE,OAAO,UAAU,CAAC;AAAA,IAClD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,iBAA2B;AACzB,WAAO,MAAM,KAAK,KAAK,YAAY,KAAK,CAAC;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,iBAAiB,WAAkC;AACjD,UAAM,YAAY,KAAK,YAAY,IAAI,SAAS;AAChD,QAAI,cAAc,OAAW,QAAO;AACpC,YAAQ,KAAK,IAAI,IAAI,aAAa;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAM,gBAA4C;AAChD,QAAI,CAAC,KAAK,MAAO,QAAO;AACxB,WAAO,KAAK,MAAM,SAAS;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,aAA4B;AAChC,QAAI,CAAC,KAAK,MAAO;AACjB,UAAM,KAAK,MAAM,MAAM;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,aAA4B;AAChC,QAAI,CAAC,KAAK,MAAO;AACjB,UAAM,KAAK,MAAM,MAAM;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,iBAAiB,QAA0C;AACvE,UAAM,UAAoB,CAAC;AAE3B,eAAW,SAAS,QAAQ;AAC1B,UAAI;AACF,cAAM,KAAK,YAA2B,gBAAgB,MAAM,OAAO;AACnE,gBAAQ,KAAK,MAAM,EAAG;AAAA,MACxB,SAAS,OAAO;AACd,aAAK,IAAI,+BAA+B,EAAE,IAAI,MAAM,IAAI,MAAM,CAAC;AAAA,MAEjE;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAM,MAAM,SAA+C;AACzD,SAAK,qBAAqB,OAAO;AAGjC,QAAI;AACJ,UAAM,YAAY,KAAK,YAAY,IAAI,QAAQ,KAAK;AACpD,QAAI,cAAc,QAAW;AAC3B,kBAAY,KAAK,IAAI,IAAI,aAAa;AACtC,WAAK,YAAY,OAAO,QAAQ,KAAK;AACrC,WAAK,IAAI,iBAAiB,EAAE,OAAO,QAAQ,OAAO,SAAS,CAAC;AAAA,IAC9D;AAGA,UAAM,qBAAqB,QAAQ,uBAAuB;AAG1D,UAAM,aAAmB;AAAA,MACvB,GAAG,KAAK;AAAA,MACR,GAAI,QAAQ,QAAQ,CAAC;AAAA,MACrB,GAAI,aAAa,SAAY,EAAE,WAAW,SAAS,IAAI,CAAC;AAAA,IAC1D;AAEA,UAAM,UAAsB;AAAA,MAC1B,SAAS,QAAQ;AAAA,MACjB,OAAO,QAAQ;AAAA,MACf,WAAW,KAAK,IAAI;AAAA,MACpB,GAAI,QAAQ,WAAW,EAAE,SAAS,QAAQ,QAAQ;AAAA,MAClD,GAAI,sBAAsB,KAAK,eAAe,EAAE,cAAc,KAAK,YAAY;AAAA,MAC/E,GAAI,QAAQ,QAAQ,EAAE,MAAM,QAAQ,KAAK;AAAA,MACzC,GAAI,QAAQ,WAAW,UAAa,EAAE,QAAQ,QAAQ,OAAO;AAAA,MAC7D,GAAI,QAAQ,eAAe,EAAE,aAAa,QAAQ,YAAY;AAAA,MAC9D,GAAI,OAAO,KAAK,UAAU,EAAE,SAAS,KAAK,EAAE,MAAM,WAAW;AAAA,IAC/D;AAEA,SAAK,IAAI,SAAS,EAAE,OAAO,QAAQ,OAAO,QAAQ,CAAC;AAGnD,QAAI,KAAK,OAAO;AAEd,YAAM,KAAK,MAAM,QAAQ,OAAO;AAChC,WAAK,IAAI,0BAA0B;AAGnC,WAAK,MAAM,MAAM,EAAE,MAAM,CAAC,QAAQ;AAChC,aAAK,IAAI,2BAA2B,GAAG;AAAA,MACzC,CAAC;AAGD,aAAO;AAAA,QACL,IAAI,UAAU,KAAK,IAAI,CAAC;AAAA,QACxB,OAAO,QAAQ;AAAA,QACf,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC;AAAA,IACF;AAGA,UAAM,WAAW,MAAM,KAAK,YAA2B,gBAAgB,OAAO;AAC9E,SAAK,IAAI,2BAA2B,EAAE,IAAI,SAAS,GAAG,CAAC;AACvD,WAAO;AAAA,EACT;AAAA,EAEQ,qBAAqB,SAA6B;AACxD,QAAI,CAAC,QAAQ,OAAO;AAClB,YAAM,IAAI,gBAAgB,qBAAqB,OAAO;AAAA,IACxD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,YAAe,UAAkB,MAA2B;AACxE,UAAM,MAAM,GAAG,KAAK,OAAO,GAAG,QAAQ;AACtC,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,OAAO;AAE9D,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,KAAK;AAAA,QAChC,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,aAAa,GAAG,KAAK,KAAK;AAAA,QAC5B;AAAA,QACA,MAAM,KAAK,UAAU,IAAI;AAAA,QACzB,QAAQ,WAAW;AAAA,MACrB,CAAC;AAED,mBAAa,SAAS;AAEtB,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,YAAY,MAAM,KAAK,kBAAkB,QAAQ;AAEvD,YAAI,SAAS,WAAW,KAAK;AAC3B,gBAAM,IAAI,oBAAoB;AAAA,QAChC;AAEA,cAAM,IAAI;AAAA,UACR,KAAK,gBAAgB,WAAW,SAAS,UAAU;AAAA,UACnD,SAAS;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAEA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B,SAAS,OAAO;AACd,mBAAa,SAAS;AAEtB,UAAI,iBAAiB,SAAS,MAAM,SAAS,cAAc;AACzD,cAAM,IAAI,aAAa;AAAA,MACzB;AAEA,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAc,kBAAkB,UAAsC;AACpE,QAAI;AACF,aAAO,MAAM,SAAS,KAAK;AAAA,IAC7B,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEQ,gBAAgB,MAAe,UAA0B;AAC/D,QAAI,QAAQ,OAAO,SAAS,YAAY,aAAa,MAAM;AACzD,aAAO,OAAQ,KAA8B,OAAO;AAAA,IACtD;AACA,QAAI,QAAQ,OAAO,SAAS,YAAY,WAAW,MAAM;AACvD,aAAO,OAAQ,KAA4B,KAAK;AAAA,IAClD;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,cAAoB;AAC1B,QAAI,CAAC,KAAK,QAAS;AAEnB,QAAI;AACF,YAAM,SAAS,KAAK,QAAQ,QAAQ,KAAK,iBAAiB;AAC1D,UAAI,QAAQ;AACV,cAAM,UAAU,KAAK,MAAM,MAAM;AACjC,cAAM,MAAM,KAAK,IAAI;AAGrB,YAAI,MAAM,QAAQ,iBAAiB,KAAK,gBAAgB;AACtD,eAAK,UAAU;AACf,eAAK,IAAI,oBAAoB,EAAE,WAAW,QAAQ,GAAG,CAAC;AAAA,QACxD,OAAO;AAEL,eAAK,QAAQ,WAAW,KAAK,iBAAiB;AAC9C,eAAK,IAAI,uCAAuC;AAAA,QAClD;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,WAAK,IAAI,uCAAuC,KAAK;AAAA,IACvD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAoB;AAC1B,QAAI,CAAC,KAAK,WAAW,CAAC,KAAK,QAAS;AAEpC,QAAI;AACF,WAAK,QAAQ,QAAQ,KAAK,mBAAmB,KAAK,UAAU,KAAK,OAAO,CAAC;AAAA,IAC3E,SAAS,OAAO;AACd,WAAK,IAAI,qCAAqC,KAAK;AAAA,IACrD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,qBAA8B;AACpC,UAAM,MAAM,KAAK,IAAI;AAGrB,QAAI,KAAK,WAAY,MAAM,KAAK,QAAQ,iBAAkB,KAAK,gBAAgB;AAC7E,WAAK,WAAW;AAChB,WAAK,UAAU;AAAA,IACjB;AAGA,QAAI,CAAC,KAAK,SAAS;AACjB,YAAM,WAAW,OAAO,aAAa,cAAc,SAAS,WAAW;AACvE,YAAM,OAAO,OAAO,WAAW,cAAc,OAAO,SAAS,WAAW;AAExE,WAAK,UAAU;AAAA,QACb,QAAI,YAAAA,IAAO;AAAA,QACX,WAAW;AAAA,QACX,gBAAgB;AAAA,QAChB,iBAAiB;AAAA,QACjB,WAAW;AAAA,QACX,eAAe;AAAA,MACjB;AAEA,WAAK,IAAI,uBAAuB,EAAE,WAAW,KAAK,QAAQ,GAAG,CAAC;AAC9D,WAAK,kBAAkB;AAAA,IACzB;AAGA,SAAK,QAAQ,iBAAiB;AAC9B,SAAK,YAAY;AAEjB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,eAA8B;AAC5B,WAAO,KAAK,SAAS,MAAM;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKA,aAA6B;AAC3B,WAAO,KAAK,UAAU,EAAE,GAAG,KAAK,QAAQ,IAAI;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAKQ,oBAA0B;AAChC,QAAI,CAAC,KAAK,QAAS;AAEnB,UAAM,YAAY,KAAK,aAAa;AAEpC,SAAK,MAAM;AAAA,MACT,SAAS;AAAA,MACT,OAAO;AAAA,MACP,MAAM;AAAA,QACJ,cAAc,KAAK,QAAQ;AAAA,QAC3B,cAAc,KAAK,QAAQ,aAAa;AAAA,QACxC,YAAY,KAAK,QAAQ,iBAAiB;AAAA,QAC1C,GAAG;AAAA,MACL;AAAA,IACF,CAAC,EAAE,MAAM,CAAC,QAAQ,KAAK,IAAI,iCAAiC,GAAG,CAAC;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAmB;AACzB,QAAI,CAAC,KAAK,QAAS;AAEnB,SAAK,IAAI,iBAAiB,EAAE,WAAW,KAAK,QAAQ,GAAG,CAAC;AAGxD,QAAI,KAAK,SAAS;AAChB,WAAK,QAAQ,WAAW,KAAK,iBAAiB;AAAA,IAChD;AACA,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA,EAKQ,sBAA4B;AAClC,QAAI,OAAO,WAAW,eAAe,KAAK,oBAAqB;AAG/D,aAAS,iBAAiB,oBAAoB,MAAM;AAClD,UAAI,SAAS,oBAAoB,UAAU;AACzC,aAAK,YAAY;AACjB,aAAK,IAAI,kCAAkC;AAAA,MAC7C;AAAA,IACF,CAAC;AAGD,WAAO,iBAAiB,YAAY,MAAM;AACxC,WAAK,WAAW;AAChB,WAAK,IAAI,uCAAuC;AAAA,IAClD,CAAC;AAED,SAAK,sBAAsB;AAC3B,SAAK,IAAI,mCAAmC;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAqB;AAC3B,QAAI,OAAO,WAAW,YAAa,QAAO,CAAC;AAE3C,UAAM,SAAS,IAAI,gBAAgB,OAAO,SAAS,MAAM;AACzD,UAAM,YAAkB,CAAC;AAEzB,UAAM,UAAU,CAAC,cAAc,cAAc,gBAAgB,YAAY,aAAa;AACtF,eAAW,OAAO,SAAS;AACzB,YAAM,QAAQ,OAAO,IAAI,GAAG;AAC5B,UAAI,OAAO;AACT,kBAAU,KAAK,GAAG,EAAE,IAAI;AAAA,MAC1B;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAM,cAAc,UAA2B,CAAC,GAA2B;AACzE,UAAM,UAAU,KAAK,mBAAmB;AACxC,YAAQ;AACR,SAAK,YAAY;AAEjB,UAAM,OAAO,QAAQ,SAAS,OAAO,WAAW,cAAc,OAAO,SAAS,WAAW;AACzF,UAAM,QAAQ,QAAQ,UAAU,OAAO,aAAa,cAAc,SAAS,QAAQ;AACnF,UAAM,WAAW,QAAQ,aAAa,OAAO,aAAa,cAAc,SAAS,WAAW;AAE5F,WAAO,KAAK,MAAM;AAAA,MAChB,SAAS;AAAA,MACT,OAAO;AAAA,MACP,MAAM;AAAA,QACJ,cAAc,QAAQ;AAAA,QACtB,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,GAAG,KAAK,aAAa;AAAA,QACrB,GAAI,QAAQ,QAAQ,CAAC;AAAA,MACvB;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,sBAA4B;AAC1B,QAAI,OAAO,WAAW,aAAa;AACjC,WAAK,IAAI,0DAA0D;AACnE;AAAA,IACF;AAGA,SAAK,cAAc,EAAE,MAAM,CAAC,QAAQ,KAAK,IAAI,qCAAqC,GAAG,CAAC;AAGtF,UAAM,oBAAoB,QAAQ,UAAU,KAAK,OAAO;AACxD,YAAQ,YAAY,IAAI,SAAS;AAC/B,wBAAkB,GAAG,IAAI;AACzB,WAAK,cAAc,EAAE,MAAM,CAAC,QAAQ,KAAK,IAAI,yCAAyC,GAAG,CAAC;AAAA,IAC5F;AAGA,UAAM,uBAAuB,QAAQ,aAAa,KAAK,OAAO;AAC9D,YAAQ,eAAe,IAAI,SAAS;AAClC,2BAAqB,GAAG,IAAI;AAAA,IAE9B;AAGA,WAAO,iBAAiB,YAAY,MAAM;AACxC,WAAK,cAAc,EAAE,MAAM,CAAC,QAAQ,KAAK,IAAI,wCAAwC,GAAG,CAAC;AAAA,IAC3F,CAAC;AAED,SAAK,IAAI,iCAAiC;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,MAAM,aAAa,SAAiD;AAClE,UAAM,UAAU,KAAK,mBAAmB;AAExC,WAAO,KAAK,MAAM;AAAA,MAChB,SAAS;AAAA,MACT,OAAO;AAAA,MACP,SAAS,QAAQ,WAAW,KAAK,UAAU;AAAA,MAC3C,MAAM;AAAA,QACJ,cAAc,QAAQ;AAAA,QACtB,WAAW,QAAQ;AAAA,QACnB,YAAY,QAAQ,YAAY;AAAA,QAChC,GAAI,QAAQ,QAAQ,CAAC;AAAA,MACvB;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,SAAS,SAAgC;AACvC,SAAK,SAAS,QAAQ;AAGtB,QAAI,QAAQ,QAAQ;AAClB,WAAK,SAAS;AAAA,QACZ,WAAW,QAAQ;AAAA,QACnB,GAAG,QAAQ;AAAA,MACb,CAAC;AAAA,IACH,OAAO;AACL,WAAK,SAAS,EAAE,WAAW,QAAQ,OAAO,CAAC;AAAA,IAC7C;AAGA,SAAK,MAAM;AAAA,MACT,SAAS;AAAA,MACT,OAAO;AAAA,MACP,SAAS,QAAQ;AAAA,MACjB,MAAM;AAAA,QACJ,cAAc,KAAK,SAAS,MAAM;AAAA,QAClC,gBAAgB,KAAK,eAAe;AAAA,QACpC,GAAI,QAAQ,UAAU,CAAC;AAAA,MACzB;AAAA,IACF,CAAC,EAAE,MAAM,CAAC,QAAQ,KAAK,IAAI,4BAA4B,GAAG,CAAC;AAE3D,SAAK,IAAI,mBAAmB,EAAE,QAAQ,QAAQ,OAAO,CAAC;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA,EAKA,YAA2B;AACzB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,QAAc;AAEZ,QAAI,KAAK,SAAS;AAChB,WAAK,WAAW;AAChB,WAAK,UAAU;AAAA,IACjB;AAGA,SAAK,SAAS;AAGd,SAAK,kBAAc,YAAAA,IAAO;AAC1B,QAAI,KAAK,SAAS;AAChB,WAAK,QAAQ,QAAQ,KAAK,YAAY,KAAK,WAAW;AAAA,IACxD;AAGA,SAAK,qBAAqB;AAE1B,SAAK,IAAI,qBAAqB;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,WAA0B;AAC9B,SAAK,IAAI,eAAe;AAGxB,QAAI,KAAK,OAAO;AACd,YAAM,KAAK,MAAM,MAAM;AACvB,YAAM,KAAK,MAAM,MAAM;AACvB,WAAK,QAAQ;AAAA,IACf;AAGA,SAAK,YAAY,MAAM;AAEvB,SAAK,IAAI,mBAAmB;AAAA,EAC9B;AACF;","names":["Dexie","uuidv4"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/errors.ts","../src/queue/index.ts"],"sourcesContent":["// Main client\nexport { Kitbase } from './client.js';\n\n// Types\nexport type {\n KitbaseConfig,\n TrackOptions,\n TrackResponse,\n Tags,\n TagValue,\n Storage,\n OfflineConfig,\n // Analytics types\n Session,\n AnalyticsConfig,\n PageViewOptions,\n RevenueOptions,\n IdentifyOptions,\n} from './types.js';\n\n// Queue types\nexport type { QueuedEvent, QueueStats } from './queue/types.js';\n\n// Errors\nexport {\n KitbaseError,\n ApiError,\n AuthenticationError,\n ValidationError,\n TimeoutError,\n} from './errors.js';\n","import { v4 as uuidv4 } from 'uuid';\nimport type {\n KitbaseConfig,\n TrackOptions,\n TrackResponse,\n LogPayload,\n Storage,\n Tags,\n Session,\n AnalyticsConfig,\n PageViewOptions,\n RevenueOptions,\n IdentifyOptions,\n} from './types.js';\nimport {\n ApiError,\n AuthenticationError,\n TimeoutError,\n ValidationError,\n} from './errors.js';\nimport { EventQueue } from './queue/index.js';\nimport type { QueuedEvent, QueueStats } from './queue/types.js';\n\nconst DEFAULT_BASE_URL = 'https://api.kitbase.dev';\nconst TIMEOUT = 30000;\nconst DEFAULT_STORAGE_KEY = 'kitbase_anonymous_id';\nconst DEFAULT_SESSION_STORAGE_KEY = 'kitbase_session';\nconst DEFAULT_SESSION_TIMEOUT = 30 * 60 * 1000; // 30 minutes\nconst ANALYTICS_CHANNEL = '__analytics';\n\n/**\n * In-memory storage fallback for non-browser environments\n */\nclass MemoryStorage implements Storage {\n private data: Map<string, string> = new Map();\n\n getItem(key: string): string | null {\n return this.data.get(key) ?? null;\n }\n\n setItem(key: string, value: string): void {\n this.data.set(key, value);\n }\n\n removeItem(key: string): void {\n this.data.delete(key);\n }\n}\n\n/**\n * Get the default storage (localStorage in browser, memory otherwise)\n */\nfunction getDefaultStorage(): Storage {\n if (typeof window !== 'undefined' && window.localStorage) {\n return window.localStorage;\n }\n return new MemoryStorage();\n}\n\n/**\n * Kitbase client for tracking events\n *\n * @example\n * ```typescript\n * import { Kitbase } from '@kitbase/sdk/events';\n *\n * const kitbase = new Kitbase({\n * token: '<YOUR_API_KEY>',\n * debug: true,\n * offline: { enabled: true },\n * });\n *\n * // Register super properties (included in all events)\n * kitbase.register({ app_version: '2.1.0', platform: 'web' });\n *\n * // Track anonymous events (anonymous_id is automatically included)\n * await kitbase.track({\n * channel: 'payments',\n * event: 'Page Viewed',\n * icon: '👀',\n * });\n *\n * // Time events for duration tracking\n * kitbase.timeEvent('Video Watched');\n * // ... later\n * await kitbase.track({\n * channel: 'engagement',\n * event: 'Video Watched', // $duration automatically included\n * });\n *\n * // Track events for a logged-in user (just pass user_id)\n * await kitbase.track({\n * channel: 'payments',\n * event: 'New Subscription',\n * user_id: 'user-123',\n * icon: '💰',\n * notify: true,\n * tags: {\n * plan: 'premium',\n * cycle: 'monthly',\n * },\n * });\n * ```\n */\nexport class Kitbase {\n private readonly token: string;\n private readonly baseUrl: string;\n private readonly storage: Storage | null;\n private readonly storageKey: string;\n private anonymousId: string | null = null;\n\n // Super properties (memory-only, merged into all events)\n private superProperties: Tags = {};\n\n // Time event tracking\n private timedEvents: Map<string, number> = new Map();\n\n // Debug mode\n private debugMode: boolean;\n\n // Offline queue\n private queue: EventQueue | null = null;\n private offlineEnabled: boolean;\n\n // Analytics & Session tracking\n private session: Session | null = null;\n private sessionTimeout: number;\n private sessionStorageKey: string;\n private analyticsEnabled: boolean;\n private autoTrackPageViews: boolean;\n private userId: string | null = null;\n private unloadListenerAdded = false;\n\n constructor(config: KitbaseConfig) {\n if (!config.token) {\n throw new ValidationError('API token is required', 'token');\n }\n\n this.token = config.token;\n // Remove trailing slashes to prevent double-slash in URLs\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n this.storageKey = config.storageKey ?? DEFAULT_STORAGE_KEY;\n this.debugMode = config.debug ?? false;\n\n // Initialize storage (null means disabled)\n if (config.storage === null) {\n this.storage = null;\n } else {\n this.storage = config.storage ?? getDefaultStorage();\n }\n\n // Load or generate anonymous ID\n this.initializeAnonymousId();\n\n // Initialize analytics configuration\n this.sessionTimeout = config.analytics?.sessionTimeout ?? DEFAULT_SESSION_TIMEOUT;\n this.sessionStorageKey = config.analytics?.sessionStorageKey ?? DEFAULT_SESSION_STORAGE_KEY;\n this.analyticsEnabled = config.analytics?.autoTrackSessions ?? true;\n this.autoTrackPageViews = config.analytics?.autoTrackPageViews ?? false;\n\n // Load existing session from storage\n if (this.analyticsEnabled) {\n this.loadSession();\n this.setupUnloadListener();\n\n // Auto-track pageviews if enabled\n if (this.autoTrackPageViews && typeof window !== 'undefined') {\n this.enableAutoPageViews();\n }\n }\n\n // Initialize offline queue if enabled\n this.offlineEnabled = config.offline?.enabled ?? false;\n if (this.offlineEnabled) {\n this.queue = new EventQueue(config.offline);\n this.queue.setDebugMode(this.debugMode, this.log.bind(this));\n this.queue.setSendCallback(this.sendQueuedEvents.bind(this));\n this.queue.startFlushTimer();\n this.log('Offline queueing enabled', {\n storageType: this.queue.getStorageType(),\n });\n }\n }\n\n /**\n * Initialize the anonymous ID from storage or generate a new one\n */\n private initializeAnonymousId(): void {\n if (this.storage) {\n const stored = this.storage.getItem(this.storageKey);\n if (stored) {\n this.anonymousId = stored;\n return;\n }\n }\n\n // Generate new anonymous ID\n this.anonymousId = uuidv4();\n\n // Persist if storage is available\n if (this.storage && this.anonymousId) {\n this.storage.setItem(this.storageKey, this.anonymousId);\n }\n }\n\n /**\n * Get the current anonymous ID\n */\n getAnonymousId(): string | null {\n return this.anonymousId;\n }\n\n // ============================================================\n // Debug Mode\n // ============================================================\n\n /**\n * Enable or disable debug mode\n * When enabled, all SDK operations are logged to the console\n *\n * @param enabled - Whether to enable debug mode\n *\n * @example\n * ```typescript\n * kitbase.setDebugMode(true);\n * // All events and operations will now be logged\n * ```\n */\n setDebugMode(enabled: boolean): void {\n this.debugMode = enabled;\n if (this.queue) {\n this.queue.setDebugMode(enabled, this.log.bind(this));\n }\n this.log(`Debug mode ${enabled ? 'enabled' : 'disabled'}`);\n }\n\n /**\n * Check if debug mode is enabled\n */\n isDebugMode(): boolean {\n return this.debugMode;\n }\n\n /**\n * Internal logging function\n */\n private log(message: string, data?: unknown): void {\n if (!this.debugMode) return;\n\n const prefix = '[Kitbase]';\n if (data !== undefined) {\n console.log(prefix, message, data);\n } else {\n console.log(prefix, message);\n }\n }\n\n // ============================================================\n // Super Properties\n // ============================================================\n\n /**\n * Register super properties that will be included with every event\n * These properties are stored in memory only and reset on page reload\n *\n * @param properties - Properties to register\n *\n * @example\n * ```typescript\n * kitbase.register({\n * app_version: '2.1.0',\n * platform: 'web',\n * environment: 'production',\n * });\n * ```\n */\n register(properties: Tags): void {\n this.superProperties = { ...this.superProperties, ...properties };\n this.log('Super properties registered', properties);\n }\n\n /**\n * Register super properties only if they haven't been set yet\n * Useful for setting default values that shouldn't override existing ones\n *\n * @param properties - Properties to register if not already set\n *\n * @example\n * ```typescript\n * kitbase.registerOnce({ first_visit: new Date().toISOString() });\n * ```\n */\n registerOnce(properties: Tags): void {\n const newProps: Tags = {};\n for (const [key, value] of Object.entries(properties)) {\n if (!(key in this.superProperties)) {\n newProps[key] = value;\n }\n }\n if (Object.keys(newProps).length > 0) {\n this.superProperties = { ...this.superProperties, ...newProps };\n this.log('Super properties registered (once)', newProps);\n }\n }\n\n /**\n * Remove a super property\n *\n * @param key - The property key to remove\n *\n * @example\n * ```typescript\n * kitbase.unregister('platform');\n * ```\n */\n unregister(key: string): void {\n if (key in this.superProperties) {\n delete this.superProperties[key];\n this.log('Super property removed', { key });\n }\n }\n\n /**\n * Get all registered super properties\n *\n * @returns A copy of the current super properties\n *\n * @example\n * ```typescript\n * const props = kitbase.getSuperProperties();\n * console.log(props); // { app_version: '2.1.0', platform: 'web' }\n * ```\n */\n getSuperProperties(): Tags {\n return { ...this.superProperties };\n }\n\n /**\n * Clear all super properties\n *\n * @example\n * ```typescript\n * kitbase.clearSuperProperties();\n * ```\n */\n clearSuperProperties(): void {\n this.superProperties = {};\n this.log('Super properties cleared');\n }\n\n // ============================================================\n // Time Events (Duration Tracking)\n // ============================================================\n\n /**\n * Start timing an event\n * When the same event is tracked later, a $duration property (in seconds)\n * will automatically be included\n *\n * @param eventName - The name of the event to time\n *\n * @example\n * ```typescript\n * kitbase.timeEvent('Video Watched');\n * // ... user watches video ...\n * await kitbase.track({\n * channel: 'engagement',\n * event: 'Video Watched',\n * tags: { video_id: '123' }\n * });\n * // Event will include $duration: 45.2 (seconds)\n * ```\n */\n timeEvent(eventName: string): void {\n this.timedEvents.set(eventName, Date.now());\n this.log('Timer started', { event: eventName });\n }\n\n /**\n * Cancel a timed event without tracking it\n *\n * @param eventName - The name of the event to cancel timing for\n *\n * @example\n * ```typescript\n * kitbase.timeEvent('Checkout Flow');\n * // User abandons checkout\n * kitbase.cancelTimeEvent('Checkout Flow');\n * ```\n */\n cancelTimeEvent(eventName: string): void {\n if (this.timedEvents.has(eventName)) {\n this.timedEvents.delete(eventName);\n this.log('Timer cancelled', { event: eventName });\n }\n }\n\n /**\n * Get all currently timed events\n *\n * @returns Array of event names that are currently being timed\n *\n * @example\n * ```typescript\n * const timedEvents = kitbase.getTimedEvents();\n * console.log(timedEvents); // ['Video Watched', 'Checkout Flow']\n * ```\n */\n getTimedEvents(): string[] {\n return Array.from(this.timedEvents.keys());\n }\n\n /**\n * Get the duration of a timed event (without stopping it)\n *\n * @param eventName - The name of the event\n * @returns Duration in seconds, or null if not being timed\n */\n getEventDuration(eventName: string): number | null {\n const startTime = this.timedEvents.get(eventName);\n if (startTime === undefined) return null;\n return (Date.now() - startTime) / 1000;\n }\n\n // ============================================================\n // Offline Queue\n // ============================================================\n\n /**\n * Get offline queue statistics\n *\n * @returns Queue statistics including size and flush status\n *\n * @example\n * ```typescript\n * const stats = await kitbase.getQueueStats();\n * console.log(stats); // { size: 5, isFlushing: false }\n * ```\n */\n async getQueueStats(): Promise<QueueStats | null> {\n if (!this.queue) return null;\n return this.queue.getStats();\n }\n\n /**\n * Manually flush the offline queue\n * Events are automatically flushed on interval and when coming back online,\n * but this method can be used to trigger an immediate flush\n *\n * @example\n * ```typescript\n * await kitbase.flushQueue();\n * ```\n */\n async flushQueue(): Promise<void> {\n if (!this.queue) return;\n await this.queue.flush();\n }\n\n /**\n * Clear all events from the offline queue\n *\n * @example\n * ```typescript\n * await kitbase.clearQueue();\n * ```\n */\n async clearQueue(): Promise<void> {\n if (!this.queue) return;\n await this.queue.clear();\n }\n\n /**\n * Callback for the queue to send batched events\n */\n private async sendQueuedEvents(events: QueuedEvent[]): Promise<number[]> {\n const sentIds: number[] = [];\n\n for (const event of events) {\n try {\n await this.sendRequest<TrackResponse>('/sdk/v1/logs', event.payload);\n sentIds.push(event.id!);\n } catch (error) {\n this.log('Failed to send queued event', { id: event.id, error });\n // Continue with next event\n }\n }\n\n return sentIds;\n }\n\n // ============================================================\n // Track Event\n // ============================================================\n\n /**\n * Track an event\n *\n * When offline queueing is enabled, events are always written to the local\n * database first (write-ahead), then sent to the server. This ensures no\n * events are lost if the browser crashes or the network fails.\n *\n * @param options - Event tracking options\n * @returns Promise resolving to the track response\n * @throws {ValidationError} When required fields are missing\n * @throws {AuthenticationError} When the API key is invalid (only when offline disabled)\n * @throws {ApiError} When the API returns an error (only when offline disabled)\n * @throws {TimeoutError} When the request times out (only when offline disabled)\n */\n async track(options: TrackOptions): Promise<TrackResponse> {\n this.validateTrackOptions(options);\n\n // Calculate duration if this event was being timed\n let duration: number | undefined;\n const startTime = this.timedEvents.get(options.event);\n if (startTime !== undefined) {\n duration = (Date.now() - startTime) / 1000;\n this.timedEvents.delete(options.event);\n this.log('Timer stopped', { event: options.event, duration });\n }\n\n // Include anonymous_id unless explicitly disabled\n const includeAnonymousId = options.includeAnonymousId !== false;\n\n // Merge super properties with event tags (event tags take precedence)\n const mergedTags: Tags = {\n ...this.superProperties,\n ...(options.tags ?? {}),\n ...(duration !== undefined ? { $duration: duration } : {}),\n };\n\n const payload: LogPayload = {\n channel: options.channel,\n event: options.event,\n timestamp: Date.now(),\n ...(options.user_id && { user_id: options.user_id }),\n ...(includeAnonymousId && this.anonymousId && { anonymous_id: this.anonymousId }),\n ...(options.icon && { icon: options.icon }),\n ...(options.notify !== undefined && { notify: options.notify }),\n ...(options.description && { description: options.description }),\n ...(Object.keys(mergedTags).length > 0 && { tags: mergedTags }),\n };\n\n this.log('Track', { event: options.event, payload });\n\n // If offline queueing is enabled, use write-ahead pattern\n if (this.queue) {\n // Always write to DB first (guaranteed durability)\n await this.queue.enqueue(payload);\n this.log('Event persisted to queue');\n\n // Trigger an immediate flush attempt (non-blocking)\n this.queue.flush().catch((err) => {\n this.log('Background flush failed', err);\n });\n\n // Return immediately after DB write\n return {\n id: `queued-${Date.now()}`,\n event: options.event,\n timestamp: new Date().toISOString(),\n };\n }\n\n // No offline queue - send directly (original behavior)\n const response = await this.sendRequest<TrackResponse>('/sdk/v1/logs', payload);\n this.log('Event sent successfully', { id: response.id });\n return response;\n }\n\n private validateTrackOptions(options: TrackOptions): void {\n if (!options.event) {\n throw new ValidationError('Event is required', 'event');\n }\n }\n\n /**\n * Send a request to the API\n */\n private async sendRequest<T>(endpoint: string, body: unknown): Promise<T> {\n const url = `${this.baseUrl}${endpoint}`;\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), TIMEOUT);\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-sdk-key': `${this.token}`,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorBody = await this.parseResponseBody(response);\n\n if (response.status === 401) {\n throw new AuthenticationError();\n }\n\n throw new ApiError(\n this.getErrorMessage(errorBody, response.statusText),\n response.status,\n errorBody,\n );\n }\n\n return (await response.json()) as T;\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof Error && error.name === 'AbortError') {\n throw new TimeoutError();\n }\n\n throw error;\n }\n }\n\n private async parseResponseBody(response: Response): Promise<unknown> {\n try {\n return await response.json();\n } catch {\n return null;\n }\n }\n\n private getErrorMessage(body: unknown, fallback: string): string {\n if (body && typeof body === 'object' && 'message' in body) {\n return String((body as { message: unknown }).message);\n }\n if (body && typeof body === 'object' && 'error' in body) {\n return String((body as { error: unknown }).error);\n }\n return fallback;\n }\n\n // ============================================================\n // Analytics & Session Management\n // ============================================================\n\n /**\n * Load session from storage\n */\n private loadSession(): void {\n if (!this.storage) return;\n\n try {\n const stored = this.storage.getItem(this.sessionStorageKey);\n if (stored) {\n const session = JSON.parse(stored) as Session;\n const now = Date.now();\n\n // Check if session is still valid (not expired)\n if (now - session.lastActivityAt < this.sessionTimeout) {\n this.session = session;\n this.log('Session restored', { sessionId: session.id });\n } else {\n // Session expired, will create new one on next activity\n this.storage.removeItem(this.sessionStorageKey);\n this.log('Session expired, removed from storage');\n }\n }\n } catch (error) {\n this.log('Failed to load session from storage', error);\n }\n }\n\n /**\n * Save session to storage\n */\n private saveSession(): void {\n if (!this.storage || !this.session) return;\n\n try {\n this.storage.setItem(this.sessionStorageKey, JSON.stringify(this.session));\n } catch (error) {\n this.log('Failed to save session to storage', error);\n }\n }\n\n /**\n * Get or create a session\n */\n private getOrCreateSession(): Session {\n const now = Date.now();\n\n // Check if session expired\n if (this.session && (now - this.session.lastActivityAt) > this.sessionTimeout) {\n this.endSession();\n this.session = null;\n }\n\n // Create new session if needed\n if (!this.session) {\n const referrer = typeof document !== 'undefined' ? document.referrer : undefined;\n const path = typeof window !== 'undefined' ? window.location.pathname : undefined;\n\n this.session = {\n id: uuidv4(),\n startedAt: now,\n lastActivityAt: now,\n screenViewCount: 0,\n entryPath: path,\n entryReferrer: referrer,\n };\n\n this.log('New session created', { sessionId: this.session.id });\n this.trackSessionStart();\n }\n\n // Update last activity\n this.session.lastActivityAt = now;\n this.saveSession();\n\n return this.session;\n }\n\n /**\n * Get the current session ID (or null if no active session)\n */\n getSessionId(): string | null {\n return this.session?.id ?? null;\n }\n\n /**\n * Get the current session data\n */\n getSession(): Session | null {\n return this.session ? { ...this.session } : null;\n }\n\n /**\n * Track session start event\n */\n private trackSessionStart(): void {\n if (!this.session) return;\n\n const utmParams = this.getUtmParams();\n\n this.track({\n channel: ANALYTICS_CHANNEL,\n event: 'session_start',\n tags: {\n __session_id: this.session.id,\n __path: this.session.entryPath ?? '', // For path column in DB\n __entry_path: this.session.entryPath ?? '', // For semantic clarity\n __referrer: this.session.entryReferrer ?? '',\n ...utmParams,\n },\n }).catch((err) => this.log('Failed to track session_start', err));\n }\n\n /**\n * End the current session (clears local state only - server calculates metrics)\n */\n private endSession(): void {\n if (!this.session) return;\n\n this.log('Session ended', { sessionId: this.session.id });\n\n // Clear session from storage (no event sent - server calculates metrics)\n if (this.storage) {\n this.storage.removeItem(this.sessionStorageKey);\n }\n this.session = null;\n }\n\n /**\n * Setup listeners for session lifecycle management\n */\n private setupUnloadListener(): void {\n if (typeof window === 'undefined' || this.unloadListenerAdded) return;\n\n // On visibility change: save session state\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') {\n this.saveSession();\n this.log('Page hidden, session state saved');\n }\n });\n\n // On page unload: end session (clears local state only)\n window.addEventListener('pagehide', () => {\n this.endSession();\n this.log('Page unloading, session ended locally');\n });\n\n this.unloadListenerAdded = true;\n this.log('Session lifecycle listeners added');\n }\n\n /**\n * Get UTM parameters from URL\n */\n private getUtmParams(): Tags {\n if (typeof window === 'undefined') return {};\n\n const params = new URLSearchParams(window.location.search);\n const utmParams: Tags = {};\n\n const utmKeys = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'];\n for (const key of utmKeys) {\n const value = params.get(key);\n if (value) {\n utmParams[`__${key}`] = value;\n }\n }\n\n return utmParams;\n }\n\n /**\n * Track a page view\n *\n * @param options - Page view options\n * @returns Promise resolving to the track response\n *\n * @example\n * ```typescript\n * // Track current page\n * await kitbase.trackPageView();\n *\n * // Track with custom path\n * await kitbase.trackPageView({ path: '/products/123', title: 'Product Details' });\n * ```\n */\n async trackPageView(options: PageViewOptions = {}): Promise<TrackResponse> {\n const session = this.getOrCreateSession();\n session.screenViewCount++;\n this.saveSession();\n\n const path = options.path ?? (typeof window !== 'undefined' ? window.location.pathname : '');\n const title = options.title ?? (typeof document !== 'undefined' ? document.title : '');\n const referrer = options.referrer ?? (typeof document !== 'undefined' ? document.referrer : '');\n\n return this.track({\n channel: ANALYTICS_CHANNEL,\n event: 'screen_view',\n tags: {\n __session_id: session.id,\n __path: path,\n __title: title,\n __referrer: referrer,\n ...this.getUtmParams(),\n ...(options.tags ?? {}),\n },\n });\n }\n\n /**\n * Enable automatic page view tracking\n * Intercepts browser history changes (pushState, replaceState, popstate)\n *\n * @example\n * ```typescript\n * kitbase.enableAutoPageViews();\n * // Now all route changes will automatically be tracked\n * ```\n */\n enableAutoPageViews(): void {\n if (typeof window === 'undefined') {\n this.log('Auto page views not available in non-browser environment');\n return;\n }\n\n // Track initial page view\n this.trackPageView().catch((err) => this.log('Failed to track initial page view', err));\n\n // Intercept pushState\n const originalPushState = history.pushState.bind(history);\n history.pushState = (...args) => {\n originalPushState(...args);\n this.trackPageView().catch((err) => this.log('Failed to track page view (pushState)', err));\n };\n\n // Intercept replaceState\n const originalReplaceState = history.replaceState.bind(history);\n history.replaceState = (...args) => {\n originalReplaceState(...args);\n // Don't track replaceState as it's usually not a navigation\n };\n\n // Listen to popstate (browser back/forward)\n window.addEventListener('popstate', () => {\n this.trackPageView().catch((err) => this.log('Failed to track page view (popstate)', err));\n });\n\n this.log('Auto page view tracking enabled');\n }\n\n /**\n * Track a revenue event\n *\n * @param options - Revenue options\n * @returns Promise resolving to the track response\n *\n * @example\n * ```typescript\n * // Track a $19.99 purchase\n * await kitbase.trackRevenue({\n * amount: 1999,\n * currency: 'USD',\n * tags: { product_id: 'prod_123', plan: 'premium' },\n * });\n * ```\n */\n async trackRevenue(options: RevenueOptions): Promise<TrackResponse> {\n const session = this.getOrCreateSession();\n\n return this.track({\n channel: ANALYTICS_CHANNEL,\n event: 'revenue',\n user_id: options.user_id ?? this.userId ?? undefined,\n tags: {\n __session_id: session.id,\n __revenue: options.amount,\n __currency: options.currency ?? 'USD',\n ...(options.tags ?? {}),\n },\n });\n }\n\n /**\n * Identify a user\n * Links the current anonymous ID to a user ID for future events\n *\n * @param options - Identify options\n *\n * @example\n * ```typescript\n * kitbase.identify({\n * userId: 'user_123',\n * traits: { email: 'user@example.com', plan: 'premium' },\n * });\n * ```\n */\n identify(options: IdentifyOptions): void {\n this.userId = options.userId;\n\n // Register user traits as super properties\n if (options.traits) {\n this.register({\n __user_id: options.userId,\n ...options.traits,\n });\n } else {\n this.register({ __user_id: options.userId });\n }\n\n // Track identify event\n this.track({\n channel: ANALYTICS_CHANNEL,\n event: 'identify',\n user_id: options.userId,\n tags: {\n __session_id: this.session?.id ?? '',\n __anonymous_id: this.anonymousId ?? '',\n ...(options.traits ?? {}),\n },\n }).catch((err) => this.log('Failed to track identify', err));\n\n this.log('User identified', { userId: options.userId });\n }\n\n /**\n * Get the current user ID (set via identify)\n */\n getUserId(): string | null {\n return this.userId;\n }\n\n /**\n * Reset the user identity and session\n * Call this when a user logs out\n *\n * @example\n * ```typescript\n * kitbase.reset();\n * ```\n */\n reset(): void {\n // End current session\n if (this.session) {\n this.endSession();\n this.session = null;\n }\n\n // Clear user ID\n this.userId = null;\n\n // Generate new anonymous ID\n this.anonymousId = uuidv4();\n if (this.storage) {\n this.storage.setItem(this.storageKey, this.anonymousId);\n }\n\n // Clear super properties\n this.clearSuperProperties();\n\n this.log('User reset complete');\n }\n\n // ============================================================\n // Cleanup\n // ============================================================\n\n /**\n * Shutdown the client and cleanup resources\n * Call this when you're done using the client to stop timers and close connections\n *\n * @example\n * ```typescript\n * await kitbase.shutdown();\n * ```\n */\n async shutdown(): Promise<void> {\n this.log('Shutting down');\n\n // Flush any remaining queued events\n if (this.queue) {\n await this.queue.flush();\n await this.queue.close();\n this.queue = null;\n }\n\n // Clear timed events\n this.timedEvents.clear();\n\n this.log('Shutdown complete');\n }\n}\n","/**\n * Base error class for Kitbase SDK errors\n */\nexport class KitbaseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'KitbaseError';\n Object.setPrototypeOf(this, KitbaseError.prototype);\n }\n}\n\n/**\n * Error thrown when API authentication fails\n */\nexport class AuthenticationError extends KitbaseError {\n constructor(message = 'Invalid API key') {\n super(message);\n this.name = 'AuthenticationError';\n Object.setPrototypeOf(this, AuthenticationError.prototype);\n }\n}\n\n/**\n * Error thrown when the API request fails\n */\nexport class ApiError extends KitbaseError {\n public readonly statusCode: number;\n public readonly response?: unknown;\n\n constructor(message: string, statusCode: number, response?: unknown) {\n super(message);\n this.name = 'ApiError';\n this.statusCode = statusCode;\n this.response = response;\n Object.setPrototypeOf(this, ApiError.prototype);\n }\n}\n\n/**\n * Error thrown when request validation fails\n */\nexport class ValidationError extends KitbaseError {\n public readonly field?: string;\n\n constructor(message: string, field?: string) {\n super(message);\n this.name = 'ValidationError';\n this.field = field;\n Object.setPrototypeOf(this, ValidationError.prototype);\n }\n}\n\n/**\n * Error thrown when a request times out\n */\nexport class TimeoutError extends KitbaseError {\n constructor(message = 'Request timed out') {\n super(message);\n this.name = 'TimeoutError';\n Object.setPrototypeOf(this, TimeoutError.prototype);\n }\n}\n\n\n\n\n\n\n\n\n","import Dexie, { type Table } from 'dexie';\nimport type { LogPayload } from '../types.js';\nimport type {\n OfflineConfig,\n QueuedEvent,\n QueueStats,\n EventQueueInterface,\n SendEventsCallback,\n} from './types.js';\n\nconst DEFAULT_CONFIG: Required<OfflineConfig> = {\n enabled: false,\n maxQueueSize: 1000,\n flushInterval: 30000,\n flushBatchSize: 50,\n maxRetries: 3,\n retryBaseDelay: 1000,\n};\n\n/**\n * Check if IndexedDB is available\n */\nfunction isIndexedDBAvailable(): boolean {\n try {\n return (\n typeof window !== 'undefined' &&\n typeof window.indexedDB !== 'undefined' &&\n window.indexedDB !== null\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Check if we're in a browser environment\n */\nfunction isBrowser(): boolean {\n return typeof window !== 'undefined' && typeof document !== 'undefined';\n}\n\n/**\n * Dexie database for event queue\n */\nclass KitbaseQueueDB extends Dexie {\n events!: Table<QueuedEvent, number>;\n\n constructor(dbName: string) {\n super(dbName);\n this.version(1).stores({\n events: '++id, timestamp, retries, lastAttempt',\n });\n }\n}\n\n/**\n * In-memory queue implementation for Node.js or when IndexedDB is unavailable\n */\nclass MemoryQueue {\n private queue: QueuedEvent[] = [];\n private idCounter = 1;\n\n async enqueue(payload: LogPayload): Promise<number> {\n const event: QueuedEvent = {\n id: this.idCounter++,\n payload,\n timestamp: Date.now(),\n retries: 0,\n };\n this.queue.push(event);\n return event.id!;\n }\n\n async dequeue(count: number): Promise<QueuedEvent[]> {\n // Sort by timestamp (oldest first) and get the first `count` events\n this.queue.sort((a, b) => a.timestamp - b.timestamp);\n return this.queue.slice(0, count);\n }\n\n async delete(ids: number[]): Promise<void> {\n this.queue = this.queue.filter((e) => !ids.includes(e.id!));\n }\n\n async updateRetries(ids: number[]): Promise<void> {\n const now = Date.now();\n for (const event of this.queue) {\n if (ids.includes(event.id!)) {\n event.retries++;\n event.lastAttempt = now;\n }\n }\n }\n\n async getStats(): Promise<{ size: number; oldestEvent?: number }> {\n const size = this.queue.length;\n const oldestEvent =\n size > 0\n ? Math.min(...this.queue.map((e) => e.timestamp))\n : undefined;\n return { size, oldestEvent };\n }\n\n async clear(): Promise<void> {\n this.queue = [];\n }\n\n async enforceMaxSize(maxSize: number): Promise<void> {\n if (this.queue.length > maxSize) {\n // Sort by timestamp and keep only the newest events\n this.queue.sort((a, b) => a.timestamp - b.timestamp);\n this.queue = this.queue.slice(-maxSize);\n }\n }\n\n async getEventsExceedingRetries(maxRetries: number): Promise<number[]> {\n return this.queue\n .filter((e) => e.retries >= maxRetries)\n .map((e) => e.id!);\n }\n}\n\n/**\n * Event queue for offline support\n * Uses IndexedDB (via Dexie) in browser, in-memory queue in Node.js\n */\nexport class EventQueue implements EventQueueInterface {\n private readonly config: Required<OfflineConfig>;\n private readonly dbName: string;\n private db: KitbaseQueueDB | null = null;\n private memoryQueue: MemoryQueue | null = null;\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private isFlushing = false;\n private sendEvents: SendEventsCallback | null = null;\n private readonly useIndexedDB: boolean;\n private debugMode = false;\n private debugLogger: ((message: string, data?: unknown) => void) | null = null;\n\n constructor(config: OfflineConfig = {}, dbName = 'kitbase-events') {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.dbName = dbName;\n this.useIndexedDB = isIndexedDBAvailable();\n\n if (this.useIndexedDB) {\n this.db = new KitbaseQueueDB(this.dbName);\n } else {\n this.memoryQueue = new MemoryQueue();\n }\n }\n\n /**\n * Set debug mode and logger\n */\n setDebugMode(enabled: boolean, logger?: (message: string, data?: unknown) => void): void {\n this.debugMode = enabled;\n this.debugLogger = logger ?? null;\n }\n\n private log(message: string, data?: unknown): void {\n if (this.debugMode && this.debugLogger) {\n this.debugLogger(message, data);\n }\n }\n\n /**\n * Set the callback for sending events\n */\n setSendCallback(callback: SendEventsCallback): void {\n this.sendEvents = callback;\n }\n\n /**\n * Check if the queue storage is available\n */\n isAvailable(): boolean {\n return this.useIndexedDB || this.memoryQueue !== null;\n }\n\n /**\n * Get the storage type being used\n */\n getStorageType(): 'indexeddb' | 'memory' {\n return this.useIndexedDB ? 'indexeddb' : 'memory';\n }\n\n /**\n * Add an event to the queue\n */\n async enqueue(payload: LogPayload): Promise<void> {\n const event: Omit<QueuedEvent, 'id'> = {\n payload,\n timestamp: Date.now(),\n retries: 0,\n };\n\n if (this.useIndexedDB && this.db) {\n await this.db.events.add(event as QueuedEvent);\n this.log('Event queued to IndexedDB', payload);\n } else if (this.memoryQueue) {\n await this.memoryQueue.enqueue(payload);\n this.log('Event queued to memory', payload);\n }\n\n // Enforce max queue size\n await this.enforceMaxQueueSize();\n }\n\n /**\n * Get and remove the next batch of events to send\n */\n async dequeue(count: number): Promise<QueuedEvent[]> {\n if (this.useIndexedDB && this.db) {\n // Get events sorted by timestamp (oldest first), excluding those with too many retries\n return this.db.events\n .where('retries')\n .below(this.config.maxRetries)\n .sortBy('timestamp')\n .then((events) => events.slice(0, count));\n } else if (this.memoryQueue) {\n const events = await this.memoryQueue.dequeue(count);\n return events.filter((e) => e.retries < this.config.maxRetries);\n }\n return [];\n }\n\n /**\n * Mark events as successfully sent (remove from queue)\n */\n async markSent(ids: number[]): Promise<void> {\n if (ids.length === 0) return;\n\n if (this.useIndexedDB && this.db) {\n await this.db.events.bulkDelete(ids);\n this.log(`Removed ${ids.length} sent events from queue`);\n } else if (this.memoryQueue) {\n await this.memoryQueue.delete(ids);\n this.log(`Removed ${ids.length} sent events from memory queue`);\n }\n }\n\n /**\n * Mark events as failed and increment retry count\n */\n async markFailed(ids: number[]): Promise<void> {\n if (ids.length === 0) return;\n\n const now = Date.now();\n\n if (this.useIndexedDB && this.db) {\n await this.db.transaction('rw', this.db.events, async () => {\n for (const id of ids) {\n const event = await this.db!.events.get(id);\n if (event) {\n await this.db!.events.update(id, {\n retries: event.retries + 1,\n lastAttempt: now,\n });\n }\n }\n });\n this.log(`Marked ${ids.length} events as failed`);\n } else if (this.memoryQueue) {\n await this.memoryQueue.updateRetries(ids);\n this.log(`Marked ${ids.length} events as failed in memory queue`);\n }\n\n // Remove events that have exceeded max retries\n await this.removeExpiredRetries();\n }\n\n /**\n * Remove events that have exceeded max retry attempts\n */\n private async removeExpiredRetries(): Promise<void> {\n if (this.useIndexedDB && this.db) {\n const expiredIds = await this.db.events\n .where('retries')\n .aboveOrEqual(this.config.maxRetries)\n .primaryKeys();\n if (expiredIds.length > 0) {\n await this.db.events.bulkDelete(expiredIds);\n this.log(`Removed ${expiredIds.length} events that exceeded max retries`);\n }\n } else if (this.memoryQueue) {\n const expiredIds = await this.memoryQueue.getEventsExceedingRetries(\n this.config.maxRetries\n );\n if (expiredIds.length > 0) {\n await this.memoryQueue.delete(expiredIds);\n this.log(`Removed ${expiredIds.length} events that exceeded max retries`);\n }\n }\n }\n\n /**\n * Enforce the maximum queue size by removing oldest events\n */\n private async enforceMaxQueueSize(): Promise<void> {\n if (this.useIndexedDB && this.db) {\n const count = await this.db.events.count();\n if (count > this.config.maxQueueSize) {\n const excess = count - this.config.maxQueueSize;\n const oldestEvents = await this.db.events\n .orderBy('timestamp')\n .limit(excess)\n .primaryKeys();\n await this.db.events.bulkDelete(oldestEvents);\n this.log(`Removed ${excess} oldest events to enforce queue size limit`);\n }\n } else if (this.memoryQueue) {\n await this.memoryQueue.enforceMaxSize(this.config.maxQueueSize);\n }\n }\n\n /**\n * Get queue statistics\n */\n async getStats(): Promise<QueueStats> {\n if (this.useIndexedDB && this.db) {\n const size = await this.db.events.count();\n const oldestEvent = await this.db.events\n .orderBy('timestamp')\n .first()\n .then((e) => e?.timestamp);\n return { size, oldestEvent, isFlushing: this.isFlushing };\n } else if (this.memoryQueue) {\n const stats = await this.memoryQueue.getStats();\n return { ...stats, isFlushing: this.isFlushing };\n }\n return { size: 0, isFlushing: this.isFlushing };\n }\n\n /**\n * Clear all events from the queue\n */\n async clear(): Promise<void> {\n if (this.useIndexedDB && this.db) {\n await this.db.events.clear();\n this.log('Queue cleared (IndexedDB)');\n } else if (this.memoryQueue) {\n await this.memoryQueue.clear();\n this.log('Queue cleared (memory)');\n }\n }\n\n /**\n * Start the automatic flush timer\n */\n startFlushTimer(): void {\n if (this.flushTimer) return;\n\n this.flushTimer = setInterval(() => {\n this.flush().catch((err) => {\n this.log('Flush timer error', err);\n });\n }, this.config.flushInterval);\n\n // Also listen for online events in browser\n if (isBrowser()) {\n window.addEventListener('online', this.handleOnline);\n }\n\n this.log(`Flush timer started (interval: ${this.config.flushInterval}ms)`);\n }\n\n /**\n * Stop the automatic flush timer\n */\n stopFlushTimer(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n if (isBrowser()) {\n window.removeEventListener('online', this.handleOnline);\n }\n\n this.log('Flush timer stopped');\n }\n\n /**\n * Handle coming back online\n */\n private handleOnline = (): void => {\n this.log('Browser came online, triggering flush');\n this.flush().catch((err) => {\n this.log('Online flush error', err);\n });\n };\n\n /**\n * Check if we're currently online\n */\n private isOnline(): boolean {\n if (isBrowser()) {\n return navigator.onLine;\n }\n // Assume online in Node.js\n return true;\n }\n\n /**\n * Manually trigger a flush of queued events\n */\n async flush(): Promise<void> {\n if (this.isFlushing) {\n this.log('Flush already in progress, skipping');\n return;\n }\n\n if (!this.isOnline()) {\n this.log('Offline, skipping flush');\n return;\n }\n\n if (!this.sendEvents) {\n this.log('No send callback configured, skipping flush');\n return;\n }\n\n this.isFlushing = true;\n\n try {\n const stats = await this.getStats();\n if (stats.size === 0) {\n this.log('Queue is empty, nothing to flush');\n return;\n }\n\n this.log(`Flushing queue (${stats.size} events)`);\n\n // Process in batches\n let processed = 0;\n while (true) {\n const events = await this.dequeue(this.config.flushBatchSize);\n if (events.length === 0) break;\n\n this.log(`Sending batch of ${events.length} events`);\n\n try {\n const sentIds = await this.sendEvents(events);\n await this.markSent(sentIds);\n\n // Mark remaining as failed\n const failedIds = events\n .filter((e) => !sentIds.includes(e.id!))\n .map((e) => e.id!);\n if (failedIds.length > 0) {\n await this.markFailed(failedIds);\n }\n\n processed += sentIds.length;\n } catch (error) {\n // Mark all as failed on network error\n const allIds = events.map((e) => e.id!);\n await this.markFailed(allIds);\n this.log('Batch send failed', error);\n break; // Stop flushing on error\n }\n }\n\n this.log(`Flush complete, sent ${processed} events`);\n } finally {\n this.isFlushing = false;\n }\n }\n\n /**\n * Close the database connection\n */\n async close(): Promise<void> {\n this.stopFlushTimer();\n if (this.db) {\n this.db.close();\n this.log('Database connection closed');\n }\n }\n}\n\nexport type { OfflineConfig, QueuedEvent, QueueStats, EventQueueInterface, SendEventsCallback };\n"],"mappings":"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","names":["Dexie","uuidv4"]}
package/dist/index.js CHANGED
@@ -461,7 +461,7 @@ var Kitbase = class {
461
461
  throw new ValidationError("API token is required", "token");
462
462
  }
463
463
  this.token = config.token;
464
- this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
464
+ this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
465
465
  this.storageKey = config.storageKey ?? DEFAULT_STORAGE_KEY;
466
466
  this.debugMode = config.debug ?? false;
467
467
  if (config.storage === null) {
@@ -974,7 +974,10 @@ var Kitbase = class {
974
974
  event: "session_start",
975
975
  tags: {
976
976
  __session_id: this.session.id,
977
+ __path: this.session.entryPath ?? "",
978
+ // For path column in DB
977
979
  __entry_path: this.session.entryPath ?? "",
980
+ // For semantic clarity
978
981
  __referrer: this.session.entryReferrer ?? "",
979
982
  ...utmParams
980
983
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/errors.ts","../src/queue/index.ts"],"sourcesContent":["import { v4 as uuidv4 } from 'uuid';\nimport type {\n KitbaseConfig,\n TrackOptions,\n TrackResponse,\n LogPayload,\n Storage,\n Tags,\n Session,\n AnalyticsConfig,\n PageViewOptions,\n RevenueOptions,\n IdentifyOptions,\n} from './types.js';\nimport {\n ApiError,\n AuthenticationError,\n TimeoutError,\n ValidationError,\n} from './errors.js';\nimport { EventQueue } from './queue/index.js';\nimport type { QueuedEvent, QueueStats } from './queue/types.js';\n\nconst DEFAULT_BASE_URL = 'https://api.kitbase.dev';\nconst TIMEOUT = 30000;\nconst DEFAULT_STORAGE_KEY = 'kitbase_anonymous_id';\nconst DEFAULT_SESSION_STORAGE_KEY = 'kitbase_session';\nconst DEFAULT_SESSION_TIMEOUT = 30 * 60 * 1000; // 30 minutes\nconst ANALYTICS_CHANNEL = '__analytics';\n\n/**\n * In-memory storage fallback for non-browser environments\n */\nclass MemoryStorage implements Storage {\n private data: Map<string, string> = new Map();\n\n getItem(key: string): string | null {\n return this.data.get(key) ?? null;\n }\n\n setItem(key: string, value: string): void {\n this.data.set(key, value);\n }\n\n removeItem(key: string): void {\n this.data.delete(key);\n }\n}\n\n/**\n * Get the default storage (localStorage in browser, memory otherwise)\n */\nfunction getDefaultStorage(): Storage {\n if (typeof window !== 'undefined' && window.localStorage) {\n return window.localStorage;\n }\n return new MemoryStorage();\n}\n\n/**\n * Kitbase client for tracking events\n *\n * @example\n * ```typescript\n * import { Kitbase } from '@kitbase/sdk/events';\n *\n * const kitbase = new Kitbase({\n * token: '<YOUR_API_KEY>',\n * debug: true,\n * offline: { enabled: true },\n * });\n *\n * // Register super properties (included in all events)\n * kitbase.register({ app_version: '2.1.0', platform: 'web' });\n *\n * // Track anonymous events (anonymous_id is automatically included)\n * await kitbase.track({\n * channel: 'payments',\n * event: 'Page Viewed',\n * icon: '👀',\n * });\n *\n * // Time events for duration tracking\n * kitbase.timeEvent('Video Watched');\n * // ... later\n * await kitbase.track({\n * channel: 'engagement',\n * event: 'Video Watched', // $duration automatically included\n * });\n *\n * // Track events for a logged-in user (just pass user_id)\n * await kitbase.track({\n * channel: 'payments',\n * event: 'New Subscription',\n * user_id: 'user-123',\n * icon: '💰',\n * notify: true,\n * tags: {\n * plan: 'premium',\n * cycle: 'monthly',\n * },\n * });\n * ```\n */\nexport class Kitbase {\n private readonly token: string;\n private readonly baseUrl: string;\n private readonly storage: Storage | null;\n private readonly storageKey: string;\n private anonymousId: string | null = null;\n\n // Super properties (memory-only, merged into all events)\n private superProperties: Tags = {};\n\n // Time event tracking\n private timedEvents: Map<string, number> = new Map();\n\n // Debug mode\n private debugMode: boolean;\n\n // Offline queue\n private queue: EventQueue | null = null;\n private offlineEnabled: boolean;\n\n // Analytics & Session tracking\n private session: Session | null = null;\n private sessionTimeout: number;\n private sessionStorageKey: string;\n private analyticsEnabled: boolean;\n private autoTrackPageViews: boolean;\n private userId: string | null = null;\n private unloadListenerAdded = false;\n\n constructor(config: KitbaseConfig) {\n if (!config.token) {\n throw new ValidationError('API token is required', 'token');\n }\n\n this.token = config.token;\n this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n this.storageKey = config.storageKey ?? DEFAULT_STORAGE_KEY;\n this.debugMode = config.debug ?? false;\n\n // Initialize storage (null means disabled)\n if (config.storage === null) {\n this.storage = null;\n } else {\n this.storage = config.storage ?? getDefaultStorage();\n }\n\n // Load or generate anonymous ID\n this.initializeAnonymousId();\n\n // Initialize analytics configuration\n this.sessionTimeout = config.analytics?.sessionTimeout ?? DEFAULT_SESSION_TIMEOUT;\n this.sessionStorageKey = config.analytics?.sessionStorageKey ?? DEFAULT_SESSION_STORAGE_KEY;\n this.analyticsEnabled = config.analytics?.autoTrackSessions ?? true;\n this.autoTrackPageViews = config.analytics?.autoTrackPageViews ?? false;\n\n // Load existing session from storage\n if (this.analyticsEnabled) {\n this.loadSession();\n this.setupUnloadListener();\n\n // Auto-track pageviews if enabled\n if (this.autoTrackPageViews && typeof window !== 'undefined') {\n this.enableAutoPageViews();\n }\n }\n\n // Initialize offline queue if enabled\n this.offlineEnabled = config.offline?.enabled ?? false;\n if (this.offlineEnabled) {\n this.queue = new EventQueue(config.offline);\n this.queue.setDebugMode(this.debugMode, this.log.bind(this));\n this.queue.setSendCallback(this.sendQueuedEvents.bind(this));\n this.queue.startFlushTimer();\n this.log('Offline queueing enabled', {\n storageType: this.queue.getStorageType(),\n });\n }\n }\n\n /**\n * Initialize the anonymous ID from storage or generate a new one\n */\n private initializeAnonymousId(): void {\n if (this.storage) {\n const stored = this.storage.getItem(this.storageKey);\n if (stored) {\n this.anonymousId = stored;\n return;\n }\n }\n\n // Generate new anonymous ID\n this.anonymousId = uuidv4();\n\n // Persist if storage is available\n if (this.storage && this.anonymousId) {\n this.storage.setItem(this.storageKey, this.anonymousId);\n }\n }\n\n /**\n * Get the current anonymous ID\n */\n getAnonymousId(): string | null {\n return this.anonymousId;\n }\n\n // ============================================================\n // Debug Mode\n // ============================================================\n\n /**\n * Enable or disable debug mode\n * When enabled, all SDK operations are logged to the console\n *\n * @param enabled - Whether to enable debug mode\n *\n * @example\n * ```typescript\n * kitbase.setDebugMode(true);\n * // All events and operations will now be logged\n * ```\n */\n setDebugMode(enabled: boolean): void {\n this.debugMode = enabled;\n if (this.queue) {\n this.queue.setDebugMode(enabled, this.log.bind(this));\n }\n this.log(`Debug mode ${enabled ? 'enabled' : 'disabled'}`);\n }\n\n /**\n * Check if debug mode is enabled\n */\n isDebugMode(): boolean {\n return this.debugMode;\n }\n\n /**\n * Internal logging function\n */\n private log(message: string, data?: unknown): void {\n if (!this.debugMode) return;\n\n const prefix = '[Kitbase]';\n if (data !== undefined) {\n console.log(prefix, message, data);\n } else {\n console.log(prefix, message);\n }\n }\n\n // ============================================================\n // Super Properties\n // ============================================================\n\n /**\n * Register super properties that will be included with every event\n * These properties are stored in memory only and reset on page reload\n *\n * @param properties - Properties to register\n *\n * @example\n * ```typescript\n * kitbase.register({\n * app_version: '2.1.0',\n * platform: 'web',\n * environment: 'production',\n * });\n * ```\n */\n register(properties: Tags): void {\n this.superProperties = { ...this.superProperties, ...properties };\n this.log('Super properties registered', properties);\n }\n\n /**\n * Register super properties only if they haven't been set yet\n * Useful for setting default values that shouldn't override existing ones\n *\n * @param properties - Properties to register if not already set\n *\n * @example\n * ```typescript\n * kitbase.registerOnce({ first_visit: new Date().toISOString() });\n * ```\n */\n registerOnce(properties: Tags): void {\n const newProps: Tags = {};\n for (const [key, value] of Object.entries(properties)) {\n if (!(key in this.superProperties)) {\n newProps[key] = value;\n }\n }\n if (Object.keys(newProps).length > 0) {\n this.superProperties = { ...this.superProperties, ...newProps };\n this.log('Super properties registered (once)', newProps);\n }\n }\n\n /**\n * Remove a super property\n *\n * @param key - The property key to remove\n *\n * @example\n * ```typescript\n * kitbase.unregister('platform');\n * ```\n */\n unregister(key: string): void {\n if (key in this.superProperties) {\n delete this.superProperties[key];\n this.log('Super property removed', { key });\n }\n }\n\n /**\n * Get all registered super properties\n *\n * @returns A copy of the current super properties\n *\n * @example\n * ```typescript\n * const props = kitbase.getSuperProperties();\n * console.log(props); // { app_version: '2.1.0', platform: 'web' }\n * ```\n */\n getSuperProperties(): Tags {\n return { ...this.superProperties };\n }\n\n /**\n * Clear all super properties\n *\n * @example\n * ```typescript\n * kitbase.clearSuperProperties();\n * ```\n */\n clearSuperProperties(): void {\n this.superProperties = {};\n this.log('Super properties cleared');\n }\n\n // ============================================================\n // Time Events (Duration Tracking)\n // ============================================================\n\n /**\n * Start timing an event\n * When the same event is tracked later, a $duration property (in seconds)\n * will automatically be included\n *\n * @param eventName - The name of the event to time\n *\n * @example\n * ```typescript\n * kitbase.timeEvent('Video Watched');\n * // ... user watches video ...\n * await kitbase.track({\n * channel: 'engagement',\n * event: 'Video Watched',\n * tags: { video_id: '123' }\n * });\n * // Event will include $duration: 45.2 (seconds)\n * ```\n */\n timeEvent(eventName: string): void {\n this.timedEvents.set(eventName, Date.now());\n this.log('Timer started', { event: eventName });\n }\n\n /**\n * Cancel a timed event without tracking it\n *\n * @param eventName - The name of the event to cancel timing for\n *\n * @example\n * ```typescript\n * kitbase.timeEvent('Checkout Flow');\n * // User abandons checkout\n * kitbase.cancelTimeEvent('Checkout Flow');\n * ```\n */\n cancelTimeEvent(eventName: string): void {\n if (this.timedEvents.has(eventName)) {\n this.timedEvents.delete(eventName);\n this.log('Timer cancelled', { event: eventName });\n }\n }\n\n /**\n * Get all currently timed events\n *\n * @returns Array of event names that are currently being timed\n *\n * @example\n * ```typescript\n * const timedEvents = kitbase.getTimedEvents();\n * console.log(timedEvents); // ['Video Watched', 'Checkout Flow']\n * ```\n */\n getTimedEvents(): string[] {\n return Array.from(this.timedEvents.keys());\n }\n\n /**\n * Get the duration of a timed event (without stopping it)\n *\n * @param eventName - The name of the event\n * @returns Duration in seconds, or null if not being timed\n */\n getEventDuration(eventName: string): number | null {\n const startTime = this.timedEvents.get(eventName);\n if (startTime === undefined) return null;\n return (Date.now() - startTime) / 1000;\n }\n\n // ============================================================\n // Offline Queue\n // ============================================================\n\n /**\n * Get offline queue statistics\n *\n * @returns Queue statistics including size and flush status\n *\n * @example\n * ```typescript\n * const stats = await kitbase.getQueueStats();\n * console.log(stats); // { size: 5, isFlushing: false }\n * ```\n */\n async getQueueStats(): Promise<QueueStats | null> {\n if (!this.queue) return null;\n return this.queue.getStats();\n }\n\n /**\n * Manually flush the offline queue\n * Events are automatically flushed on interval and when coming back online,\n * but this method can be used to trigger an immediate flush\n *\n * @example\n * ```typescript\n * await kitbase.flushQueue();\n * ```\n */\n async flushQueue(): Promise<void> {\n if (!this.queue) return;\n await this.queue.flush();\n }\n\n /**\n * Clear all events from the offline queue\n *\n * @example\n * ```typescript\n * await kitbase.clearQueue();\n * ```\n */\n async clearQueue(): Promise<void> {\n if (!this.queue) return;\n await this.queue.clear();\n }\n\n /**\n * Callback for the queue to send batched events\n */\n private async sendQueuedEvents(events: QueuedEvent[]): Promise<number[]> {\n const sentIds: number[] = [];\n\n for (const event of events) {\n try {\n await this.sendRequest<TrackResponse>('/sdk/v1/logs', event.payload);\n sentIds.push(event.id!);\n } catch (error) {\n this.log('Failed to send queued event', { id: event.id, error });\n // Continue with next event\n }\n }\n\n return sentIds;\n }\n\n // ============================================================\n // Track Event\n // ============================================================\n\n /**\n * Track an event\n *\n * When offline queueing is enabled, events are always written to the local\n * database first (write-ahead), then sent to the server. This ensures no\n * events are lost if the browser crashes or the network fails.\n *\n * @param options - Event tracking options\n * @returns Promise resolving to the track response\n * @throws {ValidationError} When required fields are missing\n * @throws {AuthenticationError} When the API key is invalid (only when offline disabled)\n * @throws {ApiError} When the API returns an error (only when offline disabled)\n * @throws {TimeoutError} When the request times out (only when offline disabled)\n */\n async track(options: TrackOptions): Promise<TrackResponse> {\n this.validateTrackOptions(options);\n\n // Calculate duration if this event was being timed\n let duration: number | undefined;\n const startTime = this.timedEvents.get(options.event);\n if (startTime !== undefined) {\n duration = (Date.now() - startTime) / 1000;\n this.timedEvents.delete(options.event);\n this.log('Timer stopped', { event: options.event, duration });\n }\n\n // Include anonymous_id unless explicitly disabled\n const includeAnonymousId = options.includeAnonymousId !== false;\n\n // Merge super properties with event tags (event tags take precedence)\n const mergedTags: Tags = {\n ...this.superProperties,\n ...(options.tags ?? {}),\n ...(duration !== undefined ? { $duration: duration } : {}),\n };\n\n const payload: LogPayload = {\n channel: options.channel,\n event: options.event,\n timestamp: Date.now(),\n ...(options.user_id && { user_id: options.user_id }),\n ...(includeAnonymousId && this.anonymousId && { anonymous_id: this.anonymousId }),\n ...(options.icon && { icon: options.icon }),\n ...(options.notify !== undefined && { notify: options.notify }),\n ...(options.description && { description: options.description }),\n ...(Object.keys(mergedTags).length > 0 && { tags: mergedTags }),\n };\n\n this.log('Track', { event: options.event, payload });\n\n // If offline queueing is enabled, use write-ahead pattern\n if (this.queue) {\n // Always write to DB first (guaranteed durability)\n await this.queue.enqueue(payload);\n this.log('Event persisted to queue');\n\n // Trigger an immediate flush attempt (non-blocking)\n this.queue.flush().catch((err) => {\n this.log('Background flush failed', err);\n });\n\n // Return immediately after DB write\n return {\n id: `queued-${Date.now()}`,\n event: options.event,\n timestamp: new Date().toISOString(),\n };\n }\n\n // No offline queue - send directly (original behavior)\n const response = await this.sendRequest<TrackResponse>('/sdk/v1/logs', payload);\n this.log('Event sent successfully', { id: response.id });\n return response;\n }\n\n private validateTrackOptions(options: TrackOptions): void {\n if (!options.event) {\n throw new ValidationError('Event is required', 'event');\n }\n }\n\n /**\n * Send a request to the API\n */\n private async sendRequest<T>(endpoint: string, body: unknown): Promise<T> {\n const url = `${this.baseUrl}${endpoint}`;\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), TIMEOUT);\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-sdk-key': `${this.token}`,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorBody = await this.parseResponseBody(response);\n\n if (response.status === 401) {\n throw new AuthenticationError();\n }\n\n throw new ApiError(\n this.getErrorMessage(errorBody, response.statusText),\n response.status,\n errorBody,\n );\n }\n\n return (await response.json()) as T;\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof Error && error.name === 'AbortError') {\n throw new TimeoutError();\n }\n\n throw error;\n }\n }\n\n private async parseResponseBody(response: Response): Promise<unknown> {\n try {\n return await response.json();\n } catch {\n return null;\n }\n }\n\n private getErrorMessage(body: unknown, fallback: string): string {\n if (body && typeof body === 'object' && 'message' in body) {\n return String((body as { message: unknown }).message);\n }\n if (body && typeof body === 'object' && 'error' in body) {\n return String((body as { error: unknown }).error);\n }\n return fallback;\n }\n\n // ============================================================\n // Analytics & Session Management\n // ============================================================\n\n /**\n * Load session from storage\n */\n private loadSession(): void {\n if (!this.storage) return;\n\n try {\n const stored = this.storage.getItem(this.sessionStorageKey);\n if (stored) {\n const session = JSON.parse(stored) as Session;\n const now = Date.now();\n\n // Check if session is still valid (not expired)\n if (now - session.lastActivityAt < this.sessionTimeout) {\n this.session = session;\n this.log('Session restored', { sessionId: session.id });\n } else {\n // Session expired, will create new one on next activity\n this.storage.removeItem(this.sessionStorageKey);\n this.log('Session expired, removed from storage');\n }\n }\n } catch (error) {\n this.log('Failed to load session from storage', error);\n }\n }\n\n /**\n * Save session to storage\n */\n private saveSession(): void {\n if (!this.storage || !this.session) return;\n\n try {\n this.storage.setItem(this.sessionStorageKey, JSON.stringify(this.session));\n } catch (error) {\n this.log('Failed to save session to storage', error);\n }\n }\n\n /**\n * Get or create a session\n */\n private getOrCreateSession(): Session {\n const now = Date.now();\n\n // Check if session expired\n if (this.session && (now - this.session.lastActivityAt) > this.sessionTimeout) {\n this.endSession();\n this.session = null;\n }\n\n // Create new session if needed\n if (!this.session) {\n const referrer = typeof document !== 'undefined' ? document.referrer : undefined;\n const path = typeof window !== 'undefined' ? window.location.pathname : undefined;\n\n this.session = {\n id: uuidv4(),\n startedAt: now,\n lastActivityAt: now,\n screenViewCount: 0,\n entryPath: path,\n entryReferrer: referrer,\n };\n\n this.log('New session created', { sessionId: this.session.id });\n this.trackSessionStart();\n }\n\n // Update last activity\n this.session.lastActivityAt = now;\n this.saveSession();\n\n return this.session;\n }\n\n /**\n * Get the current session ID (or null if no active session)\n */\n getSessionId(): string | null {\n return this.session?.id ?? null;\n }\n\n /**\n * Get the current session data\n */\n getSession(): Session | null {\n return this.session ? { ...this.session } : null;\n }\n\n /**\n * Track session start event\n */\n private trackSessionStart(): void {\n if (!this.session) return;\n\n const utmParams = this.getUtmParams();\n\n this.track({\n channel: ANALYTICS_CHANNEL,\n event: 'session_start',\n tags: {\n __session_id: this.session.id,\n __entry_path: this.session.entryPath ?? '',\n __referrer: this.session.entryReferrer ?? '',\n ...utmParams,\n },\n }).catch((err) => this.log('Failed to track session_start', err));\n }\n\n /**\n * End the current session (clears local state only - server calculates metrics)\n */\n private endSession(): void {\n if (!this.session) return;\n\n this.log('Session ended', { sessionId: this.session.id });\n\n // Clear session from storage (no event sent - server calculates metrics)\n if (this.storage) {\n this.storage.removeItem(this.sessionStorageKey);\n }\n this.session = null;\n }\n\n /**\n * Setup listeners for session lifecycle management\n */\n private setupUnloadListener(): void {\n if (typeof window === 'undefined' || this.unloadListenerAdded) return;\n\n // On visibility change: save session state\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') {\n this.saveSession();\n this.log('Page hidden, session state saved');\n }\n });\n\n // On page unload: end session (clears local state only)\n window.addEventListener('pagehide', () => {\n this.endSession();\n this.log('Page unloading, session ended locally');\n });\n\n this.unloadListenerAdded = true;\n this.log('Session lifecycle listeners added');\n }\n\n /**\n * Get UTM parameters from URL\n */\n private getUtmParams(): Tags {\n if (typeof window === 'undefined') return {};\n\n const params = new URLSearchParams(window.location.search);\n const utmParams: Tags = {};\n\n const utmKeys = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'];\n for (const key of utmKeys) {\n const value = params.get(key);\n if (value) {\n utmParams[`__${key}`] = value;\n }\n }\n\n return utmParams;\n }\n\n /**\n * Track a page view\n *\n * @param options - Page view options\n * @returns Promise resolving to the track response\n *\n * @example\n * ```typescript\n * // Track current page\n * await kitbase.trackPageView();\n *\n * // Track with custom path\n * await kitbase.trackPageView({ path: '/products/123', title: 'Product Details' });\n * ```\n */\n async trackPageView(options: PageViewOptions = {}): Promise<TrackResponse> {\n const session = this.getOrCreateSession();\n session.screenViewCount++;\n this.saveSession();\n\n const path = options.path ?? (typeof window !== 'undefined' ? window.location.pathname : '');\n const title = options.title ?? (typeof document !== 'undefined' ? document.title : '');\n const referrer = options.referrer ?? (typeof document !== 'undefined' ? document.referrer : '');\n\n return this.track({\n channel: ANALYTICS_CHANNEL,\n event: 'screen_view',\n tags: {\n __session_id: session.id,\n __path: path,\n __title: title,\n __referrer: referrer,\n ...this.getUtmParams(),\n ...(options.tags ?? {}),\n },\n });\n }\n\n /**\n * Enable automatic page view tracking\n * Intercepts browser history changes (pushState, replaceState, popstate)\n *\n * @example\n * ```typescript\n * kitbase.enableAutoPageViews();\n * // Now all route changes will automatically be tracked\n * ```\n */\n enableAutoPageViews(): void {\n if (typeof window === 'undefined') {\n this.log('Auto page views not available in non-browser environment');\n return;\n }\n\n // Track initial page view\n this.trackPageView().catch((err) => this.log('Failed to track initial page view', err));\n\n // Intercept pushState\n const originalPushState = history.pushState.bind(history);\n history.pushState = (...args) => {\n originalPushState(...args);\n this.trackPageView().catch((err) => this.log('Failed to track page view (pushState)', err));\n };\n\n // Intercept replaceState\n const originalReplaceState = history.replaceState.bind(history);\n history.replaceState = (...args) => {\n originalReplaceState(...args);\n // Don't track replaceState as it's usually not a navigation\n };\n\n // Listen to popstate (browser back/forward)\n window.addEventListener('popstate', () => {\n this.trackPageView().catch((err) => this.log('Failed to track page view (popstate)', err));\n });\n\n this.log('Auto page view tracking enabled');\n }\n\n /**\n * Track a revenue event\n *\n * @param options - Revenue options\n * @returns Promise resolving to the track response\n *\n * @example\n * ```typescript\n * // Track a $19.99 purchase\n * await kitbase.trackRevenue({\n * amount: 1999,\n * currency: 'USD',\n * tags: { product_id: 'prod_123', plan: 'premium' },\n * });\n * ```\n */\n async trackRevenue(options: RevenueOptions): Promise<TrackResponse> {\n const session = this.getOrCreateSession();\n\n return this.track({\n channel: ANALYTICS_CHANNEL,\n event: 'revenue',\n user_id: options.user_id ?? this.userId ?? undefined,\n tags: {\n __session_id: session.id,\n __revenue: options.amount,\n __currency: options.currency ?? 'USD',\n ...(options.tags ?? {}),\n },\n });\n }\n\n /**\n * Identify a user\n * Links the current anonymous ID to a user ID for future events\n *\n * @param options - Identify options\n *\n * @example\n * ```typescript\n * kitbase.identify({\n * userId: 'user_123',\n * traits: { email: 'user@example.com', plan: 'premium' },\n * });\n * ```\n */\n identify(options: IdentifyOptions): void {\n this.userId = options.userId;\n\n // Register user traits as super properties\n if (options.traits) {\n this.register({\n __user_id: options.userId,\n ...options.traits,\n });\n } else {\n this.register({ __user_id: options.userId });\n }\n\n // Track identify event\n this.track({\n channel: ANALYTICS_CHANNEL,\n event: 'identify',\n user_id: options.userId,\n tags: {\n __session_id: this.session?.id ?? '',\n __anonymous_id: this.anonymousId ?? '',\n ...(options.traits ?? {}),\n },\n }).catch((err) => this.log('Failed to track identify', err));\n\n this.log('User identified', { userId: options.userId });\n }\n\n /**\n * Get the current user ID (set via identify)\n */\n getUserId(): string | null {\n return this.userId;\n }\n\n /**\n * Reset the user identity and session\n * Call this when a user logs out\n *\n * @example\n * ```typescript\n * kitbase.reset();\n * ```\n */\n reset(): void {\n // End current session\n if (this.session) {\n this.endSession();\n this.session = null;\n }\n\n // Clear user ID\n this.userId = null;\n\n // Generate new anonymous ID\n this.anonymousId = uuidv4();\n if (this.storage) {\n this.storage.setItem(this.storageKey, this.anonymousId);\n }\n\n // Clear super properties\n this.clearSuperProperties();\n\n this.log('User reset complete');\n }\n\n // ============================================================\n // Cleanup\n // ============================================================\n\n /**\n * Shutdown the client and cleanup resources\n * Call this when you're done using the client to stop timers and close connections\n *\n * @example\n * ```typescript\n * await kitbase.shutdown();\n * ```\n */\n async shutdown(): Promise<void> {\n this.log('Shutting down');\n\n // Flush any remaining queued events\n if (this.queue) {\n await this.queue.flush();\n await this.queue.close();\n this.queue = null;\n }\n\n // Clear timed events\n this.timedEvents.clear();\n\n this.log('Shutdown complete');\n }\n}\n","/**\n * Base error class for Kitbase SDK errors\n */\nexport class KitbaseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'KitbaseError';\n Object.setPrototypeOf(this, KitbaseError.prototype);\n }\n}\n\n/**\n * Error thrown when API authentication fails\n */\nexport class AuthenticationError extends KitbaseError {\n constructor(message = 'Invalid API key') {\n super(message);\n this.name = 'AuthenticationError';\n Object.setPrototypeOf(this, AuthenticationError.prototype);\n }\n}\n\n/**\n * Error thrown when the API request fails\n */\nexport class ApiError extends KitbaseError {\n public readonly statusCode: number;\n public readonly response?: unknown;\n\n constructor(message: string, statusCode: number, response?: unknown) {\n super(message);\n this.name = 'ApiError';\n this.statusCode = statusCode;\n this.response = response;\n Object.setPrototypeOf(this, ApiError.prototype);\n }\n}\n\n/**\n * Error thrown when request validation fails\n */\nexport class ValidationError extends KitbaseError {\n public readonly field?: string;\n\n constructor(message: string, field?: string) {\n super(message);\n this.name = 'ValidationError';\n this.field = field;\n Object.setPrototypeOf(this, ValidationError.prototype);\n }\n}\n\n/**\n * Error thrown when a request times out\n */\nexport class TimeoutError extends KitbaseError {\n constructor(message = 'Request timed out') {\n super(message);\n this.name = 'TimeoutError';\n Object.setPrototypeOf(this, TimeoutError.prototype);\n }\n}\n\n\n\n\n\n\n\n\n","import Dexie, { type Table } from 'dexie';\nimport type { LogPayload } from '../types.js';\nimport type {\n OfflineConfig,\n QueuedEvent,\n QueueStats,\n EventQueueInterface,\n SendEventsCallback,\n} from './types.js';\n\nconst DEFAULT_CONFIG: Required<OfflineConfig> = {\n enabled: false,\n maxQueueSize: 1000,\n flushInterval: 30000,\n flushBatchSize: 50,\n maxRetries: 3,\n retryBaseDelay: 1000,\n};\n\n/**\n * Check if IndexedDB is available\n */\nfunction isIndexedDBAvailable(): boolean {\n try {\n return (\n typeof window !== 'undefined' &&\n typeof window.indexedDB !== 'undefined' &&\n window.indexedDB !== null\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Check if we're in a browser environment\n */\nfunction isBrowser(): boolean {\n return typeof window !== 'undefined' && typeof document !== 'undefined';\n}\n\n/**\n * Dexie database for event queue\n */\nclass KitbaseQueueDB extends Dexie {\n events!: Table<QueuedEvent, number>;\n\n constructor(dbName: string) {\n super(dbName);\n this.version(1).stores({\n events: '++id, timestamp, retries, lastAttempt',\n });\n }\n}\n\n/**\n * In-memory queue implementation for Node.js or when IndexedDB is unavailable\n */\nclass MemoryQueue {\n private queue: QueuedEvent[] = [];\n private idCounter = 1;\n\n async enqueue(payload: LogPayload): Promise<number> {\n const event: QueuedEvent = {\n id: this.idCounter++,\n payload,\n timestamp: Date.now(),\n retries: 0,\n };\n this.queue.push(event);\n return event.id!;\n }\n\n async dequeue(count: number): Promise<QueuedEvent[]> {\n // Sort by timestamp (oldest first) and get the first `count` events\n this.queue.sort((a, b) => a.timestamp - b.timestamp);\n return this.queue.slice(0, count);\n }\n\n async delete(ids: number[]): Promise<void> {\n this.queue = this.queue.filter((e) => !ids.includes(e.id!));\n }\n\n async updateRetries(ids: number[]): Promise<void> {\n const now = Date.now();\n for (const event of this.queue) {\n if (ids.includes(event.id!)) {\n event.retries++;\n event.lastAttempt = now;\n }\n }\n }\n\n async getStats(): Promise<{ size: number; oldestEvent?: number }> {\n const size = this.queue.length;\n const oldestEvent =\n size > 0\n ? Math.min(...this.queue.map((e) => e.timestamp))\n : undefined;\n return { size, oldestEvent };\n }\n\n async clear(): Promise<void> {\n this.queue = [];\n }\n\n async enforceMaxSize(maxSize: number): Promise<void> {\n if (this.queue.length > maxSize) {\n // Sort by timestamp and keep only the newest events\n this.queue.sort((a, b) => a.timestamp - b.timestamp);\n this.queue = this.queue.slice(-maxSize);\n }\n }\n\n async getEventsExceedingRetries(maxRetries: number): Promise<number[]> {\n return this.queue\n .filter((e) => e.retries >= maxRetries)\n .map((e) => e.id!);\n }\n}\n\n/**\n * Event queue for offline support\n * Uses IndexedDB (via Dexie) in browser, in-memory queue in Node.js\n */\nexport class EventQueue implements EventQueueInterface {\n private readonly config: Required<OfflineConfig>;\n private readonly dbName: string;\n private db: KitbaseQueueDB | null = null;\n private memoryQueue: MemoryQueue | null = null;\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private isFlushing = false;\n private sendEvents: SendEventsCallback | null = null;\n private readonly useIndexedDB: boolean;\n private debugMode = false;\n private debugLogger: ((message: string, data?: unknown) => void) | null = null;\n\n constructor(config: OfflineConfig = {}, dbName = 'kitbase-events') {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.dbName = dbName;\n this.useIndexedDB = isIndexedDBAvailable();\n\n if (this.useIndexedDB) {\n this.db = new KitbaseQueueDB(this.dbName);\n } else {\n this.memoryQueue = new MemoryQueue();\n }\n }\n\n /**\n * Set debug mode and logger\n */\n setDebugMode(enabled: boolean, logger?: (message: string, data?: unknown) => void): void {\n this.debugMode = enabled;\n this.debugLogger = logger ?? null;\n }\n\n private log(message: string, data?: unknown): void {\n if (this.debugMode && this.debugLogger) {\n this.debugLogger(message, data);\n }\n }\n\n /**\n * Set the callback for sending events\n */\n setSendCallback(callback: SendEventsCallback): void {\n this.sendEvents = callback;\n }\n\n /**\n * Check if the queue storage is available\n */\n isAvailable(): boolean {\n return this.useIndexedDB || this.memoryQueue !== null;\n }\n\n /**\n * Get the storage type being used\n */\n getStorageType(): 'indexeddb' | 'memory' {\n return this.useIndexedDB ? 'indexeddb' : 'memory';\n }\n\n /**\n * Add an event to the queue\n */\n async enqueue(payload: LogPayload): Promise<void> {\n const event: Omit<QueuedEvent, 'id'> = {\n payload,\n timestamp: Date.now(),\n retries: 0,\n };\n\n if (this.useIndexedDB && this.db) {\n await this.db.events.add(event as QueuedEvent);\n this.log('Event queued to IndexedDB', payload);\n } else if (this.memoryQueue) {\n await this.memoryQueue.enqueue(payload);\n this.log('Event queued to memory', payload);\n }\n\n // Enforce max queue size\n await this.enforceMaxQueueSize();\n }\n\n /**\n * Get and remove the next batch of events to send\n */\n async dequeue(count: number): Promise<QueuedEvent[]> {\n if (this.useIndexedDB && this.db) {\n // Get events sorted by timestamp (oldest first), excluding those with too many retries\n return this.db.events\n .where('retries')\n .below(this.config.maxRetries)\n .sortBy('timestamp')\n .then((events) => events.slice(0, count));\n } else if (this.memoryQueue) {\n const events = await this.memoryQueue.dequeue(count);\n return events.filter((e) => e.retries < this.config.maxRetries);\n }\n return [];\n }\n\n /**\n * Mark events as successfully sent (remove from queue)\n */\n async markSent(ids: number[]): Promise<void> {\n if (ids.length === 0) return;\n\n if (this.useIndexedDB && this.db) {\n await this.db.events.bulkDelete(ids);\n this.log(`Removed ${ids.length} sent events from queue`);\n } else if (this.memoryQueue) {\n await this.memoryQueue.delete(ids);\n this.log(`Removed ${ids.length} sent events from memory queue`);\n }\n }\n\n /**\n * Mark events as failed and increment retry count\n */\n async markFailed(ids: number[]): Promise<void> {\n if (ids.length === 0) return;\n\n const now = Date.now();\n\n if (this.useIndexedDB && this.db) {\n await this.db.transaction('rw', this.db.events, async () => {\n for (const id of ids) {\n const event = await this.db!.events.get(id);\n if (event) {\n await this.db!.events.update(id, {\n retries: event.retries + 1,\n lastAttempt: now,\n });\n }\n }\n });\n this.log(`Marked ${ids.length} events as failed`);\n } else if (this.memoryQueue) {\n await this.memoryQueue.updateRetries(ids);\n this.log(`Marked ${ids.length} events as failed in memory queue`);\n }\n\n // Remove events that have exceeded max retries\n await this.removeExpiredRetries();\n }\n\n /**\n * Remove events that have exceeded max retry attempts\n */\n private async removeExpiredRetries(): Promise<void> {\n if (this.useIndexedDB && this.db) {\n const expiredIds = await this.db.events\n .where('retries')\n .aboveOrEqual(this.config.maxRetries)\n .primaryKeys();\n if (expiredIds.length > 0) {\n await this.db.events.bulkDelete(expiredIds);\n this.log(`Removed ${expiredIds.length} events that exceeded max retries`);\n }\n } else if (this.memoryQueue) {\n const expiredIds = await this.memoryQueue.getEventsExceedingRetries(\n this.config.maxRetries\n );\n if (expiredIds.length > 0) {\n await this.memoryQueue.delete(expiredIds);\n this.log(`Removed ${expiredIds.length} events that exceeded max retries`);\n }\n }\n }\n\n /**\n * Enforce the maximum queue size by removing oldest events\n */\n private async enforceMaxQueueSize(): Promise<void> {\n if (this.useIndexedDB && this.db) {\n const count = await this.db.events.count();\n if (count > this.config.maxQueueSize) {\n const excess = count - this.config.maxQueueSize;\n const oldestEvents = await this.db.events\n .orderBy('timestamp')\n .limit(excess)\n .primaryKeys();\n await this.db.events.bulkDelete(oldestEvents);\n this.log(`Removed ${excess} oldest events to enforce queue size limit`);\n }\n } else if (this.memoryQueue) {\n await this.memoryQueue.enforceMaxSize(this.config.maxQueueSize);\n }\n }\n\n /**\n * Get queue statistics\n */\n async getStats(): Promise<QueueStats> {\n if (this.useIndexedDB && this.db) {\n const size = await this.db.events.count();\n const oldestEvent = await this.db.events\n .orderBy('timestamp')\n .first()\n .then((e) => e?.timestamp);\n return { size, oldestEvent, isFlushing: this.isFlushing };\n } else if (this.memoryQueue) {\n const stats = await this.memoryQueue.getStats();\n return { ...stats, isFlushing: this.isFlushing };\n }\n return { size: 0, isFlushing: this.isFlushing };\n }\n\n /**\n * Clear all events from the queue\n */\n async clear(): Promise<void> {\n if (this.useIndexedDB && this.db) {\n await this.db.events.clear();\n this.log('Queue cleared (IndexedDB)');\n } else if (this.memoryQueue) {\n await this.memoryQueue.clear();\n this.log('Queue cleared (memory)');\n }\n }\n\n /**\n * Start the automatic flush timer\n */\n startFlushTimer(): void {\n if (this.flushTimer) return;\n\n this.flushTimer = setInterval(() => {\n this.flush().catch((err) => {\n this.log('Flush timer error', err);\n });\n }, this.config.flushInterval);\n\n // Also listen for online events in browser\n if (isBrowser()) {\n window.addEventListener('online', this.handleOnline);\n }\n\n this.log(`Flush timer started (interval: ${this.config.flushInterval}ms)`);\n }\n\n /**\n * Stop the automatic flush timer\n */\n stopFlushTimer(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n if (isBrowser()) {\n window.removeEventListener('online', this.handleOnline);\n }\n\n this.log('Flush timer stopped');\n }\n\n /**\n * Handle coming back online\n */\n private handleOnline = (): void => {\n this.log('Browser came online, triggering flush');\n this.flush().catch((err) => {\n this.log('Online flush error', err);\n });\n };\n\n /**\n * Check if we're currently online\n */\n private isOnline(): boolean {\n if (isBrowser()) {\n return navigator.onLine;\n }\n // Assume online in Node.js\n return true;\n }\n\n /**\n * Manually trigger a flush of queued events\n */\n async flush(): Promise<void> {\n if (this.isFlushing) {\n this.log('Flush already in progress, skipping');\n return;\n }\n\n if (!this.isOnline()) {\n this.log('Offline, skipping flush');\n return;\n }\n\n if (!this.sendEvents) {\n this.log('No send callback configured, skipping flush');\n return;\n }\n\n this.isFlushing = true;\n\n try {\n const stats = await this.getStats();\n if (stats.size === 0) {\n this.log('Queue is empty, nothing to flush');\n return;\n }\n\n this.log(`Flushing queue (${stats.size} events)`);\n\n // Process in batches\n let processed = 0;\n while (true) {\n const events = await this.dequeue(this.config.flushBatchSize);\n if (events.length === 0) break;\n\n this.log(`Sending batch of ${events.length} events`);\n\n try {\n const sentIds = await this.sendEvents(events);\n await this.markSent(sentIds);\n\n // Mark remaining as failed\n const failedIds = events\n .filter((e) => !sentIds.includes(e.id!))\n .map((e) => e.id!);\n if (failedIds.length > 0) {\n await this.markFailed(failedIds);\n }\n\n processed += sentIds.length;\n } catch (error) {\n // Mark all as failed on network error\n const allIds = events.map((e) => e.id!);\n await this.markFailed(allIds);\n this.log('Batch send failed', error);\n break; // Stop flushing on error\n }\n }\n\n this.log(`Flush complete, sent ${processed} events`);\n } finally {\n this.isFlushing = false;\n }\n }\n\n /**\n * Close the database connection\n */\n async close(): Promise<void> {\n this.stopFlushTimer();\n if (this.db) {\n this.db.close();\n this.log('Database connection closed');\n }\n }\n}\n\nexport type { OfflineConfig, QueuedEvent, QueueStats, EventQueueInterface, SendEventsCallback };\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/client.ts","../src/errors.ts","../src/queue/index.ts"],"sourcesContent":["import { v4 as uuidv4 } from 'uuid';\nimport type {\n KitbaseConfig,\n TrackOptions,\n TrackResponse,\n LogPayload,\n Storage,\n Tags,\n Session,\n AnalyticsConfig,\n PageViewOptions,\n RevenueOptions,\n IdentifyOptions,\n} from './types.js';\nimport {\n ApiError,\n AuthenticationError,\n TimeoutError,\n ValidationError,\n} from './errors.js';\nimport { EventQueue } from './queue/index.js';\nimport type { QueuedEvent, QueueStats } from './queue/types.js';\n\nconst DEFAULT_BASE_URL = 'https://api.kitbase.dev';\nconst TIMEOUT = 30000;\nconst DEFAULT_STORAGE_KEY = 'kitbase_anonymous_id';\nconst DEFAULT_SESSION_STORAGE_KEY = 'kitbase_session';\nconst DEFAULT_SESSION_TIMEOUT = 30 * 60 * 1000; // 30 minutes\nconst ANALYTICS_CHANNEL = '__analytics';\n\n/**\n * In-memory storage fallback for non-browser environments\n */\nclass MemoryStorage implements Storage {\n private data: Map<string, string> = new Map();\n\n getItem(key: string): string | null {\n return this.data.get(key) ?? null;\n }\n\n setItem(key: string, value: string): void {\n this.data.set(key, value);\n }\n\n removeItem(key: string): void {\n this.data.delete(key);\n }\n}\n\n/**\n * Get the default storage (localStorage in browser, memory otherwise)\n */\nfunction getDefaultStorage(): Storage {\n if (typeof window !== 'undefined' && window.localStorage) {\n return window.localStorage;\n }\n return new MemoryStorage();\n}\n\n/**\n * Kitbase client for tracking events\n *\n * @example\n * ```typescript\n * import { Kitbase } from '@kitbase/sdk/events';\n *\n * const kitbase = new Kitbase({\n * token: '<YOUR_API_KEY>',\n * debug: true,\n * offline: { enabled: true },\n * });\n *\n * // Register super properties (included in all events)\n * kitbase.register({ app_version: '2.1.0', platform: 'web' });\n *\n * // Track anonymous events (anonymous_id is automatically included)\n * await kitbase.track({\n * channel: 'payments',\n * event: 'Page Viewed',\n * icon: '👀',\n * });\n *\n * // Time events for duration tracking\n * kitbase.timeEvent('Video Watched');\n * // ... later\n * await kitbase.track({\n * channel: 'engagement',\n * event: 'Video Watched', // $duration automatically included\n * });\n *\n * // Track events for a logged-in user (just pass user_id)\n * await kitbase.track({\n * channel: 'payments',\n * event: 'New Subscription',\n * user_id: 'user-123',\n * icon: '💰',\n * notify: true,\n * tags: {\n * plan: 'premium',\n * cycle: 'monthly',\n * },\n * });\n * ```\n */\nexport class Kitbase {\n private readonly token: string;\n private readonly baseUrl: string;\n private readonly storage: Storage | null;\n private readonly storageKey: string;\n private anonymousId: string | null = null;\n\n // Super properties (memory-only, merged into all events)\n private superProperties: Tags = {};\n\n // Time event tracking\n private timedEvents: Map<string, number> = new Map();\n\n // Debug mode\n private debugMode: boolean;\n\n // Offline queue\n private queue: EventQueue | null = null;\n private offlineEnabled: boolean;\n\n // Analytics & Session tracking\n private session: Session | null = null;\n private sessionTimeout: number;\n private sessionStorageKey: string;\n private analyticsEnabled: boolean;\n private autoTrackPageViews: boolean;\n private userId: string | null = null;\n private unloadListenerAdded = false;\n\n constructor(config: KitbaseConfig) {\n if (!config.token) {\n throw new ValidationError('API token is required', 'token');\n }\n\n this.token = config.token;\n // Remove trailing slashes to prevent double-slash in URLs\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n this.storageKey = config.storageKey ?? DEFAULT_STORAGE_KEY;\n this.debugMode = config.debug ?? false;\n\n // Initialize storage (null means disabled)\n if (config.storage === null) {\n this.storage = null;\n } else {\n this.storage = config.storage ?? getDefaultStorage();\n }\n\n // Load or generate anonymous ID\n this.initializeAnonymousId();\n\n // Initialize analytics configuration\n this.sessionTimeout = config.analytics?.sessionTimeout ?? DEFAULT_SESSION_TIMEOUT;\n this.sessionStorageKey = config.analytics?.sessionStorageKey ?? DEFAULT_SESSION_STORAGE_KEY;\n this.analyticsEnabled = config.analytics?.autoTrackSessions ?? true;\n this.autoTrackPageViews = config.analytics?.autoTrackPageViews ?? false;\n\n // Load existing session from storage\n if (this.analyticsEnabled) {\n this.loadSession();\n this.setupUnloadListener();\n\n // Auto-track pageviews if enabled\n if (this.autoTrackPageViews && typeof window !== 'undefined') {\n this.enableAutoPageViews();\n }\n }\n\n // Initialize offline queue if enabled\n this.offlineEnabled = config.offline?.enabled ?? false;\n if (this.offlineEnabled) {\n this.queue = new EventQueue(config.offline);\n this.queue.setDebugMode(this.debugMode, this.log.bind(this));\n this.queue.setSendCallback(this.sendQueuedEvents.bind(this));\n this.queue.startFlushTimer();\n this.log('Offline queueing enabled', {\n storageType: this.queue.getStorageType(),\n });\n }\n }\n\n /**\n * Initialize the anonymous ID from storage or generate a new one\n */\n private initializeAnonymousId(): void {\n if (this.storage) {\n const stored = this.storage.getItem(this.storageKey);\n if (stored) {\n this.anonymousId = stored;\n return;\n }\n }\n\n // Generate new anonymous ID\n this.anonymousId = uuidv4();\n\n // Persist if storage is available\n if (this.storage && this.anonymousId) {\n this.storage.setItem(this.storageKey, this.anonymousId);\n }\n }\n\n /**\n * Get the current anonymous ID\n */\n getAnonymousId(): string | null {\n return this.anonymousId;\n }\n\n // ============================================================\n // Debug Mode\n // ============================================================\n\n /**\n * Enable or disable debug mode\n * When enabled, all SDK operations are logged to the console\n *\n * @param enabled - Whether to enable debug mode\n *\n * @example\n * ```typescript\n * kitbase.setDebugMode(true);\n * // All events and operations will now be logged\n * ```\n */\n setDebugMode(enabled: boolean): void {\n this.debugMode = enabled;\n if (this.queue) {\n this.queue.setDebugMode(enabled, this.log.bind(this));\n }\n this.log(`Debug mode ${enabled ? 'enabled' : 'disabled'}`);\n }\n\n /**\n * Check if debug mode is enabled\n */\n isDebugMode(): boolean {\n return this.debugMode;\n }\n\n /**\n * Internal logging function\n */\n private log(message: string, data?: unknown): void {\n if (!this.debugMode) return;\n\n const prefix = '[Kitbase]';\n if (data !== undefined) {\n console.log(prefix, message, data);\n } else {\n console.log(prefix, message);\n }\n }\n\n // ============================================================\n // Super Properties\n // ============================================================\n\n /**\n * Register super properties that will be included with every event\n * These properties are stored in memory only and reset on page reload\n *\n * @param properties - Properties to register\n *\n * @example\n * ```typescript\n * kitbase.register({\n * app_version: '2.1.0',\n * platform: 'web',\n * environment: 'production',\n * });\n * ```\n */\n register(properties: Tags): void {\n this.superProperties = { ...this.superProperties, ...properties };\n this.log('Super properties registered', properties);\n }\n\n /**\n * Register super properties only if they haven't been set yet\n * Useful for setting default values that shouldn't override existing ones\n *\n * @param properties - Properties to register if not already set\n *\n * @example\n * ```typescript\n * kitbase.registerOnce({ first_visit: new Date().toISOString() });\n * ```\n */\n registerOnce(properties: Tags): void {\n const newProps: Tags = {};\n for (const [key, value] of Object.entries(properties)) {\n if (!(key in this.superProperties)) {\n newProps[key] = value;\n }\n }\n if (Object.keys(newProps).length > 0) {\n this.superProperties = { ...this.superProperties, ...newProps };\n this.log('Super properties registered (once)', newProps);\n }\n }\n\n /**\n * Remove a super property\n *\n * @param key - The property key to remove\n *\n * @example\n * ```typescript\n * kitbase.unregister('platform');\n * ```\n */\n unregister(key: string): void {\n if (key in this.superProperties) {\n delete this.superProperties[key];\n this.log('Super property removed', { key });\n }\n }\n\n /**\n * Get all registered super properties\n *\n * @returns A copy of the current super properties\n *\n * @example\n * ```typescript\n * const props = kitbase.getSuperProperties();\n * console.log(props); // { app_version: '2.1.0', platform: 'web' }\n * ```\n */\n getSuperProperties(): Tags {\n return { ...this.superProperties };\n }\n\n /**\n * Clear all super properties\n *\n * @example\n * ```typescript\n * kitbase.clearSuperProperties();\n * ```\n */\n clearSuperProperties(): void {\n this.superProperties = {};\n this.log('Super properties cleared');\n }\n\n // ============================================================\n // Time Events (Duration Tracking)\n // ============================================================\n\n /**\n * Start timing an event\n * When the same event is tracked later, a $duration property (in seconds)\n * will automatically be included\n *\n * @param eventName - The name of the event to time\n *\n * @example\n * ```typescript\n * kitbase.timeEvent('Video Watched');\n * // ... user watches video ...\n * await kitbase.track({\n * channel: 'engagement',\n * event: 'Video Watched',\n * tags: { video_id: '123' }\n * });\n * // Event will include $duration: 45.2 (seconds)\n * ```\n */\n timeEvent(eventName: string): void {\n this.timedEvents.set(eventName, Date.now());\n this.log('Timer started', { event: eventName });\n }\n\n /**\n * Cancel a timed event without tracking it\n *\n * @param eventName - The name of the event to cancel timing for\n *\n * @example\n * ```typescript\n * kitbase.timeEvent('Checkout Flow');\n * // User abandons checkout\n * kitbase.cancelTimeEvent('Checkout Flow');\n * ```\n */\n cancelTimeEvent(eventName: string): void {\n if (this.timedEvents.has(eventName)) {\n this.timedEvents.delete(eventName);\n this.log('Timer cancelled', { event: eventName });\n }\n }\n\n /**\n * Get all currently timed events\n *\n * @returns Array of event names that are currently being timed\n *\n * @example\n * ```typescript\n * const timedEvents = kitbase.getTimedEvents();\n * console.log(timedEvents); // ['Video Watched', 'Checkout Flow']\n * ```\n */\n getTimedEvents(): string[] {\n return Array.from(this.timedEvents.keys());\n }\n\n /**\n * Get the duration of a timed event (without stopping it)\n *\n * @param eventName - The name of the event\n * @returns Duration in seconds, or null if not being timed\n */\n getEventDuration(eventName: string): number | null {\n const startTime = this.timedEvents.get(eventName);\n if (startTime === undefined) return null;\n return (Date.now() - startTime) / 1000;\n }\n\n // ============================================================\n // Offline Queue\n // ============================================================\n\n /**\n * Get offline queue statistics\n *\n * @returns Queue statistics including size and flush status\n *\n * @example\n * ```typescript\n * const stats = await kitbase.getQueueStats();\n * console.log(stats); // { size: 5, isFlushing: false }\n * ```\n */\n async getQueueStats(): Promise<QueueStats | null> {\n if (!this.queue) return null;\n return this.queue.getStats();\n }\n\n /**\n * Manually flush the offline queue\n * Events are automatically flushed on interval and when coming back online,\n * but this method can be used to trigger an immediate flush\n *\n * @example\n * ```typescript\n * await kitbase.flushQueue();\n * ```\n */\n async flushQueue(): Promise<void> {\n if (!this.queue) return;\n await this.queue.flush();\n }\n\n /**\n * Clear all events from the offline queue\n *\n * @example\n * ```typescript\n * await kitbase.clearQueue();\n * ```\n */\n async clearQueue(): Promise<void> {\n if (!this.queue) return;\n await this.queue.clear();\n }\n\n /**\n * Callback for the queue to send batched events\n */\n private async sendQueuedEvents(events: QueuedEvent[]): Promise<number[]> {\n const sentIds: number[] = [];\n\n for (const event of events) {\n try {\n await this.sendRequest<TrackResponse>('/sdk/v1/logs', event.payload);\n sentIds.push(event.id!);\n } catch (error) {\n this.log('Failed to send queued event', { id: event.id, error });\n // Continue with next event\n }\n }\n\n return sentIds;\n }\n\n // ============================================================\n // Track Event\n // ============================================================\n\n /**\n * Track an event\n *\n * When offline queueing is enabled, events are always written to the local\n * database first (write-ahead), then sent to the server. This ensures no\n * events are lost if the browser crashes or the network fails.\n *\n * @param options - Event tracking options\n * @returns Promise resolving to the track response\n * @throws {ValidationError} When required fields are missing\n * @throws {AuthenticationError} When the API key is invalid (only when offline disabled)\n * @throws {ApiError} When the API returns an error (only when offline disabled)\n * @throws {TimeoutError} When the request times out (only when offline disabled)\n */\n async track(options: TrackOptions): Promise<TrackResponse> {\n this.validateTrackOptions(options);\n\n // Calculate duration if this event was being timed\n let duration: number | undefined;\n const startTime = this.timedEvents.get(options.event);\n if (startTime !== undefined) {\n duration = (Date.now() - startTime) / 1000;\n this.timedEvents.delete(options.event);\n this.log('Timer stopped', { event: options.event, duration });\n }\n\n // Include anonymous_id unless explicitly disabled\n const includeAnonymousId = options.includeAnonymousId !== false;\n\n // Merge super properties with event tags (event tags take precedence)\n const mergedTags: Tags = {\n ...this.superProperties,\n ...(options.tags ?? {}),\n ...(duration !== undefined ? { $duration: duration } : {}),\n };\n\n const payload: LogPayload = {\n channel: options.channel,\n event: options.event,\n timestamp: Date.now(),\n ...(options.user_id && { user_id: options.user_id }),\n ...(includeAnonymousId && this.anonymousId && { anonymous_id: this.anonymousId }),\n ...(options.icon && { icon: options.icon }),\n ...(options.notify !== undefined && { notify: options.notify }),\n ...(options.description && { description: options.description }),\n ...(Object.keys(mergedTags).length > 0 && { tags: mergedTags }),\n };\n\n this.log('Track', { event: options.event, payload });\n\n // If offline queueing is enabled, use write-ahead pattern\n if (this.queue) {\n // Always write to DB first (guaranteed durability)\n await this.queue.enqueue(payload);\n this.log('Event persisted to queue');\n\n // Trigger an immediate flush attempt (non-blocking)\n this.queue.flush().catch((err) => {\n this.log('Background flush failed', err);\n });\n\n // Return immediately after DB write\n return {\n id: `queued-${Date.now()}`,\n event: options.event,\n timestamp: new Date().toISOString(),\n };\n }\n\n // No offline queue - send directly (original behavior)\n const response = await this.sendRequest<TrackResponse>('/sdk/v1/logs', payload);\n this.log('Event sent successfully', { id: response.id });\n return response;\n }\n\n private validateTrackOptions(options: TrackOptions): void {\n if (!options.event) {\n throw new ValidationError('Event is required', 'event');\n }\n }\n\n /**\n * Send a request to the API\n */\n private async sendRequest<T>(endpoint: string, body: unknown): Promise<T> {\n const url = `${this.baseUrl}${endpoint}`;\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), TIMEOUT);\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-sdk-key': `${this.token}`,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorBody = await this.parseResponseBody(response);\n\n if (response.status === 401) {\n throw new AuthenticationError();\n }\n\n throw new ApiError(\n this.getErrorMessage(errorBody, response.statusText),\n response.status,\n errorBody,\n );\n }\n\n return (await response.json()) as T;\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof Error && error.name === 'AbortError') {\n throw new TimeoutError();\n }\n\n throw error;\n }\n }\n\n private async parseResponseBody(response: Response): Promise<unknown> {\n try {\n return await response.json();\n } catch {\n return null;\n }\n }\n\n private getErrorMessage(body: unknown, fallback: string): string {\n if (body && typeof body === 'object' && 'message' in body) {\n return String((body as { message: unknown }).message);\n }\n if (body && typeof body === 'object' && 'error' in body) {\n return String((body as { error: unknown }).error);\n }\n return fallback;\n }\n\n // ============================================================\n // Analytics & Session Management\n // ============================================================\n\n /**\n * Load session from storage\n */\n private loadSession(): void {\n if (!this.storage) return;\n\n try {\n const stored = this.storage.getItem(this.sessionStorageKey);\n if (stored) {\n const session = JSON.parse(stored) as Session;\n const now = Date.now();\n\n // Check if session is still valid (not expired)\n if (now - session.lastActivityAt < this.sessionTimeout) {\n this.session = session;\n this.log('Session restored', { sessionId: session.id });\n } else {\n // Session expired, will create new one on next activity\n this.storage.removeItem(this.sessionStorageKey);\n this.log('Session expired, removed from storage');\n }\n }\n } catch (error) {\n this.log('Failed to load session from storage', error);\n }\n }\n\n /**\n * Save session to storage\n */\n private saveSession(): void {\n if (!this.storage || !this.session) return;\n\n try {\n this.storage.setItem(this.sessionStorageKey, JSON.stringify(this.session));\n } catch (error) {\n this.log('Failed to save session to storage', error);\n }\n }\n\n /**\n * Get or create a session\n */\n private getOrCreateSession(): Session {\n const now = Date.now();\n\n // Check if session expired\n if (this.session && (now - this.session.lastActivityAt) > this.sessionTimeout) {\n this.endSession();\n this.session = null;\n }\n\n // Create new session if needed\n if (!this.session) {\n const referrer = typeof document !== 'undefined' ? document.referrer : undefined;\n const path = typeof window !== 'undefined' ? window.location.pathname : undefined;\n\n this.session = {\n id: uuidv4(),\n startedAt: now,\n lastActivityAt: now,\n screenViewCount: 0,\n entryPath: path,\n entryReferrer: referrer,\n };\n\n this.log('New session created', { sessionId: this.session.id });\n this.trackSessionStart();\n }\n\n // Update last activity\n this.session.lastActivityAt = now;\n this.saveSession();\n\n return this.session;\n }\n\n /**\n * Get the current session ID (or null if no active session)\n */\n getSessionId(): string | null {\n return this.session?.id ?? null;\n }\n\n /**\n * Get the current session data\n */\n getSession(): Session | null {\n return this.session ? { ...this.session } : null;\n }\n\n /**\n * Track session start event\n */\n private trackSessionStart(): void {\n if (!this.session) return;\n\n const utmParams = this.getUtmParams();\n\n this.track({\n channel: ANALYTICS_CHANNEL,\n event: 'session_start',\n tags: {\n __session_id: this.session.id,\n __path: this.session.entryPath ?? '', // For path column in DB\n __entry_path: this.session.entryPath ?? '', // For semantic clarity\n __referrer: this.session.entryReferrer ?? '',\n ...utmParams,\n },\n }).catch((err) => this.log('Failed to track session_start', err));\n }\n\n /**\n * End the current session (clears local state only - server calculates metrics)\n */\n private endSession(): void {\n if (!this.session) return;\n\n this.log('Session ended', { sessionId: this.session.id });\n\n // Clear session from storage (no event sent - server calculates metrics)\n if (this.storage) {\n this.storage.removeItem(this.sessionStorageKey);\n }\n this.session = null;\n }\n\n /**\n * Setup listeners for session lifecycle management\n */\n private setupUnloadListener(): void {\n if (typeof window === 'undefined' || this.unloadListenerAdded) return;\n\n // On visibility change: save session state\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') {\n this.saveSession();\n this.log('Page hidden, session state saved');\n }\n });\n\n // On page unload: end session (clears local state only)\n window.addEventListener('pagehide', () => {\n this.endSession();\n this.log('Page unloading, session ended locally');\n });\n\n this.unloadListenerAdded = true;\n this.log('Session lifecycle listeners added');\n }\n\n /**\n * Get UTM parameters from URL\n */\n private getUtmParams(): Tags {\n if (typeof window === 'undefined') return {};\n\n const params = new URLSearchParams(window.location.search);\n const utmParams: Tags = {};\n\n const utmKeys = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'];\n for (const key of utmKeys) {\n const value = params.get(key);\n if (value) {\n utmParams[`__${key}`] = value;\n }\n }\n\n return utmParams;\n }\n\n /**\n * Track a page view\n *\n * @param options - Page view options\n * @returns Promise resolving to the track response\n *\n * @example\n * ```typescript\n * // Track current page\n * await kitbase.trackPageView();\n *\n * // Track with custom path\n * await kitbase.trackPageView({ path: '/products/123', title: 'Product Details' });\n * ```\n */\n async trackPageView(options: PageViewOptions = {}): Promise<TrackResponse> {\n const session = this.getOrCreateSession();\n session.screenViewCount++;\n this.saveSession();\n\n const path = options.path ?? (typeof window !== 'undefined' ? window.location.pathname : '');\n const title = options.title ?? (typeof document !== 'undefined' ? document.title : '');\n const referrer = options.referrer ?? (typeof document !== 'undefined' ? document.referrer : '');\n\n return this.track({\n channel: ANALYTICS_CHANNEL,\n event: 'screen_view',\n tags: {\n __session_id: session.id,\n __path: path,\n __title: title,\n __referrer: referrer,\n ...this.getUtmParams(),\n ...(options.tags ?? {}),\n },\n });\n }\n\n /**\n * Enable automatic page view tracking\n * Intercepts browser history changes (pushState, replaceState, popstate)\n *\n * @example\n * ```typescript\n * kitbase.enableAutoPageViews();\n * // Now all route changes will automatically be tracked\n * ```\n */\n enableAutoPageViews(): void {\n if (typeof window === 'undefined') {\n this.log('Auto page views not available in non-browser environment');\n return;\n }\n\n // Track initial page view\n this.trackPageView().catch((err) => this.log('Failed to track initial page view', err));\n\n // Intercept pushState\n const originalPushState = history.pushState.bind(history);\n history.pushState = (...args) => {\n originalPushState(...args);\n this.trackPageView().catch((err) => this.log('Failed to track page view (pushState)', err));\n };\n\n // Intercept replaceState\n const originalReplaceState = history.replaceState.bind(history);\n history.replaceState = (...args) => {\n originalReplaceState(...args);\n // Don't track replaceState as it's usually not a navigation\n };\n\n // Listen to popstate (browser back/forward)\n window.addEventListener('popstate', () => {\n this.trackPageView().catch((err) => this.log('Failed to track page view (popstate)', err));\n });\n\n this.log('Auto page view tracking enabled');\n }\n\n /**\n * Track a revenue event\n *\n * @param options - Revenue options\n * @returns Promise resolving to the track response\n *\n * @example\n * ```typescript\n * // Track a $19.99 purchase\n * await kitbase.trackRevenue({\n * amount: 1999,\n * currency: 'USD',\n * tags: { product_id: 'prod_123', plan: 'premium' },\n * });\n * ```\n */\n async trackRevenue(options: RevenueOptions): Promise<TrackResponse> {\n const session = this.getOrCreateSession();\n\n return this.track({\n channel: ANALYTICS_CHANNEL,\n event: 'revenue',\n user_id: options.user_id ?? this.userId ?? undefined,\n tags: {\n __session_id: session.id,\n __revenue: options.amount,\n __currency: options.currency ?? 'USD',\n ...(options.tags ?? {}),\n },\n });\n }\n\n /**\n * Identify a user\n * Links the current anonymous ID to a user ID for future events\n *\n * @param options - Identify options\n *\n * @example\n * ```typescript\n * kitbase.identify({\n * userId: 'user_123',\n * traits: { email: 'user@example.com', plan: 'premium' },\n * });\n * ```\n */\n identify(options: IdentifyOptions): void {\n this.userId = options.userId;\n\n // Register user traits as super properties\n if (options.traits) {\n this.register({\n __user_id: options.userId,\n ...options.traits,\n });\n } else {\n this.register({ __user_id: options.userId });\n }\n\n // Track identify event\n this.track({\n channel: ANALYTICS_CHANNEL,\n event: 'identify',\n user_id: options.userId,\n tags: {\n __session_id: this.session?.id ?? '',\n __anonymous_id: this.anonymousId ?? '',\n ...(options.traits ?? {}),\n },\n }).catch((err) => this.log('Failed to track identify', err));\n\n this.log('User identified', { userId: options.userId });\n }\n\n /**\n * Get the current user ID (set via identify)\n */\n getUserId(): string | null {\n return this.userId;\n }\n\n /**\n * Reset the user identity and session\n * Call this when a user logs out\n *\n * @example\n * ```typescript\n * kitbase.reset();\n * ```\n */\n reset(): void {\n // End current session\n if (this.session) {\n this.endSession();\n this.session = null;\n }\n\n // Clear user ID\n this.userId = null;\n\n // Generate new anonymous ID\n this.anonymousId = uuidv4();\n if (this.storage) {\n this.storage.setItem(this.storageKey, this.anonymousId);\n }\n\n // Clear super properties\n this.clearSuperProperties();\n\n this.log('User reset complete');\n }\n\n // ============================================================\n // Cleanup\n // ============================================================\n\n /**\n * Shutdown the client and cleanup resources\n * Call this when you're done using the client to stop timers and close connections\n *\n * @example\n * ```typescript\n * await kitbase.shutdown();\n * ```\n */\n async shutdown(): Promise<void> {\n this.log('Shutting down');\n\n // Flush any remaining queued events\n if (this.queue) {\n await this.queue.flush();\n await this.queue.close();\n this.queue = null;\n }\n\n // Clear timed events\n this.timedEvents.clear();\n\n this.log('Shutdown complete');\n }\n}\n","/**\n * Base error class for Kitbase SDK errors\n */\nexport class KitbaseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'KitbaseError';\n Object.setPrototypeOf(this, KitbaseError.prototype);\n }\n}\n\n/**\n * Error thrown when API authentication fails\n */\nexport class AuthenticationError extends KitbaseError {\n constructor(message = 'Invalid API key') {\n super(message);\n this.name = 'AuthenticationError';\n Object.setPrototypeOf(this, AuthenticationError.prototype);\n }\n}\n\n/**\n * Error thrown when the API request fails\n */\nexport class ApiError extends KitbaseError {\n public readonly statusCode: number;\n public readonly response?: unknown;\n\n constructor(message: string, statusCode: number, response?: unknown) {\n super(message);\n this.name = 'ApiError';\n this.statusCode = statusCode;\n this.response = response;\n Object.setPrototypeOf(this, ApiError.prototype);\n }\n}\n\n/**\n * Error thrown when request validation fails\n */\nexport class ValidationError extends KitbaseError {\n public readonly field?: string;\n\n constructor(message: string, field?: string) {\n super(message);\n this.name = 'ValidationError';\n this.field = field;\n Object.setPrototypeOf(this, ValidationError.prototype);\n }\n}\n\n/**\n * Error thrown when a request times out\n */\nexport class TimeoutError extends KitbaseError {\n constructor(message = 'Request timed out') {\n super(message);\n this.name = 'TimeoutError';\n Object.setPrototypeOf(this, TimeoutError.prototype);\n }\n}\n\n\n\n\n\n\n\n\n","import Dexie, { type Table } from 'dexie';\nimport type { LogPayload } from '../types.js';\nimport type {\n OfflineConfig,\n QueuedEvent,\n QueueStats,\n EventQueueInterface,\n SendEventsCallback,\n} from './types.js';\n\nconst DEFAULT_CONFIG: Required<OfflineConfig> = {\n enabled: false,\n maxQueueSize: 1000,\n flushInterval: 30000,\n flushBatchSize: 50,\n maxRetries: 3,\n retryBaseDelay: 1000,\n};\n\n/**\n * Check if IndexedDB is available\n */\nfunction isIndexedDBAvailable(): boolean {\n try {\n return (\n typeof window !== 'undefined' &&\n typeof window.indexedDB !== 'undefined' &&\n window.indexedDB !== null\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Check if we're in a browser environment\n */\nfunction isBrowser(): boolean {\n return typeof window !== 'undefined' && typeof document !== 'undefined';\n}\n\n/**\n * Dexie database for event queue\n */\nclass KitbaseQueueDB extends Dexie {\n events!: Table<QueuedEvent, number>;\n\n constructor(dbName: string) {\n super(dbName);\n this.version(1).stores({\n events: '++id, timestamp, retries, lastAttempt',\n });\n }\n}\n\n/**\n * In-memory queue implementation for Node.js or when IndexedDB is unavailable\n */\nclass MemoryQueue {\n private queue: QueuedEvent[] = [];\n private idCounter = 1;\n\n async enqueue(payload: LogPayload): Promise<number> {\n const event: QueuedEvent = {\n id: this.idCounter++,\n payload,\n timestamp: Date.now(),\n retries: 0,\n };\n this.queue.push(event);\n return event.id!;\n }\n\n async dequeue(count: number): Promise<QueuedEvent[]> {\n // Sort by timestamp (oldest first) and get the first `count` events\n this.queue.sort((a, b) => a.timestamp - b.timestamp);\n return this.queue.slice(0, count);\n }\n\n async delete(ids: number[]): Promise<void> {\n this.queue = this.queue.filter((e) => !ids.includes(e.id!));\n }\n\n async updateRetries(ids: number[]): Promise<void> {\n const now = Date.now();\n for (const event of this.queue) {\n if (ids.includes(event.id!)) {\n event.retries++;\n event.lastAttempt = now;\n }\n }\n }\n\n async getStats(): Promise<{ size: number; oldestEvent?: number }> {\n const size = this.queue.length;\n const oldestEvent =\n size > 0\n ? Math.min(...this.queue.map((e) => e.timestamp))\n : undefined;\n return { size, oldestEvent };\n }\n\n async clear(): Promise<void> {\n this.queue = [];\n }\n\n async enforceMaxSize(maxSize: number): Promise<void> {\n if (this.queue.length > maxSize) {\n // Sort by timestamp and keep only the newest events\n this.queue.sort((a, b) => a.timestamp - b.timestamp);\n this.queue = this.queue.slice(-maxSize);\n }\n }\n\n async getEventsExceedingRetries(maxRetries: number): Promise<number[]> {\n return this.queue\n .filter((e) => e.retries >= maxRetries)\n .map((e) => e.id!);\n }\n}\n\n/**\n * Event queue for offline support\n * Uses IndexedDB (via Dexie) in browser, in-memory queue in Node.js\n */\nexport class EventQueue implements EventQueueInterface {\n private readonly config: Required<OfflineConfig>;\n private readonly dbName: string;\n private db: KitbaseQueueDB | null = null;\n private memoryQueue: MemoryQueue | null = null;\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private isFlushing = false;\n private sendEvents: SendEventsCallback | null = null;\n private readonly useIndexedDB: boolean;\n private debugMode = false;\n private debugLogger: ((message: string, data?: unknown) => void) | null = null;\n\n constructor(config: OfflineConfig = {}, dbName = 'kitbase-events') {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.dbName = dbName;\n this.useIndexedDB = isIndexedDBAvailable();\n\n if (this.useIndexedDB) {\n this.db = new KitbaseQueueDB(this.dbName);\n } else {\n this.memoryQueue = new MemoryQueue();\n }\n }\n\n /**\n * Set debug mode and logger\n */\n setDebugMode(enabled: boolean, logger?: (message: string, data?: unknown) => void): void {\n this.debugMode = enabled;\n this.debugLogger = logger ?? null;\n }\n\n private log(message: string, data?: unknown): void {\n if (this.debugMode && this.debugLogger) {\n this.debugLogger(message, data);\n }\n }\n\n /**\n * Set the callback for sending events\n */\n setSendCallback(callback: SendEventsCallback): void {\n this.sendEvents = callback;\n }\n\n /**\n * Check if the queue storage is available\n */\n isAvailable(): boolean {\n return this.useIndexedDB || this.memoryQueue !== null;\n }\n\n /**\n * Get the storage type being used\n */\n getStorageType(): 'indexeddb' | 'memory' {\n return this.useIndexedDB ? 'indexeddb' : 'memory';\n }\n\n /**\n * Add an event to the queue\n */\n async enqueue(payload: LogPayload): Promise<void> {\n const event: Omit<QueuedEvent, 'id'> = {\n payload,\n timestamp: Date.now(),\n retries: 0,\n };\n\n if (this.useIndexedDB && this.db) {\n await this.db.events.add(event as QueuedEvent);\n this.log('Event queued to IndexedDB', payload);\n } else if (this.memoryQueue) {\n await this.memoryQueue.enqueue(payload);\n this.log('Event queued to memory', payload);\n }\n\n // Enforce max queue size\n await this.enforceMaxQueueSize();\n }\n\n /**\n * Get and remove the next batch of events to send\n */\n async dequeue(count: number): Promise<QueuedEvent[]> {\n if (this.useIndexedDB && this.db) {\n // Get events sorted by timestamp (oldest first), excluding those with too many retries\n return this.db.events\n .where('retries')\n .below(this.config.maxRetries)\n .sortBy('timestamp')\n .then((events) => events.slice(0, count));\n } else if (this.memoryQueue) {\n const events = await this.memoryQueue.dequeue(count);\n return events.filter((e) => e.retries < this.config.maxRetries);\n }\n return [];\n }\n\n /**\n * Mark events as successfully sent (remove from queue)\n */\n async markSent(ids: number[]): Promise<void> {\n if (ids.length === 0) return;\n\n if (this.useIndexedDB && this.db) {\n await this.db.events.bulkDelete(ids);\n this.log(`Removed ${ids.length} sent events from queue`);\n } else if (this.memoryQueue) {\n await this.memoryQueue.delete(ids);\n this.log(`Removed ${ids.length} sent events from memory queue`);\n }\n }\n\n /**\n * Mark events as failed and increment retry count\n */\n async markFailed(ids: number[]): Promise<void> {\n if (ids.length === 0) return;\n\n const now = Date.now();\n\n if (this.useIndexedDB && this.db) {\n await this.db.transaction('rw', this.db.events, async () => {\n for (const id of ids) {\n const event = await this.db!.events.get(id);\n if (event) {\n await this.db!.events.update(id, {\n retries: event.retries + 1,\n lastAttempt: now,\n });\n }\n }\n });\n this.log(`Marked ${ids.length} events as failed`);\n } else if (this.memoryQueue) {\n await this.memoryQueue.updateRetries(ids);\n this.log(`Marked ${ids.length} events as failed in memory queue`);\n }\n\n // Remove events that have exceeded max retries\n await this.removeExpiredRetries();\n }\n\n /**\n * Remove events that have exceeded max retry attempts\n */\n private async removeExpiredRetries(): Promise<void> {\n if (this.useIndexedDB && this.db) {\n const expiredIds = await this.db.events\n .where('retries')\n .aboveOrEqual(this.config.maxRetries)\n .primaryKeys();\n if (expiredIds.length > 0) {\n await this.db.events.bulkDelete(expiredIds);\n this.log(`Removed ${expiredIds.length} events that exceeded max retries`);\n }\n } else if (this.memoryQueue) {\n const expiredIds = await this.memoryQueue.getEventsExceedingRetries(\n this.config.maxRetries\n );\n if (expiredIds.length > 0) {\n await this.memoryQueue.delete(expiredIds);\n this.log(`Removed ${expiredIds.length} events that exceeded max retries`);\n }\n }\n }\n\n /**\n * Enforce the maximum queue size by removing oldest events\n */\n private async enforceMaxQueueSize(): Promise<void> {\n if (this.useIndexedDB && this.db) {\n const count = await this.db.events.count();\n if (count > this.config.maxQueueSize) {\n const excess = count - this.config.maxQueueSize;\n const oldestEvents = await this.db.events\n .orderBy('timestamp')\n .limit(excess)\n .primaryKeys();\n await this.db.events.bulkDelete(oldestEvents);\n this.log(`Removed ${excess} oldest events to enforce queue size limit`);\n }\n } else if (this.memoryQueue) {\n await this.memoryQueue.enforceMaxSize(this.config.maxQueueSize);\n }\n }\n\n /**\n * Get queue statistics\n */\n async getStats(): Promise<QueueStats> {\n if (this.useIndexedDB && this.db) {\n const size = await this.db.events.count();\n const oldestEvent = await this.db.events\n .orderBy('timestamp')\n .first()\n .then((e) => e?.timestamp);\n return { size, oldestEvent, isFlushing: this.isFlushing };\n } else if (this.memoryQueue) {\n const stats = await this.memoryQueue.getStats();\n return { ...stats, isFlushing: this.isFlushing };\n }\n return { size: 0, isFlushing: this.isFlushing };\n }\n\n /**\n * Clear all events from the queue\n */\n async clear(): Promise<void> {\n if (this.useIndexedDB && this.db) {\n await this.db.events.clear();\n this.log('Queue cleared (IndexedDB)');\n } else if (this.memoryQueue) {\n await this.memoryQueue.clear();\n this.log('Queue cleared (memory)');\n }\n }\n\n /**\n * Start the automatic flush timer\n */\n startFlushTimer(): void {\n if (this.flushTimer) return;\n\n this.flushTimer = setInterval(() => {\n this.flush().catch((err) => {\n this.log('Flush timer error', err);\n });\n }, this.config.flushInterval);\n\n // Also listen for online events in browser\n if (isBrowser()) {\n window.addEventListener('online', this.handleOnline);\n }\n\n this.log(`Flush timer started (interval: ${this.config.flushInterval}ms)`);\n }\n\n /**\n * Stop the automatic flush timer\n */\n stopFlushTimer(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n if (isBrowser()) {\n window.removeEventListener('online', this.handleOnline);\n }\n\n this.log('Flush timer stopped');\n }\n\n /**\n * Handle coming back online\n */\n private handleOnline = (): void => {\n this.log('Browser came online, triggering flush');\n this.flush().catch((err) => {\n this.log('Online flush error', err);\n });\n };\n\n /**\n * Check if we're currently online\n */\n private isOnline(): boolean {\n if (isBrowser()) {\n return navigator.onLine;\n }\n // Assume online in Node.js\n return true;\n }\n\n /**\n * Manually trigger a flush of queued events\n */\n async flush(): Promise<void> {\n if (this.isFlushing) {\n this.log('Flush already in progress, skipping');\n return;\n }\n\n if (!this.isOnline()) {\n this.log('Offline, skipping flush');\n return;\n }\n\n if (!this.sendEvents) {\n this.log('No send callback configured, skipping flush');\n return;\n }\n\n this.isFlushing = true;\n\n try {\n const stats = await this.getStats();\n if (stats.size === 0) {\n this.log('Queue is empty, nothing to flush');\n return;\n }\n\n this.log(`Flushing queue (${stats.size} events)`);\n\n // Process in batches\n let processed = 0;\n while (true) {\n const events = await this.dequeue(this.config.flushBatchSize);\n if (events.length === 0) break;\n\n this.log(`Sending batch of ${events.length} events`);\n\n try {\n const sentIds = await this.sendEvents(events);\n await this.markSent(sentIds);\n\n // Mark remaining as failed\n const failedIds = events\n .filter((e) => !sentIds.includes(e.id!))\n .map((e) => e.id!);\n if (failedIds.length > 0) {\n await this.markFailed(failedIds);\n }\n\n processed += sentIds.length;\n } catch (error) {\n // Mark all as failed on network error\n const allIds = events.map((e) => e.id!);\n await this.markFailed(allIds);\n this.log('Batch send failed', error);\n break; // Stop flushing on error\n }\n }\n\n this.log(`Flush complete, sent ${processed} events`);\n } finally {\n this.isFlushing = false;\n }\n }\n\n /**\n * Close the database connection\n */\n async close(): Promise<void> {\n this.stopFlushTimer();\n if (this.db) {\n this.db.close();\n this.log('Database connection closed');\n }\n }\n}\n\nexport type { OfflineConfig, QueuedEvent, QueueStats, EventQueueInterface, SendEventsCallback };\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kitbase/events",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Kitbase Events SDK for TypeScript and JavaScript",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",