@gradual-so/sdk-react 0.7.2 → 0.7.3

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/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- 'use strict';var react=require('react'),sdk=require('@gradual-so/sdk'),jsxRuntime=require('react/jsx-runtime');var l=react.createContext({gradual:null,isReady:false,version:0});function f(){let e=react.useContext(l);if(!e.gradual)throw new Error("useFlag must be used within a <GradualProvider>");return {gradual:e.gradual,isReady:e.isReady,version:e.version}}function F(){let{gradual:e,isReady:a}=f();return react.useMemo(()=>({identify:t=>e.identify(t),reset:()=>e.reset(),refresh:()=>e.refresh(),isReady:a}),[e,a])}function b(e,a){let{gradual:t,isReady:r,version:i}=f(),o=react.useMemo(()=>r?t.sync.get(e,{fallback:a.fallback,context:a.context}):a.fallback,[t,r,e,a.fallback,a.context,i]);return a.detail?{value:o,isLoading:!r,isReady:r}:o}function P({apiKey:e,environment:a,baseUrl:t,polling:r,children:i}){let[o,c]=react.useState(false),[u,x]=react.useState(0),n=react.useMemo(()=>sdk.createGradual({apiKey:e,environment:a,baseUrl:t,polling:r}),[e,a,t,r]);react.useEffect(()=>{let s=true;n.ready().then(()=>{s&&c(true);});let m=n.onUpdate(()=>{s&&x(v=>v+1);});return ()=>{s=false,m();}},[n]);let y=react.useMemo(()=>({gradual:n,isReady:o,version:u}),[n,o,u]);return jsxRuntime.jsx(l,{value:y,children:i})}exports.GradualContext=l;exports.GradualProvider=P;exports.useFlag=b;exports.useGradual=F;//# sourceMappingURL=index.cjs.map
1
+ 'use strict';var react=require('react'),sdk=require('@gradual-so/sdk'),jsxRuntime=require('react/jsx-runtime');var u=react.createContext({gradual:null,isReady:false,version:0});function p(){let e=react.useContext(u);if(!e.gradual)throw new Error("useFlag must be used within a <GradualProvider>");return {gradual:e.gradual,isReady:e.isReady,version:e.version}}function R(){let{gradual:e,isReady:t}=p();return react.useMemo(()=>({identify:r=>e.identify(r),reset:()=>e.reset(),refresh:()=>e.refresh(),isReady:t}),[e,t])}function b(e,t){let{gradual:r,isReady:o,version:d}=p(),n=react.useRef(null),a=react.useMemo(()=>o?r.sync.evaluate(e,{context:t.context}):null,[r,o,e,t.context,d]),l=a?.value!==void 0&&a?.value!==null?a.value:t.fallback;return react.useEffect(()=>{if(!a)return;let s=`${e}:${a.variationKey}:${a.reason}`;n.current!==s&&(n.current=s,r.sync.track(e,a,t.context));},[r,e,a]),t.detail?{value:l,isLoading:!o,isReady:o}:l}function O({apiKey:e,environment:t,baseUrl:r,polling:o,children:d}){let[n,a]=react.useState(false),[l,s]=react.useState(0),i=react.useMemo(()=>sdk.createGradual({apiKey:e,environment:t,baseUrl:r,polling:o}),[e,t,r,o]);react.useEffect(()=>{let f=true;i.ready().then(()=>{f&&a(true);});let y=i.onUpdate(()=>{f&&s(m=>m+1);});return ()=>{f=false,y();}},[i]);let v=react.useMemo(()=>({gradual:i,isReady:n,version:l}),[i,n,l]);return jsxRuntime.jsx(u,{value:v,children:d})}exports.GradualContext=u;exports.GradualProvider=O;exports.useFlag=b;exports.useGradual=R;//# sourceMappingURL=index.cjs.map
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/context.ts","../src/hooks.ts","../src/provider.tsx"],"names":["GradualContext","createContext","useGradualContext","context","useContext","useGradual","gradual","isReady","useMemo","ctx","useFlag","key","options","version","value","GradualProvider","apiKey","environment","baseUrl","polling","children","setIsReady","useState","setVersion","createGradual","useEffect","mounted","unsubscribe","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { Gradual } from \"@gradual-so/sdk\";\nimport { createContext } from \"react\";\n\nexport interface GradualContextValue {\n gradual: Gradual | null;\n isReady: boolean;\n version: number;\n}\n\nexport const GradualContext = createContext<GradualContextValue>({\n gradual: null,\n isReady: false,\n version: 0,\n});\n","import type { EvaluationContext, Gradual } from \"@gradual-so/sdk\";\nimport { useContext, useMemo } from \"react\";\nimport { GradualContext } from \"./context\";\n\nexport interface UseFlagOptions<T> {\n fallback: T;\n context?: EvaluationContext;\n detail?: boolean;\n}\n\nexport interface FlagDetail<T> {\n value: T;\n isLoading: boolean;\n isReady: boolean;\n}\n\nfunction useGradualContext(): {\n gradual: Gradual;\n isReady: boolean;\n version: number;\n} {\n const context = useContext(GradualContext);\n if (!context.gradual) {\n throw new Error(\"useFlag must be used within a <GradualProvider>\");\n }\n return {\n gradual: context.gradual,\n isReady: context.isReady,\n version: context.version,\n };\n}\n\n/**\n * Access the Gradual client for identity management\n *\n * @example\n * ```tsx\n * const { identify, reset, isReady } = useGradual()\n *\n * useEffect(() => {\n * if (user) {\n * identify({ userId: user.id, plan: user.plan })\n * } else {\n * reset()\n * }\n * }, [user])\n * ```\n */\nexport function useGradual(): {\n identify: (context: EvaluationContext) => void;\n reset: () => void;\n isReady: boolean;\n refresh: () => Promise<void>;\n} {\n const { gradual, isReady } = useGradualContext();\n\n return useMemo(\n () => ({\n identify: (ctx: EvaluationContext) => gradual.identify(ctx),\n reset: () => gradual.reset(),\n refresh: () => gradual.refresh(),\n isReady,\n }),\n [gradual, isReady]\n );\n}\n\n/**\n * Get a feature flag value with type inference\n *\n * @example\n * ```tsx\n * // Simple usage - returns value directly\n * const showBanner = useFlag('show-banner', { fallback: false })\n * const theme = useFlag('theme', { fallback: 'light' })\n *\n * // With loading state\n * const { value, isLoading } = useFlag('experiment', {\n * fallback: 'control',\n * detail: true\n * })\n *\n * // With context override\n * const enabled = useFlag('feature', {\n * fallback: false,\n * context: { itemId: item.id }\n * })\n * ```\n */\nexport function useFlag<T>(\n key: string,\n options: UseFlagOptions<T> & { detail: true }\n): FlagDetail<T>;\nexport function useFlag<T>(\n key: string,\n options: UseFlagOptions<T> & { detail?: false }\n): T;\nexport function useFlag<T>(\n key: string,\n options: UseFlagOptions<T>\n): T | FlagDetail<T>;\nexport function useFlag<T>(\n key: string,\n options: UseFlagOptions<T>\n): T | FlagDetail<T> {\n const { gradual, isReady, version } = useGradualContext();\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: version triggers re-evaluation on snapshot update\n const value = useMemo(() => {\n if (!isReady) {\n return options.fallback;\n }\n return gradual.sync.get(key, {\n fallback: options.fallback,\n context: options.context,\n });\n }, [gradual, isReady, key, options.fallback, options.context, version]);\n\n if (options.detail) {\n return {\n value,\n isLoading: !isReady,\n isReady,\n };\n }\n\n return value;\n}\n","import { createGradual, type PollingOptions } from \"@gradual-so/sdk\";\nimport { type ReactNode, useEffect, useMemo, useState } from \"react\";\nimport { GradualContext } from \"./context\";\n\nexport interface GradualProviderProps {\n apiKey: string;\n environment: string;\n baseUrl?: string;\n polling?: PollingOptions;\n children: ReactNode;\n}\n\nexport function GradualProvider({\n apiKey,\n environment,\n baseUrl,\n polling,\n children,\n}: GradualProviderProps) {\n const [isReady, setIsReady] = useState(false);\n const [version, setVersion] = useState(0);\n\n const gradual = useMemo(\n () => createGradual({ apiKey, environment, baseUrl, polling }),\n [apiKey, environment, baseUrl, polling]\n );\n\n useEffect(() => {\n let mounted = true;\n gradual.ready().then(() => {\n if (mounted) {\n setIsReady(true);\n }\n });\n\n // Subscribe to snapshot updates from polling\n const unsubscribe = gradual.onUpdate(() => {\n if (mounted) {\n setVersion((v) => v + 1);\n }\n });\n\n return () => {\n mounted = false;\n unsubscribe();\n };\n }, [gradual]);\n\n const value = useMemo(\n () => ({ gradual, isReady, version }),\n [gradual, isReady, version]\n );\n\n return <GradualContext value={value}>{children}</GradualContext>;\n}\n"]}
1
+ {"version":3,"sources":["../src/context.ts","../src/hooks.ts","../src/provider.tsx"],"names":["GradualContext","createContext","useGradualContext","context","useContext","useGradual","gradual","isReady","useMemo","ctx","useFlag","key","options","version","trackedRef","useRef","evalResult","value","useEffect","trackKey","GradualProvider","apiKey","environment","baseUrl","polling","children","setIsReady","useState","setVersion","createGradual","mounted","unsubscribe","v","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { Gradual } from \"@gradual-so/sdk\";\nimport { createContext } from \"react\";\n\nexport interface GradualContextValue {\n gradual: Gradual | null;\n isReady: boolean;\n version: number;\n}\n\nexport const GradualContext = createContext<GradualContextValue>({\n gradual: null,\n isReady: false,\n version: 0,\n});\n","import type { EvalDetail, EvaluationContext, Gradual } from \"@gradual-so/sdk\";\nimport { useContext, useEffect, useMemo, useRef } from \"react\";\nimport { GradualContext } from \"./context\";\n\nexport interface UseFlagOptions<T> {\n fallback: T;\n context?: EvaluationContext;\n detail?: boolean;\n}\n\nexport interface FlagDetail<T> {\n value: T;\n isLoading: boolean;\n isReady: boolean;\n}\n\nfunction useGradualContext(): {\n gradual: Gradual;\n isReady: boolean;\n version: number;\n} {\n const context = useContext(GradualContext);\n if (!context.gradual) {\n throw new Error(\"useFlag must be used within a <GradualProvider>\");\n }\n return {\n gradual: context.gradual,\n isReady: context.isReady,\n version: context.version,\n };\n}\n\n/**\n * Access the Gradual client for identity management\n *\n * @example\n * ```tsx\n * const { identify, reset, isReady } = useGradual()\n *\n * useEffect(() => {\n * if (user) {\n * identify({ userId: user.id, plan: user.plan })\n * } else {\n * reset()\n * }\n * }, [user])\n * ```\n */\nexport function useGradual(): {\n identify: (context: EvaluationContext) => void;\n reset: () => void;\n isReady: boolean;\n refresh: () => Promise<void>;\n} {\n const { gradual, isReady } = useGradualContext();\n\n return useMemo(\n () => ({\n identify: (ctx: EvaluationContext) => gradual.identify(ctx),\n reset: () => gradual.reset(),\n refresh: () => gradual.refresh(),\n isReady,\n }),\n [gradual, isReady]\n );\n}\n\n/**\n * Get a feature flag value with type inference\n *\n * @example\n * ```tsx\n * // Simple usage - returns value directly\n * const showBanner = useFlag('show-banner', { fallback: false })\n * const theme = useFlag('theme', { fallback: 'light' })\n *\n * // With loading state\n * const { value, isLoading } = useFlag('experiment', {\n * fallback: 'control',\n * detail: true\n * })\n *\n * // With context override\n * const enabled = useFlag('feature', {\n * fallback: false,\n * context: { itemId: item.id }\n * })\n * ```\n */\nexport function useFlag<T>(\n key: string,\n options: UseFlagOptions<T> & { detail: true }\n): FlagDetail<T>;\nexport function useFlag<T>(\n key: string,\n options: UseFlagOptions<T> & { detail?: false }\n): T;\nexport function useFlag<T>(\n key: string,\n options: UseFlagOptions<T>\n): T | FlagDetail<T>;\nexport function useFlag<T>(\n key: string,\n options: UseFlagOptions<T>\n): T | FlagDetail<T> {\n const { gradual, isReady, version } = useGradualContext();\n const trackedRef = useRef<string | null>(null);\n\n // Pure evaluation in useMemo — no side effects\n // biome-ignore lint/correctness/useExhaustiveDependencies: version triggers re-evaluation on snapshot update\n const evalResult = useMemo((): EvalDetail | null => {\n if (!isReady) {\n return null;\n }\n return gradual.sync.evaluate(key, { context: options.context });\n }, [gradual, isReady, key, options.context, version]);\n\n const value =\n evalResult?.value !== undefined && evalResult?.value !== null\n ? (evalResult.value as T)\n : options.fallback;\n\n // Track evaluation as a side effect — runs once per distinct evaluation\n // biome-ignore lint/correctness/useExhaustiveDependencies: only track when evalResult changes\n useEffect(() => {\n if (!evalResult) {\n return;\n }\n\n const trackKey = `${key}:${evalResult.variationKey}:${evalResult.reason}`;\n if (trackedRef.current === trackKey) {\n return;\n }\n trackedRef.current = trackKey;\n\n gradual.sync.track(key, evalResult, options.context);\n }, [gradual, key, evalResult]);\n\n if (options.detail) {\n return {\n value,\n isLoading: !isReady,\n isReady,\n };\n }\n\n return value;\n}\n","import { createGradual, type PollingOptions } from \"@gradual-so/sdk\";\nimport { type ReactNode, useEffect, useMemo, useState } from \"react\";\nimport { GradualContext } from \"./context\";\n\nexport interface GradualProviderProps {\n apiKey: string;\n environment: string;\n baseUrl?: string;\n polling?: PollingOptions;\n children: ReactNode;\n}\n\nexport function GradualProvider({\n apiKey,\n environment,\n baseUrl,\n polling,\n children,\n}: GradualProviderProps) {\n const [isReady, setIsReady] = useState(false);\n const [version, setVersion] = useState(0);\n\n const gradual = useMemo(\n () => createGradual({ apiKey, environment, baseUrl, polling }),\n [apiKey, environment, baseUrl, polling]\n );\n\n useEffect(() => {\n let mounted = true;\n gradual.ready().then(() => {\n if (mounted) {\n setIsReady(true);\n }\n });\n\n // Subscribe to snapshot updates from polling\n const unsubscribe = gradual.onUpdate(() => {\n if (mounted) {\n setVersion((v) => v + 1);\n }\n });\n\n return () => {\n mounted = false;\n unsubscribe();\n };\n }, [gradual]);\n\n const value = useMemo(\n () => ({ gradual, isReady, version }),\n [gradual, isReady, version]\n );\n\n return <GradualContext value={value}>{children}</GradualContext>;\n}\n"]}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import {createContext,useMemo,useState,useEffect,useContext}from'react';import {createGradual}from'@gradual-so/sdk';import {jsx}from'react/jsx-runtime';var l=createContext({gradual:null,isReady:false,version:0});function f(){let e=useContext(l);if(!e.gradual)throw new Error("useFlag must be used within a <GradualProvider>");return {gradual:e.gradual,isReady:e.isReady,version:e.version}}function F(){let{gradual:e,isReady:a}=f();return useMemo(()=>({identify:t=>e.identify(t),reset:()=>e.reset(),refresh:()=>e.refresh(),isReady:a}),[e,a])}function b(e,a){let{gradual:t,isReady:r,version:i}=f(),o=useMemo(()=>r?t.sync.get(e,{fallback:a.fallback,context:a.context}):a.fallback,[t,r,e,a.fallback,a.context,i]);return a.detail?{value:o,isLoading:!r,isReady:r}:o}function P({apiKey:e,environment:a,baseUrl:t,polling:r,children:i}){let[o,c]=useState(false),[u,x]=useState(0),n=useMemo(()=>createGradual({apiKey:e,environment:a,baseUrl:t,polling:r}),[e,a,t,r]);useEffect(()=>{let s=true;n.ready().then(()=>{s&&c(true);});let m=n.onUpdate(()=>{s&&x(v=>v+1);});return ()=>{s=false,m();}},[n]);let y=useMemo(()=>({gradual:n,isReady:o,version:u}),[n,o,u]);return jsx(l,{value:y,children:i})}export{l as GradualContext,P as GradualProvider,b as useFlag,F as useGradual};//# sourceMappingURL=index.js.map
1
+ import {createContext,useMemo,useRef,useEffect,useState,useContext}from'react';import {createGradual}from'@gradual-so/sdk';import {jsx}from'react/jsx-runtime';var u=createContext({gradual:null,isReady:false,version:0});function p(){let e=useContext(u);if(!e.gradual)throw new Error("useFlag must be used within a <GradualProvider>");return {gradual:e.gradual,isReady:e.isReady,version:e.version}}function R(){let{gradual:e,isReady:t}=p();return useMemo(()=>({identify:r=>e.identify(r),reset:()=>e.reset(),refresh:()=>e.refresh(),isReady:t}),[e,t])}function b(e,t){let{gradual:r,isReady:o,version:d}=p(),n=useRef(null),a=useMemo(()=>o?r.sync.evaluate(e,{context:t.context}):null,[r,o,e,t.context,d]),l=a?.value!==void 0&&a?.value!==null?a.value:t.fallback;return useEffect(()=>{if(!a)return;let s=`${e}:${a.variationKey}:${a.reason}`;n.current!==s&&(n.current=s,r.sync.track(e,a,t.context));},[r,e,a]),t.detail?{value:l,isLoading:!o,isReady:o}:l}function O({apiKey:e,environment:t,baseUrl:r,polling:o,children:d}){let[n,a]=useState(false),[l,s]=useState(0),i=useMemo(()=>createGradual({apiKey:e,environment:t,baseUrl:r,polling:o}),[e,t,r,o]);useEffect(()=>{let f=true;i.ready().then(()=>{f&&a(true);});let y=i.onUpdate(()=>{f&&s(m=>m+1);});return ()=>{f=false,y();}},[i]);let v=useMemo(()=>({gradual:i,isReady:n,version:l}),[i,n,l]);return jsx(u,{value:v,children:d})}export{u as GradualContext,O as GradualProvider,b as useFlag,R as useGradual};//# sourceMappingURL=index.js.map
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/context.ts","../src/hooks.ts","../src/provider.tsx"],"names":["GradualContext","createContext","useGradualContext","context","useContext","useGradual","gradual","isReady","useMemo","ctx","useFlag","key","options","version","value","GradualProvider","apiKey","environment","baseUrl","polling","children","setIsReady","useState","setVersion","createGradual","useEffect","mounted","unsubscribe","jsx"],"mappings":"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","file":"index.js","sourcesContent":["import type { Gradual } from \"@gradual-so/sdk\";\nimport { createContext } from \"react\";\n\nexport interface GradualContextValue {\n gradual: Gradual | null;\n isReady: boolean;\n version: number;\n}\n\nexport const GradualContext = createContext<GradualContextValue>({\n gradual: null,\n isReady: false,\n version: 0,\n});\n","import type { EvaluationContext, Gradual } from \"@gradual-so/sdk\";\nimport { useContext, useMemo } from \"react\";\nimport { GradualContext } from \"./context\";\n\nexport interface UseFlagOptions<T> {\n fallback: T;\n context?: EvaluationContext;\n detail?: boolean;\n}\n\nexport interface FlagDetail<T> {\n value: T;\n isLoading: boolean;\n isReady: boolean;\n}\n\nfunction useGradualContext(): {\n gradual: Gradual;\n isReady: boolean;\n version: number;\n} {\n const context = useContext(GradualContext);\n if (!context.gradual) {\n throw new Error(\"useFlag must be used within a <GradualProvider>\");\n }\n return {\n gradual: context.gradual,\n isReady: context.isReady,\n version: context.version,\n };\n}\n\n/**\n * Access the Gradual client for identity management\n *\n * @example\n * ```tsx\n * const { identify, reset, isReady } = useGradual()\n *\n * useEffect(() => {\n * if (user) {\n * identify({ userId: user.id, plan: user.plan })\n * } else {\n * reset()\n * }\n * }, [user])\n * ```\n */\nexport function useGradual(): {\n identify: (context: EvaluationContext) => void;\n reset: () => void;\n isReady: boolean;\n refresh: () => Promise<void>;\n} {\n const { gradual, isReady } = useGradualContext();\n\n return useMemo(\n () => ({\n identify: (ctx: EvaluationContext) => gradual.identify(ctx),\n reset: () => gradual.reset(),\n refresh: () => gradual.refresh(),\n isReady,\n }),\n [gradual, isReady]\n );\n}\n\n/**\n * Get a feature flag value with type inference\n *\n * @example\n * ```tsx\n * // Simple usage - returns value directly\n * const showBanner = useFlag('show-banner', { fallback: false })\n * const theme = useFlag('theme', { fallback: 'light' })\n *\n * // With loading state\n * const { value, isLoading } = useFlag('experiment', {\n * fallback: 'control',\n * detail: true\n * })\n *\n * // With context override\n * const enabled = useFlag('feature', {\n * fallback: false,\n * context: { itemId: item.id }\n * })\n * ```\n */\nexport function useFlag<T>(\n key: string,\n options: UseFlagOptions<T> & { detail: true }\n): FlagDetail<T>;\nexport function useFlag<T>(\n key: string,\n options: UseFlagOptions<T> & { detail?: false }\n): T;\nexport function useFlag<T>(\n key: string,\n options: UseFlagOptions<T>\n): T | FlagDetail<T>;\nexport function useFlag<T>(\n key: string,\n options: UseFlagOptions<T>\n): T | FlagDetail<T> {\n const { gradual, isReady, version } = useGradualContext();\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: version triggers re-evaluation on snapshot update\n const value = useMemo(() => {\n if (!isReady) {\n return options.fallback;\n }\n return gradual.sync.get(key, {\n fallback: options.fallback,\n context: options.context,\n });\n }, [gradual, isReady, key, options.fallback, options.context, version]);\n\n if (options.detail) {\n return {\n value,\n isLoading: !isReady,\n isReady,\n };\n }\n\n return value;\n}\n","import { createGradual, type PollingOptions } from \"@gradual-so/sdk\";\nimport { type ReactNode, useEffect, useMemo, useState } from \"react\";\nimport { GradualContext } from \"./context\";\n\nexport interface GradualProviderProps {\n apiKey: string;\n environment: string;\n baseUrl?: string;\n polling?: PollingOptions;\n children: ReactNode;\n}\n\nexport function GradualProvider({\n apiKey,\n environment,\n baseUrl,\n polling,\n children,\n}: GradualProviderProps) {\n const [isReady, setIsReady] = useState(false);\n const [version, setVersion] = useState(0);\n\n const gradual = useMemo(\n () => createGradual({ apiKey, environment, baseUrl, polling }),\n [apiKey, environment, baseUrl, polling]\n );\n\n useEffect(() => {\n let mounted = true;\n gradual.ready().then(() => {\n if (mounted) {\n setIsReady(true);\n }\n });\n\n // Subscribe to snapshot updates from polling\n const unsubscribe = gradual.onUpdate(() => {\n if (mounted) {\n setVersion((v) => v + 1);\n }\n });\n\n return () => {\n mounted = false;\n unsubscribe();\n };\n }, [gradual]);\n\n const value = useMemo(\n () => ({ gradual, isReady, version }),\n [gradual, isReady, version]\n );\n\n return <GradualContext value={value}>{children}</GradualContext>;\n}\n"]}
1
+ {"version":3,"sources":["../src/context.ts","../src/hooks.ts","../src/provider.tsx"],"names":["GradualContext","createContext","useGradualContext","context","useContext","useGradual","gradual","isReady","useMemo","ctx","useFlag","key","options","version","trackedRef","useRef","evalResult","value","useEffect","trackKey","GradualProvider","apiKey","environment","baseUrl","polling","children","setIsReady","useState","setVersion","createGradual","mounted","unsubscribe","v","jsx"],"mappings":"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","file":"index.js","sourcesContent":["import type { Gradual } from \"@gradual-so/sdk\";\nimport { createContext } from \"react\";\n\nexport interface GradualContextValue {\n gradual: Gradual | null;\n isReady: boolean;\n version: number;\n}\n\nexport const GradualContext = createContext<GradualContextValue>({\n gradual: null,\n isReady: false,\n version: 0,\n});\n","import type { EvalDetail, EvaluationContext, Gradual } from \"@gradual-so/sdk\";\nimport { useContext, useEffect, useMemo, useRef } from \"react\";\nimport { GradualContext } from \"./context\";\n\nexport interface UseFlagOptions<T> {\n fallback: T;\n context?: EvaluationContext;\n detail?: boolean;\n}\n\nexport interface FlagDetail<T> {\n value: T;\n isLoading: boolean;\n isReady: boolean;\n}\n\nfunction useGradualContext(): {\n gradual: Gradual;\n isReady: boolean;\n version: number;\n} {\n const context = useContext(GradualContext);\n if (!context.gradual) {\n throw new Error(\"useFlag must be used within a <GradualProvider>\");\n }\n return {\n gradual: context.gradual,\n isReady: context.isReady,\n version: context.version,\n };\n}\n\n/**\n * Access the Gradual client for identity management\n *\n * @example\n * ```tsx\n * const { identify, reset, isReady } = useGradual()\n *\n * useEffect(() => {\n * if (user) {\n * identify({ userId: user.id, plan: user.plan })\n * } else {\n * reset()\n * }\n * }, [user])\n * ```\n */\nexport function useGradual(): {\n identify: (context: EvaluationContext) => void;\n reset: () => void;\n isReady: boolean;\n refresh: () => Promise<void>;\n} {\n const { gradual, isReady } = useGradualContext();\n\n return useMemo(\n () => ({\n identify: (ctx: EvaluationContext) => gradual.identify(ctx),\n reset: () => gradual.reset(),\n refresh: () => gradual.refresh(),\n isReady,\n }),\n [gradual, isReady]\n );\n}\n\n/**\n * Get a feature flag value with type inference\n *\n * @example\n * ```tsx\n * // Simple usage - returns value directly\n * const showBanner = useFlag('show-banner', { fallback: false })\n * const theme = useFlag('theme', { fallback: 'light' })\n *\n * // With loading state\n * const { value, isLoading } = useFlag('experiment', {\n * fallback: 'control',\n * detail: true\n * })\n *\n * // With context override\n * const enabled = useFlag('feature', {\n * fallback: false,\n * context: { itemId: item.id }\n * })\n * ```\n */\nexport function useFlag<T>(\n key: string,\n options: UseFlagOptions<T> & { detail: true }\n): FlagDetail<T>;\nexport function useFlag<T>(\n key: string,\n options: UseFlagOptions<T> & { detail?: false }\n): T;\nexport function useFlag<T>(\n key: string,\n options: UseFlagOptions<T>\n): T | FlagDetail<T>;\nexport function useFlag<T>(\n key: string,\n options: UseFlagOptions<T>\n): T | FlagDetail<T> {\n const { gradual, isReady, version } = useGradualContext();\n const trackedRef = useRef<string | null>(null);\n\n // Pure evaluation in useMemo — no side effects\n // biome-ignore lint/correctness/useExhaustiveDependencies: version triggers re-evaluation on snapshot update\n const evalResult = useMemo((): EvalDetail | null => {\n if (!isReady) {\n return null;\n }\n return gradual.sync.evaluate(key, { context: options.context });\n }, [gradual, isReady, key, options.context, version]);\n\n const value =\n evalResult?.value !== undefined && evalResult?.value !== null\n ? (evalResult.value as T)\n : options.fallback;\n\n // Track evaluation as a side effect — runs once per distinct evaluation\n // biome-ignore lint/correctness/useExhaustiveDependencies: only track when evalResult changes\n useEffect(() => {\n if (!evalResult) {\n return;\n }\n\n const trackKey = `${key}:${evalResult.variationKey}:${evalResult.reason}`;\n if (trackedRef.current === trackKey) {\n return;\n }\n trackedRef.current = trackKey;\n\n gradual.sync.track(key, evalResult, options.context);\n }, [gradual, key, evalResult]);\n\n if (options.detail) {\n return {\n value,\n isLoading: !isReady,\n isReady,\n };\n }\n\n return value;\n}\n","import { createGradual, type PollingOptions } from \"@gradual-so/sdk\";\nimport { type ReactNode, useEffect, useMemo, useState } from \"react\";\nimport { GradualContext } from \"./context\";\n\nexport interface GradualProviderProps {\n apiKey: string;\n environment: string;\n baseUrl?: string;\n polling?: PollingOptions;\n children: ReactNode;\n}\n\nexport function GradualProvider({\n apiKey,\n environment,\n baseUrl,\n polling,\n children,\n}: GradualProviderProps) {\n const [isReady, setIsReady] = useState(false);\n const [version, setVersion] = useState(0);\n\n const gradual = useMemo(\n () => createGradual({ apiKey, environment, baseUrl, polling }),\n [apiKey, environment, baseUrl, polling]\n );\n\n useEffect(() => {\n let mounted = true;\n gradual.ready().then(() => {\n if (mounted) {\n setIsReady(true);\n }\n });\n\n // Subscribe to snapshot updates from polling\n const unsubscribe = gradual.onUpdate(() => {\n if (mounted) {\n setVersion((v) => v + 1);\n }\n });\n\n return () => {\n mounted = false;\n unsubscribe();\n };\n }, [gradual]);\n\n const value = useMemo(\n () => ({ gradual, isReady, version }),\n [gradual, isReady, version]\n );\n\n return <GradualContext value={value}>{children}</GradualContext>;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradual-so/sdk-react",
3
- "version": "0.7.2",
3
+ "version": "0.7.3",
4
4
  "description": "Gradual feature flag SDK for React",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -47,7 +47,7 @@
47
47
  "typecheck": "tsc --noEmit --emitDeclarationOnly false"
48
48
  },
49
49
  "dependencies": {
50
- "@gradual-so/sdk": "^0.7.2"
50
+ "@gradual-so/sdk": "^0.7.3"
51
51
  },
52
52
  "peerDependencies": {
53
53
  "react": ">=18.0.0"