@openfeature/web-sdk 1.0.3 → 1.2.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 +33 -5
- package/dist/cjs/index.js +355 -259
- package/dist/cjs/index.js.map +4 -4
- package/dist/esm/index.js +358 -261
- package/dist/esm/index.js.map +4 -4
- package/dist/types.d.ts +85 -57
- package/package.json +5 -5
package/dist/esm/index.js
CHANGED
|
@@ -1,9 +1,27 @@
|
|
|
1
1
|
var __create = Object.create;
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defProps = Object.defineProperties;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
8
|
var __getProtoOf = Object.getPrototypeOf;
|
|
6
9
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
11
|
+
var __reflectGet = Reflect.get;
|
|
12
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
13
|
+
var __spreadValues = (a, b) => {
|
|
14
|
+
for (var prop in b || (b = {}))
|
|
15
|
+
if (__hasOwnProp.call(b, prop))
|
|
16
|
+
__defNormalProp(a, prop, b[prop]);
|
|
17
|
+
if (__getOwnPropSymbols)
|
|
18
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
19
|
+
if (__propIsEnum.call(b, prop))
|
|
20
|
+
__defNormalProp(a, prop, b[prop]);
|
|
21
|
+
}
|
|
22
|
+
return a;
|
|
23
|
+
};
|
|
24
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
7
25
|
var __commonJS = (cb, mod) => function __require() {
|
|
8
26
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
9
27
|
};
|
|
@@ -23,6 +41,27 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
41
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
24
42
|
mod
|
|
25
43
|
));
|
|
44
|
+
var __superGet = (cls, obj, key) => __reflectGet(__getProtoOf(cls), key, obj);
|
|
45
|
+
var __async = (__this, __arguments, generator) => {
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
var fulfilled = (value) => {
|
|
48
|
+
try {
|
|
49
|
+
step(generator.next(value));
|
|
50
|
+
} catch (e) {
|
|
51
|
+
reject(e);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
var rejected = (value) => {
|
|
55
|
+
try {
|
|
56
|
+
step(generator.throw(value));
|
|
57
|
+
} catch (e) {
|
|
58
|
+
reject(e);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
62
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
63
|
+
});
|
|
64
|
+
};
|
|
26
65
|
|
|
27
66
|
// ../../node_modules/eventemitter3/index.js
|
|
28
67
|
var require_eventemitter3 = __commonJS({
|
|
@@ -206,50 +245,17 @@ var require_eventemitter3 = __commonJS({
|
|
|
206
245
|
}
|
|
207
246
|
});
|
|
208
247
|
|
|
209
|
-
// src/client/open-feature-client.ts
|
|
210
|
-
import {
|
|
211
|
-
ErrorCode as ErrorCode2,
|
|
212
|
-
ProviderFatalError,
|
|
213
|
-
ProviderNotReadyError,
|
|
214
|
-
SafeLogger,
|
|
215
|
-
StandardResolutionReasons as StandardResolutionReasons2,
|
|
216
|
-
statusMatchesEvent
|
|
217
|
-
} from "@openfeature/core";
|
|
218
|
-
|
|
219
|
-
// src/open-feature.ts
|
|
220
|
-
import {
|
|
221
|
-
OpenFeatureCommonAPI,
|
|
222
|
-
ProviderWrapper,
|
|
223
|
-
objectOrUndefined,
|
|
224
|
-
stringOrUndefined
|
|
225
|
-
} from "@openfeature/core";
|
|
226
|
-
|
|
227
|
-
// src/events/open-feature-event-emitter.ts
|
|
228
|
-
import { GenericEventEmitter } from "@openfeature/core";
|
|
229
|
-
|
|
230
|
-
// ../../node_modules/eventemitter3/index.mjs
|
|
231
|
-
var import_index = __toESM(require_eventemitter3(), 1);
|
|
232
|
-
|
|
233
|
-
// src/events/open-feature-event-emitter.ts
|
|
234
|
-
var OpenFeatureEventEmitter = class extends GenericEventEmitter {
|
|
235
|
-
eventEmitter = new import_index.default();
|
|
236
|
-
constructor() {
|
|
237
|
-
super();
|
|
238
|
-
}
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
// src/events/events.ts
|
|
242
|
-
import { ClientProviderEvents } from "@openfeature/core";
|
|
243
|
-
|
|
244
248
|
// src/provider/provider.ts
|
|
245
249
|
import { ClientProviderStatus } from "@openfeature/core";
|
|
246
250
|
|
|
247
251
|
// src/provider/no-op-provider.ts
|
|
248
252
|
var REASON_NO_OP = "No-op";
|
|
249
253
|
var NoopFeatureProvider = class {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
254
|
+
constructor() {
|
|
255
|
+
this.metadata = {
|
|
256
|
+
name: "No-op Provider"
|
|
257
|
+
};
|
|
258
|
+
}
|
|
253
259
|
resolveBooleanEvaluation(_, defaultValue) {
|
|
254
260
|
return this.noOp(defaultValue);
|
|
255
261
|
}
|
|
@@ -280,10 +286,26 @@ import {
|
|
|
280
286
|
TypeMismatchError
|
|
281
287
|
} from "@openfeature/core";
|
|
282
288
|
|
|
289
|
+
// src/events/open-feature-event-emitter.ts
|
|
290
|
+
import { GenericEventEmitter } from "@openfeature/core";
|
|
291
|
+
|
|
292
|
+
// ../../node_modules/eventemitter3/index.mjs
|
|
293
|
+
var import_index = __toESM(require_eventemitter3(), 1);
|
|
294
|
+
|
|
295
|
+
// src/events/open-feature-event-emitter.ts
|
|
296
|
+
var OpenFeatureEventEmitter = class extends GenericEventEmitter {
|
|
297
|
+
constructor() {
|
|
298
|
+
super();
|
|
299
|
+
this.eventEmitter = new import_index.default();
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
// src/events/events.ts
|
|
304
|
+
import { ClientProviderEvents } from "@openfeature/core";
|
|
305
|
+
|
|
283
306
|
// src/provider/in-memory-provider/variant-not-found-error.ts
|
|
284
307
|
import { ErrorCode, OpenFeatureError } from "@openfeature/core";
|
|
285
308
|
var VariantNotFoundError = class _VariantNotFoundError extends OpenFeatureError {
|
|
286
|
-
code;
|
|
287
309
|
constructor(message) {
|
|
288
310
|
super(message);
|
|
289
311
|
Object.setPrototypeOf(this, _VariantNotFoundError.prototype);
|
|
@@ -294,40 +316,42 @@ var VariantNotFoundError = class _VariantNotFoundError extends OpenFeatureError
|
|
|
294
316
|
|
|
295
317
|
// src/provider/in-memory-provider/in-memory-provider.ts
|
|
296
318
|
var InMemoryProvider = class {
|
|
297
|
-
events = new OpenFeatureEventEmitter();
|
|
298
|
-
runsOn = "client";
|
|
299
|
-
metadata = {
|
|
300
|
-
name: "in-memory"
|
|
301
|
-
};
|
|
302
|
-
_flagConfiguration;
|
|
303
|
-
_context;
|
|
304
319
|
constructor(flagConfiguration = {}) {
|
|
305
|
-
this.
|
|
320
|
+
this.events = new OpenFeatureEventEmitter();
|
|
321
|
+
this.runsOn = "client";
|
|
322
|
+
this.metadata = {
|
|
323
|
+
name: "in-memory"
|
|
324
|
+
};
|
|
325
|
+
this._flagConfiguration = __spreadValues({}, flagConfiguration);
|
|
306
326
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
327
|
+
initialize(context) {
|
|
328
|
+
return __async(this, null, function* () {
|
|
329
|
+
try {
|
|
330
|
+
for (const key in this._flagConfiguration) {
|
|
331
|
+
this.resolveFlagWithReason(key, context);
|
|
332
|
+
}
|
|
333
|
+
this._context = context;
|
|
334
|
+
} catch (err) {
|
|
335
|
+
throw new GeneralError("initialization failure", { cause: err });
|
|
311
336
|
}
|
|
312
|
-
|
|
313
|
-
} catch (err) {
|
|
314
|
-
throw new Error("initialization failure", { cause: err });
|
|
315
|
-
}
|
|
337
|
+
});
|
|
316
338
|
}
|
|
317
339
|
/**
|
|
318
340
|
* Overwrites the configured flags.
|
|
319
341
|
* @param { FlagConfiguration } flagConfiguration new flag configuration
|
|
320
342
|
*/
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
343
|
+
putConfiguration(flagConfiguration) {
|
|
344
|
+
return __async(this, null, function* () {
|
|
345
|
+
const flagsChanged = Object.entries(flagConfiguration).filter(([key, value]) => this._flagConfiguration[key] !== value).map(([key]) => key);
|
|
346
|
+
this._flagConfiguration = __spreadValues({}, flagConfiguration);
|
|
347
|
+
try {
|
|
348
|
+
yield this.initialize(this._context);
|
|
349
|
+
this.events.emit(ClientProviderEvents.ConfigurationChanged, { flagsChanged });
|
|
350
|
+
} catch (err) {
|
|
351
|
+
this.events.emit(ClientProviderEvents.Error);
|
|
352
|
+
throw err;
|
|
353
|
+
}
|
|
354
|
+
});
|
|
331
355
|
}
|
|
332
356
|
resolveBooleanEvaluation(flagKey, defaultValue, context, logger) {
|
|
333
357
|
return this.resolveAndCheckFlag(flagKey, defaultValue, context || this._context, logger);
|
|
@@ -344,7 +368,7 @@ var InMemoryProvider = class {
|
|
|
344
368
|
resolveAndCheckFlag(flagKey, defaultValue, context, logger) {
|
|
345
369
|
if (!(flagKey in this._flagConfiguration)) {
|
|
346
370
|
const message = `no flag found with key ${flagKey}`;
|
|
347
|
-
logger
|
|
371
|
+
logger == null ? void 0 : logger.debug(message);
|
|
348
372
|
throw new FlagNotFoundError(message);
|
|
349
373
|
}
|
|
350
374
|
if (this._flagConfiguration[flagKey].disabled) {
|
|
@@ -353,7 +377,7 @@ var InMemoryProvider = class {
|
|
|
353
377
|
const resolvedFlag = this.resolveFlagWithReason(flagKey, context);
|
|
354
378
|
if (resolvedFlag.value === void 0) {
|
|
355
379
|
const message = `no value associated with variant provided for ${flagKey} found`;
|
|
356
|
-
logger
|
|
380
|
+
logger == null ? void 0 : logger.error(message);
|
|
357
381
|
throw new VariantNotFoundError(message);
|
|
358
382
|
}
|
|
359
383
|
if (typeof resolvedFlag.value != typeof defaultValue) {
|
|
@@ -367,192 +391,44 @@ var InMemoryProvider = class {
|
|
|
367
391
|
return resolutionResult;
|
|
368
392
|
} catch (error) {
|
|
369
393
|
if (!(error instanceof OpenFeatureError2)) {
|
|
370
|
-
throw new GeneralError(error
|
|
394
|
+
throw new GeneralError((error == null ? void 0 : error.message) || "unknown error");
|
|
371
395
|
}
|
|
372
396
|
throw error;
|
|
373
397
|
}
|
|
374
398
|
}
|
|
375
399
|
lookupFlagValue(flagKey, ctx) {
|
|
400
|
+
var _a;
|
|
376
401
|
const flagSpec = this._flagConfiguration[flagKey];
|
|
377
|
-
const isContextEval = ctx && flagSpec
|
|
378
|
-
const variant = isContextEval ? flagSpec.contextEvaluator
|
|
379
|
-
const value = variant && flagSpec
|
|
402
|
+
const isContextEval = ctx && (flagSpec == null ? void 0 : flagSpec.contextEvaluator);
|
|
403
|
+
const variant = isContextEval ? (_a = flagSpec.contextEvaluator) == null ? void 0 : _a.call(flagSpec, ctx) : flagSpec.defaultVariant;
|
|
404
|
+
const value = variant && (flagSpec == null ? void 0 : flagSpec.variants[variant]);
|
|
380
405
|
const reason = isContextEval ? StandardResolutionReasons.TARGETING_MATCH : StandardResolutionReasons.STATIC;
|
|
381
|
-
return {
|
|
382
|
-
value
|
|
383
|
-
|
|
406
|
+
return __spreadProps(__spreadValues({
|
|
407
|
+
value
|
|
408
|
+
}, variant && { variant }), {
|
|
384
409
|
reason
|
|
385
|
-
};
|
|
410
|
+
});
|
|
386
411
|
}
|
|
387
412
|
};
|
|
388
413
|
|
|
389
414
|
// src/open-feature.ts
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
_domainScopedProviders = /* @__PURE__ */ new Map();
|
|
397
|
-
_createEventEmitter = () => new OpenFeatureEventEmitter();
|
|
398
|
-
constructor() {
|
|
399
|
-
super("client");
|
|
400
|
-
}
|
|
401
|
-
/**
|
|
402
|
-
* Gets a singleton instance of the OpenFeature API.
|
|
403
|
-
* @ignore
|
|
404
|
-
* @returns {OpenFeatureAPI} OpenFeature API
|
|
405
|
-
*/
|
|
406
|
-
static getInstance() {
|
|
407
|
-
const globalApi = _globalThis[GLOBAL_OPENFEATURE_API_KEY];
|
|
408
|
-
if (globalApi) {
|
|
409
|
-
return globalApi;
|
|
410
|
-
}
|
|
411
|
-
const instance = new _OpenFeatureAPI();
|
|
412
|
-
_globalThis[GLOBAL_OPENFEATURE_API_KEY] = instance;
|
|
413
|
-
return instance;
|
|
414
|
-
}
|
|
415
|
-
getProviderStatus(domain) {
|
|
416
|
-
if (!domain) {
|
|
417
|
-
return this._defaultProvider.status;
|
|
418
|
-
}
|
|
419
|
-
return this._domainScopedProviders.get(domain)?.status ?? this._defaultProvider.status;
|
|
420
|
-
}
|
|
421
|
-
async setContext(domainOrContext, contextOrUndefined) {
|
|
422
|
-
const domain = stringOrUndefined(domainOrContext);
|
|
423
|
-
const context = objectOrUndefined(domainOrContext) ?? objectOrUndefined(contextOrUndefined) ?? {};
|
|
424
|
-
if (domain) {
|
|
425
|
-
const wrapper = this._domainScopedProviders.get(domain);
|
|
426
|
-
if (wrapper) {
|
|
427
|
-
const oldContext = this.getContext(domain);
|
|
428
|
-
this._domainScopedContext.set(domain, context);
|
|
429
|
-
await this.runProviderContextChangeHandler(domain, wrapper, oldContext, context);
|
|
430
|
-
} else {
|
|
431
|
-
this._domainScopedContext.set(domain, context);
|
|
432
|
-
}
|
|
433
|
-
} else {
|
|
434
|
-
const oldContext = this._context;
|
|
435
|
-
this._context = context;
|
|
436
|
-
const unboundProviders = Array.from(this._domainScopedProviders.entries()).filter(([domain2]) => !this._domainScopedContext.has(domain2)).reduce((acc, [domain2, wrapper]) => {
|
|
437
|
-
acc.push({ domain: domain2, wrapper });
|
|
438
|
-
return acc;
|
|
439
|
-
}, []);
|
|
440
|
-
const allDomainRecords = [
|
|
441
|
-
// add in the default (no domain)
|
|
442
|
-
{ domain: void 0, wrapper: this._defaultProvider },
|
|
443
|
-
...unboundProviders
|
|
444
|
-
];
|
|
445
|
-
await Promise.all(
|
|
446
|
-
allDomainRecords.map(
|
|
447
|
-
(dm) => this.runProviderContextChangeHandler(dm.domain, dm.wrapper, oldContext, context)
|
|
448
|
-
)
|
|
449
|
-
);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
getContext(domainOrUndefined) {
|
|
453
|
-
const domain = stringOrUndefined(domainOrUndefined);
|
|
454
|
-
if (domain) {
|
|
455
|
-
const context = this._domainScopedContext.get(domain);
|
|
456
|
-
if (context) {
|
|
457
|
-
return context;
|
|
458
|
-
} else {
|
|
459
|
-
this._logger.debug(`Unable to find context for '${domain}'.`);
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
return this._context;
|
|
463
|
-
}
|
|
464
|
-
async clearContext(domainOrUndefined) {
|
|
465
|
-
const domain = stringOrUndefined(domainOrUndefined);
|
|
466
|
-
if (domain) {
|
|
467
|
-
const wrapper = this._domainScopedProviders.get(domain);
|
|
468
|
-
if (wrapper) {
|
|
469
|
-
const oldContext = this.getContext(domain);
|
|
470
|
-
this._domainScopedContext.delete(domain);
|
|
471
|
-
const newContext = this.getContext();
|
|
472
|
-
await this.runProviderContextChangeHandler(domain, wrapper, oldContext, newContext);
|
|
473
|
-
} else {
|
|
474
|
-
this._domainScopedContext.delete(domain);
|
|
475
|
-
}
|
|
476
|
-
} else {
|
|
477
|
-
return this.setContext({});
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
/**
|
|
481
|
-
* Resets the global evaluation context and removes the evaluation context for
|
|
482
|
-
* all domains.
|
|
483
|
-
*/
|
|
484
|
-
async clearContexts() {
|
|
485
|
-
await this.clearContext();
|
|
486
|
-
await Promise.allSettled(Array.from(this._domainScopedProviders.keys()).map((domain) => this.clearContext(domain)));
|
|
487
|
-
}
|
|
488
|
-
/**
|
|
489
|
-
* A factory function for creating new named OpenFeature clients. Clients can contain
|
|
490
|
-
* their own state (e.g. logger, hook, context). Multiple clients can be used
|
|
491
|
-
* to segment feature flag configuration.
|
|
492
|
-
*
|
|
493
|
-
* If there is already a provider bound to this name via {@link this.setProvider setProvider}, this provider will be used.
|
|
494
|
-
* Otherwise, the default provider is used until a provider is assigned to that name.
|
|
495
|
-
* @param {string} domain An identifier which logically binds clients with providers
|
|
496
|
-
* @param {string} version The version of the client (only used for metadata)
|
|
497
|
-
* @returns {Client} OpenFeature Client
|
|
498
|
-
*/
|
|
499
|
-
getClient(domain, version) {
|
|
500
|
-
return new OpenFeatureClient(
|
|
501
|
-
// functions are passed here to make sure that these values are always up to date,
|
|
502
|
-
// and so we don't have to make these public properties on the API class.
|
|
503
|
-
() => this.getProviderForClient(domain),
|
|
504
|
-
() => this.getProviderStatus(domain),
|
|
505
|
-
() => this.buildAndCacheEventEmitterForClient(domain),
|
|
506
|
-
() => this._logger,
|
|
507
|
-
{ domain, version }
|
|
508
|
-
);
|
|
509
|
-
}
|
|
510
|
-
/**
|
|
511
|
-
* Clears all registered providers and resets the default provider.
|
|
512
|
-
* @returns {Promise<void>}
|
|
513
|
-
*/
|
|
514
|
-
async clearProviders() {
|
|
515
|
-
await super.clearProvidersAndSetDefault(NOOP_PROVIDER);
|
|
516
|
-
this._domainScopedContext.clear();
|
|
517
|
-
}
|
|
518
|
-
async runProviderContextChangeHandler(domain, wrapper, oldContext, newContext) {
|
|
519
|
-
const providerName = wrapper.provider?.metadata?.name || "unnamed-provider";
|
|
520
|
-
try {
|
|
521
|
-
if (typeof wrapper.provider.onContextChange === "function") {
|
|
522
|
-
wrapper.incrementPendingContextChanges();
|
|
523
|
-
wrapper.status = this._statusEnumType.RECONCILING;
|
|
524
|
-
this.getAssociatedEventEmitters(domain).forEach((emitter) => {
|
|
525
|
-
emitter?.emit(ClientProviderEvents.Reconciling, { domain, providerName });
|
|
526
|
-
});
|
|
527
|
-
this._apiEmitter?.emit(ClientProviderEvents.Reconciling, { domain, providerName });
|
|
528
|
-
await wrapper.provider.onContextChange(oldContext, newContext);
|
|
529
|
-
wrapper.decrementPendingContextChanges();
|
|
530
|
-
}
|
|
531
|
-
wrapper.status = this._statusEnumType.READY;
|
|
532
|
-
if (wrapper.allContextChangesSettled) {
|
|
533
|
-
this.getAssociatedEventEmitters(domain).forEach((emitter) => {
|
|
534
|
-
emitter?.emit(ClientProviderEvents.ContextChanged, { clientName: domain, domain, providerName });
|
|
535
|
-
});
|
|
536
|
-
this._apiEmitter?.emit(ClientProviderEvents.ContextChanged, { clientName: domain, domain, providerName });
|
|
537
|
-
}
|
|
538
|
-
} catch (err) {
|
|
539
|
-
wrapper.decrementPendingContextChanges();
|
|
540
|
-
wrapper.status = this._statusEnumType.ERROR;
|
|
541
|
-
if (wrapper.allContextChangesSettled) {
|
|
542
|
-
const error = err;
|
|
543
|
-
const message = `Error running ${providerName}'s context change handler: ${error?.message}`;
|
|
544
|
-
this._logger?.error(`${message}`, err);
|
|
545
|
-
this.getAssociatedEventEmitters(domain).forEach((emitter) => {
|
|
546
|
-
emitter?.emit(ClientProviderEvents.Error, { clientName: domain, domain, providerName, message });
|
|
547
|
-
});
|
|
548
|
-
this._apiEmitter?.emit(ClientProviderEvents.Error, { clientName: domain, domain, providerName, message });
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
};
|
|
553
|
-
var OpenFeature = OpenFeatureAPI.getInstance();
|
|
415
|
+
import {
|
|
416
|
+
OpenFeatureCommonAPI,
|
|
417
|
+
ProviderWrapper,
|
|
418
|
+
objectOrUndefined,
|
|
419
|
+
stringOrUndefined
|
|
420
|
+
} from "@openfeature/core";
|
|
554
421
|
|
|
555
|
-
// src/client/open-feature-client.ts
|
|
422
|
+
// src/client/internal/open-feature-client.ts
|
|
423
|
+
import {
|
|
424
|
+
ErrorCode as ErrorCode2,
|
|
425
|
+
ProviderFatalError,
|
|
426
|
+
ProviderNotReadyError,
|
|
427
|
+
SafeLogger,
|
|
428
|
+
StandardResolutionReasons as StandardResolutionReasons2,
|
|
429
|
+
instantiateErrorByErrorCode,
|
|
430
|
+
statusMatchesEvent
|
|
431
|
+
} from "@openfeature/core";
|
|
556
432
|
var OpenFeatureClient = class {
|
|
557
433
|
constructor(providerAccessor, providerStatusAccessor, emitterAccessor, globalLogger, options) {
|
|
558
434
|
this.providerAccessor = providerAccessor;
|
|
@@ -560,14 +436,14 @@ var OpenFeatureClient = class {
|
|
|
560
436
|
this.emitterAccessor = emitterAccessor;
|
|
561
437
|
this.globalLogger = globalLogger;
|
|
562
438
|
this.options = options;
|
|
439
|
+
this._hooks = [];
|
|
563
440
|
}
|
|
564
|
-
_hooks = [];
|
|
565
|
-
_clientLogger;
|
|
566
441
|
get metadata() {
|
|
442
|
+
var _a, _b;
|
|
567
443
|
return {
|
|
568
444
|
// Use domain if name is not provided
|
|
569
|
-
name: this.options.domain
|
|
570
|
-
domain: this.options.domain
|
|
445
|
+
name: (_a = this.options.domain) != null ? _a : this.options.name,
|
|
446
|
+
domain: (_b = this.options.domain) != null ? _b : this.options.name,
|
|
571
447
|
version: this.options.version,
|
|
572
448
|
providerMetadata: this.providerAccessor().metadata
|
|
573
449
|
};
|
|
@@ -576,6 +452,7 @@ var OpenFeatureClient = class {
|
|
|
576
452
|
return this.providerStatusAccessor();
|
|
577
453
|
}
|
|
578
454
|
addHandler(eventType, handler) {
|
|
455
|
+
var _a;
|
|
579
456
|
this.emitterAccessor().addHandler(eventType, handler);
|
|
580
457
|
const shouldRunNow = statusMatchesEvent(eventType, this.providerStatus);
|
|
581
458
|
if (shouldRunNow) {
|
|
@@ -586,7 +463,7 @@ var OpenFeatureClient = class {
|
|
|
586
463
|
providerName: this._provider.metadata.name
|
|
587
464
|
});
|
|
588
465
|
} catch (err) {
|
|
589
|
-
this._logger
|
|
466
|
+
(_a = this._logger) == null ? void 0 : _a.error("Error running event handler:", err);
|
|
590
467
|
}
|
|
591
468
|
}
|
|
592
469
|
}
|
|
@@ -650,6 +527,7 @@ var OpenFeatureClient = class {
|
|
|
650
527
|
return this.evaluate(flagKey, this._provider.resolveObjectEvaluation, defaultValue, "object", options);
|
|
651
528
|
}
|
|
652
529
|
evaluate(flagKey, resolver, defaultValue, flagType, options = {}) {
|
|
530
|
+
var _a, _b;
|
|
653
531
|
const allHooks = [
|
|
654
532
|
...OpenFeature.getHooks(),
|
|
655
533
|
...this.getHooks(),
|
|
@@ -657,9 +535,7 @@ var OpenFeatureClient = class {
|
|
|
657
535
|
...this._provider.hooks || []
|
|
658
536
|
];
|
|
659
537
|
const allHooksReversed = [...allHooks].reverse();
|
|
660
|
-
const context = {
|
|
661
|
-
...OpenFeature.getContext(this?.options?.domain)
|
|
662
|
-
};
|
|
538
|
+
const context = __spreadValues({}, OpenFeature.getContext((_a = this == null ? void 0 : this.options) == null ? void 0 : _a.domain));
|
|
663
539
|
const hookContext = {
|
|
664
540
|
flagKey,
|
|
665
541
|
defaultValue,
|
|
@@ -677,16 +553,18 @@ var OpenFeatureClient = class {
|
|
|
677
553
|
throw new ProviderFatalError("provider is in an irrecoverable error state");
|
|
678
554
|
}
|
|
679
555
|
const resolution = resolver.call(this._provider, flagKey, defaultValue, context, this._logger);
|
|
680
|
-
const evaluationDetails = {
|
|
681
|
-
|
|
682
|
-
flagMetadata: Object.freeze(resolution.flagMetadata ?? {}),
|
|
556
|
+
const evaluationDetails = __spreadProps(__spreadValues({}, resolution), {
|
|
557
|
+
flagMetadata: Object.freeze((_b = resolution.flagMetadata) != null ? _b : {}),
|
|
683
558
|
flagKey
|
|
684
|
-
};
|
|
559
|
+
});
|
|
560
|
+
if (evaluationDetails.errorCode) {
|
|
561
|
+
throw instantiateErrorByErrorCode(evaluationDetails.errorCode);
|
|
562
|
+
}
|
|
685
563
|
this.afterHooks(allHooksReversed, hookContext, evaluationDetails, options);
|
|
686
564
|
return evaluationDetails;
|
|
687
565
|
} catch (err) {
|
|
688
|
-
const errorMessage = err
|
|
689
|
-
const errorCode = err
|
|
566
|
+
const errorMessage = err == null ? void 0 : err.message;
|
|
567
|
+
const errorCode = (err == null ? void 0 : err.code) || ErrorCode2.GENERAL;
|
|
690
568
|
this.errorHooks(allHooksReversed, hookContext, err, options);
|
|
691
569
|
return {
|
|
692
570
|
errorCode,
|
|
@@ -701,40 +579,44 @@ var OpenFeatureClient = class {
|
|
|
701
579
|
}
|
|
702
580
|
}
|
|
703
581
|
beforeHooks(hooks, hookContext, options) {
|
|
582
|
+
var _a;
|
|
704
583
|
Object.freeze(hookContext);
|
|
705
584
|
Object.freeze(hookContext.context);
|
|
706
585
|
for (const hook of hooks) {
|
|
707
|
-
hook
|
|
586
|
+
(_a = hook == null ? void 0 : hook.before) == null ? void 0 : _a.call(hook, hookContext, Object.freeze(options.hookHints));
|
|
708
587
|
}
|
|
709
588
|
}
|
|
710
589
|
afterHooks(hooks, hookContext, evaluationDetails, options) {
|
|
590
|
+
var _a;
|
|
711
591
|
for (const hook of hooks) {
|
|
712
|
-
hook
|
|
592
|
+
(_a = hook == null ? void 0 : hook.after) == null ? void 0 : _a.call(hook, hookContext, evaluationDetails, options.hookHints);
|
|
713
593
|
}
|
|
714
594
|
}
|
|
715
595
|
errorHooks(hooks, hookContext, err, options) {
|
|
596
|
+
var _a;
|
|
716
597
|
for (const hook of hooks) {
|
|
717
598
|
try {
|
|
718
|
-
hook
|
|
599
|
+
(_a = hook == null ? void 0 : hook.error) == null ? void 0 : _a.call(hook, hookContext, err, options.hookHints);
|
|
719
600
|
} catch (err2) {
|
|
720
601
|
this._logger.error(`Unhandled error during 'error' hook: ${err2}`);
|
|
721
602
|
if (err2 instanceof Error) {
|
|
722
603
|
this._logger.error(err2.stack);
|
|
723
604
|
}
|
|
724
|
-
this._logger.error(err2
|
|
605
|
+
this._logger.error(err2 == null ? void 0 : err2.stack);
|
|
725
606
|
}
|
|
726
607
|
}
|
|
727
608
|
}
|
|
728
609
|
finallyHooks(hooks, hookContext, options) {
|
|
610
|
+
var _a;
|
|
729
611
|
for (const hook of hooks) {
|
|
730
612
|
try {
|
|
731
|
-
hook
|
|
613
|
+
(_a = hook == null ? void 0 : hook.finally) == null ? void 0 : _a.call(hook, hookContext, options.hookHints);
|
|
732
614
|
} catch (err) {
|
|
733
615
|
this._logger.error(`Unhandled error during 'finally' hook: ${err}`);
|
|
734
616
|
if (err instanceof Error) {
|
|
735
617
|
this._logger.error(err.stack);
|
|
736
618
|
}
|
|
737
|
-
this._logger.error(err
|
|
619
|
+
this._logger.error(err == null ? void 0 : err.stack);
|
|
738
620
|
}
|
|
739
621
|
}
|
|
740
622
|
}
|
|
@@ -746,6 +628,222 @@ var OpenFeatureClient = class {
|
|
|
746
628
|
}
|
|
747
629
|
};
|
|
748
630
|
|
|
631
|
+
// src/open-feature.ts
|
|
632
|
+
var GLOBAL_OPENFEATURE_API_KEY = Symbol.for("@openfeature/web-sdk/api");
|
|
633
|
+
var _globalThis = globalThis;
|
|
634
|
+
var OpenFeatureAPI = class _OpenFeatureAPI extends OpenFeatureCommonAPI {
|
|
635
|
+
constructor() {
|
|
636
|
+
super("client");
|
|
637
|
+
this._statusEnumType = ClientProviderStatus;
|
|
638
|
+
this._apiEmitter = new OpenFeatureEventEmitter();
|
|
639
|
+
this._defaultProvider = new ProviderWrapper(
|
|
640
|
+
NOOP_PROVIDER,
|
|
641
|
+
ClientProviderStatus.NOT_READY,
|
|
642
|
+
this._statusEnumType
|
|
643
|
+
);
|
|
644
|
+
this._domainScopedProviders = /* @__PURE__ */ new Map();
|
|
645
|
+
this._createEventEmitter = () => new OpenFeatureEventEmitter();
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Gets a singleton instance of the OpenFeature API.
|
|
649
|
+
* @ignore
|
|
650
|
+
* @returns {OpenFeatureAPI} OpenFeature API
|
|
651
|
+
*/
|
|
652
|
+
static getInstance() {
|
|
653
|
+
const globalApi = _globalThis[GLOBAL_OPENFEATURE_API_KEY];
|
|
654
|
+
if (globalApi) {
|
|
655
|
+
return globalApi;
|
|
656
|
+
}
|
|
657
|
+
const instance = new _OpenFeatureAPI();
|
|
658
|
+
_globalThis[GLOBAL_OPENFEATURE_API_KEY] = instance;
|
|
659
|
+
return instance;
|
|
660
|
+
}
|
|
661
|
+
getProviderStatus(domain) {
|
|
662
|
+
var _a, _b;
|
|
663
|
+
if (!domain) {
|
|
664
|
+
return this._defaultProvider.status;
|
|
665
|
+
}
|
|
666
|
+
return (_b = (_a = this._domainScopedProviders.get(domain)) == null ? void 0 : _a.status) != null ? _b : this._defaultProvider.status;
|
|
667
|
+
}
|
|
668
|
+
setProviderAndWait(clientOrProvider, providerContextOrUndefined, contextOrUndefined) {
|
|
669
|
+
return __async(this, null, function* () {
|
|
670
|
+
const domain = stringOrUndefined(clientOrProvider);
|
|
671
|
+
const provider = domain ? objectOrUndefined(providerContextOrUndefined) : objectOrUndefined(clientOrProvider);
|
|
672
|
+
const context = domain ? objectOrUndefined(contextOrUndefined) : objectOrUndefined(providerContextOrUndefined);
|
|
673
|
+
if (context) {
|
|
674
|
+
if (domain) {
|
|
675
|
+
this._domainScopedContext.set(domain, context);
|
|
676
|
+
} else {
|
|
677
|
+
this._context = context;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
yield this.setAwaitableProvider(domain, provider);
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
setProvider(domainOrProvider, providerContextOrUndefined, contextOrUndefined) {
|
|
684
|
+
const domain = stringOrUndefined(domainOrProvider);
|
|
685
|
+
const provider = domain ? objectOrUndefined(providerContextOrUndefined) : objectOrUndefined(domainOrProvider);
|
|
686
|
+
const context = domain ? objectOrUndefined(contextOrUndefined) : objectOrUndefined(providerContextOrUndefined);
|
|
687
|
+
if (context) {
|
|
688
|
+
if (domain) {
|
|
689
|
+
this._domainScopedContext.set(domain, context);
|
|
690
|
+
} else {
|
|
691
|
+
this._context = context;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
const maybePromise = this.setAwaitableProvider(domain, provider);
|
|
695
|
+
Promise.resolve(maybePromise).catch((err) => {
|
|
696
|
+
this._logger.error("Error during provider initialization:", err);
|
|
697
|
+
});
|
|
698
|
+
return this;
|
|
699
|
+
}
|
|
700
|
+
setContext(domainOrContext, contextOrUndefined) {
|
|
701
|
+
return __async(this, null, function* () {
|
|
702
|
+
var _a, _b;
|
|
703
|
+
const domain = stringOrUndefined(domainOrContext);
|
|
704
|
+
const context = (_b = (_a = objectOrUndefined(domainOrContext)) != null ? _a : objectOrUndefined(contextOrUndefined)) != null ? _b : {};
|
|
705
|
+
if (domain) {
|
|
706
|
+
const wrapper = this._domainScopedProviders.get(domain);
|
|
707
|
+
if (wrapper) {
|
|
708
|
+
const oldContext = this.getContext(domain);
|
|
709
|
+
this._domainScopedContext.set(domain, context);
|
|
710
|
+
yield this.runProviderContextChangeHandler(domain, wrapper, oldContext, context);
|
|
711
|
+
} else {
|
|
712
|
+
this._domainScopedContext.set(domain, context);
|
|
713
|
+
}
|
|
714
|
+
} else {
|
|
715
|
+
const oldContext = this._context;
|
|
716
|
+
this._context = context;
|
|
717
|
+
const unboundProviders = Array.from(this._domainScopedProviders.entries()).filter(([domain2]) => !this._domainScopedContext.has(domain2)).reduce((acc, [domain2, wrapper]) => {
|
|
718
|
+
acc.push({ domain: domain2, wrapper });
|
|
719
|
+
return acc;
|
|
720
|
+
}, []);
|
|
721
|
+
const allDomainRecords = [
|
|
722
|
+
// add in the default (no domain)
|
|
723
|
+
{ domain: void 0, wrapper: this._defaultProvider },
|
|
724
|
+
...unboundProviders
|
|
725
|
+
];
|
|
726
|
+
yield Promise.all(
|
|
727
|
+
allDomainRecords.map((dm) => this.runProviderContextChangeHandler(dm.domain, dm.wrapper, oldContext, context))
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
getContext(domainOrUndefined) {
|
|
733
|
+
const domain = stringOrUndefined(domainOrUndefined);
|
|
734
|
+
if (domain) {
|
|
735
|
+
const context = this._domainScopedContext.get(domain);
|
|
736
|
+
if (context) {
|
|
737
|
+
return context;
|
|
738
|
+
} else {
|
|
739
|
+
this._logger.debug(`Unable to find context for '${domain}'.`);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
return this._context;
|
|
743
|
+
}
|
|
744
|
+
clearContext(domainOrUndefined) {
|
|
745
|
+
return __async(this, null, function* () {
|
|
746
|
+
const domain = stringOrUndefined(domainOrUndefined);
|
|
747
|
+
if (domain) {
|
|
748
|
+
const wrapper = this._domainScopedProviders.get(domain);
|
|
749
|
+
if (wrapper) {
|
|
750
|
+
const oldContext = this.getContext(domain);
|
|
751
|
+
this._domainScopedContext.delete(domain);
|
|
752
|
+
const newContext = this.getContext();
|
|
753
|
+
yield this.runProviderContextChangeHandler(domain, wrapper, oldContext, newContext);
|
|
754
|
+
} else {
|
|
755
|
+
this._domainScopedContext.delete(domain);
|
|
756
|
+
}
|
|
757
|
+
} else {
|
|
758
|
+
return this.setContext({});
|
|
759
|
+
}
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Resets the global evaluation context and removes the evaluation context for
|
|
764
|
+
* all domains.
|
|
765
|
+
*/
|
|
766
|
+
clearContexts() {
|
|
767
|
+
return __async(this, null, function* () {
|
|
768
|
+
yield this.clearContext();
|
|
769
|
+
yield Promise.allSettled(Array.from(this._domainScopedProviders.keys()).map((domain) => this.clearContext(domain)));
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* A factory function for creating new named OpenFeature clients. Clients can contain
|
|
774
|
+
* their own state (e.g. logger, hook, context). Multiple clients can be used
|
|
775
|
+
* to segment feature flag configuration.
|
|
776
|
+
*
|
|
777
|
+
* If there is already a provider bound to this name via {@link this.setProvider setProvider}, this provider will be used.
|
|
778
|
+
* Otherwise, the default provider is used until a provider is assigned to that name.
|
|
779
|
+
* @param {string} domain An identifier which logically binds clients with providers
|
|
780
|
+
* @param {string} version The version of the client (only used for metadata)
|
|
781
|
+
* @returns {Client} OpenFeature Client
|
|
782
|
+
*/
|
|
783
|
+
getClient(domain, version) {
|
|
784
|
+
return new OpenFeatureClient(
|
|
785
|
+
// functions are passed here to make sure that these values are always up to date,
|
|
786
|
+
// and so we don't have to make these public properties on the API class.
|
|
787
|
+
() => this.getProviderForClient(domain),
|
|
788
|
+
() => this.getProviderStatus(domain),
|
|
789
|
+
() => this.buildAndCacheEventEmitterForClient(domain),
|
|
790
|
+
() => this._logger,
|
|
791
|
+
{ domain, version }
|
|
792
|
+
);
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Clears all registered providers and resets the default provider.
|
|
796
|
+
* @returns {Promise<void>}
|
|
797
|
+
*/
|
|
798
|
+
clearProviders() {
|
|
799
|
+
return __async(this, null, function* () {
|
|
800
|
+
yield __superGet(_OpenFeatureAPI.prototype, this, "clearProvidersAndSetDefault").call(this, NOOP_PROVIDER);
|
|
801
|
+
this._domainScopedContext.clear();
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
runProviderContextChangeHandler(domain, wrapper, oldContext, newContext) {
|
|
805
|
+
return __async(this, null, function* () {
|
|
806
|
+
var _a, _b, _c, _d, _e, _f;
|
|
807
|
+
const providerName = ((_b = (_a = wrapper.provider) == null ? void 0 : _a.metadata) == null ? void 0 : _b.name) || "unnamed-provider";
|
|
808
|
+
try {
|
|
809
|
+
if (typeof wrapper.provider.onContextChange === "function") {
|
|
810
|
+
const maybePromise = wrapper.provider.onContextChange(oldContext, newContext);
|
|
811
|
+
if (typeof (maybePromise == null ? void 0 : maybePromise.then) === "function") {
|
|
812
|
+
wrapper.incrementPendingContextChanges();
|
|
813
|
+
wrapper.status = this._statusEnumType.RECONCILING;
|
|
814
|
+
this.getAssociatedEventEmitters(domain).forEach((emitter) => {
|
|
815
|
+
emitter == null ? void 0 : emitter.emit(ClientProviderEvents.Reconciling, { domain, providerName });
|
|
816
|
+
});
|
|
817
|
+
(_c = this._apiEmitter) == null ? void 0 : _c.emit(ClientProviderEvents.Reconciling, { domain, providerName });
|
|
818
|
+
yield maybePromise;
|
|
819
|
+
wrapper.decrementPendingContextChanges();
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
wrapper.status = this._statusEnumType.READY;
|
|
823
|
+
if (wrapper.allContextChangesSettled) {
|
|
824
|
+
this.getAssociatedEventEmitters(domain).forEach((emitter) => {
|
|
825
|
+
emitter == null ? void 0 : emitter.emit(ClientProviderEvents.ContextChanged, { clientName: domain, domain, providerName });
|
|
826
|
+
});
|
|
827
|
+
(_d = this._apiEmitter) == null ? void 0 : _d.emit(ClientProviderEvents.ContextChanged, { clientName: domain, domain, providerName });
|
|
828
|
+
}
|
|
829
|
+
} catch (err) {
|
|
830
|
+
wrapper.decrementPendingContextChanges();
|
|
831
|
+
wrapper.status = this._statusEnumType.ERROR;
|
|
832
|
+
if (wrapper.allContextChangesSettled) {
|
|
833
|
+
const error = err;
|
|
834
|
+
const message = `Error running ${providerName}'s context change handler: ${error == null ? void 0 : error.message}`;
|
|
835
|
+
(_e = this._logger) == null ? void 0 : _e.error(`${message}`, err);
|
|
836
|
+
this.getAssociatedEventEmitters(domain).forEach((emitter) => {
|
|
837
|
+
emitter == null ? void 0 : emitter.emit(ClientProviderEvents.Error, { clientName: domain, domain, providerName, message });
|
|
838
|
+
});
|
|
839
|
+
(_f = this._apiEmitter) == null ? void 0 : _f.emit(ClientProviderEvents.Error, { clientName: domain, domain, providerName, message });
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
};
|
|
845
|
+
var OpenFeature = OpenFeatureAPI.getInstance();
|
|
846
|
+
|
|
749
847
|
// src/index.ts
|
|
750
848
|
export * from "@openfeature/core";
|
|
751
849
|
export {
|
|
@@ -753,7 +851,6 @@ export {
|
|
|
753
851
|
NOOP_PROVIDER,
|
|
754
852
|
OpenFeature,
|
|
755
853
|
OpenFeatureAPI,
|
|
756
|
-
OpenFeatureClient,
|
|
757
854
|
OpenFeatureEventEmitter,
|
|
758
855
|
ClientProviderEvents as ProviderEvents,
|
|
759
856
|
ClientProviderStatus as ProviderStatus
|