@microsoft/feature-management 2.0.0-preview.3 → 2.0.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/commonjs/featureProvider.js +10 -6
- package/dist/commonjs/featureProvider.js.map +1 -1
- package/dist/commonjs/filter/TargetingFilter.js +4 -4
- package/dist/commonjs/filter/TargetingFilter.js.map +1 -1
- package/dist/commonjs/schema/validator.js +48 -48
- package/dist/commonjs/schema/validator.js.map +1 -1
- package/dist/commonjs/telemetry/featureEvaluationEvent.js +0 -26
- package/dist/commonjs/telemetry/featureEvaluationEvent.js.map +1 -1
- package/dist/commonjs/version.js +1 -1
- package/dist/commonjs/version.js.map +1 -1
- package/dist/esm/featureProvider.js +10 -6
- package/dist/esm/featureProvider.js.map +1 -1
- package/dist/esm/filter/TargetingFilter.js +4 -4
- package/dist/esm/filter/TargetingFilter.js.map +1 -1
- package/dist/esm/schema/validator.js +48 -48
- package/dist/esm/schema/validator.js.map +1 -1
- package/dist/esm/telemetry/featureEvaluationEvent.js +0 -26
- package/dist/esm/telemetry/featureEvaluationEvent.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/version.js.map +1 -1
- package/dist/umd/index.js +63 -84
- package/dist/umd/index.js.map +1 -1
- package/package.json +4 -1
- package/types/index.d.ts +1 -1
|
@@ -21,9 +21,11 @@ class ConfigurationMapFeatureFlagProvider {
|
|
|
21
21
|
}
|
|
22
22
|
async getFeatureFlags() {
|
|
23
23
|
const featureConfig = this.#configuration.get(model.FEATURE_MANAGEMENT_KEY);
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
const featureFlags = featureConfig?.[model.FEATURE_FLAGS_KEY] ?? [];
|
|
25
|
+
featureFlags.forEach(featureFlag => {
|
|
26
|
+
validator.validateFeatureFlag(featureFlag);
|
|
27
|
+
});
|
|
28
|
+
return featureFlags;
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
31
|
/**
|
|
@@ -41,9 +43,11 @@ class ConfigurationObjectFeatureFlagProvider {
|
|
|
41
43
|
return featureFlag;
|
|
42
44
|
}
|
|
43
45
|
async getFeatureFlags() {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
const featureFlags = this.#configuration[model.FEATURE_MANAGEMENT_KEY]?.[model.FEATURE_FLAGS_KEY] ?? [];
|
|
47
|
+
featureFlags.forEach(featureFlag => {
|
|
48
|
+
validator.validateFeatureFlag(featureFlag);
|
|
49
|
+
});
|
|
50
|
+
return featureFlags;
|
|
47
51
|
}
|
|
48
52
|
}
|
|
49
53
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"featureProvider.js","sources":["../../src/featureProvider.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { IGettable } from \"./gettable.js\";\nimport { FeatureFlag, FeatureManagementConfiguration, FEATURE_MANAGEMENT_KEY, FEATURE_FLAGS_KEY } from \"./schema/model.js\";\nimport { validateFeatureFlag } from \"./schema/validator.js\";\n\nexport interface IFeatureFlagProvider {\n /**\n * Get all feature flags.\n */\n getFeatureFlags(): Promise<FeatureFlag[]>;\n\n /**\n * Get a feature flag by name.\n * @param featureName The name of the feature flag.\n */\n getFeatureFlag(featureName: string): Promise<FeatureFlag | undefined>;\n}\n\n/**\n * A feature flag provider that uses a map-like configuration to provide feature flags.\n */\nexport class ConfigurationMapFeatureFlagProvider implements IFeatureFlagProvider {\n #configuration: IGettable;\n\n constructor(configuration: IGettable) {\n this.#configuration = configuration;\n }\n async getFeatureFlag(featureName: string): Promise<FeatureFlag | undefined> {\n const featureConfig = this.#configuration.get<FeatureManagementConfiguration>(FEATURE_MANAGEMENT_KEY);\n const featureFlag = featureConfig?.[FEATURE_FLAGS_KEY]?.findLast((feature) => feature.id === featureName);\n validateFeatureFlag(featureFlag);\n return featureFlag;\n }\n\n async getFeatureFlags(): Promise<FeatureFlag[]> {\n const featureConfig = this.#configuration.get<FeatureManagementConfiguration>(FEATURE_MANAGEMENT_KEY);\n const
|
|
1
|
+
{"version":3,"file":"featureProvider.js","sources":["../../src/featureProvider.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { IGettable } from \"./gettable.js\";\nimport { FeatureFlag, FeatureManagementConfiguration, FEATURE_MANAGEMENT_KEY, FEATURE_FLAGS_KEY } from \"./schema/model.js\";\nimport { validateFeatureFlag } from \"./schema/validator.js\";\n\nexport interface IFeatureFlagProvider {\n /**\n * Get all feature flags.\n */\n getFeatureFlags(): Promise<FeatureFlag[]>;\n\n /**\n * Get a feature flag by name.\n * @param featureName The name of the feature flag.\n */\n getFeatureFlag(featureName: string): Promise<FeatureFlag | undefined>;\n}\n\n/**\n * A feature flag provider that uses a map-like configuration to provide feature flags.\n */\nexport class ConfigurationMapFeatureFlagProvider implements IFeatureFlagProvider {\n #configuration: IGettable;\n\n constructor(configuration: IGettable) {\n this.#configuration = configuration;\n }\n async getFeatureFlag(featureName: string): Promise<FeatureFlag | undefined> {\n const featureConfig = this.#configuration.get<FeatureManagementConfiguration>(FEATURE_MANAGEMENT_KEY);\n const featureFlag = featureConfig?.[FEATURE_FLAGS_KEY]?.findLast((feature) => feature.id === featureName);\n validateFeatureFlag(featureFlag);\n return featureFlag;\n }\n\n async getFeatureFlags(): Promise<FeatureFlag[]> {\n const featureConfig = this.#configuration.get<FeatureManagementConfiguration>(FEATURE_MANAGEMENT_KEY);\n const featureFlags = featureConfig?.[FEATURE_FLAGS_KEY] ?? [];\n featureFlags.forEach(featureFlag => {\n validateFeatureFlag(featureFlag);\n });\n return featureFlags;\n }\n}\n\n/**\n * A feature flag provider that uses an object-like configuration to provide feature flags.\n */\nexport class ConfigurationObjectFeatureFlagProvider implements IFeatureFlagProvider {\n #configuration: Record<string, unknown>;\n\n constructor(configuration: Record<string, unknown>) {\n this.#configuration = configuration;\n }\n\n async getFeatureFlag(featureName: string): Promise<FeatureFlag | undefined> {\n const featureFlags = this.#configuration[FEATURE_MANAGEMENT_KEY]?.[FEATURE_FLAGS_KEY];\n const featureFlag = featureFlags?.findLast((feature: FeatureFlag) => feature.id === featureName);\n validateFeatureFlag(featureFlag);\n return featureFlag;\n }\n\n async getFeatureFlags(): Promise<FeatureFlag[]> {\n const featureFlags = this.#configuration[FEATURE_MANAGEMENT_KEY]?.[FEATURE_FLAGS_KEY] ?? [];\n featureFlags.forEach(featureFlag => {\n validateFeatureFlag(featureFlag);\n });\n return featureFlags;\n }\n}\n"],"names":["FEATURE_MANAGEMENT_KEY","FEATURE_FLAGS_KEY","validateFeatureFlag"],"mappings":";;;;;AAAA;AACA;AAmBA;;AAEG;MACU,mCAAmC,CAAA;AAC5C,IAAA,cAAc,CAAY;AAE1B,IAAA,WAAA,CAAY,aAAwB,EAAA;AAChC,QAAA,IAAI,CAAC,cAAc,GAAG,aAAa,CAAC;KACvC;IACD,MAAM,cAAc,CAAC,WAAmB,EAAA;QACpC,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAiCA,4BAAsB,CAAC,CAAC;QACtG,MAAM,WAAW,GAAG,aAAa,GAAGC,uBAAiB,CAAC,EAAE,QAAQ,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,EAAE,KAAK,WAAW,CAAC,CAAC;QAC1GC,6BAAmB,CAAC,WAAW,CAAC,CAAC;AACjC,QAAA,OAAO,WAAW,CAAC;KACtB;AAED,IAAA,MAAM,eAAe,GAAA;QACjB,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAiCF,4BAAsB,CAAC,CAAC;QACtG,MAAM,YAAY,GAAG,aAAa,GAAGC,uBAAiB,CAAC,IAAI,EAAE,CAAC;AAC9D,QAAA,YAAY,CAAC,OAAO,CAAC,WAAW,IAAG;YAC/BC,6BAAmB,CAAC,WAAW,CAAC,CAAC;AACrC,SAAC,CAAC,CAAC;AACH,QAAA,OAAO,YAAY,CAAC;KACvB;AACJ,CAAA;AAED;;AAEG;MACU,sCAAsC,CAAA;AAC/C,IAAA,cAAc,CAA0B;AAExC,IAAA,WAAA,CAAY,aAAsC,EAAA;AAC9C,QAAA,IAAI,CAAC,cAAc,GAAG,aAAa,CAAC;KACvC;IAED,MAAM,cAAc,CAAC,WAAmB,EAAA;AACpC,QAAA,MAAM,YAAY,GAAG,IAAI,CAAC,cAAc,CAACF,4BAAsB,CAAC,GAAGC,uBAAiB,CAAC,CAAC;AACtF,QAAA,MAAM,WAAW,GAAG,YAAY,EAAE,QAAQ,CAAC,CAAC,OAAoB,KAAK,OAAO,CAAC,EAAE,KAAK,WAAW,CAAC,CAAC;QACjGC,6BAAmB,CAAC,WAAW,CAAC,CAAC;AACjC,QAAA,OAAO,WAAW,CAAC;KACtB;AAED,IAAA,MAAM,eAAe,GAAA;AACjB,QAAA,MAAM,YAAY,GAAG,IAAI,CAAC,cAAc,CAACF,4BAAsB,CAAC,GAAGC,uBAAiB,CAAC,IAAI,EAAE,CAAC;AAC5F,QAAA,YAAY,CAAC,OAAO,CAAC,WAAW,IAAG;YAC/BC,6BAAmB,CAAC,WAAW,CAAC,CAAC;AACrC,SAAC,CAAC,CAAC;AACH,QAAA,OAAO,YAAY,CAAC;KACvB;AACJ;;;;;"}
|
|
@@ -8,7 +8,7 @@ class TargetingFilter {
|
|
|
8
8
|
name = "Microsoft.Targeting";
|
|
9
9
|
async evaluate(context, appContext) {
|
|
10
10
|
const { featureName, parameters } = context;
|
|
11
|
-
TargetingFilter.#validateParameters(parameters);
|
|
11
|
+
TargetingFilter.#validateParameters(featureName, parameters);
|
|
12
12
|
if (appContext === undefined) {
|
|
13
13
|
throw new Error("The app context is required for targeting filter.");
|
|
14
14
|
}
|
|
@@ -51,15 +51,15 @@ class TargetingFilter {
|
|
|
51
51
|
const hint = featureName;
|
|
52
52
|
return targetingEvaluator.isTargetedPercentile(appContext?.userId, hint, 0, parameters.Audience.DefaultRolloutPercentage);
|
|
53
53
|
}
|
|
54
|
-
static #validateParameters(parameters) {
|
|
54
|
+
static #validateParameters(featureName, parameters) {
|
|
55
55
|
if (parameters.Audience.DefaultRolloutPercentage < 0 || parameters.Audience.DefaultRolloutPercentage > 100) {
|
|
56
|
-
throw new Error(
|
|
56
|
+
throw new Error(`Invalid feature flag: ${featureName}. Audience.DefaultRolloutPercentage must be a number between 0 and 100.`);
|
|
57
57
|
}
|
|
58
58
|
// validate RolloutPercentage for each group
|
|
59
59
|
if (parameters.Audience.Groups !== undefined) {
|
|
60
60
|
for (const group of parameters.Audience.Groups) {
|
|
61
61
|
if (group.RolloutPercentage < 0 || group.RolloutPercentage > 100) {
|
|
62
|
-
throw new Error(`RolloutPercentage of group ${group.Name} must be a number between 0 and 100.`);
|
|
62
|
+
throw new Error(`Invalid feature flag: ${featureName}. RolloutPercentage of group ${group.Name} must be a number between 0 and 100.`);
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TargetingFilter.js","sources":["../../../src/filter/TargetingFilter.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { IFeatureFilter } from \"./FeatureFilter.js\";\nimport { isTargetedPercentile } from \"../common/targetingEvaluator.js\";\nimport { ITargetingContext } from \"../common/ITargetingContext.js\";\n\ntype TargetingFilterParameters = {\n Audience: {\n DefaultRolloutPercentage: number;\n Users?: string[];\n Groups?: {\n Name: string;\n RolloutPercentage: number;\n }[];\n Exclusion?: {\n Users?: string[];\n Groups?: string[];\n };\n }\n}\n\ntype TargetingFilterEvaluationContext = {\n featureName: string;\n parameters: TargetingFilterParameters;\n}\n\nexport class TargetingFilter implements IFeatureFilter {\n name: string = \"Microsoft.Targeting\";\n\n async evaluate(context: TargetingFilterEvaluationContext, appContext?: ITargetingContext): Promise<boolean> {\n const { featureName, parameters } = context;\n TargetingFilter.#validateParameters(parameters);\n\n if (appContext === undefined) {\n throw new Error(\"The app context is required for targeting filter.\");\n }\n\n if (parameters.Audience.Exclusion !== undefined) {\n // check if the user is in the exclusion list\n if (appContext?.userId !== undefined &&\n parameters.Audience.Exclusion.Users !== undefined &&\n parameters.Audience.Exclusion.Users.includes(appContext.userId)) {\n return false;\n }\n // check if the user is in a group within exclusion list\n if (appContext?.groups !== undefined &&\n parameters.Audience.Exclusion.Groups !== undefined) {\n for (const excludedGroup of parameters.Audience.Exclusion.Groups) {\n if (appContext.groups.includes(excludedGroup)) {\n return false;\n }\n }\n }\n }\n\n // check if the user is being targeted directly\n if (appContext?.userId !== undefined &&\n parameters.Audience.Users !== undefined &&\n parameters.Audience.Users.includes(appContext.userId)) {\n return true;\n }\n\n // check if the user is in a group that is being targeted\n if (appContext?.groups !== undefined &&\n parameters.Audience.Groups !== undefined) {\n for (const group of parameters.Audience.Groups) {\n if (appContext.groups.includes(group.Name)) {\n const hint = `${featureName}\\n${group.Name}`;\n if (await isTargetedPercentile(appContext.userId, hint, 0, group.RolloutPercentage)) {\n return true;\n }\n }\n }\n }\n\n // check if the user is being targeted by a default rollout percentage\n const hint = featureName;\n return isTargetedPercentile(appContext?.userId, hint, 0, parameters.Audience.DefaultRolloutPercentage);\n }\n\n static #validateParameters(parameters: TargetingFilterParameters): void {\n if (parameters.Audience.DefaultRolloutPercentage < 0 || parameters.Audience.DefaultRolloutPercentage > 100) {\n throw new Error(
|
|
1
|
+
{"version":3,"file":"TargetingFilter.js","sources":["../../../src/filter/TargetingFilter.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { IFeatureFilter } from \"./FeatureFilter.js\";\nimport { isTargetedPercentile } from \"../common/targetingEvaluator.js\";\nimport { ITargetingContext } from \"../common/ITargetingContext.js\";\n\ntype TargetingFilterParameters = {\n Audience: {\n DefaultRolloutPercentage: number;\n Users?: string[];\n Groups?: {\n Name: string;\n RolloutPercentage: number;\n }[];\n Exclusion?: {\n Users?: string[];\n Groups?: string[];\n };\n }\n}\n\ntype TargetingFilterEvaluationContext = {\n featureName: string;\n parameters: TargetingFilterParameters;\n}\n\nexport class TargetingFilter implements IFeatureFilter {\n name: string = \"Microsoft.Targeting\";\n\n async evaluate(context: TargetingFilterEvaluationContext, appContext?: ITargetingContext): Promise<boolean> {\n const { featureName, parameters } = context;\n TargetingFilter.#validateParameters(featureName, parameters);\n\n if (appContext === undefined) {\n throw new Error(\"The app context is required for targeting filter.\");\n }\n\n if (parameters.Audience.Exclusion !== undefined) {\n // check if the user is in the exclusion list\n if (appContext?.userId !== undefined &&\n parameters.Audience.Exclusion.Users !== undefined &&\n parameters.Audience.Exclusion.Users.includes(appContext.userId)) {\n return false;\n }\n // check if the user is in a group within exclusion list\n if (appContext?.groups !== undefined &&\n parameters.Audience.Exclusion.Groups !== undefined) {\n for (const excludedGroup of parameters.Audience.Exclusion.Groups) {\n if (appContext.groups.includes(excludedGroup)) {\n return false;\n }\n }\n }\n }\n\n // check if the user is being targeted directly\n if (appContext?.userId !== undefined &&\n parameters.Audience.Users !== undefined &&\n parameters.Audience.Users.includes(appContext.userId)) {\n return true;\n }\n\n // check if the user is in a group that is being targeted\n if (appContext?.groups !== undefined &&\n parameters.Audience.Groups !== undefined) {\n for (const group of parameters.Audience.Groups) {\n if (appContext.groups.includes(group.Name)) {\n const hint = `${featureName}\\n${group.Name}`;\n if (await isTargetedPercentile(appContext.userId, hint, 0, group.RolloutPercentage)) {\n return true;\n }\n }\n }\n }\n\n // check if the user is being targeted by a default rollout percentage\n const hint = featureName;\n return isTargetedPercentile(appContext?.userId, hint, 0, parameters.Audience.DefaultRolloutPercentage);\n }\n\n static #validateParameters(featureName: string, parameters: TargetingFilterParameters): void {\n if (parameters.Audience.DefaultRolloutPercentage < 0 || parameters.Audience.DefaultRolloutPercentage > 100) {\n throw new Error(`Invalid feature flag: ${featureName}. Audience.DefaultRolloutPercentage must be a number between 0 and 100.`);\n }\n // validate RolloutPercentage for each group\n if (parameters.Audience.Groups !== undefined) {\n for (const group of parameters.Audience.Groups) {\n if (group.RolloutPercentage < 0 || group.RolloutPercentage > 100) {\n throw new Error(`Invalid feature flag: ${featureName}. RolloutPercentage of group ${group.Name} must be a number between 0 and 100.`);\n }\n }\n }\n }\n}\n"],"names":["isTargetedPercentile"],"mappings":"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"}
|
|
@@ -17,157 +17,157 @@ function validateFeatureFlag(featureFlag) {
|
|
|
17
17
|
throw new TypeError("Feature flag 'id' must be a string.");
|
|
18
18
|
}
|
|
19
19
|
if (featureFlag.enabled !== undefined && typeof featureFlag.enabled !== "boolean") {
|
|
20
|
-
throw new TypeError(
|
|
20
|
+
throw new TypeError(`Invalid feature flag: ${featureFlag.id}. Feature flag 'enabled' must be a boolean.`);
|
|
21
21
|
}
|
|
22
22
|
if (featureFlag.conditions !== undefined) {
|
|
23
|
-
validateFeatureEnablementConditions(featureFlag.conditions);
|
|
23
|
+
validateFeatureEnablementConditions(featureFlag.id, featureFlag.conditions);
|
|
24
24
|
}
|
|
25
25
|
if (featureFlag.variants !== undefined) {
|
|
26
|
-
validateVariants(featureFlag.variants);
|
|
26
|
+
validateVariants(featureFlag.id, featureFlag.variants);
|
|
27
27
|
}
|
|
28
28
|
if (featureFlag.allocation !== undefined) {
|
|
29
|
-
validateVariantAllocation(featureFlag.allocation);
|
|
29
|
+
validateVariantAllocation(featureFlag.id, featureFlag.allocation);
|
|
30
30
|
}
|
|
31
31
|
if (featureFlag.telemetry !== undefined) {
|
|
32
|
-
validateTelemetryOptions(featureFlag.telemetry);
|
|
32
|
+
validateTelemetryOptions(featureFlag.id, featureFlag.telemetry);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
-
function validateFeatureEnablementConditions(conditions) {
|
|
35
|
+
function validateFeatureEnablementConditions(id, conditions) {
|
|
36
36
|
if (typeof conditions !== "object") {
|
|
37
|
-
throw new TypeError(
|
|
37
|
+
throw new TypeError(`Invalid feature flag: ${id}. Feature flag 'conditions' must be an object.`);
|
|
38
38
|
}
|
|
39
39
|
if (conditions.requirement_type !== undefined && conditions.requirement_type !== "Any" && conditions.requirement_type !== "All") {
|
|
40
|
-
throw new TypeError(
|
|
40
|
+
throw new TypeError(`Invalid feature flag: ${id}. 'requirement_type' must be 'Any' or 'All'.`);
|
|
41
41
|
}
|
|
42
42
|
if (conditions.client_filters !== undefined) {
|
|
43
|
-
validateClientFilters(conditions.client_filters);
|
|
43
|
+
validateClientFilters(id, conditions.client_filters);
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
|
-
function validateClientFilters(client_filters) {
|
|
46
|
+
function validateClientFilters(id, client_filters) {
|
|
47
47
|
if (!Array.isArray(client_filters)) {
|
|
48
|
-
throw new TypeError(
|
|
48
|
+
throw new TypeError(`Invalid feature flag: ${id}. Feature flag conditions 'client_filters' must be an array.`);
|
|
49
49
|
}
|
|
50
50
|
for (const filter of client_filters) {
|
|
51
51
|
if (typeof filter.name !== "string") {
|
|
52
|
-
throw new TypeError(
|
|
52
|
+
throw new TypeError(`Invalid feature flag: ${id}. Client filter 'name' must be a string.`);
|
|
53
53
|
}
|
|
54
54
|
if (filter.parameters !== undefined && typeof filter.parameters !== "object") {
|
|
55
|
-
throw new TypeError(
|
|
55
|
+
throw new TypeError(`Invalid feature flag: ${id}. Client filter 'parameters' must be an object.`);
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
-
function validateVariants(variants) {
|
|
59
|
+
function validateVariants(id, variants) {
|
|
60
60
|
if (!Array.isArray(variants)) {
|
|
61
|
-
throw new TypeError(
|
|
61
|
+
throw new TypeError(`Invalid feature flag: ${id}. Feature flag 'variants' must be an array.`);
|
|
62
62
|
}
|
|
63
63
|
for (const variant of variants) {
|
|
64
64
|
if (typeof variant.name !== "string") {
|
|
65
|
-
throw new TypeError(
|
|
65
|
+
throw new TypeError(`Invalid feature flag: ${id}. Variant 'name' must be a string.`);
|
|
66
66
|
}
|
|
67
67
|
// skip configuration_value validation as it accepts any type
|
|
68
68
|
if (variant.status_override !== undefined && typeof variant.status_override !== "string") {
|
|
69
|
-
throw new TypeError(
|
|
69
|
+
throw new TypeError(`Invalid feature flag: ${id}. Variant 'status_override' must be a string.`);
|
|
70
70
|
}
|
|
71
71
|
if (variant.status_override !== undefined && variant.status_override !== "None" && variant.status_override !== "Enabled" && variant.status_override !== "Disabled") {
|
|
72
|
-
throw new TypeError(
|
|
72
|
+
throw new TypeError(`Invalid feature flag: ${id}. Variant 'status_override' must be 'None', 'Enabled', or 'Disabled'.`);
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
|
-
function validateVariantAllocation(allocation) {
|
|
76
|
+
function validateVariantAllocation(id, allocation) {
|
|
77
77
|
if (typeof allocation !== "object") {
|
|
78
|
-
throw new TypeError(
|
|
78
|
+
throw new TypeError(`Invalid feature flag: ${id}. Variant 'allocation' must be an object.`);
|
|
79
79
|
}
|
|
80
80
|
if (allocation.default_when_disabled !== undefined && typeof allocation.default_when_disabled !== "string") {
|
|
81
|
-
throw new TypeError(
|
|
81
|
+
throw new TypeError(`Invalid feature flag: ${id}. Variant allocation 'default_when_disabled' must be a string.`);
|
|
82
82
|
}
|
|
83
83
|
if (allocation.default_when_enabled !== undefined && typeof allocation.default_when_enabled !== "string") {
|
|
84
|
-
throw new TypeError(
|
|
84
|
+
throw new TypeError(`Invalid feature flag: ${id}. Variant allocation 'default_when_enabled' must be a string.`);
|
|
85
85
|
}
|
|
86
86
|
if (allocation.user !== undefined) {
|
|
87
|
-
validateUserVariantAllocation(allocation.user);
|
|
87
|
+
validateUserVariantAllocation(id, allocation.user);
|
|
88
88
|
}
|
|
89
89
|
if (allocation.group !== undefined) {
|
|
90
|
-
validateGroupVariantAllocation(allocation.group);
|
|
90
|
+
validateGroupVariantAllocation(id, allocation.group);
|
|
91
91
|
}
|
|
92
92
|
if (allocation.percentile !== undefined) {
|
|
93
|
-
validatePercentileVariantAllocation(allocation.percentile);
|
|
93
|
+
validatePercentileVariantAllocation(id, allocation.percentile);
|
|
94
94
|
}
|
|
95
95
|
if (allocation.seed !== undefined && typeof allocation.seed !== "string") {
|
|
96
|
-
throw new TypeError(
|
|
96
|
+
throw new TypeError(`Invalid feature flag: ${id}. Variant allocation 'seed' must be a string.`);
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
-
function validateUserVariantAllocation(UserAllocations) {
|
|
99
|
+
function validateUserVariantAllocation(id, UserAllocations) {
|
|
100
100
|
if (!Array.isArray(UserAllocations)) {
|
|
101
|
-
throw new TypeError(
|
|
101
|
+
throw new TypeError(`Invalid feature flag: ${id}. Variant 'user' allocation must be an array.`);
|
|
102
102
|
}
|
|
103
103
|
for (const allocation of UserAllocations) {
|
|
104
104
|
if (typeof allocation !== "object") {
|
|
105
|
-
throw new TypeError(
|
|
105
|
+
throw new TypeError(`Invalid feature flag: ${id}. Elements in variant 'user' allocation must be an object.`);
|
|
106
106
|
}
|
|
107
107
|
if (typeof allocation.variant !== "string") {
|
|
108
|
-
throw new TypeError(
|
|
108
|
+
throw new TypeError(`Invalid feature flag: ${id}. User allocation 'variant' must be a string.`);
|
|
109
109
|
}
|
|
110
110
|
if (!Array.isArray(allocation.users)) {
|
|
111
|
-
throw new TypeError(
|
|
111
|
+
throw new TypeError(`Invalid feature flag: ${id}. User allocation 'users' must be an array.`);
|
|
112
112
|
}
|
|
113
113
|
for (const user of allocation.users) {
|
|
114
114
|
if (typeof user !== "string") {
|
|
115
|
-
throw new TypeError(
|
|
115
|
+
throw new TypeError(`Invalid feature flag: ${id}. Elements in user allocation 'users' must be strings.`);
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
|
-
function validateGroupVariantAllocation(groupAllocations) {
|
|
120
|
+
function validateGroupVariantAllocation(id, groupAllocations) {
|
|
121
121
|
if (!Array.isArray(groupAllocations)) {
|
|
122
|
-
throw new TypeError(
|
|
122
|
+
throw new TypeError(`Invalid feature flag: ${id}. Variant 'group' allocation must be an array.`);
|
|
123
123
|
}
|
|
124
124
|
for (const allocation of groupAllocations) {
|
|
125
125
|
if (typeof allocation !== "object") {
|
|
126
|
-
throw new TypeError(
|
|
126
|
+
throw new TypeError(`Invalid feature flag: ${id}. Elements in variant 'group' allocation must be an object.`);
|
|
127
127
|
}
|
|
128
128
|
if (typeof allocation.variant !== "string") {
|
|
129
|
-
throw new TypeError(
|
|
129
|
+
throw new TypeError(`Invalid feature flag: ${id}. Group allocation 'variant' must be a string.`);
|
|
130
130
|
}
|
|
131
131
|
if (!Array.isArray(allocation.groups)) {
|
|
132
|
-
throw new TypeError(
|
|
132
|
+
throw new TypeError(`Invalid feature flag: ${id}. Group allocation 'groups' must be an array.`);
|
|
133
133
|
}
|
|
134
134
|
for (const group of allocation.groups) {
|
|
135
135
|
if (typeof group !== "string") {
|
|
136
|
-
throw new TypeError(
|
|
136
|
+
throw new TypeError(`Invalid feature flag: ${id}. Elements in group allocation 'groups' must be strings.`);
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
|
-
function validatePercentileVariantAllocation(percentileAllocations) {
|
|
141
|
+
function validatePercentileVariantAllocation(id, percentileAllocations) {
|
|
142
142
|
if (!Array.isArray(percentileAllocations)) {
|
|
143
|
-
throw new TypeError(
|
|
143
|
+
throw new TypeError(`Invalid feature flag: ${id}. Variant 'percentile' allocation must be an array.`);
|
|
144
144
|
}
|
|
145
145
|
for (const allocation of percentileAllocations) {
|
|
146
146
|
if (typeof allocation !== "object") {
|
|
147
|
-
throw new TypeError(
|
|
147
|
+
throw new TypeError(`Invalid feature flag: ${id}. Elements in variant 'percentile' allocation must be an object.`);
|
|
148
148
|
}
|
|
149
149
|
if (typeof allocation.variant !== "string") {
|
|
150
|
-
throw new TypeError(
|
|
150
|
+
throw new TypeError(`Invalid feature flag: ${id}. Percentile allocation 'variant' must be a string.`);
|
|
151
151
|
}
|
|
152
152
|
if (typeof allocation.from !== "number" || allocation.from < 0 || allocation.from > 100) {
|
|
153
|
-
throw new TypeError(
|
|
153
|
+
throw new TypeError(`Invalid feature flag: ${id}. Percentile allocation 'from' must be a number between 0 and 100.`);
|
|
154
154
|
}
|
|
155
155
|
if (typeof allocation.to !== "number" || allocation.to < 0 || allocation.to > 100) {
|
|
156
|
-
throw new TypeError(
|
|
156
|
+
throw new TypeError(`Invalid feature flag: ${id}. Percentile allocation 'to' must be a number between 0 and 100.`);
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
// #endregion
|
|
161
161
|
// #region Telemetry
|
|
162
|
-
function validateTelemetryOptions(telemetry) {
|
|
162
|
+
function validateTelemetryOptions(id, telemetry) {
|
|
163
163
|
if (typeof telemetry !== "object") {
|
|
164
|
-
throw new TypeError(
|
|
164
|
+
throw new TypeError(`Invalid feature flag: ${id}. Feature flag 'telemetry' must be an object.`);
|
|
165
165
|
}
|
|
166
166
|
if (telemetry.enabled !== undefined && typeof telemetry.enabled !== "boolean") {
|
|
167
|
-
throw new TypeError(
|
|
167
|
+
throw new TypeError(`Invalid feature flag: ${id}. Telemetry 'enabled' must be a boolean.`);
|
|
168
168
|
}
|
|
169
169
|
if (telemetry.metadata !== undefined && typeof telemetry.metadata !== "object") {
|
|
170
|
-
throw new TypeError(
|
|
170
|
+
throw new TypeError(`Invalid feature flag: ${id}. Telemetry 'metadata' must be an object.`);
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
173
|
// #endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.js","sources":["../../../src/schema/validator.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\n/**\n * Validates a feature flag object, checking if it conforms to the schema.\n * @param featureFlag The feature flag object to validate.\n */\nexport function validateFeatureFlag(featureFlag: any): void {\n if (featureFlag === undefined) {\n return; // no-op if feature flag is undefined, indicating that the feature flag is not found\n }\n if (featureFlag === null || typeof featureFlag !== \"object\") { // Note: typeof null = \"object\"\n throw new TypeError(\"Feature flag must be an object.\");\n }\n if (typeof featureFlag.id !== \"string\") {\n throw new TypeError(\"Feature flag 'id' must be a string.\");\n }\n if (featureFlag.enabled !== undefined && typeof featureFlag.enabled !== \"boolean\") {\n throw new TypeError(\"Feature flag 'enabled' must be a boolean.\");\n }\n if (featureFlag.conditions !== undefined) {\n validateFeatureEnablementConditions(featureFlag.conditions);\n }\n if (featureFlag.variants !== undefined) {\n validateVariants(featureFlag.variants);\n }\n if (featureFlag.allocation !== undefined) {\n validateVariantAllocation(featureFlag.allocation);\n }\n if (featureFlag.telemetry !== undefined) {\n validateTelemetryOptions(featureFlag.telemetry);\n }\n}\n\nfunction validateFeatureEnablementConditions(conditions: any) {\n if (typeof conditions !== \"object\") {\n throw new TypeError(\"Feature flag 'conditions' must be an object.\");\n }\n if (conditions.requirement_type !== undefined && conditions.requirement_type !== \"Any\" && conditions.requirement_type !== \"All\") {\n throw new TypeError(\"'requirement_type' must be 'Any' or 'All'.\");\n }\n if (conditions.client_filters !== undefined) {\n validateClientFilters(conditions.client_filters);\n }\n}\n\nfunction validateClientFilters(client_filters: any) {\n if (!Array.isArray(client_filters)) {\n throw new TypeError(\"Feature flag conditions 'client_filters' must be an array.\");\n }\n\n for (const filter of client_filters) {\n if (typeof filter.name !== \"string\") {\n throw new TypeError(\"Client filter 'name' must be a string.\");\n }\n if (filter.parameters !== undefined && typeof filter.parameters !== \"object\") {\n throw new TypeError(\"Client filter 'parameters' must be an object.\");\n }\n }\n}\n\nfunction validateVariants(variants: any) {\n if (!Array.isArray(variants)) {\n throw new TypeError(\"Feature flag 'variants' must be an array.\");\n }\n\n for (const variant of variants) {\n if (typeof variant.name !== \"string\") {\n throw new TypeError(\"Variant 'name' must be a string.\");\n }\n // skip configuration_value validation as it accepts any type\n if (variant.status_override !== undefined && typeof variant.status_override !== \"string\") {\n throw new TypeError(\"Variant 'status_override' must be a string.\");\n }\n if (variant.status_override !== undefined && variant.status_override !== \"None\" && variant.status_override !== \"Enabled\" && variant.status_override !== \"Disabled\") {\n throw new TypeError(\"Variant 'status_override' must be 'None', 'Enabled', or 'Disabled'.\");\n }\n }\n}\n\nfunction validateVariantAllocation(allocation: any) {\n if (typeof allocation !== \"object\") {\n throw new TypeError(\"Variant 'allocation' must be an object.\");\n }\n\n if (allocation.default_when_disabled !== undefined && typeof allocation.default_when_disabled !== \"string\") {\n throw new TypeError(\"Variant allocation 'default_when_disabled' must be a string.\");\n }\n if (allocation.default_when_enabled !== undefined && typeof allocation.default_when_enabled !== \"string\") {\n throw new TypeError(\"Variant allocation 'default_when_enabled' must be a string.\");\n }\n if (allocation.user !== undefined) {\n validateUserVariantAllocation(allocation.user);\n }\n if (allocation.group !== undefined) {\n validateGroupVariantAllocation(allocation.group);\n }\n if (allocation.percentile !== undefined) {\n validatePercentileVariantAllocation(allocation.percentile);\n }\n if (allocation.seed !== undefined && typeof allocation.seed !== \"string\") {\n throw new TypeError(\"Variant allocation 'seed' must be a string.\");\n }\n}\n\nfunction validateUserVariantAllocation(UserAllocations: any) {\n if (!Array.isArray(UserAllocations)) {\n throw new TypeError(\"Variant 'user' allocation must be an array.\");\n }\n\n for (const allocation of UserAllocations) {\n if (typeof allocation !== \"object\") {\n throw new TypeError(\"Elements in variant 'user' allocation must be an object.\");\n }\n if (typeof allocation.variant !== \"string\") {\n throw new TypeError(\"User allocation 'variant' must be a string.\");\n }\n if (!Array.isArray(allocation.users)) {\n throw new TypeError(\"User allocation 'users' must be an array.\");\n }\n for (const user of allocation.users) {\n if (typeof user !== \"string\") {\n throw new TypeError(\"Elements in user allocation 'users' must be strings.\");\n }\n }\n }\n}\n\nfunction validateGroupVariantAllocation(groupAllocations: any) {\n if (!Array.isArray(groupAllocations)) {\n throw new TypeError(\"Variant 'group' allocation must be an array.\");\n }\n\n for (const allocation of groupAllocations) {\n if (typeof allocation !== \"object\") {\n throw new TypeError(\"Elements in variant 'group' allocation must be an object.\");\n }\n if (typeof allocation.variant !== \"string\") {\n throw new TypeError(\"Group allocation 'variant' must be a string.\");\n }\n if (!Array.isArray(allocation.groups)) {\n throw new TypeError(\"Group allocation 'groups' must be an array.\");\n }\n for (const group of allocation.groups) {\n if (typeof group !== \"string\") {\n throw new TypeError(\"Elements in group allocation 'groups' must be strings.\");\n }\n }\n }\n}\n\nfunction validatePercentileVariantAllocation(percentileAllocations: any) {\n if (!Array.isArray(percentileAllocations)) {\n throw new TypeError(\"Variant 'percentile' allocation must be an array.\");\n }\n\n for (const allocation of percentileAllocations) {\n if (typeof allocation !== \"object\") {\n throw new TypeError(\"Elements in variant 'percentile' allocation must be an object.\");\n }\n if (typeof allocation.variant !== \"string\") {\n throw new TypeError(\"Percentile allocation 'variant' must be a string.\");\n }\n if (typeof allocation.from !== \"number\" || allocation.from < 0 || allocation.from > 100) {\n throw new TypeError(\"Percentile allocation 'from' must be a number between 0 and 100.\");\n }\n if (typeof allocation.to !== \"number\" || allocation.to < 0 || allocation.to > 100) {\n throw new TypeError(\"Percentile allocation 'to' must be a number between 0 and 100.\");\n }\n }\n}\n// #endregion\n\n// #region Telemetry\nfunction validateTelemetryOptions(telemetry: any) {\n if (typeof telemetry !== \"object\") {\n throw new TypeError(\"Feature flag 'telemetry' must be an object.\");\n }\n if (telemetry.enabled !== undefined && typeof telemetry.enabled !== \"boolean\") {\n throw new TypeError(\"Telemetry 'enabled' must be a boolean.\");\n }\n if (telemetry.metadata !== undefined && typeof telemetry.metadata !== \"object\") {\n throw new TypeError(\"Telemetry 'metadata' must be an object.\");\n }\n}\n// #endregion\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"validator.js","sources":["../../../src/schema/validator.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\n/**\n * Validates a feature flag object, checking if it conforms to the schema.\n * @param featureFlag The feature flag object to validate.\n */\nexport function validateFeatureFlag(featureFlag: any): void {\n if (featureFlag === undefined) {\n return; // no-op if feature flag is undefined, indicating that the feature flag is not found\n }\n if (featureFlag === null || typeof featureFlag !== \"object\") { // Note: typeof null = \"object\"\n throw new TypeError(\"Feature flag must be an object.\");\n }\n if (typeof featureFlag.id !== \"string\") {\n throw new TypeError(\"Feature flag 'id' must be a string.\");\n }\n\n if (featureFlag.enabled !== undefined && typeof featureFlag.enabled !== \"boolean\") {\n throw new TypeError(`Invalid feature flag: ${featureFlag.id}. Feature flag 'enabled' must be a boolean.`);\n }\n if (featureFlag.conditions !== undefined) {\n validateFeatureEnablementConditions(featureFlag.id, featureFlag.conditions);\n }\n if (featureFlag.variants !== undefined) {\n validateVariants(featureFlag.id, featureFlag.variants);\n }\n if (featureFlag.allocation !== undefined) {\n validateVariantAllocation(featureFlag.id, featureFlag.allocation);\n }\n if (featureFlag.telemetry !== undefined) {\n validateTelemetryOptions(featureFlag.id, featureFlag.telemetry);\n }\n}\n\nfunction validateFeatureEnablementConditions(id: string, conditions: any) {\n if (typeof conditions !== \"object\") {\n throw new TypeError(`Invalid feature flag: ${id}. Feature flag 'conditions' must be an object.`);\n }\n if (conditions.requirement_type !== undefined && conditions.requirement_type !== \"Any\" && conditions.requirement_type !== \"All\") {\n throw new TypeError(`Invalid feature flag: ${id}. 'requirement_type' must be 'Any' or 'All'.`);\n }\n if (conditions.client_filters !== undefined) {\n validateClientFilters(id, conditions.client_filters);\n }\n}\n\nfunction validateClientFilters(id: string, client_filters: any) {\n if (!Array.isArray(client_filters)) {\n throw new TypeError(`Invalid feature flag: ${id}. Feature flag conditions 'client_filters' must be an array.`);\n }\n\n for (const filter of client_filters) {\n if (typeof filter.name !== \"string\") {\n throw new TypeError(`Invalid feature flag: ${id}. Client filter 'name' must be a string.`);\n }\n if (filter.parameters !== undefined && typeof filter.parameters !== \"object\") {\n throw new TypeError(`Invalid feature flag: ${id}. Client filter 'parameters' must be an object.`);\n }\n }\n}\n\nfunction validateVariants(id: string, variants: any) {\n if (!Array.isArray(variants)) {\n throw new TypeError(`Invalid feature flag: ${id}. Feature flag 'variants' must be an array.`);\n }\n\n for (const variant of variants) {\n if (typeof variant.name !== \"string\") {\n throw new TypeError(`Invalid feature flag: ${id}. Variant 'name' must be a string.`);\n }\n // skip configuration_value validation as it accepts any type\n if (variant.status_override !== undefined && typeof variant.status_override !== \"string\") {\n throw new TypeError(`Invalid feature flag: ${id}. Variant 'status_override' must be a string.`);\n }\n if (variant.status_override !== undefined && variant.status_override !== \"None\" && variant.status_override !== \"Enabled\" && variant.status_override !== \"Disabled\") {\n throw new TypeError(`Invalid feature flag: ${id}. Variant 'status_override' must be 'None', 'Enabled', or 'Disabled'.`);\n }\n }\n}\n\nfunction validateVariantAllocation(id: string, allocation: any) {\n if (typeof allocation !== \"object\") {\n throw new TypeError(`Invalid feature flag: ${id}. Variant 'allocation' must be an object.`);\n }\n\n if (allocation.default_when_disabled !== undefined && typeof allocation.default_when_disabled !== \"string\") {\n throw new TypeError(`Invalid feature flag: ${id}. Variant allocation 'default_when_disabled' must be a string.`);\n }\n if (allocation.default_when_enabled !== undefined && typeof allocation.default_when_enabled !== \"string\") {\n throw new TypeError(`Invalid feature flag: ${id}. Variant allocation 'default_when_enabled' must be a string.`);\n }\n if (allocation.user !== undefined) {\n validateUserVariantAllocation(id, allocation.user);\n }\n if (allocation.group !== undefined) {\n validateGroupVariantAllocation(id, allocation.group);\n }\n if (allocation.percentile !== undefined) {\n validatePercentileVariantAllocation(id, allocation.percentile);\n }\n if (allocation.seed !== undefined && typeof allocation.seed !== \"string\") {\n throw new TypeError(`Invalid feature flag: ${id}. Variant allocation 'seed' must be a string.`);\n }\n}\n\nfunction validateUserVariantAllocation(id: string, UserAllocations: any) {\n if (!Array.isArray(UserAllocations)) {\n throw new TypeError(`Invalid feature flag: ${id}. Variant 'user' allocation must be an array.`);\n }\n\n for (const allocation of UserAllocations) {\n if (typeof allocation !== \"object\") {\n throw new TypeError(`Invalid feature flag: ${id}. Elements in variant 'user' allocation must be an object.`);\n }\n if (typeof allocation.variant !== \"string\") {\n throw new TypeError(`Invalid feature flag: ${id}. User allocation 'variant' must be a string.`);\n }\n if (!Array.isArray(allocation.users)) {\n throw new TypeError(`Invalid feature flag: ${id}. User allocation 'users' must be an array.`);\n }\n for (const user of allocation.users) {\n if (typeof user !== \"string\") {\n throw new TypeError(`Invalid feature flag: ${id}. Elements in user allocation 'users' must be strings.`);\n }\n }\n }\n}\n\nfunction validateGroupVariantAllocation(id: string, groupAllocations: any) {\n if (!Array.isArray(groupAllocations)) {\n throw new TypeError(`Invalid feature flag: ${id}. Variant 'group' allocation must be an array.`);\n }\n\n for (const allocation of groupAllocations) {\n if (typeof allocation !== \"object\") {\n throw new TypeError(`Invalid feature flag: ${id}. Elements in variant 'group' allocation must be an object.`);\n }\n if (typeof allocation.variant !== \"string\") {\n throw new TypeError(`Invalid feature flag: ${id}. Group allocation 'variant' must be a string.`);\n }\n if (!Array.isArray(allocation.groups)) {\n throw new TypeError(`Invalid feature flag: ${id}. Group allocation 'groups' must be an array.`);\n }\n for (const group of allocation.groups) {\n if (typeof group !== \"string\") {\n throw new TypeError(`Invalid feature flag: ${id}. Elements in group allocation 'groups' must be strings.`);\n }\n }\n }\n}\n\nfunction validatePercentileVariantAllocation(id: string, percentileAllocations: any) {\n if (!Array.isArray(percentileAllocations)) {\n throw new TypeError(`Invalid feature flag: ${id}. Variant 'percentile' allocation must be an array.`);\n }\n\n for (const allocation of percentileAllocations) {\n if (typeof allocation !== \"object\") {\n throw new TypeError(`Invalid feature flag: ${id}. Elements in variant 'percentile' allocation must be an object.`);\n }\n if (typeof allocation.variant !== \"string\") {\n throw new TypeError(`Invalid feature flag: ${id}. Percentile allocation 'variant' must be a string.`);\n }\n if (typeof allocation.from !== \"number\" || allocation.from < 0 || allocation.from > 100) {\n throw new TypeError(`Invalid feature flag: ${id}. Percentile allocation 'from' must be a number between 0 and 100.`);\n }\n if (typeof allocation.to !== \"number\" || allocation.to < 0 || allocation.to > 100) {\n throw new TypeError(`Invalid feature flag: ${id}. Percentile allocation 'to' must be a number between 0 and 100.`);\n }\n }\n}\n// #endregion\n\n// #region Telemetry\nfunction validateTelemetryOptions(id: string, telemetry: any) {\n if (typeof telemetry !== \"object\") {\n throw new TypeError(`Invalid feature flag: ${id}. Feature flag 'telemetry' must be an object.`);\n }\n if (telemetry.enabled !== undefined && typeof telemetry.enabled !== \"boolean\") {\n throw new TypeError(`Invalid feature flag: ${id}. Telemetry 'enabled' must be a boolean.`);\n }\n if (telemetry.metadata !== undefined && typeof telemetry.metadata !== \"object\") {\n throw new TypeError(`Invalid feature flag: ${id}. Telemetry 'metadata' must be an object.`);\n }\n}\n// #endregion\n"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var featureManager = require('../featureManager.js');
|
|
4
3
|
var version = require('../version.js');
|
|
5
4
|
|
|
6
5
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -11,8 +10,6 @@ const ENABLED = "Enabled";
|
|
|
11
10
|
const TARGETING_ID = "TargetingId";
|
|
12
11
|
const VARIANT = "Variant";
|
|
13
12
|
const VARIANT_ASSIGNMENT_REASON = "VariantAssignmentReason";
|
|
14
|
-
const DEFAULT_WHEN_ENABLED = "DefaultWhenEnabled";
|
|
15
|
-
const VARIANT_ASSIGNMENT_PERCENTAGE = "VariantAssignmentPercentage";
|
|
16
13
|
function createFeatureEvaluationEventProperties(result) {
|
|
17
14
|
if (result.feature === undefined) {
|
|
18
15
|
return undefined;
|
|
@@ -26,29 +23,6 @@ function createFeatureEvaluationEventProperties(result) {
|
|
|
26
23
|
[VARIANT]: result.variant ? result.variant.name : "",
|
|
27
24
|
[VARIANT_ASSIGNMENT_REASON]: result.variantAssignmentReason,
|
|
28
25
|
};
|
|
29
|
-
if (result.feature.allocation?.default_when_enabled) {
|
|
30
|
-
eventProperties[DEFAULT_WHEN_ENABLED] = result.feature.allocation.default_when_enabled;
|
|
31
|
-
}
|
|
32
|
-
if (result.variantAssignmentReason === featureManager.VariantAssignmentReason.DefaultWhenEnabled) {
|
|
33
|
-
let percentileAllocationPercentage = 0;
|
|
34
|
-
if (result.variant !== undefined && result.feature.allocation !== undefined && result.feature.allocation.percentile !== undefined) {
|
|
35
|
-
for (const percentile of result.feature.allocation.percentile) {
|
|
36
|
-
percentileAllocationPercentage += percentile.to - percentile.from;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
eventProperties[VARIANT_ASSIGNMENT_PERCENTAGE] = (100 - percentileAllocationPercentage).toString();
|
|
40
|
-
}
|
|
41
|
-
else if (result.variantAssignmentReason === featureManager.VariantAssignmentReason.Percentile) {
|
|
42
|
-
let percentileAllocationPercentage = 0;
|
|
43
|
-
if (result.variant !== undefined && result.feature.allocation !== undefined && result.feature.allocation.percentile !== undefined) {
|
|
44
|
-
for (const percentile of result.feature.allocation.percentile) {
|
|
45
|
-
if (percentile.variant === result.variant.name) {
|
|
46
|
-
percentileAllocationPercentage += percentile.to - percentile.from;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
eventProperties[VARIANT_ASSIGNMENT_PERCENTAGE] = percentileAllocationPercentage.toString();
|
|
51
|
-
}
|
|
52
26
|
const metadata = result.feature.telemetry?.metadata;
|
|
53
27
|
if (metadata) {
|
|
54
28
|
for (const key in metadata) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"featureEvaluationEvent.js","sources":["../../../src/telemetry/featureEvaluationEvent.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { EvaluationResult
|
|
1
|
+
{"version":3,"file":"featureEvaluationEvent.js","sources":["../../../src/telemetry/featureEvaluationEvent.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { EvaluationResult } from \"../featureManager\";\nimport { EVALUATION_EVENT_VERSION } from \"../version.js\";\n\nconst VERSION = \"Version\";\nconst FEATURE_NAME = \"FeatureName\";\nconst ENABLED = \"Enabled\";\nconst TARGETING_ID = \"TargetingId\";\nconst VARIANT = \"Variant\";\nconst VARIANT_ASSIGNMENT_REASON = \"VariantAssignmentReason\";\n\nexport function createFeatureEvaluationEventProperties(result: EvaluationResult): any {\n if (result.feature === undefined) {\n return undefined;\n }\n\n const eventProperties = {\n [VERSION]: EVALUATION_EVENT_VERSION,\n [FEATURE_NAME]: result.feature ? result.feature.id : \"\",\n [ENABLED]: result.enabled ? \"True\" : \"False\",\n // Ensure targetingId is string so that it will be placed in customDimensions\n [TARGETING_ID]: result.targetingId ? result.targetingId.toString() : \"\",\n [VARIANT]: result.variant ? result.variant.name : \"\",\n [VARIANT_ASSIGNMENT_REASON]: result.variantAssignmentReason,\n };\n\n const metadata = result.feature.telemetry?.metadata;\n if (metadata) {\n for (const key in metadata) {\n if (!(key in eventProperties)) {\n eventProperties[key] = metadata[key];\n }\n }\n }\n\n return eventProperties;\n}\n"],"names":["EVALUATION_EVENT_VERSION"],"mappings":";;;;AAAA;AACA;AAKA,MAAM,OAAO,GAAG,SAAS,CAAC;AAC1B,MAAM,YAAY,GAAG,aAAa,CAAC;AACnC,MAAM,OAAO,GAAG,SAAS,CAAC;AAC1B,MAAM,YAAY,GAAG,aAAa,CAAC;AACnC,MAAM,OAAO,GAAG,SAAS,CAAC;AAC1B,MAAM,yBAAyB,GAAG,yBAAyB,CAAC;AAEtD,SAAU,sCAAsC,CAAC,MAAwB,EAAA;AAC3E,IAAA,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,EAAE;AAC9B,QAAA,OAAO,SAAS,CAAC;KACpB;AAED,IAAA,MAAM,eAAe,GAAG;QACpB,CAAC,OAAO,GAAGA,gCAAwB;AACnC,QAAA,CAAC,YAAY,GAAG,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE;AACvD,QAAA,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,GAAG,MAAM,GAAG,OAAO;;AAE5C,QAAA,CAAC,YAAY,GAAG,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE,GAAG,EAAE;AACvE,QAAA,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,GAAG,EAAE;AACpD,QAAA,CAAC,yBAAyB,GAAG,MAAM,CAAC,uBAAuB;KAC9D,CAAC;IAEF,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC;IACpD,IAAI,QAAQ,EAAE;AACV,QAAA,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE;AACxB,YAAA,IAAI,EAAE,GAAG,IAAI,eAAe,CAAC,EAAE;gBAC3B,eAAe,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;aACxC;SACJ;KACJ;AAED,IAAA,OAAO,eAAe,CAAC;AAC3B;;;;"}
|
package/dist/commonjs/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sources":["../../src/version.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nexport const VERSION = \"2.0.0
|
|
1
|
+
{"version":3,"file":"version.js","sources":["../../src/version.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nexport const VERSION = \"2.0.0\";\nexport const EVALUATION_EVENT_VERSION = \"1.0.0\";\n"],"names":[],"mappings":";;AAAA;AACA;AAEO,MAAM,OAAO,GAAG,QAAQ;AACxB,MAAM,wBAAwB,GAAG;;;;;"}
|
|
@@ -19,9 +19,11 @@ class ConfigurationMapFeatureFlagProvider {
|
|
|
19
19
|
}
|
|
20
20
|
async getFeatureFlags() {
|
|
21
21
|
const featureConfig = this.#configuration.get(FEATURE_MANAGEMENT_KEY);
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
const featureFlags = featureConfig?.[FEATURE_FLAGS_KEY] ?? [];
|
|
23
|
+
featureFlags.forEach(featureFlag => {
|
|
24
|
+
validateFeatureFlag(featureFlag);
|
|
25
|
+
});
|
|
26
|
+
return featureFlags;
|
|
25
27
|
}
|
|
26
28
|
}
|
|
27
29
|
/**
|
|
@@ -39,9 +41,11 @@ class ConfigurationObjectFeatureFlagProvider {
|
|
|
39
41
|
return featureFlag;
|
|
40
42
|
}
|
|
41
43
|
async getFeatureFlags() {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
const featureFlags = this.#configuration[FEATURE_MANAGEMENT_KEY]?.[FEATURE_FLAGS_KEY] ?? [];
|
|
45
|
+
featureFlags.forEach(featureFlag => {
|
|
46
|
+
validateFeatureFlag(featureFlag);
|
|
47
|
+
});
|
|
48
|
+
return featureFlags;
|
|
45
49
|
}
|
|
46
50
|
}
|
|
47
51
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"featureProvider.js","sources":["../../src/featureProvider.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { IGettable } from \"./gettable.js\";\nimport { FeatureFlag, FeatureManagementConfiguration, FEATURE_MANAGEMENT_KEY, FEATURE_FLAGS_KEY } from \"./schema/model.js\";\nimport { validateFeatureFlag } from \"./schema/validator.js\";\n\nexport interface IFeatureFlagProvider {\n /**\n * Get all feature flags.\n */\n getFeatureFlags(): Promise<FeatureFlag[]>;\n\n /**\n * Get a feature flag by name.\n * @param featureName The name of the feature flag.\n */\n getFeatureFlag(featureName: string): Promise<FeatureFlag | undefined>;\n}\n\n/**\n * A feature flag provider that uses a map-like configuration to provide feature flags.\n */\nexport class ConfigurationMapFeatureFlagProvider implements IFeatureFlagProvider {\n #configuration: IGettable;\n\n constructor(configuration: IGettable) {\n this.#configuration = configuration;\n }\n async getFeatureFlag(featureName: string): Promise<FeatureFlag | undefined> {\n const featureConfig = this.#configuration.get<FeatureManagementConfiguration>(FEATURE_MANAGEMENT_KEY);\n const featureFlag = featureConfig?.[FEATURE_FLAGS_KEY]?.findLast((feature) => feature.id === featureName);\n validateFeatureFlag(featureFlag);\n return featureFlag;\n }\n\n async getFeatureFlags(): Promise<FeatureFlag[]> {\n const featureConfig = this.#configuration.get<FeatureManagementConfiguration>(FEATURE_MANAGEMENT_KEY);\n const
|
|
1
|
+
{"version":3,"file":"featureProvider.js","sources":["../../src/featureProvider.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { IGettable } from \"./gettable.js\";\nimport { FeatureFlag, FeatureManagementConfiguration, FEATURE_MANAGEMENT_KEY, FEATURE_FLAGS_KEY } from \"./schema/model.js\";\nimport { validateFeatureFlag } from \"./schema/validator.js\";\n\nexport interface IFeatureFlagProvider {\n /**\n * Get all feature flags.\n */\n getFeatureFlags(): Promise<FeatureFlag[]>;\n\n /**\n * Get a feature flag by name.\n * @param featureName The name of the feature flag.\n */\n getFeatureFlag(featureName: string): Promise<FeatureFlag | undefined>;\n}\n\n/**\n * A feature flag provider that uses a map-like configuration to provide feature flags.\n */\nexport class ConfigurationMapFeatureFlagProvider implements IFeatureFlagProvider {\n #configuration: IGettable;\n\n constructor(configuration: IGettable) {\n this.#configuration = configuration;\n }\n async getFeatureFlag(featureName: string): Promise<FeatureFlag | undefined> {\n const featureConfig = this.#configuration.get<FeatureManagementConfiguration>(FEATURE_MANAGEMENT_KEY);\n const featureFlag = featureConfig?.[FEATURE_FLAGS_KEY]?.findLast((feature) => feature.id === featureName);\n validateFeatureFlag(featureFlag);\n return featureFlag;\n }\n\n async getFeatureFlags(): Promise<FeatureFlag[]> {\n const featureConfig = this.#configuration.get<FeatureManagementConfiguration>(FEATURE_MANAGEMENT_KEY);\n const featureFlags = featureConfig?.[FEATURE_FLAGS_KEY] ?? [];\n featureFlags.forEach(featureFlag => {\n validateFeatureFlag(featureFlag);\n });\n return featureFlags;\n }\n}\n\n/**\n * A feature flag provider that uses an object-like configuration to provide feature flags.\n */\nexport class ConfigurationObjectFeatureFlagProvider implements IFeatureFlagProvider {\n #configuration: Record<string, unknown>;\n\n constructor(configuration: Record<string, unknown>) {\n this.#configuration = configuration;\n }\n\n async getFeatureFlag(featureName: string): Promise<FeatureFlag | undefined> {\n const featureFlags = this.#configuration[FEATURE_MANAGEMENT_KEY]?.[FEATURE_FLAGS_KEY];\n const featureFlag = featureFlags?.findLast((feature: FeatureFlag) => feature.id === featureName);\n validateFeatureFlag(featureFlag);\n return featureFlag;\n }\n\n async getFeatureFlags(): Promise<FeatureFlag[]> {\n const featureFlags = this.#configuration[FEATURE_MANAGEMENT_KEY]?.[FEATURE_FLAGS_KEY] ?? [];\n featureFlags.forEach(featureFlag => {\n validateFeatureFlag(featureFlag);\n });\n return featureFlags;\n }\n}\n"],"names":[],"mappings":";;;AAAA;AACA;AAmBA;;AAEG;MACU,mCAAmC,CAAA;AAC5C,IAAA,cAAc,CAAY;AAE1B,IAAA,WAAA,CAAY,aAAwB,EAAA;AAChC,QAAA,IAAI,CAAC,cAAc,GAAG,aAAa,CAAC;KACvC;IACD,MAAM,cAAc,CAAC,WAAmB,EAAA;QACpC,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAiC,sBAAsB,CAAC,CAAC;QACtG,MAAM,WAAW,GAAG,aAAa,GAAG,iBAAiB,CAAC,EAAE,QAAQ,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,EAAE,KAAK,WAAW,CAAC,CAAC;QAC1G,mBAAmB,CAAC,WAAW,CAAC,CAAC;AACjC,QAAA,OAAO,WAAW,CAAC;KACtB;AAED,IAAA,MAAM,eAAe,GAAA;QACjB,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAiC,sBAAsB,CAAC,CAAC;QACtG,MAAM,YAAY,GAAG,aAAa,GAAG,iBAAiB,CAAC,IAAI,EAAE,CAAC;AAC9D,QAAA,YAAY,CAAC,OAAO,CAAC,WAAW,IAAG;YAC/B,mBAAmB,CAAC,WAAW,CAAC,CAAC;AACrC,SAAC,CAAC,CAAC;AACH,QAAA,OAAO,YAAY,CAAC;KACvB;AACJ,CAAA;AAED;;AAEG;MACU,sCAAsC,CAAA;AAC/C,IAAA,cAAc,CAA0B;AAExC,IAAA,WAAA,CAAY,aAAsC,EAAA;AAC9C,QAAA,IAAI,CAAC,cAAc,GAAG,aAAa,CAAC;KACvC;IAED,MAAM,cAAc,CAAC,WAAmB,EAAA;AACpC,QAAA,MAAM,YAAY,GAAG,IAAI,CAAC,cAAc,CAAC,sBAAsB,CAAC,GAAG,iBAAiB,CAAC,CAAC;AACtF,QAAA,MAAM,WAAW,GAAG,YAAY,EAAE,QAAQ,CAAC,CAAC,OAAoB,KAAK,OAAO,CAAC,EAAE,KAAK,WAAW,CAAC,CAAC;QACjG,mBAAmB,CAAC,WAAW,CAAC,CAAC;AACjC,QAAA,OAAO,WAAW,CAAC;KACtB;AAED,IAAA,MAAM,eAAe,GAAA;AACjB,QAAA,MAAM,YAAY,GAAG,IAAI,CAAC,cAAc,CAAC,sBAAsB,CAAC,GAAG,iBAAiB,CAAC,IAAI,EAAE,CAAC;AAC5F,QAAA,YAAY,CAAC,OAAO,CAAC,WAAW,IAAG;YAC/B,mBAAmB,CAAC,WAAW,CAAC,CAAC;AACrC,SAAC,CAAC,CAAC;AACH,QAAA,OAAO,YAAY,CAAC;KACvB;AACJ;;;;"}
|
|
@@ -6,7 +6,7 @@ class TargetingFilter {
|
|
|
6
6
|
name = "Microsoft.Targeting";
|
|
7
7
|
async evaluate(context, appContext) {
|
|
8
8
|
const { featureName, parameters } = context;
|
|
9
|
-
TargetingFilter.#validateParameters(parameters);
|
|
9
|
+
TargetingFilter.#validateParameters(featureName, parameters);
|
|
10
10
|
if (appContext === undefined) {
|
|
11
11
|
throw new Error("The app context is required for targeting filter.");
|
|
12
12
|
}
|
|
@@ -49,15 +49,15 @@ class TargetingFilter {
|
|
|
49
49
|
const hint = featureName;
|
|
50
50
|
return isTargetedPercentile(appContext?.userId, hint, 0, parameters.Audience.DefaultRolloutPercentage);
|
|
51
51
|
}
|
|
52
|
-
static #validateParameters(parameters) {
|
|
52
|
+
static #validateParameters(featureName, parameters) {
|
|
53
53
|
if (parameters.Audience.DefaultRolloutPercentage < 0 || parameters.Audience.DefaultRolloutPercentage > 100) {
|
|
54
|
-
throw new Error(
|
|
54
|
+
throw new Error(`Invalid feature flag: ${featureName}. Audience.DefaultRolloutPercentage must be a number between 0 and 100.`);
|
|
55
55
|
}
|
|
56
56
|
// validate RolloutPercentage for each group
|
|
57
57
|
if (parameters.Audience.Groups !== undefined) {
|
|
58
58
|
for (const group of parameters.Audience.Groups) {
|
|
59
59
|
if (group.RolloutPercentage < 0 || group.RolloutPercentage > 100) {
|
|
60
|
-
throw new Error(`RolloutPercentage of group ${group.Name} must be a number between 0 and 100.`);
|
|
60
|
+
throw new Error(`Invalid feature flag: ${featureName}. RolloutPercentage of group ${group.Name} must be a number between 0 and 100.`);
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TargetingFilter.js","sources":["../../../src/filter/TargetingFilter.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { IFeatureFilter } from \"./FeatureFilter.js\";\nimport { isTargetedPercentile } from \"../common/targetingEvaluator.js\";\nimport { ITargetingContext } from \"../common/ITargetingContext.js\";\n\ntype TargetingFilterParameters = {\n Audience: {\n DefaultRolloutPercentage: number;\n Users?: string[];\n Groups?: {\n Name: string;\n RolloutPercentage: number;\n }[];\n Exclusion?: {\n Users?: string[];\n Groups?: string[];\n };\n }\n}\n\ntype TargetingFilterEvaluationContext = {\n featureName: string;\n parameters: TargetingFilterParameters;\n}\n\nexport class TargetingFilter implements IFeatureFilter {\n name: string = \"Microsoft.Targeting\";\n\n async evaluate(context: TargetingFilterEvaluationContext, appContext?: ITargetingContext): Promise<boolean> {\n const { featureName, parameters } = context;\n TargetingFilter.#validateParameters(parameters);\n\n if (appContext === undefined) {\n throw new Error(\"The app context is required for targeting filter.\");\n }\n\n if (parameters.Audience.Exclusion !== undefined) {\n // check if the user is in the exclusion list\n if (appContext?.userId !== undefined &&\n parameters.Audience.Exclusion.Users !== undefined &&\n parameters.Audience.Exclusion.Users.includes(appContext.userId)) {\n return false;\n }\n // check if the user is in a group within exclusion list\n if (appContext?.groups !== undefined &&\n parameters.Audience.Exclusion.Groups !== undefined) {\n for (const excludedGroup of parameters.Audience.Exclusion.Groups) {\n if (appContext.groups.includes(excludedGroup)) {\n return false;\n }\n }\n }\n }\n\n // check if the user is being targeted directly\n if (appContext?.userId !== undefined &&\n parameters.Audience.Users !== undefined &&\n parameters.Audience.Users.includes(appContext.userId)) {\n return true;\n }\n\n // check if the user is in a group that is being targeted\n if (appContext?.groups !== undefined &&\n parameters.Audience.Groups !== undefined) {\n for (const group of parameters.Audience.Groups) {\n if (appContext.groups.includes(group.Name)) {\n const hint = `${featureName}\\n${group.Name}`;\n if (await isTargetedPercentile(appContext.userId, hint, 0, group.RolloutPercentage)) {\n return true;\n }\n }\n }\n }\n\n // check if the user is being targeted by a default rollout percentage\n const hint = featureName;\n return isTargetedPercentile(appContext?.userId, hint, 0, parameters.Audience.DefaultRolloutPercentage);\n }\n\n static #validateParameters(parameters: TargetingFilterParameters): void {\n if (parameters.Audience.DefaultRolloutPercentage < 0 || parameters.Audience.DefaultRolloutPercentage > 100) {\n throw new Error(
|
|
1
|
+
{"version":3,"file":"TargetingFilter.js","sources":["../../../src/filter/TargetingFilter.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { IFeatureFilter } from \"./FeatureFilter.js\";\nimport { isTargetedPercentile } from \"../common/targetingEvaluator.js\";\nimport { ITargetingContext } from \"../common/ITargetingContext.js\";\n\ntype TargetingFilterParameters = {\n Audience: {\n DefaultRolloutPercentage: number;\n Users?: string[];\n Groups?: {\n Name: string;\n RolloutPercentage: number;\n }[];\n Exclusion?: {\n Users?: string[];\n Groups?: string[];\n };\n }\n}\n\ntype TargetingFilterEvaluationContext = {\n featureName: string;\n parameters: TargetingFilterParameters;\n}\n\nexport class TargetingFilter implements IFeatureFilter {\n name: string = \"Microsoft.Targeting\";\n\n async evaluate(context: TargetingFilterEvaluationContext, appContext?: ITargetingContext): Promise<boolean> {\n const { featureName, parameters } = context;\n TargetingFilter.#validateParameters(featureName, parameters);\n\n if (appContext === undefined) {\n throw new Error(\"The app context is required for targeting filter.\");\n }\n\n if (parameters.Audience.Exclusion !== undefined) {\n // check if the user is in the exclusion list\n if (appContext?.userId !== undefined &&\n parameters.Audience.Exclusion.Users !== undefined &&\n parameters.Audience.Exclusion.Users.includes(appContext.userId)) {\n return false;\n }\n // check if the user is in a group within exclusion list\n if (appContext?.groups !== undefined &&\n parameters.Audience.Exclusion.Groups !== undefined) {\n for (const excludedGroup of parameters.Audience.Exclusion.Groups) {\n if (appContext.groups.includes(excludedGroup)) {\n return false;\n }\n }\n }\n }\n\n // check if the user is being targeted directly\n if (appContext?.userId !== undefined &&\n parameters.Audience.Users !== undefined &&\n parameters.Audience.Users.includes(appContext.userId)) {\n return true;\n }\n\n // check if the user is in a group that is being targeted\n if (appContext?.groups !== undefined &&\n parameters.Audience.Groups !== undefined) {\n for (const group of parameters.Audience.Groups) {\n if (appContext.groups.includes(group.Name)) {\n const hint = `${featureName}\\n${group.Name}`;\n if (await isTargetedPercentile(appContext.userId, hint, 0, group.RolloutPercentage)) {\n return true;\n }\n }\n }\n }\n\n // check if the user is being targeted by a default rollout percentage\n const hint = featureName;\n return isTargetedPercentile(appContext?.userId, hint, 0, parameters.Audience.DefaultRolloutPercentage);\n }\n\n static #validateParameters(featureName: string, parameters: TargetingFilterParameters): void {\n if (parameters.Audience.DefaultRolloutPercentage < 0 || parameters.Audience.DefaultRolloutPercentage > 100) {\n throw new Error(`Invalid feature flag: ${featureName}. Audience.DefaultRolloutPercentage must be a number between 0 and 100.`);\n }\n // validate RolloutPercentage for each group\n if (parameters.Audience.Groups !== undefined) {\n for (const group of parameters.Audience.Groups) {\n if (group.RolloutPercentage < 0 || group.RolloutPercentage > 100) {\n throw new Error(`Invalid feature flag: ${featureName}. RolloutPercentage of group ${group.Name} must be a number between 0 and 100.`);\n }\n }\n }\n }\n}\n"],"names":[],"mappings":"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"}
|