@monoscopetech/browser 0.5.5 → 0.5.7
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/dist/monoscope.min.js +3 -3
- package/dist/monoscope.min.js.map +1 -1
- package/dist/monoscope.umd.js +3 -3
- package/dist/monoscope.umd.js.map +1 -1
- package/dist/replay.d.ts +15 -4
- package/dist/replay.js +166 -50
- package/package.json +1 -1
package/dist/replay.d.ts
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import { MonoscopeConfig } from "./types";
|
|
2
2
|
export declare class MonoscopeReplay {
|
|
3
|
-
events
|
|
4
|
-
config
|
|
5
|
-
sessionId
|
|
3
|
+
private events;
|
|
4
|
+
private config;
|
|
5
|
+
private sessionId;
|
|
6
|
+
private stopRecording;
|
|
7
|
+
private saveInterval;
|
|
8
|
+
private isSaving;
|
|
9
|
+
private isConfigured;
|
|
6
10
|
constructor(config: MonoscopeConfig, sessionId: string);
|
|
11
|
+
private setupEventListeners;
|
|
12
|
+
private handleUnload;
|
|
13
|
+
private handleVisibilityChange;
|
|
7
14
|
configure(): void;
|
|
8
|
-
save(): void
|
|
15
|
+
save(forceSynchronous?: boolean): Promise<void>;
|
|
16
|
+
stop(): void;
|
|
17
|
+
getEventCount(): number;
|
|
18
|
+
getSessionId(): string;
|
|
19
|
+
isRecording(): boolean;
|
|
9
20
|
}
|
package/dist/replay.js
CHANGED
|
@@ -1,72 +1,188 @@
|
|
|
1
1
|
import { getRecordConsolePlugin } from "@rrweb/rrweb-plugin-console-record";
|
|
2
2
|
import * as rrweb from "rrweb";
|
|
3
|
-
const MAX_EVENT_BATCH =
|
|
3
|
+
const MAX_EVENT_BATCH = 50;
|
|
4
|
+
const SAVE_INTERVAL = 10000;
|
|
5
|
+
const MAX_RETRY_EVENTS = 1000;
|
|
4
6
|
export class MonoscopeReplay {
|
|
5
7
|
constructor(config, sessionId) {
|
|
6
8
|
this.events = [];
|
|
9
|
+
this.stopRecording = undefined;
|
|
10
|
+
this.saveInterval = null;
|
|
11
|
+
this.isSaving = false;
|
|
12
|
+
this.isConfigured = false;
|
|
7
13
|
this.sessionId = sessionId;
|
|
8
14
|
this.config = config;
|
|
9
|
-
this.save = this.save.bind(this);
|
|
10
15
|
this.events = [];
|
|
16
|
+
// Bind methods
|
|
17
|
+
this.save = this.save.bind(this);
|
|
11
18
|
this.configure = this.configure.bind(this);
|
|
12
|
-
|
|
19
|
+
this.handleUnload = this.handleUnload.bind(this);
|
|
20
|
+
this.handleVisibilityChange = this.handleVisibilityChange.bind(this);
|
|
21
|
+
// Setup event listeners
|
|
22
|
+
this.setupEventListeners();
|
|
23
|
+
}
|
|
24
|
+
setupEventListeners() {
|
|
25
|
+
window.addEventListener("beforeunload", this.handleUnload);
|
|
26
|
+
document.addEventListener("visibilitychange", this.handleVisibilityChange);
|
|
27
|
+
window.addEventListener("pagehide", this.handleUnload);
|
|
28
|
+
}
|
|
29
|
+
handleUnload() {
|
|
30
|
+
this.save(true); // Force synchronous save on unload
|
|
31
|
+
}
|
|
32
|
+
handleVisibilityChange() {
|
|
33
|
+
if (document.visibilityState === "hidden") {
|
|
34
|
+
this.save();
|
|
35
|
+
}
|
|
13
36
|
}
|
|
14
37
|
configure() {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
if (this.isConfigured) {
|
|
39
|
+
console.warn("MonoscopeReplay already configured");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
this.stopRecording = rrweb.record({
|
|
44
|
+
emit: (event) => {
|
|
45
|
+
this.events.push(event);
|
|
46
|
+
// Auto-save when batch size reached
|
|
47
|
+
if (this.events.length >= MAX_EVENT_BATCH) {
|
|
48
|
+
this.save();
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
blockClass: "rr-block",
|
|
52
|
+
maskAllInputs: true,
|
|
53
|
+
maskInputOptions: {
|
|
54
|
+
password: true,
|
|
55
|
+
email: true,
|
|
56
|
+
tel: true,
|
|
57
|
+
},
|
|
58
|
+
maskTextClass: "rr-mask",
|
|
59
|
+
// Performance settings
|
|
60
|
+
checkoutEveryNms: 15 * 1000, // Full snapshot every 15s
|
|
61
|
+
sampling: {
|
|
62
|
+
mouseInteraction: {
|
|
63
|
+
MouseUp: false,
|
|
64
|
+
MouseDown: false,
|
|
65
|
+
Click: true,
|
|
66
|
+
ContextMenu: false,
|
|
67
|
+
DblClick: true,
|
|
68
|
+
Focus: false,
|
|
69
|
+
Blur: false,
|
|
70
|
+
TouchStart: true,
|
|
71
|
+
TouchEnd: false,
|
|
38
72
|
},
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
73
|
+
mousemove: true,
|
|
74
|
+
scroll: 150, // Throttle scroll events
|
|
75
|
+
media: 800,
|
|
76
|
+
input: "last", // Only capture final input value
|
|
77
|
+
},
|
|
78
|
+
plugins: [
|
|
79
|
+
getRecordConsolePlugin({
|
|
80
|
+
level: ["info", "log", "warn", "error"],
|
|
81
|
+
lengthThreshold: 10000,
|
|
82
|
+
stringifyOptions: {
|
|
83
|
+
stringLengthLimit: 1000,
|
|
84
|
+
numOfKeysLimit: 100,
|
|
85
|
+
depthOfLimit: 2, // Increased from 1 for better context
|
|
86
|
+
},
|
|
87
|
+
}),
|
|
88
|
+
],
|
|
89
|
+
});
|
|
90
|
+
this.saveInterval = setInterval(() => {
|
|
91
|
+
this.save();
|
|
92
|
+
}, SAVE_INTERVAL);
|
|
93
|
+
this.isConfigured = true;
|
|
94
|
+
console.log("MonoscopeReplay configured successfully");
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
console.error("Failed to configure MonoscopeReplay:", error);
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
43
100
|
}
|
|
44
|
-
save() {
|
|
45
|
-
if (this.
|
|
101
|
+
async save(forceSynchronous = false) {
|
|
102
|
+
if (this.isSaving && !forceSynchronous) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (this.events.length === 0) {
|
|
46
106
|
return;
|
|
47
|
-
let { replayEventsBaseUrl, projectId } = this.config;
|
|
48
|
-
if (!replayEventsBaseUrl) {
|
|
49
|
-
replayEventsBaseUrl = `https://app.apitoolkit.io/rrweb/${projectId}`;
|
|
50
107
|
}
|
|
51
|
-
|
|
52
|
-
|
|
108
|
+
if (this.events.length > MAX_RETRY_EVENTS) {
|
|
109
|
+
console.warn(`Event queue exceeded ${MAX_RETRY_EVENTS}, dropping oldest events`);
|
|
110
|
+
this.events = this.events.slice(-MAX_RETRY_EVENTS);
|
|
53
111
|
}
|
|
54
|
-
|
|
112
|
+
this.isSaving = true;
|
|
113
|
+
const { replayEventsBaseUrl, projectId } = this.config;
|
|
114
|
+
// Construct base URL
|
|
115
|
+
let baseUrl = replayEventsBaseUrl || "https://app.monoscope.tech";
|
|
116
|
+
baseUrl = `${baseUrl}/rrweb/${projectId}`;
|
|
117
|
+
// Get events to send and clear buffer
|
|
118
|
+
const eventsToSend = [...this.events];
|
|
55
119
|
this.events = [];
|
|
56
|
-
const
|
|
57
|
-
events,
|
|
120
|
+
const payload = {
|
|
121
|
+
events: eventsToSend,
|
|
58
122
|
sessionId: this.sessionId,
|
|
59
123
|
timestamp: new Date().toISOString(),
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
124
|
+
eventCount: eventsToSend.length,
|
|
125
|
+
};
|
|
126
|
+
try {
|
|
127
|
+
if (forceSynchronous && navigator.sendBeacon) {
|
|
128
|
+
const blob = new Blob([JSON.stringify(payload)], {
|
|
129
|
+
type: "application/json",
|
|
130
|
+
});
|
|
131
|
+
const sent = navigator.sendBeacon(baseUrl, blob);
|
|
132
|
+
if (!sent) {
|
|
133
|
+
console.warn("sendBeacon failed, events may be lost");
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
// Regular fetch with keepalive
|
|
138
|
+
const response = await fetch(baseUrl, {
|
|
139
|
+
method: "POST",
|
|
140
|
+
headers: {
|
|
141
|
+
"Content-Type": "application/json",
|
|
142
|
+
},
|
|
143
|
+
body: JSON.stringify(payload),
|
|
144
|
+
keepalive: true,
|
|
145
|
+
});
|
|
146
|
+
if (!response.ok) {
|
|
147
|
+
throw new Error(`Failed to save replay events: ${response.status} ${response.statusText}`);
|
|
148
|
+
}
|
|
149
|
+
console.log(`Successfully saved ${eventsToSend.length} replay events`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
68
153
|
console.error("Failed to save replay events:", error);
|
|
69
|
-
this.events = [...
|
|
70
|
-
|
|
154
|
+
this.events = [...eventsToSend, ...this.events];
|
|
155
|
+
if (this.events.length > MAX_RETRY_EVENTS) {
|
|
156
|
+
this.events = this.events.slice(-MAX_RETRY_EVENTS);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
finally {
|
|
160
|
+
this.isSaving = false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
stop() {
|
|
164
|
+
this.save(true);
|
|
165
|
+
if (this.stopRecording) {
|
|
166
|
+
this.stopRecording();
|
|
167
|
+
this.stopRecording = undefined;
|
|
168
|
+
}
|
|
169
|
+
if (this.saveInterval) {
|
|
170
|
+
clearInterval(this.saveInterval);
|
|
171
|
+
this.saveInterval = null;
|
|
172
|
+
}
|
|
173
|
+
window.removeEventListener("beforeunload", this.handleUnload);
|
|
174
|
+
window.removeEventListener("pagehide", this.handleUnload);
|
|
175
|
+
document.removeEventListener("visibilitychange", this.handleVisibilityChange);
|
|
176
|
+
this.isConfigured = false;
|
|
177
|
+
console.log("MonoscopeReplay stopped");
|
|
178
|
+
}
|
|
179
|
+
getEventCount() {
|
|
180
|
+
return this.events.length;
|
|
181
|
+
}
|
|
182
|
+
getSessionId() {
|
|
183
|
+
return this.sessionId;
|
|
184
|
+
}
|
|
185
|
+
isRecording() {
|
|
186
|
+
return this.isConfigured && this.stopRecording !== null;
|
|
71
187
|
}
|
|
72
188
|
}
|