flagsmith-nodejs 6.1.0 → 7.0.2
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/.github/workflows/conventional-commit.yml +29 -0
- package/.github/workflows/publish.yml +20 -17
- package/.github/workflows/pull_request.yaml +36 -33
- package/.github/workflows/release-please.yml +18 -0
- package/.gitmodules +1 -0
- package/.husky/pre-commit +1 -0
- package/.nvmrc +1 -0
- package/.prettierrc.cjs +9 -1
- package/.release-please-manifest.json +1 -0
- package/CHANGELOG.md +592 -0
- package/CODEOWNERS +1 -0
- package/README.md +0 -2
- package/build/cjs/flagsmith-engine/environments/models.d.ts +2 -1
- package/build/cjs/flagsmith-engine/environments/models.js +3 -1
- package/build/cjs/flagsmith-engine/environments/util.js +1 -1
- package/build/cjs/flagsmith-engine/evaluation/evaluationContext/evaluationContext.types.d.ts +230 -0
- package/build/cjs/flagsmith-engine/evaluation/evaluationContext/evaluationContext.types.js +8 -0
- package/build/cjs/flagsmith-engine/evaluation/evaluationContext/mappers.d.ts +5 -0
- package/build/cjs/flagsmith-engine/evaluation/evaluationContext/mappers.js +156 -0
- package/build/cjs/flagsmith-engine/evaluation/evaluationContext/types.d.ts +216 -0
- package/build/cjs/flagsmith-engine/evaluation/evaluationContext/types.js +8 -0
- package/build/cjs/flagsmith-engine/evaluation/evaluationResult/evaluationResult.types.d.ts +68 -0
- package/build/cjs/flagsmith-engine/evaluation/evaluationResult/evaluationResult.types.js +8 -0
- package/build/cjs/flagsmith-engine/evaluation/models.d.ts +50 -0
- package/build/cjs/flagsmith-engine/evaluation/models.js +26 -0
- package/build/cjs/flagsmith-engine/features/models.js +1 -1
- package/build/cjs/flagsmith-engine/features/types.d.ts +5 -0
- package/build/cjs/flagsmith-engine/features/types.js +9 -0
- package/build/cjs/flagsmith-engine/features/util.d.ts +1 -0
- package/build/cjs/flagsmith-engine/features/util.js +5 -1
- package/build/cjs/flagsmith-engine/index.d.ts +61 -9
- package/build/cjs/flagsmith-engine/index.js +176 -56
- package/build/cjs/flagsmith-engine/segments/constants.d.ts +1 -0
- package/build/cjs/flagsmith-engine/segments/constants.js +2 -1
- package/build/cjs/flagsmith-engine/segments/evaluators.d.ts +41 -7
- package/build/cjs/flagsmith-engine/segments/evaluators.js +136 -24
- package/build/cjs/flagsmith-engine/segments/models.d.ts +9 -4
- package/build/cjs/flagsmith-engine/segments/models.js +115 -13
- package/build/cjs/flagsmith-engine/utils/hashing/index.d.ts +1 -1
- package/build/cjs/flagsmith-engine/utils/hashing/index.js +4 -4
- package/build/cjs/sdk/analytics.js +3 -1
- package/build/cjs/sdk/index.d.ts +1 -3
- package/build/cjs/sdk/index.js +63 -24
- package/build/cjs/sdk/models.d.ts +8 -1
- package/build/cjs/sdk/models.js +29 -1
- package/build/cjs/sdk/utils.d.ts +1 -0
- package/build/cjs/sdk/utils.js +14 -1
- package/build/esm/flagsmith-engine/environments/models.d.ts +2 -1
- package/build/esm/flagsmith-engine/environments/models.js +3 -1
- package/build/esm/flagsmith-engine/environments/util.js +1 -1
- package/build/esm/flagsmith-engine/evaluation/evaluationContext/evaluationContext.types.d.ts +230 -0
- package/build/esm/flagsmith-engine/evaluation/evaluationContext/evaluationContext.types.js +7 -0
- package/build/esm/flagsmith-engine/evaluation/evaluationContext/mappers.d.ts +5 -0
- package/build/esm/flagsmith-engine/evaluation/evaluationContext/mappers.js +152 -0
- package/build/esm/flagsmith-engine/evaluation/evaluationContext/types.d.ts +216 -0
- package/build/esm/flagsmith-engine/evaluation/evaluationContext/types.js +7 -0
- package/build/esm/flagsmith-engine/evaluation/evaluationResult/evaluationResult.types.d.ts +68 -0
- package/build/esm/flagsmith-engine/evaluation/evaluationResult/evaluationResult.types.js +7 -0
- package/build/esm/flagsmith-engine/evaluation/models.d.ts +50 -0
- package/build/esm/flagsmith-engine/evaluation/models.js +9 -0
- package/build/esm/flagsmith-engine/features/models.js +2 -2
- package/build/esm/flagsmith-engine/features/types.d.ts +5 -0
- package/build/esm/flagsmith-engine/features/types.js +6 -0
- package/build/esm/flagsmith-engine/features/util.d.ts +1 -0
- package/build/esm/flagsmith-engine/features/util.js +3 -0
- package/build/esm/flagsmith-engine/index.d.ts +61 -9
- package/build/esm/flagsmith-engine/index.js +161 -43
- package/build/esm/flagsmith-engine/segments/constants.d.ts +1 -0
- package/build/esm/flagsmith-engine/segments/constants.js +1 -0
- package/build/esm/flagsmith-engine/segments/evaluators.d.ts +41 -7
- package/build/esm/flagsmith-engine/segments/evaluators.js +137 -25
- package/build/esm/flagsmith-engine/segments/models.d.ts +9 -4
- package/build/esm/flagsmith-engine/segments/models.js +115 -13
- package/build/esm/flagsmith-engine/utils/hashing/index.d.ts +1 -1
- package/build/esm/flagsmith-engine/utils/hashing/index.js +2 -2
- package/build/esm/sdk/analytics.js +3 -1
- package/build/esm/sdk/index.d.ts +1 -3
- package/build/esm/sdk/index.js +63 -24
- package/build/esm/sdk/models.d.ts +8 -1
- package/build/esm/sdk/models.js +29 -1
- package/build/esm/sdk/utils.d.ts +1 -0
- package/build/esm/sdk/utils.js +12 -0
- package/flagsmith-engine/environments/models.ts +3 -1
- package/flagsmith-engine/environments/util.ts +2 -1
- package/flagsmith-engine/evaluation/evaluationContext/evaluationContext.types.ts +247 -0
- package/flagsmith-engine/evaluation/evaluationContext/mappers.ts +204 -0
- package/flagsmith-engine/evaluation/evaluationContext/types.ts +233 -0
- package/flagsmith-engine/evaluation/evaluationResult/evaluationResult.types.ts +71 -0
- package/flagsmith-engine/evaluation/models.ts +96 -0
- package/flagsmith-engine/features/models.ts +3 -2
- package/flagsmith-engine/features/types.ts +5 -0
- package/flagsmith-engine/features/util.ts +4 -0
- package/flagsmith-engine/index.ts +229 -72
- package/flagsmith-engine/segments/constants.ts +1 -0
- package/flagsmith-engine/segments/evaluators.ts +178 -62
- package/flagsmith-engine/segments/models.ts +171 -23
- package/flagsmith-engine/utils/hashing/index.ts +2 -2
- package/package.json +13 -2
- package/release-please-config.json +62 -0
- package/sdk/analytics.ts +3 -1
- package/sdk/index.ts +89 -30
- package/sdk/models.ts +44 -2
- package/sdk/utils.ts +13 -0
- package/tests/engine/e2e/engine.test.ts +43 -38
- package/tests/engine/unit/engine.test.ts +306 -60
- package/tests/engine/unit/mappers.test.ts +353 -0
- package/tests/engine/unit/segments/segment_evaluators.test.ts +391 -49
- package/tests/engine/unit/segments/segments_model.test.ts +85 -0
- package/tests/engine/unit/utils/utils.test.ts +7 -7
- package/tests/engine/unit/utils.ts +1 -1
- package/tests/sdk/analytics.test.ts +6 -1
- package/tests/sdk/data/environment.json +1 -0
- package/tests/sdk/flagsmith-environment-flags.test.ts +28 -0
- package/tests/sdk/flagsmith-identity-flags.test.ts +11 -2
- package/tests/sdk/flagsmith.test.ts +190 -3
- package/tests/sdk/offline-handlers.test.ts +3 -1
- package/vitest.config.esm.ts +34 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { FlagResult, FeatureMetadata, SegmentMetadata } from './evaluationResult/evaluationResult.types.js';
|
|
2
|
+
import type { FeatureContext, EnvironmentContext, IdentityContext, SegmentContext } from './evaluationContext/evaluationContext.types.js';
|
|
3
|
+
export * from './evaluationContext/evaluationContext.types.js';
|
|
4
|
+
export declare enum SegmentSource {
|
|
5
|
+
API = "api",
|
|
6
|
+
IDENTITY_OVERRIDE = "identity_override"
|
|
7
|
+
}
|
|
8
|
+
export interface SDKFeatureMetadata extends FeatureMetadata {
|
|
9
|
+
id: number;
|
|
10
|
+
}
|
|
11
|
+
export interface FeatureContextWithMetadata<T extends FeatureMetadata = FeatureMetadata> extends FeatureContext {
|
|
12
|
+
metadata: T;
|
|
13
|
+
[k: string]: unknown;
|
|
14
|
+
}
|
|
15
|
+
export type FeaturesWithMetadata<T extends FeatureMetadata = FeatureMetadata> = {
|
|
16
|
+
[k: string]: FeatureContextWithMetadata<T>;
|
|
17
|
+
};
|
|
18
|
+
export type FlagResultWithMetadata<T extends FeatureMetadata = FeatureMetadata> = FlagResult & {
|
|
19
|
+
metadata: T;
|
|
20
|
+
};
|
|
21
|
+
export type EvaluationResultFlags<T extends FeatureMetadata = FeatureMetadata> = Record<string, FlagResultWithMetadata<T>>;
|
|
22
|
+
export interface SDKSegmentMetadata extends SegmentMetadata {
|
|
23
|
+
id?: number;
|
|
24
|
+
source?: SegmentSource;
|
|
25
|
+
}
|
|
26
|
+
export interface SegmentContextWithMetadata<T extends SegmentMetadata = SegmentMetadata> extends SegmentContext {
|
|
27
|
+
metadata: T;
|
|
28
|
+
overrides?: FeatureContextWithMetadata<FeatureMetadata>[];
|
|
29
|
+
}
|
|
30
|
+
export type SegmentsWithMetadata<T extends SegmentMetadata = SegmentMetadata> = {
|
|
31
|
+
[k: string]: SegmentContextWithMetadata<T>;
|
|
32
|
+
};
|
|
33
|
+
export interface SegmentResultWithMetadata {
|
|
34
|
+
name: string;
|
|
35
|
+
metadata: SDKSegmentMetadata;
|
|
36
|
+
}
|
|
37
|
+
export type EvaluationResultSegments = SegmentResultWithMetadata[];
|
|
38
|
+
export interface GenericEvaluationContext<T extends FeatureMetadata = FeatureMetadata, S extends SegmentMetadata = SegmentMetadata> {
|
|
39
|
+
environment: EnvironmentContext;
|
|
40
|
+
identity?: IdentityContext | null;
|
|
41
|
+
segments?: SegmentsWithMetadata<S>;
|
|
42
|
+
features?: FeaturesWithMetadata<T>;
|
|
43
|
+
[k: string]: unknown;
|
|
44
|
+
}
|
|
45
|
+
export type EvaluationContextWithMetadata = GenericEvaluationContext<SDKFeatureMetadata, SDKSegmentMetadata>;
|
|
46
|
+
export type EvaluationResult<T extends FeatureMetadata = FeatureMetadata> = {
|
|
47
|
+
flags: EvaluationResultFlags<T>;
|
|
48
|
+
segments: EvaluationResultSegments;
|
|
49
|
+
};
|
|
50
|
+
export type EvaluationResultWithMetadata = EvaluationResult<SDKFeatureMetadata>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// This file is the entry point for the evaluation module types
|
|
3
|
+
// All types from evaluations should be at least imported here and re-exported
|
|
4
|
+
// Do not use types directly from generated files
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
17
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.SegmentSource = void 0;
|
|
21
|
+
__exportStar(require("./evaluationContext/evaluationContext.types.js"), exports);
|
|
22
|
+
var SegmentSource;
|
|
23
|
+
(function (SegmentSource) {
|
|
24
|
+
SegmentSource["API"] = "api";
|
|
25
|
+
SegmentSource["IDENTITY_OVERRIDE"] = "identity_override";
|
|
26
|
+
})(SegmentSource = exports.SegmentSource || (exports.SegmentSource = {}));
|
|
@@ -93,7 +93,7 @@ class FeatureStateModel {
|
|
|
93
93
|
return myValue.multivariateFeatureOption.value;
|
|
94
94
|
default:
|
|
95
95
|
if (percentageValue === undefined) {
|
|
96
|
-
percentageValue = (0, index_js_1.
|
|
96
|
+
percentageValue = (0, index_js_1.getHashedPercentageForObjIds)([
|
|
97
97
|
this.djangoID || this.featurestateUUID,
|
|
98
98
|
identityID
|
|
99
99
|
]);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TARGETING_REASONS = void 0;
|
|
4
|
+
var TARGETING_REASONS;
|
|
5
|
+
(function (TARGETING_REASONS) {
|
|
6
|
+
TARGETING_REASONS["DEFAULT"] = "DEFAULT";
|
|
7
|
+
TARGETING_REASONS["TARGETING_MATCH"] = "TARGETING_MATCH";
|
|
8
|
+
TARGETING_REASONS["SPLIT"] = "SPLIT";
|
|
9
|
+
})(TARGETING_REASONS = exports.TARGETING_REASONS || (exports.TARGETING_REASONS = {}));
|
|
@@ -2,3 +2,4 @@ import { FeatureModel, FeatureSegment, FeatureStateModel } from './models.js';
|
|
|
2
2
|
export declare function buildFeatureModel(featuresModelJSON: any): FeatureModel;
|
|
3
3
|
export declare function buildFeatureStateModel(featuresStateModelJSON: any): FeatureStateModel;
|
|
4
4
|
export declare function buildFeatureSegment(featureSegmentJSON: any): FeatureSegment;
|
|
5
|
+
export declare function uuidToBigInt(uuid: string): BigInt;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.buildFeatureSegment = exports.buildFeatureStateModel = exports.buildFeatureModel = void 0;
|
|
3
|
+
exports.uuidToBigInt = exports.buildFeatureSegment = exports.buildFeatureStateModel = exports.buildFeatureModel = void 0;
|
|
4
4
|
const models_js_1 = require("./models.js");
|
|
5
5
|
function buildFeatureModel(featuresModelJSON) {
|
|
6
6
|
return new models_js_1.FeatureModel(featuresModelJSON.id, featuresModelJSON.name, featuresModelJSON.type);
|
|
@@ -25,3 +25,7 @@ function buildFeatureSegment(featureSegmentJSON) {
|
|
|
25
25
|
return new models_js_1.FeatureSegment(featureSegmentJSON.priority);
|
|
26
26
|
}
|
|
27
27
|
exports.buildFeatureSegment = buildFeatureSegment;
|
|
28
|
+
function uuidToBigInt(uuid) {
|
|
29
|
+
return BigInt('0x' + uuid.replace(/-/g, ''));
|
|
30
|
+
}
|
|
31
|
+
exports.uuidToBigInt = uuidToBigInt;
|
|
@@ -1,14 +1,66 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { IdentityModel } from './identities/models.js';
|
|
4
|
-
import { TraitModel } from './identities/traits/models.js';
|
|
1
|
+
import { EvaluationContextWithMetadata, EvaluationResultSegments, EvaluationResultWithMetadata, FeatureContextWithMetadata, SDKFeatureMetadata } from './evaluation/models.js';
|
|
2
|
+
import { EvaluationResultFlags } from './evaluation/models.js';
|
|
5
3
|
export { EnvironmentModel } from './environments/models.js';
|
|
6
|
-
export { FeatureModel, FeatureStateModel } from './features/models.js';
|
|
7
4
|
export { IdentityModel } from './identities/models.js';
|
|
8
5
|
export { TraitModel } from './identities/traits/models.js';
|
|
9
6
|
export { SegmentModel } from './segments/models.js';
|
|
7
|
+
export { FeatureModel, FeatureStateModel } from './features/models.js';
|
|
10
8
|
export { OrganisationModel } from './organisations/models.js';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
type SegmentOverride = {
|
|
10
|
+
feature: FeatureContextWithMetadata<SDKFeatureMetadata>;
|
|
11
|
+
segmentName: string;
|
|
12
|
+
};
|
|
13
|
+
export type SegmentOverrides = Record<string, SegmentOverride>;
|
|
14
|
+
/**
|
|
15
|
+
* Evaluates flags and segments for the given context.
|
|
16
|
+
*
|
|
17
|
+
* This is the main entry point for the evaluation engine. It processes segments,
|
|
18
|
+
* applies feature overrides based on segment priority, and returns the final flag states with
|
|
19
|
+
* evaluation reasons.
|
|
20
|
+
*
|
|
21
|
+
* @param context - EvaluationContext containing environment, identity, and segment data
|
|
22
|
+
* @returns EvaluationResult with flags, segments, and original context
|
|
23
|
+
*/
|
|
24
|
+
export declare function getEvaluationResult(context: EvaluationContextWithMetadata): EvaluationResultWithMetadata;
|
|
25
|
+
/**
|
|
26
|
+
* Evaluates which segments the identity belongs to and collects feature overrides.
|
|
27
|
+
*
|
|
28
|
+
* @param context - EvaluationContext containing identity and segment definitions
|
|
29
|
+
* @returns Object containing segments the identity belongs to and any feature overrides
|
|
30
|
+
*/
|
|
31
|
+
export declare function evaluateSegments(context: EvaluationContextWithMetadata): {
|
|
32
|
+
segments: EvaluationResultSegments;
|
|
33
|
+
segmentOverrides: Record<string, SegmentOverride>;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Processes feature overrides from segments, applying priority rules.
|
|
37
|
+
*
|
|
38
|
+
* When multiple segments override the same feature, the segment with
|
|
39
|
+
* higher priority (lower numeric value) takes precedence.
|
|
40
|
+
*
|
|
41
|
+
* @param identitySegments - Segments that the identity belongs to
|
|
42
|
+
* @returns Map of feature keys to their highest-priority segment overrides
|
|
43
|
+
*/
|
|
44
|
+
export declare function processSegmentOverrides(identitySegments: any[]): Record<string, SegmentOverride>;
|
|
45
|
+
/**
|
|
46
|
+
* Evaluates all features in the context, applying segment overrides where applicable.
|
|
47
|
+
* For each feature:
|
|
48
|
+
* - Checks if a segment override exists
|
|
49
|
+
* - Uses override values if present, otherwise evaluates the base feature
|
|
50
|
+
* - Determines appropriate evaluation reason
|
|
51
|
+
* - Handles multivariate evaluation for features without overrides
|
|
52
|
+
*
|
|
53
|
+
* @param context - EvaluationContext containing features and identity
|
|
54
|
+
* @param segmentOverrides - Map of feature keys to their segment overrides
|
|
55
|
+
* @returns EvaluationResultFlags containing evaluated flag results
|
|
56
|
+
*/
|
|
57
|
+
export declare function evaluateFeatures(context: EvaluationContextWithMetadata, segmentOverrides: Record<string, SegmentOverride>): EvaluationResultFlags<SDKFeatureMetadata>;
|
|
58
|
+
export declare function shouldApplyOverride(override: any, existingOverrides: Record<string, SegmentOverride>): boolean;
|
|
59
|
+
export declare function isHigherPriority(priorityA: number | undefined, priorityB: number | undefined): boolean;
|
|
60
|
+
export type TargetingMatchReason = {
|
|
61
|
+
type: 'SEGMENT';
|
|
62
|
+
override: SegmentOverride;
|
|
63
|
+
} | {
|
|
64
|
+
type: 'SPLIT';
|
|
65
|
+
weight: number;
|
|
66
|
+
};
|
|
@@ -1,76 +1,196 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.isHigherPriority = exports.shouldApplyOverride = exports.evaluateFeatures = exports.processSegmentOverrides = exports.evaluateSegments = exports.getEvaluationResult = exports.OrganisationModel = exports.FeatureStateModel = exports.FeatureModel = exports.SegmentModel = exports.TraitModel = exports.IdentityModel = exports.EnvironmentModel = void 0;
|
|
4
4
|
const evaluators_js_1 = require("./segments/evaluators.js");
|
|
5
|
-
const
|
|
5
|
+
const types_js_1 = require("./features/types.js");
|
|
6
|
+
const index_js_1 = require("./utils/hashing/index.js");
|
|
6
7
|
var models_js_1 = require("./environments/models.js");
|
|
7
8
|
Object.defineProperty(exports, "EnvironmentModel", { enumerable: true, get: function () { return models_js_1.EnvironmentModel; } });
|
|
8
|
-
var models_js_2 = require("./
|
|
9
|
-
Object.defineProperty(exports, "
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Object.defineProperty(exports, "
|
|
9
|
+
var models_js_2 = require("./identities/models.js");
|
|
10
|
+
Object.defineProperty(exports, "IdentityModel", { enumerable: true, get: function () { return models_js_2.IdentityModel; } });
|
|
11
|
+
var models_js_3 = require("./identities/traits/models.js");
|
|
12
|
+
Object.defineProperty(exports, "TraitModel", { enumerable: true, get: function () { return models_js_3.TraitModel; } });
|
|
13
|
+
var models_js_4 = require("./segments/models.js");
|
|
14
|
+
Object.defineProperty(exports, "SegmentModel", { enumerable: true, get: function () { return models_js_4.SegmentModel; } });
|
|
15
|
+
var models_js_5 = require("./features/models.js");
|
|
16
|
+
Object.defineProperty(exports, "FeatureModel", { enumerable: true, get: function () { return models_js_5.FeatureModel; } });
|
|
17
|
+
Object.defineProperty(exports, "FeatureStateModel", { enumerable: true, get: function () { return models_js_5.FeatureStateModel; } });
|
|
17
18
|
var models_js_6 = require("./organisations/models.js");
|
|
18
19
|
Object.defineProperty(exports, "OrganisationModel", { enumerable: true, get: function () { return models_js_6.OrganisationModel; } });
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Evaluates flags and segments for the given context.
|
|
22
|
+
*
|
|
23
|
+
* This is the main entry point for the evaluation engine. It processes segments,
|
|
24
|
+
* applies feature overrides based on segment priority, and returns the final flag states with
|
|
25
|
+
* evaluation reasons.
|
|
26
|
+
*
|
|
27
|
+
* @param context - EvaluationContext containing environment, identity, and segment data
|
|
28
|
+
* @returns EvaluationResult with flags, segments, and original context
|
|
29
|
+
*/
|
|
30
|
+
function getEvaluationResult(context) {
|
|
31
|
+
const enrichedContext = getEnrichedContext(context);
|
|
32
|
+
const { segments, segmentOverrides } = evaluateSegments(enrichedContext);
|
|
33
|
+
const flags = evaluateFeatures(enrichedContext, segmentOverrides);
|
|
34
|
+
return { flags, segments };
|
|
35
|
+
}
|
|
36
|
+
exports.getEvaluationResult = getEvaluationResult;
|
|
37
|
+
function getEnrichedContext(context) {
|
|
38
|
+
const identityKey = getIdentityKey(context);
|
|
39
|
+
if (!identityKey)
|
|
40
|
+
return context;
|
|
41
|
+
return {
|
|
42
|
+
...context,
|
|
43
|
+
...(context.identity && {
|
|
44
|
+
identity: {
|
|
45
|
+
identifier: context.identity.identifier,
|
|
46
|
+
key: identityKey,
|
|
47
|
+
traits: context.identity.traits || {}
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Evaluates which segments the identity belongs to and collects feature overrides.
|
|
54
|
+
*
|
|
55
|
+
* @param context - EvaluationContext containing identity and segment definitions
|
|
56
|
+
* @returns Object containing segments the identity belongs to and any feature overrides
|
|
57
|
+
*/
|
|
58
|
+
function evaluateSegments(context) {
|
|
59
|
+
if (!context.identity || !context.segments) {
|
|
60
|
+
return {
|
|
61
|
+
segments: [],
|
|
62
|
+
segmentOverrides: {}
|
|
63
|
+
};
|
|
24
64
|
}
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
65
|
+
const identitySegments = (0, evaluators_js_1.getIdentitySegments)(context);
|
|
66
|
+
const segments = identitySegments.map(segment => ({
|
|
67
|
+
name: segment.name,
|
|
68
|
+
...(segment.metadata
|
|
69
|
+
? {
|
|
70
|
+
metadata: {
|
|
71
|
+
...segment.metadata
|
|
32
72
|
}
|
|
33
73
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
74
|
+
: {})
|
|
75
|
+
}));
|
|
76
|
+
const segmentOverrides = processSegmentOverrides(identitySegments);
|
|
77
|
+
return { segments, segmentOverrides };
|
|
78
|
+
}
|
|
79
|
+
exports.evaluateSegments = evaluateSegments;
|
|
80
|
+
/**
|
|
81
|
+
* Processes feature overrides from segments, applying priority rules.
|
|
82
|
+
*
|
|
83
|
+
* When multiple segments override the same feature, the segment with
|
|
84
|
+
* higher priority (lower numeric value) takes precedence.
|
|
85
|
+
*
|
|
86
|
+
* @param identitySegments - Segments that the identity belongs to
|
|
87
|
+
* @returns Map of feature keys to their highest-priority segment overrides
|
|
88
|
+
*/
|
|
89
|
+
function processSegmentOverrides(identitySegments) {
|
|
90
|
+
const segmentOverrides = {};
|
|
91
|
+
for (const segment of identitySegments) {
|
|
92
|
+
if (!segment.overrides)
|
|
93
|
+
continue;
|
|
94
|
+
const overridesList = Array.isArray(segment.overrides) ? segment.overrides : [];
|
|
95
|
+
for (const override of overridesList) {
|
|
96
|
+
if (shouldApplyOverride(override, segmentOverrides)) {
|
|
97
|
+
segmentOverrides[override.name] = {
|
|
98
|
+
feature: override,
|
|
99
|
+
segmentName: segment.name
|
|
100
|
+
};
|
|
101
|
+
}
|
|
41
102
|
}
|
|
42
103
|
}
|
|
43
|
-
return
|
|
104
|
+
return segmentOverrides;
|
|
44
105
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
106
|
+
exports.processSegmentOverrides = processSegmentOverrides;
|
|
107
|
+
/**
|
|
108
|
+
* Evaluates all features in the context, applying segment overrides where applicable.
|
|
109
|
+
* For each feature:
|
|
110
|
+
* - Checks if a segment override exists
|
|
111
|
+
* - Uses override values if present, otherwise evaluates the base feature
|
|
112
|
+
* - Determines appropriate evaluation reason
|
|
113
|
+
* - Handles multivariate evaluation for features without overrides
|
|
114
|
+
*
|
|
115
|
+
* @param context - EvaluationContext containing features and identity
|
|
116
|
+
* @param segmentOverrides - Map of feature keys to their segment overrides
|
|
117
|
+
* @returns EvaluationResultFlags containing evaluated flag results
|
|
118
|
+
*/
|
|
119
|
+
function evaluateFeatures(context, segmentOverrides) {
|
|
120
|
+
const flags = {};
|
|
121
|
+
for (const feature of Object.values(context.features || {})) {
|
|
122
|
+
const segmentOverride = segmentOverrides[feature.name];
|
|
123
|
+
const finalFeature = segmentOverride ? segmentOverride.feature : feature;
|
|
124
|
+
const { value: evaluatedValue, reason: evaluatedReason } = evaluateFeatureValue(finalFeature, getIdentityKey(context));
|
|
125
|
+
flags[finalFeature.name] = {
|
|
126
|
+
name: finalFeature.name,
|
|
127
|
+
enabled: finalFeature.enabled,
|
|
128
|
+
value: evaluatedValue,
|
|
129
|
+
...(finalFeature.metadata ? { metadata: finalFeature.metadata } : {}),
|
|
130
|
+
reason: evaluatedReason ??
|
|
131
|
+
getTargetingMatchReason({ type: 'SEGMENT', override: segmentOverride })
|
|
132
|
+
};
|
|
50
133
|
}
|
|
51
|
-
return
|
|
134
|
+
return flags;
|
|
52
135
|
}
|
|
53
|
-
exports.
|
|
54
|
-
function
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return featureStates.filter(fs => !!fs.enabled);
|
|
136
|
+
exports.evaluateFeatures = evaluateFeatures;
|
|
137
|
+
function evaluateFeatureValue(feature, identityKey) {
|
|
138
|
+
if (!!feature.variants && feature.variants.length > 0 && !!identityKey) {
|
|
139
|
+
return getMultivariateFeatureValue(feature, identityKey);
|
|
58
140
|
}
|
|
59
|
-
return
|
|
141
|
+
return { value: feature.value, reason: undefined };
|
|
60
142
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Evaluates a multivariate feature flag to determine which variant value to return for a given identity.
|
|
145
|
+
*
|
|
146
|
+
* Uses deterministic hashing to ensure the same identity always receives the same variant,
|
|
147
|
+
* while distributing variants according to their configured weight percentages.
|
|
148
|
+
*
|
|
149
|
+
* @param feature - The feature context containing variants and their weights
|
|
150
|
+
* @param identityKey - The identity key used for deterministic variant selection
|
|
151
|
+
* @returns The variant value if the identity falls within a variant's range, otherwise the default feature value
|
|
152
|
+
*/
|
|
153
|
+
function getMultivariateFeatureValue(feature, identityKey) {
|
|
154
|
+
const percentageValue = (0, index_js_1.getHashedPercentageForObjIds)([feature.key, identityKey]);
|
|
155
|
+
const sortedVariants = [...(feature?.variants || [])].sort((a, b) => {
|
|
156
|
+
return (a.priority ?? Infinity) - (b.priority ?? Infinity);
|
|
157
|
+
});
|
|
158
|
+
let startPercentage = 0;
|
|
159
|
+
for (const variant of sortedVariants) {
|
|
160
|
+
const limit = startPercentage + variant.weight;
|
|
161
|
+
if (startPercentage <= percentageValue && percentageValue < limit) {
|
|
162
|
+
return {
|
|
163
|
+
value: variant.value,
|
|
164
|
+
reason: getTargetingMatchReason({ type: 'SPLIT', weight: variant.weight })
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
startPercentage = limit;
|
|
66
168
|
}
|
|
67
|
-
return
|
|
169
|
+
return { value: feature.value, reason: undefined };
|
|
68
170
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return environment.featureStates.filter(fs => !!fs.enabled);
|
|
73
|
-
}
|
|
74
|
-
return environment.featureStates;
|
|
171
|
+
function shouldApplyOverride(override, existingOverrides) {
|
|
172
|
+
const currentOverride = existingOverrides[override.name];
|
|
173
|
+
return (!currentOverride || isHigherPriority(override.priority, currentOverride.feature.priority));
|
|
75
174
|
}
|
|
76
|
-
exports.
|
|
175
|
+
exports.shouldApplyOverride = shouldApplyOverride;
|
|
176
|
+
function isHigherPriority(priorityA, priorityB) {
|
|
177
|
+
return (priorityA ?? Infinity) < (priorityB ?? Infinity);
|
|
178
|
+
}
|
|
179
|
+
exports.isHigherPriority = isHigherPriority;
|
|
180
|
+
const getTargetingMatchReason = (matchObject) => {
|
|
181
|
+
const { type } = matchObject;
|
|
182
|
+
if (type === 'SEGMENT') {
|
|
183
|
+
return matchObject.override
|
|
184
|
+
? `${types_js_1.TARGETING_REASONS.TARGETING_MATCH}; segment=${matchObject.override.segmentName}`
|
|
185
|
+
: types_js_1.TARGETING_REASONS.DEFAULT;
|
|
186
|
+
}
|
|
187
|
+
if (type === 'SPLIT') {
|
|
188
|
+
return `${types_js_1.TARGETING_REASONS.SPLIT}; weight=${matchObject.weight}`;
|
|
189
|
+
}
|
|
190
|
+
return types_js_1.TARGETING_REASONS.DEFAULT;
|
|
191
|
+
};
|
|
192
|
+
const getIdentityKey = (context) => {
|
|
193
|
+
if (!context.identity)
|
|
194
|
+
return undefined;
|
|
195
|
+
return context.identity.key || `${context.environment.key}_${context.identity?.identifier}`;
|
|
196
|
+
};
|
|
@@ -2,6 +2,7 @@ export declare const ALL_RULE = "ALL";
|
|
|
2
2
|
export declare const ANY_RULE = "ANY";
|
|
3
3
|
export declare const NONE_RULE = "NONE";
|
|
4
4
|
export declare const RULE_TYPES: string[];
|
|
5
|
+
export declare const IDENTITY_OVERRIDE_SEGMENT_NAME = "identity_overrides";
|
|
5
6
|
export declare const EQUAL = "EQUAL";
|
|
6
7
|
export declare const GREATER_THAN = "GREATER_THAN";
|
|
7
8
|
export declare const LESS_THAN = "LESS_THAN";
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CONDITION_OPERATORS = exports.IN = exports.MODULO = exports.IS_NOT_SET = exports.IS_SET = exports.PERCENTAGE_SPLIT = exports.REGEX = exports.NOT_EQUAL = exports.NOT_CONTAINS = exports.GREATER_THAN_INCLUSIVE = exports.CONTAINS = exports.LESS_THAN_INCLUSIVE = exports.LESS_THAN = exports.GREATER_THAN = exports.EQUAL = exports.RULE_TYPES = exports.NONE_RULE = exports.ANY_RULE = exports.ALL_RULE = void 0;
|
|
3
|
+
exports.CONDITION_OPERATORS = exports.IN = exports.MODULO = exports.IS_NOT_SET = exports.IS_SET = exports.PERCENTAGE_SPLIT = exports.REGEX = exports.NOT_EQUAL = exports.NOT_CONTAINS = exports.GREATER_THAN_INCLUSIVE = exports.CONTAINS = exports.LESS_THAN_INCLUSIVE = exports.LESS_THAN = exports.GREATER_THAN = exports.EQUAL = exports.IDENTITY_OVERRIDE_SEGMENT_NAME = exports.RULE_TYPES = exports.NONE_RULE = exports.ANY_RULE = exports.ALL_RULE = void 0;
|
|
4
4
|
// Segment Rules
|
|
5
5
|
exports.ALL_RULE = 'ALL';
|
|
6
6
|
exports.ANY_RULE = 'ANY';
|
|
7
7
|
exports.NONE_RULE = 'NONE';
|
|
8
8
|
exports.RULE_TYPES = [exports.ALL_RULE, exports.ANY_RULE, exports.NONE_RULE];
|
|
9
|
+
exports.IDENTITY_OVERRIDE_SEGMENT_NAME = 'identity_overrides';
|
|
9
10
|
// Segment Condition Operators
|
|
10
11
|
exports.EQUAL = 'EQUAL';
|
|
11
12
|
exports.GREATER_THAN = 'GREATER_THAN';
|
|
@@ -1,7 +1,41 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import { GenericEvaluationContext, InSegmentCondition, SegmentCondition, SegmentContext } from '../evaluation/models.js';
|
|
2
|
+
/**
|
|
3
|
+
* Returns all segments that the identity belongs to based on segment rules evaluation.
|
|
4
|
+
*
|
|
5
|
+
* An identity belongs to a segment if it matches ALL of the segment's rules.
|
|
6
|
+
* If the context has no identity or segments, returns an empty array.
|
|
7
|
+
*
|
|
8
|
+
* @param context - Evaluation context containing identity and segment definitions
|
|
9
|
+
* @returns Array of segments that the identity matches
|
|
10
|
+
*/
|
|
11
|
+
export declare function getIdentitySegments(context: GenericEvaluationContext): SegmentContext[];
|
|
12
|
+
/**
|
|
13
|
+
* Evaluates whether a segment condition matches the identity's traits or context values.
|
|
14
|
+
*
|
|
15
|
+
* Handles different types of conditions:
|
|
16
|
+
* - PERCENTAGE_SPLIT: Deterministic percentage-based bucketing using identity key
|
|
17
|
+
* - IS_SET/IS_NOT_SET: Checks for trait existence
|
|
18
|
+
* - Standard operators: EQUAL, NOT_EQUAL, etc. via SegmentConditionModel
|
|
19
|
+
* - JSONPath expressions: $.identity.identifier, $.environment.name, etc.
|
|
20
|
+
*
|
|
21
|
+
* @param condition - The condition to evaluate (property, operator, value)
|
|
22
|
+
* @param segmentKey - Key of the segment (used for percentage split hashing)
|
|
23
|
+
* @param context - Evaluation context containing identity, traits, and environment
|
|
24
|
+
* @returns true if the condition matches
|
|
25
|
+
*/
|
|
26
|
+
export declare function traitsMatchSegmentCondition(condition: SegmentCondition | InSegmentCondition, segmentKey: string, context?: GenericEvaluationContext): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Evaluates JSONPath expressions against the evaluation context.
|
|
29
|
+
*
|
|
30
|
+
* Supports accessing nested context values using JSONPath syntax.
|
|
31
|
+
* Commonly used paths:
|
|
32
|
+
* - $.identity.identifier - User's unique identifier
|
|
33
|
+
* - $.identity.key - User's internal key
|
|
34
|
+
* - $.environment.name - Environment name
|
|
35
|
+
* - $.environment.key - Environment key
|
|
36
|
+
*
|
|
37
|
+
* @param jsonPath - JSONPath expression starting with '$.'
|
|
38
|
+
* @param context - Evaluation context to query against
|
|
39
|
+
* @returns The resolved value, or undefined if path doesn't exist or is invalid
|
|
40
|
+
*/
|
|
41
|
+
export declare function getContextValue(jsonPath: string, context?: GenericEvaluationContext): any;
|