---
name: ibgib-enum-pattern
description: more robust and powerful enum pattern used in this codebase
---

# Ibgib Enum Pattern Skill

This codebase **never** uses TypeScript's built-in `enum` keyword (it has
problematic runtime semantics and poor tree-shaking). Instead, use the
following pattern for every enum-like type.

## When to use

- Three or more related string (or occasionally number) constants.
- The values are part of a discriminated union or are validated at runtime.
- A simple `'a' | 'b' | 'c'` union is fine for small, stable sets; promote
  to the full pattern once the set starts to branch out or needs a guard/validator.

## Full pattern

```typescript
// #region MyEnum enum

// 1. One exported const per value.
//    - Name: SCREAMING_SNAKE_CASE  PREFIX_MEMBER
//    - Value: kebab-case string literal
//    - Hyphens in values → underscores in const names.
export const MY_ENUM_FOO_BAR = 'foo-bar';
export const MY_ENUM_BAZ     = 'baz';

// 2. Union type — use typeof references so renaming a const propagates.
/**
 * JSDoc describing the enum's purpose.
 */
export type MyEnum =
    | typeof MY_ENUM_FOO_BAR
    | typeof MY_ENUM_BAZ
    ;

// 3. Const enum object — keys are camelCase/underscore versions of the values
//    (replace hyphens with underscores in the key).
//    Satisfies the union type so TypeScript catches typos at declaration time.
export const MyEnum = {
    /** Doc for foo-bar member. */
    foo_bar: MY_ENUM_FOO_BAR,
    /** Doc for baz member. */
    baz: MY_ENUM_BAZ,
} satisfies { readonly [key: string]: MyEnum };

// 4. Valid-values array (derived from the object — stays in sync automatically).
export const MY_ENUM_VALID_VALUES: MyEnum[] = Object.values(MyEnum);

// 5. Type guard.
export function isMyEnum(value: any): value is MyEnum {
    return MY_ENUM_VALID_VALUES.includes(value);
}

// #endregion MyEnum enum
```

## Consuming the enum

Callers should reference values via the **const object**, not the individual string constants:

```typescript
// ✅ preferred — readable, refactor-safe
switch (strategy) {
    case ParseMechanicalStrategy.markdown_heading:   return splitByHeadings(text);
    case ParseMechanicalStrategy.markdown_paragraph: return splitByParagraph(text);
    case ParseMechanicalStrategy.custom_regexp:      return splitByRegexp(text, opts.regexp);
    default: throw new Error(`(UNEXPECTED) unhandled strategy: '${strategy}'`);
}

// ⚠️  acceptable when the constant itself is needed (e.g. error messages, assertions)
if (strategy === PARSE_MECHANICAL_STRATEGY_CUSTOM_REGEXP && !opts.regexp) {
    throw new Error(`strategy is '${PARSE_MECHANICAL_STRATEGY_CUSTOM_REGEXP}' but regexp is falsy`);
}

// ❌ avoid — brittle string literals scattered through call sites
if (strategy === 'markdown-heading') { ... }
```

## Rules

| Rule | Detail |
|---|---|
| **No `enum` keyword** | Use the pattern above. `enum` has footguns (reverse mapping, ambient vs const, isolatedModules). |
| **Kebab-case values** | String values use kebab-case (`'foo-bar'`). |
| **Const names** | `PREFIX_MEMBER` in SCREAMING_SNAKE_CASE; hyphens in value → underscores in name. |
| **Object keys** | Match the value with hyphens replaced by underscores (`foo_bar`). |
| **`satisfies`** | Always use `satisfies { readonly [key: string]: MyEnum }` on the const object. |
| **Start simple** | A plain string-literal union (`'foo-bar' | 'baz'`) is fine until the set grows or needs a guard. Promote to the full pattern at that point. |
| **`_VALID_VALUES`** | Always derive from `Object.values(MyEnum)`, never hardcode. |
| **Type guard** | Implement `isMyEnum` via `VALID_VALUES.includes(value)`. |

## Region comments

Wrap the entire block in:
```typescript
// #region MyEnum enum
// ...
// #endregion MyEnum enum
```

## Example from codebase

```typescript
// #region ChromeAIAvailability enum
export const CHROME_AI_AVAILABILITY_UNAVAILABLE = 'unavailable';
export const CHROME_AI_AVAILABILITY_DOWNLOADABLE = 'downloadable';
export const CHROME_AI_AVAILABILITY_DOWNLOADING = 'downloading';
export const CHROME_AI_AVAILABILITY_AVAILABLE = 'available';

export type ChromeAIAvailability =
    | typeof CHROME_AI_AVAILABILITY_UNAVAILABLE
    | typeof CHROME_AI_AVAILABILITY_DOWNLOADABLE
    | typeof CHROME_AI_AVAILABILITY_DOWNLOADING
    | typeof CHROME_AI_AVAILABILITY_AVAILABLE
    ;

/**
 * The availability of a Chrome AI API.
 *
 * @see https://developer.chrome.com/docs/ai/built-in
 */
export const ChromeAIAvailability = {
    /**
     * The user's device or requested session options are not supported.
     * The device may have insufficient power or disk space.
     */
    unavailable: CHROME_AI_AVAILABILITY_UNAVAILABLE,
    /**
     * Additional downloads are needed to create a session, which may include
     * an expert model, a language model, or fine-tuning. User activation may
     * be required to call create().
     */
    downloadable: CHROME_AI_AVAILABILITY_DOWNLOADABLE,
    /**
     * Downloads are ongoing and must complete before you can use a a session.
     */
    downloading: CHROME_AI_AVAILABILITY_DOWNLOADING,
    /**
     * You can create a session immediately.
     */
    available: CHROME_AI_AVAILABILITY_AVAILABLE,
} satisfies { readonly [key: string]: ChromeAIAvailability; };

export const CHROME_AI_AVAILABILITY_VALID_VALUES: ChromeAIAvailability[] = Object.values(ChromeAIAvailability);

export function isChromeAIAvailability(value: any): value is ChromeAIAvailability {
    return CHROME_AI_AVAILABILITY_VALID_VALUES.includes(value);
}
// #endregion ChromeAIAvailability enum
```
