@sailfish-ai/recorder 1.10.2 → 1.10.4

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 (38) hide show
  1. package/dist/chunks/fiberHook-CEzmPkx_.js +125 -0
  2. package/dist/chunks/fiberHook-CEzmPkx_.js.br +0 -0
  3. package/dist/chunks/fiberHook-CEzmPkx_.js.gz +0 -0
  4. package/dist/chunks/fiberHook-DGANQ2ma.js +130 -0
  5. package/dist/chunks/fiberHook-DGANQ2ma.js.br +0 -0
  6. package/dist/chunks/fiberHook-DGANQ2ma.js.gz +0 -0
  7. package/dist/chunks/rrweb-plugin-console-record-BmAm-Ih_.js +181 -0
  8. package/dist/chunks/rrweb-plugin-console-record-BmAm-Ih_.js.br +0 -0
  9. package/dist/chunks/rrweb-plugin-console-record-BmAm-Ih_.js.gz +0 -0
  10. package/dist/chunks/rrweb-plugin-console-record-Cr-osXuj.js +180 -0
  11. package/dist/chunks/rrweb-plugin-console-record-Cr-osXuj.js.br +0 -0
  12. package/dist/chunks/rrweb-plugin-console-record-Cr-osXuj.js.gz +0 -0
  13. package/dist/chunks/rrweb-record-only-Ba4xyfd6.js +5253 -0
  14. package/dist/chunks/rrweb-record-only-Ba4xyfd6.js.br +0 -0
  15. package/dist/chunks/rrweb-record-only-Ba4xyfd6.js.gz +0 -0
  16. package/dist/chunks/rrweb-record-only-C5Qb-uaQ.js +5253 -0
  17. package/dist/chunks/rrweb-record-only-C5Qb-uaQ.js.br +0 -0
  18. package/dist/chunks/rrweb-record-only-C5Qb-uaQ.js.gz +0 -0
  19. package/dist/inAppReportIssueModal/index.js +171 -129
  20. package/dist/inAppReportIssueModal/integrations.js +84 -19
  21. package/dist/inAppReportIssueModal/state.js +1 -0
  22. package/dist/inAppReportIssueModal/types.js +1 -0
  23. package/dist/inAppReportIssueModal/ui.js +9 -0
  24. package/dist/index.js +259 -60
  25. package/dist/recorder.cjs +1954 -7344
  26. package/dist/recorder.js +1953 -7344
  27. package/dist/recorder.js.br +0 -0
  28. package/dist/recorder.js.gz +0 -0
  29. package/dist/recording.js +41 -32
  30. package/dist/session.js +12 -6
  31. package/dist/types/inAppReportIssueModal/integrations.d.ts +8 -0
  32. package/dist/types/inAppReportIssueModal/types.d.ts +3 -4
  33. package/dist/types/index.d.ts +11 -3
  34. package/dist/types/recording.d.ts +2 -2
  35. package/dist/types/session.d.ts +1 -0
  36. package/dist/types/websocket.d.ts +1 -0
  37. package/dist/websocket.js +11 -10
  38. package/package.json +1 -1
Binary file
Binary file
package/dist/recording.js CHANGED
@@ -1,13 +1,11 @@
1
- import { record } from "@sailfish-rrweb/rrweb-record-only";
2
- import { getRecordConsolePlugin, } from "@sailfish-rrweb/rrweb-plugin-console-record";
3
1
  // import { NetworkRecordOptions } from "@sailfish-rrweb/rrweb-plugin-network-record";
4
2
  import { EventType } from "@sailfish-rrweb/types";
5
3
  import { Complete, DomContentEventId, DomContentSource, Loading, } from "./constants";
6
- // Fiber tracking for source → DOM correlation
7
- import { installFiberHook, processExistingTree } from "./fiberHook";
8
4
  import { getCallerLocation, getCallerLocationFromTrace, } from "./sourceLocation";
9
5
  import suppressConsoleLogsDuringCall from "./suppressConsoleLogsDuringCall";
10
6
  import { initializeWebSocket, sendEvent } from "./websocket";
7
+ // Module-level reference to rrweb record, populated after dynamic import
8
+ let _record = null;
11
9
  const MASK_CLASS = "sailfishSanitize";
12
10
  const ZENDESK_ELEMENT_ID = "zendesk_chat";
13
11
  const ZENDESK_PROVIDER = "Zendesk";
@@ -140,7 +138,8 @@ export function initializeDomContentEvents(sessionId) {
140
138
  });
141
139
  });
142
140
  }
143
- export function initializeConsolePlugin(consoleRecordSettings, sessionId) {
141
+ export async function initializeConsolePlugin(consoleRecordSettings, sessionId) {
142
+ const { getRecordConsolePlugin } = await import("@sailfish-rrweb/rrweb-plugin-console-record");
144
143
  const { name, observer } = getRecordConsolePlugin(consoleRecordSettings);
145
144
  observer((payload) => {
146
145
  const anyPayload = payload;
@@ -249,21 +248,6 @@ export async function initializeRecording(captureSettings, // TODO - Sibyl post-
249
248
  backendApi, apiKey, sessionId, envValue) {
250
249
  const webSocket = initializeWebSocket(backendApi, apiKey, sessionId, envValue);
251
250
  try {
252
- // Initialize fiber tracking for source → DOM correlation
253
- // This captures React component names via _debugOwner chain and adds data-sf-* attributes
254
- // IMPORTANT: Must process existing tree BEFORE rrweb starts, so full snapshot includes attributes
255
- if (captureSettings.enableFiberTracking !== false) {
256
- try {
257
- installFiberHook();
258
- // Process any components that already rendered before hook was installed
259
- // This must happen synchronously BEFORE record() to ensure attributes are in the snapshot
260
- processExistingTree();
261
- console.log("[Sailfish] React Fiber tracking enabled");
262
- }
263
- catch (fiberError) {
264
- console.warn("[Sailfish] Failed to enable Fiber tracking:", fiberError);
265
- }
266
- }
267
251
  // Create throttled emit wrapper (only for text edit events)
268
252
  const throttledEmit = createThrottledEmit(captureSettings.textEditThrottleEnabled);
269
253
  const emitWithContext = (event) => {
@@ -271,15 +255,40 @@ backendApi, apiKey, sessionId, envValue) {
271
255
  event.sessionId = sessionId;
272
256
  throttledEmit.emit(event);
273
257
  };
274
- record({
275
- emit(event) {
276
- emitWithContext(event);
277
- },
278
- maskInputOptions: { text: true },
279
- maskInputFn,
280
- maskTextClass: MASK_CLASS,
281
- ...captureSettings,
282
- });
258
+ // Defer heavy synchronous work (Fiber hook install, DOM tree walk, rrweb full snapshot)
259
+ // via requestIdleCallback so the main thread can finish loading first.
260
+ const startHeavyWork = async () => {
261
+ // Initialize fiber tracking for source → DOM correlation
262
+ if (captureSettings.enableFiberTracking !== false) {
263
+ try {
264
+ const { installFiberHook, processExistingTree } = await import("./fiberHook");
265
+ installFiberHook();
266
+ processExistingTree();
267
+ console.log("[Sailfish] React Fiber tracking enabled");
268
+ }
269
+ catch (fiberError) {
270
+ console.warn("[Sailfish] Failed to enable Fiber tracking:", fiberError);
271
+ }
272
+ }
273
+ const { record } = await import("@sailfish-rrweb/rrweb-record-only");
274
+ _record = record;
275
+ record({
276
+ emit(event) {
277
+ emitWithContext(event);
278
+ },
279
+ maskInputOptions: { text: true },
280
+ maskInputFn,
281
+ maskTextClass: MASK_CLASS,
282
+ ...captureSettings,
283
+ });
284
+ };
285
+ if (typeof requestIdleCallback === "function") {
286
+ requestIdleCallback(startHeavyWork, { timeout: 2000 });
287
+ }
288
+ else {
289
+ // Fallback for browsers without requestIdleCallback (Safari < 16.4)
290
+ setTimeout(startHeavyWork, 0);
291
+ }
283
292
  // Flush buffered text edits before page unload
284
293
  window.addEventListener('beforeunload', () => {
285
294
  throttledEmit.flush();
@@ -302,21 +311,21 @@ backendApi, apiKey, sessionId, envValue) {
302
311
  ]);
303
312
  });
304
313
  const handleWidgetOpen = () => {
305
- record.addSailfishEvent(EventType.SailfishCustom, {
314
+ _record?.addSailfishEvent(EventType.SailfishCustom, {
306
315
  action: "customer support chat opened",
307
316
  element_id: ZENDESK_ELEMENT_ID,
308
317
  provider: ZENDESK_PROVIDER,
309
318
  });
310
319
  };
311
320
  const handleWidgetClose = () => {
312
- record.addSailfishEvent(EventType.SailfishCustom, {
321
+ _record?.addSailfishEvent(EventType.SailfishCustom, {
313
322
  action: "customer support chat closed",
314
323
  element_id: ZENDESK_ELEMENT_ID,
315
324
  provider: ZENDESK_PROVIDER,
316
325
  });
317
326
  };
318
327
  const handleUnreadMessages = (count) => {
319
- record.addSailfishEvent(EventType.SailfishCustom, {
328
+ _record?.addSailfishEvent(EventType.SailfishCustom, {
320
329
  action: "zendesk unreadmessages",
321
330
  element_id: ZENDESK_ELEMENT_ID,
322
331
  provider: ZENDESK_PROVIDER,
package/dist/session.js CHANGED
@@ -40,10 +40,16 @@ export function getOrSetSessionId() {
40
40
  }
41
41
  return sessionId;
42
42
  }
43
- // Set up beforeunload listener to mark page refresh
44
- if (HAS_WINDOW) {
45
- window.addEventListener("beforeunload", () => {
46
- // Prepend our flag to preserve customer's window.name
47
- window.name = REFRESH_FLAG + window.name;
48
- });
43
+ // Deferred: call ensureSessionListeners() from startRecording() to avoid module-level side effects
44
+ let _sessionListenersInstalled = false;
45
+ export function ensureSessionListeners() {
46
+ if (_sessionListenersInstalled)
47
+ return;
48
+ _sessionListenersInstalled = true;
49
+ if (HAS_WINDOW) {
50
+ window.addEventListener("beforeunload", () => {
51
+ // Prepend our flag to preserve customer's window.name
52
+ window.name = REFRESH_FLAG + window.name;
53
+ });
54
+ }
49
55
  }
@@ -3,8 +3,16 @@ import { IssueReportState } from "./types";
3
3
  export declare function getIntegrationData(): EngineeringTicketIntegration | null;
4
4
  export declare function hasValidIntegration(): boolean;
5
5
  export declare function fetchIntegrationData(apiKey: string, backendApi: string): Promise<void>;
6
+ /**
7
+ * Re-fetch integration data in the background. Returns the fresh data
8
+ * and updates the module-level cache. Callers can compare with previous
9
+ * data to decide whether to refresh the UI.
10
+ */
11
+ export declare function refreshIntegrationData(apiKey: string, backendApi: string): Promise<EngineeringTicketIntegration | null>;
6
12
  export declare function populateSelectOptions(selectElement: HTMLSelectElement, options: any[], defaultValue?: string): void;
7
13
  export declare function populatePriorityOptions(selectElement: HTMLSelectElement, provider: string, defaultPriority?: number): void;
14
+ export declare function populateSprintOptions(selectElement: HTMLSelectElement, sprints: any[], currentValue?: string): void;
15
+ export declare function getSprintFieldId(): string;
8
16
  export declare function updateIssueTypeOptions(selectElement: HTMLSelectElement, projectId: string): void;
9
17
  export declare function getFieldsForProject(projectId: string, issueTypeId?: string): any[];
10
18
  export declare function getUsers(): any[];
@@ -11,6 +11,7 @@ export interface IssueReportState {
11
11
  engTicketProject: string;
12
12
  engTicketPriority: number;
13
13
  engTicketLabels: string[];
14
+ engTicketSprint: string;
14
15
  engTicketIssueType: string;
15
16
  engTicketCustomFields: Record<string, any>;
16
17
  }
@@ -28,13 +29,11 @@ export declare const STORAGE_KEYS: {
28
29
  };
29
30
  export declare const DEFAULT_SHORTCUTS: ShortcutsConfig;
30
31
  export declare const ReportIssueContext: {
31
- shortcuts: {
32
- openReportIssue?: ShortcutConfig;
33
- openStartNowMode?: ShortcutConfig;
34
- };
32
+ shortcuts: ShortcutsConfig;
35
33
  resolveSessionId: (() => string) | null;
36
34
  apiKey: string | null;
37
35
  backendApi: string | null;
38
36
  triageBaseUrl: string;
39
37
  deactivateIsolation: () => void;
38
+ integrationData: any;
40
39
  };
@@ -1,11 +1,11 @@
1
- import { LogRecordOptions } from "@sailfish-rrweb/rrweb-plugin-console-record";
2
- import { ShortcutsConfig } from "./inAppReportIssueModal";
1
+ import type { LogRecordOptions } from "@sailfish-rrweb/rrweb-plugin-console-record";
2
+ import type { ShortcutsConfig } from "./inAppReportIssueModal";
3
3
  import { CaptureSettings } from "./types";
4
4
  export declare const STORAGE_VERSION = 1;
5
5
  export declare const DEFAULT_CAPTURE_SETTINGS: CaptureSettings;
6
6
  export declare const DEFAULT_CONSOLE_RECORDING_SETTINGS: LogRecordOptions;
7
7
  export declare function matchUrlWithWildcard(input: unknown, patterns: string[]): boolean;
8
- export declare function startRecording({ apiKey, backendApi, domainsToPropagateHeaderTo, domainsToNotPropagateHeaderTo, serviceVersion, serviceIdentifier, gitSha, serviceAdditionalMetadata, enableIpTracking, }: {
8
+ export declare function startRecording({ apiKey, backendApi, domainsToPropagateHeaderTo, domainsToNotPropagateHeaderTo, serviceVersion, serviceIdentifier, gitSha, serviceAdditionalMetadata, enableIpTracking, captureStreamingResponseBody, captureResponseBodyMaxMb, captureStreamPrefixKb, captureStreamTimeoutMs, }: {
9
9
  apiKey: string;
10
10
  backendApi?: string;
11
11
  domainsToPropagateHeaderTo?: string[];
@@ -15,6 +15,10 @@ export declare function startRecording({ apiKey, backendApi, domainsToPropagateH
15
15
  gitSha?: string;
16
16
  serviceAdditionalMetadata?: Record<string, any>;
17
17
  enableIpTracking?: boolean;
18
+ captureStreamingResponseBody?: boolean;
19
+ captureResponseBodyMaxMb?: number;
20
+ captureStreamPrefixKb?: number;
21
+ captureStreamTimeoutMs?: number;
18
22
  }): Promise<void>;
19
23
  export declare const initRecorder: (options: {
20
24
  apiKey: string;
@@ -29,6 +33,10 @@ export declare const initRecorder: (options: {
29
33
  customBaseUrl?: string;
30
34
  enableFiberTracking?: boolean;
31
35
  enableIpTracking?: boolean;
36
+ captureStreamingResponseBody?: boolean;
37
+ captureResponseBodyMaxMb?: number;
38
+ captureStreamPrefixKb?: number;
39
+ captureStreamTimeoutMs?: number;
32
40
  }) => Promise<void>;
33
41
  export * from "./graphql";
34
42
  export { openReportIssueModal } from "./inAppReportIssueModal";
@@ -1,4 +1,4 @@
1
- import { LogRecordOptions } from "@sailfish-rrweb/rrweb-plugin-console-record";
1
+ import type { LogRecordOptions } from "@sailfish-rrweb/rrweb-plugin-console-record";
2
2
  import ReconnectingWebSocket from "reconnecting-websocket";
3
3
  export declare const getUrlAndStoredUuids: () => {
4
4
  page_visit_uuid: string;
@@ -8,6 +8,6 @@ export declare const getUrlAndStoredUuids: () => {
8
8
  tabVisibilityState: string;
9
9
  };
10
10
  export declare function initializeDomContentEvents(sessionId: string): void;
11
- export declare function initializeConsolePlugin(consoleRecordSettings: LogRecordOptions, sessionId: string): void;
11
+ export declare function initializeConsolePlugin(consoleRecordSettings: LogRecordOptions, sessionId: string): Promise<void>;
12
12
  export declare function initializeRecording(captureSettings: any, // TODO - Sibyl post-launch - replace type
13
13
  backendApi: string, apiKey: string, sessionId: string, envValue?: string): Promise<ReconnectingWebSocket>;
@@ -1 +1,2 @@
1
1
  export declare function getOrSetSessionId(): string;
2
+ export declare function ensureSessionListeners(): void;
@@ -4,6 +4,7 @@ import ReconnectingWebSocket from "reconnecting-websocket";
4
4
  * This disables tracking and clears localStorage if backend says tracking is not active
5
5
  */
6
6
  export declare function clearStaleFuncSpanState(): void;
7
+ export declare function restoreFuncSpanState(): void;
7
8
  export declare function flushBufferedEvents(): Promise<void>;
8
9
  export declare function sendEvent(event: any): void;
9
10
  export declare function initializeWebSocket(backendApi: string, apiKey: string, sessionId: string, envValue?: string): ReconnectingWebSocket;
package/dist/websocket.js CHANGED
@@ -106,16 +106,19 @@ export function clearStaleFuncSpanState() {
106
106
  console.log("[Sailfish] Cleared stale function span tracking state (backend validation failed)");
107
107
  }
108
108
  }
109
- // Load persisted global state immediately on module initialization
110
- // This ensures headers are added from the very first HTTP request, even before WebSocket connects
111
- (() => {
109
+ // Deferred: call restoreFuncSpanState() from startRecording() to avoid module-level side effects
110
+ let _funcSpanStateRestored = false;
111
+ export function restoreFuncSpanState() {
112
+ if (_funcSpanStateRestored)
113
+ return;
114
+ _funcSpanStateRestored = true;
112
115
  const persistedState = loadGlobalFuncSpanState();
113
116
  if (persistedState && persistedState.enabled) {
114
117
  funcSpanTrackingEnabled = true;
115
118
  funcSpanExpirationTime = persistedState.expirationTimestampMs;
116
119
  isLocalTrackingMode = false;
117
120
  if (DEBUG) {
118
- console.log(`[Sailfish] Module init: Restored global function span tracking from localStorage:`, {
121
+ console.log(`[Sailfish] Restored global function span tracking from localStorage:`, {
119
122
  enabled: true,
120
123
  expirationTime: funcSpanExpirationTime,
121
124
  });
@@ -129,24 +132,22 @@ export function clearStaleFuncSpanState() {
129
132
  funcSpanExpirationTime = null;
130
133
  clearGlobalFuncSpanState();
131
134
  if (DEBUG) {
132
- console.log("[Sailfish] Module init: Persisted tracking already expired, cleared state");
135
+ console.log("[Sailfish] Persisted tracking already expired, cleared state");
133
136
  }
134
137
  }
135
138
  else {
136
- // State is still valid, keep it temporarily until WebSocket provides the true state
137
139
  if (DEBUG) {
138
- console.log("[Sailfish] Module init: Function span tracking is active and valid (temporary until WebSocket confirms)");
140
+ console.log("[Sailfish] Function span tracking is active and valid (temporary until WebSocket confirms)");
139
141
  }
140
142
  }
141
143
  }
142
144
  else {
143
- // No expiration time - keep enabled temporarily until WebSocket provides the true state
144
145
  if (DEBUG) {
145
- console.log("[Sailfish] Module init: Function span tracking is active (no expiration, temporary until WebSocket confirms)");
146
+ console.log("[Sailfish] Function span tracking is active (no expiration, temporary until WebSocket confirms)");
146
147
  }
147
148
  }
148
149
  }
149
- })();
150
+ }
150
151
  function isWebSocketOpen(ws) {
151
152
  return ws?.readyState === WebSocket.OPEN;
152
153
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sailfish-ai/recorder",
3
- "version": "1.10.2",
3
+ "version": "1.10.4",
4
4
  "publishPublicly": true,
5
5
  "type": "module",
6
6
  "main": "dist/recorder.cjs",