tracking-lib-ott 1.0.8 → 1.0.10

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,91 @@ var defaultPageMap = {
81
87
  login: { name: "Login", id: "login" }
82
88
  };
83
89
 
90
+ // src/cookie-utils.ts
91
+ var import_uuid = require("uuid");
92
+ var defaultCookieConfig = {
93
+ cookieName: "_tracking_session_id",
94
+ expirationMinutes: 30,
95
+ domain: "",
96
+ path: "/",
97
+ sameSite: "Lax"
98
+ };
99
+ var generateSessionId = () => {
100
+ return (0, import_uuid.v4)();
101
+ };
102
+ var getCookie = (name) => {
103
+ if (typeof document === "undefined") return null;
104
+ const nameEQ = name + "=";
105
+ const cookies = document.cookie.split(";");
106
+ for (let i = 0; i < cookies.length; i++) {
107
+ let cookie = cookies[i];
108
+ while (cookie.charAt(0) === " ") {
109
+ cookie = cookie.substring(1);
110
+ }
111
+ if (cookie.indexOf(nameEQ) === 0) {
112
+ return cookie.substring(nameEQ.length);
113
+ }
114
+ }
115
+ return null;
116
+ };
117
+ var setCookie = (name, value, options = {}) => {
118
+ if (typeof document === "undefined") return;
119
+ const config2 = { ...defaultCookieConfig, ...options };
120
+ const expirationMs = config2.expirationMinutes * 60 * 1e3;
121
+ const expires = new Date(Date.now() + expirationMs);
122
+ let cookieString = `${name}=${value}; expires=${expires.toUTCString()}; path=${config2.path}`;
123
+ if (config2.domain) {
124
+ cookieString += `; domain=${config2.domain}`;
125
+ }
126
+ if (config2.sameSite) {
127
+ cookieString += `; SameSite=${config2.sameSite}`;
128
+ }
129
+ if (config2.sameSite === "None") {
130
+ cookieString += "; Secure";
131
+ }
132
+ document.cookie = cookieString;
133
+ };
134
+ var getOrCreateSessionId = (options = {}) => {
135
+ const config2 = { ...defaultCookieConfig, ...options };
136
+ const existingSessionId = getCookie(config2.cookieName);
137
+ if (existingSessionId) {
138
+ return existingSessionId;
139
+ }
140
+ const newSessionId = generateSessionId();
141
+ setCookie(config2.cookieName, newSessionId, options);
142
+ return newSessionId;
143
+ };
144
+ var renewSessionCookie = (sessionId, options = {}) => {
145
+ const config2 = { ...defaultCookieConfig, ...options };
146
+ setCookie(config2.cookieName, sessionId, options);
147
+ };
148
+ var clearSessionCookie = (options = {}) => {
149
+ if (typeof document === "undefined") return;
150
+ const config2 = { ...defaultCookieConfig, ...options };
151
+ let cookieString = `${config2.cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${config2.path}`;
152
+ if (config2.domain) {
153
+ cookieString += `; domain=${config2.domain}`;
154
+ }
155
+ document.cookie = cookieString;
156
+ };
157
+
84
158
  // src/tracking.ts
85
159
  var config = { ...defaultConfig };
86
160
  var previousPageId = null;
87
161
  var pageMap = { ...defaultPageMap };
88
162
  var initTracking = (options = {}) => {
89
163
  config = { ...config, ...options };
164
+ if (!config.sessionId) {
165
+ config.sessionId = getOrCreateSessionId(config.sessionCookieConfig);
166
+ if (config.debug) {
167
+ console.log(
168
+ "[Tracking] Auto-generated session_id from cookie:",
169
+ config.sessionId
170
+ );
171
+ }
172
+ } else if (config.debug) {
173
+ console.log("[Tracking] Using user-provided session_id:", config.sessionId);
174
+ }
90
175
  if (options.pageMap) {
91
176
  pageMap = { ...pageMap, ...options.pageMap };
92
177
  }
@@ -221,6 +306,7 @@ var sendSessionStart = () => {
221
306
  event_time: sessionStartTime
222
307
  });
223
308
  log("Session started at:", new Date(sessionStartTime).toISOString());
309
+ renewSessionIdCookie();
224
310
  startProgressTimer();
225
311
  };
226
312
  var sendSessionProgress = () => {
@@ -323,6 +409,16 @@ var getSessionDuration = () => {
323
409
  if (!isSessionActive) return 0;
324
410
  return Date.now() - sessionStartTime;
325
411
  };
412
+ var renewSessionIdCookie = () => {
413
+ if (sessionConfig.sessionCookieConfig) {
414
+ const cookieName = sessionConfig.sessionCookieConfig.cookieName || "_tracking_session_id";
415
+ const sessionId = document.cookie.split("; ").find((row) => row.startsWith(cookieName + "="))?.split("=")[1];
416
+ if (sessionId) {
417
+ renewSessionCookie(sessionId, sessionConfig.sessionCookieConfig);
418
+ log("Session cookie renewed for:", sessionId);
419
+ }
420
+ }
421
+ };
326
422
 
327
423
  // src/index.ts
328
424
  var tracking = {
@@ -343,17 +439,23 @@ var index_default = tracking;
343
439
  // Annotate the CommonJS export names for ESM import in node:
344
440
  0 && (module.exports = {
345
441
  cleanupSessionTracking,
442
+ clearSessionCookie,
346
443
  defaultConfig,
347
444
  defaultPageMap,
445
+ generateSessionId,
446
+ getCookie,
447
+ getOrCreateSessionId,
348
448
  getPageInfo,
349
449
  getSessionDuration,
350
450
  initSessionTracking,
351
451
  initTracking,
352
452
  isSessionStarted,
453
+ renewSessionCookie,
353
454
  sendEvent,
354
455
  sendSessionEnd,
355
456
  sendSessionProgress,
356
457
  sendSessionStart,
458
+ setCookie,
357
459
  trackEvent,
358
460
  trackPageView,
359
461
  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":"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","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\nimport { v4 as uuidv4 } from \"uuid\";\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 theo format UUID v4\r\n */\r\nexport const generateSessionId = (): string => {\r\n return uuidv4();\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":["uuidv4","config"]}
package/dist/index.d.cts CHANGED
@@ -1,9 +1,53 @@
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 theo format UUID v4
18
+ */
19
+ declare const generateSessionId: () => string;
20
+ /**
21
+ * Lấy giá trị cookie theo tên
22
+ */
23
+ declare const getCookie: (name: string) => string | null;
24
+ /**
25
+ * Set cookie với các options
26
+ */
27
+ declare const setCookie: (name: string, value: string, options?: SessionCookieConfig) => void;
28
+ /**
29
+ * Lấy hoặc tạo mới session_id từ cookie
30
+ * Nếu cookie tồn tại và chưa hết hạn -> trả về giá trị cũ
31
+ * Nếu không -> tạo mới và lưu vào cookie
32
+ */
33
+ declare const getOrCreateSessionId: (options?: SessionCookieConfig) => string;
34
+ /**
35
+ * Gia hạn cookie session_id
36
+ * Gọi hàm này khi có event session_start để extend thời gian hết hạn
37
+ */
38
+ declare const renewSessionCookie: (sessionId: string, options?: SessionCookieConfig) => void;
39
+ /**
40
+ * Xóa session cookie
41
+ */
42
+ declare const clearSessionCookie: (options?: SessionCookieConfig) => void;
43
+
1
44
  /**
2
45
  * Session Lifecycle Tracking
3
46
  * - session_start: Khi user mở app
4
47
  * - session_progress: Định kỳ khi user đang active (foreground)
5
48
  * - session_end: Khi user thoát app
6
49
  */
50
+
7
51
  type SessionConfig = {
8
52
  /** Khoảng thời gian gửi session_progress (ms). Default: 5 phút */
9
53
  progressInterval?: number;
@@ -13,6 +57,8 @@ type SessionConfig = {
13
57
  debug?: boolean;
14
58
  /** User ID */
15
59
  userId?: string;
60
+ /** Session cookie configuration */
61
+ sessionCookieConfig?: SessionCookieConfig;
16
62
  };
17
63
  /**
18
64
  * Gửi event session_start
@@ -50,6 +96,7 @@ declare const getSessionDuration: () => number;
50
96
  /**
51
97
  * Type definitions for Tracking Library
52
98
  */
99
+
53
100
  type TrackingConfig = {
54
101
  baseURL: string;
55
102
  appName?: string;
@@ -57,7 +104,8 @@ type TrackingConfig = {
57
104
  packageId?: string;
58
105
  platform?: "Web" | "Ios" | "Android" | string;
59
106
  debug?: boolean;
60
- sessionId: string;
107
+ sessionCookieConfig?: SessionCookieConfig;
108
+ sessionId?: string;
61
109
  sessionSign: string;
62
110
  userId: number;
63
111
  deviceId: string;
@@ -144,4 +192,4 @@ declare const tracking: {
144
192
  sessionEnd: () => void;
145
193
  };
146
194
 
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 };
195
+ 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,53 @@
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 theo format UUID v4
18
+ */
19
+ declare const generateSessionId: () => string;
20
+ /**
21
+ * Lấy giá trị cookie theo tên
22
+ */
23
+ declare const getCookie: (name: string) => string | null;
24
+ /**
25
+ * Set cookie với các options
26
+ */
27
+ declare const setCookie: (name: string, value: string, options?: SessionCookieConfig) => void;
28
+ /**
29
+ * Lấy hoặc tạo mới session_id từ cookie
30
+ * Nếu cookie tồn tại và chưa hết hạn -> trả về giá trị cũ
31
+ * Nếu không -> tạo mới và lưu vào cookie
32
+ */
33
+ declare const getOrCreateSessionId: (options?: SessionCookieConfig) => string;
34
+ /**
35
+ * Gia hạn cookie session_id
36
+ * Gọi hàm này khi có event session_start để extend thời gian hết hạn
37
+ */
38
+ declare const renewSessionCookie: (sessionId: string, options?: SessionCookieConfig) => void;
39
+ /**
40
+ * Xóa session cookie
41
+ */
42
+ declare const clearSessionCookie: (options?: SessionCookieConfig) => void;
43
+
1
44
  /**
2
45
  * Session Lifecycle Tracking
3
46
  * - session_start: Khi user mở app
4
47
  * - session_progress: Định kỳ khi user đang active (foreground)
5
48
  * - session_end: Khi user thoát app
6
49
  */
50
+
7
51
  type SessionConfig = {
8
52
  /** Khoảng thời gian gửi session_progress (ms). Default: 5 phút */
9
53
  progressInterval?: number;
@@ -13,6 +57,8 @@ type SessionConfig = {
13
57
  debug?: boolean;
14
58
  /** User ID */
15
59
  userId?: string;
60
+ /** Session cookie configuration */
61
+ sessionCookieConfig?: SessionCookieConfig;
16
62
  };
17
63
  /**
18
64
  * Gửi event session_start
@@ -50,6 +96,7 @@ declare const getSessionDuration: () => number;
50
96
  /**
51
97
  * Type definitions for Tracking Library
52
98
  */
99
+
53
100
  type TrackingConfig = {
54
101
  baseURL: string;
55
102
  appName?: string;
@@ -57,7 +104,8 @@ type TrackingConfig = {
57
104
  packageId?: string;
58
105
  platform?: "Web" | "Ios" | "Android" | string;
59
106
  debug?: boolean;
60
- sessionId: string;
107
+ sessionCookieConfig?: SessionCookieConfig;
108
+ sessionId?: string;
61
109
  sessionSign: string;
62
110
  userId: number;
63
111
  deviceId: string;
@@ -144,4 +192,4 @@ declare const tracking: {
144
192
  sessionEnd: () => void;
145
193
  };
146
194
 
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 };
195
+ 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,91 @@ var defaultPageMap = {
39
39
  login: { name: "Login", id: "login" }
40
40
  };
41
41
 
42
+ // src/cookie-utils.ts
43
+ import { v4 as uuidv4 } from "uuid";
44
+ var defaultCookieConfig = {
45
+ cookieName: "_tracking_session_id",
46
+ expirationMinutes: 30,
47
+ domain: "",
48
+ path: "/",
49
+ sameSite: "Lax"
50
+ };
51
+ var generateSessionId = () => {
52
+ return uuidv4();
53
+ };
54
+ var getCookie = (name) => {
55
+ if (typeof document === "undefined") return null;
56
+ const nameEQ = name + "=";
57
+ const cookies = document.cookie.split(";");
58
+ for (let i = 0; i < cookies.length; i++) {
59
+ let cookie = cookies[i];
60
+ while (cookie.charAt(0) === " ") {
61
+ cookie = cookie.substring(1);
62
+ }
63
+ if (cookie.indexOf(nameEQ) === 0) {
64
+ return cookie.substring(nameEQ.length);
65
+ }
66
+ }
67
+ return null;
68
+ };
69
+ var setCookie = (name, value, options = {}) => {
70
+ if (typeof document === "undefined") return;
71
+ const config2 = { ...defaultCookieConfig, ...options };
72
+ const expirationMs = config2.expirationMinutes * 60 * 1e3;
73
+ const expires = new Date(Date.now() + expirationMs);
74
+ let cookieString = `${name}=${value}; expires=${expires.toUTCString()}; path=${config2.path}`;
75
+ if (config2.domain) {
76
+ cookieString += `; domain=${config2.domain}`;
77
+ }
78
+ if (config2.sameSite) {
79
+ cookieString += `; SameSite=${config2.sameSite}`;
80
+ }
81
+ if (config2.sameSite === "None") {
82
+ cookieString += "; Secure";
83
+ }
84
+ document.cookie = cookieString;
85
+ };
86
+ var getOrCreateSessionId = (options = {}) => {
87
+ const config2 = { ...defaultCookieConfig, ...options };
88
+ const existingSessionId = getCookie(config2.cookieName);
89
+ if (existingSessionId) {
90
+ return existingSessionId;
91
+ }
92
+ const newSessionId = generateSessionId();
93
+ setCookie(config2.cookieName, newSessionId, options);
94
+ return newSessionId;
95
+ };
96
+ var renewSessionCookie = (sessionId, options = {}) => {
97
+ const config2 = { ...defaultCookieConfig, ...options };
98
+ setCookie(config2.cookieName, sessionId, options);
99
+ };
100
+ var clearSessionCookie = (options = {}) => {
101
+ if (typeof document === "undefined") return;
102
+ const config2 = { ...defaultCookieConfig, ...options };
103
+ let cookieString = `${config2.cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${config2.path}`;
104
+ if (config2.domain) {
105
+ cookieString += `; domain=${config2.domain}`;
106
+ }
107
+ document.cookie = cookieString;
108
+ };
109
+
42
110
  // src/tracking.ts
43
111
  var config = { ...defaultConfig };
44
112
  var previousPageId = null;
45
113
  var pageMap = { ...defaultPageMap };
46
114
  var initTracking = (options = {}) => {
47
115
  config = { ...config, ...options };
116
+ if (!config.sessionId) {
117
+ config.sessionId = getOrCreateSessionId(config.sessionCookieConfig);
118
+ if (config.debug) {
119
+ console.log(
120
+ "[Tracking] Auto-generated session_id from cookie:",
121
+ config.sessionId
122
+ );
123
+ }
124
+ } else if (config.debug) {
125
+ console.log("[Tracking] Using user-provided session_id:", config.sessionId);
126
+ }
48
127
  if (options.pageMap) {
49
128
  pageMap = { ...pageMap, ...options.pageMap };
50
129
  }
@@ -179,6 +258,7 @@ var sendSessionStart = () => {
179
258
  event_time: sessionStartTime
180
259
  });
181
260
  log("Session started at:", new Date(sessionStartTime).toISOString());
261
+ renewSessionIdCookie();
182
262
  startProgressTimer();
183
263
  };
184
264
  var sendSessionProgress = () => {
@@ -281,6 +361,16 @@ var getSessionDuration = () => {
281
361
  if (!isSessionActive) return 0;
282
362
  return Date.now() - sessionStartTime;
283
363
  };
364
+ var renewSessionIdCookie = () => {
365
+ if (sessionConfig.sessionCookieConfig) {
366
+ const cookieName = sessionConfig.sessionCookieConfig.cookieName || "_tracking_session_id";
367
+ const sessionId = document.cookie.split("; ").find((row) => row.startsWith(cookieName + "="))?.split("=")[1];
368
+ if (sessionId) {
369
+ renewSessionCookie(sessionId, sessionConfig.sessionCookieConfig);
370
+ log("Session cookie renewed for:", sessionId);
371
+ }
372
+ }
373
+ };
284
374
 
285
375
  // src/index.ts
286
376
  var tracking = {
@@ -300,18 +390,24 @@ var tracking = {
300
390
  var index_default = tracking;
301
391
  export {
302
392
  cleanupSessionTracking,
393
+ clearSessionCookie,
303
394
  index_default as default,
304
395
  defaultConfig,
305
396
  defaultPageMap,
397
+ generateSessionId,
398
+ getCookie,
399
+ getOrCreateSessionId,
306
400
  getPageInfo,
307
401
  getSessionDuration,
308
402
  initSessionTracking,
309
403
  initTracking,
310
404
  isSessionStarted,
405
+ renewSessionCookie,
311
406
  sendEvent,
312
407
  sendSessionEnd,
313
408
  sendSessionProgress,
314
409
  sendSessionStart,
410
+ setCookie,
315
411
  trackEvent,
316
412
  trackPageView,
317
413
  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\nimport { v4 as uuidv4 } from \"uuid\";\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 theo format UUID v4\r\n */\r\nexport const generateSessionId = (): string => {\r\n return uuidv4();\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.10",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -19,5 +19,13 @@
19
19
  "scripts": {
20
20
  "build": "tsup",
21
21
  "prepublishOnly": "npm run build"
22
+ },
23
+ "dependencies": {
24
+ "uuid": "^13.0.0"
25
+ },
26
+ "devDependencies": {
27
+ "@types/uuid": "^10.0.0",
28
+ "tsup": "^8.5.1",
29
+ "typescript": "^5.9.3"
22
30
  }
23
31
  }