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 +143 -55
- package/dist/flagmint.cjs.js +8 -8
- package/dist/flagmint.es.js +4654 -4595
- package/dist/flagmint.umd.js +8 -8
- package/dist/index.d.ts +2 -1
- package/dist/sdk/core/client.d.ts +2 -0
- package/dist/sdk/core/evaluation/rolloutUtils.d.ts +2 -2
- package/dist/sdk/core/evaluation/types.d.ts +3 -1
- package/dist/sdk/core/helpers/ensureContextSource.d.ts +1 -0
- package/dist/sdk/core/helpers/logger.d.ts +11 -0
- package/dist/sdk/core/transports/WebsocketTransport.d.ts +1 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Flagmint JavaScript SDK
|
|
2
2
|
|
|
3
|
-
**Version: 1.2.
|
|
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
|
-
##
|
|
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({
|
|
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` | `
|
|
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 `
|
|
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
|
-
|
|
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
|
|
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
|
|
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:
|
|
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(
|
|
156
|
+
### `isInSegment(segment, context)`
|
|
156
157
|
|
|
157
158
|
```ts
|
|
158
|
-
import { isInSegment } from 'flagmint-sdk
|
|
159
|
+
import { isInSegment } from 'flagmint-js-sdk';
|
|
159
160
|
|
|
160
|
-
const inBetaSegment = isInSegment(
|
|
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
|
-
|
|
220
|
-
|
|
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: {
|
|
261
|
+
context: {
|
|
262
|
+
kind: "user",
|
|
263
|
+
user: { kind: "user", key: 'user123' }
|
|
264
|
+
}
|
|
235
265
|
});
|
|
236
266
|
|
|
237
|
-
// Update context later
|
|
238
|
-
client.updateContext({
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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: {
|
|
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({
|
|
267
|
-
|
|
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<
|
|
348
|
-
- `
|
|
349
|
-
- `
|
|
350
|
-
- `
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|