flagsmith-nodejs 3.3.3 → 4.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/.github/workflows/publish.yml +2 -2
- package/.github/workflows/pull_request.yaml +7 -14
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/environments/models.d.ts +3 -3
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/environments/models.js +20 -13
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/environments/util.d.ts +1 -1
- package/build/cjs/flagsmith-engine/environments/util.js +23 -0
- package/build/cjs/flagsmith-engine/features/models.js +118 -0
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/features/util.d.ts +1 -1
- package/build/cjs/flagsmith-engine/features/util.js +27 -0
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/identities/models.d.ts +2 -2
- package/build/cjs/flagsmith-engine/identities/models.js +48 -0
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/identities/traits/models.js +5 -4
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/identities/util.d.ts +2 -2
- package/build/cjs/flagsmith-engine/identities/util.js +22 -0
- package/build/cjs/flagsmith-engine/index.d.ts +14 -0
- package/build/cjs/flagsmith-engine/index.js +75 -0
- package/build/cjs/flagsmith-engine/organisations/models.js +21 -0
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/organisations/util.d.ts +1 -1
- package/build/cjs/flagsmith-engine/organisations/util.js +8 -0
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/projects/models.d.ts +2 -2
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/projects/models.js +8 -5
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/projects/util.d.ts +1 -1
- package/build/cjs/flagsmith-engine/projects/util.js +15 -0
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/segments/evaluators.d.ts +4 -4
- package/build/cjs/flagsmith-engine/segments/evaluators.js +37 -0
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/segments/models.d.ts +1 -1
- package/build/cjs/flagsmith-engine/segments/models.js +111 -0
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/segments/util.d.ts +1 -1
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/segments/util.js +9 -11
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/utils/collections.d.ts +1 -1
- package/build/cjs/flagsmith-engine/utils/collections.js +6 -0
- package/build/cjs/flagsmith-engine/utils/errors.js +6 -0
- package/build/cjs/flagsmith-engine/utils/hashing/index.js +29 -0
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/utils/index.js +5 -5
- package/build/{index.d.ts → cjs/index.d.ts} +3 -3
- package/build/cjs/index.js +18 -0
- package/build/cjs/package.json +1 -0
- package/build/{sdk → cjs/sdk}/analytics.d.ts +3 -0
- package/build/cjs/sdk/analytics.js +73 -0
- package/build/cjs/sdk/errors.js +9 -0
- package/build/{sdk → cjs/sdk}/index.d.ts +19 -18
- package/build/cjs/sdk/index.js +400 -0
- package/build/{sdk → cjs/sdk}/models.d.ts +2 -2
- package/build/cjs/sdk/models.js +101 -0
- package/build/{sdk → cjs/sdk}/offline_handlers.d.ts +1 -1
- package/build/cjs/sdk/offline_handlers.js +23 -0
- package/build/{sdk → cjs/sdk}/polling_manager.d.ts +1 -1
- package/build/cjs/sdk/polling_manager.js +29 -0
- package/build/{sdk → cjs/sdk}/types.d.ts +15 -7
- package/build/cjs/sdk/utils.d.ts +36 -0
- package/build/cjs/sdk/utils.js +63 -0
- package/build/esm/flagsmith-engine/environments/models.d.ts +22 -0
- package/build/esm/flagsmith-engine/environments/models.js +32 -0
- package/build/esm/flagsmith-engine/environments/util.d.ts +3 -0
- package/build/esm/flagsmith-engine/environments/util.js +18 -0
- package/build/esm/flagsmith-engine/features/constants.d.ts +4 -0
- package/build/esm/flagsmith-engine/features/constants.js +4 -0
- package/build/esm/flagsmith-engine/features/models.d.ts +37 -0
- package/build/esm/flagsmith-engine/features/models.js +110 -0
- package/build/esm/flagsmith-engine/features/util.d.ts +4 -0
- package/build/esm/flagsmith-engine/features/util.js +21 -0
- package/build/esm/flagsmith-engine/identities/models.d.ts +15 -0
- package/build/esm/flagsmith-engine/identities/models.js +44 -0
- package/build/esm/flagsmith-engine/identities/traits/models.d.ts +5 -0
- package/build/esm/flagsmith-engine/identities/traits/models.js +8 -0
- package/build/esm/flagsmith-engine/identities/util.d.ts +4 -0
- package/build/esm/flagsmith-engine/identities/util.js +17 -0
- package/build/esm/flagsmith-engine/index.d.ts +14 -0
- package/build/esm/flagsmith-engine/index.js +62 -0
- package/build/esm/flagsmith-engine/organisations/models.d.ts +9 -0
- package/build/esm/flagsmith-engine/organisations/models.js +17 -0
- package/build/esm/flagsmith-engine/organisations/util.d.ts +2 -0
- package/build/esm/flagsmith-engine/organisations/util.js +4 -0
- package/build/esm/flagsmith-engine/projects/models.d.ts +10 -0
- package/build/esm/flagsmith-engine/projects/models.js +13 -0
- package/build/esm/flagsmith-engine/projects/util.d.ts +2 -0
- package/build/esm/flagsmith-engine/projects/util.js +11 -0
- package/build/esm/flagsmith-engine/segments/constants.d.ts +34 -0
- package/build/esm/flagsmith-engine/segments/constants.js +36 -0
- package/build/esm/flagsmith-engine/segments/evaluators.d.ts +7 -0
- package/build/esm/flagsmith-engine/segments/evaluators.js +31 -0
- package/build/esm/flagsmith-engine/segments/models.d.ts +37 -0
- package/build/esm/flagsmith-engine/segments/models.js +102 -0
- package/build/esm/flagsmith-engine/segments/util.d.ts +6 -0
- package/build/esm/flagsmith-engine/segments/util.js +23 -0
- package/build/esm/flagsmith-engine/utils/collections.d.ts +3 -0
- package/build/esm/flagsmith-engine/utils/collections.js +2 -0
- package/build/esm/flagsmith-engine/utils/errors.d.ts +2 -0
- package/build/esm/flagsmith-engine/utils/errors.js +2 -0
- package/build/esm/flagsmith-engine/utils/hashing/index.d.ts +9 -0
- package/build/esm/flagsmith-engine/utils/hashing/index.js +25 -0
- package/build/esm/flagsmith-engine/utils/index.d.ts +1 -0
- package/build/esm/flagsmith-engine/utils/index.js +13 -0
- package/build/esm/index.d.ts +3 -0
- package/build/esm/index.js +2 -0
- package/build/esm/sdk/analytics.d.ts +35 -0
- package/build/esm/sdk/analytics.js +69 -0
- package/build/esm/sdk/errors.d.ts +4 -0
- package/build/esm/sdk/errors.js +4 -0
- package/build/esm/sdk/index.d.ts +131 -0
- package/build/esm/sdk/index.js +390 -0
- package/build/esm/sdk/models.d.ts +55 -0
- package/build/esm/sdk/models.js +94 -0
- package/build/esm/sdk/offline_handlers.d.ts +9 -0
- package/build/esm/sdk/offline_handlers.js +18 -0
- package/build/esm/sdk/polling_manager.d.ts +9 -0
- package/build/esm/sdk/polling_manager.js +25 -0
- package/build/esm/sdk/types.d.ts +38 -0
- package/build/esm/sdk/types.js +1 -0
- package/build/esm/sdk/utils.d.ts +36 -0
- package/build/esm/sdk/utils.js +56 -0
- package/flagsmith-engine/environments/models.ts +3 -3
- package/flagsmith-engine/environments/util.ts +4 -4
- package/flagsmith-engine/features/models.ts +2 -2
- package/flagsmith-engine/features/util.ts +1 -1
- package/flagsmith-engine/identities/models.ts +4 -5
- package/flagsmith-engine/identities/traits/models.ts +0 -1
- package/flagsmith-engine/identities/util.ts +4 -4
- package/flagsmith-engine/index.ts +13 -13
- package/flagsmith-engine/organisations/util.ts +1 -1
- package/flagsmith-engine/projects/models.ts +2 -2
- package/flagsmith-engine/projects/util.ts +4 -4
- package/flagsmith-engine/segments/evaluators.ts +6 -6
- package/flagsmith-engine/segments/models.ts +5 -5
- package/flagsmith-engine/segments/util.ts +3 -3
- package/flagsmith-engine/utils/collections.ts +1 -1
- package/flagsmith-engine/utils/hashing/index.ts +5 -29
- package/flagsmith-engine/utils/index.ts +1 -1
- package/index.ts +4 -8
- package/package.json +21 -16
- package/sdk/analytics.ts +7 -5
- package/sdk/index.ts +55 -46
- package/sdk/models.ts +2 -3
- package/sdk/offline_handlers.ts +2 -2
- package/sdk/polling_manager.ts +2 -3
- package/sdk/types.ts +35 -24
- package/sdk/utils.ts +49 -37
- package/tests/engine/e2e/engine.test.ts +8 -11
- package/tests/engine/unit/engine.test.ts +5 -5
- package/tests/engine/unit/segments/segment_evaluators.test.ts +9 -9
- package/tests/engine/unit/utils/utils.test.ts +2 -2
- package/tests/sdk/analytics.test.ts +8 -13
- package/tests/sdk/data/identity-with-transient-traits.json +41 -0
- package/tests/sdk/data/transient-identity.json +29 -0
- package/tests/sdk/flagsmith-cache.test.ts +16 -32
- package/tests/sdk/flagsmith-environment-flags.test.ts +21 -36
- package/tests/sdk/flagsmith-identity-flags.test.ts +83 -32
- package/tests/sdk/flagsmith.test.ts +67 -99
- package/tests/sdk/offline-handlers.test.ts +5 -6
- package/tests/sdk/polling.test.ts +6 -8
- package/tests/sdk/utils.ts +19 -15
- package/tsconfig.cjs.json +7 -0
- package/tsconfig.esm.json +7 -0
- package/tsconfig.json +8 -4
- package/vitest.config.ts +17 -0
- package/build/flagsmith-engine/environments/util.js +0 -27
- package/build/flagsmith-engine/features/models.js +0 -132
- package/build/flagsmith-engine/features/util.js +0 -27
- package/build/flagsmith-engine/identities/models.js +0 -113
- package/build/flagsmith-engine/identities/util.js +0 -46
- package/build/flagsmith-engine/index.d.ts +0 -14
- package/build/flagsmith-engine/index.js +0 -127
- package/build/flagsmith-engine/organisations/models.js +0 -21
- package/build/flagsmith-engine/organisations/util.js +0 -8
- package/build/flagsmith-engine/projects/util.js +0 -15
- package/build/flagsmith-engine/segments/evaluators.js +0 -45
- package/build/flagsmith-engine/segments/models.js +0 -147
- package/build/flagsmith-engine/utils/collections.js +0 -26
- package/build/flagsmith-engine/utils/errors.js +0 -26
- package/build/flagsmith-engine/utils/hashing/index.js +0 -60
- package/build/index.js +0 -23
- package/build/sdk/analytics.js +0 -120
- package/build/sdk/errors.js +0 -34
- package/build/sdk/index.js +0 -594
- package/build/sdk/models.js +0 -149
- package/build/sdk/offline_handlers.js +0 -66
- package/build/sdk/polling_manager.js +0 -72
- package/build/sdk/utils.d.ts +0 -12
- package/build/sdk/utils.js +0 -107
- package/jest.config.js +0 -5
- package/tests/index.js +0 -0
- /package/build/{flagsmith-engine → cjs/flagsmith-engine}/features/constants.d.ts +0 -0
- /package/build/{flagsmith-engine → cjs/flagsmith-engine}/features/constants.js +0 -0
- /package/build/{flagsmith-engine → cjs/flagsmith-engine}/features/models.d.ts +0 -0
- /package/build/{flagsmith-engine → cjs/flagsmith-engine}/identities/traits/models.d.ts +0 -0
- /package/build/{flagsmith-engine → cjs/flagsmith-engine}/organisations/models.d.ts +0 -0
- /package/build/{flagsmith-engine → cjs/flagsmith-engine}/segments/constants.d.ts +0 -0
- /package/build/{flagsmith-engine → cjs/flagsmith-engine}/segments/constants.js +0 -0
- /package/build/{flagsmith-engine → cjs/flagsmith-engine}/utils/errors.d.ts +0 -0
- /package/build/{flagsmith-engine → cjs/flagsmith-engine}/utils/hashing/index.d.ts +0 -0
- /package/build/{flagsmith-engine → cjs/flagsmith-engine}/utils/index.d.ts +0 -0
- /package/build/{sdk → cjs/sdk}/errors.d.ts +0 -0
- /package/build/{sdk → cjs/sdk}/types.js +0 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.retryFetch = exports.delay = exports.generateIdentitiesData = exports.isTraitConfig = void 0;
|
|
4
|
+
function isTraitConfig(traitValue) {
|
|
5
|
+
return !!traitValue && typeof traitValue == 'object' && traitValue.value !== undefined;
|
|
6
|
+
}
|
|
7
|
+
exports.isTraitConfig = isTraitConfig;
|
|
8
|
+
function generateIdentitiesData(identifier, traits, transient) {
|
|
9
|
+
const traitsGenerated = Object.entries(traits).map(([key, value]) => {
|
|
10
|
+
if (isTraitConfig(value)) {
|
|
11
|
+
return {
|
|
12
|
+
trait_key: key,
|
|
13
|
+
trait_value: value?.value,
|
|
14
|
+
transient: value?.transient,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
return {
|
|
19
|
+
trait_key: key,
|
|
20
|
+
trait_value: value,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
if (transient) {
|
|
25
|
+
return {
|
|
26
|
+
identifier: identifier,
|
|
27
|
+
traits: traitsGenerated,
|
|
28
|
+
transient: true
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
identifier: identifier,
|
|
33
|
+
traits: traitsGenerated
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
exports.generateIdentitiesData = generateIdentitiesData;
|
|
37
|
+
const delay = (ms) => new Promise(resolve => setTimeout(() => resolve(undefined), ms));
|
|
38
|
+
exports.delay = delay;
|
|
39
|
+
const retryFetch = (url,
|
|
40
|
+
// built-in RequestInit type doesn't have dispatcher/agent
|
|
41
|
+
fetchOptions, retries = 3, timeoutMs = 10, // set an overall timeout for this function
|
|
42
|
+
customFetch) => {
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
const retryWrapper = (n) => {
|
|
45
|
+
customFetch(url, {
|
|
46
|
+
...fetchOptions,
|
|
47
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
48
|
+
})
|
|
49
|
+
.then(res => resolve(res))
|
|
50
|
+
.catch(async (err) => {
|
|
51
|
+
if (n > 0) {
|
|
52
|
+
await (0, exports.delay)(1000);
|
|
53
|
+
retryWrapper(--n);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
reject(err);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
retryWrapper(retries);
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
exports.retryFetch = retryFetch;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { FeatureStateModel } from '../features/models.js';
|
|
2
|
+
import { IdentityModel } from '../identities/models.js';
|
|
3
|
+
import { ProjectModel } from '../projects/models.js';
|
|
4
|
+
export declare class EnvironmentAPIKeyModel {
|
|
5
|
+
id: number;
|
|
6
|
+
key: string;
|
|
7
|
+
createdAt: number;
|
|
8
|
+
name: string;
|
|
9
|
+
clientApiKey: string;
|
|
10
|
+
expiresAt?: number;
|
|
11
|
+
active: boolean;
|
|
12
|
+
constructor(id: number, key: string, createdAt: number, name: string, clientApiKey: string, expiresAt?: number);
|
|
13
|
+
isValid(): boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare class EnvironmentModel {
|
|
16
|
+
id: number;
|
|
17
|
+
apiKey: string;
|
|
18
|
+
project: ProjectModel;
|
|
19
|
+
featureStates: FeatureStateModel[];
|
|
20
|
+
identityOverrides: IdentityModel[];
|
|
21
|
+
constructor(id: number, apiKey: string, project: ProjectModel);
|
|
22
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export class EnvironmentAPIKeyModel {
|
|
2
|
+
id;
|
|
3
|
+
key;
|
|
4
|
+
createdAt;
|
|
5
|
+
name;
|
|
6
|
+
clientApiKey;
|
|
7
|
+
expiresAt;
|
|
8
|
+
active = true;
|
|
9
|
+
constructor(id, key, createdAt, name, clientApiKey, expiresAt) {
|
|
10
|
+
this.id = id;
|
|
11
|
+
this.key = key;
|
|
12
|
+
this.createdAt = createdAt;
|
|
13
|
+
this.name = name;
|
|
14
|
+
this.clientApiKey = clientApiKey;
|
|
15
|
+
this.expiresAt = expiresAt;
|
|
16
|
+
}
|
|
17
|
+
isValid() {
|
|
18
|
+
return !!this.active && (!this.expiresAt || this.expiresAt > Date.now());
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export class EnvironmentModel {
|
|
22
|
+
id;
|
|
23
|
+
apiKey;
|
|
24
|
+
project;
|
|
25
|
+
featureStates = [];
|
|
26
|
+
identityOverrides = [];
|
|
27
|
+
constructor(id, apiKey, project) {
|
|
28
|
+
this.id = id;
|
|
29
|
+
this.apiKey = apiKey;
|
|
30
|
+
this.project = project;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { buildFeatureStateModel } from '../features/util.js';
|
|
2
|
+
import { buildIdentityModel } from '../identities/util.js';
|
|
3
|
+
import { buildProjectModel } from '../projects/util.js';
|
|
4
|
+
import { EnvironmentAPIKeyModel, EnvironmentModel } from './models.js';
|
|
5
|
+
export function buildEnvironmentModel(environmentJSON) {
|
|
6
|
+
const project = buildProjectModel(environmentJSON.project);
|
|
7
|
+
const featureStates = environmentJSON.feature_states.map((fs) => buildFeatureStateModel(fs));
|
|
8
|
+
const environmentModel = new EnvironmentModel(environmentJSON.id, environmentJSON.api_key, project);
|
|
9
|
+
environmentModel.featureStates = featureStates;
|
|
10
|
+
if (!!environmentJSON.identity_overrides) {
|
|
11
|
+
environmentModel.identityOverrides = environmentJSON.identity_overrides.map((identityData) => buildIdentityModel(identityData));
|
|
12
|
+
}
|
|
13
|
+
return environmentModel;
|
|
14
|
+
}
|
|
15
|
+
export function buildEnvironmentAPIKeyModel(apiKeyJSON) {
|
|
16
|
+
const model = new EnvironmentAPIKeyModel(apiKeyJSON.id, apiKeyJSON.key, Date.parse(apiKeyJSON.created_at), apiKeyJSON.name, apiKeyJSON.client_api_key);
|
|
17
|
+
return model;
|
|
18
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export declare class FeatureModel {
|
|
2
|
+
id: number;
|
|
3
|
+
name: string;
|
|
4
|
+
type: string;
|
|
5
|
+
constructor(id: number, name: string, type: string);
|
|
6
|
+
eq(other: FeatureModel): boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare class MultivariateFeatureOptionModel {
|
|
9
|
+
value: any;
|
|
10
|
+
id: number | undefined;
|
|
11
|
+
constructor(value: any, id?: number);
|
|
12
|
+
}
|
|
13
|
+
export declare class MultivariateFeatureStateValueModel {
|
|
14
|
+
multivariateFeatureOption: MultivariateFeatureOptionModel;
|
|
15
|
+
percentageAllocation: number;
|
|
16
|
+
id: number;
|
|
17
|
+
mvFsValueUuid: string;
|
|
18
|
+
constructor(multivariate_feature_option: MultivariateFeatureOptionModel, percentage_allocation: number, id: number, mvFsValueUuid?: string);
|
|
19
|
+
}
|
|
20
|
+
export declare class FeatureStateModel {
|
|
21
|
+
feature: FeatureModel;
|
|
22
|
+
enabled: boolean;
|
|
23
|
+
djangoID: number;
|
|
24
|
+
featurestateUUID: string;
|
|
25
|
+
featureSegment?: FeatureSegment;
|
|
26
|
+
private value;
|
|
27
|
+
multivariateFeatureStateValues: MultivariateFeatureStateValueModel[];
|
|
28
|
+
constructor(feature: FeatureModel, enabled: boolean, djangoID: number, value?: any, featurestateUuid?: string);
|
|
29
|
+
setValue(value: any): void;
|
|
30
|
+
getValue(identityId?: number | string): any;
|
|
31
|
+
isHigherSegmentPriority(other: FeatureStateModel): boolean;
|
|
32
|
+
getMultivariateValue(identityID: number | string): any;
|
|
33
|
+
}
|
|
34
|
+
export declare class FeatureSegment {
|
|
35
|
+
priority: number;
|
|
36
|
+
constructor(priority: number);
|
|
37
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { randomUUID as uuidv4 } from "node:crypto";
|
|
2
|
+
import { getHashedPercentateForObjIds } from '../utils/hashing/index.js';
|
|
3
|
+
export class FeatureModel {
|
|
4
|
+
id;
|
|
5
|
+
name;
|
|
6
|
+
type;
|
|
7
|
+
constructor(id, name, type) {
|
|
8
|
+
this.id = id;
|
|
9
|
+
this.name = name;
|
|
10
|
+
this.type = type;
|
|
11
|
+
}
|
|
12
|
+
eq(other) {
|
|
13
|
+
return !!other && this.id === other.id;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class MultivariateFeatureOptionModel {
|
|
17
|
+
value;
|
|
18
|
+
id;
|
|
19
|
+
constructor(value, id) {
|
|
20
|
+
this.value = value;
|
|
21
|
+
this.id = id;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export class MultivariateFeatureStateValueModel {
|
|
25
|
+
multivariateFeatureOption;
|
|
26
|
+
percentageAllocation;
|
|
27
|
+
id;
|
|
28
|
+
mvFsValueUuid = uuidv4();
|
|
29
|
+
constructor(multivariate_feature_option, percentage_allocation, id, mvFsValueUuid) {
|
|
30
|
+
this.id = id;
|
|
31
|
+
this.percentageAllocation = percentage_allocation;
|
|
32
|
+
this.multivariateFeatureOption = multivariate_feature_option;
|
|
33
|
+
this.mvFsValueUuid = mvFsValueUuid || this.mvFsValueUuid;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export class FeatureStateModel {
|
|
37
|
+
feature;
|
|
38
|
+
enabled;
|
|
39
|
+
djangoID;
|
|
40
|
+
featurestateUUID = uuidv4();
|
|
41
|
+
featureSegment;
|
|
42
|
+
value;
|
|
43
|
+
multivariateFeatureStateValues = [];
|
|
44
|
+
constructor(feature, enabled, djangoID, value, featurestateUuid = uuidv4()) {
|
|
45
|
+
this.feature = feature;
|
|
46
|
+
this.enabled = enabled;
|
|
47
|
+
this.djangoID = djangoID;
|
|
48
|
+
this.value = value;
|
|
49
|
+
this.featurestateUUID = featurestateUuid;
|
|
50
|
+
}
|
|
51
|
+
setValue(value) {
|
|
52
|
+
this.value = value;
|
|
53
|
+
}
|
|
54
|
+
getValue(identityId) {
|
|
55
|
+
if (!!identityId && this.multivariateFeatureStateValues.length > 0) {
|
|
56
|
+
return this.getMultivariateValue(identityId);
|
|
57
|
+
}
|
|
58
|
+
return this.value;
|
|
59
|
+
}
|
|
60
|
+
/*
|
|
61
|
+
Returns `True` if `this` is higher segment priority than `other`
|
|
62
|
+
(i.e. has lower value for featureSegment.priority)
|
|
63
|
+
NOTE:
|
|
64
|
+
A segment will be considered higher priority only if:
|
|
65
|
+
1. `other` does not have a feature segment(i.e: it is an environment feature state or it's a
|
|
66
|
+
feature state with feature segment but from an old document that does not have `featureSegment.priority`)
|
|
67
|
+
but `this` does.
|
|
68
|
+
2. `other` have a feature segment with high priority
|
|
69
|
+
*/
|
|
70
|
+
isHigherSegmentPriority(other) {
|
|
71
|
+
if (!other.featureSegment || !this.featureSegment) {
|
|
72
|
+
return !!this.featureSegment && !other.featureSegment;
|
|
73
|
+
}
|
|
74
|
+
return this.featureSegment.priority < other.featureSegment.priority;
|
|
75
|
+
}
|
|
76
|
+
getMultivariateValue(identityID) {
|
|
77
|
+
let percentageValue;
|
|
78
|
+
let startPercentage = 0;
|
|
79
|
+
const sortedF = this.multivariateFeatureStateValues.sort((a, b) => {
|
|
80
|
+
return a.id - b.id;
|
|
81
|
+
});
|
|
82
|
+
for (const myValue of sortedF) {
|
|
83
|
+
switch (myValue.percentageAllocation) {
|
|
84
|
+
case 0:
|
|
85
|
+
continue;
|
|
86
|
+
case 100:
|
|
87
|
+
return myValue.multivariateFeatureOption.value;
|
|
88
|
+
default:
|
|
89
|
+
if (percentageValue === undefined) {
|
|
90
|
+
percentageValue = getHashedPercentateForObjIds([
|
|
91
|
+
this.djangoID || this.featurestateUUID,
|
|
92
|
+
identityID
|
|
93
|
+
]);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const limit = myValue.percentageAllocation + startPercentage;
|
|
97
|
+
if (startPercentage <= percentageValue && percentageValue < limit) {
|
|
98
|
+
return myValue.multivariateFeatureOption.value;
|
|
99
|
+
}
|
|
100
|
+
startPercentage = limit;
|
|
101
|
+
}
|
|
102
|
+
return this.value;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
export class FeatureSegment {
|
|
106
|
+
priority;
|
|
107
|
+
constructor(priority) {
|
|
108
|
+
this.priority = priority;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { FeatureModel, FeatureSegment, FeatureStateModel } from './models.js';
|
|
2
|
+
export declare function buildFeatureModel(featuresModelJSON: any): FeatureModel;
|
|
3
|
+
export declare function buildFeatureStateModel(featuresStateModelJSON: any): FeatureStateModel;
|
|
4
|
+
export declare function buildFeatureSegment(featureSegmentJSON: any): FeatureSegment;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { FeatureModel, FeatureSegment, FeatureStateModel, MultivariateFeatureOptionModel, MultivariateFeatureStateValueModel } from './models.js';
|
|
2
|
+
export function buildFeatureModel(featuresModelJSON) {
|
|
3
|
+
return new FeatureModel(featuresModelJSON.id, featuresModelJSON.name, featuresModelJSON.type);
|
|
4
|
+
}
|
|
5
|
+
export function buildFeatureStateModel(featuresStateModelJSON) {
|
|
6
|
+
const featureStateModel = new FeatureStateModel(buildFeatureModel(featuresStateModelJSON.feature), featuresStateModelJSON.enabled, featuresStateModelJSON.django_id, featuresStateModelJSON.feature_state_value, featuresStateModelJSON.featurestate_uuid);
|
|
7
|
+
featureStateModel.featureSegment = featuresStateModelJSON.feature_segment ?
|
|
8
|
+
buildFeatureSegment(featuresStateModelJSON.feature_segment) :
|
|
9
|
+
undefined;
|
|
10
|
+
const multivariateFeatureStateValues = featuresStateModelJSON.multivariate_feature_state_values
|
|
11
|
+
? featuresStateModelJSON.multivariate_feature_state_values.map((fsv) => {
|
|
12
|
+
const featureOption = new MultivariateFeatureOptionModel(fsv.multivariate_feature_option.value, fsv.multivariate_feature_option.id);
|
|
13
|
+
return new MultivariateFeatureStateValueModel(featureOption, fsv.percentage_allocation, fsv.id, fsv.mv_fs_value_uuid);
|
|
14
|
+
})
|
|
15
|
+
: [];
|
|
16
|
+
featureStateModel.multivariateFeatureStateValues = multivariateFeatureStateValues;
|
|
17
|
+
return featureStateModel;
|
|
18
|
+
}
|
|
19
|
+
export function buildFeatureSegment(featureSegmentJSON) {
|
|
20
|
+
return new FeatureSegment(featureSegmentJSON.priority);
|
|
21
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { IdentityFeaturesList } from '../utils/collections.js';
|
|
2
|
+
import { TraitModel } from './traits/models.js';
|
|
3
|
+
export declare class IdentityModel {
|
|
4
|
+
identifier: string;
|
|
5
|
+
environmentApiKey: string;
|
|
6
|
+
createdDate?: number;
|
|
7
|
+
identityFeatures: IdentityFeaturesList;
|
|
8
|
+
identityTraits: TraitModel[];
|
|
9
|
+
identityUuid: string;
|
|
10
|
+
djangoID: number | undefined;
|
|
11
|
+
constructor(created_date: string, identityTraits: TraitModel[], identityFeatures: IdentityFeaturesList, environmentApiKey: string, identifier: string, identityUuid?: string, djangoID?: number);
|
|
12
|
+
get compositeKey(): string;
|
|
13
|
+
static generateCompositeKey(env_key: string, identifier: string): string;
|
|
14
|
+
updateTraits(traits: TraitModel[]): void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { IdentityFeaturesList } from '../utils/collections.js';
|
|
2
|
+
import { randomUUID as uuidv4 } from 'node:crypto';
|
|
3
|
+
export class IdentityModel {
|
|
4
|
+
identifier;
|
|
5
|
+
environmentApiKey;
|
|
6
|
+
createdDate;
|
|
7
|
+
identityFeatures;
|
|
8
|
+
identityTraits;
|
|
9
|
+
identityUuid;
|
|
10
|
+
djangoID;
|
|
11
|
+
constructor(created_date, identityTraits, identityFeatures, environmentApiKey, identifier, identityUuid, djangoID) {
|
|
12
|
+
this.identityUuid = identityUuid || uuidv4();
|
|
13
|
+
this.createdDate = Date.parse(created_date) || Date.now();
|
|
14
|
+
this.identityTraits = identityTraits;
|
|
15
|
+
this.identityFeatures = new IdentityFeaturesList(...identityFeatures);
|
|
16
|
+
this.environmentApiKey = environmentApiKey;
|
|
17
|
+
this.identifier = identifier;
|
|
18
|
+
this.djangoID = djangoID;
|
|
19
|
+
}
|
|
20
|
+
get compositeKey() {
|
|
21
|
+
return IdentityModel.generateCompositeKey(this.environmentApiKey, this.identifier);
|
|
22
|
+
}
|
|
23
|
+
static generateCompositeKey(env_key, identifier) {
|
|
24
|
+
return `${env_key}_${identifier}`;
|
|
25
|
+
}
|
|
26
|
+
updateTraits(traits) {
|
|
27
|
+
const existingTraits = new Map();
|
|
28
|
+
for (const trait of this.identityTraits) {
|
|
29
|
+
existingTraits.set(trait.traitKey, trait);
|
|
30
|
+
}
|
|
31
|
+
for (const trait of traits) {
|
|
32
|
+
if (!!trait.traitValue) {
|
|
33
|
+
existingTraits.set(trait.traitKey, trait);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
existingTraits.delete(trait.traitKey);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
this.identityTraits = [];
|
|
40
|
+
for (const [k, v] of existingTraits.entries()) {
|
|
41
|
+
this.identityTraits.push(v);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { buildFeatureStateModel } from '../features/util.js';
|
|
2
|
+
import { IdentityFeaturesList } from '../utils/collections.js';
|
|
3
|
+
import { IdentityModel } from './models.js';
|
|
4
|
+
import { TraitModel } from './traits/models.js';
|
|
5
|
+
export function buildTraitModel(traitJSON) {
|
|
6
|
+
return new TraitModel(traitJSON.trait_key, traitJSON.trait_value);
|
|
7
|
+
}
|
|
8
|
+
export function buildIdentityModel(identityJSON) {
|
|
9
|
+
const featureList = identityJSON.identity_features
|
|
10
|
+
? new IdentityFeaturesList(...identityJSON.identity_features.map((f) => buildFeatureStateModel(f)))
|
|
11
|
+
: [];
|
|
12
|
+
const model = new IdentityModel(identityJSON.created_date, identityJSON.identity_traits
|
|
13
|
+
? identityJSON.identity_traits.map((trait) => buildTraitModel(trait))
|
|
14
|
+
: [], featureList, identityJSON.environment_api_key, identityJSON.identifier, identityJSON.identity_uuid);
|
|
15
|
+
model.djangoID = identityJSON.django_id;
|
|
16
|
+
return model;
|
|
17
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { EnvironmentModel } from './environments/models.js';
|
|
2
|
+
import { FeatureStateModel } from './features/models.js';
|
|
3
|
+
import { IdentityModel } from './identities/models.js';
|
|
4
|
+
import { TraitModel } from './identities/traits/models.js';
|
|
5
|
+
export { EnvironmentModel } from './environments/models.js';
|
|
6
|
+
export { FeatureStateModel } from './features/models.js';
|
|
7
|
+
export { IdentityModel } from './identities/models.js';
|
|
8
|
+
export { TraitModel } from './identities/traits/models.js';
|
|
9
|
+
export { SegmentModel } from './segments/models.js';
|
|
10
|
+
export { OrganisationModel } from './organisations/models.js';
|
|
11
|
+
export declare function getIdentityFeatureState(environment: EnvironmentModel, identity: IdentityModel, featureName: string, overrideTraits?: TraitModel[]): FeatureStateModel;
|
|
12
|
+
export declare function getIdentityFeatureStates(environment: EnvironmentModel, identity: IdentityModel, overrideTraits?: TraitModel[]): FeatureStateModel[];
|
|
13
|
+
export declare function getEnvironmentFeatureState(environment: EnvironmentModel, featureName: string): FeatureStateModel;
|
|
14
|
+
export declare function getEnvironmentFeatureStates(environment: EnvironmentModel): FeatureStateModel[];
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { getIdentitySegments } from './segments/evaluators.js';
|
|
2
|
+
import { FeatureStateNotFound } from './utils/errors.js';
|
|
3
|
+
export { EnvironmentModel } from './environments/models.js';
|
|
4
|
+
export { FeatureStateModel } from './features/models.js';
|
|
5
|
+
export { IdentityModel } from './identities/models.js';
|
|
6
|
+
export { TraitModel } from './identities/traits/models.js';
|
|
7
|
+
export { SegmentModel } from './segments/models.js';
|
|
8
|
+
export { OrganisationModel } from './organisations/models.js';
|
|
9
|
+
function getIdentityFeatureStatesDict(environment, identity, overrideTraits) {
|
|
10
|
+
// Get feature states from the environment
|
|
11
|
+
const featureStates = {};
|
|
12
|
+
for (const fs of environment.featureStates) {
|
|
13
|
+
featureStates[fs.feature.id] = fs;
|
|
14
|
+
}
|
|
15
|
+
// Override with any feature states defined by matching segments
|
|
16
|
+
const identitySegments = getIdentitySegments(environment, identity, overrideTraits);
|
|
17
|
+
for (const matchingSegment of identitySegments) {
|
|
18
|
+
for (const featureState of matchingSegment.featureStates) {
|
|
19
|
+
if (featureStates[featureState.feature.id]) {
|
|
20
|
+
if (featureStates[featureState.feature.id].isHigherSegmentPriority(featureState)) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
featureStates[featureState.feature.id] = featureState;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Override with any feature states defined directly the identity
|
|
28
|
+
for (const fs of identity.identityFeatures) {
|
|
29
|
+
if (featureStates[fs.feature.id]) {
|
|
30
|
+
featureStates[fs.feature.id] = fs;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return featureStates;
|
|
34
|
+
}
|
|
35
|
+
export function getIdentityFeatureState(environment, identity, featureName, overrideTraits) {
|
|
36
|
+
const featureStates = getIdentityFeatureStatesDict(environment, identity, overrideTraits);
|
|
37
|
+
const matchingFeature = Object.values(featureStates).filter(f => f.feature.name === featureName);
|
|
38
|
+
if (matchingFeature.length === 0) {
|
|
39
|
+
throw new FeatureStateNotFound('Feature State Not Found');
|
|
40
|
+
}
|
|
41
|
+
return matchingFeature[0];
|
|
42
|
+
}
|
|
43
|
+
export function getIdentityFeatureStates(environment, identity, overrideTraits) {
|
|
44
|
+
const featureStates = Object.values(getIdentityFeatureStatesDict(environment, identity, overrideTraits));
|
|
45
|
+
if (environment.project.hideDisabledFlags) {
|
|
46
|
+
return featureStates.filter(fs => !!fs.enabled);
|
|
47
|
+
}
|
|
48
|
+
return featureStates;
|
|
49
|
+
}
|
|
50
|
+
export function getEnvironmentFeatureState(environment, featureName) {
|
|
51
|
+
const featuresStates = environment.featureStates.filter(f => f.feature.name === featureName);
|
|
52
|
+
if (featuresStates.length === 0) {
|
|
53
|
+
throw new FeatureStateNotFound('Feature State Not Found');
|
|
54
|
+
}
|
|
55
|
+
return featuresStates[0];
|
|
56
|
+
}
|
|
57
|
+
export function getEnvironmentFeatureStates(environment) {
|
|
58
|
+
if (environment.project.hideDisabledFlags) {
|
|
59
|
+
return environment.featureStates.filter(fs => !!fs.enabled);
|
|
60
|
+
}
|
|
61
|
+
return environment.featureStates;
|
|
62
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare class OrganisationModel {
|
|
2
|
+
id: number;
|
|
3
|
+
name: string;
|
|
4
|
+
featureAnalytics: boolean;
|
|
5
|
+
stopServingFlags: boolean;
|
|
6
|
+
persistTraitData: boolean;
|
|
7
|
+
constructor(id: number, name: string, featureAnalytics: boolean, stopServingFlags: boolean, persistTraitData: boolean);
|
|
8
|
+
get uniqueSlug(): string;
|
|
9
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export class OrganisationModel {
|
|
2
|
+
id;
|
|
3
|
+
name;
|
|
4
|
+
featureAnalytics;
|
|
5
|
+
stopServingFlags;
|
|
6
|
+
persistTraitData;
|
|
7
|
+
constructor(id, name, featureAnalytics, stopServingFlags, persistTraitData) {
|
|
8
|
+
this.id = id;
|
|
9
|
+
this.name = name;
|
|
10
|
+
this.featureAnalytics = featureAnalytics;
|
|
11
|
+
this.stopServingFlags = stopServingFlags;
|
|
12
|
+
this.persistTraitData = persistTraitData;
|
|
13
|
+
}
|
|
14
|
+
get uniqueSlug() {
|
|
15
|
+
return this.id.toString() + '-' + this.name;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { OrganisationModel } from './models.js';
|
|
2
|
+
export function buildOrganizationModel(organizationJSON) {
|
|
3
|
+
return new OrganisationModel(organizationJSON.id, organizationJSON.name, organizationJSON.feature_analytics, organizationJSON.stop_serving_flags, organizationJSON.persist_trait_data);
|
|
4
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { OrganisationModel } from '../organisations/models.js';
|
|
2
|
+
import { SegmentModel } from '../segments/models.js';
|
|
3
|
+
export declare class ProjectModel {
|
|
4
|
+
id: number;
|
|
5
|
+
name: string;
|
|
6
|
+
organisation: OrganisationModel;
|
|
7
|
+
hideDisabledFlags: boolean;
|
|
8
|
+
segments: SegmentModel[];
|
|
9
|
+
constructor(id: number, name: string, hideDisabledFlags: boolean, organization: OrganisationModel);
|
|
10
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export class ProjectModel {
|
|
2
|
+
id;
|
|
3
|
+
name;
|
|
4
|
+
organisation;
|
|
5
|
+
hideDisabledFlags;
|
|
6
|
+
segments = [];
|
|
7
|
+
constructor(id, name, hideDisabledFlags, organization) {
|
|
8
|
+
this.id = id;
|
|
9
|
+
this.name = name;
|
|
10
|
+
this.hideDisabledFlags = hideDisabledFlags;
|
|
11
|
+
this.organisation = organization;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { buildOrganizationModel } from '../organisations/util.js';
|
|
2
|
+
import { buildSegmentModel } from '../segments/util.js';
|
|
3
|
+
import { ProjectModel } from './models.js';
|
|
4
|
+
export function buildProjectModel(projectJSON) {
|
|
5
|
+
const segments = projectJSON['segments']
|
|
6
|
+
? projectJSON['segments'].map((s) => buildSegmentModel(s))
|
|
7
|
+
: [];
|
|
8
|
+
const model = new ProjectModel(projectJSON.id, projectJSON.name, projectJSON.hide_disabled_flags, buildOrganizationModel(projectJSON.organisation));
|
|
9
|
+
model.segments = segments;
|
|
10
|
+
return model;
|
|
11
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export declare const ALL_RULE = "ALL";
|
|
2
|
+
export declare const ANY_RULE = "ANY";
|
|
3
|
+
export declare const NONE_RULE = "NONE";
|
|
4
|
+
export declare const RULE_TYPES: string[];
|
|
5
|
+
export declare const EQUAL = "EQUAL";
|
|
6
|
+
export declare const GREATER_THAN = "GREATER_THAN";
|
|
7
|
+
export declare const LESS_THAN = "LESS_THAN";
|
|
8
|
+
export declare const LESS_THAN_INCLUSIVE = "LESS_THAN_INCLUSIVE";
|
|
9
|
+
export declare const CONTAINS = "CONTAINS";
|
|
10
|
+
export declare const GREATER_THAN_INCLUSIVE = "GREATER_THAN_INCLUSIVE";
|
|
11
|
+
export declare const NOT_CONTAINS = "NOT_CONTAINS";
|
|
12
|
+
export declare const NOT_EQUAL = "NOT_EQUAL";
|
|
13
|
+
export declare const REGEX = "REGEX";
|
|
14
|
+
export declare const PERCENTAGE_SPLIT = "PERCENTAGE_SPLIT";
|
|
15
|
+
export declare const IS_SET = "IS_SET";
|
|
16
|
+
export declare const IS_NOT_SET = "IS_NOT_SET";
|
|
17
|
+
export declare const MODULO = "MODULO";
|
|
18
|
+
export declare const IN = "IN";
|
|
19
|
+
export declare const CONDITION_OPERATORS: {
|
|
20
|
+
EQUAL: string;
|
|
21
|
+
GREATER_THAN: string;
|
|
22
|
+
LESS_THAN: string;
|
|
23
|
+
LESS_THAN_INCLUSIVE: string;
|
|
24
|
+
CONTAINS: string;
|
|
25
|
+
GREATER_THAN_INCLUSIVE: string;
|
|
26
|
+
NOT_CONTAINS: string;
|
|
27
|
+
NOT_EQUAL: string;
|
|
28
|
+
REGEX: string;
|
|
29
|
+
PERCENTAGE_SPLIT: string;
|
|
30
|
+
IS_SET: string;
|
|
31
|
+
IS_NOT_SET: string;
|
|
32
|
+
MODULO: string;
|
|
33
|
+
IN: string;
|
|
34
|
+
};
|