UNPKG

13.3 kBMarkdownView Raw
1# conf
2
3> Simple config handling for your app or module
4
5All you have to care about is what to persist. This module will handle all the dull details like where and how.
6
7**It does not support multiple processes writing to the same store.**\
8I initially made this tool to let command-line tools persist some data.
9
10*If you need this for Electron, check out [`electron-store`](https://github.com/sindresorhus/electron-store) instead.*
11
12## Install
13
14```sh
15npm install conf
16```
17
18## Usage
19
20```js
21import Conf from 'conf';
22
23const config = new Conf({projectName: 'foo'});
24
25config.set('unicorn', '🦄');
26console.log(config.get('unicorn'));
27//=> '🦄'
28
29// Use dot-notation to access nested properties
30config.set('foo.bar', true);
31console.log(config.get('foo'));
32//=> {bar: true}
33
34config.delete('unicorn');
35console.log(config.get('unicorn'));
36//=> undefined
37```
38
39Or [create a subclass](https://github.com/sindresorhus/electron-store/blob/main/index.js).
40
41## API
42
43Changes are written to disk atomically, so if the process crashes during a write, it will not corrupt the existing config.
44
45### Conf(options?)
46
47Returns a new instance.
48
49### options
50
51Type: `object`
52
53#### defaults
54
55Type: `object`
56
57Default values for the config items.
58
59**Note:** The values in `defaults` will overwrite the `default` key in the `schema` option.
60
61#### schema
62
63Type: `object`
64
65[JSON Schema](https://json-schema.org) to validate your config data.
66
67Under the hood, the JSON Schema validator [ajv](https://github.com/epoberezkin/ajv) is used to validate your config. We use [JSON Schema draft-07](https://json-schema.org/latest/json-schema-validation.html) and support all [validation keywords](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md) and [formats](https://github.com/epoberezkin/ajv#formats).
68
69You should define your schema as an object where each key is the name of your data's property and each value is a JSON schema used to validate that property. See more [here](https://json-schema.org/understanding-json-schema/reference/object.html#properties).
70
71Example:
72
73```js
74import Conf from 'conf';
75
76const schema = {
77 foo: {
78 type: 'number',
79 maximum: 100,
80 minimum: 1,
81 default: 50
82 },
83 bar: {
84 type: 'string',
85 format: 'url'
86 }
87};
88
89const config = new Conf({
90 projectName: 'foo',
91 schema
92});
93
94console.log(config.get('foo'));
95//=> 50
96
97config.set('foo', '1');
98// [Error: Config schema violation: `foo` should be number]
99```
100
101**Note:** The `default` value will be overwritten by the `defaults` option if set.
102
103#### migrations
104
105Type: `object`
106
107**Important: I cannot provide support for this feature. It has some known bugs. I have no plans to work on it, but pull requests are welcome.**
108
109You can use migrations to perform operations to the store whenever a **project version** is upgraded.
110
111The `migrations` object should consist of a key-value pair of `'version': handler`. The `version` can also be a [semver range](https://github.com/npm/node-semver#ranges).
112
113Example:
114
115```js
116import Conf from 'conf';
117
118const store = new Conf({
119 projectName: 'foo',
120 projectVersion: …,
121 migrations: {
122 '0.0.1': store => {
123 store.set('debugPhase', true);
124 },
125 '1.0.0': store => {
126 store.delete('debugPhase');
127 store.set('phase', '1.0.0');
128 },
129 '1.0.2': store => {
130 store.set('phase', '1.0.2');
131 },
132 '>=2.0.0': store => {
133 store.set('phase', '>=2.0.0');
134 }
135 }
136});
137```
138
139> Note: The version the migrations use refers to the **project version** by default. If you want to change this behavior, specify the [`projectVersion`](#projectVersion) option.
140
141#### beforeEachMigration
142
143Type: `Function`\
144Default: `undefined`
145
146The given callback function will be called before each migration step.
147
148The function receives the store as the first argument and a context object as the second argument with the following properties:
149
150- `fromVersion` - The version the migration step is being migrated from.
151- `toVersion` - The version the migration step is being migrated to.
152- `finalVersion` - The final version after all the migrations are applied.
153- `versions` - All the versions with a migration step.
154
155This can be useful for logging purposes, preparing migration data, etc.
156
157Example:
158
159```js
160import Conf from 'conf';
161
162console.log = someLogger.log;
163
164const mainConfig = new Conf({
165 projectName: 'foo1',
166 beforeEachMigration: (store, context) => {
167 console.log(`[main-config] migrate from ${context.fromVersion} → ${context.toVersion}`);
168 },
169 migrations: {
170 '0.4.0': store => {
171 store.set('debugPhase', true);
172 },
173 }
174});
175
176const secondConfig = new Conf({
177 projectName: 'foo2',
178 beforeEachMigration: (store, context) => {
179 console.log(`[second-config] migrate from ${context.fromVersion} → ${context.toVersion}`);
180 },
181 migrations: {
182 '1.0.1': store => {
183 store.set('debugPhase', true);
184 },
185 }
186});
187```
188
189#### configName
190
191Type: `string`\
192Default: `'config'`
193
194Name of the config file (without extension).
195
196Useful if you need multiple config files for your app or module. For example, different config files between two major versions.
197
198#### projectName
199
200Type: `string`
201
202**Required unless you specify the `cwd` option.**
203
204You can fetch the `name` field from package.json.
205
206#### projectVersion
207
208Type: `string`\
209
210**Required if you specify the `migration` option.**
211
212You can fetch the `version` field from package.json.
213
214#### cwd
215
216Type: `string`\
217Default: System default [user config directory](https://github.com/sindresorhus/env-paths#pathsconfig)
218
219**You most likely don't need this. Please don't use it unless you really have to. By default, it will pick the optimal location by adhering to system conventions. You are very likely to get this wrong and annoy users.**
220
221Overrides `projectName`.
222
223The only use-case I can think of is having the config located in the app directory or on some external storage.
224
225#### encryptionKey
226
227Type: `string | Uint8Array | TypedArray | DataView`\
228Default: `undefined`
229
230Note that this is **not intended for security purposes**, since the encryption key would be easily found inside a plain-text Node.js app.
231
232Its main use is for obscurity. If a user looks through the config directory and finds the config file, since it's just a JSON file, they may be tempted to modify it. By providing an encryption key, the file will be obfuscated, which should hopefully deter any users from doing so.
233
234When specified, the store will be encrypted using the [`aes-256-cbc`](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation) encryption algorithm.
235
236#### fileExtension
237
238Type: `string`\
239Default: `'json'`
240
241Extension of the config file.
242
243You would usually not need this, but could be useful if you want to interact with a file with a custom file extension that can be associated with your app. These might be simple save/export/preference files that are intended to be shareable or saved outside of the app.
244
245#### clearInvalidConfig
246
247Type: `boolean`\
248Default: `false`
249
250The config is cleared if reading the config file causes a `SyntaxError`. This is a good behavior for unimportant data, as the config file is not intended to be hand-edited, so it usually means the config is corrupt and there's nothing the user can do about it anyway. However, if you let the user edit the config file directly, mistakes might happen and it could be more useful to throw an error when the config is invalid instead of clearing.
251
252#### serialize
253
254Type: `Function`\
255Default: `value => JSON.stringify(value, null, '\t')`
256
257Function to serialize the config object to a UTF-8 string when writing the config file.
258
259You would usually not need this, but it could be useful if you want to use a format other than JSON.
260
261#### deserialize
262
263Type: `Function`\
264Default: `JSON.parse`
265
266Function to deserialize the config object from a UTF-8 string when reading the config file.
267
268You would usually not need this, but it could be useful if you want to use a format other than JSON.
269
270#### projectSuffix
271
272Type: `string`\
273Default: `'nodejs'`
274
275**You most likely don't need this. Please don't use it unless you really have to.**
276
277Suffix appended to `projectName` during config file creation to avoid name conflicts with native apps.
278
279You can pass an empty string to remove the suffix.
280
281For example, on macOS, the config file will be stored in the `~/Library/Preferences/foo-nodejs` directory, where `foo` is the `projectName`.
282
283#### accessPropertiesByDotNotation
284
285Type: `boolean`\
286Default: `true`
287
288Accessing nested properties by dot notation. For example:
289
290```js
291import Conf from 'conf';
292
293const config = new Conf({projectName: 'foo'});
294
295config.set({
296 foo: {
297 bar: {
298 foobar: '🦄'
299 }
300 }
301});
302
303console.log(config.get('foo.bar.foobar'));
304//=> '🦄'
305```
306
307Alternatively, you can set this option to `false` so the whole string would be treated as one key.
308
309```js
310import Conf from 'conf';
311
312const config = new Conf({
313 projectName: 'foo',
314 accessPropertiesByDotNotation: false
315});
316
317config.set({
318 `foo.bar.foobar`: '🦄'
319});
320
321console.log(config.get('foo.bar.foobar'));
322//=> '🦄'
323```
324
325#### watch
326
327type: `boolean`\
328Default: `false`
329
330Watch for any changes in the config file and call the callback for `onDidChange` or `onDidAnyChange` if set. This is useful if there are multiple processes changing the same config file.
331
332#### configFileMode
333
334Type: `number`\
335Default: `0o666`
336
337The [mode](https://en.wikipedia.org/wiki/File-system_permissions#Numeric_notation) that will be used for the config file.
338
339You would usually not need this, but it could be useful if you want to restrict the permissions of the config file. Setting a permission such as `0o600` would result in a config file that can only be accessed by the user running the program.
340
341Note that setting restrictive permissions can cause problems if different users need to read the file. A common problem is a user running your tool with and without `sudo` and then not being able to access the config the second time.
342
343### Instance
344
345You can use [dot-notation](https://github.com/sindresorhus/dot-prop) in a `key` to access nested properties.
346
347The instance is [`iterable`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols) so you can use it directly in a [`for…of`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/for...of) loop.
348
349#### .set(key, value)
350
351Set an item.
352
353The `value` must be JSON serializable. Trying to set the type `undefined`, `function`, or `symbol` will result in a TypeError.
354
355#### .set(object)
356
357Set multiple items at once.
358
359#### .get(key, defaultValue?)
360
361Get an item or `defaultValue` if the item does not exist.
362
363#### .reset(...keys)
364
365Reset items to their default values, as defined by the `defaults` or `schema` option.
366
367Use `.clear()` to reset all items.
368
369#### .has(key)
370
371Check if an item exists.
372
373#### .delete(key)
374
375Delete an item.
376
377#### .clear()
378
379Delete all items.
380
381This resets known items to their default values, if defined by the `defaults` or `schema` option.
382
383#### .onDidChange(key, callback)
384
385`callback`: `(newValue, oldValue) => {}`
386
387Watches the given `key`, calling `callback` on any changes.
388
389When a key is first set `oldValue` will be `undefined`, and when a key is deleted `newValue` will be `undefined`.
390
391Returns a function which you can use to unsubscribe:
392
393```js
394const unsubscribe = conf.onDidChange(key, callback);
395
396unsubscribe();
397```
398
399#### .onDidAnyChange(callback)
400
401`callback`: `(newValue, oldValue) => {}`
402
403Watches the whole config object, calling `callback` on any changes.
404
405`oldValue` and `newValue` will be the config object before and after the change, respectively. You must compare `oldValue` to `newValue` to find out what changed.
406
407Returns a function which you can use to unsubscribe:
408
409```js
410const unsubscribe = conf.onDidAnyChange(callback);
411
412unsubscribe();
413```
414
415#### .size
416
417Get the item count.
418
419#### .store
420
421Get all the config as an object or replace the current config with an object:
422
423```js
424conf.store = {
425 hello: 'world'
426};
427```
428
429#### .path
430
431Get the path to the config file.
432
433## FAQ
434
435### How is this different from [`configstore`](https://github.com/yeoman/configstore)?
436
437I'm also the author of `configstore`. While it's pretty good, I did make some mistakes early on that are hard to change at this point. This module is the result of everything I learned from making `configstore`. Mainly where the config is stored. In `configstore`, the config is stored in `~/.config` (which is mainly a Linux convention) on all systems, while `conf` stores config in the system default [user config directory](https://github.com/sindresorhus/env-paths#pathsconfig). The `~/.config` directory, it turns out, often have an incorrect permission on macOS and Windows, which has caused a lot of grief for users.
438
439### Can I use YAML or another serialization format?
440
441The `serialize` and `deserialize` options can be used to customize the format of the config file, as long as the representation is compatible with `utf8` encoding.
442
443Example using YAML:
444
445```js
446import Conf from 'conf';
447import yaml from 'js-yaml';
448
449const config = new Conf({
450 projectName: 'foo',
451 fileExtension: 'yaml',
452 serialize: yaml.safeDump,
453 deserialize: yaml.safeLoad
454});
455```
456
457## Related
458
459- [electron-store](https://github.com/sindresorhus/electron-store) - Simple data persistence for your Electron app or module
460- [cache-conf](https://github.com/SamVerschueren/cache-conf) - Simple cache config handling for your app or module