@spanwise/rum 0.4.3 → 0.6.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 +403 -0
- package/dist/client.d.ts +9 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +19 -0
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/replay.d.ts +53 -0
- package/dist/replay.d.ts.map +1 -0
- package/dist/replay.js +201 -0
- package/dist/replay.js.map +1 -0
- package/package.json +4 -1
package/README.md
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
# @spanwise/rum
|
|
2
|
+
|
|
3
|
+
Real User Monitoring SDK for browser applications. Captures page views, clicks, errors, Web Vitals, and session replays.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @spanwise/rum
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @spanwise/rum
|
|
11
|
+
# or
|
|
12
|
+
yarn add @spanwise/rum
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { createSpanwiseBrowser } from "@spanwise/rum"
|
|
19
|
+
|
|
20
|
+
const spanwise = createSpanwiseBrowser({
|
|
21
|
+
apiKey: "sw_...",
|
|
22
|
+
serviceName: "my-app",
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
// That's it! Page views, clicks, errors, and vitals are tracked automatically.
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Configuration
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
const spanwise = createSpanwiseBrowser({
|
|
32
|
+
// Required
|
|
33
|
+
apiKey: "sw_...",
|
|
34
|
+
|
|
35
|
+
// Optional
|
|
36
|
+
serviceName: "my-app", // Service name for filtering
|
|
37
|
+
sampleRate: 1.0, // Sample 100% of sessions (default: 1.0)
|
|
38
|
+
trackPageViews: true, // Track page views (default: true)
|
|
39
|
+
trackClicks: true, // Track clicks (default: true)
|
|
40
|
+
trackVitals: true, // Track Web Vitals (default: true)
|
|
41
|
+
trackErrors: true, // Track errors (default: true)
|
|
42
|
+
trackResources: true, // Track fetch/XHR requests (default: true)
|
|
43
|
+
sessionStorage: "cookie", // "cookie" or "sessionStorage" (default: "cookie")
|
|
44
|
+
|
|
45
|
+
// Distributed Tracing
|
|
46
|
+
tracing: {
|
|
47
|
+
enabled: true, // Inject traceparent headers (default: true)
|
|
48
|
+
propagateToOrigins: [ // Origins to propagate trace context to
|
|
49
|
+
"https://api.example.com",
|
|
50
|
+
/\.example\.com$/,
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
// Session Replay (opt-in)
|
|
55
|
+
replay: {
|
|
56
|
+
enabled: true, // Enable replay recording (default: false)
|
|
57
|
+
sampleRate: 0.1, // Record 10% of sessions (default: 0.1)
|
|
58
|
+
onErrorSampleRate: 1.0, // Record 100% of sessions with errors (default: 1.0)
|
|
59
|
+
privacyMode: "strict", // Privacy mode (default: "strict")
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## API
|
|
65
|
+
|
|
66
|
+
### `spanwise.trackEvent(name, properties?)`
|
|
67
|
+
|
|
68
|
+
Track a custom event.
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
spanwise.trackEvent("purchase", {
|
|
72
|
+
productId: "123",
|
|
73
|
+
amount: 99.99,
|
|
74
|
+
})
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### `spanwise.trackPageView(url?)`
|
|
78
|
+
|
|
79
|
+
Manually track a page view. Called automatically on navigation.
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
spanwise.trackPageView("/checkout")
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### `spanwise.trackError(error, options?)`
|
|
86
|
+
|
|
87
|
+
Manually track an error.
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
try {
|
|
91
|
+
await riskyOperation()
|
|
92
|
+
} catch (error) {
|
|
93
|
+
spanwise.trackError(error, {
|
|
94
|
+
requestId: "req_123",
|
|
95
|
+
traceId: "trace_abc",
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### `spanwise.setUser(id, traits?)`
|
|
101
|
+
|
|
102
|
+
Identify the current user.
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
spanwise.setUser("user_123", {
|
|
106
|
+
email: "user@example.com",
|
|
107
|
+
plan: "pro",
|
|
108
|
+
})
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### `spanwise.getSessionId()`
|
|
112
|
+
|
|
113
|
+
Get the current session ID.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
const sessionId = spanwise.getSessionId()
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### `spanwise.startReplay()` / `spanwise.stopReplay()`
|
|
120
|
+
|
|
121
|
+
Manually control replay recording (overrides sampling).
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
// Start recording for VIP users regardless of sample rate
|
|
125
|
+
if (user.isVIP) {
|
|
126
|
+
spanwise.startReplay()
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Session Replay
|
|
133
|
+
|
|
134
|
+
Session Replay records user interactions as a video-like playback. It captures DOM mutations, mouse movements, clicks, and scrolls.
|
|
135
|
+
|
|
136
|
+
### Enabling Replay
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
const spanwise = createSpanwiseBrowser({
|
|
140
|
+
apiKey: "sw_...",
|
|
141
|
+
replay: {
|
|
142
|
+
enabled: true,
|
|
143
|
+
},
|
|
144
|
+
})
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Sampling
|
|
148
|
+
|
|
149
|
+
By default, only 10% of sessions are recorded to manage costs and storage:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
replay: {
|
|
153
|
+
enabled: true,
|
|
154
|
+
sampleRate: 0.1, // 10% of sessions
|
|
155
|
+
onErrorSampleRate: 1.0, // 100% if an error occurs
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
When an error occurs in a session that wasn't initially sampled, recording automatically starts to capture the error context.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Privacy & Data Protection
|
|
164
|
+
|
|
165
|
+
Session Replay is designed with privacy as the default. **No configuration is required for most applications** - sensitive data is automatically protected.
|
|
166
|
+
|
|
167
|
+
### Privacy Modes
|
|
168
|
+
|
|
169
|
+
| Mode | Text | Inputs | Use Case |
|
|
170
|
+
|------|------|--------|----------|
|
|
171
|
+
| `"strict"` (default) | Masked | Masked | Most applications |
|
|
172
|
+
| `"balanced"` | Visible | Masked | Marketing sites, blogs |
|
|
173
|
+
| `"permissive"` | Visible | Visible | Internal tools only |
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
replay: {
|
|
177
|
+
enabled: true,
|
|
178
|
+
privacyMode: "strict", // Default - masks everything
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### What Each Mode Does
|
|
183
|
+
|
|
184
|
+
#### Strict Mode (Default)
|
|
185
|
+
|
|
186
|
+
All text content is replaced with `****`. Form inputs show `•••••`. You see the page structure and user interactions, but no actual content.
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
┌─────────────────────────────┐
|
|
190
|
+
│ **** ******** │ ← Masked heading
|
|
191
|
+
│ │
|
|
192
|
+
│ ******** **** ** ******* │ ← Masked paragraph
|
|
193
|
+
│ ******* ** *** **** │
|
|
194
|
+
│ │
|
|
195
|
+
│ Email: [•••••••••••••] │ ← Masked input
|
|
196
|
+
│ Password: [••••••••] │
|
|
197
|
+
│ │
|
|
198
|
+
│ [**********] │ ← Masked button
|
|
199
|
+
└─────────────────────────────┘
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
#### Balanced Mode
|
|
203
|
+
|
|
204
|
+
Text is visible, but all form inputs are masked. Good for content-heavy sites.
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
┌─────────────────────────────┐
|
|
208
|
+
│ Welcome Back │ ← Visible heading
|
|
209
|
+
│ │
|
|
210
|
+
│ Please sign in to continue │ ← Visible text
|
|
211
|
+
│ │
|
|
212
|
+
│ Email: [•••••••••••••] │ ← Masked input
|
|
213
|
+
│ Password: [••••••••] │
|
|
214
|
+
│ │
|
|
215
|
+
│ [Sign In] │ ← Visible button
|
|
216
|
+
└─────────────────────────────┘
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
#### Permissive Mode
|
|
220
|
+
|
|
221
|
+
Everything is recorded as-is. **Only use for internal tools or with explicit user consent.**
|
|
222
|
+
|
|
223
|
+
### Fine-Grained Control with HTML Attributes
|
|
224
|
+
|
|
225
|
+
Regardless of privacy mode, you can control specific elements:
|
|
226
|
+
|
|
227
|
+
#### Block Elements Completely
|
|
228
|
+
|
|
229
|
+
Use `data-sw-block` to completely hide an element. It will appear as a gray placeholder in replays.
|
|
230
|
+
|
|
231
|
+
```html
|
|
232
|
+
<!-- Credit card form - never record -->
|
|
233
|
+
<div data-sw-block>
|
|
234
|
+
<input type="text" placeholder="Card number" />
|
|
235
|
+
<input type="text" placeholder="CVV" />
|
|
236
|
+
</div>
|
|
237
|
+
|
|
238
|
+
<!-- Sensitive user data -->
|
|
239
|
+
<div data-sw-block class="user-profile">
|
|
240
|
+
<p>SSN: 123-45-6789</p>
|
|
241
|
+
</div>
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
#### Mask Specific Elements
|
|
245
|
+
|
|
246
|
+
Use `data-sw-mask` to mask text while preserving structure (useful in balanced/permissive modes).
|
|
247
|
+
|
|
248
|
+
```html
|
|
249
|
+
<!-- Mask email in a visible section -->
|
|
250
|
+
<p>Contact: <span data-sw-mask>user@example.com</span></p>
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
#### Unmask Elements in Strict Mode
|
|
254
|
+
|
|
255
|
+
Use `data-sw-unmask` to show content even when using strict mode.
|
|
256
|
+
|
|
257
|
+
```html
|
|
258
|
+
<!-- Show navigation labels in strict mode -->
|
|
259
|
+
<nav data-sw-unmask>
|
|
260
|
+
<a href="/">Home</a>
|
|
261
|
+
<a href="/products">Products</a>
|
|
262
|
+
<a href="/about">About</a>
|
|
263
|
+
</nav>
|
|
264
|
+
|
|
265
|
+
<!-- Show static content -->
|
|
266
|
+
<footer data-sw-unmask>
|
|
267
|
+
<p>© 2024 My Company</p>
|
|
268
|
+
</footer>
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
#### rrweb Classes (Alternative)
|
|
272
|
+
|
|
273
|
+
The standard rrweb classes also work:
|
|
274
|
+
|
|
275
|
+
```html
|
|
276
|
+
<div class="rr-block">Hidden content</div>
|
|
277
|
+
<div class="rr-mask">Masked content</div>
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Best Practices
|
|
281
|
+
|
|
282
|
+
1. **Start with strict mode** - It's the safest default. Only relax if needed.
|
|
283
|
+
|
|
284
|
+
2. **Block sensitive sections entirely** - For areas with PII (profile pages, account settings), use `data-sw-block`.
|
|
285
|
+
|
|
286
|
+
3. **Review before production** - Test your app with replay enabled and verify no sensitive data appears.
|
|
287
|
+
|
|
288
|
+
4. **Consider user consent** - For GDPR compliance, inform users that sessions may be recorded.
|
|
289
|
+
|
|
290
|
+
5. **Use unmask sparingly** - Only unmask truly static, non-sensitive content like navigation labels.
|
|
291
|
+
|
|
292
|
+
### What's Automatically Protected
|
|
293
|
+
|
|
294
|
+
Even without configuration:
|
|
295
|
+
|
|
296
|
+
- All `<input>` values are masked (except in permissive mode)
|
|
297
|
+
- Password fields are always masked
|
|
298
|
+
- Credit card patterns are detected and masked
|
|
299
|
+
- `<script>` content is never recorded
|
|
300
|
+
- `<head>` metadata is stripped
|
|
301
|
+
|
|
302
|
+
### Sensitive Data Checklist
|
|
303
|
+
|
|
304
|
+
Before enabling replay, ensure these are blocked or masked:
|
|
305
|
+
|
|
306
|
+
- [ ] User profile information (name, email, phone)
|
|
307
|
+
- [ ] Payment forms and credit card inputs
|
|
308
|
+
- [ ] Social security numbers, government IDs
|
|
309
|
+
- [ ] Medical or health information
|
|
310
|
+
- [ ] Authentication tokens displayed on screen
|
|
311
|
+
- [ ] Private messages or chat content
|
|
312
|
+
- [ ] Financial account numbers
|
|
313
|
+
- [ ] Any PII in confirmation pages
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## Distributed Tracing
|
|
318
|
+
|
|
319
|
+
The SDK automatically injects `traceparent` headers into fetch/XHR requests, connecting frontend sessions to backend traces.
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
const spanwise = createSpanwiseBrowser({
|
|
323
|
+
apiKey: "sw_...",
|
|
324
|
+
tracing: {
|
|
325
|
+
enabled: true,
|
|
326
|
+
propagateToOrigins: [
|
|
327
|
+
"https://api.myapp.com",
|
|
328
|
+
/\.myapp\.com$/,
|
|
329
|
+
],
|
|
330
|
+
},
|
|
331
|
+
})
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Your backend will receive headers like:
|
|
335
|
+
|
|
336
|
+
```
|
|
337
|
+
traceparent: 00-{traceId}-{spanId}-01
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Browser Support
|
|
343
|
+
|
|
344
|
+
- Chrome 64+
|
|
345
|
+
- Firefox 67+
|
|
346
|
+
- Safari 12+
|
|
347
|
+
- Edge 79+
|
|
348
|
+
|
|
349
|
+
Session Replay requires:
|
|
350
|
+
- MutationObserver
|
|
351
|
+
- WeakMap
|
|
352
|
+
- requestAnimationFrame
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## Bundle Size
|
|
357
|
+
|
|
358
|
+
The base SDK is ~8KB gzipped. Session Replay adds ~40KB gzipped (rrweb).
|
|
359
|
+
|
|
360
|
+
Replay is dynamically imported only when enabled, so it won't affect your bundle if not used.
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## TypeScript
|
|
365
|
+
|
|
366
|
+
Full TypeScript support with exported types:
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
import {
|
|
370
|
+
createSpanwiseBrowser,
|
|
371
|
+
type BrowserConfig,
|
|
372
|
+
type SpanwiseBrowser,
|
|
373
|
+
type ReplayConfig,
|
|
374
|
+
type PrivacyMode,
|
|
375
|
+
type TracingConfig,
|
|
376
|
+
} from "@spanwise/rum"
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## Troubleshooting
|
|
382
|
+
|
|
383
|
+
### Replay not recording
|
|
384
|
+
|
|
385
|
+
1. Check that `replay.enabled` is `true`
|
|
386
|
+
2. Verify you're within the sample rate (try `sampleRate: 1.0` for testing)
|
|
387
|
+
3. Check browser console for errors
|
|
388
|
+
|
|
389
|
+
### High data volume
|
|
390
|
+
|
|
391
|
+
1. Reduce `sampleRate` (e.g., `0.05` for 5%)
|
|
392
|
+
2. Use `"strict"` privacy mode (smaller payloads due to masked content)
|
|
393
|
+
|
|
394
|
+
### Missing trace correlation
|
|
395
|
+
|
|
396
|
+
1. Ensure `tracing.propagateToOrigins` includes your API domain
|
|
397
|
+
2. Check that your backend parses the `traceparent` header
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## License
|
|
402
|
+
|
|
403
|
+
MIT
|
package/dist/client.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ReplayConfig } from "./replay.js";
|
|
1
2
|
import { type StorageMode } from "./session.js";
|
|
2
3
|
export type TracingConfig = {
|
|
3
4
|
/** Enable distributed tracing (default: true) */
|
|
@@ -30,6 +31,8 @@ export type BrowserConfig = {
|
|
|
30
31
|
* No cookie consent required.
|
|
31
32
|
*/
|
|
32
33
|
sessionStorage?: StorageMode;
|
|
34
|
+
/** Session replay configuration */
|
|
35
|
+
replay?: ReplayConfig;
|
|
33
36
|
};
|
|
34
37
|
export declare function createSpanwiseBrowser(config: BrowserConfig): {
|
|
35
38
|
trackEvent: () => void;
|
|
@@ -37,6 +40,8 @@ export declare function createSpanwiseBrowser(config: BrowserConfig): {
|
|
|
37
40
|
trackError: () => void;
|
|
38
41
|
setUser: () => void;
|
|
39
42
|
getSessionId: () => null;
|
|
43
|
+
startReplay: () => void;
|
|
44
|
+
stopReplay: () => void;
|
|
40
45
|
} | {
|
|
41
46
|
trackEvent: (name: string, properties?: Record<string, unknown>) => void;
|
|
42
47
|
trackPageView: (url?: string) => void;
|
|
@@ -46,6 +51,10 @@ export declare function createSpanwiseBrowser(config: BrowserConfig): {
|
|
|
46
51
|
}) => void;
|
|
47
52
|
setUser: (id: string, traits?: Record<string, unknown>) => void;
|
|
48
53
|
getSessionId: () => string;
|
|
54
|
+
/** Start replay recording manually (overrides sampling) */
|
|
55
|
+
startReplay: () => void | undefined;
|
|
56
|
+
/** Stop replay recording */
|
|
57
|
+
stopReplay: () => void | undefined;
|
|
49
58
|
};
|
|
50
59
|
export type SpanwiseBrowser = ReturnType<typeof createSpanwiseBrowser>;
|
|
51
60
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAYA,OAAO,EACN,KAAK,WAAW,EAIhB,MAAM,cAAc,CAAA;AAIrB,MAAM,MAAM,aAAa,GAAG;IAC3B,iDAAiD;IACjD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,wEAAwE;IACxE,kBAAkB,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAA;IACxC,2CAA2C;IAC3C,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,mEAAmE;IACnE,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,wCAAwC;IACxC,OAAO,CAAC,EAAE,aAAa,CAAA;IACvB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,KAAK,YAAY,EAAwB,MAAM,aAAa,CAAA;AACrE,OAAO,EACN,KAAK,WAAW,EAIhB,MAAM,cAAc,CAAA;AAIrB,MAAM,MAAM,aAAa,GAAG;IAC3B,iDAAiD;IACjD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,wEAAwE;IACxE,kBAAkB,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAA;IACxC,2CAA2C;IAC3C,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,mEAAmE;IACnE,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,wCAAwC;IACxC,OAAO,CAAC,EAAE,aAAa,CAAA;IACvB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,WAAW,CAAA;IAC5B,mCAAmC;IACnC,MAAM,CAAC,EAAE,YAAY,CAAA;CACrB,CAAA;AAmBD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa;;;;;;;;;uBAuDnD,MAAM,eACC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAClC,IAAI;0BAhBsB,MAAM,KAAG,IAAI;wBA6BlC,KAAK,YACF;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,KAChD,IAAI;kBAOc,MAAM,WAAW,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,IAAI;wBAWtC,MAAM;IAiInC,2DAA2D;;IAE3D,4BAA4B;;EAG7B;AAED,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAA"}
|
package/dist/client.js
CHANGED
|
@@ -2,6 +2,7 @@ import { captureError, setupErrorTracking } from "./errors.js";
|
|
|
2
2
|
import { createBaseEvent } from "./events.js";
|
|
3
3
|
import { instrumentFetch, updateFetchConfig } from "./instrumentation/fetch.js";
|
|
4
4
|
import { instrumentXHR, updateXHRConfig } from "./instrumentation/xhr.js";
|
|
5
|
+
import { createReplayRecorder } from "./replay.js";
|
|
5
6
|
import { getOrCreateSession, refreshSession, setStorageMode, } from "./session.js";
|
|
6
7
|
import { createTransport } from "./transport.js";
|
|
7
8
|
import { setupVitalsTracking } from "./vitals.js";
|
|
@@ -30,6 +31,8 @@ export function createSpanwiseBrowser(config) {
|
|
|
30
31
|
trackError: () => { },
|
|
31
32
|
setUser: () => { },
|
|
32
33
|
getSessionId: () => null,
|
|
34
|
+
startReplay: () => { },
|
|
35
|
+
stopReplay: () => { },
|
|
33
36
|
};
|
|
34
37
|
}
|
|
35
38
|
const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
@@ -41,6 +44,14 @@ export function createSpanwiseBrowser(config) {
|
|
|
41
44
|
baseUrl,
|
|
42
45
|
serviceName: config.serviceName,
|
|
43
46
|
});
|
|
47
|
+
// Initialize replay recorder if configured
|
|
48
|
+
const replayRecorder = config.replay?.enabled
|
|
49
|
+
? createReplayRecorder(config.replay, {
|
|
50
|
+
apiKey: config.apiKey,
|
|
51
|
+
baseUrl,
|
|
52
|
+
sessionId,
|
|
53
|
+
})
|
|
54
|
+
: null;
|
|
44
55
|
function enqueueEvent(event) {
|
|
45
56
|
refreshSession();
|
|
46
57
|
transport.enqueue(event);
|
|
@@ -71,6 +82,8 @@ export function createSpanwiseBrowser(config) {
|
|
|
71
82
|
function trackError(error, options) {
|
|
72
83
|
const event = captureError(error, sessionId, userId, options);
|
|
73
84
|
enqueueEvent(event);
|
|
85
|
+
// Mark replay for error correlation
|
|
86
|
+
replayRecorder?.markError();
|
|
74
87
|
}
|
|
75
88
|
function setUser(id, traits) {
|
|
76
89
|
userId = id;
|
|
@@ -145,6 +158,8 @@ export function createSpanwiseBrowser(config) {
|
|
|
145
158
|
if (config.trackErrors !== false) {
|
|
146
159
|
setupErrorTracking(sessionId, userId, (event) => {
|
|
147
160
|
enqueueEvent(event);
|
|
161
|
+
// Mark replay for error correlation (auto-captured errors)
|
|
162
|
+
replayRecorder?.markError();
|
|
148
163
|
});
|
|
149
164
|
}
|
|
150
165
|
// Resource tracking: capture HTTP requests with timing and trace correlation
|
|
@@ -181,6 +196,10 @@ export function createSpanwiseBrowser(config) {
|
|
|
181
196
|
trackError,
|
|
182
197
|
setUser,
|
|
183
198
|
getSessionId: getSessionIdValue,
|
|
199
|
+
/** Start replay recording manually (overrides sampling) */
|
|
200
|
+
startReplay: () => replayRecorder?.start(),
|
|
201
|
+
/** Stop replay recording */
|
|
202
|
+
stopReplay: () => replayRecorder?.stop(),
|
|
184
203
|
};
|
|
185
204
|
}
|
|
186
205
|
//# sourceMappingURL=client.js.map
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAS9D,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAC/E,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AACzE,OAAO,EAEN,kBAAkB,EAClB,cAAc,EACd,cAAc,GACd,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { createSpanwiseBrowser, type BrowserConfig, type SpanwiseBrowser, type TracingConfig, } from "./client.js";
|
|
2
|
+
export type { ReplayConfig, PrivacyMode } from "./replay.js";
|
|
2
3
|
export type { RumEvent, RumEventType, PageViewEvent, ClickEvent, VitalsEvent, ErrorEvent, CustomEvent, ResourceEvent, } from "./events.js";
|
|
3
4
|
export { getSessionId, endSession, type StorageMode } from "./session.js";
|
|
4
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,qBAAqB,EACrB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,aAAa,GAClB,MAAM,aAAa,CAAA;AACpB,YAAY,EACX,QAAQ,EACR,YAAY,EACZ,aAAa,EACb,UAAU,EACV,WAAW,EACX,UAAU,EACV,WAAW,EACX,aAAa,GACb,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,qBAAqB,EACrB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,aAAa,GAClB,MAAM,aAAa,CAAA;AACpB,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC5D,YAAY,EACX,QAAQ,EACR,YAAY,EACZ,aAAa,EACb,UAAU,EACV,WAAW,EACX,UAAU,EACV,WAAW,EACX,aAAa,GACb,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAA"}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,qBAAqB,GAIrB,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,qBAAqB,GAIrB,MAAM,aAAa,CAAA;AAYpB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAoB,MAAM,cAAc,CAAA"}
|
package/dist/replay.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Privacy mode for session replay recording.
|
|
3
|
+
*
|
|
4
|
+
* - `"strict"` (default): Masks ALL text and input values. Recommended for most apps.
|
|
5
|
+
* Only element structure and interactions are visible.
|
|
6
|
+
*
|
|
7
|
+
* - `"balanced"`: Masks input values only. Text content is visible but form data is protected.
|
|
8
|
+
* Good for marketing sites with minimal user input.
|
|
9
|
+
*
|
|
10
|
+
* - `"permissive"`: Records everything as-is. Only use for internal tools or after
|
|
11
|
+
* explicit user consent. You must ensure no PII is captured.
|
|
12
|
+
*/
|
|
13
|
+
export type PrivacyMode = "strict" | "balanced" | "permissive";
|
|
14
|
+
export type ReplayConfig = {
|
|
15
|
+
/** Enable replay recording (default: false) */
|
|
16
|
+
enabled?: boolean;
|
|
17
|
+
/** Sample rate for replays 0-1 (default: 0.1 = 10%) */
|
|
18
|
+
sampleRate?: number;
|
|
19
|
+
/** Sample rate when error occurs 0-1 (default: 1.0 = 100%) */
|
|
20
|
+
onErrorSampleRate?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Privacy mode controls what data is captured (default: "strict")
|
|
23
|
+
*
|
|
24
|
+
* - "strict": Masks all text and inputs (recommended)
|
|
25
|
+
* - "balanced": Masks inputs only, text is visible
|
|
26
|
+
* - "permissive": Records everything (use with caution)
|
|
27
|
+
*
|
|
28
|
+
* Regardless of mode, you can use HTML attributes for fine-grained control:
|
|
29
|
+
* - `data-sw-block`: Completely hides element (shows placeholder)
|
|
30
|
+
* - `data-sw-mask`: Masks text content with asterisks
|
|
31
|
+
* - `data-sw-unmask`: Shows content even in strict mode
|
|
32
|
+
*
|
|
33
|
+
* rrweb built-in classes are also supported:
|
|
34
|
+
* - `.rr-block`: Same as data-sw-block
|
|
35
|
+
* - `.rr-mask`: Same as data-sw-mask
|
|
36
|
+
*/
|
|
37
|
+
privacyMode?: PrivacyMode;
|
|
38
|
+
};
|
|
39
|
+
type ReplayTransportConfig = {
|
|
40
|
+
apiKey: string;
|
|
41
|
+
baseUrl: string;
|
|
42
|
+
sessionId: string;
|
|
43
|
+
};
|
|
44
|
+
export declare function createReplayRecorder(config: ReplayConfig, transportConfig: ReplayTransportConfig, onError?: () => void): {
|
|
45
|
+
start: () => void;
|
|
46
|
+
stop: () => void;
|
|
47
|
+
markError: () => void;
|
|
48
|
+
isRecording: () => boolean;
|
|
49
|
+
wasUpgradedByError: () => boolean;
|
|
50
|
+
};
|
|
51
|
+
export type ReplayRecorder = ReturnType<typeof createReplayRecorder>;
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=replay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,UAAU,GAAG,YAAY,CAAA;AAE9D,MAAM,MAAM,YAAY,GAAG;IAC1B,+CAA+C;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,8DAA8D;IAC9D,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B;;;;;;;;;;;;;;;OAeG;IACH,WAAW,CAAC,EAAE,WAAW,CAAA;CACzB,CAAA;AAED,KAAK,qBAAqB,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CACjB,CAAA;AAmBD,wBAAgB,oBAAoB,CACnC,MAAM,EAAE,YAAY,EACpB,eAAe,EAAE,qBAAqB,EACtC,OAAO,CAAC,EAAE,MAAM,IAAI;iBAsIF,IAAI;gBAcL,IAAI;qBAgCC,IAAI;;;EA2B1B;AAED,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA"}
|
package/dist/replay.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { gzipSync } from "fflate";
|
|
2
|
+
import { record } from "rrweb";
|
|
3
|
+
const MAX_CHUNK_SIZE = 64 * 1024; // 64KB uncompressed
|
|
4
|
+
const FLUSH_INTERVAL = 5000; // 5 seconds
|
|
5
|
+
// Selectors for privacy control
|
|
6
|
+
const BLOCK_SELECTOR = "[data-sw-block], .rr-block";
|
|
7
|
+
const MASK_SELECTOR = "[data-sw-mask], .rr-mask";
|
|
8
|
+
const UNMASK_SELECTOR = "[data-sw-unmask]";
|
|
9
|
+
// Efficient base64 encoding for Uint8Array (avoids spread operator on large arrays)
|
|
10
|
+
function uint8ToBase64(bytes) {
|
|
11
|
+
let binary = "";
|
|
12
|
+
for (const byte of bytes) {
|
|
13
|
+
binary += String.fromCharCode(byte);
|
|
14
|
+
}
|
|
15
|
+
return btoa(binary);
|
|
16
|
+
}
|
|
17
|
+
export function createReplayRecorder(config, transportConfig, onError) {
|
|
18
|
+
const events = [];
|
|
19
|
+
let estimatedSize = 0; // Track size incrementally
|
|
20
|
+
let chunkIndex = 0;
|
|
21
|
+
let flushTimer = null;
|
|
22
|
+
let isRecording = false;
|
|
23
|
+
let hasStartedRecording = false;
|
|
24
|
+
let stopRecording = null;
|
|
25
|
+
let hasErrors = false;
|
|
26
|
+
// Sampling logic
|
|
27
|
+
const shouldRecord = Math.random() < (config.sampleRate ?? 0.1);
|
|
28
|
+
let wasUpgradedByError = false;
|
|
29
|
+
function getRecordOptions() {
|
|
30
|
+
const privacyMode = config.privacyMode ?? "strict";
|
|
31
|
+
// Build privacy settings based on mode
|
|
32
|
+
const maskAllInputs = privacyMode !== "permissive";
|
|
33
|
+
const maskAllText = privacyMode === "strict";
|
|
34
|
+
// In strict mode, mask everything except explicitly unmasked elements
|
|
35
|
+
// In balanced/permissive modes, only mask elements with data-sw-mask
|
|
36
|
+
const maskTextSelector = maskAllText
|
|
37
|
+
? `*:not(${UNMASK_SELECTOR}):not(${UNMASK_SELECTOR} *)`
|
|
38
|
+
: MASK_SELECTOR;
|
|
39
|
+
return {
|
|
40
|
+
emit: (event) => handleEvent(event),
|
|
41
|
+
sampling: {
|
|
42
|
+
mousemove: true,
|
|
43
|
+
mouseInteraction: true,
|
|
44
|
+
scroll: 150,
|
|
45
|
+
input: "last",
|
|
46
|
+
},
|
|
47
|
+
// Privacy settings
|
|
48
|
+
maskAllInputs,
|
|
49
|
+
maskTextSelector,
|
|
50
|
+
blockSelector: BLOCK_SELECTOR,
|
|
51
|
+
// Slimmer DOM for performance
|
|
52
|
+
slimDOMOptions: {
|
|
53
|
+
script: true,
|
|
54
|
+
comment: true,
|
|
55
|
+
headFavicon: true,
|
|
56
|
+
headWhitespace: true,
|
|
57
|
+
headMetaSocial: true,
|
|
58
|
+
headMetaRobots: true,
|
|
59
|
+
headMetaHttpEquiv: true,
|
|
60
|
+
headMetaVerification: true,
|
|
61
|
+
headMetaAuthorship: true,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function handleEvent(event) {
|
|
66
|
+
events.push(event);
|
|
67
|
+
// Estimate event size (faster than JSON.stringify entire array)
|
|
68
|
+
estimatedSize += JSON.stringify(event).length + 1; // +1 for comma
|
|
69
|
+
if (estimatedSize >= MAX_CHUNK_SIZE) {
|
|
70
|
+
flush();
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
scheduleFlush();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function scheduleFlush() {
|
|
77
|
+
if (flushTimer)
|
|
78
|
+
return;
|
|
79
|
+
flushTimer = setTimeout(() => {
|
|
80
|
+
flushTimer = null;
|
|
81
|
+
flush();
|
|
82
|
+
}, FLUSH_INTERVAL);
|
|
83
|
+
}
|
|
84
|
+
function preparePayload(batch) {
|
|
85
|
+
const compressed = gzipSync(new TextEncoder().encode(JSON.stringify(batch)));
|
|
86
|
+
const isFullSnapshot = chunkIndex === 0;
|
|
87
|
+
return JSON.stringify({
|
|
88
|
+
sessionId: transportConfig.sessionId,
|
|
89
|
+
chunkIndex,
|
|
90
|
+
data: uint8ToBase64(compressed),
|
|
91
|
+
isFullSnapshot,
|
|
92
|
+
timestamp: Date.now(),
|
|
93
|
+
hasErrors,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
async function flush() {
|
|
97
|
+
if (events.length === 0)
|
|
98
|
+
return;
|
|
99
|
+
const batch = events.splice(0);
|
|
100
|
+
estimatedSize = 0;
|
|
101
|
+
try {
|
|
102
|
+
await fetch(`${transportConfig.baseUrl}/v1/ingest/replays`, {
|
|
103
|
+
method: "POST",
|
|
104
|
+
headers: {
|
|
105
|
+
"Content-Type": "application/json",
|
|
106
|
+
"x-api-key": transportConfig.apiKey,
|
|
107
|
+
},
|
|
108
|
+
body: preparePayload(batch),
|
|
109
|
+
keepalive: true,
|
|
110
|
+
});
|
|
111
|
+
chunkIndex++;
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
console.warn("[Spanwise Replay] Failed to send chunk:", error);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function flushBeacon() {
|
|
118
|
+
if (events.length === 0)
|
|
119
|
+
return;
|
|
120
|
+
const batch = events.splice(0);
|
|
121
|
+
estimatedSize = 0;
|
|
122
|
+
try {
|
|
123
|
+
fetch(`${transportConfig.baseUrl}/v1/ingest/replays`, {
|
|
124
|
+
method: "POST",
|
|
125
|
+
headers: {
|
|
126
|
+
"Content-Type": "application/json",
|
|
127
|
+
"x-api-key": transportConfig.apiKey,
|
|
128
|
+
},
|
|
129
|
+
body: preparePayload(batch),
|
|
130
|
+
keepalive: true,
|
|
131
|
+
}).catch(() => {
|
|
132
|
+
// Silently ignore - page is likely unloading
|
|
133
|
+
});
|
|
134
|
+
chunkIndex++;
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// Ignore errors during page unload
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function start() {
|
|
141
|
+
if (isRecording || hasStartedRecording)
|
|
142
|
+
return;
|
|
143
|
+
hasStartedRecording = true;
|
|
144
|
+
isRecording = true;
|
|
145
|
+
const stop = record(getRecordOptions());
|
|
146
|
+
stopRecording = stop ?? null;
|
|
147
|
+
// Setup unload handlers
|
|
148
|
+
window.addEventListener("visibilitychange", handleVisibilityChange);
|
|
149
|
+
window.addEventListener("pagehide", handlePageHide);
|
|
150
|
+
}
|
|
151
|
+
function stop() {
|
|
152
|
+
if (!isRecording)
|
|
153
|
+
return;
|
|
154
|
+
isRecording = false;
|
|
155
|
+
if (stopRecording) {
|
|
156
|
+
stopRecording();
|
|
157
|
+
stopRecording = null;
|
|
158
|
+
}
|
|
159
|
+
if (flushTimer) {
|
|
160
|
+
clearTimeout(flushTimer);
|
|
161
|
+
flushTimer = null;
|
|
162
|
+
}
|
|
163
|
+
// Final flush
|
|
164
|
+
flush();
|
|
165
|
+
window.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
166
|
+
window.removeEventListener("pagehide", handlePageHide);
|
|
167
|
+
}
|
|
168
|
+
function handleVisibilityChange() {
|
|
169
|
+
if (document.visibilityState === "hidden") {
|
|
170
|
+
flushBeacon();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function handlePageHide() {
|
|
174
|
+
flushBeacon();
|
|
175
|
+
}
|
|
176
|
+
function markError() {
|
|
177
|
+
hasErrors = true;
|
|
178
|
+
// Upgrade to recording if sampled out but within onErrorSampleRate
|
|
179
|
+
if (!hasStartedRecording && !shouldRecord) {
|
|
180
|
+
const onErrorRate = config.onErrorSampleRate ?? 1.0;
|
|
181
|
+
if (Math.random() < onErrorRate) {
|
|
182
|
+
wasUpgradedByError = true;
|
|
183
|
+
start();
|
|
184
|
+
onError?.();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Auto-start if sampled in
|
|
189
|
+
if (config.enabled && shouldRecord) {
|
|
190
|
+
// Defer to next tick to ensure DOM is ready
|
|
191
|
+
setTimeout(start, 0);
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
start,
|
|
195
|
+
stop,
|
|
196
|
+
markError,
|
|
197
|
+
isRecording: () => isRecording,
|
|
198
|
+
wasUpgradedByError: () => wasUpgradedByError,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=replay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spanwise/rum",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Real User Monitoring SDK for Spanwise - track sessions, page views, clicks, and Core Web Vitals",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -30,6 +30,9 @@
|
|
|
30
30
|
],
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"dependencies": {
|
|
33
|
+
"@rrweb/types": "2.0.0-alpha.18",
|
|
34
|
+
"fflate": "^0.8.2",
|
|
35
|
+
"rrweb": "2.0.0-alpha.4",
|
|
33
36
|
"web-vitals": "^4.2.4"
|
|
34
37
|
},
|
|
35
38
|
"devDependencies": {
|