typed-env
Version:
Typed environment variable parser
1 lines • 5.65 kB
Source Map (JSON)
{"version":3,"file":"index.mjs","names":["info: T","options?: Options","errors: EnvError[]","config: Record<string, unknown>"],"sources":["../src/index.ts"],"sourcesContent":["interface ArgInfoType {\n type: 'number' | 'string' | 'array' | 'boolean';\n}\n\ninterface ArgInfoParser {\n parser: (input: string) => unknown;\n}\n\ninterface ArgInfoOptional {\n optional?: boolean;\n}\n\ninterface ArgInfoDefault {\n default?: unknown;\n}\n\ninterface ArgInfoChoices {\n choices?: readonly unknown[];\n}\n\ntype ArgInfo = (ArgInfoType | ArgInfoParser) & ArgInfoOptional & ArgInfoDefault & ArgInfoChoices;\n\ninterface MappedArgType<Choice> {\n string: Choice extends (infer U)[] ? U : string;\n number: Choice extends (infer U)[] ? U : number;\n boolean: boolean;\n array: string[];\n}\n\ntype Mutable<T> = { -readonly [P in keyof T]: T[P] };\n\ntype GetArgTypeInner<T extends ArgInfo> = T extends ArgInfoParser\n ? ReturnType<T['parser']>\n : T extends ArgInfoType\n ? MappedArgType<Mutable<T['choices']>>[T['type']]\n : never;\n\ntype GetArgTypeOptional<T extends ArgInfo> = T extends ArgInfoDefault ? T['default'] : undefined;\n\ntype GetArgType<T extends ArgInfo> = T extends ArgInfoOptional\n ? GetArgTypeInner<T> | GetArgTypeOptional<T>\n : GetArgTypeInner<T>;\n\ntype ArgReturnType<T extends Record<string, ArgInfo>> = {\n [K in keyof T]: GetArgType<T[K]>;\n};\n\nexport interface Options {\n env?: Record<string, string> | NodeJS.ProcessEnv;\n}\n\nexport type Config = Record<string, ArgInfo>;\n\ninterface CustomEnvError {\n key: string;\n value: string;\n error: unknown;\n kind: 'custom';\n}\n\ninterface ChoiceEnvError {\n key: string;\n value: string;\n expected: readonly unknown[];\n kind: 'choice';\n}\n\ninterface NumberEnvError {\n key: string;\n value: string;\n kind: 'number';\n}\n\ninterface MissingEnvError {\n key: string;\n kind: 'missing';\n}\n\ntype EnvError = CustomEnvError | ChoiceEnvError | NumberEnvError | MissingEnvError;\n\nexport function createEnv<const T extends Config>(info: T, options?: Options): ArgReturnType<T> {\n const env = options?.env ?? (typeof process !== 'undefined' ? process.env : null);\n\n if (!env) {\n throw new TypeError('No env specified');\n }\n\n const errors: EnvError[] = [];\n const config: Record<string, unknown> = {};\n\n for (const [key, argInfo] of Object.entries(info)) {\n const value = env[key];\n\n if (!value) {\n if ('default' in argInfo) {\n config[key] = argInfo.default;\n } else if (!argInfo.optional) {\n errors.push({ key, kind: 'missing' });\n }\n\n continue;\n }\n\n if ('parser' in argInfo) {\n try {\n config[key] = argInfo.parser(value);\n } catch (error) {\n errors.push({ key, value, kind: 'custom', error });\n }\n\n continue;\n }\n\n if (argInfo.choices && !argInfo.choices.includes(value)) {\n errors.push({ key, value, kind: 'choice', expected: argInfo.choices });\n continue;\n }\n\n switch (argInfo.type) {\n case 'string':\n config[key] = value;\n break;\n case 'number': {\n const numberValue = Number(value);\n if (Number.isNaN(numberValue)) {\n errors.push({ key, value, kind: 'number' });\n continue;\n }\n config[key] = numberValue;\n break;\n }\n case 'boolean':\n config[key] = value.toLowerCase() === 'true' || value === '1';\n break;\n case 'array':\n config[key] = value.split(/, ?/gu);\n break;\n }\n }\n\n if (errors.length !== 0) {\n let message = 'The following environment variables are invalid:\\n';\n\n for (const error of errors) {\n switch (error.kind) {\n case 'missing':\n message += ` ${error.key} (missing)\\n`;\n break;\n case 'number':\n message += ` ${error.key}: ${error.value} (expected number)\\n`;\n break;\n case 'choice':\n message += ` ${error.key}: ${error.value} (expected one of ${error.expected.join(', ')})\\n`;\n break;\n case 'custom':\n message += ` ${error.key}: ${error.value} (${error.error})\\n`;\n break;\n }\n }\n throw new TypeError(message);\n }\n\n return config as ArgReturnType<T>;\n}\n"],"mappings":";AAgFA,SAAgB,UAAkCA,MAASC,SAAqC;CAC9F,MAAM,MAAM,SAAS,eAAe,YAAY,cAAc,QAAQ,MAAM;AAE5E,MAAK,IACH,OAAM,IAAI,UAAU;CAGtB,MAAMC,SAAqB,CAAE;CAC7B,MAAMC,SAAkC,CAAE;AAE1C,MAAK,MAAM,CAAC,KAAK,QAAQ,IAAI,OAAO,QAAQ,KAAK,EAAE;EACjD,MAAM,QAAQ,IAAI;AAElB,OAAK,OAAO;AACV,OAAI,aAAa,QACf,QAAO,OAAO,QAAQ;aACZ,QAAQ,SAClB,QAAO,KAAK;IAAE;IAAK,MAAM;GAAW,EAAC;AAGvC;EACD;AAED,MAAI,YAAY,SAAS;AACvB,OAAI;AACF,WAAO,OAAO,QAAQ,OAAO,MAAM;GACpC,SAAQ,OAAO;AACd,WAAO,KAAK;KAAE;KAAK;KAAO,MAAM;KAAU;IAAO,EAAC;GACnD;AAED;EACD;AAED,MAAI,QAAQ,YAAY,QAAQ,QAAQ,SAAS,MAAM,EAAE;AACvD,UAAO,KAAK;IAAE;IAAK;IAAO,MAAM;IAAU,UAAU,QAAQ;GAAS,EAAC;AACtE;EACD;AAED,UAAQ,QAAQ,MAAhB;GACE,KAAK;AACH,WAAO,OAAO;AACd;GACF,KAAK,UAAU;IACb,MAAM,cAAc,OAAO,MAAM;AACjC,QAAI,OAAO,MAAM,YAAY,EAAE;AAC7B,YAAO,KAAK;MAAE;MAAK;MAAO,MAAM;KAAU,EAAC;AAC3C;IACD;AACD,WAAO,OAAO;AACd;GACD;GACD,KAAK;AACH,WAAO,OAAO,MAAM,aAAa,KAAK,UAAU,UAAU;AAC1D;GACF,KAAK;AACH,WAAO,OAAO,MAAM,MAAM,QAAQ;AAClC;EACH;CACF;AAED,KAAI,OAAO,WAAW,GAAG;EACvB,IAAI,UAAU;AAEd,OAAK,MAAM,SAAS,OAClB,SAAQ,MAAM,MAAd;GACE,KAAK;AACH,gBAAY,IAAI,MAAM,IAAI;AAC1B;GACF,KAAK;AACH,gBAAY,IAAI,MAAM,IAAI,IAAI,MAAM,MAAM;AAC1C;GACF,KAAK;AACH,gBAAY,IAAI,MAAM,IAAI,IAAI,MAAM,MAAM,oBAAoB,MAAM,SAAS,KAAK,KAAK,CAAC;AACxF;GACF,KAAK;AACH,gBAAY,IAAI,MAAM,IAAI,IAAI,MAAM,MAAM,IAAI,MAAM,MAAM;AAC1D;EACH;AAEH,QAAM,IAAI,UAAU;CACrB;AAED,QAAO;AACR"}