#Configurations
Alepha provides two primitives for configuration: $env for environment variables and $atom for runtime state.
#Environment Variables with $env
$env reads environment variables with schema validation, type coercion, and defaults. Import it from "alepha".
1import { $env, t } from "alepha"; 2 3class App { 4 env = $env(t.object({ 5 DATABASE_URL: t.text(), 6 PORT: t.integer({ default: 3000 }), 7 DEBUG: t.optional(t.boolean()), 8 })); 9 10 connect() {11 console.log(this.env.DATABASE_URL); // string, guaranteed to exist12 console.log(this.env.PORT); // number, defaults to 300013 console.log(this.env.DEBUG); // boolean | undefined14 }15}
The schema must be a t.object(...). Each property maps to an environment variable name.
Alepha validates values at instantiation time and throws if required variables are missing.
#How env values are resolved
Alepha.create() merges process.env with any values passed in the state.env option:
1const alepha = Alepha.create({2 env: {3 DATABASE_URL: "postgres://localhost/mydb",4 PORT: "8080",5 },6});
Values from process.env take precedence. This means .env files loaded before the process starts (e.g. via alepha dev) are available automatically.
#Variable interpolation
String values support $VAR interpolation using other variables from the same schema:
1class Config {2 env = $env(t.object({3 HOST: t.text({ default: "localhost" }),4 PORT: t.integer({ default: 5432 }),5 DB_NAME: t.text({ default: "mydb" }),6 DATABASE_URL: t.text({ default: "postgres://$HOST:$PORT/$DB_NAME" }),7 }));8}
#Environment caching
Alepha caches parsed env results per schema. Multiple services using the same t.object(...) reference will share the same parsed output.
#State Management with $atom
$atom defines a named, typed, validated piece of global state. Use it for application-level configuration and shared data.
#Defining an atom
1import { $atom, t } from "alepha"; 2 3const appConfig = $atom({ 4 name: "app.config", 5 schema: t.object({ 6 theme: t.enum(["light", "dark"]), 7 language: t.text({ default: "en" }), 8 }), 9 default: { theme: "light", language: "en" },10});
The name uniquely identifies the atom in the state store. The schema defines the shape and validation. The default provides the initial value.
Recommended naming convention for name is dot-separated, e.g. "app.config", "user.settings", etc.
If the schema has all optional fields (via t.optional), the default is optional too. Otherwise, default is required.
#Reading and writing atoms
Use alepha.store.get() and alepha.store.set() (or alepha.set() as shorthand):
1const alepha = Alepha.create(); 2 3// Read 4const config = alepha.store.get(appConfig); 5console.log(config.theme); // "light" 6 7// Write 8alepha.store.set(appConfig, { theme: "dark", language: "fr" }); 9 10// Shorthand write on the container11alepha.set(appConfig, { theme: "dark", language: "fr" });
#Injecting atoms with $use
$use creates a reactive getter that always returns the current atom value:
1import { $atom, $use, t } from "alepha"; 2 3const count = $atom({ 4 name: "count", 5 schema: t.object({ value: t.number() }), 6 default: { value: 0 }, 7}); 8 9class Counter {10 count = $use(count);11 12 current() {13 return this.count.value; // always reads current state14 }15}
Under the hood, $use registers the atom and replaces the property with a getter that reads from the state store. When the state changes, the next property access returns the updated value:
1const alepha = Alepha.create();2const counter = alepha.inject(Counter);3 4console.log(counter.count.value); // 05 6alepha.store.set(count, { value: 42 });7console.log(counter.count.value); // 42
#State mutation events
Every store.set() call emits a "state:mutate" event:
1alepha.events.on("state:mutate", ({ key, value, prevValue }) => {2 console.log(`State "${key}" changed from`, prevValue, "to", value);3});
Atoms is not only about configuration ! This powers SSR hydration, React integration, and devtools.