@openfeature/server-sdk 1.19.0 → 1.20.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/LICENSE +1 -1
- package/README.md +79 -3
- package/dist/cjs/index.js +582 -37
- package/dist/cjs/index.js.map +4 -4
- package/dist/esm/index.js +558 -22
- package/dist/esm/index.js.map +4 -4
- package/dist/types.d.ts +146 -3
- package/package.json +1 -1
package/dist/cjs/index.js
CHANGED
|
@@ -58,15 +58,24 @@ var __async = (__this, __arguments, generator) => {
|
|
|
58
58
|
// src/index.ts
|
|
59
59
|
var index_exports = {};
|
|
60
60
|
__export(index_exports, {
|
|
61
|
+
AggregateError: () => AggregateError,
|
|
61
62
|
AsyncLocalStorageTransactionContextPropagator: () => AsyncLocalStorageTransactionContextPropagator,
|
|
63
|
+
BaseEvaluationStrategy: () => BaseEvaluationStrategy,
|
|
64
|
+
ComparisonStrategy: () => ComparisonStrategy,
|
|
65
|
+
ErrorWithCode: () => ErrorWithCode,
|
|
66
|
+
FirstMatchStrategy: () => FirstMatchStrategy,
|
|
67
|
+
FirstSuccessfulStrategy: () => FirstSuccessfulStrategy,
|
|
62
68
|
InMemoryProvider: () => InMemoryProvider,
|
|
69
|
+
MultiProvider: () => MultiProvider,
|
|
63
70
|
NOOP_PROVIDER: () => NOOP_PROVIDER,
|
|
64
71
|
NOOP_TRANSACTION_CONTEXT_PROPAGATOR: () => NOOP_TRANSACTION_CONTEXT_PROPAGATOR,
|
|
65
72
|
OpenFeature: () => OpenFeature,
|
|
66
73
|
OpenFeatureAPI: () => OpenFeatureAPI,
|
|
67
74
|
OpenFeatureEventEmitter: () => OpenFeatureEventEmitter,
|
|
68
75
|
ProviderEvents: () => import_core6.ServerProviderEvents,
|
|
69
|
-
ProviderStatus: () => import_core.ServerProviderStatus
|
|
76
|
+
ProviderStatus: () => import_core.ServerProviderStatus,
|
|
77
|
+
constructAggregateError: () => constructAggregateError,
|
|
78
|
+
throwAggregateErrorFromPromiseResults: () => throwAggregateErrorFromPromiseResults
|
|
70
79
|
});
|
|
71
80
|
module.exports = __toCommonJS(index_exports);
|
|
72
81
|
|
|
@@ -194,11 +203,555 @@ var InMemoryProvider = class {
|
|
|
194
203
|
}
|
|
195
204
|
};
|
|
196
205
|
|
|
197
|
-
// src/
|
|
206
|
+
// src/provider/multi-provider/multi-provider.ts
|
|
207
|
+
var import_core9 = require("@openfeature/core");
|
|
208
|
+
|
|
209
|
+
// src/events/open-feature-event-emitter.ts
|
|
210
|
+
var import_core4 = require("@openfeature/core");
|
|
211
|
+
var import_node_events = require("node:events");
|
|
212
|
+
var OpenFeatureEventEmitter = class extends import_core4.GenericEventEmitter {
|
|
213
|
+
constructor() {
|
|
214
|
+
super();
|
|
215
|
+
this.eventEmitter = new import_node_events.EventEmitter({ captureRejections: true });
|
|
216
|
+
this.eventEmitter.on("error", (err) => {
|
|
217
|
+
var _a;
|
|
218
|
+
(_a = this._logger) == null ? void 0 : _a.error("Error running event handler:", err);
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// src/provider/multi-provider/errors.ts
|
|
224
|
+
var import_core5 = require("@openfeature/core");
|
|
225
|
+
var ErrorWithCode = class extends import_core5.OpenFeatureError {
|
|
226
|
+
constructor(code, message) {
|
|
227
|
+
super(message);
|
|
228
|
+
this.code = code;
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
var AggregateError = class extends import_core5.GeneralError {
|
|
232
|
+
constructor(message, originalErrors) {
|
|
233
|
+
super(message);
|
|
234
|
+
this.originalErrors = originalErrors;
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
var constructAggregateError = (providerErrors) => {
|
|
238
|
+
const errorsWithSource = providerErrors.map(({ providerName, error }) => {
|
|
239
|
+
return { source: providerName, error };
|
|
240
|
+
}).flat();
|
|
241
|
+
return new AggregateError(
|
|
242
|
+
`Provider errors occurred: ${errorsWithSource[0].source}: ${errorsWithSource[0].error}`,
|
|
243
|
+
errorsWithSource
|
|
244
|
+
);
|
|
245
|
+
};
|
|
246
|
+
var throwAggregateErrorFromPromiseResults = (result, providerEntries) => {
|
|
247
|
+
const errors = result.map((r, i) => {
|
|
248
|
+
if (r.status === "rejected") {
|
|
249
|
+
return { error: r.reason, providerName: providerEntries[i].name };
|
|
250
|
+
}
|
|
251
|
+
return null;
|
|
252
|
+
}).filter((val) => Boolean(val));
|
|
253
|
+
if (errors.length) {
|
|
254
|
+
throw constructAggregateError(errors);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
// src/provider/multi-provider/hook-executor.ts
|
|
259
|
+
var HookExecutor = class {
|
|
260
|
+
constructor(logger) {
|
|
261
|
+
this.logger = logger;
|
|
262
|
+
}
|
|
263
|
+
beforeHooks(hooks, hookContext, hints) {
|
|
264
|
+
return __async(this, null, function* () {
|
|
265
|
+
var _a;
|
|
266
|
+
for (const hook of hooks != null ? hooks : []) {
|
|
267
|
+
Object.freeze(hookContext);
|
|
268
|
+
Object.assign(hookContext.context, __spreadValues({}, yield (_a = hook == null ? void 0 : hook.before) == null ? void 0 : _a.call(hook, hookContext, Object.freeze(hints))));
|
|
269
|
+
}
|
|
270
|
+
return Object.freeze(hookContext.context);
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
afterHooks(hooks, hookContext, evaluationDetails, hints) {
|
|
274
|
+
return __async(this, null, function* () {
|
|
275
|
+
var _a;
|
|
276
|
+
for (const hook of hooks != null ? hooks : []) {
|
|
277
|
+
yield (_a = hook == null ? void 0 : hook.after) == null ? void 0 : _a.call(hook, hookContext, evaluationDetails, hints);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
errorHooks(hooks, hookContext, err, hints) {
|
|
282
|
+
return __async(this, null, function* () {
|
|
283
|
+
var _a;
|
|
284
|
+
for (const hook of hooks != null ? hooks : []) {
|
|
285
|
+
try {
|
|
286
|
+
yield (_a = hook == null ? void 0 : hook.error) == null ? void 0 : _a.call(hook, hookContext, err, hints);
|
|
287
|
+
} catch (err2) {
|
|
288
|
+
this.logger.error(`Unhandled error during 'error' hook: ${err2}`);
|
|
289
|
+
if (err2 instanceof Error) {
|
|
290
|
+
this.logger.error(err2.stack);
|
|
291
|
+
}
|
|
292
|
+
this.logger.error(err2 == null ? void 0 : err2.stack);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
finallyHooks(hooks, hookContext, evaluationDetails, hints) {
|
|
298
|
+
return __async(this, null, function* () {
|
|
299
|
+
var _a;
|
|
300
|
+
for (const hook of hooks != null ? hooks : []) {
|
|
301
|
+
try {
|
|
302
|
+
yield (_a = hook == null ? void 0 : hook.finally) == null ? void 0 : _a.call(hook, hookContext, evaluationDetails, hints);
|
|
303
|
+
} catch (err) {
|
|
304
|
+
this.logger.error(`Unhandled error during 'finally' hook: ${err}`);
|
|
305
|
+
if (err instanceof Error) {
|
|
306
|
+
this.logger.error(err.stack);
|
|
307
|
+
}
|
|
308
|
+
this.logger.error(err == null ? void 0 : err.stack);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
// src/events/events.ts
|
|
316
|
+
var import_core6 = require("@openfeature/core");
|
|
317
|
+
|
|
318
|
+
// src/provider/multi-provider/status-tracker.ts
|
|
319
|
+
var StatusTracker = class {
|
|
320
|
+
constructor(events) {
|
|
321
|
+
this.events = events;
|
|
322
|
+
this.providerStatuses = {};
|
|
323
|
+
}
|
|
324
|
+
wrapEventHandler(providerEntry) {
|
|
325
|
+
var _a, _b, _c, _d;
|
|
326
|
+
const provider = providerEntry.provider;
|
|
327
|
+
(_a = provider.events) == null ? void 0 : _a.addHandler(import_core6.ServerProviderEvents.Error, (details) => {
|
|
328
|
+
this.changeProviderStatus(providerEntry.name, import_core.ServerProviderStatus.ERROR, details);
|
|
329
|
+
});
|
|
330
|
+
(_b = provider.events) == null ? void 0 : _b.addHandler(import_core6.ServerProviderEvents.Stale, (details) => {
|
|
331
|
+
this.changeProviderStatus(providerEntry.name, import_core.ServerProviderStatus.STALE, details);
|
|
332
|
+
});
|
|
333
|
+
(_c = provider.events) == null ? void 0 : _c.addHandler(import_core6.ServerProviderEvents.ConfigurationChanged, (details) => {
|
|
334
|
+
this.events.emit(import_core6.ServerProviderEvents.ConfigurationChanged, details);
|
|
335
|
+
});
|
|
336
|
+
(_d = provider.events) == null ? void 0 : _d.addHandler(import_core6.ServerProviderEvents.Ready, (details) => {
|
|
337
|
+
this.changeProviderStatus(providerEntry.name, import_core.ServerProviderStatus.READY, details);
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
providerStatus(name) {
|
|
341
|
+
return this.providerStatuses[name];
|
|
342
|
+
}
|
|
343
|
+
getStatusFromProviderStatuses() {
|
|
344
|
+
const statuses = Object.values(this.providerStatuses);
|
|
345
|
+
if (statuses.includes(import_core.ServerProviderStatus.FATAL)) {
|
|
346
|
+
return import_core.ServerProviderStatus.FATAL;
|
|
347
|
+
} else if (statuses.includes(import_core.ServerProviderStatus.NOT_READY)) {
|
|
348
|
+
return import_core.ServerProviderStatus.NOT_READY;
|
|
349
|
+
} else if (statuses.includes(import_core.ServerProviderStatus.ERROR)) {
|
|
350
|
+
return import_core.ServerProviderStatus.ERROR;
|
|
351
|
+
} else if (statuses.includes(import_core.ServerProviderStatus.STALE)) {
|
|
352
|
+
return import_core.ServerProviderStatus.STALE;
|
|
353
|
+
}
|
|
354
|
+
return import_core.ServerProviderStatus.READY;
|
|
355
|
+
}
|
|
356
|
+
changeProviderStatus(name, status, details) {
|
|
357
|
+
const currentStatus = this.getStatusFromProviderStatuses();
|
|
358
|
+
this.providerStatuses[name] = status;
|
|
359
|
+
const newStatus = this.getStatusFromProviderStatuses();
|
|
360
|
+
if (currentStatus !== newStatus) {
|
|
361
|
+
if (newStatus === import_core.ServerProviderStatus.FATAL || newStatus === import_core.ServerProviderStatus.ERROR) {
|
|
362
|
+
this.events.emit(import_core6.ServerProviderEvents.Error, details);
|
|
363
|
+
} else if (newStatus === import_core.ServerProviderStatus.STALE) {
|
|
364
|
+
this.events.emit(import_core6.ServerProviderEvents.Stale, details);
|
|
365
|
+
} else if (newStatus === import_core.ServerProviderStatus.READY) {
|
|
366
|
+
this.events.emit(import_core6.ServerProviderEvents.Ready, details);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
// src/provider/multi-provider/strategies/base-evaluation-strategy.ts
|
|
373
|
+
var BaseEvaluationStrategy = class {
|
|
374
|
+
constructor() {
|
|
375
|
+
this.runMode = "sequential";
|
|
376
|
+
}
|
|
377
|
+
shouldEvaluateThisProvider(strategyContext, _evalContext) {
|
|
378
|
+
if (strategyContext.providerStatus === import_core.ServerProviderStatus.NOT_READY || strategyContext.providerStatus === import_core.ServerProviderStatus.FATAL) {
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
return true;
|
|
382
|
+
}
|
|
383
|
+
shouldEvaluateNextProvider(_strategyContext, _context, _result) {
|
|
384
|
+
return true;
|
|
385
|
+
}
|
|
386
|
+
shouldTrackWithThisProvider(strategyContext, _context, _trackingEventName, _trackingEventDetails) {
|
|
387
|
+
if (strategyContext.providerStatus === import_core.ServerProviderStatus.NOT_READY || strategyContext.providerStatus === import_core.ServerProviderStatus.FATAL) {
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
390
|
+
return true;
|
|
391
|
+
}
|
|
392
|
+
hasError(resolution) {
|
|
393
|
+
return "thrownError" in resolution || !!resolution.details.errorCode;
|
|
394
|
+
}
|
|
395
|
+
hasErrorWithCode(resolution, code) {
|
|
396
|
+
var _a;
|
|
397
|
+
return "thrownError" in resolution ? ((_a = resolution.thrownError) == null ? void 0 : _a.code) === code : resolution.details.errorCode === code;
|
|
398
|
+
}
|
|
399
|
+
collectProviderErrors(resolutions) {
|
|
400
|
+
var _a;
|
|
401
|
+
const errors = [];
|
|
402
|
+
for (const resolution of resolutions) {
|
|
403
|
+
if ("thrownError" in resolution) {
|
|
404
|
+
errors.push({ providerName: resolution.providerName, error: resolution.thrownError });
|
|
405
|
+
} else if (resolution.details.errorCode) {
|
|
406
|
+
errors.push({
|
|
407
|
+
providerName: resolution.providerName,
|
|
408
|
+
error: new ErrorWithCode(resolution.details.errorCode, (_a = resolution.details.errorMessage) != null ? _a : "unknown error")
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return { errors };
|
|
413
|
+
}
|
|
414
|
+
resolutionToFinalResult(resolution) {
|
|
415
|
+
return { details: resolution.details, provider: resolution.provider, providerName: resolution.providerName };
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
// src/provider/multi-provider/strategies/first-match-strategy.ts
|
|
198
420
|
var import_core7 = require("@openfeature/core");
|
|
421
|
+
var FirstMatchStrategy = class extends BaseEvaluationStrategy {
|
|
422
|
+
shouldEvaluateNextProvider(strategyContext, context, result) {
|
|
423
|
+
if (this.hasErrorWithCode(result, import_core7.ErrorCode.FLAG_NOT_FOUND)) {
|
|
424
|
+
return true;
|
|
425
|
+
}
|
|
426
|
+
if (this.hasError(result)) {
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
determineFinalResult(strategyContext, context, resolutions) {
|
|
432
|
+
const finalResolution = resolutions[resolutions.length - 1];
|
|
433
|
+
if (this.hasError(finalResolution)) {
|
|
434
|
+
return this.collectProviderErrors(resolutions);
|
|
435
|
+
}
|
|
436
|
+
return this.resolutionToFinalResult(finalResolution);
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
// src/provider/multi-provider/strategies/first-successful-strategy.ts
|
|
441
|
+
var FirstSuccessfulStrategy = class extends BaseEvaluationStrategy {
|
|
442
|
+
shouldEvaluateNextProvider(strategyContext, context, result) {
|
|
443
|
+
return this.hasError(result);
|
|
444
|
+
}
|
|
445
|
+
determineFinalResult(strategyContext, context, resolutions) {
|
|
446
|
+
const finalResolution = resolutions[resolutions.length - 1];
|
|
447
|
+
if (this.hasError(finalResolution)) {
|
|
448
|
+
return this.collectProviderErrors(resolutions);
|
|
449
|
+
}
|
|
450
|
+
return this.resolutionToFinalResult(finalResolution);
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
// src/provider/multi-provider/strategies/comparison-strategy.ts
|
|
455
|
+
var import_core8 = require("@openfeature/core");
|
|
456
|
+
var ComparisonStrategy = class extends BaseEvaluationStrategy {
|
|
457
|
+
constructor(fallbackProvider, onMismatch) {
|
|
458
|
+
super();
|
|
459
|
+
this.fallbackProvider = fallbackProvider;
|
|
460
|
+
this.onMismatch = onMismatch;
|
|
461
|
+
this.runMode = "parallel";
|
|
462
|
+
}
|
|
463
|
+
determineFinalResult(strategyContext, context, resolutions) {
|
|
464
|
+
var _a;
|
|
465
|
+
let value;
|
|
466
|
+
let fallbackResolution;
|
|
467
|
+
let finalResolution;
|
|
468
|
+
let mismatch = false;
|
|
469
|
+
for (const [i, resolution] of resolutions.entries()) {
|
|
470
|
+
if (this.hasError(resolution)) {
|
|
471
|
+
return this.collectProviderErrors(resolutions);
|
|
472
|
+
}
|
|
473
|
+
if (resolution.provider === this.fallbackProvider) {
|
|
474
|
+
fallbackResolution = resolution;
|
|
475
|
+
}
|
|
476
|
+
if (i === 0) {
|
|
477
|
+
finalResolution = resolution;
|
|
478
|
+
}
|
|
479
|
+
if (typeof value !== "undefined" && value !== resolution.details.value) {
|
|
480
|
+
mismatch = true;
|
|
481
|
+
} else {
|
|
482
|
+
value = resolution.details.value;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
if (!fallbackResolution) {
|
|
486
|
+
throw new import_core8.GeneralError("Fallback provider not found in resolution results");
|
|
487
|
+
}
|
|
488
|
+
if (!finalResolution) {
|
|
489
|
+
throw new import_core8.GeneralError("Final resolution not found in resolution results");
|
|
490
|
+
}
|
|
491
|
+
if (mismatch) {
|
|
492
|
+
(_a = this.onMismatch) == null ? void 0 : _a.call(this, resolutions);
|
|
493
|
+
return {
|
|
494
|
+
details: fallbackResolution.details,
|
|
495
|
+
provider: fallbackResolution.provider
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
return this.resolutionToFinalResult(finalResolution);
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
// src/provider/multi-provider/multi-provider.ts
|
|
503
|
+
var MultiProvider = class _MultiProvider {
|
|
504
|
+
constructor(constructorProviders, evaluationStrategy = new FirstMatchStrategy(), logger = new import_core9.DefaultLogger()) {
|
|
505
|
+
this.constructorProviders = constructorProviders;
|
|
506
|
+
this.evaluationStrategy = evaluationStrategy;
|
|
507
|
+
this.logger = logger;
|
|
508
|
+
this.runsOn = "server";
|
|
509
|
+
this.events = new OpenFeatureEventEmitter();
|
|
510
|
+
this.hookContexts = /* @__PURE__ */ new WeakMap();
|
|
511
|
+
this.hookHints = /* @__PURE__ */ new WeakMap();
|
|
512
|
+
this.providerEntries = [];
|
|
513
|
+
this.providerEntriesByName = {};
|
|
514
|
+
this.statusTracker = new StatusTracker(this.events);
|
|
515
|
+
this.hookExecutor = new HookExecutor(this.logger);
|
|
516
|
+
this.registerProviders(constructorProviders);
|
|
517
|
+
const aggregateMetadata = Object.keys(this.providerEntriesByName).reduce((acc, name) => {
|
|
518
|
+
return __spreadProps(__spreadValues({}, acc), { [name]: this.providerEntriesByName[name].provider.metadata });
|
|
519
|
+
}, {});
|
|
520
|
+
this.metadata = __spreadProps(__spreadValues({}, aggregateMetadata), {
|
|
521
|
+
name: _MultiProvider.name
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
registerProviders(constructorProviders) {
|
|
525
|
+
var _a, _b;
|
|
526
|
+
const providersByName = {};
|
|
527
|
+
for (const constructorProvider of constructorProviders) {
|
|
528
|
+
const providerName = constructorProvider.provider.metadata.name;
|
|
529
|
+
const candidateName = (_a = constructorProvider.name) != null ? _a : providerName;
|
|
530
|
+
if (constructorProvider.name && providersByName[constructorProvider.name]) {
|
|
531
|
+
throw new Error("Provider names must be unique");
|
|
532
|
+
}
|
|
533
|
+
(_b = providersByName[candidateName]) != null ? _b : providersByName[candidateName] = [];
|
|
534
|
+
providersByName[candidateName].push(constructorProvider.provider);
|
|
535
|
+
}
|
|
536
|
+
for (const name of Object.keys(providersByName)) {
|
|
537
|
+
const useIndexedNames = providersByName[name].length > 1;
|
|
538
|
+
for (let i = 0; i < providersByName[name].length; i++) {
|
|
539
|
+
const indexedName = useIndexedNames ? `${name}-${i + 1}` : name;
|
|
540
|
+
this.providerEntriesByName[indexedName] = { provider: providersByName[name][i], name: indexedName };
|
|
541
|
+
this.providerEntries.push(this.providerEntriesByName[indexedName]);
|
|
542
|
+
this.statusTracker.wrapEventHandler(this.providerEntriesByName[indexedName]);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
Object.freeze(this.providerEntries);
|
|
546
|
+
Object.freeze(this.providerEntriesByName);
|
|
547
|
+
}
|
|
548
|
+
initialize(context) {
|
|
549
|
+
return __async(this, null, function* () {
|
|
550
|
+
const result = yield Promise.allSettled(
|
|
551
|
+
this.providerEntries.map((provider) => {
|
|
552
|
+
var _a, _b;
|
|
553
|
+
return (_b = (_a = provider.provider).initialize) == null ? void 0 : _b.call(_a, context);
|
|
554
|
+
})
|
|
555
|
+
);
|
|
556
|
+
throwAggregateErrorFromPromiseResults(result, this.providerEntries);
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
onClose() {
|
|
560
|
+
return __async(this, null, function* () {
|
|
561
|
+
const result = yield Promise.allSettled(this.providerEntries.map((provider) => {
|
|
562
|
+
var _a, _b;
|
|
563
|
+
return (_b = (_a = provider.provider).onClose) == null ? void 0 : _b.call(_a);
|
|
564
|
+
}));
|
|
565
|
+
throwAggregateErrorFromPromiseResults(result, this.providerEntries);
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
resolveBooleanEvaluation(flagKey, defaultValue, context) {
|
|
569
|
+
return this.flagResolutionProxy(flagKey, "boolean", defaultValue, context);
|
|
570
|
+
}
|
|
571
|
+
resolveStringEvaluation(flagKey, defaultValue, context) {
|
|
572
|
+
return this.flagResolutionProxy(flagKey, "string", defaultValue, context);
|
|
573
|
+
}
|
|
574
|
+
resolveNumberEvaluation(flagKey, defaultValue, context) {
|
|
575
|
+
return this.flagResolutionProxy(flagKey, "number", defaultValue, context);
|
|
576
|
+
}
|
|
577
|
+
resolveObjectEvaluation(flagKey, defaultValue, context) {
|
|
578
|
+
return this.flagResolutionProxy(flagKey, "object", defaultValue, context);
|
|
579
|
+
}
|
|
580
|
+
flagResolutionProxy(flagKey, flagType, defaultValue, context) {
|
|
581
|
+
return __async(this, null, function* () {
|
|
582
|
+
var _a;
|
|
583
|
+
const hookContext = this.hookContexts.get(context);
|
|
584
|
+
const hookHints = this.hookHints.get(context);
|
|
585
|
+
if (!hookContext || !hookHints) {
|
|
586
|
+
throw new import_core9.GeneralError("Hook context not available for evaluation");
|
|
587
|
+
}
|
|
588
|
+
const tasks = [];
|
|
589
|
+
for (const providerEntry of this.providerEntries) {
|
|
590
|
+
const task = this.evaluateProviderEntry(
|
|
591
|
+
flagKey,
|
|
592
|
+
flagType,
|
|
593
|
+
defaultValue,
|
|
594
|
+
providerEntry,
|
|
595
|
+
hookContext,
|
|
596
|
+
hookHints,
|
|
597
|
+
context
|
|
598
|
+
);
|
|
599
|
+
tasks.push(task);
|
|
600
|
+
if (this.evaluationStrategy.runMode === "sequential") {
|
|
601
|
+
const [shouldEvaluateNext] = yield task;
|
|
602
|
+
if (!shouldEvaluateNext) {
|
|
603
|
+
break;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
const results = yield Promise.all(tasks);
|
|
608
|
+
const resolutions = results.map(([, resolution]) => resolution).filter((r) => Boolean(r));
|
|
609
|
+
const finalResult = this.evaluationStrategy.determineFinalResult({ flagKey, flagType }, context, resolutions);
|
|
610
|
+
if ((_a = finalResult.errors) == null ? void 0 : _a.length) {
|
|
611
|
+
throw constructAggregateError(finalResult.errors);
|
|
612
|
+
}
|
|
613
|
+
if (!finalResult.details) {
|
|
614
|
+
throw new import_core9.GeneralError("No result was returned from any provider");
|
|
615
|
+
}
|
|
616
|
+
return finalResult.details;
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
evaluateProviderEntry(flagKey, flagType, defaultValue, providerEntry, hookContext, hookHints, context) {
|
|
620
|
+
return __async(this, null, function* () {
|
|
621
|
+
let evaluationResult = void 0;
|
|
622
|
+
const provider = providerEntry.provider;
|
|
623
|
+
const strategyContext = {
|
|
624
|
+
flagKey,
|
|
625
|
+
flagType,
|
|
626
|
+
provider,
|
|
627
|
+
providerName: providerEntry.name,
|
|
628
|
+
providerStatus: this.statusTracker.providerStatus(providerEntry.name)
|
|
629
|
+
};
|
|
630
|
+
if (!this.evaluationStrategy.shouldEvaluateThisProvider(strategyContext, context)) {
|
|
631
|
+
return [true, null];
|
|
632
|
+
}
|
|
633
|
+
let resolution;
|
|
634
|
+
try {
|
|
635
|
+
evaluationResult = yield this.evaluateProviderAndHooks(flagKey, defaultValue, provider, hookContext, hookHints);
|
|
636
|
+
resolution = {
|
|
637
|
+
details: evaluationResult,
|
|
638
|
+
provider,
|
|
639
|
+
providerName: providerEntry.name
|
|
640
|
+
};
|
|
641
|
+
} catch (error) {
|
|
642
|
+
resolution = {
|
|
643
|
+
thrownError: error,
|
|
644
|
+
provider,
|
|
645
|
+
providerName: providerEntry.name
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
return [
|
|
649
|
+
this.evaluationStrategy.runMode === "sequential" ? this.evaluationStrategy.shouldEvaluateNextProvider(strategyContext, context, resolution) : true,
|
|
650
|
+
resolution
|
|
651
|
+
];
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
evaluateProviderAndHooks(flagKey, defaultValue, provider, hookContext, hookHints) {
|
|
655
|
+
return __async(this, null, function* () {
|
|
656
|
+
var _a;
|
|
657
|
+
let providerContext = void 0;
|
|
658
|
+
let evaluationDetails;
|
|
659
|
+
const hookContextCopy = __spreadProps(__spreadValues({}, hookContext), { context: __spreadValues({}, hookContext.context) });
|
|
660
|
+
try {
|
|
661
|
+
providerContext = yield this.hookExecutor.beforeHooks(provider.hooks, hookContextCopy, hookHints);
|
|
662
|
+
const resolutionDetails = yield this.callProviderResolve(
|
|
663
|
+
provider,
|
|
664
|
+
flagKey,
|
|
665
|
+
defaultValue,
|
|
666
|
+
providerContext || {}
|
|
667
|
+
);
|
|
668
|
+
evaluationDetails = __spreadProps(__spreadValues({}, resolutionDetails), {
|
|
669
|
+
flagMetadata: Object.freeze((_a = resolutionDetails.flagMetadata) != null ? _a : {}),
|
|
670
|
+
flagKey
|
|
671
|
+
});
|
|
672
|
+
yield this.hookExecutor.afterHooks(provider.hooks, hookContextCopy, evaluationDetails, hookHints);
|
|
673
|
+
} catch (error) {
|
|
674
|
+
yield this.hookExecutor.errorHooks(provider.hooks, hookContextCopy, error, hookHints);
|
|
675
|
+
evaluationDetails = this.getErrorEvaluationDetails(flagKey, defaultValue, error);
|
|
676
|
+
}
|
|
677
|
+
yield this.hookExecutor.finallyHooks(provider.hooks, hookContextCopy, evaluationDetails, hookHints);
|
|
678
|
+
return evaluationDetails;
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
callProviderResolve(provider, flagKey, defaultValue, context) {
|
|
682
|
+
return __async(this, null, function* () {
|
|
683
|
+
switch (typeof defaultValue) {
|
|
684
|
+
case "string":
|
|
685
|
+
return yield provider.resolveStringEvaluation(flagKey, defaultValue, context, this.logger);
|
|
686
|
+
case "number":
|
|
687
|
+
return yield provider.resolveNumberEvaluation(flagKey, defaultValue, context, this.logger);
|
|
688
|
+
case "object":
|
|
689
|
+
return yield provider.resolveObjectEvaluation(flagKey, defaultValue, context, this.logger);
|
|
690
|
+
case "boolean":
|
|
691
|
+
return yield provider.resolveBooleanEvaluation(flagKey, defaultValue, context, this.logger);
|
|
692
|
+
default:
|
|
693
|
+
throw new import_core9.GeneralError("Invalid flag evaluation type");
|
|
694
|
+
}
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
get hooks() {
|
|
698
|
+
return [
|
|
699
|
+
{
|
|
700
|
+
before: (hookContext, hints) => __async(this, null, function* () {
|
|
701
|
+
this.hookContexts.set(hookContext.context, hookContext);
|
|
702
|
+
this.hookHints.set(hookContext.context, hints != null ? hints : {});
|
|
703
|
+
return hookContext.context;
|
|
704
|
+
})
|
|
705
|
+
}
|
|
706
|
+
];
|
|
707
|
+
}
|
|
708
|
+
track(trackingEventName, context, trackingEventDetails) {
|
|
709
|
+
var _a, _b;
|
|
710
|
+
for (const providerEntry of this.providerEntries) {
|
|
711
|
+
if (!providerEntry.provider.track) {
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
const strategyContext = {
|
|
715
|
+
provider: providerEntry.provider,
|
|
716
|
+
providerName: providerEntry.name,
|
|
717
|
+
providerStatus: this.statusTracker.providerStatus(providerEntry.name)
|
|
718
|
+
};
|
|
719
|
+
if (this.evaluationStrategy.shouldTrackWithThisProvider(
|
|
720
|
+
strategyContext,
|
|
721
|
+
context,
|
|
722
|
+
trackingEventName,
|
|
723
|
+
trackingEventDetails
|
|
724
|
+
)) {
|
|
725
|
+
try {
|
|
726
|
+
(_b = (_a = providerEntry.provider).track) == null ? void 0 : _b.call(_a, trackingEventName, context, trackingEventDetails);
|
|
727
|
+
} catch (error) {
|
|
728
|
+
this.logger.error(
|
|
729
|
+
`Error tracking event "${trackingEventName}" with provider "${providerEntry.name}":`,
|
|
730
|
+
error
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
getErrorEvaluationDetails(flagKey, defaultValue, err, flagMetadata = {}) {
|
|
737
|
+
const errorMessage = err == null ? void 0 : err.message;
|
|
738
|
+
const errorCode = (err == null ? void 0 : err.code) || import_core9.ErrorCode.GENERAL;
|
|
739
|
+
return {
|
|
740
|
+
errorCode,
|
|
741
|
+
errorMessage,
|
|
742
|
+
value: defaultValue,
|
|
743
|
+
reason: import_core9.StandardResolutionReasons.ERROR,
|
|
744
|
+
flagMetadata: Object.freeze(flagMetadata),
|
|
745
|
+
flagKey
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
|
|
750
|
+
// src/open-feature.ts
|
|
751
|
+
var import_core11 = require("@openfeature/core");
|
|
199
752
|
|
|
200
753
|
// src/client/internal/open-feature-client.ts
|
|
201
|
-
var
|
|
754
|
+
var import_core10 = require("@openfeature/core");
|
|
202
755
|
var OpenFeatureClient = class {
|
|
203
756
|
constructor(providerAccessor, providerStatusAccessor, emitterAccessor, apiContextAccessor, apiHooksAccessor, transactionContextAccessor, globalLogger, options, context = {}) {
|
|
204
757
|
this.providerAccessor = providerAccessor;
|
|
@@ -228,7 +781,7 @@ var OpenFeatureClient = class {
|
|
|
228
781
|
addHandler(eventType, handler, options) {
|
|
229
782
|
var _a;
|
|
230
783
|
this.emitterAccessor().addHandler(eventType, handler);
|
|
231
|
-
const shouldRunNow = (0,
|
|
784
|
+
const shouldRunNow = (0, import_core10.statusMatchesEvent)(eventType, this._providerStatus);
|
|
232
785
|
if (shouldRunNow) {
|
|
233
786
|
try {
|
|
234
787
|
handler({
|
|
@@ -253,7 +806,7 @@ var OpenFeatureClient = class {
|
|
|
253
806
|
return this.emitterAccessor().getHandlers(eventType);
|
|
254
807
|
}
|
|
255
808
|
setLogger(logger) {
|
|
256
|
-
this._clientLogger = new
|
|
809
|
+
this._clientLogger = new import_core10.SafeLogger(logger);
|
|
257
810
|
return this;
|
|
258
811
|
}
|
|
259
812
|
setContext(context) {
|
|
@@ -363,7 +916,7 @@ var OpenFeatureClient = class {
|
|
|
363
916
|
providerMetadata: this._provider.metadata,
|
|
364
917
|
context: mergedContext,
|
|
365
918
|
logger: this._logger,
|
|
366
|
-
hookData: new
|
|
919
|
+
hookData: new import_core10.MapHookData()
|
|
367
920
|
})
|
|
368
921
|
);
|
|
369
922
|
let evaluationDetails;
|
|
@@ -376,7 +929,7 @@ var OpenFeatureClient = class {
|
|
|
376
929
|
flagKey
|
|
377
930
|
});
|
|
378
931
|
if (resolutionDetails.errorCode) {
|
|
379
|
-
const err = (0,
|
|
932
|
+
const err = (0, import_core10.instantiateErrorByErrorCode)(resolutionDetails.errorCode, resolutionDetails.errorMessage);
|
|
380
933
|
yield this.errorHooks(allHooksReversed, hookContexts, err, options);
|
|
381
934
|
evaluationDetails = this.getErrorEvaluationDetails(flagKey, defaultValue, err, resolutionDetails.flagMetadata);
|
|
382
935
|
} else {
|
|
@@ -467,42 +1020,25 @@ var OpenFeatureClient = class {
|
|
|
467
1020
|
}
|
|
468
1021
|
shortCircuitIfNotReady() {
|
|
469
1022
|
if (this.providerStatus === import_core.ServerProviderStatus.NOT_READY) {
|
|
470
|
-
throw new
|
|
1023
|
+
throw new import_core10.ProviderNotReadyError("provider has not yet initialized");
|
|
471
1024
|
} else if (this.providerStatus === import_core.ServerProviderStatus.FATAL) {
|
|
472
|
-
throw new
|
|
1025
|
+
throw new import_core10.ProviderFatalError("provider is in an irrecoverable error state");
|
|
473
1026
|
}
|
|
474
1027
|
}
|
|
475
1028
|
getErrorEvaluationDetails(flagKey, defaultValue, err, flagMetadata = {}) {
|
|
476
1029
|
const errorMessage = err == null ? void 0 : err.message;
|
|
477
|
-
const errorCode = (err == null ? void 0 : err.code) ||
|
|
1030
|
+
const errorCode = (err == null ? void 0 : err.code) || import_core10.ErrorCode.GENERAL;
|
|
478
1031
|
return {
|
|
479
1032
|
errorCode,
|
|
480
1033
|
errorMessage,
|
|
481
1034
|
value: defaultValue,
|
|
482
|
-
reason:
|
|
1035
|
+
reason: import_core10.StandardResolutionReasons.ERROR,
|
|
483
1036
|
flagMetadata: Object.freeze(flagMetadata),
|
|
484
1037
|
flagKey
|
|
485
1038
|
};
|
|
486
1039
|
}
|
|
487
1040
|
};
|
|
488
1041
|
|
|
489
|
-
// src/events/open-feature-event-emitter.ts
|
|
490
|
-
var import_core5 = require("@openfeature/core");
|
|
491
|
-
var import_node_events = require("node:events");
|
|
492
|
-
var OpenFeatureEventEmitter = class extends import_core5.GenericEventEmitter {
|
|
493
|
-
constructor() {
|
|
494
|
-
super();
|
|
495
|
-
this.eventEmitter = new import_node_events.EventEmitter({ captureRejections: true });
|
|
496
|
-
this.eventEmitter.on("error", (err) => {
|
|
497
|
-
var _a;
|
|
498
|
-
(_a = this._logger) == null ? void 0 : _a.error("Error running event handler:", err);
|
|
499
|
-
});
|
|
500
|
-
}
|
|
501
|
-
};
|
|
502
|
-
|
|
503
|
-
// src/events/events.ts
|
|
504
|
-
var import_core6 = require("@openfeature/core");
|
|
505
|
-
|
|
506
1042
|
// src/transaction-context/no-op-transaction-context-propagator.ts
|
|
507
1043
|
var NoopTransactionContextPropagator = class {
|
|
508
1044
|
getTransactionContext() {
|
|
@@ -532,12 +1068,12 @@ var AsyncLocalStorageTransactionContextPropagator = class {
|
|
|
532
1068
|
// src/open-feature.ts
|
|
533
1069
|
var GLOBAL_OPENFEATURE_API_KEY = Symbol.for("@openfeature/js-sdk/api");
|
|
534
1070
|
var _globalThis = globalThis;
|
|
535
|
-
var OpenFeatureAPI = class _OpenFeatureAPI extends
|
|
1071
|
+
var OpenFeatureAPI = class _OpenFeatureAPI extends import_core11.OpenFeatureCommonAPI {
|
|
536
1072
|
constructor() {
|
|
537
1073
|
super("server");
|
|
538
1074
|
this._statusEnumType = import_core.ServerProviderStatus;
|
|
539
1075
|
this._apiEmitter = new OpenFeatureEventEmitter();
|
|
540
|
-
this._defaultProvider = new
|
|
1076
|
+
this._defaultProvider = new import_core11.ProviderWrapper(
|
|
541
1077
|
NOOP_PROVIDER,
|
|
542
1078
|
import_core.ServerProviderStatus.NOT_READY,
|
|
543
1079
|
this._statusEnumType
|
|
@@ -569,14 +1105,14 @@ var OpenFeatureAPI = class _OpenFeatureAPI extends import_core7.OpenFeatureCommo
|
|
|
569
1105
|
}
|
|
570
1106
|
setProviderAndWait(domainOrProvider, providerOrUndefined) {
|
|
571
1107
|
return __async(this, null, function* () {
|
|
572
|
-
const domain = (0,
|
|
573
|
-
const provider = domain ? (0,
|
|
1108
|
+
const domain = (0, import_core11.stringOrUndefined)(domainOrProvider);
|
|
1109
|
+
const provider = domain ? (0, import_core11.objectOrUndefined)(providerOrUndefined) : (0, import_core11.objectOrUndefined)(domainOrProvider);
|
|
574
1110
|
yield this.setAwaitableProvider(domain, provider);
|
|
575
1111
|
});
|
|
576
1112
|
}
|
|
577
1113
|
setProvider(clientOrProvider, providerOrUndefined) {
|
|
578
|
-
const domain = (0,
|
|
579
|
-
const provider = domain ? (0,
|
|
1114
|
+
const domain = (0, import_core11.stringOrUndefined)(clientOrProvider);
|
|
1115
|
+
const provider = domain ? (0, import_core11.objectOrUndefined)(providerOrUndefined) : (0, import_core11.objectOrUndefined)(clientOrProvider);
|
|
580
1116
|
const maybePromise = this.setAwaitableProvider(domain, provider);
|
|
581
1117
|
Promise.resolve(maybePromise).catch((err) => {
|
|
582
1118
|
this._logger.error("Error during provider initialization:", err);
|
|
@@ -595,9 +1131,9 @@ var OpenFeatureAPI = class _OpenFeatureAPI extends import_core7.OpenFeatureCommo
|
|
|
595
1131
|
}
|
|
596
1132
|
getClient(domainOrContext, versionOrContext, contextOrUndefined) {
|
|
597
1133
|
var _a, _b;
|
|
598
|
-
const domain = (0,
|
|
599
|
-
const version = (0,
|
|
600
|
-
const context = (_b = (_a = (0,
|
|
1134
|
+
const domain = (0, import_core11.stringOrUndefined)(domainOrContext);
|
|
1135
|
+
const version = (0, import_core11.stringOrUndefined)(versionOrContext);
|
|
1136
|
+
const context = (_b = (_a = (0, import_core11.objectOrUndefined)(domainOrContext)) != null ? _a : (0, import_core11.objectOrUndefined)(versionOrContext)) != null ? _b : (0, import_core11.objectOrUndefined)(contextOrUndefined);
|
|
601
1137
|
return new OpenFeatureClient(
|
|
602
1138
|
() => this.getProviderForClient(domain),
|
|
603
1139
|
() => this.getProviderStatus(domain),
|
|
@@ -648,8 +1184,15 @@ var OpenFeature = OpenFeatureAPI.getInstance();
|
|
|
648
1184
|
__reExport(index_exports, require("@openfeature/core"), module.exports);
|
|
649
1185
|
// Annotate the CommonJS export names for ESM import in node:
|
|
650
1186
|
0 && (module.exports = {
|
|
1187
|
+
AggregateError,
|
|
651
1188
|
AsyncLocalStorageTransactionContextPropagator,
|
|
1189
|
+
BaseEvaluationStrategy,
|
|
1190
|
+
ComparisonStrategy,
|
|
1191
|
+
ErrorWithCode,
|
|
1192
|
+
FirstMatchStrategy,
|
|
1193
|
+
FirstSuccessfulStrategy,
|
|
652
1194
|
InMemoryProvider,
|
|
1195
|
+
MultiProvider,
|
|
653
1196
|
NOOP_PROVIDER,
|
|
654
1197
|
NOOP_TRANSACTION_CONTEXT_PROPAGATOR,
|
|
655
1198
|
OpenFeature,
|
|
@@ -657,6 +1200,8 @@ __reExport(index_exports, require("@openfeature/core"), module.exports);
|
|
|
657
1200
|
OpenFeatureEventEmitter,
|
|
658
1201
|
ProviderEvents,
|
|
659
1202
|
ProviderStatus,
|
|
1203
|
+
constructAggregateError,
|
|
1204
|
+
throwAggregateErrorFromPromiseResults,
|
|
660
1205
|
...require("@openfeature/core")
|
|
661
1206
|
});
|
|
662
1207
|
//# sourceMappingURL=index.js.map
|