@microsoft/feature-management 2.0.0-preview.2 → 2.0.0-preview.3

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.
@@ -1,4 +1,5 @@
1
- import { FEATURE_MANAGEMENT_KEY, FEATURE_FLAGS_KEY } from './model.js';
1
+ import { FEATURE_MANAGEMENT_KEY, FEATURE_FLAGS_KEY } from './schema/model.js';
2
+ import { validateFeatureFlag } from './schema/validator.js';
2
3
 
3
4
  // Copyright (c) Microsoft Corporation.
4
5
  // Licensed under the MIT license.
@@ -12,11 +13,15 @@ class ConfigurationMapFeatureFlagProvider {
12
13
  }
13
14
  async getFeatureFlag(featureName) {
14
15
  const featureConfig = this.#configuration.get(FEATURE_MANAGEMENT_KEY);
15
- return featureConfig?.[FEATURE_FLAGS_KEY]?.find((feature) => feature.id === featureName);
16
+ const featureFlag = featureConfig?.[FEATURE_FLAGS_KEY]?.findLast((feature) => feature.id === featureName);
17
+ validateFeatureFlag(featureFlag);
18
+ return featureFlag;
16
19
  }
17
20
  async getFeatureFlags() {
18
21
  const featureConfig = this.#configuration.get(FEATURE_MANAGEMENT_KEY);
19
- return featureConfig?.[FEATURE_FLAGS_KEY] ?? [];
22
+ const featureFlag = featureConfig?.[FEATURE_FLAGS_KEY] ?? [];
23
+ validateFeatureFlag(featureFlag);
24
+ return featureFlag;
20
25
  }
21
26
  }
22
27
  /**
@@ -29,10 +34,14 @@ class ConfigurationObjectFeatureFlagProvider {
29
34
  }
30
35
  async getFeatureFlag(featureName) {
31
36
  const featureFlags = this.#configuration[FEATURE_MANAGEMENT_KEY]?.[FEATURE_FLAGS_KEY];
32
- return featureFlags?.find((feature) => feature.id === featureName);
37
+ const featureFlag = featureFlags?.findLast((feature) => feature.id === featureName);
38
+ validateFeatureFlag(featureFlag);
39
+ return featureFlag;
33
40
  }
34
41
  async getFeatureFlags() {
35
- return this.#configuration[FEATURE_MANAGEMENT_KEY]?.[FEATURE_FLAGS_KEY] ?? [];
42
+ const featureFlag = this.#configuration[FEATURE_MANAGEMENT_KEY]?.[FEATURE_FLAGS_KEY] ?? [];
43
+ validateFeatureFlag(featureFlag);
44
+ return featureFlag;
36
45
  }
37
46
  }
38
47
 
@@ -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 \"./model.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 return featureConfig?.[FEATURE_FLAGS_KEY]?.find((feature) => feature.id === featureName);\n }\n\n async getFeatureFlags(): Promise<FeatureFlag[]> {\n const featureConfig = this.#configuration.get<FeatureManagementConfiguration>(FEATURE_MANAGEMENT_KEY);\n return featureConfig?.[FEATURE_FLAGS_KEY] ?? [];\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 return featureFlags?.find((feature: FeatureFlag) => feature.id === featureName);\n }\n\n async getFeatureFlags(): Promise<FeatureFlag[]> {\n return this.#configuration[FEATURE_MANAGEMENT_KEY]?.[FEATURE_FLAGS_KEY] ?? [];\n }\n}\n"],"names":[],"mappings":";;AAAA;AACA;AAkBA;;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;AACtG,QAAA,OAAO,aAAa,GAAG,iBAAiB,CAAC,EAAE,IAAI,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,EAAE,KAAK,WAAW,CAAC,CAAC;KAC5F;AAED,IAAA,MAAM,eAAe,GAAA;QACjB,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAiC,sBAAsB,CAAC,CAAC;AACtG,QAAA,OAAO,aAAa,GAAG,iBAAiB,CAAC,IAAI,EAAE,CAAC;KACnD;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,OAAO,YAAY,EAAE,IAAI,CAAC,CAAC,OAAoB,KAAK,OAAO,CAAC,EAAE,KAAK,WAAW,CAAC,CAAC;KACnF;AAED,IAAA,MAAM,eAAe,GAAA;AACjB,QAAA,OAAO,IAAI,CAAC,cAAc,CAAC,sBAAsB,CAAC,GAAG,iBAAiB,CAAC,IAAI,EAAE,CAAC;KACjF;AACJ;;;;"}
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 featureFlag = featureConfig?.[FEATURE_FLAGS_KEY] ?? [];\n validateFeatureFlag(featureFlag);\n return featureFlag;\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 featureFlag = this.#configuration[FEATURE_MANAGEMENT_KEY]?.[FEATURE_FLAGS_KEY] ?? [];\n validateFeatureFlag(featureFlag);\n return featureFlag;\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,WAAW,GAAG,aAAa,GAAG,iBAAiB,CAAC,IAAI,EAAE,CAAC;QAC7D,mBAAmB,CAAC,WAAW,CAAC,CAAC;AACjC,QAAA,OAAO,WAAW,CAAC;KACtB;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,WAAW,GAAG,IAAI,CAAC,cAAc,CAAC,sBAAsB,CAAC,GAAG,iBAAiB,CAAC,IAAI,EAAE,CAAC;QAC3F,mBAAmB,CAAC,WAAW,CAAC,CAAC;AACjC,QAAA,OAAO,WAAW,CAAC;KACtB;AACJ;;;;"}
package/dist/esm/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { EvaluationResult, FeatureManager, VariantAssignmentReason } from './featureManager.js';
2
2
  export { ConfigurationMapFeatureFlagProvider, ConfigurationObjectFeatureFlagProvider } from './featureProvider.js';
3
+ export { createFeatureEvaluationEventProperties } from './telemetry/featureEvaluationEvent.js';
3
4
  export { VERSION } from './version.js';
4
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sources":["../../../src/schema/model.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\n// Converted from:\n// https://github.com/Azure/AppConfiguration/blob/6e544296a5607f922a423df165f60801717c7800/docs/FeatureManagement/FeatureFlag.v2.0.0.schema.json\n\n/**\n * A feature flag is a named property that can be toggled to enable or disable some feature of an application.\n */\nexport interface FeatureFlag {\n /**\n * An ID used to uniquely identify and reference the feature.\n */\n id: string;\n /**\n * A feature is OFF if enabled is false. If enabled is true, then the feature is ON if there are no conditions (null or empty) or if the conditions are satisfied.\n */\n enabled?: boolean;\n /**\n * The declaration of conditions used to dynamically enable the feature.\n */\n conditions?: FeatureEnablementConditions;\n /**\n * The list of variants defined for this feature. A variant represents a configuration value of a feature flag that can be a string, a number, a boolean, or a JSON object.\n */\n variants?: VariantDefinition[];\n /**\n * Determines how variants should be allocated for the feature to various users.\n */\n allocation?: VariantAllocation;\n /**\n * The declaration of options used to configure telemetry for this feature.\n */\n telemetry?: TelemetryOptions\n }\n\n /**\n * The declaration of conditions used to dynamically enable the feature\n */\n interface FeatureEnablementConditions {\n /**\n * Determines whether any or all registered client filters must be evaluated as true for the feature to be considered enabled.\n */\n requirement_type?: RequirementType;\n /**\n * Filters that must run on the client and be evaluated as true for the feature to be considered enabled.\n */\n client_filters?: ClientFilter[];\n }\n\n export type RequirementType = \"Any\" | \"All\";\n\n interface ClientFilter {\n /**\n * The name used to refer to a client filter.\n */\n name: string;\n /**\n * Parameters for a given client filter. A client filter can require any set of parameters of any type.\n */\n parameters?: Record<string, unknown>;\n }\n\n export interface VariantDefinition {\n /**\n * The name used to refer to a feature variant.\n */\n name: string;\n /**\n * The configuration value for this feature variant.\n */\n configuration_value?: unknown;\n /**\n * Overrides the enabled state of the feature if the given variant is assigned. Does not override the state if value is None.\n */\n status_override?: \"None\" | \"Enabled\" | \"Disabled\";\n }\n\n /**\n * Determines how variants should be allocated for the feature to various users.\n */\n interface VariantAllocation {\n /**\n * Specifies which variant should be used when the feature is considered disabled.\n */\n default_when_disabled?: string;\n /**\n * Specifies which variant should be used when the feature is considered enabled and no other allocation rules are applicable.\n */\n default_when_enabled?: string;\n /**\n * A list of objects, each containing a variant name and list of users for whom that variant should be used.\n */\n user?: UserAllocation[];\n /**\n * A list of objects, each containing a variant name and list of groups for which that variant should be used.\n */\n group?: GroupAllocation[];\n /**\n * A list of objects, each containing a variant name and percentage range for which that variant should be used.\n */\n percentile?: PercentileAllocation[]\n /**\n * The value percentile calculations are based on. The calculated percentile is consistent across features for a given user if the same nonempty seed is used.\n */\n seed?: string;\n }\n\n interface UserAllocation {\n /**\n * The name of the variant to use if the user allocation matches the current user.\n */\n variant: string;\n /**\n * Collection of users where if any match the current user, the variant specified in the user allocation is used.\n */\n users: string[];\n }\n\n interface GroupAllocation {\n /**\n * The name of the variant to use if the group allocation matches a group the current user is in.\n */\n variant: string;\n /**\n * Collection of groups where if the current user is in any of these groups, the variant specified in the group allocation is used.\n */\n groups: string[];\n }\n\n interface PercentileAllocation {\n /**\n * The name of the variant to use if the calculated percentile for the current user falls in the provided range.\n */\n variant: string;\n /**\n * The lower end of the percentage range for which this variant will be used.\n */\n from: number;\n /**\n * The upper end of the percentage range for which this variant will be used.\n */\n to: number;\n }\n\n /**\n * The declaration of options used to configure telemetry for this feature.\n */\n interface TelemetryOptions {\n /**\n * Indicates if telemetry is enabled.\n */\n enabled?: boolean;\n /**\n * A container for metadata that should be bundled with flag telemetry.\n */\n metadata?: Record<string, string>;\n }\n\n // Feature Management Section fed into feature manager.\n // Converted from https://github.com/Azure/AppConfiguration/blob/main/docs/FeatureManagement/FeatureManagement.v1.0.0.schema.json\n\n export const FEATURE_MANAGEMENT_KEY = \"feature_management\";\n export const FEATURE_FLAGS_KEY = \"feature_flags\";\n\n export interface FeatureManagementConfiguration {\n feature_management: FeatureManagement\n }\n\n /**\n * Declares feature management configuration.\n */\n export interface FeatureManagement {\n feature_flags: FeatureFlag[];\n }\n"],"names":[],"mappings":"AAAA;AACA;AA8JE;AACA;AAEO,MAAM,sBAAsB,GAAG,qBAAqB;AACpD,MAAM,iBAAiB,GAAG;;;;"}
@@ -0,0 +1,174 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT license.
3
+ /**
4
+ * Validates a feature flag object, checking if it conforms to the schema.
5
+ * @param featureFlag The feature flag object to validate.
6
+ */
7
+ function validateFeatureFlag(featureFlag) {
8
+ if (featureFlag === undefined) {
9
+ return; // no-op if feature flag is undefined, indicating that the feature flag is not found
10
+ }
11
+ if (featureFlag === null || typeof featureFlag !== "object") { // Note: typeof null = "object"
12
+ throw new TypeError("Feature flag must be an object.");
13
+ }
14
+ if (typeof featureFlag.id !== "string") {
15
+ throw new TypeError("Feature flag 'id' must be a string.");
16
+ }
17
+ if (featureFlag.enabled !== undefined && typeof featureFlag.enabled !== "boolean") {
18
+ throw new TypeError("Feature flag 'enabled' must be a boolean.");
19
+ }
20
+ if (featureFlag.conditions !== undefined) {
21
+ validateFeatureEnablementConditions(featureFlag.conditions);
22
+ }
23
+ if (featureFlag.variants !== undefined) {
24
+ validateVariants(featureFlag.variants);
25
+ }
26
+ if (featureFlag.allocation !== undefined) {
27
+ validateVariantAllocation(featureFlag.allocation);
28
+ }
29
+ if (featureFlag.telemetry !== undefined) {
30
+ validateTelemetryOptions(featureFlag.telemetry);
31
+ }
32
+ }
33
+ function validateFeatureEnablementConditions(conditions) {
34
+ if (typeof conditions !== "object") {
35
+ throw new TypeError("Feature flag 'conditions' must be an object.");
36
+ }
37
+ if (conditions.requirement_type !== undefined && conditions.requirement_type !== "Any" && conditions.requirement_type !== "All") {
38
+ throw new TypeError("'requirement_type' must be 'Any' or 'All'.");
39
+ }
40
+ if (conditions.client_filters !== undefined) {
41
+ validateClientFilters(conditions.client_filters);
42
+ }
43
+ }
44
+ function validateClientFilters(client_filters) {
45
+ if (!Array.isArray(client_filters)) {
46
+ throw new TypeError("Feature flag conditions 'client_filters' must be an array.");
47
+ }
48
+ for (const filter of client_filters) {
49
+ if (typeof filter.name !== "string") {
50
+ throw new TypeError("Client filter 'name' must be a string.");
51
+ }
52
+ if (filter.parameters !== undefined && typeof filter.parameters !== "object") {
53
+ throw new TypeError("Client filter 'parameters' must be an object.");
54
+ }
55
+ }
56
+ }
57
+ function validateVariants(variants) {
58
+ if (!Array.isArray(variants)) {
59
+ throw new TypeError("Feature flag 'variants' must be an array.");
60
+ }
61
+ for (const variant of variants) {
62
+ if (typeof variant.name !== "string") {
63
+ throw new TypeError("Variant 'name' must be a string.");
64
+ }
65
+ // skip configuration_value validation as it accepts any type
66
+ if (variant.status_override !== undefined && typeof variant.status_override !== "string") {
67
+ throw new TypeError("Variant 'status_override' must be a string.");
68
+ }
69
+ if (variant.status_override !== undefined && variant.status_override !== "None" && variant.status_override !== "Enabled" && variant.status_override !== "Disabled") {
70
+ throw new TypeError("Variant 'status_override' must be 'None', 'Enabled', or 'Disabled'.");
71
+ }
72
+ }
73
+ }
74
+ function validateVariantAllocation(allocation) {
75
+ if (typeof allocation !== "object") {
76
+ throw new TypeError("Variant 'allocation' must be an object.");
77
+ }
78
+ if (allocation.default_when_disabled !== undefined && typeof allocation.default_when_disabled !== "string") {
79
+ throw new TypeError("Variant allocation 'default_when_disabled' must be a string.");
80
+ }
81
+ if (allocation.default_when_enabled !== undefined && typeof allocation.default_when_enabled !== "string") {
82
+ throw new TypeError("Variant allocation 'default_when_enabled' must be a string.");
83
+ }
84
+ if (allocation.user !== undefined) {
85
+ validateUserVariantAllocation(allocation.user);
86
+ }
87
+ if (allocation.group !== undefined) {
88
+ validateGroupVariantAllocation(allocation.group);
89
+ }
90
+ if (allocation.percentile !== undefined) {
91
+ validatePercentileVariantAllocation(allocation.percentile);
92
+ }
93
+ if (allocation.seed !== undefined && typeof allocation.seed !== "string") {
94
+ throw new TypeError("Variant allocation 'seed' must be a string.");
95
+ }
96
+ }
97
+ function validateUserVariantAllocation(UserAllocations) {
98
+ if (!Array.isArray(UserAllocations)) {
99
+ throw new TypeError("Variant 'user' allocation must be an array.");
100
+ }
101
+ for (const allocation of UserAllocations) {
102
+ if (typeof allocation !== "object") {
103
+ throw new TypeError("Elements in variant 'user' allocation must be an object.");
104
+ }
105
+ if (typeof allocation.variant !== "string") {
106
+ throw new TypeError("User allocation 'variant' must be a string.");
107
+ }
108
+ if (!Array.isArray(allocation.users)) {
109
+ throw new TypeError("User allocation 'users' must be an array.");
110
+ }
111
+ for (const user of allocation.users) {
112
+ if (typeof user !== "string") {
113
+ throw new TypeError("Elements in user allocation 'users' must be strings.");
114
+ }
115
+ }
116
+ }
117
+ }
118
+ function validateGroupVariantAllocation(groupAllocations) {
119
+ if (!Array.isArray(groupAllocations)) {
120
+ throw new TypeError("Variant 'group' allocation must be an array.");
121
+ }
122
+ for (const allocation of groupAllocations) {
123
+ if (typeof allocation !== "object") {
124
+ throw new TypeError("Elements in variant 'group' allocation must be an object.");
125
+ }
126
+ if (typeof allocation.variant !== "string") {
127
+ throw new TypeError("Group allocation 'variant' must be a string.");
128
+ }
129
+ if (!Array.isArray(allocation.groups)) {
130
+ throw new TypeError("Group allocation 'groups' must be an array.");
131
+ }
132
+ for (const group of allocation.groups) {
133
+ if (typeof group !== "string") {
134
+ throw new TypeError("Elements in group allocation 'groups' must be strings.");
135
+ }
136
+ }
137
+ }
138
+ }
139
+ function validatePercentileVariantAllocation(percentileAllocations) {
140
+ if (!Array.isArray(percentileAllocations)) {
141
+ throw new TypeError("Variant 'percentile' allocation must be an array.");
142
+ }
143
+ for (const allocation of percentileAllocations) {
144
+ if (typeof allocation !== "object") {
145
+ throw new TypeError("Elements in variant 'percentile' allocation must be an object.");
146
+ }
147
+ if (typeof allocation.variant !== "string") {
148
+ throw new TypeError("Percentile allocation 'variant' must be a string.");
149
+ }
150
+ if (typeof allocation.from !== "number" || allocation.from < 0 || allocation.from > 100) {
151
+ throw new TypeError("Percentile allocation 'from' must be a number between 0 and 100.");
152
+ }
153
+ if (typeof allocation.to !== "number" || allocation.to < 0 || allocation.to > 100) {
154
+ throw new TypeError("Percentile allocation 'to' must be a number between 0 and 100.");
155
+ }
156
+ }
157
+ }
158
+ // #endregion
159
+ // #region Telemetry
160
+ function validateTelemetryOptions(telemetry) {
161
+ if (typeof telemetry !== "object") {
162
+ throw new TypeError("Feature flag 'telemetry' must be an object.");
163
+ }
164
+ if (telemetry.enabled !== undefined && typeof telemetry.enabled !== "boolean") {
165
+ throw new TypeError("Telemetry 'enabled' must be a boolean.");
166
+ }
167
+ if (telemetry.metadata !== undefined && typeof telemetry.metadata !== "object") {
168
+ throw new TypeError("Telemetry 'metadata' must be an object.");
169
+ }
170
+ }
171
+ // #endregion
172
+
173
+ export { validateFeatureFlag };
174
+ //# sourceMappingURL=validator.js.map
@@ -0,0 +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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"}
@@ -0,0 +1,62 @@
1
+ import { VariantAssignmentReason } from '../featureManager.js';
2
+ import { EVALUATION_EVENT_VERSION } from '../version.js';
3
+
4
+ // Copyright (c) Microsoft Corporation.
5
+ // Licensed under the MIT license.
6
+ const VERSION = "Version";
7
+ const FEATURE_NAME = "FeatureName";
8
+ const ENABLED = "Enabled";
9
+ const TARGETING_ID = "TargetingId";
10
+ const VARIANT = "Variant";
11
+ const VARIANT_ASSIGNMENT_REASON = "VariantAssignmentReason";
12
+ const DEFAULT_WHEN_ENABLED = "DefaultWhenEnabled";
13
+ const VARIANT_ASSIGNMENT_PERCENTAGE = "VariantAssignmentPercentage";
14
+ function createFeatureEvaluationEventProperties(result) {
15
+ if (result.feature === undefined) {
16
+ return undefined;
17
+ }
18
+ const eventProperties = {
19
+ [VERSION]: EVALUATION_EVENT_VERSION,
20
+ [FEATURE_NAME]: result.feature ? result.feature.id : "",
21
+ [ENABLED]: result.enabled ? "True" : "False",
22
+ // Ensure targetingId is string so that it will be placed in customDimensions
23
+ [TARGETING_ID]: result.targetingId ? result.targetingId.toString() : "",
24
+ [VARIANT]: result.variant ? result.variant.name : "",
25
+ [VARIANT_ASSIGNMENT_REASON]: result.variantAssignmentReason,
26
+ };
27
+ if (result.feature.allocation?.default_when_enabled) {
28
+ eventProperties[DEFAULT_WHEN_ENABLED] = result.feature.allocation.default_when_enabled;
29
+ }
30
+ if (result.variantAssignmentReason === VariantAssignmentReason.DefaultWhenEnabled) {
31
+ let percentileAllocationPercentage = 0;
32
+ if (result.variant !== undefined && result.feature.allocation !== undefined && result.feature.allocation.percentile !== undefined) {
33
+ for (const percentile of result.feature.allocation.percentile) {
34
+ percentileAllocationPercentage += percentile.to - percentile.from;
35
+ }
36
+ }
37
+ eventProperties[VARIANT_ASSIGNMENT_PERCENTAGE] = (100 - percentileAllocationPercentage).toString();
38
+ }
39
+ else if (result.variantAssignmentReason === VariantAssignmentReason.Percentile) {
40
+ let percentileAllocationPercentage = 0;
41
+ if (result.variant !== undefined && result.feature.allocation !== undefined && result.feature.allocation.percentile !== undefined) {
42
+ for (const percentile of result.feature.allocation.percentile) {
43
+ if (percentile.variant === result.variant.name) {
44
+ percentileAllocationPercentage += percentile.to - percentile.from;
45
+ }
46
+ }
47
+ }
48
+ eventProperties[VARIANT_ASSIGNMENT_PERCENTAGE] = percentileAllocationPercentage.toString();
49
+ }
50
+ const metadata = result.feature.telemetry?.metadata;
51
+ if (metadata) {
52
+ for (const key in metadata) {
53
+ if (!(key in eventProperties)) {
54
+ eventProperties[key] = metadata[key];
55
+ }
56
+ }
57
+ }
58
+ return eventProperties;
59
+ }
60
+
61
+ export { createFeatureEvaluationEventProperties };
62
+ //# sourceMappingURL=featureEvaluationEvent.js.map
@@ -0,0 +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, VariantAssignmentReason } 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\";\nconst DEFAULT_WHEN_ENABLED = \"DefaultWhenEnabled\";\nconst VARIANT_ASSIGNMENT_PERCENTAGE = \"VariantAssignmentPercentage\";\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 if (result.feature.allocation?.default_when_enabled) {\n eventProperties[DEFAULT_WHEN_ENABLED] = result.feature.allocation.default_when_enabled;\n }\n\n if (result.variantAssignmentReason === VariantAssignmentReason.DefaultWhenEnabled) {\n let percentileAllocationPercentage = 0;\n if (result.variant !== undefined && result.feature.allocation !== undefined && result.feature.allocation.percentile !== undefined) {\n for (const percentile of result.feature.allocation.percentile) {\n percentileAllocationPercentage += percentile.to - percentile.from;\n }\n }\n eventProperties[VARIANT_ASSIGNMENT_PERCENTAGE] = (100 - percentileAllocationPercentage).toString();\n }\n else if (result.variantAssignmentReason === VariantAssignmentReason.Percentile) {\n let percentileAllocationPercentage = 0;\n if (result.variant !== undefined && result.feature.allocation !== undefined && result.feature.allocation.percentile !== undefined) {\n for (const percentile of result.feature.allocation.percentile) {\n if (percentile.variant === result.variant.name) {\n percentileAllocationPercentage += percentile.to - percentile.from;\n }\n }\n }\n eventProperties[VARIANT_ASSIGNMENT_PERCENTAGE] = percentileAllocationPercentage.toString();\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":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT license.
3
- const VERSION = "2.0.0-preview.2";
3
+ const VERSION = "2.0.0-preview.3";
4
+ const EVALUATION_EVENT_VERSION = "1.0.0";
4
5
 
5
- export { VERSION };
6
+ export { EVALUATION_EVENT_VERSION, VERSION };
6
7
  //# sourceMappingURL=version.js.map
@@ -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-preview.2\";\n"],"names":[],"mappings":"AAAA;AACA;AAEO,MAAM,OAAO,GAAG;;;;"}
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-preview.3\";\nexport const EVALUATION_EVENT_VERSION = \"1.0.0\";\n"],"names":[],"mappings":"AAAA;AACA;AAEO,MAAM,OAAO,GAAG,kBAAkB;AAClC,MAAM,wBAAwB,GAAG;;;;"}