flagsmith-nodejs 5.1.1 → 6.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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 {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.retryFetch = exports.delay = exports.generateIdentitiesData = exports.isTraitConfig = void 0;
3
+ exports.Deferred = exports.retryFetch = exports.delay = exports.generateIdentitiesData = exports.isTraitConfig = void 0;
4
4
  function isTraitConfig(traitValue) {
5
5
  return !!traitValue && typeof traitValue == 'object' && traitValue.value !== undefined;
6
6
  }
@@ -39,25 +39,57 @@ exports.delay = delay;
39
39
  const retryFetch = (url,
40
40
  // built-in RequestInit type doesn't have dispatcher/agent
41
41
  fetchOptions, retries = 3, timeoutMs = 10, // set an overall timeout for this function
42
- customFetch) => {
43
- return new Promise((resolve, reject) => {
44
- const retryWrapper = (n) => {
45
- customFetch(url, {
42
+ retryDelayMs = 1000, customFetch) => {
43
+ const retryWrapper = async (n) => {
44
+ try {
45
+ return await customFetch(url, {
46
46
  ...fetchOptions,
47
47
  signal: AbortSignal.timeout(timeoutMs)
48
- })
49
- .then(res => resolve(res))
50
- .catch(async (err) => {
51
- if (n > 0) {
52
- await (0, exports.delay)(1000);
53
- retryWrapper(--n);
54
- }
55
- else {
56
- reject(err);
57
- }
58
48
  });
59
- };
60
- retryWrapper(retries);
61
- });
49
+ }
50
+ catch (e) {
51
+ if (n > 0) {
52
+ await (0, exports.delay)(retryDelayMs);
53
+ return await retryWrapper(n - 1);
54
+ }
55
+ else {
56
+ throw e;
57
+ }
58
+ }
59
+ };
60
+ return retryWrapper(retries);
62
61
  };
63
62
  exports.retryFetch = retryFetch;
63
+ /**
64
+ * A deferred promise can be resolved or rejected outside its creation scope.
65
+ *
66
+ * @template T The type of the value that the deferred promise will resolve to.
67
+ *
68
+ * @example
69
+ * const deferred = new Deferred<string>()
70
+ *
71
+ * // Pass the promise somewhere
72
+ * performAsyncOperation(deferred.promise)
73
+ *
74
+ * // Resolve it when ready from anywhere
75
+ * deferred.resolve("Operation completed")
76
+ * deferred.failed("Error")
77
+ */
78
+ class Deferred {
79
+ promise;
80
+ resolvePromise;
81
+ rejectPromise;
82
+ constructor(initial) {
83
+ this.promise = new Promise((resolve, reject) => {
84
+ this.resolvePromise = resolve;
85
+ this.rejectPromise = reject;
86
+ });
87
+ }
88
+ resolve(value) {
89
+ this.resolvePromise(value);
90
+ }
91
+ reject(reason) {
92
+ this.rejectPromise(reason);
93
+ }
94
+ }
95
+ exports.Deferred = Deferred;
@@ -44,7 +44,7 @@ export declare class AnalyticsProcessor {
44
44
  /**
45
45
  * Track a single evaluation event for a feature.
46
46
  *
47
- * This method is called whenever {@link Flags.isFeatureEnabled}, {@link Flags.getFeatureValue} or {@link Flags.getFlag} are called.
47
+ * @see FlagsmithConfig.enableAnalytics
48
48
  */
49
49
  trackFeature(featureName: string): void;
50
50
  }
@@ -64,7 +64,7 @@ export class AnalyticsProcessor {
64
64
  /**
65
65
  * Track a single evaluation event for a feature.
66
66
  *
67
- * This method is called whenever {@link Flags.isFeatureEnabled}, {@link Flags.getFeatureValue} or {@link Flags.getFlag} are called.
67
+ * @see FlagsmithConfig.enableAnalytics
68
68
  */
69
69
  trackFeature(featureName) {
70
70
  this.analyticsData[featureName] = (this.analyticsData[featureName] || 0) + 1;
@@ -11,6 +11,30 @@ export { FlagsmithAPIError, FlagsmithClientError } from './errors.js';
11
11
  export { DefaultFlag, Flags } from './models.js';
12
12
  export { EnvironmentDataPollingManager } from './polling_manager.js';
13
13
  export { FlagsmithCache, FlagsmithConfig } from './types.js';
14
+ /**
15
+ * A client for evaluating Flagsmith feature flags.
16
+ *
17
+ * Flags are evaluated remotely by the Flagsmith API over HTTP by default.
18
+ * To evaluate flags locally, create the client using {@link FlagsmithConfig.enableLocalEvaluation} and a server-side SDK key.
19
+ *
20
+ * @example
21
+ * import { Flagsmith, Flags, DefaultFlag } from 'flagsmith-nodejs'
22
+ *
23
+ * const flagsmith = new Flagsmith({
24
+ * environmentKey: 'your_sdk_key',
25
+ * defaultFlagHandler: (flagKey: string) => { new DefaultFlag(...) },
26
+ * });
27
+ *
28
+ * // Fetch the current environment flags
29
+ * const environmentFlags: Flags = flagsmith.getEnvironmentFlags()
30
+ * const isFooEnabled: boolean = environmentFlags.isFeatureEnabled('foo')
31
+ *
32
+ * // Evaluate flags for any identity
33
+ * const identityFlags: Flags = flagsmith.getIdentityFlags('my_user_123', {'vip': true})
34
+ * const bannerVariation: string = identityFlags.getFeatureValue('banner_flag')
35
+ *
36
+ * @see FlagsmithConfig
37
+ */
14
38
  export declare class Flagsmith {
15
39
  environmentKey?: string;
16
40
  apiUrl?: string;
@@ -29,53 +53,24 @@ export declare class Flagsmith {
29
53
  identitiesUrl?: string;
30
54
  environmentUrl?: string;
31
55
  environmentDataPollingManager?: EnvironmentDataPollingManager;
32
- environment: EnvironmentModel;
56
+ private environment?;
33
57
  offlineMode: boolean;
34
58
  offlineHandler?: BaseOfflineHandler;
35
59
  identitiesWithOverridesByIdentifier?: Map<string, IdentityModel>;
36
60
  private cache?;
37
- private onEnvironmentChange?;
61
+ private onEnvironmentChange;
38
62
  private analyticsProcessor?;
39
63
  private logger;
40
64
  private customFetch;
65
+ private readonly requestRetryDelayMilliseconds;
41
66
  /**
42
- * A Flagsmith client.
67
+ * Creates a new {@link Flagsmith} client.
43
68
  *
44
- * Provides an interface for interacting with the Flagsmith http API.
45
- * Basic Usage::
46
- *
47
- * import flagsmith from Flagsmith
48
- * const flagsmith = new Flagsmith({environmentKey: '<your API key>'});
49
- * const environmentFlags = flagsmith.getEnvironmentFlags();
50
- * const featureEnabled = environmentFlags.isFeatureEnabled('foo');
51
- * const identityFlags = flagsmith.getIdentityFlags('identifier', {'foo': 'bar'});
52
- * const featureEnabledForIdentity = identityFlags.isFeatureEnabled("foo")
53
- *
54
- * @param {string} data.environmentKey: The environment key obtained from Flagsmith interface
55
- * Required unless offlineMode is True.
56
- @param {string} data.apiUrl: Override the URL of the Flagsmith API to communicate with
57
- @param data.customHeaders: Additional headers to add to requests made to the
58
- Flagsmith API
59
- @param {number} data.requestTimeoutSeconds: Number of seconds to wait for a request to
60
- complete before terminating the request
61
- @param {boolean} data.enableLocalEvaluation: Enables local evaluation of flags
62
- @param {number} data.environmentRefreshIntervalSeconds: If using local evaluation,
63
- specify the interval period between refreshes of local environment data
64
- @param {number} data.retries: a urllib3.Retry object to use on all http requests to the
65
- Flagsmith API
66
- @param {boolean} data.enableAnalytics: if enabled, sends additional requests to the Flagsmith
67
- API to power flag analytics charts
68
- @param data.defaultFlagHandler: callable which will be used in the case where
69
- flags cannot be retrieved from the API or a non-existent feature is
70
- requested
71
- @param data.logger: an instance of the pino Logger class to use for logging
72
- @param {boolean} data.offlineMode: sets the client into offline mode. Relies on offlineHandler for
73
- evaluating flags.
74
- @param {BaseOfflineHandler} data.offlineHandler: provide a handler for offline logic. Used to get environment
75
- document from another source when in offlineMode. Works in place of
76
- defaultFlagHandler if offlineMode is not set and using remote evaluation.
77
- */
78
- constructor(data?: FlagsmithConfig);
69
+ * If using local evaluation, the environment will be fetched lazily when needed by any method. Polling the
70
+ * environment for updates will start after {@link environmentRefreshIntervalSeconds} once the client is created.
71
+ * @param data The {@link FlagsmithConfig} options for this client.
72
+ */
73
+ constructor(data: FlagsmithConfig);
79
74
  /**
80
75
  * Get all the default for flags for the current environment.
81
76
  *
@@ -110,10 +105,11 @@ export declare class Flagsmith {
110
105
  getIdentitySegments(identifier: string, traits?: {
111
106
  [key: string]: any;
112
107
  }): Promise<SegmentModel[]>;
108
+ private fetchEnvironment;
113
109
  /**
114
- * Updates the environment state for local flag evaluation.
115
- * Sets a local promise to prevent race conditions in getIdentityFlags / getIdentitySegments.
116
- * You only need to call this if you wish to bypass environmentRefreshIntervalSeconds.
110
+ * Fetch the latest environment state from the Flagsmith API to use for local flag evaluation.
111
+ *
112
+ * If the environment is currently being fetched, calling this method will not cause additional fetches.
117
113
  */
118
114
  updateEnvironment(): Promise<void>;
119
115
  close(): Promise<void>;
@@ -121,7 +117,13 @@ export declare class Flagsmith {
121
117
  /**
122
118
  * This promise ensures that the environment is retrieved before attempting to locally evaluate.
123
119
  */
124
- private environmentPromise;
120
+ private environmentPromise?;
121
+ /**
122
+ * Returns the current environment, fetching it from the API if needed.
123
+ *
124
+ * Calling this method concurrently while the environment is being fetched will not cause additional requests.
125
+ */
126
+ getEnvironment(): Promise<EnvironmentModel>;
125
127
  private getEnvironmentFromApi;
126
128
  private getEnvironmentFlagsFromDocument;
127
129
  private getIdentityFlagsFromDocument;