@monoscopetech/browser 0.7.1 → 0.8.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 +93 -7
- package/dist/index.d.ts +6 -0
- package/dist/index.js +53 -3
- 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/tracing.d.ts +4 -0
- package/dist/tracing.js +44 -6
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +5 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The **Monoscope Browser SDK** is a lightweight JavaScript library for adding **session replay**, **performance tracing**, **error tracking**, and **web vitals** to your web applications.
|
|
4
4
|
|
|
5
|
-
When used together with the [Monoscope Server SDKs](https://
|
|
5
|
+
When used together with the [Monoscope Server SDKs](https://monoscope.tech/docs/sdks/), you gain **end-to-end observability** — seamlessly connecting user interactions in the browser to backend services, APIs, and databases.
|
|
6
6
|
|
|
7
7
|
This means you can:
|
|
8
8
|
|
|
@@ -32,14 +32,13 @@ Or include it directly in your HTML using a `<script>` tag:
|
|
|
32
32
|
|
|
33
33
|
## Quick Start
|
|
34
34
|
|
|
35
|
-
Initialize Monoscope with your **
|
|
35
|
+
Initialize Monoscope with your **API key** (found in your project settings):
|
|
36
36
|
|
|
37
37
|
```javascript
|
|
38
38
|
import Monoscope from "@monoscopetech/browser";
|
|
39
39
|
|
|
40
40
|
const monoscope = new Monoscope({
|
|
41
|
-
|
|
42
|
-
serviceName: "my-web-app",
|
|
41
|
+
apiKey: "YOUR_API_KEY",
|
|
43
42
|
});
|
|
44
43
|
|
|
45
44
|
// Identify the current user
|
|
@@ -49,6 +48,13 @@ monoscope.setUser({
|
|
|
49
48
|
});
|
|
50
49
|
```
|
|
51
50
|
|
|
51
|
+
On `localhost`, debug mode is auto-enabled — you'll see a status overlay and console diagnostics. Call `monoscope.test()` to verify your setup:
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
54
|
+
const result = await monoscope.test();
|
|
55
|
+
console.log(result); // { success: true, message: "Test span sent successfully." }
|
|
56
|
+
```
|
|
57
|
+
|
|
52
58
|
---
|
|
53
59
|
|
|
54
60
|
## Configuration
|
|
@@ -57,8 +63,9 @@ The `Monoscope` constructor accepts the following options:
|
|
|
57
63
|
|
|
58
64
|
| Name | Type | Description |
|
|
59
65
|
| --- | --- | --- |
|
|
60
|
-
| `
|
|
61
|
-
| `serviceName` | `string` |
|
|
66
|
+
| `apiKey` | `string` | **Required** – Your Monoscope API key (found in project settings). |
|
|
67
|
+
| `serviceName` | `string` | Service name. Defaults to `location.hostname`. |
|
|
68
|
+
| `projectId` | `string` | Deprecated alias for `apiKey`. |
|
|
62
69
|
| `exporterEndpoint` | `string` | Endpoint for exporting traces. Defaults to Monoscope's ingest endpoint. |
|
|
63
70
|
| `propagateTraceHeaderCorsUrls` | `RegExp[]` | URL patterns where trace context headers should be propagated. Defaults to same-origin only. |
|
|
64
71
|
| `resourceAttributes` | `Record<string, string>` | Additional OpenTelemetry resource attributes. |
|
|
@@ -71,6 +78,7 @@ The `Monoscope` constructor accepts the following options:
|
|
|
71
78
|
| `replaySampleRate` | `number` | Replay sampling rate from `0` to `1`. Default `1` (100%). |
|
|
72
79
|
| `enabled` | `boolean` | Whether to start collecting data immediately. Default `true`. |
|
|
73
80
|
| `resourceTimingThresholdMs` | `number` | Minimum resource duration (ms) to report. Default `200`. |
|
|
81
|
+
| `enableUserInteraction` | `boolean` | Trace user clicks and interactions, linking them to downstream network calls. Default `false`. |
|
|
74
82
|
|
|
75
83
|
---
|
|
76
84
|
|
|
@@ -133,6 +141,15 @@ Returns the current session ID.
|
|
|
133
141
|
|
|
134
142
|
Returns the current tab ID (unique per browser tab).
|
|
135
143
|
|
|
144
|
+
### `test(): Promise<{success: boolean, message: string}>`
|
|
145
|
+
|
|
146
|
+
Sends a test span and flushes it to verify the connection is working.
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
const result = await monoscope.test();
|
|
150
|
+
console.log(result.success); // true if the span was accepted
|
|
151
|
+
```
|
|
152
|
+
|
|
136
153
|
### `enable()` / `disable()`
|
|
137
154
|
|
|
138
155
|
Dynamically enable or disable all data collection.
|
|
@@ -166,7 +183,7 @@ import { MonoscopeProvider, useMonoscope, useMonoscopeUser, MonoscopeErrorBounda
|
|
|
166
183
|
// Wrap your app with MonoscopeProvider
|
|
167
184
|
function App() {
|
|
168
185
|
return (
|
|
169
|
-
<MonoscopeProvider config={{
|
|
186
|
+
<MonoscopeProvider config={{ apiKey: "YOUR_API_KEY" }}>
|
|
170
187
|
<MonoscopeErrorBoundary fallback={<div>Something went wrong</div>}>
|
|
171
188
|
<MyApp />
|
|
172
189
|
</MonoscopeErrorBoundary>
|
|
@@ -198,6 +215,75 @@ function MyApp() {
|
|
|
198
215
|
|
|
199
216
|
---
|
|
200
217
|
|
|
218
|
+
## Custom Instrumentation
|
|
219
|
+
|
|
220
|
+
### Custom Spans
|
|
221
|
+
|
|
222
|
+
Use `startSpan()` to instrument specific operations with timing and attributes. It supports both sync and async functions — the span is automatically ended when the function returns or the promise resolves.
|
|
223
|
+
|
|
224
|
+
```javascript
|
|
225
|
+
// Sync
|
|
226
|
+
monoscope.startSpan("parse-config", (span) => {
|
|
227
|
+
span.setAttribute("config.size", rawConfig.length);
|
|
228
|
+
return parseConfig(rawConfig);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// Async
|
|
232
|
+
const data = await monoscope.startSpan("fetch-dashboard", async (span) => {
|
|
233
|
+
span.setAttribute("dashboard.id", dashboardId);
|
|
234
|
+
const res = await fetch(`/api/dashboards/${dashboardId}`);
|
|
235
|
+
span.setAttribute("http.status", res.status);
|
|
236
|
+
return res.json();
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Custom Events
|
|
241
|
+
|
|
242
|
+
Use `recordEvent()` to track discrete events without wrapping a code block:
|
|
243
|
+
|
|
244
|
+
```javascript
|
|
245
|
+
monoscope.recordEvent("feature_flag_evaluated", {
|
|
246
|
+
"flag.name": "new-checkout",
|
|
247
|
+
"flag.value": true,
|
|
248
|
+
});
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### React Components
|
|
252
|
+
|
|
253
|
+
Use the `useMonoscope()` hook to instrument React components:
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
import { useMonoscope } from "@monoscopetech/browser/react";
|
|
257
|
+
|
|
258
|
+
function CheckoutButton() {
|
|
259
|
+
const monoscope = useMonoscope();
|
|
260
|
+
|
|
261
|
+
const handleClick = () => {
|
|
262
|
+
monoscope?.startSpan("checkout.submit", async (span) => {
|
|
263
|
+
span.setAttribute("cart.items", cartItems.length);
|
|
264
|
+
await submitOrder();
|
|
265
|
+
});
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
return <button onClick={handleClick}>Checkout</button>;
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Additional OpenTelemetry Instrumentations
|
|
273
|
+
|
|
274
|
+
Pass extra OTel instrumentations via the `instrumentations` config to extend tracing beyond the built-in set:
|
|
275
|
+
|
|
276
|
+
```javascript
|
|
277
|
+
import { LongTaskInstrumentation } from "@opentelemetry/instrumentation-long-task";
|
|
278
|
+
|
|
279
|
+
const monoscope = new Monoscope({
|
|
280
|
+
apiKey: "YOUR_API_KEY",
|
|
281
|
+
instrumentations: [new LongTaskInstrumentation()],
|
|
282
|
+
});
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
201
287
|
## Features
|
|
202
288
|
|
|
203
289
|
### Session Replay
|
package/dist/index.d.ts
CHANGED
|
@@ -13,7 +13,9 @@ declare class Monoscope {
|
|
|
13
13
|
private vitals;
|
|
14
14
|
private router;
|
|
15
15
|
private _enabled;
|
|
16
|
+
private overlay;
|
|
16
17
|
constructor(config: MonoscopeConfig);
|
|
18
|
+
private logInitBanner;
|
|
17
19
|
private resolveSessionId;
|
|
18
20
|
private persistActivity;
|
|
19
21
|
private rotateSession;
|
|
@@ -24,6 +26,10 @@ declare class Monoscope {
|
|
|
24
26
|
setUser(u: MonoscopeUser): void;
|
|
25
27
|
startSpan<T>(name: string, fn: (span: Span) => T): T;
|
|
26
28
|
recordEvent(name: string, attributes?: Record<string, string | number | boolean>): void;
|
|
29
|
+
test(): Promise<{
|
|
30
|
+
success: boolean;
|
|
31
|
+
message: string;
|
|
32
|
+
}>;
|
|
27
33
|
disable(): void;
|
|
28
34
|
enable(): void;
|
|
29
35
|
isEnabled(): boolean;
|
package/dist/index.js
CHANGED
|
@@ -4,15 +4,25 @@ import { ErrorTracker } from "./errors";
|
|
|
4
4
|
import { WebVitalsCollector } from "./web-vitals";
|
|
5
5
|
import { SPARouter } from "./router";
|
|
6
6
|
import { addBreadcrumb, clearBreadcrumbs } from "./breadcrumbs";
|
|
7
|
+
import { DevOverlay } from "./overlay";
|
|
7
8
|
const SESSION_TIMEOUT_MS = 30 * 60 * 1000;
|
|
8
9
|
const LAST_ACTIVITY_KEY = "monoscope-last-activity";
|
|
9
10
|
const isBrowser = typeof window !== "undefined";
|
|
10
11
|
class Monoscope {
|
|
11
12
|
constructor(config) {
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
this.overlay = null;
|
|
14
|
+
const resolvedKey = config.apiKey || config.projectId;
|
|
15
|
+
if (!resolvedKey)
|
|
16
|
+
throw new Error("MonoscopeConfig must include apiKey (or projectId)");
|
|
17
|
+
if (config.projectId && !config.apiKey && config.debug) {
|
|
18
|
+
console.warn("[Monoscope] `projectId` is deprecated. Use `apiKey` instead.");
|
|
19
|
+
}
|
|
20
|
+
config = { ...config, apiKey: resolvedKey, projectId: resolvedKey };
|
|
21
|
+
const isLocalhost = isBrowser && (location.hostname === "localhost" || location.hostname === "127.0.0.1");
|
|
22
|
+
if (config.debug === undefined && isLocalhost)
|
|
23
|
+
config = { ...config, debug: true };
|
|
14
24
|
if (!config.serviceName)
|
|
15
|
-
|
|
25
|
+
config = { ...config, serviceName: isBrowser ? location.hostname : "unknown" };
|
|
16
26
|
this.config = config;
|
|
17
27
|
this._enabled = config.enabled !== false;
|
|
18
28
|
this.tabId = crypto.randomUUID();
|
|
@@ -36,9 +46,31 @@ class Monoscope {
|
|
|
36
46
|
});
|
|
37
47
|
this.router.start();
|
|
38
48
|
}
|
|
49
|
+
if (this._enabled && this.config.debug) {
|
|
50
|
+
this.logInitBanner();
|
|
51
|
+
if (isBrowser) {
|
|
52
|
+
this.overlay = new DevOverlay();
|
|
53
|
+
this.otel.onExportStatus = (ok) => this.overlay?.setConnectionStatus(ok);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
39
56
|
if (isBrowser)
|
|
40
57
|
this.setupActivityTracking();
|
|
41
58
|
}
|
|
59
|
+
logInitBanner() {
|
|
60
|
+
const c = this.config;
|
|
61
|
+
const endpoint = c.exporterEndpoint || "https://otelcol.monoscope.tech/v1/traces";
|
|
62
|
+
const samplePct = Math.round((c.sampleRate ?? 1) * 100);
|
|
63
|
+
const replayPct = Math.round((c.replaySampleRate ?? 1) * 100);
|
|
64
|
+
console.groupCollapsed("%c[Monoscope] ✓ Initialized", "color: #22c55e; font-weight: bold");
|
|
65
|
+
console.log(` API Key: ${c.apiKey}`);
|
|
66
|
+
console.log(` Service: ${c.serviceName}`);
|
|
67
|
+
console.log(` Session: ${this.sessionId}`);
|
|
68
|
+
console.log(` Tracing: ✓ (sample rate: ${samplePct}%)`);
|
|
69
|
+
console.log(` Replay: ✓ (sample rate: ${replayPct}%)`);
|
|
70
|
+
console.log(` Errors: ✓`);
|
|
71
|
+
console.log(` Endpoint: ${endpoint}`);
|
|
72
|
+
console.groupEnd();
|
|
73
|
+
}
|
|
42
74
|
resolveSessionId() {
|
|
43
75
|
try {
|
|
44
76
|
const storedId = sessionStorage.getItem("monoscope-session-id");
|
|
@@ -135,10 +167,28 @@ class Monoscope {
|
|
|
135
167
|
this.replay.setUser(u);
|
|
136
168
|
}
|
|
137
169
|
startSpan(name, fn) {
|
|
170
|
+
this.overlay?.incrementEvents();
|
|
138
171
|
return this.otel.startSpan(name, fn);
|
|
139
172
|
}
|
|
140
173
|
recordEvent(name, attributes) {
|
|
141
174
|
this.otel.recordEvent(name, attributes);
|
|
175
|
+
this.overlay?.incrementEvents();
|
|
176
|
+
}
|
|
177
|
+
async test() {
|
|
178
|
+
try {
|
|
179
|
+
this.otel.emitSpan("monoscope.test", { "test.timestamp": Date.now() });
|
|
180
|
+
await this.otel.forceFlush();
|
|
181
|
+
const msg = "Test span sent successfully.";
|
|
182
|
+
if (this.config.debug)
|
|
183
|
+
console.log(`[Monoscope] ${msg}`);
|
|
184
|
+
return { success: true, message: msg };
|
|
185
|
+
}
|
|
186
|
+
catch (e) {
|
|
187
|
+
const msg = `Test failed: ${e}`;
|
|
188
|
+
if (this.config.debug)
|
|
189
|
+
console.error(`[Monoscope] ${msg}`);
|
|
190
|
+
return { success: false, message: msg };
|
|
191
|
+
}
|
|
142
192
|
}
|
|
143
193
|
disable() {
|
|
144
194
|
this._enabled = false;
|