@fluxcontrolsdk/js-sdk 0.2.0 → 0.2.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 +141 -0
- package/dist/client.d.ts +23 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/index.esm.js +120 -15
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +119 -14
- package/dist/index.js.map +1 -1
- package/dist/react.d.ts.map +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -10,6 +10,62 @@ npm install @fluxcontrolsdk/js-sdk
|
|
|
10
10
|
yarn add @fluxcontrolsdk/js-sdk
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
+
## ⚙️ Configuration
|
|
14
|
+
|
|
15
|
+
> **IMPORTANT**: The `apiUrl` must end with `/api/v1/sdk`
|
|
16
|
+
|
|
17
|
+
Before using the SDK, ensure you have:
|
|
18
|
+
|
|
19
|
+
1. **SDK Key**: Your unique SDK key from the FluxControl admin console
|
|
20
|
+
2. **API URL**: The complete FluxControl API endpoint
|
|
21
|
+
|
|
22
|
+
### Correct API URL Format
|
|
23
|
+
|
|
24
|
+
✅ **Correct**:
|
|
25
|
+
```typescript
|
|
26
|
+
apiUrl: 'https://api.fluxcontrol.io/api/v1/sdk'
|
|
27
|
+
apiUrl: 'http://localhost:8000/api/v1/sdk'
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
❌ **Incorrect**:
|
|
31
|
+
```typescript
|
|
32
|
+
apiUrl: 'https://api.fluxcontrol.io' // Missing /api/v1/sdk
|
|
33
|
+
apiUrl: 'https://api.fluxcontrol.io/api' // Incomplete path
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Common Mistakes
|
|
37
|
+
|
|
38
|
+
**1. Confusing `sdkKey` with `apiKey`**
|
|
39
|
+
|
|
40
|
+
❌ **Wrong**:
|
|
41
|
+
```tsx
|
|
42
|
+
<FluxProvider config={{ apiKey: 'your-key' }}> // apiKey is wrong
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
✅ **Correct**:
|
|
46
|
+
```tsx
|
|
47
|
+
<FluxProvider sdkKey="your-key"> // sdkKey is the prop name
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**2. Passing SDK key in config instead of as prop**
|
|
51
|
+
|
|
52
|
+
❌ **Wrong**:
|
|
53
|
+
```tsx
|
|
54
|
+
<FluxProvider config={{ sdkKey: 'your-key', apiUrl: '...' }}>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
✅ **Correct**:
|
|
58
|
+
```tsx
|
|
59
|
+
<FluxProvider
|
|
60
|
+
sdkKey="your-key" // SDK key as prop
|
|
61
|
+
config={{ apiUrl: '...' }} // Config for other options
|
|
62
|
+
>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**3. Missing or incomplete API URL**
|
|
66
|
+
|
|
67
|
+
The SDK will warn you if the URL doesn't end with `/api/v1/sdk`.
|
|
68
|
+
|
|
13
69
|
## Quick Start
|
|
14
70
|
|
|
15
71
|
### Vanilla JavaScript/TypeScript
|
|
@@ -88,6 +144,8 @@ function MyComponent() {
|
|
|
88
144
|
}
|
|
89
145
|
```
|
|
90
146
|
|
|
147
|
+
> **Note**: The `useFeature` hook automatically updates when flags change. No manual polling or state management needed!
|
|
148
|
+
|
|
91
149
|
## API Reference
|
|
92
150
|
|
|
93
151
|
### FluxClient
|
|
@@ -250,6 +308,88 @@ function Experiment() {
|
|
|
250
308
|
}
|
|
251
309
|
```
|
|
252
310
|
|
|
311
|
+
## 🔧 Troubleshooting
|
|
312
|
+
|
|
313
|
+
### Common Issues
|
|
314
|
+
|
|
315
|
+
#### 401 Unauthorized Error
|
|
316
|
+
|
|
317
|
+
**Symptom**: Console shows "401 Unauthorized - Check your SDK Key"
|
|
318
|
+
|
|
319
|
+
**Solutions**:
|
|
320
|
+
1. Verify your SDK key is correct in the FluxControl admin console
|
|
321
|
+
2. Check that you're passing `sdkKey` as a prop, not in config
|
|
322
|
+
3. Ensure the SDK key hasn't expired or been revoked
|
|
323
|
+
|
|
324
|
+
#### 404 Not Found Error
|
|
325
|
+
|
|
326
|
+
**Symptom**: Console shows "404 Not Found - Verify API URL ends with '/api/v1/sdk'"
|
|
327
|
+
|
|
328
|
+
**Solutions**:
|
|
329
|
+
1. Check that `apiUrl` ends with `/api/v1/sdk`
|
|
330
|
+
2. Verify the FluxControl API is accessible at that URL
|
|
331
|
+
3. Check for typos in the URL
|
|
332
|
+
|
|
333
|
+
#### Flags Not Updating
|
|
334
|
+
|
|
335
|
+
**Symptom**: Flag values don't change when updated in admin console
|
|
336
|
+
|
|
337
|
+
**Solutions**:
|
|
338
|
+
1. Check that `pollingIntervalMs` is set (default: 60000ms = 1 minute)
|
|
339
|
+
2. Wait for the next poll cycle
|
|
340
|
+
3. For React hooks, ensure you're using SDK version 0.2.0+ (automatic reactivity)
|
|
341
|
+
4. Check browser console for fetch errors
|
|
342
|
+
|
|
343
|
+
#### React Hooks Not Reactive
|
|
344
|
+
|
|
345
|
+
**Symptom**: `useFeature` doesn't update when flags change
|
|
346
|
+
|
|
347
|
+
**Solutions**:
|
|
348
|
+
1. Ensure you're using SDK version 0.2.0 or later
|
|
349
|
+
2. Verify `FluxProvider` is wrapping your components
|
|
350
|
+
3. Check that the client is initializing successfully (no console errors)
|
|
351
|
+
4. The hook automatically subscribes to SDK events - no manual polling needed
|
|
352
|
+
|
|
353
|
+
### Debugging
|
|
354
|
+
|
|
355
|
+
#### Check SDK Ready State
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
const client = new FluxClient('your-key', { apiUrl: '...' });
|
|
359
|
+
await client.init();
|
|
360
|
+
|
|
361
|
+
if (client.isReady) {
|
|
362
|
+
console.log('SDK is ready!');
|
|
363
|
+
} else {
|
|
364
|
+
console.log('SDK not ready - check console for errors');
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
#### Subscribe to SDK Events
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
client.on('ready', (ruleset) => {
|
|
372
|
+
console.log('SDK ready, ruleset loaded:', ruleset);
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
client.on('update', (ruleset) => {
|
|
376
|
+
console.log('Ruleset updated:', ruleset);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
client.on('error', (error) => {
|
|
380
|
+
console.error('SDK error:', error);
|
|
381
|
+
});
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
#### Inspect Current Ruleset (Development Only)
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
// In browser console
|
|
388
|
+
const rulesetJson = localStorage.getItem('flux_ruleset_your-sdk-key');
|
|
389
|
+
const ruleset = JSON.parse(rulesetJson);
|
|
390
|
+
console.log(ruleset);
|
|
391
|
+
```
|
|
392
|
+
|
|
253
393
|
## Browser Support
|
|
254
394
|
|
|
255
395
|
- Chrome/Edge: Latest 2 versions
|
|
@@ -258,6 +398,7 @@ function Experiment() {
|
|
|
258
398
|
- iOS Safari: iOS 12+
|
|
259
399
|
- Android Chrome: Latest 2 versions
|
|
260
400
|
|
|
401
|
+
|
|
261
402
|
## License
|
|
262
403
|
|
|
263
404
|
Internal use only.
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Context, Config, EvaluationResult, Ruleset } from './types';
|
|
1
|
+
import { Context, Config, EvaluationResult, Ruleset, EventType, EventCallback } from './types';
|
|
2
2
|
export { Context, Config, EvaluationResult, Ruleset };
|
|
3
3
|
/**
|
|
4
4
|
* FluxControl JavaScript SDK (Thick Client)
|
|
@@ -11,7 +11,29 @@ export declare class FluxClient {
|
|
|
11
11
|
private evaluator;
|
|
12
12
|
private ruleset;
|
|
13
13
|
private pollingInterval;
|
|
14
|
+
private listeners;
|
|
15
|
+
private _isReady;
|
|
14
16
|
constructor(sdkKey: string, config: Config);
|
|
17
|
+
/**
|
|
18
|
+
* Check if the SDK is ready (ruleset loaded).
|
|
19
|
+
*/
|
|
20
|
+
get isReady(): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Subscribe to SDK events.
|
|
23
|
+
* @param event - Event type ('ready', 'update', 'error')
|
|
24
|
+
* @param callback - Function to call when event fires
|
|
25
|
+
*/
|
|
26
|
+
on(event: EventType, callback: EventCallback): void;
|
|
27
|
+
/**
|
|
28
|
+
* Unsubscribe from SDK events.
|
|
29
|
+
* @param event - Event type
|
|
30
|
+
* @param callback - Function to remove
|
|
31
|
+
*/
|
|
32
|
+
off(event: EventType, callback: EventCallback): void;
|
|
33
|
+
/**
|
|
34
|
+
* Emit an event to all listeners.
|
|
35
|
+
*/
|
|
36
|
+
private emit;
|
|
15
37
|
/**
|
|
16
38
|
* Initialize the SDK (fetch ruleset).
|
|
17
39
|
*/
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC/F,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC;AAEtD;;;;GAIG;AACH,qBAAa,UAAU;IACnB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,eAAe,CAAM;IAC7B,OAAO,CAAC,SAAS,CAAiD;IAClE,OAAO,CAAC,QAAQ,CAAkB;gBAEtB,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IA2B1C;;OAEG;IACH,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;;;OAIG;IACH,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,GAAG,IAAI;IAOnD;;;;OAIG;IACH,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,GAAG,IAAI;IAOpD;;OAEG;IACH,OAAO,CAAC,IAAI;IAaZ;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ3B;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAuEnC;;;;;;OAMG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,GAAG,OAAO;IAKhF,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM;IAKhF,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM;IAKhF,aAAa,CAAC,CAAC,GAAG,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,GAAG,CAAC;IAI7E,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,GAAG,MAAM,EAAE,YAAY,GAAE,OAAe,GAAG,OAAO;IAMjG;;OAEG;IACH,OAAO,CAAC,QAAQ;IAahB;;OAEG;IACG,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBjH,KAAK;CAKR"}
|
package/dist/index.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { createContext,
|
|
1
|
+
import React, { createContext, useEffect, useState, useContext } from 'react';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Evaluates feature flags against targeting rules.
|
|
@@ -206,9 +206,18 @@ class Evaluator {
|
|
|
206
206
|
class FluxClient {
|
|
207
207
|
constructor(sdkKey, config) {
|
|
208
208
|
this.ruleset = null;
|
|
209
|
+
this.listeners = new Map();
|
|
210
|
+
this._isReady = false;
|
|
209
211
|
if (!config.apiUrl) {
|
|
210
212
|
throw new Error('[FluxClient] apiUrl is required. Please provide the FluxControl API endpoint (e.g., "https://api.fluxcontrol.io/api/v1/sdk")');
|
|
211
213
|
}
|
|
214
|
+
// Validation warnings (non-breaking)
|
|
215
|
+
if (!sdkKey || sdkKey.trim() === '') {
|
|
216
|
+
console.warn('[FluxClient] SDK key appears to be missing or invalid. API requests will fail.');
|
|
217
|
+
}
|
|
218
|
+
if (!config.apiUrl.endsWith('/api/v1/sdk')) {
|
|
219
|
+
console.warn(`[FluxClient] API URL should end with '/api/v1/sdk'. Current: ${config.apiUrl}`);
|
|
220
|
+
}
|
|
212
221
|
this.sdkKey = sdkKey;
|
|
213
222
|
this.config = {
|
|
214
223
|
environment: 'production',
|
|
@@ -221,6 +230,50 @@ class FluxClient {
|
|
|
221
230
|
this.init();
|
|
222
231
|
}
|
|
223
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* Check if the SDK is ready (ruleset loaded).
|
|
235
|
+
*/
|
|
236
|
+
get isReady() {
|
|
237
|
+
return this._isReady;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Subscribe to SDK events.
|
|
241
|
+
* @param event - Event type ('ready', 'update', 'error')
|
|
242
|
+
* @param callback - Function to call when event fires
|
|
243
|
+
*/
|
|
244
|
+
on(event, callback) {
|
|
245
|
+
if (!this.listeners.has(event)) {
|
|
246
|
+
this.listeners.set(event, new Set());
|
|
247
|
+
}
|
|
248
|
+
this.listeners.get(event).add(callback);
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Unsubscribe from SDK events.
|
|
252
|
+
* @param event - Event type
|
|
253
|
+
* @param callback - Function to remove
|
|
254
|
+
*/
|
|
255
|
+
off(event, callback) {
|
|
256
|
+
const callbacks = this.listeners.get(event);
|
|
257
|
+
if (callbacks) {
|
|
258
|
+
callbacks.delete(callback);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Emit an event to all listeners.
|
|
263
|
+
*/
|
|
264
|
+
emit(event, data) {
|
|
265
|
+
const callbacks = this.listeners.get(event);
|
|
266
|
+
if (callbacks) {
|
|
267
|
+
callbacks.forEach(callback => {
|
|
268
|
+
try {
|
|
269
|
+
callback(data);
|
|
270
|
+
}
|
|
271
|
+
catch (e) {
|
|
272
|
+
console.error(`[FluxClient] Error in ${event} event listener:`, e);
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
224
277
|
/**
|
|
225
278
|
* Initialize the SDK (fetch ruleset).
|
|
226
279
|
*/
|
|
@@ -234,6 +287,7 @@ class FluxClient {
|
|
|
234
287
|
* Fetch ruleset from API.
|
|
235
288
|
*/
|
|
236
289
|
async fetchRuleset() {
|
|
290
|
+
const isInitialFetch = !this._isReady;
|
|
237
291
|
try {
|
|
238
292
|
const environment = this.config.environment || 'production';
|
|
239
293
|
const url = `${this.config.apiUrl}/ruleset?environment=${environment}`;
|
|
@@ -244,22 +298,56 @@ class FluxClient {
|
|
|
244
298
|
});
|
|
245
299
|
if (res.ok) {
|
|
246
300
|
this.ruleset = await res.json();
|
|
301
|
+
// Mark as ready and emit appropriate event
|
|
302
|
+
if (isInitialFetch) {
|
|
303
|
+
this._isReady = true;
|
|
304
|
+
this.emit('ready', this.ruleset);
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
this.emit('update', this.ruleset);
|
|
308
|
+
}
|
|
247
309
|
// Optional: Save to localStorage for offline boot
|
|
248
310
|
if (typeof window !== 'undefined') {
|
|
249
311
|
localStorage.setItem(`flux_ruleset_${this.sdkKey}`, JSON.stringify(this.ruleset));
|
|
250
312
|
}
|
|
251
313
|
}
|
|
252
314
|
else {
|
|
253
|
-
|
|
315
|
+
// Enhanced error messages with actionable guidance
|
|
316
|
+
let errorMessage = `[FluxClient] Failed to fetch ruleset: ${res.status} ${res.statusText}`;
|
|
317
|
+
switch (res.status) {
|
|
318
|
+
case 401:
|
|
319
|
+
errorMessage += ' - Unauthorized: Check your SDK Key';
|
|
320
|
+
break;
|
|
321
|
+
case 403:
|
|
322
|
+
errorMessage += ' - Forbidden: Check SDK key permissions';
|
|
323
|
+
break;
|
|
324
|
+
case 404:
|
|
325
|
+
errorMessage += ` - Not Found: Verify API URL ends with '/api/v1/sdk'. Current: ${this.config.apiUrl}`;
|
|
326
|
+
break;
|
|
327
|
+
case 500:
|
|
328
|
+
case 502:
|
|
329
|
+
case 503:
|
|
330
|
+
errorMessage += ' - Server Error: FluxControl API may be down';
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
console.warn(errorMessage);
|
|
334
|
+
this.emit('error', { status: res.status, message: errorMessage });
|
|
254
335
|
}
|
|
255
336
|
}
|
|
256
337
|
catch (e) {
|
|
257
|
-
|
|
338
|
+
const errorMessage = `[FluxClient] Error fetching ruleset from ${this.config.apiUrl}`;
|
|
339
|
+
console.error(errorMessage, e);
|
|
340
|
+
this.emit('error', { error: e, message: errorMessage });
|
|
258
341
|
// Try load from local storage
|
|
259
342
|
if (typeof window !== 'undefined' && !this.ruleset) {
|
|
260
343
|
const stored = localStorage.getItem(`flux_ruleset_${this.sdkKey}`);
|
|
261
|
-
if (stored)
|
|
344
|
+
if (stored) {
|
|
262
345
|
this.ruleset = JSON.parse(stored);
|
|
346
|
+
if (isInitialFetch) {
|
|
347
|
+
this._isReady = true;
|
|
348
|
+
this.emit('ready', this.ruleset);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
263
351
|
}
|
|
264
352
|
}
|
|
265
353
|
}
|
|
@@ -332,6 +420,8 @@ class FluxClient {
|
|
|
332
420
|
close() {
|
|
333
421
|
if (this.pollingInterval)
|
|
334
422
|
clearInterval(this.pollingInterval);
|
|
423
|
+
// Clear all event listeners
|
|
424
|
+
this.listeners.clear();
|
|
335
425
|
}
|
|
336
426
|
}
|
|
337
427
|
|
|
@@ -355,6 +445,15 @@ const FluxReactContext = createContext({
|
|
|
355
445
|
* </FluxProvider>
|
|
356
446
|
*/
|
|
357
447
|
function FluxProvider({ sdkKey, config, context = {}, children }) {
|
|
448
|
+
// Validation warnings (non-breaking)
|
|
449
|
+
useEffect(() => {
|
|
450
|
+
if (!sdkKey || sdkKey.trim() === '') {
|
|
451
|
+
console.warn("[FluxProvider] 'sdkKey' prop is required. Did you mean to use 'sdkKey' instead of 'apiKey'?");
|
|
452
|
+
}
|
|
453
|
+
if (config && 'apiKey' in config) {
|
|
454
|
+
console.warn("[FluxProvider] Found 'apiKey' in config. The SDK key should be passed as the 'sdkKey' prop, not in config.");
|
|
455
|
+
}
|
|
456
|
+
}, [sdkKey, config]);
|
|
358
457
|
const [client] = useState(() => new FluxClient(sdkKey, config));
|
|
359
458
|
useEffect(() => {
|
|
360
459
|
return () => {
|
|
@@ -375,29 +474,27 @@ function FluxProvider({ sdkKey, config, context = {}, children }) {
|
|
|
375
474
|
function useFeature(flagKey, defaultValue, contextOverride) {
|
|
376
475
|
const { client, context: providerContext } = useContext(FluxReactContext);
|
|
377
476
|
const [value, setValue] = useState(defaultValue);
|
|
378
|
-
const [loading, setLoading] = useState(true);
|
|
379
477
|
useEffect(() => {
|
|
380
478
|
if (!client) {
|
|
381
479
|
setValue(defaultValue);
|
|
382
|
-
setLoading(false);
|
|
383
480
|
return;
|
|
384
481
|
}
|
|
385
482
|
const context = { ...providerContext, ...contextOverride };
|
|
386
|
-
const evaluate =
|
|
483
|
+
const evaluate = () => {
|
|
387
484
|
try {
|
|
388
485
|
let result;
|
|
389
|
-
// Determine type and call appropriate method
|
|
486
|
+
// Determine type and call appropriate method (synchronous)
|
|
390
487
|
if (typeof defaultValue === 'boolean') {
|
|
391
|
-
result =
|
|
488
|
+
result = client.boolVariation(flagKey, context, defaultValue);
|
|
392
489
|
}
|
|
393
490
|
else if (typeof defaultValue === 'number') {
|
|
394
|
-
result =
|
|
491
|
+
result = client.numberVariation(flagKey, context, defaultValue);
|
|
395
492
|
}
|
|
396
493
|
else if (typeof defaultValue === 'string') {
|
|
397
|
-
result =
|
|
494
|
+
result = client.stringVariation(flagKey, context, defaultValue);
|
|
398
495
|
}
|
|
399
496
|
else {
|
|
400
|
-
result =
|
|
497
|
+
result = client.jsonVariation(flagKey, context, defaultValue);
|
|
401
498
|
}
|
|
402
499
|
setValue(result);
|
|
403
500
|
}
|
|
@@ -405,11 +502,19 @@ function useFeature(flagKey, defaultValue, contextOverride) {
|
|
|
405
502
|
console.error(`FluxControl: Error evaluating ${flagKey}:`, error);
|
|
406
503
|
setValue(defaultValue);
|
|
407
504
|
}
|
|
408
|
-
finally {
|
|
409
|
-
setLoading(false);
|
|
410
|
-
}
|
|
411
505
|
};
|
|
506
|
+
// Initial evaluation
|
|
412
507
|
evaluate();
|
|
508
|
+
// Subscribe to SDK events for automatic updates
|
|
509
|
+
const onReady = () => evaluate();
|
|
510
|
+
const onUpdate = () => evaluate();
|
|
511
|
+
client.on('ready', onReady);
|
|
512
|
+
client.on('update', onUpdate);
|
|
513
|
+
// Cleanup event listeners
|
|
514
|
+
return () => {
|
|
515
|
+
client.off('ready', onReady);
|
|
516
|
+
client.off('update', onUpdate);
|
|
517
|
+
};
|
|
413
518
|
}, [client, flagKey, defaultValue, providerContext, contextOverride]);
|
|
414
519
|
return value;
|
|
415
520
|
}
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../src/evaluator.ts","../src/client.ts","../src/react.tsx","../src/index.ts"],"sourcesContent":["// import { createHash } from 'crypto';\nimport {\n Context,\n Ruleset,\n FlagConfig,\n TargetingRule,\n Condition,\n EvaluationResult,\n Variation,\n PercentageRollout,\n} from './types';\n\n/**\n * Evaluates feature flags against targeting rules.\n */\nexport class Evaluator {\n /**\n * Evaluate a flag.\n */\n evaluate(\n flagKey: string,\n context: Context,\n ruleset: Ruleset,\n defaultValue: any\n ): EvaluationResult {\n const flag = ruleset.flags[flagKey];\n\n if (!flag) {\n return {\n value: defaultValue,\n variationKey: undefined,\n reason: 'flag_not_found',\n };\n }\n\n // Evaluate targeting rules\n if (flag.rules) {\n // Sort by priority (lower numbers = higher priority, evaluated first)\n const sortedRules = [...flag.rules].sort((a: any, b: any) => {\n const priorityA = a.priority ?? 999;\n const priorityB = b.priority ?? 999;\n return priorityA - priorityB;\n });\n\n for (const rule of sortedRules) {\n if (this.evaluateConditions(rule.conditions, context, ruleset)) {\n // Rule matched\n if (rule.variationId) {\n return this.serveVariation(flag, rule.variationId, 'rule_match');\n } else if (rule.rollout) {\n // Handle boolean flag rollout format: {on: percentage}\n return this.serveBooleanRollout(\n flag,\n rule.rollout,\n context,\n 'percentage_rollout'\n );\n }\n }\n }\n }\n\n // No rules matched, return default variation\n return this.serveVariation(flag, flag.defaultVariationId, 'default');\n }\n\n /**\n * Evaluate all conditions in a rule.\n */\n private evaluateConditions(\n conditions: Condition[],\n context: Context,\n ruleset: Ruleset\n ): boolean {\n if (!conditions || conditions.length === 0) {\n return true;\n }\n\n return conditions.every((condition) =>\n this.evaluateCondition(condition, context, ruleset)\n );\n }\n\n /**\n * Evaluate a single condition.\n */\n private evaluateCondition(\n condition: Condition,\n context: Context,\n ruleset: Ruleset\n ): boolean {\n // Get attribute value from context\n let contextValue: any;\n if (condition.attribute === 'userId' || condition.attribute === 'user_id') {\n contextValue = context.userId;\n } else if (condition.attribute === 'sessionId') {\n contextValue = context.sessionId;\n } else {\n contextValue = context.attributes?.[condition.attribute];\n }\n\n switch (condition.operator) {\n case 'IS':\n return contextValue === condition.value;\n case 'IS_NOT':\n return contextValue !== condition.value;\n case 'CONTAINS':\n return String(contextValue).includes(String(condition.value));\n case 'DOES_NOT_CONTAIN':\n return !String(contextValue).includes(String(condition.value));\n case 'IN':\n // Check if contextValue is in the array of values\n if (Array.isArray(condition.value)) {\n return condition.value.map(String).includes(String(contextValue));\n }\n return String(contextValue) === String(condition.value);\n case 'ONE_OF':\n case 'IS_ONE_OF':\n return condition.values?.includes(contextValue) ?? false;\n case 'NOT_ONE_OF':\n return !condition.values?.includes(contextValue);\n case 'GREATER_THAN':\n return Number(contextValue) > Number(condition.value);\n case 'LESS_THAN':\n return Number(contextValue) < Number(condition.value);\n case 'IN_SEGMENT':\n if (condition.segment_key) {\n const segment = ruleset.segments[condition.segment_key];\n if (segment) {\n return this.evaluateConditions(segment.conditions, context, ruleset);\n }\n }\n return false;\n default:\n console.warn(`Unknown operator: ${condition.operator}`);\n return false;\n }\n }\n\n /**\n * Serve a specific variation.\n */\n private serveVariation(\n flag: FlagConfig,\n variationId: string,\n reason: string\n ): EvaluationResult {\n const variation = flag.variations.find((v) => v.id === variationId);\n\n if (!variation) {\n return {\n value: null,\n variationKey: undefined,\n reason: 'variation_not_found',\n };\n }\n\n return {\n value: variation.value,\n variationKey: variation.key,\n reason,\n };\n }\n\n /**\n * Serve variation based on percentage rollout.\n */\n private servePercentageRollout(\n flag: FlagConfig,\n rollout: PercentageRollout,\n context: Context,\n reason: string\n ): EvaluationResult {\n const userId = context.userId || context.sessionId || 'anonymous';\n const bucket = this.getBucket(userId, flag.key || 'default'); // FIXED: Use flag.key for consistency\n\n let cumulative = 0;\n for (const item of rollout.variations) {\n cumulative += item.weight;\n if (bucket < cumulative) {\n return {\n value: item.variation.value,\n variationKey: item.variation.key,\n reason,\n };\n }\n }\n\n // Fallback to last variation\n const lastItem = rollout.variations[rollout.variations.length - 1];\n return {\n value: lastItem.variation.value,\n variationKey: lastItem.variation.key,\n reason,\n };\n }\n\n /**\n * Serve variation based on boolean rollout (simplified format: {on: percentage}).\n * Used for boolean flags where 'on' represents the percentage for 'true'.\n */\n private serveBooleanRollout(\n flag: FlagConfig,\n rollout: { on: number },\n context: Context,\n reason: string\n ): EvaluationResult {\n const userId = context.userId || context.sessionId || 'anonymous';\n const bucket = this.getBucket(userId, flag.key || 'default'); // FIXED: Use flag.key instead of flag.salt\n const percentage = rollout.on || 0;\n\n // Bucket is 0-9999, so we need to scale percentage (0-100) to 0-9999\n const threshold = (percentage / 100) * 10000;\n\n if (bucket < threshold) {\n // User is in the rollout, serve 'true' variation\n const trueVar = flag.variations.find(v => v.value === true);\n if (trueVar) {\n return {\n value: trueVar.value,\n variationKey: trueVar.key,\n reason,\n };\n }\n }\n\n // User is not in rollout, serve 'false' variation\n const falseVar = flag.variations.find(v => v.value === false);\n if (falseVar) {\n return {\n value: falseVar.value,\n variationKey: falseVar.key,\n reason,\n };\n }\n\n // Fallback\n return {\n value: false,\n variationKey: undefined,\n reason: 'rollout_fallback',\n };\n }\n\n /**\n * Get consistent bucket (0-9999) for user using MD5 hash.\n */\n /**\n * Get consistent bucket (0-9999) for user using simple hash (DJB2).\n * Format: flagKey:userId (matches Python SDK for cross-SDK consistency)\n */\n private getBucket(userId: string, flagKey: string): number {\n const input = `${flagKey}:${userId}`; // FIXED: Match Python SDK format\n let hash = 5381;\n for (let i = 0; i < input.length; i++) {\n hash = ((hash << 5) + hash) + input.charCodeAt(i); /* hash * 33 + c */\n }\n return Math.abs(hash) % 10000;\n }\n}\n","import { Evaluator } from './evaluator';\nimport { Context, Config, EvaluationResult, Ruleset } from './types';\nexport { Context, Config, EvaluationResult, Ruleset };\n\n/**\n * FluxControl JavaScript SDK (Thick Client)\n * \n * Downloads rulesets and evaluates flags locally.\n */\nexport class FluxClient {\n private sdkKey: string;\n private config: Config;\n private evaluator: Evaluator;\n private ruleset: Ruleset | null = null;\n private pollingInterval: any;\n\n constructor(sdkKey: string, config: Config) {\n if (!config.apiUrl) {\n throw new Error('[FluxClient] apiUrl is required. Please provide the FluxControl API endpoint (e.g., \"https://api.fluxcontrol.io/api/v1/sdk\")');\n }\n\n this.sdkKey = sdkKey;\n this.config = {\n environment: 'production',\n pollingIntervalMs: 60000,\n ...config\n };\n this.evaluator = new Evaluator();\n\n // Auto-init if in browser\n if (typeof window !== 'undefined') {\n this.init();\n }\n }\n\n /**\n * Initialize the SDK (fetch ruleset).\n */\n async init(): Promise<void> {\n await this.fetchRuleset();\n\n if (this.config.pollingIntervalMs && this.config.pollingIntervalMs > 0) {\n this.pollingInterval = setInterval(() => this.fetchRuleset(), this.config.pollingIntervalMs);\n }\n }\n\n /**\n * Fetch ruleset from API.\n */\n async fetchRuleset(): Promise<void> {\n try {\n const environment = this.config.environment || 'production';\n const url = `${this.config.apiUrl}/ruleset?environment=${environment}`;\n\n const res = await fetch(url, {\n headers: {\n 'Authorization': `Bearer ${this.sdkKey}`\n }\n });\n\n if (res.ok) {\n this.ruleset = await res.json();\n // Optional: Save to localStorage for offline boot\n if (typeof window !== 'undefined') {\n localStorage.setItem(`flux_ruleset_${this.sdkKey}`, JSON.stringify(this.ruleset));\n }\n } else {\n console.warn('[FluxClient] Failed to fetch ruleset:', res.status);\n }\n } catch (e) {\n console.error('[FluxClient] Error fetching ruleset:', e);\n // Try load from local storage\n if (typeof window !== 'undefined' && !this.ruleset) {\n const stored = localStorage.getItem(`flux_ruleset_${this.sdkKey}`);\n if (stored) this.ruleset = JSON.parse(stored);\n }\n }\n }\n\n /**\n * Evaluate a boolean flag.\n * Note: Sync evaluation now possible if ruleset is loaded!\n * But we keep async signature if needed or make it sync?\n * Legacy SDK was async. Let's make it sync but helpful.\n * Actually, if init() is async, we can't guarantee ruleset is there immediately.\n */\n boolVariation(flagKey: string, context: Context, defaultValue: boolean): boolean {\n const result = this.evaluate(flagKey, context, defaultValue);\n return !!result;\n }\n\n stringVariation(flagKey: string, context: Context, defaultValue: string): string {\n const result = this.evaluate(flagKey, context, defaultValue);\n return String(result);\n }\n\n numberVariation(flagKey: string, context: Context, defaultValue: number): number {\n const result = this.evaluate(flagKey, context, defaultValue);\n return Number(result);\n }\n\n jsonVariation<T = any>(flagKey: string, context: Context, defaultValue: T): T {\n return this.evaluate(flagKey, context, defaultValue) as T;\n }\n\n isEnabled(flagKey: string, contextOrId: Context | string, defaultValue: boolean = false): boolean {\n // Compatibility helper\n const context = typeof contextOrId === 'string' ? { userId: contextOrId } : contextOrId;\n return this.boolVariation(flagKey, context, defaultValue);\n }\n\n /**\n * Internal evaluation.\n */\n private evaluate(flagKey: string, context: Context, defaultValue: any): any {\n if (!this.ruleset) {\n console.warn(`[FluxClient] Ruleset not loaded. Returning default for ${flagKey}`);\n return defaultValue;\n }\n\n const result = this.evaluator.evaluate(flagKey, context, this.ruleset, defaultValue);\n\n // TODO: Queue exposure event logic here\n\n return result.value;\n }\n\n /**\n * Track a custom event.\n */\n async track(eventName: string, context: Context, properties?: Record<string, any>, value?: number): Promise<void> {\n try {\n await fetch(`${this.config.apiUrl}/events`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.sdkKey}`\n },\n body: JSON.stringify({\n events: [{\n type: 'custom',\n timestamp: new Date().toISOString(),\n eventName,\n context,\n properties,\n value\n }]\n })\n });\n } catch (e) {\n console.error('[FluxClient] Error tracking event:', e);\n }\n }\n\n close() {\n if (this.pollingInterval) clearInterval(this.pollingInterval);\n }\n}\n","/**\n * React hooks for FluxControl.\n * \n * Provides easy integration with React applications.\n */\n\nimport React, { createContext, useContext, useEffect, useState, ReactNode } from 'react';\nimport { FluxClient, Context as FluxContext } from './client';\n\ninterface FluxProviderProps {\n sdkKey: string;\n config?: any;\n context?: FluxContext;\n children: ReactNode;\n}\n\ninterface FluxContextType {\n client: FluxClient | null;\n context: FluxContext;\n}\n\nconst FluxReactContext = createContext<FluxContextType>({\n client: null,\n context: {}\n});\n\n/**\n * Provider component for FluxControl.\n * \n * Wrap your app with this to enable hooks.\n * \n * @example\n * <FluxProvider sdkKey=\"your-key\" context={{ userId: \"user-123\" }}>\n * <App />\n * </FluxProvider>\n */\nexport function FluxProvider({ sdkKey, config, context = {}, children }: FluxProviderProps) {\n const [client] = useState(() => new FluxClient(sdkKey, config));\n\n useEffect(() => {\n return () => {\n client.close();\n };\n }, [client]);\n\n return (\n <FluxReactContext.Provider value={{ client, context }}>\n {children}\n </FluxReactContext.Provider>\n );\n}\n\n/**\n * Hook to evaluate a feature flag.\n * \n * @example\n * const checkoutFlow = useFeature('checkout-flow', 'standard');\n * if (checkoutFlow === 'one-click') {\n * return <OneClickCheckout />;\n * }\n */\nexport function useFeature<T = any>(\n flagKey: string,\n defaultValue: T,\n contextOverride?: FluxContext\n): T {\n const { client, context: providerContext } = useContext(FluxReactContext);\n const [value, setValue] = useState<T>(defaultValue);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n if (!client) {\n setValue(defaultValue);\n setLoading(false);\n return;\n }\n\n const context = { ...providerContext, ...contextOverride };\n\n const evaluate = async () => {\n try {\n let result: any;\n\n // Determine type and call appropriate method\n if (typeof defaultValue === 'boolean') {\n result = await client.boolVariation(flagKey, context, defaultValue);\n } else if (typeof defaultValue === 'number') {\n result = await client.numberVariation(flagKey, context, defaultValue);\n } else if (typeof defaultValue === 'string') {\n result = await client.stringVariation(flagKey, context, defaultValue);\n } else {\n result = await client.jsonVariation(flagKey, context, defaultValue);\n }\n\n setValue(result as T);\n } catch (error) {\n console.error(`FluxControl: Error evaluating ${flagKey}:`, error);\n setValue(defaultValue);\n } finally {\n setLoading(false);\n }\n };\n\n evaluate();\n }, [client, flagKey, defaultValue, providerContext, contextOverride]);\n\n return value;\n}\n\n/**\n * Hook to get the FluxControl client instance.\n * \n * Use this for advanced scenarios like tracking custom events.\n * \n * @example\n * const client = useFluxClient();\n * client.track('button_clicked', context, { button: 'checkout' });\n */\nexport function useFluxClient(): FluxClient | null {\n const { client } = useContext(FluxReactContext);\n return client;\n}\n\n/**\n * Hook to track a custom event.\n * \n * @example\n * const trackPurchase = useTrack();\n * trackPurchase('purchase_completed', { productId: '123' }, 99.99);\n */\nexport function useTrack() {\n const { client, context } = useContext(FluxReactContext);\n\n return (\n eventName: string,\n properties?: Record<string, any>,\n value?: number,\n contextOverride?: FluxContext\n ) => {\n if (!client) {\n return;\n }\n\n const finalContext = { ...context, ...contextOverride };\n client.track(eventName, finalContext, properties, value);\n };\n}\n","/**\n * FluxControl JavaScript SDK\n * \n * @packageDocumentation\n */\n\nexport * from './types';\nexport * from './client';\nexport * from './react';\n\nimport { FluxClient } from './client';\nexport default FluxClient;\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../src/evaluator.ts","../src/client.ts","../src/react.tsx","../src/index.ts"],"sourcesContent":["// import { createHash } from 'crypto';\nimport {\n Context,\n Ruleset,\n FlagConfig,\n TargetingRule,\n Condition,\n EvaluationResult,\n Variation,\n PercentageRollout,\n} from './types';\n\n/**\n * Evaluates feature flags against targeting rules.\n */\nexport class Evaluator {\n /**\n * Evaluate a flag.\n */\n evaluate(\n flagKey: string,\n context: Context,\n ruleset: Ruleset,\n defaultValue: any\n ): EvaluationResult {\n const flag = ruleset.flags[flagKey];\n\n if (!flag) {\n return {\n value: defaultValue,\n variationKey: undefined,\n reason: 'flag_not_found',\n };\n }\n\n // Evaluate targeting rules\n if (flag.rules) {\n // Sort by priority (lower numbers = higher priority, evaluated first)\n const sortedRules = [...flag.rules].sort((a: any, b: any) => {\n const priorityA = a.priority ?? 999;\n const priorityB = b.priority ?? 999;\n return priorityA - priorityB;\n });\n\n for (const rule of sortedRules) {\n if (this.evaluateConditions(rule.conditions, context, ruleset)) {\n // Rule matched\n if (rule.variationId) {\n return this.serveVariation(flag, rule.variationId, 'rule_match');\n } else if (rule.rollout) {\n // Handle boolean flag rollout format: {on: percentage}\n return this.serveBooleanRollout(\n flag,\n rule.rollout,\n context,\n 'percentage_rollout'\n );\n }\n }\n }\n }\n\n // No rules matched, return default variation\n return this.serveVariation(flag, flag.defaultVariationId, 'default');\n }\n\n /**\n * Evaluate all conditions in a rule.\n */\n private evaluateConditions(\n conditions: Condition[],\n context: Context,\n ruleset: Ruleset\n ): boolean {\n if (!conditions || conditions.length === 0) {\n return true;\n }\n\n return conditions.every((condition) =>\n this.evaluateCondition(condition, context, ruleset)\n );\n }\n\n /**\n * Evaluate a single condition.\n */\n private evaluateCondition(\n condition: Condition,\n context: Context,\n ruleset: Ruleset\n ): boolean {\n // Get attribute value from context\n let contextValue: any;\n if (condition.attribute === 'userId' || condition.attribute === 'user_id') {\n contextValue = context.userId;\n } else if (condition.attribute === 'sessionId') {\n contextValue = context.sessionId;\n } else {\n contextValue = context.attributes?.[condition.attribute];\n }\n\n switch (condition.operator) {\n case 'IS':\n return contextValue === condition.value;\n case 'IS_NOT':\n return contextValue !== condition.value;\n case 'CONTAINS':\n return String(contextValue).includes(String(condition.value));\n case 'DOES_NOT_CONTAIN':\n return !String(contextValue).includes(String(condition.value));\n case 'IN':\n // Check if contextValue is in the array of values\n if (Array.isArray(condition.value)) {\n return condition.value.map(String).includes(String(contextValue));\n }\n return String(contextValue) === String(condition.value);\n case 'ONE_OF':\n case 'IS_ONE_OF':\n return condition.values?.includes(contextValue) ?? false;\n case 'NOT_ONE_OF':\n return !condition.values?.includes(contextValue);\n case 'GREATER_THAN':\n return Number(contextValue) > Number(condition.value);\n case 'LESS_THAN':\n return Number(contextValue) < Number(condition.value);\n case 'IN_SEGMENT':\n if (condition.segment_key) {\n const segment = ruleset.segments[condition.segment_key];\n if (segment) {\n return this.evaluateConditions(segment.conditions, context, ruleset);\n }\n }\n return false;\n default:\n console.warn(`Unknown operator: ${condition.operator}`);\n return false;\n }\n }\n\n /**\n * Serve a specific variation.\n */\n private serveVariation(\n flag: FlagConfig,\n variationId: string,\n reason: string\n ): EvaluationResult {\n const variation = flag.variations.find((v) => v.id === variationId);\n\n if (!variation) {\n return {\n value: null,\n variationKey: undefined,\n reason: 'variation_not_found',\n };\n }\n\n return {\n value: variation.value,\n variationKey: variation.key,\n reason,\n };\n }\n\n /**\n * Serve variation based on percentage rollout.\n */\n private servePercentageRollout(\n flag: FlagConfig,\n rollout: PercentageRollout,\n context: Context,\n reason: string\n ): EvaluationResult {\n const userId = context.userId || context.sessionId || 'anonymous';\n const bucket = this.getBucket(userId, flag.key || 'default'); // FIXED: Use flag.key for consistency\n\n let cumulative = 0;\n for (const item of rollout.variations) {\n cumulative += item.weight;\n if (bucket < cumulative) {\n return {\n value: item.variation.value,\n variationKey: item.variation.key,\n reason,\n };\n }\n }\n\n // Fallback to last variation\n const lastItem = rollout.variations[rollout.variations.length - 1];\n return {\n value: lastItem.variation.value,\n variationKey: lastItem.variation.key,\n reason,\n };\n }\n\n /**\n * Serve variation based on boolean rollout (simplified format: {on: percentage}).\n * Used for boolean flags where 'on' represents the percentage for 'true'.\n */\n private serveBooleanRollout(\n flag: FlagConfig,\n rollout: { on: number },\n context: Context,\n reason: string\n ): EvaluationResult {\n const userId = context.userId || context.sessionId || 'anonymous';\n const bucket = this.getBucket(userId, flag.key || 'default'); // FIXED: Use flag.key instead of flag.salt\n const percentage = rollout.on || 0;\n\n // Bucket is 0-9999, so we need to scale percentage (0-100) to 0-9999\n const threshold = (percentage / 100) * 10000;\n\n if (bucket < threshold) {\n // User is in the rollout, serve 'true' variation\n const trueVar = flag.variations.find(v => v.value === true);\n if (trueVar) {\n return {\n value: trueVar.value,\n variationKey: trueVar.key,\n reason,\n };\n }\n }\n\n // User is not in rollout, serve 'false' variation\n const falseVar = flag.variations.find(v => v.value === false);\n if (falseVar) {\n return {\n value: falseVar.value,\n variationKey: falseVar.key,\n reason,\n };\n }\n\n // Fallback\n return {\n value: false,\n variationKey: undefined,\n reason: 'rollout_fallback',\n };\n }\n\n /**\n * Get consistent bucket (0-9999) for user using MD5 hash.\n */\n /**\n * Get consistent bucket (0-9999) for user using simple hash (DJB2).\n * Format: flagKey:userId (matches Python SDK for cross-SDK consistency)\n */\n private getBucket(userId: string, flagKey: string): number {\n const input = `${flagKey}:${userId}`; // FIXED: Match Python SDK format\n let hash = 5381;\n for (let i = 0; i < input.length; i++) {\n hash = ((hash << 5) + hash) + input.charCodeAt(i); /* hash * 33 + c */\n }\n return Math.abs(hash) % 10000;\n }\n}\n","import { Evaluator } from './evaluator';\nimport { Context, Config, EvaluationResult, Ruleset, EventType, EventCallback } from './types';\nexport { Context, Config, EvaluationResult, Ruleset };\n\n/**\n * FluxControl JavaScript SDK (Thick Client)\n * \n * Downloads rulesets and evaluates flags locally.\n */\nexport class FluxClient {\n private sdkKey: string;\n private config: Config;\n private evaluator: Evaluator;\n private ruleset: Ruleset | null = null;\n private pollingInterval: any;\n private listeners: Map<EventType, Set<EventCallback>> = new Map();\n private _isReady: boolean = false;\n\n constructor(sdkKey: string, config: Config) {\n if (!config.apiUrl) {\n throw new Error('[FluxClient] apiUrl is required. Please provide the FluxControl API endpoint (e.g., \"https://api.fluxcontrol.io/api/v1/sdk\")');\n }\n\n // Validation warnings (non-breaking)\n if (!sdkKey || sdkKey.trim() === '') {\n console.warn('[FluxClient] SDK key appears to be missing or invalid. API requests will fail.');\n }\n if (!config.apiUrl.endsWith('/api/v1/sdk')) {\n console.warn(`[FluxClient] API URL should end with '/api/v1/sdk'. Current: ${config.apiUrl}`);\n }\n\n this.sdkKey = sdkKey;\n this.config = {\n environment: 'production',\n pollingIntervalMs: 60000,\n ...config\n };\n this.evaluator = new Evaluator();\n\n // Auto-init if in browser\n if (typeof window !== 'undefined') {\n this.init();\n }\n }\n\n /**\n * Check if the SDK is ready (ruleset loaded).\n */\n get isReady(): boolean {\n return this._isReady;\n }\n\n /**\n * Subscribe to SDK events.\n * @param event - Event type ('ready', 'update', 'error')\n * @param callback - Function to call when event fires\n */\n on(event: EventType, callback: EventCallback): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(callback);\n }\n\n /**\n * Unsubscribe from SDK events.\n * @param event - Event type\n * @param callback - Function to remove\n */\n off(event: EventType, callback: EventCallback): void {\n const callbacks = this.listeners.get(event);\n if (callbacks) {\n callbacks.delete(callback);\n }\n }\n\n /**\n * Emit an event to all listeners.\n */\n private emit(event: EventType, data?: any): void {\n const callbacks = this.listeners.get(event);\n if (callbacks) {\n callbacks.forEach(callback => {\n try {\n callback(data);\n } catch (e) {\n console.error(`[FluxClient] Error in ${event} event listener:`, e);\n }\n });\n }\n }\n\n /**\n * Initialize the SDK (fetch ruleset).\n */\n async init(): Promise<void> {\n await this.fetchRuleset();\n\n if (this.config.pollingIntervalMs && this.config.pollingIntervalMs > 0) {\n this.pollingInterval = setInterval(() => this.fetchRuleset(), this.config.pollingIntervalMs);\n }\n }\n\n /**\n * Fetch ruleset from API.\n */\n async fetchRuleset(): Promise<void> {\n const isInitialFetch = !this._isReady;\n\n try {\n const environment = this.config.environment || 'production';\n const url = `${this.config.apiUrl}/ruleset?environment=${environment}`;\n\n const res = await fetch(url, {\n headers: {\n 'Authorization': `Bearer ${this.sdkKey}`\n }\n });\n\n if (res.ok) {\n this.ruleset = await res.json();\n\n // Mark as ready and emit appropriate event\n if (isInitialFetch) {\n this._isReady = true;\n this.emit('ready', this.ruleset);\n } else {\n this.emit('update', this.ruleset);\n }\n\n // Optional: Save to localStorage for offline boot\n if (typeof window !== 'undefined') {\n localStorage.setItem(`flux_ruleset_${this.sdkKey}`, JSON.stringify(this.ruleset));\n }\n } else {\n // Enhanced error messages with actionable guidance\n let errorMessage = `[FluxClient] Failed to fetch ruleset: ${res.status} ${res.statusText}`;\n\n switch (res.status) {\n case 401:\n errorMessage += ' - Unauthorized: Check your SDK Key';\n break;\n case 403:\n errorMessage += ' - Forbidden: Check SDK key permissions';\n break;\n case 404:\n errorMessage += ` - Not Found: Verify API URL ends with '/api/v1/sdk'. Current: ${this.config.apiUrl}`;\n break;\n case 500:\n case 502:\n case 503:\n errorMessage += ' - Server Error: FluxControl API may be down';\n break;\n }\n\n console.warn(errorMessage);\n this.emit('error', { status: res.status, message: errorMessage });\n }\n } catch (e) {\n const errorMessage = `[FluxClient] Error fetching ruleset from ${this.config.apiUrl}`;\n console.error(errorMessage, e);\n this.emit('error', { error: e, message: errorMessage });\n\n // Try load from local storage\n if (typeof window !== 'undefined' && !this.ruleset) {\n const stored = localStorage.getItem(`flux_ruleset_${this.sdkKey}`);\n if (stored) {\n this.ruleset = JSON.parse(stored);\n if (isInitialFetch) {\n this._isReady = true;\n this.emit('ready', this.ruleset);\n }\n }\n }\n }\n }\n\n /**\n * Evaluate a boolean flag.\n * Note: Sync evaluation now possible if ruleset is loaded!\n * But we keep async signature if needed or make it sync?\n * Legacy SDK was async. Let's make it sync but helpful.\n * Actually, if init() is async, we can't guarantee ruleset is there immediately.\n */\n boolVariation(flagKey: string, context: Context, defaultValue: boolean): boolean {\n const result = this.evaluate(flagKey, context, defaultValue);\n return !!result;\n }\n\n stringVariation(flagKey: string, context: Context, defaultValue: string): string {\n const result = this.evaluate(flagKey, context, defaultValue);\n return String(result);\n }\n\n numberVariation(flagKey: string, context: Context, defaultValue: number): number {\n const result = this.evaluate(flagKey, context, defaultValue);\n return Number(result);\n }\n\n jsonVariation<T = any>(flagKey: string, context: Context, defaultValue: T): T {\n return this.evaluate(flagKey, context, defaultValue) as T;\n }\n\n isEnabled(flagKey: string, contextOrId: Context | string, defaultValue: boolean = false): boolean {\n // Compatibility helper\n const context = typeof contextOrId === 'string' ? { userId: contextOrId } : contextOrId;\n return this.boolVariation(flagKey, context, defaultValue);\n }\n\n /**\n * Internal evaluation.\n */\n private evaluate(flagKey: string, context: Context, defaultValue: any): any {\n if (!this.ruleset) {\n console.warn(`[FluxClient] Ruleset not loaded. Returning default for ${flagKey}`);\n return defaultValue;\n }\n\n const result = this.evaluator.evaluate(flagKey, context, this.ruleset, defaultValue);\n\n // TODO: Queue exposure event logic here\n\n return result.value;\n }\n\n /**\n * Track a custom event.\n */\n async track(eventName: string, context: Context, properties?: Record<string, any>, value?: number): Promise<void> {\n try {\n await fetch(`${this.config.apiUrl}/events`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.sdkKey}`\n },\n body: JSON.stringify({\n events: [{\n type: 'custom',\n timestamp: new Date().toISOString(),\n eventName,\n context,\n properties,\n value\n }]\n })\n });\n } catch (e) {\n console.error('[FluxClient] Error tracking event:', e);\n }\n }\n\n close() {\n if (this.pollingInterval) clearInterval(this.pollingInterval);\n // Clear all event listeners\n this.listeners.clear();\n }\n}\n","/**\n * React hooks for FluxControl.\n * \n * Provides easy integration with React applications.\n */\n\nimport React, { createContext, useContext, useEffect, useState, ReactNode } from 'react';\nimport { FluxClient, Context as FluxContext } from './client';\n\ninterface FluxProviderProps {\n sdkKey: string;\n config?: any;\n context?: FluxContext;\n children: ReactNode;\n}\n\ninterface FluxContextType {\n client: FluxClient | null;\n context: FluxContext;\n}\n\nconst FluxReactContext = createContext<FluxContextType>({\n client: null,\n context: {}\n});\n\n/**\n * Provider component for FluxControl.\n * \n * Wrap your app with this to enable hooks.\n * \n * @example\n * <FluxProvider sdkKey=\"your-key\" context={{ userId: \"user-123\" }}>\n * <App />\n * </FluxProvider>\n */\nexport function FluxProvider({ sdkKey, config, context = {}, children }: FluxProviderProps) {\n // Validation warnings (non-breaking)\n useEffect(() => {\n if (!sdkKey || sdkKey.trim() === '') {\n console.warn(\"[FluxProvider] 'sdkKey' prop is required. Did you mean to use 'sdkKey' instead of 'apiKey'?\");\n }\n if (config && 'apiKey' in config) {\n console.warn(\"[FluxProvider] Found 'apiKey' in config. The SDK key should be passed as the 'sdkKey' prop, not in config.\");\n }\n }, [sdkKey, config]);\n\n const [client] = useState(() => new FluxClient(sdkKey, config));\n\n useEffect(() => {\n return () => {\n client.close();\n };\n }, [client]);\n\n return (\n <FluxReactContext.Provider value={{ client, context }}>\n {children}\n </FluxReactContext.Provider>\n );\n}\n\n/**\n * Hook to evaluate a feature flag.\n * \n * @example\n * const checkoutFlow = useFeature('checkout-flow', 'standard');\n * if (checkoutFlow === 'one-click') {\n * return <OneClickCheckout />;\n * }\n */\nexport function useFeature<T = any>(\n flagKey: string,\n defaultValue: T,\n contextOverride?: FluxContext\n): T {\n const { client, context: providerContext } = useContext(FluxReactContext);\n const [value, setValue] = useState<T>(defaultValue);\n\n useEffect(() => {\n if (!client) {\n setValue(defaultValue);\n return;\n }\n\n const context = { ...providerContext, ...contextOverride };\n\n const evaluate = () => {\n try {\n let result: any;\n\n // Determine type and call appropriate method (synchronous)\n if (typeof defaultValue === 'boolean') {\n result = client.boolVariation(flagKey, context, defaultValue);\n } else if (typeof defaultValue === 'number') {\n result = client.numberVariation(flagKey, context, defaultValue);\n } else if (typeof defaultValue === 'string') {\n result = client.stringVariation(flagKey, context, defaultValue);\n } else {\n result = client.jsonVariation(flagKey, context, defaultValue);\n }\n\n setValue(result as T);\n } catch (error) {\n console.error(`FluxControl: Error evaluating ${flagKey}:`, error);\n setValue(defaultValue);\n }\n };\n\n // Initial evaluation\n evaluate();\n\n // Subscribe to SDK events for automatic updates\n const onReady = () => evaluate();\n const onUpdate = () => evaluate();\n\n client.on('ready', onReady);\n client.on('update', onUpdate);\n\n // Cleanup event listeners\n return () => {\n client.off('ready', onReady);\n client.off('update', onUpdate);\n };\n }, [client, flagKey, defaultValue, providerContext, contextOverride]);\n\n return value;\n}\n\n/**\n * Hook to get the FluxControl client instance.\n * \n * Use this for advanced scenarios like tracking custom events.\n * \n * @example\n * const client = useFluxClient();\n * client.track('button_clicked', context, { button: 'checkout' });\n */\nexport function useFluxClient(): FluxClient | null {\n const { client } = useContext(FluxReactContext);\n return client;\n}\n\n/**\n * Hook to track a custom event.\n * \n * @example\n * const trackPurchase = useTrack();\n * trackPurchase('purchase_completed', { productId: '123' }, 99.99);\n */\nexport function useTrack() {\n const { client, context } = useContext(FluxReactContext);\n\n return (\n eventName: string,\n properties?: Record<string, any>,\n value?: number,\n contextOverride?: FluxContext\n ) => {\n if (!client) {\n return;\n }\n\n const finalContext = { ...context, ...contextOverride };\n client.track(eventName, finalContext, properties, value);\n };\n}\n","/**\n * FluxControl JavaScript SDK\n * \n * @packageDocumentation\n */\n\nexport * from './types';\nexport * from './client';\nexport * from './react';\n\nimport { FluxClient } from './client';\nexport default FluxClient;\n"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -210,9 +210,18 @@
|
|
|
210
210
|
class FluxClient {
|
|
211
211
|
constructor(sdkKey, config) {
|
|
212
212
|
this.ruleset = null;
|
|
213
|
+
this.listeners = new Map();
|
|
214
|
+
this._isReady = false;
|
|
213
215
|
if (!config.apiUrl) {
|
|
214
216
|
throw new Error('[FluxClient] apiUrl is required. Please provide the FluxControl API endpoint (e.g., "https://api.fluxcontrol.io/api/v1/sdk")');
|
|
215
217
|
}
|
|
218
|
+
// Validation warnings (non-breaking)
|
|
219
|
+
if (!sdkKey || sdkKey.trim() === '') {
|
|
220
|
+
console.warn('[FluxClient] SDK key appears to be missing or invalid. API requests will fail.');
|
|
221
|
+
}
|
|
222
|
+
if (!config.apiUrl.endsWith('/api/v1/sdk')) {
|
|
223
|
+
console.warn(`[FluxClient] API URL should end with '/api/v1/sdk'. Current: ${config.apiUrl}`);
|
|
224
|
+
}
|
|
216
225
|
this.sdkKey = sdkKey;
|
|
217
226
|
this.config = {
|
|
218
227
|
environment: 'production',
|
|
@@ -225,6 +234,50 @@
|
|
|
225
234
|
this.init();
|
|
226
235
|
}
|
|
227
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* Check if the SDK is ready (ruleset loaded).
|
|
239
|
+
*/
|
|
240
|
+
get isReady() {
|
|
241
|
+
return this._isReady;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Subscribe to SDK events.
|
|
245
|
+
* @param event - Event type ('ready', 'update', 'error')
|
|
246
|
+
* @param callback - Function to call when event fires
|
|
247
|
+
*/
|
|
248
|
+
on(event, callback) {
|
|
249
|
+
if (!this.listeners.has(event)) {
|
|
250
|
+
this.listeners.set(event, new Set());
|
|
251
|
+
}
|
|
252
|
+
this.listeners.get(event).add(callback);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Unsubscribe from SDK events.
|
|
256
|
+
* @param event - Event type
|
|
257
|
+
* @param callback - Function to remove
|
|
258
|
+
*/
|
|
259
|
+
off(event, callback) {
|
|
260
|
+
const callbacks = this.listeners.get(event);
|
|
261
|
+
if (callbacks) {
|
|
262
|
+
callbacks.delete(callback);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Emit an event to all listeners.
|
|
267
|
+
*/
|
|
268
|
+
emit(event, data) {
|
|
269
|
+
const callbacks = this.listeners.get(event);
|
|
270
|
+
if (callbacks) {
|
|
271
|
+
callbacks.forEach(callback => {
|
|
272
|
+
try {
|
|
273
|
+
callback(data);
|
|
274
|
+
}
|
|
275
|
+
catch (e) {
|
|
276
|
+
console.error(`[FluxClient] Error in ${event} event listener:`, e);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
228
281
|
/**
|
|
229
282
|
* Initialize the SDK (fetch ruleset).
|
|
230
283
|
*/
|
|
@@ -238,6 +291,7 @@
|
|
|
238
291
|
* Fetch ruleset from API.
|
|
239
292
|
*/
|
|
240
293
|
async fetchRuleset() {
|
|
294
|
+
const isInitialFetch = !this._isReady;
|
|
241
295
|
try {
|
|
242
296
|
const environment = this.config.environment || 'production';
|
|
243
297
|
const url = `${this.config.apiUrl}/ruleset?environment=${environment}`;
|
|
@@ -248,22 +302,56 @@
|
|
|
248
302
|
});
|
|
249
303
|
if (res.ok) {
|
|
250
304
|
this.ruleset = await res.json();
|
|
305
|
+
// Mark as ready and emit appropriate event
|
|
306
|
+
if (isInitialFetch) {
|
|
307
|
+
this._isReady = true;
|
|
308
|
+
this.emit('ready', this.ruleset);
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
this.emit('update', this.ruleset);
|
|
312
|
+
}
|
|
251
313
|
// Optional: Save to localStorage for offline boot
|
|
252
314
|
if (typeof window !== 'undefined') {
|
|
253
315
|
localStorage.setItem(`flux_ruleset_${this.sdkKey}`, JSON.stringify(this.ruleset));
|
|
254
316
|
}
|
|
255
317
|
}
|
|
256
318
|
else {
|
|
257
|
-
|
|
319
|
+
// Enhanced error messages with actionable guidance
|
|
320
|
+
let errorMessage = `[FluxClient] Failed to fetch ruleset: ${res.status} ${res.statusText}`;
|
|
321
|
+
switch (res.status) {
|
|
322
|
+
case 401:
|
|
323
|
+
errorMessage += ' - Unauthorized: Check your SDK Key';
|
|
324
|
+
break;
|
|
325
|
+
case 403:
|
|
326
|
+
errorMessage += ' - Forbidden: Check SDK key permissions';
|
|
327
|
+
break;
|
|
328
|
+
case 404:
|
|
329
|
+
errorMessage += ` - Not Found: Verify API URL ends with '/api/v1/sdk'. Current: ${this.config.apiUrl}`;
|
|
330
|
+
break;
|
|
331
|
+
case 500:
|
|
332
|
+
case 502:
|
|
333
|
+
case 503:
|
|
334
|
+
errorMessage += ' - Server Error: FluxControl API may be down';
|
|
335
|
+
break;
|
|
336
|
+
}
|
|
337
|
+
console.warn(errorMessage);
|
|
338
|
+
this.emit('error', { status: res.status, message: errorMessage });
|
|
258
339
|
}
|
|
259
340
|
}
|
|
260
341
|
catch (e) {
|
|
261
|
-
|
|
342
|
+
const errorMessage = `[FluxClient] Error fetching ruleset from ${this.config.apiUrl}`;
|
|
343
|
+
console.error(errorMessage, e);
|
|
344
|
+
this.emit('error', { error: e, message: errorMessage });
|
|
262
345
|
// Try load from local storage
|
|
263
346
|
if (typeof window !== 'undefined' && !this.ruleset) {
|
|
264
347
|
const stored = localStorage.getItem(`flux_ruleset_${this.sdkKey}`);
|
|
265
|
-
if (stored)
|
|
348
|
+
if (stored) {
|
|
266
349
|
this.ruleset = JSON.parse(stored);
|
|
350
|
+
if (isInitialFetch) {
|
|
351
|
+
this._isReady = true;
|
|
352
|
+
this.emit('ready', this.ruleset);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
267
355
|
}
|
|
268
356
|
}
|
|
269
357
|
}
|
|
@@ -336,6 +424,8 @@
|
|
|
336
424
|
close() {
|
|
337
425
|
if (this.pollingInterval)
|
|
338
426
|
clearInterval(this.pollingInterval);
|
|
427
|
+
// Clear all event listeners
|
|
428
|
+
this.listeners.clear();
|
|
339
429
|
}
|
|
340
430
|
}
|
|
341
431
|
|
|
@@ -359,6 +449,15 @@
|
|
|
359
449
|
* </FluxProvider>
|
|
360
450
|
*/
|
|
361
451
|
function FluxProvider({ sdkKey, config, context = {}, children }) {
|
|
452
|
+
// Validation warnings (non-breaking)
|
|
453
|
+
React.useEffect(() => {
|
|
454
|
+
if (!sdkKey || sdkKey.trim() === '') {
|
|
455
|
+
console.warn("[FluxProvider] 'sdkKey' prop is required. Did you mean to use 'sdkKey' instead of 'apiKey'?");
|
|
456
|
+
}
|
|
457
|
+
if (config && 'apiKey' in config) {
|
|
458
|
+
console.warn("[FluxProvider] Found 'apiKey' in config. The SDK key should be passed as the 'sdkKey' prop, not in config.");
|
|
459
|
+
}
|
|
460
|
+
}, [sdkKey, config]);
|
|
362
461
|
const [client] = React.useState(() => new FluxClient(sdkKey, config));
|
|
363
462
|
React.useEffect(() => {
|
|
364
463
|
return () => {
|
|
@@ -379,29 +478,27 @@
|
|
|
379
478
|
function useFeature(flagKey, defaultValue, contextOverride) {
|
|
380
479
|
const { client, context: providerContext } = React.useContext(FluxReactContext);
|
|
381
480
|
const [value, setValue] = React.useState(defaultValue);
|
|
382
|
-
const [loading, setLoading] = React.useState(true);
|
|
383
481
|
React.useEffect(() => {
|
|
384
482
|
if (!client) {
|
|
385
483
|
setValue(defaultValue);
|
|
386
|
-
setLoading(false);
|
|
387
484
|
return;
|
|
388
485
|
}
|
|
389
486
|
const context = { ...providerContext, ...contextOverride };
|
|
390
|
-
const evaluate =
|
|
487
|
+
const evaluate = () => {
|
|
391
488
|
try {
|
|
392
489
|
let result;
|
|
393
|
-
// Determine type and call appropriate method
|
|
490
|
+
// Determine type and call appropriate method (synchronous)
|
|
394
491
|
if (typeof defaultValue === 'boolean') {
|
|
395
|
-
result =
|
|
492
|
+
result = client.boolVariation(flagKey, context, defaultValue);
|
|
396
493
|
}
|
|
397
494
|
else if (typeof defaultValue === 'number') {
|
|
398
|
-
result =
|
|
495
|
+
result = client.numberVariation(flagKey, context, defaultValue);
|
|
399
496
|
}
|
|
400
497
|
else if (typeof defaultValue === 'string') {
|
|
401
|
-
result =
|
|
498
|
+
result = client.stringVariation(flagKey, context, defaultValue);
|
|
402
499
|
}
|
|
403
500
|
else {
|
|
404
|
-
result =
|
|
501
|
+
result = client.jsonVariation(flagKey, context, defaultValue);
|
|
405
502
|
}
|
|
406
503
|
setValue(result);
|
|
407
504
|
}
|
|
@@ -409,11 +506,19 @@
|
|
|
409
506
|
console.error(`FluxControl: Error evaluating ${flagKey}:`, error);
|
|
410
507
|
setValue(defaultValue);
|
|
411
508
|
}
|
|
412
|
-
finally {
|
|
413
|
-
setLoading(false);
|
|
414
|
-
}
|
|
415
509
|
};
|
|
510
|
+
// Initial evaluation
|
|
416
511
|
evaluate();
|
|
512
|
+
// Subscribe to SDK events for automatic updates
|
|
513
|
+
const onReady = () => evaluate();
|
|
514
|
+
const onUpdate = () => evaluate();
|
|
515
|
+
client.on('ready', onReady);
|
|
516
|
+
client.on('update', onUpdate);
|
|
517
|
+
// Cleanup event listeners
|
|
518
|
+
return () => {
|
|
519
|
+
client.off('ready', onReady);
|
|
520
|
+
client.off('update', onUpdate);
|
|
521
|
+
};
|
|
417
522
|
}, [client, flagKey, defaultValue, providerContext, contextOverride]);
|
|
418
523
|
return value;
|
|
419
524
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/evaluator.ts","../src/client.ts","../src/react.tsx","../src/index.ts"],"sourcesContent":["// import { createHash } from 'crypto';\nimport {\n Context,\n Ruleset,\n FlagConfig,\n TargetingRule,\n Condition,\n EvaluationResult,\n Variation,\n PercentageRollout,\n} from './types';\n\n/**\n * Evaluates feature flags against targeting rules.\n */\nexport class Evaluator {\n /**\n * Evaluate a flag.\n */\n evaluate(\n flagKey: string,\n context: Context,\n ruleset: Ruleset,\n defaultValue: any\n ): EvaluationResult {\n const flag = ruleset.flags[flagKey];\n\n if (!flag) {\n return {\n value: defaultValue,\n variationKey: undefined,\n reason: 'flag_not_found',\n };\n }\n\n // Evaluate targeting rules\n if (flag.rules) {\n // Sort by priority (lower numbers = higher priority, evaluated first)\n const sortedRules = [...flag.rules].sort((a: any, b: any) => {\n const priorityA = a.priority ?? 999;\n const priorityB = b.priority ?? 999;\n return priorityA - priorityB;\n });\n\n for (const rule of sortedRules) {\n if (this.evaluateConditions(rule.conditions, context, ruleset)) {\n // Rule matched\n if (rule.variationId) {\n return this.serveVariation(flag, rule.variationId, 'rule_match');\n } else if (rule.rollout) {\n // Handle boolean flag rollout format: {on: percentage}\n return this.serveBooleanRollout(\n flag,\n rule.rollout,\n context,\n 'percentage_rollout'\n );\n }\n }\n }\n }\n\n // No rules matched, return default variation\n return this.serveVariation(flag, flag.defaultVariationId, 'default');\n }\n\n /**\n * Evaluate all conditions in a rule.\n */\n private evaluateConditions(\n conditions: Condition[],\n context: Context,\n ruleset: Ruleset\n ): boolean {\n if (!conditions || conditions.length === 0) {\n return true;\n }\n\n return conditions.every((condition) =>\n this.evaluateCondition(condition, context, ruleset)\n );\n }\n\n /**\n * Evaluate a single condition.\n */\n private evaluateCondition(\n condition: Condition,\n context: Context,\n ruleset: Ruleset\n ): boolean {\n // Get attribute value from context\n let contextValue: any;\n if (condition.attribute === 'userId' || condition.attribute === 'user_id') {\n contextValue = context.userId;\n } else if (condition.attribute === 'sessionId') {\n contextValue = context.sessionId;\n } else {\n contextValue = context.attributes?.[condition.attribute];\n }\n\n switch (condition.operator) {\n case 'IS':\n return contextValue === condition.value;\n case 'IS_NOT':\n return contextValue !== condition.value;\n case 'CONTAINS':\n return String(contextValue).includes(String(condition.value));\n case 'DOES_NOT_CONTAIN':\n return !String(contextValue).includes(String(condition.value));\n case 'IN':\n // Check if contextValue is in the array of values\n if (Array.isArray(condition.value)) {\n return condition.value.map(String).includes(String(contextValue));\n }\n return String(contextValue) === String(condition.value);\n case 'ONE_OF':\n case 'IS_ONE_OF':\n return condition.values?.includes(contextValue) ?? false;\n case 'NOT_ONE_OF':\n return !condition.values?.includes(contextValue);\n case 'GREATER_THAN':\n return Number(contextValue) > Number(condition.value);\n case 'LESS_THAN':\n return Number(contextValue) < Number(condition.value);\n case 'IN_SEGMENT':\n if (condition.segment_key) {\n const segment = ruleset.segments[condition.segment_key];\n if (segment) {\n return this.evaluateConditions(segment.conditions, context, ruleset);\n }\n }\n return false;\n default:\n console.warn(`Unknown operator: ${condition.operator}`);\n return false;\n }\n }\n\n /**\n * Serve a specific variation.\n */\n private serveVariation(\n flag: FlagConfig,\n variationId: string,\n reason: string\n ): EvaluationResult {\n const variation = flag.variations.find((v) => v.id === variationId);\n\n if (!variation) {\n return {\n value: null,\n variationKey: undefined,\n reason: 'variation_not_found',\n };\n }\n\n return {\n value: variation.value,\n variationKey: variation.key,\n reason,\n };\n }\n\n /**\n * Serve variation based on percentage rollout.\n */\n private servePercentageRollout(\n flag: FlagConfig,\n rollout: PercentageRollout,\n context: Context,\n reason: string\n ): EvaluationResult {\n const userId = context.userId || context.sessionId || 'anonymous';\n const bucket = this.getBucket(userId, flag.key || 'default'); // FIXED: Use flag.key for consistency\n\n let cumulative = 0;\n for (const item of rollout.variations) {\n cumulative += item.weight;\n if (bucket < cumulative) {\n return {\n value: item.variation.value,\n variationKey: item.variation.key,\n reason,\n };\n }\n }\n\n // Fallback to last variation\n const lastItem = rollout.variations[rollout.variations.length - 1];\n return {\n value: lastItem.variation.value,\n variationKey: lastItem.variation.key,\n reason,\n };\n }\n\n /**\n * Serve variation based on boolean rollout (simplified format: {on: percentage}).\n * Used for boolean flags where 'on' represents the percentage for 'true'.\n */\n private serveBooleanRollout(\n flag: FlagConfig,\n rollout: { on: number },\n context: Context,\n reason: string\n ): EvaluationResult {\n const userId = context.userId || context.sessionId || 'anonymous';\n const bucket = this.getBucket(userId, flag.key || 'default'); // FIXED: Use flag.key instead of flag.salt\n const percentage = rollout.on || 0;\n\n // Bucket is 0-9999, so we need to scale percentage (0-100) to 0-9999\n const threshold = (percentage / 100) * 10000;\n\n if (bucket < threshold) {\n // User is in the rollout, serve 'true' variation\n const trueVar = flag.variations.find(v => v.value === true);\n if (trueVar) {\n return {\n value: trueVar.value,\n variationKey: trueVar.key,\n reason,\n };\n }\n }\n\n // User is not in rollout, serve 'false' variation\n const falseVar = flag.variations.find(v => v.value === false);\n if (falseVar) {\n return {\n value: falseVar.value,\n variationKey: falseVar.key,\n reason,\n };\n }\n\n // Fallback\n return {\n value: false,\n variationKey: undefined,\n reason: 'rollout_fallback',\n };\n }\n\n /**\n * Get consistent bucket (0-9999) for user using MD5 hash.\n */\n /**\n * Get consistent bucket (0-9999) for user using simple hash (DJB2).\n * Format: flagKey:userId (matches Python SDK for cross-SDK consistency)\n */\n private getBucket(userId: string, flagKey: string): number {\n const input = `${flagKey}:${userId}`; // FIXED: Match Python SDK format\n let hash = 5381;\n for (let i = 0; i < input.length; i++) {\n hash = ((hash << 5) + hash) + input.charCodeAt(i); /* hash * 33 + c */\n }\n return Math.abs(hash) % 10000;\n }\n}\n","import { Evaluator } from './evaluator';\nimport { Context, Config, EvaluationResult, Ruleset } from './types';\nexport { Context, Config, EvaluationResult, Ruleset };\n\n/**\n * FluxControl JavaScript SDK (Thick Client)\n * \n * Downloads rulesets and evaluates flags locally.\n */\nexport class FluxClient {\n private sdkKey: string;\n private config: Config;\n private evaluator: Evaluator;\n private ruleset: Ruleset | null = null;\n private pollingInterval: any;\n\n constructor(sdkKey: string, config: Config) {\n if (!config.apiUrl) {\n throw new Error('[FluxClient] apiUrl is required. Please provide the FluxControl API endpoint (e.g., \"https://api.fluxcontrol.io/api/v1/sdk\")');\n }\n\n this.sdkKey = sdkKey;\n this.config = {\n environment: 'production',\n pollingIntervalMs: 60000,\n ...config\n };\n this.evaluator = new Evaluator();\n\n // Auto-init if in browser\n if (typeof window !== 'undefined') {\n this.init();\n }\n }\n\n /**\n * Initialize the SDK (fetch ruleset).\n */\n async init(): Promise<void> {\n await this.fetchRuleset();\n\n if (this.config.pollingIntervalMs && this.config.pollingIntervalMs > 0) {\n this.pollingInterval = setInterval(() => this.fetchRuleset(), this.config.pollingIntervalMs);\n }\n }\n\n /**\n * Fetch ruleset from API.\n */\n async fetchRuleset(): Promise<void> {\n try {\n const environment = this.config.environment || 'production';\n const url = `${this.config.apiUrl}/ruleset?environment=${environment}`;\n\n const res = await fetch(url, {\n headers: {\n 'Authorization': `Bearer ${this.sdkKey}`\n }\n });\n\n if (res.ok) {\n this.ruleset = await res.json();\n // Optional: Save to localStorage for offline boot\n if (typeof window !== 'undefined') {\n localStorage.setItem(`flux_ruleset_${this.sdkKey}`, JSON.stringify(this.ruleset));\n }\n } else {\n console.warn('[FluxClient] Failed to fetch ruleset:', res.status);\n }\n } catch (e) {\n console.error('[FluxClient] Error fetching ruleset:', e);\n // Try load from local storage\n if (typeof window !== 'undefined' && !this.ruleset) {\n const stored = localStorage.getItem(`flux_ruleset_${this.sdkKey}`);\n if (stored) this.ruleset = JSON.parse(stored);\n }\n }\n }\n\n /**\n * Evaluate a boolean flag.\n * Note: Sync evaluation now possible if ruleset is loaded!\n * But we keep async signature if needed or make it sync?\n * Legacy SDK was async. Let's make it sync but helpful.\n * Actually, if init() is async, we can't guarantee ruleset is there immediately.\n */\n boolVariation(flagKey: string, context: Context, defaultValue: boolean): boolean {\n const result = this.evaluate(flagKey, context, defaultValue);\n return !!result;\n }\n\n stringVariation(flagKey: string, context: Context, defaultValue: string): string {\n const result = this.evaluate(flagKey, context, defaultValue);\n return String(result);\n }\n\n numberVariation(flagKey: string, context: Context, defaultValue: number): number {\n const result = this.evaluate(flagKey, context, defaultValue);\n return Number(result);\n }\n\n jsonVariation<T = any>(flagKey: string, context: Context, defaultValue: T): T {\n return this.evaluate(flagKey, context, defaultValue) as T;\n }\n\n isEnabled(flagKey: string, contextOrId: Context | string, defaultValue: boolean = false): boolean {\n // Compatibility helper\n const context = typeof contextOrId === 'string' ? { userId: contextOrId } : contextOrId;\n return this.boolVariation(flagKey, context, defaultValue);\n }\n\n /**\n * Internal evaluation.\n */\n private evaluate(flagKey: string, context: Context, defaultValue: any): any {\n if (!this.ruleset) {\n console.warn(`[FluxClient] Ruleset not loaded. Returning default for ${flagKey}`);\n return defaultValue;\n }\n\n const result = this.evaluator.evaluate(flagKey, context, this.ruleset, defaultValue);\n\n // TODO: Queue exposure event logic here\n\n return result.value;\n }\n\n /**\n * Track a custom event.\n */\n async track(eventName: string, context: Context, properties?: Record<string, any>, value?: number): Promise<void> {\n try {\n await fetch(`${this.config.apiUrl}/events`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.sdkKey}`\n },\n body: JSON.stringify({\n events: [{\n type: 'custom',\n timestamp: new Date().toISOString(),\n eventName,\n context,\n properties,\n value\n }]\n })\n });\n } catch (e) {\n console.error('[FluxClient] Error tracking event:', e);\n }\n }\n\n close() {\n if (this.pollingInterval) clearInterval(this.pollingInterval);\n }\n}\n","/**\n * React hooks for FluxControl.\n * \n * Provides easy integration with React applications.\n */\n\nimport React, { createContext, useContext, useEffect, useState, ReactNode } from 'react';\nimport { FluxClient, Context as FluxContext } from './client';\n\ninterface FluxProviderProps {\n sdkKey: string;\n config?: any;\n context?: FluxContext;\n children: ReactNode;\n}\n\ninterface FluxContextType {\n client: FluxClient | null;\n context: FluxContext;\n}\n\nconst FluxReactContext = createContext<FluxContextType>({\n client: null,\n context: {}\n});\n\n/**\n * Provider component for FluxControl.\n * \n * Wrap your app with this to enable hooks.\n * \n * @example\n * <FluxProvider sdkKey=\"your-key\" context={{ userId: \"user-123\" }}>\n * <App />\n * </FluxProvider>\n */\nexport function FluxProvider({ sdkKey, config, context = {}, children }: FluxProviderProps) {\n const [client] = useState(() => new FluxClient(sdkKey, config));\n\n useEffect(() => {\n return () => {\n client.close();\n };\n }, [client]);\n\n return (\n <FluxReactContext.Provider value={{ client, context }}>\n {children}\n </FluxReactContext.Provider>\n );\n}\n\n/**\n * Hook to evaluate a feature flag.\n * \n * @example\n * const checkoutFlow = useFeature('checkout-flow', 'standard');\n * if (checkoutFlow === 'one-click') {\n * return <OneClickCheckout />;\n * }\n */\nexport function useFeature<T = any>(\n flagKey: string,\n defaultValue: T,\n contextOverride?: FluxContext\n): T {\n const { client, context: providerContext } = useContext(FluxReactContext);\n const [value, setValue] = useState<T>(defaultValue);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n if (!client) {\n setValue(defaultValue);\n setLoading(false);\n return;\n }\n\n const context = { ...providerContext, ...contextOverride };\n\n const evaluate = async () => {\n try {\n let result: any;\n\n // Determine type and call appropriate method\n if (typeof defaultValue === 'boolean') {\n result = await client.boolVariation(flagKey, context, defaultValue);\n } else if (typeof defaultValue === 'number') {\n result = await client.numberVariation(flagKey, context, defaultValue);\n } else if (typeof defaultValue === 'string') {\n result = await client.stringVariation(flagKey, context, defaultValue);\n } else {\n result = await client.jsonVariation(flagKey, context, defaultValue);\n }\n\n setValue(result as T);\n } catch (error) {\n console.error(`FluxControl: Error evaluating ${flagKey}:`, error);\n setValue(defaultValue);\n } finally {\n setLoading(false);\n }\n };\n\n evaluate();\n }, [client, flagKey, defaultValue, providerContext, contextOverride]);\n\n return value;\n}\n\n/**\n * Hook to get the FluxControl client instance.\n * \n * Use this for advanced scenarios like tracking custom events.\n * \n * @example\n * const client = useFluxClient();\n * client.track('button_clicked', context, { button: 'checkout' });\n */\nexport function useFluxClient(): FluxClient | null {\n const { client } = useContext(FluxReactContext);\n return client;\n}\n\n/**\n * Hook to track a custom event.\n * \n * @example\n * const trackPurchase = useTrack();\n * trackPurchase('purchase_completed', { productId: '123' }, 99.99);\n */\nexport function useTrack() {\n const { client, context } = useContext(FluxReactContext);\n\n return (\n eventName: string,\n properties?: Record<string, any>,\n value?: number,\n contextOverride?: FluxContext\n ) => {\n if (!client) {\n return;\n }\n\n const finalContext = { ...context, ...contextOverride };\n client.track(eventName, finalContext, properties, value);\n };\n}\n","/**\n * FluxControl JavaScript SDK\n * \n * @packageDocumentation\n */\n\nexport * from './types';\nexport * from './client';\nexport * from './react';\n\nimport { FluxClient } from './client';\nexport default FluxClient;\n"],"names":["createContext","useState","useEffect","useContext"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/evaluator.ts","../src/client.ts","../src/react.tsx","../src/index.ts"],"sourcesContent":["// import { createHash } from 'crypto';\nimport {\n Context,\n Ruleset,\n FlagConfig,\n TargetingRule,\n Condition,\n EvaluationResult,\n Variation,\n PercentageRollout,\n} from './types';\n\n/**\n * Evaluates feature flags against targeting rules.\n */\nexport class Evaluator {\n /**\n * Evaluate a flag.\n */\n evaluate(\n flagKey: string,\n context: Context,\n ruleset: Ruleset,\n defaultValue: any\n ): EvaluationResult {\n const flag = ruleset.flags[flagKey];\n\n if (!flag) {\n return {\n value: defaultValue,\n variationKey: undefined,\n reason: 'flag_not_found',\n };\n }\n\n // Evaluate targeting rules\n if (flag.rules) {\n // Sort by priority (lower numbers = higher priority, evaluated first)\n const sortedRules = [...flag.rules].sort((a: any, b: any) => {\n const priorityA = a.priority ?? 999;\n const priorityB = b.priority ?? 999;\n return priorityA - priorityB;\n });\n\n for (const rule of sortedRules) {\n if (this.evaluateConditions(rule.conditions, context, ruleset)) {\n // Rule matched\n if (rule.variationId) {\n return this.serveVariation(flag, rule.variationId, 'rule_match');\n } else if (rule.rollout) {\n // Handle boolean flag rollout format: {on: percentage}\n return this.serveBooleanRollout(\n flag,\n rule.rollout,\n context,\n 'percentage_rollout'\n );\n }\n }\n }\n }\n\n // No rules matched, return default variation\n return this.serveVariation(flag, flag.defaultVariationId, 'default');\n }\n\n /**\n * Evaluate all conditions in a rule.\n */\n private evaluateConditions(\n conditions: Condition[],\n context: Context,\n ruleset: Ruleset\n ): boolean {\n if (!conditions || conditions.length === 0) {\n return true;\n }\n\n return conditions.every((condition) =>\n this.evaluateCondition(condition, context, ruleset)\n );\n }\n\n /**\n * Evaluate a single condition.\n */\n private evaluateCondition(\n condition: Condition,\n context: Context,\n ruleset: Ruleset\n ): boolean {\n // Get attribute value from context\n let contextValue: any;\n if (condition.attribute === 'userId' || condition.attribute === 'user_id') {\n contextValue = context.userId;\n } else if (condition.attribute === 'sessionId') {\n contextValue = context.sessionId;\n } else {\n contextValue = context.attributes?.[condition.attribute];\n }\n\n switch (condition.operator) {\n case 'IS':\n return contextValue === condition.value;\n case 'IS_NOT':\n return contextValue !== condition.value;\n case 'CONTAINS':\n return String(contextValue).includes(String(condition.value));\n case 'DOES_NOT_CONTAIN':\n return !String(contextValue).includes(String(condition.value));\n case 'IN':\n // Check if contextValue is in the array of values\n if (Array.isArray(condition.value)) {\n return condition.value.map(String).includes(String(contextValue));\n }\n return String(contextValue) === String(condition.value);\n case 'ONE_OF':\n case 'IS_ONE_OF':\n return condition.values?.includes(contextValue) ?? false;\n case 'NOT_ONE_OF':\n return !condition.values?.includes(contextValue);\n case 'GREATER_THAN':\n return Number(contextValue) > Number(condition.value);\n case 'LESS_THAN':\n return Number(contextValue) < Number(condition.value);\n case 'IN_SEGMENT':\n if (condition.segment_key) {\n const segment = ruleset.segments[condition.segment_key];\n if (segment) {\n return this.evaluateConditions(segment.conditions, context, ruleset);\n }\n }\n return false;\n default:\n console.warn(`Unknown operator: ${condition.operator}`);\n return false;\n }\n }\n\n /**\n * Serve a specific variation.\n */\n private serveVariation(\n flag: FlagConfig,\n variationId: string,\n reason: string\n ): EvaluationResult {\n const variation = flag.variations.find((v) => v.id === variationId);\n\n if (!variation) {\n return {\n value: null,\n variationKey: undefined,\n reason: 'variation_not_found',\n };\n }\n\n return {\n value: variation.value,\n variationKey: variation.key,\n reason,\n };\n }\n\n /**\n * Serve variation based on percentage rollout.\n */\n private servePercentageRollout(\n flag: FlagConfig,\n rollout: PercentageRollout,\n context: Context,\n reason: string\n ): EvaluationResult {\n const userId = context.userId || context.sessionId || 'anonymous';\n const bucket = this.getBucket(userId, flag.key || 'default'); // FIXED: Use flag.key for consistency\n\n let cumulative = 0;\n for (const item of rollout.variations) {\n cumulative += item.weight;\n if (bucket < cumulative) {\n return {\n value: item.variation.value,\n variationKey: item.variation.key,\n reason,\n };\n }\n }\n\n // Fallback to last variation\n const lastItem = rollout.variations[rollout.variations.length - 1];\n return {\n value: lastItem.variation.value,\n variationKey: lastItem.variation.key,\n reason,\n };\n }\n\n /**\n * Serve variation based on boolean rollout (simplified format: {on: percentage}).\n * Used for boolean flags where 'on' represents the percentage for 'true'.\n */\n private serveBooleanRollout(\n flag: FlagConfig,\n rollout: { on: number },\n context: Context,\n reason: string\n ): EvaluationResult {\n const userId = context.userId || context.sessionId || 'anonymous';\n const bucket = this.getBucket(userId, flag.key || 'default'); // FIXED: Use flag.key instead of flag.salt\n const percentage = rollout.on || 0;\n\n // Bucket is 0-9999, so we need to scale percentage (0-100) to 0-9999\n const threshold = (percentage / 100) * 10000;\n\n if (bucket < threshold) {\n // User is in the rollout, serve 'true' variation\n const trueVar = flag.variations.find(v => v.value === true);\n if (trueVar) {\n return {\n value: trueVar.value,\n variationKey: trueVar.key,\n reason,\n };\n }\n }\n\n // User is not in rollout, serve 'false' variation\n const falseVar = flag.variations.find(v => v.value === false);\n if (falseVar) {\n return {\n value: falseVar.value,\n variationKey: falseVar.key,\n reason,\n };\n }\n\n // Fallback\n return {\n value: false,\n variationKey: undefined,\n reason: 'rollout_fallback',\n };\n }\n\n /**\n * Get consistent bucket (0-9999) for user using MD5 hash.\n */\n /**\n * Get consistent bucket (0-9999) for user using simple hash (DJB2).\n * Format: flagKey:userId (matches Python SDK for cross-SDK consistency)\n */\n private getBucket(userId: string, flagKey: string): number {\n const input = `${flagKey}:${userId}`; // FIXED: Match Python SDK format\n let hash = 5381;\n for (let i = 0; i < input.length; i++) {\n hash = ((hash << 5) + hash) + input.charCodeAt(i); /* hash * 33 + c */\n }\n return Math.abs(hash) % 10000;\n }\n}\n","import { Evaluator } from './evaluator';\nimport { Context, Config, EvaluationResult, Ruleset, EventType, EventCallback } from './types';\nexport { Context, Config, EvaluationResult, Ruleset };\n\n/**\n * FluxControl JavaScript SDK (Thick Client)\n * \n * Downloads rulesets and evaluates flags locally.\n */\nexport class FluxClient {\n private sdkKey: string;\n private config: Config;\n private evaluator: Evaluator;\n private ruleset: Ruleset | null = null;\n private pollingInterval: any;\n private listeners: Map<EventType, Set<EventCallback>> = new Map();\n private _isReady: boolean = false;\n\n constructor(sdkKey: string, config: Config) {\n if (!config.apiUrl) {\n throw new Error('[FluxClient] apiUrl is required. Please provide the FluxControl API endpoint (e.g., \"https://api.fluxcontrol.io/api/v1/sdk\")');\n }\n\n // Validation warnings (non-breaking)\n if (!sdkKey || sdkKey.trim() === '') {\n console.warn('[FluxClient] SDK key appears to be missing or invalid. API requests will fail.');\n }\n if (!config.apiUrl.endsWith('/api/v1/sdk')) {\n console.warn(`[FluxClient] API URL should end with '/api/v1/sdk'. Current: ${config.apiUrl}`);\n }\n\n this.sdkKey = sdkKey;\n this.config = {\n environment: 'production',\n pollingIntervalMs: 60000,\n ...config\n };\n this.evaluator = new Evaluator();\n\n // Auto-init if in browser\n if (typeof window !== 'undefined') {\n this.init();\n }\n }\n\n /**\n * Check if the SDK is ready (ruleset loaded).\n */\n get isReady(): boolean {\n return this._isReady;\n }\n\n /**\n * Subscribe to SDK events.\n * @param event - Event type ('ready', 'update', 'error')\n * @param callback - Function to call when event fires\n */\n on(event: EventType, callback: EventCallback): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(callback);\n }\n\n /**\n * Unsubscribe from SDK events.\n * @param event - Event type\n * @param callback - Function to remove\n */\n off(event: EventType, callback: EventCallback): void {\n const callbacks = this.listeners.get(event);\n if (callbacks) {\n callbacks.delete(callback);\n }\n }\n\n /**\n * Emit an event to all listeners.\n */\n private emit(event: EventType, data?: any): void {\n const callbacks = this.listeners.get(event);\n if (callbacks) {\n callbacks.forEach(callback => {\n try {\n callback(data);\n } catch (e) {\n console.error(`[FluxClient] Error in ${event} event listener:`, e);\n }\n });\n }\n }\n\n /**\n * Initialize the SDK (fetch ruleset).\n */\n async init(): Promise<void> {\n await this.fetchRuleset();\n\n if (this.config.pollingIntervalMs && this.config.pollingIntervalMs > 0) {\n this.pollingInterval = setInterval(() => this.fetchRuleset(), this.config.pollingIntervalMs);\n }\n }\n\n /**\n * Fetch ruleset from API.\n */\n async fetchRuleset(): Promise<void> {\n const isInitialFetch = !this._isReady;\n\n try {\n const environment = this.config.environment || 'production';\n const url = `${this.config.apiUrl}/ruleset?environment=${environment}`;\n\n const res = await fetch(url, {\n headers: {\n 'Authorization': `Bearer ${this.sdkKey}`\n }\n });\n\n if (res.ok) {\n this.ruleset = await res.json();\n\n // Mark as ready and emit appropriate event\n if (isInitialFetch) {\n this._isReady = true;\n this.emit('ready', this.ruleset);\n } else {\n this.emit('update', this.ruleset);\n }\n\n // Optional: Save to localStorage for offline boot\n if (typeof window !== 'undefined') {\n localStorage.setItem(`flux_ruleset_${this.sdkKey}`, JSON.stringify(this.ruleset));\n }\n } else {\n // Enhanced error messages with actionable guidance\n let errorMessage = `[FluxClient] Failed to fetch ruleset: ${res.status} ${res.statusText}`;\n\n switch (res.status) {\n case 401:\n errorMessage += ' - Unauthorized: Check your SDK Key';\n break;\n case 403:\n errorMessage += ' - Forbidden: Check SDK key permissions';\n break;\n case 404:\n errorMessage += ` - Not Found: Verify API URL ends with '/api/v1/sdk'. Current: ${this.config.apiUrl}`;\n break;\n case 500:\n case 502:\n case 503:\n errorMessage += ' - Server Error: FluxControl API may be down';\n break;\n }\n\n console.warn(errorMessage);\n this.emit('error', { status: res.status, message: errorMessage });\n }\n } catch (e) {\n const errorMessage = `[FluxClient] Error fetching ruleset from ${this.config.apiUrl}`;\n console.error(errorMessage, e);\n this.emit('error', { error: e, message: errorMessage });\n\n // Try load from local storage\n if (typeof window !== 'undefined' && !this.ruleset) {\n const stored = localStorage.getItem(`flux_ruleset_${this.sdkKey}`);\n if (stored) {\n this.ruleset = JSON.parse(stored);\n if (isInitialFetch) {\n this._isReady = true;\n this.emit('ready', this.ruleset);\n }\n }\n }\n }\n }\n\n /**\n * Evaluate a boolean flag.\n * Note: Sync evaluation now possible if ruleset is loaded!\n * But we keep async signature if needed or make it sync?\n * Legacy SDK was async. Let's make it sync but helpful.\n * Actually, if init() is async, we can't guarantee ruleset is there immediately.\n */\n boolVariation(flagKey: string, context: Context, defaultValue: boolean): boolean {\n const result = this.evaluate(flagKey, context, defaultValue);\n return !!result;\n }\n\n stringVariation(flagKey: string, context: Context, defaultValue: string): string {\n const result = this.evaluate(flagKey, context, defaultValue);\n return String(result);\n }\n\n numberVariation(flagKey: string, context: Context, defaultValue: number): number {\n const result = this.evaluate(flagKey, context, defaultValue);\n return Number(result);\n }\n\n jsonVariation<T = any>(flagKey: string, context: Context, defaultValue: T): T {\n return this.evaluate(flagKey, context, defaultValue) as T;\n }\n\n isEnabled(flagKey: string, contextOrId: Context | string, defaultValue: boolean = false): boolean {\n // Compatibility helper\n const context = typeof contextOrId === 'string' ? { userId: contextOrId } : contextOrId;\n return this.boolVariation(flagKey, context, defaultValue);\n }\n\n /**\n * Internal evaluation.\n */\n private evaluate(flagKey: string, context: Context, defaultValue: any): any {\n if (!this.ruleset) {\n console.warn(`[FluxClient] Ruleset not loaded. Returning default for ${flagKey}`);\n return defaultValue;\n }\n\n const result = this.evaluator.evaluate(flagKey, context, this.ruleset, defaultValue);\n\n // TODO: Queue exposure event logic here\n\n return result.value;\n }\n\n /**\n * Track a custom event.\n */\n async track(eventName: string, context: Context, properties?: Record<string, any>, value?: number): Promise<void> {\n try {\n await fetch(`${this.config.apiUrl}/events`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.sdkKey}`\n },\n body: JSON.stringify({\n events: [{\n type: 'custom',\n timestamp: new Date().toISOString(),\n eventName,\n context,\n properties,\n value\n }]\n })\n });\n } catch (e) {\n console.error('[FluxClient] Error tracking event:', e);\n }\n }\n\n close() {\n if (this.pollingInterval) clearInterval(this.pollingInterval);\n // Clear all event listeners\n this.listeners.clear();\n }\n}\n","/**\n * React hooks for FluxControl.\n * \n * Provides easy integration with React applications.\n */\n\nimport React, { createContext, useContext, useEffect, useState, ReactNode } from 'react';\nimport { FluxClient, Context as FluxContext } from './client';\n\ninterface FluxProviderProps {\n sdkKey: string;\n config?: any;\n context?: FluxContext;\n children: ReactNode;\n}\n\ninterface FluxContextType {\n client: FluxClient | null;\n context: FluxContext;\n}\n\nconst FluxReactContext = createContext<FluxContextType>({\n client: null,\n context: {}\n});\n\n/**\n * Provider component for FluxControl.\n * \n * Wrap your app with this to enable hooks.\n * \n * @example\n * <FluxProvider sdkKey=\"your-key\" context={{ userId: \"user-123\" }}>\n * <App />\n * </FluxProvider>\n */\nexport function FluxProvider({ sdkKey, config, context = {}, children }: FluxProviderProps) {\n // Validation warnings (non-breaking)\n useEffect(() => {\n if (!sdkKey || sdkKey.trim() === '') {\n console.warn(\"[FluxProvider] 'sdkKey' prop is required. Did you mean to use 'sdkKey' instead of 'apiKey'?\");\n }\n if (config && 'apiKey' in config) {\n console.warn(\"[FluxProvider] Found 'apiKey' in config. The SDK key should be passed as the 'sdkKey' prop, not in config.\");\n }\n }, [sdkKey, config]);\n\n const [client] = useState(() => new FluxClient(sdkKey, config));\n\n useEffect(() => {\n return () => {\n client.close();\n };\n }, [client]);\n\n return (\n <FluxReactContext.Provider value={{ client, context }}>\n {children}\n </FluxReactContext.Provider>\n );\n}\n\n/**\n * Hook to evaluate a feature flag.\n * \n * @example\n * const checkoutFlow = useFeature('checkout-flow', 'standard');\n * if (checkoutFlow === 'one-click') {\n * return <OneClickCheckout />;\n * }\n */\nexport function useFeature<T = any>(\n flagKey: string,\n defaultValue: T,\n contextOverride?: FluxContext\n): T {\n const { client, context: providerContext } = useContext(FluxReactContext);\n const [value, setValue] = useState<T>(defaultValue);\n\n useEffect(() => {\n if (!client) {\n setValue(defaultValue);\n return;\n }\n\n const context = { ...providerContext, ...contextOverride };\n\n const evaluate = () => {\n try {\n let result: any;\n\n // Determine type and call appropriate method (synchronous)\n if (typeof defaultValue === 'boolean') {\n result = client.boolVariation(flagKey, context, defaultValue);\n } else if (typeof defaultValue === 'number') {\n result = client.numberVariation(flagKey, context, defaultValue);\n } else if (typeof defaultValue === 'string') {\n result = client.stringVariation(flagKey, context, defaultValue);\n } else {\n result = client.jsonVariation(flagKey, context, defaultValue);\n }\n\n setValue(result as T);\n } catch (error) {\n console.error(`FluxControl: Error evaluating ${flagKey}:`, error);\n setValue(defaultValue);\n }\n };\n\n // Initial evaluation\n evaluate();\n\n // Subscribe to SDK events for automatic updates\n const onReady = () => evaluate();\n const onUpdate = () => evaluate();\n\n client.on('ready', onReady);\n client.on('update', onUpdate);\n\n // Cleanup event listeners\n return () => {\n client.off('ready', onReady);\n client.off('update', onUpdate);\n };\n }, [client, flagKey, defaultValue, providerContext, contextOverride]);\n\n return value;\n}\n\n/**\n * Hook to get the FluxControl client instance.\n * \n * Use this for advanced scenarios like tracking custom events.\n * \n * @example\n * const client = useFluxClient();\n * client.track('button_clicked', context, { button: 'checkout' });\n */\nexport function useFluxClient(): FluxClient | null {\n const { client } = useContext(FluxReactContext);\n return client;\n}\n\n/**\n * Hook to track a custom event.\n * \n * @example\n * const trackPurchase = useTrack();\n * trackPurchase('purchase_completed', { productId: '123' }, 99.99);\n */\nexport function useTrack() {\n const { client, context } = useContext(FluxReactContext);\n\n return (\n eventName: string,\n properties?: Record<string, any>,\n value?: number,\n contextOverride?: FluxContext\n ) => {\n if (!client) {\n return;\n }\n\n const finalContext = { ...context, ...contextOverride };\n client.track(eventName, finalContext, properties, value);\n };\n}\n","/**\n * FluxControl JavaScript SDK\n * \n * @packageDocumentation\n */\n\nexport * from './types';\nexport * from './client';\nexport * from './react';\n\nimport { FluxClient } from './client';\nexport default FluxClient;\n"],"names":["createContext","useEffect","useState","useContext"],"mappings":"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"}
|
package/dist/react.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,EAAkD,SAAS,EAAE,MAAM,OAAO,CAAC;AACzF,OAAO,EAAE,UAAU,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,UAAU,CAAC;AAE9D,UAAU,iBAAiB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,QAAQ,EAAE,SAAS,CAAC;CACvB;AAYD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAY,EAAE,QAAQ,EAAE,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,EAAkD,SAAS,EAAE,MAAM,OAAO,CAAC;AACzF,OAAO,EAAE,UAAU,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,UAAU,CAAC;AAE9D,UAAU,iBAAiB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,QAAQ,EAAE,SAAS,CAAC;CACvB;AAYD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAY,EAAE,QAAQ,EAAE,EAAE,iBAAiB,qBAwBzF;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,CAAC,GAAG,GAAG,EAC9B,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,CAAC,EACf,eAAe,CAAC,EAAE,WAAW,GAC9B,CAAC,CAoDH;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,IAAI,UAAU,GAAG,IAAI,CAGjD;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,KAIhB,WAAW,MAAM,EACjB,aAAa,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAChC,QAAQ,MAAM,EACd,kBAAkB,WAAW,UASpC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -94,4 +94,12 @@ export interface Event {
|
|
|
94
94
|
properties?: Record<string, any>;
|
|
95
95
|
value?: number;
|
|
96
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Event types emitted by FluxClient.
|
|
99
|
+
*/
|
|
100
|
+
export type EventType = 'ready' | 'update' | 'error';
|
|
101
|
+
/**
|
|
102
|
+
* Callback function for SDK events.
|
|
103
|
+
*/
|
|
104
|
+
export type EventCallback = (data?: any) => void;
|
|
97
105
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,OAAO;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,MAAM;IACnB,6FAA6F;IAC7F,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC7B,KAAK,EAAE,GAAG,CAAC;IACX,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,UAAU;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,GAAG,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5B;AAED,MAAM,WAAW,SAAS;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAC9B,UAAU,EAAE,KAAK,CAAC;QACd,SAAS,EAAE,SAAS,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;CACN;AAED,MAAM,WAAW,aAAa;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,SAAS,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,KAAK;IAClB,IAAI,EAAE,UAAU,GAAG,QAAQ,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,GAAG,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluxcontrolsdk/js-sdk",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "FluxControl JavaScript SDK for feature flags and experimentation",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|
|
@@ -63,4 +63,4 @@
|
|
|
63
63
|
"optional": true
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
|
-
}
|
|
66
|
+
}
|