Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
e6a4d76
feat: add FDv2 types, refined validators, and DataManager interface e…
kinyoklion Mar 19, 2026
a4d5ad0
feat: add FlagManager.applyChanges for FDv2 full/partial/none semantics
kinyoklion Mar 19, 2026
cb9d6b6
feat: add SourceFactoryProvider for declarative data source creation
kinyoklion Mar 19, 2026
c33790c
Merge branch 'rlamb/fdv2-flag-manager-apply-changes' into rlamb/fdv2-…
kinyoklion Mar 19, 2026
455e057
feat: add FDv2DataManagerBase for mode switching and data source life…
kinyoklion Mar 19, 2026
20f3d28
Merge branch 'main' into rlamb/fdv2-source-factory-provider
kinyoklion Mar 20, 2026
a908f6f
feat: wire FDv2 data manager into BrowserClient
kinyoklion Mar 20, 2026
68895af
feat: add FDv2 contract test wiring, suppressions, and example app
kinyoklion Mar 20, 2026
97568e4
fix: address PR review feedback on SourceFactoryProvider
kinyoklion Mar 20, 2026
25121e3
Merge branch 'rlamb/fdv2-source-factory-provider' into rlamb/fdv2-dat…
kinyoklion Mar 20, 2026
b1ca6c1
fix: update FDv2DataManagerBase for restructured SourceFactoryContext
kinyoklion Mar 20, 2026
de39213
fix: increase bundle size limits for common and sdk-client packages
kinyoklion Mar 20, 2026
d9711c4
Merge branch 'rlamb/fdv2-source-factory-provider' into rlamb/fdv2-dat…
kinyoklion Mar 20, 2026
2acb9ab
Merge branch 'rlamb/fdv2-data-manager-base' into rlamb/fdv2-browser-c…
kinyoklion Mar 20, 2026
d8a5cd8
Merge branch 'rlamb/fdv2-browser-client-integration' into rlamb/fdv2-…
kinyoklion Mar 20, 2026
a343b8b
fix: add applyChanges to FlagUpdater/FlagStore to bypass FDv1 version…
kinyoklion Mar 20, 2026
a076479
Merge branch 'rlamb/fdv2-flag-manager-apply-changes' into rlamb/fdv2-…
kinyoklion Mar 20, 2026
90e6af6
Merge branch 'rlamb/fdv2-data-manager-base' into rlamb/fdv2-browser-c…
kinyoklion Mar 20, 2026
c3af194
Merge branch 'rlamb/fdv2-browser-client-integration' into rlamb/fdv2-…
kinyoklion Mar 20, 2026
95dabe4
Merge remote-tracking branch 'origin/main' into rlamb/fdv2-flag-manag…
kinyoklion Mar 20, 2026
83cb188
Merge remote-tracking branch 'origin/main' into rlamb/fdv2-data-manag…
kinyoklion Mar 20, 2026
9887de7
Merge branch 'rlamb/fdv2-data-manager-base' into rlamb/fdv2-browser-c…
kinyoklion Mar 20, 2026
96bacc3
Merge branch 'rlamb/fdv2-browser-client-integration' into rlamb/fdv2-…
kinyoklion Mar 20, 2026
80852ff
fix: increase browser SDK bundle size limit
kinyoklion Mar 20, 2026
01bb153
Merge branch 'rlamb/fdv2-flag-manager-apply-changes' into rlamb/fdv2-…
kinyoklion Mar 20, 2026
b434d8f
Merge branch 'rlamb/fdv2-data-manager-base' into rlamb/fdv2-browser-c…
kinyoklion Mar 20, 2026
d97750e
Merge branch 'rlamb/fdv2-browser-client-integration' into rlamb/fdv2-…
kinyoklion Mar 20, 2026
eeea559
fix: delegate full applyChanges to init to avoid duplication
kinyoklion Mar 20, 2026
806b12a
Merge branch 'rlamb/fdv2-flag-manager-apply-changes' into rlamb/fdv2-…
kinyoklion Mar 20, 2026
4c4b9d0
Merge branch 'rlamb/fdv2-data-manager-base' into rlamb/fdv2-browser-c…
kinyoklion Mar 20, 2026
a490d54
Merge branch 'rlamb/fdv2-browser-client-integration' into rlamb/fdv2-…
kinyoklion Mar 20, 2026
b5e5dc3
fix: unify applyChanges at FlagStore layer for future FDv1 removal
kinyoklion Mar 20, 2026
fece197
Merge branch 'rlamb/fdv2-flag-manager-apply-changes' into rlamb/fdv2-…
kinyoklion Mar 20, 2026
87465f8
Merge branch 'rlamb/fdv2-data-manager-base' into rlamb/fdv2-browser-c…
kinyoklion Mar 20, 2026
bfaad78
Merge branch 'rlamb/fdv2-browser-client-integration' into rlamb/fdv2-…
kinyoklion Mar 20, 2026
a78c26e
Merge remote-tracking branch 'origin/main' into rlamb/fdv2-data-manag…
kinyoklion Mar 20, 2026
3fa35fd
Merge branch 'rlamb/fdv2-data-manager-base' into rlamb/fdv2-browser-c…
kinyoklion Mar 21, 2026
80a25a1
Merge branch 'rlamb/fdv2-browser-client-integration' into rlamb/fdv2-…
kinyoklion Mar 21, 2026
ecc301d
feat: refactor mode switching to discriminated union with foregroundC…
kinyoklion Mar 23, 2026
de58921
Merge branch 'rlamb/fdv2-data-manager-base' into rlamb/fdv2-browser-c…
kinyoklion Mar 23, 2026
8ac0234
fix: derive initialForegroundMode from ManualModeSwitching in Browser…
kinyoklion Mar 23, 2026
e0dc940
Merge branch 'rlamb/fdv2-browser-client-integration' into rlamb/fdv2-…
kinyoklion Mar 23, 2026
2f60e1e
fix: translate contract test initialConnectionMode to ManualModeSwitc…
kinyoklion Mar 23, 2026
76f795c
fix: add ManualModeSwitching to LDClientDataSystemOptions union type
kinyoklion Mar 23, 2026
b360da4
Merge branch 'rlamb/fdv2-data-manager-base' into rlamb/fdv2-browser-c…
kinyoklion Mar 23, 2026
09fa94a
Merge branch 'rlamb/fdv2-browser-client-integration' into rlamb/fdv2-…
kinyoklion Mar 23, 2026
a39bdb4
fix: rename initialForegroundMode to foregroundMode
kinyoklion Mar 24, 2026
a96d111
Merge branch 'rlamb/fdv2-data-manager-base' into rlamb/fdv2-browser-c…
kinyoklion Mar 24, 2026
2fecbc5
fix: rename initialForegroundMode to foregroundMode in BrowserClient
kinyoklion Mar 24, 2026
fb182ac
Merge branch 'rlamb/fdv2-browser-client-integration' into rlamb/fdv2-…
kinyoklion Mar 24, 2026
2561279
fix: simplify dataCallback by using ?? [] for transfer-none updates
kinyoklion Mar 24, 2026
a9ac466
fix: simplify dataCallback by using ?? [] for transfer-none updates
kinyoklion Mar 24, 2026
09f0a3b
Merge branch 'rlamb/fdv2-data-manager-base' into rlamb/fdv2-browser-c…
kinyoklion Mar 24, 2026
630eae0
Merge branch 'rlamb/fdv2-browser-client-integration' into rlamb/fdv2-…
kinyoklion Mar 24, 2026
659ff40
fix: unconditionally assign selector from payload state
kinyoklion Mar 24, 2026
77f5077
Merge branch 'rlamb/fdv2-data-manager-base' into rlamb/fdv2-browser-c…
kinyoklion Mar 24, 2026
160c9b9
Merge branch 'rlamb/fdv2-browser-client-integration' into rlamb/fdv2-…
kinyoklion Mar 24, 2026
9ce4858
fix: revert combined-browser size limit decrease
kinyoklion Mar 24, 2026
4e70dc0
Merge branch 'rlamb/fdv2-browser-client-integration' into rlamb/fdv2-…
kinyoklion Mar 24, 2026
5f4f9f9
fix: extract resolveForegroundMode helper with type guard
kinyoklion Mar 24, 2026
34ad09f
Merge branch 'rlamb/fdv2-browser-client-integration' into rlamb/fdv2-…
kinyoklion Mar 25, 2026
63a0ac9
fix: remove unnecessary as any cast for bootstrap check
kinyoklion Mar 25, 2026
86f3f1d
Merge branch 'rlamb/fdv2-data-manager-base' into rlamb/fdv2-browser-c…
kinyoklion Mar 25, 2026
7688344
Merge branch 'rlamb/fdv2-browser-client-integration' into rlamb/fdv2-…
kinyoklion Mar 25, 2026
104fd3e
fix: re-check closed state after await in identify to prevent resourc…
kinyoklion Mar 25, 2026
ad9a304
fix: re-check closed state after await in identify to prevent resourc…
kinyoklion Mar 25, 2026
bcd670a
Merge branch 'rlamb/fdv2-data-manager-base' into rlamb/fdv2-browser-c…
kinyoklion Mar 25, 2026
b0d31ee
Merge branch 'rlamb/fdv2-browser-client-integration' into rlamb/fdv2-…
kinyoklion Mar 25, 2026
1c7de9e
fix: update doc comments to match discriminated union API
kinyoklion Mar 25, 2026
68b4baf
fix: update doc comments to match discriminated union API
kinyoklion Mar 25, 2026
f536b54
Merge branch 'rlamb/fdv2-data-manager-base' into rlamb/fdv2-browser-c…
kinyoklion Mar 25, 2026
0ef3282
Merge branch 'rlamb/fdv2-browser-client-integration' into rlamb/fdv2-…
kinyoklion Mar 25, 2026
5233983
feat: add setConnectionMode as top-level override bypassing transitio…
kinyoklion Mar 25, 2026
1702f8d
Merge branch 'rlamb/fdv2-data-manager-base' into rlamb/fdv2-browser-c…
kinyoklion Mar 25, 2026
ef92296
Merge branch 'rlamb/fdv2-browser-client-integration' into rlamb/fdv2-…
kinyoklion Mar 25, 2026
f91a28f
fix: add .catch to flagManager.applyChanges to prevent unhandled reje…
kinyoklion Mar 25, 2026
2e9bc71
Merge branch 'rlamb/fdv2-data-manager-base' into rlamb/fdv2-browser-c…
kinyoklion Mar 25, 2026
93df491
Merge branch 'rlamb/fdv2-browser-client-integration' into rlamb/fdv2-…
kinyoklion Mar 25, 2026
154c7c1
feat: add custom is predicate to validatorOf for discriminated unions
kinyoklion Mar 25, 2026
0786b64
feat: add custom is predicate to validatorOf for discriminated unions
kinyoklion Mar 25, 2026
273af24
Merge branch 'rlamb/fdv2-data-manager-base' into rlamb/fdv2-browser-c…
kinyoklion Mar 25, 2026
3fe9c11
Merge branch 'rlamb/fdv2-browser-client-integration' into rlamb/fdv2-…
kinyoklion Mar 25, 2026
0ac3c0d
fix: forward browser streaming option to FDv2 data manager
kinyoklion Mar 25, 2026
3d17cf0
Merge branch 'rlamb/fdv2-browser-client-integration' into rlamb/fdv2-…
kinyoklion Mar 25, 2026
6c51e12
Merge remote-tracking branch 'origin/main' into rlamb/fdv2-contract-t…
kinyoklion Mar 25, 2026
74f33dc
fix: register example-fdv2 as a workspace
kinyoklion Mar 25, 2026
b0be84a
fix: resolve .env path relative to config file in example-fdv2
kinyoklion Mar 25, 2026
41c0225
fix: rename __dirname to configDir to satisfy no-underscore-dangle
kinyoklion Mar 25, 2026
3edb71a
fix: set eventsUri from serviceEndpoints for both FDv1 and FDv2 paths
kinyoklion Mar 25, 2026
3dd6306
Remove payloadFilterKey assignment from ClientEntity
kinyoklion Mar 26, 2026
037c007
fix: address PR review feedback on contract tests and example
kinyoklion Mar 26, 2026
28f6483
fix: export FDv2 configuration types from browser SDK
kinyoklion Mar 26, 2026
fd965f6
feat: add EAP disclaimers, internalize backgroundConnectionMode and f…
kinyoklion Mar 26, 2026
f3446bf
fix: set dataSystem config when useDefaultDataSystem is true without …
kinyoklion Mar 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ stats.html
.env
.env.local
.env.*.local
.claude/worktrees
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@
"packages/sdk/shopify-oxygen",
"packages/sdk/shopify-oxygen/contract-tests",
"packages/sdk/shopify-oxygen/example",
"packages/sdk/browser/example"
"packages/sdk/browser/example",
"packages/sdk/browser/example-fdv2"
],
"private": true,
"scripts": {
Expand Down
131 changes: 117 additions & 14 deletions packages/sdk/browser/contract-tests/entity/src/ClientEntity.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import { createClient, LDClient, LDLogger, LDOptions } from '@launchdarkly/js-client-sdk';
import {
createClient,
InitializerEntry,
LDClient,
LDLogger,
LDOptions,
ModeDefinition,
SynchronizerEntry,
} from '@launchdarkly/js-client-sdk';
import {
CommandParams,
CommandType,
CreateInstanceParams,
makeLogger,
SDKConfigDataInitializer,
SDKConfigDataSynchronizer,
SDKConfigModeDefinition,
SDKConfigParams,
ClientSideTestHook as TestHook,
ValueType,
Expand All @@ -12,6 +23,59 @@ import {
export const badCommandError = new Error('unsupported command');
export const malformedCommand = new Error('command was malformed');

function translateInitializer(init: SDKConfigDataInitializer): InitializerEntry | undefined {
if (init.polling) {
return {
type: 'polling',
...(init.polling.pollIntervalMs !== undefined && {
pollInterval: init.polling.pollIntervalMs / 1000,
}),
...(init.polling.baseUri && {
endpoints: { pollingBaseUri: init.polling.baseUri },
}),
};
}
return undefined;
}

function translateSynchronizer(sync: SDKConfigDataSynchronizer): SynchronizerEntry | undefined {
if (sync.streaming) {
return {
type: 'streaming',
...(sync.streaming.initialRetryDelayMs !== undefined && {
initialReconnectDelay: sync.streaming.initialRetryDelayMs / 1000,
}),
...(sync.streaming.baseUri && {
endpoints: { streamingBaseUri: sync.streaming.baseUri },
}),
};
}
if (sync.polling) {
return {
type: 'polling',
...(sync.polling.pollIntervalMs !== undefined && {
pollInterval: sync.polling.pollIntervalMs / 1000,
}),
...(sync.polling.baseUri && {
endpoints: { pollingBaseUri: sync.polling.baseUri },
}),
};
}
return undefined;
}

function translateModeDefinition(modeDef: SDKConfigModeDefinition): ModeDefinition {
const initializers: InitializerEntry[] = (modeDef.initializers ?? [])
.map(translateInitializer)
.filter((x): x is InitializerEntry => x !== undefined);

const synchronizers: SynchronizerEntry[] = (modeDef.synchronizers ?? [])
.map(translateSynchronizer)
.filter((x): x is SynchronizerEntry => x !== undefined);

return { initializers, synchronizers };
}

function makeSdkConfig(options: SDKConfigParams, tag: string) {
if (!options.clientSide) {
throw new Error('configuration did not include clientSide options');
Expand All @@ -23,7 +87,7 @@ function makeSdkConfig(options: SDKConfigParams, tag: string) {
const cf: LDOptions = {
withReasons: options.clientSide.evaluationReasons,
logger: makeLogger(`${tag}.sdk`),
useReport: options.clientSide.useReport,
useReport: options.clientSide.useReport ?? undefined,
};

if (options.serviceEndpoints) {
Expand All @@ -32,21 +96,60 @@ function makeSdkConfig(options: SDKConfigParams, tag: string) {
cf.eventsUri = options.serviceEndpoints.events;
}

if (options.polling) {
if (options.polling.baseUri) {
cf.baseUri = options.polling.baseUri;
}
if (options.dataSystem?.payloadFilter) {
cf.payloadFilterKey = options.dataSystem.payloadFilter;
}

// Can contain streaming and polling, if streaming is set override the initial connection
// mode. This can be removed when we add JS specific initialization that uses polling
// and then streaming.
if (options.streaming) {
if (options.streaming.baseUri) {
cf.streamUri = options.streaming.baseUri;
if (options.dataSystem) {
const dataSystem: any = {};

if (options.dataSystem.connectionModeConfig) {
const connMode = options.dataSystem.connectionModeConfig;
dataSystem.automaticModeSwitching = connMode.initialConnectionMode
? { type: 'manual', initialConnectionMode: connMode.initialConnectionMode }
: false;

if (connMode.customConnectionModes) {
const connectionModes: Record<string, any> = {};
Object.entries(connMode.customConnectionModes).forEach(([modeName, modeDef]) => {
connectionModes[modeName] = translateModeDefinition(modeDef);

// Per-entry endpoint overrides also set global URIs for ServiceEndpoints
// compatibility. These override the serviceEndpoints values above.
(modeDef.synchronizers ?? []).forEach((sync) => {
if (sync.streaming?.baseUri) {
cf.streamUri = sync.streaming.baseUri;
cf.streamInitialReconnectDelay = maybeTime(sync.streaming.initialRetryDelayMs);
}
if (sync.polling?.baseUri) {
cf.baseUri = sync.polling.baseUri;
}
});
(modeDef.initializers ?? []).forEach((init) => {
if (init.polling?.baseUri) {
cf.baseUri = init.polling.baseUri;
}
});
});
dataSystem.connectionModes = connectionModes;
}
}

(cf as any).dataSystem = dataSystem;
} else {
if (options.polling) {
if (options.polling.baseUri) {
cf.baseUri = options.polling.baseUri;
}
}

if (options.streaming) {
if (options.streaming.baseUri) {
cf.streamUri = options.streaming.baseUri;
}
cf.streaming = true;
cf.streamInitialReconnectDelay = maybeTime(options.streaming.initialRetryDelayMs);
}
cf.streaming = true;
cf.streamInitialReconnectDelay = maybeTime(options.streaming.initialRetryDelayMs);
}

if (options.events) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
streaming/requests/method and headers/REPORT/http
streaming/requests/URL path is computed correctly/no environment filter/base URI has no trailing slash/REPORT
streaming/requests/URL path is computed correctly/no environment filter/base URI has a trailing slash/REPORT
streaming/requests/query parameters/evaluationReasons set to [none]/REPORT
streaming/requests/query parameters/evaluationReasons set to false/REPORT
streaming/requests/query parameters/evaluationReasons set to true/REPORT
streaming/requests/context properties/single kind minimal/REPORT
streaming/requests/context properties/single kind with all attributes/REPORT
streaming/requests/context properties/multi-kind/REPORT
polling/requests/method and headers/REPORT/http
polling/requests/URL path is computed correctly/no environment filter/base URI has no trailing slash/REPORT
polling/requests/URL path is computed correctly/no environment filter/base URI has a trailing slash/REPORT
polling/requests/query parameters/evaluationReasons set to [none]/REPORT
polling/requests/query parameters/evaluationReasons set to false/REPORT
polling/requests/query parameters/evaluationReasons set to true/REPORT
polling/requests/context properties/single kind minimal/REPORT
polling/requests/context properties/single kind with all attributes/REPORT
polling/requests/context properties/multi-kind/REPORT
tags/stream requests/{"applicationId":null,"applicationVersion":"._-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678"}
tags/stream requests/{"applicationId":null,"applicationVersion":"________________________________________________________________"}
tags/stream requests/{"applicationId":"","applicationVersion":null}
tags/stream requests/{"applicationId":"","applicationVersion":""}
tags/stream requests/{"applicationId":"","applicationVersion":"._-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678"}
tags/stream requests/{"applicationId":"","applicationVersion":"________________________________________________________________"}
tags/stream requests/{"applicationId":"._-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678","applicationVersion":null}
tags/stream requests/{"applicationId":"._-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678","applicationVersion":""}
tags/stream requests/{"applicationId":"._-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678","applicationVersion":"._-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678"}
tags/stream requests/{"applicationId":"._-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678","applicationVersion":"________________________________________________________________"}
tags/stream requests/{"applicationId":"________________________________________________________________","applicationVersion":null}
tags/stream requests/{"applicationId":"________________________________________________________________","applicationVersion":""}
tags/stream requests/{"applicationId":"________________________________________________________________","applicationVersion":"._-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678"}
tags/stream requests/{"applicationId":"________________________________________________________________","applicationVersion":"________________________________________________________________"}
tags/poll requests/{"applicationId":null,"applicationVersion":"._-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678"}
tags/poll requests/{"applicationId":null,"applicationVersion":"________________________________________________________________"}
tags/poll requests/{"applicationId":"","applicationVersion":"._-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678"}
tags/poll requests/{"applicationId":"","applicationVersion":"________________________________________________________________"}
tags/poll requests/{"applicationId":"._-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678","applicationVersion":null}
tags/poll requests/{"applicationId":"._-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678","applicationVersion":""}
tags/poll requests/{"applicationId":"._-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678","applicationVersion":"._-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678"}
tags/poll requests/{"applicationId":"._-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678","applicationVersion":"________________________________________________________________"}
tags/poll requests/{"applicationId":"________________________________________________________________","applicationVersion":null}
tags/poll requests/{"applicationId":"________________________________________________________________","applicationVersion":""}
tags/poll requests/{"applicationId":"________________________________________________________________","applicationVersion":"._-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678"}
tags/poll requests/{"applicationId":"________________________________________________________________","applicationVersion":"________________________________________________________________"}
tags/disallowed characters
5 changes: 5 additions & 0 deletions packages/sdk/browser/example-fdv2/.env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Set LAUNCHDARKLY_CLIENT_SIDE_ID to your LaunchDarkly client-side ID
LAUNCHDARKLY_CLIENT_SIDE_ID=

# Set LAUNCHDARKLY_FLAG_KEY to the feature flag key you want to evaluate
LAUNCHDARKLY_FLAG_KEY=
44 changes: 44 additions & 0 deletions packages/sdk/browser/example-fdv2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# LaunchDarkly sample javascript application

We've built a simple browser application that demonstrates how this LaunchDarkly SDK works.

Below, you'll find the build procedure. For more comprehensive instructions, you can visit your [Quickstart page](https://app.launchdarkly.com/quickstart#/) or
the [{name of SDK} reference guide](https://docs.launchdarkly.com/sdk/client-side/javascript).

## Prerequisites

Nodejs 20.6.0 or later

## Build instructions

1. Make a copy of the `.env.template` and name it `.env`
```
cp .env.template .env
```
2. Set the variables in `.env` to your specific LD values
```
# Set LAUNCHDARKLY_CLIENT_SIDE_ID to your LaunchDarkly client-side ID
LAUNCHDARKLY_CLIENT_SIDE_ID=
# Set LAUNCHDARKLY_FLAG_KEY to the feature flag key you want to evaluate
LAUNCHDARKLY_FLAG_KEY=
```
> [!NOTE]
> Setting these values is equivilent to modifying the `clientSideID` and `flagKey`
> in [app.ts](./src/app.ts).
3. Install and build the project:
```bash
yarn && yarn build
```
4. On the command line, run `yarn start`
```bash
yarn start
```
> [!NOTE]
> The `yarn start` script simply runs `open index.html`. If that is not working for you,
> you can open the `index.html` file in a browser for the same results.
The application will run continuously and react to the flag changes in LaunchDarkly.
9 changes: 9 additions & 0 deletions packages/sdk/browser/example-fdv2/e2e/verify.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { expect, test as it } from '@playwright/test';

it('evaluates the feature flag to true', async ({ page }) => {
await page.goto('/');
await expect(page.locator('body')).toContainText('feature flag evaluates to true', {
timeout: 20_000,
});
});
69 changes: 69 additions & 0 deletions packages/sdk/browser/example-fdv2/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
body {
margin: 0;
padding: 20px;
background: #373841;
color: white;
font-family:
-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell',
'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

#status {
padding: 10px;
margin-bottom: 10px;
background: rgba(255,255,255,0.1);
border-radius: 4px;
}

#flag {
font-size: 1.4em;
padding: 15px;
margin-bottom: 20px;
background: rgba(255,255,255,0.05);
border-radius: 4px;
}

#controls {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
}

#controls > div {
padding: 10px;
background: rgba(255,255,255,0.05);
border-radius: 4px;
}

#controls h3 {
margin: 0 0 8px 0;
font-size: 0.9em;
text-transform: uppercase;
opacity: 0.7;
}

button {
padding: 6px 14px;
margin: 3px 2px;
border: 1px solid rgba(255,255,255,0.3);
border-radius: 4px;
background: rgba(255,255,255,0.1);
color: white;
cursor: pointer;
font-size: 0.9em;
}

button:hover {
background: rgba(255,255,255,0.2);
}

#log {
max-height: 200px;
overflow-y: auto;
font-family: monospace;
font-size: 0.8em;
line-height: 1.5;
opacity: 0.8;
}
11 changes: 11 additions & 0 deletions packages/sdk/browser/example-fdv2/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge;chrome=1">
<title>LaunchDarkly tutorial</title>
<script src="./dist/app.js" defer></script>
<link rel="stylesheet" href="./index.css">
</head>
<body></body>
</html>
29 changes: 29 additions & 0 deletions packages/sdk/browser/example-fdv2/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "@launchdarkly/browser-example-fdv2",
"version": "0.0.0",
"private": true,
"description": "LaunchDarkly example for JavaScript Browser SDK",
"homepage": "https://github.com/launchdarkly/js-core/tree/main/packages/sdk/browser/example-fdv2",
"repository": {
"type": "git",
"url": "https://github.com/launchdarkly/js-core.git"
},
"license": "Apache-2.0",
"packageManager": "yarn@3.4.1",
"type": "module",
"scripts": {
"start": "open index.html",
"clean": "rm -rf dist dist-static",
"build": "npm run clean && tsdown",
"test": "playwright test"
},
"dependencies": {
"@launchdarkly/js-client-sdk": "workspace:^"
},
"devDependencies": {
"@playwright/test": "^1.49.1",
"playwright": "^1.49.1",
"tsdown": "^0.17.0-beta.4",
"typescript": "^5.9.3"
}
}
Loading
Loading