@openfeature/react-sdk 0.4.10 → 1.0.0
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 +2 -2
- package/dist/cjs/index.js +151 -87
- package/dist/cjs/index.js.map +4 -4
- package/dist/esm/index.js +126 -65
- package/dist/esm/index.js.map +4 -4
- package/dist/types.d.ts +9 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
<img alt="Specification" src="https://img.shields.io/static/v1?label=specification&message=v0.8.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-
|
|
20
|
-
<img alt="Release" src="https://img.shields.io/static/v1?label=release&message=
|
|
19
|
+
<a href="https://github.com/open-feature/js-sdk/releases/tag/react-sdk-v1.0.0">
|
|
20
|
+
<img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v1.0.0&color=blue&style=for-the-badge" />
|
|
21
21
|
</a>
|
|
22
22
|
<!-- x-release-please-end -->
|
|
23
23
|
<br/>
|
package/dist/cjs/index.js
CHANGED
|
@@ -78,8 +78,8 @@ var __async = (__this, __arguments, generator) => {
|
|
|
78
78
|
};
|
|
79
79
|
|
|
80
80
|
// src/index.ts
|
|
81
|
-
var
|
|
82
|
-
__export(
|
|
81
|
+
var index_exports = {};
|
|
82
|
+
__export(index_exports, {
|
|
83
83
|
OpenFeatureProvider: () => OpenFeatureProvider,
|
|
84
84
|
OpenFeatureTestProvider: () => OpenFeatureTestProvider,
|
|
85
85
|
useBooleanFlagDetails: () => useBooleanFlagDetails,
|
|
@@ -91,17 +91,18 @@ __export(src_exports, {
|
|
|
91
91
|
useObjectFlagDetails: () => useObjectFlagDetails,
|
|
92
92
|
useObjectFlagValue: () => useObjectFlagValue,
|
|
93
93
|
useOpenFeatureClient: () => useOpenFeatureClient,
|
|
94
|
+
useOpenFeatureClientStatus: () => useOpenFeatureClientStatus,
|
|
94
95
|
useStringFlagDetails: () => useStringFlagDetails,
|
|
95
96
|
useStringFlagValue: () => useStringFlagValue,
|
|
96
97
|
useSuspenseFlag: () => useSuspenseFlag,
|
|
97
98
|
useTrack: () => useTrack,
|
|
98
99
|
useWhenProviderReady: () => useWhenProviderReady
|
|
99
100
|
});
|
|
100
|
-
module.exports = __toCommonJS(
|
|
101
|
+
module.exports = __toCommonJS(index_exports);
|
|
101
102
|
|
|
102
103
|
// src/evaluation/use-feature-flag.ts
|
|
103
|
-
var
|
|
104
|
-
var
|
|
104
|
+
var import_web_sdk5 = require("@openfeature/web-sdk");
|
|
105
|
+
var import_react6 = require("react");
|
|
105
106
|
|
|
106
107
|
// src/internal/context.ts
|
|
107
108
|
var import_react = __toESM(require("react"));
|
|
@@ -145,67 +146,131 @@ var DEFAULT_OPTIONS = {
|
|
|
145
146
|
var normalizeOptions = (options = {}) => {
|
|
146
147
|
const updateOnContextChanged = options.updateOnContextChanged;
|
|
147
148
|
const updateOnConfigurationChanged = options.updateOnConfigurationChanged;
|
|
148
|
-
const
|
|
149
|
+
const suspendUntilReady = "suspendUntilReady" in options ? options.suspendUntilReady : options.suspend;
|
|
149
150
|
const suspendWhileReconciling = "suspendWhileReconciling" in options ? options.suspendWhileReconciling : options.suspend;
|
|
150
|
-
return __spreadValues(__spreadValues(__spreadValues(__spreadValues({}, typeof
|
|
151
|
+
return __spreadValues(__spreadValues(__spreadValues(__spreadValues({}, typeof suspendUntilReady === "boolean" && { suspendUntilReady }), typeof suspendWhileReconciling === "boolean" && { suspendWhileReconciling }), typeof updateOnContextChanged === "boolean" && { updateOnContextChanged }), typeof updateOnConfigurationChanged === "boolean" && { updateOnConfigurationChanged });
|
|
151
152
|
};
|
|
152
153
|
|
|
153
154
|
// src/internal/suspense.ts
|
|
154
155
|
var import_web_sdk = require("@openfeature/web-sdk");
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
156
|
+
|
|
157
|
+
// src/internal/use.ts
|
|
158
|
+
var import_react2 = __toESM(require("react"));
|
|
159
|
+
var use = import_react2.default.use || // This extra generic is to avoid TypeScript mixing up the generic and JSX syntax
|
|
160
|
+
// and emitting an error.
|
|
161
|
+
// We assume that this is only for the `use(thenable)` case, not `use(context)`.
|
|
162
|
+
// https://github.com/facebook/react/blob/aed00dacfb79d17c53218404c52b1c7aa59c4a89/packages/react-server/src/ReactFizzThenable.js#L45
|
|
163
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
164
|
+
((thenable) => {
|
|
165
|
+
switch (thenable.status) {
|
|
166
|
+
case "pending":
|
|
167
|
+
throw thenable;
|
|
168
|
+
case "fulfilled":
|
|
169
|
+
return thenable.value;
|
|
170
|
+
case "rejected":
|
|
171
|
+
throw thenable.reason;
|
|
172
|
+
default:
|
|
173
|
+
thenable.status = "pending";
|
|
174
|
+
thenable.then(
|
|
175
|
+
(v) => {
|
|
176
|
+
thenable.status = "fulfilled";
|
|
177
|
+
thenable.value = v;
|
|
178
|
+
},
|
|
179
|
+
(e) => {
|
|
180
|
+
thenable.status = "rejected";
|
|
181
|
+
thenable.reason = e;
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
throw thenable;
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// src/internal/suspense.ts
|
|
189
|
+
var globalProviderSuspenseStatus = /* @__PURE__ */ new WeakMap();
|
|
190
|
+
function suspendUntilInitialized(provider, client) {
|
|
191
|
+
const statusPromiseRef = globalProviderSuspenseStatus.get(provider);
|
|
192
|
+
if (!statusPromiseRef) {
|
|
193
|
+
const statusPromise = provider !== import_web_sdk.NOOP_PROVIDER ? isProviderReady(client) : Promise.resolve();
|
|
194
|
+
globalProviderSuspenseStatus.set(provider, statusPromise);
|
|
195
|
+
use(statusPromise);
|
|
196
|
+
} else {
|
|
197
|
+
use(statusPromiseRef);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function suspendUntilReconciled(client) {
|
|
201
|
+
use(isProviderReady(client));
|
|
202
|
+
}
|
|
203
|
+
function isProviderReady(client) {
|
|
204
|
+
return __async(this, null, function* () {
|
|
205
|
+
const controller = new AbortController();
|
|
206
|
+
try {
|
|
207
|
+
return yield new Promise((resolve, reject) => {
|
|
208
|
+
client.addHandler(import_web_sdk.ProviderEvents.Ready, resolve, { signal: controller.signal });
|
|
209
|
+
client.addHandler(import_web_sdk.ProviderEvents.Error, reject, { signal: controller.signal });
|
|
210
|
+
});
|
|
211
|
+
} finally {
|
|
212
|
+
controller.abort();
|
|
213
|
+
}
|
|
166
214
|
});
|
|
167
215
|
}
|
|
168
216
|
|
|
169
217
|
// src/provider/use-open-feature-client.ts
|
|
170
|
-
var
|
|
218
|
+
var import_react3 = __toESM(require("react"));
|
|
219
|
+
|
|
220
|
+
// src/internal/errors.ts
|
|
221
|
+
var context = "Components using OpenFeature must be wrapped with an <OpenFeatureProvider>.";
|
|
222
|
+
var tip = "If you are seeing this in a test, see: https://openfeature.dev/docs/reference/technologies/client/web/react#testing";
|
|
223
|
+
var MissingContextError = class extends Error {
|
|
224
|
+
constructor(reason) {
|
|
225
|
+
super(`${reason}: ${context} ${tip}`);
|
|
226
|
+
this.name = "MissingContextError";
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
// src/provider/use-open-feature-client.ts
|
|
171
231
|
function useOpenFeatureClient() {
|
|
172
|
-
const { client } =
|
|
232
|
+
const { client } = import_react3.default.useContext(Context) || {};
|
|
173
233
|
if (!client) {
|
|
174
|
-
throw new
|
|
175
|
-
"No OpenFeature client available - components using OpenFeature must be wrapped with an <OpenFeatureProvider>. If you are seeing this in a test, see: https://openfeature.dev/docs/reference/technologies/client/web/react#testing"
|
|
176
|
-
);
|
|
234
|
+
throw new MissingContextError("No OpenFeature client available");
|
|
177
235
|
}
|
|
178
236
|
return client;
|
|
179
237
|
}
|
|
180
238
|
|
|
181
239
|
// src/provider/use-open-feature-client-status.ts
|
|
182
|
-
var
|
|
240
|
+
var import_react4 = require("react");
|
|
183
241
|
var import_web_sdk2 = require("@openfeature/web-sdk");
|
|
184
242
|
function useOpenFeatureClientStatus() {
|
|
185
243
|
const client = useOpenFeatureClient();
|
|
186
|
-
const [status, setStatus] = (0,
|
|
187
|
-
|
|
244
|
+
const [status, setStatus] = (0, import_react4.useState)(client.providerStatus);
|
|
245
|
+
const controller = new AbortController();
|
|
246
|
+
(0, import_react4.useEffect)(() => {
|
|
188
247
|
const updateStatus = () => setStatus(client.providerStatus);
|
|
189
|
-
client.addHandler(import_web_sdk2.ProviderEvents.ConfigurationChanged, updateStatus);
|
|
190
|
-
client.addHandler(import_web_sdk2.ProviderEvents.ContextChanged, updateStatus);
|
|
191
|
-
client.addHandler(import_web_sdk2.ProviderEvents.Error, updateStatus);
|
|
192
|
-
client.addHandler(import_web_sdk2.ProviderEvents.Ready, updateStatus);
|
|
193
|
-
client.addHandler(import_web_sdk2.ProviderEvents.Stale, updateStatus);
|
|
194
|
-
client.addHandler(import_web_sdk2.ProviderEvents.Reconciling, updateStatus);
|
|
248
|
+
client.addHandler(import_web_sdk2.ProviderEvents.ConfigurationChanged, updateStatus, { signal: controller.signal });
|
|
249
|
+
client.addHandler(import_web_sdk2.ProviderEvents.ContextChanged, updateStatus, { signal: controller.signal });
|
|
250
|
+
client.addHandler(import_web_sdk2.ProviderEvents.Error, updateStatus, { signal: controller.signal });
|
|
251
|
+
client.addHandler(import_web_sdk2.ProviderEvents.Ready, updateStatus, { signal: controller.signal });
|
|
252
|
+
client.addHandler(import_web_sdk2.ProviderEvents.Stale, updateStatus, { signal: controller.signal });
|
|
253
|
+
client.addHandler(import_web_sdk2.ProviderEvents.Reconciling, updateStatus, { signal: controller.signal });
|
|
195
254
|
return () => {
|
|
196
|
-
|
|
197
|
-
client.removeHandler(import_web_sdk2.ProviderEvents.ContextChanged, updateStatus);
|
|
198
|
-
client.removeHandler(import_web_sdk2.ProviderEvents.Error, updateStatus);
|
|
199
|
-
client.removeHandler(import_web_sdk2.ProviderEvents.Ready, updateStatus);
|
|
200
|
-
client.removeHandler(import_web_sdk2.ProviderEvents.Stale, updateStatus);
|
|
201
|
-
client.removeHandler(import_web_sdk2.ProviderEvents.Reconciling, updateStatus);
|
|
255
|
+
controller.abort();
|
|
202
256
|
};
|
|
203
257
|
}, [client]);
|
|
204
258
|
return status;
|
|
205
259
|
}
|
|
206
260
|
|
|
207
|
-
// src/
|
|
261
|
+
// src/provider/use-open-feature-provider.ts
|
|
262
|
+
var import_react5 = __toESM(require("react"));
|
|
208
263
|
var import_web_sdk3 = require("@openfeature/web-sdk");
|
|
264
|
+
function useOpenFeatureProvider() {
|
|
265
|
+
const openFeatureContext = import_react5.default.useContext(Context);
|
|
266
|
+
if (!openFeatureContext) {
|
|
267
|
+
throw new MissingContextError("No OpenFeature context available");
|
|
268
|
+
}
|
|
269
|
+
return import_web_sdk3.OpenFeature.getProvider(openFeatureContext.domain);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// src/internal/hook-flag-query.ts
|
|
273
|
+
var import_web_sdk4 = require("@openfeature/web-sdk");
|
|
209
274
|
var HookFlagQuery = class {
|
|
210
275
|
constructor(_details) {
|
|
211
276
|
this._details = _details;
|
|
@@ -228,7 +293,7 @@ var HookFlagQuery = class {
|
|
|
228
293
|
}
|
|
229
294
|
get isError() {
|
|
230
295
|
var _a;
|
|
231
|
-
return !!((_a = this._details) == null ? void 0 : _a.errorCode) || this._details.reason ==
|
|
296
|
+
return !!((_a = this._details) == null ? void 0 : _a.errorCode) || this._details.reason == import_web_sdk4.StandardResolutionReasons.ERROR;
|
|
232
297
|
}
|
|
233
298
|
get errorCode() {
|
|
234
299
|
var _a;
|
|
@@ -239,7 +304,7 @@ var HookFlagQuery = class {
|
|
|
239
304
|
return (_a = this._details) == null ? void 0 : _a.errorMessage;
|
|
240
305
|
}
|
|
241
306
|
get isAuthoritative() {
|
|
242
|
-
return !this.isError && this._details.reason !=
|
|
307
|
+
return !this.isError && this._details.reason != import_web_sdk4.StandardResolutionReasons.STALE && this._details.reason != import_web_sdk4.StandardResolutionReasons.DISABLED;
|
|
243
308
|
}
|
|
244
309
|
get type() {
|
|
245
310
|
return typeof this._details.value;
|
|
@@ -313,17 +378,19 @@ function attachHandlersAndResolve(flagKey, defaultValue, resolver, options) {
|
|
|
313
378
|
const defaultedOptions = __spreadValues(__spreadValues(__spreadValues({}, DEFAULT_OPTIONS), useProviderOptions()), normalizeOptions(options));
|
|
314
379
|
const client = useOpenFeatureClient();
|
|
315
380
|
const status = useOpenFeatureClientStatus();
|
|
316
|
-
|
|
317
|
-
|
|
381
|
+
const provider = useOpenFeatureProvider();
|
|
382
|
+
const controller = new AbortController();
|
|
383
|
+
if (defaultedOptions.suspendUntilReady && status === import_web_sdk5.ProviderStatus.NOT_READY) {
|
|
384
|
+
suspendUntilInitialized(provider, client);
|
|
318
385
|
}
|
|
319
|
-
if (defaultedOptions.suspendWhileReconciling && status ===
|
|
320
|
-
|
|
386
|
+
if (defaultedOptions.suspendWhileReconciling && status === import_web_sdk5.ProviderStatus.RECONCILING) {
|
|
387
|
+
suspendUntilReconciled(client);
|
|
321
388
|
}
|
|
322
|
-
const [evaluationDetails, setEvaluationDetails] = (0,
|
|
389
|
+
const [evaluationDetails, setEvaluationDetails] = (0, import_react6.useState)(
|
|
323
390
|
resolver(client).call(client, flagKey, defaultValue, options)
|
|
324
391
|
);
|
|
325
|
-
const evaluationDetailsRef = (0,
|
|
326
|
-
(0,
|
|
392
|
+
const evaluationDetailsRef = (0, import_react6.useRef)(evaluationDetails);
|
|
393
|
+
(0, import_react6.useEffect)(() => {
|
|
327
394
|
evaluationDetailsRef.current = evaluationDetails;
|
|
328
395
|
}, [evaluationDetails]);
|
|
329
396
|
const updateEvaluationDetailsCallback = () => {
|
|
@@ -337,58 +404,55 @@ function attachHandlersAndResolve(flagKey, defaultValue, resolver, options) {
|
|
|
337
404
|
updateEvaluationDetailsCallback();
|
|
338
405
|
}
|
|
339
406
|
};
|
|
340
|
-
(0,
|
|
341
|
-
if (status ===
|
|
342
|
-
client.addHandler(
|
|
407
|
+
(0, import_react6.useEffect)(() => {
|
|
408
|
+
if (status === import_web_sdk5.ProviderStatus.NOT_READY) {
|
|
409
|
+
client.addHandler(import_web_sdk5.ProviderEvents.Ready, updateEvaluationDetailsCallback, { signal: controller.signal });
|
|
343
410
|
}
|
|
344
411
|
if (defaultedOptions.updateOnContextChanged) {
|
|
345
|
-
client.addHandler(
|
|
412
|
+
client.addHandler(import_web_sdk5.ProviderEvents.ContextChanged, updateEvaluationDetailsCallback, { signal: controller.signal });
|
|
346
413
|
}
|
|
347
|
-
return () => {
|
|
348
|
-
client.removeHandler(import_web_sdk4.ProviderEvents.Ready, updateEvaluationDetailsCallback);
|
|
349
|
-
client.removeHandler(import_web_sdk4.ProviderEvents.ContextChanged, updateEvaluationDetailsCallback);
|
|
350
|
-
};
|
|
351
|
-
}, []);
|
|
352
|
-
(0, import_react4.useEffect)(() => {
|
|
353
414
|
if (defaultedOptions.updateOnConfigurationChanged) {
|
|
354
|
-
client.addHandler(
|
|
415
|
+
client.addHandler(import_web_sdk5.ProviderEvents.ConfigurationChanged, configurationChangeCallback, {
|
|
416
|
+
signal: controller.signal
|
|
417
|
+
});
|
|
355
418
|
}
|
|
356
419
|
return () => {
|
|
357
|
-
|
|
420
|
+
controller.abort();
|
|
358
421
|
};
|
|
359
422
|
}, []);
|
|
360
423
|
return evaluationDetails;
|
|
361
424
|
}
|
|
362
425
|
|
|
363
426
|
// src/provider/provider.tsx
|
|
364
|
-
var
|
|
365
|
-
var
|
|
427
|
+
var import_web_sdk6 = require("@openfeature/web-sdk");
|
|
428
|
+
var React5 = __toESM(require("react"));
|
|
366
429
|
function OpenFeatureProvider(_a) {
|
|
367
430
|
var _b = _a, { client, domain, children } = _b, options = __objRest(_b, ["client", "domain", "children"]);
|
|
368
431
|
if (!client) {
|
|
369
|
-
client =
|
|
432
|
+
client = import_web_sdk6.OpenFeature.getClient(domain);
|
|
370
433
|
}
|
|
371
|
-
return /* @__PURE__ */
|
|
434
|
+
return /* @__PURE__ */ React5.createElement(Context.Provider, { value: { client, options, domain } }, children);
|
|
372
435
|
}
|
|
373
436
|
|
|
374
437
|
// src/provider/use-when-provider-ready.ts
|
|
375
|
-
var
|
|
438
|
+
var import_web_sdk7 = require("@openfeature/web-sdk");
|
|
376
439
|
function useWhenProviderReady(options) {
|
|
440
|
+
const defaultedOptions = __spreadValues(__spreadValues(__spreadValues({}, DEFAULT_OPTIONS), useProviderOptions()), normalizeOptions(options));
|
|
377
441
|
const client = useOpenFeatureClient();
|
|
378
442
|
const status = useOpenFeatureClientStatus();
|
|
379
|
-
const
|
|
380
|
-
if (defaultedOptions.suspendUntilReady && status ===
|
|
381
|
-
|
|
443
|
+
const provider = useOpenFeatureProvider();
|
|
444
|
+
if (defaultedOptions.suspendUntilReady && status === import_web_sdk7.ProviderStatus.NOT_READY) {
|
|
445
|
+
suspendUntilInitialized(provider, client);
|
|
382
446
|
}
|
|
383
|
-
return status ===
|
|
447
|
+
return status === import_web_sdk7.ProviderStatus.READY;
|
|
384
448
|
}
|
|
385
449
|
|
|
386
450
|
// src/provider/test-provider.tsx
|
|
387
|
-
var
|
|
388
|
-
var
|
|
451
|
+
var import_web_sdk8 = require("@openfeature/web-sdk");
|
|
452
|
+
var import_react7 = __toESM(require("react"));
|
|
389
453
|
var TEST_VARIANT = "test-variant";
|
|
390
454
|
var TEST_PROVIDER = "test-provider";
|
|
391
|
-
var TestProvider = class extends
|
|
455
|
+
var TestProvider = class extends import_web_sdk8.InMemoryProvider {
|
|
392
456
|
constructor(flagValueMap, delay = 0) {
|
|
393
457
|
const flagConfig = Object.entries(flagValueMap).reduce((acc, flag) => {
|
|
394
458
|
return __spreadProps(__spreadValues({}, acc), {
|
|
@@ -423,15 +487,15 @@ var TestProvider = class extends import_web_sdk7.InMemoryProvider {
|
|
|
423
487
|
};
|
|
424
488
|
function OpenFeatureTestProvider(testProviderOptions) {
|
|
425
489
|
const { flagValueMap, provider } = testProviderOptions;
|
|
426
|
-
const effectiveProvider = flagValueMap ? new TestProvider(flagValueMap, testProviderOptions.delayMs) : mixInNoop(provider) ||
|
|
427
|
-
testProviderOptions.domain ?
|
|
428
|
-
return /* @__PURE__ */
|
|
490
|
+
const effectiveProvider = flagValueMap ? new TestProvider(flagValueMap, testProviderOptions.delayMs) : mixInNoop(provider) || import_web_sdk8.NOOP_PROVIDER;
|
|
491
|
+
testProviderOptions.domain ? import_web_sdk8.OpenFeature.setProvider(testProviderOptions.domain, effectiveProvider) : import_web_sdk8.OpenFeature.setProvider(effectiveProvider);
|
|
492
|
+
return /* @__PURE__ */ import_react7.default.createElement(OpenFeatureProvider, __spreadProps(__spreadValues({}, testProviderOptions), { domain: testProviderOptions.domain }), testProviderOptions.children);
|
|
429
493
|
}
|
|
430
494
|
function mixInNoop(provider = {}) {
|
|
431
|
-
for (const prop of Object.getOwnPropertyNames(Object.getPrototypeOf(
|
|
495
|
+
for (const prop of Object.getOwnPropertyNames(Object.getPrototypeOf(import_web_sdk8.NOOP_PROVIDER)).filter((prop2) => prop2 !== "constructor")) {
|
|
432
496
|
const patchedProvider = provider;
|
|
433
497
|
if (!Object.getPrototypeOf(patchedProvider)[prop] && !patchedProvider[prop]) {
|
|
434
|
-
patchedProvider[prop] = Object.getPrototypeOf(
|
|
498
|
+
patchedProvider[prop] = Object.getPrototypeOf(import_web_sdk8.NOOP_PROVIDER)[prop];
|
|
435
499
|
}
|
|
436
500
|
}
|
|
437
501
|
if (!provider.metadata || !provider.metadata.name) {
|
|
@@ -441,17 +505,17 @@ function mixInNoop(provider = {}) {
|
|
|
441
505
|
}
|
|
442
506
|
|
|
443
507
|
// src/context/use-context-mutator.ts
|
|
444
|
-
var
|
|
445
|
-
var
|
|
508
|
+
var import_react8 = require("react");
|
|
509
|
+
var import_web_sdk9 = require("@openfeature/web-sdk");
|
|
446
510
|
function useContextMutator(options = { defaultContext: false }) {
|
|
447
|
-
const { domain } = (0,
|
|
448
|
-
const previousContext = (0,
|
|
449
|
-
const setContext = (0,
|
|
511
|
+
const { domain } = (0, import_react8.useContext)(Context) || {};
|
|
512
|
+
const previousContext = (0, import_react8.useRef)(null);
|
|
513
|
+
const setContext = (0, import_react8.useCallback)((updatedContext) => __async(this, null, function* () {
|
|
450
514
|
if (previousContext.current !== updatedContext) {
|
|
451
515
|
if (!domain || (options == null ? void 0 : options.defaultContext)) {
|
|
452
|
-
|
|
516
|
+
import_web_sdk9.OpenFeature.setContext(updatedContext);
|
|
453
517
|
} else {
|
|
454
|
-
|
|
518
|
+
import_web_sdk9.OpenFeature.setContext(domain, updatedContext);
|
|
455
519
|
}
|
|
456
520
|
previousContext.current = updatedContext;
|
|
457
521
|
}
|
|
@@ -462,10 +526,10 @@ function useContextMutator(options = { defaultContext: false }) {
|
|
|
462
526
|
}
|
|
463
527
|
|
|
464
528
|
// src/tracking/use-track.ts
|
|
465
|
-
var
|
|
529
|
+
var import_react9 = require("react");
|
|
466
530
|
function useTrack() {
|
|
467
531
|
const client = useOpenFeatureClient();
|
|
468
|
-
const track = (0,
|
|
532
|
+
const track = (0, import_react9.useCallback)((trackingEventName, trackingEventDetails) => {
|
|
469
533
|
client.track(trackingEventName, trackingEventDetails);
|
|
470
534
|
}, []);
|
|
471
535
|
return {
|
|
@@ -474,5 +538,5 @@ function useTrack() {
|
|
|
474
538
|
}
|
|
475
539
|
|
|
476
540
|
// src/index.ts
|
|
477
|
-
__reExport(
|
|
541
|
+
__reExport(index_exports, require("@openfeature/web-sdk"), module.exports);
|
|
478
542
|
//# sourceMappingURL=index.js.map
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../src/index.ts", "../../src/evaluation/use-feature-flag.ts", "../../src/internal/context.ts", "../../src/internal/is-equal.ts", "../../src/internal/options.ts", "../../src/internal/suspense.ts", "../../src/provider/use-open-feature-client.ts", "../../src/provider/use-open-feature-client-status.ts", "../../src/
|
|
4
|
-
"sourcesContent": ["export * from './evaluation';\nexport * from './query';\nexport * from './provider';\nexport * from './context';\nexport * from './tracking';\nexport * from './options';\n// re-export the web-sdk so consumers can access that API from the react-sdk\nexport * from '@openfeature/web-sdk';\n", "import type {\n Client,\n ClientProviderEvents,\n EvaluationDetails,\n EventHandler,\n FlagEvaluationOptions,\n FlagValue,\n JsonValue} from '@openfeature/web-sdk';\nimport {\n ProviderEvents,\n ProviderStatus,\n} from '@openfeature/web-sdk';\nimport { useEffect, useRef, useState } from 'react';\nimport type { ReactFlagEvaluationNoSuspenseOptions, ReactFlagEvaluationOptions } from '../options';\nimport { DEFAULT_OPTIONS, isEqual, normalizeOptions, suspendUntilReady, useProviderOptions } from '../internal';\nimport { useOpenFeatureClient } from '../provider/use-open-feature-client';\nimport { useOpenFeatureClientStatus } from '../provider/use-open-feature-client-status';\nimport type { FlagQuery } from '../query';\nimport { HookFlagQuery } from './hook-flag-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<\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 // 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// alias to the return value of useFlag, used to keep useSuspenseFlag consistent\ntype UseFlagReturn<T extends FlagValue> = ReturnType<typeof useFlag<T>>;\n\n/**\n * Equivalent to {@link useFlag} with `options: { suspend: true }`\n * @experimental Suspense is an experimental feature subject to change in future versions.\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 {ReactFlagEvaluationNoSuspenseOptions} options for this evaluation\n * @returns { UseFlagReturn<T> } a queryable object containing useful information about the flag.\n */\nexport function useSuspenseFlag<T extends FlagValue = FlagValue>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationNoSuspenseOptions,\n): UseFlagReturn<T> {\n return useFlag(flagKey, defaultValue, { ...options, suspendUntilReady: true, suspendWhileReconciling: true });\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\n// determines if a flag should be re-evaluated based on a list of changed flags\nfunction shouldEvaluateFlag(flagKey: string, flagsChanged?: string[]): boolean {\n // if flagsChange is missing entirely, we don't know what to re-render\n return !flagsChanged || flagsChanged.includes(flagKey);\n}\n\nfunction attachHandlersAndResolve<T extends FlagValue>(\n flagKey: string,\n defaultValue: T,\n resolver: (\n client: Client,\n ) => (flagKey: string, defaultValue: T, options?: FlagEvaluationOptions) => 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 client = useOpenFeatureClient();\n const status = useOpenFeatureClientStatus();\n\n // suspense\n if (defaultedOptions.suspendUntilReady && status === ProviderStatus.NOT_READY) {\n suspendUntilReady(client);\n }\n\n if (defaultedOptions.suspendWhileReconciling && status === ProviderStatus.RECONCILING) {\n suspendUntilReady(client);\n }\n\n const [evaluationDetails, setEvaluationDetails] = useState<EvaluationDetails<T>>(\n resolver(client).call(client, flagKey, defaultValue, options),\n );\n\n // Maintain a mutable reference to the evaluation details to have a up-to-date reference in the handlers.\n const evaluationDetailsRef = useRef<EvaluationDetails<T>>(evaluationDetails);\n useEffect(() => {\n evaluationDetailsRef.current = evaluationDetails;\n }, [evaluationDetails]);\n\n const updateEvaluationDetailsCallback = () => {\n const updatedEvaluationDetails = resolver(client).call(client, flagKey, defaultValue, options);\n\n /**\n * Avoid re-rendering if the value hasn't changed. We could expose a means\n * to define a custom comparison function if users require a more\n * sophisticated comparison in the future.\n */\n if (!isEqual(updatedEvaluationDetails.value, evaluationDetailsRef.current.value)) {\n setEvaluationDetails(updatedEvaluationDetails);\n }\n };\n\n const configurationChangeCallback: EventHandler<ClientProviderEvents.ConfigurationChanged> = (eventDetails) => {\n if (shouldEvaluateFlag(flagKey, eventDetails?.flagsChanged)) {\n updateEvaluationDetailsCallback();\n }\n };\n\n useEffect(() => {\n if (status === ProviderStatus.NOT_READY) {\n // update when the provider is ready\n client.addHandler(ProviderEvents.Ready, updateEvaluationDetailsCallback);\n }\n\n if (defaultedOptions.updateOnContextChanged) {\n // update when the context changes\n client.addHandler(ProviderEvents.ContextChanged, updateEvaluationDetailsCallback);\n }\n return () => {\n // cleanup the handlers\n client.removeHandler(ProviderEvents.Ready, updateEvaluationDetailsCallback);\n client.removeHandler(ProviderEvents.ContextChanged, updateEvaluationDetailsCallback);\n };\n }, []);\n\n useEffect(() => {\n if (defaultedOptions.updateOnConfigurationChanged) {\n // update when the provider configuration changes\n client.addHandler(ProviderEvents.ConfigurationChanged, configurationChangeCallback);\n }\n return () => {\n // cleanup the handlers\n client.removeHandler(ProviderEvents.ConfigurationChanged, configurationChangeCallback);\n };\n }, []);\n\n return evaluationDetails;\n}\n", "import type { Client } from '@openfeature/web-sdk';\nimport React from 'react';\nimport type { NormalizedOptions, ReactFlagEvaluationOptions } from '../options';\nimport { normalizeOptions } from '.';\n\n/**\n * The underlying React context.\n * DO NOT EXPORT PUBLICLY\n * @internal\n */\nexport const Context = React.createContext<\n { client: Client; domain?: string; options: ReactFlagEvaluationOptions } | undefined\n>(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 { type FlagValue } from '@openfeature/web-sdk';\n\n/**\n * Deeply compare two values to determine if they are equal.\n * Supports primitives and serializable objects.\n * @param {FlagValue} value First value to compare\n * @param {FlagValue} other Second value to compare\n * @returns {boolean} True if the values are equal\n */\nexport function isEqual(value: FlagValue, other: FlagValue): boolean {\n if (value === other) {\n return true;\n }\n\n if (typeof value !== typeof other) {\n return false;\n }\n\n if (typeof value === 'object' && value !== null && other !== null) {\n const valueKeys = Object.keys(value);\n const otherKeys = Object.keys(other);\n\n if (valueKeys.length !== otherKeys.length) {\n return false;\n }\n\n for (const key of valueKeys) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (!isEqual((value as any)[key], (other as any)[key])) {\n return false;\n }\n }\n\n return true;\n }\n\n return false;\n}\n", "import type { ReactFlagEvaluationOptions, NormalizedOptions } from '../options';\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: false,\n suspendWhileReconciling: false,\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 = (\n options: ReactFlagEvaluationOptions = {},\n) => {\n const updateOnContextChanged = options.updateOnContextChanged;\n const updateOnConfigurationChanged = options.updateOnConfigurationChanged;\n\n // fall-back the suspense options to the catch-all `suspend` property\n const suspendUntilReady = 'suspendUntilReady' in options ? options.suspendUntilReady : options.suspend;\n const suspendWhileReconciling =\n 'suspendWhileReconciling' in options ? options.suspendWhileReconciling : options.suspend;\n\n return {\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 ...(typeof updateOnContextChanged === 'boolean' && { updateOnContextChanged }),\n ...(typeof updateOnConfigurationChanged === 'boolean' && { updateOnConfigurationChanged }),\n };\n};\n", "import type { Client} from '@openfeature/web-sdk';\nimport { ProviderEvents } from '@openfeature/web-sdk';\n\n/**\n * Suspends until the client is ready to evaluate feature flags.\n * DO NOT EXPORT PUBLICLY\n * @param {Client} client OpenFeature client\n */\nexport function suspendUntilReady(client: Client): Promise<void> {\n let resolve: (value: unknown) => void;\n let reject: () => void;\n throw new Promise((_resolve, _reject) => {\n resolve = _resolve;\n reject = _reject;\n client.addHandler(ProviderEvents.Ready, resolve);\n client.addHandler(ProviderEvents.Error, reject);\n }).finally(() => {\n client.removeHandler(ProviderEvents.Ready, resolve);\n client.removeHandler(ProviderEvents.Ready, reject);\n });\n}\n", "import React from 'react';\nimport { Context } from '../internal';\nimport type { 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>. If you are seeing this in a test, see: https://openfeature.dev/docs/reference/technologies/client/web/react#testing',\n );\n }\n\n return client;\n}\n", "import { useEffect, useState } from 'react';\nimport { useOpenFeatureClient } from './use-open-feature-client';\nimport type { ProviderStatus } from '@openfeature/web-sdk';\nimport { ProviderEvents } from '@openfeature/web-sdk';\n\n/**\n * Get the {@link ProviderStatus} for the OpenFeatureClient.\n * @returns {ProviderStatus} status of the client for this scope\n */\nexport function useOpenFeatureClientStatus(): ProviderStatus {\n const client = useOpenFeatureClient();\n const [status, setStatus] = useState(client.providerStatus);\n\n useEffect(() => {\n const updateStatus = () => setStatus(client.providerStatus);\n client.addHandler(ProviderEvents.ConfigurationChanged, updateStatus);\n client.addHandler(ProviderEvents.ContextChanged, updateStatus);\n client.addHandler(ProviderEvents.Error, updateStatus);\n client.addHandler(ProviderEvents.Ready, updateStatus);\n client.addHandler(ProviderEvents.Stale, updateStatus);\n client.addHandler(ProviderEvents.Reconciling, updateStatus);\n return () => {\n client.removeHandler(ProviderEvents.ConfigurationChanged, updateStatus);\n client.removeHandler(ProviderEvents.ContextChanged, updateStatus);\n client.removeHandler(ProviderEvents.Error, updateStatus);\n client.removeHandler(ProviderEvents.Ready, updateStatus);\n client.removeHandler(ProviderEvents.Stale, updateStatus);\n client.removeHandler(ProviderEvents.Reconciling, updateStatus);\n };\n }, [client]);\n\n return status;\n}\n", "import type {\n EvaluationDetails,\n FlagValue} from '@openfeature/web-sdk';\nimport {\n StandardResolutionReasons\n} from '@openfeature/web-sdk';\nimport type { FlagQuery } from '../query';\n\n\n// FlagQuery implementation, do not export\nexport class 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 type { Client} from '@openfeature/web-sdk';\nimport { OpenFeature } from '@openfeature/web-sdk';\nimport * as React from 'react';\nimport type { ReactFlagEvaluationOptions } from '../options';\nimport { Context } from '../internal';\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, domain }}>{children}</Context.Provider>;\n}\n", "import { ProviderStatus } from '@openfeature/web-sdk';\nimport { useOpenFeatureClient } from './use-open-feature-client';\nimport { useOpenFeatureClientStatus } from './use-open-feature-client-status';\nimport type { ReactFlagEvaluationOptions } from '../options';\nimport { DEFAULT_OPTIONS, useProviderOptions, normalizeOptions, suspendUntilReady } from '../internal';\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 client = useOpenFeatureClient();\n const status = useOpenFeatureClientStatus();\n // highest priority > evaluation hook options > provider options > default options > lowest priority\n const defaultedOptions = { ...DEFAULT_OPTIONS, ...useProviderOptions(), ...normalizeOptions(options) };\n\n // suspense\n if (defaultedOptions.suspendUntilReady && status === ProviderStatus.NOT_READY) {\n suspendUntilReady(client);\n }\n\n return status === ProviderStatus.READY;\n}\n", "import type {\n JsonValue,\n Provider} from '@openfeature/web-sdk';\nimport {\n InMemoryProvider,\n NOOP_PROVIDER,\n OpenFeature\n} from '@openfeature/web-sdk';\nimport React from 'react';\nimport type { NormalizedOptions } from '../options';\nimport { OpenFeatureProvider } from './provider';\n\ntype FlagValueMap = { [flagKey: string]: JsonValue };\ntype FlagConfig = ConstructorParameters<typeof InMemoryProvider>[0];\ntype TestProviderProps = Omit<React.ComponentProps<typeof OpenFeatureProvider>, 'client'> &\n (\n | {\n provider?: never;\n /**\n * Optional map of flagKeys to flagValues for this OpenFeatureTestProvider context.\n * If not supplied, all flag evaluations will default.\n */\n flagValueMap?: FlagValueMap;\n /**\n * Optional delay for the underlying test provider's readiness and reconciliation.\n * Defaults to 0.\n */\n delayMs?: number;\n }\n | {\n /**\n * An optional partial provider to pass for full control over the flag resolution for this OpenFeatureTestProvider context.\n * Any un-implemented methods or properties will no-op.\n */\n provider?: Partial<Provider>;\n flagValueMap?: never;\n delayMs?: never;\n }\n );\n\n const TEST_VARIANT = 'test-variant';\n const TEST_PROVIDER = 'test-provider';\n\n// internal provider which is basically the in-memory provider with a simpler config and some optional fake delays\nclass TestProvider extends InMemoryProvider {\n\n // initially make this undefined, we still set it if a delay is specified\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore - For maximum compatibility with previous versions, we ignore a possible TS error here,\n // since \"initialize\" was previously defined in superclass.\n // We can safely remove this ts-ignore in a few versions\n initialize: Provider['initialize'] = undefined;\n\n // \"place-holder\" init function which we only assign if want a delay\n private delayedInitialize = async () => {\n await new Promise<void>((resolve) => setTimeout(resolve, this.delay));\n };\n\n constructor(\n flagValueMap: FlagValueMap,\n private delay = 0,\n ) {\n // convert the simple flagValueMap into an in-memory config\n const flagConfig = Object.entries(flagValueMap).reduce((acc: FlagConfig, flag): FlagConfig => {\n return {\n ...acc,\n [flag[0]]: {\n variants: {\n [TEST_VARIANT]: flag[1],\n },\n defaultVariant: TEST_VARIANT,\n disabled: false,\n },\n };\n }, {});\n super(flagConfig);\n // only define and init if there's a non-zero delay specified\n this.initialize = this.delay ? this.delayedInitialize.bind(this) : undefined;\n }\n\n async onContextChange() {\n return new Promise<void>((resolve) => setTimeout(resolve, this.delay));\n }\n}\n\n/**\n * A React Context provider based on the {@link InMemoryProvider}, specifically built for testing.\n * Use this for testing components that use flag evaluation hooks.\n * @param {TestProviderProps} testProviderOptions options for the OpenFeatureTestProvider\n * @returns {OpenFeatureProvider} OpenFeatureTestProvider\n */\nexport function OpenFeatureTestProvider(testProviderOptions: TestProviderProps) {\n const { flagValueMap, provider } = testProviderOptions;\n const effectiveProvider = (\n flagValueMap ? new TestProvider(flagValueMap, testProviderOptions.delayMs) : mixInNoop(provider) || NOOP_PROVIDER\n ) as Provider;\n testProviderOptions.domain\n ? OpenFeature.setProvider(testProviderOptions.domain, effectiveProvider)\n : OpenFeature.setProvider(effectiveProvider);\n\n return (\n <OpenFeatureProvider {...(testProviderOptions as NormalizedOptions)} domain={testProviderOptions.domain}>\n {testProviderOptions.children}\n </OpenFeatureProvider>\n );\n}\n\n// mix in the no-op provider when the partial is passed\nfunction mixInNoop(provider: Partial<Provider> = {}) {\n // fill in any missing methods with no-ops\n for (const prop of Object.getOwnPropertyNames(Object.getPrototypeOf(NOOP_PROVIDER)).filter(prop => prop !== 'constructor')) {\n const patchedProvider = provider as {[key: string]: keyof Provider};\n if (!Object.getPrototypeOf(patchedProvider)[prop] && !patchedProvider[prop]) {\n patchedProvider[prop] = Object.getPrototypeOf(NOOP_PROVIDER)[prop];\n }\n }\n // fill in the metadata if missing\n if (!provider.metadata || !provider.metadata.name) {\n (provider.metadata as unknown) = { name: TEST_PROVIDER };\n }\n return provider;\n}\n", "import { useCallback, useContext, useRef } from 'react';\nimport type { EvaluationContext } from '@openfeature/web-sdk';\nimport { OpenFeature } from '@openfeature/web-sdk';\nimport { Context } from '../internal';\n\nexport type ContextMutationOptions = {\n /**\n * Mutate the default context instead of the domain scoped context applied at the `<OpenFeatureProvider/>`.\n * Note, if the `<OpenFeatureProvider/>` has no domain specified, the default is used.\n * See the {@link https://openfeature.dev/docs/reference/technologies/client/web/#manage-evaluation-context-for-domains|documentation} for more information.\n * @default false\n */\n defaultContext?: boolean;\n};\n\nexport type ContextMutation = {\n /**\n * Context-aware function to set the desired context (see: {@link ContextMutationOptions} for details).\n * There's generally no need to await the result of this function; flag evaluation hooks will re-render when the context is updated.\n * This promise never rejects.\n * @param updatedContext\n * @returns Promise for awaiting the context update\n */\n setContext: (updatedContext: EvaluationContext) => Promise<void>;\n};\n\n/**\n * Get context-aware tracking function(s) for mutating the evaluation context associated with this domain, or the default context if `defaultContext: true`.\n * See the {@link https://openfeature.dev/docs/reference/technologies/client/web/#targeting-and-context|documentation} for more information.\n * @param {ContextMutationOptions} options options for the generated function\n * @returns {ContextMutation} context-aware function(s) to mutate evaluation context\n */\nexport function useContextMutator(options: ContextMutationOptions = { defaultContext: false }): ContextMutation {\n const { domain } = useContext(Context) || {};\n const previousContext = useRef<null | EvaluationContext>(null);\n\n const setContext = useCallback(async (updatedContext: EvaluationContext) => {\n if (previousContext.current !== updatedContext) {\n if (!domain || options?.defaultContext) {\n OpenFeature.setContext(updatedContext);\n } else {\n OpenFeature.setContext(domain, updatedContext);\n }\n previousContext.current = updatedContext;\n }\n }, [domain]);\n\n return {\n setContext,\n };\n}\n", "import type { Tracking, TrackingEventDetails } from '@openfeature/web-sdk';\nimport { useCallback } from 'react';\nimport { useOpenFeatureClient } from '../provider';\n\nexport type Track = {\n /**\n * Context-aware tracking function for the parent `<OpenFeatureProvider/>`.\n * Track a user action or application state, usually representing a business objective or outcome.\n * @param trackingEventName an identifier for the event\n * @param trackingEventDetails the details of the tracking event\n */\n track: Tracking['track'];\n};\n\n/**\n * Get a context-aware tracking function.\n * @returns {Track} context-aware tracking\n */\nexport function useTrack(): Track {\n const client = useOpenFeatureClient();\n\n const track = useCallback((trackingEventName: string, trackingEventDetails?: TrackingEventDetails) => {\n client.track(trackingEventName, trackingEventDetails);\n }, []);\n\n return {\n track,\n };\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;
|
|
6
|
-
"names": ["import_web_sdk", "import_react", "React", "
|
|
3
|
+
"sources": ["../../src/index.ts", "../../src/evaluation/use-feature-flag.ts", "../../src/internal/context.ts", "../../src/internal/is-equal.ts", "../../src/internal/options.ts", "../../src/internal/suspense.ts", "../../src/internal/use.ts", "../../src/provider/use-open-feature-client.ts", "../../src/internal/errors.ts", "../../src/provider/use-open-feature-client-status.ts", "../../src/provider/use-open-feature-provider.ts", "../../src/internal/hook-flag-query.ts", "../../src/provider/provider.tsx", "../../src/provider/use-when-provider-ready.ts", "../../src/provider/test-provider.tsx", "../../src/context/use-context-mutator.ts", "../../src/tracking/use-track.ts"],
|
|
4
|
+
"sourcesContent": ["export * from './evaluation';\nexport * from './query';\nexport * from './provider';\nexport * from './context';\nexport * from './tracking';\nexport * from './options';\n// re-export the web-sdk so consumers can access that API from the react-sdk\nexport * from '@openfeature/web-sdk';\n", "import type {\n Client,\n ClientProviderEvents,\n EvaluationDetails,\n EventHandler,\n FlagEvaluationOptions,\n FlagValue,\n JsonValue,\n} from '@openfeature/web-sdk';\nimport { ProviderEvents, ProviderStatus } from '@openfeature/web-sdk';\nimport { useEffect, useRef, useState } from 'react';\nimport {\n DEFAULT_OPTIONS,\n isEqual,\n normalizeOptions,\n suspendUntilInitialized,\n suspendUntilReconciled,\n useProviderOptions,\n} from '../internal';\nimport type { ReactFlagEvaluationNoSuspenseOptions, ReactFlagEvaluationOptions } from '../options';\nimport { useOpenFeatureClient } from '../provider/use-open-feature-client';\nimport { useOpenFeatureClientStatus } from '../provider/use-open-feature-client-status';\nimport { useOpenFeatureProvider } from '../provider/use-open-feature-provider';\nimport type { FlagQuery } from '../query';\nimport { HookFlagQuery } from '../internal/hook-flag-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<\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 // 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// alias to the return value of useFlag, used to keep useSuspenseFlag consistent\ntype UseFlagReturn<T extends FlagValue> = ReturnType<typeof useFlag<T>>;\n\n/**\n * Equivalent to {@link useFlag} with `options: { suspend: true }`\n * @experimental Suspense is an experimental feature subject to change in future versions.\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 {ReactFlagEvaluationNoSuspenseOptions} options for this evaluation\n * @returns { UseFlagReturn<T> } a queryable object containing useful information about the flag.\n */\nexport function useSuspenseFlag<T extends FlagValue = FlagValue>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationNoSuspenseOptions,\n): UseFlagReturn<T> {\n return useFlag(flagKey, defaultValue, { ...options, suspendUntilReady: true, suspendWhileReconciling: true });\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\n// determines if a flag should be re-evaluated based on a list of changed flags\nfunction shouldEvaluateFlag(flagKey: string, flagsChanged?: string[]): boolean {\n // if flagsChange is missing entirely, we don't know what to re-render\n return !flagsChanged || flagsChanged.includes(flagKey);\n}\n\nfunction attachHandlersAndResolve<T extends FlagValue>(\n flagKey: string,\n defaultValue: T,\n resolver: (\n client: Client,\n ) => (flagKey: string, defaultValue: T, options?: FlagEvaluationOptions) => 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 client = useOpenFeatureClient();\n const status = useOpenFeatureClientStatus();\n const provider = useOpenFeatureProvider();\n\n const controller = new AbortController();\n\n if (defaultedOptions.suspendUntilReady && status === ProviderStatus.NOT_READY) {\n suspendUntilInitialized(provider, client);\n }\n\n if (defaultedOptions.suspendWhileReconciling && status === ProviderStatus.RECONCILING) {\n suspendUntilReconciled(client);\n }\n\n const [evaluationDetails, setEvaluationDetails] = useState<EvaluationDetails<T>>(\n resolver(client).call(client, flagKey, defaultValue, options),\n );\n\n // Maintain a mutable reference to the evaluation details to have a up-to-date reference in the handlers.\n const evaluationDetailsRef = useRef<EvaluationDetails<T>>(evaluationDetails);\n useEffect(() => {\n evaluationDetailsRef.current = evaluationDetails;\n }, [evaluationDetails]);\n\n const updateEvaluationDetailsCallback = () => {\n const updatedEvaluationDetails = resolver(client).call(client, flagKey, defaultValue, options);\n\n /**\n * Avoid re-rendering if the value hasn't changed. We could expose a means\n * to define a custom comparison function if users require a more\n * sophisticated comparison in the future.\n */\n if (!isEqual(updatedEvaluationDetails.value, evaluationDetailsRef.current.value)) {\n setEvaluationDetails(updatedEvaluationDetails);\n }\n };\n\n const configurationChangeCallback: EventHandler<ClientProviderEvents.ConfigurationChanged> = (eventDetails) => {\n if (shouldEvaluateFlag(flagKey, eventDetails?.flagsChanged)) {\n updateEvaluationDetailsCallback();\n }\n };\n\n useEffect(() => {\n if (status === ProviderStatus.NOT_READY) {\n // update when the provider is ready\n client.addHandler(ProviderEvents.Ready, updateEvaluationDetailsCallback, { signal: controller.signal });\n }\n\n if (defaultedOptions.updateOnContextChanged) {\n // update when the context changes\n client.addHandler(ProviderEvents.ContextChanged, updateEvaluationDetailsCallback, { signal: controller.signal });\n }\n\n if (defaultedOptions.updateOnConfigurationChanged) {\n // update when the provider configuration changes\n client.addHandler(ProviderEvents.ConfigurationChanged, configurationChangeCallback, {\n signal: controller.signal,\n });\n }\n return () => {\n // cleanup the handlers\n controller.abort();\n };\n }, []);\n\n return evaluationDetails;\n}\n", "import type { Client } from '@openfeature/web-sdk';\nimport React from 'react';\nimport type { NormalizedOptions, ReactFlagEvaluationOptions } from '../options';\nimport { normalizeOptions } from '.';\n\n/**\n * The underlying React context.\n *\n * **DO NOT EXPORT PUBLICLY**\n * @internal\n */\nexport const Context = React.createContext<\n { client: Client; domain?: string; options: ReactFlagEvaluationOptions } | undefined\n>(undefined);\n\n/**\n * Get a normalized copy of the options used for this OpenFeatureProvider, see {@link normalizeOptions}.\n *\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 { type FlagValue } from '@openfeature/web-sdk';\n\n/**\n * Deeply compare two values to determine if they are equal.\n * Supports primitives and serializable objects.\n * @param {FlagValue} value First value to compare\n * @param {FlagValue} other Second value to compare\n * @returns {boolean} True if the values are equal\n */\nexport function isEqual(value: FlagValue, other: FlagValue): boolean {\n if (value === other) {\n return true;\n }\n\n if (typeof value !== typeof other) {\n return false;\n }\n\n if (typeof value === 'object' && value !== null && other !== null) {\n const valueKeys = Object.keys(value);\n const otherKeys = Object.keys(other);\n\n if (valueKeys.length !== otherKeys.length) {\n return false;\n }\n\n for (const key of valueKeys) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (!isEqual((value as any)[key], (other as any)[key])) {\n return false;\n }\n }\n\n return true;\n }\n\n return false;\n}\n", "import type { ReactFlagEvaluationOptions, NormalizedOptions } from '../options';\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: false,\n suspendWhileReconciling: false,\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 = (\n options: ReactFlagEvaluationOptions = {},\n) => {\n const updateOnContextChanged = options.updateOnContextChanged;\n const updateOnConfigurationChanged = options.updateOnConfigurationChanged;\n\n // fall-back the suspense options to the catch-all `suspend` property\n const suspendUntilReady = 'suspendUntilReady' in options ? options.suspendUntilReady : options.suspend;\n const suspendWhileReconciling =\n 'suspendWhileReconciling' in options ? options.suspendWhileReconciling : options.suspend;\n\n return {\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 ...(typeof updateOnContextChanged === 'boolean' && { updateOnContextChanged }),\n ...(typeof updateOnConfigurationChanged === 'boolean' && { updateOnConfigurationChanged }),\n };\n};\n", "import type { Client, Provider } from '@openfeature/web-sdk';\nimport { NOOP_PROVIDER, ProviderEvents } from '@openfeature/web-sdk';\nimport { use } from './use';\n\n/**\n * A weak map is used to store the global suspense status for each provider. It's\n * important for this to be global to avoid rerender loops. Using useRef won't\n * work because the value isn't preserved when a promise is thrown in a component,\n * which is how suspense operates.\n */\nconst globalProviderSuspenseStatus = new WeakMap<Provider, Promise<unknown>>();\n\n/**\n * Suspends until the client is ready to evaluate feature flags.\n *\n * **DO NOT EXPORT PUBLICLY**\n * @internal\n * @param {Provider} provider the provider to suspend for\n * @param {Client} client the client to check for readiness\n */\nexport function suspendUntilInitialized(provider: Provider, client: Client) {\n const statusPromiseRef = globalProviderSuspenseStatus.get(provider);\n if (!statusPromiseRef) {\n // Noop provider is never ready, so we resolve immediately\n const statusPromise = provider !== NOOP_PROVIDER ? isProviderReady(client) : Promise.resolve();\n globalProviderSuspenseStatus.set(provider, statusPromise);\n // Use will throw the promise and React will trigger a rerender when it's resolved\n use(statusPromise);\n } else {\n // Reuse the existing promise, use won't rethrow if the promise has settled.\n use(statusPromiseRef);\n }\n}\n\n/**\n * Suspends until the provider has finished reconciling.\n *\n * **DO NOT EXPORT PUBLICLY**\n * @internal\n * @param {Client} client the client to check for readiness\n */\nexport function suspendUntilReconciled(client: Client) {\n use(isProviderReady(client));\n}\n\nasync function isProviderReady(client: Client) {\n const controller = new AbortController();\n try {\n return await new Promise((resolve, reject) => {\n client.addHandler(ProviderEvents.Ready, resolve, { signal: controller.signal });\n client.addHandler(ProviderEvents.Error, reject, { signal: controller.signal });\n });\n } finally {\n controller.abort();\n }\n}\n", "/// <reference types=\"react/experimental\" />\n// This function is adopted from https://github.com/vercel/swr\nimport React from 'react';\n\n/**\n * Extends a Promise-like value to include status tracking.\n * The extra properties are used to manage the lifecycle of the Promise, indicating its current state.\n * More information can be found in the React RFE for the use hook.\n * @see https://github.com/reactjs/rfcs/pull/229\n */\nexport type UsePromise<T> =\n Promise<T> & {\n status?: 'pending' | 'fulfilled' | 'rejected';\n value?: T;\n reason?: unknown;\n };\n\n/**\n * React.use is a React API that lets you read the value of a resource like a Promise or context.\n * It was officially added in React 19, so needs to be polyfilled to support older React versions.\n * @param {UsePromise} thenable A thenable object that represents a Promise-like value.\n * @returns {unknown} The resolved value of the thenable or throws if it's still pending or rejected.\n */\nexport const use =\n React.use ||\n // This extra generic is to avoid TypeScript mixing up the generic and JSX syntax\n // and emitting an error.\n // We assume that this is only for the `use(thenable)` case, not `use(context)`.\n // https://github.com/facebook/react/blob/aed00dacfb79d17c53218404c52b1c7aa59c4a89/packages/react-server/src/ReactFizzThenable.js#L45\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n (<T, _>(thenable: UsePromise<T>): T => {\n switch (thenable.status) {\n case 'pending':\n throw thenable;\n case 'fulfilled':\n return thenable.value as T;\n case 'rejected':\n throw thenable.reason;\n default:\n thenable.status = 'pending';\n thenable.then(\n (v) => {\n thenable.status = 'fulfilled';\n thenable.value = v;\n },\n (e) => {\n thenable.status = 'rejected';\n thenable.reason = e;\n },\n );\n throw thenable;\n }\n });\n", "import React from 'react';\nimport { Context } from '../internal';\nimport { type Client } from '@openfeature/web-sdk';\nimport { MissingContextError } from '../internal/errors';\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 MissingContextError('No OpenFeature client available');\n }\n\n return client;\n}\n", "const context = 'Components using OpenFeature must be wrapped with an <OpenFeatureProvider>.';\nconst tip = 'If you are seeing this in a test, see: https://openfeature.dev/docs/reference/technologies/client/web/react#testing';\n\nexport class MissingContextError extends Error {\n constructor(reason: string) {\n super(`${reason}: ${context} ${tip}`);\n this.name = 'MissingContextError';\n }\n}", "import { useEffect, useState } from 'react';\nimport { useOpenFeatureClient } from './use-open-feature-client';\nimport type { ProviderStatus } from '@openfeature/web-sdk';\nimport { ProviderEvents } from '@openfeature/web-sdk';\n\n/**\n * Get the {@link ProviderStatus} for the OpenFeatureClient.\n * @returns {ProviderStatus} status of the client for this scope\n */\nexport function useOpenFeatureClientStatus(): ProviderStatus {\n const client = useOpenFeatureClient();\n const [status, setStatus] = useState<ProviderStatus>(client.providerStatus);\n const controller = new AbortController();\n\n useEffect(() => {\n const updateStatus = () => setStatus(client.providerStatus);\n client.addHandler(ProviderEvents.ConfigurationChanged, updateStatus, { signal: controller.signal });\n client.addHandler(ProviderEvents.ContextChanged, updateStatus, { signal: controller.signal });\n client.addHandler(ProviderEvents.Error, updateStatus, { signal: controller.signal });\n client.addHandler(ProviderEvents.Ready, updateStatus, { signal: controller.signal });\n client.addHandler(ProviderEvents.Stale, updateStatus, { signal: controller.signal });\n client.addHandler(ProviderEvents.Reconciling, updateStatus, { signal: controller.signal });\n return () => {\n controller.abort();\n };\n }, [client]);\n\n return status;\n}\n", "import React from 'react';\nimport { Context } from '../internal';\nimport { OpenFeature } from '@openfeature/web-sdk';\nimport type { Provider } from '@openfeature/web-sdk';\nimport { MissingContextError } from '../internal/errors';\n\n/**\n * Get the {@link Provider} bound to the domain specified in the OpenFeatureProvider context.\n * Note that it isn't recommended to interact with the provider directly, but rather through\n * an OpenFeature client.\n * @returns {Provider} provider for this scope\n */\nexport function useOpenFeatureProvider(): Provider {\n const openFeatureContext = React.useContext(Context);\n\n if (!openFeatureContext) {\n throw new MissingContextError('No OpenFeature context available');\n }\n\n return OpenFeature.getProvider(openFeatureContext.domain);\n}\n", "import type {\n EvaluationDetails,\n FlagValue} from '@openfeature/web-sdk';\nimport {\n StandardResolutionReasons\n} from '@openfeature/web-sdk';\nimport type { FlagQuery } from '../query';\n\n\n// FlagQuery implementation, do not export\nexport class 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 type { Client} from '@openfeature/web-sdk';\nimport { OpenFeature } from '@openfeature/web-sdk';\nimport * as React from 'react';\nimport type { ReactFlagEvaluationOptions } from '../options';\nimport { Context } from '../internal';\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): JSX.Element {\n if (!client) {\n client = OpenFeature.getClient(domain);\n }\n\n return <Context.Provider value={{ client, options, domain }}>{children}</Context.Provider>;\n}\n", "import { ProviderStatus } from '@openfeature/web-sdk';\nimport { useOpenFeatureClient } from './use-open-feature-client';\nimport { useOpenFeatureClientStatus } from './use-open-feature-client-status';\nimport type { ReactFlagEvaluationOptions } from '../options';\nimport { DEFAULT_OPTIONS, useProviderOptions, normalizeOptions, suspendUntilInitialized } from '../internal';\nimport { useOpenFeatureProvider } from './use-open-feature-provider';\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 // highest priority > evaluation hook options > provider options > default options > lowest priority\n const defaultedOptions = { ...DEFAULT_OPTIONS, ...useProviderOptions(), ...normalizeOptions(options) };\n const client = useOpenFeatureClient();\n const status = useOpenFeatureClientStatus();\n const provider = useOpenFeatureProvider();\n\n if (defaultedOptions.suspendUntilReady && status === ProviderStatus.NOT_READY) {\n suspendUntilInitialized(provider, client);\n }\n\n return status === ProviderStatus.READY;\n}\n", "import type {\n JsonValue,\n Provider} from '@openfeature/web-sdk';\nimport {\n InMemoryProvider,\n NOOP_PROVIDER,\n OpenFeature\n} from '@openfeature/web-sdk';\nimport React from 'react';\nimport type { NormalizedOptions } from '../options';\nimport { OpenFeatureProvider } from './provider';\n\ntype FlagValueMap = { [flagKey: string]: JsonValue };\ntype FlagConfig = ConstructorParameters<typeof InMemoryProvider>[0];\ntype TestProviderProps = Omit<React.ComponentProps<typeof OpenFeatureProvider>, 'client'> &\n (\n | {\n provider?: never;\n /**\n * Optional map of flagKeys to flagValues for this OpenFeatureTestProvider context.\n * If not supplied, all flag evaluations will default.\n */\n flagValueMap?: FlagValueMap;\n /**\n * Optional delay for the underlying test provider's readiness and reconciliation.\n * Defaults to 0.\n */\n delayMs?: number;\n }\n | {\n /**\n * An optional partial provider to pass for full control over the flag resolution for this OpenFeatureTestProvider context.\n * Any un-implemented methods or properties will no-op.\n */\n provider?: Partial<Provider>;\n flagValueMap?: never;\n delayMs?: never;\n }\n );\n\n const TEST_VARIANT = 'test-variant';\n const TEST_PROVIDER = 'test-provider';\n\n// internal provider which is basically the in-memory provider with a simpler config and some optional fake delays\nclass TestProvider extends InMemoryProvider {\n\n // initially make this undefined, we still set it if a delay is specified\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore - For maximum compatibility with previous versions, we ignore a possible TS error here,\n // since \"initialize\" was previously defined in superclass.\n // We can safely remove this ts-ignore in a few versions\n initialize: Provider['initialize'] = undefined;\n\n // \"place-holder\" init function which we only assign if want a delay\n private delayedInitialize = async () => {\n await new Promise<void>((resolve) => setTimeout(resolve, this.delay));\n };\n\n constructor(\n flagValueMap: FlagValueMap,\n private delay = 0,\n ) {\n // convert the simple flagValueMap into an in-memory config\n const flagConfig = Object.entries(flagValueMap).reduce((acc: FlagConfig, flag): FlagConfig => {\n return {\n ...acc,\n [flag[0]]: {\n variants: {\n [TEST_VARIANT]: flag[1],\n },\n defaultVariant: TEST_VARIANT,\n disabled: false,\n },\n };\n }, {});\n super(flagConfig);\n // only define and init if there's a non-zero delay specified\n this.initialize = this.delay ? this.delayedInitialize.bind(this) : undefined;\n }\n\n async onContextChange() {\n return new Promise<void>((resolve) => setTimeout(resolve, this.delay));\n }\n}\n\n/**\n * A React Context provider based on the {@link InMemoryProvider}, specifically built for testing.\n * Use this for testing components that use flag evaluation hooks.\n * @param {TestProviderProps} testProviderOptions options for the OpenFeatureTestProvider\n * @returns {OpenFeatureProvider} OpenFeatureTestProvider\n */\nexport function OpenFeatureTestProvider(testProviderOptions: TestProviderProps) {\n const { flagValueMap, provider } = testProviderOptions;\n const effectiveProvider = (\n flagValueMap ? new TestProvider(flagValueMap, testProviderOptions.delayMs) : mixInNoop(provider) || NOOP_PROVIDER\n ) as Provider;\n testProviderOptions.domain\n ? OpenFeature.setProvider(testProviderOptions.domain, effectiveProvider)\n : OpenFeature.setProvider(effectiveProvider);\n\n return (\n <OpenFeatureProvider {...(testProviderOptions as NormalizedOptions)} domain={testProviderOptions.domain}>\n {testProviderOptions.children}\n </OpenFeatureProvider>\n );\n}\n\n// mix in the no-op provider when the partial is passed\nfunction mixInNoop(provider: Partial<Provider> = {}) {\n // fill in any missing methods with no-ops\n for (const prop of Object.getOwnPropertyNames(Object.getPrototypeOf(NOOP_PROVIDER)).filter(prop => prop !== 'constructor')) {\n const patchedProvider = provider as {[key: string]: keyof Provider};\n if (!Object.getPrototypeOf(patchedProvider)[prop] && !patchedProvider[prop]) {\n patchedProvider[prop] = Object.getPrototypeOf(NOOP_PROVIDER)[prop];\n }\n }\n // fill in the metadata if missing\n if (!provider.metadata || !provider.metadata.name) {\n (provider.metadata as unknown) = { name: TEST_PROVIDER };\n }\n return provider;\n}\n", "import { useCallback, useContext, useRef } from 'react';\nimport type { EvaluationContext } from '@openfeature/web-sdk';\nimport { OpenFeature } from '@openfeature/web-sdk';\nimport { Context } from '../internal';\n\nexport type ContextMutationOptions = {\n /**\n * Mutate the default context instead of the domain scoped context applied at the `<OpenFeatureProvider/>`.\n * Note, if the `<OpenFeatureProvider/>` has no domain specified, the default is used.\n * See the {@link https://openfeature.dev/docs/reference/technologies/client/web/#manage-evaluation-context-for-domains|documentation} for more information.\n * @default false\n */\n defaultContext?: boolean;\n};\n\nexport type ContextMutation = {\n /**\n * Context-aware function to set the desired context (see: {@link ContextMutationOptions} for details).\n * There's generally no need to await the result of this function; flag evaluation hooks will re-render when the context is updated.\n * This promise never rejects.\n * @param updatedContext\n * @returns Promise for awaiting the context update\n */\n setContext: (updatedContext: EvaluationContext) => Promise<void>;\n};\n\n/**\n * Get context-aware tracking function(s) for mutating the evaluation context associated with this domain, or the default context if `defaultContext: true`.\n * See the {@link https://openfeature.dev/docs/reference/technologies/client/web/#targeting-and-context|documentation} for more information.\n * @param {ContextMutationOptions} options options for the generated function\n * @returns {ContextMutation} context-aware function(s) to mutate evaluation context\n */\nexport function useContextMutator(options: ContextMutationOptions = { defaultContext: false }): ContextMutation {\n const { domain } = useContext(Context) || {};\n const previousContext = useRef<null | EvaluationContext>(null);\n\n const setContext = useCallback(async (updatedContext: EvaluationContext) => {\n if (previousContext.current !== updatedContext) {\n if (!domain || options?.defaultContext) {\n OpenFeature.setContext(updatedContext);\n } else {\n OpenFeature.setContext(domain, updatedContext);\n }\n previousContext.current = updatedContext;\n }\n }, [domain]);\n\n return {\n setContext,\n };\n}\n", "import type { Tracking, TrackingEventDetails } from '@openfeature/web-sdk';\nimport { useCallback } from 'react';\nimport { useOpenFeatureClient } from '../provider';\n\nexport type Track = {\n /**\n * Context-aware tracking function for the parent `<OpenFeatureProvider/>`.\n * Track a user action or application state, usually representing a business objective or outcome.\n * @param trackingEventName an identifier for the event\n * @param trackingEventDetails the details of the tracking event\n */\n track: Tracking['track'];\n};\n\n/**\n * Get a context-aware tracking function.\n * @returns {Track} context-aware tracking\n */\nexport function useTrack(): Track {\n const client = useOpenFeatureClient();\n\n const track = useCallback((trackingEventName: string, trackingEventDetails?: TrackingEventDetails) => {\n client.track(trackingEventName, trackingEventDetails);\n }, []);\n\n return {\n track,\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["import_web_sdk", "import_react", "React", "import_react", "React", "import_react", "React", "import_react", "import_web_sdk", "import_react", "import_web_sdk", "React", "import_web_sdk", "import_web_sdk", "React", "import_web_sdk", "import_web_sdk", "import_react", "React", "prop", "import_react", "import_web_sdk", "import_react"]
|
|
7
7
|
}
|
package/dist/esm/index.js
CHANGED
|
@@ -51,10 +51,7 @@ var __async = (__this, __arguments, generator) => {
|
|
|
51
51
|
};
|
|
52
52
|
|
|
53
53
|
// src/evaluation/use-feature-flag.ts
|
|
54
|
-
import {
|
|
55
|
-
ProviderEvents as ProviderEvents3,
|
|
56
|
-
ProviderStatus
|
|
57
|
-
} from "@openfeature/web-sdk";
|
|
54
|
+
import { ProviderEvents as ProviderEvents3, ProviderStatus } from "@openfeature/web-sdk";
|
|
58
55
|
import { useEffect as useEffect2, useRef, useState as useState2 } from "react";
|
|
59
56
|
|
|
60
57
|
// src/internal/context.ts
|
|
@@ -99,35 +96,92 @@ var DEFAULT_OPTIONS = {
|
|
|
99
96
|
var normalizeOptions = (options = {}) => {
|
|
100
97
|
const updateOnContextChanged = options.updateOnContextChanged;
|
|
101
98
|
const updateOnConfigurationChanged = options.updateOnConfigurationChanged;
|
|
102
|
-
const
|
|
99
|
+
const suspendUntilReady = "suspendUntilReady" in options ? options.suspendUntilReady : options.suspend;
|
|
103
100
|
const suspendWhileReconciling = "suspendWhileReconciling" in options ? options.suspendWhileReconciling : options.suspend;
|
|
104
|
-
return __spreadValues(__spreadValues(__spreadValues(__spreadValues({}, typeof
|
|
101
|
+
return __spreadValues(__spreadValues(__spreadValues(__spreadValues({}, typeof suspendUntilReady === "boolean" && { suspendUntilReady }), typeof suspendWhileReconciling === "boolean" && { suspendWhileReconciling }), typeof updateOnContextChanged === "boolean" && { updateOnContextChanged }), typeof updateOnConfigurationChanged === "boolean" && { updateOnConfigurationChanged });
|
|
105
102
|
};
|
|
106
103
|
|
|
107
104
|
// src/internal/suspense.ts
|
|
108
|
-
import { ProviderEvents } from "@openfeature/web-sdk";
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
105
|
+
import { NOOP_PROVIDER, ProviderEvents } from "@openfeature/web-sdk";
|
|
106
|
+
|
|
107
|
+
// src/internal/use.ts
|
|
108
|
+
import React2 from "react";
|
|
109
|
+
var use = React2.use || // This extra generic is to avoid TypeScript mixing up the generic and JSX syntax
|
|
110
|
+
// and emitting an error.
|
|
111
|
+
// We assume that this is only for the `use(thenable)` case, not `use(context)`.
|
|
112
|
+
// https://github.com/facebook/react/blob/aed00dacfb79d17c53218404c52b1c7aa59c4a89/packages/react-server/src/ReactFizzThenable.js#L45
|
|
113
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
114
|
+
((thenable) => {
|
|
115
|
+
switch (thenable.status) {
|
|
116
|
+
case "pending":
|
|
117
|
+
throw thenable;
|
|
118
|
+
case "fulfilled":
|
|
119
|
+
return thenable.value;
|
|
120
|
+
case "rejected":
|
|
121
|
+
throw thenable.reason;
|
|
122
|
+
default:
|
|
123
|
+
thenable.status = "pending";
|
|
124
|
+
thenable.then(
|
|
125
|
+
(v) => {
|
|
126
|
+
thenable.status = "fulfilled";
|
|
127
|
+
thenable.value = v;
|
|
128
|
+
},
|
|
129
|
+
(e) => {
|
|
130
|
+
thenable.status = "rejected";
|
|
131
|
+
thenable.reason = e;
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
throw thenable;
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// src/internal/suspense.ts
|
|
139
|
+
var globalProviderSuspenseStatus = /* @__PURE__ */ new WeakMap();
|
|
140
|
+
function suspendUntilInitialized(provider, client) {
|
|
141
|
+
const statusPromiseRef = globalProviderSuspenseStatus.get(provider);
|
|
142
|
+
if (!statusPromiseRef) {
|
|
143
|
+
const statusPromise = provider !== NOOP_PROVIDER ? isProviderReady(client) : Promise.resolve();
|
|
144
|
+
globalProviderSuspenseStatus.set(provider, statusPromise);
|
|
145
|
+
use(statusPromise);
|
|
146
|
+
} else {
|
|
147
|
+
use(statusPromiseRef);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function suspendUntilReconciled(client) {
|
|
151
|
+
use(isProviderReady(client));
|
|
152
|
+
}
|
|
153
|
+
function isProviderReady(client) {
|
|
154
|
+
return __async(this, null, function* () {
|
|
155
|
+
const controller = new AbortController();
|
|
156
|
+
try {
|
|
157
|
+
return yield new Promise((resolve, reject) => {
|
|
158
|
+
client.addHandler(ProviderEvents.Ready, resolve, { signal: controller.signal });
|
|
159
|
+
client.addHandler(ProviderEvents.Error, reject, { signal: controller.signal });
|
|
160
|
+
});
|
|
161
|
+
} finally {
|
|
162
|
+
controller.abort();
|
|
163
|
+
}
|
|
120
164
|
});
|
|
121
165
|
}
|
|
122
166
|
|
|
123
167
|
// src/provider/use-open-feature-client.ts
|
|
124
|
-
import
|
|
168
|
+
import React3 from "react";
|
|
169
|
+
|
|
170
|
+
// src/internal/errors.ts
|
|
171
|
+
var context = "Components using OpenFeature must be wrapped with an <OpenFeatureProvider>.";
|
|
172
|
+
var tip = "If you are seeing this in a test, see: https://openfeature.dev/docs/reference/technologies/client/web/react#testing";
|
|
173
|
+
var MissingContextError = class extends Error {
|
|
174
|
+
constructor(reason) {
|
|
175
|
+
super(`${reason}: ${context} ${tip}`);
|
|
176
|
+
this.name = "MissingContextError";
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// src/provider/use-open-feature-client.ts
|
|
125
181
|
function useOpenFeatureClient() {
|
|
126
|
-
const { client } =
|
|
182
|
+
const { client } = React3.useContext(Context) || {};
|
|
127
183
|
if (!client) {
|
|
128
|
-
throw new
|
|
129
|
-
"No OpenFeature client available - components using OpenFeature must be wrapped with an <OpenFeatureProvider>. If you are seeing this in a test, see: https://openfeature.dev/docs/reference/technologies/client/web/react#testing"
|
|
130
|
-
);
|
|
184
|
+
throw new MissingContextError("No OpenFeature client available");
|
|
131
185
|
}
|
|
132
186
|
return client;
|
|
133
187
|
}
|
|
@@ -138,27 +192,34 @@ import { ProviderEvents as ProviderEvents2 } from "@openfeature/web-sdk";
|
|
|
138
192
|
function useOpenFeatureClientStatus() {
|
|
139
193
|
const client = useOpenFeatureClient();
|
|
140
194
|
const [status, setStatus] = useState(client.providerStatus);
|
|
195
|
+
const controller = new AbortController();
|
|
141
196
|
useEffect(() => {
|
|
142
197
|
const updateStatus = () => setStatus(client.providerStatus);
|
|
143
|
-
client.addHandler(ProviderEvents2.ConfigurationChanged, updateStatus);
|
|
144
|
-
client.addHandler(ProviderEvents2.ContextChanged, updateStatus);
|
|
145
|
-
client.addHandler(ProviderEvents2.Error, updateStatus);
|
|
146
|
-
client.addHandler(ProviderEvents2.Ready, updateStatus);
|
|
147
|
-
client.addHandler(ProviderEvents2.Stale, updateStatus);
|
|
148
|
-
client.addHandler(ProviderEvents2.Reconciling, updateStatus);
|
|
198
|
+
client.addHandler(ProviderEvents2.ConfigurationChanged, updateStatus, { signal: controller.signal });
|
|
199
|
+
client.addHandler(ProviderEvents2.ContextChanged, updateStatus, { signal: controller.signal });
|
|
200
|
+
client.addHandler(ProviderEvents2.Error, updateStatus, { signal: controller.signal });
|
|
201
|
+
client.addHandler(ProviderEvents2.Ready, updateStatus, { signal: controller.signal });
|
|
202
|
+
client.addHandler(ProviderEvents2.Stale, updateStatus, { signal: controller.signal });
|
|
203
|
+
client.addHandler(ProviderEvents2.Reconciling, updateStatus, { signal: controller.signal });
|
|
149
204
|
return () => {
|
|
150
|
-
|
|
151
|
-
client.removeHandler(ProviderEvents2.ContextChanged, updateStatus);
|
|
152
|
-
client.removeHandler(ProviderEvents2.Error, updateStatus);
|
|
153
|
-
client.removeHandler(ProviderEvents2.Ready, updateStatus);
|
|
154
|
-
client.removeHandler(ProviderEvents2.Stale, updateStatus);
|
|
155
|
-
client.removeHandler(ProviderEvents2.Reconciling, updateStatus);
|
|
205
|
+
controller.abort();
|
|
156
206
|
};
|
|
157
207
|
}, [client]);
|
|
158
208
|
return status;
|
|
159
209
|
}
|
|
160
210
|
|
|
161
|
-
// src/
|
|
211
|
+
// src/provider/use-open-feature-provider.ts
|
|
212
|
+
import React4 from "react";
|
|
213
|
+
import { OpenFeature } from "@openfeature/web-sdk";
|
|
214
|
+
function useOpenFeatureProvider() {
|
|
215
|
+
const openFeatureContext = React4.useContext(Context);
|
|
216
|
+
if (!openFeatureContext) {
|
|
217
|
+
throw new MissingContextError("No OpenFeature context available");
|
|
218
|
+
}
|
|
219
|
+
return OpenFeature.getProvider(openFeatureContext.domain);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// src/internal/hook-flag-query.ts
|
|
162
223
|
import {
|
|
163
224
|
StandardResolutionReasons
|
|
164
225
|
} from "@openfeature/web-sdk";
|
|
@@ -269,11 +330,13 @@ function attachHandlersAndResolve(flagKey, defaultValue, resolver, options) {
|
|
|
269
330
|
const defaultedOptions = __spreadValues(__spreadValues(__spreadValues({}, DEFAULT_OPTIONS), useProviderOptions()), normalizeOptions(options));
|
|
270
331
|
const client = useOpenFeatureClient();
|
|
271
332
|
const status = useOpenFeatureClientStatus();
|
|
333
|
+
const provider = useOpenFeatureProvider();
|
|
334
|
+
const controller = new AbortController();
|
|
272
335
|
if (defaultedOptions.suspendUntilReady && status === ProviderStatus.NOT_READY) {
|
|
273
|
-
|
|
336
|
+
suspendUntilInitialized(provider, client);
|
|
274
337
|
}
|
|
275
338
|
if (defaultedOptions.suspendWhileReconciling && status === ProviderStatus.RECONCILING) {
|
|
276
|
-
|
|
339
|
+
suspendUntilReconciled(client);
|
|
277
340
|
}
|
|
278
341
|
const [evaluationDetails, setEvaluationDetails] = useState2(
|
|
279
342
|
resolver(client).call(client, flagKey, defaultValue, options)
|
|
@@ -295,46 +358,43 @@ function attachHandlersAndResolve(flagKey, defaultValue, resolver, options) {
|
|
|
295
358
|
};
|
|
296
359
|
useEffect2(() => {
|
|
297
360
|
if (status === ProviderStatus.NOT_READY) {
|
|
298
|
-
client.addHandler(ProviderEvents3.Ready, updateEvaluationDetailsCallback);
|
|
361
|
+
client.addHandler(ProviderEvents3.Ready, updateEvaluationDetailsCallback, { signal: controller.signal });
|
|
299
362
|
}
|
|
300
363
|
if (defaultedOptions.updateOnContextChanged) {
|
|
301
|
-
client.addHandler(ProviderEvents3.ContextChanged, updateEvaluationDetailsCallback);
|
|
364
|
+
client.addHandler(ProviderEvents3.ContextChanged, updateEvaluationDetailsCallback, { signal: controller.signal });
|
|
302
365
|
}
|
|
303
|
-
return () => {
|
|
304
|
-
client.removeHandler(ProviderEvents3.Ready, updateEvaluationDetailsCallback);
|
|
305
|
-
client.removeHandler(ProviderEvents3.ContextChanged, updateEvaluationDetailsCallback);
|
|
306
|
-
};
|
|
307
|
-
}, []);
|
|
308
|
-
useEffect2(() => {
|
|
309
366
|
if (defaultedOptions.updateOnConfigurationChanged) {
|
|
310
|
-
client.addHandler(ProviderEvents3.ConfigurationChanged, configurationChangeCallback
|
|
367
|
+
client.addHandler(ProviderEvents3.ConfigurationChanged, configurationChangeCallback, {
|
|
368
|
+
signal: controller.signal
|
|
369
|
+
});
|
|
311
370
|
}
|
|
312
371
|
return () => {
|
|
313
|
-
|
|
372
|
+
controller.abort();
|
|
314
373
|
};
|
|
315
374
|
}, []);
|
|
316
375
|
return evaluationDetails;
|
|
317
376
|
}
|
|
318
377
|
|
|
319
378
|
// src/provider/provider.tsx
|
|
320
|
-
import { OpenFeature } from "@openfeature/web-sdk";
|
|
321
|
-
import * as
|
|
379
|
+
import { OpenFeature as OpenFeature2 } from "@openfeature/web-sdk";
|
|
380
|
+
import * as React5 from "react";
|
|
322
381
|
function OpenFeatureProvider(_a) {
|
|
323
382
|
var _b = _a, { client, domain, children } = _b, options = __objRest(_b, ["client", "domain", "children"]);
|
|
324
383
|
if (!client) {
|
|
325
|
-
client =
|
|
384
|
+
client = OpenFeature2.getClient(domain);
|
|
326
385
|
}
|
|
327
|
-
return /* @__PURE__ */
|
|
386
|
+
return /* @__PURE__ */ React5.createElement(Context.Provider, { value: { client, options, domain } }, children);
|
|
328
387
|
}
|
|
329
388
|
|
|
330
389
|
// src/provider/use-when-provider-ready.ts
|
|
331
390
|
import { ProviderStatus as ProviderStatus2 } from "@openfeature/web-sdk";
|
|
332
391
|
function useWhenProviderReady(options) {
|
|
392
|
+
const defaultedOptions = __spreadValues(__spreadValues(__spreadValues({}, DEFAULT_OPTIONS), useProviderOptions()), normalizeOptions(options));
|
|
333
393
|
const client = useOpenFeatureClient();
|
|
334
394
|
const status = useOpenFeatureClientStatus();
|
|
335
|
-
const
|
|
395
|
+
const provider = useOpenFeatureProvider();
|
|
336
396
|
if (defaultedOptions.suspendUntilReady && status === ProviderStatus2.NOT_READY) {
|
|
337
|
-
|
|
397
|
+
suspendUntilInitialized(provider, client);
|
|
338
398
|
}
|
|
339
399
|
return status === ProviderStatus2.READY;
|
|
340
400
|
}
|
|
@@ -342,10 +402,10 @@ function useWhenProviderReady(options) {
|
|
|
342
402
|
// src/provider/test-provider.tsx
|
|
343
403
|
import {
|
|
344
404
|
InMemoryProvider,
|
|
345
|
-
NOOP_PROVIDER,
|
|
346
|
-
OpenFeature as
|
|
405
|
+
NOOP_PROVIDER as NOOP_PROVIDER2,
|
|
406
|
+
OpenFeature as OpenFeature3
|
|
347
407
|
} from "@openfeature/web-sdk";
|
|
348
|
-
import
|
|
408
|
+
import React6 from "react";
|
|
349
409
|
var TEST_VARIANT = "test-variant";
|
|
350
410
|
var TEST_PROVIDER = "test-provider";
|
|
351
411
|
var TestProvider = class extends InMemoryProvider {
|
|
@@ -383,15 +443,15 @@ var TestProvider = class extends InMemoryProvider {
|
|
|
383
443
|
};
|
|
384
444
|
function OpenFeatureTestProvider(testProviderOptions) {
|
|
385
445
|
const { flagValueMap, provider } = testProviderOptions;
|
|
386
|
-
const effectiveProvider = flagValueMap ? new TestProvider(flagValueMap, testProviderOptions.delayMs) : mixInNoop(provider) ||
|
|
387
|
-
testProviderOptions.domain ?
|
|
388
|
-
return /* @__PURE__ */
|
|
446
|
+
const effectiveProvider = flagValueMap ? new TestProvider(flagValueMap, testProviderOptions.delayMs) : mixInNoop(provider) || NOOP_PROVIDER2;
|
|
447
|
+
testProviderOptions.domain ? OpenFeature3.setProvider(testProviderOptions.domain, effectiveProvider) : OpenFeature3.setProvider(effectiveProvider);
|
|
448
|
+
return /* @__PURE__ */ React6.createElement(OpenFeatureProvider, __spreadProps(__spreadValues({}, testProviderOptions), { domain: testProviderOptions.domain }), testProviderOptions.children);
|
|
389
449
|
}
|
|
390
450
|
function mixInNoop(provider = {}) {
|
|
391
|
-
for (const prop of Object.getOwnPropertyNames(Object.getPrototypeOf(
|
|
451
|
+
for (const prop of Object.getOwnPropertyNames(Object.getPrototypeOf(NOOP_PROVIDER2)).filter((prop2) => prop2 !== "constructor")) {
|
|
392
452
|
const patchedProvider = provider;
|
|
393
453
|
if (!Object.getPrototypeOf(patchedProvider)[prop] && !patchedProvider[prop]) {
|
|
394
|
-
patchedProvider[prop] = Object.getPrototypeOf(
|
|
454
|
+
patchedProvider[prop] = Object.getPrototypeOf(NOOP_PROVIDER2)[prop];
|
|
395
455
|
}
|
|
396
456
|
}
|
|
397
457
|
if (!provider.metadata || !provider.metadata.name) {
|
|
@@ -402,16 +462,16 @@ function mixInNoop(provider = {}) {
|
|
|
402
462
|
|
|
403
463
|
// src/context/use-context-mutator.ts
|
|
404
464
|
import { useCallback, useContext, useRef as useRef2 } from "react";
|
|
405
|
-
import { OpenFeature as
|
|
465
|
+
import { OpenFeature as OpenFeature4 } from "@openfeature/web-sdk";
|
|
406
466
|
function useContextMutator(options = { defaultContext: false }) {
|
|
407
467
|
const { domain } = useContext(Context) || {};
|
|
408
468
|
const previousContext = useRef2(null);
|
|
409
469
|
const setContext = useCallback((updatedContext) => __async(this, null, function* () {
|
|
410
470
|
if (previousContext.current !== updatedContext) {
|
|
411
471
|
if (!domain || (options == null ? void 0 : options.defaultContext)) {
|
|
412
|
-
|
|
472
|
+
OpenFeature4.setContext(updatedContext);
|
|
413
473
|
} else {
|
|
414
|
-
|
|
474
|
+
OpenFeature4.setContext(domain, updatedContext);
|
|
415
475
|
}
|
|
416
476
|
previousContext.current = updatedContext;
|
|
417
477
|
}
|
|
@@ -447,6 +507,7 @@ export {
|
|
|
447
507
|
useObjectFlagDetails,
|
|
448
508
|
useObjectFlagValue,
|
|
449
509
|
useOpenFeatureClient,
|
|
510
|
+
useOpenFeatureClientStatus,
|
|
450
511
|
useStringFlagDetails,
|
|
451
512
|
useStringFlagValue,
|
|
452
513
|
useSuspenseFlag,
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../src/evaluation/use-feature-flag.ts", "../../src/internal/context.ts", "../../src/internal/is-equal.ts", "../../src/internal/options.ts", "../../src/internal/suspense.ts", "../../src/provider/use-open-feature-client.ts", "../../src/provider/use-open-feature-client-status.ts", "../../src/
|
|
4
|
-
"sourcesContent": ["import type {\n Client,\n ClientProviderEvents,\n EvaluationDetails,\n EventHandler,\n FlagEvaluationOptions,\n FlagValue,\n JsonValue} from '@openfeature/web-sdk';\nimport {\n ProviderEvents,\n ProviderStatus,\n} from '@openfeature/web-sdk';\nimport { useEffect, useRef, useState } from 'react';\nimport type { ReactFlagEvaluationNoSuspenseOptions, ReactFlagEvaluationOptions } from '../options';\nimport { DEFAULT_OPTIONS, isEqual, normalizeOptions, suspendUntilReady, useProviderOptions } from '../internal';\nimport { useOpenFeatureClient } from '../provider/use-open-feature-client';\nimport { useOpenFeatureClientStatus } from '../provider/use-open-feature-client-status';\nimport type { FlagQuery } from '../query';\nimport { HookFlagQuery } from './hook-flag-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<\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 // 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// alias to the return value of useFlag, used to keep useSuspenseFlag consistent\ntype UseFlagReturn<T extends FlagValue> = ReturnType<typeof useFlag<T>>;\n\n/**\n * Equivalent to {@link useFlag} with `options: { suspend: true }`\n * @experimental Suspense is an experimental feature subject to change in future versions.\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 {ReactFlagEvaluationNoSuspenseOptions} options for this evaluation\n * @returns { UseFlagReturn<T> } a queryable object containing useful information about the flag.\n */\nexport function useSuspenseFlag<T extends FlagValue = FlagValue>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationNoSuspenseOptions,\n): UseFlagReturn<T> {\n return useFlag(flagKey, defaultValue, { ...options, suspendUntilReady: true, suspendWhileReconciling: true });\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\n// determines if a flag should be re-evaluated based on a list of changed flags\nfunction shouldEvaluateFlag(flagKey: string, flagsChanged?: string[]): boolean {\n // if flagsChange is missing entirely, we don't know what to re-render\n return !flagsChanged || flagsChanged.includes(flagKey);\n}\n\nfunction attachHandlersAndResolve<T extends FlagValue>(\n flagKey: string,\n defaultValue: T,\n resolver: (\n client: Client,\n ) => (flagKey: string, defaultValue: T, options?: FlagEvaluationOptions) => 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 client = useOpenFeatureClient();\n const status = useOpenFeatureClientStatus();\n\n // suspense\n if (defaultedOptions.suspendUntilReady && status === ProviderStatus.NOT_READY) {\n suspendUntilReady(client);\n }\n\n if (defaultedOptions.suspendWhileReconciling && status === ProviderStatus.RECONCILING) {\n suspendUntilReady(client);\n }\n\n const [evaluationDetails, setEvaluationDetails] = useState<EvaluationDetails<T>>(\n resolver(client).call(client, flagKey, defaultValue, options),\n );\n\n // Maintain a mutable reference to the evaluation details to have a up-to-date reference in the handlers.\n const evaluationDetailsRef = useRef<EvaluationDetails<T>>(evaluationDetails);\n useEffect(() => {\n evaluationDetailsRef.current = evaluationDetails;\n }, [evaluationDetails]);\n\n const updateEvaluationDetailsCallback = () => {\n const updatedEvaluationDetails = resolver(client).call(client, flagKey, defaultValue, options);\n\n /**\n * Avoid re-rendering if the value hasn't changed. We could expose a means\n * to define a custom comparison function if users require a more\n * sophisticated comparison in the future.\n */\n if (!isEqual(updatedEvaluationDetails.value, evaluationDetailsRef.current.value)) {\n setEvaluationDetails(updatedEvaluationDetails);\n }\n };\n\n const configurationChangeCallback: EventHandler<ClientProviderEvents.ConfigurationChanged> = (eventDetails) => {\n if (shouldEvaluateFlag(flagKey, eventDetails?.flagsChanged)) {\n updateEvaluationDetailsCallback();\n }\n };\n\n useEffect(() => {\n if (status === ProviderStatus.NOT_READY) {\n // update when the provider is ready\n client.addHandler(ProviderEvents.Ready, updateEvaluationDetailsCallback);\n }\n\n if (defaultedOptions.updateOnContextChanged) {\n // update when the context changes\n client.addHandler(ProviderEvents.ContextChanged, updateEvaluationDetailsCallback);\n }\n return () => {\n // cleanup the handlers\n client.removeHandler(ProviderEvents.Ready, updateEvaluationDetailsCallback);\n client.removeHandler(ProviderEvents.ContextChanged, updateEvaluationDetailsCallback);\n };\n }, []);\n\n useEffect(() => {\n if (defaultedOptions.updateOnConfigurationChanged) {\n // update when the provider configuration changes\n client.addHandler(ProviderEvents.ConfigurationChanged, configurationChangeCallback);\n }\n return () => {\n // cleanup the handlers\n client.removeHandler(ProviderEvents.ConfigurationChanged, configurationChangeCallback);\n };\n }, []);\n\n return evaluationDetails;\n}\n", "import type { Client } from '@openfeature/web-sdk';\nimport React from 'react';\nimport type { NormalizedOptions, ReactFlagEvaluationOptions } from '../options';\nimport { normalizeOptions } from '.';\n\n/**\n * The underlying React context.\n * DO NOT EXPORT PUBLICLY\n * @internal\n */\nexport const Context = React.createContext<\n { client: Client; domain?: string; options: ReactFlagEvaluationOptions } | undefined\n>(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 { type FlagValue } from '@openfeature/web-sdk';\n\n/**\n * Deeply compare two values to determine if they are equal.\n * Supports primitives and serializable objects.\n * @param {FlagValue} value First value to compare\n * @param {FlagValue} other Second value to compare\n * @returns {boolean} True if the values are equal\n */\nexport function isEqual(value: FlagValue, other: FlagValue): boolean {\n if (value === other) {\n return true;\n }\n\n if (typeof value !== typeof other) {\n return false;\n }\n\n if (typeof value === 'object' && value !== null && other !== null) {\n const valueKeys = Object.keys(value);\n const otherKeys = Object.keys(other);\n\n if (valueKeys.length !== otherKeys.length) {\n return false;\n }\n\n for (const key of valueKeys) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (!isEqual((value as any)[key], (other as any)[key])) {\n return false;\n }\n }\n\n return true;\n }\n\n return false;\n}\n", "import type { ReactFlagEvaluationOptions, NormalizedOptions } from '../options';\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: false,\n suspendWhileReconciling: false,\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 = (\n options: ReactFlagEvaluationOptions = {},\n) => {\n const updateOnContextChanged = options.updateOnContextChanged;\n const updateOnConfigurationChanged = options.updateOnConfigurationChanged;\n\n // fall-back the suspense options to the catch-all `suspend` property\n const suspendUntilReady = 'suspendUntilReady' in options ? options.suspendUntilReady : options.suspend;\n const suspendWhileReconciling =\n 'suspendWhileReconciling' in options ? options.suspendWhileReconciling : options.suspend;\n\n return {\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 ...(typeof updateOnContextChanged === 'boolean' && { updateOnContextChanged }),\n ...(typeof updateOnConfigurationChanged === 'boolean' && { updateOnConfigurationChanged }),\n };\n};\n", "import type { Client} from '@openfeature/web-sdk';\nimport { ProviderEvents } from '@openfeature/web-sdk';\n\n/**\n * Suspends until the client is ready to evaluate feature flags.\n * DO NOT EXPORT PUBLICLY\n * @param {Client} client OpenFeature client\n */\nexport function suspendUntilReady(client: Client): Promise<void> {\n let resolve: (value: unknown) => void;\n let reject: () => void;\n throw new Promise((_resolve, _reject) => {\n resolve = _resolve;\n reject = _reject;\n client.addHandler(ProviderEvents.Ready, resolve);\n client.addHandler(ProviderEvents.Error, reject);\n }).finally(() => {\n client.removeHandler(ProviderEvents.Ready, resolve);\n client.removeHandler(ProviderEvents.Ready, reject);\n });\n}\n", "import React from 'react';\nimport { Context } from '../internal';\nimport type { 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>. If you are seeing this in a test, see: https://openfeature.dev/docs/reference/technologies/client/web/react#testing',\n );\n }\n\n return client;\n}\n", "import { useEffect, useState } from 'react';\nimport { useOpenFeatureClient } from './use-open-feature-client';\nimport type { ProviderStatus } from '@openfeature/web-sdk';\nimport { ProviderEvents } from '@openfeature/web-sdk';\n\n/**\n * Get the {@link ProviderStatus} for the OpenFeatureClient.\n * @returns {ProviderStatus} status of the client for this scope\n */\nexport function useOpenFeatureClientStatus(): ProviderStatus {\n const client = useOpenFeatureClient();\n const [status, setStatus] = useState(client.providerStatus);\n\n useEffect(() => {\n const updateStatus = () => setStatus(client.providerStatus);\n client.addHandler(ProviderEvents.ConfigurationChanged, updateStatus);\n client.addHandler(ProviderEvents.ContextChanged, updateStatus);\n client.addHandler(ProviderEvents.Error, updateStatus);\n client.addHandler(ProviderEvents.Ready, updateStatus);\n client.addHandler(ProviderEvents.Stale, updateStatus);\n client.addHandler(ProviderEvents.Reconciling, updateStatus);\n return () => {\n client.removeHandler(ProviderEvents.ConfigurationChanged, updateStatus);\n client.removeHandler(ProviderEvents.ContextChanged, updateStatus);\n client.removeHandler(ProviderEvents.Error, updateStatus);\n client.removeHandler(ProviderEvents.Ready, updateStatus);\n client.removeHandler(ProviderEvents.Stale, updateStatus);\n client.removeHandler(ProviderEvents.Reconciling, updateStatus);\n };\n }, [client]);\n\n return status;\n}\n", "import type {\n EvaluationDetails,\n FlagValue} from '@openfeature/web-sdk';\nimport {\n StandardResolutionReasons\n} from '@openfeature/web-sdk';\nimport type { FlagQuery } from '../query';\n\n\n// FlagQuery implementation, do not export\nexport class 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 type { Client} from '@openfeature/web-sdk';\nimport { OpenFeature } from '@openfeature/web-sdk';\nimport * as React from 'react';\nimport type { ReactFlagEvaluationOptions } from '../options';\nimport { Context } from '../internal';\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, domain }}>{children}</Context.Provider>;\n}\n", "import { ProviderStatus } from '@openfeature/web-sdk';\nimport { useOpenFeatureClient } from './use-open-feature-client';\nimport { useOpenFeatureClientStatus } from './use-open-feature-client-status';\nimport type { ReactFlagEvaluationOptions } from '../options';\nimport { DEFAULT_OPTIONS, useProviderOptions, normalizeOptions, suspendUntilReady } from '../internal';\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 client = useOpenFeatureClient();\n const status = useOpenFeatureClientStatus();\n // highest priority > evaluation hook options > provider options > default options > lowest priority\n const defaultedOptions = { ...DEFAULT_OPTIONS, ...useProviderOptions(), ...normalizeOptions(options) };\n\n // suspense\n if (defaultedOptions.suspendUntilReady && status === ProviderStatus.NOT_READY) {\n suspendUntilReady(client);\n }\n\n return status === ProviderStatus.READY;\n}\n", "import type {\n JsonValue,\n Provider} from '@openfeature/web-sdk';\nimport {\n InMemoryProvider,\n NOOP_PROVIDER,\n OpenFeature\n} from '@openfeature/web-sdk';\nimport React from 'react';\nimport type { NormalizedOptions } from '../options';\nimport { OpenFeatureProvider } from './provider';\n\ntype FlagValueMap = { [flagKey: string]: JsonValue };\ntype FlagConfig = ConstructorParameters<typeof InMemoryProvider>[0];\ntype TestProviderProps = Omit<React.ComponentProps<typeof OpenFeatureProvider>, 'client'> &\n (\n | {\n provider?: never;\n /**\n * Optional map of flagKeys to flagValues for this OpenFeatureTestProvider context.\n * If not supplied, all flag evaluations will default.\n */\n flagValueMap?: FlagValueMap;\n /**\n * Optional delay for the underlying test provider's readiness and reconciliation.\n * Defaults to 0.\n */\n delayMs?: number;\n }\n | {\n /**\n * An optional partial provider to pass for full control over the flag resolution for this OpenFeatureTestProvider context.\n * Any un-implemented methods or properties will no-op.\n */\n provider?: Partial<Provider>;\n flagValueMap?: never;\n delayMs?: never;\n }\n );\n\n const TEST_VARIANT = 'test-variant';\n const TEST_PROVIDER = 'test-provider';\n\n// internal provider which is basically the in-memory provider with a simpler config and some optional fake delays\nclass TestProvider extends InMemoryProvider {\n\n // initially make this undefined, we still set it if a delay is specified\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore - For maximum compatibility with previous versions, we ignore a possible TS error here,\n // since \"initialize\" was previously defined in superclass.\n // We can safely remove this ts-ignore in a few versions\n initialize: Provider['initialize'] = undefined;\n\n // \"place-holder\" init function which we only assign if want a delay\n private delayedInitialize = async () => {\n await new Promise<void>((resolve) => setTimeout(resolve, this.delay));\n };\n\n constructor(\n flagValueMap: FlagValueMap,\n private delay = 0,\n ) {\n // convert the simple flagValueMap into an in-memory config\n const flagConfig = Object.entries(flagValueMap).reduce((acc: FlagConfig, flag): FlagConfig => {\n return {\n ...acc,\n [flag[0]]: {\n variants: {\n [TEST_VARIANT]: flag[1],\n },\n defaultVariant: TEST_VARIANT,\n disabled: false,\n },\n };\n }, {});\n super(flagConfig);\n // only define and init if there's a non-zero delay specified\n this.initialize = this.delay ? this.delayedInitialize.bind(this) : undefined;\n }\n\n async onContextChange() {\n return new Promise<void>((resolve) => setTimeout(resolve, this.delay));\n }\n}\n\n/**\n * A React Context provider based on the {@link InMemoryProvider}, specifically built for testing.\n * Use this for testing components that use flag evaluation hooks.\n * @param {TestProviderProps} testProviderOptions options for the OpenFeatureTestProvider\n * @returns {OpenFeatureProvider} OpenFeatureTestProvider\n */\nexport function OpenFeatureTestProvider(testProviderOptions: TestProviderProps) {\n const { flagValueMap, provider } = testProviderOptions;\n const effectiveProvider = (\n flagValueMap ? new TestProvider(flagValueMap, testProviderOptions.delayMs) : mixInNoop(provider) || NOOP_PROVIDER\n ) as Provider;\n testProviderOptions.domain\n ? OpenFeature.setProvider(testProviderOptions.domain, effectiveProvider)\n : OpenFeature.setProvider(effectiveProvider);\n\n return (\n <OpenFeatureProvider {...(testProviderOptions as NormalizedOptions)} domain={testProviderOptions.domain}>\n {testProviderOptions.children}\n </OpenFeatureProvider>\n );\n}\n\n// mix in the no-op provider when the partial is passed\nfunction mixInNoop(provider: Partial<Provider> = {}) {\n // fill in any missing methods with no-ops\n for (const prop of Object.getOwnPropertyNames(Object.getPrototypeOf(NOOP_PROVIDER)).filter(prop => prop !== 'constructor')) {\n const patchedProvider = provider as {[key: string]: keyof Provider};\n if (!Object.getPrototypeOf(patchedProvider)[prop] && !patchedProvider[prop]) {\n patchedProvider[prop] = Object.getPrototypeOf(NOOP_PROVIDER)[prop];\n }\n }\n // fill in the metadata if missing\n if (!provider.metadata || !provider.metadata.name) {\n (provider.metadata as unknown) = { name: TEST_PROVIDER };\n }\n return provider;\n}\n", "import { useCallback, useContext, useRef } from 'react';\nimport type { EvaluationContext } from '@openfeature/web-sdk';\nimport { OpenFeature } from '@openfeature/web-sdk';\nimport { Context } from '../internal';\n\nexport type ContextMutationOptions = {\n /**\n * Mutate the default context instead of the domain scoped context applied at the `<OpenFeatureProvider/>`.\n * Note, if the `<OpenFeatureProvider/>` has no domain specified, the default is used.\n * See the {@link https://openfeature.dev/docs/reference/technologies/client/web/#manage-evaluation-context-for-domains|documentation} for more information.\n * @default false\n */\n defaultContext?: boolean;\n};\n\nexport type ContextMutation = {\n /**\n * Context-aware function to set the desired context (see: {@link ContextMutationOptions} for details).\n * There's generally no need to await the result of this function; flag evaluation hooks will re-render when the context is updated.\n * This promise never rejects.\n * @param updatedContext\n * @returns Promise for awaiting the context update\n */\n setContext: (updatedContext: EvaluationContext) => Promise<void>;\n};\n\n/**\n * Get context-aware tracking function(s) for mutating the evaluation context associated with this domain, or the default context if `defaultContext: true`.\n * See the {@link https://openfeature.dev/docs/reference/technologies/client/web/#targeting-and-context|documentation} for more information.\n * @param {ContextMutationOptions} options options for the generated function\n * @returns {ContextMutation} context-aware function(s) to mutate evaluation context\n */\nexport function useContextMutator(options: ContextMutationOptions = { defaultContext: false }): ContextMutation {\n const { domain } = useContext(Context) || {};\n const previousContext = useRef<null | EvaluationContext>(null);\n\n const setContext = useCallback(async (updatedContext: EvaluationContext) => {\n if (previousContext.current !== updatedContext) {\n if (!domain || options?.defaultContext) {\n OpenFeature.setContext(updatedContext);\n } else {\n OpenFeature.setContext(domain, updatedContext);\n }\n previousContext.current = updatedContext;\n }\n }, [domain]);\n\n return {\n setContext,\n };\n}\n", "import type { Tracking, TrackingEventDetails } from '@openfeature/web-sdk';\nimport { useCallback } from 'react';\nimport { useOpenFeatureClient } from '../provider';\n\nexport type Track = {\n /**\n * Context-aware tracking function for the parent `<OpenFeatureProvider/>`.\n * Track a user action or application state, usually representing a business objective or outcome.\n * @param trackingEventName an identifier for the event\n * @param trackingEventDetails the details of the tracking event\n */\n track: Tracking['track'];\n};\n\n/**\n * Get a context-aware tracking function.\n * @returns {Track} context-aware tracking\n */\nexport function useTrack(): Track {\n const client = useOpenFeatureClient();\n\n const track = useCallback((trackingEventName: string, trackingEventDetails?: TrackingEventDetails) => {\n client.track(trackingEventName, trackingEventDetails);\n }, []);\n\n return {\n track,\n };\n}\n", "export * from './evaluation';\nexport * from './query';\nexport * from './provider';\nexport * from './context';\nexport * from './tracking';\nexport * from './options';\n// re-export the web-sdk so consumers can access that API from the react-sdk\nexport * from '@openfeature/web-sdk';\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
6
|
-
"names": ["ProviderEvents", "useEffect", "useState", "
|
|
3
|
+
"sources": ["../../src/evaluation/use-feature-flag.ts", "../../src/internal/context.ts", "../../src/internal/is-equal.ts", "../../src/internal/options.ts", "../../src/internal/suspense.ts", "../../src/internal/use.ts", "../../src/provider/use-open-feature-client.ts", "../../src/internal/errors.ts", "../../src/provider/use-open-feature-client-status.ts", "../../src/provider/use-open-feature-provider.ts", "../../src/internal/hook-flag-query.ts", "../../src/provider/provider.tsx", "../../src/provider/use-when-provider-ready.ts", "../../src/provider/test-provider.tsx", "../../src/context/use-context-mutator.ts", "../../src/tracking/use-track.ts", "../../src/index.ts"],
|
|
4
|
+
"sourcesContent": ["import type {\n Client,\n ClientProviderEvents,\n EvaluationDetails,\n EventHandler,\n FlagEvaluationOptions,\n FlagValue,\n JsonValue,\n} from '@openfeature/web-sdk';\nimport { ProviderEvents, ProviderStatus } from '@openfeature/web-sdk';\nimport { useEffect, useRef, useState } from 'react';\nimport {\n DEFAULT_OPTIONS,\n isEqual,\n normalizeOptions,\n suspendUntilInitialized,\n suspendUntilReconciled,\n useProviderOptions,\n} from '../internal';\nimport type { ReactFlagEvaluationNoSuspenseOptions, ReactFlagEvaluationOptions } from '../options';\nimport { useOpenFeatureClient } from '../provider/use-open-feature-client';\nimport { useOpenFeatureClientStatus } from '../provider/use-open-feature-client-status';\nimport { useOpenFeatureProvider } from '../provider/use-open-feature-provider';\nimport type { FlagQuery } from '../query';\nimport { HookFlagQuery } from '../internal/hook-flag-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<\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 // 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// alias to the return value of useFlag, used to keep useSuspenseFlag consistent\ntype UseFlagReturn<T extends FlagValue> = ReturnType<typeof useFlag<T>>;\n\n/**\n * Equivalent to {@link useFlag} with `options: { suspend: true }`\n * @experimental Suspense is an experimental feature subject to change in future versions.\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 {ReactFlagEvaluationNoSuspenseOptions} options for this evaluation\n * @returns { UseFlagReturn<T> } a queryable object containing useful information about the flag.\n */\nexport function useSuspenseFlag<T extends FlagValue = FlagValue>(\n flagKey: string,\n defaultValue: T,\n options?: ReactFlagEvaluationNoSuspenseOptions,\n): UseFlagReturn<T> {\n return useFlag(flagKey, defaultValue, { ...options, suspendUntilReady: true, suspendWhileReconciling: true });\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\n// determines if a flag should be re-evaluated based on a list of changed flags\nfunction shouldEvaluateFlag(flagKey: string, flagsChanged?: string[]): boolean {\n // if flagsChange is missing entirely, we don't know what to re-render\n return !flagsChanged || flagsChanged.includes(flagKey);\n}\n\nfunction attachHandlersAndResolve<T extends FlagValue>(\n flagKey: string,\n defaultValue: T,\n resolver: (\n client: Client,\n ) => (flagKey: string, defaultValue: T, options?: FlagEvaluationOptions) => 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 client = useOpenFeatureClient();\n const status = useOpenFeatureClientStatus();\n const provider = useOpenFeatureProvider();\n\n const controller = new AbortController();\n\n if (defaultedOptions.suspendUntilReady && status === ProviderStatus.NOT_READY) {\n suspendUntilInitialized(provider, client);\n }\n\n if (defaultedOptions.suspendWhileReconciling && status === ProviderStatus.RECONCILING) {\n suspendUntilReconciled(client);\n }\n\n const [evaluationDetails, setEvaluationDetails] = useState<EvaluationDetails<T>>(\n resolver(client).call(client, flagKey, defaultValue, options),\n );\n\n // Maintain a mutable reference to the evaluation details to have a up-to-date reference in the handlers.\n const evaluationDetailsRef = useRef<EvaluationDetails<T>>(evaluationDetails);\n useEffect(() => {\n evaluationDetailsRef.current = evaluationDetails;\n }, [evaluationDetails]);\n\n const updateEvaluationDetailsCallback = () => {\n const updatedEvaluationDetails = resolver(client).call(client, flagKey, defaultValue, options);\n\n /**\n * Avoid re-rendering if the value hasn't changed. We could expose a means\n * to define a custom comparison function if users require a more\n * sophisticated comparison in the future.\n */\n if (!isEqual(updatedEvaluationDetails.value, evaluationDetailsRef.current.value)) {\n setEvaluationDetails(updatedEvaluationDetails);\n }\n };\n\n const configurationChangeCallback: EventHandler<ClientProviderEvents.ConfigurationChanged> = (eventDetails) => {\n if (shouldEvaluateFlag(flagKey, eventDetails?.flagsChanged)) {\n updateEvaluationDetailsCallback();\n }\n };\n\n useEffect(() => {\n if (status === ProviderStatus.NOT_READY) {\n // update when the provider is ready\n client.addHandler(ProviderEvents.Ready, updateEvaluationDetailsCallback, { signal: controller.signal });\n }\n\n if (defaultedOptions.updateOnContextChanged) {\n // update when the context changes\n client.addHandler(ProviderEvents.ContextChanged, updateEvaluationDetailsCallback, { signal: controller.signal });\n }\n\n if (defaultedOptions.updateOnConfigurationChanged) {\n // update when the provider configuration changes\n client.addHandler(ProviderEvents.ConfigurationChanged, configurationChangeCallback, {\n signal: controller.signal,\n });\n }\n return () => {\n // cleanup the handlers\n controller.abort();\n };\n }, []);\n\n return evaluationDetails;\n}\n", "import type { Client } from '@openfeature/web-sdk';\nimport React from 'react';\nimport type { NormalizedOptions, ReactFlagEvaluationOptions } from '../options';\nimport { normalizeOptions } from '.';\n\n/**\n * The underlying React context.\n *\n * **DO NOT EXPORT PUBLICLY**\n * @internal\n */\nexport const Context = React.createContext<\n { client: Client; domain?: string; options: ReactFlagEvaluationOptions } | undefined\n>(undefined);\n\n/**\n * Get a normalized copy of the options used for this OpenFeatureProvider, see {@link normalizeOptions}.\n *\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 { type FlagValue } from '@openfeature/web-sdk';\n\n/**\n * Deeply compare two values to determine if they are equal.\n * Supports primitives and serializable objects.\n * @param {FlagValue} value First value to compare\n * @param {FlagValue} other Second value to compare\n * @returns {boolean} True if the values are equal\n */\nexport function isEqual(value: FlagValue, other: FlagValue): boolean {\n if (value === other) {\n return true;\n }\n\n if (typeof value !== typeof other) {\n return false;\n }\n\n if (typeof value === 'object' && value !== null && other !== null) {\n const valueKeys = Object.keys(value);\n const otherKeys = Object.keys(other);\n\n if (valueKeys.length !== otherKeys.length) {\n return false;\n }\n\n for (const key of valueKeys) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (!isEqual((value as any)[key], (other as any)[key])) {\n return false;\n }\n }\n\n return true;\n }\n\n return false;\n}\n", "import type { ReactFlagEvaluationOptions, NormalizedOptions } from '../options';\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: false,\n suspendWhileReconciling: false,\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 = (\n options: ReactFlagEvaluationOptions = {},\n) => {\n const updateOnContextChanged = options.updateOnContextChanged;\n const updateOnConfigurationChanged = options.updateOnConfigurationChanged;\n\n // fall-back the suspense options to the catch-all `suspend` property\n const suspendUntilReady = 'suspendUntilReady' in options ? options.suspendUntilReady : options.suspend;\n const suspendWhileReconciling =\n 'suspendWhileReconciling' in options ? options.suspendWhileReconciling : options.suspend;\n\n return {\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 ...(typeof updateOnContextChanged === 'boolean' && { updateOnContextChanged }),\n ...(typeof updateOnConfigurationChanged === 'boolean' && { updateOnConfigurationChanged }),\n };\n};\n", "import type { Client, Provider } from '@openfeature/web-sdk';\nimport { NOOP_PROVIDER, ProviderEvents } from '@openfeature/web-sdk';\nimport { use } from './use';\n\n/**\n * A weak map is used to store the global suspense status for each provider. It's\n * important for this to be global to avoid rerender loops. Using useRef won't\n * work because the value isn't preserved when a promise is thrown in a component,\n * which is how suspense operates.\n */\nconst globalProviderSuspenseStatus = new WeakMap<Provider, Promise<unknown>>();\n\n/**\n * Suspends until the client is ready to evaluate feature flags.\n *\n * **DO NOT EXPORT PUBLICLY**\n * @internal\n * @param {Provider} provider the provider to suspend for\n * @param {Client} client the client to check for readiness\n */\nexport function suspendUntilInitialized(provider: Provider, client: Client) {\n const statusPromiseRef = globalProviderSuspenseStatus.get(provider);\n if (!statusPromiseRef) {\n // Noop provider is never ready, so we resolve immediately\n const statusPromise = provider !== NOOP_PROVIDER ? isProviderReady(client) : Promise.resolve();\n globalProviderSuspenseStatus.set(provider, statusPromise);\n // Use will throw the promise and React will trigger a rerender when it's resolved\n use(statusPromise);\n } else {\n // Reuse the existing promise, use won't rethrow if the promise has settled.\n use(statusPromiseRef);\n }\n}\n\n/**\n * Suspends until the provider has finished reconciling.\n *\n * **DO NOT EXPORT PUBLICLY**\n * @internal\n * @param {Client} client the client to check for readiness\n */\nexport function suspendUntilReconciled(client: Client) {\n use(isProviderReady(client));\n}\n\nasync function isProviderReady(client: Client) {\n const controller = new AbortController();\n try {\n return await new Promise((resolve, reject) => {\n client.addHandler(ProviderEvents.Ready, resolve, { signal: controller.signal });\n client.addHandler(ProviderEvents.Error, reject, { signal: controller.signal });\n });\n } finally {\n controller.abort();\n }\n}\n", "/// <reference types=\"react/experimental\" />\n// This function is adopted from https://github.com/vercel/swr\nimport React from 'react';\n\n/**\n * Extends a Promise-like value to include status tracking.\n * The extra properties are used to manage the lifecycle of the Promise, indicating its current state.\n * More information can be found in the React RFE for the use hook.\n * @see https://github.com/reactjs/rfcs/pull/229\n */\nexport type UsePromise<T> =\n Promise<T> & {\n status?: 'pending' | 'fulfilled' | 'rejected';\n value?: T;\n reason?: unknown;\n };\n\n/**\n * React.use is a React API that lets you read the value of a resource like a Promise or context.\n * It was officially added in React 19, so needs to be polyfilled to support older React versions.\n * @param {UsePromise} thenable A thenable object that represents a Promise-like value.\n * @returns {unknown} The resolved value of the thenable or throws if it's still pending or rejected.\n */\nexport const use =\n React.use ||\n // This extra generic is to avoid TypeScript mixing up the generic and JSX syntax\n // and emitting an error.\n // We assume that this is only for the `use(thenable)` case, not `use(context)`.\n // https://github.com/facebook/react/blob/aed00dacfb79d17c53218404c52b1c7aa59c4a89/packages/react-server/src/ReactFizzThenable.js#L45\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n (<T, _>(thenable: UsePromise<T>): T => {\n switch (thenable.status) {\n case 'pending':\n throw thenable;\n case 'fulfilled':\n return thenable.value as T;\n case 'rejected':\n throw thenable.reason;\n default:\n thenable.status = 'pending';\n thenable.then(\n (v) => {\n thenable.status = 'fulfilled';\n thenable.value = v;\n },\n (e) => {\n thenable.status = 'rejected';\n thenable.reason = e;\n },\n );\n throw thenable;\n }\n });\n", "import React from 'react';\nimport { Context } from '../internal';\nimport { type Client } from '@openfeature/web-sdk';\nimport { MissingContextError } from '../internal/errors';\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 MissingContextError('No OpenFeature client available');\n }\n\n return client;\n}\n", "const context = 'Components using OpenFeature must be wrapped with an <OpenFeatureProvider>.';\nconst tip = 'If you are seeing this in a test, see: https://openfeature.dev/docs/reference/technologies/client/web/react#testing';\n\nexport class MissingContextError extends Error {\n constructor(reason: string) {\n super(`${reason}: ${context} ${tip}`);\n this.name = 'MissingContextError';\n }\n}", "import { useEffect, useState } from 'react';\nimport { useOpenFeatureClient } from './use-open-feature-client';\nimport type { ProviderStatus } from '@openfeature/web-sdk';\nimport { ProviderEvents } from '@openfeature/web-sdk';\n\n/**\n * Get the {@link ProviderStatus} for the OpenFeatureClient.\n * @returns {ProviderStatus} status of the client for this scope\n */\nexport function useOpenFeatureClientStatus(): ProviderStatus {\n const client = useOpenFeatureClient();\n const [status, setStatus] = useState<ProviderStatus>(client.providerStatus);\n const controller = new AbortController();\n\n useEffect(() => {\n const updateStatus = () => setStatus(client.providerStatus);\n client.addHandler(ProviderEvents.ConfigurationChanged, updateStatus, { signal: controller.signal });\n client.addHandler(ProviderEvents.ContextChanged, updateStatus, { signal: controller.signal });\n client.addHandler(ProviderEvents.Error, updateStatus, { signal: controller.signal });\n client.addHandler(ProviderEvents.Ready, updateStatus, { signal: controller.signal });\n client.addHandler(ProviderEvents.Stale, updateStatus, { signal: controller.signal });\n client.addHandler(ProviderEvents.Reconciling, updateStatus, { signal: controller.signal });\n return () => {\n controller.abort();\n };\n }, [client]);\n\n return status;\n}\n", "import React from 'react';\nimport { Context } from '../internal';\nimport { OpenFeature } from '@openfeature/web-sdk';\nimport type { Provider } from '@openfeature/web-sdk';\nimport { MissingContextError } from '../internal/errors';\n\n/**\n * Get the {@link Provider} bound to the domain specified in the OpenFeatureProvider context.\n * Note that it isn't recommended to interact with the provider directly, but rather through\n * an OpenFeature client.\n * @returns {Provider} provider for this scope\n */\nexport function useOpenFeatureProvider(): Provider {\n const openFeatureContext = React.useContext(Context);\n\n if (!openFeatureContext) {\n throw new MissingContextError('No OpenFeature context available');\n }\n\n return OpenFeature.getProvider(openFeatureContext.domain);\n}\n", "import type {\n EvaluationDetails,\n FlagValue} from '@openfeature/web-sdk';\nimport {\n StandardResolutionReasons\n} from '@openfeature/web-sdk';\nimport type { FlagQuery } from '../query';\n\n\n// FlagQuery implementation, do not export\nexport class 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 type { Client} from '@openfeature/web-sdk';\nimport { OpenFeature } from '@openfeature/web-sdk';\nimport * as React from 'react';\nimport type { ReactFlagEvaluationOptions } from '../options';\nimport { Context } from '../internal';\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): JSX.Element {\n if (!client) {\n client = OpenFeature.getClient(domain);\n }\n\n return <Context.Provider value={{ client, options, domain }}>{children}</Context.Provider>;\n}\n", "import { ProviderStatus } from '@openfeature/web-sdk';\nimport { useOpenFeatureClient } from './use-open-feature-client';\nimport { useOpenFeatureClientStatus } from './use-open-feature-client-status';\nimport type { ReactFlagEvaluationOptions } from '../options';\nimport { DEFAULT_OPTIONS, useProviderOptions, normalizeOptions, suspendUntilInitialized } from '../internal';\nimport { useOpenFeatureProvider } from './use-open-feature-provider';\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 // highest priority > evaluation hook options > provider options > default options > lowest priority\n const defaultedOptions = { ...DEFAULT_OPTIONS, ...useProviderOptions(), ...normalizeOptions(options) };\n const client = useOpenFeatureClient();\n const status = useOpenFeatureClientStatus();\n const provider = useOpenFeatureProvider();\n\n if (defaultedOptions.suspendUntilReady && status === ProviderStatus.NOT_READY) {\n suspendUntilInitialized(provider, client);\n }\n\n return status === ProviderStatus.READY;\n}\n", "import type {\n JsonValue,\n Provider} from '@openfeature/web-sdk';\nimport {\n InMemoryProvider,\n NOOP_PROVIDER,\n OpenFeature\n} from '@openfeature/web-sdk';\nimport React from 'react';\nimport type { NormalizedOptions } from '../options';\nimport { OpenFeatureProvider } from './provider';\n\ntype FlagValueMap = { [flagKey: string]: JsonValue };\ntype FlagConfig = ConstructorParameters<typeof InMemoryProvider>[0];\ntype TestProviderProps = Omit<React.ComponentProps<typeof OpenFeatureProvider>, 'client'> &\n (\n | {\n provider?: never;\n /**\n * Optional map of flagKeys to flagValues for this OpenFeatureTestProvider context.\n * If not supplied, all flag evaluations will default.\n */\n flagValueMap?: FlagValueMap;\n /**\n * Optional delay for the underlying test provider's readiness and reconciliation.\n * Defaults to 0.\n */\n delayMs?: number;\n }\n | {\n /**\n * An optional partial provider to pass for full control over the flag resolution for this OpenFeatureTestProvider context.\n * Any un-implemented methods or properties will no-op.\n */\n provider?: Partial<Provider>;\n flagValueMap?: never;\n delayMs?: never;\n }\n );\n\n const TEST_VARIANT = 'test-variant';\n const TEST_PROVIDER = 'test-provider';\n\n// internal provider which is basically the in-memory provider with a simpler config and some optional fake delays\nclass TestProvider extends InMemoryProvider {\n\n // initially make this undefined, we still set it if a delay is specified\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore - For maximum compatibility with previous versions, we ignore a possible TS error here,\n // since \"initialize\" was previously defined in superclass.\n // We can safely remove this ts-ignore in a few versions\n initialize: Provider['initialize'] = undefined;\n\n // \"place-holder\" init function which we only assign if want a delay\n private delayedInitialize = async () => {\n await new Promise<void>((resolve) => setTimeout(resolve, this.delay));\n };\n\n constructor(\n flagValueMap: FlagValueMap,\n private delay = 0,\n ) {\n // convert the simple flagValueMap into an in-memory config\n const flagConfig = Object.entries(flagValueMap).reduce((acc: FlagConfig, flag): FlagConfig => {\n return {\n ...acc,\n [flag[0]]: {\n variants: {\n [TEST_VARIANT]: flag[1],\n },\n defaultVariant: TEST_VARIANT,\n disabled: false,\n },\n };\n }, {});\n super(flagConfig);\n // only define and init if there's a non-zero delay specified\n this.initialize = this.delay ? this.delayedInitialize.bind(this) : undefined;\n }\n\n async onContextChange() {\n return new Promise<void>((resolve) => setTimeout(resolve, this.delay));\n }\n}\n\n/**\n * A React Context provider based on the {@link InMemoryProvider}, specifically built for testing.\n * Use this for testing components that use flag evaluation hooks.\n * @param {TestProviderProps} testProviderOptions options for the OpenFeatureTestProvider\n * @returns {OpenFeatureProvider} OpenFeatureTestProvider\n */\nexport function OpenFeatureTestProvider(testProviderOptions: TestProviderProps) {\n const { flagValueMap, provider } = testProviderOptions;\n const effectiveProvider = (\n flagValueMap ? new TestProvider(flagValueMap, testProviderOptions.delayMs) : mixInNoop(provider) || NOOP_PROVIDER\n ) as Provider;\n testProviderOptions.domain\n ? OpenFeature.setProvider(testProviderOptions.domain, effectiveProvider)\n : OpenFeature.setProvider(effectiveProvider);\n\n return (\n <OpenFeatureProvider {...(testProviderOptions as NormalizedOptions)} domain={testProviderOptions.domain}>\n {testProviderOptions.children}\n </OpenFeatureProvider>\n );\n}\n\n// mix in the no-op provider when the partial is passed\nfunction mixInNoop(provider: Partial<Provider> = {}) {\n // fill in any missing methods with no-ops\n for (const prop of Object.getOwnPropertyNames(Object.getPrototypeOf(NOOP_PROVIDER)).filter(prop => prop !== 'constructor')) {\n const patchedProvider = provider as {[key: string]: keyof Provider};\n if (!Object.getPrototypeOf(patchedProvider)[prop] && !patchedProvider[prop]) {\n patchedProvider[prop] = Object.getPrototypeOf(NOOP_PROVIDER)[prop];\n }\n }\n // fill in the metadata if missing\n if (!provider.metadata || !provider.metadata.name) {\n (provider.metadata as unknown) = { name: TEST_PROVIDER };\n }\n return provider;\n}\n", "import { useCallback, useContext, useRef } from 'react';\nimport type { EvaluationContext } from '@openfeature/web-sdk';\nimport { OpenFeature } from '@openfeature/web-sdk';\nimport { Context } from '../internal';\n\nexport type ContextMutationOptions = {\n /**\n * Mutate the default context instead of the domain scoped context applied at the `<OpenFeatureProvider/>`.\n * Note, if the `<OpenFeatureProvider/>` has no domain specified, the default is used.\n * See the {@link https://openfeature.dev/docs/reference/technologies/client/web/#manage-evaluation-context-for-domains|documentation} for more information.\n * @default false\n */\n defaultContext?: boolean;\n};\n\nexport type ContextMutation = {\n /**\n * Context-aware function to set the desired context (see: {@link ContextMutationOptions} for details).\n * There's generally no need to await the result of this function; flag evaluation hooks will re-render when the context is updated.\n * This promise never rejects.\n * @param updatedContext\n * @returns Promise for awaiting the context update\n */\n setContext: (updatedContext: EvaluationContext) => Promise<void>;\n};\n\n/**\n * Get context-aware tracking function(s) for mutating the evaluation context associated with this domain, or the default context if `defaultContext: true`.\n * See the {@link https://openfeature.dev/docs/reference/technologies/client/web/#targeting-and-context|documentation} for more information.\n * @param {ContextMutationOptions} options options for the generated function\n * @returns {ContextMutation} context-aware function(s) to mutate evaluation context\n */\nexport function useContextMutator(options: ContextMutationOptions = { defaultContext: false }): ContextMutation {\n const { domain } = useContext(Context) || {};\n const previousContext = useRef<null | EvaluationContext>(null);\n\n const setContext = useCallback(async (updatedContext: EvaluationContext) => {\n if (previousContext.current !== updatedContext) {\n if (!domain || options?.defaultContext) {\n OpenFeature.setContext(updatedContext);\n } else {\n OpenFeature.setContext(domain, updatedContext);\n }\n previousContext.current = updatedContext;\n }\n }, [domain]);\n\n return {\n setContext,\n };\n}\n", "import type { Tracking, TrackingEventDetails } from '@openfeature/web-sdk';\nimport { useCallback } from 'react';\nimport { useOpenFeatureClient } from '../provider';\n\nexport type Track = {\n /**\n * Context-aware tracking function for the parent `<OpenFeatureProvider/>`.\n * Track a user action or application state, usually representing a business objective or outcome.\n * @param trackingEventName an identifier for the event\n * @param trackingEventDetails the details of the tracking event\n */\n track: Tracking['track'];\n};\n\n/**\n * Get a context-aware tracking function.\n * @returns {Track} context-aware tracking\n */\nexport function useTrack(): Track {\n const client = useOpenFeatureClient();\n\n const track = useCallback((trackingEventName: string, trackingEventDetails?: TrackingEventDetails) => {\n client.track(trackingEventName, trackingEventDetails);\n }, []);\n\n return {\n track,\n };\n}\n", "export * from './evaluation';\nexport * from './query';\nexport * from './provider';\nexport * from './context';\nexport * from './tracking';\nexport * from './options';\n// re-export the web-sdk so consumers can access that API from the react-sdk\nexport * from '@openfeature/web-sdk';\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AASA,SAAS,kBAAAA,iBAAgB,sBAAsB;AAC/C,SAAS,aAAAC,YAAW,QAAQ,YAAAC,iBAAgB;;;ACT5C,OAAO,WAAW;AAUX,IAAM,UAAU,MAAM,cAE3B,MAAS;AASJ,SAAS,qBAAwC;AACtD,QAAM,EAAE,QAAQ,IAAI,MAAM,WAAW,OAAO,KAAK,CAAC;AAClD,SAAO,iBAAiB,OAAO;AACjC;;;AChBO,SAAS,QAAQ,OAAkB,OAA2B;AACnE,MAAI,UAAU,OAAO;AACnB,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,UAAU,OAAO,OAAO;AACjC,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,UAAU,MAAM;AACjE,UAAM,YAAY,OAAO,KAAK,KAAK;AACnC,UAAM,YAAY,OAAO,KAAK,KAAK;AAEnC,QAAI,UAAU,WAAW,UAAU,QAAQ;AACzC,aAAO;AAAA,IACT;AAEA,eAAW,OAAO,WAAW;AAE3B,UAAI,CAAC,QAAS,MAAc,GAAG,GAAI,MAAc,GAAG,CAAC,GAAG;AACtD,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;;;AC9BO,IAAM,kBAA8C;AAAA,EACzD,wBAAwB;AAAA,EACxB,8BAA8B;AAAA,EAC9B,mBAAmB;AAAA,EACnB,yBAAyB;AAC3B;AASO,IAAM,mBAAgF,CAC3F,UAAsC,CAAC,MACpC;AACH,QAAM,yBAAyB,QAAQ;AACvC,QAAM,+BAA+B,QAAQ;AAG7C,QAAM,oBAAoB,uBAAuB,UAAU,QAAQ,oBAAoB,QAAQ;AAC/F,QAAM,0BACJ,6BAA6B,UAAU,QAAQ,0BAA0B,QAAQ;AAEnF,SAAO,gEAED,OAAO,sBAAsB,aAAa,EAAE,kBAAkB,IAC9D,OAAO,4BAA4B,aAAa,EAAE,wBAAwB,IAC1E,OAAO,2BAA2B,aAAa,EAAE,uBAAuB,IACxE,OAAO,iCAAiC,aAAa,EAAE,6BAA6B;AAE5F;;;ACtCA,SAAS,eAAe,sBAAsB;;;ACC9C,OAAOC,YAAW;AAqBX,IAAM,MACXA,OAAM;AAAA;AAAA;AAAA;AAAA;AAAA,CAML,CAAO,aAA+B;AACrC,UAAQ,SAAS,QAAQ;AAAA,IACvB,KAAK;AACH,YAAM;AAAA,IACR,KAAK;AACH,aAAO,SAAS;AAAA,IAClB,KAAK;AACH,YAAM,SAAS;AAAA,IACjB;AACE,eAAS,SAAS;AAClB,eAAS;AAAA,QACP,CAAC,MAAM;AACL,mBAAS,SAAS;AAClB,mBAAS,QAAQ;AAAA,QACnB;AAAA,QACA,CAAC,MAAM;AACL,mBAAS,SAAS;AAClB,mBAAS,SAAS;AAAA,QACpB;AAAA,MACF;AACA,YAAM;AAAA,EACV;AACF;;;AD1CF,IAAM,+BAA+B,oBAAI,QAAoC;AAUtE,SAAS,wBAAwB,UAAoB,QAAgB;AAC1E,QAAM,mBAAmB,6BAA6B,IAAI,QAAQ;AAClE,MAAI,CAAC,kBAAkB;AAErB,UAAM,gBAAgB,aAAa,gBAAgB,gBAAgB,MAAM,IAAI,QAAQ,QAAQ;AAC7F,iCAA6B,IAAI,UAAU,aAAa;AAExD,QAAI,aAAa;AAAA,EACnB,OAAO;AAEL,QAAI,gBAAgB;AAAA,EACtB;AACF;AASO,SAAS,uBAAuB,QAAgB;AACrD,MAAI,gBAAgB,MAAM,CAAC;AAC7B;AAEA,SAAe,gBAAgB,QAAgB;AAAA;AAC7C,UAAM,aAAa,IAAI,gBAAgB;AACvC,QAAI;AACF,aAAO,MAAM,IAAI,QAAQ,CAAC,SAAS,WAAW;AAC5C,eAAO,WAAW,eAAe,OAAO,SAAS,EAAE,QAAQ,WAAW,OAAO,CAAC;AAC9E,eAAO,WAAW,eAAe,OAAO,QAAQ,EAAE,QAAQ,WAAW,OAAO,CAAC;AAAA,MAC/E,CAAC;AAAA,IACH,UAAE;AACA,iBAAW,MAAM;AAAA,IACnB;AAAA,EACF;AAAA;;;AEvDA,OAAOC,YAAW;;;ACAlB,IAAM,UAAU;AAChB,IAAM,MAAM;AAEL,IAAM,sBAAN,cAAkC,MAAM;AAAA,EAC7C,YAAY,QAAgB;AAC1B,UAAM,GAAG,MAAM,KAAK,OAAO,IAAI,GAAG,EAAE;AACpC,SAAK,OAAO;AAAA,EACd;AACF;;;ADEO,SAAS,uBAA+B;AAC7C,QAAM,EAAE,OAAO,IAAIC,OAAM,WAAW,OAAO,KAAK,CAAC;AAEjD,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,oBAAoB,iCAAiC;AAAA,EACjE;AAEA,SAAO;AACT;;;AElBA,SAAS,WAAW,gBAAgB;AAGpC,SAAS,kBAAAC,uBAAsB;AAMxB,SAAS,6BAA6C;AAC3D,QAAM,SAAS,qBAAqB;AACpC,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAyB,OAAO,cAAc;AAC1E,QAAM,aAAa,IAAI,gBAAgB;AAEvC,YAAU,MAAM;AACd,UAAM,eAAe,MAAM,UAAU,OAAO,cAAc;AAC1D,WAAO,WAAWA,gBAAe,sBAAsB,cAAc,EAAE,QAAQ,WAAW,OAAO,CAAC;AAClG,WAAO,WAAWA,gBAAe,gBAAgB,cAAc,EAAE,QAAQ,WAAW,OAAO,CAAC;AAC5F,WAAO,WAAWA,gBAAe,OAAO,cAAc,EAAE,QAAQ,WAAW,OAAO,CAAC;AACnF,WAAO,WAAWA,gBAAe,OAAO,cAAc,EAAE,QAAQ,WAAW,OAAO,CAAC;AACnF,WAAO,WAAWA,gBAAe,OAAO,cAAc,EAAE,QAAQ,WAAW,OAAO,CAAC;AACnF,WAAO,WAAWA,gBAAe,aAAa,cAAc,EAAE,QAAQ,WAAW,OAAO,CAAC;AACzF,WAAO,MAAM;AACX,iBAAW,MAAM;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAEX,SAAO;AACT;;;AC5BA,OAAOC,YAAW;AAElB,SAAS,mBAAmB;AAUrB,SAAS,yBAAmC;AACjD,QAAM,qBAAqBC,OAAM,WAAW,OAAO;AAEnD,MAAI,CAAC,oBAAoB;AACvB,UAAM,IAAI,oBAAoB,kCAAkC;AAAA,EAClE;AAEA,SAAO,YAAY,YAAY,mBAAmB,MAAM;AAC1D;;;ACjBA;AAAA,EACE;AAAA,OACK;AAKA,IAAM,gBAAN,MAA0E;AAAA,EAC/E,YAAoB,UAAgC;AAAhC;AAAA,EAAiC;AAAA,EAErD,IAAI,UAAU;AACZ,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,QAAQ;AAjBd;AAkBI,YAAO,UAAK,aAAL,mBAAe;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;AAjChB;AAkCI,WAAO,CAAC,GAAC,UAAK,aAAL,mBAAe,cAAa,KAAK,SAAS,UAAU,0BAA0B;AAAA,EACzF;AAAA,EAEA,IAAI,YAAY;AArClB;AAsCI,YAAO,UAAK,aAAL,mBAAe;AAAA,EACxB;AAAA,EAEA,IAAI,eAAe;AAzCrB;AA0CI,YAAO,UAAK,aAAL,mBAAe;AAAA,EACxB;AAAA,EAEA,IAAI,kBAAkB;AACpB,WACE,CAAC,KAAK,WACN,KAAK,SAAS,UAAU,0BAA0B,SAClD,KAAK,SAAS,UAAU,0BAA0B;AAAA,EAEtD;AAAA,EAEA,IAAI,OAAO;AACT,WAAO,OAAO,KAAK,SAAS;AAAA,EAC9B;AACF;;;AVJO,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;AAcO,SAAS,gBACd,SACA,cACA,SACkB;AAClB,SAAO,QAAQ,SAAS,cAAc,iCAAK,UAAL,EAAc,mBAAmB,MAAM,yBAAyB,KAAK,EAAC;AAC9G;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;AAGA,SAAS,mBAAmB,SAAiB,cAAkC;AAE7E,SAAO,CAAC,gBAAgB,aAAa,SAAS,OAAO;AACvD;AAEA,SAAS,yBACP,SACA,cACA,UAGA,SACsB;AAEtB,QAAM,mBAAmB,iDAAK,kBAAoB,mBAAmB,IAAM,iBAAiB,OAAO;AACnG,QAAM,SAAS,qBAAqB;AACpC,QAAM,SAAS,2BAA2B;AAC1C,QAAM,WAAW,uBAAuB;AAExC,QAAM,aAAa,IAAI,gBAAgB;AAEvC,MAAI,iBAAiB,qBAAqB,WAAW,eAAe,WAAW;AAC7E,4BAAwB,UAAU,MAAM;AAAA,EAC1C;AAEA,MAAI,iBAAiB,2BAA2B,WAAW,eAAe,aAAa;AACrF,2BAAuB,MAAM;AAAA,EAC/B;AAEA,QAAM,CAAC,mBAAmB,oBAAoB,IAAIC;AAAA,IAChD,SAAS,MAAM,EAAE,KAAK,QAAQ,SAAS,cAAc,OAAO;AAAA,EAC9D;AAGA,QAAM,uBAAuB,OAA6B,iBAAiB;AAC3E,EAAAC,WAAU,MAAM;AACd,yBAAqB,UAAU;AAAA,EACjC,GAAG,CAAC,iBAAiB,CAAC;AAEtB,QAAM,kCAAkC,MAAM;AAC5C,UAAM,2BAA2B,SAAS,MAAM,EAAE,KAAK,QAAQ,SAAS,cAAc,OAAO;AAO7F,QAAI,CAAC,QAAQ,yBAAyB,OAAO,qBAAqB,QAAQ,KAAK,GAAG;AAChF,2BAAqB,wBAAwB;AAAA,IAC/C;AAAA,EACF;AAEA,QAAM,8BAAuF,CAAC,iBAAiB;AAC7G,QAAI,mBAAmB,SAAS,6CAAc,YAAY,GAAG;AAC3D,sCAAgC;AAAA,IAClC;AAAA,EACF;AAEA,EAAAA,WAAU,MAAM;AACd,QAAI,WAAW,eAAe,WAAW;AAEvC,aAAO,WAAWC,gBAAe,OAAO,iCAAiC,EAAE,QAAQ,WAAW,OAAO,CAAC;AAAA,IACxG;AAEA,QAAI,iBAAiB,wBAAwB;AAE3C,aAAO,WAAWA,gBAAe,gBAAgB,iCAAiC,EAAE,QAAQ,WAAW,OAAO,CAAC;AAAA,IACjH;AAEA,QAAI,iBAAiB,8BAA8B;AAEjD,aAAO,WAAWA,gBAAe,sBAAsB,6BAA6B;AAAA,QAClF,QAAQ,WAAW;AAAA,MACrB,CAAC;AAAA,IACH;AACA,WAAO,MAAM;AAEX,iBAAW,MAAM;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO;AACT;;;AWhWA,SAAS,eAAAC,oBAAmB;AAC5B,YAAYC,YAAW;AA+BhB,SAAS,oBAAoB,IAAsE;AAAtE,eAAE,UAAQ,QAAQ,SAjCtD,IAiCoC,IAA+B,oBAA/B,IAA+B,CAA7B,UAAQ,UAAQ;AACpD,MAAI,CAAC,QAAQ;AACX,aAASC,aAAY,UAAU,MAAM;AAAA,EACvC;AAEA,SAAO,qCAAC,QAAQ,UAAR,EAAiB,OAAO,EAAE,QAAQ,SAAS,OAAO,KAAI,QAAS;AACzE;;;ACvCA,SAAS,kBAAAC,uBAAsB;AAgBxB,SAAS,qBAAqB,SAA4B;AAE/D,QAAM,mBAAmB,iDAAK,kBAAoB,mBAAmB,IAAM,iBAAiB,OAAO;AACnG,QAAM,SAAS,qBAAqB;AACpC,QAAM,SAAS,2BAA2B;AAC1C,QAAM,WAAW,uBAAuB;AAExC,MAAI,iBAAiB,qBAAqB,WAAWC,gBAAe,WAAW;AAC7E,4BAAwB,UAAU,MAAM;AAAA,EAC1C;AAEA,SAAO,WAAWA,gBAAe;AACnC;;;ACzBA;AAAA,EACE;AAAA,EACA,iBAAAC;AAAA,EACA,eAAAC;AAAA,OACK;AACP,OAAOC,YAAW;AAgChB,IAAM,eAAe;AACrB,IAAM,gBAAgB;AAGxB,IAAM,eAAN,cAA2B,iBAAiB;AAAA,EAc1C,YACE,cACQ,QAAQ,GAChB;AAEA,UAAM,aAAa,OAAO,QAAQ,YAAY,EAAE,OAAO,CAAC,KAAiB,SAAqB;AAC5F,aAAO,iCACF,MADE;AAAA,QAEL,CAAC,KAAK,CAAC,CAAC,GAAG;AAAA,UACT,UAAU;AAAA,YACR,CAAC,YAAY,GAAG,KAAK,CAAC;AAAA,UACxB;AAAA,UACA,gBAAgB;AAAA,UAChB,UAAU;AAAA,QACZ;AAAA,MACF;AAAA,IACF,GAAG,CAAC,CAAC;AACL,UAAM,UAAU;AAfR;AATV;AAAA;AAAA;AAAA;AAAA;AAAA,sBAAqC;AAGrC;AAAA,SAAQ,oBAAoB,MAAY;AACtC,YAAM,IAAI,QAAc,CAAC,YAAY,WAAW,SAAS,KAAK,KAAK,CAAC;AAAA,IACtE;AAqBE,SAAK,aAAa,KAAK,QAAQ,KAAK,kBAAkB,KAAK,IAAI,IAAI;AAAA,EACrE;AAAA,EAEM,kBAAkB;AAAA;AACtB,aAAO,IAAI,QAAc,CAAC,YAAY,WAAW,SAAS,KAAK,KAAK,CAAC;AAAA,IACvE;AAAA;AACF;AAQO,SAAS,wBAAwB,qBAAwC;AAC9E,QAAM,EAAE,cAAc,SAAS,IAAI;AACnC,QAAM,oBACJ,eAAe,IAAI,aAAa,cAAc,oBAAoB,OAAO,IAAI,UAAU,QAAQ,KAAKC;AAEtG,sBAAoB,SAChBC,aAAY,YAAY,oBAAoB,QAAQ,iBAAiB,IACrEA,aAAY,YAAY,iBAAiB;AAE7C,SACE,gBAAAC,OAAA,cAAC,sDAAyB,sBAAzB,EAAoE,QAAQ,oBAAoB,WAC9F,oBAAoB,QACvB;AAEJ;AAGA,SAAS,UAAU,WAA8B,CAAC,GAAG;AAEnD,aAAW,QAAQ,OAAO,oBAAoB,OAAO,eAAeF,cAAa,CAAC,EAAE,OAAO,CAAAG,UAAQA,UAAS,aAAa,GAAG;AAC1H,UAAM,kBAAkB;AACxB,QAAI,CAAC,OAAO,eAAe,eAAe,EAAE,IAAI,KAAK,CAAC,gBAAgB,IAAI,GAAG;AAC3E,sBAAgB,IAAI,IAAI,OAAO,eAAeH,cAAa,EAAE,IAAI;AAAA,IACnE;AAAA,EACF;AAEA,MAAI,CAAC,SAAS,YAAY,CAAC,SAAS,SAAS,MAAM;AACjD,IAAC,SAAS,WAAuB,EAAE,MAAM,cAAc;AAAA,EACzD;AACA,SAAO;AACT;;;ACzHA,SAAS,aAAa,YAAY,UAAAI,eAAc;AAEhD,SAAS,eAAAC,oBAAmB;AA8BrB,SAAS,kBAAkB,UAAkC,EAAE,gBAAgB,MAAM,GAAoB;AAC5G,QAAM,EAAE,OAAO,IAAI,WAAW,OAAO,KAAK,CAAC;AAC3C,QAAM,kBAAkBC,QAAiC,IAAI;AAE7D,QAAM,aAAa,YAAY,CAAO,mBAAsC;AACxE,QAAI,gBAAgB,YAAY,gBAAgB;AAC5C,UAAI,CAAC,WAAU,mCAAS,iBAAgB;AACpC,QAAAC,aAAY,WAAW,cAAc;AAAA,MACzC,OAAO;AACH,QAAAA,aAAY,WAAW,QAAQ,cAAc;AAAA,MACjD;AACA,sBAAgB,UAAU;AAAA,IAC9B;AAAA,EACJ,IAAG,CAAC,MAAM,CAAC;AAEX,SAAO;AAAA,IACH;AAAA,EACJ;AACJ;;;ACjDA,SAAS,eAAAC,oBAAmB;AAiBrB,SAAS,WAAkB;AAChC,QAAM,SAAS,qBAAqB;AAEpC,QAAM,QAAQC,aAAY,CAAC,mBAA2B,yBAAgD;AACpG,WAAO,MAAM,mBAAmB,oBAAoB;AAAA,EACtD,GAAG,CAAC,CAAC;AAEL,SAAO;AAAA,IACL;AAAA,EACF;AACF;;;ACrBA,cAAc;",
|
|
6
|
+
"names": ["ProviderEvents", "useEffect", "useState", "React", "React", "React", "ProviderEvents", "React", "React", "useState", "useEffect", "ProviderEvents", "OpenFeature", "React", "OpenFeature", "ProviderStatus", "ProviderStatus", "NOOP_PROVIDER", "OpenFeature", "React", "NOOP_PROVIDER", "OpenFeature", "React", "prop", "useRef", "OpenFeature", "useRef", "OpenFeature", "useCallback", "useCallback"]
|
|
7
7
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FlagEvaluationOptions, FlagValue as FlagValue$1, JsonValue, EvaluationDetails as EvaluationDetails$1, Client, Provider, EvaluationContext, Tracking } from '@openfeature/web-sdk';
|
|
1
|
+
import { FlagEvaluationOptions, FlagValue as FlagValue$1, JsonValue, EvaluationDetails as EvaluationDetails$1, Client, ProviderStatus, Provider, EvaluationContext, Tracking } 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';
|
|
@@ -230,7 +230,7 @@ type ProviderProps = {
|
|
|
230
230
|
* @param {ProviderProps} properties props for the context provider
|
|
231
231
|
* @returns {OpenFeatureProvider} context provider
|
|
232
232
|
*/
|
|
233
|
-
declare function OpenFeatureProvider({ client, domain, children, ...options }: ProviderProps):
|
|
233
|
+
declare function OpenFeatureProvider({ client, domain, children, ...options }: ProviderProps): JSX.Element;
|
|
234
234
|
|
|
235
235
|
/**
|
|
236
236
|
* Get the {@link Client} instance for this OpenFeatureProvider context.
|
|
@@ -249,6 +249,12 @@ type Options = Pick<ReactFlagEvaluationOptions, 'suspendUntilReady'>;
|
|
|
249
249
|
*/
|
|
250
250
|
declare function useWhenProviderReady(options?: Options): boolean;
|
|
251
251
|
|
|
252
|
+
/**
|
|
253
|
+
* Get the {@link ProviderStatus} for the OpenFeatureClient.
|
|
254
|
+
* @returns {ProviderStatus} status of the client for this scope
|
|
255
|
+
*/
|
|
256
|
+
declare function useOpenFeatureClientStatus(): ProviderStatus;
|
|
257
|
+
|
|
252
258
|
type FlagValueMap = {
|
|
253
259
|
[flagKey: string]: JsonValue;
|
|
254
260
|
};
|
|
@@ -323,4 +329,4 @@ type Track = {
|
|
|
323
329
|
*/
|
|
324
330
|
declare function useTrack(): Track;
|
|
325
331
|
|
|
326
|
-
export { type ContextMutation, type ContextMutationOptions, type FlagQuery, type NormalizedOptions, OpenFeatureProvider, OpenFeatureTestProvider, type ReactFlagEvaluationNoSuspenseOptions, type ReactFlagEvaluationOptions, type Track, useBooleanFlagDetails, useBooleanFlagValue, useContextMutator, useFlag, useNumberFlagDetails, useNumberFlagValue, useObjectFlagDetails, useObjectFlagValue, useOpenFeatureClient, useStringFlagDetails, useStringFlagValue, useSuspenseFlag, useTrack, useWhenProviderReady };
|
|
332
|
+
export { type ContextMutation, type ContextMutationOptions, type FlagQuery, type NormalizedOptions, OpenFeatureProvider, OpenFeatureTestProvider, type ReactFlagEvaluationNoSuspenseOptions, type ReactFlagEvaluationOptions, type Track, useBooleanFlagDetails, useBooleanFlagValue, useContextMutator, useFlag, useNumberFlagDetails, useNumberFlagValue, useObjectFlagDetails, useObjectFlagValue, useOpenFeatureClient, useOpenFeatureClientStatus, useStringFlagDetails, useStringFlagValue, useSuspenseFlag, useTrack, useWhenProviderReady };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openfeature/react-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "OpenFeature React SDK",
|
|
5
5
|
"main": "./dist/cjs/index.js",
|
|
6
6
|
"files": [
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"homepage": "https://github.com/open-feature/js-sdk#readme",
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"@openfeature/web-sdk": "^1.
|
|
50
|
+
"@openfeature/web-sdk": "^1.5.0",
|
|
51
51
|
"react": ">=16.8.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|