flagsmith-nodejs 3.3.2 → 4.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/.github/workflows/publish.yml +2 -2
  2. package/.github/workflows/pull_request.yaml +3 -4
  3. package/build/{flagsmith-engine → cjs/flagsmith-engine}/environments/models.d.ts +3 -3
  4. package/build/{flagsmith-engine → cjs/flagsmith-engine}/environments/models.js +20 -13
  5. package/build/{flagsmith-engine → cjs/flagsmith-engine}/environments/util.d.ts +1 -1
  6. package/build/cjs/flagsmith-engine/environments/util.js +23 -0
  7. package/build/cjs/flagsmith-engine/features/models.js +118 -0
  8. package/build/{flagsmith-engine → cjs/flagsmith-engine}/features/util.d.ts +1 -1
  9. package/build/cjs/flagsmith-engine/features/util.js +27 -0
  10. package/build/{flagsmith-engine → cjs/flagsmith-engine}/identities/models.d.ts +2 -2
  11. package/build/cjs/flagsmith-engine/identities/models.js +48 -0
  12. package/build/{flagsmith-engine → cjs/flagsmith-engine}/identities/traits/models.js +5 -4
  13. package/build/{flagsmith-engine → cjs/flagsmith-engine}/identities/util.d.ts +2 -2
  14. package/build/cjs/flagsmith-engine/identities/util.js +22 -0
  15. package/build/cjs/flagsmith-engine/index.d.ts +14 -0
  16. package/build/cjs/flagsmith-engine/index.js +75 -0
  17. package/build/cjs/flagsmith-engine/organisations/models.js +21 -0
  18. package/build/{flagsmith-engine → cjs/flagsmith-engine}/organisations/util.d.ts +1 -1
  19. package/build/cjs/flagsmith-engine/organisations/util.js +8 -0
  20. package/build/{flagsmith-engine → cjs/flagsmith-engine}/projects/models.d.ts +2 -2
  21. package/build/{flagsmith-engine → cjs/flagsmith-engine}/projects/models.js +8 -5
  22. package/build/{flagsmith-engine → cjs/flagsmith-engine}/projects/util.d.ts +1 -1
  23. package/build/cjs/flagsmith-engine/projects/util.js +15 -0
  24. package/build/{flagsmith-engine → cjs/flagsmith-engine}/segments/evaluators.d.ts +4 -4
  25. package/build/cjs/flagsmith-engine/segments/evaluators.js +37 -0
  26. package/build/{flagsmith-engine → cjs/flagsmith-engine}/segments/models.d.ts +1 -1
  27. package/build/cjs/flagsmith-engine/segments/models.js +114 -0
  28. package/build/{flagsmith-engine → cjs/flagsmith-engine}/segments/util.d.ts +1 -1
  29. package/build/{flagsmith-engine → cjs/flagsmith-engine}/segments/util.js +9 -11
  30. package/build/{flagsmith-engine → cjs/flagsmith-engine}/utils/collections.d.ts +1 -1
  31. package/build/cjs/flagsmith-engine/utils/collections.js +6 -0
  32. package/build/cjs/flagsmith-engine/utils/errors.js +6 -0
  33. package/build/{flagsmith-engine → cjs/flagsmith-engine}/utils/hashing/index.js +8 -11
  34. package/build/{flagsmith-engine → cjs/flagsmith-engine}/utils/index.js +5 -5
  35. package/build/{index.d.ts → cjs/index.d.ts} +3 -3
  36. package/build/{index.js → cjs/index.js} +17 -17
  37. package/build/cjs/package.json +1 -0
  38. package/build/{sdk → cjs/sdk}/analytics.d.ts +3 -0
  39. package/build/cjs/sdk/analytics.js +73 -0
  40. package/build/cjs/sdk/errors.js +9 -0
  41. package/build/{sdk → cjs/sdk}/index.d.ts +19 -18
  42. package/build/cjs/sdk/index.js +400 -0
  43. package/build/{sdk → cjs/sdk}/models.d.ts +2 -2
  44. package/build/cjs/sdk/models.js +101 -0
  45. package/build/{sdk → cjs/sdk}/offline_handlers.d.ts +1 -1
  46. package/build/cjs/sdk/offline_handlers.js +46 -0
  47. package/build/{sdk → cjs/sdk}/polling_manager.d.ts +1 -1
  48. package/build/cjs/sdk/polling_manager.js +29 -0
  49. package/build/{sdk → cjs/sdk}/types.d.ts +15 -7
  50. package/build/cjs/sdk/utils.d.ts +36 -0
  51. package/build/cjs/sdk/utils.js +63 -0
  52. package/build/esm/flagsmith-engine/environments/models.d.ts +22 -0
  53. package/build/esm/flagsmith-engine/environments/models.js +32 -0
  54. package/build/esm/flagsmith-engine/environments/util.d.ts +3 -0
  55. package/build/esm/flagsmith-engine/environments/util.js +18 -0
  56. package/build/esm/flagsmith-engine/features/constants.d.ts +4 -0
  57. package/build/esm/flagsmith-engine/features/constants.js +4 -0
  58. package/build/esm/flagsmith-engine/features/models.d.ts +37 -0
  59. package/build/esm/flagsmith-engine/features/models.js +110 -0
  60. package/build/esm/flagsmith-engine/features/util.d.ts +4 -0
  61. package/build/esm/flagsmith-engine/features/util.js +21 -0
  62. package/build/esm/flagsmith-engine/identities/models.d.ts +15 -0
  63. package/build/esm/flagsmith-engine/identities/models.js +44 -0
  64. package/build/esm/flagsmith-engine/identities/traits/models.d.ts +5 -0
  65. package/build/esm/flagsmith-engine/identities/traits/models.js +8 -0
  66. package/build/esm/flagsmith-engine/identities/util.d.ts +4 -0
  67. package/build/esm/flagsmith-engine/identities/util.js +17 -0
  68. package/build/esm/flagsmith-engine/index.d.ts +14 -0
  69. package/build/esm/flagsmith-engine/index.js +62 -0
  70. package/build/esm/flagsmith-engine/organisations/models.d.ts +9 -0
  71. package/build/esm/flagsmith-engine/organisations/models.js +17 -0
  72. package/build/esm/flagsmith-engine/organisations/util.d.ts +2 -0
  73. package/build/esm/flagsmith-engine/organisations/util.js +4 -0
  74. package/build/esm/flagsmith-engine/projects/models.d.ts +10 -0
  75. package/build/esm/flagsmith-engine/projects/models.js +13 -0
  76. package/build/esm/flagsmith-engine/projects/util.d.ts +2 -0
  77. package/build/esm/flagsmith-engine/projects/util.js +11 -0
  78. package/build/esm/flagsmith-engine/segments/constants.d.ts +34 -0
  79. package/build/esm/flagsmith-engine/segments/constants.js +36 -0
  80. package/build/esm/flagsmith-engine/segments/evaluators.d.ts +7 -0
  81. package/build/esm/flagsmith-engine/segments/evaluators.js +31 -0
  82. package/build/esm/flagsmith-engine/segments/models.d.ts +37 -0
  83. package/build/esm/flagsmith-engine/segments/models.js +102 -0
  84. package/build/esm/flagsmith-engine/segments/util.d.ts +6 -0
  85. package/build/esm/flagsmith-engine/segments/util.js +23 -0
  86. package/build/esm/flagsmith-engine/utils/collections.d.ts +3 -0
  87. package/build/esm/flagsmith-engine/utils/collections.js +2 -0
  88. package/build/esm/flagsmith-engine/utils/errors.d.ts +2 -0
  89. package/build/esm/flagsmith-engine/utils/errors.js +2 -0
  90. package/build/esm/flagsmith-engine/utils/hashing/index.d.ts +9 -0
  91. package/build/esm/flagsmith-engine/utils/hashing/index.js +50 -0
  92. package/build/esm/flagsmith-engine/utils/index.d.ts +1 -0
  93. package/build/esm/flagsmith-engine/utils/index.js +13 -0
  94. package/build/esm/index.d.ts +3 -0
  95. package/build/esm/index.js +4 -0
  96. package/build/esm/sdk/analytics.d.ts +35 -0
  97. package/build/esm/sdk/analytics.js +69 -0
  98. package/build/esm/sdk/errors.d.ts +4 -0
  99. package/build/esm/sdk/errors.js +4 -0
  100. package/build/esm/sdk/index.d.ts +131 -0
  101. package/build/esm/sdk/index.js +390 -0
  102. package/build/esm/sdk/models.d.ts +55 -0
  103. package/build/esm/sdk/models.js +94 -0
  104. package/build/esm/sdk/offline_handlers.d.ts +9 -0
  105. package/build/esm/sdk/offline_handlers.js +18 -0
  106. package/build/esm/sdk/polling_manager.d.ts +9 -0
  107. package/build/esm/sdk/polling_manager.js +25 -0
  108. package/build/esm/sdk/types.d.ts +38 -0
  109. package/build/esm/sdk/types.js +1 -0
  110. package/build/esm/sdk/utils.d.ts +36 -0
  111. package/build/esm/sdk/utils.js +56 -0
  112. package/flagsmith-engine/environments/models.ts +3 -3
  113. package/flagsmith-engine/environments/util.ts +4 -4
  114. package/flagsmith-engine/features/models.ts +1 -1
  115. package/flagsmith-engine/features/util.ts +1 -1
  116. package/flagsmith-engine/identities/models.ts +3 -4
  117. package/flagsmith-engine/identities/traits/models.ts +0 -1
  118. package/flagsmith-engine/identities/util.ts +4 -4
  119. package/flagsmith-engine/index.ts +13 -13
  120. package/flagsmith-engine/organisations/util.ts +1 -1
  121. package/flagsmith-engine/projects/models.ts +2 -2
  122. package/flagsmith-engine/projects/util.ts +4 -4
  123. package/flagsmith-engine/segments/evaluators.ts +6 -6
  124. package/flagsmith-engine/segments/models.ts +4 -4
  125. package/flagsmith-engine/segments/util.ts +3 -3
  126. package/flagsmith-engine/utils/collections.ts +1 -1
  127. package/flagsmith-engine/utils/index.ts +1 -1
  128. package/index.ts +4 -4
  129. package/package.json +21 -9
  130. package/sdk/analytics.ts +7 -5
  131. package/sdk/index.ts +55 -46
  132. package/sdk/models.ts +2 -3
  133. package/sdk/offline_handlers.ts +2 -2
  134. package/sdk/polling_manager.ts +2 -3
  135. package/sdk/types.ts +35 -24
  136. package/sdk/utils.ts +49 -29
  137. package/tests/engine/e2e/engine.test.ts +5 -5
  138. package/tests/engine/unit/engine.test.ts +5 -5
  139. package/tests/engine/unit/segments/segment_evaluators.test.ts +9 -9
  140. package/tests/engine/unit/utils/utils.test.ts +1 -1
  141. package/tests/sdk/analytics.test.ts +8 -13
  142. package/tests/sdk/data/identity-with-transient-traits.json +41 -0
  143. package/tests/sdk/data/transient-identity.json +29 -0
  144. package/tests/sdk/flagsmith-cache.test.ts +16 -32
  145. package/tests/sdk/flagsmith-environment-flags.test.ts +21 -36
  146. package/tests/sdk/flagsmith-identity-flags.test.ts +83 -32
  147. package/tests/sdk/flagsmith.test.ts +67 -99
  148. package/tests/sdk/offline-handlers.test.ts +4 -5
  149. package/tests/sdk/polling.test.ts +6 -8
  150. package/tests/sdk/utils.ts +19 -15
  151. package/tsconfig.cjs.json +7 -0
  152. package/tsconfig.esm.json +7 -0
  153. package/tsconfig.json +7 -3
  154. package/vitest.config.ts +17 -0
  155. package/build/flagsmith-engine/environments/util.js +0 -27
  156. package/build/flagsmith-engine/features/models.js +0 -132
  157. package/build/flagsmith-engine/features/util.js +0 -27
  158. package/build/flagsmith-engine/identities/models.js +0 -113
  159. package/build/flagsmith-engine/identities/util.js +0 -46
  160. package/build/flagsmith-engine/index.d.ts +0 -14
  161. package/build/flagsmith-engine/index.js +0 -127
  162. package/build/flagsmith-engine/organisations/models.js +0 -21
  163. package/build/flagsmith-engine/organisations/util.js +0 -8
  164. package/build/flagsmith-engine/projects/util.js +0 -15
  165. package/build/flagsmith-engine/segments/evaluators.js +0 -45
  166. package/build/flagsmith-engine/segments/models.js +0 -147
  167. package/build/flagsmith-engine/utils/collections.js +0 -26
  168. package/build/flagsmith-engine/utils/errors.js +0 -26
  169. package/build/sdk/analytics.js +0 -120
  170. package/build/sdk/errors.js +0 -34
  171. package/build/sdk/index.js +0 -594
  172. package/build/sdk/models.js +0 -149
  173. package/build/sdk/offline_handlers.js +0 -66
  174. package/build/sdk/polling_manager.js +0 -72
  175. package/build/sdk/utils.d.ts +0 -12
  176. package/build/sdk/utils.js +0 -100
  177. package/jest.config.js +0 -5
  178. package/tests/index.js +0 -0
  179. /package/build/{flagsmith-engine → cjs/flagsmith-engine}/features/constants.d.ts +0 -0
  180. /package/build/{flagsmith-engine → cjs/flagsmith-engine}/features/constants.js +0 -0
  181. /package/build/{flagsmith-engine → cjs/flagsmith-engine}/features/models.d.ts +0 -0
  182. /package/build/{flagsmith-engine → cjs/flagsmith-engine}/identities/traits/models.d.ts +0 -0
  183. /package/build/{flagsmith-engine → cjs/flagsmith-engine}/organisations/models.d.ts +0 -0
  184. /package/build/{flagsmith-engine → cjs/flagsmith-engine}/segments/constants.d.ts +0 -0
  185. /package/build/{flagsmith-engine → cjs/flagsmith-engine}/segments/constants.js +0 -0
  186. /package/build/{flagsmith-engine → cjs/flagsmith-engine}/utils/errors.d.ts +0 -0
  187. /package/build/{flagsmith-engine → cjs/flagsmith-engine}/utils/hashing/index.d.ts +0 -0
  188. /package/build/{flagsmith-engine → cjs/flagsmith-engine}/utils/index.d.ts +0 -0
  189. /package/build/{sdk → cjs/sdk}/errors.d.ts +0 -0
  190. /package/build/{sdk → cjs/sdk}/types.js +0 -0
@@ -1,7 +1,7 @@
1
- import Flagsmith from '.';
1
+ import Flagsmith from './index.js';
2
2
 
3
3
  export class EnvironmentDataPollingManager {
4
- private interval?: NodeJS.Timer;
4
+ private interval?: NodeJS.Timeout;
5
5
  private main: Flagsmith;
6
6
  private refreshIntervalSeconds: number;
7
7
 
@@ -17,7 +17,6 @@ export class EnvironmentDataPollingManager {
17
17
  await this.main.updateEnvironment();
18
18
  }, this.refreshIntervalSeconds * 1000);
19
19
  };
20
- this.main.updateEnvironment();
21
20
  updateEnvironment();
22
21
  }
23
22
 
package/sdk/types.ts CHANGED
@@ -1,30 +1,41 @@
1
- import { DefaultFlag, Flags } from "./models";
2
- import { EnvironmentModel } from "../flagsmith-engine";
3
- import { RequestInit } from "node-fetch";
4
- import { Logger } from "pino";
5
- import { BaseOfflineHandler } from "./offline_handlers";
1
+ import { DefaultFlag, Flags } from './models.js';
2
+ import { EnvironmentModel } from '../flagsmith-engine/index.js';
3
+ import { Dispatcher } from 'undici-types';
4
+ import { Logger } from 'pino';
5
+ import { BaseOfflineHandler } from './offline_handlers.js';
6
6
 
7
+ export type IFlagsmithValue<T = string | number | boolean | null> = T;
7
8
  export interface FlagsmithCache {
8
- get(key: string): Promise<Flags|undefined> | undefined;
9
- set(key: string, value: Flags, ttl: string | number): boolean | Promise<boolean>;
10
- has(key: string): boolean | Promise<boolean>;
11
- [key: string]: any;
9
+ get(key: string): Promise<Flags | undefined> | undefined;
10
+ set(key: string, value: Flags, ttl?: string | number): boolean | Promise<boolean>;
11
+ has(key: string): boolean | Promise<boolean>;
12
+ [key: string]: any;
12
13
  }
13
14
 
15
+ export type Fetch = typeof fetch
16
+
14
17
  export interface FlagsmithConfig {
15
- environmentKey?: string;
16
- apiUrl?: string;
17
- agent?:RequestInit['agent'];
18
- customHeaders?: { [key: string]: any };
19
- requestTimeoutSeconds?: number;
20
- enableLocalEvaluation?: boolean;
21
- environmentRefreshIntervalSeconds?: number;
22
- retries?: number;
23
- enableAnalytics?: boolean;
24
- defaultFlagHandler?: (featureName: string) => DefaultFlag;
25
- cache?: FlagsmithCache,
26
- onEnvironmentChange?: (error: Error | null, result: EnvironmentModel) => void,
27
- logger?: Logger
28
- offlineMode?: boolean;
29
- offlineHandler?: BaseOfflineHandler;
18
+ environmentKey?: string;
19
+ apiUrl?: string;
20
+ agent?: Dispatcher;
21
+ fetch?: Fetch;
22
+ customHeaders?: { [key: string]: any };
23
+ requestTimeoutSeconds?: number;
24
+ enableLocalEvaluation?: boolean;
25
+ environmentRefreshIntervalSeconds?: number;
26
+ retries?: number;
27
+ enableAnalytics?: boolean;
28
+ defaultFlagHandler?: (featureName: string) => DefaultFlag;
29
+ cache?: FlagsmithCache;
30
+ onEnvironmentChange?: (error: Error | null, result: EnvironmentModel) => void;
31
+ logger?: Logger;
32
+ offlineMode?: boolean;
33
+ offlineHandler?: BaseOfflineHandler;
34
+ }
35
+
36
+ export interface ITraitConfig {
37
+ value: FlagsmithTraitValue;
38
+ transient?: boolean;
30
39
  }
40
+
41
+ export declare type FlagsmithTraitValue = IFlagsmithValue;
package/sdk/utils.ts CHANGED
@@ -1,12 +1,36 @@
1
- import fetch, { RequestInit, Response } from 'node-fetch';
2
- // @ts-ignore
3
- if (typeof fetch.default !== 'undefined') fetch = fetch.default;
1
+ import {Fetch, FlagsmithTraitValue, ITraitConfig} from './types.js';
2
+ import {Dispatcher} from "undici-types";
4
3
 
5
- export function generateIdentitiesData(identifier: string, traits: { [key: string]: any }) {
6
- const traitsGenerated = Object.entries(traits).map(trait => ({
7
- trait_key: trait[0],
8
- trait_value: trait[1]
9
- }));
4
+ type Traits = { [key: string]: ITraitConfig | FlagsmithTraitValue };
5
+
6
+ export function isTraitConfig(
7
+ traitValue: ITraitConfig | FlagsmithTraitValue
8
+ ): traitValue is ITraitConfig {
9
+ return !!traitValue && typeof traitValue == 'object' && traitValue.value !== undefined;
10
+ }
11
+
12
+ export function generateIdentitiesData(identifier: string, traits: Traits, transient: boolean) {
13
+ const traitsGenerated = Object.entries(traits).map(([key, value]) => {
14
+ if (isTraitConfig(value)) {
15
+ return {
16
+ trait_key: key,
17
+ trait_value: value?.value,
18
+ transient: value?.transient,
19
+ };
20
+ } else {
21
+ return {
22
+ trait_key: key,
23
+ trait_value: value,
24
+ };
25
+ }
26
+ });
27
+ if (transient) {
28
+ return {
29
+ identifier: identifier,
30
+ traits: traitsGenerated,
31
+ transient: true
32
+ };
33
+ }
10
34
  return {
11
35
  identifier: identifier,
12
36
  traits: traitsGenerated
@@ -18,32 +42,28 @@ export const delay = (ms: number) =>
18
42
 
19
43
  export const retryFetch = (
20
44
  url: string,
21
- fetchOptions: RequestInit,
45
+ // built-in RequestInit type doesn't have dispatcher/agent
46
+ fetchOptions: RequestInit & { dispatcher?: Dispatcher },
22
47
  retries: number = 3,
23
- timeout: number = 10// set an overall timeout for this function
48
+ timeoutMs: number = 10, // set an overall timeout for this function
49
+ customFetch: Fetch,
24
50
  ): Promise<Response> => {
25
51
  return new Promise((resolve, reject) => {
26
52
  const retryWrapper = (n: number) => {
27
- requestWrapper()
28
- .then(res => resolve(res))
29
- .catch(async err => {
30
- if (n > 0) {
31
- await delay(1000);
32
- retryWrapper(--n);
33
- } else {
34
- reject(err);
35
- }
36
- });
37
- };
38
-
39
- const requestWrapper = (): Promise<Response> => {
40
- return new Promise((resolve, reject) => {
41
- if (timeout) setTimeout(() => reject('error: timeout'), timeout);
42
- return fetch(url, fetchOptions)
43
- .then(res => resolve(res))
44
- .catch(err => reject(err))
53
+ customFetch(url, {
54
+ ...fetchOptions,
55
+ signal: AbortSignal.timeout(timeoutMs)
45
56
  })
46
- }
57
+ .then(res => resolve(res))
58
+ .catch(async err => {
59
+ if (n > 0) {
60
+ await delay(1000);
61
+ retryWrapper(--n);
62
+ } else {
63
+ reject(err);
64
+ }
65
+ });
66
+ };
47
67
 
48
68
  retryWrapper(retries);
49
69
  });
@@ -1,9 +1,9 @@
1
1
  import { readFileSync } from 'fs';
2
- import { getIdentityFeatureStates } from '../../../flagsmith-engine';
3
- import { EnvironmentModel } from '../../../flagsmith-engine/environments/models';
4
- import { buildEnvironmentModel } from '../../../flagsmith-engine/environments/util';
5
- import { IdentityModel } from '../../../flagsmith-engine/identities/models';
6
- import { buildIdentityModel } from '../../../flagsmith-engine/identities/util';
2
+ import { getIdentityFeatureStates } from '../../../flagsmith-engine/index.js';
3
+ import { EnvironmentModel } from '../../../flagsmith-engine/environments/models.js';
4
+ import { buildEnvironmentModel } from '../../../flagsmith-engine/environments/util.js';
5
+ import { IdentityModel } from '../../../flagsmith-engine/identities/models.js';
6
+ import { buildIdentityModel } from '../../../flagsmith-engine/identities/util.js';
7
7
 
8
8
  function extractTestCases(
9
9
  filePath: string
@@ -3,10 +3,10 @@ import {
3
3
  getEnvironmentFeatureStates,
4
4
  getIdentityFeatureState,
5
5
  getIdentityFeatureStates
6
- } from '../../../flagsmith-engine';
7
- import { CONSTANTS } from '../../../flagsmith-engine/features/constants';
8
- import { FeatureModel, FeatureStateModel } from '../../../flagsmith-engine/features/models';
9
- import { TraitModel } from '../../../flagsmith-engine/identities/traits/models';
6
+ } from '../../../flagsmith-engine/index.js';
7
+ import { CONSTANTS } from '../../../flagsmith-engine/features/constants.js';
8
+ import { FeatureModel, FeatureStateModel } from '../../../flagsmith-engine/features/models.js';
9
+ import { TraitModel } from '../../../flagsmith-engine/identities/traits/models.js';
10
10
  import {
11
11
  environment,
12
12
  environmentWithSegmentOverride,
@@ -17,7 +17,7 @@ import {
17
17
  identityInSegment,
18
18
  segmentConditionProperty,
19
19
  segmentConditionStringValue
20
- } from './utils';
20
+ } from './utils.js';
21
21
 
22
22
  test('test_identity_get_feature_state_without_any_override', () => {
23
23
  const feature_state = getIdentityFeatureState(environment(), identity(), feature1().name);
@@ -2,18 +2,18 @@ import {
2
2
  ALL_RULE,
3
3
  CONDITION_OPERATORS,
4
4
  PERCENTAGE_SPLIT,
5
- } from '../../../../flagsmith-engine/segments/constants';
6
- import {SegmentConditionModel} from '../../../../flagsmith-engine/segments/models';
7
- import {traitsMatchSegmentCondition, evaluateIdentityInSegment} from "../../../../flagsmith-engine/segments/evaluators";
8
- import {TraitModel, IdentityModel} from "../../../../flagsmith-engine";
9
- import {environment} from "../utils";
10
- import { buildSegmentModel } from '../../../../flagsmith-engine/segments/util';
11
- import { getHashedPercentateForObjIds } from '../../../../flagsmith-engine/utils/hashing';
5
+ } from '../../../../flagsmith-engine/segments/constants.js';
6
+ import {SegmentConditionModel} from '../../../../flagsmith-engine/segments/models.js';
7
+ import {traitsMatchSegmentCondition, evaluateIdentityInSegment} from "../../../../flagsmith-engine/segments/evaluators.js";
8
+ import {TraitModel, IdentityModel} from "../../../../flagsmith-engine/index.js";
9
+ import {environment} from "../utils.js";
10
+ import { buildSegmentModel } from '../../../../flagsmith-engine/segments/util.js';
11
+ import { getHashedPercentateForObjIds } from '../../../../flagsmith-engine/utils/hashing/index.js';
12
12
 
13
13
 
14
14
  // todo: work out how to implement this in a test function or before hook
15
- jest.mock('../../../../flagsmith-engine/utils/hashing', () => ({
16
- getHashedPercentateForObjIds: jest.fn(() => 1)
15
+ vi.mock('../../../../flagsmith-engine/utils/hashing', () => ({
16
+ getHashedPercentateForObjIds: vi.fn(() => 1)
17
17
  }));
18
18
 
19
19
 
@@ -1,5 +1,5 @@
1
1
  import { v4 as uuidv4 } from 'uuid';
2
- import { getHashedPercentateForObjIds } from '../../../../flagsmith-engine/utils/hashing';
2
+ import { getHashedPercentateForObjIds } from '../../../../flagsmith-engine/utils/hashing/index.js';
3
3
 
4
4
  describe('getHashedPercentageForObjIds', () => {
5
5
  it.each([
@@ -1,10 +1,7 @@
1
- import fetch from 'node-fetch';
2
- import { analyticsProcessor } from './utils';
3
-
4
- jest.mock('node-fetch', () => jest.fn());
1
+ import {analyticsProcessor, fetch} from './utils.js';
5
2
 
6
3
  afterEach(() => {
7
- jest.clearAllMocks();
4
+ vi.resetAllMocks();
8
5
  });
9
6
 
10
7
  test('test_analytics_processor_track_feature_updates_analytics_data', () => {
@@ -29,15 +26,14 @@ test('test_analytics_processor_flush_post_request_data_match_ananlytics_data', a
29
26
  aP.trackFeature("myFeature2");
30
27
  await aP.flush();
31
28
  expect(fetch).toHaveBeenCalledTimes(1);
32
- expect(fetch).toHaveBeenCalledWith('http://testUrlanalytics/flags/', {
29
+ expect(fetch).toHaveBeenCalledWith('http://testUrlanalytics/flags/', expect.objectContaining({
33
30
  body: '{"myFeature1":1,"myFeature2":1}',
34
31
  headers: { 'Content-Type': 'application/json', 'X-Environment-Key': 'test-key' },
35
32
  method: 'POST',
36
- timeout: 3000
37
- });
33
+ }));
38
34
  });
39
35
 
40
- jest.useFakeTimers()
36
+ vi.useFakeTimers()
41
37
  test('test_analytics_processor_flush_post_request_data_match_ananlytics_data_test', async () => {
42
38
  const aP = analyticsProcessor();
43
39
  aP.trackFeature("myFeature1");
@@ -45,7 +41,7 @@ test('test_analytics_processor_flush_post_request_data_match_ananlytics_data_tes
45
41
  aP.trackFeature("myFeature2");
46
42
  expect(fetch).toHaveBeenCalledTimes(1);
47
43
  }, 15000);
48
- jest.runOnlyPendingTimers();
44
+ vi.runOnlyPendingTimers();
49
45
  });
50
46
 
51
47
  test('test_analytics_processor_flush_early_exit_if_analytics_data_is_empty', async () => {
@@ -58,7 +54,7 @@ test('test_analytics_processor_flush_early_exit_if_analytics_data_is_empty', asy
58
54
  test('errors in fetch sending analytics data are swallowed', async () => {
59
55
  // Given
60
56
  // we mock the fetch function to throw and error to mimick a network failure
61
- (fetch as jest.MockedFunction<typeof fetch>).mockRejectedValue(new Error('some error'));
57
+ fetch.mockRejectedValue('some error');
62
58
 
63
59
  // and create the processor and track a feature so there is some analytics data
64
60
  const processor = analyticsProcessor();
@@ -76,7 +72,6 @@ test('errors in fetch sending analytics data are swallowed', async () => {
76
72
  test('analytics is only flushed once even if requested concurrently', async () => {
77
73
  const processor = analyticsProcessor();
78
74
  processor.trackFeature('myFeature');
79
- // @ts-ignore
80
75
  fetch.mockImplementation(() => {
81
76
  return new Promise((resolve, _) => {
82
77
  setTimeout(resolve, 1000)
@@ -86,7 +81,7 @@ test('analytics is only flushed once even if requested concurrently', async () =
86
81
  processor.flush(),
87
82
  processor.flush(),
88
83
  ])
89
- jest.runOnlyPendingTimers();
84
+ vi.runOnlyPendingTimers();
90
85
  await flushes;
91
86
  expect(fetch).toHaveBeenCalledTimes(1)
92
87
  })
@@ -0,0 +1,41 @@
1
+ {
2
+ "traits": [
3
+ {
4
+ "id": 1,
5
+ "trait_key": "some_trait",
6
+ "trait_value": "some_value"
7
+ },
8
+ {
9
+ "id": 2,
10
+ "trait_key": "transient_key",
11
+ "trait_value": "transient_value",
12
+ "transient": true
13
+ },
14
+ {
15
+ "id": 3,
16
+ "trait_key": "explicitly_non_transient_trait",
17
+ "trait_value": "non_transient_value",
18
+ "transient": false
19
+ }
20
+ ],
21
+ "flags": [
22
+ {
23
+ "id": 1,
24
+ "feature": {
25
+ "id": 1,
26
+ "name": "some_feature",
27
+ "created_date": "2019-08-27T14:53:45.698555Z",
28
+ "initial_value": null,
29
+ "description": null,
30
+ "default_enabled": false,
31
+ "type": "STANDARD",
32
+ "project": 1
33
+ },
34
+ "feature_state_value": "some-identity-with-transient-trait-value",
35
+ "enabled": true,
36
+ "environment": 1,
37
+ "identity": null,
38
+ "feature_segment": null
39
+ }
40
+ ]
41
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "traits": [
3
+ {
4
+ "id": 1,
5
+ "trait_key": "some_trait",
6
+ "trait_value": "some_value"
7
+ }
8
+ ],
9
+ "flags": [
10
+ {
11
+ "id": 1,
12
+ "feature": {
13
+ "id": 1,
14
+ "name": "some_feature",
15
+ "created_date": "2019-08-27T14:53:45.698555Z",
16
+ "initial_value": null,
17
+ "description": null,
18
+ "default_enabled": false,
19
+ "type": "STANDARD",
20
+ "project": 1
21
+ },
22
+ "feature_state_value": "some-transient-identity-value",
23
+ "enabled": false,
24
+ "environment": 1,
25
+ "identity": null,
26
+ "feature_segment": null
27
+ }
28
+ ]
29
+ }
@@ -1,14 +1,7 @@
1
- import fetch, { Headers } from 'node-fetch';
2
- import { environmentJSON, environmentModel, flagsJSON, flagsmith, identitiesJSON, TestCache } from './utils';
3
-
4
- jest.mock('node-fetch');
5
- jest.mock('../../sdk/polling_manager');
6
-
7
- const { Response } = jest.requireActual('node-fetch');
1
+ import { fetch, environmentJSON, environmentModel, flagsJSON, flagsmith, identitiesJSON, TestCache } from './utils.js';
8
2
 
9
3
  beforeEach(() => {
10
- // @ts-ignore
11
- jest.clearAllMocks();
4
+ vi.clearAllMocks();
12
5
  });
13
6
 
14
7
  test('test_wrong_cache_interface_throws_an_error', async () => {
@@ -21,14 +14,13 @@ test('test_wrong_cache_interface_throws_an_error', async () => {
21
14
  });
22
15
 
23
16
  test('test_empty_cache_not_read_but_populated', async () => {
24
- // @ts-ignore
25
- fetch.mockReturnValue(Promise.resolve(new Response(flagsJSON())));
17
+ fetch.mockResolvedValue(new Response(flagsJSON));
26
18
 
27
19
  const cache = new TestCache();
28
- const set = jest.spyOn(cache, 'set');
20
+ const set = vi.spyOn(cache, 'set');
29
21
 
30
22
  const flg = flagsmith({ cache });
31
- const allFlags = await (await flg.getEnvironmentFlags()).allFlags();
23
+ const allFlags = (await flg.getEnvironmentFlags()).allFlags();
32
24
 
33
25
  expect(set).toBeCalled();
34
26
  expect(await cache.has('flags')).toBe(true);
@@ -40,11 +32,10 @@ test('test_empty_cache_not_read_but_populated', async () => {
40
32
  });
41
33
 
42
34
  test('test_api_not_called_when_cache_present', async () => {
43
- // @ts-ignore
44
- fetch.mockReturnValue(Promise.resolve(new Response(flagsJSON())));
35
+ fetch.mockResolvedValue(new Response(flagsJSON));
45
36
 
46
37
  const cache = new TestCache();
47
- const set = jest.spyOn(cache, 'set');
38
+ const set = vi.spyOn(cache, 'set');
48
39
 
49
40
  const flg = flagsmith({ cache });
50
41
  await (await flg.getEnvironmentFlags()).allFlags();
@@ -60,13 +51,11 @@ test('test_api_not_called_when_cache_present', async () => {
60
51
  });
61
52
 
62
53
  test('test_api_called_twice_when_no_cache', async () => {
63
- // @ts-ignore
64
- fetch.mockReturnValue(Promise.resolve(new Response(flagsJSON())));
54
+ fetch.mockImplementation(() => Promise.resolve(new Response(flagsJSON)));
65
55
 
66
56
  const flg = flagsmith();
67
57
  await (await flg.getEnvironmentFlags()).allFlags();
68
- // @ts-ignore
69
- fetch.mockReturnValue(Promise.resolve(new Response(flagsJSON())));
58
+
70
59
  const allFlags = await (await flg.getEnvironmentFlags()).allFlags();
71
60
 
72
61
  expect(fetch).toBeCalledTimes(2);
@@ -76,14 +65,13 @@ test('test_api_called_twice_when_no_cache', async () => {
76
65
  });
77
66
 
78
67
  test('test_get_environment_flags_uses_local_environment_when_available', async () => {
79
- // @ts-ignore
80
- fetch.mockReturnValue(Promise.resolve(new Response(flagsJSON())));
68
+ fetch.mockResolvedValue(new Response(flagsJSON));
81
69
 
82
70
  const cache = new TestCache();
83
- const set = jest.spyOn(cache, 'set');
71
+ const set = vi.spyOn(cache, 'set');
84
72
 
85
73
  const flg = flagsmith({ cache });
86
- const model = environmentModel(JSON.parse(environmentJSON()));
74
+ const model = environmentModel(JSON.parse(environmentJSON));
87
75
  flg.environment = model;
88
76
 
89
77
  const allFlags = await (await flg.getEnvironmentFlags()).allFlags();
@@ -96,11 +84,10 @@ test('test_get_environment_flags_uses_local_environment_when_available', async (
96
84
  });
97
85
 
98
86
  test('test_cache_used_for_identity_flags', async () => {
99
- // @ts-ignore
100
- fetch.mockReturnValue(Promise.resolve(new Response(identitiesJSON())));
87
+ fetch.mockResolvedValue(new Response(identitiesJSON));
101
88
 
102
89
  const cache = new TestCache();
103
- const set = jest.spyOn(cache, 'set');
90
+ const set = vi.spyOn(cache, 'set');
104
91
 
105
92
  const identifier = 'identifier';
106
93
  const traits = { some_trait: 'some_value' };
@@ -120,11 +107,10 @@ test('test_cache_used_for_identity_flags', async () => {
120
107
  });
121
108
 
122
109
  test('test_cache_used_for_identity_flags_local_evaluation', async () => {
123
- // @ts-ignore
124
- fetch.mockReturnValue(Promise.resolve(new Response(environmentJSON())));
110
+ fetch.mockResolvedValue(new Response(environmentJSON));
125
111
 
126
112
  const cache = new TestCache();
127
- const set = jest.spyOn(cache, 'set');
113
+ const set = vi.spyOn(cache, 'set');
128
114
 
129
115
  const identifier = 'identifier';
130
116
  const traits = { some_trait: 'some_value' };
@@ -146,5 +132,3 @@ test('test_cache_used_for_identity_flags_local_evaluation', async () => {
146
132
  expect(identityFlags[0].value).toBe('some-value');
147
133
  expect(identityFlags[0].featureName).toBe('some_feature');
148
134
  });
149
-
150
- test('test_cache_used_for_all_flags', async () => { });
@@ -1,20 +1,15 @@
1
- import Flagsmith from '../../sdk';
2
- import fetch from 'node-fetch';
3
- import { environmentJSON, environmentModel, flagsJSON, flagsmith, identitiesJSON } from './utils';
4
- import { DefaultFlag } from '../../sdk/models';
1
+ import Flagsmith from '../../sdk/index.js';
2
+ import { environmentJSON, environmentModel, flagsJSON, flagsmith, fetch } from './utils.js';
3
+ import { DefaultFlag } from '../../sdk/models.js';
5
4
 
6
- jest.mock('node-fetch');
7
- jest.mock('../../sdk/polling_manager');
8
- const { Response } = jest.requireActual('node-fetch');
5
+ vi.mock('../../sdk/polling_manager');
9
6
 
10
7
  beforeEach(() => {
11
- // @ts-ignore
12
- jest.clearAllMocks();
8
+ vi.clearAllMocks();
13
9
  });
14
10
 
15
11
  test('test_get_environment_flags_calls_api_when_no_local_environment', async () => {
16
- // @ts-ignore
17
- fetch.mockReturnValue(Promise.resolve(new Response(flagsJSON())));
12
+ fetch.mockResolvedValue(new Response(flagsJSON));
18
13
 
19
14
  const flg = flagsmith();
20
15
  const allFlags = await (await flg.getEnvironmentFlags()).allFlags();
@@ -26,11 +21,10 @@ test('test_get_environment_flags_calls_api_when_no_local_environment', async ()
26
21
  });
27
22
 
28
23
  test('test_get_environment_flags_uses_local_environment_when_available', async () => {
29
- // @ts-ignore
30
- fetch.mockReturnValue(Promise.resolve(new Response(flagsJSON())));
24
+ fetch.mockResolvedValue(new Response(flagsJSON));
31
25
 
32
26
  const flg = flagsmith();
33
- const model = environmentModel(JSON.parse(environmentJSON()));
27
+ const model = environmentModel(JSON.parse(environmentJSON));
34
28
  flg.environment = model;
35
29
 
36
30
  const allFlags = await (await flg.getEnvironmentFlags()).allFlags();
@@ -41,8 +35,7 @@ test('test_get_environment_flags_uses_local_environment_when_available', async (
41
35
  });
42
36
 
43
37
  test('test_default_flag_is_used_when_no_environment_flags_returned', async () => {
44
- // @ts-ignore
45
- fetch.mockReturnValue(Promise.resolve(new Response(JSON.stringify([]))));
38
+ fetch.mockResolvedValue(new Response(JSON.stringify([])));
46
39
 
47
40
  const defaultFlag = new DefaultFlag('some-default-value', true);
48
41
 
@@ -64,14 +57,13 @@ test('test_default_flag_is_used_when_no_environment_flags_returned', async () =>
64
57
  });
65
58
 
66
59
  test('test_analytics_processor_tracks_flags', async () => {
67
- // @ts-ignore
68
- fetch.mockReturnValue(Promise.resolve(new Response(flagsJSON())));
60
+ fetch.mockResolvedValue(new Response(flagsJSON));
69
61
 
70
62
  const defaultFlag = new DefaultFlag('some-default-value', true);
71
63
 
72
64
  const defaultFlagHandler = (featureName: string) => defaultFlag;
73
65
 
74
- const flg = new Flagsmith({
66
+ const flg = flagsmith({
75
67
  environmentKey: 'key',
76
68
  defaultFlagHandler: defaultFlagHandler,
77
69
  enableAnalytics: true,
@@ -86,14 +78,13 @@ test('test_analytics_processor_tracks_flags', async () => {
86
78
  });
87
79
 
88
80
  test('test_getFeatureValue', async () => {
89
- // @ts-ignore
90
- fetch.mockReturnValue(Promise.resolve(new Response(flagsJSON())));
81
+ fetch.mockResolvedValue(new Response(flagsJSON));
91
82
 
92
83
  const defaultFlag = new DefaultFlag('some-default-value', true);
93
84
 
94
85
  const defaultFlagHandler = (featureName: string) => defaultFlag;
95
86
 
96
- const flg = new Flagsmith({
87
+ const flg = flagsmith({
97
88
  environmentKey: 'key',
98
89
  defaultFlagHandler: defaultFlagHandler,
99
90
  enableAnalytics: true,
@@ -106,11 +97,9 @@ test('test_getFeatureValue', async () => {
106
97
  });
107
98
 
108
99
  test('test_throws_when_no_default_flag_handler_after_multiple_API_errors', async () => {
109
- fetch
110
- // @ts-ignore
111
- .mockRejectedValue(new Error('Error during fetching the API response'));
100
+ fetch.mockRejectedValue('Error during fetching the API response');
112
101
 
113
- const flg = new Flagsmith({
102
+ const flg = flagsmith({
114
103
  environmentKey: 'key',
115
104
  });
116
105
 
@@ -124,8 +113,7 @@ test('test_non_200_response_raises_flagsmith_api_error', async () => {
124
113
  const errorResponse403 = new Response('403 Forbidden', {
125
114
  status: 403
126
115
  });
127
- // @ts-ignore
128
- fetch.mockReturnValue(Promise.resolve(errorResponse403));
116
+ fetch.mockResolvedValue(errorResponse403);
129
117
 
130
118
  const flg = new Flagsmith({
131
119
  environmentKey: 'some'
@@ -134,14 +122,13 @@ test('test_non_200_response_raises_flagsmith_api_error', async () => {
134
122
  await expect(flg.getEnvironmentFlags()).rejects.toThrow();
135
123
  });
136
124
  test('test_default_flag_is_not_used_when_environment_flags_returned', async () => {
137
- // @ts-ignore
138
- fetch.mockReturnValue(Promise.resolve(new Response(flagsJSON())));
125
+ fetch.mockResolvedValue(new Response(flagsJSON));
139
126
 
140
127
  const defaultFlag = new DefaultFlag('some-default-value', true);
141
128
 
142
129
  const defaultFlagHandler = (featureName: string) => defaultFlag;
143
130
 
144
- const flg = new Flagsmith({
131
+ const flg = flagsmith({
145
132
  environmentKey: 'key',
146
133
  defaultFlagHandler: defaultFlagHandler
147
134
  });
@@ -155,8 +142,7 @@ test('test_default_flag_is_not_used_when_environment_flags_returned', async () =
155
142
  });
156
143
 
157
144
  test('test_default_flag_is_used_when_bad_api_response_happens', async () => {
158
- // @ts-ignore
159
- fetch.mockReturnValue(Promise.resolve(new Response('bad-data')));
145
+ fetch.mockResolvedValue(new Response('bad-data'));
160
146
 
161
147
  const defaultFlag = new DefaultFlag('some-default-value', true);
162
148
 
@@ -175,14 +161,13 @@ test('test_default_flag_is_used_when_bad_api_response_happens', async () => {
175
161
  });
176
162
 
177
163
  test('test_local_evaluation', async () => {
178
- // @ts-ignore
179
- fetch.mockReturnValue(Promise.resolve(new Response(environmentJSON())));
164
+ fetch.mockResolvedValue(new Response(environmentJSON));
180
165
 
181
166
  const defaultFlag = new DefaultFlag('some-default-value', true);
182
167
 
183
168
  const defaultFlagHandler = (featureName: string) => defaultFlag;
184
169
 
185
- const flg = new Flagsmith({
170
+ const flg = flagsmith({
186
171
  environmentKey: 'ser.key',
187
172
  enableLocalEvaluation: true,
188
173
  defaultFlagHandler: defaultFlagHandler