@sailfish-ai/recorder 1.11.0 → 1.11.3

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.
Files changed (65) hide show
  1. package/README.md +94 -0
  2. package/dist/chunks/{chunkSerializer-ZzIoYlP2.js → chunkSerializer-CV4nkb5-.js} +1 -1
  3. package/dist/chunks/chunkSerializer-CV4nkb5-.js.br +0 -0
  4. package/dist/chunks/chunkSerializer-CV4nkb5-.js.gz +0 -0
  5. package/dist/chunks/{chunkSerializer-CRDpgzTs.js → chunkSerializer-jzbHv2wf.js} +1 -1
  6. package/dist/chunks/chunkSerializer-jzbHv2wf.js.br +0 -0
  7. package/dist/chunks/chunkSerializer-jzbHv2wf.js.gz +0 -0
  8. package/dist/chunks/{index-BQn1Q-2-.js → index-BP-kNUGS.js} +174 -134
  9. package/dist/chunks/index-BP-kNUGS.js.br +0 -0
  10. package/dist/chunks/index-BP-kNUGS.js.gz +0 -0
  11. package/dist/chunks/{index-Dq_tjmkZ.js → index-BynFTRFv.js} +130 -87
  12. package/dist/chunks/index-BynFTRFv.js.br +0 -0
  13. package/dist/chunks/index-BynFTRFv.js.gz +0 -0
  14. package/dist/chunks/rrweb-plugin-performance-record-BYWkWb25.js +188 -0
  15. package/dist/chunks/rrweb-plugin-performance-record-BYWkWb25.js.br +0 -0
  16. package/dist/chunks/rrweb-plugin-performance-record-BYWkWb25.js.gz +0 -0
  17. package/dist/chunks/rrweb-plugin-performance-record-Dekf6xUi.js +186 -0
  18. package/dist/chunks/rrweb-plugin-performance-record-Dekf6xUi.js.br +0 -0
  19. package/dist/chunks/rrweb-plugin-performance-record-Dekf6xUi.js.gz +0 -0
  20. package/dist/constants.js +1 -0
  21. package/dist/constants.js.br +0 -0
  22. package/dist/constants.js.gz +0 -0
  23. package/dist/inAppReportIssueModal/index.js +15 -14
  24. package/dist/inAppReportIssueModal/index.js.br +0 -0
  25. package/dist/inAppReportIssueModal/index.js.gz +0 -0
  26. package/dist/inAppReportIssueModal/integrations.js +56 -4
  27. package/dist/inAppReportIssueModal/integrations.js.br +0 -0
  28. package/dist/inAppReportIssueModal/integrations.js.gz +0 -0
  29. package/dist/index.js +68 -42
  30. package/dist/index.js.br +0 -0
  31. package/dist/index.js.gz +0 -0
  32. package/dist/recorder.cjs +2 -2
  33. package/dist/recorder.cjs.br +0 -0
  34. package/dist/recorder.cjs.gz +0 -0
  35. package/dist/recorder.js +27 -25
  36. package/dist/recorder.js.br +0 -0
  37. package/dist/recorder.js.gz +0 -0
  38. package/dist/recorder.umd.cjs +4994 -4776
  39. package/dist/recorder.umd.cjs.br +0 -0
  40. package/dist/recorder.umd.cjs.gz +0 -0
  41. package/dist/recording.js +33 -3
  42. package/dist/recording.js.br +0 -0
  43. package/dist/recording.js.gz +0 -0
  44. package/dist/sendSailfishMessages.js +4 -0
  45. package/dist/sendSailfishMessages.js.br +0 -0
  46. package/dist/sendSailfishMessages.js.gz +0 -0
  47. package/dist/snippet-auto-init.js +147 -18
  48. package/dist/snippet-auto-init.js.br +0 -0
  49. package/dist/snippet-auto-init.js.gz +0 -0
  50. package/dist/types/constants.d.ts +1 -0
  51. package/dist/types/inAppReportIssueModal/integrations.d.ts +7 -0
  52. package/dist/types/index.d.ts +23 -1
  53. package/dist/types/recording.d.ts +1 -0
  54. package/dist/types/sendSailfishMessages.d.ts +4 -0
  55. package/dist/types/snippet-auto-init.d.ts +30 -0
  56. package/dist/types/types.d.ts +1 -0
  57. package/package.json +7 -4
  58. package/dist/chunks/chunkSerializer-CRDpgzTs.js.br +0 -0
  59. package/dist/chunks/chunkSerializer-CRDpgzTs.js.gz +0 -0
  60. package/dist/chunks/chunkSerializer-ZzIoYlP2.js.br +0 -0
  61. package/dist/chunks/chunkSerializer-ZzIoYlP2.js.gz +0 -0
  62. package/dist/chunks/index-BQn1Q-2-.js.br +0 -0
  63. package/dist/chunks/index-BQn1Q-2-.js.gz +0 -0
  64. package/dist/chunks/index-Dq_tjmkZ.js.br +0 -0
  65. package/dist/chunks/index-Dq_tjmkZ.js.gz +0 -0
package/README.md CHANGED
@@ -140,6 +140,7 @@ await initRecorder({
140
140
  | `deferRecording` | `boolean` | `true` | Defers the initial DOM snapshot until after first paint / idle. |
141
141
  | `chunkSnapshot` | `boolean` | `false` | Yield to the browser every 500 nodes during the initial snapshot (smoother on very large pages). |
142
142
  | `useWsWorker` | `boolean` | `true` | Run the WebSocket sender in a Web Worker. Disable if your CSP blocks `worker-src blob:`. |
143
+ | `capturePerformanceMetrics` | `boolean` | `true` | Capture FCP / LCP / TBT / DCL / LOAD per `page_visit_uuid` via the performance plugin. Set `false` to skip — the plugin is dynamically imported, so opting out also skips loading `web-vitals` and the observers. |
143
144
  | `reportIssueShortcuts` | `ShortcutsConfig` | — | Custom keyboard shortcuts for the report-issue modal. |
144
145
  | `showEngTicketFieldsInReportIssueModalDefault` | `boolean` | `false` | Pre-expand Jira / Linear fields in the in-app issue modal. |
145
146
 
@@ -185,6 +186,98 @@ if (isFunctionSpanTrackingEnabled()) disableFunctionSpanTracking();
185
186
  enableFunctionSpanTracking();
186
187
  ```
187
188
 
189
+ ## Performance metrics
190
+
191
+ The recorder automatically captures real-user performance metrics per page visit and emits them through the same WebSocket pipeline as everything else. No additional configuration is required; capture is skipped in Lighthouse / headless / WebDriver environments to avoid polluting synthetic audits.
192
+
193
+ | Metric | Meaning | Source |
194
+ |---|---|---|
195
+ | **FCP** — First Contentful Paint | Time to first text/image paint. | `web-vitals` `onFCP` |
196
+ | **LCP** — Largest Contentful Paint | Time to the largest above-the-fold element. Closest proxy for "content is visible". | `web-vitals` `onLCP` |
197
+ | **TBT** — Total Blocking Time | Sum of blocking time from long tasks, emitted at `load`. | `PerformanceObserver({type:'longtask', buffered:true})` summing `max(0, duration − 50)` |
198
+ | **DCL** — DOMContentLoaded | `navigation.domContentLoadedEventEnd` (ms since `timeOrigin`). | Navigation Timing Level 2 |
199
+ | **LOAD** — load event | `navigation.loadEventEnd` (ms since `timeOrigin`). | Navigation Timing Level 2 |
200
+
201
+ FCP and LCP re-emit on SPA soft-navigations (via `web-vitals`' native history-API / BFCache support) — each emission is keyed to the current `page_visit_uuid`. TBT, DCL, and LOAD fire once per hard load.
202
+
203
+ Events arrive on the recorder WebSocket with `type: 28` (the numeric id reserved for performance metrics — see `backend/sailfish/rrweb/enums.py` on the Sailfish backend) and the shape:
204
+
205
+ ```json
206
+ {
207
+ "type": 28,
208
+ "timestamp": 1700000000000,
209
+ "sessionId": "<session_id>",
210
+ "page_visit_uuid": "<uuid>",
211
+ "href": "https://example.com/path",
212
+ "data": {
213
+ "plugin": "@sailfish-rrweb/rrweb/performance@1",
214
+ "payload": {
215
+ "metric": "FCP" | "LCP" | "TBT" | "DCL" | "LOAD",
216
+ "value": 1234.5,
217
+ "rating": "good" | "needs-improvement" | "poor",
218
+ "navigationType": "navigate" | "reload" | "back-forward" | "back-forward-cache" | "prerender" | "restore",
219
+ "pageVisitUuid": "<uuid>",
220
+ "href": "https://example.com/path",
221
+ "timestamp": 1700000000000
222
+ }
223
+ }
224
+ }
225
+ ```
226
+
227
+ ### Disabling capture
228
+
229
+ Pass `capturePerformanceMetrics: false` to `initRecorder` to skip the plugin entirely. Because the plugin code is dynamically imported, this also avoids loading `web-vitals` and the long-task observer:
230
+
231
+ ```ts
232
+ initRecorder({
233
+ apiKey: "YOUR_API_KEY",
234
+ capturePerformanceMetrics: false,
235
+ });
236
+ ```
237
+
238
+ ### Benchmarking the plugin's overhead
239
+
240
+ `scripts/bench-perf.js` loads the same page twice — once with capture on, once with capture off — under headless Chrome, and reports CPU, JS heap, FCP/LCP, long-task time, and total load time side-by-side.
241
+
242
+ #### One-time app change (required before running the bench)
243
+
244
+ The bench needs a way to flip capture on/off without rebuilding your app between variants. Wire `?sf_perf=off` in your `initRecorder` call so the URL controls the option — paste this where you call `initRecorder`:
245
+
246
+ ```ts
247
+ const capturePerformanceMetrics =
248
+ new URLSearchParams(window.location.search).get("sf_perf") !== "off";
249
+
250
+ initRecorder({
251
+ apiKey: "YOUR_API_KEY",
252
+ // …your other options…
253
+ capturePerformanceMetrics,
254
+ });
255
+ ```
256
+
257
+ Anything other than `?sf_perf=off` (including the param being absent) leaves capture enabled, so day-to-day usage is unaffected. The toggle exists only so the bench can switch variants by URL.
258
+
259
+ #### Run the bench
260
+
261
+ ```bash
262
+ # 1. Start your app's dev server (separate terminal). Default port 3000.
263
+ npm run start
264
+
265
+ # 2. Run the bench (defaults to http://localhost:3000, 5 runs/variant):
266
+ cd veritas/jsts-frontend
267
+ npm install # first time only — installs puppeteer
268
+ npm run bench-perf
269
+
270
+ # Override URL or sample count:
271
+ npm run bench-perf -- --url http://localhost:3000/some/page --runs 10
272
+
273
+ # Watch the runs in a real browser window (slower, useful for sanity-checking):
274
+ npm run bench-perf -- --headed
275
+ ```
276
+
277
+ Output is a table of medians, p95, and Δ% between the two variants for: wall load time, DCL, load event, FCP, LCP, long-task total, long-task blocking (the TBT proxy), CDP `TaskDuration` / `ScriptDuration` / `LayoutDuration`, and `usedJSHeapSize` (MB).
278
+
279
+ Sample interpretation: a Δ ≤ noise (typically ±5% on the smaller numbers, ±2 MB on heap) means the plugin is essentially free; anything larger is a regression worth profiling.
280
+
188
281
  ## Framework examples
189
282
 
190
283
  ### React / Vite
@@ -311,4 +404,5 @@ ReferenceError: localStorage is not defined
311
404
 
312
405
  ## License
313
406
 
407
+
314
408
  Proprietary. See [sailfishqa.com/terms](https://sailfishqa.com) for terms of service.
@@ -1,4 +1,4 @@
1
- import { y as e } from "./index-Dq_tjmkZ.js";
1
+ import { y as e } from "./index-BynFTRFv.js";
2
2
  async function chunkedSnapshot(t, n, o = {}) {
3
3
  const s = o.chunkSize ?? 500, r = o.maxChunkMs ?? 16, { blockClass: c, blockSelector: a, maskTextClass: i, maskTextSelector: d } = o;
4
4
  let u = 100001, l = 0, N = performance.now();
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const e = require("./index-BQn1Q-2-.js");
3
+ const e = require("./index-BP-kNUGS.js");
4
4
  exports.chunkedSnapshot = async function chunkedSnapshot(t, n, o = {}) {
5
5
  const s = o.chunkSize ?? 500, r = o.maxChunkMs ?? 16, { blockClass: c, blockSelector: a, maskTextClass: i, maskTextSelector: d } = o;
6
6
  let u = 100001, l = 0, N = performance.now();