UNPKG

@m-ld/gateway

Version:

m-ld gateway for services

193 lines 15.8 kB
import { join, sep } from 'path'; import { mkdir, readdir, readFile, rm, rmdir, writeFile } from 'fs/promises'; import env_paths from 'env-paths'; import createYargs from 'yargs'; import { hideBin } from 'yargs/helpers'; import * as dotenv from 'dotenv'; // [Development] Pull variables from .env file into process.env dotenv.config(); export class Env { /** * The `appName` parameter drives the default paths determination, according * to the `env-paths` module; and also the default prefix for environment * variables. * * @see https://github.com/sindresorhus/env-paths#api * @see https://yargs.js.org/docs/#api-reference-envprefix */ constructor(appName, envPaths = {}) { this.envPaths = Env.mergeConfig({ env: Env.toEnvVar(appName) }, env_paths(appName), envPaths); } /** @returns name uppercase snake-case */ static toEnvVar(name) { return name // Non-word characters (including hyphens) with underscores .replace(/\W+/g, '_') // Camel case to snake case .replace(/([A-Z])/g, '_$&') .toUpperCase(); } get envPrefix() { return this.envPaths.env ? `${this.envPaths.env}_` : ''; } /** * @returns the config as an object containing environment variables, e.g. { * TIMELD_GATEWAY_KEY1: 'key1', * TIMELD_GATEWAY_NESTED__KEY2: 'key2', * } * @param config * @param filter if non-empty, the keys to include * @param env existing env to add to * @param prefix prefix for new entries */ asEnv(config, filter = [], env = {}, prefix = this.envPrefix) { for (let [key, value] of Object.entries(config)) { if (value != null && value !== '' && (filter.length === 0 || filter.includes(key))) { const envVar = `${prefix}${Env.toEnvVar(key)}`; if (typeof value == 'object') this.asEnv(value, [], env, `${envVar}__`); else env[envVar] = `${value}`; } } return env; } baseYargs(args = hideBin(process.argv)) { // noinspection SpellCheckingInspection return createYargs(args) .updateStrings({ 'Positionals:': 'Details:' }) .parserConfiguration({ 'strip-dashed': true, 'strip-aliased': true }); } async yargs(args = hideBin(process.argv)) { const argv = this.baseYargs(args); return (this.envPaths.env ? argv.env(this.envPaths.env) : argv.env(false)) .config(await this.readConfig()) .option('logLevel', { default: process.env.LOG_LEVEL, type: 'string' }); } /** * Gets the path of a file or directory under an environment key. * Ensures that the parent directory exists. */ async readyPath(key, ...path) { if (path.length === 0) throw new RangeError('Path must contain an entry'); const parentDir = join(this.envPaths[key], ...path.slice(0, -1)); await mkdir(parentDir, { recursive: true }); return join(parentDir, ...path.slice(-1)); } async readConfig() { // Not creating anything here const configFile = join(this.envPaths['config'], 'config.json'); try { return JSON.parse(await readFile(configFile, 'utf8')); } catch (err) { return this.defaultIfNotExists(err, {}); } } /** @returns config parameter, for chaining */ async writeConfig(config) { const path = await this.readyPath('config', 'config.json'); await writeFile(path, JSON.stringify(config, null, ' ')); return config; } /** @returns final config written */ async updateConfig(...configs) { const current = await this.readConfig(); if (!configs.length) return current; return this.writeConfig(Env.mergeConfig(current, configs[0], ...configs.slice(1))); } static isConfigKey(k) { return k !== '_' && k !== '$0'; } /** * @param {*} current * @param {*} override * @param {*} more * @returns {*} */ static mergeConfig(current, override, ...more) { const merged = (function () { if (override === false) { return undefined; } else if (override == null) { return current; } else if (Array.isArray(current) && Array.isArray(override)) { return [...current, ...override]; } else if (typeof current == 'object' && typeof override == 'object') { const merged = {}; for (let key of [...Object.keys(current), ...Object.keys(override)]) { if (!(key in merged) && Env.isConfigKey(key)) merged[key] = Env.mergeConfig(current[key], override[key]); } return merged; } else { return override; } })(); return more.length > 0 ? Env.mergeConfig(merged, more[0], ...more.slice(1)) : merged; } /** * List the sub-paths, having no child directories, under the given key. * @returns leaf sub-paths */ async envDirs(key) { async function* subDirs(dir) { for (let dirEnt of await readdir(dir, { withFileTypes: true })) { if (dirEnt.isDirectory()) { const dirPath = join(dir, dirEnt.name); let anySubDirs = false; for await (let subDir of subDirs(dirPath)) { yield subDir; anySubDirs = true; } if (!anySubDirs) yield dirPath; } } } try { const envPath = this.envPaths[key]; const envDirs = []; for await (let dir of subDirs(envPath)) envDirs.push(dir.slice(envPath.length + 1).split(sep)); return envDirs; } catch (err) { return this.defaultIfNotExists(err, []); } } /** Deletes the given env directory */ async delEnvDir(key, path, { force } = {}) { const dir = join(this.envPaths[key], ...path); if (path.length > 0 && (force || (await readdir(dir)).length === 0)) { if (force) await rm(dir, { recursive: true, force: true }); else await rmdir(dir); // Tidy empty parent dirs await this.delEnvDir(key, path.slice(0, -1)); } } // noinspection JSUnusedGlobalSymbols async delEnvFile(key, path) { // Delete the given path, and then any empty parent folders await rm(join(this.envPaths[key], ...path)); await this.delEnvDir(key, path.slice(0, -1)); } defaultIfNotExists(err, defaultValue) { if (err.code === 'ENOENT') { return defaultValue; } else { throw err; } } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRW52LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2xpYi9FbnYudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDakMsT0FBTyxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLEVBQUUsRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQzdFLE9BQU8sU0FBb0IsTUFBTSxXQUFXLENBQUM7QUFDN0MsT0FBTyxXQUFXLE1BQU0sT0FBTyxDQUFDO0FBQ2hDLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDeEMsT0FBTyxLQUFLLE1BQU0sTUFBTSxRQUFRLENBQUM7QUFFakMsK0RBQStEO0FBQy9ELE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQztBQWNoQixNQUFNLE9BQU8sR0FBRztJQUdkOzs7Ozs7O09BT0c7SUFDSCxZQUFZLE9BQWUsRUFBRSxXQUE4QixFQUFFO1FBQzNELElBQUksQ0FBQyxRQUFRLEdBQUcsR0FBRyxDQUFDLFdBQVcsQ0FDN0IsRUFBRSxHQUFHLEVBQUUsR0FBRyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsRUFBRSxFQUFFLFNBQVMsQ0FBQyxPQUFPLENBQUMsRUFBRSxRQUFRLENBQUMsQ0FBQztJQUNsRSxDQUFDO0lBRUQseUNBQXlDO0lBQ3pDLE1BQU0sQ0FBQyxRQUFRLENBQUMsSUFBWTtRQUMxQixPQUFPLElBQUk7WUFDVCwyREFBMkQ7YUFDMUQsT0FBTyxDQUFDLE1BQU0sRUFBRSxHQUFHLENBQUM7WUFDckIsMkJBQTJCO2FBQzFCLE9BQU8sQ0FBQyxVQUFVLEVBQUUsS0FBSyxDQUFDO2FBQzFCLFdBQVcsRUFBRSxDQUFDO0lBQ25CLENBQUM7SUFFRCxJQUFJLFNBQVM7UUFDWCxPQUFPLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FB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