@flagpool/sdk 0.1.0 → 0.1.1
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 +182 -116
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Flagpool SDK
|
|
1
|
+
# Flagpool SDK for TypeScript / JavaScript
|
|
2
2
|
|
|
3
|
-
Official TypeScript/JavaScript SDK for Flagpool feature
|
|
3
|
+
Official TypeScript/JavaScript SDK for [Flagpool](https://flagpool.io) - the modern feature flag platform for teams who want control without complexity.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@flagpool/sdk)
|
|
6
6
|
[](https://www.npmjs.com/package/@flagpool/sdk)
|
|
@@ -17,7 +17,7 @@ npm install @flagpool/sdk
|
|
|
17
17
|
import { FlagpoolClient } from '@flagpool/sdk';
|
|
18
18
|
|
|
19
19
|
const client = new FlagpoolClient({
|
|
20
|
-
apiKey: 'fp_production_xxx',
|
|
20
|
+
apiKey: 'fp_production_xxx', // Get from Flagpool dashboard
|
|
21
21
|
environment: 'production',
|
|
22
22
|
context: {
|
|
23
23
|
userId: 'user-123',
|
|
@@ -35,10 +35,12 @@ if (client.isEnabled('new-dashboard')) {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
// String flag (A/B test)
|
|
38
|
-
const buttonColor = client.getValue('cta-button-color');
|
|
38
|
+
const buttonColor = client.getValue('cta-button-color');
|
|
39
|
+
// 'blue' | 'green' | 'orange'
|
|
39
40
|
|
|
40
41
|
// Number flag
|
|
41
|
-
const maxUpload = client.getValue('max-upload-size-mb');
|
|
42
|
+
const maxUpload = client.getValue('max-upload-size-mb');
|
|
43
|
+
// 10 | 100 | 1000
|
|
42
44
|
|
|
43
45
|
// JSON flag
|
|
44
46
|
const config = client.getValue('checkout-config');
|
|
@@ -53,45 +55,85 @@ client.close();
|
|
|
53
55
|
- ✅ **Local evaluation** - No server roundtrip per flag check
|
|
54
56
|
- ✅ **Deterministic rollouts** - Same user always gets same variation
|
|
55
57
|
- ✅ **Multiple flag types** - Boolean, string, number, JSON
|
|
56
|
-
- ✅ **
|
|
57
|
-
- ✅ **
|
|
58
|
-
- ✅ **
|
|
59
|
-
- ✅ **
|
|
60
|
-
- ✅ **Zero dependencies** - Core SDK has no external dependencies
|
|
61
|
-
|
|
62
|
-
## API Reference
|
|
63
|
-
|
|
64
|
-
### `FlagpoolClient`
|
|
58
|
+
- ✅ **Advanced targeting** - 8 operators including target lists
|
|
59
|
+
- ✅ **Real-time updates** - Automatic polling for flag changes
|
|
60
|
+
- ✅ **Offline support** - Works with cached flags when offline
|
|
61
|
+
- ✅ **Zero dependencies** - Lightweight, no external dependencies
|
|
65
62
|
|
|
66
|
-
|
|
63
|
+
## Configuration
|
|
67
64
|
|
|
68
65
|
```typescript
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
66
|
+
const client = new FlagpoolClient({
|
|
67
|
+
// Required
|
|
68
|
+
apiKey: 'fp_production_xxx', // Your environment API key
|
|
69
|
+
environment: 'production', // Environment name
|
|
70
|
+
|
|
71
|
+
// Optional
|
|
72
|
+
context: { // User context for targeting
|
|
73
|
+
userId: 'user-123',
|
|
74
|
+
email: 'user@example.com',
|
|
75
|
+
plan: 'pro',
|
|
76
|
+
// Add any attributes for targeting
|
|
77
|
+
},
|
|
78
|
+
pollingInterval: 30000, // Auto-refresh interval (ms), default: 30000
|
|
79
|
+
urlOverride: undefined, // Override API endpoint (for self-hosted)
|
|
80
|
+
});
|
|
79
81
|
```
|
|
80
82
|
|
|
81
|
-
|
|
83
|
+
## API Reference
|
|
84
|
+
|
|
85
|
+
### Methods
|
|
82
86
|
|
|
83
87
|
| Method | Description |
|
|
84
88
|
|--------|-------------|
|
|
85
|
-
| `init()` | Initialize client and fetch flags |
|
|
86
|
-
| `isEnabled(key)` | Check if boolean flag is enabled |
|
|
89
|
+
| `init()` | Initialize client and fetch flags (required before evaluation) |
|
|
90
|
+
| `isEnabled(key)` | Check if a boolean flag is enabled |
|
|
87
91
|
| `getValue(key)` | Get flag value (any type) |
|
|
88
92
|
| `getVariation(key)` | Alias for getValue |
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
92
|
-
|
|
93
|
+
| `updateContext(ctx)` | Update user context and re-evaluate flags |
|
|
94
|
+
| `onChange(callback)` | Subscribe to flag value changes |
|
|
95
|
+
| `close()` | Clean up resources (stop polling) |
|
|
96
|
+
|
|
97
|
+
### Flag Types
|
|
98
|
+
|
|
99
|
+
#### Boolean Flags
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
if (client.isEnabled('feature-flag')) {
|
|
103
|
+
// Feature is enabled for this user
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
#### String Flags
|
|
108
|
+
|
|
109
|
+
Perfect for A/B tests and feature variants:
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
const variant = client.getValue('button-color');
|
|
113
|
+
// Returns: 'blue' | 'green' | 'orange'
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
#### Number Flags
|
|
93
117
|
|
|
94
|
-
|
|
118
|
+
Great for limits, thresholds, and configurations:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
const limit = client.getValue('rate-limit');
|
|
122
|
+
// Returns: 100 | 1000 | 10000
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
#### JSON Flags
|
|
126
|
+
|
|
127
|
+
For complex configurations:
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
const config = client.getValue('checkout-config');
|
|
131
|
+
// Returns: { showCoupons: true, maxItems: 50, ... }
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Targeting Rules
|
|
135
|
+
|
|
136
|
+
Flagpool supports powerful targeting with 8 operators:
|
|
95
137
|
|
|
96
138
|
| Operator | Description | Example |
|
|
97
139
|
|----------|-------------|---------|
|
|
@@ -106,8 +148,9 @@ interface FlagpoolClientOptions {
|
|
|
106
148
|
|
|
107
149
|
## Dynamic Context Updates
|
|
108
150
|
|
|
151
|
+
Update user context on the fly - flags re-evaluate automatically:
|
|
152
|
+
|
|
109
153
|
```typescript
|
|
110
|
-
// Initial context
|
|
111
154
|
const client = new FlagpoolClient({
|
|
112
155
|
apiKey: 'fp_prod_xxx',
|
|
113
156
|
environment: 'production',
|
|
@@ -116,143 +159,166 @@ const client = new FlagpoolClient({
|
|
|
116
159
|
|
|
117
160
|
await client.init();
|
|
118
161
|
|
|
162
|
+
// User on free plan
|
|
119
163
|
console.log(client.getValue('max-upload-size-mb')); // 10
|
|
120
164
|
|
|
121
165
|
// User upgrades to pro
|
|
122
166
|
client.updateContext({ plan: 'pro' });
|
|
123
167
|
|
|
168
|
+
// Instantly gets pro limits
|
|
124
169
|
console.log(client.getValue('max-upload-size-mb')); // 100
|
|
125
170
|
```
|
|
126
171
|
|
|
127
|
-
##
|
|
128
|
-
|
|
129
|
-
For offline/edge scenarios, you can decrypt and evaluate target lists client-side.
|
|
130
|
-
|
|
131
|
-
> **Note:** The SDK has NO crypto dependencies. You provide the adapter.
|
|
172
|
+
## Real-time Updates
|
|
132
173
|
|
|
133
|
-
|
|
174
|
+
Flags automatically refresh in the background:
|
|
134
175
|
|
|
135
176
|
```typescript
|
|
136
|
-
|
|
137
|
-
|
|
177
|
+
const client = new FlagpoolClient({
|
|
178
|
+
apiKey: 'fp_prod_xxx',
|
|
179
|
+
environment: 'production',
|
|
180
|
+
context: { userId: 'user-1' },
|
|
181
|
+
pollingInterval: 30000 // Refresh every 30 seconds
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
await client.init();
|
|
138
185
|
|
|
139
|
-
|
|
140
|
-
|
|
186
|
+
// Listen for flag changes
|
|
187
|
+
client.onChange((flagKey, newValue) => {
|
|
188
|
+
console.log(`Flag ${flagKey} changed to:`, newValue);
|
|
141
189
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
};
|
|
148
|
-
}
|
|
190
|
+
// React to changes (e.g., update UI)
|
|
191
|
+
if (flagKey === 'maintenance-mode' && newValue === true) {
|
|
192
|
+
showMaintenanceBanner();
|
|
193
|
+
}
|
|
194
|
+
});
|
|
149
195
|
```
|
|
150
196
|
|
|
151
|
-
|
|
197
|
+
## Offline Support
|
|
152
198
|
|
|
153
|
-
|
|
154
|
-
import { FlagpoolClient, setCryptoAdapter } from 'flagpool-sdk';
|
|
155
|
-
import { createNodeCryptoAdapter } from './crypto-adapter';
|
|
156
|
-
|
|
157
|
-
// 1. Create and register your adapter
|
|
158
|
-
const adapter = await createNodeCryptoAdapter();
|
|
159
|
-
setCryptoAdapter(adapter);
|
|
199
|
+
The SDK caches flags locally. If the network is unavailable, it uses cached values:
|
|
160
200
|
|
|
161
|
-
|
|
201
|
+
```typescript
|
|
162
202
|
const client = new FlagpoolClient({
|
|
163
|
-
apiKey: '
|
|
164
|
-
environment: '
|
|
165
|
-
context: { userId: '
|
|
166
|
-
decryptionKey: 'your-secret-key',
|
|
167
|
-
clientSideTargetLists: true,
|
|
203
|
+
apiKey: 'fp_prod_xxx',
|
|
204
|
+
environment: 'production',
|
|
205
|
+
context: { userId: 'user-1' }
|
|
168
206
|
});
|
|
169
207
|
|
|
208
|
+
// Works even if network fails (uses cache from last successful fetch)
|
|
170
209
|
await client.init();
|
|
171
210
|
|
|
172
|
-
//
|
|
173
|
-
client.isEnabled('
|
|
211
|
+
// Always returns a value (from cache if offline)
|
|
212
|
+
const feature = client.isEnabled('my-feature');
|
|
174
213
|
```
|
|
175
214
|
|
|
176
|
-
##
|
|
215
|
+
## Framework Examples
|
|
216
|
+
|
|
217
|
+
### React
|
|
177
218
|
|
|
178
219
|
```typescript
|
|
220
|
+
import { useEffect, useState } from 'react';
|
|
221
|
+
import { FlagpoolClient } from '@flagpool/sdk';
|
|
222
|
+
|
|
179
223
|
const client = new FlagpoolClient({
|
|
180
224
|
apiKey: 'fp_prod_xxx',
|
|
181
225
|
environment: 'production',
|
|
182
|
-
context: { userId: 'user-1' },
|
|
183
|
-
streaming: true,
|
|
184
|
-
pollingInterval: 30000 // Poll every 30 seconds
|
|
185
226
|
});
|
|
186
227
|
|
|
187
|
-
|
|
228
|
+
export function useFeatureFlag(key: string) {
|
|
229
|
+
const [value, setValue] = useState<any>(null);
|
|
188
230
|
|
|
189
|
-
|
|
190
|
-
client.
|
|
191
|
-
|
|
192
|
-
});
|
|
193
|
-
```
|
|
231
|
+
useEffect(() => {
|
|
232
|
+
client.init().then(() => {
|
|
233
|
+
setValue(client.getValue(key));
|
|
234
|
+
});
|
|
194
235
|
|
|
195
|
-
|
|
236
|
+
const unsubscribe = client.onChange((changedKey, newValue) => {
|
|
237
|
+
if (changedKey === key) setValue(newValue);
|
|
238
|
+
});
|
|
196
239
|
|
|
197
|
-
|
|
240
|
+
return () => unsubscribe?.();
|
|
241
|
+
}, [key]);
|
|
198
242
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
npm install
|
|
202
|
-
|
|
203
|
-
# Start mock server (uses shared server from test-harness)
|
|
204
|
-
npm run server
|
|
243
|
+
return value;
|
|
244
|
+
}
|
|
205
245
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
246
|
+
// Usage
|
|
247
|
+
function MyComponent() {
|
|
248
|
+
const showNewFeature = useFeatureFlag('new-feature');
|
|
249
|
+
|
|
250
|
+
if (showNewFeature) {
|
|
251
|
+
return <NewFeature />;
|
|
252
|
+
}
|
|
253
|
+
return <OldFeature />;
|
|
254
|
+
}
|
|
211
255
|
```
|
|
212
256
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
### Boolean Flags
|
|
257
|
+
### Next.js
|
|
216
258
|
|
|
217
259
|
```typescript
|
|
218
|
-
|
|
219
|
-
|
|
260
|
+
// lib/flagpool.ts
|
|
261
|
+
import { FlagpoolClient } from '@flagpool/sdk';
|
|
262
|
+
|
|
263
|
+
let client: FlagpoolClient | null = null;
|
|
264
|
+
|
|
265
|
+
export async function getFlags(userId: string) {
|
|
266
|
+
if (!client) {
|
|
267
|
+
client = new FlagpoolClient({
|
|
268
|
+
apiKey: process.env.FLAGPOOL_API_KEY!,
|
|
269
|
+
environment: process.env.NODE_ENV,
|
|
270
|
+
context: { userId }
|
|
271
|
+
});
|
|
272
|
+
await client.init();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
newDashboard: client.isEnabled('new-dashboard'),
|
|
277
|
+
buttonColor: client.getValue('button-color'),
|
|
278
|
+
};
|
|
220
279
|
}
|
|
221
280
|
```
|
|
222
281
|
|
|
223
|
-
###
|
|
282
|
+
### Node.js / Express
|
|
224
283
|
|
|
225
284
|
```typescript
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
```
|
|
285
|
+
import express from 'express';
|
|
286
|
+
import { FlagpoolClient } from '@flagpool/sdk';
|
|
229
287
|
|
|
230
|
-
|
|
288
|
+
const app = express();
|
|
231
289
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
290
|
+
const flagpool = new FlagpoolClient({
|
|
291
|
+
apiKey: process.env.FLAGPOOL_API_KEY!,
|
|
292
|
+
environment: 'production',
|
|
293
|
+
});
|
|
236
294
|
|
|
237
|
-
|
|
295
|
+
// Initialize on startup
|
|
296
|
+
await flagpool.init();
|
|
238
297
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
298
|
+
app.get('/api/data', (req, res) => {
|
|
299
|
+
// Update context per-request
|
|
300
|
+
flagpool.updateContext({ userId: req.user.id });
|
|
301
|
+
|
|
302
|
+
if (flagpool.isEnabled('new-api-response')) {
|
|
303
|
+
return res.json({ version: 'v2', data: newData });
|
|
304
|
+
}
|
|
305
|
+
return res.json({ version: 'v1', data: legacyData });
|
|
306
|
+
});
|
|
242
307
|
```
|
|
243
308
|
|
|
244
|
-
##
|
|
309
|
+
## Documentation
|
|
245
310
|
|
|
246
|
-
|
|
311
|
+
For complete documentation, guides, and best practices, visit:
|
|
247
312
|
|
|
248
|
-
|
|
313
|
+
📚 **[flagpool.io/docs](https://flagpool.io/docs)**
|
|
249
314
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
315
|
+
## Support
|
|
316
|
+
|
|
317
|
+
- 📖 [Documentation](https://flagpool.io/docs)
|
|
318
|
+
- 🐛 [Report Issues](https://github.com/flagpool/flagpool-sdk/issues)
|
|
319
|
+
- ✉️ [Email Support](mailto:support@flagpool.io)
|
|
254
320
|
|
|
255
321
|
## License
|
|
256
322
|
|
|
257
|
-
MIT
|
|
323
|
+
MIT © [Flagpool](https://flagpool.io)
|
|
258
324
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flagpool/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Official Flagpool SDK for TypeScript/JavaScript - feature flags with local evaluation, deterministic rollouts, and encrypted target lists",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"url": "git+https://github.com/flagpool/flagpool-sdk.git",
|
|
26
26
|
"directory": "sdks/typescript"
|
|
27
27
|
},
|
|
28
|
-
"homepage": "https://
|
|
28
|
+
"homepage": "https://flagpool.io/docs/sdks/typescript",
|
|
29
29
|
"bugs": {
|
|
30
30
|
"url": "https://github.com/flagpool/flagpool-sdk/issues"
|
|
31
31
|
},
|