@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 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.2.3-experimental">
20
- <img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v0.2.3-experimental&color=blue&style=for-the-badge" />
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
- - [Multiple Providers and Domains](#multiple-providers-and-domains)
54
- - [Re-rendering with Context Changes](#re-rendering-with-context-changes)
55
- - [Re-rendering with Flag Configuration Changes](#re-rendering-with-flag-configuration-changes)
56
- - [Suspense Support](#suspense-support)
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, useBooleanFlagValue, useBooleanFlagDetails, OpenFeature, InMemoryProvider } from '@openfeature/react-sdk';
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
- const newMessage = useBooleanFlagValue('new-message', false);
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
- {newMessage ? <p>Welcome to this OpenFeature-enabled React app!</p> : <p>Welcome to this React app.</p>}
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 detailed flag evaluation hooks to evaluate the flag and get additional information about the flag and the evaluation.
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
- ### Multiple Providers and Domains
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
- ### Re-rendering with Context Changes
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 newMessage = useBooleanFlagValue('new-message', false, { updateOnContextChanged: false });
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
- ### Re-rendering with Flag Configuration Changes
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 newMessage = useBooleanFlagValue('new-message', false, { updateOnConfigurationChanged: false });
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
- ### Suspense Support
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 newMessage = useBooleanFlagValue('new-message', false);
247
+ const showNewMessage = useBooleanFlagValue('new-message', false);
226
248
 
227
249
  return (
228
250
  <>
229
- {newMessage ? (
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 import_react = require("react");
51
+ var import_react3 = require("react");
50
52
 
51
- // src/provider.tsx
52
- var React = __toESM(require("react"));
53
- var import_web_sdk = require("@openfeature/web-sdk");
54
- var Context = React.createContext(void 0);
55
- var OpenFeatureProvider = ({ client, domain, children }) => {
56
- if (!client) {
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 useOpenFeatureClient = () => {
62
- const client = React.useContext(Context);
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
- var DEFAULT_OPTIONS = {
73
- updateOnContextChanged: true,
74
- updateOnConfigurationChanged: true,
75
- suspendUntilReady: true,
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(flagKey, defaultValue, (client) => {
83
- return client.getBooleanDetails;
84
- }, options);
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(flagKey, defaultValue, (client) => {
91
- return client.getStringDetails;
92
- }, options);
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(flagKey, defaultValue, (client) => {
99
- return client.getNumberDetails;
100
- }, options);
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(flagKey, defaultValue, (client) => {
107
- return client.getObjectDetails;
108
- }, options);
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, import_react.useState)();
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(client, updateState, import_web_sdk2.ProviderEvents.ContextChanged, import_web_sdk2.ProviderEvents.ConfigurationChanged, import_web_sdk2.ProviderEvents.Ready);
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, import_react.useEffect)(() => {
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, import_react.useEffect)(() => {
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
- function suspend(client, updateState, ...resumeEvents) {
150
- let suspendResolver;
151
- const suspendPromise = new Promise((resolve) => {
152
- suspendResolver = () => {
153
- resolve();
154
- resumeEvents.forEach((e) => {
155
- client.removeHandler(e, suspendResolver);
156
- });
157
- client.removeHandler(import_web_sdk2.ProviderEvents.Error, suspendResolver);
158
- };
159
- resumeEvents.forEach((e) => {
160
- client.addHandler(e, suspendResolver);
161
- });
162
- client.addHandler(import_web_sdk2.ProviderEvents.Error, suspendResolver);
163
- });
164
- updateState(suspenseWrapper(suspendPromise));
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
- function suspenseWrapper(promise) {
167
- let status = 0 /* Pending */;
168
- let result;
169
- const suspended = promise.then(
170
- (value) => {
171
- status = 1 /* Success */;
172
- result = value;
173
- },
174
- (error) => {
175
- status = 2 /* Error */;
176
- result = error;
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