@monoscopetech/browser 0.6.0 → 0.7.1
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 +144 -27
- package/dist/breadcrumbs.d.ts +9 -0
- package/dist/breadcrumbs.js +13 -0
- package/dist/errors.d.ts +17 -0
- package/dist/errors.js +107 -0
- package/dist/index.d.ts +20 -1
- package/dist/index.js +161 -14
- package/dist/monoscope.min.js +5 -5
- package/dist/monoscope.min.js.map +1 -1
- package/dist/monoscope.umd.js +5 -5
- package/dist/monoscope.umd.js.map +1 -1
- package/dist/react.d.ts +28 -0
- package/dist/react.js +59 -0
- package/dist/replay.d.ts +11 -2
- package/dist/replay.js +89 -73
- package/dist/router.d.ts +13 -0
- package/dist/router.js +65 -0
- package/dist/tracing.d.ts +22 -2
- package/dist/tracing.js +194 -43
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +7 -2
- package/dist/web-vitals.d.ts +10 -0
- package/dist/web-vitals.js +31 -0
- package/package.json +40 -6
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Component } from "react";
|
|
2
|
+
import type { ReactNode, ErrorInfo } from "react";
|
|
3
|
+
import Monoscope from ".";
|
|
4
|
+
import type { MonoscopeConfig, MonoscopeUser } from "./types";
|
|
5
|
+
export declare function MonoscopeProvider({ config, children }: {
|
|
6
|
+
config: MonoscopeConfig;
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export declare function useMonoscope(): Monoscope | null;
|
|
10
|
+
export declare function useMonoscopeUser(user: MonoscopeUser | null | undefined): void;
|
|
11
|
+
type ErrorBoundaryProps = {
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
fallback?: ReactNode | ((error: Error) => ReactNode);
|
|
14
|
+
};
|
|
15
|
+
type ErrorBoundaryState = {
|
|
16
|
+
error: Error | null;
|
|
17
|
+
};
|
|
18
|
+
export declare class MonoscopeErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
19
|
+
static contextType: import("react").Context<Monoscope | null>;
|
|
20
|
+
context: Monoscope | null;
|
|
21
|
+
state: ErrorBoundaryState;
|
|
22
|
+
static getDerivedStateFromError(error: Error): {
|
|
23
|
+
error: Error;
|
|
24
|
+
};
|
|
25
|
+
componentDidCatch(error: Error, info: ErrorInfo): void;
|
|
26
|
+
render(): ReactNode;
|
|
27
|
+
}
|
|
28
|
+
export {};
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext, useRef, useEffect, Component } from "react";
|
|
4
|
+
import Monoscope from ".";
|
|
5
|
+
const MonoscopeContext = createContext(null);
|
|
6
|
+
export function MonoscopeProvider({ config, children }) {
|
|
7
|
+
const ref = useRef(null);
|
|
8
|
+
if (!ref.current && typeof window !== "undefined") {
|
|
9
|
+
ref.current = new Monoscope(config);
|
|
10
|
+
}
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const instance = ref.current;
|
|
13
|
+
if (!instance)
|
|
14
|
+
return;
|
|
15
|
+
// Deferred destroy — cleared if Strict Mode remounts immediately
|
|
16
|
+
let timer;
|
|
17
|
+
return () => {
|
|
18
|
+
timer = setTimeout(() => { instance.destroy(); ref.current = null; }, 0);
|
|
19
|
+
return void timer;
|
|
20
|
+
};
|
|
21
|
+
}, []);
|
|
22
|
+
return _jsx(MonoscopeContext.Provider, { value: ref.current, children: children });
|
|
23
|
+
}
|
|
24
|
+
export function useMonoscope() {
|
|
25
|
+
return useContext(MonoscopeContext);
|
|
26
|
+
}
|
|
27
|
+
export function useMonoscopeUser(user) {
|
|
28
|
+
const instance = useMonoscope();
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (instance && user)
|
|
31
|
+
instance.setUser(user);
|
|
32
|
+
}, [instance, user]);
|
|
33
|
+
}
|
|
34
|
+
export class MonoscopeErrorBoundary extends Component {
|
|
35
|
+
constructor() {
|
|
36
|
+
super(...arguments);
|
|
37
|
+
this.state = { error: null };
|
|
38
|
+
}
|
|
39
|
+
static getDerivedStateFromError(error) {
|
|
40
|
+
return { error };
|
|
41
|
+
}
|
|
42
|
+
componentDidCatch(error, info) {
|
|
43
|
+
this.context?.recordEvent("react.error_boundary", {
|
|
44
|
+
"error.message": error.message,
|
|
45
|
+
"error.stack": error.stack ?? "",
|
|
46
|
+
"error.component_stack": info.componentStack ?? "",
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
render() {
|
|
50
|
+
if (this.state.error) {
|
|
51
|
+
const { fallback } = this.props;
|
|
52
|
+
if (typeof fallback === "function")
|
|
53
|
+
return fallback(this.state.error);
|
|
54
|
+
return fallback ?? null;
|
|
55
|
+
}
|
|
56
|
+
return this.props.children;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
MonoscopeErrorBoundary.contextType = MonoscopeContext;
|
package/dist/replay.d.ts
CHANGED
|
@@ -3,18 +3,27 @@ export declare class MonoscopeReplay {
|
|
|
3
3
|
private events;
|
|
4
4
|
private config;
|
|
5
5
|
private sessionId;
|
|
6
|
+
private tabId;
|
|
6
7
|
private stopRecording;
|
|
7
8
|
private saveInterval;
|
|
8
9
|
private isSaving;
|
|
9
10
|
private isConfigured;
|
|
10
|
-
|
|
11
|
-
private
|
|
11
|
+
private _enabled;
|
|
12
|
+
private userAttributes;
|
|
13
|
+
private _listenersAttached;
|
|
12
14
|
private handleUnload;
|
|
13
15
|
private handleVisibilityChange;
|
|
16
|
+
constructor(config: MonoscopeConfig, sessionId: string, tabId: string);
|
|
17
|
+
private setupListeners;
|
|
18
|
+
private removeListeners;
|
|
19
|
+
private trimEvents;
|
|
14
20
|
configure(): void;
|
|
15
21
|
save(forceSynchronous?: boolean): Promise<void>;
|
|
16
22
|
stop(): void;
|
|
23
|
+
setEnabled(enabled: boolean): void;
|
|
24
|
+
setUser(user: Record<string, string | string[] | undefined>): void;
|
|
17
25
|
getEventCount(): number;
|
|
26
|
+
updateSessionId(sessionId: string): void;
|
|
18
27
|
getSessionId(): string;
|
|
19
28
|
isRecording(): boolean;
|
|
20
29
|
}
|
package/dist/replay.js
CHANGED
|
@@ -4,46 +4,71 @@ const MAX_EVENT_BATCH = 50;
|
|
|
4
4
|
const SAVE_INTERVAL = 2000;
|
|
5
5
|
const MAX_RETRY_EVENTS = 5000;
|
|
6
6
|
export class MonoscopeReplay {
|
|
7
|
-
constructor(config, sessionId) {
|
|
7
|
+
constructor(config, sessionId, tabId) {
|
|
8
8
|
this.events = [];
|
|
9
9
|
this.stopRecording = undefined;
|
|
10
10
|
this.saveInterval = null;
|
|
11
11
|
this.isSaving = false;
|
|
12
12
|
this.isConfigured = false;
|
|
13
|
+
this._enabled = true;
|
|
14
|
+
this.userAttributes = {};
|
|
15
|
+
this._listenersAttached = false;
|
|
16
|
+
this.handleUnload = () => this.save(true);
|
|
17
|
+
this.handleVisibilityChange = () => { if (document.visibilityState === "hidden")
|
|
18
|
+
this.save(); };
|
|
13
19
|
this.sessionId = sessionId;
|
|
20
|
+
this.tabId = tabId;
|
|
14
21
|
this.config = config;
|
|
15
|
-
this.
|
|
16
|
-
// Bind methods
|
|
17
|
-
this.save = this.save.bind(this);
|
|
18
|
-
this.configure = this.configure.bind(this);
|
|
19
|
-
this.handleUnload = this.handleUnload.bind(this);
|
|
20
|
-
this.handleVisibilityChange = this.handleVisibilityChange.bind(this);
|
|
21
|
-
// Setup event listeners
|
|
22
|
-
this.setupEventListeners();
|
|
22
|
+
this.setupListeners();
|
|
23
23
|
}
|
|
24
|
-
|
|
24
|
+
setupListeners() {
|
|
25
|
+
if (typeof window === "undefined" || this._listenersAttached)
|
|
26
|
+
return;
|
|
27
|
+
this._listenersAttached = true;
|
|
25
28
|
window.addEventListener("beforeunload", this.handleUnload);
|
|
26
|
-
document.addEventListener("visibilitychange", this.handleVisibilityChange);
|
|
27
29
|
window.addEventListener("pagehide", this.handleUnload);
|
|
30
|
+
document.addEventListener("visibilitychange", this.handleVisibilityChange);
|
|
28
31
|
}
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
removeListeners() {
|
|
33
|
+
if (typeof window === "undefined" || !this._listenersAttached)
|
|
34
|
+
return;
|
|
35
|
+
this._listenersAttached = false;
|
|
36
|
+
window.removeEventListener("beforeunload", this.handleUnload);
|
|
37
|
+
window.removeEventListener("pagehide", this.handleUnload);
|
|
38
|
+
document.removeEventListener("visibilitychange", this.handleVisibilityChange);
|
|
31
39
|
}
|
|
32
|
-
|
|
33
|
-
if (
|
|
34
|
-
|
|
40
|
+
trimEvents() {
|
|
41
|
+
if (this.events.length <= MAX_RETRY_EVENTS)
|
|
42
|
+
return;
|
|
43
|
+
if (this.config.debug) {
|
|
44
|
+
console.warn(`Event queue exceeded ${MAX_RETRY_EVENTS}, dropping middle events (preserving snapshots)`);
|
|
35
45
|
}
|
|
46
|
+
// rrweb EventType.FullSnapshot (type 2) — required for replay playback
|
|
47
|
+
const fullSnapshots = this.events.filter((e) => e.type === 2);
|
|
48
|
+
const otherEvents = this.events.filter((e) => e.type !== 2);
|
|
49
|
+
const remainingSlots = Math.max(0, MAX_RETRY_EVENTS - fullSnapshots.length);
|
|
50
|
+
this.events = [...fullSnapshots, ...otherEvents.slice(-remainingSlots)];
|
|
51
|
+
this.events.sort((a, b) => a.timestamp - b.timestamp);
|
|
36
52
|
}
|
|
37
53
|
configure() {
|
|
38
|
-
if (
|
|
39
|
-
|
|
54
|
+
if (typeof window === "undefined")
|
|
55
|
+
return;
|
|
56
|
+
if (this.isConfigured)
|
|
57
|
+
return;
|
|
58
|
+
this.setupListeners();
|
|
59
|
+
const rate = Math.max(0, Math.min(1, this.config.replaySampleRate ?? 1));
|
|
60
|
+
if (Math.random() >= rate) {
|
|
61
|
+
this._enabled = false;
|
|
62
|
+
if (this.config.debug)
|
|
63
|
+
console.log("MonoscopeReplay: sampled out");
|
|
40
64
|
return;
|
|
41
65
|
}
|
|
42
66
|
try {
|
|
43
67
|
this.stopRecording = rrweb.record({
|
|
44
68
|
emit: (event) => {
|
|
69
|
+
if (!this._enabled)
|
|
70
|
+
return;
|
|
45
71
|
this.events.push(event);
|
|
46
|
-
// Auto-save when batch size reached
|
|
47
72
|
if (this.events.length >= MAX_EVENT_BATCH) {
|
|
48
73
|
this.save();
|
|
49
74
|
}
|
|
@@ -56,8 +81,7 @@ export class MonoscopeReplay {
|
|
|
56
81
|
tel: true,
|
|
57
82
|
},
|
|
58
83
|
maskTextClass: "rr-mask",
|
|
59
|
-
|
|
60
|
-
checkoutEveryNms: 15 * 1000, // Full snapshot every 15s
|
|
84
|
+
checkoutEveryNms: 15 * 1000,
|
|
61
85
|
sampling: {
|
|
62
86
|
mouseInteraction: {
|
|
63
87
|
MouseUp: false,
|
|
@@ -71,9 +95,9 @@ export class MonoscopeReplay {
|
|
|
71
95
|
TouchEnd: false,
|
|
72
96
|
},
|
|
73
97
|
mousemove: true,
|
|
74
|
-
scroll: 150,
|
|
98
|
+
scroll: 150,
|
|
75
99
|
media: 800,
|
|
76
|
-
input: "last",
|
|
100
|
+
input: "last",
|
|
77
101
|
},
|
|
78
102
|
plugins: [
|
|
79
103
|
getRecordConsolePlugin({
|
|
@@ -82,100 +106,81 @@ export class MonoscopeReplay {
|
|
|
82
106
|
stringifyOptions: {
|
|
83
107
|
stringLengthLimit: 1000,
|
|
84
108
|
numOfKeysLimit: 100,
|
|
85
|
-
depthOfLimit: 2,
|
|
109
|
+
depthOfLimit: 2,
|
|
86
110
|
},
|
|
87
111
|
}),
|
|
88
112
|
],
|
|
89
113
|
});
|
|
90
|
-
this.saveInterval = setInterval(() =>
|
|
91
|
-
this.save();
|
|
92
|
-
}, SAVE_INTERVAL);
|
|
114
|
+
this.saveInterval = setInterval(() => this.save(), SAVE_INTERVAL);
|
|
93
115
|
this.isConfigured = true;
|
|
94
|
-
|
|
116
|
+
if (this.config.debug) {
|
|
117
|
+
console.log("MonoscopeReplay configured successfully");
|
|
118
|
+
}
|
|
95
119
|
}
|
|
96
120
|
catch (error) {
|
|
97
|
-
console.
|
|
98
|
-
throw error;
|
|
121
|
+
console.warn("Monoscope: failed to configure replay", error);
|
|
99
122
|
}
|
|
100
123
|
}
|
|
101
124
|
async save(forceSynchronous = false) {
|
|
102
|
-
if (this.isSaving && !forceSynchronous)
|
|
125
|
+
if (this.isSaving && !forceSynchronous)
|
|
103
126
|
return;
|
|
104
|
-
|
|
105
|
-
if (this.events.length === 0) {
|
|
127
|
+
if (this.events.length === 0)
|
|
106
128
|
return;
|
|
107
|
-
|
|
108
|
-
if (this.events.length > MAX_RETRY_EVENTS) {
|
|
109
|
-
console.warn(`Event queue exceeded ${MAX_RETRY_EVENTS}, dropping middle events (preserving snapshots)`);
|
|
110
|
-
// Find full snapshot events (type 2) - these are critical for replay
|
|
111
|
-
const fullSnapshots = this.events.filter((e) => e.type === 2);
|
|
112
|
-
const otherEvents = this.events.filter((e) => e.type !== 2);
|
|
113
|
-
// Keep all snapshots and the most recent other events
|
|
114
|
-
const remainingSlots = MAX_RETRY_EVENTS - fullSnapshots.length;
|
|
115
|
-
this.events = [...fullSnapshots, ...otherEvents.slice(-remainingSlots)];
|
|
116
|
-
// Re-sort by timestamp to maintain order
|
|
117
|
-
this.events.sort((a, b) => a.timestamp - b.timestamp);
|
|
118
|
-
}
|
|
129
|
+
this.trimEvents();
|
|
119
130
|
this.isSaving = true;
|
|
120
|
-
const {
|
|
121
|
-
// Construct base URL
|
|
122
|
-
let baseUrl = replayEventsBaseUrl || "https://app.monoscope.tech";
|
|
123
|
-
baseUrl = `${baseUrl}/rrweb/${projectId}`;
|
|
124
|
-
// Get events to send and clear buffer
|
|
131
|
+
const baseUrl = `${this.config.replayEventsBaseUrl || "https://app.monoscope.tech"}/rrweb/${this.config.projectId}`;
|
|
125
132
|
const eventsToSend = [...this.events];
|
|
126
133
|
this.events = [];
|
|
127
134
|
const payload = {
|
|
128
135
|
events: eventsToSend,
|
|
129
136
|
sessionId: this.sessionId,
|
|
137
|
+
tabId: this.tabId,
|
|
130
138
|
timestamp: new Date().toISOString(),
|
|
131
139
|
eventCount: eventsToSend.length,
|
|
140
|
+
user: Object.keys(this.userAttributes).length > 0 ? this.userAttributes : undefined,
|
|
132
141
|
};
|
|
133
142
|
try {
|
|
134
|
-
if (forceSynchronous && navigator.sendBeacon) {
|
|
143
|
+
if (forceSynchronous && typeof navigator !== "undefined" && navigator.sendBeacon) {
|
|
135
144
|
const blob = new Blob([JSON.stringify(payload)], {
|
|
136
145
|
type: "application/json",
|
|
137
146
|
});
|
|
138
147
|
const sent = navigator.sendBeacon(baseUrl, blob);
|
|
139
148
|
if (!sent) {
|
|
140
|
-
|
|
149
|
+
fetch(baseUrl, {
|
|
150
|
+
method: "POST",
|
|
151
|
+
headers: { "Content-Type": "application/json" },
|
|
152
|
+
body: JSON.stringify(payload),
|
|
153
|
+
keepalive: true,
|
|
154
|
+
}).catch(() => { });
|
|
141
155
|
}
|
|
142
156
|
}
|
|
143
157
|
else {
|
|
144
|
-
// Use keepalive so the request survives page navigation,
|
|
145
|
-
// but only when payload fits under the 64KB keepalive limit
|
|
146
158
|
const body = JSON.stringify(payload);
|
|
147
159
|
const response = await fetch(baseUrl, {
|
|
148
160
|
method: "POST",
|
|
149
|
-
headers: {
|
|
150
|
-
"Content-Type": "application/json",
|
|
151
|
-
},
|
|
161
|
+
headers: { "Content-Type": "application/json" },
|
|
152
162
|
body,
|
|
153
163
|
keepalive: body.length < 63000,
|
|
154
164
|
});
|
|
155
165
|
if (!response.ok) {
|
|
156
166
|
throw new Error(`Failed to save replay events: ${response.status} ${response.statusText}`);
|
|
157
167
|
}
|
|
158
|
-
|
|
168
|
+
if (this.config.debug) {
|
|
169
|
+
console.log(`Successfully saved ${eventsToSend.length} replay events`);
|
|
170
|
+
}
|
|
159
171
|
}
|
|
160
172
|
}
|
|
161
173
|
catch (error) {
|
|
162
|
-
console.
|
|
174
|
+
console.warn("Monoscope: failed to save replay events:", error);
|
|
163
175
|
this.events = [...eventsToSend, ...this.events];
|
|
164
|
-
|
|
165
|
-
// Preserve full snapshots when trimming
|
|
166
|
-
const fullSnapshots = this.events.filter((e) => e.type === 2);
|
|
167
|
-
const otherEvents = this.events.filter((e) => e.type !== 2);
|
|
168
|
-
const remainingSlots = MAX_RETRY_EVENTS - fullSnapshots.length;
|
|
169
|
-
this.events = [...fullSnapshots, ...otherEvents.slice(-remainingSlots)];
|
|
170
|
-
this.events.sort((a, b) => a.timestamp - b.timestamp);
|
|
171
|
-
}
|
|
176
|
+
this.trimEvents();
|
|
172
177
|
}
|
|
173
178
|
finally {
|
|
174
179
|
this.isSaving = false;
|
|
175
180
|
}
|
|
176
181
|
}
|
|
177
182
|
stop() {
|
|
178
|
-
this.save(true);
|
|
183
|
+
this.save(true).catch(() => { });
|
|
179
184
|
if (this.stopRecording) {
|
|
180
185
|
this.stopRecording();
|
|
181
186
|
this.stopRecording = undefined;
|
|
@@ -184,19 +189,30 @@ export class MonoscopeReplay {
|
|
|
184
189
|
clearInterval(this.saveInterval);
|
|
185
190
|
this.saveInterval = null;
|
|
186
191
|
}
|
|
187
|
-
|
|
188
|
-
window.removeEventListener("pagehide", this.handleUnload);
|
|
189
|
-
document.removeEventListener("visibilitychange", this.handleVisibilityChange);
|
|
192
|
+
this.removeListeners();
|
|
190
193
|
this.isConfigured = false;
|
|
191
|
-
|
|
194
|
+
if (this.config.debug) {
|
|
195
|
+
console.log("MonoscopeReplay stopped");
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
setEnabled(enabled) {
|
|
199
|
+
this._enabled = enabled;
|
|
200
|
+
if (!enabled)
|
|
201
|
+
this.stop();
|
|
202
|
+
}
|
|
203
|
+
setUser(user) {
|
|
204
|
+
this.userAttributes = { ...this.userAttributes, ...user };
|
|
192
205
|
}
|
|
193
206
|
getEventCount() {
|
|
194
207
|
return this.events.length;
|
|
195
208
|
}
|
|
209
|
+
updateSessionId(sessionId) {
|
|
210
|
+
this.sessionId = sessionId;
|
|
211
|
+
}
|
|
196
212
|
getSessionId() {
|
|
197
213
|
return this.sessionId;
|
|
198
214
|
}
|
|
199
215
|
isRecording() {
|
|
200
|
-
return this.isConfigured && this.stopRecording !==
|
|
216
|
+
return this.isConfigured && this.stopRecording !== undefined;
|
|
201
217
|
}
|
|
202
218
|
}
|
package/dist/router.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
type EmitFn = (name: string, attrs: Record<string, string | number>) => void;
|
|
2
|
+
export declare class SPARouter {
|
|
3
|
+
private emit;
|
|
4
|
+
private currentUrl;
|
|
5
|
+
private _active;
|
|
6
|
+
private origPushState;
|
|
7
|
+
private origReplaceState;
|
|
8
|
+
private popstateHandler;
|
|
9
|
+
constructor(emit: EmitFn);
|
|
10
|
+
start(): void;
|
|
11
|
+
stop(): void;
|
|
12
|
+
}
|
|
13
|
+
export {};
|
package/dist/router.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { addBreadcrumb } from "./breadcrumbs";
|
|
2
|
+
export class SPARouter {
|
|
3
|
+
constructor(emit) {
|
|
4
|
+
this.currentUrl = "";
|
|
5
|
+
this._active = false;
|
|
6
|
+
this.origPushState = null;
|
|
7
|
+
this.origReplaceState = null;
|
|
8
|
+
this.popstateHandler = null;
|
|
9
|
+
this.emit = emit;
|
|
10
|
+
}
|
|
11
|
+
start() {
|
|
12
|
+
if (typeof window === "undefined" || this._active)
|
|
13
|
+
return;
|
|
14
|
+
this._active = true;
|
|
15
|
+
this.currentUrl = location.href;
|
|
16
|
+
this.origPushState = history.pushState.bind(history);
|
|
17
|
+
this.origReplaceState = history.replaceState.bind(history);
|
|
18
|
+
const onNav = (method) => {
|
|
19
|
+
try {
|
|
20
|
+
const from = this.currentUrl;
|
|
21
|
+
const to = location.href;
|
|
22
|
+
if (from === to)
|
|
23
|
+
return;
|
|
24
|
+
this.currentUrl = to;
|
|
25
|
+
addBreadcrumb({ type: "navigation", message: `${from} → ${to}`, data: { method } });
|
|
26
|
+
this.emit("navigation", {
|
|
27
|
+
"navigation.from": from,
|
|
28
|
+
"navigation.to": to,
|
|
29
|
+
"navigation.method": method,
|
|
30
|
+
"page.title": document.title,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
try {
|
|
35
|
+
console.warn("Monoscope: error in navigation tracking", e);
|
|
36
|
+
}
|
|
37
|
+
catch { /* must never throw */ }
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
history.pushState = (...args) => {
|
|
41
|
+
this.origPushState(...args);
|
|
42
|
+
onNav("pushState");
|
|
43
|
+
};
|
|
44
|
+
history.replaceState = (...args) => {
|
|
45
|
+
this.origReplaceState(...args);
|
|
46
|
+
onNav("replaceState");
|
|
47
|
+
};
|
|
48
|
+
this.popstateHandler = () => onNav("popstate");
|
|
49
|
+
window.addEventListener("popstate", this.popstateHandler);
|
|
50
|
+
}
|
|
51
|
+
stop() {
|
|
52
|
+
if (typeof window === "undefined" || !this._active)
|
|
53
|
+
return;
|
|
54
|
+
this._active = false;
|
|
55
|
+
if (this.origPushState)
|
|
56
|
+
history.pushState = this.origPushState;
|
|
57
|
+
if (this.origReplaceState)
|
|
58
|
+
history.replaceState = this.origReplaceState;
|
|
59
|
+
if (this.popstateHandler)
|
|
60
|
+
window.removeEventListener("popstate", this.popstateHandler);
|
|
61
|
+
this.origPushState = null;
|
|
62
|
+
this.origReplaceState = null;
|
|
63
|
+
this.popstateHandler = null;
|
|
64
|
+
}
|
|
65
|
+
}
|
package/dist/tracing.d.ts
CHANGED
|
@@ -1,12 +1,32 @@
|
|
|
1
1
|
import { MonoscopeConfig, MonoscopeUser } from "./types";
|
|
2
|
+
import { Span } from "@opentelemetry/api";
|
|
2
3
|
export declare class OpenTelemetryManager {
|
|
3
4
|
private config;
|
|
4
5
|
private sessionId;
|
|
6
|
+
private tabId;
|
|
5
7
|
private provider;
|
|
6
|
-
|
|
8
|
+
private longTaskObserver;
|
|
9
|
+
private resourceObserver;
|
|
10
|
+
private _enabled;
|
|
11
|
+
private _configured;
|
|
12
|
+
private pageSpan;
|
|
13
|
+
private pageContext;
|
|
14
|
+
private endPageSpanHandler;
|
|
15
|
+
constructor(config: MonoscopeConfig, sessionId: string, tabId: string);
|
|
7
16
|
private createProvider;
|
|
17
|
+
private commonAttrs;
|
|
18
|
+
private applyCommonAttrs;
|
|
8
19
|
configure(): void;
|
|
9
|
-
private
|
|
20
|
+
private startPageSpan;
|
|
21
|
+
getPageContext(): import("@opentelemetry/api").Context | null;
|
|
22
|
+
private withPageContext;
|
|
23
|
+
emitSpan(name: string, attrs: Record<string, string | number | boolean>, configure?: (span: Span) => void): void;
|
|
24
|
+
private observeLongTasks;
|
|
25
|
+
private observeResourceTiming;
|
|
26
|
+
startSpan<T>(name: string, fn: (span: Span) => T): T;
|
|
27
|
+
recordEvent(name: string, attributes?: Record<string, string | number | boolean>): void;
|
|
28
|
+
updateSessionId(sessionId: string): void;
|
|
29
|
+
setEnabled(enabled: boolean): void;
|
|
10
30
|
shutdown(): Promise<void>;
|
|
11
31
|
setUser(newConfig: MonoscopeUser): void;
|
|
12
32
|
}
|