UNPKG

turboenv

Version:

Minimal env var validation for monorepos using Zod

201 lines (199 loc) 5.77 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { EnvError: () => EnvError, initEnv: () => initEnv }); module.exports = __toCommonJS(index_exports); var import_zod = require("zod"); var EnvError = class extends Error { constructor(type, variable, message, cause) { super(message); this.type = type; this.variable = variable; this.cause = cause; this.name = "EnvError"; } }; function initEnv(opts) { const { server, client, strict = true, onError } = opts; const clientSchema = import_zod.z.object(client); const serverSchema = import_zod.z.object(server); const mergedSchema = clientSchema.merge(serverSchema); const isClient = typeof window !== "undefined"; const getDefaultEnv = () => { if (isClient) { try { return {}; } catch { return {}; } } else { return process.env; } }; const buildRuntimeEnv = () => { const defaultEnv = getDefaultEnv(); if (!opts.runtimeEnv) { return defaultEnv; } const merged = { ...defaultEnv }; const relevantKeys = isClient ? Object.keys(client) : [...Object.keys(client), ...Object.keys(server)]; for (const key of relevantKeys) { if (key in opts.runtimeEnv) { const value = opts.runtimeEnv[key]; if (value !== void 0) { merged[key] = value; } } } return merged; }; const rawEnv = buildRuntimeEnv(); for (const key of Object.keys(client)) { if (!key.startsWith("VITE_")) { const error = new EnvError( "invalid", key, `\u274C Client environment variable "${key}" must be prefixed with "VITE_"` ); if (onError) { onError(error); continue; } throw error; } } let parsed; try { if (strict) { parsed = mergedSchema.parse(rawEnv); } else { const result = mergedSchema.safeParse(rawEnv); if (result.success) { parsed = result.data; } else { parsed = {}; for (const [key, schemaValue] of Object.entries({ ...server, ...client })) { if (rawEnv[key] !== void 0) { try { const zodSchema = schemaValue; parsed[key] = zodSchema.parse(rawEnv[key]); } catch (error) { const envError = new EnvError( "invalid", key, `\u274C Invalid value for environment variable "${key}"`, error ); if (onError) { onError(envError); } else { console.warn(envError.message); } } } else { const envError = new EnvError( "missing", key, `\u274C Missing environment variable "${key}"` ); if (onError) { onError(envError); } else { console.warn(envError.message); } } } } } } catch (error) { if (error instanceof import_zod.ZodError) { const issues = error.issues || []; const missingVars = []; const invalidVars = []; for (const issue of issues) { const path = issue.path?.join(".") || "unknown"; if (issue.code === "invalid_type") { const invalidTypeIssue = issue; if (invalidTypeIssue.received === "undefined" || invalidTypeIssue.received === void 0) { missingVars.push(path); } else { invalidVars.push( `${path}: expected ${invalidTypeIssue.expected}, received ${invalidTypeIssue.received}` ); } } else { invalidVars.push(`${path}: ${issue.message}`); } } let message = "\u274C Environment validation failed:\n"; if (missingVars.length > 0) { message += `Missing variables: ${missingVars.join(", ")} `; } if (invalidVars.length > 0) { message += `Invalid variables: ${invalidVars.join(", ")} `; } const envError = new EnvError("invalid", "", message, error); if (onError) { onError(envError); parsed = {}; } else { throw envError; } } else { throw error; } } if (isClient) { for (const key of Object.keys(server)) { Object.defineProperty(parsed, key, { get() { const error = new EnvError( "client_access", key, `\u274C Attempted to access server-only env var "${key}" on the client` ); if (onError) { onError(error); return void 0; } throw error; } }); } } return { ...parsed, client: parsed, server: isClient ? void 0 : parsed }; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { EnvError, initEnv });