> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ooneex.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Feature Flag

> Define feature flags as injectable, named toggles with a key, a description, and a sync or async enablement check.

The `@ooneex/feature-flag` component is a lightweight, type-safe contract for gating functionality behind toggles. Each flag is a small, injectable class implementing the `IFeatureFlag` interface — `getKey`, `getDescription`, and `isEnabled` — registered with a decorator and resolved from the container. The enablement check can be synchronous or asynchronous, so a flag can return a constant, read an environment variable, or query a remote service.

## Why this component

* **One focused contract.** Every flag implements the same `IFeatureFlag` interface, so call sites evaluate them the same way regardless of how each flag decides.
* **Sync or async.** `isEnabled()` may return a `boolean` or a `Promise<boolean>` — back a flag with a constant, an env var, config, or a remote check without changing callers.
* **Self-describing.** Each flag carries a stable `getKey()` and a human-readable `getDescription()`, so flags are discoverable and auditable.
* **Container-managed.** Register a flag class with a decorator and resolve it from the DI container with any scope.
* **Type-safe.** Full TypeScript support with `IFeatureFlag` and `FeatureFlagClassType`.

## How it works

You implement `IFeatureFlag` as a class, register it with the `featureFlag` decorator, and resolve it from the container. Callers invoke `isEnabled()` and branch on the result.

| Method             | Purpose                                                                     |
| ------------------ | --------------------------------------------------------------------------- |
| `getKey()`         | Return a unique, stable identifier for the flag (kebab-case by convention). |
| `getDescription()` | Return a human-readable explanation of what the flag controls.              |
| `isEnabled()`      | Decide whether the flag is on; returns `boolean` or `Promise<boolean>`.     |

The interface is the whole contract:

```typescript theme={null}
interface IFeatureFlag {
  getKey: () => string;
  getDescription: () => string;
  isEnabled: () => Promise<boolean> | boolean;
}
```

Because `isEnabled()` can be async, the same flag shape covers constant toggles, environment-driven rollouts, and remote configuration — callers always `await` the result and never need to know the source.

## Decorator and usage

### `@decorator.featureFlag(scope?)`

Registers a feature-flag class with the DI container. It accepts an optional `EContainerScope` (defaults to `EContainerScope.Singleton`).

Implement `IFeatureFlag` and register it:

```typescript theme={null}
import { decorator, type IFeatureFlag } from "@ooneex/feature-flag";

@decorator.featureFlag()
export class DarkModeFeatureFlag implements IFeatureFlag {
  public getKey(): string {
    return "dark-mode";
  }

  public getDescription(): string {
    return "Enables the dark mode theme";
  }

  public isEnabled(): boolean {
    return true;
  }
}
```

`isEnabled()` can resolve a value from a remote service, database, or environment:

```typescript theme={null}
import { decorator, type IFeatureFlag } from "@ooneex/feature-flag";

@decorator.featureFlag()
export class BetaCheckoutFeatureFlag implements IFeatureFlag {
  public getKey(): string {
    return "beta-checkout";
  }

  public getDescription(): string {
    return "Enables the redesigned checkout flow for beta users";
  }

  public async isEnabled(): Promise<boolean> {
    return process.env.BETA_CHECKOUT === "true";
  }
}
```

Resolve the flag from the container and branch on the result:

```typescript theme={null}
import { container } from "@ooneex/container";
import { DarkModeFeatureFlag } from "./DarkModeFeatureFlag";

const flag = container.get(DarkModeFeatureFlag);

if (await flag.isEnabled()) {
  // ...render dark mode
}
```

Pass a scope when a flag should not be a singleton — for example, re-evaluate per request:

```typescript theme={null}
import { EContainerScope } from "@ooneex/container";
import { decorator, type IFeatureFlag } from "@ooneex/feature-flag";

@decorator.featureFlag(EContainerScope.Transient)
export class ExperimentalFeatureFlag implements IFeatureFlag {
  public getKey(): string {
    return "experimental";
  }

  public getDescription(): string {
    return "Enables experimental features";
  }

  public isEnabled(): boolean {
    return false;
  }
}
```

## Best practices

* **Keep keys stable and unique.** Use kebab-case keys like `dark-mode` and `beta-checkout`; never reuse or rename a key once it ships, since logs and config may reference it.
* **Write a real description.** `getDescription()` is what makes flags auditable — explain what the flag controls and who it targets, not just its name.
* **Always `await` the result.** Treat `isEnabled()` as potentially async even for constant flags, so a flag can later move to a remote check without touching callers.
* **Keep the decision in one place.** Put all gating logic inside `isEnabled()`; callers should only branch on the boolean, never re-derive the condition.
* **Match the scope to the source.** Use the default singleton for constant or env-driven flags; use `Transient` or `Request` when the flag must be re-evaluated per request.
* **Inject dependencies.** When a flag reads config or calls a service, inject those dependencies through the constructor rather than reaching for globals.

## CLI command

Scaffold a feature flag class and its test file with the generator. It writes the class under `modules/<module>/src/flags/<Name>FeatureFlag.ts` and installs `@ooneex/feature-flag` if it is missing.

```bash theme={null}
# Interactive: prompts for the name
ooneex flag:create

# Provide the name
ooneex flag:create --name=NewCheckout

# Target a module and overwrite
ooneex flag:create --name=NewCheckout --module=checkout --override
```

| Option       | Description                                                                  | Default             |
| ------------ | ---------------------------------------------------------------------------- | ------------------- |
| `--name`     | Feature flag class name. The `FeatureFlag` suffix is appended automatically. | Prompted if omitted |
| `--module`   | Target module the class is generated into.                                   | `shared`            |
| `--override` | Overwrite an existing class without prompting.                               | `false`             |

The generated class is a ready-to-complete stub with the key pre-filled in kebab-case from the name:

```typescript theme={null}
import { decorator, type IFeatureFlag } from "@ooneex/feature-flag";

@decorator.featureFlag()
export class NewCheckoutFeatureFlag implements IFeatureFlag {
  public getKey(): string {
    return "new-checkout";
  }

  public getDescription(): string {
    return "";
  }

  public isEnabled(): Promise<boolean> | boolean {
    return false;
  }
}
```

See [flag:create](/cli/commands/flag-create) for the full command reference.

## Use with Claude and Codex

The generator ships a matching `flag:create` skill. It runs the scaffold and then guides your AI agent through completing the flag — setting a stable key, writing the description, and implementing `isEnabled()` with the real gating logic. Initialize the skills once for your agent:

<Tabs>
  <Tab title="Claude">
    ```bash theme={null}
    ooneex claude:init
    ```

    Then ask Claude in natural language — it maps the request to the generator, runs it, and fills in the implementation:

    ```text Prompt icon="terminal" wrap theme={null}
    Create a feature flag that enables the new checkout flow.
    ```
  </Tab>

  <Tab title="Codex">
    ```bash theme={null}
    ooneex codex:init
    ```

    Then ask Codex in natural language — it maps the request to the generator, runs it, and fills in the implementation:

    ```text Prompt icon="terminal" wrap theme={null}
    Create a feature flag that enables the new checkout flow.
    ```
  </Tab>
</Tabs>

For example, the prompt above maps to `flag:create --name=NewCheckout`, then implements `getKey`, `getDescription`, and `isEnabled` for the new checkout flow.
