@featurevisor/sdk 1.29.2 → 1.30.1
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/CHANGELOG.md +22 -0
- package/coverage/clover.xml +436 -436
- package/coverage/coverage-final.json +4 -3
- package/coverage/lcov-report/bucket.ts.html +387 -9
- 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/evaluate.ts.html +2401 -0
- package/coverage/lcov-report/feature.ts.html +27 -9
- package/coverage/lcov-report/index.html +51 -36
- package/coverage/lcov-report/instance.ts.html +80 -2396
- package/coverage/lcov-report/logger.ts.html +1 -1
- package/coverage/lcov-report/segments.ts.html +1 -1
- package/coverage/lcov.info +782 -759
- package/dist/bucket.d.ts +28 -0
- package/dist/evaluate.d.ts +59 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.gz +0 -0
- package/dist/index.mjs.map +1 -1
- package/dist/instance.d.ts +3 -44
- package/lib/bucket.d.ts +28 -0
- package/lib/evaluate.d.ts +59 -0
- package/lib/index.d.ts +1 -0
- package/lib/instance.d.ts +3 -44
- package/package.json +2 -2
- package/src/bucket.ts +126 -0
- package/src/evaluate.ts +772 -0
- package/src/feature.ts +7 -1
- package/src/index.ts +1 -0
- package/src/instance.ts +45 -817
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Feature, FeatureKey, Context, BucketKey, BucketValue, RuleKey, Traffic, Force, Required, OverrideFeature, Variation, VariationValue, VariableKey, VariableValue, VariableSchema, StickyFeatures, InitialFeatures } from "@featurevisor/types";
|
|
2
|
+
import { Logger } from "./logger";
|
|
3
|
+
import { DatafileReader } from "./datafileReader";
|
|
4
|
+
import { ConfigureBucketKey, ConfigureBucketValue } from "./bucket";
|
|
5
|
+
import type { Statuses, InterceptContext } from "./instance";
|
|
6
|
+
export declare enum EvaluationReason {
|
|
7
|
+
NOT_FOUND = "not_found",
|
|
8
|
+
NO_VARIATIONS = "no_variations",
|
|
9
|
+
NO_MATCH = "no_match",
|
|
10
|
+
DISABLED = "disabled",
|
|
11
|
+
REQUIRED = "required",
|
|
12
|
+
OUT_OF_RANGE = "out_of_range",
|
|
13
|
+
FORCED = "forced",
|
|
14
|
+
INITIAL = "initial",
|
|
15
|
+
STICKY = "sticky",
|
|
16
|
+
RULE = "rule",
|
|
17
|
+
ALLOCATED = "allocated",
|
|
18
|
+
DEFAULTED = "defaulted",
|
|
19
|
+
OVERRIDE = "override",
|
|
20
|
+
ERROR = "error"
|
|
21
|
+
}
|
|
22
|
+
type EvaluationType = "flag" | "variation" | "variable";
|
|
23
|
+
export interface Evaluation {
|
|
24
|
+
featureKey: FeatureKey;
|
|
25
|
+
reason: EvaluationReason;
|
|
26
|
+
bucketKey?: BucketKey;
|
|
27
|
+
bucketValue?: BucketValue;
|
|
28
|
+
ruleKey?: RuleKey;
|
|
29
|
+
error?: Error;
|
|
30
|
+
enabled?: boolean;
|
|
31
|
+
traffic?: Traffic;
|
|
32
|
+
forceIndex?: number;
|
|
33
|
+
force?: Force;
|
|
34
|
+
required?: Required[];
|
|
35
|
+
sticky?: OverrideFeature;
|
|
36
|
+
initial?: OverrideFeature;
|
|
37
|
+
variation?: Variation;
|
|
38
|
+
variationValue?: VariationValue;
|
|
39
|
+
variableKey?: VariableKey;
|
|
40
|
+
variableValue?: VariableValue;
|
|
41
|
+
variableSchema?: VariableSchema;
|
|
42
|
+
}
|
|
43
|
+
export interface EvaluateOptions {
|
|
44
|
+
type: EvaluationType;
|
|
45
|
+
featureKey: FeatureKey | Feature;
|
|
46
|
+
variableKey?: VariableKey;
|
|
47
|
+
context: Context;
|
|
48
|
+
logger: Logger;
|
|
49
|
+
datafileReader: DatafileReader;
|
|
50
|
+
statuses?: Statuses;
|
|
51
|
+
interceptContext?: InterceptContext;
|
|
52
|
+
stickyFeatures?: StickyFeatures;
|
|
53
|
+
initialFeatures?: InitialFeatures;
|
|
54
|
+
bucketKeySeparator?: string;
|
|
55
|
+
configureBucketKey?: ConfigureBucketKey;
|
|
56
|
+
configureBucketValue?: ConfigureBucketValue;
|
|
57
|
+
}
|
|
58
|
+
export declare function evaluate(options: EvaluateOptions): Evaluation;
|
|
59
|
+
export {};
|
package/lib/index.d.ts
CHANGED
package/lib/instance.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Context,
|
|
1
|
+
import { Context, DatafileContent, Feature, FeatureKey, InitialFeatures, StickyFeatures, VariableType, VariableValue, VariationValue, VariableKey } from "@featurevisor/types";
|
|
2
2
|
import { Logger, LogLevel } from "./logger";
|
|
3
3
|
import { Emitter } from "./emitter";
|
|
4
|
+
import { ConfigureBucketKey, ConfigureBucketValue } from "./bucket";
|
|
5
|
+
import { Evaluation } from "./evaluate";
|
|
4
6
|
export type ReadyCallback = () => void;
|
|
5
7
|
export type ActivationCallback = (featureName: string, variation: VariationValue, context: Context, captureContext: Context) => void;
|
|
6
|
-
export type ConfigureBucketKey = (feature: any, context: any, bucketKey: BucketKey) => BucketKey;
|
|
7
|
-
export type ConfigureBucketValue = (feature: any, context: any, bucketValue: BucketValue) => BucketValue;
|
|
8
8
|
export interface Statuses {
|
|
9
9
|
ready: boolean;
|
|
10
10
|
refreshInProgress: boolean;
|
|
@@ -28,42 +28,6 @@ export interface InstanceOptions {
|
|
|
28
28
|
stickyFeatures?: StickyFeatures;
|
|
29
29
|
}
|
|
30
30
|
export type DatafileFetchHandler = (datafileUrl: string) => Promise<DatafileContent>;
|
|
31
|
-
export declare enum EvaluationReason {
|
|
32
|
-
NOT_FOUND = "not_found",
|
|
33
|
-
NO_VARIATIONS = "no_variations",
|
|
34
|
-
NO_MATCH = "no_match",
|
|
35
|
-
DISABLED = "disabled",
|
|
36
|
-
REQUIRED = "required",
|
|
37
|
-
OUT_OF_RANGE = "out_of_range",
|
|
38
|
-
FORCED = "forced",
|
|
39
|
-
INITIAL = "initial",
|
|
40
|
-
STICKY = "sticky",
|
|
41
|
-
RULE = "rule",
|
|
42
|
-
ALLOCATED = "allocated",
|
|
43
|
-
DEFAULTED = "defaulted",
|
|
44
|
-
OVERRIDE = "override",
|
|
45
|
-
ERROR = "error"
|
|
46
|
-
}
|
|
47
|
-
export interface Evaluation {
|
|
48
|
-
featureKey: FeatureKey;
|
|
49
|
-
reason: EvaluationReason;
|
|
50
|
-
bucketKey?: BucketKey;
|
|
51
|
-
bucketValue?: BucketValue;
|
|
52
|
-
ruleKey?: RuleKey;
|
|
53
|
-
error?: Error;
|
|
54
|
-
enabled?: boolean;
|
|
55
|
-
traffic?: Traffic;
|
|
56
|
-
forceIndex?: number;
|
|
57
|
-
force?: Force;
|
|
58
|
-
required?: Required[];
|
|
59
|
-
sticky?: OverrideFeature;
|
|
60
|
-
initial?: OverrideFeature;
|
|
61
|
-
variation?: Variation;
|
|
62
|
-
variationValue?: VariationValue;
|
|
63
|
-
variableKey?: VariableKey;
|
|
64
|
-
variableValue?: VariableValue;
|
|
65
|
-
variableSchema?: VariableSchema;
|
|
66
|
-
}
|
|
67
31
|
type FieldType = string | VariableType;
|
|
68
32
|
type ValueType = VariableValue;
|
|
69
33
|
export declare function getValueByType(value: ValueType, fieldType: FieldType): ValueType;
|
|
@@ -94,11 +58,6 @@ export declare class FeaturevisorInstance {
|
|
|
94
58
|
setStickyFeatures(stickyFeatures: StickyFeatures | undefined): void;
|
|
95
59
|
getRevision(): string;
|
|
96
60
|
getFeature(featureKey: string | Feature): Feature | undefined;
|
|
97
|
-
/**
|
|
98
|
-
* Bucketing
|
|
99
|
-
*/
|
|
100
|
-
private getBucketKey;
|
|
101
|
-
private getBucketValue;
|
|
102
61
|
/**
|
|
103
62
|
* Statuses
|
|
104
63
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@featurevisor/sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.30.1",
|
|
4
4
|
"description": "Featurevisor SDK for Node.js and the browser",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -53,5 +53,5 @@
|
|
|
53
53
|
"compare-versions": "^6.0.0-rc.1",
|
|
54
54
|
"murmurhash": "^2.0.1"
|
|
55
55
|
},
|
|
56
|
-
"gitHead": "
|
|
56
|
+
"gitHead": "8dc524c8a2a2ccb85661177e37ef1aff7cab8273"
|
|
57
57
|
}
|
package/src/bucket.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import * as murmurhash from "murmurhash";
|
|
2
|
+
import { Feature, BucketKey, BucketValue, Context, AttributeValue } from "@featurevisor/types";
|
|
2
3
|
|
|
4
|
+
import { Logger } from "./logger";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generic hashing
|
|
8
|
+
*/
|
|
3
9
|
const HASH_SEED = 1;
|
|
4
10
|
const MAX_HASH_VALUE = Math.pow(2, 32);
|
|
5
11
|
|
|
@@ -11,3 +17,123 @@ export function getBucketedNumber(bucketKey: string): number {
|
|
|
11
17
|
|
|
12
18
|
return Math.floor(ratio * MAX_BUCKETED_NUMBER);
|
|
13
19
|
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Feature specific bucketing
|
|
23
|
+
*/
|
|
24
|
+
export type ConfigureBucketKey = (
|
|
25
|
+
feature: Feature,
|
|
26
|
+
context: Context,
|
|
27
|
+
bucketKey: BucketKey,
|
|
28
|
+
) => BucketKey;
|
|
29
|
+
|
|
30
|
+
export interface BucketKeyOptions {
|
|
31
|
+
feature: Feature;
|
|
32
|
+
context: Context;
|
|
33
|
+
logger: Logger;
|
|
34
|
+
bucketKeySeparator?: string;
|
|
35
|
+
configureBucketKey?: ConfigureBucketKey;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function getBucketKey(options: BucketKeyOptions): BucketKey {
|
|
39
|
+
const { feature, context, logger, bucketKeySeparator = ".", configureBucketKey } = options;
|
|
40
|
+
|
|
41
|
+
const featureKey = feature.key;
|
|
42
|
+
|
|
43
|
+
let type;
|
|
44
|
+
let attributeKeys;
|
|
45
|
+
|
|
46
|
+
if (typeof feature.bucketBy === "string") {
|
|
47
|
+
type = "plain";
|
|
48
|
+
attributeKeys = [feature.bucketBy];
|
|
49
|
+
} else if (Array.isArray(feature.bucketBy)) {
|
|
50
|
+
type = "and";
|
|
51
|
+
attributeKeys = feature.bucketBy;
|
|
52
|
+
} else if (typeof feature.bucketBy === "object" && Array.isArray(feature.bucketBy.or)) {
|
|
53
|
+
type = "or";
|
|
54
|
+
attributeKeys = feature.bucketBy.or;
|
|
55
|
+
} else {
|
|
56
|
+
logger.error("invalid bucketBy", { featureKey, bucketBy: feature.bucketBy });
|
|
57
|
+
|
|
58
|
+
throw new Error("invalid bucketBy");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const bucketKey: AttributeValue[] = [];
|
|
62
|
+
|
|
63
|
+
attributeKeys.forEach((attributeKey) => {
|
|
64
|
+
const attributeValue = context[attributeKey];
|
|
65
|
+
|
|
66
|
+
if (typeof attributeValue === "undefined") {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (type === "plain" || type === "and") {
|
|
71
|
+
bucketKey.push(attributeValue);
|
|
72
|
+
} else {
|
|
73
|
+
// or
|
|
74
|
+
if (bucketKey.length === 0) {
|
|
75
|
+
bucketKey.push(attributeValue);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
bucketKey.push(featureKey);
|
|
81
|
+
|
|
82
|
+
const result = bucketKey.join(bucketKeySeparator);
|
|
83
|
+
|
|
84
|
+
if (configureBucketKey) {
|
|
85
|
+
return configureBucketKey(feature, context, result);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface Bucket {
|
|
92
|
+
bucketKey: BucketKey;
|
|
93
|
+
bucketValue: BucketValue;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export type ConfigureBucketValue = (
|
|
97
|
+
feature: Feature,
|
|
98
|
+
context: Context,
|
|
99
|
+
bucketValue: BucketValue,
|
|
100
|
+
) => BucketValue;
|
|
101
|
+
|
|
102
|
+
export interface BucketValueOptions {
|
|
103
|
+
// common with BucketKeyOptions
|
|
104
|
+
feature: Feature;
|
|
105
|
+
context: Context;
|
|
106
|
+
logger: Logger;
|
|
107
|
+
bucketKeySeparator?: string;
|
|
108
|
+
configureBucketKey?: ConfigureBucketKey;
|
|
109
|
+
|
|
110
|
+
// specific to BucketValueOptions
|
|
111
|
+
configureBucketValue?: ConfigureBucketValue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function getBucket(options: BucketValueOptions): Bucket {
|
|
115
|
+
const { feature, context, logger, bucketKeySeparator, configureBucketKey, configureBucketValue } =
|
|
116
|
+
options;
|
|
117
|
+
const bucketKey = getBucketKey({
|
|
118
|
+
feature,
|
|
119
|
+
context,
|
|
120
|
+
logger,
|
|
121
|
+
bucketKeySeparator,
|
|
122
|
+
configureBucketKey,
|
|
123
|
+
});
|
|
124
|
+
const value = getBucketedNumber(bucketKey);
|
|
125
|
+
|
|
126
|
+
if (configureBucketValue) {
|
|
127
|
+
const configuredValue = configureBucketValue(feature, context, value);
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
bucketKey,
|
|
131
|
+
bucketValue: configuredValue,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
bucketKey,
|
|
137
|
+
bucketValue: value,
|
|
138
|
+
};
|
|
139
|
+
}
|