@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/esm/index.js CHANGED
@@ -170,6 +170,550 @@ var InMemoryProvider = class {
170
170
  }
171
171
  };
172
172
 
173
+ // src/provider/multi-provider/multi-provider.ts
174
+ import { DefaultLogger, ErrorCode as ErrorCode3, GeneralError as GeneralError4, StandardResolutionReasons as StandardResolutionReasons2 } from "@openfeature/core";
175
+
176
+ // src/events/open-feature-event-emitter.ts
177
+ import { GenericEventEmitter } from "@openfeature/core";
178
+ import { EventEmitter } from "node:events";
179
+ var OpenFeatureEventEmitter = class extends GenericEventEmitter {
180
+ constructor() {
181
+ super();
182
+ this.eventEmitter = new EventEmitter({ captureRejections: true });
183
+ this.eventEmitter.on("error", (err) => {
184
+ var _a;
185
+ (_a = this._logger) == null ? void 0 : _a.error("Error running event handler:", err);
186
+ });
187
+ }
188
+ };
189
+
190
+ // src/provider/multi-provider/errors.ts
191
+ import { GeneralError as GeneralError2, OpenFeatureError as OpenFeatureError3 } from "@openfeature/core";
192
+ var ErrorWithCode = class extends OpenFeatureError3 {
193
+ constructor(code, message) {
194
+ super(message);
195
+ this.code = code;
196
+ }
197
+ };
198
+ var AggregateError = class extends GeneralError2 {
199
+ constructor(message, originalErrors) {
200
+ super(message);
201
+ this.originalErrors = originalErrors;
202
+ }
203
+ };
204
+ var constructAggregateError = (providerErrors) => {
205
+ const errorsWithSource = providerErrors.map(({ providerName, error }) => {
206
+ return { source: providerName, error };
207
+ }).flat();
208
+ return new AggregateError(
209
+ `Provider errors occurred: ${errorsWithSource[0].source}: ${errorsWithSource[0].error}`,
210
+ errorsWithSource
211
+ );
212
+ };
213
+ var throwAggregateErrorFromPromiseResults = (result, providerEntries) => {
214
+ const errors = result.map((r, i) => {
215
+ if (r.status === "rejected") {
216
+ return { error: r.reason, providerName: providerEntries[i].name };
217
+ }
218
+ return null;
219
+ }).filter((val) => Boolean(val));
220
+ if (errors.length) {
221
+ throw constructAggregateError(errors);
222
+ }
223
+ };
224
+
225
+ // src/provider/multi-provider/hook-executor.ts
226
+ var HookExecutor = class {
227
+ constructor(logger) {
228
+ this.logger = logger;
229
+ }
230
+ beforeHooks(hooks, hookContext, hints) {
231
+ return __async(this, null, function* () {
232
+ var _a;
233
+ for (const hook of hooks != null ? hooks : []) {
234
+ Object.freeze(hookContext);
235
+ Object.assign(hookContext.context, __spreadValues({}, yield (_a = hook == null ? void 0 : hook.before) == null ? void 0 : _a.call(hook, hookContext, Object.freeze(hints))));
236
+ }
237
+ return Object.freeze(hookContext.context);
238
+ });
239
+ }
240
+ afterHooks(hooks, hookContext, evaluationDetails, hints) {
241
+ return __async(this, null, function* () {
242
+ var _a;
243
+ for (const hook of hooks != null ? hooks : []) {
244
+ yield (_a = hook == null ? void 0 : hook.after) == null ? void 0 : _a.call(hook, hookContext, evaluationDetails, hints);
245
+ }
246
+ });
247
+ }
248
+ errorHooks(hooks, hookContext, err, hints) {
249
+ return __async(this, null, function* () {
250
+ var _a;
251
+ for (const hook of hooks != null ? hooks : []) {
252
+ try {
253
+ yield (_a = hook == null ? void 0 : hook.error) == null ? void 0 : _a.call(hook, hookContext, err, hints);
254
+ } catch (err2) {
255
+ this.logger.error(`Unhandled error during 'error' hook: ${err2}`);
256
+ if (err2 instanceof Error) {
257
+ this.logger.error(err2.stack);
258
+ }
259
+ this.logger.error(err2 == null ? void 0 : err2.stack);
260
+ }
261
+ }
262
+ });
263
+ }
264
+ finallyHooks(hooks, hookContext, evaluationDetails, hints) {
265
+ return __async(this, null, function* () {
266
+ var _a;
267
+ for (const hook of hooks != null ? hooks : []) {
268
+ try {
269
+ yield (_a = hook == null ? void 0 : hook.finally) == null ? void 0 : _a.call(hook, hookContext, evaluationDetails, hints);
270
+ } catch (err) {
271
+ this.logger.error(`Unhandled error during 'finally' hook: ${err}`);
272
+ if (err instanceof Error) {
273
+ this.logger.error(err.stack);
274
+ }
275
+ this.logger.error(err == null ? void 0 : err.stack);
276
+ }
277
+ }
278
+ });
279
+ }
280
+ };
281
+
282
+ // src/events/events.ts
283
+ import { ServerProviderEvents } from "@openfeature/core";
284
+
285
+ // src/provider/multi-provider/status-tracker.ts
286
+ var StatusTracker = class {
287
+ constructor(events) {
288
+ this.events = events;
289
+ this.providerStatuses = {};
290
+ }
291
+ wrapEventHandler(providerEntry) {
292
+ var _a, _b, _c, _d;
293
+ const provider = providerEntry.provider;
294
+ (_a = provider.events) == null ? void 0 : _a.addHandler(ServerProviderEvents.Error, (details) => {
295
+ this.changeProviderStatus(providerEntry.name, ServerProviderStatus.ERROR, details);
296
+ });
297
+ (_b = provider.events) == null ? void 0 : _b.addHandler(ServerProviderEvents.Stale, (details) => {
298
+ this.changeProviderStatus(providerEntry.name, ServerProviderStatus.STALE, details);
299
+ });
300
+ (_c = provider.events) == null ? void 0 : _c.addHandler(ServerProviderEvents.ConfigurationChanged, (details) => {
301
+ this.events.emit(ServerProviderEvents.ConfigurationChanged, details);
302
+ });
303
+ (_d = provider.events) == null ? void 0 : _d.addHandler(ServerProviderEvents.Ready, (details) => {
304
+ this.changeProviderStatus(providerEntry.name, ServerProviderStatus.READY, details);
305
+ });
306
+ }
307
+ providerStatus(name) {
308
+ return this.providerStatuses[name];
309
+ }
310
+ getStatusFromProviderStatuses() {
311
+ const statuses = Object.values(this.providerStatuses);
312
+ if (statuses.includes(ServerProviderStatus.FATAL)) {
313
+ return ServerProviderStatus.FATAL;
314
+ } else if (statuses.includes(ServerProviderStatus.NOT_READY)) {
315
+ return ServerProviderStatus.NOT_READY;
316
+ } else if (statuses.includes(ServerProviderStatus.ERROR)) {
317
+ return ServerProviderStatus.ERROR;
318
+ } else if (statuses.includes(ServerProviderStatus.STALE)) {
319
+ return ServerProviderStatus.STALE;
320
+ }
321
+ return ServerProviderStatus.READY;
322
+ }
323
+ changeProviderStatus(name, status, details) {
324
+ const currentStatus = this.getStatusFromProviderStatuses();
325
+ this.providerStatuses[name] = status;
326
+ const newStatus = this.getStatusFromProviderStatuses();
327
+ if (currentStatus !== newStatus) {
328
+ if (newStatus === ServerProviderStatus.FATAL || newStatus === ServerProviderStatus.ERROR) {
329
+ this.events.emit(ServerProviderEvents.Error, details);
330
+ } else if (newStatus === ServerProviderStatus.STALE) {
331
+ this.events.emit(ServerProviderEvents.Stale, details);
332
+ } else if (newStatus === ServerProviderStatus.READY) {
333
+ this.events.emit(ServerProviderEvents.Ready, details);
334
+ }
335
+ }
336
+ }
337
+ };
338
+
339
+ // src/provider/multi-provider/strategies/base-evaluation-strategy.ts
340
+ var BaseEvaluationStrategy = class {
341
+ constructor() {
342
+ this.runMode = "sequential";
343
+ }
344
+ shouldEvaluateThisProvider(strategyContext, _evalContext) {
345
+ if (strategyContext.providerStatus === ServerProviderStatus.NOT_READY || strategyContext.providerStatus === ServerProviderStatus.FATAL) {
346
+ return false;
347
+ }
348
+ return true;
349
+ }
350
+ shouldEvaluateNextProvider(_strategyContext, _context, _result) {
351
+ return true;
352
+ }
353
+ shouldTrackWithThisProvider(strategyContext, _context, _trackingEventName, _trackingEventDetails) {
354
+ if (strategyContext.providerStatus === ServerProviderStatus.NOT_READY || strategyContext.providerStatus === ServerProviderStatus.FATAL) {
355
+ return false;
356
+ }
357
+ return true;
358
+ }
359
+ hasError(resolution) {
360
+ return "thrownError" in resolution || !!resolution.details.errorCode;
361
+ }
362
+ hasErrorWithCode(resolution, code) {
363
+ var _a;
364
+ return "thrownError" in resolution ? ((_a = resolution.thrownError) == null ? void 0 : _a.code) === code : resolution.details.errorCode === code;
365
+ }
366
+ collectProviderErrors(resolutions) {
367
+ var _a;
368
+ const errors = [];
369
+ for (const resolution of resolutions) {
370
+ if ("thrownError" in resolution) {
371
+ errors.push({ providerName: resolution.providerName, error: resolution.thrownError });
372
+ } else if (resolution.details.errorCode) {
373
+ errors.push({
374
+ providerName: resolution.providerName,
375
+ error: new ErrorWithCode(resolution.details.errorCode, (_a = resolution.details.errorMessage) != null ? _a : "unknown error")
376
+ });
377
+ }
378
+ }
379
+ return { errors };
380
+ }
381
+ resolutionToFinalResult(resolution) {
382
+ return { details: resolution.details, provider: resolution.provider, providerName: resolution.providerName };
383
+ }
384
+ };
385
+
386
+ // src/provider/multi-provider/strategies/first-match-strategy.ts
387
+ import { ErrorCode as ErrorCode2 } from "@openfeature/core";
388
+ var FirstMatchStrategy = class extends BaseEvaluationStrategy {
389
+ shouldEvaluateNextProvider(strategyContext, context, result) {
390
+ if (this.hasErrorWithCode(result, ErrorCode2.FLAG_NOT_FOUND)) {
391
+ return true;
392
+ }
393
+ if (this.hasError(result)) {
394
+ return false;
395
+ }
396
+ return false;
397
+ }
398
+ determineFinalResult(strategyContext, context, resolutions) {
399
+ const finalResolution = resolutions[resolutions.length - 1];
400
+ if (this.hasError(finalResolution)) {
401
+ return this.collectProviderErrors(resolutions);
402
+ }
403
+ return this.resolutionToFinalResult(finalResolution);
404
+ }
405
+ };
406
+
407
+ // src/provider/multi-provider/strategies/first-successful-strategy.ts
408
+ var FirstSuccessfulStrategy = class extends BaseEvaluationStrategy {
409
+ shouldEvaluateNextProvider(strategyContext, context, result) {
410
+ return this.hasError(result);
411
+ }
412
+ determineFinalResult(strategyContext, context, resolutions) {
413
+ const finalResolution = resolutions[resolutions.length - 1];
414
+ if (this.hasError(finalResolution)) {
415
+ return this.collectProviderErrors(resolutions);
416
+ }
417
+ return this.resolutionToFinalResult(finalResolution);
418
+ }
419
+ };
420
+
421
+ // src/provider/multi-provider/strategies/comparison-strategy.ts
422
+ import { GeneralError as GeneralError3 } from "@openfeature/core";
423
+ var ComparisonStrategy = class extends BaseEvaluationStrategy {
424
+ constructor(fallbackProvider, onMismatch) {
425
+ super();
426
+ this.fallbackProvider = fallbackProvider;
427
+ this.onMismatch = onMismatch;
428
+ this.runMode = "parallel";
429
+ }
430
+ determineFinalResult(strategyContext, context, resolutions) {
431
+ var _a;
432
+ let value;
433
+ let fallbackResolution;
434
+ let finalResolution;
435
+ let mismatch = false;
436
+ for (const [i, resolution] of resolutions.entries()) {
437
+ if (this.hasError(resolution)) {
438
+ return this.collectProviderErrors(resolutions);
439
+ }
440
+ if (resolution.provider === this.fallbackProvider) {
441
+ fallbackResolution = resolution;
442
+ }
443
+ if (i === 0) {
444
+ finalResolution = resolution;
445
+ }
446
+ if (typeof value !== "undefined" && value !== resolution.details.value) {
447
+ mismatch = true;
448
+ } else {
449
+ value = resolution.details.value;
450
+ }
451
+ }
452
+ if (!fallbackResolution) {
453
+ throw new GeneralError3("Fallback provider not found in resolution results");
454
+ }
455
+ if (!finalResolution) {
456
+ throw new GeneralError3("Final resolution not found in resolution results");
457
+ }
458
+ if (mismatch) {
459
+ (_a = this.onMismatch) == null ? void 0 : _a.call(this, resolutions);
460
+ return {
461
+ details: fallbackResolution.details,
462
+ provider: fallbackResolution.provider
463
+ };
464
+ }
465
+ return this.resolutionToFinalResult(finalResolution);
466
+ }
467
+ };
468
+
469
+ // src/provider/multi-provider/multi-provider.ts
470
+ var MultiProvider = class _MultiProvider {
471
+ constructor(constructorProviders, evaluationStrategy = new FirstMatchStrategy(), logger = new DefaultLogger()) {
472
+ this.constructorProviders = constructorProviders;
473
+ this.evaluationStrategy = evaluationStrategy;
474
+ this.logger = logger;
475
+ this.runsOn = "server";
476
+ this.events = new OpenFeatureEventEmitter();
477
+ this.hookContexts = /* @__PURE__ */ new WeakMap();
478
+ this.hookHints = /* @__PURE__ */ new WeakMap();
479
+ this.providerEntries = [];
480
+ this.providerEntriesByName = {};
481
+ this.statusTracker = new StatusTracker(this.events);
482
+ this.hookExecutor = new HookExecutor(this.logger);
483
+ this.registerProviders(constructorProviders);
484
+ const aggregateMetadata = Object.keys(this.providerEntriesByName).reduce((acc, name) => {
485
+ return __spreadProps(__spreadValues({}, acc), { [name]: this.providerEntriesByName[name].provider.metadata });
486
+ }, {});
487
+ this.metadata = __spreadProps(__spreadValues({}, aggregateMetadata), {
488
+ name: _MultiProvider.name
489
+ });
490
+ }
491
+ registerProviders(constructorProviders) {
492
+ var _a, _b;
493
+ const providersByName = {};
494
+ for (const constructorProvider of constructorProviders) {
495
+ const providerName = constructorProvider.provider.metadata.name;
496
+ const candidateName = (_a = constructorProvider.name) != null ? _a : providerName;
497
+ if (constructorProvider.name && providersByName[constructorProvider.name]) {
498
+ throw new Error("Provider names must be unique");
499
+ }
500
+ (_b = providersByName[candidateName]) != null ? _b : providersByName[candidateName] = [];
501
+ providersByName[candidateName].push(constructorProvider.provider);
502
+ }
503
+ for (const name of Object.keys(providersByName)) {
504
+ const useIndexedNames = providersByName[name].length > 1;
505
+ for (let i = 0; i < providersByName[name].length; i++) {
506
+ const indexedName = useIndexedNames ? `${name}-${i + 1}` : name;
507
+ this.providerEntriesByName[indexedName] = { provider: providersByName[name][i], name: indexedName };
508
+ this.providerEntries.push(this.providerEntriesByName[indexedName]);
509
+ this.statusTracker.wrapEventHandler(this.providerEntriesByName[indexedName]);
510
+ }
511
+ }
512
+ Object.freeze(this.providerEntries);
513
+ Object.freeze(this.providerEntriesByName);
514
+ }
515
+ initialize(context) {
516
+ return __async(this, null, function* () {
517
+ const result = yield Promise.allSettled(
518
+ this.providerEntries.map((provider) => {
519
+ var _a, _b;
520
+ return (_b = (_a = provider.provider).initialize) == null ? void 0 : _b.call(_a, context);
521
+ })
522
+ );
523
+ throwAggregateErrorFromPromiseResults(result, this.providerEntries);
524
+ });
525
+ }
526
+ onClose() {
527
+ return __async(this, null, function* () {
528
+ const result = yield Promise.allSettled(this.providerEntries.map((provider) => {
529
+ var _a, _b;
530
+ return (_b = (_a = provider.provider).onClose) == null ? void 0 : _b.call(_a);
531
+ }));
532
+ throwAggregateErrorFromPromiseResults(result, this.providerEntries);
533
+ });
534
+ }
535
+ resolveBooleanEvaluation(flagKey, defaultValue, context) {
536
+ return this.flagResolutionProxy(flagKey, "boolean", defaultValue, context);
537
+ }
538
+ resolveStringEvaluation(flagKey, defaultValue, context) {
539
+ return this.flagResolutionProxy(flagKey, "string", defaultValue, context);
540
+ }
541
+ resolveNumberEvaluation(flagKey, defaultValue, context) {
542
+ return this.flagResolutionProxy(flagKey, "number", defaultValue, context);
543
+ }
544
+ resolveObjectEvaluation(flagKey, defaultValue, context) {
545
+ return this.flagResolutionProxy(flagKey, "object", defaultValue, context);
546
+ }
547
+ flagResolutionProxy(flagKey, flagType, defaultValue, context) {
548
+ return __async(this, null, function* () {
549
+ var _a;
550
+ const hookContext = this.hookContexts.get(context);
551
+ const hookHints = this.hookHints.get(context);
552
+ if (!hookContext || !hookHints) {
553
+ throw new GeneralError4("Hook context not available for evaluation");
554
+ }
555
+ const tasks = [];
556
+ for (const providerEntry of this.providerEntries) {
557
+ const task = this.evaluateProviderEntry(
558
+ flagKey,
559
+ flagType,
560
+ defaultValue,
561
+ providerEntry,
562
+ hookContext,
563
+ hookHints,
564
+ context
565
+ );
566
+ tasks.push(task);
567
+ if (this.evaluationStrategy.runMode === "sequential") {
568
+ const [shouldEvaluateNext] = yield task;
569
+ if (!shouldEvaluateNext) {
570
+ break;
571
+ }
572
+ }
573
+ }
574
+ const results = yield Promise.all(tasks);
575
+ const resolutions = results.map(([, resolution]) => resolution).filter((r) => Boolean(r));
576
+ const finalResult = this.evaluationStrategy.determineFinalResult({ flagKey, flagType }, context, resolutions);
577
+ if ((_a = finalResult.errors) == null ? void 0 : _a.length) {
578
+ throw constructAggregateError(finalResult.errors);
579
+ }
580
+ if (!finalResult.details) {
581
+ throw new GeneralError4("No result was returned from any provider");
582
+ }
583
+ return finalResult.details;
584
+ });
585
+ }
586
+ evaluateProviderEntry(flagKey, flagType, defaultValue, providerEntry, hookContext, hookHints, context) {
587
+ return __async(this, null, function* () {
588
+ let evaluationResult = void 0;
589
+ const provider = providerEntry.provider;
590
+ const strategyContext = {
591
+ flagKey,
592
+ flagType,
593
+ provider,
594
+ providerName: providerEntry.name,
595
+ providerStatus: this.statusTracker.providerStatus(providerEntry.name)
596
+ };
597
+ if (!this.evaluationStrategy.shouldEvaluateThisProvider(strategyContext, context)) {
598
+ return [true, null];
599
+ }
600
+ let resolution;
601
+ try {
602
+ evaluationResult = yield this.evaluateProviderAndHooks(flagKey, defaultValue, provider, hookContext, hookHints);
603
+ resolution = {
604
+ details: evaluationResult,
605
+ provider,
606
+ providerName: providerEntry.name
607
+ };
608
+ } catch (error) {
609
+ resolution = {
610
+ thrownError: error,
611
+ provider,
612
+ providerName: providerEntry.name
613
+ };
614
+ }
615
+ return [
616
+ this.evaluationStrategy.runMode === "sequential" ? this.evaluationStrategy.shouldEvaluateNextProvider(strategyContext, context, resolution) : true,
617
+ resolution
618
+ ];
619
+ });
620
+ }
621
+ evaluateProviderAndHooks(flagKey, defaultValue, provider, hookContext, hookHints) {
622
+ return __async(this, null, function* () {
623
+ var _a;
624
+ let providerContext = void 0;
625
+ let evaluationDetails;
626
+ const hookContextCopy = __spreadProps(__spreadValues({}, hookContext), { context: __spreadValues({}, hookContext.context) });
627
+ try {
628
+ providerContext = yield this.hookExecutor.beforeHooks(provider.hooks, hookContextCopy, hookHints);
629
+ const resolutionDetails = yield this.callProviderResolve(
630
+ provider,
631
+ flagKey,
632
+ defaultValue,
633
+ providerContext || {}
634
+ );
635
+ evaluationDetails = __spreadProps(__spreadValues({}, resolutionDetails), {
636
+ flagMetadata: Object.freeze((_a = resolutionDetails.flagMetadata) != null ? _a : {}),
637
+ flagKey
638
+ });
639
+ yield this.hookExecutor.afterHooks(provider.hooks, hookContextCopy, evaluationDetails, hookHints);
640
+ } catch (error) {
641
+ yield this.hookExecutor.errorHooks(provider.hooks, hookContextCopy, error, hookHints);
642
+ evaluationDetails = this.getErrorEvaluationDetails(flagKey, defaultValue, error);
643
+ }
644
+ yield this.hookExecutor.finallyHooks(provider.hooks, hookContextCopy, evaluationDetails, hookHints);
645
+ return evaluationDetails;
646
+ });
647
+ }
648
+ callProviderResolve(provider, flagKey, defaultValue, context) {
649
+ return __async(this, null, function* () {
650
+ switch (typeof defaultValue) {
651
+ case "string":
652
+ return yield provider.resolveStringEvaluation(flagKey, defaultValue, context, this.logger);
653
+ case "number":
654
+ return yield provider.resolveNumberEvaluation(flagKey, defaultValue, context, this.logger);
655
+ case "object":
656
+ return yield provider.resolveObjectEvaluation(flagKey, defaultValue, context, this.logger);
657
+ case "boolean":
658
+ return yield provider.resolveBooleanEvaluation(flagKey, defaultValue, context, this.logger);
659
+ default:
660
+ throw new GeneralError4("Invalid flag evaluation type");
661
+ }
662
+ });
663
+ }
664
+ get hooks() {
665
+ return [
666
+ {
667
+ before: (hookContext, hints) => __async(this, null, function* () {
668
+ this.hookContexts.set(hookContext.context, hookContext);
669
+ this.hookHints.set(hookContext.context, hints != null ? hints : {});
670
+ return hookContext.context;
671
+ })
672
+ }
673
+ ];
674
+ }
675
+ track(trackingEventName, context, trackingEventDetails) {
676
+ var _a, _b;
677
+ for (const providerEntry of this.providerEntries) {
678
+ if (!providerEntry.provider.track) {
679
+ continue;
680
+ }
681
+ const strategyContext = {
682
+ provider: providerEntry.provider,
683
+ providerName: providerEntry.name,
684
+ providerStatus: this.statusTracker.providerStatus(providerEntry.name)
685
+ };
686
+ if (this.evaluationStrategy.shouldTrackWithThisProvider(
687
+ strategyContext,
688
+ context,
689
+ trackingEventName,
690
+ trackingEventDetails
691
+ )) {
692
+ try {
693
+ (_b = (_a = providerEntry.provider).track) == null ? void 0 : _b.call(_a, trackingEventName, context, trackingEventDetails);
694
+ } catch (error) {
695
+ this.logger.error(
696
+ `Error tracking event "${trackingEventName}" with provider "${providerEntry.name}":`,
697
+ error
698
+ );
699
+ }
700
+ }
701
+ }
702
+ }
703
+ getErrorEvaluationDetails(flagKey, defaultValue, err, flagMetadata = {}) {
704
+ const errorMessage = err == null ? void 0 : err.message;
705
+ const errorCode = (err == null ? void 0 : err.code) || ErrorCode3.GENERAL;
706
+ return {
707
+ errorCode,
708
+ errorMessage,
709
+ value: defaultValue,
710
+ reason: StandardResolutionReasons2.ERROR,
711
+ flagMetadata: Object.freeze(flagMetadata),
712
+ flagKey
713
+ };
714
+ }
715
+ };
716
+
173
717
  // src/open-feature.ts
174
718
  import {
175
719
  OpenFeatureCommonAPI,
@@ -180,13 +724,14 @@ import {
180
724
 
181
725
  // src/client/internal/open-feature-client.ts
182
726
  import {
183
- ErrorCode as ErrorCode2,
727
+ ErrorCode as ErrorCode4,
184
728
  ProviderFatalError,
185
729
  ProviderNotReadyError,
186
730
  SafeLogger,
187
- StandardResolutionReasons as StandardResolutionReasons2,
731
+ StandardResolutionReasons as StandardResolutionReasons3,
188
732
  instantiateErrorByErrorCode,
189
- statusMatchesEvent
733
+ statusMatchesEvent,
734
+ MapHookData
190
735
  } from "@openfeature/core";
191
736
  var OpenFeatureClient = class {
192
737
  constructor(providerAccessor, providerStatusAccessor, emitterAccessor, apiContextAccessor, apiHooksAccessor, transactionContextAccessor, globalLogger, options, context = {}) {
@@ -343,18 +888,21 @@ var OpenFeatureClient = class {
343
888
  ];
344
889
  const allHooksReversed = [...allHooks].reverse();
345
890
  const mergedContext = this.mergeContexts(invocationContext);
346
- const hookContext = {
347
- flagKey,
348
- defaultValue,
349
- flagValueType: flagType,
350
- clientMetadata: this.metadata,
351
- providerMetadata: this._provider.metadata,
352
- context: mergedContext,
353
- logger: this._logger
354
- };
891
+ const hookContexts = allHooksReversed.map(
892
+ () => Object.freeze({
893
+ flagKey,
894
+ defaultValue,
895
+ flagValueType: flagType,
896
+ clientMetadata: this.metadata,
897
+ providerMetadata: this._provider.metadata,
898
+ context: mergedContext,
899
+ logger: this._logger,
900
+ hookData: new MapHookData()
901
+ })
902
+ );
355
903
  let evaluationDetails;
356
904
  try {
357
- const frozenContext = yield this.beforeHooks(allHooks, hookContext, options);
905
+ const frozenContext = yield this.beforeHooks(allHooks, hookContexts, mergedContext, options);
358
906
  this.shortCircuitIfNotReady();
359
907
  const resolution = yield resolver.call(this._provider, flagKey, defaultValue, frozenContext, this._logger);
360
908
  const resolutionDetails = __spreadProps(__spreadValues({}, resolution), {
@@ -363,43 +911,54 @@ var OpenFeatureClient = class {
363
911
  });
364
912
  if (resolutionDetails.errorCode) {
365
913
  const err = instantiateErrorByErrorCode(resolutionDetails.errorCode, resolutionDetails.errorMessage);
366
- yield this.errorHooks(allHooksReversed, hookContext, err, options);
914
+ yield this.errorHooks(allHooksReversed, hookContexts, err, options);
367
915
  evaluationDetails = this.getErrorEvaluationDetails(flagKey, defaultValue, err, resolutionDetails.flagMetadata);
368
916
  } else {
369
- yield this.afterHooks(allHooksReversed, hookContext, resolutionDetails, options);
917
+ yield this.afterHooks(allHooksReversed, hookContexts, resolutionDetails, options);
370
918
  evaluationDetails = resolutionDetails;
371
919
  }
372
920
  } catch (err) {
373
- yield this.errorHooks(allHooksReversed, hookContext, err, options);
921
+ yield this.errorHooks(allHooksReversed, hookContexts, err, options);
374
922
  evaluationDetails = this.getErrorEvaluationDetails(flagKey, defaultValue, err);
375
923
  }
376
- yield this.finallyHooks(allHooksReversed, hookContext, evaluationDetails, options);
924
+ yield this.finallyHooks(allHooksReversed, hookContexts, evaluationDetails, options);
377
925
  return evaluationDetails;
378
926
  });
379
927
  }
380
- beforeHooks(hooks, hookContext, options) {
928
+ beforeHooks(hooks, hookContexts, mergedContext, options) {
381
929
  return __async(this, null, function* () {
382
930
  var _a;
383
- for (const hook of hooks) {
384
- Object.freeze(hookContext);
385
- 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))));
931
+ let accumulatedContext = mergedContext;
932
+ for (const [index, hook] of hooks.entries()) {
933
+ const hookContextIndex = hooks.length - 1 - index;
934
+ const hookContext = hookContexts[hookContextIndex];
935
+ Object.assign(hookContext.context, accumulatedContext);
936
+ const hookResult = yield (_a = hook == null ? void 0 : hook.before) == null ? void 0 : _a.call(hook, hookContext, Object.freeze(options.hookHints));
937
+ if (hookResult) {
938
+ accumulatedContext = __spreadValues(__spreadValues({}, accumulatedContext), hookResult);
939
+ for (let i = 0; i < hooks.length; i++) {
940
+ Object.assign(hookContexts[hookContextIndex].context, accumulatedContext);
941
+ }
942
+ }
386
943
  }
387
- return Object.freeze(hookContext.context);
944
+ return Object.freeze(accumulatedContext);
388
945
  });
389
946
  }
390
- afterHooks(hooks, hookContext, evaluationDetails, options) {
947
+ afterHooks(hooks, hookContexts, evaluationDetails, options) {
391
948
  return __async(this, null, function* () {
392
949
  var _a;
393
- for (const hook of hooks) {
950
+ for (const [index, hook] of hooks.entries()) {
951
+ const hookContext = hookContexts[index];
394
952
  yield (_a = hook == null ? void 0 : hook.after) == null ? void 0 : _a.call(hook, hookContext, evaluationDetails, options.hookHints);
395
953
  }
396
954
  });
397
955
  }
398
- errorHooks(hooks, hookContext, err, options) {
956
+ errorHooks(hooks, hookContexts, err, options) {
399
957
  return __async(this, null, function* () {
400
958
  var _a;
401
- for (const hook of hooks) {
959
+ for (const [index, hook] of hooks.entries()) {
402
960
  try {
961
+ const hookContext = hookContexts[index];
403
962
  yield (_a = hook == null ? void 0 : hook.error) == null ? void 0 : _a.call(hook, hookContext, err, options.hookHints);
404
963
  } catch (err2) {
405
964
  this._logger.error(`Unhandled error during 'error' hook: ${err2}`);
@@ -411,11 +970,12 @@ var OpenFeatureClient = class {
411
970
  }
412
971
  });
413
972
  }
414
- finallyHooks(hooks, hookContext, evaluationDetails, options) {
973
+ finallyHooks(hooks, hookContexts, evaluationDetails, options) {
415
974
  return __async(this, null, function* () {
416
975
  var _a;
417
- for (const hook of hooks) {
976
+ for (const [index, hook] of hooks.entries()) {
418
977
  try {
978
+ const hookContext = hookContexts[index];
419
979
  yield (_a = hook == null ? void 0 : hook.finally) == null ? void 0 : _a.call(hook, hookContext, evaluationDetails, options.hookHints);
420
980
  } catch (err) {
421
981
  this._logger.error(`Unhandled error during 'finally' hook: ${err}`);
@@ -448,35 +1008,18 @@ var OpenFeatureClient = class {
448
1008
  }
449
1009
  getErrorEvaluationDetails(flagKey, defaultValue, err, flagMetadata = {}) {
450
1010
  const errorMessage = err == null ? void 0 : err.message;
451
- const errorCode = (err == null ? void 0 : err.code) || ErrorCode2.GENERAL;
1011
+ const errorCode = (err == null ? void 0 : err.code) || ErrorCode4.GENERAL;
452
1012
  return {
453
1013
  errorCode,
454
1014
  errorMessage,
455
1015
  value: defaultValue,
456
- reason: StandardResolutionReasons2.ERROR,
1016
+ reason: StandardResolutionReasons3.ERROR,
457
1017
  flagMetadata: Object.freeze(flagMetadata),
458
1018
  flagKey
459
1019
  };
460
1020
  }
461
1021
  };
462
1022
 
463
- // src/events/open-feature-event-emitter.ts
464
- import { GenericEventEmitter } from "@openfeature/core";
465
- import { EventEmitter } from "node:events";
466
- var OpenFeatureEventEmitter = class extends GenericEventEmitter {
467
- constructor() {
468
- super();
469
- this.eventEmitter = new EventEmitter({ captureRejections: true });
470
- this.eventEmitter.on("error", (err) => {
471
- var _a;
472
- (_a = this._logger) == null ? void 0 : _a.error("Error running event handler:", err);
473
- });
474
- }
475
- };
476
-
477
- // src/events/events.ts
478
- import { ServerProviderEvents } from "@openfeature/core";
479
-
480
1023
  // src/transaction-context/no-op-transaction-context-propagator.ts
481
1024
  var NoopTransactionContextPropagator = class {
482
1025
  getTransactionContext() {
@@ -621,14 +1164,23 @@ var OpenFeature = OpenFeatureAPI.getInstance();
621
1164
  // src/index.ts
622
1165
  export * from "@openfeature/core";
623
1166
  export {
1167
+ AggregateError,
624
1168
  AsyncLocalStorageTransactionContextPropagator,
1169
+ BaseEvaluationStrategy,
1170
+ ComparisonStrategy,
1171
+ ErrorWithCode,
1172
+ FirstMatchStrategy,
1173
+ FirstSuccessfulStrategy,
625
1174
  InMemoryProvider,
1175
+ MultiProvider,
626
1176
  NOOP_PROVIDER,
627
1177
  NOOP_TRANSACTION_CONTEXT_PROPAGATOR,
628
1178
  OpenFeature,
629
1179
  OpenFeatureAPI,
630
1180
  OpenFeatureEventEmitter,
631
1181
  ServerProviderEvents as ProviderEvents,
632
- ServerProviderStatus as ProviderStatus
1182
+ ServerProviderStatus as ProviderStatus,
1183
+ constructAggregateError,
1184
+ throwAggregateErrorFromPromiseResults
633
1185
  };
634
1186
  //# sourceMappingURL=index.js.map