@mocks-server/config
Advanced tools
Comparing version 1.1.0 to 1.2.0
{ | ||
"name": "@mocks-server/config", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"description": "Modular configuration provider. Read it from file, environment and arguments", | ||
@@ -30,3 +30,2 @@ "keywords": [ | ||
"files": [ | ||
"scaffold", | ||
"src", | ||
@@ -52,3 +51,3 @@ "index.js" | ||
}, | ||
"readme": "<p align=\"center\">\n <a href=\"https://github.com/mocks-server/main/actions?query=workflow%3Abuild+branch%3Amaster\"><img src=\"https://github.com/mocks-server/main/workflows/build/badge.svg?branch=master\" alt=\"Build Status\"></a>\n <a href=\"https://codecov.io/gh/mocks-server/main\"><img src=\"https://codecov.io/gh/mocks-server/main/branch/master/graph/badge.svg?token=2S8ZR55AJV\" alt=\"Coverage\"></a>\n <a href=\"https://sonarcloud.io/project/overview?id=mocks-server_main_config\"><img src=\"https://sonarcloud.io/api/project_badges/measure?project=mocks-server_main_config&metric=alert_status\" alt=\"Quality Gate\"></a>\n <a href=\"https://www.npmjs.com/package/@mocks-server/config\"><img src=\"https://img.shields.io/npm/dm/@mocks-server/config.svg\" alt=\"Downloads\"></a>\n <a href=\"https://renovatebot.com\"><img src=\"https://img.shields.io/badge/renovate-enabled-brightgreen.svg\" alt=\"Renovate\"></a>\n <a href=\"https://github.com/mocks-server/main/blob/master/packages/config/LICENSE\"><img src=\"https://img.shields.io/npm/l/@mocks-server/config.svg\" alt=\"License\"></a>\n</p>\n\n---\n\n# Config provider\n\nModular configuration provider. __Reads__ and __validates__ configuration from:\n\n* Default option values\n* Configuration received programmatically\n* Configuration Files. Using [Cosmiconfig](https://github.com/davidtheclark/cosmiconfig)\n* Environment variables\n* Command line arguments\n\nAs a summary, it also provides:\n\n* Automatic config validation\n* Isolated configuration namespaces\n* Objects to get/set options\n* Events when any value changes\n\nDifferent namespaces can be created for each different element in the architecture. So, each different component is the unique who knows about its own options, but the user can define options for all components at a time, and using the same methods.\n\nThis module provides configuration to [Mocks Server](website-url) components and plugins, but it may be used anywhere else because it is fully configurable.\n\n## Usage\n\nA brief example:\n\n```js\nconst Config = require(\"@mocks-server/config\");\n\n// Create config\nconst config = new Config({ moduleName: \"mocks\" });\n\n// Create namespace\nconst namespace = config.addNamespace(\"fooNamespace\");\n\n// Create option\nconst option = namespace.addOption({\n name: \"fooOption\",\n type: \"string\",\n default: \"foo-value\",\n});\n\n// Load configuration\nconfig.load().then(() => {\n // Read value\n console.log(option.value);\n // Set value\n option.value = \"foo-new-value\";\n});\n\n// Listen to onChange events\noption.onChange((newValue) => {\n console.log(`Option has a new value: ${newValue}`);\n});\n```\n\n## Configuration sources\n\nOnce options and namespaces are added, and the `load` method is called, the library searches for values for the options in next sources, sorted from lower to higher priority:\n\n* __Default option value__. When the option is created, it can contain a `default` property. That value will be assigned if no other one is found.\n* __Programmatic config__. An object containing initial configuration can be provided to the `load` or `init` methods.\n* __Configuration files__. Configuration files in `process.cwd()`. [Cosmiconfig](https://github.com/davidtheclark/cosmiconfig) is used to provide this feature, so it is compatible with next files formats:\n * A `[moduleName]` property in a `package.json` file\n * A `.[moduleName]rc file with JSON or YAML syntax.`\n * A `.[moduleName]rc.json`, `.[moduleName]rc.yaml`, `.[moduleName].yml`, `.[moduleName]rc.js`, or `.[moduleName]rc.cjs` file.\n * A `[moduleName].config.js` or `[moduleName].config.cjs` CommonJS module exporting the object.\n * A `[moduleName].config.js` or `[moduleName].config.cjs` CommonJS module exporting a function. __It receives programmatic configuration as first argument__.\n * A `[moduleName].config.js` or `[moduleName].config.cjs` CommonJS module exporting an async function. __It receives programmatic configuration as first argument__.\n* __Environment variables__. Environment variables can be also used to define options values.\n* __Process arguments__. [Commander](https://github.com/tj/commander.js/) is used under the hood to get values from command line arguments and assign them to each correspondent option.\n\n## Options\n\nOptions can be added to a namespace, or to the root config object. Both `config` and namespaces have an `addOption` method for creating them. Check the [API](#api) chapter for further info.\n\nOptions can be of one of next types: `string`, `boolean`, `number`, `object` or `array`. This library automatically converts the values from command line arguments and environment variables to the expected type when possible. If the conversion is not possible or the validation fails an error is thrown. Validation errors provide enough context to users to let them know the option that failed. This library uses [`ajv`](https://github.com/ajv-validator) and [`better-ajv-errors`](https://github.com/atlassian/better-ajv-errors) for validations.\n\nHere is an example of how to add an option to the root config object, and then you have information about how the option would be set from different sources:\n\n```js\nconst config = new Config({ moduleName: \"mocks\" });\nconfig.addOption({ name: \"optionA\", type: \"string\", default: \"foo\" });\n```\n\n* __Configuration from files or programmatic__: The option must be defined at first level:\n```js\n{\n optionA: \"foo-value\"\n}\n```\n\n* __Configuration from environment variables__: The `moduleName` option must be prepended to the option name, using \"screaming snake case\".\n```sh\nMODULE_NAME_OPTION_A=\"foo-value\"\n```\n\n* __Configuration from arguments__: The argument must start by \"--\".\n```sh\n--optionA=\"foo-value\"\n```\n\n## Namespaces\n\nDifferent isolated namespaces can be created to provide configuration to different components in the architecture. For example, Mocks Server uses config namespaces in order to allow plugins to define their own options without having conflicts with core options or other plugins options.\n\n```js\nconst config = new Config({ moduleName: \"moduleName\" });\n\nconfig\n .addNamespace(\"namespaceA\")\n .addOption({ name: \"optionA\", type: \"string\", default: \"foo\" });\n```\n\nWhen a namespace is created, its name must also be used when providing the configuration to its options. Next patterns are used to define the namespace of an option:\n\n* __Configuration from files or programmatic__: The option must be defined inside an object which key corresponds to the namespace name:\n```js\n{\n namespaceA: {\n optionA: \"foo-value\"\n }\n}\n```\n\n* __Configuration from environment variables__: The `moduleName` option and the namespace name must be prepended to the option name, using \"screaming snake case\".\n```sh\nMODULE_NAME_NAMESPACE_A_OPTION_A=\"foo-value\"\n```\n\n* __Configuration from arguments__: The namespace name must be prepended to the option name, and separated by a dot. The argument must start by \"--\".\n```sh\n--namespaceA.optionA=\"foo-value\"\n```\n\n## Nested namespaces\n\nNamespaces can be nested, so they can contain also another namespaces apart of options. The previous rules about how to use namespaces when giving values to options are also valid for nested namespaces. For example:\n\n```js\nconst config = new Config({ moduleName: \"mocks\" });\n\nconfig\n .addNamespace(\"namespaceA\")\n .addNamespace(\"namespaceB\")\n .addOption({ name: \"optionA\", type: \"string\", default: \"foo\" });\n```\n\n* __Configuration from files or programmatic__: Namespace names must be nested properly.\n```js\n{\n namespaceA: {\n namespaceB: {\n optionA: \"foo-value\"\n }\n }\n}\n```\n\n* __Configuration from environment variables__: All parent namespace names must be prepended:\n\n```sh\nMOCKS_NAMESPACE_A_NAMESPACE_B_OPTION_A=\"foo-value\"\n```\n\n* __Configuration from arguments__: All parent namespace names must be prepended and separated by a dot:\n```sh\n--namespaceA.namespaceB.optionA=\"foo-value\"\n```\n\n## Option types\n\n### __String__\n\nOptions of type `string` are not parsed in any way.\n\n```js\nconst config = new Config({ moduleName: \"moduleName\" });\nconst option = config.addOption({\n name: \"optionA\",\n type: \"string\",\n});\nawait config.load();\n```\n\nExamples about how to define options of type `string` from sources:\n\n* __Programmatic or configuration files__: `{\"optionA\": \"foo-value\"}`\n* __Environment variables__: `MODULE_NAME_OPTION_A=foo-value`\n* __Arguments__: `node myProgram.js --optionA=foo-value`\n\n### __Boolean__\n\nOptions of type `boolean` are parsed when they are defined in environment variables, and they have a special format when defined in arguments:\n\n```js\nconst option = config.addOption({\n name: \"optionA\",\n type: \"boolean\",\n default: true,\n});\nawait config.load();\n```\n\nExamples about how to define options of type `string` from sources:\n\n* __Programmatic or configuration files__: `{\"optionA\": false}`\n* __Environment variables__:\n * `0` and `false` strings will be parsed into boolean `false`: `MODULE_NAME_OPTION_A=false`, or `MODULE_NAME_OPTION_A=0`.\n * Any other value will be considered `true`: `MODULE_NAME_OPTION_A=true`, `MODULE_NAME_OPTION_A=1` or `MODULE_NAME_OPTION_A=foo`.\n* __Arguments__:\n * If the option has default value `true`, then the `--no-` prefix added to the option name will produce the option to have a `false` value: `node myProgram.js --no-optionA`\n * If the option has a `false` default value, then the option name will be set it as `true`: `node myProgram.js --optionA`\n\n### __Number__\n\nOptions of type `number` are parsed when they are defined in environment variables or command line arguments.\n\n```js\nconst option = config.addOption({\n name: \"optionA\",\n type: \"number\"\n});\nawait config.load();\n```\n\nExamples about how to define options of type `number` from sources:\n\n* __Programmatic or configuration files__: `{\"optionA\": 5}`\n* __Environment variables__: `MODULE_NAME_OPTION_A=5`, or `MODULE_NAME_OPTION_A=5.65`.\n* __Arguments__: `node myProgram.js --optionA=6.78`\n\n### __Object__\n\nOptions of type `object` are parsed from JSON strings when they are defined in environment variables or command line arguments. __It is important to mention that objects are merged when they are defined in different sources when loading the configuration.__\n\n```js\nconst option = config.addOption({\n name: \"optionA\",\n type: \"object\"\n});\nawait config.load();\n```\n\nExamples about how to define options of type `object` from sources:\n\n* __Programmatic or configuration files__: `{\"optionA\": { foo: \"foo\", foo2: [\"1\", 2 ]}}`\n* __Environment variables__: `MODULE_NAME_OPTION_A={\"foo\":\"foo\",\"foo2\":[\"1\",2]}}`\n* __Arguments__: `node myProgram.js --optionA={\"foo\":\"foo\",\"foo2\":[\"1\",2]}}`\n\n### __Array__\n\nOptions of type `array` are parsed from JSON strings when they are defined in environment variables. For defining them in command line arguments, multiple arguments should be provided. __As in the case of objects, arrays are merged when they are defined in multiple sources when loading the configuration__. The result of defining it environment variables and in arguments would be both arrays concated, for example. This behavior can be disabled using the `mergeArrays` option of the [`Config` class](#config).\n\n```js\nconst option = config.addOption({\n name: \"optionA\",\n type: \"array\",\n itemsType: \"string\"\n});\nawait config.load();\n```\n\nExamples about how to define options of type `object` from sources:\n\n* __Programmatic or configuration files__: `{\"optionA\": [\"foo\",\"foo2\"]}`\n* __Environment variables__: `MODULE_NAME_OPTION_A=[\"foo\",\"foo2\"]`\n* __Arguments__: A commander variadic option is created under the hood to get the values, so array items have to be defined in separated arguments. Read the [commander docs for further info](https://github.com/tj/commander.js/#variadic-option). `node myProgram.js --optionA foo foo2`\n\n__The contents of the array are also converted to its correspondent type when the `itemsType` option is provided.__\n\n## Built-in options\n\nThe library registers some options that can be used to determine the behavior of the library itself. As the rest of the configuration created by the library, these options can be set using configuration file, environment variables, command line arguments, etc. But there are some of them that can be defined only in some specific sources because they affect to reading that sources or not.\n\nAll of the built-in options are defined in the `config` namespace:\n\n* __`config.readFile`__ _(Boolean)_: _Default: `true`_. Determines whether the configuration file should be read or not. Obviously, it would be ignored if it is defined in the configuration file.\n* __`config.readArguments`__ _(Boolean)_: _Default: `true`_. Determines whether the arguments are read or not. It can be defined only using programmatic configuration.\n* __`config.readEnvironment`__ _(Boolean)_: _Default: `true`_. Determines whether the environment variables are read or not. It can be defined using programmatic configuration or command line arguments.\n* __`config.fileSearchPlaces`__ _(Array)_: _Default from cosmiconfig_. An array of places to search for the configuration file. It can be defined in any source, except configuration files.\n* __`config.allowUnknownArguments`__ _(Boolean)_: _Default `false`_. An array of places to search for the configuration file. It can be defined in any source, except configuration files.\n\n## Lifecycle\n\nOnce the `config` instance has been created, normally you should only call to the `config.load` method to load all of the configuration, apply the validations and let the library start emitting events. But for more complex use cases there is available also another method: `config.init`.\n\nYou can call to the `config.init` method at any time before calling to `config.load`, and all configuration sources will be preloaded and values will be assigned to the options that have been already created. In this step, the validation is not executed, so you can add more namespaces or options based on some of that configuration afterwards. For example, Mocks Server first load the configuration that defines the plugins to be loaded using the `init` method, then it loads them and let them add their own options, and then it executes the `config.load` method. In this step the validation is executed, so unknown configuration properties would produce an error. Option events are not emitted until the `load` method has finished.\n\n## API\n\n### Config\n\n```js\nconst config = new Config({ moduleName: \"mocks\", mergeArrays: false });\n``` \n\n* __`Config(options)`__. Returns a `config` instance.\n \n * __`options`__ _(Object)_: Containing any of next properties:\n * __`moduleName`__: Used as prefix for environment variables names and config file names.\n * __`mergeArrays`__: _Default `true`_. When an option is of type `array` or `object`, this option defines whether arrays with different values coming from different sources are concated or not. If not, the value defined in the source with higher priority would be used.\n\n### Config instance\n\n* __`init(programmaticConfig)`__: _Async_. Read configuration and assign it to the correspondent options but do not execute validation. Allows to read partial configuration before adding more namespaces or options. Events are not still emitted.\n * `programmaticConfig` _(Object)_: Optional. Initial configuration to be set in options. It overwrite option defaults, but it is overwritten by config from files, environment and arguments.\n* __`load(programmaticConfig)`__: _Async_. Assign configuration to the correspondent options. It executes the `init` method internally if it was not done before.\n * `programmaticConfig` _(Object)_: Optional. If `init` was called before, it is ignored, otherwise, it is passed to the `init` method.\n* __`addNamespace(name)`__: Add namespace to the root. Returns a [namespace instance](#namespace-instance).\n * `name` _(String)_: Name for the namespace.\n* __`addOption(optionProperties)`__: Equivalent to the `addOption` method in namespaces, but it add the option to the root. Returns an [option instance](#option-instance).\n * `optionProperties` _(Object)_: Properties defining the option. See the `addOption` method in namespaces for further info.\n* __`addOptions(optionsProperties)`__: Add many options. Returns an array of [option instances](#option-instance).\n * `optionsProperties` _(Array)_: Array of `optionProperties`.\n* __`namespace(name)`__: Returns the [namespace instance](#namespace-instance) in the root config with name equal to `name`.\n* __`option(optionName)`__: Returns the [option instances](#option-instance) in the root config with name equal to `optionName`.\n* __`set(configuration)`__: Set configuration properties to each correspondent namespace and options.\n * `configuration` _(Object)_: Object with programmatic configuration. Levels in the object correspond to namespaces names, and last level keys correspond to option names.\n* __`validate(configuration, options)`__: Allows to prevalidate a configuration before setting it, for example. It returns an object with `valid` and `errors` properties. See [AJV docs for further info](https://ajv.js.org/guide/getting-started.html#basic-data-validation).\n * `configuration` _(Object)_: Object with configuration. Levels in the object correspond to namespaces names, and last level keys correspond to option names.\n * `options` _(Object)_: Object with extra options for validation:\n * `allowAdditionalProperties` _(Boolean)_: _Default `false`_. If true, additional properties in the configuration would not produce validation errors.\n* __`value`__: Getter returning the current values from all namespaces and options as an object. Levels in the object correspond to namespaces names, and last level keys correspond to option names. It can be also used as setter as an alias of the `set` method, with default options.\n* __`loadedFile`__: Getter returning the file path of the loaded configuration file. It returns `null` if no configuration file was loaded.\n* __`namespaces`__: Getter returning array with all root namespaces.\n* __`options`__: Getter returning array with all root options.\n* __`programmaticLoadedValues`__: Getter returning initial values from programmatic config. Useful for debugging purposes.\n* __`fileLoadedValues`__: Getter returning initial values from file config. Useful for debugging purposes.\n* __`envLoadedValues`__: Getter returning initial values from environment config. Useful for debugging purposes.\n* __`argsLoadedValues`__: Getter returning initial values from arguments. Useful for debugging purposes.\n\n### Namespace instance\n\n```js\nconst namespace = config.addNamespace(\"name\");\n``` \n\n* __`addNamespace(name)`__: Add another namespace to the current namespace. Returns a [namespace instance](#namespace-instance).\n * `name` _(String)_: Name for the namespace.\n* __`addOption(optionProperties)`__: Adds an option to the namespace. Returns an [option instance](#option-instance).\n * `optionProperties` _(Object)_: Properties defining the option.\n * __`name`__ _(String)_: Name for the option.\n * __`description`__ _(String)_: _Optional_. Used in help, traces, etc.\n * __`type`__ _(String)_. One of _`string`_, _`boolean`_, _`number`_, _`array`_ or _`object`_. Used to apply type validation when loading configuration and in `option.value` setter.\n * __`itemsType`__ _(String)_. Can be defined only when `type` is `array`. It must be one of _`string`_, _`boolean`_, _`number`_ or _`object`_.\n * __`default`__ - _Optional_. Default value. Its type depends on the `type` option.\n * __`extraData`__ - _(Object)_. _Optional_. Useful to store any extra data you want in the option. For example, Mocks Server uses it to define whether an option must be written when creating the configuration scaffold or not.\n* __`addOptions(optionsProperties)`__: Add many options. Returns an array of [option instances](#option-instance).\n * `optionsProperties` _(Array)_: Array of `optionProperties`.\n* __`namespace(name)`__: Returns the [namespace instance](#namespace-instance) in this namespace with name equal to `name`.\n* __`option(optionName)`__: Returns the [option instances](#option-instance) in this namespace with name equal to `optionName`.\n* __`name`__: Getter returning the namespace name.\n* __`namespaces`__: Getter returning an array with children namespaces.\n* __`options`__: Getter returning an array object with children options.\n* __`set(configuration)`__: Set configuration properties to each correspondent child namespace and options.\n * `configuration` _(Object)_: Object with configuration. Levels in the object correspond to child namespaces names, and last level keys correspond to option names.\n* __`value`__: Getter returning the current values from all child namespaces and options as an object. Levels in the object correspond to namespaces names, and last level keys correspond to option names. It can be also used as setter as an alias of the `set` method, with default options.\n* __`root`__: Getter returning the root configuration object.\n\n### Option instance\n\n```js\nconst option = namespace.addOption(\"name\");\nconst rootOption = config.addOption(\"name2\");\n``` \n\n* __`value`__: Getter of the current value. It can be also used as setter as an alias of the `set` method, with default options..\n* __`set(value)`__: Set value.\n* __`onChange(callback)`__: Allows to add a listener that will be executed whenever the value changes. It only emit events after calling to the `config.start` method. __It returns a function that removes the listener once executed__.\n * `callback(value)` _(Function)_: Callback to be executed whenever the option value changes. It receives the new value as first argument.\n* __`name`__: Getter returning the option name.\n* __`type`__: Getter returning the option type.\n* __`description`__: Getter returning the option description.\n* __`extraData`__: Getter returning the option extra data.\n* __`default`__: Getter returning the option default value.\n\n[website-url]: https://www.mocks-server.org\n[logo-url]: https://www.mocks-server.org/img/logo_120.png\n[main-url]: https://www.npmjs.com/package/@mocks-server/main\n" | ||
"readme": "<p align=\"center\">\n <a href=\"https://github.com/mocks-server/main/actions?query=workflow%3Abuild+branch%3Amaster\"><img src=\"https://github.com/mocks-server/main/workflows/build/badge.svg?branch=master\" alt=\"Build Status\"></a>\n <a href=\"https://codecov.io/gh/mocks-server/main\"><img src=\"https://codecov.io/gh/mocks-server/main/branch/master/graph/badge.svg?token=2S8ZR55AJV\" alt=\"Coverage\"></a>\n <a href=\"https://sonarcloud.io/project/overview?id=mocks-server_main_config\"><img src=\"https://sonarcloud.io/api/project_badges/measure?project=mocks-server_main_config&metric=alert_status\" alt=\"Quality Gate\"></a>\n <a href=\"https://www.npmjs.com/package/@mocks-server/config\"><img src=\"https://img.shields.io/npm/dm/@mocks-server/config.svg\" alt=\"Downloads\"></a>\n <a href=\"https://renovatebot.com\"><img src=\"https://img.shields.io/badge/renovate-enabled-brightgreen.svg\" alt=\"Renovate\"></a>\n <a href=\"https://github.com/mocks-server/main/blob/master/packages/config/LICENSE\"><img src=\"https://img.shields.io/npm/l/@mocks-server/config.svg\" alt=\"License\"></a>\n</p>\n\n---\n\n# Config provider\n\nModular configuration provider. __Reads__ and __validates__ configuration from:\n\n* Default option values\n* Configuration received programmatically\n* Configuration Files. Using [Cosmiconfig](https://github.com/davidtheclark/cosmiconfig)\n* Environment variables\n* Command line arguments\n\nAs a summary, it also provides:\n\n* Automatic config validation\n* Isolated configuration namespaces\n* Objects to get/set options\n* Events when any value changes\n\nDifferent namespaces can be created for each different element in the architecture. So, each different component is the unique who knows about its own options, but the user can define options for all components at a time, and using the same methods.\n\nThis module provides configuration to [Mocks Server](website-url) components and plugins, but it may be used anywhere else because it is fully configurable.\n\n## Usage\n\nA brief example:\n\n```js\nconst Config = require(\"@mocks-server/config\");\n\n// Create config\nconst config = new Config({ moduleName: \"mocks\" });\n\n// Create namespace\nconst namespace = config.addNamespace(\"fooNamespace\");\n\n// Create option\nconst option = namespace.addOption({\n name: \"fooOption\",\n type: \"string\",\n default: \"foo-value\",\n});\n\n// Load configuration\nconfig.load().then(() => {\n // Read value\n console.log(option.value);\n // Set value\n option.value = \"foo-new-value\";\n});\n\n// Listen to onChange events\noption.onChange((newValue) => {\n console.log(`Option has a new value: ${newValue}`);\n});\n```\n\n## Configuration sources\n\nOnce options and namespaces are added, and the `load` method is called, the library searches for values for the options in next sources, sorted from lower to higher priority:\n\n* __Default option value__. When the option is created, it can contain a `default` property. That value will be assigned if no other one is found.\n* __Programmatic config__. An object containing initial configuration can be provided to the `load` or `init` methods.\n* __Configuration files__. Configuration files in `process.cwd()`. [Cosmiconfig](https://github.com/davidtheclark/cosmiconfig) is used to provide this feature, so it is compatible with next files formats:\n * A `[moduleName]` property in a `package.json` file\n * A `.[moduleName]rc file with JSON or YAML syntax.`\n * A `.[moduleName]rc.json`, `.[moduleName]rc.yaml`, `.[moduleName].yml`, `.[moduleName]rc.js`, or `.[moduleName]rc.cjs` file.\n * A `[moduleName].config.js` or `[moduleName].config.cjs` CommonJS module exporting the object.\n * A `[moduleName].config.js` or `[moduleName].config.cjs` CommonJS module exporting a function. __It receives programmatic configuration as first argument__.\n * A `[moduleName].config.js` or `[moduleName].config.cjs` CommonJS module exporting an async function. __It receives programmatic configuration as first argument__.\n* __Environment variables__. Environment variables can be also used to define options values.\n* __Process arguments__. [Commander](https://github.com/tj/commander.js/) is used under the hood to get values from command line arguments and assign them to each correspondent option.\n\n## Options\n\nOptions can be added to a namespace, or to the root config object. Both `config` and namespaces have an `addOption` method for creating them. Check the [API](#api) chapter for further info.\n\nOptions can be of one of next types: `string`, `boolean`, `number`, `object` or `array`. This library automatically converts the values from command line arguments and environment variables to the expected type when possible. If the conversion is not possible or the validation fails an error is thrown. Validation errors provide enough context to users to let them know the option that failed. This library uses [`ajv`](https://github.com/ajv-validator) and [`better-ajv-errors`](https://github.com/atlassian/better-ajv-errors) for validations.\n\nHere is an example of how to add an option to the root config object, and then you have information about how the option would be set from different sources:\n\n```js\nconst config = new Config({ moduleName: \"mocks\" });\nconfig.addOption({ name: \"optionA\", type: \"string\", default: \"foo\" });\n```\n\n* __Configuration from files or programmatic__: The option must be defined at first level:\n```js\n{\n optionA: \"foo-value\"\n}\n```\n\n* __Configuration from environment variables__: The `moduleName` option must be prepended to the option name, using \"screaming snake case\".\n```sh\nMODULE_NAME_OPTION_A=\"foo-value\"\n```\n\n* __Configuration from arguments__: The argument must start by \"--\".\n```sh\n--optionA=\"foo-value\"\n```\n\n## Namespaces\n\nDifferent isolated namespaces can be created to provide configuration to different components in the architecture. For example, Mocks Server uses config namespaces in order to allow plugins to define their own options without having conflicts with core options or other plugins options.\n\n```js\nconst config = new Config({ moduleName: \"moduleName\" });\n\nconfig\n .addNamespace(\"namespaceA\")\n .addOption({ name: \"optionA\", type: \"string\", default: \"foo\" });\n```\n\nWhen a namespace is created, its name must also be used when providing the configuration to its options. Next patterns are used to define the namespace of an option:\n\n* __Configuration from files or programmatic__: The option must be defined inside an object which key corresponds to the namespace name:\n```js\n{\n namespaceA: {\n optionA: \"foo-value\"\n }\n}\n```\n\n* __Configuration from environment variables__: The `moduleName` option and the namespace name must be prepended to the option name, using \"screaming snake case\".\n```sh\nMODULE_NAME_NAMESPACE_A_OPTION_A=\"foo-value\"\n```\n\n* __Configuration from arguments__: The namespace name must be prepended to the option name, and separated by a dot. The argument must start by \"--\".\n```sh\n--namespaceA.optionA=\"foo-value\"\n```\n\n## Nested namespaces\n\nNamespaces can be nested, so they can contain also another namespaces apart of options. The previous rules about how to use namespaces when giving values to options are also valid for nested namespaces. For example:\n\n```js\nconst config = new Config({ moduleName: \"mocks\" });\n\nconfig\n .addNamespace(\"namespaceA\")\n .addNamespace(\"namespaceB\")\n .addOption({ name: \"optionA\", type: \"string\", default: \"foo\" });\n```\n\n* __Configuration from files or programmatic__: Namespace names must be nested properly.\n```js\n{\n namespaceA: {\n namespaceB: {\n optionA: \"foo-value\"\n }\n }\n}\n```\n\n* __Configuration from environment variables__: All parent namespace names must be prepended:\n\n```sh\nMOCKS_NAMESPACE_A_NAMESPACE_B_OPTION_A=\"foo-value\"\n```\n\n* __Configuration from arguments__: All parent namespace names must be prepended and separated by a dot:\n```sh\n--namespaceA.namespaceB.optionA=\"foo-value\"\n```\n\n## Option types\n\n### __String__\n\nOptions of type `string` are not parsed in any way.\n\n```js\nconst config = new Config({ moduleName: \"moduleName\" });\nconst option = config.addOption({\n name: \"optionA\",\n type: \"string\",\n});\nawait config.load();\n```\n\nExamples about how to define options of type `string` from sources:\n\n* __Programmatic or configuration files__: `{\"optionA\": \"foo-value\"}`\n* __Environment variables__: `MODULE_NAME_OPTION_A=foo-value`\n* __Arguments__: `node myProgram.js --optionA=foo-value`\n\n### __Boolean__\n\nOptions of type `boolean` are parsed when they are defined in environment variables, and they have a special format when defined in arguments:\n\n```js\nconst option = config.addOption({\n name: \"optionA\",\n type: \"boolean\",\n default: true,\n});\nawait config.load();\n```\n\nExamples about how to define options of type `string` from sources:\n\n* __Programmatic or configuration files__: `{\"optionA\": false}`\n* __Environment variables__:\n * `0` and `false` strings will be parsed into boolean `false`: `MODULE_NAME_OPTION_A=false`, or `MODULE_NAME_OPTION_A=0`.\n * Any other value will be considered `true`: `MODULE_NAME_OPTION_A=true`, `MODULE_NAME_OPTION_A=1` or `MODULE_NAME_OPTION_A=foo`.\n* __Arguments__:\n * If the option has default value `true`, then the `--no-` prefix added to the option name will produce the option to have a `false` value: `node myProgram.js --no-optionA`\n * If the option has a `false` default value, then the option name will be set it as `true`: `node myProgram.js --optionA`\n\n### __Number__\n\nOptions of type `number` are parsed when they are defined in environment variables or command line arguments.\n\n```js\nconst option = config.addOption({\n name: \"optionA\",\n type: \"number\"\n});\nawait config.load();\n```\n\nExamples about how to define options of type `number` from sources:\n\n* __Programmatic or configuration files__: `{\"optionA\": 5}`\n* __Environment variables__: `MODULE_NAME_OPTION_A=5`, or `MODULE_NAME_OPTION_A=5.65`.\n* __Arguments__: `node myProgram.js --optionA=6.78`\n\n### __Object__\n\nOptions of type `object` are parsed from JSON strings when they are defined in environment variables or command line arguments. __It is important to mention that objects are merged when they are defined in different sources when loading the configuration.__\n\n```js\nconst option = config.addOption({\n name: \"optionA\",\n type: \"object\"\n});\nawait config.load();\n```\n\nExamples about how to define options of type `object` from sources:\n\n* __Programmatic or configuration files__: `{\"optionA\": { foo: \"foo\", foo2: [\"1\", 2 ]}}`\n* __Environment variables__: `MODULE_NAME_OPTION_A={\"foo\":\"foo\",\"foo2\":[\"1\",2]}}`\n* __Arguments__: `node myProgram.js --optionA={\"foo\":\"foo\",\"foo2\":[\"1\",2]}}`\n\n### __Array__\n\nOptions of type `array` are parsed from JSON strings when they are defined in environment variables. For defining them in command line arguments, multiple arguments should be provided. __As in the case of objects, arrays are merged when they are defined in multiple sources when loading the configuration__. The result of defining it environment variables and in arguments would be both arrays concated, for example. This behavior can be disabled using the `mergeArrays` option of the [`Config` class](#config).\n\n```js\nconst option = config.addOption({\n name: \"optionA\",\n type: \"array\",\n itemsType: \"string\"\n});\nawait config.load();\n```\n\nExamples about how to define options of type `object` from sources:\n\n* __Programmatic or configuration files__: `{\"optionA\": [\"foo\",\"foo2\"]}`\n* __Environment variables__: `MODULE_NAME_OPTION_A=[\"foo\",\"foo2\"]`\n* __Arguments__: A commander variadic option is created under the hood to get the values, so array items have to be defined in separated arguments. Read the [commander docs for further info](https://github.com/tj/commander.js/#variadic-option). `node myProgram.js --optionA foo foo2`\n\n__The contents of the array are also converted to its correspondent type when the `itemsType` option is provided.__\n\n## Built-in options\n\nThe library registers some options that can be used to determine the behavior of the library itself. As the rest of the configuration created by the library, these options can be set using configuration file, environment variables, command line arguments, etc. But there are some of them that can be defined only in some specific sources because they affect to reading that sources or not.\n\nAll of the built-in options are defined in the `config` namespace:\n\n* __`config.readFile`__ _(Boolean)_: _Default: `true`_. Determines whether the configuration file should be read or not. Obviously, it would be ignored if it is defined in the configuration file.\n* __`config.readArguments`__ _(Boolean)_: _Default: `true`_. Determines whether the arguments are read or not. It can be defined only using programmatic configuration.\n* __`config.readEnvironment`__ _(Boolean)_: _Default: `true`_. Determines whether the environment variables are read or not. It can be defined using programmatic configuration or command line arguments.\n* __`config.fileSearchPlaces`__ _(Array)_: _Default from cosmiconfig_. An array of places to search for the configuration file. It can be defined in any source, except configuration files.\n* __`config.allowUnknownArguments`__ _(Boolean)_: _Default `false`_. An array of places to search for the configuration file. It can be defined in any source, except configuration files.\n\n## Lifecycle\n\nOnce the `config` instance has been created, normally you should only call to the `config.load` method to load all of the configuration, apply the validations and let the library start emitting events. But for more complex use cases there is available also another method: `config.init`.\n\nYou can call to the `config.init` method at any time before calling to `config.load`, and all configuration sources will be preloaded and values will be assigned to the options that have been already created. In this step, the validation is not executed, so you can add more namespaces or options based on some of that configuration afterwards. For example, Mocks Server first load the configuration that defines the plugins to be loaded using the `init` method, then it loads them and let them add their own options, and then it executes the `config.load` method. In this step the validation is executed, so unknown configuration properties would produce an error. Option events are not emitted until the `load` method has finished.\n\n## API\n\n### Config\n\n```js\nconst config = new Config({ moduleName: \"mocks\", mergeArrays: false });\n``` \n\n* __`Config(options)`__. Returns a `config` instance.\n \n * __`options`__ _(Object)_: Containing any of next properties:\n * __`moduleName`__: Used as prefix for environment variables names and config file names.\n * __`mergeArrays`__: _Default `true`_. When an option is of type `array` or `object`, this option defines whether arrays with different values coming from different sources are concated or not. If not, the value defined in the source with higher priority would be used.\n\n### Config instance\n\n* __`init(programmaticConfig)`__: _Async_. Read configuration and assign it to the correspondent options but do not execute validation. Allows to read partial configuration before adding more namespaces or options. Events are not still emitted.\n * `programmaticConfig` _(Object)_: Optional. Initial configuration to be set in options. It overwrite option defaults, but it is overwritten by config from files, environment and arguments.\n* __`load(programmaticConfig)`__: _Async_. Assign configuration to the correspondent options. It executes the `init` method internally if it was not done before.\n * `programmaticConfig` _(Object)_: Optional. If `init` was called before, it is ignored, otherwise, it is passed to the `init` method.\n* __`addNamespace(name)`__: Add namespace to the root. Returns a [namespace instance](#namespace-instance).\n * `name` _(String)_: Name for the namespace.\n* __`addOption(optionProperties)`__: Equivalent to the `addOption` method in namespaces, but it add the option to the root. Returns an [option instance](#option-instance).\n * `optionProperties` _(Object)_: Properties defining the option. See the `addOption` method in namespaces for further info.\n* __`addOptions(optionsProperties)`__: Add many options. Returns an array of [option instances](#option-instance).\n * `optionsProperties` _(Array)_: Array of `optionProperties`.\n* __`namespace(name)`__: Returns the [namespace instance](#namespace-instance) in the root config with name equal to `name`.\n* __`option(optionName)`__: Returns the [option instances](#option-instance) in the root config with name equal to `optionName`.\n* __`set(configuration)`__: Set configuration properties to each correspondent namespace and options.\n * `configuration` _(Object)_: Object with programmatic configuration. Levels in the object correspond to namespaces names, and last level keys correspond to option names.\n* __`validate(configuration, options)`__: Allows to prevalidate a configuration before setting it, for example. It returns an object with `valid` and `errors` properties. See [AJV docs for further info](https://ajv.js.org/guide/getting-started.html#basic-data-validation).\n * `configuration` _(Object)_: Object with configuration. Levels in the object correspond to namespaces names, and last level keys correspond to option names.\n * `options` _(Object)_: Object with extra options for validation:\n * `allowAdditionalProperties` _(Boolean)_: _Default `false`_. If true, additional properties in the configuration would not produce validation errors.\n* __`getValidationSchema(options)`__: Returns a validation schema compatible with AJV for validating the configuration of all nested namespaces.\n * `options` _(Object)_: Object with extra options for validation:\n * `allowAdditionalProperties` _(Boolean)_: _Default `false`_. If true, the validation schema will allow additional properties.\n* __`value`__: Getter returning the current values from all namespaces and options as an object. Levels in the object correspond to namespaces names, and last level keys correspond to option names. It can be also used as setter as an alias of the `set` method, with default options.\n* __`loadedFile`__: Getter returning the file path of the loaded configuration file. It returns `null` if no configuration file was loaded.\n* __`namespaces`__: Getter returning array with all root namespaces.\n* __`options`__: Getter returning array with all root options.\n* __`programmaticLoadedValues`__: Getter returning initial values from programmatic config. Useful for debugging purposes.\n* __`fileLoadedValues`__: Getter returning initial values from file config. Useful for debugging purposes.\n* __`envLoadedValues`__: Getter returning initial values from environment config. Useful for debugging purposes.\n* __`argsLoadedValues`__: Getter returning initial values from arguments. Useful for debugging purposes.\n\n### Namespace instance\n\n```js\nconst namespace = config.addNamespace(\"name\");\n``` \n\n* __`addNamespace(name)`__: Add another namespace to the current namespace. Returns a [namespace instance](#namespace-instance).\n * `name` _(String)_: Name for the namespace.\n* __`addOption(optionProperties)`__: Adds an option to the namespace. Returns an [option instance](#option-instance).\n * `optionProperties` _(Object)_: Properties defining the option.\n * __`name`__ _(String)_: Name for the option.\n * __`description`__ _(String)_: _Optional_. Used in help, traces, etc.\n * __`type`__ _(String)_. One of _`string`_, _`boolean`_, _`number`_, _`array`_ or _`object`_. Used to apply type validation when loading configuration and in `option.value` setter.\n * __`itemsType`__ _(String)_. Can be defined only when `type` is `array`. It must be one of _`string`_, _`boolean`_, _`number`_ or _`object`_.\n * __`default`__ - _Optional_. Default value. Its type depends on the `type` option.\n * __`extraData`__ - _(Object)_. _Optional_. Useful to store any extra data you want in the option. For example, Mocks Server uses it to define whether an option must be written when creating the configuration scaffold or not.\n* __`addOptions(optionsProperties)`__: Add many options. Returns an array of [option instances](#option-instance).\n * `optionsProperties` _(Array)_: Array of `optionProperties`.\n* __`namespace(name)`__: Returns the [namespace instance](#namespace-instance) in this namespace with name equal to `name`.\n* __`option(optionName)`__: Returns the [option instances](#option-instance) in this namespace with name equal to `optionName`.\n* __`name`__: Getter returning the namespace name.\n* __`namespaces`__: Getter returning an array with children namespaces.\n* __`options`__: Getter returning an array object with children options.\n* __`set(configuration)`__: Set configuration properties to each correspondent child namespace and options.\n * `configuration` _(Object)_: Object with configuration. Levels in the object correspond to child namespaces names, and last level keys correspond to option names.\n* __`value`__: Getter returning the current values from all child namespaces and options as an object. Levels in the object correspond to namespaces names, and last level keys correspond to option names. It can be also used as setter as an alias of the `set` method, with default options.\n* __`root`__: Getter returning the root configuration object.\n\n### Option instance\n\n```js\nconst option = namespace.addOption(\"name\");\nconst rootOption = config.addOption(\"name2\");\n``` \n\n* __`value`__: Getter of the current value. It can be also used as setter as an alias of the `set` method, with default options..\n* __`set(value)`__: Set value.\n* __`onChange(callback)`__: Allows to add a listener that will be executed whenever the value changes. It only emit events after calling to the `config.start` method. __It returns a function that removes the listener once executed__.\n * `callback(value)` _(Function)_: Callback to be executed whenever the option value changes. It receives the new value as first argument.\n* __`name`__: Getter returning the option name.\n* __`type`__: Getter returning the option type.\n* __`description`__: Getter returning the option description.\n* __`extraData`__: Getter returning the option extra data.\n* __`default`__: Getter returning the option default value.\n* __`hasBeenSet`__: Returns true if the option value has been actively set, no matter the source or method used to set it. Otherwise returns false.\n\n[website-url]: https://www.mocks-server.org\n[logo-url]: https://www.mocks-server.org/img/logo_120.png\n" | ||
} |
@@ -334,2 +334,5 @@ <p align="center"> | ||
* `allowAdditionalProperties` _(Boolean)_: _Default `false`_. If true, additional properties in the configuration would not produce validation errors. | ||
* __`getValidationSchema(options)`__: Returns a validation schema compatible with AJV for validating the configuration of all nested namespaces. | ||
* `options` _(Object)_: Object with extra options for validation: | ||
* `allowAdditionalProperties` _(Boolean)_: _Default `false`_. If true, the validation schema will allow additional properties. | ||
* __`value`__: Getter returning the current values from all namespaces and options as an object. Levels in the object correspond to namespaces names, and last level keys correspond to option names. It can be also used as setter as an alias of the `set` method, with default options. | ||
@@ -388,5 +391,5 @@ * __`loadedFile`__: Getter returning the file path of the loaded configuration file. It returns `null` if no configuration file was loaded. | ||
* __`default`__: Getter returning the option default value. | ||
* __`hasBeenSet`__: Returns true if the option value has been actively set, no matter the source or method used to set it. Otherwise returns false. | ||
[website-url]: https://www.mocks-server.org | ||
[logo-url]: https://www.mocks-server.org/img/logo_120.png | ||
[main-url]: https://www.npmjs.com/package/@mocks-server/main |
@@ -8,3 +8,3 @@ const deepMerge = require("deepmerge"); | ||
const { types, avoidArraysMerge } = require("./types"); | ||
const { validateConfigAndThrow, validateConfig } = require("./validation"); | ||
const { validateConfigAndThrow, validateConfig, getValidationSchema } = require("./validation"); | ||
const { checkNamespaceName, findObjectWithName, getNamespacesValues } = require("./namespaces"); | ||
@@ -119,2 +119,9 @@ | ||
getValidationSchema({ allowAdditionalProperties = false } = {}) { | ||
return getValidationSchema({ | ||
namespaces: this._namespaces, | ||
allowAdditionalProperties, | ||
}); | ||
} | ||
_validateAndThrow({ allowAdditionalProperties }) { | ||
@@ -121,0 +128,0 @@ validateConfigAndThrow(this._config, { |
@@ -22,2 +22,3 @@ const EventEmitter = require("events"); | ||
this._eventsStarted = false; | ||
this._hasBeenSet = false; | ||
} | ||
@@ -93,2 +94,3 @@ | ||
if (!isUndefined(value)) { | ||
this._hasBeenSet = true; | ||
if (merge && this.type === types.OBJECT) { | ||
@@ -108,4 +110,8 @@ this._merge(value); | ||
} | ||
get hasBeenSet() { | ||
return this._hasBeenSet; | ||
} | ||
} | ||
module.exports = Option; |
@@ -187,2 +187,6 @@ const Ajv = require("ajv"); | ||
function getValidationSchema({ namespaces, allowAdditionalProperties }) { | ||
return getConfigValidationSchema({ namespaces, allowAdditionalProperties }); | ||
} | ||
function validateOptionAndThrow(properties) { | ||
@@ -201,2 +205,3 @@ validateSchemaAndThrow(properties, optionSchema, optionValidator); | ||
validateValueTypeAndThrow, | ||
getValidationSchema, | ||
}; |
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
75577
394
14
908