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

@slimio/config

Package Overview
Dependencies
Maintainers
3
Versions
27
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@slimio/config - npm Package Compare versions

Comparing version 0.1.0 to 0.2.0

CONTRIBUTING.md

0

index.d.ts

@@ -0,0 +0,0 @@ /// <reference types="node" />

8

package.json
{
"name": "@slimio/config",
"version": "0.1.0",
"version": "0.2.0",
"description": "SlimIO Reactive JSON Config loaded",

@@ -17,3 +17,4 @@ "main": "index.js",

"engines": {
"node": ">=8.11.1"
"npm": ">=6.0.0",
"node": ">=10.1.0"
},

@@ -34,3 +35,4 @@ "keywords": [

"@escommunity/minami": "^1.0.0",
"@slimio/eslint-config": "^1.1.3",
"@slimio/eslint-config": "^1.1.5",
"@types/node": "^10.3.4",
"ava": "^0.25.0",

@@ -37,0 +39,0 @@ "eslint": "^4.19.1",

@@ -20,4 +20,6 @@ # Config

Create a simple config file for your project (take this example)
## Usage example
Create a simple json file for your project (As below)
```json

@@ -31,3 +33,3 @@ {

Install and use our package like this to recover values (with commonjs).
Now create a new Configuration instance and read it

@@ -37,9 +39,89 @@ ```js

async function main() {
const myConfig = new Config("./path/to/config.json");
await myConfig.read();
const cfg = new Config("./path/to/config.json");
cfg.read().then(() => {
console.log(cfg.get("loglevel")); // stdout: 5
}).catch(console.error);
```
console.log(myConfig.get("loglevel"));
## API
### constructor<T>(configFilePath: string, options?: Config.ConstructorOptions)
Create a new Configuration instance
```js
const options = { autoReload: true };
const cfg = new Config("./path/to/file.json", options);
```
Available options are
```ts
interface ConstructorOptions {
createOnNoEntry?: boolean;
writeOnSet?: boolean;
autoReload?: boolean;
reloadDelay?: number;
defaultSchema?: object;
}
main().catch(console.error);
```
### read(defaultPayload?: T): Promise<this>;
Will trigger and read the local configuration (on disk).
```js
const cfg = new Config("./path/to/file.json");
assert.equal(cfg.configHasBeenRead, false); // true
await cfg.read();
assert.equal(cfg.configHasBeenRead, true); // true
```
Retriggering the method will made an hot-reload of all properties. For a cold reload you will have to close the configuration before.
### setupAutoReload(): void;
Setup hot reload (with a file watcher). This method is automatically triggered if the Configuration has been created with the option `autoReload` set to true.
### get<H>(fieldPath: string): H
Get a value from a key (field path).
For example, image a json file with a `foo` field
```js
const cfg = new Config("./path/to/file.json");
await cfg.read();
const fooValue = cfg.get("foo");
```
### set<H>(fieldPath: string, fieldValue: H): void;
Set a given field in the configuration
```js
const cfg = new Config("./config.json", {
createOnNoEntry: true
});
await cfg.read({ foo: "bar" });
cfg.set("foo", "hello world!");
await cfg.writeOnDisk();
```
### observableOf(fieldPath: string): ObservableLike;
Observe a given configuration key with an Observable object!
```js
const { writeFile } = require("fs").promises;
const cfg = new Config("./config.json", {
autoReload: true,
createOnNoEntry: true
});
await cfg.read({ foo: "bar" });
// Observe initial and next value(s) of foo
cfg.observableOf("foo").subscribe(console.log);
// Re-write local config file
const newPayload = { foo: "world" };
await writeFile("./config.json", JSON.stringify(newPayload, null, 4));
```
### writeOnDisk(): Promise<void>
Write the configuration on the disk
### close(): Promise<void>
Close (and write on disk) the configuration (it will close the watcher and clean all active observers).
// Require Node.JS core packages
const { parse, extname } = require("path");
const { promisify } = require("util");
const Events = require("events");
const events = require("events");
const {
access,
readFile,
writeFile,
promises: {
access,
readFile,
writeFile
},
constants: { R_OK, W_OK }
} = require("fs");
// Require third-party NPM package(s)
// Require Third-party NPM package(s)
const watcher = require("node-watch");

@@ -21,15 +22,8 @@ const is = require("@sindresorhus/is");

// Require internal dependencie(s)
// Require Internal dependencie(s)
const { formatAjvErrors } = require("./utils");
// FS Async Wrapper
const FSAsync = {
access: promisify(access),
readFile: promisify(readFile),
writeFile: promisify(writeFile)
};
// Private Config Accessors
const payload = Symbol();
const schema = Symbol();
const payload = Symbol("payload");
const schema = Symbol("schema");

@@ -39,2 +33,3 @@ /**

* @classdesc Reactive JSON Config loader class
* @extends events
* @template T

@@ -54,6 +49,8 @@ *

*
* @event reload
*
* @author Thomas GENTILHOMME <gentilhomme.thomas@gmail.com>
* @version 0.1.0
*/
class Config extends Events {
class Config extends events {

@@ -68,10 +65,21 @@ /**

* @param {Object=} options.defaultSchema Optional default Schema
* @param {Number=} [options.reloadDelay=1000] Hot reload delay
* @param {Number=} [options.reloadDelay=1000] Hot reload delay (in milliseconds)
*
* @throws {TypeError}
* @throws {Error}
*
* @version 0.1.0
*
* @example
* const cfgOptions = {
* autoReload: true,
* createOnNoEntry: true,
* writeOnSet: true,
* reloadDelay: 2000
* };
* const cfgM = new Config("./path/to/config.json", cfgOptions);
*/
constructor(configFilePath, options = {}) {
constructor(configFilePath, options = Object.create(null)) {
super();
if (is(configFilePath) !== "string") {
if (!is.string(configFilePath)) {
throw new TypeError("Config.constructor->configFilePath should be typeof <string>");

@@ -112,6 +120,14 @@ }

* @desc Get a payload Object clone (or null if the configuration has not been read yet)
*
* @version 0.1.0
*
* @example
* const cfg = new Config("./path/to/config.json");
* await cfg.read();
* const configContent = cfg.payload;
* console.log(JSON.stringify(configContent, null, 2));
*/
get payload() {
if (!this.configHasBeenRead) {
return null;
return Object.create(null);
}

@@ -131,10 +147,25 @@

* @throws {TypeError}
*
* @version 0.1.0
*
* @example
* const cfg = new Config("./path/to/config.json");
* await cfg.read();
*
* // Assign a new cfg (payload). It should match the cfg Schema (if there is any)
* try {
* cfg.payload = {
* foo: "bar"
* };
* }
* catch (error) {
* // handle error here!
* }
*/
set payload(newPayload) {
if (!this.configHasBeenRead) {
throw new Error(
"Config.payload - cannot set a new payload when the config has not been read yet!"
);
// eslint-disable-next-line max-len
throw new Error("Config.payload - cannot set a new payload when the config has not been read yet!");
}
if (is(newPayload) !== "Object") {
if (!is.object(newPayload)) {
throw new TypeError("Config.payload->newPayload should be typeof <Object>");

@@ -165,2 +196,4 @@ }

*
* @version 0.1.0
*
* @example

@@ -182,4 +215,3 @@ * const myConfig = new Config("./path/to/config.json", {

// Declare scoped variable(s) to the top
let JSONConfig;
let JSONSchema;
let JSONConfig, JSONSchema;

@@ -189,6 +221,4 @@ // Get and parse the JSON Configuration file (if exist).

try {
await FSAsync.access(this.configFile, R_OK | W_OK);
JSONConfig = JSON.parse(
await FSAsync.readFile(this.configFile)
);
await access(this.configFile, R_OK | W_OK);
JSONConfig = JSON.parse(await readFile(this.configFile));
}

@@ -199,6 +229,6 @@ catch (err) {

}
JSONConfig = is(defaultPayload) === "Object" ?
JSONConfig = is.object(defaultPayload) ?
defaultPayload :
is.nullOrUndefined(this[payload]) ? {} : this.payload;
await FSAsync.writeFile(this.configFile, JSON.stringify(JSONConfig, null, 4));
is.nullOrUndefined(this[payload]) ? Object.create(null) : this.payload;
await writeFile(this.configFile, JSON.stringify(JSONConfig, null, 4));
}

@@ -209,6 +239,4 @@

try {
await FSAsync.access(this.schemaFile, R_OK);
JSONSchema = JSON.parse(
await FSAsync.readFile(this.schemaFile)
);
await access(this.schemaFile, R_OK);
JSONSchema = JSON.parse(await readFile(this.schemaFile));
}

@@ -241,2 +269,4 @@ catch (err) {

*
* @version 0.1.0
*
* @throws {Error}

@@ -246,6 +276,7 @@ */

if (!this.configHasBeenRead) {
throw new Error(
"Config.setupAutoReaload - cannot setup autoReload when the config has not been read yet!"
);
// eslint-disable-next-line max-len
throw new Error("Config.setupAutoReaload - cannot setup autoReload when the config has not been read yet!");
}
// Return if autoReload is already actived
if (this.autoReloadActivated) {

@@ -256,3 +287,3 @@ return;

this.autoReloadActivated = true;
this.watcher = watcher(this.configFile, { delay: this.reloadDelay }, async(evt, name) => {
this.watcher = watcher(this.configFile, { delay: this.reloadDelay }, async() => {
await this.read();

@@ -275,2 +306,4 @@ this.emit("reload");

*
* @version 0.1.0
*
* @example

@@ -291,7 +324,6 @@ * const myConfig = new Config("./path/to/config.json", {

if (!this.configHasBeenRead) {
throw new Error(
"Config.get - Unable to get a key, the configuration has not been initialized yet!"
);
// eslint-disable-next-line max-len
throw new Error("Config.get - Unable to get a key, the configuration has not been initialized yet!");
}
if (is(fieldPath) !== "string") {
if (!is.string(fieldPath)) {
throw new TypeError("Config.get->fieldPath should be typeof <string>");

@@ -311,2 +343,4 @@ }

*
* @version 0.1.0
*
* @example

@@ -339,5 +373,7 @@ * const myConfig = new Config("./config.json", {

observableOf(fieldPath) {
const fieldValue = this.get(fieldPath);
return new Observable((observer) => {
// Retrieve the field value first
const fieldValue = this.get(fieldPath);
return new Observable((observer) => {
// Send it as first Observed value!
observer.next(fieldValue);

@@ -361,2 +397,4 @@ this.subscriptionObservers.push([fieldPath, observer]);

*
* @version 0.1.0
*
* @example

@@ -383,13 +421,17 @@ * const myConfig = new Config("./config.json", {

if (!this.configHasBeenRead) {
throw new Error(
"Config.set - Unable to set a key, the configuration has not been initialized yet!"
);
// eslint-disable-next-line max-len
throw new Error("Config.set - Unable to set a key, the configuration has not been initialized yet!");
}
if (is(fieldPath) !== "string") {
if (!is.string(fieldPath)) {
throw new TypeError("Config.set->fieldPath should be typeof <string>");
}
// Setup the new cfg by using the getter/setter payload
this.payload = set(this.payload, fieldPath, fieldValue);
// If writeOnSet option is actived, writeOnDisk at the next loop iteration (lazy)
if (this.writeOnSet) {
process.nextTick(this.writeOnDisk.bind(this));
setImmediate(() => {
this.writeOnDisk().catch(console.error);
});
}

@@ -406,2 +448,11 @@ }

* @throws {Error}
*
* @version 0.1.0
*
* @example
* // Config can be created with the option `writeOnSet` that enable cfg auto-writing on disk after every set!
* const cfg = new Config("./path/to/config.json");
* await cfg.read();
* cfg.set("field.path", "value");
* await cfg.writeOnDisk();
*/

@@ -413,4 +464,4 @@ async writeOnDisk() {

await FSAsync.access(this.configFile, W_OK);
await FSAsync.writeFile(this.configFile, JSON.stringify(this[payload], null, 4));
await access(this.configFile, W_OK);
await writeFile(this.configFile, JSON.stringify(this[payload], null, 4));
}

@@ -421,11 +472,18 @@

* @method close
* @desc Close the configuration (it will close the watcher and all active observers).
* @desc Close (and write on disk) the configuration (it will close the watcher and clean all active observers).
* @memberof Config#
* @returns {Promise<void>}
*
* @throws {Error}
*
* @version 0.1.0
*
* @example
* const cfg = new Config("./path/to/config.json");
* await cfg.read();
* await cfg.close();
*/
async close() {
if (!this.configHasBeenRead) {
throw new Error(
"Config.close - Cannot close unreaded configuration"
);
throw new Error("Config.close - Cannot close unreaded configuration");
}

@@ -437,5 +495,9 @@ if (this.autoReloadActivated && !this.watcher.isClosed()) {

// Write the Configuration on the disk to be safe
await this.writeOnDisk();
for (const [, subscriptionObservers] of this.subscriptionObservers) {
// Complete all observers
for (const [index, subscriptionObservers] of this.subscriptionObservers) {
subscriptionObservers.complete();
this.subscriptionObservers.splice(index, 1);
}

@@ -453,2 +515,3 @@ this.configHasBeenRead = false;

// Export class
module.exports = Config;

@@ -11,2 +11,3 @@ /**

* @function formatAjvErrors
* @memberof utils#
* @desc format ajv errors

@@ -13,0 +14,0 @@ * @param {ajv.ErrorObject[]} ajvErrors Array of ajv error Object

/* eslint no-new: off */
/* eslint max-len: off */
// Require Dependencies!
// Require Node.JS Dependencies
const {
writeFile,
unlink
} = require("fs").promises;
// Require Third-Party Dependencies!
const avaTest = require("ava");
const is = require("@sindresorhus/is");
// Require Internal Dependencies
const Config = require("../src/config.class");
const { formatAjvErrors } = require("../src/utils.js");
const { promisify } = require("util");
const {
access,
readFile,
writeFile,
unlink
} = require("fs");
// FS Async Wrapper
const FSAsync = {
access: promisify(access),
readFile: promisify(readFile),
writeFile: promisify(writeFile),
unlink: promisify(unlink)
};
const configSchemaJSON = {

@@ -46,10 +39,10 @@ title: "Config",

for (let num = 1; num < count + 1; num++) {
toDelete.push(FSAsync.unlink(`./test/config${num}.schema.json`));
toDelete.push(FSAsync.unlink(`./test/config${num}.json`));
toDelete.push(unlink(`./test/config${num}.schema.json`));
toDelete.push(unlink(`./test/config${num}.json`));
}
toDelete.push(FSAsync.unlink("./test/basicConfig.json"));
toDelete.push(FSAsync.unlink("./test/basicConfig3.json"));
toDelete.push(FSAsync.unlink("./test/basicConfig4.json"));
toDelete.push(FSAsync.unlink("./test/defaultSchemaConfig1.json"));
toDelete.push(FSAsync.unlink("./test/defaultSchemaConfig2.json"));
toDelete.push(unlink("./test/basicConfig.json"));
toDelete.push(unlink("./test/basicConfig3.json"));
toDelete.push(unlink("./test/basicConfig4.json"));
toDelete.push(unlink("./test/defaultSchemaConfig1.json"));
toDelete.push(unlink("./test/defaultSchemaConfig2.json"));

@@ -61,7 +54,7 @@ await Promise.all(toDelete);

const num = ++count;
await FSAsync.writeFile(
await writeFile(
`./test/config${num}.schema.json`,
JSON.stringify(configSchemaJSON, null, 4)
);
await FSAsync.writeFile(
await writeFile(
`./test/config${num}.json`,

@@ -156,3 +149,3 @@ JSON.stringify(configJSON, null, 4)

});
await FSAsync.writeFile(`./test/config${num}.json`, JSON.stringify(newConfigJSON, null, 4));
await writeFile(`./test/config${num}.json`, JSON.stringify(newConfigJSON, null, 4));
});

@@ -193,3 +186,3 @@ await config.close();

const payload = config.payload;
test.is(payload, null);
test.deepEqual(payload, Object.create(null));
});

@@ -229,3 +222,3 @@

};
await FSAsync.writeFile(
await writeFile(
"./test/defaultSchemaConfig1.json",

@@ -248,3 +241,3 @@ JSON.stringify(configJSON, null, 4)

avaTest("Default Schema", async(test) => {
await FSAsync.writeFile(
await writeFile(
"./test/defaultSchemaConfig2.json",

@@ -349,3 +342,3 @@ JSON.stringify(configJSON, null, 4)

await config.read(configJSON);
await FSAsync.unlink("./test/basicConfig4.json");
await unlink("./test/basicConfig4.json");
await config.read();

@@ -352,0 +345,0 @@ await config.close();

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