@openfeature/server-sdk 1.20.0 → 1.20.2

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/LICENSE CHANGED
@@ -186,7 +186,7 @@
186
186
  same "printed page" as the copyright notice for easier
187
187
  identification within third-party archives.
188
188
 
189
- Copyright 2025 OpenFeature Maintainers
189
+ Copyright OpenFeature Maintainers
190
190
 
191
191
  Licensed under the Apache License, Version 2.0 (the "License");
192
192
  you may not use this file except in compliance with the License.
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/server-sdk-v1.20.0">
20
- <img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v1.20.0&color=blue&style=for-the-badge" />
19
+ <a href="https://github.com/open-feature/js-sdk/releases/tag/server-sdk-v1.20.2">
20
+ <img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v1.20.2&color=blue&style=for-the-badge" />
21
21
  </a>
22
22
  <!-- x-release-please-end -->
23
23
  <br/>
@@ -88,7 +88,7 @@ const client = OpenFeature.getClient();
88
88
  const v2Enabled = await client.getBooleanValue('v2_enabled', false);
89
89
 
90
90
  if (v2Enabled) {
91
- console.log("v2 is enabled");
91
+ console.log('v2 is enabled');
92
92
  }
93
93
  ```
94
94
 
@@ -98,18 +98,19 @@ See [here](https://open-feature.github.io/js-sdk/modules/_openfeature_server_sdk
98
98
 
99
99
  ## 🌟 Features
100
100
 
101
- | Status | Features | Description |
102
- | ------ | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
103
- | ✅ | [Providers](#providers) | Integrate with a commercial, open source, or in-house feature management tool. |
104
- | ✅ | [Targeting](#targeting) | Contextually-aware flag evaluation using [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context). |
105
- | ✅ | [Hooks](#hooks) | Add functionality to various stages of the flag evaluation life-cycle. |
106
- | ✅ | [Logging](#logging) | Integrate with popular logging packages. |
107
- | ✅ | [Domains](#domains) | Logically bind clients with providers. |
108
- | ✅ | [Eventing](#eventing) | React to state changes in the provider or flag management system. |
109
- | ✅ | [Transaction Context Propagation](#transaction-context-propagation) | Set a specific [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context) for a transaction (e.g. an HTTP request or a thread) |
110
- | ✅ | [Tracking](#tracking) | Associate user actions with feature flag evaluations, particularly for A/B testing. |
111
- | ✅ | [Shutdown](#shutdown) | Gracefully clean up a provider during application shutdown. |
112
- | ✅ | [Extending](#extending) | Extend OpenFeature with custom providers and hooks. |
101
+ | Status | Features | Description |
102
+ | ------ | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
103
+ | ✅ | [Providers](#providers) | Integrate with a commercial, open source, or in-house feature management tool. |
104
+ | ✅ | [Targeting](#targeting) | Contextually-aware flag evaluation using [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context). |
105
+ | ✅ | [Hooks](#hooks) | Add functionality to various stages of the flag evaluation life-cycle. |
106
+ | ✅ | [Logging](#logging) | Integrate with popular logging packages. |
107
+ | ✅ | [Domains](#domains) | Logically bind clients with providers. |
108
+ | ✅ | [Eventing](#eventing) | React to state changes in the provider or flag management system. |
109
+ | ✅ | [Transaction Context Propagation](#transaction-context-propagation) | Set a specific [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context) for a transaction (e.g. an HTTP request or a thread) |
110
+ | ✅ | [Tracking](#tracking) | Associate user actions with feature flag evaluations, particularly for A/B testing. |
111
+ | ✅ | [Shutdown](#shutdown) | Gracefully clean up a provider during application shutdown. |
112
+ | ✅ | [Extending](#extending) | Extend OpenFeature with custom providers and hooks. |
113
+ | ✅ | [Multi-Provider](#multi-provider) | Combine multiple providers with configurable evaluation strategies. |
113
114
 
114
115
  <sub>Implemented: ✅ | In-progress: ⚠️ | Not implemented yet: ❌</sub>
115
116
 
@@ -161,10 +162,7 @@ const primaryProvider = new YourPrimaryProvider();
161
162
  const backupProvider = new YourBackupProvider();
162
163
 
163
164
  // Create multi-provider with a strategy
164
- const multiProvider = new MultiProvider(
165
- [primaryProvider, backupProvider],
166
- new FirstMatchStrategy()
167
- );
165
+ const multiProvider = new MultiProvider([primaryProvider, backupProvider], new FirstMatchStrategy());
168
166
 
169
167
  // Register the multi-provider
170
168
  await OpenFeature.setProviderAndWait(multiProvider);
@@ -190,7 +188,7 @@ const oldProvider = new OldFlagProvider();
190
188
 
191
189
  const multiProvider = new MultiProvider(
192
190
  [newProvider, oldProvider], // New provider is consulted first
193
- new FirstMatchStrategy()
191
+ new FirstMatchStrategy(),
194
192
  );
195
193
 
196
194
  await OpenFeature.setProviderAndWait(multiProvider);
@@ -206,13 +204,10 @@ const providerA = new ProviderA();
206
204
  const providerB = new ProviderB();
207
205
 
208
206
  const multiProvider = new MultiProvider(
209
- [
210
- { provider: providerA },
211
- { provider: providerB }
212
- ],
207
+ [{ provider: providerA }, { provider: providerB }],
213
208
  new ComparisonStrategy(providerA, (resolutions) => {
214
209
  console.warn('Mismatch detected', resolutions);
215
- })
210
+ }),
216
211
  );
217
212
 
218
213
  await OpenFeature.setProviderAndWait(multiProvider);
@@ -226,7 +221,7 @@ If the flag management system you're using supports targeting, you can provide t
226
221
 
227
222
  ```ts
228
223
  // set a value to the global context
229
- OpenFeature.setContext({ region: "us-east-1" });
224
+ OpenFeature.setContext({ region: 'us-east-1' });
230
225
 
231
226
  // set a value to the client context
232
227
  const client = OpenFeature.getClient();
@@ -236,7 +231,7 @@ client.setContext({ version: process.env.APP_VERSION });
236
231
  const requestContext = {
237
232
  targetingKey: req.session.id,
238
233
  email: req.session.email,
239
- product: req.productId
234
+ product: req.productId,
240
235
  };
241
236
 
242
237
  const boolValue = await client.getBooleanValue('some-flag', false, requestContext);
@@ -254,7 +249,7 @@ If the hook you're looking for hasn't been created yet, see the [develop a hook]
254
249
  Once you've added a hook as a dependency, it can be registered at the global, client, or flag invocation level.
255
250
 
256
251
  ```ts
257
- import { OpenFeature } from "@openfeature/server-sdk";
252
+ import { OpenFeature } from '@openfeature/server-sdk';
258
253
 
259
254
  // add a hook globally, to run on all evaluations
260
255
  OpenFeature.addHooks(new ExampleGlobalHook());
@@ -264,7 +259,7 @@ const client = OpenFeature.getClient();
264
259
  client.addHooks(new ExampleClientHook());
265
260
 
266
261
  // add a hook for this evaluation only
267
- const boolValue = await client.getBooleanValue("bool-flag", false, { hooks: [new ExampleHook()]});
262
+ const boolValue = await client.getBooleanValue('bool-flag', false, { hooks: [new ExampleHook()] });
268
263
  ```
269
264
 
270
265
  ### Logging
@@ -274,7 +269,7 @@ This behavior can be overridden by passing a custom logger either globally or pe
274
269
  A custom logger must implement the [Logger interface](../shared/src/logger/logger.ts).
275
270
 
276
271
  ```ts
277
- import type { Logger } from "@openfeature/server-sdk";
272
+ import type { Logger } from '@openfeature/server-sdk';
278
273
 
279
274
  // The logger can be anything that conforms with the Logger interface
280
275
  const logger: Logger = console;
@@ -294,28 +289,28 @@ A domain is a logical identifier which can be used to associate clients with a p
294
289
  If a domain has no associated provider, the default provider is used.
295
290
 
296
291
  ```ts
297
- import { OpenFeature, InMemoryProvider } from "@openfeature/server-sdk";
292
+ import { OpenFeature, InMemoryProvider } from '@openfeature/server-sdk';
298
293
 
299
294
  const myFlags = {
300
- 'v2_enabled': {
295
+ v2_enabled: {
301
296
  variants: {
302
297
  on: true,
303
- off: false
298
+ off: false,
304
299
  },
305
300
  disabled: false,
306
- defaultVariant: "on"
307
- }
301
+ defaultVariant: 'on',
302
+ },
308
303
  };
309
304
 
310
305
  // Registering the default provider
311
306
  OpenFeature.setProvider(InMemoryProvider(myFlags));
312
307
  // Registering a provider to a domain
313
- OpenFeature.setProvider("my-domain", new InMemoryProvider(someOtherFlags));
308
+ OpenFeature.setProvider('my-domain', new InMemoryProvider(someOtherFlags));
314
309
 
315
310
  // A Client bound to the default provider
316
311
  const clientWithDefault = OpenFeature.getClient();
317
312
  // A Client bound to the InMemoryProvider provider
318
- const domainScopedClient = OpenFeature.getClient("my-domain");
313
+ const domainScopedClient = OpenFeature.getClient('my-domain');
319
314
  ```
320
315
 
321
316
  Domains can be defined on a provider during registration.
@@ -352,22 +347,22 @@ Transaction context can be set where specific data is available (e.g. an auth se
352
347
  The following example shows an Express middleware using transaction context propagation to propagate the request ip and user id into request scoped transaction context.
353
348
 
354
349
  ```ts
355
- import express, { Request, Response, NextFunction } from "express";
350
+ import express, { Request, Response, NextFunction } from 'express';
356
351
  import { OpenFeature, AsyncLocalStorageTransactionContextPropagator } from '@openfeature/server-sdk';
357
352
 
358
- OpenFeature.setTransactionContextPropagator(new AsyncLocalStorageTransactionContextPropagator())
353
+ OpenFeature.setTransactionContextPropagator(new AsyncLocalStorageTransactionContextPropagator());
359
354
 
360
355
  /**
361
356
  * This example is based on an express middleware.
362
357
  */
363
358
  const app = express();
364
359
  app.use((req: Request, res: Response, next: NextFunction) => {
365
- const ip = res.headers.get("X-Forwarded-For")
360
+ const ip = res.headers.get('X-Forwarded-For');
366
361
  OpenFeature.setTransactionContext({ targetingKey: req.user.id, ipAddress: ip }, () => {
367
362
  // The transaction context is used in any flag evaluation throughout the whole call chain of next
368
363
  next();
369
364
  });
370
- })
365
+ });
371
366
  ```
372
367
 
373
368
  ### Tracking
@@ -393,7 +388,7 @@ This should only be called when your application is in the process of shutting d
393
388
  ```ts
394
389
  import { OpenFeature } from '@openfeature/server-sdk';
395
390
 
396
- await OpenFeature.close()
391
+ await OpenFeature.close();
397
392
  ```
398
393
 
399
394
  ## Extending
@@ -413,7 +408,7 @@ import {
413
408
  Logger,
414
409
  Provider,
415
410
  ProviderEventEmitter,
416
- ResolutionDetails
411
+ ResolutionDetails,
417
412
  } from '@openfeature/server-sdk';
418
413
 
419
414
  // implement the provider interface
@@ -425,16 +420,36 @@ class MyProvider implements Provider {
425
420
  } as const;
426
421
  // Optional provider managed hooks
427
422
  hooks?: Hook[];
428
- resolveBooleanEvaluation(flagKey: string, defaultValue: boolean, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<boolean>> {
423
+ resolveBooleanEvaluation(
424
+ flagKey: string,
425
+ defaultValue: boolean,
426
+ context: EvaluationContext,
427
+ logger: Logger,
428
+ ): Promise<ResolutionDetails<boolean>> {
429
429
  // code to evaluate a boolean
430
430
  }
431
- resolveStringEvaluation(flagKey: string, defaultValue: string, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<string>> {
431
+ resolveStringEvaluation(
432
+ flagKey: string,
433
+ defaultValue: string,
434
+ context: EvaluationContext,
435
+ logger: Logger,
436
+ ): Promise<ResolutionDetails<string>> {
432
437
  // code to evaluate a string
433
438
  }
434
- resolveNumberEvaluation(flagKey: string, defaultValue: number, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<number>> {
439
+ resolveNumberEvaluation(
440
+ flagKey: string,
441
+ defaultValue: number,
442
+ context: EvaluationContext,
443
+ logger: Logger,
444
+ ): Promise<ResolutionDetails<number>> {
435
445
  // code to evaluate a number
436
446
  }
437
- resolveObjectEvaluation<T extends JsonValue>(flagKey: string, defaultValue: T, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<T>> {
447
+ resolveObjectEvaluation<T extends JsonValue>(
448
+ flagKey: string,
449
+ defaultValue: T,
450
+ context: EvaluationContext,
451
+ logger: Logger,
452
+ ): Promise<ResolutionDetails<T>> {
438
453
  // code to evaluate an object
439
454
  }
440
455
 
@@ -459,7 +474,7 @@ This can be a new repository or included in [the existing contrib repository](ht
459
474
  Implement your own hook by conforming to the [Hook interface](../shared/src/hooks/hook.ts).
460
475
 
461
476
  ```ts
462
- import type { Hook, HookContext, EvaluationDetails, FlagValue } from "@openfeature/server-sdk";
477
+ import type { Hook, HookContext, EvaluationDetails, FlagValue } from '@openfeature/server-sdk';
463
478
 
464
479
  export class MyHook implements Hook {
465
480
  after(hookContext: HookContext, evaluationDetails: EvaluationDetails<FlagValue>) {