tracking-lib-ott 1.0.13 → 1.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -39,8 +39,19 @@ __export(index_exports, {
39
39
  sendSessionProgress: () => sendSessionProgress,
40
40
  sendSessionStart: () => sendSessionStart,
41
41
  setCookie: () => setCookie,
42
+ setUserInfo: () => setUserInfo,
43
+ trackBufferEnded: () => trackBufferEnded,
44
+ trackBufferStarted: () => trackBufferStarted,
42
45
  trackEvent: () => trackEvent,
43
46
  trackPageView: () => trackPageView,
47
+ trackPlaybackCompleted: () => trackPlaybackCompleted,
48
+ trackPlaybackError: () => trackPlaybackError,
49
+ trackPlaybackPaused: () => trackPlaybackPaused,
50
+ trackPlaybackProgress: () => trackPlaybackProgress,
51
+ trackPlaybackResumed: () => trackPlaybackResumed,
52
+ trackPlaybackStarted: () => trackPlaybackStarted,
53
+ trackQualityChanged: () => trackQualityChanged,
54
+ trackViewContentDetails: () => trackViewContentDetails,
44
55
  updateSessionConfig: () => updateSessionConfig,
45
56
  usePageViewTracking: () => usePageViewTracking
46
57
  });
@@ -49,29 +60,19 @@ module.exports = __toCommonJS(index_exports);
49
60
  // src/constants.ts
50
61
  var defaultConfig = {
51
62
  baseURL: "",
52
- appName: "on_plus",
53
63
  appId: "",
54
64
  packageId: "com.vtvcab.onsportstv",
55
- platform: "Web",
65
+ platform: "WebPC",
56
66
  debug: false,
57
67
  // Required properties
58
68
  sessionId: "",
59
69
  sessionSign: "",
60
- userId: -1,
61
70
  deviceId: "",
62
71
  deviceModel: "",
63
72
  deviceBrand: "",
64
- ipAddress: "",
65
73
  // Optional properties
66
74
  adId: "",
67
- osVersion: "",
68
- netType: "",
69
- carrierNet: "",
70
75
  sdkVersion: "",
71
- appVersion: "",
72
- buildNumber: "",
73
- language: "",
74
- country: "",
75
76
  events: []
76
77
  };
77
78
  var defaultPageMap = {
@@ -156,8 +157,8 @@ var clearSessionCookie = (options = {}) => {
156
157
  };
157
158
 
158
159
  // src/tracking.ts
159
- var import_uuid2 = require("uuid");
160
160
  var config = { ...defaultConfig };
161
+ var userId = config.userId ?? -1;
161
162
  var previousPageId = null;
162
163
  var pageMap = { ...defaultPageMap };
163
164
  var initTracking = (options = {}) => {
@@ -183,6 +184,12 @@ var initTracking = (options = {}) => {
183
184
  console.log("[Tracking] Initialized with config:", config);
184
185
  }
185
186
  };
187
+ var setUserInfo = (userInfo) => {
188
+ userId = userInfo.userId;
189
+ if (config.debug) {
190
+ console.log("[Tracking] User info updated, userId:", userId);
191
+ }
192
+ };
186
193
  var getPageInfo = (url) => {
187
194
  const path = (url || "").split("?")[0];
188
195
  const pathSegment = path.split("/").filter(Boolean)[0] || "home";
@@ -199,35 +206,30 @@ var sendEvent = async (eventData) => {
199
206
  try {
200
207
  const { event_name, ...eventParams } = eventData;
201
208
  const eventObject = {
202
- event_id: (0, import_uuid2.v4)(),
209
+ // Required fields
210
+ user_id: userId,
211
+ // userId được set từ setUserInfo, -1 nếu chưa set
203
212
  event_name: event_name || "unknown",
204
- event_params: JSON.stringify(eventParams),
205
213
  event_time: Math.floor(Date.now() / 1e3),
206
- user_id: config.userId
214
+ // Optional fields
215
+ event_params: JSON.stringify(eventParams)
216
+ // JSON string ≤ 1024
207
217
  };
208
218
  const payload = {
209
219
  // Required properties
210
- app_name: config.appName,
211
220
  app_id: config.appId,
212
221
  package_id: config.packageId,
213
222
  platform: config.platform,
214
223
  session_id: config.sessionId,
215
224
  session_sign: config.sessionSign,
216
- user_id: config.userId,
217
225
  device_id: config.deviceId,
218
226
  device_model: config.deviceModel,
219
227
  device_brand: config.deviceBrand,
220
- ip_address: config.ipAddress,
221
228
  // Optional properties
222
229
  ad_id: config.adId,
223
- os_version: config.osVersion,
224
- net_type: config.netType,
225
- carrier_net: config.carrierNet,
230
+ device_os_version: config.deviceOsVersion,
226
231
  sdk_version: config.sdkVersion,
227
- app_version: config.appVersion,
228
- build_number: config.buildNumber,
229
- language: config.language,
230
- country: config.country,
232
+ // Note: ip_address và carrier_net - Server tự lấy từ header
231
233
  // Events batch - tự động thêm event vào mảng
232
234
  events: [...config.events ?? [], eventObject]
233
235
  };
@@ -267,6 +269,143 @@ var trackEvent = (eventName, params = {}) => {
267
269
  ...params
268
270
  });
269
271
  };
272
+ var trackViewContentDetails = (contentId, contentTitle, contentType, channelId, eventTime) => {
273
+ sendEvent({
274
+ event_name: "view_content_details",
275
+ content_id: contentId,
276
+ content_title: contentTitle,
277
+ content_type: contentType,
278
+ channel_id: channelId,
279
+ event_time: eventTime
280
+ });
281
+ };
282
+ var trackPlaybackStarted = (params) => {
283
+ sendEvent({
284
+ event_name: "playback_started",
285
+ content_id: params.contentId,
286
+ content_title: params.contentTitle,
287
+ content_type: params.contentType,
288
+ playback_session: params.playbackSession,
289
+ video_quality: params.videoQuality,
290
+ start_timestamp: params.startTimestamp,
291
+ start_position: params.startPosition,
292
+ duration: params.duration,
293
+ channel_id: params.channelId,
294
+ sub_content_id: params.subContentId,
295
+ sub_content_title: params.subContentTitle,
296
+ event_time: params.eventTime
297
+ });
298
+ };
299
+ var trackPlaybackProgress = (params) => {
300
+ sendEvent({
301
+ event_name: "playback_progress",
302
+ content_id: params.contentId,
303
+ content_title: params.contentTitle,
304
+ content_type: params.contentType,
305
+ playback_session: params.playbackSession,
306
+ total_watch_time: params.totalWatchTime,
307
+ channel_id: params.channelId,
308
+ sub_content_id: params.subContentId,
309
+ sub_content_title: params.subContentTitle,
310
+ event_time: params.eventTime
311
+ });
312
+ };
313
+ var trackPlaybackCompleted = (params) => {
314
+ sendEvent({
315
+ event_name: "playback_completed",
316
+ content_id: params.contentId,
317
+ content_title: params.contentTitle,
318
+ content_type: params.contentType,
319
+ playback_session: params.playbackSession,
320
+ total_watch_time: params.totalWatchTime,
321
+ channel_id: params.channelId,
322
+ sub_content_id: params.subContentId,
323
+ sub_content_title: params.subContentTitle,
324
+ event_time: params.eventTime
325
+ });
326
+ };
327
+ var trackPlaybackError = (params) => {
328
+ sendEvent({
329
+ event_name: "playback_error",
330
+ content_id: params.contentId,
331
+ content_title: params.contentTitle,
332
+ content_type: params.contentType,
333
+ playback_session: params.playbackSession,
334
+ channel_id: params.channelId,
335
+ error_code: params.errorCode,
336
+ error_message: params.errorMessage,
337
+ error_type: params.errorType,
338
+ event_time: params.eventTime
339
+ });
340
+ };
341
+ var trackQualityChanged = (params) => {
342
+ sendEvent({
343
+ event_name: "quality_changed",
344
+ content_id: params.contentId,
345
+ content_title: params.contentTitle,
346
+ content_type: params.contentType,
347
+ playback_session: params.playbackSession,
348
+ from_quality: params.fromQuality,
349
+ to_quality: params.toQuality,
350
+ position_sec: params.positionSec,
351
+ channel_id: params.channelId,
352
+ reason: params.reason,
353
+ event_time: params.eventTime
354
+ });
355
+ };
356
+ var trackBufferStarted = (params) => {
357
+ sendEvent({
358
+ event_name: "buffer_started",
359
+ content_id: params.contentId,
360
+ content_title: params.contentTitle,
361
+ content_type: params.contentType,
362
+ playback_session: params.playbackSession,
363
+ video_quality: params.videoQuality,
364
+ position_sec: params.positionSec,
365
+ channel_id: params.channelId,
366
+ reason: params.reason,
367
+ event_time: params.eventTime
368
+ });
369
+ };
370
+ var trackBufferEnded = (params) => {
371
+ sendEvent({
372
+ event_name: "buffer_ended",
373
+ content_id: params.contentId,
374
+ content_title: params.contentTitle,
375
+ content_type: params.contentType,
376
+ playback_session: params.playbackSession,
377
+ video_quality: params.videoQuality,
378
+ position_sec: params.positionSec,
379
+ buffer_duration_sec: params.bufferDurationSec,
380
+ channel_id: params.channelId,
381
+ reason: params.reason,
382
+ event_time: params.eventTime
383
+ });
384
+ };
385
+ var trackPlaybackPaused = (params) => {
386
+ sendEvent({
387
+ event_name: "playback_paused",
388
+ content_id: params.contentId,
389
+ content_title: params.contentTitle,
390
+ content_type: params.contentType,
391
+ playback_session: params.playbackSession,
392
+ position_sec: params.positionSec,
393
+ channel_id: params.channelId,
394
+ event_time: params.eventTime
395
+ });
396
+ };
397
+ var trackPlaybackResumed = (params) => {
398
+ sendEvent({
399
+ event_name: "playback_resumed",
400
+ content_id: params.contentId,
401
+ content_title: params.contentTitle,
402
+ content_type: params.contentType,
403
+ playback_session: params.playbackSession,
404
+ position_sec: params.positionSec,
405
+ channel_id: params.channelId,
406
+ event_time: params.eventTime
407
+ });
408
+ };
270
409
  var usePageViewTracking = (router) => {
271
410
  if (typeof window === "undefined") return;
272
411
  if (!router) return;
@@ -426,8 +565,19 @@ var renewSessionIdCookie = () => {
426
565
  // src/index.ts
427
566
  var tracking = {
428
567
  init: initTracking,
568
+ setUserInfo,
429
569
  trackPageView,
430
570
  trackEvent,
571
+ trackViewContentDetails,
572
+ trackPlaybackStarted,
573
+ trackPlaybackProgress,
574
+ trackPlaybackCompleted,
575
+ trackPlaybackError,
576
+ trackQualityChanged,
577
+ trackBufferStarted,
578
+ trackBufferEnded,
579
+ trackPlaybackPaused,
580
+ trackPlaybackResumed,
431
581
  sendEvent,
432
582
  getPageInfo,
433
583
  usePageViewTracking,
@@ -459,8 +609,19 @@ var index_default = tracking;
459
609
  sendSessionProgress,
460
610
  sendSessionStart,
461
611
  setCookie,
612
+ setUserInfo,
613
+ trackBufferEnded,
614
+ trackBufferStarted,
462
615
  trackEvent,
463
616
  trackPageView,
617
+ trackPlaybackCompleted,
618
+ trackPlaybackError,
619
+ trackPlaybackPaused,
620
+ trackPlaybackProgress,
621
+ trackPlaybackResumed,
622
+ trackPlaybackStarted,
623
+ trackQualityChanged,
624
+ trackViewContentDetails,
464
625
  updateSessionConfig,
465
626
  usePageViewTracking
466
627
  });
@@ -1 +1 @@
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\nimport { v4 as uuidv4 } from \"uuid\";\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_id: uuidv4(),\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 // Gia hạn cookie session_id mỗi khi user còn active\r\n renewSessionIdCookie();\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","import_uuid","uuidv4"]}
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 UserInfo,\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 setUserInfo,\r\n getPageInfo,\r\n sendEvent,\r\n trackPageView,\r\n trackEvent,\r\n trackViewContentDetails,\r\n trackPlaybackStarted,\r\n trackPlaybackProgress,\r\n trackPlaybackCompleted,\r\n trackPlaybackError,\r\n trackQualityChanged,\r\n trackBufferStarted,\r\n trackBufferEnded,\r\n trackPlaybackPaused,\r\n trackPlaybackResumed,\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 setUserInfo,\r\n getPageInfo,\r\n sendEvent,\r\n trackPageView,\r\n trackEvent,\r\n trackViewContentDetails,\r\n trackPlaybackStarted,\r\n trackPlaybackProgress,\r\n trackPlaybackCompleted,\r\n trackPlaybackError,\r\n trackQualityChanged,\r\n trackBufferStarted,\r\n trackBufferEnded,\r\n trackPlaybackPaused,\r\n trackPlaybackResumed,\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 setUserInfo,\r\n trackPageView,\r\n trackEvent,\r\n trackViewContentDetails,\r\n trackPlaybackStarted,\r\n trackPlaybackProgress,\r\n trackPlaybackCompleted,\r\n trackPlaybackError,\r\n trackQualityChanged,\r\n trackBufferStarted,\r\n trackBufferEnded,\r\n trackPlaybackPaused,\r\n trackPlaybackResumed,\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 appId: \"\",\r\n packageId: \"com.vtvcab.onsportstv\",\r\n platform: \"WebPC\",\r\n debug: false,\r\n\r\n // Required properties\r\n sessionId: \"\",\r\n sessionSign: \"\",\r\n deviceId: \"\",\r\n deviceModel: \"\",\r\n deviceBrand: \"\",\r\n\r\n // Optional properties\r\n adId: \"\",\r\n sdkVersion: \"\",\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 UserInfo,\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// User info state\r\nlet userId: number = config.userId ?? -1;\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 * Cập nhật thông tin người dùng (userId)\r\n * userId sẽ được tự động thêm vào mỗi event trong events array\r\n */\r\nexport const setUserInfo = (userInfo: UserInfo) => {\r\n userId = userInfo.userId;\r\n\r\n if (config.debug) {\r\n console.log(\"[Tracking] User info updated, userId:\", userId);\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 theo API spec\r\n const eventObject = {\r\n // Required fields\r\n user_id: userId, // userId được set từ setUserInfo, -1 nếu chưa set\r\n event_name: event_name || \"unknown\",\r\n event_time: Math.floor(Date.now() / 1000),\r\n // Optional fields\r\n event_params: JSON.stringify(eventParams), // JSON string ≤ 1024\r\n };\r\n\r\n const payload = {\r\n // Required properties\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\r\n device_id: config.deviceId,\r\n device_model: config.deviceModel,\r\n device_brand: config.deviceBrand,\r\n\r\n // Optional properties\r\n ad_id: config.adId,\r\n device_os_version: config.deviceOsVersion,\r\n sdk_version: config.sdkVersion,\r\n\r\n // Note: ip_address và carrier_net - Server tự lấy từ header\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 * Track view content details event\r\n * @param contentId - ID của content (required)\r\n * @param contentTitle - Tiêu đề content (required)\r\n * @param contentType - Loại content: 0=VOD, 1=Event, 2=Channel (required)\r\n * @param channelId - Mã kênh EPG (optional)\r\n * @param eventTime - Timestamp Unix (optional, mặc định lấy time hiện tại)\r\n */\r\nexport const trackViewContentDetails = (\r\n contentId: string,\r\n contentTitle: string,\r\n contentType: 0 | 1 | 2,\r\n channelId?: string,\r\n eventTime?: number,\r\n) => {\r\n sendEvent({\r\n event_name: \"view_content_details\",\r\n content_id: contentId,\r\n content_title: contentTitle,\r\n content_type: contentType,\r\n channel_id: channelId,\r\n event_time: eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback started event\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID: MD5(content_id + start_timestamp) (required)\r\n * @param params.videoQuality - Chất lượng video: 360p, 480p, 720p, 1080p, 2k, 4k (required)\r\n * @param params.startTimestamp - Unix timestamp khi bắt đầu phát (required)\r\n * @param params.startPosition - Vị trí bắt đầu: Live=0, VOD: 0 nếu mới, >0 nếu xem lại (required)\r\n * @param params.duration - Thời lượng: Live=-1, VOD=số giây (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.subContentId - ID chương trình trong kênh live (optional)\r\n * @param params.subContentTitle - Title chương trình trong kênh live (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackStarted = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n videoQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n startTimestamp: number;\r\n startPosition: number;\r\n duration: number;\r\n channelId?: string;\r\n subContentId?: string;\r\n subContentTitle?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_started\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n video_quality: params.videoQuality,\r\n start_timestamp: params.startTimestamp,\r\n start_position: params.startPosition,\r\n duration: params.duration,\r\n channel_id: params.channelId,\r\n sub_content_id: params.subContentId,\r\n sub_content_title: params.subContentTitle,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback progress event\r\n * Gửi định kỳ: loop 1 = 5s, loop 2 = 25s để track thời lượng user xem nội dung\r\n * Lưu ý: total_watch_time là tổng thời gian xem được (không tính pause)\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.totalWatchTime - Tổng thời gian đã xem tính bằng giây (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.subContentId - ID chương trình trong kênh live (optional)\r\n * @param params.subContentTitle - Title chương trình trong kênh live (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackProgress = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n totalWatchTime: number;\r\n channelId?: string;\r\n subContentId?: string;\r\n subContentTitle?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_progress\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n total_watch_time: params.totalWatchTime,\r\n channel_id: params.channelId,\r\n sub_content_id: params.subContentId,\r\n sub_content_title: params.subContentTitle,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback completed event\r\n * Tương tự playback_progress nhưng là event cuối cùng khi kết thúc xem\r\n * Với Live có thể thoát chủ động thì tracking event này\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.totalWatchTime - Tổng thời gian đã xem tính bằng giây (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.subContentId - ID chương trình trong kênh live (optional)\r\n * @param params.subContentTitle - Title chương trình trong kênh live (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackCompleted = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n totalWatchTime: number;\r\n channelId?: string;\r\n subContentId?: string;\r\n subContentTitle?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_completed\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n total_watch_time: params.totalWatchTime,\r\n channel_id: params.channelId,\r\n sub_content_id: params.subContentId,\r\n sub_content_title: params.subContentTitle,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback error event\r\n * Trigger khi:\r\n * - Video không load/play được (network timeout, source 404)\r\n * - Player error (format không hỗ trợ, DRM error)\r\n * - Source error (media source and manifest-related errors)\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.errorCode - Mã lỗi (optional)\r\n * @param params.errorMessage - Thông báo lỗi (optional)\r\n * @param params.errorType - Loại lỗi: video_playback_error, network_error... (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackError = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1;\r\n playbackSession: string;\r\n channelId?: string;\r\n errorCode?: number;\r\n errorMessage?: string;\r\n errorType?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_error\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n channel_id: params.channelId,\r\n error_code: params.errorCode,\r\n error_message: params.errorMessage,\r\n error_type: params.errorType,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track quality changed event\r\n * Trigger khi:\r\n * - Tự động thay đổi do thuật toán ABR\r\n * - Thay đổi chủ động bởi người dùng\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.fromQuality - Chất lượng trước khi đổi (required)\r\n * @param params.toQuality - Chất lượng sau khi đổi (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.reason - Lý do: automatic_abr hoặc user_selected (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackQualityChanged = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n fromQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n toQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n positionSec: number;\r\n channelId?: string;\r\n reason?: \"automatic_abr\" | \"user_selected\";\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"quality_changed\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n from_quality: params.fromQuality,\r\n to_quality: params.toQuality,\r\n position_sec: params.positionSec,\r\n channel_id: params.channelId,\r\n reason: params.reason,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track buffer started event\r\n * Buffering xảy ra khi video không còn segment kế tiếp để phát, video sẽ pause để tải thêm segment\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.videoQuality - Chất lượng video hiện tại (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.reason - Lý do: seek_operation, network_congestion (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackBufferStarted = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n videoQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n positionSec: number;\r\n channelId?: string;\r\n reason?: \"seek_operation\" | \"network_congestion\";\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"buffer_started\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n video_quality: params.videoQuality,\r\n position_sec: params.positionSec,\r\n channel_id: params.channelId,\r\n reason: params.reason,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track buffer ended event\r\n * Khi buffering kết thúc và video tiếp tục phát\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.videoQuality - Chất lượng video hiện tại (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.bufferDurationSec - Tổng số giây mà buffer xảy ra (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.reason - Lý do: seek_operation, network_congestion (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackBufferEnded = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n videoQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n positionSec: number;\r\n bufferDurationSec: number;\r\n channelId?: string;\r\n reason?: \"seek_operation\" | \"network_congestion\";\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"buffer_ended\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n video_quality: params.videoQuality,\r\n position_sec: params.positionSec,\r\n buffer_duration_sec: params.bufferDurationSec,\r\n channel_id: params.channelId,\r\n reason: params.reason,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback paused event\r\n * Khi user pause video\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackPaused = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n positionSec: number;\r\n channelId?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_paused\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n position_sec: params.positionSec,\r\n channel_id: params.channelId,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback resumed event\r\n * Khi user resume video sau khi pause\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackResumed = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n positionSec: number;\r\n channelId?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_resumed\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n position_sec: params.positionSec,\r\n channel_id: params.channelId,\r\n event_time: params.eventTime,\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 // Gia hạn cookie session_id mỗi khi user còn active\r\n renewSessionIdCookie();\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"]}