flagsmith-nodejs 5.1.0 → 6.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.
@@ -6,7 +6,7 @@ import { ANALYTICS_ENDPOINT, AnalyticsProcessor } from './analytics.js';
6
6
  import { FlagsmithAPIError } from './errors.js';
7
7
  import { Flags } from './models.js';
8
8
  import { EnvironmentDataPollingManager } from './polling_manager.js';
9
- import { generateIdentitiesData, retryFetch } from './utils.js';
9
+ import { Deferred, generateIdentitiesData, retryFetch } from './utils.js';
10
10
  import { getIdentitySegments } from '../flagsmith-engine/segments/evaluators.js';
11
11
  import { pino } from 'pino';
12
12
  export { AnalyticsProcessor } from './analytics.js';
@@ -15,6 +15,30 @@ export { DefaultFlag, Flags } from './models.js';
15
15
  export { EnvironmentDataPollingManager } from './polling_manager.js';
16
16
  const DEFAULT_API_URL = 'https://edge.api.flagsmith.com/api/v1/';
17
17
  const DEFAULT_REQUEST_TIMEOUT_SECONDS = 10;
18
+ /**
19
+ * A client for evaluating Flagsmith feature flags.
20
+ *
21
+ * Flags are evaluated remotely by the Flagsmith API over HTTP by default.
22
+ * To evaluate flags locally, create the client using {@link FlagsmithConfig.enableLocalEvaluation} and a server-side SDK key.
23
+ *
24
+ * @example
25
+ * import { Flagsmith, Flags, DefaultFlag } from 'flagsmith-nodejs'
26
+ *
27
+ * const flagsmith = new Flagsmith({
28
+ * environmentKey: 'your_sdk_key',
29
+ * defaultFlagHandler: (flagKey: string) => { new DefaultFlag(...) },
30
+ * });
31
+ *
32
+ * // Fetch the current environment flags
33
+ * const environmentFlags: Flags = flagsmith.getEnvironmentFlags()
34
+ * const isFooEnabled: boolean = environmentFlags.isFeatureEnabled('foo')
35
+ *
36
+ * // Evaluate flags for any identity
37
+ * const identityFlags: Flags = flagsmith.getIdentityFlags('my_user_123', {'vip': true})
38
+ * const bannerVariation: string = identityFlags.getFeatureValue('banner_flag')
39
+ *
40
+ * @see FlagsmithConfig
41
+ */
18
42
  export class Flagsmith {
19
43
  environmentKey = undefined;
20
44
  apiUrl = undefined;
@@ -40,61 +64,30 @@ export class Flagsmith {
40
64
  analyticsProcessor;
41
65
  logger;
42
66
  customFetch;
67
+ requestRetryDelayMilliseconds;
43
68
  /**
44
- * A Flagsmith client.
69
+ * Creates a new {@link Flagsmith} client.
45
70
  *
46
- * Provides an interface for interacting with the Flagsmith http API.
47
- * Basic Usage::
48
- *
49
- * import flagsmith from Flagsmith
50
- * const flagsmith = new Flagsmith({environmentKey: '<your API key>'});
51
- * const environmentFlags = flagsmith.getEnvironmentFlags();
52
- * const featureEnabled = environmentFlags.isFeatureEnabled('foo');
53
- * const identityFlags = flagsmith.getIdentityFlags('identifier', {'foo': 'bar'});
54
- * const featureEnabledForIdentity = identityFlags.isFeatureEnabled("foo")
55
- *
56
- * @param {string} data.environmentKey: The environment key obtained from Flagsmith interface
57
- * Required unless offlineMode is True.
58
- @param {string} data.apiUrl: Override the URL of the Flagsmith API to communicate with
59
- @param data.customHeaders: Additional headers to add to requests made to the
60
- Flagsmith API
61
- @param {number} data.requestTimeoutSeconds: Number of seconds to wait for a request to
62
- complete before terminating the request
63
- @param {boolean} data.enableLocalEvaluation: Enables local evaluation of flags
64
- @param {number} data.environmentRefreshIntervalSeconds: If using local evaluation,
65
- specify the interval period between refreshes of local environment data
66
- @param {number} data.retries: a urllib3.Retry object to use on all http requests to the
67
- Flagsmith API
68
- @param {boolean} data.enableAnalytics: if enabled, sends additional requests to the Flagsmith
69
- API to power flag analytics charts
70
- @param data.defaultFlagHandler: callable which will be used in the case where
71
- flags cannot be retrieved from the API or a non-existent feature is
72
- requested
73
- @param data.logger: an instance of the pino Logger class to use for logging
74
- @param {boolean} data.offlineMode: sets the client into offline mode. Relies on offlineHandler for
75
- evaluating flags.
76
- @param {BaseOfflineHandler} data.offlineHandler: provide a handler for offline logic. Used to get environment
77
- document from another source when in offlineMode. Works in place of
78
- defaultFlagHandler if offlineMode is not set and using remote evaluation.
79
- */
80
- constructor(data = {}) {
81
- // if (!data.offlineMode && !data.environmentKey) {
82
- // throw new Error('ValueError: environmentKey is required.');
83
- // }
71
+ * If using local evaluation, the environment will be fetched lazily when needed by any method. Polling the
72
+ * environment for updates will start after {@link environmentRefreshIntervalSeconds} once the client is created.
73
+ * @param data The {@link FlagsmithConfig} options for this client.
74
+ */
75
+ constructor(data) {
84
76
  this.agent = data.agent;
85
77
  this.customFetch = data.fetch ?? fetch;
86
78
  this.environmentKey = data.environmentKey;
87
- this.apiUrl = data.apiUrl || this.apiUrl;
79
+ this.apiUrl = data.apiUrl || DEFAULT_API_URL;
88
80
  this.customHeaders = data.customHeaders;
89
81
  this.requestTimeoutMs =
90
82
  1000 * (data.requestTimeoutSeconds ?? DEFAULT_REQUEST_TIMEOUT_SECONDS);
83
+ this.requestRetryDelayMilliseconds = data.requestRetryDelayMilliseconds ?? 1000;
91
84
  this.enableLocalEvaluation = data.enableLocalEvaluation;
92
85
  this.environmentRefreshIntervalSeconds =
93
86
  data.environmentRefreshIntervalSeconds || this.environmentRefreshIntervalSeconds;
94
87
  this.retries = data.retries;
95
88
  this.enableAnalytics = data.enableAnalytics || false;
96
89
  this.defaultFlagHandler = data.defaultFlagHandler;
97
- this.onEnvironmentChange = data.onEnvironmentChange;
90
+ this.onEnvironmentChange = (error, result) => data.onEnvironmentChange?.(error, result);
98
91
  this.logger = data.logger || pino();
99
92
  this.offlineMode = data.offlineMode || false;
100
93
  this.offlineHandler = data.offlineHandler;
@@ -105,9 +98,6 @@ export class Flagsmith {
105
98
  else if (this.defaultFlagHandler && this.offlineHandler) {
106
99
  throw new Error('ValueError: Cannot use both defaultFlagHandler and offlineHandler.');
107
100
  }
108
- if (this.offlineHandler) {
109
- this.environment = this.offlineHandler.getEnvironment();
110
- }
111
101
  if (!!data.cache) {
112
102
  this.cache = data.cache;
113
103
  }
@@ -123,11 +113,12 @@ export class Flagsmith {
123
113
  this.environmentUrl = `${this.apiUrl}environment-document/`;
124
114
  if (this.enableLocalEvaluation) {
125
115
  if (!this.environmentKey.startsWith('ser.')) {
126
- console.error('In order to use local evaluation, please generate a server key in the environment settings page.');
116
+ throw new Error('Using local evaluation requires a server-side environment key');
117
+ }
118
+ if (this.environmentRefreshIntervalSeconds > 0) {
119
+ this.environmentDataPollingManager = new EnvironmentDataPollingManager(this, this.environmentRefreshIntervalSeconds, this.logger);
120
+ this.environmentDataPollingManager.start();
127
121
  }
128
- this.environmentDataPollingManager = new EnvironmentDataPollingManager(this, this.environmentRefreshIntervalSeconds);
129
- this.environmentDataPollingManager.start();
130
- this.updateEnvironment();
131
122
  }
132
123
  if (data.enableAnalytics) {
133
124
  this.analyticsProcessor = new AnalyticsProcessor({
@@ -149,15 +140,22 @@ export class Flagsmith {
149
140
  if (!!cachedItem) {
150
141
  return cachedItem;
151
142
  }
152
- if (this.enableLocalEvaluation && !this.offlineMode) {
153
- return new Promise((resolve, reject) => this.environmentPromise.then(() => {
154
- resolve(this.getEnvironmentFlagsFromDocument());
155
- }).catch(e => reject(e)));
143
+ try {
144
+ if (this.enableLocalEvaluation || this.offlineMode) {
145
+ return await this.getEnvironmentFlagsFromDocument();
146
+ }
147
+ return await this.getEnvironmentFlagsFromApi();
156
148
  }
157
- if (this.environment) {
158
- return this.getEnvironmentFlagsFromDocument();
149
+ catch (error) {
150
+ if (!this.defaultFlagHandler) {
151
+ throw new Error('getEnvironmentFlags failed and no default flag handler was provided', { cause: error });
152
+ }
153
+ this.logger.error(error, 'getEnvironmentFlags failed');
154
+ return new Flags({
155
+ flags: {},
156
+ defaultFlagHandler: this.defaultFlagHandler
157
+ });
159
158
  }
160
- return this.getEnvironmentFlagsFromApi();
161
159
  }
162
160
  /**
163
161
  * Get all the flags for the current environment for a given identity. Will also
@@ -179,15 +177,22 @@ export class Flagsmith {
179
177
  return cachedItem;
180
178
  }
181
179
  traits = traits || {};
182
- if (this.enableLocalEvaluation) {
183
- return new Promise((resolve, reject) => this.environmentPromise.then(() => {
184
- resolve(this.getIdentityFlagsFromDocument(identifier, traits || {}));
185
- }).catch(e => reject(e)));
180
+ try {
181
+ if (this.enableLocalEvaluation || this.offlineMode) {
182
+ return await this.getIdentityFlagsFromDocument(identifier, traits || {});
183
+ }
184
+ return await this.getIdentityFlagsFromApi(identifier, traits, transient);
186
185
  }
187
- if (this.offlineMode) {
188
- return this.getIdentityFlagsFromDocument(identifier, traits || {});
186
+ catch (error) {
187
+ if (!this.defaultFlagHandler) {
188
+ throw new Error('getIdentityFlags failed and no default flag handler was provided', { cause: error });
189
+ }
190
+ this.logger.error(error, 'getIdentityFlags failed');
191
+ return new Flags({
192
+ flags: {},
193
+ defaultFlagHandler: this.defaultFlagHandler
194
+ });
189
195
  }
190
- return this.getIdentityFlagsFromApi(identifier, traits, transient);
191
196
  }
192
197
  /**
193
198
  * Get the segments for the current environment for a given identity. Will also
@@ -200,57 +205,59 @@ export class Flagsmith {
200
205
  Flagsmith, e.g. {"num_orders": 10}
201
206
  * @returns Segments that the given identity belongs to.
202
207
  */
203
- getIdentitySegments(identifier, traits) {
208
+ async getIdentitySegments(identifier, traits) {
204
209
  if (!identifier) {
205
210
  throw new Error('`identifier` argument is missing or invalid.');
206
211
  }
212
+ if (!this.enableLocalEvaluation) {
213
+ this.logger.error('This function is only permitted with local evaluation.');
214
+ return Promise.resolve([]);
215
+ }
207
216
  traits = traits || {};
208
- if (this.enableLocalEvaluation) {
209
- return new Promise((resolve, reject) => {
210
- return this.environmentPromise.then(() => {
211
- const identityModel = this.getIdentityModel(identifier, Object.keys(traits || {}).map(key => ({
212
- key,
213
- value: traits?.[key]
214
- })));
215
- const segments = getIdentitySegments(this.environment, identityModel);
216
- return resolve(segments);
217
- }).catch(e => reject(e));
218
- });
217
+ const environment = await this.getEnvironment();
218
+ const identityModel = this.getIdentityModel(environment, identifier, Object.keys(traits || {}).map(key => ({
219
+ key,
220
+ value: traits?.[key]
221
+ })));
222
+ return getIdentitySegments(environment, identityModel);
223
+ }
224
+ async fetchEnvironment() {
225
+ const deferred = new Deferred();
226
+ this.environmentPromise = deferred.promise;
227
+ try {
228
+ const environment = await this.getEnvironmentFromApi();
229
+ this.environment = environment;
230
+ if (environment.identityOverrides?.length) {
231
+ this.identitiesWithOverridesByIdentifier = new Map(environment.identityOverrides.map(identity => [identity.identifier, identity]));
232
+ }
233
+ deferred.resolve(environment);
234
+ return deferred.promise;
235
+ }
236
+ catch (error) {
237
+ deferred.reject(error);
238
+ return deferred.promise;
239
+ }
240
+ finally {
241
+ this.environmentPromise = undefined;
219
242
  }
220
- console.error('This function is only permitted with local evaluation.');
221
- return Promise.resolve([]);
222
243
  }
223
244
  /**
224
- * Updates the environment state for local flag evaluation.
225
- * Sets a local promise to prevent race conditions in getIdentityFlags / getIdentitySegments.
226
- * You only need to call this if you wish to bypass environmentRefreshIntervalSeconds.
245
+ * Fetch the latest environment state from the Flagsmith API to use for local flag evaluation.
246
+ *
247
+ * If the environment is currently being fetched, calling this method will not cause additional fetches.
227
248
  */
228
249
  async updateEnvironment() {
229
250
  try {
230
- const request = this.getEnvironmentFromApi();
231
- if (!this.environmentPromise) {
232
- this.environmentPromise = request.then(res => {
233
- this.environment = res;
234
- });
251
+ if (this.environmentPromise) {
235
252
  await this.environmentPromise;
253
+ return;
236
254
  }
237
- else {
238
- this.environment = await request;
239
- }
240
- if (this.environment.identityOverrides?.length) {
241
- this.identitiesWithOverridesByIdentifier = new Map(this.environment.identityOverrides.map(identity => [
242
- identity.identifier,
243
- identity
244
- ]));
245
- }
246
- if (this.onEnvironmentChange) {
247
- this.onEnvironmentChange(null, this.environment);
248
- }
255
+ const environment = await this.fetchEnvironment();
256
+ this.onEnvironmentChange(null, environment);
249
257
  }
250
258
  catch (e) {
251
- if (this.onEnvironmentChange) {
252
- this.onEnvironmentChange(e, this.environment);
253
- }
259
+ this.logger.error(e, 'updateEnvironment failed');
260
+ this.onEnvironmentChange(e);
254
261
  }
255
262
  }
256
263
  async close() {
@@ -271,7 +278,7 @@ export class Flagsmith {
271
278
  method: method,
272
279
  body: JSON.stringify(body),
273
280
  headers: headers
274
- }, this.retries, this.requestTimeoutMs, this.customFetch);
281
+ }, this.retries, this.requestTimeoutMs, this.requestRetryDelayMilliseconds, this.customFetch);
275
282
  if (data.status !== 200) {
276
283
  throw new FlagsmithAPIError(`Invalid request made to Flagsmith API. Response status code: ${data.status}`);
277
284
  }
@@ -281,6 +288,23 @@ export class Flagsmith {
281
288
  * This promise ensures that the environment is retrieved before attempting to locally evaluate.
282
289
  */
283
290
  environmentPromise;
291
+ /**
292
+ * Returns the current environment, fetching it from the API if needed.
293
+ *
294
+ * Calling this method concurrently while the environment is being fetched will not cause additional requests.
295
+ */
296
+ async getEnvironment() {
297
+ if (this.offlineHandler) {
298
+ return this.offlineHandler.getEnvironment();
299
+ }
300
+ if (this.environment) {
301
+ return this.environment;
302
+ }
303
+ if (!this.environmentPromise) {
304
+ this.environmentPromise = this.fetchEnvironment();
305
+ }
306
+ return this.environmentPromise;
307
+ }
284
308
  async getEnvironmentFromApi() {
285
309
  if (!this.environmentUrl) {
286
310
  throw new Error('`apiUrl` argument is missing or invalid.');
@@ -289,8 +313,9 @@ export class Flagsmith {
289
313
  return buildEnvironmentModel(environment_data);
290
314
  }
291
315
  async getEnvironmentFlagsFromDocument() {
316
+ const environment = await this.getEnvironment();
292
317
  const flags = Flags.fromFeatureStateModels({
293
- featureStates: getEnvironmentFeatureStates(this.environment),
318
+ featureStates: getEnvironmentFeatureStates(environment),
294
319
  analyticsProcessor: this.analyticsProcessor,
295
320
  defaultFlagHandler: this.defaultFlagHandler
296
321
  });
@@ -300,11 +325,12 @@ export class Flagsmith {
300
325
  return flags;
301
326
  }
302
327
  async getIdentityFlagsFromDocument(identifier, traits) {
303
- const identityModel = this.getIdentityModel(identifier, Object.keys(traits).map(key => ({
328
+ const environment = await this.getEnvironment();
329
+ const identityModel = this.getIdentityModel(environment, identifier, Object.keys(traits).map(key => ({
304
330
  key,
305
331
  value: traits[key]
306
332
  })));
307
- const featureStates = getIdentityFeatureStates(this.environment, identityModel);
333
+ const featureStates = getIdentityFeatureStates(environment, identityModel);
308
334
  const flags = Flags.fromFeatureStateModels({
309
335
  featureStates: featureStates,
310
336
  analyticsProcessor: this.analyticsProcessor,
@@ -320,69 +346,41 @@ export class Flagsmith {
320
346
  if (!this.environmentFlagsUrl) {
321
347
  throw new Error('`apiUrl` argument is missing or invalid.');
322
348
  }
323
- try {
324
- const apiFlags = await this.getJSONResponse(this.environmentFlagsUrl, 'GET');
325
- const flags = Flags.fromAPIFlags({
326
- apiFlags: apiFlags,
327
- analyticsProcessor: this.analyticsProcessor,
328
- defaultFlagHandler: this.defaultFlagHandler
329
- });
330
- if (!!this.cache) {
331
- await this.cache.set('flags', flags);
332
- }
333
- return flags;
334
- }
335
- catch (e) {
336
- if (this.offlineHandler) {
337
- return this.getEnvironmentFlagsFromDocument();
338
- }
339
- if (this.defaultFlagHandler) {
340
- return new Flags({
341
- flags: {},
342
- defaultFlagHandler: this.defaultFlagHandler
343
- });
344
- }
345
- throw e;
349
+ const apiFlags = await this.getJSONResponse(this.environmentFlagsUrl, 'GET');
350
+ const flags = Flags.fromAPIFlags({
351
+ apiFlags: apiFlags,
352
+ analyticsProcessor: this.analyticsProcessor,
353
+ defaultFlagHandler: this.defaultFlagHandler
354
+ });
355
+ if (!!this.cache) {
356
+ await this.cache.set('flags', flags);
346
357
  }
358
+ return flags;
347
359
  }
348
360
  async getIdentityFlagsFromApi(identifier, traits, transient = false) {
349
361
  if (!this.identitiesUrl) {
350
362
  throw new Error('`apiUrl` argument is missing or invalid.');
351
363
  }
352
- try {
353
- const data = generateIdentitiesData(identifier, traits, transient);
354
- const jsonResponse = await this.getJSONResponse(this.identitiesUrl, 'POST', data);
355
- const flags = Flags.fromAPIFlags({
356
- apiFlags: jsonResponse['flags'],
357
- analyticsProcessor: this.analyticsProcessor,
358
- defaultFlagHandler: this.defaultFlagHandler
359
- });
360
- if (!!this.cache) {
361
- await this.cache.set(`flags-${identifier}`, flags);
362
- }
363
- return flags;
364
- }
365
- catch (e) {
366
- if (this.offlineHandler) {
367
- return this.getIdentityFlagsFromDocument(identifier, traits);
368
- }
369
- if (this.defaultFlagHandler) {
370
- return new Flags({
371
- flags: {},
372
- defaultFlagHandler: this.defaultFlagHandler
373
- });
374
- }
375
- throw e;
364
+ const data = generateIdentitiesData(identifier, traits, transient);
365
+ const jsonResponse = await this.getJSONResponse(this.identitiesUrl, 'POST', data);
366
+ const flags = 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);
376
373
  }
374
+ return flags;
377
375
  }
378
- getIdentityModel(identifier, traits) {
376
+ getIdentityModel(environment, identifier, traits) {
379
377
  const traitModels = traits.map(trait => new TraitModel(trait.key, trait.value));
380
378
  let identityWithOverrides = this.identitiesWithOverridesByIdentifier?.get(identifier);
381
379
  if (identityWithOverrides) {
382
380
  identityWithOverrides.updateTraits(traitModels);
383
381
  return identityWithOverrides;
384
382
  }
385
- return new IdentityModel('0', traitModels, [], this.environment.apiKey, identifier);
383
+ return new IdentityModel('0', traitModels, [], environment.apiKey, identifier);
386
384
  }
387
385
  }
388
386
  export default Flagsmith;
@@ -1,9 +1,11 @@
1
1
  import Flagsmith from './index.js';
2
+ import { Logger } from 'pino';
2
3
  export declare class EnvironmentDataPollingManager {
3
4
  private interval?;
4
5
  private main;
5
6
  private refreshIntervalSeconds;
6
- constructor(main: Flagsmith, refreshIntervalSeconds: number);
7
+ private logger;
8
+ constructor(main: Flagsmith, refreshIntervalSeconds: number, logger: Logger);
7
9
  start(): void;
8
10
  stop(): void;
9
11
  }
@@ -2,16 +2,23 @@ export class EnvironmentDataPollingManager {
2
2
  interval;
3
3
  main;
4
4
  refreshIntervalSeconds;
5
- constructor(main, refreshIntervalSeconds) {
5
+ logger;
6
+ constructor(main, refreshIntervalSeconds, logger) {
6
7
  this.main = main;
7
8
  this.refreshIntervalSeconds = refreshIntervalSeconds;
9
+ this.logger = logger;
8
10
  }
9
11
  start() {
10
12
  const updateEnvironment = () => {
11
13
  if (this.interval)
12
14
  clearInterval(this.interval);
13
15
  this.interval = setInterval(async () => {
14
- await this.main.updateEnvironment();
16
+ try {
17
+ await this.main.updateEnvironment();
18
+ }
19
+ catch (error) {
20
+ this.logger.error(error, 'failed to poll environment');
21
+ }
15
22
  }, this.refreshIntervalSeconds * 1000);
16
23
  };
17
24
  updateEnvironment();
@@ -21,24 +21,92 @@ export interface FlagsmithCache {
21
21
  set(key: string, value: Flags): Promise<void>;
22
22
  }
23
23
  export type Fetch = typeof fetch;
24
+ /**
25
+ * The configuration options for a {@link Flagsmith} client.
26
+ */
24
27
  export interface FlagsmithConfig {
28
+ /**
29
+ * The environment's client-side or server-side key.
30
+ */
25
31
  environmentKey?: string;
32
+ /**
33
+ * The Flagsmith API URL. Set this if you are not using Flagsmith's public service, i.e. https://app.flagsmith.com.
34
+ *
35
+ * @default https://edge.api.flagsmith.com/api/v1/
36
+ */
26
37
  apiUrl?: string;
38
+ /**
39
+ * A custom {@link Dispatcher} to use when making HTTP requests.
40
+ */
27
41
  agent?: Dispatcher;
42
+ /**
43
+ * A custom {@link fetch} implementation to use when making HTTP requests.
44
+ */
28
45
  fetch?: Fetch;
29
- customHeaders?: {
30
- [key: string]: any;
31
- };
46
+ /**
47
+ * Custom headers to use in all HTTP requests.
48
+ */
49
+ customHeaders?: HeadersInit;
50
+ /**
51
+ * The network request timeout duration, in seconds.
52
+ *
53
+ * @default 10
54
+ */
32
55
  requestTimeoutSeconds?: number;
56
+ /**
57
+ * The amount of time, in milliseconds, to wait before retrying failed network requests.
58
+ */
59
+ requestRetryDelayMilliseconds?: number;
60
+ /**
61
+ * If enabled, flags are evaluated locally using the environment state cached in memory.
62
+ *
63
+ * The client will lazily fetch the environment from the Flagsmith API, and poll it every {@link environmentRefreshIntervalSeconds}.
64
+ */
33
65
  enableLocalEvaluation?: boolean;
66
+ /**
67
+ * The time, in seconds, to wait before refreshing the cached environment state.
68
+ * @default 60
69
+ */
34
70
  environmentRefreshIntervalSeconds?: number;
71
+ /**
72
+ * How many times to retry any failed network request before giving up.
73
+ * @default 3
74
+ */
35
75
  retries?: number;
76
+ /**
77
+ * If enabled, the client will keep track of any flags evaluated using {@link Flags.isFeatureEnabled},
78
+ * {@link Flags.getFeatureValue} or {@link Flags.getFlag}, and periodically flush this data to the Flagsmith API.
79
+ */
36
80
  enableAnalytics?: boolean;
37
- defaultFlagHandler?: (featureName: string) => DefaultFlag;
81
+ /**
82
+ * Used to return fallback values for flags when evaluation fails for any reason. If not provided and flag
83
+ * evaluation fails, an error will be thrown intsead.
84
+ *
85
+ * @param flagKey The key of the flag that failed to evaluate.
86
+ *
87
+ * @example
88
+ * // All flags disabled and with no value by default
89
+ * const defaultHandler = () => new DefaultFlag(undefined, false)
90
+ *
91
+ * // Enable only VIP flags by default
92
+ * const vipDefaultHandler = (key: string) => new Default(undefined, key.startsWith('vip_'))
93
+ */
94
+ defaultFlagHandler?: (flagKey: string) => DefaultFlag;
38
95
  cache?: FlagsmithCache;
39
- onEnvironmentChange?: (error: Error | null, result: EnvironmentModel) => void;
96
+ /**
97
+ * A callback function to invoke whenever the cached environment is updated.
98
+ * @param error The error that occurred when the environment state failed to update, if any.
99
+ * @param result The updated environment state, if no error was thrown.
100
+ */
101
+ onEnvironmentChange?: (error: Error | null, result?: EnvironmentModel) => void;
40
102
  logger?: Logger;
103
+ /**
104
+ * If enabled, the client will work offline and not make any network requests. Requires {@link offlineHandler}.
105
+ */
41
106
  offlineMode?: boolean;
107
+ /**
108
+ * If {@link offlineMode} is enabled, this handler is used to calculate the values of all flags.
109
+ */
42
110
  offlineHandler?: BaseOfflineHandler;
43
111
  }
44
112
  export interface ITraitConfig {
@@ -32,5 +32,28 @@ export declare function generateIdentitiesData(identifier: string, traits: Trait
32
32
  export declare const delay: (ms: number) => Promise<unknown>;
33
33
  export declare const retryFetch: (url: string, fetchOptions: RequestInit & {
34
34
  dispatcher?: Dispatcher;
35
- }, retries: number | undefined, timeoutMs: number | undefined, customFetch: Fetch) => Promise<Response>;
35
+ }, retries: number | undefined, timeoutMs: number | undefined, retryDelayMs: number | undefined, customFetch: Fetch) => Promise<Response>;
36
+ /**
37
+ * A deferred promise can be resolved or rejected outside its creation scope.
38
+ *
39
+ * @template T The type of the value that the deferred promise will resolve to.
40
+ *
41
+ * @example
42
+ * const deferred = new Deferred<string>()
43
+ *
44
+ * // Pass the promise somewhere
45
+ * performAsyncOperation(deferred.promise)
46
+ *
47
+ * // Resolve it when ready from anywhere
48
+ * deferred.resolve("Operation completed")
49
+ * deferred.failed("Error")
50
+ */
51
+ export declare class Deferred<T> {
52
+ readonly promise: Promise<T>;
53
+ private resolvePromise;
54
+ private rejectPromise;
55
+ constructor(initial?: T);
56
+ resolve(value: T | PromiseLike<T>): void;
57
+ reject(reason?: unknown): void;
58
+ }
36
59
  export {};