function read< const C extends readonly string[], D extends Partial> & Record, never> = {}, >(variables: C, defaults: D): Record { const missing: string[] = [] const result: Record = {} for (const variable of variables) { let value: string | number | boolean | null | undefined = process.env[variable] || defaults[variable as keyof D] if (value === undefined) { missing.push(variable) } else { if (typeof value === 'string') { if (/^\d+$/.test(value)) { value = Number(value) } else if (['false', 'true', 'null', 'undefined'].includes(value)) { // oxlint-disable-next-line no-eval value = eval(value) } } } result[variable] = value } if (missing.length) { throw new Error(`Missing required env variables: ${missing.join(', ')}`) } return result } export default read( [ 'DOMAIN', 'PROTOCOL', 'HOSTNAME', 'PORT', 'FASTIFY_PORT', 'FASTIFY_HOST', 'LOG_LEVEL', 'LOG_STREAM', 'MAILGUN_API_KEY', 'NODE_ENV', 'PGDATABASE', 'PGHOST', 'PGPASSWORD', 'PGPORT', 'PGUSER', 'REDIS_HOST', 'SESSION_SECRET', ] as const, { MAILGUN_API_KEY: 'invalid key', PGPASSWORD: null, PGPORT: null, PGUSER: null, SESSION_SECRET: 'this is not very secret but it is long enough', }, )