diff --git a/packages/shared/common/src/api/data/LDEvaluationDetail.ts b/packages/shared/common/src/api/data/LDEvaluationDetail.ts index 0a4f11dd7..84f08d5d4 100644 --- a/packages/shared/common/src/api/data/LDEvaluationDetail.ts +++ b/packages/shared/common/src/api/data/LDEvaluationDetail.ts @@ -1,6 +1,8 @@ import { LDEvaluationReason } from './LDEvaluationReason'; import { LDFlagValue } from './LDFlagValue'; +// TODO: On major version change "variationIndex" to only be optional and not nullable. + /** * An object that combines the result of a feature flag evaluation with information about * how it was calculated. diff --git a/packages/shared/common/src/internal/evaluation/evaluationDetail.ts b/packages/shared/common/src/internal/evaluation/evaluationDetail.ts deleted file mode 100644 index c08cc8687..000000000 --- a/packages/shared/common/src/internal/evaluation/evaluationDetail.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { LDEvaluationReason, LDFlagValue } from '../../api'; -import ErrorKinds from './ErrorKinds'; - -export const createErrorEvaluationDetail = (errorKind: ErrorKinds, def?: LDFlagValue) => ({ - value: def ?? null, - variationIndex: null, - reason: { kind: 'ERROR', errorKind }, -}); - -export const createSuccessEvaluationDetail = ( - value: LDFlagValue, - variationIndex?: number, - reason?: LDEvaluationReason, -) => ({ - value, - variationIndex: variationIndex ?? null, - reason: reason ?? null, -}); diff --git a/packages/shared/common/src/internal/evaluation/index.ts b/packages/shared/common/src/internal/evaluation/index.ts index 175c5d4e9..9882812df 100644 --- a/packages/shared/common/src/internal/evaluation/index.ts +++ b/packages/shared/common/src/internal/evaluation/index.ts @@ -1,11 +1,4 @@ import ErrorKinds from './ErrorKinds'; -import { createErrorEvaluationDetail, createSuccessEvaluationDetail } from './evaluationDetail'; import EventFactoryBase, { EvalEventArgs } from './EventFactoryBase'; -export { - createSuccessEvaluationDetail, - createErrorEvaluationDetail, - ErrorKinds, - EvalEventArgs, - EventFactoryBase, -}; +export { ErrorKinds, EvalEventArgs, EventFactoryBase }; diff --git a/packages/shared/sdk-client/src/LDClientImpl.storage.test.ts b/packages/shared/sdk-client/src/LDClientImpl.storage.test.ts index 1b4ffcb15..4b25031c6 100644 --- a/packages/shared/sdk-client/src/LDClientImpl.storage.test.ts +++ b/packages/shared/sdk-client/src/LDClientImpl.storage.test.ts @@ -5,10 +5,10 @@ import { setupMockStreamingProcessor, } from '@launchdarkly/private-js-mocks'; -import LDEmitter from './api/LDEmitter'; import { toMulti } from './context/addAutoEnv'; import * as mockResponseJson from './evaluation/mockResponse.json'; import LDClientImpl from './LDClientImpl'; +import LDEmitter from './LDEmitter'; import { DeleteFlag, Flags, PatchFlag } from './types'; let mockPlatform: ReturnType; diff --git a/packages/shared/sdk-client/src/LDClientImpl.ts b/packages/shared/sdk-client/src/LDClientImpl.ts index 78d8b3de8..cc8b33ad2 100644 --- a/packages/shared/sdk-client/src/LDClientImpl.ts +++ b/packages/shared/sdk-client/src/LDClientImpl.ts @@ -6,8 +6,6 @@ import { internal, LDClientError, LDContext, - LDEvaluationDetail, - LDEvaluationDetailTyped, LDFlagSet, LDFlagValue, LDLogger, @@ -20,21 +18,25 @@ import { import { LDStreamProcessor } from '@launchdarkly/js-sdk-common/dist/api/subsystem'; import { ConnectionMode, LDClient, type LDOptions } from './api'; -import LDEmitter, { EventName } from './api/LDEmitter'; +import { LDEvaluationDetail, LDEvaluationDetailTyped } from './api/LDEvaluationDetail'; import { LDIdentifyOptions } from './api/LDIdentifyOptions'; import Configuration from './configuration'; import { addAutoEnv } from './context/addAutoEnv'; import { ensureKey } from './context/ensureKey'; import createDiagnosticsManager from './diagnostics/createDiagnosticsManager'; +import { + createErrorEvaluationDetail, + createSuccessEvaluationDetail, +} from './evaluation/evaluationDetail'; import createEventProcessor from './events/createEventProcessor'; import EventFactory from './events/EventFactory'; import FlagManager from './flag-manager/FlagManager'; import { ItemDescriptor } from './flag-manager/ItemDescriptor'; +import LDEmitter, { EventName } from './LDEmitter'; import PollingProcessor from './polling/PollingProcessor'; import { DeleteFlag, Flags, PatchFlag } from './types'; -const { createErrorEvaluationDetail, createSuccessEvaluationDetail, ClientMessages, ErrorKinds } = - internal; +const { ClientMessages, ErrorKinds } = internal; export default class LDClientImpl implements LDClient { private readonly config: Configuration; @@ -483,7 +485,7 @@ export default class LDClientImpl implements LDClient { defaultValue: any, eventFactory: EventFactory, typeChecker?: (value: any) => [boolean, string], - ): LDFlagValue { + ): LDEvaluationDetail { if (!this.uncheckedContext) { this.logger.debug(ClientMessages.missingContextKeyNoEvent); return createErrorEvaluationDetail(ErrorKinds.UserNotSpecified, defaultValue); diff --git a/packages/shared/sdk-client/src/api/LDEmitter.test.ts b/packages/shared/sdk-client/src/LDEmitter.test.ts similarity index 100% rename from packages/shared/sdk-client/src/api/LDEmitter.test.ts rename to packages/shared/sdk-client/src/LDEmitter.test.ts diff --git a/packages/shared/sdk-client/src/api/LDEmitter.ts b/packages/shared/sdk-client/src/LDEmitter.ts similarity index 100% rename from packages/shared/sdk-client/src/api/LDEmitter.ts rename to packages/shared/sdk-client/src/LDEmitter.ts diff --git a/packages/shared/sdk-client/src/api/LDClient.ts b/packages/shared/sdk-client/src/api/LDClient.ts index 7902bc691..8f26ad399 100644 --- a/packages/shared/sdk-client/src/api/LDClient.ts +++ b/packages/shared/sdk-client/src/api/LDClient.ts @@ -1,13 +1,7 @@ -import { - LDContext, - LDEvaluationDetail, - LDEvaluationDetailTyped, - LDFlagSet, - LDFlagValue, - LDLogger, -} from '@launchdarkly/js-sdk-common'; +import { LDContext, LDFlagSet, LDFlagValue, LDLogger } from '@launchdarkly/js-sdk-common'; import ConnectionMode from './ConnectionMode'; +import { LDEvaluationDetail, LDEvaluationDetailTyped } from './LDEvaluationDetail'; import { LDIdentifyOptions } from './LDIdentifyOptions'; /** diff --git a/packages/shared/sdk-client/src/api/LDEvaluationDetail.ts b/packages/shared/sdk-client/src/api/LDEvaluationDetail.ts new file mode 100644 index 000000000..466522456 --- /dev/null +++ b/packages/shared/sdk-client/src/api/LDEvaluationDetail.ts @@ -0,0 +1,37 @@ +import { + LDEvaluationDetail as CommonDetail, + LDEvaluationDetailTyped as CommonDetailTyped, + LDEvaluationReason, +} from '@launchdarkly/js-sdk-common'; + +// Implementation note: In client-side SDKs the reason is optional. The common type, which is also +// used by server SDKs, has a required reason. This file contains a client specific +// LDEvaluationDetail which has an optional reason. + +// TODO: On major version change "reason" to be optional instead of nullable. + +/** + * An object that combines the result of a feature flag evaluation with information about + * how it was calculated. + * + * This is the result of calling `LDClient.variationDetail`. + */ +export type LDEvaluationDetail = Omit & { + /** + * An optional object describing the main factor that influenced the flag evaluation value. + */ + reason: LDEvaluationReason | null; +}; + +/** + * An object that combines the result of a feature flag evaluation with information about + * how it was calculated. + * + * This is the result of calling detailed variation methods. + */ +export type LDEvaluationDetailTyped = Omit, 'reason'> & { + /** + * An optional object describing the main factor that influenced the flag evaluation value. + */ + reason: LDEvaluationReason | null; +}; diff --git a/packages/shared/sdk-client/src/api/LDInspection.ts b/packages/shared/sdk-client/src/api/LDInspection.ts deleted file mode 100644 index 125a9116c..000000000 --- a/packages/shared/sdk-client/src/api/LDInspection.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { LDContext, LDEvaluationDetail } from '@launchdarkly/js-sdk-common'; - -/** - * Callback interface for collecting information about the SDK at runtime. - * - * This interface is used to collect information about flag usage. - * - * This interface should not be used by the application to access flags for the purpose of controlling application - * flow. It is intended for monitoring, analytics, or debugging purposes. - */ - -export interface LDInspectionFlagUsedHandler { - type: 'flag-used'; - - /** - * Name of the inspector. Will be used for logging issues with the inspector. - */ - name: string; - - /** - * This method is called when a flag is accessed via a variation method, or it can be called based on actions in - * wrapper SDKs which have different methods of tracking when a flag was accessed. It is not called when a call is made - * to allFlags. - */ - method: (flagKey: string, flagDetail: LDEvaluationDetail, context: LDContext) => void; -} - -/** - * Callback interface for collecting information about the SDK at runtime. - * - * This interface is used to collect information about flag data. In order to understand the - * current flag state it should be combined with {@link LDInspectionFlagValueChangedHandler}. - * This interface will get the initial flag information, and - * {@link LDInspectionFlagValueChangedHandler} will provide changes to individual flags. - * - * This interface should not be used by the application to access flags for the purpose of controlling application - * flow. It is intended for monitoring, analytics, or debugging purposes. - */ -export interface LDInspectionFlagDetailsChangedHandler { - type: 'flag-details-changed'; - - /** - * Name of the inspector. Will be used for logging issues with the inspector. - */ - name: string; - - /** - * This method is called when the flags in the store are replaced with new flags. It will contain all flags - * regardless of if they have been evaluated. - */ - method: (details: Record) => void; -} - -/** - * Callback interface for collecting information about the SDK at runtime. - * - * This interface is used to collect changes to flag data, but does not provide the initial - * data. It can be combined with {@link LDInspectionFlagValuesChangedHandler} to track the - * entire flag state. - * - * This interface should not be used by the application to access flags for the purpose of controlling application - * flow. It is intended for monitoring, analytics, or debugging purposes. - */ -export interface LDInspectionFlagDetailChangedHandler { - type: 'flag-detail-changed'; - - /** - * Name of the inspector. Will be used for logging issues with the inspector. - */ - name: string; - - /** - * This method is called when a flag is updated. It will not be called - * when all flags are updated. - */ - method: (flagKey: string, detail: LDEvaluationDetail) => void; -} - -/** - * Callback interface for collecting information about the SDK at runtime. - * - * This interface is used to track current identity state of the SDK. - * - * This interface should not be used by the application to access flags for the purpose of controlling application - * flow. It is intended for monitoring, analytics, or debugging purposes. - */ -export interface LDInspectionIdentifyHandler { - type: 'client-identity-changed'; - - /** - * Name of the inspector. Will be used for logging issues with the inspector. - */ - name: string; - - /** - * This method will be called when an identify operation completes. - */ - method: (context: LDContext) => void; -} - -export type LDInspection = - | LDInspectionFlagUsedHandler - | LDInspectionFlagDetailsChangedHandler - | LDInspectionFlagDetailChangedHandler - | LDInspectionIdentifyHandler; diff --git a/packages/shared/sdk-client/src/api/index.ts b/packages/shared/sdk-client/src/api/index.ts index de5b9e531..24c6c13ce 100644 --- a/packages/shared/sdk-client/src/api/index.ts +++ b/packages/shared/sdk-client/src/api/index.ts @@ -2,5 +2,6 @@ import ConnectionMode from './ConnectionMode'; export * from './LDOptions'; export * from './LDClient'; +export * from './LDEvaluationDetail'; export { ConnectionMode }; diff --git a/packages/shared/sdk-client/src/configuration/Configuration.test.ts b/packages/shared/sdk-client/src/configuration/Configuration.test.ts index f86709e2f..e5b18e59c 100644 --- a/packages/shared/sdk-client/src/configuration/Configuration.test.ts +++ b/packages/shared/sdk-client/src/configuration/Configuration.test.ts @@ -20,7 +20,6 @@ describe('Configuration', () => { withReasons: false, eventsUri: 'https://events.launchdarkly.com', flushInterval: 30, - inspectors: [], logger: { destination: console.error, logLevel: 1, @@ -80,17 +79,6 @@ describe('Configuration', () => { ); }); - test('invalid bootstrap should use default', () => { - // @ts-ignore - const config = new Configuration({ bootstrap: 'localStora' }); - - expect(config.bootstrap).toBeUndefined(); - expect(console.error).toHaveBeenNthCalledWith( - 1, - expect.stringMatching(/should be of type LDFlagSet, got string/i), - ); - }); - test('recognize maxCachedContexts', () => { const config = new Configuration({ maxCachedContexts: 3 }); diff --git a/packages/shared/sdk-client/src/configuration/Configuration.ts b/packages/shared/sdk-client/src/configuration/Configuration.ts index c8d7f9aa7..8d4507dcc 100644 --- a/packages/shared/sdk-client/src/configuration/Configuration.ts +++ b/packages/shared/sdk-client/src/configuration/Configuration.ts @@ -10,7 +10,6 @@ import { } from '@launchdarkly/js-sdk-common'; import { ConnectionMode, type LDOptions } from '../api'; -import { LDInspection } from '../api/LDInspection'; import validators from './validators'; const DEFAULT_POLLING_INTERVAL: number = 60 * 5; @@ -41,7 +40,6 @@ export default class Configuration { public readonly useReport = false; public readonly withReasons = false; - public readonly inspectors: LDInspection[] = []; public readonly privateAttributes: string[] = []; public readonly initialConnectionMode: ConnectionMode = 'streaming'; diff --git a/packages/shared/sdk-client/src/configuration/validators.ts b/packages/shared/sdk-client/src/configuration/validators.ts index d59597a77..7b3aaddc9 100644 --- a/packages/shared/sdk-client/src/configuration/validators.ts +++ b/packages/shared/sdk-client/src/configuration/validators.ts @@ -1,18 +1,7 @@ // eslint-disable-next-line max-classes-per-file -import { noop, TypeValidator, TypeValidators } from '@launchdarkly/js-sdk-common'; +import { TypeValidator, TypeValidators } from '@launchdarkly/js-sdk-common'; import { type LDOptions } from '../api'; -import { LDInspection } from '../api/LDInspection'; - -class BootStrapValidator implements TypeValidator { - is(u: unknown): boolean { - return typeof u === 'object' || typeof u === 'undefined' || u === null; - } - - getType(): string { - return `LDFlagSet`; - } -} class ConnectionModeValidator implements TypeValidator { is(u: unknown): boolean { @@ -46,22 +35,11 @@ const validators: Record = { pollInterval: TypeValidators.numberWithMin(30), - // TODO: inspectors - // @ts-ignore - inspectors: TypeValidators.createTypeArray('LDInspection[]', { - type: 'flag-used', - method: noop, - name: '', - }), privateAttributes: TypeValidators.StringArray, applicationInfo: TypeValidators.Object, - // TODO: bootstrap - bootstrap: new BootStrapValidator(), wrapperName: TypeValidators.String, wrapperVersion: TypeValidators.String, - // TODO: hash - hash: TypeValidators.String, }; export default validators; diff --git a/packages/shared/sdk-client/src/diagnostics/createDiagnosticsInitConfig.ts b/packages/shared/sdk-client/src/diagnostics/createDiagnosticsInitConfig.ts index f653b581f..aeb8bf3c5 100644 --- a/packages/shared/sdk-client/src/diagnostics/createDiagnosticsInitConfig.ts +++ b/packages/shared/sdk-client/src/diagnostics/createDiagnosticsInitConfig.ts @@ -26,8 +26,9 @@ const createDiagnosticsInitConfig = (config: Configuration): DiagnosticsInitConf reconnectTimeMillis: secondsToMillis(config.streamInitialReconnectDelay), diagnosticRecordingIntervalMillis: secondsToMillis(config.diagnosticRecordingInterval), allAttributesPrivate: config.allAttributesPrivate, - usingSecureMode: !!config.hash, - bootstrapMode: !!config.bootstrap, + // TODO: Implement when corresponding features are implemented. + usingSecureMode: false, + bootstrapMode: false, }); export default createDiagnosticsInitConfig; diff --git a/packages/shared/sdk-client/src/evaluation/evaluationDetail.ts b/packages/shared/sdk-client/src/evaluation/evaluationDetail.ts new file mode 100644 index 000000000..f0e8d741c --- /dev/null +++ b/packages/shared/sdk-client/src/evaluation/evaluationDetail.ts @@ -0,0 +1,26 @@ +import { internal, LDEvaluationReason, LDFlagValue } from '@launchdarkly/js-sdk-common'; + +import { LDEvaluationDetail } from '../api'; + +export function createErrorEvaluationDetail( + errorKind: internal.ErrorKinds, + def?: LDFlagValue, +): LDEvaluationDetail { + return { + value: def ?? null, + variationIndex: null, + reason: { kind: 'ERROR', errorKind }, + }; +} + +export function createSuccessEvaluationDetail( + value: LDFlagValue, + variationIndex?: number, + reason?: LDEvaluationReason, +): LDEvaluationDetail { + return { + value, + variationIndex: variationIndex ?? null, + reason: reason ?? null, + }; +} diff --git a/packages/shared/sdk-client/src/index.ts b/packages/shared/sdk-client/src/index.ts index db960e981..59821cf54 100644 --- a/packages/shared/sdk-client/src/index.ts +++ b/packages/shared/sdk-client/src/index.ts @@ -1,8 +1,19 @@ import LDClientImpl from './LDClientImpl'; +export * from '@launchdarkly/js-sdk-common'; + export * as platform from '@launchdarkly/js-sdk-common'; -export * from './api'; -export * from '@launchdarkly/js-sdk-common'; +// To replace the exports from `export *` we need to name them. +// So the below exports replace them with the Node specific variants. + +// These exports are explicit to override those from common. +export type { + LDEvaluationDetail, + LDEvaluationDetailTyped, + LDClient, + LDOptions, + ConnectionMode, +} from './api'; export { LDClientImpl }; diff --git a/packages/shared/sdk-server/src/evaluation/EvalResult.ts b/packages/shared/sdk-server/src/evaluation/EvalResult.ts index 4ddb54543..cc1f70dcc 100644 --- a/packages/shared/sdk-server/src/evaluation/EvalResult.ts +++ b/packages/shared/sdk-server/src/evaluation/EvalResult.ts @@ -2,7 +2,6 @@ import { internal, LDEvaluationDetail, LDEvaluationReason } from '@launchdarkly/ import Reasons from './Reasons'; -const { createErrorEvaluationDetail, createSuccessEvaluationDetail } = internal; /** * A class which encapsulates the result of an evaluation. It allows for differentiating between * successful and error result types. @@ -31,11 +30,22 @@ export default class EvalResult { } static forError(errorKind: internal.ErrorKinds, message?: string, def?: any): EvalResult { - return new EvalResult(true, createErrorEvaluationDetail(errorKind, def), message); + return new EvalResult( + true, + { + value: def ?? null, + variationIndex: null, + reason: { kind: 'ERROR', errorKind }, + }, + message, + ); } static forSuccess(value: any, reason: LDEvaluationReason, variationIndex?: number) { - const successDetail = createSuccessEvaluationDetail(value, variationIndex, reason); - return new EvalResult(false, successDetail as LDEvaluationDetail); + return new EvalResult(false, { + value, + variationIndex: variationIndex === undefined ? null : variationIndex, + reason, + }); } }