diff --git a/packages/sdk/svelte/__tests__/lib/client/SvelteLDClient.test.ts b/packages/sdk/svelte/__tests__/lib/client/SvelteLDClient.test.ts index 551d82852..cf8fdd3af 100644 --- a/packages/sdk/svelte/__tests__/lib/client/SvelteLDClient.test.ts +++ b/packages/sdk/svelte/__tests__/lib/client/SvelteLDClient.test.ts @@ -18,7 +18,7 @@ const mockLDClient = { on: (e: string, cb: () => void) => mockLDEventEmitter.on(e, cb), off: vi.fn(), allFlags: vi.fn().mockReturnValue(rawFlags), - variation: vi.fn(), + variation: vi.fn((_, defaultValue) => defaultValue), identify: vi.fn(), }; @@ -130,6 +130,9 @@ describe('launchDarkly', () => { const flagStore = ld.watch(booleanFlagKey); const flagStore2 = ld.watch(stringFlagKey); + // emit ready event to set initial flag values + mockLDEventEmitter.emit('ready'); + // 'test-flag' initial value is true according to `rawFlags` expect(get(flagStore)).toBe(true); // 'another-test-flag' intial value is 'flag-value' according to `rawFlags` diff --git a/packages/sdk/svelte/package.json b/packages/sdk/svelte/package.json index cf3914e34..bb972b19c 100644 --- a/packages/sdk/svelte/package.json +++ b/packages/sdk/svelte/package.json @@ -44,13 +44,11 @@ }, "peerDependencies": { "@launchdarkly/js-client-sdk": "workspace:^", - "@launchdarkly/js-client-sdk-common": "^1.10.0", "@launchdarkly/node-server-sdk": "^9.4.6", "svelte": "^4.0.0" }, "dependencies": { "@launchdarkly/js-client-sdk": "workspace:^", - "@launchdarkly/js-client-sdk-common": "1.10.0", "esm-env": "^1.0.0" }, "devDependencies": { diff --git a/packages/sdk/svelte/src/lib/client/SvelteLDClient.ts b/packages/sdk/svelte/src/lib/client/SvelteLDClient.ts index a251a37d3..0b3103056 100644 --- a/packages/sdk/svelte/src/lib/client/SvelteLDClient.ts +++ b/packages/sdk/svelte/src/lib/client/SvelteLDClient.ts @@ -27,12 +27,43 @@ function isClientInitialized(client: LDClient | undefined): asserts client is LD } } +/** + * Creates a proxy for the given flags object that intercepts access to flag values. + * When a flag value is accessed, it checks if the flag key exists in the target object. + * If the flag key exists, it returns the variation of the flag from the client. + * Otherwise, it returns the current value of the flag. + * + * @param client - The LaunchDarkly client instance used to get flag variations. + * @param flags - The initial flags object to be proxied. + * @returns A proxy object that intercepts access to flag values and returns the appropriate variation. + */ +function toFlagsProxy(client: LDClient, flags: LDFlags): LDFlags { + return new Proxy(flags, { + get(target, prop, receiver) { + const currentValue = Reflect.get(target, prop, receiver); + // only process flag keys and ignore symbols and native Object functions + if (typeof prop === 'symbol') { + return currentValue; + } + + // check if flag key exists + const validFlagKey = Object.hasOwn(target, prop); + + if (!validFlagKey) { + return currentValue; + } + + return client.variation(prop, currentValue); + }, + }); +} + /** * Creates a LaunchDarkly instance. * @returns {Object} The LaunchDarkly instance object. */ function createLD() { - let jsSdk: LDClient | undefined; + let coreLdClient: LDClient | undefined; const loading = writable(true); const flagsWritable = writable({}); @@ -43,15 +74,17 @@ function createLD() { */ function LDInitialize(clientId: LDClientID) { - jsSdk = initialize(clientId); - jsSdk!.on('ready', () => { + coreLdClient = initialize(clientId); + coreLdClient!.on('ready', () => { loading.set(false); - const allFlags = jsSdk!.allFlags(); + const rawFlags = coreLdClient!.allFlags(); + const allFlags = toFlagsProxy(coreLdClient, rawFlags); flagsWritable.set(allFlags); }); - jsSdk!.on('change', () => { - const allFlags = jsSdk!.allFlags(); + coreLdClient!.on('change', () => { + const rawFlags = coreLdClient!.allFlags(); + const allFlags = toFlagsProxy(coreLdClient, rawFlags); flagsWritable.set(allFlags); }); @@ -66,8 +99,8 @@ function createLD() { * @returns {Promise} A promise that resolves when the user is identified. */ async function identify(context: LDContext) { - isClientInitialized(jsSdk); - return jsSdk.identify(context); + isClientInitialized(coreLdClient); + return coreLdClient.identify(context); } /** @@ -84,7 +117,7 @@ function createLD() { * @returns {boolean} True if the flag is on, false otherwise. */ const isOn = (flagKey: string): boolean => { - isClientInitialized(jsSdk); + isClientInitialized(coreLdClient); const currentFlags = get(flagsWritable); return !!currentFlags[flagKey]; };