├── .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 | [](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 |
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 |
--------------------------------------------------------------------------------