UNPKG

@citrineos/base

Version:

The base module for OCPP v2.0.1 including all interfaces. This module is not intended to be used directly, but rather as a dependency for other modules.

141 lines 5.95 kB
"use strict"; // Copyright (c) 2023 S44, LLC // Copyright Contributors to the CitrineOS Project // // SPDX-License-Identifier: Apache 2.0 Object.defineProperty(exports, "__esModule", { value: true }); exports.defineConfig = defineConfig; const zod_1 = require("zod"); const types_1 = require("./types"); const args = typeof process !== 'undefined' && process.argv ? process.argv.slice(2) : []; let dynamicPrefix = 'citrineos_'; for (const arg of args) { if (arg.startsWith('--env-prefix=')) { dynamicPrefix = arg.split('=')[1].toLowerCase(); break; } } const CITRINE_ENV_VAR_PREFIX = dynamicPrefix; /** * Finds a case-insensitive match for a key in an object. * @param obj The object to search. * @param targetKey The target key. * @returns The matching key or undefined. */ function findCaseInsensitiveMatch(obj, targetKey) { const lowerTargetKey = targetKey.toLowerCase(); return Object.keys(obj).find((key) => key.toLowerCase() === lowerTargetKey); } const getZodSchemaKeyMap = (schema) => { var _a; if (schema instanceof zod_1.z.ZodEffects) { return getZodSchemaKeyMap((_a = schema._def) === null || _a === void 0 ? void 0 : _a.schema); } if (schema instanceof zod_1.z.ZodNullable || schema instanceof zod_1.z.ZodOptional) { return getZodSchemaKeyMap(schema.unwrap()); } if (schema instanceof zod_1.z.ZodArray) { return getZodSchemaKeyMap(schema.element); } if (schema instanceof zod_1.z.ZodObject) { const entries = Object.entries(schema.shape); return entries.reduce((acc, [key, value]) => { const nested = getZodSchemaKeyMap(value); if (Object.keys(nested).length > 0) { acc[key] = nested; } else { acc[key.toLowerCase()] = key; } return acc; }, {}); } return {}; }; /** * Merges configuration from environment variables into the default configuration. Allows any to keep it as generic as possible. * @param defaultConfig The default configuration. * @param envVars The environment variables. * @returns The merged configuration. */ function mergeConfigFromEnvVars(defaultConfig, envVars, configKeyMap) { const config = Object.assign({}, defaultConfig); const errors = []; for (const [fullEnvKey, value] of Object.entries(envVars)) { if (!value) { continue; } const lowercaseEnvKey = fullEnvKey.toLowerCase(); if (lowercaseEnvKey.startsWith(CITRINE_ENV_VAR_PREFIX)) { const envKeyWithoutPrefix = lowercaseEnvKey.substring(CITRINE_ENV_VAR_PREFIX.length); const path = envKeyWithoutPrefix.split('_'); let currentConfigPart = config; let currentConfigKeyMap = configKeyMap; let validMapping = true; for (let i = 0; i < path.length - 1; i++) { const part = path[i]; const matchingKey = findCaseInsensitiveMatch(currentConfigKeyMap, part); if (!matchingKey) { errors.push(`Environment variable '${fullEnvKey}' refers to unknown configuration segment '${part}'.`); validMapping = false; break; } if (currentConfigPart[matchingKey] === undefined) { currentConfigPart[matchingKey] = {}; } else if (typeof currentConfigPart[matchingKey] !== 'object' || currentConfigPart[matchingKey] === null) { errors.push(`Environment variable '${fullEnvKey}' refers to configuration segment '${part}', but its current value is not an object.`); validMapping = false; break; } currentConfigPart = currentConfigPart[matchingKey]; currentConfigKeyMap = currentConfigKeyMap[matchingKey]; } if (!validMapping) { continue; } const finalPart = path[path.length - 1]; const keyToUse = currentConfigKeyMap[finalPart.toLowerCase()] || finalPart; try { currentConfigPart[keyToUse] = JSON.parse(value); } catch (_a) { console.debug(`Mapping '${value}' as string for environment variable '${fullEnvKey}'.`); currentConfigPart[keyToUse] = value; } } } if (errors.length > 0) { errors.forEach((err) => console.error(err)); throw new Error(`Configuration errors: ${errors.join('; ')}`); } return config; } /** * Validates the system configuration to ensure required properties are set. * @param finalConfig The final system configuration. * @throws Error if required properties are not set. */ function validateFinalConfig(finalConfig) { if (!finalConfig.data.sequelize.username) { throw new Error('CITRINEOS_DATA_SEQUELIZE_USERNAME must be set if username not provided in config'); } if (!finalConfig.data.sequelize.password) { throw new Error('CITRINEOS_DATA_SEQUELIZE_PASSWORD must be set if password not provided in config'); } } /** * Defines the application configuration by merging input configuration which is defined in a file with environment variables. * Takes environment variables over predefined * @param inputConfig The file defined input configuration. * @returns The final system configuration. * @throws Error if required environment variables are not set or if there are parsing errors. */ function defineConfig(inputConfig) { const configKeyMap = getZodSchemaKeyMap(types_1.systemConfigSchema); const appConfig = mergeConfigFromEnvVars(inputConfig, process.env, configKeyMap); validateFinalConfig(appConfig); return types_1.systemConfigSchema.parse(appConfig); } //# sourceMappingURL=defineConfig.js.map