@openfeature/web-sdk 0.3.0-experimental โ†’ 0.3.2-experimental

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,18 +1,166 @@
1
- # @openfeature/web-sdk
1
+ <!-- markdownlint-disable MD033 -->
2
+ <p align="center">
3
+ <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">
7
+ </picture>
8
+ </p>
2
9
 
3
- Experimental web implementation of OpenFeature intended for use in web-browsers.
4
-
5
- ## Installation
10
+ <h2 align="center">OpenFeature Web SDK</h2>
6
11
 
7
- ```shell
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.5.2&color=yellow)](https://github.com/open-feature/spec/tree/v0.5.2)
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?
24
+
25
+ [OpenFeature][openfeature-website] is an open standard that provides a vendor-agnostic, community-driven API for feature flagging that works with your favorite feature flag management tool.
26
+
27
+ ### Why standardize feature flags?
28
+
29
+ Standardizing feature flags unifies tools and vendors behind a common interface which avoids vendor lock-in at the code level. Additionally, it offers a framework for building extensions and integrations and allows providers to focus on their unique value proposition.
30
+
31
+ ## ๐Ÿ” Requirements:
32
+
33
+ - ES2015-compatible web browser (Chrome, Edge, Firefox, etc)
34
+
35
+ ## ๐Ÿ“ฆ Installation:
36
+
37
+ ### npm
38
+
39
+ ```sh
8
40
  npm install @openfeature/web-sdk
9
41
  ```
10
42
 
11
- or
43
+ ### yarn
12
44
 
13
- ```shell
45
+ ```sh
14
46
  yarn add @openfeature/web-sdk
15
47
  ```
16
48
 
17
- ## Usage
18
- Coming soon!
49
+ ## ๐ŸŒŸ Features:
50
+
51
+ - support for various [providers](https://openfeature.dev/docs/reference/concepts/provider)
52
+ - easy integration and extension via [hooks](https://openfeature.dev/docs/reference/concepts/hooks)
53
+ - handle flags of any type: bool, string, numeric and object
54
+ - [context-aware](https://openfeature.dev/docs/reference/concepts/evaluation-context) evaluation
55
+
56
+ ## ๐Ÿš€ Usage:
57
+
58
+ ### Basics:
59
+
60
+ ```typescript
61
+ import { OpenFeature } from '@openfeature/web-sdk';
62
+
63
+ // configure a provider
64
+ await OpenFeature.setProvider(new YourProviderOfChoice());
65
+
66
+ // create a client
67
+ const client = OpenFeature.getClient('my-app');
68
+
69
+ // get a bool flag value
70
+ const boolValue = client.getBooleanValue('boolFlag', false);
71
+ ```
72
+
73
+ ### Context-aware evaluation:
74
+
75
+ Sometimes the value of a flag must take into account some dynamic criteria about the application or user, such as the user location, IP, email address, or the location of the server.
76
+ In OpenFeature, we refer to this as [`targeting`](https://openfeature.dev/specification/glossary#targeting).
77
+ If the flag system you're using supports targeting, you can provide the input data using the `EvaluationContext`.
78
+
79
+ ```typescript
80
+ // global context for static data
81
+ await OpenFeature.setContext({ origin: document.location.host })
82
+
83
+ // use contextual data to determine a flag value
84
+ const boolValue = client.getBooleanValue('some-flag', false);
85
+ ```
86
+
87
+ ### Providers:
88
+
89
+ To develop a provider, you need to create a new project and include the OpenFeature SDK as a dependency. This can be a new repository or included in an existing contrib repository available under the OpenFeature organization. Finally, youโ€™ll then need to write the provider itself. In most languages, this can be accomplished by implementing the provider interface exported by the OpenFeature SDK.
90
+
91
+ ```typescript
92
+ import { JsonValue, Provider, ResolutionDetails } from '@openfeature/web-sdk';
93
+
94
+ // implement the provider interface
95
+ class MyProvider implements Provider {
96
+ readonly metadata = {
97
+ name: 'My Provider',
98
+ } as const;
99
+
100
+ resolveBooleanEvaluation(flagKey: string, defaultValue: boolean): ResolutionDetails<boolean> {
101
+ // resolve a boolean flag value
102
+ }
103
+
104
+ resolveStringEvaluation(flagKey: string, defaultValue: string): ResolutionDetails<string> {
105
+ // resolve a string flag value
106
+ }
107
+
108
+ resolveNumberEvaluation(flagKey: string, defaultValue: number): ResolutionDetails<number> {
109
+ // resolve a numeric flag value
110
+ }
111
+
112
+ resolveObjectEvaluation<T extends JsonValue>(flagKey: string, defaultValue: T): ResolutionDetails<T> {
113
+ // resolve an object flag value
114
+ }
115
+ ```
116
+
117
+ See [here](https://openfeature.dev/docs/reference/technologies/server/javascript) for a catalog of available providers.
118
+
119
+ ### Hooks:
120
+
121
+ Hooks are a mechanism that allow for the addition of arbitrary behavior at well-defined points of the flag evaluation life-cycle. Use cases include validation of the resolved flag value, modifying or adding data to the evaluation context, logging, telemetry, and tracking.
122
+
123
+ ```typescript
124
+ import { OpenFeature, Hook, HookContext } from '@openfeature/web-sdk';
125
+
126
+ // Example hook that logs if an error occurs during flag evaluation
127
+ export class GlobalDebugHook implements Hook {
128
+ after(hookContext: HookContext, err: Error) {
129
+ console.log('hook context', hookContext);
130
+ console.error(err);
131
+ }
132
+ }
133
+ ```
134
+
135
+ See [here](https://openfeature.dev/docs/reference/technologies/server/javascript) for a catalog of available hooks.
136
+
137
+ ### Logging:
138
+
139
+ You can implement the `Logger` interface (compatible with the `console` object, and implementations from common logging libraries such as [winston](https://www.npmjs.com/package/winston)) and set it on the global API object.
140
+
141
+ ```typescript
142
+ // implement logger
143
+ class MyLogger implements Logger {
144
+ error(...args: unknown[]): void {
145
+ // implement me
146
+ }
147
+ warn(...args: unknown[]): void {
148
+ // implement me
149
+ }
150
+ info(...args: unknown[]): void {
151
+ // implement me
152
+ }
153
+ debug(...args: unknown[]): void {
154
+ // implement me
155
+ }
156
+ }
157
+
158
+ // set the logger
159
+ OpenFeature.setLogger(new MyLogger());
160
+ ```
161
+
162
+ ### Complete API documentation:
163
+
164
+ See [here](https://open-feature.github.io/js-sdk/modules/OpenFeature_Web_SDK.html) for the complete API documentation.
165
+
166
+ [openfeature-website]: https://openfeature.dev
package/dist/cjs/index.js CHANGED
@@ -94,22 +94,22 @@ var require_events = __commonJS({
94
94
  var NumberIsNaN = Number.isNaN || function NumberIsNaN2(value) {
95
95
  return value !== value;
96
96
  };
97
- function EventEmitter2() {
98
- EventEmitter2.init.call(this);
97
+ function EventEmitter() {
98
+ EventEmitter.init.call(this);
99
99
  }
100
- module2.exports = EventEmitter2;
100
+ module2.exports = EventEmitter;
101
101
  module2.exports.once = once;
102
- EventEmitter2.EventEmitter = EventEmitter2;
103
- EventEmitter2.prototype._events = void 0;
104
- EventEmitter2.prototype._eventsCount = 0;
105
- EventEmitter2.prototype._maxListeners = void 0;
102
+ EventEmitter.EventEmitter = EventEmitter;
103
+ EventEmitter.prototype._events = void 0;
104
+ EventEmitter.prototype._eventsCount = 0;
105
+ EventEmitter.prototype._maxListeners = void 0;
106
106
  var defaultMaxListeners = 10;
107
107
  function checkListener(listener) {
108
108
  if (typeof listener !== "function") {
109
109
  throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
110
110
  }
111
111
  }
112
- Object.defineProperty(EventEmitter2, "defaultMaxListeners", {
112
+ Object.defineProperty(EventEmitter, "defaultMaxListeners", {
113
113
  enumerable: true,
114
114
  get: function() {
115
115
  return defaultMaxListeners;
@@ -121,14 +121,14 @@ var require_events = __commonJS({
121
121
  defaultMaxListeners = arg;
122
122
  }
123
123
  });
124
- EventEmitter2.init = function() {
124
+ EventEmitter.init = function() {
125
125
  if (this._events === void 0 || this._events === Object.getPrototypeOf(this)._events) {
126
126
  this._events = /* @__PURE__ */ Object.create(null);
127
127
  this._eventsCount = 0;
128
128
  }
129
129
  this._maxListeners = this._maxListeners || void 0;
130
130
  };
131
- EventEmitter2.prototype.setMaxListeners = function setMaxListeners(n) {
131
+ EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
132
132
  if (typeof n !== "number" || n < 0 || NumberIsNaN(n)) {
133
133
  throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + ".");
134
134
  }
@@ -137,13 +137,13 @@ var require_events = __commonJS({
137
137
  };
138
138
  function _getMaxListeners(that) {
139
139
  if (that._maxListeners === void 0)
140
- return EventEmitter2.defaultMaxListeners;
140
+ return EventEmitter.defaultMaxListeners;
141
141
  return that._maxListeners;
142
142
  }
143
- EventEmitter2.prototype.getMaxListeners = function getMaxListeners() {
143
+ EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
144
144
  return _getMaxListeners(this);
145
145
  };
146
- EventEmitter2.prototype.emit = function emit(type) {
146
+ EventEmitter.prototype.emit = function emit(type) {
147
147
  var args = [];
148
148
  for (var i = 1; i < arguments.length; i++)
149
149
  args.push(arguments[i]);
@@ -221,11 +221,11 @@ var require_events = __commonJS({
221
221
  }
222
222
  return target;
223
223
  }
224
- EventEmitter2.prototype.addListener = function addListener(type, listener) {
224
+ EventEmitter.prototype.addListener = function addListener(type, listener) {
225
225
  return _addListener(this, type, listener, false);
226
226
  };
227
- EventEmitter2.prototype.on = EventEmitter2.prototype.addListener;
228
- EventEmitter2.prototype.prependListener = function prependListener(type, listener) {
227
+ EventEmitter.prototype.on = EventEmitter.prototype.addListener;
228
+ EventEmitter.prototype.prependListener = function prependListener(type, listener) {
229
229
  return _addListener(this, type, listener, true);
230
230
  };
231
231
  function onceWrapper() {
@@ -244,17 +244,17 @@ var require_events = __commonJS({
244
244
  state.wrapFn = wrapped;
245
245
  return wrapped;
246
246
  }
247
- EventEmitter2.prototype.once = function once2(type, listener) {
247
+ EventEmitter.prototype.once = function once2(type, listener) {
248
248
  checkListener(listener);
249
249
  this.on(type, _onceWrap(this, type, listener));
250
250
  return this;
251
251
  };
252
- EventEmitter2.prototype.prependOnceListener = function prependOnceListener(type, listener) {
252
+ EventEmitter.prototype.prependOnceListener = function prependOnceListener(type, listener) {
253
253
  checkListener(listener);
254
254
  this.prependListener(type, _onceWrap(this, type, listener));
255
255
  return this;
256
256
  };
257
- EventEmitter2.prototype.removeListener = function removeListener(type, listener) {
257
+ EventEmitter.prototype.removeListener = function removeListener(type, listener) {
258
258
  var list, events, position, i, originalListener;
259
259
  checkListener(listener);
260
260
  events = this._events;
@@ -294,8 +294,8 @@ var require_events = __commonJS({
294
294
  }
295
295
  return this;
296
296
  };
297
- EventEmitter2.prototype.off = EventEmitter2.prototype.removeListener;
298
- EventEmitter2.prototype.removeAllListeners = function removeAllListeners(type) {
297
+ EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
298
+ EventEmitter.prototype.removeAllListeners = function removeAllListeners(type) {
299
299
  var listeners, events, i;
300
300
  events = this._events;
301
301
  if (events === void 0)
@@ -347,20 +347,20 @@ var require_events = __commonJS({
347
347
  return unwrap ? [evlistener.listener || evlistener] : [evlistener];
348
348
  return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
349
349
  }
350
- EventEmitter2.prototype.listeners = function listeners(type) {
350
+ EventEmitter.prototype.listeners = function listeners(type) {
351
351
  return _listeners(this, type, true);
352
352
  };
353
- EventEmitter2.prototype.rawListeners = function rawListeners(type) {
353
+ EventEmitter.prototype.rawListeners = function rawListeners(type) {
354
354
  return _listeners(this, type, false);
355
355
  };
356
- EventEmitter2.listenerCount = function(emitter, type) {
356
+ EventEmitter.listenerCount = function(emitter, type) {
357
357
  if (typeof emitter.listenerCount === "function") {
358
358
  return emitter.listenerCount(type);
359
359
  } else {
360
360
  return listenerCount.call(emitter, type);
361
361
  }
362
362
  };
363
- EventEmitter2.prototype.listenerCount = listenerCount;
363
+ EventEmitter.prototype.listenerCount = listenerCount;
364
364
  function listenerCount(type) {
365
365
  var events = this._events;
366
366
  if (events !== void 0) {
@@ -373,7 +373,7 @@ var require_events = __commonJS({
373
373
  }
374
374
  return 0;
375
375
  }
376
- EventEmitter2.prototype.eventNames = function eventNames() {
376
+ EventEmitter.prototype.eventNames = function eventNames() {
377
377
  return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
378
378
  };
379
379
  function arrayClone(arr, n) {
@@ -464,20 +464,8 @@ __export(src_exports, {
464
464
  TypeMismatchError: () => TypeMismatchError
465
465
  });
466
466
  module.exports = __toCommonJS(src_exports);
467
- var import_events = __toESM(require_events());
468
467
 
469
468
  // ../shared/src/types.ts
470
- var ProviderEvents = /* @__PURE__ */ ((ProviderEvents2) => {
471
- ProviderEvents2["Ready"] = "PROVIDER_READY";
472
- ProviderEvents2["Error"] = "PROVIDER_ERROR";
473
- ProviderEvents2["ConfigurationChanged"] = "PROVIDER_CONFIGURATION_CHANGED";
474
- ProviderEvents2["Shutdown"] = "PROVIDER_SHUTDOWN";
475
- return ProviderEvents2;
476
- })(ProviderEvents || {});
477
- var ApiEvents = /* @__PURE__ */ ((ApiEvents2) => {
478
- ApiEvents2["ProviderChanged"] = "providerChanged";
479
- return ApiEvents2;
480
- })(ApiEvents || {});
481
469
  var StandardResolutionReasons = {
482
470
  /**
483
471
  * The resolved value was the result of a dynamic evaluation, such as a rule or specific user-targeting.
@@ -721,6 +709,20 @@ var NoopFeatureProvider = class {
721
709
  };
722
710
  var NOOP_PROVIDER = new NoopFeatureProvider();
723
711
 
712
+ // src/types.ts
713
+ var import_events = __toESM(require_events());
714
+ var ProviderEvents = /* @__PURE__ */ ((ProviderEvents2) => {
715
+ ProviderEvents2["Ready"] = "PROVIDER_READY";
716
+ ProviderEvents2["Error"] = "PROVIDER_ERROR";
717
+ ProviderEvents2["ConfigurationChanged"] = "PROVIDER_CONFIGURATION_CHANGED";
718
+ ProviderEvents2["Stale"] = "PROVIDER_STALE";
719
+ return ProviderEvents2;
720
+ })(ProviderEvents || {});
721
+ var ApiEvents = /* @__PURE__ */ ((ApiEvents2) => {
722
+ ApiEvents2["ProviderChanged"] = "providerChanged";
723
+ return ApiEvents2;
724
+ })(ApiEvents || {});
725
+
724
726
  // src/open-feature.ts
725
727
  var GLOBAL_OPENFEATURE_API_KEY = Symbol.for("@openfeature/js.api");
726
728
  var _globalThis = globalThis;
@@ -815,7 +817,7 @@ var OpenFeatureAPI = class extends OpenFeatureCommonAPI {
815
817
  getClient(name, version) {
816
818
  return new OpenFeatureClient(
817
819
  // functions are passed here to make sure that these values are always up to date,
818
- // and so we don't have to make these public properties on the API class.
820
+ // and so we don't have to make these public properties on the API class.
819
821
  () => this._provider,
820
822
  () => this._providerReady,
821
823
  () => this._apiEvents,