@databuddy/sdk 2.3.1 → 2.3.21
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/ai/vercel/index.d.mts +157 -7
- package/dist/ai/vercel/index.d.ts +157 -7
- package/dist/ai/vercel/index.mjs +229 -34
- package/dist/core/index.d.mts +3 -265
- package/dist/core/index.d.ts +3 -265
- package/dist/core/index.mjs +26 -3
- package/dist/node/index.d.mts +121 -10
- package/dist/node/index.d.ts +121 -10
- package/dist/node/index.mjs +256 -2
- package/dist/react/index.d.mts +58 -17
- package/dist/react/index.d.ts +58 -17
- package/dist/react/index.mjs +179 -86
- package/dist/shared/@databuddy/sdk.B6nwxnPC.d.mts +128 -0
- package/dist/shared/@databuddy/sdk.B6nwxnPC.d.ts +128 -0
- package/dist/shared/@databuddy/{sdk.CeYE_kaj.d.mts → sdk.BsF1xr6_.d.mts} +234 -11
- package/dist/shared/@databuddy/{sdk.CeYE_kaj.d.ts → sdk.BsF1xr6_.d.ts} +234 -11
- package/dist/shared/@databuddy/sdk.C8vEu9Y4.mjs +35 -0
- package/dist/shared/@databuddy/sdk.D0dyEsAb.mjs +482 -0
- package/dist/shared/@databuddy/sdk.DCKr2Zpd.mjs +202 -0
- package/dist/vue/index.d.mts +11 -2
- package/dist/vue/index.d.ts +11 -2
- package/dist/vue/index.mjs +19 -9
- package/package.json +3 -4
- package/dist/shared/@databuddy/sdk.BUsPV0LH.mjs +0 -25
- package/dist/shared/@databuddy/sdk.ByNF_UxE.mjs +0 -471
- package/dist/shared/@databuddy/sdk.OK9Nbqlf.d.mts +0 -64
- package/dist/shared/@databuddy/sdk.OK9Nbqlf.d.ts +0 -64
package/dist/react/index.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { D as DatabuddyConfig } from '../shared/@databuddy/sdk.
|
|
2
|
-
|
|
3
|
-
import { ReactNode } from 'react';
|
|
4
|
-
import
|
|
5
|
-
import { d as FlagsConfig, c as FlagState } from '../shared/@databuddy/sdk.OK9Nbqlf.js';
|
|
6
|
-
export { b as FlagResult, e as FlagsContext } from '../shared/@databuddy/sdk.OK9Nbqlf.js';
|
|
1
|
+
import { D as DatabuddyConfig } from '../shared/@databuddy/sdk.BsF1xr6_.js';
|
|
2
|
+
export { c as clear, f as flush, d as getAnonymousId, e as getSessionId, g as getTracker, h as getTrackingIds, j as getTrackingParams, i as isTrackerAvailable, t as track, b as trackError } from '../shared/@databuddy/sdk.BsF1xr6_.js';
|
|
3
|
+
import React, { ReactNode } from 'react';
|
|
4
|
+
import { F as FlagsConfig, a as FeatureState, b as FlagState, c as FlagsContext } from '../shared/@databuddy/sdk.B6nwxnPC.js';
|
|
7
5
|
|
|
8
6
|
/**
|
|
9
7
|
* React/Next.js component that injects the Databuddy tracking script.
|
|
@@ -56,18 +54,61 @@ export { b as FlagResult, e as FlagsContext } from '../shared/@databuddy/sdk.OK9
|
|
|
56
54
|
*/
|
|
57
55
|
declare function Databuddy(props: DatabuddyConfig): null;
|
|
58
56
|
|
|
57
|
+
/** biome-ignore-all lint/correctness/noUnusedImports: we need to import React to use the createContext function */
|
|
58
|
+
|
|
59
59
|
interface FlagsProviderProps extends FlagsConfig {
|
|
60
60
|
children: ReactNode;
|
|
61
61
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
declare function
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Flags provider component
|
|
64
|
+
* Creates a manager instance and provides flag methods to children
|
|
65
|
+
*/
|
|
66
|
+
declare function FlagsProvider({ children, ...config }: FlagsProviderProps): React.JSX.Element;
|
|
67
|
+
/**
|
|
68
|
+
* Access the full flags context
|
|
69
|
+
* @example
|
|
70
|
+
* const { isOn, getFlag, refresh } = useFlags();
|
|
71
|
+
*/
|
|
72
|
+
declare function useFlags(): FlagsContext;
|
|
73
|
+
/**
|
|
74
|
+
* Get a flag's full state with loading/error handling
|
|
75
|
+
* @example
|
|
76
|
+
* const flag = useFlag("my-feature");
|
|
77
|
+
* if (flag.loading) return <Skeleton />;
|
|
78
|
+
* return flag.on ? <NewFeature /> : <OldFeature />;
|
|
79
|
+
*/
|
|
80
|
+
declare function useFlag(key: string): FlagState;
|
|
81
|
+
/**
|
|
82
|
+
* Simple feature check - returns { on, loading, value, variant }
|
|
83
|
+
* @example
|
|
84
|
+
* const { on, loading } = useFeature("dark-mode");
|
|
85
|
+
* if (loading) return <Skeleton />;
|
|
86
|
+
* return on ? <DarkTheme /> : <LightTheme />;
|
|
87
|
+
*/
|
|
88
|
+
declare function useFeature(key: string): FeatureState;
|
|
89
|
+
/**
|
|
90
|
+
* Boolean-only feature check with default value
|
|
91
|
+
* Useful for SSR-safe rendering where you need a boolean immediately
|
|
92
|
+
* @example
|
|
93
|
+
* const isDarkMode = useFeatureOn("dark-mode", false);
|
|
94
|
+
* return isDarkMode ? <DarkTheme /> : <LightTheme />;
|
|
95
|
+
*/
|
|
96
|
+
declare function useFeatureOn(key: string, defaultValue?: boolean): boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Get a flag's typed value
|
|
99
|
+
* @example
|
|
100
|
+
* const maxItems = useFlagValue("max-items", 10);
|
|
101
|
+
* const theme = useFlagValue<"light" | "dark">("theme", "light");
|
|
102
|
+
*/
|
|
103
|
+
declare function useFlagValue<T extends boolean | string | number = boolean>(key: string, defaultValue?: T): T;
|
|
104
|
+
/**
|
|
105
|
+
* Get variant for A/B testing
|
|
106
|
+
* @example
|
|
107
|
+
* const variant = useVariant("checkout-experiment");
|
|
108
|
+
* if (variant === "control") return <OldCheckout />;
|
|
109
|
+
* if (variant === "treatment-a") return <NewCheckoutA />;
|
|
110
|
+
* return <NewCheckoutB />;
|
|
111
|
+
*/
|
|
112
|
+
declare function useVariant(key: string): string | undefined;
|
|
72
113
|
|
|
73
|
-
export { Databuddy,
|
|
114
|
+
export { Databuddy, FlagsProvider, useFeature, useFeatureOn, useFlag, useFlagValue, useFlags, useVariant };
|
package/dist/react/index.mjs
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
import { useRef, useEffect,
|
|
3
|
+
import { detectClientId } from '../core/index.mjs';
|
|
4
|
+
export { clear, flush, getAnonymousId, getSessionId, getTracker, getTrackingIds, getTrackingParams, isTrackerAvailable, track, trackError } from '../core/index.mjs';
|
|
5
|
+
import { i as isScriptInjected, c as createScript } from '../shared/@databuddy/sdk.C8vEu9Y4.mjs';
|
|
6
|
+
import React, { useRef, useMemo, useEffect, useSyncExternalStore, createContext, useContext } from 'react';
|
|
7
|
+
import { B as BrowserFlagStorage, C as CoreFlagsManager } from '../shared/@databuddy/sdk.D0dyEsAb.mjs';
|
|
8
|
+
import { l as logger } from '../shared/@databuddy/sdk.DCKr2Zpd.mjs';
|
|
7
9
|
|
|
8
10
|
function Databuddy(props) {
|
|
9
11
|
const clientId = detectClientId(props.clientId);
|
|
@@ -22,101 +24,192 @@ function Databuddy(props) {
|
|
|
22
24
|
return null;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
const FlagsReactContext = createContext(null);
|
|
28
|
+
function createFlagState(result, isLoading, isPending) {
|
|
29
|
+
if (isPending) {
|
|
30
|
+
return {
|
|
31
|
+
on: false,
|
|
32
|
+
enabled: false,
|
|
33
|
+
status: "pending",
|
|
34
|
+
loading: true,
|
|
35
|
+
isLoading: true,
|
|
36
|
+
isReady: false
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (isLoading || !result) {
|
|
40
|
+
return {
|
|
41
|
+
on: false,
|
|
42
|
+
enabled: false,
|
|
43
|
+
status: "loading",
|
|
44
|
+
loading: true,
|
|
45
|
+
isLoading: true,
|
|
46
|
+
isReady: false
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const status = result.reason === "ERROR" ? "error" : "ready";
|
|
50
|
+
return {
|
|
51
|
+
on: result.enabled,
|
|
52
|
+
enabled: result.enabled,
|
|
53
|
+
status,
|
|
54
|
+
loading: false,
|
|
55
|
+
isLoading: false,
|
|
56
|
+
isReady: true,
|
|
57
|
+
value: result.value,
|
|
58
|
+
variant: result.variant
|
|
59
|
+
};
|
|
60
|
+
}
|
|
28
61
|
function FlagsProvider({ children, ...config }) {
|
|
29
|
-
const
|
|
30
|
-
|
|
62
|
+
const storeRef = useRef({ flags: {}, isReady: false });
|
|
63
|
+
const listenersRef = useRef(/* @__PURE__ */ new Set());
|
|
64
|
+
const manager = useMemo(() => {
|
|
31
65
|
const storage = config.skipStorage ? void 0 : new BrowserFlagStorage();
|
|
32
|
-
|
|
66
|
+
return new CoreFlagsManager({
|
|
33
67
|
config,
|
|
34
68
|
storage,
|
|
35
69
|
onFlagsUpdate: (flags) => {
|
|
36
|
-
|
|
70
|
+
storeRef.current = { ...storeRef.current, flags };
|
|
71
|
+
for (const listener of listenersRef.current) {
|
|
72
|
+
listener();
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
onReady: () => {
|
|
76
|
+
storeRef.current = { ...storeRef.current, isReady: true };
|
|
77
|
+
for (const listener of listenersRef.current) {
|
|
78
|
+
listener();
|
|
79
|
+
}
|
|
37
80
|
}
|
|
38
81
|
});
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return () => {
|
|
42
|
-
managerRef.current = null;
|
|
43
|
-
flagsStore.set(managerAtom, null);
|
|
44
|
-
};
|
|
45
|
-
}, [
|
|
46
|
-
config.clientId,
|
|
47
|
-
config.apiUrl,
|
|
48
|
-
config.user?.userId,
|
|
49
|
-
config.user?.email,
|
|
50
|
-
config.disabled,
|
|
51
|
-
config.debug,
|
|
52
|
-
config.skipStorage,
|
|
53
|
-
config.isPending,
|
|
54
|
-
config.autoFetch
|
|
55
|
-
]);
|
|
82
|
+
}, [config.clientId]);
|
|
83
|
+
const prevConfigRef = useRef(config);
|
|
56
84
|
useEffect(() => {
|
|
57
|
-
|
|
58
|
-
|
|
85
|
+
const prevConfig = prevConfigRef.current;
|
|
86
|
+
const configChanged = prevConfig.apiUrl !== config.apiUrl || prevConfig.isPending !== config.isPending || prevConfig.user?.userId !== config.user?.userId || prevConfig.user?.email !== config.user?.email || prevConfig.environment !== config.environment || prevConfig.disabled !== config.disabled || prevConfig.autoFetch !== config.autoFetch || prevConfig.cacheTtl !== config.cacheTtl || prevConfig.staleTime !== config.staleTime;
|
|
87
|
+
if (configChanged) {
|
|
88
|
+
prevConfigRef.current = config;
|
|
89
|
+
manager.updateConfig(config);
|
|
59
90
|
}
|
|
60
|
-
}, [
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
91
|
+
}, [manager, config]);
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
return () => {
|
|
94
|
+
manager.destroy();
|
|
95
|
+
};
|
|
96
|
+
}, [manager]);
|
|
97
|
+
const subscribe = useMemo(
|
|
98
|
+
() => (callback) => {
|
|
99
|
+
listenersRef.current.add(callback);
|
|
100
|
+
return () => {
|
|
101
|
+
listenersRef.current.delete(callback);
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
[]
|
|
105
|
+
);
|
|
106
|
+
const getSnapshot = useMemo(() => () => storeRef.current, []);
|
|
107
|
+
const store = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
108
|
+
const contextValue = useMemo(
|
|
109
|
+
() => ({
|
|
110
|
+
// Cleaner API: getFlag returns FlagState
|
|
111
|
+
getFlag: (key) => {
|
|
112
|
+
const result = store.flags[key];
|
|
113
|
+
const managerState = manager.isEnabled(key);
|
|
114
|
+
return createFlagState(
|
|
115
|
+
result,
|
|
116
|
+
managerState.isLoading,
|
|
117
|
+
config.isPending ?? false
|
|
118
|
+
);
|
|
119
|
+
},
|
|
120
|
+
// Simple boolean check
|
|
121
|
+
isOn: (key) => {
|
|
122
|
+
const result = store.flags[key];
|
|
123
|
+
if (result) {
|
|
124
|
+
return result.enabled;
|
|
125
|
+
}
|
|
126
|
+
const state = manager.isEnabled(key);
|
|
127
|
+
return state.enabled;
|
|
128
|
+
},
|
|
129
|
+
// Get typed value
|
|
130
|
+
getValue: (key, defaultValue) => {
|
|
131
|
+
const result = store.flags[key];
|
|
132
|
+
if (result) {
|
|
133
|
+
return result.value;
|
|
134
|
+
}
|
|
135
|
+
return manager.getValue(key, defaultValue);
|
|
136
|
+
},
|
|
137
|
+
// Async fetch
|
|
138
|
+
fetchFlag: (key) => manager.getFlag(key),
|
|
139
|
+
fetchAllFlags: () => manager.fetchAllFlags(),
|
|
140
|
+
updateUser: (user) => manager.updateUser(user),
|
|
141
|
+
refresh: (forceClear = false) => manager.refresh(forceClear),
|
|
142
|
+
isReady: store.isReady,
|
|
143
|
+
// Deprecated: kept for backwards compatibility
|
|
144
|
+
isEnabled: (key) => {
|
|
145
|
+
const result = store.flags[key];
|
|
146
|
+
const managerState = manager.isEnabled(key);
|
|
147
|
+
return createFlagState(
|
|
148
|
+
result,
|
|
149
|
+
managerState.isLoading,
|
|
150
|
+
config.isPending ?? false
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
}),
|
|
154
|
+
[manager, store, config.isPending]
|
|
155
|
+
);
|
|
156
|
+
return /* @__PURE__ */ React.createElement(FlagsReactContext.Provider, { value: contextValue }, children);
|
|
72
157
|
}
|
|
73
158
|
function useFlags() {
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const isEnabled = (key) => {
|
|
84
|
-
if (!manager) {
|
|
85
|
-
return {
|
|
159
|
+
const context = useContext(FlagsReactContext);
|
|
160
|
+
if (!context) {
|
|
161
|
+
logger.warn("useFlags called outside FlagsProvider");
|
|
162
|
+
return {
|
|
163
|
+
isEnabled: () => createFlagState(void 0, false, false),
|
|
164
|
+
getFlag: () => createFlagState(void 0, false, false),
|
|
165
|
+
isOn: () => false,
|
|
166
|
+
getValue: (_key, defaultValue) => defaultValue ?? false,
|
|
167
|
+
fetchFlag: async () => ({
|
|
86
168
|
enabled: false,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
if (!manager) {
|
|
109
|
-
logger.warn("No manager for refresh");
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
manager.refresh(forceClear);
|
|
113
|
-
};
|
|
169
|
+
value: false,
|
|
170
|
+
payload: null,
|
|
171
|
+
reason: "NO_PROVIDER"
|
|
172
|
+
}),
|
|
173
|
+
fetchAllFlags: async () => {
|
|
174
|
+
},
|
|
175
|
+
updateUser: () => {
|
|
176
|
+
},
|
|
177
|
+
refresh: async () => {
|
|
178
|
+
},
|
|
179
|
+
isReady: false
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return context;
|
|
183
|
+
}
|
|
184
|
+
function useFlag(key) {
|
|
185
|
+
const { getFlag } = useFlags();
|
|
186
|
+
return getFlag(key);
|
|
187
|
+
}
|
|
188
|
+
function useFeature(key) {
|
|
189
|
+
const flag = useFlag(key);
|
|
114
190
|
return {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
191
|
+
on: flag.on,
|
|
192
|
+
loading: flag.loading,
|
|
193
|
+
status: flag.status,
|
|
194
|
+
value: flag.value,
|
|
195
|
+
variant: flag.variant
|
|
119
196
|
};
|
|
120
197
|
}
|
|
198
|
+
function useFeatureOn(key, defaultValue = false) {
|
|
199
|
+
const { isOn, isReady } = useFlags();
|
|
200
|
+
const flag = useFlag(key);
|
|
201
|
+
if (flag.loading || !isReady) {
|
|
202
|
+
return defaultValue;
|
|
203
|
+
}
|
|
204
|
+
return isOn(key);
|
|
205
|
+
}
|
|
206
|
+
function useFlagValue(key, defaultValue) {
|
|
207
|
+
const { getValue } = useFlags();
|
|
208
|
+
return getValue(key, defaultValue);
|
|
209
|
+
}
|
|
210
|
+
function useVariant(key) {
|
|
211
|
+
const flag = useFlag(key);
|
|
212
|
+
return flag.variant;
|
|
213
|
+
}
|
|
121
214
|
|
|
122
|
-
export { Databuddy, FlagsProvider, useFlags };
|
|
215
|
+
export { Databuddy, FlagsProvider, useFeature, useFeatureOn, useFlag, useFlagValue, useFlags, useVariant };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/** Flag evaluation result from API */
|
|
2
|
+
interface FlagResult {
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
value: boolean | string | number;
|
|
5
|
+
payload: Record<string, unknown> | null;
|
|
6
|
+
reason: string;
|
|
7
|
+
variant?: string;
|
|
8
|
+
}
|
|
9
|
+
/** User context for flag evaluation */
|
|
10
|
+
interface UserContext {
|
|
11
|
+
userId?: string;
|
|
12
|
+
email?: string;
|
|
13
|
+
properties?: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
/** SDK configuration */
|
|
16
|
+
interface FlagsConfig {
|
|
17
|
+
/** Client ID (website or organization ID) */
|
|
18
|
+
clientId: string;
|
|
19
|
+
/** API URL (default: https://api.databuddy.cc) */
|
|
20
|
+
apiUrl?: string;
|
|
21
|
+
/** User context for evaluation */
|
|
22
|
+
user?: UserContext;
|
|
23
|
+
/** Disable flag evaluation entirely */
|
|
24
|
+
disabled?: boolean;
|
|
25
|
+
/** Enable debug logging */
|
|
26
|
+
debug?: boolean;
|
|
27
|
+
/** Skip persistent storage (browser only) */
|
|
28
|
+
skipStorage?: boolean;
|
|
29
|
+
/** Session is loading - defer evaluation */
|
|
30
|
+
isPending?: boolean;
|
|
31
|
+
/** Auto-fetch all flags on init (default: true) */
|
|
32
|
+
autoFetch?: boolean;
|
|
33
|
+
/** Environment name */
|
|
34
|
+
environment?: string;
|
|
35
|
+
/** Cache TTL in ms - after this, cache is invalid (default: 60000) */
|
|
36
|
+
cacheTtl?: number;
|
|
37
|
+
/** Stale time in ms - after this, revalidate in background (default: cacheTtl/2) */
|
|
38
|
+
staleTime?: number;
|
|
39
|
+
}
|
|
40
|
+
/** Flag status for clear state management */
|
|
41
|
+
type FlagStatus = "loading" | "ready" | "error" | "pending";
|
|
42
|
+
/** Synchronous flag state for React hooks */
|
|
43
|
+
interface FlagState {
|
|
44
|
+
/** Whether the flag is enabled (true/false) */
|
|
45
|
+
on: boolean;
|
|
46
|
+
/** @deprecated Use `on` instead */
|
|
47
|
+
enabled: boolean;
|
|
48
|
+
/** Current status: loading, ready, error, or pending */
|
|
49
|
+
status: FlagStatus;
|
|
50
|
+
/** Whether the flag is still loading */
|
|
51
|
+
loading: boolean;
|
|
52
|
+
/** @deprecated Use `status === 'ready'` instead */
|
|
53
|
+
isLoading: boolean;
|
|
54
|
+
/** @deprecated Use `status === 'ready'` instead */
|
|
55
|
+
isReady: boolean;
|
|
56
|
+
/** The flag's value (boolean, string, or number) */
|
|
57
|
+
value?: boolean | string | number;
|
|
58
|
+
/** Variant name for multivariate flags */
|
|
59
|
+
variant?: string;
|
|
60
|
+
}
|
|
61
|
+
/** Feature state returned by useFeature hook */
|
|
62
|
+
interface FeatureState {
|
|
63
|
+
/** Whether the feature is enabled */
|
|
64
|
+
on: boolean;
|
|
65
|
+
/** Whether the flag is loading */
|
|
66
|
+
loading: boolean;
|
|
67
|
+
/** Current status */
|
|
68
|
+
status: FlagStatus;
|
|
69
|
+
/** The flag's value */
|
|
70
|
+
value?: boolean | string | number;
|
|
71
|
+
/** Variant for A/B tests */
|
|
72
|
+
variant?: string;
|
|
73
|
+
}
|
|
74
|
+
/** Context returned by useFlags hook */
|
|
75
|
+
interface FlagsContext {
|
|
76
|
+
/** @deprecated Use getFlag instead - confusing name */
|
|
77
|
+
isEnabled: (key: string) => FlagState;
|
|
78
|
+
/** Get a flag's full state */
|
|
79
|
+
getFlag: (key: string) => FlagState;
|
|
80
|
+
/** Get a flag's value with type safety */
|
|
81
|
+
getValue: <T extends boolean | string | number = boolean>(key: string, defaultValue?: T) => T;
|
|
82
|
+
/** Check if a flag is on (simple boolean) */
|
|
83
|
+
isOn: (key: string) => boolean;
|
|
84
|
+
/** Async fetch a specific flag */
|
|
85
|
+
fetchFlag: (key: string) => Promise<FlagResult>;
|
|
86
|
+
/** Fetch all flags */
|
|
87
|
+
fetchAllFlags: () => Promise<void>;
|
|
88
|
+
/** Update user context */
|
|
89
|
+
updateUser: (user: UserContext) => void;
|
|
90
|
+
/** Refresh all flags */
|
|
91
|
+
refresh: (forceClear?: boolean) => Promise<void>;
|
|
92
|
+
/** Whether the SDK is ready */
|
|
93
|
+
isReady: boolean;
|
|
94
|
+
}
|
|
95
|
+
/** Storage interface for persistence */
|
|
96
|
+
interface StorageInterface {
|
|
97
|
+
get(key: string): FlagResult | null;
|
|
98
|
+
set(key: string, value: FlagResult): void;
|
|
99
|
+
getAll(): Record<string, FlagResult>;
|
|
100
|
+
setAll(flags: Record<string, FlagResult>): void;
|
|
101
|
+
delete?(key: string): void;
|
|
102
|
+
deleteMultiple?(keys: string[]): void;
|
|
103
|
+
clear(): void;
|
|
104
|
+
cleanupExpired(): void;
|
|
105
|
+
}
|
|
106
|
+
/** Manager constructor options */
|
|
107
|
+
interface FlagsManagerOptions {
|
|
108
|
+
config: FlagsConfig;
|
|
109
|
+
storage?: StorageInterface;
|
|
110
|
+
onFlagsUpdate?: (flags: Record<string, FlagResult>) => void;
|
|
111
|
+
onConfigUpdate?: (config: FlagsConfig) => void;
|
|
112
|
+
onReady?: () => void;
|
|
113
|
+
}
|
|
114
|
+
/** Flags manager interface */
|
|
115
|
+
interface FlagsManager {
|
|
116
|
+
getFlag: (key: string, user?: UserContext) => Promise<FlagResult>;
|
|
117
|
+
isEnabled: (key: string) => FlagState;
|
|
118
|
+
getValue: <T = boolean | string | number>(key: string, defaultValue?: T) => T;
|
|
119
|
+
fetchAllFlags: (user?: UserContext) => Promise<void>;
|
|
120
|
+
updateUser: (user: UserContext) => void;
|
|
121
|
+
refresh: (forceClear?: boolean) => Promise<void>;
|
|
122
|
+
updateConfig: (config: FlagsConfig) => void;
|
|
123
|
+
getMemoryFlags: () => Record<string, FlagResult>;
|
|
124
|
+
isReady: () => boolean;
|
|
125
|
+
destroy?: () => void;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export type { FlagsConfig as F, UserContext as U, FeatureState as a, FlagState as b, FlagsContext as c, FlagResult as d, FlagsManager as e, FlagsManagerOptions as f };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/** Flag evaluation result from API */
|
|
2
|
+
interface FlagResult {
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
value: boolean | string | number;
|
|
5
|
+
payload: Record<string, unknown> | null;
|
|
6
|
+
reason: string;
|
|
7
|
+
variant?: string;
|
|
8
|
+
}
|
|
9
|
+
/** User context for flag evaluation */
|
|
10
|
+
interface UserContext {
|
|
11
|
+
userId?: string;
|
|
12
|
+
email?: string;
|
|
13
|
+
properties?: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
/** SDK configuration */
|
|
16
|
+
interface FlagsConfig {
|
|
17
|
+
/** Client ID (website or organization ID) */
|
|
18
|
+
clientId: string;
|
|
19
|
+
/** API URL (default: https://api.databuddy.cc) */
|
|
20
|
+
apiUrl?: string;
|
|
21
|
+
/** User context for evaluation */
|
|
22
|
+
user?: UserContext;
|
|
23
|
+
/** Disable flag evaluation entirely */
|
|
24
|
+
disabled?: boolean;
|
|
25
|
+
/** Enable debug logging */
|
|
26
|
+
debug?: boolean;
|
|
27
|
+
/** Skip persistent storage (browser only) */
|
|
28
|
+
skipStorage?: boolean;
|
|
29
|
+
/** Session is loading - defer evaluation */
|
|
30
|
+
isPending?: boolean;
|
|
31
|
+
/** Auto-fetch all flags on init (default: true) */
|
|
32
|
+
autoFetch?: boolean;
|
|
33
|
+
/** Environment name */
|
|
34
|
+
environment?: string;
|
|
35
|
+
/** Cache TTL in ms - after this, cache is invalid (default: 60000) */
|
|
36
|
+
cacheTtl?: number;
|
|
37
|
+
/** Stale time in ms - after this, revalidate in background (default: cacheTtl/2) */
|
|
38
|
+
staleTime?: number;
|
|
39
|
+
}
|
|
40
|
+
/** Flag status for clear state management */
|
|
41
|
+
type FlagStatus = "loading" | "ready" | "error" | "pending";
|
|
42
|
+
/** Synchronous flag state for React hooks */
|
|
43
|
+
interface FlagState {
|
|
44
|
+
/** Whether the flag is enabled (true/false) */
|
|
45
|
+
on: boolean;
|
|
46
|
+
/** @deprecated Use `on` instead */
|
|
47
|
+
enabled: boolean;
|
|
48
|
+
/** Current status: loading, ready, error, or pending */
|
|
49
|
+
status: FlagStatus;
|
|
50
|
+
/** Whether the flag is still loading */
|
|
51
|
+
loading: boolean;
|
|
52
|
+
/** @deprecated Use `status === 'ready'` instead */
|
|
53
|
+
isLoading: boolean;
|
|
54
|
+
/** @deprecated Use `status === 'ready'` instead */
|
|
55
|
+
isReady: boolean;
|
|
56
|
+
/** The flag's value (boolean, string, or number) */
|
|
57
|
+
value?: boolean | string | number;
|
|
58
|
+
/** Variant name for multivariate flags */
|
|
59
|
+
variant?: string;
|
|
60
|
+
}
|
|
61
|
+
/** Feature state returned by useFeature hook */
|
|
62
|
+
interface FeatureState {
|
|
63
|
+
/** Whether the feature is enabled */
|
|
64
|
+
on: boolean;
|
|
65
|
+
/** Whether the flag is loading */
|
|
66
|
+
loading: boolean;
|
|
67
|
+
/** Current status */
|
|
68
|
+
status: FlagStatus;
|
|
69
|
+
/** The flag's value */
|
|
70
|
+
value?: boolean | string | number;
|
|
71
|
+
/** Variant for A/B tests */
|
|
72
|
+
variant?: string;
|
|
73
|
+
}
|
|
74
|
+
/** Context returned by useFlags hook */
|
|
75
|
+
interface FlagsContext {
|
|
76
|
+
/** @deprecated Use getFlag instead - confusing name */
|
|
77
|
+
isEnabled: (key: string) => FlagState;
|
|
78
|
+
/** Get a flag's full state */
|
|
79
|
+
getFlag: (key: string) => FlagState;
|
|
80
|
+
/** Get a flag's value with type safety */
|
|
81
|
+
getValue: <T extends boolean | string | number = boolean>(key: string, defaultValue?: T) => T;
|
|
82
|
+
/** Check if a flag is on (simple boolean) */
|
|
83
|
+
isOn: (key: string) => boolean;
|
|
84
|
+
/** Async fetch a specific flag */
|
|
85
|
+
fetchFlag: (key: string) => Promise<FlagResult>;
|
|
86
|
+
/** Fetch all flags */
|
|
87
|
+
fetchAllFlags: () => Promise<void>;
|
|
88
|
+
/** Update user context */
|
|
89
|
+
updateUser: (user: UserContext) => void;
|
|
90
|
+
/** Refresh all flags */
|
|
91
|
+
refresh: (forceClear?: boolean) => Promise<void>;
|
|
92
|
+
/** Whether the SDK is ready */
|
|
93
|
+
isReady: boolean;
|
|
94
|
+
}
|
|
95
|
+
/** Storage interface for persistence */
|
|
96
|
+
interface StorageInterface {
|
|
97
|
+
get(key: string): FlagResult | null;
|
|
98
|
+
set(key: string, value: FlagResult): void;
|
|
99
|
+
getAll(): Record<string, FlagResult>;
|
|
100
|
+
setAll(flags: Record<string, FlagResult>): void;
|
|
101
|
+
delete?(key: string): void;
|
|
102
|
+
deleteMultiple?(keys: string[]): void;
|
|
103
|
+
clear(): void;
|
|
104
|
+
cleanupExpired(): void;
|
|
105
|
+
}
|
|
106
|
+
/** Manager constructor options */
|
|
107
|
+
interface FlagsManagerOptions {
|
|
108
|
+
config: FlagsConfig;
|
|
109
|
+
storage?: StorageInterface;
|
|
110
|
+
onFlagsUpdate?: (flags: Record<string, FlagResult>) => void;
|
|
111
|
+
onConfigUpdate?: (config: FlagsConfig) => void;
|
|
112
|
+
onReady?: () => void;
|
|
113
|
+
}
|
|
114
|
+
/** Flags manager interface */
|
|
115
|
+
interface FlagsManager {
|
|
116
|
+
getFlag: (key: string, user?: UserContext) => Promise<FlagResult>;
|
|
117
|
+
isEnabled: (key: string) => FlagState;
|
|
118
|
+
getValue: <T = boolean | string | number>(key: string, defaultValue?: T) => T;
|
|
119
|
+
fetchAllFlags: (user?: UserContext) => Promise<void>;
|
|
120
|
+
updateUser: (user: UserContext) => void;
|
|
121
|
+
refresh: (forceClear?: boolean) => Promise<void>;
|
|
122
|
+
updateConfig: (config: FlagsConfig) => void;
|
|
123
|
+
getMemoryFlags: () => Record<string, FlagResult>;
|
|
124
|
+
isReady: () => boolean;
|
|
125
|
+
destroy?: () => void;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export type { FlagsConfig as F, UserContext as U, FeatureState as a, FlagState as b, FlagsContext as c, FlagResult as d, FlagsManager as e, FlagsManagerOptions as f };
|