envil envil Docs

Quickstart

Define and materialize composable server and client environment Effects.

bun add @ayronforge/envil effect

Define the environment

import {
  client,
  createEnv,
  port,
  redacted,
  requiredString,
  server,
  shared,
  url,
} from "@ayronforge/envil";

export const appEnv = createEnv(
  shared({
    APP_NAME: "My App",
  }),
  server({
    DATABASE_URL: redacted(requiredString),
    PORT: port,
  }),
  client(
    {
      APP_URL: url,
    },
    {
      runtimeEnv: import.meta.env,
      prefix: "VITE_",
    },
  ),
);

Server fragments default to process.env. A client fragment containing schemas requires an explicit runtimeEnv, because browser frameworks expose public configuration differently. Static values may also be declared directly in server(...) or client(...).

Run the desired Effect

import { Effect } from "effect";

const serverEnv = Effect.runSync(appEnv.server);
const clientEnv = await Effect.runPromise(appEnv.client);

Both properties are lazy Effects. The caller chooses synchronous or asynchronous execution. If a schema or resolver is asynchronous, run the Effect asynchronously.

The client output contains client and shared values. The server output contains all three targets, matching what a server bundle can use.

Protect client builds

For Vite applications, add the Envil plugin:

import { defineConfig } from "vite";
import envil from "@ayronforge/envil/plugins/vite";

export default defineConfig({
  plugins: [envil()],
});

The plugin removes every server(...) fragment expression before the client module graph is bundled and injects an exact runtime target. It does not need to inspect or evaluate your schemas, resolver adapters, spreads, or factory calls.

See Build and Runtime Security for Rollup, Rolldown, webpack, Rspack, and esbuild setup.