@fluxcontrolsdk/js-sdk 0.1.0
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/LICENSE +21 -0
- package/README.md +260 -0
- package/dist/client.d.ts +45 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/evaluator.d.ts +40 -0
- package/dist/evaluator.d.ts.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.esm.js +452 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +465 -0
- package/dist/index.js.map +1 -0
- package/dist/react.d.ts +54 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/types.d.ts +94 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +66 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 FluxControl Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# FluxControl JavaScript SDK
|
|
2
|
+
|
|
3
|
+
High-performance JavaScript/TypeScript SDK for feature flags and experimentation in web applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @fluxcontrol/js-sdk
|
|
9
|
+
# or
|
|
10
|
+
yarn add @fluxcontrol/js-sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
### Vanilla JavaScript/TypeScript
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { FluxClient } from '@fluxcontrol/js-sdk';
|
|
19
|
+
|
|
20
|
+
// Initialize client
|
|
21
|
+
const client = new FluxClient('your-sdk-key', {
|
|
22
|
+
edgeUrl: 'https://edge.fluxcontrol.io',
|
|
23
|
+
enableAnalytics: true
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Create context
|
|
27
|
+
const context = {
|
|
28
|
+
userId: 'user-123',
|
|
29
|
+
attributes: {
|
|
30
|
+
plan: 'premium',
|
|
31
|
+
country: 'US'
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// Evaluate boolean flag
|
|
36
|
+
const enabled = await client.boolVariation('new-feature', context, false);
|
|
37
|
+
if (enabled) {
|
|
38
|
+
// New feature code
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Evaluate string flag
|
|
42
|
+
const theme = await client.stringVariation('ui-theme', context, 'light');
|
|
43
|
+
|
|
44
|
+
// Track custom event
|
|
45
|
+
await client.track('button_clicked', context, {
|
|
46
|
+
buttonId: 'checkout',
|
|
47
|
+
page: 'cart'
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Cleanup
|
|
51
|
+
client.close();
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### React
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import { FluxProvider, useFeature, useTrack } from '@fluxcontrol/js-sdk';
|
|
58
|
+
|
|
59
|
+
// Wrap your app
|
|
60
|
+
function App() {
|
|
61
|
+
return (
|
|
62
|
+
<FluxProvider
|
|
63
|
+
sdkKey="your-sdk-key"
|
|
64
|
+
context={{ userId: 'user-123' }}
|
|
65
|
+
>
|
|
66
|
+
<MyComponent />
|
|
67
|
+
</FluxProvider>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Use in components
|
|
72
|
+
function MyComponent() {
|
|
73
|
+
// Evaluate flag
|
|
74
|
+
const checkoutFlow = useFeature('checkout-flow', 'standard');
|
|
75
|
+
|
|
76
|
+
// Track events
|
|
77
|
+
const track = useTrack();
|
|
78
|
+
|
|
79
|
+
const handleClick = () => {
|
|
80
|
+
track('button_clicked', { button: 'checkout' });
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
if (checkoutFlow === 'one-click') {
|
|
84
|
+
return <OneClickCheckout onClick={handleClick} />;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return <StandardCheckout onClick={handleClick} />;
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## API Reference
|
|
92
|
+
|
|
93
|
+
### FluxClient
|
|
94
|
+
|
|
95
|
+
Main client for evaluating feature flags.
|
|
96
|
+
|
|
97
|
+
#### Constructor
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
new FluxClient(sdkKey: string, config?: Config)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Config Options:**
|
|
104
|
+
- `edgeUrl` - Edge evaluator URL (default: 'https://edge.fluxcontrol.io')
|
|
105
|
+
- `pollingInterval` - Cache refresh interval in ms (default: 60000)
|
|
106
|
+
- `enableAnalytics` - Enable event tracking (default: true)
|
|
107
|
+
- `localStorage` - Use localStorage for caching (default: true)
|
|
108
|
+
|
|
109
|
+
#### Methods
|
|
110
|
+
|
|
111
|
+
**`boolVariation(flagKey, context, defaultValue)`**
|
|
112
|
+
Evaluate a boolean flag.
|
|
113
|
+
|
|
114
|
+
**`stringVariation(flagKey, context, defaultValue)`**
|
|
115
|
+
Evaluate a string flag.
|
|
116
|
+
|
|
117
|
+
**`numberVariation(flagKey, context, defaultValue)`**
|
|
118
|
+
Evaluate a number flag.
|
|
119
|
+
|
|
120
|
+
**`jsonVariation<T>(flagKey, context, defaultValue)`**
|
|
121
|
+
Evaluate a JSON flag with type safety.
|
|
122
|
+
|
|
123
|
+
**`track(eventName, context, properties?, value?)`**
|
|
124
|
+
Track a custom event.
|
|
125
|
+
|
|
126
|
+
**`close()`**
|
|
127
|
+
Cleanup and stop polling.
|
|
128
|
+
|
|
129
|
+
### React Hooks
|
|
130
|
+
|
|
131
|
+
#### `FluxProvider`
|
|
132
|
+
|
|
133
|
+
Provider component to wrap your app.
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
<FluxProvider
|
|
137
|
+
sdkKey="your-key"
|
|
138
|
+
config={{ edgeUrl: 'https://...' }}
|
|
139
|
+
context={{ userId: 'user-123' }}
|
|
140
|
+
>
|
|
141
|
+
{children}
|
|
142
|
+
</FluxProvider>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### `useFeature(flagKey, defaultValue, contextOverride?)`
|
|
146
|
+
|
|
147
|
+
Hook to evaluate a feature flag.
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
const theme = useFeature('ui-theme', 'light');
|
|
151
|
+
const enabled = useFeature('new-feature', false);
|
|
152
|
+
const config = useFeature('feature-config', { timeout: 5000 });
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
#### `useFluxClient()`
|
|
156
|
+
|
|
157
|
+
Get the client instance for advanced usage.
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
const client = useFluxClient();
|
|
161
|
+
await client.track('custom_event', context);
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
#### `useTrack()`
|
|
165
|
+
|
|
166
|
+
Hook to track custom events.
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
const track = useTrack();
|
|
170
|
+
track('purchase', { productId: '123' }, 99.99);
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Context
|
|
174
|
+
|
|
175
|
+
The context object provides user attributes for targeting:
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
interface Context {
|
|
179
|
+
userId?: string;
|
|
180
|
+
sessionId?: string;
|
|
181
|
+
attributes?: Record<string, any>;
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Standard attributes:**
|
|
186
|
+
- `userId` - Unique user identifier
|
|
187
|
+
- `sessionId` - Session identifier
|
|
188
|
+
- `attributes` - Custom attributes (plan, country, etc.)
|
|
189
|
+
|
|
190
|
+
## Features
|
|
191
|
+
|
|
192
|
+
- ✅ **TypeScript Support** - Full type definitions
|
|
193
|
+
- ✅ **React Integration** - Hooks and Provider
|
|
194
|
+
- ✅ **Local Caching** - localStorage for offline support
|
|
195
|
+
- ✅ **Auto-refresh** - Polling for flag updates
|
|
196
|
+
- ✅ **Event Tracking** - Automatic exposure tracking
|
|
197
|
+
- ✅ **Edge Evaluation** - Secure server-side evaluation
|
|
198
|
+
- ✅ **SSR Compatible** - Works with Next.js and other frameworks
|
|
199
|
+
|
|
200
|
+
## Advanced Usage
|
|
201
|
+
|
|
202
|
+
### Custom Context per Evaluation
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
const enabled = useFeature('feature', false, {
|
|
206
|
+
attributes: { experiment: 'variant-a' }
|
|
207
|
+
});
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Conditional Rendering
|
|
211
|
+
|
|
212
|
+
```tsx
|
|
213
|
+
function FeatureGate({ flagKey, children }) {
|
|
214
|
+
const enabled = useFeature(flagKey, false);
|
|
215
|
+
return enabled ? <>{children}</> : null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
<FeatureGate flagKey="new-ui">
|
|
219
|
+
<NewUI />
|
|
220
|
+
</FeatureGate>
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### A/B Testing
|
|
224
|
+
|
|
225
|
+
```tsx
|
|
226
|
+
function Experiment() {
|
|
227
|
+
const variant = useFeature('homepage-hero', 'control');
|
|
228
|
+
const track = useTrack();
|
|
229
|
+
|
|
230
|
+
useEffect(() => {
|
|
231
|
+
// Track impression
|
|
232
|
+
track('hero_viewed', { variant });
|
|
233
|
+
}, [variant]);
|
|
234
|
+
|
|
235
|
+
const handleClick = () => {
|
|
236
|
+
track('hero_clicked', { variant });
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
switch (variant) {
|
|
240
|
+
case 'variant-a':
|
|
241
|
+
return <HeroA onClick={handleClick} />;
|
|
242
|
+
case 'variant-b':
|
|
243
|
+
return <HeroB onClick={handleClick} />;
|
|
244
|
+
default:
|
|
245
|
+
return <HeroControl onClick={handleClick} />;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Browser Support
|
|
251
|
+
|
|
252
|
+
- Chrome/Edge: Latest 2 versions
|
|
253
|
+
- Firefox: Latest 2 versions
|
|
254
|
+
- Safari: Latest 2 versions
|
|
255
|
+
- iOS Safari: iOS 12+
|
|
256
|
+
- Android Chrome: Latest 2 versions
|
|
257
|
+
|
|
258
|
+
## License
|
|
259
|
+
|
|
260
|
+
Internal use only.
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Context, Config, EvaluationResult, Ruleset } from './types';
|
|
2
|
+
export { Context, Config, EvaluationResult, Ruleset };
|
|
3
|
+
/**
|
|
4
|
+
* FluxControl JavaScript SDK (Thick Client)
|
|
5
|
+
*
|
|
6
|
+
* Downloads rulesets and evaluates flags locally.
|
|
7
|
+
*/
|
|
8
|
+
export declare class FluxClient {
|
|
9
|
+
private sdkKey;
|
|
10
|
+
private config;
|
|
11
|
+
private evaluator;
|
|
12
|
+
private ruleset;
|
|
13
|
+
private pollingInterval;
|
|
14
|
+
constructor(sdkKey: string, config?: Config);
|
|
15
|
+
/**
|
|
16
|
+
* Initialize the SDK (fetch ruleset).
|
|
17
|
+
*/
|
|
18
|
+
init(): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Fetch ruleset from API.
|
|
21
|
+
*/
|
|
22
|
+
fetchRuleset(): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Evaluate a boolean flag.
|
|
25
|
+
* Note: Sync evaluation now possible if ruleset is loaded!
|
|
26
|
+
* But we keep async signature if needed or make it sync?
|
|
27
|
+
* Legacy SDK was async. Let's make it sync but helpful.
|
|
28
|
+
* Actually, if init() is async, we can't guarantee ruleset is there immediately.
|
|
29
|
+
*/
|
|
30
|
+
boolVariation(flagKey: string, context: Context, defaultValue: boolean): boolean;
|
|
31
|
+
stringVariation(flagKey: string, context: Context, defaultValue: string): string;
|
|
32
|
+
numberVariation(flagKey: string, context: Context, defaultValue: number): number;
|
|
33
|
+
jsonVariation<T = any>(flagKey: string, context: Context, defaultValue: T): T;
|
|
34
|
+
isEnabled(flagKey: string, contextOrId: Context | string, defaultValue?: boolean): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Internal evaluation.
|
|
37
|
+
*/
|
|
38
|
+
private evaluate;
|
|
39
|
+
/**
|
|
40
|
+
* Track a custom event.
|
|
41
|
+
*/
|
|
42
|
+
track(eventName: string, context: Context, properties?: Record<string, any>, value?: number): Promise<void>;
|
|
43
|
+
close(): void;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACrE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC;AAEtD;;;;GAIG;AACH,qBAAa,UAAU;IACnB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,eAAe,CAAM;gBAEjB,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,MAAW;IAe/C;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ3B;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IA+BnC;;;;;;OAMG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,GAAG,OAAO;IAKhF,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM;IAKhF,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM;IAKhF,aAAa,CAAC,CAAC,GAAG,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,GAAG,CAAC;IAI7E,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,GAAG,MAAM,EAAE,YAAY,GAAE,OAAe,GAAG,OAAO;IAMjG;;OAEG;IACH,OAAO,CAAC,QAAQ;IAahB;;OAEG;IACG,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBjH,KAAK;CAGR"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Context, Ruleset, EvaluationResult } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Evaluates feature flags against targeting rules.
|
|
4
|
+
*/
|
|
5
|
+
export declare class Evaluator {
|
|
6
|
+
/**
|
|
7
|
+
* Evaluate a flag.
|
|
8
|
+
*/
|
|
9
|
+
evaluate(flagKey: string, context: Context, ruleset: Ruleset, defaultValue: any): EvaluationResult;
|
|
10
|
+
/**
|
|
11
|
+
* Evaluate all conditions in a rule.
|
|
12
|
+
*/
|
|
13
|
+
private evaluateConditions;
|
|
14
|
+
/**
|
|
15
|
+
* Evaluate a single condition.
|
|
16
|
+
*/
|
|
17
|
+
private evaluateCondition;
|
|
18
|
+
/**
|
|
19
|
+
* Serve a specific variation.
|
|
20
|
+
*/
|
|
21
|
+
private serveVariation;
|
|
22
|
+
/**
|
|
23
|
+
* Serve variation based on percentage rollout.
|
|
24
|
+
*/
|
|
25
|
+
private servePercentageRollout;
|
|
26
|
+
/**
|
|
27
|
+
* Serve variation based on boolean rollout (simplified format: {on: percentage}).
|
|
28
|
+
* Used for boolean flags where 'on' represents the percentage for 'true'.
|
|
29
|
+
*/
|
|
30
|
+
private serveBooleanRollout;
|
|
31
|
+
/**
|
|
32
|
+
* Get consistent bucket (0-9999) for user using MD5 hash.
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* Get consistent bucket (0-9999) for user using simple hash (DJB2).
|
|
36
|
+
* Format: flagKey:userId (matches Python SDK for cross-SDK consistency)
|
|
37
|
+
*/
|
|
38
|
+
private getBucket;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=evaluator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluator.d.ts","sourceRoot":"","sources":["../src/evaluator.ts"],"names":[],"mappings":"AACA,OAAO,EACH,OAAO,EACP,OAAO,EAIP,gBAAgB,EAGnB,MAAM,SAAS,CAAC;AAEjB;;GAEG;AACH,qBAAa,SAAS;IAClB;;OAEG;IACH,QAAQ,CACJ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,OAAO,EAChB,YAAY,EAAE,GAAG,GAClB,gBAAgB;IA0CnB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAc1B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAqDzB;;OAEG;IACH,OAAO,CAAC,cAAc;IAsBtB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA8B9B;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IA2C3B;;OAEG;IACH;;;OAGG;IACH,OAAO,CAAC,SAAS;CAQpB"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FluxControl JavaScript SDK
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
export * from './types';
|
|
7
|
+
export * from './client';
|
|
8
|
+
export * from './react';
|
|
9
|
+
import { FluxClient } from './client';
|
|
10
|
+
export default FluxClient;
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AAExB,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,eAAe,UAAU,CAAC"}
|