flagsmith-nodejs 3.3.3 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/.github/workflows/publish.yml +2 -2
  2. package/.github/workflows/pull_request.yaml +7 -14
  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 +111 -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/cjs/flagsmith-engine/utils/hashing/index.js +29 -0
  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/cjs/index.js +18 -0
  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 +23 -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 +25 -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 +2 -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 +2 -2
  115. package/flagsmith-engine/features/util.ts +1 -1
  116. package/flagsmith-engine/identities/models.ts +4 -5
  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 +5 -5
  125. package/flagsmith-engine/segments/util.ts +3 -3
  126. package/flagsmith-engine/utils/collections.ts +1 -1
  127. package/flagsmith-engine/utils/hashing/index.ts +5 -29
  128. package/flagsmith-engine/utils/index.ts +1 -1
  129. package/index.ts +4 -8
  130. package/package.json +21 -16
  131. package/sdk/analytics.ts +7 -5
  132. package/sdk/index.ts +55 -46
  133. package/sdk/models.ts +2 -3
  134. package/sdk/offline_handlers.ts +2 -2
  135. package/sdk/polling_manager.ts +2 -3
  136. package/sdk/types.ts +35 -24
  137. package/sdk/utils.ts +49 -37
  138. package/tests/engine/e2e/engine.test.ts +8 -11
  139. package/tests/engine/unit/engine.test.ts +5 -5
  140. package/tests/engine/unit/segments/segment_evaluators.test.ts +9 -9
  141. package/tests/engine/unit/utils/utils.test.ts +2 -2
  142. package/tests/sdk/analytics.test.ts +8 -13
  143. package/tests/sdk/data/identity-with-transient-traits.json +41 -0
  144. package/tests/sdk/data/transient-identity.json +29 -0
  145. package/tests/sdk/flagsmith-cache.test.ts +16 -32
  146. package/tests/sdk/flagsmith-environment-flags.test.ts +21 -36
  147. package/tests/sdk/flagsmith-identity-flags.test.ts +83 -32
  148. package/tests/sdk/flagsmith.test.ts +67 -99
  149. package/tests/sdk/offline-handlers.test.ts +5 -6
  150. package/tests/sdk/polling.test.ts +6 -8
  151. package/tests/sdk/utils.ts +19 -15
  152. package/tsconfig.cjs.json +7 -0
  153. package/tsconfig.esm.json +7 -0
  154. package/tsconfig.json +8 -4
  155. package/vitest.config.ts +17 -0
  156. package/build/flagsmith-engine/environments/util.js +0 -27
  157. package/build/flagsmith-engine/features/models.js +0 -132
  158. package/build/flagsmith-engine/features/util.js +0 -27
  159. package/build/flagsmith-engine/identities/models.js +0 -113
  160. package/build/flagsmith-engine/identities/util.js +0 -46
  161. package/build/flagsmith-engine/index.d.ts +0 -14
  162. package/build/flagsmith-engine/index.js +0 -127
  163. package/build/flagsmith-engine/organisations/models.js +0 -21
  164. package/build/flagsmith-engine/organisations/util.js +0 -8
  165. package/build/flagsmith-engine/projects/util.js +0 -15
  166. package/build/flagsmith-engine/segments/evaluators.js +0 -45
  167. package/build/flagsmith-engine/segments/models.js +0 -147
  168. package/build/flagsmith-engine/utils/collections.js +0 -26
  169. package/build/flagsmith-engine/utils/errors.js +0 -26
  170. package/build/flagsmith-engine/utils/hashing/index.js +0 -60
  171. package/build/index.js +0 -23
  172. package/build/sdk/analytics.js +0 -120
  173. package/build/sdk/errors.js +0 -34
  174. package/build/sdk/index.js +0 -594
  175. package/build/sdk/models.js +0 -149
  176. package/build/sdk/offline_handlers.js +0 -66
  177. package/build/sdk/polling_manager.js +0 -72
  178. package/build/sdk/utils.d.ts +0 -12
  179. package/build/sdk/utils.js +0 -107
  180. package/jest.config.js +0 -5
  181. package/tests/index.js +0 -0
  182. /package/build/{flagsmith-engine → cjs/flagsmith-engine}/features/constants.d.ts +0 -0
  183. /package/build/{flagsmith-engine → cjs/flagsmith-engine}/features/constants.js +0 -0
  184. /package/build/{flagsmith-engine → cjs/flagsmith-engine}/features/models.d.ts +0 -0
  185. /package/build/{flagsmith-engine → cjs/flagsmith-engine}/identities/traits/models.d.ts +0 -0
  186. /package/build/{flagsmith-engine → cjs/flagsmith-engine}/organisations/models.d.ts +0 -0
  187. /package/build/{flagsmith-engine → cjs/flagsmith-engine}/segments/constants.d.ts +0 -0
  188. /package/build/{flagsmith-engine → cjs/flagsmith-engine}/segments/constants.js +0 -0
  189. /package/build/{flagsmith-engine → cjs/flagsmith-engine}/utils/errors.d.ts +0 -0
  190. /package/build/{flagsmith-engine → cjs/flagsmith-engine}/utils/hashing/index.d.ts +0 -0
  191. /package/build/{flagsmith-engine → cjs/flagsmith-engine}/utils/index.d.ts +0 -0
  192. /package/build/{sdk → cjs/sdk}/errors.d.ts +0 -0
  193. /package/build/{sdk → cjs/sdk}/types.js +0 -0
@@ -0,0 +1,400 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Flagsmith = exports.EnvironmentDataPollingManager = exports.Flags = exports.DefaultFlag = exports.FlagsmithClientError = exports.FlagsmithAPIError = exports.AnalyticsProcessor = void 0;
4
+ const index_js_1 = require("../flagsmith-engine/index.js");
5
+ const util_js_1 = require("../flagsmith-engine/environments/util.js");
6
+ const index_js_2 = require("../flagsmith-engine/index.js");
7
+ const index_js_3 = require("../flagsmith-engine/index.js");
8
+ const analytics_js_1 = require("./analytics.js");
9
+ const errors_js_1 = require("./errors.js");
10
+ const models_js_1 = require("./models.js");
11
+ const polling_manager_js_1 = require("./polling_manager.js");
12
+ const utils_js_1 = require("./utils.js");
13
+ const evaluators_js_1 = require("../flagsmith-engine/segments/evaluators.js");
14
+ const pino_1 = require("pino");
15
+ var analytics_js_2 = require("./analytics.js");
16
+ Object.defineProperty(exports, "AnalyticsProcessor", { enumerable: true, get: function () { return analytics_js_2.AnalyticsProcessor; } });
17
+ var errors_js_2 = require("./errors.js");
18
+ Object.defineProperty(exports, "FlagsmithAPIError", { enumerable: true, get: function () { return errors_js_2.FlagsmithAPIError; } });
19
+ Object.defineProperty(exports, "FlagsmithClientError", { enumerable: true, get: function () { return errors_js_2.FlagsmithClientError; } });
20
+ var models_js_2 = require("./models.js");
21
+ Object.defineProperty(exports, "DefaultFlag", { enumerable: true, get: function () { return models_js_2.DefaultFlag; } });
22
+ Object.defineProperty(exports, "Flags", { enumerable: true, get: function () { return models_js_2.Flags; } });
23
+ var polling_manager_js_2 = require("./polling_manager.js");
24
+ Object.defineProperty(exports, "EnvironmentDataPollingManager", { enumerable: true, get: function () { return polling_manager_js_2.EnvironmentDataPollingManager; } });
25
+ const DEFAULT_API_URL = 'https://edge.api.flagsmith.com/api/v1/';
26
+ const DEFAULT_REQUEST_TIMEOUT_SECONDS = 10;
27
+ class Flagsmith {
28
+ environmentKey = undefined;
29
+ apiUrl = undefined;
30
+ customHeaders;
31
+ agent;
32
+ requestTimeoutMs;
33
+ enableLocalEvaluation = false;
34
+ environmentRefreshIntervalSeconds = 60;
35
+ retries;
36
+ enableAnalytics = false;
37
+ defaultFlagHandler;
38
+ environmentFlagsUrl;
39
+ identitiesUrl;
40
+ environmentUrl;
41
+ environmentDataPollingManager;
42
+ environment;
43
+ offlineMode = false;
44
+ offlineHandler = undefined;
45
+ identitiesWithOverridesByIdentifier;
46
+ cache;
47
+ onEnvironmentChange;
48
+ analyticsProcessor;
49
+ logger;
50
+ customFetch;
51
+ /**
52
+ * A Flagsmith client.
53
+ *
54
+ * Provides an interface for interacting with the Flagsmith http API.
55
+ * Basic Usage::
56
+ *
57
+ * import flagsmith from Flagsmith
58
+ * const flagsmith = new Flagsmith({environmentKey: '<your API key>'});
59
+ * const environmentFlags = flagsmith.getEnvironmentFlags();
60
+ * const featureEnabled = environmentFlags.isFeatureEnabled('foo');
61
+ * const identityFlags = flagsmith.getIdentityFlags('identifier', {'foo': 'bar'});
62
+ * const featureEnabledForIdentity = identityFlags.isFeatureEnabled("foo")
63
+ *
64
+ * @param {string} data.environmentKey: The environment key obtained from Flagsmith interface
65
+ * Required unless offlineMode is True.
66
+ @param {string} data.apiUrl: Override the URL of the Flagsmith API to communicate with
67
+ @param data.customHeaders: Additional headers to add to requests made to the
68
+ Flagsmith API
69
+ @param {number} data.requestTimeoutSeconds: Number of seconds to wait for a request to
70
+ complete before terminating the request
71
+ @param {boolean} data.enableLocalEvaluation: Enables local evaluation of flags
72
+ @param {number} data.environmentRefreshIntervalSeconds: If using local evaluation,
73
+ specify the interval period between refreshes of local environment data
74
+ @param {number} data.retries: a urllib3.Retry object to use on all http requests to the
75
+ Flagsmith API
76
+ @param {boolean} data.enableAnalytics: if enabled, sends additional requests to the Flagsmith
77
+ API to power flag analytics charts
78
+ @param data.defaultFlagHandler: callable which will be used in the case where
79
+ flags cannot be retrieved from the API or a non-existent feature is
80
+ requested
81
+ @param data.logger: an instance of the pino Logger class to use for logging
82
+ @param {boolean} data.offlineMode: sets the client into offline mode. Relies on offlineHandler for
83
+ evaluating flags.
84
+ @param {BaseOfflineHandler} data.offlineHandler: provide a handler for offline logic. Used to get environment
85
+ document from another source when in offlineMode. Works in place of
86
+ defaultFlagHandler if offlineMode is not set and using remote evaluation.
87
+ */
88
+ constructor(data = {}) {
89
+ // if (!data.offlineMode && !data.environmentKey) {
90
+ // throw new Error('ValueError: environmentKey is required.');
91
+ // }
92
+ this.agent = data.agent;
93
+ this.customFetch = data.fetch ?? fetch;
94
+ this.environmentKey = data.environmentKey;
95
+ this.apiUrl = data.apiUrl || this.apiUrl;
96
+ this.customHeaders = data.customHeaders;
97
+ this.requestTimeoutMs =
98
+ 1000 * (data.requestTimeoutSeconds ?? DEFAULT_REQUEST_TIMEOUT_SECONDS);
99
+ this.enableLocalEvaluation = data.enableLocalEvaluation;
100
+ this.environmentRefreshIntervalSeconds =
101
+ data.environmentRefreshIntervalSeconds || this.environmentRefreshIntervalSeconds;
102
+ this.retries = data.retries;
103
+ this.enableAnalytics = data.enableAnalytics || false;
104
+ this.defaultFlagHandler = data.defaultFlagHandler;
105
+ this.onEnvironmentChange = data.onEnvironmentChange;
106
+ this.logger = data.logger || (0, pino_1.pino)();
107
+ this.offlineMode = data.offlineMode || false;
108
+ this.offlineHandler = data.offlineHandler;
109
+ // argument validation
110
+ if (this.offlineMode && !this.offlineHandler) {
111
+ throw new Error('ValueError: offlineHandler must be provided to use offline mode.');
112
+ }
113
+ else if (this.defaultFlagHandler && this.offlineHandler) {
114
+ throw new Error('ValueError: Cannot use both defaultFlagHandler and offlineHandler.');
115
+ }
116
+ if (this.offlineHandler) {
117
+ this.environment = this.offlineHandler.getEnvironment();
118
+ }
119
+ if (!!data.cache) {
120
+ const missingMethods = ['has', 'get', 'set'].filter(method => data.cache && !data.cache[method]);
121
+ if (missingMethods.length > 0) {
122
+ throw new Error(`Please implement the following methods in your cache: ${missingMethods.join(', ')}`);
123
+ }
124
+ this.cache = data.cache;
125
+ }
126
+ if (!this.offlineMode) {
127
+ if (!this.environmentKey) {
128
+ throw new Error('ValueError: environmentKey is required.');
129
+ }
130
+ const apiUrl = data.apiUrl || DEFAULT_API_URL;
131
+ this.apiUrl = apiUrl.endsWith('/') ? apiUrl : `${apiUrl}/`;
132
+ this.environmentFlagsUrl = `${this.apiUrl}flags/`;
133
+ this.identitiesUrl = `${this.apiUrl}identities/`;
134
+ this.environmentUrl = `${this.apiUrl}environment-document/`;
135
+ if (this.enableLocalEvaluation) {
136
+ if (!this.environmentKey.startsWith('ser.')) {
137
+ console.error('In order to use local evaluation, please generate a server key in the environment settings page.');
138
+ }
139
+ this.environmentDataPollingManager = new polling_manager_js_1.EnvironmentDataPollingManager(this, this.environmentRefreshIntervalSeconds);
140
+ this.environmentDataPollingManager.start();
141
+ this.updateEnvironment();
142
+ }
143
+ this.analyticsProcessor = data.enableAnalytics
144
+ ? new analytics_js_1.AnalyticsProcessor({
145
+ environmentKey: this.environmentKey,
146
+ baseApiUrl: this.apiUrl,
147
+ requestTimeoutMs: this.requestTimeoutMs,
148
+ logger: this.logger
149
+ })
150
+ : undefined;
151
+ }
152
+ }
153
+ /**
154
+ * Get all the default for flags for the current environment.
155
+ *
156
+ * @returns Flags object holding all the flags for the current environment.
157
+ */
158
+ async getEnvironmentFlags() {
159
+ const cachedItem = !!this.cache && (await this.cache.get(`flags`));
160
+ if (!!cachedItem) {
161
+ return cachedItem;
162
+ }
163
+ if (this.enableLocalEvaluation && !this.offlineMode) {
164
+ return new Promise((resolve, reject) => this.environmentPromise.then(() => {
165
+ resolve(this.getEnvironmentFlagsFromDocument());
166
+ }).catch(e => reject(e)));
167
+ }
168
+ if (this.environment) {
169
+ return this.getEnvironmentFlagsFromDocument();
170
+ }
171
+ return this.getEnvironmentFlagsFromApi();
172
+ }
173
+ /**
174
+ * Get all the flags for the current environment for a given identity. Will also
175
+ upsert all traits to the Flagsmith API for future evaluations. Providing a
176
+ trait with a value of None will remove the trait from the identity if it exists.
177
+ *
178
+ * @param {string} identifier a unique identifier for the identity in the current
179
+ environment, e.g. email address, username, uuid
180
+ * @param {{[key:string]:any | ITraitConfig}} traits? a dictionary of traits to add / update on the identity in
181
+ Flagsmith, e.g. {"num_orders": 10} or {age: {value: 30, transient: true}}
182
+ * @returns Flags object holding all the flags for the given identity.
183
+ */
184
+ async getIdentityFlags(identifier, traits, transient = false) {
185
+ if (!identifier) {
186
+ throw new Error('`identifier` argument is missing or invalid.');
187
+ }
188
+ const cachedItem = !!this.cache && (await this.cache.get(`flags-${identifier}`));
189
+ if (!!cachedItem) {
190
+ return cachedItem;
191
+ }
192
+ traits = traits || {};
193
+ if (this.enableLocalEvaluation) {
194
+ return new Promise((resolve, reject) => this.environmentPromise.then(() => {
195
+ resolve(this.getIdentityFlagsFromDocument(identifier, traits || {}));
196
+ }).catch(e => reject(e)));
197
+ }
198
+ if (this.offlineMode) {
199
+ return this.getIdentityFlagsFromDocument(identifier, traits || {});
200
+ }
201
+ return this.getIdentityFlagsFromApi(identifier, traits, transient);
202
+ }
203
+ /**
204
+ * Get the segments for the current environment for a given identity. Will also
205
+ upsert all traits to the Flagsmith API for future evaluations. Providing a
206
+ trait with a value of None will remove the trait from the identity if it exists.
207
+ *
208
+ * @param {string} identifier a unique identifier for the identity in the current
209
+ 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}
212
+ * @returns Segments that the given identity belongs to.
213
+ */
214
+ getIdentitySegments(identifier, traits) {
215
+ if (!identifier) {
216
+ throw new Error('`identifier` argument is missing or invalid.');
217
+ }
218
+ traits = traits || {};
219
+ if (this.enableLocalEvaluation) {
220
+ return new Promise((resolve, reject) => {
221
+ return this.environmentPromise.then(() => {
222
+ const identityModel = this.getIdentityModel(identifier, Object.keys(traits || {}).map(key => ({
223
+ key,
224
+ value: traits?.[key]
225
+ })));
226
+ const segments = (0, evaluators_js_1.getIdentitySegments)(this.environment, identityModel);
227
+ return resolve(segments);
228
+ }).catch(e => reject(e));
229
+ });
230
+ }
231
+ console.error('This function is only permitted with local evaluation.');
232
+ return Promise.resolve([]);
233
+ }
234
+ /**
235
+ * Updates the environment state for local flag evaluation.
236
+ * Sets a local promise to prevent race conditions in getIdentityFlags / getIdentitySegments.
237
+ * You only need to call this if you wish to bypass environmentRefreshIntervalSeconds.
238
+ */
239
+ async updateEnvironment() {
240
+ try {
241
+ const request = this.getEnvironmentFromApi();
242
+ if (!this.environmentPromise) {
243
+ this.environmentPromise = request.then(res => {
244
+ this.environment = res;
245
+ });
246
+ await this.environmentPromise;
247
+ }
248
+ else {
249
+ this.environment = await request;
250
+ }
251
+ if (this.environment.identityOverrides?.length) {
252
+ this.identitiesWithOverridesByIdentifier = new Map(this.environment.identityOverrides.map(identity => [
253
+ identity.identifier,
254
+ identity
255
+ ]));
256
+ }
257
+ if (this.onEnvironmentChange) {
258
+ this.onEnvironmentChange(null, this.environment);
259
+ }
260
+ }
261
+ catch (e) {
262
+ if (this.onEnvironmentChange) {
263
+ this.onEnvironmentChange(e, this.environment);
264
+ }
265
+ }
266
+ }
267
+ async close() {
268
+ this.environmentDataPollingManager?.stop();
269
+ }
270
+ async getJSONResponse(url, method, body) {
271
+ const headers = { 'Content-Type': 'application/json' };
272
+ if (this.environmentKey) {
273
+ headers['X-Environment-Key'] = this.environmentKey;
274
+ }
275
+ if (this.customHeaders) {
276
+ for (const [k, v] of Object.entries(this.customHeaders)) {
277
+ headers[k] = v;
278
+ }
279
+ }
280
+ const data = await (0, utils_js_1.retryFetch)(url, {
281
+ dispatcher: this.agent,
282
+ method: method,
283
+ body: JSON.stringify(body),
284
+ headers: headers
285
+ }, this.retries, this.requestTimeoutMs, this.customFetch);
286
+ if (data.status !== 200) {
287
+ throw new errors_js_1.FlagsmithAPIError(`Invalid request made to Flagsmith API. Response status code: ${data.status}`);
288
+ }
289
+ return data.json();
290
+ }
291
+ /**
292
+ * This promise ensures that the environment is retrieved before attempting to locally evaluate.
293
+ */
294
+ environmentPromise;
295
+ async getEnvironmentFromApi() {
296
+ if (!this.environmentUrl) {
297
+ throw new Error('`apiUrl` argument is missing or invalid.');
298
+ }
299
+ const environment_data = await this.getJSONResponse(this.environmentUrl, 'GET');
300
+ return (0, util_js_1.buildEnvironmentModel)(environment_data);
301
+ }
302
+ async getEnvironmentFlagsFromDocument() {
303
+ const flags = models_js_1.Flags.fromFeatureStateModels({
304
+ featureStates: (0, index_js_1.getEnvironmentFeatureStates)(this.environment),
305
+ analyticsProcessor: this.analyticsProcessor,
306
+ defaultFlagHandler: this.defaultFlagHandler
307
+ });
308
+ if (!!this.cache) {
309
+ await this.cache.set('flags', flags);
310
+ }
311
+ return flags;
312
+ }
313
+ async getIdentityFlagsFromDocument(identifier, traits) {
314
+ const identityModel = this.getIdentityModel(identifier, Object.keys(traits).map(key => ({
315
+ key,
316
+ value: traits[key]
317
+ })));
318
+ const featureStates = (0, index_js_1.getIdentityFeatureStates)(this.environment, identityModel);
319
+ const flags = models_js_1.Flags.fromFeatureStateModels({
320
+ featureStates: featureStates,
321
+ analyticsProcessor: this.analyticsProcessor,
322
+ defaultFlagHandler: this.defaultFlagHandler,
323
+ identityID: identityModel.djangoID || identityModel.compositeKey
324
+ });
325
+ if (!!this.cache) {
326
+ await this.cache.set(`flags-${identifier}`, flags);
327
+ }
328
+ return flags;
329
+ }
330
+ async getEnvironmentFlagsFromApi() {
331
+ if (!this.environmentFlagsUrl) {
332
+ throw new Error('`apiUrl` argument is missing or invalid.');
333
+ }
334
+ try {
335
+ const apiFlags = await this.getJSONResponse(this.environmentFlagsUrl, 'GET');
336
+ const flags = models_js_1.Flags.fromAPIFlags({
337
+ apiFlags: apiFlags,
338
+ analyticsProcessor: this.analyticsProcessor,
339
+ defaultFlagHandler: this.defaultFlagHandler
340
+ });
341
+ if (!!this.cache) {
342
+ await this.cache.set('flags', flags);
343
+ }
344
+ return flags;
345
+ }
346
+ catch (e) {
347
+ if (this.offlineHandler) {
348
+ return this.getEnvironmentFlagsFromDocument();
349
+ }
350
+ if (this.defaultFlagHandler) {
351
+ return new models_js_1.Flags({
352
+ flags: {},
353
+ defaultFlagHandler: this.defaultFlagHandler
354
+ });
355
+ }
356
+ throw e;
357
+ }
358
+ }
359
+ async getIdentityFlagsFromApi(identifier, traits, transient = false) {
360
+ if (!this.identitiesUrl) {
361
+ throw new Error('`apiUrl` argument is missing or invalid.');
362
+ }
363
+ try {
364
+ const data = (0, utils_js_1.generateIdentitiesData)(identifier, traits, transient);
365
+ const jsonResponse = await this.getJSONResponse(this.identitiesUrl, 'POST', data);
366
+ const flags = models_js_1.Flags.fromAPIFlags({
367
+ apiFlags: jsonResponse['flags'],
368
+ analyticsProcessor: this.analyticsProcessor,
369
+ defaultFlagHandler: this.defaultFlagHandler
370
+ });
371
+ if (!!this.cache) {
372
+ await this.cache.set(`flags-${identifier}`, flags);
373
+ }
374
+ return flags;
375
+ }
376
+ catch (e) {
377
+ if (this.offlineHandler) {
378
+ return this.getIdentityFlagsFromDocument(identifier, traits);
379
+ }
380
+ if (this.defaultFlagHandler) {
381
+ return new models_js_1.Flags({
382
+ flags: {},
383
+ defaultFlagHandler: this.defaultFlagHandler
384
+ });
385
+ }
386
+ throw e;
387
+ }
388
+ }
389
+ getIdentityModel(identifier, traits) {
390
+ const traitModels = traits.map(trait => new index_js_3.TraitModel(trait.key, trait.value));
391
+ let identityWithOverrides = this.identitiesWithOverridesByIdentifier?.get(identifier);
392
+ if (identityWithOverrides) {
393
+ identityWithOverrides.updateTraits(traitModels);
394
+ return identityWithOverrides;
395
+ }
396
+ return new index_js_2.IdentityModel('0', traitModels, [], this.environment.apiKey, identifier);
397
+ }
398
+ }
399
+ exports.Flagsmith = Flagsmith;
400
+ exports.default = Flagsmith;
@@ -1,5 +1,5 @@
1
- import { FeatureStateModel } from '../flagsmith-engine/features/models';
2
- import { AnalyticsProcessor } from './analytics';
1
+ import { FeatureStateModel } from '../flagsmith-engine/features/models.js';
2
+ import { AnalyticsProcessor } from './analytics.js';
3
3
  export declare class BaseFlag {
4
4
  enabled: boolean;
5
5
  value: string | number | boolean | undefined;
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Flags = exports.Flag = exports.DefaultFlag = exports.BaseFlag = void 0;
4
+ class BaseFlag {
5
+ enabled;
6
+ value;
7
+ isDefault;
8
+ constructor(value, enabled, isDefault) {
9
+ this.value = value;
10
+ this.enabled = enabled;
11
+ this.isDefault = isDefault;
12
+ }
13
+ }
14
+ exports.BaseFlag = BaseFlag;
15
+ class DefaultFlag extends BaseFlag {
16
+ constructor(value, enabled) {
17
+ super(value, enabled, true);
18
+ }
19
+ }
20
+ exports.DefaultFlag = DefaultFlag;
21
+ class Flag extends BaseFlag {
22
+ featureId;
23
+ featureName;
24
+ constructor(params) {
25
+ super(params.value, params.enabled, !!params.isDefault);
26
+ this.featureId = params.featureId;
27
+ this.featureName = params.featureName;
28
+ }
29
+ static fromFeatureStateModel(fsm, identityId) {
30
+ return new Flag({
31
+ value: fsm.getValue(identityId),
32
+ enabled: fsm.enabled,
33
+ featureId: fsm.feature.id,
34
+ featureName: fsm.feature.name
35
+ });
36
+ }
37
+ static fromAPIFlag(flagData) {
38
+ return new Flag({
39
+ enabled: flagData['enabled'],
40
+ value: flagData['feature_state_value'] || flagData['value'],
41
+ featureId: flagData['feature']['id'],
42
+ featureName: flagData['feature']['name']
43
+ });
44
+ }
45
+ }
46
+ exports.Flag = Flag;
47
+ class Flags {
48
+ flags = {};
49
+ defaultFlagHandler;
50
+ analyticsProcessor;
51
+ constructor(data) {
52
+ this.flags = data.flags;
53
+ this.defaultFlagHandler = data.defaultFlagHandler;
54
+ this.analyticsProcessor = data.analyticsProcessor;
55
+ }
56
+ static fromFeatureStateModels(data) {
57
+ const flags = {};
58
+ for (const fs of data.featureStates) {
59
+ flags[fs.feature.name] = Flag.fromFeatureStateModel(fs, data.identityID);
60
+ }
61
+ return new Flags({
62
+ flags: flags,
63
+ defaultFlagHandler: data.defaultFlagHandler,
64
+ analyticsProcessor: data.analyticsProcessor
65
+ });
66
+ }
67
+ static fromAPIFlags(data) {
68
+ const flags = {};
69
+ for (const flagData of data.apiFlags) {
70
+ flags[flagData['feature']['name']] = Flag.fromAPIFlag(flagData);
71
+ }
72
+ return new Flags({
73
+ flags: flags,
74
+ defaultFlagHandler: data.defaultFlagHandler,
75
+ analyticsProcessor: data.analyticsProcessor
76
+ });
77
+ }
78
+ allFlags() {
79
+ return Object.values(this.flags);
80
+ }
81
+ getFlag(featureName) {
82
+ const flag = this.flags[featureName];
83
+ if (!flag) {
84
+ if (this.defaultFlagHandler) {
85
+ return this.defaultFlagHandler(featureName);
86
+ }
87
+ return { enabled: false, isDefault: true, value: undefined };
88
+ }
89
+ if (this.analyticsProcessor && flag.featureId) {
90
+ this.analyticsProcessor.trackFeature(flag.featureName);
91
+ }
92
+ return flag;
93
+ }
94
+ getFeatureValue(featureName) {
95
+ return this.getFlag(featureName).value;
96
+ }
97
+ isFeatureEnabled(featureName) {
98
+ return this.getFlag(featureName).enabled;
99
+ }
100
+ }
101
+ exports.Flags = Flags;
@@ -1,4 +1,4 @@
1
- import { EnvironmentModel } from '../flagsmith-engine/environments/models';
1
+ import { EnvironmentModel } from '../flagsmith-engine/environments/models.js';
2
2
  export declare class BaseOfflineHandler {
3
3
  getEnvironment(): EnvironmentModel;
4
4
  }
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LocalFileHandler = exports.BaseOfflineHandler = void 0;
4
+ const fs = require("fs");
5
+ const util_js_1 = require("../flagsmith-engine/environments/util.js");
6
+ class BaseOfflineHandler {
7
+ getEnvironment() {
8
+ throw new Error('Not implemented');
9
+ }
10
+ }
11
+ exports.BaseOfflineHandler = BaseOfflineHandler;
12
+ class LocalFileHandler extends BaseOfflineHandler {
13
+ environment;
14
+ constructor(environment_document_path) {
15
+ super();
16
+ const environment_document = fs.readFileSync(environment_document_path, 'utf8');
17
+ this.environment = (0, util_js_1.buildEnvironmentModel)(JSON.parse(environment_document));
18
+ }
19
+ getEnvironment() {
20
+ return this.environment;
21
+ }
22
+ }
23
+ exports.LocalFileHandler = LocalFileHandler;
@@ -1,4 +1,4 @@
1
- import Flagsmith from '.';
1
+ import Flagsmith from './index.js';
2
2
  export declare class EnvironmentDataPollingManager {
3
3
  private interval?;
4
4
  private main;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EnvironmentDataPollingManager = void 0;
4
+ class EnvironmentDataPollingManager {
5
+ interval;
6
+ main;
7
+ refreshIntervalSeconds;
8
+ constructor(main, refreshIntervalSeconds) {
9
+ this.main = main;
10
+ this.refreshIntervalSeconds = refreshIntervalSeconds;
11
+ }
12
+ start() {
13
+ const updateEnvironment = () => {
14
+ if (this.interval)
15
+ clearInterval(this.interval);
16
+ this.interval = setInterval(async () => {
17
+ await this.main.updateEnvironment();
18
+ }, this.refreshIntervalSeconds * 1000);
19
+ };
20
+ updateEnvironment();
21
+ }
22
+ stop() {
23
+ if (!this.interval) {
24
+ return;
25
+ }
26
+ clearInterval(this.interval);
27
+ }
28
+ }
29
+ exports.EnvironmentDataPollingManager = EnvironmentDataPollingManager;
@@ -1,18 +1,21 @@
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
+ export type IFlagsmithValue<T = string | number | boolean | null> = T;
6
7
  export interface FlagsmithCache {
7
8
  get(key: string): Promise<Flags | undefined> | undefined;
8
- set(key: string, value: Flags, ttl: string | number): boolean | Promise<boolean>;
9
+ set(key: string, value: Flags, ttl?: string | number): boolean | Promise<boolean>;
9
10
  has(key: string): boolean | Promise<boolean>;
10
11
  [key: string]: any;
11
12
  }
13
+ export type Fetch = typeof fetch;
12
14
  export interface FlagsmithConfig {
13
15
  environmentKey?: string;
14
16
  apiUrl?: string;
15
- agent?: RequestInit['agent'];
17
+ agent?: Dispatcher;
18
+ fetch?: Fetch;
16
19
  customHeaders?: {
17
20
  [key: string]: any;
18
21
  };
@@ -28,3 +31,8 @@ export interface FlagsmithConfig {
28
31
  offlineMode?: boolean;
29
32
  offlineHandler?: BaseOfflineHandler;
30
33
  }
34
+ export interface ITraitConfig {
35
+ value: FlagsmithTraitValue;
36
+ transient?: boolean;
37
+ }
38
+ export declare type FlagsmithTraitValue = IFlagsmithValue;
@@ -0,0 +1,36 @@
1
+ import { Fetch, FlagsmithTraitValue, ITraitConfig } from './types.js';
2
+ import { Dispatcher } from "undici-types";
3
+ type Traits = {
4
+ [key: string]: ITraitConfig | FlagsmithTraitValue;
5
+ };
6
+ export declare function isTraitConfig(traitValue: ITraitConfig | FlagsmithTraitValue): traitValue is ITraitConfig;
7
+ export declare function generateIdentitiesData(identifier: string, traits: Traits, transient: boolean): {
8
+ identifier: string;
9
+ traits: ({
10
+ trait_key: string;
11
+ trait_value: string | number | boolean | null;
12
+ transient: boolean | undefined;
13
+ } | {
14
+ trait_key: string;
15
+ trait_value: string | number | boolean | null;
16
+ transient?: undefined;
17
+ })[];
18
+ transient: boolean;
19
+ } | {
20
+ identifier: string;
21
+ traits: ({
22
+ trait_key: string;
23
+ trait_value: string | number | boolean | null;
24
+ transient: boolean | undefined;
25
+ } | {
26
+ trait_key: string;
27
+ trait_value: string | number | boolean | null;
28
+ transient?: undefined;
29
+ })[];
30
+ transient?: undefined;
31
+ };
32
+ export declare const delay: (ms: number) => Promise<unknown>;
33
+ export declare const retryFetch: (url: string, fetchOptions: RequestInit & {
34
+ dispatcher?: Dispatcher;
35
+ }, retries: number | undefined, timeoutMs: number | undefined, customFetch: Fetch) => Promise<Response>;
36
+ export {};