flagsmith-nodejs 3.1.1 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.husky/pre-commit CHANGED
@@ -2,5 +2,5 @@
2
2
  . "$(dirname "$0")/_/husky.sh"
3
3
 
4
4
  npm run lint
5
- git add ./flagsmith-engine ./sdk ./tests ./example ./index.ts ./.github
5
+ git add ./flagsmith-engine ./sdk ./tests ./index.ts ./.github
6
6
  npm run test
package/README.md CHANGED
@@ -22,7 +22,8 @@ If you encounter a bug or feature request we would like to hear about it. Before
22
22
  ## Testing
23
23
 
24
24
  To run the local tests you need to run following command beforehand:
25
- ```
25
+
26
+ ```bash
26
27
  git submodule add git@github.com:Flagsmith/engine-test-data.git tests/engine/engine-tests/engine-test-data/
27
28
  ```
28
29
 
@@ -1,6 +1,6 @@
1
1
  import { FeatureStateModel } from '../features/models';
2
+ import { IdentityModel } from '../identities/models';
2
3
  import { ProjectModel } from '../projects/models';
3
- import { IntegrationModel } from './integrations/models';
4
4
  export declare class EnvironmentAPIKeyModel {
5
5
  id: number;
6
6
  key: string;
@@ -17,9 +17,6 @@ export declare class EnvironmentModel {
17
17
  apiKey: string;
18
18
  project: ProjectModel;
19
19
  featureStates: FeatureStateModel[];
20
- amplitude_config?: IntegrationModel;
21
- segment_config?: IntegrationModel;
22
- mixpanel_config?: IntegrationModel;
23
- heap_config?: IntegrationModel;
20
+ identityOverrides: IdentityModel[];
24
21
  constructor(id: number, apiKey: string, project: ProjectModel);
25
22
  }
@@ -20,6 +20,7 @@ exports.EnvironmentAPIKeyModel = EnvironmentAPIKeyModel;
20
20
  var EnvironmentModel = /** @class */ (function () {
21
21
  function EnvironmentModel(id, apiKey, project) {
22
22
  this.featureStates = [];
23
+ this.identityOverrides = [];
23
24
  this.id = id;
24
25
  this.apiKey = apiKey;
25
26
  this.project = project;
@@ -2,15 +2,21 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.buildEnvironmentAPIKeyModel = exports.buildEnvironmentModel = void 0;
4
4
  var util_1 = require("../features/util");
5
- var util_2 = require("../projects/util");
5
+ var util_2 = require("../identities/util");
6
+ var util_3 = require("../projects/util");
6
7
  var models_1 = require("./models");
7
8
  function buildEnvironmentModel(environmentJSON) {
8
- var project = (0, util_2.buildProjectModel)(environmentJSON.project);
9
+ var project = (0, util_3.buildProjectModel)(environmentJSON.project);
9
10
  var featureStates = environmentJSON.feature_states.map(function (fs) {
10
11
  return (0, util_1.buildFeatureStateModel)(fs);
11
12
  });
12
13
  var environmentModel = new models_1.EnvironmentModel(environmentJSON.id, environmentJSON.api_key, project);
13
14
  environmentModel.featureStates = featureStates;
15
+ if (!!environmentJSON.identity_overrides) {
16
+ environmentModel.identityOverrides = environmentJSON.identity_overrides.map(function (identityData) {
17
+ return (0, util_2.buildIdentityModel)(identityData);
18
+ });
19
+ }
14
20
  return environmentModel;
15
21
  }
16
22
  exports.buildEnvironmentModel = buildEnvironmentModel;
@@ -7,14 +7,14 @@ function buildFeatureModel(featuresModelJSON) {
7
7
  }
8
8
  exports.buildFeatureModel = buildFeatureModel;
9
9
  function buildFeatureStateModel(featuresStateModelJSON) {
10
- var featureStateModel = new models_1.FeatureStateModel(buildFeatureModel(featuresStateModelJSON.feature), featuresStateModelJSON.enabled, featuresStateModelJSON.django_id, featuresStateModelJSON.feature_state_value, featuresStateModelJSON.uuid);
10
+ var featureStateModel = new models_1.FeatureStateModel(buildFeatureModel(featuresStateModelJSON.feature), featuresStateModelJSON.enabled, featuresStateModelJSON.django_id, featuresStateModelJSON.feature_state_value, featuresStateModelJSON.featurestate_uuid);
11
11
  featureStateModel.featureSegment = featuresStateModelJSON.feature_segment ?
12
12
  buildFeatureSegment(featuresStateModelJSON.feature_segment) :
13
13
  undefined;
14
14
  var multivariateFeatureStateValues = featuresStateModelJSON.multivariate_feature_state_values
15
15
  ? featuresStateModelJSON.multivariate_feature_state_values.map(function (fsv) {
16
16
  var featureOption = new models_1.MultivariateFeatureOptionModel(fsv.multivariate_feature_option.value, fsv.multivariate_feature_option.id);
17
- return new models_1.MultivariateFeatureStateValueModel(featureOption, fsv.percentage_allocation, fsv.id);
17
+ return new models_1.MultivariateFeatureStateValueModel(featureOption, fsv.percentage_allocation, fsv.id, fsv.mv_fs_value_uuid);
18
18
  })
19
19
  : [];
20
20
  featureStateModel.multivariateFeatureStateValues = multivariateFeatureStateValues;
@@ -3,7 +3,6 @@ import { FeatureStateModel } from './features/models';
3
3
  import { IdentityModel } from './identities/models';
4
4
  import { TraitModel } from './identities/traits/models';
5
5
  export { EnvironmentModel } from './environments/models';
6
- export { IntegrationModel } from './environments/integrations/models';
7
6
  export { FeatureStateModel } from './features/models';
8
7
  export { IdentityModel } from './identities/models';
9
8
  export { TraitModel } from './identities/traits/models';
@@ -11,23 +11,21 @@ var __values = (this && this.__values) || function(o) {
11
11
  throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
12
12
  };
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.getEnvironmentFeatureStates = exports.getEnvironmentFeatureState = exports.getIdentityFeatureStates = exports.getIdentityFeatureState = exports.OrganisationModel = exports.SegmentModel = exports.TraitModel = exports.IdentityModel = exports.FeatureStateModel = exports.IntegrationModel = exports.EnvironmentModel = void 0;
14
+ exports.getEnvironmentFeatureStates = exports.getEnvironmentFeatureState = exports.getIdentityFeatureStates = exports.getIdentityFeatureState = exports.OrganisationModel = exports.SegmentModel = exports.TraitModel = exports.IdentityModel = exports.FeatureStateModel = exports.EnvironmentModel = void 0;
15
15
  var evaluators_1 = require("./segments/evaluators");
16
16
  var errors_1 = require("./utils/errors");
17
17
  var models_1 = require("./environments/models");
18
18
  Object.defineProperty(exports, "EnvironmentModel", { enumerable: true, get: function () { return models_1.EnvironmentModel; } });
19
- var models_2 = require("./environments/integrations/models");
20
- Object.defineProperty(exports, "IntegrationModel", { enumerable: true, get: function () { return models_2.IntegrationModel; } });
21
- var models_3 = require("./features/models");
22
- Object.defineProperty(exports, "FeatureStateModel", { enumerable: true, get: function () { return models_3.FeatureStateModel; } });
23
- var models_4 = require("./identities/models");
24
- Object.defineProperty(exports, "IdentityModel", { enumerable: true, get: function () { return models_4.IdentityModel; } });
25
- var models_5 = require("./identities/traits/models");
26
- Object.defineProperty(exports, "TraitModel", { enumerable: true, get: function () { return models_5.TraitModel; } });
27
- var models_6 = require("./segments/models");
28
- Object.defineProperty(exports, "SegmentModel", { enumerable: true, get: function () { return models_6.SegmentModel; } });
29
- var models_7 = require("./organisations/models");
30
- Object.defineProperty(exports, "OrganisationModel", { enumerable: true, get: function () { return models_7.OrganisationModel; } });
19
+ var models_2 = require("./features/models");
20
+ Object.defineProperty(exports, "FeatureStateModel", { enumerable: true, get: function () { return models_2.FeatureStateModel; } });
21
+ var models_3 = require("./identities/models");
22
+ Object.defineProperty(exports, "IdentityModel", { enumerable: true, get: function () { return models_3.IdentityModel; } });
23
+ var models_4 = require("./identities/traits/models");
24
+ Object.defineProperty(exports, "TraitModel", { enumerable: true, get: function () { return models_4.TraitModel; } });
25
+ var models_5 = require("./segments/models");
26
+ Object.defineProperty(exports, "SegmentModel", { enumerable: true, get: function () { return models_5.SegmentModel; } });
27
+ var models_6 = require("./organisations/models");
28
+ Object.defineProperty(exports, "OrganisationModel", { enumerable: true, get: function () { return models_6.OrganisationModel; } });
31
29
  function getIdentityFeatureStatesDict(environment, identity, overrideTraits) {
32
30
  var e_1, _a, e_2, _b, e_3, _c, e_4, _d;
33
31
  // Get feature states from the environment
package/build/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { AnalyticsProcessor, FlagsmithAPIError, FlagsmithClientError, EnvironmentDataPollingManager, FlagsmithCache, DefaultFlag, Flags, default } from './sdk';
2
- export { EnvironmentModel, IntegrationModel, FeatureStateModel, IdentityModel, TraitModel, SegmentModel, OrganisationModel } from './flagsmith-engine';
2
+ export { FlagsmithConfig } from './sdk/types';
3
+ export { EnvironmentModel, FeatureStateModel, IdentityModel, TraitModel, SegmentModel, OrganisationModel } from './flagsmith-engine';
package/build/index.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.OrganisationModel = exports.SegmentModel = exports.TraitModel = exports.IdentityModel = exports.FeatureStateModel = exports.IntegrationModel = exports.EnvironmentModel = exports.default = exports.Flags = exports.DefaultFlag = exports.EnvironmentDataPollingManager = exports.FlagsmithClientError = exports.FlagsmithAPIError = exports.AnalyticsProcessor = void 0;
6
+ exports.OrganisationModel = exports.SegmentModel = exports.TraitModel = exports.IdentityModel = exports.FeatureStateModel = exports.EnvironmentModel = exports.default = exports.Flags = exports.DefaultFlag = exports.EnvironmentDataPollingManager = exports.FlagsmithClientError = exports.FlagsmithAPIError = exports.AnalyticsProcessor = void 0;
7
7
  var sdk_1 = __importDefault(require("./sdk"));
8
8
  var sdk_2 = require("./sdk");
9
9
  Object.defineProperty(exports, "AnalyticsProcessor", { enumerable: true, get: function () { return sdk_2.AnalyticsProcessor; } });
@@ -15,7 +15,6 @@ Object.defineProperty(exports, "Flags", { enumerable: true, get: function () { r
15
15
  Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(sdk_2).default; } });
16
16
  var flagsmith_engine_1 = require("./flagsmith-engine");
17
17
  Object.defineProperty(exports, "EnvironmentModel", { enumerable: true, get: function () { return flagsmith_engine_1.EnvironmentModel; } });
18
- Object.defineProperty(exports, "IntegrationModel", { enumerable: true, get: function () { return flagsmith_engine_1.IntegrationModel; } });
19
18
  Object.defineProperty(exports, "FeatureStateModel", { enumerable: true, get: function () { return flagsmith_engine_1.FeatureStateModel; } });
20
19
  Object.defineProperty(exports, "IdentityModel", { enumerable: true, get: function () { return flagsmith_engine_1.IdentityModel; } });
21
20
  Object.defineProperty(exports, "TraitModel", { enumerable: true, get: function () { return flagsmith_engine_1.TraitModel; } });
@@ -1,5 +1,7 @@
1
- import { RequestInit } from "node-fetch";
1
+ import { RequestInit } from 'node-fetch';
2
2
  import { EnvironmentModel } from '../flagsmith-engine/environments/models';
3
+ import { IdentityModel } from '../flagsmith-engine/identities/models';
4
+ import { BaseOfflineHandler } from './offline_handlers';
3
5
  import { DefaultFlag, Flags } from './models';
4
6
  import { EnvironmentDataPollingManager } from './polling_manager';
5
7
  import { SegmentModel } from '../flagsmith-engine/segments/models';
@@ -11,7 +13,7 @@ export { EnvironmentDataPollingManager } from './polling_manager';
11
13
  export { FlagsmithCache, FlagsmithConfig } from './types';
12
14
  export declare class Flagsmith {
13
15
  environmentKey?: string;
14
- apiUrl: string;
16
+ apiUrl?: string;
15
17
  customHeaders?: {
16
18
  [key: string]: any;
17
19
  };
@@ -22,11 +24,14 @@ export declare class Flagsmith {
22
24
  retries?: number;
23
25
  enableAnalytics: boolean;
24
26
  defaultFlagHandler?: (featureName: string) => DefaultFlag;
25
- environmentFlagsUrl: string;
26
- identitiesUrl: string;
27
- environmentUrl: string;
27
+ environmentFlagsUrl?: string;
28
+ identitiesUrl?: string;
29
+ environmentUrl?: string;
28
30
  environmentDataPollingManager?: EnvironmentDataPollingManager;
29
31
  environment: EnvironmentModel;
32
+ offlineMode: boolean;
33
+ offlineHandler?: BaseOfflineHandler;
34
+ identitiesWithOverridesByIdentifier?: Map<string, IdentityModel>;
30
35
  private cache?;
31
36
  private onEnvironmentChange?;
32
37
  private analyticsProcessor?;
@@ -45,6 +50,7 @@ export declare class Flagsmith {
45
50
  * const featureEnabledForIdentity = identityFlags.isFeatureEnabled("foo")
46
51
  *
47
52
  * @param {string} data.environmentKey: The environment key obtained from Flagsmith interface
53
+ * Required unless offlineMode is True.
48
54
  @param {string} data.apiUrl: Override the URL of the Flagsmith API to communicate with
49
55
  @param data.customHeaders: Additional headers to add to requests made to the
50
56
  Flagsmith API
@@ -58,11 +64,16 @@ export declare class Flagsmith {
58
64
  @param {boolean} data.enableAnalytics: if enabled, sends additional requests to the Flagsmith
59
65
  API to power flag analytics charts
60
66
  @param data.defaultFlagHandler: callable which will be used in the case where
61
- flags cannot be retrieved from the API or a non existent feature is
67
+ flags cannot be retrieved from the API or a non-existent feature is
62
68
  requested
63
69
  @param data.logger: an instance of the pino Logger class to use for logging
64
- */
65
- constructor(data: FlagsmithConfig);
70
+ @param {boolean} data.offlineMode: sets the client into offline mode. Relies on offlineHandler for
71
+ evaluating flags.
72
+ @param {BaseOfflineHandler} data.offlineHandler: provide a handler for offline logic. Used to get environment
73
+ document from another source when in offlineMode. Works in place of
74
+ defaultFlagHandler if offlineMode is not set and using remote evaluation.
75
+ */
76
+ constructor(data?: FlagsmithConfig);
66
77
  /**
67
78
  * Get all the default for flags for the current environment.
68
79
  *
@@ -114,6 +125,6 @@ export declare class Flagsmith {
114
125
  private getIdentityFlagsFromDocument;
115
126
  private getEnvironmentFlagsFromApi;
116
127
  private getIdentityFlagsFromApi;
117
- private buildIdentityModel;
128
+ private getIdentityModel;
118
129
  }
119
130
  export default Flagsmith;
@@ -105,6 +105,7 @@ var Flagsmith = /** @class */ (function () {
105
105
  * const featureEnabledForIdentity = identityFlags.isFeatureEnabled("foo")
106
106
  *
107
107
  * @param {string} data.environmentKey: The environment key obtained from Flagsmith interface
108
+ * Required unless offlineMode is True.
108
109
  @param {string} data.apiUrl: Override the URL of the Flagsmith API to communicate with
109
110
  @param data.customHeaders: Additional headers to add to requests made to the
110
111
  Flagsmith API
@@ -118,32 +119,54 @@ var Flagsmith = /** @class */ (function () {
118
119
  @param {boolean} data.enableAnalytics: if enabled, sends additional requests to the Flagsmith
119
120
  API to power flag analytics charts
120
121
  @param data.defaultFlagHandler: callable which will be used in the case where
121
- flags cannot be retrieved from the API or a non existent feature is
122
+ flags cannot be retrieved from the API or a non-existent feature is
122
123
  requested
123
124
  @param data.logger: an instance of the pino Logger class to use for logging
124
- */
125
+ @param {boolean} data.offlineMode: sets the client into offline mode. Relies on offlineHandler for
126
+ evaluating flags.
127
+ @param {BaseOfflineHandler} data.offlineHandler: provide a handler for offline logic. Used to get environment
128
+ document from another source when in offlineMode. Works in place of
129
+ defaultFlagHandler if offlineMode is not set and using remote evaluation.
130
+ */
125
131
  function Flagsmith(data) {
132
+ // if (!data.offlineMode && !data.environmentKey) {
133
+ // throw new Error('ValueError: environmentKey is required.');
134
+ // }
135
+ if (data === void 0) { data = {}; }
126
136
  var _a;
127
- this.apiUrl = DEFAULT_API_URL;
137
+ this.environmentKey = undefined;
138
+ this.apiUrl = undefined;
128
139
  this.enableLocalEvaluation = false;
129
140
  this.environmentRefreshIntervalSeconds = 60;
130
141
  this.enableAnalytics = false;
142
+ this.offlineMode = false;
143
+ this.offlineHandler = undefined;
131
144
  this.agent = data.agent;
132
145
  this.environmentKey = data.environmentKey;
133
146
  this.apiUrl = data.apiUrl || this.apiUrl;
134
147
  this.customHeaders = data.customHeaders;
135
- this.requestTimeoutMs = 1000 * ((_a = data.requestTimeoutSeconds) !== null && _a !== void 0 ? _a : DEFAULT_REQUEST_TIMEOUT_SECONDS);
148
+ this.requestTimeoutMs =
149
+ 1000 * ((_a = data.requestTimeoutSeconds) !== null && _a !== void 0 ? _a : DEFAULT_REQUEST_TIMEOUT_SECONDS);
136
150
  this.enableLocalEvaluation = data.enableLocalEvaluation;
137
151
  this.environmentRefreshIntervalSeconds =
138
152
  data.environmentRefreshIntervalSeconds || this.environmentRefreshIntervalSeconds;
139
153
  this.retries = data.retries;
140
154
  this.enableAnalytics = data.enableAnalytics || false;
141
155
  this.defaultFlagHandler = data.defaultFlagHandler;
142
- this.environmentFlagsUrl = "".concat(this.apiUrl, "flags/");
143
- this.identitiesUrl = "".concat(this.apiUrl, "identities/");
144
- this.environmentUrl = "".concat(this.apiUrl, "environment-document/");
145
156
  this.onEnvironmentChange = data.onEnvironmentChange;
146
157
  this.logger = data.logger || (0, pino_1.default)();
158
+ this.offlineMode = data.offlineMode || false;
159
+ this.offlineHandler = data.offlineHandler;
160
+ // argument validation
161
+ if (this.offlineMode && !this.offlineHandler) {
162
+ throw new Error('ValueError: offlineHandler must be provided to use offline mode.');
163
+ }
164
+ else if (this.defaultFlagHandler && this.offlineHandler) {
165
+ throw new Error('ValueError: Cannot use both defaultFlagHandler and offlineHandler.');
166
+ }
167
+ if (this.offlineHandler) {
168
+ this.environment = this.offlineHandler.getEnvironment();
169
+ }
147
170
  if (!!data.cache) {
148
171
  var missingMethods = ['has', 'get', 'set'].filter(function (method) { return data.cache && !data.cache[method]; });
149
172
  if (missingMethods.length > 0) {
@@ -151,22 +174,32 @@ var Flagsmith = /** @class */ (function () {
151
174
  }
152
175
  this.cache = data.cache;
153
176
  }
154
- if (this.enableLocalEvaluation) {
155
- if (!this.environmentKey.startsWith('ser.')) {
156
- console.error('In order to use local evaluation, please generate a server key in the environment settings page.');
177
+ if (!this.offlineMode) {
178
+ if (!this.environmentKey) {
179
+ throw new Error('ValueError: environmentKey is required.');
157
180
  }
158
- this.environmentDataPollingManager = new polling_manager_1.EnvironmentDataPollingManager(this, this.environmentRefreshIntervalSeconds);
159
- this.environmentDataPollingManager.start();
160
- this.updateEnvironment();
181
+ var apiUrl = data.apiUrl || DEFAULT_API_URL;
182
+ this.apiUrl = apiUrl.endsWith('/') ? apiUrl : "".concat(apiUrl, "/");
183
+ this.environmentFlagsUrl = "".concat(this.apiUrl, "flags/");
184
+ this.identitiesUrl = "".concat(this.apiUrl, "identities/");
185
+ this.environmentUrl = "".concat(this.apiUrl, "environment-document/");
186
+ if (this.enableLocalEvaluation) {
187
+ if (!this.environmentKey.startsWith('ser.')) {
188
+ console.error('In order to use local evaluation, please generate a server key in the environment settings page.');
189
+ }
190
+ this.environmentDataPollingManager = new polling_manager_1.EnvironmentDataPollingManager(this, this.environmentRefreshIntervalSeconds);
191
+ this.environmentDataPollingManager.start();
192
+ this.updateEnvironment();
193
+ }
194
+ this.analyticsProcessor = data.enableAnalytics
195
+ ? new analytics_1.AnalyticsProcessor({
196
+ environmentKey: this.environmentKey,
197
+ baseApiUrl: this.apiUrl,
198
+ requestTimeoutMs: this.requestTimeoutMs,
199
+ logger: this.logger
200
+ })
201
+ : undefined;
161
202
  }
162
- this.analyticsProcessor = data.enableAnalytics
163
- ? new analytics_1.AnalyticsProcessor({
164
- environmentKey: this.environmentKey,
165
- baseApiUrl: this.apiUrl,
166
- requestTimeoutMs: this.requestTimeoutMs,
167
- logger: this.logger
168
- })
169
- : undefined;
170
203
  }
171
204
  /**
172
205
  * Get all the default for flags for the current environment.
@@ -191,7 +224,7 @@ var Flagsmith = /** @class */ (function () {
191
224
  if (!!cachedItem) {
192
225
  return [2 /*return*/, cachedItem];
193
226
  }
194
- if (this.enableLocalEvaluation) {
227
+ if (this.enableLocalEvaluation && !this.offlineMode) {
195
228
  return [2 /*return*/, new Promise(function (resolve, reject) {
196
229
  return _this.environmentPromise.then(function () {
197
230
  resolve(_this.getEnvironmentFlagsFromDocument());
@@ -225,7 +258,7 @@ var Flagsmith = /** @class */ (function () {
225
258
  switch (_b.label) {
226
259
  case 0:
227
260
  if (!identifier) {
228
- throw new Error("`identifier` argument is missing or invalid.");
261
+ throw new Error('`identifier` argument is missing or invalid.');
229
262
  }
230
263
  _a = !!this.cache;
231
264
  if (!_a) return [3 /*break*/, 2];
@@ -246,6 +279,9 @@ var Flagsmith = /** @class */ (function () {
246
279
  }).catch(function (e) { return reject(e); });
247
280
  })];
248
281
  }
282
+ if (this.offlineMode) {
283
+ return [2 /*return*/, this.getIdentityFlagsFromDocument(identifier, traits || {})];
284
+ }
249
285
  return [2 /*return*/, this.getIdentityFlagsFromApi(identifier, traits)];
250
286
  }
251
287
  });
@@ -265,13 +301,13 @@ var Flagsmith = /** @class */ (function () {
265
301
  Flagsmith.prototype.getIdentitySegments = function (identifier, traits) {
266
302
  var _this = this;
267
303
  if (!identifier) {
268
- throw new Error("`identifier` argument is missing or invalid.");
304
+ throw new Error('`identifier` argument is missing or invalid.');
269
305
  }
270
306
  traits = traits || {};
271
307
  if (this.enableLocalEvaluation) {
272
308
  return new Promise(function (resolve, reject) {
273
309
  return _this.environmentPromise.then(function () {
274
- var identityModel = _this.buildIdentityModel(identifier, Object.keys(traits || {}).map(function (key) { return ({
310
+ var identityModel = _this.getIdentityModel(identifier, Object.keys(traits || {}).map(function (key) { return ({
275
311
  key: key,
276
312
  value: traits === null || traits === void 0 ? void 0 : traits[key]
277
313
  }); }));
@@ -289,13 +325,14 @@ var Flagsmith = /** @class */ (function () {
289
325
  * You only need to call this if you wish to bypass environmentRefreshIntervalSeconds.
290
326
  */
291
327
  Flagsmith.prototype.updateEnvironment = function () {
328
+ var _a;
292
329
  return __awaiter(this, void 0, void 0, function () {
293
- var request, _a, e_1;
330
+ var request, _b, e_1;
294
331
  var _this = this;
295
- return __generator(this, function (_b) {
296
- switch (_b.label) {
332
+ return __generator(this, function (_c) {
333
+ switch (_c.label) {
297
334
  case 0:
298
- _b.trys.push([0, 5, , 6]);
335
+ _c.trys.push([0, 5, , 6]);
299
336
  request = this.getEnvironmentFromApi();
300
337
  if (!!this.environmentPromise) return [3 /*break*/, 2];
301
338
  this.environmentPromise = request.then(function (res) {
@@ -303,21 +340,24 @@ var Flagsmith = /** @class */ (function () {
303
340
  });
304
341
  return [4 /*yield*/, this.environmentPromise];
305
342
  case 1:
306
- _b.sent();
343
+ _c.sent();
307
344
  return [3 /*break*/, 4];
308
345
  case 2:
309
- _a = this;
346
+ _b = this;
310
347
  return [4 /*yield*/, request];
311
348
  case 3:
312
- _a.environment = _b.sent();
313
- _b.label = 4;
349
+ _b.environment = _c.sent();
350
+ _c.label = 4;
314
351
  case 4:
352
+ if ((_a = this.environment.identityOverrides) === null || _a === void 0 ? void 0 : _a.length) {
353
+ this.identitiesWithOverridesByIdentifier = new Map(this.environment.identityOverrides.map(function (identity) { return [identity.identifier, identity]; }));
354
+ }
315
355
  if (this.onEnvironmentChange) {
316
356
  this.onEnvironmentChange(null, this.environment);
317
357
  }
318
358
  return [3 /*break*/, 6];
319
359
  case 5:
320
- e_1 = _b.sent();
360
+ e_1 = _c.sent();
321
361
  if (this.onEnvironmentChange) {
322
362
  this.onEnvironmentChange(e_1, this.environment);
323
363
  }
@@ -383,7 +423,11 @@ var Flagsmith = /** @class */ (function () {
383
423
  var environment_data;
384
424
  return __generator(this, function (_a) {
385
425
  switch (_a.label) {
386
- case 0: return [4 /*yield*/, this.getJSONResponse(this.environmentUrl, 'GET')];
426
+ case 0:
427
+ if (!this.environmentUrl) {
428
+ throw new Error('`apiUrl` argument is missing or invalid.');
429
+ }
430
+ return [4 /*yield*/, this.getJSONResponse(this.environmentUrl, 'GET')];
387
431
  case 1:
388
432
  environment_data = _a.sent();
389
433
  return [2 /*return*/, (0, util_1.buildEnvironmentModel)(environment_data)];
@@ -420,7 +464,7 @@ var Flagsmith = /** @class */ (function () {
420
464
  return __generator(this, function (_a) {
421
465
  switch (_a.label) {
422
466
  case 0:
423
- identityModel = this.buildIdentityModel(identifier, Object.keys(traits).map(function (key) { return ({
467
+ identityModel = this.getIdentityModel(identifier, Object.keys(traits).map(function (key) { return ({
424
468
  key: key,
425
469
  value: traits[key]
426
470
  }); }));
@@ -449,25 +493,33 @@ var Flagsmith = /** @class */ (function () {
449
493
  return __generator(this, function (_a) {
450
494
  switch (_a.label) {
451
495
  case 0:
452
- _a.trys.push([0, 4, , 5]);
453
- return [4 /*yield*/, this.getJSONResponse(this.environmentFlagsUrl, 'GET')];
496
+ if (!this.environmentFlagsUrl) {
497
+ throw new Error('`apiUrl` argument is missing or invalid.');
498
+ }
499
+ _a.label = 1;
454
500
  case 1:
501
+ _a.trys.push([1, 5, , 6]);
502
+ return [4 /*yield*/, this.getJSONResponse(this.environmentFlagsUrl, 'GET')];
503
+ case 2:
455
504
  apiFlags = _a.sent();
456
505
  flags = models_3.Flags.fromAPIFlags({
457
506
  apiFlags: apiFlags,
458
507
  analyticsProcessor: this.analyticsProcessor,
459
508
  defaultFlagHandler: this.defaultFlagHandler
460
509
  });
461
- if (!!!this.cache) return [3 /*break*/, 3];
510
+ if (!!!this.cache) return [3 /*break*/, 4];
462
511
  // @ts-ignore node-cache types are incorrect, ttl should be optional
463
512
  return [4 /*yield*/, this.cache.set('flags', flags)];
464
- case 2:
513
+ case 3:
465
514
  // @ts-ignore node-cache types are incorrect, ttl should be optional
466
515
  _a.sent();
467
- _a.label = 3;
468
- case 3: return [2 /*return*/, flags];
469
- case 4:
516
+ _a.label = 4;
517
+ case 4: return [2 /*return*/, flags];
518
+ case 5:
470
519
  e_3 = _a.sent();
520
+ if (this.offlineHandler) {
521
+ return [2 /*return*/, this.getEnvironmentFlagsFromDocument()];
522
+ }
471
523
  if (this.defaultFlagHandler) {
472
524
  return [2 /*return*/, new models_3.Flags({
473
525
  flags: {},
@@ -475,7 +527,7 @@ var Flagsmith = /** @class */ (function () {
475
527
  })];
476
528
  }
477
529
  throw e_3;
478
- case 5: return [2 /*return*/];
530
+ case 6: return [2 /*return*/];
479
531
  }
480
532
  });
481
533
  });
@@ -486,26 +538,34 @@ var Flagsmith = /** @class */ (function () {
486
538
  return __generator(this, function (_a) {
487
539
  switch (_a.label) {
488
540
  case 0:
489
- _a.trys.push([0, 4, , 5]);
541
+ if (!this.identitiesUrl) {
542
+ throw new Error('`apiUrl` argument is missing or invalid.');
543
+ }
544
+ _a.label = 1;
545
+ case 1:
546
+ _a.trys.push([1, 5, , 6]);
490
547
  data = (0, utils_1.generateIdentitiesData)(identifier, traits);
491
548
  return [4 /*yield*/, this.getJSONResponse(this.identitiesUrl, 'POST', data)];
492
- case 1:
549
+ case 2:
493
550
  jsonResponse = _a.sent();
494
551
  flags = models_3.Flags.fromAPIFlags({
495
552
  apiFlags: jsonResponse['flags'],
496
553
  analyticsProcessor: this.analyticsProcessor,
497
554
  defaultFlagHandler: this.defaultFlagHandler
498
555
  });
499
- if (!!!this.cache) return [3 /*break*/, 3];
556
+ if (!!!this.cache) return [3 /*break*/, 4];
500
557
  // @ts-ignore node-cache types are incorrect, ttl should be optional
501
558
  return [4 /*yield*/, this.cache.set("flags-".concat(identifier), flags)];
502
- case 2:
559
+ case 3:
503
560
  // @ts-ignore node-cache types are incorrect, ttl should be optional
504
561
  _a.sent();
505
- _a.label = 3;
506
- case 3: return [2 /*return*/, flags];
507
- case 4:
562
+ _a.label = 4;
563
+ case 4: return [2 /*return*/, flags];
564
+ case 5:
508
565
  e_4 = _a.sent();
566
+ if (this.offlineHandler) {
567
+ return [2 /*return*/, this.getIdentityFlagsFromDocument(identifier, traits)];
568
+ }
509
569
  if (this.defaultFlagHandler) {
510
570
  return [2 /*return*/, new models_3.Flags({
511
571
  flags: {},
@@ -513,13 +573,19 @@ var Flagsmith = /** @class */ (function () {
513
573
  })];
514
574
  }
515
575
  throw e_4;
516
- case 5: return [2 /*return*/];
576
+ case 6: return [2 /*return*/];
517
577
  }
518
578
  });
519
579
  });
520
580
  };
521
- Flagsmith.prototype.buildIdentityModel = function (identifier, traits) {
581
+ Flagsmith.prototype.getIdentityModel = function (identifier, traits) {
582
+ var _a;
522
583
  var traitModels = traits.map(function (trait) { return new models_2.TraitModel(trait.key, trait.value); });
584
+ var identityWithOverrides = (_a = this.identitiesWithOverridesByIdentifier) === null || _a === void 0 ? void 0 : _a.get(identifier);
585
+ if (identityWithOverrides) {
586
+ identityWithOverrides.updateTraits(traitModels);
587
+ return identityWithOverrides;
588
+ }
523
589
  return new models_1.IdentityModel('0', traitModels, [], this.environment.apiKey, identifier);
524
590
  };
525
591
  return Flagsmith;
@@ -0,0 +1,9 @@
1
+ import { EnvironmentModel } from '../flagsmith-engine/environments/models';
2
+ export declare class BaseOfflineHandler {
3
+ getEnvironment(): EnvironmentModel;
4
+ }
5
+ export declare class LocalFileHandler extends BaseOfflineHandler {
6
+ environment: EnvironmentModel;
7
+ constructor(environment_document_path: string);
8
+ getEnvironment(): EnvironmentModel;
9
+ }