---
title: @envlock/core
description: Schema builders, validation, dotenv parsing, .env.example rendering, diff, redact, and describe — zero runtime dependencies.
url: https://pr-1-7abef52380a8.thally.app/envlock-core-api
---

# @envlock/core

Schema builders, validation, dotenv parsing, .env.example rendering, diff, redact, and describe — zero runtime dependencies.

`@envlock/core` provides everything needed to define, validate, and project
environment contracts. It has **zero runtime dependencies**.

```bash
npm install @envlock/core
```

## Schema definition

### `defineEnv(shape): EnvSchema`

Creates a frozen schema from a record of field declarations. Keys must match
`/^[A-Za-z_][A-Za-z0-9_]*$/`; throws `TypeError` on invalid names.

```ts
import { defineEnv, env } from "@envlock/core";

const schema = defineEnv({
  PORT: env.port().default(3000),
  DATABASE_URL: env.url({ protocols: ["postgres:"] }).secret(),
});
```

### `isEnvSchema(value): boolean`

Type guard that recognizes objects with `kind: "envlock.schema"`.

### `env` builder

The `env` namespace provides factory methods for all 10 field types. See
[Schema & fields](/envlock-schema-definition) for the complete reference.

## Validation

### `loadEnv(schema, source?, options?): Infer<S>`

Validates the source (defaults to `process.env`) against the schema. Returns
fully typed values on success or throws `EnvValidationError` listing every
issue.

```ts
import { loadEnv } from "@envlock/core";
import schema from "./envlock.config.mjs";

const config = loadEnv(schema);
```

### `parseEnv(schema, source, options?): ParseResult<S>`

Non-throwing alternative. Returns a discriminated union:

```ts
type ParseResult<S> =
  | { ok: true; values: Infer<S>; issues: [] }
  | { ok: false; issues: EnvIssue[] };
```

```ts
import { parseEnv } from "@envlock/core";

const result = parseEnv(schema, process.env);
if (!result.ok) {
  console.error(formatIssues(result.issues));
  process.exit(1);
}
// result.values is typed
```

#### `ParseOptions`

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `strict` | `boolean` | `false` | Report unknown keys (only meaningful for bounded sources like dotenv files, not `process.env`). |

### `formatIssues(issues): string`

Formats an array of `EnvIssue` as an indented bullet list.

### `EnvValidationError`

Extends `Error` with `name: "EnvValidationError"` and an `.issues` array. The
message lists all issues with a count header and indented bullets.

## Types

### `EnvIssue`

```ts
interface EnvIssue {
  key: string;
  code: IssueCode;     // "missing" | "invalid" | "unknown"
  message: string;
  received?: string;   // masked as "••••••" for secret fields
}
```

### `IssueCode`

`"missing" | "invalid" | "unknown"`

### `EnvSource`

`Readonly<Record<string, string | undefined>>` — compatible with `process.env`.

## Dotenv

### `parseDotenv(text): Record<string, string>`

Parses dotenv-formatted text into a plain record. Never throws. Supports single
and double quotes, escape sequences, multi-line values, inline comments, and
`export` prefixes.

### `formatDotenv(record): string`

Serializes a record back to dotenv format.

> **Note:**
  `parseDotenv` returns a plain record. You compose sources yourself:
  `loadEnv(schema, { ...parseDotenv(text), ...process.env })`.

## Projections

### `renderExample(schema, options?): string`

Renders `.env.example` text from the schema. Each variable gets a comment with
its description and constraints; secret fields show `"••••••"` instead of the
example value.

```ts
import { renderExample } from "@envlock/core";

const text = renderExample(schema);
// # HTTP listen port
// PORT=3000
// # Primary database connection string
// DATABASE_URL=
```

#### `RenderExampleOptions`

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `header` | `readonly string[]` | default header | Comment lines at the top of the file. Pass `[]` to omit. |

### `diffEnv(schema, source): EnvDiff`

Returns `{ missing, unknown, invalid, ok }` — arrays of keys partitioned by
status. Always validates in strict mode.

### `redact(values, schema): Record`

Shallow-copies the values with secret fields replaced by `"••••••"`.

```ts
import { loadEnv, redact } from "@envlock/core";

const config = loadEnv(schema);
console.log(redact(config, schema)); // DATABASE_URL: "••••••"
```

### `describeSchema(schema): SchemaDescription[]`

Returns a JSON-serializable array describing each variable: kind, required,
hasDefault, defaultValue (masked for secrets), description, constraints, and
exampleValue.

## Constants

| Constant | Value |
| --- | --- |
| `FIELD_KINDS` | `{ string, number, integer, boolean, port, url, enum, json, duration, list }` |
| `ISSUE_CODES` | `{ missing: "missing", invalid: "invalid", unknown: "unknown" }` |
| `REDACTED_VALUE` | `"••••••"` |
| `SCHEMA_KIND` | `"envlock.schema"` |