New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@jepz20/conman

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@jepz20/conman - npm Package Compare versions

Comparing version 0.0.12 to 0.0.13

mock/conman.js

4

package.json
{
"name": "@jepz20/conman",
"version": "0.0.12",
"version": "0.0.13",
"description": "Configuration manager that supports ttl and plugabble sources",

@@ -17,3 +17,3 @@ "main": "./src/conman",

},
"gitHead": "e742f415a7c1a42cddcff5f619b34a3113681af2",
"gitHead": "520960b62d7a636a1a4c13d5933a06d2aefc852d",
"lint-staged": {

@@ -20,0 +20,0 @@ "src/**/*.*": [

@@ -66,3 +66,4 @@ # CONMAN

`conman.build()` builds the configuration object by calling the `build` function from all the added sources and returns promise the resolves when the build is completed and schedules a new build base on the `ttl` option. If `ttl` is `0` a new built is NOT schedule.
`conman.build()`
Build the sources added by the `addSource` method in order from all the added sources and returns promise the resolves when the build is completed and schedules a new build base on the `ttl` option. If `ttl` is `0` a new built is NOT schedule.

@@ -138,10 +139,2 @@ If `useFile` option is true, it will try to read a cache file and write the new config to the cache file.

### stop
`conman.stop()` stops the rebuilt interval schedule base on the TTL
### reset
`conman.reset()` clears all the sources, the configuration cache and resets all defaults options
### getObfuscate

@@ -192,4 +185,10 @@

## Integrating Conman with ExpressJS
### stop
`conman.stop()` stops the rebuilt interval schedule base on the TTL
### reset
`conman.reset()` clears all the sources, the configuration cache and resets all defaults options
## Creating your own source

@@ -196,0 +195,0 @@

@@ -24,87 +24,171 @@ const { mergeDeepRight } = require('ramda');

const _log = opts => (type, ...args) => {
if (opts.logEnabled) {
opts.logger[type](...args);
}
};
/**
* Logs only when the option is enabled
* @private
* @param {boolean} isEnabled Should log?
* @param {object} logger Object that atleast a log and error function
* @param logger.log Logs info
* @param logger.error Logs error
* @return {function(string, *)} Function that logs if enabled with the set logger
*/
function _log(isEnabled, logger) {
return function inner(type, ...args) {
if (isEnabled) {
logger[type](...args);
}
};
}
function conman(userOptions) {
let sources = [];
let privateCache;
let ttlInterval;
let logger;
let options = { ...defaultOptions, ...userOptions };
/**
* Format the config to have a timestamp
* @private
* @param {object} tree
*/
function _prepareCacheObject(tree) {
return {
lastModified: new Date().getTime(),
data: tree
};
}
logger = _log(options);
const selector = nodeSelector(logger);
/**
* Writes cache object
* @private
* @param {object} cacheObject
* @param {object} opts Options to write file
* @param {string} opts.cacheFileName Filename where to save the file
* @param {object} opts.logger Object that atleast a log and error function
*/
function _prepareCacheObject(tree) {
return {
lastModified: new Date().getTime(),
data: tree
};
}
function _writeToFile(cacheObject, opts) {
opts.logger('log', 'Writing conman cache to file');
return jsonfile
.writeFile(opts.cacheFileName, cacheObject, {
spaces: 2,
EOL: '\r\n'
})
.then(res => {
opts.logger(
'log',
`Succesfully wrote conman cache to file ${opts.cacheFileName}`
);
return res;
})
.catch(error => {
opts.logger(
'error',
`Couldn't write cache to file ${opts.cacheFileName}`,
error
);
});
}
/**
* Calculates if TTL has expired
* @private
* @param {number} lastModified Timestamp when it was last modified
* @param {number} ttl Miliseconds until next expiration
*/
function _isExpired(lastModified, ttl) {
return new Date().getTime() - lastModified <= ttl;
}
function _writeToFile(cacheObject, opts) {
if (!opts.useFile) {
return Promise.resolve();
}
logger('log', 'Writing conman cache to file');
return jsonfile
.writeFile(opts.cacheFileName, cacheObject, {
spaces: 2,
EOL: '\r\n'
})
.then(res => {
logger(
/**
* Reads file with the cache object
* @private
* @param {object} opts Options to Read file
* @param {string} opts.cacheFileName Filename where to read the file
* @param {object} opts.logger Object with atleast a log and error function
*/
function _readFromFile(opts) {
return jsonfile
.readFile(opts.cacheFileName)
.then(cache => {
const { lastModified } = cache || {};
if (_isExpired(lastModified, opts.ttl)) {
opts.logger(
'log',
`Succesfully wrote conman cache to file ${opts.cacheFileName}`
`Succesfully read cache from file ${opts.cacheFileName}`
);
return res;
})
.catch(error => {
logger(
'error',
`Couldn't write cache to file ${opts.cacheFileName}`,
error
);
});
return cache.data;
}
return null;
})
.catch(err => {
opts.logger(
'error',
`Could not read cache config file "${opts.cacheFileName}"`,
err
);
return null;
});
}
/**
* Validate if a source has all the necessary properties
* @private
* @param {Object} source
*/
function _validateSource(source) {
if (typeof source.build !== 'function') {
return 'Source should have a build function';
}
function _isExpired(cache, opts) {
return new Date().getTime() - cache.lastModified <= opts.ttl;
if (!source.type) {
return 'Source should have a type';
}
return null;
}
/**
* @private
* @param {function} selector function that gets the key from the cache
* @param {sting} key
* @param {object} _privateCache
*/
function _get(selector, key, _privateCache) {
const safeCache = _privateCache || {};
if (key === undefined) {
return safeCache;
}
function _readFromFile(opts) {
return jsonfile
.readFile(opts.cacheFileName)
.then(cache => {
if (cache && cache.lastModified && _isExpired(cache, opts)) {
logger(
'log',
`Succesfully read cache from file ${opts.cacheFileName}`
);
return cache.data;
}
return null;
})
.catch(err => {
logger(
'error',
`Could not read cache config file "${opts.cacheFileName}"`,
err
);
return null;
});
return selector.query(safeCache || {}, key);
}
/**
* Builds the sources sequencially
* @private
* @param {array} _sources array of sources
*/
function _buildSources(_sources) {
async function buildSource(config, source) {
const sourceConfig = await source.build(config);
const parseConfig = source.key
? { [source.key]: sourceConfig }
: sourceConfig;
return mergeDeepRight(config, parseConfig);
}
function _validateSource(source) {
if (typeof source.build !== 'function') {
return 'Source should have a build function';
}
if (!source.type) {
return 'Source should have a type';
}
return null;
return serialize(_sources, buildSource, {});
}
/**
* Factory to generate Conman Instances
* @namespace
* @param {Object} userOptions
* @param {number} userOptions.ttl Milliseconds before rebuilding the config
* @param {boolean} userOptions.logEnabled Should it log?
* @param {logger} userOptions.logger Object with atleast a log and error function
* @param {boolean} userOptions.useFile Should it read and write the config to a file
* @param {string} userOptions.cacheFileName Name of the file where cache config will be saved
* @returns {object}
*/
function conman(userOptions) {
let sources = [];
let privateCache;
let ttlInterval;
let options = { ...defaultOptions, ...userOptions };
options.logger = _log(options.logEnabled, options.logger);
const selector = nodeSelector(options.logger);
function _setPrivateCache(config) {
privateCache = config;
return config;
}

@@ -121,54 +205,5 @@

function addSource(source) {
const sourceError = _validateSource(source);
if (sourceError) {
throw new Error(sourceError);
}
logger('log', `Source "${source.type}" added to conman`);
sources.push(source);
return this;
}
function _get(key, _privateCache) {
const safeCache = _privateCache || {}
if (key === undefined) {
return safeCache;
}
return selector.query(safeCache || {}, key);
}
function get(keys) {
if (Array.isArray(keys)) {
return keys.map(key => _get(key, privateCache));
}
return _get(keys, privateCache);
}
function getObfuscate(keys, params) {
return obfuscate(get(keys), params);
}
function _buildSources(_sources) {
async function buildSource(config, source) {
const sourceConfig = await source.build(config);
const parseConfig = source.key
? { [source.key]: sourceConfig }
: sourceConfig;
return mergeDeepRight(config, parseConfig);
}
return serialize(_sources, buildSource, {});
}
function _setPrivateCache(config) {
privateCache = config;
return config;
}
async function _buildAndSaveCache(_sources) {
const sourcesTypes = _sources.map(({ name, type }) => name || type);
logger(
options.logger(
'log',

@@ -178,10 +213,44 @@ `Build triggered for sources: "${sourcesTypes.join()}" at ${new Date().toISOString()}`

const configs = await _buildSources(_sources);
logger(
options.logger(
'log',
`Build completed for sources: "${sourcesTypes.join()}" at ${new Date().toISOString()}`
);
await _writeToFile(_prepareCacheObject(configs), options);
if (options.useFile) {
await _writeToFile(_prepareCacheObject(configs), options);
}
return _setPrivateCache(configs);
}
/**
* @function sourceBuild
* @param {object} config - config built by previous sources
* @returns {object} built config by the source
*/
/**
* Adds a source to the conman instance
* @param {object} source
* @param {sourceBuild} source.build function that receives the config built by previous sources and returns and object
* @param {string} source.type string the identifies the type of source
* @param {string=} source.name string the identifies the specific source
* @param {string=} source.key if a key is provided the result from build will be added to that key
* @returns {object} conman instance
*/
function addSource(source) {
const sourceError = _validateSource(source);
if (sourceError) {
throw new Error(sourceError);
}
options.logger('log', `Source "${source.type}" added to conman`);
sources.push(source);
return this;
}
/**
* Build the sources added by the `addSource` method in order
* if the useFile flag is set it would read the config from the cache file if it hasn't expired
* if ttl options is not 0 it would rebuild the config in ttl milliseconds
* @returns {Promise<object>} Promise that resolves with the generated config
*/
async function build() {

@@ -201,7 +270,39 @@ _triggerInterval();

}
/**
* Gets a single key, an array of keys, or the complete config from the configuration object.
* If you want to retrieve a nested key add a `.` between each key
* @param {(Array<string>|string)=} keys Array of keys or single to key to get from the property
* @returns {(Array<*>|*)} If an array of keys is provided it returns an array of values, if a single key was provided returns the value of that key
*/
function get(keys) {
if (Array.isArray(keys)) {
return keys.map(key => _get(selector, key, privateCache));
}
return _get(selector, keys, privateCache);
}
/**
* Behaves exactly as `conman.get` but returns an obfuscated version of the value.
* @param {(Array<string>|string)=} keys Array of keys or single to key to get from the property
* @param {object} params params to obfuscate the values
* @param {float} [0.5] percentage Wercentage of the values that should be obfuscated (replaced by the `character` option
* @param {string} [""] separator Wow to divide the value that will be obfuscated
* @param {string} ["end"] What part of the value to obfuscate `start` or `end`
* @param {string} ["*"] character What character should be used to obfuscate the value
* @returns {(Array<*>|*)} If an array of keys is provided it returns an array of values, if a single key was provided returns the value of that key
*/
function getObfuscate(keys, params) {
return obfuscate(get(keys), params);
}
/**
* Stops the rebuilt interval schedule base on the TTL
*/
function stop() {
clearInterval(ttlInterval);
}
/**
* Clears all the sources, the configuration cache and resets all defaults options
*/
function reset() {

@@ -212,3 +313,2 @@ sources = [];

privateCache = undefined;
logger = undefined;
clearInterval(ttlInterval);

@@ -215,0 +315,0 @@ }

jest.mock('jsonfile');
const jsonfile = require('jsonfile');
const Conman = require('./conman');
const conman = require('./conman');

@@ -52,6 +52,6 @@ const source = (obj = {}, { key, name } = {}) => {

it('should work with default options and no sources', async () => {
const conman = Conman();
const config = await conman.build();
conman.stop();
expect(config).toEqual({});
const config = conman();
const data = await config.build();
config.stop();
expect(data).toEqual({});
});

@@ -61,6 +61,6 @@

const source1 = asyncSource({ test: 'test' });
const conman = Conman();
const config = await conman.addSource(source1).build();
conman.stop();
expect(config).toEqual({ test: 'test' });
const config = conman();
const data = await config.addSource(source1).build();
config.stop();
expect(data).toEqual({ test: 'test' });
});

@@ -70,6 +70,6 @@

const source1 = source({ test: 'test' }, { key: 'TEST' });
const conman = Conman();
const config = await conman.addSource(source1).build();
conman.stop();
expect(config).toEqual({ TEST: { test: 'test' } });
const config = conman();
const data = await config.addSource(source1).build();
config.stop();
expect(data).toEqual({ TEST: { test: 'test' } });
});

@@ -80,9 +80,9 @@

const source2 = source({ test: 'usesConfig' });
const conman = Conman();
const config = await conman
const config = conman();
const data = await config
.addSource(source1)
.addSource(source2)
.build();
conman.stop();
expect(config).toEqual({ test: 'usesConfig', extra: 'extra config' });
config.stop();
expect(data).toEqual({ test: 'usesConfig', extra: 'extra config' });
});

@@ -93,9 +93,9 @@

const source2 = asyncSource({ test: 'usesConfig' });
const conman = Conman();
const config = await conman
const config = conman();
const data = await config
.addSource(source1)
.addSource(source2)
.build();
conman.stop();
expect(config).toEqual({ test: 'usesConfig', extra: 'extra config async' });
config.stop();
expect(data).toEqual({ test: 'usesConfig', extra: 'extra config async' });
});

@@ -105,6 +105,6 @@

const source1 = source({ test: 'test async' });
const conman = Conman();
const config = await conman.addSource(source1).build();
conman.stop();
expect(config).toEqual({ test: 'test async' });
const config = conman();
const data = await config.addSource(source1).build();
config.stop();
expect(data).toEqual({ test: 'test async' });
});

@@ -115,9 +115,9 @@

const source2 = source({ test: 'test', test3: 'test3' });
const conman = Conman();
const config = await conman
const config = conman();
const data = await config
.addSource(source1)
.addSource(source2)
.build();
conman.stop();
expect(config).toEqual({ test: 'test', test2: 'test2', test3: 'test3' });
config.stop();
expect(data).toEqual({ test: 'test', test2: 'test2', test3: 'test3' });
expect(jsonfile.writeFile).toHaveBeenCalledTimes(1);

@@ -129,6 +129,6 @@ expect(jsonfile.readFile).toHaveBeenCalledTimes(1);

const source1 = source({ test: 'test async' });
const conman = Conman();
await conman.addSource(source1).build();
conman.stop();
expect(conman.get('test')).toBe('test async');
const config = conman();
await config.addSource(source1).build();
config.stop();
expect(config.get('test')).toBe('test async');
});

@@ -142,6 +142,6 @@

});
const conman = Conman();
await conman.addSource(source1).build();
conman.stop();
expect(conman.get(['test', 'test2'])).toEqual(['test async', 'test2']);
const config = conman();
await config.addSource(source1).build();
config.stop();
expect(config.get(['test', 'test2'])).toEqual(['test async', 'test2']);
});

@@ -157,6 +157,6 @@

});
const conman = Conman();
await conman.addSource(source1).build();
conman.stop();
expect(conman.get('test.test2.test3')).toBe('test3');
const config = conman();
await config.addSource(source1).build();
config.stop();
expect(config.get('test.test2.test3')).toBe('test3');
});

@@ -176,13 +176,13 @@

});
const conman = Conman();
await conman.addSource(source1).build();
conman.stop();
expect(conman.getObfuscate('test')).toBe('te**');
expect(conman.getObfuscate('testUndefined')).toBe(undefined);
expect(conman.getObfuscate('testNull')).toBe(null);
expect(conman.getObfuscate('testNumber')).toBe('12**');
expect(conman.getObfuscate('testBoolean')).toBe('tr**');
expect(conman.getObfuscate('testObj')).toEqual({ 'va***': 'v**' });
expect(conman.getObfuscate('testArr')).toEqual(['ar**', 'ar**']);
expect(conman.getObfuscate(['testObj', 'test'])).toEqual([
const config = conman();
await config.addSource(source1).build();
config.stop();
expect(config.getObfuscate('test')).toBe('te**');
expect(config.getObfuscate('testUndefined')).toBe(undefined);
expect(config.getObfuscate('testNull')).toBe(null);
expect(config.getObfuscate('testNumber')).toBe('12**');
expect(config.getObfuscate('testBoolean')).toBe('tr**');
expect(config.getObfuscate('testObj')).toEqual({ 'va***': 'v**' });
expect(config.getObfuscate('testArr')).toEqual(['ar**', 'ar**']);
expect(config.getObfuscate(['testObj', 'test'])).toEqual([
{ 'va***': 'v**' },

@@ -201,7 +201,7 @@ 'te**'

});
const conman = Conman();
await conman.addSource(source1).build();
conman.stop();
const config = conman();
await config.addSource(source1).build();
config.stop();
expect(
conman.getObfuscate('test', { position: 'start', separator: '-' })
config.getObfuscate('test', { position: 'start', separator: '-' })
).toBe('***st');

@@ -218,6 +218,6 @@ });

});
const conman = Conman();
await conman.addSource(source1).build();
conman.stop();
expect(conman.get('test.test2.test4')).toBe(undefined);
const config = conman();
await config.addSource(source1).build();
config.stop();
expect(config.get('test.test2.test4')).toBe(undefined);
});

@@ -227,6 +227,6 @@

const source1 = source({ test: 'test async', test2: 'test2' });
const conman = Conman();
await conman.addSource(source1).build();
conman.stop();
expect(conman.get()).toEqual({ test: 'test async', test2: 'test2' });
const config = conman();
await config.addSource(source1).build();
config.stop();
expect(config.get()).toEqual({ test: 'test async', test2: 'test2' });
});

@@ -237,7 +237,7 @@

jest.spyOn(source1, 'build');
const conman = Conman({ ttl: 1000 });
await conman.addSource(source1).build();
const config = conman({ ttl: 1000 });
await config.addSource(source1).build();
await new Promise(r => setTimeout(r, 3000));
conman.stop();
config.stop();
expect(source1.build).toHaveBeenCalledTimes(3);

@@ -256,4 +256,4 @@ });

jest.spyOn(source1, 'build');
const conman = Conman(options);
await conman.addSource(source1).build();
const config = conman(options);
await config.addSource(source1).build();
expect(options.logger.log).toHaveBeenCalled();

@@ -273,6 +273,6 @@ expect(jsonfile.writeFile).toHaveBeenCalledTimes(0);

});
const conman = Conman();
const config = await conman.addSource(source1).build();
conman.stop();
expect(config).toEqual({
const config = conman();
const data = await config.addSource(source1).build();
config.stop();
expect(data).toEqual({
test: 'readfromfile'

@@ -290,6 +290,6 @@ });

});
const conman = Conman();
const config = await conman.addSource(source1).build();
conman.stop();
expect(config).toEqual({
const config = conman();
const data = await config.addSource(source1).build();
config.stop();
expect(data).toEqual({
test: 'builtfromsource'

@@ -302,9 +302,9 @@ });

jsonfile.readFile.mockRejectedValueOnce();
const conman = Conman({
const config = conman({
logEnabled: true,
logger
});
const config = await conman.addSource(source1).build();
conman.stop();
expect(config).toEqual({
const data = await config.addSource(source1).build();
config.stop();
expect(data).toEqual({
test: 'builtfromsource'

@@ -318,9 +318,9 @@ });

jsonfile.writeFile.mockRejectedValueOnce();
const conman = Conman({
const config = conman({
logEnabled: true,
logger
});
const config = await conman.addSource(source1).build();
conman.stop();
expect(config).toEqual({ test: 'test', test3: 'test3' });
const data = await config.addSource(source1).build();
config.stop();
expect(data).toEqual({ test: 'test', test3: 'test3' });
expect(logger.error).toHaveBeenCalledTimes(1);

@@ -330,22 +330,18 @@ });

it(`should create independent instances with the provider`, async () => {
const instance1 = Conman({ ttl: 0 });
const instance2 = Conman({ ttl: 0 });
const conman1 = conman({ ttl: 0 });
const conman2 = conman({ ttl: 0 });
await instance1
.addSource(source({ test: 'instance1' }))
.build();
await instance2
.addSource(source({ test: 'instance2' }))
.build();
await conman1.addSource(source({ test: 'conman1' })).build();
await conman2.addSource(source({ test: 'conman2' })).build();
expect(instance1.get()).toEqual({ test: 'instance1' });
expect(instance2.get()).toEqual({ test: 'instance2' });
expect(conman1.get()).toEqual({ test: 'conman1' });
expect(conman2.get()).toEqual({ test: 'conman2' });
instance1.reset();
expect(instance1.get()).toEqual({});
expect(instance2.get()).toEqual({ test: 'instance2' });
conman1.reset();
expect(conman1.get()).toEqual({});
expect(conman2.get()).toEqual({ test: 'conman2' });
});
it('should throw an error if the source is missing its build property', async () => {
expect(() => Conman().addSource({ type: 'source' })).toThrow();
expect(() => conman().addSource({ type: 'source' })).toThrow();
});

@@ -355,3 +351,3 @@

expect(() =>
Conman().addSource({ type: 'source', build: 'not a function' })
conman().addSource({ type: 'source', build: 'not a function' })
).toThrow();

@@ -362,3 +358,3 @@ });

expect(() =>
Conman().addSource({
conman().addSource({
build() {

@@ -365,0 +361,0 @@ return {};

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc