@multiplayer-app/session-recorder-browser 1.2.27 → 1.2.28
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 +114 -31
- package/dist/browser/index.js +105 -247
- package/dist/browser/index.js.map +1 -1
- package/dist/exporters/index.js +1 -1
- package/dist/index.js +105 -236
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +105 -242
- package/dist/index.umd.js.map +1 -1
- package/dist/patch/fetch.js +59 -10
- package/dist/patch/fetch.js.map +1 -1
- package/dist/sessionWidget/index.d.ts +0 -1
- package/dist/sessionWidget/index.d.ts.map +1 -1
- package/dist/sessionWidget/index.js +0 -1
- package/dist/sessionWidget/index.js.map +1 -1
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -221,10 +221,37 @@ SessionRecorder.setSessionAttributes({
|
|
|
221
221
|
// if you're not using widget (see: `showWidget: true/false`)
|
|
222
222
|
// then you can programatically control the session recorder
|
|
223
223
|
// by using the methods below
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
SessionRecorder.
|
|
227
|
-
|
|
224
|
+
|
|
225
|
+
// Option A: fire-and-forget (simple)
|
|
226
|
+
// SessionRecorder.start()
|
|
227
|
+
// ... later ...
|
|
228
|
+
// SessionRecorder.stop('Finished session')
|
|
229
|
+
|
|
230
|
+
// Option B: wire up your own UI controls
|
|
231
|
+
const startButton = document.getElementById('start')
|
|
232
|
+
const pauseButton = document.getElementById('pause')
|
|
233
|
+
const resumeButton = document.getElementById('resume')
|
|
234
|
+
const stopButton = document.getElementById('stop')
|
|
235
|
+
|
|
236
|
+
startButton?.addEventListener('click', () => {
|
|
237
|
+
SessionRecorder.start()
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
startContinuousButton?.addEventListener('click', () => {
|
|
241
|
+
SessionRecorder.start(SessionType.CONTINUOUS)
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
pauseButton?.addEventListener('click', () => {
|
|
245
|
+
SessionRecorder.pause()
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
resumeButton?.addEventListener('click', () => {
|
|
249
|
+
SessionRecorder.resume()
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
stopButton?.addEventListener('click', () => {
|
|
253
|
+
SessionRecorder.stop('Finished session') // optional reason
|
|
254
|
+
})
|
|
228
255
|
```
|
|
229
256
|
|
|
230
257
|
### Continuous session recording
|
|
@@ -246,17 +273,28 @@ SessionRecorder.setSessionAttributes({
|
|
|
246
273
|
// if you're not using widget (see: `showWidget: true/false`)
|
|
247
274
|
// then you can programatically control the session recorder
|
|
248
275
|
// by using the methods below
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
//
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
//
|
|
276
|
+
// Option A: fire-and-forget (simple)
|
|
277
|
+
// SessionRecorder.start(SessionType.CONTINUOUS)
|
|
278
|
+
// ... later ...
|
|
279
|
+
// SessionRecorder.save()
|
|
280
|
+
// ... later ...
|
|
281
|
+
// SessionRecorder.stop('Finished session')
|
|
282
|
+
// Option B: wire up your own UI controls
|
|
283
|
+
const startContinuousButton = document.getElementById('start-continuous')
|
|
284
|
+
const saveButton = document.getElementById('save')
|
|
285
|
+
const stopButton = document.getElementById('stop')
|
|
286
|
+
|
|
287
|
+
startContinuousButton?.addEventListener('click', () => {
|
|
288
|
+
SessionRecorder.start(SessionType.CONTINUOUS)
|
|
289
|
+
})
|
|
256
290
|
|
|
257
|
-
|
|
291
|
+
saveButton?.addEventListener('click', () => {
|
|
292
|
+
SessionRecorder.save()
|
|
293
|
+
})
|
|
258
294
|
|
|
259
|
-
|
|
295
|
+
stopButton?.addEventListener('click', () => {
|
|
296
|
+
SessionRecorder.stop('Finished session') // optional reason
|
|
297
|
+
})
|
|
260
298
|
```
|
|
261
299
|
|
|
262
300
|
### Capture exceptions
|
|
@@ -361,20 +399,64 @@ export default function MyApp() {
|
|
|
361
399
|
}
|
|
362
400
|
```
|
|
363
401
|
|
|
402
|
+
### Next.js 15.3+ (App Router) — instrumentation-client.ts
|
|
403
|
+
|
|
404
|
+
On Next.js 15.3+ you can initialize the Session Recorder as early as possible with `src/instrumentation-client.ts|js`, which runs before hydration. You can also export `onRouterTransitionStart` to observe navigation. See the official docs: [instrumentation-client.ts](https://nextjs.org/docs/app/api-reference/file-conventions/instrumentation-client).
|
|
405
|
+
|
|
406
|
+
1. Create `app/instrumentation-client.ts|js`:
|
|
407
|
+
|
|
408
|
+
```ts
|
|
409
|
+
import SessionRecorder from '@multiplayer-app/session-recorder-browser'
|
|
410
|
+
|
|
411
|
+
// Initialize as early as possible (before hydration)
|
|
412
|
+
try {
|
|
413
|
+
SessionRecorder.init({
|
|
414
|
+
application: 'my-next-app',
|
|
415
|
+
version: '1.0.0',
|
|
416
|
+
environment: process.env.NEXT_PUBLIC_ENVIRONMENT ?? 'production',
|
|
417
|
+
apiKey: process.env.NEXT_PUBLIC_MULTIPLAYER_API_KEY!,
|
|
418
|
+
showWidget: true,
|
|
419
|
+
// If your APIs are on different origins, add them so OTLP headers are propagated
|
|
420
|
+
// format: string | RegExp | Array
|
|
421
|
+
propagateTraceHeaderCorsUrls: [new RegExp('https://api.example.com', 'i')]
|
|
422
|
+
})
|
|
423
|
+
} catch (error) {
|
|
424
|
+
// Keep instrumentation resilient
|
|
425
|
+
console.warn('[SessionRecorder] init failed in instrumentation-client:', error)
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Optional: Next.js will call this when navigation begins
|
|
429
|
+
export function onRouterTransitionStart(url: string, navigationType: 'push' | 'replace' | 'traverse') {
|
|
430
|
+
try {
|
|
431
|
+
SessionRecorder.navigation.record({
|
|
432
|
+
path: url || '/',
|
|
433
|
+
navigationType,
|
|
434
|
+
framework: 'nextjs',
|
|
435
|
+
source: 'instrumentation-client'
|
|
436
|
+
})
|
|
437
|
+
} catch (error) {
|
|
438
|
+
console.warn('[SessionRecorder] navigation record failed:', error)
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
364
443
|
## Note
|
|
365
444
|
|
|
366
445
|
If frontend domain doesn't match to backend one, set backend domain to `propagateTraceHeaderCorsUrls` parameter:
|
|
367
446
|
|
|
368
447
|
```javascript
|
|
369
448
|
import SessionRecorder from '@multiplayer-app/session-recorder-browser'
|
|
370
|
-
|
|
371
|
-
SessionRecorder.init({
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
})
|
|
449
|
+
try {
|
|
450
|
+
SessionRecorder.init({
|
|
451
|
+
application: 'my-next-app',
|
|
452
|
+
version: '1.0.0',
|
|
453
|
+
environment: process.env.NEXT_PUBLIC_ENVIRONMENT ?? 'production',
|
|
454
|
+
apiKey: process.env.NEXT_PUBLIC_MULTIPLAYER_API_KEY!,
|
|
455
|
+
propagateTraceHeaderCorsUrls: new RegExp(`https://your.backend.api.domain`, 'i')
|
|
456
|
+
})
|
|
457
|
+
} catch (error) {
|
|
458
|
+
console.warn('[SessionRecorder] init failed:', error)
|
|
459
|
+
}
|
|
378
460
|
```
|
|
379
461
|
|
|
380
462
|
If frontend sends api requests to two or more different domains put them to `propagateTraceHeaderCorsUrls` as array:
|
|
@@ -382,16 +464,17 @@ If frontend sends api requests to two or more different domains put them to `pro
|
|
|
382
464
|
```javascript
|
|
383
465
|
import SessionRecorder from '@multiplayer-app/session-recorder-browser'
|
|
384
466
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
467
|
+
try {
|
|
468
|
+
SessionRecorder.init({
|
|
469
|
+
// ... other config ...
|
|
470
|
+
propagateTraceHeaderCorsUrls: [
|
|
471
|
+
new RegExp(`https://your.backend.api.domain`, 'i'),
|
|
472
|
+
new RegExp(`https://another.backend.api.domain`, 'i')
|
|
473
|
+
]
|
|
474
|
+
})
|
|
475
|
+
} catch (error) {
|
|
476
|
+
console.warn('[SessionRecorder] init failed:', error)
|
|
477
|
+
}
|
|
395
478
|
```
|
|
396
479
|
|
|
397
480
|
### Framework notes
|