@outlit/browser 0.2.0 → 0.2.1

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.
@@ -4,6 +4,7 @@ import { createContext, useCallback, useEffect, useRef, useState } from "react";
4
4
  // src/tracker.ts
5
5
  import {
6
6
  DEFAULT_API_HOST,
7
+ buildCalendarEvent,
7
8
  buildCustomEvent,
8
9
  buildFormEvent,
9
10
  buildIdentifyEvent,
@@ -29,19 +30,22 @@ function capturePageview() {
29
30
  lastUrl = url;
30
31
  pageviewCallback(url, referrer, title);
31
32
  }
33
+ function capturePageviewDelayed() {
34
+ setTimeout(capturePageview, 10);
35
+ }
32
36
  function setupSpaListeners() {
33
37
  window.addEventListener("popstate", () => {
34
- capturePageview();
38
+ capturePageviewDelayed();
35
39
  });
36
40
  const originalPushState = history.pushState;
37
41
  const originalReplaceState = history.replaceState;
38
42
  history.pushState = function(...args) {
39
43
  originalPushState.apply(this, args);
40
- capturePageview();
44
+ capturePageviewDelayed();
41
45
  };
42
46
  history.replaceState = function(...args) {
43
47
  originalReplaceState.apply(this, args);
44
- capturePageview();
48
+ capturePageviewDelayed();
45
49
  };
46
50
  }
47
51
  var formCallback = null;
@@ -92,6 +96,431 @@ function stopAutocapture() {
92
96
  document.removeEventListener("submit", handleFormSubmit, true);
93
97
  }
94
98
 
99
+ // src/embed-integrations.ts
100
+ var callbacks = null;
101
+ var isListening = false;
102
+ var calSetupAttempts = 0;
103
+ var calCallbackRegistered = false;
104
+ var lastBookingUid = null;
105
+ var CAL_MAX_RETRY_ATTEMPTS = 10;
106
+ var CAL_INITIAL_DELAY_MS = 200;
107
+ var CAL_MAX_DELAY_MS = 2e3;
108
+ function parseCalComBooking(data) {
109
+ const event = {
110
+ provider: "cal.com"
111
+ };
112
+ if (data.title) {
113
+ event.eventType = data.title;
114
+ const nameMatch = data.title.match(/between .+ and (.+)$/i);
115
+ if (nameMatch?.[1]) {
116
+ event.inviteeName = nameMatch[1].trim();
117
+ }
118
+ }
119
+ if (data.startTime) event.startTime = data.startTime;
120
+ if (data.endTime) event.endTime = data.endTime;
121
+ if (data.startTime && data.endTime) {
122
+ const start = new Date(data.startTime);
123
+ const end = new Date(data.endTime);
124
+ event.duration = Math.round((end.getTime() - start.getTime()) / 6e4);
125
+ }
126
+ if (data.isRecurring !== void 0) {
127
+ event.isRecurring = data.isRecurring;
128
+ }
129
+ return event;
130
+ }
131
+ function setupCalComListener() {
132
+ if (typeof window === "undefined") return;
133
+ if (calCallbackRegistered) return;
134
+ calSetupAttempts++;
135
+ if ("Cal" in window) {
136
+ const Cal = window.Cal;
137
+ if (typeof Cal === "function") {
138
+ try {
139
+ Cal("on", {
140
+ action: "bookingSuccessfulV2",
141
+ callback: handleCalComBooking
142
+ });
143
+ calCallbackRegistered = true;
144
+ return;
145
+ } catch (_e) {
146
+ }
147
+ }
148
+ }
149
+ if (calSetupAttempts < CAL_MAX_RETRY_ATTEMPTS) {
150
+ const delay = Math.min(CAL_INITIAL_DELAY_MS * calSetupAttempts, CAL_MAX_DELAY_MS);
151
+ setTimeout(setupCalComListener, delay);
152
+ }
153
+ }
154
+ function handleCalComBooking(e) {
155
+ if (!callbacks) return;
156
+ const data = e.detail?.data;
157
+ if (!data) return;
158
+ if (data.uid && data.uid === lastBookingUid) return;
159
+ lastBookingUid = data.uid || null;
160
+ const bookingEvent = parseCalComBooking(data);
161
+ callbacks.onCalendarBooked(bookingEvent);
162
+ }
163
+ function handlePostMessage(event) {
164
+ if (!callbacks) return;
165
+ if (isCalendlyEvent(event)) {
166
+ if (event.data.event === "calendly.event_scheduled") {
167
+ const bookingEvent = parseCalendlyBooking(event.data.payload);
168
+ callbacks.onCalendarBooked(bookingEvent);
169
+ }
170
+ return;
171
+ }
172
+ if (isCalComRawMessage(event)) {
173
+ const bookingData = extractCalComBookingFromMessage(event.data);
174
+ if (bookingData) {
175
+ if (bookingData.uid && bookingData.uid === lastBookingUid) return;
176
+ lastBookingUid = bookingData.uid || null;
177
+ const bookingEvent = parseCalComBooking(bookingData);
178
+ callbacks.onCalendarBooked(bookingEvent);
179
+ }
180
+ }
181
+ }
182
+ function isCalComRawMessage(event) {
183
+ if (!event.origin.includes("cal.com")) return false;
184
+ const data = event.data;
185
+ if (!data || typeof data !== "object") return false;
186
+ const messageType = data.type || data.action;
187
+ return messageType === "bookingSuccessfulV2" || messageType === "bookingSuccessful" || messageType === "booking_successful";
188
+ }
189
+ function extractCalComBookingFromMessage(data) {
190
+ if (!data || typeof data !== "object") return null;
191
+ const messageData = data;
192
+ if (messageData.data && typeof messageData.data === "object") {
193
+ return messageData.data;
194
+ }
195
+ if (messageData.booking && typeof messageData.booking === "object") {
196
+ return messageData.booking;
197
+ }
198
+ return null;
199
+ }
200
+ function isCalendlyEvent(e) {
201
+ return e.origin === "https://calendly.com" && e.data && typeof e.data.event === "string" && e.data.event.startsWith("calendly.");
202
+ }
203
+ function parseCalendlyBooking(_payload) {
204
+ return {
205
+ provider: "calendly"
206
+ };
207
+ }
208
+ function initCalendarTracking(cbs) {
209
+ if (isListening) return;
210
+ callbacks = cbs;
211
+ isListening = true;
212
+ calSetupAttempts = 0;
213
+ window.addEventListener("message", handlePostMessage);
214
+ setupCalComListener();
215
+ }
216
+ function stopCalendarTracking() {
217
+ if (!isListening) return;
218
+ window.removeEventListener("message", handlePostMessage);
219
+ callbacks = null;
220
+ isListening = false;
221
+ calCallbackRegistered = false;
222
+ calSetupAttempts = 0;
223
+ lastBookingUid = null;
224
+ }
225
+
226
+ // src/session-tracker.ts
227
+ import { buildEngagementEvent } from "@outlit/core";
228
+ var DEFAULT_IDLE_TIMEOUT = 3e4;
229
+ var SESSION_TIMEOUT = 30 * 60 * 1e3;
230
+ var TIME_UPDATE_INTERVAL = 1e3;
231
+ var MIN_SPURIOUS_THRESHOLD = 50;
232
+ var SESSION_ID_KEY = "outlit_session_id";
233
+ var SESSION_LAST_ACTIVITY_KEY = "outlit_session_last_activity";
234
+ var SessionTracker = class {
235
+ state;
236
+ options;
237
+ idleTimeout;
238
+ timeUpdateInterval = null;
239
+ boundHandleActivity;
240
+ boundHandleVisibilityChange;
241
+ constructor(options) {
242
+ this.options = options;
243
+ this.idleTimeout = options.idleTimeout ?? DEFAULT_IDLE_TIMEOUT;
244
+ this.state = this.createInitialState();
245
+ this.boundHandleActivity = this.handleActivity.bind(this);
246
+ this.boundHandleVisibilityChange = this.handleVisibilityChange.bind(this);
247
+ this.setupEventListeners();
248
+ this.startTimeUpdateInterval();
249
+ }
250
+ /**
251
+ * Get the current session ID.
252
+ */
253
+ getSessionId() {
254
+ return this.state.sessionId;
255
+ }
256
+ // ============================================
257
+ // PUBLIC METHODS
258
+ // ============================================
259
+ /**
260
+ * Emit an engagement event for the current page session.
261
+ * Called by Tracker on exit events and SPA navigation.
262
+ *
263
+ * This method:
264
+ * 1. Finalizes any pending active time
265
+ * 2. Creates and emits the engagement event (if meaningful)
266
+ * 3. Resets state for the next session
267
+ */
268
+ emitEngagement() {
269
+ if (this.state.hasEmittedEngagement) {
270
+ return;
271
+ }
272
+ this.state.hasEmittedEngagement = true;
273
+ this.updateActiveTime();
274
+ const totalTimeMs = Date.now() - this.state.pageEntryTime;
275
+ const isSpuriousEvent = this.state.activeTimeMs < MIN_SPURIOUS_THRESHOLD && totalTimeMs < MIN_SPURIOUS_THRESHOLD;
276
+ if (!isSpuriousEvent) {
277
+ const event = buildEngagementEvent({
278
+ url: this.state.currentUrl,
279
+ referrer: document.referrer,
280
+ activeTimeMs: this.state.activeTimeMs,
281
+ totalTimeMs,
282
+ sessionId: this.state.sessionId
283
+ });
284
+ this.options.onEngagement(event);
285
+ }
286
+ this.resetState();
287
+ }
288
+ /**
289
+ * Handle SPA navigation.
290
+ * Called by Tracker when a new pageview is detected.
291
+ *
292
+ * This method:
293
+ * 1. Emits engagement for the OLD page (using stored state)
294
+ * 2. Updates state for the NEW page
295
+ */
296
+ onNavigation(newUrl) {
297
+ this.emitEngagement();
298
+ this.state.currentUrl = newUrl;
299
+ this.state.currentPath = this.extractPath(newUrl);
300
+ this.state.pageEntryTime = Date.now();
301
+ this.state.activeTimeMs = 0;
302
+ this.state.lastActiveTime = Date.now();
303
+ this.state.isUserActive = true;
304
+ this.state.hasEmittedEngagement = false;
305
+ this.resetIdleTimer();
306
+ }
307
+ /**
308
+ * Stop session tracking and clean up.
309
+ */
310
+ stop() {
311
+ this.removeEventListeners();
312
+ if (this.timeUpdateInterval) {
313
+ clearInterval(this.timeUpdateInterval);
314
+ this.timeUpdateInterval = null;
315
+ }
316
+ if (this.state.idleTimeoutId) {
317
+ clearTimeout(this.state.idleTimeoutId);
318
+ this.state.idleTimeoutId = null;
319
+ }
320
+ }
321
+ // ============================================
322
+ // PRIVATE METHODS
323
+ // ============================================
324
+ createInitialState() {
325
+ const now = Date.now();
326
+ return {
327
+ currentUrl: typeof window !== "undefined" ? window.location.href : "",
328
+ currentPath: typeof window !== "undefined" ? window.location.pathname : "/",
329
+ pageEntryTime: now,
330
+ lastActiveTime: now,
331
+ activeTimeMs: 0,
332
+ isPageVisible: typeof document !== "undefined" ? document.visibilityState === "visible" : true,
333
+ isUserActive: true,
334
+ // Assume active on page load
335
+ idleTimeoutId: null,
336
+ sessionId: this.getOrCreateSessionId(),
337
+ hasEmittedEngagement: false
338
+ };
339
+ }
340
+ resetState() {
341
+ const now = Date.now();
342
+ this.state.pageEntryTime = now;
343
+ this.state.lastActiveTime = now;
344
+ this.state.activeTimeMs = 0;
345
+ this.state.isUserActive = true;
346
+ this.resetIdleTimer();
347
+ }
348
+ /**
349
+ * Get existing session ID from storage or create a new one.
350
+ * Session ID is reset if:
351
+ * - No existing session ID in storage
352
+ * - Last activity was more than 30 minutes ago
353
+ */
354
+ getOrCreateSessionId() {
355
+ if (typeof sessionStorage === "undefined") {
356
+ return this.generateSessionId();
357
+ }
358
+ try {
359
+ const existingSessionId = sessionStorage.getItem(SESSION_ID_KEY);
360
+ const lastActivityStr = sessionStorage.getItem(SESSION_LAST_ACTIVITY_KEY);
361
+ const lastActivity = lastActivityStr ? Number.parseInt(lastActivityStr, 10) : 0;
362
+ const now = Date.now();
363
+ if (existingSessionId && lastActivity && now - lastActivity < SESSION_TIMEOUT) {
364
+ this.updateSessionActivity();
365
+ return existingSessionId;
366
+ }
367
+ const newSessionId = this.generateSessionId();
368
+ sessionStorage.setItem(SESSION_ID_KEY, newSessionId);
369
+ sessionStorage.setItem(SESSION_LAST_ACTIVITY_KEY, now.toString());
370
+ return newSessionId;
371
+ } catch {
372
+ return this.generateSessionId();
373
+ }
374
+ }
375
+ /**
376
+ * Generate a new session ID (UUID v4).
377
+ */
378
+ generateSessionId() {
379
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
380
+ return crypto.randomUUID();
381
+ }
382
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
383
+ const r = Math.random() * 16 | 0;
384
+ const v = c === "x" ? r : r & 3 | 8;
385
+ return v.toString(16);
386
+ });
387
+ }
388
+ /**
389
+ * Update the session's last activity timestamp.
390
+ */
391
+ updateSessionActivity() {
392
+ if (typeof sessionStorage === "undefined") return;
393
+ try {
394
+ sessionStorage.setItem(SESSION_LAST_ACTIVITY_KEY, Date.now().toString());
395
+ } catch {
396
+ }
397
+ }
398
+ /**
399
+ * Check if the current session has expired and create a new one if needed.
400
+ * Called when user returns to the page after being away.
401
+ */
402
+ checkSessionExpiry() {
403
+ if (typeof sessionStorage === "undefined") return;
404
+ try {
405
+ const lastActivityStr = sessionStorage.getItem(SESSION_LAST_ACTIVITY_KEY);
406
+ const lastActivity = lastActivityStr ? Number.parseInt(lastActivityStr, 10) : 0;
407
+ const now = Date.now();
408
+ if (now - lastActivity >= SESSION_TIMEOUT) {
409
+ const newSessionId = this.generateSessionId();
410
+ sessionStorage.setItem(SESSION_ID_KEY, newSessionId);
411
+ this.state.sessionId = newSessionId;
412
+ }
413
+ sessionStorage.setItem(SESSION_LAST_ACTIVITY_KEY, now.toString());
414
+ } catch {
415
+ }
416
+ }
417
+ setupEventListeners() {
418
+ if (typeof window === "undefined" || typeof document === "undefined") return;
419
+ const activityEvents = ["mousemove", "keydown", "click", "scroll", "touchstart"];
420
+ for (const event of activityEvents) {
421
+ document.addEventListener(event, this.boundHandleActivity, { passive: true });
422
+ }
423
+ document.addEventListener("visibilitychange", this.boundHandleVisibilityChange);
424
+ this.resetIdleTimer();
425
+ }
426
+ removeEventListeners() {
427
+ if (typeof window === "undefined" || typeof document === "undefined") return;
428
+ const activityEvents = ["mousemove", "keydown", "click", "scroll", "touchstart"];
429
+ for (const event of activityEvents) {
430
+ document.removeEventListener(event, this.boundHandleActivity);
431
+ }
432
+ document.removeEventListener("visibilitychange", this.boundHandleVisibilityChange);
433
+ }
434
+ /**
435
+ * Handle user activity events.
436
+ * Marks user as active and resets idle timer.
437
+ */
438
+ handleActivity() {
439
+ if (!this.state.isUserActive) {
440
+ this.checkSessionExpiry();
441
+ this.state.lastActiveTime = Date.now();
442
+ }
443
+ this.state.isUserActive = true;
444
+ this.resetIdleTimer();
445
+ this.updateSessionActivity();
446
+ }
447
+ /**
448
+ * Handle visibility change events.
449
+ * Pauses time accumulation when tab is hidden.
450
+ */
451
+ handleVisibilityChange() {
452
+ const wasVisible = this.state.isPageVisible;
453
+ const isNowVisible = document.visibilityState === "visible";
454
+ if (wasVisible && !isNowVisible) {
455
+ this.updateActiveTime();
456
+ }
457
+ this.state.isPageVisible = isNowVisible;
458
+ if (!wasVisible && isNowVisible) {
459
+ this.checkSessionExpiry();
460
+ this.state.lastActiveTime = Date.now();
461
+ this.state.hasEmittedEngagement = false;
462
+ }
463
+ }
464
+ /**
465
+ * Reset the idle timer.
466
+ * Called on activity and initialization.
467
+ */
468
+ resetIdleTimer() {
469
+ if (this.state.idleTimeoutId) {
470
+ clearTimeout(this.state.idleTimeoutId);
471
+ }
472
+ this.state.idleTimeoutId = setTimeout(() => {
473
+ this.updateActiveTime();
474
+ this.state.isUserActive = false;
475
+ }, this.idleTimeout);
476
+ }
477
+ /**
478
+ * Start the interval for updating active time.
479
+ */
480
+ startTimeUpdateInterval() {
481
+ if (this.timeUpdateInterval) return;
482
+ this.timeUpdateInterval = setInterval(() => {
483
+ this.updateActiveTime();
484
+ }, TIME_UPDATE_INTERVAL);
485
+ }
486
+ /**
487
+ * Update accumulated active time.
488
+ * Only accumulates when page is visible AND user is active.
489
+ */
490
+ updateActiveTime() {
491
+ if (this.state.isPageVisible && this.state.isUserActive) {
492
+ const now = Date.now();
493
+ this.state.activeTimeMs += now - this.state.lastActiveTime;
494
+ this.state.lastActiveTime = now;
495
+ }
496
+ }
497
+ /**
498
+ * Extract path from URL.
499
+ */
500
+ extractPath(url) {
501
+ try {
502
+ return new URL(url).pathname;
503
+ } catch {
504
+ return "/";
505
+ }
506
+ }
507
+ };
508
+ var sessionTrackerInstance = null;
509
+ function initSessionTracking(options) {
510
+ if (sessionTrackerInstance) {
511
+ console.warn("[Outlit] Session tracking already initialized");
512
+ return sessionTrackerInstance;
513
+ }
514
+ sessionTrackerInstance = new SessionTracker(options);
515
+ return sessionTrackerInstance;
516
+ }
517
+ function stopSessionTracking() {
518
+ if (sessionTrackerInstance) {
519
+ sessionTrackerInstance.stop();
520
+ sessionTrackerInstance = null;
521
+ }
522
+ }
523
+
95
524
  // src/storage.ts
96
525
  var VISITOR_ID_KEY = "outlit_visitor_id";
97
526
  function generateVisitorId() {
@@ -173,7 +602,7 @@ function setCookie(name, value, days) {
173
602
  }
174
603
 
175
604
  // src/tracker.ts
176
- var Tracker = class {
605
+ var Outlit = class {
177
606
  publicKey;
178
607
  apiHost;
179
608
  visitorId = null;
@@ -183,15 +612,29 @@ var Tracker = class {
183
612
  isInitialized = false;
184
613
  isTrackingEnabled = false;
185
614
  options;
615
+ hasHandledExit = false;
616
+ sessionTracker = null;
186
617
  constructor(options) {
187
618
  this.publicKey = options.publicKey;
188
619
  this.apiHost = options.apiHost ?? DEFAULT_API_HOST;
189
620
  this.flushInterval = options.flushInterval ?? 5e3;
190
621
  this.options = options;
191
622
  if (typeof window !== "undefined") {
192
- window.addEventListener("beforeunload", () => {
623
+ const handleExit = () => {
624
+ if (this.hasHandledExit) return;
625
+ this.hasHandledExit = true;
626
+ this.sessionTracker?.emitEngagement();
193
627
  this.flush();
628
+ };
629
+ document.addEventListener("visibilitychange", () => {
630
+ if (document.visibilityState === "hidden") {
631
+ handleExit();
632
+ } else {
633
+ this.hasHandledExit = false;
634
+ }
194
635
  });
636
+ window.addEventListener("pagehide", handleExit);
637
+ window.addEventListener("beforeunload", handleExit);
195
638
  }
196
639
  this.isInitialized = true;
197
640
  if (options.autoTrack !== false) {
@@ -216,12 +659,18 @@ var Tracker = class {
216
659
  }
217
660
  this.visitorId = getOrCreateVisitorId();
218
661
  this.startFlushTimer();
662
+ if (this.options.trackEngagement !== false) {
663
+ this.initSessionTracking();
664
+ }
219
665
  if (this.options.trackPageviews !== false) {
220
666
  this.initPageviewTracking();
221
667
  }
222
668
  if (this.options.trackForms !== false) {
223
669
  this.initFormTracking(this.options.formFieldDenylist);
224
670
  }
671
+ if (this.options.trackCalendarEmbeds !== false) {
672
+ this.initCalendarTracking();
673
+ }
225
674
  this.isTrackingEnabled = true;
226
675
  }
227
676
  /**
@@ -281,7 +730,7 @@ var Tracker = class {
281
730
  await this.sendEvents(events);
282
731
  }
283
732
  /**
284
- * Shutdown the tracker.
733
+ * Shutdown the client.
285
734
  */
286
735
  async shutdown() {
287
736
  if (this.flushTimer) {
@@ -289,13 +738,25 @@ var Tracker = class {
289
738
  this.flushTimer = null;
290
739
  }
291
740
  stopAutocapture();
741
+ stopCalendarTracking();
742
+ stopSessionTracking();
743
+ this.sessionTracker = null;
292
744
  await this.flush();
293
745
  }
294
746
  // ============================================
295
747
  // INTERNAL METHODS
296
748
  // ============================================
749
+ initSessionTracking() {
750
+ this.sessionTracker = initSessionTracking({
751
+ onEngagement: (event) => {
752
+ this.enqueue(event);
753
+ },
754
+ idleTimeout: this.options.idleTimeout
755
+ });
756
+ }
297
757
  initPageviewTracking() {
298
758
  initPageviewTracking((url, referrer, title) => {
759
+ this.sessionTracker?.onNavigation(url);
299
760
  const event = buildPageviewEvent({ url, referrer, title });
300
761
  this.enqueue(event);
301
762
  });
@@ -325,6 +786,25 @@ var Tracker = class {
325
786
  identityCallback2
326
787
  );
327
788
  }
789
+ initCalendarTracking() {
790
+ initCalendarTracking({
791
+ onCalendarBooked: (bookingEvent) => {
792
+ const event = buildCalendarEvent({
793
+ url: window.location.href,
794
+ referrer: document.referrer,
795
+ provider: bookingEvent.provider,
796
+ eventType: bookingEvent.eventType,
797
+ startTime: bookingEvent.startTime,
798
+ endTime: bookingEvent.endTime,
799
+ duration: bookingEvent.duration,
800
+ isRecurring: bookingEvent.isRecurring,
801
+ inviteeEmail: bookingEvent.inviteeEmail,
802
+ inviteeName: bookingEvent.inviteeName
803
+ });
804
+ this.enqueue(event);
805
+ }
806
+ });
807
+ }
328
808
  enqueue(event) {
329
809
  this.eventQueue.push(event);
330
810
  if (this.eventQueue.length >= 10) {
@@ -340,7 +820,7 @@ var Tracker = class {
340
820
  async sendEvents(events) {
341
821
  if (events.length === 0) return;
342
822
  if (!this.visitorId) return;
343
- const payload = buildIngestPayload(this.visitorId, "pixel", events);
823
+ const payload = buildIngestPayload(this.visitorId, "client", events);
344
824
  const url = `${this.apiHost}/api/i/v1/${this.publicKey}/events`;
345
825
  try {
346
826
  if (typeof navigator !== "undefined" && navigator.sendBeacon) {
@@ -365,7 +845,7 @@ var Tracker = class {
365
845
  // src/react/provider.tsx
366
846
  import { jsx } from "react/jsx-runtime";
367
847
  var OutlitContext = createContext({
368
- tracker: null,
848
+ outlit: null,
369
849
  isInitialized: false,
370
850
  isTrackingEnabled: false,
371
851
  enableTracking: () => {
@@ -382,12 +862,12 @@ function OutlitProvider({
382
862
  autoTrack = true,
383
863
  autoIdentify = true
384
864
  }) {
385
- const trackerRef = useRef(null);
865
+ const outlitRef = useRef(null);
386
866
  const initializedRef = useRef(false);
387
867
  const [isTrackingEnabled, setIsTrackingEnabled] = useState(false);
388
868
  useEffect(() => {
389
869
  if (initializedRef.current) return;
390
- trackerRef.current = new Tracker({
870
+ outlitRef.current = new Outlit({
391
871
  publicKey,
392
872
  apiHost,
393
873
  trackPageviews,
@@ -398,9 +878,9 @@ function OutlitProvider({
398
878
  autoIdentify
399
879
  });
400
880
  initializedRef.current = true;
401
- setIsTrackingEnabled(trackerRef.current.isEnabled());
881
+ setIsTrackingEnabled(outlitRef.current.isEnabled());
402
882
  return () => {
403
- trackerRef.current?.shutdown();
883
+ outlitRef.current?.shutdown();
404
884
  };
405
885
  }, [
406
886
  publicKey,
@@ -413,8 +893,8 @@ function OutlitProvider({
413
893
  autoIdentify
414
894
  ]);
415
895
  const enableTracking = useCallback(() => {
416
- if (trackerRef.current) {
417
- trackerRef.current.enableTracking();
896
+ if (outlitRef.current) {
897
+ outlitRef.current.enableTracking();
418
898
  setIsTrackingEnabled(true);
419
899
  }
420
900
  }, []);
@@ -422,7 +902,7 @@ function OutlitProvider({
422
902
  OutlitContext.Provider,
423
903
  {
424
904
  value: {
425
- tracker: trackerRef.current,
905
+ outlit: outlitRef.current,
426
906
  isInitialized: initializedRef.current,
427
907
  isTrackingEnabled,
428
908
  enableTracking
@@ -435,31 +915,31 @@ function OutlitProvider({
435
915
  // src/react/hooks.ts
436
916
  import { useCallback as useCallback2, useContext } from "react";
437
917
  function useOutlit() {
438
- const { tracker, isInitialized, isTrackingEnabled, enableTracking } = useContext(OutlitContext);
918
+ const { outlit, isInitialized, isTrackingEnabled, enableTracking } = useContext(OutlitContext);
439
919
  const track = useCallback2(
440
920
  (eventName, properties) => {
441
- if (!tracker) {
442
- console.warn("[Outlit] Tracker not initialized. Make sure OutlitProvider is mounted.");
921
+ if (!outlit) {
922
+ console.warn("[Outlit] Not initialized. Make sure OutlitProvider is mounted.");
443
923
  return;
444
924
  }
445
- tracker.track(eventName, properties);
925
+ outlit.track(eventName, properties);
446
926
  },
447
- [tracker]
927
+ [outlit]
448
928
  );
449
929
  const identify = useCallback2(
450
930
  (options) => {
451
- if (!tracker) {
452
- console.warn("[Outlit] Tracker not initialized. Make sure OutlitProvider is mounted.");
931
+ if (!outlit) {
932
+ console.warn("[Outlit] Not initialized. Make sure OutlitProvider is mounted.");
453
933
  return;
454
934
  }
455
- tracker.identify(options);
935
+ outlit.identify(options);
456
936
  },
457
- [tracker]
937
+ [outlit]
458
938
  );
459
939
  const getVisitorId = useCallback2(() => {
460
- if (!tracker) return null;
461
- return tracker.getVisitorId();
462
- }, [tracker]);
940
+ if (!outlit) return null;
941
+ return outlit.getVisitorId();
942
+ }, [outlit]);
463
943
  return {
464
944
  track,
465
945
  identify,