├── .circleci └── config.yml ├── .eslintignore ├── .eslintrc.json ├── .gitattributes ├── .github └── workflows │ └── node.js.yml ├── .gitignore ├── .npmrc ├── .release-it.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── lib ├── nconf.js └── nconf │ ├── common.js │ ├── formats.js │ ├── provider.js │ └── stores │ ├── argv.js │ ├── env.js │ ├── file.js │ ├── literal.js │ └── memory.js ├── package.json ├── renovate.json ├── test ├── common.test.js ├── complete.test.js ├── fixtures │ ├── bom.json │ ├── complete.json │ ├── data.js │ ├── hierarchy │ │ ├── global.json │ │ ├── hierarchical.json │ │ └── user.json │ ├── malformed.json │ ├── merge │ │ ├── file1.json │ │ ├── file2.json │ │ └── file3.json │ ├── no-bom.json │ ├── scripts │ │ ├── nconf-argv.js │ │ ├── nconf-change-argv.js │ │ ├── nconf-env.js │ │ ├── nconf-hierarchical-defaults-merge.js │ │ ├── nconf-hierarchical-file-argv.js │ │ ├── nconf-hierarchical-load-merge-with-separator.js │ │ ├── nconf-hierarchical-load-merge.js │ │ ├── nconf-hierarchical-load-save.js │ │ ├── nconf-nested-env.js │ │ ├── provider-argv.js │ │ └── provider-env.js │ ├── secure-iv.json │ └── secure.json ├── helpers.js ├── hierarchy.test.js ├── mocks │ └── mock-store.js ├── nconf.test.js ├── provider-save.test.js ├── provider.test.js └── stores │ ├── argv.test.js │ ├── env.test.js │ ├── file-store.test.js │ ├── literal.test.js │ └── memory-store.test.js └── usage.js /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | commands: 4 | test-nodejs: 5 | steps: 6 | - run: 7 | name: Versions 8 | command: npm version 9 | - checkout 10 | - restore_cache: 11 | keys: 12 | - v{{ .Environment.CIRCLE_CACHE_VERSION }}-{{ arch }}-npm-cache-{{ .Branch }}-{{ .Environment.CIRCLE_JOB }} 13 | - v{{ .Environment.CIRCLE_CACHE_VERSION }}-{{ arch }}-npm-cache-master-{{ .Environment.CIRCLE_JOB }} 14 | - run: 15 | name: Install dependencies 16 | command: npm install 17 | - run: 18 | name: Test 19 | command: npm test 20 | - coverage 21 | - save-npm-cache 22 | save-npm-cache: 23 | steps: 24 | - save_cache: 25 | key: v{{ .Environment.CIRCLE_CACHE_VERSION }}-{{ arch }}-npm-cache-{{ .Branch }}-{{ .Environment.CIRCLE_JOB }}-{{ checksum "package-lock.json" }} 26 | paths: 27 | - ~/.npm/_cacache 28 | coverage: 29 | steps: 30 | - run: 31 | command: npm run cover 32 | - run: 33 | command: npm run coveralls 34 | jobs: 35 | node-v12: 36 | docker: 37 | - image: node:12 38 | steps: 39 | - test-nodejs 40 | node-v14: 41 | docker: 42 | - image: node:14 43 | steps: 44 | - test-nodejs 45 | 46 | workflows: 47 | version: 2 48 | node-multi-build: 49 | jobs: 50 | - node-v12 51 | - node-v14 52 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | usage.js 3 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true 4 | }, 5 | "parserOptions": { 6 | "ecmaVersion": 6 7 | }, 8 | "rules": { 9 | "no-unused-vars": "error" 10 | } 11 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | package-lock.json binary 2 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [12.x, 14.x, 16.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v2 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v2 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | - run: npm install 29 | - run: npm run build --if-present 30 | - run: npm test 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | config.json 3 | test/fixtures/*.json 4 | !test/fixtures/complete.json 5 | !test/fixtures/malformed.json 6 | !test/fixtures/bom.json 7 | !test/fixtures/no-bom.json 8 | !test/fixtures/secure.json 9 | !test/fixtures/secure-iv.json 10 | node_modules/ 11 | node_modules/* 12 | npm-debug.log 13 | coverage 14 | package-lock.json 15 | 16 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "git": { 3 | "commitMessage": "chore: release ${version}", 4 | "tagName": "v${version}" 5 | }, 6 | "npm": { 7 | "publish": true 8 | }, 9 | "github": { 10 | "release": true 11 | }, 12 | "plugins": { 13 | "@release-it/conventional-changelog": { 14 | "preset": "angular" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | v0.9.1 / Fri, 3 Nov 2017 3 | ======================== 4 | * [806f1b1](https://github.com/projects/nconf/commit/806f1b1) 0.9.1 (`Matt Hamann`) 5 | * [2bdf7e1](https://github.com/projects/nconf/commit/2bdf7e1) Clean Argv Store options (#290) (`Adrien Becchis`) 6 | * [b9321b2](https://github.com/projects/nconf/commit/b9321b2) transformer can now return an undefined key (#289) (`Adrien Becchis`) 7 | * [81ce0be](https://github.com/projects/nconf/commit/81ce0be) Update changelog (`Matt Hamann`) 8 | 9 | 0.9.0 / Tue, 31 Oct 2017 10 | ======================== 11 | * [b1ee63c](https://github.com/projects/nconf/commit/b1ee63c) fix error in transform function when dealing with dropped entries (#287) (`Augusto Franzoia`) 12 | * [9f70ba1](https://github.com/projects/nconf/commit/9f70ba1) [doc] Update changelog (`Matt Hamann`) 13 | * [8afcf99](https://github.com/projects/nconf/commit/8afcf99) [dist] Version bump. 0.9.0 (`Matt Hamann`) 14 | * [b41c505](https://github.com/projects/nconf/commit/b41c505) Save conf to dedicated file (#283) (`Adrien Becchis`) 15 | * [52e0a35](https://github.com/projects/nconf/commit/52e0a35) Update changelog (`Matt Hamann`) 16 | * [fa215a4](https://github.com/projects/nconf/commit/fa215a4) add tests for the normal configuration of yargs via argv (`AdrieanKhisbe`) 17 | * [802a8d6](https://github.com/projects/nconf/commit/802a8d6) test for yargs custom instance (more flexible check isYargs) (`AdrieanKhisbe`) 18 | * [3e26bb2](https://github.com/projects/nconf/commit/3e26bb2) Add posibility to pass a yargs instance to argv() method (`Nicolas Deveaud`) 19 | * [856fdf8](https://github.com/projects/nconf/commit/856fdf8) First pass at transform functions (#279) (`Matt Hamann`) 20 | * [b9c345b](https://github.com/projects/nconf/commit/b9c345b) Fix `parseValues` option name (`Matt Hamann`) 21 | * [35088a3](https://github.com/projects/nconf/commit/35088a3) Added nconf.any method (#278) (`Matt Hamann`) 22 | * [ca10d0e](https://github.com/projects/nconf/commit/ca10d0e) Add basic linting rules (`Matt Hamann`) 23 | * [bfb0220](https://github.com/projects/nconf/commit/bfb0220) Remove unused module (#277) (`Brian Harrington`) 24 | * [532ac9c](https://github.com/projects/nconf/commit/532ac9c) Support parsing simple values from env/argv strings (#273) (`Matt Hamann`) 25 | * [b8402d4](https://github.com/projects/nconf/commit/b8402d4) Enable support for parsing JSON environment variables (#272) (`Matt Hamann`) 26 | 27 | v0.8.5 / Tue, 15 Aug 2017 28 | ========================= 29 | * [f46c449](https://github.com/projects/nconf/commit/f46c449) 0.8.5 (`Matt Hamann`) 30 | * [552300a](https://github.com/projects/nconf/commit/552300a) [doc] Document lowerCase option in .env(options) (#268) (`Matt Hamann`) 31 | * [5e8a34d](https://github.com/projects/nconf/commit/5e8a34d) enable use with webpack by removing unnecessary fs call (#252) (`evoye`) 32 | * [608b607](https://github.com/projects/nconf/commit/608b607) Add test for merging with defaults (#255) (`Chris Manson`) 33 | * [d70b6a0](https://github.com/projects/nconf/commit/d70b6a0) Fixed some issues with code escaping (`Charter Jacobson`) 34 | * [392c602](https://github.com/projects/nconf/commit/392c602) Copy `process.env` before lower-casing the keys (`Jan Klosinski`) 35 | 36 | 0.8.4 / Wed, 3 Feb 2016 37 | ======================= 38 | * [3d4e589](https://github.com/projects/nconf/commit/3d4e589) [dist] Version bump. 0.8.4 (`Jarrett Cruger`) 39 | * [4431c33](https://github.com/projects/nconf/commit/4431c33) [fix] handle buffers so we dont get ambiguous errors when we dont strictly read the file as utf8 (`Jarrett Cruger`) 40 | 41 | 0.8.3 / Mon, 1 Feb 2016 42 | ======================= 43 | * [54cab20](https://github.com/projects/nconf/commit/54cab20) [dist] Version bump. 0.8.3 (`Jarrett Cruger`) 44 | * [b447268](https://github.com/projects/nconf/commit/b447268) [fix] cleanup secure with new module (`Jarrett Cruger`) 45 | * [de551d4](https://github.com/projects/nconf/commit/de551d4) Update README.md (`Mark Oberemk`) 46 | * [c242f77](https://github.com/projects/nconf/commit/c242f77) [travis test] setup coveralls config + update README badges (`AdrieanKhisbe`) 47 | * [c8dbede](https://github.com/projects/nconf/commit/c8dbede) [test] setup istanbul coverage (`AdrieanKhisbe`) 48 | * [bdecdc2](https://github.com/projects/nconf/commit/bdecdc2) [travis] add v5 to node version tested (`AdrieanKhisbe`) 49 | * [5d6e236](https://github.com/projects/nconf/commit/5d6e236) fixed (`Wojtek Turyn`) 50 | * [37a84ae](https://github.com/projects/nconf/commit/37a84ae) required() method (`Wojtek Turyn`) 51 | 52 | 0.8.2 / Wed, 7 Oct 2015 53 | ======================= 54 | * [ddee9bc](https://github.com/projects/nconf/commit/ddee9bc) [dist] Version bump. 0.8.2 (`indexzero`) 55 | * [86bfd7c](https://github.com/projects/nconf/commit/86bfd7c) [fix] Do not trim `\n` from files read in. (`indexzero`) 56 | 57 | 0.8.1 / Fri, 2 Oct 2015 58 | ======================= 59 | * [ff0f174](https://github.com/projects/nconf/commit/ff0f174) [dist] Version bump. 0.8.1 (`indexzero`) 60 | * [11b2448](https://github.com/projects/nconf/commit/11b2448) [fix] Correct property path. Trim read secret keys from disk. (`indexzero`) 61 | * [438a2c8](https://github.com/projects/nconf/commit/438a2c8) [doc] Remove `node@0.8.0` from travis. Drop `nodeci` because it is 503 atm`. (`indexzero`) 62 | 63 | 0.8.0 / Sun, 20 Sep 2015 64 | ======================== 65 | * [ebd8e48](https://github.com/projects/nconf/commit/ebd8e48) [dist] Version bump. 0.8.0 (`indexzero`) 66 | * [be085c9](https://github.com/projects/nconf/commit/be085c9) [doc] Update CHANGELOG.md. (`indexzero`) 67 | * [0922563](https://github.com/projects/nconf/commit/0922563) [doc fix] Remove unused and outdated literate coding documentation. (`indexzero`) 68 | * [4b5030d](https://github.com/projects/nconf/commit/4b5030d) [fix] Only merge actual objects, not `null` values. Fixes #150. (`indexzero`) 69 | * [a3589fa](https://github.com/projects/nconf/commit/a3589fa) Fixing provider issue in source (`Rob Rodriguez`) 70 | * [51653e6](https://github.com/projects/nconf/commit/51653e6) Passing the value parameter to the providers (`Rob Rodriguez`) 71 | * [2030144](https://github.com/projects/nconf/commit/2030144) [test dist] Add `test/fixtures/secure.json`. (`indexzero`) 72 | * [9dbed2d](https://github.com/projects/nconf/commit/9dbed2d) [doc minor] Update docs for secure information. (`indexzero`) 73 | * [0358545](https://github.com/projects/nconf/commit/0358545) [test api] Make the format capable of sub-objects. (`indexzero`) 74 | * [04c0f3a](https://github.com/projects/nconf/commit/04c0f3a) [api test] Encrypt individual keys instead of entire stringified contents. Added basic unit tests. (`indexzero`) 75 | * [d2b3561](https://github.com/projects/nconf/commit/d2b3561) [dist] Update `.travis.yml`. (`indexzero`) 76 | * [442d2b4](https://github.com/projects/nconf/commit/442d2b4) [api] Allow for `secure` to be simply a secret string. (`indexzero`) 77 | * [2de2bc0](https://github.com/projects/nconf/commit/2de2bc0) [api] Allow for "secure" option to be passed to `nconf.stores.File` to perform content encryption / decryption with `crypto.createCipher`. (`indexzero`) 78 | * [5d95f13](https://github.com/projects/nconf/commit/5d95f13) filter out undefined values (`Christian Murphy`) 79 | * [7d6be32](https://github.com/projects/nconf/commit/7d6be32) [travis] fix yaml syntax (supposed to solve nvm bugs #182) (`Joseph Page`) 80 | * [abeeca0](https://github.com/projects/nconf/commit/abeeca0) [travis] fix npm bugs for node 0.8 (recommended way) (`Joseph Page`) 81 | * [59056fe](https://github.com/projects/nconf/commit/59056fe) Update Async and ini (`Christian Murphy`) 82 | * [a2b812f](https://github.com/projects/nconf/commit/a2b812f) Add travis tests for iojs (`Joseph Page`) 83 | * [32d560c](https://github.com/projects/nconf/commit/32d560c) Add tests for node 0.12 (`Joseph Page`) 84 | * [8a21ef3](https://github.com/projects/nconf/commit/8a21ef3) env({lowerCase:true}) option to make it possible to get() keys in lower case (`Olivier Lalonde`) 85 | * [89dff39](https://github.com/projects/nconf/commit/89dff39) Quick grammar fix (`Nick Heiner`) 86 | * [339e59a](https://github.com/projects/nconf/commit/339e59a) fix random fails on tests that use child process (`Pierre Beaujeu`) 87 | * [a65e1a3](https://github.com/projects/nconf/commit/a65e1a3) update async (`Christian Murphy`) 88 | * [a82b539](https://github.com/projects/nconf/commit/a82b539) update badge and use container build (`Christian Murphy`) 89 | * [e5b33ce](https://github.com/projects/nconf/commit/e5b33ce) Add license attribute (`Gilad Peleg`) 90 | 91 | 0.7.2 / Tue, 4 Aug 2015 92 | ======================= 93 | * [c2b8b97](https://github.com/projects/nconf/commit/c2b8b97) [dist] Version bump. 0.7.2 (`indexzero`) 94 | * [3c11ef5](https://github.com/projects/nconf/commit/3c11ef5) fix: env.match test (`Remy Sharp`) 95 | * [372521b](https://github.com/projects/nconf/commit/372521b) [doc] Add the badges!. (`indexzero`) 96 | * [80ec01b](https://github.com/projects/nconf/commit/80ec01b) replace optimist with yargs (`Christian Murphy`) 97 | * [6d86950](https://github.com/projects/nconf/commit/6d86950) Grammar nit (`Nick Heiner`) 98 | 99 | v0.7.1 / Wed, 26 Nov 2014 100 | ========================= 101 | 102 | 103 | v0.7.1 / Wed, 26 Nov 2014 104 | ========================= 105 | * [dc6aed2](https://github.com/projects/nconf/commit/dc6aed2) [dist] Version bump. 0.7.1 (`Jarrett Cruger`) 106 | * [87a3b82](https://github.com/projects/nconf/commit/87a3b82) [fix] we shouldnt be reversing here fixes #127 (`Jarrett Cruger`) 107 | * [6271cdb](https://github.com/projects/nconf/commit/6271cdb) Revert "fixing the tests" (`Jarrett Cruger`) 108 | * [f0d5b6e](https://github.com/projects/nconf/commit/f0d5b6e) [dist] Fix travis. (`indexzero`) 109 | 110 | v0.7.0 / Wed, 26 Nov 2014 111 | ========================= 112 | 113 | 114 | v0.7.0 / Wed, 26 Nov 2014 115 | ========================= 116 | * [a2a1321](https://github.com/projects/nconf/commit/a2a1321) [dist] Version bump. 0.7.0 (`indexzero`) 117 | * [352f075](https://github.com/projects/nconf/commit/352f075) [dist] "Real" CHANGELOG.md again. (`indexzero`) 118 | * [af0e9fb](https://github.com/projects/nconf/commit/af0e9fb) [dist fix] Cleanup some whitespace. (`indexzero`) 119 | * [0934255](https://github.com/projects/nconf/commit/0934255) [fix] Fixed regression introduced by #98. (`indexzero`) 120 | * [8d5fb25](https://github.com/projects/nconf/commit/8d5fb25) [fix] Fix my own sloppy coding fixing the sloppy coding from #76. (`indexzero`) 121 | * [f07bc40](https://github.com/projects/nconf/commit/f07bc40) [fix] Fix inconsistent style from #98. (`indexzero`) 122 | * [0b8aa90](https://github.com/projects/nconf/commit/0b8aa90) [fix test] Remove leftover `console.log()` from #79. (`indexzero`) 123 | * [f771500](https://github.com/projects/nconf/commit/f771500) [dist] Semantic cleanup from sloppy coding in #76. (`indexzero`) 124 | * [ffce2cb](https://github.com/projects/nconf/commit/ffce2cb) [dist] Update package.json versions. (`indexzero`) 125 | * [6301d7d](https://github.com/projects/nconf/commit/6301d7d) Update Readme; multiple file() needs custom key (`Mitchell McKenna`) 126 | * [f69e43a](https://github.com/projects/nconf/commit/f69e43a) fixing the tests (`Chris Manson`) 127 | * [c8b6c98](https://github.com/projects/nconf/commit/c8b6c98) Adding helpful information in case parsing failed. (`Martin Heidegger`) 128 | * [8105c76](https://github.com/projects/nconf/commit/8105c76) [fix] only reverse keys for "get" action to be safe. (`Christopher Jeffrey`) 129 | * [2241a36](https://github.com/projects/nconf/commit/2241a36) [fix] have latter stores precede the former stores again. (`Christopher Jeffrey`) 130 | * [0bb89ee](https://github.com/projects/nconf/commit/0bb89ee) [fix] have latter stores precede the former stores. (`Christopher Jeffrey`) 131 | * [43505a5](https://github.com/projects/nconf/commit/43505a5) Use ~ for dependencies (`Gabe Gorelick`) 132 | * [05d73de](https://github.com/projects/nconf/commit/05d73de) [fix] No need to test 0.6 anymore (`Jarrett Cruger`) 133 | * [79b9b84](https://github.com/projects/nconf/commit/79b9b84) [doc] Add a Literal example to add() (`Tommy Stanton`) 134 | * [3a7b788](https://github.com/projects/nconf/commit/3a7b788) [doc] The store for File is empty if non-existent (`Tommy Stanton`) 135 | * [9891814](https://github.com/projects/nconf/commit/9891814) Delete CHANGELOG.md (`Alexey Simonenko`) 136 | * [120f5f0](https://github.com/projects/nconf/commit/120f5f0) added documentation (`joaoafrmartins`) 137 | * [681fd2f](https://github.com/projects/nconf/commit/681fd2f) added regexp filtering to nconf env store (`joaoafrmartins`) 138 | * [039057c](https://github.com/projects/nconf/commit/039057c) allow different separator for memorystore (`José F. Romaniello`) 139 | * [b73b0e1](https://github.com/projects/nconf/commit/b73b0e1) attach help and showHelp arguments to the argv store (`Johnny Domino`) 140 | * [4894c8f](https://github.com/projects/nconf/commit/4894c8f) resolves #64 passing usage string to optimist (`Johnny Domino`) 141 | 142 | v0.6.9 / Sun, 1 Dec 2013 143 | ======================== 144 | 145 | 146 | v0.6.9 / Sun, 1 Dec 2013 147 | ======================== 148 | * [022b9bc](https://github.com/projects/nconf/commit/022b9bc) [dist] Version bump. 0.6.9 (`Jarrett Cruger`) 149 | * [9aa33b5](https://github.com/projects/nconf/commit/9aa33b5) [dist] bump optimist version, fixes #89 (`Jarrett Cruger`) 150 | * [92311c8](https://github.com/projects/nconf/commit/92311c8) [rm] kill pkginfo (`Jarrett Cruger`) 151 | * [c713936](https://github.com/projects/nconf/commit/c713936) [dist] bump async (`Jarrett Cruger`) 152 | 153 | v0.6.8 / Tue, 29 Oct 2013 154 | ========================= 155 | 156 | 157 | v0.6.8 / Tue, 29 Oct 2013 158 | ========================= 159 | * [cd81efa](https://github.com/projects/nconf/commit/cd81efa) [dist] Version bump. 0.6.8 (`Jarrett Cruger`) 160 | * [6c1eb5e](https://github.com/projects/nconf/commit/6c1eb5e) fixed white spacing and added (embarrassing absent) variable declarations (`midknight41`) 161 | * [ccd609c](https://github.com/projects/nconf/commit/ccd609c) updated version of vows as v0.6 didn't work with node 0.10 (`midknight41`) 162 | * [5546469](https://github.com/projects/nconf/commit/5546469) updated .travis.yml as travis doesn't support node 0.4 or 0.9 (`midknight41`) 163 | * [6641ed2](https://github.com/projects/nconf/commit/6641ed2) made bom tests more meaningful (`midknight41`) 164 | * [2ce8aea](https://github.com/projects/nconf/commit/2ce8aea) made bom tests more meaningful (`midknight41`) 165 | * [f7733c1](https://github.com/projects/nconf/commit/f7733c1) included bom test fixtures (`midknight41`) 166 | * [24f77a0](https://github.com/projects/nconf/commit/24f77a0) included bom test fixtures (`midknight41`) 167 | * [29f1ca2](https://github.com/projects/nconf/commit/29f1ca2) added support for BOM in load() and loadSync() (`midknight41`) 168 | * [ada15db](https://github.com/projects/nconf/commit/ada15db) Test that invalid file name is indicated (`Marcin Floryan`) 169 | * [0135d95](https://github.com/projects/nconf/commit/0135d95) Additional error information when JSON config file cannot be read (`Marcin Floryan`) 170 | * [5d2ebfb](https://github.com/projects/nconf/commit/5d2ebfb) Added test to confirm merging an Object and null behaves as expected. (`Michael Schoonmaker`) 171 | * [ed41c51](https://github.com/projects/nconf/commit/ed41c51) Updated Memory.merge to handle null values (`Michael Schoonmaker`) 172 | 173 | v0.6.7 / Thu, 20 Dec 2012 174 | ========================= 175 | 176 | 177 | v0.6.7 / Thu, 20 Dec 2012 178 | ========================= 179 | * [d77c55d](https://github.com/projects/nconf/commit/d77c55d) [dist] Version bump. 0.6.7 (`indexzero`) 180 | * [bb57c49](https://github.com/projects/nconf/commit/bb57c49) Prefer this fix for #65 to 6045618 (`Michael Hart`) 181 | 182 | v0.6.6 / Thu, 20 Dec 2012 183 | ========================= 184 | 185 | 186 | v0.6.6 / Thu, 20 Dec 2012 187 | ========================= 188 | * [aec2b4e](https://github.com/projects/nconf/commit/aec2b4e) [dist] Version bump. 0.6.6 (`indexzero`) 189 | * [6045618](https://github.com/projects/nconf/commit/6045618) [fix] Fix for #65 (`indexzero`) 190 | * [0d795ec](https://github.com/projects/nconf/commit/0d795ec) [test] Better tests to show #65 (`indexzero`) 191 | * [f19f0b6](https://github.com/projects/nconf/commit/f19f0b6) [test] Added failing test to illustrate #65 (`indexzero`) 192 | 193 | v0.6.5 / Fri, 2 Nov 2012 194 | ======================== 195 | 196 | 197 | v0.6.5 / Fri, 2 Nov 2012 198 | ======================== 199 | * [bcbaf3a](https://github.com/projects/nconf/commit/bcbaf3a) [dist] Bump version to 0.6.5 (`Maciej Małecki`) 200 | * [8b65e19](https://github.com/projects/nconf/commit/8b65e19) [test] Test on newer node versions (`Maciej Małecki`) 201 | * [8e987b8](https://github.com/projects/nconf/commit/8e987b8) make it possible to use other formats than json in common.loadFiles and common.loadFilesSync (`Christian Tellnes`) 202 | * [da39d3c](https://github.com/projects/nconf/commit/da39d3c) [fix] null values should merge properly instead of throwing errors (`Bradley Meck`) 203 | * [7421836](https://github.com/projects/nconf/commit/7421836) [fix] heirarchy fixture file path wrong in tests (`Bradley Meck`) 204 | * [683f789](https://github.com/projects/nconf/commit/683f789) [fix] #59 root get/set should work via null/undefined as key (`Bradley Meck`) 205 | * [0f092ab](https://github.com/projects/nconf/commit/0f092ab) Added docs for options hash to optimist. (`Ethan Winn`) 206 | 207 | v0.6.4 / Tue, 10 Jul 2012 208 | ========================= 209 | 210 | 211 | v0.6.4 / Tue, 10 Jul 2012 212 | ========================= 213 | * [7279bc1](https://github.com/projects/nconf/commit/7279bc1) [dist] Version bump. 0.6.4 (`indexzero`) 214 | * [d96d254](https://github.com/projects/nconf/commit/d96d254) [fix] Fix regression introduced by 36e061c4bda8d79f657dc24b1dcf1937f31d7efe (`indexzero`) 215 | * [7e8d9d6](https://github.com/projects/nconf/commit/7e8d9d6) [test] Added failing test for `.save()` regression introduced by @russfrank in 36e061c4bda8d79f657dc24b1dcf1937f31d7efe (`indexzero`) 216 | * [04e2230](https://github.com/projects/nconf/commit/04e2230) [minor doc] Update file header in test/provider-test.js (`indexzero`) 217 | 218 | v0.6.3 / Tue, 10 Jul 2012 219 | ========================= 220 | 221 | 222 | v0.6.3 / Tue, 10 Jul 2012 223 | ========================= 224 | * [c7c6b6f](https://github.com/projects/nconf/commit/c7c6b6f) [dist] Version bump. 0.6.3 (`indexzero`) 225 | * [3073430](https://github.com/projects/nconf/commit/3073430) [api test doc] Make options to `Provider.prototype.file` take more flexible options (`indexzero`) 226 | * [8b53c12](https://github.com/projects/nconf/commit/8b53c12) [minor] Use locally scoped `path` variable (`indexzero`) 227 | 228 | v0.6.2 / Tue, 10 Jul 2012 229 | ========================= 230 | 231 | 232 | v0.6.2 / Tue, 10 Jul 2012 233 | ========================= 234 | * [80a7973](https://github.com/projects/nconf/commit/80a7973) [dist] Version bump. 0.6.2 (`indexzero`) 235 | * [7515f66](https://github.com/projects/nconf/commit/7515f66) [fix] Ensure that all options are passed to `Provider.prototype.add` in `Provider.prototype.file`. Fixes #51 [doc] Update README.md and method documentation [dist] Remove vim comments (`indexzero`) 236 | 237 | v0.6.1 / Sun, 8 Jul 2012 238 | ======================== 239 | 240 | 241 | v0.6.1 / Sun, 8 Jul 2012 242 | ======================== 243 | * [eeddb70](https://github.com/projects/nconf/commit/eeddb70) [dist] Version bump. 0.6.1 (`indexzero`) 244 | * [9aaafc5](https://github.com/projects/nconf/commit/9aaafc5) Ugh, fixed whitespace (`Michael Hart`) 245 | * [3c08fad](https://github.com/projects/nconf/commit/3c08fad) Changed to as it's more accurate (`Michael Hart`) 246 | * [e15f787](https://github.com/projects/nconf/commit/e15f787) Updated README and allowed a simpley syntax (`Michael Hart`) 247 | * [92d4e9e](https://github.com/projects/nconf/commit/92d4e9e) Added test and updated docs (`Michael Hart`) 248 | * [8921d05](https://github.com/projects/nconf/commit/8921d05) Added support for nested configs via env (`Michael Hart`) 249 | * [6cbc323](https://github.com/projects/nconf/commit/6cbc323) Add reset to the list of destructive commands (`Michael Hart`) 250 | * [26d81e8](https://github.com/projects/nconf/commit/26d81e8) Merge objects if necessary when traversing stores on get() (`Michael Hart`) 251 | * [83440f9](https://github.com/projects/nconf/commit/83440f9) fix spelling in error message (`Christian Tellnes`) 252 | * [87b0dd0](https://github.com/projects/nconf/commit/87b0dd0) [minor] Use `fs.exists` when available (`Maciej Małecki`) 253 | * [1f67d35](https://github.com/projects/nconf/commit/1f67d35) [dist] Fix maintainers field (`Christian Howe`) 254 | * [6353d02](https://github.com/projects/nconf/commit/6353d02) api and doc change for flatiron/nconf#28 (`.file` may now take a string instead of an object) (`Jonathan Stewmon`) 255 | * [d3e6897](https://github.com/projects/nconf/commit/d3e6897) Proper teardowns in `complete-test.js` (`Russell Frank`) 256 | * [94bdb7d](https://github.com/projects/nconf/commit/94bdb7d) Added `complete-test.js` & fixture. (`Russell Frank`) 257 | * [36e061c](https://github.com/projects/nconf/commit/36e061c) Fixes to `Provider.save()` and tests. (`Russell Frank`) 258 | * [29eb5f9](https://github.com/projects/nconf/commit/29eb5f9) [minor] Fix whitespaces (`Pavan Kumar Sunkara`) 259 | * [6ce0b7a](https://github.com/projects/nconf/commit/6ce0b7a) Surfacing additional JSON.stringify arguments in formats.json.stringify, and adding the json_spacing option to the File constructor. (`Jordan Harband`) 260 | * [b369931](https://github.com/projects/nconf/commit/b369931) [minor] Use `fs.existsSync` when available (`Maciej Małecki`) 261 | * [d8c4749](https://github.com/projects/nconf/commit/d8c4749) [test] Test on `node@0.7` (`Maciej Małecki`) 262 | * [464af41](https://github.com/projects/nconf/commit/464af41) [fix test] Fix bad test assertion (`indexzero`) 263 | 264 | v0.5.1 / Mon, 2 Jan 2012 265 | ======================== 266 | 267 | 268 | v0.5.1 / Mon, 2 Jan 2012 269 | ======================== 270 | * [6a6e092](https://github.com/projects/nconf/commit/6a6e092) [dist] Version bump. 0.5.1 (`indexzero`) 271 | * [6242caa](https://github.com/projects/nconf/commit/6242caa) [api minor] Add `.loadSync()` to Memory store. Fixes #24 (`indexzero`) 272 | * [d0a9121](https://github.com/projects/nconf/commit/d0a9121) [test dist] Remove unused `eyes` dependency (`indexzero`) 273 | * [9e9e37b](https://github.com/projects/nconf/commit/9e9e37b) [minor] Update whitespace (`indexzero`) 274 | * [fdb73f0](https://github.com/projects/nconf/commit/fdb73f0) updated tests to verify that Provider.load respects hierarchy (`Jonathan Stewmon`) 275 | * [a216336](https://github.com/projects/nconf/commit/a216336) updated Provider.load to respect sources hierarchy (`Jonathan Stewmon`) 276 | * [6b6bf85](https://github.com/projects/nconf/commit/6b6bf85) updated optimist to version 0.3.x (`Jonathan Stewmon`) 277 | * [5c43d54](https://github.com/projects/nconf/commit/5c43d54) fixed merge issue in Provider.load by reversing store keys in getStores (`Jonathan Stewmon`) 278 | * [2804b1f](https://github.com/projects/nconf/commit/2804b1f) fixed issue caused by using same name for defaults and overrides (`Jonathan Stewmon`) 279 | * [e0e070a](https://github.com/projects/nconf/commit/e0e070a) [test] Test if `File.saveSync()` returns store content (`Maciej Małecki`) 280 | * [963387c](https://github.com/projects/nconf/commit/963387c) [api] `File.saveSync()` should return store content (`Maciej Małecki`) 281 | * [d5ce1ed](https://github.com/projects/nconf/commit/d5ce1ed) [test] Test `saveSync()` method of file store (`Maciej Małecki`) 282 | * [cf9889e](https://github.com/projects/nconf/commit/cf9889e) [dist] Upgrade vows to 0.6.x (`Pavan Kumar Sunkara`) 283 | 284 | v0.5.0 / Thu, 24 Nov 2011 285 | ========================= 286 | 287 | 288 | v0.5.0 / Thu, 24 Nov 2011 289 | ========================= 290 | * [62cb7fb](https://github.com/projects/nconf/commit/62cb7fb) [dist] Version bump. 0.5.0 (`indexzero`) 291 | * [6c720ee](https://github.com/projects/nconf/commit/6c720ee) [dist] Update Copyright and Author to Nodejitsu Inc. (`indexzero`) 292 | * [4643a14](https://github.com/projects/nconf/commit/4643a14) [doc] Updated README and added CHANGELOG.md (`indexzero`) 293 | * [90b0297](https://github.com/projects/nconf/commit/90b0297) [test] Update tests to use optional options API (`indexzero`) 294 | * [53d854a](https://github.com/projects/nconf/commit/53d854a) [api] Default to `options` if `options.store` is not available in nconf.Literal (`indexzero`) 295 | * [b658f68](https://github.com/projects/nconf/commit/b658f68) [test] Add additional test coverage for hierarchical configuration (`indexzero`) 296 | * [a9c3540](https://github.com/projects/nconf/commit/a9c3540) [fix test] Fix overwritten tests in file-store-test.js (`indexzero`) 297 | * [f4f1fdf](https://github.com/projects/nconf/commit/f4f1fdf) [fix test] Update to respected `.sources` option correctly (`indexzero`) 298 | * [bbcb271](https://github.com/projects/nconf/commit/bbcb271) [api fix] Dont eagerly create config files in `.load()` and `.loadSync()` (`indexzero`) 299 | * [021850a](https://github.com/projects/nconf/commit/021850a) [test] Move around test .json files (`indexzero`) 300 | * [0fbc9a2](https://github.com/projects/nconf/commit/0fbc9a2) [test] Added tests (which are now passing) for #15 (`indexzero`) 301 | * [16a18bf](https://github.com/projects/nconf/commit/16a18bf) [refactor] Expose all store prototypes on `nconf.*`. Expose store instances on Provider.stores and Provider.sources (`indexzero`) 302 | * [c3cebe7](https://github.com/projects/nconf/commit/c3cebe7) [refactor] Rename `.sources` to `._stores` and bring back `._sources` (`indexzero`) 303 | * [78ce556](https://github.com/projects/nconf/commit/78ce556) [minor] Dont allow `.set()` calls to change values in readOnly stores: argv, env, and literal (`indexzero`) 304 | * [1aa2f1f](https://github.com/projects/nconf/commit/1aa2f1f) [doc] Updated README.md (`indexzero`) 305 | * [47a56cc](https://github.com/projects/nconf/commit/47a56cc) [test] Test for hierarchical argv options get() (`Sander Tolsma`) 306 | * [c3c315d](https://github.com/projects/nconf/commit/c3c315d) [refactor] Refactor to make using nconf more fluent. (`indexzero`) 307 | * [2c1ef71](https://github.com/projects/nconf/commit/2c1ef71) [dist] Bump to v0.4.6 (`Marak Squires`) 308 | * [1b258bf](https://github.com/projects/nconf/commit/1b258bf) [fix] Fix option parsing (`Maciej Małecki`) 309 | * [ef3222e](https://github.com/projects/nconf/commit/ef3222e) [dist] Make `repository` point to `flatiron/nconf` (`Maciej Małecki`) 310 | 311 | v0.4.5 / Sun, 20 Nov 2011 312 | ========================= 313 | 314 | 315 | v0.4.5 / Sun, 20 Nov 2011 316 | ========================= 317 | * [f4723e9](https://github.com/projects/nconf/commit/f4723e9) [dist] Version bump. 0.4.5 (`indexzero`) 318 | * [2475d06](https://github.com/projects/nconf/commit/2475d06) [test] Test command line arguments reparsing (`Maciej Małecki`) 319 | * [bbc5885](https://github.com/projects/nconf/commit/bbc5885) [api] Reparse argv arguments on `system.loadArgv()` (`Maciej Małecki`) 320 | * [51700ca](https://github.com/projects/nconf/commit/51700ca) [test minor] Use `process.argv[0]` when spawning processes (`Maciej Małecki`) 321 | * [07f8c3e](https://github.com/projects/nconf/commit/07f8c3e) [doc] Add Travis build status image (`Maciej Małecki`) 322 | * [bab96b0](https://github.com/projects/nconf/commit/bab96b0) [test] Add `.travis.yml` for testing on Travis CI (`Maciej Małecki`) 323 | 324 | v0.4.4 / Sat, 22 Oct 2011 325 | ========================= 326 | 327 | 328 | v0.4.4 / Sat, 22 Oct 2011 329 | ========================= 330 | * [b96151e](https://github.com/projects/nconf/commit/b96151e) [dist] Version bump. 0.4.4 (`indexzero`) 331 | * [d8a3020](https://github.com/projects/nconf/commit/d8a3020) [fix] filename --> file in a few file transport examples (`Joshua Holbrook`) 332 | * [2e33082](https://github.com/projects/nconf/commit/2e33082) [api] Automatically search for a file if `options.search` is true in File store (`indexzero`) 333 | 334 | v0.4.3 / Sun, 25 Sep 2011 335 | ========================= 336 | 337 | 338 | v0.4.3 / Sun, 25 Sep 2011 339 | ========================= 340 | * [86e22cb](https://github.com/projects/nconf/commit/86e22cb) [dist] Version bump. 0.4.3 (`indexzero`) 341 | * [a2464d2](https://github.com/projects/nconf/commit/a2464d2) [api] Load sources into the default system store so they are permenantly cached (`indexzero`) 342 | 343 | v0.4.2 / Sun, 25 Sep 2011 344 | ========================= 345 | 346 | 347 | v0.4.2 / Sun, 25 Sep 2011 348 | ========================= 349 | * [e243b0b](https://github.com/projects/nconf/commit/e243b0b) [dist] Version bump. 0.4.2 (`indexzero`) 350 | * [d0aee0d](https://github.com/projects/nconf/commit/d0aee0d) [api test] Added `.sources` option for `nconf.Provider` for readonly configuration data (`indexzero`) 351 | * [0234e17](https://github.com/projects/nconf/commit/0234e17) [fix] Update bad variable reference (`indexzero`) 352 | 353 | v0.4.1 / Mon, 19 Sep 2011 354 | ========================= 355 | 356 | 357 | v0.4.1 / Mon, 19 Sep 2011 358 | ========================= 359 | * [d334d07](https://github.com/projects/nconf/commit/d334d07) [dist] Version bump. 0.4.1 (`indexzero`) 360 | * [a490c77](https://github.com/projects/nconf/commit/a490c77) [fix] Match case in `require` statements (`indexzero`) 361 | 362 | v0.4.0 / Sun, 18 Sep 2011 363 | ========================= 364 | 365 | 366 | v0.4.0 / Sun, 18 Sep 2011 367 | ========================= 368 | * [0addce4](https://github.com/projects/nconf/commit/0addce4) [dist] Version bump. 0.4.0 (`indexzero`) 369 | * [c4c8d7b](https://github.com/projects/nconf/commit/c4c8d7b) [doc] Updated docco docs (`indexzero`) 370 | * [f867e74](https://github.com/projects/nconf/commit/f867e74) [dist] Remove unused test fixtures (`indexzero`) 371 | * [1ef5797](https://github.com/projects/nconf/commit/1ef5797) [api test] Finished API and tests for hierarchical configuration storage. (`indexzero`) 372 | * [7ef9b11](https://github.com/projects/nconf/commit/7ef9b11) [doc] Minor update to library `title` (`indexzero`) 373 | * [a063880](https://github.com/projects/nconf/commit/a063880) [doc] Updated usage.js and README.md for the next hierarchical syntax. (`indexzero`) 374 | * [da2da7a](https://github.com/projects/nconf/commit/da2da7a) [api test breaking refactor] Significant refactor to how nconf works. Now a fully hierarchical configuration storage mechanism capable of multiple levels of stores of the same type. (`indexzero`) 375 | * [2bda7b6](https://github.com/projects/nconf/commit/2bda7b6) [api] Added `nconf.stores.System` (`indexzero`) 376 | 377 | v0.3.1 / Mon, 29 Aug 2011 378 | ========================= 379 | 380 | 381 | v0.3.1 / Mon, 29 Aug 2011 382 | ========================= 383 | * [54ea095](https://github.com/projects/nconf/commit/54ea095) [dist] Version bump. 0.3.1 (`indexzero`) 384 | * [e631d23](https://github.com/projects/nconf/commit/e631d23) [fix] Lazy-load any CLI arguments from `optimist` (`indexzero`) 385 | 386 | v0.3.0 / Sun, 28 Aug 2011 387 | ========================= 388 | 389 | 390 | v0.3.0 / Sun, 28 Aug 2011 391 | ========================= 392 | * [8a31728](https://github.com/projects/nconf/commit/8a31728) [dist] Version bump. 0.3.0 (`indexzero`) 393 | * [2e47d02](https://github.com/projects/nconf/commit/2e47d02) [doc] Updated README.md (`indexzero`) 394 | * [954b5fd](https://github.com/projects/nconf/commit/954b5fd) [doc] Updated docco docs (`indexzero`) 395 | * [fb392dd](https://github.com/projects/nconf/commit/fb392dd) [api test] Updated test/provider-test.js and associated merge implementation (`indexzero`) 396 | * [e8904e9](https://github.com/projects/nconf/commit/e8904e9) [api] Added `nconf.loadFiles()` method (`indexzero`) 397 | * [a6533aa](https://github.com/projects/nconf/commit/a6533aa) [dist api test] Finished integrating features from reconf and updating associated tests (`indexzero`) 398 | * [add8922](https://github.com/projects/nconf/commit/add8922) [api dist] Begin to integrate features from reconf (`indexzero`) 399 | * [57f0742](https://github.com/projects/nconf/commit/57f0742) [doc] Update README.md for nconf-redis (`indexzero`) 400 | 401 | v0.2.0 / Fri, 8 Jul 2011 402 | ======================== 403 | 404 | 405 | v0.2.0 / Fri, 8 Jul 2011 406 | ======================== 407 | * [b6adab2](https://github.com/projects/nconf/commit/b6adab2) [dist] Version bump. 0.2.0 (`indexzero`) 408 | * [8620e6b](https://github.com/projects/nconf/commit/8620e6b) [api test] Remove Redis store in preparation for nconf-redis (`indexzero`) 409 | * [49a1a6d](https://github.com/projects/nconf/commit/49a1a6d) [dist] Added LICENSE (MIT ftw) (`indexzero`) 410 | 411 | 0.1.14 / Sat, 25 Jun 2011 412 | ========================= 413 | * [d485f5e](https://github.com/projects/nconf/commit/d485f5e) [dist] Version bump. 0.1.14 (`indexzero`) 414 | * [7e4623e](https://github.com/projects/nconf/commit/7e4623e) [api test] Update `nconf.Provider` to create a new instance of the store if the options are different (`indexzero`) 415 | 416 | v0.1.13 / Fri, 24 Jun 2011 417 | ========================== 418 | 419 | 420 | v0.1.13 / Fri, 24 Jun 2011 421 | ========================== 422 | * [1b0f347](https://github.com/projects/nconf/commit/1b0f347) [dist] Version bump. 0.1.13 (`indexzero`) 423 | * [d8b5a80](https://github.com/projects/nconf/commit/d8b5a80) [minor] Small style updates to the File store (`indexzero`) 424 | * [c436851](https://github.com/projects/nconf/commit/c436851) [refactor]: Cleaned up error handling on File.loadSync and File.load [refactor]: Using path module to determine if file exists instead of throwing error [api]: File.load and File.loadSync will now automatically create the requested JSON file path if no file is found. (`Marak Squires`) 425 | * [6c6887a](https://github.com/projects/nconf/commit/6c6887a) move callback outside of try / catch (`Dominic Tarr`) 426 | 427 | v0.1.12 / Wed, 8 Jun 2011 428 | ========================= 429 | 430 | 431 | v0.1.12 / Wed, 8 Jun 2011 432 | ========================= 433 | * [ae5aec6](https://github.com/projects/nconf/commit/ae5aec6) [dist] Version bump. 0.1.12 (`indexzero`) 434 | * [76db254](https://github.com/projects/nconf/commit/76db254) [fix test] Update nconf.stores.File to respond with an error when loading malformed JSON async (`indexzero`) 435 | 436 | v0.1.11 / Tue, 7 Jun 2011 437 | ========================= 438 | 439 | 440 | v0.1.11 / Tue, 7 Jun 2011 441 | ========================= 442 | * [d7495f8](https://github.com/projects/nconf/commit/d7495f8) [dist] Version bump. 0.1.11 (`indexzero`) 443 | * [4c7aea9](https://github.com/projects/nconf/commit/4c7aea9) [doc] Update docco docs (`indexzero`) 444 | * [f611066](https://github.com/projects/nconf/commit/f611066) [dist] Update to pkginfo 0.2.0 (`indexzero`) 445 | 446 | v0.1.10 / Sun, 5 Jun 2011 447 | ========================= 448 | 449 | 450 | v0.1.10 / Sun, 5 Jun 2011 451 | ========================= 452 | * [be76887](https://github.com/projects/nconf/commit/be76887) [dist] Version bump. 0.1.10 (`indexzero`) 453 | * [7ffbf0a](https://github.com/projects/nconf/commit/7ffbf0a) [doc] Regenerate docco docs (`indexzero`) 454 | * [13f5753](https://github.com/projects/nconf/commit/13f5753) [minor] Update `nconf.version` to use pkginfo (`indexzero`) 455 | * [c9e60d9](https://github.com/projects/nconf/commit/c9e60d9) [doc] Update code docs (`indexzero`) 456 | * [4459ba5](https://github.com/projects/nconf/commit/4459ba5) [api] Added `.merge()` to stores.Memory and stores.Redis (`indexzero`) 457 | * [a4f00be](https://github.com/projects/nconf/commit/a4f00be) [dist] Update package.json and .gitignore (`indexzero`) 458 | * [8a79ef0](https://github.com/projects/nconf/commit/8a79ef0) test retrieving non-existent keys and drilling into non-objects (`Sami Samhuri`) 459 | * [6acc1fc](https://github.com/projects/nconf/commit/6acc1fc) allow storing null in redis (`Sami Samhuri`) 460 | * [faa8ab9](https://github.com/projects/nconf/commit/faa8ab9) correctly retrieve falsy values from memory (hence file) (`Sami Samhuri`) 461 | * [bdf2fc8](https://github.com/projects/nconf/commit/bdf2fc8) [fix] Fixed spelling error (`avian`) 462 | * [e7c216e](https://github.com/projects/nconf/commit/e7c216e) [minor] Clarified error message returned when a config file contains invalid JSON. (`avian`) 463 | * [e26bbe2](https://github.com/projects/nconf/commit/e26bbe2) [doc] Updated code samples for GitHub flavored markdown with Javascript (`indexzero`) 464 | 465 | v0.1.9 / Mon, 16 May 2011 466 | ========================= 467 | 468 | 469 | v0.1.9 / Mon, 16 May 2011 470 | ========================= 471 | * [78202ec](https://github.com/projects/nconf/commit/78202ec) [dist] Version bump. 0.1.9 (`indexzero`) 472 | * [87351ca](https://github.com/projects/nconf/commit/87351ca) [fix] Use the memory engine by default (`indexzero`) 473 | 474 | v0.1.8 / Mon, 16 May 2011 475 | ========================= 476 | 477 | 478 | v0.1.8 / Mon, 16 May 2011 479 | ========================= 480 | * [badbb59](https://github.com/projects/nconf/commit/badbb59) [dist] Version bump. 0.1.8 (`indexzero`) 481 | * [9da37df](https://github.com/projects/nconf/commit/9da37df) [dist api test] Refactor pluggable nconf-level logic into nconf.Provider. Update .gitignore for npm 1.0. Update pathing in source and tests to be more `require.paths` future-proof (`indexzero`) 482 | 483 | v0.1.7 / Wed, 20 Apr 2011 484 | ========================= 485 | 486 | 487 | v0.1.7 / Wed, 20 Apr 2011 488 | ========================= 489 | * [4a61560](https://github.com/projects/nconf/commit/4a61560) [dist] Version bump. 0.1.7 (`indexzero`) 490 | * [3b104f2](https://github.com/projects/nconf/commit/3b104f2) [doc] Update docco docs (`indexzero`) 491 | * [d65922d](https://github.com/projects/nconf/commit/d65922d) [api] Add `.saveSync()` and `.loadSync()` methods to File store (`indexzero`) 492 | 493 | v0.1.6 / Tue, 19 Apr 2011 494 | ========================= 495 | 496 | 497 | v0.1.6 / Tue, 19 Apr 2011 498 | ========================= 499 | * [b9951b4](https://github.com/projects/nconf/commit/b9951b4) [dist] Version bump. 0.1.6. (`indexzero`) 500 | * [da85594](https://github.com/projects/nconf/commit/da85594) [doc] Update docco docs (`indexzero`) 501 | * [067d58a](https://github.com/projects/nconf/commit/067d58a) [minor test] Add tests for File store `save()`. Improve default file format to pretty print the JSON output (`indexzero`) 502 | 503 | v0.1.5 / Wed, 13 Apr 2011 504 | ========================= 505 | 506 | 507 | v0.1.5 / Wed, 13 Apr 2011 508 | ========================= 509 | * [96859f9](https://github.com/projects/nconf/commit/96859f9) [dist] Version bump. 0.1.5 (`indexzero`) 510 | * [d99ab32](https://github.com/projects/nconf/commit/d99ab32) [fix] Dont allow `async.forEach` to be called on undefined or null arrays (`indexzero`) 511 | 512 | v0.1.4 / Tue, 5 Apr 2011 513 | ======================== 514 | 515 | 516 | v0.1.4 / Tue, 5 Apr 2011 517 | ======================== 518 | * [7484fdb](https://github.com/projects/nconf/commit/7484fdb) [dist] Version bump. 0.1.4 (`indexzero`) 519 | * [04a59e9](https://github.com/projects/nconf/commit/04a59e9) [fix] Supress errors from Redis (`indexzero`) 520 | 521 | v0.1.3 / Tue, 5 Apr 2011 522 | ======================== 523 | 524 | 525 | v0.1.3 / Tue, 5 Apr 2011 526 | ======================== 527 | * [9bd6e26](https://github.com/projects/nconf/commit/9bd6e26) [dist] Version bump. 0.1.3 (`indexzero`) 528 | * [4094125](https://github.com/projects/nconf/commit/4094125) [api] Add support for Redis auth and optional callbacks. (`indexzero`) 529 | 530 | v0.1.2 / Sun, 3 Apr 2011 531 | ======================== 532 | 533 | 534 | v0.1.2 / Sun, 3 Apr 2011 535 | ======================== 536 | * [81e1883](https://github.com/projects/nconf/commit/81e1883) [dist] Version bump. 0.1.2 (`indexzero`) 537 | * [b850ae2](https://github.com/projects/nconf/commit/b850ae2) [fix] Update path to require statement in Redis store (`indexzero`) 538 | 539 | v0.1.1 / Sat, 2 Apr 2011 540 | ======================== 541 | 542 | 543 | v0.1.1 / Sat, 2 Apr 2011 544 | ======================== 545 | * [6f16bc7](https://github.com/projects/nconf/commit/6f16bc7) [dist] Version bump. 0.1.1 (`indexzero`) 546 | * [752bb98](https://github.com/projects/nconf/commit/752bb98) [api] Improve the `.use()` method. Use the memory engine by default (`indexzero`) 547 | 548 | v0.1.0 / Sat, 2 Apr 2011 549 | ======================== 550 | 551 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011 Charlie Robbins and the Contributors. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nconf 2 | 3 | [![Version npm](https://img.shields.io/npm/v/nconf.svg?style=flat-square)](https://www.npmjs.com/package/nconf)[![npm Downloads](https://img.shields.io/npm/dm/nconf.svg?style=flat-square)](https://www.npmjs.com/package/nconf)[![Build Status](https://img.shields.io/travis/indexzero/nconf/master.svg?style=flat-square)](https://travis-ci.org/indexzero/nconf)[![Coverage](https://img.shields.io/coveralls/indexzero/nconf.svg?style=flat-square)](https://coveralls.io/github/indexzero/nconf)[![Dependencies](https://img.shields.io/david/indexzero/nconf.svg?style=flat-square)](https://david-dm.org/indexzero/nconf) 4 | 5 | Hierarchical node.js configuration with files, environment variables, command-line arguments, and atomic object merging. 6 | 7 | ## Example 8 | Using nconf is easy; it is designed to be a simple key-value store with support for both local and remote storage. Keys are namespaced and delimited by `:`. Let's dive right into sample usage: 9 | 10 | ``` js 11 | // sample.js 12 | var nconf = require('nconf'); 13 | 14 | // 15 | // Setup nconf to use (in-order): 16 | // 1. Command-line arguments 17 | // 2. Environment variables 18 | // 3. A file located at 'path/to/config.json' 19 | // 20 | nconf.argv() 21 | .env() 22 | .file({ file: 'path/to/config.json' }); 23 | 24 | // 25 | // Set a few variables on `nconf`. 26 | // 27 | nconf.set('database:host', '127.0.0.1'); 28 | nconf.set('database:port', 5984); 29 | 30 | // 31 | // Get the entire database object from nconf. This will output 32 | // { host: '127.0.0.1', port: 5984 } 33 | // 34 | console.log('foo: ' + nconf.get('foo')); 35 | console.log('NODE_ENV: ' + nconf.get('NODE_ENV')); 36 | console.log('database: ' + nconf.get('database')); 37 | 38 | // 39 | // Save the configuration object to disk 40 | // 41 | nconf.save(function (err) { 42 | require('fs').readFile('path/to/your/config.json', function (err, data) { 43 | console.dir(JSON.parse(data.toString())) 44 | }); 45 | }); 46 | ``` 47 | 48 | If you run the below script: 49 | 50 | ``` bash 51 | $ NODE_ENV=production node sample.js --foo bar 52 | ``` 53 | 54 | The output will be: 55 | 56 | ``` 57 | foo: bar 58 | NODE_ENV: production 59 | database: { host: '127.0.0.1', port: 5984 } 60 | ``` 61 | 62 | ## Hierarchical configuration 63 | 64 | Configuration management can get complicated very quickly for even trivial applications running in production. `nconf` addresses this problem by enabling you to setup a hierarchy for different sources of configuration with no defaults. **The order in which you attach these configuration sources determines their priority in the hierarchy.** Let's take a look at the options available to you 65 | 66 | 1. **nconf.argv(options)** Loads `process.argv` using yargs. If `options` is supplied it is passed along to yargs. 67 | 2. **nconf.env(options)** Loads `process.env` into the hierarchy. 68 | 3. **nconf.file(options)** Loads the configuration data at options.file into the hierarchy. 69 | 4. **nconf.defaults(options)** Loads the data in options.store into the hierarchy. 70 | 5. **nconf.overrides(options)** Loads the data in options.store into the hierarchy. 71 | 72 | A sane default for this could be: 73 | 74 | ``` js 75 | var nconf = require('nconf'); 76 | 77 | // 78 | // 1. any overrides 79 | // 80 | nconf.overrides({ 81 | 'always': 'be this value' 82 | }); 83 | 84 | // 85 | // 2. `process.env` 86 | // 3. `process.argv` 87 | // 88 | nconf.env().argv(); 89 | 90 | // 91 | // 4. Values in `config.json` 92 | // 93 | nconf.file('/path/to/config.json'); 94 | 95 | // 96 | // Or with a custom name 97 | // Note: A custom key must be supplied for hierarchy to work if multiple files are used. 98 | // 99 | nconf.file('custom', '/path/to/config.json'); 100 | 101 | // 102 | // Or searching from a base directory. 103 | // Note: `name` is optional. 104 | // 105 | nconf.file(name, { 106 | file: 'config.json', 107 | dir: 'search/from/here', 108 | search: true 109 | }); 110 | 111 | // 112 | // 5. Any default values 113 | // 114 | nconf.defaults({ 115 | 'if nothing else': 'use this value' 116 | }); 117 | ``` 118 | 119 | ## API Documentation 120 | 121 | The top-level of `nconf` is an instance of the `nconf.Provider` abstracts this all for you into a simple API. 122 | 123 | ### nconf.add(name, options) 124 | Adds a new store with the specified `name` and `options`. If `options.type` is not set, then `name` will be used instead: 125 | 126 | ``` js 127 | nconf.add('supplied', { type: 'literal', store: { 'some': 'config' } }); 128 | nconf.add('user', { type: 'file', file: '/path/to/userconf.json' }); 129 | nconf.add('global', { type: 'file', file: '/path/to/globalconf.json' }); 130 | ``` 131 | 132 | ### nconf.any(names, callback) 133 | Given a set of key names, gets the value of the first key found to be truthy. The key names can be given as separate arguments 134 | or as an array. If the last argument is a function, it will be called with the result; otherwise, the value is returned. 135 | 136 | ``` js 137 | // 138 | // Get one of 'NODEJS_PORT' and 'PORT' as a return value 139 | // 140 | var port = nconf.any('NODEJS_PORT', 'PORT'); 141 | 142 | // 143 | // Get one of 'NODEJS_IP' and 'IPADDRESS' using a callback 144 | // 145 | nconf.any(['NODEJS_IP', 'IPADDRESS'], function(err, value) { 146 | console.log('Connect to IP address ' + value); 147 | }); 148 | ``` 149 | 150 | ### nconf.use(name, options) 151 | Similar to `nconf.add`, except that it can replace an existing store if new options are provided 152 | 153 | ``` js 154 | // 155 | // Load a file store onto nconf with the specified settings 156 | // 157 | nconf.use('file', { file: '/path/to/some/config-file.json' }); 158 | 159 | // 160 | // Replace the file store with new settings 161 | // 162 | nconf.use('file', { file: 'path/to/a-new/config-file.json' }); 163 | ``` 164 | 165 | ### nconf.remove(name) 166 | Removes the store with the specified `name.` The configuration stored at that level will no longer be used for lookup(s). 167 | 168 | ``` js 169 | nconf.remove('file'); 170 | ``` 171 | 172 | ### nconf.required(keys) 173 | Declares a set of string keys to be mandatory, and throw an error if any are missing. 174 | 175 | ``` js 176 | nconf.defaults({ 177 | keya: 'a', 178 | }); 179 | 180 | nconf.required(['keya', 'keyb']); 181 | // Error: Missing required keys: keyb 182 | ``` 183 | You can also chain `.required()` calls when needed. for example when a configuration depends on another configuration store 184 | 185 | ```js 186 | config 187 | .argv() 188 | .env() 189 | .required([ 'STAGE']) //here you should have STAGE otherwise throw an error 190 | .file( 'stage', path.resolve( 'configs', 'stages', config.get( 'STAGE' ) + '.json' ) ) 191 | .required([ 'OAUTH:redirectURL']) // here you should have OAUTH:redirectURL, otherwise throw an error 192 | .file( 'oauth', path.resolve( 'configs', 'oauth', config.get( 'OAUTH:MODE' ) + '.json' ) ) 193 | .file( 'app', path.resolve( 'configs', 'app.json' ) ) 194 | .required([ 'LOGS_MODE']) // here you should haveLOGS_MODE, otherwise throw an error 195 | .add( 'logs', { 196 | type: 'literal', 197 | store: require( path.resolve( 'configs', 'logs', config.get( 'LOGS_MODE' ) + '.js') ) 198 | } ) 199 | .defaults( defaults ); 200 | ``` 201 | 202 | ## Storage Engines 203 | 204 | ### Memory 205 | A simple in-memory storage engine that stores a nested JSON representation of the configuration. To use this engine, just call `.use()` with the appropriate arguments. All calls to `.get()`, `.set()`, `.clear()`, `.reset()` methods are synchronous since we are only dealing with an in-memory object. 206 | 207 | All built-in storage engines inherit from the Memory store. 208 | 209 | Basic usage: 210 | 211 | ``` js 212 | nconf.use('memory'); 213 | ``` 214 | 215 | #### Options 216 | 217 | The options defined below apply to all storage engines that inherit from Memory. 218 | 219 | ##### `accessSeparator: string` (default: `':'`) 220 | Defines the separator used to get or set data using the `get()` and `set()` methods. Even if this is changed, the default "colon" separator will be available unless explicitly disabled (see `disableDefaultAccessSeparator`). 221 | 222 | ##### `inputSeparator: string` (default: `'__'`) 223 | This option is used by the `argv` and `env` storage engines when loading values. Since most systems only allow dashes, underscores, and alphanumeric characters in environment variables and command line arguments, the `inputSeparator` provides a mechanism for loading hierarchical values from these sources. 224 | 225 | ##### `disableDefaultAccessSeparator: {true|false}` (default: `false`) 226 | Disables the default access separator of `':'`, which is always available otherwise. This is mainly used to preserve legacy behavior. It can also be used to set keys that contain the default separator (e.g. `{ 'some:long:key' : 'some value' }`). 227 | 228 | ### Argv 229 | Responsible for loading the values parsed from `process.argv` by `yargs` into the configuration hierarchy. See the [yargs option docs](https://github.com/bcoe/yargs#optionskey-opt) for more on the option format. 230 | 231 | #### Options 232 | 233 | ##### `parseValues: {true|false}` (default: `false`) 234 | Attempt to parse well-known values (e.g. 'false', 'true', 'null', 'undefined', '3', '5.1' and JSON values) 235 | into their proper types. If a value cannot be parsed, it will remain a string. 236 | 237 | ##### `transform: function(obj)` 238 | Pass each key/value pair to the specified function for transformation. 239 | 240 | The input `obj` contains two properties passed in the following format: 241 | ``` js 242 | { 243 | key: '', 244 | value: '' 245 | } 246 | ``` 247 | 248 | The transformation function may alter both the key and the value. 249 | 250 | The function may return either an object in the same format as the input or a value that evaluates to false. 251 | If the return value is falsey, the entry will be dropped from the store, otherwise it will replace the original key/value. 252 | 253 | *Note: If the return value doesn't adhere to the above rules, an exception will be thrown.* 254 | 255 | #### Examples 256 | 257 | ``` js 258 | // 259 | // Can optionally also be an object literal to pass to `yargs`. 260 | // 261 | nconf.argv({ 262 | "x": { 263 | alias: 'example', 264 | describe: 'Example description for usage generation', 265 | demand: true, 266 | default: 'some-value', 267 | parseValues: true, 268 | transform: function(obj) { 269 | if (obj.key === 'foo') { 270 | obj.value = 'baz'; 271 | } 272 | return obj; 273 | } 274 | } 275 | }); 276 | ``` 277 | 278 | It's also possible to pass a configured yargs instance 279 | 280 | ``` js 281 | nconf.argv(require('yargs') 282 | .version('1.2.3') 283 | .usage('My usage definition') 284 | .strict() 285 | .options({ 286 | "x": { 287 | alias: 'example', 288 | describe: 'Example description for usage generation', 289 | demand: true, 290 | default: 'some-value' 291 | } 292 | })); 293 | ``` 294 | 295 | ### Env 296 | Responsible for loading the values parsed from `process.env` into the configuration hierarchy. 297 | By default, the env variables values are loaded into the configuration as strings. 298 | 299 | #### Options 300 | 301 | ##### `lowerCase: {true|false}` (default: `false`) 302 | Convert all input keys to lower case. Values are not modified. 303 | 304 | If this option is enabled, all calls to `nconf.get()` must pass in a lowercase string (e.g. `nconf.get('port')`) 305 | 306 | ##### `parseValues: {true|false}` (default: `false`) 307 | Attempt to parse well-known values (e.g. 'false', 'true', 'null', 'undefined', '3', '5.1' and JSON values) 308 | into their proper types. If a value cannot be parsed, it will remain a string. 309 | 310 | ##### `transform: function(obj)` 311 | Pass each key/value pair to the specified function for transformation. 312 | 313 | The input `obj` contains two properties passed in the following format: 314 | ``` js 315 | { 316 | key: '', 317 | value: '' 318 | } 319 | ``` 320 | 321 | The transformation function may alter both the key and the value. 322 | 323 | The function may return either an object in the same format as the input or a value that evaluates to false. 324 | If the return value is falsey, the entry will be dropped from the store, otherwise it will replace the original key/value. 325 | 326 | *Note: If the return value doesn't adhere to the above rules, an exception will be thrown.* 327 | 328 | #### `readOnly: {true|false}` (default: `true`) 329 | Allow values in the env store to be updated in the future. The default is to not allow items in the env store to be updated. 330 | 331 | #### Examples 332 | 333 | ``` js 334 | // 335 | // Can optionally also be an Array of values to limit process.env to. 336 | // 337 | nconf.env(['only', 'load', 'these', 'values', 'from', 'process.env']); 338 | 339 | // 340 | // Can also specify an input separator for nested keys 341 | // 342 | nconf.env('__'); 343 | // Get the value of the env variable 'database__host' 344 | var dbHost = nconf.get('database:host'); 345 | 346 | // 347 | // Can also lowerCase keys. 348 | // Especially handy when dealing with environment variables which are usually 349 | // uppercased while argv are lowercased. 350 | // 351 | 352 | // Given an environment variable PORT=3001 353 | nconf.env(); 354 | var port = nconf.get('port') // undefined 355 | 356 | nconf.env({ lowerCase: true }); 357 | var port = nconf.get('port') // 3001 358 | 359 | // 360 | // Or use all options 361 | // 362 | nconf.env({ 363 | inputSeparator: '__', 364 | match: /^whatever_matches_this_will_be_whitelisted/ 365 | whitelist: ['database__host', 'only', 'load', 'these', 'values', 'if', 'whatever_doesnt_match_but_is_whitelisted_gets_loaded_too'], 366 | lowerCase: true, 367 | parseValues: true, 368 | transform: function(obj) { 369 | if (obj.key === 'foo') { 370 | obj.value = 'baz'; 371 | } 372 | return obj; 373 | } 374 | }); 375 | var dbHost = nconf.get('database:host'); 376 | ``` 377 | 378 | ### Literal 379 | Loads a given object literal into the configuration hierarchy. Both `nconf.defaults()` and `nconf.overrides()` use the Literal store. 380 | 381 | ``` js 382 | nconf.defaults({ 383 | 'some': 'default value' 384 | }); 385 | ``` 386 | 387 | ### File 388 | Based on the Memory store, but provides additional methods `.save()` and `.load()` which allow you to read your configuration to and from file. As with the Memory store, all method calls are synchronous with the exception of `.save()` and `.load()` which take callback functions. 389 | 390 | It is important to note that setting keys in the File engine will not be persisted to disk until a call to `.save()` is made. Note a custom key must be supplied as the first parameter for hierarchy to work if multiple files are used. 391 | 392 | ``` js 393 | nconf.file('path/to/your/config.json'); 394 | // add multiple files, hierarchically. notice the unique key for each file 395 | nconf.file('user', 'path/to/your/user.json'); 396 | nconf.file('global', 'path/to/your/global.json'); 397 | ``` 398 | 399 | The file store is also extensible for multiple file formats, defaulting to `JSON`. To use a custom format, simply pass a format object to the `.use()` method. This object must have `.parse()` and `.stringify()` methods just like the native `JSON` object. 400 | 401 | If the file does not exist at the provided path, the store will simply be empty. 402 | 403 | #### Encrypting file contents 404 | 405 | As of `nconf@0.8.0` it is now possible to encrypt and decrypt file contents using the `secure` option: 406 | 407 | ``` js 408 | nconf.file('secure-file', { 409 | file: 'path/to/secure-file.json', 410 | secure: { 411 | secret: 'super-secretzzz-keyzz', 412 | alg: 'aes-256-ctr' 413 | } 414 | }) 415 | ``` 416 | 417 | This will encrypt each key using [`crypto.createCipheriv`](https://nodejs.org/api/crypto.html#crypto_crypto_createcipheriv_algorithm_key_iv_options), defaulting to `aes-256-ctr`. The encrypted file contents will look like this: 418 | 419 | ``` js 420 | { 421 | "config-key-name": { 422 | "alg": "aes-256-ctr", // cipher used 423 | "value": "af07fbcf", // encrypted contents 424 | "iv": "49e7803a2a5ef98c7a51a8902b76dd10" // initialization vector 425 | }, 426 | "another-config-key": { 427 | "alg": "aes-256-ctr", // cipher used 428 | "value": "e310f6d94f13", // encrypted contents 429 | "iv": "b654e01aed262f37d0acf200be193985" // initialization vector 430 | }, 431 | } 432 | ``` 433 | 434 | ### Redis 435 | There is a separate Redis-based store available through [nconf-redis][0]. To install and use this store simply: 436 | 437 | ``` bash 438 | $ npm install nconf 439 | $ npm install nconf-redis 440 | ``` 441 | 442 | Once installing both `nconf` and `nconf-redis`, you must require both modules to use the Redis store: 443 | 444 | ``` js 445 | var nconf = require('nconf'); 446 | 447 | // 448 | // Requiring `nconf-redis` will extend the `nconf` 449 | // module. 450 | // 451 | require('nconf-redis'); 452 | 453 | nconf.use('redis', { host: 'localhost', port: 6379, ttl: 60 * 60 * 1000 }); 454 | ``` 455 | 456 | ## Installation 457 | ``` bash 458 | npm install nconf --save 459 | ``` 460 | 461 | ## Run Tests 462 | Tests are written in vows and give complete coverage of all APIs and storage engines. 463 | 464 | ``` bash 465 | $ npm test 466 | ``` 467 | 468 | #### Author: [Charlie Robbins](http://nodejitsu.com) 469 | #### License: MIT 470 | 471 | [0]: http://github.com/indexzero/nconf-redis 472 | -------------------------------------------------------------------------------- /lib/nconf.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nconf.js: Top-level include for the nconf module 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var common = require('./nconf/common'), 9 | Provider = require('./nconf/provider').Provider; 10 | 11 | // 12 | // `nconf` is by default an instance of `nconf.Provider`. 13 | // 14 | var nconf = module.exports = new Provider(); 15 | 16 | // 17 | // Expose the version from the package.json 18 | // 19 | nconf.version = require('../package.json').version; 20 | 21 | // 22 | // Setup all stores as lazy-loaded getters. 23 | // 24 | nconf.__defineGetter__('Argv', function () { 25 | return require('./nconf/stores/argv').Argv; 26 | }); 27 | 28 | nconf.__defineGetter__('Env', function () { 29 | return require('./nconf/stores/env').Env; 30 | }); 31 | 32 | nconf.__defineGetter__('File', function () { 33 | return require('./nconf/stores/file').File; 34 | }); 35 | 36 | nconf.__defineGetter__('Literal', function () { 37 | return require('./nconf/stores/literal').Literal; 38 | }); 39 | 40 | nconf.__defineGetter__('Memory', function () { 41 | return require('./nconf/stores/memory').Memory; 42 | }); 43 | 44 | // 45 | // Expose the various components included with nconf 46 | // 47 | nconf.key = common.key; 48 | nconf.path = common.path; 49 | nconf.loadFiles = common.loadFiles; 50 | nconf.loadFilesSync = common.loadFilesSync; 51 | nconf.formats = require('./nconf/formats'); 52 | nconf.Provider = Provider; 53 | -------------------------------------------------------------------------------- /lib/nconf/common.js: -------------------------------------------------------------------------------- 1 | /* 2 | * utils.js: Utility functions for the nconf module. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var fs = require('fs'), 9 | async = require('async'), 10 | formats = require('./formats'), 11 | Memory = require('./stores/memory').Memory; 12 | 13 | var common = exports; 14 | 15 | // 16 | // ### function path (key) 17 | // #### @key {string} The ':' delimited key to split 18 | // Returns a fully-qualified path to a nested nconf key. 19 | // If given null or undefined it should return an empty path. 20 | // '' should still be respected as a path. 21 | // 22 | common.path = function (key, separator) { 23 | separator = separator || ':'; 24 | return key == null ? [] : key.split(separator); 25 | }; 26 | 27 | // 28 | // ### function key (arguments) 29 | // Returns a `:` joined string from the `arguments`. 30 | // 31 | common.key = function () { 32 | return Array.prototype.slice.call(arguments).join(':'); 33 | }; 34 | 35 | // 36 | // ### function key (arguments) 37 | // Returns a joined string from the `arguments`, 38 | // first argument is the join delimiter. 39 | // 40 | common.keyed = function () { 41 | return Array.prototype.slice.call(arguments, 1).join(arguments[0]); 42 | }; 43 | 44 | // 45 | // ### function loadFiles (files, callback) 46 | // #### @files {Object|Array} List of files (or settings object) to load. 47 | // #### @callback {function} Continuation to respond to when complete. 48 | // Loads all the data in the specified `files`. 49 | // 50 | common.loadFiles = function (files, callback) { 51 | if (!files) { 52 | return callback(null, {}); 53 | } 54 | 55 | var options = Array.isArray(files) ? { files: files } : files; 56 | 57 | // 58 | // Set the default JSON format if not already 59 | // specified 60 | // 61 | options.format = options.format || formats.json; 62 | 63 | function parseFile (file, next) { 64 | fs.readFile(file, function (err, data) { 65 | return !err 66 | ? next(null, options.format.parse(data.toString())) 67 | : next(err); 68 | }); 69 | } 70 | 71 | async.map(options.files, parseFile, function (err, objs) { 72 | return err ? callback(err) : callback(null, common.merge(objs)); 73 | }); 74 | }; 75 | 76 | // 77 | // ### function loadFilesSync (files) 78 | // #### @files {Object|Array} List of files (or settings object) to load. 79 | // Loads all the data in the specified `files` synchronously. 80 | // 81 | common.loadFilesSync = function (files) { 82 | if (!files) { 83 | return; 84 | } 85 | 86 | // 87 | // Set the default JSON format if not already 88 | // specified 89 | // 90 | var options = Array.isArray(files) ? { files: files } : files; 91 | options.format = options.format || formats.json; 92 | 93 | return common.merge(options.files.map(function (file) { 94 | return options.format.parse(fs.readFileSync(file, 'utf8')); 95 | })); 96 | }; 97 | 98 | // 99 | // ### function merge (objs) 100 | // #### @objs {Array} Array of object literals to merge 101 | // Merges the specified `objs` using a temporary instance 102 | // of `stores.Memory`. 103 | // 104 | common.merge = function (objs) { 105 | var store = new Memory(); 106 | 107 | objs.forEach(function (obj) { 108 | Object.keys(obj).forEach(function (key) { 109 | store.merge(key, obj[key]); 110 | }); 111 | }); 112 | 113 | return store.store; 114 | }; 115 | 116 | // 117 | // ### function capitalize (str) 118 | // #### @str {string} String to capitalize 119 | // Capitalizes the specified `str`. 120 | // 121 | common.capitalize = function (str) { 122 | return str && str[0].toUpperCase() + str.slice(1); 123 | }; 124 | 125 | // 126 | // ### function parseValues (any) 127 | // #### @any {string} String to parse as native data-type or return as is 128 | // try to parse `any` as a native data-type 129 | // 130 | common.parseValues = function (value) { 131 | var val = value; 132 | 133 | try { 134 | val = JSON.parse(value); 135 | } catch (ignore) { 136 | // Check for any other well-known strings that should be "parsed" 137 | if (value === 'undefined'){ 138 | val = void 0; 139 | } 140 | } 141 | 142 | return val; 143 | }; 144 | 145 | // 146 | // ### function transform(map, fn) 147 | // #### @map {object} Object of key/value pairs to apply `fn` to 148 | // #### @fn {function} Transformation function that will be applied to every key/value pair 149 | // transform a set of key/value pairs and return the transformed result 150 | common.transform = function(map, fn) { 151 | var pairs = Object.keys(map).map(function(key) { 152 | var obj = { key: key, value: map[key]}; 153 | var result = fn.call(null, obj); 154 | 155 | if (!result) { 156 | return null; 157 | } else if (result.key) { 158 | return result; 159 | } 160 | 161 | var error = new Error('Transform function passed to store returned an invalid format: ' + JSON.stringify(result)); 162 | error.name = 'RuntimeError'; 163 | throw error; 164 | }); 165 | 166 | 167 | return pairs 168 | .filter(function(pair) { 169 | return pair !== null; 170 | }) 171 | .reduce(function(accumulator, pair) { 172 | accumulator[pair.key] = pair.value; 173 | return accumulator; 174 | }, {}); 175 | } 176 | -------------------------------------------------------------------------------- /lib/nconf/formats.js: -------------------------------------------------------------------------------- 1 | /* 2 | * formats.js: Default formats supported by nconf 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var ini = require('ini'); 9 | 10 | var formats = exports; 11 | 12 | // 13 | // ### @json 14 | // Standard JSON format which pretty prints `.stringify()`. 15 | // 16 | formats.json = { 17 | stringify: function (obj, replacer, spacing) { 18 | return JSON.stringify(obj, replacer || null, spacing || 2) 19 | }, 20 | parse: JSON.parse 21 | }; 22 | 23 | // 24 | // ### @ini 25 | // Standard INI format supplied from the `ini` module 26 | // http://en.wikipedia.org/wiki/INI_file 27 | // 28 | formats.ini = ini; 29 | -------------------------------------------------------------------------------- /lib/nconf/provider.js: -------------------------------------------------------------------------------- 1 | /* 2 | * provider.js: Abstraction providing an interface into pluggable configuration storage. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var async = require('async'), 9 | common = require('./common'); 10 | 11 | // 12 | // ### function Provider (options) 13 | // #### @options {Object} Options for this instance. 14 | // Constructor function for the Provider object responsible 15 | // for exposing the pluggable storage features of `nconf`. 16 | // 17 | var Provider = exports.Provider = function (options) { 18 | // 19 | // Setup default options for working with `stores`, 20 | // `overrides`, `process.env` and `process.argv`. 21 | // 22 | options = options || {}; 23 | this.stores = {}; 24 | this.sources = []; 25 | this.init(options); 26 | }; 27 | 28 | // 29 | // Define wrapper functions for using basic stores 30 | // in this instance 31 | // 32 | 33 | ['argv', 'env'].forEach(function (type) { 34 | Provider.prototype[type] = function () { 35 | var args = [type].concat(Array.prototype.slice.call(arguments)); 36 | return this.add.apply(this, args); 37 | }; 38 | }); 39 | 40 | // 41 | // ### function file (key, options) 42 | // #### @key {string|Object} Fully qualified options, name of file store, or path. 43 | // #### @path {string|Object} **Optional** Full qualified options, or path. 44 | // Adds a new `File` store to this instance. Accepts the following options 45 | // 46 | // nconf.file({ file: '.jitsuconf', dir: process.env.HOME, search: true }); 47 | // nconf.file('path/to/config/file'); 48 | // nconf.file('userconfig', 'path/to/config/file'); 49 | // nconf.file('userconfig', { file: '.jitsuconf', search: true }); 50 | // 51 | Provider.prototype.file = function (key, options) { 52 | if (arguments.length == 1) { 53 | options = typeof key === 'string' ? { file: key } : key; 54 | key = 'file'; 55 | } 56 | else { 57 | options = typeof options === 'string' 58 | ? { file: options } 59 | : options; 60 | } 61 | 62 | options.type = 'file'; 63 | return this.add(key, options); 64 | }; 65 | 66 | // 67 | // Define wrapper functions for using 68 | // overrides and defaults 69 | // 70 | ['defaults', 'overrides'].forEach(function (type) { 71 | Provider.prototype[type] = function (options) { 72 | options = options || {}; 73 | if (!options.type) { 74 | options.type = 'literal'; 75 | } 76 | 77 | return this.add(type, options); 78 | }; 79 | }); 80 | 81 | // 82 | // ### function use (name, options) 83 | // #### @type {string} Type of the nconf store to use. 84 | // #### @options {Object} Options for the store instance. 85 | // Adds (or replaces) a new store with the specified `name` 86 | // and `options`. If `options.type` is not set, then `name` 87 | // will be used instead: 88 | // 89 | // provider.use('file'); 90 | // provider.use('file', { type: 'file', filename: '/path/to/userconf' }) 91 | // 92 | Provider.prototype.use = function (name, options) { 93 | options = options || {}; 94 | 95 | function sameOptions (store) { 96 | return Object.keys(options).every(function (key) { 97 | return options[key] === store[key]; 98 | }); 99 | } 100 | 101 | var store = this.stores[name], 102 | update = store && !sameOptions(store); 103 | 104 | if (!store || update) { 105 | if (update) { 106 | this.remove(name); 107 | } 108 | 109 | this.add(name, options); 110 | } 111 | 112 | return this; 113 | }; 114 | 115 | // 116 | // ### function add (name, options) 117 | // #### @name {string} Name of the store to add to this instance 118 | // #### @options {Object} Options for the store to create 119 | // Adds a new store with the specified `name` and `options`. If `options.type` 120 | // is not set, then `name` will be used instead: 121 | // 122 | // provider.add('memory'); 123 | // provider.add('userconf', { type: 'file', filename: '/path/to/userconf' }) 124 | // 125 | Provider.prototype.add = function (name, options, usage) { 126 | options = options || {}; 127 | var type = options.type || name; 128 | 129 | if (!require('../nconf')[common.capitalize(type)]) { 130 | throw new Error('Cannot add store with unknown type: ' + type); 131 | } 132 | 133 | this.stores[name] = this.create(type, options, usage); 134 | 135 | if (this.stores[name].loadSync) { 136 | this.stores[name].loadSync(); 137 | } 138 | 139 | return this; 140 | }; 141 | 142 | // 143 | // ### function remove (name) 144 | // #### @name {string} Name of the store to remove from this instance 145 | // Removes a store with the specified `name` from this instance. Users 146 | // are allowed to pass in a type argument (e.g. `memory`) as name if 147 | // this was used in the call to `.add()`. 148 | // 149 | Provider.prototype.remove = function (name) { 150 | delete this.stores[name]; 151 | return this; 152 | }; 153 | 154 | // 155 | // ### function create (type, options) 156 | // #### @type {string} Type of the nconf store to use. 157 | // #### @options {Object} Options for the store instance. 158 | // Creates a store of the specified `type` using the 159 | // specified `options`. 160 | // 161 | Provider.prototype.create = function (type, options, usage) { 162 | return new (require('../nconf')[common.capitalize(type.toLowerCase())])(options, usage); 163 | }; 164 | 165 | // 166 | // ### function init (options) 167 | // #### @options {Object} Options to initialize this instance with. 168 | // Initializes this instance with additional `stores` or `sources` in the 169 | // `options` supplied. 170 | // 171 | Provider.prototype.init = function (options) { 172 | var self = this; 173 | 174 | // 175 | // Add any stores passed in through the options 176 | // to this instance. 177 | // 178 | if (options.type) { 179 | this.add(options.type, options); 180 | } 181 | else if (options.store) { 182 | this.add(options.store.name || options.store.type, options.store); 183 | } 184 | else if (options.stores) { 185 | Object.keys(options.stores).forEach(function (name) { 186 | var store = options.stores[name]; 187 | self.add(store.name || name || store.type, store); 188 | }); 189 | } 190 | 191 | // 192 | // Add any read-only sources to this instance 193 | // 194 | if (options.source) { 195 | this.sources.push(this.create(options.source.type || options.source.name, options.source)); 196 | } 197 | else if (options.sources) { 198 | Object.keys(options.sources).forEach(function (name) { 199 | var source = options.sources[name]; 200 | self.sources.push(self.create(source.type || source.name || name, source)); 201 | }); 202 | } 203 | }; 204 | 205 | // 206 | // ### function get (key, callback) 207 | // #### @key {string} Key to retrieve for this instance. 208 | // #### @callback {function} **Optional** Continuation to respond to when complete. 209 | // Retrieves the value for the specified key (if any). 210 | // 211 | Provider.prototype.get = function (key, callback) { 212 | if (typeof key === 'function') { 213 | // Allow a * key call to be made 214 | callback = key; 215 | key = null; 216 | } 217 | 218 | // 219 | // If there is no callback we can short-circuit into the default 220 | // logic for traversing stores. 221 | // 222 | if (!callback) { 223 | return this._execute('get', 1, key, callback); 224 | } 225 | 226 | // 227 | // Otherwise the asynchronous, hierarchical `get` is 228 | // slightly more complicated because we do not need to traverse 229 | // the entire set of stores, but up until there is a defined value. 230 | // 231 | var current = 0, 232 | names = Object.keys(this.stores), 233 | self = this, 234 | response, 235 | mergeObjs = []; 236 | 237 | async.whilst(function (cb) { 238 | cb(null, typeof response === 'undefined' && current < names.length); 239 | }, function (next) { 240 | var store = self.stores[names[current]]; 241 | current++; 242 | 243 | if (store.get.length >= 2) { 244 | return store.get(key, function (err, value) { 245 | if (err) { 246 | return next(err); 247 | } 248 | 249 | response = value; 250 | 251 | // Merge objects if necessary 252 | if (response && typeof response === 'object' && !Array.isArray(response)) { 253 | mergeObjs.push(response); 254 | response = undefined; 255 | } 256 | 257 | next(); 258 | }); 259 | } 260 | 261 | response = store.get(key); 262 | 263 | // Merge objects if necessary 264 | if (response && typeof response === 'object' && !Array.isArray(response)) { 265 | mergeObjs.push(response); 266 | response = undefined; 267 | } 268 | 269 | next(); 270 | }, function (err) { 271 | if (!err && mergeObjs.length) { 272 | response = common.merge(mergeObjs.reverse()); 273 | } 274 | return err ? callback(err) : callback(null, response); 275 | }); 276 | }; 277 | 278 | 279 | // 280 | // ### function any (keys, callback) 281 | // #### @keys {array|string...} Array of keys to query, or a variable list of strings 282 | // #### @callback {function} **Optional** Continuation to respond to when complete. 283 | // Retrieves the first truthy value (if any) for the specified list of keys. 284 | // 285 | Provider.prototype.any = function (keys, callback) { 286 | 287 | if (!Array.isArray(keys)) { 288 | keys = Array.prototype.slice.call(arguments); 289 | if (keys.length > 0 && typeof keys[keys.length - 1] === 'function') { 290 | callback = keys.pop(); 291 | } else { 292 | callback = null; 293 | } 294 | } 295 | 296 | // 297 | // If there is no callback, use the short-circuited "get" 298 | // on each key in turn. 299 | // 300 | if (!callback) { 301 | var val; 302 | for (var i = 0; i < keys.length; ++i) { 303 | val = this._execute('get', 1, keys[i], callback); 304 | if (val) { 305 | return val; 306 | } 307 | } 308 | return null; 309 | } 310 | 311 | var keyIndex = 0, 312 | result, 313 | self = this; 314 | 315 | async.whilst(function(cb) { 316 | cb(null, !result && keyIndex < keys.length); 317 | }, function(next) { 318 | var key = keys[keyIndex]; 319 | keyIndex++; 320 | 321 | self.get(key, function(err, v) { 322 | if (err) { 323 | next(err); 324 | } else { 325 | result = v; 326 | next(); 327 | } 328 | }); 329 | }, function(err) { 330 | return err ? callback(err) : callback(null, result); 331 | }); 332 | }; 333 | 334 | 335 | // 336 | // ### function set (key, value, callback) 337 | // #### @key {string} Key to set in this instance 338 | // #### @value {literal|Object} Value for the specified key 339 | // #### @callback {function} **Optional** Continuation to respond to when complete. 340 | // Sets the `value` for the specified `key` in this instance. 341 | // 342 | Provider.prototype.set = function (key, value, callback) { 343 | return this._execute('set', 2, key, value, callback); 344 | }; 345 | 346 | 347 | // 348 | // ### function required (keys) 349 | // #### @keys {array} List of keys 350 | // Throws an error if any of `keys` has no value, otherwise returns `true` 351 | Provider.prototype.required = function (keys) { 352 | if (!Array.isArray(keys)) { 353 | throw new Error('Incorrect parameter, array expected'); 354 | } 355 | 356 | var missing = []; 357 | keys.forEach(function(key) { 358 | if (typeof this.get(key) === 'undefined') { 359 | missing.push(key); 360 | } 361 | }, this); 362 | 363 | if (missing.length) { 364 | throw new Error('Missing required keys: ' + missing.join(', ')); 365 | } else { 366 | return this; 367 | } 368 | 369 | }; 370 | 371 | // 372 | // ### function reset (callback) 373 | // #### @callback {function} **Optional** Continuation to respond to when complete. 374 | // Clears all keys associated with this instance. 375 | // 376 | Provider.prototype.reset = function (callback) { 377 | return this._execute('reset', 0, callback); 378 | }; 379 | 380 | // 381 | // ### function clear (key, callback) 382 | // #### @key {string} Key to remove from this instance 383 | // #### @callback {function} **Optional** Continuation to respond to when complete. 384 | // Removes the value for the specified `key` from this instance. 385 | // 386 | Provider.prototype.clear = function (key, callback) { 387 | return this._execute('clear', 1, key, callback); 388 | }; 389 | 390 | // 391 | // ### function merge ([key,] value [, callback]) 392 | // #### @key {string} Key to merge the value into 393 | // #### @value {literal|Object} Value to merge into the key 394 | // #### @callback {function} **Optional** Continuation to respond to when complete. 395 | // Merges the properties in `value` into the existing object value at `key`. 396 | // 397 | // 1. If the existing value `key` is not an Object, it will be completely overwritten. 398 | // 2. If `key` is not supplied, then the `value` will be merged into the root. 399 | // 400 | Provider.prototype.merge = function () { 401 | var self = this, 402 | args = Array.prototype.slice.call(arguments), 403 | callback = typeof args[args.length - 1] === 'function' && args.pop(), 404 | value = args.pop(), 405 | key = args.pop(); 406 | 407 | function mergeProperty (prop, next) { 408 | return self._execute('merge', 2, prop, value[prop], next); 409 | } 410 | 411 | if (!key) { 412 | if (Array.isArray(value) || typeof value !== 'object') { 413 | return onError(new Error('Cannot merge non-Object into top-level.'), callback); 414 | } 415 | 416 | return async.forEach(Object.keys(value), mergeProperty, callback || function () { }) 417 | } 418 | 419 | return this._execute('merge', 2, key, value, callback); 420 | }; 421 | 422 | // 423 | // ### function load (callback) 424 | // #### @callback {function} Continuation to respond to when complete. 425 | // Responds with an Object representing all keys associated in this instance. 426 | // 427 | Provider.prototype.load = function (callback) { 428 | var self = this; 429 | 430 | function getStores () { 431 | var stores = Object.keys(self.stores); 432 | stores.reverse(); 433 | return stores.map(function (name) { 434 | return self.stores[name]; 435 | }); 436 | } 437 | 438 | function loadStoreSync(store) { 439 | if (!store.loadSync) { 440 | throw new Error('nconf store ' + store.type + ' has no loadSync() method'); 441 | } 442 | 443 | return store.loadSync(); 444 | } 445 | 446 | function loadStore(store, next) { 447 | if (!store.load && !store.loadSync) { 448 | return next(new Error('nconf store ' + store.type + ' has no load() method')); 449 | } 450 | 451 | return store.loadSync 452 | ? next(null, store.loadSync()) 453 | : store.load(next); 454 | } 455 | 456 | function loadBatch (targets, done) { 457 | if (!done) { 458 | return common.merge(targets.map(loadStoreSync)); 459 | } 460 | 461 | async.map(targets, loadStore, function (err, objs) { 462 | return err ? done(err) : done(null, common.merge(objs)); 463 | }); 464 | } 465 | 466 | function mergeSources (data) { 467 | // 468 | // If `data` was returned then merge it into 469 | // the system store. 470 | // 471 | if (data && typeof data === 'object') { 472 | self.use('sources', { 473 | type: 'literal', 474 | store: data 475 | }); 476 | } 477 | } 478 | 479 | function loadSources () { 480 | var sourceHierarchy = self.sources.splice(0); 481 | sourceHierarchy.reverse(); 482 | 483 | // 484 | // If we don't have a callback and the current 485 | // store is capable of loading synchronously 486 | // then do so. 487 | // 488 | if (!callback) { 489 | mergeSources(loadBatch(sourceHierarchy)); 490 | return loadBatch(getStores()); 491 | } 492 | 493 | loadBatch(sourceHierarchy, function (err, data) { 494 | if (err) { 495 | return callback(err); 496 | } 497 | 498 | mergeSources(data); 499 | return loadBatch(getStores(), callback); 500 | }); 501 | } 502 | 503 | return self.sources.length 504 | ? loadSources() 505 | : loadBatch(getStores(), callback); 506 | }; 507 | 508 | // 509 | // ### function save (callback) 510 | // #### @callback {function} **optional** Continuation to respond to when 511 | // complete. 512 | // Instructs each provider to save. If a callback is provided, we will attempt 513 | // asynchronous saves on the providers, falling back to synchronous saves if 514 | // this isn't possible. If a provider does not know how to save, it will be 515 | // ignored. Returns an object consisting of all of the data which was 516 | // actually saved. 517 | // 518 | Provider.prototype.save = function (value, callback) { 519 | if (!callback && typeof value === 'function') { 520 | callback = value; 521 | value = null; 522 | } 523 | 524 | var self = this, 525 | names = Object.keys(this.stores); 526 | 527 | function saveStoreSync(memo, name) { 528 | var store = self.stores[name]; 529 | 530 | // 531 | // If the `store` doesn't have a `saveSync` method, 532 | // just ignore it and continue. 533 | // 534 | if (store.saveSync) { 535 | var ret = store.saveSync(); 536 | if (typeof ret == 'object' && ret !== null) { 537 | memo.push(ret); 538 | } 539 | } 540 | return memo; 541 | } 542 | 543 | function saveStore(memo, name, next) { 544 | var store = self.stores[name]; 545 | 546 | // 547 | // If the `store` doesn't have a `save` or saveSync` 548 | // method(s), just ignore it and continue. 549 | // 550 | 551 | if (store.save) { 552 | return store.save(value, function (err, data) { 553 | if (err) { 554 | return next(err); 555 | } 556 | 557 | if (typeof data == 'object' && data !== null) { 558 | memo.push(data); 559 | } 560 | 561 | next(null, memo); 562 | }); 563 | } 564 | else if (store.saveSync) { 565 | memo.push(store.saveSync()); 566 | } 567 | 568 | next(null, memo); 569 | } 570 | 571 | // 572 | // If we don't have a callback and the current 573 | // store is capable of saving synchronously 574 | // then do so. 575 | // 576 | if (!callback) { 577 | return common.merge(names.reduce(saveStoreSync, [])); 578 | } 579 | 580 | async.reduce(names, [], saveStore, function (err, objs) { 581 | return err ? callback(err) : callback(null, common.merge(objs)); 582 | }); 583 | }; 584 | 585 | // 586 | // ### @private function _execute (action, syncLength, [arguments]) 587 | // #### @action {string} Action to execute on `this.store`. 588 | // #### @syncLength {number} Function length of the sync version. 589 | // #### @arguments {Array} Arguments array to apply to the action 590 | // Executes the specified `action` on all stores for this instance, ensuring a callback supplied 591 | // to a synchronous store function is still invoked. 592 | // 593 | Provider.prototype._execute = function (action, syncLength /* [arguments] */) { 594 | var args = Array.prototype.slice.call(arguments, 2), 595 | callback = typeof args[args.length - 1] === 'function' && args.pop(), 596 | destructive = ['set', 'clear', 'merge', 'reset'].indexOf(action) !== -1, 597 | self = this, 598 | response, 599 | mergeObjs = [], 600 | keys = Object.keys(this.stores); 601 | 602 | 603 | function runAction (name, next) { 604 | var store = self.stores[name]; 605 | 606 | if (destructive && store.readOnly) { 607 | return next(); 608 | } 609 | 610 | return store[action].length > syncLength 611 | ? store[action].apply(store, args.concat(next)) 612 | : next(null, store[action].apply(store, args)); 613 | } 614 | 615 | if (callback) { 616 | return async.forEach(keys, runAction, function (err) { 617 | return err ? callback(err) : callback(); 618 | }); 619 | } 620 | 621 | keys.forEach(function (name) { 622 | if (typeof response === 'undefined') { 623 | var store = self.stores[name]; 624 | 625 | if (destructive && store.readOnly) { 626 | return; 627 | } 628 | 629 | response = store[action].apply(store, args); 630 | 631 | // Merge objects if necessary 632 | if (response && action === 'get' && typeof response === 'object' && !Array.isArray(response)) { 633 | mergeObjs.push(response); 634 | response = undefined; 635 | } 636 | } 637 | }); 638 | 639 | if (mergeObjs.length) { 640 | response = common.merge(mergeObjs.reverse()); 641 | } 642 | 643 | return response; 644 | } 645 | 646 | // 647 | // Throw the `err` if a callback is not supplied 648 | // 649 | function onError(err, callback) { 650 | if (callback) { 651 | return callback(err); 652 | } 653 | 654 | throw err; 655 | } 656 | -------------------------------------------------------------------------------- /lib/nconf/stores/argv.js: -------------------------------------------------------------------------------- 1 | /* 2 | * argv.js: Simple memory-based store for command-line arguments. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var util = require('util'), 9 | common = require('../common'), 10 | Memory = require('./memory').Memory; 11 | 12 | // 13 | // ### function Argv (options) 14 | // #### @options {Object} Options for this instance. 15 | // Constructor function for the Argv nconf store, a simple abstraction 16 | // around the Memory store that can read command-line arguments. 17 | // 18 | var Argv = exports.Argv = function (options, usage) { 19 | Memory.call(this, options); 20 | 21 | options = options || {}; 22 | this.type = 'argv'; 23 | this.readOnly = options.readOnly !== undefined? options.readOnly : true; 24 | this.options = options; 25 | this.usage = usage; 26 | 27 | if(typeof options.readOnly === 'boolean') { 28 | this.readOnly = options.readOnly; 29 | delete options.readOnly; 30 | // FIXME; should not mutate options!!!! 31 | } else { 32 | this.readOnly = true; 33 | } 34 | 35 | if (typeof options.transform === 'function') { 36 | this.transform = options.transform; 37 | delete options.transform; 38 | } else { 39 | this.transform = false; 40 | } 41 | }; 42 | 43 | // Inherit from the Memory store 44 | util.inherits(Argv, Memory); 45 | 46 | // 47 | // ### function loadSync () 48 | // Loads the data passed in from `process.argv` into this instance. 49 | // 50 | Argv.prototype.loadSync = function () { 51 | this.loadArgv(); 52 | return this.store; 53 | }; 54 | 55 | // 56 | // ### function loadArgv () 57 | // Loads the data passed in from the command-line arguments 58 | // into this instance. 59 | // 60 | Argv.prototype.loadArgv = function () { 61 | var self = this, 62 | yargs, argv; 63 | 64 | yargs = isYargs(this.options) ? 65 | this.options : 66 | typeof this.options === 'object' ? 67 | require('yargs')(process.argv.slice(2)).options(this.options) : 68 | require('yargs')(process.argv.slice(2)); 69 | 70 | if (typeof this.usage === 'string') { yargs.usage(this.usage) } 71 | 72 | argv = yargs.argv 73 | 74 | if (!argv) { 75 | return; 76 | } 77 | 78 | if (this.transform) { 79 | argv = common.transform(argv, this.transform); 80 | } 81 | 82 | var tempWrite = false; 83 | 84 | if(this.readOnly) { 85 | this.readOnly = false; 86 | tempWrite = true; 87 | } 88 | 89 | Object.keys(argv).forEach(function (key) { 90 | var val = argv[key]; 91 | 92 | if (typeof val !== 'undefined') { 93 | if (self.parseValues) { 94 | val = common.parseValues(val); 95 | } 96 | 97 | self.set(key, val); 98 | } 99 | }); 100 | 101 | this.showHelp = yargs.showHelp 102 | this.help = yargs.help 103 | 104 | if (tempWrite) { 105 | this.readOnly = true; 106 | } 107 | return this.store; 108 | }; 109 | 110 | function isYargs(obj) { 111 | return (typeof obj === 'function' || typeof obj === 'object') && ('argv' in obj); 112 | } 113 | -------------------------------------------------------------------------------- /lib/nconf/stores/env.js: -------------------------------------------------------------------------------- 1 | /* 2 | * env.js: Simple memory-based store for environment variables 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var util = require('util'), 9 | common = require('../common'), 10 | Memory = require('./memory').Memory; 11 | 12 | // 13 | // ### function Env (options) 14 | // #### @options {Object} Options for this instance. 15 | // Constructor function for the Env nconf store, a simple abstraction 16 | // around the Memory store that can read process environment variables. 17 | // 18 | var Env = exports.Env = function (options) { 19 | Memory.call(this, options); 20 | 21 | options = options || {}; 22 | this.type = 'env'; 23 | this.readOnly = options.readOnly !== undefined ? options.readOnly : true; 24 | this.prefix = options.prefix || ''; 25 | this.whitelist = options.whitelist || []; 26 | this.lowerCase = options.lowerCase || false; 27 | this.parseValues = options.parseValues || false; 28 | this.transform = options.transform || false; 29 | 30 | if (({}).toString.call(options.match) === '[object RegExp]' 31 | && typeof options !== 'string') { 32 | this.match = options.match; 33 | } 34 | 35 | if (options instanceof Array) { 36 | this.whitelist = options; 37 | } 38 | }; 39 | 40 | // Inherit from the Memory store 41 | util.inherits(Env, Memory); 42 | 43 | // 44 | // ### function loadSync () 45 | // Loads the data passed in from `process.env` into this instance. 46 | // 47 | Env.prototype.loadSync = function () { 48 | this.loadEnv(); 49 | return this.store; 50 | }; 51 | 52 | // 53 | // ### function loadEnv () 54 | // Loads the data passed in from `process.env` into this instance. 55 | // 56 | Env.prototype.loadEnv = function () { 57 | var self = this; 58 | 59 | var env = process.env; 60 | 61 | if (this.prefix) { 62 | env = {}; 63 | Object.keys(process.env).forEach(function (key) { 64 | if (key.indexOf(self.prefix) === 0) { 65 | env[key.replace(self.prefix, '')] = process.env[key]; 66 | } 67 | }); 68 | } 69 | 70 | if (this.lowerCase) { 71 | env = {}; 72 | Object.keys(process.env).forEach(function (key) { 73 | env[key.toLowerCase()] = process.env[key]; 74 | }); 75 | } 76 | 77 | if (this.transform) { 78 | env = common.transform(env, this.transform); 79 | } 80 | 81 | var tempWrite = false; 82 | 83 | if(this.readOnly) { 84 | this.readOnly = false; 85 | tempWrite = true; 86 | } 87 | 88 | Object.keys(env).filter(function (key) { 89 | if (self.match && self.whitelist.length) { 90 | return key.match(self.match) || self.whitelist.indexOf(key) !== -1 91 | } 92 | else if (self.match) { 93 | return key.match(self.match); 94 | } 95 | else { 96 | return !self.whitelist.length || self.whitelist.indexOf(key) !== -1 97 | } 98 | }).forEach(function (key) { 99 | 100 | var val = env[key]; 101 | 102 | if (self.parseValues) { 103 | val = common.parseValues(val); 104 | } 105 | 106 | self.set(key, val); 107 | }); 108 | 109 | if (tempWrite) { 110 | this.readOnly = true; 111 | } 112 | 113 | return this.store; 114 | }; 115 | -------------------------------------------------------------------------------- /lib/nconf/stores/file.js: -------------------------------------------------------------------------------- 1 | /* 2 | * file.js: Simple file storage engine for nconf files 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var os = require('os'), 9 | fs = require('fs'), 10 | path = require('path'), 11 | util = require('util'), 12 | crypto = require('crypto'), 13 | formats = require('../formats'), 14 | Memory = require('./memory').Memory; 15 | 16 | var exists = fs.exists || path.exists, 17 | existsSync = fs.existsSync || path.existsSync; 18 | 19 | // 20 | // ### function File (options) 21 | // #### @options {Object} Options for this instance 22 | // Constructor function for the File nconf store, a simple abstraction 23 | // around the Memory store that can persist configuration to disk. 24 | // 25 | var File = exports.File = function (options) { 26 | if (!options || !options.file) { 27 | throw new Error('Missing required option `file`'); 28 | } 29 | 30 | Memory.call(this, options); 31 | 32 | this.type = 'file'; 33 | this.file = options.file; 34 | this.dir = options.dir || process.cwd(); 35 | this.format = options.format || formats.json; 36 | this.secure = options.secure; 37 | this.spacing = options.json_spacing 38 | || options.spacing 39 | || 2; 40 | this.eol = !(options.eol === false); 41 | 42 | if (this.secure) { 43 | this.secure = Buffer.isBuffer(this.secure) || typeof this.secure === 'string' 44 | ? { secret: this.secure.toString() } 45 | : this.secure; 46 | 47 | this.secure.alg = this.secure.alg || 'aes-256-ctr'; 48 | if (this.secure.secretPath) { 49 | this.secure.secret = fs.readFileSync(this.secure.secretPath, 'utf8'); 50 | } 51 | 52 | if (!this.secure.secret) { 53 | throw new Error('secure.secret option is required'); 54 | } 55 | } 56 | 57 | if (options.search) { 58 | this.search(this.dir); 59 | } 60 | }; 61 | 62 | // Inherit from the Memory store 63 | util.inherits(File, Memory); 64 | 65 | // 66 | // ### function save (value, callback) 67 | // #### @value {Object} _Ignored_ Left here for consistency 68 | // #### @callback {function} Continuation to respond to when complete. 69 | // Saves the current configuration object to disk at `this.file` 70 | // using the format specified by `this.format`. 71 | // 72 | File.prototype.save = function (value, callback) { 73 | this.saveToFile(this.file, value, callback); 74 | }; 75 | 76 | // 77 | // ### function saveToFile (path, value, callback) 78 | // #### @path {string} The path to the file where we save the configuration to 79 | // #### @format {Object} Optional formatter, default behing the one of the store 80 | // #### @callback {function} Continuation to respond to when complete. 81 | // Saves the current configuration object to disk at `this.file` 82 | // using the format specified by `this.format`. 83 | // 84 | File.prototype.saveToFile = function (path, format, callback) { 85 | if (!callback) { 86 | callback = format; 87 | format = this.format; 88 | } 89 | 90 | fs.writeFile(path, this.stringify(format), callback); 91 | }; 92 | 93 | // 94 | // ### function saveSync (value, callback) 95 | // Saves the current configuration object to disk at `this.file` 96 | // using the format specified by `this.format` synchronously. 97 | // 98 | File.prototype.saveSync = function () { 99 | fs.writeFileSync(this.file, this.stringify()); 100 | return this.store; 101 | }; 102 | 103 | // 104 | // ### function load (callback) 105 | // #### @callback {function} Continuation to respond to when complete. 106 | // Responds with an Object representing all keys associated in this instance. 107 | // 108 | File.prototype.load = function (callback) { 109 | var self = this; 110 | 111 | exists(self.file, function (exists) { 112 | if (!exists) { 113 | return callback(null, {}); 114 | } 115 | 116 | // 117 | // Else, the path exists, read it from disk 118 | // 119 | fs.readFile(self.file, function (err, data) { 120 | if (err) { 121 | return callback(err); 122 | } 123 | 124 | try { 125 | // Deals with string that include BOM 126 | var stringData = data.toString(); 127 | if (stringData.charAt(0) === '\uFEFF') { 128 | stringData = stringData.substr(1); 129 | } 130 | 131 | self.store = self.parse(stringData); 132 | } 133 | catch (ex) { 134 | return callback(new Error("Error parsing your configuration file: [" + self.file + ']: ' + ex.message)); 135 | } 136 | 137 | callback(null, self.store); 138 | }); 139 | }); 140 | }; 141 | 142 | // 143 | // ### function loadSync (callback) 144 | // Attempts to load the data stored in `this.file` synchronously 145 | // and responds appropriately. 146 | // 147 | File.prototype.loadSync = function () { 148 | if (!existsSync(this.file)) { 149 | this.store = {}; 150 | return this.store; 151 | } 152 | 153 | // 154 | // Else, the path exists, read it from disk 155 | // 156 | try { 157 | // Deals with file that include BOM 158 | var fileData = fs.readFileSync(this.file, 'utf8'); 159 | if (fileData.charAt(0) === '\uFEFF') { 160 | fileData = fileData.substr(1); 161 | } 162 | 163 | this.store = this.parse(fileData); 164 | } 165 | catch (ex) { 166 | throw new Error("Error parsing your configuration file: [" + this.file + ']: ' + ex.message); 167 | } 168 | 169 | return this.store; 170 | }; 171 | 172 | // 173 | // ### function stringify () 174 | // Returns an encrypted version of the contents IIF 175 | // `this.secure` is enabled 176 | // 177 | File.prototype.stringify = function (format) { 178 | var data = this.store; 179 | if (!format) { 180 | format = this.format 181 | } 182 | 183 | if (this.secure) { 184 | var self = this; 185 | data = Object.keys(data).reduce(function (acc, key) { 186 | var value = format.stringify(data[key]); 187 | var iv = crypto.randomBytes(16); 188 | var cipher = crypto.createCipheriv(self.secure.alg, self.secure.secret, iv); 189 | var ciphertext = cipher.update(value, 'utf8', 'hex'); 190 | ciphertext += cipher.final('hex'); 191 | acc[key] = { alg: self.secure.alg, value: ciphertext, iv: iv.toString('hex') }; 192 | return acc; 193 | }, {}); 194 | } 195 | 196 | var stringified = format.stringify(data, null, this.spacing); 197 | var needsEOL = this.eol && stringified.slice(-1) !== os.EOL; 198 | 199 | return stringified + (needsEOL ? os.EOL : ''); 200 | }; 201 | 202 | // 203 | // ### function parse (contents) 204 | // Returns a decrypted version of the contents IFF 205 | // `this.secure` is enabled. 206 | // 207 | File.prototype.parse = function (contents) { 208 | var parsed = this.format.parse(contents); 209 | 210 | if (this.secure) { 211 | var self = this; 212 | parsed = Object.keys(parsed).reduce(function (acc, key) { 213 | var value = parsed[key]; 214 | 215 | if (!value.iv) { 216 | throw new Error('Your encrypted file is outdated (encrypted without iv). Please re-encrypt your file using a pre-v1 release of nconf, v0.10 or above.'); 217 | } 218 | let decipher = crypto.createDecipheriv(value.alg, self.secure.secret, Buffer.from(value.iv, 'hex')); 219 | 220 | var plaintext = decipher.update(value.value, 'hex', 'utf8'); 221 | plaintext += decipher.final('utf8'); 222 | acc[key] = self.format.parse(plaintext); 223 | return acc; 224 | }, {}); 225 | } 226 | 227 | return parsed; 228 | 229 | }; 230 | 231 | 232 | // 233 | // ### function search (base) 234 | // #### @base {string} Base directory (or file) to begin searching for the target file. 235 | // Attempts to find `this.file` by iteratively searching up the 236 | // directory structure 237 | // 238 | File.prototype.search = function (base) { 239 | var looking = true, 240 | fullpath, 241 | previous, 242 | stats; 243 | 244 | base = base || process.cwd(); 245 | 246 | if (this.file[0] === '/') { 247 | // 248 | // If filename for this instance is a fully qualified path 249 | // (i.e. it starts with a `'/'`) then check if it exists 250 | // 251 | try { 252 | stats = fs.statSync(fs.realpathSync(this.file)); 253 | if (stats.isFile()) { 254 | fullpath = this.file; 255 | looking = false; 256 | } 257 | } 258 | catch (ex) { 259 | // 260 | // Ignore errors 261 | // 262 | } 263 | } 264 | 265 | if (looking && base) { 266 | // 267 | // Attempt to stat the realpath located at `base` 268 | // if the directory does not exist then return false. 269 | // 270 | try { 271 | var stat = fs.statSync(fs.realpathSync(base)); 272 | looking = stat.isDirectory(); 273 | } 274 | catch (ex) { 275 | return false; 276 | } 277 | } 278 | 279 | while (looking) { 280 | // 281 | // Iteratively look up the directory structure from `base` 282 | // 283 | try { 284 | stats = fs.statSync(fs.realpathSync(fullpath = path.join(base, this.file))); 285 | looking = stats.isDirectory(); 286 | } 287 | catch (ex) { 288 | previous = base; 289 | base = path.dirname(base); 290 | 291 | if (previous === base) { 292 | // 293 | // If we've reached the top of the directory structure then simply use 294 | // the default file path. 295 | // 296 | try { 297 | stats = fs.statSync(fs.realpathSync(fullpath = path.join(this.dir, this.file))); 298 | if (stats.isDirectory()) { 299 | fullpath = undefined; 300 | } 301 | } 302 | catch (ex) { 303 | // 304 | // Ignore errors 305 | // 306 | } 307 | 308 | looking = false; 309 | } 310 | } 311 | } 312 | 313 | // 314 | // Set the file for this instance to the fullpath 315 | // that we have found during the search. In the event that 316 | // the search was unsuccessful use the original value for `this.file`. 317 | // 318 | this.file = fullpath || this.file; 319 | 320 | return fullpath; 321 | }; 322 | -------------------------------------------------------------------------------- /lib/nconf/stores/literal.js: -------------------------------------------------------------------------------- 1 | /* 2 | * literal.js: Simple literal Object store for nconf. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var util = require('util'), 9 | Memory = require('./memory').Memory 10 | 11 | var Literal = exports.Literal = function Literal (options) { 12 | Memory.call(this, options); 13 | 14 | options = options || {} 15 | this.type = 'literal'; 16 | this.readOnly = true; 17 | this.store = options.store || options; 18 | }; 19 | 20 | // Inherit from Memory store. 21 | util.inherits(Literal, Memory); 22 | 23 | // 24 | // ### function loadSync (callback) 25 | // Returns the data stored in `this.store` synchronously. 26 | // 27 | Literal.prototype.loadSync = function () { 28 | return this.store; 29 | }; -------------------------------------------------------------------------------- /lib/nconf/stores/memory.js: -------------------------------------------------------------------------------- 1 | /* 2 | * memory.js: Simple memory storage engine for nconf configuration(s) 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | const common = require('../common'); 9 | 10 | const DEFAULT_ACCESS_SEPARATOR = ':'; 11 | const DEFAULT_INPUT_SEPARATOR = '__'; 12 | 13 | // Helper function for preparing a string for regex matching 14 | function escapeRegExp(string) { 15 | return typeof string === 'string' && string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string 16 | } 17 | 18 | // 19 | // ### function Memory (options) 20 | // #### @options {Object} Options for this instance 21 | // Constructor function for the Memory nconf store which maintains 22 | // a nested json structure based on key delimiters `:`. 23 | // 24 | // e.g. `my:nested:key` ==> `{ my: { nested: { key: } } }` 25 | // 26 | var Memory = exports.Memory = function (options) { 27 | options = options || {}; 28 | this.type = 'memory'; 29 | this.store = {}; 30 | this.mtimes = {}; 31 | this.readOnly = false; 32 | this.loadFrom = options.loadFrom || null; 33 | this.accessSeparator = options.accessSeparator || DEFAULT_ACCESS_SEPARATOR; 34 | this.inputSeparator = options.inputSeparator || DEFAULT_INPUT_SEPARATOR; 35 | this.parseValues = options.parseValues || false; 36 | this.disableDefaultAccessSeparator = options.disableDefaultAccessSeparator || false; 37 | 38 | if (typeof(options) === 'string' || options instanceof RegExp) { 39 | this.inputSeparator = options; 40 | } 41 | 42 | if (this.loadFrom) { 43 | this.store = common.loadFilesSync(this.loadFrom); 44 | } 45 | }; 46 | 47 | Memory.prototype._normalizeKey = function (key) { 48 | let inputSeparator = this.inputSeparator; 49 | if (inputSeparator instanceof RegExp) { 50 | inputSeparator = inputSeparator.source; 51 | } else { 52 | inputSeparator = escapeRegExp(inputSeparator); 53 | } 54 | let separatorRegexStr = `${escapeRegExp(this.accessSeparator)}|${inputSeparator}`; 55 | 56 | if (!this.disableDefaultAccessSeparator) { 57 | separatorRegexStr += `|${DEFAULT_ACCESS_SEPARATOR}`; 58 | } 59 | 60 | const separatorRegEx = new RegExp(separatorRegexStr, 'g'); 61 | return key && key.replace(separatorRegEx, this.accessSeparator); 62 | } 63 | 64 | // 65 | // ### function get (key) 66 | // #### @key {string} Key to retrieve for this instance. 67 | // Retrieves the value for the specified key (if any). 68 | // 69 | Memory.prototype.get = function (key) { 70 | var target = this.store, 71 | path = common.path(this._normalizeKey(key), this.accessSeparator); 72 | 73 | // 74 | // Scope into the object to get the appropriate nested context 75 | // 76 | while (path.length > 0) { 77 | key = path.shift(); 78 | if (target && typeof target !== 'string' && Object.hasOwnProperty.call(target, key)) { 79 | target = target[key]; 80 | continue; 81 | } 82 | return undefined; 83 | } 84 | 85 | return target; 86 | }; 87 | 88 | // 89 | // ### function set (key, value) 90 | // #### @key {string} Key to set in this instance 91 | // #### @value {literal|Object} Value for the specified key 92 | // Sets the `value` for the specified `key` in this instance. 93 | // 94 | Memory.prototype.set = function (key, value) { 95 | if (this.readOnly) { 96 | return false; 97 | } 98 | 99 | var target = this.store, 100 | path = common.path(this._normalizeKey(key), this.accessSeparator); 101 | 102 | if (path.length === 0) { 103 | // 104 | // Root must be an object 105 | // 106 | if (!value || typeof value !== 'object') { 107 | return false; 108 | } 109 | else { 110 | this.reset(); 111 | this.store = value; 112 | return true; 113 | } 114 | } 115 | 116 | // 117 | // Update the `mtime` (modified time) of the key 118 | // 119 | this.mtimes[key] = Date.now(); 120 | 121 | // 122 | // Scope into the object to get the appropriate nested context 123 | // 124 | while (path.length > 1) { 125 | key = path.shift(); 126 | if (!target[key] || typeof target[key] !== 'object') { 127 | target[key] = {}; 128 | } 129 | 130 | target = target[key]; 131 | } 132 | 133 | // Set the specified value in the nested JSON structure 134 | key = path.shift(); 135 | if (this.parseValues) { 136 | value = common.parseValues.call(common, value); 137 | } 138 | target[key] = value; 139 | return true; 140 | }; 141 | 142 | // 143 | // ### function clear (key) 144 | // #### @key {string} Key to remove from this instance 145 | // Removes the value for the specified `key` from this instance. 146 | // 147 | Memory.prototype.clear = function (key) { 148 | if (this.readOnly) { 149 | return false; 150 | } 151 | 152 | var target = this.store, 153 | value = target, 154 | path = common.path(key, this.accessSeparator); 155 | 156 | // 157 | // Remove the key from the set of `mtimes` (modified times) 158 | // 159 | delete this.mtimes[key]; 160 | 161 | // 162 | // Scope into the object to get the appropriate nested context 163 | // 164 | for (var i = 0; i < path.length - 1; i++) { 165 | key = path[i]; 166 | value = target[key]; 167 | if (typeof value !== 'function' && typeof value !== 'object') { 168 | return false; 169 | } 170 | target = value; 171 | } 172 | 173 | // Delete the key from the nested JSON structure 174 | key = path[i]; 175 | delete target[key]; 176 | return true; 177 | }; 178 | 179 | // 180 | // ### function merge (key, value) 181 | // #### @key {string} Key to merge the value into 182 | // #### @value {literal|Object} Value to merge into the key 183 | // Merges the properties in `value` into the existing object value 184 | // at `key`. If the existing value `key` is not an Object, it will be 185 | // completely overwritten. 186 | // 187 | Memory.prototype.merge = function (key, value) { 188 | if (this.readOnly) { 189 | return false; 190 | } 191 | 192 | // 193 | // If the key is not an `Object` or is an `Array`, 194 | // then simply set it. Merging is for Objects. 195 | // 196 | if (typeof value !== 'object' || Array.isArray(value) || value === null) { 197 | return this.set(key, value); 198 | } 199 | 200 | var self = this, 201 | target = this.store, 202 | path = common.path(key, this.accessSeparator), 203 | fullKey = key; 204 | 205 | // 206 | // Update the `mtime` (modified time) of the key 207 | // 208 | this.mtimes[key] = Date.now(); 209 | 210 | // 211 | // Scope into the object to get the appropriate nested context 212 | // 213 | while (path.length > 1) { 214 | key = path.shift(); 215 | if (!target[key]) { 216 | target[key] = {}; 217 | } 218 | 219 | target = target[key]; 220 | } 221 | 222 | // Set the specified value in the nested JSON structure 223 | key = path.shift(); 224 | 225 | // 226 | // If the current value at the key target is not an `Object`, 227 | // or is an `Array` then simply override it because the new value 228 | // is an Object. 229 | // 230 | if (typeof target[key] !== 'object' || Array.isArray(target[key])) { 231 | target[key] = value; 232 | return true; 233 | } 234 | 235 | return Object.keys(value).every(function (nested) { 236 | return self.merge(common.keyed(self.accessSeparator, fullKey, nested), value[nested]); 237 | }); 238 | }; 239 | 240 | // 241 | // ### function reset (callback) 242 | // Clears all keys associated with this instance. 243 | // 244 | Memory.prototype.reset = function () { 245 | if (this.readOnly) { 246 | return false; 247 | } 248 | 249 | this.mtimes = {}; 250 | this.store = {}; 251 | return true; 252 | }; 253 | 254 | // 255 | // ### function loadSync 256 | // Returns the store managed by this instance 257 | // 258 | Memory.prototype.loadSync = function () { 259 | return this.store || {}; 260 | }; 261 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nconf", 3 | "description": "Hierarchical node.js configuration with files, environment variables, command-line arguments, and atomic object merging.", 4 | "version": "1.0.0-beta.2", 5 | "author": "Charlie Robbins ", 6 | "contributors": [ 7 | "Matt Hamann ", 8 | "Maciej Małecki ", 9 | "Jarrett Cruger ", 10 | "Adrien Becchis" 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "http://github.com/flatiron/nconf.git" 15 | }, 16 | "keywords": [ 17 | "configuration", 18 | "key value store", 19 | "plugabble" 20 | ], 21 | "dependencies": { 22 | "ini": "^2.0.0", 23 | "yargs": "^17.0.0" 24 | }, 25 | "devDependencies": { 26 | "@release-it/conventional-changelog": "^7.0.2", 27 | "async": "^3.0.0", 28 | "coveralls": "^3.1.0", 29 | "eslint": "^8.0.0", 30 | "istanbul": "^0.4.1", 31 | "jest": "^27.0.0", 32 | "nconf-yaml": "^1.0.2", 33 | "release-it": "^16.2.1" 34 | }, 35 | "main": "./lib/nconf", 36 | "scripts": { 37 | "test": "jest --verbose", 38 | "cover": "jest --coverage", 39 | "coveralls": "cat coverage/lcov.info | coveralls", 40 | "lint": "eslint .", 41 | "release": "release-it" 42 | }, 43 | "files": [ 44 | "lib" 45 | ], 46 | "engines": { 47 | "node": ">= 0.4.0" 48 | }, 49 | "license": "MIT" 50 | } 51 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["config:base"], 3 | 4 | "platform": "github", 5 | "autodiscover": false, 6 | "requireConfig": true, 7 | 8 | "ignoreNpmrcFile": true, 9 | "rangeStrategy": "replace", 10 | 11 | "packageRules": [ 12 | { 13 | "packagePatterns": [ 14 | "*" 15 | ], 16 | "minor": { 17 | "groupName": "all non-major dependencies", 18 | "groupSlug": "all-minor-patch" 19 | } 20 | } 21 | ], 22 | 23 | "commitMessagePrefix": "[dist]" 24 | } 25 | -------------------------------------------------------------------------------- /test/common.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * common.js: Tests for common utility function in nconf. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var fs = require('fs'); 9 | var path = require('path'); 10 | var helpers = require('./helpers'); 11 | var nconf = require('../lib/nconf'); 12 | 13 | var mergeDir = path.join(__dirname, 'fixtures', 'merge'); 14 | var files = fs.readdirSync(mergeDir).map(function (f) { return path.join(mergeDir, f) }); 15 | 16 | describe('nconf/common', () => { 17 | describe('Using nconf.common module', () => { 18 | it('the loadFiles() method should merge the files correctly', done => { 19 | nconf.loadFiles(files, (err, res) => { 20 | helpers.assertMerged(err, res); 21 | done(); 22 | }); 23 | }); 24 | it("the loadFilesSync() method should merge the files correctly", () => { 25 | helpers.assertMerged(null, nconf.loadFilesSync(files)); 26 | }); 27 | }); 28 | }); -------------------------------------------------------------------------------- /test/complete.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * complete-test.js: Complete test for multiple stores. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var fs = require('fs'); 9 | var nconf = require('../lib/nconf'); 10 | var data = require('./fixtures/data').data; 11 | var helpers = require('./helpers'); 12 | const _ = require('lodash'); 13 | 14 | var completeTest = helpers.fixture('complete-test.json'); 15 | var complete = helpers.fixture('complete.json'); 16 | 17 | // prime the process.env 18 | process.env['NCONF_foo'] = 'bar'; 19 | process.env.FOO = 'bar'; 20 | process.env.BAR = 'zalgo'; 21 | process.env.NODE_ENV = 'debug'; 22 | process.env.FOOBAR = 'should not load'; 23 | process.env.json_array = JSON.stringify(['foo', 'bar', 'baz']); 24 | process.env.json_obj = JSON.stringify({ foo: 'bar', baz: 'foo' }); 25 | process.env.NESTED__VALUE = 'nested'; 26 | process.env.NESTED___VALUE_EXTRA_LODASH = '_nested_'; 27 | 28 | // Ensure tests only validate a controlled set of env vars 29 | let process_env = _.pick(process.env, ['NCONF_foo', 'FOO', 'BAR', 'NODE_ENV', 'FOOBAR', 'json_array', 'json_obj', 'NESTED__VALUE', 'NESTED___VALUE_EXTRA_LODASH']); 30 | 31 | describe('nconf/multiple-stores', () => { 32 | afterAll(() => { 33 | try { 34 | // Cleanup 35 | fs.unlinkSync(completeTest); 36 | } catch (err) { 37 | // No-op - just ensures the test file is cleaned up 38 | console.log(err); 39 | } 40 | }) 41 | describe("When using the nconf with multiple providers", () => { 42 | beforeAll(done => { 43 | helpers.cp(complete, completeTest, function () { 44 | nconf.env({ 45 | // separator: '__', 46 | match: /^NCONF_/, 47 | whitelist: ['NODE_ENV', 'FOO', 'BAR'] 48 | }); 49 | nconf.file({ file: completeTest }); 50 | nconf.use('argv', { type: 'literal', store: data }); 51 | done(); 52 | }); 53 | }); 54 | it("should have the correct `stores`", () => { 55 | expect(typeof nconf.stores.env).toBe('object'); 56 | expect(typeof nconf.stores.argv).toBe('object'); 57 | expect(typeof nconf.stores.file).toBe('object'); 58 | }); 59 | it("env vars, are present", () => { 60 | ['NODE_ENV', 'FOO', 'BAR', 'NCONF_foo'].forEach(function (key) { 61 | expect(nconf.get(key)).toEqual(process.env[key]); 62 | }); 63 | }); 64 | it("json vars are present", done => { 65 | fs.readFile(complete, 'utf8', (err, data) => { 66 | expect(err).toBe(null); 67 | data = JSON.parse(data); 68 | Object.keys(data).forEach(function (key) { 69 | expect(nconf.get(key)).toEqual(data[key]); 70 | }); 71 | done(); 72 | }) 73 | }); 74 | it("literal vars are present", () => { 75 | Object.keys(data).forEach(function (key) { 76 | expect(nconf.get(key)).toEqual(data[key]); 77 | }); 78 | }); 79 | afterAll(() => { 80 | nconf.remove('file'); 81 | nconf.remove('memory'); 82 | nconf.remove('argv'); 83 | nconf.remove('env'); 84 | }); 85 | describe('saving', () => { 86 | afterEach(() => { 87 | // remove the file so that we can test saving it async 88 | fs.unlinkSync(completeTest); 89 | }); 90 | it("and saving *synchronously* correct return value, the file, saved correctly", done => { 91 | nconf.set('weebls', 'stuff'); 92 | var topic = nconf.save(); 93 | Object.keys(topic).forEach(function (key) { 94 | expect(topic[key]).toEqual(nconf.get(key)); 95 | }); 96 | fs.readFile(completeTest, 'utf8', function (err, data) { 97 | expect(err).toBe(null); 98 | data = JSON.parse(data); 99 | Object.keys(data).forEach(function (key) { 100 | expect(data[key]).toEqual(nconf.get(key)); 101 | }); 102 | expect(nconf.get('weebls')).toEqual('stuff'); 103 | done(); 104 | }); 105 | }); 106 | it("and saving *asynchronously* correct return value, the file, saved correctly", done => { 107 | nconf.set('weebls', 'crap'); 108 | nconf.save((err, data) => { 109 | Object.keys(data).forEach(function (key) { 110 | expect(data[key]).toEqual(nconf.get(key)); 111 | }); 112 | fs.readFile(completeTest, 'utf8', function (err, data) { 113 | expect(err).toBe(null); 114 | data = JSON.parse(data); 115 | Object.keys(data).forEach(function (key) { 116 | expect(data[key]).toEqual(nconf.get(key)); 117 | }); 118 | expect(nconf.get('weebls')).toEqual('crap'); 119 | done(); 120 | }); 121 | }); 122 | }); 123 | }); 124 | }); 125 | describe("When using the nconf env with custom options", () => { 126 | 127 | describe("When using env with lowerCase:true", () => { 128 | // Threw this in it's own batch to make sure it's run separately from the sync check 129 | beforeAll(done => { 130 | helpers.cp(complete, completeTest, () => { 131 | nconf.env({ lowerCase: true }); 132 | done(); 133 | }) 134 | }); 135 | afterAll(() => nconf.remove('env')) 136 | it("env vars keys also available as lower case", () => { 137 | Object.keys(process_env).forEach(function (key) { 138 | expect(nconf.get(key.toLowerCase())).toEqual(process.env[key]); 139 | }); 140 | }); 141 | }); 142 | 143 | 144 | describe("When using env with parseValues:true", () => { 145 | // Threw this in it's own batch to make sure it's run separately from the sync check 146 | beforeAll(done => { 147 | helpers.cp(complete, completeTest, () => { 148 | nconf.env({ parseValues: true }); 149 | done(); 150 | }) 151 | }); 152 | afterAll(() => nconf.remove('env')) 153 | it("JSON keys properly parsed", () => { 154 | Object.keys(process_env).forEach(function (key) { 155 | var val = process.env[key]; 156 | 157 | try { 158 | val = JSON.parse(val); 159 | } catch (err) { 160 | } 161 | 162 | expect(nconf.get(key)).toEqual(val); 163 | }); 164 | }); 165 | 166 | }); 167 | 168 | describe("When using env with transform:fn", () => { 169 | // Threw this in it's own batch to make sure it's run separately from the sync check 170 | beforeAll(done => { 171 | function testTransform(obj) { 172 | if (obj.key === 'FOO') { 173 | obj.key = 'FOOD'; 174 | obj.value = 'BARFOO'; 175 | } 176 | 177 | return obj; 178 | } 179 | 180 | helpers.cp(complete, completeTest, () => { 181 | nconf.env({ transform: testTransform }); 182 | done(); 183 | }) 184 | }); 185 | it("env vars port key/value properly transformed", () => { 186 | expect(nconf.get('FOOD')).toEqual('BARFOO'); 187 | }); 188 | 189 | afterAll(() => nconf.remove('env')) 190 | }); 191 | describe("When using env with transform:fn that drops an entry", () => { 192 | // Threw this in it's own batch to make sure it's run separately from the sync check 193 | beforeAll(done => { 194 | function testTransform(obj) { 195 | if (obj.key === 'FOO') { 196 | return false; 197 | } 198 | 199 | return obj; 200 | } 201 | 202 | helpers.cp(complete, completeTest, () => { 203 | nconf.env({ transform: testTransform }); 204 | done(); 205 | }) 206 | }); 207 | it("env vars port key/value properly transformed", () => { 208 | expect(nconf.get('FOO')).toBe(undefined); 209 | }); 210 | 211 | afterAll(() => nconf.remove('env')) 212 | }); 213 | describe("When using env with transform:fn that return an undefined value", () => { 214 | // Threw this in it's own batch to make sure it's run separately from the sync check 215 | beforeAll(done => { 216 | function testTransform(obj) { 217 | if (obj.key === 'FOO') { 218 | return { key: 'FOO', value: undefined }; 219 | } 220 | return obj; 221 | } 222 | 223 | helpers.cp(complete, completeTest, () => { 224 | nconf.env({ transform: testTransform }); 225 | done(); 226 | }) 227 | }); 228 | it("env vars port key/value properly transformed", () => { 229 | expect(nconf.get('FOO')).toBe(undefined); 230 | }); 231 | 232 | afterAll(() => nconf.remove('env')) 233 | }); 234 | describe("When using env with bad transform:fn", () => { 235 | // Threw this in it's own batch to make sure it's run separately from the sync check 236 | it(" port key/value throws transformation error", done => { 237 | 238 | function testTransform(obj) { 239 | return { foo: 'bar' }; 240 | } 241 | 242 | helpers.cp(complete, completeTest, () => { 243 | try { 244 | nconf.env({ transform: testTransform }); 245 | } catch (err) { 246 | expect(err.name).toEqual('RuntimeError') 247 | done(); 248 | } 249 | }) 250 | }); 251 | 252 | afterAll(() => nconf.remove('env')) 253 | }); 254 | describe("When using env with a separator", () => { 255 | // Threw this in it's own batch to make sure it's run separately from the sync check 256 | beforeAll(done => { 257 | helpers.cp(complete, completeTest, () => { 258 | nconf.env({ inputSeparator: /__+/ }); 259 | done(); 260 | }) 261 | }); 262 | it("can access to nested values", () => { 263 | expect(nconf.get('NESTED')).toEqual({ VALUE: 'nested', VALUE_EXTRA_LODASH: '_nested_' }); 264 | }); 265 | 266 | afterAll(() => nconf.remove('env')) 267 | }); 268 | 269 | }); 270 | }); 271 | -------------------------------------------------------------------------------- /test/fixtures/bom.json: -------------------------------------------------------------------------------- 1 | { 2 | "I've seen things": { 3 | "like": [ 4 | "carrots", 5 | "handbags", 6 | "cheese", 7 | "toilets", 8 | "russians", 9 | "planets", 10 | "hampsters", 11 | "weddings", 12 | "poets", 13 | "stalin", 14 | "kuala lumpur" 15 | ] 16 | }, 17 | "host": "weebls-stuff.com", 18 | "port": 78304 19 | } 20 | -------------------------------------------------------------------------------- /test/fixtures/complete.json: -------------------------------------------------------------------------------- 1 | { 2 | "I've seen things": { 3 | "like": [ 4 | "carrots", 5 | "handbags", 6 | "cheese", 7 | "toilets", 8 | "russians", 9 | "planets", 10 | "hampsters", 11 | "weddings", 12 | "poets", 13 | "stalin", 14 | "kuala lumpur" 15 | ] 16 | }, 17 | "host": "weebls-stuff.com", 18 | "port": 78304 19 | } 20 | -------------------------------------------------------------------------------- /test/fixtures/data.js: -------------------------------------------------------------------------------- 1 | /* 2 | * data.js: Simple data fixture for configuration test. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | exports.data = { 9 | isNull: null, 10 | literal: 'bazz', 11 | arr: ['one', 2, true, { value: 'foo' }], 12 | obj: { 13 | host: 'localhost', 14 | port: 5984, 15 | array: ['one', 2, true, { foo: 'bar' }], 16 | auth: { 17 | username: 'admin', 18 | password: 'password' 19 | } 20 | } 21 | }; 22 | 23 | exports.merge = { 24 | prop1: 1, 25 | prop2: [1, 2, 3], 26 | prop3: { 27 | foo: 'bar', 28 | bar: 'foo' 29 | } 30 | }; -------------------------------------------------------------------------------- /test/fixtures/hierarchy/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "My generic title", 3 | "color": "red", 4 | "movie": "Kill Bill" 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/hierarchy/hierarchical.json: -------------------------------------------------------------------------------- 1 | { 2 | "test": "empty" 3 | } -------------------------------------------------------------------------------- /test/fixtures/hierarchy/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "My specific title", 3 | "color": "green" 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/malformed.json: -------------------------------------------------------------------------------- 1 | { 2 | "literal": "bazz", 3 | } -------------------------------------------------------------------------------- /test/fixtures/merge/file1.json: -------------------------------------------------------------------------------- 1 | { 2 | "apples": true, 3 | "bananas": true, 4 | "foo": { 5 | "bar": "boo" 6 | }, 7 | "candy": { 8 | "something": "file1", 9 | "something1": true, 10 | "something2": true, 11 | "something5": { 12 | "first": 1, 13 | "second": 2 14 | } 15 | }, 16 | "unicorn": { 17 | "exists": true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/fixtures/merge/file2.json: -------------------------------------------------------------------------------- 1 | { 2 | "candy": { 3 | "something": "file2", 4 | "something3": true, 5 | "something4": true 6 | }, 7 | "dates": true, 8 | "elderberries": true, 9 | "unicorn": null 10 | } 11 | -------------------------------------------------------------------------------- /test/fixtures/merge/file3.json: -------------------------------------------------------------------------------- 1 | { 2 | "candy": { 3 | "something": "much better something for you", 4 | "something18": "completely unique", 5 | "something5": { 6 | "second": 99 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/fixtures/no-bom.json: -------------------------------------------------------------------------------- 1 | { 2 | "I've seen things": { 3 | "like": [ 4 | "carrots", 5 | "handbags", 6 | "cheese", 7 | "toilets", 8 | "russians", 9 | "planets", 10 | "hampsters", 11 | "weddings", 12 | "poets", 13 | "stalin", 14 | "kuala lumpur" 15 | ] 16 | }, 17 | "host": "weebls-stuff.com", 18 | "port": 78304 19 | } 20 | -------------------------------------------------------------------------------- /test/fixtures/scripts/nconf-argv.js: -------------------------------------------------------------------------------- 1 | /* 2 | * default-argv.js: Test fixture for using yargs defaults with nconf. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var nconf = require('../../../lib/nconf').argv().env(); 9 | 10 | process.stdout.write(nconf.get('something')); 11 | -------------------------------------------------------------------------------- /test/fixtures/scripts/nconf-change-argv.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nconf-change-argv.js: Test fixture for changing argv on the fly 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var nconf = require('../../../lib/nconf').argv(); 9 | 10 | // 11 | // Remove 'badValue', 'evenWorse' and 'OHNOEZ' 12 | // 13 | process.argv.splice(3, 3); 14 | nconf.stores['argv'].loadArgv(); 15 | process.stdout.write(nconf.get('something')); 16 | 17 | -------------------------------------------------------------------------------- /test/fixtures/scripts/nconf-env.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nconf-env.js: Test fixture for using process.env defaults with nconf. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var nconf = require('../../../lib/nconf').env(); 9 | 10 | process.stdout.write(nconf.get('SOMETHING')); -------------------------------------------------------------------------------- /test/fixtures/scripts/nconf-hierarchical-defaults-merge.js: -------------------------------------------------------------------------------- 1 | var path = require('path'), 2 | nconf = require('../../../lib/nconf'); 3 | 4 | nconf 5 | .file('localOverrides', path.join(__dirname, '..', 'merge', 'file3.json')) 6 | .defaults({ 7 | "candy": { 8 | "something": "a nice default", 9 | "something1": true, 10 | "something2": true, 11 | "something5": { 12 | "first": 1, 13 | "second": 2 14 | } 15 | } 16 | }); 17 | 18 | process.stdout.write(JSON.stringify({ 19 | candy: nconf.get('candy') 20 | })); 21 | -------------------------------------------------------------------------------- /test/fixtures/scripts/nconf-hierarchical-file-argv.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nconf-hierarchical-file-argv.js: Test fixture for using yargs defaults and a file store with nconf. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * (C) 2011, Sander Tolsma 6 | * 7 | */ 8 | 9 | var path = require('path'), 10 | nconf = require('../../../lib/nconf'); 11 | 12 | nconf.argv(); 13 | nconf.add('file', { 14 | file: path.join(__dirname, '../hierarchy/hierarchical.json') 15 | }); 16 | 17 | process.stdout.write(nconf.get('something') || 'undefined'); 18 | -------------------------------------------------------------------------------- /test/fixtures/scripts/nconf-hierarchical-load-merge-with-separator.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nconf-hierarchical-load-merge.js: Test fixture for loading and merging nested objects across stores. 3 | * 4 | * (C) 2012, Charlie Robbins and the Contributors. 5 | * (C) 2012, Michael Hart 6 | * 7 | */ 8 | 9 | var path = require('path'), 10 | nconf = require('../../../lib/nconf'); 11 | 12 | nconf.argv({inputSeparator: '--'}) 13 | .env('__') 14 | .file(path.join(__dirname, '..', 'merge', 'file1.json')); 15 | 16 | process.stdout.write(JSON.stringify({ 17 | apples: nconf.get('apples'), 18 | candy: nconf.get('candy') 19 | })); 20 | -------------------------------------------------------------------------------- /test/fixtures/scripts/nconf-hierarchical-load-merge.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nconf-hierarchical-load-merge.js: Test fixture for loading and merging nested objects across stores. 3 | * 4 | * (C) 2012, Charlie Robbins and the Contributors. 5 | * (C) 2012, Michael Hart 6 | * 7 | */ 8 | 9 | var path = require('path'), 10 | nconf = require('../../../lib/nconf'); 11 | 12 | nconf.argv() 13 | .file(path.join(__dirname, '..', 'merge', 'file1.json')); 14 | 15 | process.stdout.write(JSON.stringify({ 16 | apples: nconf.get('apples'), 17 | candy: nconf.get('candy') 18 | })); 19 | -------------------------------------------------------------------------------- /test/fixtures/scripts/nconf-hierarchical-load-save.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nconf-hierarchical-load-save.js: Test fixture for using yargs, envvars and a file store with nconf. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var path = require('path'), 9 | nconf = require('../../../lib/nconf'); 10 | 11 | // 12 | // Setup nconf to use (in-order): 13 | // 1. Command-line arguments 14 | // 2. Environment variables 15 | // 3. A file located at 'path/to/config.json' 16 | // 17 | nconf.argv() 18 | .env() 19 | .file({ file: path.join(__dirname, '..', 'load-save.json') }); 20 | 21 | // 22 | // Set a few variables on `nconf`. 23 | // 24 | nconf.set('database:host', '127.0.0.1'); 25 | nconf.set('database:port', 5984); 26 | 27 | process.stdout.write(nconf.get('foo')); 28 | // 29 | // Save the configuration object to disk 30 | // 31 | nconf.save(); 32 | -------------------------------------------------------------------------------- /test/fixtures/scripts/nconf-nested-env.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nconf-nested-env.js: Test fixture for env with nested keys. 3 | * 4 | * (C) 2012, Charlie Robbins and the Contributors. 5 | * (C) 2012, Michael Hart 6 | * 7 | */ 8 | 9 | var nconf = require('../../../lib/nconf').env('_'); 10 | 11 | process.stdout.write(nconf.get('SOME:THING')); 12 | -------------------------------------------------------------------------------- /test/fixtures/scripts/provider-argv.js: -------------------------------------------------------------------------------- 1 | /* 2 | * provider-argv.js: Test fixture for using yargs defaults with nconf. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var nconf = require('../../../lib/nconf'); 9 | 10 | var provider = new (nconf.Provider)().argv(); 11 | 12 | process.stdout.write(provider.get('something')); 13 | -------------------------------------------------------------------------------- /test/fixtures/scripts/provider-env.js: -------------------------------------------------------------------------------- 1 | /* 2 | * provider-argv.js: Test fixture for using process.env defaults with nconf. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var nconf = require('../../../lib/nconf'); 9 | 10 | var provider = new (nconf.Provider)().env(); 11 | 12 | process.stdout.write(provider.get('SOMETHING')); -------------------------------------------------------------------------------- /test/fixtures/secure-iv.json: -------------------------------------------------------------------------------- 1 | { 2 | "isNull": { 3 | "alg": "aes-256-ctr", 4 | "value": "16f39325", 5 | "iv": "49e7803a2a5ef98c7a51a8902b76dd10" 6 | }, 7 | "literal": { 8 | "alg": "aes-256-ctr", 9 | "value": "647965c22605", 10 | "iv": "b654e01aed262f37d0acf200be193985" 11 | }, 12 | "arr": { 13 | "alg": "aes-256-ctr", 14 | "value": "70c6d3b2ec3820e4d4fa3d7834fcf7fd51190a9e580944583ca7b30f42d230b7f271e72287f6c2ab4624685c779936aa2ed19b90", 15 | "iv": "b5263665331158c2165fe623a895870f" 16 | }, 17 | "obj": { 18 | "alg": "aes-256-ctr", 19 | "value": "521f1995698896d2fe7d76d16bec9e86b9b5b25bcfae3a4f3c52ad6f0425c3fd059793b9dec7927c616f4c4c57f2b9ee833f8c865d9aa42bbb37203763703755a0c79afd13f8c0e96e9ac47f490a12105583e31ff628104e6b216265b849cdf6642eb8a4b5bd6f532339a5f5737bd6a7ae6e7bb22148cbcfa549802336cb1322fb701151b25b5863c5c6d5047875aed9806350ae0e292c259e82d5a561eb672402d20230a0c3107ff2b1e6259ccb1bbbe7a25ce965ce56cb32f679", 20 | "iv": "355f307363d69ed58345ae395744f5f0" 21 | } 22 | } -------------------------------------------------------------------------------- /test/fixtures/secure.json: -------------------------------------------------------------------------------- 1 | { 2 | "isNull": { 3 | "alg": "aes-256-ctr", 4 | "value": "af07fbcf" 5 | }, 6 | "literal": { 7 | "alg": "aes-256-ctr", 8 | "value": "e310f6d94f13" 9 | }, 10 | "arr": { 11 | "alg": "aes-256-ctr", 12 | "value": "9a78b783175e69bb8f3458042b1c098d8ed9613410fac185b3735099224f8fe4ece0f0da8decfddbbf0eab3b7c391c47772b5441" 13 | }, 14 | "obj": { 15 | "alg": "aes-256-ctr", 16 | "value": "ba78b783175968add93a680429424ae4cf957d2916ebcfa399730bb17200ddb0ecacb183c1b1ebcd950ced76726964062e74643c995c47372bfb1311bee8f65bbeb5a1d9426537a6d83635220ec7934e1d7cc187f7218cd4afadfa2f107fb42c232d80d95c160ee704fa8e922998b0b3e47ec579dd0baef7cae6d7dbaa203d732adb5cff22b80d810d7191237999cd8dc528d8f2201ae128a9f9e2df96d1a816aa73e3e6b8e6246cd98b454e453b36f43f9117cb4af8fa85429a92" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/helpers.js: -------------------------------------------------------------------------------- 1 | /* 2 | * helpers.js: Test helpers for nconf. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var spawn = require('child_process').spawn; 9 | var fs = require('fs'); 10 | var path = require('path'); 11 | var nconf = require('../lib/nconf'); 12 | 13 | exports.assertMerged = function (err, merged) { 14 | merged = merged instanceof nconf.Provider 15 | ? merged.store.store 16 | : merged; 17 | 18 | expect() 19 | expect(err).toBeNull(); 20 | expect(typeof merged).toBe('object'); 21 | expect(merged.apples).toBeTruthy(); 22 | expect(merged.bananas).toBeTruthy(); 23 | expect(typeof merged.candy).toBe('object'); 24 | expect(merged.candy.something1).toBeTruthy(); 25 | expect(merged.candy.something2).toBeTruthy(); 26 | expect(merged.candy.something3).toBeTruthy(); 27 | expect(merged.candy.something4).toBeTruthy(); 28 | expect(merged.dates).toBeTruthy(); 29 | expect(merged.elderberries).toBeTruthy(); 30 | }; 31 | 32 | //FIXME TODO 33 | exports.assertSystemConf = function (options) { 34 | return done => { 35 | var env = null; 36 | 37 | if (options.env) { 38 | env = {} 39 | Object.keys(process.env).forEach(function (key) { 40 | env[key] = process.env[key]; 41 | }); 42 | 43 | Object.keys(options.env).forEach(function (key) { 44 | env[key] = options.env[key]; 45 | }); 46 | } 47 | 48 | var child = spawn('node', [options.script].concat(options.argv), {env: env}); 49 | child.stdout.once('data', data => { 50 | expect(data.toString()).toEqual('foobar'); 51 | done(); 52 | }); 53 | } 54 | } 55 | 56 | // copy a file 57 | exports.cp = function (from, to, callback) { 58 | fs.readFile(from, function (err, data) { 59 | if (err) return callback(err); 60 | fs.writeFile(to, data, callback); 61 | }); 62 | }; 63 | 64 | exports.fixture = function (file) { 65 | return path.join(__dirname, 'fixtures', file); 66 | }; 67 | -------------------------------------------------------------------------------- /test/hierarchy.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * hierarchy-test.js: Basic tests for hierarchical file stores. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var fs = require('fs'); 9 | var path = require('path'); 10 | var spawn = require('child_process').spawn; 11 | var nconf = require('../lib/nconf'); 12 | 13 | var configDir = path.join(__dirname, 'fixtures', 'hierarchy'); 14 | var globalConfig = path.join(configDir, 'global.json'); 15 | var userConfig = path.join(configDir, 'user.json'); 16 | 17 | describe('nconf/hierarchy, When using nconf', () => { 18 | it("configured with two file stores, should have the appropriate keys present", () => { 19 | nconf.add('user', {type: 'file', file: userConfig}); 20 | nconf.add('global', {type: 'file', file: globalConfig}); 21 | nconf.load(); 22 | 23 | expect(nconf.get('title')).toEqual('My specific title'); 24 | expect(nconf.get('color')).toEqual('green'); 25 | expect(nconf.get('movie')).toEqual('Kill Bill'); 26 | 27 | }); 28 | it("configured with two file stores using `file` should have the appropriate keys present", () => { 29 | nconf.file('user', userConfig); 30 | nconf.file('global', globalConfig); 31 | nconf.load(); 32 | 33 | expect(nconf.get('title')).toEqual('My specific title'); 34 | expect(nconf.get('color')).toEqual('green'); 35 | expect(nconf.get('movie')).toEqual('Kill Bill'); 36 | 37 | }); 38 | 39 | it("configured with .argv(), .env() and .file() should not persist information passed in to process.env and process.argv to disk", 40 | done => { 41 | var configFile = path.join(__dirname, 'fixtures', 'load-save.json'); 42 | var script = path.join(__dirname, 'fixtures', 'scripts', 'nconf-hierarchical-load-save.js'); 43 | var argv = ['--foo', 'foo', '--bar', 'bar']; 44 | var data = ''; 45 | 46 | try { 47 | fs.unlinkSync(configFile) 48 | } 49 | catch (ex) { 50 | // No-op 51 | } 52 | 53 | var child = spawn('node', [script].concat(argv)); 54 | 55 | child.stdout.on('data', function (d) { 56 | data += d; 57 | }); 58 | 59 | child.on('close', function () { 60 | fs.readFile(configFile, 'utf8', (err, ondisk) => { 61 | expect(data).toEqual('foo'); 62 | expect(JSON.parse(ondisk)).toEqual({ 63 | database: { 64 | host: '127.0.0.1', 65 | port: 5984 66 | } 67 | }); 68 | }); 69 | done(); 70 | }); 71 | 72 | }); 73 | 74 | it("configured with .argv(), .file() and invoked with nested command line options, should merge nested objects", 75 | done => { 76 | var script = path.join(__dirname, 'fixtures', 'scripts', 'nconf-hierarchical-load-merge.js'); 77 | var argv = ['--candy:something', 'foo', '--candy:something5:second', 'bar']; 78 | var data = ''; 79 | 80 | var child = spawn('node', [script].concat(argv)); 81 | 82 | child.stdout.on('data', function (d) { 83 | data += d; 84 | }); 85 | 86 | child.on('close', function () { 87 | expect(JSON.parse(data)).toEqual({ 88 | apples: true, 89 | candy: { 90 | something: 'foo', 91 | something1: true, 92 | something2: true, 93 | something5: { 94 | first: 1, 95 | second: 'bar' 96 | } 97 | } 98 | }); 99 | done(); 100 | }); 101 | }); 102 | it("configured with .argv() and separator, .file() and invoked with nested command line options should merge nested objects", done => { 103 | 104 | var script = path.join(__dirname, 'fixtures', 105 | 'scripts', 'nconf-hierarchical-load-merge-with-separator.js'); 106 | var argv = ['--candy--something', 'foo', '--candy--something5--second', 'bar']; 107 | var data = ''; 108 | process.env.candy__bonbon = 'sweet'; 109 | var child = spawn('node', [script].concat(argv), {env: process.env}); 110 | delete process.env.candy__bonbon; 111 | child.stdout.on('data', function (d) { 112 | data += d; 113 | }); 114 | 115 | child.on('close', function () { 116 | expect(JSON.parse(data)).toEqual({ 117 | apples: true, 118 | candy: { 119 | bonbon: 'sweet', 120 | something: 'foo', 121 | something1: true, 122 | something2: true, 123 | something5: { 124 | first: 1, 125 | second: 'bar' 126 | } 127 | } 128 | }); 129 | done(); 130 | }); 131 | }); 132 | 133 | it("configured with .file(), .defaults() should deep merge objects should merge nested objects ", done => { 134 | var script = path.join(__dirname, 'fixtures', 'scripts', 'nconf-hierarchical-defaults-merge.js'); 135 | var data = ''; 136 | var child = spawn('node', [script]); 137 | 138 | child.stdout.on('data', function (d) { 139 | data += d; 140 | }); 141 | 142 | child.on('close', function () { 143 | expect(JSON.parse(data)).toEqual({ 144 | candy: { 145 | something: 'much better something for you', 146 | something1: true, 147 | something2: true, 148 | something18: 'completely unique', 149 | something5: { 150 | first: 1, 151 | second: 99 152 | } 153 | } 154 | }); 155 | done(); 156 | }); 157 | }) 158 | }) 159 | -------------------------------------------------------------------------------- /test/mocks/mock-store.js: -------------------------------------------------------------------------------- 1 | /* 2 | * mock-store.js: Mock store for ensuring certain operations are actually called. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var util = require('util'), 9 | events = require('events'), 10 | nconf = require('../../lib/nconf'); 11 | 12 | var Mock = nconf.Mock = function () { 13 | events.EventEmitter.call(this); 14 | this.type = 'mock'; 15 | }; 16 | 17 | // Inherit from Memory store. 18 | util.inherits(Mock, events.EventEmitter); 19 | 20 | // 21 | // ### function save (value, callback) 22 | // #### @value {Object} _Ignored_ Left here for consistency 23 | // #### @callback {function} Continuation to respond to when complete. 24 | // Waits `1000ms` and then calls the callback and emits the `save` event. 25 | // 26 | Mock.prototype.save = function (value, callback) { 27 | if (!callback && typeof value === 'function') { 28 | callback = value; 29 | value = null; 30 | } 31 | 32 | var self = this; 33 | 34 | setTimeout(function () { 35 | self.emit('save'); 36 | callback(); 37 | }, 1000); 38 | }; -------------------------------------------------------------------------------- /test/nconf.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * file-store-test.js: Tests for the nconf File store. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var fs = require('fs'); 9 | var path = require('path'); 10 | var nconf = require('../lib/nconf'); 11 | 12 | describe('nconf, When using the nconf', () => { 13 | it("should have the correct methods set", () => { 14 | expect(typeof nconf.key).toBe('function'); 15 | expect(typeof nconf.path).toBe('function'); 16 | expect(typeof nconf.use).toBe('function'); 17 | expect(typeof nconf.any).toBe('function'); 18 | expect(typeof nconf.get).toBe('function'); 19 | expect(typeof nconf.set).toBe('function'); 20 | expect(typeof nconf.clear).toBe('function'); 21 | expect(typeof nconf.load).toBe('function'); 22 | expect(typeof nconf.save).toBe('function'); 23 | expect(typeof nconf.reset).toBe('function'); 24 | expect(typeof nconf.required).toBe('function'); 25 | }); 26 | it("the use() method should instaniate the correct store", () => { 27 | nconf.use('memory'); 28 | nconf.load(); 29 | expect(nconf.stores['memory'] instanceof nconf.Memory).toBe(true); 30 | }); 31 | it("nconf should have the correct version set", done => { 32 | fs.readFile(path.join(__dirname, '..', 'package.json'), (err, data) => { 33 | expect(err).toBe(null); 34 | data = JSON.parse(data.toString()); 35 | expect(nconf.version).toEqual(data.version); 36 | done(); 37 | }) 38 | }); 39 | describe("the required() method", () => { 40 | it("should throw error with missing keys", () => { 41 | nconf.set('foo:bar:bazz', 'buzz'); 42 | expect(nconf.required.bind(nconf, ['missing', 'foo:bar:bazz'])).toThrow(Error); 43 | }); 44 | it("should return the provider if all required keys exist", () => { 45 | var Provider = nconf.Provider; 46 | nconf.set('foo:bar:bazz', 'buzz'); 47 | expect(nconf.required(['foo:bar:bazz']) instanceof Provider).toBe(true); 48 | }); 49 | }); 50 | describe("with the memory store", () => { 51 | describe("the set() method", () => { 52 | it("should respond with true", () => { 53 | expect(nconf.set('foo:bar:bazz', 'buzz')).toBeTruthy(); 54 | }); 55 | it("should respond allow access to the root and complain about non-objects", () => { 56 | expect(nconf.set(null, null)).toBeFalsy(); 57 | expect(nconf.set(null, undefined)).toBeFalsy(); 58 | expect(nconf.set(null)).toBeFalsy(); 59 | expect(nconf.set(null, '')).toBeFalsy(); 60 | expect(nconf.set(null, 1)).toBeFalsy(); 61 | var original = nconf.get(); 62 | expect(nconf.set(null, nconf.get())).toBeTruthy(); 63 | expect(nconf.get()).not.toBe(original); 64 | expect(nconf.get()).toEqual(original) 65 | }) 66 | }); 67 | describe("the get() method", () => { 68 | it("should respond with the correct value without a callback", () => { 69 | expect(nconf.get('foo:bar:bazz')).toEqual('buzz'); 70 | }) 71 | it("should not step inside strings without a callback", () => { 72 | expect(nconf.get('foo:bar:bazz:0')).toEqual(undefined); 73 | }); 74 | it("should respond with the correct value with a callback", done => { 75 | 76 | nconf.get('foo:bar:bazz', (err, value) => { 77 | expect(value).toEqual('buzz'); 78 | done(); 79 | }) 80 | }) 81 | it("should respond allow access to the root", () => { 82 | expect(nconf.get(null)).toBeTruthy(); 83 | expect(nconf.get(undefined)).toBeTruthy(); 84 | expect(nconf.get()).toBeTruthy(); 85 | }) 86 | }); 87 | describe("the clear() method", () => { 88 | it("should respond with the true", () => { 89 | expect(nconf.get('foo:bar:bazz')).toEqual('buzz'); 90 | expect(nconf.clear('foo:bar:bazz')).toBeTruthy(); 91 | expect(typeof nconf.get('foo:bar:bazz') === 'undefined').toBeTruthy(); 92 | }) 93 | }) 94 | describe("the load() method", () => { 95 | 96 | it("should respond with the merged store without a callback", () => { 97 | expect(nconf.load()).toEqual({"foo": {"bar": {}}}); 98 | }); 99 | it("should respond with the merged store", done => { 100 | nconf.load((err, store) => { 101 | expect(err).toBe(null); 102 | expect(store).toEqual({"foo": {"bar": {}}}); 103 | done(); 104 | }); 105 | }) 106 | }) 107 | }) 108 | }) 109 | -------------------------------------------------------------------------------- /test/provider-save.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * provider-save-test.js: Ensures consistency for Provider `save` operations. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var nconf = require('../lib/nconf'); 9 | 10 | // 11 | // Expose `nconf.Mock` 12 | // 13 | require('./mocks/mock-store'); 14 | 15 | describe('nconf/provider/save', () => { 16 | describe("When using nconf an instance of 'nconf.Provider' with a Mock store", () => { 17 | var nconfMock = nconf.use('mock'); 18 | it("the save() method should actually save before responding", done => { 19 | var mock = nconf.stores.mock; 20 | 21 | mock.on('save', function () { 22 | nconfMock.saved = true; 23 | }); 24 | 25 | nconf.save(() => { 26 | expect(nconfMock.saved).toBeTruthy(); 27 | done(); 28 | }); 29 | }) 30 | }) 31 | }); -------------------------------------------------------------------------------- /test/provider.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * provider-test.js: Tests for the nconf Provider object. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var fs = require('fs'); 9 | var path = require('path'); 10 | var helpers = require('./helpers'); 11 | var nconf = require('../lib/nconf'); 12 | 13 | var fixturesDir = path.join(__dirname, 'fixtures'); 14 | var mergeFixtures = path.join(fixturesDir, 'merge'); 15 | var files = [path.join(mergeFixtures, 'file1.json'), path.join(mergeFixtures, 'file2.json')]; 16 | var override = JSON.parse(fs.readFileSync(files[0]), 'utf8'); 17 | 18 | describe('nconf/provider When using nconf', () => { 19 | describe("an instance of 'nconf.Provider'", () => { 20 | it("calling the use() method with the same store type and different options" 21 | + " should use a new instance of the store type", () => { 22 | var provider = new nconf.Provider().use('file', {file: files[0]}); 23 | var old = provider.stores['file']; 24 | 25 | expect(provider.stores.file.file).toEqual(files[0]); 26 | provider.use('file', {file: files[1]}); 27 | 28 | expect(old).not.toEqual(provider.stores.file); 29 | expect(provider.stores.file.file).toEqual(files[1]); 30 | }) 31 | }); 32 | it("respond with correct arg when 'argv' is true", 33 | helpers.assertSystemConf({ 34 | script: path.join(fixturesDir, 'scripts', 'provider-argv.js'), 35 | argv: ['--something', 'foobar'] 36 | })); 37 | it("respond with correct arg when 'env' is true", helpers.assertSystemConf({ 38 | script: path.join(fixturesDir, 'scripts', 'provider-env.js'), 39 | env: {SOMETHING: 'foobar'} 40 | })); 41 | 42 | it("respond with correct arg when 'env' is true and 'parseValues' option is true", () => { 43 | var env = { 44 | SOMETHING: 'foobar', 45 | SOMEBOOL: 'true', 46 | SOMENULL: 'null', 47 | SOMEUNDEF: 'undefined', 48 | SOMEINT: '3600', 49 | SOMEFLOAT: '0.5', 50 | SOMEBAD: '5.1a' 51 | }; 52 | var oenv = {}; 53 | Object.keys(env).forEach(function (key) { 54 | if (process.env[key]) oenv[key] = process.env[key]; 55 | process.env[key] = env[key]; 56 | }); 57 | var provider = new nconf.Provider().use('env', {parseValues: true}); 58 | Object.keys(env).forEach(function (key) { 59 | delete process.env[key]; 60 | if (oenv[key]) process.env[key] = oenv[key]; 61 | }); 62 | 63 | expect(provider.get('SOMETHING')).toEqual('foobar'); 64 | expect(provider.get('SOMEBOOL')).toEqual(true); 65 | expect(provider.get('SOMEBOOL')).not.toEqual('true'); 66 | expect(provider.get('SOMENULL')).toEqual(null); 67 | expect(provider.get('SOMEUNDEF')).toEqual(undefined); 68 | expect(provider.get('SOMEINT')).toEqual(3600); 69 | expect(provider.get('SOMEFLOAT')).toEqual(.5); 70 | expect(provider.get('SOMEBAD')).toEqual('5.1a'); 71 | }); 72 | 73 | describe("the default nconf provider", () => { 74 | 75 | it("respond with correct arg when 'argv' is set to true", helpers.assertSystemConf({ 76 | script: path.join(fixturesDir, 'scripts', 'nconf-argv.js'), 77 | argv: ['--something', 'foobar'], 78 | env: {SOMETHING: true} 79 | })); 80 | 81 | it("respond with correct arg when 'env' is set to true", helpers.assertSystemConf({ 82 | script: path.join(fixturesDir, 'scripts', 'nconf-env.js'), 83 | env: {SOMETHING: 'foobar'} 84 | })); 85 | 86 | it("respond with correct arg when 'argv' is set to true and process.argv is modified", helpers.assertSystemConf({ 87 | script: path.join(fixturesDir, 'scripts', 'nconf-change-argv.js'), 88 | argv: ['--something', 'badValue', 'evenWorse', 'OHNOEZ', 'foobar'] 89 | })); 90 | 91 | it("respond with correct arg when hierarchical 'argv' get", helpers.assertSystemConf({ 92 | script: path.join(fixturesDir, 'scripts', 'nconf-hierarchical-file-argv.js'), 93 | argv: ['--something', 'foobar'], 94 | env: {SOMETHING: true} 95 | })); 96 | 97 | it("respond with correct arg when 'env' is set to true with a nested separator", helpers.assertSystemConf({ 98 | script: path.join(fixturesDir, 'scripts', 'nconf-nested-env.js'), 99 | env: {SOME_THING: 'foobar'} 100 | })); 101 | }); 102 | 103 | describe("an instance of 'nconf.Provider'", () => { 104 | describe("the merge() method", () => { 105 | it("should have the result merged in", () => { 106 | var provider = new nconf.Provider().use('file', {file: files[1]}); 107 | provider.load(); 108 | provider.merge(override); 109 | helpers.assertMerged(null, provider.stores.file.store); 110 | expect(provider.stores.file.store.candy.something).toEqual('file1'); 111 | }); 112 | it("should merge Objects over null", () => { 113 | var provider = new nconf.Provider().use('file', {file: files[1]}); 114 | provider.load(); 115 | provider.merge(override); 116 | expect(provider.stores.file.store.unicorn.exists).toEqual(true); 117 | }); 118 | }) 119 | describe("the load() method", () => { 120 | it("should respect the hierarchy when sources are passed in", () => { 121 | var provider = new nconf.Provider({ 122 | sources: { 123 | user: { 124 | type: 'file', 125 | file: files[0] 126 | }, 127 | global: { 128 | type: 'file', 129 | file: files[1] 130 | } 131 | } 132 | }); 133 | var merged = provider.load(); 134 | helpers.assertMerged(null, merged); 135 | expect(merged.candy.something).toEqual('file1'); 136 | }) 137 | it("should respect the hierarchy when multiple stores are used", () => { 138 | var provider = new nconf.Provider().overrides({foo: {bar: 'baz'}}) 139 | .add('file1', {type: 'file', file: files[0]}) 140 | .add('file2', {type: 'file', file: files[1]}); 141 | 142 | var merged = provider.load(); 143 | 144 | helpers.assertMerged(null, merged); 145 | expect(merged.foo.bar).toEqual('baz'); 146 | expect(merged.candy.something).toEqual('file1'); 147 | }) 148 | }) 149 | }) 150 | describe("the .file() method", () => { 151 | it("should use the correct File store with a single filepath", () => { 152 | var provider = new nconf.Provider(); 153 | provider.file(helpers.fixture('store.json')); 154 | expect(typeof provider.stores.file).toBe('object'); 155 | }); 156 | it("should use the correct File store with a name and a filepath", () => { 157 | var provider = new nconf.Provider(); 158 | provider.file('custom', helpers.fixture('store.json')); 159 | expect(typeof provider.stores.custom).toBe('object'); 160 | }); 161 | it("should use the correct File store with a single object", () => { 162 | var provider = new nconf.Provider(); 163 | provider.file({ 164 | dir: helpers.fixture(''), 165 | file: 'store.json', 166 | search: true 167 | }); 168 | 169 | expect(typeof provider.stores.file).toBe('object'); 170 | expect(provider.stores.file.file).toEqual(helpers.fixture('store.json')); 171 | }); 172 | it("should use the correct File store with a name and an object", () => { 173 | var provider = new nconf.Provider(); 174 | provider.file('custom', { 175 | dir: helpers.fixture(''), 176 | file: 'store.json', 177 | search: true 178 | }); 179 | 180 | expect(typeof provider.stores.custom).toBe('object'); 181 | expect(provider.stores.custom.file).toEqual(helpers.fixture('store.json')); 182 | }) 183 | describe("the any() method", () => { 184 | var provider = new nconf.Provider({ 185 | type: 'literal', 186 | store: { 187 | key: "getThisValue" 188 | } 189 | }) 190 | describe("without a callback", () => { 191 | it("should respond with the correct value given an array of keys with one matching", () => { 192 | expect(provider.any(["notthis", "orthis", "key"])).toEqual('getThisValue'); 193 | }) 194 | it("should respond with null given an array of keys with no match", () => { 195 | expect(provider.any(["notthis", "orthis"])).toBe(null); 196 | }); 197 | it("should respond with the correct value given a variable argument list of keys with one matching", () => { 198 | expect(provider.any("notthis", "orthis", "key")).toEqual('getThisValue'); 199 | }); 200 | it("should respond with null given no arguments", () => { 201 | expect(provider.any()).toBe(null); 202 | }); 203 | }) 204 | describe("with a callback", () => { 205 | it("should respond with the correct value given an array of keys with one matching", done => { 206 | provider.any(["notthis", "orthis", "key"], (err, value) => { 207 | expect(value).toEqual('getThisValue'); 208 | done(); 209 | }); 210 | }); 211 | it("should respond with an undefined value given an array of keys with no match", done => { 212 | provider.any(["notthis", "orthis"], (err, value) => { 213 | expect(value).toBe(undefined) 214 | done(); 215 | }); 216 | }); 217 | it("should respond with the correct value given a variable argument list of keys with one matching", done => { 218 | provider.any("notthis", "orthis", "key", (err, value) => { 219 | expect(value).toEqual('getThisValue'); 220 | done(); 221 | }); 222 | }); 223 | 224 | it("should respond with an undefined value given no keys", done => { 225 | provider.any((err, value) => { 226 | expect(value).toBe(undefined) 227 | done(); 228 | }); 229 | }); 230 | }) 231 | }) 232 | }) 233 | }); 234 | -------------------------------------------------------------------------------- /test/stores/argv.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * argv-test.js: Tests for the nconf argv store. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var yargs = require('yargs'); 9 | var nconf = require('../../lib/nconf'); 10 | 11 | process.env.DEEP__NESTED__VALUE = 'foo'; 12 | 13 | describe('nconf/stores/argv, An instance of nconf.Argv', () => { 14 | 15 | it("should have the correct methods defined", () => { 16 | var argv = new nconf.Argv(); 17 | expect(typeof argv.loadSync).toBe('function'); 18 | expect(typeof argv.loadArgv).toBe('function'); 19 | expect(argv.options).toEqual({}); 20 | }); 21 | 22 | it("should be able to retrieve a value using the logical separator", () => { 23 | var argv = new nconf.Argv({ 24 | deep__nested__value: {alias: 'nv', default: 'foo'}, 25 | accessSeparator: '.', 26 | inputSeparator: '__' 27 | }); 28 | argv.loadSync(); 29 | 30 | expect(argv.accessSeparator).toBe('.'); 31 | expect(argv.get('deep.nested.value')).toBe('foo'); 32 | expect(argv.get('deep:nested:value')).toBe('foo'); 33 | }); 34 | 35 | describe("can be created with a custom yargs", () => { 36 | var yargsInstance = yargs.alias('s', 'somearg').default('s', 'false'); 37 | 38 | it("and can give access to them", () => { 39 | var argv = new nconf.Argv(yargsInstance); 40 | expect(argv.options).toBe(yargsInstance); 41 | }); 42 | 43 | it("values are the one from the custom yargv", () => { 44 | var argv = new nconf.Argv(yargsInstance); 45 | argv.loadSync(); 46 | expect(argv.get('somearg')).toBe('false'); 47 | expect(argv.get('s')).toBe('false'); 48 | }); 49 | }); 50 | 51 | describe("can be created with a nconf yargs", () => { 52 | var options = {somearg: {alias: 's', default: 'false'}}; 53 | it("and can give access to them", () => { 54 | var argv = new nconf.Argv(options); 55 | expect(argv.options).toEqual({somearg: {alias: 's', default: 'false'}}); 56 | }); 57 | 58 | it("values are the one from the custom yargv", () => { 59 | var argv = new nconf.Argv(options); 60 | argv.loadSync(); 61 | expect(argv.get('somearg')).toBe('false'); 62 | expect(argv.get('s')).toBe('false'); 63 | }); 64 | 65 | it("values cannot be altered with set when readOnly:true", () => { 66 | var argv = new nconf.Argv(options); 67 | argv.loadSync(); 68 | argv.set('somearg', 'true'); 69 | expect(argv.get('somearg')).toBe('false'); 70 | }); 71 | }); 72 | describe("can be created with readOnly set to be false", () => { 73 | 74 | it("readOnly is actually false", () => { 75 | var argv = new nconf.Argv({readOnly: false}); 76 | expect(argv.readOnly).toBe(false); 77 | }); 78 | 79 | it("values can be changed by calling .set", () => { 80 | var argv = new nconf.Argv({somearg: {alias: 's', default: 'false'}, readOnly: false}); 81 | argv.loadSync(); 82 | expect(argv.get('somearg')).toBe('false'); 83 | argv.set('somearg', 'true'); 84 | expect(argv.get('somearg')).toBe('true'); 85 | }); 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /test/stores/env.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * env-test.js: Tests for the nconf env store. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var nconf = require('../../lib/nconf'); 9 | 10 | process.env.DEEP__NESTED__VALUE = 'foo'; 11 | process.env.DEEP2__NESTED__VALUE = 'bar'; 12 | 13 | describe('nconf/stores/env, An instance of nconf.Env', () => { 14 | it("should have the correct methods defined", () => { 15 | var env = new nconf.Env(); 16 | expect(typeof env.loadSync).toBe('function'); 17 | expect(typeof env.loadEnv).toBe('function'); 18 | expect(env.whitelist instanceof Array).toBeTruthy(); 19 | expect(env.whitelist.length).toEqual(0); 20 | expect(env.inputSeparator).toEqual('__'); 21 | }); 22 | it("should have the correct methods defined and with readOnly false", () => { 23 | var env = new nconf.Env({readOnly: false}); 24 | expect(typeof env.loadSync).toBe('function'); 25 | expect(typeof env.loadEnv).toBe('function'); 26 | expect(env.whitelist instanceof Array).toBeTruthy(); 27 | expect(env.whitelist.length).toEqual(0); 28 | expect(env.inputSeparator).toEqual('__'); 29 | expect(env.readOnly).toBe(false); 30 | }); 31 | it("should be able to retrieve a value using the logical separator", () => { 32 | var env = new nconf.Env({accessSeparator: '.', inputSeparator: '__'}); 33 | env.loadSync(); 34 | 35 | expect(env.accessSeparator).toBe('.'); 36 | expect(env.get('DEEP.NESTED.VALUE')).toBe('foo'); 37 | }); 38 | it("should filter and strip prefix from environment variables", () => { 39 | var env = new nconf.Env({prefix: 'DEEP2__'}); 40 | env.loadSync(); 41 | expect(env.get('DEEP__NESTED__VALUE')).toBeUndefined(); 42 | expect(env.get('NESTED__VALUE')).toBe('bar'); 43 | }); 44 | it("should filter and strip prefix from environment variables with input and access separator", () => { 45 | var env = new nconf.Env({prefix: 'DEEP2__', accessSeparator: '.', inputSeparator: '__' }); 46 | env.loadSync(); 47 | expect(env.get('DEEP.NESTED.VALUE')).toBeUndefined(); 48 | expect(env.get('NESTED.VALUE')).toBe('bar'); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /test/stores/file-store.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * file-store-test.js: Tests for the nconf File store. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var fs = require('fs'); 9 | var os = require('os'); 10 | var path = require('path'); 11 | var nconf = require('../../lib/nconf'); 12 | var yamlFormat = require('nconf-yaml'); 13 | var data = require('../fixtures/data').data; 14 | 15 | describe('nconf/stores/file', () => { 16 | describe("When using the nconf file store", () => { 17 | describe("with a valid JSON file", () => { 18 | var filePath = path.join(__dirname, '..', 'fixtures', 'store.json'); 19 | fs.writeFileSync(filePath, JSON.stringify(data, null, 2)); 20 | 21 | it("the load() method should load the data correctly", done => { 22 | var store = new nconf.File({ file: filePath }); 23 | store.load((err, data) => { 24 | expect(err).toBe(null); 25 | expect(data).toEqual(store.store); 26 | done(); 27 | }) 28 | }); 29 | }); 30 | describe("with a malformed JSON file", () => { 31 | var filePath = path.join(__dirname, '..', 'fixtures', 'malformed.json'); 32 | 33 | it("the load() method with a malformed JSON config file, should respond with an error and indicate file name", 34 | done => { 35 | var store = new nconf.File({ file: filePath }); 36 | //FIXME this.store.load(this.callback.bind(null, null)); 37 | store.load((err) => { 38 | expect(err).toBeTruthy(); 39 | expect(err.message).toMatch(/malformed\.json/); 40 | done(); 41 | }) 42 | }); 43 | }); 44 | describe("with a valid UTF8 JSON file that contains a BOM", () => { 45 | var filePath = path.join(__dirname, '..', 'fixtures', 'bom.json'); 46 | var store = new nconf.File({ file: filePath }); 47 | 48 | it("the load() method should load the data correctly", done => { 49 | store.load((err, data) => { 50 | expect(err).toBe(null); 51 | expect(data).toEqual(store.store); 52 | done(); 53 | }); 54 | }); 55 | it("the loadSync() method should load the data correctly", () => { 56 | var data = store.loadSync(); 57 | expect(data).toEqual(store.store); 58 | }); 59 | }); 60 | describe("with a valid UTF8 JSON file that contains no BOM", () => { 61 | var filePath = path.join(__dirname, '..', 'fixtures', 'no-bom.json'); 62 | var store = new nconf.File({ file: filePath }); 63 | 64 | it("the load() method should load the data correctly", done => { 65 | store.load((err, data) => { 66 | expect(err).toBe(null); 67 | expect(data).toEqual(store.store); 68 | done(); 69 | }); 70 | }); 71 | it("the loadSync() method should load the data correctly", () => { 72 | var data = store.loadSync(); 73 | expect(data).toEqual(store.store); 74 | }); 75 | }) 76 | }); 77 | 78 | describe("When using the nconf file store", () => { 79 | var tmpPath = path.join(__dirname, '..', 'fixtures', 'tmp.json'); 80 | 81 | it("the save() method should save the data correctly", done => { 82 | var tmpStore = new nconf.File({ file: tmpPath }); 83 | 84 | Object.keys(data).forEach(function (key) { 85 | tmpStore.set(key, data[key]); 86 | }); 87 | 88 | tmpStore.save(function () { 89 | fs.readFile(tmpStore.file, function (err, d) { 90 | fs.unlinkSync(tmpStore.file); 91 | 92 | expect(err).toBe(null); 93 | expect(JSON.parse(d.toString())).toEqual(data); 94 | done(); 95 | }); 96 | }); 97 | }); 98 | it("the saveToFile() method should save the data correctly", done => { 99 | var tmpStore = new nconf.File({ file: tmpPath }); 100 | var pathFile = path.join(__dirname, '..', 'fixtures', 'tmp-save-tofile.json'); 101 | 102 | Object.keys(data).forEach(function (key) { 103 | tmpStore.set(key, data[key]); 104 | }); 105 | 106 | tmpStore.saveToFile(pathFile, function () { 107 | fs.readFile(pathFile, function (err, d) { 108 | fs.unlinkSync(pathFile); 109 | 110 | expect(err).toBe(null); 111 | expect(JSON.parse(d.toString())).toEqual(data); 112 | done(); 113 | }); 114 | }); 115 | 116 | }); 117 | it("the saveToFile() method with custom format should save the data correctly", done => { 118 | var tmpStore = new nconf.File({ file: tmpPath }); 119 | var pathFile = path.join(__dirname, '..', 'fixtures', 'tmp-save-tofile.yaml'); 120 | 121 | Object.keys(data).forEach(function (key) { 122 | tmpStore.set(key, data[key]); 123 | }); 124 | 125 | tmpStore.saveToFile(pathFile, yamlFormat, function () { 126 | fs.readFile(pathFile, function (err, d) { 127 | fs.unlinkSync(pathFile); 128 | 129 | expect(err).toBe(null); 130 | expect(yamlFormat.parse(d.toString())).toEqual(data); 131 | done(); 132 | }); 133 | }); 134 | }); 135 | }); 136 | 137 | describe("When using the nconf file store", () => { 138 | var tmpPath = path.join(__dirname, '..', 'fixtures', 'tmp.json'); 139 | it("the saveSync() method should save the data correctly", done => { 140 | var tmpStore = new nconf.File({ file: tmpPath }); 141 | Object.keys(data).forEach(function (key) { 142 | tmpStore.set(key, data[key]); 143 | }); 144 | 145 | var saved = tmpStore.saveSync(); 146 | 147 | fs.readFile(tmpStore.file, function (err, d) { 148 | fs.unlinkSync(tmpStore.file); 149 | 150 | expect(err).toBe(null); 151 | var read = JSON.parse(d.toString()); 152 | expect(read).toEqual(data); 153 | expect(read).toEqual(saved); 154 | done(); 155 | }); 156 | 157 | }); 158 | }); 159 | describe("When using the nconf file store", () => { 160 | var tmpPath = path.join(__dirname, '..', 'fixtures', 'tmp.json'); 161 | var store = new nconf.File({ file: tmpPath }); 162 | 163 | it("the set() method should respond with true", () => { 164 | expect(store.set('foo:bar:bazz', 'buzz')).toBeTruthy(); 165 | expect(store.set('falsy:number', 0)).toBeTruthy(); 166 | expect(store.set('falsy:string', '')).toBeTruthy(); 167 | expect(store.set('falsy:boolean', false)).toBeTruthy(); 168 | expect(store.set('falsy:object', null)).toBeTruthy(); 169 | }); 170 | it("the get() method should respond with the correct value", () => { 171 | expect(store.get('foo:bar:bazz')).toEqual('buzz'); 172 | expect(store.get('falsy:number')).toEqual(0); 173 | expect(store.get('falsy:string')).toEqual(''); 174 | expect(store.get('falsy:boolean')).toEqual(false); 175 | expect(store.get('falsy:object')).toEqual(null); 176 | }); 177 | it("the clear() method should respond with the true", () => { 178 | expect(store.get('foo:bar:bazz')).toEqual('buzz'); 179 | expect(store.clear('foo:bar:bazz')).toBeTruthy(); 180 | expect(typeof store.get('foo:bar:bazz') === 'undefined').toBeTruthy(); 181 | }); 182 | }); 183 | describe("When using the nconf file store", () => { 184 | 185 | it("the search() method when the target file exists higher in the directory tree should update the file appropriately", () => { 186 | var searchBase = require('os').homedir(); 187 | var filePath = path.join(searchBase, '.nconf'); 188 | fs.writeFileSync(filePath, JSON.stringify(data, null, 2)); 189 | var store = new nconf.File({ 190 | file: '.nconf' 191 | }); 192 | store.search(store.searchBase); 193 | expect(store.file).toEqual(filePath); 194 | fs.unlinkSync(filePath); 195 | }); 196 | it("the search() method when the target file doesn't exist higher in the directory tree should update the file appropriately", () => { 197 | var filePath = path.join(__dirname, '..', 'fixtures', 'search-store.json'); 198 | var store = new nconf.File({ 199 | dir: path.dirname(filePath), 200 | file: 'search-store.json' 201 | }); 202 | store.search(); 203 | expect(store.file).toEqual(filePath); 204 | }); 205 | 206 | }) 207 | describe("When using the nconf file store", () => { 208 | var secureStore = new nconf.File({ 209 | file: path.join(__dirname, '..', 'fixtures', 'secure-iv.json'), 210 | secure: 'super-secret-key-32-characterszz' 211 | }); 212 | 213 | secureStore.store = data; 214 | 215 | it("the stringify() method should encrypt properly", () => { 216 | var contents = JSON.parse(secureStore.stringify()); 217 | Object.keys(data).forEach(key => { 218 | expect(typeof contents[key]).toBe('object'); 219 | expect(typeof contents[key].value).toBe('string'); 220 | expect(contents[key].alg).toEqual('aes-256-ctr'); 221 | expect(typeof contents[key].iv).toBe('string'); 222 | }); 223 | }); 224 | it("the parse() method should decrypt properly", () => { 225 | var contents = secureStore.stringify(); 226 | var parsed = secureStore.parse(contents); 227 | expect(parsed).toEqual(data); 228 | }); 229 | it("the load() method should decrypt properly", () => { 230 | secureStore.load(function (err, loaded) { 231 | expect(err).toBe(null); 232 | expect(loaded).toEqual(data); 233 | }); 234 | }); 235 | it("the loadSync() method should decrypt properly", () => { 236 | var loaded = secureStore.loadSync(); 237 | expect(loaded).toEqual(data); 238 | }); 239 | }) 240 | 241 | describe("When using nconf file store", () => { 242 | it("the stringify() method should return a complete last line (EOL)", () => { 243 | var storePath = path.join(__dirname, '..', 'fixtures', 'store.json'); 244 | var store = new nconf.File({ 245 | file: storePath, 246 | }); 247 | 248 | var contents = store.stringify(); 249 | expect(contents.slice(-1)).toEqual(os.EOL); 250 | }); 251 | 252 | it("with option `eol` set to `false`, the stringify() method should return an incomplete last line (no EOL)", () => { 253 | var storePath = path.join(__dirname, '..', 'fixtures', 'store.json'); 254 | this.store = new nconf.File({ 255 | file: storePath, 256 | eol: false 257 | }); 258 | this.store.load((err) => { 259 | expect(err).toEqual(null); 260 | expect(this.store.stringify().slice(-1)).not.toEqual(os.EOL); 261 | }); 262 | }); 263 | }) 264 | 265 | describe("When using the nconf file store", () => { 266 | var secureStore = new nconf.File({ 267 | file: path.join(__dirname, '..', 'fixtures', 'secure.json'), 268 | secure: 'super-secretzzz' 269 | }); 270 | 271 | it("the load() method should throw an error when presented a legacy encrypted file", (done) => { 272 | secureStore.load(function (err, loaded) { 273 | try { 274 | expect(err).not.toBe(null); 275 | expect(loaded).toEqual(void 0); 276 | done(); 277 | } catch (err) { 278 | done(err); 279 | } 280 | }); 281 | }); 282 | it("the loadSync() method should throw an error when presented a legacy encrypted file", () => { 283 | expect(() => { 284 | secureStore.loadSync(); 285 | }).toThrow(); 286 | }); 287 | }) 288 | 289 | }); -------------------------------------------------------------------------------- /test/stores/literal.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * literal-test.js: Tests for the nconf literal store. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var nconf = require('../../lib/nconf'); 9 | 10 | describe('nconf/stores/literal, An instance of nconf.Literal', () => { 11 | var envOptions = {foo: 'bar', one: 2}; 12 | it("should have the correct methods defined", () => { 13 | var literal = new nconf.Literal(envOptions); 14 | expect(literal.type).toEqual('literal'); 15 | expect(typeof literal.get).toBe('function'); 16 | expect(typeof literal.set).toBe('function'); 17 | expect(typeof literal.merge).toBe('function'); 18 | expect(typeof literal.loadSync).toBe('function'); 19 | }); 20 | it("should have the correct values in the store", () => { 21 | var literal = new nconf.Literal(envOptions); 22 | expect(literal.store.foo).toEqual('bar'); 23 | expect(literal.store.one).toEqual(2); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/stores/memory-store.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * memory-store-test.js: Tests for the nconf Memory store. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var nconf = require('../../lib/nconf'); 9 | var merge = require('../fixtures/data').merge; 10 | 11 | describe('nconf/stores/memory', () => { 12 | describe("When using the nconf memory store", () => { 13 | var store = new nconf.Memory(); 14 | it("the set() method should respond with true", () => { 15 | expect(store.set('foo:bar:bazz', 'buzz')).toBeTruthy(); 16 | expect(store.set('falsy:number', 0)).toBeTruthy(); 17 | expect(store.set('falsy:string:empty', '')).toBeTruthy(); 18 | expect(store.set('falsy:string:value', 'value')).toBeTruthy(); 19 | expect(store.set('falsy:boolean', false)).toBeTruthy(); 20 | expect(store.set('falsy:object', null)).toBeTruthy(); 21 | }); 22 | 23 | it("the get() method should respond with the correct value", () => { 24 | expect(store.get('foo:bar:bazz')).toEqual('buzz'); 25 | expect(store.get('falsy:number')).toEqual(0); 26 | expect(store.get('falsy:string:empty')).toEqual(''); 27 | expect(store.get('falsy:string:value')).toEqual('value'); 28 | expect(store.get('falsy:boolean')).toEqual(false); 29 | expect(store.get('falsy:object')).toEqual(null); 30 | }); 31 | 32 | describe("the get() method should not fail when retrieving non-existent keys", () => { 33 | it("at the root level", () => { 34 | expect(store.get('this:key:does:not:exist')).toEqual(undefined); 35 | }); 36 | 37 | it("within numbers", () => { 38 | expect(store.get('falsy:number:not:exist')).toEqual(undefined); 39 | }); 40 | 41 | it("within booleans", () => { 42 | expect(store.get('falsy:boolean:not:exist')).toEqual(undefined); 43 | }); 44 | 45 | it("within objects", () => { 46 | expect(store.get('falsy:object:not:exist')).toEqual(undefined); 47 | }); 48 | 49 | it("within empty strings", () => { 50 | expect(store.get('falsy:string:empty:not:exist')).toEqual(undefined); 51 | }); 52 | 53 | it("within non-empty strings", () => { 54 | expect(store.get('falsy:string:value:not:exist')).toEqual(undefined); 55 | }); 56 | }); 57 | 58 | it("the clear() method, should respond with the true", () => { 59 | expect(store.get('foo:bar:bazz')).toEqual('buzz'); 60 | expect(store.clear('foo:bar:bazz')).toBeTruthy(); 61 | expect(typeof store.get('foo:bar:bazz') === 'undefined').toBeTruthy(); 62 | }); 63 | 64 | describe("the merge() method", () => { 65 | it("when overriding an existing literal value", () => { 66 | store.set('merge:literal', 'string-value'); 67 | store.merge('merge:literal', merge); 68 | expect(store.get('merge:literal')).toEqual(merge); 69 | }); 70 | 71 | it("when overriding an existing Array value", () => { 72 | store.set('merge:array', [1, 2, 3, 4]); 73 | store.merge('merge:array', merge); 74 | expect(store.get('merge:literal')).toEqual(merge); 75 | }); 76 | 77 | it("when merging into an existing Object value", () => { 78 | store.set('merge:object', { 79 | prop1: 2, 80 | prop2: 'prop2', 81 | prop3: { 82 | bazz: 'bazz' 83 | }, 84 | prop4: ['foo', 'bar'] 85 | }); 86 | store.merge('merge:object', merge); 87 | 88 | expect(store.get('merge:object:prop1')).toEqual(1); 89 | expect(store.get('merge:object:prop2').length).toEqual(3); 90 | expect(store.get('merge:object:prop3')).toEqual({ 91 | foo: 'bar', 92 | bar: 'foo', 93 | bazz: 'bazz' 94 | }); 95 | expect(store.get('merge:object:prop4').length).toEqual(2); 96 | }); 97 | }); 98 | }); 99 | describe("When using the nconf memory store with different logical separator", () => { 100 | var store = new nconf.Memory({ accessSeparator: '||', disableDefaultAccessSeparator: true }); 101 | 102 | it("when storing with : (colon), should store the config atomicly (leave key as-is)", () => { 103 | store.set('foo:bar:bazz', 'buzz'); 104 | expect(typeof store.get('foo:bar') === 'undefined').toBeTruthy(); 105 | expect(store.get('foo:bar:bazz')).toEqual('buzz'); 106 | }); 107 | 108 | it("when storing with separator, should be able to read the object", () => { 109 | store.set('foo||bar||bazz', 'buzz'); 110 | expect(store.get('foo||bar').bazz).toEqual('buzz'); 111 | expect(store.get('foo').bar.bazz).toEqual('buzz'); 112 | }) 113 | }); 114 | }); 115 | -------------------------------------------------------------------------------- /usage.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | path = require('path'), 3 | nconf = require('./lib/nconf'); 4 | 5 | // 6 | // Configure the provider with a single store and 7 | // support for command-line arguments and environment 8 | // variables. 9 | // 10 | var single = new nconf.Provider({ 11 | env: true, 12 | argv: true, 13 | store: { 14 | type: 'file', 15 | file: path.join(__dirname, 'config.json') 16 | } 17 | }); 18 | 19 | // 20 | // Configure the provider with multiple hierarchical stores 21 | // representing `user` and `global` configuration values. 22 | // 23 | var multiple = new nconf.Provider({ 24 | stores: [ 25 | { name: 'user', type: 'file', file: path.join(__dirname, 'user-config.json') }, 26 | { name: 'global', type: 'global', file: path.join(__dirname, 'global-config.json') } 27 | ] 28 | }); 29 | 30 | // 31 | // Setup nconf to use the 'file' store and set a couple of values; 32 | // 33 | nconf.use('file', { file: path.join(__dirname, 'config.json') }); 34 | nconf.set('database:host', '127.0.0.1'); 35 | nconf.set('database:port', 5984); 36 | 37 | // 38 | // Get the entire database object from nconf 39 | // 40 | var database = nconf.get('database'); 41 | console.dir(database); 42 | 43 | // 44 | // Save the configuration object to disk 45 | // 46 | nconf.save(function (err) { 47 | fs.readFile(path.join(__dirname, 'config.json'), function (err, data) { 48 | console.dir(JSON.parse(data.toString())) 49 | }); 50 | }); --------------------------------------------------------------------------------