@openfeature/web-sdk 0.3.11 โ†’ 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,237 +1,329 @@
1
1
  <!-- markdownlint-disable MD033 -->
2
+ <!-- x-hide-in-docs-start -->
2
3
  <p align="center">
3
4
  <picture>
4
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/open-feature/community/0e23508c163a6a1ac8c0ced3e4bd78faafe627c7/assets/logo/horizontal/white/openfeature-horizontal-white.svg">
5
- <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/open-feature/community/0e23508c163a6a1ac8c0ced3e4bd78faafe627c7/assets/logo/horizontal/black/openfeature-horizontal-black.svg">
6
- <img align="center" alt="OpenFeature Logo">
5
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/open-feature/community/0e23508c163a6a1ac8c0ced3e4bd78faafe627c7/assets/logo/horizontal/white/openfeature-horizontal-white.svg" />
6
+ <img align="center" alt="OpenFeature Logo" src="https://raw.githubusercontent.com/open-feature/community/0e23508c163a6a1ac8c0ced3e4bd78faafe627c7/assets/logo/horizontal/black/openfeature-horizontal-black.svg" />
7
7
  </picture>
8
8
  </p>
9
9
 
10
10
  <h2 align="center">OpenFeature Web SDK</h2>
11
11
 
12
- [![Project Status: WIP โ€“ Initial development is in progress, but there has not yet been a stable, usable release suitable for the public.](https://www.repostatus.org/badges/latest/wip.svg)](https://www.repostatus.org/#wip)
13
- [![npm version](https://badge.fury.io/js/@openfeature%2Fweb-sdk.svg)](https://www.npmjs.com/package/@openfeature/web-sdk)
14
- [![Specification](https://img.shields.io/static/v1?label=Specification&message=v0.6.0&color=yellow)](https://github.com/open-feature/spec/tree/v0.6.0)
15
-
16
- ## ๐Ÿงช This SDK is experimental
17
-
18
- The Web SDK is under development and based on a experimental client concepts.
19
- For more information, see this [issue](https://github.com/open-feature/spec/issues/167).
20
-
21
- ## ๐Ÿ‘‹ Hey there! Thanks for checking out the OpenFeature Web SDK
22
-
23
- ### What is OpenFeature?
12
+ <!-- x-hide-in-docs-end -->
13
+ <!-- The 'github-badges' class is used in the docs -->
14
+ <p align="center" class="github-badges">
15
+ <a href="https://github.com/open-feature/spec/tree/v0.7.0">
16
+ <img alt="Specification" src="https://img.shields.io/static/v1?label=specification&message=v0.7.0&color=yellow&style=for-the-badge" />
17
+ </a>
18
+ <!-- x-release-please-start-version -->
19
+ <a href="https://github.com/open-feature/js-sdk/releases/tag/web-sdk-v0.4.1">
20
+ <img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v0.4.1&color=blue&style=for-the-badge" />
21
+ </a>
22
+ <!-- x-release-please-end -->
23
+ <br/>
24
+ <a href="https://www.repostatus.org/#wip">
25
+ <img alt="Project Status" src="https://www.repostatus.org/badges/latest/wip.svg" />
26
+ </a>
27
+ <a href="https://open-feature.github.io/js-sdk/modules/OpenFeature_Web_SDK.html">
28
+ <img alt="API Reference" src="https://img.shields.io/badge/reference-teal?logo=javascript&logoColor=white" />
29
+ </a>
30
+ <a href="https://www.npmjs.com/package/@openfeature/web-sdk">
31
+ <img alt="NPM Download" src="https://img.shields.io/npm/dm/%40openfeature%2Fweb-sdk" />
32
+ </a>
33
+ <a href="https://codecov.io/gh/open-feature/js-sdk">
34
+ <img alt="codecov" src="https://codecov.io/gh/open-feature/js-sdk/branch/main/graph/badge.svg?token=3DC5XOEHMY" />
35
+ </a>
36
+ <a href="https://bestpractices.coreinfrastructure.org/projects/6594">
37
+ <img alt="CII Best Practices" src="https://bestpractices.coreinfrastructure.org/projects/6594/badge" />
38
+ </a>
39
+ </p>
40
+ <!-- x-hide-in-docs-start -->
24
41
 
25
- [OpenFeature][openfeature-website] is an open standard that provides a vendor-agnostic, community-driven API for feature
26
- flagging that works with your favorite feature flag management tool.
42
+ [OpenFeature](https://openfeature.dev) is an open standard that provides a vendor-agnostic, community-driven API for feature flagging that works with your favorite feature flag management tool.
27
43
 
28
- ### Why standardize feature flags?
44
+ <!-- x-hide-in-docs-end -->
29
45
 
30
- Standardizing feature flags unifies tools and vendors behind a common interface which avoids vendor lock-in at the code
31
- level. Additionally, it offers a framework for building extensions and integrations and allows providers to focus on
32
- their unique value proposition.
46
+ ## ๐Ÿš€ Quick start
33
47
 
34
- ## ๐Ÿ” Requirements:
48
+ ### Requirements
35
49
 
36
50
  - ES2015-compatible web browser (Chrome, Edge, Firefox, etc)
37
51
 
38
- ## ๐Ÿ“ฆ Installation:
52
+ ### Install
39
53
 
40
- ### npm
54
+ #### npm
41
55
 
42
56
  ```sh
43
- npm install @openfeature/web-sdk
57
+ npm install --save @openfeature/web-sdk
44
58
  ```
45
59
 
46
- ### yarn
60
+ #### yarn
47
61
 
48
62
  ```sh
49
63
  yarn add @openfeature/web-sdk
50
64
  ```
51
65
 
52
- ## ๐ŸŒŸ Features:
66
+ ### Usage
53
67
 
54
- - support for various [providers](https://openfeature.dev/docs/reference/concepts/provider)
55
- - easy integration and extension via [hooks](https://openfeature.dev/docs/reference/concepts/hooks)
56
- - handle flags of any type: bool, string, numeric and object
57
- - [context-aware](https://openfeature.dev/docs/reference/concepts/evaluation-context) evaluation
68
+ ```ts
69
+ import { OpenFeature } from '@openfeature/web-sdk';
58
70
 
59
- ## ๐Ÿš€ Usage:
71
+ // Register your feature flag provider
72
+ OpenFeature.setProvider(new YourProviderOfChoice());
60
73
 
61
- ### Basics:
74
+ // create a new client
75
+ const client = OpenFeature.getClient();
62
76
 
63
- ```typescript
64
- import { OpenFeature } from '@openfeature/web-sdk';
77
+ // Evaluate your feature flag
78
+ const v2Enabled = client.getBooleanValue('v2_enabled', false);
65
79
 
66
- // configure a provider
67
- await OpenFeature.setProvider(new YourProviderOfChoice());
80
+ if (v2Enabled) {
81
+ console.log("v2 is enabled");
82
+ }
83
+ ```
68
84
 
69
- // create a client
70
- const client = OpenFeature.getClient('my-app');
85
+ ### API Reference
71
86
 
72
- // get a bool flag value
73
- const boolValue = client.getBooleanValue('boolFlag', false);
74
- ```
87
+ See [here](https://open-feature.github.io/js-sdk/modules/OpenFeature_Web_SDK.html) for the complete API documentation.
88
+
89
+ ## ๐ŸŒŸ Features
90
+
91
+ | Status | Features | Description |
92
+ | ------ | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
93
+ | โœ… | [Providers](#providers) | Integrate with a commercial, open source, or in-house feature management tool. |
94
+ | โœ… | [Targeting](#targeting-and-context) | Contextually-aware flag evaluation using [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context). |
95
+ | โœ… | [Hooks](#hooks) | Add functionality to various stages of the flag evaluation life-cycle. |
96
+ | โœ… | [Logging](#logging) | Integrate with popular logging packages. |
97
+ | โœ… | [Named clients](#named-clients) | Utilize multiple providers in a single application. |
98
+ | โœ… | [Eventing](#eventing) | React to state changes in the provider or flag management system. |
99
+ | โœ… | [Shutdown](#shutdown) | Gracefully clean up a provider during application shutdown. |
100
+ | โœ… | [Extending](#extending) | Extend OpenFeature with custom providers and hooks. |
75
101
 
76
- ### Context-aware evaluation:
102
+ <sub>Implemented: โœ… | In-progress: โš ๏ธ | Not implemented yet: โŒ</sub>
77
103
 
78
- Sometimes the value of a flag must take into account some dynamic criteria about the application or user, such as the
79
- user location, IP, email address, or the location of the server.
80
- In OpenFeature, we refer to this as [`targeting`](https://openfeature.dev/specification/glossary#targeting).
81
- If the flag system you're using supports targeting, you can provide the input data using the `EvaluationContext`.
104
+ ### Providers
82
105
 
83
- ```typescript
84
- // global context for static data
85
- await OpenFeature.setContext({ origin: document.location.host })
106
+ [Providers](https://openfeature.dev/docs/reference/concepts/provider) are an abstraction between a flag management system and the OpenFeature SDK.
107
+ Look [here](https://openfeature.dev/ecosystem/?instant_search%5BrefinementList%5D%5Btype%5D%5B0%5D=Provider&instant_search%5BrefinementList%5D%5Bcategory%5D%5B0%5D=Client-side&instant_search%5BrefinementList%5D%5Btechnology%5D%5B0%5D=JavaScript) for a complete list of available providers.
108
+ If the provider you're looking for hasn't been created yet, see the [develop a provider](#develop-a-provider) section to learn how to build it yourself.
86
109
 
87
- // use contextual data to determine a flag value
88
- const boolValue = client.getBooleanValue('some-flag', false);
110
+ Once you've added a provider as a dependency, it can be registered with OpenFeature like this:
111
+
112
+ ```ts
113
+ OpenFeature.setProvider(new MyProvider())
89
114
  ```
90
115
 
91
- ### Providers:
116
+ In some situations, it may be beneficial to register multiple providers in the same application.
117
+ This is possible using [named clients](#named-clients), which is covered in more detail below.
92
118
 
93
- To develop a provider, you need to create a new project and include the OpenFeature SDK as a dependency. This can be a
94
- new repository or included in an existing contrib repository available under the OpenFeature organization. Finally,
95
- youโ€™ll then need to write the provider itself. In most languages, this can be accomplished by implementing the provider
96
- interface exported by the OpenFeature SDK.
119
+ ### Flag evaluation flow
97
120
 
98
- ```typescript
99
- import { JsonValue, Provider, ResolutionDetails } from '@openfeature/web-sdk';
121
+ When a new provider is added to OpenFeature client the following process happens:
100
122
 
101
- // implement the provider interface
102
- class MyProvider implements Provider {
103
- readonly metadata = {
104
- name: 'My Provider'
105
- } as const;
123
+ ```mermaid
124
+ sequenceDiagram
125
+ autonumber
126
+ Client-->+Feature Flag Provider: ResolveAll (context)
127
+ Feature Flag Provider-->-Client: Flags values
128
+ ```
106
129
 
107
- resolveBooleanEvaluation(flagKey: string, defaultValue: boolean): ResolutionDetails<boolean> {
108
- // resolve a boolean flag value
109
- }
130
+ In (1) the Client sends a request to the provider backend in order to get all values from all feature flags that it has.
131
+ Once the provider backend replies (2) the client holds all flag values and therefore the flag evaluation process is synchronous.
110
132
 
111
- resolveStringEvaluation(flagKey: string, defaultValue: string): ResolutionDetails<string> {
112
- // resolve a string flag value
113
- }
133
+ In order to prevent flag evaluation to the default value while flags are still being fetched, it is highly recommended to only look for flag value after the provider has emitted the `Ready` event.
134
+ The following code snippet provides an example.
114
135
 
115
- resolveNumberEvaluation(flagKey: string, defaultValue: number): ResolutionDetails<number> {
116
- // resolve a numeric flag value
117
- }
136
+ ```ts
137
+ import { OpenFeature, ProviderEvents } from '@openfeature/web-sdk';
118
138
 
119
- resolveObjectEvaluation<T extends JsonValue>(flagKey: string, defaultValue: T): ResolutionDetails<T> {
120
- // resolve an object flag value
121
- }
122
- ```
139
+ OpenFeature.setProvider( /*set a provider*/ );
123
140
 
124
- See [here](https://openfeature.dev/docs/reference/technologies/server/javascript) for a catalog of available providers.
141
+ // OpenFeature API
142
+ OpenFeature.addHandler(ProviderEvents.Ready, () => {
143
+ const client = OpenFeature.getClient();
144
+ const stringFlag = client.getStringValue('string-flag', "default value"))
125
145
 
126
- ### Hooks:
146
+ //use stringFlag from this point
147
+ });
148
+ ```
127
149
 
128
- Hooks are a mechanism that allow for the addition of arbitrary behavior at well-defined points of the flag evaluation
129
- life-cycle. Use cases include validation of the resolved flag value, modifying or adding data to the evaluation context,
130
- logging, telemetry, and tracking.
150
+ ### Targeting and Context
131
151
 
132
- ```typescript
133
- import { OpenFeature, Hook, HookContext } from '@openfeature/web-sdk';
152
+ Sometimes, the value of a flag must consider some dynamic criteria about the application or user, such as the user's location, IP, email address, or the server's location.
153
+ In OpenFeature, we refer to this as [targeting](https://openfeature.dev/specification/glossary#targeting).
154
+ If the flag management system you're using supports targeting, you can provide the input data using the [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context).
134
155
 
135
- // Example hook that logs if an error occurs during flag evaluation
136
- export class GlobalDebugHook implements Hook {
137
- after(hookContext: HookContext, err: Error) {
138
- console.log('hook context', hookContext);
139
- console.error(err);
140
- }
141
- }
156
+ ```ts
157
+ // Set a value to the global context
158
+ await OpenFeature.setContext({ origin: document.location.host });
142
159
  ```
143
160
 
144
- See [here](https://openfeature.dev/docs/reference/technologies/server/javascript) for a catalog of available hooks.
161
+ Context is global and setting it is `async`.
162
+ Providers may implement an `onContextChanged` method that receives the old context and the newer one.
163
+ This method is used internally by the provider to detect if, given the context change, the flags values cached on client side are invalid. If needed a request will be made to the provider with the new context in order to get the correct flags values.
145
164
 
146
- ### Logging:
165
+ ### Hooks
147
166
 
148
- You can implement the `Logger` interface (compatible with the `console` object, and implementations from common logging
149
- libraries such as [winston](https://www.npmjs.com/package/winston)) and set it on the global API object.
167
+ [Hooks](https://openfeature.dev/docs/reference/concepts/hooks) allow for custom logic to be added at well-defined points of the flag evaluation life-cycle
168
+ Look [here](https://openfeature.dev/ecosystem/?instant_search%5BrefinementList%5D%5Btype%5D%5B0%5D=Hook&instant_search%5BrefinementList%5D%5Bcategory%5D%5B0%5D=Client-side&instant_search%5BrefinementList%5D%5Btechnology%5D%5B0%5D=JavaScript) for a complete list of available hooks.
169
+ If the hook you're looking for hasn't been created yet, see the [develop a hook](#develop-a-hook) section to learn how to build it yourself.
150
170
 
151
- ```typescript
152
- // implement logger
153
- class MyLogger implements Logger {
154
- error(...args: unknown[]): void {
155
- // implement me
156
- }
171
+ Once you've added a hook as a dependency, it can be registered at the global, client, or flag invocation level.
157
172
 
158
- warn(...args: unknown[]): void {
159
- // implement me
160
- }
173
+ ```ts
174
+ import { OpenFeature } from "@openfeature/web-sdk";
161
175
 
162
- info(...args: unknown[]): void {
163
- // implement me
164
- }
176
+ // add a hook globally, to run on all evaluations
177
+ OpenFeature.addHooks(new ExampleGlobalHook());
165
178
 
166
- debug(...args: unknown[]): void {
167
- // implement me
168
- }
169
- }
179
+ // add a hook on this client, to run on all evaluations made by this client
180
+ const client = OpenFeature.getClient();
181
+ client.addHooks(new ExampleClientHook());
170
182
 
171
- // set the logger
172
- OpenFeature.setLogger(new MyLogger());
183
+ // add a hook for this evaluation only
184
+ const boolValue = client.getBooleanValue("bool-flag", false, { hooks: [new ExampleHook()]});
173
185
  ```
174
186
 
175
- ### Named clients:
187
+ ### Logging
176
188
 
177
- You can have several clients, that can be referenced by a name.
178
- Every client can have a different provider assigned. If no provider is assigned to a named client, the global default
179
- provider is used.
189
+ The JS SDK will log warnings and errors to the console by default.
190
+ This behavior can be overridden by passing a custom logger either globally or per client.
191
+ A custom logger must implement the [Logger interface](../shared/src/logger/logger.ts).
180
192
 
181
- ```typescript
182
- import { OpenFeature, ProviderEvents } from '@openfeature/web-sdk';
193
+ ```ts
194
+ import type { Logger } from "@openfeature/web-sdk";
183
195
 
184
- OpenFeature.setProvider(new YourProviderOfChoice())
185
- OpenFeature.setProvider("client-1", new YourOtherProviderOfChoice())
196
+ // The logger can be anything that conforms with the Logger interface
197
+ const logger: Logger = console;
186
198
 
187
- // Uses YourProviderOfChoice (the default)
188
- const unnamedClient = OpenFeature.getClient()
199
+ // Sets a global logger
200
+ OpenFeature.setLogger(logger);
189
201
 
190
- // Uses YourOtherProviderOfChoice as it is set explicitly
191
- const client1 = OpenFeature.getClient("client-1")
202
+ // Sets a client logger
203
+ const client = OpenFeature.getClient();
204
+ client.setLogger(logger);
205
+ ```
192
206
 
193
- // Uses YourProviderOfChoice as no provider is set
194
- const client2 = OpenFeature.getClient("client-2")
207
+ ### Named clients
208
+
209
+ Clients can be given a name.
210
+ A name is a logical identifier that can be used to associate clients with a particular provider.
211
+ If a name has no associated provider, the global provider is used.
212
+
213
+ ```ts
214
+ import { OpenFeature } from "@openfeature/web-sdk";
215
+
216
+ // Registering the default provider
217
+ OpenFeature.setProvider(NewLocalProvider());
218
+ // Registering a named provider
219
+ OpenFeature.setProvider("clientForCache", new NewCachedProvider());
220
+
221
+ // A Client backed by default provider
222
+ const clientWithDefault = OpenFeature.getClient();
223
+ // A Client backed by NewCachedProvider
224
+ const clientForCache = OpenFeature.getClient("clientForCache");
195
225
  ```
196
226
 
197
- ### Events:
227
+ ### Eventing
198
228
 
199
- Events provide a way to react to state changes in the provider or underlying flag management system.
200
- You can listen to events of either the OpenFeature API or individual clients.
229
+ Events allow you to react to state changes in the provider or underlying flag management system, such as flag definition changes, provider readiness, or error conditions.
230
+ Initialization events (`PROVIDER_READY` on success, `PROVIDER_ERROR` on failure) are dispatched for every provider.
231
+ Some providers support additional events, such as `PROVIDER_CONFIGURATION_CHANGED`.
201
232
 
202
- The events after initialization, `PROVIDER_READY` on success, `PROVIDER_ERROR` on failure during initialization,
203
- are dispatched for every provider.
204
- However, other event types may not be supported by your provider.
205
233
  Please refer to the documentation of the provider you're using to see what events are supported.
206
234
 
207
- ```typescript
235
+ ```ts
208
236
  import { OpenFeature, ProviderEvents } from '@openfeature/web-sdk';
209
237
 
210
238
  // OpenFeature API
211
239
  OpenFeature.addHandler(ProviderEvents.Ready, (eventDetails) => {
212
- console.log(`Ready event from: ${eventDetails.clientName}:`, eventDetails);
240
+ console.log(`Ready event from: ${eventDetails?.clientName}:`, eventDetails);
213
241
  });
214
242
 
215
243
  // Specific client
216
244
  const client = OpenFeature.getClient();
217
- client.addHandler(ProviderEvents.Error, async (eventDetails) => {
218
- console.log(`Error event from: ${eventDetails.clientName}:`, eventDetails);
245
+ client.addHandler(ProviderEvents.Error, (eventDetails) => {
246
+ console.log(`Error event from: ${eventDetails?.clientName}:`, eventDetails);
219
247
  });
220
248
  ```
221
249
 
222
- ### Shutdown:
250
+ ### Shutdown
223
251
 
224
252
  The OpenFeature API provides a close function to perform a cleanup of all registered providers.
225
253
  This should only be called when your application is in the process of shutting down.
226
254
 
227
- ```typescript
228
- import { OpenFeature, ProviderEvents } from '@openfeature/web-sdk';
255
+ ```ts
256
+ import { OpenFeature } from '@openfeature/web-sdk';
229
257
 
230
258
  await OpenFeature.close()
231
259
  ```
232
260
 
233
- ### Complete API documentation:
261
+ ## Extending
234
262
 
235
- See [here](https://open-feature.github.io/js-sdk/modules/OpenFeature_Web_SDK.html) for the complete API documentation.
263
+ ### Develop a provider
264
+
265
+ To develop a provider, you need to create a new project and include the OpenFeature SDK as a dependency.
266
+ This can be a new repository or included in [the existing contrib repository](https://github.com/open-feature/js-sdk-contrib) available under the OpenFeature organization.
267
+ Youโ€™ll then need to write the provider by implementing the [Provider interface](./src/provider/provider.ts) exported by the OpenFeature SDK.
268
+
269
+ ```ts
270
+ import { JsonValue, Provider, ResolutionDetails } from '@openfeature/web-sdk';
271
+
272
+ // implement the provider interface
273
+ class MyProvider implements Provider {
274
+
275
+ readonly metadata = {
276
+ name: 'My Provider',
277
+ } as const;
278
+
279
+ // Optional provider managed hooks
280
+ hooks?: Hook<FlagValue>[];
281
+
282
+ resolveBooleanEvaluation(flagKey: string, defaultValue: boolean, context: EvaluationContext, logger: Logger): ResolutionDetails<boolean> {
283
+ // code to evaluate a boolean
284
+ }
285
+
286
+ resolveStringEvaluation(flagKey: string, defaultValue: string, context: EvaluationContext, logger: Logger): ResolutionDetails<string> {
287
+ // code to evaluate a string
288
+ }
289
+
290
+ resolveNumberEvaluation(flagKey: string, defaultValue: number, context: EvaluationContext, logger: Logger): ResolutionDetails<number> {
291
+ // code to evaluate a number
292
+ }
293
+
294
+ resolveObjectEvaluation<T extends JsonValue>(flagKey: string, defaultValue: T, context: EvaluationContext, logger: Logger): ResolutionDetails<T> {
295
+ // code to evaluate an object
296
+ }
297
+
298
+ status?: ProviderStatus | undefined;
299
+ events?: OpenFeatureEventEmitter | undefined;
300
+
301
+ initialize?(context?: EvaluationContext | undefined): Promise<void> {
302
+ // code to initialize your provider
303
+ }
304
+
305
+ onClose?(): Promise<void> {
306
+ // code to shut down your provider
307
+ }
308
+ }
309
+ ```
310
+
311
+ > Built a new provider? [Let us know](https://github.com/open-feature/openfeature.dev/issues/new?assignees=&labels=provider&projects=&template=document-provider.yaml&title=%5BProvider%5D%3A+) so we can add it to the docs!
312
+
313
+ ### Develop a hook
314
+
315
+ To develop a hook, you need to create a new project and include the OpenFeature SDK as a dependency.
316
+ This can be a new repository or included in [the existing contrib repository](https://github.com/open-feature/js-sdk-contrib) available under the OpenFeature organization.
317
+ Implement your own hook by conforming to the [Hook interface](../shared/src/hooks/hook.ts).
318
+
319
+ ```ts
320
+ import type { Hook, HookContext, EvaluationDetails, FlagValue } from "@openfeature/web-sdk";
321
+
322
+ export class MyHook implements Hook {
323
+ after(hookContext: HookContext, evaluationDetails: EvaluationDetails<FlagValue>) {
324
+ // code that runs when there's an error during a flag evaluation
325
+ }
326
+ }
327
+ ```
236
328
 
237
- [openfeature-website]: https://openfeature.dev
329
+ > Built a new hook? [Let us know](https://github.com/open-feature/openfeature.dev/issues/new?assignees=&labels=hook&projects=&template=document-hook.yaml&title=%5BHook%5D%3A+) so we can add it to the docs!