@sailfish-ai/recorder 1.10.13 → 1.11.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.
Files changed (93) hide show
  1. package/README.md +309 -7
  2. package/dist/babel-plugin-sailfish-source.cjs.br +0 -0
  3. package/dist/babel-plugin-sailfish-source.cjs.gz +0 -0
  4. package/dist/babel-plugin-sailfish-source.js.br +0 -0
  5. package/dist/babel-plugin-sailfish-source.js.gz +0 -0
  6. package/dist/chunkSerializer.js.br +0 -0
  7. package/dist/chunkSerializer.js.gz +0 -0
  8. package/dist/chunks/{chunkSerializer-CodMnuS3.js → chunkSerializer-CRDpgzTs.js} +1 -1
  9. package/dist/chunks/chunkSerializer-CRDpgzTs.js.br +0 -0
  10. package/dist/chunks/chunkSerializer-CRDpgzTs.js.gz +0 -0
  11. package/dist/chunks/{chunkSerializer-Dk1eF3S8.js → chunkSerializer-ZzIoYlP2.js} +1 -1
  12. package/dist/chunks/chunkSerializer-ZzIoYlP2.js.br +0 -0
  13. package/dist/chunks/chunkSerializer-ZzIoYlP2.js.gz +0 -0
  14. package/dist/chunks/{index-DW416eVj.js → index-BQn1Q-2-.js} +36 -32
  15. package/dist/chunks/index-BQn1Q-2-.js.br +0 -0
  16. package/dist/chunks/index-BQn1Q-2-.js.gz +0 -0
  17. package/dist/chunks/{index-DvLh2k6O.js → index-Dq_tjmkZ.js} +30 -26
  18. package/dist/chunks/index-Dq_tjmkZ.js.br +0 -0
  19. package/dist/chunks/index-Dq_tjmkZ.js.gz +0 -0
  20. package/dist/constants.js.br +0 -0
  21. package/dist/constants.js.gz +0 -0
  22. package/dist/deviceInfo.js.br +0 -0
  23. package/dist/deviceInfo.js.gz +0 -0
  24. package/dist/env.js.br +0 -0
  25. package/dist/env.js.gz +0 -0
  26. package/dist/errorInterceptor.js.br +0 -0
  27. package/dist/errorInterceptor.js.gz +0 -0
  28. package/dist/eventStore.js.br +0 -0
  29. package/dist/eventStore.js.gz +0 -0
  30. package/dist/exponentialBackoff.js.br +0 -0
  31. package/dist/exponentialBackoff.js.gz +0 -0
  32. package/dist/fiberHook.js.br +0 -0
  33. package/dist/fiberHook.js.gz +0 -0
  34. package/dist/frameworkDetection.js.br +0 -0
  35. package/dist/frameworkDetection.js.gz +0 -0
  36. package/dist/graphql.js.br +0 -0
  37. package/dist/graphql.js.gz +0 -0
  38. package/dist/headlessDetection.js.br +0 -0
  39. package/dist/headlessDetection.js.gz +0 -0
  40. package/dist/inAppReportIssueModal/index.js.br +0 -0
  41. package/dist/inAppReportIssueModal/index.js.gz +0 -0
  42. package/dist/inAppReportIssueModal/integrations.js.br +0 -0
  43. package/dist/inAppReportIssueModal/integrations.js.gz +0 -0
  44. package/dist/inAppReportIssueModal/state.js.br +0 -0
  45. package/dist/inAppReportIssueModal/state.js.gz +0 -0
  46. package/dist/inAppReportIssueModal/ui.js.br +0 -0
  47. package/dist/inAppReportIssueModal/ui.js.gz +0 -0
  48. package/dist/index.js +44 -34
  49. package/dist/index.js.br +0 -0
  50. package/dist/index.js.gz +0 -0
  51. package/dist/notifyEventStore.js.br +0 -0
  52. package/dist/notifyEventStore.js.gz +0 -0
  53. package/dist/recorder.cjs +1 -1
  54. package/dist/recorder.cjs.br +0 -0
  55. package/dist/recorder.cjs.gz +0 -0
  56. package/dist/recorder.js +1 -1
  57. package/dist/recorder.js.br +0 -0
  58. package/dist/recorder.js.gz +0 -0
  59. package/dist/recorder.umd.cjs +8883 -0
  60. package/dist/recorder.umd.cjs.br +0 -0
  61. package/dist/recorder.umd.cjs.gz +0 -0
  62. package/dist/recording.js.br +0 -0
  63. package/dist/recording.js.gz +0 -0
  64. package/dist/segmentHelpers.js.br +0 -0
  65. package/dist/segmentHelpers.js.gz +0 -0
  66. package/dist/sendSailfishMessages.js.br +0 -0
  67. package/dist/sendSailfishMessages.js.gz +0 -0
  68. package/dist/session.js.br +0 -0
  69. package/dist/session.js.gz +0 -0
  70. package/dist/snippet-auto-init.js +44 -0
  71. package/dist/snippet-auto-init.js.br +0 -0
  72. package/dist/snippet-auto-init.js.gz +0 -0
  73. package/dist/sourceLocation.js.br +0 -0
  74. package/dist/sourceLocation.js.gz +0 -0
  75. package/dist/types/index.d.ts +3 -1
  76. package/dist/types/snippet-auto-init.d.ts +1 -0
  77. package/dist/types/umd-entry.d.ts +9 -0
  78. package/dist/umd-entry.js +11 -0
  79. package/dist/utils.js.br +0 -0
  80. package/dist/utils.js.gz +0 -0
  81. package/dist/uuid.js.br +0 -0
  82. package/dist/uuid.js.gz +0 -0
  83. package/dist/websocket.js.br +0 -0
  84. package/dist/websocket.js.gz +0 -0
  85. package/package.json +9 -5
  86. package/dist/chunks/chunkSerializer-CodMnuS3.js.br +0 -0
  87. package/dist/chunks/chunkSerializer-CodMnuS3.js.gz +0 -0
  88. package/dist/chunks/chunkSerializer-Dk1eF3S8.js.br +0 -0
  89. package/dist/chunks/chunkSerializer-Dk1eF3S8.js.gz +0 -0
  90. package/dist/chunks/index-DW416eVj.js.br +0 -0
  91. package/dist/chunks/index-DW416eVj.js.gz +0 -0
  92. package/dist/chunks/index-DvLh2k6O.js.br +0 -0
  93. package/dist/chunks/index-DvLh2k6O.js.gz +0 -0
package/README.md CHANGED
@@ -1,12 +1,314 @@
1
- # JS/TS Record-Only Package
1
+ # @sailfish-ai/recorder
2
2
 
3
- ## Sailfish AI's frontend recorder
3
+ Frontend session recorder for [Sailfish](https://app.sailfishqa.com). Captures the DOM, console, network activity, errors, and user interactions so replay is available in the dashboard alongside the backend traces Sailfish already collects.
4
4
 
5
- ## Trigger build - 31112025 1150PM GMT+4
5
+ Works in any browser JavaScript or TypeScript app — React, Vue, Angular, Next.js, Nuxt, Svelte, Shopify, plain HTML. Ships UMD, ESM, and CJS builds; can be installed via npm or loaded as a single `<script>` tag from a CDN.
6
6
 
7
- ## Trigger build - 06112025 0854PM GMT+4
7
+ - **Signup / dashboard:** https://app.sailfishqa.com
8
+ - **Source:** [github.com/SailfishAI/sailfish](https://github.com/SailfishAI/sailfish) (internal)
8
9
 
9
- ## Trigger build - 11182025 1125AM GMT+4
10
+ ## Installation
10
11
 
11
- ## Trigger build - 02122025 0330PM GMT+4
12
- ## Trigger build - 26122025 0330PM GMT+4
12
+ ```bash
13
+ npm install @sailfish-ai/recorder
14
+ # or
15
+ yarn add @sailfish-ai/recorder
16
+ # or
17
+ pnpm add @sailfish-ai/recorder
18
+ ```
19
+
20
+ ## Quick start
21
+
22
+ ### ES modules / bundler (Vite, Webpack, Rollup, Next.js, etc.)
23
+
24
+ ```ts
25
+ import { initRecorder } from "@sailfish-ai/recorder";
26
+
27
+ // SSR guard — don't touch browser storage during server render.
28
+ if (typeof window !== "undefined") {
29
+ initRecorder({ apiKey: "YOUR_API_KEY" });
30
+ }
31
+ ```
32
+
33
+ ### CDN (single `<script>` tag, auto-init)
34
+
35
+ The UMD build auto-initializes from `data-*` attributes on the script tag — zero JavaScript needed on your side:
36
+
37
+ ```html
38
+ <script
39
+ src="https://cdn.jsdelivr.net/npm/@sailfish-ai/recorder@1/dist/recorder.umd.cjs"
40
+ data-api-key="YOUR_API_KEY"
41
+ data-service-identifier="my-app"
42
+ data-service-version="1.0.0"
43
+ crossorigin="anonymous"
44
+ ></script>
45
+ ```
46
+
47
+ Drop that into the `<head>` of any HTML page (Shopify, Webflow, WordPress, static sites, server-rendered templates) and recording starts automatically. Errors inside the recorder are swallowed — Sailfish never breaks the host page.
48
+
49
+ ### CDN (manual init)
50
+
51
+ If you'd rather call `initRecorder` yourself — for example to pass runtime options — omit `data-api-key` and use the `SailfishRecorder` global:
52
+
53
+ ```html
54
+ <script
55
+ src="https://cdn.jsdelivr.net/npm/@sailfish-ai/recorder@1/dist/recorder.umd.cjs"
56
+ crossorigin="anonymous"
57
+ ></script>
58
+ <script>
59
+ window.SailfishRecorder.initRecorder({
60
+ apiKey: "YOUR_API_KEY",
61
+ serviceIdentifier: "my-app",
62
+ serviceVersion: "1.0.0",
63
+ });
64
+ </script>
65
+ ```
66
+
67
+ ## CDN delivery and bundle size
68
+
69
+ `@sailfish-ai/recorder` is published publicly on npm, so any major JavaScript CDN can serve it — jsDelivr, unpkg, esm.sh, esm.run, jspm. The UMD bundle is self-contained (no external dependencies to resolve) and exposes the `SailfishRecorder` global.
70
+
71
+ ### What the browser actually downloads
72
+
73
+ The UMD file on disk is ~473 KB, but browsers never see that number. CDNs do HTTP content-negotiated compression: the browser advertises `Accept-Encoding: gzip, br` automatically, and the CDN returns the file with a `Content-Encoding: br` (or `gzip`) header. The browser decompresses on arrival. **No configuration needed by the site author — just include the `<script>` tag.**
74
+
75
+ | Encoding (delivered) | Size on the wire | Browsers |
76
+ |---|---:|---|
77
+ | **Brotli** (`br`) | **~84 KB** | Chrome, Firefox, Safari, Edge (all current versions) |
78
+ | **Gzip** (`gzip`) | **~117 KB** | Older browsers, curl without `--compressed`, crawlers |
79
+ | Uncompressed | ~473 KB | Only if the client explicitly sends `Accept-Encoding: identity` |
80
+
81
+ jsDelivr and unpkg set `Vary: Accept-Encoding`, so each encoding is cached independently at the edge — the first user per encoding per region pays a small compression cost, every subsequent request is served straight from the cache.
82
+
83
+ ### CDN options
84
+
85
+ | CDN | URL pattern | Notes |
86
+ |---|---|---|
87
+ | **jsDelivr** (recommended) | `https://cdn.jsdelivr.net/npm/@sailfish-ai/recorder@1/dist/recorder.umd.cjs` | Fastest p50 in our load tests, brotli over HTTP/2, global anycast. Cache stays warm across versions. |
88
+ | **unpkg** | `https://unpkg.com/@sailfish-ai/recorder@1/dist/recorder.umd.cjs` | Cloudflare-backed, also fast. Good fallback. |
89
+ | **esm.run** / **jsDelivr `+esm`** | `https://esm.run/@sailfish-ai/recorder@1` | Single-file ESM for `<script type="module">`. Pre-bundled (~31 KB brotli over-wire). |
90
+ | **esm.sh** | `https://esm.sh/@sailfish-ai/recorder@1` | ESM with separate dependency module graph (multiple HTTP requests — slower first load). |
91
+
92
+ Version pinning:
93
+
94
+ - `@sailfish-ai/recorder@1` — track the latest `1.x` (recommended; gets bug fixes).
95
+ - `@sailfish-ai/recorder@1.10.11` — pin exactly (longest CDN cache TTL; use for absolute stability).
96
+
97
+ ### Which build format should I use?
98
+
99
+ | You're building… | Use | How |
100
+ |---|---|---|
101
+ | Plain HTML, Shopify, Webflow, WordPress, server-rendered template | **UMD via CDN** | `<script src="…/recorder.umd.cjs" data-api-key="…">` |
102
+ | React / Vue / Next.js / Svelte app with a bundler | **ESM via npm** | `import { initRecorder } from "@sailfish-ai/recorder"` |
103
+ | Modern browser app with native ESM (no bundler) | **ESM via CDN** | `<script type="module">import { initRecorder } from "https://esm.run/@sailfish-ai/recorder@1"` |
104
+ | Node.js CommonJS (unusual — this is a browser package) | **CJS via npm** | `const { initRecorder } = require("@sailfish-ai/recorder")` |
105
+
106
+ ## API
107
+
108
+ All functions are top-level exports of `@sailfish-ai/recorder`. On the CDN build they're also available as `window.SailfishRecorder.*`.
109
+
110
+ ### `initRecorder(options)`
111
+
112
+ Initialize the recorder. Returns `Promise<void>`. Safe to call once per page load; concurrent calls are coalesced.
113
+
114
+ ```ts
115
+ await initRecorder({
116
+ apiKey: "YOUR_API_KEY", // required
117
+ serviceIdentifier: "my-app", // optional — your app name in the dashboard
118
+ serviceVersion: "1.0.0", // optional — your app version for filtering sessions
119
+ });
120
+ ```
121
+
122
+ #### Options
123
+
124
+ | Field | Type | Default | Purpose |
125
+ |---|---|---|---|
126
+ | `apiKey` | `string` | **required** | Your Sailfish API key (from the dashboard). |
127
+ | `backendApi` | `string` | `https://api-service.sailfishqa.com` | Backend endpoint. Override for self-hosted Sailfish deployments. |
128
+ | `serviceIdentifier` | `string` | — | Application name shown in the dashboard. |
129
+ | `serviceVersion` | `string` | — | Application version, used for session filtering. |
130
+ | `gitSha` | `string` | auto-detected | Build commit SHA for session filtering. |
131
+ | `serviceAdditionalMetadata` | `Record<string, any>` | — | Arbitrary metadata attached to every session. |
132
+ | `domainsToPropagateHeaderTo` | `string[]` | `[]` | Domains (wildcards allowed) that receive the `X-Sf3-Rid` tracing header, letting Sailfish join frontend sessions with backend traces. |
133
+ | `domainsToNotPropagateHeaderTo` | `string[]` | `[]` | Domains to exclude, appended to the built-in denylist. |
134
+ | `enableFiberTracking` | `boolean` | `false` | Adds `data-sf-source="file.tsx:42"` attributes to DOM nodes for source-mapped coverage (React only). Pairs with the Babel plugin. |
135
+ | `enableIpTracking` | `boolean` | `false` | Fetches the visitor IP asynchronously for session metadata. |
136
+ | `captureStreamingResponseBody` | `boolean` | `true` | Capture prefixes of streaming (SSE, ndjson, chunked) responses. |
137
+ | `captureResponseBodyMaxMb` | `number` | `10` | Max non-streaming response body to capture, in MB. `0` disables body capture. |
138
+ | `captureStreamPrefixKb` | `number` | `64` | Max streaming-body prefix to capture, in KB. |
139
+ | `captureStreamTimeoutMs` | `number` | `10000` | Timeout for reading streaming bodies, in ms. |
140
+ | `deferRecording` | `boolean` | `true` | Defers the initial DOM snapshot until after first paint / idle. |
141
+ | `chunkSnapshot` | `boolean` | `false` | Yield to the browser every 500 nodes during the initial snapshot (smoother on very large pages). |
142
+ | `useWsWorker` | `boolean` | `true` | Run the WebSocket sender in a Web Worker. Disable if your CSP blocks `worker-src blob:`. |
143
+ | `reportIssueShortcuts` | `ShortcutsConfig` | — | Custom keyboard shortcuts for the report-issue modal. |
144
+ | `showEngTicketFieldsInReportIssueModalDefault` | `boolean` | `false` | Pre-expand Jira / Linear fields in the in-app issue modal. |
145
+
146
+ ### `identify(userId, traits?, overwrite?)`
147
+
148
+ Tag the current session with a user identity. Subsequent calls with the same `userId` are deduplicated.
149
+
150
+ ```ts
151
+ identify("user_abc123", { email: "dana@example.com", plan: "pro" });
152
+ ```
153
+
154
+ ### `addOrUpdateMetadata(metadata)`
155
+
156
+ Attach arbitrary metadata to the current session. Values are merged with any previously-set metadata.
157
+
158
+ ```ts
159
+ addOrUpdateMetadata({ experiment: "pricing-v2", cohort: "B" });
160
+ ```
161
+
162
+ ### `getOrSetSessionId()`
163
+
164
+ Return the current session ID (generates one if none exists). Useful for correlating logs on your side with Sailfish recordings.
165
+
166
+ ```ts
167
+ const sessionId = getOrSetSessionId();
168
+ console.log("Sailfish session:", sessionId);
169
+ ```
170
+
171
+ ### `openReportIssueModal(options?)`
172
+
173
+ Open the in-app bug-report modal. If `showEngTicketFields: true` is passed (or set at init), Jira / Linear fields are shown by default.
174
+
175
+ ```ts
176
+ openReportIssueModal({ showEngTicketFields: true });
177
+ ```
178
+
179
+ ### `enableFunctionSpanTracking()` / `disableFunctionSpanTracking()` / `isFunctionSpanTrackingEnabled()`
180
+
181
+ Toggle per-session backend function-span tracing at runtime.
182
+
183
+ ```ts
184
+ if (isFunctionSpanTrackingEnabled()) disableFunctionSpanTracking();
185
+ enableFunctionSpanTracking();
186
+ ```
187
+
188
+ ## Framework examples
189
+
190
+ ### React / Vite
191
+
192
+ ```tsx
193
+ import { useEffect } from "react";
194
+ import { initRecorder } from "@sailfish-ai/recorder";
195
+
196
+ export function App() {
197
+ useEffect(() => {
198
+ initRecorder({ apiKey: import.meta.env.VITE_SAILFISH_KEY });
199
+ }, []);
200
+ return <Routes />;
201
+ }
202
+ ```
203
+
204
+ ### Next.js (App Router)
205
+
206
+ `'use client'` is required — `initRecorder` touches `localStorage` and must run in the browser.
207
+
208
+ ```tsx
209
+ "use client";
210
+ import { useEffect } from "react";
211
+ import { initRecorder } from "@sailfish-ai/recorder";
212
+
213
+ export function SailfishProvider({ children }: { children: React.ReactNode }) {
214
+ useEffect(() => {
215
+ initRecorder({ apiKey: process.env.NEXT_PUBLIC_SAILFISH_KEY! });
216
+ }, []);
217
+ return <>{children}</>;
218
+ }
219
+ ```
220
+
221
+ Place `<SailfishProvider>` in `app/layout.tsx`.
222
+
223
+ ### Plain HTML / Shopify / Webflow / WordPress
224
+
225
+ ```html
226
+ <script
227
+ src="https://cdn.jsdelivr.net/npm/@sailfish-ai/recorder@1/dist/recorder.umd.cjs"
228
+ data-api-key="YOUR_API_KEY"
229
+ crossorigin="anonymous"
230
+ ></script>
231
+ ```
232
+
233
+ ## Babel plugin — source-mapped coverage
234
+
235
+ `@sailfish-ai/recorder/babel-plugin` injects `data-sf-source="file.tsx:42"` attributes into your JSX at compile time so clicks and views in the Sailfish dashboard link back to the exact source line.
236
+
237
+ ### Vite + React
238
+
239
+ ```ts
240
+ // vite.config.ts
241
+ import { defineConfig } from "vite";
242
+ import react from "@vitejs/plugin-react";
243
+ import sailfishSourcePlugin from "@sailfish-ai/recorder/babel-plugin";
244
+
245
+ export default defineConfig({
246
+ plugins: [
247
+ react({ babel: { plugins: [sailfishSourcePlugin] } }),
248
+ ],
249
+ });
250
+ ```
251
+
252
+ ### Babel / CRA / Next.js
253
+
254
+ ```js
255
+ // babel.config.js
256
+ const sailfishSourcePlugin = require("@sailfish-ai/recorder/babel-plugin");
257
+ module.exports = {
258
+ plugins: [[sailfishSourcePlugin, { allElements: false }]],
259
+ };
260
+ ```
261
+
262
+ Pass `{ allElements: true }` to annotate every element (default annotates only interactive ones — buttons, inputs, links, form controls).
263
+
264
+ ## Configuration recipes
265
+
266
+ ```ts
267
+ // Self-hosted Sailfish backend
268
+ initRecorder({
269
+ apiKey: "YOUR_KEY",
270
+ backendApi: "https://sailfish.your-company.com",
271
+ });
272
+
273
+ // Join frontend sessions with backend traces across your API
274
+ initRecorder({
275
+ apiKey: "YOUR_KEY",
276
+ domainsToPropagateHeaderTo: ["api.mycompany.com", "*.mycompany.com"],
277
+ });
278
+
279
+ // Strict CSP (no blob: workers)
280
+ initRecorder({
281
+ apiKey: "YOUR_KEY",
282
+ useWsWorker: false,
283
+ });
284
+
285
+ // Capture more of streaming LLM response bodies
286
+ initRecorder({
287
+ apiKey: "YOUR_KEY",
288
+ captureStreamingResponseBody: true,
289
+ captureStreamPrefixKb: 256,
290
+ captureStreamTimeoutMs: 30_000,
291
+ });
292
+ ```
293
+
294
+ ## SSR / Next.js note
295
+
296
+ `initRecorder` touches `localStorage`, `sessionStorage`, and `window` — none of which exist during server-side rendering. Always guard with `typeof window !== "undefined"` or call from `useEffect` / `onMount` / client-only equivalents.
297
+
298
+ Without a guard, frameworks like Next.js (on Vercel) will fail at build/SSR with:
299
+
300
+ ```
301
+ ReferenceError: localStorage is not defined
302
+ ```
303
+
304
+ ## Troubleshooting
305
+
306
+ - **`window.SailfishRecorder` is undefined after the `<script>` loads.** You're probably using an ESM URL (`esm.run`, `esm.sh`) with a non-module `<script>`. Either use the UMD URL (`/dist/recorder.umd.cjs`) or add `type="module"` to the tag.
307
+ - **`Refused to create a worker from 'blob:...'` in the console.** Your Content-Security-Policy blocks blob workers. Either allow `worker-src blob:` or init with `useWsWorker: false`.
308
+ - **Recordings never reach the dashboard.** Verify the `apiKey` matches the project in the Sailfish dashboard, and that your ad-blocker isn't blocking `api-service.sailfishqa.com`. Open your browser devtools network tab and filter by `sailfish`.
309
+ - **SSR error: `localStorage is not defined`.** See the SSR note above — `initRecorder` must run in a browser-only code path.
310
+ - **Mixed-content warnings.** All Sailfish CDN and API URLs are HTTPS; make sure your page also serves over HTTPS.
311
+
312
+ ## License
313
+
314
+ Proprietary. See [sailfishqa.com/terms](https://sailfishqa.com) for terms of service.
Binary file
Binary file
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const e = require("./index-DW416eVj.js");
3
+ const e = require("./index-BQn1Q-2-.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();
@@ -1,4 +1,4 @@
1
- import { y as e } from "./index-DvLh2k6O.js";
1
+ import { y as e } from "./index-Dq_tjmkZ.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();
@@ -391,7 +391,7 @@ function initializeWebSocket(t2, n2, i2, o2, s2 = false) {
391
391
  const t3 = new URL(e2);
392
392
  return `${t3.hostname}${t3.port ? `:${t3.port}` : ""}`;
393
393
  })(t2);
394
- let a2 = `${"https:" === new URL(t2).protocol ? "wss" : "ws"}://${r2}/ws/notify/?apiKey=${n2}&sessionId=${i2}&sender=JS%2FTS&version=1.10.13`;
394
+ let a2 = `${"https:" === new URL(t2).protocol ? "wss" : "ws"}://${r2}/ws/notify/?apiKey=${n2}&sessionId=${i2}&sender=JS%2FTS&version=1.11.0`;
395
395
  if (o2 && (a2 += `&envValue=${encodeURIComponent(o2)}`), w = s2 ? (function tryCreateWsWorker() {
396
396
  if ("undefined" == typeof Worker) return null;
397
397
  try {
@@ -447,11 +447,11 @@ function getFuncSpanHeader() {
447
447
  return { name: "X-Sf3-FunctionSpanCaptureOverride", value: "1-1-10-10-1-1.0-1-0-0" };
448
448
  }
449
449
  const j = Object.freeze(Object.defineProperty({ __proto__: null, clearStaleFuncSpanState, disableFunctionSpanTracking, enableFunctionSpanTracking, ensureHrefCache, flushBufferedEvents, getCachedHref, getCachedHrefNoQuery, getFuncSpanHeader, initializeFunctionSpanTrackingFromApi, initializeWebSocket, isFunctionSpanTrackingEnabled, onNavigationChange, restoreFuncSpanState, sendEvent, sendMessage }, Symbol.toStringTag, { value: "Module" }));
450
- let O = null, U = null;
451
- let z = null;
450
+ let O = null, z = null;
451
+ let U = null;
452
452
  const H = ["https://api.ipify.org?format=json", "https://api.ip.sb/jsonip", "https://api4.my-ip.io/ip.json"];
453
453
  function fetchAndSendIp(e2) {
454
- z !== e2 && (z = e2, (async () => {
454
+ U !== e2 && (U = e2, (async () => {
455
455
  for (const e3 of H) try {
456
456
  const t2 = new AbortController(), n2 = setTimeout(() => t2.abort(), 5e3), i2 = await fetch(e3, { signal: t2.signal });
457
457
  if (clearTimeout(n2), !i2.ok) continue;
@@ -459,9 +459,9 @@ function fetchAndSendIp(e2) {
459
459
  if (s2 && "string" == typeof s2 && s2.length <= 45) return void sendMessage({ type: "visitorIp", ip: s2, timestamp: exports.nowTimestamp() });
460
460
  } catch {
461
461
  }
462
- z = null;
462
+ U = null;
463
463
  })().catch(() => {
464
- z = null;
464
+ U = null;
465
465
  }));
466
466
  }
467
467
  let N = null;
@@ -711,7 +711,7 @@ async function initializeRecording(e2, n2, i2, o2, s2, r2 = true, a2 = false, l2
711
711
  }
712
712
  const { record: n4 } = await import("@sailfish-rrweb/rrweb-record-only");
713
713
  if (Q = n4, await yieldToMain(), l2) {
714
- const { chunkedSnapshot: i3 } = await Promise.resolve().then(() => require("./chunkSerializer-CodMnuS3.js")), o3 = n4.mirror;
714
+ const { chunkedSnapshot: i3 } = await Promise.resolve().then(() => require("./chunkSerializer-CRDpgzTs.js")), o3 = n4.mirror;
715
715
  let s3 = true;
716
716
  const r3 = [];
717
717
  n4({ emit(e3) {
@@ -2211,7 +2211,7 @@ function setupFetchInterceptor(e2 = [], t2 = { captureStreamingResponseBody: tru
2211
2211
  })(e3, i3, o3, d2, u2, s2, c2);
2212
2212
  } });
2213
2213
  }
2214
- async function startRecording({ apiKey: e2, backendApi: t2 = "https://api-service.sailfishqa.com", domainsToPropagateHeaderTo: i2 = [], domainsToNotPropagateHeaderTo: o2 = [], serviceVersion: s2, serviceIdentifier: r2, gitSha: a2, serviceAdditionalMetadata: l2, enableIpTracking: c2, captureStreamingResponseBody: d2 = true, captureResponseBodyMaxMb: u2 = 10, captureStreamPrefixKb: p2 = 64, captureStreamTimeoutMs: f2 = 1e4, enableFiberTracking: m2 = false, deferRecording: h2, deferRecordingStart: y2, chunkSnapshot: b2, useWsWorker: S2 = true }) {
2214
+ async function startRecording({ apiKey: e2, backendApi: t2 = "https://api-service.sailfishqa.com", domainsToPropagateHeaderTo: i2 = [], domainsToNotPropagateHeaderTo: o2 = [], serviceVersion: s2, serviceIdentifier: r2, gitSha: a2, serviceAdditionalMetadata: l2, enableIpTracking: c2, captureStreamingResponseBody: d2 = true, captureResponseBodyMaxMb: u2 = 10, captureStreamPrefixKb: p2 = 64, captureStreamTimeoutMs: f2 = 1e4, enableFiberTracking: m2 = false, deferRecording: h2, deferRecordingStart: y2, chunkSnapshot: b2, useWsWorker: S2 = true, library: v2 }) {
2215
2215
  var _a, _b;
2216
2216
  if ((function isHeadlessOrLighthouse() {
2217
2217
  try {
@@ -2224,10 +2224,10 @@ async function startRecording({ apiKey: e2, backendApi: t2 = "https://api-servic
2224
2224
  return false;
2225
2225
  }
2226
2226
  })()) return;
2227
- const v2 = h2 ?? y2 ?? true, w2 = getOrSetSessionId(), k2 = window.__sailfish_recorder || (window.__sailfish_recorder = {});
2228
- if (k2.sessionId = w2, k2.apiKey = e2, k2.backendApi = t2, k2.serviceAdditionalMetadata = l2, k2.initialized && k2.sessionId === w2 && k2.ws && 1 === k2.ws.readyState) return void trackDomainChangesOnce();
2229
- const x2 = { captureStreamingResponseBody: d2, captureResponseBodyMaxMb: u2, captureStreamPrefixKb: p2, captureStreamTimeoutMs: f2 };
2230
- sessionStorage.getItem("pageVisitUUID") || (sessionStorage.setItem("pageVisitUUID", uuidv4()), invalidateUrlCache()), k2.xhrPatched || (!(function setupXMLHttpRequestInterceptor(e3 = [], t3 = { captureStreamingResponseBody: true, captureResponseBodyMaxMb: 10, captureStreamPrefixKb: 64, captureStreamTimeoutMs: 1e4 }, i3 = []) {
2227
+ const w2 = h2 ?? y2 ?? true, k2 = getOrSetSessionId(), x2 = window.__sailfish_recorder || (window.__sailfish_recorder = {});
2228
+ if (x2.sessionId = k2, x2.apiKey = e2, x2.backendApi = t2, x2.serviceAdditionalMetadata = l2, x2.initialized && x2.sessionId === k2 && x2.ws && 1 === x2.ws.readyState) return void trackDomainChangesOnce();
2229
+ const I2 = { captureStreamingResponseBody: d2, captureResponseBodyMaxMb: u2, captureStreamPrefixKb: p2, captureStreamTimeoutMs: f2 };
2230
+ sessionStorage.getItem("pageVisitUUID") || (sessionStorage.setItem("pageVisitUUID", uuidv4()), invalidateUrlCache()), x2.xhrPatched || (!(function setupXMLHttpRequestInterceptor(e3 = [], t3 = { captureStreamingResponseBody: true, captureResponseBodyMaxMb: 10, captureStreamPrefixKb: 64, captureStreamTimeoutMs: 1e4 }, i3 = []) {
2231
2231
  const o3 = XMLHttpRequest.prototype.open, s3 = XMLHttpRequest.prototype.send, r3 = XMLHttpRequest.prototype.setRequestHeader, a3 = getOrSetSessionId(), l3 = createSkipHeadersPropagationChecker(e3, i3);
2232
2232
  XMLHttpRequest.prototype.setRequestHeader = function(e4, t4) {
2233
2233
  return this._capturedRequestHeaders || (this._capturedRequestHeaders = {}), this._capturedRequestHeaders[e4] = t4, r3.call(this, e4, t4);
@@ -2292,22 +2292,22 @@ async function startRecording({ apiKey: e2, backendApi: t2 = "https://api-servic
2292
2292
  emitFinished(false, e5, t4);
2293
2293
  }, { once: true }), s3.apply(this, e4);
2294
2294
  };
2295
- })(o2, x2, i2), k2.xhrPatched = true), k2.fetchPatched || (setupFetchInterceptor(o2, x2, i2), k2.fetchPatched = true), await yieldToMain(), k2.domEventsInit || (initializeDomContentEvents(w2), k2.domEventsInit = true), await yieldToMain(), k2.consoleInit || (initializeConsolePlugin(Le, w2), k2.consoleInit = true), await yieldToMain(), k2.errorInit || (!(function initializeErrorInterceptor() {
2295
+ })(o2, I2, i2), x2.xhrPatched = true), x2.fetchPatched || (setupFetchInterceptor(o2, I2, i2), x2.fetchPatched = true), await yieldToMain(), x2.domEventsInit || (initializeDomContentEvents(k2), x2.domEventsInit = true), await yieldToMain(), x2.consoleInit || (initializeConsolePlugin(Le, k2), x2.consoleInit = true), await yieldToMain(), x2.errorInit || (!(function initializeErrorInterceptor() {
2296
2296
  window.addEventListener("error", (e3) => {
2297
2297
  captureError(e3.error || e3.message);
2298
2298
  }), window.addEventListener("unhandledrejection", (e3) => {
2299
2299
  captureError(e3.reason, true);
2300
2300
  });
2301
- })(), k2.errorInit = true), await yieldToMain(), _ensureModuleSideEffects(), (function storeCredentialsAndConnection({ apiKey: e3, backendApi: t3 }) {
2301
+ })(), x2.errorInit = true), await yieldToMain(), _ensureModuleSideEffects(), (function storeCredentialsAndConnection({ apiKey: e3, backendApi: t3 }) {
2302
2302
  g && (sessionStorage.setItem("sailfishApiKey", e3), sessionStorage.setItem("sailfishBackendApi", t3));
2303
- })({ apiKey: e2, backendApi: t2 }), !isFunctionSpanTrackingEnabled() || k2.ws && 1 === k2.ws.readyState || fetchFunctionSpanTrackingEnabled(e2, t2).then((e3) => {
2303
+ })({ apiKey: e2, backendApi: t2 }), !isFunctionSpanTrackingEnabled() || x2.ws && 1 === x2.ws.readyState || fetchFunctionSpanTrackingEnabled(e2, t2).then((e3) => {
2304
2304
  var _a2;
2305
2305
  ((_a2 = e3.data) == null ? void 0 : _a2.isFunctionSpanTrackingEnabledFromApiKey) ?? false ? we && console.log("[Sailfish] Function span tracking state validated with backend: ACTIVE") : (clearStaleFuncSpanState(), we && console.log("[Sailfish] Cleared stale function span tracking state - backend validation shows tracking is not active"));
2306
2306
  }).catch((e3) => {
2307
2307
  we && console.warn("[Sailfish] Failed to validate function span tracking status with backend:", e3);
2308
- }), k2.sentDoNotPropagateOnce || (sendDomainsToNotPropagateHeaderTo(e2, [...o2, ...Ie], t2).catch((e3) => console.error("Failed to send domains to not propagate header to:", e3)), k2.sentDoNotPropagateOnce = true), (async function gatherAndCacheDeviceInfo() {
2308
+ }), x2.sentDoNotPropagateOnce || (sendDomainsToNotPropagateHeaderTo(e2, [...o2, ...Ie], t2).catch((e3) => console.error("Failed to send domains to not propagate header to:", e3)), x2.sentDoNotPropagateOnce = true), (async function gatherAndCacheDeviceInfo() {
2309
2309
  sendMessage({ type: "deviceInfo", data: { deviceInfo: { language: navigator.language, userAgent: navigator.userAgent } } });
2310
- })(), c2 && fetchAndSendIp(w2);
2310
+ })(), c2 && fetchAndSendIp(k2);
2311
2311
  try {
2312
2312
  const n2 = a2 ?? (function readGitSha() {
2313
2313
  var _a2;
@@ -2326,7 +2326,7 @@ async function startRecording({ apiKey: e2, backendApi: t2 = "https://api-servic
2326
2326
  if ("string" == typeof e3 && e3) return e3;
2327
2327
  } catch {
2328
2328
  }
2329
- })(), i3 = r2 ?? "", o3 = s2 ?? "", c3 = "JS/TS", d3 = (function getMapUuidFromWindow() {
2329
+ })(), i3 = r2 ?? "", o3 = s2 ?? "", c3 = v2 ?? "JS/TS", d3 = (function getMapUuidFromWindow() {
2330
2330
  try {
2331
2331
  const e3 = window;
2332
2332
  if (e3 && "string" == typeof e3.sfMapUuid && e3.sfMapUuid) return e3.sfMapUuid;
@@ -2346,15 +2346,15 @@ async function startRecording({ apiKey: e2, backendApi: t2 = "https://api-servic
2346
2346
  return { framework: i4[0] ?? null, additionalFrameworks: i4.slice(1), serviceRole: "frontend" };
2347
2347
  })(), f3 = { ...u3, serviceRole: p3.serviceRole, ...null !== p3.framework && { framework: p3.framework }, ...p3.additionalFrameworks.length > 0 && { additionalFrameworks: p3.additionalFrameworks } };
2348
2348
  await yieldToMain();
2349
- const [g2, h3] = await Promise.all([fetchCaptureSettings(e2, t2), startRecordingSession(e2, w2, t2, i3, o3, d3, n2, c3, f3)]), y3 = { ...Fe, ...(_a = g2.data) == null ? void 0 : _a.captureSettingsFromApiKey, enableFiberTracking: m2 };
2350
- if (k2.ws && 1 === k2.ws.readyState) return;
2349
+ const [g2, h3] = await Promise.all([fetchCaptureSettings(e2, t2), startRecordingSession(e2, k2, t2, i3, o3, d3, n2, c3, f3)]), y3 = { ...Fe, ...(_a = g2.data) == null ? void 0 : _a.captureSettingsFromApiKey, enableFiberTracking: m2 };
2350
+ if (x2.ws && 1 === x2.ws.readyState) return;
2351
2351
  if ((_b = h3.data) == null ? void 0 : _b.startRecordingSession) {
2352
2352
  const n3 = (l2 == null ? void 0 : l2.env) || (l2 == null ? void 0 : l2.environment);
2353
2353
  await yieldToMain();
2354
- const i4 = await initializeRecording(y3, t2, e2, w2, n3, v2, S2, b2 ?? false);
2355
- k2.ws = i4, k2.initialized = true, trackDomainChangesOnce(), k2.sentMapUuidOnce || (!(function sendMapUuidIfAvailable(e3 = "", t3 = "") {
2354
+ const i4 = await initializeRecording(y3, t2, e2, k2, n3, w2, S2, b2 ?? false);
2355
+ x2.ws = i4, x2.initialized = true, trackDomainChangesOnce(), x2.sentMapUuidOnce || (!(function sendMapUuidIfAvailable(e3 = "", t3 = "") {
2356
2356
  window.sfMapUuid && sendMessage({ type: "mapUuid", data: { mapUuid: window.sfMapUuid, serviceIdentifier: e3, serviceVersion: t3 } });
2357
- })(r2, s2), k2.sentMapUuidOnce = true);
2357
+ })(r2, s2), x2.sentMapUuidOnce = true);
2358
2358
  } else console.error("Failed to start recording session:", h3.errors || h3);
2359
2359
  } catch (e3) {
2360
2360
  console.error("Error starting recording:", e3);
@@ -2362,7 +2362,7 @@ async function startRecording({ apiKey: e2, backendApi: t2 = "https://api-servic
2362
2362
  }
2363
2363
  exports.DEFAULT_CAPTURE_SETTINGS = Fe, exports.DEFAULT_CONSOLE_RECORDING_SETTINGS = Le, exports.STORAGE_VERSION = 1, exports.addOrUpdateMetadata = function addOrUpdateMetadata(e2) {
2364
2364
  const t2 = { type: "addOrUpdateMetadata", metadata: e2 };
2365
- U && JSON.stringify(U) === JSON.stringify(e2) || (U = e2, sendMessage(t2));
2365
+ z && JSON.stringify(z) === JSON.stringify(e2) || (z = e2, sendMessage(t2));
2366
2366
  }, exports.buildBatches = buildBatches, exports.clearStaleFuncSpanState = clearStaleFuncSpanState, exports.createSkipHeadersPropagationChecker = createSkipHeadersPropagationChecker, exports.createTriageAndIssueFromRecorder = createTriageAndIssueFromRecorder, exports.createTriageFromRecorder = createTriageFromRecorder, exports.disableFunctionSpanTracking = disableFunctionSpanTracking, exports.enableFunctionSpanTracking = enableFunctionSpanTracking, exports.ensureHrefCache = ensureHrefCache, exports.eventSize = eventSize, exports.fetchAndSendIp = fetchAndSendIp, exports.fetchCaptureSettings = fetchCaptureSettings, exports.fetchEngineeringTicketPlatformIntegrations = fetchEngineeringTicketPlatformIntegrations, exports.fetchFunctionSpanTrackingEnabled = fetchFunctionSpanTrackingEnabled, exports.flushBufferedEvents = flushBufferedEvents, exports.getCachedHref = getCachedHref, exports.getCachedHrefNoQuery = getCachedHrefNoQuery, exports.getFuncSpanHeader = getFuncSpanHeader, exports.getOrSetSessionId = getOrSetSessionId, exports.getUrlAndStoredUuids = getUrlAndStoredUuids, exports.identify = function identify(e2, t2 = {}, n2 = false) {
2367
2367
  const i2 = { type: "identify", userId: e2, traits: t2 };
2368
2368
  O && O.userId === e2 && JSON.stringify(O.traits) === JSON.stringify(t2) || (O = { userId: e2, traits: t2, overwrite: n2 }, sendMessage(i2));
@@ -2370,15 +2370,19 @@ exports.DEFAULT_CAPTURE_SETTINGS = Fe, exports.DEFAULT_CONSOLE_RECORDING_SETTING
2370
2370
  if ("undefined" == typeof window) return;
2371
2371
  const t2 = window.__sailfish_recorder || (window.__sailfish_recorder = {}), n2 = getOrSetSessionId();
2372
2372
  return clearPageVisitDataFromSessionStorage(), t2.initialized && t2.sessionId === n2 && t2.ws && 1 === t2.ws.readyState ? void 0 : (t2.initPromise || (t2.initPromise = (async () => {
2373
- if (t2.hasLoggedInitOnce || (console.log("Initializing Sailfish Recorder (first run) …"), t2.hasLoggedInitOnce = true), await startRecording(e2), !t2.issueReportingInit) {
2374
- const n3 = e2.backendApi ?? "https://api-service.sailfishqa.com", [{ setupIssueReporting: i2 }, { fetchIntegrationData: o2, getIntegrationData: s2 }] = await Promise.all([Promise.resolve().then(() => ve), Promise.resolve().then(() => le)]);
2375
- let r2 = null;
2376
- try {
2377
- await o2(e2.apiKey, n3), r2 = s2();
2378
- } catch (e3) {
2379
- console.warn("[Sailfish] Failed to fetch integration data for issue reporting:", e3);
2373
+ try {
2374
+ if (t2.hasLoggedInitOnce || (console.log("Initializing Sailfish Recorder (first run) …"), t2.hasLoggedInitOnce = true), await startRecording(e2), !t2.issueReportingInit) {
2375
+ const n3 = e2.backendApi ?? "https://api-service.sailfishqa.com", [{ setupIssueReporting: i2 }, { fetchIntegrationData: o2, getIntegrationData: s2 }] = await Promise.all([Promise.resolve().then(() => ve), Promise.resolve().then(() => le)]);
2376
+ let r2 = null;
2377
+ try {
2378
+ await o2(e2.apiKey, n3), r2 = s2();
2379
+ } catch (e3) {
2380
+ console.warn("[Sailfish] Failed to fetch integration data for issue reporting:", e3);
2381
+ }
2382
+ i2({ apiKey: e2.apiKey, backendApi: n3, getSessionId: () => getOrSetSessionId(), shortcuts: e2.reportIssueShortcuts, customBaseUrl: e2.customBaseUrl, integrationData: r2, showEngTicketFieldsInReportIssueModalDefault: e2.showEngTicketFieldsInReportIssueModalDefault }), t2.issueReportingInit = true;
2380
2383
  }
2381
- i2({ apiKey: e2.apiKey, backendApi: n3, getSessionId: () => getOrSetSessionId(), shortcuts: e2.reportIssueShortcuts, customBaseUrl: e2.customBaseUrl, integrationData: r2, showEngTicketFieldsInReportIssueModalDefault: e2.showEngTicketFieldsInReportIssueModalDefault }), t2.issueReportingInit = true;
2384
+ } catch (e3) {
2385
+ console.warn("[Sailfish] Recorder initialization failed:", e3);
2382
2386
  }
2383
2387
  })().finally(() => {
2384
2388
  delete t2.initPromise;
Binary file
Binary file