@optimizely/optimizely-sdk
Version:
JavaScript SDK for Optimizely Feature Experimentation, Optimizely Full Stack (legacy), and Optimizely Rollouts
493 lines (491 loc) • 27.7 kB
TypeScript
/**
* Copyright 2020-2025, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { LoggerFacade } from '../logging/logger';
import { DefaultNotificationCenter } from '../notification_center';
import { EventProcessor } from '../event_processor/event_processor';
import { OdpManager } from '../odp/odp_manager';
import { VuidManager } from '../vuid/vuid_manager';
import { OptimizelySegmentOption } from '../odp/segment_manager/optimizely_segment_option';
import { BaseService } from '../service';
import { UserAttributes, EventTags, OptimizelyConfig, UserProfileService, OptimizelyDecideOption, FeatureVariableValue, OptimizelyDecision, Client, UserProfileServiceAsync } from '../shared_types';
import OptimizelyUserContext from '../optimizely_user_context';
import { ProjectConfigManager } from '../project_config/project_config_manager';
import * as projectConfig from '../project_config/project_config';
import { ErrorNotifier } from '../error/error_notifier';
import { CmabService } from '../core/decision_service/cmab/cmab_service';
type InputKey = 'feature_key' | 'user_id' | 'variable_key' | 'experiment_key' | 'event_key' | 'variation_id';
type StringInputs = Partial<Record<InputKey, unknown>>;
export declare const INSTANCE_CLOSED = "Instance closed";
export declare const ONREADY_TIMEOUT = "onReady timeout expired after %s ms";
export declare const INVALID_IDENTIFIER = "Invalid identifier";
export declare const INVALID_ATTRIBUTES = "Invalid attributes";
/**
* options required to create optimizely object
*/
export type OptimizelyOptions = {
projectConfigManager: ProjectConfigManager;
UNSTABLE_conditionEvaluators?: unknown;
cmabService: CmabService;
clientEngine: string;
clientVersion?: string;
errorNotifier?: ErrorNotifier;
eventProcessor?: EventProcessor;
jsonSchemaValidator?: {
validate(jsonObject: unknown): boolean;
};
logger?: LoggerFacade;
userProfileService?: UserProfileService | null;
userProfileServiceAsync?: UserProfileServiceAsync | null;
defaultDecideOptions?: OptimizelyDecideOption[];
odpManager?: OdpManager;
vuidManager?: VuidManager;
disposable?: boolean;
};
export default class Optimizely extends BaseService implements Client {
private cleanupTasks;
private nextCleanupTaskId;
private clientEngine;
private clientVersion;
private errorNotifier?;
private errorReporter;
private projectConfigManager;
private decisionService;
private eventProcessor?;
private defaultDecideOptions;
private odpManager?;
notificationCenter: DefaultNotificationCenter;
private vuidManager?;
constructor(config: OptimizelyOptions);
start(): void;
/**
* Returns the project configuration retrieved from projectConfigManager
* @return {projectConfig.ProjectConfig}
*/
getProjectConfig(): projectConfig.ProjectConfig | null;
/**
* Buckets visitor and sends impression event to Optimizely.
* @param {string} experimentKey
* @param {string} userId
* @param {UserAttributes} attributes
* @return {string|null} variation key
*/
activate(experimentKey: string, userId: string, attributes?: UserAttributes): string | null;
/**
* Create an impression event and call the event dispatcher's dispatch method to
* send this event to Optimizely. Then use the notification center to trigger
* any notification listeners for the ACTIVATE notification type.
* @param {DecisionObj} decisionObj Decision Object
* @param {string} flagKey Key for a feature flag
* @param {string} userId ID of user to whom the variation was shown
* @param {UserAttributes} attributes Optional user attributes
* @param {boolean} enabled Boolean representing if feature is enabled
*/
private sendImpressionEvent;
/**
* Sends conversion event to Optimizely.
* @param {string} eventKey
* @param {string} userId
* @param {UserAttributes} attributes
* @param {EventTags} eventTags Values associated with the event.
*/
track(eventKey: string, userId: string, attributes?: UserAttributes, eventTags?: EventTags): void;
/**
* Gets variation where visitor will be bucketed.
* @param {string} experimentKey
* @param {string} userId
* @param {UserAttributes} attributes
* @return {string|null} variation key
*/
getVariation(experimentKey: string, userId: string, attributes?: UserAttributes): string | null;
/**
* Force a user into a variation for a given experiment.
* @param {string} experimentKey
* @param {string} userId
* @param {string|null} variationKey user will be forced into. If null,
* then clear the existing experiment-to-variation mapping.
* @return {boolean} A boolean value that indicates if the set completed successfully.
*/
setForcedVariation(experimentKey: string, userId: string, variationKey: string | null): boolean;
/**
* Gets the forced variation for a given user and experiment.
* @param {string} experimentKey
* @param {string} userId
* @return {string|null} The forced variation key.
*/
getForcedVariation(experimentKey: string, userId: string): string | null;
/**
* Validate string inputs, user attributes and event tags.
* @param {StringInputs} stringInputs Map of string keys and associated values
* @param {unknown} userAttributes Optional parameter for user's attributes
* @param {unknown} eventTags Optional parameter for event tags
* @return {boolean} True if inputs are valid
*
*/
protected validateInputs(stringInputs: StringInputs, userAttributes?: unknown, eventTags?: unknown): boolean;
/**
* Shows failed activation log message and returns null when user is not activated in experiment
* @param {string} experimentKey
* @param {string} userId
* @return {null}
*/
private notActivatingExperiment;
/**
* Filters out attributes/eventTags with null or undefined values
* @param {EventTags | undefined} map
* @returns {EventTags | undefined}
*/
private filterEmptyValues;
/**
* Returns true if the feature is enabled for the given user.
* @param {string} featureKey Key of feature which will be checked
* @param {string} userId ID of user which will be checked
* @param {UserAttributes} attributes Optional user attributes
* @return {boolean} true if the feature is enabled for the user, false otherwise
*/
isFeatureEnabled(featureKey: string, userId: string, attributes?: UserAttributes): boolean;
/**
* Returns an Array containing the keys of all features in the project that are
* enabled for the given user.
* @param {string} userId
* @param {UserAttributes} attributes
* @return {string[]} Array of feature keys (strings)
*/
getEnabledFeatures(userId: string, attributes?: UserAttributes): string[];
/**
* Returns dynamically-typed value of the variable attached to the given
* feature flag. Returns null if the feature key or variable key is invalid.
*
* @param {string} featureKey Key of the feature whose variable's
* value is being accessed
* @param {string} variableKey Key of the variable whose value is
* being accessed
* @param {string} userId ID for the user
* @param {UserAttributes} attributes Optional user attributes
* @return {unknown} Value of the variable cast to the appropriate
* type, or null if the feature key is invalid or
* the variable key is invalid
*/
getFeatureVariable(featureKey: string, variableKey: string, userId: string, attributes?: UserAttributes): FeatureVariableValue;
/**
* Helper method to get the value for a variable of a certain type attached to a
* feature flag. Returns null if the feature key is invalid, the variable key is
* invalid, the given variable type does not match the variable's actual type,
* or the variable value cannot be cast to the required type. If the given variable
* type is null, the value of the variable cast to the appropriate type is returned.
*
* @param {string} featureKey Key of the feature whose variable's value is
* being accessed
* @param {string} variableKey Key of the variable whose value is being
* accessed
* @param {string|null} variableType Type of the variable whose value is being
* accessed (must be one of FEATURE_VARIABLE_TYPES
* in lib/utils/enums/index.js), or null to return the
* value of the variable cast to the appropriate type
* @param {string} userId ID for the user
* @param {UserAttributes} attributes Optional user attributes
* @return {unknown} Value of the variable cast to the appropriate
* type, or null if the feature key is invalid, thevariable
* key is invalid, or there is a mismatch with the type of
* the variable
*/
private getFeatureVariableForType;
/**
* Helper method to get the non type-casted value for a variable attached to a
* feature flag. Returns appropriate variable value depending on whether there
* was a matching variation, feature was enabled or not or varible was part of the
* available variation or not. Also logs the appropriate message explaining how it
* evaluated the value of the variable.
*
* @param {string} featureKey Key of the feature whose variable's value is
* being accessed
* @param {boolean} featureEnabled Boolean indicating if feature is enabled or not
* @param {Variation} variation variation returned by decision service
* @param {FeatureVariable} variable varible whose value is being evaluated
* @param {string} userId ID for the user
* @return {unknown} Value of the variable or null if the
* config Obj is null
*/
private getFeatureVariableValueFromVariation;
/**
* Returns value for the given boolean variable attached to the given feature
* flag.
* @param {string} featureKey Key of the feature whose variable's value is
* being accessed
* @param {string} variableKey Key of the variable whose value is being
* accessed
* @param {string} userId ID for the user
* @param {UserAttributes} attributes Optional user attributes
* @return {boolean|null} Boolean value of the variable, or null if the
* feature key is invalid, the variable key is invalid,
* or there is a mismatch with the type of the variable.
*/
getFeatureVariableBoolean(featureKey: string, variableKey: string, userId: string, attributes?: UserAttributes): boolean | null;
/**
* Returns value for the given double variable attached to the given feature
* flag.
* @param {string} featureKey Key of the feature whose variable's value is
* being accessed
* @param {string} variableKey Key of the variable whose value is being
* accessed
* @param {string} userId ID for the user
* @param {UserAttributes} attributes Optional user attributes
* @return {number|null} Number value of the variable, or null if the
* feature key is invalid, the variable key is
* invalid, or there is a mismatch with the type
* of the variable
*/
getFeatureVariableDouble(featureKey: string, variableKey: string, userId: string, attributes?: UserAttributes): number | null;
/**
* Returns value for the given integer variable attached to the given feature
* flag.
* @param {string} featureKey Key of the feature whose variable's value is
* being accessed
* @param {string} variableKey Key of the variable whose value is being
* accessed
* @param {string} userId ID for the user
* @param {UserAttributes} attributes Optional user attributes
* @return {number|null} Number value of the variable, or null if the
* feature key is invalid, the variable key is
* invalid, or there is a mismatch with the type
* of the variable
*/
getFeatureVariableInteger(featureKey: string, variableKey: string, userId: string, attributes?: UserAttributes): number | null;
/**
* Returns value for the given string variable attached to the given feature
* flag.
* @param {string} featureKey Key of the feature whose variable's value is
* being accessed
* @param {string} variableKey Key of the variable whose value is being
* accessed
* @param {string} userId ID for the user
* @param {UserAttributes} attributes Optional user attributes
* @return {string|null} String value of the variable, or null if the
* feature key is invalid, the variable key is
* invalid, or there is a mismatch with the type
* of the variable
*/
getFeatureVariableString(featureKey: string, variableKey: string, userId: string, attributes?: UserAttributes): string | null;
/**
* Returns value for the given json variable attached to the given feature
* flag.
* @param {string} featureKey Key of the feature whose variable's value is
* being accessed
* @param {string} variableKey Key of the variable whose value is being
* accessed
* @param {string} userId ID for the user
* @param {UserAttributes} attributes Optional user attributes
* @return {unknown} Object value of the variable, or null if the
* feature key is invalid, the variable key is
* invalid, or there is a mismatch with the type
* of the variable
*/
getFeatureVariableJSON(featureKey: string, variableKey: string, userId: string, attributes: UserAttributes): unknown;
/**
* Returns values for all the variables attached to the given feature
* flag.
* @param {string} featureKey Key of the feature whose variables are being
* accessed
* @param {string} userId ID for the user
* @param {UserAttributes} attributes Optional user attributes
* @return {object|null} Object containing all the variables, or null if the
* feature key is invalid
*/
getAllFeatureVariables(featureKey: string, userId: string, attributes?: UserAttributes): {
[variableKey: string]: unknown;
} | null;
/**
* Returns OptimizelyConfig object containing experiments and features data
* @return {OptimizelyConfig|null}
*
* OptimizelyConfig Object Schema
* {
* 'experimentsMap': {
* 'my-fist-experiment': {
* 'id': '111111',
* 'key': 'my-fist-experiment'
* 'variationsMap': {
* 'variation_1': {
* 'id': '121212',
* 'key': 'variation_1',
* 'variablesMap': {
* 'age': {
* 'id': '222222',
* 'key': 'age',
* 'type': 'integer',
* 'value': '0',
* }
* }
* }
* }
* }
* },
* 'featuresMap': {
* 'awesome-feature': {
* 'id': '333333',
* 'key': 'awesome-feature',
* 'experimentsMap': Object,
* 'variationsMap': Object,
* }
* }
* }
*/
getOptimizelyConfig(): OptimizelyConfig | null;
/**
* Stop background processes belonging to this instance, including:
*
* - Active datafile requests
* - Pending datafile requests
* - Pending event queue flushes
*
* In-flight datafile requests will be aborted. Any events waiting to be sent
* as part of a batched event request will be immediately flushed to the event
* dispatcher.
*
* Returns a Promise that fulfills after all in-flight event dispatcher requests
* (including any final request resulting from flushing the queue as described
* above) are complete. If there are no in-flight event dispatcher requests and
* no queued events waiting to be sent, returns an immediately-fulfilled Promise.
*
*
* NOTE: After close is called, this instance is no longer usable - any events
* generated will no longer be sent to the event dispatcher.
*
* @return {Promise}
*/
close(): Promise<unknown>;
stop(): void;
/**
* Returns a Promise that fulfills when this instance is ready to use (meaning
* it has a valid datafile), or rejects when it has failed to become ready within a period of
* time (configurable by the timeout property of the options argument), or when
* this instance is closed via the close method before it became ready.
*
* If a static project config manager with a valid datafile was provided in the constructor,
* the returned Promise is immediately fulfilled. If a polling config manager was provided,
* it will be used to fetch a datafile, and the returned promise will fulfill if that fetch
* succeeds, or it will reject if the datafile fetch does not complete before the timeout.
* The default timeout is 30 seconds.
*
* The returned Promise is fulfilled with an unknown result which is not needed to
* be inspected to know that the instance is ready. If the promise is fulfilled, it
* is guaranteed that the instance is ready to use. If the promise is rejected, it
* means the instance is not ready to use, and the reason for the promise rejection
* will contain an error denoting the cause of failure.
* @param {Object=} options
* @param {number|undefined} options.timeout
* @return {Promise}
*/
onReady(options?: {
timeout?: number;
}): Promise<unknown>;
/**
* Creates a context of the user for which decision APIs will be called.
*
* A user context will be created successfully even when the SDK is not fully configured yet, so no
* this.isValidInstance() check is performed here.
*
* @param {string} userId (Optional) The user ID to be used for bucketing.
* @param {UserAttributes} attributes (Optional) user attributes.
* @return {OptimizelyUserContext|null} An OptimizelyUserContext associated with this OptimizelyClient or
* throws if provided inputs are invalid
*/
createUserContext(userId?: string, attributes?: UserAttributes): OptimizelyUserContext;
/**
* Creates an internal context of the user for which decision APIs will be called.
*
* A user context will be created successfully even when the SDK is not fully configured yet, so no
* this.isValidInstance() check is performed here.
*
* @param {string} userId The user ID to be used for bucketing.
* @param {UserAttributes} attributes Optional user attributes.
* @return {OptimizelyUserContext|null} An OptimizelyUserContext associated with this OptimizelyClient or
* null if provided inputs are invalid
*/
private createInternalUserContext;
decide(user: OptimizelyUserContext, key: string, options?: OptimizelyDecideOption[]): OptimizelyDecision;
decideAsync(user: OptimizelyUserContext, key: string, options?: OptimizelyDecideOption[]): Promise<OptimizelyDecision>;
/**
* Get all decide options.
* @param {OptimizelyDecideOption[]} options decide options
* @return {[key: string]: boolean} Map of all provided decide options including default decide options
*/
private getAllDecideOptions;
/**
* Makes a decision for a given feature key.
*
* @param {OptimizelyUserContext} user - The user context associated with this Optimizely client.
* @param {string} key - The feature key for which a decision will be made.
* @param {DecisionObj} decisionObj - The decision object containing decision details.
* @param {DecisionReasons[]} reasons - An array of reasons for the decision.
* @param {Record<string, boolean>} options - A map of options for decision-making.
* @param {projectConfig.ProjectConfig} configObj - The project configuration object.
* @returns {OptimizelyDecision} - The decision object for the feature flag.
*/
private generateDecision;
/**
* Returns an object of decision results for multiple flag keys and a user context.
* If the SDK finds an error for a key, the response will include a decision for the key showing reasons for the error.
* The SDK will always return an object of decisions. When it cannot process requests, it will return an empty object after logging the errors.
* @param {OptimizelyUserContext} user A user context associated with this OptimizelyClient
* @param {string[]} keys An array of flag keys for which decisions will be made.
* @param {OptimizelyDecideOption[]} options An array of options for decision-making.
* @return {[key: string]: OptimizelyDecision} An object of decision results mapped by flag keys.
*/
decideForKeys(user: OptimizelyUserContext, keys: string[], options?: OptimizelyDecideOption[], ignoreEnabledFlagOption?: boolean): Record<string, OptimizelyDecision>;
decideForKeysAsync(user: OptimizelyUserContext, keys: string[], options?: OptimizelyDecideOption[], ignoreEnabledFlagOption?: boolean): Promise<Record<string, OptimizelyDecision>>;
private getDecisionForKeys;
/**
* Returns an object of decision results for all active flag keys.
* @param {OptimizelyUserContext} user A user context associated with this OptimizelyClient
* @param {OptimizelyDecideOption[]} options An array of options for decision-making.
* @return {[key: string]: OptimizelyDecision} An object of all decision results mapped by flag keys.
*/
decideAll(user: OptimizelyUserContext, options?: OptimizelyDecideOption[]): {
[key: string]: OptimizelyDecision;
};
decideAllAsync(user: OptimizelyUserContext, options?: OptimizelyDecideOption[]): Promise<Record<string, OptimizelyDecision>>;
/**
* Updates ODP Config with most recent ODP key, host, pixelUrl, and segments from the project config
*/
private updateOdpSettings;
/**
* Sends an action as an ODP Event with optional custom parameters including type, identifiers, and data
* Note: Since this depends on this.odpManager, it must await Optimizely client's onReady() promise resolution.
* @param {string} action Subcategory of the event type (i.e. "client_initialized", "identified", or a custom action)
* @param {string} type (Optional) Type of event (Defaults to "fullstack")
* @param {Map<string, string>} identifiers (Optional) Key-value map of user identifiers
* @param {Map<string, string>} data (Optional) Event data in a key-value map.
*/
sendOdpEvent(action: string, type?: string, identifiers?: Map<string, string>, data?: Map<string, unknown>): void;
/**
* Checks if ODP (Optimizely Data Platform) is integrated into the project.
* @returns { boolean } `true` if ODP settings were found in the datafile otherwise `false`
*/
isOdpIntegrated(): boolean;
/**
* Fetches list of qualified segments from ODP for a particular userId.
* @param {string} userId
* @param {Array<OptimizelySegmentOption>} options
* @returns {Promise<string[] | null>}
*/
fetchQualifiedSegments(userId: string, options?: Array<OptimizelySegmentOption>): Promise<string[] | null>;
/**
* @returns {string|undefined} Currently provisioned VUID from local ODP Manager or undefined if
* ODP Manager has not been instantiated yet for any reason.
*/
getVuid(): string | undefined;
}
export {};