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.
Feature Checkout {
Variable enabled Boolean = true
Variable max_items Number = 50
Variable header_text String = "Complete your purchase"
}
Run one command:
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
&&
The binary is at target/release/variable.
Quick start
1. Write a .var file
Feature Search {
Variable enabled Boolean = false
Variable max_results Number = 25
Variable placeholder String = "Search..."
}
2. Generate TypeScript
This creates src/generated/search.generated.ts containing:
- A
SearchVariablesinterface 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.
The .var DSL
A .var file contains one or more Feature blocks. Each feature has typed variables with mandatory defaults.
Feature Checkout {
Variable enabled Boolean = true
Variable max_items Number = 50
Variable header_text String = "Complete your purchase"
}
Feature Search {
Variable fuzzy_matching Boolean = false
Variable max_results Number = 25
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
- Variable names must be unique within a feature
- 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:
=== 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-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 (39 total)
# Build the CLI
# Update codegen snapshots after changing output format
License
MIT