@fluxcontrolsdk/js-sdk 0.1.2 → 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 +151 -7
- package/dist/client.d.ts +24 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/index.esm.js +128 -21
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +127 -20
- package/dist/index.js.map +1 -1
- package/dist/react.d.ts +2 -2
- package/dist/react.d.ts.map +1 -1
- package/dist/types.d.ts +12 -1
- 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
|
|
@@ -17,10 +73,10 @@ yarn add @fluxcontrolsdk/js-sdk
|
|
|
17
73
|
```typescript
|
|
18
74
|
import { FluxClient } from '@fluxcontrolsdk/js-sdk';
|
|
19
75
|
|
|
20
|
-
// Initialize client
|
|
76
|
+
// Initialize client (apiUrl is required)
|
|
21
77
|
const client = new FluxClient('your-sdk-key', {
|
|
22
|
-
|
|
23
|
-
|
|
78
|
+
apiUrl: 'https://api.fluxcontrol.io/api/v1/sdk', // Required: FluxControl API endpoint
|
|
79
|
+
pollingIntervalMs: 60000 // Optional: polling interval in ms
|
|
24
80
|
});
|
|
25
81
|
|
|
26
82
|
// Create context
|
|
@@ -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
|
|
@@ -101,10 +159,13 @@ new FluxClient(sdkKey: string, config?: Config)
|
|
|
101
159
|
```
|
|
102
160
|
|
|
103
161
|
**Config Options:**
|
|
104
|
-
- `
|
|
105
|
-
- `
|
|
106
|
-
- `
|
|
107
|
-
- `
|
|
162
|
+
- `apiUrl` - **Required**. FluxControl API endpoint URL (e.g., `'https://api.fluxcontrol.io/api/v1/sdk'`)
|
|
163
|
+
- `pollingIntervalMs` - Polling interval in milliseconds (default: 60000)
|
|
164
|
+
- `eventFlushIntervalMs` - Event flush interval in milliseconds (optional)
|
|
165
|
+
- `eventQueueSize` - Maximum event queue size (optional)
|
|
166
|
+
- `offlineMode` - Enable offline mode (default: false)
|
|
167
|
+
- `cacheFilePath` - Path for local cache file (optional)
|
|
168
|
+
- `timeout` - Request timeout in milliseconds (optional)
|
|
108
169
|
|
|
109
170
|
#### Methods
|
|
110
171
|
|
|
@@ -247,6 +308,88 @@ function Experiment() {
|
|
|
247
308
|
}
|
|
248
309
|
```
|
|
249
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
|
+
|
|
250
393
|
## Browser Support
|
|
251
394
|
|
|
252
395
|
- Chrome/Edge: Latest 2 versions
|
|
@@ -255,6 +398,7 @@ function Experiment() {
|
|
|
255
398
|
- iOS Safari: iOS 12+
|
|
256
399
|
- Android Chrome: Latest 2 versions
|
|
257
400
|
|
|
401
|
+
|
|
258
402
|
## License
|
|
259
403
|
|
|
260
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
|
-
|
|
14
|
+
private listeners;
|
|
15
|
+
private _isReady;
|
|
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.
|
|
@@ -204,12 +204,24 @@ class Evaluator {
|
|
|
204
204
|
* Downloads rulesets and evaluates flags locally.
|
|
205
205
|
*/
|
|
206
206
|
class FluxClient {
|
|
207
|
-
constructor(sdkKey, config
|
|
207
|
+
constructor(sdkKey, config) {
|
|
208
208
|
this.ruleset = null;
|
|
209
|
+
this.listeners = new Map();
|
|
210
|
+
this._isReady = false;
|
|
211
|
+
if (!config.apiUrl) {
|
|
212
|
+
throw new Error('[FluxClient] apiUrl is required. Please provide the FluxControl API endpoint (e.g., "https://api.fluxcontrol.io/api/v1/sdk")');
|
|
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
|
+
}
|
|
209
221
|
this.sdkKey = sdkKey;
|
|
210
222
|
this.config = {
|
|
211
|
-
|
|
212
|
-
pollingIntervalMs:
|
|
223
|
+
environment: 'production',
|
|
224
|
+
pollingIntervalMs: 60000,
|
|
213
225
|
...config
|
|
214
226
|
};
|
|
215
227
|
this.evaluator = new Evaluator();
|
|
@@ -218,6 +230,50 @@ class FluxClient {
|
|
|
218
230
|
this.init();
|
|
219
231
|
}
|
|
220
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
|
+
}
|
|
221
277
|
/**
|
|
222
278
|
* Initialize the SDK (fetch ruleset).
|
|
223
279
|
*/
|
|
@@ -231,10 +287,10 @@ class FluxClient {
|
|
|
231
287
|
* Fetch ruleset from API.
|
|
232
288
|
*/
|
|
233
289
|
async fetchRuleset() {
|
|
290
|
+
const isInitialFetch = !this._isReady;
|
|
234
291
|
try {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const url = `${this.config.apiUrl}/ruleset?environment=production`;
|
|
292
|
+
const environment = this.config.environment || 'production';
|
|
293
|
+
const url = `${this.config.apiUrl}/ruleset?environment=${environment}`;
|
|
238
294
|
const res = await fetch(url, {
|
|
239
295
|
headers: {
|
|
240
296
|
'Authorization': `Bearer ${this.sdkKey}`
|
|
@@ -242,22 +298,56 @@ class FluxClient {
|
|
|
242
298
|
});
|
|
243
299
|
if (res.ok) {
|
|
244
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
|
+
}
|
|
245
309
|
// Optional: Save to localStorage for offline boot
|
|
246
310
|
if (typeof window !== 'undefined') {
|
|
247
311
|
localStorage.setItem(`flux_ruleset_${this.sdkKey}`, JSON.stringify(this.ruleset));
|
|
248
312
|
}
|
|
249
313
|
}
|
|
250
314
|
else {
|
|
251
|
-
|
|
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 });
|
|
252
335
|
}
|
|
253
336
|
}
|
|
254
337
|
catch (e) {
|
|
255
|
-
|
|
338
|
+
const errorMessage = `[FluxClient] Error fetching ruleset from ${this.config.apiUrl}`;
|
|
339
|
+
console.error(errorMessage, e);
|
|
340
|
+
this.emit('error', { error: e, message: errorMessage });
|
|
256
341
|
// Try load from local storage
|
|
257
342
|
if (typeof window !== 'undefined' && !this.ruleset) {
|
|
258
343
|
const stored = localStorage.getItem(`flux_ruleset_${this.sdkKey}`);
|
|
259
|
-
if (stored)
|
|
344
|
+
if (stored) {
|
|
260
345
|
this.ruleset = JSON.parse(stored);
|
|
346
|
+
if (isInitialFetch) {
|
|
347
|
+
this._isReady = true;
|
|
348
|
+
this.emit('ready', this.ruleset);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
261
351
|
}
|
|
262
352
|
}
|
|
263
353
|
}
|
|
@@ -330,6 +420,8 @@ class FluxClient {
|
|
|
330
420
|
close() {
|
|
331
421
|
if (this.pollingInterval)
|
|
332
422
|
clearInterval(this.pollingInterval);
|
|
423
|
+
// Clear all event listeners
|
|
424
|
+
this.listeners.clear();
|
|
333
425
|
}
|
|
334
426
|
}
|
|
335
427
|
|
|
@@ -353,6 +445,15 @@ const FluxReactContext = createContext({
|
|
|
353
445
|
* </FluxProvider>
|
|
354
446
|
*/
|
|
355
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]);
|
|
356
457
|
const [client] = useState(() => new FluxClient(sdkKey, config));
|
|
357
458
|
useEffect(() => {
|
|
358
459
|
return () => {
|
|
@@ -373,29 +474,27 @@ function FluxProvider({ sdkKey, config, context = {}, children }) {
|
|
|
373
474
|
function useFeature(flagKey, defaultValue, contextOverride) {
|
|
374
475
|
const { client, context: providerContext } = useContext(FluxReactContext);
|
|
375
476
|
const [value, setValue] = useState(defaultValue);
|
|
376
|
-
const [loading, setLoading] = useState(true);
|
|
377
477
|
useEffect(() => {
|
|
378
478
|
if (!client) {
|
|
379
479
|
setValue(defaultValue);
|
|
380
|
-
setLoading(false);
|
|
381
480
|
return;
|
|
382
481
|
}
|
|
383
482
|
const context = { ...providerContext, ...contextOverride };
|
|
384
|
-
const evaluate =
|
|
483
|
+
const evaluate = () => {
|
|
385
484
|
try {
|
|
386
485
|
let result;
|
|
387
|
-
// Determine type and call appropriate method
|
|
486
|
+
// Determine type and call appropriate method (synchronous)
|
|
388
487
|
if (typeof defaultValue === 'boolean') {
|
|
389
|
-
result =
|
|
488
|
+
result = client.boolVariation(flagKey, context, defaultValue);
|
|
390
489
|
}
|
|
391
490
|
else if (typeof defaultValue === 'number') {
|
|
392
|
-
result =
|
|
491
|
+
result = client.numberVariation(flagKey, context, defaultValue);
|
|
393
492
|
}
|
|
394
493
|
else if (typeof defaultValue === 'string') {
|
|
395
|
-
result =
|
|
494
|
+
result = client.stringVariation(flagKey, context, defaultValue);
|
|
396
495
|
}
|
|
397
496
|
else {
|
|
398
|
-
result =
|
|
497
|
+
result = client.jsonVariation(flagKey, context, defaultValue);
|
|
399
498
|
}
|
|
400
499
|
setValue(result);
|
|
401
500
|
}
|
|
@@ -403,11 +502,19 @@ function useFeature(flagKey, defaultValue, contextOverride) {
|
|
|
403
502
|
console.error(`FluxControl: Error evaluating ${flagKey}:`, error);
|
|
404
503
|
setValue(defaultValue);
|
|
405
504
|
}
|
|
406
|
-
finally {
|
|
407
|
-
setLoading(false);
|
|
408
|
-
}
|
|
409
505
|
};
|
|
506
|
+
// Initial evaluation
|
|
410
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
|
+
};
|
|
411
518
|
}, [client, flagKey, defaultValue, providerContext, contextOverride]);
|
|
412
519
|
return value;
|
|
413
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 this.sdkKey = sdkKey;\n this.config = {\n apiUrl: config.apiUrl || 'http://localhost:8000/api/v1/sdk',\n pollingIntervalMs: config.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 // Environment context is usually passed or inferred.\n // For now, hardcode or assume API defaults.\n const url = `${this.config.apiUrl}/ruleset?environment=production`;\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
|
@@ -208,12 +208,24 @@
|
|
|
208
208
|
* Downloads rulesets and evaluates flags locally.
|
|
209
209
|
*/
|
|
210
210
|
class FluxClient {
|
|
211
|
-
constructor(sdkKey, config
|
|
211
|
+
constructor(sdkKey, config) {
|
|
212
212
|
this.ruleset = null;
|
|
213
|
+
this.listeners = new Map();
|
|
214
|
+
this._isReady = false;
|
|
215
|
+
if (!config.apiUrl) {
|
|
216
|
+
throw new Error('[FluxClient] apiUrl is required. Please provide the FluxControl API endpoint (e.g., "https://api.fluxcontrol.io/api/v1/sdk")');
|
|
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
|
+
}
|
|
213
225
|
this.sdkKey = sdkKey;
|
|
214
226
|
this.config = {
|
|
215
|
-
|
|
216
|
-
pollingIntervalMs:
|
|
227
|
+
environment: 'production',
|
|
228
|
+
pollingIntervalMs: 60000,
|
|
217
229
|
...config
|
|
218
230
|
};
|
|
219
231
|
this.evaluator = new Evaluator();
|
|
@@ -222,6 +234,50 @@
|
|
|
222
234
|
this.init();
|
|
223
235
|
}
|
|
224
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
|
+
}
|
|
225
281
|
/**
|
|
226
282
|
* Initialize the SDK (fetch ruleset).
|
|
227
283
|
*/
|
|
@@ -235,10 +291,10 @@
|
|
|
235
291
|
* Fetch ruleset from API.
|
|
236
292
|
*/
|
|
237
293
|
async fetchRuleset() {
|
|
294
|
+
const isInitialFetch = !this._isReady;
|
|
238
295
|
try {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const url = `${this.config.apiUrl}/ruleset?environment=production`;
|
|
296
|
+
const environment = this.config.environment || 'production';
|
|
297
|
+
const url = `${this.config.apiUrl}/ruleset?environment=${environment}`;
|
|
242
298
|
const res = await fetch(url, {
|
|
243
299
|
headers: {
|
|
244
300
|
'Authorization': `Bearer ${this.sdkKey}`
|
|
@@ -246,22 +302,56 @@
|
|
|
246
302
|
});
|
|
247
303
|
if (res.ok) {
|
|
248
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
|
+
}
|
|
249
313
|
// Optional: Save to localStorage for offline boot
|
|
250
314
|
if (typeof window !== 'undefined') {
|
|
251
315
|
localStorage.setItem(`flux_ruleset_${this.sdkKey}`, JSON.stringify(this.ruleset));
|
|
252
316
|
}
|
|
253
317
|
}
|
|
254
318
|
else {
|
|
255
|
-
|
|
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 });
|
|
256
339
|
}
|
|
257
340
|
}
|
|
258
341
|
catch (e) {
|
|
259
|
-
|
|
342
|
+
const errorMessage = `[FluxClient] Error fetching ruleset from ${this.config.apiUrl}`;
|
|
343
|
+
console.error(errorMessage, e);
|
|
344
|
+
this.emit('error', { error: e, message: errorMessage });
|
|
260
345
|
// Try load from local storage
|
|
261
346
|
if (typeof window !== 'undefined' && !this.ruleset) {
|
|
262
347
|
const stored = localStorage.getItem(`flux_ruleset_${this.sdkKey}`);
|
|
263
|
-
if (stored)
|
|
348
|
+
if (stored) {
|
|
264
349
|
this.ruleset = JSON.parse(stored);
|
|
350
|
+
if (isInitialFetch) {
|
|
351
|
+
this._isReady = true;
|
|
352
|
+
this.emit('ready', this.ruleset);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
265
355
|
}
|
|
266
356
|
}
|
|
267
357
|
}
|
|
@@ -334,6 +424,8 @@
|
|
|
334
424
|
close() {
|
|
335
425
|
if (this.pollingInterval)
|
|
336
426
|
clearInterval(this.pollingInterval);
|
|
427
|
+
// Clear all event listeners
|
|
428
|
+
this.listeners.clear();
|
|
337
429
|
}
|
|
338
430
|
}
|
|
339
431
|
|
|
@@ -357,6 +449,15 @@
|
|
|
357
449
|
* </FluxProvider>
|
|
358
450
|
*/
|
|
359
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]);
|
|
360
461
|
const [client] = React.useState(() => new FluxClient(sdkKey, config));
|
|
361
462
|
React.useEffect(() => {
|
|
362
463
|
return () => {
|
|
@@ -377,29 +478,27 @@
|
|
|
377
478
|
function useFeature(flagKey, defaultValue, contextOverride) {
|
|
378
479
|
const { client, context: providerContext } = React.useContext(FluxReactContext);
|
|
379
480
|
const [value, setValue] = React.useState(defaultValue);
|
|
380
|
-
const [loading, setLoading] = React.useState(true);
|
|
381
481
|
React.useEffect(() => {
|
|
382
482
|
if (!client) {
|
|
383
483
|
setValue(defaultValue);
|
|
384
|
-
setLoading(false);
|
|
385
484
|
return;
|
|
386
485
|
}
|
|
387
486
|
const context = { ...providerContext, ...contextOverride };
|
|
388
|
-
const evaluate =
|
|
487
|
+
const evaluate = () => {
|
|
389
488
|
try {
|
|
390
489
|
let result;
|
|
391
|
-
// Determine type and call appropriate method
|
|
490
|
+
// Determine type and call appropriate method (synchronous)
|
|
392
491
|
if (typeof defaultValue === 'boolean') {
|
|
393
|
-
result =
|
|
492
|
+
result = client.boolVariation(flagKey, context, defaultValue);
|
|
394
493
|
}
|
|
395
494
|
else if (typeof defaultValue === 'number') {
|
|
396
|
-
result =
|
|
495
|
+
result = client.numberVariation(flagKey, context, defaultValue);
|
|
397
496
|
}
|
|
398
497
|
else if (typeof defaultValue === 'string') {
|
|
399
|
-
result =
|
|
498
|
+
result = client.stringVariation(flagKey, context, defaultValue);
|
|
400
499
|
}
|
|
401
500
|
else {
|
|
402
|
-
result =
|
|
501
|
+
result = client.jsonVariation(flagKey, context, defaultValue);
|
|
403
502
|
}
|
|
404
503
|
setValue(result);
|
|
405
504
|
}
|
|
@@ -407,11 +506,19 @@
|
|
|
407
506
|
console.error(`FluxControl: Error evaluating ${flagKey}:`, error);
|
|
408
507
|
setValue(defaultValue);
|
|
409
508
|
}
|
|
410
|
-
finally {
|
|
411
|
-
setLoading(false);
|
|
412
|
-
}
|
|
413
509
|
};
|
|
510
|
+
// Initial evaluation
|
|
414
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
|
+
};
|
|
415
522
|
}, [client, flagKey, defaultValue, providerContext, contextOverride]);
|
|
416
523
|
return value;
|
|
417
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 this.sdkKey = sdkKey;\n this.config = {\n apiUrl: config.apiUrl || 'http://localhost:8000/api/v1/sdk',\n pollingIntervalMs: config.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 // Environment context is usually passed or inferred.\n // For now, hardcode or assume API defaults.\n const url = `${this.config.apiUrl}/ruleset?environment=production`;\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":";;;;;;IAYA;;IAEG;UACU,SAAS,CAAA;IAClB;;IAEG;IACH,IAAA,QAAQ,CACJ,OAAe,EACf,OAAgB,EAChB,OAAgB,EAChB,YAAiB,EAAA;YAEjB,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;YAEnC,IAAI,CAAC,IAAI,EAAE;gBACP,OAAO;IACH,gBAAA,KAAK,EAAE,YAAY;IACnB,gBAAA,YAAY,EAAE,SAAS;IACvB,gBAAA,MAAM,EAAE,gBAAgB;iBAC3B;YACL;;IAGA,QAAA,IAAI,IAAI,CAAC,KAAK,EAAE;;IAEZ,YAAA,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAM,EAAE,CAAM,KAAI;IACxD,gBAAA,MAAM,SAAS,GAAG,CAAC,CAAC,QAAQ,IAAI,GAAG;IACnC,gBAAA,MAAM,SAAS,GAAG,CAAC,CAAC,QAAQ,IAAI,GAAG;oBACnC,OAAO,SAAS,GAAG,SAAS;IAChC,YAAA,CAAC,CAAC;IAEF,YAAA,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE;IAC5B,gBAAA,IAAI,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;;IAE5D,oBAAA,IAAI,IAAI,CAAC,WAAW,EAAE;IAClB,wBAAA,OAAO,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC;wBACpE;IAAO,yBAAA,IAAI,IAAI,CAAC,OAAO,EAAE;;IAErB,wBAAA,OAAO,IAAI,CAAC,mBAAmB,CAC3B,IAAI,EACJ,IAAI,CAAC,OAAO,EACZ,OAAO,EACP,oBAAoB,CACvB;wBACL;oBACJ;gBACJ;YACJ;;IAGA,QAAA,OAAO,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,CAAC;QACxE;IAEA;;IAEG;IACK,IAAA,kBAAkB,CACtB,UAAuB,EACvB,OAAgB,EAChB,OAAgB,EAAA;YAEhB,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE;IACxC,YAAA,OAAO,IAAI;YACf;YAEA,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,SAAS,KAC9B,IAAI,CAAC,iBAAiB,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,CACtD;QACL;IAEA;;IAEG;IACK,IAAA,iBAAiB,CACrB,SAAoB,EACpB,OAAgB,EAChB,OAAgB,EAAA;;IAGhB,QAAA,IAAI,YAAiB;IACrB,QAAA,IAAI,SAAS,CAAC,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,SAAS,KAAK,SAAS,EAAE;IACvE,YAAA,YAAY,GAAG,OAAO,CAAC,MAAM;YACjC;IAAO,aAAA,IAAI,SAAS,CAAC,SAAS,KAAK,WAAW,EAAE;IAC5C,YAAA,YAAY,GAAG,OAAO,CAAC,SAAS;YACpC;iBAAO;gBACH,YAAY,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,SAAS,CAAC;YAC5D;IAEA,QAAA,QAAQ,SAAS,CAAC,QAAQ;IACtB,YAAA,KAAK,IAAI;IACL,gBAAA,OAAO,YAAY,KAAK,SAAS,CAAC,KAAK;IAC3C,YAAA,KAAK,QAAQ;IACT,gBAAA,OAAO,YAAY,KAAK,SAAS,CAAC,KAAK;IAC3C,YAAA,KAAK,UAAU;IACX,gBAAA,OAAO,MAAM,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACjE,YAAA,KAAK,kBAAkB;IACnB,gBAAA,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAClE,YAAA,KAAK,IAAI;;oBAEL,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE;IAChC,oBAAA,OAAO,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;oBACrE;oBACA,OAAO,MAAM,CAAC,YAAY,CAAC,KAAK,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC;IAC3D,YAAA,KAAK,QAAQ;IACb,YAAA,KAAK,WAAW;oBACZ,OAAO,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,IAAI,KAAK;IAC5D,YAAA,KAAK,YAAY;oBACb,OAAO,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC;IACpD,YAAA,KAAK,cAAc;oBACf,OAAO,MAAM,CAAC,YAAY,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC;IACzD,YAAA,KAAK,WAAW;oBACZ,OAAO,MAAM,CAAC,YAAY,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC;IACzD,YAAA,KAAK,YAAY;IACb,gBAAA,IAAI,SAAS,CAAC,WAAW,EAAE;wBACvB,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,WAAW,CAAC;wBACvD,IAAI,OAAO,EAAE;IACT,wBAAA,OAAO,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC;wBACxE;oBACJ;IACA,gBAAA,OAAO,KAAK;IAChB,YAAA;oBACI,OAAO,CAAC,IAAI,CAAC,CAAA,kBAAA,EAAqB,SAAS,CAAC,QAAQ,CAAA,CAAE,CAAC;IACvD,gBAAA,OAAO,KAAK;;QAExB;IAEA;;IAEG;IACK,IAAA,cAAc,CAClB,IAAgB,EAChB,WAAmB,EACnB,MAAc,EAAA;IAEd,QAAA,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,WAAW,CAAC;YAEnE,IAAI,CAAC,SAAS,EAAE;gBACZ,OAAO;IACH,gBAAA,KAAK,EAAE,IAAI;IACX,gBAAA,YAAY,EAAE,SAAS;IACvB,gBAAA,MAAM,EAAE,qBAAqB;iBAChC;YACL;YAEA,OAAO;gBACH,KAAK,EAAE,SAAS,CAAC,KAAK;gBACtB,YAAY,EAAE,SAAS,CAAC,GAAG;gBAC3B,MAAM;aACT;QACL;IAEA;;IAEG;IACK,IAAA,sBAAsB,CAC1B,IAAgB,EAChB,OAA0B,EAC1B,OAAgB,EAChB,MAAc,EAAA;YAEd,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,SAAS,IAAI,WAAW;IACjE,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,IAAI,SAAS,CAAC,CAAC;YAE7D,IAAI,UAAU,GAAG,CAAC;IAClB,QAAA,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,UAAU,EAAE;IACnC,YAAA,UAAU,IAAI,IAAI,CAAC,MAAM;IACzB,YAAA,IAAI,MAAM,GAAG,UAAU,EAAE;oBACrB,OAAO;IACH,oBAAA,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK;IAC3B,oBAAA,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG;wBAChC,MAAM;qBACT;gBACL;YACJ;;IAGA,QAAA,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;YAClE,OAAO;IACH,YAAA,KAAK,EAAE,QAAQ,CAAC,SAAS,CAAC,KAAK;IAC/B,YAAA,YAAY,EAAE,QAAQ,CAAC,SAAS,CAAC,GAAG;gBACpC,MAAM;aACT;QACL;IAEA;;;IAGG;IACK,IAAA,mBAAmB,CACvB,IAAgB,EAChB,OAAuB,EACvB,OAAgB,EAChB,MAAc,EAAA;YAEd,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,SAAS,IAAI,WAAW;IACjE,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,IAAI,SAAS,CAAC,CAAC;IAC7D,QAAA,MAAM,UAAU,GAAG,OAAO,CAAC,EAAE,IAAI,CAAC;;YAGlC,MAAM,SAAS,GAAG,CAAC,UAAU,GAAG,GAAG,IAAI,KAAK;IAE5C,QAAA,IAAI,MAAM,GAAG,SAAS,EAAE;;IAEpB,YAAA,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC;gBAC3D,IAAI,OAAO,EAAE;oBACT,OAAO;wBACH,KAAK,EAAE,OAAO,CAAC,KAAK;wBACpB,YAAY,EAAE,OAAO,CAAC,GAAG;wBACzB,MAAM;qBACT;gBACL;YACJ;;IAGA,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC;YAC7D,IAAI,QAAQ,EAAE;gBACV,OAAO;oBACH,KAAK,EAAE,QAAQ,CAAC,KAAK;oBACrB,YAAY,EAAE,QAAQ,CAAC,GAAG;oBAC1B,MAAM;iBACT;YACL;;YAGA,OAAO;IACH,YAAA,KAAK,EAAE,KAAK;IACZ,YAAA,YAAY,EAAE,SAAS;IACvB,YAAA,MAAM,EAAE,kBAAkB;aAC7B;QACL;IAEA;;IAEG;IACH;;;IAGG;QACK,SAAS,CAAC,MAAc,EAAE,OAAe,EAAA;YAC7C,MAAM,KAAK,GAAG,CAAA,EAAG,OAAO,IAAI,MAAM,CAAA,CAAE,CAAC;YACrC,IAAI,IAAI,GAAG,IAAI;IACf,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;IACnC,YAAA,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YACtD;YACA,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK;QACjC;IACH;;IC/PD;;;;IAIG;UACU,UAAU,CAAA;QASnB,WAAA,CAAY,MAAc,EAAE,MAAc,EAAA;YALlC,IAAA,CAAA,OAAO,GAAmB,IAAI;IAE9B,QAAA,IAAA,CAAA,SAAS,GAAuC,IAAI,GAAG,EAAE;YACzD,IAAA,CAAA,QAAQ,GAAY,KAAK;IAG7B,QAAA,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;IAChB,YAAA,MAAM,IAAI,KAAK,CAAC,8HAA8H,CAAC;YACnJ;;YAGA,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;IACjC,YAAA,OAAO,CAAC,IAAI,CAAC,gFAAgF,CAAC;YAClG;YACA,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE;gBACxC,OAAO,CAAC,IAAI,CAAC,CAAA,6DAAA,EAAgE,MAAM,CAAC,MAAM,CAAA,CAAE,CAAC;YACjG;IAEA,QAAA,IAAI,CAAC,MAAM,GAAG,MAAM;YACpB,IAAI,CAAC,MAAM,GAAG;IACV,YAAA,WAAW,EAAE,YAAY;IACzB,YAAA,iBAAiB,EAAE,KAAK;IACxB,YAAA,GAAG;aACN;IACD,QAAA,IAAI,CAAC,SAAS,GAAG,IAAI,SAAS,EAAE;;IAGhC,QAAA,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;gBAC/B,IAAI,CAAC,IAAI,EAAE;YACf;QACJ;IAEA;;IAEG;IACH,IAAA,IAAI,OAAO,GAAA;YACP,OAAO,IAAI,CAAC,QAAQ;QACxB;IAEA;;;;IAIG;QACH,EAAE,CAAC,KAAgB,EAAE,QAAuB,EAAA;YACxC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;gBAC5B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,EAAE,CAAC;YACxC;IACA,QAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC5C;IAEA;;;;IAIG;QACH,GAAG,CAAC,KAAgB,EAAE,QAAuB,EAAA;YACzC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;YAC3C,IAAI,SAAS,EAAE;IACX,YAAA,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC;YAC9B;QACJ;IAEA;;IAEG;QACK,IAAI,CAAC,KAAgB,EAAE,IAAU,EAAA;YACrC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;YAC3C,IAAI,SAAS,EAAE;IACX,YAAA,SAAS,CAAC,OAAO,CAAC,QAAQ,IAAG;IACzB,gBAAA,IAAI;wBACA,QAAQ,CAAC,IAAI,CAAC;oBAClB;oBAAE,OAAO,CAAC,EAAE;wBACR,OAAO,CAAC,KAAK,CAAC,CAAA,sBAAA,EAAyB,KAAK,CAAA,gBAAA,CAAkB,EAAE,CAAC,CAAC;oBACtE;IACJ,YAAA,CAAC,CAAC;YACN;QACJ;IAEA;;IAEG;IACH,IAAA,MAAM,IAAI,GAAA;IACN,QAAA,MAAM,IAAI,CAAC,YAAY,EAAE;IAEzB,QAAA,IAAI,IAAI,CAAC,MAAM,CAAC,iBAAiB,IAAI,IAAI,CAAC,MAAM,CAAC,iBAAiB,GAAG,CAAC,EAAE;IACpE,YAAA,IAAI,CAAC,eAAe,GAAG,WAAW,CAAC,MAAM,IAAI,CAAC,YAAY,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC;YAChG;QACJ;IAEA;;IAEG;IACH,IAAA,MAAM,YAAY,GAAA;IACd,QAAA,MAAM,cAAc,GAAG,CAAC,IAAI,CAAC,QAAQ;IAErC,QAAA,IAAI;gBACA,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,YAAY;gBAC3D,MAAM,GAAG,GAAG,CAAA,EAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAA,qBAAA,EAAwB,WAAW,CAAA,CAAE;IAEtE,YAAA,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;IACzB,gBAAA,OAAO,EAAE;IACL,oBAAA,eAAe,EAAE,CAAA,OAAA,EAAU,IAAI,CAAC,MAAM,CAAA;IACzC;IACJ,aAAA,CAAC;IAEF,YAAA,IAAI,GAAG,CAAC,EAAE,EAAE;oBACR,IAAI,CAAC,OAAO,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE;;oBAG/B,IAAI,cAAc,EAAE;IAChB,oBAAA,IAAI,CAAC,QAAQ,GAAG,IAAI;wBACpB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC;oBACpC;yBAAO;wBACH,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC;oBACrC;;IAGA,gBAAA,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;IAC/B,oBAAA,YAAY,CAAC,OAAO,CAAC,gBAAgB,IAAI,CAAC,MAAM,CAAA,CAAE,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;oBACrF;gBACJ;qBAAO;;oBAEH,IAAI,YAAY,GAAG,CAAA,sCAAA,EAAyC,GAAG,CAAC,MAAM,CAAA,CAAA,EAAI,GAAG,CAAC,UAAU,CAAA,CAAE;IAE1F,gBAAA,QAAQ,GAAG,CAAC,MAAM;IACd,oBAAA,KAAK,GAAG;4BACJ,YAAY,IAAI,qCAAqC;4BACrD;IACJ,oBAAA,KAAK,GAAG;4BACJ,YAAY,IAAI,yCAAyC;4BACzD;IACJ,oBAAA,KAAK,GAAG;4BACJ,YAAY,IAAI,kEAAkE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;4BACtG;IACJ,oBAAA,KAAK,GAAG;IACR,oBAAA,KAAK,GAAG;IACR,oBAAA,KAAK,GAAG;4BACJ,YAAY,IAAI,8CAA8C;4BAC9D;;IAGR,gBAAA,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC;IAC1B,gBAAA,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;gBACrE;YACJ;YAAE,OAAO,CAAC,EAAE;gBACR,MAAM,YAAY,GAAG,CAAA,yCAAA,EAA4C,IAAI,CAAC,MAAM,CAAC,MAAM,CAAA,CAAE;IACrF,YAAA,OAAO,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC;IAC9B,YAAA,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;;gBAGvD,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;IAChD,gBAAA,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA,aAAA,EAAgB,IAAI,CAAC,MAAM,CAAA,CAAE,CAAC;oBAClE,IAAI,MAAM,EAAE;wBACR,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;wBACjC,IAAI,cAAc,EAAE;IAChB,wBAAA,IAAI,CAAC,QAAQ,GAAG,IAAI;4BACpB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC;wBACpC;oBACJ;gBACJ;YACJ;QACJ;IAEA;;;;;;IAMG;IACH,IAAA,aAAa,CAAC,OAAe,EAAE,OAAgB,EAAE,YAAqB,EAAA;IAClE,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC;YAC5D,OAAO,CAAC,CAAC,MAAM;QACnB;IAEA,IAAA,eAAe,CAAC,OAAe,EAAE,OAAgB,EAAE,YAAoB,EAAA;IACnE,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC;IAC5D,QAAA,OAAO,MAAM,CAAC,MAAM,CAAC;QACzB;IAEA,IAAA,eAAe,CAAC,OAAe,EAAE,OAAgB,EAAE,YAAoB,EAAA;IACnE,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC;IAC5D,QAAA,OAAO,MAAM,CAAC,MAAM,CAAC;QACzB;IAEA,IAAA,aAAa,CAAU,OAAe,EAAE,OAAgB,EAAE,YAAe,EAAA;YACrE,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAM;QAC7D;IAEA,IAAA,SAAS,CAAC,OAAe,EAAE,WAA6B,EAAE,eAAwB,KAAK,EAAA;;IAEnF,QAAA,MAAM,OAAO,GAAG,OAAO,WAAW,KAAK,QAAQ,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,WAAW;YACvF,OAAO,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC;QAC7D;IAEA;;IAEG;IACK,IAAA,QAAQ,CAAC,OAAe,EAAE,OAAgB,EAAE,YAAiB,EAAA;IACjE,QAAA,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;IACf,YAAA,OAAO,CAAC,IAAI,CAAC,0DAA0D,OAAO,CAAA,CAAE,CAAC;IACjF,YAAA,OAAO,YAAY;YACvB;IAEA,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC;;YAIpF,OAAO,MAAM,CAAC,KAAK;QACvB;IAEA;;IAEG;QACH,MAAM,KAAK,CAAC,SAAiB,EAAE,OAAgB,EAAE,UAAgC,EAAE,KAAc,EAAA;IAC7F,QAAA,IAAI;gBACA,MAAM,KAAK,CAAC,CAAA,EAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAA,OAAA,CAAS,EAAE;IACxC,gBAAA,MAAM,EAAE,MAAM;IACd,gBAAA,OAAO,EAAE;IACL,oBAAA,cAAc,EAAE,kBAAkB;IAClC,oBAAA,eAAe,EAAE,CAAA,OAAA,EAAU,IAAI,CAAC,MAAM,CAAA;IACzC,iBAAA;IACD,gBAAA,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;IACjB,oBAAA,MAAM,EAAE,CAAC;IACL,4BAAA,IAAI,EAAE,QAAQ;IACd,4BAAA,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gCACnC,SAAS;gCACT,OAAO;gCACP,UAAU;gCACV;6BACH;qBACJ;IACJ,aAAA,CAAC;YACN;YAAE,OAAO,CAAC,EAAE;IACR,YAAA,OAAO,CAAC,KAAK,CAAC,oCAAoC,EAAE,CAAC,CAAC;YAC1D;QACJ;QAEA,KAAK,GAAA;YACD,IAAI,IAAI,CAAC,eAAe;IAAE,YAAA,aAAa,CAAC,IAAI,CAAC,eAAe,CAAC;;IAE7D,QAAA,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE;QAC1B;IACH;;ICjQD;;;;IAIG;IAiBH,MAAM,gBAAgB,GAAGA,mBAAa,CAAkB;IACpD,IAAA,MAAM,EAAE,IAAI;IACZ,IAAA,OAAO,EAAE;IACZ,CAAA,CAAC;IAEF;;;;;;;;;IASG;IACG,SAAU,YAAY,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,EAAqB,EAAA;;QAEtFC,eAAS,CAAC,MAAK;YACX,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;IACjC,YAAA,OAAO,CAAC,IAAI,CAAC,6FAA6F,CAAC;YAC/G;IACA,QAAA,IAAI,MAAM,IAAI,QAAQ,IAAI,MAAM,EAAE;IAC9B,YAAA,OAAO,CAAC,IAAI,CAAC,4GAA4G,CAAC;YAC9H;IACJ,IAAA,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEpB,IAAA,MAAM,CAAC,MAAM,CAAC,GAAGC,cAAQ,CAAC,MAAM,IAAI,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAE/DD,eAAS,CAAC,MAAK;IACX,QAAA,OAAO,MAAK;gBACR,MAAM,CAAC,KAAK,EAAE;IAClB,QAAA,CAAC;IACL,IAAA,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;IAEZ,IAAA,QACI,KAAA,CAAA,aAAA,CAAC,gBAAgB,CAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,IAChD,QAAQ,CACe;IAEpC;IAEA;;;;;;;;IAQG;aACa,UAAU,CACtB,OAAe,EACf,YAAe,EACf,eAA6B,EAAA;IAE7B,IAAA,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,EAAE,GAAGE,gBAAU,CAAC,gBAAgB,CAAC;QACzE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAGD,cAAQ,CAAI,YAAY,CAAC;QAEnDD,eAAS,CAAC,MAAK;YACX,IAAI,CAAC,MAAM,EAAE;gBACT,QAAQ,CAAC,YAAY,CAAC;gBACtB;YACJ;YAEA,MAAM,OAAO,GAAG,EAAE,GAAG,eAAe,EAAE,GAAG,eAAe,EAAE;YAE1D,MAAM,QAAQ,GAAG,MAAK;IAClB,YAAA,IAAI;IACA,gBAAA,IAAI,MAAW;;IAGf,gBAAA,IAAI,OAAO,YAAY,KAAK,SAAS,EAAE;wBACnC,MAAM,GAAG,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC;oBACjE;IAAO,qBAAA,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE;wBACzC,MAAM,GAAG,MAAM,CAAC,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC;oBACnE;IAAO,qBAAA,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE;wBACzC,MAAM,GAAG,MAAM,CAAC,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC;oBACnE;yBAAO;wBACH,MAAM,GAAG,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC;oBACjE;oBAEA,QAAQ,CAAC,MAAW,CAAC;gBACzB;gBAAE,OAAO,KAAK,EAAE;oBACZ,OAAO,CAAC,KAAK,CAAC,CAAA,8BAAA,EAAiC,OAAO,CAAA,CAAA,CAAG,EAAE,KAAK,CAAC;oBACjE,QAAQ,CAAC,YAAY,CAAC;gBAC1B;IACJ,QAAA,CAAC;;IAGD,QAAA,QAAQ,EAAE;;IAGV,QAAA,MAAM,OAAO,GAAG,MAAM,QAAQ,EAAE;IAChC,QAAA,MAAM,QAAQ,GAAG,MAAM,QAAQ,EAAE;IAEjC,QAAA,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC;IAC3B,QAAA,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC;;IAG7B,QAAA,OAAO,MAAK;IACR,YAAA,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC;IAC5B,YAAA,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAClC,QAAA,CAAC;IACL,IAAA,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,CAAC,CAAC;IAErE,IAAA,OAAO,KAAK;IAChB;IAEA;;;;;;;;IAQG;aACa,aAAa,GAAA;QACzB,MAAM,EAAE,MAAM,EAAE,GAAGE,gBAAU,CAAC,gBAAgB,CAAC;IAC/C,IAAA,OAAO,MAAM;IACjB;IAEA;;;;;;IAMG;aACa,QAAQ,GAAA;QACpB,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAGA,gBAAU,CAAC,gBAAgB,CAAC;QAExD,OAAO,CACH,SAAiB,EACjB,UAAgC,EAChC,KAAc,EACd,eAA6B,KAC7B;YACA,IAAI,CAAC,MAAM,EAAE;gBACT;YACJ;YAEA,MAAM,YAAY,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,eAAe,EAAE;YACvD,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,KAAK,CAAC;IAC5D,IAAA,CAAC;IACL;;ICtKA;;;;IAIG;;;;;;;;;;;;;;;"}
|
package/dist/react.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Provides easy integration with React applications.
|
|
5
5
|
*/
|
|
6
|
-
import { ReactNode } from 'react';
|
|
6
|
+
import React, { ReactNode } from 'react';
|
|
7
7
|
import { FluxClient, Context as FluxContext } from './client';
|
|
8
8
|
interface FluxProviderProps {
|
|
9
9
|
sdkKey: string;
|
|
@@ -21,7 +21,7 @@ interface FluxProviderProps {
|
|
|
21
21
|
* <App />
|
|
22
22
|
* </FluxProvider>
|
|
23
23
|
*/
|
|
24
|
-
export declare function FluxProvider({ sdkKey, config, context, children }: FluxProviderProps):
|
|
24
|
+
export declare function FluxProvider({ sdkKey, config, context, children }: FluxProviderProps): React.JSX.Element;
|
|
25
25
|
/**
|
|
26
26
|
* Hook to evaluate a feature flag.
|
|
27
27
|
*
|
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,
|
|
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
|
@@ -10,7 +10,10 @@ export interface Context {
|
|
|
10
10
|
* SDK configuration options.
|
|
11
11
|
*/
|
|
12
12
|
export interface Config {
|
|
13
|
-
|
|
13
|
+
/** Required: FluxControl API endpoint URL (e.g., 'https://api.fluxcontrol.io/api/v1/sdk') */
|
|
14
|
+
apiUrl: string;
|
|
15
|
+
/** Optional: Environment name (defaults to 'production') */
|
|
16
|
+
environment?: string;
|
|
14
17
|
pollingIntervalMs?: number;
|
|
15
18
|
eventFlushIntervalMs?: number;
|
|
16
19
|
eventQueueSize?: number;
|
|
@@ -91,4 +94,12 @@ export interface Event {
|
|
|
91
94
|
properties?: Record<string, any>;
|
|
92
95
|
value?: number;
|
|
93
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;
|
|
94
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,MAAM,CAAC,EAAE,MAAM,CAAC;
|
|
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.1
|
|
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",
|
|
@@ -48,13 +48,13 @@
|
|
|
48
48
|
"@typescript-eslint/parser": "^6.17.0",
|
|
49
49
|
"eslint": "^8.56.0",
|
|
50
50
|
"jest": "^29.7.0",
|
|
51
|
+
"jest-environment-jsdom": "^30.2.0",
|
|
51
52
|
"prettier": "^3.1.1",
|
|
52
53
|
"rollup": "^4.9.2",
|
|
53
54
|
"ts-jest": "^29.1.1",
|
|
54
55
|
"tslib": "^2.6.2",
|
|
55
56
|
"typescript": "^5.3.3"
|
|
56
57
|
},
|
|
57
|
-
"dependencies": {},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
|
60
60
|
},
|