UNPKG

@optimizely/optimizely-sdk

Version:

JavaScript SDK for Optimizely Feature Experimentation, Optimizely Full Stack (legacy), and Optimizely Rollouts

493 lines (491 loc) 27.7 kB
/** * 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 {};