@featureflare/react 0.0.3 → 0.0.5-beta.186

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
@@ -1,6 +1,6 @@
1
1
  # @featureflare/react
2
2
 
3
- React hooks and provider for ShipIt feature flags.
3
+ React hooks and provider for FeatureFlare feature flags.
4
4
 
5
5
  ## Installation
6
6
 
@@ -12,147 +12,104 @@ 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 `ShipItProvider`:
17
+ ### Minimal Setup
25
18
 
26
19
  ```typescript
27
- import { ShipItProvider } 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
- <ShipItProvider
32
- config={{
33
- // SDK automatically uses window.location.origin in browser
34
- sdkKey: 'your-client-key-here' // Client key for browser
35
- }}
36
- initialUser={{ id: 'user-123' }}
37
- >
38
- {/* Your app */}
39
- </ShipItProvider>
41
+ <FeatureFlareProvider config={config} initialUser={{ id: 'user-123', key: 'user-123' }}>
42
+ <FlagsBootstrap />
43
+ {/* app */}
44
+ </FeatureFlareProvider>
40
45
  );
41
46
  }
42
47
  ```
43
48
 
44
- ### Read Flags
45
-
46
- Use the `useBoolFlag` hook to read boolean flags:
49
+ ### Read One Flag
47
50
 
48
51
  ```typescript
49
52
  import { useBoolFlag } from '@featureflare/react';
50
53
 
51
54
  export function NewNav() {
52
55
  const { value, loading, error } = useBoolFlag('new-nav', false);
53
-
54
- if (loading) return <div>Loading...</div>;
55
- if (error) return <div>Error: {error.message}</div>;
56
-
57
- return value ? <div>New nav ON</div> : <div>New nav OFF</div>;
58
- }
59
- ```
60
-
61
- ### Update User
62
56
 
63
- Use the `useShipItUser` hook to get and update the current user:
64
-
65
- ```typescript
66
- import { useShipItUser } from '@featureflare/react';
67
-
68
- export function UserSwitcher() {
69
- const [user, setUser] = useShipItUser();
57
+ if (loading) return <div>Loading...</div>;
58
+ if (error) return <div>Error: {error}</div>;
70
59
 
71
- return (
72
- <button
73
- onClick={() =>
74
- setUser({
75
- ...(user ?? { id: 'user-123' }),
76
- meta: { ...(user?.meta ?? {}), companyId: 'northwind' }
77
- })
78
- }
79
- >
80
- Switch user
81
- </button>
82
- );
60
+ return value ? <div>New nav ON</div> : <div>New nav OFF</div>;
83
61
  }
84
62
  ```
85
63
 
86
- ## API Reference
87
-
88
- ### `ShipItProvider`
89
-
90
- Provider component that wraps your app and provides the ShipIt client context.
64
+ ## API
91
65
 
92
- #### Props
66
+ ### `FeatureFlareProvider`
93
67
 
94
- - `config: ShipItReactConfig` - Configuration object
95
- - `sdkKey?: string` - Client SDK key (recommended). If not provided, reads from `SHIPIT_CLIENT_KEY` env var.
96
- - `projectKey?: string` - Legacy: project key (requires `envKey`). Not recommended.
97
- - `envKey?: string` - Environment key (default: `'production'`). Only used with `projectKey`.
98
- - `initialUser: ShipItUserPayload` - Initial user payload
99
- - `user?: ShipItUserPayload` - Controlled user (requires `onUserChange`)
100
- - `onUserChange?: (user: ShipItUserPayload) => void` - Callback for user changes (required if using controlled `user`)
101
- - `children: React.ReactNode` - Your app components
68
+ Props:
102
69
 
103
- ### `useBoolFlag(flagKey: string, defaultValue: boolean)`
70
+ - `config: FeatureFlareReactConfig`
71
+ - `initialUser: FeatureFlareUserPayload`
72
+ - `user?: FeatureFlareUserPayload` (controlled mode)
73
+ - `onUserChange?: (user: FeatureFlareUserPayload) => void` (required in controlled mode)
74
+ - `children: React.ReactNode`
104
75
 
105
- Hook to evaluate a boolean feature flag.
76
+ ### `resolveFeatureFlareBrowserConfig(input?)`
106
77
 
107
- **Returns:**
78
+ Builds provider config from common `NEXT_PUBLIC_FEATUREFLARE_*` vars.
108
79
 
109
- - `value: boolean` - The flag value
110
- - `loading: boolean` - Whether the evaluation is in progress
111
- - `error: Error | null` - Error if evaluation failed
112
-
113
- **Example:**
114
-
115
- ```typescript
116
- const { value, loading, error } = useBoolFlag('feature-flag', false);
117
- ```
80
+ Returns:
118
81
 
119
- ### `useShipItUser()`
82
+ - `apiBaseUrl?: string`
83
+ - `envKey: 'development' | 'staging' | 'production'`
84
+ - `sdkKey?: string`
120
85
 
121
- Hook to get and update the current user.
86
+ ### `useFeatureFlareUser()`
122
87
 
123
- **Returns:**
88
+ Returns:
124
89
 
125
- `[user: ShipItUserPayload | null, setUser: (user: ShipItUserPayload) => void]`
90
+ - `[user, setUser]`
126
91
 
127
- **Example:**
92
+ ### `useFlags(...)`
128
93
 
129
- ```typescript
130
- const [user, setUser] = useShipItUser();
131
- ```
132
-
133
- ## Environment Variables
134
-
135
- The SDK automatically reads from environment variables if `sdkKey` is not provided:
136
-
137
- - `SHIPIT_CLIENT_KEY` - Client SDK key
138
-
139
- ```typescript
140
- // This will use SHIPIT_CLIENT_KEY from env if sdkKey is not provided
141
- <ShipItProvider config={{}} initialUser={{ id: 'user-123' }}>
142
- {/* ... */}
143
- </ShipItProvider>
144
- ```
94
+ Signatures:
145
95
 
146
- ## API Base URL
96
+ - `useFlags(input?: { user?: FeatureFlareUserPayload; defaultValue?: boolean; refreshIntervalMs?: number; hiddenRefreshIntervalMs?: number; pauseWhenHidden?: boolean; enabled?: boolean })`
97
+ - `useFlags(defaultValue?: boolean, options?: { refreshIntervalMs?: number; hiddenRefreshIntervalMs?: number; pauseWhenHidden?: boolean; enabled?: boolean })`
147
98
 
148
- The SDK automatically uses `window.location.origin` in the browser (assumes API is on same origin). The API URL cannot be overridden.
99
+ Returns:
149
100
 
150
- ## SDK Keys
101
+ - `flags: Array<{ key: string; value: boolean }>`
102
+ - `loading: boolean`
103
+ - `error: string | null`
151
104
 
152
- Use **client keys** for browser/mobile applications. Client keys are not secret and will be visible in your JavaScript bundle.
105
+ Behavior:
153
106
 
154
- Get your SDK keys from your ShipIt Console → Environments.
107
+ - Immediate fetch on mount/user change.
108
+ - Shared polling via provider context (multiple consumers do not duplicate requests).
109
+ - Optional polling controls with hidden-tab awareness.
110
+ - If `user` is provided in input form, hook syncs provider user automatically.
155
111
 
156
- ## License
112
+ ## Notes
157
113
 
158
- MIT
114
+ - Use client SDK keys in browser apps.
115
+ - If `config.sdkKey` is missing, skip initializing the provider.
package/dist/index.cjs CHANGED
@@ -30,11 +30,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- ShipItProvider: () => ShipItProvider,
33
+ FeatureFlareProvider: () => FeatureFlareProvider,
34
+ resolveFeatureFlareBrowserConfig: () => resolveFeatureFlareBrowserConfig,
34
35
  useBoolFlag: () => useBoolFlag,
35
36
  useBoolFlags: () => useBoolFlags,
36
- useShipItContext: () => useShipItContext,
37
- useShipItUser: () => useShipItUser
37
+ useFeatureFlareContext: () => useFeatureFlareContext,
38
+ useFeatureFlareUser: () => useFeatureFlareUser,
39
+ useFlags: () => useFlags
38
40
  });
39
41
  module.exports = __toCommonJS(index_exports);
40
42
 
@@ -42,38 +44,245 @@ module.exports = __toCommonJS(index_exports);
42
44
  var import_react = __toESM(require("react"), 1);
43
45
  var import_sdk_js = require("@featureflare/sdk-js");
44
46
  var import_jsx_runtime = require("react/jsx-runtime");
45
- var ShipItContext = (0, import_react.createContext)(null);
46
- function ShipItProvider(props) {
47
+ function resolveFeatureFlareBrowserConfig(input) {
48
+ const explicitEnv = input?.envKey;
49
+ const envFromVars = typeof process !== "undefined" ? process.env.NEXT_PUBLIC_FEATUREFLARE_ENV_KEY?.trim().toLowerCase() : "";
50
+ const resolvedEnv = explicitEnv ?? (envFromVars === "development" || envFromVars === "staging" || envFromVars === "production" ? envFromVars : typeof process !== "undefined" && process.env.NODE_ENV === "production" ? "production" : "development");
51
+ const apiBaseUrl = input?.apiBaseUrl ?? (typeof process !== "undefined" ? process.env.NEXT_PUBLIC_FEATUREFLARE_API_BASE_URL?.trim() : void 0);
52
+ const sdkKeyDevelopment = (typeof process !== "undefined" ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY_DEVELOPMENT?.trim() : "") || (typeof process !== "undefined" ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY_DEV?.trim() : "") || "";
53
+ const sdkKeyStaging = (typeof process !== "undefined" ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY_STAGING?.trim() : "") || "";
54
+ const sdkKeyProduction = (typeof process !== "undefined" ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY_PRODUCTION?.trim() : "") || (typeof process !== "undefined" ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY_PROD?.trim() : "") || "";
55
+ const sdkKeyDefault = (typeof process !== "undefined" ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY?.trim() : "") || "";
56
+ const sdkKey = (resolvedEnv === "development" ? sdkKeyDevelopment : "") || (resolvedEnv === "staging" ? sdkKeyStaging : "") || (resolvedEnv === "production" ? sdkKeyProduction : "") || sdkKeyDefault || void 0;
57
+ return {
58
+ apiBaseUrl,
59
+ envKey: resolvedEnv,
60
+ sdkKey
61
+ };
62
+ }
63
+ function normalizeSubscriptionOptions(options) {
64
+ const refreshIntervalMs = Number.isFinite(options?.refreshIntervalMs) && (options?.refreshIntervalMs ?? 0) > 0 ? Number(options?.refreshIntervalMs) : 1e4;
65
+ const hiddenRefreshIntervalMs = Number.isFinite(options?.hiddenRefreshIntervalMs) && (options?.hiddenRefreshIntervalMs ?? 0) > 0 ? Number(options?.hiddenRefreshIntervalMs) : Math.max(refreshIntervalMs * 6, 6e4);
66
+ return {
67
+ refreshIntervalMs,
68
+ hiddenRefreshIntervalMs,
69
+ pauseWhenHidden: options?.pauseWhenHidden ?? true,
70
+ enabled: options?.enabled ?? true
71
+ };
72
+ }
73
+ function createFlagsStore(client, getUser) {
74
+ const entries = /* @__PURE__ */ new Map();
75
+ let nextSubscriberId = 1;
76
+ const isHidden = () => typeof document !== "undefined" && document.visibilityState === "hidden";
77
+ const getEntry = (defaultValue) => {
78
+ const key = defaultValue ? "1" : "0";
79
+ const existing = entries.get(key);
80
+ if (existing) return existing;
81
+ const created = {
82
+ defaultValue,
83
+ snapshot: { flags: [], loading: true, error: null },
84
+ listeners: /* @__PURE__ */ new Set(),
85
+ subscribers: /* @__PURE__ */ new Map(),
86
+ timer: null,
87
+ inFlight: false
88
+ };
89
+ entries.set(key, created);
90
+ return created;
91
+ };
92
+ const emit = (entry) => {
93
+ for (const listener of entry.listeners) listener();
94
+ };
95
+ const getEffectiveOptions = (entry) => {
96
+ const active = [...entry.subscribers.values()].filter((s) => s.enabled);
97
+ if (active.length === 0) {
98
+ return { enabled: false, refreshIntervalMs: 0, hiddenRefreshIntervalMs: 0, pauseWhenHidden: true };
99
+ }
100
+ const refreshIntervalMs = active.reduce((min, s) => Math.min(min, s.refreshIntervalMs), Number.POSITIVE_INFINITY);
101
+ const hiddenActive = active.filter((s) => !s.pauseWhenHidden);
102
+ const hiddenRefreshIntervalMs = hiddenActive.length > 0 ? hiddenActive.reduce((min, s) => Math.min(min, s.hiddenRefreshIntervalMs), Number.POSITIVE_INFINITY) : 0;
103
+ return {
104
+ enabled: true,
105
+ refreshIntervalMs: Number.isFinite(refreshIntervalMs) ? refreshIntervalMs : 1e4,
106
+ hiddenRefreshIntervalMs,
107
+ pauseWhenHidden: hiddenActive.length === 0
108
+ };
109
+ };
110
+ const schedule = (entry) => {
111
+ if (entry.timer !== null) {
112
+ clearTimeout(entry.timer);
113
+ entry.timer = null;
114
+ }
115
+ const effective = getEffectiveOptions(entry);
116
+ if (!effective.enabled) return;
117
+ if (isHidden()) {
118
+ if (effective.pauseWhenHidden) return;
119
+ entry.timer = setTimeout(() => {
120
+ void refresh(entry.defaultValue);
121
+ }, effective.hiddenRefreshIntervalMs);
122
+ return;
123
+ }
124
+ entry.timer = setTimeout(() => {
125
+ void refresh(entry.defaultValue);
126
+ }, effective.refreshIntervalMs);
127
+ };
128
+ const refresh = async (defaultValue, force = false) => {
129
+ const entry = getEntry(defaultValue);
130
+ const effective = getEffectiveOptions(entry);
131
+ if (!effective.enabled && !force) return;
132
+ if (!force && isHidden() && effective.pauseWhenHidden) {
133
+ schedule(entry);
134
+ return;
135
+ }
136
+ if (entry.inFlight) return;
137
+ entry.inFlight = true;
138
+ try {
139
+ const flags = await client.flags(getUser(), defaultValue);
140
+ entry.snapshot = { flags, loading: false, error: null };
141
+ emit(entry);
142
+ } catch (error) {
143
+ const message = error instanceof Error ? error.message : String(error);
144
+ entry.snapshot = { ...entry.snapshot, loading: false, error: message };
145
+ emit(entry);
146
+ } finally {
147
+ entry.inFlight = false;
148
+ schedule(entry);
149
+ }
150
+ };
151
+ const refreshNow = (defaultValue) => {
152
+ const entry = getEntry(defaultValue);
153
+ entry.snapshot = { ...entry.snapshot, loading: true, error: null };
154
+ emit(entry);
155
+ void refresh(defaultValue, true);
156
+ };
157
+ const subscribe = (defaultValue, listener, options) => {
158
+ const entry = getEntry(defaultValue);
159
+ const subscriberId = nextSubscriberId;
160
+ nextSubscriberId += 1;
161
+ entry.listeners.add(listener);
162
+ entry.subscribers.set(subscriberId, normalizeSubscriptionOptions(options));
163
+ const effective = getEffectiveOptions(entry);
164
+ if (effective.enabled && !entry.inFlight && entry.snapshot.loading) {
165
+ void refresh(defaultValue);
166
+ } else {
167
+ schedule(entry);
168
+ }
169
+ return () => {
170
+ entry.listeners.delete(listener);
171
+ entry.subscribers.delete(subscriberId);
172
+ schedule(entry);
173
+ };
174
+ };
175
+ const updateUser = () => {
176
+ for (const entry of entries.values()) {
177
+ entry.snapshot = { ...entry.snapshot, loading: true, error: null };
178
+ emit(entry);
179
+ void refresh(entry.defaultValue);
180
+ }
181
+ };
182
+ const handleVisibilityChange = () => {
183
+ if (isHidden()) return;
184
+ for (const entry of entries.values()) {
185
+ const effective = getEffectiveOptions(entry);
186
+ if (!effective.enabled) continue;
187
+ void refresh(entry.defaultValue);
188
+ }
189
+ };
190
+ if (typeof document !== "undefined") {
191
+ document.addEventListener("visibilitychange", handleVisibilityChange);
192
+ }
193
+ const dispose = () => {
194
+ if (typeof document !== "undefined") {
195
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
196
+ }
197
+ for (const entry of entries.values()) {
198
+ if (entry.timer !== null) {
199
+ clearTimeout(entry.timer);
200
+ }
201
+ entry.timer = null;
202
+ entry.listeners.clear();
203
+ entry.subscribers.clear();
204
+ }
205
+ entries.clear();
206
+ };
207
+ return {
208
+ getState(defaultValue) {
209
+ return getEntry(defaultValue).snapshot;
210
+ },
211
+ refreshNow,
212
+ subscribe,
213
+ updateUser,
214
+ dispose
215
+ };
216
+ }
217
+ var FeatureFlareContext = (0, import_react.createContext)(null);
218
+ function FeatureFlareProvider(props) {
47
219
  if (props.user && !props.onUserChange) {
48
- throw new Error("ShipItProvider: when providing `user`, also provide `onUserChange` (controlled mode).");
220
+ throw new Error("FeatureFlareProvider: when providing `user`, also provide `onUserChange` (controlled mode).");
49
221
  }
50
222
  const [internalUser, setInternalUser] = (0, import_react.useState)(props.initialUser);
51
223
  const user = props.user ?? internalUser;
52
224
  const setUser = props.onUserChange ?? setInternalUser;
225
+ const userRef = (0, import_react.useRef)(user);
53
226
  const client = (0, import_react.useMemo)(() => {
54
- return new import_sdk_js.ShipItClient({
227
+ return new import_sdk_js.FeatureFlareClient({
228
+ apiBaseUrl: props.config.apiBaseUrl,
55
229
  sdkKey: props.config.sdkKey,
56
230
  projectKey: props.config.projectKey,
57
231
  envKey: props.config.envKey
58
232
  });
59
- }, [props.config.envKey, props.config.projectKey, props.config.sdkKey]);
60
- const value = (0, import_react.useMemo)(() => ({ client, user, setUser }), [client, user]);
61
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShipItContext.Provider, { value, children: props.children });
233
+ }, [
234
+ props.config.apiBaseUrl,
235
+ props.config.envKey,
236
+ props.config.projectKey,
237
+ props.config.sdkKey
238
+ ]);
239
+ const flagsStore = (0, import_react.useMemo)(() => createFlagsStore(client, () => userRef.current), [client]);
240
+ (0, import_react.useEffect)(() => {
241
+ userRef.current = user;
242
+ flagsStore.updateUser();
243
+ }, [flagsStore, user]);
244
+ (0, import_react.useEffect)(() => {
245
+ return () => {
246
+ flagsStore.dispose();
247
+ };
248
+ }, [flagsStore]);
249
+ const value = (0, import_react.useMemo)(
250
+ () => ({
251
+ client,
252
+ user,
253
+ setUser,
254
+ getFlagsState: flagsStore.getState,
255
+ refreshFlags: flagsStore.refreshNow,
256
+ subscribeFlags: flagsStore.subscribe
257
+ }),
258
+ [client, flagsStore, setUser, user]
259
+ );
260
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FeatureFlareContext.Provider, { value, children: props.children });
62
261
  }
63
- function useShipItContext() {
64
- const ctx = import_react.default.useContext(ShipItContext);
65
- if (!ctx) throw new Error("useShipItContext must be used within <ShipItProvider>.");
262
+ function useFeatureFlareContext() {
263
+ const ctx = import_react.default.useContext(FeatureFlareContext);
264
+ if (!ctx) throw new Error("useFeatureFlareContext must be used within <FeatureFlareProvider>.");
66
265
  return ctx;
67
266
  }
68
267
 
69
268
  // src/hooks.ts
70
269
  var import_react2 = require("react");
71
- function useShipItUser() {
72
- const { user, setUser } = useShipItContext();
270
+ var EMPTY_FLAGS_STATE = { flags: [], loading: true, error: null };
271
+ function userFingerprint(user) {
272
+ if (!user) return "";
273
+ return JSON.stringify({
274
+ id: user.id ?? "",
275
+ key: user.key ?? "",
276
+ email: user.email ?? "",
277
+ meta: user.meta ?? {}
278
+ });
279
+ }
280
+ function useFeatureFlareUser() {
281
+ const { user, setUser } = useFeatureFlareContext();
73
282
  return [user, setUser];
74
283
  }
75
284
  function useBoolFlag(flagKey, defaultValue = false) {
76
- const { client, user } = useShipItContext();
285
+ const { client, user } = useFeatureFlareContext();
77
286
  const [state, setState] = (0, import_react2.useState)({ value: defaultValue, loading: true, error: null });
78
287
  const userId = user.id ?? user.key ?? "";
79
288
  const key = (0, import_react2.useMemo)(() => `${flagKey}:${userId}`, [flagKey, userId]);
@@ -103,7 +312,7 @@ function useBoolFlag(flagKey, defaultValue = false) {
103
312
  return state;
104
313
  }
105
314
  function useBoolFlags(flagKeys, defaultValue = false) {
106
- const { client, user } = useShipItContext();
315
+ const { client, user } = useFeatureFlareContext();
107
316
  const sortedKeys = (0, import_react2.useMemo)(() => [...flagKeys].map((k) => k.trim()).filter(Boolean).sort(), [flagKeys]);
108
317
  const userId = user.id ?? user.key ?? "";
109
318
  const key = (0, import_react2.useMemo)(() => `${sortedKeys.join(",")}:${userId}`, [sortedKeys, userId]);
@@ -137,12 +346,50 @@ function useBoolFlags(flagKeys, defaultValue = false) {
137
346
  }, [client, defaultValue, key, sortedKeys, user]);
138
347
  return state;
139
348
  }
349
+ function useFlags(defaultValueOrInput = false, options = {}) {
350
+ const { subscribeFlags, getFlagsState, refreshFlags, setUser } = useFeatureFlareContext();
351
+ const parsed = (0, import_react2.useMemo)(() => {
352
+ if (typeof defaultValueOrInput === "boolean") {
353
+ return { ...options, defaultValue: defaultValueOrInput };
354
+ }
355
+ return defaultValueOrInput ?? {};
356
+ }, [defaultValueOrInput, options]);
357
+ const defaultValue = parsed.defaultValue ?? false;
358
+ const normalizedOptions = (0, import_react2.useMemo)(
359
+ () => ({
360
+ enabled: parsed.enabled ?? true,
361
+ refreshIntervalMs: parsed.refreshIntervalMs,
362
+ hiddenRefreshIntervalMs: parsed.hiddenRefreshIntervalMs,
363
+ pauseWhenHidden: parsed.pauseWhenHidden ?? true
364
+ }),
365
+ [parsed.enabled, parsed.hiddenRefreshIntervalMs, parsed.pauseWhenHidden, parsed.refreshIntervalMs]
366
+ );
367
+ const appliedUserRef = (0, import_react2.useRef)("");
368
+ const parsedUserFingerprint = (0, import_react2.useMemo)(() => userFingerprint(parsed.user), [parsed.user]);
369
+ (0, import_react2.useEffect)(() => {
370
+ if (!parsed.user) return;
371
+ if (appliedUserRef.current === parsedUserFingerprint) return;
372
+ setUser(parsed.user);
373
+ appliedUserRef.current = parsedUserFingerprint;
374
+ }, [parsed.user, parsedUserFingerprint, setUser]);
375
+ (0, import_react2.useEffect)(() => {
376
+ if (normalizedOptions.enabled === false) return;
377
+ refreshFlags(defaultValue);
378
+ }, [defaultValue, normalizedOptions.enabled, parsedUserFingerprint, refreshFlags]);
379
+ const subscribe = (0, import_react2.useMemo)(
380
+ () => (onStoreChange) => subscribeFlags(defaultValue, onStoreChange, normalizedOptions),
381
+ [defaultValue, normalizedOptions, subscribeFlags]
382
+ );
383
+ return (0, import_react2.useSyncExternalStore)(subscribe, () => getFlagsState(defaultValue), () => EMPTY_FLAGS_STATE);
384
+ }
140
385
  // Annotate the CommonJS export names for ESM import in node:
141
386
  0 && (module.exports = {
142
- ShipItProvider,
387
+ FeatureFlareProvider,
388
+ resolveFeatureFlareBrowserConfig,
143
389
  useBoolFlag,
144
390
  useBoolFlags,
145
- useShipItContext,
146
- useShipItUser
391
+ useFeatureFlareContext,
392
+ useFeatureFlareUser,
393
+ useFlags
147
394
  });
148
395
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/provider.tsx","../src/hooks.ts"],"sourcesContent":["export * from './provider.js';\nexport * from './hooks.js';\nexport type { ShipItUserPayload } from '@featureflare/sdk-js';\n","import React, { createContext, useMemo, useState } from 'react';\nimport { ShipItClient, type ShipItUserPayload } from '@featureflare/sdk-js';\n\ntype ShipItReactConfig = {\n /** Recommended: use a client key (featureflare_cli_...). */\n sdkKey?: string;\n /** Legacy/insecure browser mode: uses /api/v1/eval (no sdkKey). */\n projectKey?: string;\n envKey?: string;\n};\n\ntype ShipItContextValue = {\n client: ShipItClient;\n user: ShipItUserPayload;\n setUser: (next: ShipItUserPayload) => void;\n};\n\nconst ShipItContext = createContext<ShipItContextValue | null>(null);\n\nexport function ShipItProvider(props: {\n config: ShipItReactConfig;\n initialUser: ShipItUserPayload;\n user?: ShipItUserPayload;\n onUserChange?: (next: ShipItUserPayload) => void;\n children: React.ReactNode;\n}) {\n if (props.user && !props.onUserChange) {\n throw new Error('ShipItProvider: when providing `user`, also provide `onUserChange` (controlled mode).');\n }\n\n const [internalUser, setInternalUser] = useState<ShipItUserPayload>(props.initialUser);\n const user = props.user ?? internalUser;\n const setUser = props.onUserChange ?? setInternalUser;\n\n const client = useMemo(() => {\n return new ShipItClient({\n sdkKey: props.config.sdkKey,\n projectKey: props.config.projectKey,\n envKey: props.config.envKey\n });\n }, [props.config.envKey, props.config.projectKey, props.config.sdkKey]);\n\n const value = useMemo(() => ({ client, user, setUser }), [client, user]);\n return <ShipItContext.Provider value={value}>{props.children}</ShipItContext.Provider>;\n}\n\nexport function useShipItContext(): ShipItContextValue {\n const ctx = React.useContext(ShipItContext);\n if (!ctx) throw new Error('useShipItContext must be used within <ShipItProvider>.');\n return ctx;\n}\n","import { useEffect, useMemo, useRef, useState } from 'react';\nimport type { ShipItUserPayload } from '@featureflare/sdk-js';\nimport { useShipItContext } from './provider.js';\n\ntype BoolFlagState = {\n value: boolean;\n loading: boolean;\n error: string | null;\n};\n\ntype BoolFlagsState = {\n values: Record<string, boolean>;\n loading: boolean;\n errors: Record<string, string>;\n};\n\nexport function useShipItUser(): [ShipItUserPayload, (next: ShipItUserPayload) => void] {\n const { user, setUser } = useShipItContext();\n return [user, setUser];\n}\n\nexport function useBoolFlag(flagKey: string, defaultValue = false): BoolFlagState {\n const { client, user } = useShipItContext();\n const [state, setState] = useState<BoolFlagState>({ value: defaultValue, loading: true, error: null });\n const userId = user.id ?? user.key ?? '';\n const key = useMemo(() => `${flagKey}:${userId}`, [flagKey, userId]);\n const lastKey = useRef<string>('');\n\n useEffect(() => {\n let cancelled = false;\n const nextKey = key;\n lastKey.current = nextKey;\n setState((s) => ({ ...s, loading: true, error: null }));\n\n (async () => {\n try {\n const v = await client.bool(flagKey, user, defaultValue);\n if (cancelled) return;\n if (lastKey.current !== nextKey) return;\n setState({ value: v, loading: false, error: null });\n } catch (e) {\n if (cancelled) return;\n if (lastKey.current !== nextKey) return;\n const msg = e instanceof Error ? e.message : String(e);\n setState({ value: defaultValue, loading: false, error: msg });\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [client, defaultValue, flagKey, key, user]);\n\n return state;\n}\n\nexport function useBoolFlags(flagKeys: string[], defaultValue = false): BoolFlagsState {\n const { client, user } = useShipItContext();\n const sortedKeys = useMemo(() => [...flagKeys].map((k) => k.trim()).filter(Boolean).sort(), [flagKeys]);\n const userId = user.id ?? user.key ?? '';\n const key = useMemo(() => `${sortedKeys.join(',')}:${userId}`, [sortedKeys, userId]);\n const [state, setState] = useState<BoolFlagsState>({ values: {}, loading: true, errors: {} });\n const lastKey = useRef<string>('');\n\n useEffect(() => {\n let cancelled = false;\n const nextKey = key;\n lastKey.current = nextKey;\n setState({ values: {}, loading: true, errors: {} });\n\n (async () => {\n const values: Record<string, boolean> = {};\n const errors: Record<string, string> = {};\n await Promise.all(\n sortedKeys.map(async (flagKey) => {\n try {\n values[flagKey] = await client.bool(flagKey, user, defaultValue);\n } catch (e) {\n values[flagKey] = defaultValue;\n errors[flagKey] = e instanceof Error ? e.message : String(e);\n }\n })\n );\n\n if (cancelled) return;\n if (lastKey.current !== nextKey) return;\n setState({ values, loading: false, errors });\n })();\n\n return () => {\n cancelled = true;\n };\n }, [client, defaultValue, key, sortedKeys, user]);\n\n return state;\n}\n"],"mappings":"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","names":["React","import_react"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/provider.tsx","../src/hooks.ts"],"sourcesContent":["export * from './provider.js';\nexport * from './hooks.js';\nexport type { FeatureFlareUserPayload } from '@featureflare/sdk-js';\n","import React, { createContext, useEffect, useMemo, useRef, useState } from 'react';\nimport { FeatureFlareClient, type FeatureFlareUserPayload } from '@featureflare/sdk-js';\n\nexport type FeatureFlareEnvironmentKey = 'development' | 'staging' | 'production';\n\nexport type FeatureFlareReactConfig = {\n /** Optional: explicit FeatureFlare API base URL. */\n apiBaseUrl?: string;\n /** Recommended: use a client key (featureflare_cli_...). */\n sdkKey?: string;\n /** Legacy/insecure browser mode: uses /api/v1/eval (no sdkKey). */\n projectKey?: string;\n envKey?: FeatureFlareEnvironmentKey | string;\n};\n\nexport function resolveFeatureFlareBrowserConfig(input?: {\n envKey?: FeatureFlareEnvironmentKey;\n apiBaseUrl?: string;\n}): FeatureFlareReactConfig {\n const explicitEnv = input?.envKey;\n const envFromVars =\n typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_FEATUREFLARE_ENV_KEY?.trim().toLowerCase() : '';\n\n const resolvedEnv: FeatureFlareEnvironmentKey =\n explicitEnv ??\n (envFromVars === 'development' || envFromVars === 'staging' || envFromVars === 'production'\n ? envFromVars\n : typeof process !== 'undefined' && process.env.NODE_ENV === 'production'\n ? 'production'\n : 'development');\n\n const apiBaseUrl =\n input?.apiBaseUrl ??\n (typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_FEATUREFLARE_API_BASE_URL?.trim() : undefined);\n\n const sdkKeyDevelopment =\n (typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY_DEVELOPMENT?.trim() : '') ||\n (typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY_DEV?.trim() : '') ||\n '';\n const sdkKeyStaging =\n (typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY_STAGING?.trim() : '') || '';\n const sdkKeyProduction =\n (typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY_PRODUCTION?.trim() : '') ||\n (typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY_PROD?.trim() : '') ||\n '';\n const sdkKeyDefault =\n (typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY?.trim() : '') || '';\n\n const sdkKey =\n (resolvedEnv === 'development' ? sdkKeyDevelopment : '') ||\n (resolvedEnv === 'staging' ? sdkKeyStaging : '') ||\n (resolvedEnv === 'production' ? sdkKeyProduction : '') ||\n sdkKeyDefault ||\n undefined;\n\n return {\n apiBaseUrl,\n envKey: resolvedEnv,\n sdkKey\n };\n}\n\ntype FeatureFlareContextValue = {\n client: FeatureFlareClient;\n user: FeatureFlareUserPayload;\n setUser: (next: FeatureFlareUserPayload) => void;\n getFlagsState: (defaultValue: boolean) => FlagsState;\n refreshFlags: (defaultValue: boolean) => void;\n subscribeFlags: (\n defaultValue: boolean,\n listener: () => void,\n options?: FlagsSubscriptionOptions\n ) => () => void;\n};\n\nexport type FlagsState = {\n flags: Array<{ key: string; value: boolean }>;\n loading: boolean;\n error: string | null;\n};\n\nexport type FlagsSubscriptionOptions = {\n refreshIntervalMs?: number;\n hiddenRefreshIntervalMs?: number;\n pauseWhenHidden?: boolean;\n enabled?: boolean;\n};\n\ntype NormalizedFlagsSubscriptionOptions = {\n refreshIntervalMs: number;\n hiddenRefreshIntervalMs: number;\n pauseWhenHidden: boolean;\n enabled: boolean;\n};\n\ntype FlagsStoreEntry = {\n defaultValue: boolean;\n snapshot: FlagsState;\n listeners: Set<() => void>;\n subscribers: Map<number, NormalizedFlagsSubscriptionOptions>;\n timer: ReturnType<typeof setTimeout> | null;\n inFlight: boolean;\n};\n\nfunction normalizeSubscriptionOptions(options?: FlagsSubscriptionOptions): NormalizedFlagsSubscriptionOptions {\n const refreshIntervalMs =\n Number.isFinite(options?.refreshIntervalMs) && (options?.refreshIntervalMs ?? 0) > 0\n ? Number(options?.refreshIntervalMs)\n : 10000;\n const hiddenRefreshIntervalMs =\n Number.isFinite(options?.hiddenRefreshIntervalMs) && (options?.hiddenRefreshIntervalMs ?? 0) > 0\n ? Number(options?.hiddenRefreshIntervalMs)\n : Math.max(refreshIntervalMs * 6, 60000);\n\n return {\n refreshIntervalMs,\n hiddenRefreshIntervalMs,\n pauseWhenHidden: options?.pauseWhenHidden ?? true,\n enabled: options?.enabled ?? true\n };\n}\n\nfunction createFlagsStore(client: FeatureFlareClient, getUser: () => FeatureFlareUserPayload) {\n const entries = new Map<string, FlagsStoreEntry>();\n let nextSubscriberId = 1;\n\n const isHidden = () => typeof document !== 'undefined' && document.visibilityState === 'hidden';\n\n const getEntry = (defaultValue: boolean): FlagsStoreEntry => {\n const key = defaultValue ? '1' : '0';\n const existing = entries.get(key);\n if (existing) return existing;\n const created: FlagsStoreEntry = {\n defaultValue,\n snapshot: { flags: [], loading: true, error: null },\n listeners: new Set(),\n subscribers: new Map(),\n timer: null,\n inFlight: false\n };\n entries.set(key, created);\n return created;\n };\n\n const emit = (entry: FlagsStoreEntry) => {\n for (const listener of entry.listeners) listener();\n };\n\n const getEffectiveOptions = (entry: FlagsStoreEntry) => {\n const active = [...entry.subscribers.values()].filter((s) => s.enabled);\n if (active.length === 0) {\n return { enabled: false as const, refreshIntervalMs: 0, hiddenRefreshIntervalMs: 0, pauseWhenHidden: true };\n }\n\n const refreshIntervalMs = active.reduce((min, s) => Math.min(min, s.refreshIntervalMs), Number.POSITIVE_INFINITY);\n const hiddenActive = active.filter((s) => !s.pauseWhenHidden);\n const hiddenRefreshIntervalMs =\n hiddenActive.length > 0\n ? hiddenActive.reduce((min, s) => Math.min(min, s.hiddenRefreshIntervalMs), Number.POSITIVE_INFINITY)\n : 0;\n\n return {\n enabled: true as const,\n refreshIntervalMs: Number.isFinite(refreshIntervalMs) ? refreshIntervalMs : 10000,\n hiddenRefreshIntervalMs,\n pauseWhenHidden: hiddenActive.length === 0\n };\n };\n\n const schedule = (entry: FlagsStoreEntry) => {\n if (entry.timer !== null) {\n clearTimeout(entry.timer);\n entry.timer = null;\n }\n\n const effective = getEffectiveOptions(entry);\n if (!effective.enabled) return;\n\n if (isHidden()) {\n if (effective.pauseWhenHidden) return;\n entry.timer = setTimeout(() => {\n void refresh(entry.defaultValue);\n }, effective.hiddenRefreshIntervalMs);\n return;\n }\n\n entry.timer = setTimeout(() => {\n void refresh(entry.defaultValue);\n }, effective.refreshIntervalMs);\n };\n\n const refresh = async (defaultValue: boolean, force = false) => {\n const entry = getEntry(defaultValue);\n const effective = getEffectiveOptions(entry);\n if (!effective.enabled && !force) return;\n\n if (!force && isHidden() && effective.pauseWhenHidden) {\n schedule(entry);\n return;\n }\n\n if (entry.inFlight) return;\n entry.inFlight = true;\n\n try {\n const flags = await client.flags(getUser(), defaultValue);\n entry.snapshot = { flags, loading: false, error: null };\n emit(entry);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n entry.snapshot = { ...entry.snapshot, loading: false, error: message };\n emit(entry);\n } finally {\n entry.inFlight = false;\n schedule(entry);\n }\n };\n\n const refreshNow = (defaultValue: boolean) => {\n const entry = getEntry(defaultValue);\n entry.snapshot = { ...entry.snapshot, loading: true, error: null };\n emit(entry);\n void refresh(defaultValue, true);\n };\n\n const subscribe = (\n defaultValue: boolean,\n listener: () => void,\n options?: FlagsSubscriptionOptions\n ): (() => void) => {\n const entry = getEntry(defaultValue);\n const subscriberId = nextSubscriberId;\n nextSubscriberId += 1;\n\n entry.listeners.add(listener);\n entry.subscribers.set(subscriberId, normalizeSubscriptionOptions(options));\n\n const effective = getEffectiveOptions(entry);\n if (effective.enabled && !entry.inFlight && entry.snapshot.loading) {\n void refresh(defaultValue);\n } else {\n schedule(entry);\n }\n\n return () => {\n entry.listeners.delete(listener);\n entry.subscribers.delete(subscriberId);\n schedule(entry);\n };\n };\n\n const updateUser = () => {\n for (const entry of entries.values()) {\n entry.snapshot = { ...entry.snapshot, loading: true, error: null };\n emit(entry);\n void refresh(entry.defaultValue);\n }\n };\n\n const handleVisibilityChange = () => {\n if (isHidden()) return;\n for (const entry of entries.values()) {\n const effective = getEffectiveOptions(entry);\n if (!effective.enabled) continue;\n void refresh(entry.defaultValue);\n }\n };\n\n if (typeof document !== 'undefined') {\n document.addEventListener('visibilitychange', handleVisibilityChange);\n }\n\n const dispose = () => {\n if (typeof document !== 'undefined') {\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n }\n for (const entry of entries.values()) {\n if (entry.timer !== null) {\n clearTimeout(entry.timer);\n }\n entry.timer = null;\n entry.listeners.clear();\n entry.subscribers.clear();\n }\n entries.clear();\n };\n\n return {\n getState(defaultValue: boolean): FlagsState {\n return getEntry(defaultValue).snapshot;\n },\n refreshNow,\n subscribe,\n updateUser,\n dispose\n };\n}\n\nconst FeatureFlareContext = createContext<FeatureFlareContextValue | null>(null);\n\nexport function FeatureFlareProvider(props: {\n config: FeatureFlareReactConfig;\n initialUser: FeatureFlareUserPayload;\n user?: FeatureFlareUserPayload;\n onUserChange?: (next: FeatureFlareUserPayload) => void;\n children: React.ReactNode;\n}) {\n if (props.user && !props.onUserChange) {\n throw new Error('FeatureFlareProvider: when providing `user`, also provide `onUserChange` (controlled mode).');\n }\n\n const [internalUser, setInternalUser] = useState<FeatureFlareUserPayload>(props.initialUser);\n const user = props.user ?? internalUser;\n const setUser = props.onUserChange ?? setInternalUser;\n const userRef = useRef<FeatureFlareUserPayload>(user);\n\n const client = useMemo(() => {\n return new FeatureFlareClient({\n apiBaseUrl: props.config.apiBaseUrl,\n sdkKey: props.config.sdkKey,\n projectKey: props.config.projectKey,\n envKey: props.config.envKey\n });\n }, [\n props.config.apiBaseUrl,\n props.config.envKey,\n props.config.projectKey,\n props.config.sdkKey\n ]);\n\n const flagsStore = useMemo(() => createFlagsStore(client, () => userRef.current), [client]);\n\n useEffect(() => {\n userRef.current = user;\n flagsStore.updateUser();\n }, [flagsStore, user]);\n\n useEffect(() => {\n return () => {\n flagsStore.dispose();\n };\n }, [flagsStore]);\n\n const value = useMemo(\n () => ({\n client,\n user,\n setUser,\n getFlagsState: flagsStore.getState,\n refreshFlags: flagsStore.refreshNow,\n subscribeFlags: flagsStore.subscribe\n }),\n [client, flagsStore, setUser, user]\n );\n return <FeatureFlareContext.Provider value={value}>{props.children}</FeatureFlareContext.Provider>;\n}\n\nexport function useFeatureFlareContext(): FeatureFlareContextValue {\n const ctx = React.useContext(FeatureFlareContext);\n if (!ctx) throw new Error('useFeatureFlareContext must be used within <FeatureFlareProvider>.');\n return ctx;\n}\n","import { useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';\nimport type { FeatureFlareUserPayload } from '@featureflare/sdk-js';\nimport { useFeatureFlareContext, type FlagsSubscriptionOptions } from './provider.js';\n\ntype BoolFlagState = {\n value: boolean;\n loading: boolean;\n error: string | null;\n};\n\ntype BoolFlagsState = {\n values: Record<string, boolean>;\n loading: boolean;\n errors: Record<string, string>;\n};\n\ntype FlagsState = {\n flags: Array<{ key: string; value: boolean }>;\n loading: boolean;\n error: string | null;\n};\n\ntype UseFlagsOptions = FlagsSubscriptionOptions;\n\ntype UseFlagsInput = UseFlagsOptions & {\n defaultValue?: boolean;\n user?: FeatureFlareUserPayload;\n};\n\nconst EMPTY_FLAGS_STATE: FlagsState = { flags: [], loading: true, error: null };\n\nfunction userFingerprint(user?: FeatureFlareUserPayload): string {\n if (!user) return '';\n return JSON.stringify({\n id: user.id ?? '',\n key: user.key ?? '',\n email: user.email ?? '',\n meta: user.meta ?? {}\n });\n}\n\nexport function useFeatureFlareUser(): [FeatureFlareUserPayload, (next: FeatureFlareUserPayload) => void] {\n const { user, setUser } = useFeatureFlareContext();\n return [user, setUser];\n}\n\nexport function useBoolFlag(flagKey: string, defaultValue = false): BoolFlagState {\n const { client, user } = useFeatureFlareContext();\n const [state, setState] = useState<BoolFlagState>({ value: defaultValue, loading: true, error: null });\n const userId = user.id ?? user.key ?? '';\n const key = useMemo(() => `${flagKey}:${userId}`, [flagKey, userId]);\n const lastKey = useRef<string>('');\n\n useEffect(() => {\n let cancelled = false;\n const nextKey = key;\n lastKey.current = nextKey;\n setState((s) => ({ ...s, loading: true, error: null }));\n\n (async () => {\n try {\n const v = await client.bool(flagKey, user, defaultValue);\n if (cancelled) return;\n if (lastKey.current !== nextKey) return;\n setState({ value: v, loading: false, error: null });\n } catch (e) {\n if (cancelled) return;\n if (lastKey.current !== nextKey) return;\n const msg = e instanceof Error ? e.message : String(e);\n setState({ value: defaultValue, loading: false, error: msg });\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [client, defaultValue, flagKey, key, user]);\n\n return state;\n}\n\nexport function useBoolFlags(flagKeys: string[], defaultValue = false): BoolFlagsState {\n const { client, user } = useFeatureFlareContext();\n const sortedKeys = useMemo(() => [...flagKeys].map((k) => k.trim()).filter(Boolean).sort(), [flagKeys]);\n const userId = user.id ?? user.key ?? '';\n const key = useMemo(() => `${sortedKeys.join(',')}:${userId}`, [sortedKeys, userId]);\n const [state, setState] = useState<BoolFlagsState>({ values: {}, loading: true, errors: {} });\n const lastKey = useRef<string>('');\n\n useEffect(() => {\n let cancelled = false;\n const nextKey = key;\n lastKey.current = nextKey;\n setState({ values: {}, loading: true, errors: {} });\n\n (async () => {\n const values: Record<string, boolean> = {};\n const errors: Record<string, string> = {};\n await Promise.all(\n sortedKeys.map(async (flagKey) => {\n try {\n values[flagKey] = await client.bool(flagKey, user, defaultValue);\n } catch (e) {\n values[flagKey] = defaultValue;\n errors[flagKey] = e instanceof Error ? e.message : String(e);\n }\n })\n );\n\n if (cancelled) return;\n if (lastKey.current !== nextKey) return;\n setState({ values, loading: false, errors });\n })();\n\n return () => {\n cancelled = true;\n };\n }, [client, defaultValue, key, sortedKeys, user]);\n\n return state;\n}\n\nexport function useFlags(input?: UseFlagsInput): FlagsState;\nexport function useFlags(defaultValue?: boolean, options?: UseFlagsOptions): FlagsState;\nexport function useFlags(defaultValueOrInput: boolean | UseFlagsInput = false, options: UseFlagsOptions = {}): FlagsState {\n const { subscribeFlags, getFlagsState, refreshFlags, setUser } = useFeatureFlareContext();\n\n const parsed = useMemo<UseFlagsInput>(() => {\n if (typeof defaultValueOrInput === 'boolean') {\n return { ...options, defaultValue: defaultValueOrInput };\n }\n return defaultValueOrInput ?? {};\n }, [defaultValueOrInput, options]);\n\n const defaultValue = parsed.defaultValue ?? false;\n\n const normalizedOptions = useMemo<UseFlagsOptions>(\n () => ({\n enabled: parsed.enabled ?? true,\n refreshIntervalMs: parsed.refreshIntervalMs,\n hiddenRefreshIntervalMs: parsed.hiddenRefreshIntervalMs,\n pauseWhenHidden: parsed.pauseWhenHidden ?? true\n }),\n [parsed.enabled, parsed.hiddenRefreshIntervalMs, parsed.pauseWhenHidden, parsed.refreshIntervalMs]\n );\n\n const appliedUserRef = useRef<string>('');\n const parsedUserFingerprint = useMemo(() => userFingerprint(parsed.user), [parsed.user]);\n\n useEffect(() => {\n if (!parsed.user) return;\n if (appliedUserRef.current === parsedUserFingerprint) return;\n setUser(parsed.user);\n appliedUserRef.current = parsedUserFingerprint;\n }, [parsed.user, parsedUserFingerprint, setUser]);\n\n useEffect(() => {\n if (normalizedOptions.enabled === false) return;\n refreshFlags(defaultValue);\n }, [defaultValue, normalizedOptions.enabled, parsedUserFingerprint, refreshFlags]);\n\n const subscribe = useMemo(\n () =>\n (onStoreChange: () => void) =>\n subscribeFlags(defaultValue, onStoreChange, normalizedOptions),\n [defaultValue, normalizedOptions, subscribeFlags]\n );\n\n return useSyncExternalStore(subscribe, () => getFlagsState(defaultValue), () => EMPTY_FLAGS_STATE);\n}\n"],"mappings":"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","names":["React","import_react"]}
package/dist/index.d.cts CHANGED
@@ -1,28 +1,52 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React from 'react';
3
- import { ShipItUserPayload, ShipItClient } from '@featureflare/sdk-js';
4
- export { ShipItUserPayload } from '@featureflare/sdk-js';
3
+ import { FeatureFlareUserPayload, FeatureFlareClient } from '@featureflare/sdk-js';
4
+ export { FeatureFlareUserPayload } from '@featureflare/sdk-js';
5
5
 
6
- type ShipItReactConfig = {
6
+ type FeatureFlareEnvironmentKey = 'development' | 'staging' | 'production';
7
+ type FeatureFlareReactConfig = {
8
+ /** Optional: explicit FeatureFlare API base URL. */
9
+ apiBaseUrl?: string;
7
10
  /** Recommended: use a client key (featureflare_cli_...). */
8
11
  sdkKey?: string;
9
12
  /** Legacy/insecure browser mode: uses /api/v1/eval (no sdkKey). */
10
13
  projectKey?: string;
11
- envKey?: string;
12
- };
13
- type ShipItContextValue = {
14
- client: ShipItClient;
15
- user: ShipItUserPayload;
16
- setUser: (next: ShipItUserPayload) => void;
17
- };
18
- declare function ShipItProvider(props: {
19
- config: ShipItReactConfig;
20
- initialUser: ShipItUserPayload;
21
- user?: ShipItUserPayload;
22
- onUserChange?: (next: ShipItUserPayload) => void;
14
+ envKey?: FeatureFlareEnvironmentKey | string;
15
+ };
16
+ declare function resolveFeatureFlareBrowserConfig(input?: {
17
+ envKey?: FeatureFlareEnvironmentKey;
18
+ apiBaseUrl?: string;
19
+ }): FeatureFlareReactConfig;
20
+ type FeatureFlareContextValue = {
21
+ client: FeatureFlareClient;
22
+ user: FeatureFlareUserPayload;
23
+ setUser: (next: FeatureFlareUserPayload) => void;
24
+ getFlagsState: (defaultValue: boolean) => FlagsState$1;
25
+ refreshFlags: (defaultValue: boolean) => void;
26
+ subscribeFlags: (defaultValue: boolean, listener: () => void, options?: FlagsSubscriptionOptions) => () => void;
27
+ };
28
+ type FlagsState$1 = {
29
+ flags: Array<{
30
+ key: string;
31
+ value: boolean;
32
+ }>;
33
+ loading: boolean;
34
+ error: string | null;
35
+ };
36
+ type FlagsSubscriptionOptions = {
37
+ refreshIntervalMs?: number;
38
+ hiddenRefreshIntervalMs?: number;
39
+ pauseWhenHidden?: boolean;
40
+ enabled?: boolean;
41
+ };
42
+ declare function FeatureFlareProvider(props: {
43
+ config: FeatureFlareReactConfig;
44
+ initialUser: FeatureFlareUserPayload;
45
+ user?: FeatureFlareUserPayload;
46
+ onUserChange?: (next: FeatureFlareUserPayload) => void;
23
47
  children: React.ReactNode;
24
48
  }): react_jsx_runtime.JSX.Element;
25
- declare function useShipItContext(): ShipItContextValue;
49
+ declare function useFeatureFlareContext(): FeatureFlareContextValue;
26
50
 
27
51
  type BoolFlagState = {
28
52
  value: boolean;
@@ -34,8 +58,23 @@ type BoolFlagsState = {
34
58
  loading: boolean;
35
59
  errors: Record<string, string>;
36
60
  };
37
- declare function useShipItUser(): [ShipItUserPayload, (next: ShipItUserPayload) => void];
61
+ type FlagsState = {
62
+ flags: Array<{
63
+ key: string;
64
+ value: boolean;
65
+ }>;
66
+ loading: boolean;
67
+ error: string | null;
68
+ };
69
+ type UseFlagsOptions = FlagsSubscriptionOptions;
70
+ type UseFlagsInput = UseFlagsOptions & {
71
+ defaultValue?: boolean;
72
+ user?: FeatureFlareUserPayload;
73
+ };
74
+ declare function useFeatureFlareUser(): [FeatureFlareUserPayload, (next: FeatureFlareUserPayload) => void];
38
75
  declare function useBoolFlag(flagKey: string, defaultValue?: boolean): BoolFlagState;
39
76
  declare function useBoolFlags(flagKeys: string[], defaultValue?: boolean): BoolFlagsState;
77
+ declare function useFlags(input?: UseFlagsInput): FlagsState;
78
+ declare function useFlags(defaultValue?: boolean, options?: UseFlagsOptions): FlagsState;
40
79
 
41
- export { ShipItProvider, useBoolFlag, useBoolFlags, useShipItContext, useShipItUser };
80
+ export { type FeatureFlareEnvironmentKey, FeatureFlareProvider, type FeatureFlareReactConfig, type FlagsState$1 as FlagsState, type FlagsSubscriptionOptions, resolveFeatureFlareBrowserConfig, useBoolFlag, useBoolFlags, useFeatureFlareContext, useFeatureFlareUser, useFlags };
package/dist/index.d.ts CHANGED
@@ -1,28 +1,52 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React from 'react';
3
- import { ShipItUserPayload, ShipItClient } from '@featureflare/sdk-js';
4
- export { ShipItUserPayload } from '@featureflare/sdk-js';
3
+ import { FeatureFlareUserPayload, FeatureFlareClient } from '@featureflare/sdk-js';
4
+ export { FeatureFlareUserPayload } from '@featureflare/sdk-js';
5
5
 
6
- type ShipItReactConfig = {
6
+ type FeatureFlareEnvironmentKey = 'development' | 'staging' | 'production';
7
+ type FeatureFlareReactConfig = {
8
+ /** Optional: explicit FeatureFlare API base URL. */
9
+ apiBaseUrl?: string;
7
10
  /** Recommended: use a client key (featureflare_cli_...). */
8
11
  sdkKey?: string;
9
12
  /** Legacy/insecure browser mode: uses /api/v1/eval (no sdkKey). */
10
13
  projectKey?: string;
11
- envKey?: string;
12
- };
13
- type ShipItContextValue = {
14
- client: ShipItClient;
15
- user: ShipItUserPayload;
16
- setUser: (next: ShipItUserPayload) => void;
17
- };
18
- declare function ShipItProvider(props: {
19
- config: ShipItReactConfig;
20
- initialUser: ShipItUserPayload;
21
- user?: ShipItUserPayload;
22
- onUserChange?: (next: ShipItUserPayload) => void;
14
+ envKey?: FeatureFlareEnvironmentKey | string;
15
+ };
16
+ declare function resolveFeatureFlareBrowserConfig(input?: {
17
+ envKey?: FeatureFlareEnvironmentKey;
18
+ apiBaseUrl?: string;
19
+ }): FeatureFlareReactConfig;
20
+ type FeatureFlareContextValue = {
21
+ client: FeatureFlareClient;
22
+ user: FeatureFlareUserPayload;
23
+ setUser: (next: FeatureFlareUserPayload) => void;
24
+ getFlagsState: (defaultValue: boolean) => FlagsState$1;
25
+ refreshFlags: (defaultValue: boolean) => void;
26
+ subscribeFlags: (defaultValue: boolean, listener: () => void, options?: FlagsSubscriptionOptions) => () => void;
27
+ };
28
+ type FlagsState$1 = {
29
+ flags: Array<{
30
+ key: string;
31
+ value: boolean;
32
+ }>;
33
+ loading: boolean;
34
+ error: string | null;
35
+ };
36
+ type FlagsSubscriptionOptions = {
37
+ refreshIntervalMs?: number;
38
+ hiddenRefreshIntervalMs?: number;
39
+ pauseWhenHidden?: boolean;
40
+ enabled?: boolean;
41
+ };
42
+ declare function FeatureFlareProvider(props: {
43
+ config: FeatureFlareReactConfig;
44
+ initialUser: FeatureFlareUserPayload;
45
+ user?: FeatureFlareUserPayload;
46
+ onUserChange?: (next: FeatureFlareUserPayload) => void;
23
47
  children: React.ReactNode;
24
48
  }): react_jsx_runtime.JSX.Element;
25
- declare function useShipItContext(): ShipItContextValue;
49
+ declare function useFeatureFlareContext(): FeatureFlareContextValue;
26
50
 
27
51
  type BoolFlagState = {
28
52
  value: boolean;
@@ -34,8 +58,23 @@ type BoolFlagsState = {
34
58
  loading: boolean;
35
59
  errors: Record<string, string>;
36
60
  };
37
- declare function useShipItUser(): [ShipItUserPayload, (next: ShipItUserPayload) => void];
61
+ type FlagsState = {
62
+ flags: Array<{
63
+ key: string;
64
+ value: boolean;
65
+ }>;
66
+ loading: boolean;
67
+ error: string | null;
68
+ };
69
+ type UseFlagsOptions = FlagsSubscriptionOptions;
70
+ type UseFlagsInput = UseFlagsOptions & {
71
+ defaultValue?: boolean;
72
+ user?: FeatureFlareUserPayload;
73
+ };
74
+ declare function useFeatureFlareUser(): [FeatureFlareUserPayload, (next: FeatureFlareUserPayload) => void];
38
75
  declare function useBoolFlag(flagKey: string, defaultValue?: boolean): BoolFlagState;
39
76
  declare function useBoolFlags(flagKeys: string[], defaultValue?: boolean): BoolFlagsState;
77
+ declare function useFlags(input?: UseFlagsInput): FlagsState;
78
+ declare function useFlags(defaultValue?: boolean, options?: UseFlagsOptions): FlagsState;
40
79
 
41
- export { ShipItProvider, useBoolFlag, useBoolFlags, useShipItContext, useShipItUser };
80
+ export { type FeatureFlareEnvironmentKey, FeatureFlareProvider, type FeatureFlareReactConfig, type FlagsState$1 as FlagsState, type FlagsSubscriptionOptions, resolveFeatureFlareBrowserConfig, useBoolFlag, useBoolFlags, useFeatureFlareContext, useFeatureFlareUser, useFlags };
package/dist/index.js CHANGED
@@ -1,44 +1,251 @@
1
1
  // src/provider.tsx
2
- import React, { createContext, useMemo, useState } from "react";
3
- import { ShipItClient } from "@featureflare/sdk-js";
2
+ import React, { createContext, useEffect, useMemo, useRef, useState } from "react";
3
+ import { FeatureFlareClient } from "@featureflare/sdk-js";
4
4
  import { jsx } from "react/jsx-runtime";
5
- var ShipItContext = createContext(null);
6
- function ShipItProvider(props) {
5
+ function resolveFeatureFlareBrowserConfig(input) {
6
+ const explicitEnv = input?.envKey;
7
+ const envFromVars = typeof process !== "undefined" ? process.env.NEXT_PUBLIC_FEATUREFLARE_ENV_KEY?.trim().toLowerCase() : "";
8
+ const resolvedEnv = explicitEnv ?? (envFromVars === "development" || envFromVars === "staging" || envFromVars === "production" ? envFromVars : typeof process !== "undefined" && process.env.NODE_ENV === "production" ? "production" : "development");
9
+ const apiBaseUrl = input?.apiBaseUrl ?? (typeof process !== "undefined" ? process.env.NEXT_PUBLIC_FEATUREFLARE_API_BASE_URL?.trim() : void 0);
10
+ const sdkKeyDevelopment = (typeof process !== "undefined" ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY_DEVELOPMENT?.trim() : "") || (typeof process !== "undefined" ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY_DEV?.trim() : "") || "";
11
+ const sdkKeyStaging = (typeof process !== "undefined" ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY_STAGING?.trim() : "") || "";
12
+ const sdkKeyProduction = (typeof process !== "undefined" ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY_PRODUCTION?.trim() : "") || (typeof process !== "undefined" ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY_PROD?.trim() : "") || "";
13
+ const sdkKeyDefault = (typeof process !== "undefined" ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY?.trim() : "") || "";
14
+ const sdkKey = (resolvedEnv === "development" ? sdkKeyDevelopment : "") || (resolvedEnv === "staging" ? sdkKeyStaging : "") || (resolvedEnv === "production" ? sdkKeyProduction : "") || sdkKeyDefault || void 0;
15
+ return {
16
+ apiBaseUrl,
17
+ envKey: resolvedEnv,
18
+ sdkKey
19
+ };
20
+ }
21
+ function normalizeSubscriptionOptions(options) {
22
+ const refreshIntervalMs = Number.isFinite(options?.refreshIntervalMs) && (options?.refreshIntervalMs ?? 0) > 0 ? Number(options?.refreshIntervalMs) : 1e4;
23
+ const hiddenRefreshIntervalMs = Number.isFinite(options?.hiddenRefreshIntervalMs) && (options?.hiddenRefreshIntervalMs ?? 0) > 0 ? Number(options?.hiddenRefreshIntervalMs) : Math.max(refreshIntervalMs * 6, 6e4);
24
+ return {
25
+ refreshIntervalMs,
26
+ hiddenRefreshIntervalMs,
27
+ pauseWhenHidden: options?.pauseWhenHidden ?? true,
28
+ enabled: options?.enabled ?? true
29
+ };
30
+ }
31
+ function createFlagsStore(client, getUser) {
32
+ const entries = /* @__PURE__ */ new Map();
33
+ let nextSubscriberId = 1;
34
+ const isHidden = () => typeof document !== "undefined" && document.visibilityState === "hidden";
35
+ const getEntry = (defaultValue) => {
36
+ const key = defaultValue ? "1" : "0";
37
+ const existing = entries.get(key);
38
+ if (existing) return existing;
39
+ const created = {
40
+ defaultValue,
41
+ snapshot: { flags: [], loading: true, error: null },
42
+ listeners: /* @__PURE__ */ new Set(),
43
+ subscribers: /* @__PURE__ */ new Map(),
44
+ timer: null,
45
+ inFlight: false
46
+ };
47
+ entries.set(key, created);
48
+ return created;
49
+ };
50
+ const emit = (entry) => {
51
+ for (const listener of entry.listeners) listener();
52
+ };
53
+ const getEffectiveOptions = (entry) => {
54
+ const active = [...entry.subscribers.values()].filter((s) => s.enabled);
55
+ if (active.length === 0) {
56
+ return { enabled: false, refreshIntervalMs: 0, hiddenRefreshIntervalMs: 0, pauseWhenHidden: true };
57
+ }
58
+ const refreshIntervalMs = active.reduce((min, s) => Math.min(min, s.refreshIntervalMs), Number.POSITIVE_INFINITY);
59
+ const hiddenActive = active.filter((s) => !s.pauseWhenHidden);
60
+ const hiddenRefreshIntervalMs = hiddenActive.length > 0 ? hiddenActive.reduce((min, s) => Math.min(min, s.hiddenRefreshIntervalMs), Number.POSITIVE_INFINITY) : 0;
61
+ return {
62
+ enabled: true,
63
+ refreshIntervalMs: Number.isFinite(refreshIntervalMs) ? refreshIntervalMs : 1e4,
64
+ hiddenRefreshIntervalMs,
65
+ pauseWhenHidden: hiddenActive.length === 0
66
+ };
67
+ };
68
+ const schedule = (entry) => {
69
+ if (entry.timer !== null) {
70
+ clearTimeout(entry.timer);
71
+ entry.timer = null;
72
+ }
73
+ const effective = getEffectiveOptions(entry);
74
+ if (!effective.enabled) return;
75
+ if (isHidden()) {
76
+ if (effective.pauseWhenHidden) return;
77
+ entry.timer = setTimeout(() => {
78
+ void refresh(entry.defaultValue);
79
+ }, effective.hiddenRefreshIntervalMs);
80
+ return;
81
+ }
82
+ entry.timer = setTimeout(() => {
83
+ void refresh(entry.defaultValue);
84
+ }, effective.refreshIntervalMs);
85
+ };
86
+ const refresh = async (defaultValue, force = false) => {
87
+ const entry = getEntry(defaultValue);
88
+ const effective = getEffectiveOptions(entry);
89
+ if (!effective.enabled && !force) return;
90
+ if (!force && isHidden() && effective.pauseWhenHidden) {
91
+ schedule(entry);
92
+ return;
93
+ }
94
+ if (entry.inFlight) return;
95
+ entry.inFlight = true;
96
+ try {
97
+ const flags = await client.flags(getUser(), defaultValue);
98
+ entry.snapshot = { flags, loading: false, error: null };
99
+ emit(entry);
100
+ } catch (error) {
101
+ const message = error instanceof Error ? error.message : String(error);
102
+ entry.snapshot = { ...entry.snapshot, loading: false, error: message };
103
+ emit(entry);
104
+ } finally {
105
+ entry.inFlight = false;
106
+ schedule(entry);
107
+ }
108
+ };
109
+ const refreshNow = (defaultValue) => {
110
+ const entry = getEntry(defaultValue);
111
+ entry.snapshot = { ...entry.snapshot, loading: true, error: null };
112
+ emit(entry);
113
+ void refresh(defaultValue, true);
114
+ };
115
+ const subscribe = (defaultValue, listener, options) => {
116
+ const entry = getEntry(defaultValue);
117
+ const subscriberId = nextSubscriberId;
118
+ nextSubscriberId += 1;
119
+ entry.listeners.add(listener);
120
+ entry.subscribers.set(subscriberId, normalizeSubscriptionOptions(options));
121
+ const effective = getEffectiveOptions(entry);
122
+ if (effective.enabled && !entry.inFlight && entry.snapshot.loading) {
123
+ void refresh(defaultValue);
124
+ } else {
125
+ schedule(entry);
126
+ }
127
+ return () => {
128
+ entry.listeners.delete(listener);
129
+ entry.subscribers.delete(subscriberId);
130
+ schedule(entry);
131
+ };
132
+ };
133
+ const updateUser = () => {
134
+ for (const entry of entries.values()) {
135
+ entry.snapshot = { ...entry.snapshot, loading: true, error: null };
136
+ emit(entry);
137
+ void refresh(entry.defaultValue);
138
+ }
139
+ };
140
+ const handleVisibilityChange = () => {
141
+ if (isHidden()) return;
142
+ for (const entry of entries.values()) {
143
+ const effective = getEffectiveOptions(entry);
144
+ if (!effective.enabled) continue;
145
+ void refresh(entry.defaultValue);
146
+ }
147
+ };
148
+ if (typeof document !== "undefined") {
149
+ document.addEventListener("visibilitychange", handleVisibilityChange);
150
+ }
151
+ const dispose = () => {
152
+ if (typeof document !== "undefined") {
153
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
154
+ }
155
+ for (const entry of entries.values()) {
156
+ if (entry.timer !== null) {
157
+ clearTimeout(entry.timer);
158
+ }
159
+ entry.timer = null;
160
+ entry.listeners.clear();
161
+ entry.subscribers.clear();
162
+ }
163
+ entries.clear();
164
+ };
165
+ return {
166
+ getState(defaultValue) {
167
+ return getEntry(defaultValue).snapshot;
168
+ },
169
+ refreshNow,
170
+ subscribe,
171
+ updateUser,
172
+ dispose
173
+ };
174
+ }
175
+ var FeatureFlareContext = createContext(null);
176
+ function FeatureFlareProvider(props) {
7
177
  if (props.user && !props.onUserChange) {
8
- throw new Error("ShipItProvider: when providing `user`, also provide `onUserChange` (controlled mode).");
178
+ throw new Error("FeatureFlareProvider: when providing `user`, also provide `onUserChange` (controlled mode).");
9
179
  }
10
180
  const [internalUser, setInternalUser] = useState(props.initialUser);
11
181
  const user = props.user ?? internalUser;
12
182
  const setUser = props.onUserChange ?? setInternalUser;
183
+ const userRef = useRef(user);
13
184
  const client = useMemo(() => {
14
- return new ShipItClient({
185
+ return new FeatureFlareClient({
186
+ apiBaseUrl: props.config.apiBaseUrl,
15
187
  sdkKey: props.config.sdkKey,
16
188
  projectKey: props.config.projectKey,
17
189
  envKey: props.config.envKey
18
190
  });
19
- }, [props.config.envKey, props.config.projectKey, props.config.sdkKey]);
20
- const value = useMemo(() => ({ client, user, setUser }), [client, user]);
21
- return /* @__PURE__ */ jsx(ShipItContext.Provider, { value, children: props.children });
191
+ }, [
192
+ props.config.apiBaseUrl,
193
+ props.config.envKey,
194
+ props.config.projectKey,
195
+ props.config.sdkKey
196
+ ]);
197
+ const flagsStore = useMemo(() => createFlagsStore(client, () => userRef.current), [client]);
198
+ useEffect(() => {
199
+ userRef.current = user;
200
+ flagsStore.updateUser();
201
+ }, [flagsStore, user]);
202
+ useEffect(() => {
203
+ return () => {
204
+ flagsStore.dispose();
205
+ };
206
+ }, [flagsStore]);
207
+ const value = useMemo(
208
+ () => ({
209
+ client,
210
+ user,
211
+ setUser,
212
+ getFlagsState: flagsStore.getState,
213
+ refreshFlags: flagsStore.refreshNow,
214
+ subscribeFlags: flagsStore.subscribe
215
+ }),
216
+ [client, flagsStore, setUser, user]
217
+ );
218
+ return /* @__PURE__ */ jsx(FeatureFlareContext.Provider, { value, children: props.children });
22
219
  }
23
- function useShipItContext() {
24
- const ctx = React.useContext(ShipItContext);
25
- if (!ctx) throw new Error("useShipItContext must be used within <ShipItProvider>.");
220
+ function useFeatureFlareContext() {
221
+ const ctx = React.useContext(FeatureFlareContext);
222
+ if (!ctx) throw new Error("useFeatureFlareContext must be used within <FeatureFlareProvider>.");
26
223
  return ctx;
27
224
  }
28
225
 
29
226
  // src/hooks.ts
30
- import { useEffect, useMemo as useMemo2, useRef, useState as useState2 } from "react";
31
- function useShipItUser() {
32
- const { user, setUser } = useShipItContext();
227
+ import { useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2, useSyncExternalStore } from "react";
228
+ var EMPTY_FLAGS_STATE = { flags: [], loading: true, error: null };
229
+ function userFingerprint(user) {
230
+ if (!user) return "";
231
+ return JSON.stringify({
232
+ id: user.id ?? "",
233
+ key: user.key ?? "",
234
+ email: user.email ?? "",
235
+ meta: user.meta ?? {}
236
+ });
237
+ }
238
+ function useFeatureFlareUser() {
239
+ const { user, setUser } = useFeatureFlareContext();
33
240
  return [user, setUser];
34
241
  }
35
242
  function useBoolFlag(flagKey, defaultValue = false) {
36
- const { client, user } = useShipItContext();
243
+ const { client, user } = useFeatureFlareContext();
37
244
  const [state, setState] = useState2({ value: defaultValue, loading: true, error: null });
38
245
  const userId = user.id ?? user.key ?? "";
39
246
  const key = useMemo2(() => `${flagKey}:${userId}`, [flagKey, userId]);
40
- const lastKey = useRef("");
41
- useEffect(() => {
247
+ const lastKey = useRef2("");
248
+ useEffect2(() => {
42
249
  let cancelled = false;
43
250
  const nextKey = key;
44
251
  lastKey.current = nextKey;
@@ -63,13 +270,13 @@ function useBoolFlag(flagKey, defaultValue = false) {
63
270
  return state;
64
271
  }
65
272
  function useBoolFlags(flagKeys, defaultValue = false) {
66
- const { client, user } = useShipItContext();
273
+ const { client, user } = useFeatureFlareContext();
67
274
  const sortedKeys = useMemo2(() => [...flagKeys].map((k) => k.trim()).filter(Boolean).sort(), [flagKeys]);
68
275
  const userId = user.id ?? user.key ?? "";
69
276
  const key = useMemo2(() => `${sortedKeys.join(",")}:${userId}`, [sortedKeys, userId]);
70
277
  const [state, setState] = useState2({ values: {}, loading: true, errors: {} });
71
- const lastKey = useRef("");
72
- useEffect(() => {
278
+ const lastKey = useRef2("");
279
+ useEffect2(() => {
73
280
  let cancelled = false;
74
281
  const nextKey = key;
75
282
  lastKey.current = nextKey;
@@ -97,11 +304,49 @@ function useBoolFlags(flagKeys, defaultValue = false) {
97
304
  }, [client, defaultValue, key, sortedKeys, user]);
98
305
  return state;
99
306
  }
307
+ function useFlags(defaultValueOrInput = false, options = {}) {
308
+ const { subscribeFlags, getFlagsState, refreshFlags, setUser } = useFeatureFlareContext();
309
+ const parsed = useMemo2(() => {
310
+ if (typeof defaultValueOrInput === "boolean") {
311
+ return { ...options, defaultValue: defaultValueOrInput };
312
+ }
313
+ return defaultValueOrInput ?? {};
314
+ }, [defaultValueOrInput, options]);
315
+ const defaultValue = parsed.defaultValue ?? false;
316
+ const normalizedOptions = useMemo2(
317
+ () => ({
318
+ enabled: parsed.enabled ?? true,
319
+ refreshIntervalMs: parsed.refreshIntervalMs,
320
+ hiddenRefreshIntervalMs: parsed.hiddenRefreshIntervalMs,
321
+ pauseWhenHidden: parsed.pauseWhenHidden ?? true
322
+ }),
323
+ [parsed.enabled, parsed.hiddenRefreshIntervalMs, parsed.pauseWhenHidden, parsed.refreshIntervalMs]
324
+ );
325
+ const appliedUserRef = useRef2("");
326
+ const parsedUserFingerprint = useMemo2(() => userFingerprint(parsed.user), [parsed.user]);
327
+ useEffect2(() => {
328
+ if (!parsed.user) return;
329
+ if (appliedUserRef.current === parsedUserFingerprint) return;
330
+ setUser(parsed.user);
331
+ appliedUserRef.current = parsedUserFingerprint;
332
+ }, [parsed.user, parsedUserFingerprint, setUser]);
333
+ useEffect2(() => {
334
+ if (normalizedOptions.enabled === false) return;
335
+ refreshFlags(defaultValue);
336
+ }, [defaultValue, normalizedOptions.enabled, parsedUserFingerprint, refreshFlags]);
337
+ const subscribe = useMemo2(
338
+ () => (onStoreChange) => subscribeFlags(defaultValue, onStoreChange, normalizedOptions),
339
+ [defaultValue, normalizedOptions, subscribeFlags]
340
+ );
341
+ return useSyncExternalStore(subscribe, () => getFlagsState(defaultValue), () => EMPTY_FLAGS_STATE);
342
+ }
100
343
  export {
101
- ShipItProvider,
344
+ FeatureFlareProvider,
345
+ resolveFeatureFlareBrowserConfig,
102
346
  useBoolFlag,
103
347
  useBoolFlags,
104
- useShipItContext,
105
- useShipItUser
348
+ useFeatureFlareContext,
349
+ useFeatureFlareUser,
350
+ useFlags
106
351
  };
107
352
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/provider.tsx","../src/hooks.ts"],"sourcesContent":["import React, { createContext, useMemo, useState } from 'react';\nimport { ShipItClient, type ShipItUserPayload } from '@featureflare/sdk-js';\n\ntype ShipItReactConfig = {\n /** Recommended: use a client key (featureflare_cli_...). */\n sdkKey?: string;\n /** Legacy/insecure browser mode: uses /api/v1/eval (no sdkKey). */\n projectKey?: string;\n envKey?: string;\n};\n\ntype ShipItContextValue = {\n client: ShipItClient;\n user: ShipItUserPayload;\n setUser: (next: ShipItUserPayload) => void;\n};\n\nconst ShipItContext = createContext<ShipItContextValue | null>(null);\n\nexport function ShipItProvider(props: {\n config: ShipItReactConfig;\n initialUser: ShipItUserPayload;\n user?: ShipItUserPayload;\n onUserChange?: (next: ShipItUserPayload) => void;\n children: React.ReactNode;\n}) {\n if (props.user && !props.onUserChange) {\n throw new Error('ShipItProvider: when providing `user`, also provide `onUserChange` (controlled mode).');\n }\n\n const [internalUser, setInternalUser] = useState<ShipItUserPayload>(props.initialUser);\n const user = props.user ?? internalUser;\n const setUser = props.onUserChange ?? setInternalUser;\n\n const client = useMemo(() => {\n return new ShipItClient({\n sdkKey: props.config.sdkKey,\n projectKey: props.config.projectKey,\n envKey: props.config.envKey\n });\n }, [props.config.envKey, props.config.projectKey, props.config.sdkKey]);\n\n const value = useMemo(() => ({ client, user, setUser }), [client, user]);\n return <ShipItContext.Provider value={value}>{props.children}</ShipItContext.Provider>;\n}\n\nexport function useShipItContext(): ShipItContextValue {\n const ctx = React.useContext(ShipItContext);\n if (!ctx) throw new Error('useShipItContext must be used within <ShipItProvider>.');\n return ctx;\n}\n","import { useEffect, useMemo, useRef, useState } from 'react';\nimport type { ShipItUserPayload } from '@featureflare/sdk-js';\nimport { useShipItContext } from './provider.js';\n\ntype BoolFlagState = {\n value: boolean;\n loading: boolean;\n error: string | null;\n};\n\ntype BoolFlagsState = {\n values: Record<string, boolean>;\n loading: boolean;\n errors: Record<string, string>;\n};\n\nexport function useShipItUser(): [ShipItUserPayload, (next: ShipItUserPayload) => void] {\n const { user, setUser } = useShipItContext();\n return [user, setUser];\n}\n\nexport function useBoolFlag(flagKey: string, defaultValue = false): BoolFlagState {\n const { client, user } = useShipItContext();\n const [state, setState] = useState<BoolFlagState>({ value: defaultValue, loading: true, error: null });\n const userId = user.id ?? user.key ?? '';\n const key = useMemo(() => `${flagKey}:${userId}`, [flagKey, userId]);\n const lastKey = useRef<string>('');\n\n useEffect(() => {\n let cancelled = false;\n const nextKey = key;\n lastKey.current = nextKey;\n setState((s) => ({ ...s, loading: true, error: null }));\n\n (async () => {\n try {\n const v = await client.bool(flagKey, user, defaultValue);\n if (cancelled) return;\n if (lastKey.current !== nextKey) return;\n setState({ value: v, loading: false, error: null });\n } catch (e) {\n if (cancelled) return;\n if (lastKey.current !== nextKey) return;\n const msg = e instanceof Error ? e.message : String(e);\n setState({ value: defaultValue, loading: false, error: msg });\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [client, defaultValue, flagKey, key, user]);\n\n return state;\n}\n\nexport function useBoolFlags(flagKeys: string[], defaultValue = false): BoolFlagsState {\n const { client, user } = useShipItContext();\n const sortedKeys = useMemo(() => [...flagKeys].map((k) => k.trim()).filter(Boolean).sort(), [flagKeys]);\n const userId = user.id ?? user.key ?? '';\n const key = useMemo(() => `${sortedKeys.join(',')}:${userId}`, [sortedKeys, userId]);\n const [state, setState] = useState<BoolFlagsState>({ values: {}, loading: true, errors: {} });\n const lastKey = useRef<string>('');\n\n useEffect(() => {\n let cancelled = false;\n const nextKey = key;\n lastKey.current = nextKey;\n setState({ values: {}, loading: true, errors: {} });\n\n (async () => {\n const values: Record<string, boolean> = {};\n const errors: Record<string, string> = {};\n await Promise.all(\n sortedKeys.map(async (flagKey) => {\n try {\n values[flagKey] = await client.bool(flagKey, user, defaultValue);\n } catch (e) {\n values[flagKey] = defaultValue;\n errors[flagKey] = e instanceof Error ? e.message : String(e);\n }\n })\n );\n\n if (cancelled) return;\n if (lastKey.current !== nextKey) return;\n setState({ values, loading: false, errors });\n })();\n\n return () => {\n cancelled = true;\n };\n }, [client, defaultValue, key, sortedKeys, user]);\n\n return state;\n}\n"],"mappings":"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","names":["useMemo","useState","useState","useMemo"]}
1
+ {"version":3,"sources":["../src/provider.tsx","../src/hooks.ts"],"sourcesContent":["import React, { createContext, useEffect, useMemo, useRef, useState } from 'react';\nimport { FeatureFlareClient, type FeatureFlareUserPayload } from '@featureflare/sdk-js';\n\nexport type FeatureFlareEnvironmentKey = 'development' | 'staging' | 'production';\n\nexport type FeatureFlareReactConfig = {\n /** Optional: explicit FeatureFlare API base URL. */\n apiBaseUrl?: string;\n /** Recommended: use a client key (featureflare_cli_...). */\n sdkKey?: string;\n /** Legacy/insecure browser mode: uses /api/v1/eval (no sdkKey). */\n projectKey?: string;\n envKey?: FeatureFlareEnvironmentKey | string;\n};\n\nexport function resolveFeatureFlareBrowserConfig(input?: {\n envKey?: FeatureFlareEnvironmentKey;\n apiBaseUrl?: string;\n}): FeatureFlareReactConfig {\n const explicitEnv = input?.envKey;\n const envFromVars =\n typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_FEATUREFLARE_ENV_KEY?.trim().toLowerCase() : '';\n\n const resolvedEnv: FeatureFlareEnvironmentKey =\n explicitEnv ??\n (envFromVars === 'development' || envFromVars === 'staging' || envFromVars === 'production'\n ? envFromVars\n : typeof process !== 'undefined' && process.env.NODE_ENV === 'production'\n ? 'production'\n : 'development');\n\n const apiBaseUrl =\n input?.apiBaseUrl ??\n (typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_FEATUREFLARE_API_BASE_URL?.trim() : undefined);\n\n const sdkKeyDevelopment =\n (typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY_DEVELOPMENT?.trim() : '') ||\n (typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY_DEV?.trim() : '') ||\n '';\n const sdkKeyStaging =\n (typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY_STAGING?.trim() : '') || '';\n const sdkKeyProduction =\n (typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY_PRODUCTION?.trim() : '') ||\n (typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY_PROD?.trim() : '') ||\n '';\n const sdkKeyDefault =\n (typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_FEATUREFLARE_CLIENT_KEY?.trim() : '') || '';\n\n const sdkKey =\n (resolvedEnv === 'development' ? sdkKeyDevelopment : '') ||\n (resolvedEnv === 'staging' ? sdkKeyStaging : '') ||\n (resolvedEnv === 'production' ? sdkKeyProduction : '') ||\n sdkKeyDefault ||\n undefined;\n\n return {\n apiBaseUrl,\n envKey: resolvedEnv,\n sdkKey\n };\n}\n\ntype FeatureFlareContextValue = {\n client: FeatureFlareClient;\n user: FeatureFlareUserPayload;\n setUser: (next: FeatureFlareUserPayload) => void;\n getFlagsState: (defaultValue: boolean) => FlagsState;\n refreshFlags: (defaultValue: boolean) => void;\n subscribeFlags: (\n defaultValue: boolean,\n listener: () => void,\n options?: FlagsSubscriptionOptions\n ) => () => void;\n};\n\nexport type FlagsState = {\n flags: Array<{ key: string; value: boolean }>;\n loading: boolean;\n error: string | null;\n};\n\nexport type FlagsSubscriptionOptions = {\n refreshIntervalMs?: number;\n hiddenRefreshIntervalMs?: number;\n pauseWhenHidden?: boolean;\n enabled?: boolean;\n};\n\ntype NormalizedFlagsSubscriptionOptions = {\n refreshIntervalMs: number;\n hiddenRefreshIntervalMs: number;\n pauseWhenHidden: boolean;\n enabled: boolean;\n};\n\ntype FlagsStoreEntry = {\n defaultValue: boolean;\n snapshot: FlagsState;\n listeners: Set<() => void>;\n subscribers: Map<number, NormalizedFlagsSubscriptionOptions>;\n timer: ReturnType<typeof setTimeout> | null;\n inFlight: boolean;\n};\n\nfunction normalizeSubscriptionOptions(options?: FlagsSubscriptionOptions): NormalizedFlagsSubscriptionOptions {\n const refreshIntervalMs =\n Number.isFinite(options?.refreshIntervalMs) && (options?.refreshIntervalMs ?? 0) > 0\n ? Number(options?.refreshIntervalMs)\n : 10000;\n const hiddenRefreshIntervalMs =\n Number.isFinite(options?.hiddenRefreshIntervalMs) && (options?.hiddenRefreshIntervalMs ?? 0) > 0\n ? Number(options?.hiddenRefreshIntervalMs)\n : Math.max(refreshIntervalMs * 6, 60000);\n\n return {\n refreshIntervalMs,\n hiddenRefreshIntervalMs,\n pauseWhenHidden: options?.pauseWhenHidden ?? true,\n enabled: options?.enabled ?? true\n };\n}\n\nfunction createFlagsStore(client: FeatureFlareClient, getUser: () => FeatureFlareUserPayload) {\n const entries = new Map<string, FlagsStoreEntry>();\n let nextSubscriberId = 1;\n\n const isHidden = () => typeof document !== 'undefined' && document.visibilityState === 'hidden';\n\n const getEntry = (defaultValue: boolean): FlagsStoreEntry => {\n const key = defaultValue ? '1' : '0';\n const existing = entries.get(key);\n if (existing) return existing;\n const created: FlagsStoreEntry = {\n defaultValue,\n snapshot: { flags: [], loading: true, error: null },\n listeners: new Set(),\n subscribers: new Map(),\n timer: null,\n inFlight: false\n };\n entries.set(key, created);\n return created;\n };\n\n const emit = (entry: FlagsStoreEntry) => {\n for (const listener of entry.listeners) listener();\n };\n\n const getEffectiveOptions = (entry: FlagsStoreEntry) => {\n const active = [...entry.subscribers.values()].filter((s) => s.enabled);\n if (active.length === 0) {\n return { enabled: false as const, refreshIntervalMs: 0, hiddenRefreshIntervalMs: 0, pauseWhenHidden: true };\n }\n\n const refreshIntervalMs = active.reduce((min, s) => Math.min(min, s.refreshIntervalMs), Number.POSITIVE_INFINITY);\n const hiddenActive = active.filter((s) => !s.pauseWhenHidden);\n const hiddenRefreshIntervalMs =\n hiddenActive.length > 0\n ? hiddenActive.reduce((min, s) => Math.min(min, s.hiddenRefreshIntervalMs), Number.POSITIVE_INFINITY)\n : 0;\n\n return {\n enabled: true as const,\n refreshIntervalMs: Number.isFinite(refreshIntervalMs) ? refreshIntervalMs : 10000,\n hiddenRefreshIntervalMs,\n pauseWhenHidden: hiddenActive.length === 0\n };\n };\n\n const schedule = (entry: FlagsStoreEntry) => {\n if (entry.timer !== null) {\n clearTimeout(entry.timer);\n entry.timer = null;\n }\n\n const effective = getEffectiveOptions(entry);\n if (!effective.enabled) return;\n\n if (isHidden()) {\n if (effective.pauseWhenHidden) return;\n entry.timer = setTimeout(() => {\n void refresh(entry.defaultValue);\n }, effective.hiddenRefreshIntervalMs);\n return;\n }\n\n entry.timer = setTimeout(() => {\n void refresh(entry.defaultValue);\n }, effective.refreshIntervalMs);\n };\n\n const refresh = async (defaultValue: boolean, force = false) => {\n const entry = getEntry(defaultValue);\n const effective = getEffectiveOptions(entry);\n if (!effective.enabled && !force) return;\n\n if (!force && isHidden() && effective.pauseWhenHidden) {\n schedule(entry);\n return;\n }\n\n if (entry.inFlight) return;\n entry.inFlight = true;\n\n try {\n const flags = await client.flags(getUser(), defaultValue);\n entry.snapshot = { flags, loading: false, error: null };\n emit(entry);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n entry.snapshot = { ...entry.snapshot, loading: false, error: message };\n emit(entry);\n } finally {\n entry.inFlight = false;\n schedule(entry);\n }\n };\n\n const refreshNow = (defaultValue: boolean) => {\n const entry = getEntry(defaultValue);\n entry.snapshot = { ...entry.snapshot, loading: true, error: null };\n emit(entry);\n void refresh(defaultValue, true);\n };\n\n const subscribe = (\n defaultValue: boolean,\n listener: () => void,\n options?: FlagsSubscriptionOptions\n ): (() => void) => {\n const entry = getEntry(defaultValue);\n const subscriberId = nextSubscriberId;\n nextSubscriberId += 1;\n\n entry.listeners.add(listener);\n entry.subscribers.set(subscriberId, normalizeSubscriptionOptions(options));\n\n const effective = getEffectiveOptions(entry);\n if (effective.enabled && !entry.inFlight && entry.snapshot.loading) {\n void refresh(defaultValue);\n } else {\n schedule(entry);\n }\n\n return () => {\n entry.listeners.delete(listener);\n entry.subscribers.delete(subscriberId);\n schedule(entry);\n };\n };\n\n const updateUser = () => {\n for (const entry of entries.values()) {\n entry.snapshot = { ...entry.snapshot, loading: true, error: null };\n emit(entry);\n void refresh(entry.defaultValue);\n }\n };\n\n const handleVisibilityChange = () => {\n if (isHidden()) return;\n for (const entry of entries.values()) {\n const effective = getEffectiveOptions(entry);\n if (!effective.enabled) continue;\n void refresh(entry.defaultValue);\n }\n };\n\n if (typeof document !== 'undefined') {\n document.addEventListener('visibilitychange', handleVisibilityChange);\n }\n\n const dispose = () => {\n if (typeof document !== 'undefined') {\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n }\n for (const entry of entries.values()) {\n if (entry.timer !== null) {\n clearTimeout(entry.timer);\n }\n entry.timer = null;\n entry.listeners.clear();\n entry.subscribers.clear();\n }\n entries.clear();\n };\n\n return {\n getState(defaultValue: boolean): FlagsState {\n return getEntry(defaultValue).snapshot;\n },\n refreshNow,\n subscribe,\n updateUser,\n dispose\n };\n}\n\nconst FeatureFlareContext = createContext<FeatureFlareContextValue | null>(null);\n\nexport function FeatureFlareProvider(props: {\n config: FeatureFlareReactConfig;\n initialUser: FeatureFlareUserPayload;\n user?: FeatureFlareUserPayload;\n onUserChange?: (next: FeatureFlareUserPayload) => void;\n children: React.ReactNode;\n}) {\n if (props.user && !props.onUserChange) {\n throw new Error('FeatureFlareProvider: when providing `user`, also provide `onUserChange` (controlled mode).');\n }\n\n const [internalUser, setInternalUser] = useState<FeatureFlareUserPayload>(props.initialUser);\n const user = props.user ?? internalUser;\n const setUser = props.onUserChange ?? setInternalUser;\n const userRef = useRef<FeatureFlareUserPayload>(user);\n\n const client = useMemo(() => {\n return new FeatureFlareClient({\n apiBaseUrl: props.config.apiBaseUrl,\n sdkKey: props.config.sdkKey,\n projectKey: props.config.projectKey,\n envKey: props.config.envKey\n });\n }, [\n props.config.apiBaseUrl,\n props.config.envKey,\n props.config.projectKey,\n props.config.sdkKey\n ]);\n\n const flagsStore = useMemo(() => createFlagsStore(client, () => userRef.current), [client]);\n\n useEffect(() => {\n userRef.current = user;\n flagsStore.updateUser();\n }, [flagsStore, user]);\n\n useEffect(() => {\n return () => {\n flagsStore.dispose();\n };\n }, [flagsStore]);\n\n const value = useMemo(\n () => ({\n client,\n user,\n setUser,\n getFlagsState: flagsStore.getState,\n refreshFlags: flagsStore.refreshNow,\n subscribeFlags: flagsStore.subscribe\n }),\n [client, flagsStore, setUser, user]\n );\n return <FeatureFlareContext.Provider value={value}>{props.children}</FeatureFlareContext.Provider>;\n}\n\nexport function useFeatureFlareContext(): FeatureFlareContextValue {\n const ctx = React.useContext(FeatureFlareContext);\n if (!ctx) throw new Error('useFeatureFlareContext must be used within <FeatureFlareProvider>.');\n return ctx;\n}\n","import { useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';\nimport type { FeatureFlareUserPayload } from '@featureflare/sdk-js';\nimport { useFeatureFlareContext, type FlagsSubscriptionOptions } from './provider.js';\n\ntype BoolFlagState = {\n value: boolean;\n loading: boolean;\n error: string | null;\n};\n\ntype BoolFlagsState = {\n values: Record<string, boolean>;\n loading: boolean;\n errors: Record<string, string>;\n};\n\ntype FlagsState = {\n flags: Array<{ key: string; value: boolean }>;\n loading: boolean;\n error: string | null;\n};\n\ntype UseFlagsOptions = FlagsSubscriptionOptions;\n\ntype UseFlagsInput = UseFlagsOptions & {\n defaultValue?: boolean;\n user?: FeatureFlareUserPayload;\n};\n\nconst EMPTY_FLAGS_STATE: FlagsState = { flags: [], loading: true, error: null };\n\nfunction userFingerprint(user?: FeatureFlareUserPayload): string {\n if (!user) return '';\n return JSON.stringify({\n id: user.id ?? '',\n key: user.key ?? '',\n email: user.email ?? '',\n meta: user.meta ?? {}\n });\n}\n\nexport function useFeatureFlareUser(): [FeatureFlareUserPayload, (next: FeatureFlareUserPayload) => void] {\n const { user, setUser } = useFeatureFlareContext();\n return [user, setUser];\n}\n\nexport function useBoolFlag(flagKey: string, defaultValue = false): BoolFlagState {\n const { client, user } = useFeatureFlareContext();\n const [state, setState] = useState<BoolFlagState>({ value: defaultValue, loading: true, error: null });\n const userId = user.id ?? user.key ?? '';\n const key = useMemo(() => `${flagKey}:${userId}`, [flagKey, userId]);\n const lastKey = useRef<string>('');\n\n useEffect(() => {\n let cancelled = false;\n const nextKey = key;\n lastKey.current = nextKey;\n setState((s) => ({ ...s, loading: true, error: null }));\n\n (async () => {\n try {\n const v = await client.bool(flagKey, user, defaultValue);\n if (cancelled) return;\n if (lastKey.current !== nextKey) return;\n setState({ value: v, loading: false, error: null });\n } catch (e) {\n if (cancelled) return;\n if (lastKey.current !== nextKey) return;\n const msg = e instanceof Error ? e.message : String(e);\n setState({ value: defaultValue, loading: false, error: msg });\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [client, defaultValue, flagKey, key, user]);\n\n return state;\n}\n\nexport function useBoolFlags(flagKeys: string[], defaultValue = false): BoolFlagsState {\n const { client, user } = useFeatureFlareContext();\n const sortedKeys = useMemo(() => [...flagKeys].map((k) => k.trim()).filter(Boolean).sort(), [flagKeys]);\n const userId = user.id ?? user.key ?? '';\n const key = useMemo(() => `${sortedKeys.join(',')}:${userId}`, [sortedKeys, userId]);\n const [state, setState] = useState<BoolFlagsState>({ values: {}, loading: true, errors: {} });\n const lastKey = useRef<string>('');\n\n useEffect(() => {\n let cancelled = false;\n const nextKey = key;\n lastKey.current = nextKey;\n setState({ values: {}, loading: true, errors: {} });\n\n (async () => {\n const values: Record<string, boolean> = {};\n const errors: Record<string, string> = {};\n await Promise.all(\n sortedKeys.map(async (flagKey) => {\n try {\n values[flagKey] = await client.bool(flagKey, user, defaultValue);\n } catch (e) {\n values[flagKey] = defaultValue;\n errors[flagKey] = e instanceof Error ? e.message : String(e);\n }\n })\n );\n\n if (cancelled) return;\n if (lastKey.current !== nextKey) return;\n setState({ values, loading: false, errors });\n })();\n\n return () => {\n cancelled = true;\n };\n }, [client, defaultValue, key, sortedKeys, user]);\n\n return state;\n}\n\nexport function useFlags(input?: UseFlagsInput): FlagsState;\nexport function useFlags(defaultValue?: boolean, options?: UseFlagsOptions): FlagsState;\nexport function useFlags(defaultValueOrInput: boolean | UseFlagsInput = false, options: UseFlagsOptions = {}): FlagsState {\n const { subscribeFlags, getFlagsState, refreshFlags, setUser } = useFeatureFlareContext();\n\n const parsed = useMemo<UseFlagsInput>(() => {\n if (typeof defaultValueOrInput === 'boolean') {\n return { ...options, defaultValue: defaultValueOrInput };\n }\n return defaultValueOrInput ?? {};\n }, [defaultValueOrInput, options]);\n\n const defaultValue = parsed.defaultValue ?? false;\n\n const normalizedOptions = useMemo<UseFlagsOptions>(\n () => ({\n enabled: parsed.enabled ?? true,\n refreshIntervalMs: parsed.refreshIntervalMs,\n hiddenRefreshIntervalMs: parsed.hiddenRefreshIntervalMs,\n pauseWhenHidden: parsed.pauseWhenHidden ?? true\n }),\n [parsed.enabled, parsed.hiddenRefreshIntervalMs, parsed.pauseWhenHidden, parsed.refreshIntervalMs]\n );\n\n const appliedUserRef = useRef<string>('');\n const parsedUserFingerprint = useMemo(() => userFingerprint(parsed.user), [parsed.user]);\n\n useEffect(() => {\n if (!parsed.user) return;\n if (appliedUserRef.current === parsedUserFingerprint) return;\n setUser(parsed.user);\n appliedUserRef.current = parsedUserFingerprint;\n }, [parsed.user, parsedUserFingerprint, setUser]);\n\n useEffect(() => {\n if (normalizedOptions.enabled === false) return;\n refreshFlags(defaultValue);\n }, [defaultValue, normalizedOptions.enabled, parsedUserFingerprint, refreshFlags]);\n\n const subscribe = useMemo(\n () =>\n (onStoreChange: () => void) =>\n subscribeFlags(defaultValue, onStoreChange, normalizedOptions),\n [defaultValue, normalizedOptions, subscribeFlags]\n );\n\n return useSyncExternalStore(subscribe, () => getFlagsState(defaultValue), () => EMPTY_FLAGS_STATE);\n}\n"],"mappings":";AAAA,OAAO,SAAS,eAAe,WAAW,SAAS,QAAQ,gBAAgB;AAC3E,SAAS,0BAAwD;AAiWxD;AAnVF,SAAS,iCAAiC,OAGrB;AAC1B,QAAM,cAAc,OAAO;AAC3B,QAAM,cACJ,OAAO,YAAY,cAAc,QAAQ,IAAI,kCAAkC,KAAK,EAAE,YAAY,IAAI;AAExG,QAAM,cACJ,gBACC,gBAAgB,iBAAiB,gBAAgB,aAAa,gBAAgB,eAC3E,cACA,OAAO,YAAY,eAAe,QAAQ,IAAI,aAAa,eACzD,eACA;AAER,QAAM,aACJ,OAAO,eACN,OAAO,YAAY,cAAc,QAAQ,IAAI,uCAAuC,KAAK,IAAI;AAEhG,QAAM,qBACH,OAAO,YAAY,cAAc,QAAQ,IAAI,iDAAiD,KAAK,IAAI,QACvG,OAAO,YAAY,cAAc,QAAQ,IAAI,yCAAyC,KAAK,IAAI,OAChG;AACF,QAAM,iBACH,OAAO,YAAY,cAAc,QAAQ,IAAI,6CAA6C,KAAK,IAAI,OAAO;AAC7G,QAAM,oBACH,OAAO,YAAY,cAAc,QAAQ,IAAI,gDAAgD,KAAK,IAAI,QACtG,OAAO,YAAY,cAAc,QAAQ,IAAI,0CAA0C,KAAK,IAAI,OACjG;AACF,QAAM,iBACH,OAAO,YAAY,cAAc,QAAQ,IAAI,qCAAqC,KAAK,IAAI,OAAO;AAErG,QAAM,UACH,gBAAgB,gBAAgB,oBAAoB,QACpD,gBAAgB,YAAY,gBAAgB,QAC5C,gBAAgB,eAAe,mBAAmB,OACnD,iBACA;AAEF,SAAO;AAAA,IACL;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,EACF;AACF;AA4CA,SAAS,6BAA6B,SAAwE;AAC5G,QAAM,oBACJ,OAAO,SAAS,SAAS,iBAAiB,MAAM,SAAS,qBAAqB,KAAK,IAC/E,OAAO,SAAS,iBAAiB,IACjC;AACN,QAAM,0BACJ,OAAO,SAAS,SAAS,uBAAuB,MAAM,SAAS,2BAA2B,KAAK,IAC3F,OAAO,SAAS,uBAAuB,IACvC,KAAK,IAAI,oBAAoB,GAAG,GAAK;AAE3C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,iBAAiB,SAAS,mBAAmB;AAAA,IAC7C,SAAS,SAAS,WAAW;AAAA,EAC/B;AACF;AAEA,SAAS,iBAAiB,QAA4B,SAAwC;AAC5F,QAAM,UAAU,oBAAI,IAA6B;AACjD,MAAI,mBAAmB;AAEvB,QAAM,WAAW,MAAM,OAAO,aAAa,eAAe,SAAS,oBAAoB;AAEvF,QAAM,WAAW,CAAC,iBAA2C;AAC3D,UAAM,MAAM,eAAe,MAAM;AACjC,UAAM,WAAW,QAAQ,IAAI,GAAG;AAChC,QAAI,SAAU,QAAO;AACrB,UAAM,UAA2B;AAAA,MAC/B;AAAA,MACA,UAAU,EAAE,OAAO,CAAC,GAAG,SAAS,MAAM,OAAO,KAAK;AAAA,MAClD,WAAW,oBAAI,IAAI;AAAA,MACnB,aAAa,oBAAI,IAAI;AAAA,MACrB,OAAO;AAAA,MACP,UAAU;AAAA,IACZ;AACA,YAAQ,IAAI,KAAK,OAAO;AACxB,WAAO;AAAA,EACT;AAEA,QAAM,OAAO,CAAC,UAA2B;AACvC,eAAW,YAAY,MAAM,UAAW,UAAS;AAAA,EACnD;AAEA,QAAM,sBAAsB,CAAC,UAA2B;AACtD,UAAM,SAAS,CAAC,GAAG,MAAM,YAAY,OAAO,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO;AACtE,QAAI,OAAO,WAAW,GAAG;AACvB,aAAO,EAAE,SAAS,OAAgB,mBAAmB,GAAG,yBAAyB,GAAG,iBAAiB,KAAK;AAAA,IAC5G;AAEA,UAAM,oBAAoB,OAAO,OAAO,CAAC,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,iBAAiB,GAAG,OAAO,iBAAiB;AAChH,UAAM,eAAe,OAAO,OAAO,CAAC,MAAM,CAAC,EAAE,eAAe;AAC5D,UAAM,0BACJ,aAAa,SAAS,IAClB,aAAa,OAAO,CAAC,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,uBAAuB,GAAG,OAAO,iBAAiB,IAClG;AAEN,WAAO;AAAA,MACL,SAAS;AAAA,MACT,mBAAmB,OAAO,SAAS,iBAAiB,IAAI,oBAAoB;AAAA,MAC5E;AAAA,MACA,iBAAiB,aAAa,WAAW;AAAA,IAC3C;AAAA,EACF;AAEA,QAAM,WAAW,CAAC,UAA2B;AAC3C,QAAI,MAAM,UAAU,MAAM;AACxB,mBAAa,MAAM,KAAK;AACxB,YAAM,QAAQ;AAAA,IAChB;AAEA,UAAM,YAAY,oBAAoB,KAAK;AAC3C,QAAI,CAAC,UAAU,QAAS;AAExB,QAAI,SAAS,GAAG;AACd,UAAI,UAAU,gBAAiB;AAC/B,YAAM,QAAQ,WAAW,MAAM;AAC7B,aAAK,QAAQ,MAAM,YAAY;AAAA,MACjC,GAAG,UAAU,uBAAuB;AACpC;AAAA,IACF;AAEA,UAAM,QAAQ,WAAW,MAAM;AAC7B,WAAK,QAAQ,MAAM,YAAY;AAAA,IACjC,GAAG,UAAU,iBAAiB;AAAA,EAChC;AAEA,QAAM,UAAU,OAAO,cAAuB,QAAQ,UAAU;AAC9D,UAAM,QAAQ,SAAS,YAAY;AACnC,UAAM,YAAY,oBAAoB,KAAK;AAC3C,QAAI,CAAC,UAAU,WAAW,CAAC,MAAO;AAElC,QAAI,CAAC,SAAS,SAAS,KAAK,UAAU,iBAAiB;AACrD,eAAS,KAAK;AACd;AAAA,IACF;AAEA,QAAI,MAAM,SAAU;AACpB,UAAM,WAAW;AAEjB,QAAI;AACF,YAAM,QAAQ,MAAM,OAAO,MAAM,QAAQ,GAAG,YAAY;AACxD,YAAM,WAAW,EAAE,OAAO,SAAS,OAAO,OAAO,KAAK;AACtD,WAAK,KAAK;AAAA,IACZ,SAAS,OAAO;AACd,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,YAAM,WAAW,EAAE,GAAG,MAAM,UAAU,SAAS,OAAO,OAAO,QAAQ;AACrE,WAAK,KAAK;AAAA,IACZ,UAAE;AACA,YAAM,WAAW;AACjB,eAAS,KAAK;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,aAAa,CAAC,iBAA0B;AAC5C,UAAM,QAAQ,SAAS,YAAY;AACnC,UAAM,WAAW,EAAE,GAAG,MAAM,UAAU,SAAS,MAAM,OAAO,KAAK;AACjE,SAAK,KAAK;AACV,SAAK,QAAQ,cAAc,IAAI;AAAA,EACjC;AAEA,QAAM,YAAY,CAChB,cACA,UACA,YACiB;AACjB,UAAM,QAAQ,SAAS,YAAY;AACnC,UAAM,eAAe;AACrB,wBAAoB;AAEpB,UAAM,UAAU,IAAI,QAAQ;AAC5B,UAAM,YAAY,IAAI,cAAc,6BAA6B,OAAO,CAAC;AAEzE,UAAM,YAAY,oBAAoB,KAAK;AAC3C,QAAI,UAAU,WAAW,CAAC,MAAM,YAAY,MAAM,SAAS,SAAS;AAClE,WAAK,QAAQ,YAAY;AAAA,IAC3B,OAAO;AACL,eAAS,KAAK;AAAA,IAChB;AAEA,WAAO,MAAM;AACX,YAAM,UAAU,OAAO,QAAQ;AAC/B,YAAM,YAAY,OAAO,YAAY;AACrC,eAAS,KAAK;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,aAAa,MAAM;AACvB,eAAW,SAAS,QAAQ,OAAO,GAAG;AACpC,YAAM,WAAW,EAAE,GAAG,MAAM,UAAU,SAAS,MAAM,OAAO,KAAK;AACjE,WAAK,KAAK;AACV,WAAK,QAAQ,MAAM,YAAY;AAAA,IACjC;AAAA,EACF;AAEA,QAAM,yBAAyB,MAAM;AACnC,QAAI,SAAS,EAAG;AAChB,eAAW,SAAS,QAAQ,OAAO,GAAG;AACpC,YAAM,YAAY,oBAAoB,KAAK;AAC3C,UAAI,CAAC,UAAU,QAAS;AACxB,WAAK,QAAQ,MAAM,YAAY;AAAA,IACjC;AAAA,EACF;AAEA,MAAI,OAAO,aAAa,aAAa;AACnC,aAAS,iBAAiB,oBAAoB,sBAAsB;AAAA,EACtE;AAEA,QAAM,UAAU,MAAM;AACpB,QAAI,OAAO,aAAa,aAAa;AACnC,eAAS,oBAAoB,oBAAoB,sBAAsB;AAAA,IACzE;AACA,eAAW,SAAS,QAAQ,OAAO,GAAG;AACpC,UAAI,MAAM,UAAU,MAAM;AACxB,qBAAa,MAAM,KAAK;AAAA,MAC1B;AACA,YAAM,QAAQ;AACd,YAAM,UAAU,MAAM;AACtB,YAAM,YAAY,MAAM;AAAA,IAC1B;AACA,YAAQ,MAAM;AAAA,EAChB;AAEA,SAAO;AAAA,IACL,SAAS,cAAmC;AAC1C,aAAO,SAAS,YAAY,EAAE;AAAA,IAChC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,IAAM,sBAAsB,cAA+C,IAAI;AAExE,SAAS,qBAAqB,OAMlC;AACD,MAAI,MAAM,QAAQ,CAAC,MAAM,cAAc;AACrC,UAAM,IAAI,MAAM,6FAA6F;AAAA,EAC/G;AAEA,QAAM,CAAC,cAAc,eAAe,IAAI,SAAkC,MAAM,WAAW;AAC3F,QAAM,OAAO,MAAM,QAAQ;AAC3B,QAAM,UAAU,MAAM,gBAAgB;AACtC,QAAM,UAAU,OAAgC,IAAI;AAEpD,QAAM,SAAS,QAAQ,MAAM;AAC3B,WAAO,IAAI,mBAAmB;AAAA,MAC5B,YAAY,MAAM,OAAO;AAAA,MACzB,QAAQ,MAAM,OAAO;AAAA,MACrB,YAAY,MAAM,OAAO;AAAA,MACzB,QAAQ,MAAM,OAAO;AAAA,IACvB,CAAC;AAAA,EACH,GAAG;AAAA,IACD,MAAM,OAAO;AAAA,IACb,MAAM,OAAO;AAAA,IACb,MAAM,OAAO;AAAA,IACb,MAAM,OAAO;AAAA,EACf,CAAC;AAED,QAAM,aAAa,QAAQ,MAAM,iBAAiB,QAAQ,MAAM,QAAQ,OAAO,GAAG,CAAC,MAAM,CAAC;AAE1F,YAAU,MAAM;AACd,YAAQ,UAAU;AAClB,eAAW,WAAW;AAAA,EACxB,GAAG,CAAC,YAAY,IAAI,CAAC;AAErB,YAAU,MAAM;AACd,WAAO,MAAM;AACX,iBAAW,QAAQ;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,UAAU,CAAC;AAEf,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,eAAe,WAAW;AAAA,MAC1B,cAAc,WAAW;AAAA,MACzB,gBAAgB,WAAW;AAAA,IAC7B;AAAA,IACA,CAAC,QAAQ,YAAY,SAAS,IAAI;AAAA,EACpC;AACA,SAAO,oBAAC,oBAAoB,UAApB,EAA6B,OAAe,gBAAM,UAAS;AACrE;AAEO,SAAS,yBAAmD;AACjE,QAAM,MAAM,MAAM,WAAW,mBAAmB;AAChD,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,oEAAoE;AAC9F,SAAO;AACT;;;ACzWA,SAAS,aAAAA,YAAW,WAAAC,UAAS,UAAAC,SAAQ,YAAAC,WAAU,4BAA4B;AA6B3E,IAAM,oBAAgC,EAAE,OAAO,CAAC,GAAG,SAAS,MAAM,OAAO,KAAK;AAE9E,SAAS,gBAAgB,MAAwC;AAC/D,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,KAAK,UAAU;AAAA,IACpB,IAAI,KAAK,MAAM;AAAA,IACf,KAAK,KAAK,OAAO;AAAA,IACjB,OAAO,KAAK,SAAS;AAAA,IACrB,MAAM,KAAK,QAAQ,CAAC;AAAA,EACtB,CAAC;AACH;AAEO,SAAS,sBAA0F;AACxG,QAAM,EAAE,MAAM,QAAQ,IAAI,uBAAuB;AACjD,SAAO,CAAC,MAAM,OAAO;AACvB;AAEO,SAAS,YAAY,SAAiB,eAAe,OAAsB;AAChF,QAAM,EAAE,QAAQ,KAAK,IAAI,uBAAuB;AAChD,QAAM,CAAC,OAAO,QAAQ,IAAIC,UAAwB,EAAE,OAAO,cAAc,SAAS,MAAM,OAAO,KAAK,CAAC;AACrG,QAAM,SAAS,KAAK,MAAM,KAAK,OAAO;AACtC,QAAM,MAAMC,SAAQ,MAAM,GAAG,OAAO,IAAI,MAAM,IAAI,CAAC,SAAS,MAAM,CAAC;AACnE,QAAM,UAAUC,QAAe,EAAE;AAEjC,EAAAC,WAAU,MAAM;AACd,QAAI,YAAY;AAChB,UAAM,UAAU;AAChB,YAAQ,UAAU;AAClB,aAAS,CAAC,OAAO,EAAE,GAAG,GAAG,SAAS,MAAM,OAAO,KAAK,EAAE;AAEtD,KAAC,YAAY;AACX,UAAI;AACF,cAAM,IAAI,MAAM,OAAO,KAAK,SAAS,MAAM,YAAY;AACvD,YAAI,UAAW;AACf,YAAI,QAAQ,YAAY,QAAS;AACjC,iBAAS,EAAE,OAAO,GAAG,SAAS,OAAO,OAAO,KAAK,CAAC;AAAA,MACpD,SAAS,GAAG;AACV,YAAI,UAAW;AACf,YAAI,QAAQ,YAAY,QAAS;AACjC,cAAM,MAAM,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AACrD,iBAAS,EAAE,OAAO,cAAc,SAAS,OAAO,OAAO,IAAI,CAAC;AAAA,MAC9D;AAAA,IACF,GAAG;AAEH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,QAAQ,cAAc,SAAS,KAAK,IAAI,CAAC;AAE7C,SAAO;AACT;AAEO,SAAS,aAAa,UAAoB,eAAe,OAAuB;AACrF,QAAM,EAAE,QAAQ,KAAK,IAAI,uBAAuB;AAChD,QAAM,aAAaF,SAAQ,MAAM,CAAC,GAAG,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,CAAC,QAAQ,CAAC;AACtG,QAAM,SAAS,KAAK,MAAM,KAAK,OAAO;AACtC,QAAM,MAAMA,SAAQ,MAAM,GAAG,WAAW,KAAK,GAAG,CAAC,IAAI,MAAM,IAAI,CAAC,YAAY,MAAM,CAAC;AACnF,QAAM,CAAC,OAAO,QAAQ,IAAID,UAAyB,EAAE,QAAQ,CAAC,GAAG,SAAS,MAAM,QAAQ,CAAC,EAAE,CAAC;AAC5F,QAAM,UAAUE,QAAe,EAAE;AAEjC,EAAAC,WAAU,MAAM;AACd,QAAI,YAAY;AAChB,UAAM,UAAU;AAChB,YAAQ,UAAU;AAClB,aAAS,EAAE,QAAQ,CAAC,GAAG,SAAS,MAAM,QAAQ,CAAC,EAAE,CAAC;AAElD,KAAC,YAAY;AACX,YAAM,SAAkC,CAAC;AACzC,YAAM,SAAiC,CAAC;AACxC,YAAM,QAAQ;AAAA,QACZ,WAAW,IAAI,OAAO,YAAY;AAChC,cAAI;AACF,mBAAO,OAAO,IAAI,MAAM,OAAO,KAAK,SAAS,MAAM,YAAY;AAAA,UACjE,SAAS,GAAG;AACV,mBAAO,OAAO,IAAI;AAClB,mBAAO,OAAO,IAAI,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AAAA,UAC7D;AAAA,QACF,CAAC;AAAA,MACH;AAEA,UAAI,UAAW;AACf,UAAI,QAAQ,YAAY,QAAS;AACjC,eAAS,EAAE,QAAQ,SAAS,OAAO,OAAO,CAAC;AAAA,IAC7C,GAAG;AAEH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,QAAQ,cAAc,KAAK,YAAY,IAAI,CAAC;AAEhD,SAAO;AACT;AAIO,SAAS,SAAS,sBAA+C,OAAO,UAA2B,CAAC,GAAe;AACxH,QAAM,EAAE,gBAAgB,eAAe,cAAc,QAAQ,IAAI,uBAAuB;AAExF,QAAM,SAASF,SAAuB,MAAM;AAC1C,QAAI,OAAO,wBAAwB,WAAW;AAC5C,aAAO,EAAE,GAAG,SAAS,cAAc,oBAAoB;AAAA,IACzD;AACA,WAAO,uBAAuB,CAAC;AAAA,EACjC,GAAG,CAAC,qBAAqB,OAAO,CAAC;AAEjC,QAAM,eAAe,OAAO,gBAAgB;AAE5C,QAAM,oBAAoBA;AAAA,IACxB,OAAO;AAAA,MACL,SAAS,OAAO,WAAW;AAAA,MAC3B,mBAAmB,OAAO;AAAA,MAC1B,yBAAyB,OAAO;AAAA,MAChC,iBAAiB,OAAO,mBAAmB;AAAA,IAC7C;AAAA,IACA,CAAC,OAAO,SAAS,OAAO,yBAAyB,OAAO,iBAAiB,OAAO,iBAAiB;AAAA,EACnG;AAEA,QAAM,iBAAiBC,QAAe,EAAE;AACxC,QAAM,wBAAwBD,SAAQ,MAAM,gBAAgB,OAAO,IAAI,GAAG,CAAC,OAAO,IAAI,CAAC;AAEvF,EAAAE,WAAU,MAAM;AACd,QAAI,CAAC,OAAO,KAAM;AAClB,QAAI,eAAe,YAAY,sBAAuB;AACtD,YAAQ,OAAO,IAAI;AACnB,mBAAe,UAAU;AAAA,EAC3B,GAAG,CAAC,OAAO,MAAM,uBAAuB,OAAO,CAAC;AAEhD,EAAAA,WAAU,MAAM;AACd,QAAI,kBAAkB,YAAY,MAAO;AACzC,iBAAa,YAAY;AAAA,EAC3B,GAAG,CAAC,cAAc,kBAAkB,SAAS,uBAAuB,YAAY,CAAC;AAEjF,QAAM,YAAYF;AAAA,IAChB,MACE,CAAC,kBACC,eAAe,cAAc,eAAe,iBAAiB;AAAA,IACjE,CAAC,cAAc,mBAAmB,cAAc;AAAA,EAClD;AAEA,SAAO,qBAAqB,WAAW,MAAM,cAAc,YAAY,GAAG,MAAM,iBAAiB;AACnG;","names":["useEffect","useMemo","useRef","useState","useState","useMemo","useRef","useEffect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@featureflare/react",
3
- "version": "0.0.3",
3
+ "version": "0.0.5-beta.186",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -33,12 +33,12 @@
33
33
  "react-dom": ">=18"
34
34
  },
35
35
  "dependencies": {
36
- "@featureflare/sdk-js": "0.0.3"
36
+ "@featureflare/sdk-js": "0.0.5-beta.186"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/node": "^22.10.2",
40
- "@types/react": "^18.3.12",
41
- "@types/react-dom": "^18.3.1",
40
+ "@types/react": "^19.0.0",
41
+ "@types/react-dom": "^19.0.0",
42
42
  "eslint": "^9.16.0",
43
43
  "prettier": "^3.4.2",
44
44
  "tsup": "^8.3.5",