diff --git a/README.md b/README.md index d047765..0f48604 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Popular options include: ### Type-Safe Validation of Nested Schema ```typescript -import { parseEnv, envvar, EnvaseError } from 'envase'; +import { parseEnv, envvar } from 'envase'; import { z } from 'zod'; const config = parseEnv(process.env, { @@ -49,22 +49,24 @@ const config = parseEnv(process.env, { }, }, db: { - host: envvar('DB_HOST', z.string().min(1)), + host: envvar('DB_HOST', z.string().min(1).default('localhost')), }, - apiKey: envvar('API_KEY', z.string().min(32)), + apiKey: envvar('API_KEY', z.string().min(32).optional()), }); // config.app.listen.port -> number // config.db.host -> string -// config.apiKey -> string +// config.apiKey -> string | undefined ``` ### Environment Detection ```typescript -const config = parseEnv(process.env, {}); -// config.isProduction -> boolean -// config.isDevelopment -> boolean -// config.isTest -> boolean +import { detectNodeEnv } from 'envase'; + +const nodeEnv = detectNodeEnv(process.env); +// nodeEnv.isProduction -> boolean +// nodeEnv.isTest -> boolean +// nodeEnv.isDevelopment -> boolean ``` These flags are inferred from the `NODE_ENV` value (i.e. 'production', 'test', or 'development'). @@ -72,6 +74,9 @@ These flags are inferred from the `NODE_ENV` value (i.e. 'production', 'test', o ### Detailed error reporting ```typescript +import { parseEnv, envvar, EnvaseError } from 'envase'; +import { z } from 'zod'; + try { parseEnv(process.env, { apiKey: envvar('API_KEY', z.string().min(32)), @@ -111,6 +116,9 @@ try { ### Type Inference ```typescript +import { envvar, type InferEnv } from 'envase'; +import { z } from 'zod'; + const envSchema = { apiKey: envvar('API_KEY', z.string().min(32)), db: { @@ -135,8 +143,17 @@ This helps pair the raw env name with the shape you expect it to conform to. `parseEnv(env: Record, envSchema: T)` -Validates envvars against the schema and returns a typed configuration object -along with flags: `isProduction`, `isTest`, `isDevelopment`. +Validates envvars against the schema and returns a typed configuration object. + +### `detectNodeEnv` + +`detectNodeEnv(env: Record)` + +Standalone utility that reads NODE_ENV and returns an object with the following boolean flags: + +- isProduction: true if NODE_ENV === 'production' +- isTest: true if NODE_ENV === 'test' +- isDevelopment: true if NODE_ENV === 'development' ### `EnvaseError` diff --git a/src/core.test.ts b/src/core.test.ts index d187805..deeec02 100644 --- a/src/core.test.ts +++ b/src/core.test.ts @@ -1,9 +1,43 @@ import * as v from 'valibot'; import { describe, expect, it } from 'vitest'; import { z } from 'zod'; -import { envvar, parseEnv } from './core.ts'; +import { detectNodeEnv, envvar, parseEnv } from './core.ts'; describe('core', () => { + describe('detectNodeEnv', () => { + it('returns true for isProduction flag', () => { + const config = detectNodeEnv({ NODE_ENV: 'production' }); + + expect(config.isProduction).toBe(true); + expect(config.isTest).toBe(false); + expect(config.isDevelopment).toBe(false); + }); + + it('returns true for isTest flag', () => { + const config = detectNodeEnv({ NODE_ENV: 'test' }); + + expect(config.isProduction).toBe(false); + expect(config.isTest).toBe(true); + expect(config.isDevelopment).toBe(false); + }); + + it('returns true for isDevelopment flag', () => { + const config = detectNodeEnv({ NODE_ENV: 'development' }); + + expect(config.isProduction).toBe(false); + expect(config.isTest).toBe(false); + expect(config.isDevelopment).toBe(true); + }); + + it('returns all falsy flags if NODE_ENV is missing', () => { + const config = detectNodeEnv({}); + + expect(config.isProduction).toBe(false); + expect(config.isTest).toBe(false); + expect(config.isDevelopment).toBe(false); + }); + }); + describe('envvar', () => { it('creates a tuple with environment variable name and Standard Schema validator', () => { const envvarName = 'API_KEY'; @@ -24,14 +58,6 @@ describe('core', () => { EMPTY: '', }; - it('sets environment flags from NODE_ENV', () => { - const config = parseEnv(mockEnv, {}); - - expect(config.isTest).toBe(true); - expect(config.isProduction).toBe(false); - expect(config.isDevelopment).toBe(false); - }); - describe('using zod', () => { it('parses flat config with Zod validators', () => { const config = parseEnv(mockEnv, { diff --git a/src/core.ts b/src/core.ts index 92813c1..c0af998 100644 --- a/src/core.ts +++ b/src/core.ts @@ -4,9 +4,22 @@ import type { EnvSchema, EnvvarEntry, EnvvarValidationIssue, - ParseEnvOutput, + InferEnv, + NodeEnvInfo, } from './types.ts'; +export const detectNodeEnv = ( + env: Record, +): NodeEnvInfo => { + const nodeEnv = env.NODE_ENV; + + return { + isProduction: nodeEnv === 'production', + isTest: nodeEnv === 'test', + isDevelopment: nodeEnv === 'development', + }; +}; + export const envvar = ( name: string, schema: T, @@ -15,7 +28,7 @@ export const envvar = ( export const parseEnv = ( env: Record, envSchema: T, -): ParseEnvOutput => { +): InferEnv => { const envvarValidationIssues: EnvvarValidationIssue[] = []; // biome-ignore lint/suspicious/noExplicitAny: Explicit 'any' is required due to nature of recursive processing @@ -61,12 +74,5 @@ export const parseEnv = ( throw new EnvaseError(envvarValidationIssues); } - const nodeEnv = env.NODE_ENV; - - return { - ...config, - isProduction: nodeEnv === 'production', - isTest: nodeEnv === 'test', - isDevelopment: nodeEnv === 'development', - }; + return config; }; diff --git a/src/index.ts b/src/index.ts index b4048c6..c7b14fc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,3 @@ -export { envvar, parseEnv } from './core.ts'; +export { detectNodeEnv, envvar, parseEnv } from './core.ts'; export { EnvaseError } from './errors/envase-error.ts'; export type { InferEnv } from './types.ts'; diff --git a/src/types.ts b/src/types.ts index 81c0a6f..c812faf 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,6 +1,12 @@ import type { SimplifyDeep } from 'type-fest'; import type { StandardSchemaV1 } from './standard-schema.ts'; +export type NodeEnvInfo = { + isProduction: boolean; + isTest: boolean; + isDevelopment: boolean; +}; + export type EnvvarEntry = [string, T]; export type EnvSchema = { @@ -17,16 +23,6 @@ type RecursiveInferEnv = { export type InferEnv = SimplifyDeep>; -export type NodeEnvInfo = { - isProduction: boolean; - isTest: boolean; - isDevelopment: boolean; -}; - -export type ParseEnvOutput = SimplifyDeep< - RecursiveInferEnv & NodeEnvInfo ->; - export type EnvvarValidationIssue = { name: string; value?: string;