@rolloutly/react 0.1.2 → 0.1.4
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 +103 -8
- package/dist/index.cjs +25 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +26 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -70,6 +70,14 @@ function Banner() {
|
|
|
70
70
|
'api-rate-limit': 100,
|
|
71
71
|
}}
|
|
72
72
|
|
|
73
|
+
// Optional: User context for targeting rules
|
|
74
|
+
user={{
|
|
75
|
+
userId: 'user-123',
|
|
76
|
+
email: 'alice@example.com',
|
|
77
|
+
orgId: 'acme-corp',
|
|
78
|
+
plan: 'pro',
|
|
79
|
+
}}
|
|
80
|
+
|
|
73
81
|
// Optional: Enable debug logging
|
|
74
82
|
debug={false}
|
|
75
83
|
|
|
@@ -80,6 +88,73 @@ function Banner() {
|
|
|
80
88
|
</RolloutlyProvider>
|
|
81
89
|
```
|
|
82
90
|
|
|
91
|
+
## User Targeting
|
|
92
|
+
|
|
93
|
+
Pass user context to enable personalized flag values based on targeting rules.
|
|
94
|
+
|
|
95
|
+
### Static User Context
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
function App() {
|
|
99
|
+
const user = {
|
|
100
|
+
userId: 'user-123',
|
|
101
|
+
email: 'alice@example.com',
|
|
102
|
+
orgId: 'acme-corp',
|
|
103
|
+
plan: 'pro',
|
|
104
|
+
role: 'admin',
|
|
105
|
+
// Custom attributes
|
|
106
|
+
betaUser: true,
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<RolloutlyProvider token="rly_xxx" user={user}>
|
|
111
|
+
<MyApp />
|
|
112
|
+
</RolloutlyProvider>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Dynamic User Identification
|
|
118
|
+
|
|
119
|
+
Use `identify()` and `reset()` to manage user context dynamically:
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
function AuthComponent() {
|
|
123
|
+
const { identify, reset } = useRolloutly();
|
|
124
|
+
|
|
125
|
+
const handleLogin = async (user) => {
|
|
126
|
+
await identify({
|
|
127
|
+
userId: user.id,
|
|
128
|
+
email: user.email,
|
|
129
|
+
orgId: user.organizationId,
|
|
130
|
+
plan: user.subscription.plan,
|
|
131
|
+
});
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const handleLogout = async () => {
|
|
135
|
+
await reset();
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<>
|
|
140
|
+
<button onClick={() => handleLogin(currentUser)}>Login</button>
|
|
141
|
+
<button onClick={handleLogout}>Logout</button>
|
|
142
|
+
</>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### User Context Properties
|
|
148
|
+
|
|
149
|
+
| Property | Type | Description |
|
|
150
|
+
|----------|------|-------------|
|
|
151
|
+
| `userId` | `string` | Unique user identifier |
|
|
152
|
+
| `email` | `string` | User's email address |
|
|
153
|
+
| `orgId` | `string` | Organization/company ID |
|
|
154
|
+
| `plan` | `string` | Subscription plan |
|
|
155
|
+
| `role` | `string` | User's role |
|
|
156
|
+
| `[key]` | `string \| number \| boolean \| string[]` | Custom attributes |
|
|
157
|
+
|
|
83
158
|
## Hooks
|
|
84
159
|
|
|
85
160
|
### `useFlag<T>(key: string): T | undefined`
|
|
@@ -107,28 +182,48 @@ if (showBanner) {
|
|
|
107
182
|
|
|
108
183
|
Get all flag values as a key-value object. This is the recommended way to access multiple flags.
|
|
109
184
|
|
|
185
|
+
**CamelCase Support:** Flag keys with hyphens or underscores are automatically available in camelCase for easy destructuring!
|
|
186
|
+
|
|
110
187
|
```tsx
|
|
111
188
|
const flags = useFlags();
|
|
112
189
|
|
|
113
|
-
// Access by key
|
|
114
|
-
const myFeature = flags['
|
|
190
|
+
// Access by original key
|
|
191
|
+
const myFeature = flags['instagram-integration'];
|
|
115
192
|
|
|
116
|
-
// Or
|
|
117
|
-
const
|
|
193
|
+
// Or use camelCase version (automatically available!)
|
|
194
|
+
const myFeature = flags.instagramIntegration;
|
|
118
195
|
|
|
119
|
-
//
|
|
120
|
-
const {
|
|
196
|
+
// Clean destructuring with camelCase
|
|
197
|
+
const { instagramIntegration, newCheckout, showBanner } = useFlags();
|
|
198
|
+
|
|
199
|
+
// Both formats work:
|
|
200
|
+
// 'instagram-integration' -> instagramIntegration
|
|
201
|
+
// 'my_feature_flag' -> myFeatureFlag
|
|
121
202
|
```
|
|
122
203
|
|
|
123
204
|
### `useRolloutly(): RolloutlyContextValue`
|
|
124
205
|
|
|
125
|
-
Get the Rolloutly context including loading
|
|
206
|
+
Get the Rolloutly context including loading states, error handling, and user management.
|
|
126
207
|
|
|
127
208
|
```tsx
|
|
128
|
-
const {
|
|
209
|
+
const {
|
|
210
|
+
isLoading,
|
|
211
|
+
isError,
|
|
212
|
+
error,
|
|
213
|
+
getFlag,
|
|
214
|
+
isEnabled,
|
|
215
|
+
identify, // Update user context
|
|
216
|
+
reset, // Clear user context
|
|
217
|
+
} = useRolloutly();
|
|
129
218
|
|
|
130
219
|
if (isLoading) return <Spinner />;
|
|
131
220
|
if (isError) return <Error message={error?.message} />;
|
|
221
|
+
|
|
222
|
+
// Identify user (e.g., after login)
|
|
223
|
+
await identify({ userId: 'user-123', email: 'alice@example.com' });
|
|
224
|
+
|
|
225
|
+
// Reset user context (e.g., on logout)
|
|
226
|
+
await reset();
|
|
132
227
|
```
|
|
133
228
|
|
|
134
229
|
### `useRolloutlyClient(): RolloutlyClient`
|
package/dist/index.cjs
CHANGED
|
@@ -17,21 +17,34 @@ function RolloutlyProvider({
|
|
|
17
17
|
baseUrl,
|
|
18
18
|
realtimeEnabled = true,
|
|
19
19
|
defaultFlags,
|
|
20
|
+
user,
|
|
20
21
|
debug = false,
|
|
21
22
|
loadingComponent
|
|
22
23
|
}) {
|
|
23
24
|
const [isLoading, setIsLoading] = react.useState(true);
|
|
24
25
|
const [isError, setIsError] = react.useState(false);
|
|
25
26
|
const [error, setError] = react.useState(null);
|
|
27
|
+
const prevUserRef = react.useRef(user);
|
|
26
28
|
const client = react.useMemo(() => {
|
|
27
29
|
return new core.RolloutlyClient({
|
|
28
30
|
token,
|
|
29
31
|
baseUrl,
|
|
30
32
|
realtimeEnabled,
|
|
31
33
|
defaultFlags,
|
|
34
|
+
user,
|
|
32
35
|
debug
|
|
33
36
|
});
|
|
34
37
|
}, [token, baseUrl, realtimeEnabled, defaultFlags, debug]);
|
|
38
|
+
react.useEffect(() => {
|
|
39
|
+
if (prevUserRef.current !== user && client) {
|
|
40
|
+
prevUserRef.current = user;
|
|
41
|
+
if (user) {
|
|
42
|
+
void client.identify(user);
|
|
43
|
+
} else {
|
|
44
|
+
void client.reset();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}, [user, client]);
|
|
35
48
|
react.useEffect(() => {
|
|
36
49
|
let mounted = true;
|
|
37
50
|
client.waitForInit().then(() => {
|
|
@@ -88,12 +101,23 @@ function useRolloutly() {
|
|
|
88
101
|
},
|
|
89
102
|
[client]
|
|
90
103
|
);
|
|
104
|
+
const identify = react.useCallback(
|
|
105
|
+
async (user) => {
|
|
106
|
+
return client.identify(user);
|
|
107
|
+
},
|
|
108
|
+
[client]
|
|
109
|
+
);
|
|
110
|
+
const reset = react.useCallback(async () => {
|
|
111
|
+
return client.reset();
|
|
112
|
+
}, [client]);
|
|
91
113
|
return {
|
|
92
114
|
isLoading,
|
|
93
115
|
isError,
|
|
94
116
|
error,
|
|
95
117
|
getFlag,
|
|
96
|
-
isEnabled
|
|
118
|
+
isEnabled,
|
|
119
|
+
identify,
|
|
120
|
+
reset
|
|
97
121
|
};
|
|
98
122
|
}
|
|
99
123
|
function useFlag(key) {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/provider.tsx"],"names":["createContext","useState","useMemo","RolloutlyClient","useEffect","useContext","useCallback","useSyncExternalStore"],"mappings":"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","file":"index.cjs","sourcesContent":["'use client';\n\nimport { RolloutlyClient, type FlagValue } from '@rolloutly/core';\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n useSyncExternalStore,\n} from 'react';\n\nimport type { RolloutlyContextValue, RolloutlyProviderProps } from './types';\n\nconst RolloutlyContext = createContext<{\n client: RolloutlyClient | null;\n isLoading: boolean;\n isError: boolean;\n error: Error | null;\n}>({\n client: null,\n isLoading: true,\n isError: false,\n error: null,\n});\n\n/**\n * Provider component that initializes Rolloutly and provides context\n */\nexport function RolloutlyProvider({\n token,\n children,\n baseUrl,\n realtimeEnabled = true,\n defaultFlags,\n debug = false,\n loadingComponent,\n}: RolloutlyProviderProps): JSX.Element {\n const [isLoading, setIsLoading] = useState(true);\n const [isError, setIsError] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const client = useMemo(() => {\n return new RolloutlyClient({\n token,\n baseUrl,\n realtimeEnabled,\n defaultFlags,\n debug,\n });\n }, [token, baseUrl, realtimeEnabled, defaultFlags, debug]);\n\n useEffect(() => {\n let mounted = true;\n\n client\n .waitForInit()\n .then(() => {\n if (mounted) {\n setIsLoading(false);\n }\n })\n .catch((err) => {\n if (mounted) {\n setIsError(true);\n setError(err instanceof Error ? err : new Error(String(err)));\n setIsLoading(false);\n }\n });\n\n return () => {\n mounted = false;\n client.close();\n };\n }, [client]);\n\n const contextValue = useMemo(\n () => ({\n client,\n isLoading,\n isError,\n error,\n }),\n [client, isLoading, isError, error],\n );\n\n if (isLoading && loadingComponent) {\n return <>{loadingComponent}</>;\n }\n\n return (\n <RolloutlyContext.Provider value={contextValue}>\n {children}\n </RolloutlyContext.Provider>\n );\n}\n\n/**\n * Hook to access the Rolloutly client directly\n */\nexport function useRolloutlyClient(): RolloutlyClient {\n const { client } = useContext(RolloutlyContext);\n\n if (!client) {\n throw new Error('useRolloutlyClient must be used within RolloutlyProvider');\n }\n\n return client;\n}\n\n/**\n * Hook to get Rolloutly status\n */\nexport function useRolloutly(): RolloutlyContextValue {\n const { client, isLoading, isError, error } = useContext(RolloutlyContext);\n\n if (!client) {\n throw new Error('useRolloutly must be used within RolloutlyProvider');\n }\n\n const getFlag = useCallback(\n <T extends FlagValue = FlagValue>(key: string): T | undefined => {\n return client.getFlag<T>(key);\n },\n [client],\n );\n\n const isEnabled = useCallback(\n (key: string): boolean => {\n return client.isEnabled(key);\n },\n [client],\n );\n\n return {\n isLoading,\n isError,\n error,\n getFlag,\n isEnabled,\n };\n}\n\n/**\n * Hook to get a single flag value with real-time updates\n */\nexport function useFlag<T extends FlagValue = FlagValue>(\n key: string,\n): T | undefined {\n const client = useRolloutlyClient();\n\n const getSnapshot = useCallback((): T | undefined => {\n return client.getFlag<T>(key);\n }, [client, key]);\n\n const subscribe = useCallback(\n (onStoreChange: () => void): (() => void) => {\n return client.subscribe(onStoreChange);\n },\n [client],\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Hook to check if a boolean flag is enabled with real-time updates\n */\nexport function useFlagEnabled(key: string): boolean {\n const client = useRolloutlyClient();\n\n const getSnapshot = useCallback((): boolean => {\n return client.isEnabled(key);\n }, [client, key]);\n\n const subscribe = useCallback(\n (onStoreChange: () => void): (() => void) => {\n return client.subscribe(onStoreChange);\n },\n [client],\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Hook to get all flag values with real-time updates\n * Returns an object keyed by flag key with the flag values\n *\n * @example\n * const flags = useFlags();\n * const value = flags['my-flag'];\n *\n * // Or with destructuring:\n * const { 'my-flag': myFlag } = useFlags();\n */\nexport function useFlags(): Record<string, FlagValue | undefined> {\n const client = useRolloutlyClient();\n\n // Use the client's cached flag values to avoid creating new objects\n const getSnapshot = useCallback((): Record<string, FlagValue | undefined> => {\n return client.getFlagValues();\n }, [client]);\n\n const subscribe = useCallback(\n (onStoreChange: () => void): (() => void) => {\n return client.subscribe(onStoreChange);\n },\n [client],\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/provider.tsx"],"names":["createContext","useState","useRef","useMemo","RolloutlyClient","useEffect","useContext","useCallback","useSyncExternalStore"],"mappings":"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","file":"index.cjs","sourcesContent":["'use client';\n\nimport { RolloutlyClient, type FlagValue, type UserContext } from '@rolloutly/core';\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\n\nimport type { RolloutlyContextValue, RolloutlyProviderProps } from './types';\n\nconst RolloutlyContext = createContext<{\n client: RolloutlyClient | null;\n isLoading: boolean;\n isError: boolean;\n error: Error | null;\n}>({\n client: null,\n isLoading: true,\n isError: false,\n error: null,\n});\n\n/**\n * Provider component that initializes Rolloutly and provides context\n */\nexport function RolloutlyProvider({\n token,\n children,\n baseUrl,\n realtimeEnabled = true,\n defaultFlags,\n user,\n debug = false,\n loadingComponent,\n}: RolloutlyProviderProps): JSX.Element {\n const [isLoading, setIsLoading] = useState(true);\n const [isError, setIsError] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Track if user changed to re-identify\n const prevUserRef = useRef<UserContext | undefined>(user);\n\n // eslint-disable-next-line react-hooks/exhaustive-deps -- user changes handled via identify() in useEffect\n const client = useMemo(() => {\n return new RolloutlyClient({\n token,\n baseUrl,\n realtimeEnabled,\n defaultFlags,\n user,\n debug,\n });\n }, [token, baseUrl, realtimeEnabled, defaultFlags, debug]);\n\n // Handle user context changes\n useEffect(() => {\n if (prevUserRef.current !== user && client) {\n prevUserRef.current = user;\n\n if (user) {\n void client.identify(user);\n } else {\n void client.reset();\n }\n }\n }, [user, client]);\n\n useEffect(() => {\n let mounted = true;\n\n client\n .waitForInit()\n .then(() => {\n if (mounted) {\n setIsLoading(false);\n }\n })\n .catch((err) => {\n if (mounted) {\n setIsError(true);\n setError(err instanceof Error ? err : new Error(String(err)));\n setIsLoading(false);\n }\n });\n\n return () => {\n mounted = false;\n client.close();\n };\n }, [client]);\n\n const contextValue = useMemo(\n () => ({\n client,\n isLoading,\n isError,\n error,\n }),\n [client, isLoading, isError, error],\n );\n\n if (isLoading && loadingComponent) {\n return <>{loadingComponent}</>;\n }\n\n return (\n <RolloutlyContext.Provider value={contextValue}>\n {children}\n </RolloutlyContext.Provider>\n );\n}\n\n/**\n * Hook to access the Rolloutly client directly\n */\nexport function useRolloutlyClient(): RolloutlyClient {\n const { client } = useContext(RolloutlyContext);\n\n if (!client) {\n throw new Error('useRolloutlyClient must be used within RolloutlyProvider');\n }\n\n return client;\n}\n\n/**\n * Hook to get Rolloutly status and utilities\n */\nexport function useRolloutly(): RolloutlyContextValue {\n const { client, isLoading, isError, error } = useContext(RolloutlyContext);\n\n if (!client) {\n throw new Error('useRolloutly must be used within RolloutlyProvider');\n }\n\n const getFlag = useCallback(\n <T extends FlagValue = FlagValue>(key: string): T | undefined => {\n return client.getFlag<T>(key);\n },\n [client],\n );\n\n const isEnabled = useCallback(\n (key: string): boolean => {\n return client.isEnabled(key);\n },\n [client],\n );\n\n const identify = useCallback(\n async (user: UserContext): Promise<void> => {\n return client.identify(user);\n },\n [client],\n );\n\n const reset = useCallback(async (): Promise<void> => {\n return client.reset();\n }, [client]);\n\n return {\n isLoading,\n isError,\n error,\n getFlag,\n isEnabled,\n identify,\n reset,\n };\n}\n\n/**\n * Hook to get a single flag value with real-time updates\n */\nexport function useFlag<T extends FlagValue = FlagValue>(\n key: string,\n): T | undefined {\n const client = useRolloutlyClient();\n\n const getSnapshot = useCallback((): T | undefined => {\n return client.getFlag<T>(key);\n }, [client, key]);\n\n const subscribe = useCallback(\n (onStoreChange: () => void): (() => void) => {\n return client.subscribe(onStoreChange);\n },\n [client],\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Hook to check if a boolean flag is enabled with real-time updates\n */\nexport function useFlagEnabled(key: string): boolean {\n const client = useRolloutlyClient();\n\n const getSnapshot = useCallback((): boolean => {\n return client.isEnabled(key);\n }, [client, key]);\n\n const subscribe = useCallback(\n (onStoreChange: () => void): (() => void) => {\n return client.subscribe(onStoreChange);\n },\n [client],\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Hook to get all flag values with real-time updates\n * Returns an object keyed by flag key with the flag values\n *\n * @example\n * const flags = useFlags();\n * const value = flags['my-flag'];\n *\n * // Or with destructuring:\n * const { 'my-flag': myFlag } = useFlags();\n */\nexport function useFlags(): Record<string, FlagValue | undefined> {\n const client = useRolloutlyClient();\n\n // Use the client's cached flag values to avoid creating new objects\n const getSnapshot = useCallback((): Record<string, FlagValue | undefined> => {\n return client.getFlagValues();\n }, [client]);\n\n const subscribe = useCallback(\n (onStoreChange: () => void): (() => void) => {\n return client.subscribe(onStoreChange);\n },\n [client],\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FlagValue, RolloutlyClient } from '@rolloutly/core';
|
|
1
|
+
import { FlagValue, UserContext, RolloutlyClient } from '@rolloutly/core';
|
|
2
2
|
export { Flag, FlagMap, FlagValue } from '@rolloutly/core';
|
|
3
3
|
import { ReactNode } from 'react';
|
|
4
4
|
|
|
@@ -16,6 +16,8 @@ type RolloutlyProviderProps = {
|
|
|
16
16
|
realtimeEnabled?: boolean;
|
|
17
17
|
/** Default flag values */
|
|
18
18
|
defaultFlags?: Record<string, FlagValue>;
|
|
19
|
+
/** User context for targeting rules evaluation */
|
|
20
|
+
user?: UserContext;
|
|
19
21
|
/** Enable debug logging */
|
|
20
22
|
debug?: boolean;
|
|
21
23
|
/** Component to show while loading */
|
|
@@ -35,18 +37,22 @@ type RolloutlyContextValue = {
|
|
|
35
37
|
getFlag: <T extends FlagValue = FlagValue>(key: string) => T | undefined;
|
|
36
38
|
/** Check if a boolean flag is enabled */
|
|
37
39
|
isEnabled: (key: string) => boolean;
|
|
40
|
+
/** Identify the user (update user context) */
|
|
41
|
+
identify: (user: UserContext) => Promise<void>;
|
|
42
|
+
/** Reset user context (e.g., on logout) */
|
|
43
|
+
reset: () => Promise<void>;
|
|
38
44
|
};
|
|
39
45
|
|
|
40
46
|
/**
|
|
41
47
|
* Provider component that initializes Rolloutly and provides context
|
|
42
48
|
*/
|
|
43
|
-
declare function RolloutlyProvider({ token, children, baseUrl, realtimeEnabled, defaultFlags, debug, loadingComponent, }: RolloutlyProviderProps): JSX.Element;
|
|
49
|
+
declare function RolloutlyProvider({ token, children, baseUrl, realtimeEnabled, defaultFlags, user, debug, loadingComponent, }: RolloutlyProviderProps): JSX.Element;
|
|
44
50
|
/**
|
|
45
51
|
* Hook to access the Rolloutly client directly
|
|
46
52
|
*/
|
|
47
53
|
declare function useRolloutlyClient(): RolloutlyClient;
|
|
48
54
|
/**
|
|
49
|
-
* Hook to get Rolloutly status
|
|
55
|
+
* Hook to get Rolloutly status and utilities
|
|
50
56
|
*/
|
|
51
57
|
declare function useRolloutly(): RolloutlyContextValue;
|
|
52
58
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FlagValue, RolloutlyClient } from '@rolloutly/core';
|
|
1
|
+
import { FlagValue, UserContext, RolloutlyClient } from '@rolloutly/core';
|
|
2
2
|
export { Flag, FlagMap, FlagValue } from '@rolloutly/core';
|
|
3
3
|
import { ReactNode } from 'react';
|
|
4
4
|
|
|
@@ -16,6 +16,8 @@ type RolloutlyProviderProps = {
|
|
|
16
16
|
realtimeEnabled?: boolean;
|
|
17
17
|
/** Default flag values */
|
|
18
18
|
defaultFlags?: Record<string, FlagValue>;
|
|
19
|
+
/** User context for targeting rules evaluation */
|
|
20
|
+
user?: UserContext;
|
|
19
21
|
/** Enable debug logging */
|
|
20
22
|
debug?: boolean;
|
|
21
23
|
/** Component to show while loading */
|
|
@@ -35,18 +37,22 @@ type RolloutlyContextValue = {
|
|
|
35
37
|
getFlag: <T extends FlagValue = FlagValue>(key: string) => T | undefined;
|
|
36
38
|
/** Check if a boolean flag is enabled */
|
|
37
39
|
isEnabled: (key: string) => boolean;
|
|
40
|
+
/** Identify the user (update user context) */
|
|
41
|
+
identify: (user: UserContext) => Promise<void>;
|
|
42
|
+
/** Reset user context (e.g., on logout) */
|
|
43
|
+
reset: () => Promise<void>;
|
|
38
44
|
};
|
|
39
45
|
|
|
40
46
|
/**
|
|
41
47
|
* Provider component that initializes Rolloutly and provides context
|
|
42
48
|
*/
|
|
43
|
-
declare function RolloutlyProvider({ token, children, baseUrl, realtimeEnabled, defaultFlags, debug, loadingComponent, }: RolloutlyProviderProps): JSX.Element;
|
|
49
|
+
declare function RolloutlyProvider({ token, children, baseUrl, realtimeEnabled, defaultFlags, user, debug, loadingComponent, }: RolloutlyProviderProps): JSX.Element;
|
|
44
50
|
/**
|
|
45
51
|
* Hook to access the Rolloutly client directly
|
|
46
52
|
*/
|
|
47
53
|
declare function useRolloutlyClient(): RolloutlyClient;
|
|
48
54
|
/**
|
|
49
|
-
* Hook to get Rolloutly status
|
|
55
|
+
* Hook to get Rolloutly status and utilities
|
|
50
56
|
*/
|
|
51
57
|
declare function useRolloutly(): RolloutlyContextValue;
|
|
52
58
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RolloutlyClient } from '@rolloutly/core';
|
|
2
|
-
import { createContext, useState, useMemo, useEffect, useContext, useCallback, useSyncExternalStore } from 'react';
|
|
2
|
+
import { createContext, useState, useRef, useMemo, useEffect, useContext, useCallback, useSyncExternalStore } from 'react';
|
|
3
3
|
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
4
4
|
|
|
5
5
|
// src/provider.tsx
|
|
@@ -15,21 +15,34 @@ function RolloutlyProvider({
|
|
|
15
15
|
baseUrl,
|
|
16
16
|
realtimeEnabled = true,
|
|
17
17
|
defaultFlags,
|
|
18
|
+
user,
|
|
18
19
|
debug = false,
|
|
19
20
|
loadingComponent
|
|
20
21
|
}) {
|
|
21
22
|
const [isLoading, setIsLoading] = useState(true);
|
|
22
23
|
const [isError, setIsError] = useState(false);
|
|
23
24
|
const [error, setError] = useState(null);
|
|
25
|
+
const prevUserRef = useRef(user);
|
|
24
26
|
const client = useMemo(() => {
|
|
25
27
|
return new RolloutlyClient({
|
|
26
28
|
token,
|
|
27
29
|
baseUrl,
|
|
28
30
|
realtimeEnabled,
|
|
29
31
|
defaultFlags,
|
|
32
|
+
user,
|
|
30
33
|
debug
|
|
31
34
|
});
|
|
32
35
|
}, [token, baseUrl, realtimeEnabled, defaultFlags, debug]);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (prevUserRef.current !== user && client) {
|
|
38
|
+
prevUserRef.current = user;
|
|
39
|
+
if (user) {
|
|
40
|
+
void client.identify(user);
|
|
41
|
+
} else {
|
|
42
|
+
void client.reset();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}, [user, client]);
|
|
33
46
|
useEffect(() => {
|
|
34
47
|
let mounted = true;
|
|
35
48
|
client.waitForInit().then(() => {
|
|
@@ -86,12 +99,23 @@ function useRolloutly() {
|
|
|
86
99
|
},
|
|
87
100
|
[client]
|
|
88
101
|
);
|
|
102
|
+
const identify = useCallback(
|
|
103
|
+
async (user) => {
|
|
104
|
+
return client.identify(user);
|
|
105
|
+
},
|
|
106
|
+
[client]
|
|
107
|
+
);
|
|
108
|
+
const reset = useCallback(async () => {
|
|
109
|
+
return client.reset();
|
|
110
|
+
}, [client]);
|
|
89
111
|
return {
|
|
90
112
|
isLoading,
|
|
91
113
|
isError,
|
|
92
114
|
error,
|
|
93
115
|
getFlag,
|
|
94
|
-
isEnabled
|
|
116
|
+
isEnabled,
|
|
117
|
+
identify,
|
|
118
|
+
reset
|
|
95
119
|
};
|
|
96
120
|
}
|
|
97
121
|
function useFlag(key) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/provider.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["'use client';\n\nimport { RolloutlyClient, type FlagValue } from '@rolloutly/core';\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n useSyncExternalStore,\n} from 'react';\n\nimport type { RolloutlyContextValue, RolloutlyProviderProps } from './types';\n\nconst RolloutlyContext = createContext<{\n client: RolloutlyClient | null;\n isLoading: boolean;\n isError: boolean;\n error: Error | null;\n}>({\n client: null,\n isLoading: true,\n isError: false,\n error: null,\n});\n\n/**\n * Provider component that initializes Rolloutly and provides context\n */\nexport function RolloutlyProvider({\n token,\n children,\n baseUrl,\n realtimeEnabled = true,\n defaultFlags,\n debug = false,\n loadingComponent,\n}: RolloutlyProviderProps): JSX.Element {\n const [isLoading, setIsLoading] = useState(true);\n const [isError, setIsError] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const client = useMemo(() => {\n return new RolloutlyClient({\n token,\n baseUrl,\n realtimeEnabled,\n defaultFlags,\n debug,\n });\n }, [token, baseUrl, realtimeEnabled, defaultFlags, debug]);\n\n useEffect(() => {\n let mounted = true;\n\n client\n .waitForInit()\n .then(() => {\n if (mounted) {\n setIsLoading(false);\n }\n })\n .catch((err) => {\n if (mounted) {\n setIsError(true);\n setError(err instanceof Error ? err : new Error(String(err)));\n setIsLoading(false);\n }\n });\n\n return () => {\n mounted = false;\n client.close();\n };\n }, [client]);\n\n const contextValue = useMemo(\n () => ({\n client,\n isLoading,\n isError,\n error,\n }),\n [client, isLoading, isError, error],\n );\n\n if (isLoading && loadingComponent) {\n return <>{loadingComponent}</>;\n }\n\n return (\n <RolloutlyContext.Provider value={contextValue}>\n {children}\n </RolloutlyContext.Provider>\n );\n}\n\n/**\n * Hook to access the Rolloutly client directly\n */\nexport function useRolloutlyClient(): RolloutlyClient {\n const { client } = useContext(RolloutlyContext);\n\n if (!client) {\n throw new Error('useRolloutlyClient must be used within RolloutlyProvider');\n }\n\n return client;\n}\n\n/**\n * Hook to get Rolloutly status\n */\nexport function useRolloutly(): RolloutlyContextValue {\n const { client, isLoading, isError, error } = useContext(RolloutlyContext);\n\n if (!client) {\n throw new Error('useRolloutly must be used within RolloutlyProvider');\n }\n\n const getFlag = useCallback(\n <T extends FlagValue = FlagValue>(key: string): T | undefined => {\n return client.getFlag<T>(key);\n },\n [client],\n );\n\n const isEnabled = useCallback(\n (key: string): boolean => {\n return client.isEnabled(key);\n },\n [client],\n );\n\n return {\n isLoading,\n isError,\n error,\n getFlag,\n isEnabled,\n };\n}\n\n/**\n * Hook to get a single flag value with real-time updates\n */\nexport function useFlag<T extends FlagValue = FlagValue>(\n key: string,\n): T | undefined {\n const client = useRolloutlyClient();\n\n const getSnapshot = useCallback((): T | undefined => {\n return client.getFlag<T>(key);\n }, [client, key]);\n\n const subscribe = useCallback(\n (onStoreChange: () => void): (() => void) => {\n return client.subscribe(onStoreChange);\n },\n [client],\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Hook to check if a boolean flag is enabled with real-time updates\n */\nexport function useFlagEnabled(key: string): boolean {\n const client = useRolloutlyClient();\n\n const getSnapshot = useCallback((): boolean => {\n return client.isEnabled(key);\n }, [client, key]);\n\n const subscribe = useCallback(\n (onStoreChange: () => void): (() => void) => {\n return client.subscribe(onStoreChange);\n },\n [client],\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Hook to get all flag values with real-time updates\n * Returns an object keyed by flag key with the flag values\n *\n * @example\n * const flags = useFlags();\n * const value = flags['my-flag'];\n *\n * // Or with destructuring:\n * const { 'my-flag': myFlag } = useFlags();\n */\nexport function useFlags(): Record<string, FlagValue | undefined> {\n const client = useRolloutlyClient();\n\n // Use the client's cached flag values to avoid creating new objects\n const getSnapshot = useCallback((): Record<string, FlagValue | undefined> => {\n return client.getFlagValues();\n }, [client]);\n\n const subscribe = useCallback(\n (onStoreChange: () => void): (() => void) => {\n return client.subscribe(onStoreChange);\n },\n [client],\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/provider.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["'use client';\n\nimport { RolloutlyClient, type FlagValue, type UserContext } from '@rolloutly/core';\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\n\nimport type { RolloutlyContextValue, RolloutlyProviderProps } from './types';\n\nconst RolloutlyContext = createContext<{\n client: RolloutlyClient | null;\n isLoading: boolean;\n isError: boolean;\n error: Error | null;\n}>({\n client: null,\n isLoading: true,\n isError: false,\n error: null,\n});\n\n/**\n * Provider component that initializes Rolloutly and provides context\n */\nexport function RolloutlyProvider({\n token,\n children,\n baseUrl,\n realtimeEnabled = true,\n defaultFlags,\n user,\n debug = false,\n loadingComponent,\n}: RolloutlyProviderProps): JSX.Element {\n const [isLoading, setIsLoading] = useState(true);\n const [isError, setIsError] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Track if user changed to re-identify\n const prevUserRef = useRef<UserContext | undefined>(user);\n\n // eslint-disable-next-line react-hooks/exhaustive-deps -- user changes handled via identify() in useEffect\n const client = useMemo(() => {\n return new RolloutlyClient({\n token,\n baseUrl,\n realtimeEnabled,\n defaultFlags,\n user,\n debug,\n });\n }, [token, baseUrl, realtimeEnabled, defaultFlags, debug]);\n\n // Handle user context changes\n useEffect(() => {\n if (prevUserRef.current !== user && client) {\n prevUserRef.current = user;\n\n if (user) {\n void client.identify(user);\n } else {\n void client.reset();\n }\n }\n }, [user, client]);\n\n useEffect(() => {\n let mounted = true;\n\n client\n .waitForInit()\n .then(() => {\n if (mounted) {\n setIsLoading(false);\n }\n })\n .catch((err) => {\n if (mounted) {\n setIsError(true);\n setError(err instanceof Error ? err : new Error(String(err)));\n setIsLoading(false);\n }\n });\n\n return () => {\n mounted = false;\n client.close();\n };\n }, [client]);\n\n const contextValue = useMemo(\n () => ({\n client,\n isLoading,\n isError,\n error,\n }),\n [client, isLoading, isError, error],\n );\n\n if (isLoading && loadingComponent) {\n return <>{loadingComponent}</>;\n }\n\n return (\n <RolloutlyContext.Provider value={contextValue}>\n {children}\n </RolloutlyContext.Provider>\n );\n}\n\n/**\n * Hook to access the Rolloutly client directly\n */\nexport function useRolloutlyClient(): RolloutlyClient {\n const { client } = useContext(RolloutlyContext);\n\n if (!client) {\n throw new Error('useRolloutlyClient must be used within RolloutlyProvider');\n }\n\n return client;\n}\n\n/**\n * Hook to get Rolloutly status and utilities\n */\nexport function useRolloutly(): RolloutlyContextValue {\n const { client, isLoading, isError, error } = useContext(RolloutlyContext);\n\n if (!client) {\n throw new Error('useRolloutly must be used within RolloutlyProvider');\n }\n\n const getFlag = useCallback(\n <T extends FlagValue = FlagValue>(key: string): T | undefined => {\n return client.getFlag<T>(key);\n },\n [client],\n );\n\n const isEnabled = useCallback(\n (key: string): boolean => {\n return client.isEnabled(key);\n },\n [client],\n );\n\n const identify = useCallback(\n async (user: UserContext): Promise<void> => {\n return client.identify(user);\n },\n [client],\n );\n\n const reset = useCallback(async (): Promise<void> => {\n return client.reset();\n }, [client]);\n\n return {\n isLoading,\n isError,\n error,\n getFlag,\n isEnabled,\n identify,\n reset,\n };\n}\n\n/**\n * Hook to get a single flag value with real-time updates\n */\nexport function useFlag<T extends FlagValue = FlagValue>(\n key: string,\n): T | undefined {\n const client = useRolloutlyClient();\n\n const getSnapshot = useCallback((): T | undefined => {\n return client.getFlag<T>(key);\n }, [client, key]);\n\n const subscribe = useCallback(\n (onStoreChange: () => void): (() => void) => {\n return client.subscribe(onStoreChange);\n },\n [client],\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Hook to check if a boolean flag is enabled with real-time updates\n */\nexport function useFlagEnabled(key: string): boolean {\n const client = useRolloutlyClient();\n\n const getSnapshot = useCallback((): boolean => {\n return client.isEnabled(key);\n }, [client, key]);\n\n const subscribe = useCallback(\n (onStoreChange: () => void): (() => void) => {\n return client.subscribe(onStoreChange);\n },\n [client],\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Hook to get all flag values with real-time updates\n * Returns an object keyed by flag key with the flag values\n *\n * @example\n * const flags = useFlags();\n * const value = flags['my-flag'];\n *\n * // Or with destructuring:\n * const { 'my-flag': myFlag } = useFlags();\n */\nexport function useFlags(): Record<string, FlagValue | undefined> {\n const client = useRolloutlyClient();\n\n // Use the client's cached flag values to avoid creating new objects\n const getSnapshot = useCallback((): Record<string, FlagValue | undefined> => {\n return client.getFlagValues();\n }, [client]);\n\n const subscribe = useCallback(\n (onStoreChange: () => void): (() => void) => {\n return client.subscribe(onStoreChange);\n },\n [client],\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rolloutly/react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Rolloutly feature flags SDK - React hooks and provider",
|
|
5
5
|
"author": "Kevin Beltrão",
|
|
6
6
|
"license": "MIT",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"clean": "rm -rf dist"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@rolloutly/core": "
|
|
47
|
+
"@rolloutly/core": "workspace:*"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"react": ">=18.0.0"
|