@openfeature/react-sdk 0.2.3-experimental → 0.3.0-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 +78 -25
- package/dist/cjs/index.js +185 -79
- package/dist/cjs/index.js.map +4 -4
- package/dist/esm/index.js +195 -85
- package/dist/esm/index.js.map +4 -4
- package/dist/types.d.ts +116 -13
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
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
18
|
<!-- x-release-please-start-version -->
|
|
19
|
-
<a href="https://github.com/open-feature/js-sdk/releases/tag/react-sdk-v0.
|
|
20
|
-
<img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v0.
|
|
19
|
+
<a href="https://github.com/open-feature/js-sdk/releases/tag/react-sdk-v0.3.0-experimental">
|
|
20
|
+
<img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v0.3.0-experimental&color=blue&style=for-the-badge" />
|
|
21
21
|
</a>
|
|
22
22
|
<!-- x-release-please-end -->
|
|
23
23
|
<br/>
|
|
@@ -50,10 +50,13 @@ In addition to the feature provided by the [web sdk](https://openfeature.dev/doc
|
|
|
50
50
|
- [yarn](#yarn)
|
|
51
51
|
- [Required peer dependencies](#required-peer-dependencies)
|
|
52
52
|
- [Usage](#usage)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
- [OpenFeatureProvider context provider](#openfeatureprovider-context-provider)
|
|
54
|
+
- [Evaluation hooks](#evaluation-hooks)
|
|
55
|
+
- [Multiple Providers and Domains](#multiple-providers-and-domains)
|
|
56
|
+
- [Re-rendering with Context Changes](#re-rendering-with-context-changes)
|
|
57
|
+
- [Re-rendering with Flag Configuration Changes](#re-rendering-with-flag-configuration-changes)
|
|
58
|
+
- [Suspense Support](#suspense-support)
|
|
59
|
+
- [FAQ and troubleshooting](#faq-and-troubleshooting)
|
|
57
60
|
- [Resources](#resources)
|
|
58
61
|
|
|
59
62
|
## Quick start
|
|
@@ -87,10 +90,14 @@ The following list contains the peer dependencies of `@openfeature/react-sdk` wi
|
|
|
87
90
|
|
|
88
91
|
### Usage
|
|
89
92
|
|
|
93
|
+
#### OpenFeatureProvider context provider
|
|
94
|
+
|
|
95
|
+
The `OpenFeatureProvider` is a [React context provider](https://react.dev/reference/react/createContext#provider) which represents a scope for feature flag evaluations within a React application.
|
|
96
|
+
It binds an OpenFeature client to all evaluations within child components, and allows the use of evaluation hooks.
|
|
90
97
|
The example below shows how to use the `OpenFeatureProvider` with OpenFeature's `InMemoryProvider`.
|
|
91
98
|
|
|
92
99
|
```tsx
|
|
93
|
-
import { EvaluationContext, OpenFeatureProvider,
|
|
100
|
+
import { EvaluationContext, OpenFeatureProvider, useFlag, OpenFeature, InMemoryProvider } from '@openfeature/react-sdk';
|
|
94
101
|
|
|
95
102
|
const flagConfig = {
|
|
96
103
|
'new-message': {
|
|
@@ -109,8 +116,11 @@ const flagConfig = {
|
|
|
109
116
|
},
|
|
110
117
|
};
|
|
111
118
|
|
|
119
|
+
// Instantiate and set our provider (be sure this only happens once)!
|
|
120
|
+
// Note: there's no need to await its initialization, the React SDK handles re-rendering and suspense for you!
|
|
112
121
|
OpenFeature.setProvider(new InMemoryProvider(flagConfig));
|
|
113
122
|
|
|
123
|
+
// Enclose your content in the configured provider
|
|
114
124
|
function App() {
|
|
115
125
|
return (
|
|
116
126
|
<OpenFeatureProvider>
|
|
@@ -118,26 +128,39 @@ function App() {
|
|
|
118
128
|
</OpenFeatureProvider>
|
|
119
129
|
);
|
|
120
130
|
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
#### Evaluation hooks
|
|
121
134
|
|
|
135
|
+
Within the provider, you can use the various evaluation hooks to evaluate flags.
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
122
138
|
function Page() {
|
|
123
|
-
|
|
139
|
+
// Use the "query-style" flag evaluation hook, specifying a flag-key and a default value.
|
|
140
|
+
const { value: showNewMessage } = useFlag('new-message', true);
|
|
124
141
|
return (
|
|
125
142
|
<div className="App">
|
|
126
143
|
<header className="App-header">
|
|
127
|
-
{
|
|
144
|
+
{showNewMessage ? <p>Welcome to this OpenFeature-enabled React app!</p> : <p>Welcome to this React app.</p>}
|
|
128
145
|
</header>
|
|
129
146
|
</div>
|
|
130
147
|
)
|
|
131
148
|
}
|
|
132
|
-
|
|
133
|
-
export default App;
|
|
134
149
|
```
|
|
135
150
|
|
|
136
|
-
You use the
|
|
151
|
+
You can use the strongly-typed flag value and flag evaluation detail hooks as well, if you prefer.
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
import { useBooleanFlagValue } from '@openfeature/react-sdk';
|
|
155
|
+
|
|
156
|
+
// boolean flag evaluation
|
|
157
|
+
const value = useBooleanFlagValue('new-message', false);
|
|
158
|
+
```
|
|
137
159
|
|
|
138
160
|
```tsx
|
|
139
161
|
import { useBooleanFlagDetails } from '@openfeature/react-sdk';
|
|
140
162
|
|
|
163
|
+
// "detailed" boolean flag evaluation
|
|
141
164
|
const {
|
|
142
165
|
value,
|
|
143
166
|
variant,
|
|
@@ -146,8 +169,7 @@ const {
|
|
|
146
169
|
} = useBooleanFlagDetails('new-message', false);
|
|
147
170
|
```
|
|
148
171
|
|
|
149
|
-
|
|
150
|
-
|
|
172
|
+
#### Multiple Providers and Domains
|
|
151
173
|
|
|
152
174
|
Multiple providers can be used by passing a `domain` to the `OpenFeatureProvider`:
|
|
153
175
|
|
|
@@ -170,15 +192,15 @@ OpenFeature.getClient('my-domain');
|
|
|
170
192
|
|
|
171
193
|
For more information about `domains`, refer to the [web SDK](https://github.com/open-feature/js-sdk/blob/main/packages/client/README.md).
|
|
172
194
|
|
|
173
|
-
|
|
195
|
+
#### Re-rendering with Context Changes
|
|
174
196
|
|
|
175
197
|
By default, if the OpenFeature [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context) is modified, components will be re-rendered.
|
|
176
198
|
This is useful in cases where flag values are dependant on user-attributes or other application state (user logged in, items in card, etc).
|
|
177
|
-
You can disable this feature in the hook options:
|
|
199
|
+
You can disable this feature in the hook options (or in the [OpenFeatureProvider](#openfeatureprovider-context-provider)):
|
|
178
200
|
|
|
179
201
|
```tsx
|
|
180
202
|
function Page() {
|
|
181
|
-
const
|
|
203
|
+
const showNewMessage = useBooleanFlagValue('new-message', false, { updateOnContextChanged: false });
|
|
182
204
|
return (
|
|
183
205
|
<MyComponents></MyComponents>
|
|
184
206
|
)
|
|
@@ -187,15 +209,15 @@ function Page() {
|
|
|
187
209
|
|
|
188
210
|
For more information about how evaluation context works in the React SDK, see the documentation on OpenFeature's [static context SDK paradigm](https://openfeature.dev/specification/glossary/#static-context-paradigm).
|
|
189
211
|
|
|
190
|
-
|
|
212
|
+
#### Re-rendering with Flag Configuration Changes
|
|
191
213
|
|
|
192
214
|
By default, if the underlying provider emits a `ConfigurationChanged` event, components will be re-rendered.
|
|
193
215
|
This is useful if you want your UI to immediately reflect changes in the backend flag configuration.
|
|
194
|
-
You can disable this feature in the hook options:
|
|
216
|
+
You can disable this feature in the hook options (or in the [OpenFeatureProvider](#openfeatureprovider-context-provider)):
|
|
195
217
|
|
|
196
218
|
```tsx
|
|
197
219
|
function Page() {
|
|
198
|
-
const
|
|
220
|
+
const showNewMessage = useBooleanFlagValue('new-message', false, { updateOnConfigurationChanged: false });
|
|
199
221
|
return (
|
|
200
222
|
<MyComponents></MyComponents>
|
|
201
223
|
)
|
|
@@ -204,11 +226,11 @@ function Page() {
|
|
|
204
226
|
|
|
205
227
|
Note that if your provider doesn't support updates, this configuration has no impact.
|
|
206
228
|
|
|
207
|
-
|
|
229
|
+
#### Suspense Support
|
|
208
230
|
|
|
209
231
|
Frequently, providers need to perform some initial startup tasks.
|
|
210
|
-
It may be desireable not to display components with feature flags until this is complete.
|
|
211
|
-
Built-in [suspense](https://react.dev/reference/react/Suspense) support makes this easy
|
|
232
|
+
It may be desireable not to display components with feature flags until this is complete, or when the context changes.
|
|
233
|
+
Built-in [suspense](https://react.dev/reference/react/Suspense) support makes this easy.
|
|
212
234
|
|
|
213
235
|
```tsx
|
|
214
236
|
function Content() {
|
|
@@ -222,11 +244,11 @@ function Content() {
|
|
|
222
244
|
|
|
223
245
|
function Message() {
|
|
224
246
|
// component to render after READY.
|
|
225
|
-
const
|
|
247
|
+
const showNewMessage = useBooleanFlagValue('new-message', false);
|
|
226
248
|
|
|
227
249
|
return (
|
|
228
250
|
<>
|
|
229
|
-
{
|
|
251
|
+
{showNewMessage ? (
|
|
230
252
|
<p>Welcome to this OpenFeature-enabled React app!</p>
|
|
231
253
|
) : (
|
|
232
254
|
<p>Welcome to this plain old React app!</p>
|
|
@@ -239,6 +261,37 @@ function Fallback() {
|
|
|
239
261
|
// component to render before READY.
|
|
240
262
|
return <p>Waiting for provider to be ready...</p>;
|
|
241
263
|
}
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
This can be disabled in the hook options (or in the [OpenFeatureProvider](#openfeatureprovider-context-provider)).
|
|
268
|
+
|
|
269
|
+
## FAQ and troubleshooting
|
|
270
|
+
|
|
271
|
+
> I get an error that says something like: `A React component suspended while rendering, but no fallback UI was specified.`
|
|
272
|
+
|
|
273
|
+
The OpenFeature React SDK features built-in [suspense support](#suspense-support).
|
|
274
|
+
This means that it will render your loading fallback automatically while the your provider starts up, and during context reconciliation for any of your components using feature flags!
|
|
275
|
+
However, you will see this error if you neglect to create a suspense boundary around any components using feature flags; add a suspense boundary to resolve this issue.
|
|
276
|
+
Alternatively, you can disable this feature by setting `suspendWhileReconciling=false` and `suspendUntilReady=false` in the [evaluation hooks](#evaluation-hooks) or the [OpenFeatureProvider](#openfeatureprovider-context-provider) (which applies to all evaluation hooks in child components).
|
|
277
|
+
|
|
278
|
+
> I get odd rendering issues, or errors when components mount, if I use the suspense features.
|
|
279
|
+
|
|
280
|
+
In React 16/17's "Legacy Suspense", when a component suspends, its sibling components initially mount and then are hidden.
|
|
281
|
+
This can cause surprising effects and inconsistencies if sibling components are rendered while the provider is still getting ready.
|
|
282
|
+
To fix this, you can upgrade to React 18, which uses "Concurrent Suspense", in which siblings are not mounted until their suspended sibling resolves.
|
|
283
|
+
Alternatively, if you cannot upgrade to React 18, you can use the `useWhenProviderReady` utility hook in any sibling components to prevent them from mounting until the provider is ready.
|
|
284
|
+
|
|
285
|
+
> I am using multiple `OpenFeatureProvider` contexts, but they are sharing the same provider or evaluation context. Why?
|
|
286
|
+
|
|
287
|
+
The `OpenFeatureProvider` binds a `client` to all child components, but the provider and context associated with that client is controlled by the `domain` parameter.
|
|
288
|
+
This is consistent with all OpenFeature SDKs.
|
|
289
|
+
To scope an OpenFeatureProvider to a particular provider/context set the `domain` parameter on your `OpenFeatureProvider`:
|
|
290
|
+
|
|
291
|
+
```tsx
|
|
292
|
+
<OpenFeatureProvider domain={'my-domain'}>
|
|
293
|
+
<Page></Page>
|
|
294
|
+
</OpenFeatureProvider>
|
|
242
295
|
```
|
|
243
296
|
|
|
244
297
|
## Resources
|
package/dist/cjs/index.js
CHANGED
|
@@ -34,90 +34,179 @@ __export(src_exports, {
|
|
|
34
34
|
OpenFeatureProvider: () => OpenFeatureProvider,
|
|
35
35
|
useBooleanFlagDetails: () => useBooleanFlagDetails,
|
|
36
36
|
useBooleanFlagValue: () => useBooleanFlagValue,
|
|
37
|
+
useFlag: () => useFlag,
|
|
37
38
|
useNumberFlagDetails: () => useNumberFlagDetails,
|
|
38
39
|
useNumberFlagValue: () => useNumberFlagValue,
|
|
39
40
|
useObjectFlagDetails: () => useObjectFlagDetails,
|
|
40
41
|
useObjectFlagValue: () => useObjectFlagValue,
|
|
41
42
|
useOpenFeatureClient: () => useOpenFeatureClient,
|
|
42
43
|
useStringFlagDetails: () => useStringFlagDetails,
|
|
43
|
-
useStringFlagValue: () => useStringFlagValue
|
|
44
|
+
useStringFlagValue: () => useStringFlagValue,
|
|
45
|
+
useWhenProviderReady: () => useWhenProviderReady
|
|
44
46
|
});
|
|
45
47
|
module.exports = __toCommonJS(src_exports);
|
|
46
48
|
|
|
47
|
-
// src/use-feature-flag.ts
|
|
49
|
+
// src/evaluation/use-feature-flag.ts
|
|
48
50
|
var import_web_sdk2 = require("@openfeature/web-sdk");
|
|
49
|
-
var
|
|
51
|
+
var import_react3 = require("react");
|
|
50
52
|
|
|
51
|
-
// src/
|
|
52
|
-
var
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
client = import_web_sdk.OpenFeature.getClient(domain);
|
|
58
|
-
}
|
|
59
|
-
return /* @__PURE__ */ React.createElement(Context.Provider, { value: client }, children);
|
|
53
|
+
// src/common/options.ts
|
|
54
|
+
var DEFAULT_OPTIONS = {
|
|
55
|
+
updateOnContextChanged: true,
|
|
56
|
+
updateOnConfigurationChanged: true,
|
|
57
|
+
suspendUntilReady: true,
|
|
58
|
+
suspendWhileReconciling: true
|
|
60
59
|
};
|
|
61
|
-
var
|
|
62
|
-
const
|
|
60
|
+
var normalizeOptions = (options) => {
|
|
61
|
+
const defaultOptionsIfMissing = !options ? {} : options;
|
|
62
|
+
const suspendUntilReady = "suspendUntilReady" in defaultOptionsIfMissing ? defaultOptionsIfMissing.suspendUntilReady : defaultOptionsIfMissing.suspend;
|
|
63
|
+
const suspendWhileReconciling = "suspendWhileReconciling" in defaultOptionsIfMissing ? defaultOptionsIfMissing.suspendWhileReconciling : defaultOptionsIfMissing.suspend;
|
|
64
|
+
return {
|
|
65
|
+
updateOnContextChanged: defaultOptionsIfMissing.updateOnContextChanged,
|
|
66
|
+
updateOnConfigurationChanged: defaultOptionsIfMissing.updateOnConfigurationChanged,
|
|
67
|
+
// only return these if properly set (no undefined to allow overriding with spread)
|
|
68
|
+
...typeof suspendUntilReady === "boolean" && { suspendUntilReady },
|
|
69
|
+
...typeof suspendWhileReconciling === "boolean" && { suspendWhileReconciling }
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// src/common/suspend.ts
|
|
74
|
+
var import_web_sdk = require("@openfeature/web-sdk");
|
|
75
|
+
function suspend(client, updateState, ...resumeEvents) {
|
|
76
|
+
let suspendResolver;
|
|
77
|
+
const suspendPromise = new Promise((resolve) => {
|
|
78
|
+
suspendResolver = () => {
|
|
79
|
+
resolve();
|
|
80
|
+
resumeEvents.forEach((e) => {
|
|
81
|
+
client.removeHandler(e, suspendResolver);
|
|
82
|
+
});
|
|
83
|
+
client.removeHandler(import_web_sdk.ProviderEvents.Error, suspendResolver);
|
|
84
|
+
};
|
|
85
|
+
resumeEvents.forEach((e) => {
|
|
86
|
+
client.addHandler(e, suspendResolver);
|
|
87
|
+
});
|
|
88
|
+
client.addHandler(import_web_sdk.ProviderEvents.Error, suspendResolver);
|
|
89
|
+
});
|
|
90
|
+
updateState(suspenseWrapper(suspendPromise));
|
|
91
|
+
}
|
|
92
|
+
function suspenseWrapper(promise) {
|
|
93
|
+
let status = 0 /* Pending */;
|
|
94
|
+
let result;
|
|
95
|
+
const suspended = promise.then((value) => {
|
|
96
|
+
status = 1 /* Success */;
|
|
97
|
+
result = value;
|
|
98
|
+
}).catch((error) => {
|
|
99
|
+
status = 2 /* Error */;
|
|
100
|
+
result = error;
|
|
101
|
+
});
|
|
102
|
+
return () => {
|
|
103
|
+
switch (status) {
|
|
104
|
+
case 0 /* Pending */:
|
|
105
|
+
throw suspended;
|
|
106
|
+
case 1 /* Success */:
|
|
107
|
+
return result;
|
|
108
|
+
case 2 /* Error */:
|
|
109
|
+
throw result;
|
|
110
|
+
default:
|
|
111
|
+
throw new Error("Suspending promise is in an unknown state.");
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/provider/context.ts
|
|
117
|
+
var import_react = __toESM(require("react"));
|
|
118
|
+
var Context = import_react.default.createContext(void 0);
|
|
119
|
+
function useProviderOptions() {
|
|
120
|
+
const { options } = import_react.default.useContext(Context) || {};
|
|
121
|
+
return normalizeOptions(options);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// src/provider/use-open-feature-client.ts
|
|
125
|
+
var import_react2 = __toESM(require("react"));
|
|
126
|
+
function useOpenFeatureClient() {
|
|
127
|
+
const { client } = import_react2.default.useContext(Context) || {};
|
|
63
128
|
if (!client) {
|
|
64
129
|
throw new Error(
|
|
65
130
|
"No OpenFeature client available - components using OpenFeature must be wrapped with an <OpenFeatureProvider>"
|
|
66
131
|
);
|
|
67
132
|
}
|
|
68
133
|
return client;
|
|
69
|
-
}
|
|
134
|
+
}
|
|
70
135
|
|
|
71
|
-
// src/use-feature-flag.ts
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
suspendWhileReconciling: false
|
|
77
|
-
};
|
|
136
|
+
// src/evaluation/use-feature-flag.ts
|
|
137
|
+
function useFlag(flagKey, defaultValue, options) {
|
|
138
|
+
const query = typeof defaultValue === "boolean" ? new HookFlagQuery(useBooleanFlagDetails(flagKey, defaultValue, options)) : typeof defaultValue === "number" ? new HookFlagQuery(useNumberFlagDetails(flagKey, defaultValue, options)) : typeof defaultValue === "string" ? new HookFlagQuery(useStringFlagDetails(flagKey, defaultValue, options)) : new HookFlagQuery(useObjectFlagDetails(flagKey, defaultValue, options));
|
|
139
|
+
return query;
|
|
140
|
+
}
|
|
78
141
|
function useBooleanFlagValue(flagKey, defaultValue, options) {
|
|
79
142
|
return useBooleanFlagDetails(flagKey, defaultValue, options).value;
|
|
80
143
|
}
|
|
81
144
|
function useBooleanFlagDetails(flagKey, defaultValue, options) {
|
|
82
|
-
return attachHandlersAndResolve(
|
|
83
|
-
|
|
84
|
-
|
|
145
|
+
return attachHandlersAndResolve(
|
|
146
|
+
flagKey,
|
|
147
|
+
defaultValue,
|
|
148
|
+
(client) => {
|
|
149
|
+
return client.getBooleanDetails;
|
|
150
|
+
},
|
|
151
|
+
options
|
|
152
|
+
);
|
|
85
153
|
}
|
|
86
154
|
function useStringFlagValue(flagKey, defaultValue, options) {
|
|
87
155
|
return useStringFlagDetails(flagKey, defaultValue, options).value;
|
|
88
156
|
}
|
|
89
157
|
function useStringFlagDetails(flagKey, defaultValue, options) {
|
|
90
|
-
return attachHandlersAndResolve(
|
|
91
|
-
|
|
92
|
-
|
|
158
|
+
return attachHandlersAndResolve(
|
|
159
|
+
flagKey,
|
|
160
|
+
defaultValue,
|
|
161
|
+
(client) => {
|
|
162
|
+
return client.getStringDetails;
|
|
163
|
+
},
|
|
164
|
+
options
|
|
165
|
+
);
|
|
93
166
|
}
|
|
94
167
|
function useNumberFlagValue(flagKey, defaultValue, options) {
|
|
95
168
|
return useNumberFlagDetails(flagKey, defaultValue, options).value;
|
|
96
169
|
}
|
|
97
170
|
function useNumberFlagDetails(flagKey, defaultValue, options) {
|
|
98
|
-
return attachHandlersAndResolve(
|
|
99
|
-
|
|
100
|
-
|
|
171
|
+
return attachHandlersAndResolve(
|
|
172
|
+
flagKey,
|
|
173
|
+
defaultValue,
|
|
174
|
+
(client) => {
|
|
175
|
+
return client.getNumberDetails;
|
|
176
|
+
},
|
|
177
|
+
options
|
|
178
|
+
);
|
|
101
179
|
}
|
|
102
180
|
function useObjectFlagValue(flagKey, defaultValue, options) {
|
|
103
181
|
return useObjectFlagDetails(flagKey, defaultValue, options).value;
|
|
104
182
|
}
|
|
105
183
|
function useObjectFlagDetails(flagKey, defaultValue, options) {
|
|
106
|
-
return attachHandlersAndResolve(
|
|
107
|
-
|
|
108
|
-
|
|
184
|
+
return attachHandlersAndResolve(
|
|
185
|
+
flagKey,
|
|
186
|
+
defaultValue,
|
|
187
|
+
(client) => {
|
|
188
|
+
return client.getObjectDetails;
|
|
189
|
+
},
|
|
190
|
+
options
|
|
191
|
+
);
|
|
109
192
|
}
|
|
110
193
|
function attachHandlersAndResolve(flagKey, defaultValue, resolver, options) {
|
|
111
|
-
const defaultedOptions = { ...DEFAULT_OPTIONS, ...options };
|
|
112
|
-
const [, updateState] = (0,
|
|
194
|
+
const defaultedOptions = { ...DEFAULT_OPTIONS, ...useProviderOptions(), ...normalizeOptions(options) };
|
|
195
|
+
const [, updateState] = (0, import_react3.useState)();
|
|
113
196
|
const client = useOpenFeatureClient();
|
|
114
197
|
const forceUpdate = () => {
|
|
115
198
|
updateState({});
|
|
116
199
|
};
|
|
117
200
|
const suspendRef = () => {
|
|
118
|
-
suspend(
|
|
201
|
+
suspend(
|
|
202
|
+
client,
|
|
203
|
+
updateState,
|
|
204
|
+
import_web_sdk2.ProviderEvents.ContextChanged,
|
|
205
|
+
import_web_sdk2.ProviderEvents.ConfigurationChanged,
|
|
206
|
+
import_web_sdk2.ProviderEvents.Ready
|
|
207
|
+
);
|
|
119
208
|
};
|
|
120
|
-
(0,
|
|
209
|
+
(0, import_react3.useEffect)(() => {
|
|
121
210
|
if (client.providerStatus === import_web_sdk2.ProviderStatus.NOT_READY) {
|
|
122
211
|
client.addHandler(import_web_sdk2.ProviderEvents.Ready, forceUpdate);
|
|
123
212
|
if (defaultedOptions.suspendUntilReady) {
|
|
@@ -136,7 +225,7 @@ function attachHandlersAndResolve(flagKey, defaultValue, resolver, options) {
|
|
|
136
225
|
client.removeHandler(import_web_sdk2.ProviderEvents.Reconciling, suspendRef);
|
|
137
226
|
};
|
|
138
227
|
}, []);
|
|
139
|
-
(0,
|
|
228
|
+
(0, import_react3.useEffect)(() => {
|
|
140
229
|
if (defaultedOptions.updateOnConfigurationChanged) {
|
|
141
230
|
client.addHandler(import_web_sdk2.ProviderEvents.ConfigurationChanged, forceUpdate);
|
|
142
231
|
}
|
|
@@ -146,48 +235,65 @@ function attachHandlersAndResolve(flagKey, defaultValue, resolver, options) {
|
|
|
146
235
|
}, []);
|
|
147
236
|
return resolver(client).call(client, flagKey, defaultValue);
|
|
148
237
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
238
|
+
var HookFlagQuery = class {
|
|
239
|
+
constructor(_details) {
|
|
240
|
+
this._details = _details;
|
|
241
|
+
}
|
|
242
|
+
get details() {
|
|
243
|
+
return this._details;
|
|
244
|
+
}
|
|
245
|
+
get value() {
|
|
246
|
+
return this._details?.value;
|
|
247
|
+
}
|
|
248
|
+
get variant() {
|
|
249
|
+
return this._details.variant;
|
|
250
|
+
}
|
|
251
|
+
get flagMetadata() {
|
|
252
|
+
return this._details.flagMetadata;
|
|
253
|
+
}
|
|
254
|
+
get reason() {
|
|
255
|
+
return this._details.reason;
|
|
256
|
+
}
|
|
257
|
+
get isError() {
|
|
258
|
+
return !!this._details?.errorCode || this._details.reason == import_web_sdk2.StandardResolutionReasons.ERROR;
|
|
259
|
+
}
|
|
260
|
+
get errorCode() {
|
|
261
|
+
return this._details?.errorCode;
|
|
262
|
+
}
|
|
263
|
+
get errorMessage() {
|
|
264
|
+
return this._details?.errorMessage;
|
|
265
|
+
}
|
|
266
|
+
get isAuthoritative() {
|
|
267
|
+
return !this.isError && this._details.reason != import_web_sdk2.StandardResolutionReasons.STALE && this._details.reason != import_web_sdk2.StandardResolutionReasons.DISABLED;
|
|
268
|
+
}
|
|
269
|
+
get type() {
|
|
270
|
+
return typeof this._details.value;
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
// src/provider/provider.tsx
|
|
275
|
+
var import_web_sdk3 = require("@openfeature/web-sdk");
|
|
276
|
+
var React3 = __toESM(require("react"));
|
|
277
|
+
function OpenFeatureProvider({ client, domain, children, ...options }) {
|
|
278
|
+
if (!client) {
|
|
279
|
+
client = import_web_sdk3.OpenFeature.getClient(domain);
|
|
280
|
+
}
|
|
281
|
+
return /* @__PURE__ */ React3.createElement(Context.Provider, { value: { client, options } }, children);
|
|
165
282
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
);
|
|
179
|
-
return () => {
|
|
180
|
-
switch (status) {
|
|
181
|
-
case 0 /* Pending */:
|
|
182
|
-
throw suspended;
|
|
183
|
-
case 1 /* Success */:
|
|
184
|
-
return result;
|
|
185
|
-
case 2 /* Error */:
|
|
186
|
-
throw result;
|
|
187
|
-
default:
|
|
188
|
-
throw new Error("Suspending promise is in an unknown state.");
|
|
283
|
+
|
|
284
|
+
// src/provider/use-when-provider-ready.ts
|
|
285
|
+
var import_web_sdk4 = require("@openfeature/web-sdk");
|
|
286
|
+
var import_react4 = require("react");
|
|
287
|
+
function useWhenProviderReady(options) {
|
|
288
|
+
const [, updateState] = (0, import_react4.useState)();
|
|
289
|
+
const client = useOpenFeatureClient();
|
|
290
|
+
const defaultedOptions = { ...DEFAULT_OPTIONS, ...useProviderOptions(), ...normalizeOptions(options) };
|
|
291
|
+
(0, import_react4.useEffect)(() => {
|
|
292
|
+
if (defaultedOptions.suspendUntilReady && client.providerStatus === import_web_sdk4.ProviderStatus.NOT_READY) {
|
|
293
|
+
suspend(client, updateState, import_web_sdk4.ProviderEvents.Ready);
|
|
189
294
|
}
|
|
190
|
-
};
|
|
295
|
+
}, []);
|
|
296
|
+
return client.providerStatus === import_web_sdk4.ProviderStatus.READY;
|
|
191
297
|
}
|
|
192
298
|
|
|
193
299
|
// src/index.ts
|