@geejay/use-feature-flags 1.0.29 → 1.0.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -50,7 +50,7 @@ function useFeatureFlags(passedKey, environment = ((_a) => (_a = window == null
50
50
  const cleanupRef = react.useRef(null);
51
51
  const apiKey2 = passedKey;
52
52
  react.useEffect(() => {
53
- console.log(
53
+ process.env.DEBUG && console.log(
54
54
  "[use-feature-flags] initializing",
55
55
  `environment: ${sanitizedEnvironment}`,
56
56
  `apiKey provided: ${Boolean(apiKey2)}`
@@ -120,7 +120,7 @@ function useFeatureFlags(passedKey, environment = ((_a) => (_a = window == null
120
120
  return {
121
121
  isActive: (key) => {
122
122
  const active = state.flags.some((f) => f.key === key && f.enabled === true);
123
- console.log("[use-feature-flags] isActive", key, active);
123
+ process.env.DEBUG && console.log("[use-feature-flags] isActive", key, active);
124
124
  return active;
125
125
  },
126
126
  flags: state.flags,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/store.ts","../src/supabaseClient.ts","../src/useFeatureFlags.ts"],"names":["atom","createClient","useMemo","useAtom","useState","useRef","apiKey","useEffect"],"mappings":"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","file":"index.cjs","sourcesContent":["import { atom } from 'jotai';\n\nexport type FeatureFlag = {\n id: string;\n key: string;\n enabled: boolean;\n environment: string;\n};\n\nexport const featureFlagsAtom = atom<{ flags: FeatureFlag[]; loading: boolean }>({\n flags: [],\n loading: true,\n});\n\n// store.ts\nlet apiKey: string | null = null;\n\nexport function setApiKey(key: string) {\n apiKey = key;\n}\n\nexport function getApiKey() {\n return apiKey;\n}\n","import { createClient, type SupabaseClient } from '@supabase/supabase-js';\n\n// HMR-safe global singleton to avoid multiple GoTrueClient instances\nconst GLOBAL_KEY = '__useff_supabase_singleton__';\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __useff_supabase_singleton__: SupabaseClient | undefined;\n}\n\nexport const DEFAULT_SUPABASE_URL = 'https://khppgsehvvlukzfdqbuo.supabase.co';\n\nexport function getSupabase(): SupabaseClient {\n if (!globalThis[GLOBAL_KEY]) {\n // Prefer env vars; fallback are placeholders (replace in your app env)\n const url =\n (typeof process !== 'undefined' && process.env?.NEXT_PUBLIC_SUPABASE_URL) ||\n DEFAULT_SUPABASE_URL;\n const anon =\n (typeof process !== 'undefined' && process.env?.NEXT_PUBLIC_SUPABASE_ANON_KEY) ||\n 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImtocHBnc2VodnZsdWt6ZmRxYnVvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTI2ODU1MzQsImV4cCI6MjA2ODI2MTUzNH0.8Z4VY4HFMm95UgO21c-DnDkbLPN_0mbDBZJPExaghDk';\n\n globalThis[GLOBAL_KEY] = createClient(url, anon, {\n auth: {\n // keep this lib client stateless so it doesn't step on the host app\n persistSession: false,\n autoRefreshToken: false,\n // unique key to avoid storage collisions\n storageKey: 'use-ff-auth'\n }\n });\n }\n return globalThis[GLOBAL_KEY]!;\n}\n","\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { FeatureFlag, featureFlagsAtom } from './store';\nimport { useAtom } from 'jotai';\nimport { getSupabase, DEFAULT_SUPABASE_URL } from './supabaseClient';\n\nconst EDGE_FN_URL = `${DEFAULT_SUPABASE_URL}/functions/v1/get-feature-flags`;\n\n\n\n\ntype FlagState = {\n flags: FeatureFlag[];\n loading: boolean;\n};\n\nlet initialized = false;\n\nexport function useFeatureFlags(\n passedKey?: string,\n environment = window?.location?.hostname || 'localhost'\n) {\n const sanitizedEnvironment = useMemo(\n () => environment.replace(/:\\d+$/, ''),\n [environment]\n );\n const [state, setState] = useAtom(featureFlagsAtom);\n const [envId, setEnvId] = useState<number | null>(null);\n const debounceTimeout = useRef<NodeJS.Timeout | null>(null);\n const cleanupRef = useRef<(() => void) | null>(null);\n\n const apiKey = passedKey;\n\n useEffect(() => {\n console.log(\n '[use-feature-flags] initializing',\n `environment: ${sanitizedEnvironment}`,\n `apiKey provided: ${Boolean(apiKey)}`\n );\n }, [sanitizedEnvironment, apiKey]);\n const supabase = getSupabase();\n\n const fetchFlags = async () => {\n setState((prev) => ({ ...prev, loading: true }));\n\n console.log('[use-feature-flags] fetching flags for', sanitizedEnvironment);\n\n try {\n const res = await fetch(EDGE_FN_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(apiKey ? { 'api-key': apiKey } : {}),\n },\n body: JSON.stringify({ environment: sanitizedEnvironment }),\n });\n\n const json = await res.json();\n if (!res.ok) {\n console.warn('Edge function error:', json?.error || res.statusText);\n setState({ flags: [], loading: false });\n return;\n }\n\n const flags = json.flags || [];\n setState({ flags, loading: false });\n console.log('[use-feature-flags] fetched flags', flags);\n\n // Store environment_id from first flag (assumes all have same env)\n if (flags.length > 0 && flags[0].environment_id) {\n setEnvId(flags[0].environment_id);\n }\n } catch (err: any) {\n console.error('Error fetching flags:', err.message);\n setState({ flags: [], loading: false });\n }\n };\n\n useEffect(() => {\n if (!apiKey || initialized) return;\n initialized = true;\n\n fetchFlags();\n\n return () => {\n if (debounceTimeout.current) clearTimeout(debounceTimeout.current);\n if (cleanupRef.current) cleanupRef.current();\n };\n }, [sanitizedEnvironment, apiKey]);\n\n // Subscribe to flag changes for the correct environment\n useEffect(() => {\n if (!envId) return;\n\n const channel = supabase\n .channel(`flags-${sanitizedEnvironment}`)\n .on(\n 'postgres_changes',\n {\n event: '*',\n schema: 'public',\n table: 'feature_flags',\n filter: `environment_id=eq.${envId}`,\n },\n () => {\n if (debounceTimeout.current) clearTimeout(debounceTimeout.current);\n debounceTimeout.current = setTimeout(fetchFlags, 300);\n }\n )\n .subscribe();\n\n if (cleanupRef.current) cleanupRef.current();\n cleanupRef.current = () => supabase.removeChannel(channel);\n\n return () => {\n if (debounceTimeout.current) clearTimeout(debounceTimeout.current);\n supabase.removeChannel(channel);\n };\n }, [envId]);\n\n return {\n isActive: (key: string) => {\n const active = state.flags.some((f) => f.key === key && f.enabled === true);\n console.log('[use-feature-flags] isActive', key, active);\n return active;\n },\n flags: state.flags,\n loading: state.loading,\n };\n}\n\n\n\n\n\n"]}
1
+ {"version":3,"sources":["../src/store.ts","../src/supabaseClient.ts","../src/useFeatureFlags.ts"],"names":["atom","createClient","useMemo","useAtom","useState","useRef","apiKey","useEffect"],"mappings":"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","file":"index.cjs","sourcesContent":["import { atom } from 'jotai';\n\nexport type FeatureFlag = {\n id: string;\n key: string;\n enabled: boolean;\n environment: string;\n};\n\nexport const featureFlagsAtom = atom<{ flags: FeatureFlag[]; loading: boolean }>({\n flags: [],\n loading: true,\n});\n\n// store.ts\nlet apiKey: string | null = null;\n\nexport function setApiKey(key: string) {\n apiKey = key;\n}\n\nexport function getApiKey() {\n return apiKey;\n}\n","import { createClient, type SupabaseClient } from '@supabase/supabase-js';\n\n// HMR-safe global singleton to avoid multiple GoTrueClient instances\nconst GLOBAL_KEY = '__useff_supabase_singleton__';\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __useff_supabase_singleton__: SupabaseClient | undefined;\n}\n\nexport const DEFAULT_SUPABASE_URL = 'https://khppgsehvvlukzfdqbuo.supabase.co';\n\nexport function getSupabase(): SupabaseClient {\n if (!globalThis[GLOBAL_KEY]) {\n // Prefer env vars; fallback are placeholders (replace in your app env)\n const url =\n (typeof process !== 'undefined' && process.env?.NEXT_PUBLIC_SUPABASE_URL) ||\n DEFAULT_SUPABASE_URL;\n const anon =\n (typeof process !== 'undefined' && process.env?.NEXT_PUBLIC_SUPABASE_ANON_KEY) ||\n 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImtocHBnc2VodnZsdWt6ZmRxYnVvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTI2ODU1MzQsImV4cCI6MjA2ODI2MTUzNH0.8Z4VY4HFMm95UgO21c-DnDkbLPN_0mbDBZJPExaghDk';\n\n globalThis[GLOBAL_KEY] = createClient(url, anon, {\n auth: {\n // keep this lib client stateless so it doesn't step on the host app\n persistSession: false,\n autoRefreshToken: false,\n // unique key to avoid storage collisions\n storageKey: 'use-ff-auth'\n }\n });\n }\n return globalThis[GLOBAL_KEY]!;\n}\n","\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { FeatureFlag, featureFlagsAtom } from './store';\nimport { useAtom } from 'jotai';\nimport { getSupabase, DEFAULT_SUPABASE_URL } from './supabaseClient';\n\nconst EDGE_FN_URL = `${DEFAULT_SUPABASE_URL}/functions/v1/get-feature-flags`;\n\n\n\n\ntype FlagState = {\n flags: FeatureFlag[];\n loading: boolean;\n};\n\nlet initialized = false;\n\nexport function useFeatureFlags(\n passedKey?: string,\n environment = window?.location?.hostname || 'localhost'\n) {\n const sanitizedEnvironment = useMemo(\n () => environment.replace(/:\\d+$/, ''),\n [environment]\n );\n const [state, setState] = useAtom(featureFlagsAtom);\n const [envId, setEnvId] = useState<number | null>(null);\n const debounceTimeout = useRef<NodeJS.Timeout | null>(null);\n const cleanupRef = useRef<(() => void) | null>(null);\n\n const apiKey = passedKey;\n\n useEffect(() => {\n process.env.DEBUG && console.log(\n '[use-feature-flags] initializing',\n `environment: ${sanitizedEnvironment}`,\n `apiKey provided: ${Boolean(apiKey)}`\n );\n }, [sanitizedEnvironment, apiKey]);\n const supabase = getSupabase();\n\n const fetchFlags = async () => {\n setState((prev) => ({ ...prev, loading: true }));\n\n console.log('[use-feature-flags] fetching flags for', sanitizedEnvironment);\n\n try {\n const res = await fetch(EDGE_FN_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(apiKey ? { 'api-key': apiKey } : {}),\n },\n body: JSON.stringify({ environment: sanitizedEnvironment }),\n });\n\n const json = await res.json();\n if (!res.ok) {\n console.warn('Edge function error:', json?.error || res.statusText);\n setState({ flags: [], loading: false });\n return;\n }\n\n const flags = json.flags || [];\n setState({ flags, loading: false });\n console.log('[use-feature-flags] fetched flags', flags);\n\n // Store environment_id from first flag (assumes all have same env)\n if (flags.length > 0 && flags[0].environment_id) {\n setEnvId(flags[0].environment_id);\n }\n } catch (err: any) {\n console.error('Error fetching flags:', err.message);\n setState({ flags: [], loading: false });\n }\n };\n\n useEffect(() => {\n if (!apiKey || initialized) return;\n initialized = true;\n\n fetchFlags();\n\n return () => {\n if (debounceTimeout.current) clearTimeout(debounceTimeout.current);\n if (cleanupRef.current) cleanupRef.current();\n };\n }, [sanitizedEnvironment, apiKey]);\n\n // Subscribe to flag changes for the correct environment\n useEffect(() => {\n if (!envId) return;\n\n const channel = supabase\n .channel(`flags-${sanitizedEnvironment}`)\n .on(\n 'postgres_changes',\n {\n event: '*',\n schema: 'public',\n table: 'feature_flags',\n filter: `environment_id=eq.${envId}`,\n },\n () => {\n if (debounceTimeout.current) clearTimeout(debounceTimeout.current);\n debounceTimeout.current = setTimeout(fetchFlags, 300);\n }\n )\n .subscribe();\n\n if (cleanupRef.current) cleanupRef.current();\n cleanupRef.current = () => supabase.removeChannel(channel);\n\n return () => {\n if (debounceTimeout.current) clearTimeout(debounceTimeout.current);\n supabase.removeChannel(channel);\n };\n }, [envId]);\n\n return {\n isActive: (key: string) => {\n const active = state.flags.some((f) => f.key === key && f.enabled === true);\n process.env.DEBUG && console.log('[use-feature-flags] isActive', key, active);\n return active;\n },\n flags: state.flags,\n loading: state.loading,\n };\n}\n\n\n\n\n\n"]}
package/dist/index.js CHANGED
@@ -48,7 +48,7 @@ function useFeatureFlags(passedKey, environment = ((_a) => (_a = window == null
48
48
  const cleanupRef = useRef(null);
49
49
  const apiKey2 = passedKey;
50
50
  useEffect(() => {
51
- console.log(
51
+ process.env.DEBUG && console.log(
52
52
  "[use-feature-flags] initializing",
53
53
  `environment: ${sanitizedEnvironment}`,
54
54
  `apiKey provided: ${Boolean(apiKey2)}`
@@ -118,7 +118,7 @@ function useFeatureFlags(passedKey, environment = ((_a) => (_a = window == null
118
118
  return {
119
119
  isActive: (key) => {
120
120
  const active = state.flags.some((f) => f.key === key && f.enabled === true);
121
- console.log("[use-feature-flags] isActive", key, active);
121
+ process.env.DEBUG && console.log("[use-feature-flags] isActive", key, active);
122
122
  return active;
123
123
  },
124
124
  flags: state.flags,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/store.ts","../src/supabaseClient.ts","../src/useFeatureFlags.ts"],"names":["apiKey"],"mappings":"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","file":"index.js","sourcesContent":["import { atom } from 'jotai';\n\nexport type FeatureFlag = {\n id: string;\n key: string;\n enabled: boolean;\n environment: string;\n};\n\nexport const featureFlagsAtom = atom<{ flags: FeatureFlag[]; loading: boolean }>({\n flags: [],\n loading: true,\n});\n\n// store.ts\nlet apiKey: string | null = null;\n\nexport function setApiKey(key: string) {\n apiKey = key;\n}\n\nexport function getApiKey() {\n return apiKey;\n}\n","import { createClient, type SupabaseClient } from '@supabase/supabase-js';\n\n// HMR-safe global singleton to avoid multiple GoTrueClient instances\nconst GLOBAL_KEY = '__useff_supabase_singleton__';\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __useff_supabase_singleton__: SupabaseClient | undefined;\n}\n\nexport const DEFAULT_SUPABASE_URL = 'https://khppgsehvvlukzfdqbuo.supabase.co';\n\nexport function getSupabase(): SupabaseClient {\n if (!globalThis[GLOBAL_KEY]) {\n // Prefer env vars; fallback are placeholders (replace in your app env)\n const url =\n (typeof process !== 'undefined' && process.env?.NEXT_PUBLIC_SUPABASE_URL) ||\n DEFAULT_SUPABASE_URL;\n const anon =\n (typeof process !== 'undefined' && process.env?.NEXT_PUBLIC_SUPABASE_ANON_KEY) ||\n 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImtocHBnc2VodnZsdWt6ZmRxYnVvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTI2ODU1MzQsImV4cCI6MjA2ODI2MTUzNH0.8Z4VY4HFMm95UgO21c-DnDkbLPN_0mbDBZJPExaghDk';\n\n globalThis[GLOBAL_KEY] = createClient(url, anon, {\n auth: {\n // keep this lib client stateless so it doesn't step on the host app\n persistSession: false,\n autoRefreshToken: false,\n // unique key to avoid storage collisions\n storageKey: 'use-ff-auth'\n }\n });\n }\n return globalThis[GLOBAL_KEY]!;\n}\n","\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { FeatureFlag, featureFlagsAtom } from './store';\nimport { useAtom } from 'jotai';\nimport { getSupabase, DEFAULT_SUPABASE_URL } from './supabaseClient';\n\nconst EDGE_FN_URL = `${DEFAULT_SUPABASE_URL}/functions/v1/get-feature-flags`;\n\n\n\n\ntype FlagState = {\n flags: FeatureFlag[];\n loading: boolean;\n};\n\nlet initialized = false;\n\nexport function useFeatureFlags(\n passedKey?: string,\n environment = window?.location?.hostname || 'localhost'\n) {\n const sanitizedEnvironment = useMemo(\n () => environment.replace(/:\\d+$/, ''),\n [environment]\n );\n const [state, setState] = useAtom(featureFlagsAtom);\n const [envId, setEnvId] = useState<number | null>(null);\n const debounceTimeout = useRef<NodeJS.Timeout | null>(null);\n const cleanupRef = useRef<(() => void) | null>(null);\n\n const apiKey = passedKey;\n\n useEffect(() => {\n console.log(\n '[use-feature-flags] initializing',\n `environment: ${sanitizedEnvironment}`,\n `apiKey provided: ${Boolean(apiKey)}`\n );\n }, [sanitizedEnvironment, apiKey]);\n const supabase = getSupabase();\n\n const fetchFlags = async () => {\n setState((prev) => ({ ...prev, loading: true }));\n\n console.log('[use-feature-flags] fetching flags for', sanitizedEnvironment);\n\n try {\n const res = await fetch(EDGE_FN_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(apiKey ? { 'api-key': apiKey } : {}),\n },\n body: JSON.stringify({ environment: sanitizedEnvironment }),\n });\n\n const json = await res.json();\n if (!res.ok) {\n console.warn('Edge function error:', json?.error || res.statusText);\n setState({ flags: [], loading: false });\n return;\n }\n\n const flags = json.flags || [];\n setState({ flags, loading: false });\n console.log('[use-feature-flags] fetched flags', flags);\n\n // Store environment_id from first flag (assumes all have same env)\n if (flags.length > 0 && flags[0].environment_id) {\n setEnvId(flags[0].environment_id);\n }\n } catch (err: any) {\n console.error('Error fetching flags:', err.message);\n setState({ flags: [], loading: false });\n }\n };\n\n useEffect(() => {\n if (!apiKey || initialized) return;\n initialized = true;\n\n fetchFlags();\n\n return () => {\n if (debounceTimeout.current) clearTimeout(debounceTimeout.current);\n if (cleanupRef.current) cleanupRef.current();\n };\n }, [sanitizedEnvironment, apiKey]);\n\n // Subscribe to flag changes for the correct environment\n useEffect(() => {\n if (!envId) return;\n\n const channel = supabase\n .channel(`flags-${sanitizedEnvironment}`)\n .on(\n 'postgres_changes',\n {\n event: '*',\n schema: 'public',\n table: 'feature_flags',\n filter: `environment_id=eq.${envId}`,\n },\n () => {\n if (debounceTimeout.current) clearTimeout(debounceTimeout.current);\n debounceTimeout.current = setTimeout(fetchFlags, 300);\n }\n )\n .subscribe();\n\n if (cleanupRef.current) cleanupRef.current();\n cleanupRef.current = () => supabase.removeChannel(channel);\n\n return () => {\n if (debounceTimeout.current) clearTimeout(debounceTimeout.current);\n supabase.removeChannel(channel);\n };\n }, [envId]);\n\n return {\n isActive: (key: string) => {\n const active = state.flags.some((f) => f.key === key && f.enabled === true);\n console.log('[use-feature-flags] isActive', key, active);\n return active;\n },\n flags: state.flags,\n loading: state.loading,\n };\n}\n\n\n\n\n\n"]}
1
+ {"version":3,"sources":["../src/store.ts","../src/supabaseClient.ts","../src/useFeatureFlags.ts"],"names":["apiKey"],"mappings":"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","file":"index.js","sourcesContent":["import { atom } from 'jotai';\n\nexport type FeatureFlag = {\n id: string;\n key: string;\n enabled: boolean;\n environment: string;\n};\n\nexport const featureFlagsAtom = atom<{ flags: FeatureFlag[]; loading: boolean }>({\n flags: [],\n loading: true,\n});\n\n// store.ts\nlet apiKey: string | null = null;\n\nexport function setApiKey(key: string) {\n apiKey = key;\n}\n\nexport function getApiKey() {\n return apiKey;\n}\n","import { createClient, type SupabaseClient } from '@supabase/supabase-js';\n\n// HMR-safe global singleton to avoid multiple GoTrueClient instances\nconst GLOBAL_KEY = '__useff_supabase_singleton__';\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __useff_supabase_singleton__: SupabaseClient | undefined;\n}\n\nexport const DEFAULT_SUPABASE_URL = 'https://khppgsehvvlukzfdqbuo.supabase.co';\n\nexport function getSupabase(): SupabaseClient {\n if (!globalThis[GLOBAL_KEY]) {\n // Prefer env vars; fallback are placeholders (replace in your app env)\n const url =\n (typeof process !== 'undefined' && process.env?.NEXT_PUBLIC_SUPABASE_URL) ||\n DEFAULT_SUPABASE_URL;\n const anon =\n (typeof process !== 'undefined' && process.env?.NEXT_PUBLIC_SUPABASE_ANON_KEY) ||\n 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImtocHBnc2VodnZsdWt6ZmRxYnVvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTI2ODU1MzQsImV4cCI6MjA2ODI2MTUzNH0.8Z4VY4HFMm95UgO21c-DnDkbLPN_0mbDBZJPExaghDk';\n\n globalThis[GLOBAL_KEY] = createClient(url, anon, {\n auth: {\n // keep this lib client stateless so it doesn't step on the host app\n persistSession: false,\n autoRefreshToken: false,\n // unique key to avoid storage collisions\n storageKey: 'use-ff-auth'\n }\n });\n }\n return globalThis[GLOBAL_KEY]!;\n}\n","\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { FeatureFlag, featureFlagsAtom } from './store';\nimport { useAtom } from 'jotai';\nimport { getSupabase, DEFAULT_SUPABASE_URL } from './supabaseClient';\n\nconst EDGE_FN_URL = `${DEFAULT_SUPABASE_URL}/functions/v1/get-feature-flags`;\n\n\n\n\ntype FlagState = {\n flags: FeatureFlag[];\n loading: boolean;\n};\n\nlet initialized = false;\n\nexport function useFeatureFlags(\n passedKey?: string,\n environment = window?.location?.hostname || 'localhost'\n) {\n const sanitizedEnvironment = useMemo(\n () => environment.replace(/:\\d+$/, ''),\n [environment]\n );\n const [state, setState] = useAtom(featureFlagsAtom);\n const [envId, setEnvId] = useState<number | null>(null);\n const debounceTimeout = useRef<NodeJS.Timeout | null>(null);\n const cleanupRef = useRef<(() => void) | null>(null);\n\n const apiKey = passedKey;\n\n useEffect(() => {\n process.env.DEBUG && console.log(\n '[use-feature-flags] initializing',\n `environment: ${sanitizedEnvironment}`,\n `apiKey provided: ${Boolean(apiKey)}`\n );\n }, [sanitizedEnvironment, apiKey]);\n const supabase = getSupabase();\n\n const fetchFlags = async () => {\n setState((prev) => ({ ...prev, loading: true }));\n\n console.log('[use-feature-flags] fetching flags for', sanitizedEnvironment);\n\n try {\n const res = await fetch(EDGE_FN_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(apiKey ? { 'api-key': apiKey } : {}),\n },\n body: JSON.stringify({ environment: sanitizedEnvironment }),\n });\n\n const json = await res.json();\n if (!res.ok) {\n console.warn('Edge function error:', json?.error || res.statusText);\n setState({ flags: [], loading: false });\n return;\n }\n\n const flags = json.flags || [];\n setState({ flags, loading: false });\n console.log('[use-feature-flags] fetched flags', flags);\n\n // Store environment_id from first flag (assumes all have same env)\n if (flags.length > 0 && flags[0].environment_id) {\n setEnvId(flags[0].environment_id);\n }\n } catch (err: any) {\n console.error('Error fetching flags:', err.message);\n setState({ flags: [], loading: false });\n }\n };\n\n useEffect(() => {\n if (!apiKey || initialized) return;\n initialized = true;\n\n fetchFlags();\n\n return () => {\n if (debounceTimeout.current) clearTimeout(debounceTimeout.current);\n if (cleanupRef.current) cleanupRef.current();\n };\n }, [sanitizedEnvironment, apiKey]);\n\n // Subscribe to flag changes for the correct environment\n useEffect(() => {\n if (!envId) return;\n\n const channel = supabase\n .channel(`flags-${sanitizedEnvironment}`)\n .on(\n 'postgres_changes',\n {\n event: '*',\n schema: 'public',\n table: 'feature_flags',\n filter: `environment_id=eq.${envId}`,\n },\n () => {\n if (debounceTimeout.current) clearTimeout(debounceTimeout.current);\n debounceTimeout.current = setTimeout(fetchFlags, 300);\n }\n )\n .subscribe();\n\n if (cleanupRef.current) cleanupRef.current();\n cleanupRef.current = () => supabase.removeChannel(channel);\n\n return () => {\n if (debounceTimeout.current) clearTimeout(debounceTimeout.current);\n supabase.removeChannel(channel);\n };\n }, [envId]);\n\n return {\n isActive: (key: string) => {\n const active = state.flags.some((f) => f.key === key && f.enabled === true);\n process.env.DEBUG && console.log('[use-feature-flags] isActive', key, active);\n return active;\n },\n flags: state.flags,\n loading: state.loading,\n };\n}\n\n\n\n\n\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geejay/use-feature-flags",
3
- "version": "1.0.29",
3
+ "version": "1.0.31",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {