# Config

A `Config<T>` describes how to read and validate a single typed value of type
`T` from a `ConfigProvider`. Effect ships convenience constructors for the common
primitives, combinators for grouping and nesting them, and operators for
defaults, transformation, and fallback. Because every `Config` is also an
`Effect`, you consume it by yielding it inside `Effect.gen`.

```ts
import { Config, Effect } from "effect"

const program = Effect.gen(function* () {
  // Read and validate three values from the current ConfigProvider
  const host = yield* Config.string("HOST") // any string
  const port = yield* Config.port("PORT") // integer in 1–65535
  const debug = yield* Config.boolean("DEBUG").pipe(
    // DEBUG is optional — fall back to false when it is missing
    Config.withDefault(false)
  )

  yield* Effect.log(`http://${host}:${port} (debug=${debug})`)
})

// HOST=localhost PORT=8080 DEBUG=yes node app.js
Effect.runFork(program)
```

Each constructor takes the *name* of the key to read. With the default
environment-variable provider, `Config.string("HOST")` reads the `HOST` env var.
If a value is missing or fails validation, the program fails with a typed
`Config.ConfigError` rather than throwing.

## Primitive constructors

Effect provides constructors for the most common value types. Each one decodes
the raw string from the provider and validates it:

| Constructor                  | Reads                                                          |
| ---------------------------- | ------------------------------------------------------------- |
| `Config.string(name)`        | a string                                                      |
| `Config.nonEmptyString(name)`| a string that must contain at least one character            |
| `Config.number(name)`        | a number (allows `NaN`, `Infinity`)                          |
| `Config.finite(name)`        | a finite number (rejects `NaN`, `Infinity`)                  |
| `Config.int(name)`           | an integer (rejects floats)                                   |
| `Config.port(name)`          | an integer in `1`–`65535`                                     |
| `Config.boolean(name)`       | a boolean (`true/false`, `yes/no`, `on/off`, `1/0`, `y/n`)   |
| `Config.literal(value, name)`| exactly the given literal value                              |
| `Config.literals(arr, name)` | one of the given literal values                              |
| `Config.duration(name)`      | a `Duration` (e.g. `"10 seconds"`)                           |
| `Config.url(name)`           | a `URL`                                                       |
| `Config.date(name)`          | a valid `Date`                                                |
| `Config.logLevel(name)`      | a `LogLevel` (`"Info"`, `"Debug"`, …)                        |
| `Config.redacted(name)`      | a secret wrapped in `Redacted` — see [Redacted Secrets](https://effect.plants.sh/configuration/redacted-secrets/) |

```ts
import { Config, Effect } from "effect"

const program = Effect.gen(function* () {
  const timeout = yield* Config.duration("REQUEST_TIMEOUT") // "30 seconds" → Duration
  const level = yield* Config.logLevel("LOG_LEVEL") // "Debug" → LogLevel "Debug"
  const env = yield* Config.literals(
    ["development", "staging", "production"],
    "NODE_ENV"
  )

  yield* Effect.log(`Running in ${env} with timeout ${timeout}`)
})
```

## Providing defaults and making values optional

Not every key is required. `Config.withDefault` supplies a fallback value, and
`Config.option` turns a missing value into `Option.None`.

```ts
import { Config, Effect, Option } from "effect"

const program = Effect.gen(function* () {
  // Use 8080 when PORT is absent
  const port = yield* Config.port("PORT").pipe(Config.withDefault(8080))

  // Option<string>: Some when MAX_CONNECTIONS is set, None otherwise
  const maxConn = yield* Config.option(Config.int("MAX_CONNECTIONS"))

  yield* Effect.log(`port=${port}, maxConn=${Option.getOrElse(maxConn, () => -1)}`)
})
```
**Caution:** `withDefault` and `option` only kick in for **missing data** — an absent key
  or an `undefined` value. A value that is present but *invalid* (e.g.
  `PORT=banana`) still fails. This is intentional: a malformed config should
  surface as an error, not silently fall back to a default.

## Combining configs

`Config.all` combines several configs into one. Pass a record to get a named
struct back, or an array to get a tuple:

```ts
import { Config, Effect } from "effect"

// Combine into a named struct — the parsed result mirrors this shape
const ServerConfig = Config.all({
  host: Config.string("HOST"),
  port: Config.port("PORT")
})

const program = Effect.gen(function* () {
  const server = yield* ServerConfig
  //    ^? { host: string; port: number }
  yield* Effect.log(`${server.host}:${server.port}`)
})
```

## Transforming and validating

`Config.map` post-processes a parsed value with a pure function. When the
transformation itself can fail, use `Config.mapOrFail`, which returns an
`Effect` and can produce a `ConfigError`.

```ts
import { Config } from "effect"

// A reusable Config for a custom domain type
class HostPort {
  constructor(
    readonly host: string,
    readonly port: number
  ) {}
  get url() {
    return `${this.host}:${this.port}`
  }
}

const hostPort = Config.all([Config.string("HOST"), Config.port("PORT")]).pipe(
  // Map the tuple into a domain object once both values are validated
  Config.map(([host, port]) => new HostPort(host, port))
)
```

For richer validation — multiple fields, refinements, branded types — reach for
a `Schema` rather than hand-rolling `mapOrFail`. See the next section.

## Reading structured config with a Schema

`Config.schema` builds a `Config` from any `Schema.Codec`. This is the most
powerful constructor — every primitive constructor above is just a shortcut for
it. Use it to read and validate an entire nested struct in one shot.

```ts
import { Config, Effect, Schema } from "effect"

// Describe the whole config tree with a Schema
const AppConfig = Config.schema(
  Schema.Struct({
    host: Schema.String,
    port: Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 65535 })),
    features: Schema.Struct({
      newDashboard: Schema.Boolean
    })
  }),
  "app" // root path segment: keys live under the "app" namespace
)

const program = Effect.gen(function* () {
  const config = yield* AppConfig
  //    ^? { host: string; port: number; features: { newDashboard: boolean } }
  yield* Effect.log(`${config.host}:${config.port}`)
})
```

Validation failures from the schema (wrong type, out of range, missing key) are
wrapped in a `Config.ConfigError` whose `cause` is a `SchemaError`, so the error
message points at the exact path that failed.

## Nesting configs under a namespace

`Config.nested` scopes a config under a prefix. This keeps related keys grouped
and lets you reuse a config fragment at different paths.

```ts
import { Config, Effect } from "effect"

// Reusable fragment with un-prefixed keys
const connection = Config.all({
  host: Config.string("HOST"),
  port: Config.port("PORT")
})

const program = Effect.gen(function* () {
  // Reads DATABASE_HOST / DATABASE_PORT
  const db = yield* connection.pipe(Config.nested("DATABASE"))
  // Reads REDIS_HOST / REDIS_PORT
  const redis = yield* connection.pipe(Config.nested("REDIS"))

  yield* Effect.log(`db=${db.host}:${db.port} redis=${redis.host}:${redis.port}`)
})
```

With the environment-variable provider, each `nested` segment becomes a
`_`-separated prefix on the env var name. With a JSON provider, it becomes an
extra object level. The mechanics of how a provider resolves these paths are
covered in [Config Providers](https://effect.plants.sh/configuration/config-providers/).

## Handling config errors

A failing `Config` produces a `Config.ConfigError`. Its `cause` is either a
`SourceError` (the provider could not read the data) or a `SchemaError`
(validation failed). You can recover with the usual Effect error operators, or
fall back to another config with `Config.orElse`.

```ts
import { Config, Effect } from "effect"

const program = Effect.gen(function* () {
  // Try PRIMARY_URL first; if it errors for any reason, use the fallback
  const url = yield* Config.string("PRIMARY_URL").pipe(
    Config.orElse(() => Config.string("FALLBACK_URL"))
  )
  yield* Effect.log(url)
})
```
**Note:** `orElse` catches **all** `ConfigError`s, including validation failures —
  unlike `withDefault`, which only handles missing data. Use `withDefault` for
  "this key is optional" and `orElse` for "try another source".

## Constructors reference

Every constructor below takes an optional `name` — the root path segment to read
from the provider. Omit `name` when the config is a fragment of a larger schema
(it will read at the current path). Each primitive is a thin shortcut over
[`Config.schema`](#configschemacodec-path) with a specific codec.

| Constructor                     | Codec                              | Result            |
| ------------------------------- | ---------------------------------- | ----------------- |
| `Config.string(name?)`          | `Schema.String`                    | `string`          |
| `Config.nonEmptyString(name?)`  | `Schema.NonEmptyString`            | `string`          |
| `Config.number(name?)`          | `Schema.Number`                    | `number`          |
| `Config.finite(name?)`          | `Schema.Finite`                    | `number`          |
| `Config.int(name?)`             | `Schema.Int`                       | `number`          |
| `Config.port(name?)`            | `Config.Port`                      | `number`          |
| `Config.boolean(name?)`         | `Config.Boolean`                   | `boolean`         |
| `Config.literal(value, name?)`  | `Schema.Literal(value)`            | the literal       |
| `Config.literals(arr, name?)`   | `Schema.Literals(arr)`             | one of the set    |
| `Config.duration(name?)`        | `Schema.DurationFromString`        | `Duration`        |
| `Config.url(name?)`             | `Schema.URL`                       | `URL`             |
| `Config.date(name?)`            | `Schema.DateValid`                 | `Date`            |
| `Config.logLevel(name?)`        | `Config.LogLevel`                  | `LogLevel`        |
| `Config.redacted(name?)`        | `Schema.Redacted(Schema.String)`   | `Redacted<string>`|
| `Config.schema(codec, path?)`   | any `Schema.Codec`                 | the codec's type  |
| `Config.succeed(value)`         | —                                  | constant          |
| `Config.fail(err)`              | —                                  | always errors     |
| `Config.make(parse)`            | —                                  | custom            |

### Config.string(name?)

Reads a string using `Schema.String`. Any string value is accepted.

```ts
import { Config, ConfigProvider, Effect } from "effect"

const host = Config.string("HOST")

const provider = ConfigProvider.fromUnknown({ HOST: "localhost" })
Effect.runSync(host.parse(provider))
// => "localhost"
```

### Config.nonEmptyString(name?)

Reads a string but rejects the empty string (`Schema.NonEmptyString`).

```ts
import { Config, ConfigProvider, Effect } from "effect"

const name = Config.nonEmptyString("APP_NAME")

Effect.runSync(name.parse(ConfigProvider.fromUnknown({ APP_NAME: "api" })))
// => "api"
Effect.runSync(name.parse(ConfigProvider.fromUnknown({ APP_NAME: "" })))
// => throws ConfigError (empty string rejected)
```

### Config.number(name?)

Reads a number using `Schema.Number`. Allows `NaN` and `Infinity`.

```ts
import { Config, ConfigProvider, Effect } from "effect"

const ratio = Config.number("RATIO")

Effect.runSync(ratio.parse(ConfigProvider.fromUnknown({ RATIO: "1.5" })))
// => 1.5
```

### Config.finite(name?)

Reads a finite number using `Schema.Finite` — rejects `NaN` and `Infinity`.

```ts
import { Config, ConfigProvider, Effect } from "effect"

const factor = Config.finite("FACTOR")

Effect.runSync(factor.parse(ConfigProvider.fromUnknown({ FACTOR: "2.0" })))
// => 2
Effect.runSync(factor.parse(ConfigProvider.fromUnknown({ FACTOR: "Infinity" })))
// => throws ConfigError (not finite)
```

### Config.int(name?)

Reads an integer using `Schema.Int` — rejects floats.

```ts
import { Config, ConfigProvider, Effect } from "effect"

const retries = Config.int("MAX_RETRIES")

Effect.runSync(retries.parse(ConfigProvider.fromUnknown({ MAX_RETRIES: "3" })))
// => 3
Effect.runSync(retries.parse(ConfigProvider.fromUnknown({ MAX_RETRIES: "3.5" })))
// => throws ConfigError (not an integer)
```

### Config.port(name?)

Reads an integer in `1`–`65535` (the [`Config.Port`](#configport) schema).

```ts
import { Config, ConfigProvider, Effect } from "effect"

const port = Config.port("PORT")

Effect.runSync(port.parse(ConfigProvider.fromUnknown({ PORT: "8080" })))
// => 8080
Effect.runSync(port.parse(ConfigProvider.fromUnknown({ PORT: "70000" })))
// => throws ConfigError (out of range)
```

### Config.boolean(name?)

Reads a boolean using the [`Config.Boolean`](#configboolean) schema. Accepted
truthy strings: `true`, `yes`, `on`, `1`, `y`; falsy: `false`, `no`, `off`, `0`,
`n`.

```ts
import { Config, ConfigProvider, Effect } from "effect"

const debug = Config.boolean("DEBUG")

Effect.runSync(debug.parse(ConfigProvider.fromUnknown({ DEBUG: "yes" })))
// => true
Effect.runSync(debug.parse(ConfigProvider.fromUnknown({ DEBUG: "0" })))
// => false
```

### Config.literal(value, name?)

Accepts exactly one literal value (`Schema.Literal`). The literal can be a
string, number, boolean, `null`, or bigint (`AST.LiteralValue`).

```ts
import { Config, ConfigProvider, Effect } from "effect"

const mode = Config.literal("production", "MODE")

Effect.runSync(mode.parse(ConfigProvider.fromUnknown({ MODE: "production" })))
// => "production"
Effect.runSync(mode.parse(ConfigProvider.fromUnknown({ MODE: "dev" })))
// => throws ConfigError
```

### Config.literals(arr, name?)

Accepts one of a fixed set of literal values (`Schema.Literals`).

```ts
import { Config, ConfigProvider, Effect } from "effect"

const env = Config.literals(["development", "production"], "NODE_ENV")

Effect.runSync(env.parse(ConfigProvider.fromUnknown({ NODE_ENV: "production" })))
// => "production"
```

### Config.duration(name?)

Reads a `Duration` from a human-readable string (`Schema.DurationFromString`),
e.g. `"10 seconds"`, `"500 millis"`, `"Infinity"`.

```ts
import { Config, ConfigProvider, Effect } from "effect"

const timeout = Config.duration("TIMEOUT")

Effect.runSync(timeout.parse(ConfigProvider.fromUnknown({ TIMEOUT: "10 seconds" })))
// => Duration { _tag: "Millis", millis: 10000 }
```

### Config.url(name?)

Reads a `URL`, parsing the string with the `URL` constructor (`Schema.URL`).
Fails if the string is not a valid URL.

```ts
import { Config, ConfigProvider, Effect } from "effect"

const apiUrl = Config.url("API_URL")

const url = Effect.runSync(
  apiUrl.parse(ConfigProvider.fromUnknown({ API_URL: "https://example.com" }))
)
url.href
// => "https://example.com/"
```

### Config.date(name?)

Reads a valid `Date` (`Schema.DateValid`). Fails with a `SchemaError` if the
string produces an invalid `Date`.

```ts
import { Config, ConfigProvider, Effect } from "effect"

const createdAt = Config.date("CREATED_AT")

Effect.runSync(createdAt.parse(ConfigProvider.fromUnknown({ CREATED_AT: "2024-01-15" })))
// => Date("2024-01-15T00:00:00.000Z")
```

### Config.logLevel(name?)

Reads an Effect `LogLevel` (the [`Config.LogLevel`](#configloglevel) schema).
Accepted values: `All`, `Fatal`, `Error`, `Warn`, `Info`, `Debug`, `Trace`,
`None`.

```ts
import { Config, ConfigProvider, Effect } from "effect"

const level = Config.logLevel("LOG_LEVEL")

Effect.runSync(level.parse(ConfigProvider.fromUnknown({ LOG_LEVEL: "Info" })))
// => "Info"
```

### Config.redacted(name?)

Reads a secret string wrapped in a `Redacted` container that hides the value
from logs and `toString` (`Schema.Redacted(Schema.String)`). See
[Redacted Secrets](https://effect.plants.sh/configuration/redacted-secrets/) for working with the
result.

```ts
import { Config, ConfigProvider, Effect } from "effect"

const apiKey = Config.redacted("API_KEY")

const secret = Effect.runSync(
  apiKey.parse(ConfigProvider.fromUnknown({ API_KEY: "sk-123" }))
)
console.log(secret)
// => <redacted>
```

### Config.schema(codec, path?)

The most general constructor: builds a `Config<T>` from any `Schema.Codec`.
Every primitive constructor above delegates to it. The optional `path` sets the
root segment(s) — pass a string for a flat key or an array for a nested path.

```ts
import { Config, ConfigProvider, Effect, Schema } from "effect"

// A struct read under the "db" namespace
const DbConfig = Config.schema(
  Schema.Struct({ host: Schema.String, port: Schema.Int }),
  "db"
)

Effect.runSync(
  DbConfig.parse(
    ConfigProvider.fromUnknown({ db: { host: "localhost", port: 5432 } })
  )
)
// => { host: "localhost", port: 5432 }

// A custom nested path with an array
const deep = Config.schema(Schema.String, ["a", "b", "c"])

Effect.runSync(
  deep.parse(ConfigProvider.fromUnknown({ a: { b: { c: "hi" } } }))
)
// => "hi"
```

### Config.succeed(value)

Creates a config that ignores the provider and always succeeds with `value`.
Useful as a constant fallback inside [`Config.orElse`](#configorelseself-that).

```ts
import { Config, ConfigProvider, Effect } from "effect"

const host = Config.string("HOST").pipe(
  Config.orElse(() => Config.succeed("localhost"))
)

Effect.runSync(host.parse(ConfigProvider.fromUnknown({})))
// => "localhost"
```

### Config.fail(err)

Creates a config that always fails with a `ConfigError` wrapping the given
`SourceError` or `SchemaError`. Used inside `orElse` to re-raise a specific
error.

```ts
import { Config } from "effect"

// Inside an orElse handler, re-raise the original cause instead of recovering
const reRaise = (cause: Parameters<typeof Config.fail>[0]) => Config.fail(cause)
```

### Config.make(parse)

Low-level constructor: builds a `Config` from a function that takes a
`ConfigProvider` and returns `Effect<T, ConfigError>`. Every primitive and
combinator is built on it; reach for it only when `schema` and the combinators
cannot express what you need.

```ts
import { Config, ConfigProvider, Effect } from "effect"

const hostPort = Config.make((provider) =>
  Effect.all({
    host: Config.string("host").parse(provider),
    port: Config.number("port").parse(provider)
  })
)

Effect.runSync(hostPort.parse(ConfigProvider.fromUnknown({ host: "localhost", port: 3000 })))
// => { host: "localhost", port: 3000 }
```

## Combinators reference

### Config.map(self, f)

Post-processes the parsed value with a pure function that cannot fail. Available
data-first and data-last (pipeable).

```ts
import { Config, ConfigProvider, Effect } from "effect"

const upper = Config.string("NAME").pipe(Config.map((s) => s.toUpperCase()))

Effect.runSync(upper.parse(ConfigProvider.fromUnknown({ NAME: "alice" })))
// => "ALICE"
```

### Config.mapOrFail(self, f)

Like `map`, but `f` returns `Effect<B, ConfigError>` so the transformation can
fail with a config error.

```ts
import { Config, ConfigProvider, Effect } from "effect"

const trimmed = Config.string("NAME").pipe(
  Config.mapOrFail((s) => Effect.succeed(s.trim()))
)

Effect.runSync(trimmed.parse(ConfigProvider.fromUnknown({ NAME: "  bob  " })))
// => "bob"
```

### Config.orElse(self, that)

Catches **all** `ConfigError`s (including validation errors) and switches to the
fallback config. The `that` callback receives the error.

```ts
import { Config, ConfigProvider, Effect } from "effect"

const url = Config.string("PRIMARY_URL").pipe(
  Config.orElse(() => Config.string("FALLBACK_URL"))
)

Effect.runSync(url.parse(ConfigProvider.fromUnknown({ FALLBACK_URL: "https://b" })))
// => "https://b"
```

### Config.withDefault(self, defaultValue)

Supplies a fallback value, but **only** when the failure is caused by missing
data (a missing key or `undefined`). Validation errors still propagate.

```ts
import { Config, ConfigProvider, Effect } from "effect"

const port = Config.number("PORT").pipe(Config.withDefault(3000))

Effect.runSync(port.parse(ConfigProvider.fromUnknown({})))
// => 3000
Effect.runSync(port.parse(ConfigProvider.fromUnknown({ PORT: "not-a-number" })))
// => throws ConfigError (validation error is NOT defaulted)
```

### Config.option(self)

Returns `Some(value)` on success and `None` when the data is missing. Like
`withDefault`, only missing-data errors produce `None`; validation errors still
propagate.

```ts
import { Config, ConfigProvider, Effect } from "effect"

const maybePort = Config.option(Config.number("PORT"))

Effect.runSync(maybePort.parse(ConfigProvider.fromUnknown({})))
// => { _tag: "None" }
Effect.runSync(maybePort.parse(ConfigProvider.fromUnknown({ PORT: "8080" })))
// => { _tag: "Some", value: 8080 }
```

### Config.all(arg)

Combines multiple configs, mirroring the input shape: a record yields a struct,
an array (or any iterable) yields a tuple/array.

```ts
import { Config, ConfigProvider, Effect } from "effect"

// Record → struct
const asStruct = Config.all({
  host: Config.string("host"),
  port: Config.number("port")
})

Effect.runSync(asStruct.parse(ConfigProvider.fromUnknown({ host: "localhost", port: 5432 })))
// => { host: "localhost", port: 5432 }

// Array → tuple
const asTuple = Config.all([Config.string("host"), Config.number("port")])

Effect.runSync(asTuple.parse(ConfigProvider.fromUnknown({ host: "localhost", port: 5432 })))
// => ["localhost", 5432]
```

### Config.nested(self, name)

Prefixes every key the inner config reads with `name`. With `fromEnv` this is a
`_`-joined prefix; with `fromUnknown`/JSON it is an extra object level. Multiple
`nested` calls compose, outermost first.

```ts
import { Config, ConfigProvider, Effect } from "effect"

const host = Config.string("host").pipe(Config.nested("database"))

// JSON: extra object level
Effect.runSync(host.parse(ConfigProvider.fromUnknown({ database: { host: "localhost" } })))
// => "localhost"

// Env: "_"-joined prefix (reads database_host)
Effect.runSync(host.parse(ConfigProvider.fromEnv({ env: { database_host: "localhost" } })))
// => "localhost"
```

## Schema helpers

These exported schemas back the primitive constructors. Use them directly with
[`Config.schema`](#configschemacodec-path) — for example to read a value at a
custom path, or to embed inside a larger `Schema.Struct`.

### Config.Boolean

Schema decoding a string into a `boolean`. Accepted strings: `true`, `false`,
`yes`, `no`, `on`, `off`, `1`, `0`, `y`, `n`.

```ts
import { Config, ConfigProvider, Effect } from "effect"

const flag = Config.schema(Config.Boolean, "ENABLED")

Effect.runSync(flag.parse(ConfigProvider.fromUnknown({ ENABLED: "on" })))
// => true
```

### Config.Port

Schema for an integer in `1`–`65535` (`Schema.Int` with a between check).

```ts
import { Config, ConfigProvider, Effect } from "effect"

const port = Config.schema(Config.Port, "PORT")

Effect.runSync(port.parse(ConfigProvider.fromUnknown({ PORT: "443" })))
// => 443
```

### Config.LogLevel

Schema of `LogLevel` string literals: `All`, `Fatal`, `Error`, `Warn`, `Info`,
`Debug`, `Trace`, `None`.

```ts
import { Config, ConfigProvider, Effect } from "effect"

const level = Config.schema(Config.LogLevel, "LOG_LEVEL")

Effect.runSync(level.parse(ConfigProvider.fromUnknown({ LOG_LEVEL: "Debug" })))
// => "Debug"
```

### Config.Record(key, value, options?)

Builds a `Schema` (a union of a record and a flat-string parser) for a key-value
map. It accepts either a structured record from the provider or a flat string
like `"k=v,k2=v2"`. Customize with `separator` (default `","`) and
`keyValueSeparator` (default `"="`). Use it via `Config.schema`.

```ts
import { Config, ConfigProvider, Effect, Schema } from "effect"

const attributes = Config.schema(
  Config.Record(Schema.String, Schema.String),
  "OTEL_RESOURCE_ATTRIBUTES"
)

const provider = ConfigProvider.fromEnv({
  env: {
    OTEL_RESOURCE_ATTRIBUTES:
      "service.name=my-service,service.version=1.0.0,custom.attribute=value"
  }
})

Effect.runSync(attributes.parse(provider))
// => {
//   "service.name": "my-service",
//   "service.version": "1.0.0",
//   "custom.attribute": "value"
// }
```

### Config.TrueValues / Config.FalseValues

The literal schemas backing `Config.Boolean`: `Config.TrueValues` is
`["true", "yes", "on", "1", "y"]` and `Config.FalseValues` is
`["false", "no", "off", "0", "n"]`. These are internal-ish but exported for reuse.

```ts
import { Config } from "effect"

Config.TrueValues.literals
// => ["true", "yes", "on", "1", "y"]
Config.FalseValues.literals
// => ["false", "no", "off", "0", "n"]
```

## Wrap and unwrap

`Config.Wrap<A>` recursively replaces the primitives in a structure with
`Config`s, so a caller can pass either a single `Config<A>` or a record of
individual `Config`s. `Config.unwrap` collapses such a value back into a single
`Config<A>`.

```ts
import { Config, ConfigProvider, Effect } from "effect"

interface Options {
  host: string
  port: number
}

// Accept either Config<Options> or { host: Config<string>; port: Config<number> }
const makeConfig = (config: Config.Wrap<Options>): Config.Config<Options> =>
  Config.unwrap(config)

const config = makeConfig({
  host: Config.string("host"),
  port: Config.number("port")
})

Effect.runSync(config.parse(ConfigProvider.fromUnknown({ host: "localhost", port: 5432 })))
// => { host: "localhost", port: 5432 }
```

`Config.Success<T>` extracts the parsed value type from a `Config`:

```ts
import type { Config } from "effect"

type Host = Config.Success<Config.Config<string>>
// => string
```

## Types and guards

### Config.isConfig(u)

Type guard returning `true` when `u` is a `Config`. Used internally by `unwrap`
and when narrowing an unknown value before calling `.parse()`.

```ts
import { Config } from "effect"

Config.isConfig(Config.string("HOST"))
// => true
Config.isConfig("not a config")
// => false
```

### Config.ConfigError

The error class produced by a failing config. Its `cause` is either a
`SourceError` (the provider could not read data) or a `Schema.SchemaError`
(validation failed). Match on `error.cause._tag` to distinguish the two.

```ts
import { Config, ConfigProvider, Effect, Schema } from "effect"

const program = Config.int("PORT").pipe(
  Effect.catch((error: Config.ConfigError) =>
    Effect.succeed(
      Schema.isSchemaError(error.cause) ? "validation failed" : "could not read"
    )
  )
)

Effect.runSync(program.pipe(
  Effect.provideService(ConfigProvider.ConfigProvider, ConfigProvider.fromUnknown({ PORT: "x" }))
))
// => "validation failed"
```

### `Config.Config<T>`

The core interface. A `Config<T>` extends `Effect<T, ConfigError>`, so it can be
yielded directly in `Effect.gen` (resolving the current `ConfigProvider` from
context) or run against a specific provider with `.parse(provider)`. It is also
pipeable, so `config.pipe(Config.map(...))` works as shown throughout this page.