@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/.eslintcache +1 -1
- package/CHANGELOG.md +22 -0
- package/coverage/clover.xml +401 -393
- package/coverage/coverage-final.json +2 -2
- package/coverage/lcov-report/bucket.ts.html +1 -1
- package/coverage/lcov-report/conditions.ts.html +1 -1
- package/coverage/lcov-report/datafileReader.ts.html +1 -1
- package/coverage/lcov-report/emitter.ts.html +1 -1
- package/coverage/lcov-report/feature.ts.html +88 -22
- package/coverage/lcov-report/index.html +23 -23
- package/coverage/lcov-report/instance.ts.html +212 -32
- package/coverage/lcov-report/logger.ts.html +1 -1
- package/coverage/lcov-report/segments.ts.html +1 -1
- package/coverage/lcov.info +665 -655
- package/dist/index.js +1 -1
- package/dist/index.js.gz +0 -0
- package/dist/index.js.map +1 -1
- package/lib/feature.d.ts +5 -1
- package/lib/feature.js +19 -8
- package/lib/feature.js.map +1 -1
- package/lib/instance.d.ts +6 -1
- package/lib/instance.js +62 -27
- package/lib/instance.js.map +1 -1
- package/package.json +2 -2
- package/src/feature.ts +31 -9
- package/src/instance.ts +86 -26
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(
|
|
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
|
-
|
|
387
|
+
const configuredValue = this.configureBucketValue(feature, context, value);
|
|
388
|
+
|
|
389
|
+
return {
|
|
390
|
+
bucketKey,
|
|
391
|
+
bucketValue: configuredValue,
|
|
392
|
+
};
|
|
378
393
|
}
|
|
379
394
|
|
|
380
|
-
return
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
661
|
-
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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(
|
|
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);
|