import { z } from 'zod'; /** * Environment schemas live here so api, collab, and tooling validate their * configuration the same way. Every service calls `parseEnv` once at startup * and crashes with a readable list of problems instead of failing later at * first use. */ export const apiEnvSchema = z.object({ NODE_ENV: z.enum(['development', 'test', 'production']).default('development'), PORT: z.coerce.number().int().min(1).max(65535).default(3000), LOG_LEVEL: z.enum(['fatal', 'error', 'warn', 'info', 'debug', 'trace']).default('info'), /** Version shown in health responses; injected at image build time. */ APP_VERSION: z.string().default('0.0.0-dev'), /** PostgreSQL connection string — required, there is no sensible default. */ DATABASE_URL: z .string() .min(1) .refine((url) => url.startsWith('postgresql://') || url.startsWith('postgres://'), { message: 'must be a postgresql:// connection string', }), /** Set to "false" to skip `prisma migrate deploy` at startup (tests, tooling). */ MIGRATE_ON_START: z .enum(['true', 'false']) .default('true') .transform((value) => value === 'true'), }); export type ApiEnv = z.infer; export function parseEnv( schema: Schema, env: Record, ): z.infer { const result = schema.safeParse(env); if (!result.success) { const problems = result.error.issues .map((issue) => ` - ${issue.path.join('.') || '(root)'}: ${issue.message}`) .join('\n'); throw new Error(`Invalid environment configuration:\n${problems}`); } return result.data; }