├── .editorconfig ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── add-hook.md │ └── add-provider.md ├── component_owners.yml └── workflows │ ├── ci.yml │ ├── component-owners.yml │ ├── lint-pr.yml │ └── release-please.yml ├── .gitignore ├── .gitmodules ├── .nvmrc ├── .nxignore ├── .prettierignore ├── .prettierrc ├── .release-please-manifest.json ├── CODEOWNERS ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── assets ├── aws-ssm │ ├── create-param.png │ ├── search.png │ └── ssm-menu.png └── package.json ├── babel.config.json ├── jest.config.ts ├── jest.preset.js ├── libs ├── hooks │ ├── README.md │ └── open-telemetry │ │ ├── .eslintrc.json │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── babel.config.json │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── project.json │ │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ ├── conventions.ts │ │ │ ├── metrics │ │ │ ├── index.ts │ │ │ ├── metrics-hook.spec.ts │ │ │ └── metrics-hook.ts │ │ │ ├── otel-hook.ts │ │ │ └── traces │ │ │ ├── index.ts │ │ │ ├── tracing-hook.spec.ts │ │ │ └── tracing-hook.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json ├── providers │ ├── README.md │ ├── aws-ssm │ │ ├── .eslintrc.json │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── babel.config.json │ │ ├── jest.config.ts │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ ├── integration │ │ │ │ └── integration.spec.ts │ │ │ └── lib │ │ │ │ ├── aws-ssm-provider.spec.ts │ │ │ │ ├── aws-ssm-provider.ts │ │ │ │ ├── cache.spec.ts │ │ │ │ ├── cache.ts │ │ │ │ ├── ssm-service.spec.ts │ │ │ │ ├── ssm-service.ts │ │ │ │ └── types.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── config-cat-web │ │ ├── .eslintrc.json │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── babel.config.json │ │ ├── jest.config.ts │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── config-cat-web-provider.spec.ts │ │ │ │ └── config-cat-web-provider.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── config-cat │ │ ├── .eslintrc.json │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── babel.config.json │ │ ├── jest.config.ts │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── config-cat-provider.spec.ts │ │ │ │ └── config-cat-provider.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── env-var │ │ ├── .babelrc │ │ ├── .eslintrc.json │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── babel.config.json │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── constant-case.spec.ts │ │ │ │ ├── constant-case.ts │ │ │ │ ├── env-var-provider.spec.ts │ │ │ │ └── env-var-provider.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── flagd-web │ │ ├── .babelrc │ │ ├── .eslintrc.json │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── babel.config.json │ │ ├── jest.config.ts │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── project.json │ │ ├── src │ │ │ ├── e2e │ │ │ │ ├── constants.ts │ │ │ │ ├── index.ts │ │ │ │ ├── jest.config.ts │ │ │ │ ├── step-definitions │ │ │ │ │ ├── flag.ts │ │ │ │ │ └── index.ts │ │ │ │ └── tests │ │ │ │ │ └── provider.spec.ts │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── flagd-web-provider.spec.ts │ │ │ │ ├── flagd-web-provider.ts │ │ │ │ └── options.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── flagd │ │ ├── .babelrc │ │ ├── .eslintrc.json │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── babel.config.json │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── project.json │ │ ├── src │ │ │ ├── e2e │ │ │ │ ├── constants.ts │ │ │ │ ├── index.ts │ │ │ │ ├── jest.config.ts │ │ │ │ ├── step-definitions │ │ │ │ │ ├── configSteps.ts │ │ │ │ │ ├── contextSteps.ts │ │ │ │ │ ├── eventSteps.ts │ │ │ │ │ ├── flag.ts │ │ │ │ │ ├── flagSteps.ts │ │ │ │ │ ├── providerSteps.ts │ │ │ │ │ ├── reconnect.ts │ │ │ │ │ ├── state.ts │ │ │ │ │ └── utils.ts │ │ │ │ └── tests │ │ │ │ │ ├── flagdContainer.ts │ │ │ │ │ ├── in-process.spec.ts │ │ │ │ │ └── rpc.spec.ts │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── configuration.spec.ts │ │ │ │ ├── configuration.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── flagd-provider.spec.ts │ │ │ │ ├── flagd-provider.ts │ │ │ │ └── service │ │ │ │ ├── common │ │ │ │ ├── grpc-util.ts │ │ │ │ └── index.ts │ │ │ │ ├── grpc │ │ │ │ └── grpc-service.ts │ │ │ │ ├── in-process │ │ │ │ ├── data-fetch.ts │ │ │ │ ├── file │ │ │ │ │ ├── file-fetch.spec.ts │ │ │ │ │ └── file-fetch.ts │ │ │ │ ├── grpc │ │ │ │ │ ├── grpc-fetch.spec.ts │ │ │ │ │ └── grpc-fetch.ts │ │ │ │ ├── in-process-service.spec.ts │ │ │ │ └── in-process-service.ts │ │ │ │ └── service.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── flagsmith-client │ │ ├── .eslintrc.json │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── babel.config.json │ │ ├── jest.config.ts │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── flagsmith-client-provider.spec.ts │ │ │ │ ├── flagsmith-client-provider.ts │ │ │ │ ├── flagsmith.mocks.ts │ │ │ │ └── type-factory.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── flipt-web │ │ ├── .eslintrc.json │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── babel.config.json │ │ ├── jest.config.ts │ │ ├── jest.polyfills.js │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── project.json │ │ ├── src │ │ │ ├── __mocks__ │ │ │ │ └── state.json │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── context-transformer.spec.ts │ │ │ │ ├── context-transformer.ts │ │ │ │ ├── flipt-web-provider.spec.ts │ │ │ │ ├── flipt-web-provider.ts │ │ │ │ └── models.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── flipt │ │ ├── .eslintrc.json │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── babel.config.json │ │ ├── jest.config.ts │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── context-transformer.spec.ts │ │ │ │ ├── context-transformer.ts │ │ │ │ ├── flipt-provider.spec.ts │ │ │ │ └── flipt-provider.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── go-feature-flag-web │ │ ├── .eslintrc.json │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── babel.config.json │ │ ├── jest.config.ts │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── collector-manager.ts │ │ │ │ ├── context-transfomer.spec.ts │ │ │ │ ├── context-transformer.ts │ │ │ │ ├── controller │ │ │ │ ├── goff-api.spec.ts │ │ │ │ └── goff-api.ts │ │ │ │ ├── data-collector-hook.ts │ │ │ │ ├── errors │ │ │ │ ├── collector-error.ts │ │ │ │ ├── fetch-error.ts │ │ │ │ └── goff-error.ts │ │ │ │ ├── go-feature-flag-web-provider.spec.ts │ │ │ │ ├── go-feature-flag-web-provider.ts │ │ │ │ ├── model.ts │ │ │ │ └── test-logger.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── go-feature-flag │ │ ├── .babelrc │ │ ├── .eslintrc.json │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── babel.config.json │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── context-transfomer.spec.ts │ │ │ │ ├── context-transformer.ts │ │ │ │ ├── controller │ │ │ │ ├── cache.ts │ │ │ │ └── goff-api.ts │ │ │ │ ├── data-collector-hook.ts │ │ │ │ ├── errors │ │ │ │ ├── collector-error.ts │ │ │ │ ├── configuration-change-endpoint-not-found.ts │ │ │ │ ├── configuration-change-endpoint-unknown-err.ts │ │ │ │ ├── goff-error.ts │ │ │ │ ├── proxyNotReady.ts │ │ │ │ ├── proxyTimeout.ts │ │ │ │ ├── unauthorized.ts │ │ │ │ └── unknownError.ts │ │ │ │ ├── go-feature-flag-provider.spec.ts │ │ │ │ ├── go-feature-flag-provider.ts │ │ │ │ ├── model.ts │ │ │ │ └── test-logger.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── growthbook-client │ │ ├── .eslintrc.json │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── babel.config.json │ │ ├── jest.config.ts │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── growthbook-client-provider.spec.ts │ │ │ │ ├── growthbook-client-provider.ts │ │ │ │ ├── translate-result.spec.ts │ │ │ │ └── translate-result.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── growthbook │ │ ├── .eslintrc.json │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── babel.config.json │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── growthbook-provider.spec.ts │ │ │ │ ├── growthbook-provider.ts │ │ │ │ ├── translate-result.spec.ts │ │ │ │ └── translate-result.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── in-memory │ │ └── README.md │ ├── launchdarkly-client │ │ ├── .eslintrc.json │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── babel.config.json │ │ ├── jest.config.ts │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── launchdarkly-client-provider.spec.ts │ │ │ │ ├── launchdarkly-client-provider.ts │ │ │ │ ├── launchdarkly-provider-options.ts │ │ │ │ ├── test-logger.ts │ │ │ │ ├── translate-context.spec.ts │ │ │ │ ├── translate-context.ts │ │ │ │ ├── translate-result.spec.ts │ │ │ │ └── translate-result.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── multi-provider-web │ │ ├── .eslintrc.json │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── babel.config.json │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── errors.ts │ │ │ │ ├── hook-executor.ts │ │ │ │ ├── multi-provider-web.spec.ts │ │ │ │ ├── multi-provider-web.ts │ │ │ │ ├── status-tracker.ts │ │ │ │ ├── strategies │ │ │ │ ├── BaseEvaluationStrategy.ts │ │ │ │ ├── ComparisonStrategy.ts │ │ │ │ ├── FirstMatchStrategy.ts │ │ │ │ ├── FirstSuccessfulStrategy.ts │ │ │ │ └── index.ts │ │ │ │ └── types.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── multi-provider │ │ ├── .eslintrc.json │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── babel.config.json │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── errors.ts │ │ │ │ ├── hook-executor.ts │ │ │ │ ├── multi-provider.spec.ts │ │ │ │ ├── multi-provider.ts │ │ │ │ ├── status-tracker.ts │ │ │ │ ├── strategies │ │ │ │ ├── BaseEvaluationStrategy.ts │ │ │ │ ├── ComparisonStrategy.ts │ │ │ │ ├── FirstMatchStrategy.ts │ │ │ │ ├── FirstSuccessfulStrategy.ts │ │ │ │ └── index.ts │ │ │ │ └── types.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── ofrep-web │ │ ├── .eslintrc.json │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── babel.config.json │ │ ├── jest.config.ts │ │ ├── jest.polyfills.js │ │ ├── package.json │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── model │ │ │ │ ├── evaluate-flags-response.ts │ │ │ │ ├── in-memory-cache.ts │ │ │ │ ├── ofrep-web-provider-options.ts │ │ │ │ └── resolution-error.ts │ │ │ │ ├── ofrep-web-provider.spec.ts │ │ │ │ └── ofrep-web-provider.ts │ │ ├── test │ │ │ └── test-logger.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── ofrep │ │ ├── .eslintrc.json │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── babel.config.json │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── ofrep-provider.spec.ts │ │ │ │ └── ofrep-provider.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ └── unleash-web │ │ ├── .eslintrc.json │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── babel.config.json │ │ ├── jest.config.ts │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── project.json │ │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ ├── test-logger.ts │ │ │ ├── testdata.json │ │ │ ├── unleash-web-provider-config.ts │ │ │ ├── unleash-web-provider.spec.ts │ │ │ └── unleash-web-provider.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json └── shared │ ├── config-cat-core │ ├── .eslintrc.json │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.ts │ ├── package-lock.json │ ├── package.json │ ├── project.json │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ ├── context-transformer.spec.ts │ │ │ ├── context-transformer.ts │ │ │ └── result-mapping.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── tsconfig.spec.json │ ├── flagd-core │ ├── .eslintrc.json │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.ts │ ├── package-lock.json │ ├── package.json │ ├── project.json │ ├── src │ │ ├── e2e │ │ │ └── index.ts │ │ ├── index.ts │ │ └── lib │ │ │ ├── __snapshots__ │ │ │ └── flagd-core.spec.ts.snap │ │ │ ├── feature-flag.spec.ts │ │ │ ├── feature-flag.ts │ │ │ ├── flagd-core.spec.ts │ │ │ ├── flagd-core.ts │ │ │ ├── parser.spec.ts │ │ │ ├── parser.ts │ │ │ ├── storage.spec.ts │ │ │ ├── storage.ts │ │ │ └── targeting │ │ │ ├── common.ts │ │ │ ├── fractional.ts │ │ │ ├── sem-ver.ts │ │ │ ├── string-comp.ts │ │ │ ├── targeting.spec.ts │ │ │ └── targeting.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── tsconfig.spec.json │ └── ofrep-core │ ├── .eslintrc.json │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.ts │ ├── package.json │ ├── project.json │ ├── src │ ├── index.ts │ ├── lib │ │ ├── api │ │ │ ├── errors.ts │ │ │ ├── index.ts │ │ │ ├── ofrep-api.spec.ts │ │ │ └── ofrep-api.ts │ │ ├── index.ts │ │ ├── model │ │ │ ├── bulk-evaluation.ts │ │ │ ├── evaluation.ts │ │ │ ├── index.ts │ │ │ └── ofrep-api-result.ts │ │ └── provider │ │ │ ├── index.ts │ │ │ ├── ofrep-provider-options.spec.ts │ │ │ └── ofrep-provider-options.ts │ └── test │ │ ├── handlers.ts │ │ ├── mock-service-worker.ts │ │ └── test-constants.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── tsconfig.spec.json ├── migrations.json ├── nx.json ├── package-lock.json ├── package.json ├── project.json ├── release-please-config.json ├── renovate.json ├── tools ├── scripts │ └── publish.mjs ├── tsconfig.tools.json └── workspace-plugin │ ├── .eslintrc.json │ ├── generators.json │ ├── jest.config.ts │ ├── package.json │ ├── project.json │ ├── src │ ├── generators │ │ └── open-feature │ │ │ ├── files │ │ │ ├── hook │ │ │ │ ├── client │ │ │ │ │ ├── README.md__tmpl__ │ │ │ │ │ └── src │ │ │ │ │ │ ├── index.ts__tmpl__ │ │ │ │ │ │ └── lib │ │ │ │ │ │ ├── __libFileName__.spec.ts__tmpl__ │ │ │ │ │ │ └── __libFileName__.ts__tmpl__ │ │ │ │ └── server │ │ │ │ │ ├── README.md__tmpl__ │ │ │ │ │ └── src │ │ │ │ │ ├── index.ts__tmpl__ │ │ │ │ │ └── lib │ │ │ │ │ ├── __libFileName__.spec.ts__tmpl__ │ │ │ │ │ └── __libFileName__.ts__tmpl__ │ │ │ ├── provider │ │ │ │ ├── client │ │ │ │ │ ├── README.md__tmpl__ │ │ │ │ │ └── src │ │ │ │ │ │ ├── index.ts__tmpl__ │ │ │ │ │ │ └── lib │ │ │ │ │ │ ├── __libFileName__.spec.ts__tmpl__ │ │ │ │ │ │ └── __libFileName__.ts__tmpl__ │ │ │ │ └── server │ │ │ │ │ ├── README.md__tmpl__ │ │ │ │ │ └── src │ │ │ │ │ ├── index.ts__tmpl__ │ │ │ │ │ └── lib │ │ │ │ │ ├── __libFileName__.spec.ts__tmpl__ │ │ │ │ │ └── __libFileName__.ts__tmpl__ │ │ │ └── shared │ │ │ │ └── babel.config.json__tmpl__ │ │ │ ├── index.ts │ │ │ └── schema.json │ └── index.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── tsconfig.spec.json └── tsconfig.base.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/add-hook.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Add hook 3 | about: Add provider template 4 | title: Add {my-behavior} hook 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | # Add {my-behavior} hook 11 | 12 | ## Reasoning 13 | 14 | I want to add a hook which performs {my-behavior}. 15 | 16 | - it would be great if {my-behavior} could be included in some flag evaluations 17 | - {my-behavior} would enable developers to be more productive! 18 | - other reasons! 19 | 20 | ## Requirements: 21 | 22 | - [ ] generate hook via [tooling](https://github.com/open-feature/js-sdk-contrib/blob/main/CONTRIBUTING.md#adding-a-module) 23 | - [ ] implement `Hook` interface 24 | - [ ] add tests 25 | - [ ] complete README 26 | - [ ] add the new provider to the [OpenFeature docs](https://github.com/open-feature/openfeature.dev/issues/new/choose) 27 | 28 | Keep in mind the CONTRIBUTING guidelines: https://github.com/open-feature/js-sdk-contrib/blob/main/CONTRIBUTING.md 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/add-provider.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Add provider 3 | about: Add provider template 4 | title: Add {my-system} provider 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | # Add {my-system} provider 11 | 12 | ## Reasoning 13 | 14 | I want to add an OpenFeature provider for {my-system}. This would be valuable because... 15 | 16 | - everybody uses {my-system}! 17 | - {my system} supports {these things}! 18 | - other reasons! 19 | 20 | ## Requirements: 21 | 22 | - [ ] generate provider via [tooling](https://github.com/open-feature/js-sdk-contrib/blob/main/CONTRIBUTING.md#adding-a-module) 23 | - [ ] implement `Provider` interface 24 | - [ ] add tests 25 | - [ ] complete README 26 | - [ ] add the new provider to the [OpenFeature docs](https://github.com/open-feature/openfeature.dev/issues/new/choose) 27 | 28 | ## Resources 29 | 30 | - {useful-link} 31 | 32 | Keep in mind the CONTRIBUTING guidelines: https://github.com/open-feature/js-sdk-contrib/blob/main/CONTRIBUTING.md 33 | -------------------------------------------------------------------------------- /.github/component_owners.yml: -------------------------------------------------------------------------------- 1 | # Keep all in alphabetical order 2 | components: 3 | libs/hooks/open-telemetry: 4 | - beeme1mr 5 | - toddbaert 6 | libs/providers/aws-ssm: 7 | - gdegiorgio 8 | libs/providers/config-cat: 9 | - lukas-reining 10 | - adams85 11 | libs/providers/config-cat-web: 12 | - lukas-reining 13 | - adams85 14 | libs/providers/env-var: 15 | - beeme1mr 16 | - toddbaert 17 | libs/providers/flagd: 18 | - beeme1mr 19 | - toddbaert 20 | libs/providers/flagd-web: 21 | - beeme1mr 22 | - toddbaert 23 | libs/providers/go-feature-flag: 24 | - thomaspoignant 25 | libs/providers/go-feature-flag-web: 26 | - thomaspoignant 27 | libs/providers/launchdarkly-client: 28 | - kinyoklion 29 | - mateoc 30 | - sago2k8 31 | libs/providers/flipt: 32 | - markphelps 33 | libs/providers/flipt-web: 34 | - markphelps 35 | libs/providers/unleash-web: 36 | - jarebudev 37 | 38 | ignored-authors: 39 | - renovate-bot 40 | -------------------------------------------------------------------------------- /.github/workflows/component-owners.yml: -------------------------------------------------------------------------------- 1 | name: 'Component Owners' 2 | on: 3 | pull_request_target: 4 | 5 | permissions: 6 | contents: read # to read changed files 7 | issues: write # to read/write issue assignees 8 | pull-requests: write # to read/write PR reviewers 9 | 10 | jobs: 11 | run_self: 12 | runs-on: ubuntu-latest 13 | name: Auto Assign Owners 14 | steps: 15 | - uses: dyladan/component-owners@58bd86e9814d23f1525d0a970682cead459fa783 16 | with: 17 | config-file: .github/component_owners.yml 18 | repo-token: ${{ secrets.GITHUB_TOKEN }} 19 | -------------------------------------------------------------------------------- /.github/workflows/lint-pr.yml: -------------------------------------------------------------------------------- 1 | name: 'Lint PR' 2 | 3 | on: 4 | pull_request_target: 5 | types: 6 | - opened 7 | - edited 8 | - synchronize 9 | 10 | jobs: 11 | main: 12 | name: Validate PR title 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: amannn/action-semantic-pull-request@0723387faaf9b38adef4775cd42cfd5155ed6017 # v5 16 | id: lint_pr_title 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | 20 | - uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2 21 | # When the previous steps fails, the workflow would stop. By adding this 22 | # condition you can continue the execution with the populated error message. 23 | if: always() && (steps.lint_pr_title.outputs.error_message != null) 24 | with: 25 | header: pr-title-lint-error 26 | message: | 27 | Hey there and thank you for opening this pull request! 👋🏼 28 | 29 | We require pull request titles to follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) and it looks like your proposed title needs to be adjusted. 30 | Details: 31 | 32 | ``` 33 | ${{ steps.lint_pr_title.outputs.error_message }} 34 | ``` 35 | # Delete a previous comment when the issue has been resolved 36 | - if: ${{ steps.lint_pr_title.outputs.error_message == null }} 37 | uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2 38 | with: 39 | header: pr-title-lint-error 40 | delete: true 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | yarn-error.log 34 | testem.log 35 | /typings 36 | .nx 37 | 38 | # System Files 39 | .DS_Store 40 | Thumbs.db 41 | 42 | # generated files 43 | proto 44 | .nx 45 | 46 | # yalc stuff 47 | .yalc 48 | yalc.lock 49 | 50 | 51 | # Generated by @nx/js 52 | .verdaccio 53 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libs/providers/flagd/schemas"] 2 | path = libs/providers/flagd/schemas 3 | url = https://github.com/open-feature/flagd-schemas.git 4 | [submodule "libs/providers/flagd-web/schemas"] 5 | path = libs/providers/flagd-web/schemas 6 | url = https://github.com/open-feature/flagd-schemas.git 7 | [submodule "libs/providers/flagd/spec"] 8 | path = libs/providers/flagd/spec 9 | url = https://github.com/open-feature/spec.git 10 | [submodule "libs/shared/flagd-core/flagd-schemas"] 11 | path = libs/shared/flagd-core/flagd-schemas 12 | url = https://github.com/open-feature/flagd-schemas.git 13 | [submodule "libs/shared/flagd-core/test-harness"] 14 | path = libs/shared/flagd-core/test-harness 15 | url = https://github.com/open-feature/flagd-testbed 16 | branch = v0.5.21 17 | [submodule "libs/shared/flagd-core/spec"] 18 | path = libs/shared/flagd-core/spec 19 | url = https://github.com/open-feature/spec 20 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 20 -------------------------------------------------------------------------------- /.nxignore: -------------------------------------------------------------------------------- 1 | libs/hooks/README.md 2 | libs/providers/README.md 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Add files here to ignore them from prettier formatting 2 | 3 | /dist 4 | /coverage 5 | 6 | /.nx/cache 7 | /.nx/workspace-data -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /.release-please-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "libs/hooks/open-telemetry": "0.4.0", 3 | "libs/providers/go-feature-flag": "0.7.8", 4 | "libs/providers/flagd": "0.13.3", 5 | "libs/providers/flagd-web": "0.7.3", 6 | "libs/providers/env-var": "0.3.1", 7 | "libs/providers/config-cat": "0.7.5", 8 | "libs/providers/launchdarkly-client": "0.3.2", 9 | "libs/providers/go-feature-flag-web": "0.2.6", 10 | "libs/shared/flagd-core": "1.0.0", 11 | "libs/shared/ofrep-core": "1.0.1", 12 | "libs/providers/ofrep": "0.2.1", 13 | "libs/providers/ofrep-web": "0.3.2", 14 | "libs/providers/flipt": "0.1.3", 15 | "libs/providers/flagsmith-client": "0.1.3", 16 | "libs/providers/flipt-web": "0.1.4", 17 | "libs/providers/multi-provider": "0.1.2", 18 | "libs/providers/multi-provider-web": "0.0.3", 19 | "libs/providers/growthbook-client": "0.1.2", 20 | "libs/providers/config-cat-web": "0.1.6", 21 | "libs/shared/config-cat-core": "0.1.1", 22 | "libs/providers/unleash-web": "0.1.1", 23 | "libs/providers/growthbook": "0.1.2", 24 | "libs/providers/aws-ssm": "0.1.3" 25 | } 26 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # These owners will be the default owners for everything in 2 | # the repo. Unless a later match takes precedence 3 | # 4 | # Managed by Peribolos: https://github.com/open-feature/community/blob/main/config/open-feature/sdk-javascript/workgroup.yaml 5 | # 6 | * @open-feature/sdk-javascript-maintainers @open-feature/maintainers 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenFeature JS Contributions 2 | 3 | This repository is intended for OpenFeature contributions which are not included in the [OpenFeature SDK](https://github.com/open-feature/js-sdk). 4 | 5 | The project includes: 6 | 7 | - [Providers](./libs/providers) 8 | - [Hooks](./libs/hooks) 9 | 10 | ## Contributing 11 | 12 | Interested in contributing? Great, we'd love your help! To get started, take a look at the [CONTRIBUTING](CONTRIBUTING.md) guide. 13 | 14 | ## Useful links 15 | 16 | * For more information on OpenFeature, visit [openfeature.dev](https://openfeature.dev) 17 | * For help or feedback on this project, join us on [Slack][slack] or create a [GitHub issue][github-issue]. 18 | 19 | ## License 20 | 21 | [Apache License 2.0](LICENSE) 22 | 23 | [github-issue]: https://github.com/open-feature/js-sdk-contrib/issues/new/choose 24 | [slack]: https://cloud-native.slack.com/archives/C0344AANLA1 25 | -------------------------------------------------------------------------------- /assets/aws-ssm/create-param.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-feature/js-sdk-contrib/fbd9f9155d282984444e7a55508871b9eaca3efa/assets/aws-ssm/create-param.png -------------------------------------------------------------------------------- /assets/aws-ssm/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-feature/js-sdk-contrib/fbd9f9155d282984444e7a55508871b9eaca3efa/assets/aws-ssm/search.png -------------------------------------------------------------------------------- /assets/aws-ssm/ssm-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-feature/js-sdk-contrib/fbd9f9155d282984444e7a55508871b9eaca3efa/assets/aws-ssm/ssm-menu.png -------------------------------------------------------------------------------- /assets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "module": "commonjs" 3 | } 4 | -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "babelrcRoots": ["*"] 3 | } 4 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | import { getJestProjectsAsync } from '@nx/jest'; 2 | 3 | export default async () => ({ 4 | projects: await getJestProjectsAsync(), 5 | }); 6 | -------------------------------------------------------------------------------- /jest.preset.js: -------------------------------------------------------------------------------- 1 | const nxPreset = require('@nx/jest/preset').default; 2 | 3 | module.exports = { ...nxPreset }; 4 | -------------------------------------------------------------------------------- /libs/hooks/README.md: -------------------------------------------------------------------------------- 1 | # OpenFeature JavaScript Hooks 2 | 3 | Hooks are a mechanism whereby application developers can add arbitrary behavior to flag evaluation. They operate similarly to middleware in many web frameworks. Please see the [spec](https://openfeature.dev/docs/specification/sections/hooks) for more details. 4 | 5 | ## Add a new hook 6 | 7 | 1. `npm ci` 8 | 1. `npm run generator-hook` 9 | -------------------------------------------------------------------------------- /libs/hooks/open-telemetry/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/hooks/open-telemetry/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["minify", { "builtIns": false }]] 3 | } 4 | -------------------------------------------------------------------------------- /libs/hooks/open-telemetry/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'hooks-open-telemetry', 4 | preset: '../../../jest.preset.js', 5 | transform: { 6 | '^.+\\.[tj]s$': [ 7 | 'ts-jest', 8 | { 9 | tsconfig: '/tsconfig.spec.json', 10 | }, 11 | ], 12 | }, 13 | moduleFileExtensions: ['ts', 'js', 'html'], 14 | }; 15 | -------------------------------------------------------------------------------- /libs/hooks/open-telemetry/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/open-telemetry-hooks", 3 | "version": "0.4.0", 4 | "license": "Apache-2.0", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/open-feature/js-sdk-contrib.git", 8 | "directory": "libs/hooks/open-telemetry" 9 | }, 10 | "publishConfig": { 11 | "access": "public" 12 | }, 13 | "scripts": { 14 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 15 | "current-version": "echo $npm_package_version" 16 | }, 17 | "peerDependencies": { 18 | "@openfeature/server-sdk": "^1.13.0", 19 | "@opentelemetry/api": ">=1.3.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /libs/hooks/open-telemetry/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/traces'; 2 | export * from './lib/metrics'; 3 | -------------------------------------------------------------------------------- /libs/hooks/open-telemetry/src/lib/conventions.ts: -------------------------------------------------------------------------------- 1 | // see: https://opentelemetry.io/docs/specs/otel/logs/semantic_conventions/feature-flags/ 2 | export const FEATURE_FLAG = 'feature_flag'; 3 | export const EXCEPTION_ATTR = 'exception'; 4 | 5 | export const ACTIVE_COUNT_NAME = `${FEATURE_FLAG}.evaluation_active_count`; 6 | export const REQUESTS_TOTAL_NAME = `${FEATURE_FLAG}.evaluation_requests_total`; 7 | export const SUCCESS_TOTAL_NAME = `${FEATURE_FLAG}.evaluation_success_total`; 8 | export const ERROR_TOTAL_NAME = `${FEATURE_FLAG}.evaluation_error_total`; 9 | 10 | export type EvaluationAttributes = { [key: `${typeof FEATURE_FLAG}.${string}`]: string | undefined }; 11 | export type ExceptionAttributes = { [EXCEPTION_ATTR]: string }; 12 | 13 | export const KEY_ATTR: keyof EvaluationAttributes = `${FEATURE_FLAG}.key`; 14 | export const PROVIDER_NAME_ATTR: keyof EvaluationAttributes = `${FEATURE_FLAG}.provider_name`; 15 | export const VARIANT_ATTR: keyof EvaluationAttributes = `${FEATURE_FLAG}.variant`; 16 | export const REASON_ATTR: keyof EvaluationAttributes = `${FEATURE_FLAG}.reason`; 17 | -------------------------------------------------------------------------------- /libs/hooks/open-telemetry/src/lib/metrics/index.ts: -------------------------------------------------------------------------------- 1 | export * from './metrics-hook'; 2 | -------------------------------------------------------------------------------- /libs/hooks/open-telemetry/src/lib/otel-hook.ts: -------------------------------------------------------------------------------- 1 | import type { FlagMetadata, Logger } from '@openfeature/server-sdk'; 2 | import type { Attributes } from '@opentelemetry/api'; 3 | 4 | export type AttributeMapper = (flagMetadata: FlagMetadata) => Attributes; 5 | 6 | export type OpenTelemetryHookOptions = { 7 | /** 8 | * A function that maps OpenFeature flag metadata values to OpenTelemetry attributes. 9 | */ 10 | attributeMapper?: AttributeMapper; 11 | }; 12 | 13 | /** 14 | * Base class that does some logging and safely wraps the AttributeMapper. 15 | */ 16 | export abstract class OpenTelemetryHook { 17 | protected safeAttributeMapper: AttributeMapper; 18 | protected abstract name: string; 19 | 20 | constructor(options?: OpenTelemetryHookOptions, logger?: Logger) { 21 | this.safeAttributeMapper = (flagMetadata: FlagMetadata) => { 22 | try { 23 | return options?.attributeMapper?.(flagMetadata) || {}; 24 | } catch (err) { 25 | logger?.debug(`${this.name}: error in attributeMapper, ${err.message}, ${err.stack}`); 26 | return {}; 27 | } 28 | }; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /libs/hooks/open-telemetry/src/lib/traces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './tracing-hook'; 2 | -------------------------------------------------------------------------------- /libs/hooks/open-telemetry/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ES6" 5 | }, 6 | "files": [], 7 | "include": [], 8 | "references": [ 9 | { 10 | "path": "./tsconfig.lib.json" 11 | }, 12 | { 13 | "path": "./tsconfig.spec.json" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /libs/hooks/open-telemetry/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": [] 7 | }, 8 | "include": ["**/*.ts"], 9 | "exclude": ["jest.config.ts", "**/*.spec.ts", "**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/hooks/open-telemetry/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /libs/providers/README.md: -------------------------------------------------------------------------------- 1 | # OpenFeature JavaScript Providers 2 | 3 | Providers are responsible for performing flag evaluation. They provide an abstraction between the underlying flag management system and OpenFeature itself. This allows providers to be changed without requiring a major code refactor. Please see the [spec](https://openfeature.dev/docs/specification/sections/providers) for more details. 4 | 5 | ## Add a new provider 6 | 7 | 1. `npm ci` 8 | 1. `npm run generate-provider` 9 | -------------------------------------------------------------------------------- /libs/providers/aws-ssm/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | }, 17 | { 18 | "files": ["*.json"], 19 | "parser": "jsonc-eslint-parser", 20 | "rules": { 21 | "@nx/dependency-checks": [ 22 | "error", 23 | { 24 | "ignoredFiles": ["{projectRoot}/eslint.config.{js,cjs,mjs}"] 25 | } 26 | ] 27 | } 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /libs/providers/aws-ssm/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [0.1.3](https://github.com/open-feature/js-sdk-contrib/compare/aws-ssm-provider-v0.1.2...aws-ssm-provider-v0.1.3) (2025-06-04) 4 | 5 | 6 | ### 🐛 Bug Fixes 7 | 8 | * **deps:** update dependency @aws-sdk/client-ssm to v3.787.0 ([#1278](https://github.com/open-feature/js-sdk-contrib/issues/1278)) ([afae82c](https://github.com/open-feature/js-sdk-contrib/commit/afae82c1a1472d33b884105edaac2976c19e7423)) 9 | * **deps:** update dependency lru-cache to v11.1.0 ([#1279](https://github.com/open-feature/js-sdk-contrib/issues/1279)) ([a80f5ce](https://github.com/open-feature/js-sdk-contrib/commit/a80f5ce3d7a6e74e762a75ba8fa9f5b70ca2a179)) 10 | 11 | ## [0.1.2](https://github.com/open-feature/js-sdk-contrib/compare/aws-ssm-provider-v0.1.1...aws-ssm-provider-v0.1.2) (2025-03-27) 12 | 13 | 14 | ### ✨ New Features 15 | 16 | * **aws-ssm:** add decryption support for `SecureString` parameters ([#1241](https://github.com/open-feature/js-sdk-contrib/issues/1241)) ([043be44](https://github.com/open-feature/js-sdk-contrib/commit/043be44de1442b89876e9857478afe619fcf0b04)) 17 | 18 | ## [0.1.1](https://github.com/open-feature/js-sdk-contrib/compare/aws-ssm-provider-v0.1.0...aws-ssm-provider-v0.1.1) (2025-03-20) 19 | 20 | 21 | ### ✨ New Features 22 | 23 | * **aws-ssm:** implement AWS SSM provider ([#1221](https://github.com/open-feature/js-sdk-contrib/issues/1221)) ([819a247](https://github.com/open-feature/js-sdk-contrib/commit/819a247c41112c2873aa025ac0abd3c62eb53aca)) 24 | -------------------------------------------------------------------------------- /libs/providers/aws-ssm/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["minify", { "builtIns": false }]] 3 | } 4 | -------------------------------------------------------------------------------- /libs/providers/aws-ssm/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'aws-ssm', 4 | preset: '../../../jest.preset.js', 5 | globals: { 6 | 'ts-jest': { 7 | tsconfig: '/tsconfig.spec.json', 8 | }, 9 | }, 10 | transform: { 11 | '^.+\\.[tj]s$': 'ts-jest', 12 | }, 13 | moduleFileExtensions: ['ts', 'js', 'html'], 14 | coverageDirectory: '../../../coverage/libs/providers/aws-ssm', 15 | testEnvironment: 'node', 16 | }; 17 | -------------------------------------------------------------------------------- /libs/providers/aws-ssm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/aws-ssm-provider", 3 | "version": "0.1.3", 4 | "dependencies": { 5 | "@aws-sdk/client-ssm": "^3.759.0", 6 | "lru-cache": "^11.0.2", 7 | "tslib": "^2.3.0" 8 | }, 9 | "main": "./src/index.js", 10 | "typings": "./src/index.d.ts", 11 | "scripts": { 12 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 13 | "current-version": "echo $npm_package_version" 14 | }, 15 | "license": "Apache-2.0", 16 | "peerDependencies": { 17 | "@openfeature/server-sdk": "^1.17.0" 18 | }, 19 | "devDependencies": { 20 | "@smithy/types": "^4.1.0", 21 | "aws-sdk-client-mock": "^4.1.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /libs/providers/aws-ssm/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/aws-ssm-provider'; 2 | -------------------------------------------------------------------------------- /libs/providers/aws-ssm/src/lib/cache.ts: -------------------------------------------------------------------------------- 1 | import type { ResolutionDetails } from '@openfeature/core'; 2 | import type { LRUCacheConfig } from './types'; 3 | import { LRUCache } from 'lru-cache'; 4 | 5 | export class Cache { 6 | private cache: LRUCache>; 7 | private ttl: number; 8 | private enabled: boolean; 9 | constructor(opts: LRUCacheConfig) { 10 | this.cache = new LRUCache({ 11 | maxSize: opts.size ?? 1000, 12 | sizeCalculation: () => 1, 13 | }); 14 | this.ttl = opts.ttl ?? 300000; 15 | this.enabled = opts.enabled; 16 | } 17 | 18 | get(key: string): ResolutionDetails | undefined { 19 | if (!this.enabled) { 20 | return undefined; 21 | } 22 | return this.cache.get(key); 23 | } 24 | 25 | set(key: string, value: ResolutionDetails): void { 26 | if (!this.enabled) { 27 | return; 28 | } 29 | this.cache.set(key, value, { ttl: this.ttl }); 30 | } 31 | 32 | clear() { 33 | if (!this.enabled) { 34 | return; 35 | } 36 | this.cache.clear(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /libs/providers/aws-ssm/src/lib/types.ts: -------------------------------------------------------------------------------- 1 | import type { SSMClientConfig } from '@aws-sdk/client-ssm'; 2 | 3 | export type AwsSsmProviderConfig = { 4 | ssmClientConfig: SSMClientConfig; 5 | cacheOpts: LRUCacheConfig; 6 | enableDecryption?: boolean; 7 | }; 8 | 9 | export type LRUCacheConfig = { 10 | enabled: boolean; 11 | ttl?: number; 12 | size?: number; 13 | }; 14 | -------------------------------------------------------------------------------- /libs/providers/aws-ssm/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ES6", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noImplicitReturns": true, 9 | "noFallthroughCasesInSwitch": true, 10 | "noPropertyAccessFromIndexSignature": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/providers/aws-ssm/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/aws-ssm/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../dist/out-tsc", 5 | "module": "commonjs", 6 | "moduleResolution": "node10", 7 | "types": ["jest", "node"] 8 | }, 9 | "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/config-cat-web/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/providers/config-cat-web/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["minify", { "builtIns": false }]] 3 | } 4 | -------------------------------------------------------------------------------- /libs/providers/config-cat-web/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'providers-config-cat-web', 4 | preset: '../../../jest.preset.js', 5 | transform: { 6 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 7 | }, 8 | moduleFileExtensions: ['ts', 'js', 'html'], 9 | coverageDirectory: '../../../coverage/libs/providers/config-cat', 10 | }; 11 | -------------------------------------------------------------------------------- /libs/providers/config-cat-web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/config-cat-web-provider", 3 | "version": "0.1.6", 4 | "license": "Apache-2.0", 5 | "scripts": { 6 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 7 | "current-version": "echo $npm_package_version" 8 | }, 9 | "peerDependencies": { 10 | "@openfeature/web-sdk": "^1.0.0", 11 | "configcat-js-ssr": "^8.4.3", 12 | "@openfeature/config-cat-core": "0.1.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /libs/providers/config-cat-web/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/config-cat-web-provider'; 2 | -------------------------------------------------------------------------------- /libs/providers/config-cat-web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ES6", 5 | "forceConsistentCasingInFileNames": true, 6 | "noImplicitOverride": true, 7 | "noPropertyAccessFromIndexSignature": true, 8 | "noImplicitReturns": true, 9 | "noFallthroughCasesInSwitch": true 10 | }, 11 | "files": [], 12 | "include": [], 13 | "references": [ 14 | { 15 | "path": "./tsconfig.lib.json" 16 | }, 17 | { 18 | "path": "./tsconfig.spec.json" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /libs/providers/config-cat-web/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/config-cat-web/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /libs/providers/config-cat/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/providers/config-cat/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["minify", { "builtIns": false }]] 3 | } 4 | -------------------------------------------------------------------------------- /libs/providers/config-cat/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'providers-config-cat', 4 | preset: '../../../jest.preset.js', 5 | transform: { 6 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 7 | }, 8 | moduleFileExtensions: ['ts', 'js', 'html'], 9 | coverageDirectory: '../../../coverage/libs/providers/config-cat', 10 | }; 11 | -------------------------------------------------------------------------------- /libs/providers/config-cat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/config-cat-provider", 3 | "version": "0.7.5", 4 | "license": "Apache-2.0", 5 | "scripts": { 6 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 7 | "current-version": "echo $npm_package_version" 8 | }, 9 | "peerDependencies": { 10 | "@openfeature/server-sdk": "^1.13.5", 11 | "configcat-node": "^11.3.1", 12 | "@openfeature/config-cat-core": "0.1.1", 13 | "configcat-common": "9.3.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /libs/providers/config-cat/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/config-cat-provider'; 2 | -------------------------------------------------------------------------------- /libs/providers/config-cat/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ES6", 5 | "forceConsistentCasingInFileNames": true, 6 | "noImplicitOverride": true, 7 | "noPropertyAccessFromIndexSignature": true, 8 | "noImplicitReturns": true, 9 | "noFallthroughCasesInSwitch": true 10 | }, 11 | "files": [], 12 | "include": [], 13 | "references": [ 14 | { 15 | "path": "./tsconfig.lib.json" 16 | }, 17 | { 18 | "path": "./tsconfig.spec.json" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /libs/providers/config-cat/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/config-cat/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /libs/providers/env-var/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@nx/js/babel", 5 | { 6 | "useBuiltIns": "usage" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/env-var/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/providers/env-var/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["minify", { "builtIns": false }]] 3 | } 4 | -------------------------------------------------------------------------------- /libs/providers/env-var/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'providers-env-var', 4 | preset: '../../../jest.preset.js', 5 | transform: { 6 | '^.+\\.[tj]s$': [ 7 | 'ts-jest', 8 | { 9 | tsconfig: '/tsconfig.spec.json', 10 | }, 11 | ], 12 | }, 13 | moduleFileExtensions: ['ts', 'js', 'html'], 14 | coverageDirectory: '../../../coverage/libs/providers/env-var', 15 | }; 16 | -------------------------------------------------------------------------------- /libs/providers/env-var/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/env-var-provider", 3 | "version": "0.3.1", 4 | "license": "Apache-2.0", 5 | "scripts": { 6 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 7 | "current-version": "echo $npm_package_version" 8 | }, 9 | "peerDependencies": { 10 | "@openfeature/server-sdk": "^1.13.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /libs/providers/env-var/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/env-var-provider'; 2 | -------------------------------------------------------------------------------- /libs/providers/env-var/src/lib/constant-case.spec.ts: -------------------------------------------------------------------------------- 1 | import { constantCase } from './constant-case'; 2 | 3 | describe('Constant Case', () => { 4 | it('should be STRING', () => { 5 | expect(constantCase('string')).toBe('STRING'); 6 | }); 7 | it('should be DOT_CASE', () => { 8 | expect(constantCase('dot.case')).toBe('DOT_CASE'); 9 | }); 10 | it('should be PASCAL_CASE', () => { 11 | expect(constantCase('PascalCase')).toBe('PASCAL_CASE'); 12 | }); 13 | it('should be VERSION_1_2_10', () => { 14 | expect(constantCase('version 1.2.10')).toBe('VERSION_1_2_10'); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /libs/providers/env-var/src/lib/constant-case.ts: -------------------------------------------------------------------------------- 1 | // Support camel case ("camelCase" -> "camel Case" and "CAMELCase" -> "CAMEL Case"). 2 | const DEFAULT_SPLIT_REGEXP = [/([a-z0-9])([A-Z])/g, /([A-Z])([A-Z][a-z])/g]; 3 | 4 | // Remove all non-word characters. 5 | const DEFAULT_STRIP_REGEXP = /[^A-Z0-9]+/gi; 6 | 7 | /** 8 | * Replace `re` in the input string with the replacement value. 9 | */ 10 | function replace(input: string, re: RegExp | RegExp[], value: string) { 11 | if (re instanceof RegExp) return input.replace(re, value); 12 | return re.reduce((input, re) => input.replace(re, value), input); 13 | } 14 | 15 | export function constantCase(input: string) { 16 | const result = replace(replace(input, DEFAULT_SPLIT_REGEXP, '$1\0$2'), DEFAULT_STRIP_REGEXP, '\0'); 17 | 18 | let start = 0; 19 | let end = result.length; 20 | 21 | // Trim the delimiter from around the output string. 22 | while (result.charAt(start) === '\0') start++; 23 | while (result.charAt(end - 1) === '\0') end--; 24 | 25 | return result 26 | .slice(start, end) 27 | .split('\0') 28 | .map((i) => i.toUpperCase()) 29 | .join('_'); 30 | } 31 | -------------------------------------------------------------------------------- /libs/providers/env-var/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ES6", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/providers/env-var/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/env-var/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /libs/providers/flagd-web/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@nx/web/babel", 5 | { 6 | "useBuiltIns": "usage" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/flagd-web/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*", "schemas/**"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/providers/flagd-web/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["minify", { "builtIns": false }]] 3 | } 4 | -------------------------------------------------------------------------------- /libs/providers/flagd-web/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'providers-flagd-web', 4 | preset: '../../../jest.preset.js', 5 | transform: { 6 | '^.+\\.[tj]s$': ['ts-jest', { 7 | tsconfig: '/tsconfig.spec.json' 8 | }] 9 | }, 10 | testEnvironment: 'jsdom', 11 | moduleFileExtensions: ['ts', 'js', 'html'], 12 | // ignore e2e path 13 | testPathIgnorePatterns: ["/e2e/"], 14 | coverageDirectory: '../../../coverage/libs/providers/flagd-web', 15 | }; 16 | -------------------------------------------------------------------------------- /libs/providers/flagd-web/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/flagd-web-provider", 3 | "version": "0.7.3", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@openfeature/flagd-web-provider", 9 | "version": "0.7.3" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /libs/providers/flagd-web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/flagd-web-provider", 3 | "version": "0.7.3", 4 | "license": "Apache-2.0", 5 | "scripts": { 6 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 7 | "current-version": "echo $npm_package_version" 8 | }, 9 | "peerDependencies": { 10 | "@openfeature/web-sdk": "^1.0.0" 11 | }, 12 | "dependencies": { 13 | "@openfeature/flagd-core": "1.0.0", 14 | "@connectrpc/connect": "^1.4.0", 15 | "@connectrpc/connect-web": "^1.4.0", 16 | "@bufbuild/protobuf": "^1.2.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /libs/providers/flagd-web/src/e2e/constants.ts: -------------------------------------------------------------------------------- 1 | import { getGherkinTestPath } from '@openfeature/flagd-core'; 2 | 3 | export const FLAGD_NAME = 'flagd'; 4 | 5 | export const GHERKIN_EVALUATION_FEATURE = getGherkinTestPath( 6 | 'evaluation.feature', 7 | 'spec/specification/assets/gherkin/', 8 | ); 9 | -------------------------------------------------------------------------------- /libs/providers/flagd-web/src/e2e/index.ts: -------------------------------------------------------------------------------- 1 | export * from './constants'; 2 | export * from './step-definitions'; 3 | -------------------------------------------------------------------------------- /libs/providers/flagd-web/src/e2e/jest.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'jest'; 2 | 3 | const config: Config = { 4 | displayName: 'providers-flagd-web-e2e', 5 | clearMocks: true, 6 | preset: 'ts-jest', 7 | moduleNameMapper: { 8 | '@openfeature/flagd-core': ['/../../../../shared/flagd-core/src'], 9 | '(.+)\\.js$': '$1', 10 | }, 11 | verbose: true, 12 | }; 13 | 14 | export default config; 15 | -------------------------------------------------------------------------------- /libs/providers/flagd-web/src/e2e/step-definitions/index.ts: -------------------------------------------------------------------------------- 1 | export * from './flag'; 2 | -------------------------------------------------------------------------------- /libs/providers/flagd-web/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/flagd-web-provider'; 2 | -------------------------------------------------------------------------------- /libs/providers/flagd-web/src/lib/options.ts: -------------------------------------------------------------------------------- 1 | import type { Interceptor } from '@connectrpc/connect'; 2 | 3 | export interface Options { 4 | /** 5 | * The domain name or IP address of flagd. 6 | */ 7 | host: string; 8 | 9 | /** 10 | * The port at which the flagd gRPC service is exposed. 11 | * 12 | * @default 443 13 | */ 14 | port: number; 15 | 16 | /** 17 | * The path at which the flagd gRPC service is available, for example: /flagd-api (optional). 18 | * 19 | * @default "" 20 | */ 21 | pathPrefix: string; 22 | 23 | /** 24 | * Determines if TLS (https) should be used. 25 | * 26 | * @default true 27 | */ 28 | tls: boolean; 29 | 30 | /** 31 | * Sets maximum delay between connection retries in ms. 32 | * 33 | * @default 60000 34 | */ 35 | maxDelay: number; 36 | 37 | /** 38 | * Sets the maximum number of retries for a connection to be made to the flagd instance 39 | * 0 means unlimited. A negative number means no retries. 40 | * 41 | * @default 0 42 | */ 43 | maxRetries: number; 44 | 45 | /** 46 | * Connect interceptors applied to all calls. 47 | */ 48 | interceptors?: Interceptor[]; 49 | } 50 | 51 | export type FlagdProviderOptions = Partial & Pick; 52 | 53 | export const DEFAULT_MAX_DELAY = 60000; 54 | 55 | export function getOptions(options: FlagdProviderOptions): Options { 56 | return { 57 | ...{ 58 | port: 443, 59 | tls: true, 60 | maxRetries: 0, 61 | maxDelay: DEFAULT_MAX_DELAY, 62 | pathPrefix: '', 63 | }, 64 | ...options, 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /libs/providers/flagd-web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ES6", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "resolveJsonModule": true 14 | }, 15 | "files": [], 16 | "include": [], 17 | "references": [ 18 | { 19 | "path": "./tsconfig.lib.json" 20 | }, 21 | { 22 | "path": "./tsconfig.spec.json" 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /libs/providers/flagd-web/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "lib": ["ES2015", "DOM"], 5 | "outDir": "../../../dist/out-tsc", 6 | "declaration": true, 7 | "types": [], 8 | "allowSyntheticDefaultImports": true 9 | }, 10 | "include": ["**/*.ts"], 11 | "exclude": ["jest.config.ts", "**/*.spec.ts", "**/*.test.ts", "src/e2e/"] 12 | } 13 | -------------------------------------------------------------------------------- /libs/providers/flagd-web/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest"], 7 | "allowJs": true 8 | }, 9 | "include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts", "./src/e2e/"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/flagd/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@nx/web/babel", 5 | { 6 | "useBuiltIns": "usage" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/flagd/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*", "node_modules", "src/proto/**", "schemas/**", "spec/**"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/providers/flagd/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["minify", { "builtIns": false }]] 3 | } 4 | -------------------------------------------------------------------------------- /libs/providers/flagd/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'providers-flagd', 4 | preset: '../../../jest.preset.js', 5 | transform: { 6 | '^.+\\.[tj]s$': [ 7 | 'ts-jest', 8 | { 9 | tsconfig: '/tsconfig.spec.json', 10 | }, 11 | ], 12 | }, 13 | moduleFileExtensions: ['ts', 'js', 'html'], 14 | // ignore e2e path 15 | testPathIgnorePatterns: ['/e2e/'], 16 | coverageDirectory: '../../../coverage/libs/providers/flagd', 17 | }; 18 | -------------------------------------------------------------------------------- /libs/providers/flagd/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/flagd-provider", 3 | "version": "0.13.3", 4 | "license": "Apache-2.0", 5 | "scripts": { 6 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 7 | "current-version": "echo $npm_package_version" 8 | }, 9 | "peerDependencies": { 10 | "@grpc/grpc-js": "~1.8.0 || ~1.9.0 || ~1.10.0 || ~1.11.0 || ~1.12.0 || ~1.13.0", 11 | "@openfeature/server-sdk": "^1.17.0" 12 | }, 13 | "dependencies": { 14 | "lru-cache": "^11.0.0", 15 | "@openfeature/flagd-core": "^1.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /libs/providers/flagd/src/e2e/constants.ts: -------------------------------------------------------------------------------- 1 | import { getGherkinTestPath } from '@openfeature/flagd-core'; 2 | 3 | export const FLAGD_NAME = 'flagd'; 4 | export const UNSTABLE_CLIENT_NAME = 'unstable'; 5 | export const UNAVAILABLE_CLIENT_NAME = 'unavailable'; 6 | 7 | export const GHERKIN_FLAGD = getGherkinTestPath('*.feature'); 8 | export const CONNECTION_FEATURE = getGherkinTestPath('connection.feature'); 9 | export const CONTEXT_ENRICHMENT_FEATURE = getGherkinTestPath('contextEnrichment.feature'); 10 | export const EVALUATION_FEATURE = getGherkinTestPath('evaluation.feature'); 11 | export const EVENTS_FEATURE = getGherkinTestPath('events.feature'); 12 | export const METADATA_FEATURE = getGherkinTestPath('metadata.feature'); 13 | export const RPC_CACHING_FEATURE = getGherkinTestPath('rpc-caching.feature'); 14 | export const SELECTOR_FEATURE = getGherkinTestPath('selector.feature'); 15 | export const TARGETING_FEATURE = getGherkinTestPath('targeting.feature'); 16 | export const GHERKIN_EVALUATION_FEATURE = getGherkinTestPath( 17 | 'evaluation.feature', 18 | 'spec/specification/assets/gherkin/', 19 | ); 20 | -------------------------------------------------------------------------------- /libs/providers/flagd/src/e2e/index.ts: -------------------------------------------------------------------------------- 1 | export * from './constants'; 2 | -------------------------------------------------------------------------------- /libs/providers/flagd/src/e2e/jest.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'jest'; 2 | 3 | const config: Config = { 4 | displayName: 'providers-flagd-e2e', 5 | clearMocks: true, 6 | preset: 'ts-jest', 7 | moduleNameMapper: { 8 | '@openfeature/flagd-core': ['/../../../../shared/flagd-core/src'], 9 | '(.+)\\.js$': '$1', 10 | }, 11 | verbose: true, 12 | }; 13 | 14 | export default config; 15 | -------------------------------------------------------------------------------- /libs/providers/flagd/src/e2e/step-definitions/contextSteps.ts: -------------------------------------------------------------------------------- 1 | import type { JsonObject } from '@openfeature/server-sdk'; 2 | import type { State, Steps } from './state'; 3 | import { mapValueToType } from './utils'; 4 | 5 | export const contextSteps: Steps = 6 | (state: State) => 7 | ({ given, when, then }) => { 8 | beforeEach(() => (state.context = undefined)); 9 | given( 10 | /^a context containing a key "(.*)", with type "(.*)" and with value "(.*)"$/, 11 | (key: string, type: string, value: string) => { 12 | if (state.context == undefined) { 13 | state.context = {}; 14 | } 15 | state.context[key] = mapValueToType(value, type); 16 | }, 17 | ); 18 | given( 19 | /^a context containing a nested property with outer key "(.*)" and inner key "(.*)", with value "(.*)"$/, 20 | (outer: string, inner: string, value) => { 21 | if (state.context == undefined) { 22 | state.context = {}; 23 | } 24 | state.context[outer] = { ...(state.context[outer] as JsonObject), [inner]: value }; 25 | }, 26 | ); 27 | given(/^a context containing a targeting key with value "(.*)"$/, (key) => { 28 | if (state.context == undefined) { 29 | state.context = {}; 30 | } 31 | state.context.targetingKey = key; 32 | }); 33 | }; 34 | -------------------------------------------------------------------------------- /libs/providers/flagd/src/e2e/step-definitions/state.ts: -------------------------------------------------------------------------------- 1 | import type { StepDefinitions } from 'jest-cucumber'; 2 | import type { CacheOption, ResolverType } from '../../lib/configuration'; 3 | import type { Client, EvaluationContext, EvaluationDetails, EventDetails, FlagValue } from '@openfeature/server-sdk'; 4 | 5 | interface Flag { 6 | name: string; 7 | type: string; 8 | defaultValue: unknown; 9 | } 10 | 11 | interface Event { 12 | type: string; 13 | details?: EventDetails; 14 | } 15 | 16 | export interface State { 17 | flagsChanged?: string[]; 18 | providerType?: string; 19 | details?: EvaluationDetails; 20 | client?: Client; 21 | resolverType: ResolverType; 22 | context?: EvaluationContext; 23 | config?: { 24 | cache?: CacheOption; 25 | socketPath?: string; 26 | port: number; 27 | maxCacheSize?: number; 28 | resolverType?: ResolverType; 29 | host: string; 30 | offlineFlagSourcePath?: string; 31 | tls: boolean; 32 | selector?: string; 33 | }; 34 | options: Record; 35 | events: Event[]; 36 | flag?: Flag; 37 | } 38 | 39 | export type Steps = (state: State) => StepDefinitions; 40 | -------------------------------------------------------------------------------- /libs/providers/flagd/src/e2e/step-definitions/utils.ts: -------------------------------------------------------------------------------- 1 | import type { CacheOption, ResolverType } from '../../lib/configuration'; 2 | 3 | export function mapValueToType(value: string, type: string): any { 4 | switch (type) { 5 | case 'String': 6 | if (value == 'null') { 7 | return undefined; 8 | } 9 | return value; 10 | case 'Integer': 11 | return Number.parseInt(value); 12 | case 'Float': 13 | return Number.parseFloat(value); 14 | case 'Long': 15 | return Number.parseFloat(value); 16 | case 'Boolean': 17 | return value.toLowerCase() === 'true'; 18 | case 'ResolverType': 19 | return value.toLowerCase() as ResolverType; 20 | case 'CacheType': 21 | return value as CacheOption; 22 | case 'Object': 23 | if (value == 'null') { 24 | return undefined; 25 | } 26 | return JSON.parse(value); 27 | default: 28 | throw new Error('type not supported'); 29 | } 30 | } 31 | 32 | export function waitFor(check: () => T, options: { timeout?: number; interval?: number } = {}): Promise { 33 | const { timeout = 5000, interval = 50 } = options; // Default 5s timeout, 50ms polling interval 34 | 35 | return new Promise((resolve, reject) => { 36 | const startTime = Date.now(); 37 | 38 | const checkCondition = () => { 39 | try { 40 | const result = check(); 41 | resolve(result); // If condition passes, resolve the promise 42 | } catch (error) { 43 | if (Date.now() - startTime > timeout) { 44 | reject(new Error('Timeout while waiting for condition')); 45 | } else { 46 | setTimeout(checkCondition, interval); // Retry after interval 47 | } 48 | } 49 | }; 50 | 51 | checkCondition(); 52 | }); 53 | } 54 | -------------------------------------------------------------------------------- /libs/providers/flagd/src/e2e/tests/in-process.spec.ts: -------------------------------------------------------------------------------- 1 | import { autoBindSteps, loadFeatures } from 'jest-cucumber'; 2 | import { GHERKIN_FLAGD } from '../constants'; 3 | import { providerSteps } from '../step-definitions/providerSteps'; 4 | import { configSteps } from '../step-definitions/configSteps'; 5 | import type { State } from '../step-definitions/state'; 6 | import { eventSteps } from '../step-definitions/eventSteps'; 7 | import { flagSteps } from '../step-definitions/flagSteps'; 8 | import { contextSteps } from '../step-definitions/contextSteps'; 9 | 10 | const steps = [providerSteps, configSteps, eventSteps, flagSteps, contextSteps]; 11 | 12 | jest.setTimeout(50000); 13 | describe('in-process', () => { 14 | const state: State = { 15 | resolverType: 'in-process', 16 | options: {}, 17 | config: undefined, 18 | events: [], 19 | }; 20 | autoBindSteps( 21 | loadFeatures(GHERKIN_FLAGD, { 22 | // remove filters as we add support for features 23 | // see: https://github.com/open-feature/js-sdk-contrib/issues/1096 and child issues 24 | tagFilter: 25 | '@in-process and not @targetURI and not @customCert and not @events and not @sync and not @grace and not @metadata and not @contextEnrichment', 26 | scenarioNameTemplate: (vars) => { 27 | return `${vars.scenarioTitle} (${vars.scenarioTags.join(',')} ${vars.featureTags.join(',')})`; 28 | }, 29 | }), 30 | steps.map((step) => step(state)), 31 | ); 32 | }); 33 | -------------------------------------------------------------------------------- /libs/providers/flagd/src/e2e/tests/rpc.spec.ts: -------------------------------------------------------------------------------- 1 | import { autoBindSteps, loadFeatures } from 'jest-cucumber'; 2 | import { providerSteps } from '../step-definitions/providerSteps'; 3 | import { configSteps } from '../step-definitions/configSteps'; 4 | import type { State } from '../step-definitions/state'; 5 | import { eventSteps } from '../step-definitions/eventSteps'; 6 | import { flagSteps } from '../step-definitions/flagSteps'; 7 | import { contextSteps } from '../step-definitions/contextSteps'; 8 | import { GHERKIN_FLAGD } from '../constants'; 9 | 10 | const steps = [providerSteps, configSteps, eventSteps, flagSteps, contextSteps]; 11 | 12 | jest.setTimeout(50000); 13 | 14 | describe('rpc', () => { 15 | const state: State = { 16 | resolverType: 'rpc', 17 | options: {}, 18 | config: undefined, 19 | events: [], 20 | }; 21 | autoBindSteps( 22 | loadFeatures(GHERKIN_FLAGD, { 23 | tagFilter: 24 | // remove filters as we add support for features 25 | // see: https://github.com/open-feature/js-sdk-contrib/issues/1096 and child issues 26 | '@rpc and not @targetURI and not @customCert and not @events and not @stream and not @grace and not @metadata and not @contextEnrichment and not @caching', 27 | scenarioNameTemplate: (vars) => { 28 | return `${vars.scenarioTitle} (${vars.scenarioTags.join(',')} ${vars.featureTags.join(',')})`; 29 | }, 30 | }), 31 | steps.map((step) => step(state)), 32 | ); 33 | }); 34 | -------------------------------------------------------------------------------- /libs/providers/flagd/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/flagd-provider'; 2 | -------------------------------------------------------------------------------- /libs/providers/flagd/src/lib/constants.ts: -------------------------------------------------------------------------------- 1 | export const BASE_EVENT_STREAM_RETRY_BACKOFF_MS = 1000; 2 | export const DEFAULT_MAX_EVENT_STREAM_RETRIES = Infinity; 3 | export const EVENT_CONFIGURATION_CHANGE = 'configuration_change'; 4 | export const EVENT_PROVIDER_READY = 'provider_ready'; 5 | export const DEFAULT_MAX_CACHE_SIZE = 1000; 6 | -------------------------------------------------------------------------------- /libs/providers/flagd/src/lib/service/common/grpc-util.ts: -------------------------------------------------------------------------------- 1 | import type { ClientReadableStream } from '@grpc/grpc-js'; 2 | 3 | export const closeStreamIfDefined = (stream: ClientReadableStream | undefined) => { 4 | /** 5 | * cancel() is necessary to prevent calls from hanging the process, so we need to we need to remove all the 6 | * handlers, and add a no-op for 'error' so we can cancel without bubbling up an exception 7 | */ 8 | if (stream) { 9 | stream.removeAllListeners(); 10 | stream.on('error', () => { 11 | // swallow errors after closed 12 | }); 13 | stream.cancel(); 14 | stream.destroy(); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /libs/providers/flagd/src/lib/service/common/index.ts: -------------------------------------------------------------------------------- 1 | export * from './grpc-util'; 2 | -------------------------------------------------------------------------------- /libs/providers/flagd/src/lib/service/in-process/data-fetch.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Contract of in-process resolver's data fetcher 3 | */ 4 | export interface DataFetch { 5 | /** 6 | * Connects the data fetcher 7 | */ 8 | connect( 9 | /** 10 | * Callback that runs when data is received from the source 11 | * @param flags The flags from the source 12 | * @returns The flags that have changed 13 | */ 14 | dataCallback: (flags: string) => string[], 15 | /** 16 | * Callback that runs when the connection is re-established 17 | */ 18 | reconnectCallback: () => void, 19 | /** 20 | * Callback that runs when flags have changed 21 | * @param flagsChanged The flags that have changed 22 | */ 23 | changedCallback: (flagsChanged: string[]) => void, 24 | /** 25 | * Callback that runs when the connection is disconnected 26 | * @param message The reason for the disconnection 27 | */ 28 | disconnectCallback: (message: string) => void, 29 | ): Promise; 30 | 31 | /** 32 | * Disconnects the data fetcher 33 | */ 34 | disconnect(): Promise; 35 | } 36 | -------------------------------------------------------------------------------- /libs/providers/flagd/src/lib/service/service.ts: -------------------------------------------------------------------------------- 1 | import type { EvaluationContext, JsonValue, Logger, ResolutionDetails } from '@openfeature/server-sdk'; 2 | 3 | export interface Service { 4 | connect( 5 | reconnectCallback: () => void, 6 | changedCallback: (flagsChanged: string[]) => void, 7 | disconnectCallback: (message: string) => void, 8 | ): Promise; 9 | 10 | disconnect(): Promise; 11 | 12 | resolveBoolean( 13 | flagKey: string, 14 | defaultValue: boolean, 15 | context: EvaluationContext, 16 | logger: Logger, 17 | ): Promise>; 18 | 19 | resolveString( 20 | flagKey: string, 21 | defaultValue: string, 22 | context: EvaluationContext, 23 | logger: Logger, 24 | ): Promise>; 25 | 26 | resolveNumber( 27 | flagKey: string, 28 | defaultValue: number, 29 | context: EvaluationContext, 30 | logger: Logger, 31 | ): Promise>; 32 | 33 | resolveObject( 34 | flagKey: string, 35 | defaultValue: T, 36 | context: EvaluationContext, 37 | logger: Logger, 38 | ): Promise>; 39 | } 40 | -------------------------------------------------------------------------------- /libs/providers/flagd/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ES6", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": false, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "resolveJsonModule": true 12 | }, 13 | "files": [], 14 | "include": [], 15 | "references": [ 16 | { 17 | "path": "./tsconfig.lib.json" 18 | }, 19 | { 20 | "path": "./tsconfig.spec.json" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /libs/providers/flagd/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": [] 7 | }, 8 | "include": ["**/*.ts"], 9 | "exclude": ["jest.config.ts", "**/*.spec.ts", "**/*.test.ts", "./src/e2e"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/flagd/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts", "./src/e2e"] 9 | } 10 | -------------------------------------------------------------------------------- /libs/providers/flagsmith-client/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*", "node_modules/**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | }, 17 | { 18 | "files": ["*.json"], 19 | "parser": "jsonc-eslint-parser", 20 | "rules": { 21 | "@nx/dependency-checks": "error" 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /libs/providers/flagsmith-client/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [0.1.3](https://github.com/open-feature/js-sdk-contrib/compare/flagsmith-client-provider-v0.1.2...flagsmith-client-provider-v0.1.3) (2025-03-17) 4 | 5 | 6 | ### 🧹 Chore 7 | 8 | * **deps:** update dependency flagsmith to v4.1.4 ([#1124](https://github.com/open-feature/js-sdk-contrib/issues/1124)) ([1f8679e](https://github.com/open-feature/js-sdk-contrib/commit/1f8679e77fe4c4a7bd7390dfd46dbf8ddb6c49e4)) 9 | * update nx packages ([#1147](https://github.com/open-feature/js-sdk-contrib/issues/1147)) ([7f310fe](https://github.com/open-feature/js-sdk-contrib/commit/7f310fe87101b8aa793e1436e63c7602ccc202e3)) 10 | 11 | 12 | ### 📚 Documentation 13 | 14 | * **flagsmith:** Show example traits and targeting key ([#989](https://github.com/open-feature/js-sdk-contrib/issues/989)) ([1585b60](https://github.com/open-feature/js-sdk-contrib/commit/1585b60672290f0170d68534f12a90d10e31899f)) 15 | 16 | ## [0.1.2](https://github.com/open-feature/js-sdk-contrib/compare/flagsmith-client-provider-v0.1.1...flagsmith-client-provider-v0.1.2) (2024-04-08) 17 | 18 | 19 | ### 🐛 Bug Fixes 20 | 21 | * flagsmith project paths ([#862](https://github.com/open-feature/js-sdk-contrib/issues/862)) ([d423647](https://github.com/open-feature/js-sdk-contrib/commit/d423647b43e4762d901d9894900cf33c314ae9fe)) 22 | 23 | ## [0.1.1](https://github.com/open-feature/js-sdk-contrib/compare/flagsmith-client-provider-v0.1.0...flagsmith-client-provider-v0.1.1) (2024-04-08) 24 | 25 | 26 | ### ✨ New Features 27 | 28 | * Add Flagsmith Provider ([#836](https://github.com/open-feature/js-sdk-contrib/issues/836)) ([dc6e77f](https://github.com/open-feature/js-sdk-contrib/commit/dc6e77f777bdff920d47fde2716f7098a9a767eb)) 29 | -------------------------------------------------------------------------------- /libs/providers/flagsmith-client/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["minify", { "builtIns": false }]] 3 | } 4 | -------------------------------------------------------------------------------- /libs/providers/flagsmith-client/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'providers-flagsmith-client', 4 | preset: '../../../jest.preset.js', 5 | transform: { 6 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 7 | }, 8 | moduleFileExtensions: ['ts', 'js', 'html'] 9 | }; 10 | -------------------------------------------------------------------------------- /libs/providers/flagsmith-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/flagsmith-client-provider", 3 | "version": "0.1.3", 4 | "license": "Apache-2.0", 5 | "main": "./src/index.js", 6 | "typings": "./src/index.d.ts", 7 | "scripts": { 8 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 9 | "current-version": "echo $npm_package_version" 10 | }, 11 | "peerDependencies": { 12 | "@openfeature/web-sdk": "^1.0.0", 13 | "flagsmith": "^4.1.4" 14 | }, 15 | "dependencies": {} 16 | } 17 | -------------------------------------------------------------------------------- /libs/providers/flagsmith-client/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/flagsmith-client-provider'; 2 | -------------------------------------------------------------------------------- /libs/providers/flagsmith-client/src/lib/type-factory.ts: -------------------------------------------------------------------------------- 1 | import type { FlagValue } from '@openfeature/web-sdk'; 2 | 3 | export type FlagType = 'string' | 'number' | 'object' | 'boolean'; 4 | 5 | /** 6 | * Ret a value of the specified type based on the type parameter. 7 | * 8 | * @param value - The value to be converted or validated. 9 | * @param type - The target type for the conversion. 10 | * @returns The converted value if successful, or null if conversion fails or the type is unsupported. 11 | */ 12 | export const typeFactory = ( 13 | value: string | number | boolean | null | undefined, 14 | type: FlagType, 15 | ): FlagValue | undefined => { 16 | if (value === null) return undefined; 17 | switch (type) { 18 | case 'string': 19 | return value !== null && typeof value !== 'undefined' ? `${value}` : value; 20 | case 'number': 21 | return typeof value === 'number' ? value : parseFloat(value as string) || value; 22 | case 'boolean': 23 | return typeof value === 'boolean' ? value : false; 24 | case 'object': 25 | if (typeof value === 'string') { 26 | try { 27 | return JSON.parse(value); 28 | } catch (error) { 29 | return value; 30 | } 31 | } 32 | return value; 33 | default: 34 | return value; 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /libs/providers/flagsmith-client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ES6", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/providers/flagsmith-client/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.mocks.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/flagsmith-client/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.mocks.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /libs/providers/flipt-web/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | }, 17 | { 18 | "files": ["*.json"], 19 | "parser": "jsonc-eslint-parser", 20 | "rules": { 21 | "@nx/dependency-checks": "error" 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /libs/providers/flipt-web/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["minify", { "builtIns": false }]] 3 | } 4 | -------------------------------------------------------------------------------- /libs/providers/flipt-web/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'providers-flipt-web', 4 | preset: '../../../jest.preset.js', 5 | transform: { 6 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 7 | }, 8 | testEnvironment: 'jsdom', 9 | moduleFileExtensions: ['ts', 'js', 'html'], 10 | coverageDirectory: '../../../coverage/libs/providers/flipt-web', 11 | setupFiles: ['/jest.polyfills.js'], 12 | }; 13 | -------------------------------------------------------------------------------- /libs/providers/flipt-web/jest.polyfills.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | // jest.polyfills.js 3 | /** 4 | * @note The block below contains polyfills for Node.js globals 5 | * required for Jest to function when running JSDOM tests. 6 | * These HAVE to be require's and HAVE to be in this exact 7 | * order, since "undici" depends on the "TextEncoder" global API. 8 | */ 9 | 10 | const { TextDecoder, TextEncoder } = require('node:util'); 11 | const { ReadableStream } = require('node:stream/web'); 12 | 13 | Object.defineProperties(globalThis, { 14 | TextDecoder: { value: TextDecoder }, 15 | TextEncoder: { value: TextEncoder }, 16 | ReadableStream: { value: ReadableStream }, 17 | }); 18 | 19 | const { Blob, File } = require('node:buffer'); 20 | const { fetch, Headers, FormData, Request, Response } = require('undici'); 21 | 22 | Object.defineProperties(globalThis, { 23 | fetch: { value: fetch, writable: true }, 24 | Blob: { value: Blob }, 25 | File: { value: File }, 26 | Headers: { value: Headers }, 27 | FormData: { value: FormData }, 28 | Request: { value: Request }, 29 | Response: { value: Response }, 30 | }); 31 | -------------------------------------------------------------------------------- /libs/providers/flipt-web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/flipt-web-provider", 3 | "version": "0.1.4", 4 | "license": "Apache-2.0", 5 | "main": "./src/index.js", 6 | "typings": "./src/index.d.ts", 7 | "scripts": { 8 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 9 | "current-version": "echo $npm_package_version" 10 | }, 11 | "peerDependencies": { 12 | "@openfeature/web-sdk": "^1.0.0", 13 | "@flipt-io/flipt-client-js": "^0.0.1 || ^0.0.2", 14 | "undici": "^5.0.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /libs/providers/flipt-web/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/flipt-web-provider'; 2 | -------------------------------------------------------------------------------- /libs/providers/flipt-web/src/lib/context-transformer.spec.ts: -------------------------------------------------------------------------------- 1 | import type { EvaluationContext } from '@openfeature/server-sdk'; 2 | import { transformContext } from './context-transformer'; 3 | 4 | describe('context-transformer', () => { 5 | describe('transformContext', () => { 6 | it('should transform context correctly', () => { 7 | const context: EvaluationContext = { 8 | targetingKey: 'entity', 9 | customProp: 'test', 10 | }; 11 | 12 | const transformedContext: Record = transformContext(context); 13 | 14 | expect(transformedContext['customProp']).toBe('test'); 15 | expect(transformedContext['targetingKey']).toBeUndefined(); 16 | }); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /libs/providers/flipt-web/src/lib/context-transformer.ts: -------------------------------------------------------------------------------- 1 | import type { EvaluationContext } from '@openfeature/web-sdk'; 2 | 3 | export function transformContext(context: EvaluationContext): Record { 4 | const evalContext: Record = {}; 5 | for (const value in context) { 6 | if (value !== 'targetingKey') { 7 | evalContext[value] = context[value]?.toString() ?? ''; 8 | } 9 | } 10 | 11 | return evalContext; 12 | } 13 | -------------------------------------------------------------------------------- /libs/providers/flipt-web/src/lib/models.ts: -------------------------------------------------------------------------------- 1 | export interface FliptWebProviderOptions { 2 | url?: string; 3 | authentication?: FliptWebProviderAuthentication; 4 | fetcher?: () => Promise; 5 | } 6 | 7 | export interface FliptClientTokenAuthentication { 8 | clientToken: string; 9 | } 10 | 11 | export interface FliptJwtAuthentication { 12 | jwtToken: string; 13 | } 14 | 15 | export type FliptWebProviderAuthentication = FliptClientTokenAuthentication | FliptJwtAuthentication; 16 | 17 | export enum EvaluationReason { 18 | FLAG_DISABLED = 'FLAG_DISABLED_EVALUATION_REASON', 19 | MATCH = 'MATCH_EVALUATION_REASON', 20 | DEFAULT = 'DEFAULT_EVALUATION_REASON', 21 | UNKNOWN = 'UNKNOWN_EVALUATION_REASON', 22 | } 23 | -------------------------------------------------------------------------------- /libs/providers/flipt-web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ES6", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/providers/flipt-web/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/flipt-web/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /libs/providers/flipt/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | }, 17 | { 18 | "files": ["*.json"], 19 | "parser": "jsonc-eslint-parser", 20 | "rules": { 21 | "@nx/dependency-checks": "error" 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /libs/providers/flipt/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["minify", { "builtIns": false }]] 3 | } 4 | -------------------------------------------------------------------------------- /libs/providers/flipt/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'providers-flipt', 4 | preset: '../../../jest.preset.js', 5 | transform: { 6 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 7 | }, 8 | moduleFileExtensions: ['ts', 'js', 'html'], 9 | coverageDirectory: '../../../coverage/libs/providers/flipt', 10 | }; 11 | -------------------------------------------------------------------------------- /libs/providers/flipt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/flipt-provider", 3 | "version": "0.1.3", 4 | "license": "Apache-2.0", 5 | "main": "./src/index.js", 6 | "typings": "./src/index.d.ts", 7 | "scripts": { 8 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 9 | "current-version": "echo $npm_package_version" 10 | }, 11 | "peerDependencies": { 12 | "@openfeature/server-sdk": "^1.13.0", 13 | "@flipt-io/flipt": "^1.2.0" 14 | }, 15 | "dependencies": {} 16 | } 17 | -------------------------------------------------------------------------------- /libs/providers/flipt/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/flipt-provider'; 2 | -------------------------------------------------------------------------------- /libs/providers/flipt/src/lib/context-transformer.spec.ts: -------------------------------------------------------------------------------- 1 | import type { EvaluationContext } from '@openfeature/server-sdk'; 2 | import { transformContext } from './context-transformer'; 3 | 4 | describe('context-transformer', () => { 5 | describe('transformContext', () => { 6 | it('should transform context correctly', () => { 7 | const context: EvaluationContext = { 8 | targetingKey: 'entity', 9 | customProp: 'test', 10 | }; 11 | 12 | const transformedContext: Record = transformContext(context); 13 | 14 | expect(transformedContext['customProp']).toBe('test'); 15 | expect(transformedContext['targetingKey']).toBeUndefined(); 16 | }); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /libs/providers/flipt/src/lib/context-transformer.ts: -------------------------------------------------------------------------------- 1 | import type { EvaluationContext } from '@openfeature/server-sdk'; 2 | 3 | export function transformContext(context: EvaluationContext): Record { 4 | const evalContext: Record = {}; 5 | for (const value in context) { 6 | if (value !== 'targetingKey') { 7 | evalContext[value] = context[value]?.toString() ?? ''; 8 | } 9 | } 10 | 11 | return evalContext; 12 | } 13 | -------------------------------------------------------------------------------- /libs/providers/flipt/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ES6", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/providers/flipt/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/flipt/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag-web/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag-web/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["minify", { "builtIns": false }]] 3 | } 4 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag-web/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'providers-go-feature-flag-web', 4 | preset: '../../../jest.preset.js', 5 | globals: { 6 | 'ts-jest': { 7 | tsconfig: '/tsconfig.spec.json', 8 | }, 9 | }, 10 | transform: { 11 | '^.+\\.[tj]s$': 'ts-jest', 12 | }, 13 | testEnvironment: 'jsdom', 14 | moduleFileExtensions: ['ts', 'js', 'html'], 15 | coverageDirectory: '../../../coverage/libs/providers/go-feature-flag-web', 16 | }; 17 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag-web/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/go-feature-flag-web-provider", 3 | "version": "0.2.6", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@openfeature/go-feature-flag-web-provider", 9 | "version": "0.2.6", 10 | "peerDependencies": { 11 | "@openfeature/web-sdk": "*" 12 | } 13 | }, 14 | "node_modules/@openfeature/web-sdk": { 15 | "version": "0.3.7-experimental", 16 | "resolved": "https://registry.npmjs.org/@openfeature/web-sdk/-/web-sdk-0.3.7-experimental.tgz", 17 | "integrity": "sha512-FK9PTy+Wsw5OmCJlCD6wZsUZlnHTCycMHOU+2RNQVarct+c2l7pkz3XwKJUBjY3BHyOcW3qJEf4ojpO1fU3eJA==", 18 | "peer": true 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag-web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/go-feature-flag-web-provider", 3 | "version": "0.2.6", 4 | "license": "Apache-2.0", 5 | "scripts": { 6 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 7 | "current-version": "echo $npm_package_version" 8 | }, 9 | "peerDependencies": { 10 | "@openfeature/web-sdk": "^1.0.0" 11 | }, 12 | "dependencies": { 13 | "copy-anything": "^3.0.5" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag-web/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/go-feature-flag-web-provider'; 2 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag-web/src/lib/context-transformer.ts: -------------------------------------------------------------------------------- 1 | import type { GoFeatureFlagEvaluationContext } from './model'; 2 | import type { EvaluationContext } from '@openfeature/web-sdk'; 3 | import { TargetingKeyMissingError } from '@openfeature/web-sdk'; 4 | 5 | /** 6 | * transformContext takes the raw OpenFeature context returns a GoFeatureFlagEvaluationContext. 7 | * @param context - the context used for flag evaluation. 8 | * @returns {GoFeatureFlagEvaluationContext} the user against who we will evaluate the flag. 9 | */ 10 | export function transformContext(context: EvaluationContext): GoFeatureFlagEvaluationContext { 11 | const { targetingKey, ...attributes } = context; 12 | if (targetingKey === undefined || targetingKey === null || targetingKey === '') { 13 | throw new TargetingKeyMissingError(); 14 | } 15 | return { 16 | key: targetingKey, 17 | custom: attributes, 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag-web/src/lib/data-collector-hook.ts: -------------------------------------------------------------------------------- 1 | import type { EvaluationDetails, FlagValue, Hook, HookContext } from '@openfeature/web-sdk'; 2 | import type { CollectorManager } from './collector-manager'; 3 | 4 | const defaultTargetingKey = 'undefined-targetingKey'; 5 | type Timer = ReturnType; 6 | 7 | export class GoFeatureFlagDataCollectorHook implements Hook { 8 | private collectorManagger?: CollectorManager; 9 | 10 | constructor(collectorManager: CollectorManager) { 11 | this.collectorManagger = collectorManager; 12 | } 13 | 14 | after(hookContext: HookContext, evaluationDetails: EvaluationDetails) { 15 | const event = { 16 | contextKind: hookContext.context['anonymous'] ? 'anonymousUser' : 'user', 17 | kind: 'feature', 18 | creationDate: Math.round(Date.now() / 1000), 19 | default: false, 20 | key: hookContext.flagKey, 21 | value: evaluationDetails.value, 22 | variation: evaluationDetails.variant || 'SdkDefault', 23 | userKey: hookContext.context.targetingKey || defaultTargetingKey, 24 | source: 'PROVIDER_CACHE', 25 | }; 26 | this.collectorManagger?.add(event); 27 | } 28 | 29 | error(hookContext: HookContext) { 30 | const event = { 31 | contextKind: hookContext.context['anonymous'] ? 'anonymousUser' : 'user', 32 | kind: 'feature', 33 | creationDate: Math.round(Date.now() / 1000), 34 | default: true, 35 | key: hookContext.flagKey, 36 | value: hookContext.defaultValue, 37 | variation: 'SdkDefault', 38 | userKey: hookContext.context.targetingKey || defaultTargetingKey, 39 | source: 'PROVIDER_CACHE', 40 | }; 41 | this.collectorManagger?.add(event); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag-web/src/lib/errors/collector-error.ts: -------------------------------------------------------------------------------- 1 | import { GoFeatureFlagError } from './goff-error'; 2 | 3 | /** 4 | * An error occurred while calling the GOFF event collector. 5 | */ 6 | export class CollectorError extends GoFeatureFlagError { 7 | constructor(message?: string, originalError?: Error) { 8 | super(`${message}: ${originalError}`); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag-web/src/lib/errors/fetch-error.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * FetchError is a wrapper around the HTTP error returned by 3 | * the method fetch. 4 | * It allows to throw an error with the status code. 5 | */ 6 | export class FetchError extends Error { 7 | status: number; 8 | constructor(status: number) { 9 | super(`Request failed with status code ${status}`); 10 | this.status = status; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag-web/src/lib/errors/goff-error.ts: -------------------------------------------------------------------------------- 1 | export class GoFeatureFlagError extends Error {} 2 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag-web/src/lib/test-logger.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * TestLogger is a logger build for testing purposes. 3 | * This is not ready to be production ready, so please avoid using it. 4 | */ 5 | export default class TestLogger { 6 | public inMemoryLogger: Record = { 7 | error: [], 8 | warn: [], 9 | info: [], 10 | debug: [], 11 | }; 12 | 13 | error(...args: unknown[]): void { 14 | this.inMemoryLogger['error'].push(args.join(' ')); 15 | } 16 | 17 | warn(...args: unknown[]): void { 18 | this.inMemoryLogger['warn'].push(args.join(' ')); 19 | } 20 | 21 | info(...args: unknown[]): void { 22 | this.inMemoryLogger['info'].push(args.join(' ')); 23 | } 24 | 25 | debug(...args: unknown[]): void { 26 | this.inMemoryLogger['debug'].push(args.join(' ')); 27 | } 28 | 29 | reset() { 30 | this.inMemoryLogger = { 31 | error: [], 32 | warn: [], 33 | info: [], 34 | debug: [], 35 | }; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag-web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ES6", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": false, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag-web/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "lib": ["ES2015", "DOM"], 5 | "outDir": "../../../dist/out-tsc", 6 | "declaration": true, 7 | "types": [], 8 | "allowSyntheticDefaultImports": true 9 | }, 10 | "include": ["**/*.ts"], 11 | "exclude": ["jest.config.ts", "**/*.spec.ts", "**/*.test.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag-web/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@nx/web/babel", 5 | { 6 | "useBuiltIns": "usage" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "rules": { 5 | "@typescript-eslint/no-explicit-any": "off", 6 | "quotes": ["error", "single"] 7 | }, 8 | "overrides": [ 9 | { 10 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.ts", "*.tsx"], 15 | "rules": {} 16 | }, 17 | { 18 | "files": ["*.js", "*.jsx"], 19 | "rules": {} 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag/README.md: -------------------------------------------------------------------------------- 1 | # Server-side Go Feature Flag Provider 2 | 3 | This provider is an implementation for [`go-feature-flag`](https://github.com/thomaspoignant/go-feature-flag) a simple and complete 4 | feature flag solution, without any complex backend system to install, all you need is a file as your backend. 5 | 6 | It uses [`go-feature-flag-relay-proxy`](https://github.com/thomaspoignant/go-feature-flag-relay-proxy) which expose the capabilities of the SDK through an API layer. 7 | 8 | ## Installation 9 | 10 | ``` 11 | $ npm install @openfeature/go-feature-flag-provider 12 | ``` 13 | 14 | Required peer dependencies 15 | 16 | ``` 17 | $ npm install @openfeature/server-sdk 18 | ``` 19 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["minify", { "builtIns": false }]] 3 | } 4 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'provider-go-feature-flag', 4 | preset: '../../../jest.preset.js', 5 | globals: { 6 | 'ts-jest': { 7 | tsconfig: '/tsconfig.spec.json', 8 | }, 9 | }, 10 | transform: { 11 | '^.+\\.[tj]s$': 'ts-jest', 12 | }, 13 | moduleFileExtensions: ['ts', 'js', 'html'], 14 | coverageDirectory: '../../../coverage/libs/providers/go-feature-flag', 15 | testEnvironment: 'node', 16 | }; 17 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/go-feature-flag-provider", 3 | "version": "0.7.8", 4 | "license": "Apache-2.0", 5 | "scripts": { 6 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 7 | "current-version": "echo $npm_package_version" 8 | }, 9 | "peerDependencies": { 10 | "@openfeature/server-sdk": "^1.15.0" 11 | }, 12 | "dependencies": { 13 | "object-hash": "^3.0.0", 14 | "lru-cache": "^11.0.0", 15 | "axios": "1.8.4", 16 | "copy-anything": "^3.0.5" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/go-feature-flag-provider'; 2 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag/src/lib/context-transformer.ts: -------------------------------------------------------------------------------- 1 | import type { EvaluationContext } from '@openfeature/server-sdk'; 2 | import { sha1 } from 'object-hash'; 3 | import type { GOFFEvaluationContext } from './model'; 4 | 5 | /** 6 | * transformContext takes the raw OpenFeature context returns a GoFeatureFlagUser. 7 | * @param context - the context used for flag evaluation. 8 | * @returns {GOFFEvaluationContext} the evaluation context against which we will evaluate the flag. 9 | */ 10 | export function transformContext(context: EvaluationContext): GOFFEvaluationContext { 11 | const { targetingKey, ...attributes } = context; 12 | const key = targetingKey || sha1(context) || 'anonymous'; 13 | return { 14 | key: key, 15 | custom: attributes, 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag/src/lib/errors/collector-error.ts: -------------------------------------------------------------------------------- 1 | import { GoFeatureFlagError } from './goff-error'; 2 | 3 | /** 4 | * An error occurred while calling the GOFF event collector. 5 | */ 6 | export class CollectorError extends GoFeatureFlagError { 7 | constructor(message?: string, originalError?: Error) { 8 | super(message, originalError); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag/src/lib/errors/configuration-change-endpoint-not-found.ts: -------------------------------------------------------------------------------- 1 | import { GoFeatureFlagError } from './goff-error'; 2 | 3 | export class ConfigurationChangeEndpointNotFound extends GoFeatureFlagError { 4 | constructor(message?: string, originalError?: Error) { 5 | super(message, originalError); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag/src/lib/errors/configuration-change-endpoint-unknown-err.ts: -------------------------------------------------------------------------------- 1 | import { GoFeatureFlagError } from './goff-error'; 2 | 3 | export class ConfigurationChangeEndpointUnknownErr extends GoFeatureFlagError { 4 | constructor(message?: string, originalError?: Error) { 5 | super(message, originalError); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag/src/lib/errors/goff-error.ts: -------------------------------------------------------------------------------- 1 | export class GoFeatureFlagError extends Error {} 2 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag/src/lib/errors/proxyNotReady.ts: -------------------------------------------------------------------------------- 1 | import { ErrorCode, OpenFeatureError } from '@openfeature/server-sdk'; 2 | 3 | // ProxyNotReady is an error send when we try to call the relay proxy and he is not ready 4 | // to return a valid response. 5 | export class ProxyNotReady extends OpenFeatureError { 6 | code: ErrorCode; 7 | 8 | constructor(message: string, originalError: Error) { 9 | super(`${message}: ${originalError}`); 10 | Object.setPrototypeOf(this, ProxyNotReady.prototype); 11 | this.code = ErrorCode.PROVIDER_NOT_READY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag/src/lib/errors/proxyTimeout.ts: -------------------------------------------------------------------------------- 1 | import { ErrorCode, OpenFeatureError } from '@openfeature/server-sdk'; 2 | 3 | // ProxyTimeout is an error send when we try to call the relay proxy and he his not responding 4 | // in the appropriate time. 5 | export class ProxyTimeout extends OpenFeatureError { 6 | code: ErrorCode; 7 | 8 | constructor(message: string, originalError: Error) { 9 | super(`${message}: ${originalError}`); 10 | Object.setPrototypeOf(this, ProxyTimeout.prototype); 11 | this.code = ErrorCode.GENERAL; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag/src/lib/errors/unauthorized.ts: -------------------------------------------------------------------------------- 1 | import { ErrorCode, OpenFeatureError } from '@openfeature/server-sdk'; 2 | 3 | // Unauthorized is an error sent when the provider makes an unauthorized call to the relay proxy. 4 | export class Unauthorized extends OpenFeatureError { 5 | code: ErrorCode; 6 | 7 | constructor(message: string) { 8 | super(message); 9 | Object.setPrototypeOf(this, Unauthorized.prototype); 10 | this.code = ErrorCode.GENERAL; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag/src/lib/errors/unknownError.ts: -------------------------------------------------------------------------------- 1 | import { ErrorCode, OpenFeatureError } from '@openfeature/server-sdk'; 2 | 3 | // UnknownError is an error send when something unexpected happened. 4 | export class UnknownError extends OpenFeatureError { 5 | code: ErrorCode; 6 | 7 | constructor(message: string, originalError: Error | unknown) { 8 | super(`${message}: ${originalError}`); 9 | Object.setPrototypeOf(this, UnknownError.prototype); 10 | this.code = ErrorCode.GENERAL; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag/src/lib/test-logger.ts: -------------------------------------------------------------------------------- 1 | export default class TestLogger { 2 | public inMemoryLogger: Record = { 3 | error: [], 4 | warn: [], 5 | info: [], 6 | debug: [], 7 | }; 8 | 9 | error(...args: unknown[]): void { 10 | this.inMemoryLogger['error'].push(args.join(' ')); 11 | } 12 | 13 | warn(...args: unknown[]): void { 14 | this.inMemoryLogger['warn'].push(args.join(' ')); 15 | } 16 | 17 | info(...args: unknown[]): void { 18 | this.inMemoryLogger['info'].push(args.join(' ')); 19 | } 20 | 21 | debug(...args: unknown[]): void { 22 | this.inMemoryLogger['debug'].push(args.join(' ')); 23 | } 24 | 25 | reset() { 26 | this.inMemoryLogger = { 27 | error: [], 28 | warn: [], 29 | info: [], 30 | debug: [], 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ES6", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": [] 7 | }, 8 | "include": ["**/*.ts"], 9 | "exclude": ["jest.config.ts", "**/*.spec.ts", "**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/go-feature-flag/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /libs/providers/growthbook-client/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | }, 17 | { 18 | "files": ["*.json"], 19 | "parser": "jsonc-eslint-parser", 20 | "rules": { 21 | "@nx/dependency-checks": "error" 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /libs/providers/growthbook-client/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [0.1.2](https://github.com/open-feature/js-sdk-contrib/compare/growthbook-client-provider-v0.1.1...growthbook-client-provider-v0.1.2) (2024-09-27) 4 | 5 | 6 | ### 🐛 Bug Fixes 7 | 8 | * missing lodash dep (and tooling fix) ([#1034](https://github.com/open-feature/js-sdk-contrib/issues/1034)) ([013251b](https://github.com/open-feature/js-sdk-contrib/commit/013251b7f42135125465c44b10ea694501ee557c)) 9 | 10 | ## [0.1.1](https://github.com/open-feature/js-sdk-contrib/compare/growthbook-client-provider-v0.1.0...growthbook-client-provider-v0.1.1) (2024-05-17) 11 | 12 | 13 | ### ✨ New Features 14 | 15 | * Initial GrowthBook OpenFeature provider ([#896](https://github.com/open-feature/js-sdk-contrib/issues/896)) ([62ef69b](https://github.com/open-feature/js-sdk-contrib/commit/62ef69b05710b34f99cbc4da1e947f59f97bc00c)) 16 | -------------------------------------------------------------------------------- /libs/providers/growthbook-client/README.md: -------------------------------------------------------------------------------- 1 | # growthbook-client Provider 2 | 3 | ## Installation 4 | 5 | ``` 6 | $ npm install @openfeature/growthbook-client-provider 7 | ``` 8 | 9 | ## Example Setup 10 | 11 | ```typescript 12 | import { GrowthBook, Context, InitOptions } from '@growthbook/growthbook'; 13 | import { GrowthbookClientProvider } from '@openfeature/growthbook-client-provider'; 14 | 15 | /* 16 | * Configure your GrowthBook instance with GrowthBook context 17 | * @see https://docs.growthbook.io/lib/js#step-1-configure-your-app 18 | */ 19 | const gbContext: Context = { 20 | apiHost: 'https://cdn.growthbook.io', 21 | clientKey: 'sdk-abc123', 22 | // Only required if you have feature encryption enabled in GrowthBook 23 | decryptionKey: 'key_abc123', 24 | }; 25 | 26 | /* 27 | * optional init options 28 | * @see https://docs.growthbook.io/lib/js#switching-to-init 29 | */ 30 | const initOptions: InitOptions = { 31 | timeout: 2000, 32 | streaming: true, 33 | }; 34 | 35 | OpenFeature.setProvider(new GrowthbookClientProvider(gbContext, initOptions)); 36 | ``` 37 | 38 | ## Building 39 | 40 | Run `nx package providers-growthbook-client` to build the library. 41 | 42 | ## Running unit tests 43 | 44 | Run `nx test providers-growthbook-client` to execute the unit tests via [Jest](https://jestjs.io). 45 | -------------------------------------------------------------------------------- /libs/providers/growthbook-client/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["minify", { "builtIns": false }]] 3 | } 4 | -------------------------------------------------------------------------------- /libs/providers/growthbook-client/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'providers-growthbook-client', 4 | preset: '../../../jest.preset.js', 5 | transform: { 6 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 7 | }, 8 | moduleFileExtensions: ['ts', 'js', 'html'], 9 | coverageDirectory: '../../../coverage/libs/providers/growthbook-client', 10 | }; 11 | -------------------------------------------------------------------------------- /libs/providers/growthbook-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/growthbook-client-provider", 3 | "version": "0.1.2", 4 | "license": "Apache-2.0", 5 | "dependencies": { 6 | "lodash.isempty": "^4.4.0" 7 | }, 8 | "main": "./src/index.js", 9 | "typings": "./src/index.d.ts", 10 | "scripts": { 11 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 12 | "current-version": "echo $npm_package_version" 13 | }, 14 | "peerDependencies": { 15 | "@growthbook/growthbook": "^1.0.0", 16 | "@openfeature/web-sdk": "^1.0.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /libs/providers/growthbook-client/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/growthbook-client-provider'; 2 | -------------------------------------------------------------------------------- /libs/providers/growthbook-client/src/lib/translate-result.ts: -------------------------------------------------------------------------------- 1 | import type { FeatureResult } from '@growthbook/growthbook'; 2 | import type { ResolutionDetails } from '@openfeature/web-sdk'; 3 | import { ErrorCode, TypeMismatchError } from '@openfeature/web-sdk'; 4 | 5 | const FEATURE_RESULT_ERRORS = ['unknownFeature', 'cyclicPrerequisite']; 6 | 7 | function translateError(errorKind?: string): ErrorCode { 8 | switch (errorKind) { 9 | case 'unknownFeature': 10 | return ErrorCode.FLAG_NOT_FOUND; 11 | case 'cyclicPrerequisite': 12 | return ErrorCode.PARSE_ERROR; 13 | default: 14 | return ErrorCode.GENERAL; 15 | } 16 | } 17 | 18 | export default function translateResult(result: FeatureResult, defaultValue: T): ResolutionDetails { 19 | if (result.value !== null && typeof result.value !== typeof defaultValue) { 20 | throw new TypeMismatchError(`Expected flag type ${typeof defaultValue} but got ${typeof result.value}`); 21 | } 22 | 23 | const resolution: ResolutionDetails = { 24 | value: result.value === null ? defaultValue : result.value, 25 | reason: result.source, 26 | variant: result.experimentResult?.key, 27 | }; 28 | 29 | if (FEATURE_RESULT_ERRORS.includes(result.source)) { 30 | resolution.errorCode = translateError(result.source); 31 | } 32 | 33 | return resolution; 34 | } 35 | -------------------------------------------------------------------------------- /libs/providers/growthbook-client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ES6", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/providers/growthbook-client/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/growthbook-client/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /libs/providers/growthbook/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | }, 17 | { 18 | "files": ["*.json"], 19 | "parser": "jsonc-eslint-parser", 20 | "rules": { 21 | "@nx/dependency-checks": "error" 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /libs/providers/growthbook/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [0.1.2](https://github.com/open-feature/js-sdk-contrib/compare/growthbook-provider-v0.1.1...growthbook-provider-v0.1.2) (2025-01-10) 4 | 5 | 6 | ### ✨ New Features 7 | 8 | * Create a Growthbook server side provider ([#938](https://github.com/open-feature/js-sdk-contrib/issues/938)) ([0e6a486](https://github.com/open-feature/js-sdk-contrib/commit/0e6a4861368f69c8ed109f44464f94240567f110)) 9 | 10 | 11 | ### 🧹 Chore 12 | 13 | * update nx packages ([#1147](https://github.com/open-feature/js-sdk-contrib/issues/1147)) ([7f310fe](https://github.com/open-feature/js-sdk-contrib/commit/7f310fe87101b8aa793e1436e63c7602ccc202e3)) 14 | -------------------------------------------------------------------------------- /libs/providers/growthbook/README.md: -------------------------------------------------------------------------------- 1 | # growthbook Provider 2 | 3 | ## Installation 4 | 5 | ``` 6 | $ npm install @openfeature/growthbook-provider 7 | ``` 8 | 9 | ## Example Setup 10 | 11 | ```typescript 12 | import { GrowthBookClient, ClientOptions, InitOptions } from '@growthbook/growthbook'; 13 | import { GrowthbookProvider } from '@openfeature/growthbook-provider'; 14 | import { OpenFeature } from '@openfeature/server-sdk'; 15 | 16 | /* 17 | * Configure your GrowthBook instance with GrowthBook context 18 | * @see https://docs.growthbook.io/lib/js#step-1-configure-your-app 19 | */ 20 | const gbClientOptions: ClientOptions = { 21 | apiHost: 'https://cdn.growthbook.io', 22 | clientKey: 'sdk-abc123', 23 | // Only required if you have feature encryption enabled in GrowthBook 24 | decryptionKey: 'key_abc123', 25 | }; 26 | 27 | /* 28 | * optional init options 29 | * @see https://docs.growthbook.io/lib/js#switching-to-init 30 | */ 31 | const initOptions: InitOptions = { 32 | timeout: 2000, 33 | streaming: true, 34 | }; 35 | 36 | OpenFeature.setProvider(new GrowthbookProvider(gbClientOptions, initOptions)); 37 | ``` 38 | 39 | ## Building 40 | 41 | Run `nx package providers-growthbook` to build the library. 42 | 43 | ## Running unit tests 44 | 45 | Run `nx test providers-growthbook` to execute the unit tests via [Jest](https://jestjs.io). 46 | -------------------------------------------------------------------------------- /libs/providers/growthbook/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["minify", { "builtIns": false }]] 3 | } 4 | -------------------------------------------------------------------------------- /libs/providers/growthbook/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'providers-growthbook', 4 | preset: '../../../jest.preset.js', 5 | transform: { 6 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 7 | }, 8 | moduleFileExtensions: ['ts', 'js', 'html'], 9 | coverageDirectory: '../../../coverage/libs/providers/growthbook', 10 | }; 11 | -------------------------------------------------------------------------------- /libs/providers/growthbook/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/growthbook-provider", 3 | "version": "0.1.2", 4 | "license": "Apache-2.0", 5 | "main": "./src/index.js", 6 | "typings": "./src/index.d.ts", 7 | "scripts": { 8 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 9 | "current-version": "echo $npm_package_version" 10 | }, 11 | "peerDependencies": { 12 | "@growthbook/growthbook": "^1.3.1", 13 | "@openfeature/server-sdk": "^1.13.0" 14 | }, 15 | "dependencies": {} 16 | } 17 | -------------------------------------------------------------------------------- /libs/providers/growthbook/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/growthbook-provider'; 2 | -------------------------------------------------------------------------------- /libs/providers/growthbook/src/lib/translate-result.ts: -------------------------------------------------------------------------------- 1 | import type { FeatureResult } from '@growthbook/growthbook'; 2 | import type { ResolutionDetails } from '@openfeature/server-sdk'; 3 | import { ErrorCode, TypeMismatchError } from '@openfeature/server-sdk'; 4 | 5 | const FEATURE_RESULT_ERRORS = ['unknownFeature', 'cyclicPrerequisite']; 6 | 7 | function translateError(errorKind?: string): ErrorCode { 8 | switch (errorKind) { 9 | case 'unknownFeature': 10 | return ErrorCode.FLAG_NOT_FOUND; 11 | case 'cyclicPrerequisite': 12 | return ErrorCode.PARSE_ERROR; 13 | default: 14 | return ErrorCode.GENERAL; 15 | } 16 | } 17 | 18 | export default function translateResult(result: FeatureResult, defaultValue: T): ResolutionDetails { 19 | if (result.value !== null && typeof result.value !== typeof defaultValue) { 20 | throw new TypeMismatchError(`Expected flag type ${typeof defaultValue} but got ${typeof result.value}`); 21 | } 22 | 23 | const resolution: ResolutionDetails = { 24 | value: result.value === null ? defaultValue : result.value, 25 | reason: result.source, 26 | variant: result.experimentResult?.key, 27 | }; 28 | 29 | if (FEATURE_RESULT_ERRORS.includes(result.source)) { 30 | resolution.errorCode = translateError(result.source); 31 | } 32 | 33 | return resolution; 34 | } 35 | -------------------------------------------------------------------------------- /libs/providers/growthbook/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ES6", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/providers/growthbook/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/growthbook/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /libs/providers/in-memory/README.md: -------------------------------------------------------------------------------- 1 | # In-Memory Provider 2 | 3 | This component has been deprecated and removed. 4 | The `@openfeature/server-sdk` now contains its own `InMemoryProvider` implementation. -------------------------------------------------------------------------------- /libs/providers/launchdarkly-client/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/providers/launchdarkly-client/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["minify", { "builtIns": false }]] 3 | } 4 | -------------------------------------------------------------------------------- /libs/providers/launchdarkly-client/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'providers-launchdarkly-client', 4 | preset: '../../../jest.preset.js', 5 | transform: { 6 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 7 | }, 8 | moduleFileExtensions: ['ts', 'js', 'html'], 9 | coverageDirectory: '../../../coverage/libs/providers/launchdarkly-client', 10 | }; 11 | -------------------------------------------------------------------------------- /libs/providers/launchdarkly-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/launchdarkly-client-provider", 3 | "version": "0.3.2", 4 | "license": "Apache-2.0", 5 | "scripts": { 6 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 7 | "current-version": "echo $npm_package_version" 8 | }, 9 | "peerDependencies": { 10 | "@openfeature/web-sdk": "^1.0.0", 11 | "launchdarkly-js-client-sdk": ">=3.1.3" 12 | }, 13 | "dependencies": { 14 | "lodash.isempty": "^4.4.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /libs/providers/launchdarkly-client/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/launchdarkly-client-provider'; 2 | -------------------------------------------------------------------------------- /libs/providers/launchdarkly-client/src/lib/test-logger.ts: -------------------------------------------------------------------------------- 1 | //Copyright 2022 Catamorphic, Co. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | //you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | //http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | 15 | //Code taken from https://github.com/launchdarkly/openfeature-node-server/blob/main/__tests__/TestLogger.ts 16 | 17 | import type { LDLogger } from 'launchdarkly-js-client-sdk'; 18 | 19 | export default class TestLogger implements LDLogger { 20 | public logs: string[] = []; 21 | 22 | error(...args: unknown[]): void { 23 | this.logs.push(args.join(' ')); 24 | } 25 | 26 | warn(...args: unknown[]): void { 27 | this.logs.push(args.join(' ')); 28 | } 29 | 30 | info(...args: unknown[]): void { 31 | this.logs.push(args.join(' ')); 32 | } 33 | 34 | debug(...args: unknown[]): void { 35 | this.logs.push(args.join(' ')); 36 | } 37 | 38 | reset() { 39 | this.logs = []; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /libs/providers/launchdarkly-client/src/lib/translate-result.ts: -------------------------------------------------------------------------------- 1 | import type { ResolutionDetails } from '@openfeature/web-sdk'; 2 | import { ErrorCode } from '@openfeature/web-sdk'; 3 | import type { LDEvaluationDetail } from 'launchdarkly-js-client-sdk'; 4 | 5 | function translateErrorKind(errorKind?: string): ErrorCode { 6 | // Error code specification. 7 | // https://github.com/open-feature/spec/blob/main/specification/sections/02-providers.md#requirement-28 8 | switch (errorKind) { 9 | case 'CLIENT_NOT_READY': 10 | return ErrorCode.PROVIDER_NOT_READY; 11 | case 'MALFORMED_FLAG': 12 | return ErrorCode.PARSE_ERROR; 13 | case 'FLAG_NOT_FOUND': 14 | return ErrorCode.FLAG_NOT_FOUND; 15 | case 'USER_NOT_SPECIFIED': 16 | return ErrorCode.TARGETING_KEY_MISSING; 17 | // General errors. 18 | default: 19 | return ErrorCode.GENERAL; 20 | } 21 | } 22 | 23 | /** 24 | * Translate an {@link LDEvaluationDetail} to a {@link ResolutionDetails}. 25 | * @param result The {@link LDEvaluationDetail} to translate. 26 | * @returns An equivalent {@link ResolutionDetails}. 27 | * 28 | * @internal 29 | */ 30 | export default function translateResult(result: LDEvaluationDetail): ResolutionDetails { 31 | const resolution: ResolutionDetails = { 32 | value: result.value, 33 | variant: result.variationIndex?.toString(), 34 | reason: result.reason?.kind, 35 | }; 36 | 37 | if (result.reason?.kind === 'ERROR') { 38 | resolution.errorCode = translateErrorKind(result.reason.errorKind); 39 | } 40 | return resolution; 41 | } 42 | -------------------------------------------------------------------------------- /libs/providers/launchdarkly-client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ES6", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/providers/launchdarkly-client/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/launchdarkly-client/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /libs/providers/multi-provider-web/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | }, 17 | { 18 | "files": ["*.json"], 19 | "parser": "jsonc-eslint-parser", 20 | "rules": { 21 | "@nx/dependency-checks": "error" 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /libs/providers/multi-provider-web/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [0.0.3](https://github.com/open-feature/js-sdk-contrib/compare/multi-provider-web-v0.0.2...multi-provider-web-v0.0.3) (2024-12-24) 4 | 5 | 6 | ### 🧹 Chore 7 | 8 | * update sdks, absorb changes ([#1119](https://github.com/open-feature/js-sdk-contrib/issues/1119)) ([456be7c](https://github.com/open-feature/js-sdk-contrib/commit/456be7c81547478062ef16ac86ad05be71ab6c80)) 9 | 10 | ## [0.0.2](https://github.com/open-feature/js-sdk-contrib/compare/multi-provider-web-v0.0.1...multi-provider-web-v0.0.2) (2024-06-27) 11 | 12 | 13 | ### 🐛 Bug Fixes 14 | 15 | * correct multi-provider-web dependency ([#962](https://github.com/open-feature/js-sdk-contrib/issues/962)) ([b7c4419](https://github.com/open-feature/js-sdk-contrib/commit/b7c4419999e6a4b2edc1883bf78c1ac11afd16b6)) 16 | 17 | 18 | ### ✨ New Features 19 | 20 | * client-side multi-provider implementation ([#942](https://github.com/open-feature/js-sdk-contrib/issues/942)) ([06def3e](https://github.com/open-feature/js-sdk-contrib/commit/06def3e8f8488941c4d4f7abca17739ebc2fbb43)) 21 | -------------------------------------------------------------------------------- /libs/providers/multi-provider-web/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["minify", { "builtIns": false }]] 3 | } 4 | -------------------------------------------------------------------------------- /libs/providers/multi-provider-web/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'providers-multi-provider-web', 4 | preset: '../../../jest.preset.js', 5 | transform: { 6 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 7 | }, 8 | moduleFileExtensions: ['ts', 'js', 'html'], 9 | coverageDirectory: '../../../coverage/libs/providers/multi-provider-web', 10 | }; 11 | -------------------------------------------------------------------------------- /libs/providers/multi-provider-web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/multi-provider-web", 3 | "version": "0.0.3", 4 | "license": "Apache-2.0", 5 | "main": "./src/index.js", 6 | "typings": "./src/index.d.ts", 7 | "scripts": { 8 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 9 | "current-version": "echo $npm_package_version" 10 | }, 11 | "peerDependencies": { 12 | "@openfeature/web-sdk": "^1.4.0" 13 | }, 14 | "dependencies": {} 15 | } 16 | -------------------------------------------------------------------------------- /libs/providers/multi-provider-web/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/multi-provider-web'; 2 | export * from './lib/errors'; 3 | export * from './lib/strategies'; 4 | -------------------------------------------------------------------------------- /libs/providers/multi-provider-web/src/lib/errors.ts: -------------------------------------------------------------------------------- 1 | import type { ErrorCode } from '@openfeature/web-sdk'; 2 | import { GeneralError, OpenFeatureError } from '@openfeature/web-sdk'; 3 | import type { RegisteredProvider } from './types'; 4 | 5 | export class ErrorWithCode extends OpenFeatureError { 6 | constructor( 7 | public code: ErrorCode, 8 | message: string, 9 | ) { 10 | super(message); 11 | } 12 | } 13 | 14 | export class AggregateError extends GeneralError { 15 | constructor( 16 | message: string, 17 | public originalErrors: { source: string; error: unknown }[], 18 | ) { 19 | super(message); 20 | } 21 | } 22 | 23 | export const constructAggregateError = (providerErrors: { error: unknown; providerName: string }[]) => { 24 | const errorsWithSource = providerErrors 25 | .map(({ providerName, error }) => { 26 | return { source: providerName, error }; 27 | }) 28 | .flat(); 29 | 30 | // log first error in the message for convenience, but include all errors in the error object for completeness 31 | return new AggregateError( 32 | `Provider errors occurred: ${errorsWithSource[0].source}: ${errorsWithSource[0].error}`, 33 | errorsWithSource, 34 | ); 35 | }; 36 | 37 | export const throwAggregateErrorFromPromiseResults = ( 38 | result: PromiseSettledResult[], 39 | providerEntries: RegisteredProvider[], 40 | ) => { 41 | const errors = result 42 | .map((r, i) => { 43 | if (r.status === 'rejected') { 44 | return { error: r.reason, providerName: providerEntries[i].name }; 45 | } 46 | return null; 47 | }) 48 | .filter((val): val is { error: unknown; providerName: string } => Boolean(val)); 49 | 50 | if (errors.length) { 51 | throw constructAggregateError(errors); 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /libs/providers/multi-provider-web/src/lib/strategies/FirstMatchStrategy.ts: -------------------------------------------------------------------------------- 1 | import type { FinalResult, ProviderResolutionResult, StrategyPerProviderContext } from './BaseEvaluationStrategy'; 2 | import { BaseEvaluationStrategy } from './BaseEvaluationStrategy'; 3 | import type { EvaluationContext, FlagValue } from '@openfeature/web-sdk'; 4 | import { ErrorCode } from '@openfeature/web-sdk'; 5 | 6 | /** 7 | * Return the first result that did not indicate "flag not found". 8 | * If any provider in the course of evaluation returns or throws an error, throw that error 9 | */ 10 | export class FirstMatchStrategy extends BaseEvaluationStrategy { 11 | override shouldEvaluateNextProvider( 12 | strategyContext: StrategyPerProviderContext, 13 | context: EvaluationContext, 14 | result: ProviderResolutionResult, 15 | ): boolean { 16 | if (this.hasErrorWithCode(result, ErrorCode.FLAG_NOT_FOUND)) { 17 | return true; 18 | } 19 | if (this.hasError(result)) { 20 | return false; 21 | } 22 | return false; 23 | } 24 | 25 | override determineFinalResult( 26 | strategyContext: StrategyPerProviderContext, 27 | context: EvaluationContext, 28 | resolutions: ProviderResolutionResult[], 29 | ): FinalResult { 30 | const finalResolution = resolutions[resolutions.length - 1]; 31 | if (this.hasError(finalResolution)) { 32 | return this.collectProviderErrors(resolutions); 33 | } 34 | return this.resolutionToFinalResult(finalResolution); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /libs/providers/multi-provider-web/src/lib/strategies/FirstSuccessfulStrategy.ts: -------------------------------------------------------------------------------- 1 | import type { FinalResult, ProviderResolutionResult, StrategyPerProviderContext } from './BaseEvaluationStrategy'; 2 | import { BaseEvaluationStrategy } from './BaseEvaluationStrategy'; 3 | import type { EvaluationContext, FlagValue } from '@openfeature/web-sdk'; 4 | 5 | /** 6 | * Return the first result that did result in an error 7 | * If any provider in the course of evaluation returns or throws an error, ignore it as long as there is a successful result 8 | * If there is no successful result, throw all errors 9 | */ 10 | export class FirstSuccessfulStrategy extends BaseEvaluationStrategy { 11 | override shouldEvaluateNextProvider( 12 | strategyContext: StrategyPerProviderContext, 13 | context: EvaluationContext, 14 | result: ProviderResolutionResult, 15 | ): boolean { 16 | // evaluate next only if there was an error 17 | return this.hasError(result); 18 | } 19 | 20 | override determineFinalResult( 21 | strategyContext: StrategyPerProviderContext, 22 | context: EvaluationContext, 23 | resolutions: ProviderResolutionResult[], 24 | ): FinalResult { 25 | const finalResolution = resolutions[resolutions.length - 1]; 26 | if (this.hasError(finalResolution)) { 27 | return this.collectProviderErrors(resolutions); 28 | } 29 | return this.resolutionToFinalResult(finalResolution); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /libs/providers/multi-provider-web/src/lib/strategies/index.ts: -------------------------------------------------------------------------------- 1 | export * from './BaseEvaluationStrategy'; 2 | export * from './FirstMatchStrategy'; 3 | export * from './FirstSuccessfulStrategy'; 4 | export * from './ComparisonStrategy'; 5 | -------------------------------------------------------------------------------- /libs/providers/multi-provider-web/src/lib/types.ts: -------------------------------------------------------------------------------- 1 | // Represents an entry in the constructor's provider array which may or may not have a name set 2 | import type { Provider } from '@openfeature/web-sdk'; 3 | 4 | export type ProviderEntryInput = { 5 | provider: Provider; 6 | name?: string; 7 | }; 8 | 9 | // Represents a processed and "registered" provider entry where a name has been chosen 10 | export type RegisteredProvider = Required; 11 | -------------------------------------------------------------------------------- /libs/providers/multi-provider-web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ES6", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/providers/multi-provider-web/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/multi-provider-web/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /libs/providers/multi-provider/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | }, 17 | { 18 | "files": ["*.json"], 19 | "parser": "jsonc-eslint-parser", 20 | "rules": { 21 | "@nx/dependency-checks": "error" 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /libs/providers/multi-provider/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [0.1.2](https://github.com/open-feature/js-sdk-contrib/compare/multi-provider-v0.1.1...multi-provider-v0.1.2) (2024-12-23) 4 | 5 | 6 | ### 🧹 Chore 7 | 8 | * update sdks, absorb changes ([#1119](https://github.com/open-feature/js-sdk-contrib/issues/1119)) ([456be7c](https://github.com/open-feature/js-sdk-contrib/commit/456be7c81547478062ef16ac86ad05be71ab6c80)) 9 | 10 | ## [0.1.1](https://github.com/open-feature/js-sdk-contrib/compare/multi-provider-v0.1.0...multi-provider-v0.1.1) (2024-06-06) 11 | 12 | 13 | ### ✨ New Features 14 | 15 | * multi provider implementation for Node ([#916](https://github.com/open-feature/js-sdk-contrib/issues/916)) ([0bf2df0](https://github.com/open-feature/js-sdk-contrib/commit/0bf2df06eff3e2901d8b95a85dec1c0b321a475d)) 16 | -------------------------------------------------------------------------------- /libs/providers/multi-provider/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["minify", { "builtIns": false }]] 3 | } 4 | -------------------------------------------------------------------------------- /libs/providers/multi-provider/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'providers-multi-provider', 4 | preset: '../../../jest.preset.js', 5 | transform: { 6 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 7 | }, 8 | moduleFileExtensions: ['ts', 'js', 'html'], 9 | coverageDirectory: '../../../coverage/libs/providers/multi-provider', 10 | }; 11 | -------------------------------------------------------------------------------- /libs/providers/multi-provider/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/multi-provider", 3 | "version": "0.1.2", 4 | "license": "Apache-2.0", 5 | "main": "./src/index.js", 6 | "typings": "./src/index.d.ts", 7 | "scripts": { 8 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 9 | "current-version": "echo $npm_package_version" 10 | }, 11 | "peerDependencies": { 12 | "@openfeature/server-sdk": "^1.17.0" 13 | }, 14 | "dependencies": {} 15 | } 16 | -------------------------------------------------------------------------------- /libs/providers/multi-provider/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/multi-provider'; 2 | export * from './lib/errors'; 3 | export * from './lib/strategies'; 4 | -------------------------------------------------------------------------------- /libs/providers/multi-provider/src/lib/errors.ts: -------------------------------------------------------------------------------- 1 | import type { ErrorCode } from '@openfeature/server-sdk'; 2 | import { GeneralError, OpenFeatureError } from '@openfeature/server-sdk'; 3 | import type { RegisteredProvider } from './types'; 4 | 5 | export class ErrorWithCode extends OpenFeatureError { 6 | constructor( 7 | public code: ErrorCode, 8 | message: string, 9 | ) { 10 | super(message); 11 | } 12 | } 13 | 14 | export class AggregateError extends GeneralError { 15 | constructor( 16 | message: string, 17 | public originalErrors: { source: string; error: unknown }[], 18 | ) { 19 | super(message); 20 | } 21 | } 22 | 23 | export const constructAggregateError = (providerErrors: { error: unknown; providerName: string }[]) => { 24 | const errorsWithSource = providerErrors 25 | .map(({ providerName, error }) => { 26 | return { source: providerName, error }; 27 | }) 28 | .flat(); 29 | 30 | // log first error in the message for convenience, but include all errors in the error object for completeness 31 | return new AggregateError( 32 | `Provider errors occurred: ${errorsWithSource[0].source}: ${errorsWithSource[0].error}`, 33 | errorsWithSource, 34 | ); 35 | }; 36 | 37 | export const throwAggregateErrorFromPromiseResults = ( 38 | result: PromiseSettledResult[], 39 | providerEntries: RegisteredProvider[], 40 | ) => { 41 | const errors = result 42 | .map((r, i) => { 43 | if (r.status === 'rejected') { 44 | return { error: r.reason, providerName: providerEntries[i].name }; 45 | } 46 | return null; 47 | }) 48 | .filter((val): val is { error: unknown; providerName: string } => Boolean(val)); 49 | 50 | if (errors.length) { 51 | throw constructAggregateError(errors); 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /libs/providers/multi-provider/src/lib/strategies/FirstMatchStrategy.ts: -------------------------------------------------------------------------------- 1 | import type { FinalResult, ProviderResolutionResult, StrategyPerProviderContext } from './BaseEvaluationStrategy'; 2 | import { BaseEvaluationStrategy } from './BaseEvaluationStrategy'; 3 | import type { EvaluationContext, FlagValue } from '@openfeature/server-sdk'; 4 | import { ErrorCode, OpenFeatureError, ResolutionDetails } from '@openfeature/server-sdk'; 5 | 6 | /** 7 | * Return the first result that did not indicate "flag not found". 8 | * If any provider in the course of evaluation returns or throws an error, throw that error 9 | */ 10 | export class FirstMatchStrategy extends BaseEvaluationStrategy { 11 | override shouldEvaluateNextProvider( 12 | strategyContext: StrategyPerProviderContext, 13 | context: EvaluationContext, 14 | result: ProviderResolutionResult, 15 | ): boolean { 16 | if (this.hasErrorWithCode(result, ErrorCode.FLAG_NOT_FOUND)) { 17 | return true; 18 | } 19 | if (this.hasError(result)) { 20 | return false; 21 | } 22 | return false; 23 | } 24 | 25 | override determineFinalResult( 26 | strategyContext: StrategyPerProviderContext, 27 | context: EvaluationContext, 28 | resolutions: ProviderResolutionResult[], 29 | ): FinalResult { 30 | const finalResolution = resolutions[resolutions.length - 1]; 31 | if (this.hasError(finalResolution)) { 32 | return this.collectProviderErrors(resolutions); 33 | } 34 | return this.resolutionToFinalResult(finalResolution); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /libs/providers/multi-provider/src/lib/strategies/FirstSuccessfulStrategy.ts: -------------------------------------------------------------------------------- 1 | import type { FinalResult, ProviderResolutionResult, StrategyPerProviderContext } from './BaseEvaluationStrategy'; 2 | import { BaseEvaluationStrategy } from './BaseEvaluationStrategy'; 3 | import type { EvaluationContext, FlagValue } from '@openfeature/server-sdk'; 4 | import { ResolutionDetails } from '@openfeature/server-sdk'; 5 | 6 | /** 7 | * Return the first result that did result in an error 8 | * If any provider in the course of evaluation returns or throws an error, ignore it as long as there is a successful result 9 | * If there is no successful result, throw all errors 10 | */ 11 | export class FirstSuccessfulStrategy extends BaseEvaluationStrategy { 12 | override shouldEvaluateNextProvider( 13 | strategyContext: StrategyPerProviderContext, 14 | context: EvaluationContext, 15 | result: ProviderResolutionResult, 16 | ): boolean { 17 | // evaluate next only if there was an error 18 | return this.hasError(result); 19 | } 20 | 21 | override determineFinalResult( 22 | strategyContext: StrategyPerProviderContext, 23 | context: EvaluationContext, 24 | resolutions: ProviderResolutionResult[], 25 | ): FinalResult { 26 | const finalResolution = resolutions[resolutions.length - 1]; 27 | if (this.hasError(finalResolution)) { 28 | return this.collectProviderErrors(resolutions); 29 | } 30 | return this.resolutionToFinalResult(finalResolution); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /libs/providers/multi-provider/src/lib/strategies/index.ts: -------------------------------------------------------------------------------- 1 | export * from './BaseEvaluationStrategy'; 2 | export * from './FirstMatchStrategy'; 3 | export * from './FirstSuccessfulStrategy'; 4 | export * from './ComparisonStrategy'; 5 | -------------------------------------------------------------------------------- /libs/providers/multi-provider/src/lib/types.ts: -------------------------------------------------------------------------------- 1 | // Represents an entry in the constructor's provider array which may or may not have a name set 2 | import type { Provider } from '@openfeature/server-sdk'; 3 | 4 | export type ProviderEntryInput = { 5 | provider: Provider; 6 | name?: string; 7 | }; 8 | 9 | // Represents a processed and "registered" provider entry where a name has been chosen 10 | export type RegisteredProvider = Required; 11 | -------------------------------------------------------------------------------- /libs/providers/multi-provider/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ES6", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/providers/multi-provider/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/multi-provider/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /libs/providers/ofrep-web/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | }, 17 | { 18 | "files": ["*.json"], 19 | "parser": "jsonc-eslint-parser", 20 | "rules": { 21 | "@nx/dependency-checks": "error" 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /libs/providers/ofrep-web/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["minify", { "builtIns": false }]] 3 | } 4 | -------------------------------------------------------------------------------- /libs/providers/ofrep-web/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'providers-ofrep-web', 4 | preset: '../../../jest.preset.js', 5 | globals: { 6 | 'ts-jest': { 7 | tsconfig: '/tsconfig.spec.json', 8 | }, 9 | }, 10 | transform: { 11 | '^.+\\.[tj]s$': 'ts-jest', 12 | }, 13 | testEnvironment: 'jsdom', 14 | moduleFileExtensions: ['ts', 'js', 'html'], 15 | setupFiles: ['./jest.polyfills.js'], 16 | }; 17 | -------------------------------------------------------------------------------- /libs/providers/ofrep-web/jest.polyfills.js: -------------------------------------------------------------------------------- 1 | // jest.polyfills.js 2 | /** 3 | * @note The block below contains polyfills for Node.js globals 4 | * required for Jest to function when running JSDOM tests. 5 | * These HAVE to be require's and HAVE to be in this exact 6 | * order, since "undici" depends on the "TextEncoder" global API. 7 | * 8 | * Consider migrating to a more modern test runner if 9 | * you don't want to deal with this. 10 | */ 11 | 12 | const { TextDecoder, TextEncoder } = require('node:util'); 13 | 14 | // eslint-disable-next-line no-undef 15 | Object.defineProperties(globalThis, { 16 | TextDecoder: { value: TextDecoder }, 17 | TextEncoder: { value: TextEncoder }, 18 | }); 19 | 20 | const { Blob, File } = require('node:buffer'); 21 | const { fetch, Headers, FormData, Request, Response } = require('undici'); 22 | 23 | // eslint-disable-next-line no-undef 24 | Object.defineProperties(globalThis, { 25 | fetch: { value: fetch, writable: true }, 26 | Blob: { value: Blob }, 27 | File: { value: File }, 28 | Headers: { value: Headers }, 29 | FormData: { value: FormData }, 30 | Request: { value: Request }, 31 | Response: { value: Response }, 32 | }); 33 | -------------------------------------------------------------------------------- /libs/providers/ofrep-web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/ofrep-web-provider", 3 | "version": "0.3.2", 4 | "license": "Apache-2.0", 5 | "main": "./src/index.js", 6 | "typings": "./src/index.d.ts", 7 | "scripts": { 8 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 9 | "current-version": "echo $npm_package_version" 10 | }, 11 | "peerDependencies": { 12 | "@openfeature/web-sdk": "^1.4.0" 13 | }, 14 | "dependencies": { 15 | "undici": "^5.0.0", 16 | "@openfeature/ofrep-core": "^1.0.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /libs/providers/ofrep-web/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/ofrep-web-provider'; 2 | -------------------------------------------------------------------------------- /libs/providers/ofrep-web/src/lib/model/evaluate-flags-response.ts: -------------------------------------------------------------------------------- 1 | export enum BulkEvaluationStatus { 2 | SUCCESS_NO_CHANGES = 'SUCCESS_NO_CHANGES', 3 | SUCCESS_WITH_CHANGES = 'SUCCESS_WITH_CHANGES', 4 | } 5 | 6 | export interface EvaluateFlagsResponse { 7 | /** 8 | * Status of the bulk evaluation. 9 | */ 10 | status: BulkEvaluationStatus; 11 | /** 12 | * The List of flags changed when doing the bulk evaluation. 13 | */ 14 | flags: string[]; 15 | } 16 | -------------------------------------------------------------------------------- /libs/providers/ofrep-web/src/lib/model/in-memory-cache.ts: -------------------------------------------------------------------------------- 1 | import type { FlagMetadata, FlagValue, ResolutionDetails } from '@openfeature/web-sdk'; 2 | import type { ResolutionError } from './resolution-error'; 3 | 4 | /** 5 | * Cache of flag values from bulk evaluation. 6 | */ 7 | export type FlagCache = { [key: string]: ResolutionDetails | ResolutionError }; 8 | 9 | /** 10 | * Cache of metadata from bulk evaluation. 11 | */ 12 | export type MetadataCache = FlagMetadata; 13 | -------------------------------------------------------------------------------- /libs/providers/ofrep-web/src/lib/model/ofrep-web-provider-options.ts: -------------------------------------------------------------------------------- 1 | import type { OFREPProviderBaseOptions } from '@openfeature/ofrep-core'; 2 | 3 | export type OFREPWebProviderOptions = OFREPProviderBaseOptions & { 4 | /** 5 | * pollInterval is the time in milliseconds to wait between we call the OFREP 6 | * API to get the latest evaluation of your flags. 7 | * 8 | * If a negative number is provided, the provider will not poll the OFREP API. 9 | * Default: 30000 10 | */ 11 | pollInterval?: number; // in milliseconds 12 | }; 13 | -------------------------------------------------------------------------------- /libs/providers/ofrep-web/src/lib/model/resolution-error.ts: -------------------------------------------------------------------------------- 1 | import type { ErrorCode, ResolutionReason } from '@openfeature/web-sdk'; 2 | 3 | export type ResolutionError = { 4 | reason: ResolutionReason; 5 | errorCode: ErrorCode; 6 | errorDetails?: string; 7 | }; 8 | 9 | export function isResolutionError(response: unknown): response is ResolutionError { 10 | if (!response || typeof response !== 'object') { 11 | return false; 12 | } 13 | 14 | return 'reason' in response && 'errorCode' in response && !('value' in response); 15 | } 16 | -------------------------------------------------------------------------------- /libs/providers/ofrep-web/test/test-logger.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * TestLogger is a logger build for testing purposes. 3 | * This is not ready to be production ready, so please avoid using it. 4 | */ 5 | export default class TestLogger { 6 | error(...args: unknown[]): void { 7 | console.log(args.join(' ')); 8 | } 9 | 10 | warn(...args: unknown[]): void { 11 | console.log(args.join(' ')); 12 | } 13 | 14 | info(...args: unknown[]): void { 15 | console.log(args.join(' ')); 16 | } 17 | 18 | debug(...args: unknown[]): void { 19 | console.log(args.join(' ')); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /libs/providers/ofrep-web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ES6", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/providers/ofrep-web/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "lib": ["ES2015", "DOM"], 5 | "outDir": "../../../dist/out-tsc", 6 | "declaration": true, 7 | "types": [], 8 | "allowSyntheticDefaultImports": true 9 | }, 10 | "include": ["**/*.ts"], 11 | "exclude": ["jest.config.ts", "**/*.spec.ts", "**/*.test.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /libs/providers/ofrep-web/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /libs/providers/ofrep/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | }, 17 | { 18 | "files": ["*.json"], 19 | "parser": "jsonc-eslint-parser", 20 | "rules": { 21 | "@nx/dependency-checks": "error" 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /libs/providers/ofrep/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["minify", { "builtIns": false }]] 3 | } 4 | -------------------------------------------------------------------------------- /libs/providers/ofrep/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'providers-ofrep', 4 | preset: '../../../jest.preset.js', 5 | transform: { 6 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 7 | }, 8 | moduleFileExtensions: ['ts', 'js', 'html'], 9 | testEnvironment: 'node', 10 | coverageDirectory: '../../../coverage/libs/providers/ofrep', 11 | }; 12 | -------------------------------------------------------------------------------- /libs/providers/ofrep/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/ofrep-provider", 3 | "version": "0.2.1", 4 | "license": "Apache-2.0", 5 | "main": "./src/index.js", 6 | "typings": "./src/index.d.ts", 7 | "scripts": { 8 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 9 | "current-version": "echo $npm_package_version" 10 | }, 11 | "peerDependencies": { 12 | "@openfeature/server-sdk": "^1.6.0" 13 | }, 14 | "dependencies": { 15 | "@openfeature/ofrep-core": "^1.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /libs/providers/ofrep/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/ofrep-provider'; 2 | -------------------------------------------------------------------------------- /libs/providers/ofrep/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ES6", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/providers/ofrep/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/ofrep/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /libs/providers/unleash-web/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | }, 17 | { 18 | "files": ["*.json"], 19 | "parser": "jsonc-eslint-parser", 20 | "rules": { 21 | "@nx/dependency-checks": "error" 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /libs/providers/unleash-web/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [0.1.1](https://github.com/open-feature/js-sdk-contrib/compare/unleash-web-provider-v0.1.0...unleash-web-provider-v0.1.1) (2025-01-10) 4 | 5 | 6 | ### ✨ New Features 7 | 8 | * add unleash web provider ([#1105](https://github.com/open-feature/js-sdk-contrib/issues/1105)) ([19accf8](https://github.com/open-feature/js-sdk-contrib/commit/19accf83876ef72c0741f035c324098460e709b9)) 9 | 10 | 11 | ### 🧹 Chore 12 | 13 | * update nx packages ([#1147](https://github.com/open-feature/js-sdk-contrib/issues/1147)) ([7f310fe](https://github.com/open-feature/js-sdk-contrib/commit/7f310fe87101b8aa793e1436e63c7602ccc202e3)) 14 | -------------------------------------------------------------------------------- /libs/providers/unleash-web/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["minify", { "builtIns": false }]] 3 | } 4 | -------------------------------------------------------------------------------- /libs/providers/unleash-web/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'providers-unleash-web', 4 | preset: '../../../jest.preset.js', 5 | transform: { 6 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 7 | }, 8 | moduleFileExtensions: ['ts', 'js', 'html'], 9 | coverageDirectory: '../../../coverage/libs/providers/unleash-web', 10 | }; 11 | -------------------------------------------------------------------------------- /libs/providers/unleash-web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/unleash-web-provider", 3 | "version": "0.1.1", 4 | "license": "Apache-2.0", 5 | "main": "./src/index.js", 6 | "typings": "./src/index.d.ts", 7 | "scripts": { 8 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 9 | "current-version": "echo $npm_package_version" 10 | }, 11 | "peerDependencies": { 12 | "@openfeature/web-sdk": "^1.0.0", 13 | "unleash-proxy-client": "^3.6.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /libs/providers/unleash-web/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/unleash-web-provider'; 2 | -------------------------------------------------------------------------------- /libs/providers/unleash-web/src/lib/test-logger.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * TestLogger is a logger build for testing purposes. 3 | * This is not ready to be production ready, so please avoid using it. 4 | */ 5 | export default class TestLogger { 6 | public inMemoryLogger: Record = { 7 | error: [], 8 | warn: [], 9 | info: [], 10 | debug: [], 11 | }; 12 | 13 | error(...args: unknown[]): void { 14 | this.inMemoryLogger['error'].push(args.join(' ')); 15 | } 16 | 17 | warn(...args: unknown[]): void { 18 | this.inMemoryLogger['warn'].push(args.join(' ')); 19 | } 20 | 21 | info(...args: unknown[]): void { 22 | console.log(args); 23 | this.inMemoryLogger['info'].push(args.join(' ')); 24 | } 25 | 26 | debug(...args: unknown[]): void { 27 | console.log(args); 28 | this.inMemoryLogger['debug'].push(args.join(' ')); 29 | } 30 | 31 | reset() { 32 | this.inMemoryLogger = { 33 | error: [], 34 | warn: [], 35 | info: [], 36 | debug: [], 37 | }; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /libs/providers/unleash-web/src/lib/unleash-web-provider-config.ts: -------------------------------------------------------------------------------- 1 | import type { IConfig } from 'unleash-proxy-client'; 2 | 3 | export type UnleashConfig = IConfig; 4 | -------------------------------------------------------------------------------- /libs/providers/unleash-web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ES6", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "resolveJsonModule": true, 12 | "esModuleInterop": true, 13 | "allowSyntheticDefaultImports": true 14 | }, 15 | "files": [], 16 | "include": [], 17 | "references": [ 18 | { 19 | "path": "./tsconfig.lib.json" 20 | }, 21 | { 22 | "path": "./tsconfig.spec.json" 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /libs/providers/unleash-web/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/providers/unleash-web/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /libs/shared/config-cat-core/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | }, 17 | { 18 | "files": ["*.json"], 19 | "parser": "jsonc-eslint-parser", 20 | "rules": { 21 | "@nx/dependency-checks": "error" 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /libs/shared/config-cat-core/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [0.1.1](https://github.com/open-feature/js-sdk-contrib/compare/config-cat-core-v0.1.0...config-cat-core-v0.1.1) (2025-03-12) 4 | 5 | 6 | ### 🐛 Bug Fixes 7 | 8 | * **config-cat:** Forward default value to underlying client ([#1214](https://github.com/open-feature/js-sdk-contrib/issues/1214)) ([9d14173](https://github.com/open-feature/js-sdk-contrib/commit/9d14173cf08da3030fc58fea8786b24bafd80403)) 9 | * correct openfeature core peer version ([9753545](https://github.com/open-feature/js-sdk-contrib/commit/9753545cd9bbc647d06ab2cdfeda52cf37550c63)) 10 | 11 | 12 | ### 🧹 Chore 13 | 14 | * **deps:** update dependency @openfeature/core to <=1.6.0 ([#1126](https://github.com/open-feature/js-sdk-contrib/issues/1126)) ([d11f306](https://github.com/open-feature/js-sdk-contrib/commit/d11f30649da398806f9a6cd5917307f1ffb0ef46)) 15 | * update nx packages ([#1147](https://github.com/open-feature/js-sdk-contrib/issues/1147)) ([7f310fe](https://github.com/open-feature/js-sdk-contrib/commit/7f310fe87101b8aa793e1436e63c7602ccc202e3)) 16 | 17 | ## 0.1.0 (2024-07-21) 18 | 19 | 20 | ### ⚠ BREAKING CHANGES 21 | 22 | * implement config cat web provider ([#918](https://github.com/open-feature/js-sdk-contrib/issues/918)) 23 | 24 | ### ✨ New Features 25 | 26 | * implement config cat web provider ([#918](https://github.com/open-feature/js-sdk-contrib/issues/918)) ([e280014](https://github.com/open-feature/js-sdk-contrib/commit/e280014f8998dd2e5f2b7700f0d24842eeafab5f)) 27 | -------------------------------------------------------------------------------- /libs/shared/config-cat-core/README.md: -------------------------------------------------------------------------------- 1 | # config-cat-core 2 | 3 | The core implementation of ConfigCat providers. 4 | -------------------------------------------------------------------------------- /libs/shared/config-cat-core/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'config-cat-core', 4 | preset: '../../../jest.preset.js', 5 | testEnvironment: 'node', 6 | transform: { 7 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 8 | }, 9 | moduleFileExtensions: ['ts', 'js', 'html'], 10 | coverageDirectory: '../../../coverage/libs/shared/config-cat-core', 11 | }; 12 | -------------------------------------------------------------------------------- /libs/shared/config-cat-core/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/config-cat-core", 3 | "version": "0.1.1", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@openfeature/config-cat-core", 9 | "version": "0.1.1", 10 | "peerDependencies": { 11 | "@openfeature/core": "<=1.6.0" 12 | } 13 | }, 14 | "node_modules/@openfeature/core": { 15 | "version": "1.6.0", 16 | "resolved": "https://registry.npmjs.org/@openfeature/core/-/core-1.6.0.tgz", 17 | "integrity": "sha512-QYAtwdreZU9Mi/LXLRzXsUA7PhbtT7+UJfRBMIAy6MidZjMgIbNfoh6+MncXb3UocThn0OsYa8WLfWD9q43eCQ==", 18 | "license": "Apache-2.0", 19 | "peer": true 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /libs/shared/config-cat-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/config-cat-core", 3 | "version": "0.1.1", 4 | "license": "Apache-2.0", 5 | "scripts": { 6 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 7 | "current-version": "echo $npm_package_version" 8 | }, 9 | "peerDependencies": { 10 | "@openfeature/core": "^1.6.0" 11 | }, 12 | "dependencies": { 13 | "configcat-common": "9.3.1", 14 | "configcat-js-ssr": "^8.4.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /libs/shared/config-cat-core/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/result-mapping'; 2 | export * from './lib/context-transformer'; 3 | -------------------------------------------------------------------------------- /libs/shared/config-cat-core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "resolveJsonModule": true 12 | }, 13 | "files": [], 14 | "include": [], 15 | "references": [ 16 | { 17 | "path": "./tsconfig.lib.json" 18 | }, 19 | { 20 | "path": "./tsconfig.spec.json" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /libs/shared/config-cat-core/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": [] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/shared/config-cat-core/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest"] 7 | }, 8 | "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /libs/shared/flagd-core/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*", "test-harness", "flagd-schemas", "spec"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | }, 17 | { 18 | "files": ["*.json"], 19 | "parser": "jsonc-eslint-parser", 20 | "rules": { 21 | "@nx/dependency-checks": "error" 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /libs/shared/flagd-core/README.md: -------------------------------------------------------------------------------- 1 | # flagd-core 2 | 3 | flagd-core contain the core logic of flagd [in-process evaluation](https://flagd.dev/architecture/#in-process-evaluation) provider. 4 | This package is intended to be used by concrete implementations of flagd in-process providers. 5 | 6 | ## Usage 7 | 8 | flagd-core wraps a simple flagd feature flag storage and flag evaluation logic. 9 | 10 | To use this implementation, instantiate a `FlagdCore` and provide valid flagd flag configurations. 11 | 12 | ```typescript 13 | const core = new FlagdCore(); 14 | core.setConfigurations(FLAG_CONFIGURATION_STRING); 15 | ``` 16 | 17 | Once initialization is complete, use matching flag resolving call. 18 | 19 | ```typescript 20 | const resolution = core.resolveBooleanEvaluation('myBoolFlag', false, {}); 21 | ``` 22 | -------------------------------------------------------------------------------- /libs/shared/flagd-core/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'flagd-core', 4 | preset: '../../../jest.preset.js', 5 | testEnvironment: 'node', 6 | transform: { 7 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 8 | }, 9 | moduleFileExtensions: ['ts', 'js', 'html'], 10 | coverageDirectory: '../../../coverage/libs/shared/flagd-core', 11 | }; 12 | -------------------------------------------------------------------------------- /libs/shared/flagd-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/flagd-core", 3 | "version": "1.0.0", 4 | "license": "Apache-2.0", 5 | "scripts": { 6 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 7 | "current-version": "echo $npm_package_version" 8 | }, 9 | "peerDependencies": { 10 | "@openfeature/core": ">=1.6.0" 11 | }, 12 | "dependencies": { 13 | "ajv": "^8.12.0", 14 | "object-hash": "^3.0.0", 15 | "imurmurhash": "^0.1.4", 16 | "semver": "^7.6.3", 17 | "json-logic-engine": "^4.0.2" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /libs/shared/flagd-core/src/e2e/index.ts: -------------------------------------------------------------------------------- 1 | export const E2E_CLIENT_NAME = 'e2e'; 2 | 3 | export const IMAGE_VERSION = 'v0.5.21'; 4 | 5 | export function getGherkinTestPath(file: string, modulePath = 'test-harness/gherkin/'): string { 6 | return `libs/shared/flagd-core/${modulePath}${file}`; 7 | } 8 | -------------------------------------------------------------------------------- /libs/shared/flagd-core/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/flagd-core'; 2 | export * from './lib/feature-flag'; 3 | export * from './lib/storage'; 4 | export * from './e2e'; 5 | -------------------------------------------------------------------------------- /libs/shared/flagd-core/src/lib/targeting/common.ts: -------------------------------------------------------------------------------- 1 | import type { EvaluationContext, Logger } from '@openfeature/core'; 2 | 3 | export const flagdPropertyKey = '$flagd'; 4 | export const flagKeyPropertyKey = 'flagKey'; 5 | export const timestampPropertyKey = 'timestamp'; 6 | export const targetingPropertyKey = 'targetingKey'; 7 | export const loggerSymbol = Symbol.for('flagd.logger'); 8 | 9 | export type EvaluationContextWithLogger = EvaluationContext & { [loggerSymbol]: Logger }; 10 | 11 | export function getLoggerFromContext(context: EvaluationContextWithLogger): Logger { 12 | const logger = context[loggerSymbol]; 13 | if (!logger) { 14 | throw new Error('Logger not found in context'); 15 | } 16 | return logger; 17 | } 18 | -------------------------------------------------------------------------------- /libs/shared/flagd-core/src/lib/targeting/sem-ver.ts: -------------------------------------------------------------------------------- 1 | import { compare, parse } from 'semver'; 2 | import { getLoggerFromContext } from './common'; 3 | import type { EvaluationContextWithLogger } from './common'; 4 | 5 | export const semVerRule = 'sem_ver'; 6 | 7 | export function semVer(data: unknown, context: EvaluationContextWithLogger): boolean { 8 | const logger = getLoggerFromContext(context); 9 | if (!Array.isArray(data)) { 10 | logger.debug(`Invalid ${semVerRule} configuration: Expected an array`); 11 | return false; 12 | } 13 | 14 | const args = Array.from(data); 15 | 16 | if (args.length != 3) { 17 | logger.debug(`Invalid ${semVerRule} configuration: Expected 3 arguments, got ${args.length}`); 18 | return false; 19 | } 20 | 21 | const semVer1 = parse(args[0]); 22 | const semVer2 = parse(args[2]); 23 | 24 | if (!semVer1 || !semVer2) { 25 | logger.debug(`Invalid ${semVerRule} configuration: Unable to parse semver`); 26 | return false; 27 | } 28 | 29 | const operator = String(args[1]); 30 | const result = compare(semVer1, semVer2); 31 | 32 | switch (operator) { 33 | case '=': 34 | return result == 0; 35 | case '!=': 36 | return result != 0; 37 | case '<': 38 | return result < 0; 39 | case '<=': 40 | return result <= 0; 41 | case '>=': 42 | return result >= 0; 43 | case '>': 44 | return result > 0; 45 | case '^': 46 | return semVer1.major == semVer2.major; 47 | case '~': 48 | return semVer1.major == semVer2.major && semVer1.minor == semVer2.minor; 49 | } 50 | 51 | return false; 52 | } 53 | -------------------------------------------------------------------------------- /libs/shared/flagd-core/src/lib/targeting/string-comp.ts: -------------------------------------------------------------------------------- 1 | import { getLoggerFromContext } from './common'; 2 | import type { EvaluationContextWithLogger } from './common'; 3 | 4 | export const startsWithRule = 'starts_with'; 5 | export const endsWithRule = 'ends_with'; 6 | 7 | export function startsWith(data: unknown, context: EvaluationContextWithLogger) { 8 | return compare(startsWithRule, data, context); 9 | } 10 | 11 | export function endsWith(data: unknown, context: EvaluationContextWithLogger) { 12 | return compare(endsWithRule, data, context); 13 | } 14 | 15 | function compare(method: string, data: unknown, context: EvaluationContextWithLogger): boolean { 16 | const logger = getLoggerFromContext(context); 17 | if (!Array.isArray(data)) { 18 | logger.debug('Invalid comparison configuration: input is not an array'); 19 | return false; 20 | } 21 | 22 | if (data.length != 2) { 23 | logger.debug(`Invalid comparison configuration: invalid array length ${data.length}`); 24 | return false; 25 | } 26 | 27 | if (typeof data[0] !== 'string' || typeof data[1] !== 'string') { 28 | logger.debug('Invalid comparison configuration: array values are not strings'); 29 | return false; 30 | } 31 | 32 | switch (method) { 33 | case startsWithRule: 34 | return data[0].startsWith(data[1]); 35 | case endsWithRule: 36 | return data[0].endsWith(data[1]); 37 | default: 38 | logger.debug(`Invalid comparison configuration: Invalid method '${method}'`); 39 | return false; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /libs/shared/flagd-core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "resolveJsonModule": true 12 | }, 13 | "files": [], 14 | "include": [], 15 | "references": [ 16 | { 17 | "path": "./tsconfig.lib.json" 18 | }, 19 | { 20 | "path": "./tsconfig.spec.json" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /libs/shared/flagd-core/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": [] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts", "src/e2e"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/shared/flagd-core/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts", "src/e2e"] 9 | } 10 | -------------------------------------------------------------------------------- /libs/shared/ofrep-core/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | }, 17 | { 18 | "files": ["*.json"], 19 | "parser": "jsonc-eslint-parser", 20 | "rules": { 21 | "@nx/dependency-checks": "error" 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /libs/shared/ofrep-core/README.md: -------------------------------------------------------------------------------- 1 | # ofrep-core 2 | 3 | The core implementation of OFREP core providers. 4 | This package is intended to be used by the concrete OFREP provider implementations for API access, error handling, ... 5 | -------------------------------------------------------------------------------- /libs/shared/ofrep-core/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'ofrep-core', 4 | preset: '../../../jest.preset.js', 5 | testEnvironment: 'node', 6 | transform: { 7 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 8 | }, 9 | moduleFileExtensions: ['ts', 'js', 'html'], 10 | coverageDirectory: '../../../coverage/libs/shared/ofrep-core', 11 | }; 12 | -------------------------------------------------------------------------------- /libs/shared/ofrep-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openfeature/ofrep-core", 3 | "version": "1.0.1", 4 | "license": "Apache-2.0", 5 | "scripts": { 6 | "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi", 7 | "current-version": "echo $npm_package_version" 8 | }, 9 | "peerDependencies": { 10 | "@openfeature/core": "^1.6.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /libs/shared/ofrep-core/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib'; 2 | -------------------------------------------------------------------------------- /libs/shared/ofrep-core/src/lib/api/index.ts: -------------------------------------------------------------------------------- 1 | export * from './errors'; 2 | export * from './ofrep-api'; 3 | -------------------------------------------------------------------------------- /libs/shared/ofrep-core/src/lib/index.ts: -------------------------------------------------------------------------------- 1 | export * from './api'; 2 | export * from './model'; 3 | export * from './provider'; 4 | -------------------------------------------------------------------------------- /libs/shared/ofrep-core/src/lib/model/bulk-evaluation.ts: -------------------------------------------------------------------------------- 1 | import type { ErrorCode } from '@openfeature/core'; 2 | import type { EvaluationResponse, MetadataResponse } from './evaluation'; 3 | 4 | export interface BulkEvaluationFailureResponse extends MetadataResponse { 5 | /** 6 | * An appropriate code specific to the bulk evaluation error. See https://openfeature.dev/specification/types#error-code 7 | */ 8 | errorCode: ErrorCode; 9 | /** 10 | * Optional error details description for logging or other needs 11 | */ 12 | errorDetails?: string; 13 | } 14 | 15 | export function isBulkEvaluationFailureResponse(response: unknown): response is BulkEvaluationFailureResponse { 16 | if (!response || typeof response !== 'object') { 17 | return false; 18 | } 19 | 20 | return 'errorCode' in response; 21 | } 22 | 23 | export interface BulkEvaluationSuccessResponse extends MetadataResponse { 24 | flags?: EvaluationResponse[]; 25 | } 26 | 27 | export type BulkEvaluationNotModifiedResponse = undefined; 28 | 29 | export function isBulkEvaluationSuccessResponse(response: unknown): response is BulkEvaluationSuccessResponse { 30 | if (!response || typeof response !== 'object') { 31 | return false; 32 | } 33 | 34 | return 'flags' in response; 35 | } 36 | 37 | export type BulkEvaluationResponse = 38 | | BulkEvaluationFailureResponse 39 | | BulkEvaluationNotModifiedResponse 40 | | BulkEvaluationSuccessResponse; 41 | -------------------------------------------------------------------------------- /libs/shared/ofrep-core/src/lib/model/index.ts: -------------------------------------------------------------------------------- 1 | export * from './evaluation'; 2 | export * from './bulk-evaluation'; 3 | export * from './ofrep-api-result'; 4 | -------------------------------------------------------------------------------- /libs/shared/ofrep-core/src/lib/provider/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ofrep-provider-options'; 2 | -------------------------------------------------------------------------------- /libs/shared/ofrep-core/src/lib/provider/ofrep-provider-options.ts: -------------------------------------------------------------------------------- 1 | import type { FetchAPI } from '../api'; 2 | 3 | export type OFREPProviderBaseOptions = { 4 | /** 5 | * Base URL for OFREP requests. Relative paths are supported. 6 | * For example, if your OFREP instance is available at 7 | * "https://host.com/path/{ofrep-api}" , you should set this to 8 | * "https://host.com/path" or "/path" (if your app and OFREP instance 9 | * share the same origin). 10 | */ 11 | baseUrl: string; 12 | /** 13 | * Abort timeout in milliseconds. 14 | * 15 | * @default 10000 16 | */ 17 | timeoutMs?: number; 18 | /** 19 | * Optional fetch implementation 20 | */ 21 | fetchImplementation?: FetchAPI; 22 | /** 23 | * Optional Headers supplier function. 24 | * @returns HttpHeaders 25 | */ 26 | headersFactory?: () => Promise<[string, string][]>; 27 | /** 28 | * Optional static headers. 29 | */ 30 | headers?: [string, string][]; 31 | /** 32 | * Optional static query params. 33 | */ 34 | query?: URLSearchParams; 35 | }; 36 | 37 | /** 38 | * Builds headers from static and factory, as well as default content type 39 | * @param options options 40 | * @returns built headers 41 | */ 42 | export async function buildHeaders(options?: OFREPProviderBaseOptions, etag: string | null = null): Promise { 43 | return new Headers([ 44 | ['Content-Type', 'application/json; charset=utf-8'], 45 | ...(options?.headers || []), 46 | ...((await options?.headersFactory?.()) || []), 47 | ...(etag ? ([['If-None-Match', etag]] as [string, string][]) : []), 48 | ]); 49 | } 50 | -------------------------------------------------------------------------------- /libs/shared/ofrep-core/src/test/mock-service-worker.ts: -------------------------------------------------------------------------------- 1 | import { setupServer } from 'msw/node'; 2 | import { handlers } from './handlers'; 3 | 4 | export const server = setupServer(...handlers); 5 | -------------------------------------------------------------------------------- /libs/shared/ofrep-core/src/test/test-constants.ts: -------------------------------------------------------------------------------- 1 | import type { FlagMetadata } from '@openfeature/core'; 2 | 3 | export const TEST_FLAG_METADATA: FlagMetadata = { 4 | booleanKey: true, 5 | stringKey: 'string', 6 | numberKey: 1, 7 | } as const; 8 | 9 | export const TEST_FLAG_SET_METADATA: FlagMetadata = { 10 | flagSetBooleanKey: true, 11 | flagSetStringKey: 'string', 12 | flagSetNumberKey: 1, 13 | } as const; 14 | -------------------------------------------------------------------------------- /libs/shared/ofrep-core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "resolveJsonModule": true 12 | }, 13 | "files": [], 14 | "include": [], 15 | "references": [ 16 | { 17 | "path": "./tsconfig.lib.json" 18 | }, 19 | { 20 | "path": "./tsconfig.spec.json" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /libs/shared/ofrep-core/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": [] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/shared/ofrep-core/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest"] 7 | }, 8 | "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /nx.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/nx/schemas/nx-schema.json", 3 | "pluginsConfig": { 4 | "@nx/js": { 5 | "analyzeSourceFiles": true 6 | } 7 | }, 8 | "targetDefaults": { 9 | "build": { 10 | "dependsOn": ["^build"], 11 | "inputs": ["production", "^production"], 12 | "cache": true 13 | }, 14 | "e2e": { 15 | "cache": true 16 | }, 17 | "@nx/jest:jest": { 18 | "cache": true, 19 | "inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"], 20 | "options": { 21 | "passWithNoTests": true 22 | }, 23 | "configurations": { 24 | "ci": { 25 | "ci": true, 26 | "codeCoverage": true 27 | } 28 | } 29 | }, 30 | "@nx/eslint:lint": { 31 | "cache": true, 32 | "inputs": ["default", "{workspaceRoot}/.eslintrc.json", "{workspaceRoot}/tools/eslint-rules/**/*"] 33 | }, 34 | "@nx/js:tsc": { 35 | "cache": true, 36 | "dependsOn": ["^build"], 37 | "inputs": ["production", "^production"] 38 | } 39 | }, 40 | "namedInputs": { 41 | "default": ["{projectRoot}/**/*", "sharedGlobals"], 42 | "sharedGlobals": [], 43 | "production": ["default"] 44 | }, 45 | "useInferencePlugins": false, 46 | "defaultBase": "main" 47 | } 48 | -------------------------------------------------------------------------------- /project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-sdk-contrib", 3 | "$schema": "node_modules/nx/schemas/project-schema.json", 4 | "targets": { 5 | "local-registry": { 6 | "executor": "@nx/js:verdaccio", 7 | "options": { 8 | "port": 4873, 9 | "config": ".verdaccio/config.yml", 10 | "storage": "tmp/local-registry/storage" 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["github>open-feature/community-tooling"], 4 | "packageRules": [ 5 | { 6 | "matchPackagePatterns": ["^@nx/", "nx"], 7 | "enabled": false 8 | }, 9 | { 10 | "matchPackagePatterns": [ 11 | "^@typescript-eslint/", 12 | "^eslint" 13 | ], 14 | "groupName": "ESLint" 15 | }, 16 | { 17 | "matchPackagePatterns": [ 18 | "^@types/" 19 | ], 20 | "groupName": "Types" 21 | }, 22 | { 23 | "matchUpdateTypes": [ 24 | "minor", 25 | "patch", 26 | "pin", 27 | "digest" 28 | ], 29 | "matchDepTypes": [ 30 | "devDependencies" 31 | ], 32 | "automerge": true 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /tools/tsconfig.tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "../dist/out-tsc/tools", 5 | "rootDir": ".", 6 | "module": "commonjs", 7 | "target": "es2015", 8 | "types": ["node"], 9 | "importHelpers": false 10 | }, 11 | "include": ["**/*.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /tools/workspace-plugin/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | }, 17 | { 18 | "files": ["./package.json", "./generators.json"], 19 | "parser": "jsonc-eslint-parser", 20 | "rules": { 21 | "@nx/nx-plugin-checks": "error" 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /tools/workspace-plugin/generators.json: -------------------------------------------------------------------------------- 1 | { 2 | "generators": { 3 | "open-feature": { 4 | "implementation": "./src/generators/open-feature", 5 | "schema": "./src/generators/open-feature/schema.json", 6 | "description": "Generator open-feature" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tools/workspace-plugin/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'workspace-plugin', 4 | preset: '../../jest.preset.js', 5 | transform: { 6 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 7 | }, 8 | moduleFileExtensions: ['ts', 'js', 'html'], 9 | coverageDirectory: '../../coverage/tools/workspace-plugin', 10 | }; 11 | -------------------------------------------------------------------------------- /tools/workspace-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@js-sdk-contrib/workspace-plugin", 3 | "version": "0.0.1", 4 | "type": "commonjs", 5 | "generators": "./generators.json", 6 | "dependencies": { 7 | "@nx/devkit": "20.3.1", 8 | "@nx/eslint": "20.3.1", 9 | "@nx/js": "20.3.1" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tools/workspace-plugin/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workspace-plugin", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "tools/workspace-plugin/src", 5 | "projectType": "library", 6 | "targets": { 7 | "build": { 8 | "executor": "@nx/js:tsc", 9 | "outputs": ["{options.outputPath}"], 10 | "options": { 11 | "outputPath": "dist/tools/workspace-plugin", 12 | "main": "tools/workspace-plugin/src/index.ts", 13 | "tsConfig": "tools/workspace-plugin/tsconfig.lib.json", 14 | "assets": [ 15 | { 16 | "input": "./tools/workspace-plugin/src", 17 | "glob": "**/!(*.ts)", 18 | "output": "./src" 19 | }, 20 | { 21 | "input": "./tools/workspace-plugin/src", 22 | "glob": "**/*.d.ts", 23 | "output": "./src" 24 | }, 25 | { 26 | "input": "./tools/workspace-plugin", 27 | "glob": "generators.json", 28 | "output": "." 29 | }, 30 | { 31 | "input": "./tools/workspace-plugin", 32 | "glob": "executors.json", 33 | "output": "." 34 | } 35 | ] 36 | } 37 | }, 38 | "lint": { 39 | "executor": "@nx/eslint:lint", 40 | "outputs": ["{options.outputFile}"] 41 | }, 42 | "test": { 43 | "executor": "@nx/jest:jest", 44 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], 45 | "options": { 46 | "jestConfig": "tools/workspace-plugin/jest.config.ts" 47 | } 48 | } 49 | }, 50 | "tags": [] 51 | } 52 | -------------------------------------------------------------------------------- /tools/workspace-plugin/src/generators/open-feature/files/hook/client/README.md__tmpl__: -------------------------------------------------------------------------------- 1 | # <%= name %> Hook 2 | 3 | ## Installation 4 | 5 | ``` 6 | $ npm install <%= importPath %> 7 | ``` 8 | 9 | ## Building 10 | 11 | Run `nx package <%= nxProjectName %>` to build the library. 12 | 13 | ## Running unit tests 14 | 15 | Run `nx test <%= nxProjectName %>` to execute the unit tests via [Jest](https://jestjs.io). 16 | -------------------------------------------------------------------------------- /tools/workspace-plugin/src/generators/open-feature/files/hook/client/src/index.ts__tmpl__: -------------------------------------------------------------------------------- 1 | export * from './lib/<%= libFileName %>'; -------------------------------------------------------------------------------- /tools/workspace-plugin/src/generators/open-feature/files/hook/client/src/lib/__libFileName__.spec.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { <%= libClassName %> } from './<%= libFileName %>'; 2 | 3 | describe('<%= libClassName %>', () => { 4 | it('should be and instance of <%= libClassName %>', () => { 5 | expect(new <%= libClassName %>()).toBeInstanceOf(<%= libClassName %>); 6 | }); 7 | }); -------------------------------------------------------------------------------- /tools/workspace-plugin/src/generators/open-feature/files/hook/client/src/lib/__libFileName__.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { 2 | EvaluationDetails, 3 | FlagValue, 4 | Hook, 5 | HookContext, 6 | HookHints, 7 | } from '@openfeature/web-sdk'; 8 | 9 | export class <%= libClassName %> implements Hook { 10 | before(hookContext: HookContext, hookHints?: HookHints) {} 11 | 12 | after( 13 | hookContext: HookContext, 14 | evaluationDetails: EvaluationDetails, 15 | hookHints?: HookHints 16 | ) {} 17 | 18 | error(hookContext: HookContext, err: unknown, hookHints?: HookHints) {} 19 | 20 | finally(hookContext: HookContext, evaluationDetails: EvaluationDetails, hookHints?: HookHints) {} 21 | } 22 | -------------------------------------------------------------------------------- /tools/workspace-plugin/src/generators/open-feature/files/hook/server/README.md__tmpl__: -------------------------------------------------------------------------------- 1 | # <%= name %> Hook 2 | 3 | ## Installation 4 | 5 | ``` 6 | $ npm install <%= importPath %> 7 | ``` 8 | 9 | ## Building 10 | 11 | Run `nx package <%= nxProjectName %>` to build the library. 12 | 13 | ## Running unit tests 14 | 15 | Run `nx test <%= nxProjectName %>` to execute the unit tests via [Jest](https://jestjs.io). 16 | -------------------------------------------------------------------------------- /tools/workspace-plugin/src/generators/open-feature/files/hook/server/src/index.ts__tmpl__: -------------------------------------------------------------------------------- 1 | export * from './lib/<%= libFileName %>'; -------------------------------------------------------------------------------- /tools/workspace-plugin/src/generators/open-feature/files/hook/server/src/lib/__libFileName__.spec.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { <%= libClassName %> } from './<%= libFileName %>'; 2 | 3 | describe('<%= libClassName %>', () => { 4 | it('should be and instance of <%= libClassName %>', () => { 5 | expect(new <%= libClassName %>()).toBeInstanceOf(<%= libClassName %>); 6 | }); 7 | }); -------------------------------------------------------------------------------- /tools/workspace-plugin/src/generators/open-feature/files/hook/server/src/lib/__libFileName__.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { 2 | EvaluationDetails, 3 | FlagValue, 4 | Hook, 5 | HookContext, 6 | HookHints, 7 | } from '@openfeature/server-sdk'; 8 | 9 | export class <%= libClassName %> implements Hook { 10 | before(hookContext: HookContext, hookHints?: HookHints) {} 11 | 12 | after( 13 | hookContext: HookContext, 14 | evaluationDetails: EvaluationDetails, 15 | hookHints?: HookHints 16 | ) {} 17 | 18 | error(hookContext: HookContext, err: unknown, hookHints?: HookHints) {} 19 | 20 | finally(hookContext: HookContext, evaluationDetails: EvaluationDetails, hookHints?: HookHints) {} 21 | } 22 | -------------------------------------------------------------------------------- /tools/workspace-plugin/src/generators/open-feature/files/provider/client/README.md__tmpl__: -------------------------------------------------------------------------------- 1 | # <%= name %> Provider 2 | 3 | ## Installation 4 | 5 | ``` 6 | $ npm install <%= importPath %> 7 | ``` 8 | 9 | ## Building 10 | 11 | Run `nx package <%= nxProjectName %>` to build the library. 12 | 13 | ## Running unit tests 14 | 15 | Run `nx test <%= nxProjectName %>` to execute the unit tests via [Jest](https://jestjs.io). 16 | -------------------------------------------------------------------------------- /tools/workspace-plugin/src/generators/open-feature/files/provider/client/src/index.ts__tmpl__: -------------------------------------------------------------------------------- 1 | export * from './lib/<%= libFileName %>'; -------------------------------------------------------------------------------- /tools/workspace-plugin/src/generators/open-feature/files/provider/client/src/lib/__libFileName__.spec.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { <%= libClassName %> } from './<%= libFileName %>'; 2 | 3 | describe('<%= libClassName %>', () => { 4 | it('should be and instance of <%= libClassName %>', () => { 5 | expect(new <%= libClassName %>()).toBeInstanceOf(<%= libClassName %>); 6 | }); 7 | }); -------------------------------------------------------------------------------- /tools/workspace-plugin/src/generators/open-feature/files/provider/client/src/lib/__libFileName__.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { 2 | EvaluationContext, 3 | Provider, 4 | JsonValue, 5 | ResolutionDetails, 6 | } from '@openfeature/web-sdk'; 7 | 8 | export class <%= libClassName %> implements Provider { 9 | metadata = { 10 | name: <%= libClassName %>.name 11 | }; 12 | 13 | readonly runsOn = 'client'; 14 | 15 | hooks = []; 16 | 17 | resolveBooleanEvaluation( 18 | flagKey: string, 19 | defaultValue: boolean 20 | ): ResolutionDetails { 21 | throw new Error('Method not implemented.'); 22 | } 23 | 24 | resolveStringEvaluation( 25 | flagKey: string, 26 | defaultValue: string 27 | ): ResolutionDetails { 28 | throw new Error('Method not implemented.'); 29 | } 30 | 31 | resolveNumberEvaluation( 32 | flagKey: string, 33 | defaultValue: number 34 | ): ResolutionDetails { 35 | throw new Error('Method not implemented.'); 36 | } 37 | 38 | resolveObjectEvaluation( 39 | flagKey: string, 40 | defaultValue: U, 41 | ): ResolutionDetails { 42 | throw new Error('Method not implemented.'); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tools/workspace-plugin/src/generators/open-feature/files/provider/server/README.md__tmpl__: -------------------------------------------------------------------------------- 1 | # <%= name %> Provider 2 | 3 | ## Installation 4 | 5 | ``` 6 | $ npm install <%= importPath %> 7 | ``` 8 | 9 | ## Building 10 | 11 | Run `nx package <%= nxProjectName %>` to build the library. 12 | 13 | ## Running unit tests 14 | 15 | Run `nx test <%= nxProjectName %>` to execute the unit tests via [Jest](https://jestjs.io). 16 | -------------------------------------------------------------------------------- /tools/workspace-plugin/src/generators/open-feature/files/provider/server/src/index.ts__tmpl__: -------------------------------------------------------------------------------- 1 | export * from './lib/<%= libFileName %>'; -------------------------------------------------------------------------------- /tools/workspace-plugin/src/generators/open-feature/files/provider/server/src/lib/__libFileName__.spec.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { <%= libClassName %> } from './<%= libFileName %>'; 2 | 3 | describe('<%= libClassName %>', () => { 4 | it('should be and instance of <%= libClassName %>', () => { 5 | expect(new <%= libClassName %>()).toBeInstanceOf(<%= libClassName %>); 6 | }); 7 | }); -------------------------------------------------------------------------------- /tools/workspace-plugin/src/generators/open-feature/files/provider/server/src/lib/__libFileName__.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { 2 | EvaluationContext, 3 | Provider, 4 | JsonValue, 5 | ResolutionDetails, 6 | } from '@openfeature/server-sdk'; 7 | 8 | export class <%= libClassName %> implements Provider { 9 | metadata = { 10 | name: <%= libClassName %>.name 11 | }; 12 | 13 | readonly runsOn = 'server'; 14 | 15 | hooks = []; 16 | 17 | resolveBooleanEvaluation( 18 | flagKey: string, 19 | defaultValue: boolean, 20 | context: EvaluationContext 21 | ): Promise> { 22 | throw new Error('Method not implemented.'); 23 | } 24 | 25 | resolveStringEvaluation( 26 | flagKey: string, 27 | defaultValue: string, 28 | context: EvaluationContext 29 | ): Promise> { 30 | throw new Error('Method not implemented.'); 31 | } 32 | 33 | resolveNumberEvaluation( 34 | flagKey: string, 35 | defaultValue: number, 36 | context: EvaluationContext 37 | ): Promise> { 38 | throw new Error('Method not implemented.'); 39 | } 40 | 41 | resolveObjectEvaluation( 42 | flagKey: string, 43 | defaultValue: U, 44 | context: EvaluationContext 45 | ): Promise> { 46 | throw new Error('Method not implemented.'); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tools/workspace-plugin/src/generators/open-feature/files/shared/babel.config.json__tmpl__: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["minify", { "builtIns": false }]] 3 | } -------------------------------------------------------------------------------- /tools/workspace-plugin/src/generators/open-feature/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "cli": "nx", 4 | "$id": "open-feature", 5 | "type": "object", 6 | "properties": { 7 | "type": { 8 | "type": "string", 9 | "enum": ["hook", "provider"], 10 | "description": "Library type", 11 | "$default": { 12 | "$source": "argv", 13 | "index": 0 14 | }, 15 | "x-prompt": { 16 | "message": "Which type of library would you like to generate?", 17 | "type": "list", 18 | "items": [ 19 | { 20 | "value": "hook", 21 | "label": "Hook" 22 | }, 23 | { 24 | "value": "provider", 25 | "label": "Provider" 26 | } 27 | ] 28 | } 29 | }, 30 | "name": { 31 | "type": "string", 32 | "description": "Library name", 33 | "$default": { 34 | "$source": "argv", 35 | "index": 1 36 | }, 37 | "x-prompt": "What name should be used?" 38 | }, 39 | "category": { 40 | "type": "string", 41 | "description": "Library name", 42 | "$default": { 43 | "$source": "argv", 44 | "index": 1 45 | }, 46 | "x-prompt": { 47 | "message": "Is this library for client (web) or server(node)?", 48 | "type": "list", 49 | "items": [ 50 | { 51 | "value": "client", 52 | "label": "Client" 53 | }, 54 | { 55 | "value": "server", 56 | "label": "Server" 57 | } 58 | ] 59 | } 60 | } 61 | }, 62 | "required": ["type", "name"] 63 | } 64 | -------------------------------------------------------------------------------- /tools/workspace-plugin/src/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-feature/js-sdk-contrib/fbd9f9155d282984444e7a55508871b9eaca3efa/tools/workspace-plugin/src/index.ts -------------------------------------------------------------------------------- /tools/workspace-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs" 5 | }, 6 | "files": [], 7 | "include": [], 8 | "references": [ 9 | { 10 | "path": "./tsconfig.lib.json" 11 | }, 12 | { 13 | "path": "./tsconfig.spec.json" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /tools/workspace-plugin/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /tools/workspace-plugin/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | --------------------------------------------------------------------------------