diff --git a/lib/common_exports.ts b/lib/common_exports.ts index 947a3bcb4..93ae7db47 100644 --- a/lib/common_exports.ts +++ b/lib/common_exports.ts @@ -30,8 +30,8 @@ export { createErrorNotifier } from './error/error_notifier_factory'; export { DECISION_SOURCES, - DECISION_NOTIFICATION_TYPES, - NOTIFICATION_TYPES, } from './utils/enums'; +export { NOTIFICATION_TYPES, DECISION_NOTIFICATION_TYPES } from './notification_center/type'; + export { OptimizelyDecideOption } from './shared_types'; diff --git a/lib/core/decision_service/index.ts b/lib/core/decision_service/index.ts index dbd01061c..5cb82bbe8 100644 --- a/lib/core/decision_service/index.ts +++ b/lib/core/decision_service/index.ts @@ -19,6 +19,7 @@ import { AUDIENCE_EVALUATION_TYPES, CONTROL_ATTRIBUTES, DECISION_SOURCES, + DecisionSource, } from '../../utils/enums'; import { getAudiencesById, @@ -114,7 +115,7 @@ export const CMAB_FETCHED_VARIATION_INVALID = 'Fetched variation %s for cmab exp export interface DecisionObj { experiment: Experiment | null; variation: Variation | null; - decisionSource: string; + decisionSource: DecisionSource; cmabUuid?: string; } diff --git a/lib/entrypoint.test-d.ts b/lib/entrypoint.test-d.ts index ee6408344..b60537ea5 100644 --- a/lib/entrypoint.test-d.ts +++ b/lib/entrypoint.test-d.ts @@ -48,10 +48,10 @@ import { import { DECISION_SOURCES, - DECISION_NOTIFICATION_TYPES, - NOTIFICATION_TYPES, } from './utils/enums'; +import { NOTIFICATION_TYPES, DECISION_NOTIFICATION_TYPES } from './notification_center/type'; + import { LogLevel } from './logging/logger'; import { OptimizelyDecideOption } from './shared_types'; @@ -89,8 +89,8 @@ export type Entrypoint = { // enums DECISION_SOURCES: typeof DECISION_SOURCES; - DECISION_NOTIFICATION_TYPES: typeof DECISION_NOTIFICATION_TYPES; NOTIFICATION_TYPES: typeof NOTIFICATION_TYPES; + DECISION_NOTIFICATION_TYPES: typeof DECISION_NOTIFICATION_TYPES; // decide options OptimizelyDecideOption: typeof OptimizelyDecideOption; diff --git a/lib/entrypoint.universal.test-d.ts b/lib/entrypoint.universal.test-d.ts index fb91017b6..cde68ae97 100644 --- a/lib/entrypoint.universal.test-d.ts +++ b/lib/entrypoint.universal.test-d.ts @@ -41,10 +41,10 @@ import { RequestHandler } from './utils/http_request_handler/http'; import { UniversalBatchEventProcessorOptions } from './event_processor/event_processor_factory.universal'; import { DECISION_SOURCES, - DECISION_NOTIFICATION_TYPES, - NOTIFICATION_TYPES, } from './utils/enums'; +import { NOTIFICATION_TYPES, DECISION_NOTIFICATION_TYPES } from './notification_center/type'; + import { LogLevel } from './logging/logger'; import { OptimizelyDecideOption } from './shared_types'; @@ -82,8 +82,8 @@ export type UniversalEntrypoint = { // enums DECISION_SOURCES: typeof DECISION_SOURCES; - DECISION_NOTIFICATION_TYPES: typeof DECISION_NOTIFICATION_TYPES; NOTIFICATION_TYPES: typeof NOTIFICATION_TYPES; + DECISION_NOTIFICATION_TYPES: typeof DECISION_NOTIFICATION_TYPES; // decide options OptimizelyDecideOption: typeof OptimizelyDecideOption; diff --git a/lib/notification_center/type.ts b/lib/notification_center/type.ts index 7dcc132ab..75cfb082f 100644 --- a/lib/notification_center/type.ts +++ b/lib/notification_center/type.ts @@ -1,5 +1,5 @@ /** - * Copyright 2024, Optimizely + * Copyright 2024-2025, Optimizely * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,9 @@ */ import { LogEvent } from '../event_processor/event_dispatcher/event_dispatcher'; -import { EventTags, Experiment, UserAttributes, Variation } from '../shared_types'; +import { EventTags, Experiment, FeatureVariableValue, UserAttributes, VariableType, Variation } from '../shared_types'; +import { DecisionSource } from '../utils/enums'; +import { Nullable } from '../utils/type'; export type UserEventListenerPayload = { userId: string; @@ -43,16 +45,75 @@ export const DECISION_NOTIFICATION_TYPES = { FLAG: 'flag', } as const; + export type DecisionNotificationType = typeof DECISION_NOTIFICATION_TYPES[keyof typeof DECISION_NOTIFICATION_TYPES]; -// TODO: Add more specific types for decision info -export type OptimizelyDecisionInfo = Record; +export type ExperimentAndVariationInfo = { + experimentKey: string; + variationKey: string; +} + +export type DecisionSourceInfo = Partial; -export type DecisionListenerPayload = UserEventListenerPayload & { - type: DecisionNotificationType; - decisionInfo: OptimizelyDecisionInfo; +export type AbTestDecisonInfo = Nullable; + +type FeatureDecisionInfo = { + featureKey: string, + featureEnabled: boolean, + source: DecisionSource, + sourceInfo: DecisionSourceInfo, } +export type FeatureTestDecisionInfo = Nullable; + +export type FeatureVariableDecisionInfo = { + featureKey: string, + featureEnabled: boolean, + source: DecisionSource, + variableKey: string, + variableValue: FeatureVariableValue, + variableType: VariableType, + sourceInfo: DecisionSourceInfo, +}; + +export type VariablesMap = { [variableKey: string]: unknown } + +export type AllFeatureVariablesDecisionInfo = { + featureKey: string, + featureEnabled: boolean, + source: DecisionSource, + variableValues: VariablesMap, + sourceInfo: DecisionSourceInfo, +}; + +export type FlagDecisionInfo = { + flagKey: string, + enabled: boolean, + variationKey: string | null, + ruleKey: string | null, + variables: VariablesMap, + reasons: string[], + decisionEventDispatched: boolean, +}; + +export type DecisionInfo = { + [DECISION_NOTIFICATION_TYPES.AB_TEST]: AbTestDecisonInfo; + [DECISION_NOTIFICATION_TYPES.FEATURE]: FeatureDecisionInfo; + [DECISION_NOTIFICATION_TYPES.FEATURE_TEST]: FeatureTestDecisionInfo; + [DECISION_NOTIFICATION_TYPES.FEATURE_VARIABLE]: FeatureVariableDecisionInfo; + [DECISION_NOTIFICATION_TYPES.ALL_FEATURE_VARIABLES]: AllFeatureVariablesDecisionInfo; + [DECISION_NOTIFICATION_TYPES.FLAG]: FlagDecisionInfo; +} + +export type DecisionListenerPayloadForType = UserEventListenerPayload & { + type: T; + decisionInfo: DecisionInfo[T]; +} + +export type DecisionListenerPayload = { + [T in DecisionNotificationType]: DecisionListenerPayloadForType; +}[DecisionNotificationType]; + export type LogEventListenerPayload = LogEvent; export type OptimizelyConfigUpdateListenerPayload = undefined; diff --git a/lib/tests/test_data.ts b/lib/tests/test_data.ts index 990096f7b..e16081939 100644 --- a/lib/tests/test_data.ts +++ b/lib/tests/test_data.ts @@ -1,5 +1,5 @@ /** - * Copyright 2016-2021, 2024 Optimizely + * Copyright 2016-2021, 2024-2025 Optimizely * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -3555,7 +3555,7 @@ export var getMutexFeatureTestsConfig = function() { export var rolloutDecisionObj = { experiment: null, variation: null, - decisionSource: 'rollout', + decisionSource: 'rollout' as const, }; export var featureTestDecisionObj = { @@ -3611,7 +3611,7 @@ export var featureTestDecisionObj = { variables: [], variablesMap: {} }, - decisionSource: 'feature-test', + decisionSource: 'feature-test' as const, }; var similarRuleKeyConfig = { diff --git a/lib/utils/enums/index.ts b/lib/utils/enums/index.ts index bb5ca5e73..fe4fe9fbe 100644 --- a/lib/utils/enums/index.ts +++ b/lib/utils/enums/index.ts @@ -1,5 +1,5 @@ /** - * Copyright 2016-2024, Optimizely + * Copyright 2016-2025, Optimizely * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,15 +44,6 @@ export const NODE_CLIENT_ENGINE = 'node-sdk'; export const REACT_NATIVE_JS_CLIENT_ENGINE = 'react-native-js-sdk'; export const CLIENT_VERSION = '5.3.4'; -export const DECISION_NOTIFICATION_TYPES = { - AB_TEST: 'ab-test', - FEATURE: 'feature', - FEATURE_TEST: 'feature-test', - FEATURE_VARIABLE: 'feature-variable', - ALL_FEATURE_VARIABLES: 'all-feature-variables', - FLAG: 'flag', -}; - /* * Represents the source of a decision for feature management. When a feature * is accessed through isFeatureEnabled or getVariableValue APIs, the decision @@ -63,7 +54,9 @@ export const DECISION_SOURCES = { FEATURE_TEST: 'feature-test', ROLLOUT: 'rollout', EXPERIMENT: 'experiment', -}; +} as const; + +export type DecisionSource = typeof DECISION_SOURCES[keyof typeof DECISION_SOURCES]; export const AUDIENCE_EVALUATION_TYPES = { RULE: 'rule', @@ -104,8 +97,6 @@ export const DECISION_MESSAGES = { VARIABLE_VALUE_INVALID: 'Variable value for key "%s" is invalid or wrong type.', }; -export { NOTIFICATION_TYPES } from '../../notification_center/type'; - /** * Default milliseconds before request timeout */ diff --git a/lib/utils/type.ts b/lib/utils/type.ts index a6c31d769..c60f85d60 100644 --- a/lib/utils/type.ts +++ b/lib/utils/type.ts @@ -1,5 +1,5 @@ /** - * Copyright 2024, Optimizely + * Copyright 2024-2025, Optimizely * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,3 +31,9 @@ export type Either = { type: 'left', value: A } | { type: 'right', value: export type OpType = 'sync' | 'async'; export type OpValue = O extends 'sync' ? V : Promise; + +export type OrNull = T | null; + +export type Nullable = { + [P in keyof T]: P extends K ? OrNull : T[P]; +}