Skip to content

Commit 0486ac8

Browse files
authored
chore: add an example app for browser sdk (#1019)
This PR will add a new browser SDK example that works with the new 4.x implementation. I think this also addressed sdk-744 <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Adds a new browser SDK example app with build/run tooling and includes it in workspaces. > > - **Example app** (`packages/sdk/browser/example`): > - **App code**: Adds `index.html`, `index.css`, and `src/app.ts` demonstrating `initialize`, `identify`, `variation`, and `change`/`error` listeners with simple UI updates. > - **Build/run**: Adds `package.json` scripts (`start`, `build`), `tsconfig.json`, and `tsdown.config.ts` that substitutes `LD_CLIENT_SIDE_ID`/`LD_FLAG_KEY` from `.env.template` into `dist/app.js`. > - **Docs**: Adds `README.md` with setup instructions. > - **Monorepo**: > - Adds `packages/sdk/browser/example` to root `package.json` `workspaces`. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 4ba5b0b. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 7a3af02 commit 0486ac8

File tree

9 files changed

+227
-1
lines changed

9 files changed

+227
-1
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@
4444
"packages/sdk/combined-browser",
4545
"packages/sdk/shopify-oxygen",
4646
"packages/sdk/shopify-oxygen/contract-tests",
47-
"packages/sdk/shopify-oxygen/example"
47+
"packages/sdk/shopify-oxygen/example",
48+
"packages/sdk/browser/example"
4849
],
4950
"private": true,
5051
"scripts": {
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Set LD_CLIENT_SIDE_ID to your LaunchDarkly client-side ID
2+
LD_CLIENT_SIDE_ID=
3+
4+
# Set LD_FLAG_KEY to the feature flag key you want to evaluate
5+
LD_FLAG_KEY=
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# LaunchDarkly sample javascript application
2+
3+
# ⛔️⛔️⛔️⛔️
4+
5+
> [!CAUTION]
6+
> This example is created against a non-production SDK which means things may change and this example might
7+
> not work while this message is visible.
8+
9+
# ☝️☝️☝️☝️☝️☝️
10+
11+
We've built a simple browser application that demonstrates how this LaunchDarkly SDK works.
12+
13+
Below, you'll find the build procedure. For more comprehensive instructions, you can visit your [Quickstart page](https://app.launchdarkly.com/quickstart#/) or
14+
the [{name of SDK} reference guide](https://docs.launchdarkly.com/sdk/client-side/javascript).
15+
16+
## Prerequisites
17+
18+
Nodejs 20.6.0 or later
19+
20+
## Build instructions
21+
22+
1. Make a copy of the `.env.template` and name it `.env`
23+
```
24+
cp .env.template .env
25+
```
26+
27+
2. Set the variables in `.env` to your specific LD values
28+
```
29+
# Set LD_CLIENT_SIDE_ID to your LaunchDarkly client-side ID
30+
LD_CLIENT_SIDE_ID=
31+
32+
# Set LD_FLAG_KEY to the feature flag key you want to evaluate
33+
LD_FLAG_KEY=
34+
```
35+
> [!NOTE]
36+
> Setting these values is equivilent to modifying the `clientSideID` and `flagKey`
37+
> in [app.ts](./src/app.ts).
38+
39+
3. Install and build the project:
40+
```bash
41+
yarn && yarn build
42+
```
43+
44+
4. On the command line, run `yarn start`
45+
```bash
46+
yarn start
47+
```
48+
> [!NOTE]
49+
> The `yarn start` script simply runs `open index.html`. If that is not working for you,
50+
> you can open the `index.html` file in a browser for the same results.
51+
52+
The application will run continuously and react to the flag changes in LaunchDarkly.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
body {
2+
margin: 0;
3+
background: #373841;
4+
color: white;
5+
font-family:
6+
-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell',
7+
'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
8+
-webkit-font-smoothing: antialiased;
9+
-moz-osx-font-smoothing: grayscale;
10+
text-align: center;
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge;chrome=1">
6+
<title>LaunchDarkly tutorial</title>
7+
<script src="./dist/app.js" defer></script>
8+
<link rel="stylesheet" href="./index.css">
9+
</head>
10+
<body></body>
11+
</html>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "@launchdarkly/browser-example",
3+
"version": "0.0.0",
4+
"private": true,
5+
"description": "LaunchDarkly example for JavaScript Browser SDK",
6+
"homepage": "https://github.com/launchdarkly/js-core/tree/main/packages/sdk/browser/example",
7+
"repository": {
8+
"type": "git",
9+
"url": "https://github.com/launchdarkly/js-core.git"
10+
},
11+
"license": "Apache-2.0",
12+
"packageManager": "[email protected]",
13+
"type": "module",
14+
"scripts": {
15+
"start": "open index.html",
16+
"clean": "rm -rf dist dist-static",
17+
"build": "npm run clean && tsdown"
18+
},
19+
"dependencies": {
20+
"@launchdarkly/js-client-sdk": "workspace:^"
21+
},
22+
"devDependencies": {
23+
"tsdown": "^0.17.0-beta.4",
24+
"typescript": "^5.9.3"
25+
}
26+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { initialize } from '@launchdarkly/js-client-sdk';
2+
3+
// Set clientSideID to your LaunchDarkly client-side ID
4+
const clientSideID = 'LD_CLIENT_SIDE_ID';
5+
6+
// Set flagKey to the feature flag key you want to evaluate
7+
const flagKey = 'LD_FLAG_KEY';
8+
9+
// Set up the evaluation context. This context should appear on your
10+
// LaunchDarkly contexts dashboard soon after you run the demo.
11+
const context = {
12+
kind: 'user',
13+
key: 'example-user-key',
14+
name: 'Sandy',
15+
};
16+
17+
const div = document.createElement('div');
18+
const statusBox = document.createElement('div');
19+
20+
document.body.appendChild(statusBox);
21+
document.body.appendChild(div);
22+
23+
div.appendChild(document.createTextNode('No flag evaluations yet'));
24+
statusBox.appendChild(document.createTextNode('Initializing...'));
25+
26+
const main = async () => {
27+
const ldclient = initialize(clientSideID);
28+
const render = () => {
29+
const flagValue = ldclient.variation(flagKey, false);
30+
const label = `The ${flagKey} feature flag evaluates to ${flagValue}.`;
31+
document.body.style.background = flagValue ? '#00844B' : '#373841';
32+
div.replaceChild(document.createTextNode(label), div.firstChild as Node);
33+
};
34+
35+
ldclient.on('error', () => {
36+
statusBox.replaceChild(
37+
document.createTextNode('Error caught in client SDK'),
38+
statusBox.firstChild as Node,
39+
);
40+
});
41+
42+
// Listen for flag changes
43+
ldclient.on('change', () => {
44+
render();
45+
});
46+
47+
const { status } = await ldclient.identify(context);
48+
49+
if (status === 'completed') {
50+
statusBox.replaceChild(document.createTextNode('Initialized'), statusBox.firstChild as Node);
51+
} else if (status === 'error') {
52+
statusBox.replaceChild(
53+
document.createTextNode('Error identifying client'),
54+
statusBox.firstChild as Node,
55+
);
56+
}
57+
58+
render();
59+
};
60+
61+
main();
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"compilerOptions": {
3+
"allowSyntheticDefaultImports": true,
4+
"declaration": true,
5+
"declarationMap": true,
6+
"lib": ["ES2017", "dom"],
7+
"module": "ESNext",
8+
"moduleResolution": "node",
9+
"noImplicitOverride": true,
10+
"resolveJsonModule": true,
11+
"rootDir": ".",
12+
"outDir": "dist",
13+
"skipLibCheck": true,
14+
"sourceMap": true,
15+
"inlineSources": true,
16+
"strict": true,
17+
"stripInternal": true,
18+
"target": "ES2017",
19+
"allowJs": true
20+
},
21+
"include": ["src"]
22+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/* eslint-disable import/no-extraneous-dependencies */
2+
import fs from 'node:fs';
3+
import path from 'node:path';
4+
import { loadEnvFile } from 'node:process';
5+
import { defineConfig } from 'tsdown';
6+
7+
if (fs.existsSync('.env')) {
8+
loadEnvFile('.env');
9+
}
10+
11+
const ENTRY_FILE = path.join('src', 'app.ts');
12+
const OUTPUT_FILE = path.join('dist', 'app.js');
13+
const { LD_CLIENT_SIDE_ID, LD_FLAG_KEY } = process.env;
14+
15+
const CLIENT_SIDE_ID_PLACEHOLDER = 'LD_CLIENT_SIDE_ID';
16+
const FLAG_KEY_PLACEHOLDER = 'LD_FLAG_KEY';
17+
18+
export default defineConfig({
19+
entry: ENTRY_FILE,
20+
platform: 'browser',
21+
outDir: 'dist',
22+
noExternal: ['@launchdarkly/js-client-sdk'],
23+
hooks(hooks) {
24+
hooks.hook('build:done', () => {
25+
if (LD_CLIENT_SIDE_ID) {
26+
const content = fs.readFileSync(OUTPUT_FILE).toString();
27+
fs.writeFileSync(
28+
OUTPUT_FILE,
29+
content.replaceAll(CLIENT_SIDE_ID_PLACEHOLDER, LD_CLIENT_SIDE_ID),
30+
);
31+
}
32+
const flagKey = LD_FLAG_KEY || 'sample-feature';
33+
const content = fs.readFileSync(OUTPUT_FILE).toString();
34+
fs.writeFileSync(OUTPUT_FILE, content.replaceAll(FLAG_KEY_PLACEHOLDER, flagKey));
35+
});
36+
},
37+
});

0 commit comments

Comments
 (0)