tracking-lib-ott 1.0.8 → 1.0.9

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 CHANGED
@@ -251,6 +251,192 @@ updateSessionConfig({ progressInterval: 10 * 60 * 1000 });
251
251
 
252
252
  ---
253
253
 
254
+ ## Session ID Cookie Management
255
+
256
+ Thư viện hỗ trợ **tự động quản lý `session_id`** thông qua cookie. Nếu bạn không truyền `session_id` khi khởi tạo, thư viện sẽ tự động tạo và lưu vào cookie với thời hạn 30 phút.
257
+
258
+ ### Tính năng
259
+
260
+ - ✅ **Tự động tạo** `session_id` nếu không được truyền lên
261
+ - ✅ **Lưu vào cookie** với thời hạn mặc định 30 phút
262
+ - ✅ **Hỗ trợ group domain** (ví dụ: `.example.com`)
263
+ - ✅ **Tự động gia hạn** cookie mỗi khi có event `session_start`
264
+ - ✅ **Persistent** - Session ID được giữ nguyên khi refresh trang trong vòng 30 phút
265
+
266
+ ### Cách sử dụng
267
+
268
+ #### 1. Auto-generate Session ID (Khuyến nghị)
269
+
270
+ ```javascript
271
+ import tracking from "tracking-lib-ott";
272
+
273
+ // Không cần truyền session_id - thư viện tự động tạo và lưu cookie
274
+ tracking.init({
275
+ baseURL: "https://api.example.com/v1/event_batch",
276
+ sessionSign: "session-signature",
277
+ userId: 12345,
278
+ deviceId: "device-unique-id",
279
+ deviceModel: "iPhone 14",
280
+ deviceBrand: "Apple",
281
+ ipAddress: "192.168.1.1",
282
+ debug: true,
283
+ });
284
+
285
+ // Session ID đã được tạo tự động và lưu vào cookie: _tracking_session_id
286
+ ```
287
+
288
+ #### 2. Custom Cookie Configuration
289
+
290
+ ```javascript
291
+ tracking.init({
292
+ baseURL: "https://api.example.com/v1/event_batch",
293
+ sessionSign: "session-signature",
294
+ userId: 12345,
295
+ deviceId: "device-unique-id",
296
+ deviceModel: "iPhone 14",
297
+ deviceBrand: "Apple",
298
+ ipAddress: "192.168.1.1",
299
+
300
+ // Cấu hình cookie tùy chỉnh
301
+ sessionCookieConfig: {
302
+ cookieName: "my_session_id", // Tên cookie (mặc định: _tracking_session_id)
303
+ expirationMinutes: 60, // Thời hạn 60 phút (mặc định: 30)
304
+ domain: ".example.com", // Group domain cho tất cả subdomain
305
+ path: "/", // Cookie path
306
+ sameSite: "Lax", // 'Strict' | 'Lax' | 'None'
307
+ },
308
+
309
+ debug: true,
310
+ });
311
+ ```
312
+
313
+ #### 3. Kết hợp với Session Tracking
314
+
315
+ ```javascript
316
+ import { useEffect } from "react";
317
+ import tracking from "tracking-lib-ott";
318
+
319
+ function MyApp({ Component, pageProps }) {
320
+ useEffect(() => {
321
+ // 1. Init tracking (session_id tự động tạo)
322
+ tracking.init({
323
+ baseURL: "https://api.example.com/v1/event_batch",
324
+ sessionSign: "session-signature",
325
+ userId: 12345,
326
+ deviceId: "device-id",
327
+ deviceModel: navigator.userAgent,
328
+ deviceBrand: "Web Browser",
329
+ ipAddress: "",
330
+ sessionCookieConfig: {
331
+ domain: ".example.com",
332
+ expirationMinutes: 30,
333
+ },
334
+ debug: process.env.NODE_ENV === "development",
335
+ });
336
+
337
+ // 2. Init session tracking
338
+ tracking.initSession({
339
+ autoStart: true,
340
+ progressInterval: 5 * 60 * 1000,
341
+ sessionCookieConfig: {
342
+ domain: ".example.com",
343
+ expirationMinutes: 30,
344
+ },
345
+ });
346
+
347
+ return () => {
348
+ tracking.cleanupSession();
349
+ };
350
+ }, []);
351
+
352
+ return <Component {...pageProps} />;
353
+ }
354
+ ```
355
+
356
+ ### Cookie Configuration Options
357
+
358
+ | Option | Type | Default | Mô tả |
359
+ | ------------------- | --------------------------------- | ---------------------- | --------------------------------------------------------- |
360
+ | `cookieName` | string | `_tracking_session_id` | Tên cookie lưu session_id |
361
+ | `expirationMinutes` | number | `30` | Thời gian hết hạn cookie (phút) |
362
+ | `domain` | string | `""` | Domain cho cookie (hỗ trợ group domain như `.domain.com`) |
363
+ | `path` | string | `"/"` | Cookie path |
364
+ | `sameSite` | `"Strict"` \| `"Lax"` \| `"None"` | `"Lax"` | SameSite attribute |
365
+
366
+ ### Cơ chế hoạt động
367
+
368
+ 1. **Lần đầu khởi tạo** (không có cookie):
369
+ - Thư viện tạo `session_id` mới (format: `{timestamp}_{random}`)
370
+ - Lưu vào cookie với thời hạn 30 phút
371
+ - Ví dụ: `1737603823000_a8f3d9e2`
372
+
373
+ 2. **Refresh trang** (trong vòng 30 phút):
374
+ - Thư viện đọc `session_id` từ cookie
375
+ - Sử dụng lại session_id cũ
376
+ - Không tạo session mới
377
+
378
+ 3. **Session Start Event**:
379
+ - Mỗi khi có event `session_start`
380
+ - Cookie được **gia hạn thêm 30 phút** từ thời điểm hiện tại
381
+ - Ví dụ: 9:15 trigger session_start → cookie expires lúc 9:45
382
+
383
+ 4. **Sau 30 phút không hoạt động**:
384
+ - Cookie hết hạn và bị xóa
385
+ - Lần truy cập tiếp theo sẽ tạo `session_id` mới
386
+
387
+ ### Manual Cookie Management
388
+
389
+ ```javascript
390
+ import {
391
+ generateSessionId,
392
+ getCookie,
393
+ setCookie,
394
+ getOrCreateSessionId,
395
+ renewSessionCookie,
396
+ clearSessionCookie,
397
+ } from "tracking-lib-ott";
398
+
399
+ // Tạo session ID mới
400
+ const newSessionId = generateSessionId();
401
+ console.log(newSessionId); // "1737603823000_a8f3d9e2"
402
+
403
+ // Lấy hoặc tạo session ID
404
+ const sessionId = getOrCreateSessionId({
405
+ cookieName: "my_session",
406
+ expirationMinutes: 30,
407
+ domain: ".example.com",
408
+ });
409
+
410
+ // Gia hạn cookie
411
+ renewSessionCookie(sessionId, {
412
+ cookieName: "my_session",
413
+ expirationMinutes: 30,
414
+ });
415
+
416
+ // Xóa cookie
417
+ clearSessionCookie({ cookieName: "my_session" });
418
+
419
+ // Đọc cookie thủ công
420
+ const value = getCookie("my_session");
421
+ ```
422
+
423
+ ### User-Provided Session ID
424
+
425
+ Nếu bạn muốn tự quản lý `session_id`, vẫn có thể truyền vào như trước:
426
+
427
+ ```javascript
428
+ tracking.init({
429
+ baseURL: "https://api.example.com/v1/event_batch",
430
+ sessionId: "my-custom-session-id", // Tự cung cấp
431
+ sessionSign: "session-signature",
432
+ // ... other config
433
+ });
434
+
435
+ // Cookie sẽ KHÔNG được tạo khi bạn tự truyền session_id
436
+ ```
437
+
438
+ ---
439
+
254
440
  ## Custom Page Mapping
255
441
 
256
442
  Mặc định thư viện có sẵn mapping cho các page phổ biến. Bạn có thể tuỳ chỉnh:
package/dist/index.cjs CHANGED
@@ -21,18 +21,24 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  cleanupSessionTracking: () => cleanupSessionTracking,
24
+ clearSessionCookie: () => clearSessionCookie,
24
25
  default: () => index_default,
25
26
  defaultConfig: () => defaultConfig,
26
27
  defaultPageMap: () => defaultPageMap,
28
+ generateSessionId: () => generateSessionId,
29
+ getCookie: () => getCookie,
30
+ getOrCreateSessionId: () => getOrCreateSessionId,
27
31
  getPageInfo: () => getPageInfo,
28
32
  getSessionDuration: () => getSessionDuration,
29
33
  initSessionTracking: () => initSessionTracking,
30
34
  initTracking: () => initTracking,
31
35
  isSessionStarted: () => isSessionStarted,
36
+ renewSessionCookie: () => renewSessionCookie,
32
37
  sendEvent: () => sendEvent,
33
38
  sendSessionEnd: () => sendSessionEnd,
34
39
  sendSessionProgress: () => sendSessionProgress,
35
40
  sendSessionStart: () => sendSessionStart,
41
+ setCookie: () => setCookie,
36
42
  trackEvent: () => trackEvent,
37
43
  trackPageView: () => trackPageView,
38
44
  updateSessionConfig: () => updateSessionConfig,
@@ -81,12 +87,92 @@ var defaultPageMap = {
81
87
  login: { name: "Login", id: "login" }
82
88
  };
83
89
 
90
+ // src/cookie-utils.ts
91
+ var defaultCookieConfig = {
92
+ cookieName: "_tracking_session_id",
93
+ expirationMinutes: 30,
94
+ domain: "",
95
+ path: "/",
96
+ sameSite: "Lax"
97
+ };
98
+ var generateSessionId = () => {
99
+ const timestamp = Date.now();
100
+ const random = Math.random().toString(36).substring(2, 10);
101
+ return `${timestamp}_${random}`;
102
+ };
103
+ var getCookie = (name) => {
104
+ if (typeof document === "undefined") return null;
105
+ const nameEQ = name + "=";
106
+ const cookies = document.cookie.split(";");
107
+ for (let i = 0; i < cookies.length; i++) {
108
+ let cookie = cookies[i];
109
+ while (cookie.charAt(0) === " ") {
110
+ cookie = cookie.substring(1);
111
+ }
112
+ if (cookie.indexOf(nameEQ) === 0) {
113
+ return cookie.substring(nameEQ.length);
114
+ }
115
+ }
116
+ return null;
117
+ };
118
+ var setCookie = (name, value, options = {}) => {
119
+ if (typeof document === "undefined") return;
120
+ const config2 = { ...defaultCookieConfig, ...options };
121
+ const expirationMs = config2.expirationMinutes * 60 * 1e3;
122
+ const expires = new Date(Date.now() + expirationMs);
123
+ let cookieString = `${name}=${value}; expires=${expires.toUTCString()}; path=${config2.path}`;
124
+ if (config2.domain) {
125
+ cookieString += `; domain=${config2.domain}`;
126
+ }
127
+ if (config2.sameSite) {
128
+ cookieString += `; SameSite=${config2.sameSite}`;
129
+ }
130
+ if (config2.sameSite === "None") {
131
+ cookieString += "; Secure";
132
+ }
133
+ document.cookie = cookieString;
134
+ };
135
+ var getOrCreateSessionId = (options = {}) => {
136
+ const config2 = { ...defaultCookieConfig, ...options };
137
+ const existingSessionId = getCookie(config2.cookieName);
138
+ if (existingSessionId) {
139
+ return existingSessionId;
140
+ }
141
+ const newSessionId = generateSessionId();
142
+ setCookie(config2.cookieName, newSessionId, options);
143
+ return newSessionId;
144
+ };
145
+ var renewSessionCookie = (sessionId, options = {}) => {
146
+ const config2 = { ...defaultCookieConfig, ...options };
147
+ setCookie(config2.cookieName, sessionId, options);
148
+ };
149
+ var clearSessionCookie = (options = {}) => {
150
+ if (typeof document === "undefined") return;
151
+ const config2 = { ...defaultCookieConfig, ...options };
152
+ let cookieString = `${config2.cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${config2.path}`;
153
+ if (config2.domain) {
154
+ cookieString += `; domain=${config2.domain}`;
155
+ }
156
+ document.cookie = cookieString;
157
+ };
158
+
84
159
  // src/tracking.ts
85
160
  var config = { ...defaultConfig };
86
161
  var previousPageId = null;
87
162
  var pageMap = { ...defaultPageMap };
88
163
  var initTracking = (options = {}) => {
89
164
  config = { ...config, ...options };
165
+ if (!config.sessionId) {
166
+ config.sessionId = getOrCreateSessionId(config.sessionCookieConfig);
167
+ if (config.debug) {
168
+ console.log(
169
+ "[Tracking] Auto-generated session_id from cookie:",
170
+ config.sessionId
171
+ );
172
+ }
173
+ } else if (config.debug) {
174
+ console.log("[Tracking] Using user-provided session_id:", config.sessionId);
175
+ }
90
176
  if (options.pageMap) {
91
177
  pageMap = { ...pageMap, ...options.pageMap };
92
178
  }
@@ -221,6 +307,7 @@ var sendSessionStart = () => {
221
307
  event_time: sessionStartTime
222
308
  });
223
309
  log("Session started at:", new Date(sessionStartTime).toISOString());
310
+ renewSessionIdCookie();
224
311
  startProgressTimer();
225
312
  };
226
313
  var sendSessionProgress = () => {
@@ -323,6 +410,16 @@ var getSessionDuration = () => {
323
410
  if (!isSessionActive) return 0;
324
411
  return Date.now() - sessionStartTime;
325
412
  };
413
+ var renewSessionIdCookie = () => {
414
+ if (sessionConfig.sessionCookieConfig) {
415
+ const cookieName = sessionConfig.sessionCookieConfig.cookieName || "_tracking_session_id";
416
+ const sessionId = document.cookie.split("; ").find((row) => row.startsWith(cookieName + "="))?.split("=")[1];
417
+ if (sessionId) {
418
+ renewSessionCookie(sessionId, sessionConfig.sessionCookieConfig);
419
+ log("Session cookie renewed for:", sessionId);
420
+ }
421
+ }
422
+ };
326
423
 
327
424
  // src/index.ts
328
425
  var tracking = {
@@ -343,17 +440,23 @@ var index_default = tracking;
343
440
  // Annotate the CommonJS export names for ESM import in node:
344
441
  0 && (module.exports = {
345
442
  cleanupSessionTracking,
443
+ clearSessionCookie,
346
444
  defaultConfig,
347
445
  defaultPageMap,
446
+ generateSessionId,
447
+ getCookie,
448
+ getOrCreateSessionId,
348
449
  getPageInfo,
349
450
  getSessionDuration,
350
451
  initSessionTracking,
351
452
  initTracking,
352
453
  isSessionStarted,
454
+ renewSessionCookie,
353
455
  sendEvent,
354
456
  sendSessionEnd,
355
457
  sendSessionProgress,
356
458
  sendSessionStart,
459
+ setCookie,
357
460
  trackEvent,
358
461
  trackPageView,
359
462
  updateSessionConfig,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/tracking.ts","../src/session.ts"],"sourcesContent":["/**\r\n * Tracking Library v2 (Fetch + TypeScript)\r\n * Thư viện tracking events có thể dùng cho nhiều project\r\n */\r\n\r\n// Re-export types\r\nexport type {\r\n TrackingConfig,\r\n PageInfo,\r\n PageMap,\r\n EventData,\r\n NextRouterLike,\r\n} from \"./types\";\r\n\r\nexport type { SessionConfig } from \"./session\";\r\n\r\n// Re-export constants\r\nexport { defaultConfig, defaultPageMap } from \"./constants\";\r\n\r\n// Re-export functions\r\nexport {\r\n initTracking,\r\n getPageInfo,\r\n sendEvent,\r\n trackPageView,\r\n trackEvent,\r\n usePageViewTracking,\r\n} from \"./tracking\";\r\n\r\n// Re-export session functions\r\nexport {\r\n initSessionTracking,\r\n cleanupSessionTracking,\r\n sendSessionStart,\r\n sendSessionProgress,\r\n sendSessionEnd,\r\n updateSessionConfig,\r\n isSessionStarted,\r\n getSessionDuration,\r\n} from \"./session\";\r\n\r\n// Import for default export\r\nimport {\r\n initTracking,\r\n getPageInfo,\r\n sendEvent,\r\n trackPageView,\r\n trackEvent,\r\n usePageViewTracking,\r\n} from \"./tracking\";\r\n\r\nimport {\r\n initSessionTracking,\r\n cleanupSessionTracking,\r\n sendSessionStart,\r\n sendSessionProgress,\r\n sendSessionEnd,\r\n} from \"./session\";\r\n\r\n// Export default object\r\nconst tracking = {\r\n init: initTracking,\r\n trackPageView,\r\n trackEvent,\r\n sendEvent,\r\n getPageInfo,\r\n usePageViewTracking,\r\n // Session lifecycle\r\n initSession: initSessionTracking,\r\n cleanupSession: cleanupSessionTracking,\r\n sessionStart: sendSessionStart,\r\n sessionProgress: sendSessionProgress,\r\n sessionEnd: sendSessionEnd,\r\n};\r\n\r\nexport default tracking;\r\n","/**\r\n * Constants for Tracking Library\r\n */\r\n\r\nimport type { TrackingConfig, PageMap } from \"./types\";\r\n\r\nexport const defaultConfig: TrackingConfig = {\r\n baseURL: \"\",\r\n appName: \"on_plus\",\r\n appId: \"\",\r\n packageId: \"com.vtvcab.onsportstv\",\r\n platform: \"Web\",\r\n debug: false,\r\n\r\n // Required properties\r\n sessionId: \"\",\r\n sessionSign: \"\",\r\n userId: -1,\r\n deviceId: \"\",\r\n deviceModel: \"\",\r\n deviceBrand: \"\",\r\n ipAddress: \"\",\r\n\r\n // Optional properties\r\n adId: \"\",\r\n osVersion: \"\",\r\n netType: \"\",\r\n carrierNet: \"\",\r\n sdkVersion: \"\",\r\n appVersion: \"\",\r\n buildNumber: \"\",\r\n language: \"\",\r\n country: \"\",\r\n\r\n events: [],\r\n};\r\n\r\nexport const defaultPageMap: PageMap = {\r\n \"\": { name: \"Home\", id: \"home\" },\r\n home: { name: \"Home\", id: \"home\" },\r\n search: { name: \"Search\", id: \"search\" },\r\n detail: { name: \"Detail\", id: \"detail\" },\r\n player: { name: \"Player\", id: \"player\" },\r\n video: { name: \"Video\", id: \"video\" },\r\n live: { name: \"Live\", id: \"live\" },\r\n category: { name: \"Category\", id: \"category\" },\r\n profile: { name: \"Profile\", id: \"profile\" },\r\n login: { name: \"Login\", id: \"login\" },\r\n};\r\n","/**\r\n * Core Tracking Functions\r\n */\r\n\r\nimport type {\r\n TrackingConfig,\r\n PageMap,\r\n PageInfo,\r\n EventData,\r\n NextRouterLike,\r\n} from \"./types\";\r\nimport { defaultConfig, defaultPageMap } from \"./constants\";\r\n\r\n// Config runtime\r\nlet config: TrackingConfig = { ...defaultConfig };\r\n\r\n// State\r\nlet previousPageId: string | null = null;\r\n\r\n// Page mapping\r\nlet pageMap: any = { ...defaultPageMap };\r\n\r\n/**\r\n * Khởi tạo tracking với config\r\n */\r\nexport const initTracking = (\r\n options: Partial<TrackingConfig> & { pageMap?: Partial<PageMap> } = {},\r\n) => {\r\n config = { ...config, ...options };\r\n\r\n if (options.pageMap) {\r\n pageMap = { ...pageMap, ...options.pageMap };\r\n }\r\n\r\n if (!config.baseURL) {\r\n console.warn(\"[Tracking] baseURL is required.\");\r\n }\r\n\r\n if (config.debug) {\r\n console.log(\"[Tracking] Initialized with config:\", config);\r\n }\r\n};\r\n\r\n/**\r\n * Lấy thông tin page từ URL\r\n */\r\nexport const getPageInfo = (url: string): PageInfo => {\r\n const path = (url || \"\").split(\"?\")[0];\r\n const pathSegment = path.split(\"/\").filter(Boolean)[0] || \"home\";\r\n\r\n return (\r\n pageMap[pathSegment] || {\r\n name: pathSegment.charAt(0).toUpperCase() + pathSegment.slice(1),\r\n id: pathSegment,\r\n }\r\n );\r\n};\r\n\r\n/**\r\n * Send Event -> POST JSON\r\n */\r\nexport const sendEvent = async (\r\n eventData: EventData,\r\n): Promise<Response | void> => {\r\n if (!config.baseURL) {\r\n console.warn(\"[Tracking] Not initialized. Call initTracking() first.\");\r\n return;\r\n }\r\n\r\n try {\r\n // Tách event_name ra khỏi eventData để tạo event object\r\n const { event_name, ...eventParams } = eventData;\r\n\r\n // Tạo event object với format chuẩn\r\n const eventObject = {\r\n event_name: event_name || \"unknown\",\r\n event_params: JSON.stringify(eventParams),\r\n event_time: Math.floor(Date.now() / 1000),\r\n user_id: config.userId,\r\n };\r\n\r\n const payload = {\r\n // Required properties\r\n app_name: config.appName,\r\n app_id: config.appId,\r\n package_id: config.packageId,\r\n platform: config.platform,\r\n\r\n session_id: config.sessionId,\r\n session_sign: config.sessionSign,\r\n user_id: config.userId,\r\n\r\n device_id: config.deviceId,\r\n device_model: config.deviceModel,\r\n device_brand: config.deviceBrand,\r\n ip_address: config.ipAddress,\r\n\r\n // Optional properties\r\n ad_id: config.adId,\r\n os_version: config.osVersion,\r\n net_type: config.netType,\r\n carrier_net: config.carrierNet,\r\n sdk_version: config.sdkVersion,\r\n app_version: config.appVersion,\r\n build_number: config.buildNumber,\r\n language: config.language,\r\n country: config.country,\r\n\r\n // Events batch - tự động thêm event vào mảng\r\n events: [...(config.events ?? []), eventObject],\r\n };\r\n\r\n if (config.debug) {\r\n console.log(\"[Tracking] Sending event:\", payload);\r\n }\r\n\r\n const res = await fetch(config.baseURL, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify(payload),\r\n });\r\n\r\n if (!res.ok && config.debug) {\r\n const text = await res.text().catch(() => \"\");\r\n console.error(\"[Tracking] HTTP Error:\", res.status, text);\r\n }\r\n\r\n return res;\r\n } catch (error) {\r\n if (config.debug) {\r\n console.error(\"[Tracking] Error:\", error);\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Track page view event\r\n */\r\nexport const trackPageView = (url: string) => {\r\n const pageInfo = getPageInfo(url);\r\n\r\n sendEvent({\r\n event_name: \"page_view\",\r\n page_name: pageInfo.name,\r\n page_id: pageInfo.id,\r\n page_path: url,\r\n referrer_page_id: previousPageId || \"\",\r\n });\r\n\r\n previousPageId = pageInfo.id;\r\n};\r\n\r\n/**\r\n * Track custom event\r\n */\r\nexport const trackEvent = (eventName: string, params: EventData = {}) => {\r\n sendEvent({\r\n event_name: eventName,\r\n ...params,\r\n });\r\n};\r\n\r\n/**\r\n * Hook cho Next.js - auto track page views\r\n * Dùng cho Next.js Pages Router (next/router)\r\n */\r\nexport const usePageViewTracking = (router: NextRouterLike) => {\r\n if (typeof window === \"undefined\") return;\r\n if (!router) return;\r\n\r\n // Track initial page\r\n trackPageView(router.asPath);\r\n\r\n const handleRouteChange = (url: string) => {\r\n trackPageView(url);\r\n };\r\n\r\n router.events.on(\"routeChangeComplete\", handleRouteChange);\r\n\r\n return () => {\r\n router.events.off(\"routeChangeComplete\", handleRouteChange);\r\n };\r\n};\r\n","/**\r\n * Session Lifecycle Tracking\r\n * - session_start: Khi user mở app\r\n * - session_progress: Định kỳ khi user đang active (foreground)\r\n * - session_end: Khi user thoát app\r\n */\r\n\r\nimport { sendEvent } from \"./tracking\";\r\n\r\nexport type SessionConfig = {\r\n /** Khoảng thời gian gửi session_progress (ms). Default: 5 phút */\r\n progressInterval?: number;\r\n /** Có tự động start session khi init không. Default: true */\r\n autoStart?: boolean;\r\n /** Debug mode */\r\n debug?: boolean;\r\n /** User ID */\r\n userId?: string;\r\n};\r\n\r\nconst defaultSessionConfig: SessionConfig = {\r\n progressInterval: 5 * 60 * 1000, // 5 phút\r\n autoStart: true,\r\n debug: false,\r\n};\r\n\r\n// State\r\nlet sessionConfig: SessionConfig = { ...defaultSessionConfig };\r\nlet progressTimer: ReturnType<typeof setInterval> | null = null;\r\nlet isSessionActive = false;\r\nlet sessionStartTime: number = 0;\r\n\r\n/**\r\n * Log helper\r\n */\r\nconst log = (...args: any[]) => {\r\n if (sessionConfig.debug) {\r\n console.log(\"[Session]\", ...args);\r\n }\r\n};\r\n\r\n/**\r\n * Gửi event session_start\r\n */\r\nexport const sendSessionStart = () => {\r\n if (isSessionActive) {\r\n log(\"Session already started, skipping...\");\r\n return;\r\n }\r\n\r\n sessionStartTime = Date.now();\r\n isSessionActive = true;\r\n\r\n sendEvent({\r\n event_name: \"session_start\",\r\n event_time: sessionStartTime,\r\n });\r\n\r\n log(\"Session started at:\", new Date(sessionStartTime).toISOString());\r\n\r\n // Bắt đầu timer cho session_progress\r\n startProgressTimer();\r\n};\r\n\r\n/**\r\n * Gửi event session_progress\r\n */\r\nexport const sendSessionProgress = () => {\r\n if (!isSessionActive) return;\r\n\r\n const now = Math.floor(Date.now() / 1000);\r\n const duration = now - sessionStartTime;\r\n\r\n sendEvent({\r\n event_name: \"session_progress\",\r\n session_duration: duration,\r\n });\r\n\r\n log(\"Session progress - Duration:\", Math.round(duration / 1000), \"seconds\");\r\n};\r\n\r\n/**\r\n * Gửi event session_end\r\n */\r\nexport const sendSessionEnd = () => {\r\n if (!isSessionActive) return;\r\n\r\n const now = Math.floor(Date.now() / 1000);\r\n const duration = now - sessionStartTime;\r\n\r\n // Dùng sendBeacon để đảm bảo gửi được khi page unload\r\n // Fallback về sendEvent nếu không support\r\n sendEvent({\r\n event_name: \"session_end\",\r\n session_duration: duration,\r\n });\r\n\r\n log(\r\n \"Session ended - Total duration:\",\r\n Math.round(duration / 1000),\r\n \"seconds\",\r\n );\r\n\r\n stopProgressTimer();\r\n isSessionActive = false;\r\n};\r\n\r\n/**\r\n * Start progress timer\r\n */\r\nconst startProgressTimer = () => {\r\n stopProgressTimer(); // Clear existing timer\r\n\r\n if (sessionConfig.progressInterval && sessionConfig.progressInterval > 0) {\r\n progressTimer = setInterval(() => {\r\n if (document.visibilityState === \"visible\") {\r\n sendSessionProgress();\r\n }\r\n }, sessionConfig.progressInterval);\r\n\r\n log(\r\n \"Progress timer started, interval:\",\r\n sessionConfig.progressInterval,\r\n \"ms\",\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Stop progress timer\r\n */\r\nconst stopProgressTimer = () => {\r\n if (progressTimer) {\r\n clearInterval(progressTimer);\r\n progressTimer = null;\r\n log(\"Progress timer stopped\");\r\n }\r\n};\r\n\r\n/**\r\n * Handle visibility change\r\n * Pause/resume tracking khi user switch tab hoặc minimize\r\n */\r\nconst handleVisibilityChange = () => {\r\n if (document.visibilityState === \"hidden\") {\r\n // User rời khỏi tab - có thể gửi session_progress cuối\r\n log(\"Tab hidden - pausing progress\");\r\n stopProgressTimer();\r\n } else if (document.visibilityState === \"visible\") {\r\n // User quay lại tab - resume timer\r\n log(\"Tab visible - resuming progress\");\r\n if (isSessionActive) {\r\n startProgressTimer();\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Handle page unload (user đóng tab/browser)\r\n */\r\nconst handleBeforeUnload = () => {\r\n sendSessionEnd();\r\n};\r\n\r\n/**\r\n * Handle page hide (mobile: switch app, close tab)\r\n * Dùng pagehide thay vì beforeunload cho mobile\r\n */\r\nconst handlePageHide = (event: PageTransitionEvent) => {\r\n if (event.persisted) {\r\n // Page được cache (bfcache) - không phải thực sự đóng\r\n return;\r\n }\r\n sendSessionEnd();\r\n};\r\n\r\n/**\r\n * Khởi tạo session tracking\r\n */\r\nexport const initSessionTracking = (options: SessionConfig = {}) => {\r\n if (typeof window === \"undefined\") return;\r\n\r\n sessionConfig = { ...defaultSessionConfig, ...options };\r\n\r\n log(\"Initializing with config:\", sessionConfig);\r\n\r\n // Đăng ký event listeners\r\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\r\n window.addEventListener(\"beforeunload\", handleBeforeUnload);\r\n window.addEventListener(\"pagehide\", handlePageHide);\r\n\r\n // Auto start session\r\n if (sessionConfig.autoStart) {\r\n sendSessionStart();\r\n }\r\n};\r\n\r\n/**\r\n * Cleanup - gọi khi unmount component\r\n */\r\nexport const cleanupSessionTracking = () => {\r\n if (typeof window === \"undefined\") return;\r\n\r\n log(\"Cleaning up...\");\r\n\r\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\r\n window.removeEventListener(\"beforeunload\", handleBeforeUnload);\r\n window.removeEventListener(\"pagehide\", handlePageHide);\r\n\r\n stopProgressTimer();\r\n isSessionActive = false;\r\n};\r\n\r\n/**\r\n * Cập nhật config (ví dụ: thay đổi interval)\r\n */\r\nexport const updateSessionConfig = (options: Partial<SessionConfig>) => {\r\n sessionConfig = { ...sessionConfig, ...options };\r\n\r\n // Restart timer nếu interval thay đổi\r\n if (options.progressInterval !== undefined && isSessionActive) {\r\n startProgressTimer();\r\n }\r\n\r\n log(\"Config updated:\", sessionConfig);\r\n};\r\n\r\n/**\r\n * Kiểm tra session đang active không\r\n */\r\nexport const isSessionStarted = () => isSessionActive;\r\n\r\n/**\r\n * Lấy thời gian session hiện tại (ms)\r\n */\r\nexport const getSessionDuration = () => {\r\n if (!isSessionActive) return 0;\r\n return Date.now() - sessionStartTime;\r\n};\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACMO,IAAM,gBAAgC;AAAA,EAC3C,SAAS;AAAA,EACT,SAAS;AAAA,EACT,OAAO;AAAA,EACP,WAAW;AAAA,EACX,UAAU;AAAA,EACV,OAAO;AAAA;AAAA,EAGP,WAAW;AAAA,EACX,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,aAAa;AAAA,EACb,aAAa;AAAA,EACb,WAAW;AAAA;AAAA,EAGX,MAAM;AAAA,EACN,WAAW;AAAA,EACX,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,UAAU;AAAA,EACV,SAAS;AAAA,EAET,QAAQ,CAAC;AACX;AAEO,IAAM,iBAA0B;AAAA,EACrC,IAAI,EAAE,MAAM,QAAQ,IAAI,OAAO;AAAA,EAC/B,MAAM,EAAE,MAAM,QAAQ,IAAI,OAAO;AAAA,EACjC,QAAQ,EAAE,MAAM,UAAU,IAAI,SAAS;AAAA,EACvC,QAAQ,EAAE,MAAM,UAAU,IAAI,SAAS;AAAA,EACvC,QAAQ,EAAE,MAAM,UAAU,IAAI,SAAS;AAAA,EACvC,OAAO,EAAE,MAAM,SAAS,IAAI,QAAQ;AAAA,EACpC,MAAM,EAAE,MAAM,QAAQ,IAAI,OAAO;AAAA,EACjC,UAAU,EAAE,MAAM,YAAY,IAAI,WAAW;AAAA,EAC7C,SAAS,EAAE,MAAM,WAAW,IAAI,UAAU;AAAA,EAC1C,OAAO,EAAE,MAAM,SAAS,IAAI,QAAQ;AACtC;;;AClCA,IAAI,SAAyB,EAAE,GAAG,cAAc;AAGhD,IAAI,iBAAgC;AAGpC,IAAI,UAAe,EAAE,GAAG,eAAe;AAKhC,IAAM,eAAe,CAC1B,UAAoE,CAAC,MAClE;AACH,WAAS,EAAE,GAAG,QAAQ,GAAG,QAAQ;AAEjC,MAAI,QAAQ,SAAS;AACnB,cAAU,EAAE,GAAG,SAAS,GAAG,QAAQ,QAAQ;AAAA,EAC7C;AAEA,MAAI,CAAC,OAAO,SAAS;AACnB,YAAQ,KAAK,iCAAiC;AAAA,EAChD;AAEA,MAAI,OAAO,OAAO;AAChB,YAAQ,IAAI,uCAAuC,MAAM;AAAA,EAC3D;AACF;AAKO,IAAM,cAAc,CAAC,QAA0B;AACpD,QAAM,QAAQ,OAAO,IAAI,MAAM,GAAG,EAAE,CAAC;AACrC,QAAM,cAAc,KAAK,MAAM,GAAG,EAAE,OAAO,OAAO,EAAE,CAAC,KAAK;AAE1D,SACE,QAAQ,WAAW,KAAK;AAAA,IACtB,MAAM,YAAY,OAAO,CAAC,EAAE,YAAY,IAAI,YAAY,MAAM,CAAC;AAAA,IAC/D,IAAI;AAAA,EACN;AAEJ;AAKO,IAAM,YAAY,OACvB,cAC6B;AAC7B,MAAI,CAAC,OAAO,SAAS;AACnB,YAAQ,KAAK,wDAAwD;AACrE;AAAA,EACF;AAEA,MAAI;AAEF,UAAM,EAAE,YAAY,GAAG,YAAY,IAAI;AAGvC,UAAM,cAAc;AAAA,MAClB,YAAY,cAAc;AAAA,MAC1B,cAAc,KAAK,UAAU,WAAW;AAAA,MACxC,YAAY,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,MACxC,SAAS,OAAO;AAAA,IAClB;AAEA,UAAM,UAAU;AAAA;AAAA,MAEd,UAAU,OAAO;AAAA,MACjB,QAAQ,OAAO;AAAA,MACf,YAAY,OAAO;AAAA,MACnB,UAAU,OAAO;AAAA,MAEjB,YAAY,OAAO;AAAA,MACnB,cAAc,OAAO;AAAA,MACrB,SAAS,OAAO;AAAA,MAEhB,WAAW,OAAO;AAAA,MAClB,cAAc,OAAO;AAAA,MACrB,cAAc,OAAO;AAAA,MACrB,YAAY,OAAO;AAAA;AAAA,MAGnB,OAAO,OAAO;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,UAAU,OAAO;AAAA,MACjB,aAAa,OAAO;AAAA,MACpB,aAAa,OAAO;AAAA,MACpB,aAAa,OAAO;AAAA,MACpB,cAAc,OAAO;AAAA,MACrB,UAAU,OAAO;AAAA,MACjB,SAAS,OAAO;AAAA;AAAA,MAGhB,QAAQ,CAAC,GAAI,OAAO,UAAU,CAAC,GAAI,WAAW;AAAA,IAChD;AAEA,QAAI,OAAO,OAAO;AAChB,cAAQ,IAAI,6BAA6B,OAAO;AAAA,IAClD;AAEA,UAAM,MAAM,MAAM,MAAM,OAAO,SAAS;AAAA,MACtC,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,IAC9B,CAAC;AAED,QAAI,CAAC,IAAI,MAAM,OAAO,OAAO;AAC3B,YAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,cAAQ,MAAM,0BAA0B,IAAI,QAAQ,IAAI;AAAA,IAC1D;AAEA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,QAAI,OAAO,OAAO;AAChB,cAAQ,MAAM,qBAAqB,KAAK;AAAA,IAC1C;AAAA,EACF;AACF;AAKO,IAAM,gBAAgB,CAAC,QAAgB;AAC5C,QAAM,WAAW,YAAY,GAAG;AAEhC,YAAU;AAAA,IACR,YAAY;AAAA,IACZ,WAAW,SAAS;AAAA,IACpB,SAAS,SAAS;AAAA,IAClB,WAAW;AAAA,IACX,kBAAkB,kBAAkB;AAAA,EACtC,CAAC;AAED,mBAAiB,SAAS;AAC5B;AAKO,IAAM,aAAa,CAAC,WAAmB,SAAoB,CAAC,MAAM;AACvE,YAAU;AAAA,IACR,YAAY;AAAA,IACZ,GAAG;AAAA,EACL,CAAC;AACH;AAMO,IAAM,sBAAsB,CAAC,WAA2B;AAC7D,MAAI,OAAO,WAAW,YAAa;AACnC,MAAI,CAAC,OAAQ;AAGb,gBAAc,OAAO,MAAM;AAE3B,QAAM,oBAAoB,CAAC,QAAgB;AACzC,kBAAc,GAAG;AAAA,EACnB;AAEA,SAAO,OAAO,GAAG,uBAAuB,iBAAiB;AAEzD,SAAO,MAAM;AACX,WAAO,OAAO,IAAI,uBAAuB,iBAAiB;AAAA,EAC5D;AACF;;;AClKA,IAAM,uBAAsC;AAAA,EAC1C,kBAAkB,IAAI,KAAK;AAAA;AAAA,EAC3B,WAAW;AAAA,EACX,OAAO;AACT;AAGA,IAAI,gBAA+B,EAAE,GAAG,qBAAqB;AAC7D,IAAI,gBAAuD;AAC3D,IAAI,kBAAkB;AACtB,IAAI,mBAA2B;AAK/B,IAAM,MAAM,IAAI,SAAgB;AAC9B,MAAI,cAAc,OAAO;AACvB,YAAQ,IAAI,aAAa,GAAG,IAAI;AAAA,EAClC;AACF;AAKO,IAAM,mBAAmB,MAAM;AACpC,MAAI,iBAAiB;AACnB,QAAI,sCAAsC;AAC1C;AAAA,EACF;AAEA,qBAAmB,KAAK,IAAI;AAC5B,oBAAkB;AAElB,YAAU;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA,EACd,CAAC;AAED,MAAI,uBAAuB,IAAI,KAAK,gBAAgB,EAAE,YAAY,CAAC;AAGnE,qBAAmB;AACrB;AAKO,IAAM,sBAAsB,MAAM;AACvC,MAAI,CAAC,gBAAiB;AAEtB,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,QAAM,WAAW,MAAM;AAEvB,YAAU;AAAA,IACR,YAAY;AAAA,IACZ,kBAAkB;AAAA,EACpB,CAAC;AAED,MAAI,gCAAgC,KAAK,MAAM,WAAW,GAAI,GAAG,SAAS;AAC5E;AAKO,IAAM,iBAAiB,MAAM;AAClC,MAAI,CAAC,gBAAiB;AAEtB,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,QAAM,WAAW,MAAM;AAIvB,YAAU;AAAA,IACR,YAAY;AAAA,IACZ,kBAAkB;AAAA,EACpB,CAAC;AAED;AAAA,IACE;AAAA,IACA,KAAK,MAAM,WAAW,GAAI;AAAA,IAC1B;AAAA,EACF;AAEA,oBAAkB;AAClB,oBAAkB;AACpB;AAKA,IAAM,qBAAqB,MAAM;AAC/B,oBAAkB;AAElB,MAAI,cAAc,oBAAoB,cAAc,mBAAmB,GAAG;AACxE,oBAAgB,YAAY,MAAM;AAChC,UAAI,SAAS,oBAAoB,WAAW;AAC1C,4BAAoB;AAAA,MACtB;AAAA,IACF,GAAG,cAAc,gBAAgB;AAEjC;AAAA,MACE;AAAA,MACA,cAAc;AAAA,MACd;AAAA,IACF;AAAA,EACF;AACF;AAKA,IAAM,oBAAoB,MAAM;AAC9B,MAAI,eAAe;AACjB,kBAAc,aAAa;AAC3B,oBAAgB;AAChB,QAAI,wBAAwB;AAAA,EAC9B;AACF;AAMA,IAAM,yBAAyB,MAAM;AACnC,MAAI,SAAS,oBAAoB,UAAU;AAEzC,QAAI,+BAA+B;AACnC,sBAAkB;AAAA,EACpB,WAAW,SAAS,oBAAoB,WAAW;AAEjD,QAAI,iCAAiC;AACrC,QAAI,iBAAiB;AACnB,yBAAmB;AAAA,IACrB;AAAA,EACF;AACF;AAKA,IAAM,qBAAqB,MAAM;AAC/B,iBAAe;AACjB;AAMA,IAAM,iBAAiB,CAAC,UAA+B;AACrD,MAAI,MAAM,WAAW;AAEnB;AAAA,EACF;AACA,iBAAe;AACjB;AAKO,IAAM,sBAAsB,CAAC,UAAyB,CAAC,MAAM;AAClE,MAAI,OAAO,WAAW,YAAa;AAEnC,kBAAgB,EAAE,GAAG,sBAAsB,GAAG,QAAQ;AAEtD,MAAI,6BAA6B,aAAa;AAG9C,WAAS,iBAAiB,oBAAoB,sBAAsB;AACpE,SAAO,iBAAiB,gBAAgB,kBAAkB;AAC1D,SAAO,iBAAiB,YAAY,cAAc;AAGlD,MAAI,cAAc,WAAW;AAC3B,qBAAiB;AAAA,EACnB;AACF;AAKO,IAAM,yBAAyB,MAAM;AAC1C,MAAI,OAAO,WAAW,YAAa;AAEnC,MAAI,gBAAgB;AAEpB,WAAS,oBAAoB,oBAAoB,sBAAsB;AACvE,SAAO,oBAAoB,gBAAgB,kBAAkB;AAC7D,SAAO,oBAAoB,YAAY,cAAc;AAErD,oBAAkB;AAClB,oBAAkB;AACpB;AAKO,IAAM,sBAAsB,CAAC,YAAoC;AACtE,kBAAgB,EAAE,GAAG,eAAe,GAAG,QAAQ;AAG/C,MAAI,QAAQ,qBAAqB,UAAa,iBAAiB;AAC7D,uBAAmB;AAAA,EACrB;AAEA,MAAI,mBAAmB,aAAa;AACtC;AAKO,IAAM,mBAAmB,MAAM;AAK/B,IAAM,qBAAqB,MAAM;AACtC,MAAI,CAAC,gBAAiB,QAAO;AAC7B,SAAO,KAAK,IAAI,IAAI;AACtB;;;AHlLA,IAAM,WAAW;AAAA,EACf,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,YAAY;AACd;AAEA,IAAO,gBAAQ;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/cookie-utils.ts","../src/tracking.ts","../src/session.ts"],"sourcesContent":["/**\r\n * Tracking Library v2 (Fetch + TypeScript)\r\n * Thư viện tracking events có thể dùng cho nhiều project\r\n */\r\n\r\n// Re-export types\r\nexport type {\r\n TrackingConfig,\r\n PageInfo,\r\n PageMap,\r\n EventData,\r\n NextRouterLike,\r\n} from \"./types\";\r\n\r\nexport type { SessionConfig } from \"./session\";\r\nexport type { SessionCookieConfig } from \"./cookie-utils\";\r\n\r\n// Re-export constants\r\nexport { defaultConfig, defaultPageMap } from \"./constants\";\r\n\r\n// Re-export functions\r\nexport {\r\n initTracking,\r\n getPageInfo,\r\n sendEvent,\r\n trackPageView,\r\n trackEvent,\r\n usePageViewTracking,\r\n} from \"./tracking\";\r\n\r\n// Re-export session functions\r\nexport {\r\n initSessionTracking,\r\n cleanupSessionTracking,\r\n sendSessionStart,\r\n sendSessionProgress,\r\n sendSessionEnd,\r\n updateSessionConfig,\r\n isSessionStarted,\r\n getSessionDuration,\r\n} from \"./session\";\r\n\r\n// Re-export cookie utilities\r\nexport {\r\n generateSessionId,\r\n getCookie,\r\n setCookie,\r\n getOrCreateSessionId,\r\n renewSessionCookie,\r\n clearSessionCookie,\r\n} from \"./cookie-utils\";\r\n\r\n// Import for default export\r\nimport {\r\n initTracking,\r\n getPageInfo,\r\n sendEvent,\r\n trackPageView,\r\n trackEvent,\r\n usePageViewTracking,\r\n} from \"./tracking\";\r\n\r\nimport {\r\n initSessionTracking,\r\n cleanupSessionTracking,\r\n sendSessionStart,\r\n sendSessionProgress,\r\n sendSessionEnd,\r\n} from \"./session\";\r\n\r\n// Export default object\r\nconst tracking = {\r\n init: initTracking,\r\n trackPageView,\r\n trackEvent,\r\n sendEvent,\r\n getPageInfo,\r\n usePageViewTracking,\r\n // Session lifecycle\r\n initSession: initSessionTracking,\r\n cleanupSession: cleanupSessionTracking,\r\n sessionStart: sendSessionStart,\r\n sessionProgress: sendSessionProgress,\r\n sessionEnd: sendSessionEnd,\r\n};\r\n\r\nexport default tracking;\r\n","/**\r\n * Constants for Tracking Library\r\n */\r\n\r\nimport type { TrackingConfig, PageMap } from \"./types\";\r\n\r\nexport const defaultConfig: TrackingConfig = {\r\n baseURL: \"\",\r\n appName: \"on_plus\",\r\n appId: \"\",\r\n packageId: \"com.vtvcab.onsportstv\",\r\n platform: \"Web\",\r\n debug: false,\r\n\r\n // Required properties\r\n sessionId: \"\",\r\n sessionSign: \"\",\r\n userId: -1,\r\n deviceId: \"\",\r\n deviceModel: \"\",\r\n deviceBrand: \"\",\r\n ipAddress: \"\",\r\n\r\n // Optional properties\r\n adId: \"\",\r\n osVersion: \"\",\r\n netType: \"\",\r\n carrierNet: \"\",\r\n sdkVersion: \"\",\r\n appVersion: \"\",\r\n buildNumber: \"\",\r\n language: \"\",\r\n country: \"\",\r\n\r\n events: [],\r\n};\r\n\r\nexport const defaultPageMap: PageMap = {\r\n \"\": { name: \"Home\", id: \"home\" },\r\n home: { name: \"Home\", id: \"home\" },\r\n search: { name: \"Search\", id: \"search\" },\r\n detail: { name: \"Detail\", id: \"detail\" },\r\n player: { name: \"Player\", id: \"player\" },\r\n video: { name: \"Video\", id: \"video\" },\r\n live: { name: \"Live\", id: \"live\" },\r\n category: { name: \"Category\", id: \"category\" },\r\n profile: { name: \"Profile\", id: \"profile\" },\r\n login: { name: \"Login\", id: \"login\" },\r\n};\r\n","/**\r\n * Cookie Utilities for Session Management\r\n */\r\n\r\nexport type SessionCookieConfig = {\r\n /** Tên cookie lưu session_id. Default: '_tracking_session_id' */\r\n cookieName?: string;\r\n /** Thời gian hết hạn cookie (phút). Default: 30 */\r\n expirationMinutes?: number;\r\n /** Domain cho cookie (hỗ trợ group domain như .example.com) */\r\n domain?: string;\r\n /** Path cho cookie. Default: '/' */\r\n path?: string;\r\n /** SameSite attribute. Default: 'Lax' */\r\n sameSite?: \"Strict\" | \"Lax\" | \"None\";\r\n};\r\n\r\nconst defaultCookieConfig: Required<SessionCookieConfig> = {\r\n cookieName: \"_tracking_session_id\",\r\n expirationMinutes: 30,\r\n domain: \"\",\r\n path: \"/\",\r\n sameSite: \"Lax\",\r\n};\r\n\r\n/**\r\n * Tạo session ID ngẫu nhiên\r\n * Format: {timestamp}_{random}\r\n */\r\nexport const generateSessionId = (): string => {\r\n const timestamp = Date.now();\r\n const random = Math.random().toString(36).substring(2, 10);\r\n return `${timestamp}_${random}`;\r\n};\r\n\r\n/**\r\n * Lấy giá trị cookie theo tên\r\n */\r\nexport const getCookie = (name: string): string | null => {\r\n if (typeof document === \"undefined\") return null;\r\n\r\n const nameEQ = name + \"=\";\r\n const cookies = document.cookie.split(\";\");\r\n\r\n for (let i = 0; i < cookies.length; i++) {\r\n let cookie = cookies[i];\r\n while (cookie.charAt(0) === \" \") {\r\n cookie = cookie.substring(1);\r\n }\r\n if (cookie.indexOf(nameEQ) === 0) {\r\n return cookie.substring(nameEQ.length);\r\n }\r\n }\r\n return null;\r\n};\r\n\r\n/**\r\n * Set cookie với các options\r\n */\r\nexport const setCookie = (\r\n name: string,\r\n value: string,\r\n options: SessionCookieConfig = {},\r\n): void => {\r\n if (typeof document === \"undefined\") return;\r\n\r\n const config = { ...defaultCookieConfig, ...options };\r\n const expirationMs = config.expirationMinutes * 60 * 1000;\r\n const expires = new Date(Date.now() + expirationMs);\r\n\r\n let cookieString = `${name}=${value}; expires=${expires.toUTCString()}; path=${config.path}`;\r\n\r\n if (config.domain) {\r\n cookieString += `; domain=${config.domain}`;\r\n }\r\n\r\n if (config.sameSite) {\r\n cookieString += `; SameSite=${config.sameSite}`;\r\n }\r\n\r\n // Nếu SameSite=None thì phải có Secure\r\n if (config.sameSite === \"None\") {\r\n cookieString += \"; Secure\";\r\n }\r\n\r\n document.cookie = cookieString;\r\n};\r\n\r\n/**\r\n * Lấy hoặc tạo mới session_id từ cookie\r\n * Nếu cookie tồn tại và chưa hết hạn -> trả về giá trị cũ\r\n * Nếu không -> tạo mới và lưu vào cookie\r\n */\r\nexport const getOrCreateSessionId = (\r\n options: SessionCookieConfig = {},\r\n): string => {\r\n const config = { ...defaultCookieConfig, ...options };\r\n const existingSessionId = getCookie(config.cookieName);\r\n\r\n if (existingSessionId) {\r\n return existingSessionId;\r\n }\r\n\r\n // Tạo mới session_id\r\n const newSessionId = generateSessionId();\r\n setCookie(config.cookieName, newSessionId, options);\r\n\r\n return newSessionId;\r\n};\r\n\r\n/**\r\n * Gia hạn cookie session_id\r\n * Gọi hàm này khi có event session_start để extend thời gian hết hạn\r\n */\r\nexport const renewSessionCookie = (\r\n sessionId: string,\r\n options: SessionCookieConfig = {},\r\n): void => {\r\n const config = { ...defaultCookieConfig, ...options };\r\n setCookie(config.cookieName, sessionId, options);\r\n};\r\n\r\n/**\r\n * Xóa session cookie\r\n */\r\nexport const clearSessionCookie = (options: SessionCookieConfig = {}): void => {\r\n if (typeof document === \"undefined\") return;\r\n\r\n const config = { ...defaultCookieConfig, ...options };\r\n let cookieString = `${config.cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${config.path}`;\r\n\r\n if (config.domain) {\r\n cookieString += `; domain=${config.domain}`;\r\n }\r\n\r\n document.cookie = cookieString;\r\n};\r\n","/**\r\n * Core Tracking Functions\r\n */\r\n\r\nimport type {\r\n TrackingConfig,\r\n PageMap,\r\n PageInfo,\r\n EventData,\r\n NextRouterLike,\r\n} from \"./types\";\r\nimport { defaultConfig, defaultPageMap } from \"./constants\";\r\nimport { getOrCreateSessionId } from \"./cookie-utils\";\r\n\r\n// Config runtime\r\nlet config: TrackingConfig = { ...defaultConfig };\r\n\r\n// State\r\nlet previousPageId: string | null = null;\r\n\r\n// Page mapping\r\nlet pageMap: any = { ...defaultPageMap };\r\n\r\n/**\r\n * Khởi tạo tracking với config\r\n */\r\nexport const initTracking = (\r\n options: Partial<TrackingConfig> & { pageMap?: Partial<PageMap> } = {},\r\n) => {\r\n config = { ...config, ...options };\r\n\r\n // Auto-generate sessionId from cookie if not provided\r\n if (!config.sessionId) {\r\n config.sessionId = getOrCreateSessionId(config.sessionCookieConfig);\r\n if (config.debug) {\r\n console.log(\r\n \"[Tracking] Auto-generated session_id from cookie:\",\r\n config.sessionId,\r\n );\r\n }\r\n } else if (config.debug) {\r\n console.log(\"[Tracking] Using user-provided session_id:\", config.sessionId);\r\n }\r\n\r\n if (options.pageMap) {\r\n pageMap = { ...pageMap, ...options.pageMap };\r\n }\r\n\r\n if (!config.baseURL) {\r\n console.warn(\"[Tracking] baseURL is required.\");\r\n }\r\n\r\n if (config.debug) {\r\n console.log(\"[Tracking] Initialized with config:\", config);\r\n }\r\n};\r\n\r\n/**\r\n * Lấy thông tin page từ URL\r\n */\r\nexport const getPageInfo = (url: string): PageInfo => {\r\n const path = (url || \"\").split(\"?\")[0];\r\n const pathSegment = path.split(\"/\").filter(Boolean)[0] || \"home\";\r\n\r\n return (\r\n pageMap[pathSegment] || {\r\n name: pathSegment.charAt(0).toUpperCase() + pathSegment.slice(1),\r\n id: pathSegment,\r\n }\r\n );\r\n};\r\n\r\n/**\r\n * Send Event -> POST JSON\r\n */\r\nexport const sendEvent = async (\r\n eventData: EventData,\r\n): Promise<Response | void> => {\r\n if (!config.baseURL) {\r\n console.warn(\"[Tracking] Not initialized. Call initTracking() first.\");\r\n return;\r\n }\r\n\r\n try {\r\n // Tách event_name ra khỏi eventData để tạo event object\r\n const { event_name, ...eventParams } = eventData;\r\n\r\n // Tạo event object với format chuẩn\r\n const eventObject = {\r\n event_name: event_name || \"unknown\",\r\n event_params: JSON.stringify(eventParams),\r\n event_time: Math.floor(Date.now() / 1000),\r\n user_id: config.userId,\r\n };\r\n\r\n const payload = {\r\n // Required properties\r\n app_name: config.appName,\r\n app_id: config.appId,\r\n package_id: config.packageId,\r\n platform: config.platform,\r\n\r\n session_id: config.sessionId,\r\n session_sign: config.sessionSign,\r\n user_id: config.userId,\r\n\r\n device_id: config.deviceId,\r\n device_model: config.deviceModel,\r\n device_brand: config.deviceBrand,\r\n ip_address: config.ipAddress,\r\n\r\n // Optional properties\r\n ad_id: config.adId,\r\n os_version: config.osVersion,\r\n net_type: config.netType,\r\n carrier_net: config.carrierNet,\r\n sdk_version: config.sdkVersion,\r\n app_version: config.appVersion,\r\n build_number: config.buildNumber,\r\n language: config.language,\r\n country: config.country,\r\n\r\n // Events batch - tự động thêm event vào mảng\r\n events: [...(config.events ?? []), eventObject],\r\n };\r\n\r\n if (config.debug) {\r\n console.log(\"[Tracking] Sending event:\", payload);\r\n }\r\n\r\n const res = await fetch(config.baseURL, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify(payload),\r\n });\r\n\r\n if (!res.ok && config.debug) {\r\n const text = await res.text().catch(() => \"\");\r\n console.error(\"[Tracking] HTTP Error:\", res.status, text);\r\n }\r\n\r\n return res;\r\n } catch (error) {\r\n if (config.debug) {\r\n console.error(\"[Tracking] Error:\", error);\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Track page view event\r\n */\r\nexport const trackPageView = (url: string) => {\r\n const pageInfo = getPageInfo(url);\r\n\r\n sendEvent({\r\n event_name: \"page_view\",\r\n page_name: pageInfo.name,\r\n page_id: pageInfo.id,\r\n page_path: url,\r\n referrer_page_id: previousPageId || \"\",\r\n });\r\n\r\n previousPageId = pageInfo.id;\r\n};\r\n\r\n/**\r\n * Track custom event\r\n */\r\nexport const trackEvent = (eventName: string, params: EventData = {}) => {\r\n sendEvent({\r\n event_name: eventName,\r\n ...params,\r\n });\r\n};\r\n\r\n/**\r\n * Hook cho Next.js - auto track page views\r\n * Dùng cho Next.js Pages Router (next/router)\r\n */\r\nexport const usePageViewTracking = (router: NextRouterLike) => {\r\n if (typeof window === \"undefined\") return;\r\n if (!router) return;\r\n\r\n // Track initial page\r\n trackPageView(router.asPath);\r\n\r\n const handleRouteChange = (url: string) => {\r\n trackPageView(url);\r\n };\r\n\r\n router.events.on(\"routeChangeComplete\", handleRouteChange);\r\n\r\n return () => {\r\n router.events.off(\"routeChangeComplete\", handleRouteChange);\r\n };\r\n};\r\n","/**\r\n * Session Lifecycle Tracking\r\n * - session_start: Khi user mở app\r\n * - session_progress: Định kỳ khi user đang active (foreground)\r\n * - session_end: Khi user thoát app\r\n */\r\n\r\nimport { sendEvent } from \"./tracking\";\r\nimport { renewSessionCookie } from \"./cookie-utils\";\r\nimport type { SessionCookieConfig } from \"./cookie-utils\";\r\n\r\nexport type SessionConfig = {\r\n /** Khoảng thời gian gửi session_progress (ms). Default: 5 phút */\r\n progressInterval?: number;\r\n /** Có tự động start session khi init không. Default: true */\r\n autoStart?: boolean;\r\n /** Debug mode */\r\n debug?: boolean;\r\n /** User ID */\r\n userId?: string;\r\n /** Session cookie configuration */\r\n sessionCookieConfig?: SessionCookieConfig;\r\n};\r\n\r\nconst defaultSessionConfig: SessionConfig = {\r\n progressInterval: 5 * 60 * 1000, // 5 phút\r\n autoStart: true,\r\n debug: false,\r\n};\r\n\r\n// State\r\nlet sessionConfig: SessionConfig = { ...defaultSessionConfig };\r\nlet progressTimer: ReturnType<typeof setInterval> | null = null;\r\nlet isSessionActive = false;\r\nlet sessionStartTime: number = 0;\r\n\r\n/**\r\n * Log helper\r\n */\r\nconst log = (...args: any[]) => {\r\n if (sessionConfig.debug) {\r\n console.log(\"[Session]\", ...args);\r\n }\r\n};\r\n\r\n/**\r\n * Gửi event session_start\r\n */\r\nexport const sendSessionStart = () => {\r\n if (isSessionActive) {\r\n log(\"Session already started, skipping...\");\r\n return;\r\n }\r\n\r\n sessionStartTime = Date.now();\r\n isSessionActive = true;\r\n\r\n sendEvent({\r\n event_name: \"session_start\",\r\n event_time: sessionStartTime,\r\n });\r\n\r\n log(\"Session started at:\", new Date(sessionStartTime).toISOString());\r\n\r\n // Gia hạn cookie session_id (extend thêm 30 phút)\r\n renewSessionIdCookie();\r\n\r\n // Bắt đầu timer cho session_progress\r\n startProgressTimer();\r\n};\r\n\r\n/**\r\n * Gửi event session_progress\r\n */\r\nexport const sendSessionProgress = () => {\r\n if (!isSessionActive) return;\r\n\r\n const now = Math.floor(Date.now() / 1000);\r\n const duration = now - sessionStartTime;\r\n\r\n sendEvent({\r\n event_name: \"session_progress\",\r\n session_duration: duration,\r\n });\r\n\r\n log(\"Session progress - Duration:\", Math.round(duration / 1000), \"seconds\");\r\n};\r\n\r\n/**\r\n * Gửi event session_end\r\n */\r\nexport const sendSessionEnd = () => {\r\n if (!isSessionActive) return;\r\n\r\n const now = Math.floor(Date.now() / 1000);\r\n const duration = now - sessionStartTime;\r\n\r\n // Dùng sendBeacon để đảm bảo gửi được khi page unload\r\n // Fallback về sendEvent nếu không support\r\n sendEvent({\r\n event_name: \"session_end\",\r\n session_duration: duration,\r\n });\r\n\r\n log(\r\n \"Session ended - Total duration:\",\r\n Math.round(duration / 1000),\r\n \"seconds\",\r\n );\r\n\r\n stopProgressTimer();\r\n isSessionActive = false;\r\n};\r\n\r\n/**\r\n * Start progress timer\r\n */\r\nconst startProgressTimer = () => {\r\n stopProgressTimer(); // Clear existing timer\r\n\r\n if (sessionConfig.progressInterval && sessionConfig.progressInterval > 0) {\r\n progressTimer = setInterval(() => {\r\n if (document.visibilityState === \"visible\") {\r\n sendSessionProgress();\r\n }\r\n }, sessionConfig.progressInterval);\r\n\r\n log(\r\n \"Progress timer started, interval:\",\r\n sessionConfig.progressInterval,\r\n \"ms\",\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Stop progress timer\r\n */\r\nconst stopProgressTimer = () => {\r\n if (progressTimer) {\r\n clearInterval(progressTimer);\r\n progressTimer = null;\r\n log(\"Progress timer stopped\");\r\n }\r\n};\r\n\r\n/**\r\n * Handle visibility change\r\n * Pause/resume tracking khi user switch tab hoặc minimize\r\n */\r\nconst handleVisibilityChange = () => {\r\n if (document.visibilityState === \"hidden\") {\r\n // User rời khỏi tab - có thể gửi session_progress cuối\r\n log(\"Tab hidden - pausing progress\");\r\n stopProgressTimer();\r\n } else if (document.visibilityState === \"visible\") {\r\n // User quay lại tab - resume timer\r\n log(\"Tab visible - resuming progress\");\r\n if (isSessionActive) {\r\n startProgressTimer();\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Handle page unload (user đóng tab/browser)\r\n */\r\nconst handleBeforeUnload = () => {\r\n sendSessionEnd();\r\n};\r\n\r\n/**\r\n * Handle page hide (mobile: switch app, close tab)\r\n * Dùng pagehide thay vì beforeunload cho mobile\r\n */\r\nconst handlePageHide = (event: PageTransitionEvent) => {\r\n if (event.persisted) {\r\n // Page được cache (bfcache) - không phải thực sự đóng\r\n return;\r\n }\r\n sendSessionEnd();\r\n};\r\n\r\n/**\r\n * Khởi tạo session tracking\r\n */\r\nexport const initSessionTracking = (options: SessionConfig = {}) => {\r\n if (typeof window === \"undefined\") return;\r\n\r\n sessionConfig = { ...defaultSessionConfig, ...options };\r\n\r\n log(\"Initializing with config:\", sessionConfig);\r\n\r\n // Đăng ký event listeners\r\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\r\n window.addEventListener(\"beforeunload\", handleBeforeUnload);\r\n window.addEventListener(\"pagehide\", handlePageHide);\r\n\r\n // Auto start session\r\n if (sessionConfig.autoStart) {\r\n sendSessionStart();\r\n }\r\n};\r\n\r\n/**\r\n * Cleanup - gọi khi unmount component\r\n */\r\nexport const cleanupSessionTracking = () => {\r\n if (typeof window === \"undefined\") return;\r\n\r\n log(\"Cleaning up...\");\r\n\r\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\r\n window.removeEventListener(\"beforeunload\", handleBeforeUnload);\r\n window.removeEventListener(\"pagehide\", handlePageHide);\r\n\r\n stopProgressTimer();\r\n isSessionActive = false;\r\n};\r\n\r\n/**\r\n * Cập nhật config (ví dụ: thay đổi interval)\r\n */\r\nexport const updateSessionConfig = (options: Partial<SessionConfig>) => {\r\n sessionConfig = { ...sessionConfig, ...options };\r\n\r\n // Restart timer nếu interval thay đổi\r\n if (options.progressInterval !== undefined && isSessionActive) {\r\n startProgressTimer();\r\n }\r\n\r\n log(\"Config updated:\", sessionConfig);\r\n};\r\n\r\n/**\r\n * Kiểm tra session đang active không\r\n */\r\nexport const isSessionStarted = () => isSessionActive;\r\n\r\n/**\r\n * Lấy thời gian session hiện tại (ms)\r\n */\r\nexport const getSessionDuration = () => {\r\n if (!isSessionActive) return 0;\r\n return Date.now() - sessionStartTime;\r\n};\r\n\r\n/**\r\n * Gia hạn cookie session_id\r\n * Gọi khi có event session_start để extend thời gian hết hạn\r\n */\r\nconst renewSessionIdCookie = () => {\r\n // Lấy sessionId và config từ tracking module\r\n // Note: Cần export getter function từ tracking.ts để lấy config\r\n if (sessionConfig.sessionCookieConfig) {\r\n // Tạm thời sử dụng cookie name mặc định để lấy sessionId\r\n const cookieName =\r\n sessionConfig.sessionCookieConfig.cookieName || \"_tracking_session_id\";\r\n const sessionId = document.cookie\r\n .split(\"; \")\r\n .find((row) => row.startsWith(cookieName + \"=\"))\r\n ?.split(\"=\")[1];\r\n\r\n if (sessionId) {\r\n renewSessionCookie(sessionId, sessionConfig.sessionCookieConfig);\r\n log(\"Session cookie renewed for:\", sessionId);\r\n }\r\n }\r\n};\r\n"],"mappings":"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","names":["config"]}
package/dist/index.d.cts CHANGED
@@ -1,9 +1,54 @@
1
+ /**
2
+ * Cookie Utilities for Session Management
3
+ */
4
+ type SessionCookieConfig = {
5
+ /** Tên cookie lưu session_id. Default: '_tracking_session_id' */
6
+ cookieName?: string;
7
+ /** Thời gian hết hạn cookie (phút). Default: 30 */
8
+ expirationMinutes?: number;
9
+ /** Domain cho cookie (hỗ trợ group domain như .example.com) */
10
+ domain?: string;
11
+ /** Path cho cookie. Default: '/' */
12
+ path?: string;
13
+ /** SameSite attribute. Default: 'Lax' */
14
+ sameSite?: "Strict" | "Lax" | "None";
15
+ };
16
+ /**
17
+ * Tạo session ID ngẫu nhiên
18
+ * Format: {timestamp}_{random}
19
+ */
20
+ declare const generateSessionId: () => string;
21
+ /**
22
+ * Lấy giá trị cookie theo tên
23
+ */
24
+ declare const getCookie: (name: string) => string | null;
25
+ /**
26
+ * Set cookie với các options
27
+ */
28
+ declare const setCookie: (name: string, value: string, options?: SessionCookieConfig) => void;
29
+ /**
30
+ * Lấy hoặc tạo mới session_id từ cookie
31
+ * Nếu cookie tồn tại và chưa hết hạn -> trả về giá trị cũ
32
+ * Nếu không -> tạo mới và lưu vào cookie
33
+ */
34
+ declare const getOrCreateSessionId: (options?: SessionCookieConfig) => string;
35
+ /**
36
+ * Gia hạn cookie session_id
37
+ * Gọi hàm này khi có event session_start để extend thời gian hết hạn
38
+ */
39
+ declare const renewSessionCookie: (sessionId: string, options?: SessionCookieConfig) => void;
40
+ /**
41
+ * Xóa session cookie
42
+ */
43
+ declare const clearSessionCookie: (options?: SessionCookieConfig) => void;
44
+
1
45
  /**
2
46
  * Session Lifecycle Tracking
3
47
  * - session_start: Khi user mở app
4
48
  * - session_progress: Định kỳ khi user đang active (foreground)
5
49
  * - session_end: Khi user thoát app
6
50
  */
51
+
7
52
  type SessionConfig = {
8
53
  /** Khoảng thời gian gửi session_progress (ms). Default: 5 phút */
9
54
  progressInterval?: number;
@@ -13,6 +58,8 @@ type SessionConfig = {
13
58
  debug?: boolean;
14
59
  /** User ID */
15
60
  userId?: string;
61
+ /** Session cookie configuration */
62
+ sessionCookieConfig?: SessionCookieConfig;
16
63
  };
17
64
  /**
18
65
  * Gửi event session_start
@@ -50,6 +97,7 @@ declare const getSessionDuration: () => number;
50
97
  /**
51
98
  * Type definitions for Tracking Library
52
99
  */
100
+
53
101
  type TrackingConfig = {
54
102
  baseURL: string;
55
103
  appName?: string;
@@ -57,7 +105,8 @@ type TrackingConfig = {
57
105
  packageId?: string;
58
106
  platform?: "Web" | "Ios" | "Android" | string;
59
107
  debug?: boolean;
60
- sessionId: string;
108
+ sessionCookieConfig?: SessionCookieConfig;
109
+ sessionId?: string;
61
110
  sessionSign: string;
62
111
  userId: number;
63
112
  deviceId: string;
@@ -144,4 +193,4 @@ declare const tracking: {
144
193
  sessionEnd: () => void;
145
194
  };
146
195
 
147
- export { type EventData, type NextRouterLike, type PageInfo, type PageMap, type SessionConfig, type TrackingConfig, cleanupSessionTracking, tracking as default, defaultConfig, defaultPageMap, getPageInfo, getSessionDuration, initSessionTracking, initTracking, isSessionStarted, sendEvent, sendSessionEnd, sendSessionProgress, sendSessionStart, trackEvent, trackPageView, updateSessionConfig, usePageViewTracking };
196
+ export { type EventData, type NextRouterLike, type PageInfo, type PageMap, type SessionConfig, type SessionCookieConfig, type TrackingConfig, cleanupSessionTracking, clearSessionCookie, tracking as default, defaultConfig, defaultPageMap, generateSessionId, getCookie, getOrCreateSessionId, getPageInfo, getSessionDuration, initSessionTracking, initTracking, isSessionStarted, renewSessionCookie, sendEvent, sendSessionEnd, sendSessionProgress, sendSessionStart, setCookie, trackEvent, trackPageView, updateSessionConfig, usePageViewTracking };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,54 @@
1
+ /**
2
+ * Cookie Utilities for Session Management
3
+ */
4
+ type SessionCookieConfig = {
5
+ /** Tên cookie lưu session_id. Default: '_tracking_session_id' */
6
+ cookieName?: string;
7
+ /** Thời gian hết hạn cookie (phút). Default: 30 */
8
+ expirationMinutes?: number;
9
+ /** Domain cho cookie (hỗ trợ group domain như .example.com) */
10
+ domain?: string;
11
+ /** Path cho cookie. Default: '/' */
12
+ path?: string;
13
+ /** SameSite attribute. Default: 'Lax' */
14
+ sameSite?: "Strict" | "Lax" | "None";
15
+ };
16
+ /**
17
+ * Tạo session ID ngẫu nhiên
18
+ * Format: {timestamp}_{random}
19
+ */
20
+ declare const generateSessionId: () => string;
21
+ /**
22
+ * Lấy giá trị cookie theo tên
23
+ */
24
+ declare const getCookie: (name: string) => string | null;
25
+ /**
26
+ * Set cookie với các options
27
+ */
28
+ declare const setCookie: (name: string, value: string, options?: SessionCookieConfig) => void;
29
+ /**
30
+ * Lấy hoặc tạo mới session_id từ cookie
31
+ * Nếu cookie tồn tại và chưa hết hạn -> trả về giá trị cũ
32
+ * Nếu không -> tạo mới và lưu vào cookie
33
+ */
34
+ declare const getOrCreateSessionId: (options?: SessionCookieConfig) => string;
35
+ /**
36
+ * Gia hạn cookie session_id
37
+ * Gọi hàm này khi có event session_start để extend thời gian hết hạn
38
+ */
39
+ declare const renewSessionCookie: (sessionId: string, options?: SessionCookieConfig) => void;
40
+ /**
41
+ * Xóa session cookie
42
+ */
43
+ declare const clearSessionCookie: (options?: SessionCookieConfig) => void;
44
+
1
45
  /**
2
46
  * Session Lifecycle Tracking
3
47
  * - session_start: Khi user mở app
4
48
  * - session_progress: Định kỳ khi user đang active (foreground)
5
49
  * - session_end: Khi user thoát app
6
50
  */
51
+
7
52
  type SessionConfig = {
8
53
  /** Khoảng thời gian gửi session_progress (ms). Default: 5 phút */
9
54
  progressInterval?: number;
@@ -13,6 +58,8 @@ type SessionConfig = {
13
58
  debug?: boolean;
14
59
  /** User ID */
15
60
  userId?: string;
61
+ /** Session cookie configuration */
62
+ sessionCookieConfig?: SessionCookieConfig;
16
63
  };
17
64
  /**
18
65
  * Gửi event session_start
@@ -50,6 +97,7 @@ declare const getSessionDuration: () => number;
50
97
  /**
51
98
  * Type definitions for Tracking Library
52
99
  */
100
+
53
101
  type TrackingConfig = {
54
102
  baseURL: string;
55
103
  appName?: string;
@@ -57,7 +105,8 @@ type TrackingConfig = {
57
105
  packageId?: string;
58
106
  platform?: "Web" | "Ios" | "Android" | string;
59
107
  debug?: boolean;
60
- sessionId: string;
108
+ sessionCookieConfig?: SessionCookieConfig;
109
+ sessionId?: string;
61
110
  sessionSign: string;
62
111
  userId: number;
63
112
  deviceId: string;
@@ -144,4 +193,4 @@ declare const tracking: {
144
193
  sessionEnd: () => void;
145
194
  };
146
195
 
147
- export { type EventData, type NextRouterLike, type PageInfo, type PageMap, type SessionConfig, type TrackingConfig, cleanupSessionTracking, tracking as default, defaultConfig, defaultPageMap, getPageInfo, getSessionDuration, initSessionTracking, initTracking, isSessionStarted, sendEvent, sendSessionEnd, sendSessionProgress, sendSessionStart, trackEvent, trackPageView, updateSessionConfig, usePageViewTracking };
196
+ export { type EventData, type NextRouterLike, type PageInfo, type PageMap, type SessionConfig, type SessionCookieConfig, type TrackingConfig, cleanupSessionTracking, clearSessionCookie, tracking as default, defaultConfig, defaultPageMap, generateSessionId, getCookie, getOrCreateSessionId, getPageInfo, getSessionDuration, initSessionTracking, initTracking, isSessionStarted, renewSessionCookie, sendEvent, sendSessionEnd, sendSessionProgress, sendSessionStart, setCookie, trackEvent, trackPageView, updateSessionConfig, usePageViewTracking };
package/dist/index.js CHANGED
@@ -39,12 +39,92 @@ var defaultPageMap = {
39
39
  login: { name: "Login", id: "login" }
40
40
  };
41
41
 
42
+ // src/cookie-utils.ts
43
+ var defaultCookieConfig = {
44
+ cookieName: "_tracking_session_id",
45
+ expirationMinutes: 30,
46
+ domain: "",
47
+ path: "/",
48
+ sameSite: "Lax"
49
+ };
50
+ var generateSessionId = () => {
51
+ const timestamp = Date.now();
52
+ const random = Math.random().toString(36).substring(2, 10);
53
+ return `${timestamp}_${random}`;
54
+ };
55
+ var getCookie = (name) => {
56
+ if (typeof document === "undefined") return null;
57
+ const nameEQ = name + "=";
58
+ const cookies = document.cookie.split(";");
59
+ for (let i = 0; i < cookies.length; i++) {
60
+ let cookie = cookies[i];
61
+ while (cookie.charAt(0) === " ") {
62
+ cookie = cookie.substring(1);
63
+ }
64
+ if (cookie.indexOf(nameEQ) === 0) {
65
+ return cookie.substring(nameEQ.length);
66
+ }
67
+ }
68
+ return null;
69
+ };
70
+ var setCookie = (name, value, options = {}) => {
71
+ if (typeof document === "undefined") return;
72
+ const config2 = { ...defaultCookieConfig, ...options };
73
+ const expirationMs = config2.expirationMinutes * 60 * 1e3;
74
+ const expires = new Date(Date.now() + expirationMs);
75
+ let cookieString = `${name}=${value}; expires=${expires.toUTCString()}; path=${config2.path}`;
76
+ if (config2.domain) {
77
+ cookieString += `; domain=${config2.domain}`;
78
+ }
79
+ if (config2.sameSite) {
80
+ cookieString += `; SameSite=${config2.sameSite}`;
81
+ }
82
+ if (config2.sameSite === "None") {
83
+ cookieString += "; Secure";
84
+ }
85
+ document.cookie = cookieString;
86
+ };
87
+ var getOrCreateSessionId = (options = {}) => {
88
+ const config2 = { ...defaultCookieConfig, ...options };
89
+ const existingSessionId = getCookie(config2.cookieName);
90
+ if (existingSessionId) {
91
+ return existingSessionId;
92
+ }
93
+ const newSessionId = generateSessionId();
94
+ setCookie(config2.cookieName, newSessionId, options);
95
+ return newSessionId;
96
+ };
97
+ var renewSessionCookie = (sessionId, options = {}) => {
98
+ const config2 = { ...defaultCookieConfig, ...options };
99
+ setCookie(config2.cookieName, sessionId, options);
100
+ };
101
+ var clearSessionCookie = (options = {}) => {
102
+ if (typeof document === "undefined") return;
103
+ const config2 = { ...defaultCookieConfig, ...options };
104
+ let cookieString = `${config2.cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${config2.path}`;
105
+ if (config2.domain) {
106
+ cookieString += `; domain=${config2.domain}`;
107
+ }
108
+ document.cookie = cookieString;
109
+ };
110
+
42
111
  // src/tracking.ts
43
112
  var config = { ...defaultConfig };
44
113
  var previousPageId = null;
45
114
  var pageMap = { ...defaultPageMap };
46
115
  var initTracking = (options = {}) => {
47
116
  config = { ...config, ...options };
117
+ if (!config.sessionId) {
118
+ config.sessionId = getOrCreateSessionId(config.sessionCookieConfig);
119
+ if (config.debug) {
120
+ console.log(
121
+ "[Tracking] Auto-generated session_id from cookie:",
122
+ config.sessionId
123
+ );
124
+ }
125
+ } else if (config.debug) {
126
+ console.log("[Tracking] Using user-provided session_id:", config.sessionId);
127
+ }
48
128
  if (options.pageMap) {
49
129
  pageMap = { ...pageMap, ...options.pageMap };
50
130
  }
@@ -179,6 +259,7 @@ var sendSessionStart = () => {
179
259
  event_time: sessionStartTime
180
260
  });
181
261
  log("Session started at:", new Date(sessionStartTime).toISOString());
262
+ renewSessionIdCookie();
182
263
  startProgressTimer();
183
264
  };
184
265
  var sendSessionProgress = () => {
@@ -281,6 +362,16 @@ var getSessionDuration = () => {
281
362
  if (!isSessionActive) return 0;
282
363
  return Date.now() - sessionStartTime;
283
364
  };
365
+ var renewSessionIdCookie = () => {
366
+ if (sessionConfig.sessionCookieConfig) {
367
+ const cookieName = sessionConfig.sessionCookieConfig.cookieName || "_tracking_session_id";
368
+ const sessionId = document.cookie.split("; ").find((row) => row.startsWith(cookieName + "="))?.split("=")[1];
369
+ if (sessionId) {
370
+ renewSessionCookie(sessionId, sessionConfig.sessionCookieConfig);
371
+ log("Session cookie renewed for:", sessionId);
372
+ }
373
+ }
374
+ };
284
375
 
285
376
  // src/index.ts
286
377
  var tracking = {
@@ -300,18 +391,24 @@ var tracking = {
300
391
  var index_default = tracking;
301
392
  export {
302
393
  cleanupSessionTracking,
394
+ clearSessionCookie,
303
395
  index_default as default,
304
396
  defaultConfig,
305
397
  defaultPageMap,
398
+ generateSessionId,
399
+ getCookie,
400
+ getOrCreateSessionId,
306
401
  getPageInfo,
307
402
  getSessionDuration,
308
403
  initSessionTracking,
309
404
  initTracking,
310
405
  isSessionStarted,
406
+ renewSessionCookie,
311
407
  sendEvent,
312
408
  sendSessionEnd,
313
409
  sendSessionProgress,
314
410
  sendSessionStart,
411
+ setCookie,
315
412
  trackEvent,
316
413
  trackPageView,
317
414
  updateSessionConfig,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/tracking.ts","../src/session.ts","../src/index.ts"],"sourcesContent":["/**\r\n * Constants for Tracking Library\r\n */\r\n\r\nimport type { TrackingConfig, PageMap } from \"./types\";\r\n\r\nexport const defaultConfig: TrackingConfig = {\r\n baseURL: \"\",\r\n appName: \"on_plus\",\r\n appId: \"\",\r\n packageId: \"com.vtvcab.onsportstv\",\r\n platform: \"Web\",\r\n debug: false,\r\n\r\n // Required properties\r\n sessionId: \"\",\r\n sessionSign: \"\",\r\n userId: -1,\r\n deviceId: \"\",\r\n deviceModel: \"\",\r\n deviceBrand: \"\",\r\n ipAddress: \"\",\r\n\r\n // Optional properties\r\n adId: \"\",\r\n osVersion: \"\",\r\n netType: \"\",\r\n carrierNet: \"\",\r\n sdkVersion: \"\",\r\n appVersion: \"\",\r\n buildNumber: \"\",\r\n language: \"\",\r\n country: \"\",\r\n\r\n events: [],\r\n};\r\n\r\nexport const defaultPageMap: PageMap = {\r\n \"\": { name: \"Home\", id: \"home\" },\r\n home: { name: \"Home\", id: \"home\" },\r\n search: { name: \"Search\", id: \"search\" },\r\n detail: { name: \"Detail\", id: \"detail\" },\r\n player: { name: \"Player\", id: \"player\" },\r\n video: { name: \"Video\", id: \"video\" },\r\n live: { name: \"Live\", id: \"live\" },\r\n category: { name: \"Category\", id: \"category\" },\r\n profile: { name: \"Profile\", id: \"profile\" },\r\n login: { name: \"Login\", id: \"login\" },\r\n};\r\n","/**\r\n * Core Tracking Functions\r\n */\r\n\r\nimport type {\r\n TrackingConfig,\r\n PageMap,\r\n PageInfo,\r\n EventData,\r\n NextRouterLike,\r\n} from \"./types\";\r\nimport { defaultConfig, defaultPageMap } from \"./constants\";\r\n\r\n// Config runtime\r\nlet config: TrackingConfig = { ...defaultConfig };\r\n\r\n// State\r\nlet previousPageId: string | null = null;\r\n\r\n// Page mapping\r\nlet pageMap: any = { ...defaultPageMap };\r\n\r\n/**\r\n * Khởi tạo tracking với config\r\n */\r\nexport const initTracking = (\r\n options: Partial<TrackingConfig> & { pageMap?: Partial<PageMap> } = {},\r\n) => {\r\n config = { ...config, ...options };\r\n\r\n if (options.pageMap) {\r\n pageMap = { ...pageMap, ...options.pageMap };\r\n }\r\n\r\n if (!config.baseURL) {\r\n console.warn(\"[Tracking] baseURL is required.\");\r\n }\r\n\r\n if (config.debug) {\r\n console.log(\"[Tracking] Initialized with config:\", config);\r\n }\r\n};\r\n\r\n/**\r\n * Lấy thông tin page từ URL\r\n */\r\nexport const getPageInfo = (url: string): PageInfo => {\r\n const path = (url || \"\").split(\"?\")[0];\r\n const pathSegment = path.split(\"/\").filter(Boolean)[0] || \"home\";\r\n\r\n return (\r\n pageMap[pathSegment] || {\r\n name: pathSegment.charAt(0).toUpperCase() + pathSegment.slice(1),\r\n id: pathSegment,\r\n }\r\n );\r\n};\r\n\r\n/**\r\n * Send Event -> POST JSON\r\n */\r\nexport const sendEvent = async (\r\n eventData: EventData,\r\n): Promise<Response | void> => {\r\n if (!config.baseURL) {\r\n console.warn(\"[Tracking] Not initialized. Call initTracking() first.\");\r\n return;\r\n }\r\n\r\n try {\r\n // Tách event_name ra khỏi eventData để tạo event object\r\n const { event_name, ...eventParams } = eventData;\r\n\r\n // Tạo event object với format chuẩn\r\n const eventObject = {\r\n event_name: event_name || \"unknown\",\r\n event_params: JSON.stringify(eventParams),\r\n event_time: Math.floor(Date.now() / 1000),\r\n user_id: config.userId,\r\n };\r\n\r\n const payload = {\r\n // Required properties\r\n app_name: config.appName,\r\n app_id: config.appId,\r\n package_id: config.packageId,\r\n platform: config.platform,\r\n\r\n session_id: config.sessionId,\r\n session_sign: config.sessionSign,\r\n user_id: config.userId,\r\n\r\n device_id: config.deviceId,\r\n device_model: config.deviceModel,\r\n device_brand: config.deviceBrand,\r\n ip_address: config.ipAddress,\r\n\r\n // Optional properties\r\n ad_id: config.adId,\r\n os_version: config.osVersion,\r\n net_type: config.netType,\r\n carrier_net: config.carrierNet,\r\n sdk_version: config.sdkVersion,\r\n app_version: config.appVersion,\r\n build_number: config.buildNumber,\r\n language: config.language,\r\n country: config.country,\r\n\r\n // Events batch - tự động thêm event vào mảng\r\n events: [...(config.events ?? []), eventObject],\r\n };\r\n\r\n if (config.debug) {\r\n console.log(\"[Tracking] Sending event:\", payload);\r\n }\r\n\r\n const res = await fetch(config.baseURL, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify(payload),\r\n });\r\n\r\n if (!res.ok && config.debug) {\r\n const text = await res.text().catch(() => \"\");\r\n console.error(\"[Tracking] HTTP Error:\", res.status, text);\r\n }\r\n\r\n return res;\r\n } catch (error) {\r\n if (config.debug) {\r\n console.error(\"[Tracking] Error:\", error);\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Track page view event\r\n */\r\nexport const trackPageView = (url: string) => {\r\n const pageInfo = getPageInfo(url);\r\n\r\n sendEvent({\r\n event_name: \"page_view\",\r\n page_name: pageInfo.name,\r\n page_id: pageInfo.id,\r\n page_path: url,\r\n referrer_page_id: previousPageId || \"\",\r\n });\r\n\r\n previousPageId = pageInfo.id;\r\n};\r\n\r\n/**\r\n * Track custom event\r\n */\r\nexport const trackEvent = (eventName: string, params: EventData = {}) => {\r\n sendEvent({\r\n event_name: eventName,\r\n ...params,\r\n });\r\n};\r\n\r\n/**\r\n * Hook cho Next.js - auto track page views\r\n * Dùng cho Next.js Pages Router (next/router)\r\n */\r\nexport const usePageViewTracking = (router: NextRouterLike) => {\r\n if (typeof window === \"undefined\") return;\r\n if (!router) return;\r\n\r\n // Track initial page\r\n trackPageView(router.asPath);\r\n\r\n const handleRouteChange = (url: string) => {\r\n trackPageView(url);\r\n };\r\n\r\n router.events.on(\"routeChangeComplete\", handleRouteChange);\r\n\r\n return () => {\r\n router.events.off(\"routeChangeComplete\", handleRouteChange);\r\n };\r\n};\r\n","/**\r\n * Session Lifecycle Tracking\r\n * - session_start: Khi user mở app\r\n * - session_progress: Định kỳ khi user đang active (foreground)\r\n * - session_end: Khi user thoát app\r\n */\r\n\r\nimport { sendEvent } from \"./tracking\";\r\n\r\nexport type SessionConfig = {\r\n /** Khoảng thời gian gửi session_progress (ms). Default: 5 phút */\r\n progressInterval?: number;\r\n /** Có tự động start session khi init không. Default: true */\r\n autoStart?: boolean;\r\n /** Debug mode */\r\n debug?: boolean;\r\n /** User ID */\r\n userId?: string;\r\n};\r\n\r\nconst defaultSessionConfig: SessionConfig = {\r\n progressInterval: 5 * 60 * 1000, // 5 phút\r\n autoStart: true,\r\n debug: false,\r\n};\r\n\r\n// State\r\nlet sessionConfig: SessionConfig = { ...defaultSessionConfig };\r\nlet progressTimer: ReturnType<typeof setInterval> | null = null;\r\nlet isSessionActive = false;\r\nlet sessionStartTime: number = 0;\r\n\r\n/**\r\n * Log helper\r\n */\r\nconst log = (...args: any[]) => {\r\n if (sessionConfig.debug) {\r\n console.log(\"[Session]\", ...args);\r\n }\r\n};\r\n\r\n/**\r\n * Gửi event session_start\r\n */\r\nexport const sendSessionStart = () => {\r\n if (isSessionActive) {\r\n log(\"Session already started, skipping...\");\r\n return;\r\n }\r\n\r\n sessionStartTime = Date.now();\r\n isSessionActive = true;\r\n\r\n sendEvent({\r\n event_name: \"session_start\",\r\n event_time: sessionStartTime,\r\n });\r\n\r\n log(\"Session started at:\", new Date(sessionStartTime).toISOString());\r\n\r\n // Bắt đầu timer cho session_progress\r\n startProgressTimer();\r\n};\r\n\r\n/**\r\n * Gửi event session_progress\r\n */\r\nexport const sendSessionProgress = () => {\r\n if (!isSessionActive) return;\r\n\r\n const now = Math.floor(Date.now() / 1000);\r\n const duration = now - sessionStartTime;\r\n\r\n sendEvent({\r\n event_name: \"session_progress\",\r\n session_duration: duration,\r\n });\r\n\r\n log(\"Session progress - Duration:\", Math.round(duration / 1000), \"seconds\");\r\n};\r\n\r\n/**\r\n * Gửi event session_end\r\n */\r\nexport const sendSessionEnd = () => {\r\n if (!isSessionActive) return;\r\n\r\n const now = Math.floor(Date.now() / 1000);\r\n const duration = now - sessionStartTime;\r\n\r\n // Dùng sendBeacon để đảm bảo gửi được khi page unload\r\n // Fallback về sendEvent nếu không support\r\n sendEvent({\r\n event_name: \"session_end\",\r\n session_duration: duration,\r\n });\r\n\r\n log(\r\n \"Session ended - Total duration:\",\r\n Math.round(duration / 1000),\r\n \"seconds\",\r\n );\r\n\r\n stopProgressTimer();\r\n isSessionActive = false;\r\n};\r\n\r\n/**\r\n * Start progress timer\r\n */\r\nconst startProgressTimer = () => {\r\n stopProgressTimer(); // Clear existing timer\r\n\r\n if (sessionConfig.progressInterval && sessionConfig.progressInterval > 0) {\r\n progressTimer = setInterval(() => {\r\n if (document.visibilityState === \"visible\") {\r\n sendSessionProgress();\r\n }\r\n }, sessionConfig.progressInterval);\r\n\r\n log(\r\n \"Progress timer started, interval:\",\r\n sessionConfig.progressInterval,\r\n \"ms\",\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Stop progress timer\r\n */\r\nconst stopProgressTimer = () => {\r\n if (progressTimer) {\r\n clearInterval(progressTimer);\r\n progressTimer = null;\r\n log(\"Progress timer stopped\");\r\n }\r\n};\r\n\r\n/**\r\n * Handle visibility change\r\n * Pause/resume tracking khi user switch tab hoặc minimize\r\n */\r\nconst handleVisibilityChange = () => {\r\n if (document.visibilityState === \"hidden\") {\r\n // User rời khỏi tab - có thể gửi session_progress cuối\r\n log(\"Tab hidden - pausing progress\");\r\n stopProgressTimer();\r\n } else if (document.visibilityState === \"visible\") {\r\n // User quay lại tab - resume timer\r\n log(\"Tab visible - resuming progress\");\r\n if (isSessionActive) {\r\n startProgressTimer();\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Handle page unload (user đóng tab/browser)\r\n */\r\nconst handleBeforeUnload = () => {\r\n sendSessionEnd();\r\n};\r\n\r\n/**\r\n * Handle page hide (mobile: switch app, close tab)\r\n * Dùng pagehide thay vì beforeunload cho mobile\r\n */\r\nconst handlePageHide = (event: PageTransitionEvent) => {\r\n if (event.persisted) {\r\n // Page được cache (bfcache) - không phải thực sự đóng\r\n return;\r\n }\r\n sendSessionEnd();\r\n};\r\n\r\n/**\r\n * Khởi tạo session tracking\r\n */\r\nexport const initSessionTracking = (options: SessionConfig = {}) => {\r\n if (typeof window === \"undefined\") return;\r\n\r\n sessionConfig = { ...defaultSessionConfig, ...options };\r\n\r\n log(\"Initializing with config:\", sessionConfig);\r\n\r\n // Đăng ký event listeners\r\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\r\n window.addEventListener(\"beforeunload\", handleBeforeUnload);\r\n window.addEventListener(\"pagehide\", handlePageHide);\r\n\r\n // Auto start session\r\n if (sessionConfig.autoStart) {\r\n sendSessionStart();\r\n }\r\n};\r\n\r\n/**\r\n * Cleanup - gọi khi unmount component\r\n */\r\nexport const cleanupSessionTracking = () => {\r\n if (typeof window === \"undefined\") return;\r\n\r\n log(\"Cleaning up...\");\r\n\r\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\r\n window.removeEventListener(\"beforeunload\", handleBeforeUnload);\r\n window.removeEventListener(\"pagehide\", handlePageHide);\r\n\r\n stopProgressTimer();\r\n isSessionActive = false;\r\n};\r\n\r\n/**\r\n * Cập nhật config (ví dụ: thay đổi interval)\r\n */\r\nexport const updateSessionConfig = (options: Partial<SessionConfig>) => {\r\n sessionConfig = { ...sessionConfig, ...options };\r\n\r\n // Restart timer nếu interval thay đổi\r\n if (options.progressInterval !== undefined && isSessionActive) {\r\n startProgressTimer();\r\n }\r\n\r\n log(\"Config updated:\", sessionConfig);\r\n};\r\n\r\n/**\r\n * Kiểm tra session đang active không\r\n */\r\nexport const isSessionStarted = () => isSessionActive;\r\n\r\n/**\r\n * Lấy thời gian session hiện tại (ms)\r\n */\r\nexport const getSessionDuration = () => {\r\n if (!isSessionActive) return 0;\r\n return Date.now() - sessionStartTime;\r\n};\r\n","/**\r\n * Tracking Library v2 (Fetch + TypeScript)\r\n * Thư viện tracking events có thể dùng cho nhiều project\r\n */\r\n\r\n// Re-export types\r\nexport type {\r\n TrackingConfig,\r\n PageInfo,\r\n PageMap,\r\n EventData,\r\n NextRouterLike,\r\n} from \"./types\";\r\n\r\nexport type { SessionConfig } from \"./session\";\r\n\r\n// Re-export constants\r\nexport { defaultConfig, defaultPageMap } from \"./constants\";\r\n\r\n// Re-export functions\r\nexport {\r\n initTracking,\r\n getPageInfo,\r\n sendEvent,\r\n trackPageView,\r\n trackEvent,\r\n usePageViewTracking,\r\n} from \"./tracking\";\r\n\r\n// Re-export session functions\r\nexport {\r\n initSessionTracking,\r\n cleanupSessionTracking,\r\n sendSessionStart,\r\n sendSessionProgress,\r\n sendSessionEnd,\r\n updateSessionConfig,\r\n isSessionStarted,\r\n getSessionDuration,\r\n} from \"./session\";\r\n\r\n// Import for default export\r\nimport {\r\n initTracking,\r\n getPageInfo,\r\n sendEvent,\r\n trackPageView,\r\n trackEvent,\r\n usePageViewTracking,\r\n} from \"./tracking\";\r\n\r\nimport {\r\n initSessionTracking,\r\n cleanupSessionTracking,\r\n sendSessionStart,\r\n sendSessionProgress,\r\n sendSessionEnd,\r\n} from \"./session\";\r\n\r\n// Export default object\r\nconst tracking = {\r\n init: initTracking,\r\n trackPageView,\r\n trackEvent,\r\n sendEvent,\r\n getPageInfo,\r\n usePageViewTracking,\r\n // Session lifecycle\r\n initSession: initSessionTracking,\r\n cleanupSession: cleanupSessionTracking,\r\n sessionStart: sendSessionStart,\r\n sessionProgress: sendSessionProgress,\r\n sessionEnd: sendSessionEnd,\r\n};\r\n\r\nexport default tracking;\r\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/cookie-utils.ts","../src/tracking.ts","../src/session.ts","../src/index.ts"],"sourcesContent":["/**\r\n * Constants for Tracking Library\r\n */\r\n\r\nimport type { TrackingConfig, PageMap } from \"./types\";\r\n\r\nexport const defaultConfig: TrackingConfig = {\r\n baseURL: \"\",\r\n appName: \"on_plus\",\r\n appId: \"\",\r\n packageId: \"com.vtvcab.onsportstv\",\r\n platform: \"Web\",\r\n debug: false,\r\n\r\n // Required properties\r\n sessionId: \"\",\r\n sessionSign: \"\",\r\n userId: -1,\r\n deviceId: \"\",\r\n deviceModel: \"\",\r\n deviceBrand: \"\",\r\n ipAddress: \"\",\r\n\r\n // Optional properties\r\n adId: \"\",\r\n osVersion: \"\",\r\n netType: \"\",\r\n carrierNet: \"\",\r\n sdkVersion: \"\",\r\n appVersion: \"\",\r\n buildNumber: \"\",\r\n language: \"\",\r\n country: \"\",\r\n\r\n events: [],\r\n};\r\n\r\nexport const defaultPageMap: PageMap = {\r\n \"\": { name: \"Home\", id: \"home\" },\r\n home: { name: \"Home\", id: \"home\" },\r\n search: { name: \"Search\", id: \"search\" },\r\n detail: { name: \"Detail\", id: \"detail\" },\r\n player: { name: \"Player\", id: \"player\" },\r\n video: { name: \"Video\", id: \"video\" },\r\n live: { name: \"Live\", id: \"live\" },\r\n category: { name: \"Category\", id: \"category\" },\r\n profile: { name: \"Profile\", id: \"profile\" },\r\n login: { name: \"Login\", id: \"login\" },\r\n};\r\n","/**\r\n * Cookie Utilities for Session Management\r\n */\r\n\r\nexport type SessionCookieConfig = {\r\n /** Tên cookie lưu session_id. Default: '_tracking_session_id' */\r\n cookieName?: string;\r\n /** Thời gian hết hạn cookie (phút). Default: 30 */\r\n expirationMinutes?: number;\r\n /** Domain cho cookie (hỗ trợ group domain như .example.com) */\r\n domain?: string;\r\n /** Path cho cookie. Default: '/' */\r\n path?: string;\r\n /** SameSite attribute. Default: 'Lax' */\r\n sameSite?: \"Strict\" | \"Lax\" | \"None\";\r\n};\r\n\r\nconst defaultCookieConfig: Required<SessionCookieConfig> = {\r\n cookieName: \"_tracking_session_id\",\r\n expirationMinutes: 30,\r\n domain: \"\",\r\n path: \"/\",\r\n sameSite: \"Lax\",\r\n};\r\n\r\n/**\r\n * Tạo session ID ngẫu nhiên\r\n * Format: {timestamp}_{random}\r\n */\r\nexport const generateSessionId = (): string => {\r\n const timestamp = Date.now();\r\n const random = Math.random().toString(36).substring(2, 10);\r\n return `${timestamp}_${random}`;\r\n};\r\n\r\n/**\r\n * Lấy giá trị cookie theo tên\r\n */\r\nexport const getCookie = (name: string): string | null => {\r\n if (typeof document === \"undefined\") return null;\r\n\r\n const nameEQ = name + \"=\";\r\n const cookies = document.cookie.split(\";\");\r\n\r\n for (let i = 0; i < cookies.length; i++) {\r\n let cookie = cookies[i];\r\n while (cookie.charAt(0) === \" \") {\r\n cookie = cookie.substring(1);\r\n }\r\n if (cookie.indexOf(nameEQ) === 0) {\r\n return cookie.substring(nameEQ.length);\r\n }\r\n }\r\n return null;\r\n};\r\n\r\n/**\r\n * Set cookie với các options\r\n */\r\nexport const setCookie = (\r\n name: string,\r\n value: string,\r\n options: SessionCookieConfig = {},\r\n): void => {\r\n if (typeof document === \"undefined\") return;\r\n\r\n const config = { ...defaultCookieConfig, ...options };\r\n const expirationMs = config.expirationMinutes * 60 * 1000;\r\n const expires = new Date(Date.now() + expirationMs);\r\n\r\n let cookieString = `${name}=${value}; expires=${expires.toUTCString()}; path=${config.path}`;\r\n\r\n if (config.domain) {\r\n cookieString += `; domain=${config.domain}`;\r\n }\r\n\r\n if (config.sameSite) {\r\n cookieString += `; SameSite=${config.sameSite}`;\r\n }\r\n\r\n // Nếu SameSite=None thì phải có Secure\r\n if (config.sameSite === \"None\") {\r\n cookieString += \"; Secure\";\r\n }\r\n\r\n document.cookie = cookieString;\r\n};\r\n\r\n/**\r\n * Lấy hoặc tạo mới session_id từ cookie\r\n * Nếu cookie tồn tại và chưa hết hạn -> trả về giá trị cũ\r\n * Nếu không -> tạo mới và lưu vào cookie\r\n */\r\nexport const getOrCreateSessionId = (\r\n options: SessionCookieConfig = {},\r\n): string => {\r\n const config = { ...defaultCookieConfig, ...options };\r\n const existingSessionId = getCookie(config.cookieName);\r\n\r\n if (existingSessionId) {\r\n return existingSessionId;\r\n }\r\n\r\n // Tạo mới session_id\r\n const newSessionId = generateSessionId();\r\n setCookie(config.cookieName, newSessionId, options);\r\n\r\n return newSessionId;\r\n};\r\n\r\n/**\r\n * Gia hạn cookie session_id\r\n * Gọi hàm này khi có event session_start để extend thời gian hết hạn\r\n */\r\nexport const renewSessionCookie = (\r\n sessionId: string,\r\n options: SessionCookieConfig = {},\r\n): void => {\r\n const config = { ...defaultCookieConfig, ...options };\r\n setCookie(config.cookieName, sessionId, options);\r\n};\r\n\r\n/**\r\n * Xóa session cookie\r\n */\r\nexport const clearSessionCookie = (options: SessionCookieConfig = {}): void => {\r\n if (typeof document === \"undefined\") return;\r\n\r\n const config = { ...defaultCookieConfig, ...options };\r\n let cookieString = `${config.cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${config.path}`;\r\n\r\n if (config.domain) {\r\n cookieString += `; domain=${config.domain}`;\r\n }\r\n\r\n document.cookie = cookieString;\r\n};\r\n","/**\r\n * Core Tracking Functions\r\n */\r\n\r\nimport type {\r\n TrackingConfig,\r\n PageMap,\r\n PageInfo,\r\n EventData,\r\n NextRouterLike,\r\n} from \"./types\";\r\nimport { defaultConfig, defaultPageMap } from \"./constants\";\r\nimport { getOrCreateSessionId } from \"./cookie-utils\";\r\n\r\n// Config runtime\r\nlet config: TrackingConfig = { ...defaultConfig };\r\n\r\n// State\r\nlet previousPageId: string | null = null;\r\n\r\n// Page mapping\r\nlet pageMap: any = { ...defaultPageMap };\r\n\r\n/**\r\n * Khởi tạo tracking với config\r\n */\r\nexport const initTracking = (\r\n options: Partial<TrackingConfig> & { pageMap?: Partial<PageMap> } = {},\r\n) => {\r\n config = { ...config, ...options };\r\n\r\n // Auto-generate sessionId from cookie if not provided\r\n if (!config.sessionId) {\r\n config.sessionId = getOrCreateSessionId(config.sessionCookieConfig);\r\n if (config.debug) {\r\n console.log(\r\n \"[Tracking] Auto-generated session_id from cookie:\",\r\n config.sessionId,\r\n );\r\n }\r\n } else if (config.debug) {\r\n console.log(\"[Tracking] Using user-provided session_id:\", config.sessionId);\r\n }\r\n\r\n if (options.pageMap) {\r\n pageMap = { ...pageMap, ...options.pageMap };\r\n }\r\n\r\n if (!config.baseURL) {\r\n console.warn(\"[Tracking] baseURL is required.\");\r\n }\r\n\r\n if (config.debug) {\r\n console.log(\"[Tracking] Initialized with config:\", config);\r\n }\r\n};\r\n\r\n/**\r\n * Lấy thông tin page từ URL\r\n */\r\nexport const getPageInfo = (url: string): PageInfo => {\r\n const path = (url || \"\").split(\"?\")[0];\r\n const pathSegment = path.split(\"/\").filter(Boolean)[0] || \"home\";\r\n\r\n return (\r\n pageMap[pathSegment] || {\r\n name: pathSegment.charAt(0).toUpperCase() + pathSegment.slice(1),\r\n id: pathSegment,\r\n }\r\n );\r\n};\r\n\r\n/**\r\n * Send Event -> POST JSON\r\n */\r\nexport const sendEvent = async (\r\n eventData: EventData,\r\n): Promise<Response | void> => {\r\n if (!config.baseURL) {\r\n console.warn(\"[Tracking] Not initialized. Call initTracking() first.\");\r\n return;\r\n }\r\n\r\n try {\r\n // Tách event_name ra khỏi eventData để tạo event object\r\n const { event_name, ...eventParams } = eventData;\r\n\r\n // Tạo event object với format chuẩn\r\n const eventObject = {\r\n event_name: event_name || \"unknown\",\r\n event_params: JSON.stringify(eventParams),\r\n event_time: Math.floor(Date.now() / 1000),\r\n user_id: config.userId,\r\n };\r\n\r\n const payload = {\r\n // Required properties\r\n app_name: config.appName,\r\n app_id: config.appId,\r\n package_id: config.packageId,\r\n platform: config.platform,\r\n\r\n session_id: config.sessionId,\r\n session_sign: config.sessionSign,\r\n user_id: config.userId,\r\n\r\n device_id: config.deviceId,\r\n device_model: config.deviceModel,\r\n device_brand: config.deviceBrand,\r\n ip_address: config.ipAddress,\r\n\r\n // Optional properties\r\n ad_id: config.adId,\r\n os_version: config.osVersion,\r\n net_type: config.netType,\r\n carrier_net: config.carrierNet,\r\n sdk_version: config.sdkVersion,\r\n app_version: config.appVersion,\r\n build_number: config.buildNumber,\r\n language: config.language,\r\n country: config.country,\r\n\r\n // Events batch - tự động thêm event vào mảng\r\n events: [...(config.events ?? []), eventObject],\r\n };\r\n\r\n if (config.debug) {\r\n console.log(\"[Tracking] Sending event:\", payload);\r\n }\r\n\r\n const res = await fetch(config.baseURL, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify(payload),\r\n });\r\n\r\n if (!res.ok && config.debug) {\r\n const text = await res.text().catch(() => \"\");\r\n console.error(\"[Tracking] HTTP Error:\", res.status, text);\r\n }\r\n\r\n return res;\r\n } catch (error) {\r\n if (config.debug) {\r\n console.error(\"[Tracking] Error:\", error);\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Track page view event\r\n */\r\nexport const trackPageView = (url: string) => {\r\n const pageInfo = getPageInfo(url);\r\n\r\n sendEvent({\r\n event_name: \"page_view\",\r\n page_name: pageInfo.name,\r\n page_id: pageInfo.id,\r\n page_path: url,\r\n referrer_page_id: previousPageId || \"\",\r\n });\r\n\r\n previousPageId = pageInfo.id;\r\n};\r\n\r\n/**\r\n * Track custom event\r\n */\r\nexport const trackEvent = (eventName: string, params: EventData = {}) => {\r\n sendEvent({\r\n event_name: eventName,\r\n ...params,\r\n });\r\n};\r\n\r\n/**\r\n * Hook cho Next.js - auto track page views\r\n * Dùng cho Next.js Pages Router (next/router)\r\n */\r\nexport const usePageViewTracking = (router: NextRouterLike) => {\r\n if (typeof window === \"undefined\") return;\r\n if (!router) return;\r\n\r\n // Track initial page\r\n trackPageView(router.asPath);\r\n\r\n const handleRouteChange = (url: string) => {\r\n trackPageView(url);\r\n };\r\n\r\n router.events.on(\"routeChangeComplete\", handleRouteChange);\r\n\r\n return () => {\r\n router.events.off(\"routeChangeComplete\", handleRouteChange);\r\n };\r\n};\r\n","/**\r\n * Session Lifecycle Tracking\r\n * - session_start: Khi user mở app\r\n * - session_progress: Định kỳ khi user đang active (foreground)\r\n * - session_end: Khi user thoát app\r\n */\r\n\r\nimport { sendEvent } from \"./tracking\";\r\nimport { renewSessionCookie } from \"./cookie-utils\";\r\nimport type { SessionCookieConfig } from \"./cookie-utils\";\r\n\r\nexport type SessionConfig = {\r\n /** Khoảng thời gian gửi session_progress (ms). Default: 5 phút */\r\n progressInterval?: number;\r\n /** Có tự động start session khi init không. Default: true */\r\n autoStart?: boolean;\r\n /** Debug mode */\r\n debug?: boolean;\r\n /** User ID */\r\n userId?: string;\r\n /** Session cookie configuration */\r\n sessionCookieConfig?: SessionCookieConfig;\r\n};\r\n\r\nconst defaultSessionConfig: SessionConfig = {\r\n progressInterval: 5 * 60 * 1000, // 5 phút\r\n autoStart: true,\r\n debug: false,\r\n};\r\n\r\n// State\r\nlet sessionConfig: SessionConfig = { ...defaultSessionConfig };\r\nlet progressTimer: ReturnType<typeof setInterval> | null = null;\r\nlet isSessionActive = false;\r\nlet sessionStartTime: number = 0;\r\n\r\n/**\r\n * Log helper\r\n */\r\nconst log = (...args: any[]) => {\r\n if (sessionConfig.debug) {\r\n console.log(\"[Session]\", ...args);\r\n }\r\n};\r\n\r\n/**\r\n * Gửi event session_start\r\n */\r\nexport const sendSessionStart = () => {\r\n if (isSessionActive) {\r\n log(\"Session already started, skipping...\");\r\n return;\r\n }\r\n\r\n sessionStartTime = Date.now();\r\n isSessionActive = true;\r\n\r\n sendEvent({\r\n event_name: \"session_start\",\r\n event_time: sessionStartTime,\r\n });\r\n\r\n log(\"Session started at:\", new Date(sessionStartTime).toISOString());\r\n\r\n // Gia hạn cookie session_id (extend thêm 30 phút)\r\n renewSessionIdCookie();\r\n\r\n // Bắt đầu timer cho session_progress\r\n startProgressTimer();\r\n};\r\n\r\n/**\r\n * Gửi event session_progress\r\n */\r\nexport const sendSessionProgress = () => {\r\n if (!isSessionActive) return;\r\n\r\n const now = Math.floor(Date.now() / 1000);\r\n const duration = now - sessionStartTime;\r\n\r\n sendEvent({\r\n event_name: \"session_progress\",\r\n session_duration: duration,\r\n });\r\n\r\n log(\"Session progress - Duration:\", Math.round(duration / 1000), \"seconds\");\r\n};\r\n\r\n/**\r\n * Gửi event session_end\r\n */\r\nexport const sendSessionEnd = () => {\r\n if (!isSessionActive) return;\r\n\r\n const now = Math.floor(Date.now() / 1000);\r\n const duration = now - sessionStartTime;\r\n\r\n // Dùng sendBeacon để đảm bảo gửi được khi page unload\r\n // Fallback về sendEvent nếu không support\r\n sendEvent({\r\n event_name: \"session_end\",\r\n session_duration: duration,\r\n });\r\n\r\n log(\r\n \"Session ended - Total duration:\",\r\n Math.round(duration / 1000),\r\n \"seconds\",\r\n );\r\n\r\n stopProgressTimer();\r\n isSessionActive = false;\r\n};\r\n\r\n/**\r\n * Start progress timer\r\n */\r\nconst startProgressTimer = () => {\r\n stopProgressTimer(); // Clear existing timer\r\n\r\n if (sessionConfig.progressInterval && sessionConfig.progressInterval > 0) {\r\n progressTimer = setInterval(() => {\r\n if (document.visibilityState === \"visible\") {\r\n sendSessionProgress();\r\n }\r\n }, sessionConfig.progressInterval);\r\n\r\n log(\r\n \"Progress timer started, interval:\",\r\n sessionConfig.progressInterval,\r\n \"ms\",\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Stop progress timer\r\n */\r\nconst stopProgressTimer = () => {\r\n if (progressTimer) {\r\n clearInterval(progressTimer);\r\n progressTimer = null;\r\n log(\"Progress timer stopped\");\r\n }\r\n};\r\n\r\n/**\r\n * Handle visibility change\r\n * Pause/resume tracking khi user switch tab hoặc minimize\r\n */\r\nconst handleVisibilityChange = () => {\r\n if (document.visibilityState === \"hidden\") {\r\n // User rời khỏi tab - có thể gửi session_progress cuối\r\n log(\"Tab hidden - pausing progress\");\r\n stopProgressTimer();\r\n } else if (document.visibilityState === \"visible\") {\r\n // User quay lại tab - resume timer\r\n log(\"Tab visible - resuming progress\");\r\n if (isSessionActive) {\r\n startProgressTimer();\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Handle page unload (user đóng tab/browser)\r\n */\r\nconst handleBeforeUnload = () => {\r\n sendSessionEnd();\r\n};\r\n\r\n/**\r\n * Handle page hide (mobile: switch app, close tab)\r\n * Dùng pagehide thay vì beforeunload cho mobile\r\n */\r\nconst handlePageHide = (event: PageTransitionEvent) => {\r\n if (event.persisted) {\r\n // Page được cache (bfcache) - không phải thực sự đóng\r\n return;\r\n }\r\n sendSessionEnd();\r\n};\r\n\r\n/**\r\n * Khởi tạo session tracking\r\n */\r\nexport const initSessionTracking = (options: SessionConfig = {}) => {\r\n if (typeof window === \"undefined\") return;\r\n\r\n sessionConfig = { ...defaultSessionConfig, ...options };\r\n\r\n log(\"Initializing with config:\", sessionConfig);\r\n\r\n // Đăng ký event listeners\r\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\r\n window.addEventListener(\"beforeunload\", handleBeforeUnload);\r\n window.addEventListener(\"pagehide\", handlePageHide);\r\n\r\n // Auto start session\r\n if (sessionConfig.autoStart) {\r\n sendSessionStart();\r\n }\r\n};\r\n\r\n/**\r\n * Cleanup - gọi khi unmount component\r\n */\r\nexport const cleanupSessionTracking = () => {\r\n if (typeof window === \"undefined\") return;\r\n\r\n log(\"Cleaning up...\");\r\n\r\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\r\n window.removeEventListener(\"beforeunload\", handleBeforeUnload);\r\n window.removeEventListener(\"pagehide\", handlePageHide);\r\n\r\n stopProgressTimer();\r\n isSessionActive = false;\r\n};\r\n\r\n/**\r\n * Cập nhật config (ví dụ: thay đổi interval)\r\n */\r\nexport const updateSessionConfig = (options: Partial<SessionConfig>) => {\r\n sessionConfig = { ...sessionConfig, ...options };\r\n\r\n // Restart timer nếu interval thay đổi\r\n if (options.progressInterval !== undefined && isSessionActive) {\r\n startProgressTimer();\r\n }\r\n\r\n log(\"Config updated:\", sessionConfig);\r\n};\r\n\r\n/**\r\n * Kiểm tra session đang active không\r\n */\r\nexport const isSessionStarted = () => isSessionActive;\r\n\r\n/**\r\n * Lấy thời gian session hiện tại (ms)\r\n */\r\nexport const getSessionDuration = () => {\r\n if (!isSessionActive) return 0;\r\n return Date.now() - sessionStartTime;\r\n};\r\n\r\n/**\r\n * Gia hạn cookie session_id\r\n * Gọi khi có event session_start để extend thời gian hết hạn\r\n */\r\nconst renewSessionIdCookie = () => {\r\n // Lấy sessionId và config từ tracking module\r\n // Note: Cần export getter function từ tracking.ts để lấy config\r\n if (sessionConfig.sessionCookieConfig) {\r\n // Tạm thời sử dụng cookie name mặc định để lấy sessionId\r\n const cookieName =\r\n sessionConfig.sessionCookieConfig.cookieName || \"_tracking_session_id\";\r\n const sessionId = document.cookie\r\n .split(\"; \")\r\n .find((row) => row.startsWith(cookieName + \"=\"))\r\n ?.split(\"=\")[1];\r\n\r\n if (sessionId) {\r\n renewSessionCookie(sessionId, sessionConfig.sessionCookieConfig);\r\n log(\"Session cookie renewed for:\", sessionId);\r\n }\r\n }\r\n};\r\n","/**\r\n * Tracking Library v2 (Fetch + TypeScript)\r\n * Thư viện tracking events có thể dùng cho nhiều project\r\n */\r\n\r\n// Re-export types\r\nexport type {\r\n TrackingConfig,\r\n PageInfo,\r\n PageMap,\r\n EventData,\r\n NextRouterLike,\r\n} from \"./types\";\r\n\r\nexport type { SessionConfig } from \"./session\";\r\nexport type { SessionCookieConfig } from \"./cookie-utils\";\r\n\r\n// Re-export constants\r\nexport { defaultConfig, defaultPageMap } from \"./constants\";\r\n\r\n// Re-export functions\r\nexport {\r\n initTracking,\r\n getPageInfo,\r\n sendEvent,\r\n trackPageView,\r\n trackEvent,\r\n usePageViewTracking,\r\n} from \"./tracking\";\r\n\r\n// Re-export session functions\r\nexport {\r\n initSessionTracking,\r\n cleanupSessionTracking,\r\n sendSessionStart,\r\n sendSessionProgress,\r\n sendSessionEnd,\r\n updateSessionConfig,\r\n isSessionStarted,\r\n getSessionDuration,\r\n} from \"./session\";\r\n\r\n// Re-export cookie utilities\r\nexport {\r\n generateSessionId,\r\n getCookie,\r\n setCookie,\r\n getOrCreateSessionId,\r\n renewSessionCookie,\r\n clearSessionCookie,\r\n} from \"./cookie-utils\";\r\n\r\n// Import for default export\r\nimport {\r\n initTracking,\r\n getPageInfo,\r\n sendEvent,\r\n trackPageView,\r\n trackEvent,\r\n usePageViewTracking,\r\n} from \"./tracking\";\r\n\r\nimport {\r\n initSessionTracking,\r\n cleanupSessionTracking,\r\n sendSessionStart,\r\n sendSessionProgress,\r\n sendSessionEnd,\r\n} from \"./session\";\r\n\r\n// Export default object\r\nconst tracking = {\r\n init: initTracking,\r\n trackPageView,\r\n trackEvent,\r\n sendEvent,\r\n getPageInfo,\r\n usePageViewTracking,\r\n // Session lifecycle\r\n initSession: initSessionTracking,\r\n cleanupSession: cleanupSessionTracking,\r\n sessionStart: sendSessionStart,\r\n sessionProgress: sendSessionProgress,\r\n sessionEnd: sendSessionEnd,\r\n};\r\n\r\nexport default tracking;\r\n"],"mappings":"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","names":["config"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tracking-lib-ott",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",