@featurevisor/sdk 1.16.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/src/instance.ts CHANGED
@@ -17,6 +17,8 @@ import {
17
17
  RuleKey,
18
18
  VariableKey,
19
19
  VariableSchema,
20
+ Force,
21
+ Required,
20
22
  } from "@featurevisor/types";
21
23
 
22
24
  import { createLogger, Logger, LogLevel } from "./logger";
@@ -85,6 +87,7 @@ export type DatafileFetchHandler = (datafileUrl: string) => Promise<DatafileCont
85
87
  export enum EvaluationReason {
86
88
  NOT_FOUND = "not_found",
87
89
  NO_VARIATIONS = "no_variations",
90
+ NO_MATCH = "no_match",
88
91
  DISABLED = "disabled",
89
92
  REQUIRED = "required",
90
93
  OUT_OF_RANGE = "out_of_range",
@@ -104,11 +107,15 @@ export interface Evaluation {
104
107
  reason: EvaluationReason;
105
108
 
106
109
  // common
110
+ bucketKey?: BucketKey;
107
111
  bucketValue?: BucketValue;
108
112
  ruleKey?: RuleKey;
109
113
  error?: Error;
110
114
  enabled?: boolean;
111
115
  traffic?: Traffic;
116
+ forceIndex?: number;
117
+ force?: Force;
118
+ required?: Required[];
112
119
  sticky?: OverrideFeature;
113
120
  initial?: OverrideFeature;
114
121
 
@@ -368,16 +375,27 @@ export class FeaturevisorInstance {
368
375
  return result;
369
376
  }
370
377
 
371
- private getBucketValue(feature: Feature, context: Context): BucketValue {
378
+ private getBucketValue(
379
+ feature: Feature,
380
+ context: Context,
381
+ ): { bucketKey: BucketKey; bucketValue: BucketValue } {
372
382
  const bucketKey = this.getBucketKey(feature, context);
373
383
 
374
384
  const value = getBucketedNumber(bucketKey);
375
385
 
376
386
  if (this.configureBucketValue) {
377
- return this.configureBucketValue(feature, context, value);
387
+ const configuredValue = this.configureBucketValue(feature, context, value);
388
+
389
+ return {
390
+ bucketKey,
391
+ bucketValue: configuredValue,
392
+ };
378
393
  }
379
394
 
380
- return value;
395
+ return {
396
+ bucketKey,
397
+ bucketValue: value,
398
+ };
381
399
  }
382
400
 
383
401
  /**
@@ -470,8 +488,8 @@ export class FeaturevisorInstance {
470
488
  evaluation = {
471
489
  featureKey: key,
472
490
  reason: EvaluationReason.STICKY,
473
- enabled: this.stickyFeatures[key].enabled,
474
491
  sticky: this.stickyFeatures[key],
492
+ enabled: this.stickyFeatures[key].enabled,
475
493
  };
476
494
 
477
495
  this.logger.debug("using sticky enabled", evaluation);
@@ -490,8 +508,8 @@ export class FeaturevisorInstance {
490
508
  evaluation = {
491
509
  featureKey: key,
492
510
  reason: EvaluationReason.INITIAL,
493
- enabled: this.initialFeatures[key].enabled,
494
511
  initial: this.initialFeatures[key],
512
+ enabled: this.initialFeatures[key].enabled,
495
513
  };
496
514
 
497
515
  this.logger.debug("using initial enabled", evaluation);
@@ -521,12 +539,19 @@ export class FeaturevisorInstance {
521
539
  const finalContext = this.interceptContext ? this.interceptContext(context) : context;
522
540
 
523
541
  // forced
524
- const force = findForceFromFeature(feature, context, this.datafileReader, this.logger);
542
+ const { force, forceIndex } = findForceFromFeature(
543
+ feature,
544
+ context,
545
+ this.datafileReader,
546
+ this.logger,
547
+ );
525
548
 
526
549
  if (force && typeof force.enabled !== "undefined") {
527
550
  evaluation = {
528
551
  featureKey: feature.key,
529
552
  reason: EvaluationReason.FORCED,
553
+ forceIndex,
554
+ force,
530
555
  enabled: force.enabled,
531
556
  };
532
557
 
@@ -567,6 +592,7 @@ export class FeaturevisorInstance {
567
592
  evaluation = {
568
593
  featureKey: feature.key,
569
594
  reason: EvaluationReason.REQUIRED,
595
+ required: feature.required,
570
596
  enabled: requiredFeaturesAreEnabled,
571
597
  };
572
598
 
@@ -577,7 +603,7 @@ export class FeaturevisorInstance {
577
603
  }
578
604
 
579
605
  // bucketing
580
- const bucketValue = this.getBucketValue(feature, finalContext);
606
+ const { bucketKey, bucketValue } = this.getBucketValue(feature, finalContext);
581
607
 
582
608
  const matchedTraffic = getMatchedTraffic(
583
609
  feature.traffic,
@@ -598,9 +624,12 @@ export class FeaturevisorInstance {
598
624
  evaluation = {
599
625
  featureKey: feature.key,
600
626
  reason: EvaluationReason.ALLOCATED,
627
+ bucketKey,
628
+ bucketValue,
629
+ ruleKey: matchedTraffic.key,
630
+ traffic: matchedTraffic,
601
631
  enabled:
602
632
  typeof matchedTraffic.enabled === "undefined" ? true : matchedTraffic.enabled,
603
- bucketValue,
604
633
  };
605
634
 
606
635
  this.logger.debug("matched", evaluation);
@@ -612,8 +641,9 @@ export class FeaturevisorInstance {
612
641
  evaluation = {
613
642
  featureKey: feature.key,
614
643
  reason: EvaluationReason.OUT_OF_RANGE,
615
- enabled: false,
644
+ bucketKey,
616
645
  bucketValue,
646
+ enabled: false,
617
647
  };
618
648
 
619
649
  this.logger.debug("not matched", evaluation);
@@ -626,10 +656,11 @@ export class FeaturevisorInstance {
626
656
  evaluation = {
627
657
  featureKey: feature.key,
628
658
  reason: EvaluationReason.OVERRIDE,
629
- enabled: matchedTraffic.enabled,
659
+ bucketKey,
630
660
  bucketValue,
631
661
  ruleKey: matchedTraffic.key,
632
662
  traffic: matchedTraffic,
663
+ enabled: matchedTraffic.enabled,
633
664
  };
634
665
 
635
666
  this.logger.debug("override from rule", evaluation);
@@ -642,10 +673,11 @@ export class FeaturevisorInstance {
642
673
  evaluation = {
643
674
  featureKey: feature.key,
644
675
  reason: EvaluationReason.RULE,
645
- enabled: true,
676
+ bucketKey,
646
677
  bucketValue,
647
678
  ruleKey: matchedTraffic.key,
648
679
  traffic: matchedTraffic,
680
+ enabled: true,
649
681
  };
650
682
 
651
683
  this.logger.debug("matched traffic", evaluation);
@@ -657,9 +689,10 @@ export class FeaturevisorInstance {
657
689
  // nothing matched
658
690
  evaluation = {
659
691
  featureKey: feature.key,
660
- reason: EvaluationReason.ERROR,
661
- enabled: false,
692
+ reason: EvaluationReason.NO_MATCH,
693
+ bucketKey,
662
694
  bucketValue,
695
+ enabled: false,
663
696
  };
664
697
 
665
698
  this.logger.debug("nothing matched", evaluation);
@@ -779,7 +812,12 @@ export class FeaturevisorInstance {
779
812
  const finalContext = this.interceptContext ? this.interceptContext(context) : context;
780
813
 
781
814
  // forced
782
- const force = findForceFromFeature(feature, context, this.datafileReader, this.logger);
815
+ const { force, forceIndex } = findForceFromFeature(
816
+ feature,
817
+ context,
818
+ this.datafileReader,
819
+ this.logger,
820
+ );
783
821
 
784
822
  if (force && force.variation) {
785
823
  const variation = feature.variations.find((v) => v.value === force.variation);
@@ -788,6 +826,8 @@ export class FeaturevisorInstance {
788
826
  evaluation = {
789
827
  featureKey: feature.key,
790
828
  reason: EvaluationReason.FORCED,
829
+ forceIndex,
830
+ force,
791
831
  variation,
792
832
  };
793
833
 
@@ -798,7 +838,7 @@ export class FeaturevisorInstance {
798
838
  }
799
839
 
800
840
  // bucketing
801
- const bucketValue = this.getBucketValue(feature, finalContext);
841
+ const { bucketKey, bucketValue } = this.getBucketValue(feature, finalContext);
802
842
 
803
843
  const { matchedTraffic, matchedAllocation } = getMatchedTrafficAndAllocation(
804
844
  feature.traffic,
@@ -817,9 +857,11 @@ export class FeaturevisorInstance {
817
857
  evaluation = {
818
858
  featureKey: feature.key,
819
859
  reason: EvaluationReason.RULE,
820
- variation,
860
+ bucketKey,
821
861
  bucketValue,
822
862
  ruleKey: matchedTraffic.key,
863
+ traffic: matchedTraffic,
864
+ variation,
823
865
  };
824
866
 
825
867
  this.logger.debug("override from rule", evaluation);
@@ -836,7 +878,10 @@ export class FeaturevisorInstance {
836
878
  evaluation = {
837
879
  featureKey: feature.key,
838
880
  reason: EvaluationReason.ALLOCATED,
881
+ bucketKey,
839
882
  bucketValue,
883
+ ruleKey: matchedTraffic.key,
884
+ traffic: matchedTraffic,
840
885
  variation,
841
886
  };
842
887
 
@@ -850,7 +895,8 @@ export class FeaturevisorInstance {
850
895
  // nothing matched
851
896
  evaluation = {
852
897
  featureKey: feature.key,
853
- reason: EvaluationReason.ERROR,
898
+ reason: EvaluationReason.NO_MATCH,
899
+ bucketKey,
854
900
  bucketValue,
855
901
  };
856
902
 
@@ -1046,12 +1092,19 @@ export class FeaturevisorInstance {
1046
1092
  const finalContext = this.interceptContext ? this.interceptContext(context) : context;
1047
1093
 
1048
1094
  // forced
1049
- const force = findForceFromFeature(feature, context, this.datafileReader, this.logger);
1095
+ const { force, forceIndex } = findForceFromFeature(
1096
+ feature,
1097
+ context,
1098
+ this.datafileReader,
1099
+ this.logger,
1100
+ );
1050
1101
 
1051
1102
  if (force && force.variables && typeof force.variables[variableKey] !== "undefined") {
1052
1103
  evaluation = {
1053
1104
  featureKey: feature.key,
1054
1105
  reason: EvaluationReason.FORCED,
1106
+ forceIndex,
1107
+ force,
1055
1108
  variableKey,
1056
1109
  variableSchema,
1057
1110
  variableValue: force.variables[variableKey],
@@ -1063,7 +1116,7 @@ export class FeaturevisorInstance {
1063
1116
  }
1064
1117
 
1065
1118
  // bucketing
1066
- const bucketValue = this.getBucketValue(feature, finalContext);
1119
+ const { bucketKey, bucketValue } = this.getBucketValue(feature, finalContext);
1067
1120
 
1068
1121
  const { matchedTraffic, matchedAllocation } = getMatchedTrafficAndAllocation(
1069
1122
  feature.traffic,
@@ -1082,11 +1135,13 @@ export class FeaturevisorInstance {
1082
1135
  evaluation = {
1083
1136
  featureKey: feature.key,
1084
1137
  reason: EvaluationReason.RULE,
1138
+ bucketKey,
1139
+ bucketValue,
1140
+ ruleKey: matchedTraffic.key,
1141
+ traffic: matchedTraffic,
1085
1142
  variableKey,
1086
1143
  variableSchema,
1087
1144
  variableValue: matchedTraffic.variables[variableKey],
1088
- bucketValue,
1089
- ruleKey: matchedTraffic.key,
1090
1145
  };
1091
1146
 
1092
1147
  this.logger.debug("override from rule", evaluation);
@@ -1136,11 +1191,13 @@ export class FeaturevisorInstance {
1136
1191
  evaluation = {
1137
1192
  featureKey: feature.key,
1138
1193
  reason: EvaluationReason.OVERRIDE,
1194
+ bucketKey,
1195
+ bucketValue,
1196
+ ruleKey: matchedTraffic.key,
1197
+ traffic: matchedTraffic,
1139
1198
  variableKey,
1140
1199
  variableSchema,
1141
1200
  variableValue: override.value,
1142
- bucketValue,
1143
- ruleKey: matchedTraffic.key,
1144
1201
  };
1145
1202
 
1146
1203
  this.logger.debug("variable override", evaluation);
@@ -1153,11 +1210,13 @@ export class FeaturevisorInstance {
1153
1210
  evaluation = {
1154
1211
  featureKey: feature.key,
1155
1212
  reason: EvaluationReason.ALLOCATED,
1213
+ bucketKey,
1214
+ bucketValue,
1215
+ ruleKey: matchedTraffic.key,
1216
+ traffic: matchedTraffic,
1156
1217
  variableKey,
1157
1218
  variableSchema,
1158
1219
  variableValue: variableFromVariation.value,
1159
- bucketValue,
1160
- ruleKey: matchedTraffic.key,
1161
1220
  };
1162
1221
 
1163
1222
  this.logger.debug("allocated variable", evaluation);
@@ -1173,10 +1232,11 @@ export class FeaturevisorInstance {
1173
1232
  evaluation = {
1174
1233
  featureKey: feature.key,
1175
1234
  reason: EvaluationReason.DEFAULTED,
1235
+ bucketKey,
1236
+ bucketValue,
1176
1237
  variableKey,
1177
1238
  variableSchema,
1178
1239
  variableValue: variableSchema.defaultValue,
1179
- bucketValue,
1180
1240
  };
1181
1241
 
1182
1242
  this.logger.debug("using default value", evaluation);