├── .airtap.yml ├── .github ├── codecov.yml ├── dependabot.yml └── workflows │ ├── release.yml │ ├── sauce.yml │ └── test.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── UPGRADING.md ├── lib ├── batch.js ├── common.js ├── levelup.js ├── next-tick-browser.js └── next-tick.js ├── package.json ├── sauce-labs.svg └── test ├── .gitignore ├── batch-test.js ├── binary-test.js ├── browserify-test.js ├── chained-batch-encoding-test.js ├── clear-test.js ├── common.js ├── create-stream-vs-put-racecondition.js ├── custom-encoding-test.js ├── data ├── browser-throws.js └── browser-works.js ├── deferred-open-test.js ├── get-put-del-test.js ├── idempotent-test.js ├── index.js ├── init-test.js ├── iterator-seek-test.js ├── iterator-test.js ├── json-encoding-test.js ├── key-value-streams-test.js ├── manifest-test.js ├── maybe-error-test.js ├── no-encoding-test.js ├── null-and-undefined-test.js ├── open-patchsafe-test.js ├── read-stream-test.js ├── self.js ├── self └── manifest-test.js ├── snapshot-test.js └── util ├── discardable.js ├── rs-context.js └── rs-factory.js /.airtap.yml: -------------------------------------------------------------------------------- 1 | providers: 2 | - airtap-sauce 3 | 4 | browsers: 5 | - name: chrome 6 | - name: firefox 7 | # https://github.com/airtap/sauce/issues/11 8 | # - name: safari 9 | # version: 12..latest 10 | - name: ios_saf 11 | version: 12..latest 12 | # TODO: look into android 10 failure 13 | - name: chrome for android 14 | version: 6..9 15 | - name: chrome for android 16 | version: 11 17 | - name: msedge 18 | 19 | presets: 20 | local: 21 | providers: 22 | - airtap-playwright 23 | browsers: 24 | - name: chromium 25 | - name: firefox 26 | -------------------------------------------------------------------------------- /.github/codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | threshold: 5% 6 | patch: off 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: / 5 | schedule: 6 | interval: monthly 7 | ignore: 8 | - dependency-name: dependency-check 9 | - package-ecosystem: github-actions 10 | directory: / 11 | schedule: 12 | interval: monthly -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | tags: ['*'] 5 | permissions: 6 | contents: write 7 | jobs: 8 | release: 9 | name: Release 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v3 14 | - name: Create GitHub release 15 | uses: docker://antonyurchenko/git-release:v4 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | -------------------------------------------------------------------------------- /.github/workflows/sauce.yml: -------------------------------------------------------------------------------- 1 | name: Sauce Labs 2 | on: push 3 | concurrency: sauce-labs 4 | jobs: 5 | test: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Checkout 9 | uses: actions/checkout@v3 10 | - name: Set up node 11 | uses: actions/setup-node@v3 12 | with: 13 | node-version: 14 14 | - name: Install 15 | run: npm install 16 | env: 17 | # Download Sauce Connect binary now instead of on first run 18 | SAUCE_CONNECT_DOWNLOAD_ON_INSTALL: true 19 | - name: Add host 20 | run: echo "127.0.0.1 airtap.local" | sudo tee -a /etc/hosts 21 | - name: Test 22 | run: npm run test-browsers 23 | env: 24 | SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }} 25 | SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }} 26 | - name: Coverage 27 | run: npm run coverage 28 | - name: Codecov 29 | uses: codecov/codecov-action@v3 30 | with: 31 | file: coverage/lcov.info 32 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: [push, pull_request] 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | matrix: 8 | node: [10, 12, 14] 9 | name: Node ${{ matrix.node }} 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v3 13 | - name: Use node ${{ matrix.node }} 14 | uses: actions/setup-node@v3 15 | with: 16 | node-version: ${{ matrix.node }} 17 | - name: Install 18 | run: npm install 19 | - name: Test 20 | run: npm test 21 | - name: Coverage 22 | run: npm run coverage 23 | - name: Codecov 24 | uses: codecov/codecov-action@v3 25 | with: 26 | file: coverage/lcov.info 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | libleveldb.so 4 | libleveldb.a 5 | test-data/ 6 | _benchdb_* 7 | *.sw* 8 | bulk2ssd_pop.config 9 | libpeerconnection.log 10 | yarn.lock 11 | package-lock.json 12 | .nyc_output/ 13 | airtap.log 14 | coverage/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [5.1.1] - 2021-10-02 4 | 5 | ### Added 6 | 7 | - Document new features ([#727](https://github.com/Level/levelup/issues/727)) ([`e1ecad9`](https://github.com/Level/levelup/commit/e1ecad9)) (Vincent Weevers) 8 | 9 | ### Fixed 10 | 11 | - Expose nextTick for API parity with `abstract-leveldown` ([`7bc86e4`](https://github.com/Level/levelup/commit/7bc86e4)) (Vincent Weevers) 12 | - Set `supports.status` to true ([`e2e2c34`](https://github.com/Level/levelup/commit/e2e2c34)) (Vincent Weevers) 13 | 14 | ## [5.1.0] - 2021-10-01 15 | 16 | ### Changed 17 | 18 | - Bump `deferred-leveldown` from 6.x to 7.x ([`2226bba`](https://github.com/Level/levelup/commit/2226bba)) (Vincent Weevers) 19 | 20 | ### Added 21 | 22 | - Add `db.getMany(keys)` ([`02cf2d3`](https://github.com/Level/levelup/commit/02cf2d3)) (Vincent Weevers). 23 | 24 | ## [5.0.1] - 2021-06-07 25 | 26 | ### Changed 27 | 28 | - Remove use of `assert` module ([#721](https://github.com/Level/levelup/issues/721)) ([`f3e86ae`](https://github.com/Level/levelup/commit/f3e86ae)) (Alex Potsides) 29 | 30 | ## [5.0.0] - 2021-04-17 31 | 32 | _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._ 33 | 34 | ### Changed 35 | 36 | - **Breaking:** modernize syntax and bump `standard` ([Level/community#98](https://github.com/Level/community/issues/98)) ([`e19cd54`](https://github.com/Level/levelup/commit/e19cd54), [`762d989`](https://github.com/Level/levelup/commit/762d989)) (Vincent Weevers) 37 | - **Breaking:** remove `Batch._levelup` property ([`cfce6bb`](https://github.com/Level/levelup/commit/cfce6bb)) (Vincent Weevers). 38 | - Align `nextTick` behavior with `abstract-leveldown` ([`4b35716`](https://github.com/Level/levelup/commit/4b35716)) (Vincent Weevers). 39 | - Add `files` to package.json and remove `.npmignore` ([`29d8b5d`](https://github.com/Level/levelup/commit/29d8b5d)) (Vincent Weevers) 40 | - Replace `xtend` with `Object.assign()` ([`7bfc0d4`](https://github.com/Level/levelup/commit/7bfc0d4)) (Vincent Weevers) 41 | - Bump `deferred-leveldown`, `level-errors`, `-iterator-stream` and `-supports` ([`8b518b1`](https://github.com/Level/levelup/commit/8b518b1), [`1b0cfb8`](https://github.com/Level/levelup/commit/1b0cfb8)) (Vincent Weevers) 42 | - Refactor `promisify()` code by using `catering` module ([#700](https://github.com/Level/levelup/issues/700)) (Lars-Magnus Skog) 43 | 44 | ### Added 45 | 46 | - Support encoding options on chained batch `put()` and `del()` ([#717](https://github.com/Level/levelup/issues/717), [#633](https://github.com/Level/levelup/issues/633)) ([`0765808`](https://github.com/Level/levelup/commit/0765808)) (Vincent Weevers) 47 | 48 | ### Removed 49 | 50 | - **Breaking:** drop node 6, 8, IE, Safari 9-11 & stock Android ([Level/community#98](https://github.com/Level/community/issues/98)) ([`bb1d4da`](https://github.com/Level/levelup/commit/bb1d4da)) (Vincent Weevers). 51 | - Remove legacy range options from readme and tests ([Level/community#86](https://github.com/Level/community/issues/86)) ([`2df2a44`](https://github.com/Level/levelup/commit/2df2a44)) (Vincent Weevers) 52 | - Remove default export ([Level/community#87](https://github.com/Level/community/issues/87)) ([`3fd21e2`](https://github.com/Level/levelup/commit/3fd21e2)) (Vincent Weevers) 53 | 54 | ## [4.4.0] - 2020-04-11 55 | 56 | ### Changed 57 | 58 | - Increase `abstract-leveldown` parity ([#692](https://github.com/Level/levelup/issues/692)) ([**@vweevers**](https://github.com/vweevers)): 59 | - Add `db` property to chained batch 60 | - Remove type checks that are also performed by `abstract-leveldown` 61 | - Upgrade `dependency-check` devDependency from `^3.3.0` to `^4.1.0` ([`71a6aa3`](https://github.com/Level/levelup/commit/71a6aa3)) ([**@vweevers**](https://github.com/vweevers)) 62 | - Upgrade `airtap` devDependency from `^2.0.0` to `^3.0.0` ([#687](https://github.com/Level/levelup/issues/687)) ([**@vweevers**](https://github.com/vweevers)) 63 | 64 | ## [4.3.2] - 2019-10-04 65 | 66 | ### Changed 67 | 68 | - Upgrade `deferred-leveldown` from `~5.2.1` to `~5.3.0` ([#682](https://github.com/Level/levelup/issues/682)) ([**@vweevers**](https://github.com/vweevers)). This fixes the manifest added in 4.3.0. 69 | 70 | ### Added 71 | 72 | - Test manifest integration with `deferred-leveldown` ([#681](https://github.com/Level/levelup/issues/681)) ([**@vweevers**](https://github.com/vweevers)) 73 | 74 | ## [4.3.1] - 2019-10-03 75 | 76 | ### Fixed 77 | 78 | - Fix floating promise in constructor ([#680](https://github.com/Level/levelup/issues/680)) ([**@vweevers**](https://github.com/vweevers)) 79 | 80 | ## [4.3.0] - 2019-09-30 81 | 82 | ### Changed 83 | 84 | - Rewrite `buster` tests as `tape` tests ([#674](https://github.com/Level/levelup/issues/674)) ([**@vweevers**](https://github.com/vweevers)) 85 | - Create test suite ([#677](https://github.com/Level/levelup/issues/677)) ([**@vweevers**](https://github.com/vweevers)) 86 | 87 | ### Added 88 | 89 | - Add manifest ([Level/community#83](https://github.com/Level/community/issues/83)) ([#678](https://github.com/Level/levelup/issues/678)) ([**@vweevers**](https://github.com/vweevers)) 90 | - Add `type` property for `reachdown` ([Level/community#82](https://github.com/Level/community/issues/82)) ([#678](https://github.com/Level/levelup/issues/678)) ([**@vweevers**](https://github.com/vweevers)) 91 | 92 | ## [4.2.0] - 2019-09-08 93 | 94 | ### Changed 95 | 96 | - Upgrade `deferred-leveldown` from `~5.1.0` to `~5.2.0` ([#669](https://github.com/Level/levelup/issues/669)) ([**@vweevers**](https://github.com/vweevers)) 97 | - Upgrade `hallmark` devDependency from `^0.1.0` to `^2.0.0` ([#664](https://github.com/Level/levelup/issues/664), [#672](https://github.com/Level/levelup/issues/672)) ([**@vweevers**](https://github.com/vweevers)) 98 | - Upgrade `standard` devDependency from `^12.0.0` to `^14.1.0` ([#663](https://github.com/Level/levelup/issues/663), [`cd3af83`](https://github.com/Level/levelup/commit/cd3af83)) ([**@vweevers**](https://github.com/vweevers)) 99 | - Upgrade `memdown` devDependency from `^4.0.0` to `^5.0.0` ([#668](https://github.com/Level/levelup/issues/668)) ([**@vweevers**](https://github.com/vweevers)) 100 | 101 | ### Added 102 | 103 | - Add `clear()` method to delete all entries or a range ([#669](https://github.com/Level/levelup/issues/669)) ([**@vweevers**](https://github.com/vweevers)) 104 | 105 | ## [4.1.0] - 2019-06-28 106 | 107 | _Many thanks to [**@MeirionHughes**](https://github.com/MeirionHughes) for adding `seek()` support to `memdown`, `encoding-down`, `deferred-leveldown` and `subleveldown`. At the time of writing, all but `subleveldown` have been released. Go forth and seek!_ 108 | 109 | ### Changed 110 | 111 | - Upgrade `deferred-leveldown` from `~5.0.0` to `~5.1.0` ([#657](https://github.com/Level/levelup/issues/657)) ([**@vweevers**](https://github.com/vweevers)) 112 | - Upgrade `delayed` devDependency from `^1.0.1` to `^2.0.0` ([#659](https://github.com/Level/levelup/issues/659)) ([**@vweevers**](https://github.com/vweevers)) 113 | 114 | ### Added 115 | 116 | - Test `seek()` integration ([#661](https://github.com/Level/levelup/issues/661)) ([**@vweevers**](https://github.com/vweevers)) \* 117 | - Support options passed to `open()` ([#660](https://github.com/Level/levelup/issues/660), [#662](https://github.com/Level/levelup/issues/662)) ([**@achingbrain**](https://github.com/achingbrain), [**@vweevers**](https://github.com/vweevers)) 118 | 119 | ## [4.0.2] - 2019-06-08 120 | 121 | ### Changed 122 | 123 | - Replace `async` devDependency with `async-each` and `run-*` ([#654](https://github.com/Level/levelup/issues/654)) ([`d9ff554`](https://github.com/Level/levelup/commit/d9ff554)) ([**@vweevers**](https://github.com/vweevers)) 124 | - Upgrade `nyc` devDependency from `^13.1.0` to `^14.0.0` ([#649](https://github.com/Level/levelup/issues/649)) ([`4f8b141`](https://github.com/Level/levelup/commit/4f8b141)) ([**@vweevers**](https://github.com/vweevers)) 125 | 126 | ### Added 127 | 128 | - Document need of Promise polyfill for IE ([`a2e7a49`](https://github.com/Level/levelup/commit/a2e7a49)) ([**@vweevers**](https://github.com/vweevers)) 129 | 130 | ### Removed 131 | 132 | - Remove unused `test-10k-times.sh` ([#651](https://github.com/Level/levelup/issues/651)) ([`6a033f1`](https://github.com/Level/levelup/commit/6a033f1)) ([**@MadsAndreasenTechPeople**](https://github.com/MadsAndreasenTechPeople)) 133 | - Remove outdated `Support` section from `README.md` ([`956eb0b`](https://github.com/Level/levelup/commit/956eb0b)) ([**@vweevers**](https://github.com/vweevers)) 134 | - Remove references to old wiki in favor of [`Level/awesome`](https://github.com/Level/awesome) ([`f534fde`](https://github.com/Level/levelup/commit/f534fde)) ([**@vweevers**](https://github.com/vweevers)) 135 | 136 | ### Fixed 137 | 138 | - Fix Level badge ([`1a2199f`](https://github.com/Level/levelup/commit/1a2199f)) ([**@vweevers**](https://github.com/vweevers)) 139 | - Remove link to dead website ([`c8ccb6c`](https://github.com/Level/levelup/commit/c8ccb6c)) ([**@vweevers**](https://github.com/vweevers)) 140 | 141 | ## [4.0.1] - 2019-03-30 142 | 143 | ### Changed 144 | 145 | - Upgrade dependencies of benchmarks ([#637](https://github.com/Level/levelup/issues/637)) ([**@morolt**](https://github.com/morolt)) 146 | - Upgrade `memdown` devDependency from `^3.0.0` to `^4.0.0` ([#646](https://github.com/Level/levelup/issues/646)) ([**@vweevers**](https://github.com/vweevers)) 147 | - Upgrade `bl` devDependency from `^2.0.0` to `^3.0.0` ([#643](https://github.com/Level/levelup/issues/643)) ([**@vweevers**](https://github.com/vweevers)) 148 | - Upgrade `airtap` devDependency from `0.1.0` to `^2.0.0` ([#631](https://github.com/Level/levelup/issues/631)) ([**@vweevers**](https://github.com/vweevers)) 149 | - Upgrade `encoding-down` devDependency from `^5.0.0` to `^6.0.0` ([#629](https://github.com/Level/levelup/issues/629)) ([**@vweevers**](https://github.com/vweevers)) 150 | - Apply common project tweaks ([#634](https://github.com/Level/levelup/issues/634), [#635](https://github.com/Level/levelup/issues/635), [`b83add5`](https://github.com/Level/levelup/commit/b83add5)) ([**@vweevers**](https://github.com/vweevers)) 151 | 152 | ## [4.0.0] - 2018-12-22 153 | 154 | _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._ 155 | 156 | ### Changed 157 | 158 | - Upgrade `nyc` devDependency from `~12.0.2` to `~13.1.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 159 | - Upgrade `deferred-leveldown` dependency from `~4.0.0` to `~5.0.0` ([**@vweevers**](https://github.com/vweevers)) 160 | - Upgrade `concat-stream` devDependency from `~1.6.0` to `~2.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 161 | - Upgrade `level-iterator-stream` dependency from `~3.0.0` to `~4.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 162 | - Replace `remark-cli` with `hallmark` ([#621](https://github.com/level/levelup/issues/621)) ([**@vweevers**](https://github.com/vweevers)) 163 | - Upgrade `standard` devDependency from `^11.0.0` to `^12.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 164 | - Add `.nyc_output/` to `.npmignore` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 165 | 166 | ### Removed 167 | 168 | - Remove `IE10` from `airtap` ([#625](https://github.com/level/levelup/issues/625)) ([**@ralphtheninja**](https://github.com/ralphtheninja)) 169 | 170 | ## [3.1.1] - 2018-07-14 171 | 172 | ### Changed 173 | 174 | - Upgrade `airtap` from `0.0.7` to `0.1.0` ([**@vweevers**](https://github.com/vweevers), [**@ralphtheninja**](https://github.com/ralphtheninja)) 175 | - Upgrade `level-iterator-stream` from `~2.0.0` to `~3.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 176 | - Pass options to `batch.write()` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 177 | 178 | ### Added 179 | 180 | - Add `nyc` and `coveralls` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 181 | 182 | ### Removed 183 | 184 | - Remove node 9 ([**@ralphtheninja**](https://github.com/ralphtheninja)) 185 | 186 | ### Fixed 187 | 188 | - Fix issue with `airtap --local` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 189 | - Call `rs.destroy()` without using `.bind()` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 190 | 191 | ## [3.1.0] - 2018-06-22 192 | 193 | ### Changed 194 | 195 | - Upgrade `airtap` from `0.0.6` to `0.0.7` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 196 | - Update `.npmignore` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 197 | - Tweak copyright year for less maintenance ([**@ralphtheninja**](https://github.com/ralphtheninja)) 198 | 199 | ### Added 200 | 201 | - Expose `db.iterator()` ([**@vweevers**](https://github.com/vweevers)) 202 | - Add `remark` tooling ([**@vweevers**](https://github.com/vweevers)) 203 | 204 | ### Removed 205 | 206 | - Remove `contributors` from `package.json` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 207 | - Remove copyright headers from code ([**@ralphtheninja**](https://github.com/ralphtheninja)) 208 | - Remove LevelDB and Snappy credits ([**@vweevers**](https://github.com/vweevers)) 209 | 210 | ### Fixed 211 | 212 | - Replace `remark` with `remark-cli` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 213 | 214 | ## [3.0.1] - 2018-05-24 215 | 216 | ### Changed 217 | 218 | - Upgrade `airtap` to `0.0.6` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 219 | 220 | ### Removed 221 | 222 | - Remove `.jshintrc` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 223 | - Remove `brfs` and use `Buffer.from()` in favor of `fs.readFileSync()` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 224 | 225 | ## [3.0.0] - 2018-05-23 226 | 227 | _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._ 228 | 229 | ### Added 230 | 231 | - Add node 10 to Travis ([**@ralphtheninja**](https://github.com/ralphtheninja)) 232 | - Add browser support to test suite ([**@vweevers**](https://github.com/vweevers)) 233 | - Add `airtap` for browser tests in Sauce Labs ([**@vweevers**](https://github.com/vweevers)) 234 | 235 | ### Changed 236 | 237 | - Upgrade `memdown` to `^3.0.0` ([**@vweevers**](https://github.com/vweevers)) 238 | - Upgrade `encoding-down` to `^5.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 239 | - Upgrade `deferred-leveldown` to `~4.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 240 | - Upgrade `standard` to `^11.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 241 | - Upgrade `level-errors` to `~2.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 242 | - Upgrade `bl` to `^2.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 243 | - README: tweak api sub sections ([**@ralphtheninja**](https://github.com/ralphtheninja)) 244 | 245 | ### Fixed 246 | 247 | - Fix defunct `keyEncoding` in `inject-encoding-test.js` ([**@vweevers**](https://github.com/vweevers)) 248 | 249 | ### Removed 250 | 251 | - Remove irrelevant `leveldown-substitution-test.js` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 252 | - Remove node 4 from Travis ([**@ralphtheninja**](https://github.com/ralphtheninja)) 253 | - Remove batch operations defaulting to `put` ([**@vweevers**](https://github.com/vweevers)) 254 | - Remove compiler toolchain from Travis ([**@vweevers**](https://github.com/vweevers)) 255 | 256 | ## [2.0.2] - 2018-02-12 257 | 258 | ### Added 259 | 260 | - Add 9 to Travis ([**@ralphtheninja**](https://github.com/ralphtheninja)) 261 | 262 | ### Changed 263 | 264 | - Upgrade `browserify` to `16.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 265 | - Upgrade `leveldown` to `3.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 266 | - Upgrade `deferred-leveldown` to `3.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 267 | - README: normalize readme style ([**@ralphtheninja**](https://github.com/ralphtheninja)) 268 | - README: use markdown links instead of `` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 269 | - Clarify 'must provide db' error message ([**@adityapurwa**](https://github.com/adityapurwa)) 270 | - Update copyright year to 2018 ([**@adityapurwa**](https://github.com/adityapurwa)) 271 | 272 | ### Removed 273 | 274 | - Remove `abstract-leveldown` devDependency ([**@ralphtheninja**](https://github.com/ralphtheninja)) 275 | 276 | ## [2.0.1] - 2017-11-11 277 | 278 | ### Changed 279 | 280 | - README: clarify that options are specific to the underlying store ([**@ralphtheninja**](https://github.com/ralphtheninja)) 281 | - Upgrade `abstract-leveldown` to `3.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 282 | - Upgrade `encoding-down` to `3.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 283 | 284 | ### Fixed 285 | 286 | - Restore support for node 4 ([**@farskipper**](https://github.com/farskipper)) 287 | 288 | ## [2.0.0] - 2017-10-10 289 | 290 | _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._ 291 | 292 | ### Added 293 | 294 | - Add default export ([**@zixia**](https://github.com/zixia)) 295 | - Test that key and value of queued operation is not serialized ([**@vweevers**](https://github.com/vweevers)) 296 | - Test JSON encoding with stream ([**@vweevers**](https://github.com/vweevers)) 297 | - Add smoke test for `levelup` and `leveldown` without `encoding-down` ([**@vweevers**](https://github.com/vweevers)) 298 | 299 | ### Changed 300 | 301 | - Upgrade `leveldown` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 302 | - README: prefer 'underlying store' over database, backend etc ([**@vweevers**](https://github.com/vweevers)) 303 | - README: update badges ([**@ralphtheninja**](https://github.com/ralphtheninja)) 304 | - README: unquote properties ([**@vweevers**](https://github.com/vweevers)) 305 | - README: clarify what excluding callback means ([**@vweevers**](https://github.com/vweevers)) 306 | - README: 'arbitrary data object' => 'of any type' ([**@vweevers**](https://github.com/vweevers)) 307 | - README: reduce 'supported platforms' section ([**@vweevers**](https://github.com/vweevers)) 308 | - README: rewrite intro and relationship with leveldown ([**@vweevers**](https://github.com/vweevers)) 309 | - README: cleanup ([**@vweevers**](https://github.com/vweevers)) 310 | - README: fix bad async code example ([**@ralphtheninja**](https://github.com/ralphtheninja)) 311 | - Upgrade `deferred-leveldown` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 312 | 313 | ### Removed 314 | 315 | - Remove unstable typings and Typescript tests ([**@MeirionHughes**](https://github.com/MeirionHughes)) 316 | 317 | ## [2.0.0-rc3] - 2017-09-15 318 | 319 | ### Changed 320 | 321 | - Refactor typings, use `abstract-leveldown` types ([**@MeirionHughes**](https://github.com/MeirionHughes)) 322 | - Upgrade `leveldown` ([**@MeirionHughes**](https://github.com/MeirionHughes)) 323 | 324 | ### Fixed 325 | 326 | - Correct bad encoding options in tests ([**@MeirionHughes**](https://github.com/MeirionHughes)) 327 | 328 | ## [2.0.0-rc2] - 2017-09-11 329 | 330 | ### Added 331 | 332 | - README: add node version badge ([**@ralphtheninja**](https://github.com/ralphtheninja)) 333 | - Add Typescript definitions and testing ([**@MeirionHughes**](https://github.com/MeirionHughes)) 334 | 335 | ### Changed 336 | 337 | - README: homogenize readme style ([**@vweevers**](https://github.com/vweevers)) 338 | - Upgrade `level-errors` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 339 | - Optimize Typescript tests ([**@vweevers**](https://github.com/vweevers)) 340 | 341 | ### Removed 342 | 343 | - Remove 7 from Travis ([**@ralphtheninja**](https://github.com/ralphtheninja)) 344 | 345 | ## [2.0.0-rc1] - 2017-09-01 346 | 347 | ### Added 348 | 349 | - Add `Promise` to the API if callbacks are omitted ([**@juliangruber**](https://github.com/juliangruber)) 350 | - Add Greenkeeper badge ([**@ralphtheninja**](https://github.com/ralphtheninja)) 351 | - Add tests for `maybeError()` calling back synchronously if db is closed ([**@ralphtheninja**](https://github.com/ralphtheninja)) 352 | 353 | ### Changed 354 | 355 | - Upgrade `deferred-leveldown` to `2.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 356 | - Change `levelup` constructor to take store as first parameter ([**@ralphtheninja**](https://github.com/ralphtheninja)) 357 | - Switch to use `AbstractLevelDOWN#status` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 358 | - Upgrade copyright year to 2017 ([**@ralphtheninja**](https://github.com/ralphtheninja)) 359 | - Rename `lib/util.js` to `lib/promisify.js` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 360 | 361 | ### Removed 362 | 363 | - Remove `approximateSize()` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 364 | - Remove `destroy()` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 365 | - Remove `repair()` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 366 | - Remove `getProperty()` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 367 | - Remove `.errorIfExists` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 368 | - Remove `.createIfMissing` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 369 | - Remove `.compression` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 370 | - Remove `.cacheSize` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 371 | - Remove `.sync` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 372 | - Remove `.fillCache` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 373 | - Remove optional `leveldown` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 374 | - Remove unused `options` parameter from `maybeError` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 375 | - Remove `browser` field from `package.json` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 376 | - Remove 0.12 and 4 from Travis ([**@juliangruber**](https://github.com/juliangruber)) 377 | - Remove unused `isDefined` from `lib/util.js` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 378 | - Remove encodings ([**@ralphtheninja**](https://github.com/ralphtheninja)) 379 | - Remove `dispatchError()`, callback is always a function ([**@ralphtheninja**](https://github.com/ralphtheninja)) 380 | 381 | ### Fixed 382 | 383 | - Fix problems with zalgo in `maybeError()` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 384 | 385 | ## [1.3.9] - 2017-07-26 386 | 387 | ### Added 388 | 389 | - Add `standard` for linting ([**@ralphtheninja**](https://github.com/ralphtheninja)) 390 | - Add 8 to Travis ([**@ralphtheninja**](https://github.com/ralphtheninja)) 391 | 392 | ### Changed 393 | 394 | - Ignore `package-lock.json` and `yarn.lock` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 395 | - README: make code examples adhere to `standard` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 396 | - Upgrade dependencies ([**@ralphtheninja**](https://github.com/ralphtheninja)) 397 | 398 | ## [1.3.8] - 2017-05-29 399 | 400 | ### Changed 401 | 402 | - Revert previous `getLevelDOWN` fix ([**@ralphtheninja**](https://github.com/ralphtheninja)) 403 | - Throw more descriptive error if db factory is not a function ([**@ralphtheninja**](https://github.com/ralphtheninja)) 404 | 405 | ## [1.3.7] - 2017-05-24 406 | 407 | ### Fixed 408 | 409 | - Avoid calling `getLevelDOWN` if not present ([**@diasdavid**](https://github.com/diasdavid)) 410 | 411 | ## [1.3.6] - 2017-05-10 412 | 413 | ### Changed 414 | 415 | - Pull `LevelDOWN` loader out to non browserified module ([**@kemitchell**](https://github.com/kemitchell)) 416 | 417 | ## [1.3.5] - 2017-03-02 418 | 419 | ### Changed 420 | 421 | - Explicitly require `leveldown/package.json` ([**@PascalTemel**](https://github.com/PascalTemel)) 422 | 423 | ## [1.3.4] - 2017-03-02 424 | 425 | ### Added 426 | 427 | - Add 7 to Travis ([**@ralphtheninja**](https://github.com/ralphtheninja)) 428 | 429 | ### Removed 430 | 431 | - Remove 0.10 and 5 from Travis ([**@ralphtheninja**](https://github.com/ralphtheninja)) 432 | 433 | ## [1.3.3] - 2016-10-09 434 | 435 | ### Changed 436 | 437 | - README: fix typo ([**@jamesgrayling**](https://github.com/jamesgrayling)) 438 | - README: fix typo ([**@danielravina**](https://github.com/danielravina)) 439 | - README: fix typo ([**@juliangruber**](https://github.com/juliangruber)) 440 | 441 | ## [1.3.2] - 2016-05-17 442 | 443 | ### Added 444 | 445 | - Add node 6 to Travis ([**@ralphtheninja**](https://github.com/ralphtheninja)) 446 | 447 | ### Changed 448 | 449 | - Use `sudo: false` to run tests in containers on Travis ([**@ralphtheninja**](https://github.com/ralphtheninja)) 450 | - Update `package.json` ([**@0x00A**](https://github.com/0x00A)) 451 | - README: fix typos ([**@pra85**](https://github.com/pra85)) 452 | - README: changed build status ticker from png to svg ([**@montyanderson**](https://github.com/montyanderson)) 453 | - README: link build badge to master branch ([**@a0viedo**](https://github.com/a0viedo)) 454 | - Update copyright year to 2016 ([**@ralphtheninja**](https://github.com/ralphtheninja)) 455 | - Rename `appromixate-size-test.js` to `approximate-size-test.js` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 456 | 457 | ### Removed 458 | 459 | - Remove non supported versions from Travis ([**@ralphtheninja**](https://github.com/ralphtheninja)) 460 | 461 | ### Fixed 462 | 463 | - Ensure Travis can compile in case no prebuilt binaries can be found ([**@ralphtheninja**](https://github.com/ralphtheninja)) 464 | - Fix deprecation test ([**@juliangruber**](https://github.com/juliangruber)) 465 | 466 | ## [1.3.1] - 2015-12-10 467 | 468 | ### Added 469 | 470 | - Add node 5 to travis ([**@ralphtheninja**](https://github.com/ralphtheninja)) 471 | 472 | ### Changed 473 | 474 | - Upgrade outdated dependencies ([**@ralphtheninja**](https://github.com/ralphtheninja)) 475 | - Test on latest node 2, node 3 ([**@ralphtheninja**](https://github.com/ralphtheninja)) 476 | 477 | ## [1.3.0] - 2015-11-12 478 | 479 | ### Changed 480 | 481 | - README: fixed small typo (Stephen Sawchuck) 482 | - README: update url to Snappy ([**@hansott**](https://github.com/hansott)) 483 | - README: add dependency badge ([**@ralphtheninja**](https://github.com/ralphtheninja)) 484 | - Test on all major abi versions ([**@ralphtheninja**](https://github.com/ralphtheninja)) 485 | - Upgrade outdated dependencies ([**@ralphtheninja**](https://github.com/ralphtheninja)) 486 | - Track and expose chained batch ops queue length ([**@kemitchell**](https://github.com/kemitchell)) 487 | 488 | ### Fixed 489 | 490 | - Dev depend on `tap` to fix `npm@3` warning ([**@ralphtheninja**](https://github.com/ralphtheninja)) 491 | 492 | ## [1.2.1] - 2015-06-10 493 | 494 | ### Changed 495 | 496 | - Improve error message when trying to require `leveldown` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 497 | 498 | ## [1.2.0] - 2015-06-04 499 | 500 | ### Changed 501 | 502 | - Less restrictive version on `leveldown` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 503 | 504 | ### Fixed 505 | 506 | - Handle errors in benchmarks ([**@juliangruber**](https://github.com/juliangruber)) 507 | 508 | ## [1.1.1] - 2015-05-29 509 | 510 | ### Added 511 | 512 | - Add link to `level/community` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 513 | 514 | ### Changed 515 | 516 | - Upgrade `leveldown` dependency ([**@juliangruber**](https://github.com/juliangruber)) 517 | 518 | ### Removed 519 | 520 | - Remove compression tests ([**@juliangruber**](https://github.com/juliangruber)) 521 | 522 | ## [1.1.0] - 2015-05-17 523 | 524 | ### Changed 525 | 526 | - Batch operation default to `'put'` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 527 | 528 | ## [1.0.0] - 2015-05-14 529 | 530 | ### Removed 531 | 532 | - Remove return values from `dispatchError()` and `readError()`, they are used as voids ([**@ralphtheninja**](https://github.com/ralphtheninja)) 533 | 534 | ## [1.0.0-5] - 2015-05-07 535 | 536 | ### Changed 537 | 538 | - Target multiple iojs versions, remove notifications ([**@ralphtheninja**](https://github.com/ralphtheninja)) 539 | - Deprecate static functions `destroy()` and `repair()` ([**@juliangruber**](https://github.com/juliangruber)) 540 | 541 | ## [1.0.0-4] - 2015-05-06 542 | 543 | ### Changed 544 | 545 | - Deprecate `.approximateSize()` ([**@juliangruber**](https://github.com/juliangruber)) 546 | 547 | ## [1.0.0-3] - 2015-05-05 548 | 549 | ### Changed 550 | 551 | - Replace `tap` with `tape` + `faucet` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 552 | - Refactor read streams using `level-iterator-stream` and `level-codec` ([**@juliangruber**](https://github.com/juliangruber)) 553 | 554 | ## [1.0.0-2] - 2015-04-30 555 | 556 | ### Changed 557 | 558 | - Refactor ltgt encoding ([**@juliangruber**](https://github.com/juliangruber)) 559 | 560 | ### Fixed 561 | 562 | - Fix readStream \*AsBuffer options ([**@juliangruber**](https://github.com/juliangruber)) 563 | 564 | ## [1.0.0-1] - 2015-04-28 565 | 566 | ### Added 567 | 568 | - Add test for `valueEncoding` `'hex'` for `createReadStream` ([**@braydonf**](https://github.com/braydonf)) 569 | 570 | ### Changed 571 | 572 | - Upgrade dependencies ([**@ralphtheninja**](https://github.com/ralphtheninja)) 573 | 574 | ### Fixed 575 | 576 | - Fix `valueEncoding` bug by passing options without array ([**@braydonf**](https://github.com/braydonf)) 577 | 578 | ## [1.0.0-0] - 2015-04-28 579 | 580 | ### Added 581 | 582 | - Add [**@jcrugzz**](https://github.com/jcrugzz) as contributor 583 | - Add 0.12 and iojs to Travis ([**@ralphtheninja**](https://github.com/ralphtheninja)) 584 | 585 | ### Changed 586 | 587 | - Support values to be `null` or `undefined` ([**@kesla**](https://github.com/kesla)) 588 | - README: explain callback arguments to `del` ([**@bewest**](https://github.com/bewest)) 589 | - README: update logo and copyright ([**@ralphtheninja**](https://github.com/ralphtheninja)) 590 | - README: remove docs on `createWriteStream()` and add note on what happened to it ([**@jcrugzz**](https://github.com/jcrugzz)) 591 | - README: tweak explanation on why `createWriteStream()` was removed ([**@ralphtheninja**](https://github.com/ralphtheninja)) 592 | - README: clean up old `level-ws` reference ([**@ralphtheninja**](https://github.com/ralphtheninja)) 593 | - README: changed options for get to same as put ([**@RichardLitt**](https://github.com/RichardLitt)) 594 | - README: remove reference to write-stream and iterators ([**@ralphtheninja**](https://github.com/ralphtheninja)) 595 | - Explicit devdep versions ([**@rvagg**](https://github.com/rvagg)) 596 | - Update Travis and `package.json` scripts ([**@jcrugzz**](https://github.com/jcrugzz)) 597 | - Added errors to the available namespace when requiring `levelup` ([**@braydonf**](https://github.com/braydonf)) 598 | - Extract error codes into `level-errors` module ([**@ralphtheninja**](https://github.com/ralphtheninja)) 599 | - Use `level-codec` ([**@juliangruber**](https://github.com/juliangruber)) 600 | - Refactor iterators using new `deferred-leveldown` ([**@juliangruber**](https://github.com/juliangruber)) 601 | 602 | ### Removed 603 | 604 | - Remove 0.8 from Travis ([**@rvagg**](https://github.com/rvagg)) 605 | - Remove references to write-stream in tests ([**@jcrugzz**](https://github.com/jcrugzz)) 606 | - Remove references to write-stream ([**@jcrugzz**](https://github.com/jcrugzz)) 607 | - Remove fstream based tests ([**@jcrugzz**](https://github.com/jcrugzz)) 608 | - Remove `copy` as it requires write-stream ([**@jcrugzz**](https://github.com/jcrugzz)) 609 | - Remove unused dependencies ([**@ralphtheninja**](https://github.com/ralphtheninja)) 610 | - Remove `encoding` option ([**@juliangruber**](https://github.com/juliangruber)) 611 | - Remove `leveled` benchmarks ([**@juliangruber**](https://github.com/juliangruber)) 612 | 613 | ### Fixed 614 | 615 | - README: fix the leveldb link ([**@seriousManual**](https://github.com/seriousManual)) 616 | - Use newer memdown store ([**@sorribas**](https://github.com/sorribas)) 617 | - Check `notFound` on err ([**@doowb**](https://github.com/doowb)) 618 | - Fix benchmarks by installing `leveldown@^0.10.4` ([**@juliangruber**](https://github.com/juliangruber)) 619 | - Fix `stream-bench.js` ([**@juliangruber**](https://github.com/juliangruber)) 620 | - Replace `rvagg/node-` with `level` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 621 | 622 | ## [0.19.1] - 2016-01-23 623 | 624 | ### Added 625 | 626 | - Add 0.12, 1.0, 1.8, 2, 3, 4, 5 to Travis ([**@ralphtheninja**](https://github.com/ralphtheninja)) 627 | - Add `tape@4.x.x` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 628 | 629 | ### Changed 630 | 631 | - Upgrade `semver` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 632 | - Upgrade `tap` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 633 | - Update compiler on Travis ([**@ralphtheninja**](https://github.com/ralphtheninja)) 634 | - Fix `bustermove` version ([**@ralphtheninja**](https://github.com/ralphtheninja)) 635 | 636 | ### Removed 637 | 638 | - Remove 0.8 from Travis ([**@ralphtheninja**](https://github.com/ralphtheninja)) 639 | 640 | ## [0.19.0] - 2014-08-26 641 | 642 | ### Added 643 | 644 | - Add suport for `lt`, `lte`, `gt`, and `gte` ([**@dominictarr**](https://github.com/dominictarr)) 645 | - Add `isDefined` to util ([**@dominictarr**](https://github.com/dominictarr)) 646 | 647 | ### Changed 648 | 649 | - Refactor encodings and codec from util to separate file ([**@dominictarr**](https://github.com/dominictarr)) 650 | - Decouple codec from levelup parts for allowing arbitrary encoding strategies ([**@dominictarr**](https://github.com/dominictarr)) 651 | - Decouple read-stream from encoding and opening stuff ([**@dominictarr**](https://github.com/dominictarr)) 652 | - Keep codec on the db as `db._codec` ([**@dominictarr**](https://github.com/dominictarr)) 653 | - Refactor error checks ([**@dominictarr**](https://github.com/dominictarr)) 654 | - README: document `lt`, `lte`, `gt`, and `gte` ([**@dominictarr**](https://github.com/dominictarr)) 655 | - README: clarify ltgt ([**@dominictarr**](https://github.com/dominictarr)) 656 | - README: unmention bops ([**@dominictarr**](https://github.com/dominictarr)) 657 | - README: discourage the use of `start` and `end` a bit ([**@raboof**](https://github.com/raboof)) 658 | - README: document what `limit` does in reverse mode ([**@raboof**](https://github.com/raboof)) 659 | - README: use highest/lowest instead of largest/smallest ([**@raboof**](https://github.com/raboof)) 660 | - Binary encoding in the browser ([**@calvinmetcalf**](https://github.com/calvinmetcalf)) 661 | - Document code with comments ([**@dominictarr**](https://github.com/dominictarr)) 662 | - Minor style fixes ([**@rvagg**](https://github.com/rvagg)) 663 | - Minor whitespace changes ([**@rvagg**](https://github.com/rvagg)) 664 | - Update nodeico badge ([**@rvagg**](https://github.com/rvagg)) 665 | 666 | ### Fixed 667 | 668 | - Fix license ([**@rvagg**](https://github.com/rvagg)) 669 | 670 | ## [0.18.6] - 2014-07-26 671 | 672 | ### Changed 673 | 674 | - Change from MIT +no-false-attribs License to plain MIT ([**@andrewrk**](https://github.com/andrewrk)) 675 | - Upgrade `bl` dependency ([**@raynos**](https://github.com/raynos)) 676 | 677 | ## [0.18.5] - 2014-06-26 678 | 679 | ### Fixed 680 | 681 | - Replace `concat-stream` with `bl`, fixes [#251](https://github.com/Level/levelup/issues/251) ([**@rvagg**](https://github.com/rvagg)) 682 | 683 | ## [0.18.4] - 2014-06-24 684 | 685 | ### Changed 686 | 687 | - Reorder dependencies ([**@juliangruber**](https://github.com/juliangruber)) 688 | - Upgrade dependencies ([**@rvagg**](https://github.com/rvagg)) 689 | 690 | ### Fixed 691 | 692 | - Fix race condition on read stream's `self._iterator` ([**@nolanlawson**](https://github.com/nolanlawson)) 693 | 694 | ## [0.18.3] - 2014-04-26 695 | 696 | ### Changed 697 | 698 | - README: fix formatting ([**@rvagg**](https://github.com/rvagg)) 699 | - README: minor corrections ([**@guybrush**](https://github.com/guybrush)) 700 | - README: fix leveldown method wording ([**@juliangruber**](https://github.com/juliangruber)) 701 | - README: clarify `start`, `end` and `limit` options in `createReadStream` docs ([**@maxogden**](https://github.com/maxogden)) 702 | 703 | ### Removed 704 | 705 | - Remove `bops` and use `Buffer` instead ([**@nolanlawson**](https://github.com/nolanlawson)) 706 | 707 | ## [0.18.2] - 2013-11-26 708 | 709 | ### Added 710 | 711 | - Add `DNT` configuration ([**@rvagg**](https://github.com/rvagg)) 712 | 713 | ### Changed 714 | 715 | - Use `readable-stream` from user land across all node version ([**@rvagg**](https://github.com/rvagg)) 716 | 717 | ## [0.18.1] - 2013-11-20 718 | 719 | ### Changed 720 | 721 | - Make chained-batch obey global LevelUP object options ([**@mcavage**](https://github.com/mcavage)) 722 | 723 | ## [0.18.0] - 2013-11-18 724 | 725 | ### Changed 726 | 727 | - Upgrade to `leveldown@0.10.0` (and `bops@0.1.0` and `readable-stream@1.1.9`) ([**@rvagg**](https://github.com/rvagg)) 728 | 729 | ## [0.17.0] - 2013-10-01 730 | 731 | ### Changed 732 | 733 | - Undo factory pattern, use plain prototypal object and expose full prototype ([**@rvagg**](https://github.com/rvagg)) 734 | - Move Batch object to batch.js and expose ([**@rvagg**](https://github.com/rvagg)) 735 | - Use new package, DeferredLevelDOWN to handle all deferred open logic ([**@rvagg**](https://github.com/rvagg)) 736 | - Code cleanup, update deps (xtend) ([**@rvagg**](https://github.com/rvagg), [**@juliangruber**](https://github.com/juliangruber)) 737 | 738 | ## [0.16.0] - 2013-09-10 739 | 740 | ### Added 741 | 742 | - Add `notFound` boolean property and `status=404` property to NotFoundError ([**@rvagg**](https://github.com/rvagg)) 743 | 744 | ### Changed 745 | 746 | - Upgrade to `errno@0.1.0` which aliases `.type` and `.name` properties ([**@rvagg**](https://github.com/rvagg)) 747 | - ReadStream gracefully handles multiple `destroy()` calls ([**@mcollina**](https://github.com/mcollina)) 748 | 749 | ## [0.15.0] - 2013-08-26 750 | 751 | ### Added 752 | 753 | - Add [**@substack**](https://github.com/substack) as contributor 754 | 755 | ### Changed 756 | 757 | - New ReadStream: upgrade to streams2, remove all state-management cruft, remove fstream support ([**@substack**](https://github.com/substack)) 758 | - Upgrade LevelDOWN dependency to ~0.8.0 with Iterator lt/lte/gt/gte support and NAN as a dependency ([**@rvagg**](https://github.com/rvagg)) 759 | 760 | ## [0.14.0] - 2013-08-19 761 | 762 | ### Changed 763 | 764 | - Encodings overhaul, allow custom encoders/decoders for `keyEncoding` or `valueEncoding` ([**@dominictarr**](https://github.com/dominictarr)) 765 | 766 | ## [0.13.0] - 2013-08-11 767 | 768 | ### Changed 769 | 770 | - Upgrade LevelDOWN dependency version ~0.7.0 for Node 0.8->0.11 compatibility ([**@rvagg**](https://github.com/rvagg)) 771 | 772 | ## [0.12.0] - 2013-07-25 773 | 774 | ### Changed 775 | 776 | - Upgrade LevelDOWN dependency version ~0.6.2 ([**@rvagg**](https://github.com/rvagg)) 777 | 778 | ## [0.11.0] - 2013-07-17 779 | 780 | ### Added 781 | 782 | - Add [**@pgte**](https://github.com/pgte) as contributor 783 | 784 | ### Changed 785 | 786 | - Switch from direct Buffer access to bops for better browser compatibility ([**@juliangruber**](https://github.com/juliangruber)) 787 | - WriteStream#end accepts `data` argument ([**@pgte**](https://github.com/pgte)) 788 | 789 | ### Removed 790 | 791 | - Remove all Function#bind calls for better browser compatibility ([**@juliangruber**](https://github.com/juliangruber)) 792 | 793 | ## [0.10.0] - 2013-06-14 794 | 795 | ### Changed 796 | 797 | - Upgrade to `LevelDOWN@0.6.0` which upgrades to `LevelDB@1.11.0`, some important bugfixes: ([**@rvagg**](https://github.com/rvagg)) 798 | 799 | ## [0.9.0] - 2013-05-21 800 | 801 | ### Changed 802 | 803 | - Use `leveldown@0.5.0`, see for details ([**@rvagg**](https://github.com/rvagg)) 804 | - Race-condition(ish) fixed in ReadStream--createReadStream() does not start immediately and therefore allowed put()s to happen before the stream starts ([**@dominictarr**](https://github.com/dominictarr)) 805 | - ReadStream doesn't emit "ready" event ([**@dominictarr**](https://github.com/dominictarr)) 806 | - Allow separate encodings per operation in db.batch() ([**@juliangruber**](https://github.com/juliangruber)) 807 | - Allow separate encodings per write() in WriteStream ([**@juliangruber**](https://github.com/juliangruber)) 808 | - WriteStream supports "type" option ("put" \[default] or "del") on constructor and individual write()s ([**@mcollina**](https://github.com/mcollina)) 809 | - Expose LevelDOWN (or LevelDOWN substitute) as `db` property on LevelUP instance (e.g. db.db.approximateSize()) ([**@rvagg**](https://github.com/rvagg)) 810 | - Chained batch exposed from LevelDOWN, invoked with argument-less db.batch() ([**@juliangruber**](https://github.com/juliangruber), [**@rvagg**](https://github.com/rvagg)) 811 | - Significantly improve ReadStream performance by replacing .bind() and .apply() ([**@mcollina**](https://github.com/mcollina), [**@kesla**](https://github.com/kesla)) 812 | - Better Browserify support ([**@rvagg**](https://github.com/rvagg), [**@juliangruber**](https://github.com/juliangruber), [**@maxogden**](https://github.com/maxogden), etc.) 813 | - Deprecate secondary LevelDB-specific operations on LevelUP, prefer direct LevelDOWN access (approximateSize(), repair(), destroy(), getProperty()--new in `leveldown@0.5.0`) ([**@rvagg**](https://github.com/rvagg)) 814 | 815 | ### Removed 816 | 817 | - Remove "leveldown" from dependencies (see ) ([**@rvagg**](https://github.com/rvagg)) 818 | 819 | ## [0.8.0] - 2013-04-17 820 | 821 | ### Changed 822 | 823 | - More comprehensive argument checking, will now report back directly or throw if there is a problem rather than on nextTick ([**@rvagg**](https://github.com/rvagg)) 824 | - Expose `.options` property on LevelUP instances. ([**@rvagg**](https://github.com/rvagg)) 825 | - Further clarify 'encoding' -> 'valueEncoding' shift. db.options.valueEncoding is now authoritative even if user used 'encoding' on initialisation. ([**@rvagg**](https://github.com/rvagg)) 826 | - `level` package now published to npm that bundles `LevelUP` and `LevelDOWN` and exposes `LevelUP` directly; for planned shift to detaching LevelDOWN as a direct-dependency of LevelUP. ([**@rvagg**](https://github.com/rvagg)) 827 | 828 | ## [0.7.0] - 2013-04-08 829 | 830 | ### Added 831 | 832 | - Add windows support in `LevelDOWN@0.2.0` ([**@rvagg**](https://github.com/rvagg)) 833 | - Add 'db' option on constructor to replace LevelDOWN ([**@rvagg**](https://github.com/rvagg)) 834 | - Add `repair()` and `destroy()` aliases for LevelDOWN implementations ([**@rvagg**](https://github.com/rvagg)) 835 | 836 | ### Changed 837 | 838 | - Improved ReadStream reverse=true start key handling ([**@kesla**](https://github.com/kesla)) 839 | - ReadStream empty start and end keys ignored rather than segfault ([**@kesla**](https://github.com/kesla)) 840 | - 'encoding' option now an alias for `valueEncoding` only, `keyEncoding` defaults to `'utf8'` and must be changed explicitly ([**@rvagg**](https://github.com/rvagg)) 841 | 842 | ### Fixed 843 | 844 | - Fix early `close` emit in WriteStream ([**@rvagg**](https://github.com/rvagg)) 845 | 846 | ## [0.6.2] - 2013-03-04 847 | 848 | ### Changed 849 | 850 | - Use `xtend` package instead of internal `util._extend` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 851 | - Internal cleanup of `callback` argument detection ([**@ralphtheninja**](https://github.com/ralphtheninja)) 852 | - Move deferred-open-operations into an internal `this._db` wrapper rather than make them call public .get()/.put() etc. for a second time ([**@dominictarr**](https://github.com/dominictarr)) 853 | 854 | ## [0.6.1] - 2013-03-01 855 | 856 | ### Changed 857 | 858 | - Internal code cleanup and refactoring ([**@ralphtheninja**](https://github.com/ralphtheninja)) 859 | 860 | ### Fixed 861 | 862 | - Fix multiple `iterator.end()` calls in ReadStreams throwing errors, destroy() called while read/next is in progress [#82](https://github.com/level/levelup/issues/82) [#83](https://github.com/level/levelup/issues/83) [#84](https://github.com/level/levelup/issues/84) ([**@rvagg**](https://github.com/rvagg)) 863 | 864 | ## [0.6.0] - 2013-02-25 865 | 866 | ### Changed 867 | 868 | - Rename `ReadStream`, `KeyStream` and `ValueStream` to `createReadStream`, `createKeyStream` and `createValueStream` ([**@rvagg**](https://github.com/rvagg)) 869 | - Complete transition to `LevelDOWN` for the `LevelDB` binding. No native code left in `LevelUP` ([**@rvagg**](https://github.com/rvagg)) 870 | - LevelDOWN now keeps its own ChangeLog at: 871 | - `LevelDB@1.9.0` and `Snappy@1.1.0` are included in `leveldown@0.1.2` 872 | 873 | ## [0.6.0-rc1] - 2013-02-24 874 | 875 | ### Changed 876 | 877 | - Refactor and simplify db state code ([**@ralphtheninja**](https://github.com/ralphtheninja)) 878 | - Extract all binding code to `leveldown` project ([**@rvagg**](https://github.com/rvagg)) 879 | - Depend on `leveldown@0.0.1` ([**@rvagg**](https://github.com/rvagg)) 880 | - Simplify callback signature by removing extra, undocumented properties from some callbacks ([**@rvagg**](https://github.com/rvagg), [**@dominictarr**](https://github.com/dominictarr)) 881 | 882 | ## [0.5.4] - 2013-02-15 883 | 884 | ### Changed 885 | 886 | - Move `encodingOpts` from `levelup.js` to `util.js` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 887 | - Allow one `next()` at a time, improve `end()` handling ([**@rvagg**](https://github.com/rvagg)) 888 | - Use explicit namespaces in C++ ([**@rvagg**](https://github.com/rvagg)) 889 | 890 | ### Removed 891 | 892 | - Remove `CloseError` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 893 | - Remove `.useBatch` in `copy()` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 894 | - Ensure iterator `end` and `next` don't conflict ([**@rvagg**](https://github.com/rvagg)) 895 | 896 | ### Fixed 897 | 898 | - Fix `put`/`batch` bug in `WriteStream#_process()` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 899 | - Fix memory leak, `Persistent` callback not Dispose()d for `readStream()` ([**@rvagg**](https://github.com/rvagg)) 900 | 901 | ## [0.5.3] - 2013-01-28 902 | 903 | ### Changed 904 | 905 | - Disable all sqlite3 benchmarks ([**@ralphtheninja**](https://github.com/ralphtheninja)) 906 | - Put `LevelUP()` into closure ([**@ralphtheninja**](https://github.com/ralphtheninja)) 907 | - Swap `bufferstream` dependency for `simple-bufferstream` ([**@rvagg**](https://github.com/rvagg)) 908 | - Make `location` a read-only property on db object ([**@rvagg**](https://github.com/rvagg)) 909 | 910 | ## [0.5.3-1] - 2013-02-05 911 | 912 | ### Changed 913 | 914 | - Non shrinkwrapped release [**@rvagg**](https://github.com/rvagg) 915 | 916 | ## [0.5.2] - 2013-01-23 917 | 918 | ### Fixed 919 | 920 | - Fix incorrect scope in approximateSize function ([**@sandfox**](https://github.com/sandfox)) 921 | 922 | ## [0.5.1] - 2013-01-10 923 | 924 | ### Changed 925 | 926 | - Version bump ([**@rvagg**](https://github.com/rvagg)) 927 | 928 | ## [0.5.0] - 2013-01-08 929 | 930 | ### Added 931 | 932 | - Add support for setting size of LRU-cache ([**@kesla**](https://github.com/kesla)) 933 | 934 | ### Changed 935 | 936 | - Use `util.inherits()` from node core ([**@ralphtheninja**](https://github.com/ralphtheninja)) 937 | - Adjust copyright & contributors ([**@rvagg**](https://github.com/rvagg)) 938 | 939 | ### Fixed 940 | 941 | - Idempotent open and close, and emit \_state as events ([**@dominictarr**](https://github.com/dominictarr)) 942 | - Check that UINT32_OPTION_VALUE is a Uint32 ([**@kesla**](https://github.com/kesla)) 943 | 944 | ## [0.5.0-1] - 2013-01-09 945 | 946 | ### Changed 947 | 948 | - Change `createIfMissing` option default to `true` ([**@rvagg**](https://github.com/rvagg)) 949 | - Use `util._extend` instead of local variant ([**@rvagg**](https://github.com/rvagg)) 950 | 951 | ## [0.4.4] - 2013-01-01 952 | 953 | ### Fixed 954 | 955 | - Set `.maxListeners` to `Infinity` to prevent warnings when using deferred open ([**@juliangruber**](https://github.com/juliangruber)) 956 | 957 | ## [0.4.3] - 2012-12-30 958 | 959 | ### Added 960 | 961 | - Add [**@kesla**](https://github.com/kesla) to contributors list ([**@rvagg**](https://github.com/rvagg)) 962 | - Add `approximateSize()` ([**@kesla**](https://github.com/kesla)) 963 | 964 | ## [0.4.2] - 2012-12-30 965 | 966 | ### Added 967 | 968 | - Add [**@ralphtheninja**](https://github.com/ralphtheninja) to contributors list ([**@rvagg**](https://github.com/rvagg)) 969 | 970 | ### Fixed 971 | 972 | - Use `setImmediate` instead of `process.nextTick` for `node@0.9.5` compatibility ([**@rvagg**](https://github.com/rvagg)) 973 | 974 | ## [0.4.1] - 2012-12-19 975 | 976 | ### Removed 977 | 978 | - Remove `useBatch` option on `writeStream()` [**@rvagg**](https://github.com/rvagg) 979 | 980 | ## [0.4.0] - 2012-12-17 981 | 982 | ### Added 983 | 984 | - Add SQLite3 to test suite ([**@rvagg**](https://github.com/rvagg)) 985 | - Add basic `get()` benchmarks ([**@rvagg**](https://github.com/rvagg)) 986 | - Add `compress` boolean on `open()` ([**@rvagg**](https://github.com/rvagg)) 987 | 988 | ### Changed 989 | 990 | - Speed up `batch()` and allow non-Strings to C++ ([**@rvagg**](https://github.com/rvagg)) 991 | - Improved compression test ([**@rvagg**](https://github.com/rvagg)) 992 | - Return Strings not Buffers from C++ when possible ([**@rvagg**](https://github.com/rvagg)) 993 | - Optimised encoders and decoders ([**@rvagg**](https://github.com/rvagg)) 994 | - Revamped benchmark suite ([**@rvagg**](https://github.com/rvagg)) 995 | - Allow JS Strings through to native layer ([**@rvagg**](https://github.com/rvagg)) 996 | - Cleaner build for osx ([**@rvagg**](https://github.com/rvagg)) 997 | - Upgrade to `LevelDB@1.7` ([**@rvagg**](https://github.com/rvagg)) 998 | 999 | ### Removed 1000 | 1001 | - Remove old and unused util functions ([**@rvagg**](https://github.com/rvagg)) 1002 | - Remove compile warnings on osx ([**@rvagg**](https://github.com/rvagg)) 1003 | - Remove compile warnings for solaris ([**@rvagg**](https://github.com/rvagg)) 1004 | 1005 | ### Fixed 1006 | 1007 | - Fix `batch()` benchmarks ([**@rvagg**](https://github.com/rvagg)) 1008 | 1009 | ## [0.3.3] - 2012-12-14 1010 | 1011 | ### Added 1012 | 1013 | - Add compression tests ([**@rvagg**](https://github.com/rvagg)) 1014 | 1015 | ### Fixed 1016 | 1017 | - Fix Snappy compression ([**@rvagg**](https://github.com/rvagg)) 1018 | 1019 | ## [0.3.2] - 2012-11-24 1020 | 1021 | ### Added 1022 | 1023 | - Add more functional tests ([**@rvagg**](https://github.com/rvagg)) 1024 | - Add snapshot tests ([**@rvagg**](https://github.com/rvagg)) 1025 | 1026 | ### Changed 1027 | 1028 | - Emit raw keys and values in events ([**@rvagg**](https://github.com/rvagg)) 1029 | 1030 | ## [0.3.1] - 2012-11-21 1031 | 1032 | ### Added 1033 | 1034 | - Add benchmark suite ([**@rvagg**](https://github.com/rvagg)) 1035 | - Add `limit` option to `ReadStream` ([**@rvagg**](https://github.com/rvagg)) 1036 | 1037 | ## [0.3.0] - 2012-11-18 1038 | 1039 | ### Added 1040 | 1041 | - Add `.status` property to keep track of db status ([**@raynos**](https://github.com/raynos), [**@rvagg**](https://github.com/rvagg)) 1042 | - Add `CloseError` error type ([**@raynos**](https://github.com/raynos), [**@rvagg**](https://github.com/rvagg)) 1043 | - Add tests for deferred operations ([**@rvagg**](https://github.com/rvagg)) 1044 | 1045 | ### Changed 1046 | 1047 | - Document events ([**@rvagg**](https://github.com/rvagg)) 1048 | - Run the encoding on `start` and `end` in case your keys are JSON encoded ([**@raynos**](https://github.com/raynos)) 1049 | - First attempt at deferring operations. All operations that used to throw when called before open are now called once the database is open ([**@raynos**](https://github.com/raynos), [**@rvagg**](https://github.com/rvagg)) 1050 | 1051 | ### Fixed 1052 | 1053 | - If status is `'closing'`, call callback after db is closed ([**@raynos**](https://github.com/raynos), [**@rvagg**](https://github.com/rvagg)) 1054 | 1055 | ## [0.2.1] - 2012-10-28 1056 | 1057 | ### Fixed 1058 | 1059 | - Fix db GC when using multiple `ReadStream` ([**@rvagg**](https://github.com/rvagg)) 1060 | 1061 | ## [0.2.0] - 2012-10-28 1062 | 1063 | ### Added 1064 | 1065 | - Add support for Solaris/SunOS/SmartOS ([**@rvagg**](https://github.com/rvagg)) 1066 | 1067 | ## [0.1.2] - 2012-10-26 1068 | 1069 | ### Fixed 1070 | 1071 | - Fix bug with falsey values on `start` and `end`, fixes [#8](https://github.com/Level/levelup/issues/8) ([**@rvagg**](https://github.com/rvagg)) 1072 | 1073 | ## [0.1.1] - 2012-10-17 1074 | 1075 | ### Fixed 1076 | 1077 | - Fix bug with sticky options, fixes [#6](https://github.com/Level/levelup/issues/6) ([**@rvagg**](https://github.com/rvagg)) 1078 | 1079 | ## [0.1.0] - 2012-09-28 1080 | 1081 | ### Added 1082 | 1083 | - Add Travis setup ([**@rvagg**](https://github.com/rvagg)) 1084 | - Add `KeyStream()` and `ValueStream()` ([**@rvagg**](https://github.com/rvagg)) 1085 | 1086 | ## [0.0.5] - 2012-09-22 1087 | 1088 | ### Changed 1089 | 1090 | - Native layer errors if `key` or `value` are `undefined` or `null` ([**@rvagg**](https://github.com/rvagg)) 1091 | 1092 | ## [0.0.5-1] - 2012-09-28 1093 | 1094 | ### Added 1095 | 1096 | - Add description to `package.json` ([**@rvagg**](https://github.com/rvagg)) 1097 | 1098 | ## [0.0.4] - 2012-09-12 1099 | 1100 | ### Fixed 1101 | 1102 | - Fix bug with `options` not being passed to readable streams ([**@rvagg**](https://github.com/rvagg)) 1103 | 1104 | ## [0.0.3] - 2012-09-09 1105 | 1106 | ### Added 1107 | 1108 | - Add `reverse` functionality to readable streams ([**@rvagg**](https://github.com/rvagg)) 1109 | 1110 | ## [0.0.2] - 2012-09-07 1111 | 1112 | ### Changed 1113 | 1114 | - Do not encourage using async `throw` in documentation ([**@rvagg**](https://github.com/rvagg)) 1115 | - Return to classical prototypal inheritance ([**@rvagg**](https://github.com/rvagg)) 1116 | 1117 | ### Fixed 1118 | 1119 | - Fix typos in documentation ([**@rvagg**](https://github.com/rvagg)) 1120 | 1121 | ## [0.0.2-1] - 2012-09-07 1122 | 1123 | ### Added 1124 | 1125 | - Add repository information to `package.json` ([**@rvagg**](https://github.com/rvagg)) 1126 | 1127 | ## [0.0.1] - 2012-08-31 1128 | 1129 | ### Added 1130 | 1131 | - Add `start` and `end` options for readable streams ([**@rvagg**](https://github.com/rvagg)) 1132 | - Add `'json'` encoding ([**@rvagg**](https://github.com/rvagg)) 1133 | - Add `.nextLocation()`, `.checkBinaryTestData()`, `.loadBinaryTestData()`, `.openTestDatabase()`, `.commonTearDown()`, `.commonSetup()` and `.binaryTestDataMD5Sum` to `test/common.js` ([**@rvagg**](https://github.com/rvagg)) 1134 | - Add tests for `.readStream()` with `start` being midway key ([**@rvagg**](https://github.com/rvagg)) 1135 | - Add keywords to `package.json` ([**@rvagg**](https://github.com/rvagg)) 1136 | 1137 | ### Changed 1138 | 1139 | - New API. Database constructor now accepts callback ([**@rvagg**](https://github.com/rvagg)) 1140 | - Update documentation for new API ([**@rvagg**](https://github.com/rvagg)) 1141 | 1142 | ### Removed 1143 | 1144 | - Remove usage of `global` in tests ([**@rvagg**](https://github.com/rvagg)) 1145 | 1146 | ## [0.0.0] - 2012-08-17 1147 | 1148 | :seedling: Initial release. 1149 | 1150 | ## [0.0.0-1] - 2012-08-18 1151 | 1152 | ### Added 1153 | 1154 | - Add `bufferstream` dependency ([**@rvagg**](https://github.com/rvagg)) 1155 | 1156 | ### Changed 1157 | 1158 | - Document `ReadStream` and `WriteStream` ([**@rvagg**](https://github.com/rvagg)) 1159 | - Start using `~` in dependencies ([**@rvagg**](https://github.com/rvagg)) 1160 | 1161 | ### Removed 1162 | 1163 | - Remove unused `inherits` variable ([**@rvagg**](https://github.com/rvagg)) 1164 | 1165 | [5.1.1]: https://github.com/Level/levelup/releases/tag/v5.1.1 1166 | 1167 | [5.1.0]: https://github.com/Level/levelup/releases/tag/v5.1.0 1168 | 1169 | [5.0.1]: https://github.com/Level/levelup/releases/tag/v5.0.1 1170 | 1171 | [5.0.0]: https://github.com/Level/levelup/releases/tag/v5.0.0 1172 | 1173 | [4.4.0]: https://github.com/Level/levelup/releases/tag/v4.4.0 1174 | 1175 | [4.3.2]: https://github.com/Level/levelup/releases/tag/v4.3.2 1176 | 1177 | [4.3.1]: https://github.com/Level/levelup/releases/tag/v4.3.1 1178 | 1179 | [4.3.0]: https://github.com/Level/levelup/releases/tag/v4.3.0 1180 | 1181 | [4.2.0]: https://github.com/Level/levelup/releases/tag/v4.2.0 1182 | 1183 | [4.1.0]: https://github.com/Level/levelup/releases/tag/v4.1.0 1184 | 1185 | [4.0.2]: https://github.com/Level/levelup/releases/tag/v4.0.2 1186 | 1187 | [4.0.1]: https://github.com/Level/levelup/releases/tag/v4.0.1 1188 | 1189 | [4.0.0]: https://github.com/Level/levelup/releases/tag/v4.0.0 1190 | 1191 | [3.1.1]: https://github.com/Level/levelup/releases/tag/v3.1.1 1192 | 1193 | [3.1.0]: https://github.com/Level/levelup/releases/tag/v3.1.0 1194 | 1195 | [3.0.1]: https://github.com/Level/levelup/releases/tag/v3.0.1 1196 | 1197 | [3.0.0]: https://github.com/Level/levelup/releases/tag/v3.0.0 1198 | 1199 | [2.0.2]: https://github.com/Level/levelup/releases/tag/v2.0.2 1200 | 1201 | [2.0.1]: https://github.com/Level/levelup/releases/tag/v2.0.1 1202 | 1203 | [2.0.0]: https://github.com/Level/levelup/releases/tag/v2.0.0 1204 | 1205 | [2.0.0-rc3]: https://github.com/Level/levelup/releases/tag/v2.0.0-rc3 1206 | 1207 | [2.0.0-rc2]: https://github.com/Level/levelup/releases/tag/v2.0.0-rc2 1208 | 1209 | [2.0.0-rc1]: https://github.com/Level/levelup/releases/tag/v2.0.0-rc1 1210 | 1211 | [1.3.9]: https://github.com/Level/levelup/releases/tag/v1.3.9 1212 | 1213 | [1.3.8]: https://github.com/Level/levelup/releases/tag/v1.3.8 1214 | 1215 | [1.3.7]: https://github.com/Level/levelup/releases/tag/v1.3.7 1216 | 1217 | [1.3.6]: https://github.com/Level/levelup/releases/tag/v1.3.6 1218 | 1219 | [1.3.5]: https://github.com/Level/levelup/releases/tag/v1.3.5 1220 | 1221 | [1.3.4]: https://github.com/Level/levelup/releases/tag/v1.3.4 1222 | 1223 | [1.3.3]: https://github.com/Level/levelup/releases/tag/v1.3.3 1224 | 1225 | [1.3.2]: https://github.com/Level/levelup/releases/tag/v1.3.2 1226 | 1227 | [1.3.1]: https://github.com/Level/levelup/releases/tag/v1.3.1 1228 | 1229 | [1.3.0]: https://github.com/Level/levelup/releases/tag/v1.3.0 1230 | 1231 | [1.2.1]: https://github.com/Level/levelup/releases/tag/v1.2.1 1232 | 1233 | [1.2.0]: https://github.com/Level/levelup/releases/tag/v1.2.0 1234 | 1235 | [1.1.1]: https://github.com/Level/levelup/releases/tag/v1.1.1 1236 | 1237 | [1.1.0]: https://github.com/Level/levelup/releases/tag/v1.1.0 1238 | 1239 | [1.0.0]: https://github.com/Level/levelup/releases/tag/v1.0.0 1240 | 1241 | [1.0.0-5]: https://github.com/Level/levelup/releases/tag/v1.0.0-5 1242 | 1243 | [1.0.0-4]: https://github.com/Level/levelup/releases/tag/v1.0.0-4 1244 | 1245 | [1.0.0-3]: https://github.com/Level/levelup/releases/tag/v1.0.0-3 1246 | 1247 | [1.0.0-2]: https://github.com/Level/levelup/releases/tag/v1.0.0-2 1248 | 1249 | [1.0.0-1]: https://github.com/Level/levelup/releases/tag/v1.0.0-1 1250 | 1251 | [1.0.0-0]: https://github.com/Level/levelup/releases/tag/v1.0.0-0 1252 | 1253 | [0.19.1]: https://github.com/Level/levelup/releases/tag/v0.19.1 1254 | 1255 | [0.19.0]: https://github.com/Level/levelup/releases/tag/v0.19.0 1256 | 1257 | [0.18.6]: https://github.com/Level/levelup/releases/tag/v0.18.6 1258 | 1259 | [0.18.5]: https://github.com/Level/levelup/releases/tag/v0.18.5 1260 | 1261 | [0.18.4]: https://github.com/Level/levelup/releases/tag/v0.18.4 1262 | 1263 | [0.18.3]: https://github.com/Level/levelup/releases/tag/v0.18.3 1264 | 1265 | [0.18.2]: https://github.com/Level/levelup/releases/tag/v0.18.2 1266 | 1267 | [0.18.1]: https://github.com/Level/levelup/releases/tag/v0.18.1 1268 | 1269 | [0.18.0]: https://github.com/Level/levelup/releases/tag/0.18.0 1270 | 1271 | [0.17.0]: https://github.com/Level/levelup/releases/tag/0.17.0 1272 | 1273 | [0.16.0]: https://github.com/Level/levelup/releases/tag/0.16.0 1274 | 1275 | [0.15.0]: https://github.com/Level/levelup/releases/tag/0.15.0 1276 | 1277 | [0.14.0]: https://github.com/Level/levelup/releases/tag/0.14.0 1278 | 1279 | [0.13.0]: https://github.com/Level/levelup/releases/tag/0.13.0 1280 | 1281 | [0.12.0]: https://github.com/Level/levelup/releases/tag/0.12.0 1282 | 1283 | [0.11.0]: https://github.com/Level/levelup/releases/tag/0.11.0 1284 | 1285 | [0.10.0]: https://github.com/Level/levelup/releases/tag/0.10.0 1286 | 1287 | [0.9.0]: https://github.com/Level/levelup/releases/tag/0.9.0 1288 | 1289 | [0.8.0]: https://github.com/Level/levelup/releases/tag/0.8.0 1290 | 1291 | [0.7.0]: https://github.com/Level/levelup/releases/tag/0.7.0 1292 | 1293 | [0.6.2]: https://github.com/Level/levelup/releases/tag/0.6.2 1294 | 1295 | [0.6.1]: https://github.com/Level/levelup/releases/tag/0.6.1 1296 | 1297 | [0.6.0]: https://github.com/Level/levelup/releases/tag/0.6.0 1298 | 1299 | [0.6.0-rc1]: https://github.com/Level/levelup/releases/tag/0.6.0-rc1 1300 | 1301 | [0.5.4]: https://github.com/Level/levelup/releases/tag/0.5.4 1302 | 1303 | [0.5.3]: https://github.com/Level/levelup/releases/tag/0.5.3 1304 | 1305 | [0.5.3-1]: https://github.com/Level/levelup/releases/tag/0.5.3-1 1306 | 1307 | [0.5.2]: https://github.com/Level/levelup/releases/tag/0.5.2 1308 | 1309 | [0.5.1]: https://github.com/Level/levelup/releases/tag/0.5.1 1310 | 1311 | [0.5.0]: https://github.com/Level/levelup/releases/tag/0.5.0 1312 | 1313 | [0.5.0-1]: https://github.com/Level/levelup/releases/tag/0.5.0-1 1314 | 1315 | [0.4.4]: https://github.com/Level/levelup/releases/tag/0.4.4 1316 | 1317 | [0.4.3]: https://github.com/Level/levelup/releases/tag/0.4.3 1318 | 1319 | [0.4.2]: https://github.com/Level/levelup/releases/tag/0.4.2 1320 | 1321 | [0.4.1]: https://github.com/Level/levelup/releases/tag/0.4.1 1322 | 1323 | [0.4.0]: https://github.com/Level/levelup/releases/tag/0.4.0 1324 | 1325 | [0.3.3]: https://github.com/Level/levelup/releases/tag/0.3.3 1326 | 1327 | [0.3.2]: https://github.com/Level/levelup/releases/tag/0.3.2 1328 | 1329 | [0.3.1]: https://github.com/Level/levelup/releases/tag/0.3.1 1330 | 1331 | [0.3.0]: https://github.com/Level/levelup/releases/tag/0.3.0 1332 | 1333 | [0.2.1]: https://github.com/Level/levelup/releases/tag/0.2.1 1334 | 1335 | [0.2.0]: https://github.com/Level/levelup/releases/tag/0.2.0 1336 | 1337 | [0.1.2]: https://github.com/Level/levelup/releases/tag/0.1.2 1338 | 1339 | [0.1.1]: https://github.com/Level/levelup/releases/tag/0.1.1 1340 | 1341 | [0.1.0]: https://github.com/Level/levelup/releases/tag/0.1.0 1342 | 1343 | [0.0.5]: https://github.com/Level/levelup/releases/tag/0.0.5 1344 | 1345 | [0.0.5-1]: https://github.com/Level/levelup/releases/tag/0.0.5-1 1346 | 1347 | [0.0.4]: https://github.com/Level/levelup/releases/tag/0.0.4 1348 | 1349 | [0.0.3]: https://github.com/Level/levelup/releases/tag/0.0.3 1350 | 1351 | [0.0.2]: https://github.com/Level/levelup/releases/tag/0.0.2 1352 | 1353 | [0.0.2-1]: https://github.com/Level/levelup/releases/tag/0.0.2-1 1354 | 1355 | [0.0.1]: https://github.com/Level/levelup/releases/tag/0.0.1 1356 | 1357 | [0.0.0]: https://github.com/Level/levelup/releases/tag/0.0.0 1358 | 1359 | [0.0.0-1]: https://github.com/Level/levelup/releases/tag/0.0.0-1 1360 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2012 The contributors to levelup. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # levelup 2 | 3 | **Superseded by [`abstract-level`](https://github.com/Level/abstract-level). Please see [Frequently Asked Questions](https://github.com/Level/community#faq).** 4 | 5 | ## Table of Contents 6 | 7 |
Click to expand 8 | 9 | - [Introduction](#introduction) 10 | - [Supported Platforms](#supported-platforms) 11 | - [Usage](#usage) 12 | - [API](#api) 13 | - [`levelup(db[, options[, callback]])`](#levelupdb-options-callback) 14 | - [`db.supports`](#dbsupports) 15 | - [`db.open([options][, callback])`](#dbopenoptions-callback) 16 | - [`db.close([callback])`](#dbclosecallback) 17 | - [`db.put(key, value[, options][, callback])`](#dbputkey-value-options-callback) 18 | - [`db.get(key[, options][, callback])`](#dbgetkey-options-callback) 19 | - [`db.getMany(keys[, options][, callback])`](#dbgetmanykeys-options-callback) 20 | - [`db.del(key[, options][, callback])`](#dbdelkey-options-callback) 21 | - [`db.batch(array[, options][, callback])` _(array form)_](#dbbatcharray-options-callback-array-form) 22 | - [`db.batch()` _(chained form)_](#dbbatch-chained-form) 23 | - [`db.status`](#dbstatus) 24 | - [`db.isOperational()`](#dbisoperational) 25 | - [`db.createReadStream([options])`](#dbcreatereadstreamoptions) 26 | - [`db.createKeyStream([options])`](#dbcreatekeystreamoptions) 27 | - [`db.createValueStream([options])`](#dbcreatevaluestreamoptions) 28 | - [`db.iterator([options])`](#dbiteratoroptions) 29 | - [`db.clear([options][, callback])`](#dbclearoptions-callback) 30 | - [What happened to `db.createWriteStream`?](#what-happened-to-dbcreatewritestream) 31 | - [Promise Support](#promise-support) 32 | - [Events](#events) 33 | - [Multi-process Access](#multi-process-access) 34 | - [Contributing](#contributing) 35 | - [Big Thanks](#big-thanks) 36 | - [Donate](#donate) 37 | - [License](#license) 38 | 39 |
40 | 41 | ## Introduction 42 | 43 | **Fast and simple storage. A Node.js wrapper for `abstract-leveldown` compliant stores, which follow the characteristics of [LevelDB](https://github.com/google/leveldb).** 44 | 45 | LevelDB is a simple key-value store built by Google. It's used in Google Chrome and many other products. LevelDB supports arbitrary byte arrays as both keys and values, singular _get_, _put_ and _delete_ operations, _batched put and delete_, bi-directional iterators and simple compression using the very fast [Snappy](http://google.github.io/snappy/) algorithm. 46 | 47 | LevelDB stores entries sorted lexicographically by keys. This makes the streaming interface of `levelup` - which exposes LevelDB iterators as [Readable Streams](https://nodejs.org/docs/latest/api/stream.html#stream_readable_streams) - a very powerful query mechanism. 48 | 49 | The most common store is [`leveldown`](https://github.com/Level/leveldown/) which provides a pure C++ binding to LevelDB. [Many alternative stores are available](https://github.com/Level/awesome/#stores) such as [`level.js`](https://github.com/Level/level.js) in the browser or [`memdown`](https://github.com/Level/memdown) for an in-memory store. They typically support strings and Buffers for both keys and values. For a richer set of data types you can wrap the store with [`encoding-down`](https://github.com/Level/encoding-down). 50 | 51 | **The [`level`](https://github.com/Level/level) package is the recommended way to get started.** It conveniently bundles `levelup`, [`leveldown`](https://github.com/Level/leveldown/) and [`encoding-down`](https://github.com/Level/encoding-down). Its main export is `levelup` - i.e. you can do `var db = require('level')`. 52 | 53 | ## Supported Platforms 54 | 55 | We aim to support Active LTS and Current Node.js releases as well as browsers. For support of the underlying store, please see the respective documentation. 56 | 57 | ## Usage 58 | 59 | _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._ 60 | 61 | First you need to install `levelup`! No stores are included so you must also install `leveldown` (for example). 62 | 63 | ```sh 64 | $ npm install levelup leveldown 65 | ``` 66 | 67 | All operations are asynchronous. If you do not provide a callback, [a Promise is returned](#promise-support). 68 | 69 | ```js 70 | var levelup = require('levelup') 71 | var leveldown = require('leveldown') 72 | 73 | // 1) Create our store 74 | var db = levelup(leveldown('./mydb')) 75 | 76 | // 2) Put a key & value 77 | db.put('name', 'levelup', function (err) { 78 | if (err) return console.log('Ooops!', err) // some kind of I/O error 79 | 80 | // 3) Fetch by key 81 | db.get('name', function (err, value) { 82 | if (err) return console.log('Ooops!', err) // likely the key was not found 83 | 84 | // Ta da! 85 | console.log('name=' + value) 86 | }) 87 | }) 88 | ``` 89 | 90 | ## API 91 | 92 | ### `levelup(db[, options[, callback]])` 93 | 94 | The main entry point for creating a new `levelup` instance. 95 | 96 | - `db` must be an [`abstract-leveldown`](https://github.com/Level/abstract-leveldown) compliant store. 97 | - `options` is passed on to the underlying store when opened and is specific to the type of store being used 98 | 99 | Calling `levelup(db)` will also open the underlying store. This is an asynchronous operation which will trigger your callback if you provide one. The callback should take the form `function (err, db) {}` where `db` is the `levelup` instance. If you don't provide a callback, any read & write operations are simply queued internally until the store is fully opened, unless it fails to open, in which case an `error` event will be emitted. 100 | 101 | This leads to two alternative ways of managing a `levelup` instance: 102 | 103 | ```js 104 | levelup(leveldown(location), options, function (err, db) { 105 | if (err) throw err 106 | 107 | db.get('foo', function (err, value) { 108 | if (err) return console.log('foo does not exist') 109 | console.log('got foo =', value) 110 | }) 111 | }) 112 | ``` 113 | 114 | Versus the equivalent: 115 | 116 | ```js 117 | // Will throw if an error occurs 118 | var db = levelup(leveldown(location), options) 119 | 120 | db.get('foo', function (err, value) { 121 | if (err) return console.log('foo does not exist') 122 | console.log('got foo =', value) 123 | }) 124 | ``` 125 | 126 | ### `db.supports` 127 | 128 | A read-only [manifest](https://github.com/Level/supports). Might be used like so: 129 | 130 | ```js 131 | if (!db.supports.permanence) { 132 | throw new Error('Persistent storage is required') 133 | } 134 | 135 | if (db.supports.bufferKeys && db.supports.promises) { 136 | await db.put(Buffer.from('key'), 'value') 137 | } 138 | ``` 139 | 140 | ### `db.open([options][, callback])` 141 | 142 | Opens the underlying store. In general you shouldn't need to call this method directly as it's automatically called by [`levelup()`](#levelupdb-options-callback). However, it is possible to reopen the store after it has been closed with [`close()`](#dbclosecallback). 143 | 144 | If no callback is passed, a promise is returned. 145 | 146 | ### `db.close([callback])` 147 | 148 | `close()` closes the underlying store. The callback will receive any error encountered during closing as the first argument. 149 | 150 | You should always clean up your `levelup` instance by calling `close()` when you no longer need it to free up resources. A store cannot be opened by multiple instances of `levelup` simultaneously. 151 | 152 | If no callback is passed, a promise is returned. 153 | 154 | ### `db.put(key, value[, options][, callback])` 155 | 156 | `put()` is the primary method for inserting data into the store. Both `key` and `value` can be of any type as far as `levelup` is concerned. 157 | 158 | `options` is passed on to the underlying store. 159 | 160 | If no callback is passed, a promise is returned. 161 | 162 | ### `db.get(key[, options][, callback])` 163 | 164 | Get a value from the store by `key`. The `key` can be of any type. If it doesn't exist in the store then the callback or promise will receive an error. A not-found err object will be of type `'NotFoundError'` so you can `err.type == 'NotFoundError'` or you can perform a truthy test on the property `err.notFound`. 165 | 166 | ```js 167 | db.get('foo', function (err, value) { 168 | if (err) { 169 | if (err.notFound) { 170 | // handle a 'NotFoundError' here 171 | return 172 | } 173 | // I/O or other error, pass it up the callback chain 174 | return callback(err) 175 | } 176 | 177 | // .. handle `value` here 178 | }) 179 | ``` 180 | 181 | The optional `options` object is passed on to the underlying store. 182 | 183 | If no callback is passed, a promise is returned. 184 | 185 | ### `db.getMany(keys[, options][, callback])` 186 | 187 | Get multiple values from the store by an array of `keys`. The optional `options` object is passed on to the underlying store. 188 | 189 | The `callback` function will be called with an `Error` if the operation failed for any reason. If successful the first argument will be `null` and the second argument will be an array of values with the same order as `keys`. If a key was not found, the relevant value will be `undefined`. 190 | 191 | If no callback is provided, a promise is returned. 192 | 193 | ### `db.del(key[, options][, callback])` 194 | 195 | `del()` is the primary method for removing data from the store. 196 | 197 | ```js 198 | db.del('foo', function (err) { 199 | if (err) 200 | // handle I/O or other error 201 | }); 202 | ``` 203 | 204 | `options` is passed on to the underlying store. 205 | 206 | If no callback is passed, a promise is returned. 207 | 208 | ### `db.batch(array[, options][, callback])` _(array form)_ 209 | 210 | `batch()` can be used for very fast bulk-write operations (both _put_ and _delete_). The `array` argument should contain a list of operations to be executed sequentially, although as a whole they are performed as an atomic operation inside the underlying store. 211 | 212 | Each operation is contained in an object having the following properties: `type`, `key`, `value`, where the _type_ is either `'put'` or `'del'`. In the case of `'del'` the `value` property is ignored. Any entries with a `key` of `null` or `undefined` will cause an error to be returned on the `callback` and any `type: 'put'` entry with a `value` of `null` or `undefined` will return an error. 213 | 214 | ```js 215 | const ops = [ 216 | { type: 'del', key: 'father' }, 217 | { type: 'put', key: 'name', value: 'Yuri Irsenovich Kim' }, 218 | { type: 'put', key: 'dob', value: '16 February 1941' }, 219 | { type: 'put', key: 'spouse', value: 'Kim Young-sook' }, 220 | { type: 'put', key: 'occupation', value: 'Clown' } 221 | ] 222 | 223 | db.batch(ops, function (err) { 224 | if (err) return console.log('Ooops!', err) 225 | console.log('Great success dear leader!') 226 | }) 227 | ``` 228 | 229 | `options` is passed on to the underlying store. 230 | 231 | If no callback is passed, a promise is returned. 232 | 233 | ### `db.batch()` _(chained form)_ 234 | 235 | `batch()`, when called with no arguments will return a `Batch` object which can be used to build, and eventually commit, an atomic batch operation. Depending on how it's used, it is possible to obtain greater performance when using the chained form of `batch()` over the array form. 236 | 237 | ```js 238 | db.batch() 239 | .del('father') 240 | .put('name', 'Yuri Irsenovich Kim') 241 | .put('dob', '16 February 1941') 242 | .put('spouse', 'Kim Young-sook') 243 | .put('occupation', 'Clown') 244 | .write(function () { console.log('Done!') }) 245 | ``` 246 | 247 | **`batch.put(key, value[, options])`** 248 | 249 | Queue a _put_ operation on the current batch, not committed until a `write()` is called on the batch. The `options` argument, if provided, must be an object and is passed on to the underlying store. 250 | 251 | This method may `throw` a `WriteError` if there is a problem with your put (such as the `value` being `null` or `undefined`). 252 | 253 | **`batch.del(key[, options])`** 254 | 255 | Queue a _del_ operation on the current batch, not committed until a `write()` is called on the batch. The `options` argument, if provided, must be an object and is passed on to the underlying store. 256 | 257 | This method may `throw` a `WriteError` if there is a problem with your delete. 258 | 259 | **`batch.clear()`** 260 | 261 | Clear all queued operations on the current batch, any previous operations will be discarded. 262 | 263 | **`batch.length`** 264 | 265 | The number of queued operations on the current batch. 266 | 267 | **`batch.write([options][, callback])`** 268 | 269 | Commit the queued operations for this batch. All operations not _cleared_ will be written to the underlying store atomically, that is, they will either all succeed or fail with no partial commits. 270 | 271 | The optional `options` object is passed to the `.write()` operation of the underlying batch object. 272 | 273 | If no callback is passed, a promise is returned. 274 | 275 | ### `db.status` 276 | 277 | A readonly string that is one of: 278 | 279 | - `new` - newly created, not opened or closed 280 | - `opening` - waiting for the underlying store to be opened 281 | - `open` - successfully opened the store, available for use 282 | - `closing` - waiting for the store to be closed 283 | - `closed` - store has been successfully closed. 284 | 285 | ### `db.isOperational()` 286 | 287 | Returns `true` if the store accepts operations, which in the case of `levelup` means that `status` is either `opening` or `open`, because it opens itself and queues up operations until opened. 288 | 289 | ### `db.createReadStream([options])` 290 | 291 | Returns a [Readable Stream](https://nodejs.org/docs/latest/api/stream.html#stream_readable_streams) of key-value pairs. A pair is an object with `key` and `value` properties. By default it will stream all entries in the underlying store from start to end. Use the options described below to control the range, direction and results. 292 | 293 | ```js 294 | db.createReadStream() 295 | .on('data', function (data) { 296 | console.log(data.key, '=', data.value) 297 | }) 298 | .on('error', function (err) { 299 | console.log('Oh my!', err) 300 | }) 301 | .on('close', function () { 302 | console.log('Stream closed') 303 | }) 304 | .on('end', function () { 305 | console.log('Stream ended') 306 | }) 307 | ``` 308 | 309 | You can supply an options object as the first parameter to `createReadStream()` with the following properties: 310 | 311 | - `gt` (greater than), `gte` (greater than or equal) define the lower bound of the range to be streamed. Only entries where the key is greater than (or equal to) this option will be included in the range. When `reverse=true` the order will be reversed, but the entries streamed will be the same. 312 | 313 | - `lt` (less than), `lte` (less than or equal) define the higher bound of the range to be streamed. Only entries where the key is less than (or equal to) this option will be included in the range. When `reverse=true` the order will be reversed, but the entries streamed will be the same. 314 | 315 | - `reverse` _(boolean, default: `false`)_: stream entries in reverse order. Beware that due to the way that stores like LevelDB work, a reverse seek can be slower than a forward seek. 316 | 317 | - `limit` _(number, default: `-1`)_: limit the number of entries collected by this stream. This number represents a _maximum_ number of entries and may not be reached if you get to the end of the range first. A value of `-1` means there is no limit. When `reverse=true` the entries with the highest keys will be returned instead of the lowest keys. 318 | 319 | - `keys` _(boolean, default: `true`)_: whether the results should contain keys. If set to `true` and `values` set to `false` then results will simply be keys, rather than objects with a `key` property. Used internally by the `createKeyStream()` method. 320 | 321 | - `values` _(boolean, default: `true`)_: whether the results should contain values. If set to `true` and `keys` set to `false` then results will simply be values, rather than objects with a `value` property. Used internally by the `createValueStream()` method. 322 | 323 | ### `db.createKeyStream([options])` 324 | 325 | Returns a [Readable Stream](https://nodejs.org/docs/latest/api/stream.html#stream_readable_streams) of keys rather than key-value pairs. Use the same options as described for [`createReadStream()`](#dbcreatereadstreamoptions) to control the range and direction. 326 | 327 | You can also obtain this stream by passing an options object to `createReadStream()` with `keys` set to `true` and `values` set to `false`. The result is equivalent; both streams operate in [object mode](https://nodejs.org/docs/latest/api/stream.html#stream_object_mode). 328 | 329 | ```js 330 | db.createKeyStream() 331 | .on('data', function (data) { 332 | console.log('key=', data) 333 | }) 334 | 335 | // same as: 336 | db.createReadStream({ keys: true, values: false }) 337 | .on('data', function (data) { 338 | console.log('key=', data) 339 | }) 340 | ``` 341 | 342 | ### `db.createValueStream([options])` 343 | 344 | Returns a [Readable Stream](https://nodejs.org/docs/latest/api/stream.html#stream_readable_streams) of values rather than key-value pairs. Use the same options as described for [`createReadStream()`](#dbcreatereadstreamoptions) to control the range and direction. 345 | 346 | You can also obtain this stream by passing an options object to `createReadStream()` with `values` set to `true` and `keys` set to `false`. The result is equivalent; both streams operate in [object mode](https://nodejs.org/docs/latest/api/stream.html#stream_object_mode). 347 | 348 | ```js 349 | db.createValueStream() 350 | .on('data', function (data) { 351 | console.log('value=', data) 352 | }) 353 | 354 | // same as: 355 | db.createReadStream({ keys: false, values: true }) 356 | .on('data', function (data) { 357 | console.log('value=', data) 358 | }) 359 | ``` 360 | 361 | ### `db.iterator([options])` 362 | 363 | Returns an [`abstract-leveldown` iterator](https://github.com/Level/abstract-leveldown/#abstractleveldown_iteratoroptions), which is what powers the readable streams above. Options are the same as the range options of [`createReadStream()`](#dbcreatereadstreamoptions) and are passed to the underlying store. 364 | 365 | These iterators support [`for await...of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of): 366 | 367 | ```js 368 | for await (const [key, value] of db.iterator()) { 369 | console.log(value) 370 | } 371 | ``` 372 | 373 | ### `db.clear([options][, callback])` 374 | 375 | Delete all entries or a range. Not guaranteed to be atomic. Accepts the following range options (with the same rules as on iterators): 376 | 377 | - `gt` (greater than), `gte` (greater than or equal) define the lower bound of the range to be deleted. Only entries where the key is greater than (or equal to) this option will be included in the range. When `reverse=true` the order will be reversed, but the entries deleted will be the same. 378 | - `lt` (less than), `lte` (less than or equal) define the higher bound of the range to be deleted. Only entries where the key is less than (or equal to) this option will be included in the range. When `reverse=true` the order will be reversed, but the entries deleted will be the same. 379 | - `reverse` _(boolean, default: `false`)_: delete entries in reverse order. Only effective in combination with `limit`, to remove the last N records. 380 | - `limit` _(number, default: `-1`)_: limit the number of entries to be deleted. This number represents a _maximum_ number of entries and may not be reached if you get to the end of the range first. A value of `-1` means there is no limit. When `reverse=true` the entries with the highest keys will be deleted instead of the lowest keys. 381 | 382 | If no options are provided, all entries will be deleted. The `callback` function will be called with no arguments if the operation was successful or with an `WriteError` if it failed for any reason. 383 | 384 | If no callback is passed, a promise is returned. 385 | 386 | ## What happened to `db.createWriteStream`? 387 | 388 | `db.createWriteStream()` has been removed in order to provide a smaller and more maintainable core. It primarily existed to create symmetry with `db.createReadStream()` but through much [discussion](https://github.com/Level/levelup/issues/199), removing it was the best course of action. 389 | 390 | The main driver for this was performance. While `db.createReadStream()` performs well under most use cases, `db.createWriteStream()` was highly dependent on the application keys and values. Thus we can't provide a standard implementation and encourage more `write-stream` implementations to be created to solve the broad spectrum of use cases. 391 | 392 | Check out the implementations that the community has produced [here](https://github.com/Level/awesome#streams). 393 | 394 | ## Promise Support 395 | 396 | Each function accepting a callback returns a promise if the callback is omitted. The only exception is the `levelup` constructor itself, which if no callback is passed will lazily open the underlying store in the background. 397 | 398 | Example: 399 | 400 | ```js 401 | const db = levelup(leveldown('./my-db')) 402 | await db.put('foo', 'bar') 403 | console.log(await db.get('foo')) 404 | ``` 405 | 406 | ## Events 407 | 408 | `levelup` is an [`EventEmitter`](https://nodejs.org/api/events.html) and emits the following events. 409 | 410 | | Event | Description | Arguments | 411 | | :-------- | :-------------------------- | :------------------- | 412 | | `put` | Key has been updated | `key, value` (any) | 413 | | `del` | Key has been deleted | `key` (any) | 414 | | `batch` | Batch has executed | `operations` (array) | 415 | | `clear` | Entries were deleted | `options` (object) | 416 | | `opening` | Underlying store is opening | - | 417 | | `open` | Store has opened | - | 418 | | `ready` | Alias of `open` | - | 419 | | `closing` | Store is closing | - | 420 | | `closed` | Store has closed. | - | 421 | | `error` | An error occurred | `error` (Error) | 422 | 423 | For example you can do: 424 | 425 | ```js 426 | db.on('put', function (key, value) { 427 | console.log('inserted', { key, value }) 428 | }) 429 | ``` 430 | 431 | ## Multi-process Access 432 | 433 | Stores like LevelDB are thread-safe but they are **not** suitable for accessing with multiple processes. You should only ever have a store open from a single Node.js process. Node.js clusters are made up of multiple processes so a `levelup` instance cannot be shared between them either. 434 | 435 | See [`Level/awesome`](https://github.com/Level/awesome#shared-access) for modules like [`multileveldown`](https://github.com/mafintosh/multileveldown) that may help if you require a single store to be shared across processes. 436 | 437 | ## Contributing 438 | 439 | [`Level/levelup`](https://github.com/Level/levelup) is an **OPEN Open Source Project**. This means that: 440 | 441 | > Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project. 442 | 443 | See the [Contribution Guide](https://github.com/Level/community/blob/master/CONTRIBUTING.md) for more details. 444 | 445 | ## Big Thanks 446 | 447 | Cross-browser Testing Platform and Open Source ♥ Provided by [Sauce Labs](https://saucelabs.com). 448 | 449 | [![Sauce Labs logo](./sauce-labs.svg)](https://saucelabs.com) 450 | 451 | ## Donate 452 | 453 | Support us with a monthly donation on [Open Collective](https://opencollective.com/level) and help us continue our work. 454 | 455 | ## License 456 | 457 | [MIT](LICENSE) 458 | 459 | [level-badge]: https://leveljs.org/img/badge.svg 460 | -------------------------------------------------------------------------------- /UPGRADING.md: -------------------------------------------------------------------------------- 1 | # Upgrade Guide 2 | 3 | This document describes breaking changes and how to upgrade. For a complete list of changes including minor and patch releases, please refer to the [changelog](CHANGELOG.md). 4 | 5 | ## 5.0.0 6 | 7 | Legacy range options have been removed ([Level/community#86](https://github.com/Level/community/issues/86)). If you previously did: 8 | 9 | ```js 10 | db.createReadStream({ start: 'a', end: 'z' }) 11 | ``` 12 | 13 | An error would now be thrown and you must instead do: 14 | 15 | ```js 16 | db.createReadStream({ gte: 'a', lte: 'z' }) 17 | ``` 18 | 19 | The same applies to `db.iterator()`, `db.createKeyStream()` and `db.createValueStream()`. 20 | 21 | This release also drops support of legacy runtime environments ([Level/community#98](https://github.com/Level/community/issues/98)): 22 | 23 | - Node.js 6 and 8 24 | - Internet Explorer 11 25 | - Safari 9-11 26 | - Stock Android browser (AOSP). 27 | 28 | Lastly, in browsers, `process.nextTick()` has been replaced with [`queue-microtask`](https://github.com/feross/queue-microtask), except in streams. In the future we might use `queueMicrotask()` in Node.js too. 29 | 30 | ## 4.0.0 31 | 32 | There have been two major updates to dependencies. First, `level-iterator-stream` is now based on [`readable-stream@3`](https://github.com/nodejs/readable-stream#version-3xx). Second, `deferred-leveldown` is now based on [`abstract-leveldown@6`](https://github.com/Level/abstract-leveldown/blob/master/UPGRADING.md#v6). Please follow these links for more information; both contain significant enough changes to warrant this `levelup` major. In addition, all aforementioned dependencies and by extension `levelup` have dropped support of IE10. 33 | 34 | To get a consistent behavior between opening and opened `levelup` instances (in the former case, your store will be wrapped with `deferred-leveldown`), we recommend to pair `levelup@4` only with a store based on `abstract-leveldown` >= 6. For example, `deferred-leveldown` now rejects `null` and `undefined` values. If you pair `levelup@4` with an older store, `db.put('key', null)` would only throw an error if `db` is still opening itself. 35 | 36 | ## 3.0.0 37 | 38 | 1. Dropped support for node 4. 39 | 2. Batch operations no longer default to `'put'`. If `type` isn't specified, an error will be thrown, courtesy of `abstract-leveldown`. 40 | 41 | ## 2.0.0 42 | 43 | ### Summary 44 | 45 | There has been quite some work done for this new major version: 46 | 47 | 1. Make `levelup` more generic by reducing focus on [`leveldown`](https://github.com/Level/leveldown) and [`LevelDB`](https://github.com/google/leveldb). 48 | 2. Make `levelup` more generic by removing code related to encodings, which would allow \*down implementations to manage encodings themselves. 49 | 3. Use [`standard`](https://github.com/standard/standard) as linter to avoid bikeshedding. 50 | 4. Add a native `Promise` API for promise using geeks. Many have been asking for this. Also `async/await` is awesome. Breaking change: previously, if you did not pass a callback to an async function and there was an error, `levelup` would emit an `error` event instead. This is no longer true. 51 | 52 | Point `1` and `2` also helps out with reducing complexity. 53 | 54 | ### Upgrade Guide 55 | 56 | Since `levelup` no longer tries to load `leveldown` as a default backend you have to provide a backend instance yourself. 57 | 58 | So if you previously did: 59 | 60 | ``` 61 | $ npm i levelup leveldown --save 62 | ``` 63 | 64 | And in your code you did something like: 65 | 66 | ```js 67 | const levelup = require('levelup') 68 | const db = levelup('/path/to/db') 69 | ``` 70 | 71 | You should now do (for identical functionality): 72 | 73 | ```js 74 | const levelup = require('levelup') 75 | const encode = require('encoding-down') 76 | const leveldown = require('leveldown') 77 | const db = levelup(encode(leveldown('/path/to/db'))) 78 | ``` 79 | 80 | Note that we have moved out encodings into [`encoding-down`](https://github.com/level/encoding-down), which in itself is a \*down that wraps a \*down (meta ftw). It basically just sits in between `levelup` and the _actual_ backend to operate on encodings for keys and values. Default encoding is `'utf8'` like before. 81 | 82 | This obviously means everyone has to do a lot of code rewrite which is bad. So we aim to fix this by putting that code into [`level@2.0.0`](https://github.com/level/level), which already is used as a convenience package. 83 | 84 | Switching from `levelup` and `leveldown` combo to only using `level` you would do: 85 | 86 | ```js 87 | const level = require('level') 88 | const db = level('/path/to/db') 89 | ``` 90 | 91 | Also, we aim to simplify using `memdown` in the same way by updating `level-mem`. 92 | 93 | For more advanced usage with custom versions of `abstract-leveldown`, the first parameter to `levelup()` should be an `abstract-leveldown` instead of passing a factory function via `options.db`. 94 | 95 | So if you previously did: 96 | 97 | ```js 98 | const db = levelup('/path/to/db', { 99 | db: function (location) { 100 | return new CustomLevelDOWN(location) 101 | } 102 | }) 103 | ``` 104 | 105 | You should now do (for identical functionality): 106 | 107 | ```js 108 | const encode = require('encoding-down') 109 | const db = levelup(encode(new CustomLevelDOWN('/path/to/db'))) 110 | ``` 111 | -------------------------------------------------------------------------------- /lib/batch.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const WriteError = require('level-errors').WriteError 4 | const catering = require('catering') 5 | const getCallback = require('./common').getCallback 6 | const getOptions = require('./common').getOptions 7 | 8 | function Batch (levelup) { 9 | this.db = levelup 10 | this.batch = levelup.db.batch() 11 | this.ops = [] 12 | this.length = 0 13 | } 14 | 15 | Batch.prototype.put = function (key, value, options) { 16 | try { 17 | this.batch.put(key, value, options) 18 | } catch (e) { 19 | throw new WriteError(e) 20 | } 21 | 22 | this.ops.push({ ...options, type: 'put', key, value }) 23 | this.length++ 24 | 25 | return this 26 | } 27 | 28 | Batch.prototype.del = function (key, options) { 29 | try { 30 | this.batch.del(key, options) 31 | } catch (err) { 32 | throw new WriteError(err) 33 | } 34 | 35 | this.ops.push({ ...options, type: 'del', key }) 36 | this.length++ 37 | 38 | return this 39 | } 40 | 41 | Batch.prototype.clear = function () { 42 | try { 43 | this.batch.clear() 44 | } catch (err) { 45 | throw new WriteError(err) 46 | } 47 | 48 | this.ops = [] 49 | this.length = 0 50 | 51 | return this 52 | } 53 | 54 | Batch.prototype.write = function (options, callback) { 55 | const levelup = this.db 56 | const ops = this.ops 57 | 58 | callback = getCallback(options, callback) 59 | callback = catering.fromCallback(callback) 60 | options = getOptions(options) 61 | 62 | try { 63 | this.batch.write(options, function (err) { 64 | if (err) { return callback(new WriteError(err)) } 65 | levelup.emit('batch', ops) 66 | callback() 67 | }) 68 | } catch (err) { 69 | throw new WriteError(err) 70 | } 71 | 72 | return callback.promise 73 | } 74 | 75 | module.exports = Batch 76 | -------------------------------------------------------------------------------- /lib/common.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | exports.getCallback = function (options, callback) { 4 | return typeof options === 'function' ? options : callback 5 | } 6 | 7 | exports.getOptions = function (options) { 8 | return typeof options === 'object' && options !== null ? options : {} 9 | } 10 | -------------------------------------------------------------------------------- /lib/levelup.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const EventEmitter = require('events').EventEmitter 4 | const inherits = require('util').inherits 5 | const DeferredLevelDOWN = require('deferred-leveldown') 6 | const IteratorStream = require('level-iterator-stream') 7 | const Batch = require('./batch') 8 | const errors = require('level-errors') 9 | const supports = require('level-supports') 10 | const catering = require('catering') 11 | const getCallback = require('./common').getCallback 12 | const getOptions = require('./common').getOptions 13 | 14 | // TODO: after we drop node 10, also use queueMicrotask() in node 15 | const nextTick = require('./next-tick') 16 | 17 | const WriteError = errors.WriteError 18 | const ReadError = errors.ReadError 19 | const NotFoundError = errors.NotFoundError 20 | const OpenError = errors.OpenError 21 | const InitializationError = errors.InitializationError 22 | 23 | function LevelUP (db, options, callback) { 24 | if (!(this instanceof LevelUP)) { 25 | return new LevelUP(db, options, callback) 26 | } 27 | 28 | let error 29 | 30 | EventEmitter.call(this) 31 | this.setMaxListeners(Infinity) 32 | 33 | if (typeof options === 'function') { 34 | callback = options 35 | options = {} 36 | } 37 | 38 | options = options || {} 39 | 40 | if (!db || typeof db !== 'object') { 41 | error = new InitializationError('First argument must be an abstract-leveldown compliant store') 42 | if (typeof callback === 'function') { 43 | return nextTick(callback, error) 44 | } 45 | throw error 46 | } 47 | 48 | if (typeof db.status !== 'string') { 49 | throw new Error('.status required, old abstract-leveldown') 50 | } 51 | 52 | this.options = getOptions(options) 53 | this._db = db 54 | this.db = null 55 | this.open(callback || ((err) => { 56 | if (err) this.emit('error', err) 57 | })) 58 | 59 | // Create manifest based on deferred-leveldown's 60 | this.supports = supports(this.db.supports, { 61 | status: true, 62 | deferredOpen: true, 63 | openCallback: true, 64 | promises: true, 65 | streams: true 66 | }) 67 | 68 | // Experimental: enrich levelup interface 69 | for (const method of Object.keys(this.supports.additionalMethods)) { 70 | if (this[method] != null) continue 71 | 72 | // Don't do this.db[method].bind() because this.db is dynamic. 73 | this[method] = function (...args) { 74 | return this.db[method](...args) 75 | } 76 | } 77 | } 78 | 79 | LevelUP.prototype.emit = EventEmitter.prototype.emit 80 | LevelUP.prototype.once = EventEmitter.prototype.once 81 | inherits(LevelUP, EventEmitter) 82 | 83 | // TODO: tests 84 | Object.defineProperty(LevelUP.prototype, 'status', { 85 | enumerable: true, 86 | get () { 87 | return this.db.status 88 | } 89 | }) 90 | 91 | // TODO: tests 92 | LevelUP.prototype.isOperational = function () { 93 | return this.db.status === 'open' || this.db.status === 'opening' 94 | } 95 | 96 | LevelUP.prototype.open = function (opts, callback) { 97 | if (typeof opts === 'function') { 98 | callback = opts 99 | opts = null 100 | } 101 | 102 | callback = catering.fromCallback(callback) 103 | 104 | if (!opts) { 105 | opts = this.options 106 | } 107 | 108 | // 1) Don't check db.status until levelup has opened, 109 | // in order for levelup events to be consistent 110 | if (this.db && this.isOpen()) { 111 | nextTick(callback, null, this) 112 | return callback.promise 113 | } 114 | 115 | if (this.db && this._isOpening()) { 116 | this.once('open', () => { callback(null, this) }) 117 | return callback.promise 118 | } 119 | 120 | // 2) Instead let deferred-leveldown handle already-open cases. 121 | // TODO: ideally though, levelup would have its own status 122 | this.db = new DeferredLevelDOWN(this._db) 123 | this.emit('opening') 124 | 125 | this.db.open(opts, (err) => { 126 | if (err) { 127 | return callback(new OpenError(err)) 128 | } 129 | this.db = this._db 130 | callback(null, this) 131 | this.emit('open') 132 | this.emit('ready') 133 | }) 134 | 135 | return callback.promise 136 | } 137 | 138 | LevelUP.prototype.close = function (callback) { 139 | callback = catering.fromCallback(callback) 140 | 141 | if (this.isOpen()) { 142 | this.db.close((err, ...rest) => { 143 | this.emit('closed') 144 | callback(err, ...rest) 145 | }) 146 | this.emit('closing') 147 | } else if (this.isClosed()) { 148 | nextTick(callback) 149 | } else if (this.db.status === 'closing') { 150 | this.once('closed', callback) 151 | } else if (this._isOpening()) { 152 | this.once('open', () => { 153 | this.close(callback) 154 | }) 155 | } 156 | 157 | return callback.promise 158 | } 159 | 160 | // TODO: remove in future major 161 | LevelUP.prototype.isOpen = function () { 162 | return this.db.status === 'open' 163 | } 164 | 165 | // TODO: remove in future major 166 | LevelUP.prototype._isOpening = function () { 167 | return this.db.status === 'opening' 168 | } 169 | 170 | // TODO: remove in future major 171 | LevelUP.prototype.isClosed = function () { 172 | return (/^clos|new/).test(this.db.status) 173 | } 174 | 175 | LevelUP.prototype.get = function (key, options, callback) { 176 | callback = getCallback(options, callback) 177 | callback = catering.fromCallback(callback) 178 | 179 | if (maybeError(this, callback)) { 180 | return callback.promise 181 | } 182 | 183 | options = getOptions(options) 184 | 185 | this.db.get(key, options, function (err, value) { 186 | if (err) { 187 | if ((/notfound/i).test(err) || err.notFound) { 188 | err = new NotFoundError('Key not found in database [' + key + ']', err) 189 | } else { 190 | err = new ReadError(err) 191 | } 192 | return callback(err) 193 | } 194 | callback(null, value) 195 | }) 196 | 197 | return callback.promise 198 | } 199 | 200 | LevelUP.prototype.getMany = function (keys, options, callback) { 201 | return this.db.getMany(keys, options, callback) 202 | } 203 | 204 | LevelUP.prototype.put = function (key, value, options, callback) { 205 | callback = getCallback(options, callback) 206 | callback = catering.fromCallback(callback) 207 | 208 | if (maybeError(this, callback)) { 209 | return callback.promise 210 | } 211 | 212 | options = getOptions(options) 213 | 214 | this.db.put(key, value, options, (err) => { 215 | if (err) { 216 | return callback(new WriteError(err)) 217 | } 218 | this.emit('put', key, value) 219 | callback() 220 | }) 221 | 222 | return callback.promise 223 | } 224 | 225 | LevelUP.prototype.del = function (key, options, callback) { 226 | callback = getCallback(options, callback) 227 | callback = catering.fromCallback(callback) 228 | 229 | if (maybeError(this, callback)) { 230 | return callback.promise 231 | } 232 | 233 | options = getOptions(options) 234 | 235 | this.db.del(key, options, (err) => { 236 | if (err) { 237 | return callback(new WriteError(err)) 238 | } 239 | this.emit('del', key) 240 | callback() 241 | }) 242 | 243 | return callback.promise 244 | } 245 | 246 | LevelUP.prototype.batch = function (arr, options, callback) { 247 | if (!arguments.length) { 248 | return new Batch(this) 249 | } 250 | 251 | if (typeof arr === 'function') callback = arr 252 | else callback = getCallback(options, callback) 253 | 254 | callback = catering.fromCallback(callback) 255 | 256 | if (maybeError(this, callback)) { 257 | return callback.promise 258 | } 259 | 260 | options = getOptions(options) 261 | 262 | this.db.batch(arr, options, (err) => { 263 | if (err) { 264 | return callback(new WriteError(err)) 265 | } 266 | this.emit('batch', arr) 267 | callback() 268 | }) 269 | 270 | return callback.promise 271 | } 272 | 273 | LevelUP.prototype.iterator = function (options) { 274 | return this.db.iterator(options) 275 | } 276 | 277 | LevelUP.prototype.clear = function (options, callback) { 278 | callback = getCallback(options, callback) 279 | options = getOptions(options) 280 | callback = catering.fromCallback(callback) 281 | 282 | if (maybeError(this, callback)) { 283 | return callback.promise 284 | } 285 | 286 | this.db.clear(options, (err) => { 287 | if (err) { 288 | return callback(new WriteError(err)) 289 | } 290 | this.emit('clear', options) 291 | callback() 292 | }) 293 | 294 | return callback.promise 295 | } 296 | 297 | LevelUP.prototype.readStream = 298 | LevelUP.prototype.createReadStream = function (options) { 299 | options = Object.assign({ keys: true, values: true }, options) 300 | if (typeof options.limit !== 'number') { options.limit = -1 } 301 | return new IteratorStream(this.db.iterator(options), options) 302 | } 303 | 304 | LevelUP.prototype.keyStream = 305 | LevelUP.prototype.createKeyStream = function (options) { 306 | return this.createReadStream(Object.assign({}, options, { keys: true, values: false })) 307 | } 308 | 309 | LevelUP.prototype.valueStream = 310 | LevelUP.prototype.createValueStream = function (options) { 311 | return this.createReadStream(Object.assign({}, options, { keys: false, values: true })) 312 | } 313 | 314 | LevelUP.prototype.toString = function () { 315 | return 'LevelUP' 316 | } 317 | 318 | LevelUP.prototype.type = 'levelup' 319 | 320 | // Expose nextTick for API parity with abstract-leveldown 321 | LevelUP.prototype._nextTick = nextTick 322 | 323 | function maybeError (db, callback) { 324 | if (!db.isOperational()) { 325 | nextTick(callback, new ReadError('Database is not open')) 326 | return true 327 | } 328 | 329 | return false 330 | } 331 | 332 | LevelUP.errors = errors 333 | module.exports = LevelUP 334 | -------------------------------------------------------------------------------- /lib/next-tick-browser.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const queueMicrotask = require('queue-microtask') 4 | 5 | module.exports = function (fn, ...args) { 6 | if (args.length === 0) { 7 | queueMicrotask(fn) 8 | } else { 9 | queueMicrotask(() => fn(...args)) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/next-tick.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = process.nextTick 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "levelup", 3 | "version": "5.1.1", 4 | "description": "Fast & simple storage - a Node.js-style LevelDB wrapper", 5 | "license": "MIT", 6 | "main": "lib/levelup.js", 7 | "browser": { 8 | "./lib/next-tick.js": "./lib/next-tick-browser.js" 9 | }, 10 | "scripts": { 11 | "test": "standard && hallmark && (nyc -s node test/self.js | faucet) && nyc report", 12 | "coverage": "nyc report -r lcovonly", 13 | "test-browsers": "airtap --coverage --verbose test/self.js", 14 | "test-browsers-local": "airtap -p local test/self.js", 15 | "hallmark": "hallmark --fix", 16 | "dependency-check": "dependency-check --no-dev -i queue-microtask .", 17 | "prepublishOnly": "npm run dependency-check" 18 | }, 19 | "files": [ 20 | "lib", 21 | "CHANGELOG.md", 22 | "UPGRADING.md", 23 | "sauce-labs.svg" 24 | ], 25 | "dependencies": { 26 | "catering": "^2.0.0", 27 | "deferred-leveldown": "^7.0.0", 28 | "level-errors": "^3.0.1", 29 | "level-iterator-stream": "^5.0.0", 30 | "level-supports": "^2.0.1", 31 | "queue-microtask": "^1.2.3" 32 | }, 33 | "devDependencies": { 34 | "after": "^0.8.2", 35 | "airtap": "^4.0.1", 36 | "airtap-playwright": "^1.0.1", 37 | "airtap-sauce": "^1.1.0", 38 | "async-each": "^1.0.3", 39 | "browserify": "^17.0.0", 40 | "concat-stream": "^2.0.0", 41 | "delayed": "^2.0.0", 42 | "dependency-check": "^4.1.0", 43 | "encoding-down": "^7.1.0", 44 | "faucet": "^0.0.3", 45 | "hallmark": "^4.0.0", 46 | "level-concat-iterator": "^3.0.0", 47 | "memdown": "^6.1.0", 48 | "nyc": "^15.1.0", 49 | "run-parallel": "^1.2.0", 50 | "run-series": "^1.1.8", 51 | "simple-concat": "^1.0.0", 52 | "sinon": "^13.0.1", 53 | "standard": "^17.0.0", 54 | "tape": "^5.2.2", 55 | "trickle": "0.0.2" 56 | }, 57 | "repository": { 58 | "type": "git", 59 | "url": "https://github.com/Level/levelup.git" 60 | }, 61 | "homepage": "https://github.com/Level/levelup", 62 | "keywords": [ 63 | "level", 64 | "leveldb", 65 | "stream", 66 | "database", 67 | "db", 68 | "store", 69 | "storage", 70 | "json" 71 | ], 72 | "engines": { 73 | "node": ">=10" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /sauce-labs.svg: -------------------------------------------------------------------------------- 1 | 7 | 8 | 15 | 16 | Sauce Labs 17 | 22 | 24 | 29 | 34 | 39 | 44 | 49 | 54 | 59 | 64 | 69 | 74 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | _levelup_test_db* 2 | -------------------------------------------------------------------------------- /test/batch-test.js: -------------------------------------------------------------------------------- 1 | const levelup = require('../lib/levelup') 2 | const errors = levelup.errors 3 | const each = require('async-each') 4 | const series = require('run-series') 5 | const discardable = require('./util/discardable') 6 | 7 | module.exports = function (test, testCommon) { 8 | test('array-form batch(): multiple puts', function (t) { 9 | discardable(t, testCommon, function (db, done) { 10 | db.batch([ 11 | { type: 'put', key: 'foo', value: 'afoovalue' }, 12 | { type: 'put', key: 'bar', value: 'abarvalue' }, 13 | { type: 'put', key: 'baz', value: 'abazvalue' } 14 | ], function (err) { 15 | t.ifError(err) 16 | 17 | each(['foo', 'bar', 'baz'], function (key, next) { 18 | db.get(key, { asBuffer: false }, function (err, value) { 19 | t.ifError(err) 20 | t.is(value, 'a' + key + 'value') 21 | next() 22 | }) 23 | }, done) 24 | }) 25 | }) 26 | }) 27 | 28 | testCommon.promises && test('array-form batch(): promise interface', function (t) { 29 | discardable(t, testCommon, function (db, done) { 30 | db.batch([ 31 | { type: 'put', key: 'foo', value: 'afoovalue' }, 32 | { type: 'put', key: 'bar', value: 'abarvalue' }, 33 | { type: 'put', key: 'baz', value: 'abazvalue' } 34 | ]) 35 | .then(function () { 36 | each(['foo', 'bar', 'baz'], function (key, next) { 37 | db.get(key, { asBuffer: false }, function (err, value) { 38 | t.ifError(err) 39 | t.is(value, 'a' + key + 'value') 40 | next() 41 | }) 42 | }, done) 43 | }) 44 | .catch(done) 45 | }) 46 | }) 47 | 48 | test('array-form batch(): multiple operations', function (t) { 49 | discardable(t, testCommon, function (db, done) { 50 | series([ 51 | function (next) { 52 | db.batch([ 53 | { type: 'put', key: '1', value: 'one' }, 54 | { type: 'put', key: '2', value: 'two' }, 55 | { type: 'put', key: '3', value: 'three' } 56 | ], next) 57 | }, 58 | function (next) { 59 | db.batch([ 60 | { type: 'put', key: 'foo', value: 'afoovalue' }, 61 | { type: 'del', key: '1' }, 62 | { type: 'put', key: 'bar', value: 'abarvalue' }, 63 | { type: 'del', key: 'foo' }, 64 | { type: 'put', key: 'baz', value: 'abazvalue' } 65 | ], next) 66 | }, 67 | function (next) { 68 | // these should exist 69 | each(['2', '3', 'bar', 'baz'], function (key, next) { 70 | db.get(key, { asBuffer: false }, function (err, value) { 71 | t.ifError(err) 72 | t.ok(value != null) 73 | next() 74 | }) 75 | }, next) 76 | }, 77 | function (next) { 78 | // these shouldn't exist 79 | each(['1', 'foo'], function (key, next) { 80 | db.get(key, { asBuffer: false }, function (err, value) { 81 | t.ok(err) 82 | t.ok(err instanceof errors.NotFoundError) 83 | t.is(value, undefined) 84 | next() 85 | }) 86 | }, next) 87 | } 88 | ], done) 89 | }) 90 | }) 91 | 92 | test('chained batch(): multiple operations', function (t) { 93 | discardable(t, testCommon, function (db, done) { 94 | db.put('1', 'one', function (err) { 95 | t.ifError(err) 96 | 97 | db.batch() 98 | .put('one', '1') 99 | .del('two') 100 | .put('three', '3') 101 | .clear() 102 | .del('1') 103 | .put('2', 'two') 104 | .put('3', 'three') 105 | .del('3') 106 | .write(function (err) { 107 | t.ifError(err) 108 | 109 | each(['one', 'three', '1', '2', '3'], function (key, next) { 110 | db.get(key, { asBuffer: false }, function (err) { 111 | if (['one', 'three', '1', '3'].indexOf(key) > -1) { 112 | t.ok(err) 113 | } else { 114 | t.ifError(err) 115 | } 116 | 117 | next() 118 | }) 119 | }, done) 120 | }) 121 | }) 122 | }) 123 | }) 124 | 125 | test('chained batch(): options', function (t) { 126 | discardable(t, testCommon, function (db, done) { 127 | const batch = db.batch() 128 | let underlying = batch 129 | while (underlying.batch) underlying = underlying.batch 130 | 131 | const write = underlying.write.bind(underlying) 132 | underlying.write = function (options, cb) { 133 | t.same(options, { foo: 'bar' }) 134 | write(options, cb) 135 | } 136 | 137 | batch.put('one', '1') 138 | .write({ foo: 'bar' }, function (err) { 139 | t.ifError(err) 140 | done() 141 | }) 142 | }) 143 | }) 144 | 145 | testCommon.promises && test('chained batch(): promise interface - options', function (t) { 146 | discardable(t, testCommon, function (db, done) { 147 | const batch = db.batch() 148 | 149 | const write = batch.batch.write.bind(batch.batch) 150 | batch.batch.write = function (options, cb) { 151 | t.same(options, { foo: 'bar' }) 152 | write(options, cb) 153 | } 154 | 155 | batch.put('one', '1') 156 | .write({ foo: 'bar' }) 157 | .then(done) 158 | .catch(done) 159 | }) 160 | }) 161 | 162 | testCommon.promises && test('chained batch(): promise interface', function (t) { 163 | discardable(t, testCommon, function (db, done) { 164 | db.put('1', 'one', function (err) { 165 | t.ifError(err) 166 | 167 | db.batch() 168 | .put('one', '1') 169 | .del('two') 170 | .put('three', '3') 171 | .clear() 172 | .del('1') 173 | .put('2', 'two') 174 | .put('3', 'three') 175 | .del('3') 176 | .write() 177 | .then(function () { 178 | each(['one', 'three', '1', '2', '3'], function (key, next) { 179 | db.get(key, { asBuffer: false }, function (err) { 180 | if (['one', 'three', '1', '3'].indexOf(key) > -1) { 181 | t.ok(err) 182 | } else { 183 | t.ifError(err) 184 | } 185 | 186 | next() 187 | }) 188 | }, done) 189 | }) 190 | .catch(done) 191 | }) 192 | }) 193 | }) 194 | 195 | test('chained batch(): exposes ops queue length', function (t) { 196 | discardable(t, testCommon, function (db, done) { 197 | const batch = db.batch() 198 | .put('one', '1') 199 | .del('two') 200 | .put('three', '3') 201 | t.is(batch.length, 3) 202 | batch.clear() 203 | t.is(batch.length, 0) 204 | batch 205 | .del('1') 206 | .put('2', 'two') 207 | .put('3', 'three') 208 | .del('3') 209 | t.is(batch.length, 4) 210 | done() 211 | }) 212 | }) 213 | 214 | test('array-form batch(): can overwrite data from put()', function (t) { 215 | // checks encoding and whatnot (?) 216 | discardable(t, testCommon, function (db, done) { 217 | series([ 218 | db.put.bind(db, '1', 'one'), 219 | db.put.bind(db, '2', 'two'), 220 | db.put.bind(db, '3', 'three'), 221 | function (next) { 222 | db.batch([ 223 | { type: 'put', key: 'foo', value: 'afoovalue' }, 224 | { type: 'del', key: '1' }, 225 | { type: 'put', key: 'bar', value: 'abarvalue' }, 226 | { type: 'del', key: 'foo' }, 227 | { type: 'put', key: 'baz', value: 'abazvalue' } 228 | ], next) 229 | }, 230 | function (next) { 231 | // these should exist 232 | each(['2', '3', 'bar', 'baz'], function (key, next) { 233 | db.get(key, { asBuffer: false }, function (err, value) { 234 | t.ifError(err) 235 | t.ok(value != null) 236 | next() 237 | }) 238 | }, next) 239 | }, 240 | function (next) { 241 | // these shouldn't exist 242 | each(['1', 'foo'], function (key, next) { 243 | db.get(key, { asBuffer: false }, function (err, value) { 244 | t.ok(err) 245 | t.ok(err instanceof errors.NotFoundError) 246 | t.is(value, undefined) 247 | next() 248 | }) 249 | }, next) 250 | } 251 | ], done) 252 | }) 253 | }) 254 | 255 | test('array-form batch(): data can be read with get() and del()', function (t) { 256 | discardable(t, testCommon, function (db, done) { 257 | series([ 258 | function (next) { 259 | db.batch([ 260 | { type: 'put', key: '1', value: 'one' }, 261 | { type: 'put', key: '2', value: 'two' }, 262 | { type: 'put', key: '3', value: 'three' } 263 | ], next) 264 | }, 265 | db.del.bind(db, '1', 'one'), 266 | function (next) { 267 | // these should exist 268 | each(['2', '3'], function (key, next) { 269 | db.get(key, { asBuffer: false }, function (err, value) { 270 | t.ifError(err) 271 | t.ok(value != null) 272 | next() 273 | }) 274 | }, next) 275 | }, 276 | function (next) { 277 | // this shouldn't exist 278 | db.get('1', { asBuffer: false }, function (err, value) { 279 | t.ok(err) 280 | t.ok(err instanceof errors.NotFoundError) 281 | t.is(value, undefined) 282 | next() 283 | }) 284 | } 285 | ], done) 286 | }) 287 | }) 288 | 289 | test('chained batch() arguments', function (t) { 290 | discardable(t, testCommon, function (db, done) { 291 | const batch = db.batch() 292 | 293 | t.test('chained batch() arguments: batch#put() with missing `value`', function (t) { 294 | throws(t, batch.put.bind(batch, 'foo1'), function (err) { 295 | t.is(err.name, 'WriteError') 296 | t.is(err.message, 'value cannot be `null` or `undefined`') 297 | }) 298 | 299 | throws(t, batch.put.bind(batch, 'foo1', null), function (err) { 300 | t.is(err.name, 'WriteError') 301 | t.is(err.message, 'value cannot be `null` or `undefined`') 302 | }) 303 | 304 | t.end() 305 | }) 306 | 307 | t.test('chained batch() arguments: batch#put() with missing `key`', function (t) { 308 | throws(t, batch.put.bind(batch, undefined, 'foo1'), function (err) { 309 | t.is(err.name, 'WriteError') 310 | t.is(err.message, 'key cannot be `null` or `undefined`') 311 | }) 312 | 313 | throws(t, batch.put.bind(batch, null, 'foo1'), function (err) { 314 | t.is(err.name, 'WriteError') 315 | t.is(err.message, 'key cannot be `null` or `undefined`') 316 | }) 317 | 318 | t.end() 319 | }) 320 | 321 | t.test('chained batch() arguments: batch#put() with missing `key` and `value`', function (t) { 322 | throws(t, batch.put.bind(batch), function (err) { 323 | t.is(err.name, 'WriteError') 324 | t.is(err.message, 'key cannot be `null` or `undefined`') 325 | }) 326 | 327 | throws(t, batch.put.bind(batch, null, null), function (err) { 328 | t.is(err.name, 'WriteError') 329 | t.is(err.message, 'key cannot be `null` or `undefined`') 330 | }) 331 | 332 | t.end() 333 | }) 334 | 335 | t.test('chained batch() arguments: batch#del() with missing `key`', function (t) { 336 | throws(t, batch.del.bind(batch, undefined, 'foo1'), function (err) { 337 | t.is(err.name, 'WriteError') 338 | t.is(err.message, 'key cannot be `null` or `undefined`') 339 | }) 340 | 341 | throws(t, batch.del.bind(batch, null, 'foo1'), function (err) { 342 | t.is(err.name, 'WriteError') 343 | t.is(err.message, 'key cannot be `null` or `undefined`') 344 | }) 345 | 346 | t.end() 347 | }) 348 | 349 | t.test('chained batch() arguments: teardown', function (t) { 350 | t.end() 351 | done() 352 | }) 353 | }) 354 | }) 355 | 356 | test('chained batch(): rejects operations after write()', function (t) { 357 | discardable(t, testCommon, function (db, done) { 358 | function verify (err) { 359 | t.is(err.name, 'WriteError') 360 | t.is(err.message, 'write() already called on this batch') 361 | } 362 | 363 | const batch = db.batch() 364 | batch.put('foo', 'bar').put('boom', 'bang').del('foo').write(function (err) { 365 | t.ifError(err, 'no batch error') 366 | 367 | throws(t, function () { batch.put('whoa', 'dude') }, verify) 368 | throws(t, function () { batch.del('foo') }, verify) 369 | throws(t, function () { batch.clear() }, verify) 370 | 371 | done() 372 | }) 373 | }) 374 | }) 375 | } 376 | 377 | function throws (t, fn, verify) { 378 | try { 379 | fn() 380 | } catch (err) { 381 | return verify(err) 382 | } 383 | 384 | t.fail('did not throw') 385 | } 386 | -------------------------------------------------------------------------------- /test/binary-test.js: -------------------------------------------------------------------------------- 1 | const each = require('async-each') 2 | const discardable = require('./util/discardable') 3 | 4 | module.exports = function (test, testCommon) { 5 | test('test put() and get() with binary value {valueEncoding:binary}', function (t) { 6 | discardable(t, testCommon, function (db, done) { 7 | db.put('binarydata', testBuffer(), { valueEncoding: 'binary' }, function (err) { 8 | t.ifError(err) 9 | db.get('binarydata', { valueEncoding: 'binary' }, function (err, value) { 10 | t.ifError(err) 11 | t.ok(value) 12 | t.ok(value.equals(testBuffer())) 13 | done() 14 | }) 15 | }) 16 | }) 17 | }) 18 | 19 | test('test put() and get() with binary value {valueEncoding:binary} in factory', function (t) { 20 | discardable(t, testCommon, { valueEncoding: 'binary' }, function (db, done) { 21 | db.put('binarydata', testBuffer(), function (err) { 22 | t.ifError(err) 23 | db.get('binarydata', function (err, value) { 24 | t.ifError(err) 25 | t.ok(value) 26 | t.ok(value.equals(testBuffer())) 27 | done() 28 | }) 29 | }) 30 | }) 31 | }) 32 | 33 | testCommon.bufferKeys && test('test put() and get() with binary key {valueEncoding:binary}', function (t) { 34 | discardable(t, testCommon, function (db, done) { 35 | db.put(testBuffer(), 'binarydata', { valueEncoding: 'binary' }, function (err) { 36 | t.ifError(err) 37 | db.get(testBuffer(), { valueEncoding: 'binary' }, function (err, value) { 38 | t.ifError(err) 39 | t.ok(value instanceof Buffer, 'value is buffer') 40 | t.is(value.toString(), 'binarydata') 41 | done() 42 | }) 43 | }) 44 | }) 45 | }) 46 | 47 | test('test put() and get() with binary value {keyEncoding:utf8,valueEncoding:binary}', function (t) { 48 | discardable(t, testCommon, function (db, done) { 49 | db.put('binarydata', testBuffer(), { keyEncoding: 'utf8', valueEncoding: 'binary' }, function (err) { 50 | t.ifError(err) 51 | db.get('binarydata', { keyEncoding: 'utf8', valueEncoding: 'binary' }, function (err, value) { 52 | t.ifError(err) 53 | t.ok(value) 54 | t.ok(value.equals(testBuffer())) 55 | done() 56 | }) 57 | }) 58 | }) 59 | }) 60 | 61 | test('test put() and get() with binary value {keyEncoding:utf8,valueEncoding:binary} in factory', function (t) { 62 | discardable(t, testCommon, { keyEncoding: 'utf8', valueEncoding: 'binary' }, function (db, done) { 63 | db.put('binarydata', testBuffer(), function (err) { 64 | t.ifError(err) 65 | db.get('binarydata', function (err, value) { 66 | t.ifError(err) 67 | t.ok(value) 68 | t.ok(value.equals(testBuffer())) 69 | done() 70 | }) 71 | }) 72 | }) 73 | }) 74 | 75 | testCommon.bufferKeys && test('test put() and get() with binary key {keyEncoding:binary,valueEncoding:utf8}', function (t) { 76 | discardable(t, testCommon, function (db, done) { 77 | db.put(testBuffer(), 'binarydata', { keyEncoding: 'binary', valueEncoding: 'utf8' }, function (err) { 78 | t.ifError(err) 79 | db.get(testBuffer(), { keyEncoding: 'binary', valueEncoding: 'utf8' }, function (err, value) { 80 | t.ifError(err) 81 | t.is(value, 'binarydata') 82 | done() 83 | }) 84 | }) 85 | }) 86 | }) 87 | 88 | testCommon.bufferKeys && test('test put() and get() with binary key & value {valueEncoding:binary}', function (t) { 89 | discardable(t, testCommon, function (db, done) { 90 | db.put(testBuffer(), testBuffer(), { valueEncoding: 'binary' }, function (err) { 91 | t.ifError(err) 92 | db.get(testBuffer(), { valueEncoding: 'binary' }, function (err, value) { 93 | t.ifError(err) 94 | t.ok(value.equals(testBuffer())) 95 | done() 96 | }) 97 | }) 98 | }) 99 | }) 100 | 101 | testCommon.bufferKeys && test('test put() and del() and get() with binary key {valueEncoding:binary}', function (t) { 102 | discardable(t, testCommon, function (db, done) { 103 | db.put(testBuffer(), 'binarydata', { valueEncoding: 'binary' }, function (err) { 104 | t.ifError(err) 105 | db.del(testBuffer(), { valueEncoding: 'binary' }, function (err) { 106 | t.ifError(err) 107 | db.get(testBuffer(), { valueEncoding: 'binary' }, function (err, value) { 108 | t.ok(err) 109 | t.notOk(value) 110 | done() 111 | }) 112 | }) 113 | }) 114 | }) 115 | }) 116 | 117 | test('batch() with multiple puts', function (t) { 118 | discardable(t, testCommon, function (db, done) { 119 | db.batch([ 120 | { type: 'put', key: 'foo', value: testBuffer() }, 121 | { type: 'put', key: 'bar', value: testBuffer() }, 122 | { type: 'put', key: 'baz', value: 'abazvalue' } 123 | ], { keyEncoding: 'utf8', valueEncoding: 'binary' }, function (err) { 124 | t.ifError(err) 125 | each(['foo', 'bar', 'baz'], function (key, next) { 126 | db.get(key, { valueEncoding: 'binary' }, function (err, value) { 127 | t.ifError(err) 128 | if (key === 'baz') { 129 | t.ok(value instanceof Buffer, 'value is buffer') 130 | t.is(value.toString(), 'a' + key + 'value') 131 | next() 132 | } else { 133 | t.ok(value.equals(testBuffer())) 134 | next() 135 | } 136 | }) 137 | }, done) 138 | }) 139 | }) 140 | }) 141 | } 142 | 143 | function testBuffer () { 144 | return Buffer.from('0080c0ff', 'hex') 145 | } 146 | -------------------------------------------------------------------------------- /test/browserify-test.js: -------------------------------------------------------------------------------- 1 | const browserify = require('browserify') 2 | const path = require('path') 3 | const after = require('after') 4 | const concat = require('simple-concat') 5 | const spawn = require('child_process').spawn 6 | const PACKAGE_JSON = path.join(__dirname, '..', 'package.json') 7 | 8 | module.exports = function (test) { 9 | test('does not contain package.json', function (t) { 10 | const b = browserify(path.join(__dirname, '..'), { browserField: true }) 11 | .once('error', t.end.bind(t)) 12 | b.pipeline 13 | .on('file', function (file, id, parent) { 14 | t.isNot(file, PACKAGE_JSON) 15 | }) 16 | b.bundle(t.end.bind(t)) 17 | }) 18 | 19 | test('throws error if missing db factory', function (t) { 20 | const b = browserify(path.join(__dirname, 'data/browser-throws.js'), { browserField: true }) 21 | const node = spawn('node') 22 | const next = after(2, t.end.bind(t)) 23 | 24 | concat(node.stderr, function (err, buf) { 25 | t.ifError(err) 26 | t.ok(/InitializationError: First argument must be an abstract-leveldown compliant store/.test(buf)) 27 | next() 28 | }) 29 | 30 | node.on('exit', function (code) { 31 | t.is(code, 1) 32 | next() 33 | }) 34 | 35 | b.bundle().pipe(node.stdin) 36 | }) 37 | 38 | test('works with valid db factory (memdown)', function (t) { 39 | const b = browserify(path.join(__dirname, 'data/browser-works.js'), { browserField: true }) 40 | const node = spawn('node') 41 | node.on('exit', function (code) { 42 | t.is(code, 0) 43 | t.end() 44 | }) 45 | b.bundle().pipe(node.stdin) 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /test/chained-batch-encoding-test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const concatIterator = require('level-concat-iterator') 4 | const discardable = require('./util/discardable') 5 | 6 | module.exports = function (test, testCommon) { 7 | test('chained batch with per-operation options', function (t) { 8 | discardable(t, testCommon, function (db, done) { 9 | let ops 10 | 11 | db.once('batch', function (o) { 12 | ops = o 13 | }) 14 | 15 | db.batch() 16 | .put('a', 'a', { valueEncoding: 'json' }) 17 | .put('b', 'b') 18 | .put('"c"', 'c') 19 | .del('c', { keyEncoding: 'json', arbitraryOption: true }) 20 | .write(function (err) { 21 | t.ifError(err, 'no write error') 22 | 23 | t.same(ops, [ 24 | { type: 'put', key: 'a', value: 'a', valueEncoding: 'json' }, 25 | { type: 'put', key: 'b', value: 'b' }, 26 | { type: 'put', key: '"c"', value: 'c' }, 27 | { type: 'del', key: 'c', keyEncoding: 'json', arbitraryOption: true } 28 | ]) 29 | 30 | concatIterator(db.iterator(), function (err, entries) { 31 | t.ifError(err) 32 | t.same(entries, [ 33 | { key: 'a', value: '"a"' }, 34 | { key: 'b', value: 'b' } 35 | ]) 36 | done() 37 | }) 38 | }) 39 | }) 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /test/clear-test.js: -------------------------------------------------------------------------------- 1 | const memdown = require('memdown') 2 | const encode = require('encoding-down') 3 | const concat = require('level-concat-iterator') 4 | const levelup = require('../lib/levelup') 5 | 6 | module.exports = function (test) { 7 | test('clear()', function (t) { 8 | function makeTest (name, fn) { 9 | t.test(name, function (t) { 10 | const mem = memdown() 11 | 12 | mem.open(function (err) { 13 | t.ifError(err, 'no open error') 14 | 15 | mem.batch([ 16 | { type: 'put', key: '"a"', value: 'a' }, 17 | { type: 'put', key: '"b"', value: 'b' } 18 | ], function (err) { 19 | t.ifError(err, 'no batch error') 20 | 21 | mem.close(function (err) { 22 | t.ifError(err, 'no close error') 23 | fn(t, mem) 24 | }) 25 | }) 26 | }) 27 | }) 28 | } 29 | 30 | function verify (t, db, expectedKey) { 31 | concat(db.iterator({ keyAsBuffer: false }), function (err, entries) { 32 | t.ifError(err, 'no concat error') 33 | t.same(entries.map(function (e) { return e.key }), [expectedKey], 'got expected keys') 34 | db.close(t.end.bind(t)) 35 | }) 36 | } 37 | 38 | makeTest('clear() without encoding, without deferred-open', function (t, mem) { 39 | const db = levelup(mem) 40 | 41 | db.open(function (err) { 42 | t.ifError(err) 43 | 44 | db.clear({ gte: '"b"' }, function (err) { 45 | t.ifError(err, 'no clear error') 46 | verify(t, db, '"a"') 47 | }) 48 | }) 49 | }) 50 | 51 | makeTest('clear() without encoding, with deferred-open', function (t, mem) { 52 | const db = levelup(mem) 53 | 54 | db.clear({ gte: '"b"' }, function (err) { 55 | t.ifError(err, 'no clear error') 56 | verify(t, db, '"a"') 57 | }) 58 | }) 59 | 60 | makeTest('clear() with encoding, with deferred-open', function (t, mem) { 61 | const db = levelup(encode(mem, { keyEncoding: 'json' })) 62 | 63 | db.clear({ gte: 'b' }, function (err) { 64 | t.ifError(err, 'no clear error') 65 | verify(t, db, 'a') 66 | }) 67 | }) 68 | 69 | makeTest('clear() with encoding, without deferred-open', function (t, mem) { 70 | const db = levelup(encode(mem, { keyEncoding: 'json' })) 71 | 72 | db.open(function (err) { 73 | t.ifError(err) 74 | 75 | db.clear({ gte: 'b' }, function (err) { 76 | t.ifError(err, 'no clear error') 77 | verify(t, db, 'a') 78 | }) 79 | }) 80 | }) 81 | 82 | t.end() 83 | }) 84 | } 85 | -------------------------------------------------------------------------------- /test/common.js: -------------------------------------------------------------------------------- 1 | // Same interface as abstract-leveldown's testCommon 2 | module.exports = function testCommon (options) { 3 | const factory = options.factory 4 | const test = options.test 5 | 6 | if (typeof factory !== 'function') { 7 | throw new TypeError('factory must be a function') 8 | } 9 | 10 | if (typeof test !== 'function') { 11 | throw new TypeError('test must be a function') 12 | } 13 | 14 | return { 15 | test: test, 16 | factory: factory, 17 | 18 | bufferKeys: options.bufferKeys !== false, 19 | // createIfMissing: options.createIfMissing !== false, 20 | // errorIfExists: options.errorIfExists !== false, 21 | snapshots: options.snapshots !== false, 22 | seek: options.seek !== false, 23 | clear: !!options.clear, 24 | deferredOpen: !!options.deferredOpen, 25 | promises: !!options.promises, 26 | streams: !!options.streams, 27 | encodings: !!options.encodings 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/create-stream-vs-put-racecondition.js: -------------------------------------------------------------------------------- 1 | const levelup = require('../lib/levelup') 2 | const nextTick = require('../lib/next-tick') 3 | const memdown = require('memdown') 4 | const encdown = require('encoding-down') 5 | const after = require('after') 6 | 7 | module.exports = function (test, testCommon) { 8 | ;[true, false].forEach(function (encode) { 9 | ;[true, false].forEach(function (deferredOpen) { 10 | ;[true, false].forEach(function (delayedPut) { 11 | makeTest(test, encode, deferredOpen, delayedPut) 12 | }) 13 | }) 14 | }) 15 | } 16 | 17 | function makeTest (test, encode, deferredOpen, delayedPut) { 18 | const name = [ 19 | 'readStream before put', 20 | encode && 'encode', 21 | deferredOpen && 'deferred open', 22 | delayedPut && 'delayed put' 23 | ].filter(Boolean).join(', ') 24 | 25 | test(name, function (t) { 26 | const db = encode ? levelup(encdown(memdown())) : levelup(memdown()) 27 | const delay = delayedPut ? nextTick : callFn 28 | 29 | run(t, db, !deferredOpen, delay) 30 | }) 31 | } 32 | 33 | function run (t, db, explicitOpen, delay) { 34 | if (explicitOpen) { 35 | return db.open(function (err) { 36 | t.ifError(err, 'no open error') 37 | run(t, db, false, delay) 38 | }) 39 | } 40 | 41 | let reads = 0 42 | const next = after(11, function (err) { 43 | t.ifError(err, 'no error') 44 | t.is(reads, 0, 'got 0 items from snaphot') 45 | 46 | db.close(function (err) { 47 | t.ifError(err, 'no close error') 48 | t.end() 49 | }) 50 | }) 51 | 52 | // Should read from a snapshot, unaffected by later writes, 53 | // even if those are performed in the same tick. 54 | db.createReadStream() 55 | .on('data', function () { 56 | reads++ 57 | }) 58 | .on('end', next) 59 | 60 | // Write data 61 | delay(function () { 62 | for (let i = 0; i < 10; i++) { 63 | db.put(String(i), String(i), next) 64 | } 65 | }) 66 | } 67 | 68 | function callFn (fn) { 69 | fn() 70 | } 71 | -------------------------------------------------------------------------------- /test/custom-encoding-test.js: -------------------------------------------------------------------------------- 1 | const each = require('async-each') 2 | const discardable = require('./util/discardable') 3 | 4 | module.exports = function (test, testCommon) { 5 | test('custom encoding: simple-object values in "json" encoding', function (t) { 6 | run(t, [ 7 | { key: '0', value: 0 }, 8 | { key: '1', value: 1 }, 9 | { key: 'string', value: 'a string' }, 10 | { key: 'true', value: true }, 11 | { key: 'false', value: false } 12 | ]) 13 | }) 14 | 15 | test('custom encoding: simple-object keys in "json" encoding', function (t) { 16 | // Test keys that would be considered the same with default utf8 encoding. 17 | // Because String([1]) === String(1). 18 | run(t, [ 19 | { value: '0', key: [1] }, 20 | { value: '1', key: 1 }, 21 | { value: 'string', key: 'a string' }, 22 | { value: 'true', key: true }, 23 | { value: 'false', key: false } 24 | ]) 25 | }) 26 | 27 | test('custom encoding: complex-object values in "json" encoding', function (t) { 28 | run(t, [ 29 | { 30 | key: '0', 31 | value: { 32 | foo: 'bar', 33 | bar: [1, 2, 3], 34 | bang: { yes: true, no: false } 35 | } 36 | } 37 | ]) 38 | }) 39 | 40 | test('custom encoding: complex-object keys in "json" encoding', function (t) { 41 | // Test keys that would be considered the same with default utf8 encoding. 42 | // Because String({}) === String({}) === '[object Object]'. 43 | run(t, [ 44 | { 45 | value: '0', 46 | key: { 47 | foo: 'bar', 48 | bar: [1, 2, 3], 49 | bang: { yes: true, no: false } 50 | } 51 | }, 52 | { 53 | value: '1', 54 | key: { 55 | foo: 'different', 56 | bar: [1, 2, 3], 57 | bang: { yes: true, no: false } 58 | } 59 | } 60 | ]) 61 | }) 62 | 63 | function run (t, entries) { 64 | const customEncoding = { 65 | encode: JSON.stringify, 66 | decode: JSON.parse, 67 | buffer: false, 68 | type: 'custom' 69 | } 70 | 71 | discardable(t, testCommon, { 72 | keyEncoding: customEncoding, 73 | valueEncoding: customEncoding 74 | }, function (db, done) { 75 | const ops = entries.map(function (entry) { 76 | return { type: 'put', key: entry.key, value: entry.value } 77 | }) 78 | 79 | db.batch(ops, function (err) { 80 | t.ifError(err) 81 | each(entries, visit, done) 82 | 83 | function visit (entry, next) { 84 | db.get(entry.key, function (err, value) { 85 | t.ifError(err) 86 | t.same(entry.value, value) 87 | next() 88 | }) 89 | } 90 | }) 91 | }) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /test/data/browser-throws.js: -------------------------------------------------------------------------------- 1 | require('../..')() 2 | -------------------------------------------------------------------------------- /test/data/browser-works.js: -------------------------------------------------------------------------------- 1 | require('../..')(require('memdown')()) 2 | -------------------------------------------------------------------------------- /test/deferred-open-test.js: -------------------------------------------------------------------------------- 1 | const each = require('async-each') 2 | const parallel = require('run-parallel') 3 | const concat = require('concat-stream') 4 | const readStreamContext = require('./util/rs-context') 5 | const rsFactory = require('./util/rs-factory') 6 | 7 | module.exports = function (test, testCommon) { 8 | const createReadStream = rsFactory(testCommon) 9 | 10 | test('deferred open(): put() and get() on new database', function (t) { 11 | // 1) open database without callback, opens in next tick 12 | const db = testCommon.factory() 13 | 14 | parallel([ 15 | // 2) insert 3 values with put(), these should be deferred until the database is actually open 16 | db.put.bind(db, 'k1', 'v1'), 17 | db.put.bind(db, 'k2', 'v2'), 18 | db.put.bind(db, 'k3', 'v3') 19 | ], function () { 20 | // 3) when the callbacks have returned, the database should be open and those values should be in 21 | // verify that the values are there 22 | each([1, 2, 3], function (k, next) { 23 | db.get('k' + k, { asBuffer: false }, function (err, v) { 24 | t.ifError(err) 25 | t.is(v, 'v' + k) 26 | next() 27 | }) 28 | }, function () { 29 | db.get('k4', { asBuffer: false }, function (err) { 30 | t.ok(err) 31 | db.close(t.end.bind(t)) 32 | }) 33 | }) 34 | }) 35 | 36 | t.is(db.status, 'opening') 37 | }) 38 | 39 | test('deferred open(): put() and get() on reopened database', async function (t) { 40 | const db = testCommon.factory() 41 | 42 | await db.close() 43 | t.is(db.status, 'closed') 44 | 45 | db.open(() => {}) 46 | t.is(db.status, 'opening') 47 | 48 | await db.put('beep', 'boop') 49 | 50 | t.is(db.status, 'open') 51 | t.is(await db.get('beep', { asBuffer: false }), 'boop') 52 | 53 | await db.close() 54 | }) 55 | 56 | test('deferred open(): batch() on new database', function (t) { 57 | // 1) open database without callback, opens in next tick 58 | const db = testCommon.factory() 59 | 60 | // 2) insert 3 values with batch(), these should be deferred until the database is actually open 61 | db.batch([ 62 | { type: 'put', key: 'k1', value: 'v1' }, 63 | { type: 'put', key: 'k2', value: 'v2' }, 64 | { type: 'put', key: 'k3', value: 'v3' } 65 | ], function () { 66 | // 3) when the callbacks have returned, the database should be open and those values should be in 67 | // verify that the values are there 68 | each([1, 2, 3], function (k, next) { 69 | db.get('k' + k, { asBuffer: false }, function (err, v) { 70 | t.ifError(err) 71 | t.is(v, 'v' + k) 72 | next() 73 | }) 74 | }, function () { 75 | db.get('k4', { asBuffer: false }, function (err) { 76 | t.ok(err) 77 | db.close(t.end.bind(t)) 78 | }) 79 | }) 80 | }) 81 | 82 | t.is(db.status, 'opening') 83 | }) 84 | 85 | test('deferred open(): chained batch() on new database', function (t) { 86 | // 1) open database without callback, opens in next tick 87 | const db = testCommon.factory() 88 | 89 | // 2) insert 3 values with batch(), these should be deferred until the database is actually open 90 | db.batch() 91 | .put('k1', 'v1') 92 | .put('k2', 'v2') 93 | .put('k3', 'v3') 94 | .write(function () { 95 | // 3) when the callbacks have returned, the database should be open and those values should be in 96 | // verify that the values are there 97 | each([1, 2, 3], function (k, next) { 98 | db.get('k' + k, { asBuffer: false }, function (err, v) { 99 | t.ifError(err) 100 | t.is(v, 'v' + k) 101 | next() 102 | }) 103 | }, function () { 104 | db.get('k4', { asBuffer: false }, function (err) { 105 | t.ok(err) 106 | db.close(t.end.bind(t)) 107 | }) 108 | }) 109 | }) 110 | 111 | t.is(db.status, 'opening') 112 | }) 113 | 114 | testCommon.streams && test('deferred open(): test deferred ReadStream', function (t) { 115 | const ctx = readStreamContext(t) 116 | const db = testCommon.factory() 117 | 118 | db.batch(ctx.sourceData.slice(), function (err) { 119 | t.ifError(err) 120 | db.close(function (err) { 121 | t.ifError(err, 'no error') 122 | let async = true 123 | 124 | db.open(function (err) { 125 | async = false 126 | t.ifError(err, 'no open error') 127 | }) 128 | 129 | createReadStream(db) 130 | .on('data', ctx.dataSpy) 131 | .on('end', ctx.endSpy) 132 | .on('close', function () { 133 | ctx.verify() 134 | db.close(t.end.bind(t)) 135 | }) 136 | 137 | // db should open lazily 138 | t.ok(async) 139 | }) 140 | }) 141 | }) 142 | 143 | test('deferred open(): no maxListeners warning', function (t) { 144 | // 1) open database without callback, opens in next tick 145 | const db = testCommon.factory() 146 | const fail = t.fail.bind(t) 147 | 148 | process.on('warning', fail) 149 | 150 | // 2) provoke an EventEmitter maxListeners warning 151 | let toPut = 11 152 | 153 | for (let i = 0; i < toPut; i++) { 154 | db.put('some', 'string', function (err) { 155 | t.ifError(err) 156 | if (!--toPut) { 157 | process.removeListener('warning', fail) 158 | db.close(t.end.bind(t)) 159 | } 160 | }) 161 | } 162 | }) 163 | 164 | testCommon.encodings && test('deferred open(): value of queued operation is not serialized', function (t) { 165 | const db = testCommon.factory({ valueEncoding: 'json' }) 166 | 167 | // deferred-leveldown < 2.0.2 would serialize the object to a string. 168 | db.put('key', { thing: 2 }, function (err) { 169 | t.ifError(err) 170 | 171 | db.get('key', function (err, value) { 172 | t.ifError(err) 173 | t.same(value, { thing: 2 }) 174 | db.close(t.end.bind(t)) 175 | }) 176 | }) 177 | }) 178 | 179 | testCommon.encodings && test('deferred open(): key of queued operation is not serialized', function (t) { 180 | const db = testCommon.factory({ keyEncoding: 'json' }) 181 | 182 | // deferred-leveldown < 2.0.2 would serialize the key to a string. 183 | db.put({ thing: 2 }, 'value', function (err) { 184 | t.ifError(err) 185 | 186 | db.createKeyStream().pipe(concat(function (result) { 187 | t.same(result, [{ thing: 2 }]) 188 | db.close(t.end.bind(t)) 189 | })) 190 | }) 191 | }) 192 | } 193 | -------------------------------------------------------------------------------- /test/get-put-del-test.js: -------------------------------------------------------------------------------- 1 | const errors = require('../lib/levelup').errors 2 | const each = require('async-each') 3 | const series = require('run-series') 4 | const discardable = require('./util/discardable') 5 | 6 | module.exports = function (test, testCommon) { 7 | test('get() / put() / del(): get() on empty database causes error', function (t) { 8 | discardable(t, testCommon, function (db, done) { 9 | db.get('undefkey', function (err, value) { 10 | t.notOk(value) 11 | t.ok(err instanceof Error) 12 | t.ok(err instanceof errors.LevelUPError) 13 | t.ok(err instanceof errors.NotFoundError) 14 | t.is(err.notFound, true, 'err.notFound is `true`') 15 | t.is(err.status, 404, 'err.status is 404') 16 | t.ok(/\[undefkey\]/.test(err)) 17 | done() 18 | }) 19 | }) 20 | }) 21 | 22 | testCommon.promises && test('get() / put() / del(): get() on empty database causes error (promise)', function (t) { 23 | discardable(t, testCommon, function (db, done) { 24 | db.get('undefkey').catch(function (err) { 25 | t.ok(err instanceof Error) 26 | t.ok(err instanceof errors.LevelUPError) 27 | t.ok(err instanceof errors.NotFoundError) 28 | t.is(err.notFound, true, 'err.notFound is `true`') 29 | t.is(err.status, 404, 'err.status is 404') 30 | t.ok(/\[undefkey\]/.test(err)) 31 | done() 32 | }) 33 | }) 34 | }) 35 | 36 | test('get() / put() / del(): put() and get() simple string entries', function (t) { 37 | discardable(t, testCommon, function (db, done) { 38 | db.put('some key', 'some value stored in the database', function (err) { 39 | t.ifError(err) 40 | db.get('some key', { asBuffer: false }, function (err, value) { 41 | t.ifError(err) 42 | t.is(value, 'some value stored in the database') 43 | done() 44 | }) 45 | }) 46 | }) 47 | }) 48 | 49 | testCommon.promises && test('get() / put() / del(): put() and get() simple string entries (promise)', function (t) { 50 | discardable(t, testCommon, function (db, done) { 51 | db.put('some key', 'some value stored in the database') 52 | .then(function () { 53 | return db.get('some key', { asBuffer: false }) 54 | }) 55 | .then(function (value) { 56 | t.is(value, 'some value stored in the database') 57 | done() 58 | }) 59 | .catch(done) 60 | }) 61 | }) 62 | 63 | test('get() / put() / del(): can del() on empty database', function (t) { 64 | discardable(t, testCommon, function (db, done) { 65 | db.del('undefkey', function (err) { 66 | t.ifError(err) 67 | done() 68 | }) 69 | }) 70 | }) 71 | 72 | testCommon.promises && test('get() / put() / del(): can del() on empty database (promise)', function (t) { 73 | discardable(t, testCommon, function (db, done) { 74 | db.del('undefkey') 75 | .then(done) 76 | .catch(done) 77 | }) 78 | }) 79 | 80 | test('get() / put() / del(): del() works on real entries', function (t) { 81 | discardable(t, testCommon, function (db, done) { 82 | series([ 83 | function (next) { 84 | each(['foo', 'bar', 'baz'], function (key, next) { 85 | db.put(key, 1 + Math.random(), next) 86 | }, next) 87 | }, 88 | function (next) { 89 | db.del('bar', next) 90 | }, 91 | function (next) { 92 | each(['foo', 'bar', 'baz'], function (key, next) { 93 | db.get(key, { asBuffer: false }, function (err, value) { 94 | // we should get foo & baz but not bar 95 | if (key === 'bar') { 96 | t.ok(err) 97 | t.notOk(value) 98 | } else { 99 | t.ifError(err) 100 | t.ok(value) 101 | } 102 | 103 | next() 104 | }) 105 | }, next) 106 | } 107 | ], done) 108 | }) 109 | }) 110 | } 111 | -------------------------------------------------------------------------------- /test/idempotent-test.js: -------------------------------------------------------------------------------- 1 | const levelup = require('../lib/levelup.js') 2 | const nextTick = require('../lib/next-tick') 3 | const memdown = require('memdown') 4 | const sinon = require('sinon') 5 | 6 | module.exports = function (test, testCommon) { 7 | test('call open twice, should emit "open" once', function (t) { 8 | let n = 0 9 | let m = 0 10 | 11 | const close = function () { 12 | const closing = sinon.spy() 13 | db.on('closing', closing) 14 | db.on('closed', function () { 15 | t.is(closing.callCount, 1) 16 | t.same(closing.getCall(0).args, []) 17 | t.end() 18 | }) 19 | 20 | // close needs to be idempotent too. 21 | db.close() 22 | nextTick(db.close.bind(db)) 23 | } 24 | 25 | const db = levelup(memdown(), function () { 26 | t.is(n++, 0, 'callback should fire only once') 27 | if (n && m) { close() } 28 | }) 29 | 30 | db.on('open', function () { 31 | t.is(m++, 0, 'callback should fire only once') 32 | if (n && m) { close() } 33 | }) 34 | 35 | db.open() 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const common = require('./common') 4 | 5 | function suite (options) { 6 | const testCommon = common(options) 7 | const test = testCommon.test 8 | 9 | require('./manifest-test')(test, testCommon) 10 | require('./batch-test')(test, testCommon) 11 | if (testCommon.encodings) require('./binary-test')(test, testCommon) 12 | if (testCommon.clear) require('./clear-test')(test) 13 | if (testCommon.snapshots) require('./create-stream-vs-put-racecondition')(test, testCommon) 14 | if (testCommon.deferredOpen) require('./deferred-open-test')(test, testCommon) 15 | require('./get-put-del-test')(test, testCommon) 16 | require('./idempotent-test')(test, testCommon) 17 | require('./init-test')(test, testCommon) 18 | if (testCommon.encodings) require('./custom-encoding-test')(test, testCommon) 19 | if (testCommon.encodings) require('./json-encoding-test')(test, testCommon) 20 | if (testCommon.encodings) require('./chained-batch-encoding-test')(test, testCommon) 21 | if (testCommon.streams) require('./key-value-streams-test')(test, testCommon) 22 | require('./maybe-error-test')(test, testCommon) 23 | require('./no-encoding-test')(test, testCommon) 24 | require('./null-and-undefined-test')(test, testCommon) 25 | if (testCommon.deferredOpen) require('./open-patchsafe-test')(test, testCommon) 26 | if (testCommon.streams) require('./read-stream-test')(test, testCommon) 27 | if (testCommon.snapshots && testCommon.streams) require('./snapshot-test')(test, testCommon) 28 | require('./iterator-test')(test, testCommon) 29 | if (testCommon.seek) require('./iterator-seek-test')(test, testCommon) 30 | } 31 | 32 | suite.common = common 33 | module.exports = suite 34 | -------------------------------------------------------------------------------- /test/init-test.js: -------------------------------------------------------------------------------- 1 | const levelup = require('../lib/levelup') 2 | const nextTick = require('../lib/next-tick') 3 | const memdown = require('memdown') 4 | 5 | module.exports = function (test, testCommon) { 6 | test('Init & open(): levelup()', function (t) { 7 | t.is(typeof levelup, 'function') 8 | 9 | // db, options & callback arguments 10 | t.is(levelup.length, 3) 11 | 12 | // no db 13 | throws(t, levelup, function (err) { 14 | return /^InitializationError/.test(err) 15 | }) 16 | 17 | t.end() 18 | }) 19 | 20 | test('Init & open(): open and close statuses', function (t) { 21 | levelup(memdown(), function (err, db) { 22 | t.ifError(err, 'no error') 23 | t.is(db.isOpen(), true) 24 | 25 | db.close(function (err) { 26 | t.ifError(err) 27 | 28 | t.is(db.isOpen(), false) 29 | t.is(db.isClosed(), true) 30 | 31 | levelup(memdown(), function (err, db) { 32 | t.ifError(err) 33 | t.ok(typeof db === 'object' && db !== null) 34 | 35 | db.close(t.end.bind(t)) 36 | }) 37 | }) 38 | }) 39 | }) 40 | 41 | test('Init & open(): without callback', function (t) { 42 | const db = levelup(memdown()) 43 | t.ok(typeof db === 'object' && db !== null) 44 | db.on('ready', function () { 45 | t.is(db.isOpen(), true) 46 | db.close(t.end.bind(t)) 47 | }) 48 | }) 49 | 50 | test('Init & open(): error with callback', function (t) { 51 | t.plan(1) 52 | 53 | const mem = memdown() 54 | mem._open = function (opts, cb) { 55 | nextTick(cb, new Error('from underlying store')) 56 | } 57 | 58 | levelup(mem, function (err) { 59 | t.is(err.message, 'from underlying store') 60 | }).on('open', function () { 61 | t.fail('should not finish opening') 62 | }).on('error', function () { 63 | t.fail('should not emit error') 64 | }) 65 | }) 66 | 67 | test('Init & open(): error without callback', function (t) { 68 | t.plan(1) 69 | 70 | const mem = memdown() 71 | mem._open = function (opts, cb) { 72 | nextTick(cb, new Error('from underlying store')) 73 | } 74 | 75 | levelup(mem) 76 | .on('open', function () { 77 | t.fail('should not finish opening') 78 | }) 79 | .on('error', function (err) { 80 | t.is(err.message, 'from underlying store') 81 | }) 82 | }) 83 | 84 | test('Init & open(): validate abstract-leveldown', function (t) { 85 | t.plan(1) 86 | 87 | const down = memdown() 88 | 89 | Object.defineProperty(down, 'status', { 90 | get: function () { return null }, 91 | set: function () {} 92 | }) 93 | 94 | try { 95 | levelup(down) 96 | } catch (err) { 97 | t.is(err.message, '.status required, old abstract-leveldown') 98 | } 99 | }) 100 | 101 | test('Init & open(): support open options', function (t) { 102 | const down = memdown() 103 | 104 | levelup(down, function (err, up) { 105 | t.ifError(err, 'no error') 106 | 107 | up.close(function () { 108 | down.open = function (opts) { 109 | t.is(opts.foo, 'bar') 110 | t.end() 111 | } 112 | 113 | up.open({ foo: 'bar' }) 114 | }) 115 | }) 116 | }) 117 | } 118 | 119 | function throws (t, fn, verify) { 120 | try { 121 | fn() 122 | } catch (err) { 123 | return verify(err) 124 | } 125 | 126 | t.fail('did not throw') 127 | } 128 | -------------------------------------------------------------------------------- /test/iterator-seek-test.js: -------------------------------------------------------------------------------- 1 | const memdown = require('memdown') 2 | const encode = require('encoding-down') 3 | const levelup = require('../lib/levelup') 4 | 5 | module.exports = function (test, testCommon) { 6 | test('iterator#seek()', function (t) { 7 | const mem = memdown() 8 | 9 | t.test('setup', function (t) { 10 | mem.open(function (err) { 11 | t.ifError(err, 'no open error') 12 | mem.batch([ 13 | { type: 'put', key: '"a"', value: 'a' }, 14 | { type: 'put', key: '"b"', value: 'b' } 15 | ], function (err) { 16 | t.ifError(err, 'no batch error') 17 | mem.close(t.end.bind(t)) 18 | }) 19 | }) 20 | }) 21 | 22 | t.test('without encoding, without deferred-open', function (t) { 23 | const db = levelup(mem) 24 | 25 | db.open(function (err) { 26 | t.ifError(err, 'no open error') 27 | 28 | const it = db.iterator({ keyAsBuffer: false }) 29 | 30 | it.seek('"b"') 31 | it.next(function (err, key, value) { 32 | t.ifError(err, 'no next error') 33 | t.is(key, '"b"') 34 | it.end(function (err) { 35 | t.ifError(err, 'no end error') 36 | db.close(t.end.bind(t)) 37 | }) 38 | }) 39 | }) 40 | }) 41 | 42 | t.test('without encoding, with deferred-open', function (t) { 43 | const db = levelup(mem) 44 | const it = db.iterator({ keyAsBuffer: false }) 45 | 46 | it.seek('"b"') 47 | it.next(function (err, key, value) { 48 | t.ifError(err, 'no next error') 49 | t.is(key, '"b"') 50 | it.end(function (err) { 51 | t.ifError(err, 'no end error') 52 | db.close(t.end.bind(t)) 53 | }) 54 | }) 55 | }) 56 | 57 | t.test('with encoding, with deferred-open', function (t) { 58 | const db = levelup(encode(mem, { keyEncoding: 'json' })) 59 | const it = db.iterator() 60 | 61 | it.seek('b') 62 | it.next(function (err, key, value) { 63 | t.ifError(err, 'no next error') 64 | t.is(key, 'b') 65 | it.end(function (err) { 66 | t.ifError(err, 'no end error') 67 | db.close(t.end.bind(t)) 68 | }) 69 | }) 70 | }) 71 | 72 | t.test('with encoding, without deferred-open', function (t) { 73 | const db = levelup(encode(mem, { keyEncoding: 'json' })) 74 | 75 | db.open(function (err) { 76 | t.ifError(err, 'no open error') 77 | 78 | const it = db.iterator() 79 | 80 | it.seek('b') 81 | it.next(function (err, key, value) { 82 | t.ifError(err, 'no next error') 83 | t.is(key, 'b') 84 | it.end(function (err) { 85 | t.ifError(err, 'no end error') 86 | db.close(t.end.bind(t)) 87 | }) 88 | }) 89 | }) 90 | }) 91 | 92 | t.end() 93 | }) 94 | } 95 | -------------------------------------------------------------------------------- /test/iterator-test.js: -------------------------------------------------------------------------------- 1 | const memdown = require('memdown') 2 | const levelup = require('../lib/levelup') 3 | 4 | module.exports = function (test, testCommon) { 5 | test('simple iterator without encoding-down', function (t) { 6 | const db = levelup(memdown()) 7 | 8 | db.put('key', 'value', function (err) { 9 | t.ifError(err, 'no put error') 10 | 11 | const it = db.iterator({ 12 | keyAsBuffer: false, 13 | valueAsBuffer: false 14 | }) 15 | 16 | it.next(function (err, key, value) { 17 | t.ifError(err, 'no next error') 18 | t.is(key, 'key') 19 | t.is(value, 'value') 20 | 21 | it.end(function (err) { 22 | t.ifError(err, 'no end error') 23 | db.close(t.end.bind(t)) 24 | }) 25 | }) 26 | }) 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /test/json-encoding-test.js: -------------------------------------------------------------------------------- 1 | const each = require('async-each') 2 | const parallel = require('run-parallel') 3 | const concatStream = require('concat-stream') 4 | const concatIterator = require('level-concat-iterator') 5 | const discardable = require('./util/discardable') 6 | 7 | module.exports = function (test, testCommon) { 8 | test('json encoding: simple-object values in "json" encoding', function (t) { 9 | run(t, [ 10 | { key: '0', value: 0 }, 11 | { key: '1', value: 1 }, 12 | { key: '2', value: 'a string' }, 13 | { key: '3', value: true }, 14 | { key: '4', value: false } 15 | ]) 16 | }) 17 | 18 | test('json encoding: simple-object keys in "json" encoding', function (t) { 19 | run(t, [ 20 | { value: 'string', key: 'a string' }, 21 | { value: '0', key: 0 }, 22 | { value: '1', key: 1 }, 23 | { value: 'false', key: false }, 24 | { value: 'true', key: true } 25 | ]) 26 | }) 27 | 28 | test('json encoding: complex-object values in "json" encoding', function (t) { 29 | run(t, [ 30 | { 31 | key: '0', 32 | value: { 33 | foo: 'bar', 34 | bar: [1, 2, 3], 35 | bang: { yes: true, no: false } 36 | } 37 | } 38 | ]) 39 | }) 40 | 41 | test('json encoding: complex-object keys in "json" encoding', function (t) { 42 | run(t, [ 43 | { 44 | value: '0', 45 | key: { 46 | foo: 'bar', 47 | bar: [1, 2, 3], 48 | bang: { yes: true, no: false } 49 | } 50 | } 51 | ]) 52 | }) 53 | 54 | function run (t, entries) { 55 | discardable(t, testCommon, { 56 | keyEncoding: 'json', 57 | valueEncoding: 'json' 58 | }, function (db, done) { 59 | const ops = entries.map(function (entry) { 60 | return { type: 'put', key: entry.key, value: entry.value } 61 | }) 62 | 63 | db.batch(ops, function (err) { 64 | t.ifError(err) 65 | 66 | parallel([testGet, testStream, testIterator], function (err) { 67 | t.ifError(err) 68 | done() 69 | }) 70 | }) 71 | 72 | function testGet (next) { 73 | each(entries, function (entry, next) { 74 | db.get(entry.key, function (err, value) { 75 | t.ifError(err) 76 | t.same(entry.value, value) 77 | next() 78 | }) 79 | }, next) 80 | } 81 | 82 | function testStream (next) { 83 | db.createReadStream().pipe(concatStream(function (result) { 84 | t.same(result, entries) 85 | next() 86 | })) 87 | } 88 | 89 | function testIterator (next) { 90 | concatIterator(db.iterator(), function (err, result) { 91 | t.ifError(err) 92 | t.same(result, entries) 93 | next() 94 | }) 95 | } 96 | }) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /test/key-value-streams-test.js: -------------------------------------------------------------------------------- 1 | const sinon = require('sinon') 2 | const discardable = require('./util/discardable') 3 | 4 | module.exports = function (test, testCommon) { 5 | test('key and value streams: keyStream()', function (t) { 6 | const ctx = createContext(t) 7 | 8 | discardable(t, testCommon, function (db, done) { 9 | db.batch(ctx.sourceData.slice(), function (err) { 10 | t.ifError(err) 11 | 12 | const rs = db.keyStream() 13 | rs.on('data', ctx.dataSpy) 14 | rs.on('end', ctx.endSpy) 15 | rs.on('close', function () { 16 | ctx.verify(ctx.sourceKeys) 17 | done() 18 | }) 19 | }) 20 | }) 21 | }) 22 | 23 | test('key and value streams: readStream({keys:true,values:false})', function (t) { 24 | const ctx = createContext(t) 25 | 26 | discardable(t, testCommon, function (db, done) { 27 | db.batch(ctx.sourceData.slice(), function (err) { 28 | t.ifError(err) 29 | 30 | const rs = db.readStream({ keys: true, values: false }) 31 | rs.on('data', ctx.dataSpy) 32 | rs.on('end', ctx.endSpy) 33 | rs.on('close', function () { 34 | ctx.verify(ctx.sourceKeys) 35 | done() 36 | }) 37 | }) 38 | }) 39 | }) 40 | 41 | test('key and value streams: valueStream()', function (t) { 42 | const ctx = createContext(t) 43 | 44 | discardable(t, testCommon, function (db, done) { 45 | db.batch(ctx.sourceData.slice(), function (err) { 46 | t.ifError(err) 47 | 48 | const rs = db.valueStream() 49 | rs.on('data', ctx.dataSpy) 50 | rs.on('end', ctx.endSpy) 51 | rs.on('close', function () { 52 | ctx.verify(ctx.sourceValues) 53 | done() 54 | }) 55 | }) 56 | }) 57 | }) 58 | 59 | test('key and value streams: readStream({keys:false,values:true})', function (t) { 60 | const ctx = createContext(t) 61 | 62 | discardable(t, testCommon, function (db, done) { 63 | db.batch(ctx.sourceData.slice(), function (err) { 64 | t.ifError(err) 65 | 66 | const rs = db.readStream({ keys: false, values: true }) 67 | rs.on('data', ctx.dataSpy) 68 | rs.on('end', ctx.endSpy) 69 | rs.on('close', function () { 70 | ctx.verify(ctx.sourceValues) 71 | done() 72 | }) 73 | }) 74 | }) 75 | }) 76 | } 77 | 78 | function createContext (t) { 79 | const ctx = {} 80 | 81 | ctx.dataSpy = sinon.spy() 82 | ctx.endSpy = sinon.spy() 83 | ctx.sourceData = [] 84 | 85 | for (let i = 0; i < 100; i++) { 86 | const k = (i < 10 ? '0' : '') + i 87 | ctx.sourceData.push({ 88 | type: 'put', 89 | key: k, 90 | value: Math.random() 91 | }) 92 | } 93 | 94 | ctx.sourceKeys = Object.keys(ctx.sourceData) 95 | .map(function (k) { return ctx.sourceData[k].key }) 96 | ctx.sourceValues = Object.keys(ctx.sourceData) 97 | .map(function (k) { return ctx.sourceData[k].value }) 98 | 99 | ctx.verify = function (data) { 100 | t.is(ctx.endSpy.callCount, 1, 'stream emitted single "end" event') 101 | t.is(ctx.dataSpy.callCount, data.length, 'stream emitted correct number of "data" events') 102 | 103 | data.forEach(function (d, i) { 104 | const call = ctx.dataSpy.getCall(i) 105 | 106 | if (call) { 107 | t.is(call.args.length, 1, 'stream "data" event #' + i + ' fired with 1 argument') 108 | t.is(+call.args[0].toString(), +d, 'stream correct "data" event #' + i + ': ' + d) 109 | } 110 | }) 111 | } 112 | 113 | return ctx 114 | } 115 | -------------------------------------------------------------------------------- /test/manifest-test.js: -------------------------------------------------------------------------------- 1 | const suite = require('level-supports/test') 2 | 3 | module.exports = function (test, testCommon) { 4 | suite(test, testCommon) 5 | 6 | // TODO: add integration tests. 7 | test('manifest has expected properties', function (t) { 8 | const db = testCommon.factory() 9 | 10 | t.is(db.supports.status, true) 11 | t.is(db.supports.deferredOpen, true) 12 | t.is(db.supports.openCallback, true) 13 | t.is(db.supports.promises, true) 14 | t.is(db.supports.streams, true) 15 | 16 | db.close(t.end.bind(t)) 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /test/maybe-error-test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const discardable = require('./util/discardable') 4 | 5 | module.exports = function (test, testCommon) { 6 | test('maybeError() should be called async: put()', function (t) { 7 | discardable(t, testCommon, function (db, done) { 8 | db.close(function () { 9 | t.is(db.isClosed(), true, 'db is closed') 10 | let sync = false 11 | db.put('key', 'value', {}, function (err) { 12 | sync = true 13 | t.ok(err) 14 | t.is(err.message, 'Database is not open') 15 | }) 16 | t.is(sync, false, '.put cb called asynchronously') 17 | done() // TODO: called too soon, we still have 2 pending assertions 18 | }) 19 | }) 20 | }) 21 | 22 | test('maybeError() should be called async: get()', function (t) { 23 | discardable(t, testCommon, function (db, done) { 24 | db.put('key', 'value', {}, function (err) { 25 | t.ifError(err) 26 | db.close(function () { 27 | t.is(db.isClosed(), true, 'db is closed') 28 | let sync = false 29 | db.get('key', {}, function (err, value) { 30 | sync = true 31 | t.ok(err) 32 | t.is(err.message, 'Database is not open') 33 | }) 34 | t.is(sync, false, '.get cb called asynchronously') 35 | done() 36 | }) 37 | }) 38 | }) 39 | }) 40 | 41 | test('maybeError() should be called async: del()', function (t) { 42 | discardable(t, testCommon, function (db, done) { 43 | db.put('key', 'value', {}, function (err) { 44 | t.ifError(err) 45 | db.close(function () { 46 | t.is(db.isClosed(), true, 'db is closed') 47 | let sync = false 48 | db.del('key', {}, function (err) { 49 | sync = true 50 | t.ok(err) 51 | t.is(err.message, 'Database is not open') 52 | }) 53 | t.is(sync, false, '.del cb called asynchronously') 54 | done() 55 | }) 56 | }) 57 | }) 58 | }) 59 | 60 | test('maybeError() should be called async: batch()', function (t) { 61 | discardable(t, testCommon, function (db, done) { 62 | db.close(function () { 63 | t.is(db.isClosed(), true, 'db is closed') 64 | let sync = false 65 | db.batch([{ type: 'put', key: 'key' }], {}, function (err) { 66 | sync = true 67 | t.ok(err) 68 | t.is(err.message, 'Database is not open') 69 | }) 70 | t.is(sync, false, '.batch cb called asynchronously') 71 | done() 72 | }) 73 | }) 74 | }) 75 | 76 | test('maybeError() should be called async: getMany()', function (t) { 77 | discardable(t, testCommon, function (db, done) { 78 | db.close(function () { 79 | t.is(db.status, 'closed', 'db is closed') 80 | let sync = false 81 | db.getMany(['key'], function (err) { 82 | sync = true 83 | t.is(err && err.message, 'Database is not open') 84 | done() 85 | }) 86 | t.is(sync, false, '.getMany cb called asynchronously') 87 | }) 88 | }) 89 | }) 90 | } 91 | -------------------------------------------------------------------------------- /test/no-encoding-test.js: -------------------------------------------------------------------------------- 1 | const levelup = require('../lib/levelup') 2 | const memdown = require('memdown') 3 | 4 | module.exports = function (test, testCommon) { 5 | test('without encoding-down: serializes key', function (t) { 6 | const down = memdown() 7 | 8 | down._serializeKey = function (key) { 9 | return key.toUpperCase() 10 | } 11 | 12 | const db = levelup(down) 13 | 14 | db.put('key', 'value', function (err) { 15 | t.ifError(err) 16 | 17 | db.get('KEY', { asBuffer: false }, function (err, value) { 18 | t.ifError(err) 19 | t.is(value, 'value') 20 | db.close(t.end.bind(t)) 21 | }) 22 | }) 23 | }) 24 | 25 | test('without encoding-down: serializes value', function (t) { 26 | const down = memdown() 27 | 28 | down._serializeValue = function (value) { 29 | return value.toUpperCase() 30 | } 31 | 32 | const db = levelup(down) 33 | 34 | db.put('key', 'value', function (err) { 35 | t.ifError(err) 36 | 37 | db.get('key', { asBuffer: false }, function (err, value) { 38 | t.ifError(err) 39 | t.is(value, 'VALUE') 40 | db.close(t.end.bind(t)) 41 | }) 42 | }) 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /test/null-and-undefined-test.js: -------------------------------------------------------------------------------- 1 | const after = require('after') 2 | const discardable = require('./util/discardable') 3 | 4 | module.exports = function (test, testCommon) { 5 | test('null & undefined keys & values: cause error', function (t) { 6 | discardable(t, testCommon, function (db, done) { 7 | const next = after(12, done) 8 | 9 | ;[null, undefined].forEach(function (nullish) { 10 | db.get(nullish, function (err) { 11 | t.is(err.message, 'key cannot be `null` or `undefined`') 12 | next() 13 | }) 14 | 15 | db.del(nullish, function (err) { 16 | t.is(err.message, 'key cannot be `null` or `undefined`') 17 | next() 18 | }) 19 | 20 | db.put(nullish, 'value', function (err) { 21 | t.is(err.message, 'key cannot be `null` or `undefined`') 22 | next() 23 | }) 24 | 25 | db.put('foo', nullish, function (err, value) { 26 | t.is(err.message, 'value cannot be `null` or `undefined`') 27 | next() 28 | }) 29 | 30 | db.batch([{ key: nullish, value: 'bar', type: 'put' }], function (err) { 31 | t.is(err.message, 'key cannot be `null` or `undefined`') 32 | next() 33 | }) 34 | 35 | db.batch([{ key: 'foo', value: nullish, type: 'put' }], function (err) { 36 | t.is(err.message, 'value cannot be `null` or `undefined`') 37 | next() 38 | }) 39 | }) 40 | }) 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /test/open-patchsafe-test.js: -------------------------------------------------------------------------------- 1 | module.exports = function (test, testCommon) { 2 | test('deferred open() is patch-safe: put() on new database', makeTest(function (t, db, done) { 3 | const put = db.put 4 | let called = 0 5 | 6 | db.put = function () { 7 | called++ 8 | return put.apply(this, arguments) 9 | } 10 | 11 | db.put('key', 'VALUE', function () { 12 | t.is(called, 1) 13 | done() 14 | }) 15 | })) 16 | 17 | test('deferred open() is patch-safe: del() on new database', makeTest(function (t, db, done) { 18 | const del = db.del 19 | let called = 0 20 | 21 | db.del = function () { 22 | called++ 23 | return del.apply(this, arguments) 24 | } 25 | 26 | db.del('key', function () { 27 | t.is(called, 1) 28 | done() 29 | }) 30 | })) 31 | 32 | test('deferred open() is patch-safe: batch() on new database', makeTest(function (t, db, done) { 33 | const batch = db.batch 34 | let called = 0 35 | 36 | db.batch = function () { 37 | called++ 38 | return batch.apply(this, arguments) 39 | } 40 | 41 | db.batch([ 42 | { key: 'key', value: 'v', type: 'put' }, 43 | { key: 'key2', value: 'v2', type: 'put' } 44 | ], function () { 45 | t.is(called, 1) 46 | done() 47 | }) 48 | })) 49 | 50 | function makeTest (fn) { 51 | return function (t) { 52 | // Open database without callback, opens in next tick 53 | const db = testCommon.factory() 54 | 55 | fn(t, db, function (err) { 56 | t.ifError(err, 'no test error') 57 | db.close(t.end.bind(t)) 58 | }) 59 | 60 | // Expected state is 'opening' 61 | t.is(db._isOpening(), true) 62 | t.is(db.isOpen(), false) 63 | t.is(db.isClosed(), false) 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /test/read-stream-test.js: -------------------------------------------------------------------------------- 1 | const sinon = require('sinon') 2 | const bigBlob = Array.apply(null, Array(1024 * 100)).map(function () { return 'aaaaaaaaaa' }).join('') 3 | const discardable = require('./util/discardable') 4 | const readStreamContext = require('./util/rs-context') 5 | const rsFactory = require('./util/rs-factory') 6 | const nextTick = require('../lib/next-tick') 7 | 8 | module.exports = function (test, testCommon) { 9 | const createReadStream = rsFactory(testCommon) 10 | 11 | function makeTest (fn) { 12 | return function (t) { 13 | discardable(t, testCommon, function (db, done) { 14 | fn(t, db, readStreamContext(t), done) 15 | }) 16 | } 17 | } 18 | 19 | test('ReadStream: simple ReadStream', makeTest(function (t, db, ctx, done) { 20 | db.batch(ctx.sourceData.slice(), function (err) { 21 | t.ifError(err) 22 | 23 | const rs = createReadStream(db) 24 | rs.on('data', ctx.dataSpy) 25 | rs.on('end', ctx.endSpy) 26 | rs.on('close', function () { 27 | ctx.verify() 28 | done() 29 | }) 30 | }) 31 | })) 32 | 33 | test('ReadStream: pausing', makeTest(function (t, db, ctx, done) { 34 | let calls = 0 35 | let rs 36 | 37 | const pauseVerify = function () { 38 | t.is(calls, 5, 'stream should still be paused') 39 | rs.resume() 40 | pauseVerify.called = true 41 | } 42 | const onData = function () { 43 | if (++calls === 5) { 44 | rs.pause() 45 | setTimeout(pauseVerify, 50) 46 | } 47 | } 48 | 49 | // so we can still verify 50 | ctx.dataSpy = sinon.spy(onData) 51 | 52 | db.batch(ctx.sourceData.slice(), function (err) { 53 | t.ifError(err) 54 | 55 | rs = createReadStream(db) 56 | rs.on('data', ctx.dataSpy) 57 | rs.on('end', ctx.endSpy) 58 | rs.on('end', function () { 59 | t.is(calls, ctx.sourceData.length, 'onData was used in test') 60 | t.ok(pauseVerify.called, 'pauseVerify was used in test') 61 | ctx.verify() 62 | done() 63 | }) 64 | }) 65 | })) 66 | 67 | test('ReadStream: destroy() immediately', makeTest(function (t, db, ctx, done) { 68 | db.batch(ctx.sourceData.slice(), function (err) { 69 | t.ifError(err) 70 | 71 | const rs = createReadStream(db) 72 | rs.on('data', ctx.dataSpy) 73 | rs.on('end', ctx.endSpy) 74 | rs.on('close', function () { 75 | t.is(ctx.dataSpy.callCount, 0, '"data" event was not fired') 76 | t.is(ctx.endSpy.callCount, 0, '"end" event was not fired') 77 | done() 78 | }) 79 | rs.destroy() 80 | }) 81 | })) 82 | 83 | test('ReadStream: destroy() after close', makeTest(function (t, db, ctx, done) { 84 | db.batch(ctx.sourceData.slice(), function (err) { 85 | t.ifError(err) 86 | 87 | const rs = createReadStream(db) 88 | rs.on('data', ctx.dataSpy) 89 | rs.on('end', ctx.endSpy) 90 | rs.on('close', function () { 91 | rs.destroy() 92 | done() 93 | }) 94 | }) 95 | })) 96 | 97 | test('ReadStream: destroy() after closing db', makeTest(function (t, db, ctx, done) { 98 | db.batch(ctx.sourceData.slice(), function (err) { 99 | t.ifError(err) 100 | db.close(function (err) { 101 | t.ifError(err) 102 | const rs = createReadStream(db) 103 | rs.destroy() 104 | done() 105 | }) 106 | }) 107 | })) 108 | 109 | test('ReadStream: destroy() twice', makeTest(function (t, db, ctx, done) { 110 | db.batch(ctx.sourceData.slice(), function (err) { 111 | t.ifError(err) 112 | 113 | const rs = createReadStream(db) 114 | rs.on('data', function () { 115 | rs.destroy() 116 | rs.destroy() 117 | done() 118 | }) 119 | }) 120 | })) 121 | 122 | test('ReadStream: destroy() half way through', makeTest(function (t, db, ctx, done) { 123 | db.batch(ctx.sourceData.slice(), function (err) { 124 | t.ifError(err) 125 | 126 | const rs = createReadStream(db) 127 | const endSpy = sinon.spy() 128 | let calls = 0 129 | ctx.dataSpy = sinon.spy(function () { 130 | if (++calls === 5) { rs.destroy() } 131 | }) 132 | rs.on('data', ctx.dataSpy) 133 | rs.on('end', endSpy) 134 | rs.on('close', function () { 135 | // should do "data" 5 times ONLY 136 | t.is(ctx.dataSpy.callCount, 5, 'ReadStream emitted correct number of "data" events (5)') 137 | 138 | ctx.sourceData.slice(0, 5).forEach(function (d, i) { 139 | const call = ctx.dataSpy.getCall(i) 140 | t.ok(call) 141 | 142 | if (call) { 143 | t.is(call.args.length, 1, 'ReadStream "data" event #' + i + ' fired with 1 argument') 144 | t.ok(call.args[0].key != null, 'ReadStream "data" event #' + i + ' argument has "key" property') 145 | t.ok(call.args[0].value != null, 'ReadStream "data" event #' + i + ' argument has "value" property') 146 | t.is(call.args[0].key, d.key, 'ReadStream "data" event #' + i + ' argument has correct "key"') 147 | t.is(+call.args[0].value, +d.value, 'ReadStream "data" event #' + i + ' argument has correct "value"') 148 | } 149 | }) 150 | 151 | done() 152 | }) 153 | }) 154 | })) 155 | 156 | test('ReadStream: readStream() with "reverse=true"', makeTest(function (t, db, ctx, done) { 157 | db.batch(ctx.sourceData.slice(), function (err) { 158 | t.ifError(err) 159 | 160 | const rs = createReadStream(db, { reverse: true }) 161 | rs.on('data', ctx.dataSpy) 162 | rs.on('end', ctx.endSpy) 163 | rs.on('close', function () { 164 | ctx.verify(ctx.sourceData.slice().reverse()) 165 | done() 166 | }) 167 | }) 168 | })) 169 | 170 | test('ReadStream: readStream() with "gte"', makeTest(function (t, db, ctx, done) { 171 | db.batch(ctx.sourceData.slice(), function (err) { 172 | t.ifError(err) 173 | 174 | const rs = createReadStream(db, { gte: '50' }) 175 | rs.on('data', ctx.dataSpy) 176 | rs.on('end', ctx.endSpy) 177 | rs.on('close', function () { 178 | // slice off the first 50 so verify() expects only the last 50 even though all 100 are in the db 179 | ctx.verify(ctx.sourceData.slice(50)) 180 | done() 181 | }) 182 | }) 183 | })) 184 | 185 | test('ReadStream: readStream() with "lte" and "reverse=true"', makeTest(function (t, db, ctx, done) { 186 | db.batch(ctx.sourceData.slice(), function (err) { 187 | t.ifError(err) 188 | 189 | const rs = createReadStream(db, { lte: '50', reverse: true }) 190 | rs.on('data', ctx.dataSpy) 191 | rs.on('end', ctx.endSpy) 192 | rs.on('close', function () { 193 | // reverse and slice off the first 50 so verify() expects only the first 50 even though all 100 are in the db 194 | ctx.verify(ctx.sourceData.slice().reverse().slice(49)) 195 | done() 196 | }) 197 | }) 198 | })) 199 | 200 | test('ReadStream: readStream() with "gte" being mid-way key (float)', makeTest(function (t, db, ctx, done) { 201 | db.batch(ctx.sourceData.slice(), function (err) { 202 | t.ifError(err) 203 | 204 | // '49.5' doesn't actually exist but we expect it to start at '50' because '49' < '49.5' < '50' (in string terms as well as numeric) 205 | const rs = createReadStream(db, { gte: '49.5' }) 206 | rs.on('data', ctx.dataSpy) 207 | rs.on('end', ctx.endSpy) 208 | rs.on('close', function () { 209 | // slice off the first 50 so verify() expects only the last 50 even though all 100 are in the db 210 | ctx.verify(ctx.sourceData.slice(50)) 211 | done() 212 | }) 213 | }) 214 | })) 215 | 216 | test('ReadStream: readStream() with "lte" being mid-way key (float) and "reverse=true"', makeTest(function (t, db, ctx, done) { 217 | db.batch(ctx.sourceData.slice(), function (err) { 218 | t.ifError(err) 219 | 220 | const rs = createReadStream(db, { lte: '49.5', reverse: true }) 221 | rs.on('data', ctx.dataSpy) 222 | rs.on('end', ctx.endSpy) 223 | rs.on('close', function () { 224 | // reverse & slice off the first 50 so verify() expects only the first 50 even though all 100 are in the db 225 | ctx.verify(ctx.sourceData.slice().reverse().slice(50)) 226 | done() 227 | }) 228 | }) 229 | })) 230 | 231 | test('ReadStream: readStream() with "gte" being mid-way key (string)', makeTest(function (t, db, ctx, done) { 232 | db.batch(ctx.sourceData.slice(), function (err) { 233 | t.ifError(err) 234 | 235 | // '499999' doesn't actually exist but we expect it to start at '50' because '49' < '499999' < '50' (in string terms) 236 | // the same as the previous test but we're relying solely on string ordering 237 | const rs = createReadStream(db, { gte: '499999' }) 238 | rs.on('data', ctx.dataSpy) 239 | rs.on('end', ctx.endSpy) 240 | rs.on('close', function () { 241 | // slice off the first 50 so verify() expects only the last 50 even though all 100 are in the db 242 | ctx.verify(ctx.sourceData.slice(50)) 243 | done() 244 | }) 245 | }) 246 | })) 247 | 248 | test('ReadStream: readStream() with "lte"', makeTest(function (t, db, ctx, done) { 249 | db.batch(ctx.sourceData.slice(), function (err) { 250 | t.ifError(err) 251 | 252 | const rs = createReadStream(db, { lte: '50' }) 253 | rs.on('data', ctx.dataSpy) 254 | rs.on('end', ctx.endSpy) 255 | rs.on('close', function () { 256 | // slice off the last 49 so verify() expects only 0 -> 50 inclusive, even though all 100 are in the db 257 | ctx.verify(ctx.sourceData = ctx.sourceData.slice(0, 51)) 258 | done() 259 | }) 260 | }) 261 | })) 262 | 263 | test('ReadStream: readStream() with "lte" being mid-way key (float)', makeTest(function (t, db, ctx, done) { 264 | db.batch(ctx.sourceData.slice(), function (err) { 265 | t.ifError(err) 266 | 267 | const rs = createReadStream(db, { lte: '50.5' }) 268 | rs.on('data', ctx.dataSpy) 269 | rs.on('end', ctx.endSpy) 270 | rs.on('close', function () { 271 | // slice off the last 49 so verify() expects only 0 -> 50 inclusive, even though all 100 are in the db 272 | ctx.verify(ctx.sourceData.slice(0, 51)) 273 | done() 274 | }) 275 | }) 276 | })) 277 | 278 | test('ReadStream: readStream() with "lte" being mid-way key (string)', makeTest(function (t, db, ctx, done) { 279 | db.batch(ctx.sourceData.slice(), function (err) { 280 | t.ifError(err) 281 | 282 | const rs = createReadStream(db, { lte: '50555555' }) 283 | rs.on('data', ctx.dataSpy) 284 | rs.on('end', ctx.endSpy) 285 | rs.on('close', function () { 286 | // slice off the last 49 so verify() expects only 0 -> 50 inclusive, even though all 100 are in the db 287 | ctx.verify(ctx.sourceData.slice(0, 51)) 288 | done() 289 | }) 290 | }) 291 | })) 292 | 293 | test('ReadStream: readStream() with "gte" being mid-way key (float) and "reverse=true"', makeTest(function (t, db, ctx, done) { 294 | db.batch(ctx.sourceData.slice(), function (err) { 295 | t.ifError(err) 296 | 297 | const rs = createReadStream(db, { gte: '50.5', reverse: true }) 298 | rs.on('data', ctx.dataSpy) 299 | rs.on('end', ctx.endSpy) 300 | rs.on('close', function () { 301 | ctx.verify(ctx.sourceData.slice().reverse().slice(0, 49)) 302 | done() 303 | }) 304 | }) 305 | })) 306 | 307 | test('ReadStream: readStream() with both "gte" and "lte"', makeTest(function (t, db, ctx, done) { 308 | db.batch(ctx.sourceData.slice(), function (err) { 309 | t.ifError(err) 310 | 311 | const rs = createReadStream(db, { gte: 30, lte: 70 }) 312 | rs.on('data', ctx.dataSpy) 313 | rs.on('end', ctx.endSpy) 314 | rs.on('close', function () { 315 | // should include 30 to 70, inclusive 316 | ctx.verify(ctx.sourceData.slice(30, 71)) 317 | done() 318 | }) 319 | }) 320 | })) 321 | 322 | test('ReadStream: readStream() with both "gte" and "lte" and "reverse=true"', makeTest(function (t, db, ctx, done) { 323 | db.batch(ctx.sourceData.slice(), function (err) { 324 | t.ifError(err) 325 | 326 | const rs = createReadStream(db, { gte: 30, lte: 70, reverse: true }) 327 | rs.on('data', ctx.dataSpy) 328 | rs.on('end', ctx.endSpy) 329 | rs.on('close', function () { 330 | // expect 70 -> 30 inclusive 331 | ctx.verify(ctx.sourceData.slice().reverse().slice(29, 70)) 332 | done() 333 | }) 334 | }) 335 | })) 336 | 337 | // TODO: move this test out 338 | testCommon.encodings && test('ReadStream: hex encoding', makeTest(function (t, db, ctx, done) { 339 | const options = { keyEncoding: 'utf8', valueEncoding: 'hex' } 340 | const data = [ 341 | { type: 'put', key: 'ab', value: 'abcdef0123456789' } 342 | ] 343 | 344 | db.batch(data.slice(), options, function (err) { 345 | t.ifError(err) 346 | 347 | const rs = createReadStream(db, options) 348 | rs.on('data', function (data) { 349 | t.is(data.value, 'abcdef0123456789') 350 | }) 351 | rs.on('end', ctx.endSpy) 352 | rs.on('close', done) 353 | }) 354 | })) 355 | 356 | test('ReadStream: readStream() "reverse=true" not sticky (issue #6)', makeTest(function (t, db, ctx, done) { 357 | db.batch(ctx.sourceData.slice(), function (err) { 358 | t.ifError(err) 359 | // read in reverse, assume all's good 360 | const rs = createReadStream(db, { reverse: true }) 361 | rs.on('close', function () { 362 | // now try reading the other way 363 | const rs = createReadStream(db) 364 | rs.on('data', ctx.dataSpy) 365 | rs.on('end', ctx.endSpy) 366 | rs.on('close', function () { 367 | ctx.verify() 368 | done() 369 | }) 370 | }) 371 | rs.resume() 372 | }) 373 | })) 374 | 375 | test('ReadStream: ReadStream, gte=0', makeTest(function (t, db, ctx, done) { 376 | db.batch(ctx.sourceData.slice(), function (err) { 377 | t.ifError(err) 378 | 379 | const rs = createReadStream(db, { gte: 0 }) 380 | rs.on('data', ctx.dataSpy) 381 | rs.on('end', ctx.endSpy) 382 | rs.on('close', function () { 383 | ctx.verify() 384 | done() 385 | }) 386 | }) 387 | })) 388 | 389 | // we don't expect any data to come out of here because the keys start at '00' not 0 390 | // we just want to ensure that we don't kill the process 391 | test('ReadStream: ReadStream, lte=0', makeTest(function (t, db, ctx, done) { 392 | db.batch(ctx.sourceData.slice(), function (err) { 393 | t.ifError(err) 394 | 395 | const rs = createReadStream(db, { lte: 0 }) 396 | rs.on('data', ctx.dataSpy) 397 | rs.on('end', ctx.endSpy) 398 | rs.on('close', function () { 399 | ctx.verify([]) 400 | done() 401 | }) 402 | }) 403 | })) 404 | 405 | // this is just a fancy way of testing levelup(db).createReadStream() 406 | // i.e. not waiting for 'open' to complete 407 | // TODO: move this test out 408 | testCommon.deferredOpen && test('ReadStream: deferred ReadStream on new db', function (t) { 409 | const db = testCommon.factory() 410 | const ctx = readStreamContext(t) 411 | 412 | db.batch(ctx.sourceData.slice(), function (err) { 413 | t.ifError(err) 414 | db.close(function (err) { 415 | t.ifError(err) 416 | 417 | let async = true 418 | db.open(function (err) { 419 | async = false 420 | t.ifError(err, 'no open error') 421 | }) 422 | 423 | // is in limbo 424 | t.is(db.isOpen(), false) 425 | t.is(db.isClosed(), false) 426 | 427 | const rs = createReadStream(db) 428 | rs.on('data', ctx.dataSpy) 429 | rs.on('end', ctx.endSpy) 430 | rs.on('close', function () { 431 | ctx.verify() 432 | db.close(t.end.bind(t)) 433 | }) 434 | 435 | // Should open lazily 436 | t.ok(async) 437 | }) 438 | }) 439 | }) 440 | 441 | test('ReadStream: readStream() with "limit"', makeTest(function (t, db, ctx, done) { 442 | db.batch(ctx.sourceData.slice(), function (err) { 443 | t.ifError(err) 444 | 445 | const rs = createReadStream(db, { limit: 20 }) 446 | rs.on('data', ctx.dataSpy) 447 | rs.on('end', ctx.endSpy) 448 | rs.on('close', function () { 449 | ctx.verify(ctx.sourceData.slice(0, 20)) 450 | done() 451 | }) 452 | }) 453 | })) 454 | 455 | test('ReadStream: readStream() with "gte" and "limit"', makeTest(function (t, db, ctx, done) { 456 | db.batch(ctx.sourceData.slice(), function (err) { 457 | t.ifError(err) 458 | 459 | const rs = createReadStream(db, { gte: '20', limit: 20 }) 460 | rs.on('data', ctx.dataSpy) 461 | rs.on('end', ctx.endSpy) 462 | rs.on('close', function () { 463 | ctx.verify(ctx.sourceData.slice(20, 40)) 464 | done() 465 | }) 466 | }) 467 | })) 468 | 469 | test('ReadStream: readStream() with "lte" after "limit"', makeTest(function (t, db, ctx, done) { 470 | db.batch(ctx.sourceData.slice(), function (err) { 471 | t.ifError(err) 472 | 473 | const rs = createReadStream(db, { lte: '50', limit: 20 }) 474 | rs.on('data', ctx.dataSpy) 475 | rs.on('end', ctx.endSpy) 476 | rs.on('close', function () { 477 | ctx.verify(ctx.sourceData.slice(0, 20)) 478 | done() 479 | }) 480 | }) 481 | })) 482 | 483 | test('ReadStream: readStream() with "lte" before "limit"', makeTest(function (t, db, ctx, done) { 484 | db.batch(ctx.sourceData.slice(), function (err) { 485 | t.ifError(err) 486 | 487 | const rs = createReadStream(db, { lte: '30', limit: 50 }) 488 | rs.on('data', ctx.dataSpy) 489 | rs.on('end', ctx.endSpy) 490 | rs.on('close', function () { 491 | ctx.verify(ctx.sourceData.slice(0, 31)) 492 | done() 493 | }) 494 | }) 495 | })) 496 | 497 | // can, fairly reliably, trigger a core dump if next/end isn't 498 | // protected properly 499 | // the use of large blobs means that next() takes time to return 500 | // so we should be able to slip in an end() while it's working 501 | test('ReadStream: iterator next/end race condition', makeTest(function (t, db, ctx, done) { 502 | const data = [] 503 | let i = 5 504 | let v 505 | 506 | while (i--) { 507 | v = bigBlob + i 508 | data.push({ type: 'put', key: v, value: v }) 509 | } 510 | 511 | db.batch(data, function (err) { 512 | t.ifError(err) 513 | const rs = createReadStream(db).on('close', done) 514 | rs.once('data', function () { 515 | rs.destroy() 516 | }) 517 | }) 518 | })) 519 | 520 | test('ReadStream: can only end once', makeTest(function (t, db, ctx, done) { 521 | db.batch(ctx.sourceData.slice(), function (err) { 522 | t.ifError(err) 523 | 524 | const rs = createReadStream(db) 525 | .on('close', done) 526 | 527 | nextTick(function () { 528 | rs.destroy() 529 | }) 530 | }) 531 | })) 532 | } 533 | -------------------------------------------------------------------------------- /test/self.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const test = require('tape') 4 | const memdown = require('memdown') 5 | const encode = require('encoding-down') 6 | const levelup = require('../lib/levelup') 7 | const suite = require('.') 8 | const noop = function () {} 9 | 10 | suite({ 11 | test: test, 12 | factory: function (options) { 13 | return levelup(encode(memdown(), options)) 14 | }, 15 | clear: true, 16 | deferredOpen: true, 17 | promises: true, 18 | streams: true, 19 | encodings: true 20 | }) 21 | 22 | suite({ 23 | test: test, 24 | factory: function (options) { 25 | return levelup(memdown(), options) 26 | }, 27 | clear: true, 28 | deferredOpen: true, 29 | promises: true, 30 | streams: true, 31 | encodings: false 32 | }) 33 | 34 | // TODO to make this pass: 35 | // - Have abstract-leveldown use level-errors 36 | // - Perform type checks in same order (e.g. check key before callback) 37 | suite({ 38 | test: noop, 39 | factory: function (options) { 40 | return memdown() 41 | }, 42 | clear: true, 43 | deferredOpen: false, 44 | promises: false, 45 | streams: false, 46 | encodings: false 47 | }) 48 | 49 | // Integration tests that can't use a generic testCommon.factory() 50 | require('./self/manifest-test') 51 | 52 | if (typeof process === 'undefined' || !process.browser) { 53 | require('./browserify-test')(test) 54 | } 55 | -------------------------------------------------------------------------------- /test/self/manifest-test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const test = require('tape') 4 | const levelup = require('../..') 5 | const memdown = require('memdown') 6 | const sinon = require('sinon') 7 | 8 | test('manifest: additionalMethod is proxied', function (t) { 9 | const mem = memdown() 10 | 11 | mem.beep = sinon.spy() 12 | mem.supports = { additionalMethods: { beep: true } } 13 | 14 | const db = levelup(mem) 15 | const noop = () => {} 16 | 17 | t.is(typeof db.beep, 'function') 18 | t.is(typeof levelup.prototype.beep, 'undefined') 19 | 20 | db.beep(noop) 21 | t.is(mem.beep.callCount, 0, 'deferred') 22 | 23 | db.on('open', function () { 24 | t.is(mem.beep.callCount, 1) 25 | t.same(mem.beep.getCall(0).args, [noop]) 26 | 27 | db.beep('boop', noop) 28 | t.same(mem.beep.getCall(1).args, ['boop', noop]) 29 | 30 | db.close(t.end.bind(t)) 31 | }) 32 | }) 33 | 34 | test('manifest: additionalMethod is proxied even if function does not exist', function (t) { 35 | const mem = memdown() 36 | mem.supports = { additionalMethods: { beep: true } } 37 | const db = levelup(mem) 38 | 39 | t.is(typeof db.beep, 'function') 40 | t.is(typeof levelup.prototype.beep, 'undefined') 41 | t.end() 42 | }) 43 | 44 | test('manifest: approximateSize() et al are proxied even if manifest does not exist', function (t) { 45 | const mem = memdown() 46 | 47 | // deferred-leveldown should feature-detect these methods (for now) 48 | mem.approximateSize = function () {} 49 | mem.compactRange = function () {} 50 | 51 | mem.otherMethod = function () {} 52 | mem.supports = null 53 | 54 | const db = levelup(mem) 55 | 56 | t.is(typeof db.approximateSize, 'function') 57 | t.is(typeof db.compactRange, 'function') 58 | t.is(typeof db.otherMethod, 'undefined') 59 | 60 | t.end() 61 | }) 62 | -------------------------------------------------------------------------------- /test/snapshot-test.js: -------------------------------------------------------------------------------- 1 | const delayed = require('delayed').delayed 2 | const trickle = require('trickle') 3 | const discardable = require('./util/discardable') 4 | const readStreamContext = require('./util/rs-context') 5 | const rsFactory = require('./util/rs-factory') 6 | const nextTick = require('../lib/next-tick') 7 | 8 | module.exports = function (test, testCommon) { 9 | const createReadStream = rsFactory(testCommon) 10 | 11 | test('ReadStream implicit snapshot', function (t) { 12 | discardable(t, testCommon, function (db, done) { 13 | const ctx = readStreamContext(t) 14 | 15 | // 1) Store 100 random numbers stored in the database 16 | db.batch(ctx.sourceData.slice(), function (err) { 17 | t.ifError(err, 'no batch error') 18 | 19 | // 2) Create an iterator on the current data, pipe it through a slow stream 20 | // to make *sure* that we're going to be reading it for longer than it 21 | // takes to overwrite the data in there. 22 | 23 | const rs = createReadStream(db).pipe(trickle({ interval: 5 })) 24 | 25 | rs.on('data', ctx.dataSpy) 26 | rs.once('end', ctx.endSpy) 27 | rs.once('close', delayed(function () { 28 | ctx.verify() 29 | done() 30 | }, 0.05)) 31 | 32 | nextTick(function () { 33 | // 3) Concoct and write new random data over the top of existing items. 34 | // If we're not using a snapshot then then we'd expect the test 35 | // to fail because it'll pick up these new values rather than the 36 | // old ones. 37 | const newData = [] 38 | let i 39 | let k 40 | 41 | for (i = 0; i < 100; i++) { 42 | k = (i < 10 ? '0' : '') + i 43 | newData.push({ 44 | type: 'put', 45 | key: k, 46 | value: Math.random() 47 | }) 48 | } 49 | 50 | db.batch(newData.slice(), function (err) { 51 | t.ifError(err, 'no batch error') 52 | // we'll return here faster than it takes the readStream to complete 53 | }) 54 | }) 55 | }) 56 | }) 57 | }) 58 | } 59 | -------------------------------------------------------------------------------- /test/util/discardable.js: -------------------------------------------------------------------------------- 1 | module.exports = function discardable (t, testCommon, options, fn) { 2 | if (typeof options === 'function') { 3 | fn = options 4 | options = {} 5 | } 6 | 7 | const db = testCommon.factory(options) 8 | 9 | db.open(function () { 10 | fn(db, function done (err) { 11 | t.ifError(err, 'no test error') 12 | 13 | db.close(function (err) { 14 | t.ifError(err, 'no close error') 15 | t.end() 16 | }) 17 | }) 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /test/util/rs-context.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const sinon = require('sinon') 4 | 5 | module.exports = function readStreamContext (t) { 6 | const ctx = {} 7 | let i 8 | let k 9 | 10 | ctx.dataSpy = sinon.spy() 11 | ctx.endSpy = sinon.spy() 12 | ctx.sourceData = [] 13 | 14 | for (i = 0; i < 100; i++) { 15 | k = (i < 10 ? '0' : '') + i 16 | ctx.sourceData.push({ 17 | type: 'put', 18 | key: k, 19 | value: Math.random() 20 | }) 21 | } 22 | 23 | ctx.verify = function (data) { 24 | if (!data) data = ctx.sourceData // can pass alternative data array for verification 25 | 26 | t.is(ctx.endSpy.callCount, 1, 'ReadStream emitted single "end" event') 27 | t.is(ctx.dataSpy.callCount, data.length, 'ReadStream emitted correct number of "data" events') 28 | 29 | data.forEach(function (d, i) { 30 | const call = ctx.dataSpy.getCall(i) 31 | if (call) { 32 | t.is(call.args.length, 1, 'ReadStream "data" event #' + i + ' fired with 1 argument') 33 | t.ok(call.args[0].key, 'ReadStream "data" event #' + i + ' argument has "key" property') 34 | t.ok(call.args[0].value, 'ReadStream "data" event #' + i + ' argument has "value" property') 35 | t.is(call.args[0].key, d.key, 'ReadStream "data" event #' + i + ' argument has correct "key"') 36 | t.is(+call.args[0].value, +d.value, 'ReadStream "data" event #' + i + ' argument has correct "value"') 37 | } 38 | }) 39 | } 40 | 41 | return ctx 42 | } 43 | -------------------------------------------------------------------------------- /test/util/rs-factory.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (testCommon) { 4 | return function createReadStream (db, options) { 5 | if (!testCommon.encodings) { 6 | options = Object.assign({}, options, { 7 | keyAsBuffer: false, 8 | valueAsBuffer: false 9 | }) 10 | } 11 | 12 | return db.createReadStream(options) 13 | } 14 | } 15 | --------------------------------------------------------------------------------