diff --git a/packages/sdk/browser/src/BrowserClient.ts b/packages/sdk/browser/src/BrowserClient.ts index 2424cfa7f..7e76bd2f7 100644 --- a/packages/sdk/browser/src/BrowserClient.ts +++ b/packages/sdk/browser/src/BrowserClient.ts @@ -1,7 +1,6 @@ import { AutoEnvAttributes, base64UrlEncode, - BasicLogger, LDClient as CommonClient, Configuration, createSafeLogger, diff --git a/packages/shared/sdk-client/__tests__/context/addAutoEnv.test.ts b/packages/shared/sdk-client/__tests__/context/addAutoEnv.test.ts index b5414b543..e6980d137 100644 --- a/packages/shared/sdk-client/__tests__/context/addAutoEnv.test.ts +++ b/packages/shared/sdk-client/__tests__/context/addAutoEnv.test.ts @@ -7,7 +7,7 @@ import { } from '@launchdarkly/js-sdk-common'; import { createBasicPlatform, createLogger } from '@launchdarkly/private-js-mocks'; -import Configuration from '../../src/configuration'; +import { Configuration, ConfigurationImpl } from '../../src/configuration'; import { addApplicationInfo, addAutoEnv, @@ -31,7 +31,7 @@ describe('automatic environment attributes', () => { beforeEach(() => { ({ crypto, info } = mockPlatform); (crypto.randomUUID as jest.Mock).mockResolvedValue('test-device-key-1'); - config = new Configuration({ logger }); + config = new ConfigurationImpl({ logger }); }); afterEach(() => { @@ -338,7 +338,7 @@ describe('automatic environment attributes', () => { describe('addApplicationInfo', () => { test('add id, version, name, versionName', async () => { - config = new Configuration({ + config = new ConfigurationImpl({ applicationInfo: { id: 'com.from-config.ld', version: '2.2.2', @@ -431,7 +431,7 @@ describe('automatic environment attributes', () => { info.platformData = jest .fn() .mockReturnValueOnce({ ld_application: { version: null, locale: '' } }); - config = new Configuration({ applicationInfo: { version: '1.2.3' } }); + config = new ConfigurationImpl({ applicationInfo: { version: '1.2.3' } }); const ldApplication = await addApplicationInfo(mockPlatform, config); expect(ldApplication).toBeUndefined(); diff --git a/packages/shared/sdk-client/__tests__/diagnostics/createDiagnosticsInitConfig.test.ts b/packages/shared/sdk-client/__tests__/diagnostics/createDiagnosticsInitConfig.test.ts index 1a30e21e1..004dd0f7b 100644 --- a/packages/shared/sdk-client/__tests__/diagnostics/createDiagnosticsInitConfig.test.ts +++ b/packages/shared/sdk-client/__tests__/diagnostics/createDiagnosticsInitConfig.test.ts @@ -1,6 +1,6 @@ import { secondsToMillis } from '@launchdarkly/js-sdk-common'; -import Configuration from '../../src/configuration'; +import { ConfigurationImpl } from '../../src/configuration'; import createDiagnosticsInitConfig, { type DiagnosticsInitConfig, } from '../../src/diagnostics/createDiagnosticsInitConfig'; @@ -9,7 +9,7 @@ describe('createDiagnosticsInitConfig', () => { let initConfig: DiagnosticsInitConfig; beforeEach(() => { - initConfig = createDiagnosticsInitConfig(new Configuration()); + initConfig = createDiagnosticsInitConfig(new ConfigurationImpl()); }); test('defaults', () => { @@ -29,7 +29,7 @@ describe('createDiagnosticsInitConfig', () => { test('non-default config', () => { const custom = createDiagnosticsInitConfig( - new Configuration({ + new ConfigurationImpl({ baseUri: 'https://dev.ld.com', streamUri: 'https://stream.ld.com', eventsUri: 'https://events.ld.com', diff --git a/packages/shared/sdk-client/src/DataManager.ts b/packages/shared/sdk-client/src/DataManager.ts index ee67a05dd..1fb0e9c1c 100644 --- a/packages/shared/sdk-client/src/DataManager.ts +++ b/packages/shared/sdk-client/src/DataManager.ts @@ -20,6 +20,20 @@ import { DataSourcePaths, StreamingProcessor } from './streaming'; import { DeleteFlag, Flags, PatchFlag } from './types'; export interface DataManager { + /** + * This function handles the data management aspects of the identification process. + * + * Implementation Note: The identifyResolve and identifyReject function resolve or reject the + * identify function at LDClient level. It is likely in individual implementations that these + * functions will be passed to other components, such as a datasource, do indicate when the + * identify process has been completed. The data manager identify function should return once + * everything has been set in motion to complete the identification process. + * + * @param identifyResolve Called to reject the identify operation. + * @param identifyReject Called to complete the identify operation. + * @param context The context being identified. + * @param identifyOptions Options for identification. + */ identify( identifyResolve: () => void, identifyReject: (err: Error) => void, @@ -28,6 +42,9 @@ export interface DataManager { ): Promise; } +/** + * Factory interface for constructing data managers. + */ export interface DataManagerFactory { ( flagManager: FlagManager, diff --git a/packages/shared/sdk-client/src/LDClientImpl.ts b/packages/shared/sdk-client/src/LDClientImpl.ts index 996bf4e4d..c268d1631 100644 --- a/packages/shared/sdk-client/src/LDClientImpl.ts +++ b/packages/shared/sdk-client/src/LDClientImpl.ts @@ -21,8 +21,7 @@ import { import { LDClient, type LDOptions } from './api'; import { LDEvaluationDetail, LDEvaluationDetailTyped } from './api/LDEvaluationDetail'; import { LDIdentifyOptions } from './api/LDIdentifyOptions'; -import ConfigurationImpl from './configuration'; -import { LDClientInternalOptions } from './configuration/Configuration'; +import { Configuration, ConfigurationImpl, LDClientInternalOptions } from './configuration'; import { addAutoEnv } from './context/addAutoEnv'; import { ensureKey } from './context/ensureKey'; import { DataManager, DataManagerFactory } from './DataManager'; @@ -33,7 +32,7 @@ import { } from './evaluation/evaluationDetail'; import createEventProcessor from './events/createEventProcessor'; import EventFactory from './events/EventFactory'; -import DefaultFlagManager from './flag-manager/FlagManager'; +import DefaultFlagManager, { FlagManager } from './flag-manager/FlagManager'; import { ItemDescriptor } from './flag-manager/ItemDescriptor'; import LDEmitter, { EventName } from './LDEmitter'; import { DeleteFlag, Flags, PatchFlag } from './types'; diff --git a/packages/shared/sdk-client/src/configuration/Configuration.ts b/packages/shared/sdk-client/src/configuration/Configuration.ts index b155c433f..1066fdd9e 100644 --- a/packages/shared/sdk-client/src/configuration/Configuration.ts +++ b/packages/shared/sdk-client/src/configuration/Configuration.ts @@ -56,15 +56,17 @@ export interface Configuration { readonly trackEventModifier: (event: internal.InputCustomEvent) => internal.InputCustomEvent; } -export default class ConfigurationImpl implements Configuration { - public static DEFAULT_POLLING = 'https://clientsdk.launchdarkly.com'; - public static DEFAULT_STREAM = 'https://clientstream.launchdarkly.com'; +const DEFAULT_POLLING: string = 'https://clientsdk.launchdarkly.com'; +const DEFAULT_STREAM: string = 'https://clientstream.launchdarkly.com'; + +export { DEFAULT_POLLING, DEFAULT_STREAM }; +export default class ConfigurationImpl implements Configuration { public readonly logger: LDLogger = createSafeLogger(); - public readonly baseUri = ConfigurationImpl.DEFAULT_POLLING; + public readonly baseUri = DEFAULT_POLLING; public readonly eventsUri = ServiceEndpoints.DEFAULT_EVENTS; - public readonly streamUri = ConfigurationImpl.DEFAULT_STREAM; + public readonly streamUri = DEFAULT_STREAM; public readonly maxCachedContexts = 5; diff --git a/packages/shared/sdk-client/src/configuration/index.ts b/packages/shared/sdk-client/src/configuration/index.ts index 496ab796e..cdb5c1344 100644 --- a/packages/shared/sdk-client/src/configuration/index.ts +++ b/packages/shared/sdk-client/src/configuration/index.ts @@ -1,3 +1,14 @@ -import ConfigurationImpl from './Configuration'; +import ConfigurationImpl, { + Configuration, + DEFAULT_POLLING, + DEFAULT_STREAM, + LDClientInternalOptions, +} from './Configuration'; -export default ConfigurationImpl; +export { + Configuration, + ConfigurationImpl, + LDClientInternalOptions, + DEFAULT_POLLING, + DEFAULT_STREAM, +}; diff --git a/packages/shared/sdk-client/src/context/addAutoEnv.ts b/packages/shared/sdk-client/src/context/addAutoEnv.ts index 1a39d7057..682a44071 100644 --- a/packages/shared/sdk-client/src/context/addAutoEnv.ts +++ b/packages/shared/sdk-client/src/context/addAutoEnv.ts @@ -11,7 +11,7 @@ import { Platform, } from '@launchdarkly/js-sdk-common'; -import ConfigurationImpl from '../configuration'; +import { Configuration } from '../configuration'; import digest from '../crypto/digest'; import { getOrGenerateKey } from '../storage/getOrGenerateKey'; import { namespaceForGeneratedContextKey } from '../storage/namespaceUtils'; @@ -39,7 +39,7 @@ export const toMulti = (c: LDSingleKindContext) => { */ export const addApplicationInfo = async ( { crypto, info }: Platform, - { applicationInfo }: ConfigurationImpl, + { applicationInfo }: Configuration, ): Promise => { const { ld_application } = info.platformData(); let app = deepCompact(ld_application) ?? ({} as LDApplication); @@ -103,11 +103,7 @@ export const addDeviceInfo = async (platform: Platform) => { return undefined; }; -export const addAutoEnv = async ( - context: LDContext, - platform: Platform, - config: ConfigurationImpl, -) => { +export const addAutoEnv = async (context: LDContext, platform: Platform, config: Configuration) => { // LDUser is not supported for auto env reporting if (isLegacyUser(context)) { return context as LDUser; diff --git a/packages/shared/sdk-client/src/diagnostics/createDiagnosticsInitConfig.ts b/packages/shared/sdk-client/src/diagnostics/createDiagnosticsInitConfig.ts index 603cbefdc..e3230b05c 100644 --- a/packages/shared/sdk-client/src/diagnostics/createDiagnosticsInitConfig.ts +++ b/packages/shared/sdk-client/src/diagnostics/createDiagnosticsInitConfig.ts @@ -1,6 +1,6 @@ import { secondsToMillis, ServiceEndpoints } from '@launchdarkly/js-sdk-common'; -import ConfigurationImpl from '../configuration'; +import { Configuration, DEFAULT_POLLING, DEFAULT_STREAM } from '../configuration'; export type DiagnosticsInitConfig = { // client & server common properties @@ -17,9 +17,9 @@ export type DiagnosticsInitConfig = { usingSecureMode: boolean; bootstrapMode: boolean; }; -const createDiagnosticsInitConfig = (config: ConfigurationImpl): DiagnosticsInitConfig => ({ - customBaseURI: config.baseUri !== ConfigurationImpl.DEFAULT_POLLING, - customStreamURI: config.streamUri !== ConfigurationImpl.DEFAULT_STREAM, +const createDiagnosticsInitConfig = (config: Configuration): DiagnosticsInitConfig => ({ + customBaseURI: config.baseUri !== DEFAULT_POLLING, + customStreamURI: config.streamUri !== DEFAULT_STREAM, customEventsURI: config.eventsUri !== ServiceEndpoints.DEFAULT_EVENTS, eventsCapacity: config.capacity, eventsFlushIntervalMillis: secondsToMillis(config.flushInterval), diff --git a/packages/shared/sdk-client/src/diagnostics/createDiagnosticsManager.ts b/packages/shared/sdk-client/src/diagnostics/createDiagnosticsManager.ts index 22b397b59..15ac0f4b1 100644 --- a/packages/shared/sdk-client/src/diagnostics/createDiagnosticsManager.ts +++ b/packages/shared/sdk-client/src/diagnostics/createDiagnosticsManager.ts @@ -1,11 +1,11 @@ import { internal, Platform } from '@launchdarkly/js-sdk-common'; -import ConfigurationImpl from '../configuration'; +import { Configuration } from '../configuration'; import createDiagnosticsInitConfig from './createDiagnosticsInitConfig'; const createDiagnosticsManager = ( clientSideID: string, - config: ConfigurationImpl, + config: Configuration, platform: Platform, ) => { if (config.sendEvents && !config.diagnosticOptOut) { diff --git a/packages/shared/sdk-client/src/events/createEventProcessor.ts b/packages/shared/sdk-client/src/events/createEventProcessor.ts index fbeec26b1..ab4887925 100644 --- a/packages/shared/sdk-client/src/events/createEventProcessor.ts +++ b/packages/shared/sdk-client/src/events/createEventProcessor.ts @@ -1,10 +1,10 @@ import { ClientContext, internal, LDHeaders, Platform } from '@launchdarkly/js-sdk-common'; -import ConfigurationImpl from '../configuration'; +import { Configuration } from '../configuration'; const createEventProcessor = ( clientSideID: string, - config: ConfigurationImpl, + config: Configuration, platform: Platform, baseHeaders: LDHeaders, diagnosticsManager?: internal.DiagnosticsManager, diff --git a/packages/shared/sdk-client/src/flag-manager/FlagManager.ts b/packages/shared/sdk-client/src/flag-manager/FlagManager.ts index 1a780f759..915a81407 100644 --- a/packages/shared/sdk-client/src/flag-manager/FlagManager.ts +++ b/packages/shared/sdk-client/src/flag-manager/FlagManager.ts @@ -6,22 +6,51 @@ import { DefaultFlagStore } from './FlagStore'; import FlagUpdater, { FlagsChangeCallback } from './FlagUpdater'; import { ItemDescriptor } from './ItemDescriptor'; +/** + * Top level manager of flags for the client. LDClient should be using this + * interface and not any of the specific instances managed by it. Updates from + * data sources should be directed to the [init] and [upsert] methods of this + * interface. + */ export interface FlagManager { + /** + * Attempts to get a flag by key from the current flags. + */ get(key: string): ItemDescriptor | undefined; + + /** + * Gets all the current flags. + */ getAll(): { [key: string]: ItemDescriptor }; + + /** + * Initializes the flag manager with data from a data source. + * Persistence initialization is handled by {@link FlagPersistence} + */ init(context: Context, newFlags: { [key: string]: ItemDescriptor }): Promise; + + /** + * Attempt to update a flag. If the flag is for the wrong context, or + * it is of an older version, then an update will not be performed. + */ upsert(context: Context, key: string, item: ItemDescriptor): Promise; + + /** + * Asynchronously load cached values from persistence. + */ loadCached(context: Context): Promise; + + /** + * Register a flag change callback. + */ on(callback: FlagsChangeCallback): void; + + /** + * Unregister a flag change callback. + */ off(callback: FlagsChangeCallback): void; } -/** - * Top level manager of flags for the client. LDClient should be using this - * class and not any of the specific instances managed by it. Updates from - * data sources should be directed to the [init] and [upsert] methods of this - * class. - */ export default class DefaultFlagManager implements FlagManager { private flagStore = new DefaultFlagStore(); private flagUpdater: FlagUpdater; @@ -71,53 +100,30 @@ export default class DefaultFlagManager implements FlagManager { ); } - /** - * Attempts to get a flag by key from the current flags. - */ get(key: string): ItemDescriptor | undefined { return this.flagStore.get(key); } - /** - * Gets all the current flags. - */ getAll(): { [key: string]: ItemDescriptor } { return this.flagStore.getAll(); } - /** - * Initializes the flag manager with data from a data source. - * Persistence initialization is handled by {@link FlagPersistence} - */ async init(context: Context, newFlags: { [key: string]: ItemDescriptor }): Promise { return (await this.flagPersistencePromise).init(context, newFlags); } - /** - * Attempt to update a flag. If the flag is for the wrong context, or - * it is of an older version, then an update will not be performed. - */ async upsert(context: Context, key: string, item: ItemDescriptor): Promise { return (await this.flagPersistencePromise).upsert(context, key, item); } - /** - * Asynchronously load cached values from persistence. - */ async loadCached(context: Context): Promise { return (await this.flagPersistencePromise).loadCached(context); } - /** - * Register a flag change callback. - */ on(callback: FlagsChangeCallback): void { this.flagUpdater.on(callback); } - /** - * Unregister a flag change callback. - */ off(callback: FlagsChangeCallback): void { this.flagUpdater.off(callback); }