dynamic-config-store
Advanced tools
Comparing version 1.0.3 to 1.0.4
@@ -0,20 +1,44 @@ | ||
interface IObject { | ||
[prop: string]: any; | ||
} | ||
export declare enum ETypeOfEnvLink { | ||
STRING = "STRING", | ||
NUMBER = "NUMBER", | ||
JSON_STRING = "JSON_STRING" | ||
JSON_STRING = "JSON_STRING", | ||
FUNCTION = "FUNCTION" | ||
} | ||
interface IEnvironmentLink { | ||
export declare type TEnvironmentLink = { | ||
env: string; | ||
type: ETypeOfEnvLink; | ||
type: ETypeOfEnvLink.JSON_STRING; | ||
func?: null; | ||
required?: boolean; | ||
defaultValue?: any; | ||
} | ||
} | { | ||
env: string; | ||
type: ETypeOfEnvLink.FUNCTION; | ||
func: (value: any) => any; | ||
required?: boolean; | ||
defaultValue?: any; | ||
} | { | ||
env: string; | ||
type: ETypeOfEnvLink.NUMBER; | ||
func?: null; | ||
required?: boolean; | ||
defaultValue?: number; | ||
} | { | ||
env: string; | ||
type: ETypeOfEnvLink.STRING; | ||
func?: null; | ||
required?: boolean; | ||
defaultValue?: string; | ||
}; | ||
declare type DeepPartial<T, F = undefined> = { | ||
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : F extends undefined ? T[P] : F; | ||
}; | ||
declare type TEnvironmentLinks<T extends object> = { | ||
[property in keyof Partial<T>]: T[property] extends object ? TEnvironmentLinks<T[property]> : IEnvironmentLink; | ||
export declare type TEnvironmentLinks<T extends IObject> = { | ||
[property in keyof Partial<T>]: T[property] extends object ? TEnvironmentLinks<T[property]> : TEnvironmentLink; | ||
}; | ||
declare function isEnvLink(obj: any): obj is TEnvironmentLink; | ||
export declare type TConfigChangeReaction<T = any> = (config: T) => void; | ||
export declare class ConfigStore<T extends object> { | ||
export declare class ConfigStore<T extends IObject> { | ||
private _values; | ||
@@ -32,3 +56,3 @@ private _valuesWithAugmentations; | ||
setConfig(config: DeepPartial<T>, envOverridePrefix?: string): void; | ||
getConfig({ ignoreOverrides, ignoreEnvLinks, ignoreReactions }?: { | ||
getConfig({ ignoreOverrides, ignoreEnvLinks, ignoreReactions, }?: { | ||
ignoreOverrides?: boolean; | ||
@@ -42,2 +66,5 @@ ignoreEnvLinks?: boolean; | ||
} | ||
export declare const __jest_test_internals: { | ||
isEnvLink: typeof isEnvLink; | ||
}; | ||
export {}; |
@@ -38,24 +38,18 @@ import immer from 'immer'; | ||
ETypeOfEnvLink["JSON_STRING"] = "JSON_STRING"; | ||
ETypeOfEnvLink["FUNCTION"] = "FUNCTION"; | ||
})(ETypeOfEnvLink || (ETypeOfEnvLink = {})); | ||
// type TEnvironmentLinks<T extends object> = any; | ||
var envLinkOptionalProps = ["defaultValue", "required", "func"]; | ||
function isEnvLink(obj) { | ||
var keyLength = Object.keys(obj).length; | ||
if (obj.hasOwnProperty("env") && obj.hasOwnProperty("type")) { | ||
if (keyLength > 2) { | ||
if ((obj.hasOwnProperty("required") || obj.hasOwnProperty("defaultValue")) && keyLength <= 4) { | ||
if (keyLength === 3) { | ||
return true; | ||
} | ||
else if (obj.hasOwnProperty("required") && obj.hasOwnProperty("defaultValue")) { | ||
return true; | ||
} | ||
} | ||
} | ||
else { | ||
return true; | ||
} | ||
if (!obj.hasOwnProperty("env")) { | ||
return false; | ||
} | ||
return false; | ||
if (!obj.hasOwnProperty("type")) { | ||
return false; | ||
} | ||
var extraPropsAmount = Object.keys(obj).length - 2; | ||
var hasOptionalKeys = envLinkOptionalProps.filter(function (prop) { return obj.hasOwnProperty(prop); }); | ||
return extraPropsAmount === hasOptionalKeys.length; | ||
} | ||
function getEnvLinksFromEnv(envLinks, configName) { | ||
function getEnvLinksFromEnv(envLinks, configName, values, prefixKey) { | ||
if (prefixKey === void 0) { prefixKey = ""; } | ||
var envLinksObj = {}; | ||
@@ -67,12 +61,22 @@ var configNameString = configName.length > 0 ? " (" + configName + ")" : ""; | ||
if (isEnvLink(cur)) { | ||
var type = cur.type, env = cur.env, defaultValue = cur.defaultValue, _b = cur.required, required = _b === void 0 ? true : _b; | ||
var type = cur.type, env = cur.env, _b = cur.required, required = _b === void 0 ? true : _b, func = cur.func; | ||
if (typeof process.env[env] === "undefined") { | ||
if (required) { | ||
throw new Error("CONFIG" + configNameString + ": Couldn't get the REQUIRED environment variable [" + env + "] - you must define it. (alternatively set the link to not-required and provide a default)"); | ||
throw new Error("CONFIG" + configNameString + ": property '" + prefixKey + key + "', Couldn't get the REQUIRED environment variable [" + env + "] - you must define it. (alternatively set the link to not-required and provide a default)"); | ||
} | ||
if (typeof defaultValue === "undefined") { | ||
throw new Error("CONFIG" + configNameString + ": default value for pulling environment variable [" + env + "] is undefined (an environment variable that is not required must have a default value)"); | ||
/*if (typeof defaultValue === "undefined") { | ||
throw new Error( | ||
`CONFIG${configNameString}: default value for pulling environment variable [${env}] is undefined (an environment variable that is not required must have a default value)` | ||
); | ||
}*/ | ||
var def = void 0; | ||
if (cur.hasOwnProperty("defaultValue")) { | ||
envLinksObj[key] = cur.defaultValue; | ||
def = cur.defaultValue; | ||
console.warn("CONFIG" + configNameString + ": property '" + prefixKey + key + "' using default value (" + JSON.stringify(def) + ") because env variable \"" + env + "\" was not set."); | ||
} | ||
console.warn("CONFIG" + configNameString + ": property '" + key + "' using default value (" + JSON.stringify(defaultValue) + ") because env variable \"" + env + "\" was not set."); | ||
envLinksObj[key] = defaultValue; | ||
else { | ||
def = values[key]; | ||
console.warn("CONFIG" + configNameString + ": property '" + prefixKey + key + "' couldn't be set from env variable \"" + env + "\" (was not present) - so not changing its value from what was set before (" + def + ")."); | ||
} | ||
} | ||
@@ -88,2 +92,10 @@ else { | ||
} | ||
else if (type === ETypeOfEnvLink.FUNCTION) { | ||
if (func != null) { | ||
value = func(envString); | ||
} | ||
else { | ||
throw new Error("CONFIG" + configNameString + ": type of environment link for " + key + " [" + env + "] is of FUNCTION type, but no function has been defined. Check your envLink config."); | ||
} | ||
} | ||
else { | ||
@@ -96,3 +108,6 @@ value = envString; | ||
else { | ||
envLinksObj[key] = getEnvLinksFromEnv(cur, configName); | ||
var deeper = getEnvLinksFromEnv(cur, configName, values[key], "" + prefixKey + key + "."); | ||
if (Object.keys(deeper).length > 0) { | ||
envLinksObj[key] = getEnvLinksFromEnv(cur, configName, values[key], "" + prefixKey + key + "."); | ||
} | ||
} | ||
@@ -129,3 +144,3 @@ } | ||
} | ||
this._envLinks = merge({}, this._envLinks, getEnvLinksFromEnv(envLinks, this._configName)); | ||
this._envLinks = merge({}, this._envLinks, getEnvLinksFromEnv(envLinks, this._configName, this._values)); | ||
this.finalizeValues(); | ||
@@ -178,4 +193,7 @@ }; | ||
}()); | ||
var __jest_test_internals = { | ||
isEnvLink: isEnvLink, | ||
}; | ||
export { ETypeOfEnvLink, ConfigStore }; | ||
export { ETypeOfEnvLink, ConfigStore, __jest_test_internals }; | ||
//# sourceMappingURL=index.es5.js.map |
@@ -40,24 +40,18 @@ "use strict"; | ||
ETypeOfEnvLink["JSON_STRING"] = "JSON_STRING"; | ||
ETypeOfEnvLink["FUNCTION"] = "FUNCTION"; | ||
})(ETypeOfEnvLink = exports.ETypeOfEnvLink || (exports.ETypeOfEnvLink = {})); | ||
// type TEnvironmentLinks<T extends object> = any; | ||
var envLinkOptionalProps = ["defaultValue", "required", "func"]; | ||
function isEnvLink(obj) { | ||
var keyLength = Object.keys(obj).length; | ||
if (obj.hasOwnProperty("env") && obj.hasOwnProperty("type")) { | ||
if (keyLength > 2) { | ||
if ((obj.hasOwnProperty("required") || obj.hasOwnProperty("defaultValue")) && keyLength <= 4) { | ||
if (keyLength === 3) { | ||
return true; | ||
} | ||
else if (obj.hasOwnProperty("required") && obj.hasOwnProperty("defaultValue")) { | ||
return true; | ||
} | ||
} | ||
} | ||
else { | ||
return true; | ||
} | ||
if (!obj.hasOwnProperty("env")) { | ||
return false; | ||
} | ||
return false; | ||
if (!obj.hasOwnProperty("type")) { | ||
return false; | ||
} | ||
var extraPropsAmount = Object.keys(obj).length - 2; | ||
var hasOptionalKeys = envLinkOptionalProps.filter(function (prop) { return obj.hasOwnProperty(prop); }); | ||
return extraPropsAmount === hasOptionalKeys.length; | ||
} | ||
function getEnvLinksFromEnv(envLinks, configName) { | ||
function getEnvLinksFromEnv(envLinks, configName, values, prefixKey) { | ||
if (prefixKey === void 0) { prefixKey = ""; } | ||
var envLinksObj = {}; | ||
@@ -69,12 +63,22 @@ var configNameString = configName.length > 0 ? " (" + configName + ")" : ""; | ||
if (isEnvLink(cur)) { | ||
var type = cur.type, env = cur.env, defaultValue = cur.defaultValue, _b = cur.required, required = _b === void 0 ? true : _b; | ||
var type = cur.type, env = cur.env, _b = cur.required, required = _b === void 0 ? true : _b, func = cur.func; | ||
if (typeof process.env[env] === "undefined") { | ||
if (required) { | ||
throw new Error("CONFIG" + configNameString + ": Couldn't get the REQUIRED environment variable [" + env + "] - you must define it. (alternatively set the link to not-required and provide a default)"); | ||
throw new Error("CONFIG" + configNameString + ": property '" + prefixKey + key + "', Couldn't get the REQUIRED environment variable [" + env + "] - you must define it. (alternatively set the link to not-required and provide a default)"); | ||
} | ||
if (typeof defaultValue === "undefined") { | ||
throw new Error("CONFIG" + configNameString + ": default value for pulling environment variable [" + env + "] is undefined (an environment variable that is not required must have a default value)"); | ||
/*if (typeof defaultValue === "undefined") { | ||
throw new Error( | ||
`CONFIG${configNameString}: default value for pulling environment variable [${env}] is undefined (an environment variable that is not required must have a default value)` | ||
); | ||
}*/ | ||
var def = void 0; | ||
if (cur.hasOwnProperty("defaultValue")) { | ||
envLinksObj[key] = cur.defaultValue; | ||
def = cur.defaultValue; | ||
console.warn("CONFIG" + configNameString + ": property '" + prefixKey + key + "' using default value (" + JSON.stringify(def) + ") because env variable \"" + env + "\" was not set."); | ||
} | ||
console.warn("CONFIG" + configNameString + ": property '" + key + "' using default value (" + JSON.stringify(defaultValue) + ") because env variable \"" + env + "\" was not set."); | ||
envLinksObj[key] = defaultValue; | ||
else { | ||
def = values[key]; | ||
console.warn("CONFIG" + configNameString + ": property '" + prefixKey + key + "' couldn't be set from env variable \"" + env + "\" (was not present) - so not changing its value from what was set before (" + def + ")."); | ||
} | ||
} | ||
@@ -90,2 +94,10 @@ else { | ||
} | ||
else if (type === ETypeOfEnvLink.FUNCTION) { | ||
if (func != null) { | ||
value = func(envString); | ||
} | ||
else { | ||
throw new Error("CONFIG" + configNameString + ": type of environment link for " + key + " [" + env + "] is of FUNCTION type, but no function has been defined. Check your envLink config."); | ||
} | ||
} | ||
else { | ||
@@ -98,3 +110,6 @@ value = envString; | ||
else { | ||
envLinksObj[key] = getEnvLinksFromEnv(cur, configName); | ||
var deeper = getEnvLinksFromEnv(cur, configName, values[key], "" + prefixKey + key + "."); | ||
if (Object.keys(deeper).length > 0) { | ||
envLinksObj[key] = getEnvLinksFromEnv(cur, configName, values[key], "" + prefixKey + key + "."); | ||
} | ||
} | ||
@@ -131,3 +146,3 @@ } | ||
} | ||
this._envLinks = lodash_2.merge({}, this._envLinks, getEnvLinksFromEnv(envLinks, this._configName)); | ||
this._envLinks = lodash_2.merge({}, this._envLinks, getEnvLinksFromEnv(envLinks, this._configName, this._values)); | ||
this.finalizeValues(); | ||
@@ -181,2 +196,5 @@ }; | ||
exports.ConfigStore = ConfigStore; | ||
exports.__jest_test_internals = { | ||
isEnvLink: isEnvLink, | ||
}; | ||
//# sourceMappingURL=index.js.map |
@@ -42,24 +42,18 @@ (function (global, factory) { | ||
ETypeOfEnvLink["JSON_STRING"] = "JSON_STRING"; | ||
ETypeOfEnvLink["FUNCTION"] = "FUNCTION"; | ||
})(exports.ETypeOfEnvLink || (exports.ETypeOfEnvLink = {})); | ||
// type TEnvironmentLinks<T extends object> = any; | ||
var envLinkOptionalProps = ["defaultValue", "required", "func"]; | ||
function isEnvLink(obj) { | ||
var keyLength = Object.keys(obj).length; | ||
if (obj.hasOwnProperty("env") && obj.hasOwnProperty("type")) { | ||
if (keyLength > 2) { | ||
if ((obj.hasOwnProperty("required") || obj.hasOwnProperty("defaultValue")) && keyLength <= 4) { | ||
if (keyLength === 3) { | ||
return true; | ||
} | ||
else if (obj.hasOwnProperty("required") && obj.hasOwnProperty("defaultValue")) { | ||
return true; | ||
} | ||
} | ||
} | ||
else { | ||
return true; | ||
} | ||
if (!obj.hasOwnProperty("env")) { | ||
return false; | ||
} | ||
return false; | ||
if (!obj.hasOwnProperty("type")) { | ||
return false; | ||
} | ||
var extraPropsAmount = Object.keys(obj).length - 2; | ||
var hasOptionalKeys = envLinkOptionalProps.filter(function (prop) { return obj.hasOwnProperty(prop); }); | ||
return extraPropsAmount === hasOptionalKeys.length; | ||
} | ||
function getEnvLinksFromEnv(envLinks, configName) { | ||
function getEnvLinksFromEnv(envLinks, configName, values, prefixKey) { | ||
if (prefixKey === void 0) { prefixKey = ""; } | ||
var envLinksObj = {}; | ||
@@ -71,12 +65,22 @@ var configNameString = configName.length > 0 ? " (" + configName + ")" : ""; | ||
if (isEnvLink(cur)) { | ||
var type = cur.type, env = cur.env, defaultValue = cur.defaultValue, _b = cur.required, required = _b === void 0 ? true : _b; | ||
var type = cur.type, env = cur.env, _b = cur.required, required = _b === void 0 ? true : _b, func = cur.func; | ||
if (typeof process.env[env] === "undefined") { | ||
if (required) { | ||
throw new Error("CONFIG" + configNameString + ": Couldn't get the REQUIRED environment variable [" + env + "] - you must define it. (alternatively set the link to not-required and provide a default)"); | ||
throw new Error("CONFIG" + configNameString + ": property '" + prefixKey + key + "', Couldn't get the REQUIRED environment variable [" + env + "] - you must define it. (alternatively set the link to not-required and provide a default)"); | ||
} | ||
if (typeof defaultValue === "undefined") { | ||
throw new Error("CONFIG" + configNameString + ": default value for pulling environment variable [" + env + "] is undefined (an environment variable that is not required must have a default value)"); | ||
/*if (typeof defaultValue === "undefined") { | ||
throw new Error( | ||
`CONFIG${configNameString}: default value for pulling environment variable [${env}] is undefined (an environment variable that is not required must have a default value)` | ||
); | ||
}*/ | ||
var def = void 0; | ||
if (cur.hasOwnProperty("defaultValue")) { | ||
envLinksObj[key] = cur.defaultValue; | ||
def = cur.defaultValue; | ||
console.warn("CONFIG" + configNameString + ": property '" + prefixKey + key + "' using default value (" + JSON.stringify(def) + ") because env variable \"" + env + "\" was not set."); | ||
} | ||
console.warn("CONFIG" + configNameString + ": property '" + key + "' using default value (" + JSON.stringify(defaultValue) + ") because env variable \"" + env + "\" was not set."); | ||
envLinksObj[key] = defaultValue; | ||
else { | ||
def = values[key]; | ||
console.warn("CONFIG" + configNameString + ": property '" + prefixKey + key + "' couldn't be set from env variable \"" + env + "\" (was not present) - so not changing its value from what was set before (" + def + ")."); | ||
} | ||
} | ||
@@ -92,2 +96,10 @@ else { | ||
} | ||
else if (type === exports.ETypeOfEnvLink.FUNCTION) { | ||
if (func != null) { | ||
value = func(envString); | ||
} | ||
else { | ||
throw new Error("CONFIG" + configNameString + ": type of environment link for " + key + " [" + env + "] is of FUNCTION type, but no function has been defined. Check your envLink config."); | ||
} | ||
} | ||
else { | ||
@@ -100,3 +112,6 @@ value = envString; | ||
else { | ||
envLinksObj[key] = getEnvLinksFromEnv(cur, configName); | ||
var deeper = getEnvLinksFromEnv(cur, configName, values[key], "" + prefixKey + key + "."); | ||
if (Object.keys(deeper).length > 0) { | ||
envLinksObj[key] = getEnvLinksFromEnv(cur, configName, values[key], "" + prefixKey + key + "."); | ||
} | ||
} | ||
@@ -133,3 +148,3 @@ } | ||
} | ||
this._envLinks = lodash.merge({}, this._envLinks, getEnvLinksFromEnv(envLinks, this._configName)); | ||
this._envLinks = lodash.merge({}, this._envLinks, getEnvLinksFromEnv(envLinks, this._configName, this._values)); | ||
this.finalizeValues(); | ||
@@ -182,4 +197,8 @@ }; | ||
}()); | ||
var __jest_test_internals = { | ||
isEnvLink: isEnvLink, | ||
}; | ||
exports.ConfigStore = ConfigStore; | ||
exports.__jest_test_internals = __jest_test_internals; | ||
@@ -186,0 +205,0 @@ Object.defineProperty(exports, '__esModule', { value: true }); |
{ | ||
"name": "dynamic-config-store", | ||
"version": "1.0.3", | ||
"version": "1.0.4", | ||
"description": "Simple configuration utility for deployments and libraries", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
272
Readme.md
@@ -1,30 +0,268 @@ | ||
## dynamic-config-store | ||
## 🔱 Dynamic Config Store | ||
A configuration utility for global configurations. | ||
A utility for configurations which can be dynamically changed from various sources. | ||
An example simple config: | ||
``` | ||
yarn add dynamic-config-store | ||
``` | ||
The library has been built from the ground up in Typescript, so its recommended (though not required) | ||
to use it to get some great auto-completion suggestions for your configs. | ||
## Quick Start | ||
Let's create a basic server configuration: | ||
```typescript | ||
import { ConfigStore } from "dynamic-config-store"; | ||
const config = new ConfigStore({ | ||
accessCode: "123abc", | ||
nullThing: null, | ||
SomeLibrary: { | ||
Deeper: { | ||
key: "asdasd", | ||
otherKey: "123123", | ||
// Can pass a TypeScript interface to the constructor | ||
// for all the goodness that comes with that | ||
const config = new ConfigStore<ISimpleServerConfig>({ | ||
isProductionEnv: false, | ||
serverSecret: "123abc", | ||
instance: { | ||
port: 8000, | ||
host: { | ||
protocol: "http", | ||
hostname: "localhost", | ||
}, | ||
}, | ||
}); | ||
``` | ||
This creates our initial configuration, with **well-defined defaults**. | ||
A configuration can be easily dynamically changed via the following means: | ||
### 🔌 Environment Links | ||
#### Nice, pre-defined environment overrides for your config | ||
Let's try one now: | ||
```typescript | ||
config.setEnvLinks({ | ||
isProductionEnv: { | ||
env: "NODE_ENV", | ||
type: ETypeOfEnvLink.FUNCTION, | ||
func: v => v === "production", | ||
}, | ||
}); | ||
``` | ||
We now have a nice global, _boolean_, configuration value for `isProductionEnv` which we can call upon. | ||
No more ugly checks for `process.env.NODE_ENV === "production"` all over the place. | ||
Look at the `type` here. We passed a `FUNCTION` type, which means that whatever we read from the environment | ||
variables will be passed through a function, which we have defined here in `func`, and the returned value | ||
will become `isProductionEnv`. | ||
By default, this link is `required` - as in, if we don't find `process.env.NODE_ENV`, the config will throw an error. | ||
Let's look at some more: | ||
```typescript | ||
config.setEnvLinks({ | ||
instance: { | ||
port: { | ||
env: "SERVER_PORT", | ||
type: ETypeOfEnvLink.NUMBER, | ||
required: false | ||
}, | ||
host: { | ||
hostname: { | ||
env: "SERVER_HOST_NAME", | ||
type: ETypeOfEnvLink.STRING, | ||
defaultValue: "example.com", | ||
required: false | ||
} | ||
} | ||
}, | ||
wins: 213, | ||
}); | ||
``` | ||
A configuration can be easily defined through the following means (higher takes precedence over others): | ||
Here we define two more environment links. You can define as many and deeply in the config | ||
"tree" as you would like. For the sake of example we have run `setEnvLinks()` twice now, but | ||
you can combine all your links in one go. | ||
* Environment Overrides (every single config value has the ability to be overridden through environment variables) | ||
* e.g. `CONFIG_OVERRIDE_SERVER__PORT` | ||
* Defined Environment Links (these are nicer, pre-defined environment overrides) | ||
You can see on the second link here, we set a `defaultValue` - this will take precedence over | ||
the initial default value we set at the beginning, on defining the config. | ||
These configurations can also have reactions added into them to form extra | ||
config values based off of any changes that may happen to the config. | ||
Lastly, look at the `type` that has been set for both. `NUMBER` and `STRING` respectively. Let's go | ||
into more detail on those: | ||
#### Environment Link Types | ||
Environment variables are always of `string` type, but sometimes we don't always want strings. | ||
`dynamice-config-store` provides you with alternative ways to interpret values from the environment: | ||
- `STRING` - The value will be returned as is from the environment variable | ||
- `NUMBER` - The value will be converted and set as a number on your config property | ||
- `JSON_STRING` - The value will be put through `JSON.parse()`, this assumes that the | ||
environment value is a serialized JSON string. The config takes care of converting serialized JavaScript dates | ||
back to proper `Date` objects too. | ||
- `FUNCTION` - Allows you to define a function under the `func` property, which will simply be passed | ||
the value from the environment - and must return the final value you'd like in your config. | ||
### ⚠ Environment Overrides | ||
Every config value has the ability to be overridden through environment variables, **_even if no link exists_** | ||
Mostly used for quick fixes or debugging scenarios, when a full re-build and deployment is not desired. | ||
In our above config, we kept it simple for the sake of example. But we really should have added a little more | ||
info to make it more deliberate in its identity. | ||
We should have done something like this: | ||
```typescript | ||
const config = new ConfigStore<ISimpleServerConfig>({ | ||
isProductionEnv: false, | ||
serverSecret: "123abc", | ||
instance: { | ||
port: 8080, | ||
host: { | ||
protocol: "http", | ||
hostname: "localhost", | ||
}, | ||
}, | ||
}, "CONFIG_SERVER_OVERRIDE_", "Server Config"); | ||
``` | ||
Those two second parameters here identify this configuration much better. And especially for the sake of | ||
Environment Overrides, the first one helps us target this specific config - like so: | ||
```typescript | ||
{ | ||
isProductionEnv: false, // CONFIG_SERVER_OVERRIDE_IS_PRODUCTION_ENV | ||
serverSecret: "123abc", // CONFIG_SERVER_OVERRIDE_SERVER_SECRET | ||
instance: { | ||
port: 8080, // CONFIG_SERVER_OVERRIDE_INSTANCE__PORT | ||
host: { | ||
protocol: "http", // CONFIG_SERVER_OVERRIDE_INSTANCE__PORT__PROTOCOL | ||
hostname: "localhost", // CONFIG_SERVER_OVERRIDE_INSTANCE__PORT__HOSTNAME | ||
}, | ||
}, | ||
} | ||
``` | ||
If any of those override environment variables have been set, they will take precedence over any other value | ||
set for the config. | ||
Any value set in these overrides shall be put through `JSON.parse()`, allowing for the reviving of proper | ||
JavaScript types - including `Date` objects. So keep that in mind, because of the following cases: | ||
``` | ||
CONFIG_SERVER_OVERRIDE_IS_PRODUCTION_ENV: true // fine - can parse directly to boolean | ||
CONFIG_SERVER_OVERRIDE_SERVER_SECRET: 123abc // ERROR - not JSON parsable | ||
CONFIG_SERVER_OVERRIDE_SERVER_SECRET: "123abc" // 50 / 50 - will fail if the quotations aren't escaped | ||
CONFIG_SERVER_OVERRIDE_SERVER_SECRET: \"123abc\" // probably safest for string types | ||
CONFIG_SERVER_OVERRIDE_INSTANCE__PORT: 4000 // fine - can parse directly to number | ||
``` | ||
Basically this allows you to create great global configs for your deployments without thinking straight away | ||
about what you'd like to expose as an environment variable. If you find that later you are using an override | ||
constantly to set a certain config value - then its probably time to create a dedicated and well-named link. | ||
#### Ignoring Environment Overrides | ||
If for whatever reason (possibly security concerns) you don't want to allow an override for a certain value - all | ||
you have to do to ignore the override is get your config like so: | ||
``` | ||
const { serverSecret } = ServerConfig.getConfig({ ignoreOverrides: true }); | ||
``` | ||
### Deep Merging and Extending | ||
All of the methods in `dynamic-config-store` use **deep merging** when changing the config. This | ||
ensures that your objects deeper in the config tree are not wholly over-written or cleared when you | ||
simply want to change a single value within one. | ||
This is useful for when you are extending an external configuration, from perhaps a library or one of your own | ||
internal packages that you use in different projects for code re-use. For example, if you were extending this | ||
Server Config from the example in a project you might do something like: | ||
```typescript | ||
import { ServerConfig } from "server-utility/ServerConfig"; | ||
ServerConfig.setConfig({ | ||
serverSecret: "my-new-secret", | ||
instance: { | ||
port: 3000, | ||
}, | ||
}); | ||
``` | ||
This would set a new `serverSecret` default, and would only replace the `port` value inside of `instance`, and keep all the original defaults set by the | ||
original config during creation, here in a library apparently called `server-utility`. You could even now define | ||
your own **Environment Links** into this external configuration, specific to this deployment: | ||
```typescript | ||
ServerConfig.setEnvLinks({ | ||
serverSecret: { | ||
env: "MY_SERVER_SECRET", | ||
type: ETypeOfEnvLink.STRING | ||
}, | ||
}, true); // Setting true here will wipe out all the Links set previously! | ||
// In this case, SERVER_PORT, SERVER_HOST_NAME and NODE_ENV | ||
// default is false - as this is most often not desired | ||
``` | ||
Modularity and extensibility are first class citizens in `dynamic-config-store`! | ||
## Best practice | ||
You should always define your configuration before any other code runs. Let's look at a simple example of | ||
a config for a server deployment: | ||
Create an entry file and import the config file first, this initializes it before any other code: | ||
```typescript | ||
// project/src/entry.ts | ||
import "./ServerConfig"; | ||
import "./Server"; | ||
``` | ||
```typescript | ||
// project/src/ServerConfig.ts | ||
import { ConfigStore } from "dynamic-config-store"; | ||
export const ServerConfig = new ConfigStore<ISimpleServerConfig>({ | ||
isProductionEnv: false, | ||
serverSecret: "123abc", | ||
instance: { | ||
port: 8080, | ||
host: { | ||
protocol: "http", | ||
hostname: "localhost", | ||
}, | ||
}, | ||
}, "CONFIG_SERVER_OVERRIDE_", "Server Config"); | ||
``` | ||
That potential Server file: | ||
```typescript | ||
// project/src/Server.ts | ||
import { ServerConfig } from "./ServerConfig"; | ||
import Koa from "koa"; | ||
const { serverSecret, isProductionEnv, instance: { port, host: { protocol, hostname }} } = ServerConfig.getConfig(); | ||
const app = new Koa(); | ||
app.keys([serverSecret]); | ||
// ... app configuration ... | ||
app.listen(port, () => { | ||
if (!isProductionEnv) { | ||
console.info(`Server is not running as production!`); | ||
} | ||
console.info(`listening on ${protocol}://${hostname}:${port}`); | ||
}); | ||
``` |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
79735
649
269
0