diff --git a/.claude/commands/openspec/apply.md b/.claude/commands/openspec/apply.md index a36fd964e..347169a19 100644 --- a/.claude/commands/openspec/apply.md +++ b/.claude/commands/openspec/apply.md @@ -4,14 +4,18 @@ description: Implement an approved OpenSpec change and keep tasks in sync. category: OpenSpec tags: [openspec, apply] --- + + **Guardrails** + - Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required. - Keep changes tightly scoped to the requested outcome. - Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications. **Steps** Track these steps as TODOs and complete them one by one. + 1. Read `changes//proposal.md`, `design.md` (if present), and `tasks.md` to confirm scope and acceptance criteria. 2. Work through tasks sequentially, keeping edits minimal and focused on the requested change. 3. Confirm completion before updating statuses—make sure every item in `tasks.md` is finished. @@ -19,5 +23,6 @@ Track these steps as TODOs and complete them one by one. 5. Reference `openspec list` or `openspec show ` when additional context is required. **Reference** + - Use `openspec show --json --deltas-only` if you need additional context from the proposal while implementing. diff --git a/.claude/commands/openspec/archive.md b/.claude/commands/openspec/archive.md index 511b424a0..536505ef5 100644 --- a/.claude/commands/openspec/archive.md +++ b/.claude/commands/openspec/archive.md @@ -4,18 +4,23 @@ description: Archive a deployed OpenSpec change and update specs. category: OpenSpec tags: [openspec, archive] --- + + **Guardrails** + - Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required. - Keep changes tightly scoped to the requested outcome. - Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications. **Steps** + 1. Identify the requested change ID (via the prompt or `openspec list`). 2. Run `openspec archive --yes` to let the CLI move the change and apply spec updates without prompts (use `--skip-specs` only for tooling-only work). 3. Review the command output to confirm the target specs were updated and the change landed in `changes/archive/`. 4. Validate with `openspec validate --strict` and inspect with `openspec show ` if anything looks off. **Reference** + - Inspect refreshed specs with `openspec list --specs` and address any validation issues before handing off. diff --git a/.claude/commands/openspec/proposal.md b/.claude/commands/openspec/proposal.md index f4c1c97c1..772260970 100644 --- a/.claude/commands/openspec/proposal.md +++ b/.claude/commands/openspec/proposal.md @@ -4,14 +4,18 @@ description: Scaffold a new OpenSpec change and validate strictly. category: OpenSpec tags: [openspec, change] --- + + **Guardrails** + - Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required. - Keep changes tightly scoped to the requested outcome. - Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications. - Identify any vague or ambiguous details and ask the necessary follow-up questions before editing files. **Steps** + 1. Review `openspec/project.md`, run `openspec list` and `openspec list --specs`, and inspect related code or docs (e.g., via `rg`/`ls`) to ground the proposal in current behaviour; note any gaps that require clarification. 2. Choose a unique verb-led `change-id` and scaffold `proposal.md`, `tasks.md`, and `design.md` (when needed) under `openspec/changes//`. 3. Map the change into concrete capabilities or requirements, breaking multi-scope efforts into distinct spec deltas with clear relationships and sequencing. @@ -21,6 +25,7 @@ tags: [openspec, change] 7. Validate with `openspec validate --strict` and resolve every issue before sharing the proposal. **Reference** + - Use `openspec show --json --deltas-only` or `openspec show --type spec` to inspect details when validation fails. - Search existing requirements with `rg -n "Requirement:|Scenario:" openspec/specs` before writing new ones. - Explore the codebase with `rg `, `ls`, or direct file reads so proposals align with current implementation realities. diff --git a/.github/actions/ci/action.yml b/.github/actions/ci/action.yml index d0f2d8217..f36a8be24 100644 --- a/.github/actions/ci/action.yml +++ b/.github/actions/ci/action.yml @@ -14,8 +14,8 @@ runs: - name: Setup Node.js uses: actions/setup-node@v6 with: - node-version-file: ".nvmrc" - cache: "pnpm" + node-version-file: '.nvmrc' + cache: 'pnpm' - name: Install run: pnpm install diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 43ec581cb..8f148ea99 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -51,8 +51,6 @@ jobs: - name: Ouptut version run: echo "The version is ${{ steps.release_version.outputs.VERSION }}" - - - name: Building packages run: pnpm build diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 000000000..4eb87ed4a --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,14 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "printWidth": 80, + "singleQuote": true, + "trailingComma": "all", + "experimentalSortImports": {}, + "ignorePatterns": [ + "**/dist/**", + "**/.changeset", + "**/CHANGELOG.md", + "**/pnpm-lock.yaml", + "**/pnpm-workspace.yaml" + ] +} diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 000000000..06a69206c --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,68 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": [ + "eslint", + "typescript", + "unicorn", + "react", + "jest", + "import", + "oxc" + ], + "env": { + "node": true, + "browser": true, + "es2024": true + }, + "globals": { + "cy": "readonly", + "Cypress": "readonly", + "VERSION": "readonly", + "SplitIO": "readonly" + }, + "categories": { + "correctness": "error", + "suspicious": "warn" + }, + "ignorePatterns": ["**/node_modules/", "**/coverage/", "**/dist/"], + "rules": { + "eqeqeq": ["error", "smart"], + "no-console": "warn", + "no-unused-vars": [ + "error", + { "argsIgnorePattern": "^_", "caughtErrorsIgnorePattern": "^_" } + ], + "no-undef": "error", + "no-void": "error", + "no-negated-condition": "error", + "no-restricted-globals": ["error", "event", "atob", "btoa"], + "curly": "error", + "no-lonely-if": "error", + "no-param-reassign": "error", + "default-param-last": "error", + "no-empty": "error", + "no-empty-function": "error", + "no-array-constructor": "error", + "typescript/no-explicit-any": "off", + "typescript/no-inferrable-types": "error", + "typescript/array-type": ["error", { "default": "array" }], + "typescript/prefer-for-of": "error", + "typescript/prefer-enum-initializers": "error", + "typescript/no-unnecessary-template-expression": "off", + "unicorn/prefer-number-properties": "error", + "react/self-closing-comp": "error", + "react/react-in-jsx-scope": "off", + "react/jsx-no-useless-fragment": "off", + "react-hooks/exhaustive-deps": "warn", + "import/no-duplicates": "error", + "jest/no-disabled-tests": "warn" + }, + "overrides": [ + { + "files": ["demo/**", "scripts/**"], + "rules": { + "no-console": "off" + } + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..fabd1257a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,15 @@ +{ + "workbench.colorCustomizations": { + "tab.unfocusedActiveBorder": "#fff0" + }, + "editor.defaultFormatter": "oxc.oxc-vscode", + "editor.codeActionsOnSave": { + "source.fixAll.oxc": "explicit" + }, + "[javascript]": { + "editor.defaultFormatter": "oxc.oxc-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "oxc.oxc-vscode" + } +} diff --git a/AGENTS.md b/AGENTS.md index 066969941..1db1e1e85 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,18 +1,21 @@ + # OpenSpec Instructions These instructions are for AI assistants working in this project. Always open `@/openspec/AGENTS.md` when the request: + - Mentions planning or proposals (words like proposal, spec, change, plan) - Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work - Sounds ambiguous and you need the authoritative spec before coding Use `@/openspec/AGENTS.md` to learn: + - How to create and apply change proposals - Spec format and conventions - Project structure and guidelines Keep this managed block so 'openspec update' can refresh the instructions. - \ No newline at end of file + diff --git a/CLAUDE.md b/CLAUDE.md index 066969941..1db1e1e85 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,18 +1,21 @@ + # OpenSpec Instructions These instructions are for AI assistants working in this project. Always open `@/openspec/AGENTS.md` when the request: + - Mentions planning or proposals (words like proposal, spec, change, plan) - Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work - Sounds ambiguous and you need the authoritative spec before coding Use `@/openspec/AGENTS.md` to learn: + - How to create and apply change proposals - Spec format and conventions - Project structure and guidelines Keep this managed block so 'openspec update' can refresh the instructions. - \ No newline at end of file + diff --git a/biome.json b/biome.json deleted file mode 100644 index 116adf8b8..000000000 --- a/biome.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "$schema": "https://biomejs.dev/schemas/2.3.8/schema.json", - "vcs": { - "clientKind": "git", - "enabled": true, - "useIgnoreFile": true - }, - "files": { - "ignoreUnknown": true - }, - "formatter": { - "enabled": true, - "formatWithErrors": false, - "indentStyle": "space", - "indentWidth": 2, - "lineEnding": "lf", - "lineWidth": 80, - "includes": [ - "**", - "!**/package.json", - "!**/dist/**", - "!**/.changeset", - "!**/CHANGELOG.md", - "!**/pnpm-lock.yaml", - "!**/pnpm-workspace.yaml" - ] - }, - "assist": { "actions": { "source": { "organizeImports": "on" } } }, - "linter": { - "enabled": true, - "rules": { - "recommended": true, - "performance": { - "noAccumulatingSpread": "off" - }, - "complexity": { - "noVoid": "error", - "noUselessFragments": "off" - }, - "correctness": { - "noUndeclaredVariables": "error", - "noUnreachableSuper": "error", - "noUnusedVariables": "error", - "useExhaustiveDependencies": "warn", - "useHookAtTopLevel": "error" - }, - "style": { - "noNegationElse": "error", - "noRestrictedGlobals": { - "level": "error", - "options": { - "deniedGlobals": { - "event": "TODO: Add a custom message here.", - "atob": "TODO: Add a custom message here.", - "btoa": "TODO: Add a custom message here." - } - } - }, - "useBlockStatements": "error", - "useCollapsedElseIf": "error", - "useConsistentArrayType": { - "level": "error", - "options": { "syntax": "shorthand" } - }, - "useForOf": "error", - "useFragmentSyntax": "error", - "useShorthandAssign": "error", - "noParameterAssign": "error", - "useAsConstAssertion": "error", - "useDefaultParameterLast": "error", - "useEnumInitializers": "error", - "useSelfClosingElements": "error", - "useSingleVarDeclarator": "error", - "noUnusedTemplateLiteral": "error", - "useNumberNamespace": "error", - "noInferrableTypes": "error", - "noUselessElse": "error", - "useArrayLiterals": "error" - }, - "suspicious": { - "noEmptyBlockStatements": "error", - "noSkippedTests": "warn", - "noExplicitAny": "off" - } - }, - "includes": ["**", "!**/node_modules/", "!**/coverage/", "!**/dist/"] - }, - "javascript": { - "formatter": { - "jsxQuoteStyle": "double", - "quoteProperties": "asNeeded", - "trailingCommas": "es5", - "semicolons": "always", - "arrowParentheses": "always", - "bracketSpacing": true, - "quoteStyle": "single" - }, - "globals": ["cy", "VERSION", "Cypress", "SplitIO"] - } -} diff --git a/lint-staged.config.mjs b/lint-staged.config.mjs index 43247d8b9..da00c6fc6 100644 --- a/lint-staged.config.mjs +++ b/lint-staged.config.mjs @@ -1,6 +1,6 @@ export default { '*': [ - 'biome check --write --no-errors-on-unmatched', // Format, sort imports, lint, and apply safe fixes - 'git add -u', + 'oxlint --fix', // Lint and apply safe fixes + 'oxfmt --write', // Format and sort imports ], }; diff --git a/openspec/AGENTS.md b/openspec/AGENTS.md index 687036e14..a16c09a95 100644 --- a/openspec/AGENTS.md +++ b/openspec/AGENTS.md @@ -15,14 +15,17 @@ Instructions for AI coding assistants using OpenSpec for spec-driven development ## Three-Stage Workflow ### Stage 1: Creating Changes + Create proposal when you need to: + - Add features or functionality - Make breaking changes (API, schema) -- Change architecture or patterns +- Change architecture or patterns - Optimize performance (changes behavior) - Update security patterns Triggers (examples): + - "Help me create a change proposal" - "Help me plan a change" - "Help me create a proposal" @@ -30,10 +33,12 @@ Triggers (examples): - "I want to create a spec" Loose matching guidance: + - Contains one of: `proposal`, `change`, `spec` - With one of: `create`, `plan`, `make`, `start`, `help` Skip proposal for: + - Bug fixes (restore intended behavior) - Typos, formatting, comments - Dependency updates (non-breaking) @@ -41,13 +46,16 @@ Skip proposal for: - Tests for existing behavior **Workflow** + 1. Review `openspec/project.md`, `openspec list`, and `openspec list --specs` to understand current context. 2. Choose a unique verb-led `change-id` and scaffold `proposal.md`, `tasks.md`, optional `design.md`, and spec deltas under `openspec/changes//`. 3. Draft spec deltas using `## ADDED|MODIFIED|REMOVED Requirements` with at least one `#### Scenario:` per requirement. 4. Run `openspec validate --strict` and resolve any issues before sharing the proposal. ### Stage 2: Implementing Changes + Track these steps as TODOs and complete them one by one. + 1. **Read proposal.md** - Understand what's being built 2. **Read design.md** (if exists) - Review technical decisions 3. **Read tasks.md** - Get implementation checklist @@ -57,7 +65,9 @@ Track these steps as TODOs and complete them one by one. 7. **Approval gate** - Do not start implementation until the proposal is reviewed and approved ### Stage 3: Archiving Changes + After deployment, create separate PR to: + - Move `changes/[name]/` → `changes/archive/YYYY-MM-DD-[name]/` - Update `specs/` if capabilities changed - Use `openspec archive [change] --skip-specs --yes` for tooling-only changes @@ -66,6 +76,7 @@ After deployment, create separate PR to: ## Before Any Task **Context Checklist:** + - [ ] Read relevant specs in `specs/[capability]/spec.md` - [ ] Check pending changes in `changes/` for conflicts - [ ] Read `openspec/project.md` for conventions @@ -73,12 +84,14 @@ After deployment, create separate PR to: - [ ] Run `openspec list --specs` to see existing capabilities **Before Creating Specs:** + - Always check if capability already exists - Prefer modifying existing specs over creating duplicates - Use `openspec show [spec]` to review current state - If request is ambiguous, ask 1–2 clarifying questions before scaffolding ### Search Guidance + - Enumerate specs: `openspec spec list --long` (or `--json` for scripts) - Enumerate changes: `openspec list` (or `openspec change list --json` - deprecated but available) - Show details: @@ -148,7 +161,7 @@ openspec/ ``` New request? ├─ Bug fix restoring spec behavior? → Fix directly -├─ Typo/format/comment? → Fix directly +├─ Typo/format/comment? → Fix directly ├─ New feature/capability? → Create proposal ├─ Breaking change? → Create proposal ├─ Architecture change? → Create proposal @@ -160,43 +173,58 @@ New request? 1. **Create directory:** `changes/[change-id]/` (kebab-case, verb-led, unique) 2. **Write proposal.md:** + ```markdown ## Why + [1-2 sentences on problem/opportunity] ## What Changes + - [Bullet list of changes] - [Mark breaking changes with **BREAKING**] ## Impact + - Affected specs: [list capabilities] - Affected code: [key files/systems] ``` 3. **Create spec deltas:** `specs/[capability]/spec.md` + ```markdown ## ADDED Requirements + ### Requirement: New Feature + The system SHALL provide... #### Scenario: Success case + - **WHEN** user performs action - **THEN** expected result ## MODIFIED Requirements + ### Requirement: Existing Feature + [Complete modified requirement] ## REMOVED Requirements + ### Requirement: Old Feature + **Reason**: [Why removing] **Migration**: [How to handle] ``` + If multiple capabilities are affected, create multiple delta files under `changes/[change-id]/specs//spec.md`—one per capability. 4. **Create tasks.md:** + ```markdown ## 1. Implementation + - [ ] 1.1 Create database schema - [ ] 1.2 Implement API endpoint - [ ] 1.3 Add frontend component @@ -204,32 +232,40 @@ If multiple capabilities are affected, create multiple delta files under `change ``` 5. **Create design.md when needed:** -Create `design.md` if any of the following apply; otherwise omit it: + Create `design.md` if any of the following apply; otherwise omit it: + - Cross-cutting change (multiple services/modules) or a new architectural pattern - New external dependency or significant data model changes - Security, performance, or migration complexity - Ambiguity that benefits from technical decisions before coding Minimal `design.md` skeleton: + ```markdown ## Context + [Background, constraints, stakeholders] ## Goals / Non-Goals + - Goals: [...] - Non-Goals: [...] ## Decisions + - Decision: [What and why] - Alternatives considered: [Options + rationale] ## Risks / Trade-offs + - [Risk] → Mitigation ## Migration Plan + [Steps, rollback] ## Open Questions + - [...] ``` @@ -238,22 +274,27 @@ Minimal `design.md` skeleton: ### Critical: Scenario Formatting **CORRECT** (use #### headers): + ```markdown #### Scenario: User login success + - **WHEN** valid credentials provided - **THEN** return JWT token ``` **WRONG** (don't use bullets or bold): + ```markdown -- **Scenario: User login** ❌ -**Scenario**: User login ❌ -### Scenario: User login ❌ +- **Scenario: User login** ❌ + **Scenario**: User login ❌ + +### Scenario: User login ❌ ``` Every requirement MUST have at least one scenario. ### Requirement Wording + - Use SHALL/MUST for normative requirements (avoid should/may unless intentionally non-normative) ### Delta Operations @@ -266,6 +307,7 @@ Every requirement MUST have at least one scenario. Headers matched with `trim(header)` - whitespace ignored. #### When to use ADDED vs MODIFIED + - ADDED: Introduces a new capability or sub-capability that can stand alone as a requirement. Prefer ADDED when the change is orthogonal (e.g., adding "Slash Command Configuration") rather than altering the semantics of an existing requirement. - MODIFIED: Changes the behavior, scope, or acceptance criteria of an existing requirement. Always paste the full, updated requirement content (header + all scenarios). The archiver will replace the entire requirement with what you provide here; partial deltas will drop previous details. - RENAMED: Use when only the name changes. If you also change behavior, use RENAMED (name) plus MODIFIED (content) referencing the new name. @@ -273,14 +315,17 @@ Headers matched with `trim(header)` - whitespace ignored. Common pitfall: Using MODIFIED to add a new concern without including the previous text. This causes loss of detail at archive time. If you aren’t explicitly changing the existing requirement, add a new requirement under ADDED instead. Authoring a MODIFIED requirement correctly: -1) Locate the existing requirement in `openspec/specs//spec.md`. -2) Copy the entire requirement block (from `### Requirement: ...` through its scenarios). -3) Paste it under `## MODIFIED Requirements` and edit to reflect the new behavior. -4) Ensure the header text matches exactly (whitespace-insensitive) and keep at least one `#### Scenario:`. + +1. Locate the existing requirement in `openspec/specs//spec.md`. +2. Copy the entire requirement block (from `### Requirement: ...` through its scenarios). +3. Paste it under `## MODIFIED Requirements` and edit to reflect the new behavior. +4. Ensure the header text matches exactly (whitespace-insensitive) and keep at least one `#### Scenario:`. Example for RENAMED: + ```markdown ## RENAMED Requirements + - FROM: `### Requirement: Login` - TO: `### Requirement: User Authentication` ``` @@ -290,14 +335,17 @@ Example for RENAMED: ### Common Errors **"Change must have at least one delta"** + - Check `changes/[name]/specs/` exists with .md files - Verify files have operation prefixes (## ADDED Requirements) **"Requirement must have at least one scenario"** + - Check scenarios use `#### Scenario:` format (4 hashtags) - Don't use bullet points or bold for scenario headers **Silent scenario parsing failures** + - Exact format required: `#### Scenario: Name` - Debug with: `openspec show [change] --json --deltas-only` @@ -359,73 +407,88 @@ openspec/changes/add-2fa-notify/ ``` auth/spec.md + ```markdown ## ADDED Requirements + ### Requirement: Two-Factor Authentication + ... ``` notifications/spec.md + ```markdown ## ADDED Requirements + ### Requirement: OTP Email Notification + ... ``` ## Best Practices ### Simplicity First + - Default to <100 lines of new code - Single-file implementations until proven insufficient - Avoid frameworks without clear justification - Choose boring, proven patterns ### Complexity Triggers + Only add complexity with: + - Performance data showing current solution too slow - Concrete scale requirements (>1000 users, >100MB data) - Multiple proven use cases requiring abstraction ### Clear References + - Use `file.ts:42` format for code locations - Reference specs as `specs/auth/spec.md` - Link related changes and PRs ### Capability Naming + - Use verb-noun: `user-auth`, `payment-capture` - Single purpose per capability - 10-minute understandability rule - Split if description needs "AND" ### Change ID Naming + - Use kebab-case, short and descriptive: `add-two-factor-auth` - Prefer verb-led prefixes: `add-`, `update-`, `remove-`, `refactor-` - Ensure uniqueness; if taken, append `-2`, `-3`, etc. ## Tool Selection Guide -| Task | Tool | Why | -|------|------|-----| -| Find files by pattern | Glob | Fast pattern matching | -| Search code content | Grep | Optimized regex search | -| Read specific files | Read | Direct file access | +| Task | Tool | Why | +| --------------------- | ---- | ------------------------ | +| Find files by pattern | Glob | Fast pattern matching | +| Search code content | Grep | Optimized regex search | +| Read specific files | Read | Direct file access | | Explore unknown scope | Task | Multi-step investigation | ## Error Recovery ### Change Conflicts + 1. Run `openspec list` to see active changes 2. Check for overlapping specs 3. Coordinate with change owners 4. Consider combining proposals ### Validation Failures + 1. Run with `--strict` flag 2. Check JSON output for details 3. Verify spec file format 4. Ensure scenarios properly formatted ### Missing Context + 1. Read project.md first 2. Check related specs 3. Review recent archives @@ -434,17 +497,20 @@ Only add complexity with: ## Quick Reference ### Stage Indicators + - `changes/` - Proposed, not yet built - `specs/` - Built and deployed - `archive/` - Completed changes ### File Purposes + - `proposal.md` - Why and what - `tasks.md` - Implementation steps - `design.md` - Technical decisions - `spec.md` - Requirements and behavior ### CLI Essentials + ```bash openspec list # What's in progress? openspec show [item] # View details diff --git a/openspec/project.md b/openspec/project.md index 504f6dc21..7f1e18a29 100644 --- a/openspec/project.md +++ b/openspec/project.md @@ -3,6 +3,7 @@ ## Purpose **Flopflip** is a modern feature toggling (feature flag) library for React applications. It enables developers to: + - Toggle features on/off without redeploying applications - Run A/B tests and multivariate experiments - Manage feature releases in real-time @@ -95,6 +96,7 @@ The library provides React components, hooks, and higher-order components for co ## Domain Context **Feature Toggling Concepts**: + - **Feature Flags**: Boolean toggles for on/off control - **Variants/Experiments**: Multi-variate flags (A/B tests, canary releases) - **User Context**: Flags evaluated based on user properties (ID, segment, custom attributes) @@ -102,6 +104,7 @@ The library provides React components, hooks, and higher-order components for co - **Provider Agnostic**: Designed to work with any feature flag service or custom backend **Key Components & Hooks**: + - `ConfigureAdapter` - Set up feature flag provider - `ToggleFeature` - Conditional rendering based on flags - `useFeatureToggle()` - Read single flag value @@ -113,6 +116,7 @@ The library provides React components, hooks, and higher-order components for co **Adapter Interface Contract**: All adapters must implement: + - `configure(config, callbacks)` - Initialize with user context - `reconfigure(config)` - Update user context - Callbacks: `onFlagsStateChange`, `onStatusStateChange` for state updates @@ -137,7 +141,7 @@ All adapters must implement: - Split.io (via @flopflip/splitio-adapter) - Custom GraphQL endpoints (via @flopflip/graphql-adapter) - Custom HTTP endpoints (via @flopflip/http-adapter) -- **Package Registry**: npm (published as scoped packages @flopflip/*) +- **Package Registry**: npm (published as scoped packages @flopflip/\*) - **CDN**: unpkg.com (UMD builds available) - **CI/CD**: GitHub Actions - **Version Control**: Git/GitHub diff --git a/package.json b/package.json index 8a13aa1ea..9854b3d4f 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,34 @@ { - "private": true, "name": "flopflip", + "private": true, "description": "Monorepository for flipflop and its projects e.g. react-redux, react and the wrapper", + "keywords": [ + "@reduxjs/toolkit", + "HoC", + "LaunchDarkly", + "feature-flags", + "feature-toggles", + "react", + "redux", + "store-enhancer" + ], + "homepage": "https://github.com/tdeekens/flopflip#readme", + "bugs": { + "url": "https://github.com/tdeekens/flopflip/issues" + }, + "author": "Tobias Deekens ", "scripts": { "auth": "npm_config_registry=https://registry.npmjs.org npm whoami", - "biome:check": "biome check --write", "build": "turbo build", "changeset:version-and-format": "changeset version && pnpm format && pnpm install --no-frozen-lockfile", "changeset": "changeset", "check-types": "turbo check-types", "clean": "manypkg exec rm -rf dist && manypkg exec rm -rf .turbo && rm -rf .turbo", - "fix:biome": "biome lint --write", - "format": "biome format --write", - "lint:ci": "biome ci", - "lint": "biome lint", + "format": "oxfmt --write", + "format:check": "oxfmt --check", + "lint": "oxlint", + "lint:ci": "oxlint && oxfmt --check", + "lint:fix": "oxlint --fix", "postinstall": "husky install && manypkg check && check-node-version --package --print", "postpublish": "pinst --enable", "preinstall": "npx only-allow pnpm", @@ -23,26 +38,69 @@ "test:report": "vitest run --reporter=junit --outputFile=junit.xml", "test": "turbo test" }, - "author": "Tobias Deekens ", + "devDependencies": { + "@babel/cli": "7.28.6", + "@babel/core": "7.29.0", + "@babel/plugin-external-helpers": "7.27.1", + "@babel/plugin-proposal-export-default-from": "7.27.1", + "@babel/plugin-syntax-dynamic-import": "7.8.3", + "@babel/plugin-transform-class-properties": "7.28.6", + "@babel/plugin-transform-destructuring": "7.28.5", + "@babel/plugin-transform-export-namespace-from": "7.27.1", + "@babel/plugin-transform-modules-commonjs": "7.28.6", + "@babel/plugin-transform-nullish-coalescing-operator": "7.28.6", + "@babel/plugin-transform-object-rest-spread": "7.28.6", + "@babel/plugin-transform-optional-chaining": "7.28.6", + "@babel/plugin-transform-private-methods": "7.28.6", + "@babel/plugin-transform-private-property-in-object": "7.28.6", + "@babel/plugin-transform-react-constant-elements": "7.27.1", + "@babel/plugin-transform-regenerator": "7.29.0", + "@babel/plugin-transform-runtime": "7.29.0", + "@babel/polyfill": "7.12.1", + "@babel/preset-env": "7.29.0", + "@babel/preset-react": "7.28.5", + "@babel/preset-typescript": "7.28.5", + "@changesets/changelog-github": "0.5.2", + "@changesets/cli": "2.29.8", + "@commitlint/cli": "20.4.1", + "@commitlint/config-conventional": "20.4.1", + "@manypkg/cli": "0.25.1", + "@testing-library/jest-dom": "6.9.1", + "@types/jest": "30.0.0", + "@types/lodash": "4.17.23", + "@types/node": "24.10.13", + "@vitejs/plugin-react": "5.1.4", + "@vitest/coverage-v8": "4.0.18", + "@vitest/ui": "4.0.18", + "babel-plugin-transform-dynamic-import": "2.1.0", + "check-node-version": "4.2.1", + "colors": "1.4.0", + "core-js": "3.48.0", + "find-up": "8.0.0", + "husky": "9.1.7", + "jsdom": "28.1.0", + "lint-staged": "16.2.7", + "oxfmt": "0.34.0", + "oxlint": "1.49.0", + "pinst": "3.0.0", + "raf": "3.4.1", + "react": "19.2.4", + "react-dom": "19.2.4", + "regenerator-runtime": "0.14.1", + "rimraf": "6.1.3", + "ts-node": "10.9.2", + "tslib": "2.8.1", + "turbo": "2.8.9", + "typescript": "5.9.3", + "vite": "7.3.1", + "vitest": "4.0.18", + "vitest-localstorage-mock": "0.1.2" + }, "engines": { "node": ">=20", "npm": ">=10", "pnpm": ">=9" }, - "bugs": { - "url": "https://github.com/tdeekens/flopflip/issues" - }, - "homepage": "https://github.com/tdeekens/flopflip#readme", - "keywords": [ - "react", - "@reduxjs/toolkit", - "redux", - "feature-flags", - "feature-toggles", - "LaunchDarkly", - "HoC", - "store-enhancer" - ], "packageManager": "pnpm@10.29.3", "pnpm": { "overrides": { @@ -101,62 +159,5 @@ "maxSize": "4Kb" } ] - }, - "devDependencies": { - "@babel/cli": "7.28.6", - "@babel/core": "7.29.0", - "@babel/plugin-external-helpers": "7.27.1", - "@babel/plugin-proposal-export-default-from": "7.27.1", - "@babel/plugin-syntax-dynamic-import": "7.8.3", - "@babel/plugin-transform-class-properties": "7.28.6", - "@babel/plugin-transform-destructuring": "7.28.5", - "@babel/plugin-transform-export-namespace-from": "7.27.1", - "@babel/plugin-transform-modules-commonjs": "7.28.6", - "@babel/plugin-transform-nullish-coalescing-operator": "7.28.6", - "@babel/plugin-transform-object-rest-spread": "7.28.6", - "@babel/plugin-transform-optional-chaining": "7.28.6", - "@babel/plugin-transform-private-methods": "7.28.6", - "@babel/plugin-transform-private-property-in-object": "7.28.6", - "@babel/plugin-transform-react-constant-elements": "7.27.1", - "@babel/plugin-transform-regenerator": "7.29.0", - "@babel/plugin-transform-runtime": "7.29.0", - "@babel/polyfill": "7.12.1", - "@babel/preset-env": "7.29.0", - "@babel/preset-react": "7.28.5", - "@babel/preset-typescript": "7.28.5", - "@biomejs/biome": "2.4.0", - "@changesets/changelog-github": "0.5.2", - "@changesets/cli": "2.29.8", - "@commitlint/cli": "20.4.1", - "@commitlint/config-conventional": "20.4.1", - "@manypkg/cli": "0.25.1", - "@testing-library/jest-dom": "6.9.1", - "@types/jest": "30.0.0", - "@types/lodash": "4.17.23", - "@types/node": "24.10.13", - "@vitejs/plugin-react": "5.1.4", - "@vitest/coverage-v8": "4.0.18", - "@vitest/ui": "4.0.18", - "babel-plugin-transform-dynamic-import": "2.1.0", - "check-node-version": "4.2.1", - "colors": "1.4.0", - "core-js": "3.48.0", - "find-up": "8.0.0", - "husky": "9.1.7", - "jsdom": "28.1.0", - "lint-staged": "16.2.7", - "pinst": "3.0.0", - "raf": "3.4.1", - "react": "19.2.4", - "react-dom": "19.2.4", - "regenerator-runtime": "0.14.1", - "rimraf": "6.1.3", - "ts-node": "10.9.2", - "tslib": "2.8.1", - "turbo": "2.8.9", - "typescript": "5.9.3", - "vite": "7.3.1", - "vitest": "4.0.18", - "vitest-localstorage-mock": "0.1.2" } } diff --git a/packages/adapter-utilities/package.json b/packages/adapter-utilities/package.json index 90dce39be..5316a641b 100644 --- a/packages/adapter-utilities/package.json +++ b/packages/adapter-utilities/package.json @@ -2,14 +2,28 @@ "name": "@flopflip/adapter-utilities", "version": "15.1.7", "description": "Adapter utilities for flipflop", - "sideEffects": false, - "type": "module", - "exports": { - ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs" - } + "keywords": [ + "feature-flags", + "feature-toggles", + "types" + ], + "homepage": "https://github.com/tdeekens/flopflip#readme", + "bugs": { + "url": "https://github.com/tdeekens/flopflip/issues" }, + "license": "MIT", + "author": "Tobias Deekens ", + "repository": { + "type": "git", + "url": "https://github.com/tdeekens/flopflip.git", + "directory": "packages/adapter-utilities" + }, + "files": [ + "readme.md", + "dist/**" + ], + "type": "module", + "sideEffects": false, "main": "./dist/index.js", "typesVersions": { "*": { @@ -19,6 +33,15 @@ ] } }, + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, + "publishConfig": { + "access": "public" + }, "scripts": { "build": "rimraf dist && tsup", "check-types": "tsc --noEmit", @@ -26,29 +49,6 @@ "test:watch": "vitest", "dev": "tsup --watch --clean=false" }, - "files": [ - "readme.md", - "dist/**" - ], - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "https://github.com/tdeekens/flopflip.git", - "directory": "packages/adapter-utilities" - }, - "author": "Tobias Deekens ", - "license": "MIT", - "bugs": { - "url": "https://github.com/tdeekens/flopflip/issues" - }, - "homepage": "https://github.com/tdeekens/flopflip#readme", - "keywords": [ - "feature-flags", - "feature-toggles", - "types" - ], "dependencies": { "@babel/runtime": "7.28.6", "@flopflip/types": "workspace:*", diff --git a/packages/adapter-utilities/src/normalize-flag.ts b/packages/adapter-utilities/src/normalize-flag.ts index 7df525668..17fc2a2c8 100644 --- a/packages/adapter-utilities/src/normalize-flag.ts +++ b/packages/adapter-utilities/src/normalize-flag.ts @@ -3,7 +3,7 @@ import camelCase from 'lodash/camelCase.js'; const normalizeFlag = ( flagName: TFlagName, - flagValue?: TFlagVariation + flagValue?: TFlagVariation, ): TFlag => [ camelCase(flagName), // Multi variate flags contain a string or `null` - `false` seems more natural. diff --git a/packages/adapter-utilities/src/normalize-flags.ts b/packages/adapter-utilities/src/normalize-flags.ts index 001dd7863..afe40054c 100644 --- a/packages/adapter-utilities/src/normalize-flags.ts +++ b/packages/adapter-utilities/src/normalize-flags.ts @@ -4,20 +4,20 @@ import { normalizeFlag as defaultNormalizeFlag } from './normalize-flag'; const normalizeFlags = ( rawFlags: TFlags, - normalizer: typeof defaultNormalizeFlag = defaultNormalizeFlag + normalizer: typeof defaultNormalizeFlag = defaultNormalizeFlag, ): Record => Object.entries(rawFlags || {}).reduce( (normalizedFlags: TFlags, [flagName, flagValue]) => { const [normalizedFlagName, normalizedFlagValue]: TFlag = normalizer( flagName, - flagValue + flagValue, ); // Can't return expression as it is the assigned value normalizedFlags[normalizedFlagName] = normalizedFlagValue; return normalizedFlags; }, - {} + {}, ); export { normalizeFlags }; diff --git a/packages/adapter-utilities/test/denormalize-flag-name.spec.js b/packages/adapter-utilities/test/denormalize-flag-name.spec.js index 547831d09..1d6d23ec4 100644 --- a/packages/adapter-utilities/test/denormalize-flag-name.spec.js +++ b/packages/adapter-utilities/test/denormalize-flag-name.spec.js @@ -1,4 +1,5 @@ import { describe, expect, it } from 'vitest'; + import { denormalizeFlagName } from '../src/denormalize-flag-name'; describe('with camel case', () => { diff --git a/packages/adapter-utilities/test/expose-globally.spec.js b/packages/adapter-utilities/test/expose-globally.spec.js index f5b3a4cf8..f240795b1 100644 --- a/packages/adapter-utilities/test/expose-globally.spec.js +++ b/packages/adapter-utilities/test/expose-globally.spec.js @@ -1,5 +1,6 @@ import getGlobalThis from 'globalthis'; import { expect, it } from 'vitest'; + import { exposeGlobally } from '../src/expose-globally'; const testAdapter = { diff --git a/packages/adapter-utilities/test/normalize-flag.spec.js b/packages/adapter-utilities/test/normalize-flag.spec.js index c8d1e9059..4e5914e68 100644 --- a/packages/adapter-utilities/test/normalize-flag.spec.js +++ b/packages/adapter-utilities/test/normalize-flag.spec.js @@ -1,4 +1,5 @@ import { describe, expect, it } from 'vitest'; + import { normalizeFlag } from '../src/normalize-flag'; describe('with dashes', () => { diff --git a/packages/adapter-utilities/test/normalize-flags.spec.js b/packages/adapter-utilities/test/normalize-flags.spec.js index fbd4c489f..bfdfb7e58 100644 --- a/packages/adapter-utilities/test/normalize-flags.spec.js +++ b/packages/adapter-utilities/test/normalize-flags.spec.js @@ -1,4 +1,5 @@ import { describe, expect, it, vi } from 'vitest'; + import { normalizeFlag } from '../src/normalize-flag'; import { normalizeFlags } from '../src/normalize-flags'; diff --git a/packages/adapter-utilities/vitest.config.ts b/packages/adapter-utilities/vitest.config.ts index 516a3a1ec..fcd40d7ed 100644 --- a/packages/adapter-utilities/vitest.config.ts +++ b/packages/adapter-utilities/vitest.config.ts @@ -1,4 +1,5 @@ import { defineProject, mergeConfig } from 'vitest/config'; + import configShared from '../../vitest.shared.ts'; export default mergeConfig( @@ -7,5 +8,5 @@ export default mergeConfig( test: { environment: 'jsdom', }, - }) + }), ); diff --git a/packages/cache/package.json b/packages/cache/package.json index 3d2dc1894..2217e4556 100644 --- a/packages/cache/package.json +++ b/packages/cache/package.json @@ -2,14 +2,29 @@ "name": "@flopflip/cache", "version": "15.1.7", "description": "Caching for flipflop adapters", - "sideEffects": false, - "type": "module", - "exports": { - ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs" - } + "keywords": [ + "cache", + "client", + "feature-flags", + "feature-toggles" + ], + "homepage": "https://github.com/tdeekens/flopflip#readme", + "bugs": { + "url": "https://github.com/tdeekens/flopflip/issues" }, + "license": "MIT", + "author": "Tobias Deekens ", + "repository": { + "type": "git", + "url": "https://github.com/tdeekens/flopflip.git", + "directory": "packages/cache" + }, + "files": [ + "readme.md", + "dist/**" + ], + "type": "module", + "sideEffects": false, "main": "./dist/index.js", "typesVersions": { "*": { @@ -19,6 +34,15 @@ ] } }, + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, + "publishConfig": { + "access": "public" + }, "scripts": { "build": "rimraf dist && tsup", "check-types": "tsc --noEmit", @@ -26,30 +50,6 @@ "test:watch": "vitest", "dev": "tsup --watch --clean=false" }, - "files": [ - "readme.md", - "dist/**" - ], - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "https://github.com/tdeekens/flopflip.git", - "directory": "packages/cache" - }, - "author": "Tobias Deekens ", - "license": "MIT", - "bugs": { - "url": "https://github.com/tdeekens/flopflip/issues" - }, - "homepage": "https://github.com/tdeekens/flopflip#readme", - "keywords": [ - "feature-flags", - "feature-toggles", - "cache", - "client" - ], "dependencies": { "@flopflip/localstorage-cache": "workspace:*", "@flopflip/sessionstorage-cache": "workspace:*", diff --git a/packages/cache/src/cache.ts b/packages/cache/src/cache.ts index 89914220b..b0c521f38 100644 --- a/packages/cache/src/cache.ts +++ b/packages/cache/src/cache.ts @@ -18,7 +18,7 @@ export function encodeCacheContext(cacheContext: any) { const hashCode = [...encodedAsJson].reduce( (hash, c) => (Math.imul(31, hash) + c.charCodeAt(0)) | 0, - 0 + 0, ); const encodedCacheContext = Math.abs(hashCode).toString(); @@ -47,7 +47,7 @@ async function importCache(cacheIdentifier: TCacheIdentifiers) { async function getCache( cacheIdentifier: TCacheIdentifiers, adapterIdentifiers: TAdapterIdentifiers, - cacheContext?: any + cacheContext?: any, ) { const cacheModule = await importCache(cacheIdentifier); @@ -74,7 +74,7 @@ async function getCache( if (haveFlagsBeenWritten) { referenceCache.set( FLAGS_REFERENCE_CACHE_KEY, - [flagsCachePrefix, FLAGS_CACHE_KEY].join('/') + [flagsCachePrefix, FLAGS_CACHE_KEY].join('/'), ); } @@ -93,7 +93,7 @@ async function getCache( function getCachedFlags( cacheIdentifier: TCacheIdentifiers, - adapterIdentifiers: TAdapterIdentifiers + adapterIdentifiers: TAdapterIdentifiers, ): TFlags { const CACHE_PREFIX = getCachePrefix(adapterIdentifiers); @@ -113,7 +113,7 @@ function getCachedFlags( } } catch (_) { console.warn( - `@flopflip/cache: Failed to parse cached flags from ${cacheIdentifier}.` + `@flopflip/cache: Failed to parse cached flags from ${cacheIdentifier}.`, ); } } @@ -123,7 +123,7 @@ function getCachedFlags( function getAllCachedFlags( adapter: TAdapter, - cacheIdentifier?: TCacheIdentifiers + cacheIdentifier?: TCacheIdentifiers, ) { if (!cacheIdentifier) { return {}; @@ -135,7 +135,7 @@ function getAllCachedFlags( ...defaultFlags, ...getCachedFlags(cacheIdentifier, effectId), }), - {} + {}, ); } diff --git a/packages/cache/test/cache.spec.js b/packages/cache/test/cache.spec.js index 3a6da6b47..ee7754443 100644 --- a/packages/cache/test/cache.spec.js +++ b/packages/cache/test/cache.spec.js @@ -1,5 +1,6 @@ import { adapterIdentifiers, cacheIdentifiers } from '@flopflip/types'; import { describe, expect, it } from 'vitest'; + import { encodeCacheContext, getAllCachedFlags, @@ -18,7 +19,7 @@ describe('general caching', () => { const cache = await getCache( cacheIdentifiers.session, adapterIdentifiers.memory, - cacheContext + cacheContext, ); cache.set(flags); @@ -33,7 +34,7 @@ describe('general caching', () => { const cache = await getCache( cacheIdentifiers.session, adapterIdentifiers.memory, - cacheContext + cacheContext, ); cache.set(flags); @@ -49,15 +50,15 @@ describe('general caching', () => { const cache = await getCache( cacheIdentifiers.session, adapterIdentifiers.memory, - cacheContext + cacheContext, ); cache.set(flags); expect(sessionStorage.getItem).toHaveBeenLastCalledWith( expect.stringContaining( - `${getCachePrefix(adapterIdentifiers.memory)}/${encodeCacheContext(cacheContext)}/flags` - ) + `${getCachePrefix(adapterIdentifiers.memory)}/${encodeCacheContext(cacheContext)}/flags`, + ), ); }); }); @@ -71,17 +72,17 @@ describe('flag caching', () => { const cache = await getCache( cacheIdentifiers.session, adapterIdentifiers.memory, - cacheContext + cacheContext, ); cache.set(flags); expect( - getCachedFlags(cacheIdentifiers.session, adapterIdentifiers.memory) + getCachedFlags(cacheIdentifiers.session, adapterIdentifiers.memory), ).toStrictEqual(flags); expect(sessionStorage.getItem).toHaveBeenLastCalledWith( - `${getCachePrefix(adapterIdentifiers.memory)}/${encodeCacheContext(cacheContext)}/flags` + `${getCachePrefix(adapterIdentifiers.memory)}/${encodeCacheContext(cacheContext)}/flags`, ); }); }); @@ -97,7 +98,7 @@ describe('flag caching', () => { const localstorageAdapterCache = await getCache( cacheIdentifiers.session, adapterIdentifiers.localstorage, - cacheContext + cacheContext, ); localstorageAdapterCache.set(localstorageAdapterFlags); @@ -107,7 +108,7 @@ describe('flag caching', () => { }; expect( - getAllCachedFlags(fakeAdapter, cacheIdentifiers.session) + getAllCachedFlags(fakeAdapter, cacheIdentifiers.session), ).toStrictEqual({ ...memoryAdapterFlags, ...localstorageAdapterFlags }); }); }); diff --git a/packages/cache/vitest.config.ts b/packages/cache/vitest.config.ts index 516a3a1ec..fcd40d7ed 100644 --- a/packages/cache/vitest.config.ts +++ b/packages/cache/vitest.config.ts @@ -1,4 +1,5 @@ import { defineProject, mergeConfig } from 'vitest/config'; + import configShared from '../../vitest.shared.ts'; export default mergeConfig( @@ -7,5 +8,5 @@ export default mergeConfig( test: { environment: 'jsdom', }, - }) + }), ); diff --git a/packages/combine-adapters/package.json b/packages/combine-adapters/package.json index 48f147c98..b72f013c5 100644 --- a/packages/combine-adapters/package.json +++ b/packages/combine-adapters/package.json @@ -2,15 +2,30 @@ "name": "@flopflip/combine-adapters", "version": "15.1.7", "description": "An adapter which combines other adapters for flipflop", - "sideEffects": false, + "keywords": [ + "client", + "feature-flags", + "feature-toggles", + "memory" + ], + "homepage": "https://github.com/tdeekens/flopflip#readme", + "bugs": { + "url": "https://github.com/tdeekens/flopflip/issues" + }, + "license": "MIT", + "author": "Tobias Deekens ", + "repository": { + "type": "git", + "url": "https://github.com/tdeekens/flopflip.git", + "directory": "packages/combine-adapters" + }, + "files": [ + "readme.md", + "dist/**" + ], "type": "module", + "sideEffects": false, "main": "./dist/index.js", - "exports": { - ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs" - } - }, "typesVersions": { "*": { "*": [ @@ -19,10 +34,15 @@ ] } }, - "files": [ - "readme.md", - "dist/**" - ], + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, + "publishConfig": { + "access": "public" + }, "scripts": { "build": "rimraf dist && tsup", "check-types": "tsc --noEmit", @@ -30,26 +50,6 @@ "test:watch": "vitest", "dev": "tsup --watch --clean=false" }, - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "https://github.com/tdeekens/flopflip.git", - "directory": "packages/combine-adapters" - }, - "author": "Tobias Deekens ", - "license": "MIT", - "bugs": { - "url": "https://github.com/tdeekens/flopflip/issues" - }, - "homepage": "https://github.com/tdeekens/flopflip#readme", - "keywords": [ - "feature-flags", - "feature-toggles", - "memory", - "client" - ], "dependencies": { "@babel/runtime": "7.28.6", "@flopflip/adapter-utilities": "workspace:*", diff --git a/packages/combine-adapters/src/adapter.ts b/packages/combine-adapters/src/adapter.ts index 03bf826a9..c047f93aa 100644 --- a/packages/combine-adapters/src/adapter.ts +++ b/packages/combine-adapters/src/adapter.ts @@ -60,17 +60,17 @@ class CombineAdapters implements TCombinedAdapterInterface { updateFlags = (flags: TFlags, options?: TUpdateFlagsOptions) => { const isAdapterConfigured = this.getIsConfigurationStatus( - AdapterConfigurationStatus.Configured + AdapterConfigurationStatus.Configured, ); const hasCombinedAdapters = this.#getHasCombinedAdapters(); warning( isAdapterConfigured, - '@flopflip/combine-adapters: adapter is not configured. Flags can not be updated before.' + '@flopflip/combine-adapters: adapter is not configured. Flags can not be updated before.', ); warning( hasCombinedAdapters, - '@flopflip/combine-adapters: adapter has no combined adapters. Please combine before updating flags.' + '@flopflip/combine-adapters: adapter has no combined adapters. Please combine before updating flags.', ); if (!isAdapterConfigured || !hasCombinedAdapters) { @@ -89,18 +89,18 @@ class CombineAdapters implements TCombinedAdapterInterface { async configure( adapterArgs: TCombinedAdapterArgs, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) { const hasCombinedAdapters = this.#getHasCombinedAdapters(); const hasArgsForAllAdapters = this.#getHasArgsForAllAdapters(adapterArgs); warning( hasCombinedAdapters, - '@flopflip/combine-adapters: adapter has no combined adapters. Please combine before reconfiguring flags.' + '@flopflip/combine-adapters: adapter has no combined adapters. Please combine before reconfiguring flags.', ); warning( hasArgsForAllAdapters, - '@flopflip/combine-adapters: not all adapters have args. Please provide args for all adapters.' + '@flopflip/combine-adapters: not all adapters have args. Please provide args for all adapters.', ); if (!hasCombinedAdapters || !hasArgsForAllAdapters) { @@ -132,12 +132,12 @@ class CombineAdapters implements TCombinedAdapterInterface { onFlagsStateChange: adapterEventHandlers.onFlagsStateChange, onStatusStateChange: adapterEventHandlers.onStatusStateChange, }); - }) + }), ).then((allInitializationStatus) => { const haveAllAdaptersInitializedSuccessfully = allInitializationStatus.every( ({ initializationStatus }) => - initializationStatus === AdapterInitializationStatus.Succeeded + initializationStatus === AdapterInitializationStatus.Succeeded, ); // NOTE: We consider this adapter configured if all adapters have been asked to do so @@ -166,7 +166,7 @@ class CombineAdapters implements TCombinedAdapterInterface { async reconfigure( adapterArgs: TCombinedAdapterArgs, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) { this.setConfigurationStatus(AdapterConfigurationStatus.Configuring); @@ -175,11 +175,11 @@ class CombineAdapters implements TCombinedAdapterInterface { warning( hasCombinedAdapters, - '@flopflip/combine-adapters: adapter has no combined adapters. Please combine before reconfiguring flags.' + '@flopflip/combine-adapters: adapter has no combined adapters. Please combine before reconfiguring flags.', ); warning( hasArgsForAllAdapters, - '@flopflip/combine-adapters: not all adapters have args. Please provide args for all adapters.' + '@flopflip/combine-adapters: not all adapters have args. Please provide args for all adapters.', ); if (!hasCombinedAdapters || !hasArgsForAllAdapters) { @@ -196,12 +196,12 @@ class CombineAdapters implements TCombinedAdapterInterface { onFlagsStateChange: adapterEventHandlers.onFlagsStateChange, onStatusStateChange: adapterEventHandlers.onStatusStateChange, }); - }) + }), ).then((allInitializationStatus) => { const haveAllAdaptersInitializedSuccessfully = allInitializationStatus.every( ({ initializationStatus }) => - initializationStatus === AdapterInitializationStatus.Succeeded + initializationStatus === AdapterInitializationStatus.Succeeded, ); // NOTE: We consider this adapter reconfigured if all adapters have been asked to do so @@ -244,7 +244,7 @@ class CombineAdapters implements TCombinedAdapterInterface { async waitUntilConfigured() { return Promise.all( - this.#adapters.map(async (adapter) => adapter?.waitUntilConfigured?.()) + this.#adapters.map(async (adapter) => adapter?.waitUntilConfigured?.()), ); } diff --git a/packages/combine-adapters/test/adapter.spec.js b/packages/combine-adapters/test/adapter.spec.js index e54cbf39e..df82ee451 100644 --- a/packages/combine-adapters/test/adapter.spec.js +++ b/packages/combine-adapters/test/adapter.spec.js @@ -7,6 +7,7 @@ import { import getGlobalThis from 'globalthis'; import warning from 'tiny-warning'; import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; + import { adapter } from '../src/adapter'; vi.mock('tiny-warning', { @@ -46,7 +47,7 @@ describe('when combining', () => { it('should indicate that the adapter is not configured', () => { expect( - adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured) + adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured), ).toBe(false); }); @@ -57,14 +58,14 @@ describe('when combining', () => { beforeEach(async () => { configurationResult = await adapter.configure( adapterArgs, - adapterEventHandlers + adapterEventHandlers, ); }); it('should invoke and trigger `warning`', () => { expect(warning).toHaveBeenCalledWith( false, - expect.stringContaining('adapter has no combined adapters') + expect.stringContaining('adapter has no combined adapters'), ); }); @@ -84,14 +85,14 @@ describe('when combining', () => { adapterArgs = createAdapterArgs({ [memoryAdapter.id]: null }); configurationResult = await adapter.configure( adapterArgs, - adapterEventHandlers + adapterEventHandlers, ); }); it('should invoke and trigger `warning`', () => { expect(warning).toHaveBeenCalledWith( false, - expect.stringContaining('not all adapters have args') + expect.stringContaining('not all adapters have args'), ); }); @@ -113,13 +114,13 @@ describe('when combining', () => { const memoryAdapterConfigureSpy = vi.spyOn(memoryAdapter, 'configure'); const localstorageAdapterConfigureSpy = vi.spyOn( localstorageAdapter, - 'configure' + 'configure', ); beforeEach(async () => { configurationResult = await adapter.configure( adapterArgs, - adapterEventHandlers + adapterEventHandlers, ); }); @@ -138,24 +139,24 @@ describe('when combining', () => { status: { configurationStatus: AdapterConfigurationStatus.Configuring, }, - }) + }), ); }); it('should indicate that the adapter is configured', () => { expect( - adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured) + adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured), ).toBe(true); }); it('should invoke `configure` on all adapters', () => { expect(memoryAdapterConfigureSpy).toHaveBeenCalledWith( adapterArgs[memoryAdapter.id], - expect.anything() + expect.anything(), ); expect(localstorageAdapterConfigureSpy).toHaveBeenCalledWith( adapterArgs[localstorageAdapter.id], - expect.anything() + expect.anything(), ); }); @@ -176,7 +177,7 @@ describe('when combining', () => { status: { configurationStatus: AdapterConfigurationStatus.Configured, }, - }) + }), ); }); }); @@ -189,7 +190,7 @@ describe('when combining', () => { status: { configurationStatus: AdapterConfigurationStatus.Configured, }, - }) + }), ); }); }); @@ -202,7 +203,7 @@ describe('when combining', () => { status: { configurationStatus: AdapterConfigurationStatus.Configured, }, - }) + }), ); }); }); @@ -296,17 +297,17 @@ describe('when combining', () => { const memoryAdapterReconfigureSpy = vi.spyOn( memoryAdapter, - 'reconfigure' + 'reconfigure', ); const localstorageAdapterReconfigureSpy = vi.spyOn( localstorageAdapter, - 'reconfigure' + 'reconfigure', ); beforeEach(async () => { configurationResult = await adapter.reconfigure( { [localstorageAdapter.id]: { user }, [memoryAdapter.id]: { user } }, - adapterEventHandlers + adapterEventHandlers, ); adapterEventHandlers = createAdapterEventHandlers(); }); @@ -315,20 +316,20 @@ describe('when combining', () => { expect(configurationResult).toEqual( expect.objectContaining({ initializationStatus: 0, - }) + }), ); }); it('should invoke `reconfigure` on all adapters', () => { expect(memoryAdapterReconfigureSpy).toHaveBeenCalledWith( { user }, - expect.anything() + expect.anything(), ); expect(localstorageAdapterReconfigureSpy).toHaveBeenCalledWith( { user, }, - expect.anything() + expect.anything(), ); }); }); @@ -351,7 +352,7 @@ describe('when combining', () => { beforeEach(async () => { configurationResult = await adapter.configure( adapterArgs, - adapterEventHandlers + adapterEventHandlers, ); }); @@ -359,13 +360,13 @@ describe('when combining', () => { expect(configurationResult).toEqual( expect.objectContaining({ initializationStatus: 1, - }) + }), ); }); it('should indicate that the adapter is configured regardless', () => { expect( - adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured) + adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured), ).toBe(true); }); }); @@ -384,8 +385,8 @@ describe('when combining', () => { it('should reset the configuration status', () => { expect( adapter.getIsConfigurationStatus( - AdapterConfigurationStatus.Unconfigured - ) + AdapterConfigurationStatus.Unconfigured, + ), ).toBe(true); }); }); diff --git a/packages/combine-adapters/vitest.config.ts b/packages/combine-adapters/vitest.config.ts index 516a3a1ec..fcd40d7ed 100644 --- a/packages/combine-adapters/vitest.config.ts +++ b/packages/combine-adapters/vitest.config.ts @@ -1,4 +1,5 @@ import { defineProject, mergeConfig } from 'vitest/config'; + import configShared from '../../vitest.shared.ts'; export default mergeConfig( @@ -7,5 +8,5 @@ export default mergeConfig( test: { environment: 'jsdom', }, - }) + }), ); diff --git a/packages/cypress-plugin/package.json b/packages/cypress-plugin/package.json index cddb6c47f..056ecea87 100644 --- a/packages/cypress-plugin/package.json +++ b/packages/cypress-plugin/package.json @@ -2,14 +2,30 @@ "name": "@flopflip/cypress-plugin", "version": "15.1.7", "description": "A plugin for Cypress change feature toggles in Cypress runs", - "sideEffects": false, - "type": "module", - "exports": { - ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs" - } + "keywords": [ + "LaunchDarkly", + "client", + "feature-flags", + "feature-toggles", + "react" + ], + "homepage": "https://github.com/tdeekens/flopflip#readme", + "bugs": { + "url": "https://github.com/tdeekens/flopflip/issues" + }, + "license": "MIT", + "author": "Tobias Deekens ", + "repository": { + "type": "git", + "url": "https://github.com/tdeekens/flopflip.git", + "directory": "packages/cypress-plugin" }, + "files": [ + "readme.md", + "dist/**" + ], + "type": "module", + "sideEffects": false, "main": "./dist/index.js", "browser": "./dist/index.js", "typesVersions": { @@ -20,6 +36,15 @@ ] } }, + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, + "publishConfig": { + "access": "public" + }, "scripts": { "build": "rimraf dist && tsup", "check-types": "tsc --noEmit", @@ -27,24 +52,9 @@ "test:watch": "exit 0", "dev": "tsup --watch --clean=false" }, - "files": [ - "readme.md", - "dist/**" - ], - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "https://github.com/tdeekens/flopflip.git", - "directory": "packages/cypress-plugin" - }, - "author": "Tobias Deekens ", - "license": "MIT", - "bugs": { - "url": "https://github.com/tdeekens/flopflip/issues" + "dependencies": { + "@flopflip/types": "workspace:*" }, - "homepage": "https://github.com/tdeekens/flopflip#readme", "devDependencies": { "@flopflip/tsconfig": "workspace:*", "cypress": "13.6.4", @@ -52,15 +62,5 @@ }, "peerDependencies": { "cypress": "13.x || 14.x" - }, - "dependencies": { - "@flopflip/types": "workspace:*" - }, - "keywords": [ - "react", - "feature-flags", - "feature-toggles", - "LaunchDarkly", - "client" - ] + } } diff --git a/packages/cypress-plugin/src/plugin.ts b/packages/cypress-plugin/src/plugin.ts index 78cdf6f49..1c6d0fd5a 100644 --- a/packages/cypress-plugin/src/plugin.ts +++ b/packages/cypress-plugin/src/plugin.ts @@ -27,7 +27,7 @@ const addCommands = (options: TCypressPluginAddCommandOptions) => { if (!flopflipAdapterGlobal) { throw new Error( - '@flopflip/cypress: namespace or adapter of the passed id does not exist. Make sure you use one and the specified adapter.' + '@flopflip/cypress: namespace or adapter of the passed id does not exist. Make sure you use one and the specified adapter.', ); } @@ -42,7 +42,7 @@ const addCommands = (options: TCypressPluginAddCommandOptions) => { flopflipAdapterGlobal?.updateFlags(flags, { unsubscribeFlags: true, }); - }) + }), ); }; diff --git a/packages/graphql-adapter/package.json b/packages/graphql-adapter/package.json index 0c275c5b9..d39a83148 100644 --- a/packages/graphql-adapter/package.json +++ b/packages/graphql-adapter/package.json @@ -2,14 +2,29 @@ "name": "@flopflip/graphql-adapter", "version": "15.1.7", "description": "An GraphQL adapter for flipflop", - "sideEffects": false, - "type": "module", - "exports": { - ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs" - } + "keywords": [ + "client", + "feature-flags", + "feature-toggles", + "graphql" + ], + "homepage": "https://github.com/tdeekens/flopflip#readme", + "bugs": { + "url": "https://github.com/tdeekens/flopflip/issues" }, + "license": "MIT", + "author": "Tobias Deekens ", + "repository": { + "type": "git", + "url": "https://github.com/tdeekens/flopflip.git", + "directory": "packages/graphql-adapter" + }, + "files": [ + "readme.md", + "dist/**" + ], + "type": "module", + "sideEffects": false, "main": "./dist/index.js", "typesVersions": { "*": { @@ -19,6 +34,15 @@ ] } }, + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, + "publishConfig": { + "access": "public" + }, "scripts": { "build": "rimraf dist && tsup", "check-types": "tsc --noEmit", @@ -26,30 +50,6 @@ "test:watch": "vitest", "dev": "tsup --watch --clean=false" }, - "files": [ - "readme.md", - "dist/**" - ], - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "https://github.com/tdeekens/flopflip.git", - "directory": "packages/graphql-adapter" - }, - "author": "Tobias Deekens ", - "license": "MIT", - "bugs": { - "url": "https://github.com/tdeekens/flopflip/issues" - }, - "homepage": "https://github.com/tdeekens/flopflip#readme", - "keywords": [ - "feature-flags", - "feature-toggles", - "graphql", - "client" - ], "dependencies": { "@babel/runtime": "7.28.6", "@flopflip/adapter-utilities": "workspace:*", diff --git a/packages/graphql-adapter/src/adapter.ts b/packages/graphql-adapter/src/adapter.ts index e69bd135c..a02f601e0 100644 --- a/packages/graphql-adapter/src/adapter.ts +++ b/packages/graphql-adapter/src/adapter.ts @@ -85,7 +85,7 @@ class GraphQlAdapter implements TGraphQlAdapterInterface { }; readonly #fetchFlags = async ( - adapterArgs: TGraphQlAdapterArgs + adapterArgs: TGraphQlAdapterArgs, ): Promise => { const fetcher = adapterArgs.fetcher ?? fetch; @@ -93,7 +93,7 @@ class GraphQlAdapter implements TGraphQlAdapterInterface { method: 'POST', headers: { 'Content-Type': 'application/json', - ...(adapterArgs.getRequestHeaders?.(adapterArgs) ?? {}), + ...adapterArgs.getRequestHeaders?.(adapterArgs), }, body: JSON.stringify({ query: adapterArgs.query, @@ -125,7 +125,7 @@ class GraphQlAdapter implements TGraphQlAdapterInterface { const cache = await getCache( adapterArgs.cacheIdentifier, adapterIdentifiers.graphql, - { key: this.#adapterState.user?.key } + { key: this.#adapterState.user?.key }, ); cache.set(nextFlags); @@ -147,12 +147,12 @@ class GraphQlAdapter implements TGraphQlAdapterInterface { updateFlags: TFlagsUpdateFunction = (flags, options) => { const isAdapterConfigured = this.getIsConfigurationStatus( - AdapterConfigurationStatus.Configured + AdapterConfigurationStatus.Configured, ); warning( isAdapterConfigured, - '@flopflip/graphql-adapter: adapter not configured. Flags can not be updated before.' + '@flopflip/graphql-adapter: adapter not configured. Flags can not be updated before.', ); if (!isAdapterConfigured) { @@ -165,7 +165,7 @@ class GraphQlAdapter implements TGraphQlAdapterInterface { (updatedFlags, [flagName, flagValue]) => { const [normalizedFlagName, normalizedFlagValue] = normalizeFlag( flagName, - flagValue + flagValue, ); if (this.#getIsFlagLocked(normalizedFlagName)) { @@ -183,7 +183,7 @@ class GraphQlAdapter implements TGraphQlAdapterInterface { return updated; }, - {} + {}, ); const nextFlags: TFlags = { @@ -197,7 +197,7 @@ class GraphQlAdapter implements TGraphQlAdapterInterface { async configure( adapterArgs: TGraphQlAdapterArgs, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) { const handleFlagsChange = (nextFlags: TFlagsChange['flags']) => { if (this.#getIsAdapterUnsubscribed()) { @@ -235,7 +235,7 @@ class GraphQlAdapter implements TGraphQlAdapterInterface { const cache = await getCache( adapterArgs.cacheIdentifier, adapterIdentifiers.graphql, - { key: this.#adapterState.user?.key } + { key: this.#adapterState.user?.key }, ); cachedFlags = cache.get(); @@ -244,7 +244,7 @@ class GraphQlAdapter implements TGraphQlAdapterInterface { this.#adapterState.flags = cachedFlags; this.#adapterState.emitter.emit( 'flagsStateChange', - cachedFlags as TFlags + cachedFlags as TFlags, ); } } @@ -259,7 +259,7 @@ class GraphQlAdapter implements TGraphQlAdapterInterface { const cache = await getCache( adapterArgs.cacheIdentifier, adapterIdentifiers.graphql, - { key: this.#adapterState.user?.key } + { key: this.#adapterState.user?.key }, ); cache.set(flags); @@ -281,13 +281,13 @@ class GraphQlAdapter implements TGraphQlAdapterInterface { async reconfigure( adapterArgs: TGraphQlAdapterArgs, - _adapterEventHandlers: TAdapterEventHandlers + _adapterEventHandlers: TAdapterEventHandlers, ) { if (!this.getIsConfigurationStatus(AdapterConfigurationStatus.Configured)) { return Promise.reject( new Error( - '@flopflip/graphql-adapter: please configure adapter before reconfiguring.' - ) + '@flopflip/graphql-adapter: please configure adapter before reconfiguring.', + ), ); } @@ -297,7 +297,7 @@ class GraphQlAdapter implements TGraphQlAdapterInterface { const cache = await getCache( adapterArgs.cacheIdentifier, adapterIdentifiers.graphql, - { key: this.#adapterState.user?.key } + { key: this.#adapterState.user?.key }, ); cache.unset(); @@ -331,7 +331,7 @@ class GraphQlAdapter implements TGraphQlAdapterInterface { } else { this.#adapterState.emitter.on( this.#__internalConfiguredStatusChange__, - resolve + resolve, ); } }); diff --git a/packages/graphql-adapter/test/adapter.spec.js b/packages/graphql-adapter/test/adapter.spec.js index 0dbd04527..bd178f476 100644 --- a/packages/graphql-adapter/test/adapter.spec.js +++ b/packages/graphql-adapter/test/adapter.spec.js @@ -3,6 +3,7 @@ import { AdapterConfigurationStatus } from '@flopflip/types'; import getGlobalThis from 'globalthis'; import warning from 'tiny-warning'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + import { adapter } from '../src/adapter'; vi.mock('tiny-warning'); @@ -17,7 +18,7 @@ describe('when configuring', () => { describe('when not configured', () => { it('should indicate that the adapter is not configured', () => { expect( - adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured) + adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured), ).toBe(false); }); @@ -58,7 +59,7 @@ describe('when configured', () => { vi.useFakeTimers(); configurationResult = await adapter.configure( adapterArgs, - adapterEventHandlers + adapterEventHandlers, ); }); @@ -66,14 +67,14 @@ describe('when configured', () => { expect(configurationResult).toEqual( expect.objectContaining({ initializationStatus: 0, - }) + }), ); }); it('should invoke the fetcher with uri', () => { expect(adapterArgs.fetcher).toHaveBeenCalledWith( adapterArgs.uri, - expect.anything() + expect.anything(), ); }); @@ -96,7 +97,7 @@ describe('when configured', () => { it('should indicate that the adapter is configured', () => { expect( - adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured) + adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured), ).toBe(true); }); @@ -159,13 +160,13 @@ describe('when configured', () => { beforeEach(async () => { sessionStorage.getItem.mockReturnValueOnce( - JSON.stringify({ cached: true }) + JSON.stringify({ cached: true }), ); adapterEventHandlers = createAdapterEventHandlers(); vi.useFakeTimers(); configurationResult = await adapter.configure( adapterArgs, - adapterEventHandlers + adapterEventHandlers, ); }); @@ -177,13 +178,13 @@ describe('when configured', () => { expect(configurationResult).toEqual( expect.objectContaining({ initializationStatus: 0, - }) + }), ); }); it('should restore cached flags', () => { expect(sessionStorage.getItem).toHaveBeenCalledWith( - `@flopflip/graphql-adapter/${encodeCacheContext(adapterArgs.user)}/flags` + `@flopflip/graphql-adapter/${encodeCacheContext(adapterArgs.user)}/flags`, ); expect(adapterEventHandlers.onFlagsStateChange).toHaveBeenCalledWith({ @@ -198,9 +199,9 @@ describe('when configured', () => { expect( JSON.parse( sessionStorage.getItem( - `@flopflip/graphql-adapter/${encodeCacheContext(adapterArgs.user)}/flags` - ) - ) + `@flopflip/graphql-adapter/${encodeCacheContext(adapterArgs.user)}/flags`, + ), + ), ).toStrictEqual({ disabled: false, enabled: true }); }); @@ -217,13 +218,13 @@ describe('when configured', () => { describe('with lazy cache mode', () => { beforeEach(async () => { sessionStorage.getItem.mockReturnValueOnce( - JSON.stringify({ cached: true }) + JSON.stringify({ cached: true }), ); adapterEventHandlers = createAdapterEventHandlers(); vi.useFakeTimers(); configurationResult = await adapter.configure( adapterArgs, - adapterEventHandlers + adapterEventHandlers, ); }); @@ -322,7 +323,7 @@ describe('when configured', () => { expect(configurationResult).toEqual( expect.objectContaining({ initializationStatus: 0, - }) + }), ); }); @@ -350,7 +351,7 @@ describe('when configured', () => { it('should reset cache', () => { expect(sessionStorage.removeItem).toHaveBeenCalledWith( - `@flopflip/graphql-adapter/${encodeCacheContext(adapterArgs.user)}/flags` + `@flopflip/graphql-adapter/${encodeCacheContext(adapterArgs.user)}/flags`, ); }); }); @@ -394,7 +395,7 @@ describe('when configured', () => { it('should indicate that the adapter is not configured', () => { expect( - adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured) + adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured), ).toBe(false); }); diff --git a/packages/graphql-adapter/vitest.config.ts b/packages/graphql-adapter/vitest.config.ts index 516a3a1ec..fcd40d7ed 100644 --- a/packages/graphql-adapter/vitest.config.ts +++ b/packages/graphql-adapter/vitest.config.ts @@ -1,4 +1,5 @@ import { defineProject, mergeConfig } from 'vitest/config'; + import configShared from '../../vitest.shared.ts'; export default mergeConfig( @@ -7,5 +8,5 @@ export default mergeConfig( test: { environment: 'jsdom', }, - }) + }), ); diff --git a/packages/http-adapter/package.json b/packages/http-adapter/package.json index d6885b424..cfd8e1431 100644 --- a/packages/http-adapter/package.json +++ b/packages/http-adapter/package.json @@ -2,14 +2,29 @@ "name": "@flopflip/http-adapter", "version": "15.1.7", "description": "An HTTP adapter for flipflop", - "sideEffects": false, - "type": "module", - "exports": { - ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs" - } + "keywords": [ + "client", + "feature-flags", + "feature-toggles", + "graphql" + ], + "homepage": "https://github.com/tdeekens/flopflip#readme", + "bugs": { + "url": "https://github.com/tdeekens/flopflip/issues" }, + "license": "MIT", + "author": "Tobias Deekens ", + "repository": { + "type": "git", + "url": "https://github.com/tdeekens/flopflip.git", + "directory": "packages/http-adapter" + }, + "files": [ + "readme.md", + "dist/**" + ], + "type": "module", + "sideEffects": false, "main": "./dist/index.js", "typesVersions": { "*": { @@ -19,6 +34,15 @@ ] } }, + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, + "publishConfig": { + "access": "public" + }, "scripts": { "build": "rimraf dist && tsup", "check-types": "tsc --noEmit", @@ -26,30 +50,6 @@ "test:watch": "vitest", "dev": "tsup --watch --clean=false" }, - "files": [ - "readme.md", - "dist/**" - ], - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "https://github.com/tdeekens/flopflip.git", - "directory": "packages/http-adapter" - }, - "author": "Tobias Deekens ", - "license": "MIT", - "bugs": { - "url": "https://github.com/tdeekens/flopflip/issues" - }, - "homepage": "https://github.com/tdeekens/flopflip#readme", - "keywords": [ - "feature-flags", - "feature-toggles", - "graphql", - "client" - ], "dependencies": { "@babel/runtime": "7.28.6", "@flopflip/adapter-utilities": "workspace:*", diff --git a/packages/http-adapter/src/adapter.ts b/packages/http-adapter/src/adapter.ts index a5fcf080e..d3c94ba66 100644 --- a/packages/http-adapter/src/adapter.ts +++ b/packages/http-adapter/src/adapter.ts @@ -84,7 +84,7 @@ class HttpAdapter implements THttpAdapterInterface { }; readonly #fetchFlags = async ( - adapterArgs: THttpAdapterArgs + adapterArgs: THttpAdapterArgs, ): Promise => { const flags = await adapterArgs.execute(adapterArgs); @@ -108,7 +108,7 @@ class HttpAdapter implements THttpAdapterInterface { const cache = await getCache( adapterArgs.cacheIdentifier, adapterIdentifiers.http, - { key: this.#adapterState.user?.key } + { key: this.#adapterState.user?.key }, ); cache.set(nextFlags); @@ -130,12 +130,12 @@ class HttpAdapter implements THttpAdapterInterface { updateFlags: TFlagsUpdateFunction = (flags, options) => { const isAdapterConfigured = this.getIsConfigurationStatus( - AdapterConfigurationStatus.Configured + AdapterConfigurationStatus.Configured, ); warning( isAdapterConfigured, - '@flopflip/http-adapter: adapter not configured. Flags can not be updated before.' + '@flopflip/http-adapter: adapter not configured. Flags can not be updated before.', ); if (!isAdapterConfigured) { @@ -148,7 +148,7 @@ class HttpAdapter implements THttpAdapterInterface { (updatedFlags, [flagName, flagValue]) => { const [normalizedFlagName, normalizedFlagValue] = normalizeFlag( flagName, - flagValue + flagValue, ); if (this.#getIsFlagLocked(normalizedFlagName)) { @@ -166,7 +166,7 @@ class HttpAdapter implements THttpAdapterInterface { return updated; }, - {} + {}, ); const nextFlags: TFlags = { @@ -180,7 +180,7 @@ class HttpAdapter implements THttpAdapterInterface { async configure( adapterArgs: THttpAdapterArgs, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) { const handleFlagsChange = (nextFlags: TFlagsChange['flags']) => { if (this.#getIsAdapterUnsubscribed()) { @@ -218,7 +218,7 @@ class HttpAdapter implements THttpAdapterInterface { const cache = await getCache( adapterArgs.cacheIdentifier, adapterIdentifiers.http, - { key: this.#adapterState.user?.key } + { key: this.#adapterState.user?.key }, ); cachedFlags = cache.get(); @@ -227,7 +227,7 @@ class HttpAdapter implements THttpAdapterInterface { this.#adapterState.flags = cachedFlags; this.#adapterState.emitter.emit( 'flagsStateChange', - cachedFlags as TFlags + cachedFlags as TFlags, ); } } @@ -242,7 +242,7 @@ class HttpAdapter implements THttpAdapterInterface { const cache = await getCache( adapterArgs.cacheIdentifier, adapterIdentifiers.http, - { key: this.#adapterState.user?.key } + { key: this.#adapterState.user?.key }, ); cache.set(flags); @@ -264,13 +264,13 @@ class HttpAdapter implements THttpAdapterInterface { async reconfigure( adapterArgs: THttpAdapterArgs, - _adapterEventHandlers: TAdapterEventHandlers + _adapterEventHandlers: TAdapterEventHandlers, ) { if (!this.getIsConfigurationStatus(AdapterConfigurationStatus.Configured)) { return Promise.reject( new Error( - '@flopflip/http-adapter: please configure adapter before reconfiguring.' - ) + '@flopflip/http-adapter: please configure adapter before reconfiguring.', + ), ); } @@ -283,7 +283,7 @@ class HttpAdapter implements THttpAdapterInterface { const cache = await getCache( adapterArgs.cacheIdentifier, adapterIdentifiers.http, - { key: this.#adapterState.user?.key } + { key: this.#adapterState.user?.key }, ); cache.unset(); @@ -316,7 +316,7 @@ class HttpAdapter implements THttpAdapterInterface { } else { this.#adapterState.emitter.on( this.#__internalConfiguredStatusChange__, - resolve + resolve, ); } }); diff --git a/packages/http-adapter/test/adapter.spec.js b/packages/http-adapter/test/adapter.spec.js index 8fb8213bf..ce6a62648 100644 --- a/packages/http-adapter/test/adapter.spec.js +++ b/packages/http-adapter/test/adapter.spec.js @@ -3,6 +3,7 @@ import { AdapterConfigurationStatus } from '@flopflip/types'; import getGlobalThis from 'globalthis'; import warning from 'tiny-warning'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + import { adapter } from '../src/adapter'; vi.mock('tiny-warning'); @@ -17,7 +18,7 @@ describe('when configuring', () => { describe('when not configured', () => { it('should indicate that the adapter is not configured', () => { expect( - adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured) + adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured), ).toBe(false); }); @@ -51,7 +52,7 @@ describe('when configured', () => { vi.useFakeTimers(); configurationResult = await adapter.configure( adapterArgs, - adapterEventHandlers + adapterEventHandlers, ); }); @@ -59,7 +60,7 @@ describe('when configured', () => { expect(configurationResult).toEqual( expect.objectContaining({ initializationStatus: 0, - }) + }), ); }); @@ -74,7 +75,7 @@ describe('when configured', () => { it('should indicate that the adapter is configured', () => { expect( - adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured) + adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured), ).toBe(true); }); @@ -117,13 +118,13 @@ describe('when configured', () => { beforeEach(async () => { sessionStorage.getItem.mockReturnValueOnce( - JSON.stringify({ cached: true }) + JSON.stringify({ cached: true }), ); adapterEventHandlers = createAdapterEventHandlers(); vi.useFakeTimers(); configurationResult = await adapter.configure( adapterArgs, - adapterEventHandlers + adapterEventHandlers, ); }); @@ -135,13 +136,13 @@ describe('when configured', () => { expect(configurationResult).toEqual( expect.objectContaining({ initializationStatus: 0, - }) + }), ); }); it('should restore cached flags', () => { expect(sessionStorage.getItem).toHaveBeenCalledWith( - `@flopflip/http-adapter/${encodeCacheContext(adapterArgs.user)}/flags` + `@flopflip/http-adapter/${encodeCacheContext(adapterArgs.user)}/flags`, ); expect(adapterEventHandlers.onFlagsStateChange).toHaveBeenCalledWith({ @@ -156,9 +157,9 @@ describe('when configured', () => { expect( JSON.parse( sessionStorage.getItem( - `@flopflip/http-adapter/${encodeCacheContext(adapterArgs.user)}/flags` - ) - ) + `@flopflip/http-adapter/${encodeCacheContext(adapterArgs.user)}/flags`, + ), + ), ).toStrictEqual({ disabled: false, enabled: true }); }); @@ -175,13 +176,13 @@ describe('when configured', () => { describe('with lazy cache mode', () => { beforeEach(async () => { sessionStorage.getItem.mockReturnValueOnce( - JSON.stringify({ cached: true }) + JSON.stringify({ cached: true }), ); adapterEventHandlers = createAdapterEventHandlers(); vi.useFakeTimers(); configurationResult = await adapter.configure( adapterArgs, - adapterEventHandlers + adapterEventHandlers, ); }); @@ -281,7 +282,7 @@ describe('when configured', () => { expect(configurationResult).toEqual( expect.objectContaining({ initializationStatus: 0, - }) + }), ); }); @@ -309,7 +310,7 @@ describe('when configured', () => { it('should reset cache', () => { expect(sessionStorage.removeItem).toHaveBeenCalledWith( - `@flopflip/http-adapter/${encodeCacheContext(adapterArgs.user)}/flags` + `@flopflip/http-adapter/${encodeCacheContext(adapterArgs.user)}/flags`, ); }); }); @@ -331,7 +332,7 @@ describe('when configured', () => { expect(configurationResult).toEqual( expect.objectContaining({ initializationStatus: 0, - }) + }), ); }); @@ -359,7 +360,7 @@ describe('when configured', () => { it('should not reset cache', () => { expect(sessionStorage.removeItem).not.toHaveBeenCalledWith( - '@flopflip/http-adapter/flags' + '@flopflip/http-adapter/flags', ); }); }); @@ -404,7 +405,7 @@ describe('when configured', () => { it('should indicate that the adapter is not configured', () => { expect( - adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured) + adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured), ).toBe(false); }); diff --git a/packages/http-adapter/vitest.config.ts b/packages/http-adapter/vitest.config.ts index 516a3a1ec..fcd40d7ed 100644 --- a/packages/http-adapter/vitest.config.ts +++ b/packages/http-adapter/vitest.config.ts @@ -1,4 +1,5 @@ import { defineProject, mergeConfig } from 'vitest/config'; + import configShared from '../../vitest.shared.ts'; export default mergeConfig( @@ -7,5 +8,5 @@ export default mergeConfig( test: { environment: 'jsdom', }, - }) + }), ); diff --git a/packages/launchdarkly-adapter/package.json b/packages/launchdarkly-adapter/package.json index f7b8928e6..2573fe4e1 100644 --- a/packages/launchdarkly-adapter/package.json +++ b/packages/launchdarkly-adapter/package.json @@ -2,14 +2,29 @@ "name": "@flopflip/launchdarkly-adapter", "version": "15.1.7", "description": "A adapter around the LaunchDarkly client for flipflop", - "sideEffects": false, - "type": "module", - "exports": { - ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs" - } + "keywords": [ + "LaunchDarkly", + "client", + "feature-flags", + "feature-toggles" + ], + "homepage": "https://github.com/tdeekens/flopflip#readme", + "bugs": { + "url": "https://github.com/tdeekens/flopflip/issues" }, + "license": "MIT", + "author": "Tobias Deekens ", + "repository": { + "type": "git", + "url": "https://github.com/tdeekens/flopflip.git", + "directory": "packages/launchdarkly-adapter" + }, + "files": [ + "readme.md", + "dist/**" + ], + "type": "module", + "sideEffects": false, "main": "./dist/index.js", "typesVersions": { "*": { @@ -19,6 +34,15 @@ ] } }, + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, + "publishConfig": { + "access": "public" + }, "scripts": { "build": "rimraf dist && tsup", "check-types": "tsc --noEmit", @@ -26,29 +50,6 @@ "test:watch": "vitest", "dev": "tsup --watch --clean=false" }, - "files": [ - "readme.md", - "dist/**" - ], - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "https://github.com/tdeekens/flopflip.git", - "directory": "packages/launchdarkly-adapter" - }, - "author": "Tobias Deekens ", - "license": "MIT", - "bugs": { - "url": "https://github.com/tdeekens/flopflip/issues" - }, - "homepage": "https://github.com/tdeekens/flopflip#readme", - "devDependencies": { - "@flopflip/tsconfig": "workspace:*", - "globalthis": "1.0.4", - "tsup": "8.5.1" - }, "dependencies": { "@babel/runtime": "7.28.6", "@flopflip/adapter-utilities": "workspace:*", @@ -63,10 +64,9 @@ "tiny-warning": "1.0.3", "ts-deepmerge": "7.0.3" }, - "keywords": [ - "feature-flags", - "feature-toggles", - "LaunchDarkly", - "client" - ] + "devDependencies": { + "@flopflip/tsconfig": "workspace:*", + "globalthis": "1.0.4", + "tsup": "8.5.1" + } } diff --git a/packages/launchdarkly-adapter/src/adapter.ts b/packages/launchdarkly-adapter/src/adapter.ts index 37a0639ff..4b211f7c4 100644 --- a/packages/launchdarkly-adapter/src/adapter.ts +++ b/packages/launchdarkly-adapter/src/adapter.ts @@ -72,7 +72,7 @@ class LaunchDarklyAdapter implements TLaunchDarklyAdapterInterface { readonly #updateFlagsInAdapterState = ( flags: TFlags, - options?: TUpdateFlagsOptions + options?: TUpdateFlagsOptions, ): void => { const updatedFlags = Object.entries(flags).reduce( (updatedFlags, [flagName, flagValue]) => { @@ -95,7 +95,7 @@ class LaunchDarklyAdapter implements TLaunchDarklyAdapterInterface { return updated; }, - {} + {}, ); this.#adapterState.flags = { @@ -119,8 +119,8 @@ class LaunchDarklyAdapter implements TLaunchDarklyAdapterInterface { Object.entries(flags).filter( ([flagName]) => !this.#getIsFlagUnsubcribed(flagName) && - !this.#getIsFlagLocked(flagName) - ) + !this.#getIsFlagLocked(flagName), + ), ); readonly #getIsAnonymousContext = (context: LDContext) => !context?.key; @@ -138,26 +138,26 @@ class LaunchDarklyAdapter implements TLaunchDarklyAdapterInterface { readonly #initializeClient = ( clientSideId: TLaunchDarklyAdapterArgs['sdk']['clientSideId'], context: LDContext, - options: TLaunchDarklyAdapterArgs['sdk']['clientOptions'] + options: TLaunchDarklyAdapterArgs['sdk']['clientOptions'], ) => initializeLaunchDarklyClient(clientSideId, context, options); readonly #changeClientContext = async (nextContext: LDContext) => this.#adapterState.client?.identify ? this.#adapterState.client.identify(nextContext) : Promise.reject( - new Error('Can not change user context: client not yet initialized.') + new Error('Can not change user context: client not yet initialized.'), ); readonly #maybeUpdateFlagsInCache = async ( flagsToCache: TFlags, - cacheIdentifier?: TCacheIdentifiers + cacheIdentifier?: TCacheIdentifiers, ) => { if (cacheIdentifier) { const cache = await getCache( cacheIdentifier, adapterIdentifiers.launchdarkly, // NOTE: LDContextCommon is part of the type which we never use. - this.#adapterState.context + this.#adapterState.context, ); const cachedFlags: TFlags = cache.get(); @@ -195,14 +195,14 @@ class LaunchDarklyAdapter implements TLaunchDarklyAdapterInterface { flagsFromSdk = {}; for (const [requestedFlagName, defaultFlagValue] of Object.entries( - flags + flags, )) { const denormalizedRequestedFlagName = denormalizeFlagName(requestedFlagName); flagsFromSdk[denormalizedRequestedFlagName] = this.#adapterState.client.variation( denormalizedRequestedFlagName, - defaultFlagValue + defaultFlagValue, ); } } @@ -212,7 +212,7 @@ class LaunchDarklyAdapter implements TLaunchDarklyAdapterInterface { await this.#maybeUpdateFlagsInCache( normalizedFlags, - cacheIdentifier + cacheIdentifier, ); const flags = @@ -223,7 +223,7 @@ class LaunchDarklyAdapter implements TLaunchDarklyAdapterInterface { if (cacheMode !== cacheModes.lazy) { this.#adapterState.emitter.emit( 'flagsStateChange', - this.#adapterState.flags + this.#adapterState.flags, ); } } @@ -239,13 +239,13 @@ class LaunchDarklyAdapter implements TLaunchDarklyAdapterInterface { if (throwOnInitializationFailure) { return Promise.reject( new Error( - '@flopflip/launchdarkly-adapter: adapter failed to initialize.' - ) + '@flopflip/launchdarkly-adapter: adapter failed to initialize.', + ), ); } console.warn( - '@flopflip/launchdarkly-adapter: adapter failed to initialize.' + '@flopflip/launchdarkly-adapter: adapter failed to initialize.', ); return Promise.resolve({ @@ -257,14 +257,14 @@ class LaunchDarklyAdapter implements TLaunchDarklyAdapterInterface { return Promise.reject( new Error( - '@flopflip/launchdarkly-adapter: can not subscribe with non initialized client.' - ) + '@flopflip/launchdarkly-adapter: can not subscribe with non initialized client.', + ), ); }; readonly #didFlagChange = ( flagName: TFlagName, - nextFlagValue: TFlagVariation + nextFlagValue: TFlagVariation, ) => { const previousFlagValue = this.getFlag(flagName); @@ -294,14 +294,14 @@ class LaunchDarklyAdapter implements TLaunchDarklyAdapterInterface { async (flagValue) => { const [normalizedFlagName, normalizedFlagValue] = normalizeFlag( flagName, - flagValue as TFlagVariation + flagValue as TFlagVariation, ); await this.#maybeUpdateFlagsInCache( { [normalizedFlagName]: normalizedFlagValue, }, - cacheIdentifier + cacheIdentifier, ); if (this.#getIsFlagUnsubcribed(normalizedFlagName)) { @@ -327,7 +327,7 @@ class LaunchDarklyAdapter implements TLaunchDarklyAdapterInterface { this.#adapterState.emitter.emit( 'flagsStateChange', - this.#adapterState.flags + this.#adapterState.flags, ); }; @@ -340,7 +340,7 @@ class LaunchDarklyAdapter implements TLaunchDarklyAdapterInterface { ? scheduleTrailingEdge : scheduleImmediately), })(); - } + }, ); } } @@ -353,13 +353,13 @@ class LaunchDarklyAdapter implements TLaunchDarklyAdapterInterface { // ...and flush initial state of flags this.#adapterState.emitter.emit( 'flagsStateChange', - this.#adapterState.flags + this.#adapterState.flags, ); }; async configure( adapterArgs: TLaunchDarklyAdapterArgs, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) { const handleFlagsChange = (nextFlags: TFlagsChange['flags']) => { if (this.#getIsAdapterUnsubscribed()) { @@ -409,7 +409,7 @@ class LaunchDarklyAdapter implements TLaunchDarklyAdapterInterface { const cache = await getCache( adapterArgs.cacheIdentifier, adapterIdentifiers.launchdarkly, - this.#adapterState.context + this.#adapterState.context, ); cachedFlags = cache.get(); @@ -424,7 +424,7 @@ class LaunchDarklyAdapter implements TLaunchDarklyAdapterInterface { this.#adapterState.client = this.#initializeClient( sdk.clientSideId, this.#adapterState.context, - sdk.clientOptions ?? {} + sdk.clientOptions ?? {}, ); return this.#getInitialFlags({ @@ -449,13 +449,13 @@ class LaunchDarklyAdapter implements TLaunchDarklyAdapterInterface { async reconfigure( adapterArgs: TLaunchDarklyAdapterArgs, - _adapterEventHandlers: TAdapterEventHandlers + _adapterEventHandlers: TAdapterEventHandlers, ) { if (!this.getIsConfigurationStatus(AdapterConfigurationStatus.Configured)) { return Promise.reject( new Error( - '@flopflip/launchdarkly-adapter: please configure adapter before reconfiguring.' - ) + '@flopflip/launchdarkly-adapter: please configure adapter before reconfiguring.', + ), ); } @@ -466,7 +466,7 @@ class LaunchDarklyAdapter implements TLaunchDarklyAdapterInterface { const cache = await getCache( adapterArgs.cacheIdentifier, adapterIdentifiers.launchdarkly, - this.#adapterState.context + this.#adapterState.context, ); cache.unset(); @@ -503,20 +503,20 @@ class LaunchDarklyAdapter implements TLaunchDarklyAdapterInterface { } async updateClientContext( - updatedContextProps: TLaunchDarklyAdapterArgs['context'] + updatedContextProps: TLaunchDarklyAdapterArgs['context'], ) { const isAdapterConfigured = this.getIsConfigurationStatus( - AdapterConfigurationStatus.Configured + AdapterConfigurationStatus.Configured, ); warning( isAdapterConfigured, - '@flopflip/launchdarkly-adapter: adapter not configured. Client context can not be updated before.' + '@flopflip/launchdarkly-adapter: adapter not configured. Client context can not be updated before.', ); if (!isAdapterConfigured) { return Promise.reject( - new Error('Can not update client context: adapter not yet configured.') + new Error('Can not update client context: adapter not yet configured.'), ); } diff --git a/packages/launchdarkly-adapter/test/adapter.spec.js b/packages/launchdarkly-adapter/test/adapter.spec.js index 9a7688ac7..3e09832d0 100644 --- a/packages/launchdarkly-adapter/test/adapter.spec.js +++ b/packages/launchdarkly-adapter/test/adapter.spec.js @@ -3,6 +3,7 @@ import { AdapterConfigurationStatus } from '@flopflip/types'; import getGlobalThis from 'globalthis'; import { initialize as initializeLaunchDarklyClient } from 'launchdarkly-js-client-sdk'; import { beforeEach, describe, expect, it, vi } from 'vitest'; + import { adapter } from '../src/adapter'; vi.mock(import('launchdarkly-js-client-sdk'), async (importOriginal) => { @@ -50,7 +51,7 @@ describe('when configuring', () => { it('should indicate that the adapter is not configured', () => { expect( - adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured) + adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured), ).toBe(false); }); @@ -61,7 +62,7 @@ describe('when configuring', () => { describe('when reconfiguring before configured', () => { it('should reject reconfiguration', () => expect(adapter.reconfigure({ context: userWithKey })).rejects.toEqual( - expect.any(Error) + expect.any(Error), )); }); @@ -93,7 +94,7 @@ describe('when configuring', () => { { onStatusStateChange, onFlagsStateChange, - } + }, ); }); @@ -101,7 +102,7 @@ describe('when configuring', () => { expect(initializeLaunchDarklyClient).toHaveBeenCalledWith( clientSideId, expect.objectContaining(userWithKey), - expect.any(Object) + expect.any(Object), ); }); @@ -109,7 +110,7 @@ describe('when configuring', () => { expect(initializeLaunchDarklyClient).toHaveBeenCalledWith( expect.anything(), expect.objectContaining({ anonymous: false }), - expect.anything() + expect.anything(), ); }); }); @@ -124,7 +125,7 @@ describe('when configuring', () => { { onStatusStateChange, onFlagsStateChange, - } + }, ); }); @@ -135,7 +136,7 @@ describe('when configuring', () => { key: undefined, group: 'foo-group', }), - expect.any(Object) + expect.any(Object), ); }); @@ -143,7 +144,7 @@ describe('when configuring', () => { expect(initializeLaunchDarklyClient).toHaveBeenCalledWith( expect.anything(), expect.objectContaining({ anonymous: true }), - expect.anything() + expect.anything(), ); }); }); @@ -173,7 +174,7 @@ describe('when configuring', () => { { onStatusStateChange, onFlagsStateChange, - } + }, ); }); @@ -181,7 +182,7 @@ describe('when configuring', () => { expect(configurationResult).toEqual( expect.objectContaining({ initializationStatus: 0, - }) + }), ); }); @@ -190,8 +191,8 @@ describe('when configuring', () => { it('should indicate that the adapter is configured', () => { expect( adapter.getIsConfigurationStatus( - AdapterConfigurationStatus.Configured - ) + AdapterConfigurationStatus.Configured, + ), ).toBe(true); }); @@ -202,7 +203,7 @@ describe('when configuring', () => { on: expect.any(Function), variation: expect.any(Function), waitForInitialization: expect.any(Function), - }) + }), ); }); }); @@ -229,12 +230,12 @@ describe('when configuring', () => { it('should register callbacks to receive flag updates', () => { expect(client.on).toHaveBeenCalledWith( 'change:some-flag-1', - expect.any(Function) + expect.any(Function), ); expect(client.on).toHaveBeenCalledWith( 'change:some-flag-2', - expect.any(Function) + expect.any(Function), ); }); @@ -254,9 +255,9 @@ describe('when configuring', () => { waitForInitialization: vi.fn(() => Promise.reject( new Error( - '@flopflip/launchdarkly-adapter: adapter failed to initialize.' - ) - ) + '@flopflip/launchdarkly-adapter: adapter failed to initialize.', + ), + ), ), }); @@ -274,10 +275,10 @@ describe('when configuring', () => { { onStatusStateChange, onFlagsStateChange, - } - ) + }, + ), ).rejects.toThrow( - '@flopflip/launchdarkly-adapter: adapter failed to initialize.' + '@flopflip/launchdarkly-adapter: adapter failed to initialize.', ); }); }); @@ -289,9 +290,9 @@ describe('when configuring', () => { waitForInitialization: vi.fn(() => Promise.reject( new Error( - '@flopflip/launchdarkly-adapter: adapter failed to initialize.' - ) - ) + '@flopflip/launchdarkly-adapter: adapter failed to initialize.', + ), + ), ), }); @@ -311,8 +312,8 @@ describe('when configuring', () => { { onStatusStateChange, onFlagsStateChange, - } - ) + }, + ), ).resolves.toEqual(expect.anything()); expect(console.warn).toHaveBeenCalled(); @@ -339,7 +340,7 @@ describe('when configuring', () => { { onStatusStateChange, onFlagsStateChange, - } + }, ); }); @@ -399,7 +400,7 @@ describe('when configuring', () => { { onStatusStateChange, onFlagsStateChange, - } + }, ); }); @@ -429,7 +430,7 @@ describe('when configuring', () => { onFlagsStateChange.mockClear(); adapter.updateFlags( { someFlag1: true }, - { unsubscribeFlags: true } + { unsubscribeFlags: true }, ); triggerFlagValueChange(client, { flagValue: true }); }); @@ -465,7 +466,7 @@ describe('when configuring', () => { initializeLaunchDarklyClient.mockReturnValue(client); sessionStorage.getItem.mockReturnValueOnce( - JSON.stringify({ cached: true }) + JSON.stringify({ cached: true }), ); configurationResult = await adapter.configure( @@ -477,7 +478,7 @@ describe('when configuring', () => { { onStatusStateChange, onFlagsStateChange, - } + }, ); }); @@ -485,15 +486,15 @@ describe('when configuring', () => { expect(configurationResult).toEqual( expect.objectContaining({ initializationStatus: 0, - }) + }), ); }); it('should restore cached flags', () => { expect(sessionStorage.getItem).toHaveBeenCalledWith( `@flopflip/launchdarkly-adapter/${encodeCacheContext( - userWithKey - )}/flags` + userWithKey, + )}/flags`, ); expect(onFlagsStateChange).toHaveBeenCalledWith({ @@ -509,10 +510,10 @@ describe('when configuring', () => { JSON.parse( sessionStorage.getItem( `@flopflip/launchdarkly-adapter/${encodeCacheContext( - userWithKey - )}/flags` - ) - ) + userWithKey, + )}/flags`, + ), + ), ).toStrictEqual({ someFlag1: true, someFlag2: false, @@ -533,7 +534,7 @@ describe('when configuring', () => { initializeLaunchDarklyClient.mockReturnValue(client); sessionStorage.getItem.mockReturnValueOnce( - JSON.stringify({ cached: true }) + JSON.stringify({ cached: true }), ); configurationResult = await adapter.configure( @@ -546,7 +547,7 @@ describe('when configuring', () => { { onStatusStateChange, onFlagsStateChange, - } + }, ); }); @@ -571,7 +572,7 @@ describe('when configuring', () => { expect(configurationResult).toEqual( expect.objectContaining({ initializationStatus: 0, - }) + }), ); }); @@ -616,7 +617,7 @@ describe('when configuring', () => { { onStatusStateChange, onFlagsStateChange, - } + }, ); configurationResult = await adapter.reconfigure({ @@ -628,18 +629,18 @@ describe('when configuring', () => { expect(configurationResult).toEqual( expect.objectContaining({ initializationStatus: 0, - }) + }), ); }); it('should invoke `identify` on the `client` with the `context`', () => { expect(client.identify).toHaveBeenCalledWith( - expect.objectContaining(nextContext) + expect.objectContaining(nextContext), ); }); it('should invoke `identify` on the `client` marking the user as not anonymous', () => { expect(client.identify).toHaveBeenCalledWith( - expect.objectContaining({ anonymous: false }) + expect.objectContaining({ anonymous: false }), ); }); }); @@ -665,7 +666,7 @@ describe('when configuring', () => { { onStatusStateChange, onFlagsStateChange, - } + }, ); }); @@ -676,13 +677,13 @@ describe('when configuring', () => { it('should invoke `identify` on the client with the updated props', () => { expect(client.identify).toHaveBeenCalledWith( - expect.objectContaining(updatedClientProps) + expect.objectContaining(updatedClientProps), ); }); it('should invoke `identify` on the client with the old props', () => { expect(client.identify).toHaveBeenCalledWith( - expect.objectContaining(userWithKey) + expect.objectContaining(userWithKey), ); }); }); @@ -700,13 +701,13 @@ describe('when configuring', () => { expect.objectContaining({ ...userWithKey, ...updatedClientProps, - }) + }), ); }); it('should invoke `identify` the `ld-client` marking the `user` as not anonymous', () => { expect(client.identify).toHaveBeenCalledWith( - expect.objectContaining({ anonymous: false }) + expect.objectContaining({ anonymous: false }), ); }); }); diff --git a/packages/launchdarkly-adapter/vitest.config.ts b/packages/launchdarkly-adapter/vitest.config.ts index 516a3a1ec..fcd40d7ed 100644 --- a/packages/launchdarkly-adapter/vitest.config.ts +++ b/packages/launchdarkly-adapter/vitest.config.ts @@ -1,4 +1,5 @@ import { defineProject, mergeConfig } from 'vitest/config'; + import configShared from '../../vitest.shared.ts'; export default mergeConfig( @@ -7,5 +8,5 @@ export default mergeConfig( test: { environment: 'jsdom', }, - }) + }), ); diff --git a/packages/localstorage-adapter/package.json b/packages/localstorage-adapter/package.json index cbccda56f..f6dbbd40f 100644 --- a/packages/localstorage-adapter/package.json +++ b/packages/localstorage-adapter/package.json @@ -2,14 +2,29 @@ "name": "@flopflip/localstorage-adapter", "version": "15.1.7", "description": "An localstorage adapter for flipflop", - "sideEffects": false, - "type": "module", - "exports": { - ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs" - } + "keywords": [ + "client", + "feature-flags", + "feature-toggles", + "localstorage" + ], + "homepage": "https://github.com/tdeekens/flopflip#readme", + "bugs": { + "url": "https://github.com/tdeekens/flopflip/issues" }, + "license": "MIT", + "author": "Tobias Deekens ", + "repository": { + "type": "git", + "url": "https://github.com/tdeekens/flopflip.git", + "directory": "packages/localstorage-adapter" + }, + "files": [ + "readme.md", + "dist/**" + ], + "type": "module", + "sideEffects": false, "main": "./dist/index.js", "typesVersions": { "*": { @@ -19,6 +34,15 @@ ] } }, + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, + "publishConfig": { + "access": "public" + }, "scripts": { "build": "rimraf dist && tsup", "check-types": "tsc --noEmit", @@ -26,30 +50,6 @@ "test:watch": "vitest", "dev": "tsup --watch --clean=false" }, - "files": [ - "readme.md", - "dist/**" - ], - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "https://github.com/tdeekens/flopflip.git", - "directory": "packages/localstorage-adapter" - }, - "author": "Tobias Deekens ", - "license": "MIT", - "bugs": { - "url": "https://github.com/tdeekens/flopflip/issues" - }, - "homepage": "https://github.com/tdeekens/flopflip#readme", - "keywords": [ - "feature-flags", - "feature-toggles", - "localstorage", - "client" - ], "dependencies": { "@babel/runtime": "7.28.6", "@flopflip/adapter-utilities": "workspace:*", diff --git a/packages/localstorage-adapter/src/adapter.ts b/packages/localstorage-adapter/src/adapter.ts index f04668ab0..1791eeaba 100644 --- a/packages/localstorage-adapter/src/adapter.ts +++ b/packages/localstorage-adapter/src/adapter.ts @@ -95,7 +95,7 @@ class LocalStorageAdapter implements TLocalStorageAdapterInterface { setInterval(() => { if (!this.#getIsAdapterUnsubscribed()) { const nextFlags = normalizeFlags( - this.#cache.get(this.#getFlagsCacheKey(user)) + this.#cache.get(this.#getFlagsCacheKey(user)), ); if (this.#didFlagsChange(nextFlags)) { @@ -108,19 +108,19 @@ class LocalStorageAdapter implements TLocalStorageAdapterInterface { updateFlags = (flags: TFlags, options?: TUpdateFlagsOptions) => { const isAdapterConfigured = this.getIsConfigurationStatus( - AdapterConfigurationStatus.Configured + AdapterConfigurationStatus.Configured, ); warning( isAdapterConfigured, - '@flopflip/localstorage-adapter: adapter not configured. Flags can not be updated before.' + '@flopflip/localstorage-adapter: adapter not configured. Flags can not be updated before.', ); if (!isAdapterConfigured) { return; } - // biome-ignore lint/style/noNonNullAssertion: false positive + // oxlint-disable-next-line typescript/no-non-null-assertion -- false positive const flagsCacheKey = this.#getFlagsCacheKey(this.#adapterState.user!); const previousFlags: TFlags | undefined = this.#cache.get(flagsCacheKey); @@ -129,7 +129,7 @@ class LocalStorageAdapter implements TLocalStorageAdapterInterface { (updatedFlags: TFlags, [flagName, flagValue]) => { const [normalizedFlagName, normalizedFlagValue] = normalizeFlag( flagName, - flagValue + flagValue, ); if (this.#getIsFlagLocked(normalizedFlagName)) { @@ -147,7 +147,7 @@ class LocalStorageAdapter implements TLocalStorageAdapterInterface { return updated; }, - {} + {}, ); const nextFlags: TFlags = { @@ -163,7 +163,7 @@ class LocalStorageAdapter implements TLocalStorageAdapterInterface { async configure( adapterArgs: TLocalStorageAdapterArgs, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) { const handleFlagsChange = (nextFlags: TFlagsChange['flags']) => { if (this.#getIsAdapterUnsubscribed()) { @@ -200,7 +200,7 @@ class LocalStorageAdapter implements TLocalStorageAdapterInterface { this.setConfigurationStatus(AdapterConfigurationStatus.Configured); const flags = normalizeFlags( - this.#cache.get(this.#getFlagsCacheKey(user)) + this.#cache.get(this.#getFlagsCacheKey(user)), ); this.#adapterState.flags = flags; @@ -220,10 +220,10 @@ class LocalStorageAdapter implements TLocalStorageAdapterInterface { async reconfigure( adapterArgs: TLocalStorageAdapterArgs, - _adapterEventHandlers: TAdapterEventHandlers + _adapterEventHandlers: TAdapterEventHandlers, ) { const previousFlags = this.#cache.get( - this.#getFlagsCacheKey(adapterArgs.user) + this.#getFlagsCacheKey(adapterArgs.user), ); this.#adapterState.flags = previousFlags || {}; @@ -246,7 +246,7 @@ class LocalStorageAdapter implements TLocalStorageAdapterInterface { } else { this.#adapterState.emitter.on( this.#__internalConfiguredStatusChange__, - resolve + resolve, ); } }); diff --git a/packages/localstorage-adapter/test/adapter.spec.js b/packages/localstorage-adapter/test/adapter.spec.js index 8924b5882..09bbdce44 100644 --- a/packages/localstorage-adapter/test/adapter.spec.js +++ b/packages/localstorage-adapter/test/adapter.spec.js @@ -2,6 +2,7 @@ import { AdapterConfigurationStatus } from '@flopflip/types'; import getGlobalThis from 'globalthis'; import warning from 'tiny-warning'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + import { adapter, STORAGE_SLICE } from '../src/adapter'; vi.mock('tiny-warning'); @@ -23,7 +24,7 @@ describe('when configuring', () => { describe('when not configured', () => { it('should indicate that the adapter is not configured', () => { expect( - adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured) + adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured), ).toBe(false); }); @@ -44,7 +45,7 @@ describe('when configuring', () => { vi.useFakeTimers(); configurationResult = await adapter.configure( adapterArgs, - adapterEventHandlers + adapterEventHandlers, ); }); @@ -56,7 +57,7 @@ describe('when configuring', () => { expect(configurationResult).toEqual( expect.objectContaining({ initializationStatus: 0, - }) + }), ); }); @@ -67,13 +68,13 @@ describe('when configuring', () => { status: { configurationStatus: AdapterConfigurationStatus.Configuring, }, - }) + }), ); }); it('should indicate that the adapter is configured', () => { expect( - adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured) + adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured), ).toBe(true); }); @@ -112,9 +113,9 @@ describe('when configuring', () => { expect( JSON.parse( localStorage.getItem( - `${STORAGE_SLICE}/${adapterArgs.user.key}/flags` - ) - ) + `${STORAGE_SLICE}/${adapterArgs.user.key}/flags`, + ), + ), ).toStrictEqual(updatedFlags); }); @@ -169,9 +170,9 @@ describe('when configuring', () => { expect( JSON.parse( localStorage.getItem( - `${STORAGE_SLICE}/${adapterArgs.user.key}/flags` - ) - ) + `${STORAGE_SLICE}/${adapterArgs.user.key}/flags`, + ), + ), ).toHaveProperty('fooFlag', true); }); }); @@ -187,14 +188,14 @@ describe('when configuring', () => { localStorage.setItem( `${STORAGE_SLICE}/${adapterArgs.user.key}/flags`, - JSON.stringify(updatedFlags) + JSON.stringify(updatedFlags), ); }); it('should invoke `onFlagsStateChange`', () => { vi.advanceTimersByTime(5 * 60 * 1000); expect(adapterEventHandlers.onFlagsStateChange).toHaveBeenCalledTimes( - 1 + 1, ); }); }); @@ -221,7 +222,7 @@ describe('when configuring', () => { localStorage.setItem( `${STORAGE_SLICE}/${user.key}/flags`, - JSON.stringify({ foo: 'bar' }) + JSON.stringify({ foo: 'bar' }), ); configurationResult = await adapter.reconfigure({ user }); @@ -231,13 +232,13 @@ describe('when configuring', () => { expect(configurationResult).toEqual( expect.objectContaining({ initializationStatus: 0, - }) + }), ); }); it('should restore flags from localstorage', () => { expect(localStorage.getItem).toHaveBeenCalledWith( - `${STORAGE_SLICE}/${user.key}/flags` + `${STORAGE_SLICE}/${user.key}/flags`, ); }); diff --git a/packages/localstorage-adapter/vitest.config.ts b/packages/localstorage-adapter/vitest.config.ts index 516a3a1ec..fcd40d7ed 100644 --- a/packages/localstorage-adapter/vitest.config.ts +++ b/packages/localstorage-adapter/vitest.config.ts @@ -1,4 +1,5 @@ import { defineProject, mergeConfig } from 'vitest/config'; + import configShared from '../../vitest.shared.ts'; export default mergeConfig( @@ -7,5 +8,5 @@ export default mergeConfig( test: { environment: 'jsdom', }, - }) + }), ); diff --git a/packages/localstorage-cache/package.json b/packages/localstorage-cache/package.json index 647547ad0..79e39ef65 100644 --- a/packages/localstorage-cache/package.json +++ b/packages/localstorage-cache/package.json @@ -2,14 +2,30 @@ "name": "@flopflip/localstorage-cache", "version": "15.1.7", "description": "Localstorage cache for flipflop adapters", - "sideEffects": false, - "type": "module", - "exports": { - ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs" - } + "keywords": [ + "cache", + "client", + "feature-flags", + "feature-toggles", + "localstorage" + ], + "homepage": "https://github.com/tdeekens/flopflip#readme", + "bugs": { + "url": "https://github.com/tdeekens/flopflip/issues" }, + "license": "MIT", + "author": "Tobias Deekens ", + "repository": { + "type": "git", + "url": "https://github.com/tdeekens/flopflip.git", + "directory": "packages/localstorage-cache" + }, + "files": [ + "readme.md", + "dist/**" + ], + "type": "module", + "sideEffects": false, "main": "./dist/index.js", "typesVersions": { "*": { @@ -19,6 +35,15 @@ ] } }, + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, + "publishConfig": { + "access": "public" + }, "scripts": { "build": "rimraf dist && tsup", "check-types": "tsc --noEmit", @@ -26,31 +51,6 @@ "test:watch": "vitest", "dev": "tsup --watch --clean=false" }, - "files": [ - "readme.md", - "dist/**" - ], - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "https://github.com/tdeekens/flopflip.git", - "directory": "packages/localstorage-cache" - }, - "author": "Tobias Deekens ", - "license": "MIT", - "bugs": { - "url": "https://github.com/tdeekens/flopflip/issues" - }, - "homepage": "https://github.com/tdeekens/flopflip#readme", - "keywords": [ - "feature-flags", - "feature-toggles", - "localstorage", - "cache", - "client" - ], "dependencies": { "@flopflip/types": "workspace:*" }, diff --git a/packages/localstorage-cache/test/cache.spec.js b/packages/localstorage-cache/test/cache.spec.js index a8a8d9312..48d478bb7 100644 --- a/packages/localstorage-cache/test/cache.spec.js +++ b/packages/localstorage-cache/test/cache.spec.js @@ -1,4 +1,5 @@ import { describe, expect, it } from 'vitest'; + import { createCache } from '../src/cache'; const cachePrefix = 'test'; @@ -11,7 +12,7 @@ describe('setting a value', () => { cache.set('foo', 'bar'); expect( - JSON.parse(localStorage.getItem(`${cachePrefix}/foo`)) + JSON.parse(localStorage.getItem(`${cachePrefix}/foo`)), ).toStrictEqual('bar'); }); }); @@ -24,7 +25,7 @@ describe('setting a value', () => { cache.set('foo', 'baz'); expect( - JSON.parse(localStorage.getItem(`${cachePrefix}/foo`)) + JSON.parse(localStorage.getItem(`${cachePrefix}/foo`)), ).toStrictEqual('baz'); }); }); @@ -38,7 +39,7 @@ describe('getting a value', () => { cache.set('foo', 'bar'); expect( - JSON.parse(localStorage.getItem(`${cachePrefix}/foo`)) + JSON.parse(localStorage.getItem(`${cachePrefix}/foo`)), ).toStrictEqual('bar'); }); }); @@ -51,7 +52,7 @@ describe('getting a value', () => { cache.set('foo', json); expect( - JSON.parse(localStorage.getItem(`${cachePrefix}/foo`)) + JSON.parse(localStorage.getItem(`${cachePrefix}/foo`)), ).toStrictEqual(json); }); }); @@ -63,7 +64,7 @@ describe('unsetting a value', () => { cache.set('foo', 'bar'); expect( - JSON.parse(localStorage.getItem(`${cachePrefix}/foo`)) + JSON.parse(localStorage.getItem(`${cachePrefix}/foo`)), ).toStrictEqual('bar'); cache.unset('foo', 'bar'); diff --git a/packages/localstorage-cache/vitest.config.ts b/packages/localstorage-cache/vitest.config.ts index 516a3a1ec..fcd40d7ed 100644 --- a/packages/localstorage-cache/vitest.config.ts +++ b/packages/localstorage-cache/vitest.config.ts @@ -1,4 +1,5 @@ import { defineProject, mergeConfig } from 'vitest/config'; + import configShared from '../../vitest.shared.ts'; export default mergeConfig( @@ -7,5 +8,5 @@ export default mergeConfig( test: { environment: 'jsdom', }, - }) + }), ); diff --git a/packages/memory-adapter/package.json b/packages/memory-adapter/package.json index 9ab5a059d..94fc8f3ab 100644 --- a/packages/memory-adapter/package.json +++ b/packages/memory-adapter/package.json @@ -2,14 +2,29 @@ "name": "@flopflip/memory-adapter", "version": "15.1.7", "description": "An in memory adapter for flipflop", - "sideEffects": false, - "type": "module", - "exports": { - ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs" - } + "keywords": [ + "client", + "feature-flags", + "feature-toggles", + "memory" + ], + "homepage": "https://github.com/tdeekens/flopflip#readme", + "bugs": { + "url": "https://github.com/tdeekens/flopflip/issues" }, + "license": "MIT", + "author": "Tobias Deekens ", + "repository": { + "type": "git", + "url": "https://github.com/tdeekens/flopflip.git", + "directory": "packages/memory-adapter" + }, + "files": [ + "readme.md", + "dist/**" + ], + "type": "module", + "sideEffects": false, "main": "./dist/index.js", "typesVersions": { "*": { @@ -19,6 +34,15 @@ ] } }, + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, + "publishConfig": { + "access": "public" + }, "scripts": { "build": "rimraf dist && tsup", "check-types": "tsc --noEmit", @@ -26,30 +50,6 @@ "test:watch": "vitest", "dev": "tsup --watch --clean=false" }, - "files": [ - "readme.md", - "dist/**" - ], - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "https://github.com/tdeekens/flopflip.git", - "directory": "packages/memory-adapter" - }, - "author": "Tobias Deekens ", - "license": "MIT", - "bugs": { - "url": "https://github.com/tdeekens/flopflip/issues" - }, - "homepage": "https://github.com/tdeekens/flopflip#readme", - "keywords": [ - "feature-flags", - "feature-toggles", - "memory", - "client" - ], "dependencies": { "@babel/runtime": "7.28.6", "@flopflip/adapter-utilities": "workspace:*", diff --git a/packages/memory-adapter/src/adapter.ts b/packages/memory-adapter/src/adapter.ts index c81c07e1d..0fce1e39f 100644 --- a/packages/memory-adapter/src/adapter.ts +++ b/packages/memory-adapter/src/adapter.ts @@ -71,12 +71,12 @@ class MemoryAdapter implements TMemoryAdapterInterface { updateFlags = (flags: TFlags, options?: TUpdateFlagsOptions) => { const isAdapterConfigured = this.getIsConfigurationStatus( - AdapterConfigurationStatus.Configured + AdapterConfigurationStatus.Configured, ); warning( isAdapterConfigured, - '@flopflip/memory-adapter: adapter is not configured. Flags can not be updated before.' + '@flopflip/memory-adapter: adapter is not configured. Flags can not be updated before.', ); if (!isAdapterConfigured) { @@ -86,7 +86,7 @@ class MemoryAdapter implements TMemoryAdapterInterface { for (const [flagName, flagValue] of Object.entries(flags)) { const [normalizedFlagName, normalizedFlagValue] = normalizeFlag( flagName, - flagValue + flagValue, ); if (this.#getIsFlagLocked(normalizedFlagName)) { @@ -105,13 +105,13 @@ class MemoryAdapter implements TMemoryAdapterInterface { this.#adapterState.emitter.emit( 'flagsStateChange', - this.#adapterState.flags + this.#adapterState.flags, ); }; async configure( adapterArgs: TMemoryAdapterArgs, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) { const handleFlagsChange = (nextFlags: TFlagsChange['flags']) => { if (this.#getIsAdapterUnsubscribed()) { @@ -153,7 +153,7 @@ class MemoryAdapter implements TMemoryAdapterInterface { this.#adapterState.emitter.emit( 'flagsStateChange', - this.#adapterState.flags + this.#adapterState.flags, ); this.#adapterState.emitter.emit(this.#__internalConfiguredStatusChange__); @@ -166,7 +166,7 @@ class MemoryAdapter implements TMemoryAdapterInterface { async reconfigure( adapterArgs: TMemoryAdapterArgs, - _adapterEventHandlers: TAdapterEventHandlers + _adapterEventHandlers: TAdapterEventHandlers, ) { this.setConfigurationStatus(AdapterConfigurationStatus.Configuring); @@ -178,7 +178,7 @@ class MemoryAdapter implements TMemoryAdapterInterface { this.#adapterState.emitter.emit( 'flagsStateChange', - this.#adapterState.flags + this.#adapterState.flags, ); return Promise.resolve({ @@ -213,7 +213,7 @@ class MemoryAdapter implements TMemoryAdapterInterface { } else { this.#adapterState.emitter.on( this.#__internalConfiguredStatusChange__, - resolve + resolve, ); } }); diff --git a/packages/memory-adapter/test/adapter.spec.js b/packages/memory-adapter/test/adapter.spec.js index 24c93d18f..865d707a6 100644 --- a/packages/memory-adapter/test/adapter.spec.js +++ b/packages/memory-adapter/test/adapter.spec.js @@ -2,6 +2,7 @@ import { AdapterConfigurationStatus } from '@flopflip/types'; import getGlobalThis from 'globalthis'; import warning from 'tiny-warning'; import { beforeEach, describe, expect, it, vi } from 'vitest'; + import { adapter } from '../src/adapter'; vi.mock('tiny-warning'); @@ -29,7 +30,7 @@ describe('when configuring', () => { it('should indicate that the adapter is not configured', () => { expect( - adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured) + adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured), ).toBe(false); }); @@ -49,7 +50,7 @@ describe('when configuring', () => { beforeEach(async () => { configurationResult = await adapter.configure( adapterArgs, - adapterEventHandlers + adapterEventHandlers, ); }); @@ -57,7 +58,7 @@ describe('when configuring', () => { expect(configurationResult).toEqual( expect.objectContaining({ initializationStatus: 0, - }) + }), ); }); @@ -68,13 +69,13 @@ describe('when configuring', () => { status: { configurationStatus: AdapterConfigurationStatus.Configuring, }, - }) + }), ); }); it('should indicate that the adapter is configured', () => { expect( - adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured) + adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured), ).toBe(true); }); @@ -93,7 +94,7 @@ describe('when configuring', () => { status: { configurationStatus: AdapterConfigurationStatus.Configured, }, - }) + }), ); }); @@ -172,7 +173,7 @@ describe('when configuring', () => { expect(configurationResult).toEqual( expect.objectContaining({ initializationStatus: 0, - }) + }), ); }); @@ -225,15 +226,15 @@ describe('when configuring', () => { status: { configurationStatus: AdapterConfigurationStatus.Configuring, }, - }) + }), ); }); it('should indicate that the adapter is not configured', () => { expect( adapter.getIsConfigurationStatus( - AdapterConfigurationStatus.Configured - ) + AdapterConfigurationStatus.Configured, + ), ).toBe(false); }); @@ -244,7 +245,7 @@ describe('when configuring', () => { status: { configurationStatus: AdapterConfigurationStatus.Configuring, }, - }) + }), ); }); }); diff --git a/packages/memory-adapter/vitest.config.ts b/packages/memory-adapter/vitest.config.ts index 516a3a1ec..fcd40d7ed 100644 --- a/packages/memory-adapter/vitest.config.ts +++ b/packages/memory-adapter/vitest.config.ts @@ -1,4 +1,5 @@ import { defineProject, mergeConfig } from 'vitest/config'; + import configShared from '../../vitest.shared.ts'; export default mergeConfig( @@ -7,5 +8,5 @@ export default mergeConfig( test: { environment: 'jsdom', }, - }) + }), ); diff --git a/packages/react-broadcast/package.json b/packages/react-broadcast/package.json index b2207fb22..b7f420296 100644 --- a/packages/react-broadcast/package.json +++ b/packages/react-broadcast/package.json @@ -2,14 +2,30 @@ "name": "@flopflip/react-broadcast", "version": "15.1.7", "description": "A feature toggle wrapper to use LaunchDarkly with React", - "sideEffects": false, - "type": "module", - "exports": { - ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs" - } + "keywords": [ + "LaunchDarkly", + "client", + "feature-flags", + "feature-toggles", + "react" + ], + "homepage": "https://github.com/tdeekens/flopflip#readme", + "bugs": { + "url": "https://github.com/tdeekens/flopflip/issues" }, + "license": "MIT", + "author": "Tobias Deekens ", + "repository": { + "type": "git", + "url": "https://github.com/tdeekens/flopflip.git", + "directory": "packages/react-broadcast" + }, + "files": [ + "readme.md", + "dist/**" + ], + "type": "module", + "sideEffects": false, "main": "./dist/index.js", "browser": "./dist/index.js", "typesVersions": { @@ -20,6 +36,15 @@ ] } }, + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, + "publishConfig": { + "access": "public" + }, "scripts": { "build": "rimraf dist && tsup", "check-types": "tsc --noEmit", @@ -27,24 +52,12 @@ "test:watch": "vitest", "dev": "tsup --watch --clean=false" }, - "files": [ - "readme.md", - "dist/**" - ], - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "https://github.com/tdeekens/flopflip.git", - "directory": "packages/react-broadcast" - }, - "author": "Tobias Deekens ", - "license": "MIT", - "bugs": { - "url": "https://github.com/tdeekens/flopflip/issues" + "dependencies": { + "@babel/runtime": "7.28.6", + "@flopflip/react": "workspace:*", + "@flopflip/types": "workspace:*", + "use-sync-external-store": "1.6.0" }, - "homepage": "https://github.com/tdeekens/flopflip#readme", "devDependencies": { "@flopflip/combine-adapters": "workspace:*", "@flopflip/localstorage-adapter": "workspace:*", @@ -60,18 +73,5 @@ "peerDependencies": { "react": "18.x || 19.x", "react-dom": "18.x || 19.x" - }, - "dependencies": { - "@babel/runtime": "7.28.6", - "@flopflip/react": "workspace:*", - "@flopflip/types": "workspace:*", - "use-sync-external-store": "1.6.0" - }, - "keywords": [ - "react", - "feature-flags", - "feature-toggles", - "LaunchDarkly", - "client" - ] + } } diff --git a/packages/react-broadcast/src/branch-on-feature-toggle.tsx b/packages/react-broadcast/src/branch-on-feature-toggle.tsx index 5f99f4d5b..12c02a29d 100644 --- a/packages/react-broadcast/src/branch-on-feature-toggle.tsx +++ b/packages/react-broadcast/src/branch-on-feature-toggle.tsx @@ -1,5 +1,4 @@ import type { TFlagName, TFlagVariation } from '@flopflip/types'; -// biome-ignore lint/style/useImportType: TS is just weird import React from 'react'; import { useFeatureToggle } from './use-feature-toggle'; @@ -10,7 +9,7 @@ type TBranchOnFeatureToggleOptions = { }; function branchOnFeatureToggle>( { flag: flagName, variation: flagVariation }: TBranchOnFeatureToggleOptions, - UntoggledComponent?: React.ComponentType + UntoggledComponent?: React.ComponentType, ) { return (ToggledComponent: React.ComponentType) => { function WrappedToggledComponent(ownProps: OwnProps) { diff --git a/packages/react-broadcast/src/configure.tsx b/packages/react-broadcast/src/configure.tsx index f2f535d1f..34fcd9fc0 100644 --- a/packages/react-broadcast/src/configure.tsx +++ b/packages/react-broadcast/src/configure.tsx @@ -10,7 +10,7 @@ import { type TFlags, type TFlagsChange, } from '@flopflip/types'; -// biome-ignore lint/correctness/noUnusedImports: false positive +// oxlint-disable-next-line no-unused-vars -- false positive import React, { useCallback, useMemo } from 'react'; import { useSyncExternalStore } from 'use-sync-external-store/shim'; @@ -45,7 +45,7 @@ const useFlagsState = ({ const flags = useSyncExternalStore( store.subscribe, () => store.getSnapshot().flags, - () => store.getSnapshot().flags + () => store.getSnapshot().flags, ); const updateFlags = useCallback( @@ -78,7 +78,7 @@ const useFlagsState = ({ ...prevState.flags[adapterInterfaceIdentifier], ...flagsChange.flags, }, - ]) + ]), ), }, }; @@ -86,7 +86,7 @@ const useFlagsState = ({ return nextState; }); }, - [adapterIdentifiers] + [adapterIdentifiers], ); return [flags, updateFlags]; @@ -102,7 +102,7 @@ const useStatusState = ({ const status = useSyncExternalStore( store.subscribe, () => store.getSnapshot().status, - () => store.getSnapshot().status + () => store.getSnapshot().status, ); const setStatus = useCallback( @@ -136,7 +136,7 @@ const useStatusState = ({ ...prevState.status[adapterInterfaceIdentifier], ...statusChange.status, }, - ]) + ]), ), }, }; @@ -144,7 +144,7 @@ const useStatusState = ({ return nextState; }); }, - [adapterIdentifiers] + [adapterIdentifiers], ); return [status, setStatus]; @@ -177,7 +177,7 @@ function Configure({ updateFlags(flagsChange); }, - [updateFlags, getHasAdapterSubscriptionStatus] + [updateFlags, getHasAdapterSubscriptionStatus], ); const handleUpdateStatus = useCallback< @@ -192,7 +192,7 @@ function Configure({ updateStatus(statusChange); }, - [updateStatus, getHasAdapterSubscriptionStatus] + [updateStatus, getHasAdapterSubscriptionStatus], ); return ( diff --git a/packages/react-broadcast/src/flags-context.ts b/packages/react-broadcast/src/flags-context.ts index 82c6d6791..e38e3c919 100644 --- a/packages/react-broadcast/src/flags-context.ts +++ b/packages/react-broadcast/src/flags-context.ts @@ -7,17 +7,17 @@ import { createContext } from 'react'; const createIntialFlagsContext = ( adapterIdentifiers: Record, - initialFlags: TFlags + initialFlags: TFlags, ) => Object.fromEntries( Object.values(adapterIdentifiers).map((adapterInterfaceIdentifier) => [ adapterInterfaceIdentifier, initialFlags, - ]) + ]), ); const FlagsContext = createContext( - createIntialFlagsContext(allAdapterIdentifiers, {}) + createIntialFlagsContext(allAdapterIdentifiers, {}), ); export { FlagsContext, createIntialFlagsContext }; diff --git a/packages/react-broadcast/src/inject-feature-toggle.tsx b/packages/react-broadcast/src/inject-feature-toggle.tsx index 85345150f..b96d6575b 100644 --- a/packages/react-broadcast/src/inject-feature-toggle.tsx +++ b/packages/react-broadcast/src/inject-feature-toggle.tsx @@ -4,7 +4,6 @@ import { wrapDisplayName, } from '@flopflip/react'; import type { TFlagName, TFlagVariation } from '@flopflip/types'; -// biome-ignore lint/style/useImportType: false positive import React from 'react'; import { useFlagVariations } from './use-flag-variations'; @@ -13,10 +12,10 @@ type InjectedProps = Record; function injectFeatureToggle>( flagName: TFlagName, - propKey: string = DEFAULT_FLAG_PROP_KEY + propKey: string = DEFAULT_FLAG_PROP_KEY, ) { return ( - Component: React.ComponentType + Component: React.ComponentType, ): React.ComponentType => { function WrappedComponent(ownProps: OwnProps) { const [flagVariation] = useFlagVariations([flagName]); diff --git a/packages/react-broadcast/src/inject-feature-toggles.tsx b/packages/react-broadcast/src/inject-feature-toggles.tsx index 48ec4856b..1f83c010a 100644 --- a/packages/react-broadcast/src/inject-feature-toggles.tsx +++ b/packages/react-broadcast/src/inject-feature-toggles.tsx @@ -4,7 +4,6 @@ import { wrapDisplayName, } from '@flopflip/react'; import type { TFlagName, TFlags } from '@flopflip/types'; -// biome-ignore lint/style/useImportType: false positive import React from 'react'; import { useFlagVariations } from './use-flag-variations'; @@ -13,10 +12,10 @@ type InjectedProps = Record; function injectFeatureToggles>( flagNames: TFlagName[], - propKey: string = DEFAULT_FLAGS_PROP_KEY + propKey: string = DEFAULT_FLAGS_PROP_KEY, ) { return ( - Component: React.ComponentType + Component: React.ComponentType, ): React.ComponentType => { function WrappedComponent(ownProps: OwnProps) { const flagVariations = useFlagVariations(flagNames); @@ -24,7 +23,7 @@ function injectFeatureToggles>( flagNames.map((flagName, indexOfFlagName) => [ flagName, flagVariations[indexOfFlagName], - ]) + ]), ); const props = { ...ownProps, diff --git a/packages/react-broadcast/src/store.ts b/packages/react-broadcast/src/store.ts index c27daf6df..b41e0f052 100644 --- a/packages/react-broadcast/src/store.ts +++ b/packages/react-broadcast/src/store.ts @@ -1,7 +1,7 @@ type TListener = () => void; function createStore>( - initialState: TState + initialState: TState, ) { let state = initialState; const getSnapshot = () => state; diff --git a/packages/react-broadcast/src/test-provider.tsx b/packages/react-broadcast/src/test-provider.tsx index 6a6f7ec92..595e28b25 100644 --- a/packages/react-broadcast/src/test-provider.tsx +++ b/packages/react-broadcast/src/test-provider.tsx @@ -8,7 +8,6 @@ import { type TFlags, type TReconfigureAdapter, } from '@flopflip/types'; -// biome-ignore lint/style/useImportType: false positive import React from 'react'; import { createIntialFlagsContext, FlagsContext } from './flags-context'; @@ -26,17 +25,15 @@ const defaultProps: Pick< 'adapterIdentifiers' | 'reconfigure' | 'status' > = { adapterIdentifiers: ['test'], - status: { - ...Object.fromEntries( - Object.values(adapterIdentifiers).map((adapterInterfaceIdentifier) => [ - adapterInterfaceIdentifier, - { - subscriptionStatus: AdapterSubscriptionStatus.Subscribed, - configurationStatus: AdapterConfigurationStatus.Configured, - }, - ]) - ), - }, + status: Object.fromEntries( + Object.values(adapterIdentifiers).map((adapterInterfaceIdentifier) => [ + adapterInterfaceIdentifier, + { + subscriptionStatus: AdapterSubscriptionStatus.Subscribed, + configurationStatus: AdapterConfigurationStatus.Configured, + }, + ]), + ), }; function TestProvider({ @@ -49,12 +46,12 @@ function TestProvider({ const adapterContextValue = createAdapterContext( adapterIdentifiers, reconfigure, - status + status, ); const flagsContextValue = createIntialFlagsContext( // @ts-expect-error Can not remember. Sorry to myself. adapterIdentifiers, - flags + flags, ); return ( diff --git a/packages/react-broadcast/src/toggle-feature.tsx b/packages/react-broadcast/src/toggle-feature.tsx index e857e3b11..693492766 100644 --- a/packages/react-broadcast/src/toggle-feature.tsx +++ b/packages/react-broadcast/src/toggle-feature.tsx @@ -3,7 +3,7 @@ import { type TToggleFeatureProps, } from '@flopflip/react'; import type { TFlagName, TFlagVariation } from '@flopflip/types'; -// biome-ignore lint/correctness/noUnusedImports: false positive +// oxlint-disable-next-line no-unused-vars -- false positive import React from 'react'; import { useFeatureToggle } from './use-feature-toggle'; diff --git a/packages/react-broadcast/src/use-adapter-status.ts b/packages/react-broadcast/src/use-adapter-status.ts index 12f52d926..288426399 100644 --- a/packages/react-broadcast/src/use-adapter-status.ts +++ b/packages/react-broadcast/src/use-adapter-status.ts @@ -11,7 +11,7 @@ function useAdapterStatus({ adapterIdentifiers }: TUseAdapterStatusArgs = {}) { const adapterStatus = selectAdapterConfigurationStatus( status, - adapterIdentifiers + adapterIdentifiers, ); useDebugValue({ adapterStatus }); diff --git a/packages/react-broadcast/src/use-all-feature-toggles.ts b/packages/react-broadcast/src/use-all-feature-toggles.ts index 6f6fcaf1f..dbbaf89d1 100644 --- a/packages/react-broadcast/src/use-all-feature-toggles.ts +++ b/packages/react-broadcast/src/use-all-feature-toggles.ts @@ -8,14 +8,14 @@ function useAllFeatureToggles(): TFlags { const flagsContext = useFlagsContext(); const reversedAdapterEffectIdentifiers = [ ...adapterContext.adapterEffectIdentifiers, - ].reverse(); + ].toReversed(); return reversedAdapterEffectIdentifiers.reduce( (_allFlags, adapterIdentifier) => ({ ..._allFlags, ...flagsContext[adapterIdentifier], }), - {} + {}, ); } diff --git a/packages/react-broadcast/src/use-feature-toggle.ts b/packages/react-broadcast/src/use-feature-toggle.ts index 71ec1b5cc..06dab826f 100644 --- a/packages/react-broadcast/src/use-feature-toggle.ts +++ b/packages/react-broadcast/src/use-feature-toggle.ts @@ -6,7 +6,7 @@ import { useFlagsContext } from './use-flags-context'; function useFeatureToggle( flagName: TFlagName, - flagVariation: TFlagVariation = true + flagVariation: TFlagVariation = true, ) { const adapterContext = useAdapterContext(); const flagsContext = useFlagsContext(); @@ -14,7 +14,7 @@ function useFeatureToggle( flagsContext, adapterContext.adapterEffectIdentifiers, flagName, - flagVariation + flagVariation, ); useDebugValue({ diff --git a/packages/react-broadcast/src/use-feature-toggles.ts b/packages/react-broadcast/src/use-feature-toggles.ts index 241709df8..349d8de9d 100644 --- a/packages/react-broadcast/src/use-feature-toggles.ts +++ b/packages/react-broadcast/src/use-feature-toggles.ts @@ -12,14 +12,14 @@ function useFeatureToggles(flags: TFlags) { flagsContext, adapterContext.adapterEffectIdentifiers, flagName, - flagVariation + flagVariation, ); previousFlags.push(isFeatureEnabled); return previousFlags; }, - [] + [], ); return requestedFlags; diff --git a/packages/react-broadcast/src/use-flag-variations.ts b/packages/react-broadcast/src/use-flag-variations.ts index 275dd5089..e0fadc63f 100644 --- a/packages/react-broadcast/src/use-flag-variations.ts +++ b/packages/react-broadcast/src/use-flag-variations.ts @@ -4,7 +4,7 @@ import type { TFlagName, TFlagVariation } from '@flopflip/types'; import { useFlagsContext } from './use-flags-context'; function useFlagVariations( - flagNames: Array + flagNames: (TFlagName | undefined)[], ): TFlagVariation[] { const adapterContext = useAdapterContext(); const flagsContext = useFlagsContext(); @@ -13,8 +13,8 @@ function useFlagVariations( getFlagVariation( flagsContext, adapterContext.adapterEffectIdentifiers, - requestedVariation - ) + requestedVariation, + ), ); return flagVariations; diff --git a/packages/react-broadcast/test/branch-on-feature-toggle.spec.jsx b/packages/react-broadcast/test/branch-on-feature-toggle.spec.jsx index ec2f4462f..a0705794d 100644 --- a/packages/react-broadcast/test/branch-on-feature-toggle.spec.jsx +++ b/packages/react-broadcast/test/branch-on-feature-toggle.spec.jsx @@ -13,11 +13,11 @@ describe('without `untoggledComponent', () => { describe('when feature is disabled', () => { it('should render neither the component representing an disabled or enabled feature', async () => { const TestComponent = branchOnFeatureToggle({ flag: 'disabledFeature' })( - components.ToggledComponent + components.ToggledComponent, ); const { waitUntilConfigured, queryByFlagName } = render( - + , ); await waitUntilConfigured(); @@ -46,18 +46,18 @@ describe('without `untoggledComponent', () => { describe('when feature is enabled', () => { it('should render the component representing an enabled feature', async () => { const TestComponent = branchOnFeatureToggle({ flag: 'enabledFeature' })( - components.ToggledComponent + components.ToggledComponent, ); const { waitUntilConfigured, queryByFlagName } = render( - + , ); await waitUntilConfigured(); expect(queryByFlagName('isFeatureEnabled')).toHaveAttribute( 'data-flag-status', - 'enabled' + 'enabled', ); }); }); @@ -68,36 +68,36 @@ describe('with `untoggledComponent', () => { it('should not render the component representing a enabled feature', async () => { const TestComponent = branchOnFeatureToggle( { flag: 'disabledFeature' }, - components.UntoggledComponent + components.UntoggledComponent, )(components.ToggledComponent); const { waitUntilConfigured, queryByFlagName } = render( - + , ); await waitUntilConfigured(); expect(queryByFlagName('isFeatureEnabled')).not.toHaveAttribute( 'data-flag-status', - 'enabled' + 'enabled', ); }); it('should render the component representing a disabled feature', async () => { const TestComponent = branchOnFeatureToggle( { flag: 'disabledFeature' }, - components.UntoggledComponent + components.UntoggledComponent, )(components.ToggledComponent); const { waitUntilConfigured, queryByFlagName } = render( - + , ); await waitUntilConfigured(); expect(queryByFlagName('isFeatureEnabled')).toHaveAttribute( 'data-flag-status', - 'disabled' + 'disabled', ); }); }); @@ -106,36 +106,36 @@ describe('with `untoggledComponent', () => { it('should render the component representing a enabled feature', async () => { const TestComponent = branchOnFeatureToggle( { flag: 'enabledFeature' }, - components.UntoggledComponent + components.UntoggledComponent, )(components.ToggledComponent); const { waitUntilConfigured, queryByFlagName } = render( - + , ); await waitUntilConfigured(); expect(queryByFlagName('isFeatureEnabled')).toHaveAttribute( 'data-flag-status', - 'enabled' + 'enabled', ); }); it('should not render the component representing a disabled feature', async () => { const TestComponent = branchOnFeatureToggle( { flag: 'enabledFeature' }, - components.UntoggledComponent + components.UntoggledComponent, )(components.ToggledComponent); const { waitUntilConfigured, queryByFlagName } = render( - + , ); await waitUntilConfigured(); expect(queryByFlagName('isFeatureEnabled')).not.toHaveAttribute( 'data-flag-status', - 'disabled' + 'disabled', ); }); }); diff --git a/packages/react-broadcast/test/configure.spec.jsx b/packages/react-broadcast/test/configure.spec.jsx index 272284e36..eedfbc111 100644 --- a/packages/react-broadcast/test/configure.spec.jsx +++ b/packages/react-broadcast/test/configure.spec.jsx @@ -36,7 +36,7 @@ const render = () => { rtlRender( - + , ); const waitUntilConfigured = () => screen.findByText(/Is configured: Yes/i); diff --git a/packages/react-broadcast/test/inject-feature-toggle.spec.jsx b/packages/react-broadcast/test/inject-feature-toggle.spec.jsx index ca668321e..fae73c25e 100644 --- a/packages/react-broadcast/test/inject-feature-toggle.spec.jsx +++ b/packages/react-broadcast/test/inject-feature-toggle.spec.jsx @@ -13,11 +13,11 @@ describe('without `propKey`', () => { describe('when feature is disabled', () => { it('should render receive the flag value as `false`', async () => { const TestComponent = injectFeatureToggle('disabledFeature')( - components.FlagsToComponent + components.FlagsToComponent, ); const { waitUntilConfigured, queryByFlagName } = render( - + , ); expect(queryByFlagName('isFeatureEnabled')).toHaveTextContent('false'); @@ -28,7 +28,7 @@ describe('without `propKey`', () => { describe('when enabling feature', () => { it('should render the component representing a enabled feature', async () => { const TestComponent = injectFeatureToggle('disabledFeature')( - components.FlagsToComponent + components.FlagsToComponent, ); const { waitUntilConfigured, queryByFlagName, changeFlagVariation } = @@ -46,11 +46,11 @@ describe('without `propKey`', () => { describe('when feature is enabled', () => { it('should render receive the flag value as `true`', async () => { const TestComponent = injectFeatureToggle('enabledFeature')( - components.FlagsToComponent + components.FlagsToComponent, ); const { waitUntilConfigured, queryByFlagName } = render( - + , ); await waitUntilConfigured(); @@ -65,11 +65,11 @@ describe('with `propKey`', () => { it('should render receive the flag value as `false`', async () => { const TestComponent = injectFeatureToggle( 'disabledFeature', - 'customPropKey' + 'customPropKey', )(components.FlagsToComponent); const { waitUntilConfigured, queryByFlagName } = render( - + , ); await waitUntilConfigured(); diff --git a/packages/react-broadcast/test/inject-feature-toggles.spec.jsx b/packages/react-broadcast/test/inject-feature-toggles.spec.jsx index f93f47848..b98cbe73b 100644 --- a/packages/react-broadcast/test/inject-feature-toggles.spec.jsx +++ b/packages/react-broadcast/test/inject-feature-toggles.spec.jsx @@ -67,7 +67,7 @@ describe('with `propKey`', () => { it('should have feature enabling prop for `enabledFeature`', async () => { const TestComponent = injectFeatureToggles( ['disabledFeature', 'enabledFeature'], - 'onOffs' + 'onOffs', )(FlagsToComponentWithPropKey); const { waitUntilConfigured, queryByFlagName } = render(); @@ -80,7 +80,7 @@ describe('with `propKey`', () => { it('should have feature disabling prop for `disabledFeature`', async () => { const TestComponent = injectFeatureToggles( ['disabledFeature', 'enabledFeature'], - 'onOffs' + 'onOffs', )(FlagsToComponentWithPropKey); const { waitUntilConfigured, queryByFlagName } = render(); diff --git a/packages/react-broadcast/test/test-provider.spec.jsx b/packages/react-broadcast/test/test-provider.spec.jsx index b237d407d..cf8ee6541 100644 --- a/packages/react-broadcast/test/test-provider.spec.jsx +++ b/packages/react-broadcast/test/test-provider.spec.jsx @@ -29,7 +29,7 @@ const render = ({ flags, status } = {}) => { rtlRender( - + , ); }; diff --git a/packages/react-broadcast/test/toggle-feature.spec.jsx b/packages/react-broadcast/test/toggle-feature.spec.jsx index 29139b999..23d5d9f20 100644 --- a/packages/react-broadcast/test/toggle-feature.spec.jsx +++ b/packages/react-broadcast/test/toggle-feature.spec.jsx @@ -27,7 +27,7 @@ function TestDisabledComponent() { describe('when feature is disabled', () => { it('should not render the component representing a enabled feature', async () => { const { waitUntilConfigured, queryByFlagName } = render( - + , ); expect(queryByFlagName('disabledFeature')).not.toBeInTheDocument(); @@ -52,14 +52,14 @@ describe('when feature is disabled', () => { describe('when feature is enabled', () => { it('should render the component representing a enabled feature', async () => { const { waitUntilConfigured, queryByFlagName } = render( - + , ); await waitUntilConfigured(); expect(queryByFlagName('enabledFeature')).toHaveAttribute( 'data-flag-status', - 'enabled' + 'enabled', ); }); }); diff --git a/packages/react-broadcast/test/use-adapter-status.spec.jsx b/packages/react-broadcast/test/use-adapter-status.spec.jsx index b661d02bc..9f3c08c7a 100644 --- a/packages/react-broadcast/test/use-adapter-status.spec.jsx +++ b/packages/react-broadcast/test/use-adapter-status.spec.jsx @@ -29,6 +29,7 @@ it('should indicate the adapter not configured yet', async () => { await waitUntilConfigured(); }); +// oxlint-disable-next-line jest/expect-expect -- assertions via screen.findByText which throws on failure it('should indicate the adapter is configured', async () => { const { waitUntilConfigured } = render(); diff --git a/packages/react-broadcast/test/use-all-feature-toggles.spec.jsx b/packages/react-broadcast/test/use-all-feature-toggles.spec.jsx index bcd113050..53b016959 100644 --- a/packages/react-broadcast/test/use-all-feature-toggles.spec.jsx +++ b/packages/react-broadcast/test/use-all-feature-toggles.spec.jsx @@ -13,10 +13,10 @@ import { Configure } from '../src//configure'; import { useAllFeatureToggles } from '../src/use-all-feature-toggles'; const disabledDefaultFlags = Object.fromEntries( - Object.entries(defaultFlags).filter(([, isEnabled]) => !isEnabled) + Object.entries(defaultFlags).filter(([, isEnabled]) => !isEnabled), ); const enabledDefaultFlags = Object.fromEntries( - Object.entries(defaultFlags).filter(([, isEnabled]) => isEnabled) + Object.entries(defaultFlags).filter(([, isEnabled]) => isEnabled), ); const render = (TestComponent, { adapter, adapterArgs } = {}) => @@ -44,29 +44,33 @@ function TestComponent() { describe('with one adapter', () => { describe('disabled features', () => { - it.each( - Object.keys(disabledDefaultFlags) - )('should list disabled feature "%s"', async (featureName) => { - const { waitUntilConfigured } = render(); + it.each(Object.keys(disabledDefaultFlags))( + 'should list disabled feature "%s"', + async (featureName) => { + const { waitUntilConfigured } = render(); - await waitUntilConfigured(); + await waitUntilConfigured(); - expect( - screen.getByText(`${featureName} is disabled`) - ).toBeInTheDocument(); - }); + expect( + screen.getByText(`${featureName} is disabled`), + ).toBeInTheDocument(); + }, + ); }); describe('enabled features', () => { - it.each( - Object.keys(enabledDefaultFlags) - )('should list enabled feature "%s"', async (featureName) => { - const { waitUntilConfigured } = render(); + it.each(Object.keys(enabledDefaultFlags))( + 'should list enabled feature "%s"', + async (featureName) => { + const { waitUntilConfigured } = render(); - await waitUntilConfigured(); + await waitUntilConfigured(); - expect(screen.getByText(`${featureName} is enabled`)).toBeInTheDocument(); - }); + expect( + screen.getByText(`${featureName} is enabled`), + ).toBeInTheDocument(); + }, + ); }); }); @@ -95,33 +99,37 @@ describe('when combining adapters', () => { }); describe('without flag updating', () => { - it.each( - Object.keys(disabledDefaultFlags) - )('should list disabled feature "%s"', async (featureName) => { - const { waitUntilConfigured } = render(, { - adapter: combineAdapters, - adapterArgs, - }); + it.each(Object.keys(disabledDefaultFlags))( + 'should list disabled feature "%s"', + async (featureName) => { + const { waitUntilConfigured } = render(, { + adapter: combineAdapters, + adapterArgs, + }); - await waitUntilConfigured(); + await waitUntilConfigured(); - expect( - screen.getByText(`${featureName} is disabled`) - ).toBeInTheDocument(); - }); - - it.each( - Object.keys(enabledDefaultFlags) - )('should list enabled feature "%s"', async (featureName) => { - const { waitUntilConfigured } = render(, { - adapter: combineAdapters, - adapterArgs, - }); + expect( + screen.getByText(`${featureName} is disabled`), + ).toBeInTheDocument(); + }, + ); + + it.each(Object.keys(enabledDefaultFlags))( + 'should list enabled feature "%s"', + async (featureName) => { + const { waitUntilConfigured } = render(, { + adapter: combineAdapters, + adapterArgs, + }); - await waitUntilConfigured(); + await waitUntilConfigured(); - expect(screen.getByText(`${featureName} is enabled`)).toBeInTheDocument(); - }); + expect( + screen.getByText(`${featureName} is enabled`), + ).toBeInTheDocument(); + }, + ); }); describe('with flag updating', () => { @@ -132,13 +140,13 @@ describe('when combining adapters', () => { { adapter: combineAdapters, adapterArgs, - } + }, ); await waitUntilConfiguredFirstRender(); expect( - screen.queryByText('updatedFlag is enabled') + screen.queryByText('updatedFlag is enabled'), ).not.toBeInTheDocument(); act(() => { @@ -148,7 +156,7 @@ describe('when combining adapters', () => { }); expect( - screen.getByText('updatedMemoryAdapterFlag is enabled') + screen.getByText('updatedMemoryAdapterFlag is enabled'), ).toBeInTheDocument(); act(() => { @@ -158,7 +166,7 @@ describe('when combining adapters', () => { }); expect( - screen.getByText('updatedLocalstorageAdapterFlag is enabled') + screen.getByText('updatedLocalstorageAdapterFlag is enabled'), ).toBeInTheDocument(); }); }); @@ -169,13 +177,13 @@ describe('when combining adapters', () => { { adapter: combineAdapters, adapterArgs, - } + }, ); await waitUntilConfiguredFirstRender(); expect( - screen.queryByText('updatedFlag is enabled') + screen.queryByText('updatedFlag is enabled'), ).not.toBeInTheDocument(); act(() => { @@ -188,7 +196,7 @@ describe('when combining adapters', () => { }); expect( - screen.getByText('updatedShared is enabled') + screen.getByText('updatedShared is enabled'), ).toBeInTheDocument(); act(() => { @@ -201,7 +209,7 @@ describe('when combining adapters', () => { }); expect( - screen.getByText('updatedShared is disabled') + screen.getByText('updatedShared is disabled'), ).toBeInTheDocument(); }); }); diff --git a/packages/react-broadcast/vitest.config.ts b/packages/react-broadcast/vitest.config.ts index 516a3a1ec..fcd40d7ed 100644 --- a/packages/react-broadcast/vitest.config.ts +++ b/packages/react-broadcast/vitest.config.ts @@ -1,4 +1,5 @@ import { defineProject, mergeConfig } from 'vitest/config'; + import configShared from '../../vitest.shared.ts'; export default mergeConfig( @@ -7,5 +8,5 @@ export default mergeConfig( test: { environment: 'jsdom', }, - }) + }), ); diff --git a/packages/react-redux/package.json b/packages/react-redux/package.json index 57cd552a9..463fea9a7 100644 --- a/packages/react-redux/package.json +++ b/packages/react-redux/package.json @@ -2,14 +2,29 @@ "name": "@flopflip/react-redux", "version": "15.1.7", "description": "A feature toggle wrapper to use LaunchDarkly with React Redux", - "sideEffects": false, - "type": "module", - "exports": { - ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs" - } + "keywords": [ + "LaunchDarkly", + "client", + "feature-flags", + "feature-toggles" + ], + "homepage": "https://github.com/tdeekens/flopflip#readme", + "bugs": { + "url": "https://github.com/tdeekens/flopflip/issues" + }, + "license": "MIT", + "author": "Tobias Deekens ", + "repository": { + "type": "git", + "url": "https://github.com/tdeekens/flopflip.git", + "directory": "packages/react-redux" }, + "files": [ + "readme.md", + "dist/**" + ], + "type": "module", + "sideEffects": false, "main": "./dist/index.js", "browser": "./dist/index.js", "typesVersions": { @@ -20,6 +35,15 @@ ] } }, + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, + "publishConfig": { + "access": "public" + }, "scripts": { "build": "rimraf dist && tsup", "check-types": "tsc --noEmit", @@ -27,24 +51,14 @@ "test:watch": "vitest", "dev": "tsup --watch --clean=false" }, - "files": [ - "readme.md", - "dist/**" - ], - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "https://github.com/tdeekens/flopflip.git", - "directory": "packages/react-redux" - }, - "author": "Tobias Deekens ", - "license": "MIT", - "bugs": { - "url": "https://github.com/tdeekens/flopflip/issues" + "dependencies": { + "@babel/runtime": "7.28.6", + "@flopflip/react": "workspace:*", + "@flopflip/types": "workspace:*", + "@reduxjs/toolkit": "2.11.2", + "@types/react": "19.2.14", + "@types/react-redux": "7.1.34" }, - "homepage": "https://github.com/tdeekens/flopflip#readme", "devDependencies": { "@flopflip/combine-adapters": "workspace:*", "@flopflip/localstorage-adapter": "workspace:*", @@ -57,24 +71,10 @@ "redux": "5.0.1", "tsup": "8.5.1" }, - "dependencies": { - "@babel/runtime": "7.28.6", - "@flopflip/react": "workspace:*", - "@flopflip/types": "workspace:*", - "@reduxjs/toolkit": "2.11.2", - "@types/react": "19.2.14", - "@types/react-redux": "7.1.34" - }, "peerDependencies": { "react": "18.x || 19.x", "react-dom": "18.x || 19.x", "react-redux": "9.x", "redux": "5.x" - }, - "keywords": [ - "feature-flags", - "feature-toggles", - "LaunchDarkly", - "client" - ] + } } diff --git a/packages/react-redux/src/branch-on-feature-toggle.tsx b/packages/react-redux/src/branch-on-feature-toggle.tsx index 7460721e6..12c02a29d 100644 --- a/packages/react-redux/src/branch-on-feature-toggle.tsx +++ b/packages/react-redux/src/branch-on-feature-toggle.tsx @@ -1,5 +1,4 @@ import type { TFlagName, TFlagVariation } from '@flopflip/types'; -// biome-ignore lint/style/useImportType: false positive import React from 'react'; import { useFeatureToggle } from './use-feature-toggle'; @@ -10,7 +9,7 @@ type TBranchOnFeatureToggleOptions = { }; function branchOnFeatureToggle>( { flag: flagName, variation: flagVariation }: TBranchOnFeatureToggleOptions, - UntoggledComponent?: React.ComponentType + UntoggledComponent?: React.ComponentType, ) { return (ToggledComponent: React.ComponentType) => { function WrappedToggledComponent(ownProps: OwnProps) { diff --git a/packages/react-redux/src/configure.tsx b/packages/react-redux/src/configure.tsx index ae65586b2..ee8ae33a3 100644 --- a/packages/react-redux/src/configure.tsx +++ b/packages/react-redux/src/configure.tsx @@ -5,8 +5,9 @@ import type { TConfigureAdapterProps, TFlags, } from '@flopflip/types'; -// biome-ignore lint/correctness/noUnusedImports: false positive +// oxlint-disable-next-line no-unused-vars -- false positive import React from 'react'; + import { useUpdateFlags } from './use-update-flags'; import { useUpdateStatus } from './use-update-status'; diff --git a/packages/react-redux/src/ducks/flags.ts b/packages/react-redux/src/ducks/flags.ts index 2ba724fb0..0ed6a2299 100644 --- a/packages/react-redux/src/ducks/flags.ts +++ b/packages/react-redux/src/ducks/flags.ts @@ -22,7 +22,7 @@ const flagsSlice = createSlice({ state, action: PayloadAction< TFlagsChange & { adapterIdentifiers: TAdapterIdentifiers[] } - > + >, ) { if (action.payload.id) { state[action.payload.id] = { @@ -41,7 +41,7 @@ const flagsSlice = createSlice({ }, prepare( flagsChange: TFlagsChange, - adapterIdentifiers: TAdapterIdentifiers[] + adapterIdentifiers: TAdapterIdentifiers[], ) { return { payload: { ...flagsChange, adapterIdentifiers }, @@ -55,7 +55,7 @@ export const { updateFlags } = flagsSlice.actions; export const reducer = flagsSlice.reducer; export const createReducer = (preloadedState: TFlagsContext = initialState) => { - // biome-ignore lint/style/useDefaultParameterLast: false positive + // oxlint-disable-next-line default-param-last -- false positive return (state = preloadedState, action: ReturnType) => reducer(state, action); }; diff --git a/packages/react-redux/src/ducks/index.ts b/packages/react-redux/src/ducks/index.ts index c4546ed6b..602f85ff1 100644 --- a/packages/react-redux/src/ducks/index.ts +++ b/packages/react-redux/src/ducks/index.ts @@ -15,7 +15,7 @@ export const flopflipReducer = combineReducers({ status: statusReducer, }); export const createFlopflipReducer = ( - preloadedState: TFlagsContext = { memory: {} } + preloadedState: TFlagsContext = { memory: {} }, ) => combineReducers({ flags: createFlagsReducer(preloadedState), diff --git a/packages/react-redux/src/ducks/status.ts b/packages/react-redux/src/ducks/status.ts index 15cd97e4d..6e343dee2 100644 --- a/packages/react-redux/src/ducks/status.ts +++ b/packages/react-redux/src/ducks/status.ts @@ -20,7 +20,7 @@ const statusSlice = createSlice({ state, action: PayloadAction< TAdapterStatusChange & { adapterIdentifiers: TAdapterIdentifiers[] } - > + >, ) { if (action.payload.id) { state[action.payload.id] = { @@ -40,7 +40,7 @@ const statusSlice = createSlice({ }, prepare( statusChange: TAdapterStatusChange, - adapterIdentifiers: TAdapterIdentifiers[] + adapterIdentifiers: TAdapterIdentifiers[], ) { return { payload: { ...statusChange, adapterIdentifiers }, diff --git a/packages/react-redux/src/enhancer.ts b/packages/react-redux/src/enhancer.ts index 60c5def39..4e2c59235 100644 --- a/packages/react-redux/src/enhancer.ts +++ b/packages/react-redux/src/enhancer.ts @@ -20,9 +20,9 @@ const configureAdapter = createAction<{ function createFlopFlipEnhancer( adapter: TAdapter, - adapterArgs: TAdapterArgs + adapterArgs: TAdapterArgs, ): ( - next: StoreEnhancerStoreCreator + next: StoreEnhancerStoreCreator, ) => (reducer: Reducer, preloadedState?: StoreState) => Store { return (next) => (...args) => { @@ -38,7 +38,7 @@ function createFlopFlipEnhancer( }, onStatusStateChange: (statusChange: TAdapterStatusChange) => { store.dispatch( - updateStatus(statusChange, Object.keys(allAdapterIdentifiers)) + updateStatus(statusChange, Object.keys(allAdapterIdentifiers)), ); }, }); diff --git a/packages/react-redux/src/inject-feature-toggle.tsx b/packages/react-redux/src/inject-feature-toggle.tsx index 561e53469..d7f565032 100644 --- a/packages/react-redux/src/inject-feature-toggle.tsx +++ b/packages/react-redux/src/inject-feature-toggle.tsx @@ -4,7 +4,6 @@ import { wrapDisplayName, } from '@flopflip/react'; import type { TFlagName, TFlagVariation } from '@flopflip/types'; -// biome-ignore lint/style/useImportType: false positive import React from 'react'; import { useFlagVariations } from './use-flag-variations'; @@ -14,10 +13,10 @@ type InjectedProps = Record; const injectFeatureToggle = >( flagName: TFlagName, - propKey: string = DEFAULT_FLAG_PROP_KEY + propKey: string = DEFAULT_FLAG_PROP_KEY, ) => ( - Component: React.ComponentType + Component: React.ComponentType, ): React.ComponentType => { function WrappedComponent(ownProps: OwnProps) { const [flagVariation] = useFlagVariations([flagName]); diff --git a/packages/react-redux/src/inject-feature-toggles.tsx b/packages/react-redux/src/inject-feature-toggles.tsx index ce6b213f6..3b78be34d 100644 --- a/packages/react-redux/src/inject-feature-toggles.tsx +++ b/packages/react-redux/src/inject-feature-toggles.tsx @@ -4,7 +4,6 @@ import { wrapDisplayName, } from '@flopflip/react'; import type { TFlagName, TFlags } from '@flopflip/types'; -// biome-ignore lint/style/useImportType: false positive import React from 'react'; import { useFlagVariations } from './use-flag-variations'; @@ -14,10 +13,10 @@ type InjectedProps = Record; const injectFeatureToggles = >( flagNames: TFlagName[], - propKey: string = DEFAULT_FLAGS_PROP_KEY + propKey: string = DEFAULT_FLAGS_PROP_KEY, ) => ( - Component: React.ComponentType + Component: React.ComponentType, ): React.ComponentType => { function WrappedComponent(ownProps: OwnProps) { const flagVariations = useFlagVariations(flagNames); @@ -25,7 +24,7 @@ const injectFeatureToggles = flagNames.map((flagName, indexOfFlagName) => [ flagName, flagVariations[indexOfFlagName], - ]) + ]), ); const props = { ...ownProps, diff --git a/packages/react-redux/src/toggle-feature.tsx b/packages/react-redux/src/toggle-feature.tsx index 5840d82cf..693492766 100644 --- a/packages/react-redux/src/toggle-feature.tsx +++ b/packages/react-redux/src/toggle-feature.tsx @@ -3,8 +3,9 @@ import { type TToggleFeatureProps, } from '@flopflip/react'; import type { TFlagName, TFlagVariation } from '@flopflip/types'; -// biome-ignore lint/correctness/noUnusedImports: false positive +// oxlint-disable-next-line no-unused-vars -- false positive import React from 'react'; + import { useFeatureToggle } from './use-feature-toggle'; type TProps = { diff --git a/packages/react-redux/src/use-all-feature-toggles.ts b/packages/react-redux/src/use-all-feature-toggles.ts index f48ef211a..2a676c8d1 100644 --- a/packages/react-redux/src/use-all-feature-toggles.ts +++ b/packages/react-redux/src/use-all-feature-toggles.ts @@ -9,14 +9,14 @@ function useAllFeatureToggles(): TFlags { const allFlags = useSelector(selectFlags()); const reversedAdapterEffectIdentifiers = [ ...adapterContext.adapterEffectIdentifiers, - ].reverse(); + ].toReversed(); return reversedAdapterEffectIdentifiers.reduce( (_allFlags, adapterIdentifier) => ({ ..._allFlags, ...allFlags[adapterIdentifier], }), - {} + {}, ); } diff --git a/packages/react-redux/src/use-feature-toggle.ts b/packages/react-redux/src/use-feature-toggle.ts index ccf8a7771..02b357f7a 100644 --- a/packages/react-redux/src/use-feature-toggle.ts +++ b/packages/react-redux/src/use-feature-toggle.ts @@ -7,7 +7,7 @@ import { selectFlags } from './ducks/flags'; function useFeatureToggle( flagName: TFlagName, - flagVariation: TFlagVariation = true + flagVariation: TFlagVariation = true, ) { const adapterContext = useAdapterContext(); const allFlags = useSelector(selectFlags()); @@ -16,7 +16,7 @@ function useFeatureToggle( allFlags, adapterContext.adapterEffectIdentifiers, flagName, - flagVariation + flagVariation, ); useDebugValue({ diff --git a/packages/react-redux/src/use-feature-toggles.ts b/packages/react-redux/src/use-feature-toggles.ts index 4212c691e..11d76de7e 100644 --- a/packages/react-redux/src/use-feature-toggles.ts +++ b/packages/react-redux/src/use-feature-toggles.ts @@ -14,14 +14,14 @@ function useFeatureToggles(flags: TFlags) { allFlags, adapterContext.adapterEffectIdentifiers, flagName, - flagVariation + flagVariation, ); previousFlags.push(isFeatureEnabled); return previousFlags; }, - [] + [], ); return requestedFlags; diff --git a/packages/react-redux/src/use-flag-variations.ts b/packages/react-redux/src/use-flag-variations.ts index a08a19a0c..2fcbecbcb 100644 --- a/packages/react-redux/src/use-flag-variations.ts +++ b/packages/react-redux/src/use-flag-variations.ts @@ -5,7 +5,7 @@ import { useSelector } from 'react-redux'; import { selectFlags } from './ducks/flags'; function useFlagVariations( - flagNames: Array + flagNames: (TFlagName | undefined)[], ): TFlagVariation[] { const adapterContext = useAdapterContext(); const allFlags = useSelector(selectFlags()); @@ -13,8 +13,8 @@ function useFlagVariations( getFlagVariation( allFlags, adapterContext.adapterEffectIdentifiers, - requestedVariation - ) + requestedVariation, + ), ); return flagVariations; diff --git a/packages/react-redux/src/use-update-flags.ts b/packages/react-redux/src/use-update-flags.ts index a8ac7605c..f5c55b139 100644 --- a/packages/react-redux/src/use-update-flags.ts +++ b/packages/react-redux/src/use-update-flags.ts @@ -19,7 +19,7 @@ const useUpdateFlags = ({ return useCallback( (flagsChange: TFlagsChange) => dispatch(updateFlags(flagsChange, adapterIdentifiers)), - [dispatch, adapterIdentifiers] + [dispatch, adapterIdentifiers], ); }; diff --git a/packages/react-redux/src/use-update-status.ts b/packages/react-redux/src/use-update-status.ts index 169968d47..37c9cd195 100644 --- a/packages/react-redux/src/use-update-status.ts +++ b/packages/react-redux/src/use-update-status.ts @@ -14,7 +14,7 @@ const useUpdateStatus = (): TAdapterEventHandlers['onStatusStateChange'] => { return useCallback( (statusChange: TAdapterStatusChange) => dispatch(updateStatus(statusChange, Object.keys(allAdapterIdentifiers))), - [dispatch] + [dispatch], ); }; diff --git a/packages/react-redux/test/branch-on-feature-toggle.spec.jsx b/packages/react-redux/test/branch-on-feature-toggle.spec.jsx index ec18dd23b..af13b2060 100644 --- a/packages/react-redux/test/branch-on-feature-toggle.spec.jsx +++ b/packages/react-redux/test/branch-on-feature-toggle.spec.jsx @@ -22,12 +22,12 @@ describe('without `untoggledComponent', () => { [STATE_SLICE]: { flags: { memory: { disabledFeature: false } } }, }); const TestComponent = branchOnFeatureToggle({ flag: 'disabledFeature' })( - components.ToggledComponent + components.ToggledComponent, ); const { waitUntilConfigured, queryByFlagName } = render( store, - + , ); await waitUntilConfigured(); @@ -62,19 +62,19 @@ describe('without `untoggledComponent', () => { [STATE_SLICE]: { flags: { memory: { enabledFeature: true } } }, }); const TestComponent = branchOnFeatureToggle({ flag: 'enabledFeature' })( - components.ToggledComponent + components.ToggledComponent, ); const { waitUntilConfigured, queryByFlagName } = render( store, - + , ); await waitUntilConfigured(); expect(queryByFlagName('isFeatureEnabled')).toHaveAttribute( 'data-flag-status', - 'enabled' + 'enabled', ); }); }); @@ -88,19 +88,19 @@ describe('with `untoggledComponent', () => { }); const TestComponent = branchOnFeatureToggle( { flag: 'disabledFeature' }, - components.UntoggledComponent + components.UntoggledComponent, )(components.ToggledComponent); const { waitUntilConfigured, queryByFlagName } = render( store, - + , ); await waitUntilConfigured(); expect(queryByFlagName('isFeatureEnabled')).not.toHaveAttribute( 'data-flag-status', - 'enabled' + 'enabled', ); }); @@ -110,19 +110,19 @@ describe('with `untoggledComponent', () => { }); const TestComponent = branchOnFeatureToggle( { flag: 'disabledFeature' }, - components.UntoggledComponent + components.UntoggledComponent, )(components.ToggledComponent); const { waitUntilConfigured, queryByFlagName } = render( store, - + , ); await waitUntilConfigured(); expect(queryByFlagName('isFeatureEnabled')).toHaveAttribute( 'data-flag-status', - 'disabled' + 'disabled', ); }); }); @@ -133,19 +133,19 @@ describe('with `untoggledComponent', () => { [STATE_SLICE]: { flags: { memory: { enabledFeature: true } } }, }); const TestComponent = branchOnFeatureToggle({ flag: 'enabledFeature' })( - components.ToggledComponent + components.ToggledComponent, ); const { waitUntilConfigured, queryByFlagName } = render( store, - + , ); await waitUntilConfigured(); expect(queryByFlagName('isFeatureEnabled')).toHaveAttribute( 'data-flag-status', - 'enabled' + 'enabled', ); }); @@ -154,19 +154,19 @@ describe('with `untoggledComponent', () => { [STATE_SLICE]: { flags: { memory: { enabledFeature: true } } }, }); const TestComponent = branchOnFeatureToggle({ flag: 'enabledFeature' })( - components.ToggledComponent + components.ToggledComponent, ); const { waitUntilConfigured, queryByFlagName } = render( store, - + , ); await waitUntilConfigured(); expect(queryByFlagName('isFeatureEnabled')).not.toHaveAttribute( 'data-flag-status', - 'disabled' + 'disabled', ); }); }); diff --git a/packages/react-redux/test/configure.spec.jsx b/packages/react-redux/test/configure.spec.jsx index 580f223aa..c1952add7 100644 --- a/packages/react-redux/test/configure.spec.jsx +++ b/packages/react-redux/test/configure.spec.jsx @@ -35,7 +35,7 @@ const render = () => { - + , ); const waitUntilConfigured = () => screen.findByText(/Is configured: Yes/i); diff --git a/packages/react-redux/test/ducks/flags.spec.js b/packages/react-redux/test/ducks/flags.spec.js index 4e26579c6..ac497a6aa 100644 --- a/packages/react-redux/test/ducks/flags.spec.js +++ b/packages/react-redux/test/ducks/flags.spec.js @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; -import { STATE_SLICE } from '../../src/constants'; +import { STATE_SLICE } from '../../src/constants'; import { reducer, selectFlag, @@ -172,7 +172,7 @@ describe('selectors', () => { expect(selectFlag('flagC', ['graphql'])(state)).toEqual(true); expect(selectFlag('flagA', ['graphql', 'memory'])(state)).toEqual(true); expect(selectFlag('flagA', ['memory', 'graphql'])(state)).toEqual( - false + false, ); }); }); diff --git a/packages/react-redux/test/ducks/status.spec.js b/packages/react-redux/test/ducks/status.spec.js index b4f0e6fcd..8831b87c4 100644 --- a/packages/react-redux/test/ducks/status.spec.js +++ b/packages/react-redux/test/ducks/status.spec.js @@ -24,7 +24,7 @@ describe('action creators', () => { status: { configurationStatus: AdapterConfigurationStatus.Configured, }, - }) + }), ).toEqual({ type: expect.any(String), payload: { @@ -46,8 +46,8 @@ describe('action creators', () => { configurationStatus: AdapterConfigurationStatus.Configured, }, }, - allAdapterIdentifiers - ) + allAdapterIdentifiers, + ), ).toEqual({ type: expect.any(String), payload: { @@ -77,13 +77,13 @@ describe('reducers', () => { it('should set the new status', () => { expect( - reducer(undefined, { type: 'status/updateStatus', payload }) + reducer(undefined, { type: 'status/updateStatus', payload }), ).toEqual( expect.objectContaining({ memory: { configurationStatus: AdapterConfigurationStatus.Configuring, }, - }) + }), ); }); }); @@ -107,8 +107,8 @@ describe('reducers', () => { configurationStatus: AdapterConfigurationStatus.Configured, }, }, - { type: 'status/updateStatus', payload } - ) + { type: 'status/updateStatus', payload }, + ), ).toEqual({ memory: { configurationStatus: AdapterConfigurationStatus.Configuring, @@ -148,7 +148,7 @@ describe('selectors', () => { isConfiguring: false, isReady: false, isUnconfigured: false, - }) + }), ); }); }); @@ -157,14 +157,14 @@ describe('selectors', () => { expect(selectStatus({ adapterIdentifiers: ['http'] })(state)).toEqual( expect.objectContaining({ isUnconfigured: true, - }) + }), ); }); it('should return configuring status', () => { expect(selectStatus({ adapterIdentifiers: ['memory'] })(state)).toEqual( expect.objectContaining({ isConfiguring: true, - }) + }), ); }); }); diff --git a/packages/react-redux/test/enhancer.spec.js b/packages/react-redux/test/enhancer.spec.js index 58460fe4b..ebad8714c 100644 --- a/packages/react-redux/test/enhancer.spec.js +++ b/packages/react-redux/test/enhancer.spec.js @@ -41,7 +41,7 @@ describe('when creating enhancer', () => { adapterArgs, expect.objectContaining({ onFlagsStateChange: expect.any(Function), - }) + }), ); }); @@ -50,7 +50,7 @@ describe('when creating enhancer', () => { adapterArgs, expect.objectContaining({ onStatusStateChange: expect.any(Function), - }) + }), ); }); @@ -74,7 +74,7 @@ describe('when creating enhancer', () => { it('should invoke `dispatch` with `updateFlags`', () => { expect(dispatch).toHaveBeenCalledWith( - updateFlags(nextFlags, [adapter.id]) + updateFlags(nextFlags, [adapter.id]), ); }); }); @@ -99,7 +99,7 @@ describe('when creating enhancer', () => { it('should invoke `dispatch` with `updateStatus`', () => { expect(dispatch).toHaveBeenCalledWith( - updateStatus(nextStatus, Object.keys(allAdapterIdentifiers)) + updateStatus(nextStatus, Object.keys(allAdapterIdentifiers)), ); }); }); diff --git a/packages/react-redux/test/inject-feature-toggle.spec.jsx b/packages/react-redux/test/inject-feature-toggle.spec.jsx index 85d3e6fa6..c171359b7 100644 --- a/packages/react-redux/test/inject-feature-toggle.spec.jsx +++ b/packages/react-redux/test/inject-feature-toggle.spec.jsx @@ -22,12 +22,12 @@ describe('without `propKey`', () => { [STATE_SLICE]: { flags: { memory: { disabledFeature: false } } }, }); const TestComponent = injectFeatureToggle('disabledFeature')( - components.FlagsToComponent + components.FlagsToComponent, ); const { waitUntilConfigured, queryByFlagName } = render( store, - + , ); await waitUntilConfigured(); @@ -41,7 +41,7 @@ describe('without `propKey`', () => { [STATE_SLICE]: { flags: { memory: { disabledFeature: false } } }, }); const TestComponent = injectFeatureToggle('disabledFeature')( - components.FlagsToComponent + components.FlagsToComponent, ); const { waitUntilConfigured, queryByFlagName, changeFlagVariation } = @@ -62,12 +62,12 @@ describe('without `propKey`', () => { [STATE_SLICE]: { flags: { memory: { enabledFeature: true } } }, }); const TestComponent = injectFeatureToggle('enabledFeature')( - components.FlagsToComponent + components.FlagsToComponent, ); const { waitUntilConfigured, queryByFlagName } = render( store, - + , ); await waitUntilConfigured(); @@ -85,12 +85,12 @@ describe('with `propKey`', () => { }); const TestComponent = injectFeatureToggle( 'disabledFeature', - 'customPropKey' + 'customPropKey', )(components.FlagsToComponent); const { waitUntilConfigured, queryByFlagName } = render( store, - + , ); await waitUntilConfigured(); diff --git a/packages/react-redux/test/inject-feature-toggles.spec.jsx b/packages/react-redux/test/inject-feature-toggles.spec.jsx index 4088daee4..ade7db672 100644 --- a/packages/react-redux/test/inject-feature-toggles.spec.jsx +++ b/packages/react-redux/test/inject-feature-toggles.spec.jsx @@ -37,7 +37,7 @@ describe('injectFeatureToggles', () => { const { waitUntilConfigured, queryByFlagName } = render( store, - + , ); await waitUntilConfigured(); @@ -58,7 +58,7 @@ describe('injectFeatureToggles', () => { const { waitUntilConfigured, queryByFlagName } = render( store, - + , ); await waitUntilConfigured(); @@ -99,12 +99,12 @@ describe('injectFeatureToggles', () => { }); const TestComponent = injectFeatureToggles( ['disabledFeature', 'enabledFeature'], - 'onOffs' + 'onOffs', )(FlagsToComponentWithPropKey); const { waitUntilConfigured, queryByFlagName } = render( store, - + , ); await waitUntilConfigured(); @@ -120,12 +120,12 @@ describe('injectFeatureToggles', () => { }); const TestComponent = injectFeatureToggles( ['disabledFeature', 'enabledFeature'], - 'onOffs' + 'onOffs', )(FlagsToComponentWithPropKey); const { waitUntilConfigured, queryByFlagName } = render( store, - + , ); await waitUntilConfigured(); diff --git a/packages/react-redux/test/toggle-feature.spec.jsx b/packages/react-redux/test/toggle-feature.spec.jsx index 623360946..6b4d8fc1c 100644 --- a/packages/react-redux/test/toggle-feature.spec.jsx +++ b/packages/react-redux/test/toggle-feature.spec.jsx @@ -32,7 +32,7 @@ describe('', () => { const { waitUntilConfigured, queryByFlagName } = render( store, - + , ); await waitUntilConfigured(); @@ -81,14 +81,14 @@ describe('', () => { const { waitUntilConfigured, queryByFlagName } = render( store, - + , ); await waitUntilConfigured(); expect(queryByFlagName('enabledFeature')).toHaveAttribute( 'data-flag-status', - 'enabled' + 'enabled', ); }); }); diff --git a/packages/react-redux/test/use-all-feature-toggles.spec.jsx b/packages/react-redux/test/use-all-feature-toggles.spec.jsx index a66df5dae..33907680d 100644 --- a/packages/react-redux/test/use-all-feature-toggles.spec.jsx +++ b/packages/react-redux/test/use-all-feature-toggles.spec.jsx @@ -18,10 +18,10 @@ import { createStore } from './test-utils'; vi.mock('tiny-warning'); const disabledDefaultFlags = Object.fromEntries( - Object.entries(defaultFlags).filter(([, isEnabled]) => !isEnabled) + Object.entries(defaultFlags).filter(([, isEnabled]) => !isEnabled), ); const enabledDefaultFlags = Object.fromEntries( - Object.entries(defaultFlags).filter(([, isEnabled]) => isEnabled) + Object.entries(defaultFlags).filter(([, isEnabled]) => isEnabled), ); const render = (store, TestComponent, { adapter, adapterArgs } = {}) => @@ -50,34 +50,38 @@ function TestComponent() { describe('with one adapter', () => { describe('disabled features', () => { - it.each( - Object.keys(disabledDefaultFlags) - )('should list disabled feature "%s"', async (featureName) => { - const store = createStore({ - [STATE_SLICE]: { flags: { memory: defaultFlags } }, - }); - const { waitUntilConfigured } = render(store, ); + it.each(Object.keys(disabledDefaultFlags))( + 'should list disabled feature "%s"', + async (featureName) => { + const store = createStore({ + [STATE_SLICE]: { flags: { memory: defaultFlags } }, + }); + const { waitUntilConfigured } = render(store, ); - await waitUntilConfigured(); + await waitUntilConfigured(); - expect( - screen.getByText(`${featureName} is disabled`) - ).toBeInTheDocument(); - }); + expect( + screen.getByText(`${featureName} is disabled`), + ).toBeInTheDocument(); + }, + ); }); describe('enabled features', () => { - it.each( - Object.keys(enabledDefaultFlags) - )('should list enabled feature "%s"', async (featureName) => { - const store = createStore({ - [STATE_SLICE]: { flags: { memory: defaultFlags } }, - }); - const { waitUntilConfigured } = render(store, ); + it.each(Object.keys(enabledDefaultFlags))( + 'should list enabled feature "%s"', + async (featureName) => { + const store = createStore({ + [STATE_SLICE]: { flags: { memory: defaultFlags } }, + }); + const { waitUntilConfigured } = render(store, ); - await waitUntilConfigured(); + await waitUntilConfigured(); - expect(screen.getByText(`${featureName} is enabled`)).toBeInTheDocument(); - }); + expect( + screen.getByText(`${featureName} is enabled`), + ).toBeInTheDocument(); + }, + ); }); }); @@ -106,44 +110,48 @@ describe('when combining adapters', () => { }); describe('without flag updating', () => { - it.each( - Object.keys(disabledDefaultFlags) - )('should list disabled feature "%s"', async (featureName) => { - const store = createStore({ - [STATE_SLICE]: { - flags: { memory: defaultFlags, localstorage: defaultFlags }, - }, - }); - const { waitUntilConfigured } = render(store, , { - adapter: combineAdapters, - adapterArgs, - }); + it.each(Object.keys(disabledDefaultFlags))( + 'should list disabled feature "%s"', + async (featureName) => { + const store = createStore({ + [STATE_SLICE]: { + flags: { memory: defaultFlags, localstorage: defaultFlags }, + }, + }); + const { waitUntilConfigured } = render(store, , { + adapter: combineAdapters, + adapterArgs, + }); - await waitUntilConfigured(); + await waitUntilConfigured(); - expect( - screen.getByText(`${featureName} is disabled`) - ).toBeInTheDocument(); - }); + expect( + screen.getByText(`${featureName} is disabled`), + ).toBeInTheDocument(); + }, + ); - it.each( - Object.keys(enabledDefaultFlags) - )('should list enabled feature "%s"', async (featureName) => { - const store = createStore({ - [STATE_SLICE]: { - flags: { memory: defaultFlags, localstorage: defaultFlags }, - }, - }); + it.each(Object.keys(enabledDefaultFlags))( + 'should list enabled feature "%s"', + async (featureName) => { + const store = createStore({ + [STATE_SLICE]: { + flags: { memory: defaultFlags, localstorage: defaultFlags }, + }, + }); - const { waitUntilConfigured } = render(store, , { - adapter: combineAdapters, - adapterArgs, - }); + const { waitUntilConfigured } = render(store, , { + adapter: combineAdapters, + adapterArgs, + }); - await waitUntilConfigured(); + await waitUntilConfigured(); - expect(screen.getByText(`${featureName} is enabled`)).toBeInTheDocument(); - }); + expect( + screen.getByText(`${featureName} is enabled`), + ).toBeInTheDocument(); + }, + ); }); describe('with flag updating', () => { @@ -160,13 +168,13 @@ describe('when combining adapters', () => { { adapter: combineAdapters, adapterArgs, - } + }, ); await waitUntilConfiguredFirstRender(); expect( - screen.queryByText('updatedFlag is enabled') + screen.queryByText('updatedFlag is enabled'), ).not.toBeInTheDocument(); act(() => { @@ -176,7 +184,7 @@ describe('when combining adapters', () => { }); expect( - screen.getByText('updatedMemoryAdapterFlag is enabled') + screen.getByText('updatedMemoryAdapterFlag is enabled'), ).toBeInTheDocument(); act(() => { @@ -186,7 +194,7 @@ describe('when combining adapters', () => { }); expect( - screen.getByText('updatedLocalstorageAdapterFlag is enabled') + screen.getByText('updatedLocalstorageAdapterFlag is enabled'), ).toBeInTheDocument(); }); }); @@ -203,13 +211,13 @@ describe('when combining adapters', () => { { adapter: combineAdapters, adapterArgs, - } + }, ); await waitUntilConfiguredFirstRender(); expect( - screen.queryByText('updatedFlag is enabled') + screen.queryByText('updatedFlag is enabled'), ).not.toBeInTheDocument(); act(() => { @@ -222,7 +230,7 @@ describe('when combining adapters', () => { }); expect( - screen.getByText('updatedShared is enabled') + screen.getByText('updatedShared is enabled'), ).toBeInTheDocument(); act(() => { @@ -235,7 +243,7 @@ describe('when combining adapters', () => { }); expect( - screen.getByText('updatedShared is disabled') + screen.getByText('updatedShared is disabled'), ).toBeInTheDocument(); }); }); diff --git a/packages/react-redux/vitest.config.ts b/packages/react-redux/vitest.config.ts index 516a3a1ec..fcd40d7ed 100644 --- a/packages/react-redux/vitest.config.ts +++ b/packages/react-redux/vitest.config.ts @@ -1,4 +1,5 @@ import { defineProject, mergeConfig } from 'vitest/config'; + import configShared from '../../vitest.shared.ts'; export default mergeConfig( @@ -7,5 +8,5 @@ export default mergeConfig( test: { environment: 'jsdom', }, - }) + }), ); diff --git a/packages/react/package.json b/packages/react/package.json index f6ba32295..8cc040851 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -2,14 +2,30 @@ "name": "@flopflip/react", "version": "15.1.7", "description": "A feature toggle wrapper to use LaunchDarkly with React", - "sideEffects": false, - "type": "module", - "exports": { - ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs" - } + "keywords": [ + "LaunchDarkly", + "client", + "feature-flags", + "feature-toggles", + "react" + ], + "homepage": "https://github.com/tdeekens/flopflip#readme", + "bugs": { + "url": "https://github.com/tdeekens/flopflip/issues" }, + "license": "MIT", + "author": "Tobias Deekens ", + "repository": { + "type": "git", + "url": "https://github.com/tdeekens/flopflip.git", + "directory": "packages/react" + }, + "files": [ + "readme.md", + "dist/**" + ], + "type": "module", + "sideEffects": false, "main": "./dist/index.js", "browser": "./dist/index.js", "typesVersions": { @@ -20,6 +36,15 @@ ] } }, + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, + "publishConfig": { + "access": "public" + }, "scripts": { "build": "rimraf dist && tsup", "check-types": "tsc --noEmit", @@ -27,24 +52,16 @@ "test:watch": "vitest", "dev": "tsup --watch --clean=false" }, - "files": [ - "readme.md", - "dist/**" - ], - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "https://github.com/tdeekens/flopflip.git", - "directory": "packages/react" - }, - "author": "Tobias Deekens ", - "license": "MIT", - "bugs": { - "url": "https://github.com/tdeekens/flopflip/issues" + "dependencies": { + "@babel/runtime": "7.28.6", + "@flopflip/cache": "workspace:*", + "@flopflip/types": "workspace:*", + "@types/react-is": "19.2.0", + "lodash": "4.17.23", + "react-is": "19.2.4", + "tiny-warning": "1.0.3", + "ts-deepmerge": "7.0.3" }, - "homepage": "https://github.com/tdeekens/flopflip#readme", "devDependencies": { "@flopflip/test-utils": "workspace:*", "@flopflip/tsconfig": "workspace:*", @@ -57,22 +74,5 @@ "peerDependencies": { "react": "18.x || 19.x", "react-dom": "18.x || 19.x" - }, - "dependencies": { - "@babel/runtime": "7.28.6", - "@flopflip/cache": "workspace:*", - "@flopflip/types": "workspace:*", - "@types/react-is": "19.2.0", - "lodash": "4.17.23", - "react-is": "19.2.4", - "tiny-warning": "1.0.3", - "ts-deepmerge": "7.0.3" - }, - "keywords": [ - "react", - "feature-flags", - "feature-toggles", - "LaunchDarkly", - "client" - ] + } } diff --git a/packages/react/src/adapter-context.ts b/packages/react/src/adapter-context.ts index 61ec86a3b..c53d84cdd 100644 --- a/packages/react/src/adapter-context.ts +++ b/packages/react/src/adapter-context.ts @@ -12,7 +12,7 @@ const initialReconfigureAdapter: TReconfigureAdapter = () => undefined; const createAdapterContext = ( adapterIdentifiers?: TAdapterIdentifiers[], reconfigure?: TReconfigureAdapter, - status?: TAdaptersStatus + status?: TAdaptersStatus, ): TAdapterContext => ({ adapterEffectIdentifiers: adapterIdentifiers ?? [], reconfigure: reconfigure ?? initialReconfigureAdapter, @@ -25,7 +25,7 @@ const AdapterContext = createContext(initialAdapterContext); function hasEveryAdapterStatus( adapterConfigurationStatus: AdapterConfigurationStatus, adaptersStatus?: TAdaptersStatus, - adapterIdentifiers?: TAdapterIdentifiers[] + adapterIdentifiers?: TAdapterIdentifiers[], ) { if (Object.keys(adaptersStatus ?? {}).length === 0) { return false; @@ -35,39 +35,39 @@ function hasEveryAdapterStatus( return adapterIdentifiers.every( (adapterIdentifier) => adaptersStatus?.[adapterIdentifier]?.configurationStatus === - adapterConfigurationStatus + adapterConfigurationStatus, ); } return Object.values(adaptersStatus ?? {}).every( (adapterStatus) => - adapterStatus.configurationStatus === adapterConfigurationStatus + adapterStatus.configurationStatus === adapterConfigurationStatus, ); } const selectAdapterConfigurationStatus = ( adaptersStatus?: TAdaptersStatus, - adapterIdentifiers?: TAdapterIdentifiers[] + adapterIdentifiers?: TAdapterIdentifiers[], ) => { const isReady = hasEveryAdapterStatus( AdapterConfigurationStatus.Configured, adaptersStatus, - adapterIdentifiers + adapterIdentifiers, ); const isUnconfigured = hasEveryAdapterStatus( AdapterConfigurationStatus.Unconfigured, adaptersStatus, - adapterIdentifiers + adapterIdentifiers, ); const isConfiguring = hasEveryAdapterStatus( AdapterConfigurationStatus.Configuring, adaptersStatus, - adapterIdentifiers + adapterIdentifiers, ); const isConfigured = hasEveryAdapterStatus( AdapterConfigurationStatus.Configured, adaptersStatus, - adapterIdentifiers + adapterIdentifiers, ); const status = { isReady, isUnconfigured, isConfiguring, isConfigured }; diff --git a/packages/react/src/configure-adapter/configure-adapter.tsx b/packages/react/src/configure-adapter/configure-adapter.tsx index 3d60d21e7..0baac00ac 100644 --- a/packages/react/src/configure-adapter/configure-adapter.tsx +++ b/packages/react/src/configure-adapter/configure-adapter.tsx @@ -57,7 +57,7 @@ const useAppliedAdapterArgsState = ({ const [appliedAdapterArgs, setAppliedAdapterArgs] = useState(initialAdapterArgs); - // biome-ignore lint/correctness/useExhaustiveDependencies: false positive + // oxlint-disable-next-line react-hooks/exhaustive-deps -- false positive const applyAdapterArgs = useCallback( (nextAdapterArgs: TAdapterArgs) => { /** @@ -68,7 +68,7 @@ const useAppliedAdapterArgsState = ({ */ setAppliedAdapterArgs(nextAdapterArgs); }, - [setAppliedAdapterArgs] + [setAppliedAdapterArgs], ); return [appliedAdapterArgs, applyAdapterArgs]; @@ -83,26 +83,26 @@ type TUseAdapterStateRefReturn = [ const useAdapterStateRef = (): TUseAdapterStateRefReturn => { const adapterStateRef = useRef(AdapterStates.UNCONFIGURED); - // biome-ignore lint/correctness/useExhaustiveDependencies: false positive + // oxlint-disable-next-line react-hooks/exhaustive-deps -- false positive const setAdapterState = useCallback( (nextAdapterState: TAdapterStates) => { adapterStateRef.current = nextAdapterState; }, - [adapterStateRef] + [adapterStateRef], ); - // biome-ignore lint/correctness/useExhaustiveDependencies: false positive + // oxlint-disable-next-line react-hooks/exhaustive-deps -- false positive const getIsAdapterConfigured = useCallback( () => adapterStateRef.current === AdapterStates.CONFIGURED, - [adapterStateRef] + [adapterStateRef], ); - // biome-ignore lint/correctness/useExhaustiveDependencies: false positive + // oxlint-disable-next-line react-hooks/exhaustive-deps -- false positive const getDoesAdapterNeedInitialConfiguration = useCallback( () => adapterStateRef.current !== AdapterStates.CONFIGURED && adapterStateRef.current !== AdapterStates.CONFIGURING, - [adapterStateRef] + [adapterStateRef], ); return [ @@ -119,11 +119,11 @@ type TUsePendingAdapterArgsRefReturn = [ () => TAdapterArgs, ]; const usePendingAdapterArgsRef = ( - appliedAdapterArgs: TAdapterArgs + appliedAdapterArgs: TAdapterArgs, ): TUsePendingAdapterArgsRefReturn => { const pendingAdapterArgsRef = useRef(undefined); - // biome-ignore lint/correctness/useExhaustiveDependencies: false positive + // oxlint-disable-next-line react-hooks/exhaustive-deps -- false positive const setPendingAdapterArgs = useCallback( (nextReconfiguration: TAdapterReconfiguration): void => { /** @@ -136,13 +136,13 @@ const usePendingAdapterArgsRef = ( */ pendingAdapterArgsRef.current = mergeAdapterArgs( pendingAdapterArgsRef.current ?? appliedAdapterArgs, - nextReconfiguration + nextReconfiguration, ); }, - [appliedAdapterArgs, pendingAdapterArgsRef] + [appliedAdapterArgs, pendingAdapterArgsRef], ); - // biome-ignore lint/correctness/useExhaustiveDependencies: false positive + // oxlint-disable-next-line react-hooks/exhaustive-deps -- false positive const unsetPendingAdapterArgs = useCallback(() => { pendingAdapterArgsRef.current = undefined; }, [pendingAdapterArgsRef]); @@ -159,17 +159,17 @@ const usePendingAdapterArgsRef = ( * */ - // biome-ignore lint/correctness/useExhaustiveDependencies: false positive + // oxlint-disable-next-line react-hooks/exhaustive-deps -- false positive const getAdapterArgsForConfiguration = useCallback( (): TAdapterArgs => pendingAdapterArgsRef.current ?? appliedAdapterArgs, - [appliedAdapterArgs, pendingAdapterArgsRef] + [appliedAdapterArgs, pendingAdapterArgsRef], ); /** * NOTE: Clears the pending adapter args when applied adapter args changed. */ - // biome-ignore lint/correctness/useExhaustiveDependencies: false positive + // oxlint-disable-next-line react-hooks/exhaustive-deps -- false positive useEffect(unsetPendingAdapterArgs, [ appliedAdapterArgs, unsetPendingAdapterArgs, @@ -194,7 +194,7 @@ const useHandleDefaultFlagsCallback = ({ onFlagsStateChange({ flags: defaultFlags }); } }, - [onFlagsStateChange] + [onFlagsStateChange], ); return handleDefaultFlags; @@ -226,7 +226,7 @@ const useConfigurationEffect = ({ pendingAdapterArgsRef, appliedAdapterArgs, }: TUseConfigurationEffectOptions) => { - // biome-ignore lint/correctness/useExhaustiveDependencies: false positive + // oxlint-disable-next-line react-hooks/exhaustive-deps -- false positive useEffect(() => { if ( !shouldDeferAdapterConfiguration && @@ -335,7 +335,7 @@ const useDefaultFlagsEffect = ({ onFlagsStateChange, }); - // biome-ignore lint/correctness/useExhaustiveDependencies: false positive + // oxlint-disable-next-line react-hooks/exhaustive-deps -- false positive useEffect(() => { if (defaultFlags) { handleDefaultFlags(defaultFlags); @@ -400,14 +400,14 @@ const usePendingAdapterArgsEffect = ({ const reconfigureOrQueue = useCallback( ( nextAdapterArgs: TAdapterArgs, - options: TAdapterReconfigurationOptions + options: TAdapterReconfigurationOptions, ): void => { if (getIsAdapterConfigured()) { applyAdapterArgs( mergeAdapterArgs(appliedAdapterArgs, { adapterArgs: nextAdapterArgs, options, - }) + }), ); return; } @@ -419,7 +419,7 @@ const usePendingAdapterArgsEffect = ({ applyAdapterArgs, getIsAdapterConfigured, setPendingAdapterArgs, - ] + ], ); useEffect(() => { @@ -499,12 +499,12 @@ function ConfigureAdapter({ value={createAdapterContext( adapterEffectIdentifiers, reconfigureOrQueue, - adapterStatus + adapterStatus, )} > {(() => { const isAdapterConfigured = adapter.getIsConfigurationStatus( - AdapterConfigurationStatus.Configured + AdapterConfigurationStatus.Configured, ); if (isAdapterConfigured) { diff --git a/packages/react/src/configure-adapter/helpers.ts b/packages/react/src/configure-adapter/helpers.ts index 6d5b364ce..397eaf42c 100644 --- a/packages/react/src/configure-adapter/helpers.ts +++ b/packages/react/src/configure-adapter/helpers.ts @@ -8,7 +8,7 @@ import { Children } from 'react'; import { merge } from 'ts-deepmerge'; const isFunctionChildren = ( - children: TConfigureAdapterChildren + children: TConfigureAdapterChildren, ): children is TConfigureAdapterChildrenAsFunction => typeof children === 'function'; @@ -17,7 +17,7 @@ const isEmptyChildren = (children: TConfigureAdapterChildren) => const mergeAdapterArgs = ( previousAdapterArgs: TAdapterArgs, - { adapterArgs: nextAdapterArgs, options = {} }: TAdapterReconfiguration + { adapterArgs: nextAdapterArgs, options = {} }: TAdapterReconfiguration, ): TAdapterArgs => options.shouldOverwrite ? nextAdapterArgs diff --git a/packages/react/src/get-flag-variation.ts b/packages/react/src/get-flag-variation.ts index 423b5851e..cdfd0d859 100644 --- a/packages/react/src/get-flag-variation.ts +++ b/packages/react/src/get-flag-variation.ts @@ -13,13 +13,13 @@ import { isNil } from './is-nil'; const getFlagVariation = ( allFlags: TFlagsContext, adapterIdentifiers: TAdapterIdentifiers[], - flagName: TFlagName = DEFAULT_FLAG_PROP_KEY + flagName: TFlagName = DEFAULT_FLAG_PROP_KEY, ): TFlagVariation => { const normalizedFlagName = getNormalizedFlagName(flagName); warning( normalizedFlagName === flagName, - '@flopflip/react: passed flag name does not seem to be normalized which may result in unexpected toggling. Please refer to our readme for more information: https://github.com/tdeekens/flopflip#flag-normalization' + '@flopflip/react: passed flag name does not seem to be normalized which may result in unexpected toggling. Please refer to our readme for more information: https://github.com/tdeekens/flopflip#flag-normalization', ); for (const adapterInterfaceIdentifier of adapterIdentifiers) { diff --git a/packages/react/src/get-is-feature-enabled.ts b/packages/react/src/get-is-feature-enabled.ts index 1ec716627..7519a751c 100644 --- a/packages/react/src/get-is-feature-enabled.ts +++ b/packages/react/src/get-is-feature-enabled.ts @@ -12,7 +12,7 @@ const getIsFeatureEnabled = ( allFlags: TFlagsContext, adapterIdentifiers: TAdapterIdentifiers[], flagName: TFlagName = DEFAULT_FLAG_PROP_KEY, - flagVariation: TFlagVariation = true + flagVariation: TFlagVariation = true, ) => getFlagVariation(allFlags, adapterIdentifiers, flagName) === flagVariation; export { getIsFeatureEnabled }; diff --git a/packages/react/src/reconfigure-adapter.ts b/packages/react/src/reconfigure-adapter.ts index 25eef28a5..2d3f10926 100644 --- a/packages/react/src/reconfigure-adapter.ts +++ b/packages/react/src/reconfigure-adapter.ts @@ -1,6 +1,6 @@ import type { TUser } from '@flopflip/types'; -// biome-ignore lint/style/useImportType: false positive -import React, { Children, useEffect } from 'react'; +import type React from 'react'; +import { Children, useEffect } from 'react'; import { useAdapterContext } from './use-adapter-context'; @@ -24,7 +24,7 @@ function ReconfigureAdapter({ }, { shouldOverwrite, - } + }, ); }, [user, shouldOverwrite, adapterContext]); diff --git a/packages/react/src/toggle-feature.ts b/packages/react/src/toggle-feature.ts index af952efbc..1442fa555 100644 --- a/packages/react/src/toggle-feature.ts +++ b/packages/react/src/toggle-feature.ts @@ -24,14 +24,14 @@ function ToggleFeature({ if (untoggledComponent) { warning( isValidElementType(untoggledComponent), - `Invalid prop 'untoggledComponent' supplied to 'ToggleFeature': the prop is not a valid React component` + `Invalid prop 'untoggledComponent' supplied to 'ToggleFeature': the prop is not a valid React component`, ); } if (toggledComponent) { warning( isValidElementType(toggledComponent), - `Invalid prop 'toggledComponent' supplied to 'ToggleFeature': the prop is not a valid React component` + `Invalid prop 'toggledComponent' supplied to 'ToggleFeature': the prop is not a valid React component`, ); } diff --git a/packages/react/src/use-adapter-subscription.ts b/packages/react/src/use-adapter-subscription.ts index d3d9d9d0f..6feb13667 100644 --- a/packages/react/src/use-adapter-subscription.ts +++ b/packages/react/src/use-adapter-subscription.ts @@ -12,7 +12,7 @@ function useAdapterSubscription(adapter: TAdapter) { * is a singleton). */ const useAdapterSubscriptionStatusRef = useRef( - AdapterSubscriptionStatus.Subscribed + AdapterSubscriptionStatus.Subscribed, ); const { subscribe, unsubscribe } = adapter; @@ -35,12 +35,12 @@ function useAdapterSubscription(adapter: TAdapter) { }; }, [subscribe, unsubscribe]); - // biome-ignore lint/correctness/useExhaustiveDependencies: false positive + // oxlint-disable-next-line react-hooks/exhaustive-deps -- false positive return useCallback( (demandedAdapterSubscriptionStatus: AdapterSubscriptionStatus) => useAdapterSubscriptionStatusRef.current === demandedAdapterSubscriptionStatus, - [useAdapterSubscriptionStatusRef] + [useAdapterSubscriptionStatusRef], ); } diff --git a/packages/react/src/wrap-display-name.ts b/packages/react/src/wrap-display-name.ts index ec5f0a87a..a57ff2a6f 100644 --- a/packages/react/src/wrap-display-name.ts +++ b/packages/react/src/wrap-display-name.ts @@ -2,7 +2,7 @@ import type React from 'react'; function wrapDisplayName( BaseComponent: React.ComponentType, - hocName: string + hocName: string, ) { const previousDisplayName = BaseComponent.displayName ?? BaseComponent.name; diff --git a/packages/react/test/adapter-context.spec.js b/packages/react/test/adapter-context.spec.js index 2ed8e7046..ce6a64d05 100644 --- a/packages/react/test/adapter-context.spec.js +++ b/packages/react/test/adapter-context.spec.js @@ -11,11 +11,11 @@ describe('selectAdapterConfigurationStatus', () => { memory: { configurationStatus: AdapterConfigurationStatus.Configured, }, - }) + }), ).toEqual( expect.objectContaining({ isReady: true, - }) + }), ); }); it('should indicate configured state', () => { @@ -24,11 +24,11 @@ describe('selectAdapterConfigurationStatus', () => { memory: { configurationStatus: AdapterConfigurationStatus.Configured, }, - }) + }), ).toEqual( expect.objectContaining({ isConfigured: true, - }) + }), ); }); }); @@ -39,11 +39,11 @@ describe('selectAdapterConfigurationStatus', () => { memory: { configurationStatus: AdapterConfigurationStatus.Configuring, }, - }) + }), ).toEqual( expect.objectContaining({ isConfiguring: true, - }) + }), ); }); it('should not indicate configured state', () => { @@ -52,11 +52,11 @@ describe('selectAdapterConfigurationStatus', () => { memory: { configurationStatus: AdapterConfigurationStatus.Configuring, }, - }) + }), ).toEqual( expect.objectContaining({ isConfigured: false, - }) + }), ); }); }); @@ -67,11 +67,11 @@ describe('selectAdapterConfigurationStatus', () => { memory: { configurationStatus: AdapterConfigurationStatus.Unconfigured, }, - }) + }), ).toEqual( expect.objectContaining({ isUnconfigured: true, - }) + }), ); }); }); @@ -87,12 +87,12 @@ describe('selectAdapterConfigurationStatus', () => { configurationStatus: AdapterConfigurationStatus.Unconfigured, }, }, - ['memory'] - ) + ['memory'], + ), ).toEqual( expect.objectContaining({ isReady: true, - }) + }), ); }); it('should indicate unconfigured state for another', () => { @@ -106,12 +106,12 @@ describe('selectAdapterConfigurationStatus', () => { configurationStatus: AdapterConfigurationStatus.Unconfigured, }, }, - ['http'] - ) + ['http'], + ), ).toEqual( expect.objectContaining({ isUnconfigured: true, - }) + }), ); }); }); diff --git a/packages/react/test/configure-adapter/configure-adapter.spec.jsx b/packages/react/test/configure-adapter/configure-adapter.spec.jsx index 60c6d2d39..9e0bb71ef 100644 --- a/packages/react/test/configure-adapter/configure-adapter.spec.jsx +++ b/packages/react/test/configure-adapter/configure-adapter.spec.jsx @@ -98,7 +98,7 @@ const renderWithReconfiguration = ({ props, adapter }) => { - + , ); const waitUntilStatus = (status = AdapterStates.CONFIGURED) => @@ -164,7 +164,7 @@ describe('rendering', () => { const { waitUntilStatus } = render({ props, adapter }); expect(props.children).toHaveBeenCalledWith( - expect.objectContaining({ isAdapterConfigured: true }) + expect.objectContaining({ isAdapterConfigured: true }), ); await waitUntilStatus(); @@ -237,7 +237,7 @@ describe('when adapter configuration should not be deferred', () => { { onFlagsStateChange: mergedRenderProps.onFlagsStateChange, onStatusStateChange: mergedRenderProps.onStatusStateChange, - } + }, ); await waitUntilStatus(); @@ -273,7 +273,7 @@ describe('when providing default flags', () => { cache = await getCache( cacheIdentifiers.session, adapterIdentifiers.memory, - cacheKey + cacheKey, ); cache.set(cachedFlags); @@ -334,12 +334,12 @@ describe('when adapter args change before adapter was configured', () => { adapterArgs={nextAdapterArgs} > - + , ); expect(adapter.configure).toHaveBeenCalledWith( { ...mergedRenderProps.adapterArgs, ...nextAdapterArgs }, - expect.anything() + expect.anything(), ); await waitUntilStatus(); @@ -364,7 +364,7 @@ describe('when adapter args change after adapter was configured', () => { rerender( - + , ); await waitUntilStatus(); @@ -372,7 +372,7 @@ describe('when adapter args change after adapter was configured', () => { await waitFor(() => { expect(adapter.reconfigure).toHaveBeenCalledWith( nextAdapterArgs, - expect.anything() + expect.anything(), ); }); }); @@ -397,7 +397,7 @@ describe('when adapter args change after adapter was configured', () => { group: 'reconfigured-user-group', }, }), - expect.anything() + expect.anything(), ); }); @@ -428,7 +428,7 @@ describe('when adapter was configured and component updates', () => { rerender( - + , ); expect(adapter.configure).toHaveBeenCalledTimes(1); diff --git a/packages/react/test/configure-adapter/helpers.spec.js b/packages/react/test/configure-adapter/helpers.spec.js index b93449f8f..be6f0ab51 100644 --- a/packages/react/test/configure-adapter/helpers.spec.js +++ b/packages/react/test/configure-adapter/helpers.spec.js @@ -1,4 +1,5 @@ import { describe, expect, it } from 'vitest'; + import { mergeAdapterArgs } from '../../src/configure-adapter/helpers'; describe('mergeAdapterArgs', () => { @@ -15,7 +16,7 @@ describe('mergeAdapterArgs', () => { mergeAdapterArgs(previousAdapterArgs, { adapterArgs: nextAdapterArgs, options: { shouldOverwrite: false }, - }) + }), ).toEqual(expect.objectContaining(nextAdapterArgs)); }); @@ -24,7 +25,7 @@ describe('mergeAdapterArgs', () => { mergeAdapterArgs(previousAdapterArgs, { adapterArgs: nextAdapterArgs, options: { shouldOverwrite: false }, - }) + }), ).toEqual(expect.objectContaining(previousAdapterArgs)); }); }); @@ -42,7 +43,7 @@ describe('mergeAdapterArgs', () => { mergeAdapterArgs(previousAdapterArgs, { adapterArgs: nextAdapterArgs, options: { shouldOverwrite: true }, - }) + }), ).toEqual(expect.objectContaining(nextAdapterArgs)); }); @@ -51,7 +52,7 @@ describe('mergeAdapterArgs', () => { mergeAdapterArgs(previousAdapterArgs, { adapterArgs: nextAdapterArgs, options: { shouldOverwrite: true }, - }) + }), ).not.toEqual(expect.objectContaining(previousAdapterArgs)); }); }); diff --git a/packages/react/test/get-flag-variation.spec.js b/packages/react/test/get-flag-variation.spec.js index 6ffdca8c3..126c31ec1 100644 --- a/packages/react/test/get-flag-variation.spec.js +++ b/packages/react/test/get-flag-variation.spec.js @@ -14,7 +14,7 @@ describe('with a single adapter interface identifier', () => { const adapterIdentifiers = ['memory']; expect( - getFlagVariation(allFlags, adapterIdentifiers, 'fooFlag') + getFlagVariation(allFlags, adapterIdentifiers, 'fooFlag'), ).toBe('foo-variation'); }); }); @@ -25,7 +25,7 @@ describe('with a single adapter interface identifier', () => { const adapterIdentifiers = ['memory']; expect( - getFlagVariation(allFlags, adapterIdentifiers, 'fooFlag') + getFlagVariation(allFlags, adapterIdentifiers, 'fooFlag'), ).toBe(123); }); }); @@ -36,7 +36,7 @@ describe('with a single adapter interface identifier', () => { const adapterIdentifiers = ['memory']; expect( - getFlagVariation(allFlags, adapterIdentifiers, 'fooFlag') + getFlagVariation(allFlags, adapterIdentifiers, 'fooFlag'), ).toStrictEqual({ a: 'b' }); }); }); @@ -49,7 +49,7 @@ describe('with a single adapter interface identifier', () => { const adapterIdentifiers = ['memory']; expect(getFlagVariation(allFlags, adapterIdentifiers, 'foo-flag')).toBe( - true + true, ); }); @@ -69,7 +69,7 @@ describe('with a single adapter interface identifier', () => { const adapterIdentifiers = ['memory']; expect(getFlagVariation(allFlags, adapterIdentifiers, 'fooFlag2')).toBe( - false + false, ); }); }); @@ -86,7 +86,7 @@ describe('with multiple adapter interface identifier', () => { const adapterIdentifiers = ['memory', 'graphql']; expect(getFlagVariation(allFlags, adapterIdentifiers, 'fooFlag')).toBe( - 'memory-foo-variation' + 'memory-foo-variation', ); }); }); @@ -100,7 +100,7 @@ describe('with multiple adapter interface identifier', () => { const adapterIdentifiers = ['memory', 'graphql']; expect(getFlagVariation(allFlags, adapterIdentifiers, 'fooFlag')).toBe( - false + false, ); }); }); @@ -114,7 +114,7 @@ describe('with multiple adapter interface identifier', () => { const adapterIdentifiers = ['memory', 'graphql']; expect(getFlagVariation(allFlags, adapterIdentifiers, 'fooFlag')).toBe( - 'graphql-foo-variation' + 'graphql-foo-variation', ); }); }); diff --git a/packages/react/test/get-is-feature-enabled.spec.js b/packages/react/test/get-is-feature-enabled.spec.js index 802edf5f7..47e6c0b01 100644 --- a/packages/react/test/get-is-feature-enabled.spec.js +++ b/packages/react/test/get-is-feature-enabled.spec.js @@ -1,4 +1,5 @@ import { describe, expect, it, vi } from 'vitest'; + import { getIsFeatureEnabled } from '../src/get-is-feature-enabled'; vi.mock('tiny-warning'); @@ -14,8 +15,8 @@ describe('with existing flag', () => { allFlags, adapterIdentifiers, 'fooFlag', - 'foo-variation' - ) + 'foo-variation', + ), ).toBe(true); }); @@ -25,8 +26,8 @@ describe('with existing flag', () => { allFlags, adapterIdentifiers, 'fooFlag', - 'foo-variation-1' - ) + 'foo-variation-1', + ), ).toBe(false); }); }); @@ -37,7 +38,7 @@ describe('with existing flag', () => { const adapterIdentifiers = ['memory']; expect(getIsFeatureEnabled(allFlags, adapterIdentifiers, 'fooFlag')).toBe( - true + true, ); }); @@ -46,7 +47,7 @@ describe('with existing flag', () => { const adapterIdentifiers = ['memory']; expect(getIsFeatureEnabled(allFlags, adapterIdentifiers, 'fooFlag')).toBe( - false + false, ); }); }); @@ -58,7 +59,7 @@ describe('with non existing flag', () => { const adapterIdentifiers = ['memory']; expect(getIsFeatureEnabled(allFlags, adapterIdentifiers, 'fooFlag2')).toBe( - false + false, ); }); }); diff --git a/packages/react/test/get-normalized-flag-name.spec.js b/packages/react/test/get-normalized-flag-name.spec.js index a4f8fce1b..ebaf1cbe4 100644 --- a/packages/react/test/get-normalized-flag-name.spec.js +++ b/packages/react/test/get-normalized-flag-name.spec.js @@ -1,4 +1,5 @@ import { describe, expect, it } from 'vitest'; + import { getNormalizedFlagName } from '../src/get-normalized-flag-name'; describe('when not camel caased', () => { diff --git a/packages/react/test/is-nil.spec.js b/packages/react/test/is-nil.spec.js index 6fb634001..b2672d85f 100644 --- a/packages/react/test/is-nil.spec.js +++ b/packages/react/test/is-nil.spec.js @@ -1,4 +1,5 @@ import { describe, expect, it } from 'vitest'; + import { isNil } from '../src/is-nil'; describe('when null', () => { diff --git a/packages/react/test/reconfigure-adapter.spec.jsx b/packages/react/test/reconfigure-adapter.spec.jsx index fd3b641e5..e6b72e138 100644 --- a/packages/react/test/reconfigure-adapter.spec.jsx +++ b/packages/react/test/reconfigure-adapter.spec.jsx @@ -18,7 +18,7 @@ function TestComponent({ reconfiguration, adapterContext }) { ...reconfiguration.user, count, }), - [count, reconfiguration.user] + [count, reconfiguration.user], ); return ( @@ -54,14 +54,14 @@ describe('with children', () => { const adapterContext = createAdapterContext( ['memory'], vi.fn(), - AdapterStates.UNCONFIGURED + AdapterStates.UNCONFIGURED, ); const reconfiguration = createReconfiguration(); render( + />, ); expect(screen.getByText('Children')).toBeInTheDocument(); @@ -73,7 +73,7 @@ describe('when mounted', () => { const adapterContext = createAdapterContext( ['memory'], vi.fn(), - AdapterStates.UNCONFIGURED + AdapterStates.UNCONFIGURED, ); const reconfiguration = createReconfiguration(); @@ -81,7 +81,7 @@ describe('when mounted', () => { + />, ); expect(adapterContext.reconfigure).toHaveBeenCalledWith( @@ -90,7 +90,7 @@ describe('when mounted', () => { }, { shouldOverwrite: reconfiguration.shouldOverwrite, - } + }, ); }); }); @@ -101,7 +101,7 @@ describe('when updated', () => { const adapterContext = createAdapterContext( ['memory'], vi.fn(), - AdapterStates.UNCONFIGURED + AdapterStates.UNCONFIGURED, ); const reconfiguration = createReconfiguration(); @@ -109,7 +109,7 @@ describe('when updated', () => { + />, ); fireEvent.click(screen.queryByText(/Reconfigure without changes/i)); @@ -123,7 +123,7 @@ describe('when updated', () => { const adapterContext = createAdapterContext( ['memory'], vi.fn(), - AdapterStates.UNCONFIGURED + AdapterStates.UNCONFIGURED, ); const reconfiguration = createReconfiguration(); @@ -131,7 +131,7 @@ describe('when updated', () => { + />, ); fireEvent.click(screen.queryByText(/Reconfigure with changes/i)); @@ -143,7 +143,7 @@ describe('when updated', () => { }, { shouldOverwrite: reconfiguration.shouldOverwrite, - } + }, ); }); }); diff --git a/packages/react/test/set-display-name.spec.js b/packages/react/test/set-display-name.spec.js index 7b2b36cc0..64ce6832c 100644 --- a/packages/react/test/set-display-name.spec.js +++ b/packages/react/test/set-display-name.spec.js @@ -1,4 +1,5 @@ import { describe, expect, it } from 'vitest'; + import { setDisplayName } from '../src/set-display-name'; function BaseComponent() { diff --git a/packages/react/test/toggle-feature.spec.jsx b/packages/react/test/toggle-feature.spec.jsx index 20464488b..cb3de9451 100644 --- a/packages/react/test/toggle-feature.spec.jsx +++ b/packages/react/test/toggle-feature.spec.jsx @@ -21,7 +21,7 @@ describe('when feature disabled', () => { expect(queryByFlagName('isFeatureEnabled')).toHaveAttribute( 'data-flag-status', - 'disabled' + 'disabled', ); }); @@ -41,7 +41,7 @@ describe('when feature disabled', () => { expect(queryByFlagName('isFeatureEnabled')).not.toHaveAttribute( 'data-flag-status', - 'enabled' + 'enabled', ); }); }); @@ -82,7 +82,7 @@ describe('when feature enabled', () => { expect(queryByFlagName('isFeatureEnabled')).not.toHaveAttribute( 'data-flag-status', - 'disabled' + 'disabled', ); }); @@ -102,7 +102,7 @@ describe('when feature enabled', () => { expect(queryByFlagName('isFeatureEnabled')).toHaveAttribute( 'data-flag-status', - 'enabled' + 'enabled', ); }); }); @@ -152,7 +152,7 @@ describe('when feature enabled', () => { expect(queryByFlagName('isFeatureEnabled')).not.toHaveAttribute( 'data-flag-status', - 'disabled' + 'disabled', ); }); @@ -171,7 +171,7 @@ describe('when feature enabled', () => { expect(queryByFlagName('isFeatureEnabled')).toHaveAttribute( 'data-flag-status', - 'enabled' + 'enabled', ); }); }); @@ -200,7 +200,7 @@ describe('when feature enabled', () => { expect(queryByFlagName('isFeatureEnabled')).toHaveAttribute( 'data-flag-status', - 'enabled' + 'enabled', ); }); }); diff --git a/packages/react/test/use-adapter-subscription.spec.jsx b/packages/react/test/use-adapter-subscription.spec.jsx index d1f550bee..5f9e36d16 100644 --- a/packages/react/test/use-adapter-subscription.spec.jsx +++ b/packages/react/test/use-adapter-subscription.spec.jsx @@ -9,7 +9,7 @@ import { useAdapterSubscription } from '../src/use-adapter-subscription'; const createAdapter = () => ({ getIsConfigurationStatus: vi.fn( - () => AdapterConfigurationStatus.Unconfigured + () => AdapterConfigurationStatus.Unconfigured, ), configure: vi.fn(() => Promise.resolve()), reconfigure: vi.fn(() => Promise.resolve()), @@ -21,7 +21,7 @@ function TestComponent({ adapter }) { const getHasAdapterSubscriptionStatus = useAdapterSubscription(adapter); const isConfigured = adapter.getIsConfigurationStatus( - AdapterConfigurationStatus.Configured + AdapterConfigurationStatus.Configured, ); return ( @@ -38,7 +38,7 @@ function TestComponent({ adapter }) {
  • Is unsubscribed:{' '} {getHasAdapterSubscriptionStatus( - AdapterSubscriptionStatus.Unsubscribed + AdapterSubscriptionStatus.Unsubscribed, ) ? 'Yes' : 'No'} diff --git a/packages/react/test/wrap-display-name.spec.js b/packages/react/test/wrap-display-name.spec.js index 8ec74073b..062c63374 100644 --- a/packages/react/test/wrap-display-name.spec.js +++ b/packages/react/test/wrap-display-name.spec.js @@ -1,4 +1,5 @@ import { describe, expect, it } from 'vitest'; + import { wrapDisplayName } from '../src/wrap-display-name'; function BaseComponent() { diff --git a/packages/react/vitest.config.ts b/packages/react/vitest.config.ts index 516a3a1ec..fcd40d7ed 100644 --- a/packages/react/vitest.config.ts +++ b/packages/react/vitest.config.ts @@ -1,4 +1,5 @@ import { defineProject, mergeConfig } from 'vitest/config'; + import configShared from '../../vitest.shared.ts'; export default mergeConfig( @@ -7,5 +8,5 @@ export default mergeConfig( test: { environment: 'jsdom', }, - }) + }), ); diff --git a/packages/sessionstorage-cache/package.json b/packages/sessionstorage-cache/package.json index f960d46d1..d4a9d2057 100644 --- a/packages/sessionstorage-cache/package.json +++ b/packages/sessionstorage-cache/package.json @@ -2,14 +2,30 @@ "name": "@flopflip/sessionstorage-cache", "version": "15.1.7", "description": "Sessionstorage cache for flipflop adapters", - "sideEffects": false, - "type": "module", - "exports": { - ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs" - } + "keywords": [ + "cache", + "client", + "feature-flags", + "feature-toggles", + "sessionstorage" + ], + "homepage": "https://github.com/tdeekens/flopflip#readme", + "bugs": { + "url": "https://github.com/tdeekens/flopflip/issues" }, + "license": "MIT", + "author": "Tobias Deekens ", + "repository": { + "type": "git", + "url": "https://github.com/tdeekens/flopflip.git", + "directory": "packages/sessionstorage-cache" + }, + "files": [ + "readme.md", + "dist/**" + ], + "type": "module", + "sideEffects": false, "main": "./dist/index.js", "typesVersions": { "*": { @@ -19,6 +35,15 @@ ] } }, + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, + "publishConfig": { + "access": "public" + }, "scripts": { "build": "rimraf dist && tsup", "check-types": "tsc --noEmit", @@ -26,31 +51,6 @@ "test:watch": "vitest", "dev": "tsup --watch --clean=false" }, - "files": [ - "readme.md", - "dist/**" - ], - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "https://github.com/tdeekens/flopflip.git", - "directory": "packages/sessionstorage-cache" - }, - "author": "Tobias Deekens ", - "license": "MIT", - "bugs": { - "url": "https://github.com/tdeekens/flopflip/issues" - }, - "homepage": "https://github.com/tdeekens/flopflip#readme", - "keywords": [ - "feature-flags", - "feature-toggles", - "sessionstorage", - "cache", - "client" - ], "dependencies": { "@flopflip/types": "workspace:*" }, diff --git a/packages/sessionstorage-cache/src/cache.ts b/packages/sessionstorage-cache/src/cache.ts index 8a5dd1bd1..1d60b7120 100644 --- a/packages/sessionstorage-cache/src/cache.ts +++ b/packages/sessionstorage-cache/src/cache.ts @@ -4,7 +4,7 @@ const createCache = (options: TCacheOptions) => { const cache: TCache = { get(key) { const sessionStorageValue = sessionStorage.getItem( - [options.prefix, key].join('/') + [options.prefix, key].join('/'), ); return sessionStorageValue ? JSON.parse(sessionStorageValue) : null; @@ -13,7 +13,7 @@ const createCache = (options: TCacheOptions) => { try { sessionStorage.setItem( [options.prefix, key].join('/'), - JSON.stringify(value) + JSON.stringify(value), ); return true; } catch (_error) { diff --git a/packages/sessionstorage-cache/test/cache.spec.js b/packages/sessionstorage-cache/test/cache.spec.js index b97691fc8..8aca6b6e9 100644 --- a/packages/sessionstorage-cache/test/cache.spec.js +++ b/packages/sessionstorage-cache/test/cache.spec.js @@ -1,4 +1,5 @@ import { describe, expect, it } from 'vitest'; + import { createCache } from '../src/cache'; const cachePrefix = 'test'; @@ -11,7 +12,7 @@ describe('setting a value', () => { cache.set('foo', 'bar'); expect( - JSON.parse(sessionStorage.getItem(`${cachePrefix}/foo`)) + JSON.parse(sessionStorage.getItem(`${cachePrefix}/foo`)), ).toStrictEqual('bar'); }); }); @@ -24,7 +25,7 @@ describe('setting a value', () => { cache.set('foo', 'baz'); expect( - JSON.parse(sessionStorage.getItem(`${cachePrefix}/foo`)) + JSON.parse(sessionStorage.getItem(`${cachePrefix}/foo`)), ).toStrictEqual('baz'); }); }); @@ -38,7 +39,7 @@ describe('getting a value', () => { cache.set('foo', 'bar'); expect( - JSON.parse(sessionStorage.getItem(`${cachePrefix}/foo`)) + JSON.parse(sessionStorage.getItem(`${cachePrefix}/foo`)), ).toStrictEqual('bar'); }); }); @@ -51,7 +52,7 @@ describe('getting a value', () => { cache.set('foo', json); expect( - JSON.parse(sessionStorage.getItem(`${cachePrefix}/foo`)) + JSON.parse(sessionStorage.getItem(`${cachePrefix}/foo`)), ).toStrictEqual(json); }); }); @@ -63,7 +64,7 @@ describe('unsetting a value', () => { cache.set('foo', 'bar'); expect( - JSON.parse(sessionStorage.getItem(`${cachePrefix}/foo`)) + JSON.parse(sessionStorage.getItem(`${cachePrefix}/foo`)), ).toStrictEqual('bar'); cache.unset('foo', 'bar'); diff --git a/packages/sessionstorage-cache/vitest.config.ts b/packages/sessionstorage-cache/vitest.config.ts index 516a3a1ec..fcd40d7ed 100644 --- a/packages/sessionstorage-cache/vitest.config.ts +++ b/packages/sessionstorage-cache/vitest.config.ts @@ -1,4 +1,5 @@ import { defineProject, mergeConfig } from 'vitest/config'; + import configShared from '../../vitest.shared.ts'; export default mergeConfig( @@ -7,5 +8,5 @@ export default mergeConfig( test: { environment: 'jsdom', }, - }) + }), ); diff --git a/packages/splitio-adapter/package.json b/packages/splitio-adapter/package.json index 1e9ae05a8..027f9a107 100644 --- a/packages/splitio-adapter/package.json +++ b/packages/splitio-adapter/package.json @@ -2,14 +2,29 @@ "name": "@flopflip/splitio-adapter", "version": "15.1.7", "description": "A adapter around the split.io client for flipflop", - "sideEffects": false, - "type": "module", - "exports": { - ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs" - } + "keywords": [ + "client", + "feature-flags", + "feature-toggles", + "split.io" + ], + "homepage": "https://github.com/tdeekens/flopflip#readme", + "bugs": { + "url": "https://github.com/tdeekens/flopflip/issues" }, + "license": "MIT", + "author": "Tobias Deekens ", + "repository": { + "type": "git", + "url": "https://github.com/tdeekens/flopflip.git", + "directory": "packages/splitio-adapter" + }, + "files": [ + "readme.md", + "dist/**" + ], + "type": "module", + "sideEffects": false, "main": "./dist/index.js", "typesVersions": { "*": { @@ -19,6 +34,15 @@ ] } }, + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, + "publishConfig": { + "access": "public" + }, "scripts": { "build": "rimraf dist && tsup", "check-types": "tsc --noEmit", @@ -26,29 +50,6 @@ "test:watch": "vitest", "dev": "tsup --watch --clean=false" }, - "files": [ - "readme.md", - "dist/**" - ], - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "https://github.com/tdeekens/flopflip.git", - "directory": "packages/splitio-adapter" - }, - "author": "Tobias Deekens ", - "license": "MIT", - "bugs": { - "url": "https://github.com/tdeekens/flopflip/issues" - }, - "homepage": "https://github.com/tdeekens/flopflip#readme", - "devDependencies": { - "@flopflip/tsconfig": "workspace:*", - "globalthis": "1.0.4", - "tsup": "8.5.1" - }, "dependencies": { "@babel/runtime": "7.28.6", "@flopflip/adapter-utilities": "workspace:*", @@ -57,10 +58,9 @@ "lodash": "4.17.23", "ts-deepmerge": "7.0.3" }, - "keywords": [ - "feature-flags", - "feature-toggles", - "split.io", - "client" - ] + "devDependencies": { + "@flopflip/tsconfig": "workspace:*", + "globalthis": "1.0.4", + "tsup": "8.5.1" + } } diff --git a/packages/splitio-adapter/src/adapter.ts b/packages/splitio-adapter/src/adapter.ts index 5998e9f15..20f7778eb 100644 --- a/packages/splitio-adapter/src/adapter.ts +++ b/packages/splitio-adapter/src/adapter.ts @@ -40,7 +40,7 @@ type TSplitIOClient = { const normalizeFlag = ( flagName: TFlagName, - flagValue?: TFlagVariation + flagValue?: TFlagVariation, ): TFlag => { let normalizeFlagValue: TFlagVariation; @@ -48,10 +48,10 @@ const normalizeFlag = ( normalizeFlagValue = true; } else if (flagValue === 'off') { normalizeFlagValue = false; - } else if (flagValue != null) { - normalizeFlagValue = flagValue; - } else { + } else if (flagValue == null) { normalizeFlagValue = false; + } else { + normalizeFlagValue = flagValue; } return [camelCase(flagName), normalizeFlagValue]; @@ -102,14 +102,14 @@ class SplitioAdapter implements TSplitioAdapterInterface { { ...this.#adapterState.user, ...this.#adapterState.treatmentAttributes, - } as SplitIO.Attributes + } as SplitIO.Attributes, ); if (!this.#getIsAdapterUnsubscribed()) { onFlagsStateChange({ id: this.id, flags: normalizeFlags(flags) }); } } - } + }, ); } }; @@ -120,7 +120,7 @@ class SplitioAdapter implements TSplitioAdapterInterface { readonly #initializeClient = (): TSplitIOClient => { if (!this.#adapterState.splitioSettings) { throw Error( - 'cannot initialize SplitIo without configured settings, call configure() first' + 'cannot initialize SplitIo without configured settings, call configure() first', ); } @@ -169,7 +169,7 @@ class SplitioAdapter implements TSplitioAdapterInterface { { ...this.#adapterState.user, ...this.#adapterState.treatmentAttributes, - } as SplitIO.Attributes + } as SplitIO.Attributes, ); if (!this.#getIsAdapterUnsubscribed()) { @@ -201,7 +201,7 @@ class SplitioAdapter implements TSplitioAdapterInterface { resolve(); } - } + }, ); } else { reject(new Error()); @@ -227,18 +227,18 @@ class SplitioAdapter implements TSplitioAdapterInterface { readonly #cloneTreatmentAttributes = < T = TSplitioAdapterArgs['sdk']['treatmentAttributes'], >( - treatmentAttributes: T + treatmentAttributes: T, ): T => cloneDeep(treatmentAttributes); updateFlags() { console.log( - '@flopflip/splitio-adapter: update flags it not yet implemented.' + '@flopflip/splitio-adapter: update flags it not yet implemented.', ); } async configure( adapterArgs: TSplitioAdapterArgs, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) { const { sdk, user } = adapterArgs; @@ -247,7 +247,7 @@ class SplitioAdapter implements TSplitioAdapterInterface { this.#adapterState.user = this.#ensureUser(user); this.#adapterState.treatmentAttributes = this.#cloneTreatmentAttributes( - sdk.treatmentAttributes + sdk.treatmentAttributes, ); this.#adapterState.configuredCallbacks.onFlagsStateChange = adapterEventHandlers.onFlagsStateChange; @@ -268,7 +268,7 @@ class SplitioAdapter implements TSplitioAdapterInterface { async reconfigure( adapterArgs: TSplitioAdapterArgs, - _adapterEventHandlers: TAdapterEventHandlers + _adapterEventHandlers: TAdapterEventHandlers, ) { if ( !this.getIsConfigurationStatus(AdapterConfigurationStatus.Configured) || @@ -276,15 +276,15 @@ class SplitioAdapter implements TSplitioAdapterInterface { ) { return Promise.reject( new Error( - '@flopflip/splitio-adapter: please configure adapter before reconfiguring.' - ) + '@flopflip/splitio-adapter: please configure adapter before reconfiguring.', + ), ); } const hasUserChanged = !isEqual(this.#adapterState.user, adapterArgs.user); const hasTreatmentChanged = !isEqual( this.#adapterState.treatmentAttributes, - adapterArgs.sdk?.treatmentAttributes + adapterArgs.sdk?.treatmentAttributes, ); if (hasUserChanged) { @@ -293,7 +293,7 @@ class SplitioAdapter implements TSplitioAdapterInterface { if (hasTreatmentChanged) { this.#adapterState.treatmentAttributes = this.#cloneTreatmentAttributes( - adapterArgs.sdk.treatmentAttributes + adapterArgs.sdk.treatmentAttributes, ); } diff --git a/packages/splitio-adapter/test/adapter.spec.js b/packages/splitio-adapter/test/adapter.spec.js index 8aec8bfb1..b05f11f02 100644 --- a/packages/splitio-adapter/test/adapter.spec.js +++ b/packages/splitio-adapter/test/adapter.spec.js @@ -2,6 +2,7 @@ import { AdapterConfigurationStatus } from '@flopflip/types'; import { SplitFactory } from '@splitsoftware/splitio'; import getGlobalThis from 'globalthis'; import { beforeEach, describe, expect, it, vi } from 'vitest'; + import { adapter, createAnonymousUserKey, normalizeFlag } from '../src/adapter'; vi.mock('@splitsoftware/splitio', () => ({ @@ -39,14 +40,14 @@ describe('when configuring', () => { it('should indicate that the adapter is not configured', () => { expect( - adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured) + adapter.getIsConfigurationStatus(AdapterConfigurationStatus.Configured), ).toBe(false); }); describe('when reconfiguring before configured', () => { it('should reject reconfiguration', () => expect(adapter.reconfigure({ user: userWithKey })).rejects.toEqual( - expect.any(Error) + expect.any(Error), )); }); @@ -60,7 +61,7 @@ describe('when configuring', () => { { onStatusStateChange, onFlagsStateChange, - } + }, ); }); @@ -84,7 +85,7 @@ describe('when configuring', () => { { onStatusStateChange, onFlagsStateChange, - } + }, ); }); @@ -112,7 +113,7 @@ describe('when configuring', () => { { onStatusStateChange, onFlagsStateChange, - } + }, ); }); @@ -141,7 +142,7 @@ describe('when configuring', () => { { onStatusStateChange, onFlagsStateChange, - } + }, ); }); @@ -198,7 +199,7 @@ describe('when configuring', () => { { onStatusStateChange, onFlagsStateChange, - } + }, ); }); @@ -206,7 +207,7 @@ describe('when configuring', () => { expect(configurationResult).toEqual( expect.objectContaining({ initializationStatus: 0, - }) + }), ); }); @@ -228,8 +229,8 @@ describe('when configuring', () => { it('should indicate that the adapter is not configured', () => { expect( adapter.getIsConfigurationStatus( - AdapterConfigurationStatus.Configured - ) + AdapterConfigurationStatus.Configured, + ), ).toBe(true); }); @@ -255,7 +256,7 @@ describe('when configuring', () => { it('should register callbacks to receive flag updates', () => { expect(onStub).toHaveBeenCalledWith( factory.client().Event.SDK_UPDATE, - expect.any(Function) + expect.any(Function), ); }); }); @@ -303,7 +304,7 @@ describe('when configuring', () => { { onStatusStateChange, onFlagsStateChange, - } + }, ) .then(() => { // NOTE: Clearing stubs as they are invoked @@ -320,7 +321,7 @@ describe('when configuring', () => { }, }, }, - { onStatusStateChange, onFlagsStateChange } + { onStatusStateChange, onFlagsStateChange }, ); }); }); @@ -329,7 +330,7 @@ describe('when configuring', () => { expect(configurationResult).toEqual( expect.objectContaining({ initializationStatus: 0, - }) + }), ); }); diff --git a/packages/splitio-adapter/vitest.config.ts b/packages/splitio-adapter/vitest.config.ts index 516a3a1ec..fcd40d7ed 100644 --- a/packages/splitio-adapter/vitest.config.ts +++ b/packages/splitio-adapter/vitest.config.ts @@ -1,4 +1,5 @@ import { defineProject, mergeConfig } from 'vitest/config'; + import configShared from '../../vitest.shared.ts'; export default mergeConfig( @@ -7,5 +8,5 @@ export default mergeConfig( test: { environment: 'jsdom', }, - }) + }), ); diff --git a/packages/types/package.json b/packages/types/package.json index 9553ca38e..bf27a2e2c 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -2,14 +2,28 @@ "name": "@flopflip/types", "version": "15.1.7", "description": "Type definitions for flipflop", - "sideEffects": false, - "type": "module", - "exports": { - ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs" - } + "keywords": [ + "feature-flags", + "feature-toggles", + "types" + ], + "homepage": "https://github.com/tdeekens/flopflip#readme", + "bugs": { + "url": "https://github.com/tdeekens/flopflip/issues" }, + "license": "MIT", + "author": "Tobias Deekens ", + "repository": { + "type": "git", + "url": "https://github.com/tdeekens/flopflip.git", + "directory": "packages/types" + }, + "files": [ + "readme.md", + "dist/**" + ], + "type": "module", + "sideEffects": false, "main": "./dist/index.js", "typesVersions": { "*": { @@ -19,6 +33,15 @@ ] } }, + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, + "publishConfig": { + "access": "public" + }, "scripts": { "build": "rimraf dist && tsup", "check-types": "tsc --noEmit", @@ -26,29 +49,6 @@ "test:watch": "exit 0", "dev": "tsup --watch --clean=false" }, - "files": [ - "readme.md", - "dist/**" - ], - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "https://github.com/tdeekens/flopflip.git", - "directory": "packages/types" - }, - "author": "Tobias Deekens ", - "license": "MIT", - "bugs": { - "url": "https://github.com/tdeekens/flopflip/issues" - }, - "homepage": "https://github.com/tdeekens/flopflip#readme", - "keywords": [ - "feature-flags", - "feature-toggles", - "types" - ], "dependencies": { "launchdarkly-js-client-sdk": "3.9.0" }, diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 49f98017e..407c8089a 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -82,10 +82,10 @@ export type TGraphQlAdapterArgs< pollingIntervalMs?: number; getQueryVariables?: (adapterArgs: TGraphQlAdapterArgs) => unknown; getRequestHeaders?: ( - adapterArgs: TGraphQlAdapterArgs + adapterArgs: TGraphQlAdapterArgs, ) => Record; parseFlags?: ( - fetchedFlags: TFetchedFlags + fetchedFlags: TFetchedFlags, ) => TParsedFlags; }; export type THttpAdapterArgs< @@ -94,7 +94,7 @@ export type THttpAdapterArgs< execute: < TPassedAdapterArgs extends TBaseAdapterArgs, >( - adapterArgs: TPassedAdapterArgs + adapterArgs: TPassedAdapterArgs, ) => Promise; pollingIntervalMs?: number; }; @@ -165,7 +165,7 @@ export type TUpdateFlagsOptions = { }; export type TFlagsUpdateFunction = ( flags: TFlags, - options?: TUpdateFlagsOptions + options?: TUpdateFlagsOptions, ) => void; export interface TAdapterInterface { @@ -175,17 +175,17 @@ export interface TAdapterInterface { effectIds?: TAdapterIdentifiers[]; configure: ( adapterArgs: Args, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) => Promise; reconfigure: ( adapterArgs: Args, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) => Promise; getIsConfigurationStatus: ( - configurationStatus: AdapterConfigurationStatus + configurationStatus: AdapterConfigurationStatus, ) => boolean; setConfigurationStatus?: ( - nextConfigurationStatus: AdapterConfigurationStatus + nextConfigurationStatus: AdapterConfigurationStatus, ) => void; waitUntilConfigured?: () => Promise; reset?: () => void; @@ -196,99 +196,94 @@ export interface TAdapterInterface { getUser?: () => TUser | undefined; } -export interface TLaunchDarklyAdapterInterface - extends TAdapterInterface { +export interface TLaunchDarklyAdapterInterface extends TAdapterInterface { id: typeof adapterIdentifiers.launchdarkly; configure: ( adapterArgs: TLaunchDarklyAdapterArgs, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) => Promise; reconfigure: ( adapterArgs: TLaunchDarklyAdapterArgs, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) => Promise; getIsConfigurationStatus: ( - adapterConfigurationStatus: AdapterConfigurationStatus + adapterConfigurationStatus: AdapterConfigurationStatus, ) => boolean; getClient: () => TLDClient | undefined; getFlag: (flagName: TFlagName) => TFlagVariation | undefined; updateClientContext: ( - updatedContextProps: TLaunchDarklyAdapterArgs['context'] + updatedContextProps: TLaunchDarklyAdapterArgs['context'], ) => Promise; unsubscribe: () => void; subscribe: () => void; } -export interface TLocalStorageAdapterInterface - extends TAdapterInterface { +export interface TLocalStorageAdapterInterface extends TAdapterInterface { id: typeof adapterIdentifiers.localstorage; configure: ( adapterArgs: TLocalStorageAdapterArgs, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) => Promise; reconfigure: ( adapterArgs: TLocalStorageAdapterArgs, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) => Promise; getIsConfigurationStatus: ( - adapterConfigurationStatus: AdapterConfigurationStatus + adapterConfigurationStatus: AdapterConfigurationStatus, ) => boolean; waitUntilConfigured: () => Promise; unsubscribe: () => void; subscribe: () => void; } -export interface TGraphQlAdapterInterface - extends TAdapterInterface { +export interface TGraphQlAdapterInterface extends TAdapterInterface { id: typeof adapterIdentifiers.graphql; configure: ( adapterArgs: TGraphQlAdapterArgs, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) => Promise; reconfigure: ( adapterArgs: TGraphQlAdapterArgs, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) => Promise; getIsConfigurationStatus: ( - adapterConfigurationStatus: AdapterConfigurationStatus + adapterConfigurationStatus: AdapterConfigurationStatus, ) => boolean; waitUntilConfigured: () => Promise; unsubscribe: () => void; subscribe: () => void; } -export interface THttpAdapterInterface - extends TAdapterInterface { +export interface THttpAdapterInterface extends TAdapterInterface { id: typeof adapterIdentifiers.http; configure: ( adapterArgs: THttpAdapterArgs, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) => Promise; reconfigure: ( adapterArgs: THttpAdapterArgs, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) => Promise; getIsConfigurationStatus: ( - adapterConfigurationStatus: AdapterConfigurationStatus + adapterConfigurationStatus: AdapterConfigurationStatus, ) => boolean; waitUntilConfigured: () => Promise; unsubscribe: () => void; subscribe: () => void; } -export interface TMemoryAdapterInterface - extends TAdapterInterface { +export interface TMemoryAdapterInterface extends TAdapterInterface { id: typeof adapterIdentifiers.memory; configure: ( adapterArgs: TMemoryAdapterArgs, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) => Promise; reconfigure: ( adapterArgs: TMemoryAdapterArgs, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) => Promise; getIsConfigurationStatus: ( - adapterConfigurationStatus: AdapterConfigurationStatus + adapterConfigurationStatus: AdapterConfigurationStatus, ) => boolean; waitUntilConfigured: () => Promise; reset: () => void; @@ -297,23 +292,22 @@ export interface TMemoryAdapterInterface subscribe: () => void; } -export interface TCombinedAdapterInterface - extends TAdapterInterface { +export interface TCombinedAdapterInterface extends TAdapterInterface { id: typeof adapterIdentifiers.combined; effectIds?: TAdapterIdentifiers[]; combine: ( - adapters: TAdapterInstance[] + adapters: TAdapterInstance[], ) => void; configure: ( adapterArgs: TCombinedAdapterArgs, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) => Promise; reconfigure: ( adapterArgs: TCombinedAdapterArgs, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) => Promise; getIsConfigurationStatus: ( - adapterConfigurationStatus: AdapterConfigurationStatus + adapterConfigurationStatus: AdapterConfigurationStatus, ) => boolean; waitUntilConfigured: () => Promise; reset: () => void; @@ -322,19 +316,18 @@ export interface TCombinedAdapterInterface subscribe: () => void; } -export interface TSplitioAdapterInterface - extends TAdapterInterface { +export interface TSplitioAdapterInterface extends TAdapterInterface { id: typeof adapterIdentifiers.splitio; configure: ( adapterArgs: TSplitioAdapterArgs, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) => Promise; reconfigure: ( adapterArgs: TSplitioAdapterArgs, - adapterEventHandlers: TAdapterEventHandlers + adapterEventHandlers: TAdapterEventHandlers, ) => Promise; getIsConfigurationStatus: ( - adapterConfigurationStatus: AdapterConfigurationStatus + adapterConfigurationStatus: AdapterConfigurationStatus, ) => boolean; unsubscribe: () => void; subscribe: () => void; @@ -392,14 +385,14 @@ export type TConfigureAdapterChildrenAsFunctionArgs = { isAdapterConfigured: boolean; }; export type TConfigureAdapterChildrenAsFunction = ( - args: TConfigureAdapterChildrenAsFunctionArgs + args: TConfigureAdapterChildrenAsFunctionArgs, ) => React.ReactNode; export type TConfigureAdapterChildren = | TConfigureAdapterChildrenAsFunction | React.ReactNode; export type TReconfigureAdapter = ( adapterArgs: TAdapterArgs, - options: TAdapterReconfigurationOptions + options: TAdapterReconfigurationOptions, ) => void; export type TAdapterContext = { adapterEffectIdentifiers: TAdapterIdentifiers[]; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2f54fe8f6..d8d15eaeb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -80,9 +80,6 @@ importers: '@babel/preset-typescript': specifier: 7.28.5 version: 7.28.5(@babel/core@7.29.0) - '@biomejs/biome': - specifier: 2.4.0 - version: 2.4.0 '@changesets/changelog-github': specifier: 0.5.2 version: 0.5.2 @@ -143,6 +140,12 @@ importers: lint-staged: specifier: 16.2.7 version: 16.2.7 + oxfmt: + specifier: 0.34.0 + version: 0.34.0 + oxlint: + specifier: 1.49.0 + version: 1.49.0 pinst: specifier: 3.0.0 version: 3.0.0 @@ -1343,63 +1346,6 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} - '@biomejs/biome@2.4.0': - resolution: {integrity: sha512-iluT61cORUDIC5i/y42ljyQraCemmmcgbMLLCnYO+yh+2hjTmcMFcwY8G0zTzWCsPb3t3AyKc+0t/VuhPZULUg==} - engines: {node: '>=14.21.3'} - hasBin: true - - '@biomejs/cli-darwin-arm64@2.4.0': - resolution: {integrity: sha512-L+YpOtPSuU0etomfvFTPWRsa7+8ejaJL3yaROEoT/96HDJbR6OsvZQk0C8JUYou+XFdP+JcGxqZknkp4n934RA==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [darwin] - - '@biomejs/cli-darwin-x64@2.4.0': - resolution: {integrity: sha512-Aq+S7ffpb5ynTyLgtnEjG+W6xuTd2F7FdC7J6ShpvRhZwJhjzwITGF9vrqoOnw0sv1XWkt2Q1Rpg+hleg/Xg7Q==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [darwin] - - '@biomejs/cli-linux-arm64-musl@2.4.0': - resolution: {integrity: sha512-1rhDUq8sf7xX3tg7vbnU3WVfanKCKi40OXc4VleBMzRStmQHdeBY46aFP6VdwEomcVjyNiu+Zcr3LZtAdrZrjQ==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@biomejs/cli-linux-arm64@2.4.0': - resolution: {integrity: sha512-u2p54IhvNAWB+h7+rxCZe3reNfQYFK+ppDw+q0yegrGclFYnDPZAntv/PqgUacpC3uxTeuWFgWW7RFe3lHuxOA==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@biomejs/cli-linux-x64-musl@2.4.0': - resolution: {integrity: sha512-Omo0xhl63z47X+CrE5viEWKJhejJyndl577VoXg763U/aoATrK3r5+8DPh02GokWPeODX1Hek00OtjjooGan9w==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [linux] - libc: [musl] - - '@biomejs/cli-linux-x64@2.4.0': - resolution: {integrity: sha512-WVFOhsnzhrbMGOSIcB9yFdRV2oG2KkRRhIZiunI9gJqSU3ax9ErdnTxRfJUxZUI9NbzVxC60OCXNcu+mXfF/Tw==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@biomejs/cli-win32-arm64@2.4.0': - resolution: {integrity: sha512-aqRwW0LJLV1v1NzyLvLWQhdLmDSAV1vUh+OBdYJaa8f28XBn5BZavo+WTfqgEzALZxlNfBmu6NGO6Al3MbCULw==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [win32] - - '@biomejs/cli-win32-x64@2.4.0': - resolution: {integrity: sha512-g47s+V+OqsGxbSZN3lpav6WYOk0PIc3aCBAq+p6dwSynL3K5MA6Cg6nkzDOlu28GEHwbakW+BllzHCJCxnfK5Q==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [win32] - '@bramus/specificity@2.4.2': resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} hasBin: true @@ -1842,6 +1788,250 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@oxfmt/binding-android-arm-eabi@0.34.0': + resolution: {integrity: sha512-sqkqjh/Z38l+duOb1HtVqJTAj1grt2ttkobCopC/72+a4Xxz4xUgZPFyQ4HxrYMvyqO/YA0tvM1QbfOu70Gk1Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxfmt/binding-android-arm64@0.34.0': + resolution: {integrity: sha512-1KRCtasHcVcGOMwfOP9d5Bus2NFsN8yAYM5cBwi8LBg5UtXC3C49WHKrlEa8iF1BjOS6CR2qIqiFbGoA0DJQNQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxfmt/binding-darwin-arm64@0.34.0': + resolution: {integrity: sha512-b+Rmw9Bva6e/7PBES2wLO8sEU7Mi0+/Kv+pXSe/Y8i4fWNftZZlGwp8P01eECaUqpXATfSgNxdEKy7+ssVNz7g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxfmt/binding-darwin-x64@0.34.0': + resolution: {integrity: sha512-QGjpevWzf1T9COEokZEWt80kPOtthW1zhRbo7x4Qoz646eTTfi6XsHG2uHeDWJmTbgBoJZPMgj2TAEV/ppEZaA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxfmt/binding-freebsd-x64@0.34.0': + resolution: {integrity: sha512-VMSaC02cG75qL59M9M/szEaqq/RsLfgpzQ4nqUu8BUnX1zkiZIW2gTpUv3ZJ6qpWnHxIlAXiRZjQwmcwpvtbcg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxfmt/binding-linux-arm-gnueabihf@0.34.0': + resolution: {integrity: sha512-Klm367PFJhH6vYK3vdIOxFepSJZHPaBfIuqwxdkOcfSQ4qqc/M8sgK0UTFnJWWTA/IkhMIh1kW6uEqiZ/xtQqg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm-musleabihf@0.34.0': + resolution: {integrity: sha512-nqn0QueVXRfbN9m58/E9Zij0Ap8lzayx591eWBYn0sZrGzY1IRv9RYS7J/1YUXbb0Ugedo0a8qIWzUHU9bWQuA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm64-gnu@0.34.0': + resolution: {integrity: sha512-DDn+dcqW+sMTCEjvLoQvC/VWJjG7h8wcdN/J+g7ZTdf/3/Dx730pQElxPPGsCXPhprb11OsPyMp5FwXjMY3qvA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-arm64-musl@0.34.0': + resolution: {integrity: sha512-H+F8+71gHQoGTFPPJ6z4dD0Fzfzi0UP8Zx94h5kUmIFThLvMq5K1Y/bUUubiXwwHfwb5C3MPjUpYijiy0rj51Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-ppc64-gnu@0.34.0': + resolution: {integrity: sha512-dIGnzTNhCXqQD5pzBwduLg8pClm+t8R53qaE9i5h8iua1iaFAJyLffh4847CNZSlASb7gn1Ofuv7KoG/EpoGZg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-riscv64-gnu@0.34.0': + resolution: {integrity: sha512-FGQ2GTTooilDte/ogwWwkHuuL3lGtcE3uKM2EcC7kOXNWdUfMY6Jx3JCodNVVbFoybv4A+HuCj8WJji2uu1Ceg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-riscv64-musl@0.34.0': + resolution: {integrity: sha512-2dGbGneJ7ptOIVKMwEIHdCkdZEomh74X3ggo4hCzEXL/rl9HwfsZDR15MkqfQqAs6nVXMvtGIOMxjDYa5lwKaA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-s390x-gnu@0.34.0': + resolution: {integrity: sha512-cCtGgmrTrxq3OeSG0UAO+w6yLZTMeOF4XM9SAkNrRUxYhRQELSDQ/iNPCLyHhYNi38uHJQbS5RQweLUDpI4ajA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-x64-gnu@0.34.0': + resolution: {integrity: sha512-7AvMzmeX+k7GdgitXp99GQoIV/QZIpAS7rwxQvC/T541yWC45nwvk4mpnU8N+V6dE5SPEObnqfhCjO80s7qIsg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-x64-musl@0.34.0': + resolution: {integrity: sha512-uNiglhcmivJo1oDMh3hoN/Z0WsbEXOpRXZdQ3W/IkOpyV8WF308jFjSC1ZxajdcNRXWej0zgge9QXba58Owt+g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-openharmony-arm64@0.34.0': + resolution: {integrity: sha512-5eFsTjCyji25j6zznzlMc+wQAZJoL9oWy576xhqd2efv+N4g1swIzuSDcb1dz4gpcVC6veWe9pAwD7HnrGjLwg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxfmt/binding-win32-arm64-msvc@0.34.0': + resolution: {integrity: sha512-6id8kK0t5hKfbV6LHDzRO21wRTA6ctTlKGTZIsG/mcoir0rssvaYsedUymF4HDj7tbCUlnxCX/qOajKlEuqbIw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxfmt/binding-win32-ia32-msvc@0.34.0': + resolution: {integrity: sha512-QHaz+w673mlYqn9v/+fuiKZpjkmagleXQ+NygShDv8tdHpRYX2oYhTJwwt9j1ZfVhRgza1EIUW3JmzCXmtPdhQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxfmt/binding-win32-x64-msvc@0.34.0': + resolution: {integrity: sha512-CXKQM/VaF+yuvGru8ktleHLJoBdjBtTFmAsLGePiESiTN0NjCI/PiaiOCfHMJ1HdP1LykvARUwMvgaN3tDhcrg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxlint/binding-android-arm-eabi@1.49.0': + resolution: {integrity: sha512-2WPoh/2oK9r/i2R4o4J18AOrm3HVlWiHZ8TnuCaS4dX8m5ZzRmHW0I3eLxEurQLHWVruhQN7fHgZnah+ag5iQg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxlint/binding-android-arm64@1.49.0': + resolution: {integrity: sha512-YqJAGvNB11EzoKm1euVhZntb79alhMvWW/j12bYqdvVxn6xzEQWrEDCJg9BPo3A3tBCSUBKH7bVkAiCBqK/L1w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxlint/binding-darwin-arm64@1.49.0': + resolution: {integrity: sha512-WFocCRlvVkMhChCJ2qpJfp1Gj/IjvyjuifH9Pex8m8yHonxxQa3d8DZYreuDQU3T4jvSY8rqhoRqnpc61Nlbxw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxlint/binding-darwin-x64@1.49.0': + resolution: {integrity: sha512-BN0KniwvehbUfYztOMwEDkYoojGm/narf5oJf+/ap+6PnzMeWLezMaVARNIS0j3OdMkjHTEP8s3+GdPJ7WDywQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxlint/binding-freebsd-x64@1.49.0': + resolution: {integrity: sha512-SnkAc/DPIY6joMCiP/+53Q+N2UOGMU6ULvbztpmvPJNF/jYPGhNbKtN982uj2Gs6fpbxYkmyj08QnpkD4fbHJA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxlint/binding-linux-arm-gnueabihf@1.49.0': + resolution: {integrity: sha512-6Z3EzRvpQVIpO7uFhdiGhdE8Mh3S2VWKLL9xuxVqD6fzPhyI3ugthpYXlCChXzO8FzcYIZ3t1+Kau+h2NY1hqA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm-musleabihf@1.49.0': + resolution: {integrity: sha512-wdjXaQYAL/L25732mLlngfst4Jdmi/HLPVHb3yfCoP5mE3lO/pFFrmOJpqWodgv29suWY74Ij+RmJ/YIG5VuzQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm64-gnu@1.49.0': + resolution: {integrity: sha512-oSHpm8zmSvAG1BWUumbDRSg7moJbnwoEXKAkwDf/xTQJOzvbUknq95NVQdw/AduZr5dePftalB8rzJNGBogUMg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-arm64-musl@1.49.0': + resolution: {integrity: sha512-xeqkMOARgGBlEg9BQuPDf6ZW711X6BT5qjDyeM5XNowCJeTSdmMhpePJjTEiVbbr3t21sIlK8RE6X5bc04nWyQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxlint/binding-linux-ppc64-gnu@1.49.0': + resolution: {integrity: sha512-uvcqRO6PnlJGbL7TeePhTK5+7/JXbxGbN+C6FVmfICDeeRomgQqrfVjf0lUrVpUU8ii8TSkIbNdft3M+oNlOsQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-riscv64-gnu@1.49.0': + resolution: {integrity: sha512-Dw1HkdXAwHNH+ZDserHP2RzXQmhHtpsYYI0hf8fuGAVCIVwvS6w1+InLxpPMY25P8ASRNiFN3hADtoh6lI+4lg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-riscv64-musl@1.49.0': + resolution: {integrity: sha512-EPlMYaA05tJ9km/0dI9K57iuMq3Tw+nHst7TNIegAJZrBPtsOtYaMFZEaWj02HA8FI5QvSnRHMt+CI+RIhXJBQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxlint/binding-linux-s390x-gnu@1.49.0': + resolution: {integrity: sha512-yZiQL9qEwse34aMbnMb5VqiAWfDY+fLFuoJbHOuzB1OaJZbN1MRF9Nk+W89PIpGr5DNPDipwjZb8+Q7wOywoUQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-x64-gnu@1.49.0': + resolution: {integrity: sha512-CcCDwMMXSchNkhdgvhVn3DLZ4EnBXAD8o8+gRzahg+IdSt/72y19xBgShJgadIRF0TsRcV/MhDUMwL5N/W54aQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-x64-musl@1.49.0': + resolution: {integrity: sha512-u3HfKV8BV6t6UCCbN0RRiyqcymhrnpunVmLFI8sEa5S/EBu+p/0bJ3D7LZ2KT6PsBbrB71SWq4DeFrskOVgIZg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxlint/binding-openharmony-arm64@1.49.0': + resolution: {integrity: sha512-dRDpH9fw+oeUMpM4br0taYCFpW6jQtOuEIec89rOgDA1YhqwmeRcx0XYeCv7U48p57qJ1XZHeMGM9LdItIjfzA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxlint/binding-win32-arm64-msvc@1.49.0': + resolution: {integrity: sha512-6rrKe/wL9tn0qnOy76i1/0f4Dc3dtQnibGlU4HqR/brVHlVjzLSoaH0gAFnLnznh9yQ6gcFTBFOPrcN/eKPDGA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxlint/binding-win32-ia32-msvc@1.49.0': + resolution: {integrity: sha512-CXHLWAtLs2xG/aVy1OZiYJzrULlq0QkYpI6cd7VKMrab+qur4fXVE/B1Bp1m0h1qKTj5/FTGg6oU4qaXMjS/ug==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxlint/binding-win32-x64-msvc@1.49.0': + resolution: {integrity: sha512-VteIelt78kwzSglOozaQcs6BCS4Lk0j+QA+hGV0W8UeyaqQ3XpbZRhDU55NW1PPvCy1tg4VXsTlEaPovqto7nQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@pnpm/config.env-replace@1.1.0': resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} engines: {node: '>=12.22.0'} @@ -3599,6 +3789,21 @@ packages: outdent@0.5.0: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + oxfmt@0.34.0: + resolution: {integrity: sha512-t+zTE4XGpzPTK+Zk9gSwcJcFi4pqjl6PwO/ZxPBJiJQ2XCKMucwjPlHxvPHyVKJtkMSyrDGfQ7Ntg/hUr4OgHQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + oxlint@1.49.0: + resolution: {integrity: sha512-YZffp0gM+63CJoRhHjtjRnwKtAgUnXM6j63YQ++aigji2NVvLGsUlrXo9gJUXZOdcbfShLYtA6RuTu8GZ4lzOQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + oxlint-tsgolint: '>=0.14.1' + peerDependenciesMeta: + oxlint-tsgolint: + optional: true + p-filter@2.1.0: resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} engines: {node: '>=8'} @@ -4187,6 +4392,10 @@ packages: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} + tinypool@2.1.0: + resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} + engines: {node: ^20.0.0 || >=22.0.0} + tinyrainbow@3.0.3: resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} engines: {node: '>=14.0.0'} @@ -4625,7 +4834,7 @@ snapshots: '@babel/types': 7.29.0 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -4677,7 +4886,7 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 lodash.debounce: 4.0.8 resolve: 1.22.11 transitivePeerDependencies: @@ -5369,7 +5578,7 @@ snapshots: '@babel/parser': 7.29.0 '@babel/template': 7.28.6 '@babel/types': 7.29.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -5380,41 +5589,6 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} - '@biomejs/biome@2.4.0': - optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.4.0 - '@biomejs/cli-darwin-x64': 2.4.0 - '@biomejs/cli-linux-arm64': 2.4.0 - '@biomejs/cli-linux-arm64-musl': 2.4.0 - '@biomejs/cli-linux-x64': 2.4.0 - '@biomejs/cli-linux-x64-musl': 2.4.0 - '@biomejs/cli-win32-arm64': 2.4.0 - '@biomejs/cli-win32-x64': 2.4.0 - - '@biomejs/cli-darwin-arm64@2.4.0': - optional: true - - '@biomejs/cli-darwin-x64@2.4.0': - optional: true - - '@biomejs/cli-linux-arm64-musl@2.4.0': - optional: true - - '@biomejs/cli-linux-arm64@2.4.0': - optional: true - - '@biomejs/cli-linux-x64-musl@2.4.0': - optional: true - - '@biomejs/cli-linux-x64@2.4.0': - optional: true - - '@biomejs/cli-win32-arm64@2.4.0': - optional: true - - '@biomejs/cli-win32-x64@2.4.0': - optional: true - '@bramus/specificity@2.4.2': dependencies: css-tree: 3.1.0 @@ -5946,6 +6120,120 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 + '@oxfmt/binding-android-arm-eabi@0.34.0': + optional: true + + '@oxfmt/binding-android-arm64@0.34.0': + optional: true + + '@oxfmt/binding-darwin-arm64@0.34.0': + optional: true + + '@oxfmt/binding-darwin-x64@0.34.0': + optional: true + + '@oxfmt/binding-freebsd-x64@0.34.0': + optional: true + + '@oxfmt/binding-linux-arm-gnueabihf@0.34.0': + optional: true + + '@oxfmt/binding-linux-arm-musleabihf@0.34.0': + optional: true + + '@oxfmt/binding-linux-arm64-gnu@0.34.0': + optional: true + + '@oxfmt/binding-linux-arm64-musl@0.34.0': + optional: true + + '@oxfmt/binding-linux-ppc64-gnu@0.34.0': + optional: true + + '@oxfmt/binding-linux-riscv64-gnu@0.34.0': + optional: true + + '@oxfmt/binding-linux-riscv64-musl@0.34.0': + optional: true + + '@oxfmt/binding-linux-s390x-gnu@0.34.0': + optional: true + + '@oxfmt/binding-linux-x64-gnu@0.34.0': + optional: true + + '@oxfmt/binding-linux-x64-musl@0.34.0': + optional: true + + '@oxfmt/binding-openharmony-arm64@0.34.0': + optional: true + + '@oxfmt/binding-win32-arm64-msvc@0.34.0': + optional: true + + '@oxfmt/binding-win32-ia32-msvc@0.34.0': + optional: true + + '@oxfmt/binding-win32-x64-msvc@0.34.0': + optional: true + + '@oxlint/binding-android-arm-eabi@1.49.0': + optional: true + + '@oxlint/binding-android-arm64@1.49.0': + optional: true + + '@oxlint/binding-darwin-arm64@1.49.0': + optional: true + + '@oxlint/binding-darwin-x64@1.49.0': + optional: true + + '@oxlint/binding-freebsd-x64@1.49.0': + optional: true + + '@oxlint/binding-linux-arm-gnueabihf@1.49.0': + optional: true + + '@oxlint/binding-linux-arm-musleabihf@1.49.0': + optional: true + + '@oxlint/binding-linux-arm64-gnu@1.49.0': + optional: true + + '@oxlint/binding-linux-arm64-musl@1.49.0': + optional: true + + '@oxlint/binding-linux-ppc64-gnu@1.49.0': + optional: true + + '@oxlint/binding-linux-riscv64-gnu@1.49.0': + optional: true + + '@oxlint/binding-linux-riscv64-musl@1.49.0': + optional: true + + '@oxlint/binding-linux-s390x-gnu@1.49.0': + optional: true + + '@oxlint/binding-linux-x64-gnu@1.49.0': + optional: true + + '@oxlint/binding-linux-x64-musl@1.49.0': + optional: true + + '@oxlint/binding-openharmony-arm64@1.49.0': + optional: true + + '@oxlint/binding-win32-arm64-msvc@1.49.0': + optional: true + + '@oxlint/binding-win32-ia32-msvc@1.49.0': + optional: true + + '@oxlint/binding-win32-x64-msvc@1.49.0': + optional: true + '@pnpm/config.env-replace@1.1.0': {} '@pnpm/network.ca-file@1.0.2': @@ -6781,6 +7069,10 @@ snapshots: optionalDependencies: supports-color: 8.1.1 + debug@4.4.3: + dependencies: + ms: 2.1.3 + debug@4.4.3(supports-color@8.1.1): dependencies: ms: 2.1.3 @@ -7186,7 +7478,7 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -7199,7 +7491,7 @@ snapshots: https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -7698,6 +7990,52 @@ snapshots: outdent@0.5.0: {} + oxfmt@0.34.0: + dependencies: + tinypool: 2.1.0 + optionalDependencies: + '@oxfmt/binding-android-arm-eabi': 0.34.0 + '@oxfmt/binding-android-arm64': 0.34.0 + '@oxfmt/binding-darwin-arm64': 0.34.0 + '@oxfmt/binding-darwin-x64': 0.34.0 + '@oxfmt/binding-freebsd-x64': 0.34.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.34.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.34.0 + '@oxfmt/binding-linux-arm64-gnu': 0.34.0 + '@oxfmt/binding-linux-arm64-musl': 0.34.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.34.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.34.0 + '@oxfmt/binding-linux-riscv64-musl': 0.34.0 + '@oxfmt/binding-linux-s390x-gnu': 0.34.0 + '@oxfmt/binding-linux-x64-gnu': 0.34.0 + '@oxfmt/binding-linux-x64-musl': 0.34.0 + '@oxfmt/binding-openharmony-arm64': 0.34.0 + '@oxfmt/binding-win32-arm64-msvc': 0.34.0 + '@oxfmt/binding-win32-ia32-msvc': 0.34.0 + '@oxfmt/binding-win32-x64-msvc': 0.34.0 + + oxlint@1.49.0: + optionalDependencies: + '@oxlint/binding-android-arm-eabi': 1.49.0 + '@oxlint/binding-android-arm64': 1.49.0 + '@oxlint/binding-darwin-arm64': 1.49.0 + '@oxlint/binding-darwin-x64': 1.49.0 + '@oxlint/binding-freebsd-x64': 1.49.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.49.0 + '@oxlint/binding-linux-arm-musleabihf': 1.49.0 + '@oxlint/binding-linux-arm64-gnu': 1.49.0 + '@oxlint/binding-linux-arm64-musl': 1.49.0 + '@oxlint/binding-linux-ppc64-gnu': 1.49.0 + '@oxlint/binding-linux-riscv64-gnu': 1.49.0 + '@oxlint/binding-linux-riscv64-musl': 1.49.0 + '@oxlint/binding-linux-s390x-gnu': 1.49.0 + '@oxlint/binding-linux-x64-gnu': 1.49.0 + '@oxlint/binding-linux-x64-musl': 1.49.0 + '@oxlint/binding-openharmony-arm64': 1.49.0 + '@oxlint/binding-win32-arm64-msvc': 1.49.0 + '@oxlint/binding-win32-ia32-msvc': 1.49.0 + '@oxlint/binding-win32-x64-msvc': 1.49.0 + p-filter@2.1.0: dependencies: p-map: 2.1.0 @@ -8249,6 +8587,8 @@ snapshots: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 + tinypool@2.1.0: {} + tinyrainbow@3.0.3: {} tldts-core@6.1.86: {} diff --git a/readme.md b/readme.md index a5ec21a12..e70d5b67f 100644 --- a/readme.md +++ b/readme.md @@ -106,8 +106,8 @@ Another example would be to show a `