@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.
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +498 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +492 -12
- package/dist/index.mjs.map +1 -1
- package/dist/outlit.global.js +1 -1
- package/dist/outlit.global.js.map +1 -1
- package/dist/react/index.d.mts +12 -12
- package/dist/react/index.d.ts +12 -12
- package/dist/react/index.js +512 -33
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +507 -27
- package/dist/react/index.mjs.map +1 -1
- package/dist/{tracker-CocH64L9.d.mts → tracker-DFcTv3EM.d.mts} +39 -9
- package/dist/{tracker-CocH64L9.d.ts → tracker-DFcTv3EM.d.ts} +39 -9
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as init, t as track, a as identify, g as getInstance,
|
|
2
|
-
export { c as
|
|
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
|
-
|
|
10
|
+
Outlit: typeof Outlit;
|
|
11
11
|
enableTracking: typeof enableTracking;
|
|
12
12
|
isTrackingEnabled: typeof isTrackingEnabled;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
export {
|
|
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,
|
|
2
|
-
export { c as
|
|
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
|
-
|
|
10
|
+
Outlit: typeof Outlit;
|
|
11
11
|
enableTracking: typeof enableTracking;
|
|
12
12
|
isTrackingEnabled: typeof isTrackingEnabled;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
export {
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
66
|
+
capturePageviewDelayed();
|
|
64
67
|
};
|
|
65
68
|
history.replaceState = function(...args) {
|
|
66
69
|
originalReplaceState.apply(this, args);
|
|
67
|
-
|
|
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
|
|
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 ??
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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]
|
|
869
|
+
console.warn("[Outlit] Already initialized");
|
|
391
870
|
return instance;
|
|
392
871
|
}
|
|
393
|
-
instance = new
|
|
872
|
+
instance = new Outlit(options);
|
|
394
873
|
return instance;
|
|
395
874
|
}
|
|
396
875
|
function getInstance() {
|
|
397
876
|
if (!instance) {
|
|
398
|
-
throw new Error("[Outlit]
|
|
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
|
-
|
|
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
|
-
|
|
906
|
+
Outlit,
|
|
428
907
|
enableTracking,
|
|
429
908
|
getInstance,
|
|
430
909
|
identify,
|