@openfeature/react-sdk 0.0.6-experimental โ 0.1.1-experimental
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 +73 -17
- package/dist/cjs/index.js +44 -30
- package/dist/cjs/index.js.map +2 -2
- package/dist/esm/index.js +44 -33
- package/dist/esm/index.js.map +2 -2
- package/dist/types.d.ts +75 -996
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -15,26 +15,66 @@
|
|
|
15
15
|
<a href="https://github.com/open-feature/spec/releases/tag/v0.7.0">
|
|
16
16
|
<img alt="Specification" src="https://img.shields.io/static/v1?label=specification&message=v0.7.0&color=yellow&style=for-the-badge" />
|
|
17
17
|
</a>
|
|
18
|
+
<!-- x-release-please-start-version -->
|
|
19
|
+
<a href="https://github.com/open-feature/js-sdk/releases/tag/react-sdk-v0.1.1-experimental">
|
|
20
|
+
<img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v0.1.1-experimental&color=blue&style=for-the-badge" />
|
|
21
|
+
</a>
|
|
22
|
+
<!-- x-release-please-end -->
|
|
18
23
|
<br/>
|
|
19
24
|
<a href="https://codecov.io/gh/open-feature/js-sdk">
|
|
20
25
|
<img alt="codecov" src="https://codecov.io/gh/open-feature/js-sdk/branch/main/graph/badge.svg?token=3DC5XOEHMY" />
|
|
21
26
|
</a>
|
|
27
|
+
<a href="https://www.npmjs.com/package/@openfeature/react-sdk">
|
|
28
|
+
<img alt="NPM Download" src="https://img.shields.io/npm/dm/%40openfeature%2Freact-sdk" />
|
|
29
|
+
</a>
|
|
22
30
|
</p>
|
|
23
31
|
<!-- x-hide-in-docs-start -->
|
|
24
32
|
|
|
25
|
-
[OpenFeature](https://openfeature.dev) is an open specification that provides a vendor-agnostic, community-driven API for feature flagging that works with your favorite feature flag management tool.
|
|
33
|
+
[OpenFeature](https://openfeature.dev) is an open specification that provides a vendor-agnostic, community-driven API for feature flagging that works with your favorite feature flag management tool or in-house solution.
|
|
34
|
+
|
|
35
|
+
<!-- x-hide-in-docs-end -->
|
|
26
36
|
|
|
27
37
|
๐งช This SDK is experimental.
|
|
28
38
|
|
|
29
|
-
##
|
|
39
|
+
## Overview
|
|
40
|
+
|
|
41
|
+
The OpenFeature React SDK adds React-specific functionality to the [OpenFeature Web SDK](https://openfeature.dev/docs/reference/technologies/client/web).
|
|
42
|
+
|
|
43
|
+
In addition to the feature provided by the [web sdk](https://openfeature.dev/docs/reference/technologies/client/web), capabilities include:
|
|
44
|
+
|
|
45
|
+
- [Multiple Providers and Scoping](#multiple-providers-and-scoping)
|
|
46
|
+
- [Re-rendering with Context Changes](#re-rendering-with-context-changes)
|
|
47
|
+
- [Re-rendering with Flag Configuration Changes](#re-rendering-with-flag-configuration-changes)
|
|
48
|
+
- [Suspense Support](#suspense-support)
|
|
30
49
|
|
|
31
|
-
|
|
50
|
+
## ๐ Quick start
|
|
51
|
+
|
|
52
|
+
### Requirements
|
|
53
|
+
|
|
54
|
+
- ES2022-compatible web browser (Chrome, Edge, Firefox, etc)
|
|
55
|
+
- React version 16.8+
|
|
56
|
+
|
|
57
|
+
### Install
|
|
58
|
+
|
|
59
|
+
#### npm
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
npm install --save @openfeature/react-sdk
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
#### Required peer dependencies
|
|
66
|
+
|
|
67
|
+
The following list contains the peer dependencies of `@openfeature/react-sdk` with its expected and compatible versions:
|
|
68
|
+
|
|
69
|
+
* `@openfeature/web-sdk`: >=0.4.10
|
|
70
|
+
* `react`: >=16.8.0
|
|
71
|
+
|
|
72
|
+
### Usage
|
|
73
|
+
|
|
74
|
+
The example below shows how to use the `OpenFeatureProvider` with OpenFeature's `InMemoryProvider`.
|
|
32
75
|
|
|
33
76
|
```tsx
|
|
34
|
-
import
|
|
35
|
-
import './App.css';
|
|
36
|
-
import { OpenFeatureProvider, useFeatureFlag, OpenFeature } from '@openfeature/react-sdk';
|
|
37
|
-
import { FlagdWebProvider } from '@openfeature/flagd-web-provider';
|
|
77
|
+
import { EvaluationContext, OpenFeatureProvider, useBooleanFlagValue, useBooleanFlagDetails, OpenFeature, InMemoryProvider } from '@openfeature/react-sdk';
|
|
38
78
|
|
|
39
79
|
const flagConfig = {
|
|
40
80
|
'new-message': {
|
|
@@ -64,12 +104,11 @@ function App() {
|
|
|
64
104
|
}
|
|
65
105
|
|
|
66
106
|
function Page() {
|
|
67
|
-
const
|
|
107
|
+
const newMessage = useBooleanFlagValue('new-message', false);
|
|
68
108
|
return (
|
|
69
109
|
<div className="App">
|
|
70
110
|
<header className="App-header">
|
|
71
|
-
<
|
|
72
|
-
{booleanFlag.value ? <p>Welcome to this OpenFeature-enabled React app!</p> : <p>Welcome to this React app.</p>}
|
|
111
|
+
{newMessage ? <p>Welcome to this OpenFeature-enabled React app!</p> : <p>Welcome to this React app.</p>}
|
|
73
112
|
</header>
|
|
74
113
|
</div>
|
|
75
114
|
)
|
|
@@ -78,6 +117,19 @@ function Page() {
|
|
|
78
117
|
export default App;
|
|
79
118
|
```
|
|
80
119
|
|
|
120
|
+
You use the detailed flag evaluation hooks to evaluate the flag and get additional information about the flag and the evaluation.
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
import { useBooleanFlagDetails } from '@openfeature/react-sdk';
|
|
124
|
+
|
|
125
|
+
const {
|
|
126
|
+
value,
|
|
127
|
+
variant,
|
|
128
|
+
reason,
|
|
129
|
+
flagMetadata
|
|
130
|
+
} = useBooleanFlagDetails('new-message', false);
|
|
131
|
+
```
|
|
132
|
+
|
|
81
133
|
### Multiple Providers and Scoping
|
|
82
134
|
|
|
83
135
|
Multiple providers and scoped clients can be configured by passing a `clientName` to the `OpenFeatureProvider`:
|
|
@@ -103,11 +155,11 @@ OpenFeature.getClient('myClient');
|
|
|
103
155
|
|
|
104
156
|
By default, if the OpenFeature [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context) is modified, components will be re-rendered.
|
|
105
157
|
This is useful in cases where flag values are dependant on user-attributes or other application state (user logged in, items in card, etc).
|
|
106
|
-
You can disable this feature in the
|
|
158
|
+
You can disable this feature in the hook options:
|
|
107
159
|
|
|
108
160
|
```tsx
|
|
109
161
|
function Page() {
|
|
110
|
-
const
|
|
162
|
+
const newMessage = useBooleanFlagValue('new-message', false, { updateOnContextChanged: false });
|
|
111
163
|
return (
|
|
112
164
|
<MyComponents></MyComponents>
|
|
113
165
|
)
|
|
@@ -120,11 +172,11 @@ For more information about how evaluation context works in the React SDK, see th
|
|
|
120
172
|
|
|
121
173
|
By default, if the underlying provider emits a `ConfigurationChanged` event, components will be re-rendered.
|
|
122
174
|
This is useful if you want your UI to immediately reflect changes in the backend flag configuration.
|
|
123
|
-
You can disable this feature in the
|
|
175
|
+
You can disable this feature in the hook options:
|
|
124
176
|
|
|
125
177
|
```tsx
|
|
126
178
|
function Page() {
|
|
127
|
-
const
|
|
179
|
+
const newMessage = useBooleanFlagValue('new-message', false, { updateOnConfigurationChanged: false });
|
|
128
180
|
return (
|
|
129
181
|
<MyComponents></MyComponents>
|
|
130
182
|
)
|
|
@@ -151,11 +203,11 @@ function Content() {
|
|
|
151
203
|
|
|
152
204
|
function Message() {
|
|
153
205
|
// component to render after READY.
|
|
154
|
-
const
|
|
206
|
+
const newMessage = useBooleanFlagValue('new-message', false);
|
|
155
207
|
|
|
156
208
|
return (
|
|
157
209
|
<>
|
|
158
|
-
{
|
|
210
|
+
{newMessage ? (
|
|
159
211
|
<p>Welcome to this OpenFeature-enabled React app!</p>
|
|
160
212
|
) : (
|
|
161
213
|
<p>Welcome to this plain old React app!</p>
|
|
@@ -168,4 +220,8 @@ function Fallback() {
|
|
|
168
220
|
// component to render before READY.
|
|
169
221
|
return <p>Waiting for provider to be ready...</p>;
|
|
170
222
|
}
|
|
171
|
-
```
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Resources
|
|
226
|
+
|
|
227
|
+
- [Example repo](https://github.com/open-feature/react-test-app)
|
package/dist/cjs/index.js
CHANGED
|
@@ -3,22 +3,8 @@ var __create = Object.create;
|
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
7
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
10
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
11
|
-
var __spreadValues = (a, b) => {
|
|
12
|
-
for (var prop in b || (b = {}))
|
|
13
|
-
if (__hasOwnProp.call(b, prop))
|
|
14
|
-
__defNormalProp(a, prop, b[prop]);
|
|
15
|
-
if (__getOwnPropSymbols)
|
|
16
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
17
|
-
if (__propIsEnum.call(b, prop))
|
|
18
|
-
__defNormalProp(a, prop, b[prop]);
|
|
19
|
-
}
|
|
20
|
-
return a;
|
|
21
|
-
};
|
|
22
8
|
var __export = (target, all) => {
|
|
23
9
|
for (var name in all)
|
|
24
10
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -46,8 +32,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
46
32
|
var src_exports = {};
|
|
47
33
|
__export(src_exports, {
|
|
48
34
|
OpenFeatureProvider: () => OpenFeatureProvider,
|
|
49
|
-
|
|
50
|
-
|
|
35
|
+
useBooleanFlagDetails: () => useBooleanFlagDetails,
|
|
36
|
+
useBooleanFlagValue: () => useBooleanFlagValue,
|
|
37
|
+
useNumberFlagDetails: () => useNumberFlagDetails,
|
|
38
|
+
useNumberFlagValue: () => useNumberFlagValue,
|
|
39
|
+
useObjectFlagDetails: () => useObjectFlagDetails,
|
|
40
|
+
useObjectFlagValue: () => useObjectFlagValue,
|
|
41
|
+
useOpenFeatureClient: () => useOpenFeatureClient,
|
|
42
|
+
useStringFlagDetails: () => useStringFlagDetails,
|
|
43
|
+
useStringFlagValue: () => useStringFlagValue
|
|
51
44
|
});
|
|
52
45
|
module.exports = __toCommonJS(src_exports);
|
|
53
46
|
|
|
@@ -81,8 +74,40 @@ var DEFAULT_OPTIONS = {
|
|
|
81
74
|
updateOnConfigurationChanged: true,
|
|
82
75
|
suspend: true
|
|
83
76
|
};
|
|
84
|
-
function
|
|
85
|
-
|
|
77
|
+
function useBooleanFlagValue(flagKey, defaultValue, options) {
|
|
78
|
+
return useBooleanFlagDetails(flagKey, defaultValue, options).value;
|
|
79
|
+
}
|
|
80
|
+
function useBooleanFlagDetails(flagKey, defaultValue, options) {
|
|
81
|
+
return attachHandlersAndResolve(flagKey, defaultValue, (client) => {
|
|
82
|
+
return client.getBooleanDetails;
|
|
83
|
+
}, options);
|
|
84
|
+
}
|
|
85
|
+
function useStringFlagValue(flagKey, defaultValue, options) {
|
|
86
|
+
return useStringFlagDetails(flagKey, defaultValue, options).value;
|
|
87
|
+
}
|
|
88
|
+
function useStringFlagDetails(flagKey, defaultValue, options) {
|
|
89
|
+
return attachHandlersAndResolve(flagKey, defaultValue, (client) => {
|
|
90
|
+
return client.getStringDetails;
|
|
91
|
+
}, options);
|
|
92
|
+
}
|
|
93
|
+
function useNumberFlagValue(flagKey, defaultValue, options) {
|
|
94
|
+
return useNumberFlagDetails(flagKey, defaultValue, options).value;
|
|
95
|
+
}
|
|
96
|
+
function useNumberFlagDetails(flagKey, defaultValue, options) {
|
|
97
|
+
return attachHandlersAndResolve(flagKey, defaultValue, (client) => {
|
|
98
|
+
return client.getNumberDetails;
|
|
99
|
+
}, options);
|
|
100
|
+
}
|
|
101
|
+
function useObjectFlagValue(flagKey, defaultValue, options) {
|
|
102
|
+
return useObjectFlagDetails(flagKey, defaultValue, options).value;
|
|
103
|
+
}
|
|
104
|
+
function useObjectFlagDetails(flagKey, defaultValue, options) {
|
|
105
|
+
return attachHandlersAndResolve(flagKey, defaultValue, (client) => {
|
|
106
|
+
return client.getObjectDetails;
|
|
107
|
+
}, options);
|
|
108
|
+
}
|
|
109
|
+
function attachHandlersAndResolve(flagKey, defaultValue, resolver, options) {
|
|
110
|
+
const defaultedOptions = { ...DEFAULT_OPTIONS, ...options };
|
|
86
111
|
const [, updateState] = (0, import_react.useState)();
|
|
87
112
|
const forceUpdate = () => {
|
|
88
113
|
updateState({});
|
|
@@ -107,18 +132,7 @@ function useFeatureFlag(flagKey, defaultValue, options) {
|
|
|
107
132
|
client.removeHandler(import_web_sdk2.ProviderEvents.ConfigurationChanged, forceUpdate);
|
|
108
133
|
};
|
|
109
134
|
}, [client]);
|
|
110
|
-
return
|
|
111
|
-
}
|
|
112
|
-
function getFlag(client, flagKey, defaultValue) {
|
|
113
|
-
if (typeof defaultValue === "boolean") {
|
|
114
|
-
return client.getBooleanDetails(flagKey, defaultValue);
|
|
115
|
-
} else if (typeof defaultValue === "string") {
|
|
116
|
-
return client.getStringDetails(flagKey, defaultValue);
|
|
117
|
-
} else if (typeof defaultValue === "number") {
|
|
118
|
-
return client.getNumberDetails(flagKey, defaultValue);
|
|
119
|
-
} else {
|
|
120
|
-
return client.getObjectDetails(flagKey, defaultValue);
|
|
121
|
-
}
|
|
135
|
+
return resolver(client).call(client, flagKey, defaultValue);
|
|
122
136
|
}
|
|
123
137
|
function suspend(client, updateState) {
|
|
124
138
|
let suspendResolver;
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/index.ts", "../../src/use-feature-flag.ts", "../../src/provider.tsx"],
|
|
4
|
-
"sourcesContent": ["export * from './use-feature-flag';\nexport * from './provider';\n// re-export the web-sdk so consumers can access that API from the react-sdk\nexport * from '@openfeature/web-sdk';\n", "import { Client, EvaluationDetails, FlagEvaluationOptions, FlagValue, ProviderEvents, ProviderStatus } from '@openfeature/web-sdk';\nimport { Dispatch, SetStateAction, useEffect, useState } from 'react';\nimport { useOpenFeatureClient } from './provider';\n\ntype ReactFlagEvaluationOptions = {\n /**\n * Suspend flag evaluations while the provider is not ready.\n * Set to false if you don't want to use React Suspense API.\n * Defaults to true.\n */\n suspend?: boolean,\n /**\n * Update the component if the provider emits a ConfigurationChanged event.\n * Set to false to prevent components from re-rendering when flag value changes\n * are received by the associated provider.\n * Defaults to true.\n */\n updateOnConfigurationChanged?: boolean,\n /**\n * Update the component when the OpenFeature context changes.\n * Set to false to prevent components from re-rendering when attributes which \n * may be factors in flag evaluation change.\n * Defaults to true.\n */\n updateOnContextChanged?: boolean,\n} & FlagEvaluationOptions;\n\nconst DEFAULT_OPTIONS: ReactFlagEvaluationOptions = {\n updateOnContextChanged: true,\n updateOnConfigurationChanged: true,\n suspend: true,\n};\n\nenum SuspendState {\n Pending,\n Success,\n Error\n}\n\n/**\n * Evaluates a feature flag, returning evaluation details.\n * @param {string}flagKey the flag identifier\n * @param {T} defaultValue the default value\n * @param {ReactFlagEvaluationOptions} options options for this evaluation\n * @template T flag type\n * @returns { EvaluationDetails<T>} a EvaluationDetails object for this evaluation\n */\nexport function useFeatureFlag<T extends FlagValue>(flagKey: string, defaultValue: T, options?: ReactFlagEvaluationOptions): EvaluationDetails<T> {\n const defaultedOptions = { ...DEFAULT_OPTIONS, ...options };\n const [, updateState] = useState<object | undefined>();\n const forceUpdate = () => {\n updateState({});\n };\n const client = useOpenFeatureClient();\n\n useEffect(() => {\n\n if (client.providerStatus !== ProviderStatus.READY) {\n // update when the provider is ready\n client.addHandler(ProviderEvents.Ready, forceUpdate);\n if (defaultedOptions.suspend) {\n suspend(client, updateState);\n }\n }\n\n if (defaultedOptions.updateOnContextChanged) {\n // update when the context changes\n client.addHandler(ProviderEvents.ContextChanged, forceUpdate);\n }\n\n if (defaultedOptions.updateOnConfigurationChanged) {\n // update when the provider configuration changes\n client.addHandler(ProviderEvents.ConfigurationChanged, forceUpdate);\n }\n return () => {\n // cleanup the handlers (we can do this unconditionally with no impact)\n client.removeHandler(ProviderEvents.Ready, forceUpdate);\n client.removeHandler(ProviderEvents.ContextChanged, forceUpdate);\n client.removeHandler(ProviderEvents.ConfigurationChanged, forceUpdate);\n };\n }, [client]);\n\n return getFlag(client, flagKey, defaultValue);\n}\n\nfunction getFlag<T extends FlagValue>(client: Client, flagKey: string, defaultValue: T): EvaluationDetails<T> {\n if (typeof defaultValue === 'boolean') {\n return client.getBooleanDetails(flagKey, defaultValue) as EvaluationDetails<T>;\n } else if (typeof defaultValue === 'string') {\n return client.getStringDetails(flagKey, defaultValue) as EvaluationDetails<T>;\n } else if (typeof defaultValue === 'number') {\n return client.getNumberDetails(flagKey, defaultValue) as EvaluationDetails<T>;\n } else {\n return client.getObjectDetails(flagKey, defaultValue) as EvaluationDetails<T>;\n }\n}\n\n/**\n * Suspend function. If this runs, components using the calling hook will be suspended.\n * @param {Client} client the OpenFeature client\n * @param {Function} updateState the state update function\n */\nfunction suspend(client: Client, updateState: Dispatch<SetStateAction<object | undefined>>) {\n let suspendResolver: () => void;\n let suspendRejecter: () => void;\n const suspendPromise = new Promise<void>((resolve) => {\n suspendResolver = () => {\n resolve();\n client.removeHandler(ProviderEvents.Ready, suspendResolver); // remove handler once it's run\n };\n suspendRejecter = () => {\n resolve(); // we still resolve here, since we don't want to throw errors\n client.removeHandler(ProviderEvents.Error, suspendRejecter); // remove handler once it's run\n };\n client.addHandler(ProviderEvents.Ready, suspendResolver);\n client.addHandler(ProviderEvents.Error, suspendRejecter);\n });\n updateState(suspenseWrapper(suspendPromise));\n}\n\n/**\n * Promise wrapper that throws unresolved promises to support React suspense.\n * @param {Promise<T>} promise to wrap\n * @template T flag type\n * @returns {Function} suspense-compliant lambda\n */\nfunction suspenseWrapper <T>(promise: Promise<T>) {\n let status: SuspendState = SuspendState.Pending;\n let result: T;\n\n const suspended = promise.then(\n (value) => {\n status = SuspendState.Success;\n result = value;\n },\n (error) => {\n status = SuspendState.Error;\n result = error;\n }\n );\n\n return () => {\n switch (status) {\n case SuspendState.Pending:\n throw suspended;\n case SuspendState.Success:\n return result;\n case SuspendState.Error:\n throw result;\n default:\n throw new Error('Suspending promise is in an unknown state.');\n }\n };\n};", "import * as React from 'react';\nimport { Client, OpenFeature } from '@openfeature/web-sdk';\n\ntype ClientOrClientName =\n | {\n /**\n * The name of the client.\n * @see OpenFeature.setProvider() and overloads.\n */\n clientName: string;\n /**\n * OpenFeature client to use.\n */\n client?: never;\n }\n | {\n /**\n * OpenFeature client to use.\n */\n client: Client;\n /**\n * The name of the client.\n * @see OpenFeature.setProvider() and overloads.\n */\n clientName?: never;\n };\n\ntype ProviderProps = {\n children?: React.ReactNode;\n} & ClientOrClientName;\n\nconst Context = React.createContext<Client | undefined>(undefined);\n\nexport const OpenFeatureProvider = ({ client, clientName, children }: ProviderProps) => {\n if (!client) {\n client = OpenFeature.getClient(clientName);\n }\n\n return <Context.Provider value={client}>{children}</Context.Provider>;\n};\n\nexport const useOpenFeatureClient = () => {\n const client = React.useContext(Context);\n\n if (!client) {\n throw new Error(\n 'No OpenFeature client available - components using OpenFeature must be wrapped with an <OpenFeatureProvider>'\n );\n }\n\n return client;\n};\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["export * from './use-feature-flag';\nexport * from './provider';\n// re-export the web-sdk so consumers can access that API from the react-sdk\nexport * from '@openfeature/web-sdk';\n", "import { Client, EvaluationDetails, FlagEvaluationOptions, FlagValue, JsonValue, ProviderEvents, ProviderStatus } from '@openfeature/web-sdk';\nimport { Dispatch, SetStateAction, useEffect, useState } from 'react';\nimport { useOpenFeatureClient } from './provider';\n\ntype ReactFlagEvaluationOptions = {\n /**\n * Suspend flag evaluations while the provider is not ready.\n * Set to false if you don't want to use React Suspense API.\n * Defaults to true.\n */\n suspend?: boolean,\n /**\n * Update the component if the provider emits a ConfigurationChanged event.\n * Set to false to prevent components from re-rendering when flag value changes\n * are received by the associated provider.\n * Defaults to true.\n */\n updateOnConfigurationChanged?: boolean,\n /**\n * Update the component when the OpenFeature context changes.\n * Set to false to prevent components from re-rendering when attributes which \n * may be factors in flag evaluation change.\n * Defaults to true.\n */\n updateOnContextChanged?: boolean,\n} & FlagEvaluationOptions;\n\nconst DEFAULT_OPTIONS: ReactFlagEvaluationOptions = {\n updateOnContextChanged: true,\n updateOnConfigurationChanged: true,\n suspend: true,\n};\n\nenum SuspendState {\n Pending,\n Success,\n Error\n}\n\n/**\n * Evaluates a feature flag, returning a boolean.\n * By default, components will re-render when the flag value changes.\n * @param {string} flagKey the flag identifier\n * @param {boolean} defaultValue the default value\n * @param {ReactFlagEvaluationOptions} options options for this evaluation\n * @returns { boolean} a EvaluationDetails object for this evaluation\n */\nexport function useBooleanFlagValue(flagKey: string, defaultValue: boolean, options?: ReactFlagEvaluationOptions): boolean {\n return useBooleanFlagDetails(flagKey, defaultValue, options).value;\n}\n\n/**\n * Evaluates a feature flag, returning evaluation details.\n * By default, components will re-render when the flag value changes.\n * @param {string} flagKey the flag identifier\n * @param {boolean} defaultValue the default value\n * @param {ReactFlagEvaluationOptions} options options for this evaluation\n * @returns { EvaluationDetails<boolean>} a EvaluationDetails object for this evaluation\n */\nexport function useBooleanFlagDetails(flagKey: string, defaultValue: boolean, options?: ReactFlagEvaluationOptions): EvaluationDetails<boolean> {\n return attachHandlersAndResolve(flagKey, defaultValue, (client) => {\n return client.getBooleanDetails;\n }, options);\n}\n\n/**\n * Evaluates a feature flag, returning a string.\n * By default, components will re-render when the flag value changes.\n * @param {string} flagKey the flag identifier\n * @template {string} [T=string] A optional generic argument constraining the string\n * @param {T} defaultValue the default value\n * @param {ReactFlagEvaluationOptions} options options for this evaluation\n * @returns { boolean} a EvaluationDetails object for this evaluation\n */\nexport function useStringFlagValue<T extends string = string>(flagKey: string, defaultValue: T, options?: ReactFlagEvaluationOptions): T {\n return useStringFlagDetails(flagKey, defaultValue, options).value;\n}\n\n/**\n * Evaluates a feature flag, returning evaluation details.\n * By default, components will re-render when the flag value changes.\n * @param {string} flagKey the flag identifier\n * @template {string} [T=string] A optional generic argument constraining the string\n * @param {T} defaultValue the default value\n * @param {ReactFlagEvaluationOptions} options options for this evaluation\n * @returns { EvaluationDetails<string>} a EvaluationDetails object for this evaluation\n */\nexport function useStringFlagDetails<T extends string = string>(flagKey: string, defaultValue: T, options?: ReactFlagEvaluationOptions): EvaluationDetails<T> {\n return attachHandlersAndResolve(flagKey, defaultValue, (client) => {\n return client.getStringDetails<T>;\n }, options);\n}\n\n/**\n * Evaluates a feature flag, returning a number.\n * By default, components will re-render when the flag value changes.\n * @param {string} flagKey the flag identifier\n * @template {number} [T=number] A optional generic argument constraining the number\n * @param {T} defaultValue the default value\n * @param {ReactFlagEvaluationOptions} options options for this evaluation\n * @returns { boolean} a EvaluationDetails object for this evaluation\n */\nexport function useNumberFlagValue<T extends number = number>(flagKey: string, defaultValue: T, options?: ReactFlagEvaluationOptions): T {\n return useNumberFlagDetails(flagKey, defaultValue, options).value;\n}\n\n/**\n * Evaluates a feature flag, returning evaluation details.\n * By default, components will re-render when the flag value changes.\n * @param {string} flagKey the flag identifier\n * @template {number} [T=number] A optional generic argument constraining the number\n * @param {T} defaultValue the default value\n * @param {ReactFlagEvaluationOptions} options options for this evaluation\n * @returns { EvaluationDetails<number>} a EvaluationDetails object for this evaluation\n */\nexport function useNumberFlagDetails<T extends number = number>(flagKey: string, defaultValue: T, options?: ReactFlagEvaluationOptions): EvaluationDetails<T> {\n return attachHandlersAndResolve(flagKey, defaultValue, (client) => {\n return client.getNumberDetails<T>;\n }, options);\n}\n\n/**\n * Evaluates a feature flag, returning an object.\n * By default, components will re-render when the flag value changes.\n * @param {string} flagKey the flag identifier\n * @template {JsonValue} [T=JsonValue] A optional generic argument describing the structure\n * @param {T} defaultValue the default value\n * @param {ReactFlagEvaluationOptions} options options for this evaluation\n * @returns { boolean} a EvaluationDetails object for this evaluation\n */\nexport function useObjectFlagValue<T extends JsonValue = JsonValue>(flagKey: string, defaultValue: T, options?: ReactFlagEvaluationOptions): T {\n return useObjectFlagDetails<T>(flagKey, defaultValue, options).value;\n}\n\n/**\n * Evaluates a feature flag, returning evaluation details.\n * By default, components will re-render when the flag value changes.\n * @param {string} flagKey the flag identifier\n * @param {T} defaultValue the default value\n * @template {JsonValue} [T=JsonValue] A optional generic argument describing the structure\n * @param {ReactFlagEvaluationOptions} options options for this evaluation\n * @returns { EvaluationDetails<T>} a EvaluationDetails object for this evaluation\n */\nexport function useObjectFlagDetails<T extends JsonValue = JsonValue>(flagKey: string, defaultValue: T, options?: ReactFlagEvaluationOptions): EvaluationDetails<T> {\n return attachHandlersAndResolve(flagKey, defaultValue, (client) => {\n return client.getObjectDetails<T>;\n }, options);\n}\n\nfunction attachHandlersAndResolve<T extends FlagValue>(flagKey: string, defaultValue: T, resolver: (client: Client) => (flagKey: string, defaultValue: T) => EvaluationDetails<T>, options?: ReactFlagEvaluationOptions): EvaluationDetails<T> {\n const defaultedOptions = { ...DEFAULT_OPTIONS, ...options };\n const [, updateState] = useState<object | undefined>();\n const forceUpdate = () => {\n updateState({});\n };\n const client = useOpenFeatureClient();\n\n useEffect(() => {\n\n if (client.providerStatus !== ProviderStatus.READY) {\n // update when the provider is ready\n client.addHandler(ProviderEvents.Ready, forceUpdate);\n if (defaultedOptions.suspend) {\n suspend(client, updateState);\n }\n }\n\n if (defaultedOptions.updateOnContextChanged) {\n // update when the context changes\n client.addHandler(ProviderEvents.ContextChanged, forceUpdate);\n }\n\n if (defaultedOptions.updateOnConfigurationChanged) {\n // update when the provider configuration changes\n client.addHandler(ProviderEvents.ConfigurationChanged, forceUpdate);\n }\n return () => {\n // cleanup the handlers (we can do this unconditionally with no impact)\n client.removeHandler(ProviderEvents.Ready, forceUpdate);\n client.removeHandler(ProviderEvents.ContextChanged, forceUpdate);\n client.removeHandler(ProviderEvents.ConfigurationChanged, forceUpdate);\n };\n }, [client]);\n\n return resolver(client).call(client, flagKey, defaultValue);\n}\n\n/**\n * Suspend function. If this runs, components using the calling hook will be suspended.\n * @param {Client} client the OpenFeature client\n * @param {Function} updateState the state update function\n */\nfunction suspend(client: Client, updateState: Dispatch<SetStateAction<object | undefined>>) {\n let suspendResolver: () => void;\n let suspendRejecter: () => void;\n const suspendPromise = new Promise<void>((resolve) => {\n suspendResolver = () => {\n resolve();\n client.removeHandler(ProviderEvents.Ready, suspendResolver); // remove handler once it's run\n };\n suspendRejecter = () => {\n resolve(); // we still resolve here, since we don't want to throw errors\n client.removeHandler(ProviderEvents.Error, suspendRejecter); // remove handler once it's run\n };\n client.addHandler(ProviderEvents.Ready, suspendResolver);\n client.addHandler(ProviderEvents.Error, suspendRejecter);\n });\n updateState(suspenseWrapper(suspendPromise));\n}\n\n/**\n * Promise wrapper that throws unresolved promises to support React suspense.\n * @param {Promise<T>} promise to wrap\n * @template T flag type\n * @returns {Function} suspense-compliant lambda\n */\nfunction suspenseWrapper <T>(promise: Promise<T>) {\n let status: SuspendState = SuspendState.Pending;\n let result: T;\n\n const suspended = promise.then(\n (value) => {\n status = SuspendState.Success;\n result = value;\n },\n (error) => {\n status = SuspendState.Error;\n result = error;\n }\n );\n\n return () => {\n switch (status) {\n case SuspendState.Pending:\n throw suspended;\n case SuspendState.Success:\n return result;\n case SuspendState.Error:\n throw result;\n default:\n throw new Error('Suspending promise is in an unknown state.');\n }\n };\n};", "import * as React from 'react';\nimport { Client, OpenFeature } from '@openfeature/web-sdk';\n\ntype ClientOrClientName =\n | {\n /**\n * The name of the client.\n * @see OpenFeature.setProvider() and overloads.\n */\n clientName: string;\n /**\n * OpenFeature client to use.\n */\n client?: never;\n }\n | {\n /**\n * OpenFeature client to use.\n */\n client: Client;\n /**\n * The name of the client.\n * @see OpenFeature.setProvider() and overloads.\n */\n clientName?: never;\n };\n\ntype ProviderProps = {\n children?: React.ReactNode;\n} & ClientOrClientName;\n\nconst Context = React.createContext<Client | undefined>(undefined);\n\nexport const OpenFeatureProvider = ({ client, clientName, children }: ProviderProps) => {\n if (!client) {\n client = OpenFeature.getClient(clientName);\n }\n\n return <Context.Provider value={client}>{children}</Context.Provider>;\n};\n\nexport const useOpenFeatureClient = () => {\n const client = React.useContext(Context);\n\n if (!client) {\n throw new Error(\n 'No OpenFeature client available - components using OpenFeature must be wrapped with an <OpenFeatureProvider>'\n );\n }\n\n return client;\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["import_web_sdk"]
|
|
7
7
|
}
|
package/dist/esm/index.js
CHANGED
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
3
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
4
|
-
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
5
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
-
var __spreadValues = (a, b) => {
|
|
7
|
-
for (var prop in b || (b = {}))
|
|
8
|
-
if (__hasOwnProp.call(b, prop))
|
|
9
|
-
__defNormalProp(a, prop, b[prop]);
|
|
10
|
-
if (__getOwnPropSymbols)
|
|
11
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
12
|
-
if (__propIsEnum.call(b, prop))
|
|
13
|
-
__defNormalProp(a, prop, b[prop]);
|
|
14
|
-
}
|
|
15
|
-
return a;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
1
|
// src/use-feature-flag.ts
|
|
19
2
|
import { ProviderEvents, ProviderStatus } from "@openfeature/web-sdk";
|
|
20
3
|
import { useEffect, useState } from "react";
|
|
@@ -45,8 +28,40 @@ var DEFAULT_OPTIONS = {
|
|
|
45
28
|
updateOnConfigurationChanged: true,
|
|
46
29
|
suspend: true
|
|
47
30
|
};
|
|
48
|
-
function
|
|
49
|
-
|
|
31
|
+
function useBooleanFlagValue(flagKey, defaultValue, options) {
|
|
32
|
+
return useBooleanFlagDetails(flagKey, defaultValue, options).value;
|
|
33
|
+
}
|
|
34
|
+
function useBooleanFlagDetails(flagKey, defaultValue, options) {
|
|
35
|
+
return attachHandlersAndResolve(flagKey, defaultValue, (client) => {
|
|
36
|
+
return client.getBooleanDetails;
|
|
37
|
+
}, options);
|
|
38
|
+
}
|
|
39
|
+
function useStringFlagValue(flagKey, defaultValue, options) {
|
|
40
|
+
return useStringFlagDetails(flagKey, defaultValue, options).value;
|
|
41
|
+
}
|
|
42
|
+
function useStringFlagDetails(flagKey, defaultValue, options) {
|
|
43
|
+
return attachHandlersAndResolve(flagKey, defaultValue, (client) => {
|
|
44
|
+
return client.getStringDetails;
|
|
45
|
+
}, options);
|
|
46
|
+
}
|
|
47
|
+
function useNumberFlagValue(flagKey, defaultValue, options) {
|
|
48
|
+
return useNumberFlagDetails(flagKey, defaultValue, options).value;
|
|
49
|
+
}
|
|
50
|
+
function useNumberFlagDetails(flagKey, defaultValue, options) {
|
|
51
|
+
return attachHandlersAndResolve(flagKey, defaultValue, (client) => {
|
|
52
|
+
return client.getNumberDetails;
|
|
53
|
+
}, options);
|
|
54
|
+
}
|
|
55
|
+
function useObjectFlagValue(flagKey, defaultValue, options) {
|
|
56
|
+
return useObjectFlagDetails(flagKey, defaultValue, options).value;
|
|
57
|
+
}
|
|
58
|
+
function useObjectFlagDetails(flagKey, defaultValue, options) {
|
|
59
|
+
return attachHandlersAndResolve(flagKey, defaultValue, (client) => {
|
|
60
|
+
return client.getObjectDetails;
|
|
61
|
+
}, options);
|
|
62
|
+
}
|
|
63
|
+
function attachHandlersAndResolve(flagKey, defaultValue, resolver, options) {
|
|
64
|
+
const defaultedOptions = { ...DEFAULT_OPTIONS, ...options };
|
|
50
65
|
const [, updateState] = useState();
|
|
51
66
|
const forceUpdate = () => {
|
|
52
67
|
updateState({});
|
|
@@ -71,18 +86,7 @@ function useFeatureFlag(flagKey, defaultValue, options) {
|
|
|
71
86
|
client.removeHandler(ProviderEvents.ConfigurationChanged, forceUpdate);
|
|
72
87
|
};
|
|
73
88
|
}, [client]);
|
|
74
|
-
return
|
|
75
|
-
}
|
|
76
|
-
function getFlag(client, flagKey, defaultValue) {
|
|
77
|
-
if (typeof defaultValue === "boolean") {
|
|
78
|
-
return client.getBooleanDetails(flagKey, defaultValue);
|
|
79
|
-
} else if (typeof defaultValue === "string") {
|
|
80
|
-
return client.getStringDetails(flagKey, defaultValue);
|
|
81
|
-
} else if (typeof defaultValue === "number") {
|
|
82
|
-
return client.getNumberDetails(flagKey, defaultValue);
|
|
83
|
-
} else {
|
|
84
|
-
return client.getObjectDetails(flagKey, defaultValue);
|
|
85
|
-
}
|
|
89
|
+
return resolver(client).call(client, flagKey, defaultValue);
|
|
86
90
|
}
|
|
87
91
|
function suspend(client, updateState) {
|
|
88
92
|
let suspendResolver;
|
|
@@ -132,7 +136,14 @@ function suspenseWrapper(promise) {
|
|
|
132
136
|
export * from "@openfeature/web-sdk";
|
|
133
137
|
export {
|
|
134
138
|
OpenFeatureProvider,
|
|
135
|
-
|
|
136
|
-
|
|
139
|
+
useBooleanFlagDetails,
|
|
140
|
+
useBooleanFlagValue,
|
|
141
|
+
useNumberFlagDetails,
|
|
142
|
+
useNumberFlagValue,
|
|
143
|
+
useObjectFlagDetails,
|
|
144
|
+
useObjectFlagValue,
|
|
145
|
+
useOpenFeatureClient,
|
|
146
|
+
useStringFlagDetails,
|
|
147
|
+
useStringFlagValue
|
|
137
148
|
};
|
|
138
149
|
//# sourceMappingURL=index.js.map
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/use-feature-flag.ts", "../../src/provider.tsx", "../../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["import { Client, EvaluationDetails, FlagEvaluationOptions, FlagValue, ProviderEvents, ProviderStatus } from '@openfeature/web-sdk';\nimport { Dispatch, SetStateAction, useEffect, useState } from 'react';\nimport { useOpenFeatureClient } from './provider';\n\ntype ReactFlagEvaluationOptions = {\n /**\n * Suspend flag evaluations while the provider is not ready.\n * Set to false if you don't want to use React Suspense API.\n * Defaults to true.\n */\n suspend?: boolean,\n /**\n * Update the component if the provider emits a ConfigurationChanged event.\n * Set to false to prevent components from re-rendering when flag value changes\n * are received by the associated provider.\n * Defaults to true.\n */\n updateOnConfigurationChanged?: boolean,\n /**\n * Update the component when the OpenFeature context changes.\n * Set to false to prevent components from re-rendering when attributes which \n * may be factors in flag evaluation change.\n * Defaults to true.\n */\n updateOnContextChanged?: boolean,\n} & FlagEvaluationOptions;\n\nconst DEFAULT_OPTIONS: ReactFlagEvaluationOptions = {\n updateOnContextChanged: true,\n updateOnConfigurationChanged: true,\n suspend: true,\n};\n\nenum SuspendState {\n Pending,\n Success,\n Error\n}\n\n/**\n * Evaluates a feature flag, returning evaluation details.\n * @param {string}flagKey the flag identifier\n * @param {T} defaultValue the default value\n * @param {ReactFlagEvaluationOptions} options options for this evaluation\n * @template T flag type\n * @returns { EvaluationDetails<T>} a EvaluationDetails object for this evaluation\n */\nexport function useFeatureFlag<T extends FlagValue>(flagKey: string, defaultValue: T, options?: ReactFlagEvaluationOptions): EvaluationDetails<T> {\n const defaultedOptions = { ...DEFAULT_OPTIONS, ...options };\n const [, updateState] = useState<object | undefined>();\n const forceUpdate = () => {\n updateState({});\n };\n const client = useOpenFeatureClient();\n\n useEffect(() => {\n\n if (client.providerStatus !== ProviderStatus.READY) {\n // update when the provider is ready\n client.addHandler(ProviderEvents.Ready, forceUpdate);\n if (defaultedOptions.suspend) {\n suspend(client, updateState);\n }\n }\n\n if (defaultedOptions.updateOnContextChanged) {\n // update when the context changes\n client.addHandler(ProviderEvents.ContextChanged, forceUpdate);\n }\n\n if (defaultedOptions.updateOnConfigurationChanged) {\n // update when the provider configuration changes\n client.addHandler(ProviderEvents.ConfigurationChanged, forceUpdate);\n }\n return () => {\n // cleanup the handlers (we can do this unconditionally with no impact)\n client.removeHandler(ProviderEvents.Ready, forceUpdate);\n client.removeHandler(ProviderEvents.ContextChanged, forceUpdate);\n client.removeHandler(ProviderEvents.ConfigurationChanged, forceUpdate);\n };\n }, [client]);\n\n return getFlag(client, flagKey, defaultValue);\n}\n\nfunction getFlag<T extends FlagValue>(client: Client, flagKey: string, defaultValue: T): EvaluationDetails<T> {\n if (typeof defaultValue === 'boolean') {\n return client.getBooleanDetails(flagKey, defaultValue) as EvaluationDetails<T>;\n } else if (typeof defaultValue === 'string') {\n return client.getStringDetails(flagKey, defaultValue) as EvaluationDetails<T>;\n } else if (typeof defaultValue === 'number') {\n return client.getNumberDetails(flagKey, defaultValue) as EvaluationDetails<T>;\n } else {\n return client.getObjectDetails(flagKey, defaultValue) as EvaluationDetails<T>;\n }\n}\n\n/**\n * Suspend function. If this runs, components using the calling hook will be suspended.\n * @param {Client} client the OpenFeature client\n * @param {Function} updateState the state update function\n */\nfunction suspend(client: Client, updateState: Dispatch<SetStateAction<object | undefined>>) {\n let suspendResolver: () => void;\n let suspendRejecter: () => void;\n const suspendPromise = new Promise<void>((resolve) => {\n suspendResolver = () => {\n resolve();\n client.removeHandler(ProviderEvents.Ready, suspendResolver); // remove handler once it's run\n };\n suspendRejecter = () => {\n resolve(); // we still resolve here, since we don't want to throw errors\n client.removeHandler(ProviderEvents.Error, suspendRejecter); // remove handler once it's run\n };\n client.addHandler(ProviderEvents.Ready, suspendResolver);\n client.addHandler(ProviderEvents.Error, suspendRejecter);\n });\n updateState(suspenseWrapper(suspendPromise));\n}\n\n/**\n * Promise wrapper that throws unresolved promises to support React suspense.\n * @param {Promise<T>} promise to wrap\n * @template T flag type\n * @returns {Function} suspense-compliant lambda\n */\nfunction suspenseWrapper <T>(promise: Promise<T>) {\n let status: SuspendState = SuspendState.Pending;\n let result: T;\n\n const suspended = promise.then(\n (value) => {\n status = SuspendState.Success;\n result = value;\n },\n (error) => {\n status = SuspendState.Error;\n result = error;\n }\n );\n\n return () => {\n switch (status) {\n case SuspendState.Pending:\n throw suspended;\n case SuspendState.Success:\n return result;\n case SuspendState.Error:\n throw result;\n default:\n throw new Error('Suspending promise is in an unknown state.');\n }\n };\n};", "import * as React from 'react';\nimport { Client, OpenFeature } from '@openfeature/web-sdk';\n\ntype ClientOrClientName =\n | {\n /**\n * The name of the client.\n * @see OpenFeature.setProvider() and overloads.\n */\n clientName: string;\n /**\n * OpenFeature client to use.\n */\n client?: never;\n }\n | {\n /**\n * OpenFeature client to use.\n */\n client: Client;\n /**\n * The name of the client.\n * @see OpenFeature.setProvider() and overloads.\n */\n clientName?: never;\n };\n\ntype ProviderProps = {\n children?: React.ReactNode;\n} & ClientOrClientName;\n\nconst Context = React.createContext<Client | undefined>(undefined);\n\nexport const OpenFeatureProvider = ({ client, clientName, children }: ProviderProps) => {\n if (!client) {\n client = OpenFeature.getClient(clientName);\n }\n\n return <Context.Provider value={client}>{children}</Context.Provider>;\n};\n\nexport const useOpenFeatureClient = () => {\n const client = React.useContext(Context);\n\n if (!client) {\n throw new Error(\n 'No OpenFeature client available - components using OpenFeature must be wrapped with an <OpenFeatureProvider>'\n );\n }\n\n return client;\n};\n", "export * from './use-feature-flag';\nexport * from './provider';\n// re-export the web-sdk so consumers can access that API from the react-sdk\nexport * from '@openfeature/web-sdk';\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import { Client, EvaluationDetails, FlagEvaluationOptions, FlagValue, JsonValue, ProviderEvents, ProviderStatus } from '@openfeature/web-sdk';\nimport { Dispatch, SetStateAction, useEffect, useState } from 'react';\nimport { useOpenFeatureClient } from './provider';\n\ntype ReactFlagEvaluationOptions = {\n /**\n * Suspend flag evaluations while the provider is not ready.\n * Set to false if you don't want to use React Suspense API.\n * Defaults to true.\n */\n suspend?: boolean,\n /**\n * Update the component if the provider emits a ConfigurationChanged event.\n * Set to false to prevent components from re-rendering when flag value changes\n * are received by the associated provider.\n * Defaults to true.\n */\n updateOnConfigurationChanged?: boolean,\n /**\n * Update the component when the OpenFeature context changes.\n * Set to false to prevent components from re-rendering when attributes which \n * may be factors in flag evaluation change.\n * Defaults to true.\n */\n updateOnContextChanged?: boolean,\n} & FlagEvaluationOptions;\n\nconst DEFAULT_OPTIONS: ReactFlagEvaluationOptions = {\n updateOnContextChanged: true,\n updateOnConfigurationChanged: true,\n suspend: true,\n};\n\nenum SuspendState {\n Pending,\n Success,\n Error\n}\n\n/**\n * Evaluates a feature flag, returning a boolean.\n * By default, components will re-render when the flag value changes.\n * @param {string} flagKey the flag identifier\n * @param {boolean} defaultValue the default value\n * @param {ReactFlagEvaluationOptions} options options for this evaluation\n * @returns { boolean} a EvaluationDetails object for this evaluation\n */\nexport function useBooleanFlagValue(flagKey: string, defaultValue: boolean, options?: ReactFlagEvaluationOptions): boolean {\n return useBooleanFlagDetails(flagKey, defaultValue, options).value;\n}\n\n/**\n * Evaluates a feature flag, returning evaluation details.\n * By default, components will re-render when the flag value changes.\n * @param {string} flagKey the flag identifier\n * @param {boolean} defaultValue the default value\n * @param {ReactFlagEvaluationOptions} options options for this evaluation\n * @returns { EvaluationDetails<boolean>} a EvaluationDetails object for this evaluation\n */\nexport function useBooleanFlagDetails(flagKey: string, defaultValue: boolean, options?: ReactFlagEvaluationOptions): EvaluationDetails<boolean> {\n return attachHandlersAndResolve(flagKey, defaultValue, (client) => {\n return client.getBooleanDetails;\n }, options);\n}\n\n/**\n * Evaluates a feature flag, returning a string.\n * By default, components will re-render when the flag value changes.\n * @param {string} flagKey the flag identifier\n * @template {string} [T=string] A optional generic argument constraining the string\n * @param {T} defaultValue the default value\n * @param {ReactFlagEvaluationOptions} options options for this evaluation\n * @returns { boolean} a EvaluationDetails object for this evaluation\n */\nexport function useStringFlagValue<T extends string = string>(flagKey: string, defaultValue: T, options?: ReactFlagEvaluationOptions): T {\n return useStringFlagDetails(flagKey, defaultValue, options).value;\n}\n\n/**\n * Evaluates a feature flag, returning evaluation details.\n * By default, components will re-render when the flag value changes.\n * @param {string} flagKey the flag identifier\n * @template {string} [T=string] A optional generic argument constraining the string\n * @param {T} defaultValue the default value\n * @param {ReactFlagEvaluationOptions} options options for this evaluation\n * @returns { EvaluationDetails<string>} a EvaluationDetails object for this evaluation\n */\nexport function useStringFlagDetails<T extends string = string>(flagKey: string, defaultValue: T, options?: ReactFlagEvaluationOptions): EvaluationDetails<T> {\n return attachHandlersAndResolve(flagKey, defaultValue, (client) => {\n return client.getStringDetails<T>;\n }, options);\n}\n\n/**\n * Evaluates a feature flag, returning a number.\n * By default, components will re-render when the flag value changes.\n * @param {string} flagKey the flag identifier\n * @template {number} [T=number] A optional generic argument constraining the number\n * @param {T} defaultValue the default value\n * @param {ReactFlagEvaluationOptions} options options for this evaluation\n * @returns { boolean} a EvaluationDetails object for this evaluation\n */\nexport function useNumberFlagValue<T extends number = number>(flagKey: string, defaultValue: T, options?: ReactFlagEvaluationOptions): T {\n return useNumberFlagDetails(flagKey, defaultValue, options).value;\n}\n\n/**\n * Evaluates a feature flag, returning evaluation details.\n * By default, components will re-render when the flag value changes.\n * @param {string} flagKey the flag identifier\n * @template {number} [T=number] A optional generic argument constraining the number\n * @param {T} defaultValue the default value\n * @param {ReactFlagEvaluationOptions} options options for this evaluation\n * @returns { EvaluationDetails<number>} a EvaluationDetails object for this evaluation\n */\nexport function useNumberFlagDetails<T extends number = number>(flagKey: string, defaultValue: T, options?: ReactFlagEvaluationOptions): EvaluationDetails<T> {\n return attachHandlersAndResolve(flagKey, defaultValue, (client) => {\n return client.getNumberDetails<T>;\n }, options);\n}\n\n/**\n * Evaluates a feature flag, returning an object.\n * By default, components will re-render when the flag value changes.\n * @param {string} flagKey the flag identifier\n * @template {JsonValue} [T=JsonValue] A optional generic argument describing the structure\n * @param {T} defaultValue the default value\n * @param {ReactFlagEvaluationOptions} options options for this evaluation\n * @returns { boolean} a EvaluationDetails object for this evaluation\n */\nexport function useObjectFlagValue<T extends JsonValue = JsonValue>(flagKey: string, defaultValue: T, options?: ReactFlagEvaluationOptions): T {\n return useObjectFlagDetails<T>(flagKey, defaultValue, options).value;\n}\n\n/**\n * Evaluates a feature flag, returning evaluation details.\n * By default, components will re-render when the flag value changes.\n * @param {string} flagKey the flag identifier\n * @param {T} defaultValue the default value\n * @template {JsonValue} [T=JsonValue] A optional generic argument describing the structure\n * @param {ReactFlagEvaluationOptions} options options for this evaluation\n * @returns { EvaluationDetails<T>} a EvaluationDetails object for this evaluation\n */\nexport function useObjectFlagDetails<T extends JsonValue = JsonValue>(flagKey: string, defaultValue: T, options?: ReactFlagEvaluationOptions): EvaluationDetails<T> {\n return attachHandlersAndResolve(flagKey, defaultValue, (client) => {\n return client.getObjectDetails<T>;\n }, options);\n}\n\nfunction attachHandlersAndResolve<T extends FlagValue>(flagKey: string, defaultValue: T, resolver: (client: Client) => (flagKey: string, defaultValue: T) => EvaluationDetails<T>, options?: ReactFlagEvaluationOptions): EvaluationDetails<T> {\n const defaultedOptions = { ...DEFAULT_OPTIONS, ...options };\n const [, updateState] = useState<object | undefined>();\n const forceUpdate = () => {\n updateState({});\n };\n const client = useOpenFeatureClient();\n\n useEffect(() => {\n\n if (client.providerStatus !== ProviderStatus.READY) {\n // update when the provider is ready\n client.addHandler(ProviderEvents.Ready, forceUpdate);\n if (defaultedOptions.suspend) {\n suspend(client, updateState);\n }\n }\n\n if (defaultedOptions.updateOnContextChanged) {\n // update when the context changes\n client.addHandler(ProviderEvents.ContextChanged, forceUpdate);\n }\n\n if (defaultedOptions.updateOnConfigurationChanged) {\n // update when the provider configuration changes\n client.addHandler(ProviderEvents.ConfigurationChanged, forceUpdate);\n }\n return () => {\n // cleanup the handlers (we can do this unconditionally with no impact)\n client.removeHandler(ProviderEvents.Ready, forceUpdate);\n client.removeHandler(ProviderEvents.ContextChanged, forceUpdate);\n client.removeHandler(ProviderEvents.ConfigurationChanged, forceUpdate);\n };\n }, [client]);\n\n return resolver(client).call(client, flagKey, defaultValue);\n}\n\n/**\n * Suspend function. If this runs, components using the calling hook will be suspended.\n * @param {Client} client the OpenFeature client\n * @param {Function} updateState the state update function\n */\nfunction suspend(client: Client, updateState: Dispatch<SetStateAction<object | undefined>>) {\n let suspendResolver: () => void;\n let suspendRejecter: () => void;\n const suspendPromise = new Promise<void>((resolve) => {\n suspendResolver = () => {\n resolve();\n client.removeHandler(ProviderEvents.Ready, suspendResolver); // remove handler once it's run\n };\n suspendRejecter = () => {\n resolve(); // we still resolve here, since we don't want to throw errors\n client.removeHandler(ProviderEvents.Error, suspendRejecter); // remove handler once it's run\n };\n client.addHandler(ProviderEvents.Ready, suspendResolver);\n client.addHandler(ProviderEvents.Error, suspendRejecter);\n });\n updateState(suspenseWrapper(suspendPromise));\n}\n\n/**\n * Promise wrapper that throws unresolved promises to support React suspense.\n * @param {Promise<T>} promise to wrap\n * @template T flag type\n * @returns {Function} suspense-compliant lambda\n */\nfunction suspenseWrapper <T>(promise: Promise<T>) {\n let status: SuspendState = SuspendState.Pending;\n let result: T;\n\n const suspended = promise.then(\n (value) => {\n status = SuspendState.Success;\n result = value;\n },\n (error) => {\n status = SuspendState.Error;\n result = error;\n }\n );\n\n return () => {\n switch (status) {\n case SuspendState.Pending:\n throw suspended;\n case SuspendState.Success:\n return result;\n case SuspendState.Error:\n throw result;\n default:\n throw new Error('Suspending promise is in an unknown state.');\n }\n };\n};", "import * as React from 'react';\nimport { Client, OpenFeature } from '@openfeature/web-sdk';\n\ntype ClientOrClientName =\n | {\n /**\n * The name of the client.\n * @see OpenFeature.setProvider() and overloads.\n */\n clientName: string;\n /**\n * OpenFeature client to use.\n */\n client?: never;\n }\n | {\n /**\n * OpenFeature client to use.\n */\n client: Client;\n /**\n * The name of the client.\n * @see OpenFeature.setProvider() and overloads.\n */\n clientName?: never;\n };\n\ntype ProviderProps = {\n children?: React.ReactNode;\n} & ClientOrClientName;\n\nconst Context = React.createContext<Client | undefined>(undefined);\n\nexport const OpenFeatureProvider = ({ client, clientName, children }: ProviderProps) => {\n if (!client) {\n client = OpenFeature.getClient(clientName);\n }\n\n return <Context.Provider value={client}>{children}</Context.Provider>;\n};\n\nexport const useOpenFeatureClient = () => {\n const client = React.useContext(Context);\n\n if (!client) {\n throw new Error(\n 'No OpenFeature client available - components using OpenFeature must be wrapped with an <OpenFeatureProvider>'\n );\n }\n\n return client;\n};\n", "export * from './use-feature-flag';\nexport * from './provider';\n// re-export the web-sdk so consumers can access that API from the react-sdk\nexport * from '@openfeature/web-sdk';\n"],
|
|
5
|
+
"mappings": ";AAAA,SAAiF,gBAAgB,sBAAsB;AACvH,SAAmC,WAAW,gBAAgB;;;ACD9D,YAAY,WAAW;AACvB,SAAiB,mBAAmB;AA8BpC,IAAM,UAAgB,oBAAkC,MAAS;AAE1D,IAAM,sBAAsB,CAAC,EAAE,QAAQ,YAAY,SAAS,MAAqB;AACtF,MAAI,CAAC,QAAQ;AACX,aAAS,YAAY,UAAU,UAAU;AAAA,EAC3C;AAEA,SAAO,oCAAC,QAAQ,UAAR,EAAiB,OAAO,UAAS,QAAS;AACpD;AAEO,IAAM,uBAAuB,MAAM;AACxC,QAAM,SAAe,iBAAW,OAAO;AAEvC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;ADxBA,IAAM,kBAA8C;AAAA,EAClD,wBAAwB;AAAA,EACxB,8BAA8B;AAAA,EAC9B,SAAS;AACX;AAgBO,SAAS,oBAAoB,SAAiB,cAAuB,SAA+C;AACzH,SAAO,sBAAsB,SAAS,cAAc,OAAO,EAAE;AAC/D;AAUO,SAAS,sBAAsB,SAAiB,cAAuB,SAAkE;AAC9I,SAAO,yBAAyB,SAAS,cAAc,CAAC,WAAW;AACjE,WAAO,OAAO;AAAA,EAChB,GAAI,OAAO;AACb;AAWO,SAAS,mBAA8C,SAAiB,cAAiB,SAAyC;AACvI,SAAO,qBAAqB,SAAS,cAAc,OAAO,EAAE;AAC9D;AAWO,SAAS,qBAAgD,SAAiB,cAAiB,SAA4D;AAC5J,SAAO,yBAAyB,SAAS,cAAc,CAAC,WAAW;AACjE,WAAO,OAAO;AAAA,EAChB,GAAI,OAAO;AACb;AAWO,SAAS,mBAA8C,SAAiB,cAAiB,SAAyC;AACvI,SAAO,qBAAqB,SAAS,cAAc,OAAO,EAAE;AAC9D;AAWO,SAAS,qBAAgD,SAAiB,cAAiB,SAA4D;AAC5J,SAAO,yBAAyB,SAAS,cAAc,CAAC,WAAW;AACjE,WAAO,OAAO;AAAA,EAChB,GAAI,OAAO;AACb;AAWO,SAAS,mBAAoD,SAAiB,cAAiB,SAAyC;AAC7I,SAAO,qBAAwB,SAAS,cAAc,OAAO,EAAE;AACjE;AAWO,SAAS,qBAAsD,SAAiB,cAAiB,SAA4D;AAClK,SAAO,yBAAyB,SAAS,cAAc,CAAC,WAAW;AACjE,WAAO,OAAO;AAAA,EAChB,GAAI,OAAO;AACb;AAEA,SAAS,yBAA8C,SAAiB,cAAiB,UAA0F,SAA4D;AAC7O,QAAM,mBAAmB,EAAE,GAAG,iBAAiB,GAAG,QAAQ;AAC1D,QAAM,CAAC,EAAE,WAAW,IAAI,SAA6B;AACrD,QAAM,cAAc,MAAM;AACxB,gBAAY,CAAC,CAAC;AAAA,EAChB;AACA,QAAM,SAAS,qBAAqB;AAEpC,YAAU,MAAM;AAEd,QAAI,OAAO,mBAAmB,eAAe,OAAO;AAElD,aAAO,WAAW,eAAe,OAAO,WAAW;AACnD,UAAI,iBAAiB,SAAS;AAC5B,gBAAQ,QAAQ,WAAW;AAAA,MAC7B;AAAA,IACF;AAEA,QAAI,iBAAiB,wBAAwB;AAE3C,aAAO,WAAW,eAAe,gBAAgB,WAAW;AAAA,IAC9D;AAEA,QAAI,iBAAiB,8BAA8B;AAEjD,aAAO,WAAW,eAAe,sBAAsB,WAAW;AAAA,IACpE;AACA,WAAO,MAAM;AAEX,aAAO,cAAc,eAAe,OAAO,WAAW;AACtD,aAAO,cAAc,eAAe,gBAAgB,WAAW;AAC/D,aAAO,cAAc,eAAe,sBAAsB,WAAW;AAAA,IACvE;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAEX,SAAO,SAAS,MAAM,EAAE,KAAK,QAAQ,SAAS,YAAY;AAC5D;AAOA,SAAS,QAAQ,QAAgB,aAA2D;AAC1F,MAAI;AACJ,MAAI;AACJ,QAAM,iBAAiB,IAAI,QAAc,CAAC,YAAY;AACpD,sBAAkB,MAAM;AACtB,cAAQ;AACR,aAAO,cAAc,eAAe,OAAO,eAAe;AAAA,IAC5D;AACA,sBAAkB,MAAM;AACtB,cAAQ;AACR,aAAO,cAAc,eAAe,OAAO,eAAe;AAAA,IAC5D;AACA,WAAO,WAAW,eAAe,OAAO,eAAe;AACvD,WAAO,WAAW,eAAe,OAAO,eAAe;AAAA,EACzD,CAAC;AACD,cAAY,gBAAgB,cAAc,CAAC;AAC7C;AAQA,SAAS,gBAAoB,SAAqB;AAChD,MAAI,SAAuB;AAC3B,MAAI;AAEJ,QAAM,YAAY,QAAQ;AAAA,IACxB,CAAC,UAAU;AACT,eAAS;AACT,eAAS;AAAA,IACX;AAAA,IACA,CAAC,UAAU;AACT,eAAS;AACT,eAAS;AAAA,IACX;AAAA,EACF;AAEA,SAAO,MAAM;AACX,YAAQ,QAAQ;AAAA,MACd,KAAK;AACH,cAAM;AAAA,MACR,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,cAAM;AAAA,MACR;AACE,cAAM,IAAI,MAAM,4CAA4C;AAAA,IAChE;AAAA,EACF;AACF;;;AEhPA,cAAc;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|