variable 0.1.2

Type-safe feature flag code generation from .var files
# Variable

Variable is a type-safe feature flag system. You define features in `.var` files, and Variable generates typed code so you never misspell a flag name, pass the wrong type, or forget a default.

```
1: Feature Checkout = {
    1: Variable enabled Boolean = true
    2: Variable max_items Number = 50
    3: Variable header_text String = "Complete your purchase"
}
```

Run one command:

```sh
variable generate --out ./src/generated features.var
```

Get type-safe TypeScript:

```typescript
import { VariableClient } from "@variable/runtime";
import { getCheckoutVariables } from "./generated/features.generated";

const client = new VariableClient();
const checkout = getCheckoutVariables(client);

checkout.enabled      // boolean
checkout.max_items    // number
checkout.header_text  // string
```

No string lookups. No `as any`. Full autocompletion.

## Getting Started

### Prerequisites

- [Rust]https://rustup.rs/ (1.85+)
- [Node.js]https://nodejs.org/ (for TypeScript projects)

### Build from source

```sh
git clone <repo-url> && cd variable
cargo build --release
```

The binary is at `target/release/variable`.

### Quick start

**1. Write a `.var` file**

```
1: Feature Search = {
    1: Variable enabled Boolean = false
    2: Variable max_results Number = 25
    3: Variable placeholder String = "Search..."
}
```

**2. Generate TypeScript**

```sh
variable generate --out ./src/generated search.var
```

This creates `src/generated/search.generated.ts` containing:
- A `SearchVariables` interface with typed fields
- A `getSearchVariables()` function that returns values with defaults baked in

**3. Use in your code**

```typescript
import { VariableClient } from "@variable/runtime";
import { getSearchVariables } from "./generated/search.generated";

const client = new VariableClient();
const search = getSearchVariables(client);

console.log(search.max_results); // 25
```

The `VariableClient` accepts a `Provider` that can supply overrides (from a remote service, local config, etc.). With no provider, you get the defaults from your `.var` file.

**4. Encode binary snapshot bytes**

```sh
variable encode search.var > search.snapshot.bin
```

The `encode` command writes the full snapshot payload to stdout, so it can be piped to files or other tools.

**5. Decode binary snapshot bytes for diagnostics**

```sh
variable decode --pretty search.snapshot.bin
```

`decode` emits JSON containing decoded snapshot content and diagnostics (without attempting to reconstruct `.var` source).

## The `.var` DSL

A `.var` file contains one or more `Feature` blocks. Each feature has typed variables with mandatory defaults.

```
1: Feature Checkout = {
    1: Variable enabled Boolean = true
    2: Variable max_items Number = 50
    3: Variable header_text String = "Complete your purchase"
}

2: Feature Search = {
    1: Variable fuzzy_matching Boolean = false
    2: Variable max_results Number = 25
    3: Variable placeholder String = "Search..."
}
```

### Supported types

| Type | TypeScript | Example |
|------|------------|---------|
| `Boolean` | `boolean` | `true`, `false` |
| `Number` | `number` | `42`, `3.14` |
| `String` | `string` | `"hello"`, `"line\nnewline"` |

### Rules

- Feature names must be unique across the file
- Feature IDs must be unique across the file
- Variable names must be unique within a feature
- Variable IDs must be unique within a feature
- Feature and variable IDs are required (`u32`)
- Every variable must have a default value
- String literals support escape sequences: `\n`, `\t`, `\\`, `\"`

## What gets generated

For a feature named `Checkout`, Variable generates:

```typescript
// This file is generated by Variable. Do not edit.
import { VariableClient } from "@variable/runtime";

export interface CheckoutVariables {
  enabled: boolean;
  max_items: number;
  header_text: string;
}

const checkoutDefaults: CheckoutVariables = {
  enabled: true,
  max_items: 50,
  header_text: "Complete your purchase",
};

export function getCheckoutVariables(client: VariableClient): CheckoutVariables {
  const overrides = client.getFeatureValues("Checkout");
  return {
    ...checkoutDefaults,
    ...overrides,
  } as CheckoutVariables;
}
```

Defaults are baked in. Overrides from a provider are spread on top. You always get a complete, typed object.

## Runtime

The `@variable/runtime` package (in `runtime/ts/`) provides the types that generated code imports:

```typescript
import { VariableClient, NoopProvider } from "@variable/runtime";

// Default: no overrides, just use .var defaults
const client = new VariableClient();

// Or bring your own provider
const client = new VariableClient(myProvider);
```

A `Provider` implements one method:

```typescript
interface Provider {
  getManifest(): Record<string, Record<string, unknown>>;
}
```

The `NoopProvider` returns an empty manifest, so all values fall back to the defaults in generated code.

## Examples

Working examples are in [`examples/`](examples/). Each is a self-contained project you can run.

### [Basic]examples/ts/basic/

Generates code from a `.var` file and uses it at runtime:

```sh
cd examples/ts/basic
npm install
npm start
```

```
=== Checkout Feature ===
  enabled:     true
  max_items:   50
  header_text: Complete your purchase

=== Search Feature ===
  enabled:      false
  max_results:  10
  placeholder:  Search...

All assertions passed!
```

## Project Structure

```
variable-core/       Lexer, parser, AST, validation
variable-codegen/    TypeScript code generation
variable-wire/       Binary wire-format encode/decode tooling
variable-cli/        CLI binary (the `variable` command)
runtime/ts/          TypeScript runtime (@variable/runtime)
examples/ts/         TypeScript usage examples
fixtures/            Sample .var files for testing
```

## Development

```sh
# Run all tests
cargo test

# Build the CLI
cargo build --release

# Update codegen snapshots after changing output format
cargo insta review
```

## License

MIT