@openfeature/server-sdk 1.11.0 → 1.13.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 +55 -31
- package/dist/cjs/index.js +93 -60
- package/dist/cjs/index.js.map +4 -4
- package/dist/esm/index.js +86 -43
- package/dist/esm/index.js.map +3 -3
- package/dist/types.d.ts +99 -70
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
<img alt="Specification" src="https://img.shields.io/static/v1?label=specification&message=v0.7.0&color=yellow&style=for-the-badge" />
|
|
17
17
|
</a>
|
|
18
18
|
<!-- x-release-please-start-version -->
|
|
19
|
-
<a href="https://github.com/open-feature/js-sdk/releases/tag/server-sdk-v1.
|
|
20
|
-
<img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v1.
|
|
19
|
+
<a href="https://github.com/open-feature/js-sdk/releases/tag/server-sdk-v1.13.0">
|
|
20
|
+
<img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v1.13.0&color=blue&style=for-the-badge" />
|
|
21
21
|
</a>
|
|
22
22
|
<!-- x-release-please-end -->
|
|
23
23
|
<br/>
|
|
@@ -86,16 +86,17 @@ See [here](https://open-feature.github.io/js-sdk/modules/_openfeature_server_sdk
|
|
|
86
86
|
|
|
87
87
|
## 🌟 Features
|
|
88
88
|
|
|
89
|
-
| Status | Features
|
|
90
|
-
|
|
91
|
-
| ✅ | [Providers](#providers)
|
|
92
|
-
| ✅ | [Targeting](#targeting)
|
|
93
|
-
| ✅ | [Hooks](#hooks)
|
|
94
|
-
| ✅ | [Logging](#logging)
|
|
95
|
-
| ✅ | [
|
|
96
|
-
| ✅ | [Eventing](#eventing)
|
|
97
|
-
| ✅ | [Shutdown](#shutdown)
|
|
98
|
-
| ✅ | [
|
|
89
|
+
| Status | Features | Description |
|
|
90
|
+
|--------|---------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
91
|
+
| ✅ | [Providers](#providers) | Integrate with a commercial, open source, or in-house feature management tool. |
|
|
92
|
+
| ✅ | [Targeting](#targeting) | Contextually-aware flag evaluation using [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context). |
|
|
93
|
+
| ✅ | [Hooks](#hooks) | Add functionality to various stages of the flag evaluation life-cycle. |
|
|
94
|
+
| ✅ | [Logging](#logging) | Integrate with popular logging packages. |
|
|
95
|
+
| ✅ | [Domains](#domains) | Logically bind clients with providers. |
|
|
96
|
+
| ✅ | [Eventing](#eventing) | React to state changes in the provider or flag management system. |
|
|
97
|
+
| ✅ | [Shutdown](#shutdown) | Gracefully clean up a provider during application shutdown. |
|
|
98
|
+
| ✅ | [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) | |
|
|
99
|
+
| ✅ | [Extending](#extending) | Extend OpenFeature with custom providers and hooks. |
|
|
99
100
|
|
|
100
101
|
<sub>Implemented: ✅ | In-progress: ⚠️ | Not implemented yet: ❌</sub>
|
|
101
102
|
|
|
@@ -113,7 +114,7 @@ To register a provider and ensure it is ready before further actions are taken,
|
|
|
113
114
|
|
|
114
115
|
```ts
|
|
115
116
|
await OpenFeature.setProviderAndWait(new MyProvider());
|
|
116
|
-
```
|
|
117
|
+
```
|
|
117
118
|
|
|
118
119
|
#### Synchronous
|
|
119
120
|
|
|
@@ -126,7 +127,7 @@ OpenFeature.setProvider(new MyProvider());
|
|
|
126
127
|
Once the provider has been registered, the status can be tracked using [events](#eventing).
|
|
127
128
|
|
|
128
129
|
In some situations, it may be beneficial to register multiple providers in the same application.
|
|
129
|
-
This is possible using [
|
|
130
|
+
This is possible using [domains](#domains), which is covered in more details below.
|
|
130
131
|
|
|
131
132
|
### Targeting
|
|
132
133
|
|
|
@@ -154,7 +155,7 @@ const boolValue = await client.getBooleanValue('some-flag', false, requestContex
|
|
|
154
155
|
|
|
155
156
|
### Hooks
|
|
156
157
|
|
|
157
|
-
[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
|
|
158
|
+
[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.
|
|
158
159
|
Look [here](https://openfeature.dev/ecosystem/?instant_search%5BrefinementList%5D%5Btype%5D%5B0%5D=Hook&instant_search%5BrefinementList%5D%5Bcategory%5D%5B0%5D=Server-side&instant_search%5BrefinementList%5D%5Btechnology%5D%5B0%5D=JavaScript) for a complete list of available hooks.
|
|
159
160
|
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.
|
|
160
161
|
|
|
@@ -176,7 +177,7 @@ const boolValue = await client.getBooleanValue("bool-flag", false, { hooks: [new
|
|
|
176
177
|
|
|
177
178
|
### Logging
|
|
178
179
|
|
|
179
|
-
The JS SDK will log
|
|
180
|
+
The Node.JS SDK will log warnings and errors to the console by default.
|
|
180
181
|
This behavior can be overridden by passing a custom logger either globally or per client.
|
|
181
182
|
A custom logger must implement the [Logger interface](../shared/src/logger/logger.ts).
|
|
182
183
|
|
|
@@ -186,7 +187,7 @@ import type { Logger } from "@openfeature/server-sdk";
|
|
|
186
187
|
// The logger can be anything that conforms with the Logger interface
|
|
187
188
|
const logger: Logger = console;
|
|
188
189
|
|
|
189
|
-
// Sets a global logger
|
|
190
|
+
// Sets a global logger
|
|
190
191
|
OpenFeature.setLogger(logger);
|
|
191
192
|
|
|
192
193
|
// Sets a client logger
|
|
@@ -194,11 +195,11 @@ const client = OpenFeature.getClient();
|
|
|
194
195
|
client.setLogger(logger);
|
|
195
196
|
```
|
|
196
197
|
|
|
197
|
-
###
|
|
198
|
+
### Domains
|
|
198
199
|
|
|
199
|
-
Clients can be
|
|
200
|
-
A
|
|
201
|
-
If a
|
|
200
|
+
Clients can be assigned to a domain.
|
|
201
|
+
A domain is a logical identifier which can be used to associate clients with a particular provider.
|
|
202
|
+
If a domain has no associated provider, the default provider is used.
|
|
202
203
|
|
|
203
204
|
```ts
|
|
204
205
|
import { OpenFeature, InMemoryProvider } from "@openfeature/server-sdk";
|
|
@@ -216,16 +217,16 @@ const myFlags = {
|
|
|
216
217
|
|
|
217
218
|
// Registering the default provider
|
|
218
219
|
OpenFeature.setProvider(InMemoryProvider(myFlags));
|
|
219
|
-
// Registering a
|
|
220
|
-
OpenFeature.setProvider("
|
|
220
|
+
// Registering a provider to a domain
|
|
221
|
+
OpenFeature.setProvider("my-domain", new InMemoryProvider(someOtherFlags));
|
|
221
222
|
|
|
222
|
-
// A Client
|
|
223
|
+
// A Client bound to the default provider
|
|
223
224
|
const clientWithDefault = OpenFeature.getClient();
|
|
224
|
-
// A Client
|
|
225
|
-
const
|
|
225
|
+
// A Client bound to the InMemoryProvider provider
|
|
226
|
+
const domainScopedClient = OpenFeature.getClient("my-domain");
|
|
226
227
|
```
|
|
227
228
|
|
|
228
|
-
|
|
229
|
+
Domains can be defined on a provider during registration.
|
|
229
230
|
For more details, please refer to the [providers](#providers) section.
|
|
230
231
|
|
|
231
232
|
### Eventing
|
|
@@ -251,6 +252,32 @@ client.addHandler(ProviderEvents.Error, (eventDetails) => {
|
|
|
251
252
|
});
|
|
252
253
|
```
|
|
253
254
|
|
|
255
|
+
### Transaction Context Propagation
|
|
256
|
+
|
|
257
|
+
Transaction context is a container for transaction-specific evaluation context (e.g. user id, user agent, IP).
|
|
258
|
+
Transaction context can be set where specific data is available (e.g. an auth service or request handler) and by using the transaction context propagator it will automatically be applied to all flag evaluations within a transaction (e.g. a request or thread).
|
|
259
|
+
|
|
260
|
+
The following example shows an Express middleware using transaction context propagation to propagate the request ip and user id into request scoped transaction context.
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
import express, { Request, Response, NextFunction } from "express";
|
|
264
|
+
import { OpenFeature, AsyncLocalStorageTransactionContextPropagator } from '@openfeature/server-sdk';
|
|
265
|
+
|
|
266
|
+
OpenFeature.setTransactionContextPropagator(new AsyncLocalStorageTransactionContextPropagator())
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* This example is based on an express middleware.
|
|
270
|
+
*/
|
|
271
|
+
const app = express();
|
|
272
|
+
app.use((req: Request, res: Response, next: NextFunction) => {
|
|
273
|
+
const ip = res.headers.get("X-Forwarded-For")
|
|
274
|
+
OpenFeature.setTransactionContext({ targetingKey: req.user.id, ipAddress: ip }, () => {
|
|
275
|
+
// The transaction context is used in any flag evaluation throughout the whole call chain of next
|
|
276
|
+
next();
|
|
277
|
+
});
|
|
278
|
+
})
|
|
279
|
+
```
|
|
280
|
+
|
|
254
281
|
### Shutdown
|
|
255
282
|
|
|
256
283
|
The OpenFeature API provides a close function to perform a cleanup of all registered providers.
|
|
@@ -279,7 +306,6 @@ import {
|
|
|
279
306
|
Logger,
|
|
280
307
|
Provider,
|
|
281
308
|
ProviderEventEmitter,
|
|
282
|
-
ProviderStatus,
|
|
283
309
|
ResolutionDetails
|
|
284
310
|
} from '@openfeature/server-sdk';
|
|
285
311
|
|
|
@@ -305,10 +331,8 @@ class MyProvider implements Provider {
|
|
|
305
331
|
// code to evaluate an object
|
|
306
332
|
}
|
|
307
333
|
|
|
308
|
-
status?: ProviderStatus | undefined;
|
|
309
|
-
|
|
310
334
|
// implement with "new OpenFeatureEventEmitter()", and use "emit()" to emit events
|
|
311
|
-
events?: ProviderEventEmitter<AnyProviderEvent> | undefined;
|
|
335
|
+
events?: ProviderEventEmitter<AnyProviderEvent> | undefined;
|
|
312
336
|
|
|
313
337
|
initialize?(context?: EvaluationContext | undefined): Promise<void> {
|
|
314
338
|
// code to initialize your provider
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
6
|
var __export = (target, all) => {
|
|
9
7
|
for (var name in all)
|
|
@@ -18,19 +16,12 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
16
|
return to;
|
|
19
17
|
};
|
|
20
18
|
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
21
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
-
mod
|
|
28
|
-
));
|
|
29
19
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
20
|
|
|
31
21
|
// src/index.ts
|
|
32
22
|
var src_exports = {};
|
|
33
23
|
__export(src_exports, {
|
|
24
|
+
AsyncLocalStorageTransactionContextPropagator: () => AsyncLocalStorageTransactionContextPropagator,
|
|
34
25
|
InMemoryProvider: () => InMemoryProvider,
|
|
35
26
|
NOOP_PROVIDER: () => NOOP_PROVIDER,
|
|
36
27
|
NOOP_TRANSACTION_CONTEXT_PROPAGATOR: () => NOOP_TRANSACTION_CONTEXT_PROPAGATOR,
|
|
@@ -38,23 +29,42 @@ __export(src_exports, {
|
|
|
38
29
|
OpenFeatureAPI: () => OpenFeatureAPI,
|
|
39
30
|
OpenFeatureClient: () => OpenFeatureClient,
|
|
40
31
|
OpenFeatureEventEmitter: () => OpenFeatureEventEmitter,
|
|
41
|
-
ProviderEvents: () =>
|
|
32
|
+
ProviderEvents: () => import_core2.ServerProviderEvents,
|
|
33
|
+
ProviderStatus: () => import_core3.ServerProviderStatus
|
|
42
34
|
});
|
|
43
35
|
module.exports = __toCommonJS(src_exports);
|
|
44
36
|
|
|
45
37
|
// src/client/open-feature-client.ts
|
|
46
38
|
var import_core7 = require("@openfeature/core");
|
|
47
39
|
|
|
48
|
-
// src/
|
|
40
|
+
// src/open-feature.ts
|
|
41
|
+
var import_core6 = require("@openfeature/core");
|
|
42
|
+
|
|
43
|
+
// src/events/open-feature-event-emitter.ts
|
|
49
44
|
var import_core = require("@openfeature/core");
|
|
45
|
+
var import_node_events = require("events");
|
|
46
|
+
var OpenFeatureEventEmitter = class extends import_core.GenericEventEmitter {
|
|
47
|
+
eventEmitter = new import_node_events.EventEmitter({ captureRejections: true });
|
|
48
|
+
constructor() {
|
|
49
|
+
super();
|
|
50
|
+
this.eventEmitter.on("error", (err) => {
|
|
51
|
+
this._logger?.error("Error running event handler:", err);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// src/events/events.ts
|
|
57
|
+
var import_core2 = require("@openfeature/core");
|
|
58
|
+
|
|
59
|
+
// src/provider/provider.ts
|
|
60
|
+
var import_core3 = require("@openfeature/core");
|
|
61
|
+
|
|
62
|
+
// src/provider/no-op-provider.ts
|
|
50
63
|
var REASON_NO_OP = "No-op";
|
|
51
64
|
var NoopFeatureProvider = class {
|
|
52
65
|
metadata = {
|
|
53
66
|
name: "No-op Provider"
|
|
54
67
|
};
|
|
55
|
-
get status() {
|
|
56
|
-
return import_core.ProviderStatus.NOT_READY;
|
|
57
|
-
}
|
|
58
68
|
resolveBooleanEvaluation(_, defaultValue) {
|
|
59
69
|
return this.noOp(defaultValue);
|
|
60
70
|
}
|
|
@@ -77,17 +87,17 @@ var NoopFeatureProvider = class {
|
|
|
77
87
|
var NOOP_PROVIDER = new NoopFeatureProvider();
|
|
78
88
|
|
|
79
89
|
// src/provider/in-memory-provider/in-memory-provider.ts
|
|
80
|
-
var
|
|
90
|
+
var import_core5 = require("@openfeature/core");
|
|
81
91
|
|
|
82
92
|
// src/provider/in-memory-provider/variant-not-found-error.ts
|
|
83
|
-
var
|
|
84
|
-
var VariantFoundError = class extends
|
|
93
|
+
var import_core4 = require("@openfeature/core");
|
|
94
|
+
var VariantFoundError = class _VariantFoundError extends import_core4.OpenFeatureError {
|
|
85
95
|
code;
|
|
86
96
|
constructor(message) {
|
|
87
97
|
super(message);
|
|
88
|
-
Object.setPrototypeOf(this,
|
|
98
|
+
Object.setPrototypeOf(this, _VariantFoundError.prototype);
|
|
89
99
|
this.name = "VariantFoundError";
|
|
90
|
-
this.code =
|
|
100
|
+
this.code = import_core4.ErrorCode.GENERAL;
|
|
91
101
|
}
|
|
92
102
|
};
|
|
93
103
|
|
|
@@ -109,7 +119,7 @@ var InMemoryProvider = class {
|
|
|
109
119
|
putConfiguration(flagConfiguration) {
|
|
110
120
|
const flagsChanged = Object.entries(flagConfiguration).filter(([key, value]) => this._flagConfiguration[key] !== value).map(([key]) => key);
|
|
111
121
|
this._flagConfiguration = { ...flagConfiguration };
|
|
112
|
-
this.events.emit(
|
|
122
|
+
this.events.emit(import_core2.ServerProviderEvents.ConfigurationChanged, { flagsChanged });
|
|
113
123
|
}
|
|
114
124
|
resolveBooleanEvaluation(flagKey, defaultValue, context, logger) {
|
|
115
125
|
return this.resolveFlagWithReason(flagKey, defaultValue, context, logger);
|
|
@@ -127,12 +137,12 @@ var InMemoryProvider = class {
|
|
|
127
137
|
try {
|
|
128
138
|
const resolutionResult = this.lookupFlagValue(flagKey, defaultValue, ctx, logger);
|
|
129
139
|
if (typeof resolutionResult?.value != typeof defaultValue) {
|
|
130
|
-
throw new
|
|
140
|
+
throw new import_core5.TypeMismatchError();
|
|
131
141
|
}
|
|
132
142
|
return resolutionResult;
|
|
133
143
|
} catch (error) {
|
|
134
|
-
if (!(error instanceof
|
|
135
|
-
throw new
|
|
144
|
+
if (!(error instanceof import_core5.OpenFeatureError)) {
|
|
145
|
+
throw new import_core5.GeneralError(error?.message || "unknown error");
|
|
136
146
|
}
|
|
137
147
|
throw error;
|
|
138
148
|
}
|
|
@@ -141,11 +151,11 @@ var InMemoryProvider = class {
|
|
|
141
151
|
if (!(flagKey in this._flagConfiguration)) {
|
|
142
152
|
const message = `no flag found with key ${flagKey}`;
|
|
143
153
|
logger?.debug(message);
|
|
144
|
-
throw new
|
|
154
|
+
throw new import_core5.FlagNotFoundError(message);
|
|
145
155
|
}
|
|
146
156
|
const flagSpec = this._flagConfiguration[flagKey];
|
|
147
157
|
if (flagSpec.disabled) {
|
|
148
|
-
return { value: defaultValue, reason:
|
|
158
|
+
return { value: defaultValue, reason: import_core5.StandardResolutionReasons.DISABLED };
|
|
149
159
|
}
|
|
150
160
|
const isContextEval = ctx && flagSpec?.contextEvaluator;
|
|
151
161
|
const variant = isContextEval ? flagSpec.contextEvaluator?.(ctx) : flagSpec.defaultVariant;
|
|
@@ -158,47 +168,42 @@ var InMemoryProvider = class {
|
|
|
158
168
|
return {
|
|
159
169
|
value,
|
|
160
170
|
...variant && { variant },
|
|
161
|
-
reason: isContextEval ?
|
|
171
|
+
reason: isContextEval ? import_core5.StandardResolutionReasons.TARGETING_MATCH : import_core5.StandardResolutionReasons.STATIC
|
|
162
172
|
};
|
|
163
173
|
}
|
|
164
174
|
};
|
|
165
175
|
|
|
166
|
-
// src/open-feature.ts
|
|
167
|
-
var import_core6 = require("@openfeature/core");
|
|
168
|
-
|
|
169
176
|
// src/transaction-context/no-op-transaction-context-propagator.ts
|
|
170
177
|
var NoopTransactionContextPropagator = class {
|
|
171
178
|
getTransactionContext() {
|
|
172
179
|
return {};
|
|
173
180
|
}
|
|
174
|
-
setTransactionContext(_, callback) {
|
|
175
|
-
callback();
|
|
181
|
+
setTransactionContext(_, callback, ...args) {
|
|
182
|
+
callback(...args);
|
|
176
183
|
}
|
|
177
184
|
};
|
|
178
185
|
var NOOP_TRANSACTION_CONTEXT_PROPAGATOR = new NoopTransactionContextPropagator();
|
|
179
186
|
|
|
180
|
-
// src/
|
|
181
|
-
var
|
|
182
|
-
var
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
});
|
|
187
|
+
// src/transaction-context/async-local-storage-transaction-context-propagator.ts
|
|
188
|
+
var import_async_hooks = require("async_hooks");
|
|
189
|
+
var AsyncLocalStorageTransactionContextPropagator = class {
|
|
190
|
+
asyncLocalStorage = new import_async_hooks.AsyncLocalStorage();
|
|
191
|
+
getTransactionContext() {
|
|
192
|
+
return this.asyncLocalStorage.getStore() ?? {};
|
|
193
|
+
}
|
|
194
|
+
setTransactionContext(transactionContext, callback, ...args) {
|
|
195
|
+
this.asyncLocalStorage.run(transactionContext, callback, ...args);
|
|
190
196
|
}
|
|
191
197
|
};
|
|
192
198
|
|
|
193
|
-
// src/events/events.ts
|
|
194
|
-
var import_core5 = require("@openfeature/core");
|
|
195
|
-
|
|
196
199
|
// src/open-feature.ts
|
|
197
200
|
var GLOBAL_OPENFEATURE_API_KEY = Symbol.for("@openfeature/js-sdk/api");
|
|
198
201
|
var _globalThis = globalThis;
|
|
199
|
-
var OpenFeatureAPI = class extends import_core6.OpenFeatureCommonAPI {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
+
var OpenFeatureAPI = class _OpenFeatureAPI extends import_core6.OpenFeatureCommonAPI {
|
|
203
|
+
_statusEnumType = import_core3.ServerProviderStatus;
|
|
204
|
+
_apiEmitter = new OpenFeatureEventEmitter();
|
|
205
|
+
_defaultProvider = new import_core6.ProviderWrapper(NOOP_PROVIDER, import_core3.ServerProviderStatus.NOT_READY, this._statusEnumType);
|
|
206
|
+
_domainScopedProviders = /* @__PURE__ */ new Map();
|
|
202
207
|
_createEventEmitter = () => new OpenFeatureEventEmitter();
|
|
203
208
|
_transactionContextPropagator = NOOP_TRANSACTION_CONTEXT_PROPAGATOR;
|
|
204
209
|
constructor() {
|
|
@@ -214,10 +219,16 @@ var OpenFeatureAPI = class extends import_core6.OpenFeatureCommonAPI {
|
|
|
214
219
|
if (globalApi) {
|
|
215
220
|
return globalApi;
|
|
216
221
|
}
|
|
217
|
-
const instance = new
|
|
222
|
+
const instance = new _OpenFeatureAPI();
|
|
218
223
|
_globalThis[GLOBAL_OPENFEATURE_API_KEY] = instance;
|
|
219
224
|
return instance;
|
|
220
225
|
}
|
|
226
|
+
getProviderStatus(domain) {
|
|
227
|
+
if (!domain) {
|
|
228
|
+
return this._defaultProvider.status;
|
|
229
|
+
}
|
|
230
|
+
return this._domainScopedProviders.get(domain)?.status ?? this._defaultProvider.status;
|
|
231
|
+
}
|
|
221
232
|
setContext(context) {
|
|
222
233
|
this._context = context;
|
|
223
234
|
return this;
|
|
@@ -225,15 +236,16 @@ var OpenFeatureAPI = class extends import_core6.OpenFeatureCommonAPI {
|
|
|
225
236
|
getContext() {
|
|
226
237
|
return this._context;
|
|
227
238
|
}
|
|
228
|
-
getClient(
|
|
229
|
-
const
|
|
239
|
+
getClient(domainOrContext, versionOrContext, contextOrUndefined) {
|
|
240
|
+
const domain = (0, import_core6.stringOrUndefined)(domainOrContext);
|
|
230
241
|
const version = (0, import_core6.stringOrUndefined)(versionOrContext);
|
|
231
|
-
const context = (0, import_core6.objectOrUndefined)(
|
|
242
|
+
const context = (0, import_core6.objectOrUndefined)(domainOrContext) ?? (0, import_core6.objectOrUndefined)(versionOrContext) ?? (0, import_core6.objectOrUndefined)(contextOrUndefined);
|
|
232
243
|
return new OpenFeatureClient(
|
|
233
|
-
() => this.getProviderForClient(
|
|
234
|
-
() => this.
|
|
244
|
+
() => this.getProviderForClient(domain),
|
|
245
|
+
() => this.getProviderStatus(domain),
|
|
246
|
+
() => this.buildAndCacheEventEmitterForClient(domain),
|
|
235
247
|
() => this._logger,
|
|
236
|
-
{
|
|
248
|
+
{ domain, version },
|
|
237
249
|
context
|
|
238
250
|
);
|
|
239
251
|
}
|
|
@@ -273,8 +285,9 @@ var OpenFeature = OpenFeatureAPI.getInstance();
|
|
|
273
285
|
|
|
274
286
|
// src/client/open-feature-client.ts
|
|
275
287
|
var OpenFeatureClient = class {
|
|
276
|
-
constructor(providerAccessor, emitterAccessor, globalLogger, options, context = {}) {
|
|
288
|
+
constructor(providerAccessor, providerStatusAccessor, emitterAccessor, globalLogger, options, context = {}) {
|
|
277
289
|
this.providerAccessor = providerAccessor;
|
|
290
|
+
this.providerStatusAccessor = providerStatusAccessor;
|
|
278
291
|
this.emitterAccessor = emitterAccessor;
|
|
279
292
|
this.globalLogger = globalLogger;
|
|
280
293
|
this.options = options;
|
|
@@ -285,17 +298,26 @@ var OpenFeatureClient = class {
|
|
|
285
298
|
_clientLogger;
|
|
286
299
|
get metadata() {
|
|
287
300
|
return {
|
|
288
|
-
name
|
|
301
|
+
// Use domain if name is not provided
|
|
302
|
+
name: this.options.domain ?? this.options.name,
|
|
303
|
+
domain: this.options.domain ?? this.options.name,
|
|
289
304
|
version: this.options.version,
|
|
290
305
|
providerMetadata: this.providerAccessor().metadata
|
|
291
306
|
};
|
|
292
307
|
}
|
|
308
|
+
get providerStatus() {
|
|
309
|
+
return this.providerStatusAccessor();
|
|
310
|
+
}
|
|
293
311
|
addHandler(eventType, handler) {
|
|
294
312
|
this.emitterAccessor().addHandler(eventType, handler);
|
|
295
|
-
const shouldRunNow = (0, import_core7.statusMatchesEvent)(eventType, this.
|
|
313
|
+
const shouldRunNow = (0, import_core7.statusMatchesEvent)(eventType, this._providerStatus);
|
|
296
314
|
if (shouldRunNow) {
|
|
297
315
|
try {
|
|
298
|
-
handler({
|
|
316
|
+
handler({
|
|
317
|
+
clientName: this.metadata.name,
|
|
318
|
+
domain: this.metadata.domain,
|
|
319
|
+
providerName: this._provider.metadata.name
|
|
320
|
+
});
|
|
299
321
|
} catch (err) {
|
|
300
322
|
this._logger?.error("Error running event handler:", err);
|
|
301
323
|
}
|
|
@@ -401,6 +423,11 @@ var OpenFeatureClient = class {
|
|
|
401
423
|
};
|
|
402
424
|
try {
|
|
403
425
|
const frozenContext = await this.beforeHooks(allHooks, hookContext, options);
|
|
426
|
+
if (this.providerStatus === import_core3.ServerProviderStatus.NOT_READY) {
|
|
427
|
+
throw new import_core7.ProviderNotReadyError("provider has not yet initialized");
|
|
428
|
+
} else if (this.providerStatus === import_core3.ServerProviderStatus.FATAL) {
|
|
429
|
+
throw new import_core7.ProviderFatalError("provider is in an irrecoverable error state");
|
|
430
|
+
}
|
|
404
431
|
const resolution = await resolver.call(this._provider, flagKey, defaultValue, frozenContext, this._logger);
|
|
405
432
|
const evaluationDetails = {
|
|
406
433
|
...resolution,
|
|
@@ -469,6 +496,9 @@ var OpenFeatureClient = class {
|
|
|
469
496
|
get _provider() {
|
|
470
497
|
return this.providerAccessor();
|
|
471
498
|
}
|
|
499
|
+
get _providerStatus() {
|
|
500
|
+
return this.providerStatusAccessor();
|
|
501
|
+
}
|
|
472
502
|
get _logger() {
|
|
473
503
|
return this._clientLogger || this.globalLogger();
|
|
474
504
|
}
|
|
@@ -478,6 +508,7 @@ var OpenFeatureClient = class {
|
|
|
478
508
|
__reExport(src_exports, require("@openfeature/core"), module.exports);
|
|
479
509
|
// Annotate the CommonJS export names for ESM import in node:
|
|
480
510
|
0 && (module.exports = {
|
|
511
|
+
AsyncLocalStorageTransactionContextPropagator,
|
|
481
512
|
InMemoryProvider,
|
|
482
513
|
NOOP_PROVIDER,
|
|
483
514
|
NOOP_TRANSACTION_CONTEXT_PROPAGATOR,
|
|
@@ -485,6 +516,8 @@ __reExport(src_exports, require("@openfeature/core"), module.exports);
|
|
|
485
516
|
OpenFeatureAPI,
|
|
486
517
|
OpenFeatureClient,
|
|
487
518
|
OpenFeatureEventEmitter,
|
|
488
|
-
ProviderEvents
|
|
519
|
+
ProviderEvents,
|
|
520
|
+
ProviderStatus,
|
|
521
|
+
...require("@openfeature/core")
|
|
489
522
|
});
|
|
490
523
|
//# sourceMappingURL=index.js.map
|