@tengence/track-sdk-web 1.1.4 → 1.2.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 CHANGED
@@ -1 +1,292 @@
1
- **@tengence/track-sdk-web**
1
+ # @tengence/track-sdk-web
2
+
3
+ Tengence Web 行为埋点 SDK。上报统一走 `Authorization: Bearer`,凭证三选一:**apiKey 直连**(配置即用,不请求鉴权地址)、**authUrl 自动换取**(SDK 内部获取并管理短期 accessToken)、**setToken 外部注入**(覆盖内部 token)。推送地址内置默认,可初始化覆盖。
4
+
5
+ ## 安装与引入
6
+
7
+ UMD:
8
+
9
+ ```html
10
+ <script src="https://static.tengence.com/track/tengence-track-sdk@latest.js"></script>
11
+ ```
12
+
13
+ ## 初始化
14
+
15
+ ### 方式一:authUrl 自动换取 accessToken(推荐,无长期密钥暴露)
16
+
17
+ 配置鉴权地址后,SDK 首次上报前自动向 authUrl 换取短期 accessToken,缓存复用(localStorage + 内存),401 时自动清缓存重新获取并重试一次,无需业务方干预。
18
+
19
+ ```js
20
+ const client = TrackSDK.init({
21
+ // 可选,不传则需 apiKey 或 setToken;authUrl 服务的特定参数(如 module=track)放入查询串,SDK 请求体固定为空对象 {}
22
+ authUrl: "https://www.tengence.com/wp-json/tengence/v1/auth/access-token?module=track",
23
+ platform: "web", // web/h5/ios/android/wechat_miniprogram/alipay_miniprogram,缺省按 UA 推断 web/h5
24
+ traceBehavior: true,
25
+ traceError: true,
26
+ tracePerformance: true,
27
+ });
28
+ ```
29
+
30
+ ### 方式二:apiKey 直连(无鉴权服务 / 简单场景)
31
+
32
+ ```js
33
+ const client = TrackSDK.init({
34
+ apiKey: "sk-xxxxxxxx", // 配置后优先直连,不再请求 authUrl
35
+ platform: "web",
36
+ });
37
+ ```
38
+
39
+ ### 方式三:外部 setToken 注入 accessToken
40
+
41
+ 外部调用方已有自己的 token 体系时,初始化后随时注入,注入的 token **覆盖** SDK 内部获取的 token(仅内存,不持久化):
42
+
43
+ ```js
44
+ const client = TrackSDK.init({ platform: "web" });
45
+ client.setToken("<外部短期 accessToken>"); // 覆盖内部 token
46
+ client.setToken(); // 清除外部注入,回退 apiKey / authUrl 逻辑
47
+ ```
48
+
49
+ > **凭证要求**:`apiKey`、`authUrl`、`setToken` 至少提供其一,否则 SDK 无法获取上报凭证,初始化与上报时会提示错误且**不推送数据**(数据保留在缓存队列)。
50
+ > **推送地址**:`pushUrl` 不传时使用内置默认地址 `https://api.tengence.com/v1/push/event`,私有化 / 多环境可显式覆盖。
51
+ > **凭证优先级**:apiKey > 外部 setToken 注入 > authUrl 内部换取。
52
+
53
+ ## 上报事件
54
+
55
+ v1.1.12 起主 API 为 **`track`**(对齐 Segment / Mixpanel / Amplitude 主流 `track(event, properties)`),原 `report` 已**移除**(破坏性变更,见下方迁移说明):
56
+
57
+ ```js
58
+ // 字符串形式:track(eventType, eventDetail?, options?) —— 推荐
59
+ client.track("click", { id: "btn-1", name: "提交" }); // eventDetail 非字符串会被序列化为 JSON 字符串;建议键数 ≤ 50(不硬限,注意单条 64KB 上限)
60
+ client.track("pay", { order: "x" }, { data_type: "custom", track_real_time: true }); // options 透传高频控制字段
61
+
62
+ // 对象形式:track({ event_type, event_detail, ... }) —— 兼容完整字段(原 report 语义)
63
+ client.track({
64
+ event_type: "click",
65
+ data_type: "behavior",
66
+ event_detail: { id: "btn-1", name: "提交" },
67
+ spm: "a.b.c",
68
+ });
69
+ ```
70
+
71
+ **手动立即上报**:`await client.flush()` —— 立即触发普通 + 实时两通道发送,返回 Promise 等待在途发送完成(无数据时立即 resolve);适合跳页/退出前主动冲刷,对齐 Amplitude / 神策 `flush()`。
72
+
73
+ > **report → track 迁移**:对象形式 `report({...})` 直接改为 `track({...})`,参数与语义不变;原 `report` 已从 `client` 与 `window.trackMonitor` 移除。
74
+ > `TrackSDK` 全局仅暴露 `init`;`track` / `flush` / `setToken` / `registerGlobalProperties` 等方法通过 `init` 返回的 client 实例调用(也可经全局 `window.trackMonitor` 访问)。
75
+
76
+ ## 注册全局扩展属性
77
+
78
+ 对**每个事件都想携带**的稳定属性(如语言、货币、AB 分组),初始化后调用一次 `registerGlobalProperties`,注册时即提供值;SDK 内部将注册字段组装进 `register_properties` 字段自动随每个事件上报,**业务侧无需每次 track 手动携带,也看不到该字段**。未注册的扩展字段不会被上报(由 SDK 端过滤)。
79
+
80
+ ```js
81
+ client.registerGlobalProperties({
82
+ language: "zh-CN", // 值仅支持 string / number / boolean
83
+ currency: "CNY",
84
+ });
85
+ ```
86
+
87
+ - 注册即提供值;重复注册同名键以最新值为准;localStorage 持久化,跨会话保留
88
+ - 键数上限 50,超限部分丢弃并告警
89
+ - 每次上报时动态变化的业务值(金额、折扣等)请放在 `track()` 的 `event_detail` 中携带,无需注册
90
+
91
+ ## 字段说明
92
+
93
+ | 字段 | 说明 |
94
+ | --- | --- |
95
+ | `pushUrl` | 推送地址,默认 `https://api.tengence.com/v1/push/event`(原 `url` 改名) |
96
+ | `authUrl` | 鉴权地址(获取 accessToken),无默认值;配置且未配 apiKey 时 SDK 内部自动换取;authUrl 服务所需特定参数(如 `module=track`)请放入地址查询串(如 `...?module=track`),SDK 请求体固定为空对象 `{}` |
97
+ | `apiKey` | 应用长期 apiKey(`sk-`)直连优先,配置后不再请求 authUrl |
98
+ | `platform` | 终端维度:`web` / `h5` / `ios` / `android` / `wechat_miniprogram` / `alipay_miniprogram`,缺省按 UA 推断 |
99
+ | `cacheNumber` | 缓存条数触发上限,默认 **100 条**(100 条或 5 秒触发上报,先到先发) |
100
+ | `reportInterval` | 普通通道上报周期,默认 **5000ms(5 秒)**(对齐 Mixpanel/Segment 主流批量窗口) |
101
+ | `maxCacheSize` | 缓存体积触发上限,默认 **56KB**(与 fetch keepalive 64KB 上限兼容) |
102
+ | `maxRetries` | 最大重试次数,默认 **10**;耗尽后数据保留队列(不丢弃),console.warn 提示 |
103
+ | `realTimeUrl` | 实时推送地址,无默认值;**未配置时实时事件并入普通通道上报(单份、不丢、不双写)**;配置后实时事件走独立实时通道(200ms 快发,按 `realTimeReportInterval`) |
104
+ | `realTimeReportInterval` | 实时通道上报周期,默认 **200ms** |
105
+ | `errorTraceRate` | 错误采样率,默认 **0.5**(50%);传 `0` 关闭错误上报、传 `1` 全量上报(v1.1.8 起显式 0/1 生效) |
106
+ | `resourceFlowLimit` | 资源性能明细条数上限,默认 **15**(v1.1.16,范围 [0,200]):`navigation_timing`/`metrics` 不受影响;传 `0` 关闭 `resource_flow` 采集(页面加载即上报 15 条资源明细体积较大,低流量场景可关) |
107
+ | `sanitize` | 发送前 PII 脱敏(**默认关闭**,业务按需显式开启):传入对象开启——`stripQuery` 剥离 URL 查询串(默认 true)、`maxTextLength` 超长文本截断(默认 0 关闭,0 关闭截断)、`preserveQueryKeys` 剥离时保留的键;传 `false` 显式关闭 |
108
+ | `enableAutoSessionId` | 会话自动维护开关,默认 **false**:外部未传 `session_id` 时,开启则 SDK 维护 30min 空闲超时会话(超时自动重置新 id,活跃顺延);关闭则不带 `session_id` |
109
+ | `enableTracking` | 采集总开关,默认 **true**(v1.1.11):传 `false` 初始化或 `client.enableTracking(false)` 即退出采集(opt-out)——后续 `track` 忽略、**清空本地与实时未上报队列**(合规语义:退出后不再持有/上报任何数据)、停止一切发送(含周期定时、卸载补发);`client.enableTracking()` 无参返回当前状态,`client.enableTracking(true)` 恢复采集。开关状态持久化 localStorage,刷新后保持 |
110
+ | `enableScroll` | 滚动深度采集开关,默认 **true**(v1.1.13):到达 25%/50%/75%/90% 深度各上报一次 `scroll` 事件(每档仅一次,90% 后解除监听);`false` 关闭 |
111
+ | `enableFormTrack` | 表单交互采集开关,默认 **true**(v1.1.13):自动上报 `form_start`(表单内控件首次 focus,每表单一次)/ `form_submit`(每次提交);**只采表单元数据(id/class/name/action/可见文本),不采 input 值**(隐私安全,神策同策略) |
112
+ | `requestTimeout` | 单次上报请求超时,默认 **10000ms**(v1.1.11),范围 [1000, 60000]:fetch 用 AbortController、XHR 用 `timeout` 兜底;超时数据保留队列按退避重试;**页面卸载补发(pagehide/beforeunload)不设超时**(keepalive 兜底不被 abort 中断) |
113
+ | `onError(error)` | SDK 自身错误回调(v1.1.11,默认无):`error` 形如 `{ code, message, context? }`,详见下方错误码表;与 `onDrop` 职责分离——`onDrop` 感知**数据面**(滞留/丢弃),`onError` 感知**系统面**(配置缺失、token 获取/失效、队列读写失败、请求超时等 SDK 内部故障);回调内抛错不影响 SDK 主流程 |
114
+ | `beforeSend(events)` | 发送前钩子:返回修改后的事件数组(Segment middleware 风格,业务自定义增删/脱敏) |
115
+ | `onDrop(events, reason)` | 数据滞留/丢弃回调(默认无):`reason` 为 `rejected_4xx`(4xx 拒绝,含单条 413 超限)/ `retries_exhausted`(重试耗尽)/ `item_too_large`(单条超限丢弃)/ `queue_overflow`(队列超上限丢最旧)/ `server_rejected`(v1.1.10:网关 200 + rejected_ids 校验明确拒绝,从队列移除并通知);业务可编程感知失败,不改变保留语义 |
116
+ | `client.setToken(token)` | 注入外部 accessToken(覆盖内部获取;传 undefined 清除,回退 apiKey/authUrl) |
117
+ | `client.track(eventType, eventDetail?, options?)` | 上报主 API(v1.1.12):字符串形式 `track("click", {id:1})` / 对象形式 `track({event_type,...})` 兼容全字段;`options` 透传 `data_type`/`session_id`/`request_id`/`spm`/`track_real_time`/`platform` |
118
+ | `client.flush()` | 手动立即上报(v1.1.12):触发普通+实时两通道发送,返回 Promise 等待在途发送完成 |
119
+ | `client.registerGlobalProperties(props)` | 注册全局扩展属性(注册即提供值),SDK 内部组装进 `register_properties` 上报,用户不可见 |
120
+
121
+ ## 上报队列与重试机制
122
+
123
+ - **双通道**:普通通道(批量)与实时通道(快发)两条独立队列,均 localStorage 持久化;实时通道仅在配置 `realTimeUrl` 后启用,未配置时实时事件并入普通通道(单份、不丢、不双写);**所有类型事件(含错误)默认走普通通道**,仅业务显式 `track_real_time: true` 的事件走实时通道(口子保留,待实时后台就绪后启用)
124
+ - **成功后清理**:批量提交**成功后才按该批 `event_id` 列表从队列移除**;提交失败(任何原因)数据保留在队列,绝不丢弃。**v1.1.10(N-4)**:网关响应含 `accepted_ids`/`rejected_ids` 时按服务端确认**精确清理**——只清被接收的条目;`rejected_ids`(服务端校验明确拒绝,永久性拒绝)从队列移除并触发 `onDrop("server_rejected")` 通知业务(对齐 Segment/Sentry:永久错误不重试);响应缺 ids(异常兜底)按整批成功清理 + warn,防死循环
125
+ - **触发时机**:普通通道满 100 条 / 满 56KB / 每 5 秒定时(先到先发);实时通道 200ms 定时;**定时器到点强制发送**(v1.1.9 修复:原"定时到点仅检查不发送"导致低流量数据滞留)
126
+ - **重试**:普通/实时通道独立计数(互不阻塞);指数退避 + 抖动(封顶 60s),429 尊重 `Retry-After`;HTTP 已响应的 4xx/5xx 不重复回退请求;其他 4xx(数据被拒)标记滞留保留、不自动轰炸;重试耗尽后数据保留并 warn;**413(PayloadTooLarge)自动降容**:批次减半重发(对齐 Amplitude/Mixpanel),单条仍超限才滞留
127
+ - **失败感知**:`onDrop(events, reason)` 回调(v1.1.9 起)——`rejected_4xx`/`retries_exhausted`/`item_too_large`/`queue_overflow`;v1.1.10 新增 `server_rejected`(网关 200 + rejected_ids 校验拒绝,从队列移除并通知);传输失败类数据仍按保留原则处理
128
+ - **卸载兜底**:beforeunload + pagehide + visibilitychange(hidden) 三重尽力补发;keepalive 超 64KB 自动切片分批;**网络恢复(online)立即补发**
129
+ - **凭证**:apiKey > 外部 setToken 注入 > authUrl 内部换取(缓存复用 + single-flight + 401 自动刷新一次)
130
+ - **会话**:`session_id` 调用方显式传入优先;未传且开启 `enableAutoSessionId`(默认关闭)时,SDK 自动维护 30min **空闲超时**会话(sessionStorage,30min 无活跃则重置新 id,活跃顺延)
131
+ - **脱敏**:默认关闭(业界主流,业务按需显式开启)——显式传 `sanitize: { stripQuery, maxTextLength, preserveQueryKeys }` 后,发送前剥离 `page_url` 及 URL 类字段查询串、超长文本截断;`sanitize: false` 显式关闭;也可用 `beforeSend` 钩子自定义
132
+ - **隐私合规(opt-out)**:`enableTracking` 采集总开关(v1.1.11,默认 true),`client.enableTracking(false)` 退出采集——后续事件忽略、清空本地与实时未上报队列、停止一切发送(含周期/卸载补发),对齐主流(Amplitude/Mixpanel opt-out 语义);开关持久化,刷新保持
133
+ - **隐私合规(GPC,v1.1.14)**:浏览器"全局隐私控制"信号(`navigator.globalPrivacyControl === true`,用户在浏览器层选择不出售/不分享数据)自动等效于退出采集——作为**叠加关闭层**:事件不入队、不发请求;init 时读取一次 + 每次上报前复查(浏览器无 GPC 变化事件);`respectGpc: false` 关闭信号响应(默认 true);**GPC 不覆写持久化的 `enableTracking`**(二者独立叠加,任一关闭即停采;即使 `enableTracking(true)` 恢复,GPC 仍开启时采集保持关闭)
134
+ - **请求超时**:单次请求默认 10s(v1.1.11,范围 [1s, 60s]),fetch AbortController + XHR timeout 双保险;超时保留队列按退避重试;**页面卸载补发不设超时**(keepalive 不被 abort 中断)
135
+ - **多标签页**:队列基于 localStorage,多标签页同时写入可能互相覆盖(last-write-wins);建议单标签页使用,服务端已按 `event_id` 去重
136
+ - **队列性能(v1.1.12)**:内存镜像为主数据源 + 增量字节统计,入队 O(1);落盘 200ms 防抖合并写(多次入队合并一次序列化,取代原每次入队全量 parse+stringify 的 O(n²));**成功清理路径立即落盘**(页面清理后瞬间崩溃不残留已提交批次,避免下次会话重复发送);页面关闭/隐藏(beforeunload/pagehide/visibilitychange hidden)强制同步落盘,崩溃窗口 ≤200ms;隐私模式 localStorage 不可用时数据仅保留内存、镜像不丢
137
+
138
+ ## SDK 错误码(`onError` 回调,v1.1.11)
139
+
140
+ | code | 含义 | 数据处置 |
141
+ | --- | --- | --- |
142
+ | `init_missing_credential` | 初始化/上报时未配置 `apiKey`、`authUrl` 且未 `setToken` | 本批不上报,数据保留队列待重试 |
143
+ | `token_fetch_failed` | 从 `authUrl` 换取 accessToken 失败(网络/响应缺字段) | 本批不上报,数据保留队列待重试 |
144
+ | `token_invalid` | 上报 401/403 后自动刷新 accessToken 仍失败 | 本批不上报,数据保留队列待重试 |
145
+ | `request_timeout` | 上报请求超时(fetch abort / XHR timeout) | 数据保留队列按退避重试 |
146
+ | `queue_read_failed` | 读取 localStorage 队列失败(损坏 JSON 等) | 按空队列处理 |
147
+ | `queue_write_failed` | 写入 localStorage 队列失败(隐私模式/容量满) | 队列清空处理(尽力兜底) |
148
+ | `localstorage_unavailable` | localStorage 完全不可用(隐私模式) | 本次数据仅保留内存 |
149
+ | `enqueue_failed` | 事件入队失败 | 该事件丢弃 |
150
+ | `serialize_failed` | 事件 `event_detail` 序列化失败(循环引用/BigInt 等,v1.1.16) | 该事件丢弃(不冒泡到业务调用方) |
151
+ | `before_send_failed` | `beforeSend` 钩子抛错(v1.1.17) | 回退原始数据发送(不丢事件) |
152
+
153
+ ## 自动采集事件(全埋点,v1.1.13 补全)
154
+
155
+ `traceBehavior: true`(默认)时自动采集以下事件,无需业务手动 `track`:
156
+
157
+ | 事件 | 触发 | 说明 |
158
+ | --- | --- | --- |
159
+ | `page_view` | 页面加载 / hash 变化 / pushState / replaceState / **popstate(v1.1.13 新增)** | 200ms 防抖 + URL 去重;SPA 前进/后退补报页面浏览(对齐 GA4 监听 pushState/popState/replaceState) |
160
+ | `page_leave` | 路由变化 / 页面隐藏 / 关闭 | 停留 >200ms 才报,携带停留时长 |
161
+ | `click` | 预设标签 A/INPUT/TEXTAREA/BUTTON / `track-click` 属性 / `closest('a, button, [track-click]')` | 采集元素 id/class/name/type/XPath/坐标/占比 |
162
+ | `scroll`(v1.1.13) | 滚动到达 25%/50%/75%/90% 深度 | 每档仅一次(300ms 节流 + Set 去重),90% 后解除监听;`enableScroll: false` 关闭 |
163
+ | `form_start`(v1.1.13) | 表单内控件首次 focus | 每表单仅一次;只采表单元数据不采 input 值;`enableFormTrack: false` 关闭 |
164
+ | `form_submit`(v1.1.13) | 表单提交(捕获阶段) | 每次提交上报,含 submitter 信息;只采表单元数据不采 input 值 |
165
+ | `expose` / `view` / `view_expose` | 元素进入可视区(IntersectionObserver) | 可视区停留默认 500ms(`data-duration-view`/`data-duration-expose` 可逐元素覆盖) |
166
+
167
+ ## v1.1.17 变更(自 v1.1.16)
168
+
169
+ | 变更 | 说明 |
170
+ | --- | --- |
171
+ | README script 标签改 `@latest.js`(D1) | 安装示例指向 CDN latest 软链(deploy 自动维护),不再随版本迭代手动改 README |
172
+ | exposePlugin DOM 就绪兜底(D2) | `<head>` 同步加载并 init 时 `document.body` 尚不存在,`MutationObserver.observe(null)` 抛 TypeError 导致 init 崩溃;现兜底 `document.body \|\| document.documentElement`(subtree 覆盖 body),对齐 GA4/神策 DOM 就绪保护 |
173
+ | 空 `resource_flow` 不上报(D5) | 页面无资源明细时不再上报 `{data: []}` 空事件(噪声) |
174
+ | `beforeSend` 钩子兜底(D6) | 钩子返回非数组(undefined/null)时回退原始数据,避免 `{ data: undefined }` 空载荷;钩子抛错不崩,`onError("before_send_failed")` 通知后按原始数据发送(对齐 Segment middleware 容错) |
175
+ | 验证 | typecheck 通过;vitest **31/31**(新增 D5 空资源、D6 返回 undefined/抛错 3 用例,fetch mock 验证请求体);冒烟 38/38 全绿 |
176
+
177
+ ## v1.1.16 变更(自 v1.1.15)
178
+
179
+ | 变更 | 说明 |
180
+ | --- | --- |
181
+ | event_detail 序列化兜底(C1) | `track()` 传循环引用 / BigInt 等导致 `JSON.stringify` 抛错时,**丢弃该事件并 `onError("serialize_failed")` 通知,不再冒泡到业务调用方**(对齐 Segment/Amplitude 序列化兜底);业务代码不会因错误参数崩溃 |
182
+ | 注册扩展属性内存缓存(C2) | `register_properties` 首次读取后持有,每次事件省一次 localStorage 读 + parse(registerGlobalProperties 写入时同步更新) |
183
+ | `resourceFlowLimit` 可配(C7) | 新增配置(默认 15,范围 [0,200]):`resource_flow` 明细条数上限;传 `0` 关闭该项采集(navigation_timing/metrics 不受影响) |
184
+ | 放弃 IE11(C6) | babel target `ie:11` → `esmodules: true`:原 IE11 目标与 `Promise.finally` 等运行时 API 实际不兼容(未引入 polyfill 会崩);现只面向现代浏览器,UMD 保留原生 async/await,产物更小、运行更快 |
185
+ | 细节修正(C3/C4/C5) | README 键数声明改为"建议 ≤50 键"(与实现一致,不硬限);`getErrorKey` 的 "cors" 分支加注释(保留用于防止 Script error 被 resource 误采);脱敏 URL 键正则收紧为 `(^|_)href$`(原 `href` 匹配过宽) |
186
+ | 验证 | typecheck 通过;vitest **28/28**(新增 C1 循环引用/BigInt 兜底、C7 关闭/限条数 4 用例);冒烟 38/38 全绿 |
187
+
188
+ ## v1.1.15 变更(自 v1.1.14)
189
+
190
+ | 变更 | 说明 |
191
+ | --- | --- |
192
+ | `maxCacheSize` 单位修复(B1) | 队列体积以 KB 计,但默认值误写成字节量级(`1024*56=57344`),实际触发阈值变成 56MB,体积触发上报形同虚设;修正为 **56(KB)**,恢复"满 56KB 触发"文档语义(与 fetch keepalive 64KB 切片兼容) |
193
+ | SDK 自身请求排除采集(B2) | `proxyFetch`/`proxyXmlHttp` 覆写全局后,SDK 自身上报/鉴权请求也会进入 http 错误采集(遇 401/413 会额外生成 http_error 事件自举上报/递归);现采集时排除 pushUrl/authUrl/realTimeUrl 端点(对齐 Sentry 忽略自身请求) |
194
+ | `track()` options.platform 生效(B3) | 原 `TrackOptions.platform` 被静默忽略(硬编码全局平台值);现 `platform: data.platform \|\| 全局值`,单事件可覆盖平台维度 |
195
+ | 指纹哈希换 djb2(B4) | errorUuid 去重指纹由自实现 485 行 MD5 换为 ~10 行 djb2(非加密用途,确定性 + 低碰撞足够);**dist 体积 -8.1KB(62.5KB → 54.4KB,-13%)**;指纹格式变化不影响服务端(服务端按 event_id 去重) |
196
+ | 边界防御(B5/B6) | `collectNavigationTiming` 对 `getEntriesByType` 返回 undefined 的边界防御(防老浏览器 TypeError);设备 ID 首次解析后内存缓存,每次事件省 2 次存储读 |
197
+ | 死代码清理(B7) | logger.ts 移除长注释代码块 |
198
+ | 验证 | typecheck 通过;vitest 24/24(md5 向量测试改为 djb2 向量:`""→00001505`、`"abc"→0b885c8b`);冒烟 38/38 全绿 |
199
+
200
+ ## v1.1.14 变更(自 v1.1.13)
201
+
202
+ | 变更 | 说明 |
203
+ | --- | --- |
204
+ | GPC 隐私信号(第四批 G4) | 新增 `respectGpc`(默认 true):浏览器全局隐私控制信号开启(`navigator.globalPrivacyControl === true`)时自动等效退出采集(叠加关闭层,不入队、不发请求);init 读取一次 + 每次上报前复查;不覆写持久化 `enableTracking`;`false` 关闭信号响应 |
205
+ | 测试与 CI(N-5) | 新增 vitest + jsdom 自动化测试(24 用例:GPC 三态/叠加层、md5 向量、PII 脱敏、队列/重试语义、错误捕获、beforeSend/onDrop),`npm test` / `npm run test:watch` / `npm run test:e2e`(冒烟)/ `npm run typecheck`;GitHub Actions CI(push/PR:build → test → typecheck → e2e,tag 与 package.json 版本一致性发布门) |
206
+ | 脱敏修复(M-15) | 修正 `stripQuery` 独立生效:原实现仅 `maxTextLength > 0` 时才剥 detail 内 URL 类字段查询串(开启 stripQuery 但未设截断时静默失效);现开启脱敏即剥离,文本截断按 `maxTextLength` 各自独立 |
207
+
208
+ ## v1.1.13 变更(自 v1.1.12)
209
+
210
+ | 变更 | 说明 |
211
+ | --- | --- |
212
+ | 滚动深度采集(P0-①) | 新增 `enableScroll`(默认 **true**)+ `scroll` 事件:滚动到达 25%/50%/75%/90% 深度各上报一次(300ms 节流、每档 Set 去重、90% 后解除监听);深度算法 `round((scrollTop+视口高)/文档高×100)`;对齐 GA4 Enhanced Measurement scroll 深度 + 业界多阈值(GA4 仅 90% 单档) |
213
+ | 页面浏览补报(P0-②) | `page_view` 新增 **popstate** 监听(复用 200ms 防抖 + URL 去重):浏览器前进/后退(SPA history 模式)补报页面浏览;与现有 pushState/replaceState/hash 上报互补不重复;对齐 GA4 监听 `popState` |
214
+ | 跨域 Script error 归一化(P0-③) | 修复跨域脚本未加 `crossorigin` 时的 **Script error 丢失**(原 `getErrorKey` 判为 `"cors"` 且无分支处理):归一化为 `js` 错误上报,统一指纹 `js-Script error.` 全站去重为一条(防刷屏),meta 标注 `crossorigin_missing: true`,**不伪造堆栈**;README 建议生产脚本加 `crossorigin="anonymous"` 获取完整堆栈;对齐 Sentry Script error 归一化 |
215
+ | 表单交互采集(P1-④) | 新增 `enableFormTrack`(默认 **true**)+ `form_start`/`form_submit` 事件:focusin 委托捕获表单控件首次 focus(每表单一次防刷屏)+ submit 捕获阶段每次提交(含 submitter_id/text);**只采表单元数据,不采 input 值**(隐私安全,神策同策略);document 级事件委托自动覆盖动态表单;对齐 GA4 form_start/form_submit |
216
+ | 冒烟测试 | 新增 S(scroll 四档各一次/不重复)、P(popstate 补报 page_view)、E(Script error 归一化/去重/不伪造堆栈)、F(form_start 去重/form_submit/不采 input 值),**38/38 全绿**;mock 升级:多槽监听收集(对齐真实浏览器同事件多回调)+ ErrorEvent polyfill |
217
+
218
+ ## v1.1.12 变更(自 v1.1.11)
219
+
220
+ | 变更 | 说明 |
221
+ | --- | --- |
222
+ | 主 API 改 `track`(破坏性变更) | 移除 `report`(`client` 与 `window.trackMonitor` 均无),主 API 改为 `track(eventType, eventDetail?, options?)`,对齐 Segment/Mixpanel/Amplitude 主流签名;字符串形式 `track("click", {id:1})` + 对象形式 `track({event_type,...})` 兼容全字段;`options` 透传 `data_type`/`session_id`/`request_id`/`spm`/`track_real_time`/`platform`。**迁移**:`report({...})` → `track({...})`,参数语义不变 |
223
+ | 新增 `flush()` | `client.flush(): Promise<void>`:立即触发普通+实时两通道发送,返回 Promise 等待在途发送完成(无数据立即 resolve);跳页/退出前主动冲刷,对齐 Amplitude/神策 |
224
+ | 队列性能优化 | 内存镜像 + 增量字节,入队 O(1)(原每次入队全量 parse+stringify 的 O(n²));落盘 200ms 防抖合并写;**成功清理立即落盘**(崩溃不残留已提交批次);页面关闭/隐藏强制同步落盘,崩溃窗口 ≤200ms;隐私模式仅内存不丢镜像。条数/体积/周期/成功才清理/onDrop 语义不变 |
225
+ | 冒烟测试 | 全量迁移 report→track + 新增 F1(flush 立即上报/等待在途)、T1(track 字符串/对象/options 三形式),34/34 全绿 |
226
+
227
+ ## v1.1.11 变更(自 v1.1.10)
228
+
229
+ | 变更 | 说明 |
230
+ | --- | --- |
231
+ | 隐私合规 opt-out(G1) | 新增 `enableTracking`(默认 true)+ `client.enableTracking(flag?)`:退出采集后事件忽略、**清空本地与实时未上报队列**、停止一切发送(含周期/卸载补发,beforeReport 加总闸);开关持久化 localStorage;命名遵循前端 `enable*` 规范 |
232
+ | 请求超时(G2) | 新增 `requestTimeout`(默认 10000ms,[1000, 60000]):fetch 用 AbortController、XHR 用 `timeout` 双保险;超时数据保留队列按退避重试;**卸载补发(pagehide/beforeunload)不设超时**(keepalive 兜底不被 abort 中断) |
233
+ | SDK 错误可观测(G5) | 新增 `onError(error)` 回调 + 错误码表(8 个 code):配置缺失、token 获取/失效、队列读写失败、请求超时、入队失败等系统面故障通知;与 `onDrop` 职责分离(数据面 vs 系统面);回调内抛错不影响 SDK 主流程 |
234
+ | 冒烟测试 | 新增 W1/W2/W3(opt-out 停止发送/恢复/清空队列)、X1(超时走退避重试 + onError)、X2(卸载补发无超时 signal)、Y1/Y2/Y3(onError 三场景),32/32 全绿;场景隔离升级(resetCalls 重置 SDK 幂等实例标记 + token 缓存过期控制) |
235
+
236
+ ## v1.1.10 变更(自 v1.1.9)
237
+
238
+ | 变更 | 说明 |
239
+ | --- | --- |
240
+ | 网关精确清理(N-4) | 响应新增 `accepted_ids`/`rejected_ids`(网关侧配套改造,部分接受不再整体 400):成功后只清理被服务端接收的条目;`rejected_ids`(校验类永久性拒绝,重发必再拒)从队列移除并 `onDrop("server_rejected")` 通知;响应缺 ids 时按整批成功清理 + warn(防死循环) |
241
+ | onDrop 新 reason | 新增 `server_rejected`(网关 200 + rejected_ids);`rejected_4xx` 保留(真实 HTTP 4xx 整体拒绝场景) |
242
+ | 冒烟测试 | 新增 V1(部分接受→精确清理 + server_rejected)/ V2(缺 ids 兜底),24/24 全绿 |
243
+
244
+ ## v1.1.9 变更(自 v1.1.8)
245
+
246
+ | 变更 | 说明 |
247
+ | --- | --- |
248
+ | 413 自动降容 | `HTTP 413 PayloadTooLarge` 不再当作普通 4xx 滞留:批次 >1 时**减半重发**(对齐 Amplitude/Mixpanel),成功后复位;单条仍超限才滞留保留(防死循环) |
249
+ | onDrop 回调 | 新增 `onDrop(events, reason)`:`rejected_4xx`(4xx 拒绝,含单条 413 超限)/ `retries_exhausted`(重试耗尽)/ `item_too_large`(单条超限丢弃)/ `queue_overflow`(队列超上限丢最旧);业务可编程感知失败,不改变保留语义 |
250
+ | 定时器到点强制发送 | 修复"定时到点仅检查条数不发送"问题(低流量页面数据可能长期滞留):周期定时器到点即发送(时间触发),与"满 100 条 / 满 56KB / 每 5 秒"语义一致 |
251
+ | 冒烟测试 | 新增 T(413 降容)/ U(onDrop 四场景),22/22 全绿;场景隔离增强(resetCalls 清空 storage,避免前序滞留数据污染) |
252
+
253
+ ## v1.1.8 变更(自 v1.1.7)
254
+
255
+ | 变更 | 说明 |
256
+ | --- | --- |
257
+ | 配置 0 值生效 | `cacheNumber`/`reportInterval`/`maxCacheSize`/`maxRetries`/`errorTraceRate` 由 `\|\|` 改 `??`,显式传 `0` 不再被回填默认值(如 `errorTraceRate: 0` 关闭错误上报、`maxRetries: 0` 不重试);非法值回退默认并告警 |
258
+ | init 幂等 | 重复调用 `init` 复用首次实例并告警,不再叠加监听器/代理/Observer |
259
+ | PII 脱敏 | 新增 `sanitize` 配置与 `beforeSend` 钩子;**默认关闭**(业界主流),显式开启后剥离 `page_url` 及 URL 类字段查询串、超长文本截断 |
260
+ | 实时通道(修订) | 撤销"错误自动走实时":所有类型事件(含错误)默认走普通通道;仅业务显式 `track_real_time: true` 走实时(口子保留,待实时后台就绪) |
261
+ | 会话管理 | 新增 `enableAutoSessionId`(默认 **false**):外部未传 `session_id` 且开关开启时,SDK 自动维护 30min **空闲超时**会话(sessionStorage,活跃顺延);调用方显式传入始终优先 |
262
+ | 网络恢复补发 | 监听 `online` 事件,断网恢复后立即 flush |
263
+ | 性能上报时机 | 性能指标(CWV)除 beforeunload 外,增加 pagehide / visibilitychange(hidden) 兜底(30s 防抖),缓解移动端丢失 |
264
+ | 日志开关持久化 | `enableLogger`/`disableLogger` 写入的持久化开关在 init 时生效(原只写不读,刷新失效) |
265
+ | 其他修复 | `isVisible` 反逻辑修正;错误去重数组 Set 化 + 上限 1000;md5 自检惰性化(模块加载零开销) |
266
+
267
+ ## v1.1.7 变更(自 v1.1.6)
268
+
269
+ | 变更 | 说明 |
270
+ | --- | --- |
271
+ | 批量默认参数 | `cacheNumber` 500→**100 条**、`reportInterval` 30s→**5s**(对齐 Mixpanel/Segment 主流批量窗口,低流量事件延迟 30s→5s) |
272
+ | 队列语义 | 提交**成功后才按 event_id 清理**该批;失败数据保留队列,**绝不丢弃**(原为"发送前清空队列,失败拼回") |
273
+ | 实时事件分发 | 仅配置 `realTimeUrl` 时走实时通道;未配置时并入普通通道(原为双写两条队列导致重复上报) |
274
+ | 重试机制 | 普通/实时通道独立计数;线性退避 → **指数退避 + 抖动 + Retry-After**(封顶 60s);4xx/5xx 不再回退 XHR 重复请求;重试耗尽后数据保留 + warn(原为静默丢弃) |
275
+ | 队列上限 | 新增条数硬上限 2000 条(丢最旧)与单条体积上限 64KB(丢单条),替代"写满清空整队列" |
276
+ | 卸载兜底 | beforeunload + pagehide + visibilitychange(hidden);keepalive 超 64KB 自动切片 |
277
+
278
+ ## v1.1.6 破坏性变更与迁移(自 v1.1.5)
279
+
280
+ | 变更 | 迁移方式 |
281
+ | --- | --- |
282
+ | `url` 改名 `pushUrl` | 初始化配置 `url` 改为 `pushUrl`(默认值不变) |
283
+ | 初始化配置 `accessToken` 移除 | 不再初始化时传入;改由 `client.setToken(token)` 运行时注入 |
284
+ | `onTokenExpired` 回调移除 | 无需配置:配置 `authUrl` 时 SDK 内部自动换取并在 401 后自动刷新重试;外部 token 生命周期由调用方自行管理 |
285
+ | 凭证要求收紧 | `apiKey` / `authUrl` / `setToken` 至少其一,否则提示错误且不推送 |
286
+
287
+ ## 测试与 CI(v1.1.14)
288
+
289
+ - **单元与行为测试**:vitest + jsdom(`npm test`,watch 用 `npm run test:watch`),覆盖 GPC 三态与叠加层、md5 向量、PII 脱敏(stripQuery/preserveQueryKeys/maxTextLength/默认原文)、enableTracking 持久化、队列语义(入队最新在前、flush 成功清理、失败保留、满条立即触发)、beforeSend 过滤、onDrop(rejected_4xx)、错误捕获与 Script error 归一化
290
+ - **类型检查**:`npm run typecheck`(`tsc --noEmit`,覆盖 src + test)
291
+ - **端到端冒烟**:`npm run test:e2e`(scripts/smoke-test.mjs,mock 浏览器环境驱动 dist 产物,验证队列/重试/脱敏/会话/幂等/滚动/表单/错误全链路)
292
+ - **CI**:`.github/workflows/ci.yml`——push/PR 触发:build → test → typecheck → e2e;`vX.Y.Z` tag 自动校验与 package.json version 一致(发布门,防错发)
package/dist/client.d.ts CHANGED
@@ -1,23 +1,59 @@
1
- import { BaseOptions, ClientReportData, ReportFunction } from "./interface";
1
+ import { BaseOptions, ReportData, TrackFunction, RegisterProperties, DropReason, SdkError } from "./interface";
2
2
  declare global {
3
3
  interface Window {
4
4
  trackMonitor: {
5
- report: ReportFunction;
5
+ track: TrackFunction;
6
+ flush: () => Promise<void>;
6
7
  bindUser: (id: string) => void;
7
8
  unBindUser: () => void;
8
9
  enableLogger: () => void;
9
10
  disableLogger: () => void;
11
+ setToken: (token?: string) => void;
12
+ registerGlobalProperties: (props: RegisterProperties) => void;
13
+ enableTracking: (flag?: boolean) => boolean;
10
14
  };
11
15
  }
12
16
  }
13
- export declare let options: Required<BaseOptions> | undefined;
17
+ interface SdkOptions {
18
+ pushUrl: string;
19
+ authUrl?: string;
20
+ realTimeUrl?: string;
21
+ cacheNumber: number;
22
+ reportInterval: number;
23
+ realTimeReportInterval: number;
24
+ maxCacheSize: number;
25
+ maxRetries: number;
26
+ isLogger: boolean;
27
+ errorTraceRate: number;
28
+ resourceFlowLimit: number;
29
+ traceBehavior: boolean;
30
+ tracePerformance: boolean;
31
+ traceError: boolean;
32
+ enableAutoSessionId: boolean;
33
+ sanitize: {
34
+ stripQuery: boolean;
35
+ maxTextLength: number;
36
+ preserveQueryKeys: string[];
37
+ };
38
+ beforeSend?: (events: ReportData[]) => ReportData[];
39
+ onDrop?: (events: ReportData[], reason: DropReason) => void;
40
+ enableTracking: boolean;
41
+ respectGpc: boolean;
42
+ requestTimeout: number;
43
+ onError?: (err: SdkError) => void;
44
+ }
45
+ export declare let options: SdkOptions | undefined;
14
46
  declare const createBrowserClient: (opt: BaseOptions) => {
15
47
  client: {
16
- report: (data: ClientReportData) => void;
48
+ track: TrackFunction;
49
+ flush: () => Promise<void>;
17
50
  bindUser: (id: string) => void;
18
51
  unBindUser: () => void;
19
52
  enableLogger: () => void;
20
53
  disableLogger: () => void;
54
+ setToken: (token?: string) => void;
55
+ registerGlobalProperties: (props: RegisterProperties) => void;
56
+ enableTracking: (flag?: boolean) => boolean;
21
57
  };
22
58
  beforeClose: () => void;
23
59
  };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,13 @@
1
1
  import { BaseOptions } from './interface';
2
2
  declare const init: (opt: BaseOptions) => {
3
- report: (data: import("./interface").ClientReportData) => void;
3
+ track: import("./interface").TrackFunction;
4
+ flush: () => Promise<void>;
4
5
  bindUser: (id: string) => void;
5
6
  unBindUser: () => void;
6
7
  enableLogger: () => void;
7
8
  disableLogger: () => void;
9
+ setToken: (token?: string | undefined) => void;
10
+ registerGlobalProperties: (props: import("./interface").RegisterProperties) => void;
11
+ enableTracking: (flag?: boolean | undefined) => boolean;
8
12
  };
9
13
  export default init;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- const e=!!navigator?.sendBeacon,t=new Int32Array(4);class n{static hashStr(e,t=!1){return this.onePassHasher.start().appendStr(e).end(t)}static hashAsciiStr(e,t=!1){return this.onePassHasher.start().appendAsciiStr(e).end(t)}static stateIdentity=new Int32Array([1732584193,-271733879,-1732584194,271733878]);static buffer32Identity=new Int32Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);static hexChars="0123456789abcdef";static hexOut=[];static onePassHasher=new n;static _hex(e){const t=n.hexChars,r=n.hexOut;let o,i,a,s;for(s=0;s<4;s+=1)for(i=8*s,o=e[s],a=0;a<8;a+=2)r[i+1+a]=t.charAt(15&o),o>>>=4,r[i+0+a]=t.charAt(15&o),o>>>=4;return r.join("")}static _md5cycle(e,t){let n=e[0],r=e[1],o=e[2],i=e[3];n+=(r&o|~r&i)+t[0]-680876936|0,n=(n<<7|n>>>25)+r|0,i+=(n&r|~n&o)+t[1]-389564586|0,i=(i<<12|i>>>20)+n|0,o+=(i&n|~i&r)+t[2]+606105819|0,o=(o<<17|o>>>15)+i|0,r+=(o&i|~o&n)+t[3]-1044525330|0,r=(r<<22|r>>>10)+o|0,n+=(r&o|~r&i)+t[4]-176418897|0,n=(n<<7|n>>>25)+r|0,i+=(n&r|~n&o)+t[5]+1200080426|0,i=(i<<12|i>>>20)+n|0,o+=(i&n|~i&r)+t[6]-1473231341|0,o=(o<<17|o>>>15)+i|0,r+=(o&i|~o&n)+t[7]-45705983|0,r=(r<<22|r>>>10)+o|0,n+=(r&o|~r&i)+t[8]+1770035416|0,n=(n<<7|n>>>25)+r|0,i+=(n&r|~n&o)+t[9]-1958414417|0,i=(i<<12|i>>>20)+n|0,o+=(i&n|~i&r)+t[10]-42063|0,o=(o<<17|o>>>15)+i|0,r+=(o&i|~o&n)+t[11]-1990404162|0,r=(r<<22|r>>>10)+o|0,n+=(r&o|~r&i)+t[12]+1804603682|0,n=(n<<7|n>>>25)+r|0,i+=(n&r|~n&o)+t[13]-40341101|0,i=(i<<12|i>>>20)+n|0,o+=(i&n|~i&r)+t[14]-1502002290|0,o=(o<<17|o>>>15)+i|0,r+=(o&i|~o&n)+t[15]+1236535329|0,r=(r<<22|r>>>10)+o|0,n+=(r&i|o&~i)+t[1]-165796510|0,n=(n<<5|n>>>27)+r|0,i+=(n&o|r&~o)+t[6]-1069501632|0,i=(i<<9|i>>>23)+n|0,o+=(i&r|n&~r)+t[11]+643717713|0,o=(o<<14|o>>>18)+i|0,r+=(o&n|i&~n)+t[0]-373897302|0,r=(r<<20|r>>>12)+o|0,n+=(r&i|o&~i)+t[5]-701558691|0,n=(n<<5|n>>>27)+r|0,i+=(n&o|r&~o)+t[10]+38016083|0,i=(i<<9|i>>>23)+n|0,o+=(i&r|n&~r)+t[15]-660478335|0,o=(o<<14|o>>>18)+i|0,r+=(o&n|i&~n)+t[4]-405537848|0,r=(r<<20|r>>>12)+o|0,n+=(r&i|o&~i)+t[9]+568446438|0,n=(n<<5|n>>>27)+r|0,i+=(n&o|r&~o)+t[14]-1019803690|0,i=(i<<9|i>>>23)+n|0,o+=(i&r|n&~r)+t[3]-187363961|0,o=(o<<14|o>>>18)+i|0,r+=(o&n|i&~n)+t[8]+1163531501|0,r=(r<<20|r>>>12)+o|0,n+=(r&i|o&~i)+t[13]-1444681467|0,n=(n<<5|n>>>27)+r|0,i+=(n&o|r&~o)+t[2]-51403784|0,i=(i<<9|i>>>23)+n|0,o+=(i&r|n&~r)+t[7]+1735328473|0,o=(o<<14|o>>>18)+i|0,r+=(o&n|i&~n)+t[12]-1926607734|0,r=(r<<20|r>>>12)+o|0,n+=(r^o^i)+t[5]-378558|0,n=(n<<4|n>>>28)+r|0,i+=(n^r^o)+t[8]-2022574463|0,i=(i<<11|i>>>21)+n|0,o+=(i^n^r)+t[11]+1839030562|0,o=(o<<16|o>>>16)+i|0,r+=(o^i^n)+t[14]-35309556|0,r=(r<<23|r>>>9)+o|0,n+=(r^o^i)+t[1]-1530992060|0,n=(n<<4|n>>>28)+r|0,i+=(n^r^o)+t[4]+1272893353|0,i=(i<<11|i>>>21)+n|0,o+=(i^n^r)+t[7]-155497632|0,o=(o<<16|o>>>16)+i|0,r+=(o^i^n)+t[10]-1094730640|0,r=(r<<23|r>>>9)+o|0,n+=(r^o^i)+t[13]+681279174|0,n=(n<<4|n>>>28)+r|0,i+=(n^r^o)+t[0]-358537222|0,i=(i<<11|i>>>21)+n|0,o+=(i^n^r)+t[3]-722521979|0,o=(o<<16|o>>>16)+i|0,r+=(o^i^n)+t[6]+76029189|0,r=(r<<23|r>>>9)+o|0,n+=(r^o^i)+t[9]-640364487|0,n=(n<<4|n>>>28)+r|0,i+=(n^r^o)+t[12]-421815835|0,i=(i<<11|i>>>21)+n|0,o+=(i^n^r)+t[15]+530742520|0,o=(o<<16|o>>>16)+i|0,r+=(o^i^n)+t[2]-995338651|0,r=(r<<23|r>>>9)+o|0,n+=(o^(r|~i))+t[0]-198630844|0,n=(n<<6|n>>>26)+r|0,i+=(r^(n|~o))+t[7]+1126891415|0,i=(i<<10|i>>>22)+n|0,o+=(n^(i|~r))+t[14]-1416354905|0,o=(o<<15|o>>>17)+i|0,r+=(i^(o|~n))+t[5]-57434055|0,r=(r<<21|r>>>11)+o|0,n+=(o^(r|~i))+t[12]+1700485571|0,n=(n<<6|n>>>26)+r|0,i+=(r^(n|~o))+t[3]-1894986606|0,i=(i<<10|i>>>22)+n|0,o+=(n^(i|~r))+t[10]-1051523|0,o=(o<<15|o>>>17)+i|0,r+=(i^(o|~n))+t[1]-2054922799|0,r=(r<<21|r>>>11)+o|0,n+=(o^(r|~i))+t[8]+1873313359|0,n=(n<<6|n>>>26)+r|0,i+=(r^(n|~o))+t[15]-30611744|0,i=(i<<10|i>>>22)+n|0,o+=(n^(i|~r))+t[6]-1560198380|0,o=(o<<15|o>>>17)+i|0,r+=(i^(o|~n))+t[13]+1309151649|0,r=(r<<21|r>>>11)+o|0,n+=(o^(r|~i))+t[4]-145523070|0,n=(n<<6|n>>>26)+r|0,i+=(r^(n|~o))+t[11]-1120210379|0,i=(i<<10|i>>>22)+n|0,o+=(n^(i|~r))+t[2]+718787259|0,o=(o<<15|o>>>17)+i|0,r+=(i^(o|~n))+t[9]-343485551|0,r=(r<<21|r>>>11)+o|0,e[0]=n+e[0]|0,e[1]=r+e[1]|0,e[2]=o+e[2]|0,e[3]=i+e[3]|0}_dataLength=0;_bufferLength=0;_state=new Int32Array(4);_buffer=new ArrayBuffer(68);_buffer8;_buffer32;constructor(){this._buffer8=new Uint8Array(this._buffer,0,68),this._buffer32=new Uint32Array(this._buffer,0,17),this.start()}start(){return this._dataLength=0,this._bufferLength=0,this._state.set(n.stateIdentity),this}appendStr(e){const t=this._buffer8,r=this._buffer32;let o,i,a=this._bufferLength;for(i=0;i<e.length;i+=1){if(o=e.charCodeAt(i),o<128)t[a++]=o;else if(o<2048)t[a++]=192+(o>>>6),t[a++]=63&o|128;else if(o<55296||o>56319)t[a++]=224+(o>>>12),t[a++]=o>>>6&63|128,t[a++]=63&o|128;else{if(o=1024*(o-55296)+(e.charCodeAt(++i)-56320)+65536,o>1114111)throw new Error("Unicode standard supports code points up to U+10FFFF");t[a++]=240+(o>>>18),t[a++]=o>>>12&63|128,t[a++]=o>>>6&63|128,t[a++]=63&o|128}a>=64&&(this._dataLength+=64,n._md5cycle(this._state,r),a-=64,r[0]=r[16])}return this._bufferLength=a,this}appendAsciiStr(e){const t=this._buffer8,r=this._buffer32;let o,i=this._bufferLength,a=0;for(;;){for(o=Math.min(e.length-a,64-i);o--;)t[i++]=e.charCodeAt(a++);if(i<64)break;this._dataLength+=64,n._md5cycle(this._state,r),i=0}return this._bufferLength=i,this}appendByteArray(e){const t=this._buffer8,r=this._buffer32;let o,i=this._bufferLength,a=0;for(;;){for(o=Math.min(e.length-a,64-i);o--;)t[i++]=e[a++];if(i<64)break;this._dataLength+=64,n._md5cycle(this._state,r),i=0}return this._bufferLength=i,this}getState(){const e=this._state;return{buffer:String.fromCharCode.apply(null,Array.from(this._buffer8)),buflen:this._bufferLength,length:this._dataLength,state:[e[0],e[1],e[2],e[3]]}}setState(e){const t=e.buffer,n=e.state,r=this._state;let o;for(this._dataLength=e.length,this._bufferLength=e.buflen,r[0]=n[0],r[1]=n[1],r[2]=n[2],r[3]=n[3],o=0;o<t.length;o+=1)this._buffer8[o]=t.charCodeAt(o)}end(e=!1){const r=this._bufferLength,o=this._buffer8,i=this._buffer32,a=1+(r>>2);this._dataLength+=r;const s=8*this._dataLength;if(o[r]=128,o[r+1]=o[r+2]=o[r+3]=0,i.set(n.buffer32Identity.subarray(a),a),r>55&&(n._md5cycle(this._state,i),i.set(n.buffer32Identity)),s<=4294967295)i[14]=s;else{const n=s.toString(16).match(/(.*?)(.{0,8})$/);if(null===n)return e?t:"";const r=parseInt(n[2],16),o=parseInt(n[1],16)||0;i[14]=r,i[15]=o}return n._md5cycle(this._state,i),e?this._state:n._hex(this._state)}}if("5d41402abc4b2a76b9719d911017c592"!==n.hashStr("hello"))throw new Error("Md5 self test failed.");const r=window.requestIdleCallback||window.requestAnimationFrame||(e=>setTimeout(e,17));function o(e,t=150,n=!1){let r=null;return function(...o){const i=this,a=n&&!r;r&&clearTimeout(r),r=setTimeout((()=>{r=null,n||e.apply(i,o)}),t),a&&e.apply(i,o)}}const i=(e,t=300)=>{let n=0;return function(...r){const o=Date.now();o-n>=t&&(n=o,e.apply(this,r))}};function a(e,t=""){if(!e)return t;const n=e.parentElement,{id:r}=e,o=e.tagName.toLowerCase(),i=t?`>${t}`:"";return n&&n!==document.documentElement&&n!==document.body?r?`#${r}${i}`:a(n,`${o}${i}`):`${o}${i}`}const s=()=>{if(window.performance?.navigation)return performance.navigation.type;if(window.performance?.getEntriesByType){const e=performance.getEntriesByType("navigation");if(e.length>0)return e[0].type}return 255},c=e=>{"prerendering"in document&&document.prerendering?(e=>{"prerendering"in document&&document.prerendering?addEventListener("prerenderingchange",(()=>e()),!0):e()})((()=>c(e))):"complete"!==document?.readyState?addEventListener("load",(()=>c(e)),!0):setTimeout(e)};function l(e){return"number"!=typeof e||!isFinite(e)||e<0?null:Math.round(100*e)/100}const d=Array(32);const u=e=>e instanceof ErrorEvent?"Script error."===e.message?"cors":"js":"resource",m=e=>n.hashStr(e);const p={view:"#3b82f6",click:"#06b6d4",expose:"#8b5cf6",enter:"#10b981",leave:"#f59e0b",stay:"#84cc16",cart:"#ec4899",buy:"#ef4444",pay:"#dc2626",default:"#6b7280"},f={view:"👁️",click:"👆",expose:"✨",enter:"🚪",leave:"👋",stay:"⏱️",cart:"🛒",buy:"💰",pay:"💳",default:"📊"},h={common:"#3b82f6",behavior:"#10b981",performance:"#f59e0b",error:"#ef4444",custom:"#8b5cf6",default:"#6b7280"},g=(e,t)=>{t.forEach(((t,n)=>{const r=t.event_type||"unknown",o=t.data_type||"common",i=(e=>p[e]||p.default)(r),a=(e=>h[e]||h.default)(o),s=(e=>f[e]||f.default)(r);let c=t.event_detail;if("string"==typeof c)try{c=JSON.parse(c)}catch(e){}console.groupCollapsed(`%c${s} [TM-${e}] %c${o.toUpperCase()}%c → %c${r.toUpperCase()}%c #${n+1}`,"color: #6b7280; font-weight: bold;",`color: ${a}; font-weight: bold; background: ${a}20; padding: 2px 6px; border-radius: 3px;`,"color: #6b7280;",`color: ${i}; font-weight: bold; background: ${i}20; padding: 2px 6px; border-radius: 3px;`,"color: #6b7280; font-weight: normal;");const l={...t,event_detail:c};console.log("%c"+JSON.stringify(l,null,2),"color: #6b7280; font-family: monospace; font-size: 11px; line-height: 1.5;"),console.groupEnd()})),console.log(`%c✅ [TM] 本次通过 ${e} 上报 ${t.length} 条埋点数据`,"color: #10b981; font-weight: bold; font-size: 12px; padding: 4px 8px; background: #10b98120; border-radius: 4px; border-left: 3px solid #10b981;")};let y;const v="TRACK_MONITOR_DATA",w="MONITOR_REAL_TIME_DATA",T="TRACK_MONITOR_USER",b="TRACK_MONITOR_LOGGER",_={timer:null,promise:null},E={timer:null,promise:null},S=e=>{const t=JSON.stringify(e);return(new TextEncoder).encode(t).length/1024},L=t=>{y={accessKey:t.accessKey,channelId:t.channelId,siteId:t.siteId,url:t.url||"https://api.tengence.com/v1/push/event",realTimeUrl:t.realTimeUrl||"https://api.tengence.com/v1/push/real-time-event",cacheNumber:t.cacheNumber||30,reportInterval:t.reportInterval||3e4,realTimeReportInterval:t.realTimeReportInterval||200,maxCacheSize:t.maxCacheSize||57344,maxRetries:t.maxRetries||10,isLogger:!!t.isLogger,errorTraceRate:t.errorTraceRate||.5,traceBehavior:t.traceBehavior??!0,tracePerformance:t.tracePerformance??!0,traceError:t.traceError??!0};let{url:n,realTimeUrl:o,cacheNumber:i,reportInterval:a,realTimeReportInterval:s,maxCacheSize:c,maxRetries:l}=y,d=0;const u=e=>{try{localStorage.setItem(T,e)}catch(e){console.error("[TM]: monitor bind user fail: ",e)}},m=()=>{localStorage.removeItem(T)},p=e=>{let t=[];try{t=JSON.parse(localStorage.getItem(e)||"[]")}catch(e){console.error("[TM]: get monitor data fail: ",e)}return t},f=(e,t)=>{try{localStorage.setItem(e,JSON.stringify(t))}catch(n){console.error("[TM]: save monitor data error: ",t),localStorage.setItem(e,"[]")}},h=(t,r)=>{let i=t?o:n;if(i=i.includes("?")?`${i}&accessKey=${y?.accessKey}`:`${i}?accessKey=${y?.accessKey}`,e){const e=((e,t)=>navigator.sendBeacon(e,t))(i,new Blob([JSON.stringify({data:r})],{type:"application/json"}));if(e)return y?.isLogger&&g("SendBeacon",r),Promise.resolve()}return y?.isLogger&&g("XHR",r),(({method:e,url:t,data:n,headers:r={},withCredentials:o=!1})=>new Promise(((i,a)=>{const s=new XMLHttpRequest;s.open(e,t,!0),Object.keys(r).forEach((e=>{s.setRequestHeader(e,r[e])})),s.withCredentials=!!o,s.onreadystatechange=()=>{if(4===s.readyState)if(s.status>=200&&s.status<300)try{const e=JSON.parse(s.responseText);i(e)}catch(e){a(e)}else a(new Error(`[TM]: Request failed with status ${s.status}`))},s.onerror=()=>{a(new Error("[TM]: Network error"))},s.ontimeout=()=>{a(new Error("[TM]: Request timeout"))},n?s.send(n):s.send()})))({url:i,method:"POST",headers:{"Content-Type":"application/json"},data:JSON.stringify({data:r}),withCredentials:!0})},L=(e,t=!1)=>{const n=p(e);if(!n.length)return;const r=(e=>e===w)(e),o=r?E:_;if(!d&&(n.length>=i||t))return null!==o.timer&&(clearTimeout(o.timer),o.timer=null),f(e,[]),void h(r,n).then((()=>{d=0})).catch((()=>{console.error("[TM]: send report fail, retryCount: ",d),C(e,n)})).finally((()=>{L(e)}));if(null===o.timer){const t=(d+1)*(r?s:a);o.timer=setTimeout((()=>{const t=p(e);t.length?o.promise||(f(e,[]),o.promise=h(r,t).then((()=>{d=0})).catch((()=>{C(e,t)})).finally((()=>{o.timer=null,o.promise=null,L(e)}))):o.timer=null}),t)}},C=(e,t)=>{if(d++,d>l)return void(d=0);const n=p(e);f(e,n.concat(t))},A=e=>{if(!e||"object"!=typeof e)return void console.error("[TM]: report data must be an object");const t=!0===e.track_real_time,n=(e=>({session_id:e.session_id,request_id:e.request_id,spm:e.spm,data_type:e.data_type||"behavior",event_type:e.event_type,event_detail:"string"==typeof e.event_detail?e.event_detail:JSON.stringify(e.event_detail),page_url:location.href,os:window.navigator.platform,user_id:localStorage.getItem(T)||void 0,user_agent:window.navigator.userAgent,screen_width:window.screen.width,screen_height:window.screen.height,lib_version:"1.1.4",lib_type:"js",event_time:(new Date).getTime().toString(),timezone_offset:(new Date).getTimezoneOffset().toString(),site_id:y?.siteId,channel_id:y?.channelId}))(e);try{if(t){let e=[n];const t=p(w);t&&(e=e.concat(t)),f(w,e);const r=S(e);L(w,r>=c)}let e=[n];const r=p(v);r&&(e=e.concat(r)),f(v,e);const o=S(e);L(v,o>=c)}catch(e){console.error("[TM]: report fail: ",e)}},I=()=>{y.isLogger=!0;try{localStorage.setItem(b,"true")}catch(e){console.error("[TM]: enable logger fail:",e)}},M=()=>{y.isLogger=!1;try{localStorage.setItem(b,"false")}catch(e){console.error("[TM]: disable logger fail:",e)}};return r((()=>{L(v,!0),L(w,!0)})),window.trackMonitor={report:A,bindUser:u,unBindUser:m,enableLogger:I,disableLogger:M},{client:{report:A,bindUser:u,unBindUser:m,enableLogger:I,disableLogger:M},beforeClose:()=>{window.addEventListener("beforeunload",(()=>{L(v,!0),L(w,!0)}))}}},C=(A=16,function(){for(let e=0;e<2*A;e++)d[e]=Math.floor(16*Math.random())+48,d[e]>=58&&(d[e]+=39);return String.fromCharCode.apply(null,d.slice(0,2*A))})();var A;const I=e=>{const t=history[e];return function(){const n=t.apply(this,arguments),r=new Event(e);return window.dispatchEvent(r),n}},M=e=>{window.addEventListener("replaceState",(t=>e(t)),!0),window.addEventListener("pushState",(t=>e(t)),!0)},x=e=>{window.addEventListener("popstate",(t=>e(t)),!0)},k=()=>{history.pushState=I("pushState"),history.replaceState=I("replaceState"),(()=>{let e="";const t=()=>{const t=window.location.href;if(t===e)return;const{host:n,hostname:r,href:o,protocol:i,origin:a,port:c,pathname:l,search:d,hash:u}=window.location,m={host:n,hostname:r,href:o,protocol:i,origin:a,port:c,pathname:l,search:d,hash:u,title:document.title,language:navigator.language.substring(0,2),user_agent:navigator.userAgent,win_screen:`${window.screen.width}x${window.screen.height}`,doc_screen:`${document.documentElement.clientWidth}x${document.documentElement.clientHeight}`,referrer:document.referrer,type:s()};window.trackMonitor.report({data_type:"common",event_type:"page_view",event_detail:m}),e=t},n=o(t,200);t(),x((()=>{n()})),M((()=>{n()}))})(),(()=>{let e=document.referrer,t=window.location.href,n=Date.now(),r=!0;const i=(r=!1)=>{if(window.location.href!==t||r){const r=Date.now()-n;r>200&&window.trackMonitor.report({data_type:"common",event_type:"page_leave",event_detail:{url:t,referrer:e,duration:r}}),n=Date.now(),e=t,t=window.location.href}},a=o(i);i(),x((()=>{a()})),M((()=>{a()})),document.addEventListener("visibilitychange",(()=>{"hidden"===document.visibilityState?r&&(r=!1,i(!0)):(r=!0,n=Date.now())})),window.addEventListener("beforeunload",(()=>{r&&(r=!1,i(!0))}),!0)})(),(()=>{const e=i((e=>{const t=function(e){if(!e)return;if(["A","INPUT","TEXTAREA","BUTTON"].includes(e.tagName))return e;if(e.hasAttribute("track-click"))return e;return e.closest("a, button, [track-click]")||void 0}(e.target);if(!t)return;const n={element_id:t.id,element_content:t.textContent?.trim()||null,element_class_name:t.className||null,element_name:t.getAttribute("name")||null,element_type:t.tagName.toLowerCase(),element_path:a(t),scroll_width:document.documentElement.scrollWidth,scroll_height:document.documentElement.scrollHeight,pageX:e.pageX,pageY:e.pageY,ratio:window.devicePixelRatio||1};window.trackMonitor.report({data_type:"common",event_type:"click",event_detail:n})}));window.addEventListener("click",e,!0)})()};let R=null,$=null;const q="track-expose",N="track-view",P=e=>{try{$=new WeakMap}catch(e){console.error("[TM]: create WeakMap fail: ",e)}const t=(e,t=!1)=>{const r=$?.get(e);if(r&&!t)return;r&&(r.viewTimer&&clearTimeout(r.viewTimer),r.exposeTimer&&clearTimeout(r.exposeTimer));const o=e.dataset.durationView||500,i=e.dataset.durationExpose||500;$?.set(e,{duration_view:o,duration_expose:i,viewTimer:null,exposeTimer:null}),R?.observe(e),t&&function(e){if(!(e instanceof Element))return!1;if(!function e(t){if(!t)return!0;const n=window.getComputedStyle(t);return"none"!==n.display&&"hidden"!==n.visibility&&0!==parseFloat(n.opacity)&&e(t.parentElement)}(e))return!1;const t=e.getBoundingClientRect();return!(t.top>=0&&t.left>=0&&t.bottom<=(window.innerHeight||document.documentElement.clientHeight)&&t.right<=(window.innerWidth||document.documentElement.clientWidth))}(e)&&n(e)},n=t=>{const n=$?.get(t);if(!n)return;const o=t.dataset.eventDetail,i=t.dataset.traceid??void 0,a=t.dataset.spm,s=t.hasAttribute("track-real-time"),c=t.hasAttribute(N),l=t.hasAttribute(q);c&&l?n.exposeTimer=window.setTimeout((()=>{e({spm:a,request_id:i,data_type:"common",event_type:"view_expose",event_detail:o,track_real_time:s}),t.removeAttribute(N),n.exposeTimer=null}),parseInt(n.duration_expose,10)):(c&&(n.viewTimer=window.setTimeout((()=>{e({spm:a,request_id:i,data_type:"common",event_type:"view",event_detail:o,track_real_time:s}),t.removeAttribute(N),l&&n.exposeTimer||r(t),n.viewTimer=null}),parseInt(n.duration_view,10))),l&&(n.exposeTimer=window.setTimeout((()=>{e({spm:a,request_id:i,data_type:"common",event_type:"expose",event_detail:o,track_real_time:s}),n.exposeTimer=null}),parseInt(n.duration_expose,10))))},r=e=>{const t=$?.get(e);t&&(R?.unobserve(e),t.viewTimer&&clearTimeout(t.viewTimer),t.exposeTimer&&clearTimeout(t.exposeTimer),$?.delete(e))},o=e=>{e.forEach((e=>{const t=e.target,o=$?.get(t);o&&(o.viewTimer&&(clearTimeout(o.viewTimer),o.viewTimer=null),o.exposeTimer&&(clearTimeout(o.exposeTimer),o.exposeTimer=null),e.isIntersecting&&(n(t),t.hasAttribute(N)||t.hasAttribute(q)||r(t)))}))};R=(()=>{let e=null;try{e=new IntersectionObserver(o,{root:null,threshold:.5})}catch(e){console.error("[TM]: create IntersectionObserver fail: ",e)}return e})();new Set([...document.querySelectorAll(`[${q}]`),...document.querySelectorAll(`[${N}]`)]).forEach((e=>t(e))),new MutationObserver((e=>{e.forEach((e=>{if("attributes"===e.type&&e.target instanceof HTMLElement){const n=e.target,o=e.attributeName,i=["data-event-detail","data-traceid","data-spm","data-duration-view","data-duration-expose"];n.hasAttribute(q)||n.hasAttribute(N)?i.includes(o)?t(n,!0):o!==q&&o!==N||t(n,!1):r(n)}else"childList"===e.type&&(e.addedNodes.forEach((e=>{if(e instanceof HTMLElement)if(e.hasAttribute(q)||e.hasAttribute(N))t(e);else{new Set([...e.querySelectorAll(`[${q}]`),...e.querySelectorAll(`[${N}]`)]).forEach((e=>t(e)))}})),e.removedNodes.forEach((e=>{if(e instanceof HTMLElement){const t=$?.get(e);if(t)r(e);else{new Set([...e.querySelectorAll(`[${q}]`),...e.querySelectorAll(`[${N}]`)]).forEach((e=>r(e)))}}})))}))})).observe(document.body,{attributes:!0,childList:!0,subtree:!0})},B=/^\s*at (?:(.*?) ?\()?((?:file|https?|blob|chrome-extension|address|native|eval|webpack|<anonymous>|[-a-z]+:|.*bundle|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i;function O(e){const t=e.match(B);if(!t)return{};return{filename:t[2],functionName:t[1]||"",lineno:parseInt(t[3],10)||void 0,colno:parseInt(t[4],10)||void 0}}function H(e){try{const{stack:t}=e;if(!t)return[];const n=[];for(const e of t.split("\n").slice(1)){const t=O(e);!t||t.filename?.includes("chrome-extension://")||t.filename?.includes("node_modules")||t.filename?.includes("<anonymous>")||n.push(t)}return n.slice(0,10)}catch(e){return[]}}const F=[],z=(e,t)=>{var n;((n=y?.errorTraceRate??.5)<0||n>1?(console.warn("[TM]: probability should be between 0 and 1"),0):Math.random()<n)&&(window.trackMonitor.report(t),F.push(e))},U=()=>{window.addEventListener("error",(e=>{(e=>{if("js"!==u(e))return;if((e=>{const t=e.error?.stack||"";return/chrome-extension:\/\//.test(t)})(e))return;const t=m(`js-${e.message}-${e.filename}`);if(F.includes(t))return;const n=H(e.error),r={traceId:C,msg:e.message,target:n[0]?`${n[0].filename?.split("?")[0]}:${n[0].lineno}:${n[0].colno}`:"",stack:n,errorUuid:t,meta:{filename:e.filename,colno:e.colno,lineno:e.lineno}};z(t,{data_type:"error",event_type:"js",event_detail:r})})(e)}),!0)},D=()=>{window.addEventListener("error",(e=>{(e=>{if("resource"!==u(e))return;const t=e.target,n=m(`resource-${t.src}-${t.tagName}`);if(F.includes(n))return;const r={traceId:C,msg:`加载${t.tagName}失败`,target:t.src?.split("?")[0],errorUuid:n,meta:{url:t.src,html:t.outerHTML,tagName:t.tagName}};z(n,{data_type:"error",event_type:"resource",event_detail:r})})(e)}),!0)},j=()=>{window.addEventListener("unhandledrejection",(e=>(e=>{const t=e.reason?.message||e.reason,n=m(`unhandledrejection-${t}`);if(F.includes(n))return;const r={traceId:C,msg:t,stack:H(e.reason),errorUuid:n};z(n,{data_type:"error",event_type:"unhandledrejection",event_detail:r})})(e)),!0)},K=()=>{const e=(e,t,n)=>{if(t){const r=m(`http-${t}-${e.url}-${n}`);if(F.includes(r))return;const o={traceId:C,errorType:t,msg:n,target:e.url.split("?")[0],errorUuid:r,request:e};z(r,{data_type:"error",event_type:"http",event_detail:o})}else if(e.status>=400){let n=e.statusText;const r=m(`http-${t="http_error"}-${e.url}-${n}`);if(F.includes(r))return;const o={traceId:C,errorUuid:r,errorType:t,target:e.url.split("?")[0],msg:n,request:e};z(r,{data_type:"error",event_type:"http",event_detail:o})}};((e,t)=>{if("XMLHttpRequest"in window&&"function"==typeof window.XMLHttpRequest){const e=window.XMLHttpRequest;window.oXMLHttpRequest||(window.oXMLHttpRequest=e),window.XMLHttpRequest=function(){const n=new e,{open:r,send:o}=n;let i={};n.open=(e,t,o,a,s)=>{void 0===o&&(o=!0),i.method=e,i.url=t,r.call(n,e,t,o,a,s)},n.send=e=>{i.requestTime=(new Date).getTime(),o.call(n,e)};const a=(e,r)=>{const{status:o,statusText:a}=n,s=(new Date).getTime();i={...i,status:o,statusText:a,responseTime:s,duration:s-i.requestTime},"function"==typeof t&&t(i,e,r)};return n.addEventListener("loadend",(()=>a())),n.addEventListener("error",(()=>a("net_error","ERR_NETWORK"))),n.addEventListener("abort",(()=>a("cannel_request","ERR_CANCELED"))),n.addEventListener("timeout",(()=>a("timeout","ERR_NETWORK"))),n}}})(0,e),((e,t)=>{if("fetch"in window&&"function"==typeof window.fetch){const e=window.fetch;window.oFetch||(window.oFetch=e),window.fetch=async(n,r)=>{let o={};return o.method=r?.method||"",o.url=(n&&"string"!=typeof n?n?.url:n)||"",o.requestTime=(new Date).getTime(),e.call(window,n,r).then((async e=>{if("function"==typeof t){const n=e.clone(),r=(new Date).getTime();o={...o,status:n.status,statusText:n.statusText,responseTime:r,duration:r-o.requestTime},t(o)}return e})).catch((e=>{if("function"==typeof t){const n=(new Date).getTime();o={...o,responseTime:n,duration:n-o.requestTime},"AbortError"===e.name?t(o,"cannel_request","ERR_CANCELED"):t(o,"net_error","ERR_NETWORK")}return Promise.reject(e)}))}}})(0,e)};let X=-1;const W=e=>{addEventListener("pageshow",(t=>{t.persisted&&(X=t.timeStamp,e(t))}),!0)},J=(e,t,n,r)=>{let o,i;return a=>{t.value>=0&&(a||r)&&(i=t.value-(o??0),(i||void 0===o)&&(o=t.value,t.delta=i,t.rating=((e,t)=>e>t[1]?"poor":e>t[0]?"needs-improvement":"good")(t.value,n),e(t)))}},G=e=>{requestAnimationFrame((()=>requestAnimationFrame((()=>e()))))},V=()=>{const e=performance.getEntriesByType("navigation")[0];if(e&&e.responseStart>0&&e.responseStart<performance.now())return e},Y=()=>{const e=V();return e?.activationStart??0},Q=(e,t=-1)=>{const n=V();let r="navigate";return X>=0?r="back-forward-cache":n&&(document.prerendering||Y()>0?r="prerender":document.wasDiscarded?r="restore":n.type&&(r=n.type.replace(/_/g,"-"))),{name:e,value:t,rating:"good",delta:0,entries:[],id:`v5-${Date.now()}-${Math.floor(8999999999999*Math.random())+1e12}`,navigationType:r}},Z=new WeakMap;function ee(e,t){return Z.get(e)||Z.set(e,new t),Z.get(e)}class te{t;i=0;o=[];h(e){if(e.hadRecentInput)return;const t=this.o[0],n=this.o.at(-1);this.i&&t&&n&&e.startTime-n.startTime<1e3&&e.startTime-t.startTime<5e3?(this.i+=e.value,this.o.push(e)):(this.i=e.value,this.o=[e]),this.t?.(e)}}const ne=(e,t,n={})=>{try{if(PerformanceObserver.supportedEntryTypes.includes(e)){const r=new PerformanceObserver((e=>{Promise.resolve().then((()=>{t(e.getEntries())}))}));return r.observe({type:e,buffered:!0,...n}),r}}catch{}},re=e=>{let t=!1;return()=>{t||(e(),t=!0)}};let oe=-1;const ie=new Set,ae=()=>"hidden"!==document.visibilityState||document.prerendering?1/0:0,se=e=>{if("hidden"===document.visibilityState){if("visibilitychange"===e.type)for(const e of ie)e();isFinite(oe)||(oe="visibilitychange"===e.type?e.timeStamp:0,removeEventListener("prerenderingchange",se,!0))}},ce=()=>{if(oe<0){const e=Y(),t=document.prerendering?void 0:globalThis.performance.getEntriesByType("visibility-state").filter((t=>"hidden"===t.name&&t.startTime>e))[0]?.startTime;oe=t??ae(),addEventListener("visibilitychange",se,!0),addEventListener("prerenderingchange",se,!0),W((()=>{setTimeout((()=>{oe=ae()}))}))}return{get firstHiddenTime(){return oe},onHidden(e){ie.add(e)}}},le=e=>{document.prerendering?addEventListener("prerenderingchange",(()=>e()),!0):e()},de=[1800,3e3],ue=(e,t={})=>{le((()=>{const n=ce();let r,o=Q("FCP");const i=ne("paint",(e=>{for(const t of e)"first-contentful-paint"===t.name&&(i.disconnect(),t.startTime<n.firstHiddenTime&&(o.value=Math.max(t.startTime-Y(),0),o.entries.push(t),r(!0)))}));i&&(r=J(e,o,de,t.reportAllChanges),W((n=>{o=Q("FCP"),r=J(e,o,de,t.reportAllChanges),G((()=>{o.value=performance.now()-n.timeStamp,r(!0)}))})))}))},me=[.1,.25];let pe=0,fe=1/0,he=0;const ge=e=>{for(const t of e)t.interactionId&&(fe=Math.min(fe,t.interactionId),he=Math.max(he,t.interactionId),pe=he?(he-fe)/7+1:0)};let ye;const ve=()=>ye?pe:performance.interactionCount??0;let we=0;class Te{u=[];l=new Map;m;p;v(){we=ve(),this.u.length=0,this.l.clear()}L(){const e=Math.min(this.u.length-1,Math.floor((ve()-we)/50));return this.u[e]}h(e){if(this.m?.(e),!e.interactionId&&"first-input"!==e.entryType)return;const t=this.u.at(-1);let n=this.l.get(e.interactionId);if(n||this.u.length<10||e.duration>t.P){if(n?e.duration>n.P?(n.entries=[e],n.P=e.duration):e.duration===n.P&&e.startTime===n.entries[0].startTime&&n.entries.push(e):(n={id:e.interactionId,entries:[e],P:e.duration},this.l.set(n.id,n),this.u.push(n)),this.u.sort(((e,t)=>t.P-e.P)),this.u.length>10){const e=this.u.splice(10);for(const t of e)this.l.delete(t.id)}this.p?.(n)}}}const be=e=>{const t=globalThis.requestIdleCallback||setTimeout;"hidden"===document.visibilityState?e():(e=re(e),addEventListener("visibilitychange",e,{once:!0,capture:!0}),t((()=>{e(),removeEventListener("visibilitychange",e,{capture:!0})})))},_e=[200,500],Ee=(e,t={})=>{if(!globalThis.PerformanceEventTiming||!("interactionId"in PerformanceEventTiming.prototype))return;const n=ce();le((()=>{"interactionCount"in performance||ye||(ye=ne("event",ge,{type:"event",buffered:!0,durationThreshold:0}));let r,o=Q("INP");const i=ee(t,Te),a=e=>{be((()=>{for(const t of e)i.h(t);const t=i.L();t&&t.P!==o.value&&(o.value=t.P,o.entries=t.entries,r())}))},s=ne("event",a,{durationThreshold:t.durationThreshold??40});r=J(e,o,_e,t.reportAllChanges),s&&(s.observe({type:"first-input",buffered:!0}),n.onHidden((()=>{a(s.takeRecords()),r(!0)})),W((()=>{i.v(),o=Q("INP"),r=J(e,o,_e,t.reportAllChanges)})))}))};class Se{m;h(e){this.m?.(e)}}const Le=[2500,4e3],Ce=[800,1800],Ae=e=>{document.prerendering?le((()=>Ae(e))):"complete"!==document.readyState?addEventListener("load",(()=>Ae(e)),!0):setTimeout(e)};function Ie(){const e={ttfb:null,lcp:null,fp:null,fcp:null,connectionRtt:null,ttfbRequestTime:null,inp:null,cls:null,dcl:null,loadTime:null};((e,t={})=>{le((()=>{const n=ce();let r,o=Q("LCP");const i=ee(t,Se),a=e=>{t.reportAllChanges||(e=e.slice(-1));for(const t of e)i.h(t),t.startTime<n.firstHiddenTime&&(o.value=Math.max(t.startTime-Y(),0),o.entries=[t],r())},s=ne("largest-contentful-paint",a);if(s){r=J(e,o,Le,t.reportAllChanges);const n=re((()=>{a(s.takeRecords()),s.disconnect(),r(!0)})),i=e=>{e.isTrusted&&(be(n),removeEventListener(e.type,i,{capture:!0}))};for(const e of["keydown","click","visibilitychange"])addEventListener(e,i,{capture:!0});W((n=>{o=Q("LCP"),r=J(e,o,Le,t.reportAllChanges),G((()=>{o.value=performance.now()-n.timeStamp,r(!0)}))}))}}))})((t=>{e.lcp=l(t.value)}),{reportAllChanges:!0}),((e,t={})=>{const n=ce();ue(re((()=>{let r,o=Q("CLS",0);const i=ee(t,te),a=e=>{for(const t of e)i.h(t);i.i>o.value&&(o.value=i.i,o.entries=i.o,r())},s=ne("layout-shift",a);s&&(r=J(e,o,me,t.reportAllChanges),n.onHidden((()=>{a(s.takeRecords()),r(!0)})),W((()=>{i.i=0,o=Q("CLS",0),r=J(e,o,me,t.reportAllChanges),G((()=>r()))})),setTimeout(r))})))})((t=>{e.cls=Math.round(1e4*t.value)/1e4}),{reportAllChanges:!0}),Ee((t=>{e.inp=l(t.value)}),{reportAllChanges:!0}),((e,t={})=>{let n=Q("TTFB"),r=J(e,n,Ce,t.reportAllChanges);Ae((()=>{const o=V();o&&(n.value=Math.max(o.responseStart-Y(),0),n.entries=[o],r(!0),W((()=>{n=Q("TTFB",0),r=J(e,n,Ce,t.reportAllChanges),r(!0)})))}))})((t=>{e.ttfb=l(t.value)})),ue((t=>{e.fcp=l(t.value)}));window.addEventListener("beforeunload",(()=>{(()=>{try{performance.getEntriesByType("paint").forEach((t=>{"first-paint"===t.name&&(e.fp=l(t.startTime))}))}catch(e){console.warn("[TM]: Failed to collect FP:",e)}try{const t=navigator.connection||navigator.mozConnection||navigator.webkitConnection;t&&void 0!==t.rtt&&(e.connectionRtt=t.rtt)}catch(e){console.warn("[TM]: Failed to collect Connection RTT:",e)}try{const t=performance.getEntriesByType("navigation")[0];if(t){const{responseStart:n,requestStart:r}=t;n>0&&r>0&&r<=n&&(e.ttfbRequestTime=l(n-r))}}catch(e){console.warn("[TM]: Failed to collect TTFB Request Time:",e)}try{const t=performance.getEntriesByType("navigation")[0];if(t){const{domContentLoadedEventEnd:n,loadEventEnd:r,fetchStart:o}=t;n>0&&o>0&&(e.dcl=l(n-o)),r>0&&o>0&&(e.loadTime=l(r-o))}}catch(e){console.warn("[TM]: Failed to collect DCL and Load Time:",e)}})(),window.trackMonitor.report({data_type:"performance",event_type:"metrics",event_detail:{traceId:C,data:e}})}))}const Me=e=>{const{client:t,beforeClose:n}=L(e);return P(t.report),!1!==e.traceBehavior&&k(),!1!==e.traceError&&(U(),D(),j(),K()),!1!==e.tracePerformance&&c((()=>{!function(){const e=window.performance||window.msPerformance;if(!e)return{dnsLookupTime:null,tcpConnectTime:null,ttfb:null,transferTime:null,domParseTime:null,domCompleteTime:null,loadEventTime:null,totalPageLoad:null};const t=e.getEntriesByType&&e.getEntriesByType("navigation")&&e.getEntriesByType("navigation")[0];let n=null;if(t)n=t;else{const t=e.timing;t&&(n={startTime:0,domainLookupStart:t.domainLookupStart,domainLookupEnd:t.domainLookupEnd,connectStart:t.connectStart,connectEnd:t.connectEnd,requestStart:t.requestStart,responseStart:t.responseStart,responseEnd:t.responseEnd,domInteractive:t.domInteractive,domComplete:t.domComplete,loadEventEnd:t.loadEventEnd,fetchStart:t.fetchStart},!n.requestStart&&t.fetchStart&&(n.requestStart=t.fetchStart))}const r={dnsLookupTime:n?l(n.domainLookupEnd-n.domainLookupStart):null,tcpConnectTime:n?l(n.connectEnd-n.connectStart):null,ttfb:n?l(n.responseStart-(n.requestStart??n.fetchStart??0)):null,transferTime:n?l(n.responseEnd-n.responseStart):null,domParseTime:n?l(n.domInteractive-n.responseEnd):null,domCompleteTime:n?l(n.domComplete-n.domInteractive):null,loadEventTime:n?l(n.loadEventEnd-(n.domComplete??0)):null,totalPageLoad:n?l((n.loadEventEnd??performance.now())-(n.startTime??0)):null};window.trackMonitor.report({data_type:"performance",event_type:"navigation_timing",event_detail:{traceId:C,data:r}})}(),function(){const e=window.performance||window.msPerformance;if(!e)return[];const t=[];if(e.getEntriesByType){const n=(e.getEntriesByType("resource")||[]).map((e=>{const t="number"==typeof e.redirectEnd&&"number"==typeof e.redirectStart?l(e.redirectEnd-e.redirectStart):null,n="number"==typeof e.domainLookupEnd&&"number"==typeof e.domainLookupStart?l(e.domainLookupEnd-e.domainLookupStart):null,r="number"==typeof e.connectEnd&&"number"==typeof e.connectStart?l(e.connectEnd-e.connectStart):null,o="number"==typeof e.requestStart&&"number"==typeof e.fetchStart?l(e.requestStart-e.fetchStart):null,i="number"==typeof e.responseStart&&"number"==typeof e.requestStart?l(e.responseStart-e.requestStart):null,a="number"==typeof e.responseEnd&&"number"==typeof e.responseStart?l(e.responseEnd-e.responseStart):null,s="number"==typeof e.duration?l(e.duration):null,c="number"==typeof e.startTime?l(e.startTime):null,d="number"==typeof e.transferSize&&isFinite(e.transferSize)&&e.transferSize>=0?Math.round(e.transferSize):null,u="number"==typeof e.encodedBodySize&&isFinite(e.encodedBodySize)&&e.encodedBodySize>=0?Math.round(e.encodedBodySize):null,m="number"==typeof e.decodedBodySize&&isFinite(e.decodedBodySize)&&e.decodedBodySize>=0?Math.round(e.decodedBodySize):null;return{name:e.name,initiatorType:e.initiatorType||"",startTime:c,duration:s,redirectTime:t,cacheTime:o,dnsTime:n,connectTime:r,requestTime:i,responseTime:a,transferSize:d,encodedBodySize:u,decodedBodySize:m}}));n.sort(((e,t)=>(t.duration??0)-(e.duration??0)));for(let e=0;e<Math.min(15,n.length);e++)t.push(n[e])}window.trackMonitor.report({data_type:"performance",event_type:"resource_flow",event_detail:{traceId:C,data:t}})}(),Ie()})),c((()=>{n()})),t};export{Me as default};
1
+ class e extends Error{status;constructor(e,t){super(t||`[TM]: Auth error ${e}`),this.name="AuthError",this.status=e}}const t=({method:e,url:t,data:r,headers:n={},withCredentials:o=!1,timeoutMs:i})=>new Promise(((a,s)=>{const c=new XMLHttpRequest;c.open(e,t,!0),i&&(c.timeout=i),Object.keys(n).forEach((e=>{c.setRequestHeader(e,n[e])})),c.withCredentials=!!o,c.onreadystatechange=()=>{if(4===c.readyState)if(c.status>=200&&c.status<300)try{const e=JSON.parse(c.responseText);a(e)}catch(e){s(e)}else{const e=new Error(`[TM]: Request failed with status ${c.status}`);e.status=c.status,s(e)}},c.onerror=()=>{s(new Error("[TM]: Network error"))},c.ontimeout=()=>{const e=new Error("[TM]: Request timeout");e.timeout=!0,s(e)},r?c.send(r):c.send()})),r=(e,t,r)=>{if(!r)return fetch(e,t);const n=new AbortController,o=setTimeout((()=>n.abort()),r);return fetch(e,{...t,signal:n.signal}).catch((e=>{if(e&&"AbortError"===e.name){const e=new Error("[TM]: Request timeout");throw e.timeout=!0,e}throw e})).finally((()=>clearTimeout(o)))},n=(n,o,i,a)=>{const s={"Content-Type":"application/json"};i&&(s.Authorization=`Bearer ${i}`);const c=()=>t({method:"POST",url:n,data:JSON.stringify(o),headers:s,withCredentials:!0,timeoutMs:a});return"function"!=typeof fetch?c():r(n,{method:"POST",keepalive:!0,credentials:"include",headers:s,body:JSON.stringify(o)},a).then((t=>{if(401===t.status||403===t.status)throw new e(t.status,`[TM]: Auth error ${t.status}`);if(429===t.status){const e=new Error("[TM]: Request throttled with status 429");e.status=429;const r=t.headers.get("Retry-After");if(r){const t=parseInt(r,10);isNaN(t)||(e.retryAfter=1e3*t)}throw e}if(!t.ok){const e=new Error(`[TM]: Request failed with status ${t.status}`);throw e.status=t.status,e}return t.json()})).catch((t=>{if(t instanceof e)throw t;if(t&&t.status)throw t;return c()}))},o=window.requestIdleCallback||window.requestAnimationFrame||(e=>setTimeout(e,17));function i(e,t=150,r=!1){let n=null;return function(...o){const i=this,a=r&&!n;n&&clearTimeout(n),n=setTimeout((()=>{n=null,r||e.apply(i,o)}),t),a&&e.apply(i,o)}}const a=(e,t=300)=>{let r=0;return function(...n){const o=Date.now();o-r>=t&&(r=o,e.apply(this,n))}};function s(e,t=""){if(!e)return t;const r=e.parentElement,{id:n}=e,o=e.tagName.toLowerCase(),i=t?`>${t}`:"";return r&&r!==document.documentElement&&r!==document.body?n?`#${n}${i}`:s(r,`${o}${i}`):`${o}${i}`}const c=()=>{if(window.performance?.navigation)return performance.navigation.type;if(window.performance?.getEntriesByType){const e=performance.getEntriesByType("navigation");if(e.length>0)return e[0].type}return 255},l=e=>{"prerendering"in document&&document.prerendering?(e=>{"prerendering"in document&&document.prerendering?addEventListener("prerenderingchange",(()=>e()),!0):e()})((()=>l(e))):"complete"!==document?.readyState?addEventListener("load",(()=>l(e)),!0):setTimeout(e)};function d(e){return"number"!=typeof e||!isFinite(e)||e<0?null:Math.round(100*e)/100}function u(){const e=new Uint8Array(16);if("undefined"!=typeof crypto&&"function"==typeof crypto.getRandomValues)crypto.getRandomValues(e);else for(let t=0;t<16;t++)e[t]=Math.floor(256*Math.random());e[6]=15&e[6]|64,e[8]=63&e[8]|128;let t="";for(let r=0;r<16;r++)t+=(e[r]<16?"0":"")+e[r].toString(16);return t}const m=e=>e instanceof ErrorEvent?"Script error."===e.message?"cors":"js":"resource",p=e=>(e=>{let t=5381;for(let r=0;r<e.length;r++)t=(t<<5)+t+e.charCodeAt(r)>>>0;return t.toString(16).padStart(8,"0")})(e)+(e=>{let t=5381;for(let r=e.length-1;r>=0;r--)t=((t<<5)+t^e.charCodeAt(r))>>>0;return t.toString(16).padStart(8,"0")})(e);const f={view:"#3b82f6",click:"#06b6d4",expose:"#8b5cf6",enter:"#10b981",leave:"#f59e0b",stay:"#84cc16",cart:"#ec4899",buy:"#ef4444",pay:"#dc2626",default:"#6b7280"},h={view:"👁️",click:"👆",expose:"✨",enter:"🚪",leave:"👋",stay:"⏱️",cart:"🛒",buy:"💰",pay:"💳",default:"📊"},g={common:"#3b82f6",behavior:"#10b981",performance:"#f59e0b",error:"#ef4444",custom:"#8b5cf6",default:"#6b7280"},y=(e,t)=>{t.forEach(((t,r)=>{const n=t.event_type||"unknown",o=t.data_type||"common",i=(e=>f[e]||f.default)(n),a=(e=>g[e]||g.default)(o),s=(e=>h[e]||h.default)(n);let c=t.event_detail;if("string"==typeof c)try{c=JSON.parse(c)}catch(e){}console.groupCollapsed(`%c${s} [TM-${e}] %c${o.toUpperCase()}%c → %c${n.toUpperCase()}%c #${r+1}`,"color: #6b7280; font-weight: bold;",`color: ${a}; font-weight: bold; background: ${a}20; padding: 2px 6px; border-radius: 3px;`,"color: #6b7280;",`color: ${i}; font-weight: bold; background: ${i}20; padding: 2px 6px; border-radius: 3px;`,"color: #6b7280; font-weight: normal;");const l={...t,event_detail:c};console.log("%c"+JSON.stringify(l,null,2),"color: #6b7280; font-family: monospace; font-size: 11px; line-height: 1.5;"),console.groupEnd()})),console.log(`%c✅ [TM] 本次通过 ${e} 上报 ${t.length} 条埋点数据`,"color: #10b981; font-weight: bold; font-size: 12px; padding: 4px 8px; background: #10b98120; border-radius: 4px; border-left: 3px solid #10b981;")};let w;const v="TRACK_MONITOR_DATA",T="MONITOR_REAL_TIME_DATA",_="TRACK_MONITOR_USER",b="tengence_device_id",S="tengence_register_properties",E="TRACK_MONITOR_LOGGER",k="tengence_tracking_enabled",x=2e3,M="tengence_access_token";let A=null,L=null;const C=e=>((e,n,o)=>{const i={"Content-Type":"application/json"},a=()=>t({method:"POST",url:e,data:JSON.stringify(n),headers:i,withCredentials:!0,timeoutMs:o});return"function"!=typeof fetch?a():r(e,{method:"POST",credentials:"include",headers:i,body:JSON.stringify(n)},o).then((e=>{if(!e.ok){const t=new Error(`[TM]: Request failed with status ${e.status}`);throw t.status=e.status,t}return e.json()})).catch((e=>{if(e&&e.status)throw e;return a()}))})(e,{},w?.requestTimeout).then((e=>{const t=e&&e.data,r=t&&t.accessToken,n=t&&t.accessExpAt;if(!r||"number"!=typeof n)throw new Error("[TM]: authUrl 响应缺少 accessToken/accessExpAt");return((e,t)=>{A={token:e,expAt:t};try{localStorage.setItem(M,JSON.stringify({token:e,expAt:t}))}catch(e){}})(r,n),r})),R=e=>{const t=(()=>{if(A)return A;try{const e=localStorage.getItem(M);if(!e)return null;const t=JSON.parse(e);if(t&&"string"==typeof t.token&&t.token&&"number"==typeof t.expAt)return A={token:t.token,expAt:t.expAt},A}catch(e){}return null})();return(r=t)&&r.token.length>0&&r.expAt-6e4>Date.now()?Promise.resolve(t.token):(L||(L=C(e).catch((e=>{throw e})).finally((()=>{L=null}))),L);var r},I={timer:null,promise:null},$={timer:null,promise:null},q=e=>{const t=JSON.stringify(e);return(new TextEncoder).encode(t).length/1024};let N=null;const P=()=>{if(N)return N;try{const e=localStorage.getItem(S),t=e?JSON.parse(e):{};return N=t,t}catch(e){return console.error("[TM]: get register properties fail: ",e),{}}},O=e=>{N=e;try{localStorage.setItem(S,JSON.stringify(e))}catch(e){console.error("[TM]: save register properties fail: ",e)}},U=e=>{if(!e||"object"!=typeof e||Array.isArray(e))return void console.error("[TM]: registerGlobalProperties props must be a plain object");const t={...P(),...e},r=Object.keys(t);if(r.length>50){const e={};return r.slice(0,50).forEach((r=>{e[r]=t[r]})),console.warn("[TM]: register properties exceed max 50 keys, extra keys dropped"),void O(e)}O(t)},B=e=>{const t=document.cookie.match(new RegExp("(?:^|;\\s*)"+e+"=([^;]*)"));return t?decodeURIComponent(t[1]):void 0},z=e=>{document.cookie=`${b}=${encodeURIComponent(e)}; Max-Age=63072000; Path=/; Secure; SameSite=Lax`};let D;const F=()=>{if(D)return D;try{let e=localStorage.getItem(b);if(e)return B(b)||z(e),D=e;const t=B(b);if(t){try{localStorage.setItem(b,t)}catch(e){}return D=t}e=u();try{localStorage.setItem(b,e)}catch(e){}return z(e),D=e}catch(e){return void console.error("[TM]: get or create device id fail: ",e)}},j="tengence_session_id",H=()=>{try{const e=sessionStorage.getItem(j);let t=e?JSON.parse(e):null;const r=Date.now();return!t||!t.id||r-t.lastActiveAt>18e5?(t={id:u(),lastActiveAt:r},sessionStorage.setItem(j,JSON.stringify(t))):(t.lastActiveAt=r,sessionStorage.setItem(j,JSON.stringify(t))),t.id}catch(e){return}},J=(e,t)=>{try{const r=new URL(e,location.href);if(!r.searchParams.toString())return e;if(t&&t.length){const e=[];for(const n of t){const t=r.searchParams.get(n);null!==t&&e.push(`${n}=${encodeURIComponent(t)}`)}r.search=e.length?`?${e.join("&")}`:""}else r.search="";return r.toString()}catch(t){return e}},K=(e,t,r)=>{if("string"==typeof e)return t>0&&e.length>t?e.slice(0,t)+"...[truncated]":e;if(Array.isArray(e))return e.map((e=>K(e,t,r)));if(e&&"object"==typeof e){const n={};for(const o of Object.keys(e)){const i=e[o];"string"==typeof i&&/(^|_)url$|(^|_)href$|^referrer$/i.test(o)&&/^https?:\/\//i.test(i)?n[o]=J(i,r):n[o]=K(i,t,r)}return n}return e},W=e=>{const t={page_url:location.href,hostname:window.location.hostname||window.location.host,os:window.navigator.platform,user_id:localStorage.getItem(_)||void 0,user_agent:window.navigator.userAgent,screen_width:window.screen.width,screen_height:window.screen.height,lib_version:"1.2.0",lib_type:"js",event_time:Date.now(),timezone_offset:(new Date).getTimezoneOffset().toString()},r=w?.sanitize;let n;try{n="string"==typeof e.event_detail?e.event_detail:JSON.stringify(r&&(r.maxTextLength>0||r.stripQuery)?K(e.event_detail,r.maxTextLength,r.preserveQueryKeys):e.event_detail)}catch(e){return Y("serialize_failed","事件 event_detail 序列化失败(循环引用/BigInt?),该事件丢弃",{error:e}),void console.warn("[TM]: event_detail 序列化失败,该事件丢弃",e)}return{session_id:e.session_id||(w?.enableAutoSessionId?H():void 0),request_id:e.request_id,event_id:e.event_id||u(),spm:e.spm,data_type:e.data_type||"behavior",event_type:e.event_type,event_detail:n,...t,page_url:r&&r.stripQuery?J(t.page_url,r.preserveQueryKeys):t.page_url,device_id:F(),register_properties:JSON.stringify(P()),platform:e.platform||X}};let X;const Q=()=>{try{return"true"===localStorage.getItem(E)}catch(e){return!1}},G=(e,t,r,n,o)=>void 0===e?t:"number"!=typeof e||Number.isNaN(e)||e<r||e>n?(console.warn(`[TM]: 配置 ${o}=${e} 非法,回退默认值 ${t}`),t):e,V=(e,t)=>{try{w?.onDrop?.(e,t)}catch(e){}},Y=(e,t,r)=>{w?.isLogger&&console.error(`[TM]: ${t}`);try{w?.onError?.({code:e,message:t,context:r})}catch(e){}},Z=()=>{try{const e=localStorage.getItem(k);return null===e||"1"===e}catch(e){return!0}},ee=t=>{const r=t.apiKey;let i;const a=/Android|iPhone|iPad|iPod|Mobile/i.test(window.navigator.userAgent);X=t.platform||(a?"h5":"web"),w={pushUrl:t.pushUrl||"https://api.tengence.com/v1/push/event",authUrl:t.authUrl,realTimeUrl:t.realTimeUrl,cacheNumber:G(t.cacheNumber,100,1,1/0,"cacheNumber"),reportInterval:G(t.reportInterval,5e3,100,1/0,"reportInterval"),realTimeReportInterval:G(t.realTimeReportInterval,200,100,1/0,"realTimeReportInterval"),maxCacheSize:G(t.maxCacheSize,56,1,1/0,"maxCacheSize"),maxRetries:G(t.maxRetries,10,0,1/0,"maxRetries"),isLogger:t.isLogger??Q(),errorTraceRate:G(t.errorTraceRate,.5,0,1,"errorTraceRate"),resourceFlowLimit:G(t.resourceFlowLimit,15,0,200,"resourceFlowLimit"),traceBehavior:t.traceBehavior??!0,tracePerformance:t.tracePerformance??!0,traceError:t.traceError??!0,sanitize:!1===t.sanitize?{stripQuery:!1,maxTextLength:0,preserveQueryKeys:[]}:{stripQuery:t.sanitize?.stripQuery??!1,maxTextLength:G(t.sanitize?.maxTextLength,0,0,1/0,"sanitize.maxTextLength"),preserveQueryKeys:t.sanitize?.preserveQueryKeys||[]},enableAutoSessionId:t.enableAutoSessionId??!1,enableTracking:t.enableTracking??Z(),respectGpc:t.respectGpc??!0,requestTimeout:G(t.requestTimeout,1e4,1e3,6e4,"requestTimeout"),beforeSend:t.beforeSend,onDrop:t.onDrop,onError:t.onError};let{pushUrl:s,authUrl:c,realTimeUrl:l,cacheNumber:d,reportInterval:u,realTimeReportInterval:m,maxCacheSize:p,maxRetries:f}=w;r||c||(console.error("[TM]: 未配置 apiKey 或 authUrl,SDK 无法获取上报凭证,数据将不会上报。请配置 apiKey(直连)或 authUrl(自动换取 accessToken),或调用 setToken 注入 accessToken。"),Y("init_missing_credential","未配置 apiKey 或 authUrl,SDK 无法获取上报凭证,数据将不会上报。请配置 apiKey(直连)或 authUrl(自动换取 accessToken),或调用 setToken 注入 accessToken。"));const h={retryCount:0,nextRetryAt:0,retryExhausted:!1,halveBatch:!1},g={retryCount:0,nextRetryAt:0,retryExhausted:!1,halveBatch:!1},b=e=>{try{localStorage.setItem(_,e)}catch(e){console.error("[TM]: monitor bind user fail: ",e)}},S=()=>{localStorage.removeItem(_)},L={},C={},N=new Set;let P=null;const O=()=>{null!==P&&(clearTimeout(P),P=null),N.size&&(N.forEach((e=>{try{localStorage.setItem(e,JSON.stringify(L[e]||[]))}catch(t){Y("queue_write_failed","写入本地缓存队列失败(隐私模式/容量满?),本次数据仅保留内存",{key:e,error:t})}})),N.clear())},B=e=>{if(L[e])return L[e];let t=[];try{t=JSON.parse(localStorage.getItem(e)||"[]")}catch(t){Y("queue_read_failed","读取本地缓存队列失败,按空队列处理",{key:e,error:t})}return L[e]=t,C[e]=t.reduce(((e,t)=>e+q(t)),0),L[e]},z=(e,t)=>{N.delete(e),L[e]=t,C[e]=t.reduce(((e,t)=>e+q(t)),0);try{localStorage.setItem(e,JSON.stringify(t))}catch(t){Y("queue_write_failed","写入本地缓存队列失败(隐私模式/容量满?),队列清空处理",{key:e,error:t});try{localStorage.setItem(e,"[]")}catch(t){Y("localstorage_unavailable","localStorage 不可用,本次数据仅保留内存",{key:e,error:t})}}},D=e=>{i=e},F=async(t,o,a=!1)=>{const d=t&&l?l:s;let u;try{u=r||i||(c?await R(c):void 0)}catch(e){throw Y("token_fetch_failed","从 authUrl 获取 accessToken 失败,本批暂不上报(保留队列待重试)",{authUrl:c,error:e}),e}if(!u)throw Y("init_missing_credential","未配置 apiKey/authUrl 且未注入 accessToken,本次数据未上报(保留缓存队列待重试)"),new Error("[TM]: credential missing, skip report");w?.isLogger&&y(t?"RealTimeFetch":"Fetch",o);const m=w?.beforeSend?(()=>{let e;try{e=w.beforeSend(o)}catch(e){Y("before_send_failed","beforeSend 钩子抛错,本次按原始数据发送",{error:e})}return Array.isArray(e)?e:o})():o,p=a?void 0:w?.requestTimeout;return n(d,{data:m},u,p).catch((async t=>{const o=t?.status;if((t instanceof e||401===o||403===o)&&!r&&!i&&c){(()=>{A=null;try{localStorage.removeItem(M)}catch(e){}})();const e=await R(c).catch((()=>{}));if(e)return n(d,{data:m},e,p);Y("token_invalid","上报 401/403 且刷新 accessToken 失败,本批暂不上报(保留队列待重试)",{authUrl:c})}throw t}))},j=(e,t)=>{const r=new Set(t.filter((e=>!!e)));if(!r.size)return;const n=B(e);if(!n.length)return;const o=n.filter((e=>e.event_id&&!r.has(e.event_id)));o.length!==n.length&&z(e,o)},H=(e,t,r=0)=>{const n=(t?m:u)*Math.pow(2,e)*(.8+.4*Math.random()),o=Math.min(n,6e4);return Math.max(o,r)},J=(e,t)=>{const r=q(t);if(r>=64)return V([t],"item_too_large"),void console.warn("[TM]: 单条事件超过体积上限(64KB),丢弃该条");const n=B(e);if(n.unshift(t),C[e]+=r,n.length>x){const t=n.length-x,r=n.splice(n.length-t,n.length);C[e]=n.reduce(((e,t)=>e+q(t)),0),V(r,"queue_overflow"),console.warn(`[TM]: 队列超过上限(2000条),丢弃最旧 ${t} 条`)}(e=>{N.add(e),null===P&&(P=setTimeout((()=>{P=null,O()}),200))})(e),K(e,C[e]>=p)},K=(e,t=!1,r=!1)=>{if(!w?.enableTracking)return;let n=B(e);if(!n.length)return;const o=(e=>e===T)(e),i=o?$:I,a=(e=>e?g:h)(o);if(a.halveBatch&&n.length>1&&(n=n.slice(0,Math.ceil(n.length/2))),a.retryExhausted)return;if(Date.now()<a.nextRetryAt)return void(null===i.timer&&(i.timer=setTimeout((()=>{i.timer=null,K(e)}),a.nextRetryAt-Date.now())));if(0!==a.retryCount||(n.length>=d||t)){if(null!==i.timer&&(clearTimeout(i.timer),i.timer=null),i.promise)return;i.promise=(async(e,t,r,n,o=!1)=>{const i=[];let a=[],s=0;for(const e of t){const t=1024*q(e);a.length&&s+t>65536&&(i.push(a),a=[],s=0),a.push(e),s+=t}a.length&&i.push(a);for(const t of i){const n=await F(r,t,o),i=n?.data?.accepted_ids,a=n?.data?.rejected_ids;if(Array.isArray(i)){if(j(e,i),Array.isArray(a)&&a.length){const r=new Set(a);j(e,a);const n=t.filter((e=>e.event_id&&r.has(e.event_id)));n.length&&V(n,"server_rejected"),console.warn(`[TM]: 服务端校验拒绝 ${a.length} 条(永久性拒绝),已从队列移除并通知 onDrop`)}}else j(e,t.map((e=>e.event_id))),w?.isLogger&&console.warn("[TM]: 响应缺少 accepted_ids,按整批成功清理")}n.retryCount=0,n.nextRetryAt=0,n.halveBatch=!1})(e,n,o,a,r).catch((e=>{((e,t,r,n,o)=>{t?.timeout&&Y("request_timeout","上报请求超时,数据保留队列按退避重试",{url:r&&l?l:s,timeoutMs:w?.requestTimeout});const i=t?.status;if(413===i)return void(o.length>1?(n.halveBatch=!0,n.retryCount++,n.nextRetryAt=Date.now()+H(n.retryCount,r,0),console.warn(`[TM]: 批次过大(HTTP 413),降容半批重发(本批 ${o.length} 条)`)):(n.retryExhausted=!0,V(o,"rejected_4xx"),console.warn("[TM]: 单条事件仍超限(HTTP 413),数据保留在队列")));if(i&&i>=400&&i<500&&401!==i&&403!==i&&408!==i&&410!==i&&429!==i)return n.retryExhausted=!0,V(o,"rejected_4xx"),void console.warn(`[TM]: 批次提交被服务端拒绝(HTTP ${i}),数据保留在队列并暂停自动重试`);if(n.retryCount++,n.retryCount>f)return n.retryExhausted=!0,V(o,"retries_exhausted"),void console.warn(`[TM]: 重试次数已达上限(${f}),数据保留在队列(不丢弃),待下次会话或手动触发重试`);const a=429===i&&t?.retryAfter||0;n.nextRetryAt=Date.now()+H(n.retryCount,r,a),console.error(`[TM]: send report fail, retryCount: ${n.retryCount}, next retry after ${Math.round((n.nextRetryAt-Date.now())/1e3)}s`)})(0,e,o,a,n)})).finally((()=>{i.timer=null,i.promise=null,K(e)}))}else if(null===i.timer){const t=o?m:u;i.timer=setTimeout((()=>{i.timer=null,K(e,!0)}),t)}},ee=e=>{if(!w?.enableTracking||w?.respectGpc&&function(){const e="undefined"!=typeof navigator?navigator:void 0;return!0===e?.globalPrivacyControl}())return void(w?.isLogger&&console.warn("[TM]: 用户已选择退出采集(enableTracking=false 或 GPC 信号开启),本事件忽略"));if(!e||"object"!=typeof e)return void console.error("[TM]: track data must be an object");const t=!0===e.track_real_time,r=W(e);if(r)try{J(t&&l?T:v,r)}catch(e){Y("enqueue_failed","事件入队失败",{error:e})}},te=(e,t,r)=>{ee("string"==typeof e?{event_type:e,event_detail:t,...r}:e)},re=()=>{K(v,!0),K(T,!0);const e=[I.promise,$.promise].filter((e=>!!e));return Promise.all(e).then((()=>{}))},ne=()=>{w.isLogger=!0;try{localStorage.setItem(E,"true")}catch(e){console.error("[TM]: enable logger fail:",e)}},oe=()=>{w.isLogger=!1;try{localStorage.setItem(E,"false")}catch(e){console.error("[TM]: disable logger fail:",e)}};o((()=>{K(v,!0),K(T,!0)}));const ie=e=>("boolean"==typeof e&&(w.enableTracking=e,(e=>{try{localStorage.setItem(k,e?"1":"0")}catch(e){}})(e),e?w?.isLogger&&console.warn("[TM]: 恢复采集"):(z(v,[]),z(T,[]),w?.isLogger&&console.warn("[TM]: 用户已选择退出采集,已清空未上报队列"))),!0===w?.enableTracking);return window.trackMonitor={track:te,flush:re,bindUser:b,unBindUser:S,enableLogger:ne,disableLogger:oe,setToken:D,registerGlobalProperties:U,enableTracking:ie},{client:{track:te,flush:re,bindUser:b,unBindUser:S,enableLogger:ne,disableLogger:oe,setToken:D,registerGlobalProperties:U,enableTracking:ie},beforeClose:()=>{const e=()=>{O(),K(v,!0,!0),K(T,!0,!0)};window.addEventListener("beforeunload",e),window.addEventListener("pagehide",e),document.addEventListener("visibilitychange",(()=>{"hidden"===document.visibilityState&&e()})),window.addEventListener("online",e)}}},te=(re=16,function(){const e=new Uint8Array(re);if("undefined"!=typeof crypto&&"function"==typeof crypto.getRandomValues)crypto.getRandomValues(e);else for(let t=0;t<re;t++)e[t]=Math.floor(256*Math.random());let t="";for(let r=0;r<re;r++)t+=(e[r]<16?"0":"")+e[r].toString(16);return t})();var re;const ne=e=>{const t=history[e];return function(){const r=t.apply(this,arguments),n=new Event(e);return window.dispatchEvent(n),r}},oe=e=>{window.addEventListener("replaceState",(t=>e(t)),!0),window.addEventListener("pushState",(t=>e(t)),!0)},ie=e=>{window.addEventListener("popstate",(t=>e(t)),!0)},ae=[25,50,75,90],se=e=>{history.pushState=ne("pushState"),history.replaceState=ne("replaceState"),(()=>{let e="";const t=()=>{const t=window.location.href;if(t===e)return;const{host:r,hostname:n,href:o,protocol:i,origin:a,port:s,pathname:l,search:d,hash:u}=window.location,m={host:r,hostname:n,href:o,protocol:i,origin:a,port:s,pathname:l,search:d,hash:u,title:document.title,language:navigator.language.substring(0,2),user_agent:navigator.userAgent,win_screen:`${window.screen.width}x${window.screen.height}`,doc_screen:`${document.documentElement.clientWidth}x${document.documentElement.clientHeight}`,referrer:document.referrer,type:c()};window.trackMonitor.track({data_type:"common",event_type:"page_view",event_detail:m}),e=t},r=i(t,200);t(),ie((()=>{r()})),oe((()=>{r()})),window.addEventListener("popstate",(()=>{r()}))})(),(()=>{let e=document.referrer,t=window.location.href,r=Date.now(),n=!0;const o=(n=!1)=>{if(window.location.href!==t||n){const n=Date.now()-r;n>200&&window.trackMonitor.track({data_type:"common",event_type:"page_leave",event_detail:{url:t,referrer:e,duration:n}}),r=Date.now(),e=t,t=window.location.href}},a=i(o);o(),ie((()=>{a()})),oe((()=>{a()})),document.addEventListener("visibilitychange",(()=>{"hidden"===document.visibilityState?n&&(n=!1,o(!0)):(n=!0,r=Date.now())})),window.addEventListener("beforeunload",(()=>{n&&(n=!1,o(!0))}),!0)})(),(()=>{const e=a((e=>{const t=function(e){if(!e)return;if(["A","INPUT","TEXTAREA","BUTTON"].includes(e.tagName))return e;if(e.hasAttribute("track-click"))return e;return e.closest("a, button, [track-click]")||void 0}(e.target);if(!t)return;const r={element_id:t.id,element_content:t.textContent?.trim().slice(0,200)||null,element_class_name:t.className||null,element_name:t.getAttribute("name")||null,element_type:t.tagName.toLowerCase(),element_path:s(t),scroll_width:document.documentElement.scrollWidth,scroll_height:document.documentElement.scrollHeight,pageX:e.pageX,pageY:e.pageY,ratio:window.devicePixelRatio||1};window.trackMonitor.track({data_type:"common",event_type:"click",event_detail:r})}));window.addEventListener("click",e,!0)})(),!1!==e?.enableScroll&&(()=>{const e=new Set,t=a((()=>{const r=document.documentElement,n=r.scrollHeight-r.clientHeight;if(n<=0)return;const o=Math.round((window.scrollY||r.scrollTop)/n*100),i=ae.filter((t=>o>=t&&!e.has(t)));i.length&&(i.forEach((t=>{e.add(t),window.trackMonitor.track({data_type:"common",event_type:"scroll",event_detail:{scroll_depth:t}})})),e.has(90)&&window.removeEventListener("scroll",t))}),300);window.addEventListener("scroll",t,{passive:!0})})()};let ce=null,le=null;const de="track-expose",ue="track-view",me=e=>{try{le=new WeakMap}catch(e){console.error("[TM]: create WeakMap fail: ",e)}const t=(e,t=!1)=>{const n=le?.get(e);if(n&&!t)return;n&&(n.viewTimer&&clearTimeout(n.viewTimer),n.exposeTimer&&clearTimeout(n.exposeTimer));const o=e.dataset.durationView||500,i=e.dataset.durationExpose||500;le?.set(e,{duration_view:o,duration_expose:i,viewTimer:null,exposeTimer:null}),ce?.observe(e),t&&function(e){if(!(e instanceof Element))return!1;if(!function e(t){if(!t)return!0;const r=window.getComputedStyle(t);return"none"!==r.display&&"hidden"!==r.visibility&&0!==parseFloat(r.opacity)&&e(t.parentElement)}(e))return!1;const t=e.getBoundingClientRect();return t.top>=0&&t.left>=0&&t.bottom<=(window.innerHeight||document.documentElement.clientHeight)&&t.right<=(window.innerWidth||document.documentElement.clientWidth)}(e)&&r(e)},r=t=>{const r=le?.get(t);if(!r)return;const o=t.dataset.eventDetail,i=t.dataset.traceid??void 0,a=t.dataset.spm,s=t.hasAttribute("track-real-time"),c=t.hasAttribute(ue),l=t.hasAttribute(de);c&&l?r.exposeTimer=window.setTimeout((()=>{e({spm:a,request_id:i,data_type:"common",event_type:"view_expose",event_detail:o,track_real_time:s}),t.removeAttribute(ue),r.exposeTimer=null}),parseInt(r.duration_expose,10)):(c&&(r.viewTimer=window.setTimeout((()=>{e({spm:a,request_id:i,data_type:"common",event_type:"view",event_detail:o,track_real_time:s}),t.removeAttribute(ue),l&&r.exposeTimer||n(t),r.viewTimer=null}),parseInt(r.duration_view,10))),l&&(r.exposeTimer=window.setTimeout((()=>{e({spm:a,request_id:i,data_type:"common",event_type:"expose",event_detail:o,track_real_time:s}),r.exposeTimer=null}),parseInt(r.duration_expose,10))))},n=e=>{const t=le?.get(e);t&&(ce?.unobserve(e),t.viewTimer&&clearTimeout(t.viewTimer),t.exposeTimer&&clearTimeout(t.exposeTimer),le?.delete(e))},o=e=>{e.forEach((e=>{const t=e.target,o=le?.get(t);o&&(o.viewTimer&&(clearTimeout(o.viewTimer),o.viewTimer=null),o.exposeTimer&&(clearTimeout(o.exposeTimer),o.exposeTimer=null),e.isIntersecting&&(r(t),t.hasAttribute(ue)||t.hasAttribute(de)||n(t)))}))};ce=(()=>{let e=null;try{e=new IntersectionObserver(o,{root:null,threshold:.5})}catch(e){console.error("[TM]: create IntersectionObserver fail: ",e)}return e})();new Set([...document.querySelectorAll(`[${de}]`),...document.querySelectorAll(`[${ue}]`)]).forEach((e=>t(e))),new MutationObserver((e=>{e.forEach((e=>{if("attributes"===e.type&&e.target instanceof HTMLElement){const r=e.target,o=e.attributeName,i=["data-event-detail","data-traceid","data-spm","data-duration-view","data-duration-expose"];r.hasAttribute(de)||r.hasAttribute(ue)?i.includes(o)?t(r,!0):o!==de&&o!==ue||t(r,!1):n(r)}else"childList"===e.type&&(e.addedNodes.forEach((e=>{if(e instanceof HTMLElement)if(e.hasAttribute(de)||e.hasAttribute(ue))t(e);else{new Set([...e.querySelectorAll(`[${de}]`),...e.querySelectorAll(`[${ue}]`)]).forEach((e=>t(e)))}})),e.removedNodes.forEach((e=>{if(e instanceof HTMLElement){const t=le?.get(e);if(t)n(e);else{new Set([...e.querySelectorAll(`[${de}]`),...e.querySelectorAll(`[${ue}]`)]).forEach((e=>n(e)))}}})))}))})).observe(document.body||document.documentElement,{attributes:!0,childList:!0,subtree:!0})},pe=/^\s*at (?:(.*?) ?\()?((?:file|https?|blob|chrome-extension|address|native|eval|webpack|<anonymous>|[-a-z]+:|.*bundle|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i;function fe(e){const t=e.match(pe);if(!t)return{};return{filename:t[2],functionName:t[1]||"",lineno:parseInt(t[3],10)||void 0,colno:parseInt(t[4],10)||void 0}}function he(e){try{const{stack:t}=e;if(!t)return[];const r=[];for(const e of t.split("\n").slice(1)){const t=fe(e);!t||t.filename?.includes("chrome-extension://")||t.filename?.includes("node_modules")||t.filename?.includes("<anonymous>")||r.push(t)}return r.slice(0,10)}catch(e){return[]}}const ge=new Set,ye=(e,t)=>{if((r=w?.errorTraceRate??.5)<0||r>1?(console.warn("[TM]: probability should be between 0 and 1"),0):Math.random()<r){if(window.trackMonitor.track(t),ge.size>=1e3){const e=ge.values().next().value;void 0!==e&&ge.delete(e)}ge.add(e)}var r},we=()=>{window.addEventListener("error",(e=>{(e=>{if(e instanceof ErrorEvent&&"Script error."===e.message){const t=p("js-Script error.");if(ge.has(t))return;return void ye(t,{data_type:"error",event_type:"js",event_detail:{traceId:te,msg:'Script error.(跨域脚本未加载 crossorigin 属性,无法获取堆栈;建议脚本加 crossorigin="anonymous")',target:"",stack:[],errorUuid:t,meta:{filename:e.filename,colno:e.colno,lineno:e.lineno,crossorigin_missing:!0}}})}if("js"!==m(e))return;if((e=>{const t=e.error?.stack||"";return/chrome-extension:\/\//.test(t)})(e))return;const t=p(`js-${e.message}-${e.filename}`);if(ge.has(t))return;const r=he(e.error),n={traceId:te,msg:e.message,target:r[0]?`${r[0].filename?.split("?")[0]}:${r[0].lineno}:${r[0].colno}`:"",stack:r,errorUuid:t,meta:{filename:e.filename,colno:e.colno,lineno:e.lineno}};ye(t,{data_type:"error",event_type:"js",event_detail:n})})(e)}),!0)},ve=()=>{window.addEventListener("error",(e=>{(e=>{if("resource"!==m(e))return;const t=e.target,r=p(`resource-${t.src}-${t.tagName}`);if(ge.has(r))return;const n={traceId:te,msg:`加载${t.tagName}失败`,target:t.src?.split("?")[0],errorUuid:r,meta:{url:t.src,html:t.outerHTML,tagName:t.tagName}};ye(r,{data_type:"error",event_type:"resource",event_detail:n})})(e)}),!0)},Te=()=>{window.addEventListener("unhandledrejection",(e=>(e=>{const t=e.reason?.message||e.reason,r=p(`unhandledrejection-${t}`);if(ge.has(r))return;const n={traceId:te,msg:t,stack:he(e.reason),errorUuid:r};ye(r,{data_type:"error",event_type:"unhandledrejection",event_detail:n})})(e)),!0)},_e=()=>{const e=(e,t,r)=>{var n;if("string"!=typeof(n=e.url)||![w?.pushUrl,w?.authUrl,w?.realTimeUrl].filter((e=>!!e)).some((e=>0===n.indexOf(e))))if(t){const n=p(`http-${t}-${e.url}-${r}`);if(ge.has(n))return;const o={traceId:te,errorType:t,msg:r,target:e.url.split("?")[0],errorUuid:n,request:e};ye(n,{data_type:"error",event_type:"http",event_detail:o})}else if(e.status>=400){let r=e.statusText;const n=p(`http-${t="http_error"}-${e.url}-${r}`);if(ge.has(n))return;const o={traceId:te,errorUuid:n,errorType:t,target:e.url.split("?")[0],msg:r,request:e};ye(n,{data_type:"error",event_type:"http",event_detail:o})}};((e,t)=>{if("XMLHttpRequest"in window&&"function"==typeof window.XMLHttpRequest){const e=window.XMLHttpRequest;window.oXMLHttpRequest||(window.oXMLHttpRequest=e),window.XMLHttpRequest=function(){const r=new e,{open:n,send:o}=r;let i={};r.open=(e,t,o,a,s)=>{void 0===o&&(o=!0),i.method=e,i.url=t,n.call(r,e,t,o,a,s)},r.send=e=>{i.requestTime=(new Date).getTime(),o.call(r,e)};const a=(e,n)=>{const{status:o,statusText:a}=r,s=(new Date).getTime();i={...i,status:o,statusText:a,responseTime:s,duration:s-i.requestTime},"function"==typeof t&&t(i,e,n)};return r.addEventListener("loadend",(()=>a())),r.addEventListener("error",(()=>a("net_error","ERR_NETWORK"))),r.addEventListener("abort",(()=>a("cannel_request","ERR_CANCELED"))),r.addEventListener("timeout",(()=>a("timeout","ERR_NETWORK"))),r}}})(0,e),((e,t)=>{if("fetch"in window&&"function"==typeof window.fetch){const e=window.fetch;window.oFetch||(window.oFetch=e),window.fetch=async(r,n)=>{let o={};return o.method=n?.method||"",o.url=(r&&"string"!=typeof r?r?.url:r)||"",o.requestTime=(new Date).getTime(),e.call(window,r,n).then((async e=>{if("function"==typeof t){const r=e.clone(),n=(new Date).getTime();o={...o,status:r.status,statusText:r.statusText,responseTime:n,duration:n-o.requestTime},t(o)}return e})).catch((e=>{if("function"==typeof t){const r=(new Date).getTime();o={...o,responseTime:r,duration:r-o.requestTime},"AbortError"===e.name?t(o,"cannel_request","ERR_CANCELED"):t(o,"net_error","ERR_NETWORK")}return Promise.reject(e)}))}}})(0,e)};const be=new WeakSet,Se=e=>({form_id:e.id||null,form_class:"string"==typeof e.className?e.className:null,form_name:e.getAttribute("name")||null,form_action:e.getAttribute("action")||null,form_text:(e.textContent||"").trim().slice(0,200)||null});let Ee=-1;const ke=e=>{addEventListener("pageshow",(t=>{t.persisted&&(Ee=t.timeStamp,e(t))}),!0)},xe=(e,t,r,n)=>{let o,i;return a=>{t.value>=0&&(a||n)&&(i=t.value-(o??0),(i||void 0===o)&&(o=t.value,t.delta=i,t.rating=((e,t)=>e>t[1]?"poor":e>t[0]?"needs-improvement":"good")(t.value,r),e(t)))}},Me=e=>{requestAnimationFrame((()=>requestAnimationFrame((()=>e()))))},Ae=()=>{const e=performance.getEntriesByType("navigation")[0];if(e&&e.responseStart>0&&e.responseStart<performance.now())return e},Le=()=>{const e=Ae();return e?.activationStart??0},Ce=(e,t=-1)=>{const r=Ae();let n="navigate";return Ee>=0?n="back-forward-cache":r&&(document.prerendering||Le()>0?n="prerender":document.wasDiscarded?n="restore":r.type&&(n=r.type.replace(/_/g,"-"))),{name:e,value:t,rating:"good",delta:0,entries:[],id:`v5-${Date.now()}-${Math.floor(8999999999999*Math.random())+1e12}`,navigationType:n}},Re=new WeakMap;function Ie(e,t){return Re.get(e)||Re.set(e,new t),Re.get(e)}class $e{t;i=0;o=[];h(e){if(e.hadRecentInput)return;const t=this.o[0],r=this.o.at(-1);this.i&&t&&r&&e.startTime-r.startTime<1e3&&e.startTime-t.startTime<5e3?(this.i+=e.value,this.o.push(e)):(this.i=e.value,this.o=[e]),this.t?.(e)}}const qe=(e,t,r={})=>{try{if(PerformanceObserver.supportedEntryTypes.includes(e)){const n=new PerformanceObserver((e=>{Promise.resolve().then((()=>{t(e.getEntries())}))}));return n.observe({type:e,buffered:!0,...r}),n}}catch{}},Ne=e=>{let t=!1;return()=>{t||(e(),t=!0)}};let Pe=-1;const Oe=new Set,Ue=()=>"hidden"!==document.visibilityState||document.prerendering?1/0:0,Be=e=>{if("hidden"===document.visibilityState){if("visibilitychange"===e.type)for(const e of Oe)e();isFinite(Pe)||(Pe="visibilitychange"===e.type?e.timeStamp:0,removeEventListener("prerenderingchange",Be,!0))}},ze=()=>{if(Pe<0){const e=Le(),t=document.prerendering?void 0:globalThis.performance.getEntriesByType("visibility-state").filter((t=>"hidden"===t.name&&t.startTime>e))[0]?.startTime;Pe=t??Ue(),addEventListener("visibilitychange",Be,!0),addEventListener("prerenderingchange",Be,!0),ke((()=>{setTimeout((()=>{Pe=Ue()}))}))}return{get firstHiddenTime(){return Pe},onHidden(e){Oe.add(e)}}},De=e=>{document.prerendering?addEventListener("prerenderingchange",(()=>e()),!0):e()},Fe=[1800,3e3],je=(e,t={})=>{De((()=>{const r=ze();let n,o=Ce("FCP");const i=qe("paint",(e=>{for(const t of e)"first-contentful-paint"===t.name&&(i.disconnect(),t.startTime<r.firstHiddenTime&&(o.value=Math.max(t.startTime-Le(),0),o.entries.push(t),n(!0)))}));i&&(n=xe(e,o,Fe,t.reportAllChanges),ke((r=>{o=Ce("FCP"),n=xe(e,o,Fe,t.reportAllChanges),Me((()=>{o.value=performance.now()-r.timeStamp,n(!0)}))})))}))},He=[.1,.25];let Je=0,Ke=1/0,We=0;const Xe=e=>{for(const t of e)t.interactionId&&(Ke=Math.min(Ke,t.interactionId),We=Math.max(We,t.interactionId),Je=We?(We-Ke)/7+1:0)};let Qe;const Ge=()=>Qe?Je:performance.interactionCount??0;let Ve=0;class Ye{u=[];l=new Map;m;p;v(){Ve=Ge(),this.u.length=0,this.l.clear()}L(){const e=Math.min(this.u.length-1,Math.floor((Ge()-Ve)/50));return this.u[e]}h(e){if(this.m?.(e),!e.interactionId&&"first-input"!==e.entryType)return;const t=this.u.at(-1);let r=this.l.get(e.interactionId);if(r||this.u.length<10||e.duration>t.P){if(r?e.duration>r.P?(r.entries=[e],r.P=e.duration):e.duration===r.P&&e.startTime===r.entries[0].startTime&&r.entries.push(e):(r={id:e.interactionId,entries:[e],P:e.duration},this.l.set(r.id,r),this.u.push(r)),this.u.sort(((e,t)=>t.P-e.P)),this.u.length>10){const e=this.u.splice(10);for(const t of e)this.l.delete(t.id)}this.p?.(r)}}}const Ze=e=>{const t=globalThis.requestIdleCallback||setTimeout;"hidden"===document.visibilityState?e():(e=Ne(e),addEventListener("visibilitychange",e,{once:!0,capture:!0}),t((()=>{e(),removeEventListener("visibilitychange",e,{capture:!0})})))},et=[200,500],tt=(e,t={})=>{if(!globalThis.PerformanceEventTiming||!("interactionId"in PerformanceEventTiming.prototype))return;const r=ze();De((()=>{"interactionCount"in performance||Qe||(Qe=qe("event",Xe,{type:"event",buffered:!0,durationThreshold:0}));let n,o=Ce("INP");const i=Ie(t,Ye),a=e=>{Ze((()=>{for(const t of e)i.h(t);const t=i.L();t&&t.P!==o.value&&(o.value=t.P,o.entries=t.entries,n())}))},s=qe("event",a,{durationThreshold:t.durationThreshold??40});n=xe(e,o,et,t.reportAllChanges),s&&(s.observe({type:"first-input",buffered:!0}),r.onHidden((()=>{a(s.takeRecords()),n(!0)})),ke((()=>{i.v(),o=Ce("INP"),n=xe(e,o,et,t.reportAllChanges)})))}))};class rt{m;h(e){this.m?.(e)}}const nt=[2500,4e3],ot=[800,1800],it=e=>{document.prerendering?De((()=>it(e))):"complete"!==document.readyState?addEventListener("load",(()=>it(e)),!0):setTimeout(e)};function at(){const e={ttfb:null,lcp:null,fp:null,fcp:null,connectionRtt:null,ttfbRequestTime:null,inp:null,cls:null,dcl:null,loadTime:null};((e,t={})=>{De((()=>{const r=ze();let n,o=Ce("LCP");const i=Ie(t,rt),a=e=>{t.reportAllChanges||(e=e.slice(-1));for(const t of e)i.h(t),t.startTime<r.firstHiddenTime&&(o.value=Math.max(t.startTime-Le(),0),o.entries=[t],n())},s=qe("largest-contentful-paint",a);if(s){n=xe(e,o,nt,t.reportAllChanges);const r=Ne((()=>{a(s.takeRecords()),s.disconnect(),n(!0)})),i=e=>{e.isTrusted&&(Ze(r),removeEventListener(e.type,i,{capture:!0}))};for(const e of["keydown","click","visibilitychange"])addEventListener(e,i,{capture:!0});ke((r=>{o=Ce("LCP"),n=xe(e,o,nt,t.reportAllChanges),Me((()=>{o.value=performance.now()-r.timeStamp,n(!0)}))}))}}))})((t=>{e.lcp=d(t.value)}),{reportAllChanges:!0}),((e,t={})=>{const r=ze();je(Ne((()=>{let n,o=Ce("CLS",0);const i=Ie(t,$e),a=e=>{for(const t of e)i.h(t);i.i>o.value&&(o.value=i.i,o.entries=i.o,n())},s=qe("layout-shift",a);s&&(n=xe(e,o,He,t.reportAllChanges),r.onHidden((()=>{a(s.takeRecords()),n(!0)})),ke((()=>{i.i=0,o=Ce("CLS",0),n=xe(e,o,He,t.reportAllChanges),Me((()=>n()))})),setTimeout(n))})))})((t=>{e.cls=Math.round(1e4*t.value)/1e4}),{reportAllChanges:!0}),tt((t=>{e.inp=d(t.value)}),{reportAllChanges:!0}),((e,t={})=>{let r=Ce("TTFB"),n=xe(e,r,ot,t.reportAllChanges);it((()=>{const o=Ae();o&&(r.value=Math.max(o.responseStart-Le(),0),r.entries=[o],n(!0),ke((()=>{r=Ce("TTFB",0),n=xe(e,r,ot,t.reportAllChanges),n(!0)})))}))})((t=>{e.ttfb=d(t.value)})),je((t=>{e.fcp=d(t.value)}));const t=()=>{(()=>{try{performance.getEntriesByType("paint").forEach((t=>{"first-paint"===t.name&&(e.fp=d(t.startTime))}))}catch(e){console.warn("[TM]: Failed to collect FP:",e)}try{const t=navigator.connection||navigator.mozConnection||navigator.webkitConnection;t&&void 0!==t.rtt&&(e.connectionRtt=t.rtt)}catch(e){console.warn("[TM]: Failed to collect Connection RTT:",e)}try{const t=performance.getEntriesByType("navigation")[0];if(t){const{responseStart:r,requestStart:n}=t;r>0&&n>0&&n<=r&&(e.ttfbRequestTime=d(r-n))}}catch(e){console.warn("[TM]: Failed to collect TTFB Request Time:",e)}try{const t=performance.getEntriesByType("navigation")[0];if(t){const{domContentLoadedEventEnd:r,loadEventEnd:n,fetchStart:o}=t;r>0&&o>0&&(e.dcl=d(r-o)),n>0&&o>0&&(e.loadTime=d(n-o))}}catch(e){console.warn("[TM]: Failed to collect DCL and Load Time:",e)}})(),window.trackMonitor.track({data_type:"performance",event_type:"metrics",event_detail:{traceId:te,data:e}})};let r=0;const n=()=>{const e=Date.now();e-r<3e4||(r=e,t())};window.addEventListener("beforeunload",t),window.addEventListener("pagehide",n),document.addEventListener("visibilitychange",(()=>{"hidden"===document.visibilityState&&n()}))}const st="__tengence_sdk",ct=e=>{if("undefined"!=typeof window&&window.trackMonitor&&window.trackMonitor[st])return console.warn("[TM]: init 已调用过,复用现有实例(如需重建请刷新页面)"),window.trackMonitor;const{client:t,beforeClose:r}=ee(e);var n;return me(t.track),!1!==e.traceBehavior&&se({enableScroll:e.enableScroll}),!1!==e.enableFormTrack&&(n=t.track,document.addEventListener("focusin",(e=>{const t=e.target;if(!(r=t)||"INPUT"!==r.tagName&&"SELECT"!==r.tagName&&"TEXTAREA"!==r.tagName)return;var r;const o=t.closest("form");o&&!be.has(o)&&(be.add(o),n({data_type:"common",event_type:"form_start",event_detail:Se(o)}))}),!0),document.addEventListener("submit",(e=>{const t=e.target;if(!t||"FORM"!==t.tagName)return;const r=e.submitter;n({data_type:"common",event_type:"form_submit",event_detail:{...Se(t),submitter_id:r?.id||null,submitter_text:(r?.textContent||"").trim().slice(0,200)||null}})}),!0)),!1!==e.traceError&&(we(),ve(),Te(),_e()),!1!==e.tracePerformance&&l((()=>{!function(){const e=window.performance||window.msPerformance;if(!e)return{dnsLookupTime:null,tcpConnectTime:null,ttfb:null,transferTime:null,domParseTime:null,domCompleteTime:null,loadEventTime:null,totalPageLoad:null};const t=e.getEntriesByType&&"function"==typeof e.getEntriesByType?e.getEntriesByType("navigation"):void 0,r=Array.isArray(t)?t[0]:void 0;let n=null;if(r)n=r;else{const t=e.timing;t&&(n={startTime:0,domainLookupStart:t.domainLookupStart,domainLookupEnd:t.domainLookupEnd,connectStart:t.connectStart,connectEnd:t.connectEnd,requestStart:t.requestStart,responseStart:t.responseStart,responseEnd:t.responseEnd,domInteractive:t.domInteractive,domComplete:t.domComplete,loadEventEnd:t.loadEventEnd,fetchStart:t.fetchStart},!n.requestStart&&t.fetchStart&&(n.requestStart=t.fetchStart))}const o={dnsLookupTime:n?d(n.domainLookupEnd-n.domainLookupStart):null,tcpConnectTime:n?d(n.connectEnd-n.connectStart):null,ttfb:n?d(n.responseStart-(n.requestStart??n.fetchStart??0)):null,transferTime:n?d(n.responseEnd-n.responseStart):null,domParseTime:n?d(n.domInteractive-n.responseEnd):null,domCompleteTime:n?d(n.domComplete-n.domInteractive):null,loadEventTime:n?d(n.loadEventEnd-(n.domComplete??0)):null,totalPageLoad:n?d((n.loadEventEnd??performance.now())-(n.startTime??0)):null};window.trackMonitor.track({data_type:"performance",event_type:"navigation_timing",event_detail:{traceId:te,data:o}})}(),function(){const e=window.performance||window.msPerformance;if(!e)return[];const t=[];if(e.getEntriesByType){const r=(e.getEntriesByType("resource")||[]).map((e=>{const t="number"==typeof e.redirectEnd&&"number"==typeof e.redirectStart?d(e.redirectEnd-e.redirectStart):null,r="number"==typeof e.domainLookupEnd&&"number"==typeof e.domainLookupStart?d(e.domainLookupEnd-e.domainLookupStart):null,n="number"==typeof e.connectEnd&&"number"==typeof e.connectStart?d(e.connectEnd-e.connectStart):null,o="number"==typeof e.requestStart&&"number"==typeof e.fetchStart?d(e.requestStart-e.fetchStart):null,i="number"==typeof e.responseStart&&"number"==typeof e.requestStart?d(e.responseStart-e.requestStart):null,a="number"==typeof e.responseEnd&&"number"==typeof e.responseStart?d(e.responseEnd-e.responseStart):null,s="number"==typeof e.duration?d(e.duration):null,c="number"==typeof e.startTime?d(e.startTime):null,l="number"==typeof e.transferSize&&isFinite(e.transferSize)&&e.transferSize>=0?Math.round(e.transferSize):null,u="number"==typeof e.encodedBodySize&&isFinite(e.encodedBodySize)&&e.encodedBodySize>=0?Math.round(e.encodedBodySize):null,m="number"==typeof e.decodedBodySize&&isFinite(e.decodedBodySize)&&e.decodedBodySize>=0?Math.round(e.decodedBodySize):null;return{name:e.name,initiatorType:e.initiatorType||"",startTime:c,duration:s,redirectTime:t,cacheTime:o,dnsTime:r,connectTime:n,requestTime:i,responseTime:a,transferSize:l,encodedBodySize:u,decodedBodySize:m}}));r.sort(((e,t)=>(t.duration??0)-(e.duration??0)));const n=w?.resourceFlowLimit??15;if(0===n)return;for(let e=0;e<Math.min(n,r.length);e++)t.push(r[e]);if(!t.length)return}window.trackMonitor.track({data_type:"performance",event_type:"resource_flow",event_detail:{traceId:te,data:t}})}(),at()})),l((()=>{r()})),window.trackMonitor[st]=!0,window.trackMonitor};export{ct as default};
@@ -0,0 +1,2 @@
1
+ import init from './index';
2
+ export { init };