@flipswitch-io/sdk 0.1.4 → 0.1.6

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
@@ -21,27 +21,105 @@ interface FlipswitchOptions {
21
21
  * @default true
22
22
  */
23
23
  enableRealtime?: boolean;
24
- /**
25
- * Enable telemetry collection.
26
- * When enabled, the SDK sends usage statistics (SDK version, runtime version,
27
- * OS, architecture) to help improve the service. No personal data is collected.
28
- * @default true
29
- */
30
- enableTelemetry?: boolean;
31
24
  /**
32
25
  * Custom fetch function for making HTTP requests.
33
26
  * Useful for testing or custom networking needs.
34
27
  */
35
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;
36
67
  }
37
68
  /**
38
- * Event emitted when a flag changes.
69
+ * Event emitted when a single flag is updated.
39
70
  */
40
- interface FlagChangeEvent {
71
+ interface FlagUpdatedEvent {
72
+ /**
73
+ * The key of the flag that changed.
74
+ */
75
+ flagKey: string;
76
+ /**
77
+ * ISO timestamp of when the change occurred.
78
+ */
79
+ timestamp: string;
80
+ }
81
+ /**
82
+ * Event emitted when configuration changes that may affect multiple flags.
83
+ */
84
+ interface ConfigUpdatedEvent {
85
+ /**
86
+ * ISO timestamp of when the change occurred.
87
+ */
88
+ timestamp: string;
89
+ }
90
+ /**
91
+ * Event emitted when an API key has been rotated or rotation was aborted.
92
+ */
93
+ interface ApiKeyRotatedEvent {
41
94
  /**
42
- * The ID of the environment where the change occurred.
95
+ * ISO timestamp when the current key expires.
96
+ * Null if the rotation was aborted.
43
97
  */
44
- environmentId: number;
98
+ validUntil: string | null;
99
+ /**
100
+ * ISO timestamp of when the event occurred.
101
+ */
102
+ timestamp: string;
103
+ }
104
+ /**
105
+ * Union type for all flag events.
106
+ * Used internally to handle event types.
107
+ */
108
+ type FlagEvent = {
109
+ type: 'flag-updated';
110
+ data: FlagUpdatedEvent;
111
+ } | {
112
+ type: 'config-updated';
113
+ data: ConfigUpdatedEvent;
114
+ } | {
115
+ type: 'api-key-rotated';
116
+ data: ApiKeyRotatedEvent;
117
+ };
118
+ /**
119
+ * @deprecated Use FlagUpdatedEvent or ConfigUpdatedEvent instead.
120
+ * Event emitted when a flag changes (legacy format).
121
+ */
122
+ interface FlagChangeEvent {
45
123
  /**
46
124
  * The key of the flag that changed, or null for bulk invalidation.
47
125
  */
@@ -122,13 +200,24 @@ declare class FlipswitchProvider {
122
200
  private readonly baseUrl;
123
201
  private readonly apiKey;
124
202
  private readonly enableRealtime;
125
- private readonly enableTelemetry;
126
203
  private readonly fetchImpl;
127
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;
128
211
  private sseClient;
129
212
  private _status;
130
213
  private eventHandlers;
131
214
  private userEventHandlers;
215
+ private pollingTimer;
216
+ private sseRetryCount;
217
+ private isPollingFallbackActive;
218
+ private onlineHandler;
219
+ private offlineHandler;
220
+ private _isOnline;
132
221
  constructor(options: FlipswitchOptions, eventHandlers?: FlipswitchEventHandlers);
133
222
  private getTelemetrySdkHeader;
134
223
  private getTelemetryRuntimeHeader;
@@ -141,10 +230,34 @@ declare class FlipswitchProvider {
141
230
  * Validates the API key and starts SSE connection if real-time is enabled.
142
231
  */
143
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;
144
241
  /**
145
242
  * Called when the provider is shut down.
146
243
  */
147
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;
148
261
  /**
149
262
  * Start the SSE connection for real-time updates.
150
263
  */
@@ -155,7 +268,7 @@ declare class FlipswitchProvider {
155
268
  private getTelemetryHeadersMap;
156
269
  /**
157
270
  * Handle a flag change event from SSE.
158
- * Emits PROVIDER_CONFIGURATION_CHANGED to trigger re-evaluation.
271
+ * Triggers OFREP cache refresh, updates browser cache, and emits PROVIDER_CONFIGURATION_CHANGED.
159
272
  */
160
273
  private handleFlagChange;
161
274
  /**
@@ -221,6 +334,7 @@ declare class FlipswitchProvider {
221
334
  /**
222
335
  * SSE client for real-time flag change notifications.
223
336
  * Handles automatic reconnection with exponential backoff.
337
+ * Includes visibility API integration for browser environments.
224
338
  */
225
339
  declare class SseClient {
226
340
  private readonly baseUrl;
@@ -228,16 +342,38 @@ declare class SseClient {
228
342
  private readonly onFlagChange;
229
343
  private readonly onStatusChange?;
230
344
  private readonly telemetryHeaders?;
345
+ private readonly enableVisibilityHandling;
231
346
  private eventSource;
232
347
  private retryDelay;
233
348
  private reconnectTimeout;
234
349
  private closed;
350
+ private paused;
235
351
  private status;
236
- 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);
237
355
  /**
238
356
  * Start the SSE connection.
239
357
  */
240
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;
241
377
  /**
242
378
  * Connect using fetch-based SSE (supports custom headers).
243
379
  */
@@ -300,4 +436,81 @@ declare class FlagCache {
300
436
  handleFlagChange(event: FlagChangeEvent): void;
301
437
  }
302
438
 
303
- export { FlagCache, type FlagChangeEvent, type FlagEvaluation, 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
@@ -21,27 +21,105 @@ interface FlipswitchOptions {
21
21
  * @default true
22
22
  */
23
23
  enableRealtime?: boolean;
24
- /**
25
- * Enable telemetry collection.
26
- * When enabled, the SDK sends usage statistics (SDK version, runtime version,
27
- * OS, architecture) to help improve the service. No personal data is collected.
28
- * @default true
29
- */
30
- enableTelemetry?: boolean;
31
24
  /**
32
25
  * Custom fetch function for making HTTP requests.
33
26
  * Useful for testing or custom networking needs.
34
27
  */
35
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;
36
67
  }
37
68
  /**
38
- * Event emitted when a flag changes.
69
+ * Event emitted when a single flag is updated.
39
70
  */
40
- interface FlagChangeEvent {
71
+ interface FlagUpdatedEvent {
72
+ /**
73
+ * The key of the flag that changed.
74
+ */
75
+ flagKey: string;
76
+ /**
77
+ * ISO timestamp of when the change occurred.
78
+ */
79
+ timestamp: string;
80
+ }
81
+ /**
82
+ * Event emitted when configuration changes that may affect multiple flags.
83
+ */
84
+ interface ConfigUpdatedEvent {
85
+ /**
86
+ * ISO timestamp of when the change occurred.
87
+ */
88
+ timestamp: string;
89
+ }
90
+ /**
91
+ * Event emitted when an API key has been rotated or rotation was aborted.
92
+ */
93
+ interface ApiKeyRotatedEvent {
41
94
  /**
42
- * The ID of the environment where the change occurred.
95
+ * ISO timestamp when the current key expires.
96
+ * Null if the rotation was aborted.
43
97
  */
44
- environmentId: number;
98
+ validUntil: string | null;
99
+ /**
100
+ * ISO timestamp of when the event occurred.
101
+ */
102
+ timestamp: string;
103
+ }
104
+ /**
105
+ * Union type for all flag events.
106
+ * Used internally to handle event types.
107
+ */
108
+ type FlagEvent = {
109
+ type: 'flag-updated';
110
+ data: FlagUpdatedEvent;
111
+ } | {
112
+ type: 'config-updated';
113
+ data: ConfigUpdatedEvent;
114
+ } | {
115
+ type: 'api-key-rotated';
116
+ data: ApiKeyRotatedEvent;
117
+ };
118
+ /**
119
+ * @deprecated Use FlagUpdatedEvent or ConfigUpdatedEvent instead.
120
+ * Event emitted when a flag changes (legacy format).
121
+ */
122
+ interface FlagChangeEvent {
45
123
  /**
46
124
  * The key of the flag that changed, or null for bulk invalidation.
47
125
  */
@@ -122,13 +200,24 @@ declare class FlipswitchProvider {
122
200
  private readonly baseUrl;
123
201
  private readonly apiKey;
124
202
  private readonly enableRealtime;
125
- private readonly enableTelemetry;
126
203
  private readonly fetchImpl;
127
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;
128
211
  private sseClient;
129
212
  private _status;
130
213
  private eventHandlers;
131
214
  private userEventHandlers;
215
+ private pollingTimer;
216
+ private sseRetryCount;
217
+ private isPollingFallbackActive;
218
+ private onlineHandler;
219
+ private offlineHandler;
220
+ private _isOnline;
132
221
  constructor(options: FlipswitchOptions, eventHandlers?: FlipswitchEventHandlers);
133
222
  private getTelemetrySdkHeader;
134
223
  private getTelemetryRuntimeHeader;
@@ -141,10 +230,34 @@ declare class FlipswitchProvider {
141
230
  * Validates the API key and starts SSE connection if real-time is enabled.
142
231
  */
143
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;
144
241
  /**
145
242
  * Called when the provider is shut down.
146
243
  */
147
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;
148
261
  /**
149
262
  * Start the SSE connection for real-time updates.
150
263
  */
@@ -155,7 +268,7 @@ declare class FlipswitchProvider {
155
268
  private getTelemetryHeadersMap;
156
269
  /**
157
270
  * Handle a flag change event from SSE.
158
- * Emits PROVIDER_CONFIGURATION_CHANGED to trigger re-evaluation.
271
+ * Triggers OFREP cache refresh, updates browser cache, and emits PROVIDER_CONFIGURATION_CHANGED.
159
272
  */
160
273
  private handleFlagChange;
161
274
  /**
@@ -221,6 +334,7 @@ declare class FlipswitchProvider {
221
334
  /**
222
335
  * SSE client for real-time flag change notifications.
223
336
  * Handles automatic reconnection with exponential backoff.
337
+ * Includes visibility API integration for browser environments.
224
338
  */
225
339
  declare class SseClient {
226
340
  private readonly baseUrl;
@@ -228,16 +342,38 @@ declare class SseClient {
228
342
  private readonly onFlagChange;
229
343
  private readonly onStatusChange?;
230
344
  private readonly telemetryHeaders?;
345
+ private readonly enableVisibilityHandling;
231
346
  private eventSource;
232
347
  private retryDelay;
233
348
  private reconnectTimeout;
234
349
  private closed;
350
+ private paused;
235
351
  private status;
236
- 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);
237
355
  /**
238
356
  * Start the SSE connection.
239
357
  */
240
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;
241
377
  /**
242
378
  * Connect using fetch-based SSE (supports custom headers).
243
379
  */
@@ -300,4 +436,81 @@ declare class FlagCache {
300
436
  handleFlagChange(event: FlagChangeEvent): void;
301
437
  }
302
438
 
303
- export { FlagCache, type FlagChangeEvent, type FlagEvaluation, 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 };