@athenna/config
Advanced tools
Comparing version 4.1.0 to 4.2.0
{ | ||
"name": "@athenna/config", | ||
"version": "4.1.0", | ||
"version": "4.2.0", | ||
"description": "Cache and handle environment variables and config files of Athenna.", | ||
@@ -22,3 +22,3 @@ "license": "MIT", | ||
"test": "npm run --silent lint:fix && sh node bin/test.ts", | ||
"test:debug": "cross-env DEBUG=api:* sh node --inspect bin/test.ts", | ||
"test:debug": "cross-env NODE_DEBUG=athenna:* sh node --inspect bin/test.ts", | ||
"test:coverage": "c8 npm run --silent test" | ||
@@ -47,2 +47,3 @@ }, | ||
"#src/types": "./src/types/index.js", | ||
"#src/debug": "./src/debug/index.js", | ||
"#tests/*": "./tests/*.js", | ||
@@ -52,9 +53,9 @@ "#tests": "./tests/index.js" | ||
"dependencies": { | ||
"dotenv": "^16.1.0", | ||
"magicast": "^0.2.2", | ||
"dotenv": "^16.3.1", | ||
"magicast": "^0.2.9", | ||
"syntax-error": "^1.4.0" | ||
}, | ||
"devDependencies": { | ||
"@athenna/common": "^4.1.0", | ||
"@athenna/test": "^4.1.0", | ||
"@athenna/common": "^4.2.0", | ||
"@athenna/test": "^4.2.0", | ||
"@types/lodash": "^4.14.191", | ||
@@ -64,4 +65,4 @@ "@types/syntax-error": "^1.4.2", | ||
"@typescript-eslint/parser": "^5.56.0", | ||
"c8": "^7.12.0", | ||
"commitizen": "^4.2.6", | ||
"c8": "^8.0.0", | ||
"commitizen": "^4.3.0", | ||
"cross-env": "^7.0.3", | ||
@@ -68,0 +69,0 @@ "cz-conventional-changelog": "^3.3.0", |
@@ -21,3 +21,3 @@ /** | ||
/** | ||
* Clear all the configurations of configs object. | ||
* Clear all the configurations of config object. | ||
*/ | ||
@@ -34,15 +34,15 @@ static clear(): typeof Config; | ||
/** | ||
* Verify if configuration key exists. | ||
* Verify if a configuration key exists. | ||
*/ | ||
static exists(key: string): boolean; | ||
/** | ||
* Verify if configuration key does not exist. | ||
* Verify if a configuration key does not exist. | ||
*/ | ||
static notExists(key: string): boolean; | ||
/** | ||
* Verify if configuration keys exists. | ||
* Verify if configuration keys exist. | ||
*/ | ||
static existsAll(...keys: any[]): boolean; | ||
/** | ||
* Verify if configuration keys not exists. | ||
* Verify if configuration keys not exist. | ||
*/ | ||
@@ -55,3 +55,3 @@ static notExistsAll(...keys: any[]): boolean; | ||
/** | ||
* Set a value in the configuration key if value is not defined. | ||
* Set a value in the configuration key if the value is not defined. | ||
*/ | ||
@@ -61,3 +61,3 @@ static safeSet(key: string, value: any | any[]): typeof Config; | ||
* Push a value to a configuration key that is a valid array. | ||
* If configuration is not an array, an exception will be thrown. | ||
* If the configuration is not an array, an exception will be thrown. | ||
*/ | ||
@@ -64,0 +64,0 @@ static push(key: string, value: any | any[]): typeof Config; |
@@ -10,2 +10,3 @@ /** | ||
import { File, Json, Path, ObjectBuilder, Exec, Module, Is, } from '@athenna/common'; | ||
import { debug } from '#src/debug'; | ||
import { loadFile, writeFile } from 'magicast'; | ||
@@ -35,3 +36,3 @@ import { sep, parse, extname } from 'node:path'; | ||
/** | ||
* Clear all the configurations of configs object. | ||
* Clear all the configurations of config object. | ||
*/ | ||
@@ -59,3 +60,3 @@ static clear() { | ||
/** | ||
* Verify if configuration key exists. | ||
* Verify if a configuration key exists. | ||
*/ | ||
@@ -66,3 +67,3 @@ static exists(key) { | ||
/** | ||
* Verify if configuration key does not exist. | ||
* Verify if a configuration key does not exist. | ||
*/ | ||
@@ -73,3 +74,3 @@ static notExists(key) { | ||
/** | ||
* Verify if configuration keys exists. | ||
* Verify if configuration keys exist. | ||
*/ | ||
@@ -80,3 +81,3 @@ static existsAll(...keys) { | ||
/** | ||
* Verify if configuration keys not exists. | ||
* Verify if configuration keys not exist. | ||
*/ | ||
@@ -94,3 +95,3 @@ static notExistsAll(...keys) { | ||
/** | ||
* Set a value in the configuration key if value is not defined. | ||
* Set a value in the configuration key if the value is not defined. | ||
*/ | ||
@@ -106,3 +107,3 @@ static safeSet(key, value) { | ||
* Push a value to a configuration key that is a valid array. | ||
* If configuration is not an array, an exception will be thrown. | ||
* If the configuration is not an array, an exception will be thrown. | ||
*/ | ||
@@ -170,3 +171,3 @@ static push(key, value) { | ||
if (extname(path)) { | ||
safe ? this.safeLoad(path) : this.load(path); | ||
safe ? await this.safeLoad(path) : await this.load(path); | ||
return; | ||
@@ -197,2 +198,3 @@ } | ||
if (!(await File.exists(path))) { | ||
debug('Configuration file path %s does not exist, and will be skipped.', path); | ||
return; | ||
@@ -205,5 +207,7 @@ } | ||
if (base.includes('.js.map') || base.includes('.d.ts')) { | ||
debug('Configuration file %s being skipped since its extension is not valid.', base); | ||
return; | ||
} | ||
if (base.includes('.ts') && Env('IS_TS') === false) { | ||
debug('Configuration file %s being skipped since its a TypeScript file and the application is not running in a TypeScript environment.', base); | ||
return; | ||
@@ -220,2 +224,3 @@ } | ||
const filePath = `${dir}/${fileBase}`; | ||
debug('Nested configuration found, loading file %s first.', fileBase); | ||
await this.safeLoad(filePath, callNumber + 1); | ||
@@ -236,6 +241,7 @@ } | ||
/** | ||
* Add random number to file import path so | ||
* Add random number to file an import path so | ||
* Node.js will not cache the imported file. | ||
*/ | ||
file.href = `${file.href}?version=${Math.random()}`; | ||
debug('Loading configuration file %s with configuration key as %s.', file.href, configKey); | ||
this.configs.set(configKey, await file.import()); | ||
@@ -242,0 +248,0 @@ this.paths.set(configKey, file.path); |
@@ -19,7 +19,7 @@ /** | ||
* Cast the environment variable if it values matches a | ||
* number string, boolean string or json string. | ||
* number string, boolean string, or json string. | ||
*/ | ||
static castEnv(environment: string): any; | ||
/** | ||
* Resolve the env file according to NODE_ENV | ||
* Resolve the env file according to APP_ENV or NODE_ENV | ||
* environment variable. | ||
@@ -42,7 +42,11 @@ */ | ||
/** | ||
* Get the NODE_ENV variable from process.env or from the | ||
* .env file if exists in project root. | ||
* Get the APP_ENV or NODE_ENV variable from process.env or | ||
* from the .env file if exists in project root. | ||
*/ | ||
static getNodeEnv(lookupNodeEnv: boolean): string; | ||
static getAppEnv(lookupNodeEnv: boolean): string; | ||
/** | ||
* Get the env value from .env file. | ||
*/ | ||
static getEnvFromString(envName: string, content: string): string; | ||
/** | ||
* Verify if some environment is defined ignoring. | ||
@@ -49,0 +53,0 @@ */ |
@@ -11,2 +11,3 @@ /** | ||
import { Env } from '#src'; | ||
import { debug } from '#src/debug'; | ||
import { File, Is, Path } from '@athenna/common'; | ||
@@ -41,3 +42,3 @@ export class EnvHelper { | ||
* Cast the environment variable if it values matches a | ||
* number string, boolean string or json string. | ||
* number string, boolean string, or json string. | ||
*/ | ||
@@ -57,7 +58,7 @@ static castEnv(environment) { | ||
/** | ||
* Resolve the env file according to NODE_ENV | ||
* Resolve the env file according to APP_ENV or NODE_ENV | ||
* environment variable. | ||
*/ | ||
static resolveFile(lookupNodeEnv = false) { | ||
const environment = this.getNodeEnv(lookupNodeEnv); | ||
const environment = this.getAppEnv(lookupNodeEnv); | ||
const configurations = { | ||
@@ -70,2 +71,6 @@ path: Path.pwd('.env'), | ||
} | ||
if (configurations.override) { | ||
debug('Environment variables override is enabled.'); | ||
} | ||
debug('Loading env file %s path.', configurations.path); | ||
dotenv.config(configurations); | ||
@@ -93,39 +98,53 @@ } | ||
/** | ||
* Get the NODE_ENV variable from process.env or from the | ||
* .env file if exists in project root. | ||
* Get the APP_ENV or NODE_ENV variable from process.env or | ||
* from the .env file if exists in project root. | ||
*/ | ||
static getNodeEnv(lookupNodeEnv) { | ||
if (this.isDefinedEnv(process.env.NODE_ENV)) { | ||
return process.env.NODE_ENV; | ||
static getAppEnv(lookupNodeEnv) { | ||
const appEnv = process.env.APP_ENV || process.env.NODE_ENV; | ||
if (this.isDefinedEnv(appEnv)) { | ||
debug('Application environment defined by %s env variable.', process.env.APP_ENV ? 'APP_ENV' : 'NODE_ENV'); | ||
return appEnv; | ||
} | ||
if (!lookupNodeEnv) { | ||
debug('Lookup of APP_ENV/NODE_ENV env variables are disabled, skipping it.'); | ||
return null; | ||
} | ||
const file = new File(Path.pwd('.env'), ''); | ||
if (!file.fileExists) { | ||
if (!File.existsSync(Path.pwd('.env'))) { | ||
debug('Unable to found env file at application root: %s. Skipping APP_ENV/NODE_ENV lookup.', Path.pwd('.env')); | ||
return null; | ||
} | ||
const content = file.getContentAsStringSync(); | ||
if (content && content.includes('NODE_ENV=')) { | ||
let value = content.split('NODE_ENV=')[1]; | ||
if (value.includes('\n')) { | ||
value = value.split('\n')[0]; | ||
} | ||
const serializedValue = value | ||
.replace(/'/g, '') | ||
.replace(/"/g, '') | ||
.replace(/\r/g, ''); | ||
if (this.isDefinedEnv(serializedValue)) { | ||
const file = new File(Path.pwd(`.env.${serializedValue}`), ''); | ||
if (!file.fileExists) { | ||
return null; | ||
} | ||
process.env.NODE_ENV = serializedValue; | ||
return process.env.NODE_ENV; | ||
} | ||
const content = new File(Path.pwd('.env')).getContentAsStringSync(); | ||
if (!content) { | ||
debug('File %s content is empty, Skipping APP_ENV/NODE_ENV lookup.', Path.pwd('.env')); | ||
return null; | ||
} | ||
if (content.includes('APP_ENV')) { | ||
return this.getEnvFromString('APP_ENV', content); | ||
} | ||
if (content.includes('NODE_ENV')) { | ||
return this.getEnvFromString('NODE_ENV', content); | ||
} | ||
return null; | ||
} | ||
/** | ||
* Get the env value from .env file. | ||
*/ | ||
static getEnvFromString(envName, content) { | ||
let value = content.split(`${envName}=`)[1]; | ||
if (value.includes('\n')) { | ||
value = value.split('\n')[0]; | ||
} | ||
const serializedValue = value | ||
.replace(/'/g, '') | ||
.replace(/"/g, '') | ||
.replace(/\r/g, ''); | ||
if (this.isDefinedEnv(serializedValue) && | ||
File.existsSync(Path.pwd(`.env.${serializedValue}`))) { | ||
process.env[envName] = serializedValue; | ||
debug('Found %s env variable with value %s inside %s file, returning it as environment value.', envName, serializedValue, Path.pwd(`.env.${serializedValue}`)); | ||
return process.env[envName]; | ||
} | ||
return null; | ||
} | ||
/** | ||
* Verify if some environment is defined ignoring. | ||
@@ -132,0 +151,0 @@ */ |
@@ -19,3 +19,3 @@ /** | ||
* Set or subscribe a KEY:VALUE property in some property of the RC configuration file. | ||
* You can also pass any value as second parameter to set multiple properties at once. | ||
* You can also pass any value as a second parameter to set multiple properties at once. | ||
* | ||
@@ -51,5 +51,6 @@ * @example | ||
/** | ||
* Verify if file is a module or not. | ||
* Verify if the file is a module or not. | ||
* TODO: move to @athenna/common. | ||
*/ | ||
private static isModule; | ||
} |
@@ -11,2 +11,3 @@ /** | ||
import { File, ObjectBuilder } from '@athenna/common'; | ||
import { debug } from '#src/debug/index'; | ||
export class Rc { | ||
@@ -23,2 +24,3 @@ /** | ||
this.file = await new File(path).load(); | ||
debug('RC file loaded: %s', path); | ||
const json = this.file.getContentAsJsonSync(); | ||
@@ -36,2 +38,3 @@ if (json === null) { | ||
if (this.file.base === 'package.json') { | ||
debug('Using the "athenna" property of package.json file as RC content.'); | ||
this.content.set(json.athenna); | ||
@@ -46,3 +49,3 @@ } | ||
* Set or subscribe a KEY:VALUE property in some property of the RC configuration file. | ||
* You can also pass any value as second parameter to set multiple properties at once. | ||
* You can also pass any value as a second parameter to set multiple properties at once. | ||
* | ||
@@ -102,4 +105,3 @@ * @example | ||
} | ||
let athennaRcJson = await this.file.getContentAsJson(); | ||
athennaRcJson = this.content.get(); | ||
const athennaRcJson = this.content.get(); | ||
await this.file.setContent(this.toStringJson(athennaRcJson)); | ||
@@ -114,3 +116,4 @@ } | ||
/** | ||
* Verify if file is a module or not. | ||
* Verify if the file is a module or not. | ||
* TODO: move to @athenna/common. | ||
*/ | ||
@@ -117,0 +120,0 @@ static isModule() { |
@@ -13,4 +13,4 @@ /** | ||
export * from '#src/config/Config'; | ||
export * from '#src/decorators/Value'; | ||
export * from '#src/annotations/Value'; | ||
export * from '#src/helpers/Rc'; | ||
export * from '#src/helpers/EnvHelper'; |
@@ -13,4 +13,4 @@ /** | ||
export * from '#src/config/Config'; | ||
export * from '#src/decorators/Value'; | ||
export * from '#src/annotations/Value'; | ||
export * from '#src/helpers/Rc'; | ||
export * from '#src/helpers/EnvHelper'; |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
42089
31
1059
8
Updateddotenv@^16.3.1
Updatedmagicast@^0.2.9