@geejay/use-feature-flags 1.0.3 → 1.0.5
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/useFeatureFlags.d.ts +1 -1
- package/dist/useFeatureFlags.js +50 -55
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FeatureFlag } from './store';
|
|
2
2
|
export declare const DEFAULT_SUPABASE_URL = "https://khppgsehvvlukzfdqbuo.supabase.co";
|
|
3
|
-
export declare const DEFAULT_SUPABASE_KEY = "
|
|
3
|
+
export declare const DEFAULT_SUPABASE_KEY = "yeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImtocHBnc2VodnZsdWt6ZmRxYnVvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTI2ODU1MzQsImV4cCI6MjA2ODI2MTUzNH0.8Z4VY4HFMm95UgO21c-DnDkbLPN_0mbDBZJPExaghDk";
|
|
4
4
|
export declare function useFeatureFlags(passedKey?: string, environment?: string): {
|
|
5
5
|
isActive: (key: string) => boolean;
|
|
6
6
|
flags: FeatureFlag[];
|
package/dist/useFeatureFlags.js
CHANGED
|
@@ -1,75 +1,44 @@
|
|
|
1
|
-
import { useEffect, useRef, useState } from 'react';
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import { createClient } from '@supabase/supabase-js';
|
|
3
3
|
export const DEFAULT_SUPABASE_URL = 'https://khppgsehvvlukzfdqbuo.supabase.co';
|
|
4
|
-
export const DEFAULT_SUPABASE_KEY = '
|
|
5
|
-
const
|
|
4
|
+
export const DEFAULT_SUPABASE_KEY = 'yeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImtocHBnc2VodnZsdWt6ZmRxYnVvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTI2ODU1MzQsImV4cCI6MjA2ODI2MTUzNH0.8Z4VY4HFMm95UgO21c-DnDkbLPN_0mbDBZJPExaghDk';
|
|
5
|
+
const EDGE_FN_URL = `${DEFAULT_SUPABASE_URL}/functions/v1/get-feature-flags`;
|
|
6
6
|
let initialized = false;
|
|
7
7
|
export function useFeatureFlags(passedKey, environment = 'localhost') {
|
|
8
8
|
const [state, setState] = useState({ flags: [], loading: true });
|
|
9
|
+
const [envId, setEnvId] = useState(null);
|
|
9
10
|
const debounceTimeout = useRef(null);
|
|
10
11
|
const cleanupRef = useRef();
|
|
11
12
|
const apiKey = passedKey || DEFAULT_SUPABASE_KEY;
|
|
13
|
+
const supabase = useMemo(() => {
|
|
14
|
+
return createClient(DEFAULT_SUPABASE_URL, passedKey || DEFAULT_SUPABASE_KEY);
|
|
15
|
+
}, [passedKey]);
|
|
12
16
|
const fetchFlags = async () => {
|
|
13
17
|
setState((prev) => ({ ...prev, loading: true }));
|
|
14
18
|
try {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
const res = await fetch(EDGE_FN_URL, {
|
|
20
|
+
method: 'POST',
|
|
21
|
+
headers: {
|
|
22
|
+
'Content-Type': 'application/json',
|
|
23
|
+
'api-key': apiKey,
|
|
24
|
+
},
|
|
25
|
+
body: JSON.stringify({ environment }),
|
|
26
|
+
});
|
|
27
|
+
const json = await res.json();
|
|
28
|
+
if (!res.ok) {
|
|
29
|
+
console.warn('Edge function error:', (json === null || json === void 0 ? void 0 : json.error) || res.statusText);
|
|
23
30
|
setState({ flags: [], loading: false });
|
|
24
31
|
return;
|
|
25
32
|
}
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
.
|
|
31
|
-
.eq('tenant_id', tenant)
|
|
32
|
-
.maybeSingle();
|
|
33
|
-
if (envError || !(env === null || env === void 0 ? void 0 : env.id)) {
|
|
34
|
-
console.warn('Environment not found:', environment);
|
|
35
|
-
setState({ flags: [], loading: false });
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
const environmentId = env.id;
|
|
39
|
-
const { data: flagsData, error: flagsError } = await supabase
|
|
40
|
-
.from('feature_flags')
|
|
41
|
-
.select('*')
|
|
42
|
-
.eq('tenant_id', tenant)
|
|
43
|
-
.eq('environment_id', environmentId);
|
|
44
|
-
if (flagsError) {
|
|
45
|
-
console.error('Flag fetch error:', flagsError.message);
|
|
46
|
-
setState({ flags: [], loading: false });
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
setState({ flags: flagsData || [], loading: false });
|
|
50
|
-
// Clean up existing channel if present
|
|
51
|
-
if (cleanupRef.current)
|
|
52
|
-
cleanupRef.current();
|
|
53
|
-
const channel = supabase
|
|
54
|
-
.channel(`flags-${environment}`)
|
|
55
|
-
.on('postgres_changes', {
|
|
56
|
-
event: '*',
|
|
57
|
-
schema: 'public',
|
|
58
|
-
table: 'feature_flags',
|
|
59
|
-
filter: `environment_id=eq.${environmentId}`,
|
|
60
|
-
}, () => {
|
|
61
|
-
if (debounceTimeout.current)
|
|
62
|
-
clearTimeout(debounceTimeout.current);
|
|
63
|
-
debounceTimeout.current = setTimeout(fetchFlags, 300);
|
|
64
|
-
})
|
|
65
|
-
.subscribe();
|
|
66
|
-
cleanupRef.current = () => {
|
|
67
|
-
supabase.removeChannel(channel);
|
|
68
|
-
};
|
|
33
|
+
const flags = json.flags || [];
|
|
34
|
+
setState({ flags, loading: false });
|
|
35
|
+
// Store environment_id from first flag (assumes all have same env)
|
|
36
|
+
if (flags.length > 0 && flags[0].environment_id) {
|
|
37
|
+
setEnvId(flags[0].environment_id);
|
|
69
38
|
}
|
|
70
39
|
}
|
|
71
40
|
catch (err) {
|
|
72
|
-
console.error('
|
|
41
|
+
console.error('Error fetching flags:', err.message);
|
|
73
42
|
setState({ flags: [], loading: false });
|
|
74
43
|
}
|
|
75
44
|
};
|
|
@@ -85,6 +54,32 @@ export function useFeatureFlags(passedKey, environment = 'localhost') {
|
|
|
85
54
|
cleanupRef.current();
|
|
86
55
|
};
|
|
87
56
|
}, [environment, apiKey]);
|
|
57
|
+
// Subscribe to flag changes for the correct environment
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (!envId)
|
|
60
|
+
return;
|
|
61
|
+
const channel = supabase
|
|
62
|
+
.channel(`flags-${environment}`)
|
|
63
|
+
.on('postgres_changes', {
|
|
64
|
+
event: '*',
|
|
65
|
+
schema: 'public',
|
|
66
|
+
table: 'feature_flags',
|
|
67
|
+
filter: `environment_id=eq.${envId}`,
|
|
68
|
+
}, () => {
|
|
69
|
+
if (debounceTimeout.current)
|
|
70
|
+
clearTimeout(debounceTimeout.current);
|
|
71
|
+
debounceTimeout.current = setTimeout(fetchFlags, 300);
|
|
72
|
+
})
|
|
73
|
+
.subscribe();
|
|
74
|
+
if (cleanupRef.current)
|
|
75
|
+
cleanupRef.current();
|
|
76
|
+
cleanupRef.current = () => supabase.removeChannel(channel);
|
|
77
|
+
return () => {
|
|
78
|
+
if (debounceTimeout.current)
|
|
79
|
+
clearTimeout(debounceTimeout.current);
|
|
80
|
+
supabase.removeChannel(channel);
|
|
81
|
+
};
|
|
82
|
+
}, [envId]);
|
|
88
83
|
return {
|
|
89
84
|
isActive: (key) => state.flags.some((f) => f.key === key && f.enabled),
|
|
90
85
|
flags: state.flags,
|