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 @@ -43,7 +43,8 @@
"contract-tests",
"packages/sdk/combined-browser",
"packages/sdk/shopify-oxygen",
"packages/sdk/shopify-oxygen/contract-tests"
"packages/sdk/shopify-oxygen/contract-tests",
"packages/sdk/shopify-oxygen/example"
],
"private": true,
"scripts": {
Expand Down
30 changes: 30 additions & 0 deletions packages/sdk/shopify-oxygen/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# LaunchDarkly sample Shopify Oxygen application

We've built a simple console 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#/).
<!-- NOTE: no official docs in LD website yet
or the [{name of SDK} reference guide](https://docs.launchdarkly.com/sdk/{path to the page for that SDK}).
-->

This demo requires `Node >= 22.0.0` and `yarn@^3.4.1`

## Build instructions

1. Edit [`src/index.ts`](src/index.ts) and set the value of `sdkKey` to your LaunchDarkly SDK key.
```
const sdkKey = "1234567890abcdef";
```

2. If there is an existing boolean feature flag in your LaunchDarkly project that
you want to evaluate, set `flagKey` to the flag key:
```
const flagKey = "my-flag-key";
```
> Otherwise, `sample-feature` will be used by default.

3. On the command line, run `yarn start`, You should receive the message:
```
The {flagKey} feature flag evaluates to {flagValue}.
```
The application will run continuously and react to the flag changes in LaunchDarkly.
90 changes: 90 additions & 0 deletions packages/sdk/shopify-oxygen/example/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { createMiniOxygen } from '@shopify/mini-oxygen';

/* eslint-disable no-console */

/**
* This is script is a simple runner for our example app. This script will run
* the compiled example on a local worker implementation to emulate a Oxygen worker runtime.
*
* For the actual example implementation, see the src/index.ts file.
*/

const printValueAndBanner = (flagKey: string, flagValue: string) => {
console.log(`*** The '${flagKey}' feature flag evaluates to ${flagValue}.`);

if (flagValue) {
console.log(
` ██
██
████████
███████
██ LAUNCHDARKLY █
███████
████████
██
██
`,
);
}
};

const main = async () => {
// NOTE: you will see logging coming from mini-oxygen's default request hook.
// https://github.com/Shopify/hydrogen/blob/5a38948133766e358c5f357f52562f6fdcfe7969/packages/mini-oxygen/src/worker/index.ts#L225
const miniOxygen = createMiniOxygen({
debug: false,
workers: [
{
name: 'main',
modules: true,
scriptPath: 'dist/index.js',
},
],
});

miniOxygen.ready.then(() => {
console.log('Oxygen worker is started...');
console.log('Press "q" or Ctrl+C to quit...');

// Dispatch fetch every 5 seconds
const interval = setInterval(() => {
// NOTE: This is a bogus URL and will not be used in the actual fetch handler.
// please see the src/index.ts file for the actual fetch handler.
miniOxygen
.dispatchFetch('https://localhost:8000' as any)
.then((d) => d.json() as Promise<any>)
.then(({ flagValue, flagKey }) => {
console.clear();
printValueAndBanner(flagKey, flagValue);
console.log('Press "q" or Ctrl+C to quit...');
})
.catch((err) => {
console.log('Error dispatching fetch:', err.message);
console.log('Press "q" or Ctrl+C to quit...');
});
}, 1000);

// Handle keypresses for cleanup
process.stdin.setRawMode(true);
process.stdin.resume();
process.stdin.setEncoding('utf8');

process.stdin.on('data', async (key: string) => {
// Handle Ctrl+C
if (key === '\u0003') {
clearInterval(interval);
await miniOxygen.dispose();
process.exit();
}

// Handle 'q' key
if (key === 'q' || key === 'Q') {
clearInterval(interval);
await miniOxygen.dispose();
process.exit();
}
});
});
};

main().catch(console.error);
19 changes: 19 additions & 0 deletions packages/sdk/shopify-oxygen/example/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "shopify-oxygen-sdk-example",
"packageManager": "[email protected]",
"scripts": {
"build": "tsup",
"clean": "rm -rf dist",
"start": "yarn clean && yarn build && ts-node app.ts"
},
"type": "module",
"dependencies": {
"@launchdarkly/shopify-oxygen-sdk": "latest",
Copy link

Choose a reason for hiding this comment

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

Bug: Example uses latest instead of workspace version

The example package specifies "@launchdarkly/shopify-oxygen-sdk": "latest" which fetches from npm instead of using the local workspace version. Other packages in the monorepo use "*" to reference workspace packages. This causes the example to use a published version rather than the local development version, which breaks the development workflow and could fail if the package isn't published.

Fix in Cursor Fix in Web

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this is fine? If we are only looking at this from a POV of a how-to example, then I'd argue using latest published version is probably more correct.

"@shopify/mini-oxygen": "^4.0.0",
"ts-node": "^10.9.2",
"typescript": "^5.9.3"
},
"devDependencies": {
"tsup": "^8.5.1"
}
}
31 changes: 31 additions & 0 deletions packages/sdk/shopify-oxygen/example/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { init } from '@launchdarkly/shopify-oxygen-sdk';

// Set sdkKey to your LaunchDarkly SDK key.
const sdkKey = 'sample-sdk-key';

// Set featureFlagKey to the feature flag key you want to evaluate.
const flagKey = 'sample-feature';

const context = {
kind: 'user',
key: 'example-user-key',
name: 'Sandy',
};

const sdkOptions = {
// See the README.md file for more information on the options.
};

export default {
async fetch() {
const ldClient = await init(sdkKey, sdkOptions);
await ldClient.waitForInitialization({ timeout: 10 });
const flagValue = await ldClient.variation(flagKey, context, false);

// Flush events and close the client
await ldClient.flush();
ldClient.close();

return new Response(JSON.stringify({ flagKey, flagValue }), { status: 200 });
},
};
20 changes: 20 additions & 0 deletions packages/sdk/shopify-oxygen/example/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"declaration": true,
"declarationMap": true,
"lib": ["es6"],
"module": "es2022",
"moduleResolution": "node",
"noImplicitOverride": true,
"outDir": "dist",
"resolveJsonModule": true,
"rootDir": ".",
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"stripInternal": true,
"target": "ES2022",
},
"exclude": ["tsup.config.ts"]
}
16 changes: 16 additions & 0 deletions packages/sdk/shopify-oxygen/example/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// It is a dev dependency and the linter doesn't understand.
// @ts-ignore - tsup is a dev dependency installed at runtime
// eslint-disable-next-line import/no-extraneous-dependencies
import { defineConfig } from 'tsup';

export default defineConfig({
entry: {
index: 'src/index.ts',
},
minify: true,
format: ['esm'],
splitting: false,
clean: true,
noExternal: ['@launchdarkly/shopify-oxygen-sdk'],
dts: true,
});