tracking-lib-ott 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +265 -0
- package/dist/index.cjs +192 -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 +181 -27
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# Tracking Library OTT
|
|
2
|
+
|
|
3
|
+
Thư viện tracking events dành cho các dự án OTT (Web, iOS, Android).
|
|
4
|
+
|
|
5
|
+
## Cài đặt
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install tracking-lib-ott
|
|
9
|
+
# hoặc
|
|
10
|
+
yarn add tracking-lib-ott
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Sử dụng cơ bản
|
|
14
|
+
|
|
15
|
+
### 1. Khởi tạo (Init)
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
import tracking from "tracking-lib-ott";
|
|
19
|
+
|
|
20
|
+
tracking.init({
|
|
21
|
+
baseURL: "https://api.example.com/v1/event_batch",
|
|
22
|
+
debug: true, // Bật log để debug
|
|
23
|
+
|
|
24
|
+
// Thông tin bắt buộc
|
|
25
|
+
session_id: "unique-session-id",
|
|
26
|
+
session_sign: "session-signature",
|
|
27
|
+
user_id: 12345,
|
|
28
|
+
deviceId: "device-unique-id",
|
|
29
|
+
deviceModel: "iPhone 14",
|
|
30
|
+
deviceBrand: "Apple",
|
|
31
|
+
ip_address: "192.168.1.1",
|
|
32
|
+
|
|
33
|
+
// Thông tin tuỳ chọn
|
|
34
|
+
appName: "on_plus",
|
|
35
|
+
appId: "app-id",
|
|
36
|
+
packageId: "com.vtvcab.onsportstv",
|
|
37
|
+
platform: "Web", // 'Web' | 'Ios' | 'Android'
|
|
38
|
+
os_version: "17.0",
|
|
39
|
+
app_version: "1.0.0",
|
|
40
|
+
language: "vi",
|
|
41
|
+
country: "VN",
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 2. Track Page View
|
|
46
|
+
|
|
47
|
+
```javascript
|
|
48
|
+
// Track khi user vào một trang
|
|
49
|
+
tracking.trackPageView("/home");
|
|
50
|
+
tracking.trackPageView("/detail/123");
|
|
51
|
+
tracking.trackPageView("/live/channel-1");
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 3. Track Custom Event
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
// Track event tuỳ ý với params
|
|
58
|
+
tracking.trackEvent("button_click", {
|
|
59
|
+
button_id: "subscribe_btn",
|
|
60
|
+
page_id: "detail",
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
tracking.trackEvent("video_play", {
|
|
64
|
+
video_id: "12345",
|
|
65
|
+
video_title: "Trận đấu hay nhất",
|
|
66
|
+
duration: 3600,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
tracking.trackEvent("search", {
|
|
70
|
+
keyword: "bóng đá",
|
|
71
|
+
results_count: 25,
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 4. Send Event trực tiếp
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
// Gửi event với full control payload
|
|
79
|
+
await tracking.sendEvent({
|
|
80
|
+
event_name: "custom_event",
|
|
81
|
+
custom_field_1: "value1",
|
|
82
|
+
custom_field_2: "value2",
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Tích hợp Next.js (Pages Router)
|
|
89
|
+
|
|
90
|
+
### Trong `_app.js` hoặc `_app.tsx`
|
|
91
|
+
|
|
92
|
+
```javascript
|
|
93
|
+
import { useEffect } from "react";
|
|
94
|
+
import { useRouter } from "next/router";
|
|
95
|
+
import tracking from "tracking-lib-ott";
|
|
96
|
+
|
|
97
|
+
function MyApp({ Component, pageProps }) {
|
|
98
|
+
const router = useRouter();
|
|
99
|
+
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
// Khởi tạo tracking
|
|
102
|
+
tracking.init({
|
|
103
|
+
baseURL: process.env.NEXT_PUBLIC_TRACKING_URL,
|
|
104
|
+
session_id: "session-id-from-somewhere",
|
|
105
|
+
session_sign: "session-sign",
|
|
106
|
+
user_id: 12345,
|
|
107
|
+
deviceId: "device-id",
|
|
108
|
+
deviceModel: navigator.userAgent,
|
|
109
|
+
deviceBrand: "Web Browser",
|
|
110
|
+
ip_address: "", // Lấy từ API hoặc để trống
|
|
111
|
+
debug: process.env.NODE_ENV === "development",
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Auto track page views
|
|
115
|
+
const cleanup = tracking.usePageViewTracking(router);
|
|
116
|
+
|
|
117
|
+
return cleanup;
|
|
118
|
+
}, [router]);
|
|
119
|
+
|
|
120
|
+
return <Component {...pageProps} />;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export default MyApp;
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Custom Page Mapping
|
|
129
|
+
|
|
130
|
+
Mặc định thư viện có sẵn mapping cho các page phổ biến. Bạn có thể tuỳ chỉnh:
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
tracking.init({
|
|
134
|
+
baseURL: "...",
|
|
135
|
+
// ... other config
|
|
136
|
+
|
|
137
|
+
pageMap: {
|
|
138
|
+
// Thêm hoặc ghi đè page mapping
|
|
139
|
+
phim: { name: "Phim", id: "movie" },
|
|
140
|
+
"the-thao": { name: "Thể thao", id: "sports" },
|
|
141
|
+
"tin-tuc": { name: "Tin tức", id: "news" },
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Page mapping mặc định:**
|
|
147
|
+
|
|
148
|
+
| URL Path | Page Name | Page ID |
|
|
149
|
+
| ----------- | --------- | -------- |
|
|
150
|
+
| `/` | Home | home |
|
|
151
|
+
| `/home` | Home | home |
|
|
152
|
+
| `/search` | Search | search |
|
|
153
|
+
| `/detail` | Detail | detail |
|
|
154
|
+
| `/player` | Player | player |
|
|
155
|
+
| `/video` | Video | video |
|
|
156
|
+
| `/live` | Live | live |
|
|
157
|
+
| `/category` | Category | category |
|
|
158
|
+
| `/profile` | Profile | profile |
|
|
159
|
+
| `/login` | Login | login |
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## API Reference
|
|
164
|
+
|
|
165
|
+
### `tracking.init(options)`
|
|
166
|
+
|
|
167
|
+
Khởi tạo thư viện với config.
|
|
168
|
+
|
|
169
|
+
| Option | Type | Required | Mô tả |
|
|
170
|
+
| -------------- | ------- | -------- | ---------------------------- |
|
|
171
|
+
| `baseURL` | string | ✅ | URL endpoint để gửi events |
|
|
172
|
+
| `session_id` | string | ✅ | Session ID của user |
|
|
173
|
+
| `session_sign` | string | ✅ | Session signature |
|
|
174
|
+
| `user_id` | number | ✅ | User ID (-1 nếu chưa login) |
|
|
175
|
+
| `deviceId` | string | ✅ | Device unique ID |
|
|
176
|
+
| `deviceModel` | string | ✅ | Tên model thiết bị |
|
|
177
|
+
| `deviceBrand` | string | ✅ | Hãng thiết bị |
|
|
178
|
+
| `ip_address` | string | ✅ | IP address |
|
|
179
|
+
| `debug` | boolean | ❌ | Bật/tắt debug logs |
|
|
180
|
+
| `appName` | string | ❌ | Tên app (default: `on_plus`) |
|
|
181
|
+
| `platform` | string | ❌ | Platform (default: `Web`) |
|
|
182
|
+
| `pageMap` | object | ❌ | Custom page mapping |
|
|
183
|
+
|
|
184
|
+
### `tracking.trackPageView(url)`
|
|
185
|
+
|
|
186
|
+
Track sự kiện xem trang.
|
|
187
|
+
|
|
188
|
+
```javascript
|
|
189
|
+
tracking.trackPageView("/detail/123?tab=info");
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### `tracking.trackEvent(eventName, params)`
|
|
193
|
+
|
|
194
|
+
Track custom event.
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
tracking.trackEvent("video_complete", { video_id: "123" });
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### `tracking.sendEvent(eventData)`
|
|
201
|
+
|
|
202
|
+
Gửi event trực tiếp với full payload.
|
|
203
|
+
|
|
204
|
+
```javascript
|
|
205
|
+
await tracking.sendEvent({
|
|
206
|
+
event_name: "my_event",
|
|
207
|
+
...customData,
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### `tracking.usePageViewTracking(router)`
|
|
212
|
+
|
|
213
|
+
Auto track page views cho Next.js Pages Router.
|
|
214
|
+
|
|
215
|
+
```javascript
|
|
216
|
+
const cleanup = tracking.usePageViewTracking(router);
|
|
217
|
+
// Gọi cleanup() khi unmount
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Event Payload Structure
|
|
223
|
+
|
|
224
|
+
Mỗi event được gửi lên server có cấu trúc:
|
|
225
|
+
|
|
226
|
+
```json
|
|
227
|
+
{
|
|
228
|
+
"app_name": "on_plus",
|
|
229
|
+
"app_id": "",
|
|
230
|
+
"package_id": "com.vtvcab.onsportstv",
|
|
231
|
+
"platform": "Web",
|
|
232
|
+
|
|
233
|
+
"session_id": "...",
|
|
234
|
+
"session_sign": "...",
|
|
235
|
+
"user_id": 12345,
|
|
236
|
+
|
|
237
|
+
"device_id": "...",
|
|
238
|
+
"device_model": "...",
|
|
239
|
+
"device_brand": "...",
|
|
240
|
+
"ip_address": "...",
|
|
241
|
+
|
|
242
|
+
"event_name": "page_view",
|
|
243
|
+
"page_name": "Home",
|
|
244
|
+
"page_id": "home",
|
|
245
|
+
"page_path": "/home",
|
|
246
|
+
"referrer_page_id": ""
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Debug Mode
|
|
253
|
+
|
|
254
|
+
Khi bật `debug: true`, thư viện sẽ log ra console:
|
|
255
|
+
|
|
256
|
+
- Config khi khởi tạo
|
|
257
|
+
- Mỗi event được gửi đi
|
|
258
|
+
- Lỗi nếu có
|
|
259
|
+
|
|
260
|
+
```javascript
|
|
261
|
+
tracking.init({
|
|
262
|
+
debug: process.env.NODE_ENV === "development",
|
|
263
|
+
// ...
|
|
264
|
+
});
|
|
265
|
+
```
|
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,173 @@ 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
|
+
event_name: "session_start",
|
|
216
|
+
event_time: sessionStartTime
|
|
217
|
+
});
|
|
218
|
+
log("Session started at:", new Date(sessionStartTime).toISOString());
|
|
219
|
+
startProgressTimer();
|
|
220
|
+
};
|
|
221
|
+
var sendSessionProgress = () => {
|
|
222
|
+
if (!isSessionActive) return;
|
|
223
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
224
|
+
const duration = now - sessionStartTime;
|
|
225
|
+
sendEvent({
|
|
226
|
+
event_name: "session_progress",
|
|
227
|
+
event_time: now,
|
|
228
|
+
session_duration: duration
|
|
229
|
+
});
|
|
230
|
+
log("Session progress - Duration:", Math.round(duration / 1e3), "seconds");
|
|
231
|
+
};
|
|
232
|
+
var sendSessionEnd = () => {
|
|
233
|
+
if (!isSessionActive) return;
|
|
234
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
235
|
+
const duration = now - sessionStartTime;
|
|
236
|
+
sendEvent({
|
|
237
|
+
event_name: "session_end",
|
|
238
|
+
event_time: now,
|
|
239
|
+
session_duration: duration
|
|
240
|
+
});
|
|
241
|
+
log(
|
|
242
|
+
"Session ended - Total duration:",
|
|
243
|
+
Math.round(duration / 1e3),
|
|
244
|
+
"seconds"
|
|
245
|
+
);
|
|
246
|
+
stopProgressTimer();
|
|
247
|
+
isSessionActive = false;
|
|
248
|
+
};
|
|
249
|
+
var startProgressTimer = () => {
|
|
250
|
+
stopProgressTimer();
|
|
251
|
+
if (sessionConfig.progressInterval && sessionConfig.progressInterval > 0) {
|
|
252
|
+
progressTimer = setInterval(() => {
|
|
253
|
+
if (document.visibilityState === "visible") {
|
|
254
|
+
sendSessionProgress();
|
|
255
|
+
}
|
|
256
|
+
}, sessionConfig.progressInterval);
|
|
257
|
+
log(
|
|
258
|
+
"Progress timer started, interval:",
|
|
259
|
+
sessionConfig.progressInterval,
|
|
260
|
+
"ms"
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
var stopProgressTimer = () => {
|
|
265
|
+
if (progressTimer) {
|
|
266
|
+
clearInterval(progressTimer);
|
|
267
|
+
progressTimer = null;
|
|
268
|
+
log("Progress timer stopped");
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
var handleVisibilityChange = () => {
|
|
272
|
+
if (document.visibilityState === "hidden") {
|
|
273
|
+
log("Tab hidden - pausing progress");
|
|
274
|
+
stopProgressTimer();
|
|
275
|
+
} else if (document.visibilityState === "visible") {
|
|
276
|
+
log("Tab visible - resuming progress");
|
|
277
|
+
if (isSessionActive) {
|
|
278
|
+
startProgressTimer();
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
var handleBeforeUnload = () => {
|
|
283
|
+
sendSessionEnd();
|
|
284
|
+
};
|
|
285
|
+
var handlePageHide = (event) => {
|
|
286
|
+
if (event.persisted) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
sendSessionEnd();
|
|
290
|
+
};
|
|
291
|
+
var initSessionTracking = (options = {}) => {
|
|
292
|
+
if (typeof window === "undefined") return;
|
|
293
|
+
sessionConfig = { ...defaultSessionConfig, ...options };
|
|
294
|
+
log("Initializing with config:", sessionConfig);
|
|
295
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
296
|
+
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
297
|
+
window.addEventListener("pagehide", handlePageHide);
|
|
298
|
+
if (sessionConfig.autoStart) {
|
|
299
|
+
sendSessionStart();
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
var cleanupSessionTracking = () => {
|
|
303
|
+
if (typeof window === "undefined") return;
|
|
304
|
+
log("Cleaning up...");
|
|
305
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
306
|
+
window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
307
|
+
window.removeEventListener("pagehide", handlePageHide);
|
|
308
|
+
stopProgressTimer();
|
|
309
|
+
isSessionActive = false;
|
|
310
|
+
};
|
|
311
|
+
var updateSessionConfig = (options) => {
|
|
312
|
+
sessionConfig = { ...sessionConfig, ...options };
|
|
313
|
+
if (options.progressInterval !== void 0 && isSessionActive) {
|
|
314
|
+
startProgressTimer();
|
|
315
|
+
}
|
|
316
|
+
log("Config updated:", sessionConfig);
|
|
317
|
+
};
|
|
318
|
+
var isSessionStarted = () => isSessionActive;
|
|
319
|
+
var getSessionDuration = () => {
|
|
320
|
+
if (!isSessionActive) return 0;
|
|
321
|
+
return Date.now() - sessionStartTime;
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
// src/index.ts
|
|
175
325
|
var tracking = {
|
|
176
326
|
init: initTracking,
|
|
177
327
|
trackPageView,
|
|
178
328
|
trackEvent,
|
|
179
329
|
sendEvent,
|
|
180
330
|
getPageInfo,
|
|
181
|
-
usePageViewTracking
|
|
331
|
+
usePageViewTracking,
|
|
332
|
+
// Session lifecycle
|
|
333
|
+
initSession: initSessionTracking,
|
|
334
|
+
cleanupSession: cleanupSessionTracking,
|
|
335
|
+
sessionStart: sendSessionStart,
|
|
336
|
+
sessionProgress: sendSessionProgress,
|
|
337
|
+
sessionEnd: sendSessionEnd
|
|
182
338
|
};
|
|
183
339
|
var index_default = tracking;
|
|
184
340
|
// Annotate the CommonJS export names for ESM import in node:
|
|
185
341
|
0 && (module.exports = {
|
|
342
|
+
cleanupSessionTracking,
|
|
343
|
+
defaultConfig,
|
|
344
|
+
defaultPageMap,
|
|
186
345
|
getPageInfo,
|
|
346
|
+
getSessionDuration,
|
|
347
|
+
initSessionTracking,
|
|
187
348
|
initTracking,
|
|
349
|
+
isSessionStarted,
|
|
188
350
|
sendEvent,
|
|
351
|
+
sendSessionEnd,
|
|
352
|
+
sendSessionProgress,
|
|
353
|
+
sendSessionStart,
|
|
189
354
|
trackEvent,
|
|
190
355
|
trackPageView,
|
|
356
|
+
updateSessionConfig,
|
|
191
357
|
usePageViewTracking
|
|
192
358
|
});
|
|
193
359
|
//# 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 event_name: \"session_start\",\r\n event_time: sessionStartTime,\r\n });\r\n\r\n log(\"Session started at:\", new Date(sessionStartTime).toISOString());\r\n\r\n // Bắt đầu timer cho session_progress\r\n startProgressTimer();\r\n};\r\n\r\n/**\r\n * Gửi event session_progress\r\n */\r\nexport const sendSessionProgress = () => {\r\n if (!isSessionActive) return;\r\n\r\n const now = Math.floor(Date.now() / 1000);\r\n const duration = now - sessionStartTime;\r\n\r\n sendEvent({\r\n event_name: \"session_progress\",\r\n event_time: now,\r\n session_duration: duration,\r\n });\r\n\r\n log(\"Session progress - Duration:\", Math.round(duration / 1000), \"seconds\");\r\n};\r\n\r\n/**\r\n * Gửi event session_end\r\n */\r\nexport const sendSessionEnd = () => {\r\n if (!isSessionActive) return;\r\n\r\n const now = Math.floor(Date.now() / 1000);\r\n const duration = now - sessionStartTime;\r\n\r\n // Dùng sendBeacon để đảm bảo gửi được khi page unload\r\n // Fallback về sendEvent nếu không support\r\n sendEvent({\r\n event_name: \"session_end\",\r\n event_time: now,\r\n session_duration: duration,\r\n });\r\n\r\n log(\r\n \"Session ended - Total duration:\",\r\n Math.round(duration / 1000),\r\n \"seconds\",\r\n );\r\n\r\n stopProgressTimer();\r\n isSessionActive = false;\r\n};\r\n\r\n/**\r\n * Start progress timer\r\n */\r\nconst startProgressTimer = () => {\r\n stopProgressTimer(); // Clear existing timer\r\n\r\n if (sessionConfig.progressInterval && sessionConfig.progressInterval > 0) {\r\n progressTimer = setInterval(() => {\r\n if (document.visibilityState === \"visible\") {\r\n sendSessionProgress();\r\n }\r\n }, sessionConfig.progressInterval);\r\n\r\n log(\r\n \"Progress timer started, interval:\",\r\n sessionConfig.progressInterval,\r\n \"ms\",\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Stop progress timer\r\n */\r\nconst stopProgressTimer = () => {\r\n if (progressTimer) {\r\n clearInterval(progressTimer);\r\n progressTimer = null;\r\n log(\"Progress timer stopped\");\r\n }\r\n};\r\n\r\n/**\r\n * Handle visibility change\r\n * Pause/resume tracking khi user switch tab hoặc minimize\r\n */\r\nconst handleVisibilityChange = () => {\r\n if (document.visibilityState === \"hidden\") {\r\n // User rời khỏi tab - có thể gửi session_progress cuối\r\n log(\"Tab hidden - pausing progress\");\r\n stopProgressTimer();\r\n } else if (document.visibilityState === \"visible\") {\r\n // User quay lại tab - resume timer\r\n log(\"Tab visible - resuming progress\");\r\n if (isSessionActive) {\r\n startProgressTimer();\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Handle page unload (user đóng tab/browser)\r\n */\r\nconst handleBeforeUnload = () => {\r\n sendSessionEnd();\r\n};\r\n\r\n/**\r\n * Handle page hide (mobile: switch app, close tab)\r\n * Dùng pagehide thay vì beforeunload cho mobile\r\n */\r\nconst handlePageHide = (event: PageTransitionEvent) => {\r\n if (event.persisted) {\r\n // Page được cache (bfcache) - không phải thực sự đóng\r\n return;\r\n }\r\n sendSessionEnd();\r\n};\r\n\r\n/**\r\n * Khởi tạo session tracking\r\n */\r\nexport const initSessionTracking = (options: SessionConfig = {}) => {\r\n if (typeof window === \"undefined\") return;\r\n\r\n sessionConfig = { ...defaultSessionConfig, ...options };\r\n\r\n log(\"Initializing with config:\", sessionConfig);\r\n\r\n // Đăng ký event listeners\r\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\r\n window.addEventListener(\"beforeunload\", handleBeforeUnload);\r\n window.addEventListener(\"pagehide\", handlePageHide);\r\n\r\n // Auto start session\r\n if (sessionConfig.autoStart) {\r\n sendSessionStart();\r\n }\r\n};\r\n\r\n/**\r\n * Cleanup - gọi khi unmount component\r\n */\r\nexport const cleanupSessionTracking = () => {\r\n if (typeof window === \"undefined\") return;\r\n\r\n log(\"Cleaning up...\");\r\n\r\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\r\n window.removeEventListener(\"beforeunload\", handleBeforeUnload);\r\n window.removeEventListener(\"pagehide\", handlePageHide);\r\n\r\n stopProgressTimer();\r\n isSessionActive = false;\r\n};\r\n\r\n/**\r\n * Cập nhật config (ví dụ: thay đổi interval)\r\n */\r\nexport const updateSessionConfig = (options: Partial<SessionConfig>) => {\r\n sessionConfig = { ...sessionConfig, ...options };\r\n\r\n // Restart timer nếu interval thay đổi\r\n if (options.progressInterval !== undefined && isSessionActive) {\r\n startProgressTimer();\r\n }\r\n\r\n log(\"Config updated:\", sessionConfig);\r\n};\r\n\r\n/**\r\n * Kiểm tra session đang active không\r\n */\r\nexport const isSessionStarted = () => isSessionActive;\r\n\r\n/**\r\n * Lấy thời gian session hiện tại (ms)\r\n */\r\nexport const getSessionDuration = () => {\r\n if (!isSessionActive) return 0;\r\n return Date.now() - sessionStartTime;\r\n};\r\n"],"mappings":"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","names":[]}
|
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,173 @@ 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
|
+
event_name: "session_start",
|
|
174
|
+
event_time: sessionStartTime
|
|
175
|
+
});
|
|
176
|
+
log("Session started at:", new Date(sessionStartTime).toISOString());
|
|
177
|
+
startProgressTimer();
|
|
178
|
+
};
|
|
179
|
+
var sendSessionProgress = () => {
|
|
180
|
+
if (!isSessionActive) return;
|
|
181
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
182
|
+
const duration = now - sessionStartTime;
|
|
183
|
+
sendEvent({
|
|
184
|
+
event_name: "session_progress",
|
|
185
|
+
event_time: now,
|
|
186
|
+
session_duration: duration
|
|
187
|
+
});
|
|
188
|
+
log("Session progress - Duration:", Math.round(duration / 1e3), "seconds");
|
|
189
|
+
};
|
|
190
|
+
var sendSessionEnd = () => {
|
|
191
|
+
if (!isSessionActive) return;
|
|
192
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
193
|
+
const duration = now - sessionStartTime;
|
|
194
|
+
sendEvent({
|
|
195
|
+
event_name: "session_end",
|
|
196
|
+
event_time: now,
|
|
197
|
+
session_duration: duration
|
|
198
|
+
});
|
|
199
|
+
log(
|
|
200
|
+
"Session ended - Total duration:",
|
|
201
|
+
Math.round(duration / 1e3),
|
|
202
|
+
"seconds"
|
|
203
|
+
);
|
|
204
|
+
stopProgressTimer();
|
|
205
|
+
isSessionActive = false;
|
|
206
|
+
};
|
|
207
|
+
var startProgressTimer = () => {
|
|
208
|
+
stopProgressTimer();
|
|
209
|
+
if (sessionConfig.progressInterval && sessionConfig.progressInterval > 0) {
|
|
210
|
+
progressTimer = setInterval(() => {
|
|
211
|
+
if (document.visibilityState === "visible") {
|
|
212
|
+
sendSessionProgress();
|
|
213
|
+
}
|
|
214
|
+
}, sessionConfig.progressInterval);
|
|
215
|
+
log(
|
|
216
|
+
"Progress timer started, interval:",
|
|
217
|
+
sessionConfig.progressInterval,
|
|
218
|
+
"ms"
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
var stopProgressTimer = () => {
|
|
223
|
+
if (progressTimer) {
|
|
224
|
+
clearInterval(progressTimer);
|
|
225
|
+
progressTimer = null;
|
|
226
|
+
log("Progress timer stopped");
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
var handleVisibilityChange = () => {
|
|
230
|
+
if (document.visibilityState === "hidden") {
|
|
231
|
+
log("Tab hidden - pausing progress");
|
|
232
|
+
stopProgressTimer();
|
|
233
|
+
} else if (document.visibilityState === "visible") {
|
|
234
|
+
log("Tab visible - resuming progress");
|
|
235
|
+
if (isSessionActive) {
|
|
236
|
+
startProgressTimer();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
var handleBeforeUnload = () => {
|
|
241
|
+
sendSessionEnd();
|
|
242
|
+
};
|
|
243
|
+
var handlePageHide = (event) => {
|
|
244
|
+
if (event.persisted) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
sendSessionEnd();
|
|
248
|
+
};
|
|
249
|
+
var initSessionTracking = (options = {}) => {
|
|
250
|
+
if (typeof window === "undefined") return;
|
|
251
|
+
sessionConfig = { ...defaultSessionConfig, ...options };
|
|
252
|
+
log("Initializing with config:", sessionConfig);
|
|
253
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
254
|
+
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
255
|
+
window.addEventListener("pagehide", handlePageHide);
|
|
256
|
+
if (sessionConfig.autoStart) {
|
|
257
|
+
sendSessionStart();
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
var cleanupSessionTracking = () => {
|
|
261
|
+
if (typeof window === "undefined") return;
|
|
262
|
+
log("Cleaning up...");
|
|
263
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
264
|
+
window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
265
|
+
window.removeEventListener("pagehide", handlePageHide);
|
|
266
|
+
stopProgressTimer();
|
|
267
|
+
isSessionActive = false;
|
|
268
|
+
};
|
|
269
|
+
var updateSessionConfig = (options) => {
|
|
270
|
+
sessionConfig = { ...sessionConfig, ...options };
|
|
271
|
+
if (options.progressInterval !== void 0 && isSessionActive) {
|
|
272
|
+
startProgressTimer();
|
|
273
|
+
}
|
|
274
|
+
log("Config updated:", sessionConfig);
|
|
275
|
+
};
|
|
276
|
+
var isSessionStarted = () => isSessionActive;
|
|
277
|
+
var getSessionDuration = () => {
|
|
278
|
+
if (!isSessionActive) return 0;
|
|
279
|
+
return Date.now() - sessionStartTime;
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
// src/index.ts
|
|
145
283
|
var tracking = {
|
|
146
284
|
init: initTracking,
|
|
147
285
|
trackPageView,
|
|
148
286
|
trackEvent,
|
|
149
287
|
sendEvent,
|
|
150
288
|
getPageInfo,
|
|
151
|
-
usePageViewTracking
|
|
289
|
+
usePageViewTracking,
|
|
290
|
+
// Session lifecycle
|
|
291
|
+
initSession: initSessionTracking,
|
|
292
|
+
cleanupSession: cleanupSessionTracking,
|
|
293
|
+
sessionStart: sendSessionStart,
|
|
294
|
+
sessionProgress: sendSessionProgress,
|
|
295
|
+
sessionEnd: sendSessionEnd
|
|
152
296
|
};
|
|
153
297
|
var index_default = tracking;
|
|
154
298
|
export {
|
|
299
|
+
cleanupSessionTracking,
|
|
155
300
|
index_default as default,
|
|
301
|
+
defaultConfig,
|
|
302
|
+
defaultPageMap,
|
|
156
303
|
getPageInfo,
|
|
304
|
+
getSessionDuration,
|
|
305
|
+
initSessionTracking,
|
|
157
306
|
initTracking,
|
|
307
|
+
isSessionStarted,
|
|
158
308
|
sendEvent,
|
|
309
|
+
sendSessionEnd,
|
|
310
|
+
sendSessionProgress,
|
|
311
|
+
sendSessionStart,
|
|
159
312
|
trackEvent,
|
|
160
313
|
trackPageView,
|
|
314
|
+
updateSessionConfig,
|
|
161
315
|
usePageViewTracking
|
|
162
316
|
};
|
|
163
317
|
//# 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 event_name: \"session_start\",\r\n event_time: sessionStartTime,\r\n });\r\n\r\n log(\"Session started at:\", new Date(sessionStartTime).toISOString());\r\n\r\n // Bắt đầu timer cho session_progress\r\n startProgressTimer();\r\n};\r\n\r\n/**\r\n * Gửi event session_progress\r\n */\r\nexport const sendSessionProgress = () => {\r\n if (!isSessionActive) return;\r\n\r\n const now = Math.floor(Date.now() / 1000);\r\n const duration = now - sessionStartTime;\r\n\r\n sendEvent({\r\n event_name: \"session_progress\",\r\n event_time: now,\r\n session_duration: duration,\r\n });\r\n\r\n log(\"Session progress - Duration:\", Math.round(duration / 1000), \"seconds\");\r\n};\r\n\r\n/**\r\n * Gửi event session_end\r\n */\r\nexport const sendSessionEnd = () => {\r\n if (!isSessionActive) return;\r\n\r\n const now = Math.floor(Date.now() / 1000);\r\n const duration = now - sessionStartTime;\r\n\r\n // Dùng sendBeacon để đảm bảo gửi được khi page unload\r\n // Fallback về sendEvent nếu không support\r\n sendEvent({\r\n event_name: \"session_end\",\r\n event_time: now,\r\n session_duration: duration,\r\n });\r\n\r\n log(\r\n \"Session ended - Total duration:\",\r\n Math.round(duration / 1000),\r\n \"seconds\",\r\n );\r\n\r\n stopProgressTimer();\r\n isSessionActive = false;\r\n};\r\n\r\n/**\r\n * Start progress timer\r\n */\r\nconst startProgressTimer = () => {\r\n stopProgressTimer(); // Clear existing timer\r\n\r\n if (sessionConfig.progressInterval && sessionConfig.progressInterval > 0) {\r\n progressTimer = setInterval(() => {\r\n if (document.visibilityState === \"visible\") {\r\n sendSessionProgress();\r\n }\r\n }, sessionConfig.progressInterval);\r\n\r\n log(\r\n \"Progress timer started, interval:\",\r\n sessionConfig.progressInterval,\r\n \"ms\",\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Stop progress timer\r\n */\r\nconst stopProgressTimer = () => {\r\n if (progressTimer) {\r\n clearInterval(progressTimer);\r\n progressTimer = null;\r\n log(\"Progress timer stopped\");\r\n }\r\n};\r\n\r\n/**\r\n * Handle visibility change\r\n * Pause/resume tracking khi user switch tab hoặc minimize\r\n */\r\nconst handleVisibilityChange = () => {\r\n if (document.visibilityState === \"hidden\") {\r\n // User rời khỏi tab - có thể gửi session_progress cuối\r\n log(\"Tab hidden - pausing progress\");\r\n stopProgressTimer();\r\n } else if (document.visibilityState === \"visible\") {\r\n // User quay lại tab - resume timer\r\n log(\"Tab visible - resuming progress\");\r\n if (isSessionActive) {\r\n startProgressTimer();\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Handle page unload (user đóng tab/browser)\r\n */\r\nconst handleBeforeUnload = () => {\r\n sendSessionEnd();\r\n};\r\n\r\n/**\r\n * Handle page hide (mobile: switch app, close tab)\r\n * Dùng pagehide thay vì beforeunload cho mobile\r\n */\r\nconst handlePageHide = (event: PageTransitionEvent) => {\r\n if (event.persisted) {\r\n // Page được cache (bfcache) - không phải thực sự đóng\r\n return;\r\n }\r\n sendSessionEnd();\r\n};\r\n\r\n/**\r\n * Khởi tạo session tracking\r\n */\r\nexport const initSessionTracking = (options: SessionConfig = {}) => {\r\n if (typeof window === \"undefined\") return;\r\n\r\n sessionConfig = { ...defaultSessionConfig, ...options };\r\n\r\n log(\"Initializing with config:\", sessionConfig);\r\n\r\n // Đăng ký event listeners\r\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\r\n window.addEventListener(\"beforeunload\", handleBeforeUnload);\r\n window.addEventListener(\"pagehide\", handlePageHide);\r\n\r\n // Auto start session\r\n if (sessionConfig.autoStart) {\r\n sendSessionStart();\r\n }\r\n};\r\n\r\n/**\r\n * Cleanup - gọi khi unmount component\r\n */\r\nexport const cleanupSessionTracking = () => {\r\n if (typeof window === \"undefined\") return;\r\n\r\n log(\"Cleaning up...\");\r\n\r\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\r\n window.removeEventListener(\"beforeunload\", handleBeforeUnload);\r\n window.removeEventListener(\"pagehide\", handlePageHide);\r\n\r\n stopProgressTimer();\r\n isSessionActive = false;\r\n};\r\n\r\n/**\r\n * Cập nhật config (ví dụ: thay đổi interval)\r\n */\r\nexport const updateSessionConfig = (options: Partial<SessionConfig>) => {\r\n sessionConfig = { ...sessionConfig, ...options };\r\n\r\n // Restart timer nếu interval thay đổi\r\n if (options.progressInterval !== undefined && isSessionActive) {\r\n startProgressTimer();\r\n }\r\n\r\n log(\"Config updated:\", sessionConfig);\r\n};\r\n\r\n/**\r\n * Kiểm tra session đang active không\r\n */\r\nexport const isSessionStarted = () => isSessionActive;\r\n\r\n/**\r\n * Lấy thời gian session hiện tại (ms)\r\n */\r\nexport const getSessionDuration = () => {\r\n if (!isSessionActive) return 0;\r\n return Date.now() - sessionStartTime;\r\n};\r\n","/**\r\n * Tracking Library v2 (Fetch + TypeScript)\r\n * Thư viện tracking events có thể dùng cho nhiều project\r\n */\r\n\r\n// Re-export types\r\nexport type {\r\n TrackingConfig,\r\n PageInfo,\r\n PageMap,\r\n EventData,\r\n NextRouterLike,\r\n} from \"./types\";\r\n\r\nexport type { SessionConfig } from \"./session\";\r\n\r\n// Re-export constants\r\nexport { defaultConfig, defaultPageMap } from \"./constants\";\r\n\r\n// Re-export functions\r\nexport {\r\n initTracking,\r\n getPageInfo,\r\n sendEvent,\r\n trackPageView,\r\n trackEvent,\r\n usePageViewTracking,\r\n} from \"./tracking\";\r\n\r\n// Re-export session functions\r\nexport {\r\n initSessionTracking,\r\n cleanupSessionTracking,\r\n sendSessionStart,\r\n sendSessionProgress,\r\n sendSessionEnd,\r\n updateSessionConfig,\r\n isSessionStarted,\r\n getSessionDuration,\r\n} from \"./session\";\r\n\r\n// Import for default export\r\nimport {\r\n initTracking,\r\n getPageInfo,\r\n sendEvent,\r\n trackPageView,\r\n trackEvent,\r\n usePageViewTracking,\r\n} from \"./tracking\";\r\n\r\nimport {\r\n initSessionTracking,\r\n cleanupSessionTracking,\r\n sendSessionStart,\r\n sendSessionProgress,\r\n sendSessionEnd,\r\n} from \"./session\";\r\n\r\n// Export default object\r\nconst tracking = {\r\n init: initTracking,\r\n trackPageView,\r\n trackEvent,\r\n sendEvent,\r\n getPageInfo,\r\n usePageViewTracking,\r\n // Session lifecycle\r\n initSession: initSessionTracking,\r\n cleanupSession: cleanupSessionTracking,\r\n sessionStart: sendSessionStart,\r\n sessionProgress: sendSessionProgress,\r\n sessionEnd: sendSessionEnd,\r\n};\r\n\r\nexport default tracking;\r\n"],"mappings":"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","names":[]}
|