@openfeature/react-sdk 0.2.4-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.4-experimental">
20
- <img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v0.2.4-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,7 +90,9 @@ The following list contains the peer dependencies of `@openfeature/react-sdk` wi
87
90
 
88
91
  ### Usage
89
92
 
90
- The `OpenFeatureProvider` represents a scope for feature flag evaluations within a React application.
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.
91
96
  It binds an OpenFeature client to all evaluations within child components, and allows the use of evaluation hooks.
92
97
  The example below shows how to use the `OpenFeatureProvider` with OpenFeature's `InMemoryProvider`.
93
98
 
@@ -123,9 +128,15 @@ function App() {
123
128
  </OpenFeatureProvider>
124
129
  );
125
130
  }
131
+ ```
132
+
133
+ #### Evaluation hooks
126
134
 
135
+ Within the provider, you can use the various evaluation hooks to evaluate flags.
136
+
137
+ ```tsx
127
138
  function Page() {
128
- // Use the "query-style" flag evaluation hook.
139
+ // Use the "query-style" flag evaluation hook, specifying a flag-key and a default value.
129
140
  const { value: showNewMessage } = useFlag('new-message', true);
130
141
  return (
131
142
  <div className="App">
@@ -135,9 +146,8 @@ function Page() {
135
146
  </div>
136
147
  )
137
148
  }
138
-
139
- export default App;
140
149
  ```
150
+
141
151
  You can use the strongly-typed flag value and flag evaluation detail hooks as well, if you prefer.
142
152
 
143
153
  ```tsx
@@ -159,8 +169,7 @@ const {
159
169
  } = useBooleanFlagDetails('new-message', false);
160
170
  ```
161
171
 
162
- ### Multiple Providers and Domains
163
-
172
+ #### Multiple Providers and Domains
164
173
 
165
174
  Multiple providers can be used by passing a `domain` to the `OpenFeatureProvider`:
166
175
 
@@ -183,11 +192,11 @@ OpenFeature.getClient('my-domain');
183
192
 
184
193
  For more information about `domains`, refer to the [web SDK](https://github.com/open-feature/js-sdk/blob/main/packages/client/README.md).
185
194
 
186
- ### Re-rendering with Context Changes
195
+ #### Re-rendering with Context Changes
187
196
 
188
197
  By default, if the OpenFeature [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context) is modified, components will be re-rendered.
189
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).
190
- 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)):
191
200
 
192
201
  ```tsx
193
202
  function Page() {
@@ -200,11 +209,11 @@ function Page() {
200
209
 
201
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).
202
211
 
203
- ### Re-rendering with Flag Configuration Changes
212
+ #### Re-rendering with Flag Configuration Changes
204
213
 
205
214
  By default, if the underlying provider emits a `ConfigurationChanged` event, components will be re-rendered.
206
215
  This is useful if you want your UI to immediately reflect changes in the backend flag configuration.
207
- 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)):
208
217
 
209
218
  ```tsx
210
219
  function Page() {
@@ -217,11 +226,11 @@ function Page() {
217
226
 
218
227
  Note that if your provider doesn't support updates, this configuration has no impact.
219
228
 
220
- ### Suspense Support
229
+ #### Suspense Support
221
230
 
222
231
  Frequently, providers need to perform some initial startup tasks.
223
- It may be desireable not to display components with feature flags until this is complete.
224
- 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.
225
234
 
226
235
  ```tsx
227
236
  function Content() {
@@ -252,6 +261,37 @@ function Fallback() {
252
261
  // component to render before READY.
253
262
  return <p>Waiting for provider to be ready...</p>;
254
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>
255
295
  ```
256
296
 
257
297
  ## Resources
package/dist/cjs/index.js CHANGED
@@ -41,41 +41,99 @@ __export(src_exports, {
41
41
  useObjectFlagValue: () => useObjectFlagValue,
42
42
  useOpenFeatureClient: () => useOpenFeatureClient,
43
43
  useStringFlagDetails: () => useStringFlagDetails,
44
- useStringFlagValue: () => useStringFlagValue
44
+ useStringFlagValue: () => useStringFlagValue,
45
+ useWhenProviderReady: () => useWhenProviderReady
45
46
  });
46
47
  module.exports = __toCommonJS(src_exports);
47
48
 
48
49
  // src/evaluation/use-feature-flag.ts
49
50
  var import_web_sdk2 = require("@openfeature/web-sdk");
50
- var import_react = require("react");
51
+ var import_react3 = require("react");
51
52
 
52
- // src/provider/provider.tsx
53
- var React = __toESM(require("react"));
54
- var import_web_sdk = require("@openfeature/web-sdk");
55
- var Context = React.createContext(void 0);
56
- var OpenFeatureProvider = ({ client, domain, children }) => {
57
- if (!client) {
58
- client = import_web_sdk.OpenFeature.getClient(domain);
59
- }
60
- 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
59
+ };
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
+ };
61
71
  };
62
- var useOpenFeatureClient = () => {
63
- const client = React.useContext(Context);
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) || {};
64
128
  if (!client) {
65
129
  throw new Error(
66
130
  "No OpenFeature client available - components using OpenFeature must be wrapped with an <OpenFeatureProvider>"
67
131
  );
68
132
  }
69
133
  return client;
70
- };
134
+ }
71
135
 
72
136
  // src/evaluation/use-feature-flag.ts
73
- var DEFAULT_OPTIONS = {
74
- updateOnContextChanged: true,
75
- updateOnConfigurationChanged: true,
76
- suspendUntilReady: true,
77
- suspendWhileReconciling: false
78
- };
79
137
  function useFlag(flagKey, defaultValue, options) {
80
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));
81
139
  return query;
@@ -133,8 +191,8 @@ function useObjectFlagDetails(flagKey, defaultValue, options) {
133
191
  );
134
192
  }
135
193
  function attachHandlersAndResolve(flagKey, defaultValue, resolver, options) {
136
- const defaultedOptions = { ...DEFAULT_OPTIONS, ...options };
137
- const [, updateState] = (0, import_react.useState)();
194
+ const defaultedOptions = { ...DEFAULT_OPTIONS, ...useProviderOptions(), ...normalizeOptions(options) };
195
+ const [, updateState] = (0, import_react3.useState)();
138
196
  const client = useOpenFeatureClient();
139
197
  const forceUpdate = () => {
140
198
  updateState({});
@@ -148,7 +206,7 @@ function attachHandlersAndResolve(flagKey, defaultValue, resolver, options) {
148
206
  import_web_sdk2.ProviderEvents.Ready
149
207
  );
150
208
  };
151
- (0, import_react.useEffect)(() => {
209
+ (0, import_react3.useEffect)(() => {
152
210
  if (client.providerStatus === import_web_sdk2.ProviderStatus.NOT_READY) {
153
211
  client.addHandler(import_web_sdk2.ProviderEvents.Ready, forceUpdate);
154
212
  if (defaultedOptions.suspendUntilReady) {
@@ -167,7 +225,7 @@ function attachHandlersAndResolve(flagKey, defaultValue, resolver, options) {
167
225
  client.removeHandler(import_web_sdk2.ProviderEvents.Reconciling, suspendRef);
168
226
  };
169
227
  }, []);
170
- (0, import_react.useEffect)(() => {
228
+ (0, import_react3.useEffect)(() => {
171
229
  if (defaultedOptions.updateOnConfigurationChanged) {
172
230
  client.addHandler(import_web_sdk2.ProviderEvents.ConfigurationChanged, forceUpdate);
173
231
  }
@@ -177,46 +235,6 @@ function attachHandlersAndResolve(flagKey, defaultValue, resolver, options) {
177
235
  }, []);
178
236
  return resolver(client).call(client, flagKey, defaultValue);
179
237
  }
180
- function suspend(client, updateState, ...resumeEvents) {
181
- let suspendResolver;
182
- const suspendPromise = new Promise((resolve) => {
183
- suspendResolver = () => {
184
- resolve();
185
- resumeEvents.forEach((e) => {
186
- client.removeHandler(e, suspendResolver);
187
- });
188
- client.removeHandler(import_web_sdk2.ProviderEvents.Error, suspendResolver);
189
- };
190
- resumeEvents.forEach((e) => {
191
- client.addHandler(e, suspendResolver);
192
- });
193
- client.addHandler(import_web_sdk2.ProviderEvents.Error, suspendResolver);
194
- });
195
- updateState(suspenseWrapper(suspendPromise));
196
- }
197
- function suspenseWrapper(promise) {
198
- let status = 0 /* Pending */;
199
- let result;
200
- const suspended = promise.then((value) => {
201
- status = 1 /* Success */;
202
- result = value;
203
- }).catch((error) => {
204
- status = 2 /* Error */;
205
- result = error;
206
- });
207
- return () => {
208
- switch (status) {
209
- case 0 /* Pending */:
210
- throw suspended;
211
- case 1 /* Success */:
212
- return result;
213
- case 2 /* Error */:
214
- throw result;
215
- default:
216
- throw new Error("Suspending promise is in an unknown state.");
217
- }
218
- };
219
- }
220
238
  var HookFlagQuery = class {
221
239
  constructor(_details) {
222
240
  this._details = _details;
@@ -253,6 +271,31 @@ var HookFlagQuery = class {
253
271
  }
254
272
  };
255
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);
282
+ }
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);
294
+ }
295
+ }, []);
296
+ return client.providerStatus === import_web_sdk4.ProviderStatus.READY;
297
+ }
298
+
256
299
  // src/index.ts
257
300
  __reExport(src_exports, require("@openfeature/web-sdk"), module.exports);
258
301
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../src/index.ts", "../../src/evaluation/use-feature-flag.ts", "../../src/provider/provider.tsx"],
4
- "sourcesContent": ["export * from './evaluation';\nexport * from './query';\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 {\n Client,\n EvaluationDetails,\n FlagEvaluationOptions,\n FlagValue,\n JsonValue,\n ProviderEvents,\n ProviderStatus,\n StandardResolutionReasons,\n} from '@openfeature/web-sdk';\nimport { Dispatch, SetStateAction, useEffect, useState } from 'react';\nimport { useOpenFeatureClient } from '../provider';\nimport { FlagQuery } from '../query';\n\ntype ReactFlagEvaluationOptions = {\n /**\n * Suspend flag evaluations while the provider is not ready.\n * Set to false if you don't want to show suspense fallbacks until the provider is initialized.\n * Defaults to true.\n */\n suspendUntilReady?: boolean;\n /**\n * Suspend flag evaluations while the provider's context is being reconciled.\n * Set to true if you want to show suspense fallbacks while flags are re-evaluated after context changes.\n * Defaults to false.\n */\n suspendWhileReconciling?: 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 suspendUntilReady: true,\n suspendWhileReconciling: false,\n};\n\nenum SuspendState {\n Pending,\n Success,\n Error,\n}\n\n// This type is a bit wild-looking, but I think we need it.\n// We have to use the conditional, because otherwise useFlag('key', false) would return false, not boolean (too constrained).\n// We have a duplicate for the hook return below, this one is just used for casting because the name isn't as clear\ntype ConstrainedFlagQuery<T> = FlagQuery<\n T extends boolean\n ? boolean\n : T extends number\n ? number\n : T extends string\n ? string\n : T extends JsonValue\n ? T\n : JsonValue\n>;\n\n/**\n * Evaluates a feature flag generically, returning an react-flavored queryable object.\n * The resolver method to use is based on the type of the defaultValue.\n * For type-specific hooks, use {@link useBooleanFlagValue}, {@link useBooleanFlagDetails} and equivalents.\n * By default, components will re-render when the flag value changes.\n * @param {string} flagKey the flag identifier\n * @template {FlagValue} T A optional generic argument constraining the default.\n * @param {T} defaultValue the default value; used to determine what resolved type should be used.\n * @param {ReactFlagEvaluationOptions} options for this evaluation\n * @returns { FlagQuery } a queryable object containing useful information about the flag.\n */\nexport function useFlag<T extends FlagValue = FlagValue>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): FlagQuery<\nT extends boolean\n ? boolean\n : T extends number\n ? number\n : T extends string\n ? string\n : T extends JsonValue\n ? T\n : JsonValue\n> {\n // use the default value to determine the resolver to call\n const query =\n typeof defaultValue === 'boolean'\n ? new HookFlagQuery<boolean>(useBooleanFlagDetails(flagKey, defaultValue, options))\n : typeof defaultValue === 'number'\n ? new HookFlagQuery<number>(useNumberFlagDetails(flagKey, defaultValue, options))\n : typeof defaultValue === 'string'\n ? new HookFlagQuery<string>(useStringFlagDetails(flagKey, defaultValue, options))\n : new HookFlagQuery<JsonValue>(useObjectFlagDetails(flagKey, defaultValue, options));\n // TS sees this as HookFlagQuery<JsonValue>, because the compiler isn't aware of the `typeof` checks above.\n return query as unknown as ConstrainedFlagQuery<T>;\n}\n\n/**\n * Evaluates a feature flag, returning a boolean.\n * By default, components will re-render when the flag value changes.\n * For a generic hook returning a queryable interface, see {@link useFlag}.\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(\n flagKey: string,\n defaultValue: boolean,\n options?: ReactFlagEvaluationOptions,\n): 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 * For a generic hook returning a queryable interface, see {@link useFlag}.\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(\n flagKey: string,\n defaultValue: boolean,\n options?: ReactFlagEvaluationOptions,\n): EvaluationDetails<boolean> {\n return attachHandlersAndResolve(\n flagKey,\n defaultValue,\n (client) => {\n return client.getBooleanDetails;\n },\n options,\n );\n}\n\n/**\n * Evaluates a feature flag, returning a string.\n * By default, components will re-render when the flag value changes.\n * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): string {\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 * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): EvaluationDetails<string> {\n return attachHandlersAndResolve(\n flagKey,\n defaultValue,\n (client) => {\n return client.getStringDetails<T>;\n },\n options,\n );\n}\n\n/**\n * Evaluates a feature flag, returning a number.\n * By default, components will re-render when the flag value changes.\n * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): number {\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 * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): EvaluationDetails<number> {\n return attachHandlersAndResolve(\n flagKey,\n defaultValue,\n (client) => {\n return client.getNumberDetails<T>;\n },\n options,\n );\n}\n\n/**\n * Evaluates a feature flag, returning an object.\n * By default, components will re-render when the flag value changes.\n * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): 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 * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): EvaluationDetails<T> {\n return attachHandlersAndResolve(\n flagKey,\n defaultValue,\n (client) => {\n return client.getObjectDetails<T>;\n },\n options,\n );\n}\n\nfunction attachHandlersAndResolve<T extends FlagValue>(\n flagKey: string,\n defaultValue: T,\n resolver: (client: Client) => (flagKey: string, defaultValue: T) => EvaluationDetails<T>,\n options?: ReactFlagEvaluationOptions,\n): EvaluationDetails<T> {\n const defaultedOptions = { ...DEFAULT_OPTIONS, ...options };\n const [, updateState] = useState<object | undefined>();\n const client = useOpenFeatureClient();\n const forceUpdate = () => {\n updateState({});\n };\n const suspendRef = () => {\n suspend(\n client,\n updateState,\n ProviderEvents.ContextChanged,\n ProviderEvents.ConfigurationChanged,\n ProviderEvents.Ready,\n );\n };\n\n useEffect(() => {\n if (client.providerStatus === ProviderStatus.NOT_READY) {\n // update when the provider is ready\n client.addHandler(ProviderEvents.Ready, forceUpdate);\n if (defaultedOptions.suspendUntilReady) {\n suspend(client, updateState, ProviderEvents.Ready);\n }\n }\n\n if (defaultedOptions.updateOnContextChanged) {\n // update when the context changes\n client.addHandler(ProviderEvents.ContextChanged, forceUpdate);\n if (defaultedOptions.suspendWhileReconciling) {\n client.addHandler(ProviderEvents.Reconciling, suspendRef);\n }\n }\n return () => {\n // cleanup the handlers\n client.removeHandler(ProviderEvents.Ready, forceUpdate);\n client.removeHandler(ProviderEvents.ContextChanged, forceUpdate);\n client.removeHandler(ProviderEvents.Reconciling, suspendRef);\n };\n }, []);\n\n useEffect(() => {\n if (defaultedOptions.updateOnConfigurationChanged) {\n // update when the provider configuration changes\n client.addHandler(ProviderEvents.ConfigurationChanged, forceUpdate);\n }\n return () => {\n // cleanup the handlers\n client.removeHandler(ProviderEvents.ConfigurationChanged, forceUpdate);\n };\n }, []);\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 * @param {ProviderEvents[]} resumeEvents list of events which will resume the suspend\n */\nfunction suspend(\n client: Client,\n updateState: Dispatch<SetStateAction<object | undefined>>,\n ...resumeEvents: ProviderEvents[]\n) {\n let suspendResolver: () => void;\n\n const suspendPromise = new Promise<void>((resolve) => {\n suspendResolver = () => {\n resolve();\n resumeEvents.forEach((e) => {\n client.removeHandler(e, suspendResolver); // remove handlers once they've run\n });\n client.removeHandler(ProviderEvents.Error, suspendResolver);\n };\n resumeEvents.forEach((e) => {\n client.addHandler(e, suspendResolver);\n });\n client.addHandler(ProviderEvents.Error, suspendResolver); // we never want to throw, resolve with errors - we may make this configurable later\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\n .then((value) => {\n status = SuspendState.Success;\n result = value;\n })\n .catch((error) => {\n status = SuspendState.Error;\n result = error;\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}\n\n// FlagQuery implementation, do not export\nclass HookFlagQuery<T extends FlagValue = FlagValue> implements FlagQuery {\n constructor(private _details: EvaluationDetails<T>) {}\n\n get details() {\n return this._details;\n }\n\n get value() {\n return this._details?.value;\n }\n\n get variant() {\n return this._details.variant;\n }\n\n get flagMetadata() {\n return this._details.flagMetadata;\n }\n\n get reason() {\n return this._details.reason;\n }\n\n get isError() {\n return !!this._details?.errorCode || this._details.reason == StandardResolutionReasons.ERROR;\n }\n\n get errorCode() {\n return this._details?.errorCode;\n }\n\n get errorMessage() {\n return this._details?.errorMessage;\n }\n\n get isAuthoritative() {\n return (\n !this.isError &&\n this._details.reason != StandardResolutionReasons.STALE &&\n this._details.reason != StandardResolutionReasons.DISABLED\n );\n }\n\n get type() {\n return typeof this._details.value;\n }\n}\n", "import * as React from 'react';\nimport { Client, OpenFeature } from '@openfeature/web-sdk';\n\ntype ClientOrDomain =\n | {\n /**\n * An identifier which logically binds clients with providers\n * @see OpenFeature.setProvider() and overloads.\n */\n domain?: string;\n client?: never;\n }\n | {\n /**\n * OpenFeature client to use.\n */\n client?: Client;\n domain?: never;\n };\n\ntype ProviderProps = {\n children?: React.ReactNode;\n} & ClientOrDomain;\n\nconst Context = React.createContext<Client | undefined>(undefined);\n\nexport const OpenFeatureProvider = ({ client, domain, children }: ProviderProps) => {\n if (!client) {\n client = OpenFeature.getClient(domain);\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
- "names": ["import_web_sdk"]
3
+ "sources": ["../../src/index.ts", "../../src/evaluation/use-feature-flag.ts", "../../src/common/options.ts", "../../src/common/suspend.ts", "../../src/provider/context.ts", "../../src/provider/use-open-feature-client.ts", "../../src/provider/provider.tsx", "../../src/provider/use-when-provider-ready.ts"],
4
+ "sourcesContent": ["export * from './evaluation';\nexport * from './query';\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 {\n Client,\n EvaluationDetails,\n FlagValue,\n JsonValue,\n ProviderEvents,\n ProviderStatus,\n StandardResolutionReasons\n} from '@openfeature/web-sdk';\nimport { useEffect, useState } from 'react';\nimport { DEFAULT_OPTIONS, ReactFlagEvaluationOptions, normalizeOptions } from '../common/options';\nimport { suspend } from '../common/suspend';\nimport { useProviderOptions } from '../provider/context';\nimport { useOpenFeatureClient } from '../provider/use-open-feature-client';\nimport { FlagQuery } from '../query';\n\n// This type is a bit wild-looking, but I think we need it.\n// We have to use the conditional, because otherwise useFlag('key', false) would return false, not boolean (too constrained).\n// We have a duplicate for the hook return below, this one is just used for casting because the name isn't as clear\ntype ConstrainedFlagQuery<T> = FlagQuery<\n T extends boolean\n ? boolean\n : T extends number\n ? number\n : T extends string\n ? string\n : T extends JsonValue\n ? T\n : JsonValue\n>;\n\n/**\n * Evaluates a feature flag generically, returning an react-flavored queryable object.\n * The resolver method to use is based on the type of the defaultValue.\n * For type-specific hooks, use {@link useBooleanFlagValue}, {@link useBooleanFlagDetails} and equivalents.\n * By default, components will re-render when the flag value changes.\n * @param {string} flagKey the flag identifier\n * @template {FlagValue} T A optional generic argument constraining the default.\n * @param {T} defaultValue the default value; used to determine what resolved type should be used.\n * @param {ReactFlagEvaluationOptions} options for this evaluation\n * @returns { FlagQuery } a queryable object containing useful information about the flag.\n */\nexport function useFlag<T extends FlagValue = FlagValue>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): FlagQuery<\nT extends boolean\n ? boolean\n : T extends number\n ? number\n : T extends string\n ? string\n : T extends JsonValue\n ? T\n : JsonValue\n> {\n // use the default value to determine the resolver to call\n const query =\n typeof defaultValue === 'boolean'\n ? new HookFlagQuery<boolean>(useBooleanFlagDetails(flagKey, defaultValue, options))\n : typeof defaultValue === 'number'\n ? new HookFlagQuery<number>(useNumberFlagDetails(flagKey, defaultValue, options))\n : typeof defaultValue === 'string'\n ? new HookFlagQuery<string>(useStringFlagDetails(flagKey, defaultValue, options))\n : new HookFlagQuery<JsonValue>(useObjectFlagDetails(flagKey, defaultValue, options));\n // TS sees this as HookFlagQuery<JsonValue>, because the compiler isn't aware of the `typeof` checks above.\n return query as unknown as ConstrainedFlagQuery<T>;\n}\n\n/**\n * Evaluates a feature flag, returning a boolean.\n * By default, components will re-render when the flag value changes.\n * For a generic hook returning a queryable interface, see {@link useFlag}.\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(\n flagKey: string,\n defaultValue: boolean,\n options?: ReactFlagEvaluationOptions,\n): 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 * For a generic hook returning a queryable interface, see {@link useFlag}.\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(\n flagKey: string,\n defaultValue: boolean,\n options?: ReactFlagEvaluationOptions,\n): EvaluationDetails<boolean> {\n return attachHandlersAndResolve(\n flagKey,\n defaultValue,\n (client) => {\n return client.getBooleanDetails;\n },\n options,\n );\n}\n\n/**\n * Evaluates a feature flag, returning a string.\n * By default, components will re-render when the flag value changes.\n * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): string {\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 * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): EvaluationDetails<string> {\n return attachHandlersAndResolve(\n flagKey,\n defaultValue,\n (client) => {\n return client.getStringDetails<T>;\n },\n options,\n );\n}\n\n/**\n * Evaluates a feature flag, returning a number.\n * By default, components will re-render when the flag value changes.\n * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): number {\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 * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): EvaluationDetails<number> {\n return attachHandlersAndResolve(\n flagKey,\n defaultValue,\n (client) => {\n return client.getNumberDetails<T>;\n },\n options,\n );\n}\n\n/**\n * Evaluates a feature flag, returning an object.\n * By default, components will re-render when the flag value changes.\n * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): 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 * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): EvaluationDetails<T> {\n return attachHandlersAndResolve(\n flagKey,\n defaultValue,\n (client) => {\n return client.getObjectDetails<T>;\n },\n options,\n );\n}\n\nfunction attachHandlersAndResolve<T extends FlagValue>(\n flagKey: string,\n defaultValue: T,\n resolver: (client: Client) => (flagKey: string, defaultValue: T) => EvaluationDetails<T>,\n options?: ReactFlagEvaluationOptions,\n): EvaluationDetails<T> {\n // highest priority > evaluation hook options > provider options > default options > lowest priority\n const defaultedOptions = { ...DEFAULT_OPTIONS, ...useProviderOptions(), ...normalizeOptions(options)};\n const [, updateState] = useState<object | undefined>();\n const client = useOpenFeatureClient();\n const forceUpdate = () => {\n updateState({});\n };\n const suspendRef = () => {\n suspend(\n client,\n updateState,\n ProviderEvents.ContextChanged,\n ProviderEvents.ConfigurationChanged,\n ProviderEvents.Ready,\n );\n };\n\n useEffect(() => {\n if (client.providerStatus === ProviderStatus.NOT_READY) {\n // update when the provider is ready\n client.addHandler(ProviderEvents.Ready, forceUpdate);\n if (defaultedOptions.suspendUntilReady) {\n suspend(client, updateState, ProviderEvents.Ready);\n }\n }\n\n if (defaultedOptions.updateOnContextChanged) {\n // update when the context changes\n client.addHandler(ProviderEvents.ContextChanged, forceUpdate);\n if (defaultedOptions.suspendWhileReconciling) {\n client.addHandler(ProviderEvents.Reconciling, suspendRef);\n }\n }\n return () => {\n // cleanup the handlers\n client.removeHandler(ProviderEvents.Ready, forceUpdate);\n client.removeHandler(ProviderEvents.ContextChanged, forceUpdate);\n client.removeHandler(ProviderEvents.Reconciling, suspendRef);\n };\n }, []);\n\n useEffect(() => {\n if (defaultedOptions.updateOnConfigurationChanged) {\n // update when the provider configuration changes\n client.addHandler(ProviderEvents.ConfigurationChanged, forceUpdate);\n }\n return () => {\n // cleanup the handlers\n client.removeHandler(ProviderEvents.ConfigurationChanged, forceUpdate);\n };\n }, []);\n\n return resolver(client).call(client, flagKey, defaultValue);\n}\n\n// FlagQuery implementation, do not export\nclass HookFlagQuery<T extends FlagValue = FlagValue> implements FlagQuery {\n constructor(private _details: EvaluationDetails<T>) {}\n\n get details() {\n return this._details;\n }\n\n get value() {\n return this._details?.value;\n }\n\n get variant() {\n return this._details.variant;\n }\n\n get flagMetadata() {\n return this._details.flagMetadata;\n }\n\n get reason() {\n return this._details.reason;\n }\n\n get isError() {\n return !!this._details?.errorCode || this._details.reason == StandardResolutionReasons.ERROR;\n }\n\n get errorCode() {\n return this._details?.errorCode;\n }\n\n get errorMessage() {\n return this._details?.errorMessage;\n }\n\n get isAuthoritative() {\n return (\n !this.isError &&\n this._details.reason != StandardResolutionReasons.STALE &&\n this._details.reason != StandardResolutionReasons.DISABLED\n );\n }\n\n get type() {\n return typeof this._details.value;\n }\n}\n", "import { FlagEvaluationOptions } from '@openfeature/web-sdk';\n\nexport type ReactFlagEvaluationOptions = ({\n /**\n * Enable or disable all suspense functionality.\n * Cannot be used in conjunction with `suspendUntilReady` and `suspendWhileReconciling` options.\n */\n suspend?: boolean;\n suspendUntilReady?: never;\n suspendWhileReconciling?: never;\n} | {\n /**\n * Suspend flag evaluations while the provider is not ready.\n * Set to false if you don't want to show suspense fallbacks until the provider is initialized.\n * Defaults to true.\n * Cannot be used in conjunction with `suspend` option.\n */\n suspendUntilReady?: boolean;\n /**\n * Suspend flag evaluations while the provider's context is being reconciled.\n * Set to true if you want to show suspense fallbacks while flags are re-evaluated after context changes.\n * Defaults to true.\n * Cannot be used in conjunction with `suspend` option.\n */\n suspendWhileReconciling?: boolean;\n suspend?: never;\n}) & {\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\nexport type NormalizedOptions = Omit<ReactFlagEvaluationOptions, 'suspend'>;\n\n/**\n * Default options.\n * DO NOT EXPORT PUBLICLY\n * @internal\n */\nexport const DEFAULT_OPTIONS: ReactFlagEvaluationOptions = {\n updateOnContextChanged: true,\n updateOnConfigurationChanged: true,\n suspendUntilReady: true,\n suspendWhileReconciling: true,\n};\n\n/**\n * Returns normalization options (all `undefined` fields removed, and `suspend` decomposed to `suspendUntilReady` and `suspendWhileReconciling`).\n * DO NOT EXPORT PUBLICLY\n * @internal\n * @param {ReactFlagEvaluationOptions} options options to normalize\n * @returns {NormalizedOptions} normalized options\n */\nexport const normalizeOptions: (options?: ReactFlagEvaluationOptions) => NormalizedOptions = (options?: ReactFlagEvaluationOptions) => {\n const defaultOptionsIfMissing = !options ? {} : options;\n // fall-back the suspense options\n const suspendUntilReady = 'suspendUntilReady' in defaultOptionsIfMissing ? defaultOptionsIfMissing.suspendUntilReady : defaultOptionsIfMissing.suspend;\n const suspendWhileReconciling = 'suspendWhileReconciling' in defaultOptionsIfMissing ? defaultOptionsIfMissing.suspendWhileReconciling : defaultOptionsIfMissing.suspend;\n return {\n updateOnContextChanged: defaultOptionsIfMissing.updateOnContextChanged,\n updateOnConfigurationChanged: defaultOptionsIfMissing.updateOnConfigurationChanged,\n // only return these if properly set (no undefined to allow overriding with spread)\n ...(typeof suspendUntilReady === 'boolean' && {suspendUntilReady}),\n ...(typeof suspendWhileReconciling === 'boolean' && {suspendWhileReconciling}),\n };\n};\n", "import { Client, ProviderEvents } from '@openfeature/web-sdk';\nimport { Dispatch, SetStateAction } from 'react';\n\nenum SuspendState {\n Pending,\n Success,\n Error,\n}\n\n/**\n * Suspend function. If this runs, components using the calling hook will be suspended.\n * DO NOT EXPORT PUBLICLY \n * @internal\n * @param {Client} client the OpenFeature client\n * @param {Function} updateState the state update function\n * @param {ProviderEvents[]} resumeEvents list of events which will resume the suspend\n */\nexport function suspend(\n client: Client,\n updateState: Dispatch<SetStateAction<object | undefined>>,\n ...resumeEvents: ProviderEvents[]\n) {\n let suspendResolver: () => void;\n\n const suspendPromise = new Promise<void>((resolve) => {\n suspendResolver = () => {\n resolve();\n resumeEvents.forEach((e) => {\n client.removeHandler(e, suspendResolver); // remove handlers once they've run\n });\n client.removeHandler(ProviderEvents.Error, suspendResolver);\n };\n resumeEvents.forEach((e) => {\n client.addHandler(e, suspendResolver);\n });\n client.addHandler(ProviderEvents.Error, suspendResolver); // we never want to throw, resolve with errors - we may make this configurable later\n });\n updateState(suspenseWrapper(suspendPromise));\n}\n\n/**\n * Promise wrapper that throws unresolved promises to support React suspense.\n * DO NOT EXPORT PUBLICLY\n * @internal\n * @param {Promise<T>} promise to wrap\n * @template T flag type\n * @returns {Function} suspense-compliant lambda\n */\nexport function suspenseWrapper<T>(promise: Promise<T>) {\n let status: SuspendState = SuspendState.Pending;\n let result: T;\n\n const suspended = promise\n .then((value) => {\n status = SuspendState.Success;\n result = value;\n })\n .catch((error) => {\n status = SuspendState.Error;\n result = error;\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}\n", "import { Client } from '@openfeature/web-sdk';\nimport React from 'react';\nimport { NormalizedOptions, ReactFlagEvaluationOptions, normalizeOptions } from '../common/options';\n\n/**\n * The underlying React context.\n * DO NOT EXPORT PUBLICLY\n * @internal\n */\nexport const Context = React.createContext<{ client: Client; options: ReactFlagEvaluationOptions } | undefined>(undefined);\n\n/**\n * Get a normalized copy of the options used for this OpenFeatureProvider, see {@link normalizeOptions}.\n * DO NOT EXPORT PUBLICLY\n * @internal\n * @returns {NormalizedOptions} normalized options the defaulted options, not defaulted or normalized.\n */\nexport function useProviderOptions(): NormalizedOptions {\n const { options } = React.useContext(Context) || {};\n return normalizeOptions(options);\n}\n", "import React from 'react';\nimport { Context } from './context';\nimport { Client } from '@openfeature/web-sdk';\n\n/**\n * Get the {@link Client} instance for this OpenFeatureProvider context.\n * Note that the provider to which this is bound is determined by the OpenFeatureProvider's domain.\n * @returns {Client} client for this scope\n */\nexport function useOpenFeatureClient(): Client {\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", "import { Client, OpenFeature } from '@openfeature/web-sdk';\nimport * as React from 'react';\nimport { ReactFlagEvaluationOptions } from '../common/options';\nimport { Context } from './context';\n\ntype ClientOrDomain =\n | {\n /**\n * An identifier which logically binds clients with providers\n * @see OpenFeature.setProvider() and overloads.\n */\n domain?: string;\n client?: never;\n }\n | {\n /**\n * OpenFeature client to use.\n */\n client?: Client;\n domain?: never;\n };\n\ntype ProviderProps = {\n children?: React.ReactNode;\n} & ClientOrDomain &\n ReactFlagEvaluationOptions;\n\n /**\n * Provides a scope for evaluating feature flags by binding a client to all child components.\n * @param {ProviderProps} properties props for the context provider\n * @returns {OpenFeatureProvider} context provider\n */\nexport function OpenFeatureProvider({ client, domain, children, ...options }: ProviderProps) {\n if (!client) {\n client = OpenFeature.getClient(domain);\n }\n\n return <Context.Provider value={{ client, options }}>{children}</Context.Provider>;\n}\n", "import { ProviderEvents, ProviderStatus } from '@openfeature/web-sdk';\nimport { useEffect, useState } from 'react';\nimport { DEFAULT_OPTIONS, ReactFlagEvaluationOptions, normalizeOptions } from '../common/options';\nimport { suspend } from '../common/suspend';\nimport { useProviderOptions } from './context';\nimport { useOpenFeatureClient } from './use-open-feature-client';\n\ntype Options = Pick<ReactFlagEvaluationOptions, 'suspendUntilReady'>;\n\n/**\n * Utility hook that triggers suspense until the provider is {@link ProviderStatus.READY}, without evaluating any flags.\n * Especially useful for React v16/17 \"Legacy Suspense\", in which siblings to suspending components are\n * initially mounted and then hidden (see: https://github.com/reactwg/react-18/discussions/7).\n * @param {Options} options options for suspense\n * @returns {boolean} boolean indicating if provider is {@link ProviderStatus.READY}, useful if suspense is disabled and you want to handle loaders on your own\n */\nexport function useWhenProviderReady(options?: Options): boolean {\n const [, updateState] = useState<object | undefined>();\n const client = useOpenFeatureClient();\n // highest priority > evaluation hook options > provider options > default options > lowest priority\n const defaultedOptions = { ...DEFAULT_OPTIONS, ...useProviderOptions(), ...normalizeOptions(options)};\n\n useEffect(() => {\n if (defaultedOptions.suspendUntilReady && client.providerStatus === ProviderStatus.NOT_READY) {\n suspend(client, updateState, ProviderEvents.Ready);\n }\n }, []);\n\n return client.providerStatus === ProviderStatus.READY;\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,kBAQO;AACP,IAAAC,gBAAoC;;;ACyC7B,IAAM,kBAA8C;AAAA,EACzD,wBAAwB;AAAA,EACxB,8BAA8B;AAAA,EAC9B,mBAAmB;AAAA,EACnB,yBAAyB;AAC3B;AASO,IAAM,mBAAgF,CAAC,YAAyC;AACrI,QAAM,0BAA0B,CAAC,UAAU,CAAC,IAAI;AAEhD,QAAM,oBAAoB,uBAAuB,0BAA0B,wBAAwB,oBAAoB,wBAAwB;AAC/I,QAAM,0BAA0B,6BAA6B,0BAA0B,wBAAwB,0BAA0B,wBAAwB;AACjK,SAAO;AAAA,IACL,wBAAwB,wBAAwB;AAAA,IAChD,8BAA8B,wBAAwB;AAAA;AAAA,IAEtD,GAAI,OAAO,sBAAsB,aAAa,EAAC,kBAAiB;AAAA,IAChE,GAAI,OAAO,4BAA4B,aAAa,EAAC,wBAAuB;AAAA,EAC9E;AACF;;;AC5EA,qBAAuC;AAiBhC,SAAS,QACd,QACA,gBACG,cACH;AACA,MAAI;AAEJ,QAAM,iBAAiB,IAAI,QAAc,CAAC,YAAY;AACpD,sBAAkB,MAAM;AACtB,cAAQ;AACR,mBAAa,QAAQ,CAAC,MAAM;AAC1B,eAAO,cAAc,GAAG,eAAe;AAAA,MACzC,CAAC;AACD,aAAO,cAAc,8BAAe,OAAO,eAAe;AAAA,IAC5D;AACA,iBAAa,QAAQ,CAAC,MAAM;AAC1B,aAAO,WAAW,GAAG,eAAe;AAAA,IACtC,CAAC;AACD,WAAO,WAAW,8BAAe,OAAO,eAAe;AAAA,EACzD,CAAC;AACD,cAAY,gBAAgB,cAAc,CAAC;AAC7C;AAUO,SAAS,gBAAmB,SAAqB;AACtD,MAAI,SAAuB;AAC3B,MAAI;AAEJ,QAAM,YAAY,QACf,KAAK,CAAC,UAAU;AACf,aAAS;AACT,aAAS;AAAA,EACX,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,aAAS;AACT,aAAS;AAAA,EACX,CAAC;AAEH,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;;;ACzEA,mBAAkB;AAQX,IAAM,UAAU,aAAAC,QAAM,cAAmF,MAAS;AAQlH,SAAS,qBAAwC;AACtD,QAAM,EAAE,QAAQ,IAAI,aAAAA,QAAM,WAAW,OAAO,KAAK,CAAC;AAClD,SAAO,iBAAiB,OAAO;AACjC;;;ACpBA,IAAAC,gBAAkB;AASX,SAAS,uBAA+B;AAC7C,QAAM,EAAE,OAAO,IAAI,cAAAC,QAAM,WAAW,OAAO,KAAK,CAAC;AAEjD,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;AJuBO,SAAS,QACd,SACA,cACA,SAWA;AAEA,QAAM,QACJ,OAAO,iBAAiB,YACpB,IAAI,cAAuB,sBAAsB,SAAS,cAAc,OAAO,CAAC,IAChF,OAAO,iBAAiB,WACtB,IAAI,cAAsB,qBAAqB,SAAS,cAAc,OAAO,CAAC,IAC9E,OAAO,iBAAiB,WACtB,IAAI,cAAsB,qBAAqB,SAAS,cAAc,OAAO,CAAC,IAC9E,IAAI,cAAyB,qBAAqB,SAAS,cAAc,OAAO,CAAC;AAE3F,SAAO;AACT;AAWO,SAAS,oBACd,SACA,cACA,SACS;AACT,SAAO,sBAAsB,SAAS,cAAc,OAAO,EAAE;AAC/D;AAWO,SAAS,sBACd,SACA,cACA,SAC4B;AAC5B,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,CAAC,WAAW;AACV,aAAO,OAAO;AAAA,IAChB;AAAA,IACA;AAAA,EACF;AACF;AAYO,SAAS,mBACd,SACA,cACA,SACQ;AACR,SAAO,qBAAqB,SAAS,cAAc,OAAO,EAAE;AAC9D;AAYO,SAAS,qBACd,SACA,cACA,SAC2B;AAC3B,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,CAAC,WAAW;AACV,aAAO,OAAO;AAAA,IAChB;AAAA,IACA;AAAA,EACF;AACF;AAYO,SAAS,mBACd,SACA,cACA,SACQ;AACR,SAAO,qBAAqB,SAAS,cAAc,OAAO,EAAE;AAC9D;AAYO,SAAS,qBACd,SACA,cACA,SAC2B;AAC3B,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,CAAC,WAAW;AACV,aAAO,OAAO;AAAA,IAChB;AAAA,IACA;AAAA,EACF;AACF;AAYO,SAAS,mBACd,SACA,cACA,SACG;AACH,SAAO,qBAAwB,SAAS,cAAc,OAAO,EAAE;AACjE;AAYO,SAAS,qBACd,SACA,cACA,SACsB;AACtB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,CAAC,WAAW;AACV,aAAO,OAAO;AAAA,IAChB;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,yBACP,SACA,cACA,UACA,SACsB;AAEtB,QAAM,mBAAmB,EAAE,GAAG,iBAAiB,GAAG,mBAAmB,GAAG,GAAG,iBAAiB,OAAO,EAAC;AACpG,QAAM,CAAC,EAAE,WAAW,QAAI,wBAA6B;AACrD,QAAM,SAAS,qBAAqB;AACpC,QAAM,cAAc,MAAM;AACxB,gBAAY,CAAC,CAAC;AAAA,EAChB;AACA,QAAM,aAAa,MAAM;AACvB;AAAA,MACE;AAAA,MACA;AAAA,MACA,+BAAe;AAAA,MACf,+BAAe;AAAA,MACf,+BAAe;AAAA,IACjB;AAAA,EACF;AAEA,+BAAU,MAAM;AACd,QAAI,OAAO,mBAAmB,+BAAe,WAAW;AAEtD,aAAO,WAAW,+BAAe,OAAO,WAAW;AACnD,UAAI,iBAAiB,mBAAmB;AACtC,gBAAQ,QAAQ,aAAa,+BAAe,KAAK;AAAA,MACnD;AAAA,IACF;AAEA,QAAI,iBAAiB,wBAAwB;AAE3C,aAAO,WAAW,+BAAe,gBAAgB,WAAW;AAC5D,UAAI,iBAAiB,yBAAyB;AAC5C,eAAO,WAAW,+BAAe,aAAa,UAAU;AAAA,MAC1D;AAAA,IACF;AACA,WAAO,MAAM;AAEX,aAAO,cAAc,+BAAe,OAAO,WAAW;AACtD,aAAO,cAAc,+BAAe,gBAAgB,WAAW;AAC/D,aAAO,cAAc,+BAAe,aAAa,UAAU;AAAA,IAC7D;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,+BAAU,MAAM;AACd,QAAI,iBAAiB,8BAA8B;AAEjD,aAAO,WAAW,+BAAe,sBAAsB,WAAW;AAAA,IACpE;AACA,WAAO,MAAM;AAEX,aAAO,cAAc,+BAAe,sBAAsB,WAAW;AAAA,IACvE;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO,SAAS,MAAM,EAAE,KAAK,QAAQ,SAAS,YAAY;AAC5D;AAGA,IAAM,gBAAN,MAA0E;AAAA,EACxE,YAAoB,UAAgC;AAAhC;AAAA,EAAiC;AAAA,EAErD,IAAI,UAAU;AACZ,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,QAAQ;AACV,WAAO,KAAK,UAAU;AAAA,EACxB;AAAA,EAEA,IAAI,UAAU;AACZ,WAAO,KAAK,SAAS;AAAA,EACvB;AAAA,EAEA,IAAI,eAAe;AACjB,WAAO,KAAK,SAAS;AAAA,EACvB;AAAA,EAEA,IAAI,SAAS;AACX,WAAO,KAAK,SAAS;AAAA,EACvB;AAAA,EAEA,IAAI,UAAU;AACZ,WAAO,CAAC,CAAC,KAAK,UAAU,aAAa,KAAK,SAAS,UAAU,0CAA0B;AAAA,EACzF;AAAA,EAEA,IAAI,YAAY;AACd,WAAO,KAAK,UAAU;AAAA,EACxB;AAAA,EAEA,IAAI,eAAe;AACjB,WAAO,KAAK,UAAU;AAAA,EACxB;AAAA,EAEA,IAAI,kBAAkB;AACpB,WACE,CAAC,KAAK,WACN,KAAK,SAAS,UAAU,0CAA0B,SAClD,KAAK,SAAS,UAAU,0CAA0B;AAAA,EAEtD;AAAA,EAEA,IAAI,OAAO;AACT,WAAO,OAAO,KAAK,SAAS;AAAA,EAC9B;AACF;;;AK5VA,IAAAC,kBAAoC;AACpC,IAAAC,SAAuB;AA+BhB,SAAS,oBAAoB,EAAE,QAAQ,QAAQ,UAAU,GAAG,QAAQ,GAAkB;AAC3F,MAAI,CAAC,QAAQ;AACX,aAAS,4BAAY,UAAU,MAAM;AAAA,EACvC;AAEA,SAAO,qCAAC,QAAQ,UAAR,EAAiB,OAAO,EAAE,QAAQ,QAAQ,KAAI,QAAS;AACjE;;;ACtCA,IAAAC,kBAA+C;AAC/C,IAAAC,gBAAoC;AAe7B,SAAS,qBAAqB,SAA4B;AAC/D,QAAM,CAAC,EAAE,WAAW,QAAI,wBAA6B;AACrD,QAAM,SAAS,qBAAqB;AAEpC,QAAM,mBAAmB,EAAE,GAAG,iBAAiB,GAAG,mBAAmB,GAAG,GAAG,iBAAiB,OAAO,EAAC;AAEpG,+BAAU,MAAM;AACd,QAAI,iBAAiB,qBAAqB,OAAO,mBAAmB,+BAAe,WAAW;AAC5F,cAAQ,QAAQ,aAAa,+BAAe,KAAK;AAAA,IACnD;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO,OAAO,mBAAmB,+BAAe;AAClD;;;APzBA,wBAAc,iCAJd;",
6
+ "names": ["import_web_sdk", "import_react", "React", "import_react", "React", "import_web_sdk", "React", "import_web_sdk", "import_react"]
7
7
  }
package/dist/esm/index.js CHANGED
@@ -1,38 +1,95 @@
1
1
  // src/evaluation/use-feature-flag.ts
2
2
  import {
3
- ProviderEvents,
3
+ ProviderEvents as ProviderEvents2,
4
4
  ProviderStatus,
5
5
  StandardResolutionReasons
6
6
  } from "@openfeature/web-sdk";
7
7
  import { useEffect, useState } from "react";
8
8
 
9
- // src/provider/provider.tsx
10
- import * as React from "react";
11
- import { OpenFeature } from "@openfeature/web-sdk";
12
- var Context = React.createContext(void 0);
13
- var OpenFeatureProvider = ({ client, domain, children }) => {
14
- if (!client) {
15
- client = OpenFeature.getClient(domain);
16
- }
17
- return /* @__PURE__ */ React.createElement(Context.Provider, { value: client }, children);
9
+ // src/common/options.ts
10
+ var DEFAULT_OPTIONS = {
11
+ updateOnContextChanged: true,
12
+ updateOnConfigurationChanged: true,
13
+ suspendUntilReady: true,
14
+ suspendWhileReconciling: true
15
+ };
16
+ var normalizeOptions = (options) => {
17
+ const defaultOptionsIfMissing = !options ? {} : options;
18
+ const suspendUntilReady = "suspendUntilReady" in defaultOptionsIfMissing ? defaultOptionsIfMissing.suspendUntilReady : defaultOptionsIfMissing.suspend;
19
+ const suspendWhileReconciling = "suspendWhileReconciling" in defaultOptionsIfMissing ? defaultOptionsIfMissing.suspendWhileReconciling : defaultOptionsIfMissing.suspend;
20
+ return {
21
+ updateOnContextChanged: defaultOptionsIfMissing.updateOnContextChanged,
22
+ updateOnConfigurationChanged: defaultOptionsIfMissing.updateOnConfigurationChanged,
23
+ // only return these if properly set (no undefined to allow overriding with spread)
24
+ ...typeof suspendUntilReady === "boolean" && { suspendUntilReady },
25
+ ...typeof suspendWhileReconciling === "boolean" && { suspendWhileReconciling }
26
+ };
18
27
  };
19
- var useOpenFeatureClient = () => {
20
- const client = React.useContext(Context);
28
+
29
+ // src/common/suspend.ts
30
+ import { ProviderEvents } from "@openfeature/web-sdk";
31
+ function suspend(client, updateState, ...resumeEvents) {
32
+ let suspendResolver;
33
+ const suspendPromise = new Promise((resolve) => {
34
+ suspendResolver = () => {
35
+ resolve();
36
+ resumeEvents.forEach((e) => {
37
+ client.removeHandler(e, suspendResolver);
38
+ });
39
+ client.removeHandler(ProviderEvents.Error, suspendResolver);
40
+ };
41
+ resumeEvents.forEach((e) => {
42
+ client.addHandler(e, suspendResolver);
43
+ });
44
+ client.addHandler(ProviderEvents.Error, suspendResolver);
45
+ });
46
+ updateState(suspenseWrapper(suspendPromise));
47
+ }
48
+ function suspenseWrapper(promise) {
49
+ let status = 0 /* Pending */;
50
+ let result;
51
+ const suspended = promise.then((value) => {
52
+ status = 1 /* Success */;
53
+ result = value;
54
+ }).catch((error) => {
55
+ status = 2 /* Error */;
56
+ result = error;
57
+ });
58
+ return () => {
59
+ switch (status) {
60
+ case 0 /* Pending */:
61
+ throw suspended;
62
+ case 1 /* Success */:
63
+ return result;
64
+ case 2 /* Error */:
65
+ throw result;
66
+ default:
67
+ throw new Error("Suspending promise is in an unknown state.");
68
+ }
69
+ };
70
+ }
71
+
72
+ // src/provider/context.ts
73
+ import React from "react";
74
+ var Context = React.createContext(void 0);
75
+ function useProviderOptions() {
76
+ const { options } = React.useContext(Context) || {};
77
+ return normalizeOptions(options);
78
+ }
79
+
80
+ // src/provider/use-open-feature-client.ts
81
+ import React2 from "react";
82
+ function useOpenFeatureClient() {
83
+ const { client } = React2.useContext(Context) || {};
21
84
  if (!client) {
22
85
  throw new Error(
23
86
  "No OpenFeature client available - components using OpenFeature must be wrapped with an <OpenFeatureProvider>"
24
87
  );
25
88
  }
26
89
  return client;
27
- };
90
+ }
28
91
 
29
92
  // src/evaluation/use-feature-flag.ts
30
- var DEFAULT_OPTIONS = {
31
- updateOnContextChanged: true,
32
- updateOnConfigurationChanged: true,
33
- suspendUntilReady: true,
34
- suspendWhileReconciling: false
35
- };
36
93
  function useFlag(flagKey, defaultValue, options) {
37
94
  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));
38
95
  return query;
@@ -90,7 +147,7 @@ function useObjectFlagDetails(flagKey, defaultValue, options) {
90
147
  );
91
148
  }
92
149
  function attachHandlersAndResolve(flagKey, defaultValue, resolver, options) {
93
- const defaultedOptions = { ...DEFAULT_OPTIONS, ...options };
150
+ const defaultedOptions = { ...DEFAULT_OPTIONS, ...useProviderOptions(), ...normalizeOptions(options) };
94
151
  const [, updateState] = useState();
95
152
  const client = useOpenFeatureClient();
96
153
  const forceUpdate = () => {
@@ -100,80 +157,40 @@ function attachHandlersAndResolve(flagKey, defaultValue, resolver, options) {
100
157
  suspend(
101
158
  client,
102
159
  updateState,
103
- ProviderEvents.ContextChanged,
104
- ProviderEvents.ConfigurationChanged,
105
- ProviderEvents.Ready
160
+ ProviderEvents2.ContextChanged,
161
+ ProviderEvents2.ConfigurationChanged,
162
+ ProviderEvents2.Ready
106
163
  );
107
164
  };
108
165
  useEffect(() => {
109
166
  if (client.providerStatus === ProviderStatus.NOT_READY) {
110
- client.addHandler(ProviderEvents.Ready, forceUpdate);
167
+ client.addHandler(ProviderEvents2.Ready, forceUpdate);
111
168
  if (defaultedOptions.suspendUntilReady) {
112
- suspend(client, updateState, ProviderEvents.Ready);
169
+ suspend(client, updateState, ProviderEvents2.Ready);
113
170
  }
114
171
  }
115
172
  if (defaultedOptions.updateOnContextChanged) {
116
- client.addHandler(ProviderEvents.ContextChanged, forceUpdate);
173
+ client.addHandler(ProviderEvents2.ContextChanged, forceUpdate);
117
174
  if (defaultedOptions.suspendWhileReconciling) {
118
- client.addHandler(ProviderEvents.Reconciling, suspendRef);
175
+ client.addHandler(ProviderEvents2.Reconciling, suspendRef);
119
176
  }
120
177
  }
121
178
  return () => {
122
- client.removeHandler(ProviderEvents.Ready, forceUpdate);
123
- client.removeHandler(ProviderEvents.ContextChanged, forceUpdate);
124
- client.removeHandler(ProviderEvents.Reconciling, suspendRef);
179
+ client.removeHandler(ProviderEvents2.Ready, forceUpdate);
180
+ client.removeHandler(ProviderEvents2.ContextChanged, forceUpdate);
181
+ client.removeHandler(ProviderEvents2.Reconciling, suspendRef);
125
182
  };
126
183
  }, []);
127
184
  useEffect(() => {
128
185
  if (defaultedOptions.updateOnConfigurationChanged) {
129
- client.addHandler(ProviderEvents.ConfigurationChanged, forceUpdate);
186
+ client.addHandler(ProviderEvents2.ConfigurationChanged, forceUpdate);
130
187
  }
131
188
  return () => {
132
- client.removeHandler(ProviderEvents.ConfigurationChanged, forceUpdate);
189
+ client.removeHandler(ProviderEvents2.ConfigurationChanged, forceUpdate);
133
190
  };
134
191
  }, []);
135
192
  return resolver(client).call(client, flagKey, defaultValue);
136
193
  }
137
- function suspend(client, updateState, ...resumeEvents) {
138
- let suspendResolver;
139
- const suspendPromise = new Promise((resolve) => {
140
- suspendResolver = () => {
141
- resolve();
142
- resumeEvents.forEach((e) => {
143
- client.removeHandler(e, suspendResolver);
144
- });
145
- client.removeHandler(ProviderEvents.Error, suspendResolver);
146
- };
147
- resumeEvents.forEach((e) => {
148
- client.addHandler(e, suspendResolver);
149
- });
150
- client.addHandler(ProviderEvents.Error, suspendResolver);
151
- });
152
- updateState(suspenseWrapper(suspendPromise));
153
- }
154
- function suspenseWrapper(promise) {
155
- let status = 0 /* Pending */;
156
- let result;
157
- const suspended = promise.then((value) => {
158
- status = 1 /* Success */;
159
- result = value;
160
- }).catch((error) => {
161
- status = 2 /* Error */;
162
- result = error;
163
- });
164
- return () => {
165
- switch (status) {
166
- case 0 /* Pending */:
167
- throw suspended;
168
- case 1 /* Success */:
169
- return result;
170
- case 2 /* Error */:
171
- throw result;
172
- default:
173
- throw new Error("Suspending promise is in an unknown state.");
174
- }
175
- };
176
- }
177
194
  var HookFlagQuery = class {
178
195
  constructor(_details) {
179
196
  this._details = _details;
@@ -210,6 +227,31 @@ var HookFlagQuery = class {
210
227
  }
211
228
  };
212
229
 
230
+ // src/provider/provider.tsx
231
+ import { OpenFeature } from "@openfeature/web-sdk";
232
+ import * as React3 from "react";
233
+ function OpenFeatureProvider({ client, domain, children, ...options }) {
234
+ if (!client) {
235
+ client = OpenFeature.getClient(domain);
236
+ }
237
+ return /* @__PURE__ */ React3.createElement(Context.Provider, { value: { client, options } }, children);
238
+ }
239
+
240
+ // src/provider/use-when-provider-ready.ts
241
+ import { ProviderEvents as ProviderEvents3, ProviderStatus as ProviderStatus2 } from "@openfeature/web-sdk";
242
+ import { useEffect as useEffect2, useState as useState2 } from "react";
243
+ function useWhenProviderReady(options) {
244
+ const [, updateState] = useState2();
245
+ const client = useOpenFeatureClient();
246
+ const defaultedOptions = { ...DEFAULT_OPTIONS, ...useProviderOptions(), ...normalizeOptions(options) };
247
+ useEffect2(() => {
248
+ if (defaultedOptions.suspendUntilReady && client.providerStatus === ProviderStatus2.NOT_READY) {
249
+ suspend(client, updateState, ProviderEvents3.Ready);
250
+ }
251
+ }, []);
252
+ return client.providerStatus === ProviderStatus2.READY;
253
+ }
254
+
213
255
  // src/index.ts
214
256
  export * from "@openfeature/web-sdk";
215
257
  export {
@@ -223,6 +265,7 @@ export {
223
265
  useObjectFlagValue,
224
266
  useOpenFeatureClient,
225
267
  useStringFlagDetails,
226
- useStringFlagValue
268
+ useStringFlagValue,
269
+ useWhenProviderReady
227
270
  };
228
271
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../src/evaluation/use-feature-flag.ts", "../../src/provider/provider.tsx", "../../src/index.ts"],
4
- "sourcesContent": ["import {\n Client,\n EvaluationDetails,\n FlagEvaluationOptions,\n FlagValue,\n JsonValue,\n ProviderEvents,\n ProviderStatus,\n StandardResolutionReasons,\n} from '@openfeature/web-sdk';\nimport { Dispatch, SetStateAction, useEffect, useState } from 'react';\nimport { useOpenFeatureClient } from '../provider';\nimport { FlagQuery } from '../query';\n\ntype ReactFlagEvaluationOptions = {\n /**\n * Suspend flag evaluations while the provider is not ready.\n * Set to false if you don't want to show suspense fallbacks until the provider is initialized.\n * Defaults to true.\n */\n suspendUntilReady?: boolean;\n /**\n * Suspend flag evaluations while the provider's context is being reconciled.\n * Set to true if you want to show suspense fallbacks while flags are re-evaluated after context changes.\n * Defaults to false.\n */\n suspendWhileReconciling?: 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 suspendUntilReady: true,\n suspendWhileReconciling: false,\n};\n\nenum SuspendState {\n Pending,\n Success,\n Error,\n}\n\n// This type is a bit wild-looking, but I think we need it.\n// We have to use the conditional, because otherwise useFlag('key', false) would return false, not boolean (too constrained).\n// We have a duplicate for the hook return below, this one is just used for casting because the name isn't as clear\ntype ConstrainedFlagQuery<T> = FlagQuery<\n T extends boolean\n ? boolean\n : T extends number\n ? number\n : T extends string\n ? string\n : T extends JsonValue\n ? T\n : JsonValue\n>;\n\n/**\n * Evaluates a feature flag generically, returning an react-flavored queryable object.\n * The resolver method to use is based on the type of the defaultValue.\n * For type-specific hooks, use {@link useBooleanFlagValue}, {@link useBooleanFlagDetails} and equivalents.\n * By default, components will re-render when the flag value changes.\n * @param {string} flagKey the flag identifier\n * @template {FlagValue} T A optional generic argument constraining the default.\n * @param {T} defaultValue the default value; used to determine what resolved type should be used.\n * @param {ReactFlagEvaluationOptions} options for this evaluation\n * @returns { FlagQuery } a queryable object containing useful information about the flag.\n */\nexport function useFlag<T extends FlagValue = FlagValue>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): FlagQuery<\nT extends boolean\n ? boolean\n : T extends number\n ? number\n : T extends string\n ? string\n : T extends JsonValue\n ? T\n : JsonValue\n> {\n // use the default value to determine the resolver to call\n const query =\n typeof defaultValue === 'boolean'\n ? new HookFlagQuery<boolean>(useBooleanFlagDetails(flagKey, defaultValue, options))\n : typeof defaultValue === 'number'\n ? new HookFlagQuery<number>(useNumberFlagDetails(flagKey, defaultValue, options))\n : typeof defaultValue === 'string'\n ? new HookFlagQuery<string>(useStringFlagDetails(flagKey, defaultValue, options))\n : new HookFlagQuery<JsonValue>(useObjectFlagDetails(flagKey, defaultValue, options));\n // TS sees this as HookFlagQuery<JsonValue>, because the compiler isn't aware of the `typeof` checks above.\n return query as unknown as ConstrainedFlagQuery<T>;\n}\n\n/**\n * Evaluates a feature flag, returning a boolean.\n * By default, components will re-render when the flag value changes.\n * For a generic hook returning a queryable interface, see {@link useFlag}.\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(\n flagKey: string,\n defaultValue: boolean,\n options?: ReactFlagEvaluationOptions,\n): 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 * For a generic hook returning a queryable interface, see {@link useFlag}.\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(\n flagKey: string,\n defaultValue: boolean,\n options?: ReactFlagEvaluationOptions,\n): EvaluationDetails<boolean> {\n return attachHandlersAndResolve(\n flagKey,\n defaultValue,\n (client) => {\n return client.getBooleanDetails;\n },\n options,\n );\n}\n\n/**\n * Evaluates a feature flag, returning a string.\n * By default, components will re-render when the flag value changes.\n * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): string {\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 * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): EvaluationDetails<string> {\n return attachHandlersAndResolve(\n flagKey,\n defaultValue,\n (client) => {\n return client.getStringDetails<T>;\n },\n options,\n );\n}\n\n/**\n * Evaluates a feature flag, returning a number.\n * By default, components will re-render when the flag value changes.\n * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): number {\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 * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): EvaluationDetails<number> {\n return attachHandlersAndResolve(\n flagKey,\n defaultValue,\n (client) => {\n return client.getNumberDetails<T>;\n },\n options,\n );\n}\n\n/**\n * Evaluates a feature flag, returning an object.\n * By default, components will re-render when the flag value changes.\n * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): 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 * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): EvaluationDetails<T> {\n return attachHandlersAndResolve(\n flagKey,\n defaultValue,\n (client) => {\n return client.getObjectDetails<T>;\n },\n options,\n );\n}\n\nfunction attachHandlersAndResolve<T extends FlagValue>(\n flagKey: string,\n defaultValue: T,\n resolver: (client: Client) => (flagKey: string, defaultValue: T) => EvaluationDetails<T>,\n options?: ReactFlagEvaluationOptions,\n): EvaluationDetails<T> {\n const defaultedOptions = { ...DEFAULT_OPTIONS, ...options };\n const [, updateState] = useState<object | undefined>();\n const client = useOpenFeatureClient();\n const forceUpdate = () => {\n updateState({});\n };\n const suspendRef = () => {\n suspend(\n client,\n updateState,\n ProviderEvents.ContextChanged,\n ProviderEvents.ConfigurationChanged,\n ProviderEvents.Ready,\n );\n };\n\n useEffect(() => {\n if (client.providerStatus === ProviderStatus.NOT_READY) {\n // update when the provider is ready\n client.addHandler(ProviderEvents.Ready, forceUpdate);\n if (defaultedOptions.suspendUntilReady) {\n suspend(client, updateState, ProviderEvents.Ready);\n }\n }\n\n if (defaultedOptions.updateOnContextChanged) {\n // update when the context changes\n client.addHandler(ProviderEvents.ContextChanged, forceUpdate);\n if (defaultedOptions.suspendWhileReconciling) {\n client.addHandler(ProviderEvents.Reconciling, suspendRef);\n }\n }\n return () => {\n // cleanup the handlers\n client.removeHandler(ProviderEvents.Ready, forceUpdate);\n client.removeHandler(ProviderEvents.ContextChanged, forceUpdate);\n client.removeHandler(ProviderEvents.Reconciling, suspendRef);\n };\n }, []);\n\n useEffect(() => {\n if (defaultedOptions.updateOnConfigurationChanged) {\n // update when the provider configuration changes\n client.addHandler(ProviderEvents.ConfigurationChanged, forceUpdate);\n }\n return () => {\n // cleanup the handlers\n client.removeHandler(ProviderEvents.ConfigurationChanged, forceUpdate);\n };\n }, []);\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 * @param {ProviderEvents[]} resumeEvents list of events which will resume the suspend\n */\nfunction suspend(\n client: Client,\n updateState: Dispatch<SetStateAction<object | undefined>>,\n ...resumeEvents: ProviderEvents[]\n) {\n let suspendResolver: () => void;\n\n const suspendPromise = new Promise<void>((resolve) => {\n suspendResolver = () => {\n resolve();\n resumeEvents.forEach((e) => {\n client.removeHandler(e, suspendResolver); // remove handlers once they've run\n });\n client.removeHandler(ProviderEvents.Error, suspendResolver);\n };\n resumeEvents.forEach((e) => {\n client.addHandler(e, suspendResolver);\n });\n client.addHandler(ProviderEvents.Error, suspendResolver); // we never want to throw, resolve with errors - we may make this configurable later\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\n .then((value) => {\n status = SuspendState.Success;\n result = value;\n })\n .catch((error) => {\n status = SuspendState.Error;\n result = error;\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}\n\n// FlagQuery implementation, do not export\nclass HookFlagQuery<T extends FlagValue = FlagValue> implements FlagQuery {\n constructor(private _details: EvaluationDetails<T>) {}\n\n get details() {\n return this._details;\n }\n\n get value() {\n return this._details?.value;\n }\n\n get variant() {\n return this._details.variant;\n }\n\n get flagMetadata() {\n return this._details.flagMetadata;\n }\n\n get reason() {\n return this._details.reason;\n }\n\n get isError() {\n return !!this._details?.errorCode || this._details.reason == StandardResolutionReasons.ERROR;\n }\n\n get errorCode() {\n return this._details?.errorCode;\n }\n\n get errorMessage() {\n return this._details?.errorMessage;\n }\n\n get isAuthoritative() {\n return (\n !this.isError &&\n this._details.reason != StandardResolutionReasons.STALE &&\n this._details.reason != StandardResolutionReasons.DISABLED\n );\n }\n\n get type() {\n return typeof this._details.value;\n }\n}\n", "import * as React from 'react';\nimport { Client, OpenFeature } from '@openfeature/web-sdk';\n\ntype ClientOrDomain =\n | {\n /**\n * An identifier which logically binds clients with providers\n * @see OpenFeature.setProvider() and overloads.\n */\n domain?: string;\n client?: never;\n }\n | {\n /**\n * OpenFeature client to use.\n */\n client?: Client;\n domain?: never;\n };\n\ntype ProviderProps = {\n children?: React.ReactNode;\n} & ClientOrDomain;\n\nconst Context = React.createContext<Client | undefined>(undefined);\n\nexport const OpenFeatureProvider = ({ client, domain, children }: ProviderProps) => {\n if (!client) {\n client = OpenFeature.getClient(domain);\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 './evaluation';\nexport * from './query';\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": "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",
6
- "names": []
3
+ "sources": ["../../src/evaluation/use-feature-flag.ts", "../../src/common/options.ts", "../../src/common/suspend.ts", "../../src/provider/context.ts", "../../src/provider/use-open-feature-client.ts", "../../src/provider/provider.tsx", "../../src/provider/use-when-provider-ready.ts", "../../src/index.ts"],
4
+ "sourcesContent": ["import {\n Client,\n EvaluationDetails,\n FlagValue,\n JsonValue,\n ProviderEvents,\n ProviderStatus,\n StandardResolutionReasons\n} from '@openfeature/web-sdk';\nimport { useEffect, useState } from 'react';\nimport { DEFAULT_OPTIONS, ReactFlagEvaluationOptions, normalizeOptions } from '../common/options';\nimport { suspend } from '../common/suspend';\nimport { useProviderOptions } from '../provider/context';\nimport { useOpenFeatureClient } from '../provider/use-open-feature-client';\nimport { FlagQuery } from '../query';\n\n// This type is a bit wild-looking, but I think we need it.\n// We have to use the conditional, because otherwise useFlag('key', false) would return false, not boolean (too constrained).\n// We have a duplicate for the hook return below, this one is just used for casting because the name isn't as clear\ntype ConstrainedFlagQuery<T> = FlagQuery<\n T extends boolean\n ? boolean\n : T extends number\n ? number\n : T extends string\n ? string\n : T extends JsonValue\n ? T\n : JsonValue\n>;\n\n/**\n * Evaluates a feature flag generically, returning an react-flavored queryable object.\n * The resolver method to use is based on the type of the defaultValue.\n * For type-specific hooks, use {@link useBooleanFlagValue}, {@link useBooleanFlagDetails} and equivalents.\n * By default, components will re-render when the flag value changes.\n * @param {string} flagKey the flag identifier\n * @template {FlagValue} T A optional generic argument constraining the default.\n * @param {T} defaultValue the default value; used to determine what resolved type should be used.\n * @param {ReactFlagEvaluationOptions} options for this evaluation\n * @returns { FlagQuery } a queryable object containing useful information about the flag.\n */\nexport function useFlag<T extends FlagValue = FlagValue>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): FlagQuery<\nT extends boolean\n ? boolean\n : T extends number\n ? number\n : T extends string\n ? string\n : T extends JsonValue\n ? T\n : JsonValue\n> {\n // use the default value to determine the resolver to call\n const query =\n typeof defaultValue === 'boolean'\n ? new HookFlagQuery<boolean>(useBooleanFlagDetails(flagKey, defaultValue, options))\n : typeof defaultValue === 'number'\n ? new HookFlagQuery<number>(useNumberFlagDetails(flagKey, defaultValue, options))\n : typeof defaultValue === 'string'\n ? new HookFlagQuery<string>(useStringFlagDetails(flagKey, defaultValue, options))\n : new HookFlagQuery<JsonValue>(useObjectFlagDetails(flagKey, defaultValue, options));\n // TS sees this as HookFlagQuery<JsonValue>, because the compiler isn't aware of the `typeof` checks above.\n return query as unknown as ConstrainedFlagQuery<T>;\n}\n\n/**\n * Evaluates a feature flag, returning a boolean.\n * By default, components will re-render when the flag value changes.\n * For a generic hook returning a queryable interface, see {@link useFlag}.\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(\n flagKey: string,\n defaultValue: boolean,\n options?: ReactFlagEvaluationOptions,\n): 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 * For a generic hook returning a queryable interface, see {@link useFlag}.\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(\n flagKey: string,\n defaultValue: boolean,\n options?: ReactFlagEvaluationOptions,\n): EvaluationDetails<boolean> {\n return attachHandlersAndResolve(\n flagKey,\n defaultValue,\n (client) => {\n return client.getBooleanDetails;\n },\n options,\n );\n}\n\n/**\n * Evaluates a feature flag, returning a string.\n * By default, components will re-render when the flag value changes.\n * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): string {\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 * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): EvaluationDetails<string> {\n return attachHandlersAndResolve(\n flagKey,\n defaultValue,\n (client) => {\n return client.getStringDetails<T>;\n },\n options,\n );\n}\n\n/**\n * Evaluates a feature flag, returning a number.\n * By default, components will re-render when the flag value changes.\n * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): number {\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 * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): EvaluationDetails<number> {\n return attachHandlersAndResolve(\n flagKey,\n defaultValue,\n (client) => {\n return client.getNumberDetails<T>;\n },\n options,\n );\n}\n\n/**\n * Evaluates a feature flag, returning an object.\n * By default, components will re-render when the flag value changes.\n * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): 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 * For a generic hook returning a queryable interface, see {@link useFlag}.\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>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationOptions,\n): EvaluationDetails<T> {\n return attachHandlersAndResolve(\n flagKey,\n defaultValue,\n (client) => {\n return client.getObjectDetails<T>;\n },\n options,\n );\n}\n\nfunction attachHandlersAndResolve<T extends FlagValue>(\n flagKey: string,\n defaultValue: T,\n resolver: (client: Client) => (flagKey: string, defaultValue: T) => EvaluationDetails<T>,\n options?: ReactFlagEvaluationOptions,\n): EvaluationDetails<T> {\n // highest priority > evaluation hook options > provider options > default options > lowest priority\n const defaultedOptions = { ...DEFAULT_OPTIONS, ...useProviderOptions(), ...normalizeOptions(options)};\n const [, updateState] = useState<object | undefined>();\n const client = useOpenFeatureClient();\n const forceUpdate = () => {\n updateState({});\n };\n const suspendRef = () => {\n suspend(\n client,\n updateState,\n ProviderEvents.ContextChanged,\n ProviderEvents.ConfigurationChanged,\n ProviderEvents.Ready,\n );\n };\n\n useEffect(() => {\n if (client.providerStatus === ProviderStatus.NOT_READY) {\n // update when the provider is ready\n client.addHandler(ProviderEvents.Ready, forceUpdate);\n if (defaultedOptions.suspendUntilReady) {\n suspend(client, updateState, ProviderEvents.Ready);\n }\n }\n\n if (defaultedOptions.updateOnContextChanged) {\n // update when the context changes\n client.addHandler(ProviderEvents.ContextChanged, forceUpdate);\n if (defaultedOptions.suspendWhileReconciling) {\n client.addHandler(ProviderEvents.Reconciling, suspendRef);\n }\n }\n return () => {\n // cleanup the handlers\n client.removeHandler(ProviderEvents.Ready, forceUpdate);\n client.removeHandler(ProviderEvents.ContextChanged, forceUpdate);\n client.removeHandler(ProviderEvents.Reconciling, suspendRef);\n };\n }, []);\n\n useEffect(() => {\n if (defaultedOptions.updateOnConfigurationChanged) {\n // update when the provider configuration changes\n client.addHandler(ProviderEvents.ConfigurationChanged, forceUpdate);\n }\n return () => {\n // cleanup the handlers\n client.removeHandler(ProviderEvents.ConfigurationChanged, forceUpdate);\n };\n }, []);\n\n return resolver(client).call(client, flagKey, defaultValue);\n}\n\n// FlagQuery implementation, do not export\nclass HookFlagQuery<T extends FlagValue = FlagValue> implements FlagQuery {\n constructor(private _details: EvaluationDetails<T>) {}\n\n get details() {\n return this._details;\n }\n\n get value() {\n return this._details?.value;\n }\n\n get variant() {\n return this._details.variant;\n }\n\n get flagMetadata() {\n return this._details.flagMetadata;\n }\n\n get reason() {\n return this._details.reason;\n }\n\n get isError() {\n return !!this._details?.errorCode || this._details.reason == StandardResolutionReasons.ERROR;\n }\n\n get errorCode() {\n return this._details?.errorCode;\n }\n\n get errorMessage() {\n return this._details?.errorMessage;\n }\n\n get isAuthoritative() {\n return (\n !this.isError &&\n this._details.reason != StandardResolutionReasons.STALE &&\n this._details.reason != StandardResolutionReasons.DISABLED\n );\n }\n\n get type() {\n return typeof this._details.value;\n }\n}\n", "import { FlagEvaluationOptions } from '@openfeature/web-sdk';\n\nexport type ReactFlagEvaluationOptions = ({\n /**\n * Enable or disable all suspense functionality.\n * Cannot be used in conjunction with `suspendUntilReady` and `suspendWhileReconciling` options.\n */\n suspend?: boolean;\n suspendUntilReady?: never;\n suspendWhileReconciling?: never;\n} | {\n /**\n * Suspend flag evaluations while the provider is not ready.\n * Set to false if you don't want to show suspense fallbacks until the provider is initialized.\n * Defaults to true.\n * Cannot be used in conjunction with `suspend` option.\n */\n suspendUntilReady?: boolean;\n /**\n * Suspend flag evaluations while the provider's context is being reconciled.\n * Set to true if you want to show suspense fallbacks while flags are re-evaluated after context changes.\n * Defaults to true.\n * Cannot be used in conjunction with `suspend` option.\n */\n suspendWhileReconciling?: boolean;\n suspend?: never;\n}) & {\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\nexport type NormalizedOptions = Omit<ReactFlagEvaluationOptions, 'suspend'>;\n\n/**\n * Default options.\n * DO NOT EXPORT PUBLICLY\n * @internal\n */\nexport const DEFAULT_OPTIONS: ReactFlagEvaluationOptions = {\n updateOnContextChanged: true,\n updateOnConfigurationChanged: true,\n suspendUntilReady: true,\n suspendWhileReconciling: true,\n};\n\n/**\n * Returns normalization options (all `undefined` fields removed, and `suspend` decomposed to `suspendUntilReady` and `suspendWhileReconciling`).\n * DO NOT EXPORT PUBLICLY\n * @internal\n * @param {ReactFlagEvaluationOptions} options options to normalize\n * @returns {NormalizedOptions} normalized options\n */\nexport const normalizeOptions: (options?: ReactFlagEvaluationOptions) => NormalizedOptions = (options?: ReactFlagEvaluationOptions) => {\n const defaultOptionsIfMissing = !options ? {} : options;\n // fall-back the suspense options\n const suspendUntilReady = 'suspendUntilReady' in defaultOptionsIfMissing ? defaultOptionsIfMissing.suspendUntilReady : defaultOptionsIfMissing.suspend;\n const suspendWhileReconciling = 'suspendWhileReconciling' in defaultOptionsIfMissing ? defaultOptionsIfMissing.suspendWhileReconciling : defaultOptionsIfMissing.suspend;\n return {\n updateOnContextChanged: defaultOptionsIfMissing.updateOnContextChanged,\n updateOnConfigurationChanged: defaultOptionsIfMissing.updateOnConfigurationChanged,\n // only return these if properly set (no undefined to allow overriding with spread)\n ...(typeof suspendUntilReady === 'boolean' && {suspendUntilReady}),\n ...(typeof suspendWhileReconciling === 'boolean' && {suspendWhileReconciling}),\n };\n};\n", "import { Client, ProviderEvents } from '@openfeature/web-sdk';\nimport { Dispatch, SetStateAction } from 'react';\n\nenum SuspendState {\n Pending,\n Success,\n Error,\n}\n\n/**\n * Suspend function. If this runs, components using the calling hook will be suspended.\n * DO NOT EXPORT PUBLICLY \n * @internal\n * @param {Client} client the OpenFeature client\n * @param {Function} updateState the state update function\n * @param {ProviderEvents[]} resumeEvents list of events which will resume the suspend\n */\nexport function suspend(\n client: Client,\n updateState: Dispatch<SetStateAction<object | undefined>>,\n ...resumeEvents: ProviderEvents[]\n) {\n let suspendResolver: () => void;\n\n const suspendPromise = new Promise<void>((resolve) => {\n suspendResolver = () => {\n resolve();\n resumeEvents.forEach((e) => {\n client.removeHandler(e, suspendResolver); // remove handlers once they've run\n });\n client.removeHandler(ProviderEvents.Error, suspendResolver);\n };\n resumeEvents.forEach((e) => {\n client.addHandler(e, suspendResolver);\n });\n client.addHandler(ProviderEvents.Error, suspendResolver); // we never want to throw, resolve with errors - we may make this configurable later\n });\n updateState(suspenseWrapper(suspendPromise));\n}\n\n/**\n * Promise wrapper that throws unresolved promises to support React suspense.\n * DO NOT EXPORT PUBLICLY\n * @internal\n * @param {Promise<T>} promise to wrap\n * @template T flag type\n * @returns {Function} suspense-compliant lambda\n */\nexport function suspenseWrapper<T>(promise: Promise<T>) {\n let status: SuspendState = SuspendState.Pending;\n let result: T;\n\n const suspended = promise\n .then((value) => {\n status = SuspendState.Success;\n result = value;\n })\n .catch((error) => {\n status = SuspendState.Error;\n result = error;\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}\n", "import { Client } from '@openfeature/web-sdk';\nimport React from 'react';\nimport { NormalizedOptions, ReactFlagEvaluationOptions, normalizeOptions } from '../common/options';\n\n/**\n * The underlying React context.\n * DO NOT EXPORT PUBLICLY\n * @internal\n */\nexport const Context = React.createContext<{ client: Client; options: ReactFlagEvaluationOptions } | undefined>(undefined);\n\n/**\n * Get a normalized copy of the options used for this OpenFeatureProvider, see {@link normalizeOptions}.\n * DO NOT EXPORT PUBLICLY\n * @internal\n * @returns {NormalizedOptions} normalized options the defaulted options, not defaulted or normalized.\n */\nexport function useProviderOptions(): NormalizedOptions {\n const { options } = React.useContext(Context) || {};\n return normalizeOptions(options);\n}\n", "import React from 'react';\nimport { Context } from './context';\nimport { Client } from '@openfeature/web-sdk';\n\n/**\n * Get the {@link Client} instance for this OpenFeatureProvider context.\n * Note that the provider to which this is bound is determined by the OpenFeatureProvider's domain.\n * @returns {Client} client for this scope\n */\nexport function useOpenFeatureClient(): Client {\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", "import { Client, OpenFeature } from '@openfeature/web-sdk';\nimport * as React from 'react';\nimport { ReactFlagEvaluationOptions } from '../common/options';\nimport { Context } from './context';\n\ntype ClientOrDomain =\n | {\n /**\n * An identifier which logically binds clients with providers\n * @see OpenFeature.setProvider() and overloads.\n */\n domain?: string;\n client?: never;\n }\n | {\n /**\n * OpenFeature client to use.\n */\n client?: Client;\n domain?: never;\n };\n\ntype ProviderProps = {\n children?: React.ReactNode;\n} & ClientOrDomain &\n ReactFlagEvaluationOptions;\n\n /**\n * Provides a scope for evaluating feature flags by binding a client to all child components.\n * @param {ProviderProps} properties props for the context provider\n * @returns {OpenFeatureProvider} context provider\n */\nexport function OpenFeatureProvider({ client, domain, children, ...options }: ProviderProps) {\n if (!client) {\n client = OpenFeature.getClient(domain);\n }\n\n return <Context.Provider value={{ client, options }}>{children}</Context.Provider>;\n}\n", "import { ProviderEvents, ProviderStatus } from '@openfeature/web-sdk';\nimport { useEffect, useState } from 'react';\nimport { DEFAULT_OPTIONS, ReactFlagEvaluationOptions, normalizeOptions } from '../common/options';\nimport { suspend } from '../common/suspend';\nimport { useProviderOptions } from './context';\nimport { useOpenFeatureClient } from './use-open-feature-client';\n\ntype Options = Pick<ReactFlagEvaluationOptions, 'suspendUntilReady'>;\n\n/**\n * Utility hook that triggers suspense until the provider is {@link ProviderStatus.READY}, without evaluating any flags.\n * Especially useful for React v16/17 \"Legacy Suspense\", in which siblings to suspending components are\n * initially mounted and then hidden (see: https://github.com/reactwg/react-18/discussions/7).\n * @param {Options} options options for suspense\n * @returns {boolean} boolean indicating if provider is {@link ProviderStatus.READY}, useful if suspense is disabled and you want to handle loaders on your own\n */\nexport function useWhenProviderReady(options?: Options): boolean {\n const [, updateState] = useState<object | undefined>();\n const client = useOpenFeatureClient();\n // highest priority > evaluation hook options > provider options > default options > lowest priority\n const defaultedOptions = { ...DEFAULT_OPTIONS, ...useProviderOptions(), ...normalizeOptions(options)};\n\n useEffect(() => {\n if (defaultedOptions.suspendUntilReady && client.providerStatus === ProviderStatus.NOT_READY) {\n suspend(client, updateState, ProviderEvents.Ready);\n }\n }, []);\n\n return client.providerStatus === ProviderStatus.READY;\n}\n", "export * from './evaluation';\nexport * from './query';\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": "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",
6
+ "names": ["ProviderEvents", "React", "React", "ProviderEvents", "React", "ProviderEvents", "ProviderStatus", "useEffect", "useState", "useState", "useEffect", "ProviderStatus", "ProviderEvents"]
7
7
  }
package/dist/types.d.ts CHANGED
@@ -1,8 +1,49 @@
1
- import { FlagValue as FlagValue$1, JsonValue, EvaluationDetails as EvaluationDetails$1, FlagEvaluationOptions, Client } from '@openfeature/web-sdk';
1
+ import { FlagEvaluationOptions, FlagValue as FlagValue$1, JsonValue, EvaluationDetails as EvaluationDetails$1, Client } from '@openfeature/web-sdk';
2
2
  export * from '@openfeature/web-sdk';
3
3
  import { FlagValue, EvaluationDetails, FlagMetadata, StandardResolutionReasons, ErrorCode } from '@openfeature/core';
4
4
  import * as React from 'react';
5
5
 
6
+ type ReactFlagEvaluationOptions = ({
7
+ /**
8
+ * Enable or disable all suspense functionality.
9
+ * Cannot be used in conjunction with `suspendUntilReady` and `suspendWhileReconciling` options.
10
+ */
11
+ suspend?: boolean;
12
+ suspendUntilReady?: never;
13
+ suspendWhileReconciling?: never;
14
+ } | {
15
+ /**
16
+ * Suspend flag evaluations while the provider is not ready.
17
+ * Set to false if you don't want to show suspense fallbacks until the provider is initialized.
18
+ * Defaults to true.
19
+ * Cannot be used in conjunction with `suspend` option.
20
+ */
21
+ suspendUntilReady?: boolean;
22
+ /**
23
+ * Suspend flag evaluations while the provider's context is being reconciled.
24
+ * Set to true if you want to show suspense fallbacks while flags are re-evaluated after context changes.
25
+ * Defaults to true.
26
+ * Cannot be used in conjunction with `suspend` option.
27
+ */
28
+ suspendWhileReconciling?: boolean;
29
+ suspend?: never;
30
+ }) & {
31
+ /**
32
+ * Update the component if the provider emits a ConfigurationChanged event.
33
+ * Set to false to prevent components from re-rendering when flag value changes
34
+ * are received by the associated provider.
35
+ * Defaults to true.
36
+ */
37
+ updateOnConfigurationChanged?: boolean;
38
+ /**
39
+ * Update the component when the OpenFeature context changes.
40
+ * Set to false to prevent components from re-rendering when attributes which
41
+ * may be factors in flag evaluation change.
42
+ * Defaults to true.
43
+ */
44
+ updateOnContextChanged?: boolean;
45
+ } & FlagEvaluationOptions;
46
+
6
47
  interface FlagQuery<T extends FlagValue = FlagValue> {
7
48
  /**
8
49
  * A structure representing the result of the flag evaluation process
@@ -51,34 +92,6 @@ interface FlagQuery<T extends FlagValue = FlagValue> {
51
92
  readonly type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function';
52
93
  }
53
94
 
54
- type ReactFlagEvaluationOptions = {
55
- /**
56
- * Suspend flag evaluations while the provider is not ready.
57
- * Set to false if you don't want to show suspense fallbacks until the provider is initialized.
58
- * Defaults to true.
59
- */
60
- suspendUntilReady?: boolean;
61
- /**
62
- * Suspend flag evaluations while the provider's context is being reconciled.
63
- * Set to true if you want to show suspense fallbacks while flags are re-evaluated after context changes.
64
- * Defaults to false.
65
- */
66
- suspendWhileReconciling?: boolean;
67
- /**
68
- * Update the component if the provider emits a ConfigurationChanged event.
69
- * Set to false to prevent components from re-rendering when flag value changes
70
- * are received by the associated provider.
71
- * Defaults to true.
72
- */
73
- updateOnConfigurationChanged?: boolean;
74
- /**
75
- * Update the component when the OpenFeature context changes.
76
- * Set to false to prevent components from re-rendering when attributes which
77
- * may be factors in flag evaluation change.
78
- * Defaults to true.
79
- */
80
- updateOnContextChanged?: boolean;
81
- } & FlagEvaluationOptions;
82
95
  /**
83
96
  * Evaluates a feature flag generically, returning an react-flavored queryable object.
84
97
  * The resolver method to use is based on the type of the defaultValue.
@@ -194,8 +207,29 @@ type ClientOrDomain = {
194
207
  };
195
208
  type ProviderProps = {
196
209
  children?: React.ReactNode;
197
- } & ClientOrDomain;
198
- declare const OpenFeatureProvider: ({ client, domain, children }: ProviderProps) => React.JSX.Element;
199
- declare const useOpenFeatureClient: () => Client;
210
+ } & ClientOrDomain & ReactFlagEvaluationOptions;
211
+ /**
212
+ * Provides a scope for evaluating feature flags by binding a client to all child components.
213
+ * @param {ProviderProps} properties props for the context provider
214
+ * @returns {OpenFeatureProvider} context provider
215
+ */
216
+ declare function OpenFeatureProvider({ client, domain, children, ...options }: ProviderProps): React.JSX.Element;
217
+
218
+ /**
219
+ * Get the {@link Client} instance for this OpenFeatureProvider context.
220
+ * Note that the provider to which this is bound is determined by the OpenFeatureProvider's domain.
221
+ * @returns {Client} client for this scope
222
+ */
223
+ declare function useOpenFeatureClient(): Client;
224
+
225
+ type Options = Pick<ReactFlagEvaluationOptions, 'suspendUntilReady'>;
226
+ /**
227
+ * Utility hook that triggers suspense until the provider is {@link ProviderStatus.READY}, without evaluating any flags.
228
+ * Especially useful for React v16/17 "Legacy Suspense", in which siblings to suspending components are
229
+ * initially mounted and then hidden (see: https://github.com/reactwg/react-18/discussions/7).
230
+ * @param {Options} options options for suspense
231
+ * @returns {boolean} boolean indicating if provider is {@link ProviderStatus.READY}, useful if suspense is disabled and you want to handle loaders on your own
232
+ */
233
+ declare function useWhenProviderReady(options?: Options): boolean;
200
234
 
201
- export { FlagQuery, OpenFeatureProvider, useBooleanFlagDetails, useBooleanFlagValue, useFlag, useNumberFlagDetails, useNumberFlagValue, useObjectFlagDetails, useObjectFlagValue, useOpenFeatureClient, useStringFlagDetails, useStringFlagValue };
235
+ export { FlagQuery, OpenFeatureProvider, useBooleanFlagDetails, useBooleanFlagValue, useFlag, useNumberFlagDetails, useNumberFlagValue, useObjectFlagDetails, useObjectFlagValue, useOpenFeatureClient, useStringFlagDetails, useStringFlagValue, useWhenProviderReady };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openfeature/react-sdk",
3
- "version": "0.2.4-experimental",
3
+ "version": "0.3.0-experimental",
4
4
  "description": "OpenFeature React SDK",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "files": [