@monoscopetech/browser 0.7.2 → 0.9.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 +24 -9
- package/dist/errors.js +9 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.js +76 -12
- 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/overlay.d.ts +12 -0
- package/dist/overlay.js +63 -0
- package/dist/react.d.ts +7 -3
- package/dist/react.js +2 -1
- package/dist/replay.js +13 -4
- package/dist/router.d.ts +3 -3
- package/dist/router.js +3 -8
- package/dist/tracing.d.ts +37 -6
- package/dist/tracing.js +272 -38
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +6 -2
- package/dist/web-vitals.js +5 -0
- package/package.json +1 -1
package/dist/overlay.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const DISMISS_KEY = "monoscope-overlay-dismissed";
|
|
2
|
+
export class DevOverlay {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.el = null;
|
|
5
|
+
this.eventCount = 0;
|
|
6
|
+
this.statusDot = null;
|
|
7
|
+
this.countEl = null;
|
|
8
|
+
this.connEl = null;
|
|
9
|
+
if (typeof document === "undefined")
|
|
10
|
+
return;
|
|
11
|
+
try {
|
|
12
|
+
if (sessionStorage.getItem(DISMISS_KEY))
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
catch { }
|
|
16
|
+
this.mount();
|
|
17
|
+
}
|
|
18
|
+
mount() {
|
|
19
|
+
const el = document.createElement("div");
|
|
20
|
+
Object.assign(el.style, {
|
|
21
|
+
position: "fixed", bottom: "12px", right: "12px", zIndex: "2147483647",
|
|
22
|
+
background: "#1a1a2e", color: "#e0e0e0", fontFamily: "system-ui, sans-serif",
|
|
23
|
+
fontSize: "12px", padding: "6px 10px", borderRadius: "6px",
|
|
24
|
+
boxShadow: "0 2px 8px rgba(0,0,0,0.3)", display: "flex", alignItems: "center", gap: "6px",
|
|
25
|
+
cursor: "default", userSelect: "none",
|
|
26
|
+
});
|
|
27
|
+
this.statusDot = document.createElement("span");
|
|
28
|
+
Object.assign(this.statusDot.style, {
|
|
29
|
+
width: "7px", height: "7px", borderRadius: "50%", background: "#22c55e", display: "inline-block",
|
|
30
|
+
});
|
|
31
|
+
this.countEl = document.createElement("span");
|
|
32
|
+
this.countEl.textContent = "0";
|
|
33
|
+
this.countEl.style.opacity = "0.7";
|
|
34
|
+
this.connEl = document.createElement("span");
|
|
35
|
+
this.connEl.style.opacity = "0.7";
|
|
36
|
+
const close = document.createElement("span");
|
|
37
|
+
close.textContent = "\u00d7";
|
|
38
|
+
Object.assign(close.style, { cursor: "pointer", marginLeft: "4px", opacity: "0.6", fontSize: "14px" });
|
|
39
|
+
close.onclick = () => this.dismiss();
|
|
40
|
+
el.append(this.statusDot, " Monoscope ", this.countEl, " ", this.connEl, close);
|
|
41
|
+
this.el = el;
|
|
42
|
+
(document.body || document.documentElement).appendChild(el);
|
|
43
|
+
}
|
|
44
|
+
incrementEvents() {
|
|
45
|
+
this.eventCount++;
|
|
46
|
+
if (this.countEl)
|
|
47
|
+
this.countEl.textContent = String(this.eventCount);
|
|
48
|
+
}
|
|
49
|
+
setConnectionStatus(ok) {
|
|
50
|
+
if (this.connEl)
|
|
51
|
+
this.connEl.textContent = ok ? "Connected" : "Connection failed";
|
|
52
|
+
if (this.statusDot)
|
|
53
|
+
this.statusDot.style.background = ok ? "#22c55e" : "#ef4444";
|
|
54
|
+
}
|
|
55
|
+
dismiss() {
|
|
56
|
+
this.el?.remove();
|
|
57
|
+
this.el = null;
|
|
58
|
+
try {
|
|
59
|
+
sessionStorage.setItem(DISMISS_KEY, "1");
|
|
60
|
+
}
|
|
61
|
+
catch { }
|
|
62
|
+
}
|
|
63
|
+
}
|
package/dist/react.d.ts
CHANGED
|
@@ -2,10 +2,14 @@ import { Component } from "react";
|
|
|
2
2
|
import type { ReactNode, ErrorInfo } from "react";
|
|
3
3
|
import Monoscope from ".";
|
|
4
4
|
import type { MonoscopeConfig, MonoscopeUser } from "./types";
|
|
5
|
-
|
|
6
|
-
config: MonoscopeConfig;
|
|
5
|
+
type ProviderProps = {
|
|
7
6
|
children: ReactNode;
|
|
8
|
-
}
|
|
7
|
+
} & ({
|
|
8
|
+
config: MonoscopeConfig;
|
|
9
|
+
} | ({
|
|
10
|
+
config?: undefined;
|
|
11
|
+
} & MonoscopeConfig));
|
|
12
|
+
export declare function MonoscopeProvider({ children, ...rest }: ProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
9
13
|
export declare function useMonoscope(): Monoscope | null;
|
|
10
14
|
export declare function useMonoscopeUser(user: MonoscopeUser | null | undefined): void;
|
|
11
15
|
type ErrorBoundaryProps = {
|
package/dist/react.js
CHANGED
|
@@ -3,7 +3,8 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import { createContext, useContext, useRef, useEffect, Component } from "react";
|
|
4
4
|
import Monoscope from ".";
|
|
5
5
|
const MonoscopeContext = createContext(null);
|
|
6
|
-
export function MonoscopeProvider({
|
|
6
|
+
export function MonoscopeProvider({ children, ...rest }) {
|
|
7
|
+
const config = rest.config ?? rest;
|
|
7
8
|
const ref = useRef(null);
|
|
8
9
|
if (!ref.current && typeof window !== "undefined") {
|
|
9
10
|
ref.current = new Monoscope(config);
|
package/dist/replay.js
CHANGED
|
@@ -128,7 +128,8 @@ export class MonoscopeReplay {
|
|
|
128
128
|
return;
|
|
129
129
|
this.trimEvents();
|
|
130
130
|
this.isSaving = true;
|
|
131
|
-
const
|
|
131
|
+
const apiKey = this.config.apiKey || this.config.projectId || "";
|
|
132
|
+
const baseUrl = `${this.config.replayEventsBaseUrl || "https://app.monoscope.tech"}/api/v1/rrweb`;
|
|
132
133
|
const eventsToSend = [...this.events];
|
|
133
134
|
this.events = [];
|
|
134
135
|
const payload = {
|
|
@@ -139,6 +140,7 @@ export class MonoscopeReplay {
|
|
|
139
140
|
eventCount: eventsToSend.length,
|
|
140
141
|
user: Object.keys(this.userAttributes).length > 0 ? this.userAttributes : undefined,
|
|
141
142
|
};
|
|
143
|
+
const headers = { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey}` };
|
|
142
144
|
try {
|
|
143
145
|
if (forceSynchronous && typeof navigator !== "undefined" && navigator.sendBeacon) {
|
|
144
146
|
const blob = new Blob([JSON.stringify(payload)], {
|
|
@@ -148,7 +150,7 @@ export class MonoscopeReplay {
|
|
|
148
150
|
if (!sent) {
|
|
149
151
|
fetch(baseUrl, {
|
|
150
152
|
method: "POST",
|
|
151
|
-
headers
|
|
153
|
+
headers,
|
|
152
154
|
body: JSON.stringify(payload),
|
|
153
155
|
keepalive: true,
|
|
154
156
|
}).catch(() => { });
|
|
@@ -158,12 +160,19 @@ export class MonoscopeReplay {
|
|
|
158
160
|
const body = JSON.stringify(payload);
|
|
159
161
|
const response = await fetch(baseUrl, {
|
|
160
162
|
method: "POST",
|
|
161
|
-
headers
|
|
163
|
+
headers,
|
|
162
164
|
body,
|
|
163
165
|
keepalive: body.length < 63000,
|
|
164
166
|
});
|
|
165
167
|
if (!response.ok) {
|
|
166
|
-
|
|
168
|
+
const status = response.status;
|
|
169
|
+
if (status === 401 || status === 403) {
|
|
170
|
+
console.warn("[Monoscope] Replay upload authentication failed. Your apiKey may be invalid.");
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
console.warn(`[Monoscope] Replay upload failed (${status}). Check your apiKey and network.`);
|
|
174
|
+
}
|
|
175
|
+
throw new Error(`Failed to save replay events: ${status} ${response.statusText}`);
|
|
167
176
|
}
|
|
168
177
|
if (this.config.debug) {
|
|
169
178
|
console.log(`Successfully saved ${eventsToSend.length} replay events`);
|
package/dist/router.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
type
|
|
1
|
+
type NavFn = (from: string, to: string, method: string) => void;
|
|
2
2
|
export declare class SPARouter {
|
|
3
|
-
private
|
|
3
|
+
private onNavigation;
|
|
4
4
|
private currentUrl;
|
|
5
5
|
private _active;
|
|
6
6
|
private origPushState;
|
|
7
7
|
private origReplaceState;
|
|
8
8
|
private popstateHandler;
|
|
9
|
-
constructor(
|
|
9
|
+
constructor(onNavigation: NavFn);
|
|
10
10
|
start(): void;
|
|
11
11
|
stop(): void;
|
|
12
12
|
}
|
package/dist/router.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { addBreadcrumb } from "./breadcrumbs";
|
|
2
2
|
export class SPARouter {
|
|
3
|
-
constructor(
|
|
3
|
+
constructor(onNavigation) {
|
|
4
4
|
this.currentUrl = "";
|
|
5
5
|
this._active = false;
|
|
6
6
|
this.origPushState = null;
|
|
7
7
|
this.origReplaceState = null;
|
|
8
8
|
this.popstateHandler = null;
|
|
9
|
-
this.
|
|
9
|
+
this.onNavigation = onNavigation;
|
|
10
10
|
}
|
|
11
11
|
start() {
|
|
12
12
|
if (typeof window === "undefined" || this._active)
|
|
@@ -23,12 +23,7 @@ export class SPARouter {
|
|
|
23
23
|
return;
|
|
24
24
|
this.currentUrl = to;
|
|
25
25
|
addBreadcrumb({ type: "navigation", message: `${from} → ${to}`, data: { method } });
|
|
26
|
-
this.
|
|
27
|
-
"navigation.from": from,
|
|
28
|
-
"navigation.to": to,
|
|
29
|
-
"navigation.method": method,
|
|
30
|
-
"page.title": document.title,
|
|
31
|
-
});
|
|
26
|
+
this.onNavigation(from, to, method);
|
|
32
27
|
}
|
|
33
28
|
catch (e) {
|
|
34
29
|
try {
|
package/dist/tracing.d.ts
CHANGED
|
@@ -1,30 +1,61 @@
|
|
|
1
1
|
import { MonoscopeConfig, MonoscopeUser } from "./types";
|
|
2
2
|
import { Span } from "@opentelemetry/api";
|
|
3
|
+
export type MonoscopeKind = "page_load" | "navigation" | "interaction" | "network" | "resource" | "web_vital" | "error" | "long_task" | "custom";
|
|
4
|
+
export declare function shortPath(url: string): string;
|
|
5
|
+
export declare function describeElement(el: EventTarget | Element | null | undefined): string;
|
|
6
|
+
/**
|
|
7
|
+
* RFC4122 v4 id with a fallback for non-secure contexts (HTTP / file:// /
|
|
8
|
+
* older Safari/Edge) where `crypto.randomUUID` is undefined.
|
|
9
|
+
*/
|
|
10
|
+
export declare function newId(): string;
|
|
3
11
|
export declare class OpenTelemetryManager {
|
|
4
12
|
private config;
|
|
5
13
|
private sessionId;
|
|
6
14
|
private tabId;
|
|
15
|
+
private pageviewId;
|
|
7
16
|
private provider;
|
|
17
|
+
private processor;
|
|
8
18
|
private longTaskObserver;
|
|
9
19
|
private resourceObserver;
|
|
10
20
|
private _enabled;
|
|
11
21
|
private _configured;
|
|
12
|
-
private
|
|
13
|
-
private
|
|
14
|
-
private
|
|
22
|
+
private _firstExportLogged;
|
|
23
|
+
private routeSpan;
|
|
24
|
+
private routeContext;
|
|
25
|
+
private routeIdleTimer;
|
|
26
|
+
private flushOnHideHandler;
|
|
27
|
+
private visibilityHandler;
|
|
28
|
+
onExportStatus: ((ok: boolean) => void) | null;
|
|
29
|
+
onSpanStart: (() => void) | null;
|
|
15
30
|
constructor(config: MonoscopeConfig, sessionId: string, tabId: string);
|
|
16
31
|
private createProvider;
|
|
17
32
|
private commonAttrs;
|
|
18
33
|
private applyCommonAttrs;
|
|
19
34
|
configure(): void;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
35
|
+
getPageviewId(): string;
|
|
36
|
+
rotatePageview(): string;
|
|
37
|
+
/**
|
|
38
|
+
* Open a short-lived route.change root span for an SPA navigation. Closes
|
|
39
|
+
* any previous route span, rotates pageview.id, and publishes the span as
|
|
40
|
+
* the active context so async work started in the same Zone (fetch/XHR)
|
|
41
|
+
* inherits it as parent. Auto-closes after ROUTE_IDLE_MS or on next nav.
|
|
42
|
+
*/
|
|
43
|
+
startRouteChange(from: string, to: string, method: string): void;
|
|
44
|
+
endRouteChange(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Flush pending spans before the JS context is destroyed. Critical for
|
|
47
|
+
* MPAs where every navigation unloads the page, and still valuable for
|
|
48
|
+
* SPAs at tab close. pagehide is preferred over beforeunload (fires for
|
|
49
|
+
* bfcache eviction and mobile backgrounding; beforeunload does not).
|
|
50
|
+
*/
|
|
51
|
+
private installFlushOnHide;
|
|
52
|
+
private withActiveContext;
|
|
23
53
|
emitSpan(name: string, attrs: Record<string, string | number | boolean>, configure?: (span: Span) => void): void;
|
|
24
54
|
private observeLongTasks;
|
|
25
55
|
private observeResourceTiming;
|
|
26
56
|
startSpan<T>(name: string, fn: (span: Span) => T): T;
|
|
27
57
|
recordEvent(name: string, attributes?: Record<string, string | number | boolean>): void;
|
|
58
|
+
forceFlush(): Promise<void>;
|
|
28
59
|
updateSessionId(sessionId: string): void;
|
|
29
60
|
setEnabled(enabled: boolean): void;
|
|
30
61
|
shutdown(): Promise<void>;
|