diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 72770be..e7618bd 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,5 +1,5 @@ -import type { TypeOf, ZodError, ZodObject, ZodType } from "zod"; -import { object } from "zod"; +import type { TypeOf, ZodError, ZodObject, ZodType, AnyZodObject } from "zod"; +import { object, ZodDefault } from "zod"; export type ErrorMessage = T; export type Simplify = { @@ -212,6 +212,16 @@ export type CreateEnv< > >; +const getDefaults = (schema: Schema) => { + return Object.fromEntries( + Object.entries(schema.shape).map(([key, value]) => { + if (value instanceof ZodDefault) + return [key, value._def.defaultValue()]; + return [key, undefined] + }) + ) +} + export function createEnv< TPrefix extends TPrefixFormat, TServer extends TServerFormat = NonNullable, @@ -232,9 +242,6 @@ export function createEnv< } } - const skip = !!opts.skipValidation; - // biome-ignore lint/suspicious/noExplicitAny: - if (skip) return runtimeEnv as any; const _client = typeof opts.client === "object" ? opts.client : {}; const _server = typeof opts.server === "object" ? opts.server : {}; @@ -247,6 +254,17 @@ export function createEnv< const allClient = client.merge(shared); const allServer = server.merge(shared).merge(client); + + const skip = !!opts.skipValidation; + // biome-ignore lint/suspicious/noExplicitAny: + if (skip) { + return { + ...getDefaults(allServer), + ...runtimeEnv, + } as any; + } + + const parsed = isServer ? allServer.safeParse(runtimeEnv) // on server we can validate all env vars : allClient.safeParse(runtimeEnv); // on client we can only validate the ones that are exposed diff --git a/packages/core/test/smoke.test.ts b/packages/core/test/smoke.test.ts index 6432449..d30d5af 100644 --- a/packages/core/test/smoke.test.ts +++ b/packages/core/test/smoke.test.ts @@ -620,3 +620,25 @@ describe("extending presets", () => { }); }); }); + +describe("skipping validation", () => { + test("returns values when present", () => { + const env = createEnv({ + server: { BAR: z.string().default("bar") }, + runtimeEnv: { BAR: "foo" }, + skipValidation: true, + }); + + expect(env).toMatchObject({ BAR: "foo" }); + }); + + test("returns defaults when values missing", () => { + const env = createEnv({ + server: { BAR: z.string().default("bar") }, + runtimeEnv: {}, + skipValidation: true, + }); + + expect(env).toMatchObject({ BAR: "bar" }); + }); +});