@supashiphq/react-sdk 0.7.7 → 0.7.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -295,6 +295,66 @@ function FeatureList() {
295
295
  }
296
296
  ```
297
297
 
298
+ ### SupaFeature Component
299
+
300
+ A declarative component for rendering different UI based on boolean feature flag values.
301
+
302
+ ```tsx
303
+ <SupaFeature
304
+ feature="feature-name"
305
+ loading={<Skeleton />}
306
+ variations={{
307
+ true: <ComponentA />,
308
+ false: <ComponentB />,
309
+ }}
310
+ />
311
+ ```
312
+
313
+ **Props:**
314
+
315
+ | Prop | Type | Required | Description |
316
+ | ------------- | --------------------------------------- | -------- | ---------------------------------------------------------------- |
317
+ | `feature` | `string` | Yes | The feature flag key to evaluate |
318
+ | `variations` | `{ true: ReactNode, false: ReactNode }` | Yes | Components to render for true/false values |
319
+ | `loading` | `ReactNode` | No | Component to render while loading |
320
+ | `fallback` | `ReactNode` | No | Component to render when feature value is neither true nor false |
321
+ | `context` | `Record<string, unknown>` | No | Context override for this feature |
322
+ | `shouldFetch` | `boolean` | No | Whether to fetch the feature (default: true) |
323
+
324
+ **Examples:**
325
+
326
+ ```tsx
327
+ // Simple boolean feature flag
328
+ function Header() {
329
+ return (
330
+ <SupaFeature
331
+ feature="new-header"
332
+ loading={<HeaderSkeleton />}
333
+ variations={{
334
+ true: <NewHeader />,
335
+ false: <OldHeader />,
336
+ }}
337
+ />
338
+ )
339
+ }
340
+
341
+ // With context
342
+ function UserDashboard() {
343
+ const { user } = useAuth()
344
+
345
+ return (
346
+ <SupaFeature
347
+ feature="beta-dashboard"
348
+ context={{ userId: user.id, plan: user.plan }}
349
+ variations={{
350
+ true: <BetaDashboard />,
351
+ false: <StandardDashboard />,
352
+ }}
353
+ />
354
+ )
355
+ }
356
+ ```
357
+
298
358
  ### useFeatureContext Hook
299
359
 
300
360
  Access and update the feature context within components.
package/dist/index.d.mts CHANGED
@@ -231,13 +231,16 @@ interface SupaFeatureProps {
231
231
  */
232
232
  shouldFetch?: boolean;
233
233
  /**
234
- * Variations object mapping feature values/keys to JSX elements
234
+ * Variations object mapping "true" or "false" to JSX elements
235
235
  */
236
- variations: Record<string, ReactNode>;
236
+ variations: {
237
+ true: ReactNode;
238
+ false: ReactNode;
239
+ };
237
240
  /**
238
- * Key in variations object to use for loading state
241
+ * Component to render during loading state
239
242
  */
240
- loading?: string;
243
+ loading?: ReactNode;
241
244
  }
242
245
  /**
243
246
  * SupaFeature component that conditionally renders variations based on feature flag values.
@@ -247,10 +250,10 @@ interface SupaFeatureProps {
247
250
  * ```tsx
248
251
  * <SupaFeature
249
252
  * feature="new-header"
253
+ * loading={<HeaderSkeleton />}
250
254
  * variations={{
251
- * "true": <NewHeader />,
252
- * "false": <OldHeader />,
253
- * loading: <HeaderSkeleton />
255
+ * true: <NewHeader />,
256
+ * false: <OldHeader />
254
257
  * }}
255
258
  * />
256
259
  * ```
package/dist/index.d.ts CHANGED
@@ -231,13 +231,16 @@ interface SupaFeatureProps {
231
231
  */
232
232
  shouldFetch?: boolean;
233
233
  /**
234
- * Variations object mapping feature values/keys to JSX elements
234
+ * Variations object mapping "true" or "false" to JSX elements
235
235
  */
236
- variations: Record<string, ReactNode>;
236
+ variations: {
237
+ true: ReactNode;
238
+ false: ReactNode;
239
+ };
237
240
  /**
238
- * Key in variations object to use for loading state
241
+ * Component to render during loading state
239
242
  */
240
- loading?: string;
243
+ loading?: ReactNode;
241
244
  }
242
245
  /**
243
246
  * SupaFeature component that conditionally renders variations based on feature flag values.
@@ -247,10 +250,10 @@ interface SupaFeatureProps {
247
250
  * ```tsx
248
251
  * <SupaFeature
249
252
  * feature="new-header"
253
+ * loading={<HeaderSkeleton />}
250
254
  * variations={{
251
- * "true": <NewHeader />,
252
- * "false": <OldHeader />,
253
- * loading: <HeaderSkeleton />
255
+ * true: <NewHeader />,
256
+ * false: <OldHeader />
254
257
  * }}
255
258
  * />
256
259
  * ```
package/dist/index.js CHANGED
@@ -437,18 +437,15 @@ function SupaFeature({
437
437
  context,
438
438
  shouldFetch
439
439
  });
440
- if (isLoading && loading && variations[loading]) {
441
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: variations[loading] });
442
- }
443
440
  if (isLoading) {
444
- return null;
445
- }
446
- if (!hasValue(featureValue)) {
447
- return null;
441
+ return loading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: loading }) : null;
448
442
  }
449
443
  const valueKey = String(featureValue);
450
- if (variations[valueKey]) {
451
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: variations[valueKey] });
444
+ if (variations.true && valueKey === "true") {
445
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: variations.true });
446
+ }
447
+ if (variations.false && (valueKey === "false" || !hasValue(featureValue))) {
448
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: variations.false });
452
449
  }
453
450
  return null;
454
451
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/provider.tsx","../src/query.ts","../src/hooks.ts","../src/utils.ts","../src/components.tsx"],"sourcesContent":["'use client'\n\nexport * from './types'\nexport * from './hooks'\nexport * from './provider'\nexport * from './query'\nexport * from './components'\n","'use client'\n\nimport React, {\n createContext,\n useContext,\n ReactNode,\n useMemo,\n useCallback,\n useRef,\n useEffect,\n} from 'react'\nimport {\n SupaClient,\n SupaClientConfig as SupaProviderConfig,\n FeatureContext,\n FeatureValue,\n Features,\n SupaToolbarOverrideChange,\n SupaToolbarPluginConfig,\n} from '@supashiphq/javascript-sdk'\nimport { useQueryClient } from './query'\n\ninterface SupaContextValue<TFeatures extends Features<Record<string, FeatureValue>>> {\n client: SupaClient<TFeatures>\n updateContext: (context: FeatureContext, mergeWithExisting?: boolean) => void\n getContext: () => FeatureContext | undefined\n}\n\nconst SupaContext = createContext<SupaContextValue<any> | null>(null)\n\ninterface SupaProviderProps<TFeatures extends Features<Record<string, FeatureValue>>> {\n config: Omit<SupaProviderConfig, 'plugins' | 'toolbar'> & { features: TFeatures }\n toolbar?: SupaToolbarPluginConfig | boolean\n children: ReactNode\n}\n\n// Default toolbar config (stable reference)\nconst DEFAULT_TOOLBAR_CONFIG: SupaToolbarPluginConfig = { enabled: 'auto' }\n\nexport function SupaProvider<TFeatures extends Features<Record<string, FeatureValue>>>({\n config,\n toolbar,\n children,\n}: SupaProviderProps<TFeatures>): React.JSX.Element {\n const queryClient = useQueryClient()\n\n // Use default if toolbar not provided\n const effectiveToolbar = toolbar ?? DEFAULT_TOOLBAR_CONFIG\n\n // Stable callback for toolbar override changes\n const handleOverrideChange = useCallback(\n (\n featureOverride: SupaToolbarOverrideChange,\n allOverrides: Record<string, FeatureValue>\n ): void => {\n // Call user's onOverrideChange if provided\n if (typeof effectiveToolbar === 'object' && effectiveToolbar.onOverrideChange) {\n effectiveToolbar.onOverrideChange(featureOverride, allOverrides)\n }\n\n // Invalidate the query cache for the changed feature to trigger refetch\n if (featureOverride.feature) {\n // Invalidate single feature queries (useFeature)\n queryClient.invalidateQueriesByPrefix(['feature', featureOverride.feature])\n // Invalidate multi-feature queries (useFeatures) that include this feature\n queryClient.invalidateQueriesByPrefix(['features'])\n }\n },\n [effectiveToolbar, queryClient]\n )\n\n // Use ref to persist client across StrictMode double-renders in development\n const clientRef = useRef<SupaClient<TFeatures> | null>(null)\n\n // Initialize client only once to prevent duplicate toolbars in StrictMode\n if (!clientRef.current) {\n // Merge toolbar config with React Query cache invalidation\n const toolbarConfig =\n effectiveToolbar === false\n ? false\n : {\n ...(typeof effectiveToolbar === 'object' ? effectiveToolbar : {}),\n onOverrideChange: handleOverrideChange,\n }\n\n clientRef.current = new SupaClient<TFeatures>({\n ...config,\n toolbar: toolbarConfig,\n })\n }\n\n const client = clientRef.current\n\n // Cleanup client on unmount\n useEffect(() => {\n return () => {\n if (clientRef.current) {\n // Cleanup plugins (which includes toolbar cleanup)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const client = clientRef.current as any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (client.plugins && Array.isArray(client.plugins)) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n client.plugins.forEach((plugin: any) => {\n if (plugin.cleanup) {\n plugin.cleanup()\n }\n })\n }\n clientRef.current = null\n }\n }\n }, [])\n\n // Memoized context update function\n const updateContext = useCallback(\n (context: FeatureContext, mergeWithExisting: boolean = true) => {\n client.updateContext(context, mergeWithExisting)\n },\n [client]\n )\n\n // Memoized context getter function\n const getContext = useCallback(() => {\n return client.getContext()\n }, [client])\n\n const contextValue = useMemo(\n () => ({\n client,\n updateContext,\n getContext,\n }),\n [client, updateContext, getContext]\n )\n\n return <SupaContext.Provider value={contextValue}>{children}</SupaContext.Provider>\n}\n\nexport function useClient<\n TFeatures extends Features<Record<string, FeatureValue>>,\n>(): SupaClient<TFeatures> {\n const context = useContext(SupaContext)\n if (!context) {\n throw new Error('useClient must be used within a SupaProvider')\n }\n return context.client as SupaClient<TFeatures>\n}\n\n/**\n * Hook to update the context dynamically\n * Useful when context depends on authentication or other async operations\n */\nexport function useFeatureContext(): Omit<SupaContextValue<any>, 'client'> {\n const context = useContext(SupaContext)\n if (!context) {\n throw new Error('useFeatureContext must be used within a SupaProvider')\n }\n return {\n updateContext: context.updateContext,\n getContext: context.getContext,\n }\n}\n","'use client'\n\nimport { useCallback, useEffect, useReducer, useRef } from 'react'\n\n// Query status types\nexport type QueryStatus = 'idle' | 'loading' | 'success' | 'error'\n\n// Base query state (managed by reducer)\nexport interface BaseQueryState<TData = unknown, TError = Error> {\n status: QueryStatus\n data: TData | undefined\n error: TError | null\n isLoading: boolean\n isSuccess: boolean\n isError: boolean\n isIdle: boolean\n isFetching: boolean\n dataUpdatedAt: number\n}\n\n// Query state with refetch method (returned by useQuery)\nexport interface QueryState<TData = unknown, TError = Error> extends BaseQueryState<TData, TError> {\n refetch: () => Promise<void>\n}\n\n// Initial state factory for queries\nexport function getInitialQueryState<TData, TError>(\n initialData?: TData,\n enabled: boolean = true\n): BaseQueryState<TData, TError> {\n return {\n status: !enabled ? 'idle' : initialData !== undefined ? 'success' : 'idle',\n data: initialData,\n error: null,\n isLoading: enabled && initialData === undefined,\n isSuccess: initialData !== undefined,\n isError: false,\n isIdle: !enabled || initialData === undefined,\n isFetching: false,\n dataUpdatedAt: initialData !== undefined ? Date.now() : 0,\n }\n}\n\n// Query options\nexport interface UseQueryOptions<TData = unknown, TError = Error> {\n enabled?: boolean\n retry?: number | boolean\n retryDelay?: number\n staleTime?: number\n cacheTime?: number\n refetchOnWindowFocus?: boolean\n initialData?: TData\n onSuccess?: (data: TData) => void\n onError?: (error: TError) => void\n onSettled?: (data: TData | undefined, error: TError | null) => void\n}\n\n// Query key type\nexport type QueryKey = unknown[]\n\n// Actions for the query reducer\ntype QueryAction<TData, TError> =\n | { type: 'FETCH_START' }\n | { type: 'FETCH_SUCCESS'; payload: { data: TData } }\n | { type: 'FETCH_ERROR'; payload: { error: TError } }\n\n// Reducer for query state\nfunction queryReducer<TData, TError>(\n state: BaseQueryState<TData, TError>,\n action: QueryAction<TData, TError>\n): BaseQueryState<TData, TError> {\n switch (action.type) {\n case 'FETCH_START':\n return {\n ...state,\n status: state.data === undefined ? 'loading' : state.status,\n isLoading: state.data === undefined,\n isIdle: false,\n isFetching: true,\n }\n case 'FETCH_SUCCESS':\n return {\n ...state,\n status: 'success',\n data: action.payload.data,\n error: null,\n isLoading: false,\n isSuccess: true,\n isError: false,\n isIdle: false,\n isFetching: false,\n dataUpdatedAt: Date.now(),\n }\n case 'FETCH_ERROR':\n return {\n ...state,\n status: 'error',\n error: action.payload.error,\n isLoading: false,\n isSuccess: !!state.data,\n isError: true,\n isIdle: false,\n isFetching: false,\n }\n default:\n return state\n }\n}\n\n// Cache implementation\nclass QueryCache {\n private cache: Map<string, { data: unknown; timestamp: number }> = new Map()\n private timers: Map<string, ReturnType<typeof setTimeout>> = new Map()\n private observers: Map<string, Set<() => void>> = new Map()\n\n getQuery(queryKey: string): unknown {\n const entry = this.cache.get(queryKey)\n return entry ? entry.data : undefined\n }\n\n isStale(queryKey: string, staleTime: number): boolean {\n const entry = this.cache.get(queryKey)\n if (!entry) return true\n return Date.now() - entry.timestamp > staleTime\n }\n\n setQuery(queryKey: string, data: unknown, cacheTime: number = 5 * 60 * 1000): void {\n this.cache.set(queryKey, { data, timestamp: Date.now() })\n\n // Clear previous timer if it exists\n if (this.timers.has(queryKey)) {\n clearTimeout(this.timers.get(queryKey)!)\n }\n\n // Set new timer for cache expiration\n const timer = setTimeout(() => {\n this.cache.delete(queryKey)\n this.timers.delete(queryKey)\n }, cacheTime)\n\n this.timers.set(queryKey, timer)\n }\n\n subscribe(queryKey: string, callback: () => void): () => void {\n if (!this.observers.has(queryKey)) {\n this.observers.set(queryKey, new Set())\n }\n this.observers.get(queryKey)!.add(callback)\n\n // Return unsubscribe function\n return () => {\n const callbacks = this.observers.get(queryKey)\n if (callbacks) {\n callbacks.delete(callback)\n if (callbacks.size === 0) {\n this.observers.delete(queryKey)\n }\n }\n }\n }\n\n private notifyObservers(queryKey: string): void {\n const callbacks = this.observers.get(queryKey)\n if (callbacks) {\n callbacks.forEach(callback => callback())\n }\n }\n\n invalidateQuery(queryKey: string): void {\n this.cache.delete(queryKey)\n if (this.timers.has(queryKey)) {\n clearTimeout(this.timers.get(queryKey)!)\n this.timers.delete(queryKey)\n }\n this.notifyObservers(queryKey)\n }\n\n invalidateQueries(queryKeyPrefix: string): void {\n for (const [key] of this.cache) {\n if (key.startsWith(queryKeyPrefix)) {\n this.invalidateQuery(key)\n }\n }\n }\n\n clear(): void {\n this.cache.clear()\n this.timers.forEach(timer => clearTimeout(timer))\n this.timers.clear()\n this.observers.clear()\n }\n}\n\n// Singleton instance of QueryCache\nexport const queryCache = new QueryCache()\n\n// Query Client class for managing queries\nexport class QueryClient {\n private cache: QueryCache\n\n constructor(cache: QueryCache = queryCache) {\n this.cache = cache\n }\n\n /**\n * Invalidates a specific query by its query key\n * This will remove it from cache and trigger refetch in all components using this query\n */\n invalidateQueries(queryKey: QueryKey): void {\n const stringifiedKey = stableStringifyQueryKey(queryKey)\n this.cache.invalidateQuery(stringifiedKey)\n }\n\n /**\n * Invalidates all queries matching a partial query key\n * For example, invalidateQueriesByPrefix(['feature']) will invalidate all feature queries\n */\n invalidateQueriesByPrefix(queryKeyPrefix: QueryKey): void {\n const stringifiedPrefix = stableStringifyQueryKey(queryKeyPrefix)\n this.cache.invalidateQueries(stringifiedPrefix.slice(0, -1)) // Remove closing bracket\n }\n\n /**\n * Clears all queries from the cache\n */\n clear(): void {\n this.cache.clear()\n }\n}\n\n// Singleton instance of QueryClient\nconst queryClient = new QueryClient()\n\n// Hook to access the query client\nexport function useQueryClient(): QueryClient {\n return queryClient\n}\n\n// Stable stringify for query keys\nfunction stableStringifyQueryKey(queryKey: QueryKey): string {\n return JSON.stringify(queryKey, (_, val) => (typeof val === 'function' ? val.toString() : val))\n}\n\n// The useQuery hook\nexport function useQuery<TData = unknown, TError = Error>(\n queryKey: QueryKey,\n queryFn: () => Promise<TData>,\n options: UseQueryOptions<TData, TError> = {}\n): QueryState<TData, TError> {\n const {\n enabled = true,\n retry = 3,\n retryDelay = 1000,\n staleTime = 0,\n cacheTime = 5 * 60 * 1000,\n refetchOnWindowFocus = true,\n initialData,\n } = options\n\n const stringifiedQueryKey = stableStringifyQueryKey(queryKey)\n const queryFnRef = useRef(queryFn)\n const optionsRef = useRef(options)\n\n // Update refs when dependencies change\n useEffect(() => {\n queryFnRef.current = queryFn\n optionsRef.current = options\n }, [queryFn, options])\n\n // Initialize state from cache or with default\n const cachedData = queryCache.getQuery(stringifiedQueryKey)\n const isStale = queryCache.isStale(stringifiedQueryKey, staleTime)\n\n // Use cached data for initial state even if stale to prevent unnecessary loading states\n const initialStateData = cachedData !== undefined ? (cachedData as TData) : initialData\n const initialState = getInitialQueryState<TData, TError>(initialStateData, enabled)\n const [state, dispatch] = useReducer(queryReducer<TData, TError>, initialState)\n\n const executeFetch = useCallback(async (): Promise<void> => {\n if (!enabled) return\n\n dispatch({ type: 'FETCH_START' })\n\n let retryCount = 0\n const maxRetries = typeof retry === 'boolean' ? (retry ? 3 : 0) : retry\n\n const runQuery = async (): Promise<void> => {\n try {\n const data = await queryFnRef.current()\n\n // Update cache\n queryCache.setQuery(stringifiedQueryKey, data, cacheTime)\n\n dispatch({ type: 'FETCH_SUCCESS', payload: { data } })\n\n if (optionsRef.current.onSuccess) {\n optionsRef.current.onSuccess(data)\n }\n\n if (optionsRef.current.onSettled) {\n optionsRef.current.onSettled(data, null)\n }\n } catch (err) {\n const error = err as TError\n\n if (retryCount < maxRetries) {\n retryCount++\n await new Promise(resolve => setTimeout(resolve, retryDelay))\n return runQuery()\n }\n\n dispatch({ type: 'FETCH_ERROR', payload: { error } })\n\n if (optionsRef.current.onError) {\n optionsRef.current.onError(error)\n }\n\n if (optionsRef.current.onSettled) {\n optionsRef.current.onSettled(undefined, error)\n }\n }\n }\n\n await runQuery()\n }, [enabled, stringifiedQueryKey, retry, retryDelay, cacheTime])\n\n // Execute query on mount and when dependencies change\n useEffect(() => {\n if (enabled && (isStale || state.data === undefined)) {\n executeFetch()\n }\n }, [enabled, isStale, executeFetch, state.data])\n\n // Handle window focus\n useEffect(() => {\n if (!refetchOnWindowFocus) return\n\n const handleFocus = (): void => {\n if (enabled && isStale) {\n executeFetch()\n }\n }\n\n window.addEventListener('focus', handleFocus)\n return () => window.removeEventListener('focus', handleFocus)\n }, [enabled, isStale, executeFetch, refetchOnWindowFocus])\n\n // Subscribe to cache invalidations\n useEffect(() => {\n const unsubscribe = queryCache.subscribe(stringifiedQueryKey, () => {\n // When query is invalidated, refetch\n executeFetch()\n })\n\n return unsubscribe\n }, [stringifiedQueryKey, executeFetch])\n\n // Memoize refetch function\n const refetch = useCallback(async () => {\n await executeFetch()\n }, [executeFetch])\n\n return {\n ...state,\n refetch,\n }\n}\n","'use client'\n\nimport { useClient } from './provider'\nimport { useQuery, QueryState } from './query'\nimport { FeatureValue, FeatureContext } from '@supashiphq/javascript-sdk'\nimport { FeatureKey, TypedFeatures, Features } from './types'\n\n// Custom return types for hooks with generics\nexport interface UseFeatureResult<T extends FeatureValue>\n extends Omit<QueryState<T | null>, 'data'> {\n feature: T | null\n}\n\nexport interface UseFeaturesResult<T extends Record<string, FeatureValue>>\n extends Omit<QueryState<T>, 'data'> {\n features: T\n}\n\nconst STALE_TIME = 5 * 60 * 1000 // 5 minutes\nconst CACHE_TIME = 10 * 60 * 1000 // 10 minutes\n\n/**\n * Returns the state of a given feature for the current context.\n *\n * @example\n * ```ts\n * function MyComponent() {\n * const { feature, isLoading } = useFeature('my-feature')\n * if (isLoading) return <div>Loading...</div>\n * return <div>Feature value: {String(feature)}</div>\n * }\n * ```\n *\n * @remarks\n * For type-safe feature flags, augment the Features interface:\n * ```ts\n * declare module '@supashiphq/react-sdk' {\n * interface Features {\n * 'my-feature': { value: 'variant-a' | 'variant-b' }\n * 'dark-mode': { value: boolean }\n * }\n * }\n *\n * // Now get full type safety:\n * const { feature } = useFeature('my-feature')\n * // feature is typed as 'variant-a' | 'variant-b'\n * ```\n */\nexport function useFeature<TKey extends FeatureKey>(\n key: TKey,\n options?: { context?: FeatureContext; shouldFetch?: boolean }\n): TypedFeatures[TKey] {\n const client = useClient()\n const { context, shouldFetch = true } = options ?? {}\n\n // Get the fallback value from feature definitions\n const fallbackValue = client.getFeatureFallback(key as string)\n\n const result = useQuery(\n ['feature', key, context],\n async (): Promise<FeatureValue> => {\n return await client.getFeature(key as string, { context })\n },\n {\n enabled: shouldFetch,\n staleTime: STALE_TIME,\n cacheTime: CACHE_TIME,\n refetchOnWindowFocus: false, // Feature flags shouldn't refetch on focus\n }\n )\n\n const { data, ...rest } = result\n return {\n ...rest,\n feature: data ?? fallbackValue,\n } as TypedFeatures[TKey]\n}\n\n/**\n * Extract feature value type from Features interface\n */\ntype ExtractFeatureValue<T> = T extends { value: infer V } ? V : FeatureValue\n\n/**\n * Returns the state of multiple features for the current context.\n *\n * @example\n * ```ts\n * function MyComponent() {\n * const { features, isLoading } = useFeatures(['feature-a', 'feature-b'])\n * if (isLoading) return <div>Loading...</div>\n * return <div>Feature A: {String(features['feature-a'])}</div>\n * }\n * ```\n */\nexport function useFeatures<TKeys extends readonly FeatureKey[]>(\n featureNames: TKeys,\n options?: {\n context?: FeatureContext\n shouldFetch?: boolean\n }\n): UseFeaturesResult<{\n [K in TKeys[number]]: K extends keyof Features\n ? ExtractFeatureValue<Features[K]>\n : FeatureValue | null\n}> {\n const client = useClient()\n const { context, shouldFetch = true } = options ?? {}\n\n type ResultType = {\n [K in TKeys[number]]: K extends keyof Features\n ? ExtractFeatureValue<Features[K]>\n : FeatureValue | null\n }\n\n const result = useQuery(\n ['features', featureNames.join(','), context],\n async (): Promise<ResultType> => {\n const features = await client.getFeatures([...featureNames] as string[], { context })\n return features as ResultType\n },\n {\n enabled: shouldFetch,\n staleTime: STALE_TIME,\n cacheTime: CACHE_TIME,\n refetchOnWindowFocus: false, // Feature flags shouldn't refetch on focus\n }\n )\n\n const { data, ...rest } = result\n return {\n ...rest,\n features: data ?? ({} as ResultType),\n }\n}\n","export function hasValue(value: unknown): boolean {\n return value !== undefined && value !== null\n}\n","'use client'\n\nimport React, { ReactNode } from 'react'\nimport { useFeature } from './hooks'\nimport { FeatureKey, FeatureContext } from './types'\nimport { hasValue } from './utils'\n\nexport interface SupaFeatureProps {\n /**\n * The feature flag key to evaluate\n */\n feature: FeatureKey\n\n /**\n * Context for feature evaluation\n */\n context?: FeatureContext\n\n /**\n * Whether to fetch the feature (default: true)\n */\n shouldFetch?: boolean\n\n /**\n * Variations object mapping feature values/keys to JSX elements\n */\n variations: Record<string, ReactNode>\n\n /**\n * Key in variations object to use for loading state\n */\n loading?: string\n}\n\n/**\n * SupaFeature component that conditionally renders variations based on feature flag values.\n * Uses the default value defined in the client configuration.\n *\n * @example\n * ```tsx\n * <SupaFeature\n * feature=\"new-header\"\n * variations={{\n * \"true\": <NewHeader />,\n * \"false\": <OldHeader />,\n * loading: <HeaderSkeleton />\n * }}\n * />\n * ```\n */\nexport function SupaFeature({\n feature,\n context,\n shouldFetch = true,\n variations,\n loading,\n}: SupaFeatureProps): React.JSX.Element | null {\n const { feature: featureValue, isLoading } = useFeature(feature, {\n context,\n shouldFetch,\n })\n\n // Show loading state if provided and currently loading\n if (isLoading && loading && variations[loading]) {\n return <>{variations[loading]}</>\n }\n\n // Don't render anything if still loading and no loader provided\n if (isLoading) {\n return null\n }\n\n // Don't render anything if no feature value (client config should provide defaults)\n if (!hasValue(featureValue)) {\n return null\n }\n\n // Convert feature value to string for object key lookup\n const valueKey = String(featureValue)\n\n // Find matching variation by exact key match\n if (variations[valueKey]) {\n return <>{variations[valueKey]}</>\n }\n\n // Don't render anything if no variation matches\n return null\n}\n"],"mappings":"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","names":["import_react","queryClient","client","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/provider.tsx","../src/query.ts","../src/hooks.ts","../src/utils.ts","../src/components.tsx"],"sourcesContent":["'use client'\n\nexport * from './types'\nexport * from './hooks'\nexport * from './provider'\nexport * from './query'\nexport * from './components'\n","'use client'\n\nimport React, {\n createContext,\n useContext,\n ReactNode,\n useMemo,\n useCallback,\n useRef,\n useEffect,\n} from 'react'\nimport {\n SupaClient,\n SupaClientConfig as SupaProviderConfig,\n FeatureContext,\n FeatureValue,\n Features,\n SupaToolbarOverrideChange,\n SupaToolbarPluginConfig,\n} from '@supashiphq/javascript-sdk'\nimport { useQueryClient } from './query'\n\ninterface SupaContextValue<TFeatures extends Features<Record<string, FeatureValue>>> {\n client: SupaClient<TFeatures>\n updateContext: (context: FeatureContext, mergeWithExisting?: boolean) => void\n getContext: () => FeatureContext | undefined\n}\n\nconst SupaContext = createContext<SupaContextValue<any> | null>(null)\n\ninterface SupaProviderProps<TFeatures extends Features<Record<string, FeatureValue>>> {\n config: Omit<SupaProviderConfig, 'plugins' | 'toolbar'> & { features: TFeatures }\n toolbar?: SupaToolbarPluginConfig | boolean\n children: ReactNode\n}\n\n// Default toolbar config (stable reference)\nconst DEFAULT_TOOLBAR_CONFIG: SupaToolbarPluginConfig = { enabled: 'auto' }\n\nexport function SupaProvider<TFeatures extends Features<Record<string, FeatureValue>>>({\n config,\n toolbar,\n children,\n}: SupaProviderProps<TFeatures>): React.JSX.Element {\n const queryClient = useQueryClient()\n\n // Use default if toolbar not provided\n const effectiveToolbar = toolbar ?? DEFAULT_TOOLBAR_CONFIG\n\n // Stable callback for toolbar override changes\n const handleOverrideChange = useCallback(\n (\n featureOverride: SupaToolbarOverrideChange,\n allOverrides: Record<string, FeatureValue>\n ): void => {\n // Call user's onOverrideChange if provided\n if (typeof effectiveToolbar === 'object' && effectiveToolbar.onOverrideChange) {\n effectiveToolbar.onOverrideChange(featureOverride, allOverrides)\n }\n\n // Invalidate the query cache for the changed feature to trigger refetch\n if (featureOverride.feature) {\n // Invalidate single feature queries (useFeature)\n queryClient.invalidateQueriesByPrefix(['feature', featureOverride.feature])\n // Invalidate multi-feature queries (useFeatures) that include this feature\n queryClient.invalidateQueriesByPrefix(['features'])\n }\n },\n [effectiveToolbar, queryClient]\n )\n\n // Use ref to persist client across StrictMode double-renders in development\n const clientRef = useRef<SupaClient<TFeatures> | null>(null)\n\n // Initialize client only once to prevent duplicate toolbars in StrictMode\n if (!clientRef.current) {\n // Merge toolbar config with React Query cache invalidation\n const toolbarConfig =\n effectiveToolbar === false\n ? false\n : {\n ...(typeof effectiveToolbar === 'object' ? effectiveToolbar : {}),\n onOverrideChange: handleOverrideChange,\n }\n\n clientRef.current = new SupaClient<TFeatures>({\n ...config,\n toolbar: toolbarConfig,\n })\n }\n\n const client = clientRef.current\n\n // Cleanup client on unmount\n useEffect(() => {\n return () => {\n if (clientRef.current) {\n // Cleanup plugins (which includes toolbar cleanup)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const client = clientRef.current as any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (client.plugins && Array.isArray(client.plugins)) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n client.plugins.forEach((plugin: any) => {\n if (plugin.cleanup) {\n plugin.cleanup()\n }\n })\n }\n clientRef.current = null\n }\n }\n }, [])\n\n // Memoized context update function\n const updateContext = useCallback(\n (context: FeatureContext, mergeWithExisting: boolean = true) => {\n client.updateContext(context, mergeWithExisting)\n },\n [client]\n )\n\n // Memoized context getter function\n const getContext = useCallback(() => {\n return client.getContext()\n }, [client])\n\n const contextValue = useMemo(\n () => ({\n client,\n updateContext,\n getContext,\n }),\n [client, updateContext, getContext]\n )\n\n return <SupaContext.Provider value={contextValue}>{children}</SupaContext.Provider>\n}\n\nexport function useClient<\n TFeatures extends Features<Record<string, FeatureValue>>,\n>(): SupaClient<TFeatures> {\n const context = useContext(SupaContext)\n if (!context) {\n throw new Error('useClient must be used within a SupaProvider')\n }\n return context.client as SupaClient<TFeatures>\n}\n\n/**\n * Hook to update the context dynamically\n * Useful when context depends on authentication or other async operations\n */\nexport function useFeatureContext(): Omit<SupaContextValue<any>, 'client'> {\n const context = useContext(SupaContext)\n if (!context) {\n throw new Error('useFeatureContext must be used within a SupaProvider')\n }\n return {\n updateContext: context.updateContext,\n getContext: context.getContext,\n }\n}\n","'use client'\n\nimport { useCallback, useEffect, useReducer, useRef } from 'react'\n\n// Query status types\nexport type QueryStatus = 'idle' | 'loading' | 'success' | 'error'\n\n// Base query state (managed by reducer)\nexport interface BaseQueryState<TData = unknown, TError = Error> {\n status: QueryStatus\n data: TData | undefined\n error: TError | null\n isLoading: boolean\n isSuccess: boolean\n isError: boolean\n isIdle: boolean\n isFetching: boolean\n dataUpdatedAt: number\n}\n\n// Query state with refetch method (returned by useQuery)\nexport interface QueryState<TData = unknown, TError = Error> extends BaseQueryState<TData, TError> {\n refetch: () => Promise<void>\n}\n\n// Initial state factory for queries\nexport function getInitialQueryState<TData, TError>(\n initialData?: TData,\n enabled: boolean = true\n): BaseQueryState<TData, TError> {\n return {\n status: !enabled ? 'idle' : initialData !== undefined ? 'success' : 'idle',\n data: initialData,\n error: null,\n isLoading: enabled && initialData === undefined,\n isSuccess: initialData !== undefined,\n isError: false,\n isIdle: !enabled || initialData === undefined,\n isFetching: false,\n dataUpdatedAt: initialData !== undefined ? Date.now() : 0,\n }\n}\n\n// Query options\nexport interface UseQueryOptions<TData = unknown, TError = Error> {\n enabled?: boolean\n retry?: number | boolean\n retryDelay?: number\n staleTime?: number\n cacheTime?: number\n refetchOnWindowFocus?: boolean\n initialData?: TData\n onSuccess?: (data: TData) => void\n onError?: (error: TError) => void\n onSettled?: (data: TData | undefined, error: TError | null) => void\n}\n\n// Query key type\nexport type QueryKey = unknown[]\n\n// Actions for the query reducer\ntype QueryAction<TData, TError> =\n | { type: 'FETCH_START' }\n | { type: 'FETCH_SUCCESS'; payload: { data: TData } }\n | { type: 'FETCH_ERROR'; payload: { error: TError } }\n\n// Reducer for query state\nfunction queryReducer<TData, TError>(\n state: BaseQueryState<TData, TError>,\n action: QueryAction<TData, TError>\n): BaseQueryState<TData, TError> {\n switch (action.type) {\n case 'FETCH_START':\n return {\n ...state,\n status: state.data === undefined ? 'loading' : state.status,\n isLoading: state.data === undefined,\n isIdle: false,\n isFetching: true,\n }\n case 'FETCH_SUCCESS':\n return {\n ...state,\n status: 'success',\n data: action.payload.data,\n error: null,\n isLoading: false,\n isSuccess: true,\n isError: false,\n isIdle: false,\n isFetching: false,\n dataUpdatedAt: Date.now(),\n }\n case 'FETCH_ERROR':\n return {\n ...state,\n status: 'error',\n error: action.payload.error,\n isLoading: false,\n isSuccess: !!state.data,\n isError: true,\n isIdle: false,\n isFetching: false,\n }\n default:\n return state\n }\n}\n\n// Cache implementation\nclass QueryCache {\n private cache: Map<string, { data: unknown; timestamp: number }> = new Map()\n private timers: Map<string, ReturnType<typeof setTimeout>> = new Map()\n private observers: Map<string, Set<() => void>> = new Map()\n\n getQuery(queryKey: string): unknown {\n const entry = this.cache.get(queryKey)\n return entry ? entry.data : undefined\n }\n\n isStale(queryKey: string, staleTime: number): boolean {\n const entry = this.cache.get(queryKey)\n if (!entry) return true\n return Date.now() - entry.timestamp > staleTime\n }\n\n setQuery(queryKey: string, data: unknown, cacheTime: number = 5 * 60 * 1000): void {\n this.cache.set(queryKey, { data, timestamp: Date.now() })\n\n // Clear previous timer if it exists\n if (this.timers.has(queryKey)) {\n clearTimeout(this.timers.get(queryKey)!)\n }\n\n // Set new timer for cache expiration\n const timer = setTimeout(() => {\n this.cache.delete(queryKey)\n this.timers.delete(queryKey)\n }, cacheTime)\n\n this.timers.set(queryKey, timer)\n }\n\n subscribe(queryKey: string, callback: () => void): () => void {\n if (!this.observers.has(queryKey)) {\n this.observers.set(queryKey, new Set())\n }\n this.observers.get(queryKey)!.add(callback)\n\n // Return unsubscribe function\n return () => {\n const callbacks = this.observers.get(queryKey)\n if (callbacks) {\n callbacks.delete(callback)\n if (callbacks.size === 0) {\n this.observers.delete(queryKey)\n }\n }\n }\n }\n\n private notifyObservers(queryKey: string): void {\n const callbacks = this.observers.get(queryKey)\n if (callbacks) {\n callbacks.forEach(callback => callback())\n }\n }\n\n invalidateQuery(queryKey: string): void {\n this.cache.delete(queryKey)\n if (this.timers.has(queryKey)) {\n clearTimeout(this.timers.get(queryKey)!)\n this.timers.delete(queryKey)\n }\n this.notifyObservers(queryKey)\n }\n\n invalidateQueries(queryKeyPrefix: string): void {\n for (const [key] of this.cache) {\n if (key.startsWith(queryKeyPrefix)) {\n this.invalidateQuery(key)\n }\n }\n }\n\n clear(): void {\n this.cache.clear()\n this.timers.forEach(timer => clearTimeout(timer))\n this.timers.clear()\n this.observers.clear()\n }\n}\n\n// Singleton instance of QueryCache\nexport const queryCache = new QueryCache()\n\n// Query Client class for managing queries\nexport class QueryClient {\n private cache: QueryCache\n\n constructor(cache: QueryCache = queryCache) {\n this.cache = cache\n }\n\n /**\n * Invalidates a specific query by its query key\n * This will remove it from cache and trigger refetch in all components using this query\n */\n invalidateQueries(queryKey: QueryKey): void {\n const stringifiedKey = stableStringifyQueryKey(queryKey)\n this.cache.invalidateQuery(stringifiedKey)\n }\n\n /**\n * Invalidates all queries matching a partial query key\n * For example, invalidateQueriesByPrefix(['feature']) will invalidate all feature queries\n */\n invalidateQueriesByPrefix(queryKeyPrefix: QueryKey): void {\n const stringifiedPrefix = stableStringifyQueryKey(queryKeyPrefix)\n this.cache.invalidateQueries(stringifiedPrefix.slice(0, -1)) // Remove closing bracket\n }\n\n /**\n * Clears all queries from the cache\n */\n clear(): void {\n this.cache.clear()\n }\n}\n\n// Singleton instance of QueryClient\nconst queryClient = new QueryClient()\n\n// Hook to access the query client\nexport function useQueryClient(): QueryClient {\n return queryClient\n}\n\n// Stable stringify for query keys\nfunction stableStringifyQueryKey(queryKey: QueryKey): string {\n return JSON.stringify(queryKey, (_, val) => (typeof val === 'function' ? val.toString() : val))\n}\n\n// The useQuery hook\nexport function useQuery<TData = unknown, TError = Error>(\n queryKey: QueryKey,\n queryFn: () => Promise<TData>,\n options: UseQueryOptions<TData, TError> = {}\n): QueryState<TData, TError> {\n const {\n enabled = true,\n retry = 3,\n retryDelay = 1000,\n staleTime = 0,\n cacheTime = 5 * 60 * 1000,\n refetchOnWindowFocus = true,\n initialData,\n } = options\n\n const stringifiedQueryKey = stableStringifyQueryKey(queryKey)\n const queryFnRef = useRef(queryFn)\n const optionsRef = useRef(options)\n\n // Update refs when dependencies change\n useEffect(() => {\n queryFnRef.current = queryFn\n optionsRef.current = options\n }, [queryFn, options])\n\n // Initialize state from cache or with default\n const cachedData = queryCache.getQuery(stringifiedQueryKey)\n const isStale = queryCache.isStale(stringifiedQueryKey, staleTime)\n\n // Use cached data for initial state even if stale to prevent unnecessary loading states\n const initialStateData = cachedData !== undefined ? (cachedData as TData) : initialData\n const initialState = getInitialQueryState<TData, TError>(initialStateData, enabled)\n const [state, dispatch] = useReducer(queryReducer<TData, TError>, initialState)\n\n const executeFetch = useCallback(async (): Promise<void> => {\n if (!enabled) return\n\n dispatch({ type: 'FETCH_START' })\n\n let retryCount = 0\n const maxRetries = typeof retry === 'boolean' ? (retry ? 3 : 0) : retry\n\n const runQuery = async (): Promise<void> => {\n try {\n const data = await queryFnRef.current()\n\n // Update cache\n queryCache.setQuery(stringifiedQueryKey, data, cacheTime)\n\n dispatch({ type: 'FETCH_SUCCESS', payload: { data } })\n\n if (optionsRef.current.onSuccess) {\n optionsRef.current.onSuccess(data)\n }\n\n if (optionsRef.current.onSettled) {\n optionsRef.current.onSettled(data, null)\n }\n } catch (err) {\n const error = err as TError\n\n if (retryCount < maxRetries) {\n retryCount++\n await new Promise(resolve => setTimeout(resolve, retryDelay))\n return runQuery()\n }\n\n dispatch({ type: 'FETCH_ERROR', payload: { error } })\n\n if (optionsRef.current.onError) {\n optionsRef.current.onError(error)\n }\n\n if (optionsRef.current.onSettled) {\n optionsRef.current.onSettled(undefined, error)\n }\n }\n }\n\n await runQuery()\n }, [enabled, stringifiedQueryKey, retry, retryDelay, cacheTime])\n\n // Execute query on mount and when dependencies change\n useEffect(() => {\n if (enabled && (isStale || state.data === undefined)) {\n executeFetch()\n }\n }, [enabled, isStale, executeFetch, state.data])\n\n // Handle window focus\n useEffect(() => {\n if (!refetchOnWindowFocus) return\n\n const handleFocus = (): void => {\n if (enabled && isStale) {\n executeFetch()\n }\n }\n\n window.addEventListener('focus', handleFocus)\n return () => window.removeEventListener('focus', handleFocus)\n }, [enabled, isStale, executeFetch, refetchOnWindowFocus])\n\n // Subscribe to cache invalidations\n useEffect(() => {\n const unsubscribe = queryCache.subscribe(stringifiedQueryKey, () => {\n // When query is invalidated, refetch\n executeFetch()\n })\n\n return unsubscribe\n }, [stringifiedQueryKey, executeFetch])\n\n // Memoize refetch function\n const refetch = useCallback(async () => {\n await executeFetch()\n }, [executeFetch])\n\n return {\n ...state,\n refetch,\n }\n}\n","'use client'\n\nimport { useClient } from './provider'\nimport { useQuery, QueryState } from './query'\nimport { FeatureValue, FeatureContext } from '@supashiphq/javascript-sdk'\nimport { FeatureKey, TypedFeatures, Features } from './types'\n\n// Custom return types for hooks with generics\nexport interface UseFeatureResult<T extends FeatureValue>\n extends Omit<QueryState<T | null>, 'data'> {\n feature: T | null\n}\n\nexport interface UseFeaturesResult<T extends Record<string, FeatureValue>>\n extends Omit<QueryState<T>, 'data'> {\n features: T\n}\n\nconst STALE_TIME = 5 * 60 * 1000 // 5 minutes\nconst CACHE_TIME = 10 * 60 * 1000 // 10 minutes\n\n/**\n * Returns the state of a given feature for the current context.\n *\n * @example\n * ```ts\n * function MyComponent() {\n * const { feature, isLoading } = useFeature('my-feature')\n * if (isLoading) return <div>Loading...</div>\n * return <div>Feature value: {String(feature)}</div>\n * }\n * ```\n *\n * @remarks\n * For type-safe feature flags, augment the Features interface:\n * ```ts\n * declare module '@supashiphq/react-sdk' {\n * interface Features {\n * 'my-feature': { value: 'variant-a' | 'variant-b' }\n * 'dark-mode': { value: boolean }\n * }\n * }\n *\n * // Now get full type safety:\n * const { feature } = useFeature('my-feature')\n * // feature is typed as 'variant-a' | 'variant-b'\n * ```\n */\nexport function useFeature<TKey extends FeatureKey>(\n key: TKey,\n options?: { context?: FeatureContext; shouldFetch?: boolean }\n): TypedFeatures[TKey] {\n const client = useClient()\n const { context, shouldFetch = true } = options ?? {}\n\n // Get the fallback value from feature definitions\n const fallbackValue = client.getFeatureFallback(key as string)\n\n const result = useQuery(\n ['feature', key, context],\n async (): Promise<FeatureValue> => {\n return await client.getFeature(key as string, { context })\n },\n {\n enabled: shouldFetch,\n staleTime: STALE_TIME,\n cacheTime: CACHE_TIME,\n refetchOnWindowFocus: false, // Feature flags shouldn't refetch on focus\n }\n )\n\n const { data, ...rest } = result\n return {\n ...rest,\n feature: data ?? fallbackValue,\n } as TypedFeatures[TKey]\n}\n\n/**\n * Extract feature value type from Features interface\n */\ntype ExtractFeatureValue<T> = T extends { value: infer V } ? V : FeatureValue\n\n/**\n * Returns the state of multiple features for the current context.\n *\n * @example\n * ```ts\n * function MyComponent() {\n * const { features, isLoading } = useFeatures(['feature-a', 'feature-b'])\n * if (isLoading) return <div>Loading...</div>\n * return <div>Feature A: {String(features['feature-a'])}</div>\n * }\n * ```\n */\nexport function useFeatures<TKeys extends readonly FeatureKey[]>(\n featureNames: TKeys,\n options?: {\n context?: FeatureContext\n shouldFetch?: boolean\n }\n): UseFeaturesResult<{\n [K in TKeys[number]]: K extends keyof Features\n ? ExtractFeatureValue<Features[K]>\n : FeatureValue | null\n}> {\n const client = useClient()\n const { context, shouldFetch = true } = options ?? {}\n\n type ResultType = {\n [K in TKeys[number]]: K extends keyof Features\n ? ExtractFeatureValue<Features[K]>\n : FeatureValue | null\n }\n\n const result = useQuery(\n ['features', featureNames.join(','), context],\n async (): Promise<ResultType> => {\n const features = await client.getFeatures([...featureNames] as string[], { context })\n return features as ResultType\n },\n {\n enabled: shouldFetch,\n staleTime: STALE_TIME,\n cacheTime: CACHE_TIME,\n refetchOnWindowFocus: false, // Feature flags shouldn't refetch on focus\n }\n )\n\n const { data, ...rest } = result\n return {\n ...rest,\n features: data ?? ({} as ResultType),\n }\n}\n","export function hasValue(value: unknown): boolean {\n return value !== undefined && value !== null\n}\n","'use client'\n\nimport React, { ReactNode } from 'react'\nimport { useFeature } from './hooks'\nimport { FeatureKey, FeatureContext } from './types'\nimport { hasValue } from './utils'\n\nexport interface SupaFeatureProps {\n /**\n * The feature flag key to evaluate\n */\n feature: FeatureKey\n\n /**\n * Context for feature evaluation\n */\n context?: FeatureContext\n\n /**\n * Whether to fetch the feature (default: true)\n */\n shouldFetch?: boolean\n\n /**\n * Variations object mapping \"true\" or \"false\" to JSX elements\n */\n variations: {\n true: ReactNode\n false: ReactNode\n }\n\n /**\n * Component to render during loading state\n */\n loading?: ReactNode\n}\n\n/**\n * SupaFeature component that conditionally renders variations based on feature flag values.\n * Uses the default value defined in the client configuration.\n *\n * @example\n * ```tsx\n * <SupaFeature\n * feature=\"new-header\"\n * loading={<HeaderSkeleton />}\n * variations={{\n * true: <NewHeader />,\n * false: <OldHeader />\n * }}\n * />\n * ```\n */\nexport function SupaFeature({\n feature,\n context,\n shouldFetch = true,\n variations,\n loading,\n}: SupaFeatureProps): React.JSX.Element | null {\n const { feature: featureValue, isLoading } = useFeature(feature, {\n context,\n shouldFetch,\n })\n\n // Show loading state if provided and currently loading\n if (isLoading) {\n return loading ? <>{loading}</> : null\n }\n\n // Convert feature value to boolean string for lookup\n const valueKey = String(featureValue)\n\n // Match \"true\" or \"false\" variations\n if (variations.true && valueKey === 'true') {\n return <>{variations.true}</>\n }\n\n if (variations.false && (valueKey === 'false' || !hasValue(featureValue))) {\n return <>{variations.false}</>\n }\n\n // Don't render anything if no variation matches\n return null\n}\n"],"mappings":"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","names":["import_react","queryClient","client","import_jsx_runtime"]}
package/dist/index.mjs CHANGED
@@ -411,18 +411,15 @@ function SupaFeature({
411
411
  context,
412
412
  shouldFetch
413
413
  });
414
- if (isLoading && loading && variations[loading]) {
415
- return /* @__PURE__ */ jsx2(Fragment, { children: variations[loading] });
416
- }
417
414
  if (isLoading) {
418
- return null;
419
- }
420
- if (!hasValue(featureValue)) {
421
- return null;
415
+ return loading ? /* @__PURE__ */ jsx2(Fragment, { children: loading }) : null;
422
416
  }
423
417
  const valueKey = String(featureValue);
424
- if (variations[valueKey]) {
425
- return /* @__PURE__ */ jsx2(Fragment, { children: variations[valueKey] });
418
+ if (variations.true && valueKey === "true") {
419
+ return /* @__PURE__ */ jsx2(Fragment, { children: variations.true });
420
+ }
421
+ if (variations.false && (valueKey === "false" || !hasValue(featureValue))) {
422
+ return /* @__PURE__ */ jsx2(Fragment, { children: variations.false });
426
423
  }
427
424
  return null;
428
425
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/provider.tsx","../src/query.ts","../src/hooks.ts","../src/utils.ts","../src/components.tsx"],"sourcesContent":["'use client'\n\nimport React, {\n createContext,\n useContext,\n ReactNode,\n useMemo,\n useCallback,\n useRef,\n useEffect,\n} from 'react'\nimport {\n SupaClient,\n SupaClientConfig as SupaProviderConfig,\n FeatureContext,\n FeatureValue,\n Features,\n SupaToolbarOverrideChange,\n SupaToolbarPluginConfig,\n} from '@supashiphq/javascript-sdk'\nimport { useQueryClient } from './query'\n\ninterface SupaContextValue<TFeatures extends Features<Record<string, FeatureValue>>> {\n client: SupaClient<TFeatures>\n updateContext: (context: FeatureContext, mergeWithExisting?: boolean) => void\n getContext: () => FeatureContext | undefined\n}\n\nconst SupaContext = createContext<SupaContextValue<any> | null>(null)\n\ninterface SupaProviderProps<TFeatures extends Features<Record<string, FeatureValue>>> {\n config: Omit<SupaProviderConfig, 'plugins' | 'toolbar'> & { features: TFeatures }\n toolbar?: SupaToolbarPluginConfig | boolean\n children: ReactNode\n}\n\n// Default toolbar config (stable reference)\nconst DEFAULT_TOOLBAR_CONFIG: SupaToolbarPluginConfig = { enabled: 'auto' }\n\nexport function SupaProvider<TFeatures extends Features<Record<string, FeatureValue>>>({\n config,\n toolbar,\n children,\n}: SupaProviderProps<TFeatures>): React.JSX.Element {\n const queryClient = useQueryClient()\n\n // Use default if toolbar not provided\n const effectiveToolbar = toolbar ?? DEFAULT_TOOLBAR_CONFIG\n\n // Stable callback for toolbar override changes\n const handleOverrideChange = useCallback(\n (\n featureOverride: SupaToolbarOverrideChange,\n allOverrides: Record<string, FeatureValue>\n ): void => {\n // Call user's onOverrideChange if provided\n if (typeof effectiveToolbar === 'object' && effectiveToolbar.onOverrideChange) {\n effectiveToolbar.onOverrideChange(featureOverride, allOverrides)\n }\n\n // Invalidate the query cache for the changed feature to trigger refetch\n if (featureOverride.feature) {\n // Invalidate single feature queries (useFeature)\n queryClient.invalidateQueriesByPrefix(['feature', featureOverride.feature])\n // Invalidate multi-feature queries (useFeatures) that include this feature\n queryClient.invalidateQueriesByPrefix(['features'])\n }\n },\n [effectiveToolbar, queryClient]\n )\n\n // Use ref to persist client across StrictMode double-renders in development\n const clientRef = useRef<SupaClient<TFeatures> | null>(null)\n\n // Initialize client only once to prevent duplicate toolbars in StrictMode\n if (!clientRef.current) {\n // Merge toolbar config with React Query cache invalidation\n const toolbarConfig =\n effectiveToolbar === false\n ? false\n : {\n ...(typeof effectiveToolbar === 'object' ? effectiveToolbar : {}),\n onOverrideChange: handleOverrideChange,\n }\n\n clientRef.current = new SupaClient<TFeatures>({\n ...config,\n toolbar: toolbarConfig,\n })\n }\n\n const client = clientRef.current\n\n // Cleanup client on unmount\n useEffect(() => {\n return () => {\n if (clientRef.current) {\n // Cleanup plugins (which includes toolbar cleanup)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const client = clientRef.current as any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (client.plugins && Array.isArray(client.plugins)) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n client.plugins.forEach((plugin: any) => {\n if (plugin.cleanup) {\n plugin.cleanup()\n }\n })\n }\n clientRef.current = null\n }\n }\n }, [])\n\n // Memoized context update function\n const updateContext = useCallback(\n (context: FeatureContext, mergeWithExisting: boolean = true) => {\n client.updateContext(context, mergeWithExisting)\n },\n [client]\n )\n\n // Memoized context getter function\n const getContext = useCallback(() => {\n return client.getContext()\n }, [client])\n\n const contextValue = useMemo(\n () => ({\n client,\n updateContext,\n getContext,\n }),\n [client, updateContext, getContext]\n )\n\n return <SupaContext.Provider value={contextValue}>{children}</SupaContext.Provider>\n}\n\nexport function useClient<\n TFeatures extends Features<Record<string, FeatureValue>>,\n>(): SupaClient<TFeatures> {\n const context = useContext(SupaContext)\n if (!context) {\n throw new Error('useClient must be used within a SupaProvider')\n }\n return context.client as SupaClient<TFeatures>\n}\n\n/**\n * Hook to update the context dynamically\n * Useful when context depends on authentication or other async operations\n */\nexport function useFeatureContext(): Omit<SupaContextValue<any>, 'client'> {\n const context = useContext(SupaContext)\n if (!context) {\n throw new Error('useFeatureContext must be used within a SupaProvider')\n }\n return {\n updateContext: context.updateContext,\n getContext: context.getContext,\n }\n}\n","'use client'\n\nimport { useCallback, useEffect, useReducer, useRef } from 'react'\n\n// Query status types\nexport type QueryStatus = 'idle' | 'loading' | 'success' | 'error'\n\n// Base query state (managed by reducer)\nexport interface BaseQueryState<TData = unknown, TError = Error> {\n status: QueryStatus\n data: TData | undefined\n error: TError | null\n isLoading: boolean\n isSuccess: boolean\n isError: boolean\n isIdle: boolean\n isFetching: boolean\n dataUpdatedAt: number\n}\n\n// Query state with refetch method (returned by useQuery)\nexport interface QueryState<TData = unknown, TError = Error> extends BaseQueryState<TData, TError> {\n refetch: () => Promise<void>\n}\n\n// Initial state factory for queries\nexport function getInitialQueryState<TData, TError>(\n initialData?: TData,\n enabled: boolean = true\n): BaseQueryState<TData, TError> {\n return {\n status: !enabled ? 'idle' : initialData !== undefined ? 'success' : 'idle',\n data: initialData,\n error: null,\n isLoading: enabled && initialData === undefined,\n isSuccess: initialData !== undefined,\n isError: false,\n isIdle: !enabled || initialData === undefined,\n isFetching: false,\n dataUpdatedAt: initialData !== undefined ? Date.now() : 0,\n }\n}\n\n// Query options\nexport interface UseQueryOptions<TData = unknown, TError = Error> {\n enabled?: boolean\n retry?: number | boolean\n retryDelay?: number\n staleTime?: number\n cacheTime?: number\n refetchOnWindowFocus?: boolean\n initialData?: TData\n onSuccess?: (data: TData) => void\n onError?: (error: TError) => void\n onSettled?: (data: TData | undefined, error: TError | null) => void\n}\n\n// Query key type\nexport type QueryKey = unknown[]\n\n// Actions for the query reducer\ntype QueryAction<TData, TError> =\n | { type: 'FETCH_START' }\n | { type: 'FETCH_SUCCESS'; payload: { data: TData } }\n | { type: 'FETCH_ERROR'; payload: { error: TError } }\n\n// Reducer for query state\nfunction queryReducer<TData, TError>(\n state: BaseQueryState<TData, TError>,\n action: QueryAction<TData, TError>\n): BaseQueryState<TData, TError> {\n switch (action.type) {\n case 'FETCH_START':\n return {\n ...state,\n status: state.data === undefined ? 'loading' : state.status,\n isLoading: state.data === undefined,\n isIdle: false,\n isFetching: true,\n }\n case 'FETCH_SUCCESS':\n return {\n ...state,\n status: 'success',\n data: action.payload.data,\n error: null,\n isLoading: false,\n isSuccess: true,\n isError: false,\n isIdle: false,\n isFetching: false,\n dataUpdatedAt: Date.now(),\n }\n case 'FETCH_ERROR':\n return {\n ...state,\n status: 'error',\n error: action.payload.error,\n isLoading: false,\n isSuccess: !!state.data,\n isError: true,\n isIdle: false,\n isFetching: false,\n }\n default:\n return state\n }\n}\n\n// Cache implementation\nclass QueryCache {\n private cache: Map<string, { data: unknown; timestamp: number }> = new Map()\n private timers: Map<string, ReturnType<typeof setTimeout>> = new Map()\n private observers: Map<string, Set<() => void>> = new Map()\n\n getQuery(queryKey: string): unknown {\n const entry = this.cache.get(queryKey)\n return entry ? entry.data : undefined\n }\n\n isStale(queryKey: string, staleTime: number): boolean {\n const entry = this.cache.get(queryKey)\n if (!entry) return true\n return Date.now() - entry.timestamp > staleTime\n }\n\n setQuery(queryKey: string, data: unknown, cacheTime: number = 5 * 60 * 1000): void {\n this.cache.set(queryKey, { data, timestamp: Date.now() })\n\n // Clear previous timer if it exists\n if (this.timers.has(queryKey)) {\n clearTimeout(this.timers.get(queryKey)!)\n }\n\n // Set new timer for cache expiration\n const timer = setTimeout(() => {\n this.cache.delete(queryKey)\n this.timers.delete(queryKey)\n }, cacheTime)\n\n this.timers.set(queryKey, timer)\n }\n\n subscribe(queryKey: string, callback: () => void): () => void {\n if (!this.observers.has(queryKey)) {\n this.observers.set(queryKey, new Set())\n }\n this.observers.get(queryKey)!.add(callback)\n\n // Return unsubscribe function\n return () => {\n const callbacks = this.observers.get(queryKey)\n if (callbacks) {\n callbacks.delete(callback)\n if (callbacks.size === 0) {\n this.observers.delete(queryKey)\n }\n }\n }\n }\n\n private notifyObservers(queryKey: string): void {\n const callbacks = this.observers.get(queryKey)\n if (callbacks) {\n callbacks.forEach(callback => callback())\n }\n }\n\n invalidateQuery(queryKey: string): void {\n this.cache.delete(queryKey)\n if (this.timers.has(queryKey)) {\n clearTimeout(this.timers.get(queryKey)!)\n this.timers.delete(queryKey)\n }\n this.notifyObservers(queryKey)\n }\n\n invalidateQueries(queryKeyPrefix: string): void {\n for (const [key] of this.cache) {\n if (key.startsWith(queryKeyPrefix)) {\n this.invalidateQuery(key)\n }\n }\n }\n\n clear(): void {\n this.cache.clear()\n this.timers.forEach(timer => clearTimeout(timer))\n this.timers.clear()\n this.observers.clear()\n }\n}\n\n// Singleton instance of QueryCache\nexport const queryCache = new QueryCache()\n\n// Query Client class for managing queries\nexport class QueryClient {\n private cache: QueryCache\n\n constructor(cache: QueryCache = queryCache) {\n this.cache = cache\n }\n\n /**\n * Invalidates a specific query by its query key\n * This will remove it from cache and trigger refetch in all components using this query\n */\n invalidateQueries(queryKey: QueryKey): void {\n const stringifiedKey = stableStringifyQueryKey(queryKey)\n this.cache.invalidateQuery(stringifiedKey)\n }\n\n /**\n * Invalidates all queries matching a partial query key\n * For example, invalidateQueriesByPrefix(['feature']) will invalidate all feature queries\n */\n invalidateQueriesByPrefix(queryKeyPrefix: QueryKey): void {\n const stringifiedPrefix = stableStringifyQueryKey(queryKeyPrefix)\n this.cache.invalidateQueries(stringifiedPrefix.slice(0, -1)) // Remove closing bracket\n }\n\n /**\n * Clears all queries from the cache\n */\n clear(): void {\n this.cache.clear()\n }\n}\n\n// Singleton instance of QueryClient\nconst queryClient = new QueryClient()\n\n// Hook to access the query client\nexport function useQueryClient(): QueryClient {\n return queryClient\n}\n\n// Stable stringify for query keys\nfunction stableStringifyQueryKey(queryKey: QueryKey): string {\n return JSON.stringify(queryKey, (_, val) => (typeof val === 'function' ? val.toString() : val))\n}\n\n// The useQuery hook\nexport function useQuery<TData = unknown, TError = Error>(\n queryKey: QueryKey,\n queryFn: () => Promise<TData>,\n options: UseQueryOptions<TData, TError> = {}\n): QueryState<TData, TError> {\n const {\n enabled = true,\n retry = 3,\n retryDelay = 1000,\n staleTime = 0,\n cacheTime = 5 * 60 * 1000,\n refetchOnWindowFocus = true,\n initialData,\n } = options\n\n const stringifiedQueryKey = stableStringifyQueryKey(queryKey)\n const queryFnRef = useRef(queryFn)\n const optionsRef = useRef(options)\n\n // Update refs when dependencies change\n useEffect(() => {\n queryFnRef.current = queryFn\n optionsRef.current = options\n }, [queryFn, options])\n\n // Initialize state from cache or with default\n const cachedData = queryCache.getQuery(stringifiedQueryKey)\n const isStale = queryCache.isStale(stringifiedQueryKey, staleTime)\n\n // Use cached data for initial state even if stale to prevent unnecessary loading states\n const initialStateData = cachedData !== undefined ? (cachedData as TData) : initialData\n const initialState = getInitialQueryState<TData, TError>(initialStateData, enabled)\n const [state, dispatch] = useReducer(queryReducer<TData, TError>, initialState)\n\n const executeFetch = useCallback(async (): Promise<void> => {\n if (!enabled) return\n\n dispatch({ type: 'FETCH_START' })\n\n let retryCount = 0\n const maxRetries = typeof retry === 'boolean' ? (retry ? 3 : 0) : retry\n\n const runQuery = async (): Promise<void> => {\n try {\n const data = await queryFnRef.current()\n\n // Update cache\n queryCache.setQuery(stringifiedQueryKey, data, cacheTime)\n\n dispatch({ type: 'FETCH_SUCCESS', payload: { data } })\n\n if (optionsRef.current.onSuccess) {\n optionsRef.current.onSuccess(data)\n }\n\n if (optionsRef.current.onSettled) {\n optionsRef.current.onSettled(data, null)\n }\n } catch (err) {\n const error = err as TError\n\n if (retryCount < maxRetries) {\n retryCount++\n await new Promise(resolve => setTimeout(resolve, retryDelay))\n return runQuery()\n }\n\n dispatch({ type: 'FETCH_ERROR', payload: { error } })\n\n if (optionsRef.current.onError) {\n optionsRef.current.onError(error)\n }\n\n if (optionsRef.current.onSettled) {\n optionsRef.current.onSettled(undefined, error)\n }\n }\n }\n\n await runQuery()\n }, [enabled, stringifiedQueryKey, retry, retryDelay, cacheTime])\n\n // Execute query on mount and when dependencies change\n useEffect(() => {\n if (enabled && (isStale || state.data === undefined)) {\n executeFetch()\n }\n }, [enabled, isStale, executeFetch, state.data])\n\n // Handle window focus\n useEffect(() => {\n if (!refetchOnWindowFocus) return\n\n const handleFocus = (): void => {\n if (enabled && isStale) {\n executeFetch()\n }\n }\n\n window.addEventListener('focus', handleFocus)\n return () => window.removeEventListener('focus', handleFocus)\n }, [enabled, isStale, executeFetch, refetchOnWindowFocus])\n\n // Subscribe to cache invalidations\n useEffect(() => {\n const unsubscribe = queryCache.subscribe(stringifiedQueryKey, () => {\n // When query is invalidated, refetch\n executeFetch()\n })\n\n return unsubscribe\n }, [stringifiedQueryKey, executeFetch])\n\n // Memoize refetch function\n const refetch = useCallback(async () => {\n await executeFetch()\n }, [executeFetch])\n\n return {\n ...state,\n refetch,\n }\n}\n","'use client'\n\nimport { useClient } from './provider'\nimport { useQuery, QueryState } from './query'\nimport { FeatureValue, FeatureContext } from '@supashiphq/javascript-sdk'\nimport { FeatureKey, TypedFeatures, Features } from './types'\n\n// Custom return types for hooks with generics\nexport interface UseFeatureResult<T extends FeatureValue>\n extends Omit<QueryState<T | null>, 'data'> {\n feature: T | null\n}\n\nexport interface UseFeaturesResult<T extends Record<string, FeatureValue>>\n extends Omit<QueryState<T>, 'data'> {\n features: T\n}\n\nconst STALE_TIME = 5 * 60 * 1000 // 5 minutes\nconst CACHE_TIME = 10 * 60 * 1000 // 10 minutes\n\n/**\n * Returns the state of a given feature for the current context.\n *\n * @example\n * ```ts\n * function MyComponent() {\n * const { feature, isLoading } = useFeature('my-feature')\n * if (isLoading) return <div>Loading...</div>\n * return <div>Feature value: {String(feature)}</div>\n * }\n * ```\n *\n * @remarks\n * For type-safe feature flags, augment the Features interface:\n * ```ts\n * declare module '@supashiphq/react-sdk' {\n * interface Features {\n * 'my-feature': { value: 'variant-a' | 'variant-b' }\n * 'dark-mode': { value: boolean }\n * }\n * }\n *\n * // Now get full type safety:\n * const { feature } = useFeature('my-feature')\n * // feature is typed as 'variant-a' | 'variant-b'\n * ```\n */\nexport function useFeature<TKey extends FeatureKey>(\n key: TKey,\n options?: { context?: FeatureContext; shouldFetch?: boolean }\n): TypedFeatures[TKey] {\n const client = useClient()\n const { context, shouldFetch = true } = options ?? {}\n\n // Get the fallback value from feature definitions\n const fallbackValue = client.getFeatureFallback(key as string)\n\n const result = useQuery(\n ['feature', key, context],\n async (): Promise<FeatureValue> => {\n return await client.getFeature(key as string, { context })\n },\n {\n enabled: shouldFetch,\n staleTime: STALE_TIME,\n cacheTime: CACHE_TIME,\n refetchOnWindowFocus: false, // Feature flags shouldn't refetch on focus\n }\n )\n\n const { data, ...rest } = result\n return {\n ...rest,\n feature: data ?? fallbackValue,\n } as TypedFeatures[TKey]\n}\n\n/**\n * Extract feature value type from Features interface\n */\ntype ExtractFeatureValue<T> = T extends { value: infer V } ? V : FeatureValue\n\n/**\n * Returns the state of multiple features for the current context.\n *\n * @example\n * ```ts\n * function MyComponent() {\n * const { features, isLoading } = useFeatures(['feature-a', 'feature-b'])\n * if (isLoading) return <div>Loading...</div>\n * return <div>Feature A: {String(features['feature-a'])}</div>\n * }\n * ```\n */\nexport function useFeatures<TKeys extends readonly FeatureKey[]>(\n featureNames: TKeys,\n options?: {\n context?: FeatureContext\n shouldFetch?: boolean\n }\n): UseFeaturesResult<{\n [K in TKeys[number]]: K extends keyof Features\n ? ExtractFeatureValue<Features[K]>\n : FeatureValue | null\n}> {\n const client = useClient()\n const { context, shouldFetch = true } = options ?? {}\n\n type ResultType = {\n [K in TKeys[number]]: K extends keyof Features\n ? ExtractFeatureValue<Features[K]>\n : FeatureValue | null\n }\n\n const result = useQuery(\n ['features', featureNames.join(','), context],\n async (): Promise<ResultType> => {\n const features = await client.getFeatures([...featureNames] as string[], { context })\n return features as ResultType\n },\n {\n enabled: shouldFetch,\n staleTime: STALE_TIME,\n cacheTime: CACHE_TIME,\n refetchOnWindowFocus: false, // Feature flags shouldn't refetch on focus\n }\n )\n\n const { data, ...rest } = result\n return {\n ...rest,\n features: data ?? ({} as ResultType),\n }\n}\n","export function hasValue(value: unknown): boolean {\n return value !== undefined && value !== null\n}\n","'use client'\n\nimport React, { ReactNode } from 'react'\nimport { useFeature } from './hooks'\nimport { FeatureKey, FeatureContext } from './types'\nimport { hasValue } from './utils'\n\nexport interface SupaFeatureProps {\n /**\n * The feature flag key to evaluate\n */\n feature: FeatureKey\n\n /**\n * Context for feature evaluation\n */\n context?: FeatureContext\n\n /**\n * Whether to fetch the feature (default: true)\n */\n shouldFetch?: boolean\n\n /**\n * Variations object mapping feature values/keys to JSX elements\n */\n variations: Record<string, ReactNode>\n\n /**\n * Key in variations object to use for loading state\n */\n loading?: string\n}\n\n/**\n * SupaFeature component that conditionally renders variations based on feature flag values.\n * Uses the default value defined in the client configuration.\n *\n * @example\n * ```tsx\n * <SupaFeature\n * feature=\"new-header\"\n * variations={{\n * \"true\": <NewHeader />,\n * \"false\": <OldHeader />,\n * loading: <HeaderSkeleton />\n * }}\n * />\n * ```\n */\nexport function SupaFeature({\n feature,\n context,\n shouldFetch = true,\n variations,\n loading,\n}: SupaFeatureProps): React.JSX.Element | null {\n const { feature: featureValue, isLoading } = useFeature(feature, {\n context,\n shouldFetch,\n })\n\n // Show loading state if provided and currently loading\n if (isLoading && loading && variations[loading]) {\n return <>{variations[loading]}</>\n }\n\n // Don't render anything if still loading and no loader provided\n if (isLoading) {\n return null\n }\n\n // Don't render anything if no feature value (client config should provide defaults)\n if (!hasValue(featureValue)) {\n return null\n }\n\n // Convert feature value to string for object key lookup\n const valueKey = String(featureValue)\n\n // Find matching variation by exact key match\n if (variations[valueKey]) {\n return <>{variations[valueKey]}</>\n }\n\n // Don't render anything if no variation matches\n return null\n}\n"],"mappings":"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","names":["useCallback","useRef","useEffect","queryClient","useCallback","useRef","useEffect","client","jsx"]}
1
+ {"version":3,"sources":["../src/provider.tsx","../src/query.ts","../src/hooks.ts","../src/utils.ts","../src/components.tsx"],"sourcesContent":["'use client'\n\nimport React, {\n createContext,\n useContext,\n ReactNode,\n useMemo,\n useCallback,\n useRef,\n useEffect,\n} from 'react'\nimport {\n SupaClient,\n SupaClientConfig as SupaProviderConfig,\n FeatureContext,\n FeatureValue,\n Features,\n SupaToolbarOverrideChange,\n SupaToolbarPluginConfig,\n} from '@supashiphq/javascript-sdk'\nimport { useQueryClient } from './query'\n\ninterface SupaContextValue<TFeatures extends Features<Record<string, FeatureValue>>> {\n client: SupaClient<TFeatures>\n updateContext: (context: FeatureContext, mergeWithExisting?: boolean) => void\n getContext: () => FeatureContext | undefined\n}\n\nconst SupaContext = createContext<SupaContextValue<any> | null>(null)\n\ninterface SupaProviderProps<TFeatures extends Features<Record<string, FeatureValue>>> {\n config: Omit<SupaProviderConfig, 'plugins' | 'toolbar'> & { features: TFeatures }\n toolbar?: SupaToolbarPluginConfig | boolean\n children: ReactNode\n}\n\n// Default toolbar config (stable reference)\nconst DEFAULT_TOOLBAR_CONFIG: SupaToolbarPluginConfig = { enabled: 'auto' }\n\nexport function SupaProvider<TFeatures extends Features<Record<string, FeatureValue>>>({\n config,\n toolbar,\n children,\n}: SupaProviderProps<TFeatures>): React.JSX.Element {\n const queryClient = useQueryClient()\n\n // Use default if toolbar not provided\n const effectiveToolbar = toolbar ?? DEFAULT_TOOLBAR_CONFIG\n\n // Stable callback for toolbar override changes\n const handleOverrideChange = useCallback(\n (\n featureOverride: SupaToolbarOverrideChange,\n allOverrides: Record<string, FeatureValue>\n ): void => {\n // Call user's onOverrideChange if provided\n if (typeof effectiveToolbar === 'object' && effectiveToolbar.onOverrideChange) {\n effectiveToolbar.onOverrideChange(featureOverride, allOverrides)\n }\n\n // Invalidate the query cache for the changed feature to trigger refetch\n if (featureOverride.feature) {\n // Invalidate single feature queries (useFeature)\n queryClient.invalidateQueriesByPrefix(['feature', featureOverride.feature])\n // Invalidate multi-feature queries (useFeatures) that include this feature\n queryClient.invalidateQueriesByPrefix(['features'])\n }\n },\n [effectiveToolbar, queryClient]\n )\n\n // Use ref to persist client across StrictMode double-renders in development\n const clientRef = useRef<SupaClient<TFeatures> | null>(null)\n\n // Initialize client only once to prevent duplicate toolbars in StrictMode\n if (!clientRef.current) {\n // Merge toolbar config with React Query cache invalidation\n const toolbarConfig =\n effectiveToolbar === false\n ? false\n : {\n ...(typeof effectiveToolbar === 'object' ? effectiveToolbar : {}),\n onOverrideChange: handleOverrideChange,\n }\n\n clientRef.current = new SupaClient<TFeatures>({\n ...config,\n toolbar: toolbarConfig,\n })\n }\n\n const client = clientRef.current\n\n // Cleanup client on unmount\n useEffect(() => {\n return () => {\n if (clientRef.current) {\n // Cleanup plugins (which includes toolbar cleanup)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const client = clientRef.current as any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (client.plugins && Array.isArray(client.plugins)) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n client.plugins.forEach((plugin: any) => {\n if (plugin.cleanup) {\n plugin.cleanup()\n }\n })\n }\n clientRef.current = null\n }\n }\n }, [])\n\n // Memoized context update function\n const updateContext = useCallback(\n (context: FeatureContext, mergeWithExisting: boolean = true) => {\n client.updateContext(context, mergeWithExisting)\n },\n [client]\n )\n\n // Memoized context getter function\n const getContext = useCallback(() => {\n return client.getContext()\n }, [client])\n\n const contextValue = useMemo(\n () => ({\n client,\n updateContext,\n getContext,\n }),\n [client, updateContext, getContext]\n )\n\n return <SupaContext.Provider value={contextValue}>{children}</SupaContext.Provider>\n}\n\nexport function useClient<\n TFeatures extends Features<Record<string, FeatureValue>>,\n>(): SupaClient<TFeatures> {\n const context = useContext(SupaContext)\n if (!context) {\n throw new Error('useClient must be used within a SupaProvider')\n }\n return context.client as SupaClient<TFeatures>\n}\n\n/**\n * Hook to update the context dynamically\n * Useful when context depends on authentication or other async operations\n */\nexport function useFeatureContext(): Omit<SupaContextValue<any>, 'client'> {\n const context = useContext(SupaContext)\n if (!context) {\n throw new Error('useFeatureContext must be used within a SupaProvider')\n }\n return {\n updateContext: context.updateContext,\n getContext: context.getContext,\n }\n}\n","'use client'\n\nimport { useCallback, useEffect, useReducer, useRef } from 'react'\n\n// Query status types\nexport type QueryStatus = 'idle' | 'loading' | 'success' | 'error'\n\n// Base query state (managed by reducer)\nexport interface BaseQueryState<TData = unknown, TError = Error> {\n status: QueryStatus\n data: TData | undefined\n error: TError | null\n isLoading: boolean\n isSuccess: boolean\n isError: boolean\n isIdle: boolean\n isFetching: boolean\n dataUpdatedAt: number\n}\n\n// Query state with refetch method (returned by useQuery)\nexport interface QueryState<TData = unknown, TError = Error> extends BaseQueryState<TData, TError> {\n refetch: () => Promise<void>\n}\n\n// Initial state factory for queries\nexport function getInitialQueryState<TData, TError>(\n initialData?: TData,\n enabled: boolean = true\n): BaseQueryState<TData, TError> {\n return {\n status: !enabled ? 'idle' : initialData !== undefined ? 'success' : 'idle',\n data: initialData,\n error: null,\n isLoading: enabled && initialData === undefined,\n isSuccess: initialData !== undefined,\n isError: false,\n isIdle: !enabled || initialData === undefined,\n isFetching: false,\n dataUpdatedAt: initialData !== undefined ? Date.now() : 0,\n }\n}\n\n// Query options\nexport interface UseQueryOptions<TData = unknown, TError = Error> {\n enabled?: boolean\n retry?: number | boolean\n retryDelay?: number\n staleTime?: number\n cacheTime?: number\n refetchOnWindowFocus?: boolean\n initialData?: TData\n onSuccess?: (data: TData) => void\n onError?: (error: TError) => void\n onSettled?: (data: TData | undefined, error: TError | null) => void\n}\n\n// Query key type\nexport type QueryKey = unknown[]\n\n// Actions for the query reducer\ntype QueryAction<TData, TError> =\n | { type: 'FETCH_START' }\n | { type: 'FETCH_SUCCESS'; payload: { data: TData } }\n | { type: 'FETCH_ERROR'; payload: { error: TError } }\n\n// Reducer for query state\nfunction queryReducer<TData, TError>(\n state: BaseQueryState<TData, TError>,\n action: QueryAction<TData, TError>\n): BaseQueryState<TData, TError> {\n switch (action.type) {\n case 'FETCH_START':\n return {\n ...state,\n status: state.data === undefined ? 'loading' : state.status,\n isLoading: state.data === undefined,\n isIdle: false,\n isFetching: true,\n }\n case 'FETCH_SUCCESS':\n return {\n ...state,\n status: 'success',\n data: action.payload.data,\n error: null,\n isLoading: false,\n isSuccess: true,\n isError: false,\n isIdle: false,\n isFetching: false,\n dataUpdatedAt: Date.now(),\n }\n case 'FETCH_ERROR':\n return {\n ...state,\n status: 'error',\n error: action.payload.error,\n isLoading: false,\n isSuccess: !!state.data,\n isError: true,\n isIdle: false,\n isFetching: false,\n }\n default:\n return state\n }\n}\n\n// Cache implementation\nclass QueryCache {\n private cache: Map<string, { data: unknown; timestamp: number }> = new Map()\n private timers: Map<string, ReturnType<typeof setTimeout>> = new Map()\n private observers: Map<string, Set<() => void>> = new Map()\n\n getQuery(queryKey: string): unknown {\n const entry = this.cache.get(queryKey)\n return entry ? entry.data : undefined\n }\n\n isStale(queryKey: string, staleTime: number): boolean {\n const entry = this.cache.get(queryKey)\n if (!entry) return true\n return Date.now() - entry.timestamp > staleTime\n }\n\n setQuery(queryKey: string, data: unknown, cacheTime: number = 5 * 60 * 1000): void {\n this.cache.set(queryKey, { data, timestamp: Date.now() })\n\n // Clear previous timer if it exists\n if (this.timers.has(queryKey)) {\n clearTimeout(this.timers.get(queryKey)!)\n }\n\n // Set new timer for cache expiration\n const timer = setTimeout(() => {\n this.cache.delete(queryKey)\n this.timers.delete(queryKey)\n }, cacheTime)\n\n this.timers.set(queryKey, timer)\n }\n\n subscribe(queryKey: string, callback: () => void): () => void {\n if (!this.observers.has(queryKey)) {\n this.observers.set(queryKey, new Set())\n }\n this.observers.get(queryKey)!.add(callback)\n\n // Return unsubscribe function\n return () => {\n const callbacks = this.observers.get(queryKey)\n if (callbacks) {\n callbacks.delete(callback)\n if (callbacks.size === 0) {\n this.observers.delete(queryKey)\n }\n }\n }\n }\n\n private notifyObservers(queryKey: string): void {\n const callbacks = this.observers.get(queryKey)\n if (callbacks) {\n callbacks.forEach(callback => callback())\n }\n }\n\n invalidateQuery(queryKey: string): void {\n this.cache.delete(queryKey)\n if (this.timers.has(queryKey)) {\n clearTimeout(this.timers.get(queryKey)!)\n this.timers.delete(queryKey)\n }\n this.notifyObservers(queryKey)\n }\n\n invalidateQueries(queryKeyPrefix: string): void {\n for (const [key] of this.cache) {\n if (key.startsWith(queryKeyPrefix)) {\n this.invalidateQuery(key)\n }\n }\n }\n\n clear(): void {\n this.cache.clear()\n this.timers.forEach(timer => clearTimeout(timer))\n this.timers.clear()\n this.observers.clear()\n }\n}\n\n// Singleton instance of QueryCache\nexport const queryCache = new QueryCache()\n\n// Query Client class for managing queries\nexport class QueryClient {\n private cache: QueryCache\n\n constructor(cache: QueryCache = queryCache) {\n this.cache = cache\n }\n\n /**\n * Invalidates a specific query by its query key\n * This will remove it from cache and trigger refetch in all components using this query\n */\n invalidateQueries(queryKey: QueryKey): void {\n const stringifiedKey = stableStringifyQueryKey(queryKey)\n this.cache.invalidateQuery(stringifiedKey)\n }\n\n /**\n * Invalidates all queries matching a partial query key\n * For example, invalidateQueriesByPrefix(['feature']) will invalidate all feature queries\n */\n invalidateQueriesByPrefix(queryKeyPrefix: QueryKey): void {\n const stringifiedPrefix = stableStringifyQueryKey(queryKeyPrefix)\n this.cache.invalidateQueries(stringifiedPrefix.slice(0, -1)) // Remove closing bracket\n }\n\n /**\n * Clears all queries from the cache\n */\n clear(): void {\n this.cache.clear()\n }\n}\n\n// Singleton instance of QueryClient\nconst queryClient = new QueryClient()\n\n// Hook to access the query client\nexport function useQueryClient(): QueryClient {\n return queryClient\n}\n\n// Stable stringify for query keys\nfunction stableStringifyQueryKey(queryKey: QueryKey): string {\n return JSON.stringify(queryKey, (_, val) => (typeof val === 'function' ? val.toString() : val))\n}\n\n// The useQuery hook\nexport function useQuery<TData = unknown, TError = Error>(\n queryKey: QueryKey,\n queryFn: () => Promise<TData>,\n options: UseQueryOptions<TData, TError> = {}\n): QueryState<TData, TError> {\n const {\n enabled = true,\n retry = 3,\n retryDelay = 1000,\n staleTime = 0,\n cacheTime = 5 * 60 * 1000,\n refetchOnWindowFocus = true,\n initialData,\n } = options\n\n const stringifiedQueryKey = stableStringifyQueryKey(queryKey)\n const queryFnRef = useRef(queryFn)\n const optionsRef = useRef(options)\n\n // Update refs when dependencies change\n useEffect(() => {\n queryFnRef.current = queryFn\n optionsRef.current = options\n }, [queryFn, options])\n\n // Initialize state from cache or with default\n const cachedData = queryCache.getQuery(stringifiedQueryKey)\n const isStale = queryCache.isStale(stringifiedQueryKey, staleTime)\n\n // Use cached data for initial state even if stale to prevent unnecessary loading states\n const initialStateData = cachedData !== undefined ? (cachedData as TData) : initialData\n const initialState = getInitialQueryState<TData, TError>(initialStateData, enabled)\n const [state, dispatch] = useReducer(queryReducer<TData, TError>, initialState)\n\n const executeFetch = useCallback(async (): Promise<void> => {\n if (!enabled) return\n\n dispatch({ type: 'FETCH_START' })\n\n let retryCount = 0\n const maxRetries = typeof retry === 'boolean' ? (retry ? 3 : 0) : retry\n\n const runQuery = async (): Promise<void> => {\n try {\n const data = await queryFnRef.current()\n\n // Update cache\n queryCache.setQuery(stringifiedQueryKey, data, cacheTime)\n\n dispatch({ type: 'FETCH_SUCCESS', payload: { data } })\n\n if (optionsRef.current.onSuccess) {\n optionsRef.current.onSuccess(data)\n }\n\n if (optionsRef.current.onSettled) {\n optionsRef.current.onSettled(data, null)\n }\n } catch (err) {\n const error = err as TError\n\n if (retryCount < maxRetries) {\n retryCount++\n await new Promise(resolve => setTimeout(resolve, retryDelay))\n return runQuery()\n }\n\n dispatch({ type: 'FETCH_ERROR', payload: { error } })\n\n if (optionsRef.current.onError) {\n optionsRef.current.onError(error)\n }\n\n if (optionsRef.current.onSettled) {\n optionsRef.current.onSettled(undefined, error)\n }\n }\n }\n\n await runQuery()\n }, [enabled, stringifiedQueryKey, retry, retryDelay, cacheTime])\n\n // Execute query on mount and when dependencies change\n useEffect(() => {\n if (enabled && (isStale || state.data === undefined)) {\n executeFetch()\n }\n }, [enabled, isStale, executeFetch, state.data])\n\n // Handle window focus\n useEffect(() => {\n if (!refetchOnWindowFocus) return\n\n const handleFocus = (): void => {\n if (enabled && isStale) {\n executeFetch()\n }\n }\n\n window.addEventListener('focus', handleFocus)\n return () => window.removeEventListener('focus', handleFocus)\n }, [enabled, isStale, executeFetch, refetchOnWindowFocus])\n\n // Subscribe to cache invalidations\n useEffect(() => {\n const unsubscribe = queryCache.subscribe(stringifiedQueryKey, () => {\n // When query is invalidated, refetch\n executeFetch()\n })\n\n return unsubscribe\n }, [stringifiedQueryKey, executeFetch])\n\n // Memoize refetch function\n const refetch = useCallback(async () => {\n await executeFetch()\n }, [executeFetch])\n\n return {\n ...state,\n refetch,\n }\n}\n","'use client'\n\nimport { useClient } from './provider'\nimport { useQuery, QueryState } from './query'\nimport { FeatureValue, FeatureContext } from '@supashiphq/javascript-sdk'\nimport { FeatureKey, TypedFeatures, Features } from './types'\n\n// Custom return types for hooks with generics\nexport interface UseFeatureResult<T extends FeatureValue>\n extends Omit<QueryState<T | null>, 'data'> {\n feature: T | null\n}\n\nexport interface UseFeaturesResult<T extends Record<string, FeatureValue>>\n extends Omit<QueryState<T>, 'data'> {\n features: T\n}\n\nconst STALE_TIME = 5 * 60 * 1000 // 5 minutes\nconst CACHE_TIME = 10 * 60 * 1000 // 10 minutes\n\n/**\n * Returns the state of a given feature for the current context.\n *\n * @example\n * ```ts\n * function MyComponent() {\n * const { feature, isLoading } = useFeature('my-feature')\n * if (isLoading) return <div>Loading...</div>\n * return <div>Feature value: {String(feature)}</div>\n * }\n * ```\n *\n * @remarks\n * For type-safe feature flags, augment the Features interface:\n * ```ts\n * declare module '@supashiphq/react-sdk' {\n * interface Features {\n * 'my-feature': { value: 'variant-a' | 'variant-b' }\n * 'dark-mode': { value: boolean }\n * }\n * }\n *\n * // Now get full type safety:\n * const { feature } = useFeature('my-feature')\n * // feature is typed as 'variant-a' | 'variant-b'\n * ```\n */\nexport function useFeature<TKey extends FeatureKey>(\n key: TKey,\n options?: { context?: FeatureContext; shouldFetch?: boolean }\n): TypedFeatures[TKey] {\n const client = useClient()\n const { context, shouldFetch = true } = options ?? {}\n\n // Get the fallback value from feature definitions\n const fallbackValue = client.getFeatureFallback(key as string)\n\n const result = useQuery(\n ['feature', key, context],\n async (): Promise<FeatureValue> => {\n return await client.getFeature(key as string, { context })\n },\n {\n enabled: shouldFetch,\n staleTime: STALE_TIME,\n cacheTime: CACHE_TIME,\n refetchOnWindowFocus: false, // Feature flags shouldn't refetch on focus\n }\n )\n\n const { data, ...rest } = result\n return {\n ...rest,\n feature: data ?? fallbackValue,\n } as TypedFeatures[TKey]\n}\n\n/**\n * Extract feature value type from Features interface\n */\ntype ExtractFeatureValue<T> = T extends { value: infer V } ? V : FeatureValue\n\n/**\n * Returns the state of multiple features for the current context.\n *\n * @example\n * ```ts\n * function MyComponent() {\n * const { features, isLoading } = useFeatures(['feature-a', 'feature-b'])\n * if (isLoading) return <div>Loading...</div>\n * return <div>Feature A: {String(features['feature-a'])}</div>\n * }\n * ```\n */\nexport function useFeatures<TKeys extends readonly FeatureKey[]>(\n featureNames: TKeys,\n options?: {\n context?: FeatureContext\n shouldFetch?: boolean\n }\n): UseFeaturesResult<{\n [K in TKeys[number]]: K extends keyof Features\n ? ExtractFeatureValue<Features[K]>\n : FeatureValue | null\n}> {\n const client = useClient()\n const { context, shouldFetch = true } = options ?? {}\n\n type ResultType = {\n [K in TKeys[number]]: K extends keyof Features\n ? ExtractFeatureValue<Features[K]>\n : FeatureValue | null\n }\n\n const result = useQuery(\n ['features', featureNames.join(','), context],\n async (): Promise<ResultType> => {\n const features = await client.getFeatures([...featureNames] as string[], { context })\n return features as ResultType\n },\n {\n enabled: shouldFetch,\n staleTime: STALE_TIME,\n cacheTime: CACHE_TIME,\n refetchOnWindowFocus: false, // Feature flags shouldn't refetch on focus\n }\n )\n\n const { data, ...rest } = result\n return {\n ...rest,\n features: data ?? ({} as ResultType),\n }\n}\n","export function hasValue(value: unknown): boolean {\n return value !== undefined && value !== null\n}\n","'use client'\n\nimport React, { ReactNode } from 'react'\nimport { useFeature } from './hooks'\nimport { FeatureKey, FeatureContext } from './types'\nimport { hasValue } from './utils'\n\nexport interface SupaFeatureProps {\n /**\n * The feature flag key to evaluate\n */\n feature: FeatureKey\n\n /**\n * Context for feature evaluation\n */\n context?: FeatureContext\n\n /**\n * Whether to fetch the feature (default: true)\n */\n shouldFetch?: boolean\n\n /**\n * Variations object mapping \"true\" or \"false\" to JSX elements\n */\n variations: {\n true: ReactNode\n false: ReactNode\n }\n\n /**\n * Component to render during loading state\n */\n loading?: ReactNode\n}\n\n/**\n * SupaFeature component that conditionally renders variations based on feature flag values.\n * Uses the default value defined in the client configuration.\n *\n * @example\n * ```tsx\n * <SupaFeature\n * feature=\"new-header\"\n * loading={<HeaderSkeleton />}\n * variations={{\n * true: <NewHeader />,\n * false: <OldHeader />\n * }}\n * />\n * ```\n */\nexport function SupaFeature({\n feature,\n context,\n shouldFetch = true,\n variations,\n loading,\n}: SupaFeatureProps): React.JSX.Element | null {\n const { feature: featureValue, isLoading } = useFeature(feature, {\n context,\n shouldFetch,\n })\n\n // Show loading state if provided and currently loading\n if (isLoading) {\n return loading ? <>{loading}</> : null\n }\n\n // Convert feature value to boolean string for lookup\n const valueKey = String(featureValue)\n\n // Match \"true\" or \"false\" variations\n if (variations.true && valueKey === 'true') {\n return <>{variations.true}</>\n }\n\n if (variations.false && (valueKey === 'false' || !hasValue(featureValue))) {\n return <>{variations.false}</>\n }\n\n // Don't render anything if no variation matches\n return null\n}\n"],"mappings":"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","names":["useCallback","useRef","useEffect","queryClient","useCallback","useRef","useEffect","client","jsx"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supashiphq/react-sdk",
3
- "version": "0.7.7",
3
+ "version": "0.7.8",
4
4
  "description": "Supaship SDK for React",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -39,7 +39,7 @@
39
39
  "react-dom": ">=16.8.0"
40
40
  },
41
41
  "dependencies": {
42
- "@supashiphq/javascript-sdk": "^0.7.7"
42
+ "@supashiphq/javascript-sdk": "^0.7.8"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@jest/globals": "^30.0.0",