@openfeature/server-sdk 1.18.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/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/open-feature.ts
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 import_core4 = require("@openfeature/core");
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, import_core4.statusMatchesEvent)(eventType, this._providerStatus);
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 import_core4.SafeLogger(logger);
809
+ this._clientLogger = new import_core10.SafeLogger(logger);
257
810
  return this;
258
811
  }
259
812
  setContext(context) {
@@ -354,18 +907,21 @@ var OpenFeatureClient = class {
354
907
  ];
355
908
  const allHooksReversed = [...allHooks].reverse();
356
909
  const mergedContext = this.mergeContexts(invocationContext);
357
- const hookContext = {
358
- flagKey,
359
- defaultValue,
360
- flagValueType: flagType,
361
- clientMetadata: this.metadata,
362
- providerMetadata: this._provider.metadata,
363
- context: mergedContext,
364
- logger: this._logger
365
- };
910
+ const hookContexts = allHooksReversed.map(
911
+ () => Object.freeze({
912
+ flagKey,
913
+ defaultValue,
914
+ flagValueType: flagType,
915
+ clientMetadata: this.metadata,
916
+ providerMetadata: this._provider.metadata,
917
+ context: mergedContext,
918
+ logger: this._logger,
919
+ hookData: new import_core10.MapHookData()
920
+ })
921
+ );
366
922
  let evaluationDetails;
367
923
  try {
368
- const frozenContext = yield this.beforeHooks(allHooks, hookContext, options);
924
+ const frozenContext = yield this.beforeHooks(allHooks, hookContexts, mergedContext, options);
369
925
  this.shortCircuitIfNotReady();
370
926
  const resolution = yield resolver.call(this._provider, flagKey, defaultValue, frozenContext, this._logger);
371
927
  const resolutionDetails = __spreadProps(__spreadValues({}, resolution), {
@@ -373,44 +929,55 @@ var OpenFeatureClient = class {
373
929
  flagKey
374
930
  });
375
931
  if (resolutionDetails.errorCode) {
376
- const err = (0, import_core4.instantiateErrorByErrorCode)(resolutionDetails.errorCode, resolutionDetails.errorMessage);
377
- yield this.errorHooks(allHooksReversed, hookContext, err, options);
932
+ const err = (0, import_core10.instantiateErrorByErrorCode)(resolutionDetails.errorCode, resolutionDetails.errorMessage);
933
+ yield this.errorHooks(allHooksReversed, hookContexts, err, options);
378
934
  evaluationDetails = this.getErrorEvaluationDetails(flagKey, defaultValue, err, resolutionDetails.flagMetadata);
379
935
  } else {
380
- yield this.afterHooks(allHooksReversed, hookContext, resolutionDetails, options);
936
+ yield this.afterHooks(allHooksReversed, hookContexts, resolutionDetails, options);
381
937
  evaluationDetails = resolutionDetails;
382
938
  }
383
939
  } catch (err) {
384
- yield this.errorHooks(allHooksReversed, hookContext, err, options);
940
+ yield this.errorHooks(allHooksReversed, hookContexts, err, options);
385
941
  evaluationDetails = this.getErrorEvaluationDetails(flagKey, defaultValue, err);
386
942
  }
387
- yield this.finallyHooks(allHooksReversed, hookContext, evaluationDetails, options);
943
+ yield this.finallyHooks(allHooksReversed, hookContexts, evaluationDetails, options);
388
944
  return evaluationDetails;
389
945
  });
390
946
  }
391
- beforeHooks(hooks, hookContext, options) {
947
+ beforeHooks(hooks, hookContexts, mergedContext, options) {
392
948
  return __async(this, null, function* () {
393
949
  var _a;
394
- for (const hook of hooks) {
395
- Object.freeze(hookContext);
396
- Object.assign(hookContext.context, __spreadValues(__spreadValues({}, hookContext.context), yield (_a = hook == null ? void 0 : hook.before) == null ? void 0 : _a.call(hook, hookContext, Object.freeze(options.hookHints))));
950
+ let accumulatedContext = mergedContext;
951
+ for (const [index, hook] of hooks.entries()) {
952
+ const hookContextIndex = hooks.length - 1 - index;
953
+ const hookContext = hookContexts[hookContextIndex];
954
+ Object.assign(hookContext.context, accumulatedContext);
955
+ const hookResult = yield (_a = hook == null ? void 0 : hook.before) == null ? void 0 : _a.call(hook, hookContext, Object.freeze(options.hookHints));
956
+ if (hookResult) {
957
+ accumulatedContext = __spreadValues(__spreadValues({}, accumulatedContext), hookResult);
958
+ for (let i = 0; i < hooks.length; i++) {
959
+ Object.assign(hookContexts[hookContextIndex].context, accumulatedContext);
960
+ }
961
+ }
397
962
  }
398
- return Object.freeze(hookContext.context);
963
+ return Object.freeze(accumulatedContext);
399
964
  });
400
965
  }
401
- afterHooks(hooks, hookContext, evaluationDetails, options) {
966
+ afterHooks(hooks, hookContexts, evaluationDetails, options) {
402
967
  return __async(this, null, function* () {
403
968
  var _a;
404
- for (const hook of hooks) {
969
+ for (const [index, hook] of hooks.entries()) {
970
+ const hookContext = hookContexts[index];
405
971
  yield (_a = hook == null ? void 0 : hook.after) == null ? void 0 : _a.call(hook, hookContext, evaluationDetails, options.hookHints);
406
972
  }
407
973
  });
408
974
  }
409
- errorHooks(hooks, hookContext, err, options) {
975
+ errorHooks(hooks, hookContexts, err, options) {
410
976
  return __async(this, null, function* () {
411
977
  var _a;
412
- for (const hook of hooks) {
978
+ for (const [index, hook] of hooks.entries()) {
413
979
  try {
980
+ const hookContext = hookContexts[index];
414
981
  yield (_a = hook == null ? void 0 : hook.error) == null ? void 0 : _a.call(hook, hookContext, err, options.hookHints);
415
982
  } catch (err2) {
416
983
  this._logger.error(`Unhandled error during 'error' hook: ${err2}`);
@@ -422,11 +989,12 @@ var OpenFeatureClient = class {
422
989
  }
423
990
  });
424
991
  }
425
- finallyHooks(hooks, hookContext, evaluationDetails, options) {
992
+ finallyHooks(hooks, hookContexts, evaluationDetails, options) {
426
993
  return __async(this, null, function* () {
427
994
  var _a;
428
- for (const hook of hooks) {
995
+ for (const [index, hook] of hooks.entries()) {
429
996
  try {
997
+ const hookContext = hookContexts[index];
430
998
  yield (_a = hook == null ? void 0 : hook.finally) == null ? void 0 : _a.call(hook, hookContext, evaluationDetails, options.hookHints);
431
999
  } catch (err) {
432
1000
  this._logger.error(`Unhandled error during 'finally' hook: ${err}`);
@@ -452,42 +1020,25 @@ var OpenFeatureClient = class {
452
1020
  }
453
1021
  shortCircuitIfNotReady() {
454
1022
  if (this.providerStatus === import_core.ServerProviderStatus.NOT_READY) {
455
- throw new import_core4.ProviderNotReadyError("provider has not yet initialized");
1023
+ throw new import_core10.ProviderNotReadyError("provider has not yet initialized");
456
1024
  } else if (this.providerStatus === import_core.ServerProviderStatus.FATAL) {
457
- throw new import_core4.ProviderFatalError("provider is in an irrecoverable error state");
1025
+ throw new import_core10.ProviderFatalError("provider is in an irrecoverable error state");
458
1026
  }
459
1027
  }
460
1028
  getErrorEvaluationDetails(flagKey, defaultValue, err, flagMetadata = {}) {
461
1029
  const errorMessage = err == null ? void 0 : err.message;
462
- const errorCode = (err == null ? void 0 : err.code) || import_core4.ErrorCode.GENERAL;
1030
+ const errorCode = (err == null ? void 0 : err.code) || import_core10.ErrorCode.GENERAL;
463
1031
  return {
464
1032
  errorCode,
465
1033
  errorMessage,
466
1034
  value: defaultValue,
467
- reason: import_core4.StandardResolutionReasons.ERROR,
1035
+ reason: import_core10.StandardResolutionReasons.ERROR,
468
1036
  flagMetadata: Object.freeze(flagMetadata),
469
1037
  flagKey
470
1038
  };
471
1039
  }
472
1040
  };
473
1041
 
474
- // src/events/open-feature-event-emitter.ts
475
- var import_core5 = require("@openfeature/core");
476
- var import_node_events = require("node:events");
477
- var OpenFeatureEventEmitter = class extends import_core5.GenericEventEmitter {
478
- constructor() {
479
- super();
480
- this.eventEmitter = new import_node_events.EventEmitter({ captureRejections: true });
481
- this.eventEmitter.on("error", (err) => {
482
- var _a;
483
- (_a = this._logger) == null ? void 0 : _a.error("Error running event handler:", err);
484
- });
485
- }
486
- };
487
-
488
- // src/events/events.ts
489
- var import_core6 = require("@openfeature/core");
490
-
491
1042
  // src/transaction-context/no-op-transaction-context-propagator.ts
492
1043
  var NoopTransactionContextPropagator = class {
493
1044
  getTransactionContext() {
@@ -517,12 +1068,12 @@ var AsyncLocalStorageTransactionContextPropagator = class {
517
1068
  // src/open-feature.ts
518
1069
  var GLOBAL_OPENFEATURE_API_KEY = Symbol.for("@openfeature/js-sdk/api");
519
1070
  var _globalThis = globalThis;
520
- var OpenFeatureAPI = class _OpenFeatureAPI extends import_core7.OpenFeatureCommonAPI {
1071
+ var OpenFeatureAPI = class _OpenFeatureAPI extends import_core11.OpenFeatureCommonAPI {
521
1072
  constructor() {
522
1073
  super("server");
523
1074
  this._statusEnumType = import_core.ServerProviderStatus;
524
1075
  this._apiEmitter = new OpenFeatureEventEmitter();
525
- this._defaultProvider = new import_core7.ProviderWrapper(
1076
+ this._defaultProvider = new import_core11.ProviderWrapper(
526
1077
  NOOP_PROVIDER,
527
1078
  import_core.ServerProviderStatus.NOT_READY,
528
1079
  this._statusEnumType
@@ -554,14 +1105,14 @@ var OpenFeatureAPI = class _OpenFeatureAPI extends import_core7.OpenFeatureCommo
554
1105
  }
555
1106
  setProviderAndWait(domainOrProvider, providerOrUndefined) {
556
1107
  return __async(this, null, function* () {
557
- const domain = (0, import_core7.stringOrUndefined)(domainOrProvider);
558
- const provider = domain ? (0, import_core7.objectOrUndefined)(providerOrUndefined) : (0, import_core7.objectOrUndefined)(domainOrProvider);
1108
+ const domain = (0, import_core11.stringOrUndefined)(domainOrProvider);
1109
+ const provider = domain ? (0, import_core11.objectOrUndefined)(providerOrUndefined) : (0, import_core11.objectOrUndefined)(domainOrProvider);
559
1110
  yield this.setAwaitableProvider(domain, provider);
560
1111
  });
561
1112
  }
562
1113
  setProvider(clientOrProvider, providerOrUndefined) {
563
- const domain = (0, import_core7.stringOrUndefined)(clientOrProvider);
564
- const provider = domain ? (0, import_core7.objectOrUndefined)(providerOrUndefined) : (0, import_core7.objectOrUndefined)(clientOrProvider);
1114
+ const domain = (0, import_core11.stringOrUndefined)(clientOrProvider);
1115
+ const provider = domain ? (0, import_core11.objectOrUndefined)(providerOrUndefined) : (0, import_core11.objectOrUndefined)(clientOrProvider);
565
1116
  const maybePromise = this.setAwaitableProvider(domain, provider);
566
1117
  Promise.resolve(maybePromise).catch((err) => {
567
1118
  this._logger.error("Error during provider initialization:", err);
@@ -580,9 +1131,9 @@ var OpenFeatureAPI = class _OpenFeatureAPI extends import_core7.OpenFeatureCommo
580
1131
  }
581
1132
  getClient(domainOrContext, versionOrContext, contextOrUndefined) {
582
1133
  var _a, _b;
583
- const domain = (0, import_core7.stringOrUndefined)(domainOrContext);
584
- const version = (0, import_core7.stringOrUndefined)(versionOrContext);
585
- const context = (_b = (_a = (0, import_core7.objectOrUndefined)(domainOrContext)) != null ? _a : (0, import_core7.objectOrUndefined)(versionOrContext)) != null ? _b : (0, import_core7.objectOrUndefined)(contextOrUndefined);
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);
586
1137
  return new OpenFeatureClient(
587
1138
  () => this.getProviderForClient(domain),
588
1139
  () => this.getProviderStatus(domain),
@@ -633,8 +1184,15 @@ var OpenFeature = OpenFeatureAPI.getInstance();
633
1184
  __reExport(index_exports, require("@openfeature/core"), module.exports);
634
1185
  // Annotate the CommonJS export names for ESM import in node:
635
1186
  0 && (module.exports = {
1187
+ AggregateError,
636
1188
  AsyncLocalStorageTransactionContextPropagator,
1189
+ BaseEvaluationStrategy,
1190
+ ComparisonStrategy,
1191
+ ErrorWithCode,
1192
+ FirstMatchStrategy,
1193
+ FirstSuccessfulStrategy,
637
1194
  InMemoryProvider,
1195
+ MultiProvider,
638
1196
  NOOP_PROVIDER,
639
1197
  NOOP_TRANSACTION_CONTEXT_PROPAGATOR,
640
1198
  OpenFeature,
@@ -642,6 +1200,8 @@ __reExport(index_exports, require("@openfeature/core"), module.exports);
642
1200
  OpenFeatureEventEmitter,
643
1201
  ProviderEvents,
644
1202
  ProviderStatus,
1203
+ constructAggregateError,
1204
+ throwAggregateErrorFromPromiseResults,
645
1205
  ...require("@openfeature/core")
646
1206
  });
647
1207
  //# sourceMappingURL=index.js.map