├── .nvmrc ├── .yarnrc.yml ├── packages ├── flag-evaluation │ ├── .prettierignore │ ├── tsconfig.build.json │ ├── tsconfig.eslint.json │ ├── eslint.config.js │ ├── tsconfig.json │ ├── jest.config.js │ └── package.json ├── cli │ ├── .prettierignore │ ├── tsconfig.eslint.json │ ├── eslint.config.js │ ├── tsconfig.json │ ├── utils │ │ ├── commander.ts │ │ ├── types.ts │ │ ├── file.ts │ │ ├── constants.ts │ │ ├── urls.ts │ │ ├── schemas.ts │ │ └── version.ts │ ├── vite.config.js │ ├── commands │ │ ├── apps.ts │ │ ├── new.ts │ │ └── auth.ts │ ├── schema.json │ ├── stores │ │ └── auth.ts │ ├── services │ │ └── bootstrap.ts │ └── package.json ├── node-sdk │ ├── .prettierignore │ ├── typedoc.json │ ├── examples │ │ ├── cloudflare-worker │ │ │ ├── .prettierignore │ │ │ ├── .vscode │ │ │ │ └── settings.json │ │ │ ├── vitest.config.mts │ │ │ ├── package.json │ │ │ ├── README.md │ │ │ ├── src │ │ │ │ └── index.ts │ │ │ ├── wrangler.jsonc │ │ │ └── tsconfig.json │ │ └── express │ │ │ ├── bucketConfig.json │ │ │ ├── tsconfig.json │ │ │ ├── serve.ts │ │ │ ├── package.json │ │ │ ├── README.md │ │ │ ├── app.test.ts │ │ │ └── bucket.ts │ ├── tsconfig.build.json │ ├── tsconfig.eslint.json │ ├── docs │ │ ├── type-check-failed.png │ │ └── type-check-payload-failed.png │ ├── eslint.config.js │ ├── vite.config.js │ ├── tsconfig.json │ ├── test │ │ ├── testConfig.json │ │ └── config.test.ts │ ├── src │ │ ├── index.ts │ │ ├── edgeClient.ts │ │ ├── flusher.ts │ │ ├── rate-limiter.ts │ │ └── inRequestCache.ts │ └── package.json ├── react-sdk │ ├── dev │ │ ├── .env │ │ ├── plain │ │ │ ├── vite-env.d.ts │ │ │ ├── tsconfig.json │ │ │ ├── index.tsx │ │ │ └── index.html │ │ ├── nextjs-flag-demo │ │ │ ├── .eslintrc.json │ │ │ ├── next.config.mjs │ │ │ ├── app │ │ │ │ ├── favicon.ico │ │ │ │ ├── globals.css │ │ │ │ └── layout.tsx │ │ │ ├── postcss.config.mjs │ │ │ ├── README.md │ │ │ ├── components │ │ │ │ ├── Flags.tsx │ │ │ │ └── Providers.tsx │ │ │ ├── .gitignore │ │ │ ├── tailwind.config.ts │ │ │ ├── public │ │ │ │ ├── vercel.svg │ │ │ │ └── next.svg │ │ │ ├── tsconfig.json │ │ │ └── package.json │ │ └── nextjs-bootstrap-demo │ │ │ ├── .eslintrc.json │ │ │ ├── next.config.mjs │ │ │ ├── app │ │ │ ├── favicon.ico │ │ │ ├── globals.css │ │ │ ├── client.ts │ │ │ └── layout.tsx │ │ │ ├── postcss.config.mjs │ │ │ ├── README.md │ │ │ ├── components │ │ │ └── Flags.tsx │ │ │ ├── .gitignore │ │ │ ├── tailwind.config.ts │ │ │ ├── public │ │ │ ├── vercel.svg │ │ │ └── next.svg │ │ │ ├── tsconfig.json │ │ │ └── package.json │ ├── .prettierignore │ ├── tsconfig.build.json │ ├── typedoc.json │ ├── tsconfig.eslint.json │ ├── tsconfig.json │ ├── vite.config.mjs │ ├── eslint.config.js │ └── package.json ├── vue-sdk │ ├── .prettierignore │ ├── tsconfig.build.json │ ├── typedoc.json │ ├── src │ │ ├── version.ts │ │ ├── vue.d.ts │ │ ├── ReflagClientProvider.vue │ │ ├── index.ts │ │ ├── ReflagBootstrappedProvider.vue │ │ └── ReflagProvider.vue │ ├── tsconfig.eslint.json │ ├── dev │ │ └── plain │ │ │ ├── env.d.ts │ │ │ ├── index.ts │ │ │ ├── index.html │ │ │ └── components │ │ │ ├── Track.vue │ │ │ ├── Section.vue │ │ │ ├── MissingKeyMessage.vue │ │ │ ├── StartHuddlesButton.vue │ │ │ ├── Events.vue │ │ │ └── RequestFeedback.vue │ ├── tsconfig.json │ ├── vite.config.mjs │ ├── eslint.config.js │ └── package.json ├── openfeature-node-provider │ ├── .prettierignore │ ├── tsconfig.build.json │ ├── tsconfig.eslint.json │ ├── eslint.config.js │ ├── tsconfig.json │ ├── vite.config.js │ ├── example │ │ ├── package.json │ │ ├── tsconfig.json │ │ ├── serve.ts │ │ ├── reflag.ts │ │ └── README.md │ └── package.json ├── browser-sdk │ ├── .prettierignore │ ├── typedoc.json │ ├── tsconfig.build.json │ ├── src │ │ ├── toolbar │ │ │ ├── index.css │ │ │ ├── index.ts │ │ │ ├── Switch.css │ │ │ ├── Switch.tsx │ │ │ └── Flags.css │ │ ├── feedback │ │ │ ├── ui │ │ │ │ ├── RadialProgress.css │ │ │ │ ├── css.d.ts │ │ │ │ ├── index.css │ │ │ │ ├── Plug.tsx │ │ │ │ ├── Button.tsx │ │ │ │ ├── RadialProgress.tsx │ │ │ │ ├── config │ │ │ │ │ └── defaultTranslations.tsx │ │ │ │ ├── StarRating.css │ │ │ │ ├── Button.css │ │ │ │ ├── hooks │ │ │ │ │ └── useTimer.ts │ │ │ │ ├── types.ts │ │ │ │ ├── index.ts │ │ │ │ └── FeedbackDialog.css │ │ │ ├── promptStorage.ts │ │ │ └── prompts.ts │ │ ├── ui │ │ │ ├── packages │ │ │ │ └── floating-ui-preact-dom │ │ │ │ │ ├── README.md │ │ │ │ │ ├── utils │ │ │ │ │ ├── roundByDPR.ts │ │ │ │ │ ├── getDPR.ts │ │ │ │ │ ├── useLatestRef.ts │ │ │ │ │ └── deepEqual.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── arrow.ts │ │ │ ├── icons │ │ │ │ ├── Check.tsx │ │ │ │ ├── CheckCircle.tsx │ │ │ │ ├── Close.tsx │ │ │ │ ├── Logo.tsx │ │ │ │ ├── VerySatisfied.tsx │ │ │ │ ├── Neutral.tsx │ │ │ │ ├── Dissatisfied.tsx │ │ │ │ ├── Satisfied.tsx │ │ │ │ └── VeryDissatisfied.tsx │ │ │ ├── types.ts │ │ │ ├── constants.ts │ │ │ └── utils.ts │ │ ├── config.ts │ │ ├── rateLimiter.ts │ │ ├── logger.ts │ │ ├── index.ts │ │ ├── hooksManager.ts │ │ └── context.ts │ ├── postcss.config.js │ ├── tsconfig.eslint.json │ ├── test │ │ ├── mocks │ │ │ └── server.ts │ │ ├── testLogger.ts │ │ ├── e2e │ │ │ ├── empty.html │ │ │ └── give-feedback-button.html │ │ ├── flagCache.test.ts │ │ └── httpClient.test.ts │ ├── vite.e2e.config.js │ ├── tsconfig.json │ ├── vitest.setup.ts │ ├── example │ │ └── typescript │ │ │ ├── index.html │ │ │ └── app.ts │ ├── playwright.config.ts │ ├── eslint.config.js │ ├── vite.config.mjs │ └── package.json ├── openfeature-browser-provider │ ├── .prettierignore │ ├── example │ │ ├── .eslintrc.json │ │ ├── next.config.mjs │ │ ├── postcss.config.mjs │ │ ├── app │ │ │ ├── page.tsx │ │ │ ├── layout.tsx │ │ │ ├── globals.css │ │ │ └── featureManagement.ts │ │ ├── README.md │ │ ├── components │ │ │ ├── OpenFeatureProvider.tsx │ │ │ ├── HuddleFeature.tsx │ │ │ └── Context.tsx │ │ ├── .gitignore │ │ ├── tailwind.config.ts │ │ ├── tsconfig.json │ │ └── package.json │ ├── tsconfig.build.json │ ├── tsconfig.eslint.json │ ├── eslint.config.js │ ├── tsconfig.json │ ├── vite.config.js │ └── package.json ├── tsconfig │ ├── package.json │ └── library.json └── eslint-config │ └── package.json ├── .prettierignore ├── lerna.json ├── .gitattributes ├── .vscode ├── extensions.json └── settings.json ├── .editorconfig ├── vitest.workspace.js ├── .gitignore ├── package.json ├── LICENSE ├── README.md ├── .github └── workflows │ ├── publish.yml │ └── package-ci.yml └── docs.sh /.nvmrc: -------------------------------------------------------------------------------- 1 | 20.15.1 2 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | -------------------------------------------------------------------------------- /packages/flag-evaluation/.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | coverage 3 | -------------------------------------------------------------------------------- /packages/cli/.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | eslint-report.json 3 | gen 4 | -------------------------------------------------------------------------------- /packages/node-sdk/.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | eslint-report.json 3 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/.env: -------------------------------------------------------------------------------- 1 | PUBLISHABLE_KEY=your_publishable_key 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/build 2 | **/gen 3 | **/node_modules 4 | **/dist 5 | -------------------------------------------------------------------------------- /packages/vue-sdk/.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | eslint-report.json 3 | dev 4 | -------------------------------------------------------------------------------- /packages/react-sdk/.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | eslint-report.json 3 | dev 4 | -------------------------------------------------------------------------------- /packages/cli/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/node-sdk/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tsconfig": "tsconfig.build.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/openfeature-node-provider/.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | eslint-report.json 3 | -------------------------------------------------------------------------------- /packages/browser-sdk/.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | eslint-report.json 3 | test-results 4 | -------------------------------------------------------------------------------- /packages/browser-sdk/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tsconfig": "tsconfig.build.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/plain/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/node-sdk/examples/cloudflare-worker/.prettierignore: -------------------------------------------------------------------------------- 1 | worker-configuration.d.ts 2 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-flag-demo/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /packages/browser-sdk/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/node-sdk/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/openfeature-browser-provider/.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | eslint-report.json 3 | test-results 4 | .next 5 | -------------------------------------------------------------------------------- /packages/openfeature-browser-provider/example/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-bootstrap-demo/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /packages/react-sdk/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/vue-sdk/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/flag-evaluation/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/react-sdk/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tsconfig": "tsconfig.build.json", 3 | "entryPoints": ["src/index.tsx"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/vue-sdk/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tsconfig": "tsconfig.build.json", 3 | "entryPoints": ["src/index.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/openfeature-node-provider/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/node-sdk/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["./src", "./test", "./*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/openfeature-browser-provider/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/toolbar/index.css: -------------------------------------------------------------------------------- 1 | @import url(./Toolbar.css); 2 | @import url(./Flags.css); 3 | @import url(./Switch.css); 4 | -------------------------------------------------------------------------------- /packages/vue-sdk/src/version.ts: -------------------------------------------------------------------------------- 1 | import { version } from "../package.json"; 2 | 3 | export const SDK_VERSION = `vue-sdk/${version}`; 4 | -------------------------------------------------------------------------------- /packages/flag-evaluation/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["./src", "./test", "./*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/react-sdk/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["./src", "./test", "./dev", "./*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/vue-sdk/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["./src", "./test", "./dev", "./*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "node_modules/lerna/schemas/lerna-schema.json", 3 | "version": "independent", 4 | "npmClient": "yarn" 5 | } 6 | -------------------------------------------------------------------------------- /packages/browser-sdk/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [require("postcss-nesting"), require("postcss-preset-env")], 3 | }; 4 | -------------------------------------------------------------------------------- /packages/browser-sdk/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["./src", "./test", "./dev", "./*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/node-sdk/docs/type-check-failed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reflagcom/javascript/HEAD/packages/node-sdk/docs/type-check-failed.png -------------------------------------------------------------------------------- /packages/tsconfig/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@reflag/tsconfig", 3 | "version": "0.0.2", 4 | "license": "MIT", 5 | "private": true 6 | } 7 | -------------------------------------------------------------------------------- /packages/cli/eslint.config.js: -------------------------------------------------------------------------------- 1 | import base from "@reflag/eslint-config/base.js"; 2 | 3 | export default [...base, { ignores: ["dist/", "gen/"] }]; 4 | -------------------------------------------------------------------------------- /packages/flag-evaluation/eslint.config.js: -------------------------------------------------------------------------------- 1 | const base = require("@reflag/eslint-config"); 2 | 3 | module.exports = [...base, { ignores: ["dist/"] }]; 4 | -------------------------------------------------------------------------------- /packages/node-sdk/examples/cloudflare-worker/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "wrangler.json": "jsonc" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/node-sdk/examples/express/bucketConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": { 3 | "show-todos": true, 4 | "create-todos": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/openfeature-node-provider/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["./src", "./test", "./*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/node-sdk/docs/type-check-payload-failed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reflagcom/javascript/HEAD/packages/node-sdk/docs/type-check-payload-failed.png -------------------------------------------------------------------------------- /packages/node-sdk/eslint.config.js: -------------------------------------------------------------------------------- 1 | const base = require("@reflag/eslint-config"); 2 | 3 | module.exports = [...base, { ignores: ["dist/", "examples/"] }]; 4 | -------------------------------------------------------------------------------- /packages/openfeature-browser-provider/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["./src", "./test", "./dev", "./*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-flag-demo/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-bootstrap-demo/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-flag-demo/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reflagcom/javascript/HEAD/packages/react-sdk/dev/nextjs-flag-demo/app/favicon.ico -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /.yarn/** linguist-vendored 2 | /.yarn/releases/* binary 3 | /.yarn/plugins/**/* binary 4 | /.pnp.* binary linguist-generated 5 | -------------------------------------------------------------------------------- /packages/openfeature-browser-provider/example/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /packages/openfeature-browser-provider/eslint.config.js: -------------------------------------------------------------------------------- 1 | const base = require("@reflag/eslint-config"); 2 | 3 | module.exports = [...base, { ignores: ["dist/", "example/"] }]; 4 | -------------------------------------------------------------------------------- /packages/openfeature-node-provider/eslint.config.js: -------------------------------------------------------------------------------- 1 | const base = require("@reflag/eslint-config"); 2 | 3 | module.exports = [...base, { ignores: ["dist/", "example/"] }]; 4 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-bootstrap-demo/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reflagcom/javascript/HEAD/packages/react-sdk/dev/nextjs-bootstrap-demo/app/favicon.ico -------------------------------------------------------------------------------- /packages/react-sdk/dev/plain/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "module": "ESNext" 5 | }, 6 | "include": ["."] 7 | } 8 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/feedback/ui/RadialProgress.css: -------------------------------------------------------------------------------- 1 | .radial-progress { 2 | > circle { 3 | stroke: var(--reflag-feedback-dialog-color, #1e1f24); 4 | opacity: 0.2; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/browser-sdk/test/mocks/server.ts: -------------------------------------------------------------------------------- 1 | import { setupServer } from "msw/node"; 2 | 3 | import { handlers } from "./handlers"; 4 | 5 | export const server = setupServer(...handlers); 6 | -------------------------------------------------------------------------------- /packages/browser-sdk/vite.e2e.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | include: ["test/e2e/**/*.test.?(c|m)[jt]s?(x)"], 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "esbenp.prettier-vscode", 5 | "editorconfig.editorconfig", 6 | "rohit-gohri.format-code-action" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/feedback/ui/css.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.css" { 2 | const src: string; 3 | export default src; 4 | } 5 | declare module "*?inline" { 6 | const src: string; 7 | export default src; 8 | } 9 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/feedback/ui/index.css: -------------------------------------------------------------------------------- 1 | @import url(./Button.css); 2 | @import url(./FeedbackDialog.css); 3 | @import url(./FeedbackForm.css); 4 | @import url(./RadialProgress.css); 5 | @import url(./StarRating.css); 6 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-flag-demo/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /packages/browser-sdk/test/testLogger.ts: -------------------------------------------------------------------------------- 1 | import { vi } from "vitest"; 2 | 3 | export const testLogger = { 4 | log: vi.fn(), 5 | info: vi.fn(), 6 | warn: vi.fn(), 7 | error: vi.fn(), 8 | debug: vi.fn(), 9 | }; 10 | -------------------------------------------------------------------------------- /packages/openfeature-browser-provider/example/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-bootstrap-demo/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/ui/packages/floating-ui-preact-dom/README.md: -------------------------------------------------------------------------------- 1 | # @floating-ui-preact-dom 2 | 3 | This contains a modified version of https://github.com/floating-ui/floating-ui/tree/master/packages/react-dom to support Preact. 4 | -------------------------------------------------------------------------------- /packages/vue-sdk/dev/plain/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | interface ImportMetaEnv { 4 | readonly VITE_PUBLISHABLE_KEY: string; 5 | } 6 | 7 | interface ImportMeta { 8 | readonly env: ImportMetaEnv; 9 | } 10 | -------------------------------------------------------------------------------- /packages/vue-sdk/dev/plain/index.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue"; 2 | 3 | import App from "./App.vue"; 4 | 5 | const el = document.getElementById("app"); 6 | 7 | if (el) { 8 | const app = createApp(App); 9 | app.mount(el); 10 | } 11 | -------------------------------------------------------------------------------- /packages/flag-evaluation/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@reflag/tsconfig/library", 3 | "compilerOptions": { 4 | "lib": [], 5 | "outDir": "./dist/" 6 | }, 7 | "include": ["src"], 8 | "typeRoots": ["./node_modules/@types", "./types"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/ui/packages/floating-ui-preact-dom/utils/roundByDPR.ts: -------------------------------------------------------------------------------- 1 | import { getDPR } from "./getDPR"; 2 | 3 | export function roundByDPR(element: Element, value: number) { 4 | const dpr = getDPR(element); 5 | return Math.round(value * dpr) / dpr; 6 | } 7 | -------------------------------------------------------------------------------- /packages/flag-evaluation/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ 2 | module.exports = { 3 | preset: "ts-jest", 4 | testPathIgnorePatterns: ["/dist/", "/node_modules/"], 5 | clearMocks: true, 6 | maxWorkers: 1, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/node-sdk/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import { defaultExclude } from "vitest/config"; 3 | 4 | export default defineConfig({ 5 | test: { 6 | environment: "node", 7 | exclude: [...defaultExclude, "**/examples/**"], 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /packages/vue-sdk/src/vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.vue" { 2 | import type { DefineComponent } from "vue"; 3 | 4 | const component: DefineComponent< 5 | Record, 6 | Record, 7 | unknown 8 | >; 9 | export default component; 10 | } 11 | -------------------------------------------------------------------------------- /packages/browser-sdk/test/e2e/empty.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Reflag Test 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/ui/packages/floating-ui-preact-dom/utils/getDPR.ts: -------------------------------------------------------------------------------- 1 | export function getDPR(element: Element): number { 2 | if (typeof window === "undefined") { 3 | return 1; 4 | } 5 | const win = element.ownerDocument.defaultView || window; 6 | return win.devicePixelRatio || 1; 7 | } 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | 9 | [*.{js,ts,jsx,tsx,html,css,json,yml,md}] 10 | charset = utf-8 11 | indent_style = space 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/ui/packages/floating-ui-preact-dom/utils/useLatestRef.ts: -------------------------------------------------------------------------------- 1 | import { useLayoutEffect, useRef } from "preact/hooks"; 2 | 3 | export function useLatestRef(value: T) { 4 | const ref = useRef(value); 5 | useLayoutEffect(() => { 6 | ref.current = value; 7 | }); 8 | return ref; 9 | } 10 | -------------------------------------------------------------------------------- /packages/openfeature-node-provider/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@reflag/tsconfig/library", 3 | "compilerOptions": { 4 | "outDir": "./dist/", 5 | "declarationDir": "./dist/types", 6 | "skipLibCheck": true 7 | }, 8 | "include": ["src"], 9 | "typeRoots": ["./node_modules/@types", "./types"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@reflag/tsconfig/library", 3 | "compilerOptions": { 4 | "outDir": "./dist/", 5 | "target": "ESNext", 6 | "module": "NodeNext", 7 | "moduleResolution": "NodeNext" 8 | }, 9 | "include": ["**/*.ts"], 10 | "exclude": ["node_modules", "dist", "**/*.spec.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/vue-sdk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@reflag/tsconfig/library", 3 | "compilerOptions": { 4 | "lib": [], 5 | "outDir": "./dist/", 6 | "declarationDir": "./dist/types", 7 | "declarationMap": true 8 | }, 9 | "include": ["src", "src/**/*.d.ts"], 10 | "typeRoots": ["./node_modules/@types"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/node-sdk/examples/cloudflare-worker/vitest.config.mts: -------------------------------------------------------------------------------- 1 | import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config"; 2 | 3 | export default defineWorkersConfig({ 4 | test: { 5 | poolOptions: { 6 | workers: { 7 | wrangler: { configPath: "./wrangler.jsonc" }, 8 | }, 9 | }, 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /packages/react-sdk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@reflag/tsconfig/library", 3 | "compilerOptions": { 4 | "lib": [], 5 | "outDir": "./dist/", 6 | "declarationDir": "./dist/types", 7 | "jsx": "react", 8 | "declarationMap": true 9 | }, 10 | "include": ["src", "dev"], 11 | "typeRoots": ["./node_modules/@types"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/cli/utils/commander.ts: -------------------------------------------------------------------------------- 1 | import { Command } from "commander"; 2 | 3 | export function commandName(command: Command) { 4 | if (command.parent) { 5 | const parentName = command.parent.name(); 6 | if (parentName && parentName !== "index") { 7 | return `${parentName} ${command.name()}`; 8 | } 9 | } 10 | return command.name(); 11 | } 12 | -------------------------------------------------------------------------------- /packages/node-sdk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@reflag/tsconfig/library", 3 | "compilerOptions": { 4 | "lib": [], 5 | "outDir": "./dist/", 6 | "declarationDir": "./dist/types", 7 | "declarationMap": true, 8 | "noUncheckedIndexedAccess": true 9 | }, 10 | "include": ["src"], 11 | "typeRoots": ["./node_modules/@types", "./types"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/plain/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | 4 | import { App } from "./app"; 5 | 6 | const el = document.getElementById("app"); 7 | 8 | if (el) { 9 | const root = ReactDOM.createRoot(el); 10 | root.render( 11 | 12 | 13 | , 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-flag-demo/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | The purpose of this project is to demonstrate usage integration with the Reflag React SDK. 4 | 5 | ## Getting Started 6 | 7 | Run the development server: 8 | 9 | ```bash 10 | yarn dev 11 | ``` 12 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-bootstrap-demo/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | The purpose of this project is to demonstrate usage integration with the Reflag React SDK. 4 | 5 | ## Getting Started 6 | 7 | Run the development server: 8 | 9 | ```bash 10 | yarn dev 11 | ``` 12 | -------------------------------------------------------------------------------- /packages/openfeature-node-provider/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | environment: "node", 6 | exclude: [ 7 | "**/node_modules/**", 8 | "**/dist/**", 9 | "**/.{idea,git,cache,output,temp}/**", 10 | "**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build}.config.*", 11 | ], 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /packages/tsconfig/library.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "ES6", 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "resolveJsonModule": true, 8 | "esModuleInterop": true, 9 | "sourceMap": true, 10 | "declaration": true, 11 | "noUnusedLocals": true, 12 | "noUnusedParameters": true, 13 | "noImplicitReturns": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/ui/packages/floating-ui-preact-dom/index.ts: -------------------------------------------------------------------------------- 1 | export { arrow } from "./arrow"; 2 | export { useFloating } from "./useFloating"; 3 | export { 4 | autoPlacement, 5 | autoUpdate, 6 | computePosition, 7 | detectOverflow, 8 | flip, 9 | getOverflowAncestors, 10 | hide, 11 | inline, 12 | limitShift, 13 | offset, 14 | platform, 15 | shift, 16 | size, 17 | } from "@floating-ui/dom"; 18 | -------------------------------------------------------------------------------- /packages/cli/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | environment: "node", 6 | exclude: [ 7 | "**/node_modules/**", 8 | "**/dist/**", 9 | "**/.{idea,git,cache,output,temp}/**", 10 | "**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build}.config.*", 11 | "**/example/**", 12 | ], 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /packages/openfeature-browser-provider/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@reflag/tsconfig/library", 3 | "compilerOptions": { 4 | "outDir": "./dist/", 5 | "jsx": "react", 6 | "jsxFactory": "h", 7 | "jsxFragmentFactory": "Fragment", 8 | "declarationDir": "./dist/types", 9 | "skipLibCheck": true 10 | }, 11 | "include": ["src", "dev"], 12 | "typeRoots": ["./node_modules/@types", "./types"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/vue-sdk/dev/plain/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Reflag Vue SDK playground 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /packages/browser-sdk/test/e2e/give-feedback-button.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Reflag Test 7 | 8 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/plain/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Reflag React SDK playground 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/feedback/ui/Plug.tsx: -------------------------------------------------------------------------------- 1 | import { FunctionComponent, h } from "preact"; 2 | 3 | import { Logo } from "../../ui/icons/Logo"; 4 | 5 | export const Plug: FunctionComponent = () => { 6 | return ( 7 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /packages/openfeature-browser-provider/example/app/page.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import { HuddleFeature } from "@/components/HuddleFeature"; 3 | import { Context } from "@/components/Context"; 4 | 5 | export default function Home() { 6 | return ( 7 |
8 | 9 | 10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /packages/openfeature-node-provider/example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "packageManager": "yarn@4.1.1", 4 | "scripts": { 5 | "start": "tsx serve.ts" 6 | }, 7 | "type": "commonjs", 8 | "main": "serve.ts", 9 | "dependencies": { 10 | "express": "^4.20.0", 11 | "tsx": "^4.16.2", 12 | "typescript": "^5.5.3" 13 | }, 14 | "devDependencies": { 15 | "@types/express": "^4.17.21" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/node-sdk/test/testConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "flagOverrides": { 3 | "myFlag": { "isEnabled": true }, 4 | "myFlagFalse": false, 5 | "myFlagWithConfig": { 6 | "isEnabled": true, 7 | "config": { 8 | "key": "config-1", 9 | "payload": { "something": "else" } 10 | } 11 | } 12 | }, 13 | "secretKey": "mySecretKey", 14 | "offline": true, 15 | "apiBaseUrl": "http://localhost:3000" 16 | } 17 | -------------------------------------------------------------------------------- /packages/vue-sdk/dev/plain/components/Track.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | -------------------------------------------------------------------------------- /vitest.workspace.js: -------------------------------------------------------------------------------- 1 | import { defineWorkspace } from "vitest/config"; 2 | 3 | export default defineWorkspace([ 4 | "./packages/openfeature-browser-provider/vite.config.js", 5 | "./packages/browser-sdk/vitest.config.ts", 6 | "./packages/openfeature-node-provider/vite.config.js", 7 | "./packages/node-sdk/vite.config.js", 8 | "./packages/react-sdk/vite.config.mjs", 9 | "./packages/vue-sdk/vite.config.mjs", 10 | "./packages/cli/vite.config.js", 11 | ]); 12 | -------------------------------------------------------------------------------- /packages/browser-sdk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@reflag/tsconfig/library", 3 | "compilerOptions": { 4 | "lib": [], 5 | "outDir": "./dist/", 6 | "jsx": "react", 7 | "jsxFactory": "h", 8 | "jsxFragmentFactory": "Fragment", 9 | "declarationDir": "./dist/types", 10 | "skipLibCheck": true, 11 | "declarationMap": true 12 | }, 13 | "include": ["src", "test", "dev"], 14 | "typeRoots": ["./node_modules/@types", "./types"] 15 | } 16 | -------------------------------------------------------------------------------- /packages/browser-sdk/vitest.setup.ts: -------------------------------------------------------------------------------- 1 | import { afterAll, afterEach, beforeAll } from "vitest"; 2 | 3 | import { server } from "./test/mocks/server.js"; 4 | 5 | beforeAll(() => { 6 | server.listen({ 7 | onUnhandledRequest(request) { 8 | console.error("Unhandled %s %s", request.method, request.url); 9 | }, 10 | }); 11 | }); 12 | 13 | afterEach(() => { 14 | server.resetHandlers(); 15 | }); 16 | 17 | afterAll(() => { 18 | server.close(); 19 | }); 20 | -------------------------------------------------------------------------------- /packages/node-sdk/examples/express/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "module": "CommonJS", 5 | "moduleResolution": "Node", 6 | "allowImportingTsExtensions": true, 7 | "allowArbitraryExtensions": true, 8 | "noEmit": true, 9 | "verbatimModuleSyntax": false, 10 | "esModuleInterop": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "strict": true, 13 | "skipLibCheck": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/openfeature-browser-provider/example/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | The purpose of this project is to demonstrate usage integration with the Reflag OpenFeature React SDK. 4 | 5 | ## Getting Started 6 | 7 | Run the development server: 8 | 9 | ```bash 10 | NEXT_PUBLIC_REFLAG_PUBLISHABLE_KEY= yarn dev 11 | ``` 12 | -------------------------------------------------------------------------------- /packages/openfeature-node-provider/example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "module": "CommonJS", 5 | "moduleResolution": "Node", 6 | "allowImportingTsExtensions": true, 7 | "customConditions": ["module"], 8 | "allowArbitraryExtensions": true, 9 | "noEmit": true, 10 | "verbatimModuleSyntax": false, 11 | "esModuleInterop": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "strict": true, 14 | "skipLibCheck": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/openfeature-node-provider/example/serve.ts: -------------------------------------------------------------------------------- 1 | import reflag from "./reflag"; 2 | import app from "./app"; 3 | 4 | // Initialize Reflag SDK before starting the server, 5 | // so that features are available when the server starts. 6 | reflag.initialize().then(() => { 7 | // Start listening for requests only after Reflag is initialized, 8 | // which guarantees that features are available. 9 | app.listen(process.env.PORT ?? 3000, () => { 10 | console.log("Server is running on port 3000"); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /packages/vue-sdk/dev/plain/components/Section.vue: -------------------------------------------------------------------------------- 1 | 6 | 23 | -------------------------------------------------------------------------------- /packages/node-sdk/examples/cloudflare-worker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloudflare-worker", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "deploy": "wrangler deploy", 7 | "dev": "wrangler dev", 8 | "start": "wrangler dev", 9 | "test": "vitest", 10 | "cf-typegen": "wrangler types" 11 | }, 12 | "devDependencies": { 13 | "@cloudflare/vitest-pool-workers": "^0.8.19", 14 | "typescript": "^5.5.2", 15 | "vitest": "~3.2.0", 16 | "wrangler": "^4.20.5" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/openfeature-browser-provider/example/components/OpenFeatureProvider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { initOpenFeature } from "@/app/featureManagement"; 4 | import { OpenFeatureProvider as OFProvider } from "@openfeature/react-sdk"; 5 | import { useEffect } from "react"; 6 | 7 | export const OpenFeatureProvider = ({ 8 | children, 9 | }: { 10 | children: React.ReactNode; 11 | }) => { 12 | useEffect(() => { 13 | initOpenFeature(); 14 | }, []); 15 | 16 | return {children}; 17 | }; 18 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/feedback/ui/Button.tsx: -------------------------------------------------------------------------------- 1 | import { FunctionComponent, h } from "preact"; 2 | 3 | export type ButtonProps = h.JSX.HTMLAttributes & { 4 | variant?: "primary" | "secondary"; 5 | }; 6 | 7 | export const Button: FunctionComponent = ({ 8 | variant = "primary", 9 | children, 10 | ...rest 11 | }) => { 12 | const classes = ["button", variant].join(" "); 13 | 14 | return ( 15 | 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/ui/icons/Check.tsx: -------------------------------------------------------------------------------- 1 | import { FunctionComponent, h } from "preact"; 2 | 3 | export const Check: FunctionComponent> = ( 4 | props, 5 | ) => ( 6 | 13 | 17 | 18 | ); 19 | -------------------------------------------------------------------------------- /packages/node-sdk/examples/express/serve.ts: -------------------------------------------------------------------------------- 1 | import "dotenv/config"; 2 | 3 | import reflag from "./reflag"; 4 | import app from "./app"; 5 | 6 | // Initialize Reflag SDK before starting the server, 7 | // so that features are available when the server starts. 8 | reflag.initialize().then(() => { 9 | // Start listening for requests only after Reflag is initialized, 10 | // which guarantees that features are available. 11 | app.listen(process.env.PORT ?? 3000, () => { 12 | console.log("Server is running on port 3000"); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /packages/node-sdk/examples/express/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "packageManager": "yarn@4.10.3", 4 | "scripts": { 5 | "start": "tsx serve.ts", 6 | "test": "vitest" 7 | }, 8 | "type": "commonjs", 9 | "main": "serve.ts", 10 | "dependencies": { 11 | "express": "^4.20.0", 12 | "tsx": "^4.16.2", 13 | "typescript": "^5.5.3", 14 | "wrangler": "^4.20.5" 15 | }, 16 | "devDependencies": { 17 | "@types/express": "^4.17.21", 18 | "@types/supertest": "^6.0.2", 19 | "supertest": "^7.0.0", 20 | "vitest": "^2.1.9" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-flag-demo/components/Flags.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | import { useFlag } from "@reflag/react-sdk"; 5 | 6 | export const Flags = () => { 7 | const { isEnabled } = useFlag("huddles"); 8 | return ( 9 |
10 |

Huddles feature enabled:

11 |
12 |         {JSON.stringify(isEnabled)}
13 |       
14 |
15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-bootstrap-demo/components/Flags.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | import { useFlag } from "@reflag/react-sdk"; 5 | 6 | export const Flags = () => { 7 | const { isEnabled } = useFlag("huddles"); 8 | return ( 9 |
10 |

Huddles feature enabled:

11 |
12 |         {JSON.stringify(isEnabled)}
13 |       
14 |
15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-flag-demo/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/config.ts: -------------------------------------------------------------------------------- 1 | import { version } from "../package.json"; 2 | 3 | export const API_BASE_URL = "https://front.reflag.com"; 4 | export const APP_BASE_URL = "https://app.reflag.com"; 5 | export const SSE_REALTIME_BASE_URL = "https://livemessaging.bucket.co"; 6 | 7 | export const SDK_VERSION_HEADER_NAME = "reflag-sdk-version"; 8 | 9 | export const SDK_VERSION = `browser-sdk/${version}`; 10 | export const FLAG_EVENTS_PER_MIN = 1; 11 | export const FLAGS_EXPIRE_MS = 30 * 24 * 60 * 60 * 1000; // expire entirely after 30 days 12 | 13 | export const IS_SERVER = typeof window === "undefined"; 14 | -------------------------------------------------------------------------------- /packages/node-sdk/examples/cloudflare-worker/README.md: -------------------------------------------------------------------------------- 1 | # cloudflare-worker 2 | 3 | This is a simple example of how to use the Reflag SDK in a Cloudflare Worker. 4 | It demonstrates how to initialize the client and evaluate flags. 5 | It also shows how to flush the client and wait for any in-flight requests to complete. 6 | 7 | - Set the REFLAG_SECRET_KEY environment variable in wrangler.jsonc to get started. 8 | - Run `yarn dev` in your terminal to start a development server 9 | - Open a browser tab at http://localhost:8787/ to see your worker in action 10 | - Run `yarn run deploy` to publish your worker 11 | -------------------------------------------------------------------------------- /packages/openfeature-browser-provider/example/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-bootstrap-demo/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-flag-demo/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | const config: Config = { 4 | content: [ 5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./components/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./app/**/*.{js,ts,jsx,tsx,mdx}", 8 | ], 9 | theme: { 10 | extend: { 11 | backgroundImage: { 12 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", 13 | "gradient-conic": 14 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", 15 | }, 16 | }, 17 | }, 18 | plugins: [], 19 | }; 20 | export default config; 21 | -------------------------------------------------------------------------------- /packages/openfeature-browser-provider/example/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | const config: Config = { 4 | content: [ 5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./components/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./app/**/*.{js,ts,jsx,tsx,mdx}", 8 | ], 9 | theme: { 10 | extend: { 11 | backgroundImage: { 12 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", 13 | "gradient-conic": 14 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", 15 | }, 16 | }, 17 | }, 18 | plugins: [], 19 | }; 20 | export default config; 21 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-bootstrap-demo/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | const config: Config = { 4 | content: [ 5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./components/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./app/**/*.{js,ts,jsx,tsx,mdx}", 8 | ], 9 | theme: { 10 | extend: { 11 | backgroundImage: { 12 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", 13 | "gradient-conic": 14 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", 15 | }, 16 | }, 17 | }, 18 | plugins: [], 19 | }; 20 | export default config; 21 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/ui/icons/CheckCircle.tsx: -------------------------------------------------------------------------------- 1 | import { FunctionComponent, h } from "preact"; 2 | 3 | export const CheckCircle: FunctionComponent< 4 | h.JSX.SVGAttributes 5 | > = (props) => ( 6 | 13 | 17 | 18 | ); 19 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-flag-demo/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/ui/icons/Close.tsx: -------------------------------------------------------------------------------- 1 | import { FunctionComponent, h } from "preact"; 2 | 3 | export const Close: FunctionComponent> = ( 4 | props, 5 | ) => ( 6 | 14 | 18 | 19 | ); 20 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-bootstrap-demo/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/cli/utils/types.ts: -------------------------------------------------------------------------------- 1 | export const FunnelStepList = [ 2 | "company", 3 | "segment", 4 | "tried", 5 | "adopted", 6 | "retained", 7 | ] as const; 8 | 9 | export type FunnelStep = (typeof FunnelStepList)[number]; 10 | 11 | export type FeedbackSource = "api" | "manual" | "prompt" | "sdk" | "widget"; 12 | 13 | export type SatisfactionScore = 0 | 1 | 2 | 3 | 4 | 5; 14 | 15 | export type ParamType = string | number | boolean | Date; 16 | 17 | export type PaginatedResponse = { 18 | data: T[]; 19 | metadata: Record; 20 | totalCount: number; 21 | pageIndex: number; 22 | pageSize: number; 23 | sortBy: string; 24 | sortOrder: string; 25 | }; 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | coverage 10 | 11 | node_modules 12 | gen 13 | dist 14 | dist-ssr 15 | *.local 16 | 17 | # Editor directories and files 18 | .vscode/* 19 | !.vscode/extensions.json 20 | !.vscode/settings.json 21 | .idea 22 | .DS_Store 23 | *.suo 24 | *.ntvs* 25 | *.njsproj 26 | *.sln 27 | *.sw? 28 | test-results/ 29 | playwright-report/ 30 | playwright/.cache/ 31 | 32 | .pnp.* 33 | .yarn 34 | !.yarn/patches 35 | !.yarn/plugins 36 | !.yarn/releases 37 | !.yarn/sdks 38 | !.yarn/versions 39 | 40 | junit.xml 41 | 42 | .next 43 | eslint-report.json 44 | reflag.config.json 45 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-flag-demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-bootstrap-demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/ui/icons/Logo.tsx: -------------------------------------------------------------------------------- 1 | import { FunctionComponent, h } from "preact"; 2 | 3 | export const Logo: FunctionComponent> = ( 4 | props = { height: "10px", width: "10px" }, 5 | ) => ( 6 | 14 | 18 | 19 | ); 20 | -------------------------------------------------------------------------------- /packages/openfeature-browser-provider/example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-flag-demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-flag-demo", 3 | "version": "0.2.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@reflag/react-sdk": "workspace:^", 13 | "next": "14.2.21", 14 | "react": "^18", 15 | "react-dom": "^18" 16 | }, 17 | "devDependencies": { 18 | "@types/node": "^22.12.0", 19 | "@types/react": "^18", 20 | "@types/react-dom": "^18", 21 | "eslint": "^8", 22 | "eslint-config-next": "14.2.5", 23 | "postcss": "^8", 24 | "tailwindcss": "^3.4.1", 25 | "typescript": "^5.7.3" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/browser-sdk/example/typescript/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Reflag flag management 7 | 8 | 9 | Loading... 10 | 16 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /packages/openfeature-browser-provider/example/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.css"; 4 | import { OpenFeatureProvider } from "@/components/OpenFeatureProvider"; 5 | 6 | const inter = Inter({ subsets: ["latin"] }); 7 | 8 | export const metadata: Metadata = { 9 | title: "Create Next App", 10 | description: "Generated by create next app", 11 | }; 12 | 13 | export default function RootLayout({ 14 | children, 15 | }: Readonly<{ 16 | children: React.ReactNode; 17 | }>) { 18 | return ( 19 | 20 | 21 | {children} 22 | 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-bootstrap-demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-bootstrap-demo", 3 | "version": "0.2.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@reflag/node-sdk": "workspace:^", 13 | "@reflag/react-sdk": "workspace:^", 14 | "next": "14.2.21", 15 | "react": "^18", 16 | "react-dom": "^18" 17 | }, 18 | "devDependencies": { 19 | "@types/node": "^22.12.0", 20 | "@types/react": "^18", 21 | "@types/react-dom": "^18", 22 | "eslint": "^8", 23 | "eslint-config-next": "14.2.5", 24 | "postcss": "^8", 25 | "tailwindcss": "^3.4.1", 26 | "typescript": "^5.7.3" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/vue-sdk/src/ReflagClientProvider.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 31 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-flag-demo/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --foreground-rgb: 0, 0, 0; 7 | --background-start-rgb: 214, 219, 220; 8 | --background-end-rgb: 255, 255, 255; 9 | } 10 | 11 | @media (prefers-color-scheme: dark) { 12 | :root { 13 | --foreground-rgb: 255, 255, 255; 14 | --background-start-rgb: 0, 0, 0; 15 | --background-end-rgb: 0, 0, 0; 16 | } 17 | } 18 | 19 | body { 20 | color: rgb(var(--foreground-rgb)); 21 | background: linear-gradient( 22 | to bottom, 23 | transparent, 24 | rgb(var(--background-end-rgb)) 25 | ) 26 | rgb(var(--background-start-rgb)); 27 | } 28 | 29 | @layer utilities { 30 | .text-balance { 31 | text-wrap: balance; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/openfeature-browser-provider/example/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --foreground-rgb: 0, 0, 0; 7 | --background-start-rgb: 214, 219, 220; 8 | --background-end-rgb: 255, 255, 255; 9 | } 10 | 11 | @media (prefers-color-scheme: dark) { 12 | :root { 13 | --foreground-rgb: 255, 255, 255; 14 | --background-start-rgb: 0, 0, 0; 15 | --background-end-rgb: 0, 0, 0; 16 | } 17 | } 18 | 19 | body { 20 | color: rgb(var(--foreground-rgb)); 21 | background: linear-gradient( 22 | to bottom, 23 | transparent, 24 | rgb(var(--background-end-rgb)) 25 | ) 26 | rgb(var(--background-start-rgb)); 27 | } 28 | 29 | @layer utilities { 30 | .text-balance { 31 | text-wrap: balance; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-bootstrap-demo/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --foreground-rgb: 0, 0, 0; 7 | --background-start-rgb: 214, 219, 220; 8 | --background-end-rgb: 255, 255, 255; 9 | } 10 | 11 | @media (prefers-color-scheme: dark) { 12 | :root { 13 | --foreground-rgb: 255, 255, 255; 14 | --background-start-rgb: 0, 0, 0; 15 | --background-end-rgb: 0, 0, 0; 16 | } 17 | } 18 | 19 | body { 20 | color: rgb(var(--foreground-rgb)); 21 | background: linear-gradient( 22 | to bottom, 23 | transparent, 24 | rgb(var(--background-end-rgb)) 25 | ) 26 | rgb(var(--background-start-rgb)); 27 | } 28 | 29 | @layer utilities { 30 | .text-balance { 31 | text-wrap: balance; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/eslint-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@reflag/eslint-config", 3 | "version": "0.0.2", 4 | "license": "MIT", 5 | "private": true, 6 | "main": "./base.js", 7 | "dependencies": { 8 | "@eslint/js": "^9.21.0", 9 | "@typescript-eslint/eslint-plugin": "^8.25.0", 10 | "@typescript-eslint/parser": "^8.25.0", 11 | "eslint": "^9.21.0", 12 | "eslint-config-prettier": "^10.0.1", 13 | "eslint-import-resolver-typescript": "^3.8.3", 14 | "eslint-plugin-import": "^2.31.0", 15 | "eslint-plugin-react": "^7.37.4", 16 | "eslint-plugin-react-hooks": "^5.1.0", 17 | "eslint-plugin-simple-import-sort": "^12.1.1", 18 | "eslint-plugin-unused-imports": "^4.1.4", 19 | "globals": "^16.2.0", 20 | "prettier": "^3.5.2", 21 | "typescript": "^5.7.3" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/node-sdk/src/index.ts: -------------------------------------------------------------------------------- 1 | export { BoundReflagClient, ReflagClient } from "./client"; 2 | export { EdgeClient, EdgeClientOptions } from "./edgeClient"; 3 | export type { 4 | Attributes, 5 | BatchBufferOptions, 6 | BootstrappedFlags, 7 | CacheStrategy, 8 | ClientOptions, 9 | Context, 10 | ContextWithTracking, 11 | EmptyFlagRemoteConfig, 12 | Flag, 13 | FlagConfigVariant, 14 | FlagDefinition, 15 | FlagOverride, 16 | FlagOverrides, 17 | FlagOverridesFn, 18 | FlagRemoteConfig, 19 | Flags, 20 | FlagType, 21 | HttpClient, 22 | HttpClientResponse, 23 | IdType, 24 | LOG_LEVELS, 25 | Logger, 26 | LogLevel, 27 | RawFlag, 28 | RawFlagRemoteConfig, 29 | RawFlags, 30 | TrackingMeta, 31 | TrackOptions, 32 | TypedFlagKey, 33 | TypedFlags, 34 | } from "./types"; 35 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-flag-demo/components/Providers.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React, { ReactNode } from "react"; 4 | import { ReflagProvider } from "@reflag/react-sdk"; 5 | 6 | type Props = { 7 | publishableKey: string; 8 | children: ReactNode; 9 | }; 10 | 11 | export const Providers = ({ publishableKey, children }: Props) => { 12 | return ( 13 | 25 | {children} 26 | 27 | ); 28 | }; 29 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-bootstrap-demo/app/client.ts: -------------------------------------------------------------------------------- 1 | import { ReflagClient as ReflagNodeClient } from "@reflag/node-sdk"; 2 | 3 | const secretKey = process.env.REFLAG_SECRET_KEY || ""; 4 | const offline = process.env.CI === "true"; 5 | 6 | declare global { 7 | var serverClient: ReflagNodeClient; 8 | } 9 | 10 | /** 11 | * Create a singleton server client and store it in globalThis. 12 | * This avoids creating multiple instances of the client in each loaded chunk. 13 | * @returns The server client. 14 | */ 15 | export async function getServerClient() { 16 | if (!globalThis.serverClient) { 17 | globalThis.serverClient = new ReflagNodeClient({ 18 | secretKey, 19 | offline, 20 | }); 21 | } 22 | await globalThis.serverClient.initialize(); 23 | return globalThis.serverClient; 24 | } 25 | -------------------------------------------------------------------------------- /packages/react-sdk/dev/nextjs-flag-demo/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.css"; 4 | import { Providers } from "@/components/Providers"; 5 | 6 | const inter = Inter({ subsets: ["latin"] }); 7 | 8 | export const metadata: Metadata = { 9 | title: "Create Next App", 10 | description: "Generated by create next app", 11 | }; 12 | 13 | const publishableKey = process.env.PUBLISHABLE_KEY || ""; 14 | 15 | export default function RootLayout({ 16 | children, 17 | }: Readonly<{ 18 | children: React.ReactNode; 19 | }>) { 20 | return ( 21 | 22 | 23 | {children} 24 | 25 | 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/toolbar/index.ts: -------------------------------------------------------------------------------- 1 | import { h, render } from "preact"; 2 | 3 | import { ReflagClient } from "../client"; 4 | import { toolbarContainerId } from "../ui/constants"; 5 | import { ToolbarPosition } from "../ui/types"; 6 | import { attachContainer } from "../ui/utils"; 7 | 8 | import Toolbar from "./Toolbar"; 9 | 10 | type showToolbarToggleOptions = { 11 | reflagClient: ReflagClient; 12 | position?: ToolbarPosition; 13 | }; 14 | 15 | export const DEFAULT_PLACEMENT = "bottom-right" as const; 16 | 17 | export function showToolbarToggle(options: showToolbarToggleOptions) { 18 | const shadowRoot = attachContainer(toolbarContainerId); 19 | const position: ToolbarPosition = options.position ?? { 20 | placement: DEFAULT_PLACEMENT, 21 | }; 22 | 23 | render(h(Toolbar, { ...options, position }), shadowRoot); 24 | } 25 | -------------------------------------------------------------------------------- /packages/openfeature-node-provider/example/reflag.ts: -------------------------------------------------------------------------------- 1 | import { OpenFeature } from "@openfeature/server-sdk"; 2 | import { ReflagNodeProvider } from "../src"; 3 | 4 | if (!process.env.REFLAG_SECRET_KEY) { 5 | throw new Error("REFLAG_SECRET_KEY is required"); 6 | } 7 | 8 | export type CreateTodosConfig = { 9 | maxLength: number; 10 | }; 11 | 12 | const provider = new ReflagNodeProvider({ 13 | secretKey: process.env.REFLAG_SECRET_KEY!, 14 | fallbackFlags: { 15 | "show-todos": { 16 | isEnabled: true, 17 | }, 18 | "create-todos": { 19 | isEnabled: true, 20 | config: { 21 | key: "default", 22 | payload: { 23 | maxLength: 100, 24 | }, 25 | }, 26 | }, 27 | }, 28 | logger: console, 29 | }); 30 | 31 | OpenFeature.setProvider(provider); 32 | 33 | export default provider; 34 | -------------------------------------------------------------------------------- /packages/vue-sdk/dev/plain/components/MissingKeyMessage.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /packages/openfeature-browser-provider/example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-openfeature-example", 3 | "version": "0.2.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@openfeature/core": "1.3.0", 13 | "@openfeature/react-sdk": "^0.4.5", 14 | "@openfeature/web-sdk": "^1.2.3", 15 | "@reflag/react-sdk": "workspace:^", 16 | "next": "14.2.26", 17 | "react": "^18", 18 | "react-dom": "^18" 19 | }, 20 | "devDependencies": { 21 | "@types/node": "^22.12.0", 22 | "@types/react": "^18", 23 | "@types/react-dom": "^18", 24 | "eslint": "^8", 25 | "eslint-config-next": "14.2.5", 26 | "postcss": "^8", 27 | "tailwindcss": "^3.4.1", 28 | "typescript": "^5.7.3" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/feedback/ui/RadialProgress.tsx: -------------------------------------------------------------------------------- 1 | import { FunctionComponent, h } from "preact"; 2 | 3 | export const RadialProgress: FunctionComponent<{ 4 | diameter: number; 5 | progress: number; 6 | }> = ({ diameter, progress }) => { 7 | const stroke = 2; 8 | const radius = diameter / 2 - stroke; 9 | const circumference = 2 * Math.PI * radius; 10 | const filled = circumference * progress; 11 | 12 | return ( 13 | 14 | 24 | 25 | ); 26 | }; 27 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/feedback/ui/config/defaultTranslations.tsx: -------------------------------------------------------------------------------- 1 | import { FeedbackTranslations } from "../types"; 2 | /** 3 | * {@includeCode ./defaultTranslations.tsx} 4 | */ 5 | export const DEFAULT_TRANSLATIONS: FeedbackTranslations = { 6 | DefaultQuestionLabel: "How satisfied are you with this feature?", 7 | QuestionPlaceholder: "Write a comment", 8 | ScoreStatusDescription: "Pick a score and leave a comment", 9 | ScoreStatusLoading: "Saving score, please wait...", 10 | ScoreStatusReceived: "Score has been received!", 11 | ScoreVeryDissatisfiedLabel: "Very dissatisfied (1/5)", 12 | ScoreDissatisfiedLabel: "Dissatisfied (2/5)", 13 | ScoreNeutralLabel: "Neutral (3/5)", 14 | ScoreSatisfiedLabel: "Satisfied (4/5)", 15 | ScoreVerySatisfiedLabel: "Very satisfied (5/5)", 16 | SuccessMessage: "Feedback received, thank you!", 17 | SendButton: "Send feedback", 18 | }; 19 | -------------------------------------------------------------------------------- /packages/cli/utils/file.ts: -------------------------------------------------------------------------------- 1 | import { access, constants } from "node:fs/promises"; 2 | import os from "node:os"; 3 | import { join } from "node:path"; 4 | /** 5 | * Checks if a file exists at the given path. 6 | * @param path The path to the file. 7 | * @returns True if the file exists, false otherwise. 8 | */ 9 | export async function fileExists(path: string): Promise { 10 | try { 11 | await access(path, constants.F_OK); 12 | return true; 13 | } catch { 14 | return false; 15 | } 16 | } 17 | 18 | // Helper to resolve home directory 19 | export const resolvePath = (p: string) => { 20 | return join( 21 | ...p.split("/").map((part) => { 22 | if (part === "~") { 23 | return os.homedir(); 24 | } else if (part === "@") { 25 | return process.env.APPDATA ?? ""; 26 | } else { 27 | return part; 28 | } 29 | }), 30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /packages/cli/utils/constants.ts: -------------------------------------------------------------------------------- 1 | import os from "node:os"; 2 | import { join } from "node:path"; 3 | import { fileURLToPath } from "node:url"; 4 | 5 | export const CLIENT_VERSION_HEADER_NAME = "reflag-sdk-version"; 6 | export const CLIENT_VERSION_HEADER_VALUE = (version: string) => 7 | `cli/${version}`; 8 | 9 | export const CONFIG_FILE_NAME = "reflag.config.json"; 10 | export const AUTH_FILE = join(os.homedir(), ".reflag-auth"); 11 | export const SCHEMA_URL = `https://unpkg.com/@reflag/cli@latest/schema.json`; 12 | 13 | export const DEFAULT_BASE_URL = "https://app.reflag.com"; 14 | export const DEFAULT_API_URL = `${DEFAULT_BASE_URL}/api`; 15 | export const DEFAULT_TYPES_OUTPUT = join("gen", "flags.d.ts"); 16 | 17 | export const DEFAULT_AUTH_TIMEOUT = 60000; // 60 seconds 18 | 19 | export const MODULE_ROOT = fileURLToPath(import.meta.url).substring( 20 | 0, 21 | fileURLToPath(import.meta.url).lastIndexOf("cli") + 3, 22 | ); 23 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/rateLimiter.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from "./logger"; 2 | 3 | const oneMinute = 60 * 1000; 4 | 5 | export default class RateLimiter { 6 | private eventsByKey: Record = {}; 7 | 8 | constructor( 9 | private eventsPerMinute: number, 10 | private logger: Logger, 11 | ) {} 12 | 13 | public rateLimited(key: string, func: () => R): R | undefined { 14 | const now = Date.now(); 15 | 16 | if (!this.eventsByKey[key]) { 17 | this.eventsByKey[key] = []; 18 | } 19 | 20 | const events = this.eventsByKey[key]; 21 | 22 | while (events.length && now - events[0] > oneMinute) { 23 | events.shift(); 24 | } 25 | 26 | const limitExceeded = events.length >= this.eventsPerMinute; 27 | if (limitExceeded) { 28 | this.logger.debug("Rate limit exceeded", { key }); 29 | return; 30 | } 31 | 32 | events.push(now); 33 | return func(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/node-sdk/examples/express/README.md: -------------------------------------------------------------------------------- 1 | # Reflag Node-SDK Express example 2 | 3 | This directory contains a simple example of how to use Reflag's `node-sdk` with 4 | `Express` framework. The example code sets up a Reflag SDK client, starts a 5 | simple REST API service, and uses a set of predefined features to control 6 | a user's access to the API. 7 | 8 | The Reflag SDK client is initialized before the API is started and then, instances 9 | of the client are bound to each individual user's request, to allow for fetching 10 | the relevant features for each request. 11 | 12 | To get started, create an app on [Reflag](https://reflag.com) and take a note of the 13 | secret key which is found under _"Settings"_ -> _"Environments"_. 14 | 15 | ## Running 16 | 17 | The following code snippet should be enough to demonstrate the functionality 18 | of the SDK: 19 | 20 | ```sh 21 | yarn install 22 | 23 | REFLAG_SECRET_KEY= yarn start 24 | ``` 25 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/ui/types.ts: -------------------------------------------------------------------------------- 1 | import { Placement } from "./packages/floating-ui-preact-dom/types"; 2 | 3 | export type DialogPlacement = 4 | | "bottom-right" 5 | | "bottom-left" 6 | | "top-right" 7 | | "top-left"; 8 | 9 | export type PopoverPlacement = Placement; 10 | 11 | export type Offset = { 12 | /** 13 | * Offset from the nearest horizontal screen edge after placement is resolved 14 | */ 15 | x?: string | number; 16 | /** 17 | * Offset from the nearest vertical screen edge after placement is resolved 18 | */ 19 | y?: string | number; 20 | }; 21 | 22 | export type Position = 23 | | { type: "MODAL" } 24 | | { 25 | type: "DIALOG"; 26 | placement: DialogPlacement; 27 | offset?: Offset; 28 | } 29 | | { 30 | type: "POPOVER"; 31 | anchor: HTMLElement | null; 32 | placement?: PopoverPlacement; 33 | }; 34 | 35 | export interface ToolbarPosition { 36 | placement: DialogPlacement; 37 | offset?: Offset; 38 | } 39 | -------------------------------------------------------------------------------- /packages/browser-sdk/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from "@playwright/test"; 2 | 3 | /** 4 | * See https://playwright.dev/docs/test-configuration. 5 | */ 6 | export default defineConfig({ 7 | testDir: "./test/e2e", 8 | testMatch: "**/*.spec.?(c|m)[jt]s?(x)", 9 | fullyParallel: true, 10 | forbidOnly: !!process.env.CI, 11 | retries: process.env.CI ? 2 : 0, 12 | reporter: "list", 13 | 14 | use: { 15 | trace: "on-first-retry", 16 | }, 17 | 18 | projects: [ 19 | { 20 | name: "chromium", 21 | use: { ...devices["Desktop Chrome"] }, 22 | }, 23 | { 24 | name: "firefox", 25 | use: { ...devices["Desktop Firefox"] }, 26 | }, 27 | { 28 | name: "webkit", 29 | use: { ...devices["Desktop Safari"] }, 30 | }, 31 | ], 32 | 33 | webServer: { 34 | // separate port to let the app run alongside the tracking sdk tests 35 | command: "npx http-server . -p 8001", 36 | timeout: 120 * 1000, 37 | port: 8001, 38 | }, 39 | }); 40 | -------------------------------------------------------------------------------- /packages/openfeature-browser-provider/vite.config.js: -------------------------------------------------------------------------------- 1 | import { resolve } from "path"; 2 | import { defineConfig } from "vite"; 3 | import dts from "vite-plugin-dts"; 4 | 5 | export default defineConfig({ 6 | test: { 7 | environment: "jsdom", 8 | exclude: [ 9 | "test/e2e/**", 10 | "**/node_modules/**", 11 | "**/dist/**", 12 | "**/cypress/**", 13 | "**/.{idea,git,cache,output,temp}/**", 14 | "**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build}.config.*", 15 | ], 16 | }, 17 | plugins: [dts({ insertTypesEntry: true })], 18 | build: { 19 | exclude: ["**/node_modules/**", "test/e2e/**", "**/*.test.ts"], 20 | sourcemap: true, 21 | lib: { 22 | // Could also be a dictionary or array of multiple entry points 23 | entry: resolve(__dirname, "src/index.ts"), 24 | name: "ReflagOpenFeatureBrowserProvider", 25 | // the proper extensions will be added 26 | fileName: "reflag-openfeature-browser-provider", 27 | }, 28 | }, 29 | }); 30 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/toolbar/Switch.css: -------------------------------------------------------------------------------- 1 | .switch { 2 | cursor: pointer; 3 | position: relative; 4 | } 5 | 6 | .switch-input { 7 | border: 0px; 8 | clip: rect(0px, 0px, 0px, 0px); 9 | height: 1px; 10 | width: 1px; 11 | margin: -1px; 12 | padding: 0px; 13 | overflow: hidden; 14 | white-space: nowrap; 15 | position: absolute; 16 | } 17 | 18 | .switch-track { 19 | position: relative; 20 | transition: background 0.1s ease; 21 | background: var(--gray600); 22 | border-radius: 999px; 23 | transition: transform 0.1s ease-out; 24 | } 25 | 26 | .switch-input:focus-visible + .switch-track { 27 | outline: 1px solid #fff; 28 | outline-offset: 1px; 29 | } 30 | 31 | .switch[data-enabled="true"] .switch-track { 32 | background: white; 33 | } 34 | 35 | .switch-dot { 36 | background: white; 37 | border-radius: 50%; 38 | position: absolute; 39 | top: 1px; 40 | transition: transform 0.1s ease-out; 41 | box-shadow: 0 0px 5px rgba(0, 0, 0, 0.2); 42 | } 43 | 44 | .switch[data-enabled="true"] .switch-dot { 45 | background: var(--black); 46 | } 47 | -------------------------------------------------------------------------------- /packages/react-sdk/vite.config.mjs: -------------------------------------------------------------------------------- 1 | import { resolve } from "path"; 2 | import preserveDirectives from "rollup-preserve-directives"; 3 | import { defineConfig } from "vite"; 4 | import dts from "vite-plugin-dts"; 5 | 6 | export default defineConfig({ 7 | test: { 8 | root: __dirname, 9 | environment: "jsdom", 10 | }, 11 | optimizeDeps: { 12 | include: ["@reflag/browser-sdk"], 13 | }, 14 | plugins: [ 15 | dts({ insertTypesEntry: true, exclude: ["dev"] }), 16 | preserveDirectives(), 17 | ], 18 | build: { 19 | exclude: ["**/node_modules/**", "test/e2e/**", "dev"], 20 | sourcemap: true, 21 | lib: { 22 | entry: resolve(__dirname, "src/index.tsx"), 23 | name: "ReflagReactSDK", 24 | fileName: "reflag-react-sdk", 25 | formats: ["es", "umd"], 26 | }, 27 | rollupOptions: { 28 | external: ["react", "react-dom"], 29 | output: { 30 | globals: { 31 | react: "React", 32 | }, 33 | }, 34 | }, 35 | }, 36 | server: { 37 | open: "/dev/plain/index.html", 38 | }, 39 | }); 40 | -------------------------------------------------------------------------------- /packages/node-sdk/src/edgeClient.ts: -------------------------------------------------------------------------------- 1 | import { ReflagClient } from "./client"; 2 | import { ClientOptions } from "./types"; 3 | 4 | export type EdgeClientOptions = Omit< 5 | ClientOptions, 6 | "cacheStrategy" | "flushIntervalMs" | "batchOptions" 7 | >; 8 | 9 | /** 10 | * The EdgeClient is ReflagClient pre-configured to be used in edge runtimes, like 11 | * Cloudflare Workers. 12 | * 13 | * @example 14 | * ```ts 15 | * // set the REFLAG_SECRET_KEY environment variable or pass the secret key in the constructor 16 | * const client = new EdgeClient(); 17 | * 18 | * // evaluate a flag 19 | * const context = { 20 | * user: { id: "user-id" }, 21 | * company: { id: "company-id" }, 22 | * } 23 | * const { isEnabled } = client.getFlag(context, "flag-key"); 24 | * 25 | * ``` 26 | */ 27 | export class EdgeClient extends ReflagClient { 28 | constructor(options: EdgeClientOptions = {}) { 29 | const opts = { 30 | ...options, 31 | cacheStrategy: "in-request" as const, 32 | batchOptions: { 33 | intervalMs: 0, 34 | }, 35 | }; 36 | super(opts); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workspaces", 3 | "version": "0.0.1", 4 | "private": true, 5 | "license": "MIT", 6 | "workspaces": [ 7 | "packages/*", 8 | "packages/react-sdk/dev/*", 9 | "packages/openfeature-browser-provider/example" 10 | ], 11 | "scripts": { 12 | "dev": "lerna run dev --parallel", 13 | "build": "lerna run build --stream", 14 | "test:ci": "lerna run test:ci --stream", 15 | "test": "lerna run test --stream", 16 | "format": "lerna run format --stream", 17 | "prettier": "lerna run prettier --stream", 18 | "prettier:fix": "lerna run prettier -- --write", 19 | "lint": "lerna run lint --stream", 20 | "lint:ci": "lerna run lint:ci --stream", 21 | "version": "lerna version --exact --no-push", 22 | "docs": "./docs.sh" 23 | }, 24 | "packageManager": "yarn@4.10.3", 25 | "devDependencies": { 26 | "lerna": "^8.1.3", 27 | "prettier": "^3.5.2", 28 | "typedoc": "0.27.6", 29 | "typedoc-plugin-frontmatter": "^1.1.2", 30 | "typedoc-plugin-markdown": "^4.4.2", 31 | "typedoc-plugin-mdn-links": "^4.0.7", 32 | "typescript": "^5.7.3" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/vue-sdk/dev/plain/components/StartHuddlesButton.vue: -------------------------------------------------------------------------------- 1 | 9 | 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Bucket ApS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/cli/commands/apps.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import { Command } from "commander"; 3 | import ora from "ora"; 4 | 5 | import { listApps } from "../services/bootstrap.js"; 6 | import { configStore } from "../stores/config.js"; 7 | import { handleError } from "../utils/errors.js"; 8 | 9 | export const listAppsAction = async () => { 10 | const baseUrl = configStore.getConfig("baseUrl"); 11 | const spinner = ora(`Loading apps from ${chalk.cyan(baseUrl)}...`).start(); 12 | 13 | try { 14 | const apps = listApps(); 15 | spinner.succeed(`Loaded apps from ${chalk.cyan(baseUrl)}.`); 16 | console.table(apps.map(({ name, id, demo }) => ({ name, id, demo }))); 17 | } catch (error) { 18 | spinner.fail("Failed to list apps."); 19 | handleError(error, "Apps List"); 20 | } 21 | }; 22 | 23 | export function registerAppCommands(cli: Command) { 24 | const appsCommand = new Command("apps").description("Manage apps."); 25 | 26 | appsCommand 27 | .command("list") 28 | .alias("ls") 29 | .description("List all available apps.") 30 | .action(listAppsAction); 31 | 32 | cli.addCommand(appsCommand); 33 | } 34 | -------------------------------------------------------------------------------- /packages/cli/utils/urls.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import slugMod from "slug"; 3 | 4 | import { DEFAULT_BASE_URL } from "./constants.js"; 5 | 6 | export type UrlArgs = { id: string; name: string }; 7 | 8 | export function slug({ id, name }: UrlArgs) { 9 | return `${slugMod(name).substring(0, 15)}-${id}`; 10 | } 11 | 12 | export function stripTrailingSlash(str: T): T { 13 | return str?.endsWith("/") ? (str.slice(0, -1) as T) : str; 14 | } 15 | 16 | export const successUrl = (baseUrl: string) => `${baseUrl}/cli-login/success`; 17 | export const errorUrl = (baseUrl: string, error: string) => 18 | `${baseUrl}/cli-login/error?error=${error}`; 19 | 20 | export const baseUrlSuffix = (baseUrl: string) => { 21 | return baseUrl !== DEFAULT_BASE_URL ? ` at ${chalk.cyan(baseUrl)}` : ""; 22 | }; 23 | 24 | export function environmentUrl(baseUrl: string, environment: UrlArgs): string { 25 | return `${baseUrl}/envs/${slug(environment)}`; 26 | } 27 | 28 | export function featureUrl( 29 | baseUrl: string, 30 | env: UrlArgs, 31 | feature: UrlArgs, 32 | ): string { 33 | return `${environmentUrl(baseUrl, env)}/features/${slug(feature)}`; 34 | } 35 | -------------------------------------------------------------------------------- /packages/vue-sdk/vite.config.mjs: -------------------------------------------------------------------------------- 1 | import { resolve } from "path"; 2 | import vue from "@vitejs/plugin-vue"; 3 | import preserveDirectives from "rollup-preserve-directives"; 4 | import { defineConfig } from "vite"; 5 | import dts from "vite-plugin-dts"; 6 | 7 | export default defineConfig({ 8 | test: { 9 | environment: "jsdom", 10 | }, 11 | optimizeDeps: { 12 | include: ["@reflag/browser-sdk"], 13 | }, 14 | resolve: { 15 | alias: { 16 | vue: "vue/dist/vue.esm-bundler.js", 17 | }, 18 | }, 19 | plugins: [ 20 | vue(), 21 | dts({ insertTypesEntry: true, exclude: ["dev"] }), 22 | preserveDirectives(), 23 | ], 24 | build: { 25 | exclude: ["**/node_modules/**", "test/e2e/**", "dev"], 26 | sourcemap: true, 27 | lib: { 28 | entry: resolve(__dirname, "src/index.ts"), 29 | name: "ReflagVueSDK", 30 | fileName: "reflag-vue-sdk", 31 | formats: ["es", "umd"], 32 | }, 33 | rollupOptions: { 34 | external: ["vue"], 35 | output: { 36 | globals: { 37 | vue: "Vue", 38 | }, 39 | }, 40 | }, 41 | }, 42 | server: { 43 | open: "/dev/plain/index.html", 44 | }, 45 | }); 46 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/logger.ts: -------------------------------------------------------------------------------- 1 | export interface Logger { 2 | debug(message: string, ...args: any[]): void; 3 | info(message: string, ...args: any[]): void; 4 | warn(message: string, ...args: any[]): void; 5 | error(message: string, ...args: any[]): void; 6 | } 7 | 8 | export const quietConsoleLogger = { 9 | debug(_: string) { 10 | // do nothing 11 | }, 12 | info(_: string) { 13 | // do nothing 14 | }, 15 | warn(message: string, ...args: any[]) { 16 | console.warn(message, ...args); 17 | }, 18 | error(message: string, ...args: any[]) { 19 | console.error(message, ...args); 20 | }, 21 | }; 22 | 23 | export function loggerWithPrefix(logger: Logger, prefix: string): Logger { 24 | return { 25 | debug(message: string, ...args: any[]) { 26 | logger.debug(`${prefix} ${message}`, ...args); 27 | }, 28 | info(message: string, ...args: any[]) { 29 | logger.info(`${prefix} ${message}`, ...args); 30 | }, 31 | warn(message: string, ...args: any[]) { 32 | logger.warn(`${prefix} ${message}`, ...args); 33 | }, 34 | error(message: string, ...args: any[]) { 35 | logger.error(`${prefix} ${message}`, ...args); 36 | }, 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /packages/openfeature-browser-provider/example/app/featureManagement.ts: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ReflagBrowserSDKProvider } from "@reflag/openfeature-browser-provider"; 4 | import { OpenFeature } from "@openfeature/react-sdk"; 5 | 6 | const publishableKey = process.env.NEXT_PUBLIC_REFLAG_PUBLISHABLE_KEY; 7 | 8 | let reflagProvider: ReflagBrowserSDKProvider | null = null; 9 | let initialized = false; 10 | 11 | export async function initOpenFeature() { 12 | if (initialized) { 13 | return; 14 | } 15 | initialized = true; 16 | 17 | if (!publishableKey) { 18 | console.error("No publishable key set for Reflag"); 19 | return; 20 | } 21 | reflagProvider = new ReflagBrowserSDKProvider({ 22 | publishableKey, 23 | fallbackFlags: { 24 | huddle: { 25 | key: "zoom", // huddleMeetingProvider 26 | payload: { 27 | joinUrl: "https://zoom.us/join", 28 | }, 29 | }, 30 | }, 31 | }); 32 | return OpenFeature.setProviderAndWait(reflagProvider); 33 | } 34 | 35 | export function track(event: string, attributes?: { [key: string]: any }) { 36 | console.log("Tracking event", event, attributes); 37 | reflagProvider?.client?.track(event, attributes); 38 | } 39 | -------------------------------------------------------------------------------- /packages/cli/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "Reflag cli schema", 4 | "type": "object", 5 | "properties": { 6 | "baseUrl": { 7 | "type": "string", 8 | "pattern": "^https?://.*", 9 | "description": "Base URL for the API. Defaults to https://app.reflag.com." 10 | }, 11 | "apiUrl": { 12 | "type": "string", 13 | "pattern": "^https?://.*", 14 | "description": "API URL for the API. Defaults to https://app.reflag.com/api." 15 | }, 16 | "appId": { 17 | "type": "string", 18 | "minLength": 14, 19 | "maxLength": 14, 20 | "description": "Mandatory ID for the Reflag app. You can find it by calling reflag apps list." 21 | }, 22 | "typesOutput": { 23 | "type": "array", 24 | "description": "List of paths to output the types. The path is relative to the current working directory.", 25 | "items": { 26 | "type": "object", 27 | "properties": { 28 | "path": { "type": "string" }, 29 | "format": { "type": "string", "enum": ["react", "node"] } 30 | }, 31 | "required": ["path"] 32 | } 33 | } 34 | }, 35 | "required": ["appId"] 36 | } 37 | -------------------------------------------------------------------------------- /packages/node-sdk/examples/express/app.test.ts: -------------------------------------------------------------------------------- 1 | import request from "supertest"; 2 | import app, { todos } from "./app"; 3 | import { beforeEach, describe, it, expect, beforeAll } from "vitest"; 4 | 5 | import reflag from "./reflag"; 6 | 7 | beforeAll(async () => await reflag.initialize()); 8 | beforeEach(() => { 9 | reflag.featureOverrides = { 10 | "show-todos": true, 11 | }; 12 | }); 13 | 14 | describe("API Tests", () => { 15 | it("should return 200 for the root endpoint", async () => { 16 | const response = await request(app).get("/"); 17 | expect(response.status).toBe(200); 18 | expect(response.body).toEqual({ message: "Ready to manage some TODOs!" }); 19 | }); 20 | 21 | it("should return todos", async () => { 22 | const response = await request(app).get("/todos"); 23 | expect(response.status).toBe(200); 24 | expect(response.body).toEqual({ todos }); 25 | }); 26 | 27 | it("should return no todos when list is disabled", async () => { 28 | reflag.featureOverrides = () => ({ 29 | "show-todos": false, 30 | }); 31 | const response = await request(app).get("/todos"); 32 | expect(response.status).toBe(200); 33 | expect(response.body).toEqual({ todos: [] }); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /packages/browser-sdk/eslint.config.js: -------------------------------------------------------------------------------- 1 | const base = require("@reflag/eslint-config"); 2 | const preactConfig = require("eslint-config-preact"); 3 | 4 | const compatPlugin = require("eslint-plugin-compat"); 5 | const reactPlugin = require("eslint-plugin-react"); 6 | const reactHooksPlugin = require("eslint-plugin-react-hooks"); 7 | 8 | module.exports = [ 9 | ...base, 10 | { 11 | // Preact projects 12 | files: ["**/*.tsx"], 13 | 14 | settings: { 15 | react: { 16 | // We only care about marking h() as being a used variable. 17 | pragma: "h", 18 | // We use "react 16.0" to avoid pushing folks to UNSAFE_ methods. 19 | version: "16.0", 20 | }, 21 | }, 22 | plugins: { 23 | compat: compatPlugin, 24 | react: reactPlugin, 25 | "react-hooks": reactHooksPlugin, 26 | }, 27 | rules: { 28 | ...preactConfig.rules, 29 | // Ignore React attributes that are not valid in Preact. 30 | // Alternatively, we could use the preact/compat alias or turn off the rule. 31 | "react/no-unknown-property": ["off"], 32 | "no-unused-vars": ["off"], 33 | "react/no-danger": ["off"], 34 | }, 35 | }, 36 | { ignores: ["dist/", "example/"] }, 37 | ]; 38 | -------------------------------------------------------------------------------- /packages/browser-sdk/vite.config.mjs: -------------------------------------------------------------------------------- 1 | import { resolve } from "path"; 2 | import { defineConfig } from "vite"; 3 | import dts from "vite-plugin-dts"; 4 | import { defaultExclude } from "vitest/config"; 5 | 6 | export default defineConfig({ 7 | test: { 8 | root: __dirname, 9 | environment: "jsdom", 10 | globals: true, 11 | setupFiles: ["./vitest.setup.ts"], 12 | exclude: [...defaultExclude, "test/e2e/**"], 13 | }, 14 | plugins: [dts({ insertTypesEntry: true })], 15 | build: { 16 | exclude: ["**/node_modules/**", "test/e2e/**"], 17 | sourcemap: true, 18 | lib: { 19 | // Could also be a dictionary or array of multiple entry points 20 | entry: resolve(__dirname, "src/index.ts"), 21 | name: "ReflagBrowserSDK", 22 | // the proper extensions will be added 23 | fileName: "reflag-browser-sdk", 24 | }, 25 | rollupOptions: { 26 | // make sure to externalize deps that shouldn't be bundled 27 | // into your library 28 | // external: ["vue"], 29 | output: { 30 | // Provide global variables to use in the UMD build 31 | // for externalized deps 32 | globals: { 33 | ReflagClient: "ReflagClient", 34 | }, 35 | }, 36 | }, 37 | }, 38 | }); 39 | -------------------------------------------------------------------------------- /packages/flag-evaluation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@reflag/flag-evaluation", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/reflagcom/javascript.git" 8 | }, 9 | "publishConfig": { 10 | "access": "public" 11 | }, 12 | "scripts": { 13 | "build": "tsc --project tsconfig.build.json", 14 | "test": "vitest", 15 | "test:ci": "vitest --reporter=default --reporter=junit --outputFile=junit.xml", 16 | "lint": "eslint .", 17 | "lint:ci": "eslint --output-file eslint-report.json --format json .", 18 | "prettier": "prettier --check .", 19 | "format": "yarn lint --fix && yarn prettier --write", 20 | "preversion": "yarn lint && yarn prettier && yarn vitest run -c vite.config.js && yarn build" 21 | }, 22 | "files": [ 23 | "dist" 24 | ], 25 | "main": "./dist/index.js", 26 | "types": "./dist/index.d.ts", 27 | "devDependencies": { 28 | "@reflag/eslint-config": "^0.0.2", 29 | "@reflag/tsconfig": "^0.0.2", 30 | "@types/node": "^22.12.0", 31 | "eslint": "^9.21.0", 32 | "prettier": "^3.5.2", 33 | "ts-node": "^10.9.2", 34 | "typescript": "^5.7.3", 35 | "vitest": "^2.0.5" 36 | }, 37 | "dependencies": { 38 | "js-sha256": "0.11.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/openfeature-node-provider/example/README.md: -------------------------------------------------------------------------------- 1 | # Reflag Node OpenFeature provider express example 2 | 3 | This directory contains a simple example of how to use Reflag's `node-sdk` 4 | with and the OpenFeature Reflag Node Provider with the `Express` framework. 5 | 6 | The example code sets up a Reflag Node Provider, starts a 7 | simple REST API service, and uses a set of predefined features to control 8 | a user's access to the API. 9 | 10 | The provider is initialized before the API is started and then, instances 11 | of the client are bound to each individual user's request, to allow for fetching 12 | the relevant features for each request. 13 | 14 | To get started, create an app on [Reflag](https://reflag.com) and take a note of the 15 | secret key which is found under _"Settings"_ -> _"Environments"_. 16 | 17 | ## Context 18 | 19 | See [defaultTranslator](https://github.com/reflagcom/javascript/blob/7d108db2d1bde6e40d9eda92b66d06a1fbb7fa3f/packages/openfeature-node-provider/src/index.ts#L23-L45) for how OpenFeature context is translated into Reflag context 20 | by default 21 | 22 | ## Running 23 | 24 | The following code snippet should be enough to demonstrate the functionality 25 | of the SDK: 26 | 27 | ```sh 28 | yarn install 29 | 30 | REFLAG_SECRET_KEY= yarn start 31 | ``` 32 | -------------------------------------------------------------------------------- /packages/vue-sdk/src/index.ts: -------------------------------------------------------------------------------- 1 | import { App } from "vue"; 2 | 3 | import ReflagBootstrappedProvider from "./ReflagBootstrappedProvider.vue"; 4 | import ReflagClientProvider from "./ReflagClientProvider.vue"; 5 | import ReflagProvider from "./ReflagProvider.vue"; 6 | 7 | export { 8 | useClient, 9 | useFlag, 10 | useIsLoading, 11 | useOnEvent, 12 | useRequestFeedback, 13 | useSendFeedback, 14 | useTrack, 15 | useUpdateCompany, 16 | useUpdateOtherContext, 17 | useUpdateUser, 18 | } from "./hooks"; 19 | export type { 20 | BootstrappedFlags, 21 | EmptyFlagRemoteConfig, 22 | Flag, 23 | Flags, 24 | FlagType, 25 | ReflagBaseProps, 26 | ReflagBootstrappedProps, 27 | ReflagClientProviderProps, 28 | ReflagInitOptionsBase, 29 | ReflagProps, 30 | RequestFlagFeedbackOptions, 31 | TypedFlags, 32 | } from "./types"; 33 | export type { 34 | CheckEvent, 35 | CompanyContext, 36 | TrackEvent, 37 | UserContext, 38 | } from "@reflag/browser-sdk"; 39 | 40 | export { ReflagBootstrappedProvider, ReflagClientProvider, ReflagProvider }; 41 | 42 | export default { 43 | install(app: App) { 44 | app.component("ReflagProvider", ReflagProvider); 45 | app.component("ReflagBootstrappedProvider", ReflagBootstrappedProvider); 46 | app.component("ReflagClientProvider", ReflagClientProvider); 47 | }, 48 | }; 49 | -------------------------------------------------------------------------------- /packages/browser-sdk/src/toolbar/Switch.tsx: -------------------------------------------------------------------------------- 1 | import { Fragment, h } from "preact"; 2 | 3 | interface SwitchProps extends h.JSX.HTMLAttributes { 4 | checked: boolean; 5 | width?: number; 6 | height?: number; 7 | } 8 | 9 | const gutter = 1; 10 | 11 | export function Switch({ 12 | checked, 13 | width = 24, 14 | height = 14, 15 | ...props 16 | }: SwitchProps) { 17 | return ( 18 | <> 19 |