flagmint-react-sdk 0.7.13 → 0.7.14
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 +278 -362
- package/dist/client.cjs +3 -3
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +148 -0
- package/dist/client.d.ts +148 -0
- package/dist/client.js +3 -3
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/client.cjs
CHANGED
|
@@ -106,14 +106,14 @@ function useFlagmint() {
|
|
|
106
106
|
};
|
|
107
107
|
}
|
|
108
108
|
function useFlags() {
|
|
109
|
-
const store =
|
|
109
|
+
const store = react.useContext(FlagmintStoreContext);
|
|
110
110
|
if (!store) {
|
|
111
111
|
return {};
|
|
112
112
|
}
|
|
113
113
|
return zustand.useStore(store, (state) => state.flags);
|
|
114
114
|
}
|
|
115
115
|
function useFlag(key, fallback) {
|
|
116
|
-
const store =
|
|
116
|
+
const store = react.useContext(FlagmintStoreContext);
|
|
117
117
|
if (!store) {
|
|
118
118
|
return fallback;
|
|
119
119
|
}
|
|
@@ -121,7 +121,7 @@ function useFlag(key, fallback) {
|
|
|
121
121
|
return flagValue != null ? flagValue : fallback;
|
|
122
122
|
}
|
|
123
123
|
function useFlagmintReady() {
|
|
124
|
-
const store =
|
|
124
|
+
const store = react.useContext(FlagmintStoreContext);
|
|
125
125
|
if (!store) {
|
|
126
126
|
return false;
|
|
127
127
|
}
|
package/dist/client.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/store/store.ts","../src/providers/FlagmintProvider.tsx","../src/hooks/index.ts"],"names":["createStore","createContext","useMemo","useRef","FlagClient","useEffect","useContext","useStore"],"mappings":"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","file":"client.cjs","sourcesContent":["'use client';\nimport { createStore, StoreApi } from \"zustand/vanilla\";\nimport { Flags, FlagStore } from \"@/types\";\n\n\nexport function createFlagStore(initialFlags: Flags = {}): StoreApi<FlagStore> {\n return createStore<FlagStore>((set, get) => ({\n flags: initialFlags,\n client: null,\n isInitialized: false,\n isReady: false,\n setFlags: (flags) => {\n set({ flags: { ...flags } })\n },\n setFlag: (key, value) => set((state) => ({\n flags: { ...state.flags, [key]: value }\n })),\n setClient: (client) => set({ client }),\n setInitialized: (initialized) => set({ isInitialized: initialized }),\n setReady: (ready) => set({ isReady: ready })\n }))\n}\n","'use client';\nimport { FlagClient } from \"flagmint-js-sdk\"\nimport { useMemo, useRef, useEffect, createContext } from \"react\"\nimport { createFlagStore } from \"@/store/store\"\nimport { FlagmintProviderProps, Flags, FlagStore } from \"@/types\"\nimport { StoreApi } from \"zustand\"\n\nexport const FlagmintStoreContext = createContext<StoreApi<FlagStore> | null>(null)\n\n/**\n * Provides the Flagmint flag store to React components and keeps it synced with FlagClient updates.\n * Lifecycle overview:\n * - App loads → FlagClient constructed → initialize() begins.\n * - Cached flags applied → subscribers notified → React renders cached values.\n * - Transport set up → initial fetch → store updated → subscribers notified.\n * - Subsequent server updates propagate via client.subscribe to re-render consumers.\n * - When context changes (updateContext), flags are re-fetched and the store is refreshed.\n *\n * @param props Component props: children, client options, optional initial flags, and deferInitialization flag.\n * @returns A provider that supplies the flag store context to its children.\n */\nexport function FlagmintProvider<\n T extends FlagClient<T, C>,\n C extends Record<string, any> = Record<string, any>\n>({\n children,\n options,\n initialFlags = {},\n deferInitialization = false\n}: FlagmintProviderProps<T, C>) {\n // Create a new store instance for each provider (per-request in SSR)\n const store = useMemo(() => createFlagStore(initialFlags), [])\n \n // Use ref to avoid recreating client on every render\n const clientRef = useRef<FlagClient<T, C> | null>(null)\n const initPromiseRef = useRef<Promise<FlagClient<T, C>> | null>(null)\n const unsubscribeRef = useRef<(() => void) | null>(null)\n \n // Initialization function similar to Vue plugin\n const init = async (): Promise<FlagClient<T, C>> => {\n if (clientRef.current) return clientRef.current // Avoid re-init\n \n // Return existing promise if init is already in progress\n if (initPromiseRef.current) return initPromiseRef.current\n \n const { setClient, setReady, setInitialized, setFlags } = store.getState()\n \n initPromiseRef.current = (async () => {\n try {\n // Create the FlagClient\n const client = new FlagClient<T, C>(options)\n \n // Wait for client to be ready\n await client.ready()\n \n // Get initial flags from client\n const initialClientFlags = client.getFlags()\n setFlags(initialClientFlags)\n \n // Subscribe to flag changes - THIS IS THE KEY FIX\n const unsubscribe = client.subscribe((updatedFlags: Flags) => {\n // Get fresh reference each time\n const currentStore = store.getState();\n currentStore.setFlags(updatedFlags);\n })\n \n unsubscribeRef.current = unsubscribe\n \n clientRef.current = client\n setClient(client)\n setReady(true)\n setInitialized(true)\n \n return client\n } catch (error) {\n console.error('Failed to initialize Flagmint client:', error)\n throw error\n }\n })()\n \n return initPromiseRef.current\n }\n\n useEffect(() => {\n if (!deferInitialization) {\n init().catch(error => {\n console.error('Auto-initialization failed:', error)\n })\n }\n\n return () => {\n // Unsubscribe from flag changes\n if (unsubscribeRef.current) {\n unsubscribeRef.current()\n unsubscribeRef.current = null\n }\n \n // Destroy client\n if (clientRef.current) {\n clientRef.current.destroy?.()\n clientRef.current = null\n initPromiseRef.current = null\n }\n }\n }, [deferInitialization])\n\n return (\n <FlagmintStoreContext.Provider value={store}>\n {children}\n </FlagmintStoreContext.Provider>\n )\n}\n","'use client';\nimport { FlagValue } from \"flagmint-js-sdk\"\nimport { useContext } from \"react\"\nimport { StoreApi, useStore } from \"zustand\"\nimport { FlagmintStoreContext } from \"@/providers/FlagmintProvider\"\nimport { FlagStore, Flags } from \"@/types\"\n\n// Helper to get the store from context\nexport function useFlagmintStore(): StoreApi<FlagStore> {\n const store = useContext(FlagmintStoreContext)\n if (!store) {\n throw new Error('useFlagmintStore must be used within a FlagmintProvider')\n }\n return store\n}\n\n// Hook to get the client instance and update context\nexport function useFlagmint() {\n const store = useFlagmintStore()\n const client = useStore(store, (state) => state.client)\n const isInitialized = useStore(store, (state) => state.isInitialized)\n\n const updateContext = async (context: Record<string, any>) => {\n if (!client) {\n throw new Error('Flagmint client not initialized')\n }\n await client.updateContext(context)\n }\n\n return {\n client,\n isInitialized,\n updateContext\n }\n}\n\n// Hook to get all flags - only re-renders when flags change\nexport function useFlags(): Flags {\n const store = useFlagmintStore()\n if (!store) {\n return {};\n }\n return useStore(store, (state) => state.flags)\n}\n\n// Simplified useFlag - just read from the reactive store\nexport function useFlag<T = FlagValue>(\n key: string,\n fallback?: T\n): T {\n const store = useFlagmintStore()\n if (!store) {\n return fallback as T;\n }\n // Subscribe to this specific flag in the store\n const flagValue = useStore(store, (state) => state.flags[key])\n \n // Return the flag value or fallback\n return (flagValue ?? fallback) as T\n}\n\n// Hook to check if the client is ready\nexport function useFlagmintReady(): boolean {\n const store = useFlagmintStore()\n if (!store) {\n return false;\n }\n // Subscribe to both isReady and isInitialized\n const isReady = useStore(store, (state) => state.isReady)\n const isInitialized = useStore(store, (state) => state.isInitialized)\n \n return isReady && isInitialized\n}"]}
|
|
1
|
+
{"version":3,"sources":["../src/store/store.ts","../src/providers/FlagmintProvider.tsx","../src/hooks/index.ts"],"names":["createStore","createContext","useMemo","useRef","FlagClient","useEffect","useContext","useStore"],"mappings":"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","file":"client.cjs","sourcesContent":["'use client';\nimport { createStore, StoreApi } from \"zustand/vanilla\";\nimport { Flags, FlagStore } from \"@/types\";\n\n\nexport function createFlagStore(initialFlags: Flags = {}): StoreApi<FlagStore> {\n return createStore<FlagStore>((set, get) => ({\n flags: initialFlags,\n client: null,\n isInitialized: false,\n isReady: false,\n setFlags: (flags) => {\n set({ flags: { ...flags } })\n },\n setFlag: (key, value) => set((state) => ({\n flags: { ...state.flags, [key]: value }\n })),\n setClient: (client) => set({ client }),\n setInitialized: (initialized) => set({ isInitialized: initialized }),\n setReady: (ready) => set({ isReady: ready })\n }))\n}\n","'use client';\nimport { FlagClient } from \"flagmint-js-sdk\"\nimport { useMemo, useRef, useEffect, createContext } from \"react\"\nimport { createFlagStore } from \"@/store/store\"\nimport { FlagmintProviderProps, Flags, FlagStore } from \"@/types\"\nimport { StoreApi } from \"zustand\"\n\nexport const FlagmintStoreContext = createContext<StoreApi<FlagStore> | null>(null)\n\n/**\n * Provides the Flagmint flag store to React components and keeps it synced with FlagClient updates.\n * Lifecycle overview:\n * - App loads → FlagClient constructed → initialize() begins.\n * - Cached flags applied → subscribers notified → React renders cached values.\n * - Transport set up → initial fetch → store updated → subscribers notified.\n * - Subsequent server updates propagate via client.subscribe to re-render consumers.\n * - When context changes (updateContext), flags are re-fetched and the store is refreshed.\n *\n * @param props Component props: children, client options, optional initial flags, and deferInitialization flag.\n * @returns A provider that supplies the flag store context to its children.\n */\nexport function FlagmintProvider<\n T extends FlagClient<T, C>,\n C extends Record<string, any> = Record<string, any>\n>({\n children,\n options,\n initialFlags = {},\n deferInitialization = false\n}: FlagmintProviderProps<T, C>) {\n // Create a new store instance for each provider (per-request in SSR)\n const store = useMemo(() => createFlagStore(initialFlags), [])\n \n // Use ref to avoid recreating client on every render\n const clientRef = useRef<FlagClient<T, C> | null>(null)\n const initPromiseRef = useRef<Promise<FlagClient<T, C>> | null>(null)\n const unsubscribeRef = useRef<(() => void) | null>(null)\n \n // Initialization function similar to Vue plugin\n const init = async (): Promise<FlagClient<T, C>> => {\n if (clientRef.current) return clientRef.current // Avoid re-init\n \n // Return existing promise if init is already in progress\n if (initPromiseRef.current) return initPromiseRef.current\n \n const { setClient, setReady, setInitialized, setFlags } = store.getState()\n \n initPromiseRef.current = (async () => {\n try {\n // Create the FlagClient\n const client = new FlagClient<T, C>(options)\n \n // Wait for client to be ready\n await client.ready()\n \n // Get initial flags from client\n const initialClientFlags = client.getFlags()\n setFlags(initialClientFlags)\n \n // Subscribe to flag changes - THIS IS THE KEY FIX\n const unsubscribe = client.subscribe((updatedFlags: Flags) => {\n // Get fresh reference each time\n const currentStore = store.getState();\n currentStore.setFlags(updatedFlags);\n })\n \n unsubscribeRef.current = unsubscribe\n \n clientRef.current = client\n setClient(client)\n setReady(true)\n setInitialized(true)\n \n return client\n } catch (error) {\n console.error('Failed to initialize Flagmint client:', error)\n throw error\n }\n })()\n \n return initPromiseRef.current\n }\n\n useEffect(() => {\n if (!deferInitialization) {\n init().catch(error => {\n console.error('Auto-initialization failed:', error)\n })\n }\n\n return () => {\n // Unsubscribe from flag changes\n if (unsubscribeRef.current) {\n unsubscribeRef.current()\n unsubscribeRef.current = null\n }\n \n // Destroy client\n if (clientRef.current) {\n clientRef.current.destroy?.()\n clientRef.current = null\n initPromiseRef.current = null\n }\n }\n }, [deferInitialization])\n\n return (\n <FlagmintStoreContext.Provider value={store}>\n {children}\n </FlagmintStoreContext.Provider>\n )\n}\n","'use client';\nimport { FlagValue } from \"flagmint-js-sdk\"\nimport { useContext } from \"react\"\nimport { StoreApi, useStore } from \"zustand\"\nimport { FlagmintStoreContext } from \"@/providers/FlagmintProvider\"\nimport { FlagStore, Flags } from \"@/types\"\n\n/**\n * Low-level hook that returns the raw Zustand store instance from context.\n *\n * **Use this when building custom hooks on top of the SDK**, not for general\n * flag consumption. Unlike the consumer hooks, this will throw if called\n * outside a FlagmintProvider - making it useful for catching misuse early\n * during development.\n *\n * @throws {Error} If called outside of a FlagmintProvider\n *\n * @example\n * // Building a custom hook that combines multiple flags\n * function useCanAccessBetaFeatures() {\n * const store = useFlagmintStore();\n * return useStore(store, (state) =>\n * state.flags['beta_access'] && state.flags['new_ui']\n * );\n * }\n */\nexport function useFlagmintStore(): StoreApi<FlagStore> {\n const store = useContext(FlagmintStoreContext)\n if (!store) {\n throw new Error('useFlagmintStore must be used within a FlagmintProvider')\n }\n return store\n}\n\n/**\n * Hook that provides access to the underlying FlagClient instance and\n * the ability to update the evaluation context.\n *\n * **Use this when you need to update the context** after the initial render,\n * for example when a user logs in, changes their plan, or switches organization.\n * Re-renders are triggered when `isInitialized` changes.\n *\n * @throws {Error} If called outside of a FlagmintProvider\n * @throws {Error} If `updateContext` is called before the client is initialized\n *\n * @example\n * // Update context when user logs in\n * function Dashboard() {\n * const { updateContext, isInitialized } = useFlagmint();\n * const user = useCurrentUser();\n *\n * useEffect(() => {\n * if (user) {\n * updateContext({ userId: user.id, plan: user.plan });\n * }\n * }, [user]);\n *\n * if (!isInitialized) return <Spinner />;\n * return <DashboardContent />;\n * }\n */\nexport function useFlagmint() {\n const store = useFlagmintStore()\n const client = useStore(store, (state) => state.client)\n const isInitialized = useStore(store, (state) => state.isInitialized)\n\n const updateContext = async (context: Record<string, any>) => {\n if (!client) {\n throw new Error('Flagmint client not initialized')\n }\n await client.updateContext(context)\n }\n\n return {\n client,\n isInitialized,\n updateContext\n }\n}\n\n/**\n * Hook that returns all feature flags as a reactive object.\n *\n * **Use this when you need to access multiple flags at once**, for example\n * to pass them to a child component or when rendering a list of features.\n * The component re-renders whenever any flag value changes.\n *\n * For single flag access prefer `useFlag` which only re-renders when that\n * specific flag changes.\n *\n * Returns an empty object `{}` if called outside a FlagmintProvider,\n * making it safe to use on public routes or before the user is authenticated.\n *\n * @returns All evaluated feature flags as a key-value object\n *\n * @example\n * // Passing multiple flags to a child\n * function FeatureList() {\n * const flags = useFlags();\n *\n * return (\n * <ul>\n * {Object.entries(flags).map(([key, value]) => (\n * <li key={key}>{key}: {String(value)}</li>\n * ))}\n * </ul>\n * );\n * }\n */\nexport function useFlags(): Flags {\n const store = useContext(FlagmintStoreContext);\n if (!store) {\n return {};\n }\n return useStore(store, (state) => state.flags);\n}\n\n/**\n * Hook that returns the current value of a single feature flag.\n *\n * **This is the primary hook for feature flag consumption.** Use this in\n * any component that needs to conditionally render or behave based on a\n * flag value. The component only re-renders when this specific flag changes,\n * not when other flags update.\n *\n * Returns `fallback` silently if called outside a FlagmintProvider, making\n * it safe to use on public routes or before the user is authenticated.\n *\n * @param key - The feature flag key to evaluate\n * @param fallback - Value to return if the flag is not found or the provider is not ready\n * @returns The flag value cast to type T, or the fallback value\n *\n * @example\n * // Boolean flag - show/hide a feature\n * function RightRail() {\n * const showDocumentation = useFlag<boolean>('show_documentation_feature', false);\n *\n * return (\n * <div>\n * {showDocumentation && <DocumentationCard />}\n * </div>\n * );\n * }\n *\n * @example\n * // String flag - switch between variants\n * function Header() {\n * const colorScheme = useFlag<string>('color_scheme', 'light');\n * return <header className={colorScheme}>...</header>;\n * }\n *\n * @example\n * // Number flag - configure a limit\n * function ResultsList() {\n * const resultsPerPage = useFlag<number>('results_per_page', 10);\n * return <List limit={resultsPerPage} />;\n * }\n */\nexport function useFlag<T = FlagValue>(\n key: string,\n fallback?: T\n): T {\n const store = useContext(FlagmintStoreContext);\n if (!store) {\n return fallback as T;\n }\n const flagValue = useStore(store, (state) => state.flags[key]);\n return (flagValue ?? fallback) as T;\n}\n\n/**\n * Hook that returns whether the Flagmint client has finished initializing\n * and is ready to serve flag values.\n *\n * **Use this to gate rendering** of components that depend on flags having\n * their real server values, preventing a flash of incorrect content on\n * first load. Both `isReady` and `isInitialized` must be true before\n * this returns `true`.\n *\n * Returns `false` silently if called outside a FlagmintProvider, making\n * it safe to use on public routes.\n *\n * @returns `true` when the client is initialized and flags are loaded\n *\n * @example\n * // Gate rendering until flags are ready\n * function App() {\n * const isReady = useFlagmintReady();\n * if (!isReady) return <Spinner />;\n * return <Dashboard />;\n * }\n *\n * @example\n * // Avoid flash of wrong content on first load\n * function PricingPage() {\n * const isReady = useFlagmintReady();\n * const showNewPricing = useFlag<boolean>('new_pricing_page', false);\n *\n * if (!isReady) return <Skeleton />;\n * return showNewPricing ? <NewPricing /> : <OldPricing />;\n * }\n */\nexport function useFlagmintReady(): boolean {\n const store = useContext(FlagmintStoreContext);\n if (!store) {\n return false;\n }\n const isReady = useStore(store, (state) => state.isReady);\n const isInitialized = useStore(store, (state) => state.isInitialized);\n return isReady && isInitialized;\n}"]}
|
package/dist/client.d.cts
CHANGED
|
@@ -51,14 +51,162 @@ declare const FlagmintStoreContext: react.Context<StoreApi<FlagStore> | null>;
|
|
|
51
51
|
*/
|
|
52
52
|
declare function FlagmintProvider<T extends FlagClient<T, C>, C extends Record<string, any> = Record<string, any>>({ children, options, initialFlags, deferInitialization }: FlagmintProviderProps<T, C>): react_jsx_runtime.JSX.Element;
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Low-level hook that returns the raw Zustand store instance from context.
|
|
56
|
+
*
|
|
57
|
+
* **Use this when building custom hooks on top of the SDK**, not for general
|
|
58
|
+
* flag consumption. Unlike the consumer hooks, this will throw if called
|
|
59
|
+
* outside a FlagmintProvider - making it useful for catching misuse early
|
|
60
|
+
* during development.
|
|
61
|
+
*
|
|
62
|
+
* @throws {Error} If called outside of a FlagmintProvider
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* // Building a custom hook that combines multiple flags
|
|
66
|
+
* function useCanAccessBetaFeatures() {
|
|
67
|
+
* const store = useFlagmintStore();
|
|
68
|
+
* return useStore(store, (state) =>
|
|
69
|
+
* state.flags['beta_access'] && state.flags['new_ui']
|
|
70
|
+
* );
|
|
71
|
+
* }
|
|
72
|
+
*/
|
|
54
73
|
declare function useFlagmintStore(): StoreApi<FlagStore>;
|
|
74
|
+
/**
|
|
75
|
+
* Hook that provides access to the underlying FlagClient instance and
|
|
76
|
+
* the ability to update the evaluation context.
|
|
77
|
+
*
|
|
78
|
+
* **Use this when you need to update the context** after the initial render,
|
|
79
|
+
* for example when a user logs in, changes their plan, or switches organization.
|
|
80
|
+
* Re-renders are triggered when `isInitialized` changes.
|
|
81
|
+
*
|
|
82
|
+
* @throws {Error} If called outside of a FlagmintProvider
|
|
83
|
+
* @throws {Error} If `updateContext` is called before the client is initialized
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* // Update context when user logs in
|
|
87
|
+
* function Dashboard() {
|
|
88
|
+
* const { updateContext, isInitialized } = useFlagmint();
|
|
89
|
+
* const user = useCurrentUser();
|
|
90
|
+
*
|
|
91
|
+
* useEffect(() => {
|
|
92
|
+
* if (user) {
|
|
93
|
+
* updateContext({ userId: user.id, plan: user.plan });
|
|
94
|
+
* }
|
|
95
|
+
* }, [user]);
|
|
96
|
+
*
|
|
97
|
+
* if (!isInitialized) return <Spinner />;
|
|
98
|
+
* return <DashboardContent />;
|
|
99
|
+
* }
|
|
100
|
+
*/
|
|
55
101
|
declare function useFlagmint(): {
|
|
56
102
|
client: flagmint_js_sdk.FlagClient<any, any> | null;
|
|
57
103
|
isInitialized: boolean;
|
|
58
104
|
updateContext: (context: Record<string, any>) => Promise<void>;
|
|
59
105
|
};
|
|
106
|
+
/**
|
|
107
|
+
* Hook that returns all feature flags as a reactive object.
|
|
108
|
+
*
|
|
109
|
+
* **Use this when you need to access multiple flags at once**, for example
|
|
110
|
+
* to pass them to a child component or when rendering a list of features.
|
|
111
|
+
* The component re-renders whenever any flag value changes.
|
|
112
|
+
*
|
|
113
|
+
* For single flag access prefer `useFlag` which only re-renders when that
|
|
114
|
+
* specific flag changes.
|
|
115
|
+
*
|
|
116
|
+
* Returns an empty object `{}` if called outside a FlagmintProvider,
|
|
117
|
+
* making it safe to use on public routes or before the user is authenticated.
|
|
118
|
+
*
|
|
119
|
+
* @returns All evaluated feature flags as a key-value object
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* // Passing multiple flags to a child
|
|
123
|
+
* function FeatureList() {
|
|
124
|
+
* const flags = useFlags();
|
|
125
|
+
*
|
|
126
|
+
* return (
|
|
127
|
+
* <ul>
|
|
128
|
+
* {Object.entries(flags).map(([key, value]) => (
|
|
129
|
+
* <li key={key}>{key}: {String(value)}</li>
|
|
130
|
+
* ))}
|
|
131
|
+
* </ul>
|
|
132
|
+
* );
|
|
133
|
+
* }
|
|
134
|
+
*/
|
|
60
135
|
declare function useFlags(): Flags;
|
|
136
|
+
/**
|
|
137
|
+
* Hook that returns the current value of a single feature flag.
|
|
138
|
+
*
|
|
139
|
+
* **This is the primary hook for feature flag consumption.** Use this in
|
|
140
|
+
* any component that needs to conditionally render or behave based on a
|
|
141
|
+
* flag value. The component only re-renders when this specific flag changes,
|
|
142
|
+
* not when other flags update.
|
|
143
|
+
*
|
|
144
|
+
* Returns `fallback` silently if called outside a FlagmintProvider, making
|
|
145
|
+
* it safe to use on public routes or before the user is authenticated.
|
|
146
|
+
*
|
|
147
|
+
* @param key - The feature flag key to evaluate
|
|
148
|
+
* @param fallback - Value to return if the flag is not found or the provider is not ready
|
|
149
|
+
* @returns The flag value cast to type T, or the fallback value
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* // Boolean flag - show/hide a feature
|
|
153
|
+
* function RightRail() {
|
|
154
|
+
* const showDocumentation = useFlag<boolean>('show_documentation_feature', false);
|
|
155
|
+
*
|
|
156
|
+
* return (
|
|
157
|
+
* <div>
|
|
158
|
+
* {showDocumentation && <DocumentationCard />}
|
|
159
|
+
* </div>
|
|
160
|
+
* );
|
|
161
|
+
* }
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* // String flag - switch between variants
|
|
165
|
+
* function Header() {
|
|
166
|
+
* const colorScheme = useFlag<string>('color_scheme', 'light');
|
|
167
|
+
* return <header className={colorScheme}>...</header>;
|
|
168
|
+
* }
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* // Number flag - configure a limit
|
|
172
|
+
* function ResultsList() {
|
|
173
|
+
* const resultsPerPage = useFlag<number>('results_per_page', 10);
|
|
174
|
+
* return <List limit={resultsPerPage} />;
|
|
175
|
+
* }
|
|
176
|
+
*/
|
|
61
177
|
declare function useFlag<T = FlagValue$1>(key: string, fallback?: T): T;
|
|
178
|
+
/**
|
|
179
|
+
* Hook that returns whether the Flagmint client has finished initializing
|
|
180
|
+
* and is ready to serve flag values.
|
|
181
|
+
*
|
|
182
|
+
* **Use this to gate rendering** of components that depend on flags having
|
|
183
|
+
* their real server values, preventing a flash of incorrect content on
|
|
184
|
+
* first load. Both `isReady` and `isInitialized` must be true before
|
|
185
|
+
* this returns `true`.
|
|
186
|
+
*
|
|
187
|
+
* Returns `false` silently if called outside a FlagmintProvider, making
|
|
188
|
+
* it safe to use on public routes.
|
|
189
|
+
*
|
|
190
|
+
* @returns `true` when the client is initialized and flags are loaded
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* // Gate rendering until flags are ready
|
|
194
|
+
* function App() {
|
|
195
|
+
* const isReady = useFlagmintReady();
|
|
196
|
+
* if (!isReady) return <Spinner />;
|
|
197
|
+
* return <Dashboard />;
|
|
198
|
+
* }
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* // Avoid flash of wrong content on first load
|
|
202
|
+
* function PricingPage() {
|
|
203
|
+
* const isReady = useFlagmintReady();
|
|
204
|
+
* const showNewPricing = useFlag<boolean>('new_pricing_page', false);
|
|
205
|
+
*
|
|
206
|
+
* if (!isReady) return <Skeleton />;
|
|
207
|
+
* return showNewPricing ? <NewPricing /> : <OldPricing />;
|
|
208
|
+
* }
|
|
209
|
+
*/
|
|
62
210
|
declare function useFlagmintReady(): boolean;
|
|
63
211
|
|
|
64
212
|
declare function createFlagStore(initialFlags?: Flags): StoreApi$1<FlagStore>;
|
package/dist/client.d.ts
CHANGED
|
@@ -51,14 +51,162 @@ declare const FlagmintStoreContext: react.Context<StoreApi<FlagStore> | null>;
|
|
|
51
51
|
*/
|
|
52
52
|
declare function FlagmintProvider<T extends FlagClient<T, C>, C extends Record<string, any> = Record<string, any>>({ children, options, initialFlags, deferInitialization }: FlagmintProviderProps<T, C>): react_jsx_runtime.JSX.Element;
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Low-level hook that returns the raw Zustand store instance from context.
|
|
56
|
+
*
|
|
57
|
+
* **Use this when building custom hooks on top of the SDK**, not for general
|
|
58
|
+
* flag consumption. Unlike the consumer hooks, this will throw if called
|
|
59
|
+
* outside a FlagmintProvider - making it useful for catching misuse early
|
|
60
|
+
* during development.
|
|
61
|
+
*
|
|
62
|
+
* @throws {Error} If called outside of a FlagmintProvider
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* // Building a custom hook that combines multiple flags
|
|
66
|
+
* function useCanAccessBetaFeatures() {
|
|
67
|
+
* const store = useFlagmintStore();
|
|
68
|
+
* return useStore(store, (state) =>
|
|
69
|
+
* state.flags['beta_access'] && state.flags['new_ui']
|
|
70
|
+
* );
|
|
71
|
+
* }
|
|
72
|
+
*/
|
|
54
73
|
declare function useFlagmintStore(): StoreApi<FlagStore>;
|
|
74
|
+
/**
|
|
75
|
+
* Hook that provides access to the underlying FlagClient instance and
|
|
76
|
+
* the ability to update the evaluation context.
|
|
77
|
+
*
|
|
78
|
+
* **Use this when you need to update the context** after the initial render,
|
|
79
|
+
* for example when a user logs in, changes their plan, or switches organization.
|
|
80
|
+
* Re-renders are triggered when `isInitialized` changes.
|
|
81
|
+
*
|
|
82
|
+
* @throws {Error} If called outside of a FlagmintProvider
|
|
83
|
+
* @throws {Error} If `updateContext` is called before the client is initialized
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* // Update context when user logs in
|
|
87
|
+
* function Dashboard() {
|
|
88
|
+
* const { updateContext, isInitialized } = useFlagmint();
|
|
89
|
+
* const user = useCurrentUser();
|
|
90
|
+
*
|
|
91
|
+
* useEffect(() => {
|
|
92
|
+
* if (user) {
|
|
93
|
+
* updateContext({ userId: user.id, plan: user.plan });
|
|
94
|
+
* }
|
|
95
|
+
* }, [user]);
|
|
96
|
+
*
|
|
97
|
+
* if (!isInitialized) return <Spinner />;
|
|
98
|
+
* return <DashboardContent />;
|
|
99
|
+
* }
|
|
100
|
+
*/
|
|
55
101
|
declare function useFlagmint(): {
|
|
56
102
|
client: flagmint_js_sdk.FlagClient<any, any> | null;
|
|
57
103
|
isInitialized: boolean;
|
|
58
104
|
updateContext: (context: Record<string, any>) => Promise<void>;
|
|
59
105
|
};
|
|
106
|
+
/**
|
|
107
|
+
* Hook that returns all feature flags as a reactive object.
|
|
108
|
+
*
|
|
109
|
+
* **Use this when you need to access multiple flags at once**, for example
|
|
110
|
+
* to pass them to a child component or when rendering a list of features.
|
|
111
|
+
* The component re-renders whenever any flag value changes.
|
|
112
|
+
*
|
|
113
|
+
* For single flag access prefer `useFlag` which only re-renders when that
|
|
114
|
+
* specific flag changes.
|
|
115
|
+
*
|
|
116
|
+
* Returns an empty object `{}` if called outside a FlagmintProvider,
|
|
117
|
+
* making it safe to use on public routes or before the user is authenticated.
|
|
118
|
+
*
|
|
119
|
+
* @returns All evaluated feature flags as a key-value object
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* // Passing multiple flags to a child
|
|
123
|
+
* function FeatureList() {
|
|
124
|
+
* const flags = useFlags();
|
|
125
|
+
*
|
|
126
|
+
* return (
|
|
127
|
+
* <ul>
|
|
128
|
+
* {Object.entries(flags).map(([key, value]) => (
|
|
129
|
+
* <li key={key}>{key}: {String(value)}</li>
|
|
130
|
+
* ))}
|
|
131
|
+
* </ul>
|
|
132
|
+
* );
|
|
133
|
+
* }
|
|
134
|
+
*/
|
|
60
135
|
declare function useFlags(): Flags;
|
|
136
|
+
/**
|
|
137
|
+
* Hook that returns the current value of a single feature flag.
|
|
138
|
+
*
|
|
139
|
+
* **This is the primary hook for feature flag consumption.** Use this in
|
|
140
|
+
* any component that needs to conditionally render or behave based on a
|
|
141
|
+
* flag value. The component only re-renders when this specific flag changes,
|
|
142
|
+
* not when other flags update.
|
|
143
|
+
*
|
|
144
|
+
* Returns `fallback` silently if called outside a FlagmintProvider, making
|
|
145
|
+
* it safe to use on public routes or before the user is authenticated.
|
|
146
|
+
*
|
|
147
|
+
* @param key - The feature flag key to evaluate
|
|
148
|
+
* @param fallback - Value to return if the flag is not found or the provider is not ready
|
|
149
|
+
* @returns The flag value cast to type T, or the fallback value
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* // Boolean flag - show/hide a feature
|
|
153
|
+
* function RightRail() {
|
|
154
|
+
* const showDocumentation = useFlag<boolean>('show_documentation_feature', false);
|
|
155
|
+
*
|
|
156
|
+
* return (
|
|
157
|
+
* <div>
|
|
158
|
+
* {showDocumentation && <DocumentationCard />}
|
|
159
|
+
* </div>
|
|
160
|
+
* );
|
|
161
|
+
* }
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* // String flag - switch between variants
|
|
165
|
+
* function Header() {
|
|
166
|
+
* const colorScheme = useFlag<string>('color_scheme', 'light');
|
|
167
|
+
* return <header className={colorScheme}>...</header>;
|
|
168
|
+
* }
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* // Number flag - configure a limit
|
|
172
|
+
* function ResultsList() {
|
|
173
|
+
* const resultsPerPage = useFlag<number>('results_per_page', 10);
|
|
174
|
+
* return <List limit={resultsPerPage} />;
|
|
175
|
+
* }
|
|
176
|
+
*/
|
|
61
177
|
declare function useFlag<T = FlagValue$1>(key: string, fallback?: T): T;
|
|
178
|
+
/**
|
|
179
|
+
* Hook that returns whether the Flagmint client has finished initializing
|
|
180
|
+
* and is ready to serve flag values.
|
|
181
|
+
*
|
|
182
|
+
* **Use this to gate rendering** of components that depend on flags having
|
|
183
|
+
* their real server values, preventing a flash of incorrect content on
|
|
184
|
+
* first load. Both `isReady` and `isInitialized` must be true before
|
|
185
|
+
* this returns `true`.
|
|
186
|
+
*
|
|
187
|
+
* Returns `false` silently if called outside a FlagmintProvider, making
|
|
188
|
+
* it safe to use on public routes.
|
|
189
|
+
*
|
|
190
|
+
* @returns `true` when the client is initialized and flags are loaded
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* // Gate rendering until flags are ready
|
|
194
|
+
* function App() {
|
|
195
|
+
* const isReady = useFlagmintReady();
|
|
196
|
+
* if (!isReady) return <Spinner />;
|
|
197
|
+
* return <Dashboard />;
|
|
198
|
+
* }
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* // Avoid flash of wrong content on first load
|
|
202
|
+
* function PricingPage() {
|
|
203
|
+
* const isReady = useFlagmintReady();
|
|
204
|
+
* const showNewPricing = useFlag<boolean>('new_pricing_page', false);
|
|
205
|
+
*
|
|
206
|
+
* if (!isReady) return <Skeleton />;
|
|
207
|
+
* return showNewPricing ? <NewPricing /> : <OldPricing />;
|
|
208
|
+
* }
|
|
209
|
+
*/
|
|
62
210
|
declare function useFlagmintReady(): boolean;
|
|
63
211
|
|
|
64
212
|
declare function createFlagStore(initialFlags?: Flags): StoreApi$1<FlagStore>;
|
package/dist/client.js
CHANGED
|
@@ -104,14 +104,14 @@ function useFlagmint() {
|
|
|
104
104
|
};
|
|
105
105
|
}
|
|
106
106
|
function useFlags() {
|
|
107
|
-
const store =
|
|
107
|
+
const store = useContext(FlagmintStoreContext);
|
|
108
108
|
if (!store) {
|
|
109
109
|
return {};
|
|
110
110
|
}
|
|
111
111
|
return useStore(store, (state) => state.flags);
|
|
112
112
|
}
|
|
113
113
|
function useFlag(key, fallback) {
|
|
114
|
-
const store =
|
|
114
|
+
const store = useContext(FlagmintStoreContext);
|
|
115
115
|
if (!store) {
|
|
116
116
|
return fallback;
|
|
117
117
|
}
|
|
@@ -119,7 +119,7 @@ function useFlag(key, fallback) {
|
|
|
119
119
|
return flagValue != null ? flagValue : fallback;
|
|
120
120
|
}
|
|
121
121
|
function useFlagmintReady() {
|
|
122
|
-
const store =
|
|
122
|
+
const store = useContext(FlagmintStoreContext);
|
|
123
123
|
if (!store) {
|
|
124
124
|
return false;
|
|
125
125
|
}
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/store/store.ts","../src/providers/FlagmintProvider.tsx","../src/hooks/index.ts"],"names":[],"mappings":"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","file":"client.js","sourcesContent":["'use client';\nimport { createStore, StoreApi } from \"zustand/vanilla\";\nimport { Flags, FlagStore } from \"@/types\";\n\n\nexport function createFlagStore(initialFlags: Flags = {}): StoreApi<FlagStore> {\n return createStore<FlagStore>((set, get) => ({\n flags: initialFlags,\n client: null,\n isInitialized: false,\n isReady: false,\n setFlags: (flags) => {\n set({ flags: { ...flags } })\n },\n setFlag: (key, value) => set((state) => ({\n flags: { ...state.flags, [key]: value }\n })),\n setClient: (client) => set({ client }),\n setInitialized: (initialized) => set({ isInitialized: initialized }),\n setReady: (ready) => set({ isReady: ready })\n }))\n}\n","'use client';\nimport { FlagClient } from \"flagmint-js-sdk\"\nimport { useMemo, useRef, useEffect, createContext } from \"react\"\nimport { createFlagStore } from \"@/store/store\"\nimport { FlagmintProviderProps, Flags, FlagStore } from \"@/types\"\nimport { StoreApi } from \"zustand\"\n\nexport const FlagmintStoreContext = createContext<StoreApi<FlagStore> | null>(null)\n\n/**\n * Provides the Flagmint flag store to React components and keeps it synced with FlagClient updates.\n * Lifecycle overview:\n * - App loads → FlagClient constructed → initialize() begins.\n * - Cached flags applied → subscribers notified → React renders cached values.\n * - Transport set up → initial fetch → store updated → subscribers notified.\n * - Subsequent server updates propagate via client.subscribe to re-render consumers.\n * - When context changes (updateContext), flags are re-fetched and the store is refreshed.\n *\n * @param props Component props: children, client options, optional initial flags, and deferInitialization flag.\n * @returns A provider that supplies the flag store context to its children.\n */\nexport function FlagmintProvider<\n T extends FlagClient<T, C>,\n C extends Record<string, any> = Record<string, any>\n>({\n children,\n options,\n initialFlags = {},\n deferInitialization = false\n}: FlagmintProviderProps<T, C>) {\n // Create a new store instance for each provider (per-request in SSR)\n const store = useMemo(() => createFlagStore(initialFlags), [])\n \n // Use ref to avoid recreating client on every render\n const clientRef = useRef<FlagClient<T, C> | null>(null)\n const initPromiseRef = useRef<Promise<FlagClient<T, C>> | null>(null)\n const unsubscribeRef = useRef<(() => void) | null>(null)\n \n // Initialization function similar to Vue plugin\n const init = async (): Promise<FlagClient<T, C>> => {\n if (clientRef.current) return clientRef.current // Avoid re-init\n \n // Return existing promise if init is already in progress\n if (initPromiseRef.current) return initPromiseRef.current\n \n const { setClient, setReady, setInitialized, setFlags } = store.getState()\n \n initPromiseRef.current = (async () => {\n try {\n // Create the FlagClient\n const client = new FlagClient<T, C>(options)\n \n // Wait for client to be ready\n await client.ready()\n \n // Get initial flags from client\n const initialClientFlags = client.getFlags()\n setFlags(initialClientFlags)\n \n // Subscribe to flag changes - THIS IS THE KEY FIX\n const unsubscribe = client.subscribe((updatedFlags: Flags) => {\n // Get fresh reference each time\n const currentStore = store.getState();\n currentStore.setFlags(updatedFlags);\n })\n \n unsubscribeRef.current = unsubscribe\n \n clientRef.current = client\n setClient(client)\n setReady(true)\n setInitialized(true)\n \n return client\n } catch (error) {\n console.error('Failed to initialize Flagmint client:', error)\n throw error\n }\n })()\n \n return initPromiseRef.current\n }\n\n useEffect(() => {\n if (!deferInitialization) {\n init().catch(error => {\n console.error('Auto-initialization failed:', error)\n })\n }\n\n return () => {\n // Unsubscribe from flag changes\n if (unsubscribeRef.current) {\n unsubscribeRef.current()\n unsubscribeRef.current = null\n }\n \n // Destroy client\n if (clientRef.current) {\n clientRef.current.destroy?.()\n clientRef.current = null\n initPromiseRef.current = null\n }\n }\n }, [deferInitialization])\n\n return (\n <FlagmintStoreContext.Provider value={store}>\n {children}\n </FlagmintStoreContext.Provider>\n )\n}\n","'use client';\nimport { FlagValue } from \"flagmint-js-sdk\"\nimport { useContext } from \"react\"\nimport { StoreApi, useStore } from \"zustand\"\nimport { FlagmintStoreContext } from \"@/providers/FlagmintProvider\"\nimport { FlagStore, Flags } from \"@/types\"\n\n// Helper to get the store from context\nexport function useFlagmintStore(): StoreApi<FlagStore> {\n const store = useContext(FlagmintStoreContext)\n if (!store) {\n throw new Error('useFlagmintStore must be used within a FlagmintProvider')\n }\n return store\n}\n\n// Hook to get the client instance and update context\nexport function useFlagmint() {\n const store = useFlagmintStore()\n const client = useStore(store, (state) => state.client)\n const isInitialized = useStore(store, (state) => state.isInitialized)\n\n const updateContext = async (context: Record<string, any>) => {\n if (!client) {\n throw new Error('Flagmint client not initialized')\n }\n await client.updateContext(context)\n }\n\n return {\n client,\n isInitialized,\n updateContext\n }\n}\n\n// Hook to get all flags - only re-renders when flags change\nexport function useFlags(): Flags {\n const store = useFlagmintStore()\n if (!store) {\n return {};\n }\n return useStore(store, (state) => state.flags)\n}\n\n// Simplified useFlag - just read from the reactive store\nexport function useFlag<T = FlagValue>(\n key: string,\n fallback?: T\n): T {\n const store = useFlagmintStore()\n if (!store) {\n return fallback as T;\n }\n // Subscribe to this specific flag in the store\n const flagValue = useStore(store, (state) => state.flags[key])\n \n // Return the flag value or fallback\n return (flagValue ?? fallback) as T\n}\n\n// Hook to check if the client is ready\nexport function useFlagmintReady(): boolean {\n const store = useFlagmintStore()\n if (!store) {\n return false;\n }\n // Subscribe to both isReady and isInitialized\n const isReady = useStore(store, (state) => state.isReady)\n const isInitialized = useStore(store, (state) => state.isInitialized)\n \n return isReady && isInitialized\n}"]}
|
|
1
|
+
{"version":3,"sources":["../src/store/store.ts","../src/providers/FlagmintProvider.tsx","../src/hooks/index.ts"],"names":[],"mappings":"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","file":"client.js","sourcesContent":["'use client';\nimport { createStore, StoreApi } from \"zustand/vanilla\";\nimport { Flags, FlagStore } from \"@/types\";\n\n\nexport function createFlagStore(initialFlags: Flags = {}): StoreApi<FlagStore> {\n return createStore<FlagStore>((set, get) => ({\n flags: initialFlags,\n client: null,\n isInitialized: false,\n isReady: false,\n setFlags: (flags) => {\n set({ flags: { ...flags } })\n },\n setFlag: (key, value) => set((state) => ({\n flags: { ...state.flags, [key]: value }\n })),\n setClient: (client) => set({ client }),\n setInitialized: (initialized) => set({ isInitialized: initialized }),\n setReady: (ready) => set({ isReady: ready })\n }))\n}\n","'use client';\nimport { FlagClient } from \"flagmint-js-sdk\"\nimport { useMemo, useRef, useEffect, createContext } from \"react\"\nimport { createFlagStore } from \"@/store/store\"\nimport { FlagmintProviderProps, Flags, FlagStore } from \"@/types\"\nimport { StoreApi } from \"zustand\"\n\nexport const FlagmintStoreContext = createContext<StoreApi<FlagStore> | null>(null)\n\n/**\n * Provides the Flagmint flag store to React components and keeps it synced with FlagClient updates.\n * Lifecycle overview:\n * - App loads → FlagClient constructed → initialize() begins.\n * - Cached flags applied → subscribers notified → React renders cached values.\n * - Transport set up → initial fetch → store updated → subscribers notified.\n * - Subsequent server updates propagate via client.subscribe to re-render consumers.\n * - When context changes (updateContext), flags are re-fetched and the store is refreshed.\n *\n * @param props Component props: children, client options, optional initial flags, and deferInitialization flag.\n * @returns A provider that supplies the flag store context to its children.\n */\nexport function FlagmintProvider<\n T extends FlagClient<T, C>,\n C extends Record<string, any> = Record<string, any>\n>({\n children,\n options,\n initialFlags = {},\n deferInitialization = false\n}: FlagmintProviderProps<T, C>) {\n // Create a new store instance for each provider (per-request in SSR)\n const store = useMemo(() => createFlagStore(initialFlags), [])\n \n // Use ref to avoid recreating client on every render\n const clientRef = useRef<FlagClient<T, C> | null>(null)\n const initPromiseRef = useRef<Promise<FlagClient<T, C>> | null>(null)\n const unsubscribeRef = useRef<(() => void) | null>(null)\n \n // Initialization function similar to Vue plugin\n const init = async (): Promise<FlagClient<T, C>> => {\n if (clientRef.current) return clientRef.current // Avoid re-init\n \n // Return existing promise if init is already in progress\n if (initPromiseRef.current) return initPromiseRef.current\n \n const { setClient, setReady, setInitialized, setFlags } = store.getState()\n \n initPromiseRef.current = (async () => {\n try {\n // Create the FlagClient\n const client = new FlagClient<T, C>(options)\n \n // Wait for client to be ready\n await client.ready()\n \n // Get initial flags from client\n const initialClientFlags = client.getFlags()\n setFlags(initialClientFlags)\n \n // Subscribe to flag changes - THIS IS THE KEY FIX\n const unsubscribe = client.subscribe((updatedFlags: Flags) => {\n // Get fresh reference each time\n const currentStore = store.getState();\n currentStore.setFlags(updatedFlags);\n })\n \n unsubscribeRef.current = unsubscribe\n \n clientRef.current = client\n setClient(client)\n setReady(true)\n setInitialized(true)\n \n return client\n } catch (error) {\n console.error('Failed to initialize Flagmint client:', error)\n throw error\n }\n })()\n \n return initPromiseRef.current\n }\n\n useEffect(() => {\n if (!deferInitialization) {\n init().catch(error => {\n console.error('Auto-initialization failed:', error)\n })\n }\n\n return () => {\n // Unsubscribe from flag changes\n if (unsubscribeRef.current) {\n unsubscribeRef.current()\n unsubscribeRef.current = null\n }\n \n // Destroy client\n if (clientRef.current) {\n clientRef.current.destroy?.()\n clientRef.current = null\n initPromiseRef.current = null\n }\n }\n }, [deferInitialization])\n\n return (\n <FlagmintStoreContext.Provider value={store}>\n {children}\n </FlagmintStoreContext.Provider>\n )\n}\n","'use client';\nimport { FlagValue } from \"flagmint-js-sdk\"\nimport { useContext } from \"react\"\nimport { StoreApi, useStore } from \"zustand\"\nimport { FlagmintStoreContext } from \"@/providers/FlagmintProvider\"\nimport { FlagStore, Flags } from \"@/types\"\n\n/**\n * Low-level hook that returns the raw Zustand store instance from context.\n *\n * **Use this when building custom hooks on top of the SDK**, not for general\n * flag consumption. Unlike the consumer hooks, this will throw if called\n * outside a FlagmintProvider - making it useful for catching misuse early\n * during development.\n *\n * @throws {Error} If called outside of a FlagmintProvider\n *\n * @example\n * // Building a custom hook that combines multiple flags\n * function useCanAccessBetaFeatures() {\n * const store = useFlagmintStore();\n * return useStore(store, (state) =>\n * state.flags['beta_access'] && state.flags['new_ui']\n * );\n * }\n */\nexport function useFlagmintStore(): StoreApi<FlagStore> {\n const store = useContext(FlagmintStoreContext)\n if (!store) {\n throw new Error('useFlagmintStore must be used within a FlagmintProvider')\n }\n return store\n}\n\n/**\n * Hook that provides access to the underlying FlagClient instance and\n * the ability to update the evaluation context.\n *\n * **Use this when you need to update the context** after the initial render,\n * for example when a user logs in, changes their plan, or switches organization.\n * Re-renders are triggered when `isInitialized` changes.\n *\n * @throws {Error} If called outside of a FlagmintProvider\n * @throws {Error} If `updateContext` is called before the client is initialized\n *\n * @example\n * // Update context when user logs in\n * function Dashboard() {\n * const { updateContext, isInitialized } = useFlagmint();\n * const user = useCurrentUser();\n *\n * useEffect(() => {\n * if (user) {\n * updateContext({ userId: user.id, plan: user.plan });\n * }\n * }, [user]);\n *\n * if (!isInitialized) return <Spinner />;\n * return <DashboardContent />;\n * }\n */\nexport function useFlagmint() {\n const store = useFlagmintStore()\n const client = useStore(store, (state) => state.client)\n const isInitialized = useStore(store, (state) => state.isInitialized)\n\n const updateContext = async (context: Record<string, any>) => {\n if (!client) {\n throw new Error('Flagmint client not initialized')\n }\n await client.updateContext(context)\n }\n\n return {\n client,\n isInitialized,\n updateContext\n }\n}\n\n/**\n * Hook that returns all feature flags as a reactive object.\n *\n * **Use this when you need to access multiple flags at once**, for example\n * to pass them to a child component or when rendering a list of features.\n * The component re-renders whenever any flag value changes.\n *\n * For single flag access prefer `useFlag` which only re-renders when that\n * specific flag changes.\n *\n * Returns an empty object `{}` if called outside a FlagmintProvider,\n * making it safe to use on public routes or before the user is authenticated.\n *\n * @returns All evaluated feature flags as a key-value object\n *\n * @example\n * // Passing multiple flags to a child\n * function FeatureList() {\n * const flags = useFlags();\n *\n * return (\n * <ul>\n * {Object.entries(flags).map(([key, value]) => (\n * <li key={key}>{key}: {String(value)}</li>\n * ))}\n * </ul>\n * );\n * }\n */\nexport function useFlags(): Flags {\n const store = useContext(FlagmintStoreContext);\n if (!store) {\n return {};\n }\n return useStore(store, (state) => state.flags);\n}\n\n/**\n * Hook that returns the current value of a single feature flag.\n *\n * **This is the primary hook for feature flag consumption.** Use this in\n * any component that needs to conditionally render or behave based on a\n * flag value. The component only re-renders when this specific flag changes,\n * not when other flags update.\n *\n * Returns `fallback` silently if called outside a FlagmintProvider, making\n * it safe to use on public routes or before the user is authenticated.\n *\n * @param key - The feature flag key to evaluate\n * @param fallback - Value to return if the flag is not found or the provider is not ready\n * @returns The flag value cast to type T, or the fallback value\n *\n * @example\n * // Boolean flag - show/hide a feature\n * function RightRail() {\n * const showDocumentation = useFlag<boolean>('show_documentation_feature', false);\n *\n * return (\n * <div>\n * {showDocumentation && <DocumentationCard />}\n * </div>\n * );\n * }\n *\n * @example\n * // String flag - switch between variants\n * function Header() {\n * const colorScheme = useFlag<string>('color_scheme', 'light');\n * return <header className={colorScheme}>...</header>;\n * }\n *\n * @example\n * // Number flag - configure a limit\n * function ResultsList() {\n * const resultsPerPage = useFlag<number>('results_per_page', 10);\n * return <List limit={resultsPerPage} />;\n * }\n */\nexport function useFlag<T = FlagValue>(\n key: string,\n fallback?: T\n): T {\n const store = useContext(FlagmintStoreContext);\n if (!store) {\n return fallback as T;\n }\n const flagValue = useStore(store, (state) => state.flags[key]);\n return (flagValue ?? fallback) as T;\n}\n\n/**\n * Hook that returns whether the Flagmint client has finished initializing\n * and is ready to serve flag values.\n *\n * **Use this to gate rendering** of components that depend on flags having\n * their real server values, preventing a flash of incorrect content on\n * first load. Both `isReady` and `isInitialized` must be true before\n * this returns `true`.\n *\n * Returns `false` silently if called outside a FlagmintProvider, making\n * it safe to use on public routes.\n *\n * @returns `true` when the client is initialized and flags are loaded\n *\n * @example\n * // Gate rendering until flags are ready\n * function App() {\n * const isReady = useFlagmintReady();\n * if (!isReady) return <Spinner />;\n * return <Dashboard />;\n * }\n *\n * @example\n * // Avoid flash of wrong content on first load\n * function PricingPage() {\n * const isReady = useFlagmintReady();\n * const showNewPricing = useFlag<boolean>('new_pricing_page', false);\n *\n * if (!isReady) return <Skeleton />;\n * return showNewPricing ? <NewPricing /> : <OldPricing />;\n * }\n */\nexport function useFlagmintReady(): boolean {\n const store = useContext(FlagmintStoreContext);\n if (!store) {\n return false;\n }\n const isReady = useStore(store, (state) => state.isReady);\n const isInitialized = useStore(store, (state) => state.isInitialized);\n return isReady && isInitialized;\n}"]}
|
package/dist/index.cjs
CHANGED
|
@@ -107,14 +107,14 @@ function useFlagmint() {
|
|
|
107
107
|
};
|
|
108
108
|
}
|
|
109
109
|
function useFlags() {
|
|
110
|
-
const store =
|
|
110
|
+
const store = react.useContext(FlagmintStoreContext);
|
|
111
111
|
if (!store) {
|
|
112
112
|
return {};
|
|
113
113
|
}
|
|
114
114
|
return zustand.useStore(store, (state) => state.flags);
|
|
115
115
|
}
|
|
116
116
|
function useFlag(key, fallback) {
|
|
117
|
-
const store =
|
|
117
|
+
const store = react.useContext(FlagmintStoreContext);
|
|
118
118
|
if (!store) {
|
|
119
119
|
return fallback;
|
|
120
120
|
}
|
|
@@ -122,7 +122,7 @@ function useFlag(key, fallback) {
|
|
|
122
122
|
return flagValue != null ? flagValue : fallback;
|
|
123
123
|
}
|
|
124
124
|
function useFlagmintReady() {
|
|
125
|
-
const store =
|
|
125
|
+
const store = react.useContext(FlagmintStoreContext);
|
|
126
126
|
if (!store) {
|
|
127
127
|
return false;
|
|
128
128
|
}
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/store/store.ts","../src/providers/FlagmintProvider.tsx","../src/hooks/index.ts"],"names":["createStore","createContext","useMemo","useRef","FlagClient","useEffect","useContext","useStore"],"mappings":"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","file":"index.cjs","sourcesContent":["'use client';\nimport { createStore, StoreApi } from \"zustand/vanilla\";\nimport { Flags, FlagStore } from \"@/types\";\n\n\nexport function createFlagStore(initialFlags: Flags = {}): StoreApi<FlagStore> {\n return createStore<FlagStore>((set, get) => ({\n flags: initialFlags,\n client: null,\n isInitialized: false,\n isReady: false,\n setFlags: (flags) => {\n set({ flags: { ...flags } })\n },\n setFlag: (key, value) => set((state) => ({\n flags: { ...state.flags, [key]: value }\n })),\n setClient: (client) => set({ client }),\n setInitialized: (initialized) => set({ isInitialized: initialized }),\n setReady: (ready) => set({ isReady: ready })\n }))\n}\n","'use client';\nimport { FlagClient } from \"flagmint-js-sdk\"\nimport { useMemo, useRef, useEffect, createContext } from \"react\"\nimport { createFlagStore } from \"@/store/store\"\nimport { FlagmintProviderProps, Flags, FlagStore } from \"@/types\"\nimport { StoreApi } from \"zustand\"\n\nexport const FlagmintStoreContext = createContext<StoreApi<FlagStore> | null>(null)\n\n/**\n * Provides the Flagmint flag store to React components and keeps it synced with FlagClient updates.\n * Lifecycle overview:\n * - App loads → FlagClient constructed → initialize() begins.\n * - Cached flags applied → subscribers notified → React renders cached values.\n * - Transport set up → initial fetch → store updated → subscribers notified.\n * - Subsequent server updates propagate via client.subscribe to re-render consumers.\n * - When context changes (updateContext), flags are re-fetched and the store is refreshed.\n *\n * @param props Component props: children, client options, optional initial flags, and deferInitialization flag.\n * @returns A provider that supplies the flag store context to its children.\n */\nexport function FlagmintProvider<\n T extends FlagClient<T, C>,\n C extends Record<string, any> = Record<string, any>\n>({\n children,\n options,\n initialFlags = {},\n deferInitialization = false\n}: FlagmintProviderProps<T, C>) {\n // Create a new store instance for each provider (per-request in SSR)\n const store = useMemo(() => createFlagStore(initialFlags), [])\n \n // Use ref to avoid recreating client on every render\n const clientRef = useRef<FlagClient<T, C> | null>(null)\n const initPromiseRef = useRef<Promise<FlagClient<T, C>> | null>(null)\n const unsubscribeRef = useRef<(() => void) | null>(null)\n \n // Initialization function similar to Vue plugin\n const init = async (): Promise<FlagClient<T, C>> => {\n if (clientRef.current) return clientRef.current // Avoid re-init\n \n // Return existing promise if init is already in progress\n if (initPromiseRef.current) return initPromiseRef.current\n \n const { setClient, setReady, setInitialized, setFlags } = store.getState()\n \n initPromiseRef.current = (async () => {\n try {\n // Create the FlagClient\n const client = new FlagClient<T, C>(options)\n \n // Wait for client to be ready\n await client.ready()\n \n // Get initial flags from client\n const initialClientFlags = client.getFlags()\n setFlags(initialClientFlags)\n \n // Subscribe to flag changes - THIS IS THE KEY FIX\n const unsubscribe = client.subscribe((updatedFlags: Flags) => {\n // Get fresh reference each time\n const currentStore = store.getState();\n currentStore.setFlags(updatedFlags);\n })\n \n unsubscribeRef.current = unsubscribe\n \n clientRef.current = client\n setClient(client)\n setReady(true)\n setInitialized(true)\n \n return client\n } catch (error) {\n console.error('Failed to initialize Flagmint client:', error)\n throw error\n }\n })()\n \n return initPromiseRef.current\n }\n\n useEffect(() => {\n if (!deferInitialization) {\n init().catch(error => {\n console.error('Auto-initialization failed:', error)\n })\n }\n\n return () => {\n // Unsubscribe from flag changes\n if (unsubscribeRef.current) {\n unsubscribeRef.current()\n unsubscribeRef.current = null\n }\n \n // Destroy client\n if (clientRef.current) {\n clientRef.current.destroy?.()\n clientRef.current = null\n initPromiseRef.current = null\n }\n }\n }, [deferInitialization])\n\n return (\n <FlagmintStoreContext.Provider value={store}>\n {children}\n </FlagmintStoreContext.Provider>\n )\n}\n","'use client';\nimport { FlagValue } from \"flagmint-js-sdk\"\nimport { useContext } from \"react\"\nimport { StoreApi, useStore } from \"zustand\"\nimport { FlagmintStoreContext } from \"@/providers/FlagmintProvider\"\nimport { FlagStore, Flags } from \"@/types\"\n\n// Helper to get the store from context\nexport function useFlagmintStore(): StoreApi<FlagStore> {\n const store = useContext(FlagmintStoreContext)\n if (!store) {\n throw new Error('useFlagmintStore must be used within a FlagmintProvider')\n }\n return store\n}\n\n// Hook to get the client instance and update context\nexport function useFlagmint() {\n const store = useFlagmintStore()\n const client = useStore(store, (state) => state.client)\n const isInitialized = useStore(store, (state) => state.isInitialized)\n\n const updateContext = async (context: Record<string, any>) => {\n if (!client) {\n throw new Error('Flagmint client not initialized')\n }\n await client.updateContext(context)\n }\n\n return {\n client,\n isInitialized,\n updateContext\n }\n}\n\n// Hook to get all flags - only re-renders when flags change\nexport function useFlags(): Flags {\n const store = useFlagmintStore()\n if (!store) {\n return {};\n }\n return useStore(store, (state) => state.flags)\n}\n\n// Simplified useFlag - just read from the reactive store\nexport function useFlag<T = FlagValue>(\n key: string,\n fallback?: T\n): T {\n const store = useFlagmintStore()\n if (!store) {\n return fallback as T;\n }\n // Subscribe to this specific flag in the store\n const flagValue = useStore(store, (state) => state.flags[key])\n \n // Return the flag value or fallback\n return (flagValue ?? fallback) as T\n}\n\n// Hook to check if the client is ready\nexport function useFlagmintReady(): boolean {\n const store = useFlagmintStore()\n if (!store) {\n return false;\n }\n // Subscribe to both isReady and isInitialized\n const isReady = useStore(store, (state) => state.isReady)\n const isInitialized = useStore(store, (state) => state.isInitialized)\n \n return isReady && isInitialized\n}"]}
|
|
1
|
+
{"version":3,"sources":["../src/store/store.ts","../src/providers/FlagmintProvider.tsx","../src/hooks/index.ts"],"names":["createStore","createContext","useMemo","useRef","FlagClient","useEffect","useContext","useStore"],"mappings":"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","file":"index.cjs","sourcesContent":["'use client';\nimport { createStore, StoreApi } from \"zustand/vanilla\";\nimport { Flags, FlagStore } from \"@/types\";\n\n\nexport function createFlagStore(initialFlags: Flags = {}): StoreApi<FlagStore> {\n return createStore<FlagStore>((set, get) => ({\n flags: initialFlags,\n client: null,\n isInitialized: false,\n isReady: false,\n setFlags: (flags) => {\n set({ flags: { ...flags } })\n },\n setFlag: (key, value) => set((state) => ({\n flags: { ...state.flags, [key]: value }\n })),\n setClient: (client) => set({ client }),\n setInitialized: (initialized) => set({ isInitialized: initialized }),\n setReady: (ready) => set({ isReady: ready })\n }))\n}\n","'use client';\nimport { FlagClient } from \"flagmint-js-sdk\"\nimport { useMemo, useRef, useEffect, createContext } from \"react\"\nimport { createFlagStore } from \"@/store/store\"\nimport { FlagmintProviderProps, Flags, FlagStore } from \"@/types\"\nimport { StoreApi } from \"zustand\"\n\nexport const FlagmintStoreContext = createContext<StoreApi<FlagStore> | null>(null)\n\n/**\n * Provides the Flagmint flag store to React components and keeps it synced with FlagClient updates.\n * Lifecycle overview:\n * - App loads → FlagClient constructed → initialize() begins.\n * - Cached flags applied → subscribers notified → React renders cached values.\n * - Transport set up → initial fetch → store updated → subscribers notified.\n * - Subsequent server updates propagate via client.subscribe to re-render consumers.\n * - When context changes (updateContext), flags are re-fetched and the store is refreshed.\n *\n * @param props Component props: children, client options, optional initial flags, and deferInitialization flag.\n * @returns A provider that supplies the flag store context to its children.\n */\nexport function FlagmintProvider<\n T extends FlagClient<T, C>,\n C extends Record<string, any> = Record<string, any>\n>({\n children,\n options,\n initialFlags = {},\n deferInitialization = false\n}: FlagmintProviderProps<T, C>) {\n // Create a new store instance for each provider (per-request in SSR)\n const store = useMemo(() => createFlagStore(initialFlags), [])\n \n // Use ref to avoid recreating client on every render\n const clientRef = useRef<FlagClient<T, C> | null>(null)\n const initPromiseRef = useRef<Promise<FlagClient<T, C>> | null>(null)\n const unsubscribeRef = useRef<(() => void) | null>(null)\n \n // Initialization function similar to Vue plugin\n const init = async (): Promise<FlagClient<T, C>> => {\n if (clientRef.current) return clientRef.current // Avoid re-init\n \n // Return existing promise if init is already in progress\n if (initPromiseRef.current) return initPromiseRef.current\n \n const { setClient, setReady, setInitialized, setFlags } = store.getState()\n \n initPromiseRef.current = (async () => {\n try {\n // Create the FlagClient\n const client = new FlagClient<T, C>(options)\n \n // Wait for client to be ready\n await client.ready()\n \n // Get initial flags from client\n const initialClientFlags = client.getFlags()\n setFlags(initialClientFlags)\n \n // Subscribe to flag changes - THIS IS THE KEY FIX\n const unsubscribe = client.subscribe((updatedFlags: Flags) => {\n // Get fresh reference each time\n const currentStore = store.getState();\n currentStore.setFlags(updatedFlags);\n })\n \n unsubscribeRef.current = unsubscribe\n \n clientRef.current = client\n setClient(client)\n setReady(true)\n setInitialized(true)\n \n return client\n } catch (error) {\n console.error('Failed to initialize Flagmint client:', error)\n throw error\n }\n })()\n \n return initPromiseRef.current\n }\n\n useEffect(() => {\n if (!deferInitialization) {\n init().catch(error => {\n console.error('Auto-initialization failed:', error)\n })\n }\n\n return () => {\n // Unsubscribe from flag changes\n if (unsubscribeRef.current) {\n unsubscribeRef.current()\n unsubscribeRef.current = null\n }\n \n // Destroy client\n if (clientRef.current) {\n clientRef.current.destroy?.()\n clientRef.current = null\n initPromiseRef.current = null\n }\n }\n }, [deferInitialization])\n\n return (\n <FlagmintStoreContext.Provider value={store}>\n {children}\n </FlagmintStoreContext.Provider>\n )\n}\n","'use client';\nimport { FlagValue } from \"flagmint-js-sdk\"\nimport { useContext } from \"react\"\nimport { StoreApi, useStore } from \"zustand\"\nimport { FlagmintStoreContext } from \"@/providers/FlagmintProvider\"\nimport { FlagStore, Flags } from \"@/types\"\n\n/**\n * Low-level hook that returns the raw Zustand store instance from context.\n *\n * **Use this when building custom hooks on top of the SDK**, not for general\n * flag consumption. Unlike the consumer hooks, this will throw if called\n * outside a FlagmintProvider - making it useful for catching misuse early\n * during development.\n *\n * @throws {Error} If called outside of a FlagmintProvider\n *\n * @example\n * // Building a custom hook that combines multiple flags\n * function useCanAccessBetaFeatures() {\n * const store = useFlagmintStore();\n * return useStore(store, (state) =>\n * state.flags['beta_access'] && state.flags['new_ui']\n * );\n * }\n */\nexport function useFlagmintStore(): StoreApi<FlagStore> {\n const store = useContext(FlagmintStoreContext)\n if (!store) {\n throw new Error('useFlagmintStore must be used within a FlagmintProvider')\n }\n return store\n}\n\n/**\n * Hook that provides access to the underlying FlagClient instance and\n * the ability to update the evaluation context.\n *\n * **Use this when you need to update the context** after the initial render,\n * for example when a user logs in, changes their plan, or switches organization.\n * Re-renders are triggered when `isInitialized` changes.\n *\n * @throws {Error} If called outside of a FlagmintProvider\n * @throws {Error} If `updateContext` is called before the client is initialized\n *\n * @example\n * // Update context when user logs in\n * function Dashboard() {\n * const { updateContext, isInitialized } = useFlagmint();\n * const user = useCurrentUser();\n *\n * useEffect(() => {\n * if (user) {\n * updateContext({ userId: user.id, plan: user.plan });\n * }\n * }, [user]);\n *\n * if (!isInitialized) return <Spinner />;\n * return <DashboardContent />;\n * }\n */\nexport function useFlagmint() {\n const store = useFlagmintStore()\n const client = useStore(store, (state) => state.client)\n const isInitialized = useStore(store, (state) => state.isInitialized)\n\n const updateContext = async (context: Record<string, any>) => {\n if (!client) {\n throw new Error('Flagmint client not initialized')\n }\n await client.updateContext(context)\n }\n\n return {\n client,\n isInitialized,\n updateContext\n }\n}\n\n/**\n * Hook that returns all feature flags as a reactive object.\n *\n * **Use this when you need to access multiple flags at once**, for example\n * to pass them to a child component or when rendering a list of features.\n * The component re-renders whenever any flag value changes.\n *\n * For single flag access prefer `useFlag` which only re-renders when that\n * specific flag changes.\n *\n * Returns an empty object `{}` if called outside a FlagmintProvider,\n * making it safe to use on public routes or before the user is authenticated.\n *\n * @returns All evaluated feature flags as a key-value object\n *\n * @example\n * // Passing multiple flags to a child\n * function FeatureList() {\n * const flags = useFlags();\n *\n * return (\n * <ul>\n * {Object.entries(flags).map(([key, value]) => (\n * <li key={key}>{key}: {String(value)}</li>\n * ))}\n * </ul>\n * );\n * }\n */\nexport function useFlags(): Flags {\n const store = useContext(FlagmintStoreContext);\n if (!store) {\n return {};\n }\n return useStore(store, (state) => state.flags);\n}\n\n/**\n * Hook that returns the current value of a single feature flag.\n *\n * **This is the primary hook for feature flag consumption.** Use this in\n * any component that needs to conditionally render or behave based on a\n * flag value. The component only re-renders when this specific flag changes,\n * not when other flags update.\n *\n * Returns `fallback` silently if called outside a FlagmintProvider, making\n * it safe to use on public routes or before the user is authenticated.\n *\n * @param key - The feature flag key to evaluate\n * @param fallback - Value to return if the flag is not found or the provider is not ready\n * @returns The flag value cast to type T, or the fallback value\n *\n * @example\n * // Boolean flag - show/hide a feature\n * function RightRail() {\n * const showDocumentation = useFlag<boolean>('show_documentation_feature', false);\n *\n * return (\n * <div>\n * {showDocumentation && <DocumentationCard />}\n * </div>\n * );\n * }\n *\n * @example\n * // String flag - switch between variants\n * function Header() {\n * const colorScheme = useFlag<string>('color_scheme', 'light');\n * return <header className={colorScheme}>...</header>;\n * }\n *\n * @example\n * // Number flag - configure a limit\n * function ResultsList() {\n * const resultsPerPage = useFlag<number>('results_per_page', 10);\n * return <List limit={resultsPerPage} />;\n * }\n */\nexport function useFlag<T = FlagValue>(\n key: string,\n fallback?: T\n): T {\n const store = useContext(FlagmintStoreContext);\n if (!store) {\n return fallback as T;\n }\n const flagValue = useStore(store, (state) => state.flags[key]);\n return (flagValue ?? fallback) as T;\n}\n\n/**\n * Hook that returns whether the Flagmint client has finished initializing\n * and is ready to serve flag values.\n *\n * **Use this to gate rendering** of components that depend on flags having\n * their real server values, preventing a flash of incorrect content on\n * first load. Both `isReady` and `isInitialized` must be true before\n * this returns `true`.\n *\n * Returns `false` silently if called outside a FlagmintProvider, making\n * it safe to use on public routes.\n *\n * @returns `true` when the client is initialized and flags are loaded\n *\n * @example\n * // Gate rendering until flags are ready\n * function App() {\n * const isReady = useFlagmintReady();\n * if (!isReady) return <Spinner />;\n * return <Dashboard />;\n * }\n *\n * @example\n * // Avoid flash of wrong content on first load\n * function PricingPage() {\n * const isReady = useFlagmintReady();\n * const showNewPricing = useFlag<boolean>('new_pricing_page', false);\n *\n * if (!isReady) return <Skeleton />;\n * return showNewPricing ? <NewPricing /> : <OldPricing />;\n * }\n */\nexport function useFlagmintReady(): boolean {\n const store = useContext(FlagmintStoreContext);\n if (!store) {\n return false;\n }\n const isReady = useStore(store, (state) => state.isReady);\n const isInitialized = useStore(store, (state) => state.isInitialized);\n return isReady && isInitialized;\n}"]}
|
package/dist/index.js
CHANGED
|
@@ -105,14 +105,14 @@ function useFlagmint() {
|
|
|
105
105
|
};
|
|
106
106
|
}
|
|
107
107
|
function useFlags() {
|
|
108
|
-
const store =
|
|
108
|
+
const store = useContext(FlagmintStoreContext);
|
|
109
109
|
if (!store) {
|
|
110
110
|
return {};
|
|
111
111
|
}
|
|
112
112
|
return useStore(store, (state) => state.flags);
|
|
113
113
|
}
|
|
114
114
|
function useFlag(key, fallback) {
|
|
115
|
-
const store =
|
|
115
|
+
const store = useContext(FlagmintStoreContext);
|
|
116
116
|
if (!store) {
|
|
117
117
|
return fallback;
|
|
118
118
|
}
|
|
@@ -120,7 +120,7 @@ function useFlag(key, fallback) {
|
|
|
120
120
|
return flagValue != null ? flagValue : fallback;
|
|
121
121
|
}
|
|
122
122
|
function useFlagmintReady() {
|
|
123
|
-
const store =
|
|
123
|
+
const store = useContext(FlagmintStoreContext);
|
|
124
124
|
if (!store) {
|
|
125
125
|
return false;
|
|
126
126
|
}
|