blog-tracking-sdk 1.0.0

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 ADDED
@@ -0,0 +1,134 @@
1
+ # blog-tracking-sdk
2
+
3
+ Blog页面埋点信息 SDK (单页面临时方案)。
4
+ 支持自动采集:PageView, 15s有效停留, 滚动深度监听, 切后台/页面关闭上报。
5
+
6
+ ## 特性
7
+
8
+ - **框架无关核心**: `SessionManager` 统一管理会话生命周期。
9
+ - **多框架适配**: 提供 React Hook 和 Vue Composable 开箱即用。
10
+ - **自动采集**: 自动处理 `referrer`, `device_type`, `os`, `browser` 等环境信息。
11
+ - **网络解耦**: 不依赖特定 HTTP 库,由业务方传入 `requestFn`。
12
+
13
+ ## 埋点字段说明 (Data Dictionary)
14
+
15
+ SDK 会自动采集以下信息,并将其传递给您配置的 `requestFn`。
16
+
17
+ | 字段 (Field) | 类型 | 必填 | 说明 (Description) | 示例值 | 获取方式 |
18
+ | :--- | :--- | :--- | :--- | :--- | :--- |
19
+ | **event_name** | `string` | **Y** | 事件类型标识。`page_view`=进入页面; `page_stay_end`=离开页面 | `page_view` | SDK 自动生成 |
20
+ | **page_url** | `string` | **Y** | 当前页面的完整 URL 地址 | `https://site.com/news/123` | `window.location.href` |
21
+ | **page_path** | `string` | N | URL 的路径部分,用于聚合分析 | `/news/123` | `window.location.pathname` |
22
+ | **page_title** | `string` | N | 页面标题。优先取传入值,否则取文档标题 | `震惊!某男子竟然...` | `detail.title` \|\| `document.title` |
23
+ | **page_type** | `string` | **Y** | 页面/业务类型,目前固定为详情页 | `news_detail` | SDK 固定值 |
24
+ | **page_id** | `string` | **Y** | 业务对象 ID (如文章ID、商品ID) | `10086` | 参数传入 `id` |
25
+ | **referrer** | `string` | N | 来源页面 URL (上一跳地址) | `https://google.com` | `document.referrer` |
26
+ | **source** | `string` | **Y** | 流量来源渠道。优先级:UTM参数 > Referrer判断 > Direct | `search`, `social`, `direct` | SDK 算法自动推导 |
27
+ | **device_type** | `string` | N | 设备类型,区分移动端/桌面端 | `mobile`, `desktop` | UserAgent 正则匹配 |
28
+ | **os** | `string` | N | 操作系统名称 | `iOS`, `Android`, `Windows` | UserAgent 正则匹配 |
29
+ | **browser** | `string` | N | 浏览器名称 | `Chrome`, `Safari` | UserAgent 正则匹配 |
30
+ | **screen_width** | `number` | N | 屏幕物理宽度 (px) | `390` | `window.screen.width` |
31
+ | **stay_duration** | `number` | N | 页面停留时长 (秒)。仅 `page_stay_end` 有该值 | `150` | SDK 内部计时 |
32
+ | **end_reason** | `string` | N | 会话结束原因。`jump`=跳转; `close`=关闭; `background`=切后台 | `close` | SDK 监听事件判断 |
33
+ | **is_effective** | `number` | N | 是否有效阅读。1=有效(>15s或深滚动), 2=无效 | `1` | SDK 行为判断 |
34
+
35
+ ## 安装
36
+
37
+ ```bash
38
+ npm install blog-tracking-sdk
39
+ # 或
40
+ yarn add blog-tracking-sdk
41
+ ```
42
+
43
+ ## 快速上手
44
+
45
+ ### 1. 全局初始化 (必须)
46
+
47
+ 在项目入口文件(如 `main.ts`, `App.tsx`)中初始化单例。
48
+
49
+ ```typescript
50
+ import { BlogTracking } from 'blog-tracking-sdk';
51
+ import axios from 'axios';
52
+
53
+ // 初始化 SDK,传入发送请求的函数
54
+ BlogTracking.getInstance(async (payload) => {
55
+ // 这里可以使用 axios, fetch, 或者 navigator.sendBeacon
56
+ // payload 包含 event_name, page_url 等所有埋点数据
57
+
58
+ if (payload.event_name === 'page_stay_end' && payload.end_reason === 'close') {
59
+ // 页面关闭时建议使用 sendBeacon
60
+ const blob = new Blob([JSON.stringify(payload)], { type: 'application/json' });
61
+ navigator.sendBeacon('/api/pv/event/save', blob);
62
+ } else {
63
+ return axios.post('/api/pv/event/save', payload);
64
+ }
65
+ });
66
+ ```
67
+
68
+ ### 2. React 项目使用
69
+
70
+ 在新闻详情页组件中使用。
71
+
72
+ ```tsx
73
+ import { useNewsTracking } from 'blog-tracking-sdk/react';
74
+
75
+ const NewsDetail = ({ detail }) => {
76
+ // 传入埋点详情对象
77
+ // SDK 会自动处理 start/stop/scroll/visibility 逻辑
78
+ useNewsTracking({
79
+ id: detail.id,
80
+ title: detail.title,
81
+ });
82
+
83
+ return (
84
+ <div>
85
+ <h1>{detail.title}</h1>
86
+ {/* ... */}
87
+ </div>
88
+ );
89
+ };
90
+ ```
91
+
92
+ ### 3. Vue 项目使用
93
+
94
+ 在 Setup 中使用,支持响应式 Ref。
95
+
96
+ ```vue
97
+ <script setup lang="ts">
98
+ import { toRefs } from 'vue';
99
+ import { useNewsTracking } from 'blog-tracking-sdk/vue';
100
+
101
+ const props = defineProps<{ detail: any }>();
102
+ const { detail } = toRefs(props);
103
+
104
+ // 传入 Ref,当 detail 变化时会自动重启新的会话
105
+ useNewsTracking(detail);
106
+ </script>
107
+ ```
108
+
109
+ ## NPM 发布指南
110
+
111
+ 如果您拥有本包的源码并希望发布新版本:
112
+
113
+ 1. **登录 NPM (仅需一次)**
114
+ ```bash
115
+ npm login
116
+ # 确保源是 https://registry.npmjs.org/
117
+ ```
118
+
119
+ 2. **构建**
120
+ ```bash
121
+ npm run build
122
+ ```
123
+
124
+ 3. **更新版本**
125
+ ```bash
126
+ npm version patch # 0.0.1 -> 0.0.2
127
+ # npm version minor # 0.1.0
128
+ # npm version major # 1.0.0
129
+ ```
130
+
131
+ 4. **发布**
132
+ ```bash
133
+ npm publish --access public
134
+ ```
package/dist/index.cjs ADDED
@@ -0,0 +1,244 @@
1
+ 'use strict';
2
+
3
+ // src/core/types.ts
4
+ var TrackingSource = /* @__PURE__ */ ((TrackingSource2) => {
5
+ TrackingSource2["Search"] = "search";
6
+ TrackingSource2["Social"] = "social";
7
+ TrackingSource2["Direct"] = "direct";
8
+ TrackingSource2["Referral"] = "referral";
9
+ TrackingSource2["AppBanner"] = "app_banner";
10
+ TrackingSource2["AppTie"] = "app_tie";
11
+ TrackingSource2["Edm"] = "edm";
12
+ TrackingSource2["GuanPhone"] = "guan_phone";
13
+ TrackingSource2["GuanBanner"] = "guan_banner";
14
+ TrackingSource2["AppAd"] = "app_ad";
15
+ return TrackingSource2;
16
+ })(TrackingSource || {});
17
+
18
+ // src/core/Tracker.ts
19
+ var Tracker = class _Tracker {
20
+ constructor(requestFn) {
21
+ this.deviceStorageKey = "BLOG_TRACKING_DEVICE_ID";
22
+ this.requestFn = requestFn;
23
+ this.deviceId = this.getOrInitDeviceId();
24
+ }
25
+ static getInstance(requestFn) {
26
+ if (!_Tracker.instance) {
27
+ if (!requestFn) {
28
+ throw new Error("Tracker needs requestFn for the first initialization");
29
+ }
30
+ _Tracker.instance = new _Tracker(requestFn);
31
+ }
32
+ return _Tracker.instance;
33
+ }
34
+ getOrInitDeviceId() {
35
+ try {
36
+ let id = localStorage.getItem(this.deviceStorageKey);
37
+ if (!id) {
38
+ id = this.generateUUID();
39
+ localStorage.setItem(this.deviceStorageKey, id);
40
+ }
41
+ return id;
42
+ } catch (e) {
43
+ console.warn("[BlogTracking] localStorage unavailable, using temporary ID");
44
+ return this.generateUUID();
45
+ }
46
+ }
47
+ generateUUID() {
48
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
49
+ const r = Math.random() * 16 | 0;
50
+ const v = c === "x" ? r : r & 3 | 8;
51
+ return v.toString(16);
52
+ });
53
+ }
54
+ getSource() {
55
+ if (typeof window === "undefined") return "direct" /* Direct */;
56
+ const urlParams = new URLSearchParams(window.location.search);
57
+ const utmSource = urlParams.get("utm_source");
58
+ if (utmSource) return utmSource;
59
+ const referrer = document.referrer || "";
60
+ const referrerLower = referrer.toLowerCase();
61
+ if (referrerLower.includes("google") || referrerLower.includes("bing")) {
62
+ return "search" /* Search */;
63
+ }
64
+ const socialKeywords = ["twitter", "t.co", "x.com", "instagram", "facebook", "discord", "reddit", "tiktok"];
65
+ if (socialKeywords.some((k) => referrerLower.includes(k))) {
66
+ return "social" /* Social */;
67
+ }
68
+ if (!referrer) return "direct" /* Direct */;
69
+ return "referral" /* Referral */;
70
+ }
71
+ validate(data) {
72
+ const requiredFields = [
73
+ "event_name",
74
+ "page_url",
75
+ "device_id",
76
+ "page_type",
77
+ "page_id",
78
+ "source"
79
+ ];
80
+ for (const field of requiredFields) {
81
+ if (!data[field]) {
82
+ console.warn(`[BlogTracking] Missing field: ${field}`, data);
83
+ return false;
84
+ }
85
+ }
86
+ if (data.event_name === "page_stay_end") {
87
+ if (data.stay_duration === void 0 || data.is_effective === void 0 || !data.end_reason) {
88
+ console.warn(`[BlogTracking] page_stay_end missing fields`, data);
89
+ return false;
90
+ }
91
+ }
92
+ return true;
93
+ }
94
+ getDeviceType() {
95
+ if (typeof navigator === "undefined") return "desktop";
96
+ const ua = navigator.userAgent;
97
+ const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(ua);
98
+ return isMobile ? "mobile" : "desktop";
99
+ }
100
+ getOS() {
101
+ if (typeof navigator === "undefined") return "Unknown";
102
+ const ua = navigator.userAgent;
103
+ if (/iPad|iPhone|iPod/.test(ua)) return "iOS";
104
+ if (/Android/.test(ua)) return "Android";
105
+ if (/Mac/.test(ua)) return "Mac";
106
+ if (/Win/.test(ua)) return "Windows";
107
+ if (/Linux/.test(ua)) return "Linux";
108
+ return "Unknown";
109
+ }
110
+ getBrowser() {
111
+ if (typeof navigator === "undefined") return "Unknown";
112
+ const ua = navigator.userAgent;
113
+ if (ua.indexOf("Chrome") > -1 && ua.indexOf("Safari") > -1 && ua.indexOf("Edg") === -1) return "Chrome";
114
+ if (ua.indexOf("Safari") > -1 && ua.indexOf("Chrome") === -1) return "Safari";
115
+ if (ua.indexOf("Firefox") > -1) return "Firefox";
116
+ if (ua.indexOf("Edg") > -1) return "Edge";
117
+ return "Unknown";
118
+ }
119
+ async report(event, payload) {
120
+ try {
121
+ const fullData = {
122
+ ...payload,
123
+ event_name: event,
124
+ device_id: this.deviceId,
125
+ source: payload.source || this.getSource(),
126
+ page_url: payload.page_url || (typeof window !== "undefined" ? window.location.href : ""),
127
+ referrer: payload.referrer || (typeof document !== "undefined" ? document.referrer : ""),
128
+ page_path: payload.page_path || (typeof window !== "undefined" ? window.location.pathname : ""),
129
+ page_title: payload.page_title || (typeof document !== "undefined" ? document.title : ""),
130
+ device_type: payload.device_type || this.getDeviceType(),
131
+ os: payload.os || this.getOS(),
132
+ browser: payload.browser || this.getBrowser(),
133
+ screen_width: payload.screen_width || (typeof window !== "undefined" ? window.screen.width : 0)
134
+ };
135
+ if (this.validate(fullData)) {
136
+ await this.requestFn(fullData);
137
+ }
138
+ } catch (e) {
139
+ console.error("[BlogTracking] Report failed", e);
140
+ }
141
+ }
142
+ };
143
+
144
+ // src/core/SessionManager.ts
145
+ var SessionManager = class {
146
+ constructor(detail) {
147
+ this.startTime = 0;
148
+ this.isEffective = false;
149
+ this.hasReportedEffective = false;
150
+ this.hasReportedEnd = false;
151
+ this.timeTimer = null;
152
+ this.detail = detail;
153
+ this.handleInteraction = this.triggerEffective.bind(this);
154
+ this.handleScroll = () => {
155
+ if (this.isEffective) return;
156
+ const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
157
+ const scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
158
+ const clientHeight = document.documentElement.clientHeight || document.body.clientHeight;
159
+ const scrollPercent = (scrollTop + clientHeight) / scrollHeight;
160
+ if (scrollPercent > 0.3) {
161
+ this.triggerEffective();
162
+ }
163
+ };
164
+ this.handleVisibilityChange = () => {
165
+ if (document.visibilityState === "hidden") {
166
+ this.reportEvent("background");
167
+ }
168
+ };
169
+ this.handlePageHide = () => {
170
+ this.reportEvent("close");
171
+ };
172
+ }
173
+ start() {
174
+ this.startTime = Date.now();
175
+ this.isEffective = false;
176
+ this.hasReportedEffective = false;
177
+ this.hasReportedEnd = false;
178
+ Tracker.getInstance().report("page_view", {
179
+ page_url: window.location.href,
180
+ page_path: window.location.pathname,
181
+ page_title: this.detail.title,
182
+ page_type: "news_detail",
183
+ page_id: String(this.detail.id)
184
+ });
185
+ this.timeTimer = setTimeout(() => {
186
+ this.triggerEffective();
187
+ }, 15e3);
188
+ window.addEventListener("click", this.handleInteraction);
189
+ window.addEventListener("copy", this.handleInteraction);
190
+ window.addEventListener("scroll", this.handleScroll, { passive: true });
191
+ document.addEventListener("visibilitychange", this.handleVisibilityChange);
192
+ window.addEventListener("pagehide", this.handlePageHide);
193
+ }
194
+ stop(reason = "jump") {
195
+ this.reportEvent(reason);
196
+ this.cleanup();
197
+ }
198
+ cleanup() {
199
+ if (this.timeTimer) {
200
+ clearTimeout(this.timeTimer);
201
+ this.timeTimer = null;
202
+ }
203
+ window.removeEventListener("click", this.handleInteraction);
204
+ window.removeEventListener("copy", this.handleInteraction);
205
+ window.removeEventListener("scroll", this.handleScroll);
206
+ document.removeEventListener("visibilitychange", this.handleVisibilityChange);
207
+ window.removeEventListener("pagehide", this.handlePageHide);
208
+ }
209
+ triggerEffective() {
210
+ if (!this.isEffective) {
211
+ this.isEffective = true;
212
+ this.reportEvent("effective_trigger");
213
+ }
214
+ }
215
+ reportEvent(reason) {
216
+ if (!this.detail) return;
217
+ if (reason !== "effective_trigger" && this.hasReportedEffective) {
218
+ return;
219
+ }
220
+ if (this.hasReportedEnd && reason !== "effective_trigger") return;
221
+ const duration = Math.floor((Date.now() - this.startTime) / 1e3);
222
+ const effectiveStatus = this.isEffective ? 1 : 2;
223
+ const backendReason = reason === "effective_trigger" ? "jump" : reason;
224
+ Tracker.getInstance().report("page_stay_end", {
225
+ page_url: window.location.href,
226
+ page_type: "news_detail",
227
+ page_id: String(this.detail.id),
228
+ stay_duration: duration,
229
+ is_effective: effectiveStatus,
230
+ end_reason: backendReason
231
+ });
232
+ if (reason === "effective_trigger") {
233
+ this.hasReportedEffective = true;
234
+ } else {
235
+ this.hasReportedEnd = true;
236
+ }
237
+ }
238
+ };
239
+
240
+ exports.BlogTracking = Tracker;
241
+ exports.SessionManager = SessionManager;
242
+ exports.TrackingSource = TrackingSource;
243
+ //# sourceMappingURL=index.cjs.map
244
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/types.ts","../src/core/Tracker.ts","../src/core/SessionManager.ts"],"names":["TrackingSource"],"mappings":"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","file":"index.cjs","sourcesContent":["export enum TrackingSource {\n Search = 'search',\n Social = 'social',\n Direct = 'direct',\n Referral = 'referral',\n AppBanner = 'app_banner',\n AppTie = 'app_tie',\n Edm = 'edm',\n GuanPhone = 'guan_phone',\n GuanBanner = 'guan_banner',\n AppAd = 'app_ad',\n}\n\nexport interface TrackingPayload {\n event_name: 'page_view' | 'page_stay_end';\n page_url: string;\n page_path?: string;\n page_title?: string;\n page_type: 'news_detail';\n page_id: string;\n referrer?: string;\n source: TrackingSource | string;\n device_type?: 'mobile' | 'desktop';\n os?: string;\n browser?: string;\n screen_width?: number;\n timestamp?: string;\n device_id: string;\n ip?: string;\n \n // page_stay_end specific\n stay_duration?: number;\n is_effective?: 1 | 2; // 1: effective, 2: inefficient\n end_reason?: EndReason;\n}\n\nexport type EndReason = 'close' | 'jump' | 'background';\n\nexport interface TrackingDetail {\n id: number | string;\n title: string;\n [key: string]: any;\n}\n","import { TrackingPayload, TrackingSource } from './types';\n\nexport class Tracker {\n private static instance: Tracker;\n private deviceId: string;\n private deviceStorageKey = 'BLOG_TRACKING_DEVICE_ID';\n\n private requestFn: (data: any) => Promise<any>;\n\n private constructor(requestFn: (data: any) => Promise<any>) {\n this.requestFn = requestFn;\n this.deviceId = this.getOrInitDeviceId();\n }\n\n public static getInstance(requestFn?: (data: any) => Promise<any>): Tracker {\n if (!Tracker.instance) {\n if (!requestFn) {\n throw new Error('Tracker needs requestFn for the first initialization');\n }\n Tracker.instance = new Tracker(requestFn);\n }\n return Tracker.instance;\n }\n\n private getOrInitDeviceId(): string {\n try {\n let id = localStorage.getItem(this.deviceStorageKey);\n if (!id) {\n id = this.generateUUID();\n localStorage.setItem(this.deviceStorageKey, id);\n }\n return id;\n } catch (e) {\n console.warn('[BlogTracking] localStorage unavailable, using temporary ID');\n return this.generateUUID();\n }\n }\n\n private generateUUID(): string {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n const r = Math.random() * 16 | 0;\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n }\n\n public getSource(): string {\n if (typeof window === 'undefined') return TrackingSource.Direct;\n\n const urlParams = new URLSearchParams(window.location.search);\n const utmSource = urlParams.get('utm_source');\n\n if (utmSource) return utmSource;\n\n const referrer = document.referrer || '';\n const referrerLower = referrer.toLowerCase();\n\n if (referrerLower.includes('google') || referrerLower.includes('bing')) {\n return TrackingSource.Search;\n }\n\n const socialKeywords = ['twitter', 't.co', 'x.com', 'instagram', 'facebook', 'discord', 'reddit', 'tiktok'];\n if (socialKeywords.some(k => referrerLower.includes(k))) {\n return TrackingSource.Social;\n }\n\n if (!referrer) return TrackingSource.Direct;\n\n return TrackingSource.Referral;\n }\n\n private validate(data: Partial<TrackingPayload>): boolean {\n const requiredFields: (keyof TrackingPayload)[] = [\n 'event_name', 'page_url', 'device_id', 'page_type', 'page_id', 'source'\n ];\n\n for (const field of requiredFields) {\n if (!data[field]) {\n console.warn(`[BlogTracking] Missing field: ${field}`, data);\n return false;\n }\n }\n\n if (data.event_name === 'page_stay_end') {\n if (\n data.stay_duration === undefined ||\n data.is_effective === undefined ||\n !data.end_reason\n ) {\n console.warn(`[BlogTracking] page_stay_end missing fields`, data);\n return false;\n }\n }\n\n return true;\n }\n\n private getDeviceType(): 'mobile' | 'desktop' {\n if (typeof navigator === 'undefined') return 'desktop';\n const ua = navigator.userAgent;\n const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(ua);\n return isMobile ? 'mobile' : 'desktop';\n }\n\n private getOS(): string {\n if (typeof navigator === 'undefined') return 'Unknown';\n const ua = navigator.userAgent;\n if (/iPad|iPhone|iPod/.test(ua)) return 'iOS';\n if (/Android/.test(ua)) return 'Android';\n if (/Mac/.test(ua)) return 'Mac';\n if (/Win/.test(ua)) return 'Windows';\n if (/Linux/.test(ua)) return 'Linux';\n return 'Unknown';\n }\n\n private getBrowser(): string {\n if (typeof navigator === 'undefined') return 'Unknown';\n const ua = navigator.userAgent;\n if (ua.indexOf('Chrome') > -1 && ua.indexOf('Safari') > -1 && ua.indexOf('Edg') === -1) return 'Chrome';\n if (ua.indexOf('Safari') > -1 && ua.indexOf('Chrome') === -1) return 'Safari';\n if (ua.indexOf('Firefox') > -1) return 'Firefox';\n if (ua.indexOf('Edg') > -1) return 'Edge';\n return 'Unknown';\n }\n\n public async report(event: 'page_view' | 'page_stay_end', payload: Partial<TrackingPayload>) {\n try {\n const fullData: Partial<TrackingPayload> = {\n ...payload,\n event_name: event,\n device_id: this.deviceId,\n source: payload.source || this.getSource(),\n page_url: payload.page_url || (typeof window !== 'undefined' ? window.location.href : ''),\n referrer: payload.referrer || (typeof document !== 'undefined' ? document.referrer : ''),\n page_path: payload.page_path || (typeof window !== 'undefined' ? window.location.pathname : ''),\n page_title: payload.page_title || (typeof document !== 'undefined' ? document.title : ''),\n device_type: payload.device_type || this.getDeviceType(),\n os: payload.os || this.getOS(),\n browser: payload.browser || this.getBrowser(),\n screen_width: payload.screen_width || (typeof window !== 'undefined' ? window.screen.width : 0),\n };\n\n if (this.validate(fullData)) {\n await this.requestFn(fullData);\n }\n } catch (e) {\n console.error('[BlogTracking] Report failed', e);\n }\n }\n}\n","import { Tracker } from './Tracker';\nimport { EndReason, TrackingDetail } from './types';\n\nexport class SessionManager {\n private detail: TrackingDetail;\n private startTime: number = 0;\n private isEffective: boolean = false;\n private hasReportedEffective: boolean = false;\n private hasReportedEnd: boolean = false;\n \n private timeTimer: any = null;\n private handleInteraction: () => void;\n private handleScroll: () => void;\n private handleVisibilityChange: () => void;\n private handlePageHide: () => void;\n\n constructor(detail: TrackingDetail) {\n this.detail = detail;\n\n // Bind methods to preserve 'this'\n this.handleInteraction = this.triggerEffective.bind(this);\n \n this.handleScroll = () => {\n if (this.isEffective) return;\n const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;\n const scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;\n const clientHeight = document.documentElement.clientHeight || document.body.clientHeight;\n \n const scrollPercent = (scrollTop + clientHeight) / scrollHeight;\n if (scrollPercent > 0.3) {\n this.triggerEffective();\n }\n };\n\n this.handleVisibilityChange = () => {\n if (document.visibilityState === 'hidden') {\n this.reportEvent('background');\n }\n };\n\n this.handlePageHide = () => {\n this.reportEvent('close');\n };\n }\n\n public start() {\n this.startTime = Date.now();\n this.isEffective = false;\n this.hasReportedEffective = false;\n this.hasReportedEnd = false;\n\n // Report Page View\n Tracker.getInstance().report('page_view', {\n page_url: window.location.href,\n page_path: window.location.pathname,\n page_title: this.detail.title,\n page_type: 'news_detail',\n page_id: String(this.detail.id),\n });\n\n // 1. Time trigger (15s)\n this.timeTimer = setTimeout(() => {\n this.triggerEffective();\n }, 15000);\n\n // 2. Interaction listeners\n window.addEventListener('click', this.handleInteraction);\n window.addEventListener('copy', this.handleInteraction);\n window.addEventListener('scroll', this.handleScroll, { passive: true });\n\n // 3. Visibility & PageHide\n document.addEventListener('visibilitychange', this.handleVisibilityChange);\n window.addEventListener('pagehide', this.handlePageHide);\n }\n\n public stop(reason: EndReason = 'jump') {\n this.reportEvent(reason);\n this.cleanup();\n }\n\n private cleanup() {\n if (this.timeTimer) {\n clearTimeout(this.timeTimer);\n this.timeTimer = null;\n }\n window.removeEventListener('click', this.handleInteraction);\n window.removeEventListener('copy', this.handleInteraction);\n window.removeEventListener('scroll', this.handleScroll);\n document.removeEventListener('visibilitychange', this.handleVisibilityChange);\n window.removeEventListener('pagehide', this.handlePageHide);\n }\n\n private triggerEffective() {\n if (!this.isEffective) {\n this.isEffective = true;\n this.reportEvent('effective_trigger');\n }\n }\n\n private reportEvent(reason: EndReason | 'effective_trigger') {\n if (!this.detail) return;\n \n // Avoid duplicates for effective trigger\n if (reason !== 'effective_trigger' && this.hasReportedEffective) {\n // If we already reported effective, we don't block the END report, \n // BUT the original logic says: \"If already reported effective, then subsequent END report is NOT sent?\"\n // Re-reading original logic:\n // \"if (reason !== 'effective_trigger' && hasReportedEffective.current) { return; }\"\n // Wait, this means if effective reported, we NEVER report stay_end?\n // Let's check original useNewsTracking.ts carefully.\n \n // Original: \n // if (reason !== 'effective_trigger' && hasReportedEffective.current) { return; }\n // This implies that if user stayed > 15s (effective), we do NOT send a separate 'close'/'jump' event report with duration?\n // That seems to be the logic in the original file. I will replicate it faithfully.\n return; \n }\n\n if (this.hasReportedEnd && reason !== 'effective_trigger') return;\n\n const duration = Math.floor((Date.now() - this.startTime) / 1000);\n const effectiveStatus = this.isEffective ? 1 : 2;\n const backendReason = reason === 'effective_trigger' ? 'jump' : reason;\n\n Tracker.getInstance().report('page_stay_end', {\n page_url: window.location.href,\n page_type: 'news_detail',\n page_id: String(this.detail.id),\n stay_duration: duration,\n is_effective: effectiveStatus,\n end_reason: backendReason as EndReason\n });\n\n if (reason === 'effective_trigger') {\n this.hasReportedEffective = true;\n } else {\n this.hasReportedEnd = true;\n }\n }\n}\n"]}
@@ -0,0 +1,40 @@
1
+ import { T as TrackingPayload, a as TrackingDetail, E as EndReason } from './types-D2W6ZbkS.cjs';
2
+ export { b as TrackingSource } from './types-D2W6ZbkS.cjs';
3
+
4
+ declare class Tracker {
5
+ private static instance;
6
+ private deviceId;
7
+ private deviceStorageKey;
8
+ private requestFn;
9
+ private constructor();
10
+ static getInstance(requestFn?: (data: any) => Promise<any>): Tracker;
11
+ private getOrInitDeviceId;
12
+ private generateUUID;
13
+ getSource(): string;
14
+ private validate;
15
+ private getDeviceType;
16
+ private getOS;
17
+ private getBrowser;
18
+ report(event: 'page_view' | 'page_stay_end', payload: Partial<TrackingPayload>): Promise<void>;
19
+ }
20
+
21
+ declare class SessionManager {
22
+ private detail;
23
+ private startTime;
24
+ private isEffective;
25
+ private hasReportedEffective;
26
+ private hasReportedEnd;
27
+ private timeTimer;
28
+ private handleInteraction;
29
+ private handleScroll;
30
+ private handleVisibilityChange;
31
+ private handlePageHide;
32
+ constructor(detail: TrackingDetail);
33
+ start(): void;
34
+ stop(reason?: EndReason): void;
35
+ private cleanup;
36
+ private triggerEffective;
37
+ private reportEvent;
38
+ }
39
+
40
+ export { Tracker as BlogTracking, SessionManager, TrackingDetail, TrackingPayload };
@@ -0,0 +1,40 @@
1
+ import { T as TrackingPayload, a as TrackingDetail, E as EndReason } from './types-D2W6ZbkS.js';
2
+ export { b as TrackingSource } from './types-D2W6ZbkS.js';
3
+
4
+ declare class Tracker {
5
+ private static instance;
6
+ private deviceId;
7
+ private deviceStorageKey;
8
+ private requestFn;
9
+ private constructor();
10
+ static getInstance(requestFn?: (data: any) => Promise<any>): Tracker;
11
+ private getOrInitDeviceId;
12
+ private generateUUID;
13
+ getSource(): string;
14
+ private validate;
15
+ private getDeviceType;
16
+ private getOS;
17
+ private getBrowser;
18
+ report(event: 'page_view' | 'page_stay_end', payload: Partial<TrackingPayload>): Promise<void>;
19
+ }
20
+
21
+ declare class SessionManager {
22
+ private detail;
23
+ private startTime;
24
+ private isEffective;
25
+ private hasReportedEffective;
26
+ private hasReportedEnd;
27
+ private timeTimer;
28
+ private handleInteraction;
29
+ private handleScroll;
30
+ private handleVisibilityChange;
31
+ private handlePageHide;
32
+ constructor(detail: TrackingDetail);
33
+ start(): void;
34
+ stop(reason?: EndReason): void;
35
+ private cleanup;
36
+ private triggerEffective;
37
+ private reportEvent;
38
+ }
39
+
40
+ export { Tracker as BlogTracking, SessionManager, TrackingDetail, TrackingPayload };