#
tokens: 49958/50000 93/327 files (page 1/9)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 1 of 9. Use http://codebase.md/bucketco/bucket-javascript-sdk?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .editorconfig
├── .gitattributes
├── .github
│   └── workflows
│       ├── package-ci.yml
│       └── publish.yml
├── .gitignore
├── .nvmrc
├── .prettierignore
├── .vscode
│   ├── extensions.json
│   └── settings.json
├── .yarnrc.yml
├── docs.sh
├── lerna.json
├── LICENSE
├── package.json
├── packages
│   ├── browser-sdk
│   │   ├── .prettierignore
│   │   ├── eslint.config.js
│   │   ├── example
│   │   │   ├── feedback
│   │   │   │   ├── feedback.html
│   │   │   │   └── Feedback.jsx
│   │   │   └── typescript
│   │   │       ├── app.ts
│   │   │       └── index.html
│   │   ├── FEEDBACK.md
│   │   ├── index.html
│   │   ├── package.json
│   │   ├── playwright.config.ts
│   │   ├── postcss.config.js
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── client.ts
│   │   │   ├── config.ts
│   │   │   ├── context.ts
│   │   │   ├── feedback
│   │   │   │   ├── feedback.ts
│   │   │   │   ├── prompts.ts
│   │   │   │   ├── promptStorage.ts
│   │   │   │   └── ui
│   │   │   │       ├── Button.css
│   │   │   │       ├── Button.tsx
│   │   │   │       ├── config
│   │   │   │       │   └── defaultTranslations.tsx
│   │   │   │       ├── css.d.ts
│   │   │   │       ├── FeedbackDialog.css
│   │   │   │       ├── FeedbackDialog.tsx
│   │   │   │       ├── FeedbackForm.css
│   │   │   │       ├── FeedbackForm.tsx
│   │   │   │       ├── hooks
│   │   │   │       │   └── useTimer.ts
│   │   │   │       ├── index.css
│   │   │   │       ├── index.ts
│   │   │   │       ├── Plug.tsx
│   │   │   │       ├── RadialProgress.css
│   │   │   │       ├── RadialProgress.tsx
│   │   │   │       ├── StarRating.css
│   │   │   │       ├── StarRating.tsx
│   │   │   │       └── types.ts
│   │   │   ├── flag
│   │   │   │   ├── flagCache.ts
│   │   │   │   └── flags.ts
│   │   │   ├── hooksManager.ts
│   │   │   ├── httpClient.ts
│   │   │   ├── index.ts
│   │   │   ├── logger.ts
│   │   │   ├── rateLimiter.ts
│   │   │   ├── sse.ts
│   │   │   ├── toolbar
│   │   │   │   ├── Flags.css
│   │   │   │   ├── Flags.tsx
│   │   │   │   ├── index.css
│   │   │   │   ├── index.ts
│   │   │   │   ├── Switch.css
│   │   │   │   ├── Switch.tsx
│   │   │   │   ├── Toolbar.css
│   │   │   │   └── Toolbar.tsx
│   │   │   └── ui
│   │   │       ├── constants.ts
│   │   │       ├── Dialog.css
│   │   │       ├── Dialog.tsx
│   │   │       ├── icons
│   │   │       │   ├── Check.tsx
│   │   │       │   ├── CheckCircle.tsx
│   │   │       │   ├── Close.tsx
│   │   │       │   ├── Dissatisfied.tsx
│   │   │       │   ├── Logo.tsx
│   │   │       │   ├── Neutral.tsx
│   │   │       │   ├── Satisfied.tsx
│   │   │       │   ├── VeryDissatisfied.tsx
│   │   │       │   └── VerySatisfied.tsx
│   │   │       ├── packages
│   │   │       │   └── floating-ui-preact-dom
│   │   │       │       ├── arrow.ts
│   │   │       │       ├── index.ts
│   │   │       │       ├── README.md
│   │   │       │       ├── types.ts
│   │   │       │       ├── useFloating.ts
│   │   │       │       └── utils
│   │   │       │           ├── deepEqual.ts
│   │   │       │           ├── getDPR.ts
│   │   │       │           ├── roundByDPR.ts
│   │   │       │           └── useLatestRef.ts
│   │   │       ├── types.ts
│   │   │       └── utils.ts
│   │   ├── test
│   │   │   ├── client.test.ts
│   │   │   ├── e2e
│   │   │   │   ├── acceptance.browser.spec.ts
│   │   │   │   ├── empty.html
│   │   │   │   ├── feedback-widget.browser.spec.ts
│   │   │   │   └── give-feedback-button.html
│   │   │   ├── flagCache.test.ts
│   │   │   ├── flags.test.ts
│   │   │   ├── hooksManager.test.ts
│   │   │   ├── httpClient.test.ts
│   │   │   ├── init.test.ts
│   │   │   ├── mocks
│   │   │   │   ├── handlers.ts
│   │   │   │   └── server.ts
│   │   │   ├── prompts.test.ts
│   │   │   ├── promptStorage.test.ts
│   │   │   ├── rateLimiter.test.ts
│   │   │   ├── sse.test.ts
│   │   │   ├── testLogger.ts
│   │   │   └── usage.test.ts
│   │   ├── tsconfig.build.json
│   │   ├── tsconfig.eslint.json
│   │   ├── tsconfig.json
│   │   ├── typedoc.json
│   │   ├── vite.config.mjs
│   │   ├── vite.e2e.config.js
│   │   └── vitest.setup.ts
│   ├── cli
│   │   ├── .prettierignore
│   │   ├── commands
│   │   │   ├── apps.ts
│   │   │   ├── auth.ts
│   │   │   ├── flags.ts
│   │   │   ├── init.ts
│   │   │   ├── mcp.ts
│   │   │   ├── new.ts
│   │   │   └── rules.ts
│   │   ├── eslint.config.js
│   │   ├── index.ts
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── schema.json
│   │   ├── services
│   │   │   ├── bootstrap.ts
│   │   │   ├── flags.ts
│   │   │   ├── mcp.ts
│   │   │   └── rules.ts
│   │   ├── stores
│   │   │   ├── auth.ts
│   │   │   └── config.ts
│   │   ├── test
│   │   │   └── json.test.ts
│   │   ├── tsconfig.eslint.json
│   │   ├── tsconfig.json
│   │   ├── utils
│   │   │   ├── auth.ts
│   │   │   ├── commander.ts
│   │   │   ├── constants.ts
│   │   │   ├── errors.ts
│   │   │   ├── file.ts
│   │   │   ├── gen.ts
│   │   │   ├── json.ts
│   │   │   ├── options.ts
│   │   │   ├── schemas.ts
│   │   │   ├── types.ts
│   │   │   ├── urls.ts
│   │   │   └── version.ts
│   │   └── vite.config.js
│   ├── eslint-config
│   │   ├── base.js
│   │   └── package.json
│   ├── flag-evaluation
│   │   ├── .prettierignore
│   │   ├── eslint.config.js
│   │   ├── jest.config.js
│   │   ├── package.json
│   │   ├── src
│   │   │   └── index.ts
│   │   ├── test
│   │   │   └── index.test.ts
│   │   ├── tsconfig.build.json
│   │   ├── tsconfig.eslint.json
│   │   └── tsconfig.json
│   ├── node-sdk
│   │   ├── .prettierignore
│   │   ├── docs
│   │   │   ├── type-check-failed.png
│   │   │   └── type-check-payload-failed.png
│   │   ├── eslint.config.js
│   │   ├── examples
│   │   │   ├── cloudflare-worker
│   │   │   │   ├── .gitignore
│   │   │   │   ├── .prettierignore
│   │   │   │   ├── .vscode
│   │   │   │   │   └── settings.json
│   │   │   │   ├── package.json
│   │   │   │   ├── README.md
│   │   │   │   ├── src
│   │   │   │   │   └── index.ts
│   │   │   │   ├── tsconfig.json
│   │   │   │   ├── vitest.config.mts
│   │   │   │   ├── worker-configuration.d.ts
│   │   │   │   ├── wrangler.jsonc
│   │   │   │   └── yarn.lock
│   │   │   └── express
│   │   │       ├── app.test.ts
│   │   │       ├── app.ts
│   │   │       ├── bucket.ts
│   │   │       ├── bucketConfig.json
│   │   │       ├── package.json
│   │   │       ├── README.md
│   │   │       ├── serve.ts
│   │   │       ├── tsconfig.json
│   │   │       └── yarn.lock
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── batch-buffer.ts
│   │   │   ├── client.ts
│   │   │   ├── config.ts
│   │   │   ├── edgeClient.ts
│   │   │   ├── fetch-http-client.ts
│   │   │   ├── flusher.ts
│   │   │   ├── index.ts
│   │   │   ├── inRequestCache.ts
│   │   │   ├── periodicallyUpdatingCache.ts
│   │   │   ├── rate-limiter.ts
│   │   │   ├── types.ts
│   │   │   └── utils.ts
│   │   ├── test
│   │   │   ├── batch-buffer.test.ts
│   │   │   ├── client.test.ts
│   │   │   ├── config.test.ts
│   │   │   ├── fetch-http-client.test.ts
│   │   │   ├── flusher.test.ts
│   │   │   ├── inRequestCache.test.ts
│   │   │   ├── periodicallyUpdatingCache.test.ts
│   │   │   ├── rate-limiter.test.ts
│   │   │   ├── testConfig.json
│   │   │   └── utils.test.ts
│   │   ├── tsconfig.build.json
│   │   ├── tsconfig.eslint.json
│   │   ├── tsconfig.json
│   │   ├── typedoc.json
│   │   └── vite.config.js
│   ├── openfeature-browser-provider
│   │   ├── .prettierignore
│   │   ├── eslint.config.js
│   │   ├── example
│   │   │   ├── .eslintrc.json
│   │   │   ├── .gitignore
│   │   │   ├── app
│   │   │   │   ├── featureManagement.ts
│   │   │   │   ├── globals.css
│   │   │   │   ├── layout.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── components
│   │   │   │   ├── Context.tsx
│   │   │   │   ├── HuddleFeature.tsx
│   │   │   │   └── OpenFeatureProvider.tsx
│   │   │   ├── next.config.mjs
│   │   │   ├── package.json
│   │   │   ├── postcss.config.mjs
│   │   │   ├── README.md
│   │   │   ├── tailwind.config.ts
│   │   │   └── tsconfig.json
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── index.test.ts
│   │   │   └── index.ts
│   │   ├── tsconfig.build.json
│   │   ├── tsconfig.eslint.json
│   │   ├── tsconfig.json
│   │   └── vite.config.js
│   ├── openfeature-node-provider
│   │   ├── .prettierignore
│   │   ├── eslint.config.js
│   │   ├── example
│   │   │   ├── app.ts
│   │   │   ├── package.json
│   │   │   ├── README.md
│   │   │   ├── reflag.ts
│   │   │   ├── serve.ts
│   │   │   ├── tsconfig.json
│   │   │   └── yarn.lock
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── index.test.ts
│   │   │   └── index.ts
│   │   ├── tsconfig.build.json
│   │   ├── tsconfig.eslint.json
│   │   ├── tsconfig.json
│   │   └── vite.config.js
│   ├── react-sdk
│   │   ├── .prettierignore
│   │   ├── dev
│   │   │   ├── .env
│   │   │   ├── nextjs-bootstrap-demo
│   │   │   │   ├── .eslintrc.json
│   │   │   │   ├── .gitignore
│   │   │   │   ├── app
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── favicon.ico
│   │   │   │   │   ├── globals.css
│   │   │   │   │   ├── layout.tsx
│   │   │   │   │   └── page.tsx
│   │   │   │   ├── components
│   │   │   │   │   └── Flags.tsx
│   │   │   │   ├── next.config.mjs
│   │   │   │   ├── package.json
│   │   │   │   ├── postcss.config.mjs
│   │   │   │   ├── public
│   │   │   │   │   ├── next.svg
│   │   │   │   │   └── vercel.svg
│   │   │   │   ├── README.md
│   │   │   │   ├── tailwind.config.ts
│   │   │   │   └── tsconfig.json
│   │   │   ├── nextjs-flag-demo
│   │   │   │   ├── .eslintrc.json
│   │   │   │   ├── .gitignore
│   │   │   │   ├── app
│   │   │   │   │   ├── favicon.ico
│   │   │   │   │   ├── globals.css
│   │   │   │   │   ├── layout.tsx
│   │   │   │   │   └── page.tsx
│   │   │   │   ├── components
│   │   │   │   │   ├── Flags.tsx
│   │   │   │   │   └── Providers.tsx
│   │   │   │   ├── next.config.mjs
│   │   │   │   ├── package.json
│   │   │   │   ├── postcss.config.mjs
│   │   │   │   ├── public
│   │   │   │   │   ├── next.svg
│   │   │   │   │   └── vercel.svg
│   │   │   │   ├── README.md
│   │   │   │   ├── tailwind.config.ts
│   │   │   │   └── tsconfig.json
│   │   │   └── plain
│   │   │       ├── app.tsx
│   │   │       ├── index.html
│   │   │       ├── index.tsx
│   │   │       ├── tsconfig.json
│   │   │       └── vite-env.d.ts
│   │   ├── eslint.config.js
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   └── index.tsx
│   │   ├── test
│   │   │   └── usage.test.tsx
│   │   ├── tsconfig.build.json
│   │   ├── tsconfig.eslint.json
│   │   ├── tsconfig.json
│   │   ├── typedoc.json
│   │   └── vite.config.mjs
│   ├── tsconfig
│   │   ├── library.json
│   │   └── package.json
│   └── vue-sdk
│       ├── .prettierignore
│       ├── dev
│       │   └── plain
│       │       ├── App.vue
│       │       ├── components
│       │       │   ├── Events.vue
│       │       │   ├── FlagsList.vue
│       │       │   ├── MissingKeyMessage.vue
│       │       │   ├── RequestFeedback.vue
│       │       │   ├── Section.vue
│       │       │   ├── StartHuddlesButton.vue
│       │       │   └── Track.vue
│       │       ├── env.d.ts
│       │       ├── index.html
│       │       └── index.ts
│       ├── eslint.config.js
│       ├── package.json
│       ├── README.md
│       ├── src
│       │   ├── hooks.ts
│       │   ├── index.ts
│       │   ├── ReflagBootstrappedProvider.vue
│       │   ├── ReflagClientProvider.vue
│       │   ├── ReflagProvider.vue
│       │   ├── types.ts
│       │   ├── version.ts
│       │   └── vue.d.ts
│       ├── test
│       │   └── usage.test.ts
│       ├── tsconfig.build.json
│       ├── tsconfig.eslint.json
│       ├── tsconfig.json
│       ├── typedoc.json
│       └── vite.config.mjs
├── README.md
├── typedoc.json
├── vitest.workspace.js
└── yarn.lock
```

# Files

--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------

```
1 | 20.15.1
2 | 
```

--------------------------------------------------------------------------------
/packages/flag-evaluation/.prettierignore:
--------------------------------------------------------------------------------

```
1 | dist
2 | coverage
3 | 
```

--------------------------------------------------------------------------------
/packages/node-sdk/.prettierignore:
--------------------------------------------------------------------------------

```
1 | dist
2 | eslint-report.json
3 | 
```

--------------------------------------------------------------------------------
/packages/openfeature-node-provider/.prettierignore:
--------------------------------------------------------------------------------

```
1 | dist
2 | eslint-report.json
3 | 
```

--------------------------------------------------------------------------------
/.yarnrc.yml:
--------------------------------------------------------------------------------

```yaml
1 | nodeLinker: node-modules
2 | 
```

--------------------------------------------------------------------------------
/packages/node-sdk/examples/cloudflare-worker/.prettierignore:
--------------------------------------------------------------------------------

```
1 | worker-configuration.d.ts
2 | 
```

--------------------------------------------------------------------------------
/packages/cli/.prettierignore:
--------------------------------------------------------------------------------

```
1 | dist
2 | eslint-report.json
3 | gen
4 | 
```

--------------------------------------------------------------------------------
/packages/react-sdk/.prettierignore:
--------------------------------------------------------------------------------

```
1 | dist
2 | eslint-report.json
3 | dev
4 | 
```

--------------------------------------------------------------------------------
/packages/vue-sdk/.prettierignore:
--------------------------------------------------------------------------------

```
1 | dist
2 | eslint-report.json
3 | dev
4 | 
```

--------------------------------------------------------------------------------
/packages/browser-sdk/.prettierignore:
--------------------------------------------------------------------------------

```
1 | dist
2 | eslint-report.json
3 | test-results
4 | 
```

--------------------------------------------------------------------------------
/packages/react-sdk/dev/.env:
--------------------------------------------------------------------------------

```
1 | PUBLISHABLE_KEY=your_publishable_key
2 | 
```

--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------

```
1 | **/build
2 | **/gen
3 | **/node_modules
4 | **/dist
5 | 
```

--------------------------------------------------------------------------------
/packages/openfeature-browser-provider/example/.eslintrc.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "extends": "next/core-web-vitals"
3 | }
4 | 
```

--------------------------------------------------------------------------------
/packages/react-sdk/dev/nextjs-bootstrap-demo/.eslintrc.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "extends": "next/core-web-vitals"
3 | }
4 | 
```

--------------------------------------------------------------------------------
/packages/react-sdk/dev/nextjs-flag-demo/.eslintrc.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "extends": "next/core-web-vitals"
3 | }
4 | 
```

--------------------------------------------------------------------------------
/packages/openfeature-browser-provider/.prettierignore:
--------------------------------------------------------------------------------

```
1 | dist
2 | eslint-report.json
3 | test-results
4 | .next
5 | 
```

--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------

```
1 | /.yarn/**            linguist-vendored
2 | /.yarn/releases/*    binary
3 | /.yarn/plugins/**/*  binary
4 | /.pnp.*              binary linguist-generated
5 | 
```

--------------------------------------------------------------------------------
/.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/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/.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 | 
```

--------------------------------------------------------------------------------
/.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/node-sdk/examples/cloudflare-worker/.gitignore:
--------------------------------------------------------------------------------

```
  1 | # Logs
  2 | 
  3 | logs
  4 | _.log
  5 | npm-debug.log_
  6 | yarn-debug.log*
  7 | yarn-error.log*
  8 | lerna-debug.log*
  9 | .pnpm-debug.log*
 10 | 
 11 | # Diagnostic reports (https://nodejs.org/api/report.html)
 12 | 
 13 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
 14 | 
 15 | # Runtime data
 16 | 
 17 | pids
 18 | _.pid
 19 | _.seed
 20 | \*.pid.lock
 21 | 
 22 | # Directory for instrumented libs generated by jscoverage/JSCover
 23 | 
 24 | lib-cov
 25 | 
 26 | # Coverage directory used by tools like istanbul
 27 | 
 28 | coverage
 29 | \*.lcov
 30 | 
 31 | # nyc test coverage
 32 | 
 33 | .nyc_output
 34 | 
 35 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
 36 | 
 37 | .grunt
 38 | 
 39 | # Bower dependency directory (https://bower.io/)
 40 | 
 41 | bower_components
 42 | 
 43 | # node-waf configuration
 44 | 
 45 | .lock-wscript
 46 | 
 47 | # Compiled binary addons (https://nodejs.org/api/addons.html)
 48 | 
 49 | build/Release
 50 | 
 51 | # Dependency directories
 52 | 
 53 | node_modules/
 54 | jspm_packages/
 55 | 
 56 | # Snowpack dependency directory (https://snowpack.dev/)
 57 | 
 58 | web_modules/
 59 | 
 60 | # TypeScript cache
 61 | 
 62 | \*.tsbuildinfo
 63 | 
 64 | # Optional npm cache directory
 65 | 
 66 | .npm
 67 | 
 68 | # Optional eslint cache
 69 | 
 70 | .eslintcache
 71 | 
 72 | # Optional stylelint cache
 73 | 
 74 | .stylelintcache
 75 | 
 76 | # Microbundle cache
 77 | 
 78 | .rpt2_cache/
 79 | .rts2_cache_cjs/
 80 | .rts2_cache_es/
 81 | .rts2_cache_umd/
 82 | 
 83 | # Optional REPL history
 84 | 
 85 | .node_repl_history
 86 | 
 87 | # Output of 'npm pack'
 88 | 
 89 | \*.tgz
 90 | 
 91 | # Yarn Integrity file
 92 | 
 93 | .yarn-integrity
 94 | 
 95 | # dotenv environment variable files
 96 | 
 97 | .env
 98 | .env.development.local
 99 | .env.test.local
100 | .env.production.local
101 | .env.local
102 | 
103 | # parcel-bundler cache (https://parceljs.org/)
104 | 
105 | .cache
106 | .parcel-cache
107 | 
108 | # Next.js build output
109 | 
110 | .next
111 | out
112 | 
113 | # Nuxt.js build / generate output
114 | 
115 | .nuxt
116 | dist
117 | 
118 | # Gatsby files
119 | 
120 | .cache/
121 | 
122 | # Comment in the public line in if your project uses Gatsby and not Next.js
123 | 
124 | # https://nextjs.org/blog/next-9-1#public-directory-support
125 | 
126 | # public
127 | 
128 | # vuepress build output
129 | 
130 | .vuepress/dist
131 | 
132 | # vuepress v2.x temp and cache directory
133 | 
134 | .temp
135 | .cache
136 | 
137 | # Docusaurus cache and generated files
138 | 
139 | .docusaurus
140 | 
141 | # Serverless directories
142 | 
143 | .serverless/
144 | 
145 | # FuseBox cache
146 | 
147 | .fusebox/
148 | 
149 | # DynamoDB Local files
150 | 
151 | .dynamodb/
152 | 
153 | # TernJS port file
154 | 
155 | .tern-port
156 | 
157 | # Stores VSCode versions used for testing VSCode extensions
158 | 
159 | .vscode-test
160 | 
161 | # yarn v2
162 | 
163 | .yarn/cache
164 | .yarn/unplugged
165 | .yarn/build-state.yml
166 | .yarn/install-state.gz
167 | .pnp.\*
168 | 
169 | # wrangler project
170 | 
171 | .dev.vars
172 | .wrangler/
173 | 
```

--------------------------------------------------------------------------------
/packages/browser-sdk/src/ui/packages/floating-ui-preact-dom/README.md:
--------------------------------------------------------------------------------

```markdown
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/react-sdk/dev/nextjs-bootstrap-demo/README.md:
--------------------------------------------------------------------------------

```markdown
 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-flag-demo/README.md:
--------------------------------------------------------------------------------

```markdown
 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-browser-provider/example/README.md:
--------------------------------------------------------------------------------

```markdown
 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=<publishable-key> yarn dev
11 | ```
12 | 
```

--------------------------------------------------------------------------------
/packages/node-sdk/examples/cloudflare-worker/README.md:
--------------------------------------------------------------------------------

```markdown
 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/node-sdk/examples/express/README.md:
--------------------------------------------------------------------------------

```markdown
 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=<secretKey> yarn start
24 | ```
25 | 
```

--------------------------------------------------------------------------------
/packages/openfeature-node-provider/example/README.md:
--------------------------------------------------------------------------------

```markdown
 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=<secretKey> yarn start
31 | ```
32 | 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Reflag
 2 | 
 3 | Feature flags for SaaS that run on TypeScript. [Learn more and get started](https://reflag.com/)
 4 | 
 5 | ## React SDK
 6 | 
 7 | Client side React SDK
 8 | 
 9 | [Read the docs](packages/react-sdk/README.md)
10 | 
11 | ## Vue SDK (beta)
12 | 
13 | Client side Vue SDK
14 | 
15 | [Read the docs](packages/vue-sdk/README.md)
16 | 
17 | ## Browser SDK
18 | 
19 | Browser SDK for use in non-React web applications
20 | 
21 | [Read the docs](packages/browser-sdk/README.md)
22 | 
23 | ## Node.js SDK
24 | 
25 | Node.js SDK for use on the server side.
26 | Use this for Cloudflare Workers as well.
27 | 
28 | [Read the docs](packages/node-sdk/README.md)
29 | 
30 | ## Reflag CLI
31 | 
32 | CLI to interact with Reflag and generate types
33 | 
34 | [Read the docs](packages/cli/README.md)
35 | 
36 | ## OpenFeature Browser Provider
37 | 
38 | Use Reflag with OpenFeature in the browser through the Reflag OpenFeature Browser Provider
39 | 
40 | [Read the docs](packages/openfeature-browser-provider/README.md)
41 | 
42 | ## OpenFeature Node.js Provider
43 | 
44 | Use the Reflag with OpenFeature on the server in Node.js through the Reflag OpenFeature Node.js Provider
45 | 
46 | [Read the docs](packages/openfeature-node-provider/README.md)
47 | 
48 | ## Development
49 | 
50 | ### Versioning
51 | 
52 | 1. Create a new branch locally
53 | 2. Run `yarn run version`
54 | 3. Push and PR
55 | 
56 | ### Publishing
57 | 
58 | The [Github Action](.github/workflows/publish.yml) will automatically publish any versioned packages when merging to `main`
59 | 
```

--------------------------------------------------------------------------------
/packages/openfeature-browser-provider/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Reflag Browser OpenFeature Provider
  2 | 
  3 | The official OpenFeature Browser provider for [Reflag.com](https://reflag.com) flag management service.
  4 | 
  5 | It uses the Reflag Browser SDK internally and thus allow you to collect [automated feedback surveys](https://github.com/reflagcom/javascript/tree/main/packages/browser-sdk#qualitative-feedback)
  6 | when people use your flag as well as tracking which customers use which features.
  7 | 
  8 | If you're using React, you'll be better off with the [Reflag React SDK](https://github.com/reflagcom/javascript/blob/main/packages/react-sdk/README.md) or the [OpenFeature React SDK](https://openfeature.dev/docs/reference/technologies/client/web/react/).
  9 | 
 10 | See the `example` folder for how to use the OpenFeature React SDK with Next.js.
 11 | 
 12 | ## Installation
 13 | 
 14 | The OpenFeature SDK is required as peer dependency.
 15 | 
 16 | The minimum required version of `@openfeature/web-sdk` currently is `1.0`.
 17 | 
 18 | ```shell
 19 | npm install @openfeature/web-sdk @reflag/openfeature-browser-provider
 20 | ```
 21 | 
 22 | ## Migrating from Bucket OpenFeature SDK
 23 | 
 24 | If you have been using the Bucket SDKs, the following list will help you migrate to Reflag SDK:
 25 | 
 26 | - `Bucket*` classes, and types have been renamed to `Reflag*` (e.g. `BucketClient` is now `ReflagClient`)
 27 | - The `fallbackFeatures` property in client constructor and configuration files has been renamed to `fallbackFlags`
 28 | - `featureKey` has been renamed to `flagKey` in all methods that accepts that argument
 29 | - The SDKs will not emit `evaluate` and `evaluate-config` events anymore
 30 | - The new cookies that are stored in the client's browser are now `reflag-*` prefixed instead og `bucket-*`
 31 | 
 32 | If you are running with strict Content Security Policies active on your website, you will need change them as follows:
 33 | 
 34 | - `connect-src https://front.bucket.co` to `connect-src https://front.reflag.com`
 35 | 
 36 | Finally, if you have customized the look & feel of the Feedback component, update `--bucket-feedback-*` CSS classes to `--reflag-feedback-*`
 37 | 
 38 | ## Sample initialization
 39 | 
 40 | ```ts
 41 | import { ReflagBrowserProvider } from "@reflag/openfeature-browser-provider";
 42 | import { OpenFeature } from "@openfeature/web-sdk";
 43 | 
 44 | // initialize provider
 45 | const publishableKey = "<your-reflag-publishable-key>";
 46 | 
 47 | const reflagProvider = new ReflagBrowserProvider({ publishableKey });
 48 | 
 49 | // set open feature provider and get client
 50 | await OpenFeature.setProviderAndWait(reflagProvider);
 51 | const client = OpenFeature.getClient();
 52 | 
 53 | // use client
 54 | const boolValue = client.getBooleanValue("huddles", false);
 55 | 
 56 | // use more complex, dynamic config-enabled functionality.
 57 | const feedbackConfig = client.getObjectValue("ask-feedback", {
 58 |   question: "How are you enjoying this feature?",
 59 | });
 60 | ```
 61 | 
 62 | Initializing the Reflag Browser Provider will
 63 | also initialize [automatic feedback surveys](https://github.com/reflagcom/javascript/tree/main/packages/browser-sdk#qualitative-feedback).
 64 | 
 65 | ## Feature resolution methods
 66 | 
 67 | The Reflag OpenFeature Provider implements the OpenFeature evaluation interface for different value types. Each method handles the resolution of flags according to the OpenFeature specification.
 68 | 
 69 | ### Common behavior
 70 | 
 71 | All resolution methods share these behaviors:
 72 | 
 73 | - Return default value with `PROVIDER_NOT_READY` if client is not initialized,
 74 | - Return default value with `FLAG_NOT_FOUND` if flag doesn't exist,
 75 | - Return default value with `ERROR` if there was a type mismatch,
 76 | - Return evaluated value with `TARGETING_MATCH` on successful resolution.
 77 | 
 78 | ### Type-Specific Methods
 79 | 
 80 | #### Boolean Resolution
 81 | 
 82 | ```ts
 83 | client.getBooleanValue("my-flag", false);
 84 | ```
 85 | 
 86 | Returns the flag's enabled state. This is the most common use case for flags.
 87 | 
 88 | #### String Resolution
 89 | 
 90 | ```ts
 91 | client.getStringValue("my-flag", "default");
 92 | ```
 93 | 
 94 | Returns the flag's remote config key (also known as "variant"). Useful for multi-variate use cases.
 95 | 
 96 | #### Number Resolution
 97 | 
 98 | ```ts
 99 | client.getNumberValue("my-flag", 0);
100 | ```
101 | 
102 | Not directly supported by Reflag. Use `getObjectValue` instead for numeric configurations.
103 | 
104 | #### Object Resolution
105 | 
106 | ```ts
107 | // works for any type:
108 | client.getObjectValue("my-flag", { defaultValue: true });
109 | client.getObjectValue("my-flag", "string-value");
110 | client.getObjectValue("my-flag", 199);
111 | ```
112 | 
113 | Returns the flag's remote config payload with type validation. This is the most flexible method,
114 | allowing for complex configuration objects or simple types.
115 | 
116 | The object resolution performs runtime type checking between the default value and the flag payload to ensure type safety.
117 | 
118 | ## Context
119 | 
120 | To convert the OpenFeature context to a Reflag appropriate context
121 | pass a translation function along to the `ReflagBrowserProvider` constructor
122 | like so:
123 | 
124 | ```ts
125 | import { ReflagBrowserProvider } from "@reflag/openfeature-browser-provider";
126 | import { EvaluationContext, OpenFeature } from "@openfeature/web-sdk";
127 | 
128 | // initialize provider
129 | const publishableKey = "<your-reflag-publishable-key>";
130 | 
131 | // this converts the context to a Reflag compatible context
132 | // adapt it to fit your need
133 | const contextTranslator = (context?: EvaluationContext) => {
134 |   return {
135 |     user: {
136 |       id: context.targetingKey ?? context["userId"],
137 |       email: context["email"]?.toString(),
138 |       name: context["name"]?.toString(),
139 |       avatar: context["avatar"]?.toString(),
140 |       country: context["country"]?.toString(),
141 |     },
142 |     company: {
143 |       id: context["companyId"],
144 |       name: context["companyName"]?.toString(),
145 |       avatar: context["companyAvatar"]?.toString(),
146 |       plan: context["companyPlan"]?.toString(),
147 |     },
148 |   };
149 | };
150 | 
151 | const reflagOpenFeatureProvider = new ReflagBrowserProvider({
152 |   publishableKey,
153 |   contextTranslator,
154 | });
155 | ```
156 | 
157 | To update the context, call `OpenFeature.setContext(myNewContext);`
158 | 
159 | ```ts
160 | await OpenFeature.setContext({ userId: "my-key" });
161 | ```
162 | 
163 | ## Tracking flag usage
164 | 
165 | The Reflag OpenFeature Provider supports the OpenFeature tracking API
166 | natively.
167 | 
168 | ```ts
169 | import { ReflagBrowserProvider } from "@reflag/openfeature-browser-provider";
170 | import { OpenFeature } from "@openfeature/web-sdk";
171 | 
172 | // initialize provider
173 | const publishableKey = "<your-reflag-publishable-key>";
174 | 
175 | const reflagProvider = new ReflagBrowserProvider({ publishableKey });
176 | 
177 | // set OpenFeature provider and get client
178 | await OpenFeature.setProviderAndWait(reflagProvider);
179 | const client = OpenFeature.getClient();
180 | 
181 | // use client to send an event when user uses a flag
182 | client.track("huddles");
183 | ```
184 | 
185 | ## License
186 | 
187 | > MIT License
188 | > Copyright (c) 2025 Bucket ApS
189 | 
```

--------------------------------------------------------------------------------
/packages/openfeature-node-provider/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Reflag Node.js OpenFeature Provider
  2 | 
  3 | The official OpenFeature Node.js provider for [Reflag](https://reflag.com) feature management service.
  4 | 
  5 | ## Installation
  6 | 
  7 | ```shell
  8 | npm install @reflag/openfeature-node-provider
  9 | ```
 10 | 
 11 | ### Required peer dependencies
 12 | 
 13 | The OpenFeature SDK is required as peer dependency.
 14 | The minimum required version of `@openfeature/server-sdk` currently is `1.13.5`.
 15 | The minimum required version of `@reflag/node-sdk` currently is `2.0.0`.
 16 | 
 17 | ```shell
 18 | npm install @openfeature/server-sdk @reflag/node-sdk
 19 | ```
 20 | 
 21 | ## Migrating from Bucket OpenFeature SDK
 22 | 
 23 | If you have been using the Bucket SDKs, the following list will help you migrate to Reflag SDK:
 24 | 
 25 | - `Bucket*` classes, and types have been renamed to `Reflag*` (e.g. `BucketClient` is now `ReflagClient`)
 26 | - All environment variables that were prefixed with `BUCKET_` are now prefixed with `REFLAG_`
 27 | - The `BUCKET_HOST` environment variable and `host` option have been removed from `ReflagClient` constructor, use `REFLAG_API_BASE_URL` instead
 28 | - The `BUCKET_FEATURES_ENABLED` and `BUCKET_FEATURES_DISABLED` have been renamed to `REFLAG_FLAGS_ENABLED` and `REFLAG_FLAGS_DISABLED`
 29 | - The default configuration file has been renamed from `bucketConfig.json` to `reflag.config.json`
 30 | - The `fallbackFeatures` property in client constructor and configuration files has been renamed to `fallbackFlags`
 31 | - `featureKey` has been renamed to `flagKey` in all methods that accepts that argument
 32 | - The SDKs will not emit `evaluate` and `evaluate-config` events anymore
 33 | 
 34 | ## Usage
 35 | 
 36 | The provider uses the [Reflag Node.js SDK](https://docs.reflag.com/quickstart/supported-languages-frameworks/node.js-sdk).
 37 | The available options can be found in the [Reflag Node.js SDK](https://github.com/reflagcom/javascript/tree/main/packages/node-sdk#initialization-options).
 38 | 
 39 | ### Example using the default configuration
 40 | 
 41 | ```typescript
 42 | import { ReflagNodeProvider } from "@reflag/openfeature-node-provider";
 43 | import { OpenFeature } from "@openfeature/server-sdk";
 44 | 
 45 | const provider = new ReflagNodeProvider({ secretKey });
 46 | 
 47 | await OpenFeature.setProviderAndWait(provider);
 48 | 
 49 | // set a value to the global context
 50 | OpenFeature.setContext({ region: "us-east-1" });
 51 | 
 52 | // set a value to the invocation context
 53 | // this is merged with the global context
 54 | const requestContext = {
 55 |   targetingKey: req.user.id,
 56 |   email: req.user.email,
 57 |   companyPlan: req.locals.plan,
 58 | };
 59 | 
 60 | const client = OpenFeature.getClient();
 61 | 
 62 | const enterpriseFlagEnabled = await client.getBooleanValue(
 63 |   "enterpriseFlag",
 64 |   false,
 65 |   requestContext,
 66 | );
 67 | ```
 68 | 
 69 | ## Feature resolution methods
 70 | 
 71 | The Reflag OpenFeature Provider implements the OpenFeature evaluation interface for different value types. Each method handles the resolution of flags according to the OpenFeature specification.
 72 | 
 73 | ### Common behavior
 74 | 
 75 | All resolution methods share these behaviors:
 76 | 
 77 | - Return default value with `PROVIDER_NOT_READY` if client is not initialized,
 78 | - Return default value with `FLAG_NOT_FOUND` if flag doesn't exist,
 79 | - Return default value with `ERROR` if there was a type mismatch,
 80 | - Return evaluated value with `TARGETING_MATCH` on successful resolution.
 81 | 
 82 | ### Type-Specific Methods
 83 | 
 84 | #### Boolean Resolution
 85 | 
 86 | ```ts
 87 | client.getBooleanValue("my-flag", false);
 88 | ```
 89 | 
 90 | Returns the flags's enabled state. This is the most common use case for flags.
 91 | 
 92 | #### String Resolution
 93 | 
 94 | ```ts
 95 | client.getStringValue("my-flag", "default");
 96 | ```
 97 | 
 98 | Returns the flags's remote config key (also known as "variant"). Useful for multi-variate use cases.
 99 | 
100 | #### Number Resolution
101 | 
102 | ```ts
103 | client.getNumberValue("my-flag", 0);
104 | ```
105 | 
106 | Not directly supported by Reflag. Use `getObjectValue` instead for numeric configurations.
107 | 
108 | #### Object Resolution
109 | 
110 | ```ts
111 | // works for any type:
112 | client.getObjectValue("my-flag", { defaultValue: true });
113 | client.getObjectValue("my-flag", "string-value");
114 | client.getObjectValue("my-flag", 199);
115 | ```
116 | 
117 | Returns the flag's remote config payload with type validation. This is the most flexible method,
118 | allowing for complex configuration objects or simple types.
119 | 
120 | The object resolution performs runtime type checking between the default value and the flag payload to ensure type safety.
121 | 
122 | ## Translating Evaluation Context
123 | 
124 | Reflag uses a context object of the following shape:
125 | 
126 | ```ts
127 | /**
128 |  * Describes the current user context, company context, and other context.
129 |  * This is used to determine if flag targeting matches and to track events.
130 |  **/
131 | export type ReflagContext = {
132 |   /**
133 |    * The user context. If the user is set, the user ID is required.
134 |    */
135 |   user?: {
136 |     id: string;
137 |     name?: string;
138 |     email?: string;
139 |     avatar?: string;
140 |     [k: string]: any;
141 |   };
142 | 
143 |   /**
144 |    * The company context. If the company is set, the company ID is required.
145 |    */
146 |   company?: { id: string; name?: string; avatar?: string; [k: string]: any };
147 | 
148 |   /**
149 |    * The other context. This is used for any additional context that is not related to user or company.
150 |    */
151 |   other?: Record<string, any>;
152 | };
153 | ```
154 | 
155 | To use the Reflag Node.js OpenFeature provider, you must convert your OpenFeature contexts to Reflag contexts.
156 | You can achieve this by supplying a context translation function which takes the OpenFeature context and returns
157 | a corresponding Reflag Context:
158 | 
159 | ```ts
160 | import { ReflagNodeProvider } from "@openfeature/reflag-node-provider";
161 | 
162 | const contextTranslator = (context: EvaluationContext): ReflagContext => {
163 |   return {
164 |     user: {
165 |       id: context.targetingKey ?? context["userId"]?.toString(),
166 |       name: context["name"]?.toString(),
167 |       email: context["email"]?.toString(),
168 |       avatar: context["avatar"]?.toString(),
169 |       country: context["country"]?.toString(),
170 |     },
171 |     company: {
172 |       id: context["companyId"]?.toString(),
173 |       name: context["companyName"]?.toString(),
174 |       avatar: context["companyAvatar"]?.toString(),
175 |       plan: context["companyPlan"]?.toString(),
176 |     },
177 |   };
178 | };
179 | 
180 | const provider = new ReflagNodeProvider({ secretKey, contextTranslator });
181 | 
182 | OpenFeature.setProvider(provider);
183 | ```
184 | 
185 | ## Tracking feature adoption
186 | 
187 | The Reflag OpenFeature provider supports the OpenFeature Tracking API.
188 | It's straight forward to start sending tracking events through OpenFeature.
189 | 
190 | Simply call the "track" method on the OpenFeature client:
191 | 
192 | ```typescript
193 | import { ReflagNodeProvider } from "@reflag/openfeature-node-provider";
194 | import { OpenFeature } from "@openfeature/server-sdk";
195 | 
196 | const provider = new ReflagNodeProvider({ secretKey });
197 | 
198 | await OpenFeature.setProviderAndWait(provider);
199 | 
200 | const client = OpenFeature.getClient();
201 | 
202 | // `evaluationContext` is whatever you use to evaluate features based off
203 | const enterpriseFlagEnabled = await client.track("huddles", evaluationContext);
204 | ```
205 | 
206 | ## License
207 | 
208 | > MIT License
209 | > Copyright (c) 2025 Bucket ApS
210 | 
```

--------------------------------------------------------------------------------
/packages/cli/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Reflag CLI
  2 | 
  3 | Command-line interface for interacting with Reflag services. The CLI allows you to manage apps,
  4 | flags, authentication, and generate TypeScript types for your Reflag flags. With this tool,
  5 | you can streamline your flagging workflow directly from your terminal.
  6 | 
  7 | ## Installation
  8 | 
  9 | Install the CLI as a development dependency in your project:
 10 | 
 11 | ```bash
 12 | # npm
 13 | npm install --save-dev @reflag/cli
 14 | 
 15 | # yarn
 16 | yarn add --dev @reflag/cli
 17 | ```
 18 | 
 19 | Run the `new` command from your project's root directory to initialize the CLI, create a flag, and generate TypeScript types in one step:
 20 | 
 21 | ```bash
 22 | # npm
 23 | npx reflag new
 24 | 
 25 | # yarn
 26 | yarn reflag new
 27 | ```
 28 | 
 29 | ## Migrating from Bucket SDK
 30 | 
 31 | If you're migrating from the Bucket CLI, here are the key changes to be aware of:
 32 | 
 33 | - **Command name**: Changed from `bucket` to `reflag`
 34 | - **Type definitions file**: Renamed from `features.d.ts` to `flags.d.ts` (manually remove the old file if it was committed)
 35 | - **Authentication file**: Changed from `.bucket-auth` to `.reflag-auth` (rename or remove the old file)
 36 | - **Configuration file**: Changed from `bucket.config.json` to `reflag.config.json` (rename or remove the old file)
 37 | - **Command**: `features` command is now `flags`
 38 | - **Environment variable**: Use `REFLAG_API_KEY` instead of `BUCKET_API_KEY`
 39 | 
 40 | **Important**: Update your scripts, build steps, and `.gitignore` patterns to reflect these changes.
 41 | 
 42 | ### Individual Commands
 43 | 
 44 | For more control, you can run each command individually:
 45 | 
 46 | ```bash
 47 | # Initialize Reflag in your project (if not already setup)
 48 | npx reflag init
 49 | 
 50 | # Create a new flag
 51 | npx reflag flags create "My Flag"
 52 | 
 53 | # Generate TypeScript types for your flags
 54 | npx reflag flags types
 55 | ```
 56 | 
 57 | ## Configuration
 58 | 
 59 | The CLI creates a `reflag.config.json` file in your project directory when you run `reflag init`. This file contains all the necessary settings for your Reflag integration.
 60 | 
 61 | ### Configuration File Structure
 62 | 
 63 | Here are all the configuration options available in the `reflag.config.json` file:
 64 | 
 65 | ```json
 66 | {
 67 |   "$schema": "https://unpkg.com/@reflag/cli@latest/schema.json",
 68 |   "baseUrl": "https://app.reflag.com",
 69 |   "apiUrl": "https://app.reflag.com/api",
 70 |   "appId": "ap123456789",
 71 |   "typesOutput": [
 72 |     {
 73 |       "path": "gen/flags.d.ts",
 74 |       "format": "react"
 75 |     }
 76 |   ]
 77 | }
 78 | ```
 79 | 
 80 | | Option        | Description                                                                                                                                                          | Default                                            |
 81 | | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
 82 | | `$schema`     | Autocompletion for the config. `latest` can be replaced with a specific version.                                                                                     | "https://unpkg.com/@reflag/cli@latest/schema.json" |
 83 | | `baseUrl`     | Base URL for Reflag services.                                                                                                                                        | "https://app.reflag.com"                           |
 84 | | `apiUrl`      | API URL for Reflag services (overrides baseUrl for API calls).                                                                                                       | "https://app.reflag.com/api"                       |
 85 | | `appId`       | Your Reflag application ID.                                                                                                                                          | Required                                           |
 86 | | `typesOutput` | Path(s) where TypeScript types will be generated. Can be a string or an array of objects with `path` and `format` properties. Available formats: `react` and `node`. | "gen/flags.ts" with format "react"                 |
 87 | 
 88 | You can override these settings using command-line options for individual commands.
 89 | 
 90 | ## Commands
 91 | 
 92 | ### `reflag init`
 93 | 
 94 | Initialize a new Reflag configuration in your project. This creates a `reflag.config.json` file with your settings and prompts for any required information not provided via options.
 95 | 
 96 | ```bash
 97 | npx reflag init [--overwrite]
 98 | ```
 99 | 
100 | Options:
101 | 
102 | - `--overwrite`: Overwrite existing configuration file if one exists.
103 | - `--app-id <id>`: Set the application ID.
104 | - `--key-format <format>`: Set the key format for flags.
105 | 
106 | ### `reflag new [flagName]`
107 | 
108 | All-in-one command to get started quickly. This command combines `init`, flag creation, and type generation in a single step. Use this for the fastest way to get up and running with Reflag.
109 | 
110 | ```bash
111 | npx reflag new "My Flag" [--app-id ap123456789] [--key my-flag]  [--key-format custom] [--out gen/flags.ts] [--format react]
112 | ```
113 | 
114 | Options:
115 | 
116 | - `--key`: Specific key for the flag.
117 | - `--app-id`: App ID to use.
118 | - `--key-format`: Format for flag keys (custom, snake, camel, etc.).
119 | - `--out`: Path to generate TypeScript types.
120 | - `--format`: Format of the generated types (react or node).
121 | 
122 | If you prefer more control over each step, you can use the individual commands (`init`, `flags create`, `flags types`) instead.
123 | 
124 | ### `reflag login`
125 | 
126 | Authenticate with your Reflag account. This stores your credentials securely for subsequent operations.
127 | 
128 | ```bash
129 | npx reflag login
130 | ```
131 | 
132 | ### `reflag logout`
133 | 
134 | Sign out from your Reflag account and remove stored credentials.
135 | 
136 | ```bash
137 | npx reflag logout
138 | ```
139 | 
140 | ### `reflag flags`
141 | 
142 | Manage your Reflag flags with these subcommands:
143 | 
144 | #### `reflag flags create [flagName]`
145 | 
146 | Create a new flag in your Reflag app. The command guides you through the flag creation process with interactive prompts if options are not provided.
147 | 
148 | ```bash
149 | npx reflag flags create "My Flag" [--app-id ap123456789] [--key my-flag] [--key-format custom]
150 | ```
151 | 
152 | Options:
153 | 
154 | - `--key`: Specific key for the flag.
155 | - `--app-id`: App ID to use.
156 | - `--key-format`: Format for flag keys.
157 | 
158 | #### `reflag flags list`
159 | 
160 | List all flags for the current app. This helps you visualize what flags are available and their current configurations.
161 | 
162 | ```bash
163 | npx reflag flags list [--app-id ap123456789]
164 | ```
165 | 
166 | Options:
167 | 
168 | - `--app-id`: App ID to use.
169 | 
170 | #### `reflag flags types`
171 | 
172 | Generate TypeScript types for your flags. This ensures type safety when using Reflag flags in your TypeScript/JavaScript applications.
173 | 
174 | ```bash
175 | npx reflag flags types [--app-id ap123456789] [--out gen/flags.ts] [--format react]
176 | ```
177 | 
178 | Options:
179 | 
180 | - `--app-id`: App ID to use.
181 | - `--out`: Path to generate TypeScript types.
182 | - `--format`: Format of the generated types (react or node).
183 | 
184 | ### `reflag apps`
185 | 
186 | Commands for managing Reflag apps.
187 | 
188 | ## Global Options
189 | 
190 | These options can be used with any command:
191 | 
192 | - `--debug`: Enable debug mode for verbose output.
193 | - `--base-url <url>`: Set the base URL for Reflag API.
194 | - `--api-url <url>`: Set the API URL directly (overrides base URL).
195 | - `--api-key <key>`: Reflag API key for non-interactive authentication.
196 | - `--help`: Display help information for a command.
197 | 
198 | ## AI-Assisted Development
199 | 
200 | Reflag provides powerful AI-assisted development capabilities through rules and Model Context Protocol (MCP). These features help your AI development tools better understand your flags and provide more accurate assistance.
201 | 
202 | ### Reflag Rules (Recommended)
203 | 
204 | The `rules` command helps you set up AI-specific rules for your project. These rules enable AI tools to better understand how to work with Reflag flags and how they should be used in your codebase.
205 | 
206 | ```bash
207 | npx reflag rules [--format <cursor|copilot>] [--yes]
208 | ```
209 | 
210 | Options:
211 | 
212 | - `--format`: Format to add rules in:
213 |   - `cursor`: Adds rules to `.cursor/rules/reflag.mdc` for Cursor IDE integration.
214 |   - `copilot`: Adds rules to `.github/copilot-instructions.md` for GitHub Copilot integration.
215 | - `--yes`: Skip confirmation prompts and overwrite existing files without asking.
216 | 
217 | This command adds rules to your project that provide AI tools with context about how to set up and use Reflag flags. For the copilot format, the rules are added to a dedicated section in the file, allowing you to maintain other copilot instructions alongside Reflag's rules.
218 | 
219 | ## Model Context Protocol
220 | 
221 | The Model Context Protocol (MCP) is an open protocol that provides a standardized way to connect AI models to different data sources and tools. In the context of Reflag, MCP enables your code editor to understand your flags, their states, and their relationships within your codebase. This creates a seamless bridge between your flag management workflow and AI-powered development tools. The MCP server is hosted by Reflag, making it easy to get started.
222 | 
223 | _\*\*Note: The Reflag `mcp` CLI command was previously used for a \_local_ server. However, in recent versions of the Reflag CLI, the `mcp` command has been repurposed to help you connect to the new remote MCP server.\*\*\_
224 | 
225 | ### Setting up MCP
226 | 
227 | The `mcp` command helps you configure your editor or AI client to connect with Reflag's remote MCP server. This allows your AI tools to understand your flags and provide more contextual assistance.
228 | 
229 | ```bash
230 | npx reflag mcp [--editor <editor>] [--scope <local|global>]
231 | ```
232 | 
233 | Options:
234 | 
235 | - `--editor`: The editor/client to configure:
236 |   - `cursor`: [Cursor IDE](https://www.cursor.com/)
237 |   - `vscode`: [Visual Studio Code](https://code.visualstudio.com/)
238 |   - `claude`: [Claude Desktop](https://claude.ai/download)
239 |   - `windsurf`: [Windsurf](https://windsurf.com/editor)
240 | - `--scope`: Whether to configure settings globally or locally for the project.
241 | 
242 | The command will guide you through:
243 | 
244 | 1. Selecting which editor/client to configure.
245 | 2. Choosing which Reflag app to connect to.
246 | 3. Deciding between global or project-local configuration.
247 | 4. Setting up the appropriate configuration file for your chosen editor .
248 | 
249 | _**Note: The setup uses [mcp-remote](https://github.com/geelen/mcp-remote) as a compatibility layer allowing the remote hosted Reflag MCP server to work with all editors/clients that support MCP STDIO servers. If your editor/client supports HTTP Streaming with OAuth you can connect to the Reflag MCP server directly.**_
250 | 
251 | ## Using in CI/CD Pipelines (Beta)
252 | 
253 | The Reflag CLI is designed to work seamlessly in CI/CD pipelines. For automated environments where interactive login is not possible, use the `--api-key` option or specify the API key in the `REFLAG_API_KEY` environment variable.
254 | 
255 | ```bash
256 | # Generate types in CI/CD
257 | npx reflag apps list --api-key $REFLAG_API_KEY
258 | ```
259 | 
260 | **Important restrictions:**
261 | 
262 | - When using `--api-key`, the `login` and `logout` commands are disabled
263 | - API keys bypass all interactive authentication flows
264 | - API keys are bound to one app only. Commands such as `apps list` will only return the bound app
265 | - Store API keys securely using your CI/CD platform's secret management
266 | 
267 | Example CI workflow:
268 | 
269 | ```yaml
270 | # GitHub Actions example
271 | - name: Generate types
272 |   run: npx reflag flags types --api-key ${{ secrets.REFLAG_API_KEY }}
273 | 
274 | # GitHub Actions example (using environment):
275 | - name: Generate types (environment)
276 |   run: npx reflag flags types
277 |   env:
278 |     REFLAG_API_KEY: ${{ secrets.REFLAG_CI_API_KEY }}
279 | ```
280 | 
281 | ## Development
282 | 
283 | ```bash
284 | # Build the CLI
285 | yarn build
286 | 
287 | # Run the CLI locally
288 | yarn reflag [command]
289 | 
290 | # Lint and format code
291 | yarn lint
292 | yarn format
293 | ```
294 | 
295 | ## Requirements
296 | 
297 | - Node.js >=18.0.0
298 | 
299 | ## License
300 | 
301 | > MIT License
302 | > Copyright (c) 2025 Bucket ApS
303 | 
```

--------------------------------------------------------------------------------
/packages/vue-sdk/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Reflag Vue SDK (beta)
  2 | 
  3 | Vue client side library for [Reflag.com](https://reflag.com)
  4 | 
  5 | Reflag supports flag toggling, tracking flag usage, requesting feedback on features and remotely configuring flags.
  6 | 
  7 | The Reflag Vue SDK comes with the same built-in toolbar as the browser SDK which appears on `localhost` by default.
  8 | 
  9 | ## Install
 10 | 
 11 | Install via npm:
 12 | 
 13 | ```shell
 14 | npm i @reflag/vue-sdk
 15 | ```
 16 | 
 17 | ## Migrating from Bucket SDK
 18 | 
 19 | If you have been using the Bucket SDKs, the following list will help you migrate to Reflag SDK:
 20 | 
 21 | - `Bucket*` classes, and types have been renamed to `Reflag*` (e.g. `BucketClient` is now `ReflagClient`)
 22 | - `Feature*` classes, and types have been renamed to `Feature*` (e.g. `Feature` is now `Flag`, `RawFeatures` is now `RawFlags`)
 23 | - All methods that contained `feature` in the name have been renamed to use the `flag` terminology (e.g. `getFeature` is `getFlag`)
 24 | - The `fallbackFeatures` property in client constructor and configuration files has been renamed to `fallbackFlags`
 25 | - `featureKey` has been renamed to `flagKey` in all methods that accepts that argument
 26 | - The SDKs will not emit `evaluate` and `evaluate-config` events anymore
 27 | - The new cookies that are stored in the client's browser are now `reflag-*` prefixed instead og `bucket-*`
 28 | - The `featuresUpdated` hook has been renamed to `flagsUpdated`
 29 | - The `checkIsEnabled` and `checkConfig` hooks have been removed, use `check` from now on
 30 | 
 31 | To ease in transition to Reflag SDK, some of the old methods have been preserved as aliases to the new methods:
 32 | 
 33 | - `getFeature` method is an alias for `getFlag`
 34 | - `getFeatures` method is an alias for `getFlags`
 35 | - `featuresUpdated` hook is an alias for `flagsUpdated`
 36 | 
 37 | If you are running with strict Content Security Policies active on your website, you will need change them as follows:
 38 | 
 39 | - `connect-src https://front.bucket.co` to `connect-src https://front.reflag.com`
 40 | 
 41 | Finally, if you have customized the look & feel of the Feedback component, update `--bucket-feedback-*` CSS classes to `--reflag-feedback-*`
 42 | 
 43 | ## Get started
 44 | 
 45 | ### 1. Add the `ReflagProvider` context provider
 46 | 
 47 | Add the `ReflagProvider` context provider to your application:
 48 | 
 49 | **Example:**
 50 | 
 51 | ```vue
 52 | <script setup lang="ts">
 53 | import { ReflagProvider } from "@reflag/vue-sdk";
 54 | </script>
 55 | 
 56 | <ReflagProvider
 57 |   :publishable-key="publishableKey"
 58 |   :context="{
 59 |     user: { id: 'user_123', name: 'John Doe', email: '[email protected]' },
 60 |     company: { id: 'acme_inc', plan: 'pro' },
 61 |   }"
 62 | >
 63 |   <!-- your app -->
 64 | </ReflagProvider>
 65 | ```
 66 | 
 67 | If using Nuxt, wrap `<ReflagProvider>` in `<ClientOnly>`. `<ReflagProvider>` only renders client-side currently.
 68 | 
 69 | ### 2. Use `useFlag get flag status
 70 | 
 71 | ```vue
 72 | <script setup lang="ts">
 73 | import { useFlag } from "@reflag/vue-sdk";
 74 | 
 75 | const { isEnabled } = useFlag("huddles");
 76 | </script>
 77 | 
 78 | <template>
 79 |   <div v-if="isEnabled">
 80 |     <button>Start huddles!</button>
 81 |   </div>
 82 | </template>
 83 | ```
 84 | 
 85 | See [useFlag()](#useflag) for a full example
 86 | 
 87 | ## Setting context
 88 | 
 89 | Reflag determines which flags are active for a given `user`, `company`, or `other` context.
 90 | You can pass these to the `ReflagProvider` using the `context` prop.
 91 | 
 92 | ### Using the `context` prop
 93 | 
 94 | ```vue
 95 | <ReflagProvider
 96 |   :publishable-key="publishableKey"
 97 |   :context="{
 98 |     user: { id: 'user_123', name: 'John Doe', email: '[email protected]' },
 99 |     company: { id: 'acme_inc', plan: 'pro' },
100 |     other: { source: 'web' },
101 |   }"
102 | >
103 |   <!-- your app -->
104 | </ReflagProvider>
105 | ```
106 | 
107 | ### Legacy individual props (deprecated)
108 | 
109 | For backward compatibility, you can still use individual props, but these are deprecated and will be removed in the next major version:
110 | 
111 | ```vue
112 | <ReflagProvider
113 |   :publishable-key="publishableKey"
114 |   :user="{ id: 'user_123', name: 'John Doe', email: '[email protected]' }"
115 |   :company="{ id: 'acme_inc', plan: 'pro' }"
116 |   :other-context="{ source: 'web' }"
117 | >
118 |   <!-- your app -->
119 | </ReflagProvider>
120 | ```
121 | 
122 | > [!Important]
123 | > The `user`, `company`, and `otherContext` props are deprecated. Use the `context` prop instead, which provides the same functionality in a more structured way.
124 | 
125 | ### Context requirements
126 | 
127 | If you supply `user` or `company` objects, they must include at least the `id` property otherwise they will be ignored in their entirety.
128 | In addition to the `id`, you must also supply anything additional that you want to be able to evaluate flag targeting rules against.
129 | Attributes which are not properties of the `user` or `company` can be supplied using the `other` property.
130 | 
131 | Attributes cannot be nested (multiple levels) and must be either strings, numbers or booleans.
132 | A number of special attributes exist:
133 | 
134 | - `name` -- display name for `user`/`company`,
135 | - `email` -- the email of the user,
136 | - `avatar` -- the URL for `user`/`company` avatar image.
137 | 
138 | To retrieve flags along with their targeting information, use `useFlag(key: string)` hook (described in a section below).
139 | 
140 | Note that accessing `isEnabled` on the object returned by `useFlag()` automatically
141 | generates a `check` event.
142 | 
143 | ## Remote config
144 | 
145 | Remote config is a dynamic and flexible approach to configuring flag behavior outside of your app – without needing to re-deploy it.
146 | 
147 | Similar to `isEnabled`, each flag accessed using the `useFlag()` hook, has a `config` property. This configuration is managed from within Reflag. It is managed similar to the way access to flags is managed, but instead of the
148 | binary `isEnabled` you can have multiple configuration values which are given to different user/companies.
149 | 
150 | ### Get started with Remote config
151 | 
152 | ```ts
153 | const {
154 |   isEnabled,
155 |   config: { key, payload },
156 | } = useFlag("huddles");
157 | 
158 | // isEnabled: true,
159 | // key: "gpt-3.5",
160 | // payload: { maxTokens: 10000, model: "gpt-3.5-beta1" }
161 | ```
162 | 
163 | `key` is mandatory for a config, but if a flag has no config or no config value was matched against the context, the `key` will be `undefined`. Make sure to check against this case when trying to use the configuration in your application. `payload` is an optional JSON value for arbitrary configuration needs.
164 | 
165 | Note that, similar to `isEnabled`, accessing `config` on the object returned by `useFlag()` automatically
166 | generates a `check` event.
167 | 
168 | ## `<ReflagProvider>` component
169 | 
170 | The `<ReflagProvider>` initializes the Reflag SDK, fetches flags and starts listening for automated feedback survey events. The component can be configured using a number of props:
171 | 
172 | - `publishableKey` is used to connect the provider to an _environment_ on Reflag. Find your `publishableKey` under [environment settings](https://app.reflag.com/env-current/settings/app-environments) in Reflag,
173 | - `context`: An object containing `user`, `company`, and `other` properties that make up the evaluation context used to determine if a flag is enabled or not. `company` and `user` contexts are automatically transmitted to Reflag servers so the Reflag app can show you which companies have access to which flags etc.
174 | - `company`, `user` and `otherContext` (deprecated): Individual props for context. These are deprecated in favor of the `context` prop and will be removed in the next major version.
175 | 
176 |   > [!Note]
177 |   > If you specify `company` and/or `user` they must have at least the `id` property, otherwise they will be ignored in their entirety. You should also supply anything additional you want to be able to evaluate flag targeting against,
178 | 
179 | - `timeoutMs`: Timeout in milliseconds when fetching flags from the server,
180 | - `staleWhileRevalidate`: If set to `true`, stale flags will be returned while refetching flags in the background,
181 | - `expireTimeMs`: If set, flags will be cached between page loads for this duration (in milliseconds),
182 | - `staleTimeMs`: Maximum time (in milliseconds) that stale flags will be returned if `staleWhileRevalidate` is true and new flags cannot be fetched.
183 | 
184 | - `enableTracking`: Set to `false` to stop sending tracking events and user/company updates to Reflag. Useful when you're impersonating a user (defaults to `true`),
185 | - `apiBaseUrl`: Optional base URL for the Reflag API. Use this to override the default API endpoint,
186 | - `appBaseUrl`: Optional base URL for the Reflag application. Use this to override the default app URL,
187 | - `sseBaseUrl`: Optional base URL for Server-Sent Events. Use this to override the default SSE endpoint,
188 | - `debug`: Set to `true` to enable debug logging to the console,
189 | - `toolbar`: Optional [configuration](https://docs.reflag.com/supported-languages/browser-sdk/globals#toolbaroptions) for the Reflag toolbar,
190 | - `feedback`: Optional configuration for feedback collection
191 | 
192 | ### Loading states
193 | 
194 | ReflagProvider lets you define a template to be shown while ReflagProvider is initializing:
195 | 
196 | ```vue
197 | <template>
198 |   <ReflagProvider
199 |     :publishable-key="publishableKey"
200 |     :user="user"
201 |     :company="{ id: 'acme_inc', plan: 'pro' }"
202 |   >
203 |     <template #loading>Loading...</template>
204 |     <StartHuddlesButton />
205 |   </ReflagProvider>
206 | </template>
207 | ```
208 | 
209 | If you want more control over loading screens, `useIsLoading()` returns a `Ref<boolean>` which you can use to customize the loading experience.
210 | 
211 | ## `<ReflagBootstrappedProvider>` component
212 | 
213 | The `<ReflagBootstrappedProvider>` component is a specialized version of `ReflagProvider` designed for server-side rendering and preloaded flag scenarios. Instead of fetching flags on initialization, it uses pre-fetched flags, resulting in faster initial page loads and better SSR compatibility.
214 | 
215 | ### Usage
216 | 
217 | ```vue
218 | <script setup lang="ts">
219 | import { ReflagBootstrappedProvider } from "@reflag/vue-sdk";
220 | 
221 | // Pre-fetched flags (typically from your server/SSR layer)
222 | const bootstrappedFlags = {
223 |   context: {
224 |     user: { id: "user123", name: "John Doe", email: "[email protected]" },
225 |     company: { id: "company456", name: "Acme Inc", plan: "enterprise" },
226 |   },
227 |   flags: {
228 |     huddles: {
229 |       isEnabled: true,
230 |       config: {
231 |         key: "enhanced",
232 |         payload: { maxParticipants: 50, videoQuality: "hd" },
233 |       },
234 |     },
235 |   },
236 | };
237 | </script>
238 | 
239 | <template>
240 |   <ReflagBootstrappedProvider
241 |     :publishable-key="publishableKey"
242 |     :flags="bootstrappedFlags"
243 |   >
244 |     <StartHuddlesButton />
245 |   </ReflagBootstrappedProvider>
246 | </template>
247 | ```
248 | 
249 | ### Getting bootstrapped flags
250 | 
251 | You'll typically generate the `bootstrappedFlags` object on your server using the Node.js SDK or by fetching from the Reflag API. Here's an example using the Node.js SDK:
252 | 
253 | ```js
254 | // server.js (Node.js/SSR)
255 | import { ReflagClient } from "@reflag/node-sdk";
256 | 
257 | const client = new ReflagClient({
258 |   secretKey: "your-secret-key", // Use secret key on server
259 | });
260 | await client.initialize();
261 | 
262 | // Fetch flags for specific context
263 | const context = {
264 |   user: { id: "user123", name: "John Doe", email: "[email protected]" },
265 |   company: { id: "company456", name: "Acme Inc", plan: "enterprise" },
266 | };
267 | 
268 | const bootstrappedFlags = client.getFlagsForBootstrap(context);
269 | 
270 | // Pass to your Vue app
271 | ```
272 | 
273 | ### ReflagBootstrappedProvider Props
274 | 
275 | `ReflagBootstrappedProvider` accepts all the same props as `ReflagProvider` except:
276 | 
277 | - `flags`: The pre-fetched flags object containing context and flag data
278 | - All other props available in `ReflagProvider` are supported except `context`, `user`, `company`, and `otherContext` (which are extracted from `flags.context`)
279 | 
280 | If the `flags` prop is not provided or is undefined, the provider will not initialize the client and will render in a non-loading state.
281 | 
282 | ## `<ReflagClientProvider>` component
283 | 
284 | The `<ReflagClientProvider>` is a lower-level component that accepts a pre-initialized `ReflagClient` instance. This is useful for advanced use cases where you need full control over client initialization or want to share a client instance across multiple parts of your application.
285 | 
286 | ### ReflagClientProvider Usage
287 | 
288 | ```vue
289 | <script setup lang="ts">
290 | import { ReflagClient } from "@reflag/browser-sdk";
291 | import { ReflagClientProvider } from "@reflag/vue-sdk";
292 | 
293 | // Initialize the client yourself
294 | const client = new ReflagClient({
295 |   publishableKey: "your-publishable-key",
296 |   user: { id: "user123", name: "John Doe" },
297 |   company: { id: "company456", name: "Acme Inc" },
298 |   // ... other configuration options
299 | });
300 | 
301 | // Initialize the client
302 | await client.initialize();
303 | </script>
304 | 
305 | <template>
306 |   <ReflagClientProvider :client="client">
307 |     <template #loading>Loading...</template>
308 |     <Router />
309 |   </ReflagClientProvider>
310 | </template>
311 | ```
312 | 
313 | ### ReflagClientProvider Props
314 | 
315 | The `ReflagClientProvider` accepts the following props:
316 | 
317 | - `client`: A pre-initialized `ReflagClient` instance
318 | 
319 | ### Slots
320 | 
321 | - `loading`: Optional slot to show while the client is initializing (same as `ReflagProvider`)
322 | 
323 | > [!Note]
324 | > Most applications should use `ReflagProvider` or `ReflagBootstrappedProvider` instead of `ReflagClientProvider`. Only use this component when you need the advanced control it provides.
325 | 
326 | ## Hooks
327 | 
328 | ### `useFlag()`
329 | 
330 | Returns the state of a given flag for the current context. The composable provides access to flags and their configurations.
331 | 
332 | `useFlag()` returns an object with this shape:
333 | 
334 | ```ts
335 | {
336 |   isEnabled: boolean, // is the flag enabled
337 |   track: () => void, // send a track event when the flag is used
338 |   requestFeedback: (...) => void // open up a feedback dialog
339 |   config: {key: string, payload: any},  // remote configuration for this flag
340 |   isLoading: boolean // if you want to manage loading state at the flag level
341 | }
342 | ```
343 | 
344 | Example:
345 | 
346 | ```vue
347 | <script setup lang="ts">
348 | import { useFlag } from "@reflag/vue-sdk";
349 | 
350 | const { isEnabled, track, requestFeedback, config } = useFlag("huddles");
351 | </script>
352 | 
353 | <template>
354 |   <div v-if="isLoading">Loading...</div>
355 |   <div v-else-if="!isEnabled">Flag not available</div>
356 |   <div v-else>
357 |     <button @click="track()">Start huddles!</button>
358 |     <button
359 |       @click="
360 |         (e) =>
361 |           requestFeedback({
362 |             title:
363 |               config.payload?.question ??
364 |               'How do you like the Huddles feature?',
365 |             position: {
366 |               type: 'POPOVER',
367 |               anchor: e.currentTarget as HTMLElement,
368 |             },
369 |           })
370 |       "
371 |     >
372 |       Give feedback!
373 |     </button>
374 |   </div>
375 | </template>
376 | ```
377 | 
378 | See the reference docs for details.
379 | 
380 | ### `useTrack()`
381 | 
382 | `useTrack()` returns a function which lets you send custom events to Reflag. It takes a string argument with the event name and optionally an object with properties to attach the event.
383 | 
384 | Using `track` returned from `useFlag()` calls this track function with the flag key as the event name.
385 | 
386 | ```vue
387 | <script setup lang="ts">
388 | import { useTrack } from "@reflag/vue-sdk";
389 | 
390 | const track = useTrack();
391 | </script>
392 | 
393 | <template>
394 |   <div>
395 |     <button @click="track('Huddles Started', { huddlesType: 'voice' })">
396 |       Start voice huddles!
397 |     </button>
398 |   </div>
399 | </template>
400 | ```
401 | 
402 | ### `useRequestFeedback()`
403 | 
404 | Returns a function that lets you open up a dialog to ask for feedback on a specific feature. This is useful for collecting targeted feedback about specific features.
405 | 
406 | See [Automated Feedback Surveys](https://docs.reflag.com/product-handbook/live-satisfaction) for how to do this automatically, without code.
407 | 
408 | When using the `useRequestFeedback` you must pass the flag key to `requestFeedback`.
409 | The example below shows how to use `position` to ensure the popover appears next to the "Give feedback!" button.
410 | 
411 | ```vue
412 | <script setup lang="ts">
413 | import { useRequestFeedback } from "@reflag/vue-sdk";
414 | 
415 | const requestFeedback = useRequestFeedback();
416 | </script>
417 | 
418 | <template>
419 |   <button
420 |     @click="
421 |       (e) =>
422 |         requestFeedback({
423 |           flagKey: 'huddles',
424 |           title: 'How satisfied are you with file uploads?',
425 |           position: {
426 |             type: 'POPOVER',
427 |             anchor: e.currentTarget as HTMLElement,
428 |           },
429 |           // Optional custom styling
430 |           style: {
431 |             theme: 'light',
432 |             primaryColor: '#007AFF',
433 |           },
434 |         })
435 |     "
436 |   >
437 |     Give feedback!
438 |   </button>
439 | </template>
440 | ```
441 | 
442 | See the [Feedback Documentation](https://github.com/reflagcom/javascript/blob/main/packages/browser-sdk/FEEDBACK.md#manual-feedback-collection) for more information on `requestFeedback` options.
443 | 
444 | ### `useSendFeedback()`
445 | 
446 | Returns a function that lets you send feedback to Reflag. This is useful if you've manually collected feedback through your own UI and want to send it to Reflag.
447 | 
448 | ```vue
449 | <script setup lang="ts">
450 | import { useSendFeedback } from "@reflag/vue-sdk";
451 | 
452 | const sendFeedback = useSendFeedback();
453 | 
454 | const handleSubmit = async (data: FormData) => {
455 |   await sendFeedback({
456 |     flagKey: "reflag-flag-key",
457 |     score: parseInt(data.get("score") as string),
458 |     comment: data.get("comment") as string,
459 |   });
460 | };
461 | </script>
462 | 
463 | <template>
464 |   <form @submit="handleSubmit">
465 |     <!-- form content -->
466 |   </form>
467 | </template>
468 | ```
469 | 
470 | ### `useUpdateUser()`, `useUpdateCompany()` and `useUpdateOtherContext()`
471 | 
472 | These composables return functions that let you update the attributes for the currently set user, company, or other context. Updates to user/company are stored remotely and affect flag targeting, while "other" context updates only affect the current session.
473 | 
474 | ```vue
475 | <script setup lang="ts">
476 | import {
477 |   useUpdateUser,
478 |   useUpdateCompany,
479 |   useUpdateOtherContext,
480 | } from "@reflag/vue-sdk";
481 | 
482 | const updateUser = useUpdateUser();
483 | const updateCompany = useUpdateCompany();
484 | const updateOtherContext = useUpdateOtherContext();
485 | 
486 | const handleUserUpdate = async () => {
487 |   await updateUser({
488 |     role: "admin",
489 |     betaFeatures: "enabled",
490 |   });
491 | };
492 | 
493 | const handleCompanyUpdate = async () => {
494 |   await updateCompany({
495 |     plan: "enterprise",
496 |     employees: 500,
497 |   });
498 | };
499 | 
500 | const handleContextUpdate = async () => {
501 |   await updateOtherContext({
502 |     currentWorkspace: "workspace-123",
503 |     theme: "dark",
504 |   });
505 | };
506 | </script>
507 | 
508 | <template>
509 |   <div>
510 |     <button @click="handleUserUpdate">Update User</button>
511 |     <button @click="handleCompanyUpdate">Update Company</button>
512 |     <button @click="handleContextUpdate">Update Context</button>
513 |   </div>
514 | </template>
515 | ```
516 | 
517 | Note: To change the `user.id` or `company.id`, you need to update the props passed to `ReflagProvider` instead of using these composables.
518 | 
519 | ### `useClient()`
520 | 
521 | Returns the `ReflagClient` used by the `ReflagProvider`. The client offers more functionality that
522 | is not directly accessible through the other composables.
523 | 
524 | ```vue
525 | <script setup>
526 | import { useClient } from "@reflag/vue-sdk";
527 | import { onMounted } from "vue";
528 | 
529 | const client = useClient();
530 | 
531 | console.log(client.getContext());
532 | </script>
533 | 
534 | <template>
535 |   <!-- your component content -->
536 | </template>
537 | ```
538 | 
539 | ### `useIsLoading()`
540 | 
541 | Returns a `Ref<boolean>` to indicate if Reflag has finished loading.
542 | Initially, the value will be `true` if no bootstrap flags have been provided and the client has not be initialized.
543 | 
544 | ```vue
545 | <script setup>
546 | import { useIsLoading } from "@reflag/vue-sdk";
547 | import { Spinner } from "./Spinner";
548 | 
549 | const isLoading = useIsLoading();
550 | </script>
551 | 
552 | <template>
553 |   <!-- your component content -->
554 | </template>
555 | ```
556 | 
557 | ### `useOnEvent()`
558 | 
559 | Vue composable for listening to Reflag client events. This composable automatically handles mounting and unmounting of event listeners.
560 | 
561 | Available events include:
562 | 
563 | - `flagsUpdated`: Triggered when flags are updated
564 | - `track`: Triggered when tracking events are sent
565 | - `feedback`: Triggered when feedback is sent
566 | 
567 | ```vue
568 | <script setup lang="ts">
569 | import { useOnEvent } from "@reflag/vue-sdk";
570 | 
571 | // Listen to flag updates
572 | useOnEvent("flagsUpdated", () => {
573 |   console.log("Flags have been updated");
574 | });
575 | </script>
576 | 
577 | <template>
578 |   <!-- your component content -->
579 | </template>
580 | ```
581 | 
582 | You can also provide a specific client instance if needed:
583 | 
584 | ```vue
585 | <script setup lang="ts">
586 | import { ReflagClient } from "@reflag/browser-sdk";
587 | 
588 | const myReflagClient = new ReflagClient();
589 | 
590 | useOnEvent(
591 |   "flagsUpdated",
592 |   () => {
593 |     console.log("flags updated");
594 |   },
595 |   myReflagClient,
596 | );
597 | </script>
598 | 
599 | <template>
600 |   <!-- your component content -->
601 | </template>
602 | ```
603 | 
604 | ## Content Security Policy (CSP)
605 | 
606 | See [CSP](https://github.com/reflagcom/javascript/blob/main/packages/browser-sdk/README.md#content-security-policy-csp) for info on using Reflag React SDK with CSP
607 | 
608 | ## License
609 | 
610 | MIT License
611 | 
612 | Copyright (c) 2025 Bucket ApS
613 | 
```

--------------------------------------------------------------------------------
/packages/browser-sdk/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Reflag Browser SDK
  2 | 
  3 | Basic client for [Reflag.com](https://reflag.com). If you're using React, you'll be better off with the Reflag React SDK.
  4 | 
  5 | Reflag supports flag toggling, tracking flag usage, [collecting feedback](#qualitative-feedback-on-beta-flags) on flags, and [remotely configuring flags](#remote-config).
  6 | 
  7 | ## Install
  8 | 
  9 | First find your `publishableKey` under [environment settings](https://app.reflag.com/env-current/settings/app-environments) in Reflag.
 10 | 
 11 | The package can be imported or used directly in a HTML script tag:
 12 | 
 13 | A. Import module:
 14 | 
 15 | ```typescript
 16 | import { ReflagClient } from "@reflag/browser-sdk";
 17 | 
 18 | const user = {
 19 |   id: 42,
 20 |   role: "manager",
 21 | };
 22 | 
 23 | const company = {
 24 |   id: 99,
 25 |   plan: "enterprise",
 26 | };
 27 | 
 28 | const reflagClient = new ReflagClient({ publishableKey, user, company });
 29 | 
 30 | await reflagClient.initialize();
 31 | 
 32 | const {
 33 |   isEnabled,
 34 |   config: { payload: question },
 35 |   track,
 36 |   requestFeedback,
 37 | } = reflagClient.getFlag("huddle");
 38 | 
 39 | if (isEnabled) {
 40 |   // Show flag. When retrieving `isEnabled` the client automatically
 41 |   // sends a "check" event for the "huddle" flag which is shown in the
 42 |   // Reflag UI.
 43 | 
 44 |   // On usage, call `track` to let Reflag know that a user interacted with the flag
 45 |   track();
 46 | 
 47 |   // The `payload` is a user-supplied JSON in Reflag that is dynamically picked
 48 |   // out depending on the user/company.
 49 |   const question = payload?.question ?? "Tell us what you think of Huddles";
 50 | 
 51 |   // Use `requestFeedback` to create "Send feedback" buttons easily for specific
 52 |   // flags. This is not related to `track` and you can call them individually.
 53 |   requestFeedback({ title: question });
 54 | }
 55 | 
 56 | // `track` just calls `reflagClient.track(<flagKey>)` to send an event using the same flag key
 57 | // You can also use `track` on the client directly to send any custom event.
 58 | reflagClient.track("huddle");
 59 | 
 60 | // similarly, `requestFeedback` just calls `reflagClient.requestFeedback({flagKey: <flagKey>})`
 61 | // which you can also call directly:
 62 | reflagClient.requestFeedback({ flagKey: "huddle" });
 63 | ```
 64 | 
 65 | B. Script tag (client-side directly in html)
 66 | 
 67 | See [example/browser.html](https://github.com/reflagcom/javascript/tree/main/packages/browser-sdk/example/browser.html) for a working example:
 68 | 
 69 | ```html
 70 | <script src="https://cdn.jsdelivr.net/npm/@reflag/browser-sdk@2"></script>
 71 | <script>
 72 |   const reflag = new ReflagBrowserSDK.ReflagClient({
 73 |     publishableKey: "publishableKey",
 74 |     user: { id: "42" },
 75 |     company: { id: "1" },
 76 |   });
 77 | 
 78 |   reflag.initialize().then(() => {
 79 |     console.log("Reflag initialized");
 80 |     document.getElementById("loading").style.display = "none";
 81 |     document.getElementById("start-huddle").style.display = "block";
 82 |   });
 83 | </script>
 84 | <span id="loading">Loading...</span>
 85 | <button
 86 |   id="start-huddle"
 87 |   style="display: none"
 88 |   onClick="reflag.track('Started huddle')"
 89 | >
 90 |   Click me
 91 | </button>
 92 | ```
 93 | 
 94 | ### Init options
 95 | 
 96 | Supply these to the constructor call:
 97 | 
 98 | ```typescript
 99 | type Configuration = {
100 |   logger: console; // by default only logs warn/error, by passing `console` you'll log everything
101 |   apiBaseUrl?: "https://front.reflag.com";
102 |   sseBaseUrl?: "https://livemessaging.bucket.co";
103 |   feedback?: undefined; // See FEEDBACK.md
104 |   enableTracking?: true; // set to `false` to stop sending track events and user/company updates to Reflag servers. Useful when you're impersonating a user
105 |   fallbackFlags?:
106 |     | string[]
107 |     | Record<string, { key: string; payload: any } | true>; // Enable these flags if unable to contact reflag.com. Can be a list of flag keys or a record with configuration values
108 |   timeoutMs?: number; // Timeout for fetching flags (default: 5000ms)
109 |   staleWhileRevalidate?: boolean; // Revalidate in the background when cached flags turn stale to avoid latency in the UI (default: false)
110 |   staleTimeMs?: number; // at initialization time flags are loaded from the cache unless they have gone stale. Defaults to 0 which means the cache is disabled. Increase this in the case of a non-SPA
111 |   expireTimeMs?: number; // In case we're unable to fetch flags from Reflag, cached/stale flags will be used instead until they expire after `expireTimeMs`. Default is 30 days
112 |   offline?: boolean; // Use the SDK in offline mode. Offline mode is useful during testing and local development
113 | };
114 | ```
115 | 
116 | ## Migrating from Bucket SDK
117 | 
118 | If you have been using the Bucket SDKs, the following list will help you migrate to Reflag SDK:
119 | 
120 | - `Bucket*` classes, and types have been renamed to `Reflag*` (e.g. `BucketClient` is now `ReflagClient`)
121 | - `Feature*` classes, and types have been renamed to `Feature*` (e.g. `Feature` is now `Flag`, `RawFeatures` is now `RawFlags`)
122 | - All methods that contained `feature` in the name have been renamed to use the `flag` terminology (e.g. `getFeature` is `getFlag`)
123 | - The `fallbackFeatures` property in client constructor and configuration files has been renamed to `fallbackFlags`
124 | - `featureKey` has been renamed to `flagKey` in all methods that accepts that argument
125 | - The new cookies that are stored in the client's browser are now `reflag-*` prefixed instead og `bucket-*`
126 | - The `featuresUpdated` hook has been renamed to `flagsUpdated`
127 | - The `checkIsEnabled` and `checkConfig` hooks have been removed, use `check` from now on
128 | 
129 | To ease in transition to Reflag SDK, some of the old methods have been preserved as aliases to the new methods:
130 | 
131 | - `getFeature` method is an alias for `getFlag`
132 | - `getFeatures` method is an alias for `getFlags`
133 | - `featuresUpdated` hook is an alias for `flagsUpdated`
134 | 
135 | If you are running with strict Content Security Policies active on your website, you will need change them as follows:
136 | 
137 | - `connect-src https://front.bucket.co` to `connect-src https://front.reflag.com`
138 | 
139 | Finally, if you have customized the look & feel of the Feedback component, update `--bucket-feedback-*` CSS classes to `--reflag-feedback-*`
140 | 
141 | ## Flag toggles
142 | 
143 | Reflag determines which flags are active for a given user/company. The user/company is given in the ReflagClient constructor.
144 | 
145 | If you supply `user` or `company` objects, they must include at least the `id` property otherwise they will be ignored in their entirety.
146 | In addition to the `id`, you must also supply anything additional that you want to be able to evaluate flag targeting rules against.
147 | 
148 | Attributes cannot be nested (multiple levels) and must be either strings, integers or booleans.
149 | Some attributes are special and used in Reflag UI:
150 | 
151 | - `name` -- display name for `user`/`company`,
152 | - `email` -- is accepted for `user`s and will be highlighted in the Reflag UI if available,
153 | - `avatar` -- can be provided for both `user` and `company` and should be an URL to an image.
154 | 
155 | ```ts
156 | const reflagClient = new ReflagClient({
157 |   publishableKey,
158 |   user: {
159 |     id: "user_123",
160 |     name: "John Doe",
161 |     email: "[email protected]"
162 |     avatar: "https://example.com/images/udsy6363"
163 |   },
164 |   company: {
165 |     id: "company_123",
166 |     name: "Acme, Inc",
167 |     avatar: "https://example.com/images/31232ds"
168 |   },
169 | });
170 | ```
171 | 
172 | To retrieve flags along with their targeting information, use `getFlag(key: string)`:
173 | 
174 | ```ts
175 | const huddle = reflagClient.getFlag("huddle");
176 | // {
177 | //   isEnabled: true,
178 | //   config: { key: "zoom", payload: { ... } },
179 | //   track: () => Promise<Response>
180 | //   requestFeedback: (options: RequestFeedbackData) => void
181 | // }
182 | ```
183 | 
184 | You can use `getFlags()` to retrieve all enabled flags currently.
185 | 
186 | ```ts
187 | const flags = reflagClient.getFlags();
188 | // {
189 | //   huddle: {
190 | //     isEnabled: true,
191 | //     targetingVersion: 42,
192 | //     config: ...
193 | //   }
194 | // }
195 | ```
196 | 
197 | `getFlags()` is meant to be more low-level than `getFlag()` and it typically used
198 | by down-stream clients, like the React SDK.
199 | 
200 | Note that accessing `isEnabled` on the object returned by `getFlags` does not automatically
201 | generate a `check` event, contrary to the `isEnabled` property on the object returned by `getFlag`.
202 | 
203 | ## Remote config
204 | 
205 | Remote config is a dynamic and flexible approach to configuring flag behavior outside of your app – without needing to re-deploy it.
206 | 
207 | Similar to `isEnabled`, each flag has a `config` property. This configuration is managed from within Reflag.
208 | It is managed similar to the way access to flags is managed, but instead of the binary `isEnabled` you can have
209 | multiple configuration values which are given to different user/companies.
210 | 
211 | ```ts
212 | const flags = reflagClient.getFlags();
213 | // {
214 | //   huddle: {
215 | //     isEnabled: true,
216 | //     targetingVersion: 42,
217 | //     config: {
218 | //       key: "gpt-3.5",
219 | //       payload: { maxTokens: 10000, model: "gpt-3.5-beta1" }
220 | //     }
221 | //   }
222 | // }
223 | ```
224 | 
225 | `key` is mandatory for a config, but if a flag has no config or no config value was matched against the context, the `key` will be `undefined`. Make sure to check against this case when trying to use the configuration in your application. `payload` is an optional JSON value for arbitrary configuration needs.
226 | 
227 | Just as `isEnabled`, accessing `config` on the object returned by `getFlags` does not automatically
228 | generate a `check` event, contrary to the `config` property on the object returned by `getFlag`.
229 | 
230 | ## Server-side rendering and bootstrapping
231 | 
232 | For server-side rendered applications, you can eliminate the initial network request by bootstrapping the client with pre-fetched flag data.
233 | 
234 | ### Init options bootstrapped
235 | 
236 | ```typescript
237 | type Configuration = {
238 |   logger: console; // by default only logs warn/error, by passing `console` you'll log everything
239 |   apiBaseUrl?: "https://front.reflag.com";
240 |   sseBaseUrl?: "https://livemessaging.bucket.co";
241 |   feedback?: undefined; // See FEEDBACK.md
242 |   enableTracking?: true; // set to `false` to stop sending track events and user/company updates to Reflag servers. Useful when you're impersonating a user
243 |   offline?: boolean; // Use the SDK in offline mode. Offline mode is useful during testing and local development
244 |   bootstrappedFlags?: FetchedFlags; // Pre-fetched flags from server-side (see Server-side rendering section)
245 | };
246 | ```
247 | 
248 | ### Using bootstrappedFlags
249 | 
250 | Use the Node SDK's `getFlagsForBootstrap()` method to pre-fetch flags server-side, then pass them to the browser client:
251 | 
252 | ```typescript
253 | // Server-side: Get flags using Node SDK
254 | import { ReflagClient as ReflagNodeClient } from "@reflag/node-sdk";
255 | 
256 | const serverClient = new ReflagNodeClient({ secretKey: "your-secret-key" });
257 | await serverClient.initialize();
258 | 
259 | const { flags } = serverClient.getFlagsForBootstrap({
260 |   user: { id: "user123", name: "John Doe", email: "[email protected]" },
261 |   company: { id: "company456", name: "Acme Inc", plan: "enterprise" },
262 | });
263 | 
264 | // Pass flags data to client using your framework's preferred method
265 | // or for example in a script tag
266 | app.get("/", (req, res) => {
267 |   res.set("Content-Type", "text/html");
268 |   res.send(
269 |     Buffer.from(
270 |       `<script>var flags = ${JSON.stringify(flags)};</script>
271 |       <main id="app"></main>`,
272 |     ),
273 |   );
274 | });
275 | 
276 | // Client-side: Initialize with pre-fetched flags
277 | import { ReflagClient } from "@reflag/browser-sdk";
278 | 
279 | const reflagClient = new ReflagClient({
280 |   publishableKey: "your-publishable-key",
281 |   user: { id: "user123", name: "John Doe", email: "[email protected]" },
282 |   company: { id: "company456", name: "Acme Inc", plan: "enterprise" },
283 |   bootstrappedFlags: flags, // No network request needed
284 | });
285 | 
286 | await reflagClient.initialize(); // Initializes all but flags
287 | const { isEnabled } = reflagClient.getFlag("huddle");
288 | ```
289 | 
290 | This eliminates loading states and improves performance by avoiding the initial flags API call.
291 | 
292 | ## Context management
293 | 
294 | ### Updating user/company/other context
295 | 
296 | Attributes given for the user/company/other context in the ReflagClient constructor can be updated for use in flag targeting evaluation with the `updateUser()`, `updateCompany()` and `updateOtherContext()` methods.
297 | They return a promise which resolves once the flags have been re-evaluated follow the update of the attributes.
298 | 
299 | The following shows how to let users self-opt-in for a new flag. The flag must have the rule `voiceHuddleOptIn IS true` set in the Reflag UI.
300 | 
301 | ```ts
302 | // toggle opt-in for the voiceHuddle flag:
303 | const { isEnabled } = reflagClient.getFlag("voiceHuddle");
304 | // this toggles the flag on/off. The promise returns once flag targeting has been
305 | // re-evaluated.
306 | await reflagClient.updateUser({ voiceHuddleOptIn: (!isEnabled).toString() });
307 | ```
308 | 
309 | > [!NOTE] > `user`/`company` attributes are also stored remotely on the Reflag servers and will automatically be used to evaluate flag targeting if the page is refreshed.
310 | 
311 | ### setContext()
312 | 
313 | The `setContext()` method allows you to replace the entire context (user, company, and other attributes) at once. This method is useful when you need to completely change the context, such as when a user logs in or switches between different accounts.
314 | 
315 | ```ts
316 | await reflagClient.setContext({
317 |   user: {
318 |     id: "new-user-123",
319 |     name: "Jane Doe",
320 |     email: "[email protected]",
321 |     role: "admin",
322 |   },
323 |   company: {
324 |     id: "company-456",
325 |     name: "New Company Inc",
326 |     plan: "enterprise",
327 |   },
328 |   other: {
329 |     feature: "beta",
330 |     locale: "en-US",
331 |   },
332 | });
333 | ```
334 | 
335 | The method will:
336 | 
337 | - Replace the entire context with the new values
338 | - Re-evaluate all flags based on the new context
339 | - Update the user and company information on Reflag servers
340 | - Return a promise that resolves once the flags have been re-evaluated
341 | 
342 | ### getContext()
343 | 
344 | The `getContext()` method returns the current context being used for flag evaluation. This is useful for debugging or when you need to inspect the current user, company, and other attributes.
345 | 
346 | ```ts
347 | const currentContext = reflagClient.getContext();
348 | console.log(currentContext);
349 | // {
350 | //   user: { id: "user-123", name: "John Doe", email: "[email protected]" },
351 | //   company: { id: "company-456", name: "Acme Inc", plan: "enterprise" },
352 | //   other: { locale: "en-US", feature: "beta" }
353 | // }
354 | ```
355 | 
356 | The returned context object contains:
357 | 
358 | - `user`: Current user attributes (if any)
359 | - `company`: Current company attributes (if any)
360 | - `other`: Additional context attributes not related to user or company
361 | 
362 | ## Toolbar
363 | 
364 | The Reflag Toolbar is great for toggling flags on/off for yourself to ensure that everything works both when a flag is on and when it's off.
365 | 
366 | <img width="352" alt="Toolbar screenshot" src="https://github.com/user-attachments/assets/c223df5a-4bd8-49a1-8b4a-ad7001357693" />
367 | 
368 | The toolbar will automatically appear on `localhost`. However, it can also be incredibly useful in production.
369 | You have full control over when it appears through the `toolbar` configuration option passed to the `ReflagClient`.
370 | 
371 | You can pass a simple boolean to force the toolbar to appear/disappear:
372 | 
373 | ```typescript
374 | const client = new ReflagClient({
375 |   // show the toolbar even in production if the user is an internal/admin user
376 |   toolbar: user?.isInternal,
377 |   ...
378 | });
379 | ```
380 | 
381 | You can also configure the position of the toolbar on the screen:
382 | 
383 | ```typescript
384 | const client = new ReflagClient({
385 |   toolbar: {
386 |     show: true;
387 |     position: {
388 |       placement: "bottom-left",
389 |       offset: {x: "1rem", y: "1rem"}
390 |     }
391 |   }
392 |   ...
393 | })
394 | ```
395 | 
396 | See [the reference](https://docs.reflag.com/supported-languages/browser-sdk/globals#toolbaroptions) for details.
397 | 
398 | ## Qualitative feedback on beta flags
399 | 
400 | Reflag can collect qualitative feedback from your users in the form of a [Customer Satisfaction Score](https://en.wikipedia.org/wiki/Customer_satisfaction) and a comment.
401 | 
402 | ### Automated feedback collection
403 | 
404 | The Reflag Browser SDK comes with automated feedback collection mode enabled by default, which lets the Reflag service ask your users for feedback for relevant flags just after they've used them.
405 | 
406 | > [!NOTE]
407 | > To get started with automatic feedback collection, make sure you've set `user` in the `ReflagClient` constructor.
408 | 
409 | Automated feedback surveys work even if you're not using the SDK to send events to Reflag.
410 | It works because the Reflag Browser SDK maintains a live connection to Reflag's servers and can automatically show a feedback prompt whenever the Reflag servers determines that an event should trigger a prompt - regardless of how this event is sent to Reflag.
411 | 
412 | You can find all the options to make changes to the default behavior in the [Reflag feedback documentation](./FEEDBACK.md).
413 | 
414 | ### Reflag feedback UI
415 | 
416 | Reflag can assist you with collecting your user's feedback by offering a pre-built UI, allowing you to get started with minimal code and effort.
417 | 
418 | [Read the Reflag feedback UI documentation](./FEEDBACK.md)
419 | 
420 | ### Reflag feedback SDK
421 | 
422 | Feedback can be submitted to Reflag using the SDK:
423 | 
424 | ```ts
425 | reflagClient.feedback({
426 |   flagKey: "my-flag-key", // String (required), copy from Flag feedback tab
427 |   score: 5, // Number: 1-5 (optional)
428 |   comment: "Absolutely stellar work!", // String (optional)
429 | });
430 | ```
431 | 
432 | ### Reflag feedback API
433 | 
434 | If you are not using the Reflag Browser SDK, you can still submit feedback using the HTTP API.
435 | 
436 | See details in [Feedback HTTP API](https://docs.reflag.com/api/http-api#post-feedback)
437 | 
438 | ## Tracking flag usage
439 | 
440 | The `track` function lets you send events to Reflag to denote flag usage.
441 | By default Reflag expects event names to align with the flag keys, but
442 | you can customize it as you wish.
443 | 
444 | ```ts
445 | reflagClient.track("huddle", { voiceHuddle: true });
446 | ```
447 | 
448 | ## Event listeners
449 | 
450 | Event listeners allow for capturing various events occurring in the `ReflagClient`. This is useful to build integrations with other system or for various debugging purposes. There are 5 kinds of events:
451 | 
452 | - `check`: Your code used `isEnabled` or `config` for a flag
453 | - `flagsUpdated`: Flags were updated. Either because they were loaded as part of initialization or because the user/company updated
454 | - `user`: User information updated (similar to the `identify` call used in tracking terminology)
455 | - `company`: Company information updated (sometimes to the `group` call used in tracking terminology)
456 | - `track`: Track event occurred.
457 | 
458 | Use the `on()` method to add an event listener to respond to certain events. See the API reference for details on each hook.
459 | 
460 | ```ts
461 | import { ReflagClient, CheckEvent, RawFlags } from "@reflag/browser-sdk";
462 | 
463 | const client = new ReflagClient({
464 |   // options
465 | });
466 | 
467 | // or add the hooks after construction:
468 | const unsub = client.on("check", (check: CheckEvent) =>
469 |   console.log(`Check event ${check}`),
470 | );
471 | // use the returned function to unsubscribe, or call `off()` with the same arguments again
472 | unsub();
473 | ```
474 | 
475 | ## Zero PII
476 | 
477 | The Reflag Browser SDK doesn't collect any metadata and HTTP IP addresses are _not_ being stored.
478 | 
479 | For tracking individual users, we recommend using something like database ID as userId, as it's unique and doesn't include any PII (personal identifiable information). If, however, you're using e.g. email address as userId, but prefer not to send any PII to Reflag, you can hash the sensitive data before sending it to Reflag:
480 | 
481 | ```ts
482 | import reflag from "@reflag/browser-sdk";
483 | import { sha256 } from "crypto-hash";
484 | 
485 | reflag.user(await sha256("john_doe"));
486 | ```
487 | 
488 | ## Use of cookies
489 | 
490 | The Reflag Browser SDK uses a couple of cookies to support automated feedback surveys. These cookies are not used for tracking purposes and thus should not need to appear in cookie consent forms.
491 | 
492 | The two cookies are:
493 | 
494 | - `reflag-prompt-${userId}`: store the last automated feedback prompt message ID received to avoid repeating surveys
495 | - `reflag-token-${userId}`: caching a token used to connect to Reflag's live messaging infrastructure that is used to deliver automated feedback surveys in real time.
496 | 
497 | ## TypeScript
498 | 
499 | Types are bundled together with the library and exposed automatically when importing through a package manager.
500 | 
501 | ## Content Security Policy (CSP)
502 | 
503 | If you are running with strict Content Security Policies active on your website, you will need to enable these directives in order to use the SDK:
504 | 
505 | | Directive   | Values                                                             | Reason                                                                                                                                |
506 | | ----------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
507 | | connect-src | [https://front.reflag.com](https://front.reflag.com)               | Basic functionality`                                                                                                                  |
508 | | connect-src | [https://livemessaging.bucket.co](https://livemessaging.bucket.co) | Server sent events for use in automated feedback surveys, which allows for automatically collecting feedback when a user used a flag. |
509 | | style-src   | 'unsafe-inline'                                                    | The feedback UI is styled with inline styles. Not having this directive results unstyled HTML elements.                               |
510 | 
511 | If you are including the Reflag tracking SDK with a `<script>`-tag from `jsdelivr.net` you will also need:
512 | 
513 | | Directive       | Values                                               | Reason                          |
514 | | --------------- | ---------------------------------------------------- | ------------------------------- |
515 | | script-src-elem | [https://cdn.jsdelivr.net](https://cdn.jsdelivr.net) | Loads the Reflag SDK from a CDN |
516 | 
517 | ## License
518 | 
519 | > MIT License
520 | > Copyright (c) 2025 Bucket ApS
521 | 
```

--------------------------------------------------------------------------------
/packages/react-sdk/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Reflag React SDK
  2 | 
  3 | React client side library for [Reflag.com](https://reflag.com)
  4 | 
  5 | Reflag supports flag toggling, tracking flag usage, [requesting feedback](#userequestfeedback) on features, and [remotely configuring flags](#remote-config).
  6 | 
  7 | The Reflag React SDK comes with a [built-in toolbar](https://docs.reflag.com/supported-languages/browser-sdk#toolbar) which appears on `localhost` by default.
  8 | 
  9 | ## Install
 10 | 
 11 | Install via npm:
 12 | 
 13 | ```shell
 14 | npm i @reflag/react-sdk
 15 | ```
 16 | 
 17 | ## Get started
 18 | 
 19 | ### 1. Add the `ReflagProvider` context provider
 20 | 
 21 | Add the `ReflagProvider` context provider to your application:
 22 | 
 23 | **Example:**
 24 | 
 25 | ```tsx
 26 | import { ReflagProvider } from "@reflag/react-sdk";
 27 | 
 28 | <ReflagProvider
 29 |   publishableKey="{YOUR_PUBLISHABLE_KEY}"
 30 |   context={{
 31 |     company: { id: "acme_inc", plan: "pro" },
 32 |     user: { id: "john doe" },
 33 |   }}
 34 |   loadingComponent={<Loading />}
 35 | >
 36 |   {/* children here are shown when loading finishes or immediately if no `loadingComponent` is given */}
 37 | </ReflagProvider>;
 38 | ```
 39 | 
 40 | ### 2. Create a new flag and set up type safety
 41 | 
 42 | Install the Reflag CLI:
 43 | 
 44 | ```shell
 45 | npm i --save-dev @reflag/cli
 46 | ```
 47 | 
 48 | Run `npx reflag new` to create your first flag!
 49 | On the first run, it will sign into Reflag and set up type generation for your project:
 50 | 
 51 | ```shell
 52 | ❯ npx reflag new
 53 | Opened web browser to facilitate login: https://app.reflag.com/api/oauth/cli/authorize
 54 | 
 55 | Welcome to ◪ Reflag!
 56 | 
 57 | ? Where should we generate the types? gen/flags.d.ts
 58 | ? What is the output format? react
 59 | ✔ Configuration created at reflag.config.json.
 60 | 
 61 | Creating flag for app Slick app.
 62 | ? New flag name: Huddle
 63 | ? New flag key: huddle
 64 | ✔ Created flag Huddle with key huddle (https://app.reflag.com/features/huddles)
 65 | ✔ Generated react types in gen/flags.d.ts.
 66 | ```
 67 | 
 68 | > [!Note]
 69 | > By default, types will be generated in `gen/flags.d.ts`.
 70 | > The default `tsconfig.json` file `include`s this file by default, but if your `tsconfig.json` is different, make sure the file is covered in the `include` property.
 71 | 
 72 | ### 3. Use `useFlag(<flagKey>)` to get flag status
 73 | 
 74 | Using the `useFlag` hook from your components lets you toggle flags on/off and track flag usage:
 75 | 
 76 | **Example:**
 77 | 
 78 | ```tsx
 79 | function StartHuddleButton() {
 80 |   const {
 81 |     isEnabled, // boolean indicating if the flag is enabled
 82 |     track, // track usage of the flag
 83 |   } = useFlag("huddle");
 84 | 
 85 |   if (!isEnabled) {
 86 |     return null;
 87 |   }
 88 | 
 89 |   return <button onClick={track}>Start huddle!</button>;
 90 | }
 91 | ```
 92 | 
 93 | `useFlag` can help you do much more. See a full example for `useFlag` [see below](#useflag).
 94 | 
 95 | ## Setting context
 96 | 
 97 | Reflag determines which flags are active for a given `user`, `company`, or `other` context.
 98 | You can pass these to the `ReflagProvider` using the `context` prop.
 99 | 
100 | ### Using the `context` prop
101 | 
102 | ```tsx
103 | <ReflagProvider
104 |   publishableKey={YOUR_PUBLISHABLE_KEY}
105 |   context={{
106 |     user: { id: "user_123", name: "John Doe", email: "[email protected]" },
107 |     company: { id: "company_123", name: "Acme, Inc" },
108 |     other: { source: "web" },
109 |   }}
110 | >
111 |   <LoadingReflag>
112 |     {/* children here are shown when loading finishes */}
113 |   </LoadingReflag>
114 | </ReflagProvider>
115 | ```
116 | 
117 | ### Legacy individual props (deprecated)
118 | 
119 | For backward compatibility, you can still use individual props, but these are deprecated and will be removed in the next major version:
120 | 
121 | ```tsx
122 | <ReflagProvider
123 |   publishableKey={YOUR_PUBLISHABLE_KEY}
124 |   user={{ id: "user_123", name: "John Doe", email: "[email protected]" }}
125 |   company={{ id: "company_123", name: "Acme, Inc" }}
126 |   otherContext={{ source: "web" }}
127 | >
128 |   <LoadingReflag>
129 |     {/* children here are shown when loading finishes */}
130 |   </LoadingReflag>
131 | </ReflagProvider>
132 | ```
133 | 
134 | > [!Important]
135 | > The `user`, `company`, and `otherContext` props are deprecated. Use the `context` prop instead, which provides the same functionality in a more structured way.
136 | 
137 | ### Context requirements
138 | 
139 | If you supply `user` or `company` objects, they must include at least the `id` property otherwise they will be ignored in their entirety.
140 | In addition to the `id`, you must also supply anything additional that you want to be able to evaluate flag targeting rules against.
141 | Attributes which are not properties of the `user` or `company` can be supplied using the `other` property.
142 | 
143 | Attributes cannot be nested (multiple levels) and must be either strings, numbers or booleans.
144 | A number of special attributes exist:
145 | 
146 | - `name` -- display name for `user`/`company`,
147 | - `email` -- the email of the user,
148 | - `avatar` -- the URL for `user`/`company` avatar image.
149 | 
150 | To retrieve flags along with their targeting information, use `useFlag(key: string)` hook (described in a section below).
151 | 
152 | Note that accessing `isEnabled` on the object returned by `useFlag()` automatically
153 | generates a `check` event.
154 | 
155 | ## Remote config
156 | 
157 | Remote config is a dynamic and flexible approach to configuring flag behavior outside of your app – without needing to re-deploy it.
158 | 
159 | Similar to `isEnabled`, each flag accessed using the `useFlag()` hook, has a `config` property. This configuration is managed from within Reflag. It is managed similar to the way access to flags is managed, but instead of the
160 | binary `isEnabled` you can have multiple configuration values which are given to different user/companies.
161 | 
162 | ### Get started with Remote config
163 | 
164 | 1. Update your flag definitions:
165 | 
166 | ```typescript
167 | import "@reflag/react-sdk";
168 | 
169 | // Define your flags by extending the `Flags` interface in @reflag/react-sdk
170 | declare module "@reflag/react-sdk" {
171 |   interface Flags {
172 |     huddle: {
173 |       // change from `boolean` to an object which sets
174 |       // a type for the remote config for `questionnaire`
175 |       maxTokens: number;
176 |       model: string;
177 |     };
178 |   }
179 | }
180 | ```
181 | 
182 | ```ts
183 | const {
184 |   isEnabled,
185 |   config: { key, payload },
186 | } = useFlag("huddles");
187 | 
188 | // isEnabled: true,
189 | // key: "gpt-3.5",
190 | // payload: { maxTokens: 10000, model: "gpt-3.5-beta1" }
191 | ```
192 | 
193 | `key` is mandatory for a config, but if a flag has no config or no config value was matched against the context, the `key` will be `undefined`. Make sure to check against this case when trying to use the configuration in your application. `payload` is an optional JSON value for arbitrary configuration needs.
194 | 
195 | Note that, similar to `isEnabled`, accessing `config` on the object returned by `useFlag()` automatically
196 | generates a `check` event.
197 | 
198 | ## Toolbar
199 | 
200 | The Reflag Toolbar is great for toggling flags on/off for yourself to ensure that everything works both when a flag is on and when it's off.
201 | 
202 | <img width="310" height="265" alt="Toolbar" src="https://github.com/user-attachments/assets/61492915-0d30-446d-a163-3eb16d9024b2" />
203 | 
204 | The toolbar will automatically appear on `localhost`. However, it can also be incredibly useful in production. You have full control over when it appears through the `toolbar` configuration option passed to the ReflagProvider.
205 | 
206 | You can pass a simple boolean to force the toolbar to appear/disappear:
207 | 
208 | ```ts
209 | <ReflagProvider
210 |   ...
211 |   // show the toolbar even in production if the user is an internal/admin user
212 |   toolbar={user?.isInternal}
213 |   ...
214 | });
215 | ```
216 | 
217 | ## Server-side rendering and bootstrapping
218 | 
219 | For server-side rendered applications, you can eliminate the initial network request by bootstrapping the client with pre-fetched flag data using the `ReflagBootstrappedProvider`.
220 | 
221 | ### Using `ReflagBootstrappedProvider`
222 | 
223 | The `<ReflagBootstrappedProvider>` component is a specialized version of `ReflagProvider` designed for server-side rendering and preloaded flag scenarios. Instead of fetching flags on initialization, it uses pre-fetched flags, resulting in faster initial page loads and better SSR compatibility.
224 | 
225 | ```tsx
226 | import { useState, useEffect } from "react";
227 | import { BootstrappedFlags } from "@reflag/react-sdk";
228 | 
229 | interface BootstrapData {
230 |   user: User;
231 |   flags: BootstrappedFlags;
232 | }
233 | 
234 | function useBootstrap() {
235 |   const [data, setData] = useState<BootstrapData | null>(null);
236 | 
237 |   useEffect(() => {
238 |     fetch("/bootstrap")
239 |       .then((res) => res.json())
240 |       .then(setData);
241 |   }, []);
242 | 
243 |   return data;
244 | }
245 | 
246 | // Usage in your app
247 | function App() {
248 |   const { user, flags } = useBootstrap();
249 | 
250 |   return (
251 |     <AuthProvider user={user}>
252 |       <ReflagBootstrappedProvider
253 |         publishableKey="your-publishable-key"
254 |         flags={flags}
255 |       >
256 |         <Router />
257 |       </ReflagBootstrappedProvider>
258 |     </AuthProvider>
259 |   );
260 | }
261 | ```
262 | 
263 | ### Server-side endpoint setup
264 | 
265 | Create an endpoint that provides bootstrap data to your client application:
266 | 
267 | ```typescript
268 | // server.js or your Express app
269 | import { ReflagClient as ReflagNodeClient } from "@reflag/node-sdk";
270 | 
271 | const reflagClient = new ReflagNodeClient({
272 |   secretKey: process.env.REFLAG_SECRET_KEY,
273 | });
274 | await reflagClient.initialize();
275 | 
276 | app.get("/bootstrap", (req, res) => {
277 |   const user = getUser(req); // Get user from your auth system
278 |   const company = getCompany(req); // Get company from your auth system
279 | 
280 |   const flags = reflagClient.getFlagsForBootstrap({
281 |     user: { id: "user123", name: "John Doe", email: "[email protected]" },
282 |     company: { id: "company456", name: "Acme Inc", plan: "enterprise" },
283 |     other: { source: "web" },
284 |   });
285 | 
286 |   res.status(200).json({
287 |     user,
288 |     flags,
289 |   });
290 | });
291 | ```
292 | 
293 | ### Next.js Page Router SSR example
294 | 
295 | For Next.js applications using server-side rendering, you can pre-fetch flags in `getServerSideProps`:
296 | 
297 | ```typescript
298 | // pages/index.tsx
299 | import { GetServerSideProps } from "next";
300 | import { ReflagClient as ReflagNodeClient } from "@reflag/node-sdk";
301 | import { ReflagBootstrappedProvider, BootstrappedFlags, useFlag } from "@reflag/react-sdk";
302 | 
303 | interface PageProps {
304 |   bootstrapData: BootstrappedFlags;
305 | }
306 | 
307 | export const getServerSideProps: GetServerSideProps = async (context) => {
308 |   const serverClient = new ReflagNodeClient({
309 |     secretKey: process.env.REFLAG_SECRET_KEY
310 |   });
311 |   await serverClient.initialize();
312 | 
313 |   const user = await getUserFromSession(context.req);
314 |   const company = await getCompanyFromUser(user);
315 | 
316 |   const bootstrapData = serverClient.getFlagsForBootstrap({
317 |     user: { id: "user123", name: "John Doe", email: "[email protected]" },
318 |     company: { id: "company456", name: "Acme Inc", plan: "enterprise" },
319 |     other: { page: "homepage" }
320 |   });
321 | 
322 |   return { props: { bootstrapData } };
323 | };
324 | 
325 | export default function HomePage({ bootstrapData }: PageProps) {
326 |   return (
327 |     <ReflagBootstrappedProvider
328 |       publishableKey={process.env.NEXT_PUBLIC_REFLAG_PUBLISHABLE_KEY}
329 |       flags={bootstrapData}
330 |     >
331 |       <HuddleFeature />
332 |     </ReflagBootstrappedProvider>
333 |   );
334 | }
335 | 
336 | function HuddleFeature() {
337 |   const { isEnabled, track, config } = useFlag("huddle");
338 | 
339 |   if (!isEnabled) return null;
340 | 
341 |   return (
342 |     <div>
343 |       <h2>Start a Huddle</h2>
344 |       <p>Max participants: {config.payload?.maxParticipants ?? 10}</p>
345 |       <p>Video quality: {config.payload?.videoQuality ?? "standard"}</p>
346 |       <button onClick={track}>Start Huddle</button>
347 |     </div>
348 |   );
349 | }
350 | ```
351 | 
352 | This approach eliminates loading states and improves performance by avoiding the initial flags API call.
353 | 
354 | ### Next.js App Router example
355 | 
356 | For Next.js applications using the App Router (Next.js 13+), you can pre-fetch flags in Server Components and pass them to client components:
357 | 
358 | ```typescript
359 | // app/layout.tsx (Server Component)
360 | import { ReflagClient as ReflagNodeClient } from "@reflag/node-sdk";
361 | import { ClientProviders } from "./providers";
362 | 
363 | async function getBootstrapData() {
364 |   const serverClient = new ReflagNodeClient({
365 |     secretKey: process.env.REFLAG_SECRET_KEY!
366 |   });
367 |   await serverClient.initialize();
368 | 
369 |   // In a real app, you'd get user/company from your auth system
370 |   const bootstrapData = serverClient.getFlagsForBootstrap({
371 |     user: { id: "user123", name: "John Doe", email: "[email protected]" },
372 |     company: { id: "company456", name: "Acme Inc", plan: "enterprise" },
373 |     other: { source: "web" }
374 |   });
375 | 
376 |   return bootstrapData;
377 | }
378 | 
379 | export default async function RootLayout({
380 |   children,
381 | }: {
382 |   children: React.ReactNode;
383 | }) {
384 |   const bootstrapData = await getBootstrapData();
385 | 
386 |   return (
387 |     <html lang="en">
388 |       <body>
389 |         <ClientProviders bootstrapData={bootstrapData}>
390 |           {children}
391 |         </ClientProviders>
392 |       </body>
393 |     </html>
394 |   );
395 | }
396 | ```
397 | 
398 | ```typescript
399 | // app/providers.tsx (Client Component)
400 | "use client";
401 | 
402 | import { ReflagBootstrappedProvider, BootstrappedFlags } from "@reflag/react-sdk";
403 | 
404 | interface ClientProvidersProps {
405 |   children: React.ReactNode;
406 |   bootstrapData: BootstrappedFlags;
407 | }
408 | 
409 | export function ClientProviders({ children, bootstrapData }: ClientProvidersProps) {
410 |   return (
411 |     <ReflagBootstrappedProvider
412 |       publishableKey={process.env.NEXT_PUBLIC_REFLAG_PUBLISHABLE_KEY!}
413 |       flags={bootstrapData}
414 |     >
415 |       {children}
416 |     </ReflagBootstrappedProvider>
417 |   );
418 | }
419 | ```
420 | 
421 | ```typescript
422 | // app/page.tsx (Server Component)
423 | import { HuddleFeature } from "./huddle-feature";
424 | 
425 | export default function HomePage() {
426 |   return (
427 |     <main>
428 |       <h1>My App</h1>
429 |       <HuddleFeature />
430 |     </main>
431 |   );
432 | }
433 | ```
434 | 
435 | ```typescript
436 | // app/huddle-feature.tsx (Client Component)
437 | "use client";
438 | 
439 | import { useFlag } from "@reflag/react-sdk";
440 | 
441 | export function HuddleFeature() {
442 |   const { isEnabled, track, config } = useFlag("huddle");
443 | 
444 |   if (!isEnabled) return null;
445 | 
446 |   return (
447 |     <div>
448 |       <h2>Start a Huddle</h2>
449 |       <p>Max participants: {config.payload?.maxParticipants ?? 10}</p>
450 |       <p>Video quality: {config.payload?.videoQuality ?? "standard"}</p>
451 |       <button onClick={track}>Start Huddle</button>
452 |     </div>
453 |   );
454 | }
455 | ```
456 | 
457 | This App Router approach leverages Server Components for server-side flag fetching while using Client Components only where React state and hooks are needed.
458 | 
459 | ## `<ReflagClientProvider>` component
460 | 
461 | The `<ReflagClientProvider>` is a lower-level component that accepts a pre-initialized `ReflagClient` instance. This is useful for advanced use cases where you need full control over client initialization or want to share a client instance across multiple parts of your application.
462 | 
463 | ### Usage
464 | 
465 | ```tsx
466 | import { ReflagClient } from "@reflag/browser-sdk";
467 | import { ReflagClientProvider } from "@reflag/react-sdk";
468 | 
469 | // Initialize the client yourself
470 | const client = new ReflagClient({
471 |   publishableKey: "your-publishable-key",
472 |   user: { id: "user123", name: "John Doe" },
473 |   company: { id: "company456", name: "Acme Inc" },
474 |   // ... other configuration options
475 | });
476 | 
477 | // Initialize the client
478 | await client.initialize();
479 | 
480 | function App() {
481 |   return (
482 |     <ReflagClientProvider client={client} loadingComponent={<Loading />}>
483 |       <Router />
484 |     </ReflagClientProvider>
485 |   );
486 | }
487 | ```
488 | 
489 | ### Props
490 | 
491 | The `ReflagClientProvider` accepts the following props:
492 | 
493 | - `client`: A pre-initialized `ReflagClient` instance
494 | - `loadingComponent`: Optional React component to show while the client is initializing (same as `ReflagProvider`)
495 | 
496 | > [!Note]
497 | > Most applications should use `ReflagProvider` or `ReflagBootstrappedProvider` instead of `ReflagClientProvider`. Only use this component when you need the advanced control it provides.
498 | 
499 | ## `<ReflagProvider>` component
500 | 
501 | The `<ReflagProvider>` initializes the Reflag SDK, fetches flags and starts listening for automated feedback survey events. The component can be configured using a number of props:
502 | 
503 | - `publishableKey` is used to connect the provider to an _environment_ on Reflag. Find your `publishableKey` under [environment settings](https://app.reflag.com/env-current/settings/app-environments) in Reflag,
504 | - `context` (recommended): An object containing `user`, `company`, and `other` properties that make up the evaluation context used to determine if a flag is enabled or not. `company` and `user` contexts are automatically transmitted to Reflag servers so the Reflag app can show you which companies have access to which flags etc.
505 | - `company`, `user` and `other` (deprecated): Individual props for context. These are deprecated in favor of the `context` prop and will be removed in the next major version.
506 |   > [!Note]
507 |   > If you specify `company` and/or `user` they must have at least the `id` property, otherwise they will be ignored in their entirety. You should also supply anything additional you want to be able to evaluate flag targeting against,
508 | - `fallbackFlags`: A list of strings which specify which flags to consider enabled if the SDK is unable to fetch flags. Can be provided in two formats:
509 | 
510 |   ```ts
511 |   // Simple array of flag keys
512 |   fallbackFlags={["flag1", "flag2"]}
513 | 
514 |   // Or with configuration overrides
515 |   fallbackFlags: {
516 |       "flag1": true,  // just enable the flag
517 |       "flag2": {      // enable with configuration
518 |         key: "variant-a",
519 |         payload: {
520 |           limit: 100,
521 |           mode: "test"
522 |         }
523 |       }
524 |   }
525 |   ```
526 | 
527 | - `timeoutMs`: Timeout in milliseconds when fetching flags from the server.
528 | - `staleWhileRevalidate`: If set to `true`, stale flags will be returned while refetching flags in the background.
529 | - `expireTimeMs`: If set, flags will be cached between page loads for this duration (in milliseconds).
530 | - `staleTimeMs`: Maximum time (in milliseconds) that stale flags will be returned if `staleWhileRevalidate` is true and new flags cannot be fetched.
531 | - `offline`: Provide this option when testing or in local development environments to avoid contacting Reflag servers.
532 | - `loadingComponent` lets you specify an React component to be rendered instead of the children while the Reflag provider is initializing. If you want more control over loading screens, `useFlag()` and `useIsLoading` returns `isLoading` which you can use to customize the loading experience.
533 | - `enableTracking`: Set to `false` to stop sending tracking events and user/company updates to Reflag. Useful when you're impersonating a user (defaults to `true`),
534 | - `apiBaseUrl`: Optional base URL for the Reflag API. Use this to override the default API endpoint,
535 | - `appBaseUrl`: Optional base URL for the Reflag application. Use this to override the default app URL,
536 | - `sseBaseUrl`: Optional base URL for Server-Sent Events. Use this to override the default SSE endpoint,
537 | - `debug`: Set to `true` to enable debug logging to the console,
538 | - `toolbar`: Optional [configuration](https://docs.reflag.com/supported-languages/browser-sdk/globals#toolbaroptions) for the Reflag toolbar,
539 | - `feedback`: Optional configuration for feedback collection
540 | 
541 | ## `<ReflagBootstrappedProvider>` component
542 | 
543 | The `<ReflagBootstrappedProvider>` is a specialized version of the `ReflagProvider` that uses pre-fetched flag data instead of making network requests during initialization. This is ideal for server-side rendering scenarios.
544 | 
545 | The component accepts the following props:
546 | 
547 | - `flags`: Pre-fetched flags data of type `BootstrappedFlags` obtained from the Node SDK's `getFlagsForBootstrap()` method. This contains both the context (user, company, other) and the flags data.
548 | - All other props available in [`ReflagProvider`](#reflagprovider-component) are supported except `context`, `user`, `company`, and `other` (which are extracted from `flags.context`).
549 | 
550 | **Example:**
551 | 
552 | ```tsx
553 | import {
554 |   ReflagBootstrappedProvider,
555 |   BootstrappedFlags,
556 | } from "@reflag/react-sdk";
557 | 
558 | interface AppProps {
559 |   bootstrapData: BootstrappedFlags;
560 | }
561 | 
562 | function App({ bootstrapData }: AppProps) {
563 |   return (
564 |     <ReflagBootstrappedProvider
565 |       publishableKey="your-publishable-key"
566 |       flags={bootstrapData}
567 |       loadingComponent={<Loading />}
568 |       debug={process.env.NODE_ENV === "development"}
569 |     >
570 |       <Router />
571 |     </ReflagBootstrappedProvider>
572 |   );
573 | }
574 | ```
575 | 
576 | > [!Note]
577 | > When using `ReflagBootstrappedProvider`, the context (user, company, and other) is extracted from the `flags.context` property and doesn't need to be passed separately.
578 | 
579 | ## Hooks
580 | 
581 | ### `useFlag()`
582 | 
583 | Returns the state of a given flag for the current context. The hook provides type-safe access to flags and their configurations.
584 | 
585 | ```tsx
586 | import { useFlag } from "@reflag/react-sdk";
587 | import { Loading } from "./Loading";
588 | 
589 | function StartHuddleButton() {
590 |   const {
591 |     isLoading, // true while flags are being loaded
592 |     isEnabled, // boolean indicating if the flag is enabled
593 |     config: {
594 |       // flag configuration
595 |       key, // string identifier for the config variant
596 |       payload, // type-safe configuration object
597 |     },
598 |     track, // function to track flag usage
599 |     requestFeedback, // function to request feedback for this flag
600 |   } = useFlag("huddle");
601 | 
602 |   if (isLoading) {
603 |     return <Loading />;
604 |   }
605 | 
606 |   if (!isEnabled) {
607 |     return null;
608 |   }
609 | 
610 |   return (
611 |     <>
612 |       <button onClick={track}>Start huddle!</button>
613 |       <button
614 |         onClick={(e) =>
615 |           requestFeedback({
616 |             title: payload?.question ?? "How do you like the Huddles feature?",
617 |             position: {
618 |               type: "POPOVER",
619 |               anchor: e.currentTarget as HTMLElement,
620 |             },
621 |           })
622 |         }
623 |       >
624 |         Give feedback!
625 |       </button>
626 |     </>
627 |   );
628 | }
629 | ```
630 | 
631 | ### `useTrack()`
632 | 
633 | `useTrack()` lets you send custom events to Reflag. Use this whenever a user _uses_ a feature. These events can be used to analyze feature usage in Reflag.
634 | 
635 | ```tsx
636 | import { useTrack } from "@reflag/react-sdk";
637 | 
638 | function StartHuddle() {
639 |   const { track } = useTrack();
640 |   <div>
641 |     <button onClick={() => track("Huddle Started", { huddleType: "voice" })}>
642 |       Start voice huddle!
643 |     </button>
644 |   </div>;
645 | }
646 | ```
647 | 
648 | ### `useRequestFeedback()`
649 | 
650 | `useRequestFeedback()` returns a function that lets you open up a dialog to ask for feedback on a specific feature. This is useful for collecting targeted feedback about specific features as part of roll out. See [Automated Feedback Surveys](https://docs.reflag.com/product-handbook/live-satisfaction) for how to do this automatically, without code.
651 | 
652 | When using the `useRequestFeedback` you must pass the flag key to `requestFeedback`.
653 | The example below shows how to use `position` to ensure the popover appears next to the "Give feedback!" button.
654 | 
655 | ```tsx
656 | import { useRequestFeedback } from "@reflag/react-sdk";
657 | 
658 | function FeedbackButton() {
659 |   const requestFeedback = useRequestFeedback();
660 |   return (
661 |     <button
662 |       onClick={(e) =>
663 |         requestFeedback({
664 |           flagKey: "huddle-flag",
665 |           title: "How satisfied are you with file uploads?",
666 |           position: {
667 |             type: "POPOVER",
668 |             anchor: e.currentTarget as HTMLElement,
669 |           },
670 |           // Optional custom styling
671 |           style: {
672 |             theme: "light",
673 |             primaryColor: "#007AFF",
674 |           },
675 |         })
676 |       }
677 |     >
678 |       Give feedback!
679 |     </button>
680 |   );
681 | }
682 | ```
683 | 
684 | See the [Feedback Documentation](https://github.com/reflagcom/javascript/blob/main/packages/browser-sdk/FEEDBACK.md#manual-feedback-collection) for more information on `requestFeedback` options.
685 | 
686 | ### `useSendFeedback()`
687 | 
688 | Returns a function that lets you send feedback to Reflag. This is useful if you've manually collected feedback through your own UI and want to send it to Reflag.
689 | 
690 | ```tsx
691 | import { useSendFeedback } from "@reflag/react-sdk";
692 | 
693 | function CustomFeedbackForm() {
694 |   const sendFeedback = useSendFeedback();
695 | 
696 |   const handleSubmit = async (data: FormData) => {
697 |     await sendFeedback({
698 |       flagKey: "reflag-flag-key",
699 |       score: parseInt(data.get("score") as string),
700 |       comment: data.get("comment") as string,
701 |     });
702 |   };
703 | 
704 |   return <form onSubmit={handleSubmit}>...</form>;
705 | }
706 | ```
707 | 
708 | ### `useUpdateUser()`, `useUpdateCompany()` and `useUpdateOtherContext()`
709 | 
710 | These hooks return functions that let you update the attributes for the currently set user, company, or other context. Updates to user/company are stored remotely and affect flag targeting, while "other" context updates only affect the current session.
711 | 
712 | ```tsx
713 | import {
714 |   useUpdateUser,
715 |   useUpdateCompany,
716 |   useUpdateOtherContext,
717 | } from "@reflag/react-sdk";
718 | 
719 | function FlagOptIn() {
720 |   const updateUser = useUpdateUser();
721 |   const updateCompany = useUpdateCompany();
722 |   const updateOtherContext = useUpdateOtherContext();
723 | 
724 |   const handleUserUpdate = async () => {
725 |     await updateUser({
726 |       role: "admin",
727 |       betaFlags: "enabled",
728 |     });
729 |   };
730 | 
731 |   const handleCompanyUpdate = async () => {
732 |     await updateCompany({
733 |       plan: "enterprise",
734 |       employees: 500,
735 |     });
736 |   };
737 | 
738 |   const handleContextUpdate = async () => {
739 |     await updateOtherContext({
740 |       currentWorkspace: "workspace-123",
741 |       theme: "dark",
742 |     });
743 |   };
744 | 
745 |   return (
746 |     <div>
747 |       <button onClick={handleUserUpdate}>Update User</button>
748 |       <button onClick={handleCompanyUpdate}>Update Company</button>
749 |       <button onClick={handleContextUpdate}>Update Context</button>
750 |     </div>
751 |   );
752 | }
753 | ```
754 | 
755 | ### `useClient()`
756 | 
757 | Returns the `ReflagClient` used by the `ReflagProvider`. The client offers more functionality that
758 | is not directly accessible thorough the other hooks.
759 | 
760 | ```tsx
761 | import { useClient } from "@reflag/react-sdk";
762 | 
763 | function LoggingWrapper({ children }: { children: ReactNode }) {
764 |   const client = useClient();
765 | 
766 |   console.log(client.getContext());
767 | 
768 |   return children;
769 | }
770 | ```
771 | 
772 | ### `useIsLoading()`
773 | 
774 | Returns the loading state of the flags in the `ReflagClient`.
775 | Initially, the value will be `true` if no bootstrap flags have been provided and the client has not be initialized.
776 | 
777 | ```tsx
778 | import { useIsLoading } from "@reflag/react-sdk";
779 | import { Spinner } from "./Spinner";
780 | 
781 | function LoadingWrapper({ children }: { children: ReactNode }) {
782 |   const isLoading = useIsLoading();
783 | 
784 |   if (isLoading) {
785 |     return <Spinner />;
786 |   }
787 | 
788 |   return children;
789 | }
790 | ```
791 | 
792 | ### `useOnEvent()`
793 | 
794 | Attach a callback handler to client events to act on changes. It automatically disposes itself on unmount.
795 | 
796 | ```tsx
797 | import { useOnEvent } from "@reflag/react-sdk";
798 | 
799 | function LoggingWrapper({ children }: { children: ReactNode }) {
800 |   useOnEvent("flagsUpdated", (newFlags) => {
801 |     console.log(newFlags);
802 |   });
803 | 
804 |   return children;
805 | }
806 | ```
807 | 
808 | ## Migrating from Bucket SDK
809 | 
810 | If you have been using the Bucket SDKs, the following list will help you migrate to Reflag SDK:
811 | 
812 | - `Bucket*` classes, and types have been renamed to `Reflag*` (e.g. `BucketClient` is now `ReflagClient`)
813 | - `Feature*` classes, and types have been renamed to `Flag*` (e.g. `Feature` is now `Flag`, `RawFeatures` is now `RawFlags`)
814 | - When using strongly-typed flags, the new `Flags` interface replaced `Features` interface
815 | - All methods that contained `feature` in the name have been renamed to use the `flag` terminology (e.g. `getFeature` is `getFlag`)
816 | - The `fallbackFeatures` property in client constructor and configuration files has been renamed to `fallbackFlags`
817 | - `featureKey` has been renamed to `flagKey` in all methods that accepts that argument
818 | - The SDKs will not emit `evaluate` and `evaluate-config` events anymore
819 | - The new cookies that are stored in the client's browser are now `reflag-*` prefixed instead of `bucket-*`
820 | - The `featuresUpdated` hook has been renamed to `flagsUpdated`
821 | - The `checkIsEnabled` and `checkConfig` hooks have been removed, use `check` from now on
822 | 
823 | To ease in transition to Reflag SDK, some of the old methods have been preserved as aliases to the new methods:
824 | 
825 | - `getFeature` method is an alias for `getFlag`
826 | - `getFeatures` method is an alias for `getFlags`
827 | - `useFeature` method is an alias for `useFlag`
828 | - `featuresUpdated` hook is an alias for `flagsUpdated`
829 | 
830 | If you are running with strict Content Security Policies active on your website, you will need change them as follows:
831 | 
832 | - `connect-src https://front.bucket.co` to `connect-src https://front.reflag.com`
833 | 
834 | ## Content Security Policy (CSP)
835 | 
836 | See [CSP](https://github.com/reflagcom/javascript/blob/main/packages/browser-sdk/README.md#content-security-policy-csp) for info on using Reflag React SDK with CSP
837 | 
838 | ## License
839 | 
840 | MIT License
841 | 
842 | Copyright (c) 2025 Bucket ApS
843 | 
```

--------------------------------------------------------------------------------
/packages/node-sdk/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Reflag Node.js SDK
  2 | 
  3 | Node.js, JavaScript/TypeScript client for [Reflag.com](https://reflag.com).
  4 | 
  5 | Reflag supports flag toggling, tracking flag usage, collecting feedback on features, and [remotely configuring flags](#remote-config).
  6 | 
  7 | ## Installation
  8 | 
  9 | Install using your favorite package manager:
 10 | 
 11 | {% tabs %}
 12 | {% tab title="npm" %}
 13 | 
 14 | ```sh
 15 | npm i @reflag/node-sdk
 16 | ```
 17 | 
 18 | {% endtab %}
 19 | 
 20 | {% tab title="yarn" %}
 21 | 
 22 | ```sh
 23 | yarn add @reflag/node-sdk
 24 | ```
 25 | 
 26 | {% endtab %}
 27 | 
 28 | {% tab title="bun" %}
 29 | 
 30 | ```sh
 31 | bun add @reflag/node-sdk
 32 | ```
 33 | 
 34 | {% endtab %}
 35 | 
 36 | {% tab title="pnpm" %}
 37 | 
 38 | ```sh
 39 | pnpm add @reflag/node-sdk
 40 | ```
 41 | 
 42 | {% endtab %}
 43 | 
 44 | {% tab title="deno" %}
 45 | 
 46 | ```sh
 47 | deno add npm:@reflag/node-sdk
 48 | ```
 49 | 
 50 | {% endtab %}
 51 | {% endtabs %}
 52 | 
 53 | Other supported languages/frameworks are in the [Supported languages](https://docs.reflag.com/quickstart/supported-languages) documentation pages.
 54 | 
 55 | You can also [use the HTTP API directly](https://docs.reflag.com/api/http-api)
 56 | 
 57 | ## Basic usage
 58 | 
 59 | To get started you need to obtain your secret key from the [environment settings](https://app.reflag.com/env-current/settings/app-environments)
 60 | in Reflag.
 61 | 
 62 | > [!CAUTION]
 63 | > Secret keys are meant for use in server side SDKs only. Secret keys offer the users the ability to obtain
 64 | > information that is often sensitive and thus should not be used in client-side applications.
 65 | 
 66 | Reflag will load settings through the various environment variables automatically (see [Configuring](#configuring) below).
 67 | 
 68 | 1. Find the Reflag secret key for your development environment under [environment settings](https://app.reflag.com/env-current/settings/app-environments) in Reflag.
 69 | 2. Set `REFLAG_SECRET_KEY` in your `.env` file
 70 | 3. Create a `reflag.ts` file containing the following:
 71 | 
 72 | ```typescript
 73 | import { ReflagClient } from "@reflag/node-sdk";
 74 | 
 75 | // Create a new instance of the client with the secret key. Additional options
 76 | // are available, such as supplying a logger and other custom properties.
 77 | //
 78 | // We recommend that only one global instance of `client` should be created
 79 | // to avoid multiple round-trips to our servers.
 80 | export const reflagClient = new ReflagClient();
 81 | 
 82 | // Initialize the client and begin fetching flag targeting definitions.
 83 | // You must call this method prior to any calls to `getFlags()`,
 84 | // otherwise an empty object will be returned.
 85 | reflagClient.initialize().then(() => {
 86 |   console.log("Reflag initialized!");
 87 | });
 88 | ```
 89 | 
 90 | Once the client is initialized, you can obtain flags along with the `isEnabled`
 91 | status to indicate whether the flag is targeted for this user/company:
 92 | 
 93 | > [!IMPORTANT]
 94 | > If `user.id` or `company.id` is not given, the whole `user` or `company` object is ignored.
 95 | 
 96 | ```typescript
 97 | // configure the client
 98 | const boundClient = reflagClient.bindClient({
 99 |   user: {
100 |     id: "john_doe",
101 |     name: "John Doe",
102 |     email: "[email protected]",
103 |     avatar: "https://example.com/users/jdoe",
104 |   },
105 |   company: {
106 |     id: "acme_inc",
107 |     name: "Acme, Inc.",
108 |     avatar: "https://example.com/companies/acme",
109 |   },
110 | });
111 | 
112 | // get the huddle flag using company, user and custom context to
113 | // evaluate the targeting.
114 | const { isEnabled, track, config } = boundClient.getFlag("huddle");
115 | 
116 | if (isEnabled) {
117 |   // this is your flag gated code ...
118 |   // send an event when the flag is used:
119 |   track();
120 | 
121 |   if (config?.key === "zoom") {
122 |     // this code will run if a given remote configuration
123 |     // is set up.
124 |   }
125 | 
126 |   // CAUTION: if you plan to use the event for automated feedback surveys
127 |   // call `flush` immediately after `track`. It can optionally be awaited
128 |   // to guarantee the sent happened.
129 |   boundClient.flush();
130 | }
131 | ```
132 | 
133 | You can also use the `getFlags()` method which returns a map of all flags:
134 | 
135 | ```typescript
136 | // get the current flags (uses company, user and custom context to
137 | // evaluate the flags).
138 | const flags = boundClient.getFlags();
139 | const bothEnabled = flags.huddle?.isEnabled && flags.voiceHuddle?.isEnabled;
140 | ```
141 | 
142 | ## High performance flag targeting
143 | 
144 | The SDK contacts the Reflag servers when you call `initialize()`
145 | and downloads the flags with their targeting rules.
146 | These rules are then matched against the user/company information you provide
147 | to `getFlags()` (or through `bindClient(..).getFlags()`). That means the
148 | `getFlags()` call does not need to contact the Reflag servers once
149 | `initialize()` has completed. `ReflagClient` will continue to periodically
150 | download the targeting rules from the Reflag servers in the background.
151 | 
152 | ### Batch Operations
153 | 
154 | The SDK automatically batches operations like user/company updates and flag tracking events to minimize API calls.
155 | The batch buffer is configurable through the client options:
156 | 
157 | ```typescript
158 | const client = new ReflagClient({
159 |   batchOptions: {
160 |     maxSize: 100, // Maximum number of events to batch
161 |     intervalMs: 1000, // Flush interval in milliseconds
162 |   },
163 | });
164 | ```
165 | 
166 | You can manually flush the batch buffer at any time:
167 | 
168 | ```typescript
169 | await client.flush();
170 | ```
171 | 
172 | > [!TIP]
173 | > It's recommended to call `flush()` before your application shuts down to ensure all events are sent.
174 | 
175 | ### Rate Limiting
176 | 
177 | The SDK includes automatic rate limiting for flag events to prevent overwhelming the API.
178 | Rate limiting is applied per unique combination of flag key and context. The rate limiter window size is configurable:
179 | 
180 | ```typescript
181 | const client = new ReflagClient({
182 |   rateLimiterOptions: {
183 |     windowSizeMs: 60000, // Rate limiting window size in milliseconds
184 |   },
185 | });
186 | ```
187 | 
188 | ### Flag definitions
189 | 
190 | Flag definitions include the rules needed to determine which flags should be enabled and which config values should be applied to any given user/company.
191 | Flag definitions are automatically fetched when calling `initialize()`.
192 | They are then cached and refreshed in the background.
193 | It's also possible to get the currently in use flag definitions:
194 | 
195 | ```typescript
196 | import fs from "fs";
197 | 
198 | const client = new ReflagClient();
199 | 
200 | const flagDefs = await client.getFlagDefinitions();
201 | // [{
202 | //   key: "huddle",
203 | //   description: "Live voice conversations with colleagues."
204 | //   flag: { ... }
205 | //   config: { ... }
206 | // }]
207 | ```
208 | 
209 | ## Bootstrapping client-side applications
210 | 
211 | The `getFlagsForBootstrap()` method is designed for server-side rendering (SSR) scenarios where you need to pass flag data to client-side applications. This method returns raw flag data without wrapper functions, making it suitable for serialization and client-side hydration.
212 | 
213 | ```typescript
214 | const client = new ReflagClient();
215 | await client.initialize();
216 | 
217 | // Get flags for bootstrapping with full context
218 | const { context, flags } = client.getFlagsForBootstrap({
219 |   user: {
220 |     id: "user123",
221 |     name: "John Doe",
222 |     email: "[email protected]",
223 |   },
224 |   company: {
225 |     id: "company456",
226 |     name: "Acme Inc",
227 |     plan: "enterprise",
228 |   },
229 |   other: {
230 |     source: "web",
231 |     platform: "desktop",
232 |   },
233 | });
234 | 
235 | // Pass this data to your client-side application
236 | // The flags object contains raw flag data suitable for JSON serialization
237 | console.log(flags);
238 | // {
239 | //   "huddle": {
240 | //     "key": "huddle",
241 | //     "isEnabled": true,
242 | //     "config": {
243 | //       "key": "enhanced",
244 | //       "payload": { "maxParticipants": 50, "videoQuality": "hd" },
245 | //     }
246 | //   }
247 | // }
248 | ```
249 | 
250 | You can also use a bound client for simpler API:
251 | 
252 | ```typescript
253 | const boundClient = client.bindClient({
254 |   user: { id: "user123", name: "John Doe", email: "[email protected]" },
255 |   company: { id: "company456", name: "Acme Inc", plan: "enterprise" },
256 | });
257 | 
258 | const { context, flags } = boundClient.getFlagsForBootstrap();
259 | ```
260 | 
261 | ### Key differences from `getFlags()`
262 | 
263 | - **Raw data**: Returns plain objects without `track()` functions, making them JSON serializable
264 | - **Context included**: Returns both the evaluated flags and the context used for evaluation
265 | - **Bootstrapping focus**: Designed specifically for passing data to client-side applications
266 | 
267 | ## Edge-runtimes like Cloudflare Workers
268 | 
269 | To use the Reflag NodeSDK with Cloudflare workers, set the `node_compat` flag [in your wrangler file](https://developers.cloudflare.com/workers/runtime-apis/nodejs/#get-started).
270 | 
271 | Instead of using `ReflagClient`, use `EdgeClient` and make sure you call `ctx.waitUntil(reflag.flush());` before returning from your worker function.
272 | 
273 | ```typescript
274 | import { EdgeClient } from "@reflag/node-sdk";
275 | 
276 | // set the REFLAG_SECRET_KEY environment variable or pass the secret key in the constructor
277 | const reflag = new EdgeClient();
278 | 
279 | export default {
280 |   async fetch(request, _env, ctx): Promise<Response> {
281 |     // initialize the client and wait for it to complete
282 |     // if the client was initialized on a previous invocation, this is a no-op.
283 |     await reflag.initialize();
284 |     const flags = reflag.getFlags({
285 |       user: { id: "userId" },
286 |       company: { id: "companyId" },
287 |     });
288 | 
289 |     // ensure all events are flushed and any requests to refresh the flag cache
290 |     // have completed after the response is sent
291 |     ctx.waitUntil(reflag.flush());
292 | 
293 |     return new Response(
294 |       `Flags for user ${userId} and company ${companyId}: ${JSON.stringify(flags, null, 2)}`,
295 |     );
296 |   },
297 | };
298 | ```
299 | 
300 | See [examples/cloudflare-worker](https://github.com/reflagcom/javascript/tree/main/packages/node-sdk/examples/cloudflare-worker/src/index.ts) for a deployable example.
301 | 
302 | Reflag maintains a cached set of flag definitions in the memory of your worker which it uses to decide which flags to turn on for which users/companies.
303 | 
304 | The SDK caches flag definitions in memory for fast performance. The first request to a new worker instance fetches definitions from Reflag's servers, while subsequent requests use the cache. When the cache expires, it's updated in the background. `ctx.waitUntil(reflag.flush())` ensures completion of the background work, so response times are not affected. This background work may increase wall-clock time for your worker, but it will not measurably increase billable CPU time on platforms like Cloudflare.
305 | 
306 | ## Error Handling
307 | 
308 | The SDK is designed to fail gracefully and never throw exceptions to the caller. Instead, it logs errors and provides
309 | fallback behavior:
310 | 
311 | 1. **Flag Evaluation Failures**:
312 | 
313 |    ```typescript
314 |    const { isEnabled } = client.getFlag("my-flag");
315 |    // If flag evaluation fails, isEnabled will be false
316 |    ```
317 | 
318 | 2. **Network Errors**:
319 | 
320 |    ```typescript
321 |    // Network errors during tracking are logged but don't affect your application
322 |    const { isEnabled, track } = client.getFlag("my-flag");
323 |    if (isEnabled) {
324 |      // network errors are caught internally and logged and never bubbled up to your application
325 |      // no need to try/catch around "track" or "getFlag"
326 |      await track();
327 |    }
328 |    ```
329 | 
330 | 3. **Missing Context**:
331 | 
332 |    ```typescript
333 |    // The SDK tracks missing context fields but continues operation
334 |    const flags = client.getFlags({
335 |      user: { id: "user123" },
336 |      // Missing company context will be logged but won't cause errors
337 |    });
338 |    ```
339 | 
340 | 4. **Offline Mode**:
341 | 
342 |    ```typescript
343 |    // In offline mode, the SDK uses flag overrides
344 |    const client = new ReflagClient({
345 |      offline: true,
346 |      flagOverrides: () => ({
347 |        "my-flag": true,
348 |      }),
349 |    });
350 |    ```
351 | 
352 | The SDK logs all errors with appropriate severity levels. You can customize logging by providing your own logger:
353 | 
354 | ```typescript
355 | const client = new ReflagClient({
356 |   logger: {
357 |     debug: (msg) => console.debug(msg),
358 |     info: (msg) => console.info(msg),
359 |     warn: (msg) => console.warn(msg),
360 |     error: (msg, error) => {
361 |       console.error(msg, error);
362 |       // Send to your error tracking service
363 |       errorTracker.capture(error);
364 |     },
365 |   },
366 | });
367 | ```
368 | 
369 | ## Remote config
370 | 
371 | Remote config is a dynamic and flexible approach to configuring flag behavior outside of your app – without needing to re-deploy it.
372 | 
373 | Similar to `isEnabled`, each flag has a `config` property. This configuration is managed from within Reflag.
374 | It is managed similar to the way access to flags is managed, but instead of the binary `isEnabled` you can have
375 | multiple configuration values which are given to different user/companies.
376 | 
377 | ```ts
378 | const flags = reflagClient.getFlags();
379 | // {
380 | //   huddle: {
381 | //     isEnabled: true,
382 | //     targetingVersion: 42,
383 | //     config: {
384 | //       key: "gpt-3.5",
385 | //       payload: { maxTokens: 10000, model: "gpt-3.5-beta1" }
386 | //     }
387 | //   }
388 | // }
389 | ```
390 | 
391 | `key` is mandatory for a config, but if a flag has no config or no config value was matched against the context, the `key` will be `undefined`. Make sure to check against this case when trying to use the configuration in your application. `payload` is an optional JSON value for arbitrary configuration needs.
392 | 
393 | Just as `isEnabled`, accessing `config` on the object returned by `getFlags` does not automatically
394 | generate a `check` event, contrary to the `config` property on the object returned by `getFlag`.
395 | 
396 | ## Configuring
397 | 
398 | The Reflag `Node.js` SDK can be configured through environment variables,
399 | a configuration file on disk or by passing options to the `ReflagClient`
400 | constructor. By default, the SDK searches for `reflag.config.json` in the
401 | current working directory.
402 | 
403 | | Option          | Type                    | Description                                                                                                                                                                                                                                         | Env Var                                     |
404 | | --------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- |
405 | | `secretKey`     | string                  | The secret key used for authentication with Reflag's servers.                                                                                                                                                                                       | REFLAG_SECRET_KEY                           |
406 | | `logLevel`      | string                  | The log level for the SDK (e.g., `"DEBUG"`, `"INFO"`, `"WARN"`, `"ERROR"`). Default: `INFO`                                                                                                                                                         | REFLAG_LOG_LEVEL                            |
407 | | `offline`       | boolean                 | Operate in offline mode. Default: `false`, except in tests it will default to `true` based off of the `TEST` env. var.                                                                                                                              | REFLAG_OFFLINE                              |
408 | | `apiBaseUrl`    | string                  | The base API URL for the Reflag servers.                                                                                                                                                                                                            | REFLAG_API_BASE_URL                         |
409 | | `flagOverrides` | Record<string, boolean> | An object specifying flag overrides for testing or local development. See [examples/express/app.test.ts](https://github.com/reflagcom/javascript/tree/main/packages/node-sdk/examples/express/app.test.ts) for how to use `flagOverrides` in tests. | REFLAG_FLAGS_ENABLED, REFLAG_FLAGS_DISABLED |
410 | | `configFile`    | string                  | Load this config file from disk. Default: `reflag.config.json`                                                                                                                                                                                      | REFLAG_CONFIG_FILE                          |
411 | 
412 | > [!NOTE] > `REFLAG_FLAGS_ENABLED` and `REFLAG_FLAGS_DISABLED` are comma separated lists of flags which will be enabled or disabled respectively.
413 | 
414 | `reflag.config.json` example:
415 | 
416 | ```json
417 | {
418 |   "secretKey": "...",
419 |   "logLevel": "warn",
420 |   "offline": true,
421 |   "apiBaseUrl": "https://proxy.slick-demo.com",
422 |   "flagOverrides": {
423 |     "huddles": true,
424 |     "voiceChat": { "isEnabled": false },
425 |     "aiAssist": {
426 |       "isEnabled": true,
427 |       "config": {
428 |         "key": "gpt-4.0",
429 |         "payload": {
430 |           "maxTokens": 50000
431 |         }
432 |       }
433 |     }
434 |   }
435 | }
436 | ```
437 | 
438 | When using a `reflag.config.json` for local development, make sure you add it to your
439 | `.gitignore` file. You can also set these options directly in the `ReflagClient`
440 | constructor. The precedence for configuration options is as follows, listed in the
441 | order of importance:
442 | 
443 | 1. Options passed along to the constructor directly,
444 | 2. Environment variable,
445 | 3. The config file.
446 | 
447 | ## Type safe flags
448 | 
449 | To get type checked flags, install the Reflag CLI:
450 | 
451 | ```sh
452 | npm i --save-dev @reflag/cli
453 | ```
454 | 
455 | then generate the types:
456 | 
457 | ```sh
458 | npx reflag flags types
459 | ```
460 | 
461 | This will generate a `reflag.d.ts` containing all your flags.
462 | Any flag look ups will now be checked against the flags that exist in Reflag.
463 | 
464 | Here's an example of a failed type check:
465 | 
466 | ```typescript
467 | import { ReflagClient } from "@reflag/node-sdk";
468 | 
469 | export const reflagClient = new ReflagClient();
470 | 
471 | reflagClient.initialize().then(() => {
472 |   console.log("Reflag initialized!");
473 | 
474 |   // TypeScript will catch this error: "invalid-flag" doesn't exist
475 |   reflagClient.getFlag("invalid-flag");
476 | 
477 |   const {
478 |     isEnabled,
479 |     config: { payload },
480 |   } = reflagClient.getFlag("create-todos");
481 | });
482 | ```
483 | 
484 | ![Type check failed](docs/type-check-failed.png "Type check failed")
485 | 
486 | This is an example of a failed config payload check:
487 | 
488 | ```typescript
489 | reflagClient.initialize().then(() => {
490 |   // TypeScript will catch this error as well: "minLength" is not part of the payload.
491 |   if (isEnabled && todo.length > config.payload.minLength) {
492 |     // ...
493 |   }
494 | });
495 | ```
496 | 
497 | ![Config type check failed](docs/type-check-payload-failed.png "Remote config type check failed")
498 | 
499 | ## Testing
500 | 
501 | When writing tests that cover code with flags, you can toggle flags on/off programmatically to test the different behavior.
502 | 
503 | `reflag.ts`:
504 | 
505 | ```typescript
506 | import { ReflagClient } from "@reflag/node-sdk";
507 | 
508 | export const reflag = new ReflagClient();
509 | ```
510 | 
511 | `app.test.ts`:
512 | 
513 | ```typescript
514 | import { reflag } from "./reflag.ts";
515 | 
516 | beforeAll(async () => await reflag.initialize());
517 | afterEach(() => {
518 |   reflag.clearFlagOverrides();
519 | });
520 | 
521 | describe("API Tests", () => {
522 |   it("should return 200 for the root endpoint", async () => {
523 |     reflag.flagOverrides = {
524 |       "show-todo": true,
525 |     };
526 | 
527 |     const response = await request(app).get("/");
528 |     expect(response.status).toBe(200);
529 |     expect(response.body).toEqual({ message: "Ready to manage some TODOs!" });
530 |   });
531 | });
532 | ```
533 | 
534 | See more on flag overrides in the section below.
535 | 
536 | ## Flag Overrides
537 | 
538 | Flag overrides allow you to override flags and their configurations locally. This is particularly useful for development and testing. You can specify overrides in three ways:
539 | 
540 | 1. Through environment variables:
541 | 
542 | ```bash
543 | REFLAG_FLAGS_ENABLED=flag1,flag2
544 | REFLAG_FLAGS_DISABLED=flag3,flag4
545 | ```
546 | 
547 | 1. Through `reflag.config.json`:
548 | 
549 | ```json
550 | {
551 |   "flagOverrides": {
552 |     "delete-todos": {
553 |       "isEnabled": true,
554 |       "config": {
555 |         "key": "dev-config",
556 |         "payload": {
557 |           "requireConfirmation": true,
558 |           "maxDeletionsPerDay": 5
559 |         }
560 |       }
561 |     }
562 |   }
563 | }
564 | ```
565 | 
566 | 1. Programmatically through the client options:
567 | 
568 | You can use a simple `Record<string, boolean>` and pass it either in the constructor or by setting `client.flagOverrides`:
569 | 
570 | ```typescript
571 | // pass directly in the constructor
572 | const client = new ReflagClient({ flagOverrides: { myFlag: true } });
573 | // or set on the client at a later time
574 | client.flagOverrides = { myFlag: false };
575 | 
576 | // clear flag overrides. Same as setting to {}.
577 | client.clearFlagOverrides();
578 | ```
579 | 
580 | To get dynamic overrides, use a function which takes a context and returns a boolean or an object with the shape of `{isEnabled, config}`:
581 | 
582 | ```typescript
583 | import { ReflagClient, Context } from "@reflag/node-sdk";
584 | 
585 | const flagOverrides = (context: Context) => ({
586 |   "delete-todos": {
587 |     isEnabled: true,
588 |     config: {
589 |       key: "dev-config",
590 |       payload: {
591 |         requireConfirmation: true,
592 |         maxDeletionsPerDay: 5,
593 |       },
594 |     },
595 |   },
596 | });
597 | 
598 | const client = new ReflagClient({
599 |   flagOverrides,
600 | });
601 | ```
602 | 
603 | ## Remote Flag Evaluation
604 | 
605 | In addition to local flag evaluation, Reflag supports remote evaluation using stored context. This is useful when you want to evaluate flags using user/company attributes that were previously sent to Reflag:
606 | 
607 | ```typescript
608 | // First, update user and company attributes
609 | await client.updateUser("user123", {
610 |   attributes: {
611 |     role: "admin",
612 |     subscription: "premium",
613 |   },
614 | });
615 | 
616 | await client.updateCompany("company456", {
617 |   attributes: {
618 |     tier: "enterprise",
619 |     employees: 1000,
620 |   },
621 | });
622 | 
623 | // Later, evaluate flags remotely using stored context
624 | const flags = await client.getFlagsRemote("company456", "user123");
625 | // Or evaluate a single flag
626 | const flag = await client.getFlagRemote(
627 |   "create-todos",
628 |   "company456",
629 |   "user123",
630 | );
631 | 
632 | // You can also provide additional context
633 | const flagsWithContext = await client.getFlagsRemote("company456", "user123", {
634 |   other: {
635 |     location: "US",
636 |     platform: "mobile",
637 |   },
638 | });
639 | ```
640 | 
641 | Remote evaluation is particularly useful when:
642 | 
643 | - You want to use the most up-to-date user/company attributes stored in Reflag
644 | - You don't want to pass all context attributes with every evaluation
645 | - You need to ensure consistent flag evaluation across different services
646 | 
647 | ## Using with Express
648 | 
649 | A popular way to integrate the Reflag Node.js SDK is through an express middleware.
650 | 
651 | ```typescript
652 | import reflag from "./reflag";
653 | import express from "express";
654 | import { BoundReflagClient } from "@reflag/node-sdk";
655 | 
656 | // Augment the Express types to include a `boundReflagClient` property on the
657 | // `res.locals` object.
658 | // This will allow us to access the ReflagClient instance in our route handlers
659 | // without having to pass it around manually
660 | declare global {
661 |   namespace Express {
662 |     interface Locals {
663 |       boundReflagClient: BoundReflagClient;
664 |     }
665 |   }
666 | }
667 | 
668 | // Add express middleware
669 | app.use((req, res, next) => {
670 |   // Extract the user and company IDs from the request
671 |   // You'll want to use a proper authentication and identification
672 |   // mechanism in a real-world application
673 |   const user = {
674 |     id: req.user?.id,
675 |     name: req.user?.name,
676 |     email: req.user?.email
677 |   }
678 | 
679 |   const company = {
680 |     id: req.user?.companyId,
681 |     name: req.user?.companyName
682 |   }
683 | 
684 |   // Create a new BoundReflagClient instance by calling the `bindClient`
685 |   // method on a `ReflagClient` instance
686 |   // This will create a new instance that is bound to the user/company given.
687 |   const boundReflagClient = reflag.bindClient({ user, company });
688 | 
689 |   // Store the BoundReflagClient instance in the `res.locals` object so we
690 |   // can access it in our route handlers
691 |   res.locals.boundReflagClient = boundReflagClient;
692 |   next();
693 | });
694 | 
695 | // Now use res.locals.boundReflagClient in your handlers
696 | app.get("/todos", async (_req, res) => {
697 |   const { track, isEnabled } = res.locals.boundReflagClient.getFlag("show-todos");
698 | 
699 |   if (!isEnabled) {
700 |     res.status(403).send({"error": "flag inaccessible"})
701 |     return
702 |   }
703 | 
704 |   ...
705 | }
706 | ```
707 | 
708 | See [examples/express/app.ts](https://github.com/reflagcom/javascript/tree/main/packages/node-sdk/example/express/app.ts) for a full example.
709 | 
710 | ## Remote flag evaluation with stored context
711 | 
712 | If you don't want to provide context each time when evaluating flags but
713 | rather you would like to utilize the attributes you sent to Reflag previously
714 | (by calling `updateCompany` and `updateUser`) you can do so by calling `getFlagsRemote`
715 | (or `getFlagRemote` for a specific flag) with providing just `userId` and `companyId`.
716 | These methods will call Reflag's servers and flags will be evaluated remotely
717 | using the stored attributes.
718 | 
719 | ```typescript
720 | // Update user and company attributes
721 | client.updateUser("john_doe", {
722 |   attributes: {
723 |     name: "John O.",
724 |     role: "admin",
725 |   },
726 | });
727 | 
728 | client.updateCompany("acme_inc", {
729 |   attributes: {
730 |     name: "Acme, Inc",
731 |     tier: "premium"
732 |   },
733 | });
734 | ...
735 | 
736 | // This will evaluate flags with respecting the attributes sent previously
737 | const flags = await client.getFlagsRemote("acme_inc", "john_doe");
738 | ```
739 | 
740 | > [!IMPORTANT]
741 | > User and company attribute updates are processed asynchronously, so there might
742 | > be a small delay between when attributes are updated and when they are available
743 | > for evaluation.
744 | 
745 | ## Opting out of tracking
746 | 
747 | There are use cases in which you not want to be sending `user`, `company` and
748 | `track` events to [Reflag.com](https://reflag.com). These are usually cases where you could be impersonating
749 | another user in the system and do not want to interfere with the data being
750 | collected by Reflag.
751 | 
752 | To disable tracking, bind the client using `bindClient()` as follows:
753 | 
754 | ```typescript
755 | // binds the client to a given user and company and set `enableTracking` to `false`.
756 | const boundClient = client.bindClient({ user, company, enableTracking: false });
757 | 
758 | boundClient.track("some event"); // this will not actually send the event to Reflag.
759 | 
760 | // the following code will not update the `user` nor `company` in Reflag and will
761 | // not send `track` events either.
762 | const { isEnabled, track } = boundClient.getFlag("user-menu");
763 | if (isEnabled) {
764 |   track();
765 | }
766 | ```
767 | 
768 | Another way way to disable tracking without employing a bound client is to call `getFlag()`
769 | or `getFlags()` by supplying `enableTracking: false` in the arguments passed to
770 | these functions.
771 | 
772 | > [!IMPORTANT]
773 | > Note, however, that calling `track()`, `updateCompany()` or `updateUser()` in the `ReflagClient`
774 | > will still send tracking data. As such, it is always recommended to use `bindClient()`
775 | > when using this SDK.
776 | 
777 | ## Flushing
778 | 
779 | ReflagClient employs a batching technique to minimize the number of calls that are sent to
780 | Reflag's servers.
781 | 
782 | By default, the SDK automatically subscribes to process exit signals and attempts to flush
783 | any pending events. This behavior is controlled by the `flushOnExit` option in the client configuration:
784 | 
785 | ```typescript
786 | const client = new ReflagClient({
787 |   batchOptions: {
788 |     flushOnExit: false, // disable automatic flushing on exit
789 |   },
790 | });
791 | ```
792 | 
793 | ## Tracking custom events and setting custom attributes
794 | 
795 | Tracking allows events and updating user/company attributes in Reflag.
796 | For example, if a customer changes their plan, you'll want Reflag to know about it,
797 | in order to continue to provide up-do-date targeting information in the Reflag interface.
798 | 
799 | The following example shows how to register a new user, associate it with a company
800 | and finally update the plan they are on.
801 | 
802 | ```typescript
803 | // registers the user with Reflag using the provided unique ID, and
804 | // providing a set of custom attributes (can be anything)
805 | client.updateUser("user_id", {
806 |   attributes: { longTimeUser: true, payingCustomer: false },
807 | });
808 | client.updateCompany("company_id", { userId: "user_id" });
809 | 
810 | // the user started a voice huddle
811 | client.track("user_id", "huddle", { attributes: { voice: true } });
812 | ```
813 | 
814 | It's also possible to achieve the same through a bound client in the following manner:
815 | 
816 | ```typescript
817 | const boundClient = client.bindClient({
818 |   user: { id: "user_id", longTimeUser: true, payingCustomer: false },
819 |   company: { id: "company_id" },
820 | });
821 | 
822 | boundClient.track("huddle", { attributes: { voice: true } });
823 | ```
824 | 
825 | Some attributes are used by Reflag to improve the UI, and are recommended
826 | to provide for easier navigation:
827 | 
828 | - `name` -- display name for `user`/`company`,
829 | - `email` -- the email of the user,
830 | - `avatar` -- the URL for `user`/`company` avatar image.
831 | 
832 | Attributes cannot be nested (multiple levels) and must be either strings,
833 | integers or booleans.
834 | 
835 | ## Managing `Last seen`
836 | 
837 | By default `updateUser`/`updateCompany` calls automatically update the given
838 | user/company `Last seen` property on Reflag servers.
839 | 
840 | You can control if `Last seen` should be updated when the events are sent by setting
841 | `meta.active = false`. This is often useful if you
842 | have a background job that goes through a set of companies just to update their
843 | attributes but not their activity.
844 | 
845 | Example:
846 | 
847 | ```typescript
848 | client.updateUser("john_doe", {
849 |   attributes: { name: "John O." },
850 |   meta: { active: true },
851 | });
852 | 
853 | client.updateCompany("acme_inc", {
854 |   attributes: { name: "Acme, Inc" },
855 |   meta: { active: false },
856 | });
857 | ```
858 | 
859 | `bindClient()` updates attributes on the Reflag servers but does not automatically
860 | update `Last seen`.
861 | 
862 | ## Zero PII
863 | 
864 | The Reflag SDK doesn't collect any metadata and HTTP IP addresses are _not_ being
865 | stored. For tracking individual users, we recommend using something like database
866 | ID as userId, as it's unique and doesn't include any PII (personal identifiable
867 | information). If, however, you're using e.g. email address as userId, but prefer
868 | not to send any PII to Reflag, you can hash the sensitive data before sending
869 | it to Reflag:
870 | 
871 | ```typescript
872 | import { sha256 } from 'crypto-hash';
873 | 
874 | client.updateUser({ userId: await sha256("john_doe"), ... });
875 | ```
876 | 
877 | ## Migrating from Bucket SDK
878 | 
879 | If you have been using the Bucket SDKs previously, the following list will help you migrate to Reflag SDK:
880 | 
881 | - `Bucket*` classes, and types have been renamed to `Reflag*` (e.g. `BucketClient` is now `ReflagClient`)
882 | - `Feature*` classes, and types have been renamed to `Flag*` (e.g. `Feature` is now `Flag`, `RawFeatures` is now `RawFlags`)
883 | - When using strongly-typed flags, the new `Flags` interface replaced `Features` interface
884 | - All methods that contained `feature` in the name have been renamed to use the `flag` terminology (e.g. `getFeature` is `getFlag`)
885 | - All environment variables that were prefixed with `BUCKET_` are now prefixed with `REFLAG_`
886 | - The `BUCKET_HOST` environment variable and `host` option have been removed from `ReflagClient` constructor, use `REFLAG_API_BASE_URL` instead
887 | - The `BUCKET_FEATURES_ENABLED` and `BUCKET_FEATURES_DISABLED` have been renamed to `REFLAG_FLAGS_ENABLED` and `REFLAG_FLAGS_DISABLED`
888 | - The default configuration file has been renamed from `bucketConfig.json` to `reflag.config.json`
889 | - The `fallbackFeatures` property in client constructor and configuration files has been renamed to `fallbackFlags`
890 | - `featureKey` has been renamed to `flagKey` in all methods that accepts that argument
891 | - The SDKs will not emit `evaluate` and `evaluate-config` events anymore
892 | 
893 | ## Typescript
894 | 
895 | Types are bundled together with the library and exposed automatically when importing
896 | through a package manager.
897 | 
898 | ## License
899 | 
900 | > MIT License
901 | > Copyright (c) 2025 Bucket ApS
902 | 
```

--------------------------------------------------------------------------------
/packages/cli/tsconfig.eslint.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "extends": "./tsconfig.json"
3 | }
4 | 
```

--------------------------------------------------------------------------------
/packages/react-sdk/dev/plain/vite-env.d.ts:
--------------------------------------------------------------------------------

```typescript
1 | /// <reference types="vite/client" />
2 | 
```

--------------------------------------------------------------------------------
/packages/browser-sdk/typedoc.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "tsconfig": "tsconfig.build.json"
3 | }
4 | 
```

--------------------------------------------------------------------------------
/packages/node-sdk/typedoc.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "tsconfig": "tsconfig.build.json"
3 | }
4 | 
```

--------------------------------------------------------------------------------
/packages/browser-sdk/tsconfig.build.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "extends": "./tsconfig.json",
3 |   "include": ["src"]
4 | }
5 | 
```

--------------------------------------------------------------------------------
/packages/flag-evaluation/tsconfig.build.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "extends": "./tsconfig.json",
3 |   "include": ["src"]
4 | }
5 | 
```

--------------------------------------------------------------------------------
/packages/node-sdk/tsconfig.build.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "extends": "./tsconfig.json",
3 |   "include": ["src"]
4 | }
5 | 
```

--------------------------------------------------------------------------------
/packages/openfeature-browser-provider/tsconfig.build.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "extends": "./tsconfig.json",
3 |   "include": ["src"]
4 | }
5 | 
```

--------------------------------------------------------------------------------
/packages/openfeature-node-provider/tsconfig.build.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "extends": "./tsconfig.json",
3 |   "include": ["src"]
4 | }
5 | 
```

--------------------------------------------------------------------------------
/packages/react-sdk/tsconfig.build.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "extends": "./tsconfig.json",
3 |   "include": ["src"]
4 | }
5 | 
```

--------------------------------------------------------------------------------
/packages/vue-sdk/tsconfig.build.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "extends": "./tsconfig.json",
3 |   "include": ["src"]
4 | }
5 | 
```

--------------------------------------------------------------------------------
/packages/node-sdk/examples/cloudflare-worker/.vscode/settings.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "files.associations": {
3 |     "wrangler.json": "jsonc"
4 |   }
5 | }
6 | 
```

--------------------------------------------------------------------------------
/packages/node-sdk/examples/express/bucketConfig.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "overrides": {
3 |     "show-todos": true,
4 |     "create-todos": true
5 |   }
6 | }
7 | 
```

--------------------------------------------------------------------------------
/packages/vue-sdk/typedoc.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "tsconfig": "tsconfig.build.json",
3 |   "entryPoints": ["src/index.ts"]
4 | }
5 | 
```

--------------------------------------------------------------------------------
/packages/react-sdk/typedoc.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "tsconfig": "tsconfig.build.json",
3 |   "entryPoints": ["src/index.tsx"]
4 | }
5 | 
```

--------------------------------------------------------------------------------
/packages/flag-evaluation/tsconfig.eslint.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "extends": "./tsconfig.json",
3 |   "include": ["./src", "./test", "./*.ts"]
4 | }
5 | 
```

--------------------------------------------------------------------------------
/packages/node-sdk/tsconfig.eslint.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "extends": "./tsconfig.json",
3 |   "include": ["./src", "./test", "./*.ts"]
4 | }
5 | 
```

--------------------------------------------------------------------------------
/packages/openfeature-node-provider/tsconfig.eslint.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "extends": "./tsconfig.json",
3 |   "include": ["./src", "./test", "./*.ts"]
4 | }
5 | 
```

--------------------------------------------------------------------------------
/packages/browser-sdk/src/toolbar/index.css:
--------------------------------------------------------------------------------

```css
1 | @import url(./Toolbar.css);
2 | @import url(./Flags.css);
3 | @import url(./Switch.css);
4 | 
```

--------------------------------------------------------------------------------
/packages/browser-sdk/tsconfig.eslint.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "extends": "./tsconfig.json",
3 |   "include": ["./src", "./test", "./dev", "./*.ts"]
4 | }
5 | 
```

--------------------------------------------------------------------------------
/packages/openfeature-browser-provider/tsconfig.eslint.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "extends": "./tsconfig.json",
3 |   "include": ["./src", "./test", "./dev", "./*.ts"]
4 | }
5 | 
```

--------------------------------------------------------------------------------
/packages/react-sdk/tsconfig.eslint.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "extends": "./tsconfig.json",
3 |   "include": ["./src", "./test", "./dev", "./*.ts"]
4 | }
5 | 
```

--------------------------------------------------------------------------------
/packages/vue-sdk/tsconfig.eslint.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "extends": "./tsconfig.json",
3 |   "include": ["./src", "./test", "./dev", "./*.ts"]
4 | }
5 | 
```

--------------------------------------------------------------------------------
/packages/openfeature-browser-provider/example/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/next.config.mjs:
--------------------------------------------------------------------------------

```
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {};
3 | 
4 | export default nextConfig;
5 | 
```

--------------------------------------------------------------------------------
/packages/vue-sdk/src/version.ts:
--------------------------------------------------------------------------------

```typescript
1 | import { version } from "../package.json";
2 | 
3 | export const SDK_VERSION = `vue-sdk/${version}`;
4 | 
```

--------------------------------------------------------------------------------
/packages/browser-sdk/postcss.config.js:
--------------------------------------------------------------------------------

```javascript
1 | module.exports = {
2 |   plugins: [require("postcss-nesting"), require("postcss-preset-env")],
3 | };
4 | 
```

--------------------------------------------------------------------------------
/packages/tsconfig/package.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "name": "@reflag/tsconfig",
3 |   "version": "0.0.2",
4 |   "license": "MIT",
5 |   "private": true
6 | }
7 | 
```

--------------------------------------------------------------------------------
/packages/flag-evaluation/eslint.config.js:
--------------------------------------------------------------------------------

```javascript
1 | const base = require("@reflag/eslint-config");
2 | 
3 | module.exports = [...base, { ignores: ["dist/"] }];
4 | 
```

--------------------------------------------------------------------------------
/packages/cli/eslint.config.js:
--------------------------------------------------------------------------------

```javascript
1 | import base from "@reflag/eslint-config/base.js";
2 | 
3 | export default [...base, { ignores: ["dist/", "gen/"] }];
4 | 
```

--------------------------------------------------------------------------------
/packages/react-sdk/dev/plain/tsconfig.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "extends": "../../tsconfig.json",
3 |   "compilerOptions": {
4 |     "module": "ESNext"
5 |   },
6 |   "include": ["."]
7 | }
8 | 
```

--------------------------------------------------------------------------------
/packages/openfeature-browser-provider/eslint.config.js:
--------------------------------------------------------------------------------

```javascript
1 | const base = require("@reflag/eslint-config");
2 | 
3 | module.exports = [...base, { ignores: ["dist/", "example/"] }];
4 | 
```

--------------------------------------------------------------------------------
/packages/openfeature-node-provider/eslint.config.js:
--------------------------------------------------------------------------------

```javascript
1 | const base = require("@reflag/eslint-config");
2 | 
3 | module.exports = [...base, { ignores: ["dist/", "example/"] }];
4 | 
```

--------------------------------------------------------------------------------
/packages/node-sdk/eslint.config.js:
--------------------------------------------------------------------------------

```javascript
1 | const base = require("@reflag/eslint-config");
2 | 
3 | module.exports = [...base, { ignores: ["dist/", "examples/"] }];
4 | 
```

--------------------------------------------------------------------------------
/packages/browser-sdk/src/feedback/ui/RadialProgress.css:
--------------------------------------------------------------------------------

```css
1 | .radial-progress {
2 |   > circle {
3 |     stroke: var(--reflag-feedback-dialog-color, #1e1f24);
4 |     opacity: 0.2;
5 |   }
6 | }
7 | 
```

--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "$schema": "node_modules/lerna/schemas/lerna-schema.json",
3 |   "version": "independent",
4 |   "npmClient": "yarn"
5 | }
6 | 
```

--------------------------------------------------------------------------------
/packages/browser-sdk/test/mocks/server.ts:
--------------------------------------------------------------------------------

```typescript
1 | import { setupServer } from "msw/node";
2 | 
3 | import { handlers } from "./handlers";
4 | 
5 | export const server = setupServer(...handlers);
6 | 
```

--------------------------------------------------------------------------------
/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/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/vite.e2e.config.js:
--------------------------------------------------------------------------------

```javascript
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 | 
```

--------------------------------------------------------------------------------
/packages/browser-sdk/src/feedback/ui/css.d.ts:
--------------------------------------------------------------------------------

```typescript
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/test/testLogger.ts:
--------------------------------------------------------------------------------

```typescript
 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 | 
```

--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------

```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/index.css:
--------------------------------------------------------------------------------

```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/vue-sdk/dev/plain/env.d.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /// <reference types="vite/client" />
 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:
--------------------------------------------------------------------------------

```typescript
 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/browser-sdk/src/ui/packages/floating-ui-preact-dom/utils/roundByDPR.ts:
--------------------------------------------------------------------------------

```typescript
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/tsconfig.json:
--------------------------------------------------------------------------------

```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/useLatestRef.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { useLayoutEffect, useRef } from "preact/hooks";
 2 | 
 3 | export function useLatestRef<T>(value: T) {
 4 |   const ref = useRef(value);
 5 |   useLayoutEffect(() => {
 6 |     ref.current = value;
 7 |   });
 8 |   return ref;
 9 | }
10 | 
```

--------------------------------------------------------------------------------
/packages/browser-sdk/src/ui/packages/floating-ui-preact-dom/utils/getDPR.ts:
--------------------------------------------------------------------------------

```typescript
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 | 
```

--------------------------------------------------------------------------------
/packages/node-sdk/vite.config.js:
--------------------------------------------------------------------------------

```javascript
 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/browser-sdk/test/e2e/empty.html:
--------------------------------------------------------------------------------

```html
 1 | <!doctype html>
 2 | <html lang="en">
 3 |   <head>
 4 |     <meta charset="UTF-8" />
 5 |     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 6 |     <title>Reflag Test</title>
 7 |   </head>
 8 |   <body></body>
 9 | </html>
10 | 
```

--------------------------------------------------------------------------------
/packages/vue-sdk/src/vue.d.ts:
--------------------------------------------------------------------------------

```typescript
 1 | declare module "*.vue" {
 2 |   import type { DefineComponent } from "vue";
 3 | 
 4 |   const component: DefineComponent<
 5 |     Record<string, unknown>,
 6 |     Record<string, unknown>,
 7 |     unknown
 8 |   >;
 9 |   export default component;
10 | }
11 | 
```
Page 1/9FirstPrevNextLast