variable 0.1.2

Type-safe feature flag code generation from .var files
variable-0.1.2 is not a library.

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:

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

Get type-safe 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

Build from source

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

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

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

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

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:

// 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:

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:

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/. Each is a self-contained project you can run.

Basic

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

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

# Run all tests
cargo test

# Build the CLI
cargo build --release

# Update codegen snapshots after changing output format
cargo insta review

License

MIT