From 555a5caaaff4f50d2badb01c9dee94b40b7c9f3b Mon Sep 17 00:00:00 2001 From: MarkXian Date: Fri, 24 Jul 2026 17:31:04 +0800 Subject: [PATCH 1/3] fix: guard import.meta env lookup --- packages/effect/src/ConfigProvider.ts | 10 +++++++++- packages/effect/test/ConfigProvider.test.ts | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/packages/effect/src/ConfigProvider.ts b/packages/effect/src/ConfigProvider.ts index f9585b20842..7c47bec90a5 100644 --- a/packages/effect/src/ConfigProvider.ts +++ b/packages/effect/src/ConfigProvider.ts @@ -851,7 +851,7 @@ export function fromEnv(options?: { }): ConfigProvider { const env: Record = options?.env ?? { ...globalThis?.process?.env, - ...(import.meta as any)?.env + ...getImportMetaEnv() } const preserveEmptyStrings = options?.preserveEmptyStrings === true const trie = buildEnvTrie(env) @@ -859,6 +859,14 @@ export function fromEnv(options?: { return make((path) => Effect.succeed(nodeAtEnv(trie, env, path, preserveEmptyStrings))) } +function getImportMetaEnv(): Record | undefined { + try { + return (import.meta as any)?.env + } catch { + return undefined + } +} + type EnvTrieNode = { children?: Record } diff --git a/packages/effect/test/ConfigProvider.test.ts b/packages/effect/test/ConfigProvider.test.ts index ca4309183b5..16775f3efce 100644 --- a/packages/effect/test/ConfigProvider.test.ts +++ b/packages/effect/test/ConfigProvider.test.ts @@ -191,6 +191,22 @@ describe("ConfigProvider", () => { }) describe("fromEnv", () => { + it("uses the default environment when no env is provided", async () => { + const key = "EFFECT_CONFIG_PROVIDER_TEST_DEFAULT_ENV" + const previous = process.env[key] + process.env[key] = "value1" + try { + const provider = ConfigProvider.fromEnv() + await assertSuccess(provider, [key], ConfigProvider.makeValue("value1")) + } finally { + if (previous === undefined) { + delete process.env[key] + } else { + process.env[key] = previous + } + } + }) + it("env without an underscore", async () => { const env = { A: "value1" } const provider = ConfigProvider.fromEnv({ env }) From d85872d5c4beb5e792965201c3e82ceb7716acbe Mon Sep 17 00:00:00 2001 From: MarkXian Date: Fri, 24 Jul 2026 20:57:12 +0800 Subject: [PATCH 2/3] fix: remove import.meta from default env provider --- packages/effect/src/ConfigProvider.ts | 15 +++---------- packages/effect/test/ConfigProvider.test.ts | 25 +++++++++++++++++++++ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/packages/effect/src/ConfigProvider.ts b/packages/effect/src/ConfigProvider.ts index 7c47bec90a5..0b9e9bbe735 100644 --- a/packages/effect/src/ConfigProvider.ts +++ b/packages/effect/src/ConfigProvider.ts @@ -810,8 +810,8 @@ function emptyStringAsMissing(value: string | undefined, preserveEmptyStrings: b * purely numeric names, the node is reported as an `Array`; otherwise as a * `Record`. * - * The default environment merges `process.env` and `import.meta.env` (when - * available). Override by passing `{ env: { ... } }`. + * The default environment reads `process.env` when available. For runtimes that + * expose environment variables elsewhere, pass `{ env: { ... } }`. * * Literal empty strings are treated as missing values when loaded as values by * default. Pass `{ preserveEmptyStrings: true }` to keep empty strings as @@ -850,8 +850,7 @@ export function fromEnv(options?: { readonly preserveEmptyStrings?: boolean | undefined }): ConfigProvider { const env: Record = options?.env ?? { - ...globalThis?.process?.env, - ...getImportMetaEnv() + ...globalThis?.process?.env } const preserveEmptyStrings = options?.preserveEmptyStrings === true const trie = buildEnvTrie(env) @@ -859,14 +858,6 @@ export function fromEnv(options?: { return make((path) => Effect.succeed(nodeAtEnv(trie, env, path, preserveEmptyStrings))) } -function getImportMetaEnv(): Record | undefined { - try { - return (import.meta as any)?.env - } catch { - return undefined - } -} - type EnvTrieNode = { children?: Record } diff --git a/packages/effect/test/ConfigProvider.test.ts b/packages/effect/test/ConfigProvider.test.ts index 16775f3efce..df19e011b8b 100644 --- a/packages/effect/test/ConfigProvider.test.ts +++ b/packages/effect/test/ConfigProvider.test.ts @@ -1,6 +1,7 @@ import { describe, it } from "@effect/vitest" import { deepStrictEqual } from "@effect/vitest/utils" import { ConfigProvider, Effect, FileSystem, Layer, Path, PlatformError, Result } from "effect" +import * as Fs from "node:fs" async function assertSuccess( provider: ConfigProvider.ConfigProvider, @@ -207,6 +208,30 @@ describe("ConfigProvider", () => { } }) + it("uses an explicit env over the default environment", async () => { + const key = "EFFECT_CONFIG_PROVIDER_TEST_DEFAULT_ENV" + const previous = process.env[key] + process.env[key] = "default" + try { + const provider = ConfigProvider.fromEnv({ env: { [key]: "explicit" } }) + await assertSuccess(provider, [key], ConfigProvider.makeValue("explicit")) + } finally { + if (previous === undefined) { + delete process.env[key] + } else { + process.env[key] = previous + } + } + }) + + it("does not reference import.meta in the common ConfigProvider module", () => { + const sourcePath = Fs.existsSync("src/ConfigProvider.ts") + ? "src/ConfigProvider.ts" + : "packages/effect/src/ConfigProvider.ts" + const source = Fs.readFileSync(sourcePath, "utf8") + deepStrictEqual(source.includes("import.meta"), false) + }) + it("env without an underscore", async () => { const env = { A: "value1" } const provider = ConfigProvider.fromEnv({ env }) From 94ce1567f5750cae73079ceb97bbf3920310e03a Mon Sep 17 00:00:00 2001 From: MarkXian Date: Fri, 24 Jul 2026 21:05:16 +0800 Subject: [PATCH 3/3] chore: add config provider changeset --- .changeset/configprovider-import-meta-env.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/configprovider-import-meta-env.md diff --git a/.changeset/configprovider-import-meta-env.md b/.changeset/configprovider-import-meta-env.md new file mode 100644 index 00000000000..bd02b360dbb --- /dev/null +++ b/.changeset/configprovider-import-meta-env.md @@ -0,0 +1,5 @@ +--- +"effect": patch +--- + +Remove the default `import.meta.env` lookup from `ConfigProvider.fromEnv`, fixing module analysis failures in runtimes that do not support `import.meta`, closes #6358.