flagmint-js-sdk 1.2.4 → 1.2.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/README.md +323 -16
- package/dist/flagmint.cjs.js +1 -1
- package/dist/flagmint.es.js +13 -12
- package/dist/flagmint.umd.js +6 -6
- package/dist/sdk/core/client.d.ts +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,16 +1,53 @@
|
|
|
1
1
|
# Flagmint JavaScript SDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Version: 1.2.5**
|
|
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.
|
|
6
|
+
|
|
7
|
+
## ✨ Key Features
|
|
8
|
+
|
|
9
|
+
- 🎯 **Framework-Agnostic**: Works with React, Vue, vanilla JS, Node.js, and more
|
|
10
|
+
- 🔄 **Flexible Transport**: WebSocket for real-time updates or long-polling fallback
|
|
11
|
+
- 💾 **Pluggable Caching**: Use built-in sync cache, async cache (Redis/filesystem), or custom implementations
|
|
12
|
+
- 🚀 **Server & Browser Support**: Compatible with browser environments, Node.js, React Native
|
|
13
|
+
- 🔒 **Type-Safe**: Full TypeScript support with comprehensive type definitions
|
|
14
|
+
- ⚡ **Zero-Config Defaults**: Works out of the box with sensible defaults
|
|
4
15
|
|
|
5
16
|
## 📦 Installation
|
|
6
17
|
|
|
7
18
|
```bash
|
|
8
|
-
npm install flagmint-sdk
|
|
19
|
+
npm install flagmint-js-sdk
|
|
9
20
|
# or
|
|
10
|
-
yarn add flagmint-sdk
|
|
21
|
+
yarn add flagmint-js-sdk
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## � Quick Start
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { FlagClient } from 'flagmint-sdk';
|
|
28
|
+
|
|
29
|
+
// Create a client instance
|
|
30
|
+
const client = new FlagClient({
|
|
31
|
+
apiKey: 'ff_your_api_key_here',
|
|
32
|
+
context: {
|
|
33
|
+
kind: "multi | user | organisation",
|
|
34
|
+
user: { kind: "user", key: '', email: '' },
|
|
35
|
+
organization: { kind: "organization", key: '', plan: '' }
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Wait for initial connection
|
|
40
|
+
await client.ready();
|
|
41
|
+
|
|
42
|
+
// Get flag values
|
|
43
|
+
const showBanner = client.getFlag('show_banner', false);
|
|
44
|
+
const featureVersion = client.getFlag('feature_version', 'v1');
|
|
45
|
+
|
|
46
|
+
// Update context and re-evaluate
|
|
47
|
+
client.updateContext({ user_id: 'user456' });
|
|
11
48
|
```
|
|
12
49
|
|
|
13
|
-
##
|
|
50
|
+
## FlagClientOptions
|
|
14
51
|
|
|
15
52
|
| Option | Type | Default | Description |
|
|
16
53
|
| --------------------- | ----------------------------------------- | ------------------ | --------------------------------------------------------------------- |
|
|
@@ -73,6 +110,14 @@ const client = new FlagClient({
|
|
|
73
110
|
});
|
|
74
111
|
```
|
|
75
112
|
|
|
113
|
+
### Transport Performance
|
|
114
|
+
|
|
115
|
+
| Mode | Latency | Bandwidth | Best For |
|
|
116
|
+
|------|---------|-----------|----------|
|
|
117
|
+
| WebSocket | Lowest (real-time) | Efficient | Real-time dashboards, live updates |
|
|
118
|
+
| Long-Polling | Medium | Higher | Simpler setup, less overhead on server |
|
|
119
|
+
| Auto | Lowest to Medium | Varies | Most applications (recommended) |
|
|
120
|
+
|
|
76
121
|
## ⚙️ Evaluation Helpers
|
|
77
122
|
|
|
78
123
|
The SDK includes utilities to evaluate flag targeting rules and rollouts:
|
|
@@ -107,30 +152,292 @@ const rolloutValue = evaluateRollout(rolloutConfig, userContext);
|
|
|
107
152
|
|
|
108
153
|
These helpers underpin reliable, deterministic assignment of users to flag variants.
|
|
109
154
|
|
|
110
|
-
|
|
155
|
+
### `isInSegment(context, segment)`
|
|
111
156
|
|
|
112
157
|
```ts
|
|
158
|
+
import { isInSegment } from 'flagmint-sdk/core/evaluation';
|
|
159
|
+
|
|
160
|
+
const inBetaSegment = isInSegment(userContext, betaSegment);
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Evaluates whether a user context matches a segment's criteria.
|
|
164
|
+
|
|
165
|
+
## 📖 Framework Integration Examples
|
|
166
|
+
|
|
167
|
+
### React
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
import { useEffect, useState } from 'react';
|
|
113
171
|
import { FlagClient } from 'flagmint-sdk';
|
|
114
172
|
|
|
115
173
|
const client = new FlagClient({
|
|
116
|
-
apiKey: '
|
|
117
|
-
context: {
|
|
174
|
+
apiKey: 'ff_...',
|
|
175
|
+
context: {
|
|
176
|
+
kind: "multi | user | organisation",
|
|
177
|
+
user: { kind: "user", key: '', email: '' },
|
|
178
|
+
organization: { kind: "organization", key: '', plan: '' }
|
|
179
|
+
}
|
|
118
180
|
});
|
|
119
181
|
|
|
120
|
-
|
|
121
|
-
|
|
182
|
+
export function App() {
|
|
183
|
+
const [flags, setFlags] = useState({});
|
|
184
|
+
|
|
185
|
+
useEffect(() => {
|
|
186
|
+
client.ready().then(() => {
|
|
187
|
+
setFlags({
|
|
188
|
+
showBeta: client.getFlag('show_beta', false),
|
|
189
|
+
theme: client.getFlag('theme', 'light')
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
}, []);
|
|
193
|
+
|
|
194
|
+
return (
|
|
195
|
+
<div className={`theme-${flags.theme}`}>
|
|
196
|
+
{flags.showBeta && <BetaFeature />}
|
|
197
|
+
</div>
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
```
|
|
122
201
|
|
|
123
|
-
|
|
124
|
-
|
|
202
|
+
### Node.js / Express
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
import { FlagClient } from 'flagmint-sdk';
|
|
206
|
+
import * as asyncCache from 'flagmint-sdk/core/cacheHelper.async';
|
|
207
|
+
|
|
208
|
+
const client = new FlagClient({
|
|
209
|
+
apiKey: 'ff_...',
|
|
210
|
+
cacheAdapter: {
|
|
211
|
+
loadFlags: asyncCache.loadCachedFlags,
|
|
212
|
+
saveFlags: asyncCache.saveCachedFlags,
|
|
213
|
+
loadContext: asyncCache.loadCachedContext,
|
|
214
|
+
saveContext: asyncCache.saveCachedContext
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
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);
|
|
221
|
+
|
|
222
|
+
res.json({ enabled: isEnabled });
|
|
223
|
+
});
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## 🔄 Context Management
|
|
227
|
+
|
|
228
|
+
### Updating Context
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
// Initial context
|
|
232
|
+
const client = new FlagClient({
|
|
233
|
+
apiKey: '...',
|
|
234
|
+
context: { user_id: 'user123' }
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// Update context later
|
|
238
|
+
client.updateContext({
|
|
239
|
+
user_id: 'user123',
|
|
240
|
+
plan: 'premium',
|
|
241
|
+
country: 'CA'
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Persisting Context
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
const client = new FlagClient({
|
|
249
|
+
apiKey: '...',
|
|
250
|
+
persistContext: true, // Saves to localStorage/AsyncStorage
|
|
251
|
+
context: { user_id: 'user123' }
|
|
252
|
+
});
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## 🔌 Deferred Initialization
|
|
256
|
+
|
|
257
|
+
For scenarios where you need to set up the client before context is available:
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
const client = new FlagClient({
|
|
261
|
+
apiKey: '...',
|
|
262
|
+
deferInitialization: true
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// Later, when context is ready:
|
|
266
|
+
client.updateContext({ user_id: 'user123' });
|
|
267
|
+
await client.init();
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## 🌍 Advanced Usage
|
|
271
|
+
|
|
272
|
+
### Error Handling
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
const client = new FlagClient({
|
|
276
|
+
apiKey: '...',
|
|
277
|
+
onError: (error) => {
|
|
278
|
+
console.error('Flag client error:', error);
|
|
279
|
+
// Report to monitoring service
|
|
280
|
+
sentry.captureException(error);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Preview Mode
|
|
286
|
+
|
|
287
|
+
For testing or preview environments where you want to bypass remote flag fetching:
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
const client = new FlagClient({
|
|
291
|
+
apiKey: '...',
|
|
292
|
+
previewMode: true,
|
|
293
|
+
rawFlags: {
|
|
294
|
+
new_checkout: true,
|
|
295
|
+
discount_percent: 20,
|
|
296
|
+
ui_variant: 'experimental'
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// All getFlag calls will use rawFlags
|
|
301
|
+
const checkout = client.getFlag('new_checkout', false); // true
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Custom Cache Implementations
|
|
305
|
+
|
|
306
|
+
```ts
|
|
307
|
+
import { FlagClient } from 'flagmint-sdk';
|
|
308
|
+
import redis from 'redis';
|
|
309
|
+
|
|
310
|
+
const redisClient = redis.createClient();
|
|
311
|
+
|
|
312
|
+
const client = new FlagClient({
|
|
313
|
+
apiKey: '...',
|
|
314
|
+
cacheAdapter: {
|
|
315
|
+
loadFlags: async (apiKey) => {
|
|
316
|
+
const cached = await redisClient.get(`flags:${apiKey}`);
|
|
317
|
+
return cached ? JSON.parse(cached) : null;
|
|
318
|
+
},
|
|
319
|
+
saveFlags: async (apiKey, flags) => {
|
|
320
|
+
await redisClient.setex(
|
|
321
|
+
`flags:${apiKey}`,
|
|
322
|
+
600, // 10 min TTL
|
|
323
|
+
JSON.stringify(flags)
|
|
324
|
+
);
|
|
325
|
+
},
|
|
326
|
+
loadContext: async (apiKey) => {
|
|
327
|
+
const cached = await redisClient.get(`context:${apiKey}`);
|
|
328
|
+
return cached ? JSON.parse(cached) : null;
|
|
329
|
+
},
|
|
330
|
+
saveContext: async (apiKey, context) => {
|
|
331
|
+
await redisClient.setex(
|
|
332
|
+
`context:${apiKey}`,
|
|
333
|
+
3600, // 1 hour TTL
|
|
334
|
+
JSON.stringify(context)
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
});
|
|
125
339
|
```
|
|
126
340
|
|
|
341
|
+
## 📋 API Reference
|
|
342
|
+
|
|
343
|
+
### `FlagClient`
|
|
344
|
+
|
|
345
|
+
**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
|
|
357
|
+
|
|
358
|
+
### Cache Adapter Interface
|
|
359
|
+
|
|
360
|
+
```ts
|
|
361
|
+
interface CacheAdapter {
|
|
362
|
+
loadFlags(apiKey: string): Promise<Record<string, any>> | Record<string, any>;
|
|
363
|
+
saveFlags(apiKey: string, flags: Record<string, any>): Promise<void> | void;
|
|
364
|
+
loadContext(apiKey: string): Promise<Record<string, any> | null> | Record<string, any> | null;
|
|
365
|
+
saveContext(apiKey: string, context: Record<string, any>): Promise<void> | void;
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
## 🛠️ Development & Contributing
|
|
370
|
+
|
|
371
|
+
### Build
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
npm run build
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Test
|
|
378
|
+
|
|
379
|
+
```bash
|
|
380
|
+
npm test
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Project Structure
|
|
384
|
+
|
|
385
|
+
```
|
|
386
|
+
sdk/
|
|
387
|
+
├── core/
|
|
388
|
+
│ ├── client.ts # Main FlagClient
|
|
389
|
+
│ ├── types.ts # Type definitions
|
|
390
|
+
│ ├── evaluation/ # Flag evaluation logic
|
|
391
|
+
│ ├── helpers/ # Cache helpers (sync & async)
|
|
392
|
+
│ └── transports/ # Transport implementations
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
## 🐛 Troubleshooting
|
|
396
|
+
|
|
397
|
+
### Client not connecting
|
|
398
|
+
|
|
399
|
+
- Verify your API key is correct
|
|
400
|
+
- Check network connectivity
|
|
401
|
+
- Review browser console for errors
|
|
402
|
+
- Ensure CORS is properly configured if using from browser
|
|
403
|
+
|
|
404
|
+
### Flags not updating
|
|
405
|
+
|
|
406
|
+
- Confirm `enableOfflineCache: false` or cache is working correctly
|
|
407
|
+
- Check transport mode (WebSocket vs long-polling)
|
|
408
|
+
- Verify context is properly set with `updateContext()`
|
|
409
|
+
|
|
410
|
+
### Performance issues
|
|
411
|
+
|
|
412
|
+
- Consider using long-polling instead of WebSocket for high-traffic scenarios
|
|
413
|
+
- Implement proper cache TTLs
|
|
414
|
+
- Monitor transport connection status
|
|
415
|
+
|
|
416
|
+
## 📞 Support & Resources
|
|
417
|
+
|
|
418
|
+
- **Documentation**: See [Flagmint Docs](https://docs.flagmint.io)
|
|
419
|
+
- **Issues**: Report on [GitHub](https://github.com/flagmint/js-sdk/issues)
|
|
420
|
+
- **Email**: support@flagmint.io
|
|
421
|
+
|
|
422
|
+
## 🚀 Changelog
|
|
423
|
+
|
|
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
|
|
430
|
+
|
|
127
431
|
## 📖 References
|
|
128
432
|
|
|
129
|
-
* Core client:
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
433
|
+
* Core client: [sdk/core/client.ts](sdk/core/client.ts)
|
|
434
|
+
* Type definitions: [sdk/core/types.ts](sdk/core/types.ts)
|
|
435
|
+
* Cache helpers: [sdk/core/helpers/cacheHelper.ts](sdk/core/helpers/cacheHelper.ts), [cacheHelper.async.ts](sdk/core/helpers/cacheHelper.async.ts)
|
|
436
|
+
* Transports: [WebsocketTransport](sdk/core/transports/WebsocketTransport.ts), [LongPollingTransport](sdk/core/transports/LongPollingTransport.ts)
|
|
437
|
+
* Evaluation: [evaluateFlagValue](sdk/core/evaluation/evaluateFlagValue.ts), [evaluateRollout](sdk/core/evaluation/evaluateRollout.ts), [rolloutUtils](sdk/core/evaluation/rolloutUtils.ts)
|
|
133
438
|
|
|
134
439
|
---
|
|
135
440
|
|
|
136
|
-
|
|
441
|
+
**License**: MIT © Flagmint Team
|
|
442
|
+
|
|
443
|
+
**Maintained with ❤️ by the Flagmint Team**
|