@spanwise/rum 0.5.0 → 0.7.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 +424 -0
- package/dist/client.d.ts +9 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +4 -2
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/replay.d.ts +44 -7
- package/dist/replay.d.ts.map +1 -1
- package/dist/replay.js +150 -49
- package/dist/replay.js.map +1 -1
- package/package.json +2 -4
package/README.md
ADDED
|
@@ -0,0 +1,424 @@
|
|
|
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.forceReplayUpload()`
|
|
120
|
+
|
|
121
|
+
Force replay upload for specific users (bypasses sampling).
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
// Force upload for VIP users regardless of sample rate
|
|
125
|
+
if (user.isVIP) {
|
|
126
|
+
spanwise.forceReplayUpload()
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### `spanwise.stopReplay()`
|
|
131
|
+
|
|
132
|
+
Stop replay recording entirely.
|
|
133
|
+
|
|
134
|
+
### `spanwise.isReplayUploading()`
|
|
135
|
+
|
|
136
|
+
Check if replay is currently uploading to server (vs buffering in memory).
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Session Replay
|
|
141
|
+
|
|
142
|
+
Session Replay records user interactions as a video-like playback. It captures DOM mutations, mouse movements, clicks, and scrolls.
|
|
143
|
+
|
|
144
|
+
### Enabling Replay
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
const spanwise = createSpanwiseBrowser({
|
|
148
|
+
apiKey: "sw_...",
|
|
149
|
+
replay: {
|
|
150
|
+
enabled: true,
|
|
151
|
+
},
|
|
152
|
+
})
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### How It Works: Rolling Buffer
|
|
156
|
+
|
|
157
|
+
Session Replay uses a **rolling buffer architecture** (similar to Sentry):
|
|
158
|
+
|
|
159
|
+
1. **Always recording** - When enabled, the SDK always records to a 60-second memory buffer
|
|
160
|
+
2. **Sampled sessions** (default 10%) - Upload immediately to server
|
|
161
|
+
3. **Non-sampled sessions** (90%) - Keep buffer in memory, zero network cost
|
|
162
|
+
4. **On error** - Flush the 60-second buffer and start uploading
|
|
163
|
+
|
|
164
|
+
This means you **always capture what happened before an error**, not just after.
|
|
165
|
+
|
|
166
|
+
### Sampling
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
replay: {
|
|
170
|
+
enabled: true,
|
|
171
|
+
sampleRate: 0.1, // 10% upload immediately
|
|
172
|
+
onErrorSampleRate: 1.0, // 100% upload on error (includes 60s buffer)
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
| Scenario | What's Captured |
|
|
177
|
+
|----------|-----------------|
|
|
178
|
+
| Sampled (10%) | Full session from start |
|
|
179
|
+
| Not sampled + error | 60 seconds before error + everything after |
|
|
180
|
+
| Not sampled + no error | Nothing sent (zero network cost) |
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Privacy & Data Protection
|
|
185
|
+
|
|
186
|
+
Session Replay is designed with privacy as the default. **No configuration is required for most applications** - sensitive data is automatically protected.
|
|
187
|
+
|
|
188
|
+
### Privacy Modes
|
|
189
|
+
|
|
190
|
+
| Mode | Text | Inputs | Use Case |
|
|
191
|
+
|------|------|--------|----------|
|
|
192
|
+
| `"strict"` (default) | Masked | Masked | Most applications |
|
|
193
|
+
| `"balanced"` | Visible | Masked | Marketing sites, blogs |
|
|
194
|
+
| `"permissive"` | Visible | Visible | Internal tools only |
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
replay: {
|
|
198
|
+
enabled: true,
|
|
199
|
+
privacyMode: "strict", // Default - masks everything
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### What Each Mode Does
|
|
204
|
+
|
|
205
|
+
#### Strict Mode (Default)
|
|
206
|
+
|
|
207
|
+
All text content is replaced with `****`. Form inputs show `•••••`. You see the page structure and user interactions, but no actual content.
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
┌─────────────────────────────┐
|
|
211
|
+
│ **** ******** │ ← Masked heading
|
|
212
|
+
│ │
|
|
213
|
+
│ ******** **** ** ******* │ ← Masked paragraph
|
|
214
|
+
│ ******* ** *** **** │
|
|
215
|
+
│ │
|
|
216
|
+
│ Email: [•••••••••••••] │ ← Masked input
|
|
217
|
+
│ Password: [••••••••] │
|
|
218
|
+
│ │
|
|
219
|
+
│ [**********] │ ← Masked button
|
|
220
|
+
└─────────────────────────────┘
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
#### Balanced Mode
|
|
224
|
+
|
|
225
|
+
Text is visible, but all form inputs are masked. Good for content-heavy sites.
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
┌─────────────────────────────┐
|
|
229
|
+
│ Welcome Back │ ← Visible heading
|
|
230
|
+
│ │
|
|
231
|
+
│ Please sign in to continue │ ← Visible text
|
|
232
|
+
│ │
|
|
233
|
+
│ Email: [•••••••••••••] │ ← Masked input
|
|
234
|
+
│ Password: [••••••••] │
|
|
235
|
+
│ │
|
|
236
|
+
│ [Sign In] │ ← Visible button
|
|
237
|
+
└─────────────────────────────┘
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
#### Permissive Mode
|
|
241
|
+
|
|
242
|
+
Everything is recorded as-is. **Only use for internal tools or with explicit user consent.**
|
|
243
|
+
|
|
244
|
+
### Fine-Grained Control with HTML Attributes
|
|
245
|
+
|
|
246
|
+
Regardless of privacy mode, you can control specific elements:
|
|
247
|
+
|
|
248
|
+
#### Block Elements Completely
|
|
249
|
+
|
|
250
|
+
Use `data-sw-block` to completely hide an element. It will appear as a gray placeholder in replays.
|
|
251
|
+
|
|
252
|
+
```html
|
|
253
|
+
<!-- Credit card form - never record -->
|
|
254
|
+
<div data-sw-block>
|
|
255
|
+
<input type="text" placeholder="Card number" />
|
|
256
|
+
<input type="text" placeholder="CVV" />
|
|
257
|
+
</div>
|
|
258
|
+
|
|
259
|
+
<!-- Sensitive user data -->
|
|
260
|
+
<div data-sw-block class="user-profile">
|
|
261
|
+
<p>SSN: 123-45-6789</p>
|
|
262
|
+
</div>
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
#### Mask Specific Elements
|
|
266
|
+
|
|
267
|
+
Use `data-sw-mask` to mask text while preserving structure (useful in balanced/permissive modes).
|
|
268
|
+
|
|
269
|
+
```html
|
|
270
|
+
<!-- Mask email in a visible section -->
|
|
271
|
+
<p>Contact: <span data-sw-mask>user@example.com</span></p>
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
#### Unmask Elements in Strict Mode
|
|
275
|
+
|
|
276
|
+
Use `data-sw-unmask` to show content even when using strict mode.
|
|
277
|
+
|
|
278
|
+
```html
|
|
279
|
+
<!-- Show navigation labels in strict mode -->
|
|
280
|
+
<nav data-sw-unmask>
|
|
281
|
+
<a href="/">Home</a>
|
|
282
|
+
<a href="/products">Products</a>
|
|
283
|
+
<a href="/about">About</a>
|
|
284
|
+
</nav>
|
|
285
|
+
|
|
286
|
+
<!-- Show static content -->
|
|
287
|
+
<footer data-sw-unmask>
|
|
288
|
+
<p>© 2024 My Company</p>
|
|
289
|
+
</footer>
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
#### rrweb Classes (Alternative)
|
|
293
|
+
|
|
294
|
+
The standard rrweb classes also work:
|
|
295
|
+
|
|
296
|
+
```html
|
|
297
|
+
<div class="rr-block">Hidden content</div>
|
|
298
|
+
<div class="rr-mask">Masked content</div>
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Best Practices
|
|
302
|
+
|
|
303
|
+
1. **Start with strict mode** - It's the safest default. Only relax if needed.
|
|
304
|
+
|
|
305
|
+
2. **Block sensitive sections entirely** - For areas with PII (profile pages, account settings), use `data-sw-block`.
|
|
306
|
+
|
|
307
|
+
3. **Review before production** - Test your app with replay enabled and verify no sensitive data appears.
|
|
308
|
+
|
|
309
|
+
4. **Consider user consent** - For GDPR compliance, inform users that sessions may be recorded.
|
|
310
|
+
|
|
311
|
+
5. **Use unmask sparingly** - Only unmask truly static, non-sensitive content like navigation labels.
|
|
312
|
+
|
|
313
|
+
### What's Automatically Protected
|
|
314
|
+
|
|
315
|
+
Even without configuration:
|
|
316
|
+
|
|
317
|
+
- All `<input>` values are masked (except in permissive mode)
|
|
318
|
+
- Password fields are always masked
|
|
319
|
+
- Credit card patterns are detected and masked
|
|
320
|
+
- `<script>` content is never recorded
|
|
321
|
+
- `<head>` metadata is stripped
|
|
322
|
+
|
|
323
|
+
### Sensitive Data Checklist
|
|
324
|
+
|
|
325
|
+
Before enabling replay, ensure these are blocked or masked:
|
|
326
|
+
|
|
327
|
+
- [ ] User profile information (name, email, phone)
|
|
328
|
+
- [ ] Payment forms and credit card inputs
|
|
329
|
+
- [ ] Social security numbers, government IDs
|
|
330
|
+
- [ ] Medical or health information
|
|
331
|
+
- [ ] Authentication tokens displayed on screen
|
|
332
|
+
- [ ] Private messages or chat content
|
|
333
|
+
- [ ] Financial account numbers
|
|
334
|
+
- [ ] Any PII in confirmation pages
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## Distributed Tracing
|
|
339
|
+
|
|
340
|
+
The SDK automatically injects `traceparent` headers into fetch/XHR requests, connecting frontend sessions to backend traces.
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
const spanwise = createSpanwiseBrowser({
|
|
344
|
+
apiKey: "sw_...",
|
|
345
|
+
tracing: {
|
|
346
|
+
enabled: true,
|
|
347
|
+
propagateToOrigins: [
|
|
348
|
+
"https://api.myapp.com",
|
|
349
|
+
/\.myapp\.com$/,
|
|
350
|
+
],
|
|
351
|
+
},
|
|
352
|
+
})
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Your backend will receive headers like:
|
|
356
|
+
|
|
357
|
+
```
|
|
358
|
+
traceparent: 00-{traceId}-{spanId}-01
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## Browser Support
|
|
364
|
+
|
|
365
|
+
- Chrome 64+
|
|
366
|
+
- Firefox 67+
|
|
367
|
+
- Safari 12+
|
|
368
|
+
- Edge 79+
|
|
369
|
+
|
|
370
|
+
Session Replay requires:
|
|
371
|
+
- MutationObserver
|
|
372
|
+
- WeakMap
|
|
373
|
+
- requestAnimationFrame
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## Bundle Size
|
|
378
|
+
|
|
379
|
+
The base SDK is ~8KB gzipped. Session Replay adds ~40KB gzipped (rrweb).
|
|
380
|
+
|
|
381
|
+
Replay is dynamically imported only when enabled, so it won't affect your bundle if not used.
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
## TypeScript
|
|
386
|
+
|
|
387
|
+
Full TypeScript support with exported types:
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
import {
|
|
391
|
+
createSpanwiseBrowser,
|
|
392
|
+
type BrowserConfig,
|
|
393
|
+
type SpanwiseBrowser,
|
|
394
|
+
type ReplayConfig,
|
|
395
|
+
type PrivacyMode,
|
|
396
|
+
type TracingConfig,
|
|
397
|
+
} from "@spanwise/rum"
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## Troubleshooting
|
|
403
|
+
|
|
404
|
+
### Replay not recording
|
|
405
|
+
|
|
406
|
+
1. Check that `replay.enabled` is `true`
|
|
407
|
+
2. Verify you're within the sample rate (try `sampleRate: 1.0` for testing)
|
|
408
|
+
3. Check browser console for errors
|
|
409
|
+
|
|
410
|
+
### High data volume
|
|
411
|
+
|
|
412
|
+
1. Reduce `sampleRate` (e.g., `0.05` for 5%)
|
|
413
|
+
2. Use `"strict"` privacy mode (smaller payloads due to masked content)
|
|
414
|
+
|
|
415
|
+
### Missing trace correlation
|
|
416
|
+
|
|
417
|
+
1. Ensure `tracing.propagateToOrigins` includes your API domain
|
|
418
|
+
2. Check that your backend parses the `traceparent` header
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
## License
|
|
423
|
+
|
|
424
|
+
MIT
|
package/dist/client.d.ts
CHANGED
|
@@ -42,6 +42,10 @@ export declare function createSpanwiseBrowser(config: BrowserConfig): {
|
|
|
42
42
|
getSessionId: () => null;
|
|
43
43
|
startReplay: () => void;
|
|
44
44
|
stopReplay: () => void;
|
|
45
|
+
/** Force replay upload (e.g., for VIP users) - overrides sampling */
|
|
46
|
+
forceReplayUpload?: undefined;
|
|
47
|
+
/** Check if replay is currently uploading to server */
|
|
48
|
+
isReplayUploading?: undefined;
|
|
45
49
|
} | {
|
|
46
50
|
trackEvent: (name: string, properties?: Record<string, unknown>) => void;
|
|
47
51
|
trackPageView: (url?: string) => void;
|
|
@@ -51,10 +55,13 @@ export declare function createSpanwiseBrowser(config: BrowserConfig): {
|
|
|
51
55
|
}) => void;
|
|
52
56
|
setUser: (id: string, traits?: Record<string, unknown>) => void;
|
|
53
57
|
getSessionId: () => string;
|
|
54
|
-
/**
|
|
55
|
-
|
|
58
|
+
/** Force replay upload (e.g., for VIP users) - overrides sampling */
|
|
59
|
+
forceReplayUpload: () => void | undefined;
|
|
56
60
|
/** Stop replay recording */
|
|
57
61
|
stopReplay: () => void | undefined;
|
|
62
|
+
/** Check if replay is currently uploading to server */
|
|
63
|
+
isReplayUploading: () => boolean;
|
|
64
|
+
startReplay?: undefined;
|
|
58
65
|
};
|
|
59
66
|
export type SpanwiseBrowser = ReturnType<typeof createSpanwiseBrowser>;
|
|
60
67
|
//# 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,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
|
|
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;;;;;;;;IA2NzD,qEAAqE;;IAIrE,uDAAuD;;;uBAxKjD,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,qEAAqE;;IAErE,4BAA4B;;IAE5B,uDAAuD;;;EAGxD;AAED,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAA"}
|
package/dist/client.js
CHANGED
|
@@ -196,10 +196,12 @@ export function createSpanwiseBrowser(config) {
|
|
|
196
196
|
trackError,
|
|
197
197
|
setUser,
|
|
198
198
|
getSessionId: getSessionIdValue,
|
|
199
|
-
/**
|
|
200
|
-
|
|
199
|
+
/** Force replay upload (e.g., for VIP users) - overrides sampling */
|
|
200
|
+
forceReplayUpload: () => replayRecorder?.startUploading(),
|
|
201
201
|
/** Stop replay recording */
|
|
202
202
|
stopReplay: () => replayRecorder?.stop(),
|
|
203
|
+
/** Check if replay is currently uploading to server */
|
|
204
|
+
isReplayUploading: () => replayRecorder?.isUploading() ?? false,
|
|
203
205
|
};
|
|
204
206
|
}
|
|
205
207
|
//# 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":"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
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { createSpanwiseBrowser, type BrowserConfig, type SpanwiseBrowser, type TracingConfig, } from "./client.js";
|
|
2
|
-
export type { ReplayConfig } from "./replay.js";
|
|
2
|
+
export type { ReplayConfig, PrivacyMode } from "./replay.js";
|
|
3
3
|
export type { RumEvent, RumEventType, PageViewEvent, ClickEvent, VitalsEvent, ErrorEvent, CustomEvent, ResourceEvent, } from "./events.js";
|
|
4
4
|
export { getSessionId, endSession, type StorageMode } from "./session.js";
|
|
5
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,EAAE,YAAY,EAAE,MAAM,aAAa,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/replay.d.ts
CHANGED
|
@@ -1,28 +1,65 @@
|
|
|
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";
|
|
1
14
|
export type ReplayConfig = {
|
|
2
15
|
/** Enable replay recording (default: false) */
|
|
3
16
|
enabled?: boolean;
|
|
4
|
-
/** Sample rate for
|
|
17
|
+
/** Sample rate for immediate upload 0-1 (default: 0.1 = 10%) */
|
|
5
18
|
sampleRate?: number;
|
|
6
19
|
/** Sample rate when error occurs 0-1 (default: 1.0 = 100%) */
|
|
7
20
|
onErrorSampleRate?: number;
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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;
|
|
14
38
|
};
|
|
15
39
|
type ReplayTransportConfig = {
|
|
16
40
|
apiKey: string;
|
|
17
41
|
baseUrl: string;
|
|
18
42
|
sessionId: string;
|
|
19
43
|
};
|
|
44
|
+
/**
|
|
45
|
+
* Session Replay Recorder with Rolling Buffer
|
|
46
|
+
*
|
|
47
|
+
* Architecture:
|
|
48
|
+
* - Always records to memory when enabled
|
|
49
|
+
* - If sampled (sampleRate): uploads immediately
|
|
50
|
+
* - If not sampled: keeps 60s rolling buffer in memory
|
|
51
|
+
* - On error: flushes buffer and starts uploading (if onErrorSampleRate passes)
|
|
52
|
+
* - Zero network cost for sessions without errors that weren't sampled
|
|
53
|
+
*/
|
|
20
54
|
export declare function createReplayRecorder(config: ReplayConfig, transportConfig: ReplayTransportConfig, onError?: () => void): {
|
|
21
55
|
start: () => void;
|
|
22
56
|
stop: () => void;
|
|
23
57
|
markError: () => void;
|
|
24
58
|
isRecording: () => boolean;
|
|
59
|
+
isUploading: () => boolean;
|
|
25
60
|
wasUpgradedByError: () => boolean;
|
|
61
|
+
/** Force switch to upload mode (for manual control) */
|
|
62
|
+
startUploading: () => void;
|
|
26
63
|
};
|
|
27
64
|
export type ReplayRecorder = ReturnType<typeof createReplayRecorder>;
|
|
28
65
|
export {};
|
package/dist/replay.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,YAAY,GAAG;IAC1B,+CAA+C;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,
|
|
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,gEAAgE;IAChE,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;AAqBD;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CACnC,MAAM,EAAE,YAAY,EACpB,eAAe,EAAE,qBAAqB,EACtC,OAAO,CAAC,EAAE,MAAM,IAAI;iBA2LF,IAAI;gBAuBL,IAAI;qBA6CC,IAAI;;;;IA4BzB,uDAAuD;0BAjHzB,IAAI;EAoHnC;AAED,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA"}
|
package/dist/replay.js
CHANGED
|
@@ -2,7 +2,13 @@ import { gzipSync } from "fflate";
|
|
|
2
2
|
import { record } from "rrweb";
|
|
3
3
|
const MAX_CHUNK_SIZE = 64 * 1024; // 64KB uncompressed
|
|
4
4
|
const FLUSH_INTERVAL = 5000; // 5 seconds
|
|
5
|
-
|
|
5
|
+
const BUFFER_DURATION_MS = 60_000; // 60 seconds rolling buffer
|
|
6
|
+
const BUFFER_CLEANUP_INTERVAL = 10_000; // Cleanup every 10 seconds
|
|
7
|
+
// Selectors for privacy control
|
|
8
|
+
const BLOCK_SELECTOR = "[data-sw-block], .rr-block";
|
|
9
|
+
const MASK_SELECTOR = "[data-sw-mask], .rr-mask";
|
|
10
|
+
const UNMASK_SELECTOR = "[data-sw-unmask]";
|
|
11
|
+
// Efficient base64 encoding for Uint8Array
|
|
6
12
|
function uint8ToBase64(bytes) {
|
|
7
13
|
let binary = "";
|
|
8
14
|
for (const byte of bytes) {
|
|
@@ -10,25 +16,38 @@ function uint8ToBase64(bytes) {
|
|
|
10
16
|
}
|
|
11
17
|
return btoa(binary);
|
|
12
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Session Replay Recorder with Rolling Buffer
|
|
21
|
+
*
|
|
22
|
+
* Architecture:
|
|
23
|
+
* - Always records to memory when enabled
|
|
24
|
+
* - If sampled (sampleRate): uploads immediately
|
|
25
|
+
* - If not sampled: keeps 60s rolling buffer in memory
|
|
26
|
+
* - On error: flushes buffer and starts uploading (if onErrorSampleRate passes)
|
|
27
|
+
* - Zero network cost for sessions without errors that weren't sampled
|
|
28
|
+
*/
|
|
13
29
|
export function createReplayRecorder(config, transportConfig, onError) {
|
|
14
|
-
|
|
15
|
-
|
|
30
|
+
// Event buffer - always populated while recording
|
|
31
|
+
const buffer = [];
|
|
32
|
+
let estimatedSize = 0;
|
|
33
|
+
// Upload state
|
|
16
34
|
let chunkIndex = 0;
|
|
17
35
|
let flushTimer = null;
|
|
36
|
+
let bufferCleanupTimer = null;
|
|
37
|
+
// Recording state
|
|
18
38
|
let isRecording = false;
|
|
19
|
-
let
|
|
20
|
-
let stopRecording = null;
|
|
39
|
+
let stopRecordingFn = null;
|
|
21
40
|
let hasErrors = false;
|
|
22
|
-
|
|
23
|
-
|
|
41
|
+
let mode = "buffer";
|
|
42
|
+
// Sampling decisions (made once at session start)
|
|
43
|
+
const shouldUploadImmediately = Math.random() < (config.sampleRate ?? 0.1);
|
|
24
44
|
let wasUpgradedByError = false;
|
|
25
45
|
function getRecordOptions() {
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
const maskTextSelector = privacyLevel === "mask" ? "*" : config.maskSelector;
|
|
46
|
+
const privacyMode = config.privacyMode ?? "strict";
|
|
47
|
+
const maskAllInputs = privacyMode !== "permissive";
|
|
48
|
+
const maskAllText = privacyMode === "strict";
|
|
30
49
|
return {
|
|
31
|
-
emit:
|
|
50
|
+
emit: handleEvent,
|
|
32
51
|
sampling: {
|
|
33
52
|
mousemove: true,
|
|
34
53
|
mouseInteraction: true,
|
|
@@ -36,23 +55,39 @@ export function createReplayRecorder(config, transportConfig, onError) {
|
|
|
36
55
|
input: "last",
|
|
37
56
|
},
|
|
38
57
|
maskAllInputs,
|
|
39
|
-
|
|
40
|
-
|
|
58
|
+
maskAllText,
|
|
59
|
+
maskTextSelector: maskAllText ? undefined : MASK_SELECTOR,
|
|
60
|
+
unmaskTextSelector: maskAllText ? UNMASK_SELECTOR : undefined,
|
|
61
|
+
blockSelector: BLOCK_SELECTOR,
|
|
62
|
+
slimDOMOptions: {
|
|
63
|
+
script: true,
|
|
64
|
+
comment: true,
|
|
65
|
+
headFavicon: true,
|
|
66
|
+
headWhitespace: true,
|
|
67
|
+
headMetaSocial: true,
|
|
68
|
+
headMetaRobots: true,
|
|
69
|
+
headMetaHttpEquiv: true,
|
|
70
|
+
headMetaVerification: true,
|
|
71
|
+
headMetaAuthorship: true,
|
|
72
|
+
},
|
|
41
73
|
};
|
|
42
74
|
}
|
|
43
75
|
function handleEvent(event) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
76
|
+
buffer.push(event);
|
|
77
|
+
estimatedSize += JSON.stringify(event).length + 1;
|
|
78
|
+
if (mode === "upload") {
|
|
79
|
+
// In upload mode, flush when chunk is large enough
|
|
80
|
+
if (estimatedSize >= MAX_CHUNK_SIZE) {
|
|
81
|
+
flush();
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
scheduleFlush();
|
|
85
|
+
}
|
|
52
86
|
}
|
|
87
|
+
// In buffer mode, events just accumulate (cleaned up periodically)
|
|
53
88
|
}
|
|
54
89
|
function scheduleFlush() {
|
|
55
|
-
if (flushTimer)
|
|
90
|
+
if (flushTimer || mode !== "upload")
|
|
56
91
|
return;
|
|
57
92
|
flushTimer = setTimeout(() => {
|
|
58
93
|
flushTimer = null;
|
|
@@ -61,20 +96,19 @@ export function createReplayRecorder(config, transportConfig, onError) {
|
|
|
61
96
|
}
|
|
62
97
|
function preparePayload(batch) {
|
|
63
98
|
const compressed = gzipSync(new TextEncoder().encode(JSON.stringify(batch)));
|
|
64
|
-
const isFullSnapshot = chunkIndex === 0;
|
|
65
99
|
return JSON.stringify({
|
|
66
100
|
sessionId: transportConfig.sessionId,
|
|
67
101
|
chunkIndex,
|
|
68
102
|
data: uint8ToBase64(compressed),
|
|
69
|
-
isFullSnapshot,
|
|
103
|
+
isFullSnapshot: chunkIndex === 0,
|
|
70
104
|
timestamp: Date.now(),
|
|
71
105
|
hasErrors,
|
|
72
106
|
});
|
|
73
107
|
}
|
|
74
108
|
async function flush() {
|
|
75
|
-
if (
|
|
109
|
+
if (buffer.length === 0 || mode !== "upload")
|
|
76
110
|
return;
|
|
77
|
-
const batch =
|
|
111
|
+
const batch = buffer.splice(0);
|
|
78
112
|
estimatedSize = 0;
|
|
79
113
|
try {
|
|
80
114
|
await fetch(`${transportConfig.baseUrl}/v1/ingest/replays`, {
|
|
@@ -93,9 +127,9 @@ export function createReplayRecorder(config, transportConfig, onError) {
|
|
|
93
127
|
}
|
|
94
128
|
}
|
|
95
129
|
function flushBeacon() {
|
|
96
|
-
if (
|
|
130
|
+
if (buffer.length === 0 || mode !== "upload")
|
|
97
131
|
return;
|
|
98
|
-
const batch =
|
|
132
|
+
const batch = buffer.splice(0);
|
|
99
133
|
estimatedSize = 0;
|
|
100
134
|
try {
|
|
101
135
|
fetch(`${transportConfig.baseUrl}/v1/ingest/replays`, {
|
|
@@ -115,13 +149,64 @@ export function createReplayRecorder(config, transportConfig, onError) {
|
|
|
115
149
|
// Ignore errors during page unload
|
|
116
150
|
}
|
|
117
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Clean up old events from buffer (only in buffer mode)
|
|
154
|
+
* Keeps last 60 seconds of events
|
|
155
|
+
*/
|
|
156
|
+
function cleanupBuffer() {
|
|
157
|
+
if (mode !== "buffer" || buffer.length === 0)
|
|
158
|
+
return;
|
|
159
|
+
const cutoff = Date.now() - BUFFER_DURATION_MS;
|
|
160
|
+
let removeCount = 0;
|
|
161
|
+
// Find how many events to remove (they're chronologically ordered)
|
|
162
|
+
for (const event of buffer) {
|
|
163
|
+
if (event.timestamp < cutoff) {
|
|
164
|
+
removeCount++;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (removeCount > 0) {
|
|
171
|
+
buffer.splice(0, removeCount);
|
|
172
|
+
// Recalculate size estimate
|
|
173
|
+
estimatedSize = buffer.reduce((sum, e) => sum + JSON.stringify(e).length + 1, 0);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Switch from buffer mode to upload mode
|
|
178
|
+
* Flushes the entire buffer and starts uploading
|
|
179
|
+
*/
|
|
180
|
+
function switchToUploadMode() {
|
|
181
|
+
if (mode === "upload")
|
|
182
|
+
return;
|
|
183
|
+
mode = "upload";
|
|
184
|
+
// Stop buffer cleanup
|
|
185
|
+
if (bufferCleanupTimer) {
|
|
186
|
+
clearInterval(bufferCleanupTimer);
|
|
187
|
+
bufferCleanupTimer = null;
|
|
188
|
+
}
|
|
189
|
+
// Flush the buffer immediately
|
|
190
|
+
if (buffer.length > 0) {
|
|
191
|
+
flush();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
118
194
|
function start() {
|
|
119
|
-
if (isRecording
|
|
195
|
+
if (isRecording)
|
|
120
196
|
return;
|
|
121
|
-
hasStartedRecording = true;
|
|
122
197
|
isRecording = true;
|
|
198
|
+
// Determine initial mode
|
|
199
|
+
if (shouldUploadImmediately) {
|
|
200
|
+
mode = "upload";
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
mode = "buffer";
|
|
204
|
+
// Start buffer cleanup interval
|
|
205
|
+
bufferCleanupTimer = setInterval(cleanupBuffer, BUFFER_CLEANUP_INTERVAL);
|
|
206
|
+
}
|
|
207
|
+
// Start rrweb recording
|
|
123
208
|
const stop = record(getRecordOptions());
|
|
124
|
-
|
|
209
|
+
stopRecordingFn = stop ?? null;
|
|
125
210
|
// Setup unload handlers
|
|
126
211
|
window.addEventListener("visibilitychange", handleVisibilityChange);
|
|
127
212
|
window.addEventListener("pagehide", handlePageHide);
|
|
@@ -130,41 +215,54 @@ export function createReplayRecorder(config, transportConfig, onError) {
|
|
|
130
215
|
if (!isRecording)
|
|
131
216
|
return;
|
|
132
217
|
isRecording = false;
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
|
|
218
|
+
if (stopRecordingFn) {
|
|
219
|
+
stopRecordingFn();
|
|
220
|
+
stopRecordingFn = null;
|
|
136
221
|
}
|
|
137
222
|
if (flushTimer) {
|
|
138
223
|
clearTimeout(flushTimer);
|
|
139
224
|
flushTimer = null;
|
|
140
225
|
}
|
|
141
|
-
|
|
142
|
-
|
|
226
|
+
if (bufferCleanupTimer) {
|
|
227
|
+
clearInterval(bufferCleanupTimer);
|
|
228
|
+
bufferCleanupTimer = null;
|
|
229
|
+
}
|
|
230
|
+
// Final flush only if in upload mode
|
|
231
|
+
if (mode === "upload") {
|
|
232
|
+
flush();
|
|
233
|
+
}
|
|
143
234
|
window.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
144
235
|
window.removeEventListener("pagehide", handlePageHide);
|
|
145
236
|
}
|
|
146
237
|
function handleVisibilityChange() {
|
|
147
|
-
if (document.visibilityState === "hidden") {
|
|
238
|
+
if (document.visibilityState === "hidden" && mode === "upload") {
|
|
148
239
|
flushBeacon();
|
|
149
240
|
}
|
|
150
241
|
}
|
|
151
242
|
function handlePageHide() {
|
|
152
|
-
|
|
243
|
+
if (mode === "upload") {
|
|
244
|
+
flushBeacon();
|
|
245
|
+
}
|
|
153
246
|
}
|
|
247
|
+
/**
|
|
248
|
+
* Mark that an error occurred in this session
|
|
249
|
+
* If in buffer mode, switches to upload mode (subject to onErrorSampleRate)
|
|
250
|
+
*/
|
|
154
251
|
function markError() {
|
|
155
252
|
hasErrors = true;
|
|
156
|
-
//
|
|
157
|
-
if (
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
253
|
+
// If already uploading, nothing to do
|
|
254
|
+
if (mode === "upload")
|
|
255
|
+
return;
|
|
256
|
+
// Check if we should upgrade to upload mode
|
|
257
|
+
const onErrorRate = config.onErrorSampleRate ?? 1.0;
|
|
258
|
+
if (Math.random() < onErrorRate) {
|
|
259
|
+
wasUpgradedByError = true;
|
|
260
|
+
switchToUploadMode();
|
|
261
|
+
onError?.();
|
|
164
262
|
}
|
|
165
263
|
}
|
|
166
|
-
// Auto-start
|
|
167
|
-
if (config.enabled
|
|
264
|
+
// Auto-start recording when enabled
|
|
265
|
+
if (config.enabled) {
|
|
168
266
|
// Defer to next tick to ensure DOM is ready
|
|
169
267
|
setTimeout(start, 0);
|
|
170
268
|
}
|
|
@@ -173,7 +271,10 @@ export function createReplayRecorder(config, transportConfig, onError) {
|
|
|
173
271
|
stop,
|
|
174
272
|
markError,
|
|
175
273
|
isRecording: () => isRecording,
|
|
274
|
+
isUploading: () => mode === "upload",
|
|
176
275
|
wasUpgradedByError: () => wasUpgradedByError,
|
|
276
|
+
/** Force switch to upload mode (for manual control) */
|
|
277
|
+
startUploading: switchToUploadMode,
|
|
177
278
|
};
|
|
178
279
|
}
|
|
179
280
|
//# sourceMappingURL=replay.js.map
|
package/dist/replay.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;
|
|
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.7.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",
|
|
@@ -14,9 +14,7 @@
|
|
|
14
14
|
"types": "./dist/index.d.ts"
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
|
-
"files": [
|
|
18
|
-
"dist"
|
|
19
|
-
],
|
|
17
|
+
"files": ["dist"],
|
|
20
18
|
"scripts": {
|
|
21
19
|
"build": "tsc",
|
|
22
20
|
"dev": "tsc --watch",
|