Skip to content

Commit b8e16f3

Browse files
committed
refactor: shared config builder
1 parent 61679d9 commit b8e16f3

6 files changed

Lines changed: 226 additions & 202 deletions

File tree

packages/sdk/browser/contract-tests/entity/src/ClientEntity.ts

Lines changed: 31 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,12 @@ import {
88
} from '@launchdarkly/js-client-sdk';
99
import {
1010
ClientEntity,
11+
ConfigBuilder,
1112
CreateInstanceParams,
1213
IClientEntity,
13-
makeSdkConfig,
14-
parseClientOptions,
1514
SDKConfigDataInitializer,
1615
SDKConfigDataSynchronizer,
1716
SDKConfigModeDefinition,
18-
SDKConfigParams,
1917
} from '@launchdarkly/js-contract-test-utils/client';
2018

2119
function translateInitializer(init: SDKConfigDataInitializer): InitializerEntry | undefined {
@@ -71,25 +69,28 @@ function translateModeDefinition(modeDef: SDKConfigModeDefinition): ModeDefiniti
7169
return { initializers, synchronizers };
7270
}
7371

74-
/**
75-
* Browser-specific makeSdkConfig that wraps the shared base config with
76-
* FDv2 data system translation and browser-specific options.
77-
*/
78-
function makeBrowserSdkConfig(options: SDKConfigParams, tag: string): LDOptions {
79-
const isSet = (x?: unknown) => x !== null && x !== undefined;
80-
const maybeTime = (seconds?: number) => (isSet(seconds) ? seconds! / 1000 : undefined);
72+
export async function newSdkClientEntity(
73+
_id: string,
74+
options: CreateInstanceParams,
75+
): Promise<IClientEntity> {
76+
const config = new ConfigBuilder(options).set({ fetchGoals: false });
8177

82-
const cf = { ...makeSdkConfig(options, tag), fetchGoals: false } as LDOptions;
78+
if (options.configuration.dataSystem) {
79+
// FDv2: skip legacy streaming — data system handles connection modes
80+
config.skip('streaming');
8381

84-
if (options.dataSystem?.payloadFilter) {
85-
cf.payloadFilterKey = options.dataSystem.payloadFilter;
86-
}
82+
const isSet = (x?: unknown) => x !== null && x !== undefined;
83+
const maybeTime = (seconds?: number) => (isSet(seconds) ? seconds! / 1000 : undefined);
84+
const fdv2Overrides: Record<string, unknown> = {};
85+
86+
if (options.configuration.dataSystem.payloadFilter) {
87+
fdv2Overrides.payloadFilterKey = options.configuration.dataSystem.payloadFilter;
88+
}
8789

88-
if (options.dataSystem) {
8990
const dataSystem: any = {};
9091

91-
if (options.dataSystem.connectionModeConfig) {
92-
const connMode = options.dataSystem.connectionModeConfig;
92+
if (options.configuration.dataSystem.connectionModeConfig) {
93+
const connMode = options.configuration.dataSystem.connectionModeConfig;
9394
dataSystem.automaticModeSwitching = connMode.initialConnectionMode
9495
? { type: 'manual', initialConnectionMode: connMode.initialConnectionMode }
9596
: false;
@@ -99,58 +100,49 @@ function makeBrowserSdkConfig(options: SDKConfigParams, tag: string): LDOptions
99100
Object.entries(connMode.customConnectionModes).forEach(([modeName, modeDef]) => {
100101
connectionModes[modeName] = translateModeDefinition(modeDef);
101102

102-
// Per-entry endpoint overrides also set global URIs for ServiceEndpoints
103-
// compatibility. These override the serviceEndpoints values above.
104103
(modeDef.synchronizers ?? []).forEach((sync) => {
105104
if (sync.streaming?.baseUri) {
106-
cf.streamUri = sync.streaming.baseUri;
107-
cf.streamInitialReconnectDelay = maybeTime(sync.streaming.initialRetryDelayMs);
105+
fdv2Overrides.streamUri = sync.streaming.baseUri;
106+
fdv2Overrides.streamInitialReconnectDelay = maybeTime(
107+
sync.streaming.initialRetryDelayMs,
108+
);
108109
}
109110
if (sync.polling?.baseUri) {
110-
cf.baseUri = sync.polling.baseUri;
111+
fdv2Overrides.baseUri = sync.polling.baseUri;
111112
}
112113
});
113114
(modeDef.initializers ?? []).forEach((init) => {
114115
if (init.polling?.baseUri) {
115-
cf.baseUri = init.polling.baseUri;
116+
fdv2Overrides.baseUri = init.polling.baseUri;
116117
}
117118
});
118119
});
119120
dataSystem.connectionModes = connectionModes;
120121
}
121122
}
122123

123-
(cf as any).dataSystem = dataSystem;
124+
fdv2Overrides.dataSystem = dataSystem;
125+
config.set(fdv2Overrides);
124126
}
125127

126-
return cf;
127-
}
128-
129-
export async function newSdkClientEntity(
130-
_id: string,
131-
options: CreateInstanceParams,
132-
): Promise<IClientEntity> {
133-
const { timeout, initialContext, credential, initCanFail } = parseClientOptions(options);
134-
135-
const sdkConfig = makeBrowserSdkConfig(options.configuration, options.tag);
136-
137-
const client = createClient(credential, initialContext as LDContext, sdkConfig);
128+
const sdkConfig = config.build() as LDOptions;
129+
const client = createClient(config.credential, config.initialContext as LDContext, sdkConfig);
138130

139131
let failed = false;
140132
try {
141133
await Promise.race([
142134
client.start(),
143135
new Promise((_resolve, reject) => {
144-
setTimeout(reject, timeout);
136+
setTimeout(reject, config.timeout);
145137
}),
146138
]);
147139
} catch (_) {
148140
failed = true;
149141
}
150-
if (failed && !initCanFail) {
142+
if (failed && !config.initCanFail) {
151143
client.close();
152144
throw new Error('client initialization failed');
153145
}
154146

155-
return new ClientEntity(client, options.tag);
147+
return new ClientEntity(client, config.tag);
156148
}
Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import {
22
ClientEntity,
3+
ConfigBuilder,
34
CreateInstanceParams,
45
IClientEntity,
5-
parseClientOptions,
66
} from '@launchdarkly/js-contract-test-utils/client';
77
import {
88
AutoEnvAttributes,
@@ -15,24 +15,12 @@ export async function newSdkClientEntity(
1515
_id: string,
1616
options: CreateInstanceParams,
1717
): Promise<IClientEntity> {
18-
const { timeout, sdkConfig, initialContext, initCanFail } = parseClientOptions(options);
19-
20-
// Add RN-specific options
21-
const rnConfig: LDOptions = {
22-
...sdkConfig,
18+
const config = new ConfigBuilder(options).skip('streaming').set({
2319
automaticNetworkHandling: false,
2420
automaticBackgroundHandling: false,
25-
} as LDOptions;
26-
27-
// RN uses initialConnectionMode instead of streaming boolean
28-
if (options.configuration.polling) {
29-
rnConfig.initialConnectionMode = 'polling';
30-
}
31-
if (options.configuration.streaming) {
32-
rnConfig.initialConnectionMode = 'streaming';
33-
}
34-
// Remove the browser-style streaming field — RN doesn't use it
35-
delete (rnConfig as Record<string, unknown>).streaming;
21+
...(options.configuration.polling && { initialConnectionMode: 'polling' }),
22+
...(options.configuration.streaming && { initialConnectionMode: 'streaming' }),
23+
});
3624

3725
const autoEnvAttributes = options.configuration.clientSide?.includeEnvironmentAttributes
3826
? AutoEnvAttributes.Enabled
@@ -41,27 +29,27 @@ export async function newSdkClientEntity(
4129
const client = new ReactNativeLDClient(
4230
options.configuration.credential || 'unknown-mobile-key',
4331
autoEnvAttributes,
44-
rnConfig,
32+
config.build() as LDOptions,
4533
);
4634

4735
let failed = false;
4836
try {
4937
await Promise.race([
50-
client.identify(initialContext as LDContext, {
51-
timeout: timeout / 1000,
38+
client.identify(config.initialContext as LDContext, {
39+
timeout: config.timeout / 1000,
5240
waitForNetworkResults: true,
5341
}),
5442
new Promise((_resolve, reject) => {
55-
setTimeout(reject, timeout);
43+
setTimeout(reject, config.timeout);
5644
}),
5745
]);
5846
} catch (_) {
5947
failed = true;
6048
}
61-
if (failed && !initCanFail) {
49+
if (failed && !config.initCanFail) {
6250
client.close();
6351
throw new Error('client initialization failed');
6452
}
6553

66-
return new ClientEntity(client, options.tag);
54+
return new ClientEntity(client, config.tag);
6755
}

packages/sdk/react/contract-tests/app/ClientRoot.tsx

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import React, { useEffect, useRef, useState } from 'react';
44

55
import {
66
Capability,
7+
ConfigBuilder,
78
IClientEntity,
8-
parseClientOptions,
99
TestHarnessWebSocketBuilder,
1010
} from '@launchdarkly/js-contract-test-utils/client';
1111
import {
@@ -42,7 +42,6 @@ export default function ClientRoot({ children }: { children: React.ReactNode })
4242
const commandHandlers = useRef(new Map<string, CommandHandler>());
4343
const handlerReadyMap = useRef(new Map<string, () => void>());
4444
const clientsRef = useRef<ClientRecord[]>([]);
45-
// Entity map for the WS builder's get/delete callbacks
4645
const entityMap = useRef(new Map<string, IClientEntity>());
4746

4847
const onReady = (readyId: string) => {
@@ -53,18 +52,17 @@ export default function ClientRoot({ children }: { children: React.ReactNode })
5352
const ws = new TestHarnessWebSocketBuilder()
5453
.setCapabilities(CAPABILITIES)
5554
.onCreateClient(async (id, params) => {
56-
const { timeout, sdkConfig, initialContext, credential, initCanFail } =
57-
parseClientOptions(params);
55+
const config = new ConfigBuilder(params).set({ fetchGoals: false });
5856

5957
const client = createClient(
60-
credential,
61-
initialContext as LDContext,
62-
{ ...sdkConfig, fetchGoals: false } as LDOptions,
58+
config.credential,
59+
config.initialContext as LDContext,
60+
config.build() as LDOptions,
6361
);
6462

65-
const { status } = await client.start({ timeout: timeout / 1000 });
63+
const { status } = await client.start({ timeout: config.timeout / 1000 });
6664

67-
if (status === 'failed' && !initCanFail) {
65+
if (status === 'failed' && !config.initCanFail) {
6866
client.close();
6967
throw new Error('client initialization failed');
7068
}

0 commit comments

Comments
 (0)