@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.mjs CHANGED
@@ -9,26 +9,37 @@ import { OFREPWebProvider } from "@openfeature/ofrep-web-provider";
9
9
  var MIN_RETRY_DELAY = 1e3;
10
10
  var MAX_RETRY_DELAY = 3e4;
11
11
  var SseClient = class {
12
- constructor(baseUrl, apiKey, onFlagChange, onStatusChange, telemetryHeaders) {
12
+ constructor(baseUrl, apiKey, onFlagChange, onStatusChange, telemetryHeaders, enableVisibilityHandling = true) {
13
13
  this.baseUrl = baseUrl;
14
14
  this.apiKey = apiKey;
15
15
  this.onFlagChange = onFlagChange;
16
16
  this.onStatusChange = onStatusChange;
17
17
  this.telemetryHeaders = telemetryHeaders;
18
+ this.enableVisibilityHandling = enableVisibilityHandling;
18
19
  this.eventSource = null;
19
20
  this.retryDelay = MIN_RETRY_DELAY;
20
21
  this.reconnectTimeout = null;
21
22
  this.closed = false;
23
+ this.paused = false;
22
24
  this.status = "disconnected";
25
+ this.visibilityHandler = null;
26
+ this.abortController = null;
23
27
  }
24
28
  /**
25
29
  * Start the SSE connection.
26
30
  */
27
31
  connect() {
28
- if (this.closed) return;
32
+ if (this.closed || this.paused) return;
33
+ if (this.abortController) {
34
+ this.abortController.abort();
35
+ }
36
+ this.abortController = new AbortController();
29
37
  if (this.eventSource) {
30
38
  this.eventSource.close();
31
39
  }
40
+ if (this.enableVisibilityHandling) {
41
+ this.setupVisibilityHandling();
42
+ }
32
43
  this.updateStatus("connecting");
33
44
  const url = `${this.baseUrl}/api/v1/flags/events`;
34
45
  try {
@@ -38,11 +49,62 @@ var SseClient = class {
38
49
  this.connectWithPolyfill(url);
39
50
  }
40
51
  } catch (error) {
41
- console.error("[Flipswitch] Failed to establish SSE connection:", error);
52
+ console.warn("[Flipswitch] Failed to establish SSE connection:", error);
42
53
  this.updateStatus("error");
43
54
  this.scheduleReconnect();
44
55
  }
45
56
  }
57
+ /**
58
+ * Setup visibility API handling.
59
+ * Disconnects when tab is hidden, reconnects when visible.
60
+ */
61
+ setupVisibilityHandling() {
62
+ if (typeof document === "undefined" || this.visibilityHandler) return;
63
+ this.visibilityHandler = () => {
64
+ if (document.hidden) {
65
+ this.pause();
66
+ } else {
67
+ this.resume();
68
+ }
69
+ };
70
+ document.addEventListener("visibilitychange", this.visibilityHandler);
71
+ }
72
+ /**
73
+ * Pause the SSE connection (used by visibility API).
74
+ * Different from close() - can be resumed.
75
+ */
76
+ pause() {
77
+ if (this.paused || this.closed) return;
78
+ this.paused = true;
79
+ if (this.abortController) {
80
+ this.abortController.abort();
81
+ this.abortController = null;
82
+ }
83
+ if (this.reconnectTimeout) {
84
+ clearTimeout(this.reconnectTimeout);
85
+ this.reconnectTimeout = null;
86
+ }
87
+ if (this.eventSource) {
88
+ this.eventSource.close();
89
+ this.eventSource = null;
90
+ }
91
+ this.updateStatus("disconnected");
92
+ }
93
+ /**
94
+ * Resume the SSE connection after being paused.
95
+ */
96
+ resume() {
97
+ if (!this.paused || this.closed) return;
98
+ this.paused = false;
99
+ this.retryDelay = MIN_RETRY_DELAY;
100
+ this.connect();
101
+ }
102
+ /**
103
+ * Check if the connection is paused.
104
+ */
105
+ isPaused() {
106
+ return this.paused;
107
+ }
46
108
  /**
47
109
  * Connect using fetch-based SSE (supports custom headers).
48
110
  */
@@ -56,7 +118,8 @@ var SseClient = class {
56
118
  };
57
119
  const response = await fetch(url, {
58
120
  method: "GET",
59
- headers
121
+ headers,
122
+ signal: this.abortController?.signal
60
123
  });
61
124
  if (!response.ok) {
62
125
  throw new Error(`SSE connection failed: ${response.status}`);
@@ -97,14 +160,14 @@ var SseClient = class {
97
160
  };
98
161
  processStream().catch((error) => {
99
162
  if (!this.closed) {
100
- console.error("[Flipswitch] SSE stream error:", error);
163
+ console.warn("[Flipswitch] SSE stream error:", error);
101
164
  this.updateStatus("error");
102
165
  this.scheduleReconnect();
103
166
  }
104
167
  });
105
168
  } catch (error) {
106
169
  if (!this.closed) {
107
- console.error("[Flipswitch] SSE connection error:", error);
170
+ console.warn("[Flipswitch] SSE connection error:", error);
108
171
  this.updateStatus("error");
109
172
  this.scheduleReconnect();
110
173
  }
@@ -124,13 +187,34 @@ var SseClient = class {
124
187
  if (eventType === "heartbeat") {
125
188
  return;
126
189
  }
127
- if (eventType === "flag-change") {
128
- try {
129
- const event = JSON.parse(data);
190
+ try {
191
+ if (eventType === "flag-updated") {
192
+ const parsed = JSON.parse(data);
193
+ const event = {
194
+ flagKey: parsed.flagKey,
195
+ timestamp: parsed.timestamp
196
+ };
130
197
  this.onFlagChange(event);
131
- } catch (error) {
132
- console.error("[Flipswitch] Failed to parse flag-change event:", error);
198
+ } else if (eventType === "config-updated") {
199
+ const parsed = JSON.parse(data);
200
+ const event = {
201
+ flagKey: null,
202
+ // null indicates all flags should be refreshed
203
+ timestamp: parsed.timestamp
204
+ };
205
+ this.onFlagChange(event);
206
+ } else if (eventType === "api-key-rotated") {
207
+ const parsed = JSON.parse(data);
208
+ if (!parsed.validUntil) {
209
+ console.info("[Flipswitch] API key rotation was aborted");
210
+ } else {
211
+ console.warn(
212
+ `[Flipswitch] API key was rotated. Current key valid until: ${parsed.validUntil}`
213
+ );
214
+ }
133
215
  }
216
+ } catch (error) {
217
+ console.error(`[Flipswitch] Failed to parse ${eventType} event:`, error);
134
218
  }
135
219
  }
136
220
  /**
@@ -167,6 +251,14 @@ var SseClient = class {
167
251
  close() {
168
252
  this.closed = true;
169
253
  this.updateStatus("disconnected");
254
+ if (this.visibilityHandler && typeof document !== "undefined") {
255
+ document.removeEventListener("visibilitychange", this.visibilityHandler);
256
+ this.visibilityHandler = null;
257
+ }
258
+ if (this.abortController) {
259
+ this.abortController.abort();
260
+ this.abortController = null;
261
+ }
170
262
  if (this.reconnectTimeout) {
171
263
  clearTimeout(this.reconnectTimeout);
172
264
  this.reconnectTimeout = null;
@@ -178,9 +270,202 @@ var SseClient = class {
178
270
  }
179
271
  };
180
272
 
273
+ // src/browser-cache.ts
274
+ var CACHE_PREFIX = "flipswitch:flags:";
275
+ var CACHE_META_KEY = "flipswitch:cache-meta";
276
+ var BrowserCache = class {
277
+ constructor() {
278
+ this.cacheVersion = 1;
279
+ this.storage = this.getStorage();
280
+ }
281
+ /**
282
+ * Get storage instance if available.
283
+ */
284
+ getStorage() {
285
+ if (typeof window === "undefined") return null;
286
+ try {
287
+ const testKey = "__flipswitch_test__";
288
+ window.localStorage.setItem(testKey, "test");
289
+ window.localStorage.removeItem(testKey);
290
+ return window.localStorage;
291
+ } catch {
292
+ return null;
293
+ }
294
+ }
295
+ /**
296
+ * Check if browser cache is available.
297
+ */
298
+ isAvailable() {
299
+ return this.storage !== null;
300
+ }
301
+ /**
302
+ * Get a cached flag value.
303
+ */
304
+ get(flagKey) {
305
+ if (!this.storage) return null;
306
+ try {
307
+ const key = CACHE_PREFIX + flagKey;
308
+ const item = this.storage.getItem(key);
309
+ if (!item) return null;
310
+ const cached = JSON.parse(item);
311
+ return cached;
312
+ } catch {
313
+ return null;
314
+ }
315
+ }
316
+ /**
317
+ * Set a cached flag value.
318
+ */
319
+ set(flagKey, value, variant, reason) {
320
+ if (!this.storage) return;
321
+ try {
322
+ const key = CACHE_PREFIX + flagKey;
323
+ const cached = {
324
+ value,
325
+ timestamp: Date.now(),
326
+ variant,
327
+ reason
328
+ };
329
+ this.storage.setItem(key, JSON.stringify(cached));
330
+ this.updateMeta();
331
+ } catch {
332
+ }
333
+ }
334
+ /**
335
+ * Set multiple cached flag values at once.
336
+ */
337
+ setAll(flags) {
338
+ if (!this.storage) return;
339
+ try {
340
+ for (const flag of flags) {
341
+ const cacheKey = CACHE_PREFIX + flag.key;
342
+ const cached = {
343
+ value: flag.value,
344
+ timestamp: Date.now(),
345
+ variant: flag.variant,
346
+ reason: flag.reason
347
+ };
348
+ this.storage.setItem(cacheKey, JSON.stringify(cached));
349
+ }
350
+ this.updateMeta();
351
+ } catch {
352
+ }
353
+ }
354
+ /**
355
+ * Get all cached flags.
356
+ */
357
+ getAll() {
358
+ const result = /* @__PURE__ */ new Map();
359
+ if (!this.storage) return result;
360
+ try {
361
+ for (let i = 0; i < this.storage.length; i++) {
362
+ const key = this.storage.key(i);
363
+ if (key?.startsWith(CACHE_PREFIX)) {
364
+ const flagKey = key.slice(CACHE_PREFIX.length);
365
+ const item = this.storage.getItem(key);
366
+ if (item) {
367
+ const cached = JSON.parse(item);
368
+ result.set(flagKey, cached);
369
+ }
370
+ }
371
+ }
372
+ } catch {
373
+ }
374
+ return result;
375
+ }
376
+ /**
377
+ * Invalidate a specific flag or all flags.
378
+ */
379
+ invalidate(flagKey) {
380
+ if (!this.storage) return;
381
+ try {
382
+ if (flagKey) {
383
+ this.storage.removeItem(CACHE_PREFIX + flagKey);
384
+ } else {
385
+ const keysToRemove = [];
386
+ for (let i = 0; i < this.storage.length; i++) {
387
+ const key = this.storage.key(i);
388
+ if (key?.startsWith(CACHE_PREFIX)) {
389
+ keysToRemove.push(key);
390
+ }
391
+ }
392
+ for (const key of keysToRemove) {
393
+ this.storage.removeItem(key);
394
+ }
395
+ }
396
+ this.updateMeta();
397
+ } catch {
398
+ }
399
+ }
400
+ /**
401
+ * Update cache metadata.
402
+ */
403
+ updateMeta() {
404
+ if (!this.storage) return;
405
+ try {
406
+ const meta = {
407
+ lastUpdated: Date.now(),
408
+ version: this.cacheVersion
409
+ };
410
+ this.storage.setItem(CACHE_META_KEY, JSON.stringify(meta));
411
+ } catch {
412
+ }
413
+ }
414
+ /**
415
+ * Get cache metadata.
416
+ */
417
+ getMeta() {
418
+ if (!this.storage) return null;
419
+ try {
420
+ const item = this.storage.getItem(CACHE_META_KEY);
421
+ if (!item) return null;
422
+ return JSON.parse(item);
423
+ } catch {
424
+ return null;
425
+ }
426
+ }
427
+ /**
428
+ * Get the age of a cached flag in milliseconds.
429
+ */
430
+ getAge(flagKey) {
431
+ const cached = this.get(flagKey);
432
+ if (!cached) return null;
433
+ return Date.now() - cached.timestamp;
434
+ }
435
+ /**
436
+ * Check if a cached flag is stale based on max age.
437
+ */
438
+ isStale(flagKey, maxAgeMs) {
439
+ const age = this.getAge(flagKey);
440
+ if (age === null) return true;
441
+ return age > maxAgeMs;
442
+ }
443
+ /**
444
+ * Clear all Flipswitch data from localStorage.
445
+ */
446
+ clear() {
447
+ if (!this.storage) return;
448
+ try {
449
+ const keysToRemove = [];
450
+ for (let i = 0; i < this.storage.length; i++) {
451
+ const key = this.storage.key(i);
452
+ if (key?.startsWith("flipswitch:")) {
453
+ keysToRemove.push(key);
454
+ }
455
+ }
456
+ for (const key of keysToRemove) {
457
+ this.storage.removeItem(key);
458
+ }
459
+ } catch {
460
+ }
461
+ }
462
+ };
463
+
181
464
  // src/provider.ts
182
465
  var DEFAULT_BASE_URL = "https://api.flipswitch.io";
183
- var SDK_VERSION = "0.1.1";
466
+ var SDK_VERSION = "0.1.2";
467
+ var DEFAULT_POLLING_INTERVAL = 3e4;
468
+ var DEFAULT_MAX_SSE_RETRIES = 5;
184
469
  var FlipswitchProvider = class {
185
470
  constructor(options, eventHandlers) {
186
471
  this.metadata = {
@@ -191,23 +476,41 @@ var FlipswitchProvider = class {
191
476
  this._status = ClientProviderStatus.NOT_READY;
192
477
  this.eventHandlers = /* @__PURE__ */ new Map();
193
478
  this.userEventHandlers = {};
479
+ this.pollingTimer = null;
480
+ this.sseRetryCount = 0;
481
+ this.isPollingFallbackActive = false;
482
+ this.onlineHandler = null;
483
+ this.offlineHandler = null;
484
+ this._isOnline = true;
194
485
  this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
195
486
  this.apiKey = options.apiKey;
196
487
  this.enableRealtime = options.enableRealtime ?? true;
197
- this.enableTelemetry = options.enableTelemetry ?? true;
198
488
  this.fetchImpl = options.fetchImplementation ?? (typeof window !== "undefined" ? fetch.bind(window) : fetch);
199
489
  this.userEventHandlers = eventHandlers ?? {};
200
- const headers = [["X-API-Key", this.apiKey]];
201
- if (this.enableTelemetry) {
202
- headers.push(["X-Flipswitch-SDK", this.getTelemetrySdkHeader()]);
203
- headers.push(["X-Flipswitch-Runtime", this.getTelemetryRuntimeHeader()]);
204
- headers.push(["X-Flipswitch-OS", this.getTelemetryOsHeader()]);
205
- headers.push(["X-Flipswitch-Features", this.getTelemetryFeaturesHeader()]);
490
+ const isBrowser = typeof window !== "undefined";
491
+ this.enableVisibilityHandling = options.enableVisibilityHandling ?? isBrowser;
492
+ this.offlineMode = options.offlineMode ?? isBrowser;
493
+ this.enablePollingFallback = options.enablePollingFallback ?? true;
494
+ this.pollingInterval = options.pollingInterval ?? DEFAULT_POLLING_INTERVAL;
495
+ this.maxSseRetries = options.maxSseRetries ?? DEFAULT_MAX_SSE_RETRIES;
496
+ const persistCache = options.persistCache ?? isBrowser;
497
+ this.browserCache = persistCache ? new BrowserCache() : null;
498
+ if (typeof navigator !== "undefined") {
499
+ this._isOnline = navigator.onLine;
206
500
  }
501
+ const headers = [
502
+ ["X-API-Key", this.apiKey],
503
+ ["X-Flipswitch-SDK", this.getTelemetrySdkHeader()],
504
+ ["X-Flipswitch-Runtime", this.getTelemetryRuntimeHeader()],
505
+ ["X-Flipswitch-OS", this.getTelemetryOsHeader()],
506
+ ["X-Flipswitch-Features", this.getTelemetryFeaturesHeader()]
507
+ ];
207
508
  this.ofrepProvider = new OFREPWebProvider({
208
509
  baseUrl: this.baseUrl,
209
510
  fetchImplementation: this.fetchImpl,
210
- headers
511
+ headers,
512
+ pollInterval: 0
513
+ // Disable polling - SSE handles real-time updates
211
514
  });
212
515
  }
213
516
  getTelemetrySdkHeader() {
@@ -261,7 +564,6 @@ var FlipswitchProvider = class {
261
564
  return `sse=${this.enableRealtime}`;
262
565
  }
263
566
  getTelemetryHeaders() {
264
- if (!this.enableTelemetry) return {};
265
567
  return {
266
568
  "X-Flipswitch-SDK": this.getTelemetrySdkHeader(),
267
569
  "X-Flipswitch-Runtime": this.getTelemetryRuntimeHeader(),
@@ -278,6 +580,13 @@ var FlipswitchProvider = class {
278
580
  */
279
581
  async initialize(context) {
280
582
  this._status = ClientProviderStatus.NOT_READY;
583
+ this.setupOfflineHandling();
584
+ if (!this._isOnline && this.offlineMode) {
585
+ console.warn("[Flipswitch] Starting in offline mode - using cached flag values");
586
+ this._status = ClientProviderStatus.STALE;
587
+ this.emit(ClientProviderEvents.Stale);
588
+ return;
589
+ }
281
590
  try {
282
591
  await this.ofrepProvider.initialize(context);
283
592
  } catch (error) {
@@ -312,15 +621,107 @@ var FlipswitchProvider = class {
312
621
  this._status = ClientProviderStatus.READY;
313
622
  this.emit(ClientProviderEvents.Ready);
314
623
  }
624
+ /**
625
+ * Setup online/offline event handling.
626
+ */
627
+ setupOfflineHandling() {
628
+ if (typeof window === "undefined" || !this.offlineMode) return;
629
+ this.onlineHandler = () => {
630
+ this._isOnline = true;
631
+ console.info("[Flipswitch] Connection restored - refreshing flags");
632
+ if (this.enableRealtime && this.sseClient) {
633
+ this.sseClient.resume();
634
+ }
635
+ this.refreshFlags();
636
+ };
637
+ this.offlineHandler = () => {
638
+ this._isOnline = false;
639
+ console.warn("[Flipswitch] Connection lost - serving cached values");
640
+ if (this.sseClient) {
641
+ this.sseClient.pause();
642
+ }
643
+ this.stopPolling();
644
+ if (this._status !== ClientProviderStatus.STALE) {
645
+ this._status = ClientProviderStatus.STALE;
646
+ this.emit(ClientProviderEvents.Stale);
647
+ }
648
+ };
649
+ window.addEventListener("online", this.onlineHandler);
650
+ window.addEventListener("offline", this.offlineHandler);
651
+ }
652
+ /**
653
+ * Refresh flags from the server.
654
+ */
655
+ async refreshFlags() {
656
+ try {
657
+ await this.ofrepProvider.onContextChange?.({}, {});
658
+ if (this._status === ClientProviderStatus.STALE) {
659
+ this._status = ClientProviderStatus.READY;
660
+ this.emit(ClientProviderEvents.Ready);
661
+ }
662
+ this.emit(ClientProviderEvents.ConfigurationChanged);
663
+ } catch (error) {
664
+ console.warn("[Flipswitch] Failed to refresh flags:", error);
665
+ }
666
+ }
315
667
  /**
316
668
  * Called when the provider is shut down.
317
669
  */
318
670
  async onClose() {
319
671
  this.sseClient?.close();
320
672
  this.sseClient = null;
673
+ this.stopPolling();
674
+ if (typeof window !== "undefined") {
675
+ if (this.onlineHandler) {
676
+ window.removeEventListener("online", this.onlineHandler);
677
+ this.onlineHandler = null;
678
+ }
679
+ if (this.offlineHandler) {
680
+ window.removeEventListener("offline", this.offlineHandler);
681
+ this.offlineHandler = null;
682
+ }
683
+ }
321
684
  await this.ofrepProvider.onClose?.();
322
685
  this._status = ClientProviderStatus.NOT_READY;
323
686
  }
687
+ /**
688
+ * Stop the polling fallback.
689
+ */
690
+ stopPolling() {
691
+ if (this.pollingTimer) {
692
+ clearInterval(this.pollingTimer);
693
+ this.pollingTimer = null;
694
+ this.isPollingFallbackActive = false;
695
+ }
696
+ }
697
+ /**
698
+ * Start polling fallback when SSE fails.
699
+ */
700
+ startPollingFallback() {
701
+ if (this.isPollingFallbackActive || !this.enablePollingFallback) return;
702
+ console.info(`[Flipswitch] Starting polling fallback (interval: ${this.pollingInterval}ms)`);
703
+ this.isPollingFallbackActive = true;
704
+ this.pollingTimer = setInterval(async () => {
705
+ if (!this._isOnline) return;
706
+ try {
707
+ await this.refreshFlags();
708
+ } catch (error) {
709
+ console.warn("[Flipswitch] Polling refresh failed:", error);
710
+ }
711
+ }, this.pollingInterval);
712
+ }
713
+ /**
714
+ * Check if the provider is currently online.
715
+ */
716
+ isOnline() {
717
+ return this._isOnline;
718
+ }
719
+ /**
720
+ * Check if polling fallback is active.
721
+ */
722
+ isPollingActive() {
723
+ return this.isPollingFallbackActive;
724
+ }
324
725
  /**
325
726
  * Start the SSE connection for real-time updates.
326
727
  */
@@ -335,14 +736,27 @@ var FlipswitchProvider = class {
335
736
  (status) => {
336
737
  this.userEventHandlers.onConnectionStatusChange?.(status);
337
738
  if (status === "error") {
739
+ this.sseRetryCount++;
740
+ if (this.sseRetryCount >= this.maxSseRetries && this.enablePollingFallback) {
741
+ console.warn(`[Flipswitch] SSE failed after ${this.sseRetryCount} retries - falling back to polling`);
742
+ this.startPollingFallback();
743
+ }
338
744
  this._status = ClientProviderStatus.STALE;
339
745
  this.emit(ClientProviderEvents.Stale);
340
- } else if (status === "connected" && this._status === ClientProviderStatus.STALE) {
341
- this._status = ClientProviderStatus.READY;
342
- this.emit(ClientProviderEvents.Ready);
746
+ } else if (status === "connected") {
747
+ this.sseRetryCount = 0;
748
+ if (this.isPollingFallbackActive) {
749
+ console.info("[Flipswitch] SSE reconnected - stopping polling fallback");
750
+ this.stopPolling();
751
+ }
752
+ if (this._status === ClientProviderStatus.STALE) {
753
+ this._status = ClientProviderStatus.READY;
754
+ this.emit(ClientProviderEvents.Ready);
755
+ }
343
756
  }
344
757
  },
345
- telemetryHeaders
758
+ telemetryHeaders,
759
+ this.enableVisibilityHandling
346
760
  );
347
761
  this.sseClient.connect();
348
762
  }
@@ -350,9 +764,6 @@ var FlipswitchProvider = class {
350
764
  * Get telemetry headers as a map.
351
765
  */
352
766
  getTelemetryHeadersMap() {
353
- if (!this.enableTelemetry) {
354
- return void 0;
355
- }
356
767
  return {
357
768
  "X-Flipswitch-SDK": this.getTelemetrySdkHeader(),
358
769
  "X-Flipswitch-Runtime": this.getTelemetryRuntimeHeader(),
@@ -362,10 +773,22 @@ var FlipswitchProvider = class {
362
773
  }
363
774
  /**
364
775
  * Handle a flag change event from SSE.
365
- * Emits PROVIDER_CONFIGURATION_CHANGED to trigger re-evaluation.
776
+ * Triggers OFREP cache refresh, updates browser cache, and emits PROVIDER_CONFIGURATION_CHANGED.
366
777
  */
367
- handleFlagChange(event) {
778
+ async handleFlagChange(event) {
368
779
  this.userEventHandlers.onFlagChange?.(event);
780
+ if (this.browserCache) {
781
+ if (event.flagKey) {
782
+ this.browserCache.invalidate(event.flagKey);
783
+ } else {
784
+ this.browserCache.invalidate();
785
+ }
786
+ }
787
+ try {
788
+ await this.ofrepProvider.onContextChange?.({}, {});
789
+ } catch (error) {
790
+ console.warn("[Flipswitch] Failed to refresh flags after SSE event:", error);
791
+ }
369
792
  this.emit(ClientProviderEvents.ConfigurationChanged);
370
793
  }
371
794
  /**
@@ -614,6 +1037,7 @@ var FlagCache = class {
614
1037
  }
615
1038
  };
616
1039
  export {
1040
+ BrowserCache,
617
1041
  FlagCache,
618
1042
  FlipswitchProvider,
619
1043
  SseClient
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flipswitch-io/sdk",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Flipswitch SDK with real-time SSE support for OpenFeature",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -56,7 +56,7 @@
56
56
  "@openfeature/ofrep-provider": "^0.2.2",
57
57
  "@openfeature/server-sdk": "^1.17.0",
58
58
  "@openfeature/web-sdk": "^1.7.2",
59
- "@types/node": "^20.11.0",
59
+ "@types/node": "^24.0.0",
60
60
  "eslint": "^9.0.0",
61
61
  "tsup": "^8.0.1",
62
62
  "tsx": "^4.7.0",