@jerry_aurora/sky-monitor-sdk 0.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 +355 -0
- package/dist/chunk-2EV6VMEO.js +2 -0
- package/dist/chunk-2EV6VMEO.js.map +1 -0
- package/dist/chunk-2R7LNQYV.js +4 -0
- package/dist/chunk-2R7LNQYV.js.map +1 -0
- package/dist/chunk-4DK7RV2K.js +2 -0
- package/dist/chunk-4DK7RV2K.js.map +1 -0
- package/dist/chunk-4GL6IXHZ.cjs +2 -0
- package/dist/chunk-4GL6IXHZ.cjs.map +1 -0
- package/dist/chunk-4K5NIPXS.cjs +2 -0
- package/dist/chunk-4K5NIPXS.cjs.map +1 -0
- package/dist/chunk-5RS6LIZN.js +2 -0
- package/dist/chunk-5RS6LIZN.js.map +1 -0
- package/dist/chunk-5VYMD33V.cjs +2 -0
- package/dist/chunk-5VYMD33V.cjs.map +1 -0
- package/dist/chunk-647GK2XE.js +2 -0
- package/dist/chunk-647GK2XE.js.map +1 -0
- package/dist/chunk-6KJXTS2V.js +2 -0
- package/dist/chunk-6KJXTS2V.js.map +1 -0
- package/dist/chunk-6YTKBMJD.cjs +2 -0
- package/dist/chunk-6YTKBMJD.cjs.map +1 -0
- package/dist/chunk-ACIPNVBT.cjs +2 -0
- package/dist/chunk-ACIPNVBT.cjs.map +1 -0
- package/dist/chunk-BEGDR54W.js +2 -0
- package/dist/chunk-BEGDR54W.js.map +1 -0
- package/dist/chunk-BVOILM65.cjs +2 -0
- package/dist/chunk-BVOILM65.cjs.map +1 -0
- package/dist/chunk-FBQC6XIY.cjs +2 -0
- package/dist/chunk-FBQC6XIY.cjs.map +1 -0
- package/dist/chunk-GBREIA45.js +2 -0
- package/dist/chunk-GBREIA45.js.map +1 -0
- package/dist/chunk-GU7O5MWM.js +2 -0
- package/dist/chunk-GU7O5MWM.js.map +1 -0
- package/dist/chunk-JMHMJVBJ.cjs +2 -0
- package/dist/chunk-JMHMJVBJ.cjs.map +1 -0
- package/dist/chunk-LAATIQ3Y.cjs +2 -0
- package/dist/chunk-LAATIQ3Y.cjs.map +1 -0
- package/dist/chunk-LFJTX6FJ.cjs +2 -0
- package/dist/chunk-LFJTX6FJ.cjs.map +1 -0
- package/dist/chunk-LKBNR7RI.js +2 -0
- package/dist/chunk-LKBNR7RI.js.map +1 -0
- package/dist/chunk-NM4RALDJ.cjs +2 -0
- package/dist/chunk-NM4RALDJ.cjs.map +1 -0
- package/dist/chunk-O32X45L3.js +2 -0
- package/dist/chunk-O32X45L3.js.map +1 -0
- package/dist/chunk-S3ROE6GJ.js +2 -0
- package/dist/chunk-S3ROE6GJ.js.map +1 -0
- package/dist/chunk-SNOBW3V4.js +2 -0
- package/dist/chunk-SNOBW3V4.js.map +1 -0
- package/dist/chunk-XOLMXHFL.js +2 -0
- package/dist/chunk-XOLMXHFL.js.map +1 -0
- package/dist/chunk-Y2IWAJSY.cjs +4 -0
- package/dist/chunk-Y2IWAJSY.cjs.map +1 -0
- package/dist/chunk-Y46XWPAC.cjs +2 -0
- package/dist/chunk-Y46XWPAC.cjs.map +1 -0
- package/dist/chunk-ZIKPTR2L.cjs +2 -0
- package/dist/chunk-ZIKPTR2L.cjs.map +1 -0
- package/dist/index-Bh5S5dim.d.ts +203 -0
- package/dist/index-D6soZ1u6.d.cts +203 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +32 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/platforms/browser.cjs +2 -0
- package/dist/platforms/browser.cjs.map +1 -0
- package/dist/platforms/browser.d.cts +10 -0
- package/dist/platforms/browser.d.ts +10 -0
- package/dist/platforms/browser.js +2 -0
- package/dist/platforms/browser.js.map +1 -0
- package/dist/plugins/dedupe.cjs +2 -0
- package/dist/plugins/dedupe.cjs.map +1 -0
- package/dist/plugins/dedupe.d.cts +39 -0
- package/dist/plugins/dedupe.d.ts +39 -0
- package/dist/plugins/dedupe.js +2 -0
- package/dist/plugins/dedupe.js.map +1 -0
- package/dist/plugins/error.cjs +2 -0
- package/dist/plugins/error.cjs.map +1 -0
- package/dist/plugins/error.d.cts +51 -0
- package/dist/plugins/error.d.ts +51 -0
- package/dist/plugins/error.js +2 -0
- package/dist/plugins/error.js.map +1 -0
- package/dist/plugins/fetch.cjs +2 -0
- package/dist/plugins/fetch.cjs.map +1 -0
- package/dist/plugins/fetch.d.cts +35 -0
- package/dist/plugins/fetch.d.ts +35 -0
- package/dist/plugins/fetch.js +2 -0
- package/dist/plugins/fetch.js.map +1 -0
- package/dist/plugins/offline-queue.cjs +2 -0
- package/dist/plugins/offline-queue.cjs.map +1 -0
- package/dist/plugins/offline-queue.d.cts +93 -0
- package/dist/plugins/offline-queue.d.ts +93 -0
- package/dist/plugins/offline-queue.js +2 -0
- package/dist/plugins/offline-queue.js.map +1 -0
- package/dist/plugins/performance.cjs +2 -0
- package/dist/plugins/performance.cjs.map +1 -0
- package/dist/plugins/performance.d.cts +31 -0
- package/dist/plugins/performance.d.ts +31 -0
- package/dist/plugins/performance.js +2 -0
- package/dist/plugins/performance.js.map +1 -0
- package/dist/plugins/replay.cjs +77 -0
- package/dist/plugins/replay.cjs.map +1 -0
- package/dist/plugins/replay.js +77 -0
- package/dist/plugins/replay.js.map +1 -0
- package/dist/plugins/sampling.cjs +2 -0
- package/dist/plugins/sampling.cjs.map +1 -0
- package/dist/plugins/sampling.d.cts +56 -0
- package/dist/plugins/sampling.d.ts +56 -0
- package/dist/plugins/sampling.js +2 -0
- package/dist/plugins/sampling.js.map +1 -0
- package/dist/plugins/session.cjs +2 -0
- package/dist/plugins/session.cjs.map +1 -0
- package/dist/plugins/session.d.cts +74 -0
- package/dist/plugins/session.d.ts +74 -0
- package/dist/plugins/session.js +2 -0
- package/dist/plugins/session.js.map +1 -0
- package/dist/plugins/trace.cjs +2 -0
- package/dist/plugins/trace.cjs.map +1 -0
- package/dist/plugins/trace.d.cts +125 -0
- package/dist/plugins/trace.d.ts +125 -0
- package/dist/plugins/trace.js +2 -0
- package/dist/plugins/trace.js.map +1 -0
- package/dist/plugins/transport.cjs +2 -0
- package/dist/plugins/transport.cjs.map +1 -0
- package/dist/plugins/transport.d.cts +231 -0
- package/dist/plugins/transport.d.ts +231 -0
- package/dist/plugins/transport.js +2 -0
- package/dist/plugins/transport.js.map +1 -0
- package/dist/plugins/xhr.cjs +2 -0
- package/dist/plugins/xhr.cjs.map +1 -0
- package/dist/plugins/xhr.d.cts +35 -0
- package/dist/plugins/xhr.d.ts +35 -0
- package/dist/plugins/xhr.js +2 -0
- package/dist/plugins/xhr.js.map +1 -0
- package/dist/presets.cjs +2 -0
- package/dist/presets.cjs.map +1 -0
- package/dist/presets.d.cts +45 -0
- package/dist/presets.d.ts +45 -0
- package/dist/presets.js +2 -0
- package/dist/presets.js.map +1 -0
- package/dist/sky-monitor-replay.umd.js +77 -0
- package/dist/sky-monitor-replay.umd.js.map +1 -0
- package/dist/sky-monitor.umd.js +4 -0
- package/dist/sky-monitor.umd.js.map +1 -0
- package/dist/types-BGUJGEpp.d.cts +90 -0
- package/dist/types-BGUJGEpp.d.ts +90 -0
- package/package.json +122 -0
package/README.md
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
# Sky Monitor SDK
|
|
2
|
+
|
|
3
|
+
轻量级 AI 应用监控 SDK,核心 gzip 仅 1.4KB。
|
|
4
|
+
|
|
5
|
+
## 为什么需要它?
|
|
6
|
+
|
|
7
|
+
传统监控 SDK(Sentry、LogRocket)针对通用 Web 应用设计,无法满足 AI 对话场景的特殊需求:
|
|
8
|
+
|
|
9
|
+
| 痛点 | 传统方案 | Sky Monitor |
|
|
10
|
+
|-----|---------|-------------|
|
|
11
|
+
| SSE 流式响应 | 只能监控请求开始/结束 | 完整生命周期追踪(TTFB/TTLB/阶段/卡顿) |
|
|
12
|
+
| Tool Calling | 无法追踪 | 工具调用链路、耗时、结果 |
|
|
13
|
+
| AI 生图 | 无法追踪 | 图片加载状态、尺寸、耗时 |
|
|
14
|
+
| 对话重试 | 丢失关联 | traceId 串联重试链路 |
|
|
15
|
+
| 包体积 | 50KB+ | 核心 1.4KB,按需加载 |
|
|
16
|
+
|
|
17
|
+
## 核心能力
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
21
|
+
│ AI 对话追踪 SSE 生命周期 · 阶段拆分 · Tool Calling · 卡顿检测 │
|
|
22
|
+
├─────────────────────────────────────────────────────────────┤
|
|
23
|
+
│ 错误监控 JS 错误 · Promise 拒绝 · 资源加载失败 · 去重 │
|
|
24
|
+
├─────────────────────────────────────────────────────────────┤
|
|
25
|
+
│ 性能采集 Web Vitals (FCP/LCP/CLS/INP) · HTTP 请求耗时 │
|
|
26
|
+
├─────────────────────────────────────────────────────────────┤
|
|
27
|
+
│ 智能上报 优先级队列 · 按类型差异化策略 · 离线缓存 · 指数退避重试 │
|
|
28
|
+
├─────────────────────────────────────────────────────────────┤
|
|
29
|
+
│ 会话录制 基于 rrweb · 问题复现 · 隐私脱敏 │
|
|
30
|
+
└─────────────────────────────────────────────────────────────┘
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 安装
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install @sky-monitor/sdk
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 快速接入
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import { createMonitorWithPreset } from '@sky-monitor/sdk'
|
|
43
|
+
|
|
44
|
+
const monitor = createMonitorWithPreset({
|
|
45
|
+
appId: 'my-ai-app',
|
|
46
|
+
endpoint: '/api/monitor', // 上报地址
|
|
47
|
+
preset: 'production', // 预设模式
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
monitor.setContext({ userId: 'user_123', version: '1.0.0' })
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
| 预设 | 上报模式 | 采样率 | 插件 |
|
|
54
|
+
|-----|---------|-------|------|
|
|
55
|
+
| development | immediate | 100% | 全部 |
|
|
56
|
+
| production | batch | 10% | 核心 |
|
|
57
|
+
| minimal | batch | 5% | 仅错误 |
|
|
58
|
+
|
|
59
|
+
## 全量配置
|
|
60
|
+
|
|
61
|
+
对象配置方式,插件按 `priority` 自动排序,无需关心注册顺序:
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import { createMonitorWithPreset } from '@sky-monitor/sdk'
|
|
65
|
+
|
|
66
|
+
const monitor = createMonitorWithPreset({
|
|
67
|
+
// ===== 基础配置 =====
|
|
68
|
+
appId: 'my-ai-app',
|
|
69
|
+
endpoint: '/api/monitor',
|
|
70
|
+
debug: true,
|
|
71
|
+
|
|
72
|
+
// ===== 启用的插件 =====
|
|
73
|
+
plugins: ['error', 'performance', 'fetch', 'xhr', 'session', 'dedupe', 'sampling'],
|
|
74
|
+
|
|
75
|
+
// ===== 传输配置 =====
|
|
76
|
+
transport: {
|
|
77
|
+
mode: 'batch',
|
|
78
|
+
typeConfig: {
|
|
79
|
+
js_error: 'immediate',
|
|
80
|
+
promise_error: 'immediate',
|
|
81
|
+
web_vital: 'batch',
|
|
82
|
+
http_request: 'throttle',
|
|
83
|
+
},
|
|
84
|
+
batchSize: 10,
|
|
85
|
+
flushInterval: 5000,
|
|
86
|
+
throttleInterval: 1000,
|
|
87
|
+
maxRetries: 3,
|
|
88
|
+
baseRetryDelay: 1000,
|
|
89
|
+
maxRetryDelay: 30000,
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
// ===== 采样配置 =====
|
|
93
|
+
sampling: {
|
|
94
|
+
rate: 0.1,
|
|
95
|
+
mode: 'session',
|
|
96
|
+
typeRates: { web_vital: 0.5 },
|
|
97
|
+
alwaysSample: ['js_error'],
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
// ===== 错误监控配置 =====
|
|
101
|
+
error: {
|
|
102
|
+
captureConsoleError: false,
|
|
103
|
+
dedupeWindow: 5000,
|
|
104
|
+
ignoreErrors: [/ResizeObserver/],
|
|
105
|
+
ignoreUrls: [/chrome-extension/],
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
// ===== Fetch 拦截配置 =====
|
|
109
|
+
fetch: {
|
|
110
|
+
includeUrls: [/\/api\//],
|
|
111
|
+
excludeUrls: [/\/health/],
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
// ===== XHR 拦截配置 =====
|
|
115
|
+
xhr: {
|
|
116
|
+
includeUrls: [/\/api\//],
|
|
117
|
+
excludeUrls: [/\/health/],
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
// ===== 去重配置 =====
|
|
121
|
+
dedupe: {
|
|
122
|
+
windowMs: 5000,
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
// ===== 离线队列配置 =====
|
|
126
|
+
offlineQueue: {
|
|
127
|
+
maxRetries: 3,
|
|
128
|
+
retryInterval: 10000,
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
// ===== 会话录制配置(需单独导入)=====
|
|
132
|
+
// replay: { ... } // 见下方 "会话录制" 章节
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
monitor.setContext({ userId: 'user_123', version: '1.0.0' })
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## 手动注册插件
|
|
139
|
+
|
|
140
|
+
需要更细粒度控制时,可以手动注册插件(插件有 `priority` 自动排序):
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { Monitor } from '@sky-monitor/sdk'
|
|
144
|
+
import { ErrorPlugin } from '@sky-monitor/sdk/plugins/error'
|
|
145
|
+
import { FetchPlugin } from '@sky-monitor/sdk/plugins/fetch'
|
|
146
|
+
import { TransportPlugin } from '@sky-monitor/sdk/plugins/transport'
|
|
147
|
+
|
|
148
|
+
const monitor = new Monitor({ appId: 'my-ai-app', debug: true })
|
|
149
|
+
|
|
150
|
+
// 注册顺序无关,按 priority 自动排序
|
|
151
|
+
monitor.use(new TransportPlugin({ endpoint: '/api/monitor' }))
|
|
152
|
+
monitor.use(new ErrorPlugin({ ignoreErrors: [/ResizeObserver/] }))
|
|
153
|
+
monitor.use(new FetchPlugin({ includeUrls: [/\/api\//] }))
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## 会话录制(Replay)
|
|
157
|
+
|
|
158
|
+
Replay 插件内置 rrweb(~182KB),单独打包以保持主包体积:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
import { createMonitorWithPreset } from '@sky-monitor/sdk'
|
|
162
|
+
import { ReplayPlugin } from '@sky-monitor/sdk/plugins/replay'
|
|
163
|
+
|
|
164
|
+
const monitor = createMonitorWithPreset({
|
|
165
|
+
appId: 'my-ai-app',
|
|
166
|
+
endpoint: '/api/monitor',
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
// 单独注册 Replay 插件
|
|
170
|
+
monitor.use(new ReplayPlugin({
|
|
171
|
+
checkoutEveryNms: 30000,
|
|
172
|
+
bufferSize: 1000,
|
|
173
|
+
flushInterval: 10000,
|
|
174
|
+
blockSelector: '.no-record',
|
|
175
|
+
maskInputOptions: { password: true },
|
|
176
|
+
}))
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## 配置类型定义
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
// ===== 完整配置 =====
|
|
183
|
+
interface MonitorConfig {
|
|
184
|
+
appId: string // 应用标识(必填)
|
|
185
|
+
endpoint?: string // 上报地址,默认 '/api/monitor'
|
|
186
|
+
debug?: boolean // 调试模式
|
|
187
|
+
preset?: 'development' | 'production' | 'minimal'
|
|
188
|
+
plugins?: PluginName[] // 启用的插件
|
|
189
|
+
|
|
190
|
+
// 插件配置
|
|
191
|
+
transport?: TransportPluginOptions
|
|
192
|
+
sampling?: SamplingPluginOptions
|
|
193
|
+
error?: ErrorPluginOptions
|
|
194
|
+
fetch?: FetchPluginOptions
|
|
195
|
+
xhr?: XHRPluginOptions
|
|
196
|
+
dedupe?: DedupePluginOptions
|
|
197
|
+
offlineQueue?: OfflineQueuePluginOptions
|
|
198
|
+
replay?: ReplayPluginOptions
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ===== 传输配置 =====
|
|
202
|
+
interface TransportPluginOptions {
|
|
203
|
+
mode?: 'immediate' | 'batch' | 'throttle'
|
|
204
|
+
typeConfig?: Record<string, 'immediate' | 'batch' | 'throttle'>
|
|
205
|
+
batchSize?: number // 默认 10
|
|
206
|
+
flushInterval?: number // 默认 5000ms
|
|
207
|
+
throttleInterval?: number // 默认 1000ms
|
|
208
|
+
maxRetries?: number // 默认 3
|
|
209
|
+
baseRetryDelay?: number // 默认 1000ms
|
|
210
|
+
maxRetryDelay?: number // 默认 30000ms
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ===== 采样配置 =====
|
|
214
|
+
interface SamplingPluginOptions {
|
|
215
|
+
rate?: number // 默认采样率 (0-1)
|
|
216
|
+
mode?: 'random' | 'session' | 'user'
|
|
217
|
+
typeRates?: Record<string, number>
|
|
218
|
+
alwaysSample?: string[]
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ===== 错误监控配置 =====
|
|
222
|
+
interface ErrorPluginOptions {
|
|
223
|
+
captureConsoleError?: boolean // 默认 false
|
|
224
|
+
dedupeWindow?: number // 默认 5000ms
|
|
225
|
+
ignoreErrors?: RegExp[]
|
|
226
|
+
ignoreUrls?: RegExp[]
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ===== 网络请求配置 =====
|
|
230
|
+
interface FetchPluginOptions {
|
|
231
|
+
includeUrls?: RegExp[]
|
|
232
|
+
excludeUrls?: RegExp[]
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
interface XHRPluginOptions {
|
|
236
|
+
includeUrls?: RegExp[]
|
|
237
|
+
excludeUrls?: RegExp[]
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ===== 去重配置 =====
|
|
241
|
+
interface DedupePluginOptions {
|
|
242
|
+
windowMs?: number // 默认 5000ms
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// ===== 离线队列配置 =====
|
|
246
|
+
interface OfflineQueuePluginOptions {
|
|
247
|
+
maxRetries?: number // 默认 3
|
|
248
|
+
retryInterval?: number // 默认 10000ms
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ===== 会话录制配置 =====
|
|
252
|
+
interface ReplayPluginOptions {
|
|
253
|
+
checkoutEveryNms?: number // 默认 30000ms
|
|
254
|
+
bufferSize?: number // 默认 1000
|
|
255
|
+
flushInterval?: number // 默认 10000ms
|
|
256
|
+
blockSelector?: string
|
|
257
|
+
maskInputOptions?: { password?: boolean; email?: boolean }
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// ===== AI 对话追踪 =====
|
|
261
|
+
interface TraceOptions {
|
|
262
|
+
aiMessageId: string // AI 消息 ID(必填)
|
|
263
|
+
previousTraceId?: string // 重试时的前一个 traceId
|
|
264
|
+
stallThreshold?: number // 卡顿检测阈值(毫秒)
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## AI 对话追踪使用
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
const trace = monitor.createTrace({ aiMessageId: 'msg_123', stallThreshold: 5000 })
|
|
272
|
+
|
|
273
|
+
trace.start() // SSE 开始
|
|
274
|
+
trace.firstChunk() // 首字节 (TTFB)
|
|
275
|
+
trace.phaseStart('thinking') // 阶段开始
|
|
276
|
+
trace.phaseEnd('thinking')
|
|
277
|
+
trace.toolStart('web_search', { query: 'AI' })
|
|
278
|
+
trace.toolEnd('web_search', { success: true, resultCount: 5 })
|
|
279
|
+
trace.complete() // SSE 完成 (TTLB)
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## 后端接收示例
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
// app/api/monitor/route.ts
|
|
286
|
+
import { NextRequest, NextResponse } from 'next/server'
|
|
287
|
+
|
|
288
|
+
interface MonitorEvent {
|
|
289
|
+
id: string
|
|
290
|
+
type: string // 'js_error' | 'http_request' | 'sse_complete' | ...
|
|
291
|
+
timestamp: number
|
|
292
|
+
data: {
|
|
293
|
+
// http_request
|
|
294
|
+
url?: string
|
|
295
|
+
method?: string
|
|
296
|
+
status?: number
|
|
297
|
+
duration?: number // HTTP 请求耗时(毫秒)
|
|
298
|
+
// js_error
|
|
299
|
+
message?: string
|
|
300
|
+
stack?: string
|
|
301
|
+
// sse_complete
|
|
302
|
+
ttfb?: number
|
|
303
|
+
ttlb?: number
|
|
304
|
+
// web_vital
|
|
305
|
+
name?: string
|
|
306
|
+
value?: number
|
|
307
|
+
[key: string]: unknown
|
|
308
|
+
}
|
|
309
|
+
context: {
|
|
310
|
+
appId: string
|
|
311
|
+
sessionId?: string
|
|
312
|
+
userId?: string
|
|
313
|
+
url?: string
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export async function POST(req: NextRequest) {
|
|
318
|
+
const events: MonitorEvent[] = await req.json()
|
|
319
|
+
await db.monitorEvents.createMany({ data: events })
|
|
320
|
+
return NextResponse.json({ success: true })
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## 包体积
|
|
325
|
+
|
|
326
|
+
| 模块 | 原始 | Gzip |
|
|
327
|
+
|-----|------|------|
|
|
328
|
+
| 核心 | 4.1 KB | 1.4 KB |
|
|
329
|
+
| 全量 UMD | 32 KB | 8.2 KB |
|
|
330
|
+
| Replay UMD(含 rrweb) | 182 KB | 58 KB |
|
|
331
|
+
|
|
332
|
+
> Replay 插件内置 rrweb,需单独导入
|
|
333
|
+
|
|
334
|
+
## CDN 使用(UMD)
|
|
335
|
+
|
|
336
|
+
```html
|
|
337
|
+
<!-- 核心 SDK -->
|
|
338
|
+
<script src="https://cdn.example.com/sky-monitor.umd.js"></script>
|
|
339
|
+
<script>
|
|
340
|
+
const monitor = SkyMonitor.createMonitor({ appId: 'my-app' })
|
|
341
|
+
monitor.setContext({ userId: 'user_123' })
|
|
342
|
+
</script>
|
|
343
|
+
|
|
344
|
+
<!-- 需要 Replay 时额外加载 -->
|
|
345
|
+
<script src="https://cdn.example.com/sky-monitor-replay.umd.js"></script>
|
|
346
|
+
<script>
|
|
347
|
+
monitor.use(new SkyMonitorReplay.ReplayPlugin({
|
|
348
|
+
blockSelector: '.no-record'
|
|
349
|
+
}))
|
|
350
|
+
</script>
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## License
|
|
354
|
+
|
|
355
|
+
MIT
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {a as a$1}from'./chunk-4DK7RV2K.js';var d="sky-monitor",n="events";var s=class{constructor(){this._db=null;this._dbPromise=null;}async save(e){try{(await this._getDB()).transaction(n,"readwrite").objectStore(n).add(e);}catch(t){console.warn("[SkyMonitor] Storage save failed:",t);}}async getAll(){try{let e=await this._getDB();return new Promise((t,r)=>{let i=e.transaction(n,"readonly").objectStore(n).getAll();i.onsuccess=()=>t(i.result),i.onerror=()=>r(i.error);})}catch(e){return console.warn("[SkyMonitor] Storage getAll failed:",e),[]}}async clear(){try{(await this._getDB()).transaction(n,"readwrite").objectStore(n).clear();}catch(e){console.warn("[SkyMonitor] Storage clear failed:",e);}}async _getDB(){return this._db?this._db:(this._dbPromise||(this._dbPromise=this._openDB()),this._db=await this._dbPromise,this._db)}_openDB(){return new Promise((e,t)=>{let r=indexedDB.open(d,1);r.onerror=()=>t(r.error),r.onsuccess=()=>e(r.result),r.onupgradeneeded=u=>{let i=u.target.result;i.objectStoreNames.contains(n)||i.createObjectStore(n,{keyPath:"id"});};})}};var a=class{constructor(){this._onlineHandler=null;this._retryTimer=null;}setupOnce(e,t){typeof window>"u"||(this._onlineHandler=()=>t.onOnline(),window.addEventListener("online",this._onlineHandler),this._retryTimer=setInterval(()=>t.onRetry(),e),t.onRetry());}teardown(){this._retryTimer&&(clearInterval(this._retryTimer),this._retryTimer=null),this._onlineHandler&&typeof window<"u"&&(window.removeEventListener("online",this._onlineHandler),this._onlineHandler=null);}isOnline(){return typeof navigator>"u"?true:navigator.onLine}};var c=class{constructor(e={}){this.name="offline-queue";this.priority=400;this._retryCount=0;this._isSending=false;this._storage=e.storage??new s,this._maxRetries=e.maxRetries??3,this._retryInterval=e.retryInterval??1e4,this._platformAdapter=new a;}setupOnce(e){a$1().registerOfflineQueue(this),this._platformAdapter.setupOnce(this._retryInterval,{onOnline:()=>this._onOnline(),onRetry:()=>this._retry()});}processEvent(e){return this._storage.save(e).catch(t=>{console.warn("[SkyMonitor] OfflineQueue save failed:",t);}),e}async saveEvents(e){for(let t of e)await this._storage.save(t);}teardown(){this._platformAdapter.teardown();}_onOnline(){this._retryCount=0,this._retry();}async _retry(){if(this._platformAdapter.isOnline()&&!(this._retryCount>=this._maxRetries)&&!this._isSending){this._isSending=true;try{let e=await this._storage.getAll();if(e.length===0){this._isSending=!1;return}await a$1().sendEvents(e)?(await this._storage.clear(),this._retryCount=0):this._retryCount++;}catch(e){this._retryCount++,console.warn("[SkyMonitor] OfflineQueue retry failed:",e);}finally{this._isSending=false;}}}async flush(){try{let e=await this._storage.getAll();if(e.length===0)return !0;let t=await a$1().sendEvents(e);return t&&await this._storage.clear(),t}catch(e){return console.warn("[SkyMonitor] OfflineQueue flush failed:",e),false}}async getCount(){return (await this._storage.getAll()).length}};export{s as a,c as b};//# sourceMappingURL=chunk-2EV6VMEO.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-2EV6VMEO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/platforms/browser/storage.ts","../src/plugins/offline-queue/platforms/browser/index.ts","../src/plugins/offline-queue/index.ts"],"names":["DB_NAME","STORE_NAME","BrowserStorage","event","e","db","resolve","reject","request","BrowserOfflineQueueAdapter","retryInterval","callbacks","OfflineQueuePlugin","options","_monitor","getPluginCoordinator","events","success"],"mappings":"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","file":"chunk-2EV6VMEO.js","sourcesContent":["/**\n * 浏览器存储适配器 - IndexedDB\n */\n\nimport type { IStorage, MonitorEvent } from '../../core/types'\n\nconst DB_NAME = 'sky-monitor'\nconst STORE_NAME = 'events'\nconst DB_VERSION = 1\n\n/**\n * IndexedDB 存储实现\n */\nexport class BrowserStorage implements IStorage {\n private _db: IDBDatabase | null = null\n private _dbPromise: Promise<IDBDatabase> | null = null\n\n /**\n * 保存事件\n */\n async save(event: MonitorEvent): Promise<void> {\n try {\n const db = await this._getDB()\n const tx = db.transaction(STORE_NAME, 'readwrite')\n tx.objectStore(STORE_NAME).add(event)\n } catch (e) {\n console.warn('[SkyMonitor] Storage save failed:', e)\n }\n }\n\n /**\n * 获取所有事件\n */\n async getAll(): Promise<MonitorEvent[]> {\n try {\n const db = await this._getDB()\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, 'readonly')\n const request = tx.objectStore(STORE_NAME).getAll()\n request.onsuccess = () => resolve(request.result)\n request.onerror = () => reject(request.error)\n })\n } catch (e) {\n console.warn('[SkyMonitor] Storage getAll failed:', e)\n return []\n }\n }\n\n /**\n * 清空所有事件\n */\n async clear(): Promise<void> {\n try {\n const db = await this._getDB()\n const tx = db.transaction(STORE_NAME, 'readwrite')\n tx.objectStore(STORE_NAME).clear()\n } catch (e) {\n console.warn('[SkyMonitor] Storage clear failed:', e)\n }\n }\n\n /**\n * 获取数据库连接(单例)\n */\n private async _getDB(): Promise<IDBDatabase> {\n if (this._db) return this._db\n\n if (!this._dbPromise) {\n this._dbPromise = this._openDB()\n }\n\n this._db = await this._dbPromise\n return this._db\n }\n\n /**\n * 打开数据库\n */\n private _openDB(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(DB_NAME, DB_VERSION)\n\n request.onerror = () => reject(request.error)\n request.onsuccess = () => resolve(request.result)\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result\n if (!db.objectStoreNames.contains(STORE_NAME)) {\n db.createObjectStore(STORE_NAME, { keyPath: 'id' })\n }\n }\n })\n }\n}\n","/**\n * OfflineQueue 插件 - 浏览器平台实现\n *\n * 负责浏览器特定的网络状态检测:\n * - navigator.onLine 检测\n * - online/offline 事件监听\n */\n\nexport interface BrowserOfflineQueueCallbacks {\n onOnline: () => void\n onRetry: () => void\n}\n\n/**\n * 浏览器平台适配\n * 管理网络状态检测和事件监听\n */\nexport class BrowserOfflineQueueAdapter {\n private _onlineHandler: (() => void) | null = null\n private _retryTimer: ReturnType<typeof setInterval> | null = null\n\n /**\n * 初始化浏览器网络状态监听\n */\n setupOnce(retryInterval: number, callbacks: BrowserOfflineQueueCallbacks): void {\n if (typeof window === 'undefined') return\n\n this._onlineHandler = () => callbacks.onOnline()\n window.addEventListener('online', this._onlineHandler)\n\n // 启动重试定时器\n this._retryTimer = setInterval(() => callbacks.onRetry(), retryInterval)\n\n // 页面加载时尝试重传\n callbacks.onRetry()\n }\n\n /**\n * 清理浏览器事件监听\n */\n teardown(): void {\n if (this._retryTimer) {\n clearInterval(this._retryTimer)\n this._retryTimer = null\n }\n if (this._onlineHandler && typeof window !== 'undefined') {\n window.removeEventListener('online', this._onlineHandler)\n this._onlineHandler = null\n }\n }\n\n /**\n * 检查网络是否在线\n */\n isOnline(): boolean {\n if (typeof navigator === 'undefined') return true\n return navigator.onLine\n }\n}\n","/**\n * OfflineQueue 插件 - 离线队列\n *\n * 修复版本:\n * - 正确的重传逻辑\n * - 通过协调器与 TransportPlugin 通信\n * - 发送成功后才清理\n */\n\nimport type { Plugin, IMonitor, MonitorEvent, IStorage } from '../../core/types'\nimport { getPluginCoordinator, type IOfflineQueue } from '../../core/plugin-coordinator'\nimport { BrowserStorage } from '../../platforms/browser/storage'\nimport { BrowserOfflineQueueAdapter } from './platforms/browser'\n\nexport interface OfflineQueuePluginOptions {\n /** 存储适配器(可选,默认使用 BrowserStorage) */\n storage?: IStorage\n /** 最大重试次数,默认 3 */\n maxRetries?: number\n /** 重试间隔(毫秒),默认 10000 */\n retryInterval?: number\n}\n\n/**\n * OfflineQueue 插件\n * IndexedDB 离线队列,网络恢复后自动重传\n */\nexport class OfflineQueuePlugin implements Plugin, IOfflineQueue {\n name = 'offline-queue'\n priority = 400\n\n private _storage: IStorage\n private _maxRetries: number\n private _retryInterval: number\n private _retryCount: number = 0\n private _isSending: boolean = false\n private _platformAdapter: BrowserOfflineQueueAdapter\n\n constructor(options: OfflineQueuePluginOptions = {}) {\n this._storage = options.storage ?? new BrowserStorage()\n this._maxRetries = options.maxRetries ?? 3\n this._retryInterval = options.retryInterval ?? 10000\n this._platformAdapter = new BrowserOfflineQueueAdapter()\n }\n\n /**\n * 插件初始化\n */\n setupOnce(_monitor: IMonitor): void {\n // 注册到协调器\n getPluginCoordinator().registerOfflineQueue(this)\n\n this._platformAdapter.setupOnce(this._retryInterval, {\n onOnline: () => this._onOnline(),\n onRetry: () => this._retry(),\n })\n }\n\n /**\n * 事件处理:存入离线队列\n */\n processEvent(event: MonitorEvent): MonitorEvent {\n this._storage.save(event).catch((e) => {\n console.warn('[SkyMonitor] OfflineQueue save failed:', e)\n })\n return event\n }\n\n /**\n * 批量保存事件到离线队列\n */\n async saveEvents(events: MonitorEvent[]): Promise<void> {\n for (const event of events) {\n await this._storage.save(event)\n }\n }\n\n /**\n * 插件销毁\n */\n teardown(): void {\n this._platformAdapter.teardown()\n }\n\n /**\n * 网络恢复时重置重试计数并立即重传\n */\n private _onOnline(): void {\n this._retryCount = 0\n this._retry()\n }\n\n /**\n * 重传离线事件(通过协调器发送)\n */\n private async _retry(): Promise<void> {\n // 检查网络状态\n if (!this._platformAdapter.isOnline()) return\n // 检查重试次数\n if (this._retryCount >= this._maxRetries) return\n // 防止并发重传\n if (this._isSending) return\n\n this._isSending = true\n\n try {\n const events = await this._storage.getAll()\n if (events.length === 0) {\n this._isSending = false\n return\n }\n\n // 通过协调器发送\n const success = await getPluginCoordinator().sendEvents(events)\n\n if (success) {\n // 发送成功,清理存储\n await this._storage.clear()\n this._retryCount = 0\n } else {\n // 发送失败,增加重试计数\n this._retryCount++\n }\n } catch (e) {\n this._retryCount++\n console.warn('[SkyMonitor] OfflineQueue retry failed:', e)\n } finally {\n this._isSending = false\n }\n }\n\n /**\n * 手动触发重传\n */\n async flush(): Promise<boolean> {\n try {\n const events = await this._storage.getAll()\n if (events.length === 0) return true\n\n const success = await getPluginCoordinator().sendEvents(events)\n if (success) {\n await this._storage.clear()\n }\n return success\n } catch (e) {\n console.warn('[SkyMonitor] OfflineQueue flush failed:', e)\n return false\n }\n }\n\n /**\n * 获取离线事件数量\n */\n async getCount(): Promise<number> {\n const events = await this._storage.getAll()\n return events.length\n }\n}\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
var l=class{constructor(){this._originalOnerror=null;this._unhandledRejectionHandler=null;this._resourceErrorHandler=null;this._originalConsoleError=null;}setupOnce(e,r){typeof window>"u"||(this._setupOnerror(r),this._setupUnhandledRejection(r),this._setupResourceError(r),e&&this._setupConsoleError(r));}teardown(){typeof window>"u"||(this._originalOnerror!==null&&(window.onerror=this._originalOnerror),this._unhandledRejectionHandler&&window.removeEventListener("unhandledrejection",this._unhandledRejectionHandler),this._resourceErrorHandler&&window.removeEventListener("error",this._resourceErrorHandler,true),this._originalConsoleError&&(console.error=this._originalConsoleError));}_setupOnerror(e){this._originalOnerror=window.onerror,window.onerror=(r,n,o,t,s)=>{e.onJSError(typeof r=="string"?r:r?.type||"Unknown error",n,o,t,s),this._originalOnerror?.call(window,r,n,o,t,s);};}_setupUnhandledRejection(e){this._unhandledRejectionHandler=r=>{e.onPromiseError(r.reason);},window.addEventListener("unhandledrejection",this._unhandledRejectionHandler);}_setupResourceError(e){this._resourceErrorHandler=r=>{let n=r.target;!n||n===window||!n.tagName||e.onResourceError(n);},window.addEventListener("error",this._resourceErrorHandler,true);}_setupConsoleError(e){this._originalConsoleError=console.error,console.error=(...r)=>{e.onConsoleError(r),this._originalConsoleError.apply(console,r);};}};var d=class{constructor(e={}){this.name="error";this.priority=150;this._monitor=null;this._seen=new Map;this._options={captureConsoleError:false,dedupeWindow:5e3,ignoreErrors:[],ignoreUrls:[],...e},this._platformAdapter=new l;}setupOnce(e){this._monitor=e,this._platformAdapter.setupOnce(this._options.captureConsoleError,{onJSError:(r,n,o,t,s)=>this._handleJSError(r,n,o,t,s),onPromiseError:r=>this._handlePromiseError(r),onResourceError:r=>this._handleResourceError(r),onConsoleError:r=>this._handleConsoleError(r)});}teardown(){this._platformAdapter.teardown(),this._monitor=null;}_handleJSError(e,r,n,o,t){let s=e,a=this._isCrossOriginError(s,r,n,o);if(this._shouldIgnore(s,r||""))return;let i=this._getFingerprint(t?.stack||`${r}:${n}:${o}`);this._isDuplicate(i)||(a?this._monitor.track("js_error",{message:"Script error (cross-origin)",isCrossOrigin:true,hint:'Add crossorigin="anonymous" to script tag and ensure CORS headers'}):this._monitor.track("js_error",{message:s,filename:r,lineno:n,colno:o,stack:t?.stack,parsedStack:this._parseStack(t?.stack)}));}_handlePromiseError(e){let r=e instanceof Error?e.message:String(e),n=e instanceof Error?e.stack:void 0;if(this._shouldIgnore(r,""))return;let o=this._getFingerprint(n||r);this._isDuplicate(o)||this._monitor.track("promise_error",{message:r,stack:n});}_handleResourceError(e){let r=e.tagName.toLowerCase(),n=e.src||e.href||"";if(!n||this._shouldIgnore("",n))return;let o=this._getResourceType(r),t=this._getFingerprint(n);this._isDuplicate(t)||this._monitor.track("resource_error",{resourceUrl:n,resourceType:o,tagName:r});}_handleConsoleError(e){let r=e.map(n=>String(n)).join(" ");if(!this._shouldIgnore(r,"")){let n=this._getFingerprint(r);this._isDuplicate(n)||this._monitor.track("console_error",{message:r});}}_isCrossOriginError(e,r,n,o){return e==="Script error."||e==="Script error"||e.toLowerCase().includes("script error")&&!r&&!n&&!o}_parseStack(e){if(!e)return;let r=[],n=/^\s*at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?$/,o=/^(.+?)@(.+?):(\d+):(\d+)$/,t=e.split(`
|
|
2
|
+
`).slice(0,5);for(let s of t){let a=s.trim();if(!a||a.startsWith("Error:"))continue;let i=n.exec(a);if(i){r.push({function:i[1]||"<anonymous>",filename:i[2],lineno:parseInt(i[3],10),colno:parseInt(i[4],10)});continue}i=o.exec(a),i&&r.push({function:i[1]||"<anonymous>",filename:i[2],lineno:parseInt(i[3],10),colno:parseInt(i[4],10)});}return r.length>0?r:void 0}_shouldIgnore(e,r){return !!(this._options.ignoreErrors.some(n=>n.test(e))||r&&this._options.ignoreUrls.some(n=>n.test(r)))}_getFingerprint(e){let r=e.split(`
|
|
3
|
+
`).slice(0,3).join("");return this._hash(r)}_hash(e){let r=0;for(let n=0;n<e.length;n++){let o=e.charCodeAt(n);r=(r<<5)-r+o,r=r&r;}return r.toString(36)}_isDuplicate(e){let r=Date.now(),n=this._seen.get(e);return n&&r-n<this._options.dedupeWindow?true:(this._seen.set(e,r),this._cleanup(r),false)}_cleanup(e){for(let[r,n]of this._seen)e-n>=this._options.dedupeWindow&&this._seen.delete(r);}_getResourceType(e){return {img:"image",script:"script",link:"stylesheet",video:"video",audio:"audio"}[e]||"unknown"}};export{d as a};//# sourceMappingURL=chunk-2R7LNQYV.js.map
|
|
4
|
+
//# sourceMappingURL=chunk-2R7LNQYV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/plugins/error/platforms/browser/index.ts","../src/plugins/error/index.ts"],"names":["BrowserErrorAdapter","captureConsoleError","callbacks","msg","url","line","col","error","e","target","args","ErrorPlugin","options","monitor","reason","element","message","isCrossOrigin","fingerprint","stack","tagName","resourceUrl","resourceType","a","frames","chromeRegex","firefoxRegex","lines","trimmed","match","re","str","hash","i","char","now","lastSeen","fp","ts"],"mappings":"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","file":"chunk-2R7LNQYV.js","sourcesContent":["/**\n * Error 插件 - 浏览器平台实现\n *\n * 负责浏览器特定的错误捕获:\n * - window.onerror\n * - unhandledrejection 事件\n * - 资源加载失败事件\n * - console.error 劫持\n */\n\nexport interface BrowserErrorCallbacks {\n onJSError: (msg: string, url: string | undefined, line: number | undefined, col: number | undefined, error: Error | undefined) => void\n onPromiseError: (reason: unknown) => void\n onResourceError: (element: HTMLElement) => void\n onConsoleError: (args: unknown[]) => void\n}\n\n/**\n * 浏览器平台适配\n * 管理错误事件监听\n */\nexport class BrowserErrorAdapter {\n private _originalOnerror: OnErrorEventHandler | null = null\n private _unhandledRejectionHandler: ((e: PromiseRejectionEvent) => void) | null = null\n private _resourceErrorHandler: ((e: Event) => void) | null = null\n private _originalConsoleError: typeof console.error | null = null\n\n /**\n * 初始化错误监听\n */\n setupOnce(\n captureConsoleError: boolean,\n callbacks: BrowserErrorCallbacks\n ): void {\n if (typeof window === 'undefined') return\n\n this._setupOnerror(callbacks)\n this._setupUnhandledRejection(callbacks)\n this._setupResourceError(callbacks)\n\n if (captureConsoleError) {\n this._setupConsoleError(callbacks)\n }\n }\n\n /**\n * 清理错误监听\n */\n teardown(): void {\n if (typeof window === 'undefined') return\n\n if (this._originalOnerror !== null) {\n window.onerror = this._originalOnerror\n }\n if (this._unhandledRejectionHandler) {\n window.removeEventListener('unhandledrejection', this._unhandledRejectionHandler)\n }\n if (this._resourceErrorHandler) {\n window.removeEventListener('error', this._resourceErrorHandler, true)\n }\n if (this._originalConsoleError) {\n console.error = this._originalConsoleError\n }\n }\n\n private _setupOnerror(callbacks: BrowserErrorCallbacks): void {\n this._originalOnerror = window.onerror\n\n window.onerror = (msg, url, line, col, error) => {\n callbacks.onJSError(\n typeof msg === 'string' ? msg : msg?.type || 'Unknown error',\n url,\n line,\n col,\n error\n )\n this._originalOnerror?.call(window, msg, url, line, col, error)\n }\n }\n\n private _setupUnhandledRejection(callbacks: BrowserErrorCallbacks): void {\n this._unhandledRejectionHandler = (e: PromiseRejectionEvent) => {\n callbacks.onPromiseError(e.reason)\n }\n\n window.addEventListener('unhandledrejection', this._unhandledRejectionHandler)\n }\n\n private _setupResourceError(callbacks: BrowserErrorCallbacks): void {\n this._resourceErrorHandler = (e: Event) => {\n const target = e.target\n if (!target || target === window || !(target as HTMLElement).tagName) return\n\n callbacks.onResourceError(target as HTMLElement)\n }\n\n window.addEventListener('error', this._resourceErrorHandler, true)\n }\n\n private _setupConsoleError(callbacks: BrowserErrorCallbacks): void {\n this._originalConsoleError = console.error\n\n console.error = (...args: unknown[]) => {\n callbacks.onConsoleError(args)\n this._originalConsoleError!.apply(console, args)\n }\n }\n}\n","/**\n * Error 插件 - JS 异常捕获\n */\n\nimport type { Plugin, IMonitor } from '../../core/types'\nimport { BrowserErrorAdapter } from './platforms/browser'\n\nexport interface ErrorPluginOptions {\n /** 是否劫持 console.error,默认 false */\n captureConsoleError?: boolean\n /** 去重窗口(毫秒),默认 5000 */\n dedupeWindow?: number\n /** 忽略的错误消息正则 */\n ignoreErrors?: RegExp[]\n /** 忽略的脚本 URL 正则 */\n ignoreUrls?: RegExp[]\n}\n\n/**\n * Error 插件\n * 捕获 JS 运行时错误、Promise 错误、资源加载失败\n */\nexport class ErrorPlugin implements Plugin {\n name = 'error'\n priority = 150\n\n private _monitor: IMonitor | null = null\n private _options: Required<ErrorPluginOptions>\n private _seen: Map<string, number> = new Map()\n private _platformAdapter: BrowserErrorAdapter\n\n constructor(options: ErrorPluginOptions = {}) {\n this._options = {\n captureConsoleError: false,\n dedupeWindow: 5000,\n ignoreErrors: [],\n ignoreUrls: [],\n ...options,\n }\n this._platformAdapter = new BrowserErrorAdapter()\n }\n\n /**\n * 插件初始化\n */\n setupOnce(monitor: IMonitor): void {\n this._monitor = monitor\n this._platformAdapter.setupOnce(this._options.captureConsoleError, {\n onJSError: (msg, url, line, col, error) => this._handleJSError(msg, url, line, col, error),\n onPromiseError: (reason) => this._handlePromiseError(reason),\n onResourceError: (element) => this._handleResourceError(element),\n onConsoleError: (args) => this._handleConsoleError(args),\n })\n }\n\n /**\n * 插件销毁\n */\n teardown(): void {\n this._platformAdapter.teardown()\n this._monitor = null\n }\n\n private _handleJSError(\n msg: string,\n url: string | undefined,\n line: number | undefined,\n col: number | undefined,\n error: Error | undefined\n ): void {\n const message = msg\n\n // 检测跨域脚本错误\n const isCrossOrigin = this._isCrossOriginError(message, url, line, col)\n\n if (this._shouldIgnore(message, url || '')) return\n\n const fingerprint = this._getFingerprint(error?.stack || `${url}:${line}:${col}`)\n if (this._isDuplicate(fingerprint)) return\n\n if (isCrossOrigin) {\n this._monitor!.track('js_error', {\n message: 'Script error (cross-origin)',\n isCrossOrigin: true,\n hint: 'Add crossorigin=\"anonymous\" to script tag and ensure CORS headers',\n })\n } else {\n this._monitor!.track('js_error', {\n message,\n filename: url,\n lineno: line,\n colno: col,\n stack: error?.stack,\n parsedStack: this._parseStack(error?.stack),\n })\n }\n }\n\n private _handlePromiseError(reason: unknown): void {\n const message = reason instanceof Error ? reason.message : String(reason)\n const stack = reason instanceof Error ? reason.stack : undefined\n\n if (this._shouldIgnore(message, '')) return\n\n const fingerprint = this._getFingerprint(stack || message)\n if (this._isDuplicate(fingerprint)) return\n\n this._monitor!.track('promise_error', {\n message,\n stack,\n })\n }\n\n private _handleResourceError(element: HTMLElement): void {\n const tagName = element.tagName.toLowerCase()\n const resourceUrl =\n (element as HTMLImageElement).src ||\n (element as HTMLLinkElement).href ||\n ''\n\n if (!resourceUrl) return\n if (this._shouldIgnore('', resourceUrl)) return\n\n const resourceType = this._getResourceType(tagName)\n const fingerprint = this._getFingerprint(resourceUrl)\n if (this._isDuplicate(fingerprint)) return\n\n this._monitor!.track('resource_error', {\n resourceUrl,\n resourceType,\n tagName,\n })\n }\n\n private _handleConsoleError(args: unknown[]): void {\n const message = args.map((a) => String(a)).join(' ')\n\n if (!this._shouldIgnore(message, '')) {\n const fingerprint = this._getFingerprint(message)\n if (!this._isDuplicate(fingerprint)) {\n this._monitor!.track('console_error', { message })\n }\n }\n }\n\n private _isCrossOriginError(\n message: string,\n url: string | undefined,\n line: number | undefined,\n col: number | undefined\n ): boolean {\n return (\n message === 'Script error.' ||\n message === 'Script error' ||\n (message.toLowerCase().includes('script error') && !url && !line && !col)\n )\n }\n\n private _parseStack(stack?: string): Array<{\n filename?: string\n function?: string\n lineno?: number\n colno?: number\n }> | undefined {\n if (!stack) return undefined\n\n const frames: Array<{\n filename?: string\n function?: string\n lineno?: number\n colno?: number\n }> = []\n\n const chromeRegex = /^\\s*at\\s+(?:(.+?)\\s+\\()?(.+?):(\\d+):(\\d+)\\)?$/\n const firefoxRegex = /^(.+?)@(.+?):(\\d+):(\\d+)$/\n\n const lines = stack.split('\\n').slice(0, 5)\n\n for (const line of lines) {\n const trimmed = line.trim()\n if (!trimmed || trimmed.startsWith('Error:')) continue\n\n let match = chromeRegex.exec(trimmed)\n if (match) {\n frames.push({\n function: match[1] || '<anonymous>',\n filename: match[2],\n lineno: parseInt(match[3], 10),\n colno: parseInt(match[4], 10),\n })\n continue\n }\n\n match = firefoxRegex.exec(trimmed)\n if (match) {\n frames.push({\n function: match[1] || '<anonymous>',\n filename: match[2],\n lineno: parseInt(match[3], 10),\n colno: parseInt(match[4], 10),\n })\n }\n }\n\n return frames.length > 0 ? frames : undefined\n }\n\n private _shouldIgnore(message: string, url: string): boolean {\n if (this._options.ignoreErrors.some((re) => re.test(message))) return true\n if (url && this._options.ignoreUrls.some((re) => re.test(url))) return true\n return false\n }\n\n private _getFingerprint(stack: string): string {\n const frames = stack.split('\\n').slice(0, 3).join('')\n return this._hash(frames)\n }\n\n private _hash(str: string): string {\n let hash = 0\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i)\n hash = ((hash << 5) - hash) + char\n hash = hash & hash\n }\n return hash.toString(36)\n }\n\n private _isDuplicate(fingerprint: string): boolean {\n const now = Date.now()\n const lastSeen = this._seen.get(fingerprint)\n\n if (lastSeen && now - lastSeen < this._options.dedupeWindow) {\n return true\n }\n\n this._seen.set(fingerprint, now)\n this._cleanup(now)\n return false\n }\n\n private _cleanup(now: number): void {\n for (const [fp, ts] of this._seen) {\n if (now - ts >= this._options.dedupeWindow) {\n this._seen.delete(fp)\n }\n }\n }\n\n private _getResourceType(tagName: string): string {\n const map: Record<string, string> = {\n img: 'image',\n script: 'script',\n link: 'stylesheet',\n video: 'video',\n audio: 'audio',\n }\n return map[tagName] || 'unknown'\n }\n}\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var t=class{constructor(){this._transport=null;this._offlineQueue=null;}registerTransport(e){this._transport=e;}registerOfflineQueue(e){this._offlineQueue=e;}getTransport(){return this._transport}getOfflineQueue(){return this._offlineQueue}async moveToOffline(e){if(this._offlineQueue)try{await this._offlineQueue.saveEvents(e);}catch(o){console.warn("[SkyMonitor] Move to offline failed:",o);}}async sendEvents(e){return this._transport?this._transport.send(e):false}teardown(){this._transport=null,this._offlineQueue=null;}},n=null;function i(){return n||(n=new t),n}export{i as a};//# sourceMappingURL=chunk-4DK7RV2K.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-4DK7RV2K.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/plugin-coordinator.ts"],"names":["PluginCoordinator","transport","offlineQueue","events","e","_coordinator","getPluginCoordinator"],"mappings":"AA+BO,IAAMA,CAAAA,CAAN,KAAwB,CAAxB,WAAA,EAAA,CACL,KAAQ,UAAA,CAAgC,IAAA,CACxC,KAAQ,aAAA,CAAsC,KAAA,CAK9C,kBAAkBC,CAAAA,CAA6B,CAC7C,KAAK,UAAA,CAAaA,EACpB,CAKA,oBAAA,CAAqBC,CAAAA,CAAmC,CACtD,IAAA,CAAK,aAAA,CAAgBA,EACvB,CAKA,YAAA,EAAkC,CAChC,OAAO,IAAA,CAAK,UACd,CAKA,eAAA,EAAwC,CACtC,OAAO,IAAA,CAAK,aACd,CAMA,MAAM,cAAcC,CAAAA,CAAuC,CACzD,GAAI,IAAA,CAAK,aAAA,CACP,GAAI,CACF,MAAM,IAAA,CAAK,cAAc,UAAA,CAAWA,CAAM,EAC5C,CAAA,MAASC,CAAAA,CAAG,CACV,OAAA,CAAQ,IAAA,CAAK,uCAAwCA,CAAC,EACxD,CAEJ,CAMA,MAAM,WAAWD,CAAAA,CAA0C,CACzD,OAAI,IAAA,CAAK,UAAA,CACA,IAAA,CAAK,UAAA,CAAW,IAAA,CAAKA,CAAM,EAE7B,KACT,CAKA,UAAiB,CACf,IAAA,CAAK,WAAa,IAAA,CAClB,IAAA,CAAK,cAAgB,KACvB,CACF,EAGIE,CAAAA,CAAyC,IAAA,CAKtC,SAASC,CAAAA,EAA0C,CACxD,OAAKD,CAAAA,GACHA,CAAAA,CAAe,IAAIL,CAAAA,CAAAA,CAEdK,CACT","file":"chunk-4DK7RV2K.js","sourcesContent":["/**\n * Plugin Coordinator - 插件协调器\n *\n * 解决插件之间的双向依赖问题:\n * - TransportPlugin 和 OfflineQueuePlugin 的协调\n * - 统一管理插件间的通信\n *\n * 设计原则:\n * - 插件不直接相互引用\n * - 通过协调器进行通信\n * - 单向依赖:Plugin → Coordinator\n */\n\nimport type { MonitorEvent, ITransport } from './types'\n\n/**\n * 离线队列接口\n */\nexport interface IOfflineQueue {\n /** 保存事件到离线队列 */\n saveEvents(events: MonitorEvent[]): Promise<void>\n /** 手动触发重传 */\n flush(): Promise<boolean>\n /** 获取离线事件数量 */\n getCount(): Promise<number>\n}\n\n/**\n * 插件协调器\n * 管理 TransportPlugin 和 OfflineQueuePlugin 之间的通信\n */\nexport class PluginCoordinator {\n private _transport: ITransport | null = null\n private _offlineQueue: IOfflineQueue | null = null\n\n /**\n * 注册传输适配器\n */\n registerTransport(transport: ITransport): void {\n this._transport = transport\n }\n\n /**\n * 注册离线队列\n */\n registerOfflineQueue(offlineQueue: IOfflineQueue): void {\n this._offlineQueue = offlineQueue\n }\n\n /**\n * 获取传输适配器(供 OfflineQueuePlugin 使用)\n */\n getTransport(): ITransport | null {\n return this._transport\n }\n\n /**\n * 获取离线队列(供 TransportPlugin 使用)\n */\n getOfflineQueue(): IOfflineQueue | null {\n return this._offlineQueue\n }\n\n /**\n * 将事件移入离线队列\n * TransportPlugin 在重试失败后调用\n */\n async moveToOffline(events: MonitorEvent[]): Promise<void> {\n if (this._offlineQueue) {\n try {\n await this._offlineQueue.saveEvents(events)\n } catch (e) {\n console.warn('[SkyMonitor] Move to offline failed:', e)\n }\n }\n }\n\n /**\n * 发送事件\n * OfflineQueuePlugin 在重传时调用\n */\n async sendEvents(events: MonitorEvent[]): Promise<boolean> {\n if (this._transport) {\n return this._transport.send(events)\n }\n return false\n }\n\n /**\n * 清理\n */\n teardown(): void {\n this._transport = null\n this._offlineQueue = null\n }\n}\n\n/** 全局协调器实例 */\nlet _coordinator: PluginCoordinator | null = null\n\n/**\n * 获取全局协调器实例\n */\nexport function getPluginCoordinator(): PluginCoordinator {\n if (!_coordinator) {\n _coordinator = new PluginCoordinator()\n }\n return _coordinator\n}\n\n/**\n * 重置协调器(用于测试)\n */\nexport function resetPluginCoordinator(): void {\n if (_coordinator) {\n _coordinator.teardown()\n _coordinator = null\n }\n}\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';var s=class{constructor(){this._originalFetch=null;}setupOnce(o,t){typeof window>"u"||(this._originalFetch=window.fetch,window.fetch=async(r,e)=>{let n=typeof r=="string"?r:r.url;if(o(n))return this._originalFetch.call(window,r,e);let a=Date.now(),l=e?.method||"GET";try{await t.onRequest(n,l,a);let i=await this._originalFetch.call(window,r,e);return t.onSuccess(n,l,i.status,Date.now()-a),i}catch(i){throw t.onError(n,l,Date.now()-a,String(i)),i}});}teardown(){this._originalFetch&&typeof window<"u"&&(window.fetch=this._originalFetch),this._originalFetch=null;}};var u=class{constructor(o={}){this.name="fetch";this.priority=200;this._monitor=null;this._options=o,this._platformAdapter=new s;}setupOnce(o){this._monitor=o,this._platformAdapter.setupOnce(t=>this._shouldIgnore(t),{onRequest:async()=>{},onSuccess:(t,r,e,n)=>{this._monitor.track("http_request",{url:t,method:r,status:e,duration:n});},onError:(t,r,e,n)=>{this._monitor.track("http_request",{url:t,method:r,status:0,duration:e,error:n});}});}teardown(){this._platformAdapter.teardown(),this._monitor=null;}_shouldIgnore(o){let{includeUrls:t,excludeUrls:r}=this._options;return !!(r?.some(e=>e.test(o))||t&&!t.some(e=>e.test(o)))}};exports.a=u;//# sourceMappingURL=chunk-4GL6IXHZ.cjs.map
|
|
2
|
+
//# sourceMappingURL=chunk-4GL6IXHZ.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/plugins/fetch/platforms/browser/index.ts","../src/plugins/fetch/index.ts"],"names":["BrowserFetchAdapter","shouldIgnore","callbacks","input","init","url","start","method","response","error","FetchPlugin","options","monitor","status","duration","includeUrls","excludeUrls","re"],"mappings":"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","file":"chunk-4GL6IXHZ.cjs","sourcesContent":["/**\n * Fetch 插件 - 浏览器平台实现\n *\n * 负责浏览器特定的 fetch 拦截:\n * - 包装 window.fetch\n * - 保留原始引用\n */\n\nexport interface BrowserFetchCallbacks {\n onRequest: (url: string, method: string, start: number) => Promise<void>\n onSuccess: (url: string, method: string, status: number, duration: number) => void\n onError: (url: string, method: string, duration: number, error: string) => void\n}\n\n/**\n * 浏览器平台适配\n * 管理 window.fetch 拦截\n */\nexport class BrowserFetchAdapter {\n private _originalFetch: typeof fetch | null = null\n\n /**\n * 初始化 fetch 拦截\n */\n setupOnce(\n shouldIgnore: (url: string) => boolean,\n callbacks: BrowserFetchCallbacks\n ): void {\n if (typeof window === 'undefined') return\n\n this._originalFetch = window.fetch\n\n window.fetch = async (input, init) => {\n const url = typeof input === 'string' ? input : (input as Request).url\n\n if (shouldIgnore(url)) {\n return this._originalFetch!.call(window, input, init)\n }\n\n const start = Date.now()\n const method = init?.method || 'GET'\n\n try {\n await callbacks.onRequest(url, method, start)\n const response = await this._originalFetch!.call(window, input, init)\n callbacks.onSuccess(url, method, response.status, Date.now() - start)\n return response\n } catch (error) {\n callbacks.onError(url, method, Date.now() - start, String(error))\n throw error\n }\n }\n }\n\n /**\n * 清理 fetch 拦截\n */\n teardown(): void {\n if (this._originalFetch && typeof window !== 'undefined') {\n window.fetch = this._originalFetch\n }\n this._originalFetch = null\n }\n}\n","/**\n * Fetch 插件 - 拦截 fetch 请求\n */\n\nimport type { Plugin, IMonitor } from '../../core/types'\nimport { BrowserFetchAdapter } from './platforms/browser'\n\nexport interface FetchPluginOptions {\n /** 包含的 URL 正则(白名单) */\n includeUrls?: RegExp[]\n /** 排除的 URL 正则(黑名单) */\n excludeUrls?: RegExp[]\n}\n\n/**\n * Fetch 插件\n * 包装模式拦截 fetch,保留原始引用\n */\nexport class FetchPlugin implements Plugin {\n name = 'fetch'\n priority = 200\n\n private _monitor: IMonitor | null = null\n private _options: FetchPluginOptions\n private _platformAdapter: BrowserFetchAdapter\n\n constructor(options: FetchPluginOptions = {}) {\n this._options = options\n this._platformAdapter = new BrowserFetchAdapter()\n }\n\n /**\n * 插件初始化\n */\n setupOnce(monitor: IMonitor): void {\n this._monitor = monitor\n this._platformAdapter.setupOnce(\n (url) => this._shouldIgnore(url),\n {\n onRequest: async () => {\n // 可选:请求前的钩子\n },\n onSuccess: (url, method, status, duration) => {\n this._monitor!.track('http_request', {\n url,\n method,\n status,\n duration,\n })\n },\n onError: (url, method, duration, error) => {\n this._monitor!.track('http_request', {\n url,\n method,\n status: 0,\n duration,\n error,\n })\n },\n }\n )\n }\n\n /**\n * 插件销毁\n */\n teardown(): void {\n this._platformAdapter.teardown()\n this._monitor = null\n }\n\n private _shouldIgnore(url: string): boolean {\n const { includeUrls, excludeUrls } = this._options\n if (excludeUrls?.some((re) => re.test(url))) return true\n if (includeUrls && !includeUrls.some((re) => re.test(url))) return true\n return false\n }\n}\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';var r=class{constructor(n={}){this.name="dedupe";this.priority=20;this._seen=new Map;this._windowMs=n.windowMs??5e3;}processEvent(n){let t=this._getFingerprint(n),e=Date.now();this._cleanup(e);let i=this._seen.get(t);return i&&e-i<this._windowMs?null:(this._seen.set(t,e),n)}_getFingerprint(n){let t=`${n.type}:${JSON.stringify(n.data)}`;return this._hash(t)}_hash(n){let t=0;for(let e=0;e<n.length;e++){let i=n.charCodeAt(e);t=(t<<5)-t+i,t=t&t;}return t.toString(36)}_cleanup(n){for(let[t,e]of this._seen)n-e>=this._windowMs&&this._seen.delete(t);}};exports.a=r;//# sourceMappingURL=chunk-4K5NIPXS.cjs.map
|
|
2
|
+
//# sourceMappingURL=chunk-4K5NIPXS.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/plugins/dedupe/index.ts"],"names":["DedupePlugin","options","event","fingerprint","now","lastSeen","key","str","hash","i","char","timestamp"],"mappings":"aAeO,IAAMA,CAAAA,CAAN,KAAqC,CAO1C,WAAA,CAAYC,CAAAA,CAA+B,EAAC,CAAG,CAN/C,IAAA,CAAA,IAAA,CAAO,QAAA,CACP,IAAA,CAAA,QAAA,CAAW,EAAA,CAGX,KAAQ,KAAA,CAA6B,IAAI,GAAA,CAGvC,IAAA,CAAK,SAAA,CAAYA,CAAAA,CAAQ,QAAA,EAAY,IACvC,CAKA,YAAA,CAAaC,CAAAA,CAA0C,CACrD,IAAMC,CAAAA,CAAc,IAAA,CAAK,eAAA,CAAgBD,CAAK,EACxCE,CAAAA,CAAM,IAAA,CAAK,GAAA,EAAI,CAGrB,IAAA,CAAK,QAAA,CAASA,CAAG,CAAA,CAGjB,IAAMC,CAAAA,CAAW,IAAA,CAAK,KAAA,CAAM,GAAA,CAAIF,CAAW,CAAA,CAC3C,OAAIE,GAAYD,CAAAA,CAAMC,CAAAA,CAAW,IAAA,CAAK,SAAA,CAC7B,IAAA,EAGT,IAAA,CAAK,KAAA,CAAM,GAAA,CAAIF,EAAaC,CAAG,CAAA,CACxBF,CAAAA,CACT,CAKQ,eAAA,CAAgBA,CAAAA,CAA6B,CACnD,IAAMI,EAAM,CAAA,EAAGJ,CAAAA,CAAM,IAAI,CAAA,CAAA,EAAI,IAAA,CAAK,SAAA,CAAUA,CAAAA,CAAM,IAAI,CAAC,CAAA,CAAA,CACvD,OAAO,IAAA,CAAK,KAAA,CAAMI,CAAG,CACvB,CAKQ,MAAMC,CAAAA,CAAqB,CACjC,IAAIC,CAAAA,CAAO,CAAA,CACX,IAAA,IAASC,CAAAA,CAAI,CAAA,CAAGA,EAAIF,CAAAA,CAAI,MAAA,CAAQE,CAAAA,EAAAA,CAAK,CACnC,IAAMC,CAAAA,CAAOH,CAAAA,CAAI,UAAA,CAAWE,CAAC,CAAA,CAC7BD,CAAAA,CAAAA,CAASA,CAAAA,EAAQ,CAAA,EAAKA,CAAAA,CAAQE,CAAAA,CAC9BF,CAAAA,CAAOA,CAAAA,CAAOA,EAChB,CACA,OAAOA,CAAAA,CAAK,QAAA,CAAS,EAAE,CACzB,CAKQ,SAASJ,CAAAA,CAAmB,CAClC,IAAA,GAAW,CAACD,CAAAA,CAAaQ,CAAS,CAAA,GAAK,IAAA,CAAK,MACtCP,CAAAA,CAAMO,CAAAA,EAAa,IAAA,CAAK,SAAA,EAC1B,IAAA,CAAK,KAAA,CAAM,MAAA,CAAOR,CAAW,EAGnC,CACF","file":"chunk-4K5NIPXS.cjs","sourcesContent":["/**\n * Dedupe 插件 - 事件去重\n */\n\nimport type { Plugin, MonitorEvent } from '../../core/types'\n\nexport interface DedupePluginOptions {\n /** 去重窗口(毫秒),默认 5000 */\n windowMs?: number\n}\n\n/**\n * 去重插件\n * 基于事件指纹在时间窗口内去重\n */\nexport class DedupePlugin implements Plugin {\n name = 'dedupe'\n priority = 20\n\n private _windowMs: number\n private _seen: Map<string, number> = new Map()\n\n constructor(options: DedupePluginOptions = {}) {\n this._windowMs = options.windowMs ?? 5000\n }\n\n /**\n * 事件处理:按指纹去重\n */\n processEvent(event: MonitorEvent): MonitorEvent | null {\n const fingerprint = this._getFingerprint(event)\n const now = Date.now()\n\n // 清理过期指纹\n this._cleanup(now)\n\n // 检查是否重复\n const lastSeen = this._seen.get(fingerprint)\n if (lastSeen && now - lastSeen < this._windowMs) {\n return null\n }\n\n this._seen.set(fingerprint, now)\n return event\n }\n\n /**\n * 生成事件指纹\n */\n private _getFingerprint(event: MonitorEvent): string {\n const key = `${event.type}:${JSON.stringify(event.data)}`\n return this._hash(key)\n }\n\n /**\n * 简单 hash 函数\n */\n private _hash(str: string): string {\n let hash = 0\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i)\n hash = ((hash << 5) - hash) + char\n hash = hash & hash\n }\n return hash.toString(36)\n }\n\n /**\n * 清理过期指纹\n */\n private _cleanup(now: number): void {\n for (const [fingerprint, timestamp] of this._seen) {\n if (now - timestamp >= this._windowMs) {\n this._seen.delete(fingerprint)\n }\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var r=["js_error","promise_error","resource_error","web_vital","session_start","session_end"],s=class{constructor(e={}){this.name="sampling";this.priority=10;this._shouldSample=null;this._rate=e.rate??1,this._mode=e.mode??"session",this._typeRates=e.typeRates??{},this._alwaysSample=new Set([...r,...e.alwaysSample??[]]);}setupOnce(e){(this._mode==="user"||this._mode==="session")&&(this._shouldSample=this._decideSampling());}processEvent(e){if(this._alwaysSample.has(e.type))return e;let a=this._typeRates[e.type]??this._rate,t;switch(this._mode){case "user":case "session":t=this._shouldSample??true;break;default:t=Math.random()<a;break}return t?e:null}_decideSampling(){return Math.random()<this._rate}resetSampling(){(this._mode==="user"||this._mode==="session")&&(this._shouldSample=this._decideSampling());}};export{s as a};//# sourceMappingURL=chunk-5RS6LIZN.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-5RS6LIZN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/plugins/sampling/index.ts"],"names":["DEFAULT_ALWAYS_SAMPLE","SamplingPlugin","options","_monitor","event","rate","shouldSample"],"mappings":"AA6BA,IAAMA,CAAAA,CAAwB,CAC5B,UAAA,CACA,eAAA,CACA,iBACA,WAAA,CACA,eAAA,CACA,aACF,CAAA,CAKaC,CAAAA,CAAN,KAAuC,CAU5C,WAAA,CAAYC,EAAiC,EAAC,CAAG,CATjD,IAAA,CAAA,IAAA,CAAO,UAAA,CACP,IAAA,CAAA,QAAA,CAAW,EAAA,CAMX,IAAA,CAAQ,cAAgC,IAAA,CAGtC,IAAA,CAAK,KAAA,CAAQA,CAAAA,CAAQ,IAAA,EAAQ,CAAA,CAC7B,IAAA,CAAK,KAAA,CAAQA,EAAQ,IAAA,EAAQ,SAAA,CAC7B,IAAA,CAAK,UAAA,CAAaA,CAAAA,CAAQ,SAAA,EAAa,EAAC,CACxC,KAAK,aAAA,CAAgB,IAAI,GAAA,CAAI,CAC3B,GAAGF,CAAAA,CACH,GAAIE,CAAAA,CAAQ,cAAgB,EAC9B,CAAC,EACH,CAKA,SAAA,CAAUC,CAAAA,CAA0B,CAAA,CAE9B,KAAK,KAAA,GAAU,MAAA,EAAU,IAAA,CAAK,KAAA,GAAU,SAAA,IAC1C,IAAA,CAAK,aAAA,CAAgB,IAAA,CAAK,iBAAgB,EAE9C,CAKA,YAAA,CAAaC,CAAAA,CAA0C,CAErD,GAAI,IAAA,CAAK,aAAA,CAAc,IAAIA,CAAAA,CAAM,IAAI,CAAA,CACnC,OAAOA,CAAAA,CAIT,IAAMC,CAAAA,CAAO,IAAA,CAAK,WAAWD,CAAAA,CAAM,IAAI,CAAA,EAAK,IAAA,CAAK,KAAA,CAG7CE,CAAAA,CAEJ,OAAQ,IAAA,CAAK,OACX,KAAK,MAAA,CACL,KAAK,SAAA,CAEHA,CAAAA,CAAe,IAAA,CAAK,aAAA,EAAiB,KACrC,MAEF,QAEEA,CAAAA,CAAe,IAAA,CAAK,MAAA,EAAO,CAAID,CAAAA,CAC/B,KACJ,CAEA,OAAOC,CAAAA,CAAeF,CAAAA,CAAQ,IAChC,CAKQ,eAAA,EAA2B,CAGjC,OAAO,KAAK,MAAA,EAAO,CAAI,IAAA,CAAK,KAC9B,CAKA,aAAA,EAAsB,CAAA,CAChB,IAAA,CAAK,QAAU,MAAA,EAAU,IAAA,CAAK,KAAA,GAAU,SAAA,IAC1C,IAAA,CAAK,aAAA,CAAgB,IAAA,CAAK,eAAA,IAE9B,CACF","file":"chunk-5RS6LIZN.js","sourcesContent":["/**\n * Sampling 插件 - 智能采样\n *\n * 支持:\n * - 按用户/会话采样(同一用户要么全采要么不采)\n * - 按事件类型差异化采样率\n * - 重要事件(error/vital)默认全量\n */\n\nimport type { Plugin, MonitorEvent, IMonitor } from '../../core/types'\n\n/** 采样模式 */\nexport type SamplingMode = 'random' | 'user' | 'session'\n\n/** 事件类型采样率配置 */\nexport type TypeRates = Record<string, number>\n\nexport interface SamplingPluginOptions {\n /** 默认采样率 0-1,默认 1.0(全量) */\n rate?: number\n /** 采样模式,默认 'session' */\n mode?: SamplingMode\n /** 按事件类型的采样率(覆盖默认) */\n typeRates?: TypeRates\n /** 始终采样的事件类型(不受采样率影响) */\n alwaysSample?: string[]\n}\n\n/** 默认始终采样的事件类型 */\nconst DEFAULT_ALWAYS_SAMPLE = [\n 'js_error',\n 'promise_error',\n 'resource_error',\n 'web_vital',\n 'session_start',\n 'session_end',\n]\n\n/**\n * 采样插件\n */\nexport class SamplingPlugin implements Plugin {\n name = 'sampling'\n priority = 10\n\n private _rate: number\n private _mode: SamplingMode\n private _typeRates: TypeRates\n private _alwaysSample: Set<string>\n private _shouldSample: boolean | null = null // 缓存用户/会话级采样决策\n\n constructor(options: SamplingPluginOptions = {}) {\n this._rate = options.rate ?? 1.0\n this._mode = options.mode ?? 'session'\n this._typeRates = options.typeRates ?? {}\n this._alwaysSample = new Set([\n ...DEFAULT_ALWAYS_SAMPLE,\n ...(options.alwaysSample ?? []),\n ])\n }\n\n /**\n * 插件初始化\n */\n setupOnce(_monitor: IMonitor): void {\n // 用户/会话模式:初始化时决定是否采样\n if (this._mode === 'user' || this._mode === 'session') {\n this._shouldSample = this._decideSampling()\n }\n }\n\n /**\n * 事件处理:采样过滤\n */\n processEvent(event: MonitorEvent): MonitorEvent | null {\n // 1. 始终采样的事件类型\n if (this._alwaysSample.has(event.type)) {\n return event\n }\n\n // 2. 获取该事件类型的采样率\n const rate = this._typeRates[event.type] ?? this._rate\n\n // 3. 根据模式决定是否采样\n let shouldSample: boolean\n\n switch (this._mode) {\n case 'user':\n case 'session':\n // 用户/会话级:使用缓存的决策\n shouldSample = this._shouldSample ?? true\n break\n case 'random':\n default:\n // 随机模式:每个事件独立决策\n shouldSample = Math.random() < rate\n break\n }\n\n return shouldSample ? event : null\n }\n\n /**\n * 决定是否采样(用户/会话级)\n */\n private _decideSampling(): boolean {\n // 基于随机数决定,但结果会被缓存\n // 这样同一个会话内的所有事件采样决策一致\n return Math.random() < this._rate\n }\n\n /**\n * 重置采样决策(新会话时调用)\n */\n resetSampling(): void {\n if (this._mode === 'user' || this._mode === 'session') {\n this._shouldSample = this._decideSampling()\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';var chunkY46XWPAC_cjs=require('./chunk-Y46XWPAC.cjs');var i=class{constructor(t,e){this._state="idle";this._startTime=null;this._firstChunkTime=null;this._phases=new Map;this._tools=new Map;this._imageLoads=new Map;this._lastChunkTime=null;this._stallTimer=null;this._isStalled=false;this._monitor=t,this._traceId=chunkY46XWPAC_cjs.a(),this._aiMessageId=e.aiMessageId,this._previousTraceId=e.previousTraceId,this._stallThreshold=e.stallThreshold??5e3;}get id(){return this._traceId}get traceId(){return this._traceId}get state(){return this._state}start(){this._state==="idle"&&(this._state="started",this._startTime=Date.now(),this._monitor.track("sse_start",{traceId:this._traceId,aiMessageId:this._aiMessageId}),this._previousTraceId&&this._monitor.track("user_retry",{traceId:this._traceId,previousTraceId:this._previousTraceId}));}firstChunk(){if(this._state!=="started"||this._firstChunkTime!==null)return;this._firstChunkTime=Date.now();let t=this._firstChunkTime-(this._startTime||this._firstChunkTime);this._monitor.track("sse_first_chunk",{traceId:this._traceId,ttfb:t});}complete(){if(this._state!=="started")return;this._state="ended",this._stopStallDetection();let t=Date.now()-(this._startTime||Date.now());this._monitor.track("sse_complete",{traceId:this._traceId,ttlb:t});}error(t){if(this._state!=="started")return;this._state="ended",this._stopStallDetection();let e=Date.now()-(this._startTime||Date.now());this._monitor.track("sse_error",{traceId:this._traceId,error:t,duration:e});}abort(t){if(this._state!=="started")return;this._state="ended",this._stopStallDetection();let e=Date.now()-(this._startTime||Date.now());this._monitor.track("sse_abort",{traceId:this._traceId,reason:t,duration:e});}phaseStart(t){this._state==="started"&&(this._phases.set(t,Date.now()),this._monitor.track("phase_start",{traceId:this._traceId,phase:t}));}phaseEnd(t){if(this._state!=="started")return;let e=this._phases.get(t);if(e===void 0)return;let r=Date.now()-e;this._phases.delete(t),this._monitor.track("phase_end",{traceId:this._traceId,phase:t,duration:r});}toolStart(t,e,r){if(this._state!=="started")return "";let s=r||chunkY46XWPAC_cjs.d();return this._tools.set(s,{name:t,startTime:Date.now()}),this._monitor.track("tool_start",{traceId:this._traceId,toolCallId:s,name:t,args:e}),s}toolEnd(t,e){if(this._state!=="started")return;let r=null,s=null;if(e.toolCallId)s=this._tools.get(e.toolCallId)||null,r=e.toolCallId;else for(let[c,a]of this._tools)if(a.name===t){r=c,s=a;break}if(!r||!s)return;let h=Date.now()-s.startTime;this._tools.delete(r),this._monitor.track("tool_end",{traceId:this._traceId,toolCallId:r,name:t,success:e.success,duration:h,...e.imageUrl&&{imageUrl:e.imageUrl},...e.width&&{width:e.width},...e.height&&{height:e.height},...e.resultCount!==void 0&&{resultCount:e.resultCount},...e.sources&&{sources:e.sources},...e.error&&{error:e.error}});}imageLoadStart(t){this._state==="started"&&(this._imageLoads.set(t,Date.now()),this._monitor.track("image_load_start",{traceId:this._traceId,imageUrl:t}));}imageLoadEnd(t,e){if(this._state!=="started")return;let r=this._imageLoads.get(t);if(r===void 0)return;let s=Date.now()-r;this._imageLoads.delete(t),e.success?this._monitor.track("image_load_complete",{traceId:this._traceId,imageUrl:t,duration:s,...e.size!==void 0&&{size:e.size}}):this._monitor.track("image_load_error",{traceId:this._traceId,imageUrl:t,error:e.error||"Unknown error",duration:s});}recordChunk(){if(this._state!=="started")return;let t=Date.now();if(this._isStalled&&this._lastChunkTime){let e=t-this._lastChunkTime;this._monitor.track("sse_resume",{traceId:this._traceId,stallDuration:e}),this._isStalled=false;}this._lastChunkTime=t,this._startStallDetection();}_startStallDetection(){this._stopStallDetection(),this._stallTimer=setTimeout(()=>this._onStall(),this._stallThreshold);}_stopStallDetection(){this._stallTimer&&(clearTimeout(this._stallTimer),this._stallTimer=null);}_onStall(){this._state==="started"&&(this._isStalled=true,this._monitor.track("sse_stall",{traceId:this._traceId,stallDuration:this._stallThreshold,lastChunkTime:this._lastChunkTime}),this._startStallDetection());}};var l=class{constructor(){this.name="trace";this.priority=100;this._monitor=null;this._currentTrace=null;}setupOnce(t){this._monitor=t;let e=this;Object.defineProperties(t,{createTrace:{value:r=>new i(t,r),writable:false},setCurrentTrace:{value:r=>{e._currentTrace=r;},writable:false},getCurrentTrace:{value:()=>e._currentTrace,writable:false}});}processEvent(t){return this._currentTrace&&(t.context.traceId=this._currentTrace.id),t}teardown(){this._currentTrace=null,this._monitor=null;}};exports.a=i;exports.b=l;//# sourceMappingURL=chunk-5VYMD33V.cjs.map
|
|
2
|
+
//# sourceMappingURL=chunk-5VYMD33V.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/plugins/trace/trace.ts","../src/plugins/trace/index.ts"],"names":["Trace","monitor","options","generateTraceId","ttfb","ttlb","message","duration","reason","phase","startTime","name","args","externalToolCallId","toolCallId","generateToolCallId","result","matchedId","matchedTool","id","tool","imageUrl","now","stallDuration","TracePlugin","self","trace","event"],"mappings":"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","file":"chunk-5VYMD33V.cjs","sourcesContent":["/**\n * Trace 类 - SSE 链路追踪\n */\n\nimport type { IMonitor } from '../../core/types'\nimport type { TraceOptions, TraceOptionsExtended, TraceState, Phase, ToolResult, ImageLoadResult } from './types'\nimport { generateTraceId, generateToolCallId } from '../../core/utils'\n\n/**\n * SSE 链路追踪类\n */\nexport class Trace {\n private _traceId: string\n private _aiMessageId: string\n private _state: TraceState = 'idle'\n private _monitor: IMonitor\n\n private _startTime: number | null = null\n private _firstChunkTime: number | null = null\n private _phases: Map<Phase, number> = new Map()\n private _tools: Map<string, { name: string; startTime: number }> = new Map()\n private _imageLoads: Map<string, number> = new Map()\n\n private _previousTraceId?: string\n private _lastChunkTime: number | null = null\n private _stallTimer: ReturnType<typeof setTimeout> | null = null\n private _stallThreshold: number\n private _isStalled: boolean = false\n\n constructor(monitor: IMonitor, options: TraceOptions | TraceOptionsExtended) {\n this._monitor = monitor\n this._traceId = generateTraceId()\n this._aiMessageId = options.aiMessageId\n this._previousTraceId = (options as TraceOptionsExtended).previousTraceId\n this._stallThreshold = (options as TraceOptionsExtended).stallThreshold ?? 5000\n }\n\n get id(): string {\n return this._traceId\n }\n\n get traceId(): string {\n return this._traceId\n }\n\n get state(): TraceState {\n return this._state\n }\n\n start(): void {\n if (this._state !== 'idle') return\n this._state = 'started'\n this._startTime = Date.now()\n\n this._monitor.track('sse_start', {\n traceId: this._traceId,\n aiMessageId: this._aiMessageId,\n })\n\n if (this._previousTraceId) {\n this._monitor.track('user_retry', {\n traceId: this._traceId,\n previousTraceId: this._previousTraceId,\n })\n }\n }\n\n firstChunk(): void {\n if (this._state !== 'started' || this._firstChunkTime !== null) return\n this._firstChunkTime = Date.now()\n const ttfb = this._firstChunkTime - (this._startTime || this._firstChunkTime)\n\n this._monitor.track('sse_first_chunk', { traceId: this._traceId, ttfb })\n }\n\n complete(): void {\n if (this._state !== 'started') return\n this._state = 'ended'\n this._stopStallDetection()\n const ttlb = Date.now() - (this._startTime || Date.now())\n\n this._monitor.track('sse_complete', { traceId: this._traceId, ttlb })\n }\n\n error(message: string): void {\n if (this._state !== 'started') return\n this._state = 'ended'\n this._stopStallDetection()\n const duration = Date.now() - (this._startTime || Date.now())\n\n this._monitor.track('sse_error', { traceId: this._traceId, error: message, duration })\n }\n\n abort(reason?: string): void {\n if (this._state !== 'started') return\n this._state = 'ended'\n this._stopStallDetection()\n const duration = Date.now() - (this._startTime || Date.now())\n\n this._monitor.track('sse_abort', { traceId: this._traceId, reason, duration })\n }\n\n phaseStart(phase: Phase): void {\n if (this._state !== 'started') return\n this._phases.set(phase, Date.now())\n this._monitor.track('phase_start', { traceId: this._traceId, phase })\n }\n\n phaseEnd(phase: Phase): void {\n if (this._state !== 'started') return\n const startTime = this._phases.get(phase)\n if (startTime === undefined) return\n\n const duration = Date.now() - startTime\n this._phases.delete(phase)\n this._monitor.track('phase_end', { traceId: this._traceId, phase, duration })\n }\n\n\n toolStart(name: string, args?: Record<string, unknown>, externalToolCallId?: string): string {\n if (this._state !== 'started') return ''\n const toolCallId = externalToolCallId || generateToolCallId()\n this._tools.set(toolCallId, { name, startTime: Date.now() })\n this._monitor.track('tool_start', { traceId: this._traceId, toolCallId, name, args })\n return toolCallId\n }\n\n toolEnd(name: string, result: ToolResult): void {\n if (this._state !== 'started') return\n\n let matchedId: string | null = null\n let matchedTool: { name: string; startTime: number } | null = null\n\n if (result.toolCallId) {\n matchedTool = this._tools.get(result.toolCallId) || null\n matchedId = result.toolCallId\n } else {\n for (const [id, tool] of this._tools) {\n if (tool.name === name) {\n matchedId = id\n matchedTool = tool\n break\n }\n }\n }\n\n if (!matchedId || !matchedTool) return\n\n const duration = Date.now() - matchedTool.startTime\n this._tools.delete(matchedId)\n\n this._monitor.track('tool_end', {\n traceId: this._traceId,\n toolCallId: matchedId,\n name,\n success: result.success,\n duration,\n ...(result.imageUrl && { imageUrl: result.imageUrl }),\n ...(result.width && { width: result.width }),\n ...(result.height && { height: result.height }),\n ...(result.resultCount !== undefined && { resultCount: result.resultCount }),\n ...(result.sources && { sources: result.sources }),\n ...(result.error && { error: result.error }),\n })\n }\n\n imageLoadStart(imageUrl: string): void {\n if (this._state !== 'started') return\n this._imageLoads.set(imageUrl, Date.now())\n this._monitor.track('image_load_start', { traceId: this._traceId, imageUrl })\n }\n\n imageLoadEnd(imageUrl: string, result: ImageLoadResult): void {\n if (this._state !== 'started') return\n const startTime = this._imageLoads.get(imageUrl)\n if (startTime === undefined) return\n\n const duration = Date.now() - startTime\n this._imageLoads.delete(imageUrl)\n\n if (result.success) {\n this._monitor.track('image_load_complete', {\n traceId: this._traceId,\n imageUrl,\n duration,\n ...(result.size !== undefined && { size: result.size }),\n })\n } else {\n this._monitor.track('image_load_error', {\n traceId: this._traceId,\n imageUrl,\n error: result.error || 'Unknown error',\n duration,\n })\n }\n }\n\n recordChunk(): void {\n if (this._state !== 'started') return\n const now = Date.now()\n\n if (this._isStalled && this._lastChunkTime) {\n const stallDuration = now - this._lastChunkTime\n this._monitor.track('sse_resume', { traceId: this._traceId, stallDuration })\n this._isStalled = false\n }\n\n this._lastChunkTime = now\n this._startStallDetection()\n }\n\n private _startStallDetection(): void {\n this._stopStallDetection()\n this._stallTimer = setTimeout(() => this._onStall(), this._stallThreshold)\n }\n\n private _stopStallDetection(): void {\n if (this._stallTimer) {\n clearTimeout(this._stallTimer)\n this._stallTimer = null\n }\n }\n\n private _onStall(): void {\n if (this._state !== 'started') return\n this._isStalled = true\n\n this._monitor.track('sse_stall', {\n traceId: this._traceId,\n stallDuration: this._stallThreshold,\n lastChunkTime: this._lastChunkTime,\n })\n\n this._startStallDetection()\n }\n}\n","/**\n * Trace 插件 - AI 对话链路追踪\n */\n\nimport type { Plugin, IMonitor, MonitorEvent } from '../../core/types'\nimport type { TraceOptions, TraceOptionsExtended } from './types'\nimport { Trace } from './trace'\n\nexport type { TraceOptions, TraceOptionsExtended, TraceState, Phase, ToolResult, ImageLoadResult } from './types'\nexport { Trace } from './trace'\n\n/**\n * Trace 插件\n * 提供 AI 对话链路追踪能力\n */\nexport class TracePlugin implements Plugin {\n name = 'trace'\n priority = 100\n\n private _monitor: IMonitor | null = null\n private _currentTrace: Trace | null = null\n\n /**\n * 插件初始化\n */\n setupOnce(monitor: IMonitor): void {\n this._monitor = monitor\n\n // 扩展 Monitor API\n const self = this\n Object.defineProperties(monitor, {\n createTrace: {\n value: (options: TraceOptions | TraceOptionsExtended) => {\n return new Trace(monitor, options)\n },\n writable: false,\n },\n setCurrentTrace: {\n value: (trace: Trace | null) => {\n self._currentTrace = trace\n },\n writable: false,\n },\n getCurrentTrace: {\n value: () => self._currentTrace,\n writable: false,\n },\n })\n }\n\n /**\n * 事件处理:关联 traceId\n */\n processEvent(event: MonitorEvent): MonitorEvent {\n if (this._currentTrace) {\n event.context.traceId = this._currentTrace.id\n }\n return event\n }\n\n /**\n * 插件销毁\n */\n teardown(): void {\n this._currentTrace = null\n this._monitor = null\n }\n}\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=0,i=0;function t(r=""){let n=Date.now();n===i?e++:(e=0,i=n);let s=n.toString(36),a=Math.random().toString(36).slice(2,7),o=[s,e.toString(36),a];return r?`${r}_${o.join("_")}`:o.join("_")}function g(){return t("trc")}function c(){return t("ses")}function u(){return t("evt")}function l(){return t("tool")}export{g as a,c as b,u as c,l as d};//# sourceMappingURL=chunk-647GK2XE.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-647GK2XE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/utils.ts"],"names":["counter","lastTimestamp","generateId","prefix","now","timestamp","random","parts","generateTraceId","generateSessionId","generateEventId","generateToolCallId"],"mappings":"AAKA,IAAIA,CAAAA,CAAU,EACVC,CAAAA,CAAgB,CAAA,CAUb,SAASC,CAAAA,CAAWC,CAAAA,CAAiB,GAAY,CACtD,IAAMC,EAAM,IAAA,CAAK,GAAA,GAGbA,CAAAA,GAAQH,CAAAA,CACVD,KAEAA,CAAAA,CAAU,CAAA,CACVC,EAAgBG,CAAAA,CAAAA,CAGlB,IAAMC,EAAYD,CAAAA,CAAI,QAAA,CAAS,EAAE,CAAA,CAC3BE,CAAAA,CAAS,KAAK,MAAA,EAAO,CAAE,SAAS,EAAE,CAAA,CAAE,MAAM,CAAA,CAAG,CAAC,EAC9CC,CAAAA,CAAQ,CAACF,EAAWL,CAAAA,CAAQ,QAAA,CAAS,EAAE,CAAA,CAAGM,CAAM,EAEtD,OAAOH,CAAAA,CAAS,GAAGA,CAAM,CAAA,CAAA,EAAII,EAAM,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA,CAAKA,CAAAA,CAAM,KAAK,GAAG,CACjE,CAKO,SAASC,CAAAA,EAA0B,CACxC,OAAON,CAAAA,CAAW,KAAK,CACzB,CAKO,SAASO,CAAAA,EAA4B,CAC1C,OAAOP,CAAAA,CAAW,KAAK,CACzB,CAKO,SAASQ,GAA0B,CACxC,OAAOR,EAAW,KAAK,CACzB,CAKO,SAASS,CAAAA,EAA6B,CAC3C,OAAOT,CAAAA,CAAW,MAAM,CAC1B","file":"chunk-647GK2XE.js","sourcesContent":["/**\n * 工具函数\n */\n\n// 计数器,用于同一毫秒内生成不同 ID\nlet counter = 0\nlet lastTimestamp = 0\n\n/**\n * 生成唯一 ID(类 ULID 格式)\n * 格式: 前缀_时间戳(base36)_计数器_随机数\n * 示例: trc_m5x2k8_0_a3b7c\n *\n * @param prefix - ID 前缀,用于区分类型\n * @returns 唯一标识符\n */\nexport function generateId(prefix: string = ''): string {\n const now = Date.now()\n\n // 同一毫秒内递增计数器\n if (now === lastTimestamp) {\n counter++\n } else {\n counter = 0\n lastTimestamp = now\n }\n\n const timestamp = now.toString(36)\n const random = Math.random().toString(36).slice(2, 7)\n const parts = [timestamp, counter.toString(36), random]\n\n return prefix ? `${prefix}_${parts.join('_')}` : parts.join('_')\n}\n\n/**\n * 生成 Trace ID\n */\nexport function generateTraceId(): string {\n return generateId('trc')\n}\n\n/**\n * 生成 Session ID\n */\nexport function generateSessionId(): string {\n return generateId('ses')\n}\n\n/**\n * 生成 Event ID\n */\nexport function generateEventId(): string {\n return generateId('evt')\n}\n\n/**\n * 生成 Tool Call ID\n */\nexport function generateToolCallId(): string {\n return generateId('tool')\n}\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {b}from'./chunk-647GK2XE.js';var t=class{constructor(e){this._startTime=null;this._traceCount=0;this._toolUsage=new Map;this._visible=true;this._ended=false;this._monitor=e,this._sessionId=b();}get sessionId(){return this._sessionId}get id(){return this._sessionId}get ended(){return this._ended}start(){this._startTime===null&&(this._startTime=Date.now(),this._monitor.track("session_start",{sessionId:this._sessionId,timestamp:this._startTime}));}end(){if(this._ended||this._startTime===null)return;this._ended=true;let e=Date.now()-this._startTime;this._monitor.track("session_end",{sessionId:this._sessionId,duration:e,traceCount:this._traceCount,toolUsage:Object.fromEntries(this._toolUsage)});}incrementTraceCount(){this._ended||this._traceCount++;}incrementToolUsage(e){if(this._ended)return;let s=this._toolUsage.get(e)||0;this._toolUsage.set(e,s+1);}setVisible(e){this._ended||this._visible===e||(this._visible=e,this._monitor.track("session_visibility",{sessionId:this._sessionId,visible:e,timestamp:Date.now()}));}getStats(){let e=this._startTime?Date.now()-this._startTime:0;return {sessionId:this._sessionId,duration:e,traceCount:this._traceCount,toolUsage:Object.fromEntries(this._toolUsage)}}};var i=class{constructor(){this._visibilityHandler=null;this._beforeUnloadHandler=null;}setupOnce(e){typeof window>"u"||(this._visibilityHandler=()=>{e.onVisibilityChange(!document.hidden);},document.addEventListener("visibilitychange",this._visibilityHandler),this._beforeUnloadHandler=()=>e.onBeforeUnload(),window.addEventListener("beforeunload",this._beforeUnloadHandler));}teardown(){typeof window>"u"||(this._visibilityHandler&&(document.removeEventListener("visibilitychange",this._visibilityHandler),this._visibilityHandler=null),this._beforeUnloadHandler&&(window.removeEventListener("beforeunload",this._beforeUnloadHandler),this._beforeUnloadHandler=null));}};var r=class{constructor(){this.name="session";this.priority=100;this._session=null;this._platformAdapter=new i;}setupOnce(e){let s=this;Object.defineProperties(e,{startSession:{value:()=>{s._session&&!s._session.ended||(s._session=new t(e),s._session.start(),s._setupAutoListeners());},writable:false},endSession:{value:()=>{!s._session||s._session.ended||(s._session.end(),s._platformAdapter.teardown());},writable:false},getSession:{value:()=>s._session,writable:false}});}processEvent(e){return this._session&&!this._session.ended&&(e.context.sessionId=this._session.id),e}teardown(){this._platformAdapter.teardown(),this._session&&!this._session.ended&&this._session.end(),this._session=null;}_setupAutoListeners(){this._platformAdapter.setupOnce({onVisibilityChange:e=>{this._session&&!this._session.ended&&this._session.setVisible(e);},onBeforeUnload:()=>{this._session&&!this._session.ended&&this._session.end();}});}};export{t as a,r as b};//# sourceMappingURL=chunk-6KJXTS2V.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-6KJXTS2V.js.map
|