@featureflare/react 0.0.5 → 0.0.6-beta.259

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
@@ -12,150 +12,186 @@ pnpm add @featureflare/react
12
12
  yarn add @featureflare/react
13
13
  ```
14
14
 
15
- **Peer Dependencies:**
16
-
17
- - `react >= 18`
18
- - `react-dom >= 18`
19
-
20
15
  ## Usage
21
16
 
22
- ### Setup Provider
23
-
24
- Wrap your app with `FeatureFlareProvider`:
17
+ ### Minimal Setup
25
18
 
26
19
  ```typescript
27
- import { FeatureFlareProvider } from '@featureflare/react';
20
+ import { FeatureFlareProvider, resolveFeatureFlareBrowserConfig, useFlags } from '@featureflare/react';
21
+
22
+ function FlagsBootstrap() {
23
+ useFlags({
24
+ user: { id: 'user-123', email: 'dev@company.com' },
25
+ defaultValue: false,
26
+ refreshIntervalMs: 10000,
27
+ pauseWhenHidden: true,
28
+ });
29
+
30
+ return null;
31
+ }
28
32
 
29
33
  export function App() {
34
+ const config = resolveFeatureFlareBrowserConfig();
35
+
36
+ if (!config.sdkKey) {
37
+ return <div>Feature flags disabled</div>;
38
+ }
39
+
30
40
  return (
31
41
  <FeatureFlareProvider
32
42
  config={{
33
- // Optional: explicit FeatureFlare API URL
34
- apiBaseUrl: 'https://flags.your-company.com',
35
- // SDK defaults to FeatureFlare Cloud Run unless overridden
36
- sdkKey: 'your-client-key-here' // Client key for browser
43
+ ...config,
44
+ bootstrap: {
45
+ flags: { 'new-nav': true }
46
+ }
37
47
  }}
38
- initialUser={{ id: 'user-123' }}
48
+ initialUser={{ id: 'user-123', key: 'user-123' }}
39
49
  >
40
- {/* Your app */}
50
+ <FlagsBootstrap />
51
+ {/* app */}
41
52
  </FeatureFlareProvider>
42
53
  );
43
54
  }
44
55
  ```
45
56
 
46
- ### Read Flags
47
-
48
- Use the `useBoolFlag` hook to read boolean flags:
57
+ ### Read One Flag
49
58
 
50
59
  ```typescript
51
- import { useBoolFlag } from '@featureflare/react';
60
+ import { useFlag } from '@featureflare/react';
52
61
 
53
62
  export function NewNav() {
54
- const { value, loading, error } = useBoolFlag('new-nav', false);
55
-
63
+ const { value, loading, error } = useFlag('new-nav', false);
64
+
56
65
  if (loading) return <div>Loading...</div>;
57
- if (error) return <div>Error: {error.message}</div>;
58
-
66
+ if (error) return <div>Error: {error}</div>;
67
+
59
68
  return value ? <div>New nav ON</div> : <div>New nav OFF</div>;
60
69
  }
61
70
  ```
62
71
 
63
- ### Update User
72
+ ### SSR/HTML Bootstrap (anti-flicker working flow)
73
+
74
+ Server-side (example in Next.js `getServerSideProps`):
75
+
76
+ ```typescript
77
+ import { FeatureFlareClient } from '@featureflare/sdk-js';
78
+
79
+ export async function getServerSideProps() {
80
+ const user = { id: 'user-123', key: 'user-123' };
81
+ const sdk = new FeatureFlareClient({
82
+ apiBaseUrl: process.env.FEATUREFLARE_API_BASE_URL,
83
+ sdkKey: process.env.FEATUREFLARE_SERVER_SDK_KEY
84
+ });
85
+
86
+ const nav = await sdk.bool('new-nav', user, false);
87
+
88
+ return {
89
+ props: {
90
+ user,
91
+ ffBootstrap: {
92
+ flags: { 'new-nav': nav }
93
+ }
94
+ }
95
+ };
96
+ }
97
+ ```
64
98
 
65
- Use the `useFeatureFlareUser` hook to get and update the current user:
99
+ Client-side provider wiring:
66
100
 
67
101
  ```typescript
68
- import { useFeatureFlareUser } from '@featureflare/react';
102
+ import { FeatureFlareProvider, resolveFeatureFlareBrowserConfig } from '@featureflare/react';
69
103
 
70
- export function UserSwitcher() {
71
- const [user, setUser] = useFeatureFlareUser();
104
+ export default function Page({ user, ffBootstrap }) {
105
+ const config = resolveFeatureFlareBrowserConfig();
72
106
 
73
107
  return (
74
- <button
75
- onClick={() =>
76
- setUser({
77
- ...(user ?? { id: 'user-123' }),
78
- meta: { ...(user?.meta ?? {}), companyId: 'northwind' }
79
- })
80
- }
108
+ <FeatureFlareProvider
109
+ config={{
110
+ ...config,
111
+ bootstrap: ffBootstrap
112
+ }}
113
+ initialUser={user}
81
114
  >
82
- Switch user
83
- </button>
115
+ {/* First render uses bootstrap synchronously; no loading flicker for these flags */}
116
+ <App />
117
+ </FeatureFlareProvider>
84
118
  );
85
119
  }
86
120
  ```
87
121
 
88
- ## API Reference
122
+ For non-SSR apps, inject a bootstrap JSON payload into HTML before app startup and pass it to `config.bootstrap` the same way.
123
+
124
+ ## API
89
125
 
90
126
  ### `FeatureFlareProvider`
91
127
 
92
- Provider component that wraps your app and provides the FeatureFlare client context.
128
+ Props:
129
+
130
+ - `config: FeatureFlareReactConfig`
131
+ - `initialUser: FeatureFlareUserPayload`
132
+ - `user?: FeatureFlareUserPayload` (controlled mode)
133
+ - `onUserChange?: (user: FeatureFlareUserPayload) => void` (required in controlled mode)
134
+ - `children: React.ReactNode`
93
135
 
94
- #### Props
136
+ ### `resolveFeatureFlareBrowserConfig(input?)`
95
137
 
96
- - `config: FeatureFlareReactConfig` - Configuration object
97
- - `apiBaseUrl?: string` - Optional explicit FeatureFlare API base URL.
98
- - `sdkKey?: string` - Client SDK key (recommended). If not provided, reads from `FEATUREFLARE_CLIENT_KEY` env var.
99
- - `projectKey?: string` - Legacy: project key (requires `envKey`). Not recommended.
100
- - `envKey?: string` - Environment key (default: `'production'`). Only used with `projectKey`.
101
- - `initialUser: FeatureFlareUserPayload` - Initial user payload
102
- - `user?: FeatureFlareUserPayload` - Controlled user (requires `onUserChange`)
103
- - `onUserChange?: (user: FeatureFlareUserPayload) => void` - Callback for user changes (required if using controlled `user`)
104
- - `children: React.ReactNode` - Your app components
138
+ Builds provider config from common `NEXT_PUBLIC_FEATUREFLARE_*` vars.
105
139
 
106
- ### `useBoolFlag(flagKey: string, defaultValue: boolean)`
140
+ Returns:
107
141
 
108
- Hook to evaluate a boolean feature flag.
142
+ - `apiBaseUrl?: string`
143
+ - `envKey: 'development' | 'staging' | 'production'`
144
+ - `sdkKey?: string`
109
145
 
110
- **Returns:**
146
+ ### `useFeatureFlareUser()`
111
147
 
112
- - `value: boolean` - The flag value
113
- - `loading: boolean` - Whether the evaluation is in progress
114
- - `error: Error | null` - Error if evaluation failed
148
+ Returns:
115
149
 
116
- **Example:**
150
+ - `[user, setUser]`
117
151
 
118
- ```typescript
119
- const { value, loading, error } = useBoolFlag('feature-flag', false);
120
- ```
152
+ ### `useFlags(...)`
121
153
 
122
- ### `useFeatureFlareUser()`
154
+ Signatures:
123
155
 
124
- Hook to get and update the current user.
156
+ - `useFlags(keys: string[], defaultValue?: boolean)` `{ values, loading, errors }`
157
+ - `useFlags(input?: { user?: FeatureFlareUserPayload; defaultValue?: boolean; refreshIntervalMs?: number; hiddenRefreshIntervalMs?: number; pauseWhenHidden?: boolean; enabled?: boolean })`
158
+ - `useFlags(defaultValue?: boolean, options?: { refreshIntervalMs?: number; hiddenRefreshIntervalMs?: number; pauseWhenHidden?: boolean; enabled?: boolean })`
125
159
 
126
- **Returns:**
160
+ ### `useBoolFlags(...)`
127
161
 
128
- `[user: FeatureFlareUserPayload | null, setUser: (user: FeatureFlareUserPayload) => void]`
162
+ Signatures:
129
163
 
130
- **Example:**
164
+ - `useBoolFlags(keys: string[], defaultValue?: boolean)` → `{ values, loading, errors }`
165
+ - `useBoolFlags(defaultValue?: boolean, options?: { refreshIntervalMs?: number; hiddenRefreshIntervalMs?: number; pauseWhenHidden?: boolean; enabled?: boolean })` → `{ values, loading, errors }` (all flags)
131
166
 
132
- ```typescript
133
- const [user, setUser] = useFeatureFlareUser();
134
- ```
167
+ Returns:
135
168
 
136
- ## Environment Variables
169
+ - `flags: Array<{ key: string; value: boolean }>`
170
+ - `loading: boolean`
171
+ - `error: string | null`
137
172
 
138
- The SDK automatically reads from environment variables if `sdkKey` is not provided:
173
+ ### `useFlag(key, defaultValue?)`
139
174
 
140
- - `FEATUREFLARE_CLIENT_KEY` - Client SDK key
175
+ Selector-based single-flag subscription. Components only re-render when that flag changes.
141
176
 
142
- ```typescript
143
- // This will use FEATUREFLARE_CLIENT_KEY from env if sdkKey is not provided
144
- <FeatureFlareProvider config={{}} initialUser={{ id: 'user-123' }}>
145
- {/* ... */}
146
- </FeatureFlareProvider>
147
- ```
177
+ ### `useFlagDiagnostics(key)`
148
178
 
149
- ## API Base URL
179
+ Returns per-flag runtime diagnostics (source, stale state, cache timestamps, latency).
150
180
 
151
- The SDK automatically uses `window.location.origin` in the browser (assumes API is on same origin). The API URL cannot be overridden.
181
+ Behavior:
152
182
 
153
- ## SDK Keys
183
+ - Immediate fetch on mount/user change.
184
+ - Shared polling via provider context (multiple consumers do not duplicate requests).
185
+ - Optional polling controls with hidden-tab awareness.
186
+ - If `user` is provided in input form, hook syncs provider user automatically.
154
187
 
155
- Use **client keys** for browser/mobile applications. Client keys are not secret and will be visible in your JavaScript bundle.
188
+ ## Notes
156
189
 
157
- Get your SDK keys from your FeatureFlare Console → Environments.
190
+ - Use client SDK keys in browser apps.
191
+ - If `config.sdkKey` is missing, skip initializing the provider.
158
192
 
159
- ## License
193
+ ## Security: Client-side flags are not authorization
160
194
 
161
- MIT
195
+ Client keys can be extracted from your frontend bundle. **Never gate truly sensitive operations solely with client-evaluated flags**—enforce authorization on your backend.
196
+ - `config.bootstrap?: { flags, killSwitches }` for SSR/HTML bootstrap to avoid first-render flicker
197
+ - `config.timeoutMs | maxRetries | backoffMs | jitter | cacheTtlMs | staleTtlMs | realtime` are forwarded to the SDK client