@sailfish-ai/recorder 1.8.7 → 1.8.9

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/dist/websocket.js CHANGED
@@ -14,15 +14,102 @@ const FUNCSPAN_HEADER_VALUE = "1-0-5-5-0-1.0";
14
14
  // Tracking configuration type constants (must match backend TrackingConfigurationType enum)
15
15
  const TRACKING_CONFIG_GLOBAL = "global";
16
16
  const TRACKING_CONFIG_PER_SESSION = "per_session";
17
+ // localStorage key for persisting global function span tracking state
18
+ const FUNCSPAN_GLOBAL_STATE_KEY = "sailfish_funcspan_global_state";
17
19
  let webSocket = null;
18
20
  let isDraining = false;
19
21
  let inFlightFlush = null;
20
22
  let flushIntervalId = null;
23
+ function saveGlobalFuncSpanState(enabled, expirationTimestampMs) {
24
+ try {
25
+ if (typeof localStorage === "undefined")
26
+ return;
27
+ const state = {
28
+ enabled,
29
+ expirationTimestampMs,
30
+ };
31
+ localStorage.setItem(FUNCSPAN_GLOBAL_STATE_KEY, JSON.stringify(state));
32
+ if (DEBUG) {
33
+ console.log(`[Sailfish] Saved global function span state to localStorage:`, state);
34
+ }
35
+ }
36
+ catch (e) {
37
+ if (DEBUG) {
38
+ console.warn(`[Sailfish] Failed to save global function span state to localStorage:`, e);
39
+ }
40
+ }
41
+ }
42
+ function loadGlobalFuncSpanState() {
43
+ try {
44
+ if (typeof localStorage === "undefined")
45
+ return null;
46
+ const stored = localStorage.getItem(FUNCSPAN_GLOBAL_STATE_KEY);
47
+ if (!stored)
48
+ return null;
49
+ const state = JSON.parse(stored);
50
+ // Check if the stored state has expired
51
+ if (state.enabled && state.expirationTimestampMs) {
52
+ const now = Date.now();
53
+ if (now >= state.expirationTimestampMs) {
54
+ // Expired - clear localStorage and return null
55
+ if (DEBUG) {
56
+ console.log(`[Sailfish] Stored global function span state has expired, clearing`);
57
+ }
58
+ localStorage.removeItem(FUNCSPAN_GLOBAL_STATE_KEY);
59
+ return null;
60
+ }
61
+ }
62
+ if (DEBUG) {
63
+ console.log(`[Sailfish] Loaded global function span state from localStorage:`, state);
64
+ }
65
+ return state;
66
+ }
67
+ catch (e) {
68
+ if (DEBUG) {
69
+ console.warn(`[Sailfish] Failed to load global function span state from localStorage:`, e);
70
+ }
71
+ return null;
72
+ }
73
+ }
74
+ function clearGlobalFuncSpanState() {
75
+ try {
76
+ if (typeof localStorage === "undefined")
77
+ return;
78
+ localStorage.removeItem(FUNCSPAN_GLOBAL_STATE_KEY);
79
+ if (DEBUG) {
80
+ console.log(`[Sailfish] Cleared global function span state from localStorage`);
81
+ }
82
+ }
83
+ catch (e) {
84
+ if (DEBUG) {
85
+ console.warn(`[Sailfish] Failed to clear global function span state from localStorage:`, e);
86
+ }
87
+ }
88
+ }
21
89
  // Function span tracking state (only manages enabled/disabled)
22
90
  let funcSpanTrackingEnabled = false;
23
91
  let funcSpanTimeoutId = null;
24
92
  let funcSpanExpirationTime = null; // Timestamp when tracking should expire (milliseconds)
25
93
  let isLocalTrackingMode = false; // True when tracking is enabled locally (Report Issue), not globally
94
+ // Saved global state when switching to local mode
95
+ let savedGlobalTimeoutId = null;
96
+ let savedGlobalExpirationTime = null;
97
+ // Load persisted global state immediately on module initialization
98
+ // This ensures headers are added from the very first HTTP request, even before WebSocket connects
99
+ (() => {
100
+ const persistedState = loadGlobalFuncSpanState();
101
+ if (persistedState && persistedState.enabled) {
102
+ funcSpanTrackingEnabled = true;
103
+ funcSpanExpirationTime = persistedState.expirationTimestampMs;
104
+ isLocalTrackingMode = false;
105
+ if (DEBUG) {
106
+ console.log(`[Sailfish] Module init: Restored global function span tracking from localStorage:`, {
107
+ enabled: true,
108
+ expirationTimestampMs: funcSpanExpirationTime,
109
+ });
110
+ }
111
+ }
112
+ })();
26
113
  function isWebSocketOpen(ws) {
27
114
  return ws?.readyState === WebSocket.OPEN;
28
115
  }
@@ -117,6 +204,9 @@ export function sendEvent(event) {
117
204
  }
118
205
  }
119
206
  export function initializeWebSocket(backendApi, apiKey, sessionId) {
207
+ // Note: Global function span tracking state is now loaded at module initialization time
208
+ // (see IIFE above) so headers work immediately, even before WebSocket connects.
209
+ // The WebSocket "funcSpanTrackingControl" message will update the state if needed.
120
210
  const wsHost = getWebSocketHost(backendApi);
121
211
  const apiProtocol = new URL(backendApi).protocol;
122
212
  const wsScheme = apiProtocol === "https:" ? "wss" : "ws";
@@ -187,11 +277,15 @@ export function initializeWebSocket(backendApi, apiKey, sessionId) {
187
277
  console.log(`[Sailfish] Server expiration timestamp: ${funcSpanExpirationTime}, ms until expiration: ${msUntilExpiration}`);
188
278
  }
189
279
  if (msUntilExpiration > 0) {
280
+ // Save to localStorage for persistence across page refreshes
281
+ saveGlobalFuncSpanState(true, funcSpanExpirationTime);
190
282
  funcSpanTimeoutId = window.setTimeout(() => {
191
283
  // Only auto-disable if still in global mode
192
284
  if (!isLocalTrackingMode) {
193
285
  funcSpanTrackingEnabled = false;
194
286
  funcSpanExpirationTime = null;
287
+ // Clear localStorage when tracking expires
288
+ clearGlobalFuncSpanState();
195
289
  if (DEBUG) {
196
290
  console.log(`[Sailfish] GLOBAL function span tracking auto-disabled at server expiration time`);
197
291
  }
@@ -202,6 +296,8 @@ export function initializeWebSocket(backendApi, apiKey, sessionId) {
202
296
  // Already expired
203
297
  funcSpanTrackingEnabled = false;
204
298
  funcSpanExpirationTime = null;
299
+ // Clear localStorage if tracking already expired
300
+ clearGlobalFuncSpanState();
205
301
  if (DEBUG) {
206
302
  console.log(`[Sailfish] Tracking already expired, not enabling`);
207
303
  }
@@ -212,10 +308,14 @@ export function initializeWebSocket(backendApi, apiKey, sessionId) {
212
308
  const timeoutSeconds = data.timeoutSeconds || 3600; // Default 1 hour
213
309
  if (timeoutSeconds > 0) {
214
310
  funcSpanExpirationTime = Date.now() + (timeoutSeconds * 1000);
311
+ // Save to localStorage for persistence across page refreshes
312
+ saveGlobalFuncSpanState(true, funcSpanExpirationTime);
215
313
  funcSpanTimeoutId = window.setTimeout(() => {
216
314
  if (!isLocalTrackingMode) {
217
315
  funcSpanTrackingEnabled = false;
218
316
  funcSpanExpirationTime = null;
317
+ // Clear localStorage when tracking expires
318
+ clearGlobalFuncSpanState();
219
319
  if (DEBUG) {
220
320
  console.log(`[Sailfish] GLOBAL function span tracking auto-disabled after ${timeoutSeconds}s (legacy)`);
221
321
  }
@@ -244,8 +344,9 @@ export function initializeWebSocket(backendApi, apiKey, sessionId) {
244
344
  }
245
345
  }
246
346
  else {
247
- // When disabled, clear expiration time
347
+ // When disabled, clear expiration time and localStorage
248
348
  funcSpanExpirationTime = null;
349
+ clearGlobalFuncSpanState();
249
350
  }
250
351
  }
251
352
  }
@@ -288,14 +389,18 @@ export function enableFunctionSpanTracking() {
288
389
  if (DEBUG) {
289
390
  console.log("[Sailfish] enableFunctionSpanTracking() called - Report Issue recording started (LOCAL MODE)");
290
391
  }
392
+ // If global tracking was already active, save its state so we can restore it later
393
+ if (funcSpanTrackingEnabled && !isLocalTrackingMode) {
394
+ savedGlobalTimeoutId = funcSpanTimeoutId;
395
+ savedGlobalExpirationTime = funcSpanExpirationTime;
396
+ if (DEBUG) {
397
+ console.log("[Sailfish] Saved global tracking state while switching to local mode");
398
+ }
399
+ }
291
400
  funcSpanTrackingEnabled = true;
292
401
  isLocalTrackingMode = true; // Mark as local tracking
293
402
  funcSpanExpirationTime = null; // Local mode has no expiration
294
- // Clear any existing timeout
295
- if (funcSpanTimeoutId !== null) {
296
- window.clearTimeout(funcSpanTimeoutId);
297
- funcSpanTimeoutId = null;
298
- }
403
+ funcSpanTimeoutId = null; // Clear timeout (saved above if it was global)
299
404
  // Report this session to backend for tracking with per_session configuration type
300
405
  if (isWebSocketOpen(webSocket)) {
301
406
  try {
@@ -348,17 +453,28 @@ export function disableFunctionSpanTracking() {
348
453
  console.warn(`[FUNCSPAN STOP] ✗ WebSocket not open, cannot notify tracking end`);
349
454
  }
350
455
  if (isLocalTrackingMode) {
351
- funcSpanTrackingEnabled = false;
352
456
  isLocalTrackingMode = false;
353
- funcSpanExpirationTime = null;
354
- if (DEBUG) {
355
- console.log("[Sailfish] LOCAL tracking mode disabled");
457
+ // Restore global tracking state if it was active before local mode
458
+ if (savedGlobalTimeoutId !== null || savedGlobalExpirationTime !== null) {
459
+ funcSpanTrackingEnabled = true; // Keep tracking enabled (global was active)
460
+ funcSpanExpirationTime = savedGlobalExpirationTime;
461
+ funcSpanTimeoutId = savedGlobalTimeoutId;
462
+ if (DEBUG) {
463
+ console.log("[Sailfish] LOCAL tracking mode disabled, restored GLOBAL tracking state");
464
+ }
465
+ // Clear saved state
466
+ savedGlobalTimeoutId = null;
467
+ savedGlobalExpirationTime = null;
468
+ }
469
+ else {
470
+ // No global tracking was active, fully disable
471
+ funcSpanTrackingEnabled = false;
472
+ funcSpanExpirationTime = null;
473
+ funcSpanTimeoutId = null;
474
+ if (DEBUG) {
475
+ console.log("[Sailfish] LOCAL tracking mode disabled, no global tracking to restore");
476
+ }
356
477
  }
357
- }
358
- // Clear any existing timeout
359
- if (funcSpanTimeoutId !== null) {
360
- window.clearTimeout(funcSpanTimeoutId);
361
- funcSpanTimeoutId = null;
362
478
  }
363
479
  }
364
480
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sailfish-ai/recorder",
3
- "version": "1.8.7",
3
+ "version": "1.8.9",
4
4
  "publishPublicly": true,
5
5
  "type": "module",
6
6
  "main": "dist/recorder.cjs",