@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 +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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
|
|
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
|
package/dist/index.cjs.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","
|
|
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,
|
|
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","
|
|
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.
|
|
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.
|
|
50
|
+
"@gradual-so/sdk": "^0.7.3"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"react": ">=18.0.0"
|