flagsmith-nodejs 3.3.3 → 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 -37
  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 -107
  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
@@ -0,0 +1,56 @@
1
+ export function isTraitConfig(traitValue) {
2
+ return !!traitValue && typeof traitValue == 'object' && traitValue.value !== undefined;
3
+ }
4
+ export function generateIdentitiesData(identifier, traits, transient) {
5
+ const traitsGenerated = Object.entries(traits).map(([key, value]) => {
6
+ if (isTraitConfig(value)) {
7
+ return {
8
+ trait_key: key,
9
+ trait_value: value?.value,
10
+ transient: value?.transient,
11
+ };
12
+ }
13
+ else {
14
+ return {
15
+ trait_key: key,
16
+ trait_value: value,
17
+ };
18
+ }
19
+ });
20
+ if (transient) {
21
+ return {
22
+ identifier: identifier,
23
+ traits: traitsGenerated,
24
+ transient: true
25
+ };
26
+ }
27
+ return {
28
+ identifier: identifier,
29
+ traits: traitsGenerated
30
+ };
31
+ }
32
+ export const delay = (ms) => new Promise(resolve => setTimeout(() => resolve(undefined), ms));
33
+ export const retryFetch = (url,
34
+ // built-in RequestInit type doesn't have dispatcher/agent
35
+ fetchOptions, retries = 3, timeoutMs = 10, // set an overall timeout for this function
36
+ customFetch) => {
37
+ return new Promise((resolve, reject) => {
38
+ const retryWrapper = (n) => {
39
+ customFetch(url, {
40
+ ...fetchOptions,
41
+ signal: AbortSignal.timeout(timeoutMs)
42
+ })
43
+ .then(res => resolve(res))
44
+ .catch(async (err) => {
45
+ if (n > 0) {
46
+ await delay(1000);
47
+ retryWrapper(--n);
48
+ }
49
+ else {
50
+ reject(err);
51
+ }
52
+ });
53
+ };
54
+ retryWrapper(retries);
55
+ });
56
+ };
@@ -1,6 +1,6 @@
1
- import { FeatureStateModel } from '../features/models';
2
- import { IdentityModel } from '../identities/models';
3
- import { ProjectModel } from '../projects/models';
1
+ import { FeatureStateModel } from '../features/models.js';
2
+ import { IdentityModel } from '../identities/models.js';
3
+ import { ProjectModel } from '../projects/models.js';
4
4
 
5
5
  export class EnvironmentAPIKeyModel {
6
6
  id: number;
@@ -1,7 +1,7 @@
1
- import { buildFeatureStateModel } from '../features/util';
2
- import { buildIdentityModel } from '../identities/util';
3
- import { buildProjectModel } from '../projects/util';
4
- import { EnvironmentAPIKeyModel, EnvironmentModel } from './models';
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
5
 
6
6
  export function buildEnvironmentModel(environmentJSON: any) {
7
7
  const project = buildProjectModel(environmentJSON.project);
@@ -1,5 +1,5 @@
1
1
  import { v4 as uuidv4 } from 'uuid';
2
- import { getHashedPercentateForObjIds } from '../utils/hashing';
2
+ import { getHashedPercentateForObjIds } from '../utils/hashing/index.js';
3
3
 
4
4
  export class FeatureModel {
5
5
  id: number;
@@ -4,7 +4,7 @@ import {
4
4
  FeatureStateModel,
5
5
  MultivariateFeatureOptionModel,
6
6
  MultivariateFeatureStateValueModel
7
- } from './models';
7
+ } from './models.js';
8
8
 
9
9
  export function buildFeatureModel(featuresModelJSON: any): FeatureModel {
10
10
  return new FeatureModel(featuresModelJSON.id, featuresModelJSON.name, featuresModelJSON.type);
@@ -1,6 +1,5 @@
1
- import { FeatureStateModel } from '../features/models';
2
- import { IdentityFeaturesList } from '../utils/collections';
3
- import { TraitModel } from './traits/models';
1
+ import { IdentityFeaturesList } from '../utils/collections.js';
2
+ import { TraitModel } from './traits/models.js';
4
3
 
5
4
  const { v4: uuidv4 } = require('uuid');
6
5
 
@@ -20,7 +19,7 @@ export class IdentityModel {
20
19
  environmentApiKey: string,
21
20
  identifier: string,
22
21
  identityUuid?: string,
23
- djangoID?: number
22
+ djangoID?: number,
24
23
  ) {
25
24
  this.identityUuid = identityUuid || uuidv4();
26
25
  this.createdDate = Date.parse(created_date) || Date.now();
@@ -1,7 +1,6 @@
1
1
  export class TraitModel {
2
2
  traitKey: string;
3
3
  traitValue: any;
4
-
5
4
  constructor(key: string, value: any) {
6
5
  this.traitKey = key;
7
6
  this.traitValue = value;
@@ -1,7 +1,7 @@
1
- import { buildFeatureStateModel } from '../features/util';
2
- import { IdentityFeaturesList } from '../utils/collections';
3
- import { IdentityModel } from './models';
4
- import { TraitModel } from './traits/models';
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
5
 
6
6
  export function buildTraitModel(traitJSON: any): TraitModel {
7
7
  return new TraitModel(traitJSON.trait_key, traitJSON.trait_value);
@@ -1,17 +1,17 @@
1
- import { EnvironmentModel } from './environments/models';
2
- import { FeatureStateModel } from './features/models';
3
- import { IdentityModel } from './identities/models';
4
- import { TraitModel } from './identities/traits/models';
5
- import { getIdentitySegments } from './segments/evaluators';
6
- import { SegmentModel } from './segments/models';
7
- import { FeatureStateNotFound } from './utils/errors';
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
+ import { getIdentitySegments } from './segments/evaluators.js';
6
+ import { SegmentModel } from './segments/models.js';
7
+ import { FeatureStateNotFound } from './utils/errors.js';
8
8
 
9
- export { EnvironmentModel } from './environments/models';
10
- export { FeatureStateModel } from './features/models';
11
- export { IdentityModel } from './identities/models';
12
- export { TraitModel } from './identities/traits/models';
13
- export { SegmentModel } from './segments/models';
14
- export { OrganisationModel } from './organisations/models';
9
+ export { EnvironmentModel } from './environments/models.js';
10
+ export { FeatureStateModel } from './features/models.js';
11
+ export { IdentityModel } from './identities/models.js';
12
+ export { TraitModel } from './identities/traits/models.js';
13
+ export { SegmentModel } from './segments/models.js';
14
+ export { OrganisationModel } from './organisations/models.js';
15
15
 
16
16
  function getIdentityFeatureStatesDict(
17
17
  environment: EnvironmentModel,
@@ -1,4 +1,4 @@
1
- import { OrganisationModel } from './models';
1
+ import { OrganisationModel } from './models.js';
2
2
 
3
3
  export function buildOrganizationModel(organizationJSON: any): OrganisationModel {
4
4
  return new OrganisationModel(
@@ -1,5 +1,5 @@
1
- import { OrganisationModel } from '../organisations/models';
2
- import { SegmentModel } from '../segments/models';
1
+ import { OrganisationModel } from '../organisations/models.js';
2
+ import { SegmentModel } from '../segments/models.js';
3
3
 
4
4
  export class ProjectModel {
5
5
  id: number;
@@ -1,7 +1,7 @@
1
- import { buildOrganizationModel } from '../organisations/util';
2
- import { SegmentModel } from '../segments/models';
3
- import { buildSegmentModel } from '../segments/util';
4
- import { ProjectModel } from './models';
1
+ import { buildOrganizationModel } from '../organisations/util.js';
2
+ import { SegmentModel } from '../segments/models.js';
3
+ import { buildSegmentModel } from '../segments/util.js';
4
+ import { ProjectModel } from './models.js';
5
5
 
6
6
  export function buildProjectModel(projectJSON: any): ProjectModel {
7
7
  const segments: SegmentModel[] = projectJSON['segments']
@@ -1,9 +1,9 @@
1
- import { EnvironmentModel } from '../environments/models';
2
- import { IdentityModel } from '../identities/models';
3
- import { TraitModel } from '../identities/traits/models';
4
- import { getHashedPercentateForObjIds } from '../utils/hashing';
5
- import { PERCENTAGE_SPLIT, IS_SET, IS_NOT_SET } from './constants';
6
- import { SegmentConditionModel, SegmentModel, SegmentRuleModel } from './models';
1
+ import { EnvironmentModel } from '../environments/models.js';
2
+ import { IdentityModel } from '../identities/models.js';
3
+ import { TraitModel } from '../identities/traits/models.js';
4
+ import { getHashedPercentateForObjIds } from '../utils/hashing/index.js';
5
+ import { PERCENTAGE_SPLIT, IS_SET, IS_NOT_SET } from './constants.js';
6
+ import { SegmentConditionModel, SegmentModel, SegmentRuleModel } from './models.js';
7
7
 
8
8
  export function getIdentitySegments(
9
9
  environment: EnvironmentModel,
@@ -1,7 +1,7 @@
1
1
  import semver from 'semver';
2
2
 
3
- import { FeatureStateModel } from '../features/models';
4
- import { getCastingFunction as getCastingFunction } from '../utils';
3
+ import { FeatureStateModel } from '../features/models.js';
4
+ import { getCastingFunction as getCastingFunction } from '../utils/index.js';
5
5
  import {
6
6
  ALL_RULE,
7
7
  ANY_RULE,
@@ -11,8 +11,8 @@ import {
11
11
  MODULO,
12
12
  IN,
13
13
  CONDITION_OPERATORS
14
- } from './constants';
15
- import { isSemver } from './util';
14
+ } from './constants.js';
15
+ import { isSemver } from './util.js';
16
16
 
17
17
  export const all = (iterable: Array<any>) => iterable.filter(e => !!e).length === iterable.length;
18
18
  export const any = (iterable: Array<any>) => iterable.filter(e => !!e).length > 0;
@@ -1,5 +1,5 @@
1
- import { buildFeatureStateModel } from '../features/util';
2
- import { SegmentConditionModel, SegmentModel, SegmentRuleModel } from './models';
1
+ import { buildFeatureStateModel } from '../features/util.js';
2
+ import { SegmentConditionModel, SegmentModel, SegmentRuleModel } from './models.js';
3
3
 
4
4
  export function buildSegmentConditionModel(segmentConditionJSON: any): SegmentConditionModel {
5
5
  return new SegmentConditionModel(
@@ -34,4 +34,4 @@ export function isSemver(value: any) {
34
34
 
35
35
  export function removeSemverSuffix(value: string) {
36
36
  return value.replace(':semver', '');
37
- }
37
+ }
@@ -1,3 +1,3 @@
1
- import { FeatureStateModel } from '../features/models';
1
+ import { FeatureStateModel } from '../features/models.js';
2
2
 
3
3
  export class IdentityFeaturesList extends Array<FeatureStateModel> {}
@@ -1,4 +1,4 @@
1
- import { removeSemverSuffix } from "../segments/util";
1
+ import { removeSemverSuffix } from "../segments/util.js";
2
2
 
3
3
  export function getCastingFunction(traitType: 'boolean' | 'string' | 'number' | 'semver' | any): CallableFunction {
4
4
  switch (traitType) {
package/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import Flagsmith from "./sdk";
1
+ import Flagsmith from "./sdk/index.js";
2
2
 
3
3
  export {
4
4
  AnalyticsProcessor,
@@ -9,11 +9,11 @@ export {
9
9
  DefaultFlag,
10
10
  Flags,
11
11
  default
12
- } from './sdk';
12
+ } from './sdk/index.js';
13
13
 
14
14
  export {
15
15
  FlagsmithConfig
16
- } from './sdk/types'
16
+ } from './sdk/types.js'
17
17
 
18
18
  export {
19
19
  EnvironmentModel,
@@ -22,6 +22,6 @@ export {
22
22
  TraitModel,
23
23
  SegmentModel,
24
24
  OrganisationModel
25
- } from './flagsmith-engine';
25
+ } from './flagsmith-engine/index.js';
26
26
 
27
27
  module.exports = Flagsmith;
package/package.json CHANGED
@@ -1,8 +1,16 @@
1
1
  {
2
2
  "name": "flagsmith-nodejs",
3
- "version": "3.3.3",
3
+ "version": "4.0.0-beta.1",
4
4
  "description": "Flagsmith lets you manage features flags and remote config across web, mobile and server side applications. Deliver true Continuous Integration. Get builds out faster. Control who has access to new features.",
5
- "main": "build/index.js",
5
+ "main": "./build/cjs/index.js",
6
+ "type": "module",
7
+ "engines": {
8
+ "node": ">=18"
9
+ },
10
+ "exports": {
11
+ "import": "./build/esm/index.js",
12
+ "require": "./build/cjs/index.js"
13
+ },
6
14
  "repository": {
7
15
  "type": "git",
8
16
  "url": "https://github.com/Flagsmith/flagsmith-nodejs-client"
@@ -42,10 +50,11 @@
42
50
  "license": "MIT",
43
51
  "scripts": {
44
52
  "lint": "prettier --write .",
45
- "test": "jest --coverage --coverageReporters='text'",
46
- "test:watch": "jest --coverage --watch --coverageReporters='text'",
47
- "test:debug": "node --inspect-brk node_modules/.bin/jest --coverage --watch --coverageReporters='text'",
48
- "build": "tsc",
53
+ "test": "vitest --coverage --run",
54
+ "test:watch": "vitest",
55
+ "test:debug": "vitest --inspect-brk --no-file-parallelism --coverage",
56
+ "prebuild": "rm -rf ./build",
57
+ "build": "tsc -b tsconfig.cjs.json tsconfig.esm.json && echo '{\"type\": \"commonjs\"}'> build/cjs/package.json",
49
58
  "deploy": "npm i && npm run build && npm publish",
50
59
  "deploy:beta": "npm i && npm run build && npm publish --tag beta",
51
60
  "prepare": "husky install"
@@ -53,22 +62,25 @@
53
62
  "dependencies": {
54
63
  "big-integer": "^1.6.51",
55
64
  "md5": "^2.3.0",
56
- "node-fetch": "^2.1.2",
57
65
  "pino": "^8.8.0",
58
66
  "semver": "^7.3.7",
67
+ "undici-types": "^6.19.8",
59
68
  "uuid": "^8.3.2"
60
69
  },
61
70
  "devDependencies": {
62
71
  "@types/jest": "^27.4.1",
63
72
  "@types/md5": "^2.3.2",
64
- "@types/node-fetch": "^2.6.1",
73
+ "@types/node": "^20.16.10",
65
74
  "@types/semver": "^7.3.9",
66
75
  "@types/uuid": "^8.3.4",
76
+ "@vitest/coverage-v8": "^2.1.2",
67
77
  "esbuild": "^0.14.25",
68
78
  "husky": "^7.0.4",
69
79
  "jest": "^27.5.1",
70
80
  "prettier": "^2.2.1",
71
81
  "ts-jest": "^27.1.3",
72
- "typescript": "^4.6.2"
82
+ "typescript": "^4.9.5",
83
+ "undici": "^6.19.8",
84
+ "vitest": "^2.1.2"
73
85
  }
74
86
  }
package/sdk/analytics.ts CHANGED
@@ -1,5 +1,5 @@
1
- import fetch from 'node-fetch';
2
- import pino, { Logger } from 'pino';
1
+ import { pino, Logger } from 'pino';
2
+ import { Fetch } from "./types.js";
3
3
 
4
4
  const ANALYTICS_ENDPOINT = 'analytics/flags/';
5
5
 
@@ -14,6 +14,7 @@ export class AnalyticsProcessor {
14
14
  private requestTimeoutMs: number = 3000;
15
15
  private logger: Logger;
16
16
  private currentFlush: ReturnType<typeof fetch> | undefined;
17
+ private customFetch: Fetch;
17
18
 
18
19
  /**
19
20
  * AnalyticsProcessor is used to track how often individual Flags are evaluated within
@@ -24,13 +25,14 @@ export class AnalyticsProcessor {
24
25
  * @param data.requestTimeoutMs used to tell requests to stop waiting for a response after a
25
26
  given number of milliseconds
26
27
  */
27
- constructor(data: { environmentKey: string; baseApiUrl: string; requestTimeoutMs?: number, logger?: Logger }) {
28
+ constructor(data: { environmentKey: string; baseApiUrl: string; requestTimeoutMs?: number, logger?: Logger, fetch?: Fetch }) {
28
29
  this.analyticsEndpoint = data.baseApiUrl + ANALYTICS_ENDPOINT;
29
30
  this.environmentKey = data.environmentKey;
30
31
  this.lastFlushed = Date.now();
31
32
  this.analyticsData = {};
32
33
  this.requestTimeoutMs = data.requestTimeoutMs || this.requestTimeoutMs;
33
34
  this.logger = data.logger || pino();
35
+ this.customFetch = data.fetch ?? fetch;
34
36
  }
35
37
  /**
36
38
  * Sends all the collected data to the api asynchronously and resets the timer
@@ -41,10 +43,10 @@ export class AnalyticsProcessor {
41
43
  }
42
44
 
43
45
  try {
44
- this.currentFlush = fetch(this.analyticsEndpoint, {
46
+ this.currentFlush = this.customFetch(this.analyticsEndpoint, {
45
47
  method: 'POST',
46
48
  body: JSON.stringify(this.analyticsData),
47
- timeout: this.requestTimeoutMs,
49
+ signal: AbortSignal.timeout(this.requestTimeoutMs),
48
50
  headers: {
49
51
  'Content-Type': 'application/json',
50
52
  'X-Environment-Key': this.environmentKey
package/sdk/index.ts CHANGED
@@ -1,28 +1,28 @@
1
- import { RequestInit } from 'node-fetch';
2
- import { getEnvironmentFeatureStates, 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 { TraitModel } from '../flagsmith-engine/identities/traits/models';
7
-
8
- import { AnalyticsProcessor } from './analytics';
9
- import { BaseOfflineHandler } from './offline_handlers';
10
- import { FlagsmithAPIError, FlagsmithClientError } from './errors';
11
-
12
- import { DefaultFlag, Flags } from './models';
13
- import { EnvironmentDataPollingManager } from './polling_manager';
14
- import { generateIdentitiesData, retryFetch } from './utils';
15
- import { SegmentModel } from '../flagsmith-engine/segments/models';
16
- import { getIdentitySegments } from '../flagsmith-engine/segments/evaluators';
17
- import { FlagsmithCache, FlagsmithConfig } from './types';
18
- import pino, { Logger } from 'pino';
19
-
20
- export { AnalyticsProcessor } from './analytics';
21
- export { FlagsmithAPIError, FlagsmithClientError } from './errors';
22
-
23
- export { DefaultFlag, Flags } from './models';
24
- export { EnvironmentDataPollingManager } from './polling_manager';
25
- export { FlagsmithCache, FlagsmithConfig } from './types';
1
+ import { Dispatcher } from 'undici-types';
2
+ import { getEnvironmentFeatureStates, getIdentityFeatureStates } from '../flagsmith-engine/index.js';
3
+ import { EnvironmentModel } from '../flagsmith-engine/index.js';
4
+ import { buildEnvironmentModel } from '../flagsmith-engine/environments/util.js';
5
+ import { IdentityModel } from '../flagsmith-engine/index.js';
6
+ import { TraitModel } from '../flagsmith-engine/index.js';
7
+
8
+ import { AnalyticsProcessor } from './analytics.js';
9
+ import { BaseOfflineHandler } from './offline_handlers.js';
10
+ import { FlagsmithAPIError, FlagsmithClientError } from './errors.js';
11
+
12
+ import { DefaultFlag, Flags } from './models.js';
13
+ import { EnvironmentDataPollingManager } from './polling_manager.js';
14
+ import { generateIdentitiesData, retryFetch } from './utils.js';
15
+ import { SegmentModel } from '../flagsmith-engine/index.js';
16
+ import { getIdentitySegments } from '../flagsmith-engine/segments/evaluators.js';
17
+ import { Fetch, FlagsmithCache, FlagsmithConfig, FlagsmithTraitValue, ITraitConfig } from './types.js';
18
+ import { pino, Logger } from 'pino';
19
+
20
+ export { AnalyticsProcessor } from './analytics.js';
21
+ export { FlagsmithAPIError, FlagsmithClientError } from './errors.js';
22
+
23
+ export { DefaultFlag, Flags } from './models.js';
24
+ export { EnvironmentDataPollingManager } from './polling_manager.js';
25
+ export { FlagsmithCache, FlagsmithConfig } from './types.js';
26
26
 
27
27
  const DEFAULT_API_URL = 'https://edge.api.flagsmith.com/api/v1/';
28
28
  const DEFAULT_REQUEST_TIMEOUT_SECONDS = 10;
@@ -31,7 +31,7 @@ export class Flagsmith {
31
31
  environmentKey?: string = undefined;
32
32
  apiUrl?: string = undefined;
33
33
  customHeaders?: { [key: string]: any };
34
- agent: RequestInit['agent'];
34
+ agent?: Dispatcher;
35
35
  requestTimeoutMs?: number;
36
36
  enableLocalEvaluation?: boolean = false;
37
37
  environmentRefreshIntervalSeconds: number = 60;
@@ -39,7 +39,6 @@ export class Flagsmith {
39
39
  enableAnalytics: boolean = false;
40
40
  defaultFlagHandler?: (featureName: string) => DefaultFlag;
41
41
 
42
-
43
42
  environmentFlagsUrl?: string;
44
43
  identitiesUrl?: string;
45
44
  environmentUrl?: string;
@@ -55,6 +54,7 @@ export class Flagsmith {
55
54
  private onEnvironmentChange?: (error: Error | null, result: EnvironmentModel) => void;
56
55
  private analyticsProcessor?: AnalyticsProcessor;
57
56
  private logger: Logger;
57
+ private customFetch: Fetch;
58
58
  /**
59
59
  * A Flagsmith client.
60
60
  *
@@ -98,6 +98,7 @@ export class Flagsmith {
98
98
  // }
99
99
 
100
100
  this.agent = data.agent;
101
+ this.customFetch = data.fetch ?? fetch;
101
102
  this.environmentKey = data.environmentKey;
102
103
  this.apiUrl = data.apiUrl || this.apiUrl;
103
104
  this.customHeaders = data.customHeaders;
@@ -168,11 +169,11 @@ export class Flagsmith {
168
169
 
169
170
  this.analyticsProcessor = data.enableAnalytics
170
171
  ? new AnalyticsProcessor({
171
- environmentKey: this.environmentKey,
172
- baseApiUrl: this.apiUrl,
173
- requestTimeoutMs: this.requestTimeoutMs,
174
- logger: this.logger
175
- })
172
+ environmentKey: this.environmentKey,
173
+ baseApiUrl: this.apiUrl,
174
+ requestTimeoutMs: this.requestTimeoutMs,
175
+ logger: this.logger
176
+ })
176
177
  : undefined;
177
178
  }
178
179
  }
@@ -207,11 +208,15 @@ export class Flagsmith {
207
208
  *
208
209
  * @param {string} identifier a unique identifier for the identity in the current
209
210
  environment, e.g. email address, username, uuid
210
- * @param {{[key:string]:any}} traits? a dictionary of traits to add / update on the identity in
211
- Flagsmith, e.g. {"num_orders": 10}
211
+ * @param {{[key:string]:any | ITraitConfig}} traits? a dictionary of traits to add / update on the identity in
212
+ Flagsmith, e.g. {"num_orders": 10} or {age: {value: 30, transient: true}}
212
213
  * @returns Flags object holding all the flags for the given identity.
213
214
  */
214
- async getIdentityFlags(identifier: string, traits?: { [key: string]: any }): Promise<Flags> {
215
+ async getIdentityFlags(
216
+ identifier: string,
217
+ traits?: { [key: string]: FlagsmithTraitValue | ITraitConfig },
218
+ transient: boolean = false
219
+ ): Promise<Flags> {
215
220
  if (!identifier) {
216
221
  throw new Error('`identifier` argument is missing or invalid.');
217
222
  }
@@ -232,7 +237,7 @@ export class Flagsmith {
232
237
  return this.getIdentityFlagsFromDocument(identifier, traits || {});
233
238
  }
234
239
 
235
- return this.getIdentityFlagsFromApi(identifier, traits);
240
+ return this.getIdentityFlagsFromApi(identifier, traits, transient);
236
241
  }
237
242
 
238
243
  /**
@@ -293,8 +298,11 @@ export class Flagsmith {
293
298
  }
294
299
  if (this.environment.identityOverrides?.length) {
295
300
  this.identitiesWithOverridesByIdentifier = new Map<string, IdentityModel>(
296
- this.environment.identityOverrides.map(identity => [identity.identifier, identity]
297
- ));
301
+ this.environment.identityOverrides.map(identity => [
302
+ identity.identifier,
303
+ identity
304
+ ])
305
+ );
298
306
  }
299
307
  if (this.onEnvironmentChange) {
300
308
  this.onEnvironmentChange(null, this.environment);
@@ -329,13 +337,14 @@ export class Flagsmith {
329
337
  const data = await retryFetch(
330
338
  url,
331
339
  {
332
- agent: this.agent,
340
+ dispatcher: this.agent,
333
341
  method: method,
334
342
  body: JSON.stringify(body),
335
343
  headers: headers
336
344
  },
337
345
  this.retries,
338
- this.requestTimeoutMs || undefined
346
+ this.requestTimeoutMs,
347
+ this.customFetch,
339
348
  );
340
349
 
341
350
  if (data.status !== 200) {
@@ -367,7 +376,6 @@ export class Flagsmith {
367
376
  defaultFlagHandler: this.defaultFlagHandler
368
377
  });
369
378
  if (!!this.cache) {
370
- // @ts-ignore node-cache types are incorrect, ttl should be optional
371
379
  await this.cache.set('flags', flags);
372
380
  }
373
381
  return flags;
@@ -395,7 +403,6 @@ export class Flagsmith {
395
403
  });
396
404
 
397
405
  if (!!this.cache) {
398
- // @ts-ignore node-cache types are incorrect, ttl should be optional
399
406
  await this.cache.set(`flags-${identifier}`, flags);
400
407
  }
401
408
 
@@ -414,7 +421,6 @@ export class Flagsmith {
414
421
  defaultFlagHandler: this.defaultFlagHandler
415
422
  });
416
423
  if (!!this.cache) {
417
- // @ts-ignore node-cache types are incorrect, ttl should be optional
418
424
  await this.cache.set('flags', flags);
419
425
  }
420
426
  return flags;
@@ -433,12 +439,16 @@ export class Flagsmith {
433
439
  }
434
440
  }
435
441
 
436
- private async getIdentityFlagsFromApi(identifier: string, traits: { [key: string]: any }) {
442
+ private async getIdentityFlagsFromApi(
443
+ identifier: string,
444
+ traits: { [key: string]: FlagsmithTraitValue | ITraitConfig },
445
+ transient: boolean = false
446
+ ) {
437
447
  if (!this.identitiesUrl) {
438
448
  throw new Error('`apiUrl` argument is missing or invalid.');
439
449
  }
440
450
  try {
441
- const data = generateIdentitiesData(identifier, traits);
451
+ const data = generateIdentitiesData(identifier, traits, transient);
442
452
  const jsonResponse = await this.getJSONResponse(this.identitiesUrl, 'POST', data);
443
453
  const flags = Flags.fromAPIFlags({
444
454
  apiFlags: jsonResponse['flags'],
@@ -446,7 +456,6 @@ export class Flagsmith {
446
456
  defaultFlagHandler: this.defaultFlagHandler
447
457
  });
448
458
  if (!!this.cache) {
449
- // @ts-ignore node-cache types are incorrect, ttl should be optional
450
459
  await this.cache.set(`flags-${identifier}`, flags);
451
460
  }
452
461
  return flags;
package/sdk/models.ts CHANGED
@@ -1,6 +1,5 @@
1
- import { FeatureStateModel } from '../flagsmith-engine/features/models';
2
- import { AnalyticsProcessor } from './analytics';
3
- import { FlagsmithClientError } from './errors';
1
+ import { FeatureStateModel } from '../flagsmith-engine/features/models.js';
2
+ import { AnalyticsProcessor } from './analytics.js';
4
3
 
5
4
  export class BaseFlag {
6
5
  enabled: boolean;
@@ -1,6 +1,6 @@
1
1
  import * as fs from 'fs';
2
- import { buildEnvironmentModel } from '../flagsmith-engine/environments/util';
3
- import { EnvironmentModel } from '../flagsmith-engine/environments/models';
2
+ import { buildEnvironmentModel } from '../flagsmith-engine/environments/util.js';
3
+ import { EnvironmentModel } from '../flagsmith-engine/environments/models.js';
4
4
 
5
5
  export class BaseOfflineHandler {
6
6
  getEnvironment() : EnvironmentModel {