Thank you for your interest in contributing to the OpenFeature JS Client! This document provides guidelines for contributing to the project, with a focus on the release process.
This is a monorepo managed with Lerna that contains multiple packages:
@datadog/flagging-core- Runtime-agnostic flag-evaluation logic@datadog/openfeature-browser- Browser-specific bindings for OpenFeature@datadog/openfeature-node-server- Node.js server bindings for OpenFeature
The project uses independent versioning, meaning each package can have its own version number. Internal dependencies (e.g., @datadog/flagging-core) are pinned to exact versions on release (via command.version.exact in lerna.json) to prevent version skew.
-
Install dependencies:
yarn install
-
Build all packages:
yarn build
-
Run tests:
yarn test -
Type checking:
yarn typecheck
-
Linting:
yarn lint yarn lint:fix # Auto-fix issues
Run yarn example:react-native to build and pack the local core package and prepare
an isolated Expo app. The same command reuses a per-checkout cache directory outside
the repository and reinstalls a fresh core tarball on every run. Stop Metro before
refreshing, then follow the printed cd / npm start commands to launch Expo Go
on an iOS/Android simulator or device. The app verifies evaluation on Hermes and
supports exports-enabled, legacy CommonJS, and legacy ESM Metro modes.
See the manual app README for prerequisites,
expected results, and how to rebuild after SDK changes. Unlike the automated
yarn test:react-native-install smoke test, this runs the code in a native JS runtime,
not Node.
The default @datadog/flagging-core and @datadog/openfeature-browser entrypoints are expected to stay optimized for precomputed configurations. Rules-based parsing and its Protobuf-ES dependency must remain behind the ./rules-based entrypoints.
For a new core subpath, add its source module and package.json.exports mapping, then run the
normal core build. Compatibility manifests, declaration mappings, publish-file lists, bundles,
and smoke-test imports are derived automatically. See Adding a public entry point.
Commit the generated metadata; yarn check:entrypoints rejects drift before CI builds or packing.
Recurring checks keep these contracts visible:
-
yarn test:buildchecks generated entrypoint metadata and tests the generator, including adding a second/nested export without bundler registration. -
yarn test:react-native-installdiscovers every packed JavaScript subpath and checks modern and legacy resolution in the Metro matrix. -
packages/core/test/entrypoint-boundaries.spec.tswalks runtime imports from the default core/browser source entrypoints and fails if they reach generated protobuf code, Protobuf-ES, or rules-only parser modules. -
yarn test:browser-installbuilds the packed browser smoke app and runsscripts/report-entrypoint-bundle-sizes.js. The report comparesDatadogProvider(precomputed fetching) withDatadogCoreProviderplusfetchRulesConfiguration(local rules evaluation), using the same OpenFeature initialization, boolean evaluation, and context-change flow with telemetry disabled for both. Playwright supplies configuration responses outside the measured bundles. These are complete scenario JS sizes, including OpenFeature and the shared harness, not isolated SDK or decoder sizes. Additional parser/fetch-wrapper smoke scenarios remain part of the dependency checks, summarized as a pass/fail result rather than a size table. Tracking-hook sizes are reported separately. In GitHub Actions the report is also appended to the step summary, pull request CI updates a sticky comment with the same report, and the script fails if default/precomputed bundles contain protobuf markers or rules-based bundles lack them. -
yarn test:node-installbuilds independent Webpack production bundles from the installed tarballs for the Node provider, SSR helpers (fetch client rules and serialize), and both together.scripts/report-node-entrypoint-bundle-sizes.jsreports raw/gzip JS sizes and the same-build cost of adding SSR helpers to the provider. It selects the ESMmoduleexport condition, bundles dependencies except Node built-ins, and checks Webpack module statistics to ensure that only SSR bundles include Protobuf runtime or generated schema modules. The bundled functions are tested with fixture responses; the fixtures are not bundled. OpenFeature and dd-trace are not included. These are bundler scenario sizes, not npm install sizes, unbundled Node import costs, payload sizes, latency, or a direct comparison with the browser scenarios. To regenerate the report after installation, runnode scripts/report-node-entrypoint-bundle-sizes.jsandnode --test test-app-node/bundle.test.cjs.
The sticky PR comment combines browser and Node reports after package-install checks. If only one report was generated, it identifies the missing report. The comment is only created or updated when the PR changes a bundle-relevant path. The allowlist in scripts/comment-entrypoint-bundle-sizes.js covers packages/browser/**, packages/core/**, packages/node-server/**, test-app/**, test-app-node/**, scripts/build/**, scripts/lib/**, the package-install/report/webpack-runner scripts, root dependency and build configuration, .yarn/**, and .github/workflows/ci.yaml. Markdown files are excluded. The filter checks the full PR diff, including deletions and both paths of renames, and publishes conservatively if GitHub's 3,000-file listing limit is reached. Report generation, the CI summary, smoke tests, and dependency checks still run regardless of the changed paths. yarn test:build covers the reports, filter, and comment creation/update behavior with mocked GitHub responses.
- Ensure you're not on the
mainbranch (releases should be done from feature branches) - All tests must pass
- Code must be linted and type-checked
- Changes should be committed and pushed
- Proper GitHub secrets must be configured:
NPM_PUBLISH_TOKEN_FLAGGING_CORE- Token for publishing core packageNPM_PUBLISH_TOKEN- Token for publishing browser package
The project supports different build modes that affect how the SDK version is determined:
- Default mode when
BUILD_MODEis not set - SDK version is set to
"dev" - Used during development and testing
- Used for public releases
- SDK version uses the version from the package's
package.json - This is the mode used for production releases
- Used on staging and production Datadog web app
- SDK version format:
{package-version}-{commit-sha} - Example:
0.1.0-alpha.2-a1b2c3d4
Run builds through yarn build, yarn workspace <package-name> build, or from the package directory. The build scripts read package.json from their working directory; release and canary builds fail if the package version cannot be read. Development builds continue to use dev.
yarn test:build verifies package-specific version stamping and runs in regular CI.
The publishing job sets BUILD_MODE=release for all steps, including the prepack rebuilds triggered by packing and publishing. CI also runs the packed-browser smoke test in release mode and verifies that configuration requests report the installed package's version.
The project also supports different SDK setups:
npm(default) - For npm package distributioncdn- For CDN distribution
All packages are published with the latest npm tag.
-
Switch to a feature branch:
# For independent releases (describe what's being released) git checkout -b release/node-server-1.3.0 git checkout -b release/browser-and-node-server-1.3.0 # For unified releases (all packages with same version) git checkout -b release/v1.2.3
-
Ensure internal dependencies use exact versions:
yarn node ./scripts/release/update-peer-dependency-versions.js
This script reads each package's current version and updates internal dependencies (like
@datadog/flagging-core) to use exact versions without the^prefix. This prevents version skew between packages.After running, verify the changes:
git diff packages/*/package.jsonYou should see changes like:
- "@datadog/flagging-core": "^1.2.1" + "@datadog/flagging-core": "1.2.1"
-
Update the version using the CLI:
For independent releases (recommended):
yarn release
This command:
- Validates you're not on the
mainbranch - Runs
lerna version --exact - Prompts for version updates only for changed packages
- Creates version commits and tags per package (e.g.,
@datadog/openfeature-node-server@1.3.0) - Pushes version tags to Github
For unified releases (all packages with same version):
yarn release:all
This command:
- Same as above, but uses
--force-publishto prompt for all packages - Use this when you want to release all packages together with the same version
- Validates you're not on the
-
Open a PR from your release branch and merge it with a merge commit — not a squash.
yarn releasepushes the version tag onto your branch commit. Squashing creates a new commit onmainand orphans that tag, which breaks change detection on the next release (Lerna falls back to an old tag and prompts to version packages that never changed). A merge commit keeps the tag reachable.
Publishing is fully automated via GitHub workflows!
-
Create a GitHub Release:
- Go to the GitHub repository
- Click "Releases" → "Create a new release"
- Set the tag to match your version:
- Independent:
@datadog/openfeature-node-server@1.3.0(publishes only that package) - Unified:
v1.2.1(publishes all packages)
- Independent:
- Add release notes describing your changes or use the
Generate Release Notesbutton - Click "Publish release"
For multiple independent releases: Create a separate GitHub release for each package tag (e.g., one for
@datadog/openfeature-browser@1.3.0and one for@datadog/openfeature-node-server@1.3.0). -
Automated Publishing Workflow:
The
release.yamlworkflow will automatically trigger and:Validation Phase:
- Checks that the GitHub release tag matches the corresponding package version
- Fails fast if validation doesn't pass
Build and Publish Phase:
- Installs dependencies with
yarn install --immutable - Builds all packages in release mode (
BUILD_MODE=release) - Creates package tarballs with
yarn lerna run pack --stream
Publishing Sequence:
For independent releases (
@datadog/pkg@x.y.z):- Publishes only the specified package
- If publishing
browserornode-server, waits forflagging-coreto be available on npm first
For unified releases (
vX.Y.Z):- Publishes
@datadog/flagging-corefirst - Waits for npm registry propagation (up to 5 minutes)
- Publishes
@datadog/openfeature-browser - Publishes
@datadog/openfeature-node-server
# Build all formats (CommonJS and ESM)
cd packages/core
yarn build
# Build CommonJS only
yarn build:cjs
# Build ESM only
yarn build:esm
# Create package tarball
yarn pack# Build all formats (CommonJS, ESM, and bundle)
cd packages/browser
yarn build
# Build bundle for CDN
SDK_SETUP=cdn yarn build:bundle
# Build CommonJS only
yarn build:cjs
# Build ESM only
yarn build:esm
# Create package tarball
yarn pack-
BUILD_MODE: Controls the SDK version formatdev(default) - Development versionrelease- Production release versioncanary- Canary version with commit SHA
-
SDK_SETUP: Controls the SDK setup typenpm(default) - For npm distributioncdn- For CDN distribution
Since this project uses independent versioning:
- Each package has its own version number (stored in its
package.json) yarn releaseprompts for version updates only for changed packages (independent releases)yarn release:allprompts for all packages (unified releases)- Internal dependencies are pinned to exact versions (configured via
command.version.exactinlerna.json) - Version commits and tags are created per package (e.g.,
@datadog/openfeature-node-server@1.3.0)
⚠️ Version policy for@datadog/flagging-core: Internal dependencies are pinned to exact versions (enforced byscripts/internal-deps-validate.sh), so our packages never pull a core update implicitly. From2.0.0onward,flagging-corefollows normal semver — minor/patch for backward-compatible changes, major for breaking ones. Two rules still apply:
- Do not publish new
1.xversions offlagging-core. Legacy consumers still on^1.2.1would pull them and risk version skew; the2.0.0major bump exists to cap those consumers below2.x.- Bumping core does not reach dependents automatically. Because
openfeature-browserandopenfeature-node-serverpin core exactly, you must update each dependent's pin and re-release it (see Step 2) for consumers to pick up the new core.
The GitHub Actions workflow (release.yaml) includes several safety measures:
-
Version Consistency Check:
- Compares GitHub release tag with the corresponding package version
- Ensures tags and package versions are synchronized
-
Dependency Coordination:
- Core package is published first
- Waits for npm registry propagation (up to 5 minutes)
- Browser package gets updated core dependency automatically
-
Build Integrity:
- Uses
BUILD_MODE=releasefor production builds - Replaces build environment variables correctly
- Creates both npm packages and CDN bundles
- Uses
-
Run all tests:
yarn test -
Type checking:
yarn typecheck
-
Linting:
yarn lint
-
Build verification:
yarn clean yarn build yarn build:bundle
-
Package creation test:
yarn version # Pins internal dependencies and creates package tarballs
-
Release from main branch:
- Error: "please do not release from
mainbranch" - Solution: Create a feature branch for releases
- Error: "please do not release from
-
Version mismatch in GitHub workflow:
- Error: "Release tag doesn't match lerna.json version"
- Solution: Ensure the GitHub release tag exactly matches
v{version}format where{version}is fromlerna.json
-
Build environment issues:
- Ensure
BUILD_MODEandSDK_SETUPare set correctly - Check that all dependencies are installed
- Ensure
-
Version synchronization issues:
- Run
yarn node ./scripts/release/update-peer-dependency-versions.jsto pin internal dependencies - Verify internal dependencies use exact versions (no
^prefix)
- Run
-
GitHub workflow failures:
- Check the Actions tab for detailed error logs
- Ensure GitHub secrets are properly configured:
NPM_PUBLISH_TOKEN_FLAGGING_COREfor core packageNPM_PUBLISH_TOKENfor browser package
- Verify the release tag matches the version in
lerna.json
-
npm registry propagation delays:
- The workflow waits up to 5 minutes for the core package to be available
- If this fails, it may indicate npm registry issues
- Check npm status page or try republishing manually
-
Webpack build issues:
- Ensure all TypeScript configurations are valid
- Check that webpack configurations in each package are correct
- Verify all required dependencies are installed
- Check the README.md for basic project information
- Review the scripts in the
scripts/directory for implementation details - Check the GitHub Actions tab for workflow status and logs
- Examine the
scripts/cliscript for available commands (release,release_all,version,typecheck,lint) - Open an issue on GitHub for bugs or feature requests
If the automated workflow fails and you need to publish manually:
-
Build packages:
BUILD_MODE=release yarn build yarn version
-
Publish core package:
cd packages/core npm publish --tag latest -
Wait for propagation, then publish remaining packages:
cd packages/browser npm publish --tag latest cd ../node-server npm publish --tag latest
All third-party dependency licenses are tracked in LICENSE-3rdparty.csv. This file is
auto-generated and must be kept up to date whenever dependencies change. CI will fail
if it is stale.
Re-generate the file whenever you add, remove, or update a dependency in any package.json.
| Requirement | Details |
|---|---|
| Python 3.11.12 | pyenv install 3.11.12 && pyenv local 3.11.12 |
| Go 1.23+ | Required by dd-license-attribution |
| dd-license-attribution | pip install dd-license-attribution (repo) |
| GITHUB_TOKEN | See below |
For Datadog employees, see the internal dd-license-attribution guide.
If you already use the GitHub CLI, the easiest option is:
export GITHUB_TOKEN=$(gh auth token)Otherwise, create a fine-grained personal access token with read access to Contents and Metadata at https://github.com/settings/personal-access-tokens and export it:
export GITHUB_TOKEN="github_pat_..."export GITHUB_TOKEN=$(gh auth token)
yarn licenses:generateThis overwrites LICENSE-3rdparty.csv with the latest data. Commit the result.
yarn licenses:validateThis checks that every npm package in yarn.lock has a corresponding entry in the CSV.
No external tools or tokens are needed — it runs in CI the same way. If it fails, run
yarn licenses:generate and commit the result.
- Use TypeScript for all new code
- Follow the existing code style and patterns
- Run
yarn lint:fixbefore committing - Ensure all tests pass before submitting changes
Follow conventional commit format with gitmoji:
✨ feat:for new features🐛 fix:for bug fixes📝 docs:for documentation changes🎨 style:for formatting changes♻️ refactor:for code refactoring✅ test:for test changes👷 chore:for maintenance tasks
Example: ✨ feat(browser): add new flag evaluation method