Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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.
86 changes: 86 additions & 0 deletions packages/sdk/shopify-oxygen/example/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {createMiniOxygen} from '@shopify/mini-oxygen';

/**
* 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, flagValue) => {
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('Dispatching fetch every 5 seconds. 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')
.then(d => d.json())
.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);
Copy link

Choose a reason for hiding this comment

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

Bug: Polling Frequency Mismatch: Documentation vs. Reality

The console message and comment state "Dispatching fetch every 5 seconds" but the setInterval uses 1000 milliseconds (1 second). This mismatch between documentation and implementation means the application polls 5 times more frequently than users are told to expect.

Fix in Cursor Fix in Web


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

process.stdin.on('data', async (key) => {
// 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();
}
});
});
Copy link

Choose a reason for hiding this comment

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

Bug: Unhandled Promises Threaten Application Stability

The miniOxygen.ready.then() promise chain lacks error handling. If miniOxygen.ready rejects, it will cause an unhandled promise rejection since the promise chain is detached from the main() function's error handling. This could cause the application to crash or behave unexpectedly without proper error reporting.

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.

this is fine

}

main().catch(console.error);
17 changes: 17 additions & 0 deletions packages/sdk/shopify-oxygen/example/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "shopify-oxygen-sdk-example",
"packageManager": "[email protected]",
"scripts": {
"build": "tsup",
"clean": "rm -rf dist",
"start": "yarn clean && yarn build && yarn node app.js"
},
"type": "module",
"dependencies": {
"@launchdarkly/shopify-oxygen-sdk": "latest",
"@shopify/mini-oxygen": "^4.0.0"
},
"devDependencies": {
"tsup": "^8.5.1"
}
}
27 changes: 27 additions & 0 deletions packages/sdk/shopify-oxygen/example/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
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);

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,
});