Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .changeset/config-provider-option-lookup.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
"effect": patch
---

Refine the `ConfigProvider` interface so lookup absence is explicit and path
transformation is provider behavior.
Refine the `ConfigProvider` interface so lookup absence uses `undefined` and
path transformation is provider behavior.

`ConfigProvider.load` and the lookup function accepted by
`ConfigProvider.make` now return `Option<Node>`. Use `Option.none()` when a path
does not exist and `Option.some(node)` when it does.
`ConfigProvider.make` now return `Node | undefined`. Use `undefined` when a path
does not exist and return the `Node` directly when it does.

`ConfigProvider` now exposes `mapInput` as a capability. The exported
`ConfigProvider.mapInput` combinator delegates to it, preserving transformation
Expand Down
5 changes: 5 additions & 0 deletions .changeset/fix-config-or-else-evidence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"effect": patch
---

Preserve provider input evidence when `Config.orElse` recovers a configuration failure.
5 changes: 5 additions & 0 deletions .changeset/refine-config-absence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"effect": patch
---

Refine `Config` loading and absence semantics. `Config.schema` now derives a provider loading policy from the encoded `StringTree` schema, materializes mixed-shape union members independently, and leaves separated scalar parsing to `Config.Array` and `Config.Record`. Schemas whose canonical `StringTree` encoding remains opaque, such as `Schema.Any`, `Schema.Unknown`, or `Schema.Json`, are rejected when the config is constructed; use a concrete shape or `Schema.fromJsonString(Schema.Json)` for scalar JSON. Missing or unavailable representations are decoded as `undefined` before `Config.withDefault` and `Config.option` decide absence. Partially supplied `Config.all` groups are rejected, successful values such as `undefined` and explicitly present empty structures are preserved, and the internal path prefix is removed from the public `Config.parse` signature.
6 changes: 3 additions & 3 deletions migration/annotations/effect__Config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"effect/Config#all":
replacement: "Config.all"
note: "Unchanged; combine an iterable or record of Config values."
note: "Combine an iterable or record of Config values. A wholly absent product can use Config.withDefault or Config.option, while a partially supplied product fails."
"effect/Config#array":
replacement: "Config.schema(Config.Array(valueSchema), path)"
note: "Array parsing is schema-based in v4; rebuild the element Config as a Schema and pass the optional path to Config.schema."
Expand All @@ -15,7 +15,7 @@
note: "Collection parsing is schema-based in v4; use Schema.Chunk when a Chunk result is still required."
"effect/Config#Config":
replacement: "Config.Config"
note: "The model remains and is still a yieldable Effect; it now also exposes parse(provider, pathPrefix?)."
note: "The model remains a yieldable Effect and exposes parse(provider). Compose logical lookup paths with Config.schema(..., path) and Config.nested; parsing no longer accepts a public path prefix."
"effect/Config#Config.IsPlainObject":
replacement: "none"
note: "This private conditional helper is no longer exposed; use Config.Wrap for the public recursive wrapping contract."
Expand Down Expand Up @@ -72,7 +72,7 @@
note: "Unchanged."
"effect/Config#primitive":
replacement: "Config.schema(customSchema, path)"
note: "Custom primitive parsing moved to Schema codecs; express decoding and diagnostics in a Schema, then pass it to Config.schema."
note: "Custom primitive parsing moved to Schema codecs; express decoding and diagnostics in a Schema, then pass it to Config.schema. Its canonical StringTree encoding must expose a concrete shape; opaque encodings such as Schema.Any or Schema.Unknown are not supported."
"effect/Config#redacted":
replacement: "Config.redacted"
note: "The string/path overload remains; replace the v3 Config argument overload with Config.map(config, Redacted.make)."
Expand Down
10 changes: 5 additions & 5 deletions migration/annotations/effect__ConfigError.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@
replacement: "Schema.isSchemaError(error.cause)"
note: "Parsing and validation failures are SchemaError causes; inspect the contained SchemaIssue for finer classification."
"effect/ConfigError#isMissingData":
replacement: "Schema.isSchemaError(error.cause)"
note: "After narrowing to SchemaError, recursively inspect MissingKey, absent InvalidType/InvalidValue, Pointer, and aggregate issues; there is no public one-step guard."
replacement: "none"
note: "Do not infer semantic absence from a SchemaIssue. Use Config.withDefault or Config.option; they distinguish absent provider input from successful undefined, invalid input, and partial products."
"effect/ConfigError#isMissingDataOnly":
replacement: "Config.withDefault / Config.option"
note: "The public classifier was removed; these combinators retain the supported missing-only fallback behavior."
note: "The public classifier was removed. These combinators use provider lookup evidence rather than recursively classifying SchemaIssue values."
"effect/ConfigError#isOr":
replacement: "error.cause.issue._tag === \"AnyOf\""
note: "After narrowing cause with Schema.isSchemaError, inspect the SchemaIssue tag; the old Or node no longer exists."
Expand All @@ -44,8 +44,8 @@
replacement: "none"
note: "The Unsupported variant was removed; report unsupported custom decoding through a SchemaError or source failures through ConfigProvider.SourceError."
"effect/ConfigError#MissingData":
replacement: "new Config.ConfigError(new Schema.SchemaError(new SchemaIssue.MissingKey(...)))"
note: "Missing configuration is represented by Schema issues, commonly MissingKey under one or more Pointer nodes."
replacement: "none"
note: "There is no public missing-data error variant. A required absent config ultimately fails with a SchemaError, while Config.withDefault and Config.option handle semantic absence before it enters the public Effect error channel."
"effect/ConfigError#Options":
replacement: "none"
note: "The shared constructor options type was removed; ConfigProvider.SourceError accepts message and optional cause, while Schema issues have issue-specific constructors."
Expand Down
8 changes: 4 additions & 4 deletions migration/annotations/effect__ConfigProvider.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"effect/ConfigProvider#ConfigProvider":
replacement: "ConfigProvider.ConfigProvider"
note: "The model remains but now exposes `load(path)`, returning `Effect<Option<Node>, SourceError>`, and `mapInput(f)` for provider-owned path transformation. `Option.none()` means the path is missing; `Option.some(node)` means it exists."
note: "The model remains but now exposes `load(path)`, returning `Effect<Node | undefined, SourceError>`, and `mapInput(f)` for provider-owned path transformation. `undefined` means the path is missing; a `Node` means it exists."
"effect/ConfigProvider#ConfigProvider.Flat":
replacement: "ConfigProvider.ConfigProvider"
note: "Flat providers were removed; implement the unified path-based provider with ConfigProvider.make."
Expand Down Expand Up @@ -33,7 +33,7 @@
note: "The constructor remains; pass env and preserveEmptyStrings options. Paths use underscore semantics, while sequence separators belong on Config schemas."
"effect/ConfigProvider#fromFlat":
replacement: "ConfigProvider.make"
note: "Flat providers were unified with ConfigProvider; implement lookup by returning `Option.some(node)` for a found `Value`, `Record`, or `Array` node, or `Option.none()` when missing."
note: "Flat providers were unified with ConfigProvider; return a `Value`, `Record`, or `Array` node for a found path, or `undefined` when missing."
"effect/ConfigProvider#fromJson":
replacement: "ConfigProvider.fromUnknown"
note: "Renamed to reflect support for any in-memory JavaScript value."
Expand All @@ -48,10 +48,10 @@
note: "Transform string path segments explicitly with mapInput."
"effect/ConfigProvider#make":
replacement: "ConfigProvider.make"
note: "The constructor now takes a path lookup returning `Effect<Option<Node>, SourceError>`, rather than a full Config loader and flattened provider. Use `Option.none()` for a missing path and `Option.some(node)` for a found node."
note: "The constructor now takes a path lookup returning `Effect<Node | undefined, SourceError>`, rather than a full Config loader and flattened provider. Return `undefined` for a missing path and a `Node` for a found path."
"effect/ConfigProvider#makeFlat":
replacement: "ConfigProvider.make"
note: "The flat-provider constructor was removed; return `Option.some(node)` for a found `Value`, `Record`, or `Array` node, or `Option.none()` when missing."
note: "The flat-provider constructor was removed; return a `Value`, `Record`, or `Array` node for a found path, or `undefined` when missing."
"effect/ConfigProvider#mapInputPath":
replacement: "ConfigProvider.mapInput"
note: "Renamed and generalized: the callback receives and returns the complete Path, including numeric array indexes."
Expand Down
22 changes: 11 additions & 11 deletions migration/v3-to-v4.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# v3 to v4 Migration Reference

Base: `v3` (`3d390f232bdbc3f0d3d6a2ae3c775084f494b547`)
Base: `3d390f232bdbc3f0d3d6a2ae3c775084f494b547` (`3d390f232bdbc3f0d3d6a2ae3c775084f494b547`)

Head: `main` (`24e0e93dc307dc2c2ae86caacb7289e1dab3c103`)

Expand Down Expand Up @@ -8947,7 +8947,7 @@ Schema.toArbitraryLazy(schema)

### `effect/Config`

- `Config.Config` -> `Config.Config`: The model remains and is still a yieldable Effect; it now also exposes parse(provider, pathPrefix?).
- `Config.Config` -> `Config.Config`: The model remains a yieldable Effect and exposes parse(provider). Compose logical lookup paths with Config.schema(..., path) and Config.nested; parsing no longer accepts a public path prefix.

- `Config.Config.IsPlainObject` -> `none`: This private conditional helper is no longer exposed; use Config.Wrap for the public recursive wrapping contract.

Expand All @@ -8959,7 +8959,7 @@ Schema.toArbitraryLazy(schema)

- `Config.LiteralValue` -> `SchemaAST.LiteralValue`: Use the literal value type shared by v4 Schema constructors.

- `Config.all` -> `Config.all`: Unchanged; combine an iterable or record of Config values.
- `Config.all` -> `Config.all`: Combine an iterable or record of Config values. A wholly absent product can use Config.withDefault or Config.option, while a partially supplied product fails.

- `Config.array` -> `Config.schema(Config.Array(valueSchema), path)`: Array parsing is schema-based in v4; rebuild the element Config as a Schema and pass the optional path to Config.schema.

Expand Down Expand Up @@ -8995,7 +8995,7 @@ Schema.toArbitraryLazy(schema)

- `Config.port` -> `Config.port`: Unchanged.

- `Config.primitive` -> `Config.schema(customSchema, path)`: Custom primitive parsing moved to Schema codecs; express decoding and diagnostics in a Schema, then pass it to Config.schema.
- `Config.primitive` -> `Config.schema(customSchema, path)`: Custom primitive parsing moved to Schema codecs; express decoding and diagnostics in a Schema, then pass it to Config.schema. Its canonical StringTree encoding must expose a concrete shape; opaque encodings such as Schema.Any or Schema.Unknown are not supported.

- `Config.redacted` -> `Config.redacted`: The string/path overload remains; replace the v3 Config argument overload with Config.map(config, Redacted.make).

Expand Down Expand Up @@ -9037,7 +9037,7 @@ Schema.toArbitraryLazy(schema)

- `ConfigError.InvalidData` -> `new Config.ConfigError(new Schema.SchemaError(issue))`: Invalid configuration is now expressed as a SchemaIssue wrapped by SchemaError and Config.ConfigError.

- `ConfigError.MissingData` -> `new Config.ConfigError(new Schema.SchemaError(new SchemaIssue.MissingKey(...)))`: Missing configuration is represented by Schema issues, commonly MissingKey under one or more Pointer nodes.
- `ConfigError.MissingData` -> `none`: There is no public missing-data error variant. A required absent config ultimately fails with a SchemaError, while Config.withDefault and Config.option handle semantic absence before it enters the public Effect error channel.

- `ConfigError.Options` -> `none`: The shared constructor options type was removed; ConfigProvider.SourceError accepts message and optional cause, while Schema issues have issue-specific constructors.

Expand All @@ -9053,9 +9053,9 @@ Schema.toArbitraryLazy(schema)

- `ConfigError.isInvalidData` -> `Schema.isSchemaError(error.cause)`: Parsing and validation failures are SchemaError causes; inspect the contained SchemaIssue for finer classification.

- `ConfigError.isMissingData` -> `Schema.isSchemaError(error.cause)`: After narrowing to SchemaError, recursively inspect MissingKey, absent InvalidType/InvalidValue, Pointer, and aggregate issues; there is no public one-step guard.
- `ConfigError.isMissingData` -> `none`: Do not infer semantic absence from a SchemaIssue. Use Config.withDefault or Config.option; they distinguish absent provider input from successful undefined, invalid input, and partial products.

- `ConfigError.isMissingDataOnly` -> `Config.withDefault / Config.option`: The public classifier was removed; these combinators retain the supported missing-only fallback behavior.
- `ConfigError.isMissingDataOnly` -> `Config.withDefault / Config.option`: The public classifier was removed. These combinators use provider lookup evidence rather than recursively classifying SchemaIssue values.

- `ConfigError.isOr` -> `error.cause.issue._tag === "AnyOf"`: After narrowing cause with Schema.isSchemaError, inspect the SchemaIssue tag; the old Or node no longer exists.

Expand All @@ -9069,7 +9069,7 @@ Schema.toArbitraryLazy(schema)

### `effect/ConfigProvider`

- `ConfigProvider.ConfigProvider` -> `ConfigProvider.ConfigProvider`: The model remains but now exposes `load(path)`, returning `Effect<Option<Node>, SourceError>`, and `mapInput(f)` for provider-owned path transformation. `Option.none()` means the path is missing; `Option.some(node)` means it exists.
- `ConfigProvider.ConfigProvider` -> `ConfigProvider.ConfigProvider`: The model remains but now exposes `load(path)`, returning `Effect<Node | undefined, SourceError>`, and `mapInput(f)` for provider-owned path transformation. `undefined` means the path is missing; a `Node` means it exists.

- `ConfigProvider.ConfigProvider.Flat` -> `ConfigProvider.ConfigProvider`: Flat providers were removed; implement the unified path-based provider with ConfigProvider.make.

Expand All @@ -9091,7 +9091,7 @@ Schema.toArbitraryLazy(schema)

- `ConfigProvider.fromEnv` -> `ConfigProvider.fromEnv`: The constructor remains; pass env and preserveEmptyStrings options. Paths use underscore semantics, while sequence separators belong on Config schemas.

- `ConfigProvider.fromFlat` -> `ConfigProvider.make`: Flat providers were unified with ConfigProvider; implement lookup by returning `Option.some(node)` for a found `Value`, `Record`, or `Array` node, or `Option.none()` when missing.
- `ConfigProvider.fromFlat` -> `ConfigProvider.make`: Flat providers were unified with ConfigProvider; return a `Value`, `Record`, or `Array` node for a found path, or `undefined` when missing.

- `ConfigProvider.fromJson` -> `ConfigProvider.fromUnknown`: Renamed to reflect support for any in-memory JavaScript value.

Expand All @@ -9101,9 +9101,9 @@ Schema.toArbitraryLazy(schema)

- `ConfigProvider.lowerCase` -> `ConfigProvider.mapInput((path) => path.map((part) => typeof part === "string" ? part.toLowerCase() : part))`: Transform string path segments explicitly with mapInput.

- `ConfigProvider.make` -> `ConfigProvider.make`: The constructor now takes a path lookup returning `Effect<Option<Node>, SourceError>`, rather than a full Config loader and flattened provider. Use `Option.none()` for a missing path and `Option.some(node)` for a found node.
- `ConfigProvider.make` -> `ConfigProvider.make`: The constructor now takes a path lookup returning `Effect<Node | undefined, SourceError>`, rather than a full Config loader and flattened provider. Return `undefined` for a missing path and a `Node` for a found path.

- `ConfigProvider.makeFlat` -> `ConfigProvider.make`: The flat-provider constructor was removed; return `Option.some(node)` for a found `Value`, `Record`, or `Array` node, or `Option.none()` when missing.
- `ConfigProvider.makeFlat` -> `ConfigProvider.make`: The flat-provider constructor was removed; return a `Value`, `Record`, or `Array` node for a found path, or `undefined` when missing.

- `ConfigProvider.mapInputPath` -> `ConfigProvider.mapInput`: Renamed and generalized: the callback receives and returns the complete Path, including numeric array indexes.

Expand Down
Loading
Loading