@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 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
- edgeUrl: 'https://edge.fluxcontrol.io',
23
- enableAnalytics: true
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
- - `edgeUrl` - Edge evaluator URL (default: 'https://edge.fluxcontrol.io')
105
- - `pollingInterval` - Cache refresh interval in ms (default: 60000)
106
- - `enableAnalytics` - Enable event tracking (default: true)
107
- - `localStorage` - Use localStorage for caching (default: true)
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
- constructor(sdkKey: string, config?: Config);
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
  */
@@ -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;AACrE,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;gBAEjB,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,MAAW;IAe/C;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ3B;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IA+BnC;;;;;;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;CAGR"}
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, useState, useEffect, useContext } from 'react';
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
- apiUrl: config.apiUrl || 'http://localhost:8000/api/v1/sdk',
212
- pollingIntervalMs: config.pollingIntervalMs || 60000,
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
- // Environment context is usually passed or inferred.
236
- // For now, hardcode or assume API defaults.
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
- console.warn('[FluxClient] Failed to fetch ruleset:', res.status);
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
- console.error('[FluxClient] Error fetching ruleset:', e);
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 = async () => {
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 = await client.boolVariation(flagKey, context, defaultValue);
488
+ result = client.boolVariation(flagKey, context, defaultValue);
390
489
  }
391
490
  else if (typeof defaultValue === 'number') {
392
- result = await client.numberVariation(flagKey, context, defaultValue);
491
+ result = client.numberVariation(flagKey, context, defaultValue);
393
492
  }
394
493
  else if (typeof defaultValue === 'string') {
395
- result = await client.stringVariation(flagKey, context, defaultValue);
494
+ result = client.stringVariation(flagKey, context, defaultValue);
396
495
  }
397
496
  else {
398
- result = await client.jsonVariation(flagKey, context, defaultValue);
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
  }
@@ -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":";;AAYA;;AAEG;MACU,SAAS,CAAA;AAClB;;AAEG;AACH,IAAA,QAAQ,CACJ,OAAe,EACf,OAAgB,EAChB,OAAgB,EAChB,YAAiB,EAAA;QAEjB,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;QAEnC,IAAI,CAAC,IAAI,EAAE;YACP,OAAO;AACH,gBAAA,KAAK,EAAE,YAAY;AACnB,gBAAA,YAAY,EAAE,SAAS;AACvB,gBAAA,MAAM,EAAE,gBAAgB;aAC3B;QACL;;AAGA,QAAA,IAAI,IAAI,CAAC,KAAK,EAAE;;AAEZ,YAAA,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAM,EAAE,CAAM,KAAI;AACxD,gBAAA,MAAM,SAAS,GAAG,CAAC,CAAC,QAAQ,IAAI,GAAG;AACnC,gBAAA,MAAM,SAAS,GAAG,CAAC,CAAC,QAAQ,IAAI,GAAG;gBACnC,OAAO,SAAS,GAAG,SAAS;AAChC,YAAA,CAAC,CAAC;AAEF,YAAA,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE;AAC5B,gBAAA,IAAI,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;;AAE5D,oBAAA,IAAI,IAAI,CAAC,WAAW,EAAE;AAClB,wBAAA,OAAO,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC;oBACpE;AAAO,yBAAA,IAAI,IAAI,CAAC,OAAO,EAAE;;AAErB,wBAAA,OAAO,IAAI,CAAC,mBAAmB,CAC3B,IAAI,EACJ,IAAI,CAAC,OAAO,EACZ,OAAO,EACP,oBAAoB,CACvB;oBACL;gBACJ;YACJ;QACJ;;AAGA,QAAA,OAAO,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,CAAC;IACxE;AAEA;;AAEG;AACK,IAAA,kBAAkB,CACtB,UAAuB,EACvB,OAAgB,EAChB,OAAgB,EAAA;QAEhB,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE;AACxC,YAAA,OAAO,IAAI;QACf;QAEA,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,SAAS,KAC9B,IAAI,CAAC,iBAAiB,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,CACtD;IACL;AAEA;;AAEG;AACK,IAAA,iBAAiB,CACrB,SAAoB,EACpB,OAAgB,EAChB,OAAgB,EAAA;;AAGhB,QAAA,IAAI,YAAiB;AACrB,QAAA,IAAI,SAAS,CAAC,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,SAAS,KAAK,SAAS,EAAE;AACvE,YAAA,YAAY,GAAG,OAAO,CAAC,MAAM;QACjC;AAAO,aAAA,IAAI,SAAS,CAAC,SAAS,KAAK,WAAW,EAAE;AAC5C,YAAA,YAAY,GAAG,OAAO,CAAC,SAAS;QACpC;aAAO;YACH,YAAY,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,SAAS,CAAC;QAC5D;AAEA,QAAA,QAAQ,SAAS,CAAC,QAAQ;AACtB,YAAA,KAAK,IAAI;AACL,gBAAA,OAAO,YAAY,KAAK,SAAS,CAAC,KAAK;AAC3C,YAAA,KAAK,QAAQ;AACT,gBAAA,OAAO,YAAY,KAAK,SAAS,CAAC,KAAK;AAC3C,YAAA,KAAK,UAAU;AACX,gBAAA,OAAO,MAAM,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AACjE,YAAA,KAAK,kBAAkB;AACnB,gBAAA,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAClE,YAAA,KAAK,IAAI;;gBAEL,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE;AAChC,oBAAA,OAAO,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;gBACrE;gBACA,OAAO,MAAM,CAAC,YAAY,CAAC,KAAK,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC;AAC3D,YAAA,KAAK,QAAQ;AACb,YAAA,KAAK,WAAW;gBACZ,OAAO,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,IAAI,KAAK;AAC5D,YAAA,KAAK,YAAY;gBACb,OAAO,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC;AACpD,YAAA,KAAK,cAAc;gBACf,OAAO,MAAM,CAAC,YAAY,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC;AACzD,YAAA,KAAK,WAAW;gBACZ,OAAO,MAAM,CAAC,YAAY,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC;AACzD,YAAA,KAAK,YAAY;AACb,gBAAA,IAAI,SAAS,CAAC,WAAW,EAAE;oBACvB,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,WAAW,CAAC;oBACvD,IAAI,OAAO,EAAE;AACT,wBAAA,OAAO,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC;oBACxE;gBACJ;AACA,gBAAA,OAAO,KAAK;AAChB,YAAA;gBACI,OAAO,CAAC,IAAI,CAAC,CAAA,kBAAA,EAAqB,SAAS,CAAC,QAAQ,CAAA,CAAE,CAAC;AACvD,gBAAA,OAAO,KAAK;;IAExB;AAEA;;AAEG;AACK,IAAA,cAAc,CAClB,IAAgB,EAChB,WAAmB,EACnB,MAAc,EAAA;AAEd,QAAA,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,WAAW,CAAC;QAEnE,IAAI,CAAC,SAAS,EAAE;YACZ,OAAO;AACH,gBAAA,KAAK,EAAE,IAAI;AACX,gBAAA,YAAY,EAAE,SAAS;AACvB,gBAAA,MAAM,EAAE,qBAAqB;aAChC;QACL;QAEA,OAAO;YACH,KAAK,EAAE,SAAS,CAAC,KAAK;YACtB,YAAY,EAAE,SAAS,CAAC,GAAG;YAC3B,MAAM;SACT;IACL;AAEA;;AAEG;AACK,IAAA,sBAAsB,CAC1B,IAAgB,EAChB,OAA0B,EAC1B,OAAgB,EAChB,MAAc,EAAA;QAEd,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,SAAS,IAAI,WAAW;AACjE,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,IAAI,SAAS,CAAC,CAAC;QAE7D,IAAI,UAAU,GAAG,CAAC;AAClB,QAAA,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,UAAU,EAAE;AACnC,YAAA,UAAU,IAAI,IAAI,CAAC,MAAM;AACzB,YAAA,IAAI,MAAM,GAAG,UAAU,EAAE;gBACrB,OAAO;AACH,oBAAA,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK;AAC3B,oBAAA,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG;oBAChC,MAAM;iBACT;YACL;QACJ;;AAGA,QAAA,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;QAClE,OAAO;AACH,YAAA,KAAK,EAAE,QAAQ,CAAC,SAAS,CAAC,KAAK;AAC/B,YAAA,YAAY,EAAE,QAAQ,CAAC,SAAS,CAAC,GAAG;YACpC,MAAM;SACT;IACL;AAEA;;;AAGG;AACK,IAAA,mBAAmB,CACvB,IAAgB,EAChB,OAAuB,EACvB,OAAgB,EAChB,MAAc,EAAA;QAEd,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,SAAS,IAAI,WAAW;AACjE,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,IAAI,SAAS,CAAC,CAAC;AAC7D,QAAA,MAAM,UAAU,GAAG,OAAO,CAAC,EAAE,IAAI,CAAC;;QAGlC,MAAM,SAAS,GAAG,CAAC,UAAU,GAAG,GAAG,IAAI,KAAK;AAE5C,QAAA,IAAI,MAAM,GAAG,SAAS,EAAE;;AAEpB,YAAA,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC;YAC3D,IAAI,OAAO,EAAE;gBACT,OAAO;oBACH,KAAK,EAAE,OAAO,CAAC,KAAK;oBACpB,YAAY,EAAE,OAAO,CAAC,GAAG;oBACzB,MAAM;iBACT;YACL;QACJ;;AAGA,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC;QAC7D,IAAI,QAAQ,EAAE;YACV,OAAO;gBACH,KAAK,EAAE,QAAQ,CAAC,KAAK;gBACrB,YAAY,EAAE,QAAQ,CAAC,GAAG;gBAC1B,MAAM;aACT;QACL;;QAGA,OAAO;AACH,YAAA,KAAK,EAAE,KAAK;AACZ,YAAA,YAAY,EAAE,SAAS;AACvB,YAAA,MAAM,EAAE,kBAAkB;SAC7B;IACL;AAEA;;AAEG;AACH;;;AAGG;IACK,SAAS,CAAC,MAAc,EAAE,OAAe,EAAA;QAC7C,MAAM,KAAK,GAAG,CAAA,EAAG,OAAO,IAAI,MAAM,CAAA,CAAE,CAAC;QACrC,IAAI,IAAI,GAAG,IAAI;AACf,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACnC,YAAA,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACtD;QACA,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK;IACjC;AACH;;AC/PD;;;;AAIG;MACU,UAAU,CAAA;IAOnB,WAAA,CAAY,MAAc,EAAE,MAAA,GAAiB,EAAE,EAAA;QAHvC,IAAA,CAAA,OAAO,GAAmB,IAAI;AAIlC,QAAA,IAAI,CAAC,MAAM,GAAG,MAAM;QACpB,IAAI,CAAC,MAAM,GAAG;AACV,YAAA,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,kCAAkC;AAC3D,YAAA,iBAAiB,EAAE,MAAM,CAAC,iBAAiB,IAAI,KAAK;AACpD,YAAA,GAAG;SACN;AACD,QAAA,IAAI,CAAC,SAAS,GAAG,IAAI,SAAS,EAAE;;AAGhC,QAAA,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;YAC/B,IAAI,CAAC,IAAI,EAAE;QACf;IACJ;AAEA;;AAEG;AACH,IAAA,MAAM,IAAI,GAAA;AACN,QAAA,MAAM,IAAI,CAAC,YAAY,EAAE;AAEzB,QAAA,IAAI,IAAI,CAAC,MAAM,CAAC,iBAAiB,IAAI,IAAI,CAAC,MAAM,CAAC,iBAAiB,GAAG,CAAC,EAAE;AACpE,YAAA,IAAI,CAAC,eAAe,GAAG,WAAW,CAAC,MAAM,IAAI,CAAC,YAAY,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC;QAChG;IACJ;AAEA;;AAEG;AACH,IAAA,MAAM,YAAY,GAAA;AACd,QAAA,IAAI;;;YAGA,MAAM,GAAG,GAAG,CAAA,EAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAA,+BAAA,CAAiC;AAElE,YAAA,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;AACzB,gBAAA,OAAO,EAAE;AACL,oBAAA,eAAe,EAAE,CAAA,OAAA,EAAU,IAAI,CAAC,MAAM,CAAA;AACzC;AACJ,aAAA,CAAC;AAEF,YAAA,IAAI,GAAG,CAAC,EAAE,EAAE;gBACR,IAAI,CAAC,OAAO,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE;;AAE/B,gBAAA,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;AAC/B,oBAAA,YAAY,CAAC,OAAO,CAAC,gBAAgB,IAAI,CAAC,MAAM,CAAA,CAAE,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBACrF;YACJ;iBAAO;gBACH,OAAO,CAAC,IAAI,CAAC,uCAAuC,EAAE,GAAG,CAAC,MAAM,CAAC;YACrE;QACJ;QAAE,OAAO,CAAC,EAAE;AACR,YAAA,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,CAAC,CAAC;;YAExD,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;AAChD,gBAAA,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA,aAAA,EAAgB,IAAI,CAAC,MAAM,CAAA,CAAE,CAAC;AAClE,gBAAA,IAAI,MAAM;oBAAE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;YACjD;QACJ;IACJ;AAEA;;;;;;AAMG;AACH,IAAA,aAAa,CAAC,OAAe,EAAE,OAAgB,EAAE,YAAqB,EAAA;AAClE,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC;QAC5D,OAAO,CAAC,CAAC,MAAM;IACnB;AAEA,IAAA,eAAe,CAAC,OAAe,EAAE,OAAgB,EAAE,YAAoB,EAAA;AACnE,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC;AAC5D,QAAA,OAAO,MAAM,CAAC,MAAM,CAAC;IACzB;AAEA,IAAA,eAAe,CAAC,OAAe,EAAE,OAAgB,EAAE,YAAoB,EAAA;AACnE,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC;AAC5D,QAAA,OAAO,MAAM,CAAC,MAAM,CAAC;IACzB;AAEA,IAAA,aAAa,CAAU,OAAe,EAAE,OAAgB,EAAE,YAAe,EAAA;QACrE,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAM;IAC7D;AAEA,IAAA,SAAS,CAAC,OAAe,EAAE,WAA6B,EAAE,eAAwB,KAAK,EAAA;;AAEnF,QAAA,MAAM,OAAO,GAAG,OAAO,WAAW,KAAK,QAAQ,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,WAAW;QACvF,OAAO,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC;IAC7D;AAEA;;AAEG;AACK,IAAA,QAAQ,CAAC,OAAe,EAAE,OAAgB,EAAE,YAAiB,EAAA;AACjE,QAAA,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;AACf,YAAA,OAAO,CAAC,IAAI,CAAC,0DAA0D,OAAO,CAAA,CAAE,CAAC;AACjF,YAAA,OAAO,YAAY;QACvB;AAEA,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC;;QAIpF,OAAO,MAAM,CAAC,KAAK;IACvB;AAEA;;AAEG;IACH,MAAM,KAAK,CAAC,SAAiB,EAAE,OAAgB,EAAE,UAAgC,EAAE,KAAc,EAAA;AAC7F,QAAA,IAAI;YACA,MAAM,KAAK,CAAC,CAAA,EAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAA,OAAA,CAAS,EAAE;AACxC,gBAAA,MAAM,EAAE,MAAM;AACd,gBAAA,OAAO,EAAE;AACL,oBAAA,cAAc,EAAE,kBAAkB;AAClC,oBAAA,eAAe,EAAE,CAAA,OAAA,EAAU,IAAI,CAAC,MAAM,CAAA;AACzC,iBAAA;AACD,gBAAA,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;AACjB,oBAAA,MAAM,EAAE,CAAC;AACL,4BAAA,IAAI,EAAE,QAAQ;AACd,4BAAA,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;4BACnC,SAAS;4BACT,OAAO;4BACP,UAAU;4BACV;yBACH;iBACJ;AACJ,aAAA,CAAC;QACN;QAAE,OAAO,CAAC,EAAE;AACR,YAAA,OAAO,CAAC,KAAK,CAAC,oCAAoC,EAAE,CAAC,CAAC;QAC1D;IACJ;IAEA,KAAK,GAAA;QACD,IAAI,IAAI,CAAC,eAAe;AAAE,YAAA,aAAa,CAAC,IAAI,CAAC,eAAe,CAAC;IACjE;AACH;;AC1JD;;;;AAIG;AAiBH,MAAM,gBAAgB,GAAG,aAAa,CAAkB;AACpD,IAAA,MAAM,EAAE,IAAI;AACZ,IAAA,OAAO,EAAE;AACZ,CAAA,CAAC;AAEF;;;;;;;;;AASG;AACG,SAAU,YAAY,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,EAAqB,EAAA;AACtF,IAAA,MAAM,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,IAAI,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE/D,SAAS,CAAC,MAAK;AACX,QAAA,OAAO,MAAK;YACR,MAAM,CAAC,KAAK,EAAE;AAClB,QAAA,CAAC;AACL,IAAA,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;AAEZ,IAAA,QACI,KAAA,CAAA,aAAA,CAAC,gBAAgB,CAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,IAChD,QAAQ,CACe;AAEpC;AAEA;;;;;;;;AAQG;SACa,UAAU,CACtB,OAAe,EACf,YAAe,EACf,eAA6B,EAAA;AAE7B,IAAA,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,EAAE,GAAG,UAAU,CAAC,gBAAgB,CAAC;IACzE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAI,YAAY,CAAC;IACnD,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC;IAE5C,SAAS,CAAC,MAAK;QACX,IAAI,CAAC,MAAM,EAAE;YACT,QAAQ,CAAC,YAAY,CAAC;YACtB,UAAU,CAAC,KAAK,CAAC;YACjB;QACJ;QAEA,MAAM,OAAO,GAAG,EAAE,GAAG,eAAe,EAAE,GAAG,eAAe,EAAE;AAE1D,QAAA,MAAM,QAAQ,GAAG,YAAW;AACxB,YAAA,IAAI;AACA,gBAAA,IAAI,MAAW;;AAGf,gBAAA,IAAI,OAAO,YAAY,KAAK,SAAS,EAAE;AACnC,oBAAA,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC;gBACvE;AAAO,qBAAA,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE;AACzC,oBAAA,MAAM,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC;gBACzE;AAAO,qBAAA,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE;AACzC,oBAAA,MAAM,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC;gBACzE;qBAAO;AACH,oBAAA,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC;gBACvE;gBAEA,QAAQ,CAAC,MAAW,CAAC;YACzB;YAAE,OAAO,KAAK,EAAE;gBACZ,OAAO,CAAC,KAAK,CAAC,CAAA,8BAAA,EAAiC,OAAO,CAAA,CAAA,CAAG,EAAE,KAAK,CAAC;gBACjE,QAAQ,CAAC,YAAY,CAAC;YAC1B;oBAAU;gBACN,UAAU,CAAC,KAAK,CAAC;YACrB;AACJ,QAAA,CAAC;AAED,QAAA,QAAQ,EAAE;AACd,IAAA,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,CAAC,CAAC;AAErE,IAAA,OAAO,KAAK;AAChB;AAEA;;;;;;;;AAQG;SACa,aAAa,GAAA;IACzB,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,gBAAgB,CAAC;AAC/C,IAAA,OAAO,MAAM;AACjB;AAEA;;;;;;AAMG;SACa,QAAQ,GAAA;IACpB,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC,gBAAgB,CAAC;IAExD,OAAO,CACH,SAAiB,EACjB,UAAgC,EAChC,KAAc,EACd,eAA6B,KAC7B;QACA,IAAI,CAAC,MAAM,EAAE;YACT;QACJ;QAEA,MAAM,YAAY,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,eAAe,EAAE;QACvD,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,KAAK,CAAC;AAC5D,IAAA,CAAC;AACL;;AClJA;;;;AAIG;;;;"}
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
- apiUrl: config.apiUrl || 'http://localhost:8000/api/v1/sdk',
216
- pollingIntervalMs: config.pollingIntervalMs || 60000,
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
- // Environment context is usually passed or inferred.
240
- // For now, hardcode or assume API defaults.
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
- console.warn('[FluxClient] Failed to fetch ruleset:', res.status);
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
- console.error('[FluxClient] Error fetching ruleset:', e);
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 = async () => {
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 = await client.boolVariation(flagKey, context, defaultValue);
492
+ result = client.boolVariation(flagKey, context, defaultValue);
394
493
  }
395
494
  else if (typeof defaultValue === 'number') {
396
- result = await client.numberVariation(flagKey, context, defaultValue);
495
+ result = client.numberVariation(flagKey, context, defaultValue);
397
496
  }
398
497
  else if (typeof defaultValue === 'string') {
399
- result = await client.stringVariation(flagKey, context, defaultValue);
498
+ result = client.stringVariation(flagKey, context, defaultValue);
400
499
  }
401
500
  else {
402
- result = await client.jsonVariation(flagKey, context, defaultValue);
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":"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"}
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): any;
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
  *
@@ -1 +1 @@
1
- {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAc,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,OAczF;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,CAAC,GAAG,GAAG,EAC9B,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,CAAC,EACf,eAAe,CAAC,EAAE,WAAW,GAC9B,CAAC,CA0CH;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"}
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
- apiUrl?: string;
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
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
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.2",
3
+ "version": "0.2.1",
4
4
  "description": "FluxControl JavaScript SDK for feature flags and experimentation",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",
@@ -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
  },