tracking-lib-ott 1.0.1 → 1.0.3
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 +204 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +85 -21
- package/dist/index.d.ts +85 -21
- package/dist/index.js +193 -27
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -20,15 +20,27 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
cleanupSessionTracking: () => cleanupSessionTracking,
|
|
23
24
|
default: () => index_default,
|
|
25
|
+
defaultConfig: () => defaultConfig,
|
|
26
|
+
defaultPageMap: () => defaultPageMap,
|
|
24
27
|
getPageInfo: () => getPageInfo,
|
|
28
|
+
getSessionDuration: () => getSessionDuration,
|
|
29
|
+
initSessionTracking: () => initSessionTracking,
|
|
25
30
|
initTracking: () => initTracking,
|
|
31
|
+
isSessionStarted: () => isSessionStarted,
|
|
26
32
|
sendEvent: () => sendEvent,
|
|
33
|
+
sendSessionEnd: () => sendSessionEnd,
|
|
34
|
+
sendSessionProgress: () => sendSessionProgress,
|
|
35
|
+
sendSessionStart: () => sendSessionStart,
|
|
27
36
|
trackEvent: () => trackEvent,
|
|
28
37
|
trackPageView: () => trackPageView,
|
|
38
|
+
updateSessionConfig: () => updateSessionConfig,
|
|
29
39
|
usePageViewTracking: () => usePageViewTracking
|
|
30
40
|
});
|
|
31
41
|
module.exports = __toCommonJS(index_exports);
|
|
42
|
+
|
|
43
|
+
// src/constants.ts
|
|
32
44
|
var defaultConfig = {
|
|
33
45
|
baseURL: "",
|
|
34
46
|
appName: "on_plus",
|
|
@@ -37,28 +49,26 @@ var defaultConfig = {
|
|
|
37
49
|
platform: "Web",
|
|
38
50
|
debug: false,
|
|
39
51
|
// Required properties
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
52
|
+
sessionId: "",
|
|
53
|
+
sessionSign: "",
|
|
54
|
+
userId: -1,
|
|
43
55
|
deviceId: "",
|
|
44
56
|
deviceModel: "",
|
|
45
57
|
deviceBrand: "",
|
|
46
|
-
|
|
58
|
+
ipAddress: "",
|
|
47
59
|
// Optional properties
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
60
|
+
adId: "",
|
|
61
|
+
osVersion: "",
|
|
62
|
+
netType: "",
|
|
63
|
+
carrierNet: "",
|
|
64
|
+
sdkVersion: "",
|
|
65
|
+
appVersion: "",
|
|
66
|
+
buildNumber: "",
|
|
55
67
|
language: "",
|
|
56
68
|
country: "",
|
|
57
69
|
events: []
|
|
58
70
|
};
|
|
59
|
-
var
|
|
60
|
-
var previousPageId = null;
|
|
61
|
-
var pageMap = {
|
|
71
|
+
var defaultPageMap = {
|
|
62
72
|
"": { name: "Home", id: "home" },
|
|
63
73
|
home: { name: "Home", id: "home" },
|
|
64
74
|
search: { name: "Search", id: "search" },
|
|
@@ -70,6 +80,11 @@ var pageMap = {
|
|
|
70
80
|
profile: { name: "Profile", id: "profile" },
|
|
71
81
|
login: { name: "Login", id: "login" }
|
|
72
82
|
};
|
|
83
|
+
|
|
84
|
+
// src/tracking.ts
|
|
85
|
+
var config = { ...defaultConfig };
|
|
86
|
+
var previousPageId = null;
|
|
87
|
+
var pageMap = { ...defaultPageMap };
|
|
73
88
|
var initTracking = (options = {}) => {
|
|
74
89
|
config = { ...config, ...options };
|
|
75
90
|
if (options.pageMap) {
|
|
@@ -102,21 +117,21 @@ var sendEvent = async (eventData) => {
|
|
|
102
117
|
app_id: config.appId,
|
|
103
118
|
package_id: config.packageId,
|
|
104
119
|
platform: config.platform,
|
|
105
|
-
session_id: config.
|
|
106
|
-
session_sign: config.
|
|
107
|
-
user_id: config.
|
|
120
|
+
session_id: config.sessionId,
|
|
121
|
+
session_sign: config.sessionSign,
|
|
122
|
+
user_id: config.userId,
|
|
108
123
|
device_id: config.deviceId,
|
|
109
124
|
device_model: config.deviceModel,
|
|
110
125
|
device_brand: config.deviceBrand,
|
|
111
|
-
ip_address: config.
|
|
126
|
+
ip_address: config.ipAddress,
|
|
112
127
|
// Optional properties
|
|
113
|
-
ad_id: config.
|
|
114
|
-
os_version: config.
|
|
115
|
-
net_type: config.
|
|
116
|
-
carrier_net: config.
|
|
117
|
-
sdk_version: config.
|
|
118
|
-
app_version: config.
|
|
119
|
-
build_number: config.
|
|
128
|
+
ad_id: config.adId,
|
|
129
|
+
os_version: config.osVersion,
|
|
130
|
+
net_type: config.netType,
|
|
131
|
+
carrier_net: config.carrierNet,
|
|
132
|
+
sdk_version: config.sdkVersion,
|
|
133
|
+
app_version: config.appVersion,
|
|
134
|
+
build_number: config.buildNumber,
|
|
120
135
|
language: config.language,
|
|
121
136
|
country: config.country,
|
|
122
137
|
// Events batch (tuỳ backend)
|
|
@@ -172,22 +187,185 @@ var usePageViewTracking = (router) => {
|
|
|
172
187
|
router.events.off("routeChangeComplete", handleRouteChange);
|
|
173
188
|
};
|
|
174
189
|
};
|
|
190
|
+
|
|
191
|
+
// src/session.ts
|
|
192
|
+
var defaultSessionConfig = {
|
|
193
|
+
progressInterval: 5 * 60 * 1e3,
|
|
194
|
+
// 5 phút
|
|
195
|
+
autoStart: true,
|
|
196
|
+
debug: false
|
|
197
|
+
};
|
|
198
|
+
var sessionConfig = { ...defaultSessionConfig };
|
|
199
|
+
var progressTimer = null;
|
|
200
|
+
var isSessionActive = false;
|
|
201
|
+
var sessionStartTime = 0;
|
|
202
|
+
var log = (...args) => {
|
|
203
|
+
if (sessionConfig.debug) {
|
|
204
|
+
console.log("[Session]", ...args);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
var sendSessionStart = () => {
|
|
208
|
+
if (isSessionActive) {
|
|
209
|
+
log("Session already started, skipping...");
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
sessionStartTime = Date.now();
|
|
213
|
+
isSessionActive = true;
|
|
214
|
+
sendEvent({
|
|
215
|
+
events: [
|
|
216
|
+
{
|
|
217
|
+
event_name: "session_start",
|
|
218
|
+
event_time: sessionStartTime
|
|
219
|
+
}
|
|
220
|
+
]
|
|
221
|
+
});
|
|
222
|
+
log("Session started at:", new Date(sessionStartTime).toISOString());
|
|
223
|
+
startProgressTimer();
|
|
224
|
+
};
|
|
225
|
+
var sendSessionProgress = () => {
|
|
226
|
+
if (!isSessionActive) return;
|
|
227
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
228
|
+
const duration = now - sessionStartTime;
|
|
229
|
+
sendEvent({
|
|
230
|
+
events: [
|
|
231
|
+
{
|
|
232
|
+
event_name: "session_progress",
|
|
233
|
+
event_time: now,
|
|
234
|
+
session_duration: duration
|
|
235
|
+
}
|
|
236
|
+
]
|
|
237
|
+
});
|
|
238
|
+
log("Session progress - Duration:", Math.round(duration / 1e3), "seconds");
|
|
239
|
+
};
|
|
240
|
+
var sendSessionEnd = () => {
|
|
241
|
+
if (!isSessionActive) return;
|
|
242
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
243
|
+
const duration = now - sessionStartTime;
|
|
244
|
+
sendEvent({
|
|
245
|
+
events: [
|
|
246
|
+
{
|
|
247
|
+
event_name: "session_end",
|
|
248
|
+
event_time: now,
|
|
249
|
+
session_duration: duration
|
|
250
|
+
}
|
|
251
|
+
]
|
|
252
|
+
});
|
|
253
|
+
log(
|
|
254
|
+
"Session ended - Total duration:",
|
|
255
|
+
Math.round(duration / 1e3),
|
|
256
|
+
"seconds"
|
|
257
|
+
);
|
|
258
|
+
stopProgressTimer();
|
|
259
|
+
isSessionActive = false;
|
|
260
|
+
};
|
|
261
|
+
var startProgressTimer = () => {
|
|
262
|
+
stopProgressTimer();
|
|
263
|
+
if (sessionConfig.progressInterval && sessionConfig.progressInterval > 0) {
|
|
264
|
+
progressTimer = setInterval(() => {
|
|
265
|
+
if (document.visibilityState === "visible") {
|
|
266
|
+
sendSessionProgress();
|
|
267
|
+
}
|
|
268
|
+
}, sessionConfig.progressInterval);
|
|
269
|
+
log(
|
|
270
|
+
"Progress timer started, interval:",
|
|
271
|
+
sessionConfig.progressInterval,
|
|
272
|
+
"ms"
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
var stopProgressTimer = () => {
|
|
277
|
+
if (progressTimer) {
|
|
278
|
+
clearInterval(progressTimer);
|
|
279
|
+
progressTimer = null;
|
|
280
|
+
log("Progress timer stopped");
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
var handleVisibilityChange = () => {
|
|
284
|
+
if (document.visibilityState === "hidden") {
|
|
285
|
+
log("Tab hidden - pausing progress");
|
|
286
|
+
stopProgressTimer();
|
|
287
|
+
} else if (document.visibilityState === "visible") {
|
|
288
|
+
log("Tab visible - resuming progress");
|
|
289
|
+
if (isSessionActive) {
|
|
290
|
+
startProgressTimer();
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
var handleBeforeUnload = () => {
|
|
295
|
+
sendSessionEnd();
|
|
296
|
+
};
|
|
297
|
+
var handlePageHide = (event) => {
|
|
298
|
+
if (event.persisted) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
sendSessionEnd();
|
|
302
|
+
};
|
|
303
|
+
var initSessionTracking = (options = {}) => {
|
|
304
|
+
if (typeof window === "undefined") return;
|
|
305
|
+
sessionConfig = { ...defaultSessionConfig, ...options };
|
|
306
|
+
log("Initializing with config:", sessionConfig);
|
|
307
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
308
|
+
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
309
|
+
window.addEventListener("pagehide", handlePageHide);
|
|
310
|
+
if (sessionConfig.autoStart) {
|
|
311
|
+
sendSessionStart();
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
var cleanupSessionTracking = () => {
|
|
315
|
+
if (typeof window === "undefined") return;
|
|
316
|
+
log("Cleaning up...");
|
|
317
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
318
|
+
window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
319
|
+
window.removeEventListener("pagehide", handlePageHide);
|
|
320
|
+
stopProgressTimer();
|
|
321
|
+
isSessionActive = false;
|
|
322
|
+
};
|
|
323
|
+
var updateSessionConfig = (options) => {
|
|
324
|
+
sessionConfig = { ...sessionConfig, ...options };
|
|
325
|
+
if (options.progressInterval !== void 0 && isSessionActive) {
|
|
326
|
+
startProgressTimer();
|
|
327
|
+
}
|
|
328
|
+
log("Config updated:", sessionConfig);
|
|
329
|
+
};
|
|
330
|
+
var isSessionStarted = () => isSessionActive;
|
|
331
|
+
var getSessionDuration = () => {
|
|
332
|
+
if (!isSessionActive) return 0;
|
|
333
|
+
return Date.now() - sessionStartTime;
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
// src/index.ts
|
|
175
337
|
var tracking = {
|
|
176
338
|
init: initTracking,
|
|
177
339
|
trackPageView,
|
|
178
340
|
trackEvent,
|
|
179
341
|
sendEvent,
|
|
180
342
|
getPageInfo,
|
|
181
|
-
usePageViewTracking
|
|
343
|
+
usePageViewTracking,
|
|
344
|
+
// Session lifecycle
|
|
345
|
+
initSession: initSessionTracking,
|
|
346
|
+
cleanupSession: cleanupSessionTracking,
|
|
347
|
+
sessionStart: sendSessionStart,
|
|
348
|
+
sessionProgress: sendSessionProgress,
|
|
349
|
+
sessionEnd: sendSessionEnd
|
|
182
350
|
};
|
|
183
351
|
var index_default = tracking;
|
|
184
352
|
// Annotate the CommonJS export names for ESM import in node:
|
|
185
353
|
0 && (module.exports = {
|
|
354
|
+
cleanupSessionTracking,
|
|
355
|
+
defaultConfig,
|
|
356
|
+
defaultPageMap,
|
|
186
357
|
getPageInfo,
|
|
358
|
+
getSessionDuration,
|
|
359
|
+
initSessionTracking,
|
|
187
360
|
initTracking,
|
|
361
|
+
isSessionStarted,
|
|
188
362
|
sendEvent,
|
|
363
|
+
sendSessionEnd,
|
|
364
|
+
sendSessionProgress,
|
|
365
|
+
sendSessionStart,
|
|
189
366
|
trackEvent,
|
|
190
367
|
trackPageView,
|
|
368
|
+
updateSessionConfig,
|
|
191
369
|
usePageViewTracking
|
|
192
370
|
});
|
|
193
371
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.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\nexport type TrackingConfig = {\r\n baseURL: string;\r\n appName?: string;\r\n appId?: string;\r\n packageId?: string;\r\n platform?: \"Web\" | \"Ios\" | \"Android\" | string;\r\n debug?: boolean;\r\n\r\n // Required properties\r\n session_id: string;\r\n session_sign: string;\r\n user_id: number;\r\n deviceId: string;\r\n deviceModel: string;\r\n deviceBrand: string;\r\n ip_address: string;\r\n\r\n // Optional properties\r\n ad_id?: string;\r\n os_version?: string;\r\n net_type?: string;\r\n carrier_net?: string;\r\n sdk_version?: string;\r\n app_version?: string;\r\n build_number?: string;\r\n language?: string;\r\n country?: string;\r\n\r\n // Optional: events (nếu backend cần batch)\r\n events?: any[];\r\n};\r\n\r\nexport type PageInfo = {\r\n name: string;\r\n id: string;\r\n};\r\n\r\nexport type PageMap = Record<string, PageInfo>;\r\n\r\nexport type EventData = Record<string, any>;\r\n\r\nconst 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 session_id: \"\",\r\n session_sign: \"\",\r\n user_id: -1,\r\n deviceId: \"\",\r\n deviceModel: \"\",\r\n deviceBrand: \"\",\r\n ip_address: \"\",\r\n\r\n // Optional properties\r\n ad_id: \"\",\r\n os_version: \"\",\r\n net_type: \"\",\r\n carrier_net: \"\",\r\n sdk_version: \"\",\r\n app_version: \"\",\r\n build_number: \"\",\r\n language: \"\",\r\n country: \"\",\r\n\r\n events: [],\r\n};\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 = {\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/**\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 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.session_id,\r\n session_sign: config.session_sign,\r\n user_id: config.user_id,\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.ip_address,\r\n\r\n // Optional properties\r\n ad_id: config.ad_id,\r\n os_version: config.os_version,\r\n net_type: config.net_type,\r\n carrier_net: config.carrier_net,\r\n sdk_version: config.sdk_version,\r\n app_version: config.app_version,\r\n build_number: config.build_number,\r\n language: config.language,\r\n country: config.country,\r\n\r\n // Events batch (tuỳ backend)\r\n events: config.events ?? [],\r\n\r\n // Data riêng cho event\r\n ...eventData,\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 type NextRouterLike = {\r\n asPath: string;\r\n events: {\r\n on: (event: \"routeChangeComplete\", cb: (url: string) => void) => void;\r\n off: (event: \"routeChangeComplete\", cb: (url: string) => void) => void;\r\n };\r\n};\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// 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};\r\n\r\nexport default tracking;\r\n"],"mappings":"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","names":[]}
|
|
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 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 (tuỳ backend)\r\n events: config.events ?? [],\r\n\r\n // Data riêng cho event\r\n ...eventData,\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};\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 events: [\r\n {\r\n event_name: \"session_start\",\r\n event_time: sessionStartTime,\r\n },\r\n ],\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 events: [\r\n {\r\n event_name: \"session_progress\",\r\n event_time: now,\r\n session_duration: duration,\r\n },\r\n ],\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 events: [\r\n {\r\n event_name: \"session_end\",\r\n event_time: now,\r\n session_duration: duration,\r\n },\r\n ],\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":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,52 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Session Lifecycle Tracking
|
|
3
|
+
* - session_start: Khi user mở app
|
|
4
|
+
* - session_progress: Định kỳ khi user đang active (foreground)
|
|
5
|
+
* - session_end: Khi user thoát app
|
|
6
|
+
*/
|
|
7
|
+
type SessionConfig = {
|
|
8
|
+
/** Khoảng thời gian gửi session_progress (ms). Default: 5 phút */
|
|
9
|
+
progressInterval?: number;
|
|
10
|
+
/** Có tự động start session khi init không. Default: true */
|
|
11
|
+
autoStart?: boolean;
|
|
12
|
+
/** Debug mode */
|
|
13
|
+
debug?: boolean;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Gửi event session_start
|
|
17
|
+
*/
|
|
18
|
+
declare const sendSessionStart: () => void;
|
|
19
|
+
/**
|
|
20
|
+
* Gửi event session_progress
|
|
21
|
+
*/
|
|
22
|
+
declare const sendSessionProgress: () => void;
|
|
23
|
+
/**
|
|
24
|
+
* Gửi event session_end
|
|
25
|
+
*/
|
|
26
|
+
declare const sendSessionEnd: () => void;
|
|
27
|
+
/**
|
|
28
|
+
* Khởi tạo session tracking
|
|
29
|
+
*/
|
|
30
|
+
declare const initSessionTracking: (options?: SessionConfig) => void;
|
|
31
|
+
/**
|
|
32
|
+
* Cleanup - gọi khi unmount component
|
|
33
|
+
*/
|
|
34
|
+
declare const cleanupSessionTracking: () => void;
|
|
35
|
+
/**
|
|
36
|
+
* Cập nhật config (ví dụ: thay đổi interval)
|
|
37
|
+
*/
|
|
38
|
+
declare const updateSessionConfig: (options: Partial<SessionConfig>) => void;
|
|
39
|
+
/**
|
|
40
|
+
* Kiểm tra session đang active không
|
|
41
|
+
*/
|
|
42
|
+
declare const isSessionStarted: () => boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Lấy thời gian session hiện tại (ms)
|
|
45
|
+
*/
|
|
46
|
+
declare const getSessionDuration: () => number;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Type definitions for Tracking Library
|
|
4
50
|
*/
|
|
5
51
|
type TrackingConfig = {
|
|
6
52
|
baseURL: string;
|
|
@@ -9,20 +55,20 @@ type TrackingConfig = {
|
|
|
9
55
|
packageId?: string;
|
|
10
56
|
platform?: "Web" | "Ios" | "Android" | string;
|
|
11
57
|
debug?: boolean;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
58
|
+
sessionId: string;
|
|
59
|
+
sessionSign: string;
|
|
60
|
+
userId: number;
|
|
15
61
|
deviceId: string;
|
|
16
62
|
deviceModel: string;
|
|
17
63
|
deviceBrand: string;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
64
|
+
ipAddress: string;
|
|
65
|
+
adId?: string;
|
|
66
|
+
osVersion?: string;
|
|
67
|
+
netType?: string;
|
|
68
|
+
carrierNet?: string;
|
|
69
|
+
sdkVersion?: string;
|
|
70
|
+
appVersion?: string;
|
|
71
|
+
buildNumber?: string;
|
|
26
72
|
language?: string;
|
|
27
73
|
country?: string;
|
|
28
74
|
events?: any[];
|
|
@@ -33,6 +79,25 @@ type PageInfo = {
|
|
|
33
79
|
};
|
|
34
80
|
type PageMap = Record<string, PageInfo>;
|
|
35
81
|
type EventData = Record<string, any>;
|
|
82
|
+
type NextRouterLike = {
|
|
83
|
+
asPath: string;
|
|
84
|
+
events: {
|
|
85
|
+
on: (event: "routeChangeComplete", cb: (url: string) => void) => void;
|
|
86
|
+
off: (event: "routeChangeComplete", cb: (url: string) => void) => void;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Constants for Tracking Library
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
declare const defaultConfig: TrackingConfig;
|
|
95
|
+
declare const defaultPageMap: PageMap;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Core Tracking Functions
|
|
99
|
+
*/
|
|
100
|
+
|
|
36
101
|
/**
|
|
37
102
|
* Khởi tạo tracking với config
|
|
38
103
|
*/
|
|
@@ -59,14 +124,8 @@ declare const trackEvent: (eventName: string, params?: EventData) => void;
|
|
|
59
124
|
* Hook cho Next.js - auto track page views
|
|
60
125
|
* Dùng cho Next.js Pages Router (next/router)
|
|
61
126
|
*/
|
|
62
|
-
type NextRouterLike = {
|
|
63
|
-
asPath: string;
|
|
64
|
-
events: {
|
|
65
|
-
on: (event: "routeChangeComplete", cb: (url: string) => void) => void;
|
|
66
|
-
off: (event: "routeChangeComplete", cb: (url: string) => void) => void;
|
|
67
|
-
};
|
|
68
|
-
};
|
|
69
127
|
declare const usePageViewTracking: (router: NextRouterLike) => (() => void) | undefined;
|
|
128
|
+
|
|
70
129
|
declare const tracking: {
|
|
71
130
|
init: (options?: Partial<TrackingConfig> & {
|
|
72
131
|
pageMap?: Partial<PageMap>;
|
|
@@ -76,6 +135,11 @@ declare const tracking: {
|
|
|
76
135
|
sendEvent: (eventData: EventData) => Promise<Response | void>;
|
|
77
136
|
getPageInfo: (url: string) => PageInfo;
|
|
78
137
|
usePageViewTracking: (router: NextRouterLike) => (() => void) | undefined;
|
|
138
|
+
initSession: (options?: SessionConfig) => void;
|
|
139
|
+
cleanupSession: () => void;
|
|
140
|
+
sessionStart: () => void;
|
|
141
|
+
sessionProgress: () => void;
|
|
142
|
+
sessionEnd: () => void;
|
|
79
143
|
};
|
|
80
144
|
|
|
81
|
-
export { type EventData, type NextRouterLike, type PageInfo, type PageMap, type TrackingConfig, tracking as default, getPageInfo, initTracking, sendEvent, trackEvent, trackPageView, usePageViewTracking };
|
|
145
|
+
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,52 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Session Lifecycle Tracking
|
|
3
|
+
* - session_start: Khi user mở app
|
|
4
|
+
* - session_progress: Định kỳ khi user đang active (foreground)
|
|
5
|
+
* - session_end: Khi user thoát app
|
|
6
|
+
*/
|
|
7
|
+
type SessionConfig = {
|
|
8
|
+
/** Khoảng thời gian gửi session_progress (ms). Default: 5 phút */
|
|
9
|
+
progressInterval?: number;
|
|
10
|
+
/** Có tự động start session khi init không. Default: true */
|
|
11
|
+
autoStart?: boolean;
|
|
12
|
+
/** Debug mode */
|
|
13
|
+
debug?: boolean;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Gửi event session_start
|
|
17
|
+
*/
|
|
18
|
+
declare const sendSessionStart: () => void;
|
|
19
|
+
/**
|
|
20
|
+
* Gửi event session_progress
|
|
21
|
+
*/
|
|
22
|
+
declare const sendSessionProgress: () => void;
|
|
23
|
+
/**
|
|
24
|
+
* Gửi event session_end
|
|
25
|
+
*/
|
|
26
|
+
declare const sendSessionEnd: () => void;
|
|
27
|
+
/**
|
|
28
|
+
* Khởi tạo session tracking
|
|
29
|
+
*/
|
|
30
|
+
declare const initSessionTracking: (options?: SessionConfig) => void;
|
|
31
|
+
/**
|
|
32
|
+
* Cleanup - gọi khi unmount component
|
|
33
|
+
*/
|
|
34
|
+
declare const cleanupSessionTracking: () => void;
|
|
35
|
+
/**
|
|
36
|
+
* Cập nhật config (ví dụ: thay đổi interval)
|
|
37
|
+
*/
|
|
38
|
+
declare const updateSessionConfig: (options: Partial<SessionConfig>) => void;
|
|
39
|
+
/**
|
|
40
|
+
* Kiểm tra session đang active không
|
|
41
|
+
*/
|
|
42
|
+
declare const isSessionStarted: () => boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Lấy thời gian session hiện tại (ms)
|
|
45
|
+
*/
|
|
46
|
+
declare const getSessionDuration: () => number;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Type definitions for Tracking Library
|
|
4
50
|
*/
|
|
5
51
|
type TrackingConfig = {
|
|
6
52
|
baseURL: string;
|
|
@@ -9,20 +55,20 @@ type TrackingConfig = {
|
|
|
9
55
|
packageId?: string;
|
|
10
56
|
platform?: "Web" | "Ios" | "Android" | string;
|
|
11
57
|
debug?: boolean;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
58
|
+
sessionId: string;
|
|
59
|
+
sessionSign: string;
|
|
60
|
+
userId: number;
|
|
15
61
|
deviceId: string;
|
|
16
62
|
deviceModel: string;
|
|
17
63
|
deviceBrand: string;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
64
|
+
ipAddress: string;
|
|
65
|
+
adId?: string;
|
|
66
|
+
osVersion?: string;
|
|
67
|
+
netType?: string;
|
|
68
|
+
carrierNet?: string;
|
|
69
|
+
sdkVersion?: string;
|
|
70
|
+
appVersion?: string;
|
|
71
|
+
buildNumber?: string;
|
|
26
72
|
language?: string;
|
|
27
73
|
country?: string;
|
|
28
74
|
events?: any[];
|
|
@@ -33,6 +79,25 @@ type PageInfo = {
|
|
|
33
79
|
};
|
|
34
80
|
type PageMap = Record<string, PageInfo>;
|
|
35
81
|
type EventData = Record<string, any>;
|
|
82
|
+
type NextRouterLike = {
|
|
83
|
+
asPath: string;
|
|
84
|
+
events: {
|
|
85
|
+
on: (event: "routeChangeComplete", cb: (url: string) => void) => void;
|
|
86
|
+
off: (event: "routeChangeComplete", cb: (url: string) => void) => void;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Constants for Tracking Library
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
declare const defaultConfig: TrackingConfig;
|
|
95
|
+
declare const defaultPageMap: PageMap;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Core Tracking Functions
|
|
99
|
+
*/
|
|
100
|
+
|
|
36
101
|
/**
|
|
37
102
|
* Khởi tạo tracking với config
|
|
38
103
|
*/
|
|
@@ -59,14 +124,8 @@ declare const trackEvent: (eventName: string, params?: EventData) => void;
|
|
|
59
124
|
* Hook cho Next.js - auto track page views
|
|
60
125
|
* Dùng cho Next.js Pages Router (next/router)
|
|
61
126
|
*/
|
|
62
|
-
type NextRouterLike = {
|
|
63
|
-
asPath: string;
|
|
64
|
-
events: {
|
|
65
|
-
on: (event: "routeChangeComplete", cb: (url: string) => void) => void;
|
|
66
|
-
off: (event: "routeChangeComplete", cb: (url: string) => void) => void;
|
|
67
|
-
};
|
|
68
|
-
};
|
|
69
127
|
declare const usePageViewTracking: (router: NextRouterLike) => (() => void) | undefined;
|
|
128
|
+
|
|
70
129
|
declare const tracking: {
|
|
71
130
|
init: (options?: Partial<TrackingConfig> & {
|
|
72
131
|
pageMap?: Partial<PageMap>;
|
|
@@ -76,6 +135,11 @@ declare const tracking: {
|
|
|
76
135
|
sendEvent: (eventData: EventData) => Promise<Response | void>;
|
|
77
136
|
getPageInfo: (url: string) => PageInfo;
|
|
78
137
|
usePageViewTracking: (router: NextRouterLike) => (() => void) | undefined;
|
|
138
|
+
initSession: (options?: SessionConfig) => void;
|
|
139
|
+
cleanupSession: () => void;
|
|
140
|
+
sessionStart: () => void;
|
|
141
|
+
sessionProgress: () => void;
|
|
142
|
+
sessionEnd: () => void;
|
|
79
143
|
};
|
|
80
144
|
|
|
81
|
-
export { type EventData, type NextRouterLike, type PageInfo, type PageMap, type TrackingConfig, tracking as default, getPageInfo, initTracking, sendEvent, trackEvent, trackPageView, usePageViewTracking };
|
|
145
|
+
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 };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// src/
|
|
1
|
+
// src/constants.ts
|
|
2
2
|
var defaultConfig = {
|
|
3
3
|
baseURL: "",
|
|
4
4
|
appName: "on_plus",
|
|
@@ -7,28 +7,26 @@ var defaultConfig = {
|
|
|
7
7
|
platform: "Web",
|
|
8
8
|
debug: false,
|
|
9
9
|
// Required properties
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
sessionId: "",
|
|
11
|
+
sessionSign: "",
|
|
12
|
+
userId: -1,
|
|
13
13
|
deviceId: "",
|
|
14
14
|
deviceModel: "",
|
|
15
15
|
deviceBrand: "",
|
|
16
|
-
|
|
16
|
+
ipAddress: "",
|
|
17
17
|
// Optional properties
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
adId: "",
|
|
19
|
+
osVersion: "",
|
|
20
|
+
netType: "",
|
|
21
|
+
carrierNet: "",
|
|
22
|
+
sdkVersion: "",
|
|
23
|
+
appVersion: "",
|
|
24
|
+
buildNumber: "",
|
|
25
25
|
language: "",
|
|
26
26
|
country: "",
|
|
27
27
|
events: []
|
|
28
28
|
};
|
|
29
|
-
var
|
|
30
|
-
var previousPageId = null;
|
|
31
|
-
var pageMap = {
|
|
29
|
+
var defaultPageMap = {
|
|
32
30
|
"": { name: "Home", id: "home" },
|
|
33
31
|
home: { name: "Home", id: "home" },
|
|
34
32
|
search: { name: "Search", id: "search" },
|
|
@@ -40,6 +38,11 @@ var pageMap = {
|
|
|
40
38
|
profile: { name: "Profile", id: "profile" },
|
|
41
39
|
login: { name: "Login", id: "login" }
|
|
42
40
|
};
|
|
41
|
+
|
|
42
|
+
// src/tracking.ts
|
|
43
|
+
var config = { ...defaultConfig };
|
|
44
|
+
var previousPageId = null;
|
|
45
|
+
var pageMap = { ...defaultPageMap };
|
|
43
46
|
var initTracking = (options = {}) => {
|
|
44
47
|
config = { ...config, ...options };
|
|
45
48
|
if (options.pageMap) {
|
|
@@ -72,21 +75,21 @@ var sendEvent = async (eventData) => {
|
|
|
72
75
|
app_id: config.appId,
|
|
73
76
|
package_id: config.packageId,
|
|
74
77
|
platform: config.platform,
|
|
75
|
-
session_id: config.
|
|
76
|
-
session_sign: config.
|
|
77
|
-
user_id: config.
|
|
78
|
+
session_id: config.sessionId,
|
|
79
|
+
session_sign: config.sessionSign,
|
|
80
|
+
user_id: config.userId,
|
|
78
81
|
device_id: config.deviceId,
|
|
79
82
|
device_model: config.deviceModel,
|
|
80
83
|
device_brand: config.deviceBrand,
|
|
81
|
-
ip_address: config.
|
|
84
|
+
ip_address: config.ipAddress,
|
|
82
85
|
// Optional properties
|
|
83
|
-
ad_id: config.
|
|
84
|
-
os_version: config.
|
|
85
|
-
net_type: config.
|
|
86
|
-
carrier_net: config.
|
|
87
|
-
sdk_version: config.
|
|
88
|
-
app_version: config.
|
|
89
|
-
build_number: config.
|
|
86
|
+
ad_id: config.adId,
|
|
87
|
+
os_version: config.osVersion,
|
|
88
|
+
net_type: config.netType,
|
|
89
|
+
carrier_net: config.carrierNet,
|
|
90
|
+
sdk_version: config.sdkVersion,
|
|
91
|
+
app_version: config.appVersion,
|
|
92
|
+
build_number: config.buildNumber,
|
|
90
93
|
language: config.language,
|
|
91
94
|
country: config.country,
|
|
92
95
|
// Events batch (tuỳ backend)
|
|
@@ -142,22 +145,185 @@ var usePageViewTracking = (router) => {
|
|
|
142
145
|
router.events.off("routeChangeComplete", handleRouteChange);
|
|
143
146
|
};
|
|
144
147
|
};
|
|
148
|
+
|
|
149
|
+
// src/session.ts
|
|
150
|
+
var defaultSessionConfig = {
|
|
151
|
+
progressInterval: 5 * 60 * 1e3,
|
|
152
|
+
// 5 phút
|
|
153
|
+
autoStart: true,
|
|
154
|
+
debug: false
|
|
155
|
+
};
|
|
156
|
+
var sessionConfig = { ...defaultSessionConfig };
|
|
157
|
+
var progressTimer = null;
|
|
158
|
+
var isSessionActive = false;
|
|
159
|
+
var sessionStartTime = 0;
|
|
160
|
+
var log = (...args) => {
|
|
161
|
+
if (sessionConfig.debug) {
|
|
162
|
+
console.log("[Session]", ...args);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
var sendSessionStart = () => {
|
|
166
|
+
if (isSessionActive) {
|
|
167
|
+
log("Session already started, skipping...");
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
sessionStartTime = Date.now();
|
|
171
|
+
isSessionActive = true;
|
|
172
|
+
sendEvent({
|
|
173
|
+
events: [
|
|
174
|
+
{
|
|
175
|
+
event_name: "session_start",
|
|
176
|
+
event_time: sessionStartTime
|
|
177
|
+
}
|
|
178
|
+
]
|
|
179
|
+
});
|
|
180
|
+
log("Session started at:", new Date(sessionStartTime).toISOString());
|
|
181
|
+
startProgressTimer();
|
|
182
|
+
};
|
|
183
|
+
var sendSessionProgress = () => {
|
|
184
|
+
if (!isSessionActive) return;
|
|
185
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
186
|
+
const duration = now - sessionStartTime;
|
|
187
|
+
sendEvent({
|
|
188
|
+
events: [
|
|
189
|
+
{
|
|
190
|
+
event_name: "session_progress",
|
|
191
|
+
event_time: now,
|
|
192
|
+
session_duration: duration
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
});
|
|
196
|
+
log("Session progress - Duration:", Math.round(duration / 1e3), "seconds");
|
|
197
|
+
};
|
|
198
|
+
var sendSessionEnd = () => {
|
|
199
|
+
if (!isSessionActive) return;
|
|
200
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
201
|
+
const duration = now - sessionStartTime;
|
|
202
|
+
sendEvent({
|
|
203
|
+
events: [
|
|
204
|
+
{
|
|
205
|
+
event_name: "session_end",
|
|
206
|
+
event_time: now,
|
|
207
|
+
session_duration: duration
|
|
208
|
+
}
|
|
209
|
+
]
|
|
210
|
+
});
|
|
211
|
+
log(
|
|
212
|
+
"Session ended - Total duration:",
|
|
213
|
+
Math.round(duration / 1e3),
|
|
214
|
+
"seconds"
|
|
215
|
+
);
|
|
216
|
+
stopProgressTimer();
|
|
217
|
+
isSessionActive = false;
|
|
218
|
+
};
|
|
219
|
+
var startProgressTimer = () => {
|
|
220
|
+
stopProgressTimer();
|
|
221
|
+
if (sessionConfig.progressInterval && sessionConfig.progressInterval > 0) {
|
|
222
|
+
progressTimer = setInterval(() => {
|
|
223
|
+
if (document.visibilityState === "visible") {
|
|
224
|
+
sendSessionProgress();
|
|
225
|
+
}
|
|
226
|
+
}, sessionConfig.progressInterval);
|
|
227
|
+
log(
|
|
228
|
+
"Progress timer started, interval:",
|
|
229
|
+
sessionConfig.progressInterval,
|
|
230
|
+
"ms"
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
var stopProgressTimer = () => {
|
|
235
|
+
if (progressTimer) {
|
|
236
|
+
clearInterval(progressTimer);
|
|
237
|
+
progressTimer = null;
|
|
238
|
+
log("Progress timer stopped");
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
var handleVisibilityChange = () => {
|
|
242
|
+
if (document.visibilityState === "hidden") {
|
|
243
|
+
log("Tab hidden - pausing progress");
|
|
244
|
+
stopProgressTimer();
|
|
245
|
+
} else if (document.visibilityState === "visible") {
|
|
246
|
+
log("Tab visible - resuming progress");
|
|
247
|
+
if (isSessionActive) {
|
|
248
|
+
startProgressTimer();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
var handleBeforeUnload = () => {
|
|
253
|
+
sendSessionEnd();
|
|
254
|
+
};
|
|
255
|
+
var handlePageHide = (event) => {
|
|
256
|
+
if (event.persisted) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
sendSessionEnd();
|
|
260
|
+
};
|
|
261
|
+
var initSessionTracking = (options = {}) => {
|
|
262
|
+
if (typeof window === "undefined") return;
|
|
263
|
+
sessionConfig = { ...defaultSessionConfig, ...options };
|
|
264
|
+
log("Initializing with config:", sessionConfig);
|
|
265
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
266
|
+
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
267
|
+
window.addEventListener("pagehide", handlePageHide);
|
|
268
|
+
if (sessionConfig.autoStart) {
|
|
269
|
+
sendSessionStart();
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
var cleanupSessionTracking = () => {
|
|
273
|
+
if (typeof window === "undefined") return;
|
|
274
|
+
log("Cleaning up...");
|
|
275
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
276
|
+
window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
277
|
+
window.removeEventListener("pagehide", handlePageHide);
|
|
278
|
+
stopProgressTimer();
|
|
279
|
+
isSessionActive = false;
|
|
280
|
+
};
|
|
281
|
+
var updateSessionConfig = (options) => {
|
|
282
|
+
sessionConfig = { ...sessionConfig, ...options };
|
|
283
|
+
if (options.progressInterval !== void 0 && isSessionActive) {
|
|
284
|
+
startProgressTimer();
|
|
285
|
+
}
|
|
286
|
+
log("Config updated:", sessionConfig);
|
|
287
|
+
};
|
|
288
|
+
var isSessionStarted = () => isSessionActive;
|
|
289
|
+
var getSessionDuration = () => {
|
|
290
|
+
if (!isSessionActive) return 0;
|
|
291
|
+
return Date.now() - sessionStartTime;
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
// src/index.ts
|
|
145
295
|
var tracking = {
|
|
146
296
|
init: initTracking,
|
|
147
297
|
trackPageView,
|
|
148
298
|
trackEvent,
|
|
149
299
|
sendEvent,
|
|
150
300
|
getPageInfo,
|
|
151
|
-
usePageViewTracking
|
|
301
|
+
usePageViewTracking,
|
|
302
|
+
// Session lifecycle
|
|
303
|
+
initSession: initSessionTracking,
|
|
304
|
+
cleanupSession: cleanupSessionTracking,
|
|
305
|
+
sessionStart: sendSessionStart,
|
|
306
|
+
sessionProgress: sendSessionProgress,
|
|
307
|
+
sessionEnd: sendSessionEnd
|
|
152
308
|
};
|
|
153
309
|
var index_default = tracking;
|
|
154
310
|
export {
|
|
311
|
+
cleanupSessionTracking,
|
|
155
312
|
index_default as default,
|
|
313
|
+
defaultConfig,
|
|
314
|
+
defaultPageMap,
|
|
156
315
|
getPageInfo,
|
|
316
|
+
getSessionDuration,
|
|
317
|
+
initSessionTracking,
|
|
157
318
|
initTracking,
|
|
319
|
+
isSessionStarted,
|
|
158
320
|
sendEvent,
|
|
321
|
+
sendSessionEnd,
|
|
322
|
+
sendSessionProgress,
|
|
323
|
+
sendSessionStart,
|
|
159
324
|
trackEvent,
|
|
160
325
|
trackPageView,
|
|
326
|
+
updateSessionConfig,
|
|
161
327
|
usePageViewTracking
|
|
162
328
|
};
|
|
163
329
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.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\nexport type TrackingConfig = {\r\n baseURL: string;\r\n appName?: string;\r\n appId?: string;\r\n packageId?: string;\r\n platform?: \"Web\" | \"Ios\" | \"Android\" | string;\r\n debug?: boolean;\r\n\r\n // Required properties\r\n session_id: string;\r\n session_sign: string;\r\n user_id: number;\r\n deviceId: string;\r\n deviceModel: string;\r\n deviceBrand: string;\r\n ip_address: string;\r\n\r\n // Optional properties\r\n ad_id?: string;\r\n os_version?: string;\r\n net_type?: string;\r\n carrier_net?: string;\r\n sdk_version?: string;\r\n app_version?: string;\r\n build_number?: string;\r\n language?: string;\r\n country?: string;\r\n\r\n // Optional: events (nếu backend cần batch)\r\n events?: any[];\r\n};\r\n\r\nexport type PageInfo = {\r\n name: string;\r\n id: string;\r\n};\r\n\r\nexport type PageMap = Record<string, PageInfo>;\r\n\r\nexport type EventData = Record<string, any>;\r\n\r\nconst 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 session_id: \"\",\r\n session_sign: \"\",\r\n user_id: -1,\r\n deviceId: \"\",\r\n deviceModel: \"\",\r\n deviceBrand: \"\",\r\n ip_address: \"\",\r\n\r\n // Optional properties\r\n ad_id: \"\",\r\n os_version: \"\",\r\n net_type: \"\",\r\n carrier_net: \"\",\r\n sdk_version: \"\",\r\n app_version: \"\",\r\n build_number: \"\",\r\n language: \"\",\r\n country: \"\",\r\n\r\n events: [],\r\n};\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 = {\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/**\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 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.session_id,\r\n session_sign: config.session_sign,\r\n user_id: config.user_id,\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.ip_address,\r\n\r\n // Optional properties\r\n ad_id: config.ad_id,\r\n os_version: config.os_version,\r\n net_type: config.net_type,\r\n carrier_net: config.carrier_net,\r\n sdk_version: config.sdk_version,\r\n app_version: config.app_version,\r\n build_number: config.build_number,\r\n language: config.language,\r\n country: config.country,\r\n\r\n // Events batch (tuỳ backend)\r\n events: config.events ?? [],\r\n\r\n // Data riêng cho event\r\n ...eventData,\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 type NextRouterLike = {\r\n asPath: string;\r\n events: {\r\n on: (event: \"routeChangeComplete\", cb: (url: string) => void) => void;\r\n off: (event: \"routeChangeComplete\", cb: (url: string) => void) => void;\r\n };\r\n};\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// 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};\r\n\r\nexport default tracking;\r\n"],"mappings":"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","names":[]}
|
|
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 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 (tuỳ backend)\r\n events: config.events ?? [],\r\n\r\n // Data riêng cho event\r\n ...eventData,\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};\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 events: [\r\n {\r\n event_name: \"session_start\",\r\n event_time: sessionStartTime,\r\n },\r\n ],\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 events: [\r\n {\r\n event_name: \"session_progress\",\r\n event_time: now,\r\n session_duration: duration,\r\n },\r\n ],\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 events: [\r\n {\r\n event_name: \"session_end\",\r\n event_time: now,\r\n session_duration: duration,\r\n },\r\n ],\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":[]}
|