@cogs/environment
v1.0.0
Published
Typed env registry machinery — a generic factory that turns a per-app variable catalog into a Zod runtime schema, startup validators, and a public-only client snapshot. Ships the framework, not the variable list
Downloads
1,373
Readme
@cogs/environment
Typed env registry machinery — the reusable framework, not a variable list.
Each app declares its own per-repo catalog and passes it to defineEnvironment,
which derives everything else from that single source of truth: a Zod runtime
schema, startup validators, and a public-only client snapshot.
These are runtime vars — read from process.env at request time, no
NEXT_PUBLIC_ prefix, never build-inlined. source / type are metadata for
docs + tooling; they do not change how a value is read.
Peer dependency: zod (^3).
See docs/ENVIRONMENT-CONFIG.md
for the full spec.
API
defineEnvironment(catalog)
Generic factory over a catalog of Record<string, EnvironmentVariableDefinition>.
Returns:
| member | description |
| --- | --- |
| catalog | the catalog you passed in |
| RuntimeEnvironmentSchema | z.object built from each def.schema (unconstrained keys fall back to z.string().optional()) |
| validateRuntimeEnvironment(env = process.env) | non-throwing safeParse — inspect .success / .error.issues |
| assertValidRuntimeEnvironment(env = process.env) | throws with formatted issues; call once at startup (e.g. instrumentation.ts) |
| getPublicEnv(env = process.env) | returns only keys whose def.type === 'public' — secrets are excluded by omission |
Primitives
optionalSecret(message)—z.preprocessthat trims, treats blank as omitted, and requires a non-empty value when present. Use for optional secrets.- Types:
EnvironmentVariableSource(vault|runtime|build|generated),EnvironmentVariableType(public|secret|connection),EnvironmentVariableDefinition. - Generic derived types:
EnvironmentVariableKey<C>(→keyof C) andEnvState<C>(→{ [K in keyof C]?: string }).
Worked example
import { defineEnvironment, optionalSecret } from "@cogs/environment";
import { z } from "zod";
export const { RuntimeEnvironmentSchema, validateRuntimeEnvironment, assertValidRuntimeEnvironment, getPublicEnv } =
defineEnvironment({
ENV: {
source: "runtime",
type: "public",
description: "Deployment environment name.",
schema: z.enum(["dev", "qa", "prod"]).optional(),
},
FOO_API_BASE_URL: {
source: "runtime",
type: "connection",
description: "Base URL for the Foo API service (wired into the fetch-client).",
schema: z.string().url().optional(),
},
AUTH_CLIENT_SECRET: {
source: "vault",
type: "secret",
description: "OIDC client secret. Server-side only — never expose to the client.",
schema: optionalSecret("AUTH_CLIENT_SECRET must be non-empty when provided"),
},
});At startup (instrumentation.ts):
assertValidRuntimeEnvironment();Feeding the client
getPublicEnv is what feeds EnvProvider's initialEnv. The server layout
builds the snapshot from getPublicEnv(process.env) and passes it to a
'use client' EnvProvider; client code reads via useEnv(), never
process.env. Because getPublicEnv returns only type: 'public' keys,
secrets and connection URLs never reach the browser.
// app/layout.tsx (server)
const initialEnv = getPublicEnv(process.env);
return <EnvProvider initialEnv={initialEnv}>{children}</EnvProvider>;Related
- Pairs with
@cogs/config— the framework-agnostic layered.envloader that populatesprocess.envbefore this registry reads it. - The app-side
EnvProvider+setClientConfig+ auth-header wiring lives in theruntime-env-configskill, not in either package.
