@flipswitch-io/sdk 0.1.5 → 0.1.7

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/index.d.mts CHANGED
@@ -26,6 +26,44 @@ interface FlipswitchOptions {
26
26
  * Useful for testing or custom networking needs.
27
27
  */
28
28
  fetchImplementation?: typeof fetch;
29
+ /**
30
+ * Persist flag values to localStorage (browser only).
31
+ * When enabled, flag values are cached in localStorage and used
32
+ * as fallback when offline or during initial load.
33
+ * @default true (in browser environments)
34
+ */
35
+ persistCache?: boolean;
36
+ /**
37
+ * Enable offline mode support.
38
+ * When enabled, the provider will detect offline state and serve
39
+ * cached values without attempting network requests.
40
+ * @default true (in browser environments)
41
+ */
42
+ offlineMode?: boolean;
43
+ /**
44
+ * Enable visibility API integration (browser only).
45
+ * When enabled, SSE connection is paused when the tab is hidden
46
+ * and resumed when it becomes visible, saving resources.
47
+ * @default true (in browser environments)
48
+ */
49
+ enableVisibilityHandling?: boolean;
50
+ /**
51
+ * Enable polling fallback when SSE fails.
52
+ * After maxSseRetries, the provider will fall back to polling.
53
+ * @default true
54
+ */
55
+ enablePollingFallback?: boolean;
56
+ /**
57
+ * Polling interval in milliseconds for fallback mode.
58
+ * Only used when SSE connection fails and polling fallback is enabled.
59
+ * @default 30000 (30 seconds)
60
+ */
61
+ pollingInterval?: number;
62
+ /**
63
+ * Maximum SSE retry attempts before falling back to polling.
64
+ * @default 5
65
+ */
66
+ maxSseRetries?: number;
29
67
  }
30
68
  /**
31
69
  * Event emitted when a single flag is updated.
@@ -45,18 +83,27 @@ interface FlagUpdatedEvent {
45
83
  */
46
84
  interface ConfigUpdatedEvent {
47
85
  /**
48
- * The reason for the configuration update.
49
- * Possible values: 'segment-modified', 'api-key-rotated'
86
+ * ISO timestamp of when the change occurred.
50
87
  */
51
- reason: string;
88
+ timestamp: string;
89
+ }
90
+ /**
91
+ * Event emitted when an API key has been rotated or rotation was aborted.
92
+ */
93
+ interface ApiKeyRotatedEvent {
52
94
  /**
53
- * ISO timestamp of when the change occurred.
95
+ * ISO timestamp when the current key expires.
96
+ * Null if the rotation was aborted.
97
+ */
98
+ validUntil: string | null;
99
+ /**
100
+ * ISO timestamp of when the event occurred.
54
101
  */
55
102
  timestamp: string;
56
103
  }
57
104
  /**
58
105
  * Union type for all flag events.
59
- * Used internally to handle both event types.
106
+ * Used internally to handle event types.
60
107
  */
61
108
  type FlagEvent = {
62
109
  type: 'flag-updated';
@@ -64,6 +111,9 @@ type FlagEvent = {
64
111
  } | {
65
112
  type: 'config-updated';
66
113
  data: ConfigUpdatedEvent;
114
+ } | {
115
+ type: 'api-key-rotated';
116
+ data: ApiKeyRotatedEvent;
67
117
  };
68
118
  /**
69
119
  * @deprecated Use FlagUpdatedEvent or ConfigUpdatedEvent instead.
@@ -152,10 +202,22 @@ declare class FlipswitchProvider {
152
202
  private readonly enableRealtime;
153
203
  private readonly fetchImpl;
154
204
  private readonly ofrepProvider;
205
+ private readonly browserCache;
206
+ private readonly enableVisibilityHandling;
207
+ private readonly enablePollingFallback;
208
+ private readonly pollingInterval;
209
+ private readonly maxSseRetries;
210
+ private readonly offlineMode;
155
211
  private sseClient;
156
212
  private _status;
157
213
  private eventHandlers;
158
214
  private userEventHandlers;
215
+ private pollingTimer;
216
+ private sseRetryCount;
217
+ private isPollingFallbackActive;
218
+ private onlineHandler;
219
+ private offlineHandler;
220
+ private _isOnline;
159
221
  constructor(options: FlipswitchOptions, eventHandlers?: FlipswitchEventHandlers);
160
222
  private getTelemetrySdkHeader;
161
223
  private getTelemetryRuntimeHeader;
@@ -168,10 +230,34 @@ declare class FlipswitchProvider {
168
230
  * Validates the API key and starts SSE connection if real-time is enabled.
169
231
  */
170
232
  initialize(context?: EvaluationContext): Promise<void>;
233
+ /**
234
+ * Setup online/offline event handling.
235
+ */
236
+ private setupOfflineHandling;
237
+ /**
238
+ * Refresh flags from the server.
239
+ */
240
+ private refreshFlags;
171
241
  /**
172
242
  * Called when the provider is shut down.
173
243
  */
174
244
  onClose(): Promise<void>;
245
+ /**
246
+ * Stop the polling fallback.
247
+ */
248
+ private stopPolling;
249
+ /**
250
+ * Start polling fallback when SSE fails.
251
+ */
252
+ private startPollingFallback;
253
+ /**
254
+ * Check if the provider is currently online.
255
+ */
256
+ isOnline(): boolean;
257
+ /**
258
+ * Check if polling fallback is active.
259
+ */
260
+ isPollingActive(): boolean;
175
261
  /**
176
262
  * Start the SSE connection for real-time updates.
177
263
  */
@@ -182,7 +268,7 @@ declare class FlipswitchProvider {
182
268
  private getTelemetryHeadersMap;
183
269
  /**
184
270
  * Handle a flag change event from SSE.
185
- * Emits PROVIDER_CONFIGURATION_CHANGED to trigger re-evaluation.
271
+ * Triggers OFREP cache refresh, updates browser cache, and emits PROVIDER_CONFIGURATION_CHANGED.
186
272
  */
187
273
  private handleFlagChange;
188
274
  /**
@@ -248,6 +334,7 @@ declare class FlipswitchProvider {
248
334
  /**
249
335
  * SSE client for real-time flag change notifications.
250
336
  * Handles automatic reconnection with exponential backoff.
337
+ * Includes visibility API integration for browser environments.
251
338
  */
252
339
  declare class SseClient {
253
340
  private readonly baseUrl;
@@ -255,16 +342,38 @@ declare class SseClient {
255
342
  private readonly onFlagChange;
256
343
  private readonly onStatusChange?;
257
344
  private readonly telemetryHeaders?;
345
+ private readonly enableVisibilityHandling;
258
346
  private eventSource;
259
347
  private retryDelay;
260
348
  private reconnectTimeout;
261
349
  private closed;
350
+ private paused;
262
351
  private status;
263
- constructor(baseUrl: string, apiKey: string, onFlagChange: (event: FlagChangeEvent) => void, onStatusChange?: ((status: SseConnectionStatus) => void) | undefined, telemetryHeaders?: Record<string, string> | undefined);
352
+ private visibilityHandler;
353
+ private abortController;
354
+ constructor(baseUrl: string, apiKey: string, onFlagChange: (event: FlagChangeEvent) => void, onStatusChange?: ((status: SseConnectionStatus) => void) | undefined, telemetryHeaders?: Record<string, string> | undefined, enableVisibilityHandling?: boolean);
264
355
  /**
265
356
  * Start the SSE connection.
266
357
  */
267
358
  connect(): void;
359
+ /**
360
+ * Setup visibility API handling.
361
+ * Disconnects when tab is hidden, reconnects when visible.
362
+ */
363
+ private setupVisibilityHandling;
364
+ /**
365
+ * Pause the SSE connection (used by visibility API).
366
+ * Different from close() - can be resumed.
367
+ */
368
+ pause(): void;
369
+ /**
370
+ * Resume the SSE connection after being paused.
371
+ */
372
+ resume(): void;
373
+ /**
374
+ * Check if the connection is paused.
375
+ */
376
+ isPaused(): boolean;
268
377
  /**
269
378
  * Connect using fetch-based SSE (supports custom headers).
270
379
  */
@@ -327,4 +436,81 @@ declare class FlagCache {
327
436
  handleFlagChange(event: FlagChangeEvent): void;
328
437
  }
329
438
 
330
- export { type ConfigUpdatedEvent, FlagCache, type FlagChangeEvent, type FlagEvaluation, type FlagEvent, type FlagUpdatedEvent, type FlipswitchEventHandlers, type FlipswitchOptions, FlipswitchProvider, SseClient, type SseConnectionStatus };
439
+ /**
440
+ * Browser-based cache for flag values using localStorage.
441
+ * Provides persistence across page reloads and offline support.
442
+ */
443
+ interface CachedFlag {
444
+ value: unknown;
445
+ timestamp: number;
446
+ variant?: string;
447
+ reason?: string;
448
+ }
449
+ interface CacheMeta {
450
+ lastUpdated: number;
451
+ version: number;
452
+ }
453
+ /**
454
+ * Browser cache for persisting flag values to localStorage.
455
+ * Falls back gracefully when localStorage is unavailable.
456
+ */
457
+ declare class BrowserCache {
458
+ private readonly storage;
459
+ private readonly cacheVersion;
460
+ constructor();
461
+ /**
462
+ * Get storage instance if available.
463
+ */
464
+ private getStorage;
465
+ /**
466
+ * Check if browser cache is available.
467
+ */
468
+ isAvailable(): boolean;
469
+ /**
470
+ * Get a cached flag value.
471
+ */
472
+ get(flagKey: string): CachedFlag | null;
473
+ /**
474
+ * Set a cached flag value.
475
+ */
476
+ set(flagKey: string, value: unknown, variant?: string, reason?: string): void;
477
+ /**
478
+ * Set multiple cached flag values at once.
479
+ */
480
+ setAll(flags: Array<{
481
+ key: string;
482
+ value: unknown;
483
+ variant?: string;
484
+ reason?: string;
485
+ }>): void;
486
+ /**
487
+ * Get all cached flags.
488
+ */
489
+ getAll(): Map<string, CachedFlag>;
490
+ /**
491
+ * Invalidate a specific flag or all flags.
492
+ */
493
+ invalidate(flagKey?: string): void;
494
+ /**
495
+ * Update cache metadata.
496
+ */
497
+ private updateMeta;
498
+ /**
499
+ * Get cache metadata.
500
+ */
501
+ getMeta(): CacheMeta | null;
502
+ /**
503
+ * Get the age of a cached flag in milliseconds.
504
+ */
505
+ getAge(flagKey: string): number | null;
506
+ /**
507
+ * Check if a cached flag is stale based on max age.
508
+ */
509
+ isStale(flagKey: string, maxAgeMs: number): boolean;
510
+ /**
511
+ * Clear all Flipswitch data from localStorage.
512
+ */
513
+ clear(): void;
514
+ }
515
+
516
+ export { type ApiKeyRotatedEvent, BrowserCache, type ConfigUpdatedEvent, FlagCache, type FlagChangeEvent, type FlagEvaluation, type FlagEvent, type FlagUpdatedEvent, type FlipswitchEventHandlers, type FlipswitchOptions, FlipswitchProvider, SseClient, type SseConnectionStatus };
package/dist/index.d.ts CHANGED
@@ -26,6 +26,44 @@ interface FlipswitchOptions {
26
26
  * Useful for testing or custom networking needs.
27
27
  */
28
28
  fetchImplementation?: typeof fetch;
29
+ /**
30
+ * Persist flag values to localStorage (browser only).
31
+ * When enabled, flag values are cached in localStorage and used
32
+ * as fallback when offline or during initial load.
33
+ * @default true (in browser environments)
34
+ */
35
+ persistCache?: boolean;
36
+ /**
37
+ * Enable offline mode support.
38
+ * When enabled, the provider will detect offline state and serve
39
+ * cached values without attempting network requests.
40
+ * @default true (in browser environments)
41
+ */
42
+ offlineMode?: boolean;
43
+ /**
44
+ * Enable visibility API integration (browser only).
45
+ * When enabled, SSE connection is paused when the tab is hidden
46
+ * and resumed when it becomes visible, saving resources.
47
+ * @default true (in browser environments)
48
+ */
49
+ enableVisibilityHandling?: boolean;
50
+ /**
51
+ * Enable polling fallback when SSE fails.
52
+ * After maxSseRetries, the provider will fall back to polling.
53
+ * @default true
54
+ */
55
+ enablePollingFallback?: boolean;
56
+ /**
57
+ * Polling interval in milliseconds for fallback mode.
58
+ * Only used when SSE connection fails and polling fallback is enabled.
59
+ * @default 30000 (30 seconds)
60
+ */
61
+ pollingInterval?: number;
62
+ /**
63
+ * Maximum SSE retry attempts before falling back to polling.
64
+ * @default 5
65
+ */
66
+ maxSseRetries?: number;
29
67
  }
30
68
  /**
31
69
  * Event emitted when a single flag is updated.
@@ -45,18 +83,27 @@ interface FlagUpdatedEvent {
45
83
  */
46
84
  interface ConfigUpdatedEvent {
47
85
  /**
48
- * The reason for the configuration update.
49
- * Possible values: 'segment-modified', 'api-key-rotated'
86
+ * ISO timestamp of when the change occurred.
50
87
  */
51
- reason: string;
88
+ timestamp: string;
89
+ }
90
+ /**
91
+ * Event emitted when an API key has been rotated or rotation was aborted.
92
+ */
93
+ interface ApiKeyRotatedEvent {
52
94
  /**
53
- * ISO timestamp of when the change occurred.
95
+ * ISO timestamp when the current key expires.
96
+ * Null if the rotation was aborted.
97
+ */
98
+ validUntil: string | null;
99
+ /**
100
+ * ISO timestamp of when the event occurred.
54
101
  */
55
102
  timestamp: string;
56
103
  }
57
104
  /**
58
105
  * Union type for all flag events.
59
- * Used internally to handle both event types.
106
+ * Used internally to handle event types.
60
107
  */
61
108
  type FlagEvent = {
62
109
  type: 'flag-updated';
@@ -64,6 +111,9 @@ type FlagEvent = {
64
111
  } | {
65
112
  type: 'config-updated';
66
113
  data: ConfigUpdatedEvent;
114
+ } | {
115
+ type: 'api-key-rotated';
116
+ data: ApiKeyRotatedEvent;
67
117
  };
68
118
  /**
69
119
  * @deprecated Use FlagUpdatedEvent or ConfigUpdatedEvent instead.
@@ -152,10 +202,22 @@ declare class FlipswitchProvider {
152
202
  private readonly enableRealtime;
153
203
  private readonly fetchImpl;
154
204
  private readonly ofrepProvider;
205
+ private readonly browserCache;
206
+ private readonly enableVisibilityHandling;
207
+ private readonly enablePollingFallback;
208
+ private readonly pollingInterval;
209
+ private readonly maxSseRetries;
210
+ private readonly offlineMode;
155
211
  private sseClient;
156
212
  private _status;
157
213
  private eventHandlers;
158
214
  private userEventHandlers;
215
+ private pollingTimer;
216
+ private sseRetryCount;
217
+ private isPollingFallbackActive;
218
+ private onlineHandler;
219
+ private offlineHandler;
220
+ private _isOnline;
159
221
  constructor(options: FlipswitchOptions, eventHandlers?: FlipswitchEventHandlers);
160
222
  private getTelemetrySdkHeader;
161
223
  private getTelemetryRuntimeHeader;
@@ -168,10 +230,34 @@ declare class FlipswitchProvider {
168
230
  * Validates the API key and starts SSE connection if real-time is enabled.
169
231
  */
170
232
  initialize(context?: EvaluationContext): Promise<void>;
233
+ /**
234
+ * Setup online/offline event handling.
235
+ */
236
+ private setupOfflineHandling;
237
+ /**
238
+ * Refresh flags from the server.
239
+ */
240
+ private refreshFlags;
171
241
  /**
172
242
  * Called when the provider is shut down.
173
243
  */
174
244
  onClose(): Promise<void>;
245
+ /**
246
+ * Stop the polling fallback.
247
+ */
248
+ private stopPolling;
249
+ /**
250
+ * Start polling fallback when SSE fails.
251
+ */
252
+ private startPollingFallback;
253
+ /**
254
+ * Check if the provider is currently online.
255
+ */
256
+ isOnline(): boolean;
257
+ /**
258
+ * Check if polling fallback is active.
259
+ */
260
+ isPollingActive(): boolean;
175
261
  /**
176
262
  * Start the SSE connection for real-time updates.
177
263
  */
@@ -182,7 +268,7 @@ declare class FlipswitchProvider {
182
268
  private getTelemetryHeadersMap;
183
269
  /**
184
270
  * Handle a flag change event from SSE.
185
- * Emits PROVIDER_CONFIGURATION_CHANGED to trigger re-evaluation.
271
+ * Triggers OFREP cache refresh, updates browser cache, and emits PROVIDER_CONFIGURATION_CHANGED.
186
272
  */
187
273
  private handleFlagChange;
188
274
  /**
@@ -248,6 +334,7 @@ declare class FlipswitchProvider {
248
334
  /**
249
335
  * SSE client for real-time flag change notifications.
250
336
  * Handles automatic reconnection with exponential backoff.
337
+ * Includes visibility API integration for browser environments.
251
338
  */
252
339
  declare class SseClient {
253
340
  private readonly baseUrl;
@@ -255,16 +342,38 @@ declare class SseClient {
255
342
  private readonly onFlagChange;
256
343
  private readonly onStatusChange?;
257
344
  private readonly telemetryHeaders?;
345
+ private readonly enableVisibilityHandling;
258
346
  private eventSource;
259
347
  private retryDelay;
260
348
  private reconnectTimeout;
261
349
  private closed;
350
+ private paused;
262
351
  private status;
263
- constructor(baseUrl: string, apiKey: string, onFlagChange: (event: FlagChangeEvent) => void, onStatusChange?: ((status: SseConnectionStatus) => void) | undefined, telemetryHeaders?: Record<string, string> | undefined);
352
+ private visibilityHandler;
353
+ private abortController;
354
+ constructor(baseUrl: string, apiKey: string, onFlagChange: (event: FlagChangeEvent) => void, onStatusChange?: ((status: SseConnectionStatus) => void) | undefined, telemetryHeaders?: Record<string, string> | undefined, enableVisibilityHandling?: boolean);
264
355
  /**
265
356
  * Start the SSE connection.
266
357
  */
267
358
  connect(): void;
359
+ /**
360
+ * Setup visibility API handling.
361
+ * Disconnects when tab is hidden, reconnects when visible.
362
+ */
363
+ private setupVisibilityHandling;
364
+ /**
365
+ * Pause the SSE connection (used by visibility API).
366
+ * Different from close() - can be resumed.
367
+ */
368
+ pause(): void;
369
+ /**
370
+ * Resume the SSE connection after being paused.
371
+ */
372
+ resume(): void;
373
+ /**
374
+ * Check if the connection is paused.
375
+ */
376
+ isPaused(): boolean;
268
377
  /**
269
378
  * Connect using fetch-based SSE (supports custom headers).
270
379
  */
@@ -327,4 +436,81 @@ declare class FlagCache {
327
436
  handleFlagChange(event: FlagChangeEvent): void;
328
437
  }
329
438
 
330
- export { type ConfigUpdatedEvent, FlagCache, type FlagChangeEvent, type FlagEvaluation, type FlagEvent, type FlagUpdatedEvent, type FlipswitchEventHandlers, type FlipswitchOptions, FlipswitchProvider, SseClient, type SseConnectionStatus };
439
+ /**
440
+ * Browser-based cache for flag values using localStorage.
441
+ * Provides persistence across page reloads and offline support.
442
+ */
443
+ interface CachedFlag {
444
+ value: unknown;
445
+ timestamp: number;
446
+ variant?: string;
447
+ reason?: string;
448
+ }
449
+ interface CacheMeta {
450
+ lastUpdated: number;
451
+ version: number;
452
+ }
453
+ /**
454
+ * Browser cache for persisting flag values to localStorage.
455
+ * Falls back gracefully when localStorage is unavailable.
456
+ */
457
+ declare class BrowserCache {
458
+ private readonly storage;
459
+ private readonly cacheVersion;
460
+ constructor();
461
+ /**
462
+ * Get storage instance if available.
463
+ */
464
+ private getStorage;
465
+ /**
466
+ * Check if browser cache is available.
467
+ */
468
+ isAvailable(): boolean;
469
+ /**
470
+ * Get a cached flag value.
471
+ */
472
+ get(flagKey: string): CachedFlag | null;
473
+ /**
474
+ * Set a cached flag value.
475
+ */
476
+ set(flagKey: string, value: unknown, variant?: string, reason?: string): void;
477
+ /**
478
+ * Set multiple cached flag values at once.
479
+ */
480
+ setAll(flags: Array<{
481
+ key: string;
482
+ value: unknown;
483
+ variant?: string;
484
+ reason?: string;
485
+ }>): void;
486
+ /**
487
+ * Get all cached flags.
488
+ */
489
+ getAll(): Map<string, CachedFlag>;
490
+ /**
491
+ * Invalidate a specific flag or all flags.
492
+ */
493
+ invalidate(flagKey?: string): void;
494
+ /**
495
+ * Update cache metadata.
496
+ */
497
+ private updateMeta;
498
+ /**
499
+ * Get cache metadata.
500
+ */
501
+ getMeta(): CacheMeta | null;
502
+ /**
503
+ * Get the age of a cached flag in milliseconds.
504
+ */
505
+ getAge(flagKey: string): number | null;
506
+ /**
507
+ * Check if a cached flag is stale based on max age.
508
+ */
509
+ isStale(flagKey: string, maxAgeMs: number): boolean;
510
+ /**
511
+ * Clear all Flipswitch data from localStorage.
512
+ */
513
+ clear(): void;
514
+ }
515
+
516
+ export { type ApiKeyRotatedEvent, BrowserCache, type ConfigUpdatedEvent, FlagCache, type FlagChangeEvent, type FlagEvaluation, type FlagEvent, type FlagUpdatedEvent, type FlipswitchEventHandlers, type FlipswitchOptions, FlipswitchProvider, SseClient, type SseConnectionStatus };