Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
44 changes: 44 additions & 0 deletions packages/sdk/browser/example/README.md
Original file line number Diff line number Diff line change
@@ -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.
11 changes: 11 additions & 0 deletions packages/sdk/browser/example/index.css
Original file line number Diff line number Diff line change
@@ -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;
}
11 changes: 11 additions & 0 deletions packages/sdk/browser/example/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>
26 changes: 26 additions & 0 deletions packages/sdk/browser/example/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "@launchdarkly/browser-example",
"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",
"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:^"
},
"devDependencies": {
"tsdown": "^0.17.0-beta.4",
"typescript": "^5.9.3"
}
}
46 changes: 46 additions & 0 deletions packages/sdk/browser/example/src/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { initialize } from '@launchdarkly/js-client-sdk';

// Set clientSideID to your LaunchDarkly client-side ID
const clientSideID = '';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we could set this up to work with a .env, or regular environment variable, then that would be good.

Generally speaking this approach results in accidental commits of tokens. Client-side IDs are not secret, so it isn't a big issue, but still nice to not have to worry about it.

If you do use a .env, then you can make sure it is in the git ignore.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(We can do the same thing with the flagKey, for convenience.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea for this kind of example, I think we can inject those values during build time... I'll refactor this example, once we get the waitForInit PR in.


// 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...'));
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);
};

ldclient.on('error', () => {
div.replaceChild(document.createTextNode('Error caught in client SDK'), div.firstChild as Node);
});

ldclient.on('change', () => {
render();
});

ldclient.identify(context).catch(() => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we moved to a result returning method, we shouldn't need the catch, correct? Or we need to make sure we cannot throw from that method.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can assume top-level await? Then just await the result?

div.replaceChild(document.createTextNode('Error identifying client'), div.firstChild as Node);
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Identify error handling uses catch but promise never rejects

The .catch() handler on ldclient.identify(context) will never execute because the SDK's identify() method returns a promise that doesn't reject on errors. Per the SDK's documented behavior, the promise "will not be rejected" - instead, errors are returned as an LDIdentifyResult object with status: 'error'. To properly handle identify failures, the code would need to use .then() and check result.status for error conditions.

Fix in Cursor Fix in Web

} catch (error) {
div.replaceChild(
document.createTextNode(`Error initializing LaunchDarkly client: ${error}`),
div.firstChild as Node,
);
document.body.style.background = '#373841';
}
22 changes: 22 additions & 0 deletions packages/sdk/browser/example/tsconfig.json
Original file line number Diff line number Diff line change
@@ -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"]
}
9 changes: 9 additions & 0 deletions packages/sdk/browser/example/tsdown.config.ts
Original file line number Diff line number Diff line change
@@ -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'],
});