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,21 +1,16 @@
1
- import Flagsmith from '../../sdk';
2
- import fetch from 'node-fetch';
3
- import { environmentJSON, flagsmith, identitiesJSON } from './utils';
4
- import { DefaultFlag } from '../../sdk/models';
1
+ import Flagsmith from '../../sdk/index.js';
2
+ import { fetch, environmentJSON, flagsmith, identitiesJSON, identityWithTransientTraitsJSON, transientIdentityJSON } 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
 
16
12
  test('test_get_identity_flags_calls_api_when_no_local_environment_no_traits', async () => {
17
- // @ts-ignore
18
- fetch.mockReturnValue(Promise.resolve(new Response(identitiesJSON())));
13
+ fetch.mockResolvedValue(new Response(identitiesJSON));
19
14
  const identifier = 'identifier';
20
15
 
21
16
  const flg = flagsmith();
@@ -28,14 +23,12 @@ test('test_get_identity_flags_calls_api_when_no_local_environment_no_traits', as
28
23
  });
29
24
 
30
25
  test('test_get_identity_flags_uses_environment_when_local_environment_no_traits', async () => {
31
- // @ts-ignore
32
- fetch.mockReturnValue(Promise.resolve(new Response(environmentJSON())));
26
+ fetch.mockResolvedValue(new Response(environmentJSON))
33
27
  const identifier = 'identifier';
34
28
 
35
29
  const flg = flagsmith({
36
- environmentKey: 'ser.key',
37
- enableLocalEvaluation: true,
38
-
30
+ environmentKey: 'ser.key',
31
+ enableLocalEvaluation: true,
39
32
  });
40
33
 
41
34
 
@@ -47,8 +40,7 @@ test('test_get_identity_flags_uses_environment_when_local_environment_no_traits'
47
40
  });
48
41
 
49
42
  test('test_get_identity_flags_calls_api_when_no_local_environment_with_traits', async () => {
50
- // @ts-ignore
51
- fetch.mockReturnValue(Promise.resolve(new Response(identitiesJSON())));
43
+ fetch.mockResolvedValue(new Response(identitiesJSON))
52
44
  const identifier = 'identifier';
53
45
  const traits = { some_trait: 'some_value' };
54
46
  const flg = flagsmith();
@@ -61,14 +53,13 @@ test('test_get_identity_flags_calls_api_when_no_local_environment_with_traits',
61
53
  });
62
54
 
63
55
  test('test_default_flag_is_not_used_when_identity_flags_returned', async () => {
64
- // @ts-ignore
65
- fetch.mockReturnValue(Promise.resolve(new Response(identitiesJSON())));
56
+ fetch.mockResolvedValue(new Response(identitiesJSON))
66
57
 
67
58
  const defaultFlag = new DefaultFlag('some-default-value', true);
68
59
 
69
60
  const defaultFlagHandler = (featureName: string) => defaultFlag;
70
61
 
71
- const flg = new Flagsmith({
62
+ const flg = flagsmith({
72
63
  environmentKey: 'key',
73
64
  defaultFlagHandler: defaultFlagHandler
74
65
  });
@@ -82,8 +73,7 @@ test('test_default_flag_is_not_used_when_identity_flags_returned', async () => {
82
73
  });
83
74
 
84
75
  test('test_default_flag_is_used_when_no_identity_flags_returned', async () => {
85
- // @ts-ignore
86
- fetch.mockReturnValue(Promise.resolve(new Response(JSON.stringify({ flags: [], traits: [] }))));
76
+ fetch.mockResolvedValue(new Response(JSON.stringify({ flags: [], traits: [] })));
87
77
 
88
78
  const defaultFlag = new DefaultFlag('some-default-value', true);
89
79
  const defaultFlagHandler = (featureName: string) => defaultFlag;
@@ -102,8 +92,7 @@ test('test_default_flag_is_used_when_no_identity_flags_returned', async () => {
102
92
  });
103
93
 
104
94
  test('test_default_flag_is_used_when_no_identity_flags_returned_due_to_error', async () => {
105
- // @ts-ignore
106
- fetch.mockReturnValue(Promise.resolve(new Response('bad data')));
95
+ fetch.mockResolvedValue(new Response('bad data'))
107
96
 
108
97
  const defaultFlag = new DefaultFlag('some-default-value', true);
109
98
  const defaultFlagHandler = (featureName: string) => defaultFlag;
@@ -122,12 +111,10 @@ test('test_default_flag_is_used_when_no_identity_flags_returned_due_to_error', a
122
111
  });
123
112
 
124
113
  test('test_default_flag_is_used_when_no_identity_flags_returned_and_no_custom_default_flag_handler', async () => {
125
- // @ts-ignore
126
- fetch.mockReturnValue(Promise.resolve(new Response(JSON.stringify({ flags: [], traits: [] }))));
127
-
114
+ fetch.mockResolvedValue(new Response(JSON.stringify({ flags: [], traits: [] })))
128
115
 
129
- const flg = new Flagsmith({
130
- environmentKey: 'key',
116
+ const flg = flagsmith({
117
+ environmentKey: 'key',
131
118
  });
132
119
 
133
120
  const flags = await flg.getIdentityFlags('identifier');
@@ -140,8 +127,7 @@ test('test_default_flag_is_used_when_no_identity_flags_returned_and_no_custom_de
140
127
 
141
128
 
142
129
  test('test_get_identity_flags_multivariate_value_with_local_evaluation_enabled', async () => {
143
- // @ts-ignore
144
- fetch.mockReturnValue(Promise.resolve(new Response(environmentJSON())));
130
+ fetch.mockResolvedValue(new Response(environmentJSON));
145
131
  const identifier = 'identifier';
146
132
 
147
133
  const flg = flagsmith({
@@ -155,3 +141,68 @@ test('test_get_identity_flags_multivariate_value_with_local_evaluation_enabled',
155
141
  expect(identityFlags.getFeatureValue('mv_feature')).toBe('bar');
156
142
  expect(identityFlags.isFeatureEnabled('mv_feature')).toBe(false);
157
143
  });
144
+
145
+
146
+ test('test_transient_identity', async () => {
147
+ fetch.mockResolvedValue(new Response(transientIdentityJSON));
148
+ const identifier = 'transient_identifier';
149
+ const traits = { some_trait: 'some_value' };
150
+ const traitsInRequest = [{trait_key:Object.keys(traits)[0],trait_value:traits.some_trait}]
151
+ const transient = true;
152
+ const flg = flagsmith();
153
+ const identityFlags = (await flg.getIdentityFlags(identifier, traits, transient)).allFlags();
154
+
155
+ expect(fetch).toHaveBeenCalledWith(
156
+ `https://edge.api.flagsmith.com/api/v1/identities/`,
157
+ expect.objectContaining({
158
+ method: 'POST',
159
+ headers: { 'Content-Type': 'application/json', 'X-Environment-Key': 'sometestfakekey' },
160
+ body: JSON.stringify({identifier, traits: traitsInRequest, transient })
161
+ }
162
+ ));
163
+
164
+ expect(identityFlags[0].enabled).toBe(false);
165
+ expect(identityFlags[0].value).toBe('some-transient-identity-value');
166
+ expect(identityFlags[0].featureName).toBe('some_feature');
167
+ });
168
+
169
+
170
+ test('test_identity_with_transient_traits', async () => {
171
+ fetch.mockResolvedValue(new Response(identityWithTransientTraitsJSON));
172
+ const identifier = 'transient_trait_identifier';
173
+ const traits = {
174
+ some_trait: 'some_value',
175
+ another_trait: {value: 'another_value', transient: true},
176
+ explicitly_non_transient_trait: {value: 'non_transient_value', transient: false}
177
+ }
178
+ const traitsInRequest = [
179
+ {
180
+ trait_key:Object.keys(traits)[0],
181
+ trait_value:traits.some_trait,
182
+ },
183
+ {
184
+ trait_key:Object.keys(traits)[1],
185
+ trait_value:traits.another_trait.value,
186
+ transient: true,
187
+ },
188
+ {
189
+ trait_key:Object.keys(traits)[2],
190
+ trait_value:traits.explicitly_non_transient_trait.value,
191
+ transient: false,
192
+ },
193
+ ]
194
+ const flg = flagsmith();
195
+
196
+ const identityFlags = (await flg.getIdentityFlags(identifier, traits)).allFlags();
197
+ expect(fetch).toHaveBeenCalledWith(
198
+ `https://edge.api.flagsmith.com/api/v1/identities/`,
199
+ expect.objectContaining({
200
+ method: 'POST',
201
+ headers: { 'Content-Type': 'application/json', 'X-Environment-Key': 'sometestfakekey' },
202
+ body: JSON.stringify({identifier, traits: traitsInRequest})
203
+ })
204
+ );
205
+ expect(identityFlags[0].enabled).toBe(true);
206
+ expect(identityFlags[0].value).toBe('some-identity-with-transient-trait-value');
207
+ expect(identityFlags[0].featureName).toBe('some_feature');
208
+ });
@@ -1,25 +1,19 @@
1
- import Flagsmith from '../../sdk';
2
- import { EnvironmentDataPollingManager } from '../../sdk/polling_manager';
3
- import fetch, { RequestInit } from 'node-fetch';
4
- import { environmentJSON, environmentModel, flagsJSON, flagsmith, identitiesJSON } from './utils';
5
- import { DefaultFlag, Flags } from '../../sdk/models';
6
- import { delay } from '../../sdk/utils';
7
- import { EnvironmentModel } from '../../flagsmith-engine/environments/models';
8
- import https from 'https'
9
- import { BaseOfflineHandler } from '../../sdk/offline_handlers';
10
-
11
- jest.mock('node-fetch');
12
- jest.mock('../../sdk/polling_manager');
13
- const { Response } = jest.requireActual('node-fetch');
14
-
1
+ import Flagsmith from '../../sdk/index.js';
2
+ import { EnvironmentDataPollingManager } from '../../sdk/polling_manager.js';
3
+ import { environmentJSON, environmentModel, flagsJSON, flagsmith, fetch, offlineEnvironmentJSON } from './utils.js';
4
+ import { DefaultFlag, Flags } from '../../sdk/models.js';
5
+ import { delay } from '../../sdk/utils.js';
6
+ import { EnvironmentModel } from '../../flagsmith-engine/environments/models.js';
7
+ import { BaseOfflineHandler } from '../../sdk/offline_handlers.js';
8
+ import { Agent } from 'undici';
9
+
10
+ vi.mock('../../sdk/polling_manager');
15
11
  beforeEach(() => {
16
- // @ts-ignore
17
- jest.clearAllMocks();
12
+ vi.clearAllMocks();
18
13
  });
19
14
 
20
15
  test('test_flagsmith_starts_polling_manager_on_init_if_enabled', () => {
21
- // @ts-ignore
22
- fetch.mockReturnValue(Promise.resolve(new Response(environmentJSON())));
16
+ fetch.mockResolvedValue(new Response(environmentJSON));
23
17
  new Flagsmith({
24
18
  environmentKey: 'ser.key',
25
19
  enableLocalEvaluation: true
@@ -28,9 +22,8 @@ test('test_flagsmith_starts_polling_manager_on_init_if_enabled', () => {
28
22
  });
29
23
 
30
24
  test('test_flagsmith_local_evaluation_key_required', () => {
31
- // @ts-ignore
32
- fetch.mockReturnValue(Promise.resolve(new Response(environmentJSON())));
33
- console.error = jest.fn();
25
+ fetch.mockResolvedValue(new Response(environmentJSON));
26
+ console.error = vi.fn();
34
27
  new Flagsmith({
35
28
  environmentKey: 'bad.key',
36
29
  enableLocalEvaluation: true
@@ -39,26 +32,25 @@ test('test_flagsmith_local_evaluation_key_required', () => {
39
32
  });
40
33
 
41
34
  test('test_update_environment_sets_environment', async () => {
42
- // @ts-ignore
43
- fetch.mockReturnValue(Promise.resolve(new Response(environmentJSON())));
35
+ fetch.mockResolvedValue(new Response(environmentJSON));
44
36
  const flg = flagsmith();
45
37
  await flg.updateEnvironment();
46
38
  expect(flg.environment).toBeDefined();
47
39
 
48
- const model = environmentModel(JSON.parse(environmentJSON()));
40
+ const model = environmentModel(JSON.parse(environmentJSON));
49
41
 
50
42
  expect(flg.environment).toStrictEqual(model);
51
43
  });
52
44
 
53
45
  test('test_set_agent_options', async () => {
54
- const agent = new https.Agent({})
46
+ const agent = new Agent({})
55
47
 
56
- // @ts-ignore
57
- fetch.mockImplementation((url: string, options: RequestInit) => {
58
- if (options.agent !== agent) {
48
+ fetch.mockImplementation((url, options) => {
49
+ //@ts-ignore I give up
50
+ if (options.dispatcher !== agent) {
59
51
  throw new Error("Agent has not been set on retry fetch")
60
52
  }
61
- return Promise.resolve(new Response(environmentJSON()))
53
+ return Promise.resolve(new Response(environmentJSON))
62
54
  });
63
55
 
64
56
  const flg = flagsmith({
@@ -67,13 +59,11 @@ test('test_set_agent_options', async () => {
67
59
 
68
60
  await flg.updateEnvironment();
69
61
  expect(flg.environment).toBeDefined();
70
-
71
62
  });
72
63
 
73
64
  test('test_get_identity_segments', async () => {
74
- // @ts-ignore
75
- fetch.mockReturnValue(Promise.resolve(new Response(environmentJSON())));
76
- const flg = new Flagsmith({
65
+ fetch.mockResolvedValue(new Response(environmentJSON));
66
+ const flg = flagsmith({
77
67
  environmentKey: 'ser.key',
78
68
  enableLocalEvaluation: true
79
69
  });
@@ -85,8 +75,7 @@ test('test_get_identity_segments', async () => {
85
75
 
86
76
 
87
77
  test('test_get_identity_segments_empty_without_local_eval', async () => {
88
- // @ts-ignore
89
- fetch.mockReturnValue(Promise.resolve(new Response(environmentJSON())));
78
+ fetch.mockResolvedValue(new Response(environmentJSON));
90
79
  const flg = new Flagsmith({
91
80
  environmentKey: 'ser.key',
92
81
  enableLocalEvaluation: false
@@ -96,14 +85,11 @@ test('test_get_identity_segments_empty_without_local_eval', async () => {
96
85
  });
97
86
 
98
87
  test('test_update_environment_uses_req_when_inited', async () => {
99
- // @ts-ignore
100
- fetch.mockReturnValue(Promise.resolve(new Response(environmentJSON())));
101
- const identifier = 'identifier';
88
+ fetch.mockResolvedValue(new Response(environmentJSON));
102
89
 
103
90
  const flg = flagsmith({
104
91
  environmentKey: 'ser.key',
105
92
  enableLocalEvaluation: true,
106
-
107
93
  });
108
94
 
109
95
  delay(400);
@@ -114,9 +100,7 @@ test('test_update_environment_uses_req_when_inited', async () => {
114
100
  });
115
101
 
116
102
  test('test_isFeatureEnabled_environment', async () => {
117
- // @ts-ignore
118
- fetch.mockReturnValue(Promise.resolve(new Response(flagsJSON())));
119
-
103
+ fetch.mockResolvedValue(new Response(environmentJSON));
120
104
  const defaultFlag = new DefaultFlag('some-default-value', true);
121
105
 
122
106
  const defaultFlagHandler = (featureName: string) => defaultFlag;
@@ -135,10 +119,9 @@ test('test_isFeatureEnabled_environment', async () => {
135
119
 
136
120
  test('test_fetch_recovers_after_single_API_error', async () => {
137
121
  fetch
138
- // @ts-ignore
139
- .mockRejectedValueOnce(new Error('Error during fetching the API response'))
140
- .mockReturnValue(Promise.resolve(new Response(flagsJSON())));
141
- const flg = new Flagsmith({
122
+ .mockRejectedValue('Error during fetching the API response')
123
+ .mockResolvedValue(new Response(flagsJSON));
124
+ const flg = flagsmith({
142
125
  environmentKey: 'key',
143
126
  });
144
127
 
@@ -151,7 +134,6 @@ test('test_fetch_recovers_after_single_API_error', async () => {
151
134
 
152
135
  test('test_default_flag_used_after_multiple_API_errors', async () => {
153
136
  fetch
154
- // @ts-ignore
155
137
  .mockRejectedValue(new Error('Error during fetching the API response'));
156
138
  const defaultFlag = new DefaultFlag('some-default-value', true);
157
139
 
@@ -170,8 +152,10 @@ test('test_default_flag_used_after_multiple_API_errors', async () => {
170
152
  });
171
153
 
172
154
  test('default flag handler used when timeout occurs', async () => {
173
- // @ts-ignore
174
- fetch.mockReturnValue(Promise.resolve(sleep(10000)));
155
+ fetch.mockImplementation(async (...args) => {
156
+ await sleep(10000)
157
+ return fetch(...args)
158
+ });
175
159
 
176
160
  const defaultFlag = new DefaultFlag('some-default-value', true);
177
161
 
@@ -180,7 +164,7 @@ test('default flag handler used when timeout occurs', async () => {
180
164
  const flg = new Flagsmith({
181
165
  environmentKey: 'key',
182
166
  defaultFlagHandler: defaultFlagHandler,
183
- requestTimeoutSeconds: 0.001,
167
+ requestTimeoutSeconds: 0.0001,
184
168
  });
185
169
 
186
170
  const flags = await flg.getEnvironmentFlags();
@@ -200,28 +184,22 @@ test('request timeout uses default if not provided', async () => {
200
184
  })
201
185
 
202
186
  test('test_throws_when_no_identityFlags_returned_due_to_error', async () => {
203
- // @ts-ignore
204
- fetch.mockReturnValue(Promise.resolve(new Response('bad data')));
205
-
187
+ fetch.mockResolvedValue(new Response('bad data'));
206
188
 
207
189
  const flg = new Flagsmith({
208
190
  environmentKey: 'key',
209
191
  });
210
192
 
211
- expect(async () => {
212
- await flg.getIdentityFlags('identifier');
213
- }).rejects.toThrow();
214
-
193
+ await expect(async () => await flg.getIdentityFlags('identifier'))
194
+ .rejects
195
+ .toThrow();
215
196
  });
216
197
 
217
198
  test('test onEnvironmentChange is called when provided', async () => {
218
- // @ts-ignore
219
- fetch.mockReturnValue(Promise.resolve(new Response(environmentJSON())));
220
-
221
199
  const callback = {
222
200
  callback: (e: Error | null, result: EnvironmentModel) => { }
223
201
  };
224
- const callbackSpy = jest.spyOn(callback, 'callback');
202
+ const callbackSpy = vi.spyOn(callback, 'callback');
225
203
 
226
204
  const flg = new Flagsmith({
227
205
  environmentKey: 'ser.key',
@@ -235,46 +213,40 @@ test('test onEnvironmentChange is called when provided', async () => {
235
213
  });
236
214
 
237
215
  test('test onEnvironmentChange is called after error', async () => {
238
- // @ts-ignore
239
- fetch.mockReturnValue(Promise.resolve(new Response('aaa')));
240
-
241
- const callback = {
242
- callback: (e: Error | null, result: EnvironmentModel) => { }
243
- };
244
- const callbackSpy = jest.spyOn(callback, 'callback');
216
+ const callback = vi.fn((e, result) => {})
245
217
 
246
218
  const flg = new Flagsmith({
247
219
  environmentKey: 'ser.key',
248
220
  enableLocalEvaluation: true,
249
- onEnvironmentChange: callback.callback,
221
+ onEnvironmentChange: callback,
250
222
  });
251
223
 
252
224
  await delay(200);
253
225
 
254
- expect(callbackSpy).toBeCalled();
226
+ expect(callback).toBeCalled();
255
227
  });
256
228
 
257
229
  test('getIdentityFlags throws error if identifier is empty string', async () => {
258
- const flagsmith = new Flagsmith({
230
+ const flg = flagsmith({
259
231
  environmentKey: 'key',
260
232
  });
261
233
 
262
- await expect(flagsmith.getIdentityFlags('')).rejects.toThrow('`identifier` argument is missing or invalid.');
234
+ await expect(flg.getIdentityFlags('')).rejects.toThrow('`identifier` argument is missing or invalid.');
263
235
  })
264
236
 
265
237
 
266
238
  test('getIdentitySegments throws error if identifier is empty string', () => {
267
- const flagsmith = new Flagsmith({
239
+ const flg = flagsmith({
268
240
  environmentKey: 'key',
269
241
  });
270
242
 
271
- expect(() => { flagsmith.getIdentitySegments(''); }).toThrow('`identifier` argument is missing or invalid.');
243
+ expect(() => { flg.getIdentitySegments(''); }).toThrow('`identifier` argument is missing or invalid.');
272
244
  })
273
245
 
274
246
 
275
247
  test('offline_mode', async () => {
276
248
  // Given
277
- const environment: EnvironmentModel = environmentModel(JSON.parse(environmentJSON('offline-environment.json')));
249
+ const environment: EnvironmentModel = environmentModel(JSON.parse(offlineEnvironmentJSON));
278
250
 
279
251
  class DummyOfflineHandler extends BaseOfflineHandler {
280
252
  getEnvironment(): EnvironmentModel {
@@ -283,18 +255,18 @@ test('offline_mode', async () => {
283
255
  }
284
256
 
285
257
  // When
286
- const flagsmith = new Flagsmith({ offlineMode: true, offlineHandler: new DummyOfflineHandler() });
258
+ const flg = flagsmith({ offlineMode: true, offlineHandler: new DummyOfflineHandler() });
287
259
 
288
260
  // Then
289
261
  // we can request the flags from the client successfully
290
- const environmentFlags: Flags = await flagsmith.getEnvironmentFlags();
262
+ const environmentFlags: Flags = await flg.getEnvironmentFlags();
291
263
  let flag = environmentFlags.getFlag('some_feature');
292
264
  expect(flag.isDefault).toBe(false);
293
265
  expect(flag.enabled).toBe(true);
294
266
  expect(flag.value).toBe('offline-value');
295
267
 
296
268
 
297
- const identityFlags: Flags = await flagsmith.getIdentityFlags("identity");
269
+ const identityFlags: Flags = await flg.getIdentityFlags("identity");
298
270
  flag = identityFlags.getFlag('some_feature');
299
271
  expect(flag.isDefault).toBe(false);
300
272
  expect(flag.enabled).toBe(true);
@@ -304,24 +276,24 @@ test('offline_mode', async () => {
304
276
 
305
277
  test('test_flagsmith_uses_offline_handler_if_set_and_no_api_response', async () => {
306
278
  // Given
307
- const environment: EnvironmentModel = environmentModel(JSON.parse(environmentJSON('offline-environment.json')));
279
+ const environment: EnvironmentModel = environmentModel(JSON.parse(offlineEnvironmentJSON));
308
280
  const api_url = 'http://some.flagsmith.com/api/v1/';
309
- const mock_offline_handler = new BaseOfflineHandler() as jest.Mocked<BaseOfflineHandler>;
281
+ const mock_offline_handler = new BaseOfflineHandler();
310
282
 
311
- jest.spyOn(mock_offline_handler, 'getEnvironment').mockReturnValue(environment);
283
+ vi.spyOn(mock_offline_handler, 'getEnvironment').mockReturnValue(environment);
312
284
 
313
- const flagsmith = new Flagsmith({
285
+ const flg = flagsmith({
314
286
  environmentKey: 'some-key',
315
287
  apiUrl: api_url,
316
288
  offlineHandler: mock_offline_handler,
317
289
  });
318
290
 
319
- jest.spyOn(flagsmith, 'getEnvironmentFlags');
320
- jest.spyOn(flagsmith, 'getIdentityFlags');
291
+ vi.spyOn(flg, 'getEnvironmentFlags');
292
+ vi.spyOn(flg, 'getIdentityFlags');
321
293
 
322
294
 
323
- flagsmith.environmentFlagsUrl = 'http://some.flagsmith.com/api/v1/environment-flags';
324
- flagsmith.identitiesUrl = 'http://some.flagsmith.com/api/v1/identities';
295
+ flg.environmentFlagsUrl = 'http://some.flagsmith.com/api/v1/environment-flags';
296
+ flg.identitiesUrl = 'http://some.flagsmith.com/api/v1/identities';
325
297
 
326
298
  // Mock a 500 Internal Server Error response
327
299
  const errorResponse = new Response(null, {
@@ -329,17 +301,16 @@ test('test_flagsmith_uses_offline_handler_if_set_and_no_api_response', async ()
329
301
  statusText: 'Internal Server Error',
330
302
  });
331
303
 
332
- // @ts-ignore
333
- fetch.mockReturnValue(Promise.resolve(errorResponse));
304
+ fetch.mockResolvedValue(errorResponse);
334
305
 
335
306
  // When
336
- const environmentFlags: Flags = await flagsmith.getEnvironmentFlags();
337
- const identityFlags: Flags = await flagsmith.getIdentityFlags('identity', {});
307
+ const environmentFlags: Flags = await flg.getEnvironmentFlags();
308
+ const identityFlags: Flags = await flg.getIdentityFlags('identity', {});
338
309
 
339
310
  // Then
340
311
  expect(mock_offline_handler.getEnvironment).toHaveBeenCalledTimes(1);
341
- expect(flagsmith.getEnvironmentFlags).toHaveBeenCalled();
342
- expect(flagsmith.getIdentityFlags).toHaveBeenCalled();
312
+ expect(flg.getEnvironmentFlags).toHaveBeenCalled();
313
+ expect(flg.getIdentityFlags).toHaveBeenCalled();
343
314
 
344
315
  expect(environmentFlags.isFeatureEnabled('some_feature')).toBe(true);
345
316
  expect(environmentFlags.getFeatureValue('some_feature')).toBe('offline-value');
@@ -357,15 +328,14 @@ test('cannot use offline mode without offline handler', () => {
357
328
 
358
329
  test('cannot use both default handler and offline handler', () => {
359
330
  // When and Then
360
- expect(() => new Flagsmith({
331
+ expect(() => flagsmith({
361
332
  offlineHandler: new BaseOfflineHandler(),
362
- defaultFlagHandler: (flagName) => new DefaultFlag('foo', true)
333
+ defaultFlagHandler: () => new DefaultFlag('foo', true)
363
334
  })).toThrowError('ValueError: Cannot use both defaultFlagHandler and offlineHandler.');
364
335
  });
365
336
 
366
337
  test('cannot create Flagsmith client in remote evaluation without API key', () => {
367
338
  // When and Then
368
- // @ts-ignore
369
339
  expect(() => new Flagsmith()).toThrowError('ValueError: environmentKey is required.');
370
340
  });
371
341
 
@@ -376,10 +346,8 @@ function sleep(ms: number) {
376
346
  });
377
347
  }
378
348
  test('test_localEvaluation_true__identity_overrides_evaluated', async () => {
379
- // @ts-ignore
380
- fetch.mockReturnValue(Promise.resolve(new Response(environmentJSON())));
381
-
382
- const flg = new Flagsmith({
349
+ fetch.mockResolvedValue(new Response(environmentJSON));
350
+ const flg = flagsmith({
383
351
  environmentKey: 'ser.key',
384
352
  enableLocalEvaluation: true,
385
353
  });
@@ -1,10 +1,10 @@
1
1
  import * as fs from 'fs';
2
- import { LocalFileHandler } from '../../sdk/offline_handlers';
3
- import { EnvironmentModel } from '../../flagsmith-engine';
2
+ import { LocalFileHandler } from '../../sdk/offline_handlers.js';
3
+ import { EnvironmentModel } from '../../flagsmith-engine/index.js';
4
4
 
5
5
  const offlineEnvironment = require('./data/offline-environment.json');
6
6
 
7
- jest.mock('fs')
7
+ vi.mock('fs')
8
8
 
9
9
  const offlineEnvironmentString = JSON.stringify(offlineEnvironment)
10
10
 
@@ -13,8 +13,7 @@ test('local file handler', () => {
13
13
 
14
14
  // Mock the fs.readFileSync function to return environmentJson
15
15
 
16
- // @ts-ignore
17
- const readFileSyncMock = jest.spyOn(fs, 'readFileSync');
16
+ const readFileSyncMock = vi.spyOn(fs, 'readFileSync');
18
17
  readFileSyncMock.mockImplementation(() => offlineEnvironmentString);
19
18
 
20
19
  // Given
@@ -1,12 +1,10 @@
1
- import Flagsmith from '../../sdk';
2
- import { EnvironmentDataPollingManager } from '../../sdk/polling_manager';
3
- import { delay } from '../../sdk/utils';
4
- jest.mock('../../sdk');
5
- jest.mock('node-fetch');
1
+ import Flagsmith from '../../sdk/index.js';
2
+ import { EnvironmentDataPollingManager } from '../../sdk/polling_manager.js';
3
+ import { delay } from '../../sdk/utils.js';
4
+ vi.mock('../../sdk');
6
5
 
7
6
  beforeEach(() => {
8
- // @ts-ignore
9
- Flagsmith.mockClear();
7
+ vi.clearAllMocks()
10
8
  });
11
9
 
12
10
  test('test_polling_manager_correctly_stops_if_never_started', async () => {
@@ -55,5 +53,5 @@ test('test_polling_manager_calls_update_environment_on_each_refresh', async () =
55
53
  pollingManager.start();
56
54
  await delay(450);
57
55
  pollingManager.stop();
58
- expect(flagsmith.updateEnvironment).toHaveBeenCalledTimes(5);
56
+ expect(flagsmith.updateEnvironment).toHaveBeenCalledTimes(4);
59
57
  });
@@ -1,9 +1,9 @@
1
1
  import { readFileSync } from 'fs';
2
- import { buildEnvironmentModel } from '../../flagsmith-engine/environments/util';
3
- import { AnalyticsProcessor } from '../../sdk/analytics';
4
- import Flagsmith from '../../sdk';
5
- import { FlagsmithCache } from '../../sdk/types';
6
- import { Flag, Flags } from '../../sdk/models';
2
+ import { buildEnvironmentModel } from '../../flagsmith-engine/environments/util.js';
3
+ import { AnalyticsProcessor } from '../../sdk/analytics.js';
4
+ import Flagsmith from '../../sdk/index.js';
5
+ import { FlagsmithCache } from '../../sdk/types.js';
6
+ import { Flags } from '../../sdk/models.js';
7
7
 
8
8
  const DATA_DIR = __dirname + '/data/';
9
9
 
@@ -24,10 +24,13 @@ export class TestCache implements FlagsmithCache {
24
24
  }
25
25
  }
26
26
 
27
+ export const fetch = vi.fn(global.fetch)
28
+
27
29
  export function analyticsProcessor() {
28
30
  return new AnalyticsProcessor({
29
31
  environmentKey: 'test-key',
30
- baseApiUrl: 'http://testUrl'
32
+ baseApiUrl: 'http://testUrl',
33
+ fetch,
31
34
  });
32
35
  }
33
36
 
@@ -38,22 +41,23 @@ export function apiKey(): string {
38
41
  export function flagsmith(params = {}) {
39
42
  return new Flagsmith({
40
43
  environmentKey: apiKey(),
44
+ fetch,
41
45
  ...params,
42
46
  });
43
47
  }
44
48
 
45
- export function environmentJSON(environmentFilename: string = 'environment.json') {
46
- return readFileSync(DATA_DIR + environmentFilename, 'utf-8');
47
- }
49
+ export const environmentJSON = readFileSync(DATA_DIR + 'environment.json', 'utf-8');
50
+
51
+ export const offlineEnvironmentJSON = readFileSync(DATA_DIR + 'offline-environment.json', 'utf-8')
48
52
 
49
53
  export function environmentModel(environmentJSON: any) {
50
54
  return buildEnvironmentModel(environmentJSON);
51
55
  }
52
56
 
53
- export function flagsJSON() {
54
- return readFileSync(DATA_DIR + 'flags.json', 'utf-8');
55
- }
57
+ export const flagsJSON = readFileSync(DATA_DIR + 'flags.json', 'utf-8')
56
58
 
57
- export function identitiesJSON() {
58
- return readFileSync(DATA_DIR + 'identities.json', 'utf-8');
59
- }
59
+ export const identitiesJSON = readFileSync(DATA_DIR + 'identities.json', 'utf-8')
60
+
61
+ export const transientIdentityJSON = readFileSync(DATA_DIR + 'transient-identity.json', 'utf-8')
62
+
63
+ export const identityWithTransientTraitsJSON = readFileSync(DATA_DIR + 'identity-with-transient-traits.json', 'utf-8')