@outlit/browser 0.1.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.
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { i as init, t as track, a as identify, g as getInstance, T as Tracker, e as enableTracking, b as isTrackingEnabled } from './tracker-CocH64L9.mjs';
2
- export { c as TrackerOptions } from './tracker-CocH64L9.mjs';
1
+ import { i as init, t as track, a as identify, g as getInstance, O as Outlit, e as enableTracking, b as isTrackingEnabled } from './tracker-DFcTv3EM.mjs';
2
+ export { c as OutlitOptions } from './tracker-DFcTv3EM.mjs';
3
3
  export { BrowserIdentifyOptions, BrowserTrackOptions, TrackerConfig, UtmParams } from '@outlit/core';
4
4
 
5
5
  declare const _default: {
@@ -7,9 +7,9 @@ declare const _default: {
7
7
  track: typeof track;
8
8
  identify: typeof identify;
9
9
  getInstance: typeof getInstance;
10
- Tracker: typeof Tracker;
10
+ Outlit: typeof Outlit;
11
11
  enableTracking: typeof enableTracking;
12
12
  isTrackingEnabled: typeof isTrackingEnabled;
13
13
  };
14
14
 
15
- export { Tracker, _default as default, enableTracking, getInstance, identify, init, isTrackingEnabled, track };
15
+ export { Outlit, _default as default, enableTracking, getInstance, identify, init, isTrackingEnabled, track };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { i as init, t as track, a as identify, g as getInstance, T as Tracker, e as enableTracking, b as isTrackingEnabled } from './tracker-CocH64L9.js';
2
- export { c as TrackerOptions } from './tracker-CocH64L9.js';
1
+ import { i as init, t as track, a as identify, g as getInstance, O as Outlit, e as enableTracking, b as isTrackingEnabled } from './tracker-DFcTv3EM.js';
2
+ export { c as OutlitOptions } from './tracker-DFcTv3EM.js';
3
3
  export { BrowserIdentifyOptions, BrowserTrackOptions, TrackerConfig, UtmParams } from '@outlit/core';
4
4
 
5
5
  declare const _default: {
@@ -7,9 +7,9 @@ declare const _default: {
7
7
  track: typeof track;
8
8
  identify: typeof identify;
9
9
  getInstance: typeof getInstance;
10
- Tracker: typeof Tracker;
10
+ Outlit: typeof Outlit;
11
11
  enableTracking: typeof enableTracking;
12
12
  isTrackingEnabled: typeof isTrackingEnabled;
13
13
  };
14
14
 
15
- export { Tracker, _default as default, enableTracking, getInstance, identify, init, isTrackingEnabled, track };
15
+ export { Outlit, _default as default, enableTracking, getInstance, identify, init, isTrackingEnabled, track };
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var src_exports = {};
22
22
  __export(src_exports, {
23
- Tracker: () => Tracker,
23
+ Outlit: () => Outlit,
24
24
  default: () => src_default,
25
25
  enableTracking: () => enableTracking,
26
26
  getInstance: () => getInstance,
@@ -32,7 +32,7 @@ __export(src_exports, {
32
32
  module.exports = __toCommonJS(src_exports);
33
33
 
34
34
  // src/tracker.ts
35
- var import_core2 = require("@outlit/core");
35
+ var import_core3 = require("@outlit/core");
36
36
 
37
37
  // src/autocapture.ts
38
38
  var import_core = require("@outlit/core");
@@ -52,19 +52,22 @@ function capturePageview() {
52
52
  lastUrl = url;
53
53
  pageviewCallback(url, referrer, title);
54
54
  }
55
+ function capturePageviewDelayed() {
56
+ setTimeout(capturePageview, 10);
57
+ }
55
58
  function setupSpaListeners() {
56
59
  window.addEventListener("popstate", () => {
57
- capturePageview();
60
+ capturePageviewDelayed();
58
61
  });
59
62
  const originalPushState = history.pushState;
60
63
  const originalReplaceState = history.replaceState;
61
64
  history.pushState = function(...args) {
62
65
  originalPushState.apply(this, args);
63
- capturePageview();
66
+ capturePageviewDelayed();
64
67
  };
65
68
  history.replaceState = function(...args) {
66
69
  originalReplaceState.apply(this, args);
67
- capturePageview();
70
+ capturePageviewDelayed();
68
71
  };
69
72
  }
70
73
  var formCallback = null;
@@ -115,6 +118,431 @@ function stopAutocapture() {
115
118
  document.removeEventListener("submit", handleFormSubmit, true);
116
119
  }
117
120
 
121
+ // src/embed-integrations.ts
122
+ var callbacks = null;
123
+ var isListening = false;
124
+ var calSetupAttempts = 0;
125
+ var calCallbackRegistered = false;
126
+ var lastBookingUid = null;
127
+ var CAL_MAX_RETRY_ATTEMPTS = 10;
128
+ var CAL_INITIAL_DELAY_MS = 200;
129
+ var CAL_MAX_DELAY_MS = 2e3;
130
+ function parseCalComBooking(data) {
131
+ const event = {
132
+ provider: "cal.com"
133
+ };
134
+ if (data.title) {
135
+ event.eventType = data.title;
136
+ const nameMatch = data.title.match(/between .+ and (.+)$/i);
137
+ if (nameMatch?.[1]) {
138
+ event.inviteeName = nameMatch[1].trim();
139
+ }
140
+ }
141
+ if (data.startTime) event.startTime = data.startTime;
142
+ if (data.endTime) event.endTime = data.endTime;
143
+ if (data.startTime && data.endTime) {
144
+ const start = new Date(data.startTime);
145
+ const end = new Date(data.endTime);
146
+ event.duration = Math.round((end.getTime() - start.getTime()) / 6e4);
147
+ }
148
+ if (data.isRecurring !== void 0) {
149
+ event.isRecurring = data.isRecurring;
150
+ }
151
+ return event;
152
+ }
153
+ function setupCalComListener() {
154
+ if (typeof window === "undefined") return;
155
+ if (calCallbackRegistered) return;
156
+ calSetupAttempts++;
157
+ if ("Cal" in window) {
158
+ const Cal = window.Cal;
159
+ if (typeof Cal === "function") {
160
+ try {
161
+ Cal("on", {
162
+ action: "bookingSuccessfulV2",
163
+ callback: handleCalComBooking
164
+ });
165
+ calCallbackRegistered = true;
166
+ return;
167
+ } catch (_e) {
168
+ }
169
+ }
170
+ }
171
+ if (calSetupAttempts < CAL_MAX_RETRY_ATTEMPTS) {
172
+ const delay = Math.min(CAL_INITIAL_DELAY_MS * calSetupAttempts, CAL_MAX_DELAY_MS);
173
+ setTimeout(setupCalComListener, delay);
174
+ }
175
+ }
176
+ function handleCalComBooking(e) {
177
+ if (!callbacks) return;
178
+ const data = e.detail?.data;
179
+ if (!data) return;
180
+ if (data.uid && data.uid === lastBookingUid) return;
181
+ lastBookingUid = data.uid || null;
182
+ const bookingEvent = parseCalComBooking(data);
183
+ callbacks.onCalendarBooked(bookingEvent);
184
+ }
185
+ function handlePostMessage(event) {
186
+ if (!callbacks) return;
187
+ if (isCalendlyEvent(event)) {
188
+ if (event.data.event === "calendly.event_scheduled") {
189
+ const bookingEvent = parseCalendlyBooking(event.data.payload);
190
+ callbacks.onCalendarBooked(bookingEvent);
191
+ }
192
+ return;
193
+ }
194
+ if (isCalComRawMessage(event)) {
195
+ const bookingData = extractCalComBookingFromMessage(event.data);
196
+ if (bookingData) {
197
+ if (bookingData.uid && bookingData.uid === lastBookingUid) return;
198
+ lastBookingUid = bookingData.uid || null;
199
+ const bookingEvent = parseCalComBooking(bookingData);
200
+ callbacks.onCalendarBooked(bookingEvent);
201
+ }
202
+ }
203
+ }
204
+ function isCalComRawMessage(event) {
205
+ if (!event.origin.includes("cal.com")) return false;
206
+ const data = event.data;
207
+ if (!data || typeof data !== "object") return false;
208
+ const messageType = data.type || data.action;
209
+ return messageType === "bookingSuccessfulV2" || messageType === "bookingSuccessful" || messageType === "booking_successful";
210
+ }
211
+ function extractCalComBookingFromMessage(data) {
212
+ if (!data || typeof data !== "object") return null;
213
+ const messageData = data;
214
+ if (messageData.data && typeof messageData.data === "object") {
215
+ return messageData.data;
216
+ }
217
+ if (messageData.booking && typeof messageData.booking === "object") {
218
+ return messageData.booking;
219
+ }
220
+ return null;
221
+ }
222
+ function isCalendlyEvent(e) {
223
+ return e.origin === "https://calendly.com" && e.data && typeof e.data.event === "string" && e.data.event.startsWith("calendly.");
224
+ }
225
+ function parseCalendlyBooking(_payload) {
226
+ return {
227
+ provider: "calendly"
228
+ };
229
+ }
230
+ function initCalendarTracking(cbs) {
231
+ if (isListening) return;
232
+ callbacks = cbs;
233
+ isListening = true;
234
+ calSetupAttempts = 0;
235
+ window.addEventListener("message", handlePostMessage);
236
+ setupCalComListener();
237
+ }
238
+ function stopCalendarTracking() {
239
+ if (!isListening) return;
240
+ window.removeEventListener("message", handlePostMessage);
241
+ callbacks = null;
242
+ isListening = false;
243
+ calCallbackRegistered = false;
244
+ calSetupAttempts = 0;
245
+ lastBookingUid = null;
246
+ }
247
+
248
+ // src/session-tracker.ts
249
+ var import_core2 = require("@outlit/core");
250
+ var DEFAULT_IDLE_TIMEOUT = 3e4;
251
+ var SESSION_TIMEOUT = 30 * 60 * 1e3;
252
+ var TIME_UPDATE_INTERVAL = 1e3;
253
+ var MIN_SPURIOUS_THRESHOLD = 50;
254
+ var SESSION_ID_KEY = "outlit_session_id";
255
+ var SESSION_LAST_ACTIVITY_KEY = "outlit_session_last_activity";
256
+ var SessionTracker = class {
257
+ state;
258
+ options;
259
+ idleTimeout;
260
+ timeUpdateInterval = null;
261
+ boundHandleActivity;
262
+ boundHandleVisibilityChange;
263
+ constructor(options) {
264
+ this.options = options;
265
+ this.idleTimeout = options.idleTimeout ?? DEFAULT_IDLE_TIMEOUT;
266
+ this.state = this.createInitialState();
267
+ this.boundHandleActivity = this.handleActivity.bind(this);
268
+ this.boundHandleVisibilityChange = this.handleVisibilityChange.bind(this);
269
+ this.setupEventListeners();
270
+ this.startTimeUpdateInterval();
271
+ }
272
+ /**
273
+ * Get the current session ID.
274
+ */
275
+ getSessionId() {
276
+ return this.state.sessionId;
277
+ }
278
+ // ============================================
279
+ // PUBLIC METHODS
280
+ // ============================================
281
+ /**
282
+ * Emit an engagement event for the current page session.
283
+ * Called by Tracker on exit events and SPA navigation.
284
+ *
285
+ * This method:
286
+ * 1. Finalizes any pending active time
287
+ * 2. Creates and emits the engagement event (if meaningful)
288
+ * 3. Resets state for the next session
289
+ */
290
+ emitEngagement() {
291
+ if (this.state.hasEmittedEngagement) {
292
+ return;
293
+ }
294
+ this.state.hasEmittedEngagement = true;
295
+ this.updateActiveTime();
296
+ const totalTimeMs = Date.now() - this.state.pageEntryTime;
297
+ const isSpuriousEvent = this.state.activeTimeMs < MIN_SPURIOUS_THRESHOLD && totalTimeMs < MIN_SPURIOUS_THRESHOLD;
298
+ if (!isSpuriousEvent) {
299
+ const event = (0, import_core2.buildEngagementEvent)({
300
+ url: this.state.currentUrl,
301
+ referrer: document.referrer,
302
+ activeTimeMs: this.state.activeTimeMs,
303
+ totalTimeMs,
304
+ sessionId: this.state.sessionId
305
+ });
306
+ this.options.onEngagement(event);
307
+ }
308
+ this.resetState();
309
+ }
310
+ /**
311
+ * Handle SPA navigation.
312
+ * Called by Tracker when a new pageview is detected.
313
+ *
314
+ * This method:
315
+ * 1. Emits engagement for the OLD page (using stored state)
316
+ * 2. Updates state for the NEW page
317
+ */
318
+ onNavigation(newUrl) {
319
+ this.emitEngagement();
320
+ this.state.currentUrl = newUrl;
321
+ this.state.currentPath = this.extractPath(newUrl);
322
+ this.state.pageEntryTime = Date.now();
323
+ this.state.activeTimeMs = 0;
324
+ this.state.lastActiveTime = Date.now();
325
+ this.state.isUserActive = true;
326
+ this.state.hasEmittedEngagement = false;
327
+ this.resetIdleTimer();
328
+ }
329
+ /**
330
+ * Stop session tracking and clean up.
331
+ */
332
+ stop() {
333
+ this.removeEventListeners();
334
+ if (this.timeUpdateInterval) {
335
+ clearInterval(this.timeUpdateInterval);
336
+ this.timeUpdateInterval = null;
337
+ }
338
+ if (this.state.idleTimeoutId) {
339
+ clearTimeout(this.state.idleTimeoutId);
340
+ this.state.idleTimeoutId = null;
341
+ }
342
+ }
343
+ // ============================================
344
+ // PRIVATE METHODS
345
+ // ============================================
346
+ createInitialState() {
347
+ const now = Date.now();
348
+ return {
349
+ currentUrl: typeof window !== "undefined" ? window.location.href : "",
350
+ currentPath: typeof window !== "undefined" ? window.location.pathname : "/",
351
+ pageEntryTime: now,
352
+ lastActiveTime: now,
353
+ activeTimeMs: 0,
354
+ isPageVisible: typeof document !== "undefined" ? document.visibilityState === "visible" : true,
355
+ isUserActive: true,
356
+ // Assume active on page load
357
+ idleTimeoutId: null,
358
+ sessionId: this.getOrCreateSessionId(),
359
+ hasEmittedEngagement: false
360
+ };
361
+ }
362
+ resetState() {
363
+ const now = Date.now();
364
+ this.state.pageEntryTime = now;
365
+ this.state.lastActiveTime = now;
366
+ this.state.activeTimeMs = 0;
367
+ this.state.isUserActive = true;
368
+ this.resetIdleTimer();
369
+ }
370
+ /**
371
+ * Get existing session ID from storage or create a new one.
372
+ * Session ID is reset if:
373
+ * - No existing session ID in storage
374
+ * - Last activity was more than 30 minutes ago
375
+ */
376
+ getOrCreateSessionId() {
377
+ if (typeof sessionStorage === "undefined") {
378
+ return this.generateSessionId();
379
+ }
380
+ try {
381
+ const existingSessionId = sessionStorage.getItem(SESSION_ID_KEY);
382
+ const lastActivityStr = sessionStorage.getItem(SESSION_LAST_ACTIVITY_KEY);
383
+ const lastActivity = lastActivityStr ? Number.parseInt(lastActivityStr, 10) : 0;
384
+ const now = Date.now();
385
+ if (existingSessionId && lastActivity && now - lastActivity < SESSION_TIMEOUT) {
386
+ this.updateSessionActivity();
387
+ return existingSessionId;
388
+ }
389
+ const newSessionId = this.generateSessionId();
390
+ sessionStorage.setItem(SESSION_ID_KEY, newSessionId);
391
+ sessionStorage.setItem(SESSION_LAST_ACTIVITY_KEY, now.toString());
392
+ return newSessionId;
393
+ } catch {
394
+ return this.generateSessionId();
395
+ }
396
+ }
397
+ /**
398
+ * Generate a new session ID (UUID v4).
399
+ */
400
+ generateSessionId() {
401
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
402
+ return crypto.randomUUID();
403
+ }
404
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
405
+ const r = Math.random() * 16 | 0;
406
+ const v = c === "x" ? r : r & 3 | 8;
407
+ return v.toString(16);
408
+ });
409
+ }
410
+ /**
411
+ * Update the session's last activity timestamp.
412
+ */
413
+ updateSessionActivity() {
414
+ if (typeof sessionStorage === "undefined") return;
415
+ try {
416
+ sessionStorage.setItem(SESSION_LAST_ACTIVITY_KEY, Date.now().toString());
417
+ } catch {
418
+ }
419
+ }
420
+ /**
421
+ * Check if the current session has expired and create a new one if needed.
422
+ * Called when user returns to the page after being away.
423
+ */
424
+ checkSessionExpiry() {
425
+ if (typeof sessionStorage === "undefined") return;
426
+ try {
427
+ const lastActivityStr = sessionStorage.getItem(SESSION_LAST_ACTIVITY_KEY);
428
+ const lastActivity = lastActivityStr ? Number.parseInt(lastActivityStr, 10) : 0;
429
+ const now = Date.now();
430
+ if (now - lastActivity >= SESSION_TIMEOUT) {
431
+ const newSessionId = this.generateSessionId();
432
+ sessionStorage.setItem(SESSION_ID_KEY, newSessionId);
433
+ this.state.sessionId = newSessionId;
434
+ }
435
+ sessionStorage.setItem(SESSION_LAST_ACTIVITY_KEY, now.toString());
436
+ } catch {
437
+ }
438
+ }
439
+ setupEventListeners() {
440
+ if (typeof window === "undefined" || typeof document === "undefined") return;
441
+ const activityEvents = ["mousemove", "keydown", "click", "scroll", "touchstart"];
442
+ for (const event of activityEvents) {
443
+ document.addEventListener(event, this.boundHandleActivity, { passive: true });
444
+ }
445
+ document.addEventListener("visibilitychange", this.boundHandleVisibilityChange);
446
+ this.resetIdleTimer();
447
+ }
448
+ removeEventListeners() {
449
+ if (typeof window === "undefined" || typeof document === "undefined") return;
450
+ const activityEvents = ["mousemove", "keydown", "click", "scroll", "touchstart"];
451
+ for (const event of activityEvents) {
452
+ document.removeEventListener(event, this.boundHandleActivity);
453
+ }
454
+ document.removeEventListener("visibilitychange", this.boundHandleVisibilityChange);
455
+ }
456
+ /**
457
+ * Handle user activity events.
458
+ * Marks user as active and resets idle timer.
459
+ */
460
+ handleActivity() {
461
+ if (!this.state.isUserActive) {
462
+ this.checkSessionExpiry();
463
+ this.state.lastActiveTime = Date.now();
464
+ }
465
+ this.state.isUserActive = true;
466
+ this.resetIdleTimer();
467
+ this.updateSessionActivity();
468
+ }
469
+ /**
470
+ * Handle visibility change events.
471
+ * Pauses time accumulation when tab is hidden.
472
+ */
473
+ handleVisibilityChange() {
474
+ const wasVisible = this.state.isPageVisible;
475
+ const isNowVisible = document.visibilityState === "visible";
476
+ if (wasVisible && !isNowVisible) {
477
+ this.updateActiveTime();
478
+ }
479
+ this.state.isPageVisible = isNowVisible;
480
+ if (!wasVisible && isNowVisible) {
481
+ this.checkSessionExpiry();
482
+ this.state.lastActiveTime = Date.now();
483
+ this.state.hasEmittedEngagement = false;
484
+ }
485
+ }
486
+ /**
487
+ * Reset the idle timer.
488
+ * Called on activity and initialization.
489
+ */
490
+ resetIdleTimer() {
491
+ if (this.state.idleTimeoutId) {
492
+ clearTimeout(this.state.idleTimeoutId);
493
+ }
494
+ this.state.idleTimeoutId = setTimeout(() => {
495
+ this.updateActiveTime();
496
+ this.state.isUserActive = false;
497
+ }, this.idleTimeout);
498
+ }
499
+ /**
500
+ * Start the interval for updating active time.
501
+ */
502
+ startTimeUpdateInterval() {
503
+ if (this.timeUpdateInterval) return;
504
+ this.timeUpdateInterval = setInterval(() => {
505
+ this.updateActiveTime();
506
+ }, TIME_UPDATE_INTERVAL);
507
+ }
508
+ /**
509
+ * Update accumulated active time.
510
+ * Only accumulates when page is visible AND user is active.
511
+ */
512
+ updateActiveTime() {
513
+ if (this.state.isPageVisible && this.state.isUserActive) {
514
+ const now = Date.now();
515
+ this.state.activeTimeMs += now - this.state.lastActiveTime;
516
+ this.state.lastActiveTime = now;
517
+ }
518
+ }
519
+ /**
520
+ * Extract path from URL.
521
+ */
522
+ extractPath(url) {
523
+ try {
524
+ return new URL(url).pathname;
525
+ } catch {
526
+ return "/";
527
+ }
528
+ }
529
+ };
530
+ var sessionTrackerInstance = null;
531
+ function initSessionTracking(options) {
532
+ if (sessionTrackerInstance) {
533
+ console.warn("[Outlit] Session tracking already initialized");
534
+ return sessionTrackerInstance;
535
+ }
536
+ sessionTrackerInstance = new SessionTracker(options);
537
+ return sessionTrackerInstance;
538
+ }
539
+ function stopSessionTracking() {
540
+ if (sessionTrackerInstance) {
541
+ sessionTrackerInstance.stop();
542
+ sessionTrackerInstance = null;
543
+ }
544
+ }
545
+
118
546
  // src/storage.ts
119
547
  var VISITOR_ID_KEY = "outlit_visitor_id";
120
548
  function generateVisitorId() {
@@ -196,7 +624,7 @@ function setCookie(name, value, days) {
196
624
  }
197
625
 
198
626
  // src/tracker.ts
199
- var Tracker = class {
627
+ var Outlit = class {
200
628
  publicKey;
201
629
  apiHost;
202
630
  visitorId = null;
@@ -206,15 +634,29 @@ var Tracker = class {
206
634
  isInitialized = false;
207
635
  isTrackingEnabled = false;
208
636
  options;
637
+ hasHandledExit = false;
638
+ sessionTracker = null;
209
639
  constructor(options) {
210
640
  this.publicKey = options.publicKey;
211
- this.apiHost = options.apiHost ?? import_core2.DEFAULT_API_HOST;
641
+ this.apiHost = options.apiHost ?? import_core3.DEFAULT_API_HOST;
212
642
  this.flushInterval = options.flushInterval ?? 5e3;
213
643
  this.options = options;
214
644
  if (typeof window !== "undefined") {
215
- window.addEventListener("beforeunload", () => {
645
+ const handleExit = () => {
646
+ if (this.hasHandledExit) return;
647
+ this.hasHandledExit = true;
648
+ this.sessionTracker?.emitEngagement();
216
649
  this.flush();
650
+ };
651
+ document.addEventListener("visibilitychange", () => {
652
+ if (document.visibilityState === "hidden") {
653
+ handleExit();
654
+ } else {
655
+ this.hasHandledExit = false;
656
+ }
217
657
  });
658
+ window.addEventListener("pagehide", handleExit);
659
+ window.addEventListener("beforeunload", handleExit);
218
660
  }
219
661
  this.isInitialized = true;
220
662
  if (options.autoTrack !== false) {
@@ -239,12 +681,18 @@ var Tracker = class {
239
681
  }
240
682
  this.visitorId = getOrCreateVisitorId();
241
683
  this.startFlushTimer();
684
+ if (this.options.trackEngagement !== false) {
685
+ this.initSessionTracking();
686
+ }
242
687
  if (this.options.trackPageviews !== false) {
243
688
  this.initPageviewTracking();
244
689
  }
245
690
  if (this.options.trackForms !== false) {
246
691
  this.initFormTracking(this.options.formFieldDenylist);
247
692
  }
693
+ if (this.options.trackCalendarEmbeds !== false) {
694
+ this.initCalendarTracking();
695
+ }
248
696
  this.isTrackingEnabled = true;
249
697
  }
250
698
  /**
@@ -261,7 +709,7 @@ var Tracker = class {
261
709
  console.warn("[Outlit] Tracking not enabled. Call enableTracking() first.");
262
710
  return;
263
711
  }
264
- const event = (0, import_core2.buildCustomEvent)({
712
+ const event = (0, import_core3.buildCustomEvent)({
265
713
  url: window.location.href,
266
714
  referrer: document.referrer,
267
715
  eventName,
@@ -278,7 +726,7 @@ var Tracker = class {
278
726
  console.warn("[Outlit] Tracking not enabled. Call enableTracking() first.");
279
727
  return;
280
728
  }
281
- const event = (0, import_core2.buildIdentifyEvent)({
729
+ const event = (0, import_core3.buildIdentifyEvent)({
282
730
  url: window.location.href,
283
731
  referrer: document.referrer,
284
732
  email: options.email,
@@ -304,7 +752,7 @@ var Tracker = class {
304
752
  await this.sendEvents(events);
305
753
  }
306
754
  /**
307
- * Shutdown the tracker.
755
+ * Shutdown the client.
308
756
  */
309
757
  async shutdown() {
310
758
  if (this.flushTimer) {
@@ -312,14 +760,26 @@ var Tracker = class {
312
760
  this.flushTimer = null;
313
761
  }
314
762
  stopAutocapture();
763
+ stopCalendarTracking();
764
+ stopSessionTracking();
765
+ this.sessionTracker = null;
315
766
  await this.flush();
316
767
  }
317
768
  // ============================================
318
769
  // INTERNAL METHODS
319
770
  // ============================================
771
+ initSessionTracking() {
772
+ this.sessionTracker = initSessionTracking({
773
+ onEngagement: (event) => {
774
+ this.enqueue(event);
775
+ },
776
+ idleTimeout: this.options.idleTimeout
777
+ });
778
+ }
320
779
  initPageviewTracking() {
321
780
  initPageviewTracking((url, referrer, title) => {
322
- const event = (0, import_core2.buildPageviewEvent)({ url, referrer, title });
781
+ this.sessionTracker?.onNavigation(url);
782
+ const event = (0, import_core3.buildPageviewEvent)({ url, referrer, title });
323
783
  this.enqueue(event);
324
784
  });
325
785
  }
@@ -336,7 +796,7 @@ var Tracker = class {
336
796
  } : void 0;
337
797
  initFormTracking(
338
798
  (url, formId, fields) => {
339
- const event = (0, import_core2.buildFormEvent)({
799
+ const event = (0, import_core3.buildFormEvent)({
340
800
  url,
341
801
  referrer: document.referrer,
342
802
  formId,
@@ -348,6 +808,25 @@ var Tracker = class {
348
808
  identityCallback2
349
809
  );
350
810
  }
811
+ initCalendarTracking() {
812
+ initCalendarTracking({
813
+ onCalendarBooked: (bookingEvent) => {
814
+ const event = (0, import_core3.buildCalendarEvent)({
815
+ url: window.location.href,
816
+ referrer: document.referrer,
817
+ provider: bookingEvent.provider,
818
+ eventType: bookingEvent.eventType,
819
+ startTime: bookingEvent.startTime,
820
+ endTime: bookingEvent.endTime,
821
+ duration: bookingEvent.duration,
822
+ isRecurring: bookingEvent.isRecurring,
823
+ inviteeEmail: bookingEvent.inviteeEmail,
824
+ inviteeName: bookingEvent.inviteeName
825
+ });
826
+ this.enqueue(event);
827
+ }
828
+ });
829
+ }
351
830
  enqueue(event) {
352
831
  this.eventQueue.push(event);
353
832
  if (this.eventQueue.length >= 10) {
@@ -363,7 +842,7 @@ var Tracker = class {
363
842
  async sendEvents(events) {
364
843
  if (events.length === 0) return;
365
844
  if (!this.visitorId) return;
366
- const payload = (0, import_core2.buildIngestPayload)(this.visitorId, "pixel", events);
845
+ const payload = (0, import_core3.buildIngestPayload)(this.visitorId, "client", events);
367
846
  const url = `${this.apiHost}/api/i/v1/${this.publicKey}/events`;
368
847
  try {
369
848
  if (typeof navigator !== "undefined" && navigator.sendBeacon) {
@@ -387,15 +866,15 @@ var Tracker = class {
387
866
  var instance = null;
388
867
  function init(options) {
389
868
  if (instance) {
390
- console.warn("[Outlit] Tracker already initialized");
869
+ console.warn("[Outlit] Already initialized");
391
870
  return instance;
392
871
  }
393
- instance = new Tracker(options);
872
+ instance = new Outlit(options);
394
873
  return instance;
395
874
  }
396
875
  function getInstance() {
397
876
  if (!instance) {
398
- throw new Error("[Outlit] Tracker not initialized. Call init() first.");
877
+ throw new Error("[Outlit] Not initialized. Call init() first.");
399
878
  }
400
879
  return instance;
401
880
  }
@@ -418,13 +897,13 @@ var src_default = {
418
897
  track,
419
898
  identify,
420
899
  getInstance,
421
- Tracker,
900
+ Outlit,
422
901
  enableTracking,
423
902
  isTrackingEnabled
424
903
  };
425
904
  // Annotate the CommonJS export names for ESM import in node:
426
905
  0 && (module.exports = {
427
- Tracker,
906
+ Outlit,
428
907
  enableTracking,
429
908
  getInstance,
430
909
  identify,