@fluxcontrolsdk/js-sdk 0.2.0 → 0.2.1

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