flagmint-js-sdk 1.2.19 → 1.2.21

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/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Flagmint JavaScript SDK
2
2
 
3
- **Version: 1.2.5**
3
+ **Version: 1.2.21**
4
4
 
5
- This SDK provides a framework-agnostic client for evaluating feature flags, with pluggable caching (sync or async) and transport strategies (WebSocket or long-polling). It's designed for both browser and server-side Node.js environments.
5
+ This SDK provides a javascript framework-agnostic client for evaluating feature flags, with pluggable caching (sync or async) and transport strategies (WebSocket or long-polling). It's designed for both browser and server-side Node.js environments.
6
6
 
7
7
  ## ✨ Key Features
8
8
 
@@ -21,10 +21,10 @@ npm install flagmint-js-sdk
21
21
  yarn add flagmint-js-sdk
22
22
  ```
23
23
 
24
- ## Quick Start
24
+ ## Quick Start
25
25
 
26
26
  ```ts
27
- import { FlagClient } from 'flagmint-sdk';
27
+ import { FlagClient } from 'flagmint-js-sdk';
28
28
 
29
29
  // Create a client instance
30
30
  const client = new FlagClient({
@@ -36,7 +36,7 @@ const client = new FlagClient({
36
36
  }
37
37
  });
38
38
 
39
- // Wait for initial connection
39
+ // Wait for initial connection and flag synchronization
40
40
  await client.ready();
41
41
 
42
42
  // Get flag values
@@ -44,7 +44,10 @@ const showBanner = client.getFlag('show_banner', false);
44
44
  const featureVersion = client.getFlag('feature_version', 'v1');
45
45
 
46
46
  // Update context and re-evaluate
47
- client.updateContext({ user_id: 'user456' });
47
+ await client.updateContext({
48
+ kind: "user",
49
+ user: { kind: "user", key: 'user456', email: 'user456@example.com' }
50
+ });
48
51
  ```
49
52
 
50
53
  ## FlagClientOptions
@@ -53,14 +56,14 @@ client.updateContext({ user_id: 'user456' });
53
56
  | --------------------- | ----------------------------------------- | ------------------ | --------------------------------------------------------------------- |
54
57
  | `apiKey` | `string` | **Required** | Your environment API key. |
55
58
  | `context` | `Record<string, any>` | `{}` | Initial evaluation context (e.g. user attributes). |
56
- | `enableOfflineCache` | `boolean` | `false` | Enable caching of flags locally. |
59
+ | `enableOfflineCache` | `boolean` | `true` | Enable caching of flags locally. |
57
60
  | `persistContext` | `boolean` | `false` | Persist evaluation context across sessions. | |
58
61
  | `cacheAdapter` | `CacheAdapter<C>` | Sync `cacheHelper` | Custom cache implementation (see examples). |
59
62
  | `transportMode` | `'auto' \| 'websocket' \| 'long-polling'` | `'auto'` | How to fetch flags: prefer WebSocket, or use long-polling transport. |
60
- | `onError` | `(error: Error) => void` | — | Callback for transport or initialization errors. |
63
+ | `onError` | `(error: Error) => void` | — | Callback for transport or initialization errors. Still throws on `ready()` if connection fails. |
61
64
  | `previewMode` | `boolean` | `false` | Evaluate using `rawFlags` only, bypassing remote fetch. |
62
65
  | `rawFlags` | `Record<string, FlagValue>` | — | Local-only flag definitions used when `previewMode: true`. |
63
- | `deferInitialization` | `boolean` | `false` | If `true`, client initialization is deferred until you call `init()`. |
66
+ | `deferInitialization` | `boolean` | `false` | If `true`, client initialization is deferred until you call `ready()`. |
64
67
 
65
68
  ## 🔧 Cache Adapters
66
69
 
@@ -69,10 +72,9 @@ client.updateContext({ user_id: 'user456' });
69
72
  By default, the SDK uses a **sync** `localStorage`-based helper in browsers or a `Map` in Node.js:
70
73
 
71
74
  ```ts
72
- import { FlagClient } from 'flagmint-sdk';
75
+ import { FlagClient, syncCache } from 'flagmint-js-sdk';
73
76
  // No setup needed for browser; for Node you can override:
74
- import { setCacheStorage } from 'flagmint-sdk/core/cacheHelper';
75
- setCacheStorage({
77
+ syncCache.setCacheStorage({
76
78
  getItem: key => myMap.get(key) ?? null,
77
79
  setItem: (key, val) => myMap.set(key, val),
78
80
  });
@@ -83,8 +85,7 @@ setCacheStorage({
83
85
  Use the **async** helper for server-side or React Native:
84
86
 
85
87
  ```ts
86
- import { FlagClient } from 'flagmint-sdk';
87
- import * as asyncCache from 'flagmint-sdk/core/cacheHelper.async';
88
+ import { FlagClient, asyncCache } from 'flagmint-js-sdk';
88
89
 
89
90
  const client = new FlagClient({
90
91
  apiKey: '...',
@@ -125,7 +126,7 @@ The SDK includes utilities to evaluate flag targeting rules and rollouts:
125
126
  ### `evaluateFlagValue(flag, context)`
126
127
 
127
128
  ```ts
128
- import { evaluateFlagValue } from 'flagmint-sdk/core/evaluation';
129
+ import { evaluateFlagValue } from 'flagmint-js-sdk';
129
130
 
130
131
  const result = evaluateFlagValue(flagValue, userContext);
131
132
  ```
@@ -137,7 +138,7 @@ const result = evaluateFlagValue(flagValue, userContext);
137
138
  ### `evaluateRollout(rollout, context)`
138
139
 
139
140
  ```ts
140
- import { evaluateRollout } from 'flagmint-sdk/core/evaluation';
141
+ import { evaluateRollout } from 'flagmint-js-sdk';
141
142
 
142
143
  const rolloutValue = evaluateRollout(rolloutConfig, userContext);
143
144
  ```
@@ -148,16 +149,16 @@ const rolloutValue = evaluateRollout(rolloutConfig, userContext);
148
149
  ### `rolloutUtils`
149
150
 
150
151
  * `hashToPercentage(value: string): number`
151
- * `pickVariant(value: string, variants: VariantRollout): T | null`
152
+ * `pickVariant(value: string, variants: VariantOption[]): string | number | boolean | null`
152
153
 
153
154
  These helpers underpin reliable, deterministic assignment of users to flag variants.
154
155
 
155
- ### `isInSegment(context, segment)`
156
+ ### `isInSegment(segment, context)`
156
157
 
157
158
  ```ts
158
- import { isInSegment } from 'flagmint-sdk/core/evaluation';
159
+ import { isInSegment } from 'flagmint-js-sdk';
159
160
 
160
- const inBetaSegment = isInSegment(userContext, betaSegment);
161
+ const inBetaSegment = isInSegment(betaSegment, userContext);
161
162
  ```
162
163
 
163
164
  Evaluates whether a user context matches a segment's criteria.
@@ -168,7 +169,7 @@ Evaluates whether a user context matches a segment's criteria.
168
169
 
169
170
  ```tsx
170
171
  import { useEffect, useState } from 'react';
171
- import { FlagClient } from 'flagmint-sdk';
172
+ import { FlagClient } from 'flagmint-js-sdk';
172
173
 
173
174
  const client = new FlagClient({
174
175
  apiKey: 'ff_...',
@@ -189,6 +190,16 @@ export function App() {
189
190
  theme: client.getFlag('theme', 'light')
190
191
  });
191
192
  });
193
+
194
+ // Subscribe to flag updates
195
+ const unsubscribe = client.subscribe((updatedFlags) => {
196
+ setFlags({
197
+ showBeta: client.getFlag('show_beta', false),
198
+ theme: client.getFlag('theme', 'light')
199
+ });
200
+ });
201
+
202
+ return () => unsubscribe();
192
203
  }, []);
193
204
 
194
205
  return (
@@ -202,8 +213,7 @@ export function App() {
202
213
  ### Node.js / Express
203
214
 
204
215
  ```ts
205
- import { FlagClient } from 'flagmint-sdk';
206
- import * as asyncCache from 'flagmint-sdk/core/cacheHelper.async';
216
+ import { FlagClient, asyncCache } from 'flagmint-js-sdk';
207
217
 
208
218
  const client = new FlagClient({
209
219
  apiKey: 'ff_...',
@@ -215,9 +225,26 @@ const client = new FlagClient({
215
225
  }
216
226
  });
217
227
 
228
+ // Wait for initialization
229
+ await client.ready();
230
+
218
231
  app.get('/api/feature', async (req, res) => {
219
- const userContext = { userId: req.user.id, plan: req.user.plan };
220
- const isEnabled = client.getFlag('new_api', false, userContext);
232
+ // Update context with proper structure
233
+ await client.updateContext({
234
+ kind: "multi",
235
+ user: {
236
+ kind: "user",
237
+ key: req.user.id,
238
+ email: req.user.email
239
+ },
240
+ organization: {
241
+ kind: "organization",
242
+ key: req.user.orgId,
243
+ plan: req.user.plan
244
+ }
245
+ });
246
+
247
+ const isEnabled = client.getFlag('new_api', false);
221
248
 
222
249
  res.json({ enabled: isEnabled });
223
250
  });
@@ -231,14 +258,26 @@ app.get('/api/feature', async (req, res) => {
231
258
  // Initial context
232
259
  const client = new FlagClient({
233
260
  apiKey: '...',
234
- context: { user_id: 'user123' }
261
+ context: {
262
+ kind: "user",
263
+ user: { kind: "user", key: 'user123' }
264
+ }
235
265
  });
236
266
 
237
- // Update context later
238
- client.updateContext({
239
- user_id: 'user123',
240
- plan: 'premium',
241
- country: 'CA'
267
+ // Update context later (e.g., after user upgrades)
268
+ await client.updateContext({
269
+ kind: "multi",
270
+ user: {
271
+ kind: "user",
272
+ key: 'user123',
273
+ email: 'user@example.com'
274
+ },
275
+ organization: {
276
+ kind: "organization",
277
+ key: 'org456',
278
+ plan: 'premium',
279
+ country: 'CA'
280
+ }
242
281
  });
243
282
  ```
244
283
 
@@ -248,7 +287,10 @@ client.updateContext({
248
287
  const client = new FlagClient({
249
288
  apiKey: '...',
250
289
  persistContext: true, // Saves to localStorage/AsyncStorage
251
- context: { user_id: 'user123' }
290
+ context: {
291
+ kind: "user",
292
+ user: { kind: "user", key: 'user123', email: 'user@example.com' }
293
+ }
252
294
  });
253
295
  ```
254
296
 
@@ -263,8 +305,11 @@ const client = new FlagClient({
263
305
  });
264
306
 
265
307
  // Later, when context is ready:
266
- client.updateContext({ user_id: 'user123' });
267
- await client.init();
308
+ await client.updateContext({
309
+ kind: "user",
310
+ user: { kind: "user", key: 'user123', email: 'user@example.com' }
311
+ });
312
+ await client.ready(); // Initializes and connects
268
313
  ```
269
314
 
270
315
  ## 🌍 Advanced Usage
@@ -282,6 +327,34 @@ const client = new FlagClient({
282
327
  });
283
328
  ```
284
329
 
330
+ **Important: Error Handling with Cache**
331
+
332
+ When the server is unreachable but cached flags exist:
333
+ - ✅ Cached flags ARE loaded and accessible via `getFlag()`
334
+ - ⚠️ `onError` callback is called with the connection error
335
+ - ❌ `ready()` promise still **rejects** with the error
336
+
337
+ This means you should handle both:
338
+
339
+ ```ts
340
+ const client = new FlagClient({
341
+ apiKey: '...',
342
+ enableOfflineCache: true,
343
+ onError: (error) => {
344
+ console.warn('Flagmint connection failed, using cached flags:', error);
345
+ }
346
+ });
347
+
348
+ try {
349
+ await client.ready();
350
+ console.log('Connected to Flagmint');
351
+ } catch (error) {
352
+ console.error('Flagmint unreachable:', error);
353
+ // Cached flags are still available!
354
+ const feature = client.getFlag('my_feature', false); // Works!
355
+ }
356
+ ```
357
+
285
358
  ### Preview Mode
286
359
 
287
360
  For testing or preview environments where you want to bypass remote flag fetching:
@@ -304,7 +377,7 @@ const checkout = client.getFlag('new_checkout', false); // true
304
377
  ### Custom Cache Implementations
305
378
 
306
379
  ```ts
307
- import { FlagClient } from 'flagmint-sdk';
380
+ import { FlagClient } from 'flagmint-js-sdk';
308
381
  import redis from 'redis';
309
382
 
310
383
  const redisClient = redis.createClient();
@@ -343,17 +416,26 @@ const client = new FlagClient({
343
416
  ### `FlagClient`
344
417
 
345
418
  **Methods:**
346
- - `ready(): Promise<void>` - Wait for initial connection
347
- - `getFlag<T>(key: string, defaultValue: T, context?: Record<string, any>): T` - Get flag value
348
- - `updateContext(context: Record<string, any>): void` - Update evaluation context
349
- - `init(): Promise<void>` - Initialize client (if deferred)
350
- - `disconnect(): void` - Close transport connection
351
-
352
- **Events:**
353
- - `flagsUpdated` - Fired when flags change
354
- - `contextUpdated` - Fired when context changes
355
- - `connected` - Fired when transport connects
356
- - `disconnected` - Fired when transport disconnects
419
+ - `ready(timeoutMs?: number): Promise<void>` - Wait for initial connection (default timeout: 3000ms)
420
+ - `getFlag<K>(key: K, fallback?: T): T` - Get flag value with optional fallback
421
+ - `getFlags(): FeatureFlags<T>` - Get all flags as an object
422
+ - `updateContext(context: Record<string, any>): Promise<void>` - Update evaluation context (async)
423
+ - `subscribe(callback: (flags) => void): () => void` - Subscribe to flag updates, returns unsubscribe function
424
+ - `destroy(): void` - Close transport connection and cleanup resources
425
+
426
+ ### Subscribing to Flag Updates
427
+
428
+ Instead of event listeners, use the `subscribe()` method:
429
+
430
+ ```ts
431
+ const unsubscribe = client.subscribe((flags) => {
432
+ console.log('Flags updated:', flags);
433
+ // Handle flag updates
434
+ });
435
+
436
+ // Later, to unsubscribe:
437
+ unsubscribe();
438
+ ```
357
439
 
358
440
  ### Cache Adapter Interface
359
441
 
@@ -403,9 +485,20 @@ sdk/
403
485
 
404
486
  ### Flags not updating
405
487
 
406
- - Confirm `enableOfflineCache: false` or cache is working correctly
488
+ - Confirm `enableOfflineCache` setting matches your needs
407
489
  - Check transport mode (WebSocket vs long-polling)
408
- - Verify context is properly set with `updateContext()`
490
+ - Verify context is properly set with `await updateContext()`
491
+ - Use `subscribe()` to listen for flag changes instead of polling
492
+
493
+ ### Cache behavior when server is down
494
+
495
+ **Important**: When `enableOfflineCache: true` (default):
496
+ - Cached flags are loaded even if the server is unreachable
497
+ - `ready()` will still throw an error on connection failure
498
+ - You can still access cached flags via `getFlag()` after catching the error
499
+ - Consider wrapping `ready()` in try/catch and continuing with cached data
500
+
501
+ See the "Error Handling with Cache" section above for examples.
409
502
 
410
503
  ### Performance issues
411
504
 
@@ -419,14 +512,9 @@ sdk/
419
512
  - **Issues**: Report on [GitHub](https://github.com/flagmint/js-sdk/issues)
420
513
  - **Email**: support@flagmint.io
421
514
 
422
- ## 🚀 Changelog
515
+ ## 🚀 Releases & Changelog
423
516
 
424
- ### v1.2.5
425
- - Enhanced async cache helper for better server-side support
426
- - Improved WebSocket connection stability
427
- - Better error reporting and handling
428
- - Added support for context persistence
429
- - Performance optimizations for large flag sets
517
+ See [GitHub Releases](https://github.com/flagmint/js-sdk/releases) for the complete version history, changelog, and upgrade guides.
430
518
 
431
519
  ## 📖 References
432
520