@deepsure/page-replay-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,279 @@
1
+ # 金融监管录制系统 JavaScript SDK
2
+
3
+ 用于金融场景的会话录制和回溯,支持自动会话管理、增量上传和数据脱敏。
4
+
5
+ ## 安装
6
+
7
+ ### CDN 引入
8
+
9
+ ```html
10
+ <script src="https://cdn.yourplatform.com/sdk/v1/recorder.js"></script>
11
+ ```
12
+
13
+ ### NPM 安装
14
+
15
+ ```bash
16
+ npm install @finance-recorder/sdk
17
+ ```
18
+
19
+ ## 快速开始
20
+
21
+ ### 基础用法
22
+
23
+ ```html
24
+ <script src="https://cdn.yourplatform.com/sdk/v1/recorder.js"></script>
25
+ <script>
26
+ FinanceRecorder.init({
27
+ apiKey: 'your_api_key',
28
+ projectId: 'proj_12345',
29
+ autoStart: true
30
+ });
31
+ </script>
32
+ ```
33
+
34
+ ### 完整配置
35
+
36
+ ```javascript
37
+ import FinanceRecorder from '@finance-recorder/sdk';
38
+
39
+ await FinanceRecorder.init({
40
+ // 必需配置
41
+ apiKey: 'your_api_key', // API 密钥
42
+ projectId: 'proj_12345', // 项目 ID
43
+
44
+ // 可选配置
45
+ apiBaseUrl: 'https://api.yourplatform.com', // API 地址
46
+ autoStart: true, // 自动开始录制
47
+ chunkSize: 100, // 批量上传大小
48
+ uploadInterval: 30000, // 上传间隔(毫秒)
49
+
50
+ // 隐私配置
51
+ privacy: {
52
+ maskAllInputs: false, // 是否屏蔽所有输入
53
+ maskInputOptions: {
54
+ idCard: true, // 身份证脱敏
55
+ phone: true, // 手机号脱敏
56
+ bankCard: true, // 银行卡脱敏
57
+ }
58
+ },
59
+
60
+ // 业务元数据
61
+ metadata: {
62
+ productType: '车险', // 产品类型
63
+ quoteId: 'quote_789', // 报价单号
64
+ userId: 'user_123' // 用户 ID
65
+ },
66
+
67
+ // 回调函数
68
+ onSessionCreated: (session) => {
69
+ console.log('会话已创建:', session.sessionId);
70
+ },
71
+ onUploadProgress: (progress) => {
72
+ console.log('上传进度:', progress);
73
+ },
74
+ onError: (message, error) => {
75
+ console.error(message, error);
76
+ }
77
+ });
78
+ ```
79
+
80
+ ## API 参考
81
+
82
+ ### init(config)
83
+
84
+ 初始化录制器。
85
+
86
+ **参数:**
87
+ - `apiKey` (string, 必需) - API 密钥
88
+ - `projectId` (string, 必需) - 项目 ID
89
+ - `apiBaseUrl` (string) - API 基础 URL
90
+ - `autoStart` (boolean) - 是否自动开始录制,默认 true
91
+ - `privacy` (object) - 隐私配置
92
+ - `metadata` (object) - 业务元数据
93
+
94
+ **返回:** Promise<FinanceRecorder>
95
+
96
+ ### start(customMetadata?)
97
+
98
+ 手动开始录制。
99
+
100
+ **参数:**
101
+ - `customMetadata` (object, 可选) - 自定义元数据
102
+
103
+ **返回:** Promise<void>
104
+
105
+ ### stop()
106
+
107
+ 停止录制并上传剩余数据。
108
+
109
+ **返回:** Promise<string> - 会话 ID
110
+
111
+ ### setMetadata(metadata)
112
+
113
+ 更新会话元数据。
114
+
115
+ **参数:**
116
+ - `metadata` (object) - 要添加/更新的元数据
117
+
118
+ ### getSessionId()
119
+
120
+ 获取当前会话 ID。
121
+
122
+ **返回:** string | null
123
+
124
+ ### isRecording()
125
+
126
+ 检查是否正在录制。
127
+
128
+ **返回:** boolean
129
+
130
+ ## 使用场景
131
+
132
+ ### 场景 1:保险投保流程录制
133
+
134
+ ```javascript
135
+ FinanceRecorder.init({
136
+ apiKey: 'your_api_key',
137
+ projectId: 'proj_insurance',
138
+ metadata: {
139
+ productType: '车险',
140
+ policyNumber: 'POL-2025-001',
141
+ applicantId: 'user_123'
142
+ },
143
+ privacy: {
144
+ maskInputOptions: {
145
+ idCard: true,
146
+ phone: true,
147
+ bankCard: true
148
+ }
149
+ }
150
+ });
151
+ ```
152
+
153
+ ### 场景 2:客服协助录制
154
+
155
+ ```javascript
156
+ // 仅在客服协助时录制
157
+ const startAssist = async () => {
158
+ await FinanceRecorder.init({
159
+ apiKey: 'your_api_key',
160
+ projectId: 'proj_support',
161
+ autoStart: false
162
+ });
163
+
164
+ // 用户同意后开始录制
165
+ await FinanceRecorder.start({
166
+ supportTicketId: 'TICKET-123',
167
+ agentId: 'agent_456'
168
+ });
169
+ };
170
+
171
+ const endAssist = async () => {
172
+ const sessionId = await FinanceRecorder.stop();
173
+ console.log('会话已保存:', sessionId);
174
+ };
175
+ ```
176
+
177
+ ### 场景 3:动态更新元数据
178
+
179
+ ```javascript
180
+ // 初始化
181
+ await FinanceRecorder.init({
182
+ apiKey: 'your_api_key',
183
+ projectId: 'proj_12345',
184
+ metadata: {
185
+ step: 'quote'
186
+ }
187
+ });
188
+
189
+ // 用户进入下一步
190
+ FinanceRecorder.setMetadata({ step: 'fill_info' });
191
+
192
+ // 用户提交申请
193
+ FinanceRecorder.setMetadata({
194
+ step: 'submit',
195
+ applicationId: 'APP-2025-001'
196
+ });
197
+ ```
198
+
199
+ ## 隐私保护
200
+
201
+ ### 自动脱敏
202
+
203
+ SDK 自动检测并脱敏以下敏感信息:
204
+
205
+ - **身份证号**:`110101199001011234` → `110101********1234`
206
+ - **手机号**:`13812345678` → `138****5678`
207
+ - **银行卡号**:`6222021234567890123` → `622202******123`
208
+
209
+ ### 手动屏蔽元素
210
+
211
+ 在 HTML 中添加 `rr-block` 类名:
212
+
213
+ ```html
214
+ <div class="rr-block">
215
+ 这个区域不会被录制
216
+ </div>
217
+ ```
218
+
219
+ ## 最佳实践
220
+
221
+ ### 1. 错误处理
222
+
223
+ ```javascript
224
+ FinanceRecorder.init({
225
+ apiKey: 'your_api_key',
226
+ projectId: 'proj_12345',
227
+ onError: (message, error) => {
228
+ // 上报到错误监控系统
229
+ Sentry.captureException(error, {
230
+ tags: { component: 'recorder' },
231
+ extra: { message }
232
+ });
233
+ }
234
+ });
235
+ ```
236
+
237
+ ### 2. 性能监控
238
+
239
+ ```javascript
240
+ const perfObserver = new PerformanceObserver((list) => {
241
+ for (const entry of list.getEntries()) {
242
+ FinanceRecorder.setMetadata({
243
+ [`perf_${entry.name}`]: entry.duration
244
+ });
245
+ }
246
+ });
247
+
248
+ perfObserver.observe({ entryTypes: ['navigation', 'resource'] });
249
+ ```
250
+
251
+ ### 3. 用户同意
252
+
253
+ ```javascript
254
+ // 显示用户同意弹窗
255
+ const consent = await showConsentDialog();
256
+
257
+ if (consent) {
258
+ await FinanceRecorder.init({
259
+ apiKey: 'your_api_key',
260
+ projectId: 'proj_12345',
261
+ metadata: {
262
+ consentGiven: true,
263
+ consentTime: new Date().toISOString()
264
+ }
265
+ });
266
+ }
267
+ ```
268
+
269
+ ## 浏览器兼容性
270
+
271
+ - Chrome 60+
272
+ - Firefox 55+
273
+ - Safari 11+
274
+ - Edge 79+
275
+
276
+ ## 许可证
277
+
278
+ MIT
279
+
@@ -0,0 +1,91 @@
1
+ /**
2
+ * 页面录制系统 JavaScript SDK
3
+ */
4
+
5
+ export interface RecorderConfig {
6
+ /** API 密钥 */
7
+ apiKey: string;
8
+ /** 项目 ID */
9
+ projectId: string;
10
+ /** API 基础 URL */
11
+ apiBaseUrl?: string;
12
+ /** 是否自动开始录制 */
13
+ autoStart?: boolean;
14
+ /** 批量上传大小 */
15
+ chunkSize?: number;
16
+ /** 上传间隔(毫秒) */
17
+ uploadInterval?: number;
18
+ /** 隐私配置 */
19
+ privacy?: PrivacyConfig;
20
+ /** 业务元数据 */
21
+ metadata?: Record<string, any>;
22
+ /** 错误回调 */
23
+ onError?: (message: string, error: Error) => void;
24
+ /** 会话创建回调 */
25
+ onSessionCreated?: (session: Session) => void;
26
+ /** 上传进度回调 */
27
+ onUploadProgress?: (progress: UploadProgress) => void;
28
+ }
29
+
30
+ export interface PrivacyConfig {
31
+ /** 是否屏蔽所有输入 */
32
+ maskAllInputs?: boolean;
33
+ /** 输入屏蔽选项 */
34
+ maskInputOptions?: {
35
+ /** 身份证脱敏 */
36
+ idCard?: boolean;
37
+ /** 手机号脱敏 */
38
+ phone?: boolean;
39
+ /** 银行卡脱敏 */
40
+ bankCard?: boolean;
41
+ };
42
+ }
43
+
44
+ export interface Session {
45
+ sessionId: string;
46
+ projectId: string;
47
+ metadata?: Record<string, any>;
48
+ createdAt?: string;
49
+ }
50
+
51
+ export interface UploadProgress {
52
+ sessionId: string;
53
+ uploaded: number;
54
+ total: number;
55
+ }
56
+
57
+ declare class PageRecorder {
58
+ /**
59
+ * 初始化录制器
60
+ */
61
+ init(config: RecorderConfig): Promise<PageRecorder>;
62
+
63
+ /**
64
+ * 开始录制
65
+ */
66
+ start(customMetadata?: Record<string, any>): Promise<void>;
67
+
68
+ /**
69
+ * 停止录制
70
+ */
71
+ stop(): Promise<string | null>;
72
+
73
+ /**
74
+ * 设置元数据
75
+ */
76
+ setMetadata(metadata: Record<string, any>): void;
77
+
78
+ /**
79
+ * 获取当前会话 ID
80
+ */
81
+ getSessionId(): string | null;
82
+
83
+ /**
84
+ * 是否正在录制
85
+ */
86
+ isRecording(): boolean;
87
+ }
88
+
89
+ declare const recorder: PageRecorder;
90
+ export default recorder;
91
+
@@ -0,0 +1,2 @@
1
+ import{record as t}from"rrweb";import s from"pako";class e{constructor(t,s){this.baseUrl=t,this.apiKey=s}async request(t,s={}){const e=`${this.baseUrl}${t}`,i={"Content-Type":"application/json","X-API-Key":this.apiKey,...s.headers},n=await fetch(e,{...s,headers:i});if(!n.ok){const t=await n.json().catch(()=>({}));throw new Error(t.message||`HTTP ${n.status}`)}return await n.json()}async createSession(t){return(await this.request("/api/v1/sessions",{method:"POST",body:JSON.stringify(t)})).data}async appendEvents(t,s){return await this.request(`/api/v1/sessions/${t}/append`,{method:"PATCH",body:JSON.stringify({events:s,compressed:!0})})}async finalizeSession(t,s){return await this.request(`/api/v1/sessions/${t}/finalize`,{method:"POST",body:JSON.stringify(s)})}}const i=new class{constructor(){this.config=null,this.sessionId=null,this.recording=!1,this.stopFn=null,this.events=[],this.uploadQueue=[],this.uploadTimer=null,this.apiClient=null}async init(t){return this.config={apiKey:t.apiKey,projectId:t.projectId,apiBaseUrl:t.apiBaseUrl||"https://api.yourplatform.com",autoStart:!1!==t.autoStart,chunkSize:t.chunkSize||100,uploadInterval:t.uploadInterval||3e4,privacy:{maskAllInputs:!1,maskInputOptions:{idCard:!0,phone:!0,bankCard:!0,...t.privacy?.maskInputOptions},...t.privacy},metadata:t.metadata||{},onError:t.onError||console.error,onSessionCreated:t.onSessionCreated||(()=>{}),onUploadProgress:t.onUploadProgress||(()=>{})},this.apiClient=new e(this.config.apiBaseUrl,this.config.apiKey),this.config.autoStart&&await this.start(),this}async start(s={}){if(this.recording)console.warn("Recording already in progress");else try{const e=await this.apiClient.createSession({projectId:this.config.projectId,metadata:{...this.config.metadata,...s,url:window.location.href,userAgent:navigator.userAgent,screenResolution:`${window.screen.width}x${window.screen.height}`,startTime:(new Date).toISOString()}});this.sessionId=e.sessionId,this.recording=!0,this.events=[],this.config.onSessionCreated(e),this.stopFn=t({emit:t=>{this.handleEvent(t)},checkoutEveryNms:3e5,...this.getPrivacyConfig(),sampling:{mousemove:!0,mouseInteraction:!0,scroll:150,input:"last"},recordCanvas:!1,inlineImages:!1,collectFonts:!1}),this.startUploadTimer(),console.log("Recording started:",this.sessionId)}catch(t){throw this.config.onError("Failed to start recording",t),t}}async stop(){if(!this.recording)return;this.recording=!1,this.stopFn&&(this.stopFn(),this.stopFn=null),this.stopUploadTimer(),await this.flushEvents(),await this.apiClient.finalizeSession(this.sessionId,{endTime:(new Date).toISOString(),eventCount:this.events.length}),console.log("Recording stopped:",this.sessionId);const t=this.sessionId;return this.sessionId=null,this.events=[],t}handleEvent(t){this.events.push(t),this.events.length>=this.config.chunkSize&&this.flushEvents()}async flushEvents(){if(0===this.events.length||!this.sessionId)return;const t=[...this.events];this.events=[];try{const s=this.compressEvents(t);await this.apiClient.appendEvents(this.sessionId,s),this.config.onUploadProgress({sessionId:this.sessionId,uploaded:t.length,total:this.events.length+t.length})}catch(s){this.events.unshift(...t),this.config.onError("Failed to upload events",s),setTimeout(()=>this.flushEvents(),5e3)}}compressEvents(t){const e=JSON.stringify(t),i=s.gzip(e);return btoa(String.fromCharCode(...i))}getPrivacyConfig(){return{blockClass:"rr-block",maskAllInputs:this.config.privacy.maskAllInputs,maskInputOptions:this.config.privacy.maskInputOptions,maskTextFn:(t,s)=>this.maskSensitiveData(t,s)}}maskSensitiveData(t,s){if(!t)return t;const e=t.trim();return this.config.privacy.maskInputOptions.idCard&&/^\d{15}|\d{17}[\dXx]$/.test(e)?e.replace(/(\d{6})\d{8}(\d{4})/,"$1********$2"):this.config.privacy.maskInputOptions.phone&&/^1[3-9]\d{9}$/.test(e)?e.replace(/(\d{3})\d{4}(\d{4})/,"$1****$2"):this.config.privacy.maskInputOptions.bankCard&&/^\d{16,19}$/.test(e)?e.replace(/(\d{6})\d+(\d{3})/,"$1******$2"):t}startUploadTimer(){this.uploadTimer=setInterval(()=>{this.flushEvents()},this.config.uploadInterval)}stopUploadTimer(){this.uploadTimer&&(clearInterval(this.uploadTimer),this.uploadTimer=null)}setMetadata(t){this.config.metadata={...this.config.metadata,...t}}getSessionId(){return this.sessionId}isRecording(){return this.recording}};"undefined"!=typeof window&&(window.PageRecorder=i);export{i as default};
2
+ //# sourceMappingURL=index.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.esm.js","sources":["../src/index.js"],"sourcesContent":["/**\n * 页面录制系统 JavaScript SDK\n * 用于页面会话录制和回溯\n */\n\nimport { record } from 'rrweb';\nimport pako from 'pako';\n\nclass PageRecorder {\n constructor() {\n this.config = null;\n this.sessionId = null;\n this.recording = false;\n this.stopFn = null;\n this.events = [];\n this.uploadQueue = [];\n this.uploadTimer = null;\n this.apiClient = null;\n }\n\n /**\n * 初始化录制器\n */\n async init(config) {\n this.config = {\n apiKey: config.apiKey,\n projectId: config.projectId,\n apiBaseUrl: config.apiBaseUrl || 'https://api.yourplatform.com',\n autoStart: config.autoStart !== false,\n chunkSize: config.chunkSize || 100,\n uploadInterval: config.uploadInterval || 30000, // 30秒\n privacy: {\n maskAllInputs: false,\n maskInputOptions: {\n idCard: true,\n phone: true,\n bankCard: true,\n ...config.privacy?.maskInputOptions,\n },\n ...config.privacy,\n },\n metadata: config.metadata || {},\n onError: config.onError || console.error,\n onSessionCreated: config.onSessionCreated || (() => {}),\n onUploadProgress: config.onUploadProgress || (() => {}),\n };\n\n // 初始化 API 客户端\n this.apiClient = new APIClient(this.config.apiBaseUrl, this.config.apiKey);\n\n // 自动开始录制\n if (this.config.autoStart) {\n await this.start();\n }\n\n return this;\n }\n\n /**\n * 开始录制\n */\n async start(customMetadata = {}) {\n if (this.recording) {\n console.warn('Recording already in progress');\n return;\n }\n\n try {\n // 创建会话\n const session = await this.apiClient.createSession({\n projectId: this.config.projectId,\n metadata: {\n ...this.config.metadata,\n ...customMetadata,\n url: window.location.href,\n userAgent: navigator.userAgent,\n screenResolution: `${window.screen.width}x${window.screen.height}`,\n startTime: new Date().toISOString(),\n },\n });\n\n this.sessionId = session.sessionId;\n this.recording = true;\n this.events = [];\n\n // 通知会话创建\n this.config.onSessionCreated(session);\n\n // 开始 rrweb 录制\n this.stopFn = record({\n emit: (event) => {\n this.handleEvent(event);\n },\n checkoutEveryNms: 5 * 60 * 1000,\n ...this.getPrivacyConfig(),\n sampling: {\n mousemove: true,\n mouseInteraction: true,\n scroll: 150,\n input: 'last',\n },\n recordCanvas: false,\n inlineImages: false,\n collectFonts: false,\n });\n\n // 启动定时上传\n this.startUploadTimer();\n\n console.log('Recording started:', this.sessionId);\n } catch (error) {\n this.config.onError('Failed to start recording', error);\n throw error;\n }\n }\n\n /**\n * 停止录制\n */\n async stop() {\n if (!this.recording) {\n return;\n }\n\n this.recording = false;\n\n // 停止 rrweb 录制\n if (this.stopFn) {\n this.stopFn();\n this.stopFn = null;\n }\n\n // 停止定时上传\n this.stopUploadTimer();\n\n // 上传剩余事件\n await this.flushEvents();\n\n // 完成会话\n await this.apiClient.finalizeSession(this.sessionId, {\n endTime: new Date().toISOString(),\n eventCount: this.events.length,\n });\n\n console.log('Recording stopped:', this.sessionId);\n\n const sessionId = this.sessionId;\n this.sessionId = null;\n this.events = [];\n\n return sessionId;\n }\n\n /**\n * 处理录制事件\n */\n handleEvent(event) {\n this.events.push(event);\n\n // 批量上传\n if (this.events.length >= this.config.chunkSize) {\n this.flushEvents();\n }\n }\n\n /**\n * 上传事件\n */\n async flushEvents() {\n if (this.events.length === 0 || !this.sessionId) {\n return;\n }\n\n const eventsToUpload = [...this.events];\n this.events = [];\n\n try {\n // 压缩数据\n const compressed = this.compressEvents(eventsToUpload);\n\n // 上传\n await this.apiClient.appendEvents(this.sessionId, compressed);\n\n // 通知上传进度\n this.config.onUploadProgress({\n sessionId: this.sessionId,\n uploaded: eventsToUpload.length,\n total: this.events.length + eventsToUpload.length,\n });\n } catch (error) {\n // 上传失败,放回队列\n this.events.unshift(...eventsToUpload);\n this.config.onError('Failed to upload events', error);\n\n // 重试\n setTimeout(() => this.flushEvents(), 5000);\n }\n }\n\n /**\n * 压缩事件数据\n */\n compressEvents(events) {\n const json = JSON.stringify(events);\n const compressed = pako.gzip(json);\n return btoa(String.fromCharCode(...compressed));\n }\n\n /**\n * 获取隐私配置\n */\n getPrivacyConfig() {\n return {\n blockClass: 'rr-block',\n maskAllInputs: this.config.privacy.maskAllInputs,\n maskInputOptions: this.config.privacy.maskInputOptions,\n maskTextFn: (text, element) => {\n return this.maskSensitiveData(text, element);\n },\n };\n }\n\n /**\n * 脱敏敏感数据\n */\n maskSensitiveData(text, element) {\n if (!text) return text;\n\n const trimmed = text.trim();\n\n // 身份证\n if (this.config.privacy.maskInputOptions.idCard && /^\\d{15}|\\d{17}[\\dXx]$/.test(trimmed)) {\n return trimmed.replace(/(\\d{6})\\d{8}(\\d{4})/, '$1********$2');\n }\n\n // 手机号\n if (this.config.privacy.maskInputOptions.phone && /^1[3-9]\\d{9}$/.test(trimmed)) {\n return trimmed.replace(/(\\d{3})\\d{4}(\\d{4})/, '$1****$2');\n }\n\n // 银行卡\n if (this.config.privacy.maskInputOptions.bankCard && /^\\d{16,19}$/.test(trimmed)) {\n return trimmed.replace(/(\\d{6})\\d+(\\d{3})/, '$1******$2');\n }\n\n return text;\n }\n\n /**\n * 启动定时上传\n */\n startUploadTimer() {\n this.uploadTimer = setInterval(() => {\n this.flushEvents();\n }, this.config.uploadInterval);\n }\n\n /**\n * 停止定时上传\n */\n stopUploadTimer() {\n if (this.uploadTimer) {\n clearInterval(this.uploadTimer);\n this.uploadTimer = null;\n }\n }\n\n /**\n * 添加自定义元数据\n */\n setMetadata(metadata) {\n this.config.metadata = {\n ...this.config.metadata,\n ...metadata,\n };\n }\n\n /**\n * 获取当前会话 ID\n */\n getSessionId() {\n return this.sessionId;\n }\n\n /**\n * 是否正在录制\n */\n isRecording() {\n return this.recording;\n }\n}\n\n/**\n * API 客户端\n */\nclass APIClient {\n constructor(baseUrl, apiKey) {\n this.baseUrl = baseUrl;\n this.apiKey = apiKey;\n }\n\n async request(endpoint, options = {}) {\n const url = `${this.baseUrl}${endpoint}`;\n const headers = {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n ...options.headers,\n };\n\n const response = await fetch(url, {\n ...options,\n headers,\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.message || `HTTP ${response.status}`);\n }\n\n return await response.json();\n }\n\n async createSession(data) {\n const result = await this.request('/api/v1/sessions', {\n method: 'POST',\n body: JSON.stringify(data),\n });\n return result.data;\n }\n\n async appendEvents(sessionId, compressedEvents) {\n return await this.request(`/api/v1/sessions/${sessionId}/append`, {\n method: 'PATCH',\n body: JSON.stringify({\n events: compressedEvents,\n compressed: true,\n }),\n });\n }\n\n async finalizeSession(sessionId, data) {\n return await this.request(`/api/v1/sessions/${sessionId}/finalize`, {\n method: 'POST',\n body: JSON.stringify(data),\n });\n }\n}\n\n// 导出单例\nconst recorder = new PageRecorder();\n\n// 全局对象\nif (typeof window !== 'undefined') {\n window.PageRecorder = recorder;\n}\n\nexport default recorder;\n\n"],"names":["APIClient","constructor","baseUrl","apiKey","this","request","endpoint","options","url","headers","response","fetch","ok","error","json","catch","Error","message","status","createSession","data","method","body","JSON","stringify","appendEvents","sessionId","compressedEvents","events","compressed","finalizeSession","recorder","config","recording","stopFn","uploadQueue","uploadTimer","apiClient","init","projectId","apiBaseUrl","autoStart","chunkSize","uploadInterval","privacy","maskAllInputs","maskInputOptions","idCard","phone","bankCard","metadata","onError","console","onSessionCreated","onUploadProgress","start","customMetadata","warn","session","window","location","href","userAgent","navigator","screenResolution","screen","width","height","startTime","Date","toISOString","record","emit","event","handleEvent","checkoutEveryNms","getPrivacyConfig","sampling","mousemove","mouseInteraction","scroll","input","recordCanvas","inlineImages","collectFonts","startUploadTimer","log","stop","stopUploadTimer","flushEvents","endTime","eventCount","length","push","eventsToUpload","compressEvents","uploaded","total","unshift","setTimeout","pako","gzip","btoa","String","fromCharCode","blockClass","maskTextFn","text","element","maskSensitiveData","trimmed","trim","test","replace","setInterval","clearInterval","setMetadata","getSessionId","isRecording","PageRecorder"],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";var t=require("rrweb"),s=require("pako");class e{constructor(t,s){this.baseUrl=t,this.apiKey=s}async request(t,s={}){const e=`${this.baseUrl}${t}`,i={"Content-Type":"application/json","X-API-Key":this.apiKey,...s.headers},n=await fetch(e,{...s,headers:i});if(!n.ok){const t=await n.json().catch(()=>({}));throw new Error(t.message||`HTTP ${n.status}`)}return await n.json()}async createSession(t){return(await this.request("/api/v1/sessions",{method:"POST",body:JSON.stringify(t)})).data}async appendEvents(t,s){return await this.request(`/api/v1/sessions/${t}/append`,{method:"PATCH",body:JSON.stringify({events:s,compressed:!0})})}async finalizeSession(t,s){return await this.request(`/api/v1/sessions/${t}/finalize`,{method:"POST",body:JSON.stringify(s)})}}const i=new class{constructor(){this.config=null,this.sessionId=null,this.recording=!1,this.stopFn=null,this.events=[],this.uploadQueue=[],this.uploadTimer=null,this.apiClient=null}async init(t){return this.config={apiKey:t.apiKey,projectId:t.projectId,apiBaseUrl:t.apiBaseUrl||"https://api.yourplatform.com",autoStart:!1!==t.autoStart,chunkSize:t.chunkSize||100,uploadInterval:t.uploadInterval||3e4,privacy:{maskAllInputs:!1,maskInputOptions:{idCard:!0,phone:!0,bankCard:!0,...t.privacy?.maskInputOptions},...t.privacy},metadata:t.metadata||{},onError:t.onError||console.error,onSessionCreated:t.onSessionCreated||(()=>{}),onUploadProgress:t.onUploadProgress||(()=>{})},this.apiClient=new e(this.config.apiBaseUrl,this.config.apiKey),this.config.autoStart&&await this.start(),this}async start(s={}){if(this.recording)console.warn("Recording already in progress");else try{const e=await this.apiClient.createSession({projectId:this.config.projectId,metadata:{...this.config.metadata,...s,url:window.location.href,userAgent:navigator.userAgent,screenResolution:`${window.screen.width}x${window.screen.height}`,startTime:(new Date).toISOString()}});this.sessionId=e.sessionId,this.recording=!0,this.events=[],this.config.onSessionCreated(e),this.stopFn=t.record({emit:t=>{this.handleEvent(t)},checkoutEveryNms:3e5,...this.getPrivacyConfig(),sampling:{mousemove:!0,mouseInteraction:!0,scroll:150,input:"last"},recordCanvas:!1,inlineImages:!1,collectFonts:!1}),this.startUploadTimer(),console.log("Recording started:",this.sessionId)}catch(t){throw this.config.onError("Failed to start recording",t),t}}async stop(){if(!this.recording)return;this.recording=!1,this.stopFn&&(this.stopFn(),this.stopFn=null),this.stopUploadTimer(),await this.flushEvents(),await this.apiClient.finalizeSession(this.sessionId,{endTime:(new Date).toISOString(),eventCount:this.events.length}),console.log("Recording stopped:",this.sessionId);const t=this.sessionId;return this.sessionId=null,this.events=[],t}handleEvent(t){this.events.push(t),this.events.length>=this.config.chunkSize&&this.flushEvents()}async flushEvents(){if(0===this.events.length||!this.sessionId)return;const t=[...this.events];this.events=[];try{const s=this.compressEvents(t);await this.apiClient.appendEvents(this.sessionId,s),this.config.onUploadProgress({sessionId:this.sessionId,uploaded:t.length,total:this.events.length+t.length})}catch(s){this.events.unshift(...t),this.config.onError("Failed to upload events",s),setTimeout(()=>this.flushEvents(),5e3)}}compressEvents(t){const e=JSON.stringify(t),i=s.gzip(e);return btoa(String.fromCharCode(...i))}getPrivacyConfig(){return{blockClass:"rr-block",maskAllInputs:this.config.privacy.maskAllInputs,maskInputOptions:this.config.privacy.maskInputOptions,maskTextFn:(t,s)=>this.maskSensitiveData(t,s)}}maskSensitiveData(t,s){if(!t)return t;const e=t.trim();return this.config.privacy.maskInputOptions.idCard&&/^\d{15}|\d{17}[\dXx]$/.test(e)?e.replace(/(\d{6})\d{8}(\d{4})/,"$1********$2"):this.config.privacy.maskInputOptions.phone&&/^1[3-9]\d{9}$/.test(e)?e.replace(/(\d{3})\d{4}(\d{4})/,"$1****$2"):this.config.privacy.maskInputOptions.bankCard&&/^\d{16,19}$/.test(e)?e.replace(/(\d{6})\d+(\d{3})/,"$1******$2"):t}startUploadTimer(){this.uploadTimer=setInterval(()=>{this.flushEvents()},this.config.uploadInterval)}stopUploadTimer(){this.uploadTimer&&(clearInterval(this.uploadTimer),this.uploadTimer=null)}setMetadata(t){this.config.metadata={...this.config.metadata,...t}}getSessionId(){return this.sessionId}isRecording(){return this.recording}};"undefined"!=typeof window&&(window.PageRecorder=i),module.exports=i;
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/index.js"],"sourcesContent":["/**\n * 页面录制系统 JavaScript SDK\n * 用于页面会话录制和回溯\n */\n\nimport { record } from 'rrweb';\nimport pako from 'pako';\n\nclass PageRecorder {\n constructor() {\n this.config = null;\n this.sessionId = null;\n this.recording = false;\n this.stopFn = null;\n this.events = [];\n this.uploadQueue = [];\n this.uploadTimer = null;\n this.apiClient = null;\n }\n\n /**\n * 初始化录制器\n */\n async init(config) {\n this.config = {\n apiKey: config.apiKey,\n projectId: config.projectId,\n apiBaseUrl: config.apiBaseUrl || 'https://api.yourplatform.com',\n autoStart: config.autoStart !== false,\n chunkSize: config.chunkSize || 100,\n uploadInterval: config.uploadInterval || 30000, // 30秒\n privacy: {\n maskAllInputs: false,\n maskInputOptions: {\n idCard: true,\n phone: true,\n bankCard: true,\n ...config.privacy?.maskInputOptions,\n },\n ...config.privacy,\n },\n metadata: config.metadata || {},\n onError: config.onError || console.error,\n onSessionCreated: config.onSessionCreated || (() => {}),\n onUploadProgress: config.onUploadProgress || (() => {}),\n };\n\n // 初始化 API 客户端\n this.apiClient = new APIClient(this.config.apiBaseUrl, this.config.apiKey);\n\n // 自动开始录制\n if (this.config.autoStart) {\n await this.start();\n }\n\n return this;\n }\n\n /**\n * 开始录制\n */\n async start(customMetadata = {}) {\n if (this.recording) {\n console.warn('Recording already in progress');\n return;\n }\n\n try {\n // 创建会话\n const session = await this.apiClient.createSession({\n projectId: this.config.projectId,\n metadata: {\n ...this.config.metadata,\n ...customMetadata,\n url: window.location.href,\n userAgent: navigator.userAgent,\n screenResolution: `${window.screen.width}x${window.screen.height}`,\n startTime: new Date().toISOString(),\n },\n });\n\n this.sessionId = session.sessionId;\n this.recording = true;\n this.events = [];\n\n // 通知会话创建\n this.config.onSessionCreated(session);\n\n // 开始 rrweb 录制\n this.stopFn = record({\n emit: (event) => {\n this.handleEvent(event);\n },\n checkoutEveryNms: 5 * 60 * 1000,\n ...this.getPrivacyConfig(),\n sampling: {\n mousemove: true,\n mouseInteraction: true,\n scroll: 150,\n input: 'last',\n },\n recordCanvas: false,\n inlineImages: false,\n collectFonts: false,\n });\n\n // 启动定时上传\n this.startUploadTimer();\n\n console.log('Recording started:', this.sessionId);\n } catch (error) {\n this.config.onError('Failed to start recording', error);\n throw error;\n }\n }\n\n /**\n * 停止录制\n */\n async stop() {\n if (!this.recording) {\n return;\n }\n\n this.recording = false;\n\n // 停止 rrweb 录制\n if (this.stopFn) {\n this.stopFn();\n this.stopFn = null;\n }\n\n // 停止定时上传\n this.stopUploadTimer();\n\n // 上传剩余事件\n await this.flushEvents();\n\n // 完成会话\n await this.apiClient.finalizeSession(this.sessionId, {\n endTime: new Date().toISOString(),\n eventCount: this.events.length,\n });\n\n console.log('Recording stopped:', this.sessionId);\n\n const sessionId = this.sessionId;\n this.sessionId = null;\n this.events = [];\n\n return sessionId;\n }\n\n /**\n * 处理录制事件\n */\n handleEvent(event) {\n this.events.push(event);\n\n // 批量上传\n if (this.events.length >= this.config.chunkSize) {\n this.flushEvents();\n }\n }\n\n /**\n * 上传事件\n */\n async flushEvents() {\n if (this.events.length === 0 || !this.sessionId) {\n return;\n }\n\n const eventsToUpload = [...this.events];\n this.events = [];\n\n try {\n // 压缩数据\n const compressed = this.compressEvents(eventsToUpload);\n\n // 上传\n await this.apiClient.appendEvents(this.sessionId, compressed);\n\n // 通知上传进度\n this.config.onUploadProgress({\n sessionId: this.sessionId,\n uploaded: eventsToUpload.length,\n total: this.events.length + eventsToUpload.length,\n });\n } catch (error) {\n // 上传失败,放回队列\n this.events.unshift(...eventsToUpload);\n this.config.onError('Failed to upload events', error);\n\n // 重试\n setTimeout(() => this.flushEvents(), 5000);\n }\n }\n\n /**\n * 压缩事件数据\n */\n compressEvents(events) {\n const json = JSON.stringify(events);\n const compressed = pako.gzip(json);\n return btoa(String.fromCharCode(...compressed));\n }\n\n /**\n * 获取隐私配置\n */\n getPrivacyConfig() {\n return {\n blockClass: 'rr-block',\n maskAllInputs: this.config.privacy.maskAllInputs,\n maskInputOptions: this.config.privacy.maskInputOptions,\n maskTextFn: (text, element) => {\n return this.maskSensitiveData(text, element);\n },\n };\n }\n\n /**\n * 脱敏敏感数据\n */\n maskSensitiveData(text, element) {\n if (!text) return text;\n\n const trimmed = text.trim();\n\n // 身份证\n if (this.config.privacy.maskInputOptions.idCard && /^\\d{15}|\\d{17}[\\dXx]$/.test(trimmed)) {\n return trimmed.replace(/(\\d{6})\\d{8}(\\d{4})/, '$1********$2');\n }\n\n // 手机号\n if (this.config.privacy.maskInputOptions.phone && /^1[3-9]\\d{9}$/.test(trimmed)) {\n return trimmed.replace(/(\\d{3})\\d{4}(\\d{4})/, '$1****$2');\n }\n\n // 银行卡\n if (this.config.privacy.maskInputOptions.bankCard && /^\\d{16,19}$/.test(trimmed)) {\n return trimmed.replace(/(\\d{6})\\d+(\\d{3})/, '$1******$2');\n }\n\n return text;\n }\n\n /**\n * 启动定时上传\n */\n startUploadTimer() {\n this.uploadTimer = setInterval(() => {\n this.flushEvents();\n }, this.config.uploadInterval);\n }\n\n /**\n * 停止定时上传\n */\n stopUploadTimer() {\n if (this.uploadTimer) {\n clearInterval(this.uploadTimer);\n this.uploadTimer = null;\n }\n }\n\n /**\n * 添加自定义元数据\n */\n setMetadata(metadata) {\n this.config.metadata = {\n ...this.config.metadata,\n ...metadata,\n };\n }\n\n /**\n * 获取当前会话 ID\n */\n getSessionId() {\n return this.sessionId;\n }\n\n /**\n * 是否正在录制\n */\n isRecording() {\n return this.recording;\n }\n}\n\n/**\n * API 客户端\n */\nclass APIClient {\n constructor(baseUrl, apiKey) {\n this.baseUrl = baseUrl;\n this.apiKey = apiKey;\n }\n\n async request(endpoint, options = {}) {\n const url = `${this.baseUrl}${endpoint}`;\n const headers = {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n ...options.headers,\n };\n\n const response = await fetch(url, {\n ...options,\n headers,\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.message || `HTTP ${response.status}`);\n }\n\n return await response.json();\n }\n\n async createSession(data) {\n const result = await this.request('/api/v1/sessions', {\n method: 'POST',\n body: JSON.stringify(data),\n });\n return result.data;\n }\n\n async appendEvents(sessionId, compressedEvents) {\n return await this.request(`/api/v1/sessions/${sessionId}/append`, {\n method: 'PATCH',\n body: JSON.stringify({\n events: compressedEvents,\n compressed: true,\n }),\n });\n }\n\n async finalizeSession(sessionId, data) {\n return await this.request(`/api/v1/sessions/${sessionId}/finalize`, {\n method: 'POST',\n body: JSON.stringify(data),\n });\n }\n}\n\n// 导出单例\nconst recorder = new PageRecorder();\n\n// 全局对象\nif (typeof window !== 'undefined') {\n window.PageRecorder = recorder;\n}\n\nexport default recorder;\n\n"],"names":["APIClient","constructor","baseUrl","apiKey","this","request","endpoint","options","url","headers","response","fetch","ok","error","json","catch","Error","message","status","createSession","data","method","body","JSON","stringify","appendEvents","sessionId","compressedEvents","events","compressed","finalizeSession","recorder","config","recording","stopFn","uploadQueue","uploadTimer","apiClient","init","projectId","apiBaseUrl","autoStart","chunkSize","uploadInterval","privacy","maskAllInputs","maskInputOptions","idCard","phone","bankCard","metadata","onError","console","onSessionCreated","onUploadProgress","start","customMetadata","warn","session","window","location","href","userAgent","navigator","screenResolution","screen","width","height","startTime","Date","toISOString","record","emit","event","handleEvent","checkoutEveryNms","getPrivacyConfig","sampling","mousemove","mouseInteraction","scroll","input","recordCanvas","inlineImages","collectFonts","startUploadTimer","log","stop","stopUploadTimer","flushEvents","endTime","eventCount","length","push","eventsToUpload","compressEvents","uploaded","total","unshift","setTimeout","pako","gzip","btoa","String","fromCharCode","blockClass","maskTextFn","text","element","maskSensitiveData","trimmed","trim","test","replace","setInterval","clearInterval","setMetadata","getSessionId","isRecording","PageRecorder"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ !function(t,s){"object"==typeof exports&&"undefined"!=typeof module?module.exports=s(require("rrweb"),require("pako")):"function"==typeof define&&define.amd?define(["rrweb","pako"],s):(t="undefined"!=typeof globalThis?globalThis:t||self).PageRecorder=s(t.rrweb,t.pako)}(this,function(t,s){"use strict";class e{constructor(t,s){this.baseUrl=t,this.apiKey=s}async request(t,s={}){const e=`${this.baseUrl}${t}`,i={"Content-Type":"application/json","X-API-Key":this.apiKey,...s.headers},n=await fetch(e,{...s,headers:i});if(!n.ok){const t=await n.json().catch(()=>({}));throw new Error(t.message||`HTTP ${n.status}`)}return await n.json()}async createSession(t){return(await this.request("/api/v1/sessions",{method:"POST",body:JSON.stringify(t)})).data}async appendEvents(t,s){return await this.request(`/api/v1/sessions/${t}/append`,{method:"PATCH",body:JSON.stringify({events:s,compressed:!0})})}async finalizeSession(t,s){return await this.request(`/api/v1/sessions/${t}/finalize`,{method:"POST",body:JSON.stringify(s)})}}const i=new class{constructor(){this.config=null,this.sessionId=null,this.recording=!1,this.stopFn=null,this.events=[],this.uploadQueue=[],this.uploadTimer=null,this.apiClient=null}async init(t){return this.config={apiKey:t.apiKey,projectId:t.projectId,apiBaseUrl:t.apiBaseUrl||"https://api.yourplatform.com",autoStart:!1!==t.autoStart,chunkSize:t.chunkSize||100,uploadInterval:t.uploadInterval||3e4,privacy:{maskAllInputs:!1,maskInputOptions:{idCard:!0,phone:!0,bankCard:!0,...t.privacy?.maskInputOptions},...t.privacy},metadata:t.metadata||{},onError:t.onError||console.error,onSessionCreated:t.onSessionCreated||(()=>{}),onUploadProgress:t.onUploadProgress||(()=>{})},this.apiClient=new e(this.config.apiBaseUrl,this.config.apiKey),this.config.autoStart&&await this.start(),this}async start(s={}){if(this.recording)console.warn("Recording already in progress");else try{const e=await this.apiClient.createSession({projectId:this.config.projectId,metadata:{...this.config.metadata,...s,url:window.location.href,userAgent:navigator.userAgent,screenResolution:`${window.screen.width}x${window.screen.height}`,startTime:(new Date).toISOString()}});this.sessionId=e.sessionId,this.recording=!0,this.events=[],this.config.onSessionCreated(e),this.stopFn=t.record({emit:t=>{this.handleEvent(t)},checkoutEveryNms:3e5,...this.getPrivacyConfig(),sampling:{mousemove:!0,mouseInteraction:!0,scroll:150,input:"last"},recordCanvas:!1,inlineImages:!1,collectFonts:!1}),this.startUploadTimer(),console.log("Recording started:",this.sessionId)}catch(t){throw this.config.onError("Failed to start recording",t),t}}async stop(){if(!this.recording)return;this.recording=!1,this.stopFn&&(this.stopFn(),this.stopFn=null),this.stopUploadTimer(),await this.flushEvents(),await this.apiClient.finalizeSession(this.sessionId,{endTime:(new Date).toISOString(),eventCount:this.events.length}),console.log("Recording stopped:",this.sessionId);const t=this.sessionId;return this.sessionId=null,this.events=[],t}handleEvent(t){this.events.push(t),this.events.length>=this.config.chunkSize&&this.flushEvents()}async flushEvents(){if(0===this.events.length||!this.sessionId)return;const t=[...this.events];this.events=[];try{const s=this.compressEvents(t);await this.apiClient.appendEvents(this.sessionId,s),this.config.onUploadProgress({sessionId:this.sessionId,uploaded:t.length,total:this.events.length+t.length})}catch(s){this.events.unshift(...t),this.config.onError("Failed to upload events",s),setTimeout(()=>this.flushEvents(),5e3)}}compressEvents(t){const e=JSON.stringify(t),i=s.gzip(e);return btoa(String.fromCharCode(...i))}getPrivacyConfig(){return{blockClass:"rr-block",maskAllInputs:this.config.privacy.maskAllInputs,maskInputOptions:this.config.privacy.maskInputOptions,maskTextFn:(t,s)=>this.maskSensitiveData(t,s)}}maskSensitiveData(t,s){if(!t)return t;const e=t.trim();return this.config.privacy.maskInputOptions.idCard&&/^\d{15}|\d{17}[\dXx]$/.test(e)?e.replace(/(\d{6})\d{8}(\d{4})/,"$1********$2"):this.config.privacy.maskInputOptions.phone&&/^1[3-9]\d{9}$/.test(e)?e.replace(/(\d{3})\d{4}(\d{4})/,"$1****$2"):this.config.privacy.maskInputOptions.bankCard&&/^\d{16,19}$/.test(e)?e.replace(/(\d{6})\d+(\d{3})/,"$1******$2"):t}startUploadTimer(){this.uploadTimer=setInterval(()=>{this.flushEvents()},this.config.uploadInterval)}stopUploadTimer(){this.uploadTimer&&(clearInterval(this.uploadTimer),this.uploadTimer=null)}setMetadata(t){this.config.metadata={...this.config.metadata,...t}}getSessionId(){return this.sessionId}isRecording(){return this.recording}};return"undefined"!=typeof window&&(window.PageRecorder=i),i});
2
+ //# sourceMappingURL=index.umd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.umd.js","sources":["../src/index.js"],"sourcesContent":["/**\n * 页面录制系统 JavaScript SDK\n * 用于页面会话录制和回溯\n */\n\nimport { record } from 'rrweb';\nimport pako from 'pako';\n\nclass PageRecorder {\n constructor() {\n this.config = null;\n this.sessionId = null;\n this.recording = false;\n this.stopFn = null;\n this.events = [];\n this.uploadQueue = [];\n this.uploadTimer = null;\n this.apiClient = null;\n }\n\n /**\n * 初始化录制器\n */\n async init(config) {\n this.config = {\n apiKey: config.apiKey,\n projectId: config.projectId,\n apiBaseUrl: config.apiBaseUrl || 'https://api.yourplatform.com',\n autoStart: config.autoStart !== false,\n chunkSize: config.chunkSize || 100,\n uploadInterval: config.uploadInterval || 30000, // 30秒\n privacy: {\n maskAllInputs: false,\n maskInputOptions: {\n idCard: true,\n phone: true,\n bankCard: true,\n ...config.privacy?.maskInputOptions,\n },\n ...config.privacy,\n },\n metadata: config.metadata || {},\n onError: config.onError || console.error,\n onSessionCreated: config.onSessionCreated || (() => {}),\n onUploadProgress: config.onUploadProgress || (() => {}),\n };\n\n // 初始化 API 客户端\n this.apiClient = new APIClient(this.config.apiBaseUrl, this.config.apiKey);\n\n // 自动开始录制\n if (this.config.autoStart) {\n await this.start();\n }\n\n return this;\n }\n\n /**\n * 开始录制\n */\n async start(customMetadata = {}) {\n if (this.recording) {\n console.warn('Recording already in progress');\n return;\n }\n\n try {\n // 创建会话\n const session = await this.apiClient.createSession({\n projectId: this.config.projectId,\n metadata: {\n ...this.config.metadata,\n ...customMetadata,\n url: window.location.href,\n userAgent: navigator.userAgent,\n screenResolution: `${window.screen.width}x${window.screen.height}`,\n startTime: new Date().toISOString(),\n },\n });\n\n this.sessionId = session.sessionId;\n this.recording = true;\n this.events = [];\n\n // 通知会话创建\n this.config.onSessionCreated(session);\n\n // 开始 rrweb 录制\n this.stopFn = record({\n emit: (event) => {\n this.handleEvent(event);\n },\n checkoutEveryNms: 5 * 60 * 1000,\n ...this.getPrivacyConfig(),\n sampling: {\n mousemove: true,\n mouseInteraction: true,\n scroll: 150,\n input: 'last',\n },\n recordCanvas: false,\n inlineImages: false,\n collectFonts: false,\n });\n\n // 启动定时上传\n this.startUploadTimer();\n\n console.log('Recording started:', this.sessionId);\n } catch (error) {\n this.config.onError('Failed to start recording', error);\n throw error;\n }\n }\n\n /**\n * 停止录制\n */\n async stop() {\n if (!this.recording) {\n return;\n }\n\n this.recording = false;\n\n // 停止 rrweb 录制\n if (this.stopFn) {\n this.stopFn();\n this.stopFn = null;\n }\n\n // 停止定时上传\n this.stopUploadTimer();\n\n // 上传剩余事件\n await this.flushEvents();\n\n // 完成会话\n await this.apiClient.finalizeSession(this.sessionId, {\n endTime: new Date().toISOString(),\n eventCount: this.events.length,\n });\n\n console.log('Recording stopped:', this.sessionId);\n\n const sessionId = this.sessionId;\n this.sessionId = null;\n this.events = [];\n\n return sessionId;\n }\n\n /**\n * 处理录制事件\n */\n handleEvent(event) {\n this.events.push(event);\n\n // 批量上传\n if (this.events.length >= this.config.chunkSize) {\n this.flushEvents();\n }\n }\n\n /**\n * 上传事件\n */\n async flushEvents() {\n if (this.events.length === 0 || !this.sessionId) {\n return;\n }\n\n const eventsToUpload = [...this.events];\n this.events = [];\n\n try {\n // 压缩数据\n const compressed = this.compressEvents(eventsToUpload);\n\n // 上传\n await this.apiClient.appendEvents(this.sessionId, compressed);\n\n // 通知上传进度\n this.config.onUploadProgress({\n sessionId: this.sessionId,\n uploaded: eventsToUpload.length,\n total: this.events.length + eventsToUpload.length,\n });\n } catch (error) {\n // 上传失败,放回队列\n this.events.unshift(...eventsToUpload);\n this.config.onError('Failed to upload events', error);\n\n // 重试\n setTimeout(() => this.flushEvents(), 5000);\n }\n }\n\n /**\n * 压缩事件数据\n */\n compressEvents(events) {\n const json = JSON.stringify(events);\n const compressed = pako.gzip(json);\n return btoa(String.fromCharCode(...compressed));\n }\n\n /**\n * 获取隐私配置\n */\n getPrivacyConfig() {\n return {\n blockClass: 'rr-block',\n maskAllInputs: this.config.privacy.maskAllInputs,\n maskInputOptions: this.config.privacy.maskInputOptions,\n maskTextFn: (text, element) => {\n return this.maskSensitiveData(text, element);\n },\n };\n }\n\n /**\n * 脱敏敏感数据\n */\n maskSensitiveData(text, element) {\n if (!text) return text;\n\n const trimmed = text.trim();\n\n // 身份证\n if (this.config.privacy.maskInputOptions.idCard && /^\\d{15}|\\d{17}[\\dXx]$/.test(trimmed)) {\n return trimmed.replace(/(\\d{6})\\d{8}(\\d{4})/, '$1********$2');\n }\n\n // 手机号\n if (this.config.privacy.maskInputOptions.phone && /^1[3-9]\\d{9}$/.test(trimmed)) {\n return trimmed.replace(/(\\d{3})\\d{4}(\\d{4})/, '$1****$2');\n }\n\n // 银行卡\n if (this.config.privacy.maskInputOptions.bankCard && /^\\d{16,19}$/.test(trimmed)) {\n return trimmed.replace(/(\\d{6})\\d+(\\d{3})/, '$1******$2');\n }\n\n return text;\n }\n\n /**\n * 启动定时上传\n */\n startUploadTimer() {\n this.uploadTimer = setInterval(() => {\n this.flushEvents();\n }, this.config.uploadInterval);\n }\n\n /**\n * 停止定时上传\n */\n stopUploadTimer() {\n if (this.uploadTimer) {\n clearInterval(this.uploadTimer);\n this.uploadTimer = null;\n }\n }\n\n /**\n * 添加自定义元数据\n */\n setMetadata(metadata) {\n this.config.metadata = {\n ...this.config.metadata,\n ...metadata,\n };\n }\n\n /**\n * 获取当前会话 ID\n */\n getSessionId() {\n return this.sessionId;\n }\n\n /**\n * 是否正在录制\n */\n isRecording() {\n return this.recording;\n }\n}\n\n/**\n * API 客户端\n */\nclass APIClient {\n constructor(baseUrl, apiKey) {\n this.baseUrl = baseUrl;\n this.apiKey = apiKey;\n }\n\n async request(endpoint, options = {}) {\n const url = `${this.baseUrl}${endpoint}`;\n const headers = {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n ...options.headers,\n };\n\n const response = await fetch(url, {\n ...options,\n headers,\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.message || `HTTP ${response.status}`);\n }\n\n return await response.json();\n }\n\n async createSession(data) {\n const result = await this.request('/api/v1/sessions', {\n method: 'POST',\n body: JSON.stringify(data),\n });\n return result.data;\n }\n\n async appendEvents(sessionId, compressedEvents) {\n return await this.request(`/api/v1/sessions/${sessionId}/append`, {\n method: 'PATCH',\n body: JSON.stringify({\n events: compressedEvents,\n compressed: true,\n }),\n });\n }\n\n async finalizeSession(sessionId, data) {\n return await this.request(`/api/v1/sessions/${sessionId}/finalize`, {\n method: 'POST',\n body: JSON.stringify(data),\n });\n }\n}\n\n// 导出单例\nconst recorder = new PageRecorder();\n\n// 全局对象\nif (typeof window !== 'undefined') {\n window.PageRecorder = recorder;\n}\n\nexport default recorder;\n\n"],"names":["APIClient","constructor","baseUrl","apiKey","this","request","endpoint","options","url","headers","response","fetch","ok","error","json","catch","Error","message","status","createSession","data","method","body","JSON","stringify","appendEvents","sessionId","compressedEvents","events","compressed","finalizeSession","recorder","config","recording","stopFn","uploadQueue","uploadTimer","apiClient","init","projectId","apiBaseUrl","autoStart","chunkSize","uploadInterval","privacy","maskAllInputs","maskInputOptions","idCard","phone","bankCard","metadata","onError","console","onSessionCreated","onUploadProgress","start","customMetadata","warn","session","window","location","href","userAgent","navigator","screenResolution","screen","width","height","startTime","Date","toISOString","record","emit","event","handleEvent","checkoutEveryNms","getPrivacyConfig","sampling","mousemove","mouseInteraction","scroll","input","recordCanvas","inlineImages","collectFonts","startUploadTimer","log","stop","stopUploadTimer","flushEvents","endTime","eventCount","length","push","eventsToUpload","compressEvents","uploaded","total","unshift","setTimeout","pako","gzip","btoa","String","fromCharCode","blockClass","maskTextFn","text","element","maskSensitiveData","trimmed","trim","test","replace","setInterval","clearInterval","setMetadata","getSessionId","isRecording","PageRecorder"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@deepsure/page-replay-sdk",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "DeepSure PageReplay SDK",
6
+ "main": "dist/index.js",
7
+ "module": "dist/index.esm.js",
8
+ "browser": "dist/index.umd.js",
9
+ "types": "dist/index.d.ts",
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "scripts": {
14
+ "build": "rollup -c && npm run ensure-types",
15
+ "ensure-types": "mkdir -p dist && test -f src/index.d.ts && cp src/index.d.ts dist/index.d.ts || true",
16
+ "dev": "rollup -c -w",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "keywords": [
20
+ "session-replay",
21
+ "finance",
22
+ "compliance",
23
+ "recorder",
24
+ "page-replay"
25
+ ],
26
+ "author": "DeepSure",
27
+ "license": "MIT",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": ""
31
+ },
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "dependencies": {
36
+ "rrweb": "^2.0.0-alpha.4",
37
+ "pako": "^2.1.0"
38
+ },
39
+ "devDependencies": {
40
+ "@rollup/plugin-commonjs": "^25.0.7",
41
+ "@rollup/plugin-node-resolve": "^15.2.3",
42
+ "@rollup/plugin-terser": "^0.4.4",
43
+ "rollup": "^4.9.4"
44
+ },
45
+ "peerDependencies": {
46
+ "rrweb": "^2.0.0-alpha.4"
47
+ }
48
+ }
49
+