From 10487270ed0e3577c21bfc46ae07cb09a1988666 Mon Sep 17 00:00:00 2001 From: Steven Zhang Date: Mon, 1 Dec 2025 13:48:28 -0600 Subject: [PATCH 1/2] chore: add an example app for browser sdk --- package.json | 3 +- packages/sdk/browser/example/README.md | 44 +++++++++++++++++++ packages/sdk/browser/example/index.css | 11 +++++ packages/sdk/browser/example/index.html | 11 +++++ packages/sdk/browser/example/package.json | 27 ++++++++++++ packages/sdk/browser/example/src/app.ts | 42 ++++++++++++++++++ packages/sdk/browser/example/tsconfig.json | 22 ++++++++++ packages/sdk/browser/example/tsdown.config.ts | 9 ++++ 8 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 packages/sdk/browser/example/README.md create mode 100644 packages/sdk/browser/example/index.css create mode 100644 packages/sdk/browser/example/index.html create mode 100644 packages/sdk/browser/example/package.json create mode 100644 packages/sdk/browser/example/src/app.ts create mode 100644 packages/sdk/browser/example/tsconfig.json create mode 100644 packages/sdk/browser/example/tsdown.config.ts diff --git a/package.json b/package.json index 87317618e..11e71f91d 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,8 @@ "packages/sdk/combined-browser", "packages/sdk/shopify-oxygen", "packages/sdk/shopify-oxygen/contract-tests", - "packages/sdk/shopify-oxygen/example" + "packages/sdk/shopify-oxygen/example", + "packages/sdk/browser/example" ], "private": true, "scripts": { diff --git a/packages/sdk/browser/example/README.md b/packages/sdk/browser/example/README.md new file mode 100644 index 000000000..09294d194 --- /dev/null +++ b/packages/sdk/browser/example/README.md @@ -0,0 +1,44 @@ +# LaunchDarkly sample javascript application + +# ⛔️⛔️⛔️⛔️ + +> [!CAUTION] +> This example is created against a non-production SDK which means things may change and this example might +> not work while this message is visible. + +# ☝️☝️☝️☝️☝️☝️ + +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). + +## Build instructions + +Modify [app.ts](./src/app.ts) with the following changes: + +1. Set the value of the {`clientSideID`} variable in {file name} to your client-side ID: + ```typescript + const clientSideID = "my-client-side-id"; + ``` + +2. If there is an existing boolean feature flag in your LaunchDarkly project that + you want to evaluate, set `flagKey` to the flag key: + ```typescript + const flagKey = "my-flag-key"; + ``` + +3. If you haven't already, 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. \ No newline at end of file diff --git a/packages/sdk/browser/example/index.css b/packages/sdk/browser/example/index.css new file mode 100644 index 000000000..90dc2b50f --- /dev/null +++ b/packages/sdk/browser/example/index.css @@ -0,0 +1,11 @@ +body { + margin: 0; + 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; + text-align: center; +} diff --git a/packages/sdk/browser/example/index.html b/packages/sdk/browser/example/index.html new file mode 100644 index 000000000..26e38b21d --- /dev/null +++ b/packages/sdk/browser/example/index.html @@ -0,0 +1,11 @@ + + + + + + LaunchDarkly tutorial + + + + + diff --git a/packages/sdk/browser/example/package.json b/packages/sdk/browser/example/package.json new file mode 100644 index 000000000..122b9cabb --- /dev/null +++ b/packages/sdk/browser/example/package.json @@ -0,0 +1,27 @@ +{ + "name": "@launchdarkly/browser-example", + "version": "0.0.0", + "prviate": true, + "description": "LaunchDarkly example for JavaScript Browser SDK", + "homepage": "https://github.com/launchdarkly/js-core/tree/main/packages/sdk/browser/example", + "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" + }, + "dependencies": { + "@launchdarkly/js-client-sdk": "workspace:^", + "express": "^5.1.0" + }, + "devDependencies": { + "tsdown": "^0.17.0-beta.4", + "typescript": "^5.9.3" + } +} diff --git a/packages/sdk/browser/example/src/app.ts b/packages/sdk/browser/example/src/app.ts new file mode 100644 index 000000000..9b93c3e29 --- /dev/null +++ b/packages/sdk/browser/example/src/app.ts @@ -0,0 +1,42 @@ +import { initialize } from '@launchdarkly/js-client-sdk'; + +// Set clientSideID to your LaunchDarkly client-side ID +const clientSideID = ''; + +// Set flagKey to the feature flag key you want to evaluate +const flagKey = 'sample-feature'; + +// Set up the evaluation context. This context should appear on your +// LaunchDarkly contexts dashboard soon after you run the demo. +const context = { + kind: 'user', + key: 'example-user-key', + name: 'Sandy', +}; + +const div = document.createElement('div'); +document.body.appendChild(div); +div.appendChild(document.createTextNode('Initializing...')); + +const ldclient = initialize(clientSideID); + +function render() { + const flagValue = ldclient.variation(flagKey, false); + const label = `The ${flagKey} feature flag evaluates to ${flagValue}.`; + document.body.style.background = flagValue ? '#00844B' : '#373841'; + div.replaceChild(document.createTextNode(label), div.firstChild as Node); +} + +ldclient.identify(context).then(() => { + ldclient.on('initialized', () => { + div.replaceChild( + document.createTextNode('SDK successfully initialized!'), + div.firstChild as Node, + ); + }); + ldclient.on('failed', () => { + div.replaceChild(document.createTextNode('SDK failed to initialize'), div.firstChild as Node); + }); + ldclient.on('ready', render); + ldclient.on('change', render); +}); diff --git a/packages/sdk/browser/example/tsconfig.json b/packages/sdk/browser/example/tsconfig.json new file mode 100644 index 000000000..a1aec48ce --- /dev/null +++ b/packages/sdk/browser/example/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "declaration": true, + "declarationMap": true, + "lib": ["ES2017", "dom"], + "module": "ESNext", + "moduleResolution": "node", + "noImplicitOverride": true, + "resolveJsonModule": true, + "rootDir": ".", + "outDir": "dist", + "skipLibCheck": true, + "sourceMap": true, + "inlineSources": true, + "strict": true, + "stripInternal": true, + "target": "ES2017", + "allowJs": true + }, + "include": ["src"] +} diff --git a/packages/sdk/browser/example/tsdown.config.ts b/packages/sdk/browser/example/tsdown.config.ts new file mode 100644 index 000000000..f040e8c0f --- /dev/null +++ b/packages/sdk/browser/example/tsdown.config.ts @@ -0,0 +1,9 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import { defineConfig } from 'tsdown'; + +export default defineConfig({ + entry: 'src/app.ts', + platform: 'browser', + outDir: 'dist', + noExternal: ['@launchdarkly/js-client-sdk'], +}); From f4ad1e74b39c6248fed97c1cedde12ad901fec1a Mon Sep 17 00:00:00 2001 From: Steven Zhang Date: Mon, 1 Dec 2025 17:17:49 -0600 Subject: [PATCH 2/2] chore: addressing cursor comments --- packages/sdk/browser/example/package.json | 5 ++- packages/sdk/browser/example/src/app.ts | 42 +++++++++++++---------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/packages/sdk/browser/example/package.json b/packages/sdk/browser/example/package.json index 122b9cabb..3d52d11fe 100644 --- a/packages/sdk/browser/example/package.json +++ b/packages/sdk/browser/example/package.json @@ -1,7 +1,7 @@ { "name": "@launchdarkly/browser-example", "version": "0.0.0", - "prviate": true, + "private": true, "description": "LaunchDarkly example for JavaScript Browser SDK", "homepage": "https://github.com/launchdarkly/js-core/tree/main/packages/sdk/browser/example", "repository": { @@ -17,8 +17,7 @@ "build": "npm run clean && tsdown" }, "dependencies": { - "@launchdarkly/js-client-sdk": "workspace:^", - "express": "^5.1.0" + "@launchdarkly/js-client-sdk": "workspace:^" }, "devDependencies": { "tsdown": "^0.17.0-beta.4", diff --git a/packages/sdk/browser/example/src/app.ts b/packages/sdk/browser/example/src/app.ts index 9b93c3e29..add826bc9 100644 --- a/packages/sdk/browser/example/src/app.ts +++ b/packages/sdk/browser/example/src/app.ts @@ -17,26 +17,30 @@ const context = { const div = document.createElement('div'); document.body.appendChild(div); div.appendChild(document.createTextNode('Initializing...')); +try { + const ldclient = initialize(clientSideID); + const render = () => { + const flagValue = ldclient.variation(flagKey, false); + const label = `The ${flagKey} feature flag evaluates to ${flagValue}.`; + document.body.style.background = flagValue ? '#00844B' : '#373841'; + div.replaceChild(document.createTextNode(label), div.firstChild as Node); + }; -const ldclient = initialize(clientSideID); - -function render() { - const flagValue = ldclient.variation(flagKey, false); - const label = `The ${flagKey} feature flag evaluates to ${flagValue}.`; - document.body.style.background = flagValue ? '#00844B' : '#373841'; - div.replaceChild(document.createTextNode(label), div.firstChild as Node); -} + ldclient.on('error', () => { + div.replaceChild(document.createTextNode('Error caught in client SDK'), div.firstChild as Node); + }); -ldclient.identify(context).then(() => { - ldclient.on('initialized', () => { - div.replaceChild( - document.createTextNode('SDK successfully initialized!'), - div.firstChild as Node, - ); + ldclient.on('change', () => { + render(); }); - ldclient.on('failed', () => { - div.replaceChild(document.createTextNode('SDK failed to initialize'), div.firstChild as Node); + + ldclient.identify(context).catch(() => { + div.replaceChild(document.createTextNode('Error identifying client'), div.firstChild as Node); }); - ldclient.on('ready', render); - ldclient.on('change', render); -}); +} catch (error) { + div.replaceChild( + document.createTextNode(`Error initializing LaunchDarkly client: ${error}`), + div.firstChild as Node, + ); + document.body.style.background = '#373841'; +}